Merged PR #528
Support for detecting MTA by its response. Message IDs are reconstructed based on detected MTA. Bugfixes for AWS. AWS now seems to work.
This commit is contained in:
parent
bbbe671d59
commit
3c72e778d9
6 changed files with 83 additions and 45 deletions
|
@ -93,6 +93,14 @@ export default class CUD extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
submitFormValuesMutator(data) {
|
submitFormValuesMutator(data) {
|
||||||
|
data.seconds = Number.parseInt(data.daysAfter) * 3600 * 24;
|
||||||
|
|
||||||
|
if (data.entity === Entity.SUBSCRIPTION) {
|
||||||
|
data.event = data.subscriptionEvent;
|
||||||
|
} else if (data.entity === Entity.CAMPAIGN) {
|
||||||
|
data.event = data.campaignEvent;
|
||||||
|
}
|
||||||
|
|
||||||
return filterData(data, ['name', 'description', 'entity', 'event', 'seconds', 'enabled', 'source_campaign']);
|
return filterData(data, ['name', 'description', 'entity', 'event', 'seconds', 'enabled', 'source_campaign']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,22 +165,14 @@ export default class CUD extends Component {
|
||||||
this.disableForm();
|
this.disableForm();
|
||||||
this.setFormStatusMessage('info', t('saving'));
|
this.setFormStatusMessage('info', t('saving'));
|
||||||
|
|
||||||
const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url, data => {
|
const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url);
|
||||||
data.seconds = Number.parseInt(data.daysAfter) * 3600 * 24;
|
|
||||||
|
|
||||||
if (data.entity === Entity.SUBSCRIPTION) {
|
|
||||||
data.event = data.subscriptionEvent;
|
|
||||||
} else if (data.entity === Entity.CAMPAIGN) {
|
|
||||||
data.event = data.campaignEvent;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (submitResult) {
|
if (submitResult) {
|
||||||
if (this.props.entity) {
|
if (this.props.entity) {
|
||||||
if (submitAndLeave) {
|
if (submitAndLeave) {
|
||||||
this.navigateToWithFlashMessage(`/campaigns/${this.props.campaign.id}/triggers`, 'success', t('triggerUpdated'));
|
this.navigateToWithFlashMessage(`/campaigns/${this.props.campaign.id}/triggers`, 'success', t('triggerUpdated'));
|
||||||
} else {
|
} else {
|
||||||
await this.getFormValuesFromURL(`rest/triggers/${this.props.campaign.id}/${this.props.entity.id}`, ::this.getFormValuesMutator);
|
await this.getFormValuesFromURL(`rest/triggers/${this.props.campaign.id}/${this.props.entity.id}`);
|
||||||
this.enableForm();
|
this.enableForm();
|
||||||
this.setFormStatusMessage('success', t('triggerUpdated'));
|
this.setFormStatusMessage('success', t('triggerUpdated'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,18 +57,21 @@ export default class List extends Component {
|
||||||
segmentId: PropTypes.string
|
segmentId: PropTypes.string
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSegmentSelection(props) {
|
componentDidMount() {
|
||||||
this.populateFormValues({
|
this.populateFormValues({
|
||||||
segment: props.segmentId || ''
|
segment: this.props.segmentId || ''
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
this.updateSegmentSelection(this.props);
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidUpdate() {
|
componentDidUpdate() {
|
||||||
this.updateSegmentSelection(this.props);
|
const segmentId = this.props.segmentId || '';
|
||||||
|
|
||||||
|
if (this.getFormValue('segment') !== segmentId) {
|
||||||
|
// Populate is used here because it does not invoke onChange
|
||||||
|
this.populateFormValues({
|
||||||
|
segment: segmentId
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
|
|
@ -4,7 +4,16 @@ import React, {Component} from 'react';
|
||||||
import {withTranslation} from '../lib/i18n';
|
import {withTranslation} from '../lib/i18n';
|
||||||
import {Title, withPageHelpers} from '../lib/page'
|
import {Title, withPageHelpers} from '../lib/page'
|
||||||
import {Link} from 'react-router-dom'
|
import {Link} from 'react-router-dom'
|
||||||
import {Button, ButtonRow, Form, FormSendMethod, InputField, withForm, withFormErrorHandlers} from '../lib/form';
|
import {
|
||||||
|
Button,
|
||||||
|
ButtonRow,
|
||||||
|
filterData,
|
||||||
|
Form,
|
||||||
|
FormSendMethod,
|
||||||
|
InputField,
|
||||||
|
withForm,
|
||||||
|
withFormErrorHandlers
|
||||||
|
} from '../lib/form';
|
||||||
import {withAsyncErrorHandler, withErrorHandling} from '../lib/error-handling';
|
import {withAsyncErrorHandler, withErrorHandling} from '../lib/error-handling';
|
||||||
import passwordValidator from '../../../shared/password-validator';
|
import passwordValidator from '../../../shared/password-validator';
|
||||||
import axios from '../lib/axios';
|
import axios from '../lib/axios';
|
||||||
|
@ -39,6 +48,10 @@ export default class Account extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
submitFormValuesMutator(data) {
|
||||||
|
return filterData(data, ['password']);
|
||||||
|
}
|
||||||
|
|
||||||
@withAsyncErrorHandler
|
@withAsyncErrorHandler
|
||||||
async validateResetToken() {
|
async validateResetToken() {
|
||||||
const params = this.props.match.params;
|
const params = this.props.match.params;
|
||||||
|
@ -96,9 +109,7 @@ export default class Account extends Component {
|
||||||
this.disableForm();
|
this.disableForm();
|
||||||
this.setFormStatusMessage('info', t('resettingPassword'));
|
this.setFormStatusMessage('info', t('resettingPassword'));
|
||||||
|
|
||||||
const submitSuccessful = await this.validateAndSendFormValuesToURL(FormSendMethod.POST, 'rest/password-reset', data => {
|
const submitSuccessful = await this.validateAndSendFormValuesToURL(FormSendMethod.POST, 'rest/password-reset');
|
||||||
delete data.password2;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (submitSuccessful) {
|
if (submitSuccessful) {
|
||||||
this.navigateToWithFlashMessage('/login', 'success', t('passwordReset-1'));
|
this.navigateToWithFlashMessage('/login', 'success', t('passwordReset-1'));
|
||||||
|
|
|
@ -78,6 +78,12 @@ export default class CUD extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
submitFormValuesMutator(data) {
|
submitFormValuesMutator(data) {
|
||||||
|
this.mailerTypes[data.mailer_type].beforeSave(data);
|
||||||
|
if (!data.verpEnabled) {
|
||||||
|
data.verp_hostname = null;
|
||||||
|
data.verp_disable_sender_header = false;
|
||||||
|
}
|
||||||
|
|
||||||
return filterData(data, ['name', 'description', 'from_email', 'from_email_overridable', 'from_name',
|
return filterData(data, ['name', 'description', 'from_email', 'from_email_overridable', 'from_name',
|
||||||
'from_name_overridable', 'reply_to', 'reply_to_overridable', 'subject', 'subject_overridable', 'x_mailer',
|
'from_name_overridable', 'reply_to', 'reply_to_overridable', 'subject', 'subject_overridable', 'x_mailer',
|
||||||
'verp_hostname', 'verp_disable_sender_header', 'mailer_type', 'mailer_settings', 'namespace']);
|
'verp_hostname', 'verp_disable_sender_header', 'mailer_type', 'mailer_settings', 'namespace']);
|
||||||
|
@ -155,13 +161,7 @@ export default class CUD extends Component {
|
||||||
this.disableForm();
|
this.disableForm();
|
||||||
this.setFormStatusMessage('info', t('saving'));
|
this.setFormStatusMessage('info', t('saving'));
|
||||||
|
|
||||||
const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url, data => {
|
const submitResult = await this.validateAndSendFormValuesToURL(sendMethod, url);
|
||||||
this.mailerTypes[data.mailer_type].beforeSave(data);
|
|
||||||
if (!data.verpEnabled) {
|
|
||||||
data.verp_hostname = null;
|
|
||||||
data.verp_disable_sender_header = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (submitResult) {
|
if (submitResult) {
|
||||||
if (this.props.entity) {
|
if (this.props.entity) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const config = require('config');
|
const config = require('config');
|
||||||
|
const log = require('./log');
|
||||||
const mailers = require('./mailers');
|
const mailers = require('./mailers');
|
||||||
const knex = require('./knex');
|
const knex = require('./knex');
|
||||||
const subscriptions = require('../models/subscriptions');
|
const subscriptions = require('../models/subscriptions');
|
||||||
|
@ -20,8 +21,7 @@ const htmlToText = require('html-to-text');
|
||||||
const {getPublicUrl} = require('./urls');
|
const {getPublicUrl} = require('./urls');
|
||||||
const blacklist = require('../models/blacklist');
|
const blacklist = require('../models/blacklist');
|
||||||
const libmime = require('libmime');
|
const libmime = require('libmime');
|
||||||
const shares = require('../models/shares')
|
const shares = require('../models/shares');
|
||||||
|
|
||||||
|
|
||||||
class CampaignSender {
|
class CampaignSender {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -83,7 +83,7 @@ class CampaignSender {
|
||||||
|
|
||||||
const getOverridable = key => {
|
const getOverridable = key => {
|
||||||
return sendConfiguration[key];
|
return sendConfiguration[key];
|
||||||
}
|
};
|
||||||
|
|
||||||
const campaignAddress = [campaign.cid, list.cid, subscriptionGrouped.cid].join('.');
|
const campaignAddress = [campaign.cid, list.cid, subscriptionGrouped.cid].join('.');
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ class CampaignSender {
|
||||||
replyTo: getOverridable('reply_to'),
|
replyTo: getOverridable('reply_to'),
|
||||||
xMailer: sendConfiguration.x_mailer ? sendConfiguration.x_mailer : false,
|
xMailer: sendConfiguration.x_mailer ? sendConfiguration.x_mailer : false,
|
||||||
to: {
|
to: {
|
||||||
name: tools.formatMessage(campaign, list, subscriptionGrouped, mergeTags, list.to_name, false),
|
name: list.to_name === null ? undefined : tools.formatMessage(campaign, list, subscriptionGrouped, mergeTags, list.to_name, false),
|
||||||
address: subscriptionGrouped.email
|
address: subscriptionGrouped.email
|
||||||
},
|
},
|
||||||
sender: useVerpSenderHeader ? campaignAddress + '@' + sendConfiguration.verp_hostname : false,
|
sender: useVerpSenderHeader ? campaignAddress + '@' + sendConfiguration.verp_hostname : false,
|
||||||
|
@ -336,7 +336,7 @@ class CampaignSender {
|
||||||
} else {
|
} else {
|
||||||
return sendConfiguration[key] || '';
|
return sendConfiguration[key] || '';
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
const mail = {
|
const mail = {
|
||||||
from: {
|
from: {
|
||||||
|
@ -346,7 +346,7 @@ class CampaignSender {
|
||||||
replyTo: getOverridable('reply_to'),
|
replyTo: getOverridable('reply_to'),
|
||||||
xMailer: sendConfiguration.x_mailer ? sendConfiguration.x_mailer : false,
|
xMailer: sendConfiguration.x_mailer ? sendConfiguration.x_mailer : false,
|
||||||
to: {
|
to: {
|
||||||
name: tools.formatMessage(campaign, list, subscriptionGrouped, mergeTags, list.to_name, false),
|
name: list.to_name === null ? undefined : tools.formatMessage(campaign, list, subscriptionGrouped, mergeTags, list.to_name, false),
|
||||||
address: subscriptionGrouped.email
|
address: subscriptionGrouped.email
|
||||||
},
|
},
|
||||||
sender: this.useVerpSenderHeader ? campaignAddress + '@' + sendConfiguration.verp_hostname : false,
|
sender: this.useVerpSenderHeader ? campaignAddress + '@' + sendConfiguration.verp_hostname : false,
|
||||||
|
@ -391,24 +391,49 @@ class CampaignSender {
|
||||||
|
|
||||||
let status;
|
let status;
|
||||||
let response;
|
let response;
|
||||||
let responseId;
|
let responseId = null;
|
||||||
try {
|
try {
|
||||||
const info = await mailer.sendMassMail(mail);
|
const info = await mailer.sendMassMail(mail);
|
||||||
status = SubscriptionStatus.SUBSCRIBED;
|
status = SubscriptionStatus.SUBSCRIBED;
|
||||||
|
|
||||||
/*
|
log.verbose('CampaignSender', `response: ${info.response} messageId: ${info.messageId}`);
|
||||||
ZoneMTA
|
|
||||||
|
let match;
|
||||||
|
if ((match = info.response.match(/^250 Message queued as ([0-9a-f]+)$/))) {
|
||||||
|
/*
|
||||||
|
ZoneMTA
|
||||||
info.response: 250 Message queued as 1691ad7f7ae00080fd
|
info.response: 250 Message queued as 1691ad7f7ae00080fd
|
||||||
info.messageId: <e65c9386-e899-7d01-b21e-ec03c3a9d9b4@sathyasai.org>
|
info.messageId: <e65c9386-e899-7d01-b21e-ec03c3a9d9b4@sathyasai.org>
|
||||||
|
*/
|
||||||
|
response = info.response;
|
||||||
|
responseId = match[1];
|
||||||
|
|
||||||
Postal Mail Server
|
} else if ((match = info.messageId.match(/^<([^>@]*)@.*amazonses\.com>$/))) {
|
||||||
|
/*
|
||||||
|
AWS SES
|
||||||
|
info.response: 0102016ad2244c0a-955492f2-9194-4cd1-bef9-70a45906a5a7-000000
|
||||||
|
info.messageId: <0102016ad2244c0a-955492f2-9194-4cd1-bef9-70a45906a5a7-000000@eu-west-1.amazonses.com>
|
||||||
|
*/
|
||||||
|
response = info.response;
|
||||||
|
responseId = match[1];
|
||||||
|
|
||||||
|
} else if (info.response.match(/^250 OK$/) && (match = info.messageId.match(/^<([^>]*)>$/))) {
|
||||||
|
/*
|
||||||
|
Postal Mail Server
|
||||||
info.response: 250 OK
|
info.response: 250 OK
|
||||||
info.messageId: <xxxxxxxxx@xxx.xx> (postal messageId)
|
info.messageId: <xxxxxxxxx@xxx.xx> (postal messageId)
|
||||||
*/
|
*/
|
||||||
|
response = info.response;
|
||||||
|
responseId = match[1];
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
Fallback - Mailtrain v1 behavior
|
||||||
|
*/
|
||||||
|
response = info.response || info.messageId;
|
||||||
|
responseId = response.split(/\s+/).pop();
|
||||||
|
}
|
||||||
|
|
||||||
console.log(`response: ${info.response} messageId: ${info.messageId}`);
|
|
||||||
response = info.response || info.messageId;
|
|
||||||
responseId = info.messageId.replace(/(^<|>$)/g, "") || response.split(/\s+/).pop();
|
|
||||||
|
|
||||||
await knex('campaigns').where('id', campaign.id).increment('delivered');
|
await knex('campaigns').where('id', campaign.id).increment('delivered');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
@ -417,6 +442,7 @@ class CampaignSender {
|
||||||
await knex('campaigns').where('id', campaign.id).increment('delivered').increment('bounced');
|
await knex('campaigns').where('id', campaign.id).increment('delivered').increment('bounced');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const now = new Date();
|
const now = new Date();
|
||||||
|
|
||||||
if (campaign.type === CampaignType.REGULAR || campaign.type === CampaignType.RSS_ENTRY) {
|
if (campaign.type === CampaignType.REGULAR || campaign.type === CampaignType.RSS_ENTRY) {
|
||||||
|
|
|
@ -13,8 +13,6 @@ const uploads = multer();
|
||||||
|
|
||||||
|
|
||||||
router.postAsync('/aws', async (req, res) => {
|
router.postAsync('/aws', async (req, res) => {
|
||||||
console.log(req.body);
|
|
||||||
|
|
||||||
if (typeof req.body === 'string') {
|
if (typeof req.body === 'string') {
|
||||||
req.body = JSON.parse(req.body);
|
req.body = JSON.parse(req.body);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue