@caelo-cms/provisioning 0.10.49 → 0.10.50
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/caddy/Caddyfile.production +4 -0
- package/caddy/Caddyfile.staging +4 -0
- package/dist/agent-db.js +4 -0
- package/dist/caddy-redirects.d.ts +15 -0
- package/dist/caddy-redirects.d.ts.map +1 -0
- package/dist/caddy-redirects.js +65 -0
- package/dist/caddy-redirects.js.map +1 -0
- package/dist/caddy.d.ts +22 -2
- package/dist/caddy.d.ts.map +1 -1
- package/dist/caddy.js +66 -17
- package/dist/caddy.js.map +1 -1
- package/dist/cli-routing.d.ts.map +1 -1
- package/dist/cli-routing.js +7 -0
- package/dist/cli-routing.js.map +1 -1
- package/dist/cli.js +248 -254
- package/dist/cli.js.map +1 -1
- package/dist/compose.d.ts +54 -2
- package/dist/compose.d.ts.map +1 -1
- package/dist/compose.js +86 -32
- package/dist/compose.js.map +1 -1
- package/dist/gcp-names.d.ts +12 -0
- package/dist/gcp-names.d.ts.map +1 -1
- package/dist/gcp-names.js +21 -0
- package/dist/gcp-names.js.map +1 -1
- package/dist/gcp-url-map.d.ts +27 -0
- package/dist/gcp-url-map.d.ts.map +1 -0
- package/dist/gcp-url-map.js +62 -0
- package/dist/gcp-url-map.js.map +1 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/index.js.map +1 -1
- package/dist/infra-contract.d.ts +20 -1
- package/dist/infra-contract.d.ts.map +1 -1
- package/dist/infra-contract.js +39 -1
- package/dist/infra-contract.js.map +1 -1
- package/dist/public-role-isolation.d.ts +19 -0
- package/dist/public-role-isolation.d.ts.map +1 -0
- package/dist/public-role-isolation.js +69 -0
- package/dist/public-role-isolation.js.map +1 -0
- package/dist/release-contract.d.ts.map +1 -1
- package/dist/release-contract.js +3 -0
- package/dist/release-contract.js.map +1 -1
- package/dist/release-manifest.d.ts.map +1 -1
- package/dist/release-manifest.js +2 -1
- package/dist/release-manifest.js.map +1 -1
- package/dist/self-hosted-backup.d.ts +86 -0
- package/dist/self-hosted-backup.d.ts.map +1 -0
- package/dist/self-hosted-backup.js +350 -0
- package/dist/self-hosted-backup.js.map +1 -0
- package/dist/self-hosted-release.d.ts +12 -3
- package/dist/self-hosted-release.d.ts.map +1 -1
- package/dist/self-hosted-release.js +6 -4
- package/dist/self-hosted-release.js.map +1 -1
- package/dist/self-hosted-start.d.ts +50 -0
- package/dist/self-hosted-start.d.ts.map +1 -0
- package/dist/self-hosted-start.js +59 -0
- package/dist/self-hosted-start.js.map +1 -0
- package/dist/self-hosted-upgrade.d.ts +30 -0
- package/dist/self-hosted-upgrade.d.ts.map +1 -1
- package/dist/self-hosted-upgrade.js +29 -7
- package/dist/self-hosted-upgrade.js.map +1 -1
- package/dist/tar-entries.d.ts +14 -0
- package/dist/tar-entries.d.ts.map +1 -0
- package/dist/tar-entries.js +104 -0
- package/dist/tar-entries.js.map +1 -0
- package/dist/upgrade/converge.d.ts +2 -2
- package/dist/upgrade/converge.d.ts.map +1 -1
- package/dist/upgrade/converge.js +44 -2
- package/dist/upgrade/converge.js.map +1 -1
- package/dist/upgrader-assess.d.ts.map +1 -1
- package/dist/upgrader-assess.js +2 -1
- package/dist/upgrader-assess.js.map +1 -1
- package/dist/url-map-redirects-access.d.ts +15 -0
- package/dist/url-map-redirects-access.d.ts.map +1 -0
- package/dist/url-map-redirects-access.js +81 -0
- package/dist/url-map-redirects-access.js.map +1 -0
- package/dist/wizard.d.ts.map +1 -1
- package/dist/wizard.js +3 -1
- package/dist/wizard.js.map +1 -1
- package/dist/wizards/gcp.d.ts.map +1 -1
- package/dist/wizards/gcp.js +45 -0
- package/dist/wizards/gcp.js.map +1 -1
- package/package.json +2 -2
- package/src/admin-memory.test.ts +2 -2
- package/src/caddy-redirects.docker.test.ts +255 -0
- package/src/caddy-redirects.test.ts +115 -0
- package/src/caddy-redirects.ts +68 -0
- package/src/caddy-validate.test.ts +119 -0
- package/src/caddy.test.ts +108 -0
- package/src/caddy.ts +89 -20
- package/src/cli-routing.test.ts +12 -0
- package/src/cli-routing.ts +7 -0
- package/src/cli.ts +299 -287
- package/src/compose.ts +106 -34
- package/src/gcp-names.ts +23 -0
- package/src/gcp-url-map.test.ts +150 -0
- package/src/gcp-url-map.ts +81 -0
- package/src/index.test.ts +18 -146
- package/src/index.ts +0 -9
- package/src/infra-contract.test.ts +39 -0
- package/src/infra-contract.ts +42 -1
- package/src/public-role-isolation.test.ts +89 -0
- package/src/public-role-isolation.ts +83 -0
- package/src/release-contract.ts +3 -0
- package/src/release-manifest.test.ts +12 -0
- package/src/release-manifest.ts +2 -1
- package/src/self-hosted-backup.test.ts +344 -0
- package/src/self-hosted-backup.ts +480 -0
- package/src/self-hosted-compose.test.ts +88 -4
- package/src/self-hosted-release.ts +22 -7
- package/src/self-hosted-start.test.ts +68 -0
- package/src/self-hosted-start.ts +123 -0
- package/src/self-hosted-upgrade.ts +35 -8
- package/src/stack-contract.test.ts +2 -2
- package/src/tar-entries.test.ts +52 -0
- package/src/tar-entries.ts +116 -0
- package/src/upgrade/converge.ts +51 -4
- package/src/upgrade/upgrade.test.ts +78 -1
- package/src/upgrader-assess.test.ts +22 -0
- package/src/upgrader-assess.ts +2 -1
- package/src/url-map-redirects-access.test.ts +88 -0
- package/src/url-map-redirects-access.ts +89 -0
- package/src/wizard.ts +3 -1
- package/src/wizards/gcp.ts +48 -0
- package/stacks/aws/index.ts +34 -2
- package/stacks/azure/index.ts +13 -0
- package/stacks/gcp/index.ts +24 -17
- package/stacks/gcp-firebase/index.ts +7 -0
- package/stacks/self-hosted/Pulumi.yaml +2 -2
- package/stacks/self-hosted/README.md +3 -3
- package/stacks/self-hosted/index.ts +45 -22
- package/dist/redirects-emit.d.ts +0 -52
- package/dist/redirects-emit.d.ts.map +0 -1
- package/dist/redirects-emit.js +0 -111
- package/dist/redirects-emit.js.map +0 -1
- package/src/redirects-emit.ts +0 -175
package/src/caddy.ts
CHANGED
|
@@ -5,18 +5,32 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Reads the `domains` table + the deploy targets and emits a
|
|
7
7
|
* deterministic Caddyfile string. The CLI's `regenerate-caddy`
|
|
8
|
-
* sub-command writes this to
|
|
8
|
+
* sub-command writes this to `.caelo/Caddyfile` and runs
|
|
9
9
|
* `caddy reload`.
|
|
10
10
|
*
|
|
11
11
|
* Per-vhost shape:
|
|
12
12
|
* <hostname> {
|
|
13
|
-
* # admin → reverse_proxy
|
|
14
|
-
* # public →
|
|
15
|
-
*
|
|
13
|
+
* # admin → reverse_proxy <adminUpstream>
|
|
14
|
+
* # public → /api/* → <gatewayUpstream>; everything else is the
|
|
15
|
+
* # static site (try_files + file_server, 404 → /404.html)
|
|
16
16
|
* }
|
|
17
17
|
*
|
|
18
|
+
* Upstreams are `host:port` as seen FROM CADDY. In the Compose install
|
|
19
|
+
* Caddy runs in its own container, where `localhost` is Caddy itself, so
|
|
20
|
+
* the CLI passes the Compose service names (`caelo-admin:5173`,
|
|
21
|
+
* `caelo-gateway:8090`) — issue #707.
|
|
22
|
+
*
|
|
23
|
+
* Every directive here must be one Caddy accepts: the output is checked
|
|
24
|
+
* with `caddy validate` on the pinned Caddy image (caddy-validate.test.ts,
|
|
25
|
+
* run in CI) — issue #566 shipped a `try_files` nested inside
|
|
26
|
+
* `file_server { }`, which Caddy 2.11 refuses, so no vhost came up.
|
|
27
|
+
*
|
|
18
28
|
* Staging vhosts force `X-Robots-Tag: noindex`.
|
|
19
29
|
*
|
|
30
|
+
* Public vhosts import the redirect snippet of the build they serve
|
|
31
|
+
* (`_redirects.caddy`, #587); the Caddy container reloads itself when a
|
|
32
|
+
* deploy changes it (caddy-redirects.ts).
|
|
33
|
+
*
|
|
20
34
|
* Public vhosts mark content-hashed build outputs (fonts, plugin
|
|
21
35
|
* bundles — `CONTENT_HASHED_PATH_PATTERN` in @caelo-cms/shared)
|
|
22
36
|
* `immutable` for a year. Everything else keeps Caddy's file_server
|
|
@@ -26,13 +40,22 @@
|
|
|
26
40
|
* hashed URL (an old page requesting an asset the new build dropped)
|
|
27
41
|
* is never cached for a year.
|
|
28
42
|
*
|
|
43
|
+
* A path that matches no file is a real 404 serving the generator's
|
|
44
|
+
* `/404.html` — never the home page with a 200 (an SPA-style
|
|
45
|
+
* `/index.html` fallback would turn every dead URL into a soft 404).
|
|
46
|
+
* Same behaviour as the GCS / S3 / Blob adapters (CLAUDE.md §11.B).
|
|
47
|
+
*
|
|
29
48
|
* `.ico` files (theme favicons) are served as `image/x-icon`, the one
|
|
30
49
|
* icon MIME the media library stores and `<link rel="icon" type>`
|
|
31
50
|
* declares. file_server only fills Content-Type when no earlier handler
|
|
32
51
|
* set it, so the `header` line wins over the host's mime.types name.
|
|
33
52
|
*/
|
|
34
53
|
|
|
35
|
-
import {
|
|
54
|
+
import {
|
|
55
|
+
CONTENT_HASHED_PATH_PATTERN,
|
|
56
|
+
caddyRedirectsImport,
|
|
57
|
+
IMMUTABLE_CACHE_CONTROL,
|
|
58
|
+
} from "@caelo-cms/shared";
|
|
36
59
|
|
|
37
60
|
export interface CaddyDomainSpec {
|
|
38
61
|
readonly hostname: string;
|
|
@@ -40,21 +63,64 @@ export interface CaddyDomainSpec {
|
|
|
40
63
|
readonly env: "production" | "staging";
|
|
41
64
|
}
|
|
42
65
|
|
|
66
|
+
/**
|
|
67
|
+
* How Caddy gets certificates: `acme` — publicly trusted ones from Let's
|
|
68
|
+
* Encrypt / ZeroSSL (needs public DNS + ports 80/443 reachable); `internal`
|
|
69
|
+
* — Caddy's own local CA, for a LAN or evaluation install without public
|
|
70
|
+
* DNS (browsers warn until the CA root is trusted).
|
|
71
|
+
*/
|
|
72
|
+
export type CaddyTlsMode = "acme" | "internal";
|
|
73
|
+
|
|
43
74
|
export interface CaddyfileSpec {
|
|
44
75
|
readonly ownerEmail: string;
|
|
45
|
-
readonly
|
|
76
|
+
readonly tls: CaddyTlsMode;
|
|
77
|
+
readonly publicSiteRoot: string; // absolute path inside the Caddy container
|
|
46
78
|
readonly stagingSiteRoot: string;
|
|
47
|
-
|
|
48
|
-
readonly
|
|
79
|
+
/** `host:port` of the admin as Caddy reaches it (Compose: `caelo-admin:5173`). */
|
|
80
|
+
readonly adminUpstream: string;
|
|
81
|
+
/** `host:port` of the API gateway as Caddy reaches it (Compose: `caelo-gateway:8090`). */
|
|
82
|
+
readonly gatewayUpstream: string;
|
|
49
83
|
readonly domains: ReadonlyArray<CaddyDomainSpec>;
|
|
50
84
|
}
|
|
51
85
|
|
|
86
|
+
/** The host the admin of a self-hosted install answers on (same convention as GCP). */
|
|
87
|
+
export function selfHostedAdminHost(domain: string): string {
|
|
88
|
+
return `admin.${domain}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* The vhosts every self-hosted install serves before an Owner adds domains
|
|
93
|
+
* in the admin: the admin on `admin.<domain>`, the live site on `<domain>`,
|
|
94
|
+
* the staging site on its own host. The admin needs its own host — it
|
|
95
|
+
* routes from `/` (`/setup`, `/login`, …), so it cannot share one with the
|
|
96
|
+
* public site, and Caddy refuses two site blocks for the same address.
|
|
97
|
+
*/
|
|
98
|
+
export function selfHostedSeedDomains(domain: string, stagingDomain: string): CaddyDomainSpec[] {
|
|
99
|
+
return [
|
|
100
|
+
{ hostname: selfHostedAdminHost(domain), kind: "admin", env: "production" },
|
|
101
|
+
{ hostname: domain, kind: "public", env: "production" },
|
|
102
|
+
{ hostname: stagingDomain, kind: "public", env: "staging" },
|
|
103
|
+
];
|
|
104
|
+
}
|
|
105
|
+
|
|
52
106
|
export function generateCaddyfile(spec: CaddyfileSpec): string {
|
|
107
|
+
const hostnames = new Set<string>();
|
|
108
|
+
for (const d of spec.domains) {
|
|
109
|
+
if (hostnames.has(d.hostname)) {
|
|
110
|
+
// Caddy refuses a config with two site blocks for one address, so a
|
|
111
|
+
// duplicate would take every vhost down, not just this one.
|
|
112
|
+
throw new Error(
|
|
113
|
+
`Caddyfile: ${d.hostname} is listed twice — one host serves either the admin or a public site, not both.`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
hostnames.add(d.hostname);
|
|
117
|
+
}
|
|
118
|
+
|
|
53
119
|
const blocks: string[] = [];
|
|
54
120
|
|
|
55
121
|
// Global options.
|
|
56
122
|
blocks.push(`{
|
|
57
|
-
email ${spec.ownerEmail}
|
|
123
|
+
email ${spec.ownerEmail}${spec.tls === "internal" ? "\n local_certs" : ""}
|
|
58
124
|
}
|
|
59
125
|
`);
|
|
60
126
|
|
|
@@ -68,7 +134,7 @@ export function generateCaddyfile(spec: CaddyfileSpec): string {
|
|
|
68
134
|
blocks.push(`# No domains configured yet — cms-provision regenerate-caddy
|
|
69
135
|
# will overwrite this file when you add one at /settings/domains.
|
|
70
136
|
:8081 {
|
|
71
|
-
reverse_proxy
|
|
137
|
+
reverse_proxy ${spec.adminUpstream}
|
|
72
138
|
}
|
|
73
139
|
`);
|
|
74
140
|
}
|
|
@@ -80,15 +146,18 @@ function vhost(d: CaddyDomainSpec, spec: CaddyfileSpec): string {
|
|
|
80
146
|
const noindex = d.env === "staging" ? `\n header X-Robots-Tag "noindex"` : "";
|
|
81
147
|
if (d.kind === "admin") {
|
|
82
148
|
return `${d.hostname} {${noindex}
|
|
83
|
-
reverse_proxy
|
|
149
|
+
reverse_proxy ${spec.adminUpstream}
|
|
84
150
|
}
|
|
85
151
|
`;
|
|
86
152
|
}
|
|
87
|
-
// public — API routes go to the gateway,
|
|
88
|
-
//
|
|
153
|
+
// public — API routes go to the gateway, the rest is the static site.
|
|
154
|
+
// `try_files` is its own directive (a rewrite), never a file_server
|
|
155
|
+
// subdirective (#566).
|
|
89
156
|
const root = d.env === "staging" ? spec.stagingSiteRoot : spec.publicSiteRoot;
|
|
157
|
+
// #587 — the redirects table of the build this vhost serves.
|
|
90
158
|
return `${d.hostname} {${noindex}
|
|
91
159
|
root * ${root}
|
|
160
|
+
${caddyRedirectsImport(root)}
|
|
92
161
|
@content_hashed {
|
|
93
162
|
path_regexp content_hashed ${CONTENT_HASHED_PATH_PATTERN}
|
|
94
163
|
file
|
|
@@ -97,15 +166,15 @@ function vhost(d: CaddyDomainSpec, spec: CaddyfileSpec): string {
|
|
|
97
166
|
@ico path *.ico
|
|
98
167
|
header @ico Content-Type "image/x-icon"
|
|
99
168
|
handle /api/* {
|
|
100
|
-
reverse_proxy
|
|
101
|
-
}
|
|
102
|
-
handle /admin/* {
|
|
103
|
-
reverse_proxy localhost:${spec.adminPort}
|
|
169
|
+
reverse_proxy ${spec.gatewayUpstream}
|
|
104
170
|
}
|
|
105
171
|
handle {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
172
|
+
try_files {path} {path}/index.html
|
|
173
|
+
file_server
|
|
174
|
+
}
|
|
175
|
+
handle_errors 404 {
|
|
176
|
+
rewrite * /404.html
|
|
177
|
+
file_server
|
|
109
178
|
}
|
|
110
179
|
}
|
|
111
180
|
`;
|
package/src/cli-routing.test.ts
CHANGED
|
@@ -13,6 +13,18 @@ describe("resolveCliRoute", () => {
|
|
|
13
13
|
).toEqual({ kind: "wizard" });
|
|
14
14
|
});
|
|
15
15
|
|
|
16
|
+
it("routes the self-hosted one-command install to init (#707)", () => {
|
|
17
|
+
expect(
|
|
18
|
+
resolveCliRoute(
|
|
19
|
+
argv("--provider", "self-hosted", "--domain", "example.com", "--owner-email", "o@x.y"),
|
|
20
|
+
HANDLERS,
|
|
21
|
+
),
|
|
22
|
+
).toEqual({ kind: "handler", name: "init" });
|
|
23
|
+
expect(resolveCliRoute(argv("--provider", "self-hosted", "--help"), HANDLERS)).toEqual({
|
|
24
|
+
kind: "usage",
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
16
28
|
it("routes a bare invocation to the wizard", () => {
|
|
17
29
|
expect(resolveCliRoute(argv(), HANDLERS)).toEqual({ kind: "wizard" });
|
|
18
30
|
});
|
package/src/cli-routing.ts
CHANGED
|
@@ -30,6 +30,13 @@ export function resolveCliRoute(argv: readonly string[], handlerNames: Iterable<
|
|
|
30
30
|
if (argv.includes("--no-wizard") || argv.includes("--help") || argv.includes("-h")) {
|
|
31
31
|
return { kind: "usage" };
|
|
32
32
|
}
|
|
33
|
+
// #707 — `--provider self-hosted --domain … --owner-email …` is the
|
|
34
|
+
// self-hosted one-command install: `init` writes `.caelo/` and starts the
|
|
35
|
+
// stack. (The cloud wizard has nothing to provision for it.)
|
|
36
|
+
const provider = argv.indexOf("--provider");
|
|
37
|
+
if (provider !== -1 && argv[provider + 1] === "self-hosted") {
|
|
38
|
+
return { kind: "handler", name: "init" };
|
|
39
|
+
}
|
|
33
40
|
return { kind: "wizard" };
|
|
34
41
|
}
|
|
35
42
|
|