@caelo-cms/provisioning 0.10.37 → 0.10.39

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.
Files changed (62) hide show
  1. package/dist/admin-domain.d.ts +21 -3
  2. package/dist/admin-domain.d.ts.map +1 -1
  3. package/dist/admin-domain.js +88 -16
  4. package/dist/admin-domain.js.map +1 -1
  5. package/dist/cert-wait.d.ts +84 -0
  6. package/dist/cert-wait.d.ts.map +1 -0
  7. package/dist/cert-wait.js +95 -0
  8. package/dist/cert-wait.js.map +1 -0
  9. package/dist/cli.js +146 -23
  10. package/dist/cli.js.map +1 -1
  11. package/dist/dns/groups.d.ts +36 -0
  12. package/dist/dns/groups.d.ts.map +1 -0
  13. package/dist/dns/groups.js +46 -0
  14. package/dist/dns/groups.js.map +1 -0
  15. package/dist/gcloud-components.d.ts +42 -0
  16. package/dist/gcloud-components.d.ts.map +1 -0
  17. package/dist/gcloud-components.js +53 -0
  18. package/dist/gcloud-components.js.map +1 -0
  19. package/dist/gcloud.d.ts +14 -0
  20. package/dist/gcloud.d.ts.map +1 -1
  21. package/dist/gcloud.js +18 -5
  22. package/dist/gcloud.js.map +1 -1
  23. package/dist/install-state.d.ts +8 -0
  24. package/dist/install-state.d.ts.map +1 -1
  25. package/dist/install-state.js +5 -0
  26. package/dist/install-state.js.map +1 -1
  27. package/dist/lifecycle.d.ts.map +1 -1
  28. package/dist/lifecycle.js +19 -3
  29. package/dist/lifecycle.js.map +1 -1
  30. package/dist/redirects-emit.d.ts +0 -13
  31. package/dist/redirects-emit.d.ts.map +1 -1
  32. package/dist/redirects-emit.js +19 -0
  33. package/dist/redirects-emit.js.map +1 -1
  34. package/dist/regions.d.ts.map +1 -1
  35. package/dist/regions.js +4 -1
  36. package/dist/regions.js.map +1 -1
  37. package/dist/runtime-secrets.d.ts +13 -1
  38. package/dist/runtime-secrets.d.ts.map +1 -1
  39. package/dist/runtime-secrets.js +40 -4
  40. package/dist/runtime-secrets.js.map +1 -1
  41. package/dist/self-hosted-roles.d.ts.map +1 -1
  42. package/dist/self-hosted-roles.js +2 -0
  43. package/dist/self-hosted-roles.js.map +1 -1
  44. package/dist/stack-contract.d.ts +16 -2
  45. package/dist/stack-contract.d.ts.map +1 -1
  46. package/dist/stack-contract.js +24 -1
  47. package/dist/stack-contract.js.map +1 -1
  48. package/dist/wizard.d.ts +5 -19
  49. package/dist/wizard.d.ts.map +1 -1
  50. package/dist/wizard.js +45 -1
  51. package/dist/wizard.js.map +1 -1
  52. package/dist/wizards/gcp-pulumi.d.ts +2 -0
  53. package/dist/wizards/gcp-pulumi.d.ts.map +1 -1
  54. package/dist/wizards/gcp-pulumi.js +6 -0
  55. package/dist/wizards/gcp-pulumi.js.map +1 -1
  56. package/dist/wizards/gcp.d.ts.map +1 -1
  57. package/dist/wizards/gcp.js +129 -112
  58. package/dist/wizards/gcp.js.map +1 -1
  59. package/package.json +3 -3
  60. package/stacks/gcp/index.ts +20 -3
  61. package/stacks/gcp-firebase/index.ts +20 -5
  62. package/stacks/self-hosted/index.ts +3 -1
