@caelo-cms/provisioning 0.1.0
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 +18 -0
- package/caddy/Caddyfile.staging +21 -0
- package/package.json +27 -0
- package/src/adapter.ts +103 -0
- package/src/bootstrap-token.ts +20 -0
- package/src/caddy.ts +93 -0
- package/src/cdn-copy.ts +84 -0
- package/src/cli.ts +674 -0
- package/src/compose.ts +123 -0
- package/src/index.test.ts +246 -0
- package/src/index.ts +52 -0
- package/src/redirects-emit.ts +166 -0
- package/stacks/aws/Pulumi.yaml +39 -0
- package/stacks/aws/README.md +80 -0
- package/stacks/aws/build-edge.ts +80 -0
- package/stacks/aws/edge-handler-bundle.js +21 -0
- package/stacks/aws/edge-handler.ts +87 -0
- package/stacks/aws/index.ts +412 -0
- package/stacks/azure/Pulumi.yaml +37 -0
- package/stacks/azure/README.md +69 -0
- package/stacks/azure/edge-handler.ts +88 -0
- package/stacks/azure/index.ts +309 -0
- package/stacks/gcp/Pulumi.yaml +36 -0
- package/stacks/gcp/README.md +78 -0
- package/stacks/gcp/edge-handler.ts +106 -0
- package/stacks/gcp/index.ts +483 -0
- package/stacks/self-hosted/Pulumi.yaml +27 -0
- package/stacks/self-hosted/README.md +43 -0
- package/stacks/self-hosted/index.ts +117 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MPL-2.0
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Caelo self-hosted Pulumi entry. Composes three resources:
|
|
5
|
+
* 1. `caeloConfigFile` — the generated docker-compose.yml + Caddyfile,
|
|
6
|
+
* written via local.Command on `pulumi up` and torn down on
|
|
7
|
+
* `pulumi destroy`.
|
|
8
|
+
* 2. `caeloBootstrapToken` — a single-use 24h owner-bootstrap token
|
|
9
|
+
* written to `<caeloDir>/pending-token.json`. Captured as a Pulumi
|
|
10
|
+
* output so operators can read `pulumi stack output bootstrapUrl`
|
|
11
|
+
* after `pulumi up`.
|
|
12
|
+
* 3. `caeloDockerCompose` — runs `docker compose up -d` and tears down
|
|
13
|
+
* with `docker compose down -v` on destroy.
|
|
14
|
+
*
|
|
15
|
+
* Why thin: Caelo's self-hosted stack is fundamentally a single Compose
|
|
16
|
+
* project. Wrapping it in real Pulumi resources buys lifecycle tracking
|
|
17
|
+
* + the same CLI shape we'll use for GCP / AWS / Azure in P15, without
|
|
18
|
+
* forcing operators to learn a separate provisioning DSL. The
|
|
19
|
+
* cms-provision CLI stays as the dev-iteration path; Pulumi owns the
|
|
20
|
+
* production install.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { resolve } from "node:path";
|
|
24
|
+
import { local } from "@pulumi/command";
|
|
25
|
+
import * as pulumi from "@pulumi/pulumi";
|
|
26
|
+
import { generateBootstrapToken } from "../../src/bootstrap-token.js";
|
|
27
|
+
import { generateCaddyfile } from "../../src/caddy.js";
|
|
28
|
+
import { generateDockerCompose } from "../../src/compose.js";
|
|
29
|
+
|
|
30
|
+
const cfg = new pulumi.Config();
|
|
31
|
+
const domain = cfg.require("domain");
|
|
32
|
+
const ownerEmail = cfg.require("ownerEmail");
|
|
33
|
+
const caeloDir = cfg.get("caeloDir") ?? "./.caelo";
|
|
34
|
+
|
|
35
|
+
const composePath = resolve(caeloDir, "docker-compose.yml");
|
|
36
|
+
const caddyPath = resolve(caeloDir, "Caddyfile");
|
|
37
|
+
const tokenPath = resolve(caeloDir, "pending-token.json");
|
|
38
|
+
|
|
39
|
+
// Mint secrets at preview time. Pulumi's secret-handling encrypts these
|
|
40
|
+
// in the state file; `pulumi stack output --show-secrets postgresPassword`
|
|
41
|
+
// is the operator's recovery path.
|
|
42
|
+
const postgresPassword = pulumi.secret(randomHex(32));
|
|
43
|
+
const minioRootUser = "caelo";
|
|
44
|
+
const minioRootPassword = pulumi.secret(randomHex(32));
|
|
45
|
+
|
|
46
|
+
const composeYaml = pulumi.all([postgresPassword, minioRootPassword]).apply(([pgPw, minioPw]) =>
|
|
47
|
+
generateDockerCompose({
|
|
48
|
+
domain,
|
|
49
|
+
postgresPassword: pgPw,
|
|
50
|
+
minioRootUser,
|
|
51
|
+
minioRootPassword: minioPw,
|
|
52
|
+
anthropicApiKey: process.env.ANTHROPIC_API_KEY,
|
|
53
|
+
resendApiKey: process.env.RESEND_API_KEY,
|
|
54
|
+
diskSize: "20Gi",
|
|
55
|
+
}),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const caddyConf = generateCaddyfile({
|
|
59
|
+
ownerEmail,
|
|
60
|
+
publicSiteRoot: "/srv/caelo/output/production/current",
|
|
61
|
+
stagingSiteRoot: "/srv/caelo/output/staging/current",
|
|
62
|
+
adminPort: 5173,
|
|
63
|
+
gatewayPort: 8090,
|
|
64
|
+
domains: [
|
|
65
|
+
{ hostname: domain, kind: "admin", env: "production" as const },
|
|
66
|
+
{ hostname: domain, kind: "public", env: "production" as const },
|
|
67
|
+
{ hostname: `staging.${domain}`, kind: "public", env: "staging" as const },
|
|
68
|
+
],
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
// Mint the owner bootstrap token at preview time. Captured below as a
|
|
72
|
+
// Pulumi output so the URL surfaces on `pulumi up`.
|
|
73
|
+
const tokenInfo = generateBootstrapToken();
|
|
74
|
+
|
|
75
|
+
// Stage all three files via a single local.Command so a destroy
|
|
76
|
+
// removes them transactionally.
|
|
77
|
+
const writeFiles = new local.Command("caeloConfigFile", {
|
|
78
|
+
create: pulumi.interpolate`mkdir -p ${caeloDir} && cat > ${composePath} <<'EOF'
|
|
79
|
+
${composeYaml}
|
|
80
|
+
EOF
|
|
81
|
+
cat > ${caddyPath} <<'EOF'
|
|
82
|
+
${caddyConf}
|
|
83
|
+
EOF
|
|
84
|
+
cat > ${tokenPath} <<'EOF'
|
|
85
|
+
${JSON.stringify(tokenInfo, null, 2)}
|
|
86
|
+
EOF
|
|
87
|
+
`,
|
|
88
|
+
delete: pulumi.interpolate`rm -f ${composePath} ${caddyPath} ${tokenPath}`,
|
|
89
|
+
triggers: [composeYaml, caddyConf],
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Bring the stack up. Triggers on the file content so a `pulumi up`
|
|
93
|
+
// after a config change re-runs `up -d` (which is idempotent).
|
|
94
|
+
const composeUp = new local.Command(
|
|
95
|
+
"caeloDockerCompose",
|
|
96
|
+
{
|
|
97
|
+
create: pulumi.interpolate`docker compose -f ${composePath} up -d`,
|
|
98
|
+
delete: pulumi.interpolate`docker compose -f ${composePath} down -v`,
|
|
99
|
+
triggers: [composeYaml],
|
|
100
|
+
},
|
|
101
|
+
{ dependsOn: [writeFiles] },
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
export const composeFilePath = composePath;
|
|
105
|
+
export const caddyFilePath = caddyPath;
|
|
106
|
+
export const bootstrapUrl = pulumi.interpolate`https://${domain}/setup?token=${tokenInfo.token}`;
|
|
107
|
+
export const bootstrapTokenExpiresAt = tokenInfo.expiresAt;
|
|
108
|
+
export { minioRootPassword, postgresPassword };
|
|
109
|
+
|
|
110
|
+
// Re-export so the destroy summary shows operators what they'll lose.
|
|
111
|
+
export const composeRunOutput = composeUp.stdout;
|
|
112
|
+
|
|
113
|
+
function randomHex(bytes: number): string {
|
|
114
|
+
const buf = new Uint8Array(bytes);
|
|
115
|
+
crypto.getRandomValues(buf);
|
|
116
|
+
return [...buf].map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
117
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"extends": "../../tsconfig.base.json",
|
|
3
|
+
"compilerOptions": {
|
|
4
|
+
"composite": true,
|
|
5
|
+
"rootDir": "./src",
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"noEmit": false
|
|
8
|
+
},
|
|
9
|
+
"include": ["src/**/*"],
|
|
10
|
+
"references": [
|
|
11
|
+
{
|
|
12
|
+
"path": "../shared"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"exclude": ["**/*.test.ts", "**/dist", "**/node_modules"]
|
|
16
|
+
}
|