make recvmail work
unlike sendmail, recvmail is always optional. this is the case because the cloudron may not receive emails at all, so app always has to be prepared for it. part of #804
This commit is contained in:
119
src/apps.js
119
src/apps.js
@@ -40,6 +40,7 @@ exports = module.exports = {
|
||||
setDebugMode,
|
||||
setEnvironment,
|
||||
setMailbox,
|
||||
setInbox,
|
||||
setLocation,
|
||||
setDataDir,
|
||||
repair,
|
||||
@@ -180,7 +181,8 @@ const APPS_FIELDS_PREFIXED = [ 'apps.id', 'apps.appStoreId', 'apps.installationS
|
||||
'apps.health', 'apps.containerId', 'apps.manifestJson', 'apps.accessRestrictionJson', 'apps.memoryLimit', 'apps.cpuShares',
|
||||
'apps.label', 'apps.tagsJson', 'apps.taskId', 'apps.reverseProxyConfigJson', 'apps.servicesConfigJson', 'apps.operatorsJson',
|
||||
'apps.sso', 'apps.debugModeJson', 'apps.enableBackup', 'apps.proxyAuth', 'apps.containerIp', 'apps.crontab',
|
||||
'apps.creationTime', 'apps.updateTime', 'apps.enableMailbox', 'apps.mailboxName', 'apps.mailboxDomain', 'apps.enableAutomaticUpdate',
|
||||
'apps.creationTime', 'apps.updateTime', 'apps.enableAutomaticUpdate',
|
||||
'apps.enableMailbox', 'apps.mailboxName', 'apps.mailboxDomain', 'apps.enableInbox', 'apps.inboxName', 'apps.inboxDomain',
|
||||
'apps.dataDir', 'apps.ts', 'apps.healthTime', '(apps.icon IS NOT NULL) AS hasIcon', '(apps.appStoreIcon IS NOT NULL) AS hasAppStoreIcon' ].join(',');
|
||||
|
||||
// const PORT_BINDINGS_FIELDS = [ 'hostPort', 'type', 'environmentVariable', 'appId' ].join(',');
|
||||
@@ -494,10 +496,11 @@ function getDataDir(app, dataDir) {
|
||||
function removeInternalFields(app) {
|
||||
return _.pick(app,
|
||||
'id', 'appStoreId', 'installationState', 'error', 'runState', 'health', 'taskId',
|
||||
'location', 'domain', 'fqdn', 'mailboxName', 'mailboxDomain', 'crontab',
|
||||
'location', 'domain', 'fqdn', 'crontab',
|
||||
'accessRestriction', 'manifest', 'portBindings', 'iconUrl', 'memoryLimit', 'cpuShares', 'operators',
|
||||
'sso', 'debugMode', 'reverseProxyConfig', 'enableBackup', 'creationTime', 'updateTime', 'ts', 'tags',
|
||||
'label', 'alternateDomains', 'aliasDomains', 'env', 'enableAutomaticUpdate', 'dataDir', 'mounts', 'enableMailbox');
|
||||
'label', 'alternateDomains', 'aliasDomains', 'env', 'enableAutomaticUpdate', 'dataDir', 'mounts',
|
||||
'enableMailbox', 'mailboxName', 'mailboxDomain', 'enableInbox', 'inboxName', 'inboxDomain');
|
||||
}
|
||||
|
||||
// non-admins can only see these
|
||||
@@ -586,6 +589,7 @@ function postProcess(result) {
|
||||
result.enableBackup = !!result.enableBackup;
|
||||
result.enableAutomaticUpdate = !!result.enableAutomaticUpdate;
|
||||
result.enableMailbox = !!result.enableMailbox;
|
||||
result.enableInbox = !!result.enableInbox;
|
||||
result.proxyAuth = !!result.proxyAuth;
|
||||
result.hasIcon = !!result.hasIcon;
|
||||
result.hasAppStoreIcon = !!result.hasAppStoreIcon;
|
||||
@@ -702,30 +706,31 @@ async function add(id, appStoreId, manifest, location, domain, portBindings, dat
|
||||
|
||||
portBindings = portBindings || { };
|
||||
|
||||
const manifestJson = JSON.stringify(manifest);
|
||||
const accessRestriction = data.accessRestriction || null;
|
||||
const accessRestrictionJson = JSON.stringify(accessRestriction);
|
||||
const memoryLimit = data.memoryLimit || 0;
|
||||
const cpuShares = data.cpuShares || 512;
|
||||
const installationState = data.installationState;
|
||||
const runState = data.runState;
|
||||
const sso = 'sso' in data ? data.sso : null;
|
||||
const debugModeJson = data.debugMode ? JSON.stringify(data.debugMode) : null;
|
||||
const env = data.env || {};
|
||||
const label = data.label || null;
|
||||
const tagsJson = data.tags ? JSON.stringify(data.tags) : null;
|
||||
const mailboxName = data.mailboxName || null;
|
||||
const mailboxDomain = data.mailboxDomain || null;
|
||||
const reverseProxyConfigJson = data.reverseProxyConfig ? JSON.stringify(data.reverseProxyConfig) : null;
|
||||
const servicesConfigJson = data.servicesConfig ? JSON.stringify(data.servicesConfig) : null;
|
||||
const enableMailbox = data.enableMailbox || false;
|
||||
const icon = data.icon || null;
|
||||
const manifestJson = JSON.stringify(manifest),
|
||||
accessRestriction = data.accessRestriction || null,
|
||||
accessRestrictionJson = JSON.stringify(accessRestriction),
|
||||
memoryLimit = data.memoryLimit || 0,
|
||||
cpuShares = data.cpuShares || 512,
|
||||
installationState = data.installationState,
|
||||
runState = data.runState,
|
||||
sso = 'sso' in data ? data.sso : null,
|
||||
debugModeJson = data.debugMode ? JSON.stringify(data.debugMode) : null,
|
||||
env = data.env || {},
|
||||
label = data.label || null,
|
||||
tagsJson = data.tags ? JSON.stringify(data.tags) : null,
|
||||
mailboxName = data.mailboxName || null,
|
||||
mailboxDomain = data.mailboxDomain || null,
|
||||
reverseProxyConfigJson = data.reverseProxyConfig ? JSON.stringify(data.reverseProxyConfig) : null,
|
||||
servicesConfigJson = data.servicesConfig ? JSON.stringify(data.servicesConfig) : null,
|
||||
enableMailbox = data.enableMailbox || false,
|
||||
icon = data.icon || null;
|
||||
|
||||
let queries = [];
|
||||
const queries = [];
|
||||
|
||||
queries.push({
|
||||
query: 'INSERT INTO apps (id, appStoreId, manifestJson, installationState, runState, accessRestrictionJson, memoryLimit, cpuShares, '
|
||||
+ 'sso, debugModeJson, mailboxName, mailboxDomain, label, tagsJson, reverseProxyConfigJson, servicesConfigJson, icon, enableMailbox) '
|
||||
+ 'sso, debugModeJson, mailboxName, mailboxDomain, label, tagsJson, reverseProxyConfigJson, servicesConfigJson, icon, '
|
||||
+ 'enableMailbox) '
|
||||
+ ' VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
|
||||
args: [ id, appStoreId, manifestJson, installationState, runState, accessRestrictionJson, memoryLimit, cpuShares,
|
||||
sso, debugModeJson, mailboxName, mailboxDomain, label, tagsJson, reverseProxyConfigJson, servicesConfigJson, icon, enableMailbox ]
|
||||
@@ -1178,7 +1183,6 @@ async function install(data, auditSource) {
|
||||
overwriteDns = 'overwriteDns' in data ? data.overwriteDns : false,
|
||||
skipDnsSetup = 'skipDnsSetup' in data ? data.skipDnsSetup : false,
|
||||
appStoreId = data.appStoreId,
|
||||
enableMailbox = 'enabledMailbox' in data ? data.enableMailbox : true,
|
||||
manifest = data.manifest;
|
||||
|
||||
let error = manifestFormat.parse(manifest);
|
||||
@@ -1216,6 +1220,7 @@ async function install(data, auditSource) {
|
||||
if (settings.isDemo() && constants.DEMO_BLACKLISTED_APPS.includes(appStoreId)) throw new BoxError(BoxError.BAD_FIELD, 'This app is blacklisted in the demo');
|
||||
|
||||
// sendmail is enabled by default
|
||||
const enableMailbox = 'enableMailbox' in data ? data.enableMailbox : true;
|
||||
const mailboxName = manifest.addons?.sendmail ? mailboxNameForLocation(location, manifest) : null;
|
||||
const mailboxDomain = manifest.addons?.sendmail ? domain : null;
|
||||
|
||||
@@ -1477,8 +1482,9 @@ async function setMailbox(app, data, auditSource) {
|
||||
assert.strictEqual(typeof data, 'object');
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
const { enable, mailboxDomain } = data;
|
||||
assert.strictEqual(typeof enable, 'boolean');
|
||||
assert.strictEqual(typeof data.enable, 'boolean');
|
||||
|
||||
const enableMailbox = data.enable;
|
||||
|
||||
const appId = app.id;
|
||||
let error = checkAppState(app, exports.ISTATE_PENDING_RECREATE_CONTAINER);
|
||||
@@ -1486,25 +1492,64 @@ async function setMailbox(app, data, auditSource) {
|
||||
|
||||
if (!app.manifest.addons?.sendmail) throw new BoxError(BoxError.BAD_FIELD, 'App does not use sendmail');
|
||||
const optional = 'optional' in app.manifest.addons.sendmail ? app.manifest.addons.sendmail.optional : false;
|
||||
if (!optional && !enable) throw new BoxError(BoxError.BAD_FIELD, 'App requires sendmail to be enabled');
|
||||
if (!optional && !enableMailbox) throw new BoxError(BoxError.BAD_FIELD, 'App requires sendmail to be enabled');
|
||||
|
||||
await mail.getDomain(mailboxDomain); // check if domain exists
|
||||
let mailboxName = data.mailboxName || null;
|
||||
const mailboxDomain = data.mailboxDomain || null;
|
||||
|
||||
let mailboxName = data.mailboxName;
|
||||
if (mailboxName) {
|
||||
error = mail.validateName(mailboxName);
|
||||
if (error) throw new BoxError(BoxError.BAD_FIELD, error.message, { field: 'mailboxName' });
|
||||
} else {
|
||||
mailboxName = mailboxNameForLocation(app.location, app.domain, app.manifest);
|
||||
if (enableMailbox) {
|
||||
await mail.getDomain(mailboxDomain); // check if domain exists
|
||||
|
||||
if (mailboxName) {
|
||||
error = mail.validateName(mailboxName);
|
||||
if (error) throw new BoxError(BoxError.BAD_FIELD, error.message, { field: 'mailboxName' });
|
||||
} else {
|
||||
mailboxName = mailboxNameForLocation(app.location, app.domain, app.manifest);
|
||||
}
|
||||
}
|
||||
|
||||
const task = {
|
||||
args: {},
|
||||
values: { enableMailbox: enable, mailboxName, mailboxDomain }
|
||||
values: { enableMailbox, mailboxName, mailboxDomain }
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_RECREATE_CONTAINER, task);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, mailboxName, taskId });
|
||||
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, mailboxName, mailboxDomain, taskId });
|
||||
|
||||
return { taskId };
|
||||
}
|
||||
|
||||
async function setInbox(app, data, auditSource) {
|
||||
assert.strictEqual(typeof app, 'object');
|
||||
assert.strictEqual(typeof data, 'object');
|
||||
assert.strictEqual(typeof auditSource, 'object');
|
||||
|
||||
assert.strictEqual(typeof data.enable, 'boolean');
|
||||
|
||||
const enableInbox = data.enable;
|
||||
const appId = app.id;
|
||||
let error = checkAppState(app, exports.ISTATE_PENDING_RECREATE_CONTAINER);
|
||||
if (error) throw error;
|
||||
|
||||
if (!app.manifest.addons?.recvmail) throw new BoxError(BoxError.BAD_FIELD, 'App does not use recvmail addon');
|
||||
|
||||
const inboxName = data.inboxName || null;
|
||||
const inboxDomain = data.inboxDomain || null;
|
||||
if (enableInbox) {
|
||||
const domain = await mail.getDomain(data.inboxDomain); // check if domain exists
|
||||
if (!domain.enabled) throw new BoxError(BoxError.BAD_FIELD, 'Domain does not have incoming email enabled');
|
||||
|
||||
error = mail.validateName(data.inboxName);
|
||||
if (error) throw new BoxError(BoxError.BAD_FIELD, error.message, { field: 'inboxName' });
|
||||
}
|
||||
|
||||
const task = {
|
||||
args: {},
|
||||
values: { enableInbox, inboxName, inboxDomain }
|
||||
};
|
||||
const taskId = await addTask(appId, exports.ISTATE_PENDING_RECREATE_CONTAINER, task);
|
||||
|
||||
await eventlog.add(eventlog.ACTION_APP_CONFIGURE, auditSource, { appId, app, enableInbox, inboxName, inboxDomain, taskId });
|
||||
|
||||
return { taskId };
|
||||
}
|
||||
@@ -1826,7 +1871,7 @@ async function repair(app, data, auditSource) {
|
||||
error = checkManifestConstraints(data.manifest);
|
||||
if (error) throw error;
|
||||
|
||||
if (!hasMailAddon(data.manifest)) { // clear if repair removed addon
|
||||
if (!data.manifest.addons?.sendmail) { // clear if repair removed addon
|
||||
task.values.mailboxName = task.values.mailboxDomain = null;
|
||||
} else if (!app.mailboxName || app.mailboxName.endsWith('.app')) { // allocate since repair added the addon
|
||||
task.values.mailboxName = mailboxNameForLocation(app.location, data.manifest);
|
||||
|
||||
Reference in New Issue
Block a user