@@ -38,6 +38,7 @@ import {
38
38
  type CloudRunEnvVar,
39
39
  type CloudRunSlug,
40
40
  databaseUrls,
41
+ defaultStagingHost,
41
42
  gatewayEnvContract,
42
43
  type RuntimeSecret,
43
44
  runtimeSecretBindings,
@@ -46,6 +47,8 @@ import {
46
47
 
47
48
  const cfg = new pulumi.Config();
48
49
  const domain = cfg.require("domain");
50
+ // #650 — the protected staging site's host (wizard / `--staging-domain`).
51
+ const stagingHost = cfg.get("stagingDomain") ?? defaultStagingHost(domain);
49
52
  const ownerEmail = cfg.require("ownerEmail");
50
53
  const project = cfg.require("project");
51
54
  // #607 — chosen by the operator in the wizard and fixed after install; no
@@ -245,6 +248,7 @@ const runtimeSecretIds: Record<RuntimeSecret, pulumi.Input<string>> = {
245
248
  "secret-kek": kekSecret.resource.secretId,
246
249
  "internal-secret": gcpSecretId(env, "internal-secret"),
247
250
  "tool-approval-secret": gcpSecretId(env, "tool-approval-secret"),
251
+ "staging-gate-secret": gcpSecretId(env, "staging-gate-secret"),
248
252
  "public-role-password": gcpSecretId(env, "public-role-password"),
249
253
  "gateway-role-password": gcpSecretId(env, "gateway-role-password"),
250
254
  };
@@ -645,6 +649,7 @@ const envContractInputs = {
645
649
  env,
646
650
  domain,
647
651
  region,
652
+ stagingHost,
648
653
  databaseUrls: {
649
654
  admin: dbUrls.admin,
650
655
  publicAdmin: dbUrls.publicAdmin,
@@ -1015,17 +1020,29 @@ const urlMap = new gcp.compute.URLMap(
1015
1020
  opts,
1016
1021
  );
1017
1022
 
1018
- // Managed TLS cert covering both hostnames. Single cert resource;
1019
- // the certificate's SAN list grows by one entry vs the prior version.
1023
+ // Managed TLS cert covering both hostnames.
1020
1024
  const managedCert = new gcp.compute.ManagedSslCertificate(
1021
1025
  `${namePrefix}-cert`,
1022
1026
  { managed: { domains: [domain, adminDomain] } },
1023
1027
  opts,
1024
1028
  );
1025
1029
 
1030
+ // The admin's own certificate. A multi-domain managed certificate is only
1031
+ // served once EVERY domain on it validates, and the main domain validates
1032
+ // only after its A record points here — which an operator with a live site
1033
+ // does at go-live, not during the install. With this one the admin gets
1034
+ // HTTPS as soon as admin.<domain> resolves. The combined certificate above
1035
+ // stays (replacing it would re-issue the live site's certificate); the load
1036
+ // balancer picks whichever ACTIVE certificate matches the requested host.
1037
+ const adminCert = new gcp.compute.ManagedSslCertificate(
1038
+ `${namePrefix}-admin-cert`,
1039
+ { managed: { domains: [adminDomain] } },
1040
+ opts,
1041
+ );
1042
+
1026
1043
  const httpsProxy = new gcp.compute.TargetHttpsProxy(
1027
1044
  `${namePrefix}-https-proxy`,
1028
- { urlMap: urlMap.id, sslCertificates: [managedCert.id] },
1045
+ { urlMap: urlMap.id, sslCertificates: [adminCert.id, managedCert.id] },
1029
1046
  opts,
1030
1047
  );
1031
1048
 
@@ -45,6 +45,7 @@ import {
45
45
  type CloudRunEnvVar,
46
46
  type CloudRunSlug,
47
47
  databaseUrls,
48
+ defaultStagingHost,
48
49
  gatewayEnvContract,
49
50
  type RuntimeSecret,
50
51
  runtimeSecretBindings,
@@ -52,6 +53,8 @@ import {
52
53
 
53
54
  const cfg = new pulumi.Config();
54
55
  const domain = cfg.require("domain");
56
+ // #650 — the protected staging site's host (wizard / `--staging-domain`).
57
+ const stagingHost = cfg.get("stagingDomain") ?? defaultStagingHost(domain);
55
58
  const ownerEmail = cfg.require("ownerEmail");
56
59
  const project = cfg.require("project");
57
60
  // #607 — chosen by the operator in the wizard and fixed after install; no
@@ -219,6 +222,7 @@ const runtimeSecretIds: Record<RuntimeSecret, pulumi.Input<string>> = {
219
222
  "secret-kek": kekSecret.resource.secretId,
220
223
  "internal-secret": gcpSecretId(env, "internal-secret"),
221
224
  "tool-approval-secret": gcpSecretId(env, "tool-approval-secret"),
225
+ "staging-gate-secret": gcpSecretId(env, "staging-gate-secret"),
222
226
  "public-role-password": gcpSecretId(env, "public-role-password"),
223
227
  "gateway-role-password": gcpSecretId(env, "gateway-role-password"),
224
228
  };
@@ -590,6 +594,7 @@ const envContractInputs = {
590
594
  env,
591
595
  domain,
592
596
  region,
597
+ stagingHost,
593
598
  databaseUrls: {
594
599
  admin: dbUrls.admin,
595
600
  publicAdmin: dbUrls.publicAdmin,
@@ -606,10 +611,14 @@ const gatewaySvc = cloudRunService({
606
611
  maxInstances: 100,
607
612
  memory: "512Mi",
608
613
  timeout: "60s",
609
- // v0.3.1 — gateway is reachable ONLY via Firebase Hosting
610
- // rewrites or any caller holding roles/run.invoker on the
611
- // service. Anonymous internet traffic gets a Cloud Run 403.
612
- ingress: "INGRESS_TRAFFIC_INTERNAL_LOAD_BALANCER",
614
+ // #650 — all traffic: the gateway also serves staging.<domain> through
615
+ // a Cloud Run domain mapping (`cms-provision staging-domain enable`),
616
+ // and Cloud Run refuses domain-mapped requests under an internal-only
617
+ // ingress. Exposure is unchanged in substance: the service was already
618
+ // invokable by allUsers (gateway-public-invoker below) and every /api
619
+ // request passes its rate limits, captcha and honeypot; staging requests
620
+ // pass the staging access gate.
621
+ ingress: "INGRESS_TRAFFIC_ALL",
613
622
  contractEnv: gatewayEnvContract(envContractInputs),
614
623
  });
615
624
 
@@ -940,12 +949,18 @@ const dnsRecords: pulumi.Output<DnsRecord[]> = pulumi
940
949
  // groups; each group has a list of `desired` records the operator
941
950
  // creates and `discovered` records currently in DNS. We surface the
942
951
  // `desired` set so the operator knows what to put in the registrar.
952
+ // Each record carries its own name: besides the apex A records Firebase
953
+ // asks for TXT records, some on `_acme-challenge.<domain>`, which let it
954
+ // issue the certificate before the apex moves. Records Firebase wants
955
+ // REMOVED (the old site's A records) are not records to add; replacing
956
+ // the apex records at go-live drops them.
943
957
  const apex = firebaseUpdates?.[0];
944
958
  if (apex) {
945
959
  for (const desired of apex.desireds ?? []) {
946
960
  for (const v of desired.records ?? []) {
961
+ if (v.requiredAction === "REMOVE") continue;
947
962
  out.push({
948
- hostname: domain,
963
+ hostname: (v.domainName ?? desired.domainName ?? domain).replace(/\.$/, ""),
949
964
  type: (v.type ?? "A") as DnsRecord["type"],
950
965
  value: v.rdata ?? "",
951
966
  purpose: "Public site → Firebase Hosting CDN",
@@ -29,6 +29,8 @@ import { generateDockerCompose } from "../../dist/compose.js";
29
29
 
30
30
  const cfg = new pulumi.Config();
31
31
  const domain = cfg.require("domain");
32
+ // #650 — the staging site's host (install setting, default staging.<domain>).
33
+ const stagingHost = cfg.get("stagingDomain") ?? `staging.${domain}`;
32
34
  const ownerEmail = cfg.require("ownerEmail");
33
35
  const caeloDir = cfg.get("caeloDir") ?? "./.caelo";
34
36
  // #553 — same knob as every adapter; undefined = stack-contract default.
@@ -81,7 +83,7 @@ const caddyConf = generateCaddyfile({
81
83
  domains: [
82
84
  { hostname: domain, kind: "admin", env: "production" as const },
83
85
  { hostname: domain, kind: "public", env: "production" as const },
84
- { hostname: `staging.${domain}`, kind: "public", env: "staging" as const },
86
+ { hostname: stagingHost, kind: "public", env: "staging" as const },
85
87
  ],
86
88
  });
87
89