Built-in Zone MTA
Plugin for ZoneMTA for per-message DKIM keys.
This commit is contained in:
parent
d103a2cc79
commit
77c64f487d
18 changed files with 231 additions and 110 deletions
|
@ -142,14 +142,16 @@ export default class CUD extends Component {
|
|||
|
||||
@withAsyncErrorHandler
|
||||
async fetchSendConfiguration(sendConfigurationId) {
|
||||
this.fetchSendConfigurationId = sendConfigurationId;
|
||||
if (sendConfigurationId) {
|
||||
this.fetchSendConfigurationId = sendConfigurationId;
|
||||
|
||||
const result = await axios.get(getUrl(`rest/send-configurations-public/${sendConfigurationId}`));
|
||||
const result = await axios.get(getUrl(`rest/send-configurations-public/${sendConfigurationId}`));
|
||||
|
||||
if (sendConfigurationId === this.fetchSendConfigurationId) {
|
||||
this.setState({
|
||||
sendConfiguration: result.data
|
||||
});
|
||||
if (sendConfigurationId === this.fetchSendConfigurationId) {
|
||||
this.setState({
|
||||
sendConfiguration: result.data
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -442,7 +442,8 @@ class TextArea extends Component {
|
|||
label: PropTypes.string.isRequired,
|
||||
placeholder: PropTypes.string,
|
||||
help: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
format: PropTypes.string
|
||||
format: PropTypes.string,
|
||||
className: PropTypes.string
|
||||
}
|
||||
|
||||
static contextTypes = {
|
||||
|
@ -452,11 +453,12 @@ class TextArea extends Component {
|
|||
render() {
|
||||
const props = this.props;
|
||||
const owner = this.context.formStateOwner;
|
||||
const id = this.props.id;
|
||||
const id = props.id;
|
||||
const htmlId = 'form_' + id;
|
||||
const className = props.className || ''
|
||||
|
||||
return wrapInput(id, htmlId, owner, props.format, '', props.label, props.help,
|
||||
<textarea id={htmlId} placeholder={props.placeholder} value={owner.getFormValue(id) || ''} className="form-control" aria-describedby={htmlId + '_help'} onChange={evt => owner.updateFormValue(id, evt.target.value)}></textarea>
|
||||
<textarea id={htmlId} placeholder={props.placeholder} value={owner.getFormValue(id) || ''} className={`form-control ${className}`} aria-describedby={htmlId + '_help'} onChange={evt => owner.updateFormValue(id, evt.target.value)}></textarea>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,6 +82,7 @@ export default class CUD extends Component {
|
|||
this.getFormValuesFromEntity(this.props.entity, data => {
|
||||
this.mailerTypes[data.mailer_type].afterLoad(data);
|
||||
data.verpEnabled = !!data.verp_hostname;
|
||||
data.verp_hostname = data.verp_hostname || '';
|
||||
});
|
||||
|
||||
} else {
|
||||
|
@ -89,9 +90,13 @@ export default class CUD extends Component {
|
|||
name: '',
|
||||
description: '',
|
||||
namespace: mailtrainConfig.user.namespace,
|
||||
from_email: '',
|
||||
from_email_overridable: false,
|
||||
from_name: '',
|
||||
from_name_overridable: false,
|
||||
reply_to: '',
|
||||
reply_to_overridable: false,
|
||||
subject: '',
|
||||
subject_overridable: false,
|
||||
verpEnabled: false,
|
||||
verp_hostname: '',
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import React from "react";
|
||||
|
||||
import {MailerType} from "../../../shared/send-configurations";
|
||||
import {MailerType, ZoneMTAType} from "../../../shared/send-configurations";
|
||||
import {
|
||||
CheckBox,
|
||||
Dropdown,
|
||||
|
@ -11,6 +11,7 @@ import {
|
|||
TextArea
|
||||
} from "../lib/form";
|
||||
import {Trans} from "react-i18next";
|
||||
import styles from "./styles.scss";
|
||||
|
||||
export const mailerTypesOrder = [
|
||||
MailerType.ZONE_MTA,
|
||||
|
@ -139,6 +140,12 @@ export function getMailerTypes(t) {
|
|||
{ key: 'eu-west-1', label: t('euwest1')}
|
||||
];
|
||||
|
||||
const zoneMtaTypeOptions = [
|
||||
{ key: ZoneMTAType.WITH_MAILTRAIN_HEADER_CONF, label: t('Dynamic configuration of DKIM keys via ZoneMTA\'s Mailtrain plugin (use this option for builtin ZoneMTA)')},
|
||||
{ key: ZoneMTAType.WITH_HTTP_CONF, label: t('Dynamic configuration of DKIM keys via ZoneMTA\'s HTTP config plugin')},
|
||||
{ key: ZoneMTAType.REGULAR, label: t('No dynamic configuration of DKIM keys')}
|
||||
]
|
||||
|
||||
mailerTypes[MailerType.GENERIC_SMTP] = {
|
||||
getForm: owner =>
|
||||
<div>
|
||||
|
@ -182,39 +189,49 @@ export function getMailerTypes(t) {
|
|||
};
|
||||
|
||||
mailerTypes[MailerType.ZONE_MTA] = {
|
||||
getForm: owner =>
|
||||
<div>
|
||||
<Fieldset label={t('mailerSettings')}>
|
||||
<Dropdown id="mailer_type" label={t('mailerType')} options={typeOptions}/>
|
||||
<InputField id="smtpHostname" label={t('hostname')} placeholder={t('hostnameEgSmtpexamplecom')}/>
|
||||
<InputField id="smtpPort" label={t('port')} placeholder={t('portEg465AutodetectedIfLeftBlank')}/>
|
||||
<Dropdown id="smtpEncryption" label={t('encryption')} options={smtpEncryptionOptions}/>
|
||||
<CheckBox id="smtpUseAuth" text={t('enableSmtpAuthentication')}/>
|
||||
{ owner.getFormValue('smtpUseAuth') &&
|
||||
<div>
|
||||
<InputField id="smtpUser" label={t('username')} placeholder={t('usernameEgMyaccount@examplecom')}/>
|
||||
<InputField id="smtpPassword" label={t('password')} placeholder={t('usernameEgMyaccount@examplecom')}/>
|
||||
</div>
|
||||
getForm: owner => {
|
||||
const zoneMtaType = Number.parseInt(owner.getFormValue('zoneMtaType'));
|
||||
return (
|
||||
<div>
|
||||
<Fieldset label={t('mailerSettings')}>
|
||||
<Dropdown id="mailer_type" label={t('mailerType')} options={typeOptions}/>
|
||||
<Dropdown id="zoneMtaType" label={t('Dynamic configuration')} options={zoneMtaTypeOptions}/>
|
||||
<InputField id="smtpHostname" label={t('hostname')} placeholder={t('hostnameEgSmtpexamplecom')}/>
|
||||
<InputField id="smtpPort" label={t('port')} placeholder={t('portEg465AutodetectedIfLeftBlank')}/>
|
||||
<Dropdown id="smtpEncryption" label={t('encryption')} options={smtpEncryptionOptions}/>
|
||||
<CheckBox id="smtpUseAuth" text={t('enableSmtpAuthentication')}/>
|
||||
{ owner.getFormValue('smtpUseAuth') &&
|
||||
<div>
|
||||
<InputField id="smtpUser" label={t('username')} placeholder={t('usernameEgMyaccount@examplecom')}/>
|
||||
<InputField id="smtpPassword" label={t('password')} placeholder={t('usernameEgMyaccount@examplecom')}/>
|
||||
</div>
|
||||
}
|
||||
</Fieldset>
|
||||
{(zoneMtaType === ZoneMTAType.WITH_MAILTRAIN_HEADER_CONF || zoneMtaType === ZoneMTAType.WITH_HTTP_CONF) &&
|
||||
<Fieldset label={t('dkimSigning')}>
|
||||
<Trans i18nKey="ifYouAreUsingZoneMtaThenMailtrainCan"><p>If you are using ZoneMTA then Mailtrain can provide a DKIM key for signing all outgoing messages.</p></Trans>
|
||||
<Trans i18nKey="doNotUseSensitiveKeysHereThePrivateKeyIs"><p className="text-warning">Do not use sensitive keys here. The private key is not encrypted in the database.</p></Trans>
|
||||
{zoneMtaType === ZoneMTAType.WITH_HTTP_CONF &&
|
||||
<InputField id="dkimApiKey" label={t('zoneMtaDkimApiKey')} help={t('secretValueKnownToZoneMtaForRequesting')}/>
|
||||
}
|
||||
<InputField id="dkimDomain" label={t('dkimDomain')} help={t('leaveBlankToUseTheSenderEmailAddress')}/>
|
||||
<InputField id="dkimSelector" label={t('dkimKeySelector')} help={t('signingIsDisabledWithoutAValidSelector')}/>
|
||||
<TextArea id="dkimPrivateKey" className={styles.dkimPrivateKey} label={t('dkimPrivateKey')} placeholder={t('beginsWithBeginRsaPrivateKey')} help={t('signingIsDisabledWithoutAValidPrivateKey')}/>
|
||||
</Fieldset>
|
||||
}
|
||||
</Fieldset>
|
||||
<Fieldset label={t('dkimSigning')}>
|
||||
<Trans i18nKey="ifYouAreUsingZoneMtaThenMailtrainCan"><p>If you are using ZoneMTA then Mailtrain can provide a DKIM key for signing all outgoing messages. Other services usually provide their own means to DKIM sign your messages.</p></Trans>
|
||||
<Trans i18nKey="doNotUseSensitiveKeysHereThePrivateKeyIs"><p className="text-warning">Do not use sensitive keys here. The private key is not encrypted in the database.</p></Trans>
|
||||
<InputField id="dkimApiKey" label={t('zoneMtaDkimApiKey')} help={t('secretValueKnownToZoneMtaForRequesting')}/>
|
||||
<InputField id="dkimDomain" label={t('dkimDomain')} help={t('leaveBlankToUseTheSenderEmailAddress')}/>
|
||||
<InputField id="dkimSelector" label={t('dkimKeySelector')} help={t('signingIsDisabledWithoutAValidSelector')}/>
|
||||
<TextArea id="dkimPrivateKey" label={t('dkimPrivateKey')} placeholder={t('beginsWithBeginRsaPrivateKey')} help={t('signingIsDisabledWithoutAValidPrivateKey')}/>
|
||||
</Fieldset>
|
||||
<Fieldset label={t('advancedMailerSettings')}>
|
||||
<CheckBox id="logTransactions" text={t('logSmtpTransactions')}/>
|
||||
<CheckBox id="smtpAllowSelfSigned" text={t('allowSelfsignedCertificates')}/>
|
||||
<InputField id="maxConnections" label={t('maxConnections')} placeholder={t('theCountOfMaxConnectionsEg10')} help={t('theCountOfMaximumSimultaneousConnections')}/>
|
||||
<InputField id="smtpMaxMessages" label={t('maxMessages')} placeholder={t('theCountOfMaxMessagesEg100')} help={t('theNumberOfMessagesToSendThroughASingle')}/>
|
||||
<InputField id="throttling" label={t('throttling')} placeholder={t('messagesPerHourEg1000')} help={t('maximumNumberOfMessagesToSendInAnHour')}/>
|
||||
</Fieldset>
|
||||
</div>,
|
||||
<Fieldset label={t('advancedMailerSettings')}>
|
||||
<CheckBox id="logTransactions" text={t('logSmtpTransactions')}/>
|
||||
<CheckBox id="smtpAllowSelfSigned" text={t('allowSelfsignedCertificates')}/>
|
||||
<InputField id="maxConnections" label={t('maxConnections')} placeholder={t('theCountOfMaxConnectionsEg10')} help={t('theCountOfMaximumSimultaneousConnections')}/>
|
||||
<InputField id="smtpMaxMessages" label={t('maxMessages')} placeholder={t('theCountOfMaxMessagesEg100')} help={t('theNumberOfMessagesToSendThroughASingle')}/>
|
||||
<InputField id="throttling" label={t('throttling')} placeholder={t('messagesPerHourEg1000')} help={t('maximumNumberOfMessagesToSendInAnHour')}/>
|
||||
</Fieldset>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
initData: () => ({
|
||||
...getInitGenericSMTP(),
|
||||
zoneMtaType: ZoneMTAType.REGULAR,
|
||||
dkimApiKey: '',
|
||||
dkimDomain: '',
|
||||
dkimSelector: '',
|
||||
|
@ -222,6 +239,7 @@ export function getMailerTypes(t) {
|
|||
}),
|
||||
afterLoad: data => {
|
||||
afterLoadGenericSMTP(data);
|
||||
data.zoneMtaType = data.mailer_settings.zoneMtaType;
|
||||
data.dkimApiKey = data.mailer_settings.dkimApiKey;
|
||||
data.dkimDomain = data.mailer_settings.dkimDomain;
|
||||
data.dkimSelector = data.mailer_settings.dkimSelector;
|
||||
|
@ -229,10 +247,17 @@ export function getMailerTypes(t) {
|
|||
},
|
||||
beforeSave: data => {
|
||||
beforeSaveGenericSMTP(data);
|
||||
data.mailer_settings.dkimApiKey = data.dkimApiKey;
|
||||
data.mailer_settings.dkimDomain = data.dkimDomain;
|
||||
data.mailer_settings.dkimSelector = data.dkimSelector;
|
||||
data.mailer_settings.dkimPrivateKey = data.dkimPrivateKey;
|
||||
const zoneMtaType = Number.parseInt(data.zoneMtaType);
|
||||
data.mailer_settings.zoneMtaType = zoneMtaType;
|
||||
if (zoneMtaType === ZoneMTAType.WITH_HTTP_CONF || zoneMtaType === ZoneMTAType.WITH_MAILTRAIN_HEADER_CONF) {
|
||||
data.mailer_settings.dkimDomain = data.dkimDomain;
|
||||
data.mailer_settings.dkimSelector = data.dkimSelector;
|
||||
data.mailer_settings.dkimPrivateKey = data.dkimPrivateKey;
|
||||
}
|
||||
if (zoneMtaType === ZoneMTAType.WITH_HTTP_CONF) {
|
||||
data.mailer_settings.dkimApiKey = data.dkimApiKey;
|
||||
}
|
||||
|
||||
clearBeforeSave(data);
|
||||
},
|
||||
afterTypeChange: mutState => {
|
||||
|
|
3
client/src/send-configurations/styles.scss
Normal file
3
client/src/send-configurations/styles.scss
Normal file
|
@ -0,0 +1,3 @@
|
|||
textarea.dkimPrivateKey {
|
||||
height: 200px;
|
||||
}
|
|
@ -215,6 +215,9 @@ testServer:
|
|||
password: testpass
|
||||
logger: false
|
||||
|
||||
builtinZoneMTA:
|
||||
enabled: true
|
||||
|
||||
seleniumWebDriver:
|
||||
browser: phantomjs
|
||||
|
||||
|
|
|
@ -23,4 +23,6 @@ passwordresetlink="xxx"
|
|||
[reports]
|
||||
enabled=true
|
||||
[redis]
|
||||
enabled=true
|
||||
enabled=true
|
||||
[log]
|
||||
level="verbose"
|
|
@ -21,6 +21,7 @@ const privilegeHelpers = require('./lib/privilege-helpers');
|
|||
const knex = require('./lib/knex');
|
||||
const shares = require('./models/shares');
|
||||
const { AppType } = require('../shared/app');
|
||||
const builtinZoneMta = require('./lib/builtin-zone-mta');
|
||||
|
||||
const trustedPort = config.www.trustedPort;
|
||||
const sandboxPort = config.www.sandboxPort;
|
||||
|
@ -94,29 +95,31 @@ dbcheck(err => { // Check if database needs upgrading before starting the server
|
|||
.then(() =>
|
||||
executor.spawn(() =>
|
||||
testServer(() =>
|
||||
verpServer(() =>
|
||||
startHTTPServer(AppType.TRUSTED, 'trusted', trustedPort, () =>
|
||||
startHTTPServer(AppType.SANDBOXED, 'sandbox', sandboxPort, () =>
|
||||
startHTTPServer(AppType.PUBLIC, 'public', publicPort, () => {
|
||||
privilegeHelpers.dropRootPrivileges();
|
||||
verpServer(() =>
|
||||
builtinZoneMta.spawn(() =>
|
||||
startHTTPServer(AppType.TRUSTED, 'trusted', trustedPort, () =>
|
||||
startHTTPServer(AppType.SANDBOXED, 'sandbox', sandboxPort, () =>
|
||||
startHTTPServer(AppType.PUBLIC, 'public', publicPort, () => {
|
||||
privilegeHelpers.dropRootPrivileges();
|
||||
|
||||
tzupdate.start();
|
||||
tzupdate.start();
|
||||
|
||||
importer.spawn(() =>
|
||||
feedcheck.spawn(() =>
|
||||
senders.spawn(() => {
|
||||
triggers.start();
|
||||
gdprCleanup.start();
|
||||
importer.spawn(() =>
|
||||
feedcheck.spawn(() =>
|
||||
senders.spawn(() => {
|
||||
triggers.start();
|
||||
gdprCleanup.start();
|
||||
|
||||
postfixBounceServer(async () => {
|
||||
await reportProcessor.init();
|
||||
log.info('Service', 'All services started');
|
||||
appBuilder.setReady();
|
||||
});
|
||||
})
|
||||
)
|
||||
);
|
||||
})
|
||||
postfixBounceServer(async () => {
|
||||
await reportProcessor.init();
|
||||
log.info('Service', 'All services started');
|
||||
appBuilder.setReady();
|
||||
});
|
||||
})
|
||||
)
|
||||
);
|
||||
})
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
|
45
server/lib/builtin-zone-mta.js
Normal file
45
server/lib/builtin-zone-mta.js
Normal file
|
@ -0,0 +1,45 @@
|
|||
'use strict';
|
||||
|
||||
const config = require('config');
|
||||
const fork = require('child_process').fork;
|
||||
const log = require('./log');
|
||||
const path = require('path');
|
||||
|
||||
let zoneMtaProcess;
|
||||
|
||||
module.exports = {
|
||||
spawn
|
||||
};
|
||||
|
||||
function spawn(callback) {
|
||||
if (config.builtinZoneMTA.enabled) {
|
||||
log.info('ZoneMTA', 'Starting built-in Zone MTA process');
|
||||
|
||||
zoneMtaProcess = fork(
|
||||
path.join(__dirname, '..', '..', 'zone-mta', 'index.js'),
|
||||
['--config=' + path.join(__dirname, '..', '..', 'zone-mta', 'config', 'zonemta.js')],
|
||||
{
|
||||
cwd: path.join(__dirname, '..', '..', 'zone-mta'),
|
||||
env: {NODE_ENV: process.env.NODE_ENV}
|
||||
}
|
||||
);
|
||||
|
||||
zoneMtaProcess.on('message', msg => {
|
||||
if (msg) {
|
||||
if (msg.type === 'zone-mta-started') {
|
||||
log.info('ZoneMTA', 'ZoneMTA process started');
|
||||
return callback();
|
||||
} else if (msg.type === 'entries-added') {
|
||||
senders.scheduleCheck();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
zoneMtaProcess.on('close', (code, signal) => {
|
||||
log.error('ZoneMTA', 'ZoneMTA process exited with code %s signal %s', code, signal);
|
||||
});
|
||||
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
}
|
|
@ -328,9 +328,9 @@ class CampaignSender {
|
|||
|
||||
const getOverridable = key => {
|
||||
if (sendConfiguration[key + '_overridable'] && this.campaign[key + '_override'] !== null) {
|
||||
return campaign[key + '_override'];
|
||||
return campaign[key + '_override'] || '';
|
||||
} else {
|
||||
return sendConfiguration[key];
|
||||
return sendConfiguration[key] || '';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ const transports = new Map();
|
|||
async function getOrCreateMailer(sendConfigurationId) {
|
||||
let sendConfiguration;
|
||||
|
||||
if (!sendConfiguration) {
|
||||
if (!sendConfigurationId) {
|
||||
sendConfiguration = await sendConfigurations.getSystemSendConfiguration();
|
||||
} else {
|
||||
sendConfiguration = await sendConfigurations.getById(contextHelpers.getAdminContext(), sendConfigurationId, false, true);
|
||||
|
@ -38,8 +38,35 @@ function invalidateMailer(sendConfigurationId) {
|
|||
|
||||
|
||||
|
||||
function _addDkimKeys(transport, mail) {
|
||||
const sendConfiguration = transport.mailer.sendConfiguration;
|
||||
|
||||
if (sendConfiguration.mailer_type === sendConfigurations.MailerType.ZONE_MTA) {
|
||||
if (!mail.headers) {
|
||||
mail.headers = {};
|
||||
}
|
||||
|
||||
const dkimDomain = sendConfiguration.mailer_settings.dkimDomain;
|
||||
const dkimSelector = (sendConfiguration.mailer_settings.dkimSelector || '').trim();
|
||||
const dkimPrivateKey = (sendConfiguration.mailer_settings.dkimPrivateKey || '').trim();
|
||||
|
||||
if (dkimSelector && dkimPrivateKey) {
|
||||
const from = (mail.from.address || '').trim();
|
||||
const domain = from.split('@').pop().toLowerCase().trim();
|
||||
|
||||
mail.headers['x-mailtrain-dkim'] = JSON.stringify({
|
||||
domainName: dkimDomain || domain,
|
||||
keySelector: dkimSelector,
|
||||
privateKey: dkimPrivateKey
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function _sendMail(transport, mail, template) {
|
||||
_addDkimKeys(transport, mail);
|
||||
|
||||
let tryCount = 0;
|
||||
const trySend = (callback) => {
|
||||
tryCount++;
|
||||
|
@ -209,6 +236,7 @@ async function _createTransport(sendConfiguration) {
|
|||
}
|
||||
|
||||
transport.mailer = {
|
||||
sendConfiguration,
|
||||
throttleWait: bluebird.promisify(throttleWait),
|
||||
sendTransactionalMail: async (mail, template) => await _sendTransactionalMail(transport, mail, template),
|
||||
sendMassMail: async (mail, template) => await _sendMail(transport, mail)
|
||||
|
|
|
@ -666,46 +666,33 @@ async function getMessageByCid(messageCid) {
|
|||
|
||||
const [campaignCid, listCid, subscriptionCid] = messageCidElems;
|
||||
|
||||
await knex.transaction(async tx => {
|
||||
return await knex.transaction(async tx => {
|
||||
const list = await tx('lists').where('cid', listCid).select('id');
|
||||
const subscrTblName = subscriptions.getSubscriptionTableName(list.id);
|
||||
|
||||
const message = await tx('campaign_messages')
|
||||
.innerJoin('campaigns', 'campaign_messages.campaign', 'campaigns.id')
|
||||
.innerJoin(subscrTblName, subscrTblName + '.id', 'campaign_messages.subscription')
|
||||
.leftJoin('segments', 'segment.id', 'campaign_messages.segment') // This is just to make sure that the respective segment still exists or return null if it doesn't
|
||||
.leftJoin('send_configurations', 'send_configurations.id', 'campaign_messages.send_configuration') // This is just to make sure that the respective send_configuration still exists or return null if it doesn't
|
||||
.where(subscrTblName + '.cid', subscriptionCid)
|
||||
.where('campaigns.cid', campaignCid)
|
||||
.select([
|
||||
'campaign_messages.id', 'campaign_messages.campaign', 'campaign_messages.list', 'segments.id AS segment', 'campaign_messages.subscription',
|
||||
'send_configurations.id AS send_configuration', 'campaign_messages.status', 'campaign_messages.response', 'campaign_messages.response_id',
|
||||
'campaign_messages.updated', 'campaign_messages.created', 'send_configurations.verp_hostname AS verp_hostname'
|
||||
]);
|
||||
|
||||
if (message) {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'campaign', message.campaign, 'manageMessages');
|
||||
}
|
||||
'campaign_messages.id', 'campaign_messages.campaign', 'campaign_messages.list', 'campaign_messages.subscription', 'campaign_messages.status'
|
||||
])
|
||||
.first();
|
||||
|
||||
return message;
|
||||
});
|
||||
}
|
||||
|
||||
async function getMessageByResponseId(responseId) {
|
||||
await knex.transaction(async tx => {
|
||||
return await knex.transaction(async tx => {
|
||||
console.log(responseId);
|
||||
const message = await tx('campaign_messages')
|
||||
.leftJoin('segments', 'segment.id', 'campaign_messages.segment') // This is just to make sure that the respective segment still exists or return null if it doesn't
|
||||
.leftJoin('send_configurations', 'send_configurations.id', 'campaign_messages.send_configuration') // This is just to make sure that the respective send_configuration still exists or return null if it doesn't
|
||||
.where('campaign_messages.response_id', responseId)
|
||||
.select([
|
||||
'campaign_messages.id', 'campaign_messages.campaign', 'campaign_messages.list', 'segments.id AS segment', 'campaign_messages.subscription',
|
||||
'send_configurations.id AS send_configuration', 'campaign_messages.status', 'campaign_messages.response', 'campaign_messages.response_id',
|
||||
'campaign_messages.updated', 'campaign_messages.created', 'send_configurations.verp_hostname AS verp_hostname'
|
||||
]);
|
||||
|
||||
if (message) {
|
||||
await shares.enforceEntityPermissionTx(tx, context, 'campaign', message.campaign, 'manageMessages');
|
||||
}
|
||||
'campaign_messages.id', 'campaign_messages.campaign', 'campaign_messages.list', 'campaign_messages.subscription', 'campaign_messages.status'
|
||||
])
|
||||
.first();
|
||||
|
||||
return message;
|
||||
});
|
||||
|
@ -763,6 +750,7 @@ async function changeStatusByMessage(context, message, subscriptionStatus, updat
|
|||
}
|
||||
|
||||
await _changeStatusByMessageTx(tx, context, message, subscriptionStatus);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -180,22 +180,28 @@ router.postAsync('/mailgun', uploads.any(), async (req, res) => {
|
|||
|
||||
|
||||
router.postAsync('/zone-mta', async (req, res) => {
|
||||
if (typeof req.body === 'string') {
|
||||
req.body = JSON.parse(req.body);
|
||||
}
|
||||
|
||||
if (req.body.id) {
|
||||
const message = await campaigns.getMessageByCid(req.body.id);
|
||||
|
||||
if (message) {
|
||||
await campaigns.changeStatusByMessage(contextHelpers.getAdminContext(), message, SubscriptionStatus.BOUNCED, true);
|
||||
log.verbose('ZoneMTA', 'Marked message %s as bounced', req.body.id);
|
||||
try {
|
||||
if (typeof req.body === 'string') {
|
||||
req.body = JSON.parse(req.body);
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true
|
||||
});
|
||||
if (req.body.id) {
|
||||
const message = await campaigns.getMessageByResponseId(req.body.id);
|
||||
console.log(message);
|
||||
|
||||
if (message) {
|
||||
await campaigns.changeStatusByMessage(contextHelpers.getAdminContext(), message, SubscriptionStatus.BOUNCED, true);
|
||||
log.verbose('ZoneMTA', 'Marked message %s as bounced', req.body.id);
|
||||
}
|
||||
}
|
||||
|
||||
res.json({
|
||||
success: true
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -164,7 +164,7 @@ async function processCampaign(campaignId) {
|
|||
}
|
||||
} catch (err) {
|
||||
log.error('Senders', `Sending campaign ${campaignId} failed with error: ${err.message}`)
|
||||
log.verbose(err);
|
||||
log.verbose(err.stack);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,7 +204,7 @@ async function scheduleCampaigns() {
|
|||
}
|
||||
} catch (err) {
|
||||
log.error('Senders', `Scheduling campaigns failed with error: ${err.message}`)
|
||||
log.verbose(err);
|
||||
log.verbose(err.stack);
|
||||
}
|
||||
|
||||
|
||||
|
@ -249,7 +249,7 @@ async function processQueued() {
|
|||
}
|
||||
} catch (err) {
|
||||
log.error('Senders', `Processing queued messages failed with error: ${err.message}`)
|
||||
log.verbose(err);
|
||||
log.verbose(err.stack);
|
||||
}
|
||||
|
||||
queuedSchedulerRunning = false;
|
||||
|
@ -326,7 +326,7 @@ async function init() {
|
|||
scheduleCampaigns();
|
||||
|
||||
} else if (type === 'reload-config') {
|
||||
for (const worker of workerProcesses.keys()) {
|
||||
for (const workerId of workerProcesses.keys()) {
|
||||
sendToWorker(workerId, 'reload-config', msg.data);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,7 +35,7 @@ async function processMessages(campaignId, subscribers) {
|
|||
log.verbose('Senders', 'Message sent and status updated for %s:%s', subData.listId, subData.email || subData.subscriptionId);
|
||||
} catch (err) {
|
||||
log.error('Senders', `Sending message to ${subData.listId}:${subData.email} failed with error: ${err.message}`)
|
||||
log.verbose(err);
|
||||
log.verbose(err.stack);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ const contextHelpers = require('../../../lib/context-helpers');
|
|||
const mosaicoTemplates = require('../../../../shared/mosaico-templates');
|
||||
const {getGlobalNamespaceId} = require('../../../../shared/namespaces');
|
||||
const {getAdminId} = require('../../../../shared/users');
|
||||
const { MailerType, getSystemSendConfigurationId, getSystemSendConfigurationCid } = require('../../../../shared/send-configurations');
|
||||
const { MailerType, ZoneMTAType, getSystemSendConfigurationId, getSystemSendConfigurationCid } = require('../../../../shared/send-configurations');
|
||||
const { enforce } = require('../../../lib/helpers');
|
||||
const { EntityVals: TriggerEntityVals, EventVals: TriggerEventVals } = require('../../../../shared/triggers');
|
||||
const { SubscriptionSource } = require('../../../../shared/lists');
|
||||
|
@ -753,6 +753,7 @@ async function migrateSettings(knex) {
|
|||
if (settings.dkimApiKey) {
|
||||
mailer_type = MailerType.ZONE_MTA;
|
||||
mailer_settings.dkimApiKey = settings.dkimApiKey;
|
||||
mailer_settings.zoneMtaType = ZoneMTAType.WITH_HTTP_CONF;
|
||||
mailer_settings.dkimDomain = settings.dkimDomain;
|
||||
mailer_settings.dkimSelector = settings.dkimSelector;
|
||||
mailer_settings.dkimPrivateKey = settings.dkimPrivateKey;
|
||||
|
|
|
@ -6,6 +6,12 @@ const MailerType = {
|
|||
AWS_SES: 'aws_ses'
|
||||
};
|
||||
|
||||
const ZoneMTAType = {
|
||||
REGULAR: 0,
|
||||
WITH_HTTP_CONF: 1,
|
||||
WITH_MAILTRAIN_HEADER_CONF: 2
|
||||
}
|
||||
|
||||
function getSystemSendConfigurationId() {
|
||||
return 1;
|
||||
}
|
||||
|
@ -16,6 +22,7 @@ function getSystemSendConfigurationCid() {
|
|||
|
||||
module.exports = {
|
||||
MailerType,
|
||||
ZoneMTAType,
|
||||
getSystemSendConfigurationId,
|
||||
getSystemSendConfigurationCid
|
||||
};
|
1
zone-mta
Submodule
1
zone-mta
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 7990c49dda71c8cb65ddd83da3b785711d26894b
|
Loading…
Reference in a new issue