diff --git a/package-lock.json b/package-lock.json index 041196c..fe25826 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "domcloud-bridge", - "version": "0.49.0", + "version": "0.50.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "domcloud-bridge", - "version": "0.49.0", + "version": "0.50.0", "license": "MIT", "dependencies": { "axios": "^1.6.8", diff --git a/package.json b/package.json index 2de16f0..b37e1b7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "domcloud-bridge", - "version": "0.49.0", + "version": "0.50.0", "description": "Deployment runner for DOM Cloud", "main": "app.js", "engines": { diff --git a/src/executor/runner.js b/src/executor/runner.js index a92cf91..719279f 100644 --- a/src/executor/runner.js +++ b/src/executor/runner.js @@ -1,6 +1,5 @@ import { executeLock, - getDbName, getRevision, getVersion, spawnSudoUtil, @@ -187,10 +186,7 @@ export default async function runConfig(config, domain, writer, sandbox = false) for (const feature of config.features) { const key = typeof feature === 'string' ? splitLimit(feature, / /g, 2)[0] : Object.keys(feature)[0]; const value = typeof feature === 'string' ? feature.substring(key.length + 1) : feature[key]; - const isFeatureEnabled = ( /** @type {string} */ f) => { - return domaindata['Features'].includes(f); - } - let enabled; + if (!sandbox) { switch (key) { case 'modify': @@ -259,82 +255,6 @@ export default async function runConfig(config, domain, writer, sandbox = false) } } switch (key) { - case 'mysql': - case 'mariadb': - enabled = isFeatureEnabled('mysql'); - if (value === "off") { - await writeLog("$> Disabling MySQL"); - if (enabled) { - await virtExec("disable-feature", value, { - domain, - mysql: true, - }); - } else { - await writeLog("Already disabled"); - } - } else { - let dbname = null; - if (!enabled) { - await writeLog("$> Enabling MySQL"); - await virtExec("enable-feature", value, { - domain, - mysql: true, - }); - dbname = config.subdomain || "db"; - } - if (value.startsWith("create ")) { - dbname = value.substr("create ".length); - } - if (!dbname) { - break; - } - dbname = getDbName(domaindata['Username'], dbname); - await writeLog(`$> Creating db instance ${dbname} on MySQL`); - await virtExec("create-database", { - domain, - name: dbname, - type: 'mysql', - }); - } - break; - case 'postgres': - case 'postgresql': - enabled = isFeatureEnabled('postgres'); - if (value === "off") { - await writeLog("$> Disabling PostgreSQL"); - if (enabled) { - await virtExec("disable-feature", value, { - domain, - postgres: true, - }); - } else { - await writeLog("Already disabled"); - } - } else { - let dbname = null; - if (!enabled) { - await writeLog("$> Enabling PostgreSQL"); - await virtExec("enable-feature", value, { - domain, - postgres: true, - }); - dbname = config.subdomain || "db"; - } - if (value.startsWith("create ")) { - dbname = value.substr("create ".length); - } - if (!dbname) { - break; - } - dbname = getDbName(domaindata['Username'], dbname); - await writeLog(`$> Creating db instance ${dbname} on PostgreSQL`); - await virtExec("create-database", { - domain, - name: dbname, - type: 'postgres', - }); - } - break; case 'firewall': if (value === '' || value === 'on') { await writeLog("$> Changing firewall protection to " + (value || 'on')); @@ -354,7 +274,7 @@ export default async function runConfig(config, domain, writer, sandbox = false) } await sshExec('unset HISTFILE TERM', false); // https://stackoverflow.com/a/9039154/3908409 await sshExec(`export CI=true CONTINUOUS_INTEGRATION=true LANG=en_US.UTF-8 LC_ALL=en_US.UTF-8 PIP_PROGRESS_BAR=off`, false); - await sshExec(`DATABASE='${getDbName(domaindata['Username'])}' USERNAME='${domaindata['Username']}' PASSWORD='${domaindata['Password']}'`, false); + await sshExec(`USERNAME='${domaindata['Username']}' PASSWORD='${domaindata['Password']}'`, false); const firewallOn = await firewallStatus(); if (config.subdomain) { await runConfigSubdomain(config, domaindata, [config.subdomain, domain].join('.'), sshExec, writeLog, virtExec, firewallOn); diff --git a/src/executor/runnersub.js b/src/executor/runnersub.js index b5d2318..00646a2 100644 --- a/src/executor/runnersub.js +++ b/src/executor/runnersub.js @@ -1,7 +1,7 @@ import path from "path"; import { detectCanShareSSL, - escapeShell, getLtsPhp, spawnSudoUtil, splitLimit + escapeShell, getDbName, getLtsPhp, spawnSudoUtil, splitLimit } from "../util.js"; import { iptablesExec } from "./iptables.js"; import { namedExec } from "./named.js"; @@ -33,15 +33,105 @@ export async function runConfigSubdomain(config, domaindata, subdomain, sshExec, } } + let domainprefix = stillroot || !subdomaindata['Parent domain'] ? "db" : subdomain.slice(0, -(subdomaindata['Parent domain'].length + 1)); + let dbname = getDbName(domaindata['Username'], domainprefix); + const featureRunner = async (/** @type {object|string} */ feature) => { - const key = typeof feature === 'string' ? splitLimit(feature, / /g, 2)[0] : Object.keys(feature)[0]; + let key = typeof feature === 'string' ? splitLimit(feature, / /g, 2)[0] : Object.keys(feature)[0]; let value = typeof feature === 'string' ? feature.substring(key.length + 1) : feature[key]; + if (key == 'mariadb') { + key = 'mysql'; + } + if (key == 'postgresql') { + key = 'postgres'; + } + let enabled = domaindata['Features'].includes(key); + let subenabled = subdomaindata['Features'].includes(key); + let dbneedcreate = false; switch (key) { + case 'mysql': + if (!stillroot && !enabled) { + await writeLog("Problem: Can't manage MySQL while it is disabled in parent domain"); + break; + } + if (value === "off") { + await writeLog("$> Disabling MySQL"); + if (enabled) { + await virtExec("disable-feature", value, { + subdomain, + mysql: true, + }); + } else { + await writeLog("Already disabled"); + } + break; + } + if (!enabled) { + await writeLog("$> Enabling MySQL"); + await virtExec("enable-feature", value, { + subdomain, + mysql: true, + }); + dbneedcreate = true; + } + if (value.startsWith("create ")) { + let newdb = value.substr("create ".length).trim(); + dbname = getDbName(domaindata['Username'], domainprefix == "db" ? newdb : domainprefix + '_' + newdb); + dbneedcreate = true; + } + if (!dbneedcreate) { + break; + } + await writeLog(`$> Creating db instance ${dbname} on MySQL`); + await virtExec("create-database", { + subdomain, + name: dbname, + type: 'mysql', + }); + break; + case 'postgres': + if (!stillroot && !enabled) { + await writeLog("Problem: Can't manage PostgreSQL while it is disabled in parent domain"); + break; + } + if (value === "off") { + await writeLog("$> Disabling PostgreSQL"); + if (enabled) { + await virtExec("disable-feature", value, { + subdomain, + postgres: true, + }); + } else { + await writeLog("Already disabled"); + } + break; + } + if (!enabled) { + await writeLog("$> Enabling PostgreSQL"); + await virtExec("enable-feature", value, { + subdomain, + postgres: true, + }); + dbneedcreate = true; + } + if (value.startsWith("create ")) { + let newdb = value.substr("create ".length).trim(); + dbname = getDbName(domaindata['Username'], domainprefix == "db" ? newdb : domainprefix + '_' + newdb); + dbneedcreate = true; + } + if (!dbneedcreate) { + break; + } + await writeLog(`$> Creating db instance ${dbname} on PostgreSQL`); + await virtExec("create-database", { + subdomain, + name: dbname, + type: 'postgres', + }); + break; case 'dns': - let enabled = domaindata['Features'].includes('dns'); - let subenabled = subdomaindata['Features'].includes('dns'); if (!stillroot && !enabled) { - await writeLog("Problem: Can't manage DNS while parent domain DNS is disabled"); + await writeLog("Problem: Can't manage DNS while it is disabled in parent domain"); break; } if (value === "off") { @@ -54,39 +144,41 @@ export async function runConfigSubdomain(config, domaindata, subdomain, sshExec, } else { await writeLog("Already disabled"); } - } else { - if (!subenabled) { - await writeLog("$> Enabling DNS feature"); - await virtExec("enable-feature", value, { - domain: subdomain, - dns: true, - }); + break; + } + if (!subenabled) { + await writeLog("$> Enabling DNS feature"); + await virtExec("enable-feature", value, { + domain: subdomain, + dns: true, + }); + } + if (!Array.isArray(value)) { + break; + } + if (!stillroot) { + await writeLog("Problem: Can't manage DNS records on subdomain"); + break; + } + await writeLog("$> Applying DNS records"); + for (let i = 0; i < value.length; i++) { + if (typeof value[i] !== 'string') { + continue; + } + if (!value[i].startsWith("add ") && !value[i].startsWith("del ")) { + value[i] = `add ${value[i]}`; } - if (Array.isArray(value)) { - if (stillroot) { - await writeLog("$> Applying DNS records"); - for (let i = 0; i < value.length; i++) { - if (typeof value[i] === 'string') { - if (!value[i].startsWith("add ") && !value[i].startsWith("del ")) { - value[i] = `add ${value[i]}`; - } - const values = splitLimit(value[i] + '', / /g, 4); - if (values.length == 4) { - value[i] = { - action: values[0].toLowerCase() === 'del' ? 'del' : 'add', - type: values[1].toLowerCase(), - domain: values[2].toLowerCase(), - value: values[3], - }; - } - } - } - await writeLog(await namedExec.set(subdomain, value)); - } else { - await writeLog("Problem: Can't manage DNS records on subdomain"); - } + const values = splitLimit(value[i] + '', / /g, 4); + if (values.length == 4) { + value[i] = { + action: values[0].toLowerCase() === 'del' ? 'del' : 'add', + type: values[1].toLowerCase(), + domain: values[2].toLowerCase(), + value: values[3], + }; } } + await writeLog(await namedExec.set(subdomain, value)); break; case 'php': if (value == 'lts' || value == 'latest') { @@ -345,6 +437,7 @@ export async function runConfigSubdomain(config, domaindata, subdomain, sshExec, } } if (config.commands) { + await sshExec(`export DATABASE='${dbname}'`, false); if (config.envs) { let entries = Object.entries(config.envs); if (entries.length > 0) diff --git a/src/util.js b/src/util.js index 63a3b81..d80801e 100644 --- a/src/util.js +++ b/src/util.js @@ -406,7 +406,7 @@ export const normalizeShellOutput = function ( /** @type {string[]} */ output) { } export const getDbName = function ( /** @type {string} */ user, /** @type {string} */ db = 'db') { - return (`${user.replace(/-/g, '_')}_${db}`).replace(/[^a-zA-Z0-9_]/g, ''); + return (`${user.replace(/-/g, '_')}_${db.replace(/[-.]/g, '_')}`).replace(/[^a-zA-Z0-9_]/g, ''); } export const escapeNginx = function ( /** @type {string} */ str) {