diff --git a/migrations/20240227102406-apps-add-inboxDomain-constraint.js b/migrations/20240227102406-apps-add-inboxDomain-constraint.js new file mode 100644 index 000000000..a550c8cf2 --- /dev/null +++ b/migrations/20240227102406-apps-add-inboxDomain-constraint.js @@ -0,0 +1,17 @@ +'use strict'; + +exports.up = function(db, callback) { + db.runSql('ALTER TABLE apps ADD CONSTRAINT inbox_domain_constraint FOREIGN KEY(inboxDomain) REFERENCES domains(domain)', function (error) +{ + if (error) console.error(error); + callback(error); + }); +}; + +exports.down = function(db, callback) { + db.runSql('ALTER TABLE apps DROP FOREIGN KEY inbox_domain_constraint', function (error) { + if (error) console.error(error); + callback(error); + }); +}; + diff --git a/migrations/schema.sql b/migrations/schema.sql index c7a4fcf61..7696c2fc5 100644 --- a/migrations/schema.sql +++ b/migrations/schema.sql @@ -105,6 +105,7 @@ CREATE TABLE IF NOT EXISTS apps( upstreamUri VARCHAR(256) DEFAULT "", FOREIGN KEY(mailboxDomain) REFERENCES domains(domain), + FOREIGN KEY(inboxDomain) REFERENCES domains(domain), FOREIGN KEY(taskId) REFERENCES tasks(id), FOREIGN KEY(storageVolumeId) REFERENCES volumes(id), UNIQUE (storageVolumeId, storageVolumePrefix), diff --git a/src/apps.js b/src/apps.js index 96aa0b4b1..904c7b023 100644 --- a/src/apps.js +++ b/src/apps.js @@ -1994,6 +1994,11 @@ async function updateApp(app, data, auditSource) { values.mailboxDomain = app.domain; } + if (!manifest.addons?.recvmail) { // clear if the update removed addon. required for fk constraint + values.enableInbox = false; + values.inboxName = values.inboxDomain = null; + } + const hasSso = !!updateConfig.manifest.addons?.proxyAuth || !!updateConfig.manifest.addons?.ldap || !!manifest.addons?.oidc; if (!hasSso && app.sso) values.sso = false; // turn off sso flag, if the update removes sso options @@ -2149,6 +2154,11 @@ async function restore(app, backupId, auditSource) { values.mailboxDomain = app.domain; } + if (!manifest.addons?.recvmail) { // recvmail is always optional. clear if restore removed addon + values.enableInbox = false; + values.inboxName = values.inboxDomain = null; + } + const restoreConfig = { remotePath: backupInfo.remotePath, backupFormat: backupInfo.format }; const task = { @@ -2290,7 +2300,7 @@ async function clone(app, data, user, auditSource) { error = validatePortBindings(data.portBindings || null, manifest); if (error) throw error; - const newPortBindings = translatePortBindings(data.portBindings || null, manifest); + const portBindings = translatePortBindings(data.portBindings || null, manifest); // should we copy the original app's mailbox settings instead? const mailboxName = manifest.addons?.sendmail ? mailboxNameForSubdomain(subdomain, manifest) : null; @@ -2304,6 +2314,8 @@ async function clone(app, data, user, auditSource) { 'enableMailbox', 'mailboxDisplayName', 'mailboxName', 'mailboxDomain', 'enableInbox', 'inboxName', 'inboxDomain', 'enableTurn', 'enableRedis', 'mounts', 'enableBackup', 'enableAutomaticUpdate', 'accessRestriction', 'operators', 'sso'); + if (!manifest.addons?.recvmail) dolly.inboxDomain = null; // needed because we are cloning _current_ app settings with old manifest + const obj = Object.assign(dolly, { installationState: exports.ISTATE_PENDING_CLONE, runState: exports.RSTATE_RUNNING, @@ -2316,8 +2328,8 @@ async function clone(app, data, user, auditSource) { icon: icons.icon, }); - const [addError] = await safe(add(newAppId, appStoreId, manifest, subdomain, domain, newPortBindings, obj)); - if (addError && addError.reason === BoxError.ALREADY_EXISTS) throw getDuplicateErrorDetails(addError.message, locations, newPortBindings); + const [addError] = await safe(add(newAppId, appStoreId, manifest, subdomain, domain, portBindings, obj)); + if (addError && addError.reason === BoxError.ALREADY_EXISTS) throw getDuplicateErrorDetails(addError.message, locations, portBindings); if (addError) throw addError; await purchaseApp({ appId: newAppId, appstoreId: app.appStoreId, manifestId: manifest.id || 'customapp' });