@geekmidas/cli 10.0.0-alpha.10 → 10.0.0-alpha.101
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/README.md +315 -414
- package/bin/adjacent-tsconfig.mjs +46 -8
- package/bin/gkm.mjs +11 -3
- package/bin/owning-tsconfig-jsx-register.mjs +18 -0
- package/bin/owning-tsconfig-jsx.mjs +108 -0
- package/dist/EndpointGenerator-Br97xgs_.mjs +1031 -0
- package/dist/EndpointGenerator-Br97xgs_.mjs.map +1 -0
- package/dist/EndpointGenerator-By5_tYE9.cjs +1090 -0
- package/dist/EndpointGenerator-By5_tYE9.cjs.map +1 -0
- package/dist/GoDaddyProvider-BOuLf_Py.cjs +388 -0
- package/dist/GoDaddyProvider-BOuLf_Py.cjs.map +1 -0
- package/dist/GoDaddyProvider-u9MF-Dqk.mjs +388 -0
- package/dist/GoDaddyProvider-u9MF-Dqk.mjs.map +1 -0
- package/dist/{HostingerProvider-Cft7lEYr.mjs → HostingerProvider-BNpS9aRJ.mjs} +29 -25
- package/dist/HostingerProvider-BNpS9aRJ.mjs.map +1 -0
- package/dist/{HostingerProvider-h3EbEp-Z.cjs → HostingerProvider-DR1iws1P.cjs} +29 -25
- package/dist/HostingerProvider-DR1iws1P.cjs.map +1 -0
- package/dist/LegacyStateStore-DWmHGNgH.cjs +124 -0
- package/dist/LegacyStateStore-DWmHGNgH.cjs.map +1 -0
- package/dist/LegacyStateStore-vhkx7Bbq.mjs +124 -0
- package/dist/LegacyStateStore-vhkx7Bbq.mjs.map +1 -0
- package/dist/LocalStateStore-BVKDADoG.cjs +178 -0
- package/dist/LocalStateStore-BVKDADoG.cjs.map +1 -0
- package/dist/LocalStateStore-FVsg0UoM.mjs +167 -0
- package/dist/LocalStateStore-FVsg0UoM.mjs.map +1 -0
- package/dist/OpenApiTsGenerator-BgSX_Z-e.cjs +541 -0
- package/dist/OpenApiTsGenerator-BgSX_Z-e.cjs.map +1 -0
- package/dist/OpenApiTsGenerator-C8aTX--y.mjs +536 -0
- package/dist/OpenApiTsGenerator-C8aTX--y.mjs.map +1 -0
- package/dist/{Route53Provider-di2fMM5N.cjs → Route53Provider-Bllus2NR.cjs} +21 -22
- package/dist/Route53Provider-Bllus2NR.cjs.map +1 -0
- package/dist/{Route53Provider-RdpUDJQk.mjs → Route53Provider-QXxjkZO1.mjs} +13 -13
- package/dist/Route53Provider-QXxjkZO1.mjs.map +1 -0
- package/dist/S3StateStore-B8seZUsw.mjs +223 -0
- package/dist/S3StateStore-B8seZUsw.mjs.map +1 -0
- package/dist/S3StateStore-B_2gE9Un.cjs +222 -0
- package/dist/S3StateStore-B_2gE9Un.cjs.map +1 -0
- package/dist/SSMStateStore-CTDEI5Bi.cjs +204 -0
- package/dist/SSMStateStore-CTDEI5Bi.cjs.map +1 -0
- package/dist/SSMStateStore-DGTbBomw.mjs +205 -0
- package/dist/SSMStateStore-DGTbBomw.mjs.map +1 -0
- package/dist/SubscriberGenerator-1-IsjIwF.mjs +449 -0
- package/dist/SubscriberGenerator-1-IsjIwF.mjs.map +1 -0
- package/dist/SubscriberGenerator-BlIOdbi-.cjs +490 -0
- package/dist/SubscriberGenerator-BlIOdbi-.cjs.map +1 -0
- package/dist/add-BB2tLVc2.cjs +720 -0
- package/dist/add-BB2tLVc2.cjs.map +1 -0
- package/dist/add-CIbhGE5v.mjs +718 -0
- package/dist/add-CIbhGE5v.mjs.map +1 -0
- package/dist/appPorts-C9z9Pzn9.mjs +223 -0
- package/dist/appPorts-C9z9Pzn9.mjs.map +1 -0
- package/dist/appPorts-Cu79hJF6.cjs +264 -0
- package/dist/appPorts-Cu79hJF6.cjs.map +1 -0
- package/dist/aws-8QQ8n-t6.mjs +89 -0
- package/dist/aws-8QQ8n-t6.mjs.map +1 -0
- package/dist/aws-DY9NqQIT.cjs +100 -0
- package/dist/aws-DY9NqQIT.cjs.map +1 -0
- package/dist/awsStore-Bx5nCFnS.mjs +121 -0
- package/dist/awsStore-Bx5nCFnS.mjs.map +1 -0
- package/dist/awsStore-u3ehhMoY.cjs +138 -0
- package/dist/awsStore-u3ehhMoY.cjs.map +1 -0
- package/dist/{backup-provisioner-Bgj5Vi_L.mjs → backup-provisioner-C8DnCmxK.mjs} +11 -8
- package/dist/backup-provisioner-C8DnCmxK.mjs.map +1 -0
- package/dist/{backup-provisioner-BrsHwYG0.cjs → backup-provisioner-oDEsPj02.cjs} +22 -20
- package/dist/backup-provisioner-oDEsPj02.cjs.map +1 -0
- package/dist/backups/runner.cjs +519 -0
- package/dist/backups/runner.cjs.map +1 -0
- package/dist/backups/runner.d.cts +102 -0
- package/dist/backups/runner.d.cts.map +1 -0
- package/dist/backups/runner.d.mts +102 -0
- package/dist/backups/runner.d.mts.map +1 -0
- package/dist/backups/runner.mjs +506 -0
- package/dist/backups/runner.mjs.map +1 -0
- package/dist/{bundler-BY1OmV1e.cjs → bundler-Ba6BER3w.cjs} +38 -35
- package/dist/bundler-Ba6BER3w.cjs.map +1 -0
- package/dist/{bundler-Be8Zg4Wd.mjs → bundler-CHjRYVKh.mjs} +36 -32
- package/dist/bundler-CHjRYVKh.mjs.map +1 -0
- package/dist/cli-CtlqYRlN.cjs +71 -0
- package/dist/cli-CtlqYRlN.cjs.map +1 -0
- package/dist/cli-DOlkhrsh.mjs +71 -0
- package/dist/cli-DOlkhrsh.mjs.map +1 -0
- package/dist/compose-FDP_TaFt.mjs +3154 -0
- package/dist/compose-FDP_TaFt.mjs.map +1 -0
- package/dist/compose-uAWLy-nR.cjs +3267 -0
- package/dist/compose-uAWLy-nR.cjs.map +1 -0
- package/dist/config-D48erO2j.d.mts +269 -0
- package/dist/config-D48erO2j.d.mts.map +1 -0
- package/dist/config-DHDtsQrB.d.cts +269 -0
- package/dist/config-DHDtsQrB.d.cts.map +1 -0
- package/dist/config.cjs +421 -11
- package/dist/config.cjs.map +1 -0
- package/dist/config.d.cts +4 -120
- package/dist/config.d.mts +4 -120
- package/dist/config.mjs +405 -4
- package/dist/config.mjs.map +1 -0
- package/dist/credentialSchemas-DDda4Mmm.cjs +272 -0
- package/dist/credentialSchemas-DDda4Mmm.cjs.map +1 -0
- package/dist/credentialSchemas-DvitraL-.mjs +237 -0
- package/dist/credentialSchemas-DvitraL-.mjs.map +1 -0
- package/dist/credentials-B5N5yKk6.mjs +267 -0
- package/dist/credentials-B5N5yKk6.mjs.map +1 -0
- package/dist/credentials-Zqr3QQYB.cjs +350 -0
- package/dist/credentials-Zqr3QQYB.cjs.map +1 -0
- package/dist/databases-BeGP5eDh.mjs +495 -0
- package/dist/databases-BeGP5eDh.mjs.map +1 -0
- package/dist/databases-DGALFDG3.cjs +598 -0
- package/dist/databases-DGALFDG3.cjs.map +1 -0
- package/dist/define-6poNqm5O.mjs +106 -0
- package/dist/define-6poNqm5O.mjs.map +1 -0
- package/dist/define-crmMcXFS.cjs +153 -0
- package/dist/define-crmMcXFS.cjs.map +1 -0
- package/dist/deploy/public.cjs +212 -0
- package/dist/deploy/public.cjs.map +1 -0
- package/dist/deploy/public.d.cts +653 -0
- package/dist/deploy/public.d.cts.map +1 -0
- package/dist/deploy/public.d.mts +653 -0
- package/dist/deploy/public.d.mts.map +1 -0
- package/dist/deploy/public.mjs +145 -0
- package/dist/deploy/public.mjs.map +1 -0
- package/dist/deploy/sniffer-envkit-patch.cjs +18 -10
- package/dist/deploy/sniffer-envkit-patch.cjs.map +1 -1
- package/dist/deploy/sniffer-envkit-patch.d.cts +0 -2
- package/dist/deploy/sniffer-envkit-patch.d.cts.map +1 -1
- package/dist/deploy/sniffer-envkit-patch.d.mts +0 -2
- package/dist/deploy/sniffer-envkit-patch.d.mts.map +1 -1
- package/dist/deploy/sniffer-envkit-patch.mjs +10 -2
- package/dist/deploy/sniffer-envkit-patch.mjs.map +1 -1
- package/dist/deploy/sniffer-envparser-worker.cjs +54 -0
- package/dist/deploy/sniffer-envparser-worker.cjs.map +1 -0
- package/dist/deploy/sniffer-envparser-worker.d.cts +1 -0
- package/dist/deploy/sniffer-envparser-worker.d.mts +1 -0
- package/dist/deploy/sniffer-envparser-worker.mjs +55 -0
- package/dist/deploy/sniffer-envparser-worker.mjs.map +1 -0
- package/dist/deploy/sniffer-hooks.cjs +13 -7
- package/dist/deploy/sniffer-hooks.cjs.map +1 -1
- package/dist/deploy/sniffer-hooks.d.cts +1 -2
- package/dist/deploy/sniffer-hooks.d.cts.map +1 -1
- package/dist/deploy/sniffer-hooks.d.mts +1 -2
- package/dist/deploy/sniffer-hooks.d.mts.map +1 -1
- package/dist/deploy/sniffer-hooks.mjs +10 -3
- package/dist/deploy/sniffer-hooks.mjs.map +1 -1
- package/dist/deploy/sniffer-loader.cjs +16 -7
- package/dist/deploy/sniffer-loader.cjs.map +1 -1
- package/dist/deploy/sniffer-loader.d.cts +1 -1
- package/dist/deploy/sniffer-loader.d.mts +1 -1
- package/dist/deploy/sniffer-loader.mjs +14 -3
- package/dist/deploy/sniffer-loader.mjs.map +1 -1
- package/dist/deploy/sniffer-routes-worker.cjs +11 -11
- package/dist/deploy/sniffer-routes-worker.cjs.map +1 -1
- package/dist/deploy/sniffer-routes-worker.d.cts +1 -1
- package/dist/deploy/sniffer-routes-worker.d.mts +1 -1
- package/dist/deploy/sniffer-routes-worker.mjs +4 -4
- package/dist/deploy/sniffer-routes-worker.mjs.map +1 -1
- package/dist/deploy/sniffer-worker.cjs +15 -6
- package/dist/deploy/sniffer-worker.cjs.map +1 -1
- package/dist/deploy/sniffer-worker.d.cts +1 -3
- package/dist/deploy/sniffer-worker.d.cts.map +1 -1
- package/dist/deploy/sniffer-worker.d.mts +1 -3
- package/dist/deploy/sniffer-worker.d.mts.map +1 -1
- package/dist/deploy/sniffer-worker.mjs +15 -4
- package/dist/deploy/sniffer-worker.mjs.map +1 -1
- package/dist/deploy-DUUc3jV9.cjs +2390 -0
- package/dist/deploy-DUUc3jV9.cjs.map +1 -0
- package/dist/deploy-DgZxTnGS.mjs +2317 -0
- package/dist/deploy-DgZxTnGS.mjs.map +1 -0
- package/dist/derive-B4vDpzgP.mjs +150 -0
- package/dist/derive-B4vDpzgP.mjs.map +1 -0
- package/dist/derive-DAbo49sm.cjs +161 -0
- package/dist/derive-DAbo49sm.cjs.map +1 -0
- package/dist/discover-BfhVW_kT.cjs +600 -0
- package/dist/discover-BfhVW_kT.cjs.map +1 -0
- package/dist/discover-CvTtV5ZD.d.cts +108 -0
- package/dist/discover-CvTtV5ZD.d.cts.map +1 -0
- package/dist/discover-W2kab9Lc.d.mts +108 -0
- package/dist/discover-W2kab9Lc.d.mts.map +1 -0
- package/dist/discover-kziEht4W.mjs +527 -0
- package/dist/discover-kziEht4W.mjs.map +1 -0
- package/dist/dns-DDDCfj75.mjs +926 -0
- package/dist/dns-DDDCfj75.mjs.map +1 -0
- package/dist/dns-DnE_02tB.cjs +1057 -0
- package/dist/dns-DnE_02tB.cjs.map +1 -0
- package/dist/dokploy-BrfdeVEu.cjs +38 -0
- package/dist/dokploy-BrfdeVEu.cjs.map +1 -0
- package/dist/dokploy-DBlGSdnb.mjs +38 -0
- package/dist/dokploy-DBlGSdnb.mjs.map +1 -0
- package/dist/drivers-BLU1fNs_.cjs +200 -0
- package/dist/drivers-BLU1fNs_.cjs.map +1 -0
- package/dist/drivers-C08LL4E8.mjs +177 -0
- package/dist/drivers-C08LL4E8.mjs.map +1 -0
- package/dist/{emit-CiZS_5Ub.mjs → emit-C1e8qD08.mjs} +13 -26
- package/dist/emit-C1e8qD08.mjs.map +1 -0
- package/dist/{emit-CbcRolsa.cjs → emit-Dmc1nvdS.cjs} +31 -57
- package/dist/emit-Dmc1nvdS.cjs.map +1 -0
- package/dist/encryption-CeW3xpE1.cjs +90 -0
- package/dist/encryption-CeW3xpE1.cjs.map +1 -0
- package/dist/encryption-DC8zsh2s.mjs +89 -0
- package/dist/encryption-DC8zsh2s.mjs.map +1 -0
- package/dist/engine-BXxvcxVq.cjs +3195 -0
- package/dist/engine-BXxvcxVq.cjs.map +1 -0
- package/dist/engine-z0UClzE9.mjs +3082 -0
- package/dist/engine-z0UClzE9.mjs.map +1 -0
- package/dist/errors-D8Gwp5kU.mjs +23 -0
- package/dist/errors-D8Gwp5kU.mjs.map +1 -0
- package/dist/errors-DMC4Umsb.cjs +34 -0
- package/dist/errors-DMC4Umsb.cjs.map +1 -0
- package/dist/github-DJgO5z37.mjs +829 -0
- package/dist/github-DJgO5z37.mjs.map +1 -0
- package/dist/github-DWoFgxtl.cjs +829 -0
- package/dist/github-DWoFgxtl.cjs.map +1 -0
- package/dist/identity-CBXr4wNi.mjs +129 -0
- package/dist/identity-CBXr4wNi.mjs.map +1 -0
- package/dist/identity-CbLfgQNT.cjs +182 -0
- package/dist/identity-CbLfgQNT.cjs.map +1 -0
- package/dist/{index-C6e-ULLx.d.mts → index-8OfpOtSI.d.cts} +39 -24
- package/dist/index-8OfpOtSI.d.cts.map +1 -0
- package/dist/{index-CR5urhWX.d.cts → index-Dpji6Bxq.d.mts} +39 -24
- package/dist/index-Dpji6Bxq.d.mts.map +1 -0
- package/dist/index.cjs +10022 -12718
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +10648 -13356
- package/dist/index.mjs.map +1 -1
- package/dist/local-BwC2v2Y8.mjs +224 -0
- package/dist/local-BwC2v2Y8.mjs.map +1 -0
- package/dist/local-DNhnkugT.cjs +241 -0
- package/dist/local-DNhnkugT.cjs.map +1 -0
- package/dist/migrate-B1OjPwUY.cjs +181 -0
- package/dist/migrate-B1OjPwUY.cjs.map +1 -0
- package/dist/migrate-CWSCkzu3.cjs +271 -0
- package/dist/migrate-CWSCkzu3.cjs.map +1 -0
- package/dist/migrate-DIVxoju6.mjs +266 -0
- package/dist/migrate-DIVxoju6.mjs.map +1 -0
- package/dist/migrate-DoI2hWNH.mjs +181 -0
- package/dist/migrate-DoI2hWNH.mjs.map +1 -0
- package/dist/openapi.cjs +214 -10
- package/dist/openapi.cjs.map +1 -0
- package/dist/openapi.d.cts +38 -17
- package/dist/openapi.d.cts.map +1 -1
- package/dist/openapi.d.mts +38 -17
- package/dist/openapi.d.mts.map +1 -1
- package/dist/openapi.mjs +208 -5
- package/dist/openapi.mjs.map +1 -0
- package/dist/projectBucket-BBbQ1lHF.cjs +263 -0
- package/dist/projectBucket-BBbQ1lHF.cjs.map +1 -0
- package/dist/projectBucket-C_DN4WzF.mjs +222 -0
- package/dist/projectBucket-C_DN4WzF.mjs.map +1 -0
- package/dist/provider-BqpjG9QM.cjs +57 -0
- package/dist/provider-BqpjG9QM.cjs.map +1 -0
- package/dist/provider-CEOKk8NS.mjs +46 -0
- package/dist/provider-CEOKk8NS.mjs.map +1 -0
- package/dist/ready-BtxiI7Qb.cjs +403 -0
- package/dist/ready-BtxiI7Qb.cjs.map +1 -0
- package/dist/ready-CzHNHhI6.mjs +374 -0
- package/dist/ready-CzHNHhI6.mjs.map +1 -0
- package/dist/reconcile/public.cjs +3 -5
- package/dist/reconcile/public.d.cts +11 -87
- package/dist/reconcile/public.d.cts.map +1 -1
- package/dist/reconcile/public.d.mts +11 -87
- package/dist/reconcile/public.d.mts.map +1 -1
- package/dist/reconcile/public.mjs +3 -4
- package/dist/{reconcile-CFs8C_1a.mjs → reconcile--I7V4Klj.mjs} +4 -7
- package/dist/reconcile--I7V4Klj.mjs.map +1 -0
- package/dist/{reconcile-D0pimNiy.cjs → reconcile-CuCQBa5-.cjs} +5 -8
- package/dist/reconcile-CuCQBa5-.cjs.map +1 -0
- package/dist/resolve-WAwYMN6q.cjs +316 -0
- package/dist/resolve-WAwYMN6q.cjs.map +1 -0
- package/dist/resolve-pmXIdi9j.mjs +263 -0
- package/dist/resolve-pmXIdi9j.mjs.map +1 -0
- package/dist/rollback-CLtwbByg.mjs +136 -0
- package/dist/rollback-CLtwbByg.mjs.map +1 -0
- package/dist/rollback-Cz8F9xSB.cjs +165 -0
- package/dist/rollback-Cz8F9xSB.cjs.map +1 -0
- package/dist/rolldown-runtime-BhDjJH2R.mjs +15 -0
- package/dist/{chunk-CUT6urMc.cjs → rolldown-runtime-C0BPl7ul.cjs} +23 -10
- package/dist/run-BJLIerMM.mjs +133 -0
- package/dist/run-BJLIerMM.mjs.map +1 -0
- package/dist/run-fVZ-rLnc.cjs +174 -0
- package/dist/run-fVZ-rLnc.cjs.map +1 -0
- package/dist/runtime-ACdfK59n.d.cts +82 -0
- package/dist/runtime-ACdfK59n.d.cts.map +1 -0
- package/dist/runtime-YyQuekxi.d.mts +82 -0
- package/dist/runtime-YyQuekxi.d.mts.map +1 -0
- package/dist/s3-CrPoA89T.cjs +215 -0
- package/dist/s3-CrPoA89T.cjs.map +1 -0
- package/dist/s3-DQXZdmCM.mjs +215 -0
- package/dist/s3-DQXZdmCM.mjs.map +1 -0
- package/dist/sandbox/config-worker.cjs +43 -0
- package/dist/sandbox/config-worker.cjs.map +1 -0
- package/dist/sandbox/config-worker.d.cts +1 -0
- package/dist/sandbox/config-worker.d.mts +1 -0
- package/dist/sandbox/config-worker.mjs +44 -0
- package/dist/sandbox/config-worker.mjs.map +1 -0
- package/dist/sandbox/credentials-worker.cjs +54 -0
- package/dist/sandbox/credentials-worker.cjs.map +1 -0
- package/dist/sandbox/credentials-worker.d.cts +1 -0
- package/dist/sandbox/credentials-worker.d.mts +1 -0
- package/dist/sandbox/credentials-worker.mjs +55 -0
- package/dist/sandbox/credentials-worker.mjs.map +1 -0
- package/dist/sandbox/discover-worker.cjs +47 -0
- package/dist/sandbox/discover-worker.cjs.map +1 -0
- package/dist/sandbox/discover-worker.d.cts +1 -0
- package/dist/sandbox/discover-worker.d.mts +1 -0
- package/dist/sandbox/discover-worker.mjs +48 -0
- package/dist/sandbox/discover-worker.mjs.map +1 -0
- package/dist/sandbox/migrate-worker.cjs +75 -0
- package/dist/sandbox/migrate-worker.cjs.map +1 -0
- package/dist/sandbox/migrate-worker.d.cts +1 -0
- package/dist/sandbox/migrate-worker.d.mts +1 -0
- package/dist/sandbox/migrate-worker.mjs +76 -0
- package/dist/sandbox/migrate-worker.mjs.map +1 -0
- package/dist/sandbox-C4g1eG31.d.mts +104 -0
- package/dist/sandbox-C4g1eG31.d.mts.map +1 -0
- package/dist/sandbox-CHv3OYXg.cjs +165 -0
- package/dist/sandbox-CHv3OYXg.cjs.map +1 -0
- package/dist/sandbox-CbVB7uOD.mjs +118 -0
- package/dist/sandbox-CbVB7uOD.mjs.map +1 -0
- package/dist/sandbox-CdKgQ3XU.d.cts +104 -0
- package/dist/sandbox-CdKgQ3XU.d.cts.map +1 -0
- package/dist/{workspace-C6VJQVtG.cjs → schemas-BCwKKA42.mjs} +958 -1842
- package/dist/schemas-BCwKKA42.mjs.map +1 -0
- package/dist/{workspace-zoqRbihZ.mjs → schemas-BQ_v0lEX.cjs} +1048 -1658
- package/dist/schemas-BQ_v0lEX.cjs.map +1 -0
- package/dist/secretsManager-CQORubp9.mjs +97 -0
- package/dist/secretsManager-CQORubp9.mjs.map +1 -0
- package/dist/secretsManager-RAGcEDX1.cjs +108 -0
- package/dist/secretsManager-RAGcEDX1.cjs.map +1 -0
- package/dist/seeds-BHKUAExH.mjs +8148 -0
- package/dist/seeds-BHKUAExH.mjs.map +1 -0
- package/dist/seeds-Dk4WHhjU.cjs +9115 -0
- package/dist/seeds-Dk4WHhjU.cjs.map +1 -0
- package/dist/sniffer-BjBZ01PE.cjs +1998 -0
- package/dist/sniffer-BjBZ01PE.cjs.map +1 -0
- package/dist/sniffer-CaXl51vO.mjs +1825 -0
- package/dist/sniffer-CaXl51vO.mjs.map +1 -0
- package/dist/sst-B-Uz4cvm.cjs +464 -0
- package/dist/sst-B-Uz4cvm.cjs.map +1 -0
- package/dist/sst-C1pxbFEd.mjs +441 -0
- package/dist/sst-C1pxbFEd.mjs.map +1 -0
- package/dist/stages-DJrOYLZ-.mjs +176 -0
- package/dist/stages-DJrOYLZ-.mjs.map +1 -0
- package/dist/stages-_3RVNG83.cjs +176 -0
- package/dist/stages-_3RVNG83.cjs.map +1 -0
- package/dist/target/public.cjs +15 -0
- package/dist/target/public.d.cts +28 -0
- package/dist/target/public.d.cts.map +1 -0
- package/dist/target/public.d.mts +28 -0
- package/dist/target/public.d.mts.map +1 -0
- package/dist/target/public.mjs +4 -0
- package/dist/terminal-Bs_lNQhL.mjs +85 -0
- package/dist/terminal-Bs_lNQhL.mjs.map +1 -0
- package/dist/terminal-CsQz4oT0.cjs +85 -0
- package/dist/terminal-CsQz4oT0.cjs.map +1 -0
- package/dist/types-B6UXlEkf.d.cts +7650 -0
- package/dist/types-B6UXlEkf.d.cts.map +1 -0
- package/dist/types-HqVPddRK.d.mts +7649 -0
- package/dist/types-HqVPddRK.d.mts.map +1 -0
- package/dist/utils-D2Nk1Nv2.cjs +205 -0
- package/dist/utils-D2Nk1Nv2.cjs.map +1 -0
- package/dist/utils-D_fxUqvU.mjs +156 -0
- package/dist/utils-D_fxUqvU.mjs.map +1 -0
- package/dist/vitest.cjs +65 -0
- package/dist/vitest.cjs.map +1 -0
- package/dist/vitest.d.cts +29 -0
- package/dist/vitest.d.cts.map +1 -0
- package/dist/vitest.d.mts +29 -0
- package/dist/vitest.d.mts.map +1 -0
- package/dist/vitest.mjs +65 -0
- package/dist/vitest.mjs.map +1 -0
- package/dist/workerRuntime-BXfhbe13.cjs +108 -0
- package/dist/workerRuntime-BXfhbe13.cjs.map +1 -0
- package/dist/workerRuntime-CsOyBRx2.mjs +79 -0
- package/dist/workerRuntime-CsOyBRx2.mjs.map +1 -0
- package/dist/workspace/index.cjs +8 -4
- package/dist/workspace/index.d.cts +3 -3
- package/dist/workspace/index.d.mts +3 -3
- package/dist/workspace/index.mjs +2 -3
- package/dist/workspace-BwqQnool.cjs +2806 -0
- package/dist/workspace-BwqQnool.cjs.map +1 -0
- package/dist/workspace-D1QbNd55.mjs +2394 -0
- package/dist/workspace-D1QbNd55.mjs.map +1 -0
- package/package.json +54 -22
- package/dist/CachedStateProvider-BLo7zv7S.cjs +0 -3
- package/dist/CachedStateProvider-BxoAm-B2.mjs +0 -3
- package/dist/CachedStateProvider-CqPdW7CB.cjs +0 -60
- package/dist/CachedStateProvider-CqPdW7CB.cjs.map +0 -1
- package/dist/CachedStateProvider-DTniehMP.mjs +0 -54
- package/dist/CachedStateProvider-DTniehMP.mjs.map +0 -1
- package/dist/HostingerProvider-Cft7lEYr.mjs.map +0 -1
- package/dist/HostingerProvider-h3EbEp-Z.cjs.map +0 -1
- package/dist/LocalStateProvider-Bn6tS-oL.mjs +0 -42
- package/dist/LocalStateProvider-Bn6tS-oL.mjs.map +0 -1
- package/dist/LocalStateProvider-Db9SEjyH.cjs +0 -43
- package/dist/LocalStateProvider-Db9SEjyH.cjs.map +0 -1
- package/dist/Route53Provider-RdpUDJQk.mjs.map +0 -1
- package/dist/Route53Provider-di2fMM5N.cjs.map +0 -1
- package/dist/SSMStateProvider-BRt2py-A.mjs +0 -66
- package/dist/SSMStateProvider-BRt2py-A.mjs.map +0 -1
- package/dist/SSMStateProvider-DyX8s9HI.cjs +0 -66
- package/dist/SSMStateProvider-DyX8s9HI.cjs.map +0 -1
- package/dist/backup-provisioner-Bgj5Vi_L.mjs.map +0 -1
- package/dist/backup-provisioner-BrsHwYG0.cjs.map +0 -1
- package/dist/bundler-BY1OmV1e.cjs.map +0 -1
- package/dist/bundler-Be8Zg4Wd.mjs.map +0 -1
- package/dist/chunk-Duj1WY3L.mjs +0 -7
- package/dist/config-BZ1F0WuA.cjs +0 -462
- package/dist/config-BZ1F0WuA.cjs.map +0 -1
- package/dist/config-DVVph9yz.mjs +0 -402
- package/dist/config-DVVph9yz.mjs.map +0 -1
- package/dist/config.d.cts.map +0 -1
- package/dist/config.d.mts.map +0 -1
- package/dist/credentials-B5zRHdAl.mjs +0 -141
- package/dist/credentials-B5zRHdAl.mjs.map +0 -1
- package/dist/credentials-BapzjyV5.cjs +0 -195
- package/dist/credentials-BapzjyV5.cjs.map +0 -1
- package/dist/credentials-Xamqe_NJ.cjs +0 -2681
- package/dist/credentials-Xamqe_NJ.cjs.map +0 -1
- package/dist/credentials-dP8vRVRv.mjs +0 -2549
- package/dist/credentials-dP8vRVRv.mjs.map +0 -1
- package/dist/discover-CAlFgYec.mjs +0 -229
- package/dist/discover-CAlFgYec.mjs.map +0 -1
- package/dist/discover-DTmFeloL.cjs +0 -271
- package/dist/discover-DTmFeloL.cjs.map +0 -1
- package/dist/dokploy-api-BS2xnJLZ.mjs +0 -3
- package/dist/dokploy-api-BsVH0y_s.cjs +0 -718
- package/dist/dokploy-api-BsVH0y_s.cjs.map +0 -1
- package/dist/dokploy-api-BuxDRo3l.cjs +0 -3
- package/dist/dokploy-api-aTFPz7_4.mjs +0 -706
- package/dist/dokploy-api-aTFPz7_4.mjs.map +0 -1
- package/dist/emit-CbcRolsa.cjs.map +0 -1
- package/dist/emit-CiZS_5Ub.mjs.map +0 -1
- package/dist/encryption-C6jaU0hE.cjs +0 -54
- package/dist/encryption-C6jaU0hE.cjs.map +0 -1
- package/dist/encryption-CHzzeR8a.cjs +0 -4
- package/dist/encryption-CQUncu7v.mjs +0 -3
- package/dist/encryption-D-abyhZx.mjs +0 -42
- package/dist/encryption-D-abyhZx.mjs.map +0 -1
- package/dist/fullstack-secrets-C-GGohke.cjs +0 -111
- package/dist/fullstack-secrets-C-GGohke.cjs.map +0 -1
- package/dist/fullstack-secrets-D1o9-G5l.mjs +0 -87
- package/dist/fullstack-secrets-D1o9-G5l.mjs.map +0 -1
- package/dist/generator-CNzBw7g5.cjs +0 -289
- package/dist/generator-CNzBw7g5.cjs.map +0 -1
- package/dist/generator-VE2JrFlY.mjs +0 -253
- package/dist/generator-VE2JrFlY.mjs.map +0 -1
- package/dist/index-C6e-ULLx.d.mts.map +0 -1
- package/dist/index-CR5urhWX.d.cts.map +0 -1
- package/dist/openapi-SjsgARie.cjs +0 -2075
- package/dist/openapi-SjsgARie.cjs.map +0 -1
- package/dist/openapi-Xx0A7Wg0.mjs +0 -2021
- package/dist/openapi-Xx0A7Wg0.mjs.map +0 -1
- package/dist/openapi-react-query-Byltvm_7.mjs +0 -167
- package/dist/openapi-react-query-Byltvm_7.mjs.map +0 -1
- package/dist/openapi-react-query-CmJ1xDbn.cjs +0 -173
- package/dist/openapi-react-query-CmJ1xDbn.cjs.map +0 -1
- package/dist/openapi-react-query.cjs +0 -4
- package/dist/openapi-react-query.d.cts +0 -11
- package/dist/openapi-react-query.d.cts.map +0 -1
- package/dist/openapi-react-query.d.mts +0 -11
- package/dist/openapi-react-query.d.mts.map +0 -1
- package/dist/openapi-react-query.mjs +0 -4
- package/dist/reconcile-CFs8C_1a.mjs.map +0 -1
- package/dist/reconcile-D0pimNiy.cjs.map +0 -1
- package/dist/setup-BA8ciTPU.mjs +0 -11
- package/dist/setup-BsZvZP6f.cjs +0 -11
- package/dist/setup-C8KOx8_8.mjs +0 -443
- package/dist/setup-C8KOx8_8.mjs.map +0 -1
- package/dist/setup-hC6hI86f.cjs +0 -473
- package/dist/setup-hC6hI86f.cjs.map +0 -1
- package/dist/storage-BJmzYxt8.cjs +0 -363
- package/dist/storage-BJmzYxt8.cjs.map +0 -1
- package/dist/storage-BM9hGpgv.cjs +0 -7
- package/dist/storage-Bi24WfoW.mjs +0 -297
- package/dist/storage-Bi24WfoW.mjs.map +0 -1
- package/dist/storage-CYlGwWTj.mjs +0 -3
- package/dist/sync-CmEuNZsB.mjs +0 -76
- package/dist/sync-CmEuNZsB.mjs.map +0 -1
- package/dist/sync-D306yOWu.cjs +0 -4
- package/dist/sync-SizaChmC.cjs +0 -93
- package/dist/sync-SizaChmC.cjs.map +0 -1
- package/dist/sync-hOqAWHR6.mjs +0 -3
- package/dist/types-6pkp2RRj.d.mts +0 -2220
- package/dist/types-6pkp2RRj.d.mts.map +0 -1
- package/dist/types-Y3fKQSAK.d.cts +0 -2220
- package/dist/types-Y3fKQSAK.d.cts.map +0 -1
- package/dist/workspace-BzrG_2SI.mjs +0 -8
- package/dist/workspace-C6VJQVtG.cjs.map +0 -1
- package/dist/workspace-XXtNhqJl.cjs +0 -9
- package/dist/workspace-zoqRbihZ.mjs.map +0 -1
|
@@ -0,0 +1,1057 @@
|
|
|
1
|
+
const require_errors = require("./errors-DMC4Umsb.cjs");
|
|
2
|
+
const require_workspace = require("./workspace-BwqQnool.cjs");
|
|
3
|
+
const require_seeds = require("./seeds-Dk4WHhjU.cjs");
|
|
4
|
+
let node_path = require("node:path");
|
|
5
|
+
let node_fs = require("node:fs");
|
|
6
|
+
let node_net = require("node:net");
|
|
7
|
+
let node_crypto = require("node:crypto");
|
|
8
|
+
let _geekmidas_manifest = require("@geekmidas/manifest");
|
|
9
|
+
let node_module = require("node:module");
|
|
10
|
+
let node_url = require("node:url");
|
|
11
|
+
let node_dns_promises = require("node:dns/promises");
|
|
12
|
+
//#region src/providers/dns.ts
|
|
13
|
+
/** Whether the workspace deploys through the compose target. */
|
|
14
|
+
function deploysWithCompose(workspace) {
|
|
15
|
+
return workspace.deploy?.default === "compose" || Object.values(workspace.apps).some((app) => app.resolvedDeployTarget === "compose");
|
|
16
|
+
}
|
|
17
|
+
//#endregion
|
|
18
|
+
//#region src/backups/config.ts
|
|
19
|
+
/**
|
|
20
|
+
* Which stages take backups, and where they are kept.
|
|
21
|
+
*
|
|
22
|
+
* A deployed compose stage whose stack runs Postgres is backed up by
|
|
23
|
+
* default — every day at 02:00 UTC, kept 30 days — unless
|
|
24
|
+
* `deploy.backups.<stage>` says otherwise. The local stage never is.
|
|
25
|
+
*
|
|
26
|
+
* Every backup is kept in the project bucket (`providers/projectBucket.ts`),
|
|
27
|
+
* beside the stage's deploy state, one folder per run and one file per
|
|
28
|
+
* database:
|
|
29
|
+
*
|
|
30
|
+
* ```
|
|
31
|
+
* gkm/<project>/<stage>/backups/2026-10-10/02-00-00Z/database.sql.gz
|
|
32
|
+
* gkm/<project>/<stage>/backups/2026-10-10/02-00-00Z/auth-database.sql.gz
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* The file is named after the database's construct, the way its migrations
|
|
36
|
+
* folder is: `AuthDatabase` is `db/auth-database`, and `auth-database.sql.gz`.
|
|
37
|
+
*/
|
|
38
|
+
/** Whether the stack runs Postgres: some construct is a database. */
|
|
39
|
+
function runsPostgres(manifest) {
|
|
40
|
+
return Object.values(manifest).some((d) => d.kind === "database");
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* What `stage`'s backups are: on — by its entry or the default — off by
|
|
44
|
+
* `false`, or none, for the local stage, a stage compose does not deploy,
|
|
45
|
+
* and a stack with no database.
|
|
46
|
+
*/
|
|
47
|
+
function stageBackups(workspace, manifest, stage) {
|
|
48
|
+
if (stage === workspace.stages?.local) return {
|
|
49
|
+
mode: "none",
|
|
50
|
+
reason: "local"
|
|
51
|
+
};
|
|
52
|
+
if (!deploysWithCompose(workspace)) return {
|
|
53
|
+
mode: "none",
|
|
54
|
+
reason: "not-compose"
|
|
55
|
+
};
|
|
56
|
+
if (!runsPostgres(manifest)) return {
|
|
57
|
+
mode: "none",
|
|
58
|
+
reason: "no-postgres"
|
|
59
|
+
};
|
|
60
|
+
const value = (workspace.deploy?.backups)?.[stage];
|
|
61
|
+
const checked = require_workspace.checkStageBackups(stage, value ?? {});
|
|
62
|
+
if (checked === false) return { mode: "disabled" };
|
|
63
|
+
return {
|
|
64
|
+
mode: "on",
|
|
65
|
+
backups: checked,
|
|
66
|
+
source: value === void 0 ? "default" : "config"
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Where a stage's backups are, in the project bucket — under the same
|
|
71
|
+
* `<prefix>/<project>/<stage>` its deploy state is: `gkm/shop/production/backups`.
|
|
72
|
+
*/
|
|
73
|
+
function backupsPrefix(workspace, stage) {
|
|
74
|
+
const state = workspace.state;
|
|
75
|
+
const prefix = state?.provider === "s3" && state.prefix !== void 0 ? state.prefix.replace(/\/+$/, "") : "gkm";
|
|
76
|
+
return `${prefix ? `${prefix}/` : ""}${workspace.name}/${stage}/backups`;
|
|
77
|
+
}
|
|
78
|
+
/** A run folder's time back, or `undefined` for a key that is not one. */
|
|
79
|
+
function runTime(folder) {
|
|
80
|
+
const match = /^(\d{4}-\d{2}-\d{2})\/(\d{2})-(\d{2})-(\d{2})Z$/.exec(folder);
|
|
81
|
+
if (!match) return void 0;
|
|
82
|
+
const at = /* @__PURE__ */ new Date(`${match[1]}T${match[2]}:${match[3]}:${match[4]}Z`);
|
|
83
|
+
return Number.isNaN(at.getTime()) ? void 0 : at;
|
|
84
|
+
}
|
|
85
|
+
/** A database's file in a run's folder: `auth-database.sql.gz`. */
|
|
86
|
+
function backupFile(database) {
|
|
87
|
+
return `${database.file}.sql.gz`;
|
|
88
|
+
}
|
|
89
|
+
/** Each database the stack runs, by its construct and its name in Postgres. */
|
|
90
|
+
function backupDatabases(resources) {
|
|
91
|
+
return resources.filter((r) => r.kind === "database").map((r) => ({
|
|
92
|
+
id: r.id,
|
|
93
|
+
file: (0, _geekmidas_manifest.kebabCase)(r.id),
|
|
94
|
+
name: r.name
|
|
95
|
+
})).sort((a, b) => a.file.localeCompare(b.file));
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* The expiry of every stage's backups, by each stage's own entry — what the
|
|
99
|
+
* project bucket's lifecycle carries beside its own rule.
|
|
100
|
+
*
|
|
101
|
+
* A put replaces a bucket's whole lifecycle, so this is built from every
|
|
102
|
+
* deployed stage each time, never from the stage being deployed alone: one
|
|
103
|
+
* stage's deploy keeps every other's rule. A stage set to `false` has none,
|
|
104
|
+
* and its backups are kept until someone deletes them.
|
|
105
|
+
*/
|
|
106
|
+
function backupExpiries(workspace, manifest) {
|
|
107
|
+
const expiries = [];
|
|
108
|
+
for (const stage of [...workspace.stages?.deployed ?? []].sort()) {
|
|
109
|
+
const choice = stageBackups(workspace, manifest, stage);
|
|
110
|
+
if (choice.mode !== "on") continue;
|
|
111
|
+
expiries.push({
|
|
112
|
+
id: `gkm-backups-${stage}`,
|
|
113
|
+
prefix: `${backupsPrefix(workspace, stage)}/`,
|
|
114
|
+
days: choice.backups.keepDays,
|
|
115
|
+
abortIncompleteDays: 1
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
return expiries;
|
|
119
|
+
}
|
|
120
|
+
//#endregion
|
|
121
|
+
//#region src/backups/service.ts
|
|
122
|
+
/**
|
|
123
|
+
* The stack's `backups` service: what a deployed compose stage with Postgres
|
|
124
|
+
* runs beside its apps, unless `deploy.backups.<stage>` is `false`.
|
|
125
|
+
*
|
|
126
|
+
* - **The image** is built from the stack's own Postgres image —
|
|
127
|
+
* `postgres:<major>-alpine`, already on the server for the stack, and with
|
|
128
|
+
* the `pg_dump` that matches the server — with Node added and gkm's runner
|
|
129
|
+
* (`runner.ts`) copied in. Nothing is pulled that the stack does not have,
|
|
130
|
+
* and nothing is mounted from the host: the build context is two files.
|
|
131
|
+
* - **Its key** is `BACKUPS_URL`, from the stage's secrets, like any app's:
|
|
132
|
+
* allowed to put objects under the stage's backups prefix and nothing else.
|
|
133
|
+
* The server holds no AWS credentials of its own.
|
|
134
|
+
* - **Its login** is a role of its own, `<project>_backups`, granted
|
|
135
|
+
* `pg_read_all_data` — it can read every database and write none. Postgres
|
|
136
|
+
* before 14 has no such role, and there it signs in as the stack's
|
|
137
|
+
* superuser.
|
|
138
|
+
* - **Its health** is the last good run: unhealthy once that is more than
|
|
139
|
+
* twice the schedule's longest gap old, after a startup grace.
|
|
140
|
+
*/
|
|
141
|
+
/** The service's name in the stack. */
|
|
142
|
+
const BACKUPS_SERVICE = "backups";
|
|
143
|
+
/** The secret its key is written to. */
|
|
144
|
+
const BACKUPS_URL_KEY = "BACKUPS_URL";
|
|
145
|
+
/** Where the runner is, in the image. */
|
|
146
|
+
const RUNNER_PATH = "/gkm/backup.mjs";
|
|
147
|
+
/** The first Postgres with `pg_read_all_data`. */
|
|
148
|
+
const READ_ALL_DATA_SINCE = 14;
|
|
149
|
+
/** A stage that backs up, and no key to back up with. */
|
|
150
|
+
var BackupsUrlMissing = class extends require_errors.GkmError {
|
|
151
|
+
stage;
|
|
152
|
+
constructor(stage) {
|
|
153
|
+
super(`'${stage}' runs Postgres, so its stack backs it up, and its secrets hold no ${BACKUPS_URL_KEY} to back up with. The deploy writes it — the IAM user and key it creates in the stage's AWS account — so run the deploy with the account's credentials. To take no backups on this stage, set deploy.backups.${stage}: false.`);
|
|
154
|
+
this.stage = stage;
|
|
155
|
+
this.name = "BackupsUrlMissing";
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
/** The backups role: `<project>_backups`, as the superuser is `<project>_admin`. */
|
|
159
|
+
function backupsRole(project) {
|
|
160
|
+
return `${require_seeds.postgresRoleSlug(project)}_backups`;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* The runner's source, as JavaScript: the built `backups/runner.mjs` beside
|
|
164
|
+
* the CLI's bundle, or — run from source — `runner.ts`, with its types
|
|
165
|
+
* stripped by esbuild.
|
|
166
|
+
*/
|
|
167
|
+
function runnerSource() {
|
|
168
|
+
const here = (0, node_path.dirname)((0, node_url.fileURLToPath)(require("url").pathToFileURL(__filename).href));
|
|
169
|
+
for (const path of [
|
|
170
|
+
(0, node_path.join)(here, "backups", "runner.mjs"),
|
|
171
|
+
(0, node_path.join)(here, "runner.mjs"),
|
|
172
|
+
(0, node_path.join)(here, "..", "backups", "runner.mjs")
|
|
173
|
+
]) if ((0, node_fs.existsSync)(path)) return (0, node_fs.readFileSync)(path, "utf-8");
|
|
174
|
+
const { transformSync } = (0, node_module.createRequire)(require("url").pathToFileURL(__filename).href)("esbuild");
|
|
175
|
+
return transformSync((0, node_fs.readFileSync)((0, node_path.join)(here, "runner.ts"), "utf-8"), {
|
|
176
|
+
loader: "ts",
|
|
177
|
+
format: "esm",
|
|
178
|
+
target: "node22"
|
|
179
|
+
}).code;
|
|
180
|
+
}
|
|
181
|
+
/** The image's Dockerfile: the stack's Postgres, Node, and the runner. */
|
|
182
|
+
function backupsDockerfile(from) {
|
|
183
|
+
return `# Generated by gkm — the stage's backups: pg_dump from ${from}, and gkm's uploader.
|
|
184
|
+
FROM ${from}
|
|
185
|
+
RUN apk add --no-cache nodejs
|
|
186
|
+
COPY backup.mjs ${RUNNER_PATH}
|
|
187
|
+
USER postgres
|
|
188
|
+
ENTRYPOINT []
|
|
189
|
+
CMD ["node", "${RUNNER_PATH}", "serve"]
|
|
190
|
+
`;
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* The stack's backups service, for a stage whose backups are on.
|
|
194
|
+
*
|
|
195
|
+
* @throws {BackupsUrlMissing} when the stage's secrets hold no key
|
|
196
|
+
*/
|
|
197
|
+
function stackBackups(input) {
|
|
198
|
+
const { plan, stage } = input;
|
|
199
|
+
const version = plan.resources.find((r) => r.version)?.version ?? _geekmidas_manifest.DEFAULT_POSTGRES_VERSION;
|
|
200
|
+
const from = require_seeds.postgresImage(version);
|
|
201
|
+
const script = runnerSource();
|
|
202
|
+
const dockerfile = backupsDockerfile(from);
|
|
203
|
+
const hash = (0, node_crypto.createHash)("sha256").update(dockerfile).update(script).digest("hex").slice(0, 12);
|
|
204
|
+
const readOnly = version >= READ_ALL_DATA_SINCE;
|
|
205
|
+
const role = backupsRole(input.workspace.name);
|
|
206
|
+
const login = readOnly ? {
|
|
207
|
+
user: role,
|
|
208
|
+
password: require_seeds.localRolePassword(input.workspace.name, plan, role, input.seed)
|
|
209
|
+
} : input.superuser;
|
|
210
|
+
const databases = backupDatabases(plan.resources);
|
|
211
|
+
const prefix = backupsPrefix(input.workspace, stage);
|
|
212
|
+
const { kind, ...schedule } = input.backups.schedule;
|
|
213
|
+
let env = {};
|
|
214
|
+
if (!input.buildOnly) {
|
|
215
|
+
const url = input.custom[BACKUPS_URL_KEY];
|
|
216
|
+
if (!url) throw new BackupsUrlMissing(stage);
|
|
217
|
+
env = {
|
|
218
|
+
BACKUPS_URL: url,
|
|
219
|
+
BACKUPS_PREFIX: prefix,
|
|
220
|
+
BACKUPS_DATABASES: JSON.stringify(databases.map(({ file, name }) => ({
|
|
221
|
+
file,
|
|
222
|
+
name
|
|
223
|
+
}))),
|
|
224
|
+
BACKUPS_SCHEDULE: JSON.stringify(kind === "cron" ? {
|
|
225
|
+
kind,
|
|
226
|
+
minutes: input.backups.schedule.minutes,
|
|
227
|
+
hours: input.backups.schedule.hours,
|
|
228
|
+
days: input.backups.schedule.days,
|
|
229
|
+
months: input.backups.schedule.months,
|
|
230
|
+
weekdays: input.backups.schedule.weekdays
|
|
231
|
+
} : {
|
|
232
|
+
kind,
|
|
233
|
+
...schedule
|
|
234
|
+
}),
|
|
235
|
+
BACKUPS_MAX_GAP_SECONDS: String(input.backups.maxGapSeconds),
|
|
236
|
+
PGHOST: "postgres",
|
|
237
|
+
PGPORT: "5432",
|
|
238
|
+
PGUSER: login.user,
|
|
239
|
+
PGPASSWORD: login.password,
|
|
240
|
+
...input.telemetry?.OTEL_EXPORTER_OTLP_ENDPOINT ? {
|
|
241
|
+
OTEL_EXPORTER_OTLP_ENDPOINT: input.telemetry.OTEL_EXPORTER_OTLP_ENDPOINT,
|
|
242
|
+
...input.telemetry.OTEL_EXPORTER_OTLP_HEADERS ? { OTEL_EXPORTER_OTLP_HEADERS: input.telemetry.OTEL_EXPORTER_OTLP_HEADERS } : {},
|
|
243
|
+
OTEL_SERVICE_NAME: BACKUPS_SERVICE
|
|
244
|
+
} : {}
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
return {
|
|
248
|
+
backups: input.backups,
|
|
249
|
+
databases,
|
|
250
|
+
prefix,
|
|
251
|
+
from,
|
|
252
|
+
image: `${input.project}-backups:pg${version}-${hash}`,
|
|
253
|
+
files: {
|
|
254
|
+
Dockerfile: dockerfile,
|
|
255
|
+
"backup.mjs": script
|
|
256
|
+
},
|
|
257
|
+
login,
|
|
258
|
+
readOnly,
|
|
259
|
+
env
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
/** The service in the stack's compose file. */
|
|
263
|
+
function backupsService(backups, envFiles) {
|
|
264
|
+
return {
|
|
265
|
+
image: backups.image,
|
|
266
|
+
build: {
|
|
267
|
+
context: `./${BACKUPS_SERVICE}`,
|
|
268
|
+
dockerfile: "Dockerfile"
|
|
269
|
+
},
|
|
270
|
+
pull_policy: "build",
|
|
271
|
+
restart: "unless-stopped",
|
|
272
|
+
...envFiles ? { env_file: [{
|
|
273
|
+
path: `./${BACKUPS_SERVICE}.env`,
|
|
274
|
+
format: "raw"
|
|
275
|
+
}] } : {},
|
|
276
|
+
depends_on: { postgres: { condition: "service_healthy" } },
|
|
277
|
+
healthcheck: {
|
|
278
|
+
test: [
|
|
279
|
+
"CMD",
|
|
280
|
+
"node",
|
|
281
|
+
RUNNER_PATH,
|
|
282
|
+
"health"
|
|
283
|
+
],
|
|
284
|
+
interval: "1m",
|
|
285
|
+
timeout: "10s",
|
|
286
|
+
retries: 3,
|
|
287
|
+
start_period: "30s"
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* The backups role, created and kept: able to sign in and read every
|
|
293
|
+
* database, and nothing else. None when it signs in as the superuser.
|
|
294
|
+
*/
|
|
295
|
+
function backupsRoleStatements(backups) {
|
|
296
|
+
if (!backups.readOnly) return [];
|
|
297
|
+
const role = require_seeds.quoteIdentifier(backups.login.user);
|
|
298
|
+
const password = `'${backups.login.password.replace(/'/g, "''")}'`;
|
|
299
|
+
return [
|
|
300
|
+
{
|
|
301
|
+
id: BACKUPS_SERVICE,
|
|
302
|
+
describe: `role ${backups.login.user}`,
|
|
303
|
+
exists: {
|
|
304
|
+
sql: "SELECT 1 FROM pg_roles WHERE rolname = $1",
|
|
305
|
+
values: [backups.login.user]
|
|
306
|
+
},
|
|
307
|
+
create: `CREATE ROLE ${role} LOGIN PASSWORD ${password}`
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
id: BACKUPS_SERVICE,
|
|
311
|
+
describe: `role ${backups.login.user}: its password`,
|
|
312
|
+
create: `ALTER ROLE ${role} WITH LOGIN PASSWORD ${password}`
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
id: BACKUPS_SERVICE,
|
|
316
|
+
describe: `role ${backups.login.user}: read every database`,
|
|
317
|
+
create: `GRANT pg_read_all_data TO ${role}`
|
|
318
|
+
}
|
|
319
|
+
];
|
|
320
|
+
}
|
|
321
|
+
//#endregion
|
|
322
|
+
//#region src/compose/dns.ts
|
|
323
|
+
/**
|
|
324
|
+
* A compose stage's DNS: the records its public hosts need, and the check that
|
|
325
|
+
* they resolve to its server before a certificate is asked for.
|
|
326
|
+
*
|
|
327
|
+
* - **The server** (`serverAddress`): the stage's own secrets hold its public
|
|
328
|
+
* addresses — `GKM_SERVER_IPV4`, and `GKM_SERVER_IPV6` where it has one.
|
|
329
|
+
* Reserved for gkm: never handed to an app.
|
|
330
|
+
* - **The plan** (`planStackDns`, pure): every public host the stack serves —
|
|
331
|
+
* its apex, each app, each file server, the public log UI — pointed at the
|
|
332
|
+
* server. An A record per host (and an AAAA with an IPv6 address), or, where
|
|
333
|
+
* the domain's `dns` entry says `records: { mode: 'cname', target }`, one A
|
|
334
|
+
* record for the target and a CNAME to it for every other host but the
|
|
335
|
+
* apex, which DNS forbids a CNAME at.
|
|
336
|
+
* - **Applying it** (`applyStackDns`): through the domain's provider in `dns`,
|
|
337
|
+
* a record set at a time. A record with the right value is left alone; one
|
|
338
|
+
* with another value is replaced and the change printed. A `manual` domain
|
|
339
|
+
* is printed and nothing else. A dry run reads, prints and writes nothing.
|
|
340
|
+
* - **The check** (`checkStackDns`): each host resolved with the system
|
|
341
|
+
* resolver. One that is not the server stops the deploy with
|
|
342
|
+
* {@link HostNotPointingAtServer}, before Caddy or Traefik ask Let's Encrypt
|
|
343
|
+
* for a certificate it cannot get — and use up the rate limit trying.
|
|
344
|
+
* - **The step** (`stageDns`): all three, as every deploy runs them — the plan applied through each provider,
|
|
345
|
+
* what was written read back from the provider, and only the hosts no
|
|
346
|
+
* provider wrote resolved publicly. A brand-new name is never looked up
|
|
347
|
+
* before its record exists, so no resolver caches it as missing.
|
|
348
|
+
*/
|
|
349
|
+
/** The line that says how to set the server's address. */
|
|
350
|
+
function serverAddressHint(stage) {
|
|
351
|
+
return `gkm secrets:set ${require_workspace.SERVER_IPV4_KEY} '<ip>' --stage ${stage}`;
|
|
352
|
+
}
|
|
353
|
+
/** A server address in the stage's secrets that is not an address. */
|
|
354
|
+
var ServerAddressInvalid = class extends require_errors.GkmError {
|
|
355
|
+
stage;
|
|
356
|
+
key;
|
|
357
|
+
value;
|
|
358
|
+
constructor(stage, key, value) {
|
|
359
|
+
const ipv4 = key === require_workspace.SERVER_IPV4_KEY;
|
|
360
|
+
super(`${key} in the '${stage}' stage's secrets is ${JSON.stringify(value)}, which is not an ${ipv4 ? "IPv4 address (like 203.0.113.10)" : "IPv6 address (like 2001:db8::10)"}. Set the server's public address: gkm secrets:set ${key} '<ip>' --stage ${stage}.`);
|
|
361
|
+
this.stage = stage;
|
|
362
|
+
this.key = key;
|
|
363
|
+
this.value = value;
|
|
364
|
+
this.name = "ServerAddressInvalid";
|
|
365
|
+
}
|
|
366
|
+
};
|
|
367
|
+
/**
|
|
368
|
+
* A compose stage that serves real domains with no server address: its hosts
|
|
369
|
+
* cannot be pointed or checked, and a certificate would be asked for blind.
|
|
370
|
+
*/
|
|
371
|
+
var ServerAddressMissing = class extends require_errors.GkmError {
|
|
372
|
+
stage;
|
|
373
|
+
domain;
|
|
374
|
+
constructor(stage, domain) {
|
|
375
|
+
super(`The '${stage}' stage serves ${domain} and its secrets hold no ${require_workspace.SERVER_IPV4_KEY} — the public address of the server its stack runs on, which its hosts' DNS records point at and every deploy checks before asking for certificates. Set it: ${serverAddressHint(stage)} (and ${require_workspace.SERVER_IPV6_KEY} for an IPv6 address).`);
|
|
376
|
+
this.stage = stage;
|
|
377
|
+
this.domain = domain;
|
|
378
|
+
this.name = "ServerAddressMissing";
|
|
379
|
+
}
|
|
380
|
+
};
|
|
381
|
+
/**
|
|
382
|
+
* The server's address from the stage's secrets, checked — `undefined` when
|
|
383
|
+
* `GKM_SERVER_IPV4` is not set.
|
|
384
|
+
*
|
|
385
|
+
* @throws {ServerAddressInvalid} for a value that is not an address
|
|
386
|
+
*/
|
|
387
|
+
function serverAddress(stage, custom) {
|
|
388
|
+
const ipv4 = custom?.[require_workspace.SERVER_IPV4_KEY]?.trim();
|
|
389
|
+
const ipv6 = custom?.[require_workspace.SERVER_IPV6_KEY]?.trim();
|
|
390
|
+
if (ipv6 && !(0, node_net.isIPv6)(ipv6)) throw new ServerAddressInvalid(stage, require_workspace.SERVER_IPV6_KEY, ipv6);
|
|
391
|
+
if (!ipv4) return void 0;
|
|
392
|
+
if (!(0, node_net.isIPv4)(ipv4)) throw new ServerAddressInvalid(stage, require_workspace.SERVER_IPV4_KEY, ipv4);
|
|
393
|
+
return {
|
|
394
|
+
ipv4,
|
|
395
|
+
...ipv6 ? { ipv6 } : {}
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
/**
|
|
399
|
+
* A name that resolves to this machine by definition — `localhost` and every
|
|
400
|
+
* `*.localhost` — and is in nobody's DNS.
|
|
401
|
+
*/
|
|
402
|
+
function isLocalHost(host) {
|
|
403
|
+
const name = host.toLowerCase().replace(/\.$/, "");
|
|
404
|
+
return name === "localhost" || name.endsWith(".localhost");
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* The server's address, required where the stage serves real domains — not
|
|
408
|
+
* `*.localhost` ones, which point at this machine already.
|
|
409
|
+
*
|
|
410
|
+
* @throws {ServerAddressMissing} when the stage has a domain and no address
|
|
411
|
+
* @throws {ServerAddressInvalid} for a value that is not an address
|
|
412
|
+
*/
|
|
413
|
+
function requiredServerAddress(stage, domain, custom) {
|
|
414
|
+
const server = serverAddress(stage, custom);
|
|
415
|
+
if (!server && domain && !isLocalHost(domain)) throw new ServerAddressMissing(stage, domain);
|
|
416
|
+
return server;
|
|
417
|
+
}
|
|
418
|
+
const MANAGED = [
|
|
419
|
+
"A",
|
|
420
|
+
"AAAA",
|
|
421
|
+
"CNAME"
|
|
422
|
+
];
|
|
423
|
+
/** Every public host the stack serves, once each, sorted. */
|
|
424
|
+
function stackHosts(stack) {
|
|
425
|
+
return [...new Set(stack.routes.map((route) => route.host))].sort();
|
|
426
|
+
}
|
|
427
|
+
/** The TTL a domain's records are written with. */
|
|
428
|
+
function recordTtl(config) {
|
|
429
|
+
const ttl = "ttl" in config ? config.ttl : void 0;
|
|
430
|
+
if (ttl) return ttl;
|
|
431
|
+
return config.provider === "godaddy" ? 600 : 300;
|
|
432
|
+
}
|
|
433
|
+
function providerName(config) {
|
|
434
|
+
return typeof config.provider === "string" ? config.provider : "custom";
|
|
435
|
+
}
|
|
436
|
+
/**
|
|
437
|
+
* A wildcard host: gkm writes one record per public host, so a stage on
|
|
438
|
+
* another server — or a name someone else points elsewhere — is never caught
|
|
439
|
+
* by a record it did not ask for.
|
|
440
|
+
*/
|
|
441
|
+
var DnsWildcardRefused = class extends require_errors.GkmError {
|
|
442
|
+
stage;
|
|
443
|
+
host;
|
|
444
|
+
constructor(stage, host) {
|
|
445
|
+
super(`'${stage}' would point ${host} at its server, and gkm never writes a wildcard record: each public host gets its own. Give the app a subdomain of its own instead.`);
|
|
446
|
+
this.stage = stage;
|
|
447
|
+
this.host = host;
|
|
448
|
+
this.name = "DnsWildcardRefused";
|
|
449
|
+
}
|
|
450
|
+
};
|
|
451
|
+
/** The records a stage's hosts need — pure. */
|
|
452
|
+
function planStackDns(input) {
|
|
453
|
+
const { stage, server } = input;
|
|
454
|
+
const dns = input.dns ? require_seeds.normalizeDnsConfig(input.dns) : {};
|
|
455
|
+
const records = /* @__PURE__ */ new Map();
|
|
456
|
+
const uncovered = [];
|
|
457
|
+
const add = (host, domain, type, value) => {
|
|
458
|
+
const name = require_seeds.extractSubdomain(host, domain);
|
|
459
|
+
records.set(`${domain} ${name} ${type}`, {
|
|
460
|
+
host,
|
|
461
|
+
domain,
|
|
462
|
+
name,
|
|
463
|
+
type,
|
|
464
|
+
value,
|
|
465
|
+
ttl: recordTtl(dns[domain])
|
|
466
|
+
});
|
|
467
|
+
};
|
|
468
|
+
const pointAt = (host, domain) => {
|
|
469
|
+
add(host, domain, "A", server.ipv4);
|
|
470
|
+
if (server.ipv6) add(host, domain, "AAAA", server.ipv6);
|
|
471
|
+
};
|
|
472
|
+
for (const raw of input.hosts) {
|
|
473
|
+
const host = raw.toLowerCase();
|
|
474
|
+
if (host.includes("*")) throw new DnsWildcardRefused(stage, host);
|
|
475
|
+
const domain = require_seeds.findRootDomain(host, dns);
|
|
476
|
+
if (!domain) {
|
|
477
|
+
uncovered.push(host);
|
|
478
|
+
continue;
|
|
479
|
+
}
|
|
480
|
+
const target = require_workspace.cnameTarget(dns[domain].records, stage);
|
|
481
|
+
if (target) pointAt(target, domain);
|
|
482
|
+
if (target && host !== domain && host !== target) add(host, domain, "CNAME", target);
|
|
483
|
+
else pointAt(host, domain);
|
|
484
|
+
}
|
|
485
|
+
const domains = /* @__PURE__ */ new Map();
|
|
486
|
+
for (const record of [...records.values()].sort((a, b) => a.domain.localeCompare(b.domain) || a.name.localeCompare(b.name) || a.type.localeCompare(b.type))) {
|
|
487
|
+
const config = dns[record.domain];
|
|
488
|
+
const plan = domains.get(record.domain) ?? {
|
|
489
|
+
domain: record.domain,
|
|
490
|
+
provider: providerName(config),
|
|
491
|
+
config,
|
|
492
|
+
records: []
|
|
493
|
+
};
|
|
494
|
+
plan.records.push(record);
|
|
495
|
+
domains.set(record.domain, plan);
|
|
496
|
+
}
|
|
497
|
+
return {
|
|
498
|
+
stage,
|
|
499
|
+
server,
|
|
500
|
+
domains: [...domains.values()],
|
|
501
|
+
uncovered: uncovered.sort()
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
/** Records a provider accepted and then did not read back with their value. */
|
|
505
|
+
var DnsRecordsNotConfirmed = class extends require_errors.GkmError {
|
|
506
|
+
stage;
|
|
507
|
+
domain;
|
|
508
|
+
provider;
|
|
509
|
+
records;
|
|
510
|
+
constructor(stage, domain, provider, records) {
|
|
511
|
+
const lines = records.map((r) => ` - ${r.host} ${r.type}: ${r.found.length > 0 ? r.found.join(", ") : "nothing"}, expected ${r.expected}`);
|
|
512
|
+
super(`${provider} accepted '${stage}''s records for ${domain}, and reading them back does not show them:\n${lines.join("\n")}\nDeploy again; if it persists, check the records in the provider's console — another tool may be writing the same names.`);
|
|
513
|
+
this.stage = stage;
|
|
514
|
+
this.domain = domain;
|
|
515
|
+
this.provider = provider;
|
|
516
|
+
this.records = records;
|
|
517
|
+
this.name = "DnsRecordsNotConfirmed";
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
/** `api.example.com A 203.0.113.10 (TTL 600)` */
|
|
521
|
+
function describe(record) {
|
|
522
|
+
return `${record.host.padEnd(32)} ${record.type.padEnd(5)} ${record.value} (TTL ${record.ttl})`;
|
|
523
|
+
}
|
|
524
|
+
function printManual(plan, log, why) {
|
|
525
|
+
log(` ${plan.domain}: ${why} — create these records at its DNS host:`);
|
|
526
|
+
for (const record of plan.records) log(` ${describe(record)}`);
|
|
527
|
+
return plan.records.map((r) => ({
|
|
528
|
+
domain: r.domain,
|
|
529
|
+
name: r.name,
|
|
530
|
+
type: r.type,
|
|
531
|
+
action: "manual",
|
|
532
|
+
value: r.value,
|
|
533
|
+
ttl: r.ttl
|
|
534
|
+
}));
|
|
535
|
+
}
|
|
536
|
+
/** The existing records of every planned name, for the types gkm manages. */
|
|
537
|
+
async function existingRecords(provider, plan) {
|
|
538
|
+
const names = [...new Set(plan.records.map((r) => r.name))];
|
|
539
|
+
const wanted = names.flatMap((name) => MANAGED.map((type) => ({
|
|
540
|
+
name,
|
|
541
|
+
type
|
|
542
|
+
})));
|
|
543
|
+
if (provider.readRecords) return provider.readRecords(plan.domain, wanted);
|
|
544
|
+
return (await provider.getRecords(plan.domain)).filter((r) => names.includes(r.name) && MANAGED.includes(r.type));
|
|
545
|
+
}
|
|
546
|
+
const same = (a, b) => a.trim().replace(/\.$/, "").toLowerCase() === b.trim().replace(/\.$/, "").toLowerCase();
|
|
547
|
+
/**
|
|
548
|
+
* What each domain's records need, compared with what is there: creates,
|
|
549
|
+
* updates, and the deletes a change of mode needs — a name moving from an A
|
|
550
|
+
* record to a CNAME, or back, cannot hold both. Nothing else is deleted.
|
|
551
|
+
*/
|
|
552
|
+
function diffDomainDns(plan, existing) {
|
|
553
|
+
const changes = [];
|
|
554
|
+
const find = (name, type) => existing.find((r) => r.name === name && r.type === type);
|
|
555
|
+
const planned = /* @__PURE__ */ new Map();
|
|
556
|
+
for (const record of plan.records) {
|
|
557
|
+
const types = planned.get(record.name) ?? /* @__PURE__ */ new Set();
|
|
558
|
+
types.add(record.type);
|
|
559
|
+
planned.set(record.name, types);
|
|
560
|
+
}
|
|
561
|
+
for (const [name, types] of planned) {
|
|
562
|
+
const conflicting = types.has("CNAME") ? ["A", "AAAA"] : ["CNAME"];
|
|
563
|
+
for (const type of conflicting) {
|
|
564
|
+
const found = find(name, type);
|
|
565
|
+
if (!found || found.values.length === 0) continue;
|
|
566
|
+
changes.push({
|
|
567
|
+
domain: plan.domain,
|
|
568
|
+
name,
|
|
569
|
+
type,
|
|
570
|
+
action: "delete",
|
|
571
|
+
previous: found.values.join(", ")
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
for (const record of plan.records) {
|
|
576
|
+
const found = find(record.name, record.type);
|
|
577
|
+
const base = {
|
|
578
|
+
domain: plan.domain,
|
|
579
|
+
name: record.name,
|
|
580
|
+
type: record.type,
|
|
581
|
+
value: record.value,
|
|
582
|
+
ttl: record.ttl
|
|
583
|
+
};
|
|
584
|
+
if (!found || found.values.length === 0) changes.push({
|
|
585
|
+
...base,
|
|
586
|
+
action: "create"
|
|
587
|
+
});
|
|
588
|
+
else if (found.values.length === 1 && same(found.values[0], record.value)) changes.push({
|
|
589
|
+
...base,
|
|
590
|
+
action: "unchanged"
|
|
591
|
+
});
|
|
592
|
+
else changes.push({
|
|
593
|
+
...base,
|
|
594
|
+
action: "update",
|
|
595
|
+
previous: found.values.join(", ")
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
return changes;
|
|
599
|
+
}
|
|
600
|
+
/** A host's full name from a domain and a relative name. */
|
|
601
|
+
function fullName(domain, name) {
|
|
602
|
+
return name === "@" ? domain : `${name}.${domain}`;
|
|
603
|
+
}
|
|
604
|
+
function printChange(change, dryRun, log) {
|
|
605
|
+
const host = fullName(change.domain, change.name).padEnd(32);
|
|
606
|
+
const type = change.type.padEnd(5);
|
|
607
|
+
const would = dryRun ? "would " : "";
|
|
608
|
+
switch (change.action) {
|
|
609
|
+
case "unchanged":
|
|
610
|
+
log(` ✓ ${host} ${type} ${change.value} (TTL ${change.ttl}) — up to date`);
|
|
611
|
+
break;
|
|
612
|
+
case "create":
|
|
613
|
+
log(` + ${host} ${type} ${change.value} (TTL ${change.ttl}) — ${would}create`);
|
|
614
|
+
break;
|
|
615
|
+
case "update":
|
|
616
|
+
log(` ~ ${host} ${type} ${change.previous} → ${change.value} (TTL ${change.ttl}) — ${would}update`);
|
|
617
|
+
break;
|
|
618
|
+
case "write":
|
|
619
|
+
log(dryRun ? ` ? ${host} ${type} ${change.value} (TTL ${change.ttl}) — would write; current value unknown (key cannot read DNS)` : ` ! ${host} ${type} ${change.value} (TTL ${change.ttl}) — written (not diffed)`);
|
|
620
|
+
break;
|
|
621
|
+
case "delete": log(` - ${host} ${type} ${change.previous} — ${would}delete (a name cannot hold a CNAME and an A record)`);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Each domain's records, through its provider — and, with `confirm`, what was
|
|
626
|
+
* written read back from it. A record already holding its value is neither
|
|
627
|
+
* written nor read again.
|
|
628
|
+
*
|
|
629
|
+
* @throws {DnsRecordsNotConfirmed} when a written record does not read back
|
|
630
|
+
*/
|
|
631
|
+
async function settleStackDns(plan, options = {}) {
|
|
632
|
+
const dryRun = options.dryRun === true;
|
|
633
|
+
const log = options.log ?? ((line) => console.log(line));
|
|
634
|
+
const providerFor = options.providerFor ?? ((config) => require_seeds.createDnsProvider({
|
|
635
|
+
config,
|
|
636
|
+
...options.credentials ? { godaddy: options.credentials } : {},
|
|
637
|
+
...options.hostinger ? { hostinger: options.hostinger } : {}
|
|
638
|
+
}));
|
|
639
|
+
const changes = [];
|
|
640
|
+
const confirmed = /* @__PURE__ */ new Set();
|
|
641
|
+
const unconfirmed = /* @__PURE__ */ new Set();
|
|
642
|
+
const hostsOf = (domain) => new Set(domain.records.map((r) => r.host));
|
|
643
|
+
for (const host of plan.uncovered) log(` ⚠ ${host} is under no domain in dns — point it at ${plan.server.ipv4} yourself`);
|
|
644
|
+
for (const domain of plan.domains) {
|
|
645
|
+
if (domain.provider === "manual") {
|
|
646
|
+
changes.push(...printManual(domain, log, "manual"));
|
|
647
|
+
for (const host of hostsOf(domain)) unconfirmed.add(host);
|
|
648
|
+
continue;
|
|
649
|
+
}
|
|
650
|
+
const provider = await providerFor(domain.config);
|
|
651
|
+
if (!provider) {
|
|
652
|
+
changes.push(...printManual(domain, log, "manual"));
|
|
653
|
+
for (const host of hostsOf(domain)) unconfirmed.add(host);
|
|
654
|
+
continue;
|
|
655
|
+
}
|
|
656
|
+
log(` ${domain.domain} (${provider.name})${dryRun ? " — dry run" : ""}`);
|
|
657
|
+
let existing;
|
|
658
|
+
let diff;
|
|
659
|
+
let unreadable = false;
|
|
660
|
+
try {
|
|
661
|
+
existing = await existingRecords(provider, domain);
|
|
662
|
+
diff = diffDomainDns(domain, existing);
|
|
663
|
+
} catch (error) {
|
|
664
|
+
if (!(error instanceof require_seeds.DnsRecordsUnreadable)) throw error;
|
|
665
|
+
log(` ℹ The ${provider.name} key cannot read ${domain.domain}'s DNS records, so changes cannot be diffed: every record is written (idempotent).`);
|
|
666
|
+
unreadable = true;
|
|
667
|
+
existing = [];
|
|
668
|
+
diff = domain.records.map((r) => ({
|
|
669
|
+
domain: r.domain,
|
|
670
|
+
name: r.name,
|
|
671
|
+
type: r.type,
|
|
672
|
+
action: "write",
|
|
673
|
+
value: r.value,
|
|
674
|
+
ttl: r.ttl
|
|
675
|
+
}));
|
|
676
|
+
}
|
|
677
|
+
for (const change of diff) printChange(change, dryRun, log);
|
|
678
|
+
if (!plan.server.ipv6) for (const name of new Set(domain.records.map((r) => r.name))) {
|
|
679
|
+
const aaaa = existing.find((r) => r.name === name && r.type === "AAAA");
|
|
680
|
+
const planned = domain.records.find((r) => r.name === name && r.type === "A");
|
|
681
|
+
if (aaaa && planned && aaaa.values.length > 0) log(` ⚠ ${fullName(domain.domain, name)} also has AAAA ${aaaa.values.join(", ")}: set ${require_workspace.SERVER_IPV6_KEY} in the '${plan.stage}' stage's secrets to the server's IPv6 address, or remove that record, or IPv6 clients go elsewhere`);
|
|
682
|
+
}
|
|
683
|
+
changes.push(...diff);
|
|
684
|
+
const written = new Set(diff.filter((c) => c.action !== "unchanged").map((c) => fullName(c.domain, c.name)));
|
|
685
|
+
for (const host of hostsOf(domain)) if (unreadable) {
|
|
686
|
+
if (!dryRun) unconfirmed.add(host);
|
|
687
|
+
} else if (!written.has(host)) confirmed.add(host);
|
|
688
|
+
if (dryRun) continue;
|
|
689
|
+
const deletes = diff.filter((c) => c.action === "delete");
|
|
690
|
+
if (deletes.length > 0) await provider.deleteRecords(domain.domain, deletes.map((c) => ({
|
|
691
|
+
name: c.name,
|
|
692
|
+
type: c.type
|
|
693
|
+
})));
|
|
694
|
+
const writes = diff.filter((c) => c.action === "create" || c.action === "update" || c.action === "write");
|
|
695
|
+
if (writes.length > 0) await provider.upsertRecords(domain.domain, writes.map((c) => ({
|
|
696
|
+
name: c.name,
|
|
697
|
+
type: c.type,
|
|
698
|
+
value: c.value,
|
|
699
|
+
ttl: c.ttl
|
|
700
|
+
})));
|
|
701
|
+
if (!options.confirm || unreadable || written.size === 0) continue;
|
|
702
|
+
const rewritten = {
|
|
703
|
+
...domain,
|
|
704
|
+
records: domain.records.filter((r) => written.has(r.host))
|
|
705
|
+
};
|
|
706
|
+
const after = await existingRecords(provider, rewritten);
|
|
707
|
+
const missing = [];
|
|
708
|
+
for (const record of rewritten.records) {
|
|
709
|
+
const values = after.find((r) => r.name === record.name && r.type === record.type)?.values ?? [];
|
|
710
|
+
if (values.length !== 1 || !same(values[0], record.value)) missing.push({
|
|
711
|
+
host: record.host,
|
|
712
|
+
type: record.type,
|
|
713
|
+
expected: record.value,
|
|
714
|
+
found: values
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
if (missing.length > 0) throw new DnsRecordsNotConfirmed(plan.stage, domain.domain, provider.name, missing);
|
|
718
|
+
for (const host of written) confirmed.add(host);
|
|
719
|
+
log(` ✓ ${written.size} host${written.size === 1 ? "" : "s"} read back from ${provider.name}`);
|
|
720
|
+
}
|
|
721
|
+
for (const host of plan.uncovered) unconfirmed.add(host);
|
|
722
|
+
return {
|
|
723
|
+
changes,
|
|
724
|
+
confirmed: [...confirmed].sort(),
|
|
725
|
+
unconfirmed: [...unconfirmed].filter((h) => !confirmed.has(h)).sort()
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
/** The system resolver: what the server's own clients would be told. */
|
|
729
|
+
const systemLookup = async (host) => {
|
|
730
|
+
return (await (0, node_dns_promises.lookup)(host, { all: true })).map((a) => a.address);
|
|
731
|
+
};
|
|
732
|
+
/** Hosts that do not resolve to the stage's server. */
|
|
733
|
+
var HostNotPointingAtServer = class extends require_errors.GkmError {
|
|
734
|
+
stage;
|
|
735
|
+
hosts;
|
|
736
|
+
expected;
|
|
737
|
+
fix;
|
|
738
|
+
constructor(stage, hosts, expected, fix) {
|
|
739
|
+
const want = expected.ipv6 ? `${expected.ipv4} (and ${expected.ipv6})` : expected.ipv4;
|
|
740
|
+
const lines = hosts.map((h) => ` - ${h.host} → ${h.resolved.length > 0 ? h.resolved.join(", ") : `nothing${h.error ? ` (${h.error})` : ""}`}, expected ${want}`);
|
|
741
|
+
super(`${hosts.length === 1 ? "A host" : `${hosts.length} hosts`} of '${stage}' do${hosts.length === 1 ? "es" : ""} not resolve to its server, so a certificate for ${hosts.length === 1 ? "it" : "them"} cannot be issued:\n${lines.join("\n")}\n${fix}\nRecords can take a few minutes to propagate. With a CDN or proxy in front of the server, pass --skip-dns.`);
|
|
742
|
+
this.stage = stage;
|
|
743
|
+
this.hosts = hosts;
|
|
744
|
+
this.expected = expected;
|
|
745
|
+
this.fix = fix;
|
|
746
|
+
this.name = "HostNotPointingAtServer";
|
|
747
|
+
}
|
|
748
|
+
};
|
|
749
|
+
/**
|
|
750
|
+
* Every host resolved: each must resolve to the server's IPv4 address and to
|
|
751
|
+
* nothing but the server's addresses.
|
|
752
|
+
*
|
|
753
|
+
* @throws {HostNotPointingAtServer} naming every host that does not
|
|
754
|
+
*/
|
|
755
|
+
async function checkStackDns(input) {
|
|
756
|
+
const lookup = input.lookup ?? systemLookup;
|
|
757
|
+
const { server } = input;
|
|
758
|
+
const ours = new Set([server.ipv4, server.ipv6].filter(Boolean).map((a) => a.toLowerCase()));
|
|
759
|
+
const bad = [];
|
|
760
|
+
const ok = [];
|
|
761
|
+
for (const host of input.hosts) {
|
|
762
|
+
let resolved;
|
|
763
|
+
try {
|
|
764
|
+
resolved = [...new Set(await lookup(host))];
|
|
765
|
+
} catch (error) {
|
|
766
|
+
const code = error && typeof error === "object" && "code" in error ? String(error.code) : error instanceof Error ? error.message : String(error);
|
|
767
|
+
bad.push({
|
|
768
|
+
host,
|
|
769
|
+
resolved: [],
|
|
770
|
+
error: code
|
|
771
|
+
});
|
|
772
|
+
continue;
|
|
773
|
+
}
|
|
774
|
+
if (resolved.some((a) => a.toLowerCase() === server.ipv4.toLowerCase()) && resolved.every((a) => ours.has(a.toLowerCase()))) ok.push(host);
|
|
775
|
+
else bad.push({
|
|
776
|
+
host,
|
|
777
|
+
resolved
|
|
778
|
+
});
|
|
779
|
+
}
|
|
780
|
+
if (bad.length > 0) throw new HostNotPointingAtServer(input.stage, bad, {
|
|
781
|
+
ipv4: server.ipv4,
|
|
782
|
+
...server.ipv6 ? { ipv6: server.ipv6 } : {}
|
|
783
|
+
}, dnsFix(input, bad));
|
|
784
|
+
return ok;
|
|
785
|
+
}
|
|
786
|
+
/** How to fix it: the deploy, or the records to create by hand. */
|
|
787
|
+
function dnsFix(input, bad) {
|
|
788
|
+
const hosts = bad.map((h) => h.host);
|
|
789
|
+
let plan;
|
|
790
|
+
try {
|
|
791
|
+
plan = planStackDns({
|
|
792
|
+
stage: input.stage,
|
|
793
|
+
hosts,
|
|
794
|
+
server: input.server,
|
|
795
|
+
dns: input.dns
|
|
796
|
+
});
|
|
797
|
+
} catch {
|
|
798
|
+
plan = {
|
|
799
|
+
stage: input.stage,
|
|
800
|
+
server: input.server,
|
|
801
|
+
domains: [],
|
|
802
|
+
uncovered: hosts
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
const automatic = plan.domains.filter((d) => d.provider !== "manual");
|
|
806
|
+
const byHand = [...plan.domains.filter((d) => d.provider === "manual").flatMap((d) => d.records.map(describe)), ...plan.uncovered.flatMap((host) => [`${host.padEnd(32)} A ${input.server.ipv4}`, ...input.server.ipv6 ? [`${host.padEnd(32)} AAAA ${input.server.ipv6}`] : []])];
|
|
807
|
+
const lines = [];
|
|
808
|
+
if (automatic.length > 0) lines.push(`Fix: gkm deploy --stage ${input.stage} writes the records for ${automatic.map((d) => d.domain).join(", ")} through their provider (--dry-run shows them first).`);
|
|
809
|
+
if (byHand.length > 0) lines.push(`${automatic.length > 0 ? "And create" : "Fix: create"} these records at the DNS host:`, ...byHand.map((line) => ` ${line}`));
|
|
810
|
+
return lines.join("\n");
|
|
811
|
+
}
|
|
812
|
+
/** The environment variable each token-authenticated provider reads. */
|
|
813
|
+
const DNS_TOKEN_KEYS = {
|
|
814
|
+
godaddy: "GODADDY_API_TOKEN",
|
|
815
|
+
hostinger: "HOSTINGER_API_TOKEN"
|
|
816
|
+
};
|
|
817
|
+
/**
|
|
818
|
+
* A domain whose records the deploy writes, and no token to write them with.
|
|
819
|
+
* Raised before anything is built, started or written.
|
|
820
|
+
*/
|
|
821
|
+
var DnsCredentialMissing = class extends require_errors.GkmError {
|
|
822
|
+
stage;
|
|
823
|
+
domain;
|
|
824
|
+
provider;
|
|
825
|
+
key;
|
|
826
|
+
constructor(stage, domain, provider, key) {
|
|
827
|
+
super(`'${stage}''s hosts are under ${domain}, whose DNS is ${provider}'s, and the deploy writes their records — but ${key} is not set. In CI, add ${key} as a secret on the '${stage}' GitHub environment and pass it to the step that writes the records: env: { ${key}: \${{ secrets.${key} }} }. On this machine, export ${key} or run gkm login --provider ${provider}. With a CDN or proxy in front of the server, pass --skip-dns.`);
|
|
828
|
+
this.stage = stage;
|
|
829
|
+
this.domain = domain;
|
|
830
|
+
this.provider = provider;
|
|
831
|
+
this.key = key;
|
|
832
|
+
this.name = "DnsCredentialMissing";
|
|
833
|
+
}
|
|
834
|
+
};
|
|
835
|
+
/**
|
|
836
|
+
* The `dns` domain a stage's own domain (`domains.<stage>`) is under — where
|
|
837
|
+
* its hosts' records go — or none.
|
|
838
|
+
*/
|
|
839
|
+
function stageDnsDomains(stage, domains, dns) {
|
|
840
|
+
const own = domains?.[stage];
|
|
841
|
+
if (!own || !dns || isLocalHost(own)) return [];
|
|
842
|
+
const normalized = require_seeds.normalizeDnsConfig(dns);
|
|
843
|
+
const root = require_seeds.findRootDomain(own.toLowerCase(), normalized);
|
|
844
|
+
return root ? [{
|
|
845
|
+
domain: root,
|
|
846
|
+
config: normalized[root]
|
|
847
|
+
}] : [];
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Each token the domains' providers need, from `credentials` — `manual`,
|
|
851
|
+
* Route53 (the AWS SDK's own chain) and a provider object need none.
|
|
852
|
+
*
|
|
853
|
+
* @throws {DnsCredentialMissing} naming the first domain with no token
|
|
854
|
+
*/
|
|
855
|
+
async function dnsCredentials(stage, domains, credentials) {
|
|
856
|
+
const found = {};
|
|
857
|
+
for (const { domain, config } of domains) {
|
|
858
|
+
const provider = config.provider;
|
|
859
|
+
if (provider !== "godaddy" && provider !== "hostinger") continue;
|
|
860
|
+
if (found[provider]) continue;
|
|
861
|
+
const token = await credentials.get({ kind: provider });
|
|
862
|
+
if (!token?.token) throw new DnsCredentialMissing(stage, domain, provider, DNS_TOKEN_KEYS[provider]);
|
|
863
|
+
found[provider] = token;
|
|
864
|
+
}
|
|
865
|
+
return found;
|
|
866
|
+
}
|
|
867
|
+
/**
|
|
868
|
+
* A stage's DNS, as every deploy runs it: the
|
|
869
|
+
* stage's own hosts planned, written through each domain's provider where
|
|
870
|
+
* they differ, and read back from it. Only A, AAAA and CNAME records of
|
|
871
|
+
* those names are touched; nothing is a wildcard, and no other name is read,
|
|
872
|
+
* written or deleted. What no provider confirms — a `manual` domain, a host
|
|
873
|
+
* under no domain in `dns`, a key that cannot read — is resolved publicly.
|
|
874
|
+
* A dry run prints the plan and writes nothing.
|
|
875
|
+
*
|
|
876
|
+
* @throws {DnsCredentialMissing} before anything is read or written
|
|
877
|
+
* @throws {DnsRecordsNotConfirmed} when a written record does not read back
|
|
878
|
+
* @throws {HostNotPointingAtServer} when a resolved host is not the server
|
|
879
|
+
*/
|
|
880
|
+
async function stageDns(input) {
|
|
881
|
+
const { stage, server } = input;
|
|
882
|
+
const log = input.log ?? ((line) => console.log(line));
|
|
883
|
+
const plan = planStackDns({
|
|
884
|
+
stage,
|
|
885
|
+
hosts: input.hosts,
|
|
886
|
+
server,
|
|
887
|
+
dns: input.dns
|
|
888
|
+
});
|
|
889
|
+
const tokens = await dnsCredentials(stage, plan.domains.map(({ domain, config }) => ({
|
|
890
|
+
domain,
|
|
891
|
+
config
|
|
892
|
+
})), input.credentials);
|
|
893
|
+
const applied = !input.dns ? {
|
|
894
|
+
changes: [],
|
|
895
|
+
confirmed: [],
|
|
896
|
+
unconfirmed: [...plan.uncovered]
|
|
897
|
+
} : await settleStackDns(plan, {
|
|
898
|
+
...input.dryRun ? { dryRun: true } : { confirm: true },
|
|
899
|
+
log,
|
|
900
|
+
credentials: {
|
|
901
|
+
...input.godaddy ?? {},
|
|
902
|
+
...tokens.godaddy ? { credential: tokens.godaddy } : {},
|
|
903
|
+
log
|
|
904
|
+
},
|
|
905
|
+
...tokens.hostinger ? { hostinger: tokens.hostinger } : {},
|
|
906
|
+
...input.providerFor ? { providerFor: input.providerFor } : {}
|
|
907
|
+
});
|
|
908
|
+
let resolved = [];
|
|
909
|
+
if (applied.unconfirmed.length > 0) resolved = await checkStackDns({
|
|
910
|
+
stage,
|
|
911
|
+
hosts: applied.unconfirmed,
|
|
912
|
+
server,
|
|
913
|
+
dns: input.dns,
|
|
914
|
+
...input.lookup ? { lookup: input.lookup } : {}
|
|
915
|
+
});
|
|
916
|
+
return {
|
|
917
|
+
plan,
|
|
918
|
+
changes: applied.changes,
|
|
919
|
+
confirmed: applied.confirmed,
|
|
920
|
+
resolved
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
//#endregion
|
|
924
|
+
Object.defineProperty(exports, "BACKUPS_SERVICE", {
|
|
925
|
+
enumerable: true,
|
|
926
|
+
get: function() {
|
|
927
|
+
return BACKUPS_SERVICE;
|
|
928
|
+
}
|
|
929
|
+
});
|
|
930
|
+
Object.defineProperty(exports, "BACKUPS_URL_KEY", {
|
|
931
|
+
enumerable: true,
|
|
932
|
+
get: function() {
|
|
933
|
+
return BACKUPS_URL_KEY;
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
Object.defineProperty(exports, "HostNotPointingAtServer", {
|
|
937
|
+
enumerable: true,
|
|
938
|
+
get: function() {
|
|
939
|
+
return HostNotPointingAtServer;
|
|
940
|
+
}
|
|
941
|
+
});
|
|
942
|
+
Object.defineProperty(exports, "RUNNER_PATH", {
|
|
943
|
+
enumerable: true,
|
|
944
|
+
get: function() {
|
|
945
|
+
return RUNNER_PATH;
|
|
946
|
+
}
|
|
947
|
+
});
|
|
948
|
+
Object.defineProperty(exports, "backupDatabases", {
|
|
949
|
+
enumerable: true,
|
|
950
|
+
get: function() {
|
|
951
|
+
return backupDatabases;
|
|
952
|
+
}
|
|
953
|
+
});
|
|
954
|
+
Object.defineProperty(exports, "backupExpiries", {
|
|
955
|
+
enumerable: true,
|
|
956
|
+
get: function() {
|
|
957
|
+
return backupExpiries;
|
|
958
|
+
}
|
|
959
|
+
});
|
|
960
|
+
Object.defineProperty(exports, "backupFile", {
|
|
961
|
+
enumerable: true,
|
|
962
|
+
get: function() {
|
|
963
|
+
return backupFile;
|
|
964
|
+
}
|
|
965
|
+
});
|
|
966
|
+
Object.defineProperty(exports, "backupsPrefix", {
|
|
967
|
+
enumerable: true,
|
|
968
|
+
get: function() {
|
|
969
|
+
return backupsPrefix;
|
|
970
|
+
}
|
|
971
|
+
});
|
|
972
|
+
Object.defineProperty(exports, "backupsRoleStatements", {
|
|
973
|
+
enumerable: true,
|
|
974
|
+
get: function() {
|
|
975
|
+
return backupsRoleStatements;
|
|
976
|
+
}
|
|
977
|
+
});
|
|
978
|
+
Object.defineProperty(exports, "backupsService", {
|
|
979
|
+
enumerable: true,
|
|
980
|
+
get: function() {
|
|
981
|
+
return backupsService;
|
|
982
|
+
}
|
|
983
|
+
});
|
|
984
|
+
Object.defineProperty(exports, "deploysWithCompose", {
|
|
985
|
+
enumerable: true,
|
|
986
|
+
get: function() {
|
|
987
|
+
return deploysWithCompose;
|
|
988
|
+
}
|
|
989
|
+
});
|
|
990
|
+
Object.defineProperty(exports, "dnsCredentials", {
|
|
991
|
+
enumerable: true,
|
|
992
|
+
get: function() {
|
|
993
|
+
return dnsCredentials;
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
Object.defineProperty(exports, "isLocalHost", {
|
|
997
|
+
enumerable: true,
|
|
998
|
+
get: function() {
|
|
999
|
+
return isLocalHost;
|
|
1000
|
+
}
|
|
1001
|
+
});
|
|
1002
|
+
Object.defineProperty(exports, "requiredServerAddress", {
|
|
1003
|
+
enumerable: true,
|
|
1004
|
+
get: function() {
|
|
1005
|
+
return requiredServerAddress;
|
|
1006
|
+
}
|
|
1007
|
+
});
|
|
1008
|
+
Object.defineProperty(exports, "runTime", {
|
|
1009
|
+
enumerable: true,
|
|
1010
|
+
get: function() {
|
|
1011
|
+
return runTime;
|
|
1012
|
+
}
|
|
1013
|
+
});
|
|
1014
|
+
Object.defineProperty(exports, "serverAddressHint", {
|
|
1015
|
+
enumerable: true,
|
|
1016
|
+
get: function() {
|
|
1017
|
+
return serverAddressHint;
|
|
1018
|
+
}
|
|
1019
|
+
});
|
|
1020
|
+
Object.defineProperty(exports, "stackBackups", {
|
|
1021
|
+
enumerable: true,
|
|
1022
|
+
get: function() {
|
|
1023
|
+
return stackBackups;
|
|
1024
|
+
}
|
|
1025
|
+
});
|
|
1026
|
+
Object.defineProperty(exports, "stackHosts", {
|
|
1027
|
+
enumerable: true,
|
|
1028
|
+
get: function() {
|
|
1029
|
+
return stackHosts;
|
|
1030
|
+
}
|
|
1031
|
+
});
|
|
1032
|
+
Object.defineProperty(exports, "stageBackups", {
|
|
1033
|
+
enumerable: true,
|
|
1034
|
+
get: function() {
|
|
1035
|
+
return stageBackups;
|
|
1036
|
+
}
|
|
1037
|
+
});
|
|
1038
|
+
Object.defineProperty(exports, "stageDns", {
|
|
1039
|
+
enumerable: true,
|
|
1040
|
+
get: function() {
|
|
1041
|
+
return stageDns;
|
|
1042
|
+
}
|
|
1043
|
+
});
|
|
1044
|
+
Object.defineProperty(exports, "stageDnsDomains", {
|
|
1045
|
+
enumerable: true,
|
|
1046
|
+
get: function() {
|
|
1047
|
+
return stageDnsDomains;
|
|
1048
|
+
}
|
|
1049
|
+
});
|
|
1050
|
+
Object.defineProperty(exports, "systemLookup", {
|
|
1051
|
+
enumerable: true,
|
|
1052
|
+
get: function() {
|
|
1053
|
+
return systemLookup;
|
|
1054
|
+
}
|
|
1055
|
+
});
|
|
1056
|
+
|
|
1057
|
+
//# sourceMappingURL=dns-DnE_02tB.cjs.map
|