@geekmidas/cli 10.0.0-alpha.124 → 10.0.0-alpha.125
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/{EndpointGenerator-B2MUa4M2.cjs → EndpointGenerator-BGxf9jWE.cjs} +8 -4
- package/dist/{EndpointGenerator-B2MUa4M2.cjs.map → EndpointGenerator-BGxf9jWE.cjs.map} +1 -1
- package/dist/{EndpointGenerator-Z_BUTocT.mjs → EndpointGenerator-errMeldx.mjs} +8 -4
- package/dist/{EndpointGenerator-Z_BUTocT.mjs.map → EndpointGenerator-errMeldx.mjs.map} +1 -1
- package/dist/{GoDaddyProvider-55x4o-4z.mjs → GoDaddyProvider-BN1dODm9.mjs} +2 -2
- package/dist/{GoDaddyProvider-55x4o-4z.mjs.map → GoDaddyProvider-BN1dODm9.mjs.map} +1 -1
- package/dist/{GoDaddyProvider-0z5RAN1k.cjs → GoDaddyProvider-BTdtqhI_.cjs} +2 -2
- package/dist/{GoDaddyProvider-0z5RAN1k.cjs.map → GoDaddyProvider-BTdtqhI_.cjs.map} +1 -1
- package/dist/{LegacyStateStore-DRpB-yC5.cjs → LegacyStateStore-C2S8TGwx.cjs} +3 -3
- package/dist/{LegacyStateStore-DRpB-yC5.cjs.map → LegacyStateStore-C2S8TGwx.cjs.map} +1 -1
- package/dist/{LegacyStateStore-C-vumKSN.mjs → LegacyStateStore-DgXmuG87.mjs} +3 -3
- package/dist/{LegacyStateStore-C-vumKSN.mjs.map → LegacyStateStore-DgXmuG87.mjs.map} +1 -1
- package/dist/{LocalStateStore-C3ummuDT.cjs → LocalStateStore-CaJw9H-3.cjs} +2 -2
- package/dist/{LocalStateStore-C3ummuDT.cjs.map → LocalStateStore-CaJw9H-3.cjs.map} +1 -1
- package/dist/{LocalStateStore-CtOjpV1w.mjs → LocalStateStore-ojY_BiMK.mjs} +2 -2
- package/dist/{LocalStateStore-CtOjpV1w.mjs.map → LocalStateStore-ojY_BiMK.mjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-MtnMQL_g.mjs → OpenApiTsGenerator-W_HRXH1X.mjs} +2 -2
- package/dist/{OpenApiTsGenerator-MtnMQL_g.mjs.map → OpenApiTsGenerator-W_HRXH1X.mjs.map} +1 -1
- package/dist/{OpenApiTsGenerator-t3_E98Fi.cjs → OpenApiTsGenerator-cQgtxWQo.cjs} +2 -2
- package/dist/{OpenApiTsGenerator-t3_E98Fi.cjs.map → OpenApiTsGenerator-cQgtxWQo.cjs.map} +1 -1
- package/dist/{S3StateStore-BC4Jm3Tw.mjs → S3StateStore-BSPkxhrx.mjs} +2 -2
- package/dist/{S3StateStore-BC4Jm3Tw.mjs.map → S3StateStore-BSPkxhrx.mjs.map} +1 -1
- package/dist/{S3StateStore-BeArp0aF.cjs → S3StateStore-BqugXIcb.cjs} +2 -2
- package/dist/{S3StateStore-BeArp0aF.cjs.map → S3StateStore-BqugXIcb.cjs.map} +1 -1
- package/dist/{SSMStateStore-DcstR6cW.cjs → SSMStateStore-B0OsuT_k.cjs} +2 -2
- package/dist/{SSMStateStore-DcstR6cW.cjs.map → SSMStateStore-B0OsuT_k.cjs.map} +1 -1
- package/dist/{SSMStateStore-Crl76aYZ.mjs → SSMStateStore-DM85OiH0.mjs} +2 -2
- package/dist/{SSMStateStore-Crl76aYZ.mjs.map → SSMStateStore-DM85OiH0.mjs.map} +1 -1
- package/dist/{SubscriberGenerator-C3QTae-R.cjs → SubscriberGenerator-BDUp7OeY.cjs} +3 -3
- package/dist/SubscriberGenerator-BDUp7OeY.cjs.map +1 -0
- package/dist/{SubscriberGenerator-COAVPgM-.mjs → SubscriberGenerator-ocQ2yj0K.mjs} +3 -3
- package/dist/{SubscriberGenerator-C3QTae-R.cjs.map → SubscriberGenerator-ocQ2yj0K.mjs.map} +1 -1
- package/dist/{add-CIjmgzkb.mjs → add-C7ZBXIzM.mjs} +6 -6
- package/dist/{add-CIjmgzkb.mjs.map → add-C7ZBXIzM.mjs.map} +1 -1
- package/dist/{add-BFjb6mMq.cjs → add-CrxvmSpq.cjs} +6 -6
- package/dist/{add-BFjb6mMq.cjs.map → add-CrxvmSpq.cjs.map} +1 -1
- package/dist/{bundler-CiUoIsku.cjs → bundler-DVUXW95p.cjs} +5 -5
- package/dist/{bundler-CiUoIsku.cjs.map → bundler-DVUXW95p.cjs.map} +1 -1
- package/dist/{bundler-D6KFNpQk.mjs → bundler-DpZ9uBGN.mjs} +5 -5
- package/dist/{bundler-D6KFNpQk.mjs.map → bundler-DpZ9uBGN.mjs.map} +1 -1
- package/dist/{cli-r4eUr98C.cjs → cli-BMRhE6cZ.cjs} +3 -3
- package/dist/{cli-r4eUr98C.cjs.map → cli-BMRhE6cZ.cjs.map} +1 -1
- package/dist/{cli-DNtSTtMc.mjs → cli-BWfYyy1k.mjs} +3 -3
- package/dist/{cli-DNtSTtMc.mjs.map → cli-BWfYyy1k.mjs.map} +1 -1
- package/dist/{compose-DJvvmF3X.mjs → compose-BFmdLig3.mjs} +232 -36
- package/dist/compose-BFmdLig3.mjs.map +1 -0
- package/dist/{compose-BfXXWJSp.cjs → compose-Bqcy0lqo.cjs} +232 -36
- package/dist/compose-Bqcy0lqo.cjs.map +1 -0
- package/dist/{config-IgK6YaGp.d.cts → config-CdytFgSR.d.cts} +3 -3
- package/dist/{config-IgK6YaGp.d.cts.map → config-CdytFgSR.d.cts.map} +1 -1
- package/dist/{config-kE-p5hxq.d.mts → config-_y1FGBWR.d.mts} +3 -3
- package/dist/{config-kE-p5hxq.d.mts.map → config-_y1FGBWR.d.mts.map} +1 -1
- package/dist/config.cjs +2 -2
- package/dist/config.d.cts +3 -3
- package/dist/config.d.mts +3 -3
- package/dist/config.mjs +2 -2
- package/dist/{credentialSchemas-AhIBuQxQ.cjs → credentialSchemas-DFxvGEwa.cjs} +2 -2
- package/dist/{credentialSchemas-AhIBuQxQ.cjs.map → credentialSchemas-DFxvGEwa.cjs.map} +1 -1
- package/dist/{credentialSchemas-DLKbxYBy.mjs → credentialSchemas-DaMpt-Dx.mjs} +2 -2
- package/dist/{credentialSchemas-DLKbxYBy.mjs.map → credentialSchemas-DaMpt-Dx.mjs.map} +1 -1
- package/dist/deploy/public.cjs +8 -8
- package/dist/deploy/public.d.cts +3 -3
- package/dist/deploy/public.d.cts.map +1 -1
- package/dist/deploy/public.d.mts +3 -3
- package/dist/deploy/public.d.mts.map +1 -1
- package/dist/deploy/public.mjs +8 -8
- package/dist/{deploy-CiRYwFmr.mjs → deploy-D8pUA9Y0.mjs} +797 -25
- package/dist/deploy-D8pUA9Y0.mjs.map +1 -0
- package/dist/{deploy-BbI1UMh3.cjs → deploy-DVrNjX1X.cjs} +796 -24
- package/dist/deploy-DVrNjX1X.cjs.map +1 -0
- package/dist/{discover-BvJOVsYG.mjs → discover-CzGM5P3p.mjs} +71 -1
- package/dist/discover-CzGM5P3p.mjs.map +1 -0
- package/dist/{discover-ojkcbxHw.cjs → discover-DL6h-J97.cjs} +71 -1
- package/dist/discover-DL6h-J97.cjs.map +1 -0
- package/dist/{dokploy-MM7c3IE0.mjs → dokploy-CzDGNB6A.mjs} +2 -2
- package/dist/{dokploy-MM7c3IE0.mjs.map → dokploy-CzDGNB6A.mjs.map} +1 -1
- package/dist/{dokploy-C0aQrCgs.cjs → dokploy-TtK6Zyl3.cjs} +2 -2
- package/dist/{dokploy-C0aQrCgs.cjs.map → dokploy-TtK6Zyl3.cjs.map} +1 -1
- package/dist/{drivers-Chb5Akov.mjs → drivers-C-VqMtld.mjs} +2 -2
- package/dist/{drivers-Chb5Akov.mjs.map → drivers-C-VqMtld.mjs.map} +1 -1
- package/dist/{drivers-CQ32PB5w.cjs → drivers-L29WvE95.cjs} +2 -2
- package/dist/{drivers-CQ32PB5w.cjs.map → drivers-L29WvE95.cjs.map} +1 -1
- package/dist/{eas-CJoys9yh.cjs → eas-D3clzkni.cjs} +2 -2
- package/dist/{eas-CJoys9yh.cjs.map → eas-D3clzkni.cjs.map} +1 -1
- package/dist/{eas-1Ee6kql0.mjs → eas-DaobzDks.mjs} +2 -2
- package/dist/{eas-1Ee6kql0.mjs.map → eas-DaobzDks.mjs.map} +1 -1
- package/dist/{emit-qvHxMV9b.cjs → emit-CYloptRS.cjs} +18 -1
- package/dist/emit-CYloptRS.cjs.map +1 -0
- package/dist/{emit-CNpuV_6Q.mjs → emit-CiJGdy_f.mjs} +18 -1
- package/dist/emit-CiJGdy_f.mjs.map +1 -0
- package/dist/{encryption-Bo9VlRnl.cjs → encryption-DBkIFiR2.cjs} +3 -3
- package/dist/{encryption-Bo9VlRnl.cjs.map → encryption-DBkIFiR2.cjs.map} +1 -1
- package/dist/{encryption-B8Bk1Mvu.mjs → encryption-DV31gEY8.mjs} +3 -3
- package/dist/{encryption-B8Bk1Mvu.mjs.map → encryption-DV31gEY8.mjs.map} +1 -1
- package/dist/{engine-BuinpJfS.mjs → engine-CFOR9h6X.mjs} +138 -13
- package/dist/engine-CFOR9h6X.mjs.map +1 -0
- package/dist/{engine-esxTxcIx.cjs → engine-CuZUGbEh.cjs} +138 -13
- package/dist/engine-CuZUGbEh.cjs.map +1 -0
- package/dist/{github-BEA4K3_X.cjs → github-CsvtWSBW.cjs} +41 -6
- package/dist/github-CsvtWSBW.cjs.map +1 -0
- package/dist/{github-BUlKJfOV.mjs → github-Nep8pI4_.mjs} +41 -6
- package/dist/github-Nep8pI4_.mjs.map +1 -0
- package/dist/{googlePlay-CiqAS2ma.cjs → googlePlay-C-UfBXJU.cjs} +3 -3
- package/dist/{googlePlay-CiqAS2ma.cjs.map → googlePlay-C-UfBXJU.cjs.map} +1 -1
- package/dist/{googlePlay-CK05QiTq.mjs → googlePlay-lF7Atm_N.mjs} +3 -3
- package/dist/{googlePlay-CK05QiTq.mjs.map → googlePlay-lF7Atm_N.mjs.map} +1 -1
- package/dist/{index-DhUf0B0m.d.mts → index-DrkV7N5w.d.mts} +2 -2
- package/dist/{index-4CO0HD27.d.cts.map → index-DrkV7N5w.d.mts.map} +1 -1
- package/dist/{index-4CO0HD27.d.cts → index-yaXZ9m-J.d.cts} +2 -2
- package/dist/{index-DhUf0B0m.d.mts.map → index-yaXZ9m-J.d.cts.map} +1 -1
- package/dist/index.cjs +147 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +147 -65
- package/dist/index.mjs.map +1 -1
- package/dist/{migrate-DeQJBexh.cjs → migrate-Dp3GhFnf.cjs} +4 -4
- package/dist/{migrate-DeQJBexh.cjs.map → migrate-Dp3GhFnf.cjs.map} +1 -1
- package/dist/{migrate-CnbvrVag.mjs → migrate-NfbPVhi9.mjs} +4 -4
- package/dist/{migrate-CnbvrVag.mjs.map → migrate-NfbPVhi9.mjs.map} +1 -1
- package/dist/{migrate-BzCHT9qs.mjs → migrate-_sBX0pve.mjs} +3 -3
- package/dist/{migrate-BzCHT9qs.mjs.map → migrate-_sBX0pve.mjs.map} +1 -1
- package/dist/{migrate-C-hmbjpG.cjs → migrate-hlNKNWmg.cjs} +3 -3
- package/dist/{migrate-C-hmbjpG.cjs.map → migrate-hlNKNWmg.cjs.map} +1 -1
- package/dist/openapi.cjs +3 -3
- package/dist/openapi.d.cts +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +3 -3
- package/dist/{publish-BISBrrqR.mjs → publish-BuUVaZVy.mjs} +2 -2
- package/dist/{publish-BISBrrqR.mjs.map → publish-BuUVaZVy.mjs.map} +1 -1
- package/dist/{publish-DtSIB-hZ.cjs → publish-ZOo4xEuZ.cjs} +2 -2
- package/dist/{publish-DtSIB-hZ.cjs.map → publish-ZOo4xEuZ.cjs.map} +1 -1
- package/dist/{ready-DqsyadOs.mjs → ready-Ca883OvY.mjs} +6 -6
- package/dist/{ready-DqsyadOs.mjs.map → ready-Ca883OvY.mjs.map} +1 -1
- package/dist/{ready-IVGu-q18.cjs → ready-qnqp6JAY.cjs} +6 -6
- package/dist/{ready-IVGu-q18.cjs.map → ready-qnqp6JAY.cjs.map} +1 -1
- package/dist/reconcile/public.cjs +2 -2
- package/dist/reconcile/public.d.cts +1 -1
- package/dist/reconcile/public.d.cts.map +1 -1
- package/dist/reconcile/public.d.mts +1 -1
- package/dist/reconcile/public.d.mts.map +1 -1
- package/dist/reconcile/public.mjs +2 -2
- package/dist/{resolve-BT7fkPN5.mjs → resolve-BCU9pYDx.mjs} +3 -3
- package/dist/{resolve-BT7fkPN5.mjs.map → resolve-BCU9pYDx.mjs.map} +1 -1
- package/dist/{resolve-C7GKQcE4.cjs → resolve-C9QqOl5t.cjs} +3 -3
- package/dist/{resolve-C7GKQcE4.cjs.map → resolve-C9QqOl5t.cjs.map} +1 -1
- package/dist/{rollback-BEiB-Pje.cjs → rollback-CTWdDBgB.cjs} +5 -5
- package/dist/{rollback-BEiB-Pje.cjs.map → rollback-CTWdDBgB.cjs.map} +1 -1
- package/dist/{rollback-DbP0KuDw.mjs → rollback-CWyyTQ0B.mjs} +5 -5
- package/dist/{rollback-DbP0KuDw.mjs.map → rollback-CWyyTQ0B.mjs.map} +1 -1
- package/dist/{runtime-BFJQNqwd.d.mts → runtime-DCkfI2E7.d.mts} +2 -2
- package/dist/{runtime-BFJQNqwd.d.mts.map → runtime-DCkfI2E7.d.mts.map} +1 -1
- package/dist/{runtime-BJO-FFef.d.cts → runtime-Ie4znWIB.d.cts} +2 -2
- package/dist/{runtime-BJO-FFef.d.cts.map → runtime-Ie4znWIB.d.cts.map} +1 -1
- package/dist/{s3-B4FTQQkV.mjs → s3-BZzSnker.mjs} +2 -2
- package/dist/{s3-B4FTQQkV.mjs.map → s3-BZzSnker.mjs.map} +1 -1
- package/dist/{s3-DwKLXwUT.cjs → s3-pqgmCTOq.cjs} +2 -2
- package/dist/{s3-DwKLXwUT.cjs.map → s3-pqgmCTOq.cjs.map} +1 -1
- package/dist/sandbox/credentials-worker.cjs +2 -2
- package/dist/sandbox/credentials-worker.mjs +2 -2
- package/dist/sandbox/discover-worker.cjs +1 -1
- package/dist/sandbox/discover-worker.mjs +1 -1
- package/dist/sandbox/migrate-worker.cjs +1 -1
- package/dist/sandbox/migrate-worker.mjs +1 -1
- package/dist/{seeds-rG-Evfz9.mjs → seeds-CK7w79VC.mjs} +516 -39
- package/dist/seeds-CK7w79VC.mjs.map +1 -0
- package/dist/{seeds-DOfxmDRO.cjs → seeds-CPEcmoPn.cjs} +599 -38
- package/dist/seeds-CPEcmoPn.cjs.map +1 -0
- package/dist/{serverSetup-xGw1fxuZ.mjs → serverSetup-BAPNuNEX.mjs} +6 -6
- package/dist/{serverSetup-xGw1fxuZ.mjs.map → serverSetup-BAPNuNEX.mjs.map} +1 -1
- package/dist/{serverSetup-CWWVcU6Y.cjs → serverSetup-C6t6Vtt7.cjs} +6 -6
- package/dist/{serverSetup-CWWVcU6Y.cjs.map → serverSetup-C6t6Vtt7.cjs.map} +1 -1
- package/dist/{sniffer-C7O9Pna_.cjs → sniffer-DdX_WN_-.cjs} +8 -8
- package/dist/{sniffer-C7O9Pna_.cjs.map → sniffer-DdX_WN_-.cjs.map} +1 -1
- package/dist/{sniffer-DVPgfyHr.mjs → sniffer-g9v7cqDN.mjs} +8 -8
- package/dist/{sniffer-DVPgfyHr.mjs.map → sniffer-g9v7cqDN.mjs.map} +1 -1
- package/dist/{snsSubscriptions-CSkteK2J.cjs → snsSubscriptions-5Cqww8S7.cjs} +15 -3
- package/dist/snsSubscriptions-5Cqww8S7.cjs.map +1 -0
- package/dist/{snsSubscriptions-CvBIGGp0.mjs → snsSubscriptions-BmYeeTH8.mjs} +4 -4
- package/dist/snsSubscriptions-BmYeeTH8.mjs.map +1 -0
- package/dist/{stage-2EKpzPQ1.mjs → stage-C-I2dVw1.mjs} +6 -6
- package/dist/{stage-2EKpzPQ1.mjs.map → stage-C-I2dVw1.mjs.map} +1 -1
- package/dist/{stage-fRfFtLwj.cjs → stage-CUE5pMmw.cjs} +6 -6
- package/dist/{stage-fRfFtLwj.cjs.map → stage-CUE5pMmw.cjs.map} +1 -1
- package/dist/{stages-B7mCWI_R.cjs → stages-3yUZaZeA.cjs} +2 -2
- package/dist/{stages-B7mCWI_R.cjs.map → stages-3yUZaZeA.cjs.map} +1 -1
- package/dist/{stages-DSltTW2d.mjs → stages-DTTv0NZW.mjs} +2 -2
- package/dist/{stages-DSltTW2d.mjs.map → stages-DTTv0NZW.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/{terminal-D1aeTkgv.cjs → terminal-PKhPAajN.cjs} +2 -2
- package/dist/{terminal-D1aeTkgv.cjs.map → terminal-PKhPAajN.cjs.map} +1 -1
- package/dist/{terminal-BAVrDhhN.mjs → terminal-bqY33lpN.mjs} +2 -2
- package/dist/{terminal-BAVrDhhN.mjs.map → terminal-bqY33lpN.mjs.map} +1 -1
- package/dist/{types-CFU2uvrz.d.cts → types-Bmjs4jZV.d.mts} +8 -2
- package/dist/{types-D3BeGucy.d.mts.map → types-Bmjs4jZV.d.mts.map} +1 -1
- package/dist/{types-D3BeGucy.d.mts → types-D0IsQxES.d.cts} +9 -1
- package/dist/{types-CFU2uvrz.d.cts.map → types-D0IsQxES.d.cts.map} +1 -1
- package/dist/vitest.cjs +1 -1
- package/dist/vitest.mjs +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-DUnnVjqC.mjs → workspace-DBHF9Gba.mjs} +5 -3
- package/dist/workspace-DBHF9Gba.mjs.map +1 -0
- package/dist/{workspace-C82GLPnf.cjs → workspace-Du4Afb03.cjs} +5 -3
- package/dist/workspace-Du4Afb03.cjs.map +1 -0
- package/package.json +17 -15
- package/dist/SubscriberGenerator-COAVPgM-.mjs.map +0 -1
- package/dist/compose-BfXXWJSp.cjs.map +0 -1
- package/dist/compose-DJvvmF3X.mjs.map +0 -1
- package/dist/deploy-BbI1UMh3.cjs.map +0 -1
- package/dist/deploy-CiRYwFmr.mjs.map +0 -1
- package/dist/discover-BvJOVsYG.mjs.map +0 -1
- package/dist/discover-ojkcbxHw.cjs.map +0 -1
- package/dist/emit-CNpuV_6Q.mjs.map +0 -1
- package/dist/emit-qvHxMV9b.cjs.map +0 -1
- package/dist/engine-BuinpJfS.mjs.map +0 -1
- package/dist/engine-esxTxcIx.cjs.map +0 -1
- package/dist/github-BEA4K3_X.cjs.map +0 -1
- package/dist/github-BUlKJfOV.mjs.map +0 -1
- package/dist/seeds-DOfxmDRO.cjs.map +0 -1
- package/dist/seeds-rG-Evfz9.mjs.map +0 -1
- package/dist/snsSubscriptions-CSkteK2J.cjs.map +0 -1
- package/dist/snsSubscriptions-CvBIGGp0.mjs.map +0 -1
- package/dist/workspace-C82GLPnf.cjs.map +0 -1
- package/dist/workspace-DUnnVjqC.mjs.map +0 -1
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { t as GkmError } from "./errors-D8Gwp5kU.mjs";
|
|
2
2
|
import { r as storedCredentials, x as gkmHome } from "./credentials-DpfnUvIR.mjs";
|
|
3
|
-
import { $ as IamUserNotOwned, G as checkS3Config, J as BucketNameUnavailable, K as AccessKeyLimit, L as resolveStageTelemetry, N as assertDeployedStage, Q as BucketRegionUnknown, U as stageProvider, W as S3_PROVISIONING, X as BucketProbeFailed, Y as BucketOwnedByAnotherProject, Z as BucketRegionMismatch, et as ProvisionedBucketUnreachable, ht as isReservedStageKey, nt as RotationInProgress, q as BucketKeySetElsewhere, rt as S3RegionRequired, tt as RecordedBucketUnreachable, u as getAppBuildOrder } from "./workspace-
|
|
4
|
-
import {
|
|
3
|
+
import { $ as IamUserNotOwned, G as checkS3Config, J as BucketNameUnavailable, K as AccessKeyLimit, L as resolveStageTelemetry, N as assertDeployedStage, Q as BucketRegionUnknown, U as stageProvider, W as S3_PROVISIONING, X as BucketProbeFailed, Y as BucketOwnedByAnotherProject, Z as BucketRegionMismatch, et as ProvisionedBucketUnreachable, ht as isReservedStageKey, nt as RotationInProgress, q as BucketKeySetElsewhere, rt as S3RegionRequired, tt as RecordedBucketUnreachable, u as getAppBuildOrder } from "./workspace-DBHF9Gba.mjs";
|
|
4
|
+
import { D as BACKUPS_SERVICE, I as backupsPrefix, O as BACKUPS_URL_KEY, P as backupExpiries, R as stageBackups, S as assertSitePasswordTarget, _ as stageDnsDomains, a as subscriptionKey, b as awsEndpoint, d as recordTtl, l as dnsCredentials, m as settleStackDns, o as subscriptionResource, x as awsProvisioningCredentials, y as awsClientConfig, z as deploysWithCompose } from "./snsSubscriptions-BmYeeTH8.mjs";
|
|
5
5
|
import { o as builtinTarget } from "./define-6poNqm5O.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { $ as ORIGIN_VERIFY_HEADER, En as mergeLifecycle, N as stageProviderNotes, P as PROVIDER_KINDS, Qt as initStageSecrets, Yt as secretsStoreFor, _t as appEnvKeys, br as resolveHost, ct as ExternalServicesNotConfigured, d as DeployJournal, dt as devServicesUsed, er as usesTelemetry, f as extractSubdomain, ft as externalServices, ht as stageServiceDeclarations, j as assertStageProvidersEnabled, m as normalizeDnsConfig, nt as originSecretKey, p as findRootDomain, pt as reportDevServices, rt as processorOriginHost, st as DevServicesNeedServerTarget, tt as newOriginSecret, v as createComposeState, vr as NoDomainForStage, wt as constructGlobs, y as createEmptyState, yr as isMainFrontendApp } from "./seeds-CK7w79VC.mjs";
|
|
7
7
|
import { n as derivedApps, t as appKey } from "./derive-B4vDpzgP.mjs";
|
|
8
|
-
import { s as subscriberRoute } from "./SubscriberGenerator-
|
|
8
|
+
import { s as subscriberRoute } from "./SubscriberGenerator-ocQ2yj0K.mjs";
|
|
9
9
|
import { n as deployIdentity, t as applicationName } from "./identity-CBXr4wNi.mjs";
|
|
10
|
-
import { i as discover, m as eventsEdge, p as withOutput } from "./discover-
|
|
10
|
+
import { i as discover, m as eventsEdge, p as withOutput } from "./discover-CzGM5P3p.mjs";
|
|
11
11
|
import { c as withSandbox } from "./sandbox-CbVB7uOD.mjs";
|
|
12
12
|
import { findWorkspaceRoot, loadWorkspaceConfig } from "./config.mjs";
|
|
13
|
-
import { a as parseTargetOptions, o as resolveTarget } from "./resolve-
|
|
13
|
+
import { a as parseTargetOptions, o as resolveTarget } from "./resolve-BCU9pYDx.mjs";
|
|
14
14
|
import { t as LocalSandbox } from "./local-B7Zv1NoA.mjs";
|
|
15
|
-
import { E as eventError, S as createStateStore, v as assertStateOutlivesRun, y as assertStateRoom } from "./sniffer-
|
|
15
|
+
import { E as eventError, S as createStateStore, v as assertStateOutlivesRun, y as assertStateRoom } from "./sniffer-g9v7cqDN.mjs";
|
|
16
16
|
import { a as findProjectBucket, i as ensureProjectBucket, o as projectBucketName, r as callerAccountId, s as reconcileProjectBucketLifecycle } from "./projectBucket-C_DN4WzF.mjs";
|
|
17
|
-
import { n as assertStageCredentials } from "./credentialSchemas-
|
|
17
|
+
import { n as assertStageCredentials } from "./credentialSchemas-DaMpt-Dx.mjs";
|
|
18
18
|
import { resolve } from "node:path";
|
|
19
19
|
import { randomInt } from "node:crypto";
|
|
20
|
-
import { dependentsOf, kebabCase, provideKey, providedKeyFor } from "@geekmidas/manifest";
|
|
20
|
+
import { bucketExpiry, dependentsOf, kebabCase, provideKey, providedKeyFor } from "@geekmidas/manifest";
|
|
21
21
|
import * as s3Url from "@geekmidas/storage/s3-url";
|
|
22
22
|
//#region src/target/secrets.ts
|
|
23
23
|
/**
|
|
@@ -219,10 +219,13 @@ function openResource(bucket, pattern) {
|
|
|
219
219
|
}
|
|
220
220
|
/**
|
|
221
221
|
* The bucket's own policy: every request refused unless it came over TLS,
|
|
222
|
-
* and — only when a file server opens paths on it —
|
|
223
|
-
*
|
|
222
|
+
* and — only when a file server opens paths on it — `s3:GetObject` on
|
|
223
|
+
* exactly those prefixes: to anyone, or, when a CDN serves them, to
|
|
224
|
+
* CloudFront alone, for exactly the stage's distributions. Behind a CDN the
|
|
225
|
+
* bucket is never public: a key outside the open prefixes is refused to
|
|
226
|
+
* CloudFront too, so the edge answers it 403.
|
|
224
227
|
*/
|
|
225
|
-
function bucketPolicy(bucket, open = []) {
|
|
228
|
+
function bucketPolicy(bucket, open = [], options = {}) {
|
|
226
229
|
const statements = [{
|
|
227
230
|
Sid: "GkmDenyInsecureTransport",
|
|
228
231
|
Effect: "Deny",
|
|
@@ -231,12 +234,21 @@ function bucketPolicy(bucket, open = []) {
|
|
|
231
234
|
Resource: [bucketArn(bucket), `${bucketArn(bucket)}/*`],
|
|
232
235
|
Condition: { Bool: { "aws:SecureTransport": "false" } }
|
|
233
236
|
}];
|
|
234
|
-
|
|
237
|
+
const resources = [...new Set(open.map((p) => openResource(bucket, p)))].sort();
|
|
238
|
+
if (open.length > 0 && options.cdn) statements.push({
|
|
239
|
+
Sid: "GkmCdnRead",
|
|
240
|
+
Effect: "Allow",
|
|
241
|
+
Principal: { Service: "cloudfront.amazonaws.com" },
|
|
242
|
+
Action: "s3:GetObject",
|
|
243
|
+
Resource: resources,
|
|
244
|
+
Condition: { StringEquals: { "AWS:SourceArn": [...new Set(options.cdn.distributionArns)].sort() } }
|
|
245
|
+
});
|
|
246
|
+
else if (open.length > 0) statements.push({
|
|
235
247
|
Sid: "GkmOpenPaths",
|
|
236
248
|
Effect: "Allow",
|
|
237
249
|
Principal: "*",
|
|
238
250
|
Action: "s3:GetObject",
|
|
239
|
-
Resource:
|
|
251
|
+
Resource: resources
|
|
240
252
|
});
|
|
241
253
|
return {
|
|
242
254
|
Version: "2012-10-17",
|
|
@@ -285,7 +297,7 @@ function canonical(value, key) {
|
|
|
285
297
|
}
|
|
286
298
|
if (value && typeof value === "object") return Object.fromEntries(Object.keys(value).sort().map((k) => {
|
|
287
299
|
const v = value[k];
|
|
288
|
-
return [k, canonical(k === "Action" || k === "Resource" || k === "AWS" ? Array.isArray(v) ? v : [v] : v, k)];
|
|
300
|
+
return [k, canonical(k === "Action" || k === "Resource" || k === "AWS" || k === "AWS:SourceArn" ? Array.isArray(v) ? v : [v] : v, k)];
|
|
289
301
|
}));
|
|
290
302
|
return value;
|
|
291
303
|
}
|
|
@@ -331,7 +343,7 @@ function identityTags(ctx, id) {
|
|
|
331
343
|
];
|
|
332
344
|
}
|
|
333
345
|
/** A read whose "there is none" answer is one of `missing`. */
|
|
334
|
-
async function readOr$
|
|
346
|
+
async function readOr$3(read, missing) {
|
|
335
347
|
try {
|
|
336
348
|
return await read();
|
|
337
349
|
} catch (error) {
|
|
@@ -361,7 +373,7 @@ async function ensureIamUser(ctx, clients, input) {
|
|
|
361
373
|
key: `iam-user:${id}`,
|
|
362
374
|
type: "iam-user"
|
|
363
375
|
};
|
|
364
|
-
const found = await readOr$
|
|
376
|
+
const found = await readOr$3(() => iam.send(new IAM.GetUserCommand({ UserName: user })), ["NoSuchEntity", "NoSuchEntityException"]);
|
|
365
377
|
if (found?.User) {
|
|
366
378
|
const tags = await iam.send(new IAM.ListUserTagsCommand({ UserName: user }));
|
|
367
379
|
const tag = (key) => tags.Tags?.find((t) => t.Key === key)?.Value;
|
|
@@ -381,7 +393,7 @@ async function ensureIamUser(ctx, clients, input) {
|
|
|
381
393
|
await ctx.state.ready(entry, user);
|
|
382
394
|
});
|
|
383
395
|
const exists = Boolean(found?.User) || !ctx.dryRun;
|
|
384
|
-
const current = exists ? await readOr$
|
|
396
|
+
const current = exists ? await readOr$3(() => iam.send(new IAM.GetUserPolicyCommand({
|
|
385
397
|
UserName: user,
|
|
386
398
|
PolicyName: input.policyName
|
|
387
399
|
})), ["NoSuchEntity", "NoSuchEntityException"]) : void 0;
|
|
@@ -795,6 +807,738 @@ async function verifyStageBackups(input) {
|
|
|
795
807
|
}
|
|
796
808
|
}
|
|
797
809
|
//#endregion
|
|
810
|
+
//#region src/providers/dnsWriter.ts
|
|
811
|
+
/**
|
|
812
|
+
* The DNS records a provider asks for — a certificate's validation record, a
|
|
813
|
+
* CDN's name — written through the stage's own DNS providers.
|
|
814
|
+
*
|
|
815
|
+
* The same machinery the deploy's DNS step uses for the stack's hosts
|
|
816
|
+
* (`settleStackDns`), handed a plan of CNAMEs instead of the server's
|
|
817
|
+
* addresses: a record holding its value is left alone, one with another value
|
|
818
|
+
* is replaced and printed, and what is written is read back. A host under no
|
|
819
|
+
* domain in `dns`, or under a `manual` one, is printed for the person to
|
|
820
|
+
* create and reported back, so the provider can say what it is waiting on.
|
|
821
|
+
*/
|
|
822
|
+
async function writeProviderDns(records, options) {
|
|
823
|
+
const { log } = options;
|
|
824
|
+
const dns = options.workspace.dns ? normalizeDnsConfig(options.workspace.dns) : {};
|
|
825
|
+
const manual = [];
|
|
826
|
+
const domains = /* @__PURE__ */ new Map();
|
|
827
|
+
for (const record of records) {
|
|
828
|
+
const host = record.host.toLowerCase().replace(/\.$/, "");
|
|
829
|
+
const value = record.value.replace(/\.$/, "");
|
|
830
|
+
const domain = findRootDomain(host, dns);
|
|
831
|
+
if (!domain) {
|
|
832
|
+
log(` create at its DNS host: ${host} CNAME ${value}`);
|
|
833
|
+
manual.push(host);
|
|
834
|
+
continue;
|
|
835
|
+
}
|
|
836
|
+
const config = dns[domain];
|
|
837
|
+
const plan = domains.get(domain) ?? {
|
|
838
|
+
domain,
|
|
839
|
+
provider: typeof config.provider === "string" ? config.provider : "custom",
|
|
840
|
+
config,
|
|
841
|
+
records: []
|
|
842
|
+
};
|
|
843
|
+
const planned = {
|
|
844
|
+
host,
|
|
845
|
+
domain,
|
|
846
|
+
name: extractSubdomain(host, domain),
|
|
847
|
+
type: record.type,
|
|
848
|
+
value,
|
|
849
|
+
ttl: recordTtl(config)
|
|
850
|
+
};
|
|
851
|
+
plan.records.push(planned);
|
|
852
|
+
domains.set(domain, plan);
|
|
853
|
+
}
|
|
854
|
+
if (domains.size === 0) return { manual };
|
|
855
|
+
const plans = [...domains.values()];
|
|
856
|
+
const tokens = await dnsCredentials(options.stage, plans.map(({ domain, config }) => ({
|
|
857
|
+
domain,
|
|
858
|
+
config
|
|
859
|
+
})), options.credentials);
|
|
860
|
+
const applied = await settleStackDns({
|
|
861
|
+
stage: options.stage,
|
|
862
|
+
server: { ipv4: "" },
|
|
863
|
+
domains: plans,
|
|
864
|
+
uncovered: []
|
|
865
|
+
}, {
|
|
866
|
+
...options.dryRun ? { dryRun: true } : { confirm: true },
|
|
867
|
+
log,
|
|
868
|
+
credentials: {
|
|
869
|
+
...tokens.godaddy ? { credential: tokens.godaddy } : {},
|
|
870
|
+
log
|
|
871
|
+
},
|
|
872
|
+
...tokens.hostinger ? { hostinger: tokens.hostinger } : {},
|
|
873
|
+
...options.providerFor ? { providerFor: options.providerFor } : {}
|
|
874
|
+
});
|
|
875
|
+
for (const change of applied.changes) {
|
|
876
|
+
if (change.action !== "manual") continue;
|
|
877
|
+
const host = plans.find((p) => p.domain === change.domain)?.records.find((r) => r.name === change.name)?.host;
|
|
878
|
+
if (host) manual.push(host);
|
|
879
|
+
}
|
|
880
|
+
return { manual: [...new Set(manual)].sort() };
|
|
881
|
+
}
|
|
882
|
+
//#endregion
|
|
883
|
+
//#region src/providers/s3/cdn.ts
|
|
884
|
+
/** AWS's managed `CachingOptimized` cache policy: path-keyed, gzip and brotli. */
|
|
885
|
+
const CACHING_OPTIMIZED = "658327ea-f89d-4fab-a63d-7e88639e58f6";
|
|
886
|
+
/** What an immutable path is served with. */
|
|
887
|
+
const IMMUTABLE_CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
888
|
+
/** The only region CloudFront reads a certificate from. */
|
|
889
|
+
const CDN_REGION = "us-east-1";
|
|
890
|
+
/** The distribution's bucket origin. */
|
|
891
|
+
const ORIGIN_ID = "bucket";
|
|
892
|
+
/** Its processors' origin: the stack's processor unit, at a host of its own. */
|
|
893
|
+
const PROCESSORS_ORIGIN_ID = "processors";
|
|
894
|
+
/**
|
|
895
|
+
* AWS's managed `CachingOptimizedForUncompressedObjects`: keyed on the path
|
|
896
|
+
* alone, and nothing re-compressed — a derivative is an image, already
|
|
897
|
+
* compressed, and its width is in the path, so nothing else may fragment it.
|
|
898
|
+
*/
|
|
899
|
+
const CACHING_OPTIMIZED_UNCOMPRESSED = "b2884449-e4de-46a7-ac36-70bc7f1ddd6d";
|
|
900
|
+
/** A CDN is a name on the stage's domain, and the stage has none. */
|
|
901
|
+
var FileServerCdnNeedsDomain = class extends GkmError {
|
|
902
|
+
server;
|
|
903
|
+
stage;
|
|
904
|
+
constructor(server, stage) {
|
|
905
|
+
super(`'${server}' is served through a CDN on '${stage}', which answers on <fileserver>.<domain> — and '${stage}' has no domain. Set domains.${stage} in gkm.config.ts, or set deploy.objects.${stage}.cdn to false to serve it from the bucket's own endpoint.`);
|
|
906
|
+
this.server = server;
|
|
907
|
+
this.stage = stage;
|
|
908
|
+
this.name = "FileServerCdnNeedsDomain";
|
|
909
|
+
}
|
|
910
|
+
};
|
|
911
|
+
/**
|
|
912
|
+
* The certificate is waiting on its validation record: one the person has to
|
|
913
|
+
* create, or one that has not propagated yet. Everything up to it is
|
|
914
|
+
* recorded, so the next deploy carries on from here.
|
|
915
|
+
*/
|
|
916
|
+
var CdnCertificatePending = class extends GkmError {
|
|
917
|
+
server;
|
|
918
|
+
host;
|
|
919
|
+
record;
|
|
920
|
+
reason;
|
|
921
|
+
constructor(server, host, record, reason) {
|
|
922
|
+
super((reason === "manual" ? `The certificate for ${host} ('${server}') is validated by a DNS record no DNS provider in gkm.config.ts can write. Create it at the domain's DNS host:
|
|
923
|
+
` : `The certificate for ${host} ('${server}') is not issued yet: ACM has not seen its validation record. It is written; DNS can take a while to answer with it. Check it resolves:
|
|
924
|
+
`) + ` ${record.name} CNAME ${record.value}\nthen deploy again — the certificate and everything before it are recorded, and the deploy carries on from there.`);
|
|
925
|
+
this.server = server;
|
|
926
|
+
this.host = host;
|
|
927
|
+
this.record = record;
|
|
928
|
+
this.reason = reason;
|
|
929
|
+
this.name = "CdnCertificatePending";
|
|
930
|
+
}
|
|
931
|
+
};
|
|
932
|
+
/** ACM gave up on the certificate. */
|
|
933
|
+
var CdnCertificateFailed = class extends GkmError {
|
|
934
|
+
server;
|
|
935
|
+
host;
|
|
936
|
+
status;
|
|
937
|
+
reason;
|
|
938
|
+
constructor(server, host, status, reason) {
|
|
939
|
+
super(`ACM did not issue the certificate for ${host} ('${server}'): ${status}` + (reason ? ` (${reason})` : "") + ". A CAA record on the domain that does not allow amazon.com, or a validation record removed before it was checked, are the usual causes. Fix it and deploy again: a new certificate is requested.");
|
|
940
|
+
this.server = server;
|
|
941
|
+
this.host = host;
|
|
942
|
+
this.status = status;
|
|
943
|
+
this.reason = reason;
|
|
944
|
+
this.name = "CdnCertificateFailed";
|
|
945
|
+
}
|
|
946
|
+
};
|
|
947
|
+
/** Where a file server answers: `<fileserver>.<domain>`. */
|
|
948
|
+
function cdnHost(serverId, domain) {
|
|
949
|
+
return `${appKey(serverId)}.${domain}`.toLowerCase();
|
|
950
|
+
}
|
|
951
|
+
/**
|
|
952
|
+
* The name the stage's CDN resources go by, within CloudFront's limits:
|
|
953
|
+
* letters, digits, `-` and `_`, and at most 64 characters (an origin access
|
|
954
|
+
* control's). Kept whole where it fits; otherwise shortened with a hash of
|
|
955
|
+
* the whole, so two long names never meet.
|
|
956
|
+
*/
|
|
957
|
+
function cdnName(scope, stage, serverId) {
|
|
958
|
+
const whole = `gkm-${scope}-${stage}-${appKey(serverId)}`.replace(/[^A-Za-z0-9_-]/g, "-");
|
|
959
|
+
if (whole.length <= 56) return whole;
|
|
960
|
+
return `${whole.slice(0, 47)}-${hash(whole)}`;
|
|
961
|
+
}
|
|
962
|
+
function hash(value) {
|
|
963
|
+
let h = 2166136261;
|
|
964
|
+
for (let i = 0; i < value.length; i++) {
|
|
965
|
+
h ^= value.charCodeAt(i);
|
|
966
|
+
h = Math.imul(h, 16777619);
|
|
967
|
+
}
|
|
968
|
+
return (h >>> 0).toString(36).padStart(8, "0").slice(0, 8);
|
|
969
|
+
}
|
|
970
|
+
/**
|
|
971
|
+
* An open or immutable glob as a CloudFront path pattern. CloudFront's `*`
|
|
972
|
+
* crosses `/`, so a single star is as wide as a double one — the bucket
|
|
973
|
+
* policy reads it the same way, and the construct's own check is the
|
|
974
|
+
* stricter of the three.
|
|
975
|
+
*/
|
|
976
|
+
function pathPattern(glob) {
|
|
977
|
+
return glob.replace(/^\/+/, "").replace(/\*\*/g, "*");
|
|
978
|
+
}
|
|
979
|
+
/** A CloudFront list: its `Quantity` and, when it has some, its `Items`. */
|
|
980
|
+
function list(items) {
|
|
981
|
+
return items.length > 0 ? {
|
|
982
|
+
Quantity: items.length,
|
|
983
|
+
Items: [...items]
|
|
984
|
+
} : { Quantity: 0 };
|
|
985
|
+
}
|
|
986
|
+
/** One of the stage's response headers policies. */
|
|
987
|
+
function headersPolicyConfig(input) {
|
|
988
|
+
return {
|
|
989
|
+
Name: input.name,
|
|
990
|
+
Comment: input.comment,
|
|
991
|
+
...input.origins.length > 0 ? { CorsConfig: {
|
|
992
|
+
AccessControlAllowOrigins: list([...input.origins].sort()),
|
|
993
|
+
AccessControlAllowHeaders: {
|
|
994
|
+
Quantity: 1,
|
|
995
|
+
Items: ["*"]
|
|
996
|
+
},
|
|
997
|
+
AccessControlAllowMethods: {
|
|
998
|
+
Quantity: 3,
|
|
999
|
+
Items: [
|
|
1000
|
+
"GET",
|
|
1001
|
+
"HEAD",
|
|
1002
|
+
"OPTIONS"
|
|
1003
|
+
]
|
|
1004
|
+
},
|
|
1005
|
+
AccessControlAllowCredentials: false,
|
|
1006
|
+
AccessControlMaxAgeSec: 86400,
|
|
1007
|
+
OriginOverride: false
|
|
1008
|
+
} } : {},
|
|
1009
|
+
...input.immutable ? { CustomHeadersConfig: {
|
|
1010
|
+
Quantity: 1,
|
|
1011
|
+
Items: [{
|
|
1012
|
+
Header: "Cache-Control",
|
|
1013
|
+
Value: IMMUTABLE_CACHE_CONTROL,
|
|
1014
|
+
Override: true
|
|
1015
|
+
}]
|
|
1016
|
+
} } : {}
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
/** The parts of a distribution's config this provider owns. */
|
|
1020
|
+
function distributionConfig(input) {
|
|
1021
|
+
const behaviour = (policy) => ({
|
|
1022
|
+
TargetOriginId: ORIGIN_ID,
|
|
1023
|
+
ViewerProtocolPolicy: "redirect-to-https",
|
|
1024
|
+
AllowedMethods: {
|
|
1025
|
+
Quantity: 3,
|
|
1026
|
+
Items: [
|
|
1027
|
+
"GET",
|
|
1028
|
+
"HEAD",
|
|
1029
|
+
"OPTIONS"
|
|
1030
|
+
],
|
|
1031
|
+
CachedMethods: {
|
|
1032
|
+
Quantity: 2,
|
|
1033
|
+
Items: ["GET", "HEAD"]
|
|
1034
|
+
}
|
|
1035
|
+
},
|
|
1036
|
+
Compress: true,
|
|
1037
|
+
CachePolicyId: CACHING_OPTIMIZED,
|
|
1038
|
+
...policy ? { ResponseHeadersPolicyId: policy } : {}
|
|
1039
|
+
});
|
|
1040
|
+
const patterns = [...new Set(input.immutable.map(pathPattern))].sort();
|
|
1041
|
+
const processors = input.processors;
|
|
1042
|
+
const processorPatterns = processors ? [...new Set(processors.prefixes.map((p) => `${pathPattern(p)}*`))].sort() : [];
|
|
1043
|
+
return {
|
|
1044
|
+
CallerReference: input.callerReference,
|
|
1045
|
+
Comment: input.comment,
|
|
1046
|
+
Enabled: true,
|
|
1047
|
+
Aliases: {
|
|
1048
|
+
Quantity: 1,
|
|
1049
|
+
Items: [input.host]
|
|
1050
|
+
},
|
|
1051
|
+
Origins: list([{
|
|
1052
|
+
Id: ORIGIN_ID,
|
|
1053
|
+
DomainName: input.bucketDomain,
|
|
1054
|
+
OriginPath: "",
|
|
1055
|
+
S3OriginConfig: { OriginAccessIdentity: "" },
|
|
1056
|
+
OriginAccessControlId: input.originAccessControlId
|
|
1057
|
+
}, ...processors ? [{
|
|
1058
|
+
Id: PROCESSORS_ORIGIN_ID,
|
|
1059
|
+
DomainName: processors.host,
|
|
1060
|
+
OriginPath: "",
|
|
1061
|
+
CustomOriginConfig: {
|
|
1062
|
+
HTTPPort: 80,
|
|
1063
|
+
HTTPSPort: 443,
|
|
1064
|
+
OriginProtocolPolicy: "https-only",
|
|
1065
|
+
OriginSslProtocols: {
|
|
1066
|
+
Quantity: 1,
|
|
1067
|
+
Items: ["TLSv1.2"]
|
|
1068
|
+
},
|
|
1069
|
+
OriginReadTimeout: 30,
|
|
1070
|
+
OriginKeepaliveTimeout: 5
|
|
1071
|
+
},
|
|
1072
|
+
CustomHeaders: {
|
|
1073
|
+
Quantity: 1,
|
|
1074
|
+
Items: [{
|
|
1075
|
+
HeaderName: ORIGIN_VERIFY_HEADER,
|
|
1076
|
+
HeaderValue: processors.secret
|
|
1077
|
+
}]
|
|
1078
|
+
}
|
|
1079
|
+
}] : []]),
|
|
1080
|
+
DefaultCacheBehavior: behaviour(input.corsPolicyId),
|
|
1081
|
+
CacheBehaviors: list([...processorPatterns.map((PathPattern) => ({
|
|
1082
|
+
...behaviour(input.corsPolicyId),
|
|
1083
|
+
TargetOriginId: PROCESSORS_ORIGIN_ID,
|
|
1084
|
+
Compress: false,
|
|
1085
|
+
CachePolicyId: CACHING_OPTIMIZED_UNCOMPRESSED,
|
|
1086
|
+
PathPattern
|
|
1087
|
+
})), ...patterns.map((PathPattern) => ({
|
|
1088
|
+
...behaviour(input.immutablePolicyId),
|
|
1089
|
+
PathPattern
|
|
1090
|
+
}))]),
|
|
1091
|
+
ViewerCertificate: {
|
|
1092
|
+
ACMCertificateArn: input.certificateArn,
|
|
1093
|
+
SSLSupportMethod: "sni-only",
|
|
1094
|
+
MinimumProtocolVersion: "TLSv1.2_2021",
|
|
1095
|
+
CloudFrontDefaultCertificate: false
|
|
1096
|
+
},
|
|
1097
|
+
HttpVersion: "http2and3",
|
|
1098
|
+
IsIPV6Enabled: true,
|
|
1099
|
+
DefaultRootObject: ""
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
/**
|
|
1103
|
+
* Whether `current` already holds everything in `wanted`: each key `wanted`
|
|
1104
|
+
* names, recursively, with lists compared as sets. What else `current` holds
|
|
1105
|
+
* — logging, a WAF, geo restrictions set by hand — is left alone, and a
|
|
1106
|
+
* `CallerReference`, fixed at creation, is never compared.
|
|
1107
|
+
*/
|
|
1108
|
+
function holds(current, wanted, key) {
|
|
1109
|
+
if (key === "CallerReference") return true;
|
|
1110
|
+
if (Array.isArray(wanted)) return Array.isArray(current) && current.length === wanted.length && wanted.every((w) => current.some((c) => holds(c, w)));
|
|
1111
|
+
if (wanted && typeof wanted === "object") {
|
|
1112
|
+
if (!current || typeof current !== "object") return false;
|
|
1113
|
+
return Object.entries(wanted).every(([k, v]) => holds(current[k], v, k));
|
|
1114
|
+
}
|
|
1115
|
+
return current === wanted;
|
|
1116
|
+
}
|
|
1117
|
+
/** Header configs a policy may hold that this provider never sets. */
|
|
1118
|
+
const FOREIGN_HEADER_CONFIGS = [
|
|
1119
|
+
"CorsConfig",
|
|
1120
|
+
"CustomHeadersConfig",
|
|
1121
|
+
"SecurityHeadersConfig",
|
|
1122
|
+
"RemoveHeadersConfig",
|
|
1123
|
+
"ServerTimingHeadersConfig"
|
|
1124
|
+
];
|
|
1125
|
+
/** Whether a policy is exactly what is wanted: nothing missing, nothing more. */
|
|
1126
|
+
function sameHeadersPolicy(current, wanted) {
|
|
1127
|
+
if (!current || !holds(current, wanted)) return false;
|
|
1128
|
+
return FOREIGN_HEADER_CONFIGS.every((k) => {
|
|
1129
|
+
if (k in wanted) return true;
|
|
1130
|
+
const extra = current[k];
|
|
1131
|
+
return !extra || extra.Quantity === 0;
|
|
1132
|
+
});
|
|
1133
|
+
}
|
|
1134
|
+
const defaultCdnClients = async (credential) => {
|
|
1135
|
+
const CF = await import("@aws-sdk/client-cloudfront");
|
|
1136
|
+
const ACM = await import("@aws-sdk/client-acm");
|
|
1137
|
+
const config = await awsClientConfig(credential, CDN_REGION);
|
|
1138
|
+
const cloudfront = awsEndpoint(process.env, "CLOUDFRONT");
|
|
1139
|
+
const acm = awsEndpoint(process.env, "ACM");
|
|
1140
|
+
return {
|
|
1141
|
+
CF,
|
|
1142
|
+
ACM,
|
|
1143
|
+
cloudfront: new CF.CloudFrontClient({
|
|
1144
|
+
...config,
|
|
1145
|
+
...cloudfront ? { endpoint: cloudfront } : {}
|
|
1146
|
+
}),
|
|
1147
|
+
acm: new ACM.ACMClient({
|
|
1148
|
+
...config,
|
|
1149
|
+
...acm ? { endpoint: acm } : {}
|
|
1150
|
+
}),
|
|
1151
|
+
wait: {
|
|
1152
|
+
timeoutMs: 9e5,
|
|
1153
|
+
intervalMs: 1e4
|
|
1154
|
+
}
|
|
1155
|
+
};
|
|
1156
|
+
};
|
|
1157
|
+
let makeCdnClients = defaultCdnClients;
|
|
1158
|
+
/** What an AWS SDK error is called, whichever field carries it. */
|
|
1159
|
+
function errorName$2(error) {
|
|
1160
|
+
const e = error;
|
|
1161
|
+
return e?.Code ?? e?.name ?? "";
|
|
1162
|
+
}
|
|
1163
|
+
async function readOr$2(read, missing) {
|
|
1164
|
+
try {
|
|
1165
|
+
return await read();
|
|
1166
|
+
} catch (error) {
|
|
1167
|
+
if (missing.includes(errorName$2(error))) return void 0;
|
|
1168
|
+
throw error;
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
1172
|
+
/** What stands in for an id a dry run would create. */
|
|
1173
|
+
const PLANNED = "(created by this deploy)";
|
|
1174
|
+
/** Each file server over a bucket, behind its own distribution. */
|
|
1175
|
+
async function ensureBucketCdn(ctx, input) {
|
|
1176
|
+
const domain = ctx.workspace.domains?.[ctx.stage];
|
|
1177
|
+
if (!domain) throw new FileServerCdnNeedsDomain(input.servers[0].id, ctx.stage);
|
|
1178
|
+
const clients = await makeCdnClients(ctx.credential);
|
|
1179
|
+
const out = {
|
|
1180
|
+
distributionArns: [],
|
|
1181
|
+
urls: {}
|
|
1182
|
+
};
|
|
1183
|
+
for (const server of input.servers) {
|
|
1184
|
+
const { arn, url } = await ensureServerCdn(ctx, clients, {
|
|
1185
|
+
...input,
|
|
1186
|
+
server,
|
|
1187
|
+
host: cdnHost(server.id, domain),
|
|
1188
|
+
processorHost: processorOriginHost(server.id, domain)
|
|
1189
|
+
});
|
|
1190
|
+
out.distributionArns.push(arn);
|
|
1191
|
+
out.urls[server.id] = url;
|
|
1192
|
+
}
|
|
1193
|
+
return out;
|
|
1194
|
+
}
|
|
1195
|
+
async function ensureServerCdn(ctx, clients, input) {
|
|
1196
|
+
const { server, host } = input;
|
|
1197
|
+
const name = cdnName(ctx.identity.scope, ctx.stage, server.id);
|
|
1198
|
+
const comment = `gkm ${ctx.identity.key} ${ctx.stage} ${server.id}`.slice(0, 128);
|
|
1199
|
+
const certificateArn = await ensureCertificate(ctx, clients, {
|
|
1200
|
+
id: server.id,
|
|
1201
|
+
host,
|
|
1202
|
+
name
|
|
1203
|
+
});
|
|
1204
|
+
const originAccessControlId = await ensureOriginAccessControl(ctx, clients, {
|
|
1205
|
+
id: server.id,
|
|
1206
|
+
name,
|
|
1207
|
+
bucket: input.bucket
|
|
1208
|
+
});
|
|
1209
|
+
const corsPolicyId = input.origins.length > 0 ? await ensureHeadersPolicy(ctx, clients, {
|
|
1210
|
+
id: server.id,
|
|
1211
|
+
kind: "cors",
|
|
1212
|
+
config: headersPolicyConfig({
|
|
1213
|
+
name: `${name}-cors`,
|
|
1214
|
+
comment: `${comment}: CORS`,
|
|
1215
|
+
origins: input.origins,
|
|
1216
|
+
immutable: false
|
|
1217
|
+
})
|
|
1218
|
+
}) : void 0;
|
|
1219
|
+
const immutablePolicyId = server.immutable.length > 0 ? await ensureHeadersPolicy(ctx, clients, {
|
|
1220
|
+
id: server.id,
|
|
1221
|
+
kind: "immutable",
|
|
1222
|
+
config: headersPolicyConfig({
|
|
1223
|
+
name: `${name}-immutable`,
|
|
1224
|
+
comment: `${comment}: CORS and Cache-Control immutable`,
|
|
1225
|
+
origins: input.origins,
|
|
1226
|
+
immutable: true
|
|
1227
|
+
})
|
|
1228
|
+
}) : void 0;
|
|
1229
|
+
const processors = server.processors?.length ? {
|
|
1230
|
+
prefixes: server.processors,
|
|
1231
|
+
host: input.processorHost,
|
|
1232
|
+
secret: await ensureOriginSecret(ctx, server.id)
|
|
1233
|
+
} : void 0;
|
|
1234
|
+
const distribution = await ensureDistribution(ctx, clients, {
|
|
1235
|
+
id: server.id,
|
|
1236
|
+
comment,
|
|
1237
|
+
config: distributionConfig({
|
|
1238
|
+
comment,
|
|
1239
|
+
callerReference: name,
|
|
1240
|
+
host,
|
|
1241
|
+
certificateArn,
|
|
1242
|
+
bucketDomain: `${input.bucket}.s3.${input.region}.amazonaws.com`,
|
|
1243
|
+
originAccessControlId,
|
|
1244
|
+
...corsPolicyId ? { corsPolicyId } : {},
|
|
1245
|
+
...immutablePolicyId ? { immutablePolicyId } : {},
|
|
1246
|
+
immutable: server.immutable,
|
|
1247
|
+
...processors ? { processors } : {}
|
|
1248
|
+
})
|
|
1249
|
+
});
|
|
1250
|
+
if (distribution.domainName !== PLANNED) {
|
|
1251
|
+
if ((await ctx.writeDns([{
|
|
1252
|
+
host,
|
|
1253
|
+
type: "CNAME",
|
|
1254
|
+
value: distribution.domainName
|
|
1255
|
+
}])).manual.includes(host)) ctx.log(` ${host} answers once that record exists — ${server.id}'s URL is https://${host}`);
|
|
1256
|
+
}
|
|
1257
|
+
return {
|
|
1258
|
+
arn: distribution.arn,
|
|
1259
|
+
url: `https://${host}`
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* The secret the CDN sends a file server's processors: the one in the stage's
|
|
1264
|
+
* secrets, or a new one written there — read by the stack, whose edge checks
|
|
1265
|
+
* it, when it composes.
|
|
1266
|
+
*/
|
|
1267
|
+
async function ensureOriginSecret(ctx, serverId) {
|
|
1268
|
+
const key = originSecretKey(serverId);
|
|
1269
|
+
const kept = ctx.secrets[key];
|
|
1270
|
+
if (kept) return kept;
|
|
1271
|
+
const secret = newOriginSecret();
|
|
1272
|
+
ctx.log(` ${serverId}: a new processor origin secret, kept as ${key}`);
|
|
1273
|
+
await ctx.writeSecrets({ [key]: secret });
|
|
1274
|
+
return secret;
|
|
1275
|
+
}
|
|
1276
|
+
/** The certificate for `host`: recorded, adopted, or requested — then issued. */
|
|
1277
|
+
async function ensureCertificate(ctx, clients, input) {
|
|
1278
|
+
const { ACM, acm } = clients;
|
|
1279
|
+
const { id, host } = input;
|
|
1280
|
+
const entry = {
|
|
1281
|
+
key: `acm-certificate:${id}`,
|
|
1282
|
+
type: "acm-certificate",
|
|
1283
|
+
data: { host }
|
|
1284
|
+
};
|
|
1285
|
+
const describe = (arn) => readOr$2(() => acm.send(new ACM.DescribeCertificateCommand({ CertificateArn: arn })), ["ResourceNotFoundException"]).then((out) => out?.Certificate);
|
|
1286
|
+
let arn;
|
|
1287
|
+
const recorded = ctx.state.record(entry.key);
|
|
1288
|
+
if (recorded?.id && recorded.data?.host === host) {
|
|
1289
|
+
const found = await describe(recorded.id);
|
|
1290
|
+
if (found && found.Status !== "FAILED" && found.Status !== "VALIDATION_TIMED_OUT" && found.Status !== "REVOKED" && found.Status !== "EXPIRED") arn = recorded.id;
|
|
1291
|
+
}
|
|
1292
|
+
if (!arn) {
|
|
1293
|
+
arn = await findCertificate(clients, host);
|
|
1294
|
+
if (arn) ctx.log(` adopting the certificate for ${host} — ${arn}`);
|
|
1295
|
+
}
|
|
1296
|
+
if (!arn) {
|
|
1297
|
+
if (ctx.dryRun) {
|
|
1298
|
+
await ctx.change({
|
|
1299
|
+
construct: id,
|
|
1300
|
+
resource: `certificate ${host}`,
|
|
1301
|
+
change: "request (ACM, us-east-1, DNS-validated)"
|
|
1302
|
+
}, async () => {});
|
|
1303
|
+
return PLANNED;
|
|
1304
|
+
}
|
|
1305
|
+
await ctx.state.pending(entry);
|
|
1306
|
+
arn = (await acm.send(new ACM.RequestCertificateCommand({
|
|
1307
|
+
DomainName: host,
|
|
1308
|
+
ValidationMethod: "DNS",
|
|
1309
|
+
IdempotencyToken: input.name.replace(/[^A-Za-z0-9]/g, "").slice(0, 32),
|
|
1310
|
+
Tags: identityTags(ctx, id).map((t) => ({
|
|
1311
|
+
Key: t.Key,
|
|
1312
|
+
Value: t.Value
|
|
1313
|
+
}))
|
|
1314
|
+
}))).CertificateArn;
|
|
1315
|
+
await ctx.change({
|
|
1316
|
+
construct: id,
|
|
1317
|
+
resource: `certificate ${host}`,
|
|
1318
|
+
change: "request (ACM, us-east-1, DNS-validated)"
|
|
1319
|
+
}, async () => {});
|
|
1320
|
+
}
|
|
1321
|
+
if (!ctx.dryRun) await ctx.state.ready(entry, arn);
|
|
1322
|
+
let certificate = await describe(arn);
|
|
1323
|
+
let record = certificate?.DomainValidationOptions?.[0]?.ResourceRecord;
|
|
1324
|
+
for (let i = 0; !record && i < 15; i++) {
|
|
1325
|
+
await sleep(Math.min(clients.wait.intervalMs, 2e3));
|
|
1326
|
+
certificate = await describe(arn);
|
|
1327
|
+
record = certificate?.DomainValidationOptions?.[0]?.ResourceRecord;
|
|
1328
|
+
}
|
|
1329
|
+
const validation = record ? {
|
|
1330
|
+
name: record.Name.replace(/\.$/, ""),
|
|
1331
|
+
value: record.Value.replace(/\.$/, "")
|
|
1332
|
+
} : void 0;
|
|
1333
|
+
const written = validation ? await ctx.writeDns([{
|
|
1334
|
+
host: validation.name,
|
|
1335
|
+
type: "CNAME",
|
|
1336
|
+
value: validation.value
|
|
1337
|
+
}]) : { manual: [] };
|
|
1338
|
+
if (certificate?.Status === "ISSUED" || ctx.dryRun) return arn;
|
|
1339
|
+
if (validation && written.manual.includes(validation.name.toLowerCase())) throw new CdnCertificatePending(id, host, validation, "manual");
|
|
1340
|
+
ctx.log(` waiting for ACM to issue the certificate for ${host}…`);
|
|
1341
|
+
const deadline = Date.now() + clients.wait.timeoutMs;
|
|
1342
|
+
while (certificate?.Status === "PENDING_VALIDATION" && Date.now() < deadline) {
|
|
1343
|
+
await sleep(clients.wait.intervalMs);
|
|
1344
|
+
certificate = await describe(arn);
|
|
1345
|
+
}
|
|
1346
|
+
if (certificate?.Status === "ISSUED") return arn;
|
|
1347
|
+
if (certificate?.Status === "PENDING_VALIDATION" || !certificate) throw new CdnCertificatePending(id, host, validation ?? {
|
|
1348
|
+
name: `(not published yet) ${host}`,
|
|
1349
|
+
value: ""
|
|
1350
|
+
}, "timeout");
|
|
1351
|
+
throw new CdnCertificateFailed(id, host, certificate.Status ?? "unknown", certificate.FailureReason);
|
|
1352
|
+
}
|
|
1353
|
+
/** An issued or pending certificate for exactly `host`, if the account has one. */
|
|
1354
|
+
async function findCertificate(clients, host) {
|
|
1355
|
+
const { ACM, acm } = clients;
|
|
1356
|
+
let NextToken;
|
|
1357
|
+
do {
|
|
1358
|
+
const page = await acm.send(new ACM.ListCertificatesCommand({
|
|
1359
|
+
CertificateStatuses: ["ISSUED", "PENDING_VALIDATION"],
|
|
1360
|
+
...NextToken ? { NextToken } : {}
|
|
1361
|
+
}));
|
|
1362
|
+
const match = page.CertificateSummaryList?.find((c) => c.DomainName?.toLowerCase() === host);
|
|
1363
|
+
if (match?.CertificateArn) return match.CertificateArn;
|
|
1364
|
+
NextToken = page.NextToken;
|
|
1365
|
+
} while (NextToken);
|
|
1366
|
+
}
|
|
1367
|
+
/** The origin access control CloudFront signs its bucket requests with. */
|
|
1368
|
+
async function ensureOriginAccessControl(ctx, clients, input) {
|
|
1369
|
+
const { CF, cloudfront } = clients;
|
|
1370
|
+
const entry = {
|
|
1371
|
+
key: `cloudfront-oac:${input.id}`,
|
|
1372
|
+
type: "cloudfront-oac",
|
|
1373
|
+
data: { name: input.name }
|
|
1374
|
+
};
|
|
1375
|
+
const recorded = ctx.state.record(entry.key)?.id;
|
|
1376
|
+
if (recorded) {
|
|
1377
|
+
if (await readOr$2(() => cloudfront.send(new CF.GetOriginAccessControlCommand({ Id: recorded })), ["NoSuchOriginAccessControl"])) return recorded;
|
|
1378
|
+
}
|
|
1379
|
+
let Marker;
|
|
1380
|
+
do {
|
|
1381
|
+
const page = await cloudfront.send(new CF.ListOriginAccessControlsCommand({ ...Marker ? { Marker } : {} }));
|
|
1382
|
+
const match = page.OriginAccessControlList?.Items?.find((o) => o.Name === input.name);
|
|
1383
|
+
if (match?.Id) {
|
|
1384
|
+
if (!ctx.dryRun) await ctx.state.ready(entry, match.Id);
|
|
1385
|
+
return match.Id;
|
|
1386
|
+
}
|
|
1387
|
+
Marker = page.OriginAccessControlList?.NextMarker;
|
|
1388
|
+
} while (Marker);
|
|
1389
|
+
let id = PLANNED;
|
|
1390
|
+
await ctx.change({
|
|
1391
|
+
construct: input.id,
|
|
1392
|
+
resource: `origin access control ${input.name}`,
|
|
1393
|
+
change: `create (CloudFront signs its reads of ${input.bucket})`
|
|
1394
|
+
}, async () => {
|
|
1395
|
+
await ctx.state.pending(entry);
|
|
1396
|
+
id = (await cloudfront.send(new CF.CreateOriginAccessControlCommand({ OriginAccessControlConfig: {
|
|
1397
|
+
Name: input.name,
|
|
1398
|
+
Description: `gkm: CloudFront's reads of ${input.bucket}`,
|
|
1399
|
+
OriginAccessControlOriginType: "s3",
|
|
1400
|
+
SigningBehavior: "always",
|
|
1401
|
+
SigningProtocol: "sigv4"
|
|
1402
|
+
} }))).OriginAccessControl.Id;
|
|
1403
|
+
await ctx.state.ready(entry, id);
|
|
1404
|
+
});
|
|
1405
|
+
return id;
|
|
1406
|
+
}
|
|
1407
|
+
/** One response headers policy, as wanted. */
|
|
1408
|
+
async function ensureHeadersPolicy(ctx, clients, input) {
|
|
1409
|
+
const { CF, cloudfront } = clients;
|
|
1410
|
+
const { config } = input;
|
|
1411
|
+
const entry = {
|
|
1412
|
+
key: `cloudfront-headers:${input.id}:${input.kind}`,
|
|
1413
|
+
type: "cloudfront-headers",
|
|
1414
|
+
data: { name: config.Name }
|
|
1415
|
+
};
|
|
1416
|
+
const read = (Id) => readOr$2(() => cloudfront.send(new CF.GetResponseHeadersPolicyConfigCommand({ Id })), ["NoSuchResponseHeadersPolicy"]);
|
|
1417
|
+
let id = ctx.state.record(entry.key)?.id;
|
|
1418
|
+
let current = id ? await read(id) : void 0;
|
|
1419
|
+
if (!current) {
|
|
1420
|
+
id = void 0;
|
|
1421
|
+
let Marker;
|
|
1422
|
+
do {
|
|
1423
|
+
const page = await cloudfront.send(new CF.ListResponseHeadersPoliciesCommand({
|
|
1424
|
+
Type: "custom",
|
|
1425
|
+
...Marker ? { Marker } : {}
|
|
1426
|
+
}));
|
|
1427
|
+
const match = page.ResponseHeadersPolicyList?.Items?.find((p) => p.ResponseHeadersPolicy?.ResponseHeadersPolicyConfig?.Name === config.Name);
|
|
1428
|
+
if (match?.ResponseHeadersPolicy?.Id) {
|
|
1429
|
+
id = match.ResponseHeadersPolicy.Id;
|
|
1430
|
+
current = await read(id);
|
|
1431
|
+
}
|
|
1432
|
+
Marker = page.ResponseHeadersPolicyList?.NextMarker;
|
|
1433
|
+
} while (!id && Marker);
|
|
1434
|
+
}
|
|
1435
|
+
if (id && current) {
|
|
1436
|
+
if (!ctx.dryRun) await ctx.state.ready(entry, id);
|
|
1437
|
+
if (sameHeadersPolicy(current.ResponseHeadersPolicyConfig, config)) return id;
|
|
1438
|
+
const existing = id;
|
|
1439
|
+
await ctx.change({
|
|
1440
|
+
construct: input.id,
|
|
1441
|
+
resource: `response headers policy ${config.Name}`,
|
|
1442
|
+
change: "put back what drifted"
|
|
1443
|
+
}, async () => {
|
|
1444
|
+
await cloudfront.send(new CF.UpdateResponseHeadersPolicyCommand({
|
|
1445
|
+
Id: existing,
|
|
1446
|
+
IfMatch: current.ETag,
|
|
1447
|
+
ResponseHeadersPolicyConfig: config
|
|
1448
|
+
}));
|
|
1449
|
+
});
|
|
1450
|
+
return existing;
|
|
1451
|
+
}
|
|
1452
|
+
let created = PLANNED;
|
|
1453
|
+
await ctx.change({
|
|
1454
|
+
construct: input.id,
|
|
1455
|
+
resource: `response headers policy ${config.Name}`,
|
|
1456
|
+
change: "create"
|
|
1457
|
+
}, async () => {
|
|
1458
|
+
await ctx.state.pending(entry);
|
|
1459
|
+
created = (await cloudfront.send(new CF.CreateResponseHeadersPolicyCommand({ ResponseHeadersPolicyConfig: config }))).ResponseHeadersPolicy.Id;
|
|
1460
|
+
await ctx.state.ready(entry, created);
|
|
1461
|
+
});
|
|
1462
|
+
return created;
|
|
1463
|
+
}
|
|
1464
|
+
/** The distribution: recorded, found by its comment, or created — as wanted. */
|
|
1465
|
+
async function ensureDistribution(ctx, clients, input) {
|
|
1466
|
+
const { CF, cloudfront } = clients;
|
|
1467
|
+
const entry = {
|
|
1468
|
+
key: `cloudfront-distribution:${input.id}`,
|
|
1469
|
+
type: "cloudfront-distribution"
|
|
1470
|
+
};
|
|
1471
|
+
const get = (Id) => readOr$2(() => cloudfront.send(new CF.GetDistributionCommand({ Id })), ["NoSuchDistribution"]);
|
|
1472
|
+
let found = ctx.state.record(entry.key)?.id ? await get(ctx.state.record(entry.key).id) : void 0;
|
|
1473
|
+
if (!found) {
|
|
1474
|
+
let Marker;
|
|
1475
|
+
do {
|
|
1476
|
+
const page = await cloudfront.send(new CF.ListDistributionsCommand({ ...Marker ? { Marker } : {} }));
|
|
1477
|
+
const match = page.DistributionList?.Items?.find((d) => d.Comment === input.comment);
|
|
1478
|
+
if (match?.Id) found = await get(match.Id);
|
|
1479
|
+
Marker = page.DistributionList?.NextMarker;
|
|
1480
|
+
} while (!found && Marker);
|
|
1481
|
+
}
|
|
1482
|
+
if (found?.Distribution) {
|
|
1483
|
+
const distribution = found.Distribution;
|
|
1484
|
+
const id = distribution.Id;
|
|
1485
|
+
const located = {
|
|
1486
|
+
arn: distribution.ARN,
|
|
1487
|
+
domainName: distribution.DomainName
|
|
1488
|
+
};
|
|
1489
|
+
if (!ctx.dryRun) await ctx.state.ready({
|
|
1490
|
+
...entry,
|
|
1491
|
+
data: located
|
|
1492
|
+
}, id);
|
|
1493
|
+
const current = await cloudfront.send(new CF.GetDistributionConfigCommand({ Id: id }));
|
|
1494
|
+
if (holds(current.DistributionConfig, input.config)) return located;
|
|
1495
|
+
await ctx.change({
|
|
1496
|
+
construct: input.id,
|
|
1497
|
+
resource: `distribution ${id}`,
|
|
1498
|
+
change: "put back what drifted"
|
|
1499
|
+
}, async () => {
|
|
1500
|
+
await cloudfront.send(new CF.UpdateDistributionCommand({
|
|
1501
|
+
Id: id,
|
|
1502
|
+
IfMatch: current.ETag,
|
|
1503
|
+
DistributionConfig: {
|
|
1504
|
+
...current.DistributionConfig,
|
|
1505
|
+
...input.config,
|
|
1506
|
+
CallerReference: current.DistributionConfig.CallerReference
|
|
1507
|
+
}
|
|
1508
|
+
}));
|
|
1509
|
+
});
|
|
1510
|
+
return located;
|
|
1511
|
+
}
|
|
1512
|
+
let created = {
|
|
1513
|
+
arn: `arn:aws:cloudfront::${PLANNED}`,
|
|
1514
|
+
domainName: PLANNED
|
|
1515
|
+
};
|
|
1516
|
+
const host = input.config.Aliases?.Items?.[0] ?? input.id;
|
|
1517
|
+
await ctx.change({
|
|
1518
|
+
construct: input.id,
|
|
1519
|
+
resource: `distribution ${host}`,
|
|
1520
|
+
change: "create (CloudFront, takes minutes to reach every edge)"
|
|
1521
|
+
}, async () => {
|
|
1522
|
+
await ctx.state.pending(entry);
|
|
1523
|
+
const out = await cloudfront.send(new CF.CreateDistributionWithTagsCommand({ DistributionConfigWithTags: {
|
|
1524
|
+
DistributionConfig: input.config,
|
|
1525
|
+
Tags: { Items: identityTags(ctx, input.id).map((t) => ({
|
|
1526
|
+
Key: t.Key,
|
|
1527
|
+
Value: t.Value
|
|
1528
|
+
})) }
|
|
1529
|
+
} }));
|
|
1530
|
+
created = {
|
|
1531
|
+
arn: out.Distribution.ARN,
|
|
1532
|
+
domainName: out.Distribution.DomainName
|
|
1533
|
+
};
|
|
1534
|
+
await ctx.state.ready({
|
|
1535
|
+
...entry,
|
|
1536
|
+
data: created
|
|
1537
|
+
}, out.Distribution.Id);
|
|
1538
|
+
});
|
|
1539
|
+
return created;
|
|
1540
|
+
}
|
|
1541
|
+
//#endregion
|
|
798
1542
|
//#region src/providers/s3/events.ts
|
|
799
1543
|
/** What S3 tells the topic about: every object written, and every removed. */
|
|
800
1544
|
const BUCKET_EVENTS = ["s3:ObjectCreated:*", "s3:ObjectRemoved:*"];
|
|
@@ -1223,10 +1967,12 @@ function bucketConstructs(manifest) {
|
|
|
1223
1967
|
return Object.entries(manifest).filter(([, d]) => d.kind === "objects").map(([id, d]) => ({
|
|
1224
1968
|
id,
|
|
1225
1969
|
versioned: d.versioned === true,
|
|
1226
|
-
...
|
|
1970
|
+
...bucketExpiry(manifest, id) ? { expire: bucketExpiry(manifest, id) } : {},
|
|
1227
1971
|
servers: Object.entries(manifest).filter(([, s]) => s.kind === "file-server" && s.of === id).map(([sid, s]) => ({
|
|
1228
1972
|
id: sid,
|
|
1229
|
-
open: s.open ?? []
|
|
1973
|
+
open: s.open ?? [],
|
|
1974
|
+
immutable: s.immutable ?? [],
|
|
1975
|
+
...s.processors?.length ? { processors: [...new Set(s.processors.map((p) => p.prefix))].sort() } : {}
|
|
1230
1976
|
})).sort((a, b) => a.id.localeCompare(b.id))
|
|
1231
1977
|
})).sort((a, b) => a.id.localeCompare(b.id));
|
|
1232
1978
|
}
|
|
@@ -1377,12 +2123,25 @@ async function ensureBucket(ctx, stageClients, construct, watchers) {
|
|
|
1377
2123
|
clients = await makeClients(ctx.credential, located.region);
|
|
1378
2124
|
ctx.log(` '${bucket}' is in ${located.region}, not ${stageClients.region}: '${id}' is reached and addressed there`);
|
|
1379
2125
|
}
|
|
2126
|
+
const cdn = ctx.config.cdn !== false && construct.servers.length > 0 ? await ensureBucketCdn(ctx, {
|
|
2127
|
+
bucket,
|
|
2128
|
+
region: clients.region,
|
|
2129
|
+
servers: construct.servers,
|
|
2130
|
+
origins: bucketOrigins({
|
|
2131
|
+
workspace: ctx.workspace,
|
|
2132
|
+
manifest: ctx.manifest,
|
|
2133
|
+
runnables: ctx.runnables,
|
|
2134
|
+
stage: ctx.stage,
|
|
2135
|
+
bucket: id
|
|
2136
|
+
}).origins
|
|
2137
|
+
}) : void 0;
|
|
1380
2138
|
await ensureBucketSettings(ctx, clients, {
|
|
1381
2139
|
id,
|
|
1382
2140
|
bucket,
|
|
1383
2141
|
exists,
|
|
1384
2142
|
versioning,
|
|
1385
2143
|
open,
|
|
2144
|
+
...cdn ? { cdn } : {},
|
|
1386
2145
|
...construct.expire ? { expire: construct.expire } : {}
|
|
1387
2146
|
});
|
|
1388
2147
|
const events = watchers ? await ensureBucketEvents(ctx, clients, {
|
|
@@ -1419,8 +2178,9 @@ async function ensureBucket(ctx, stageClients, construct, watchers) {
|
|
|
1419
2178
|
watchers
|
|
1420
2179
|
});
|
|
1421
2180
|
}
|
|
1422
|
-
const
|
|
2181
|
+
const bucketUrl = bucketPublicUrl(bucket, clients.region, clients.endpoint);
|
|
1423
2182
|
for (const server of construct.servers) {
|
|
2183
|
+
const publicUrl = cdn?.urls[server.id] ?? bucketUrl;
|
|
1424
2184
|
const key = provideKey(server.id, "url");
|
|
1425
2185
|
const entry = {
|
|
1426
2186
|
key: `file-server:${server.id}`,
|
|
@@ -1578,7 +2338,7 @@ async function ensureBucketSettings(ctx, clients, input) {
|
|
|
1578
2338
|
Value: t.Value
|
|
1579
2339
|
})) }
|
|
1580
2340
|
})));
|
|
1581
|
-
const isOpen = input.open.length > 0;
|
|
2341
|
+
const isOpen = input.open.length > 0 && !input.cdn;
|
|
1582
2342
|
const block = publicAccessBlock(isOpen);
|
|
1583
2343
|
const ensureBlock = async () => {
|
|
1584
2344
|
const now = (await read(() => s3.send(new S3.GetPublicAccessBlockCommand({ Bucket })), ["NoSuchPublicAccessBlockConfiguration"]))?.PublicAccessBlockConfiguration;
|
|
@@ -1587,10 +2347,10 @@ async function ensureBucketSettings(ctx, clients, input) {
|
|
|
1587
2347
|
PublicAccessBlockConfiguration: block
|
|
1588
2348
|
})));
|
|
1589
2349
|
};
|
|
1590
|
-
const policy = bucketPolicy(bucket, input.open);
|
|
2350
|
+
const policy = bucketPolicy(bucket, input.open, input.cdn ? { cdn: input.cdn } : {});
|
|
1591
2351
|
const ensurePolicy = async () => {
|
|
1592
2352
|
const current = await read(() => s3.send(new S3.GetBucketPolicyCommand({ Bucket })), ["NoSuchBucketPolicy"]);
|
|
1593
|
-
if (!(current?.Policy !== void 0 && canonicalPolicy(JSON.parse(current.Policy)) === canonicalPolicy(policy))) await set(isOpen ? `policy: TLS only, public read on ${input.open.join(", ")}` : "policy: TLS only", () => s3.send(new S3.PutBucketPolicyCommand({
|
|
2353
|
+
if (!(current?.Policy !== void 0 && canonicalPolicy(JSON.parse(current.Policy)) === canonicalPolicy(policy))) await set(isOpen ? `policy: TLS only, public read on ${input.open.join(", ")}` : input.cdn && input.open.length > 0 ? `policy: TLS only, CloudFront reads ${input.open.join(", ")}` : "policy: TLS only", () => s3.send(new S3.PutBucketPolicyCommand({
|
|
1594
2354
|
Bucket,
|
|
1595
2355
|
Policy: JSON.stringify(policy)
|
|
1596
2356
|
})));
|
|
@@ -1892,6 +2652,17 @@ async function provisionStage(options) {
|
|
|
1892
2652
|
log(` ${dryRun ? "would " : ""}${action.change} — ${action.resource}`);
|
|
1893
2653
|
},
|
|
1894
2654
|
writeSecrets,
|
|
2655
|
+
writeDns: (records) => writeProviderDns(records, {
|
|
2656
|
+
workspace,
|
|
2657
|
+
stage,
|
|
2658
|
+
dryRun,
|
|
2659
|
+
credentials: options.credentials ?? storedCredentials({
|
|
2660
|
+
env,
|
|
2661
|
+
...options.home ? { home: options.home } : {}
|
|
2662
|
+
}),
|
|
2663
|
+
...options.dnsProviderFor ? { providerFor: options.dnsProviderFor } : {},
|
|
2664
|
+
log
|
|
2665
|
+
}),
|
|
1895
2666
|
log
|
|
1896
2667
|
});
|
|
1897
2668
|
if (entry.report.actions.length === 0) log(" up to date");
|
|
@@ -2334,6 +3105,7 @@ async function provisionResources(phaseCtx, request, ctx, store, emit) {
|
|
|
2334
3105
|
...request.rotateKeys ? { rotateKeys: true } : {},
|
|
2335
3106
|
...request.retireOldKeys ? { retireOldKeys: true } : {},
|
|
2336
3107
|
...ctx.home ? { home: ctx.home } : {},
|
|
3108
|
+
credentials: ctx.credentials,
|
|
2337
3109
|
log: (line) => phaseCtx.logger.info(line)
|
|
2338
3110
|
});
|
|
2339
3111
|
if (!ctx.dryRun) return;
|
|
@@ -2664,4 +3436,4 @@ async function load(input, sandbox) {
|
|
|
2664
3436
|
//#endregion
|
|
2665
3437
|
export { UnknownDeployApps as a, bucketName as c, TargetBuildsNothing as i, iamUserName as l, NoDeployableApps as n, backupsUserName as o, RollbackFailed as r, IAM_USER_PATH as s, deploy as t };
|
|
2666
3438
|
|
|
2667
|
-
//# sourceMappingURL=deploy-
|
|
3439
|
+
//# sourceMappingURL=deploy-D8pUA9Y0.mjs.map
|