@geekmidas/cli 10.0.0-alpha.121 → 10.0.0-alpha.123
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{GoDaddyProvider-D0MQoOM4.mjs → GoDaddyProvider-BlB5nJky.mjs} +2 -2
- package/dist/{GoDaddyProvider-D0MQoOM4.mjs.map → GoDaddyProvider-BlB5nJky.mjs.map} +1 -1
- package/dist/{GoDaddyProvider-3C8U_tVu.cjs → GoDaddyProvider-Dubq-pCc.cjs} +2 -2
- package/dist/{GoDaddyProvider-3C8U_tVu.cjs.map → GoDaddyProvider-Dubq-pCc.cjs.map} +1 -1
- package/dist/{LegacyStateStore-DHsoQGOo.cjs → LegacyStateStore-ClvUC8qy.cjs} +3 -3
- package/dist/{LegacyStateStore-DHsoQGOo.cjs.map → LegacyStateStore-ClvUC8qy.cjs.map} +1 -1
- package/dist/{LegacyStateStore-CRi7TosX.mjs → LegacyStateStore-DGALQGZu.mjs} +3 -3
- package/dist/{LegacyStateStore-CRi7TosX.mjs.map → LegacyStateStore-DGALQGZu.mjs.map} +1 -1
- package/dist/{LocalStateStore-BQXTkeoS.cjs → LocalStateStore-43kSYNpw.cjs} +2 -2
- package/dist/{LocalStateStore-BQXTkeoS.cjs.map → LocalStateStore-43kSYNpw.cjs.map} +1 -1
- package/dist/{LocalStateStore-Dkth0jXX.mjs → LocalStateStore-CBYUzVR5.mjs} +2 -2
- package/dist/{LocalStateStore-Dkth0jXX.mjs.map → LocalStateStore-CBYUzVR5.mjs.map} +1 -1
- package/dist/{S3StateStore-pZZnLLBd.mjs → S3StateStore-DLHjDYYP.mjs} +2 -2
- package/dist/{S3StateStore-pZZnLLBd.mjs.map → S3StateStore-DLHjDYYP.mjs.map} +1 -1
- package/dist/{S3StateStore-DGKZgyKz.cjs → S3StateStore-ahE7hSNK.cjs} +2 -2
- package/dist/{S3StateStore-DGKZgyKz.cjs.map → S3StateStore-ahE7hSNK.cjs.map} +1 -1
- package/dist/{SSMStateStore-CZRMERDA.mjs → SSMStateStore-j5RuMkch.mjs} +2 -2
- package/dist/{SSMStateStore-CZRMERDA.mjs.map → SSMStateStore-j5RuMkch.mjs.map} +1 -1
- package/dist/{SSMStateStore-DzkyjQ5D.cjs → SSMStateStore-qIzIuazA.cjs} +2 -2
- package/dist/{SSMStateStore-DzkyjQ5D.cjs.map → SSMStateStore-qIzIuazA.cjs.map} +1 -1
- package/dist/{add-C0kXOsWS.mjs → add-Bm-C56lH.mjs} +4 -4
- package/dist/{add-C0kXOsWS.mjs.map → add-Bm-C56lH.mjs.map} +1 -1
- package/dist/{add-Df8cmz0H.cjs → add-C2CvXrYw.cjs} +4 -4
- package/dist/{add-Df8cmz0H.cjs.map → add-C2CvXrYw.cjs.map} +1 -1
- package/dist/{bundler-Dx_grUmc.cjs → bundler-Dk7tAvAX.cjs} +5 -5
- package/dist/{bundler-Dx_grUmc.cjs.map → bundler-Dk7tAvAX.cjs.map} +1 -1
- package/dist/{bundler-B-I4Du99.mjs → bundler-DnkFt4mn.mjs} +5 -5
- package/dist/{bundler-B-I4Du99.mjs.map → bundler-DnkFt4mn.mjs.map} +1 -1
- package/dist/{cli-DL7GxGAe.mjs → cli-CVeHFXNE.mjs} +2 -2
- package/dist/{cli-DL7GxGAe.mjs.map → cli-CVeHFXNE.mjs.map} +1 -1
- package/dist/{cli-Cd9exatJ.cjs → cli-Di464j1O.cjs} +2 -2
- package/dist/{cli-Cd9exatJ.cjs.map → cli-Di464j1O.cjs.map} +1 -1
- package/dist/{compose-Mdgea4Yt.cjs → compose-ChVvb3xM.cjs} +65 -13
- package/dist/compose-ChVvb3xM.cjs.map +1 -0
- package/dist/{compose-C43c03WZ.mjs → compose-cHkF---f.mjs} +65 -13
- package/dist/compose-cHkF---f.mjs.map +1 -0
- package/dist/{config-D48erO2j.d.mts → config-B3iz9BtK.d.cts} +4 -4
- package/dist/{config-D48erO2j.d.mts.map → config-B3iz9BtK.d.cts.map} +1 -1
- package/dist/{config-DHDtsQrB.d.cts → config-DrOX29sT.d.mts} +4 -4
- package/dist/{config-DHDtsQrB.d.cts.map → config-DrOX29sT.d.mts.map} +1 -1
- package/dist/config.cjs +1 -1
- package/dist/config.d.cts +3 -3
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +1 -1
- package/dist/deploy/public.cjs +7 -7
- package/dist/deploy/public.d.cts +5 -5
- package/dist/deploy/public.d.cts.map +1 -1
- package/dist/deploy/public.d.mts +5 -5
- package/dist/deploy/public.d.mts.map +1 -1
- package/dist/deploy/public.mjs +7 -7
- package/dist/{deploy-BMM2hx-K.cjs → deploy-BffR_uEN.cjs} +7 -6
- package/dist/{deploy-BMM2hx-K.cjs.map → deploy-BffR_uEN.cjs.map} +1 -1
- package/dist/{deploy-D5YhwDng.mjs → deploy-DO4djCHb.mjs} +7 -6
- package/dist/{deploy-D5YhwDng.mjs.map → deploy-DO4djCHb.mjs.map} +1 -1
- package/dist/{discover-CSW_BMWa.d.cts → discover-C08meHhu.d.mts} +2 -2
- package/dist/{discover-CSW_BMWa.d.cts.map → discover-C08meHhu.d.mts.map} +1 -1
- package/dist/{discover-xtT7fgWg.d.mts → discover-DipvfWpK.d.cts} +2 -2
- package/dist/{discover-xtT7fgWg.d.mts.map → discover-DipvfWpK.d.cts.map} +1 -1
- package/dist/{dns-BIYf20p7.cjs → dns-Bmnuttja.cjs} +346 -27
- package/dist/dns-Bmnuttja.cjs.map +1 -0
- package/dist/{dns-agf2VCdV.mjs → dns-DFvO5a53.mjs} +317 -30
- package/dist/dns-DFvO5a53.mjs.map +1 -0
- package/dist/{dokploy-2p4PK6ej.mjs → dokploy-BcZm93Cc.mjs} +2 -2
- package/dist/{dokploy-2p4PK6ej.mjs.map → dokploy-BcZm93Cc.mjs.map} +1 -1
- package/dist/{dokploy-BANMTroy.cjs → dokploy-CsdvfH3t.cjs} +2 -2
- package/dist/{dokploy-BANMTroy.cjs.map → dokploy-CsdvfH3t.cjs.map} +1 -1
- package/dist/{eas-iT2JDHCh.mjs → eas-CBShP20v.mjs} +2 -2
- package/dist/{eas-iT2JDHCh.mjs.map → eas-CBShP20v.mjs.map} +1 -1
- package/dist/{eas-CNi16SoO.cjs → eas-CN_cUXth.cjs} +2 -2
- package/dist/{eas-CNi16SoO.cjs.map → eas-CN_cUXth.cjs.map} +1 -1
- package/dist/{encryption-DkXVbyZc.mjs → encryption-CITa6dXL.mjs} +3 -3
- package/dist/{encryption-DkXVbyZc.mjs.map → encryption-CITa6dXL.mjs.map} +1 -1
- package/dist/{encryption-d2lLtkYx.cjs → encryption-CPDM49Hi.cjs} +3 -3
- package/dist/{encryption-d2lLtkYx.cjs.map → encryption-CPDM49Hi.cjs.map} +1 -1
- package/dist/{engine-CatKW6E-.mjs → engine-CTnHmHjz.mjs} +4 -4
- package/dist/{engine-CatKW6E-.mjs.map → engine-CTnHmHjz.mjs.map} +1 -1
- package/dist/{engine-DlEHexED.cjs → engine-D-jfMVj-.cjs} +4 -4
- package/dist/{engine-DlEHexED.cjs.map → engine-D-jfMVj-.cjs.map} +1 -1
- package/dist/{github-DErsqKGN.mjs → github-9RlA7EjN.mjs} +5 -5
- package/dist/{github-DErsqKGN.mjs.map → github-9RlA7EjN.mjs.map} +1 -1
- package/dist/{github-BAeaC1Pl.cjs → github-D56PSK39.cjs} +5 -5
- package/dist/{github-BAeaC1Pl.cjs.map → github-D56PSK39.cjs.map} +1 -1
- package/dist/{googlePlay-BXJ5ZjMK.mjs → googlePlay-COi5LXON.mjs} +3 -3
- package/dist/{googlePlay-BXJ5ZjMK.mjs.map → googlePlay-COi5LXON.mjs.map} +1 -1
- package/dist/{googlePlay-BVcfFWRO.cjs → googlePlay-SX_jQeJf.cjs} +3 -3
- package/dist/{googlePlay-BVcfFWRO.cjs.map → googlePlay-SX_jQeJf.cjs.map} +1 -1
- package/dist/{index-8OfpOtSI.d.cts → index-DcyRA5Rf.d.mts} +2 -2
- package/dist/{index-8OfpOtSI.d.cts.map → index-DcyRA5Rf.d.mts.map} +1 -1
- package/dist/{index-Dpji6Bxq.d.mts → index-DjLSMgAL.d.cts} +2 -2
- package/dist/{index-Dpji6Bxq.d.mts.map → index-DjLSMgAL.d.cts.map} +1 -1
- package/dist/index.cjs +53 -53
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +53 -53
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-s4QizIoM.mjs → migrate-0J6gsfrQ.mjs} +3 -3
- package/dist/{migrate-s4QizIoM.mjs.map → migrate-0J6gsfrQ.mjs.map} +1 -1
- package/dist/{migrate-L4a273dY.cjs → migrate-BCz1lkh_.cjs} +3 -3
- package/dist/{migrate-L4a273dY.cjs.map → migrate-BCz1lkh_.cjs.map} +1 -1
- package/dist/{migrate-D79GI0XM.cjs → migrate-CrtxXeGY.cjs} +3 -3
- package/dist/{migrate-D79GI0XM.cjs.map → migrate-CrtxXeGY.cjs.map} +1 -1
- package/dist/{migrate-Bb1MmGHl.mjs → migrate-DJa_Dlsl.mjs} +3 -3
- package/dist/{migrate-Bb1MmGHl.mjs.map → migrate-DJa_Dlsl.mjs.map} +1 -1
- package/dist/openapi.cjs +1 -1
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +1 -1
- package/dist/{publish-CcTlmJT6.mjs → publish-CuQlOW5p.mjs} +2 -2
- package/dist/{publish-CcTlmJT6.mjs.map → publish-CuQlOW5p.mjs.map} +1 -1
- package/dist/{publish-ydscCYsW.cjs → publish-qSpnvmdX.cjs} +2 -2
- package/dist/{publish-ydscCYsW.cjs.map → publish-qSpnvmdX.cjs.map} +1 -1
- package/dist/reconcile/public.d.cts +2 -2
- package/dist/reconcile/public.d.mts +2 -2
- package/dist/{resolve-BgL-brF9.cjs → resolve-AsfohXd1.cjs} +3 -3
- package/dist/{resolve-BgL-brF9.cjs.map → resolve-AsfohXd1.cjs.map} +1 -1
- package/dist/{resolve-BLp2trep.mjs → resolve-j8hUcbGz.mjs} +3 -3
- package/dist/{resolve-BLp2trep.mjs.map → resolve-j8hUcbGz.mjs.map} +1 -1
- package/dist/{rollback-2cyLXEH1.cjs → rollback-Bxq_URgL.cjs} +5 -5
- package/dist/{rollback-2cyLXEH1.cjs.map → rollback-Bxq_URgL.cjs.map} +1 -1
- package/dist/{rollback-DwqZ8zzj.mjs → rollback-JgbAY43C.mjs} +5 -5
- package/dist/{rollback-DwqZ8zzj.mjs.map → rollback-JgbAY43C.mjs.map} +1 -1
- package/dist/{runtime-YyQuekxi.d.mts → runtime-DIZTeGJV.d.cts} +2 -2
- package/dist/{runtime-ACdfK59n.d.cts.map → runtime-DIZTeGJV.d.cts.map} +1 -1
- package/dist/{runtime-ACdfK59n.d.cts → runtime-WMLJY-db.d.mts} +2 -2
- package/dist/{runtime-YyQuekxi.d.mts.map → runtime-WMLJY-db.d.mts.map} +1 -1
- package/dist/{s3-DQXZdmCM.mjs → s3-B4FTQQkV.mjs} +2 -2
- package/dist/{s3-DQXZdmCM.mjs.map → s3-B4FTQQkV.mjs.map} +1 -1
- package/dist/{s3-CrPoA89T.cjs → s3-DwKLXwUT.cjs} +2 -2
- package/dist/{s3-CrPoA89T.cjs.map → s3-DwKLXwUT.cjs.map} +1 -1
- package/dist/{sandbox-CdKgQ3XU.d.cts → sandbox-Co1y2CmF.d.mts} +2 -2
- package/dist/{sandbox-C4g1eG31.d.mts.map → sandbox-Co1y2CmF.d.mts.map} +1 -1
- package/dist/{sandbox-C4g1eG31.d.mts → sandbox-DvNqr6od.d.cts} +2 -2
- package/dist/{sandbox-CdKgQ3XU.d.cts.map → sandbox-DvNqr6od.d.cts.map} +1 -1
- package/dist/{seeds-C2w4UqVP.cjs → seeds-BWC-qjSI.cjs} +11 -5
- package/dist/{seeds-C2w4UqVP.cjs.map → seeds-BWC-qjSI.cjs.map} +1 -1
- package/dist/{seeds-Cwtv-U9-.mjs → seeds-Dv78P9LS.mjs} +6 -6
- package/dist/{seeds-Cwtv-U9-.mjs.map → seeds-Dv78P9LS.mjs.map} +1 -1
- package/dist/{serverSetup-BdFOD93s.cjs → serverSetup-CtV80a5M.cjs} +6 -6
- package/dist/{serverSetup-BdFOD93s.cjs.map → serverSetup-CtV80a5M.cjs.map} +1 -1
- package/dist/{serverSetup-utKsoyGH.mjs → serverSetup-DB6sKwAy.mjs} +6 -6
- package/dist/{serverSetup-utKsoyGH.mjs.map → serverSetup-DB6sKwAy.mjs.map} +1 -1
- package/dist/{sniffer-CF7wNxMo.cjs → sniffer-ctpwBBkT.cjs} +7 -7
- package/dist/{sniffer-CF7wNxMo.cjs.map → sniffer-ctpwBBkT.cjs.map} +1 -1
- package/dist/{sniffer-8C4q6Szd.mjs → sniffer-qDFUQmWP.mjs} +7 -7
- package/dist/{sniffer-8C4q6Szd.mjs.map → sniffer-qDFUQmWP.mjs.map} +1 -1
- package/dist/{stage-BvM_u2z4.mjs → stage-C7lf36az.mjs} +5 -5
- package/dist/{stage-BvM_u2z4.mjs.map → stage-C7lf36az.mjs.map} +1 -1
- package/dist/{stage-DEQJGW3r.cjs → stage-DzALYUwY.cjs} +5 -5
- package/dist/{stage-DEQJGW3r.cjs.map → stage-DzALYUwY.cjs.map} +1 -1
- package/dist/{stages-D4SnG5yl.cjs → stages-B7mCWI_R.cjs} +2 -2
- package/dist/{stages-D4SnG5yl.cjs.map → stages-B7mCWI_R.cjs.map} +1 -1
- package/dist/{stages-Cb9rqRbN.mjs → stages-DSltTW2d.mjs} +2 -2
- package/dist/{stages-Cb9rqRbN.mjs.map → stages-DSltTW2d.mjs.map} +1 -1
- package/dist/target/public.cjs +1 -1
- package/dist/target/public.d.cts +2 -2
- package/dist/target/public.d.mts +2 -2
- package/dist/target/public.mjs +1 -1
- package/dist/{types-B6UXlEkf.d.cts → types-C8gXOLWL.d.mts} +22 -3
- package/dist/{types-HqVPddRK.d.mts.map → types-C8gXOLWL.d.mts.map} +1 -1
- package/dist/{types-HqVPddRK.d.mts → types-Q2SC_ly2.d.cts} +23 -2
- package/dist/{types-B6UXlEkf.d.cts.map → types-Q2SC_ly2.d.cts.map} +1 -1
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +2 -2
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-BwqQnool.cjs → workspace-C82GLPnf.cjs} +8 -2
- package/dist/workspace-C82GLPnf.cjs.map +1 -0
- package/dist/{workspace-D1QbNd55.mjs → workspace-DUnnVjqC.mjs} +8 -2
- package/dist/workspace-DUnnVjqC.mjs.map +1 -0
- package/package.json +17 -16
- package/dist/compose-C43c03WZ.mjs.map +0 -1
- package/dist/compose-Mdgea4Yt.cjs.map +0 -1
- package/dist/dns-BIYf20p7.cjs.map +0 -1
- package/dist/dns-agf2VCdV.mjs.map +0 -1
- package/dist/workspace-BwqQnool.cjs.map +0 -1
- package/dist/workspace-D1QbNd55.mjs.map +0 -1
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
|
|
2
|
-
import { dt as checkStageBackups, ft as SERVER_IPV4_KEY, mt as cnameTarget, pt as SERVER_IPV6_KEY } from "./workspace-
|
|
3
|
-
import {
|
|
2
|
+
import { dt as checkStageBackups, ft as SERVER_IPV4_KEY, mt as cnameTarget, pt as SERVER_IPV6_KEY } from "./workspace-DUnnVjqC.mjs";
|
|
3
|
+
import { An as localRolePassword, O as DnsRecordsUnreadable, f as extractSubdomain, k as createDnsProvider, m as normalizeDnsConfig, mn as postgresRoleSlug, p as findRootDomain, qn as postgresImage, un as quoteIdentifier } from "./seeds-Dv78P9LS.mjs";
|
|
4
|
+
import { t as appKey } from "./derive-B4vDpzgP.mjs";
|
|
4
5
|
import { createRequire } from "node:module";
|
|
5
6
|
import { dirname, join } from "node:path";
|
|
6
7
|
import { existsSync, readFileSync } from "node:fs";
|
|
7
8
|
import { isIPv4, isIPv6 } from "node:net";
|
|
8
|
-
import { createHash } from "node:crypto";
|
|
9
|
-
import { DEFAULT_POSTGRES_VERSION, kebabCase } from "@geekmidas/manifest";
|
|
9
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
10
|
+
import { DEFAULT_POSTGRES_VERSION, environmentCase, kebabCase } from "@geekmidas/manifest";
|
|
10
11
|
import { fileURLToPath } from "node:url";
|
|
12
|
+
import bcrypt from "bcryptjs";
|
|
11
13
|
import { lookup } from "node:dns/promises";
|
|
12
14
|
//#region src/providers/dns.ts
|
|
13
15
|
/** Whether the workspace deploys through the compose target. */
|
|
@@ -319,6 +321,153 @@ function backupsRoleStatements(backups) {
|
|
|
319
321
|
];
|
|
320
322
|
}
|
|
321
323
|
//#endregion
|
|
324
|
+
//#region src/compose/sitePassword.ts
|
|
325
|
+
/**
|
|
326
|
+
* A deployed stage's sites behind a password — `deploy.sitePassword` — so a
|
|
327
|
+
* staging build is not public while it is being tried.
|
|
328
|
+
*
|
|
329
|
+
* The edge asks for it with HTTP basic auth, before a request reaches the
|
|
330
|
+
* site: the browser prompts, and nothing of the site loads until the stage's
|
|
331
|
+
* login is given. The user is the stage's name; the password is generated
|
|
332
|
+
* once per site and kept in the stage's secrets, where one set by hand wins.
|
|
333
|
+
* Only its bcrypt hash reaches the edge's config.
|
|
334
|
+
*
|
|
335
|
+
* Pure.
|
|
336
|
+
*/
|
|
337
|
+
/** `deploy.sitePassword` names something that is not one of the stage's sites. */
|
|
338
|
+
var UnknownPasswordSite = class extends GkmError {
|
|
339
|
+
stage;
|
|
340
|
+
site;
|
|
341
|
+
sites;
|
|
342
|
+
constructor(stage, site, sites) {
|
|
343
|
+
super(`deploy.sitePassword.${stage} names '${site}', which is not a site of this workspace. ${sites.length > 0 ? `Its sites: ${sites.map((s) => `'${s}'`).join(", ")}.` : "It declares no site."} Name one of them, or set ${stage}: true for every site.`);
|
|
344
|
+
this.stage = stage;
|
|
345
|
+
this.site = site;
|
|
346
|
+
this.sites = sites;
|
|
347
|
+
this.name = "UnknownPasswordSite";
|
|
348
|
+
}
|
|
349
|
+
};
|
|
350
|
+
/** A site the stage puts behind a password, with none in the stage's secrets. */
|
|
351
|
+
var SitePasswordMissing = class extends GkmError {
|
|
352
|
+
stage;
|
|
353
|
+
key;
|
|
354
|
+
constructor(stage, key) {
|
|
355
|
+
super(`deploy.sitePassword puts a site of '${stage}' behind a password, and the stage has no ${key} in its secrets. Run gkm compose --stage ${stage} once to generate it, or set one: gkm secrets:set ${key} '…' --stage ${stage}`);
|
|
356
|
+
this.stage = stage;
|
|
357
|
+
this.key = key;
|
|
358
|
+
this.name = "SitePasswordMissing";
|
|
359
|
+
}
|
|
360
|
+
};
|
|
361
|
+
/**
|
|
362
|
+
* `deploy.sitePassword` names a stage that deploys through a target that
|
|
363
|
+
* cannot ask for a login. Refused rather than ignored: ignored, the stage
|
|
364
|
+
* would be public while its config says it is not.
|
|
365
|
+
*/
|
|
366
|
+
var SitePasswordNeedsCompose = class extends GkmError {
|
|
367
|
+
stage;
|
|
368
|
+
target;
|
|
369
|
+
constructor(stage, target) {
|
|
370
|
+
super(`deploy.sitePassword puts '${stage}' behind a password, and it deploys through '${target}', which cannot ask for one yet — only compose does. Remove ${stage} from deploy.sitePassword, or deploy it with compose.`);
|
|
371
|
+
this.stage = stage;
|
|
372
|
+
this.target = target;
|
|
373
|
+
this.name = "SitePasswordNeedsCompose";
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
/**
|
|
377
|
+
* @throws {SitePasswordNeedsCompose} where `stage` asks for a site password
|
|
378
|
+
* and `target` is not compose
|
|
379
|
+
*/
|
|
380
|
+
function assertSitePasswordTarget(workspace, stage, target) {
|
|
381
|
+
if (workspace.deploy?.sitePassword?.[stage] && target !== "compose") throw new SitePasswordNeedsCompose(stage, target);
|
|
382
|
+
}
|
|
383
|
+
/** The stage secret a site's password is kept in: `WEB_SITE_PASSWORD`. */
|
|
384
|
+
function sitePasswordKey(site) {
|
|
385
|
+
return `${environmentCase(site)}_SITE_PASSWORD`;
|
|
386
|
+
}
|
|
387
|
+
/** Every site the manifest declares, by app name. */
|
|
388
|
+
function siteNames(manifest) {
|
|
389
|
+
return Object.values(manifest).filter((declaration) => declaration.kind === "site").map((declaration) => appKey(declaration.id));
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* The sites `deploy.sitePassword` puts behind a password on `stage`, by app
|
|
393
|
+
* name: every one for `true`, the ones it names otherwise.
|
|
394
|
+
*
|
|
395
|
+
* @throws {UnknownPasswordSite} for a name that is not one of `sites`
|
|
396
|
+
*/
|
|
397
|
+
function passwordSites(workspace, stage, sites) {
|
|
398
|
+
const choice = workspace.deploy?.sitePassword?.[stage];
|
|
399
|
+
if (!choice) return [];
|
|
400
|
+
if (choice === true) return [...sites];
|
|
401
|
+
const chosen = /* @__PURE__ */ new Set();
|
|
402
|
+
for (const name of choice) {
|
|
403
|
+
const site = appKey(name);
|
|
404
|
+
if (!sites.includes(site)) throw new UnknownPasswordSite(stage, name, sites);
|
|
405
|
+
chosen.add(site);
|
|
406
|
+
}
|
|
407
|
+
return [...chosen];
|
|
408
|
+
}
|
|
409
|
+
/** A password: 192 random bits, URL-safe, so it is typed or pasted as is. */
|
|
410
|
+
function generateSitePassword() {
|
|
411
|
+
return randomBytes(24).toString("base64url");
|
|
412
|
+
}
|
|
413
|
+
/**
|
|
414
|
+
* The stage's secrets with a generated password for each site, where it has
|
|
415
|
+
* none. Pure, like `withGeneratedSecrets`: the caller keeps the result once
|
|
416
|
+
* the run goes ahead.
|
|
417
|
+
*/
|
|
418
|
+
function withSitePasswords(secrets, sites) {
|
|
419
|
+
const missing = sites.map(sitePasswordKey).filter((key) => !secrets.custom?.[key]);
|
|
420
|
+
if (missing.length === 0) return {
|
|
421
|
+
secrets,
|
|
422
|
+
generated: []
|
|
423
|
+
};
|
|
424
|
+
return {
|
|
425
|
+
secrets: {
|
|
426
|
+
...secrets,
|
|
427
|
+
custom: {
|
|
428
|
+
...secrets.custom,
|
|
429
|
+
...Object.fromEntries(missing.map((key) => [key, generateSitePassword()]))
|
|
430
|
+
},
|
|
431
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
432
|
+
},
|
|
433
|
+
generated: missing
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* The login a site is served behind on `stage`.
|
|
438
|
+
*
|
|
439
|
+
* The hash's salt is derived from the password, so the same password always
|
|
440
|
+
* renders the same edge config: a deploy that changed nothing rewrites
|
|
441
|
+
* nothing, and the proxy is not reloaded for it. A per-hash random salt
|
|
442
|
+
* guards against a precomputed table of common passwords; a generated one is
|
|
443
|
+
* 192 random bits, which no table holds.
|
|
444
|
+
*
|
|
445
|
+
* @throws {SitePasswordMissing} where the stage's secrets hold none
|
|
446
|
+
*/
|
|
447
|
+
function siteAuth(stage, site, custom) {
|
|
448
|
+
const key = sitePasswordKey(site);
|
|
449
|
+
const password = custom[key];
|
|
450
|
+
if (!password) throw new SitePasswordMissing(stage, key);
|
|
451
|
+
return {
|
|
452
|
+
user: stage,
|
|
453
|
+
password,
|
|
454
|
+
hash: bcrypt.hashSync(password, saltFor(password)),
|
|
455
|
+
key
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
/** bcrypt's own base-64 alphabet, in which a salt is written. */
|
|
459
|
+
const BCRYPT_ALPHABET = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
|
|
460
|
+
/**
|
|
461
|
+
* A bcrypt salt — `$2a$`, which Caddy and Traefik both read, at cost 10 —
|
|
462
|
+
* derived from the password.
|
|
463
|
+
*/
|
|
464
|
+
function saltFor(password) {
|
|
465
|
+
const digest = createHash("sha256").update(password).digest();
|
|
466
|
+
let salt = "";
|
|
467
|
+
for (let i = 0; i < 22; i++) salt += BCRYPT_ALPHABET[digest[i] % 64];
|
|
468
|
+
return `$2a$10$${salt}`;
|
|
469
|
+
}
|
|
470
|
+
//#endregion
|
|
322
471
|
//#region src/compose/dns.ts
|
|
323
472
|
/**
|
|
324
473
|
* A compose stage's DNS: the records its public hosts need, and the check that
|
|
@@ -341,10 +490,17 @@ function backupsRoleStatements(backups) {
|
|
|
341
490
|
* resolver. One that is not the server stops the deploy with
|
|
342
491
|
* {@link HostNotPointingAtServer}, before Caddy or Traefik ask Let's Encrypt
|
|
343
492
|
* for a certificate it cannot get — and use up the rate limit trying.
|
|
344
|
-
* - **
|
|
345
|
-
*
|
|
346
|
-
*
|
|
347
|
-
*
|
|
493
|
+
* - **What gkm wrote before** (`diffRecordedDns`, pure): the plan against the
|
|
494
|
+
* records the stage's state says gkm wrote — added, kept, and stale: a
|
|
495
|
+
* host the stage no longer serves, after a domain or an app changed.
|
|
496
|
+
* - **The prune** (`pruneStackDns`): each stale record deleted through its
|
|
497
|
+
* domain's provider, if it still holds the value gkm wrote. One someone
|
|
498
|
+
* pointed elsewhere is left alone; one no provider can delete is printed.
|
|
499
|
+
* - **The step** (`stageDns`): all of it, as every deploy runs it — the plan applied through each provider,
|
|
500
|
+
* what was written read back from the provider, the stale records pruned,
|
|
501
|
+
* and only the hosts no provider wrote resolved publicly. A brand-new name
|
|
502
|
+
* is never looked up before its record exists, so no resolver caches it as
|
|
503
|
+
* missing.
|
|
348
504
|
*/
|
|
349
505
|
/** The line that says how to set the server's address. */
|
|
350
506
|
function serverAddressHint(stage) {
|
|
@@ -536,12 +692,23 @@ function printManual(plan, log, why) {
|
|
|
536
692
|
/** The existing records of every planned name, for the types gkm manages. */
|
|
537
693
|
async function existingRecords(provider, plan) {
|
|
538
694
|
const names = [...new Set(plan.records.map((r) => r.name))];
|
|
539
|
-
|
|
695
|
+
return readRecords(provider, plan.domain, names.flatMap((name) => MANAGED.map((type) => ({
|
|
540
696
|
name,
|
|
541
697
|
type
|
|
542
|
-
})));
|
|
543
|
-
|
|
544
|
-
|
|
698
|
+
}))));
|
|
699
|
+
}
|
|
700
|
+
/** Exactly these names and types, as the provider holds them. */
|
|
701
|
+
async function readRecords(provider, domain, wanted) {
|
|
702
|
+
if (provider.readRecords) return provider.readRecords(domain, wanted);
|
|
703
|
+
return (await provider.getRecords(domain)).filter((r) => wanted.some((w) => w.name === r.name && w.type === r.type));
|
|
704
|
+
}
|
|
705
|
+
/** The provider for a domain's config: `providerFor`, or the real one. */
|
|
706
|
+
function providerResolver(options) {
|
|
707
|
+
return options.providerFor ?? ((config) => createDnsProvider({
|
|
708
|
+
config,
|
|
709
|
+
...options.credentials ? { godaddy: options.credentials } : {},
|
|
710
|
+
...options.hostinger ? { hostinger: options.hostinger } : {}
|
|
711
|
+
}));
|
|
545
712
|
}
|
|
546
713
|
const same = (a, b) => a.trim().replace(/\.$/, "").toLowerCase() === b.trim().replace(/\.$/, "").toLowerCase();
|
|
547
714
|
/**
|
|
@@ -631,11 +798,7 @@ function printChange(change, dryRun, log) {
|
|
|
631
798
|
async function settleStackDns(plan, options = {}) {
|
|
632
799
|
const dryRun = options.dryRun === true;
|
|
633
800
|
const log = options.log ?? ((line) => console.log(line));
|
|
634
|
-
const providerFor = options
|
|
635
|
-
config,
|
|
636
|
-
...options.credentials ? { godaddy: options.credentials } : {},
|
|
637
|
-
...options.hostinger ? { hostinger: options.hostinger } : {}
|
|
638
|
-
}));
|
|
801
|
+
const providerFor = providerResolver(options);
|
|
639
802
|
const changes = [];
|
|
640
803
|
const confirmed = /* @__PURE__ */ new Set();
|
|
641
804
|
const unconfirmed = /* @__PURE__ */ new Set();
|
|
@@ -725,6 +888,110 @@ async function settleStackDns(plan, options = {}) {
|
|
|
725
888
|
unconfirmed: [...unconfirmed].filter((h) => !confirmed.has(h)).sort()
|
|
726
889
|
};
|
|
727
890
|
}
|
|
891
|
+
/** `api.example.com A` — a record's identity, whatever its value. */
|
|
892
|
+
const recordKey = (fqdn, type) => `${fqdn.trim().replace(/\.$/, "").toLowerCase()} ${type.toUpperCase()}`;
|
|
893
|
+
/**
|
|
894
|
+
* The plan against the records gkm wrote for the stage — pure. A record is
|
|
895
|
+
* its full name and its type: one the plan has at another value is kept (the
|
|
896
|
+
* apply updates it), and one whose name or type the plan no longer has is
|
|
897
|
+
* stale — a host the stage stopped serving when its domain or an app changed,
|
|
898
|
+
* or an AAAA once its server has no IPv6 address.
|
|
899
|
+
*/
|
|
900
|
+
function diffRecordedDns(plan, recorded) {
|
|
901
|
+
const planned = plan.domains.flatMap((domain) => domain.records);
|
|
902
|
+
const wanted = new Set(planned.map((r) => recordKey(r.host, r.type)));
|
|
903
|
+
const written = new Set(recorded.map((r) => recordKey(r.fqdn, r.type)));
|
|
904
|
+
return {
|
|
905
|
+
added: planned.filter((r) => !written.has(recordKey(r.host, r.type))),
|
|
906
|
+
kept: planned.filter((r) => written.has(recordKey(r.host, r.type))),
|
|
907
|
+
stale: recorded.filter((r) => !wanted.has(recordKey(r.fqdn, r.type)))
|
|
908
|
+
};
|
|
909
|
+
}
|
|
910
|
+
/**
|
|
911
|
+
* Each stale record deleted through its domain's provider — only where the
|
|
912
|
+
* provider still holds exactly the value gkm wrote, so a name someone has
|
|
913
|
+
* since pointed elsewhere is never taken from them. Where no provider can
|
|
914
|
+
* read and delete it — a `manual` domain, one gone from `dns`, a key that
|
|
915
|
+
* can only write — it is printed for a person to remove. A dry run reads
|
|
916
|
+
* and prints, and deletes nothing.
|
|
917
|
+
*/
|
|
918
|
+
async function pruneStackDns(stale, options) {
|
|
919
|
+
if (stale.length === 0) return [];
|
|
920
|
+
const dryRun = options.dryRun === true;
|
|
921
|
+
const log = options.log ?? ((line) => console.log(line));
|
|
922
|
+
const providerFor = providerResolver(options);
|
|
923
|
+
const dns = options.dns ? normalizeDnsConfig(options.dns) : {};
|
|
924
|
+
const byDomain = /* @__PURE__ */ new Map();
|
|
925
|
+
for (const record of stale) byDomain.set(record.domain, [...byDomain.get(record.domain) ?? [], record]);
|
|
926
|
+
log(` No longer served by '${options.stage}':`);
|
|
927
|
+
const pruned = [];
|
|
928
|
+
const byHand = (domain, records, why) => {
|
|
929
|
+
log(` ${domain}: ${why} — remove these records at its DNS host:`);
|
|
930
|
+
for (const record of records) {
|
|
931
|
+
log(` ${record.fqdn.padEnd(32)} ${record.type.padEnd(5)} ${record.value}`);
|
|
932
|
+
pruned.push({
|
|
933
|
+
record,
|
|
934
|
+
action: "manual"
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
};
|
|
938
|
+
for (const [domain, records] of byDomain) {
|
|
939
|
+
const config = dns[domain];
|
|
940
|
+
if (!config) {
|
|
941
|
+
byHand(domain, records, "no longer in dns");
|
|
942
|
+
continue;
|
|
943
|
+
}
|
|
944
|
+
const provider = providerName(config) === "manual" ? null : await providerFor(config);
|
|
945
|
+
if (!provider) {
|
|
946
|
+
byHand(domain, records, "manual");
|
|
947
|
+
continue;
|
|
948
|
+
}
|
|
949
|
+
let existing;
|
|
950
|
+
try {
|
|
951
|
+
existing = await readRecords(provider, domain, records.map((r) => ({
|
|
952
|
+
name: r.name,
|
|
953
|
+
type: r.type
|
|
954
|
+
})));
|
|
955
|
+
} catch (error) {
|
|
956
|
+
if (!(error instanceof DnsRecordsUnreadable)) throw error;
|
|
957
|
+
byHand(domain, records, `the ${provider.name} key cannot read its records, so none is deleted unchecked`);
|
|
958
|
+
continue;
|
|
959
|
+
}
|
|
960
|
+
const would = dryRun ? "would " : "";
|
|
961
|
+
const deletes = [];
|
|
962
|
+
for (const record of records) {
|
|
963
|
+
const host = record.fqdn.padEnd(32);
|
|
964
|
+
const type = record.type.padEnd(5);
|
|
965
|
+
const found = existing.find((r) => r.name === record.name && r.type === record.type)?.values ?? [];
|
|
966
|
+
if (found.length === 0) {
|
|
967
|
+
log(` · ${host} ${type} already gone`);
|
|
968
|
+
pruned.push({
|
|
969
|
+
record,
|
|
970
|
+
action: "gone"
|
|
971
|
+
});
|
|
972
|
+
} else if (found.length === 1 && same(found[0], record.value)) {
|
|
973
|
+
log(` - ${host} ${type} ${record.value} — ${would}delete`);
|
|
974
|
+
deletes.push({
|
|
975
|
+
record,
|
|
976
|
+
action: "delete"
|
|
977
|
+
});
|
|
978
|
+
} else {
|
|
979
|
+
log(` ⚠ ${host} ${type} ${found.join(", ")} — pointed elsewhere since gkm wrote ${record.value}; left as it is`);
|
|
980
|
+
pruned.push({
|
|
981
|
+
record,
|
|
982
|
+
action: "changed",
|
|
983
|
+
found
|
|
984
|
+
});
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
if (!dryRun && deletes.length > 0) await provider.deleteRecords(domain, deletes.map(({ record }) => ({
|
|
988
|
+
name: record.name,
|
|
989
|
+
type: record.type
|
|
990
|
+
})));
|
|
991
|
+
pruned.push(...deletes);
|
|
992
|
+
}
|
|
993
|
+
return pruned;
|
|
994
|
+
}
|
|
728
995
|
/** The system resolver: what the server's own clients would be told. */
|
|
729
996
|
const systemLookup = async (host) => {
|
|
730
997
|
return (await lookup(host, { all: true })).map((a) => a.address);
|
|
@@ -867,8 +1134,9 @@ async function dnsCredentials(stage, domains, credentials) {
|
|
|
867
1134
|
/**
|
|
868
1135
|
* A stage's DNS, as every deploy runs it: the
|
|
869
1136
|
* stage's own hosts planned, written through each domain's provider where
|
|
870
|
-
* they differ, and read back from it
|
|
871
|
-
*
|
|
1137
|
+
* they differ, and read back from it — and each record gkm wrote for a host
|
|
1138
|
+
* the stage no longer serves found, for `prune` to delete. Only A, AAAA and CNAME records of those
|
|
1139
|
+
* names are touched; nothing is a wildcard, and no other name is read,
|
|
872
1140
|
* written or deleted. What no provider confirms — a `manual` domain, a host
|
|
873
1141
|
* under no domain in `dns`, a key that cannot read — is resolved publicly.
|
|
874
1142
|
* A dry run prints the plan and writes nothing.
|
|
@@ -886,16 +1154,17 @@ async function stageDns(input) {
|
|
|
886
1154
|
server,
|
|
887
1155
|
dns: input.dns
|
|
888
1156
|
});
|
|
889
|
-
const
|
|
1157
|
+
const recorded = diffRecordedDns(plan, input.recorded ?? []);
|
|
1158
|
+
const dns = input.dns ? normalizeDnsConfig(input.dns) : {};
|
|
1159
|
+
const staleDomains = [...new Set(recorded.stale.map((r) => r.domain))].filter((domain) => dns[domain] && !plan.domains.some((d) => d.domain === domain)).map((domain) => ({
|
|
1160
|
+
domain,
|
|
1161
|
+
config: dns[domain]
|
|
1162
|
+
}));
|
|
1163
|
+
const tokens = await dnsCredentials(stage, [...plan.domains.map(({ domain, config }) => ({
|
|
890
1164
|
domain,
|
|
891
1165
|
config
|
|
892
|
-
})), input.credentials);
|
|
893
|
-
const
|
|
894
|
-
changes: [],
|
|
895
|
-
confirmed: [],
|
|
896
|
-
unconfirmed: [...plan.uncovered]
|
|
897
|
-
} : await settleStackDns(plan, {
|
|
898
|
-
...input.dryRun ? { dryRun: true } : { confirm: true },
|
|
1166
|
+
})), ...staleDomains], input.credentials);
|
|
1167
|
+
const providers = {
|
|
899
1168
|
log,
|
|
900
1169
|
credentials: {
|
|
901
1170
|
...input.godaddy ?? {},
|
|
@@ -904,6 +1173,22 @@ async function stageDns(input) {
|
|
|
904
1173
|
},
|
|
905
1174
|
...tokens.hostinger ? { hostinger: tokens.hostinger } : {},
|
|
906
1175
|
...input.providerFor ? { providerFor: input.providerFor } : {}
|
|
1176
|
+
};
|
|
1177
|
+
const applied = !input.dns ? {
|
|
1178
|
+
changes: [],
|
|
1179
|
+
confirmed: [],
|
|
1180
|
+
unconfirmed: [...plan.uncovered]
|
|
1181
|
+
} : await settleStackDns(plan, {
|
|
1182
|
+
...input.dryRun ? { dryRun: true } : { confirm: true },
|
|
1183
|
+
...providers
|
|
1184
|
+
});
|
|
1185
|
+
const deleted = new Set(applied.changes.filter((c) => c.action === "delete").map((c) => recordKey(fullName(c.domain, c.name), c.type)));
|
|
1186
|
+
const stale = recorded.stale.filter((r) => !deleted.has(recordKey(r.fqdn, r.type)));
|
|
1187
|
+
const prune = () => pruneStackDns(stale, {
|
|
1188
|
+
stage,
|
|
1189
|
+
dns: input.dns,
|
|
1190
|
+
...input.dryRun ? { dryRun: true } : {},
|
|
1191
|
+
...providers
|
|
907
1192
|
});
|
|
908
1193
|
let resolved = [];
|
|
909
1194
|
if (applied.unconfirmed.length > 0) resolved = await checkStackDns({
|
|
@@ -917,10 +1202,12 @@ async function stageDns(input) {
|
|
|
917
1202
|
plan,
|
|
918
1203
|
changes: applied.changes,
|
|
919
1204
|
confirmed: applied.confirmed,
|
|
920
|
-
resolved
|
|
1205
|
+
resolved,
|
|
1206
|
+
recorded,
|
|
1207
|
+
prune
|
|
921
1208
|
};
|
|
922
1209
|
}
|
|
923
1210
|
//#endregion
|
|
924
|
-
export { deploysWithCompose as
|
|
1211
|
+
export { backupFile as C, deploysWithCompose as D, stageBackups as E, backupExpiries as S, runTime as T, RUNNER_PATH as _, serverAddressHint as a, stackBackups as b, stageDnsDomains as c, passwordSites as d, siteAuth as f, BACKUPS_URL_KEY as g, BACKUPS_SERVICE as h, requiredServerAddress as i, systemLookup as l, withSitePasswords as m, dnsCredentials as n, stackHosts as o, siteNames as p, isLocalHost as r, stageDns as s, HostNotPointingAtServer as t, assertSitePasswordTarget as u, backupsRoleStatements as v, backupsPrefix as w, backupDatabases as x, backupsService as y };
|
|
925
1212
|
|
|
926
|
-
//# sourceMappingURL=dns-
|
|
1213
|
+
//# sourceMappingURL=dns-DFvO5a53.mjs.map
|