diff --git a/src/apps.js b/src/apps.js index 629f55462..e6fcb3c78 100644 --- a/src/apps.js +++ b/src/apps.js @@ -749,24 +749,27 @@ async function updateWithConstraints(id, app, constraints) { if ('subdomain' in app && 'domain' in app) { // must be updated together as they are unique together queries.push({ query: 'DELETE FROM locations WHERE appId = ?', args: [ id ]}); // all locations of an app must be updated together queries.push({ query: 'INSERT INTO locations (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)', args: [ id, app.domain, app.subdomain, Location.TYPE_PRIMARY ]}); + } - if ('secondaryDomains' in app) { - app.secondaryDomains.forEach(function (d) { - queries.push({ query: 'INSERT INTO locations (appId, domain, subdomain, type, environmentVariable) VALUES (?, ?, ?, ?, ?)', args: [ id, d.domain, d.subdomain, Location.TYPE_SECONDARY, d.environmentVariable ]}); - }); - } + if ('secondaryDomains' in app) { + queries.push({ query: 'DELETE FROM locations WHERE appId = ? AND type = ?', args: [ id, Location.TYPE_SECONDARY ]}); + app.secondaryDomains.forEach(function (d) { + queries.push({ query: 'INSERT INTO locations (appId, domain, subdomain, type, environmentVariable) VALUES (?, ?, ?, ?, ?)', args: [ id, d.domain, d.subdomain, Location.TYPE_SECONDARY, d.environmentVariable ]}); + }); + } - if ('redirectDomains' in app) { - app.redirectDomains.forEach(function (d) { - queries.push({ query: 'INSERT INTO locations (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)', args: [ id, d.domain, d.subdomain, Location.TYPE_REDIRECT ]}); - }); - } + if ('redirectDomains' in app) { + queries.push({ query: 'DELETE FROM locations WHERE appId = ? AND type = ?', args: [ id, Location.TYPE_REDIRECT ]}); + app.redirectDomains.forEach(function (d) { + queries.push({ query: 'INSERT INTO locations (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)', args: [ id, d.domain, d.subdomain, Location.TYPE_REDIRECT ]}); + }); + } - if ('aliasDomains' in app) { - app.aliasDomains.forEach(function (d) { - queries.push({ query: 'INSERT INTO locations (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)', args: [ id, d.domain, d.subdomain, Location.TYPE_ALIAS ]}); - }); - } + if ('aliasDomains' in app) { + queries.push({ query: 'DELETE FROM locations WHERE appId = ? AND type = ?', args: [ id, Location.TYPE_ALIAS ]}); + app.aliasDomains.forEach(function (d) { + queries.push({ query: 'INSERT INTO locations (appId, domain, subdomain, type) VALUES (?, ?, ?, ?)', args: [ id, d.domain, d.subdomain, Location.TYPE_ALIAS ]}); + }); } if ('mounts' in app) { @@ -2499,6 +2502,10 @@ async function restore(app, backupId, auditSource) { values.inboxName = values.inboxDomain = null; } + // prune secondaryDomains whose httpPorts no longer exist in the restored manifest + const newHttpPorts = manifest.httpPorts || {}; + values.secondaryDomains = app.secondaryDomains.filter(sd => sd.environmentVariable in newHttpPorts); + const restoreConfig = { backupId: restoreBackup.id }; const task = { diff --git a/src/apptask.js b/src/apptask.js index cbb89bbd6..33870988f 100644 --- a/src/apptask.js +++ b/src/apptask.js @@ -692,6 +692,7 @@ async function updateCommand(app, args, progressCallback) { // FIXME: this does not handle option changes (like multipleDatabases) const unusedAddons = _.omit(app.manifest.addons, Object.keys(updateConfig.manifest.addons)); const httpPortChanged = app.manifest.httpPort !== updateConfig.manifest.httpPort; + const httpPortsChanged = !_.isEqual(app.manifest.httpPorts, updateConfig.manifest.httpPorts); const proxyAuthChanged = !_.isEqual(app.manifest.addons?.proxyAuth, updateConfig.manifest.addons?.proxyAuth); // this protects against the theoretical possibility of an app being marked for update from @@ -762,6 +763,7 @@ async function updateCommand(app, args, progressCallback) { if (environmentVariable in newHttpPorts) continue; // domain still in use secondaryDomains.splice(i, 1); // remove domain } + const removedSecondaryDomains = app.secondaryDomains.filter(sd => !secondaryDomains.some(nsd => nsd.subdomain === sd.subdomain && nsd.domain === sd.domain)); const values = { manifest: updateConfig.manifest, @@ -778,6 +780,8 @@ async function updateCommand(app, args, progressCallback) { await updateApp(app, values); // switch over to the new config + if (removedSecondaryDomains.length) await dns.unregisterLocations(removedSecondaryDomains, progressCallback); + await progressCallback({ percent: 45, message: 'Downloading icon' }); await downloadIcon(app); @@ -797,7 +801,7 @@ async function updateCommand(app, args, progressCallback) { await startApp(app); await progressCallback({ percent: 90, message: 'Configuring reverse proxy' }); - if (proxyAuthChanged || httpPortChanged) { + if (proxyAuthChanged || httpPortChanged || httpPortsChanged || removedSecondaryDomains.length) { await reverseProxy.configureApp(app, AuditSource.APPTASK); } diff --git a/src/reverseproxy.js b/src/reverseproxy.js index 868d894a8..1649161a2 100644 --- a/src/reverseproxy.js +++ b/src/reverseproxy.js @@ -482,7 +482,9 @@ async function writeAppConfigs(app) { const locations = getAppLocationsSync(app); - if (!safe.fs.mkdirSync(path.join(paths.NGINX_APPCONFIG_DIR, app.id), { recursive: true })) throw new BoxError(BoxError.FS_ERROR, `Could not create nginx config directory: ${safe.error.message}`); + const configDir = path.join(paths.NGINX_APPCONFIG_DIR, app.id); + safe.fs.rmSync(configDir, { recursive: true, force: true }); // remove any stale configs + if (!safe.fs.mkdirSync(configDir, { recursive: true })) throw new BoxError(BoxError.FS_ERROR, `Could not create nginx config directory: ${safe.error.message}`); for (const location of locations) { const certificatePath = await writeCertificate(location);