Files
cloudron-box/src/settings.js
T

432 lines
14 KiB
JavaScript
Raw Normal View History

'use strict';
exports = module.exports = {
getAutoupdatePattern,
setAutoupdatePattern,
2020-08-15 18:19:01 -07:00
getTimeZone,
setTimeZone,
2023-07-12 10:01:53 +05:30
getBackupPolicy,
setBackupPolicy,
2020-08-15 18:19:01 -07:00
getBackupConfig,
setBackupConfig,
2015-11-07 18:02:45 -08:00
2021-01-21 12:18:11 -08:00
getServicesConfig,
setServicesConfig,
2018-05-16 17:31:32 -07:00
2020-08-15 18:19:01 -07:00
getRegistryConfig,
setRegistryConfig,
2019-10-22 22:07:44 -07:00
2020-11-17 18:58:43 +01:00
getLanguage,
setLanguage,
2020-08-15 18:19:01 -07:00
provider,
2021-08-18 13:25:42 -07:00
list,
2020-08-15 18:19:01 -07:00
initCache,
2019-07-26 10:49:29 -07:00
// these values come from the cache
2020-08-15 18:19:01 -07:00
apiServerOrigin,
webServerOrigin,
consoleServerOrigin,
2021-05-05 12:29:04 -07:00
dashboardDomain,
setDashboardLocation,
2020-08-15 23:17:47 -07:00
setMailLocation,
mailFqdn,
mailDomain,
2021-05-05 12:29:04 -07:00
dashboardOrigin,
dashboardFqdn,
2019-07-26 10:49:29 -07:00
2020-08-15 18:19:01 -07:00
isDemo,
2019-07-26 10:49:29 -07:00
get,
set,
2023-08-02 19:17:22 +05:30
getBlob,
setBlob,
2021-09-20 18:04:01 +02:00
// booleans. if you add an entry here, be sure to fix list()
2017-01-02 13:05:48 +01:00
DYNAMIC_DNS_KEY: 'dynamic_dns',
2019-07-25 11:16:52 -07:00
DEMO_KEY: 'demo',
2017-07-18 13:31:43 -07:00
2021-09-20 18:04:01 +02:00
// json. if you add an entry here, be sure to fix list()
2015-11-07 18:02:45 -08:00
BACKUP_CONFIG_KEY: 'backup_config',
2023-07-12 10:01:53 +05:30
BACKUP_POLICY_KEY: 'backup_policy',
2021-01-21 12:18:11 -08:00
SERVICES_CONFIG_KEY: 'services_config',
EXTERNAL_LDAP_KEY: 'external_ldap_config',
2023-08-03 07:35:14 +05:30
DIRECTORY_SERVER_KEY: 'directory_server_config',
2019-10-22 22:07:44 -07:00
REGISTRY_CONFIG_KEY: 'registry_config',
IPV4_CONFIG_KEY: 'ipv4_config',
2020-02-05 14:30:56 -08:00
SUPPORT_CONFIG_KEY: 'support_config',
2022-01-13 14:34:02 -08:00
PROFILE_CONFIG_KEY: 'profile_config',
2021-09-20 13:05:42 +02:00
GHOSTS_CONFIG_KEY: 'ghosts_config',
2021-09-22 09:13:16 -07:00
REVERSE_PROXY_CONFIG_KEY: 'reverseproxy_config',
2022-02-15 13:12:34 -08:00
IPV6_CONFIG_KEY: 'ipv6_config',
2017-07-18 13:31:43 -07:00
// strings
AUTOUPDATE_PATTERN_KEY: 'autoupdate_pattern',
2017-07-18 13:31:43 -07:00
TIME_ZONE_KEY: 'time_zone',
2023-08-02 20:01:29 +05:30
OIDC_COOKIE_SECRET_KEY: 'cookie_secret',
2017-07-18 13:31:43 -07:00
CLOUDRON_NAME_KEY: 'cloudron_name',
2020-11-17 18:58:43 +01:00
LANGUAGE_KEY: 'language',
2019-04-30 22:57:43 -07:00
CLOUDRON_ID_KEY: 'cloudron_id',
2022-03-31 21:50:53 -07:00
APPSTORE_API_TOKEN_KEY: 'appstore_api_token',
2022-03-31 22:04:52 -07:00
APPSTORE_WEB_TOKEN_KEY: 'appstore_web_token',
FIREWALL_BLOCKLIST_KEY: 'firewall_blocklist',
2023-05-13 14:59:57 +02:00
TRUSTED_IPS_KEY: 'trusted_ips_key',
2017-07-18 13:31:43 -07:00
2019-07-25 11:16:52 -07:00
API_SERVER_ORIGIN_KEY: 'api_server_origin',
WEB_SERVER_ORIGIN_KEY: 'web_server_origin',
CONSOLE_SERVER_ORIGIN_KEY: 'console_server_origin',
2021-05-05 12:29:04 -07:00
DASHBOARD_DOMAIN_KEY: 'admin_domain',
DASHBOARD_FQDN_KEY: 'admin_fqdn',
2020-08-15 23:17:47 -07:00
MAIL_DOMAIN_KEY: 'mail_domain',
MAIL_FQDN_KEY: 'mail_fqdn',
2019-07-25 11:16:52 -07:00
PROVIDER_KEY: 'provider',
FOOTER_KEY: 'footer',
2019-01-25 16:33:22 -08:00
// blobs
2021-04-29 15:46:11 -07:00
CLOUDRON_AVATAR_KEY: 'cloudron_avatar',
2019-07-26 10:49:29 -07:00
// testing
2021-08-18 15:40:28 -07:00
_setApiServerOrigin: setApiServerOrigin,
2021-08-19 13:24:38 -07:00
_clear: clear,
_set: set
};
2021-01-20 11:10:17 -08:00
const assert = require('assert'),
2016-10-11 11:36:25 +02:00
backups = require('./backups.js'),
2019-10-22 11:03:56 -07:00
BoxError = require('./boxerror.js'),
2016-12-14 14:54:17 +01:00
constants = require('./constants.js'),
2019-03-04 10:25:18 -08:00
cron = require('./cron.js'),
CronJob = require('cron').CronJob,
2021-08-18 13:25:42 -07:00
database = require('./database.js'),
2019-07-26 10:49:29 -07:00
debug = require('debug')('box:settings'),
2019-10-22 22:07:44 -07:00
docker = require('./docker.js'),
2016-06-02 13:36:47 -07:00
moment = require('moment-timezone'),
2021-05-14 15:07:29 -07:00
mounts = require('./mounts.js'),
paths = require('./paths.js'),
safe = require('safetydance'),
2020-11-19 23:38:59 +01:00
translation = require('./translation.js'),
_ = require('underscore');
2021-08-18 13:25:42 -07:00
const SETTINGS_FIELDS = [ 'name', 'value' ].join(',');
const SETTINGS_BLOB_FIELDS = [ 'name', 'valueBlob' ].join(',');
2021-08-18 13:25:42 -07:00
2021-08-19 13:24:38 -07:00
const gDefaults = (function () {
2021-08-31 08:47:01 -07:00
const result = { };
result[exports.AUTOUPDATE_PATTERN_KEY] = cron.DEFAULT_AUTOUPDATE_PATTERN;
2023-05-07 20:46:32 +02:00
result[exports.TIME_ZONE_KEY] = 'UTC';
2020-11-17 18:58:43 +01:00
result[exports.LANGUAGE_KEY] = 'en';
2016-10-21 12:48:48 +02:00
result[exports.BACKUP_CONFIG_KEY] = {
provider: 'filesystem',
2023-08-01 17:38:48 +05:30
backupFolder: paths.DEFAULT_BACKUP_DIR,
2018-11-16 13:52:36 -08:00
format: 'tgz',
encryption: null,
2023-07-12 10:01:53 +05:30
};
result[exports.BACKUP_POLICY_KEY] = {
retention: { keepWithinSecs: 2 * 24 * 60 * 60 }, // 2 days
schedule: '00 00 23 * * *' // every day at 11pm
2016-10-21 12:48:48 +02:00
};
2021-01-21 12:18:11 -08:00
result[exports.SERVICES_CONFIG_KEY] = {};
2021-03-02 18:21:35 -08:00
result[exports.REGISTRY_CONFIG_KEY] = {
provider: 'noop'
};
2020-07-09 14:35:34 -07:00
2021-05-05 12:29:04 -07:00
result[exports.DASHBOARD_DOMAIN_KEY] = '';
result[exports.DASHBOARD_FQDN_KEY] = '';
2020-08-15 23:17:47 -07:00
result[exports.MAIL_DOMAIN_KEY] = '';
result[exports.MAIL_FQDN_KEY] = '';
2019-07-26 10:49:29 -07:00
result[exports.API_SERVER_ORIGIN_KEY] = 'https://api.cloudron.io';
result[exports.WEB_SERVER_ORIGIN_KEY] = 'https://cloudron.io';
2022-05-05 09:37:05 -07:00
result[exports.CONSOLE_SERVER_ORIGIN_KEY] = 'https://console.cloudron.io';
2019-07-26 10:49:29 -07:00
result[exports.DEMO_KEY] = false;
return result;
})();
2019-07-26 10:49:29 -07:00
let gCache = {};
2019-03-04 10:25:18 -08:00
function notifyChange(key, value) {
2019-03-04 15:45:04 -08:00
assert.strictEqual(typeof key, 'string');
// value is a variant
2019-03-04 10:25:18 -08:00
cron.handleSettingsChanged(key, value);
2017-02-07 10:30:52 -08:00
}
async function get(key) {
assert.strictEqual(typeof key, 'string');
const result = await database.query(`SELECT ${SETTINGS_FIELDS} FROM settings WHERE name = ?`, [ key ]);
2021-08-18 15:40:28 -07:00
if (result.length === 0) return null; // can't return the default value here because we might need to massage/json parse the result
return result[0].value;
}
async function set(key, value) {
assert.strictEqual(typeof key, 'string');
assert(value === null || typeof value === 'string');
await database.query('INSERT INTO settings (name, value) VALUES (?, ?) ON DUPLICATE KEY UPDATE value=VALUES(value)', [ key, value ]); // don't rely on affectedRows here since it gives 2
}
async function getBlob(key) {
assert.strictEqual(typeof key, 'string');
const result = await database.query(`SELECT ${SETTINGS_BLOB_FIELDS} FROM settings WHERE name = ?`, [ key ]);
if (result.length === 0) return null;
return result[0].valueBlob;
}
async function setBlob(key, value) {
assert.strictEqual(typeof key, 'string');
assert(value === null || Buffer.isBuffer(value));
await database.query('INSERT INTO settings (name, valueBlob) VALUES (?, ?) ON DUPLICATE KEY UPDATE valueBlob=VALUES(valueBlob)', [ key, value ]); // don't rely on affectedRows here since it gives 2
}
async function clear() {
await database.query('DELETE FROM settings');
}
2021-08-19 13:24:38 -07:00
async function setAutoupdatePattern(pattern) {
assert.strictEqual(typeof pattern, 'string');
if (pattern !== constants.AUTOUPDATE_PATTERN_NEVER) { // check if pattern is valid
2021-08-19 13:24:38 -07:00
const job = safe.safeCall(function () { return new CronJob(pattern); });
2022-02-07 13:19:59 -08:00
if (!job) throw new BoxError(BoxError.BAD_FIELD, 'Invalid pattern');
}
2021-08-19 13:24:38 -07:00
await set(exports.AUTOUPDATE_PATTERN_KEY, pattern);
notifyChange(exports.AUTOUPDATE_PATTERN_KEY, pattern);
}
2021-08-19 13:24:38 -07:00
async function getAutoupdatePattern() {
const pattern = await get(exports.AUTOUPDATE_PATTERN_KEY);
if (pattern === null) return gDefaults[exports.AUTOUPDATE_PATTERN_KEY];
return pattern;
}
2021-08-19 13:24:38 -07:00
async function setTimeZone(tz) {
assert.strictEqual(typeof tz, 'string');
2016-06-02 13:36:47 -07:00
2022-02-07 13:19:59 -08:00
if (moment.tz.names().indexOf(tz) === -1) throw new BoxError(BoxError.BAD_FIELD, 'Bad timeZone');
2021-08-19 13:24:38 -07:00
await set(exports.TIME_ZONE_KEY, tz);
notifyChange(exports.TIME_ZONE_KEY, tz);
}
2021-08-19 13:24:38 -07:00
async function getTimeZone() {
const tz = await get(exports.TIME_ZONE_KEY);
if (tz === null) return gDefaults[exports.TIME_ZONE_KEY];
return tz;
}
2023-07-12 10:01:53 +05:30
async function getBackupPolicy() {
const result = await get(exports.BACKUP_POLICY_KEY);
if (result === null) return gDefaults[exports.BACKUP_POLICY_KEY];
return JSON.parse(result);
}
async function setBackupPolicy(policy) {
assert.strictEqual(typeof policy, 'object');
const error = await backups.validatePolicy(policy);
if (error) throw error;
await set(exports.BACKUP_POLICY_KEY, JSON.stringify(policy));
notifyChange(exports.BACKUP_POLICY_KEY, policy);
}
2021-08-19 13:24:38 -07:00
async function getBackupConfig() {
const value = await get(exports.BACKUP_CONFIG_KEY);
if (value === null) return gDefaults[exports.BACKUP_CONFIG_KEY];
2015-11-07 18:02:45 -08:00
const backupConfig = JSON.parse(value); // { provider, token, password, region, prefix, bucket }
2021-08-19 13:24:38 -07:00
return backupConfig;
2015-11-07 18:02:45 -08:00
}
2021-08-19 13:24:38 -07:00
async function setBackupConfig(backupConfig) {
2015-11-07 18:02:45 -08:00
assert.strictEqual(typeof backupConfig, 'object');
2021-08-19 13:24:38 -07:00
const oldConfig = await getBackupConfig();
2021-08-19 13:24:38 -07:00
backups.injectPrivateFields(backupConfig, oldConfig);
2023-01-24 14:53:51 +01:00
if (mounts.isManagedProvider(backupConfig.provider)) {
2021-08-19 13:24:38 -07:00
let error = mounts.validateMountOptions(backupConfig.provider, backupConfig.mountOptions);
if (error) throw error;
2021-06-21 22:37:32 -07:00
[error] = await safe(mounts.tryAddMount(mounts.mountObjectFromBackupConfig(backupConfig), { timeout: 10 })); // 10 seconds
2021-06-21 22:37:32 -07:00
2021-08-19 13:24:38 -07:00
if (error) {
2022-01-26 12:40:28 -08:00
if (mounts.isManagedProvider(oldConfig.provider)) { // put back the old mount configuration
2021-08-19 13:24:38 -07:00
debug('setBackupConfig: rolling back to previous mount configuration');
2021-06-21 22:37:32 -07:00
await safe(mounts.tryAddMount(mounts.mountObjectFromBackupConfig(oldConfig), { timeout: 10 }));
2021-06-21 22:37:32 -07:00
}
2021-08-19 13:24:38 -07:00
throw error;
}
2021-08-19 13:24:38 -07:00
}
2019-02-09 18:08:10 -08:00
const error = await backups.testConfig(backupConfig);
if (error) throw error;
2020-05-12 14:00:05 -07:00
2021-08-19 13:24:38 -07:00
if ('password' in backupConfig) { // user set password
2023-07-13 12:42:38 +05:30
const error = await backups.validateEncryptionPassword(backupConfig.password);
if (error) throw error;
2021-08-19 13:24:38 -07:00
backupConfig.encryption = backups.generateEncryptionKeysSync(backupConfig.password);
delete backupConfig.password;
}
// if any of these changes, we have to clear the cache
if (!_.isEqual(_.omit(backupConfig, 'limits'), _.omit(oldConfig, 'limits'))) {
2021-08-19 13:24:38 -07:00
debug('setBackupConfig: clearing backup cache');
backups.cleanupCacheFilesSync();
}
2019-02-08 22:21:17 -08:00
2021-08-19 13:24:38 -07:00
await set(exports.BACKUP_CONFIG_KEY, JSON.stringify(backupConfig));
2021-05-14 15:07:29 -07:00
2022-01-26 12:40:28 -08:00
if (mounts.isManagedProvider(oldConfig.provider) && !mounts.isManagedProvider(backupConfig.provider)) {
2021-08-19 13:24:38 -07:00
debug('setBackupConfig: removing old backup mount point');
await safe(mounts.removeMount(mounts.mountObjectFromBackupConfig(oldConfig)));
2021-08-19 13:24:38 -07:00
}
2021-08-19 13:24:38 -07:00
notifyChange(exports.BACKUP_CONFIG_KEY, backupConfig);
2015-11-07 18:02:45 -08:00
}
2021-08-19 13:24:38 -07:00
async function getServicesConfig() {
const value = await get(exports.SERVICES_CONFIG_KEY);
if (value === null) return gDefaults[exports.SERVICES_CONFIG_KEY];
return JSON.parse(value);
2018-05-16 17:31:32 -07:00
}
2021-08-19 13:24:38 -07:00
async function setServicesConfig(platformConfig) {
await set(exports.SERVICES_CONFIG_KEY, JSON.stringify(platformConfig));
notifyChange(exports.SERVICES_CONFIG_KEY, platformConfig);
2018-05-16 17:31:32 -07:00
}
2021-08-19 13:24:38 -07:00
async function getRegistryConfig() {
const value = await get(exports.REGISTRY_CONFIG_KEY);
if (value === null) return gDefaults[exports.REGISTRY_CONFIG_KEY];
return JSON.parse(value);
2019-10-22 22:07:44 -07:00
}
2021-08-19 13:24:38 -07:00
async function setRegistryConfig(registryConfig) {
2019-10-22 22:07:44 -07:00
assert.strictEqual(typeof registryConfig, 'object');
2021-08-19 13:24:38 -07:00
const currentConfig = await getRegistryConfig();
2019-10-22 22:07:44 -07:00
2021-08-19 13:24:38 -07:00
docker.injectPrivateFields(registryConfig, currentConfig);
2019-10-22 22:07:44 -07:00
2021-08-25 19:41:46 -07:00
await docker.testRegistryConfig(registryConfig);
2019-10-22 22:07:44 -07:00
2021-08-19 13:24:38 -07:00
await set(exports.REGISTRY_CONFIG_KEY, JSON.stringify(registryConfig));
2019-10-22 22:07:44 -07:00
2021-08-19 13:24:38 -07:00
notifyChange(exports.REGISTRY_CONFIG_KEY, registryConfig);
2019-10-22 22:07:44 -07:00
}
2021-08-19 11:00:35 -07:00
async function getLanguage() {
const value = await get(exports.LANGUAGE_KEY);
if (value === null) return gDefaults[exports.LANGUAGE_KEY];
return value;
2020-11-17 18:58:43 +01:00
}
2021-08-19 11:00:35 -07:00
async function setLanguage(language) {
2020-11-17 18:58:43 +01:00
assert.strictEqual(typeof language, 'string');
2021-08-19 11:00:35 -07:00
const languages = await translation.getLanguages();
2020-11-18 00:10:06 +01:00
2021-08-19 11:00:35 -07:00
if (languages.indexOf(language) === -1) throw new BoxError(BoxError.NOT_FOUND, 'Language not found');
2020-11-18 00:10:06 +01:00
2021-08-19 11:00:35 -07:00
await set(exports.LANGUAGE_KEY, language);
notifyChange(exports.LANGUAGE_KEY, language);
2020-11-17 18:58:43 +01:00
}
2021-08-18 13:25:42 -07:00
async function list() {
const settings = await database.query(`SELECT ${SETTINGS_FIELDS} FROM settings WHERE value IS NOT NULL ORDER BY name`);
const result = Object.assign({}, gDefaults);
2021-08-18 13:25:42 -07:00
settings.forEach(function (setting) { result[setting.name] = setting.value; });
2021-08-18 13:25:42 -07:00
// convert booleans
result[exports.DEMO_KEY] = !!result[exports.DEMO_KEY];
2017-01-02 13:47:49 +01:00
2021-08-18 13:25:42 -07:00
// convert JSON objects
2023-08-03 08:11:42 +05:30
[exports.BACKUP_POLICY_KEY, exports.BACKUP_CONFIG_KEY, exports.SERVICES_CONFIG_KEY,
exports.REGISTRY_CONFIG_KEY ].forEach(function (key) {
2021-08-18 13:25:42 -07:00
result[key] = typeof result[key] === 'object' ? result[key] : safe.JSON.parse(result[key]);
});
2021-08-18 13:25:42 -07:00
return result;
}
2019-07-26 10:49:29 -07:00
2021-08-18 13:25:42 -07:00
async function initCache() {
2019-07-26 10:49:29 -07:00
debug('initCache: pre-load settings');
2021-08-18 13:25:42 -07:00
const allSettings = await list();
const provider = safe.fs.readFileSync(paths.PROVIDER_FILE, 'utf8');
gCache = {
apiServerOrigin: allSettings[exports.API_SERVER_ORIGIN_KEY],
webServerOrigin: allSettings[exports.WEB_SERVER_ORIGIN_KEY],
consoleServerOrigin: allSettings[exports.CONSOLE_SERVER_ORIGIN_KEY],
2021-08-18 13:25:42 -07:00
dashboardDomain: allSettings[exports.DASHBOARD_DOMAIN_KEY],
dashboardFqdn: allSettings[exports.DASHBOARD_FQDN_KEY],
mailDomain: allSettings[exports.MAIL_DOMAIN_KEY],
mailFqdn: allSettings[exports.MAIL_FQDN_KEY],
isDemo: allSettings[exports.DEMO_KEY],
provider: provider ? provider.trim() : 'generic'
};
2019-07-26 10:49:29 -07:00
}
// this is together so we can do this in a transaction later
2021-08-19 13:24:38 -07:00
async function setDashboardLocation(dashboardDomain, dashboardFqdn) {
assert.strictEqual(typeof dashboardDomain, 'string');
2021-05-05 12:29:04 -07:00
assert.strictEqual(typeof dashboardFqdn, 'string');
2019-07-26 10:49:29 -07:00
2021-08-19 13:24:38 -07:00
await set(exports.DASHBOARD_DOMAIN_KEY, dashboardDomain);
await set(exports.DASHBOARD_FQDN_KEY, dashboardFqdn);
2019-07-26 10:49:29 -07:00
2021-08-19 13:24:38 -07:00
gCache.dashboardDomain = dashboardDomain;
gCache.dashboardFqdn = dashboardFqdn;
2019-07-26 10:49:29 -07:00
}
2021-08-19 13:24:38 -07:00
async function setMailLocation(mailDomain, mailFqdn) {
2020-08-15 23:17:47 -07:00
assert.strictEqual(typeof mailDomain, 'string');
assert.strictEqual(typeof mailFqdn, 'string');
2021-08-19 13:24:38 -07:00
await set(exports.MAIL_DOMAIN_KEY, mailDomain);
await set(exports.MAIL_FQDN_KEY, mailFqdn);
2020-08-15 23:17:47 -07:00
2021-08-19 13:24:38 -07:00
gCache.mailDomain = mailDomain;
gCache.mailFqdn = mailFqdn;
2020-08-15 23:17:47 -07:00
}
2021-08-19 13:24:38 -07:00
async function setApiServerOrigin(origin) {
2019-07-26 10:49:29 -07:00
assert.strictEqual(typeof origin, 'string');
2021-08-19 13:24:38 -07:00
await set(exports.API_SERVER_ORIGIN_KEY, origin);
2019-07-26 10:49:29 -07:00
2021-08-19 13:24:38 -07:00
gCache.apiServerOrigin = origin;
notifyChange(exports.API_SERVER_ORIGIN_KEY, origin);
2019-07-26 10:49:29 -07:00
}
2019-10-29 15:46:33 -07:00
function provider() { return gCache.provider; }
2019-07-26 10:49:29 -07:00
function apiServerOrigin() { return gCache.apiServerOrigin; }
function webServerOrigin() { return gCache.webServerOrigin; }
function consoleServerOrigin() { return gCache.consoleServerOrigin; }
2021-05-05 12:29:04 -07:00
function dashboardDomain() { return gCache.dashboardDomain; }
function dashboardFqdn() { return gCache.dashboardFqdn; }
2019-07-26 10:49:29 -07:00
function isDemo() { return gCache.isDemo; }
2020-08-15 23:17:47 -07:00
function mailDomain() { return gCache.mailDomain; }
function mailFqdn() { return gCache.mailFqdn; }
2021-05-05 12:29:04 -07:00
function dashboardOrigin() { return 'https://' + dashboardFqdn(); }