I have a very weird session issue - some odd cross session problem that has me baffled.
Basically I have an express 3 app in which I have 2, seemingly unrelated things - one is a standard contact form that someone can use to email me and the other is an API call to mailchimp to add subscribers when they place an order. These are 2 completely different calls/actions.
At the top of my contact form I have a check for the variable 'error' which I can use in my controller code to say something such as "invalid email" or other error message. My contact form has 2 controller actions - one for GET which shows the form and one for POST which submits and shows errors if any.
The issue is this - when I load the contact form initially I see this message: "Error: [email protected] is already subscribed to list General. Click here to update your profile." which is a MailChimp message - not only unrelated to the process but unrelated to my session! This error can happen if a returning customer places and order and they are already subscribed. I don't ever show them this message not, as far as I can tell, actually store it. So I am confused.
One thing to note is that the order part is called from the node server upon receiving an xml file from the payment gateway (so not a regular session user) - so maybe there are some session internals I am not understanding.
The code is below but here's a summary of the issue. A node server, without a user session active, is making an API call to mailchimp to add a subscriber using this module - It is common that the same email will be subscribed twice so mailchimp will reply with an "already subscribed" message which I *do no*t store or load in Express's error locals. Then a user visits the site, goes to contact form which checks if there are any error messages from that user trying to submit the form with say missing fields - and they see the mailchimp message showing another person's email address.
Here's the relevant code.
app.js:
var express = require("express"),
flash = require("connect-flash");
...
app.use(flash());
...
app.use(function(req, res, next) {
var msgs;
msgs = req.session.messages || [];
res.locals.messages = msgs;
res.locals.hasMessages = !!msgs.length;
req.session.messages = [];
return next();
});
...
app.get("/contact", express.csrf(), routes.main.contact);
app.post("/contact", express.csrf(), routes.main.submit);
contact form controller:
exports.contact = function(req, res) {
res.locals.token = req.session._csrf;
res.render('main/contact');
};
exports.submit = function(req, res) {
var send = function(message, fn) {
var sendgrid = new SendGrid(settings.sendgrid_username, settings.sendgrid_password);
sendgrid.send({
to: settings.contact_email,
from: message.email,
subject: 'Contact Message',
text: message.message
}, fn);
};
var validate = function(message) {
var v = new Validator(),
errors = []
;
v.error = function(msg) {
errors.push(msg);
};
v.check(message.name, 'Please enter your name').len(1, 100);
v.check(message.email, 'Please enter a valid email address').isEmail();
v.check(message.message, 'Please enter a valid message').len(1, 1000);
return errors;
};
function render() {
res.locals.token = req.session._csrf;
res.render('main/contact', locals);
}
var message = req.body.message,
errors = validate(message),
locals = {}
;
if (errors.length === 0) {
send(message, function(success) {
if (!success) {
locals.error = 'Error sending message';
locals.message = message;
} else {
locals.notice = 'Your message has been sent.';
}
render();
});
} else {
locals.error = 'Your message has errors:';
locals.errors = errors;
locals.message = message;
render();
}
};
contact form:
{% if error or notice %}
<div id="message" class="alert alert-{% if error %}error{% else %}success{% endif %} ">
<button type="button" class="close" data-dismiss="alert">×</button>
<h4>{% if error %}{{ error }}{% else %}{{ notice }}{% endif %}</h4>
{% if errors %}
<ul>
{% for e in errors %}
<li>{{ e }}</li>
{% endfor %}
</ul>
{% endif %}
</div>
{% endif %}
<form method="post">
...
order form controller (called by payment gateway api on successful order - never called directly by user)
var joinNewsletter = function(data) {
try {
var api = new MailChimpAPI(settings.mailchimp_api, { version : '1.3', secure : false });
api.listSubscribe({
id: settings.mailchimp_list_id,
email_address: data.email,
merge_vars: {
fname: data.first,
lname: data.last
},
double_optin: data.optin || false
}, function() {});
} catch (error) {
console.log(error.message);
}
};
...
joinNewsletter({
email: order.email,
first: order.fname,
last: order.lname
});
Oh good god I figured it out after a looooot of debugging. The issues are twofold:
The MailChimpAPI module has a bug - when an error is received it creates this code:
error = new Error(message || (message = ''));
The error var is undeclared so it's hitting the global error var.
It seems that error is being handled as an app.locals variable hence bridging user sessions.
I am fixing it by a) patching the MailChimp helper code and filing a bug report and b) making sure to not use 'error' as a view variable ever again.
Woah.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With