diff --git a/dashboard/src/js/client.js b/dashboard/src/js/client.js index 765c775af..8ca7e045e 100644 --- a/dashboard/src/js/client.js +++ b/dashboard/src/js/client.js @@ -3736,7 +3736,7 @@ angular.module('Application').service('Client', ['$http', '$interval', '$timeout return 'Certificate(s) of ' + data.domains.join(',') + ' was cleaned up since they expired 6 months ago'; case ACTION_DASHBOARD_DOMAIN_UPDATE: - return 'Dashboard domain set to ' + data.fqdn; + return 'Dashboard domain set to ' + data.fqdn || (data.subdomain + '.' + data.domain); case ACTION_DOMAIN_ADD: return 'Domain ' + data.domain + ' with ' + data.provider + ' provider was added'; diff --git a/migrations/20230816133750-settings-rename-fqdn-to-subdomain.js b/migrations/20230816133750-settings-rename-fqdn-to-subdomain.js new file mode 100644 index 000000000..2432dba5a --- /dev/null +++ b/migrations/20230816133750-settings-rename-fqdn-to-subdomain.js @@ -0,0 +1,20 @@ +'use strict'; + +exports.up = async function(db) { + await db.runSql('UPDATE settings SET name=?,value=? WHERE name=?', [ 'dashboard_subdomain', 'my', 'admin_fqdn']); + await db.runSql('UPDATE settings SET name=? WHERE name=?', [ 'dashboard_domain', 'admin_domain']); + + let result = await db.runSql('SELECT * FROM settings WHERE name=?', [ 'mail_fqdn' ]); + if (!result.length) return; + const mailFqdn = result[0].value; + + result = await db.runSql('SELECT * FROM settings WHERE name=?', [ 'mail_domain' ]); + if (!result.length) return; + const mailDomain = result[0].value; + const mailSubdomain = mailFqdn.slice(0, -mailDomain.length-1); + + await db.runSql('UPDATE settings SET name=?,value=? WHERE name=?', [ 'mail_subdomain', mailSubdomain, 'mail_fqdn']); +}; + +exports.down = async function(/* db */) { +}; diff --git a/scripts/cloudron-support b/scripts/cloudron-support index ec9b84f5f..d24bf93f6 100755 --- a/scripts/cloudron-support +++ b/scripts/cloudron-support @@ -40,9 +40,9 @@ while true; do --owner-login) admin_username=$(mysql -NB -uroot -ppassword -e "SELECT username FROM box.users WHERE role='owner' AND username IS NOT NULL ORDER BY creationTime LIMIT 1" 2>/dev/null) admin_password=$(pwgen -1s 12) - dashboard_domain=$(mysql -NB -uroot -ppassword -e "SELECT value FROM box.settings WHERE name='admin_fqdn'" 2>/dev/null) + dashboard_domain=$(mysql -NB -uroot -ppassword -e "SELECT value FROM box.settings WHERE name='dashboard_domain'" 2>/dev/null) mysql -NB -uroot -ppassword -e "INSERT INTO box.settings (name, value) VALUES ('ghosts_config', '{\"${admin_username}\":\"${admin_password}\"}') ON DUPLICATE KEY UPDATE name='ghosts_config', value='{\"${admin_username}\":\"${admin_password}\"}'" 2>/dev/null - echo "Login at https://${dashboard_domain} as ${admin_username} / ${admin_password} . This password may only be used once." + echo "Login at https://my.${dashboard_domain} as ${admin_username} / ${admin_password} . This password may only be used once." exit 0 ;; --reset-appstore-account) @@ -50,8 +50,8 @@ while true; do read -e -p "Reset the Cloudron.io account? [y/N] " choice [[ "$choice" != [Yy]* ]] && exit 1 mysql -uroot -ppassword -e "DELETE FROM box.settings WHERE name='cloudron_token';" 2>/dev/null - dashboard_domain=$(mysql -NB -uroot -ppassword -e "SELECT value FROM box.settings WHERE name='admin_fqdn'" 2>/dev/null) - echo "Account reset. Please re-login at https://${dashboard_domain}/#/appstore" + dashboard_domain=$(mysql -NB -uroot -ppassword -e "SELECT value FROM box.settings WHERE name='dashboard_domain'" 2>/dev/null) + echo "Account reset. Please re-login at https://my.${dashboard_domain}/#/appstore" exit 0 ;; --) break;; @@ -114,7 +114,7 @@ echo -e $LINE"Ubuntu"$LINE >> $OUT lsb_release -a &>> $OUT echo -e $LINE"Dashboard Domain"$LINE >> $OUT -mysql -NB -uroot -ppassword -e "SELECT value FROM box.settings WHERE name='admin_fqdn'" &>> $OUT 2>/dev/null || true +mysql -NB -uroot -ppassword -e "SELECT value FROM box.settings WHERE name='dashboard_domain'" &>> $OUT 2>/dev/null || true echo -e $LINE"Docker containers"$LINE >> $OUT if ! timeout --kill-after 10s 15s docker ps -a &>> $OUT 2>&1; then diff --git a/src/dashboard.js b/src/dashboard.js index 195f1fbdf..5c86b6f4d 100644 --- a/src/dashboard.js +++ b/src/dashboard.js @@ -33,23 +33,24 @@ const apps = require('./apps.js'), async function getLocation() { const domain = await settings.get(settings.DASHBOARD_DOMAIN_KEY) || ''; - const fqdn = await settings.get(settings.DASHBOARD_FQDN_KEY) || ''; + const subdomain = await settings.get(settings.DASHBOARD_SUBDOMAIN_KEY) || ''; + const fqdn = domain ? dns.fqdn(subdomain, domain) : ''; - return { domain, fqdn, subdomain: fqdn ? constants.DASHBOARD_SUBDOMAIN : '' }; + return { subdomain, domain, fqdn }; } -async function setLocation(domain) { +async function setLocation(subdomain, domain) { + assert.strictEqual(typeof subdomain, 'string'); assert.strictEqual(typeof domain, 'string'); - const fqdn = domain ? dns.fqdn(constants.DASHBOARD_SUBDOMAIN, domain) : ''; + await settings.set(settings.DASHBOARD_SUBDOMAIN_KEY, subdomain); await settings.set(settings.DASHBOARD_DOMAIN_KEY, domain); - await settings.set(settings.DASHBOARD_FQDN_KEY, fqdn); debug(`setLocation: ${domain || ''}`); } async function clearLocation() { - await setLocation(''); + await setLocation('', ''); } async function getConfig() { @@ -110,7 +111,8 @@ async function prepareLocation(subdomain, domain, auditSource, progressCallback) await reverseProxy.ensureCertificate(location, {}, auditSource); } -async function setupLocation(domain, auditSource) { +async function setupLocation(subdomain, domain, auditSource) { + assert.strictEqual(typeof subdomain, 'string'); assert.strictEqual(typeof domain, 'string'); assert.strictEqual(typeof auditSource, 'object'); @@ -119,14 +121,13 @@ async function setupLocation(domain, auditSource) { if (constants.DEMO) throw new BoxError(BoxError.CONFLICT, 'Not allowed in demo mode'); await reverseProxy.writeDashboardConfig(domain); - const fqdn = dns.fqdn(constants.DASHBOARD_SUBDOMAIN, domain); const currentLocation = await getLocation(); - await setLocation(domain); + await setLocation(subdomain, domain); if (!currentLocation.domain) return; // first time location is set, no change notification needed debug('setupLocation: notifying platform of domain change'); - await eventlog.add(eventlog.ACTION_DASHBOARD_DOMAIN_UPDATE, auditSource, { domain, fqdn }); + await eventlog.add(eventlog.ACTION_DASHBOARD_DOMAIN_UPDATE, auditSource, { subdomain, domain }); await safe(appstore.updateCloudron({ domain }), { debug }); await platform.onDashboardLocationChanged(auditSource); } diff --git a/src/mailserver.js b/src/mailserver.js index f19a03701..ea2d91517 100644 --- a/src/mailserver.js +++ b/src/mailserver.js @@ -251,13 +251,12 @@ async function checkCertificate() { async function getLocation() { const domain = await settings.get(settings.MAIL_DOMAIN_KEY); - const fqdn = await settings.get(settings.MAIL_FQDN_KEY); + const subdomain = await settings.get(settings.MAIL_SUBDOMAIN_KEY); - if (!domain || !fqdn) return {}; + if (!domain || !subdomain) return {}; - const subdomain = fqdn.substr(0, fqdn.length - domain.length - 1); - - return { domain, fqdn, subdomain }; + const fqdn = dns.fqdn(subdomain, domain); + return { subdomain, domain, fqdn }; } async function changeLocation(auditSource, progressCallback) { @@ -289,12 +288,12 @@ async function changeLocation(auditSource, progressCallback) { await restartIfActivated(); } -async function setLocation(mailDomain, mailFqdn) { - assert.strictEqual(typeof mailDomain, 'string'); - assert.strictEqual(typeof mailFqdn, 'string'); +async function setLocation(subdomain, domain) { + assert.strictEqual(typeof subdomain, 'string'); + assert.strictEqual(typeof domain, 'string'); - await settings.set(settings.MAIL_DOMAIN_KEY, mailDomain); - await settings.set(settings.MAIL_FQDN_KEY, mailFqdn); + await settings.set(settings.MAIL_SUBDOMAIN_KEY, subdomain); + await settings.set(settings.MAIL_DOMAIN_KEY, domain); } async function startChangeLocation(subdomain, domain, auditSource) { @@ -307,9 +306,7 @@ async function startChangeLocation(subdomain, domain, auditSource) { const error = dns.validateHostname(subdomain, domain); if (error) throw new BoxError(BoxError.BAD_FIELD, `Bad mail location: ${error.message}`); - const fqdn = dns.fqdn(subdomain, domain); - - await setLocation(domain, fqdn); + await setLocation(subdomain, domain); const taskId = await tasks.add(tasks.TASK_CHANGE_MAIL_LOCATION, [ auditSource ]); tasks.startTask(taskId, {}); diff --git a/src/provision.js b/src/provision.js index 968809219..300276a72 100644 --- a/src/provision.js +++ b/src/provision.js @@ -79,7 +79,7 @@ async function setupTask(domain, auditSource) { await dns.waitForLocations([location], (progress) => setProgress('setup', progress.message)); await reverseProxy.ensureCertificate(location, {}, auditSource); await ensureDhparams(); - await dashboard.setupLocation(domain, auditSource); + await dashboard.setupLocation(constants.DASHBOARD_SUBDOMAIN, domain, auditSource); setProgress('setup', 'Done'), await eventlog.add(eventlog.ACTION_PROVISION, auditSource, {}); } catch (error) { @@ -119,7 +119,7 @@ async function setup(domainConfig, ipv4Config, auditSource) { dkimSelector: 'cloudron' }; - await mailServer.setLocation(domain, `${constants.DASHBOARD_SUBDOMAIN}.${domain}`); // default mail location. do this before we add the domain for upserting mail DNS + await mailServer.setLocation(constants.DASHBOARD_SUBDOMAIN, domain); // default mail location. do this before we add the domain for upserting mail DNS await domains.add(domain, data, auditSource); await network.setIPv4Config(ipv4Config); @@ -187,7 +187,7 @@ async function restoreTask(backupConfig, remotePath, ipv4Config, options, auditS await dns.waitForLocations([location], (progress) => setProgress('setup', progress.message)); await reverseProxy.ensureCertificate(location, {}, auditSource); } - await dashboard.setupLocation(location.domain, auditSource); + await dashboard.setupLocation(constants.DASHBOARD_SUBDOMAIN, location.domain, auditSource); delete backupConfig.rootPath; await backups.setConfig(backupConfig); diff --git a/src/routes/dashboard.js b/src/routes/dashboard.js index 6650c57f8..ca5ffb53c 100644 --- a/src/routes/dashboard.js +++ b/src/routes/dashboard.js @@ -9,6 +9,7 @@ exports = module.exports = { const AuditSource = require('../auditsource.js'), BoxError = require('../boxerror.js'), + constants = require('../constants.js'), dashboard = require('../dashboard.js'), HttpError = require('connect-lastmile').HttpError, HttpSuccess = require('connect-lastmile').HttpSuccess, @@ -33,7 +34,7 @@ async function startPrepareLocation(req, res, next) { async function setupLocation(req, res, next) { if (!req.body.domain || typeof req.body.domain !== 'string') return next(new HttpError(400, 'domain must be a string')); - const [error] = await safe(dashboard.setupLocation(req.body.domain, AuditSource.fromRequest(req))); + const [error] = await safe(dashboard.setupLocation(constants.DASHBOARD_SUBDOMAIN, req.body.domain, AuditSource.fromRequest(req))); if (error) return next(BoxError.toHttpError(error)); next(new HttpSuccess(204, {})); diff --git a/src/settings.js b/src/settings.js index 92fbc74f2..c35eec1a3 100644 --- a/src/settings.js +++ b/src/settings.js @@ -21,8 +21,8 @@ exports = module.exports = { CLOUDRON_ID_KEY: 'cloudron_id', CLOUDRON_NAME_KEY: 'cloudron_name', CONSOLE_SERVER_ORIGIN_KEY: 'console_server_origin', - DASHBOARD_DOMAIN_KEY: 'admin_domain', - DASHBOARD_FQDN_KEY: 'admin_fqdn', + DASHBOARD_DOMAIN_KEY: 'dashboard_domain', + DASHBOARD_SUBDOMAIN_KEY: 'dashboard_subdomain', DIRECTORY_SERVER_KEY: 'directory_server_config', DYNAMIC_DNS_KEY: 'dynamic_dns', EXTERNAL_LDAP_KEY: 'external_ldap_config', @@ -33,7 +33,7 @@ exports = module.exports = { IPV6_CONFIG_KEY: 'ipv6_config', LANGUAGE_KEY: 'language', MAIL_DOMAIN_KEY: 'mail_domain', - MAIL_FQDN_KEY: 'mail_fqdn', + MAIL_SUBDOMAIN_KEY: 'mail_subdomain', OIDC_COOKIE_SECRET_KEY: 'cookie_secret', PROFILE_CONFIG_KEY: 'profile_config', REGISTRY_CONFIG_KEY: 'registry_config', diff --git a/src/test/common.js b/src/test/common.js index def25cb56..7ba754e43 100644 --- a/src/test/common.js +++ b/src/test/common.js @@ -215,14 +215,14 @@ async function databaseSetup() { await database.initialize(); await database._clear(); await appstore._setApiServerOrigin(exports.mockApiServerOrigin); - await dashboard._setLocation(exports.dashboardDomain); + await dashboard._setLocation(constants.DASHBOARD_SUBDOMAIN, exports.dashboardDomain); } async function domainSetup() { nock.cleanAll(); await databaseSetup(); - await mailServer.setLocation(domain.domain, `${constants.DASHBOARD_SUBDOMAIN}.${domain.domain}`); // default mail location. do this before we add the domain for upserting mail DNS + await mailServer.setLocation(constants.DASHBOARD_SUBDOMAIN, domain.domain); // default mail location. do this before we add the domain for upserting mail DNS await domains.add(domain.domain, domain, auditSource); }