@supatype/cli 0.1.0-alpha.6 → 0.1.0-alpha.8
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/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +208 -1
- package/.turbo/turbo-typecheck.log +1 -1
- package/dist/app/proxy-dev-app.d.ts +13 -0
- package/dist/app/proxy-dev-app.d.ts.map +1 -0
- package/dist/app/proxy-dev-app.js +53 -0
- package/dist/app/proxy-dev-app.js.map +1 -0
- package/dist/app-config.d.ts +7 -0
- package/dist/app-config.d.ts.map +1 -0
- package/dist/app-config.js +113 -0
- package/dist/app-config.js.map +1 -0
- package/dist/augmentation-generator.d.ts +2 -0
- package/dist/augmentation-generator.d.ts.map +1 -0
- package/dist/augmentation-generator.js +111 -0
- package/dist/augmentation-generator.js.map +1 -0
- package/dist/binary-cache.d.ts +94 -0
- package/dist/binary-cache.d.ts.map +1 -0
- package/dist/binary-cache.js +669 -0
- package/dist/binary-cache.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +13 -7
- package/dist/cli.js.map +1 -1
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +4 -3
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/app.d.ts.map +1 -1
- package/dist/commands/app.js +56 -209
- package/dist/commands/app.js.map +1 -1
- package/dist/commands/cache.d.ts +6 -0
- package/dist/commands/cache.d.ts.map +1 -0
- package/dist/commands/cache.js +105 -0
- package/dist/commands/cache.js.map +1 -0
- package/dist/commands/cloud.d.ts +20 -0
- package/dist/commands/cloud.d.ts.map +1 -1
- package/dist/commands/cloud.js +50 -52
- package/dist/commands/cloud.js.map +1 -1
- package/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +47 -54
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/deploy.d.ts +2 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +79 -52
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts +11 -0
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +759 -385
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +30 -15
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/engine.d.ts +1 -3
- package/dist/commands/engine.d.ts.map +1 -1
- package/dist/commands/engine.js +13 -85
- package/dist/commands/engine.js.map +1 -1
- package/dist/commands/functions.d.ts.map +1 -1
- package/dist/commands/functions.js +92 -105
- package/dist/commands/functions.js.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +22 -12
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +137 -410
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/migrate-from-v1.d.ts +5 -0
- package/dist/commands/migrate-from-v1.d.ts.map +1 -0
- package/dist/commands/migrate-from-v1.js +125 -0
- package/dist/commands/migrate-from-v1.js.map +1 -0
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +27 -23
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/pg.d.ts +8 -0
- package/dist/commands/pg.d.ts.map +1 -0
- package/dist/commands/pg.js +102 -0
- package/dist/commands/pg.js.map +1 -0
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +5 -66
- package/dist/commands/pull.js.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +128 -38
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/seed.d.ts +2 -0
- package/dist/commands/seed.d.ts.map +1 -1
- package/dist/commands/seed.js +44 -11
- package/dist/commands/seed.js.map +1 -1
- package/dist/commands/self-host.d.ts +7 -1
- package/dist/commands/self-host.d.ts.map +1 -1
- package/dist/commands/self-host.js +272 -758
- package/dist/commands/self-host.js.map +1 -1
- package/dist/commands/self-update.d.ts +9 -0
- package/dist/commands/self-update.d.ts.map +1 -0
- package/dist/commands/self-update.js +33 -0
- package/dist/commands/self-update.js.map +1 -0
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +4 -3
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/types.d.ts +3 -0
- package/dist/commands/types.d.ts.map +1 -0
- package/dist/commands/types.js +62 -0
- package/dist/commands/types.js.map +1 -0
- package/dist/commands/update.d.ts +7 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +93 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/components.d.ts +5 -0
- package/dist/components.d.ts.map +1 -0
- package/dist/components.js +3 -0
- package/dist/components.js.map +1 -0
- package/dist/config.d.ts +10 -51
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +101 -33
- package/dist/config.js.map +1 -1
- package/dist/dev-compose.d.ts +17 -0
- package/dist/dev-compose.d.ts.map +1 -0
- package/dist/dev-compose.js +374 -0
- package/dist/dev-compose.js.map +1 -0
- package/dist/diff-output.d.ts +4 -0
- package/dist/diff-output.d.ts.map +1 -0
- package/dist/diff-output.js +12 -0
- package/dist/diff-output.js.map +1 -0
- package/dist/docker-postgres.d.ts +57 -0
- package/dist/docker-postgres.d.ts.map +1 -0
- package/dist/docker-postgres.js +208 -0
- package/dist/docker-postgres.js.map +1 -0
- package/dist/engine-client.d.ts +69 -0
- package/dist/engine-client.d.ts.map +1 -0
- package/dist/engine-client.js +157 -0
- package/dist/engine-client.js.map +1 -0
- package/dist/ensure-binary.d.ts +7 -0
- package/dist/ensure-binary.d.ts.map +1 -0
- package/dist/ensure-binary.js +17 -0
- package/dist/ensure-binary.js.map +1 -0
- package/dist/functions-router-gen.d.ts +14 -0
- package/dist/functions-router-gen.d.ts.map +1 -0
- package/dist/functions-router-gen.js +199 -0
- package/dist/functions-router-gen.js.map +1 -0
- package/dist/index.d.ts +4 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -3
- package/dist/index.js.map +1 -1
- package/dist/kong-config.d.ts +25 -0
- package/dist/kong-config.d.ts.map +1 -0
- package/dist/kong-config.js +71 -0
- package/dist/kong-config.js.map +1 -0
- package/dist/local-gateway.d.ts +7 -0
- package/dist/local-gateway.d.ts.map +1 -0
- package/dist/local-gateway.js +9 -0
- package/dist/local-gateway.js.map +1 -0
- package/dist/local-storage.d.ts +8 -0
- package/dist/local-storage.d.ts.map +1 -0
- package/dist/local-storage.js +14 -0
- package/dist/local-storage.js.map +1 -0
- package/dist/pgbouncer-userlist.d.ts +5 -0
- package/dist/pgbouncer-userlist.d.ts.map +1 -0
- package/dist/pgbouncer-userlist.js +14 -0
- package/dist/pgbouncer-userlist.js.map +1 -0
- package/dist/postgres-ctl.d.ts +44 -0
- package/dist/postgres-ctl.d.ts.map +1 -0
- package/dist/postgres-ctl.js +137 -0
- package/dist/postgres-ctl.js.map +1 -0
- package/dist/process-manager.d.ts +43 -0
- package/dist/process-manager.d.ts.map +1 -0
- package/dist/process-manager.js +135 -0
- package/dist/process-manager.js.map +1 -0
- package/dist/project-config.d.ts +235 -0
- package/dist/project-config.d.ts.map +1 -0
- package/dist/project-config.js +160 -0
- package/dist/project-config.js.map +1 -0
- package/dist/pull-utils.d.ts +15 -0
- package/dist/pull-utils.d.ts.map +1 -1
- package/dist/pull-utils.js +12 -0
- package/dist/pull-utils.js.map +1 -1
- package/dist/release-pins.d.ts +7 -0
- package/dist/release-pins.d.ts.map +1 -0
- package/dist/release-pins.js +27 -0
- package/dist/release-pins.js.map +1 -0
- package/dist/release-public-key.d.ts +8 -0
- package/dist/release-public-key.d.ts.map +1 -0
- package/dist/release-public-key.js +13 -0
- package/dist/release-public-key.js.map +1 -0
- package/dist/runtime-routes.d.ts +34 -0
- package/dist/runtime-routes.d.ts.map +1 -0
- package/dist/runtime-routes.js +252 -0
- package/dist/runtime-routes.js.map +1 -0
- package/dist/schema-ast-v2.d.ts +127 -0
- package/dist/schema-ast-v2.d.ts.map +1 -0
- package/dist/schema-ast-v2.js +226 -0
- package/dist/schema-ast-v2.js.map +1 -0
- package/dist/scripts/postinstall.d.ts +5 -6
- package/dist/scripts/postinstall.d.ts.map +1 -1
- package/dist/scripts/postinstall.js +36 -20
- package/dist/scripts/postinstall.js.map +1 -1
- package/dist/self-host-compose.d.ts +22 -0
- package/dist/self-host-compose.d.ts.map +1 -0
- package/dist/self-host-compose.js +347 -0
- package/dist/self-host-compose.js.map +1 -0
- package/dist/storage-provision.d.ts +24 -0
- package/dist/storage-provision.d.ts.map +1 -0
- package/dist/storage-provision.js +44 -0
- package/dist/storage-provision.js.map +1 -0
- package/dist/studio-admin-roles.d.ts +7 -0
- package/dist/studio-admin-roles.d.ts.map +1 -0
- package/dist/studio-admin-roles.js +14 -0
- package/dist/studio-admin-roles.js.map +1 -0
- package/dist/studio-dev-server.d.ts +22 -0
- package/dist/studio-dev-server.d.ts.map +1 -0
- package/dist/studio-dev-server.js +28 -0
- package/dist/studio-dev-server.js.map +1 -0
- package/dist/systemd.d.ts +26 -0
- package/dist/systemd.d.ts.map +1 -0
- package/dist/systemd.js +102 -0
- package/dist/systemd.js.map +1 -0
- package/dist/tsx-runner.d.ts.map +1 -1
- package/dist/tsx-runner.js +9 -2
- package/dist/tsx-runner.js.map +1 -1
- package/dist/type-extractor.d.ts +4 -0
- package/dist/type-extractor.d.ts.map +1 -0
- package/dist/type-extractor.js +1213 -0
- package/dist/type-extractor.js.map +1 -0
- package/dist/type-resolver.d.ts +33 -0
- package/dist/type-resolver.d.ts.map +1 -0
- package/dist/type-resolver.js +338 -0
- package/dist/type-resolver.js.map +1 -0
- package/package.json +4 -3
- package/releases/deno/VERSION +1 -0
- package/scripts/mirror-deno-release.sh +76 -0
- package/src/TYPE-RESOLUTION.md +294 -0
- package/src/app/proxy-dev-app.ts +67 -0
- package/src/app-config.ts +128 -0
- package/src/augmentation-generator.ts +126 -0
- package/src/binary-cache.ts +822 -0
- package/src/cli.ts +13 -8
- package/src/commands/admin.ts +4 -3
- package/src/commands/app.ts +67 -231
- package/src/commands/cache.ts +117 -0
- package/src/commands/cloud.ts +63 -64
- package/src/commands/db.ts +54 -63
- package/src/commands/deploy.ts +96 -62
- package/src/commands/dev.ts +933 -405
- package/src/commands/diff.ts +31 -29
- package/src/commands/engine.ts +13 -116
- package/src/commands/functions.ts +97 -115
- package/src/commands/generate.ts +23 -10
- package/src/commands/init.ts +149 -414
- package/src/commands/migrate-from-v1.ts +131 -0
- package/src/commands/migrate.ts +27 -23
- package/src/commands/pg.ts +133 -0
- package/src/commands/pull.ts +6 -85
- package/src/commands/push.ts +161 -56
- package/src/commands/seed.ts +54 -12
- package/src/commands/self-host.ts +312 -880
- package/src/commands/self-update.ts +45 -0
- package/src/commands/status.ts +4 -3
- package/src/commands/types.ts +76 -0
- package/src/commands/update.ts +109 -0
- package/src/components.ts +6 -0
- package/src/config.ts +127 -94
- package/src/dev-compose.ts +455 -0
- package/src/diff-output.ts +12 -0
- package/src/docker-postgres.ts +295 -0
- package/src/engine-client.ts +236 -0
- package/src/ensure-binary.ts +28 -0
- package/src/functions-router-gen.ts +224 -0
- package/src/index.ts +4 -12
- package/src/kong-config.ts +93 -0
- package/src/local-gateway.ts +9 -0
- package/src/local-storage.ts +14 -0
- package/src/pgbouncer-userlist.ts +15 -0
- package/src/postgres-ctl.ts +171 -0
- package/src/process-manager.ts +168 -0
- package/src/project-config.ts +386 -0
- package/src/pull-utils.ts +24 -0
- package/src/release-pins.ts +31 -0
- package/src/release-public-key.ts +12 -0
- package/src/runtime-routes.ts +291 -0
- package/src/schema-ast-v2.ts +324 -0
- package/src/scripts/postinstall.ts +36 -25
- package/src/self-host-compose.ts +389 -0
- package/src/storage-provision.ts +58 -0
- package/src/studio-admin-roles.ts +16 -0
- package/src/studio-dev-server.ts +53 -0
- package/src/systemd.ts +137 -0
- package/src/tsx-runner.ts +11 -1
- package/src/type-extractor.ts +1479 -0
- package/src/type-resolver.ts +457 -0
- package/tests/app-command.test.ts +54 -0
- package/tests/augmentation-generator.test.ts +59 -0
- package/tests/binary-cache-cloud-overrides.test.ts +123 -0
- package/tests/cached-artifact-format.test.ts +84 -0
- package/tests/cli-help.test.ts +40 -14
- package/tests/config.test.ts +171 -37
- package/tests/docker-postgres.test.ts +39 -0
- package/tests/engine-distribution.test.ts +3 -3
- package/tests/ensure-binary.test.ts +59 -0
- package/tests/init.test.ts +28 -86
- package/tests/migrate-from-v1.test.ts +29 -0
- package/tests/normalize-admin-config.test.ts +48 -0
- package/tests/pg-spawn-env.test.ts +18 -0
- package/tests/postgres-archive-tag.test.ts +9 -0
- package/tests/proxy-dev-app.test.ts +33 -0
- package/tests/pull-utils.test.ts +36 -1
- package/tests/release-pins.test.ts +28 -0
- package/tests/runtime-contract.test.ts +351 -0
- package/tests/seed-discover.test.ts +31 -0
- package/tests/studio-admin-roles.test.ts +27 -0
- package/tests/tsconfig.json +9 -0
- package/tests/type-extractor.test.ts +985 -0
- package/tests/type-resolver.test.ts +59 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/vitest.config.ts +12 -0
- package/dist/engine/cache.d.ts +0 -37
- package/dist/engine/cache.d.ts.map +0 -1
- package/dist/engine/cache.js +0 -121
- package/dist/engine/cache.js.map +0 -1
- package/dist/engine/download.d.ts +0 -19
- package/dist/engine/download.d.ts.map +0 -1
- package/dist/engine/download.js +0 -108
- package/dist/engine/download.js.map +0 -1
- package/dist/engine/platform.d.ts +0 -24
- package/dist/engine/platform.d.ts.map +0 -1
- package/dist/engine/platform.js +0 -50
- package/dist/engine/platform.js.map +0 -1
- package/dist/engine/resolve.d.ts +0 -37
- package/dist/engine/resolve.d.ts.map +0 -1
- package/dist/engine/resolve.js +0 -133
- package/dist/engine/resolve.js.map +0 -1
- package/dist/engine/update-notify.d.ts +0 -11
- package/dist/engine/update-notify.d.ts.map +0 -1
- package/dist/engine/update-notify.js +0 -43
- package/dist/engine/update-notify.js.map +0 -1
- package/dist/engine/verify.d.ts +0 -50
- package/dist/engine/verify.d.ts.map +0 -1
- package/dist/engine/verify.js +0 -161
- package/dist/engine/verify.js.map +0 -1
- package/dist/engine-version.d.ts +0 -35
- package/dist/engine-version.d.ts.map +0 -1
- package/dist/engine-version.js +0 -35
- package/dist/engine-version.js.map +0 -1
- package/dist/engine.d.ts +0 -34
- package/dist/engine.d.ts.map +0 -1
- package/dist/engine.js +0 -76
- package/dist/engine.js.map +0 -1
- package/src/engine/cache.ts +0 -135
- package/src/engine/download.ts +0 -143
- package/src/engine/platform.ts +0 -66
- package/src/engine/resolve.ts +0 -197
- package/src/engine/update-notify.ts +0 -50
- package/src/engine/verify.ts +0 -206
- package/src/engine-version.ts +0 -39
- package/src/engine.ts +0 -99
|
@@ -1,796 +1,310 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* self-host commands — manage self-hosted deployments.
|
|
3
|
+
*
|
|
4
|
+
* Compose-based commands are the canonical path.
|
|
5
|
+
* Native/systemd commands are kept temporarily for migration compatibility.
|
|
6
|
+
*/
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import { existsSync, readFileSync, mkdirSync, writeFileSync } from "node:fs";
|
|
9
|
+
import { join, resolve } from "node:path";
|
|
10
|
+
import { homedir } from "node:os";
|
|
4
11
|
import { spawnSync } from "node:child_process";
|
|
5
|
-
import {
|
|
12
|
+
import { gzipSync } from "node:zlib";
|
|
13
|
+
import { loadConfig } from "../config.js";
|
|
14
|
+
import { connectionString } from "../project-config.js";
|
|
15
|
+
import { resolveBinary } from "../binary-cache.js";
|
|
16
|
+
import { generateUnits } from "../systemd.js";
|
|
17
|
+
import { readPid } from "../process-manager.js";
|
|
18
|
+
import { localStorageEnv } from "../local-storage.js";
|
|
19
|
+
import { runDockerCompose, writeSelfHostCompose } from "../self-host-compose.js";
|
|
6
20
|
export function registerSelfHost(program) {
|
|
7
21
|
const selfHostCmd = program
|
|
8
22
|
.command("self-host")
|
|
9
|
-
.description("Manage self-hosted
|
|
10
|
-
selfHostCmd
|
|
11
|
-
.command("
|
|
12
|
-
.description("
|
|
13
|
-
|
|
14
|
-
.
|
|
15
|
-
.
|
|
16
|
-
.
|
|
17
|
-
.
|
|
18
|
-
|
|
23
|
+
.description("Manage self-hosted deployments (Docker Compose only)");
|
|
24
|
+
const composeCmd = selfHostCmd
|
|
25
|
+
.command("compose")
|
|
26
|
+
.description("Manage compose-based self-host runtime");
|
|
27
|
+
composeCmd
|
|
28
|
+
.command("render")
|
|
29
|
+
.description("Render deterministic self-host compose artifacts")
|
|
30
|
+
.action(() => {
|
|
31
|
+
const cwd = process.cwd();
|
|
32
|
+
const config = loadConfig(cwd);
|
|
33
|
+
const out = writeSelfHostCompose(cwd, config);
|
|
34
|
+
console.log(`Wrote ${out.composePath}`);
|
|
35
|
+
console.log(`Wrote ${out.kongPath}`);
|
|
19
36
|
});
|
|
20
|
-
|
|
37
|
+
composeCmd
|
|
38
|
+
.command("up")
|
|
39
|
+
.description("Render and start compose services")
|
|
40
|
+
.option("-d, --detach", "Start in detached mode", true)
|
|
41
|
+
.action((opts) => {
|
|
42
|
+
const cwd = process.cwd();
|
|
43
|
+
const config = loadConfig(cwd);
|
|
44
|
+
const out = writeSelfHostCompose(cwd, config);
|
|
45
|
+
const status = runDockerCompose(out.composePath, opts.detach ? ["up", "-d"] : ["up"], cwd);
|
|
46
|
+
process.exitCode = status;
|
|
47
|
+
});
|
|
48
|
+
composeCmd
|
|
49
|
+
.command("down")
|
|
50
|
+
.description("Stop compose services")
|
|
51
|
+
.action(() => {
|
|
52
|
+
const cwd = process.cwd();
|
|
53
|
+
const config = loadConfig(cwd);
|
|
54
|
+
const out = writeSelfHostCompose(cwd, config);
|
|
55
|
+
process.exitCode = runDockerCompose(out.composePath, ["down"], cwd);
|
|
56
|
+
});
|
|
57
|
+
composeCmd
|
|
21
58
|
.command("status")
|
|
22
|
-
.description("Show
|
|
59
|
+
.description("Show compose service status")
|
|
23
60
|
.action(() => {
|
|
24
|
-
|
|
61
|
+
const cwd = process.cwd();
|
|
62
|
+
const config = loadConfig(cwd);
|
|
63
|
+
const out = writeSelfHostCompose(cwd, config);
|
|
64
|
+
process.exitCode = runDockerCompose(out.composePath, ["ps"], cwd);
|
|
25
65
|
});
|
|
26
|
-
|
|
66
|
+
composeCmd
|
|
27
67
|
.command("logs")
|
|
28
|
-
.description("Tail logs
|
|
29
|
-
.option("--service <name>", "
|
|
30
|
-
.option("--follow", "Follow log output")
|
|
68
|
+
.description("Tail compose logs")
|
|
69
|
+
.option("--service <name>", "Filter to one service")
|
|
70
|
+
.option("-f, --follow", "Follow log output", true)
|
|
31
71
|
.action((opts) => {
|
|
72
|
+
const cwd = process.cwd();
|
|
73
|
+
const config = loadConfig(cwd);
|
|
74
|
+
const out = writeSelfHostCompose(cwd, config);
|
|
32
75
|
const args = ["logs"];
|
|
33
76
|
if (opts.follow)
|
|
34
|
-
args.push("
|
|
77
|
+
args.push("-f");
|
|
35
78
|
if (opts.service)
|
|
36
79
|
args.push(opts.service);
|
|
37
|
-
runDockerCompose(args,
|
|
38
|
-
});
|
|
39
|
-
selfHostCmd
|
|
40
|
-
.command("backup")
|
|
41
|
-
.description("Create a Postgres dump and store it locally")
|
|
42
|
-
.option("--output <path>", "Output file path", `./backups/backup-${timestamp()}.sql.gz`)
|
|
43
|
-
.action((opts) => {
|
|
44
|
-
backup(process.cwd(), opts.output);
|
|
80
|
+
process.exitCode = runDockerCompose(out.composePath, args, cwd);
|
|
45
81
|
});
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
.
|
|
54
|
-
.description("Safely upgrade services with backup, rolling restart, and automatic rollback")
|
|
55
|
-
.option("--skip-backup", "Skip automatic pre-upgrade backup")
|
|
56
|
-
.option("--skip-migrations", "Skip database migration step")
|
|
82
|
+
// ── Legacy native/systemd helpers (hidden; use compose for self-host) ─────
|
|
83
|
+
const legacyCmd = new Command("native");
|
|
84
|
+
selfHostCmd.addCommand(legacyCmd, { hidden: true });
|
|
85
|
+
legacyCmd
|
|
86
|
+
.command("install-service", "Generate systemd unit files and (on Linux) install + enable them")
|
|
87
|
+
.option("--output-dir <path>", "Write unit files here instead of /etc/systemd/system/")
|
|
88
|
+
.option("--user <name>", "User to run services as")
|
|
89
|
+
.option("--no-enable", "Generate unit files but do not enable/start them")
|
|
57
90
|
.action(async (opts) => {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
91
|
+
logLegacyWarning("install-service");
|
|
92
|
+
const cwd = process.cwd();
|
|
93
|
+
const config = loadConfig(cwd);
|
|
94
|
+
const systemdDir = opts.outputDir ?? ".supatype/systemd";
|
|
95
|
+
const absSystemdDir = resolve(cwd, systemdDir);
|
|
96
|
+
console.log("Generating systemd unit files...");
|
|
97
|
+
const { postgres, server } = generateUnits(config, cwd, {
|
|
98
|
+
outputDir: absSystemdDir,
|
|
99
|
+
...(opts.user !== undefined && { user: opts.user }),
|
|
66
100
|
});
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
async function setup(cwd, opts) {
|
|
77
|
-
// Load domain from opts or supatype.config.ts
|
|
78
|
-
const domain = opts.domain ?? loadDomainFromConfig(cwd);
|
|
79
|
-
if (!domain) {
|
|
80
|
-
console.error("Error: --domain is required (or set selfHost.domain in supatype.config.ts)");
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
console.log("Fetching latest image versions...");
|
|
84
|
-
const [postgresTag, authTag] = await Promise.all([
|
|
85
|
-
fetchLatestTag("supatype/postgres", "17-latest"),
|
|
86
|
-
fetchLatestTag("supatype/auth", "v1.0.0"),
|
|
87
|
-
]);
|
|
88
|
-
console.log(` postgres supatype/postgres:${postgresTag}`);
|
|
89
|
-
console.log(` auth supatype/auth:${authTag}`);
|
|
90
|
-
const deployDir = resolve(cwd, "deploy");
|
|
91
|
-
mkdirSync(deployDir, { recursive: true });
|
|
92
|
-
const write = (rel, content) => {
|
|
93
|
-
const full = join(deployDir, rel);
|
|
94
|
-
mkdirSync(resolve(full, ".."), { recursive: true });
|
|
95
|
-
writeFileSync(full, content, "utf8");
|
|
96
|
-
console.log(` created deploy/${rel}`);
|
|
97
|
-
};
|
|
98
|
-
// Generate all secrets
|
|
99
|
-
const pgPassword = randomBytes(24).toString("hex");
|
|
100
|
-
const jwtSecret = randomBytes(32).toString("hex");
|
|
101
|
-
const now = Math.floor(Date.now() / 1000);
|
|
102
|
-
const exp = now + 10 * 365 * 24 * 60 * 60; // 10 years
|
|
103
|
-
const anonKey = signJwt({ iss: "supatype", role: "anon", iat: now, exp }, jwtSecret);
|
|
104
|
-
const serviceKey = signJwt({ iss: "supatype", role: "service_role", iat: now, exp }, jwtSecret);
|
|
105
|
-
console.log("\nGenerating production deployment files...\n");
|
|
106
|
-
write(".env.production", envProductionTemplate(domain, pgPassword, jwtSecret, anonKey, serviceKey));
|
|
107
|
-
write("docker-compose.yml", productionComposeTemplate(domain, opts, postgresTag, authTag));
|
|
108
|
-
write("Caddyfile", caddyfileTemplate(domain, opts.sslEmail));
|
|
109
|
-
write("pgbouncer.ini", productionPgbouncerIni());
|
|
110
|
-
write("userlist.txt", productionUserlist(pgPassword));
|
|
111
|
-
write("deploy.sh", deployScript(domain));
|
|
112
|
-
// Copy kong.yml if it exists
|
|
113
|
-
const kongSrc = resolve(cwd, ".supatype/kong.yml");
|
|
114
|
-
if (existsSync(kongSrc)) {
|
|
115
|
-
copyFileSync(kongSrc, join(deployDir, "kong.yml"));
|
|
116
|
-
console.log(" copied deploy/kong.yml");
|
|
117
|
-
}
|
|
118
|
-
// Make deploy.sh executable on Unix
|
|
119
|
-
try {
|
|
120
|
-
spawnSync("chmod", ["+x", join(deployDir, "deploy.sh")]);
|
|
121
|
-
}
|
|
122
|
-
catch { /* non-Unix, ignore */ }
|
|
123
|
-
console.log(`
|
|
124
|
-
╔══════════════════════════════════════════════════════════════╗
|
|
125
|
-
║ SAVE THESE SECRETS — they will not be shown again! ║
|
|
126
|
-
╚══════════════════════════════════════════════════════════════╝
|
|
127
|
-
|
|
128
|
-
POSTGRES_PASSWORD=${pgPassword}
|
|
129
|
-
JWT_SECRET=${jwtSecret}
|
|
130
|
-
ANON_KEY=${anonKey}
|
|
131
|
-
SERVICE_ROLE_KEY=${serviceKey}
|
|
132
|
-
|
|
133
|
-
These are also written to deploy/.env.production — back it up securely.
|
|
134
|
-
DO NOT commit deploy/.env.production to source control.
|
|
135
|
-
|
|
136
|
-
Next steps:
|
|
137
|
-
1. Copy the deploy/ directory to your VPS
|
|
138
|
-
2. SSH into the VPS and run: bash deploy.sh
|
|
139
|
-
3. Your app will be live at https://${domain}
|
|
140
|
-
`);
|
|
141
|
-
}
|
|
142
|
-
// ─── Operations ───────────────────────────────────────────────────────────────
|
|
143
|
-
function runDockerCompose(args, label) {
|
|
144
|
-
const deployDir = resolve(process.cwd(), "deploy");
|
|
145
|
-
if (!existsSync(join(deployDir, "docker-compose.yml"))) {
|
|
146
|
-
console.error("deploy/docker-compose.yml not found. Run: supatype self-host setup");
|
|
147
|
-
process.exit(1);
|
|
148
|
-
}
|
|
149
|
-
const result = spawnSync("docker", ["compose", "-f", join(deployDir, "docker-compose.yml"), ...args], {
|
|
150
|
-
stdio: "inherit",
|
|
151
|
-
cwd: deployDir,
|
|
152
|
-
});
|
|
153
|
-
if (result.status !== 0)
|
|
154
|
-
process.exit(result.status ?? 1);
|
|
155
|
-
}
|
|
156
|
-
function backup(cwd, outputPath) {
|
|
157
|
-
const deployDir = resolve(cwd, "deploy");
|
|
158
|
-
if (!existsSync(join(deployDir, "docker-compose.yml"))) {
|
|
159
|
-
console.error("deploy/docker-compose.yml not found. Run: supatype self-host setup");
|
|
160
|
-
process.exit(1);
|
|
161
|
-
}
|
|
162
|
-
const fullOutput = resolve(cwd, outputPath);
|
|
163
|
-
mkdirSync(resolve(fullOutput, ".."), { recursive: true });
|
|
164
|
-
console.log(`Backing up database to ${outputPath}...`);
|
|
165
|
-
const result = spawnSync("docker", [
|
|
166
|
-
"compose",
|
|
167
|
-
"-f", join(deployDir, "docker-compose.yml"),
|
|
168
|
-
"exec", "-T", "db",
|
|
169
|
-
"sh", "-c", "pg_dumpall -U postgres | gzip",
|
|
170
|
-
], { cwd: deployDir, encoding: "buffer" });
|
|
171
|
-
if (result.status !== 0) {
|
|
172
|
-
console.error("Backup failed:", result.stderr?.toString());
|
|
173
|
-
process.exit(1);
|
|
174
|
-
}
|
|
175
|
-
writeFileSync(fullOutput, result.stdout);
|
|
176
|
-
console.log(`Backup saved to ${outputPath}`);
|
|
177
|
-
}
|
|
178
|
-
function update(cwd) {
|
|
179
|
-
const deployDir = resolve(cwd, "deploy");
|
|
180
|
-
console.log("Pulling latest images...");
|
|
181
|
-
spawnSync("docker", ["compose", "-f", join(deployDir, "docker-compose.yml"), "pull"], {
|
|
182
|
-
stdio: "inherit",
|
|
183
|
-
cwd: deployDir,
|
|
184
|
-
});
|
|
185
|
-
console.log("Restarting services...");
|
|
186
|
-
spawnSync("docker", ["compose", "-f", join(deployDir, "docker-compose.yml"), "up", "-d", "--wait"], {
|
|
187
|
-
stdio: "inherit",
|
|
188
|
-
cwd: deployDir,
|
|
189
|
-
});
|
|
190
|
-
console.log("Update complete.");
|
|
191
|
-
}
|
|
192
|
-
const MANAGED_SERVICES = [
|
|
193
|
-
{ composeName: "db", image: "supatype/postgres", repo: "supatype/postgres", fallbackTag: "17-latest" },
|
|
194
|
-
{ composeName: "gotrue", image: "supatype/auth", repo: "supatype/auth", fallbackTag: "v1.0.0" },
|
|
195
|
-
{ composeName: "postgrest", image: "postgrest/postgrest", repo: "PostgREST/postgrest", fallbackTag: "v12.2.8" },
|
|
196
|
-
{ composeName: "kong", image: "kong", repo: null, fallbackTag: "3.6" },
|
|
197
|
-
{ composeName: "caddy", image: "caddy", repo: null, fallbackTag: "2" },
|
|
198
|
-
{ composeName: "pgbouncer", image: "pgbouncer/pgbouncer", repo: null, fallbackTag: "latest" },
|
|
199
|
-
{ composeName: "functions", image: "denoland/deno", repo: "denoland/deno", fallbackTag: "latest" },
|
|
200
|
-
];
|
|
201
|
-
function getCurrentImageTag(deployDir, serviceName) {
|
|
202
|
-
const result = spawnSync("docker", ["compose", "-f", join(deployDir, "docker-compose.yml"), "images", serviceName, "--format", "json"], { cwd: deployDir, encoding: "utf8" });
|
|
203
|
-
if (result.status !== 0 || !result.stdout.trim())
|
|
204
|
-
return null;
|
|
205
|
-
try {
|
|
206
|
-
// docker compose images --format json outputs one JSON object per line
|
|
207
|
-
const lines = result.stdout.trim().split("\n");
|
|
208
|
-
for (const line of lines) {
|
|
209
|
-
const data = JSON.parse(line);
|
|
210
|
-
if (data.Tag)
|
|
211
|
-
return data.Tag;
|
|
101
|
+
console.log(` wrote ${postgres}`);
|
|
102
|
+
console.log(` wrote ${server}`);
|
|
103
|
+
if (!opts.enable) {
|
|
104
|
+
console.log(`\nTo install manually:\n` +
|
|
105
|
+
` sudo cp ${postgres} /etc/systemd/system/\n` +
|
|
106
|
+
` sudo cp ${server} /etc/systemd/system/\n` +
|
|
107
|
+
` sudo systemctl daemon-reload\n` +
|
|
108
|
+
` sudo systemctl enable --now supatype-postgres supatype-server`);
|
|
109
|
+
return;
|
|
212
110
|
}
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
111
|
+
if (process.platform !== "linux") {
|
|
112
|
+
console.log("\nNote: systemd unit installation is only supported on Linux.\n" +
|
|
113
|
+
`Unit files are at ${absSystemdDir}/`);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
// Install to /etc/systemd/system/
|
|
117
|
+
console.log("\nInstalling to /etc/systemd/system/ (requires sudo)...");
|
|
118
|
+
const units = [
|
|
119
|
+
{ src: postgres, dest: "/etc/systemd/system/supatype-postgres.service" },
|
|
120
|
+
{ src: server, dest: "/etc/systemd/system/supatype-server.service" },
|
|
121
|
+
];
|
|
122
|
+
for (const { src, dest } of units) {
|
|
123
|
+
const cp = spawnSync("sudo", ["cp", src, dest], { stdio: "inherit" });
|
|
124
|
+
if (cp.status !== 0) {
|
|
125
|
+
console.error(`Failed to copy ${src} to ${dest}`);
|
|
126
|
+
process.exit(1);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const daemonReload = spawnSync("sudo", ["systemctl", "daemon-reload"], { stdio: "inherit" });
|
|
130
|
+
if (daemonReload.status !== 0) {
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
const enable = spawnSync("sudo", ["systemctl", "enable", "--now", "supatype-postgres", "supatype-server"], { stdio: "inherit" });
|
|
134
|
+
if (enable.status !== 0) {
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
console.log("\nServices installed and started.");
|
|
138
|
+
console.log(" supatype-postgres.service");
|
|
139
|
+
console.log(" supatype-server.service");
|
|
140
|
+
});
|
|
141
|
+
// ── serve ──────────────────────────────────────────────────────────────────
|
|
142
|
+
legacyCmd
|
|
143
|
+
.command("serve", "Start supatype-server in the foreground (for standalone mode)")
|
|
144
|
+
.option("--port <port>", "Override port from config")
|
|
145
|
+
.action(async (opts) => {
|
|
146
|
+
logLegacyWarning("serve");
|
|
147
|
+
const cwd = process.cwd();
|
|
148
|
+
const config = loadConfig(cwd);
|
|
149
|
+
const serverBin = await resolveBinary("server", config);
|
|
150
|
+
const port = opts.port ?? String(config.server.port ?? 54321);
|
|
151
|
+
const args = [
|
|
152
|
+
"--port", port,
|
|
153
|
+
"--mode", config.server.mode,
|
|
154
|
+
...(config.server.domain ? ["--domain", config.server.domain] : []),
|
|
155
|
+
];
|
|
156
|
+
const stateDir = join(homedir(), ".supatype", "projects", config.project.name);
|
|
157
|
+
const storageEnv = config.storage?.provider !== "s3" ? localStorageEnv(stateDir) : {};
|
|
158
|
+
console.log(`Starting supatype-server on port ${port}...`);
|
|
159
|
+
const result = spawnSync(serverBin, args, {
|
|
160
|
+
stdio: "inherit",
|
|
161
|
+
cwd,
|
|
162
|
+
env: { ...process.env, ...storageEnv },
|
|
224
163
|
});
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
return null;
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
function loadSelfHostConfig(cwd) {
|
|
237
|
-
try {
|
|
238
|
-
const { loadConfig } = require("../config.js");
|
|
164
|
+
process.exitCode = result.status ?? 1;
|
|
165
|
+
});
|
|
166
|
+
// ── reload ─────────────────────────────────────────────────────────────────
|
|
167
|
+
legacyCmd
|
|
168
|
+
.command("reload", "Reload the running supatype-server (SIGHUP for config reload)")
|
|
169
|
+
.action(() => {
|
|
170
|
+
logLegacyWarning("reload");
|
|
171
|
+
const cwd = process.cwd();
|
|
239
172
|
const config = loadConfig(cwd);
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
return undefined;
|
|
249
|
-
return selfHostConfig.services[serviceName];
|
|
250
|
-
}
|
|
251
|
-
function checkServiceHealth(deployDir, serviceName, timeoutSeconds = 60) {
|
|
252
|
-
console.log(` Checking health of ${serviceName}...`);
|
|
253
|
-
const deadline = Date.now() + timeoutSeconds * 1000;
|
|
254
|
-
while (Date.now() < deadline) {
|
|
255
|
-
const result = spawnSync("docker", ["compose", "-f", join(deployDir, "docker-compose.yml"), "ps", serviceName, "--format", "json"], { cwd: deployDir, encoding: "utf8" });
|
|
256
|
-
if (result.status === 0 && result.stdout.trim()) {
|
|
257
|
-
const lines = result.stdout.trim().split("\n");
|
|
258
|
-
for (const line of lines) {
|
|
259
|
-
try {
|
|
260
|
-
const data = JSON.parse(line);
|
|
261
|
-
// A service is considered healthy if:
|
|
262
|
-
// - it has a healthcheck and Health is "healthy", or
|
|
263
|
-
// - it has no healthcheck and State is "running"
|
|
264
|
-
if (data.Health === "healthy")
|
|
265
|
-
return true;
|
|
266
|
-
if (!data.Health && data.State === "running")
|
|
267
|
-
return true;
|
|
268
|
-
// Status field sometimes contains "Up ... (healthy)"
|
|
269
|
-
if (data.Status && data.Status.includes("healthy"))
|
|
270
|
-
return true;
|
|
271
|
-
if (data.Status && !data.Status.includes("health") && data.State === "running")
|
|
272
|
-
return true;
|
|
273
|
-
}
|
|
274
|
-
catch { /* skip bad line */ }
|
|
173
|
+
const stateDir = join(homedir(), ".supatype", "projects", config.project.name);
|
|
174
|
+
const pid = readPid(join(stateDir, "pid"), "server");
|
|
175
|
+
if (!pid) {
|
|
176
|
+
// Try systemctl if running as a service
|
|
177
|
+
if (process.platform === "linux") {
|
|
178
|
+
const result = spawnSync("systemctl", ["reload", "supatype-server"], { stdio: "inherit" });
|
|
179
|
+
process.exitCode = result.status ?? 1;
|
|
180
|
+
return;
|
|
275
181
|
}
|
|
182
|
+
console.error("Server does not appear to be running (no PID file found).");
|
|
183
|
+
process.exitCode = 1;
|
|
184
|
+
return;
|
|
276
185
|
}
|
|
277
|
-
spawnSync("sleep", ["3"]);
|
|
278
|
-
}
|
|
279
|
-
return false;
|
|
280
|
-
}
|
|
281
|
-
function rollbackService(deployDir, serviceName, previousImage) {
|
|
282
|
-
console.log(` Rolling back ${serviceName} to ${previousImage}...`);
|
|
283
|
-
// Pull the previous image back
|
|
284
|
-
const pullResult = spawnSync("docker", ["pull", previousImage], { stdio: "inherit", cwd: deployDir });
|
|
285
|
-
if (pullResult.status !== 0)
|
|
286
|
-
return false;
|
|
287
|
-
// Restart the service (docker compose will use the image now available)
|
|
288
|
-
// We need to re-tag or use docker compose up with the old image.
|
|
289
|
-
// The simplest reliable approach: stop the service, then start it.
|
|
290
|
-
// Since compose file may have :latest or a tag, we re-pull and restart.
|
|
291
|
-
const upResult = spawnSync("docker", ["compose", "-f", join(deployDir, "docker-compose.yml"), "up", "-d", "--no-deps", serviceName], { stdio: "inherit", cwd: deployDir });
|
|
292
|
-
return upResult.status === 0;
|
|
293
|
-
}
|
|
294
|
-
function applyDatabaseMigrations(deployDir) {
|
|
295
|
-
console.log("\nApplying database migrations...");
|
|
296
|
-
// Check if migrations directory exists
|
|
297
|
-
const migrationsDir = resolve(deployDir, "..", "migrations");
|
|
298
|
-
if (!existsSync(migrationsDir)) {
|
|
299
|
-
console.log(" No migrations directory found, skipping.");
|
|
300
|
-
return true;
|
|
301
|
-
}
|
|
302
|
-
// Run migrations via docker exec into the db container
|
|
303
|
-
const result = spawnSync("docker", [
|
|
304
|
-
"compose",
|
|
305
|
-
"-f", join(deployDir, "docker-compose.yml"),
|
|
306
|
-
"exec", "-T", "db",
|
|
307
|
-
"sh", "-c",
|
|
308
|
-
`for f in /migrations/*.sql; do [ -f "$f" ] && psql -U postgres -d supatype -f "$f" && echo "Applied: $f"; done`,
|
|
309
|
-
], {
|
|
310
|
-
cwd: deployDir,
|
|
311
|
-
stdio: "inherit",
|
|
312
|
-
// Mount migrations directory
|
|
313
|
-
env: { ...process.env },
|
|
314
|
-
});
|
|
315
|
-
// Also try with a copy approach if the volume isn't mounted
|
|
316
|
-
if (result.status !== 0) {
|
|
317
|
-
// Copy and run migrations one at a time
|
|
318
|
-
const { readdirSync } = require("node:fs");
|
|
319
186
|
try {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
const sqlPath = resolve(migrationsDir, file);
|
|
323
|
-
const sql = readFileSync(sqlPath, "utf8");
|
|
324
|
-
const execResult = spawnSync("docker", [
|
|
325
|
-
"compose",
|
|
326
|
-
"-f", join(deployDir, "docker-compose.yml"),
|
|
327
|
-
"exec", "-T", "db",
|
|
328
|
-
"psql", "-U", "postgres", "-d", "supatype", "-c", sql,
|
|
329
|
-
], { cwd: deployDir, encoding: "utf8" });
|
|
330
|
-
if (execResult.status !== 0) {
|
|
331
|
-
console.error(` Failed to apply migration ${file}: ${execResult.stderr}`);
|
|
332
|
-
return false;
|
|
333
|
-
}
|
|
334
|
-
console.log(` Applied: ${file}`);
|
|
335
|
-
}
|
|
187
|
+
process.kill(pid, "SIGHUP");
|
|
188
|
+
console.log(`Sent SIGHUP to supatype-server (pid ${pid}).`);
|
|
336
189
|
}
|
|
337
190
|
catch (err) {
|
|
338
|
-
console.error(`
|
|
339
|
-
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
console.log(" Migrations complete.");
|
|
343
|
-
return true;
|
|
344
|
-
}
|
|
345
|
-
/** Summarize a release body to a short changelog line. */
|
|
346
|
-
function summarizeChangelog(body) {
|
|
347
|
-
if (!body.trim())
|
|
348
|
-
return "(no changelog available)";
|
|
349
|
-
// Take first 3 non-empty lines, strip markdown headers
|
|
350
|
-
const lines = body
|
|
351
|
-
.split("\n")
|
|
352
|
-
.map(l => l.trim())
|
|
353
|
-
.filter(l => l.length > 0)
|
|
354
|
-
.map(l => l.replace(/^#+\s*/, ""))
|
|
355
|
-
.slice(0, 3);
|
|
356
|
-
const summary = lines.join("; ");
|
|
357
|
-
return summary.length > 120 ? summary.slice(0, 117) + "..." : summary;
|
|
358
|
-
}
|
|
359
|
-
async function upgrade(cwd, opts) {
|
|
360
|
-
const deployDir = resolve(cwd, "deploy");
|
|
361
|
-
if (!existsSync(join(deployDir, "docker-compose.yml"))) {
|
|
362
|
-
console.error("deploy/docker-compose.yml not found. Run: supatype self-host setup");
|
|
363
|
-
process.exit(1);
|
|
364
|
-
}
|
|
365
|
-
const selfHostConfig = loadSelfHostConfig(cwd);
|
|
366
|
-
// ── Step 1: Check current vs latest versions ──────────────────────────────
|
|
367
|
-
console.log("Checking service versions...\n");
|
|
368
|
-
const plans = [];
|
|
369
|
-
for (const svc of MANAGED_SERVICES) {
|
|
370
|
-
const pin = isServicePinned(selfHostConfig, svc.composeName);
|
|
371
|
-
const currentTag = getCurrentImageTag(deployDir, svc.composeName);
|
|
372
|
-
if (pin) {
|
|
373
|
-
console.log(` ${svc.composeName.padEnd(12)} pinned at ${pin.version} (skipping)`);
|
|
374
|
-
plans.push({
|
|
375
|
-
service: svc,
|
|
376
|
-
currentTag,
|
|
377
|
-
latestTag: pin.version,
|
|
378
|
-
changelog: "",
|
|
379
|
-
pinned: true,
|
|
380
|
-
});
|
|
381
|
-
continue;
|
|
191
|
+
console.error(`Failed to signal pid ${pid}:`, err.message);
|
|
192
|
+
process.exitCode = 1;
|
|
382
193
|
}
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
194
|
+
});
|
|
195
|
+
// ── status ─────────────────────────────────────────────────────────────────
|
|
196
|
+
legacyCmd
|
|
197
|
+
.command("status", "Show running status of supatype services")
|
|
198
|
+
.action(() => {
|
|
199
|
+
logLegacyWarning("status");
|
|
200
|
+
const cwd = process.cwd();
|
|
201
|
+
const config = loadConfig(cwd);
|
|
202
|
+
const stateDir = join(homedir(), ".supatype", "projects", config.project.name);
|
|
203
|
+
console.log(`Project: ${config.project.name}\n`);
|
|
204
|
+
if (process.platform === "linux" && existsSync("/run/systemd/system")) {
|
|
205
|
+
// systemd is active
|
|
206
|
+
for (const svc of ["supatype-postgres", "supatype-server"]) {
|
|
207
|
+
const result = spawnSync("systemctl", ["status", "--no-pager", "--lines=0", svc], {
|
|
208
|
+
encoding: "utf8",
|
|
209
|
+
});
|
|
210
|
+
const active = result.stdout?.includes("active (running)") ? "running" : "stopped";
|
|
211
|
+
console.log(` ${svc}: ${active}`);
|
|
390
212
|
}
|
|
391
213
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
console.log(
|
|
214
|
+
else {
|
|
215
|
+
// PID file check
|
|
216
|
+
const serverPid = readPid(join(stateDir, "pid"), "server");
|
|
217
|
+
const pgPid = readPid(join(stateDir, "pid"), "postgres");
|
|
218
|
+
console.log(` postgres: ${pgPid ? `running (pid ${pgPid})` : "stopped"}`);
|
|
219
|
+
console.log(` supatype-server: ${serverPid ? `running (pid ${serverPid})` : "stopped"}`);
|
|
397
220
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
latestTag,
|
|
402
|
-
changelog,
|
|
403
|
-
pinned: false,
|
|
404
|
-
});
|
|
405
|
-
}
|
|
406
|
-
const upgradeable = plans.filter(p => !p.pinned && p.currentTag !== p.latestTag);
|
|
407
|
-
if (upgradeable.length === 0) {
|
|
408
|
-
console.log("\nAll services are up to date. Nothing to upgrade.");
|
|
409
|
-
return;
|
|
410
|
-
}
|
|
411
|
-
console.log(`\n${upgradeable.length} service(s) will be upgraded.\n`);
|
|
412
|
-
// ── Step 2: Pre-upgrade backup ────────────────────────────────────────────
|
|
413
|
-
if (!opts.skipBackup) {
|
|
414
|
-
const backupPath = `./backups/pre-upgrade-${timestamp()}.sql.gz`;
|
|
415
|
-
console.log(`Creating pre-upgrade backup: ${backupPath}`);
|
|
416
|
-
backup(cwd, backupPath);
|
|
417
|
-
console.log("Backup complete.\n");
|
|
418
|
-
}
|
|
419
|
-
else {
|
|
420
|
-
console.log("Skipping pre-upgrade backup (--skip-backup).\n");
|
|
421
|
-
}
|
|
422
|
-
// ── Step 3: Apply database migrations ─────────────────────────────────────
|
|
423
|
-
if (!opts.skipMigrations) {
|
|
424
|
-
const migrationOk = applyDatabaseMigrations(deployDir);
|
|
425
|
-
if (!migrationOk) {
|
|
426
|
-
console.error("\nDatabase migration failed. Aborting upgrade.");
|
|
427
|
-
console.error("Your pre-upgrade backup is available. To restore:");
|
|
428
|
-
console.error(" docker compose exec -T db sh -c 'gunzip | psql -U postgres' < <backup-file>");
|
|
429
|
-
process.exit(1);
|
|
221
|
+
const logDir = join(stateDir, "logs");
|
|
222
|
+
if (existsSync(logDir)) {
|
|
223
|
+
console.log(`\nLogs: ${logDir}`);
|
|
430
224
|
}
|
|
431
|
-
}
|
|
432
|
-
// ──
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
225
|
+
});
|
|
226
|
+
// ── logs ───────────────────────────────────────────────────────────────────
|
|
227
|
+
selfHostCmd
|
|
228
|
+
.command("logs", "Tail supatype service logs", { hidden: true })
|
|
229
|
+
.option("--service <name>", "Show logs for: postgres | server")
|
|
230
|
+
.option("--lines <n>", "Number of lines to show", "50")
|
|
231
|
+
.option("-f, --follow", "Follow log output")
|
|
232
|
+
.action((opts) => {
|
|
233
|
+
logLegacyWarning("logs");
|
|
234
|
+
const cwd = process.cwd();
|
|
235
|
+
const config = loadConfig(cwd);
|
|
236
|
+
const stateDir = join(homedir(), ".supatype", "projects", config.project.name);
|
|
237
|
+
const logDir = join(stateDir, "logs");
|
|
238
|
+
if (process.platform === "linux" && existsSync("/run/systemd/system")) {
|
|
239
|
+
const args = ["--no-pager", "--lines", opts.lines];
|
|
240
|
+
if (opts.follow)
|
|
241
|
+
args.push("--follow");
|
|
242
|
+
if (opts.service)
|
|
243
|
+
args.push(`-u`, `supatype-${opts.service}`);
|
|
244
|
+
else
|
|
245
|
+
args.push("-u", "supatype-postgres", "-u", "supatype-server");
|
|
246
|
+
spawnSync("journalctl", args, { stdio: "inherit" });
|
|
247
|
+
return;
|
|
450
248
|
}
|
|
451
|
-
//
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
}
|
|
459
|
-
failed.push(svc.composeName);
|
|
460
|
-
continue;
|
|
249
|
+
// File-based logs
|
|
250
|
+
const targets = [];
|
|
251
|
+
if (!opts.service || opts.service === "postgres") {
|
|
252
|
+
targets.push({ label: "postgres", file: join(logDir, "postgres.log") });
|
|
253
|
+
}
|
|
254
|
+
if (!opts.service || opts.service === "server") {
|
|
255
|
+
targets.push({ label: "server", file: join(logDir, "server.log") });
|
|
461
256
|
}
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
const
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
else {
|
|
479
|
-
console.error(` WARNING: Rollback failed for ${svc.composeName}.`);
|
|
480
|
-
}
|
|
257
|
+
for (const { label, file } of targets) {
|
|
258
|
+
if (!existsSync(file)) {
|
|
259
|
+
console.log(`[${label}] log file not found: ${file}`);
|
|
260
|
+
continue;
|
|
261
|
+
}
|
|
262
|
+
if (opts.follow) {
|
|
263
|
+
const tail = spawnSync("tail", ["-f", "-n", opts.lines, file], { stdio: "inherit" });
|
|
264
|
+
process.exitCode = tail.status ?? 0;
|
|
265
|
+
}
|
|
266
|
+
else {
|
|
267
|
+
const n = parseInt(opts.lines, 10);
|
|
268
|
+
const content = readFileSync(file, "utf8");
|
|
269
|
+
const lines = content.split("\n");
|
|
270
|
+
console.log(lines.slice(-n).join("\n"));
|
|
481
271
|
}
|
|
482
|
-
failed.push(svc.composeName);
|
|
483
|
-
continue;
|
|
484
272
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
console.error(" 1. Check logs: supatype self-host logs --service <name>");
|
|
495
|
-
console.error(" 2. Check status: supatype self-host status");
|
|
496
|
-
console.error(" 3. Restore backup: docker compose exec -T db sh -c 'gunzip | psql -U postgres' < <backup-file>");
|
|
497
|
-
console.error(" 4. Pin a version: Add services.<name>.version in supatype.config.ts selfHost config");
|
|
498
|
-
process.exit(1);
|
|
499
|
-
}
|
|
500
|
-
}
|
|
501
|
-
// ─── Config helpers ───────────────────────────────────────────────────────────
|
|
502
|
-
function loadDomainFromConfig(cwd) {
|
|
503
|
-
try {
|
|
504
|
-
const { loadConfig } = require("../config.js");
|
|
273
|
+
});
|
|
274
|
+
// ── backup ─────────────────────────────────────────────────────────────────
|
|
275
|
+
legacyCmd
|
|
276
|
+
.command("backup", "Create a Postgres dump of the project database")
|
|
277
|
+
.option("--output <path>", "Output file path (default: ./backups/backup-<timestamp>.sql.gz)")
|
|
278
|
+
.option("--connection <url>", "Database connection URL (overrides config)")
|
|
279
|
+
.action((opts) => {
|
|
280
|
+
logLegacyWarning("backup");
|
|
281
|
+
const cwd = process.cwd();
|
|
505
282
|
const config = loadConfig(cwd);
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
SITE_URL=https://${domain}
|
|
530
|
-
|
|
531
|
-
# SMTP — required for user email confirmation in production
|
|
532
|
-
SMTP_HOST=
|
|
533
|
-
SMTP_PORT=587
|
|
534
|
-
SMTP_USER=
|
|
535
|
-
SMTP_PASS=
|
|
536
|
-
SMTP_SENDER_NAME=Supatype
|
|
537
|
-
`;
|
|
538
|
-
}
|
|
539
|
-
function productionComposeTemplate(domain, opts, postgresTag, authTag) {
|
|
540
|
-
const appService = opts.appDockerfile
|
|
541
|
-
? `
|
|
542
|
-
app:
|
|
543
|
-
build:
|
|
544
|
-
context: ..
|
|
545
|
-
dockerfile: ${opts.appDockerfile}
|
|
546
|
-
environment:
|
|
547
|
-
SUPATYPE_URL: http://kong:8000
|
|
548
|
-
SUPATYPE_ANON_KEY: \${ANON_KEY}
|
|
549
|
-
SUPATYPE_SERVICE_ROLE_KEY: \${SERVICE_ROLE_KEY}
|
|
550
|
-
networks:
|
|
551
|
-
- supatype
|
|
552
|
-
depends_on:
|
|
553
|
-
- kong
|
|
554
|
-
restart: unless-stopped
|
|
555
|
-
`
|
|
556
|
-
: "";
|
|
557
|
-
return `# Production docker-compose — generated by supatype self-host setup
|
|
558
|
-
# Run with: docker compose up -d (from within the deploy/ directory)
|
|
559
|
-
|
|
560
|
-
services:
|
|
561
|
-
db:
|
|
562
|
-
image: supatype/postgres:${postgresTag}
|
|
563
|
-
environment:
|
|
564
|
-
POSTGRES_PASSWORD: \${POSTGRES_PASSWORD}
|
|
565
|
-
POSTGRES_DB: \${POSTGRES_DB:-supatype}
|
|
566
|
-
volumes:
|
|
567
|
-
- db-data:/var/lib/postgresql/data
|
|
568
|
-
networks:
|
|
569
|
-
- supatype
|
|
570
|
-
healthcheck:
|
|
571
|
-
test: ["CMD-SHELL", "pg_isready -U postgres"]
|
|
572
|
-
interval: 10s
|
|
573
|
-
timeout: 5s
|
|
574
|
-
retries: 20
|
|
575
|
-
restart: unless-stopped
|
|
576
|
-
|
|
577
|
-
pgbouncer:
|
|
578
|
-
image: pgbouncer/pgbouncer:latest
|
|
579
|
-
volumes:
|
|
580
|
-
- ./pgbouncer.ini:/etc/pgbouncer/pgbouncer.ini:ro
|
|
581
|
-
- ./userlist.txt:/etc/pgbouncer/userlist.txt:ro
|
|
582
|
-
networks:
|
|
583
|
-
- supatype
|
|
584
|
-
depends_on:
|
|
585
|
-
db:
|
|
586
|
-
condition: service_healthy
|
|
587
|
-
restart: unless-stopped
|
|
588
|
-
|
|
589
|
-
gotrue:
|
|
590
|
-
image: supatype/auth:${authTag}
|
|
591
|
-
environment:
|
|
592
|
-
GOTRUE_API_HOST: 0.0.0.0
|
|
593
|
-
GOTRUE_API_PORT: 9999
|
|
594
|
-
GOTRUE_DB_DRIVER: postgres
|
|
595
|
-
GOTRUE_DB_DATABASE_URL: "postgres://postgres:\${POSTGRES_PASSWORD}@pgbouncer:6432/\${POSTGRES_DB:-supatype}?search_path=auth"
|
|
596
|
-
GOTRUE_SITE_URL: https://${domain}
|
|
597
|
-
GOTRUE_JWT_SECRET: \${JWT_SECRET}
|
|
598
|
-
GOTRUE_JWT_EXP: 3600
|
|
599
|
-
GOTRUE_JWT_AUD: authenticated
|
|
600
|
-
GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
|
|
601
|
-
GOTRUE_JWT_ADMIN_ROLES: service_role
|
|
602
|
-
GOTRUE_MAILER_AUTOCONFIRM: false
|
|
603
|
-
GOTRUE_SMTP_HOST: \${SMTP_HOST}
|
|
604
|
-
GOTRUE_SMTP_PORT: \${SMTP_PORT:-587}
|
|
605
|
-
GOTRUE_SMTP_USER: \${SMTP_USER}
|
|
606
|
-
GOTRUE_SMTP_PASS: \${SMTP_PASS}
|
|
607
|
-
GOTRUE_SMTP_SENDER_NAME: \${SMTP_SENDER_NAME:-Supatype}
|
|
608
|
-
GOTRUE_MAILER_URLPATHS_CONFIRMATION: /auth/v1/verify
|
|
609
|
-
GOTRUE_MAILER_URLPATHS_RECOVERY: /auth/v1/verify
|
|
610
|
-
GOTRUE_MAILER_URLPATHS_EMAIL_CHANGE: /auth/v1/verify
|
|
611
|
-
GOTRUE_MAILER_URLPATHS_INVITE: /auth/v1/verify
|
|
612
|
-
GOTRUE_DISABLE_SIGNUP: false
|
|
613
|
-
networks:
|
|
614
|
-
- supatype
|
|
615
|
-
depends_on:
|
|
616
|
-
pgbouncer:
|
|
617
|
-
condition: service_started
|
|
618
|
-
restart: unless-stopped
|
|
619
|
-
|
|
620
|
-
postgrest:
|
|
621
|
-
image: postgrest/postgrest:v12.2.8
|
|
622
|
-
environment:
|
|
623
|
-
PGRST_DB_URI: postgresql://authenticator:\${POSTGRES_PASSWORD}@pgbouncer:6432/\${POSTGRES_DB:-supatype}
|
|
624
|
-
PGRST_DB_SCHEMA: public
|
|
625
|
-
PGRST_DB_ANON_ROLE: anon
|
|
626
|
-
PGRST_JWT_SECRET: \${JWT_SECRET}
|
|
627
|
-
PGRST_DB_EXTRA_SEARCH_PATH: public,extensions
|
|
628
|
-
PGRST_DB_POOL: 3
|
|
629
|
-
networks:
|
|
630
|
-
- supatype
|
|
631
|
-
depends_on:
|
|
632
|
-
pgbouncer:
|
|
633
|
-
condition: service_started
|
|
634
|
-
restart: unless-stopped
|
|
635
|
-
|
|
636
|
-
kong:
|
|
637
|
-
image: kong:3.6
|
|
638
|
-
environment:
|
|
639
|
-
KONG_DATABASE: "off"
|
|
640
|
-
KONG_DECLARATIVE_CONFIG: /etc/kong/kong.yml
|
|
641
|
-
KONG_PROXY_ACCESS_LOG: /dev/stdout
|
|
642
|
-
KONG_ADMIN_ACCESS_LOG: /dev/stdout
|
|
643
|
-
KONG_PROXY_ERROR_LOG: /dev/stderr
|
|
644
|
-
KONG_ADMIN_ERROR_LOG: /dev/stderr
|
|
645
|
-
volumes:
|
|
646
|
-
- ./kong.yml:/etc/kong/kong.yml:ro
|
|
647
|
-
networks:
|
|
648
|
-
- supatype
|
|
649
|
-
depends_on:
|
|
650
|
-
- postgrest
|
|
651
|
-
- gotrue
|
|
652
|
-
restart: unless-stopped
|
|
653
|
-
${appService}
|
|
654
|
-
functions:
|
|
655
|
-
image: denoland/deno:latest
|
|
656
|
-
environment:
|
|
657
|
-
SUPATYPE_URL: http://kong:8000
|
|
658
|
-
SUPATYPE_ANON_KEY: \${ANON_KEY}
|
|
659
|
-
SUPATYPE_SERVICE_ROLE_KEY: \${SERVICE_ROLE_KEY}
|
|
660
|
-
FUNCTIONS_DIR: /functions
|
|
661
|
-
volumes:
|
|
662
|
-
- ../supatype/functions:/functions:ro
|
|
663
|
-
networks:
|
|
664
|
-
- supatype
|
|
665
|
-
depends_on:
|
|
666
|
-
- kong
|
|
667
|
-
mem_limit: 512m
|
|
668
|
-
cpus: 1.0
|
|
669
|
-
restart: unless-stopped
|
|
670
|
-
|
|
671
|
-
caddy:
|
|
672
|
-
image: caddy:2
|
|
673
|
-
ports:
|
|
674
|
-
- "80:80"
|
|
675
|
-
- "443:443"
|
|
676
|
-
volumes:
|
|
677
|
-
- ./Caddyfile:/etc/caddy/Caddyfile:ro
|
|
678
|
-
- caddy-data:/data
|
|
679
|
-
- caddy-config:/config
|
|
680
|
-
networks:
|
|
681
|
-
- supatype
|
|
682
|
-
depends_on:
|
|
683
|
-
- kong
|
|
684
|
-
restart: unless-stopped
|
|
685
|
-
|
|
686
|
-
networks:
|
|
687
|
-
supatype:
|
|
688
|
-
driver: bridge
|
|
689
|
-
|
|
690
|
-
volumes:
|
|
691
|
-
db-data:
|
|
692
|
-
caddy-data:
|
|
693
|
-
caddy-config:
|
|
694
|
-
`;
|
|
695
|
-
}
|
|
696
|
-
function caddyfileTemplate(domain, sslEmail) {
|
|
697
|
-
const emailLine = sslEmail ? `\n\ttls ${sslEmail}\n` : "";
|
|
698
|
-
return `${domain} {${emailLine}
|
|
699
|
-
\treverse_proxy kong:8000
|
|
700
|
-
|
|
701
|
-
\theader {
|
|
702
|
-
\t\tStrict-Transport-Security "max-age=31536000; includeSubDomains"
|
|
703
|
-
\t\tX-Frame-Options "SAMEORIGIN"
|
|
704
|
-
\t\tX-Content-Type-Options "nosniff"
|
|
705
|
-
\t}
|
|
706
|
-
}
|
|
707
|
-
`;
|
|
708
|
-
}
|
|
709
|
-
function productionPgbouncerIni() {
|
|
710
|
-
return `[databases]
|
|
711
|
-
* = host=db port=5432
|
|
712
|
-
|
|
713
|
-
[pgbouncer]
|
|
714
|
-
listen_addr = 0.0.0.0
|
|
715
|
-
listen_port = 6432
|
|
716
|
-
auth_type = md5
|
|
717
|
-
auth_file = /etc/pgbouncer/userlist.txt
|
|
718
|
-
pool_mode = transaction
|
|
719
|
-
default_pool_size = 20
|
|
720
|
-
max_db_connections = 60
|
|
721
|
-
max_client_conn = 100
|
|
722
|
-
server_reset_query = DEALLOCATE ALL
|
|
723
|
-
ignore_startup_parameters = extra_float_digits
|
|
724
|
-
`;
|
|
725
|
-
}
|
|
726
|
-
function productionUserlist(pgPassword) {
|
|
727
|
-
// PgBouncer md5 format: "md5" + md5(password + username)
|
|
728
|
-
const md5Hash = (s) => {
|
|
729
|
-
const { createHash } = require("node:crypto");
|
|
730
|
-
return createHash("md5").update(s).digest("hex");
|
|
731
|
-
};
|
|
732
|
-
const postgresHash = "md5" + md5Hash(pgPassword + "postgres");
|
|
733
|
-
const authenticatorHash = "md5" + md5Hash(pgPassword + "authenticator");
|
|
734
|
-
return `# PgBouncer userlist — generated by supatype self-host setup
|
|
735
|
-
# Regenerate by running: supatype self-host setup
|
|
736
|
-
"postgres" "${postgresHash}"
|
|
737
|
-
"authenticator" "${authenticatorHash}"
|
|
738
|
-
`;
|
|
283
|
+
const conn = opts.connection ?? connectionString(config);
|
|
284
|
+
const outFile = opts.output ?? resolve(cwd, "backups", `backup-${new Date().toISOString().replace(/[:.]/g, "-")}.sql.gz`);
|
|
285
|
+
mkdirSync(resolve(outFile, ".."), { recursive: true });
|
|
286
|
+
console.log(`Backing up database to ${outFile}...`);
|
|
287
|
+
try {
|
|
288
|
+
// Avoid shell interpolation of user-supplied values.
|
|
289
|
+
const pgDump = spawnSync("pg_dump", [conn], {
|
|
290
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
291
|
+
});
|
|
292
|
+
if (pgDump.status !== 0) {
|
|
293
|
+
const stderr = pgDump.stderr?.toString("utf8") ?? "";
|
|
294
|
+
throw new Error(stderr.trim() || "pg_dump failed");
|
|
295
|
+
}
|
|
296
|
+
const compressed = gzipSync(pgDump.stdout);
|
|
297
|
+
writeFileSync(outFile, compressed);
|
|
298
|
+
console.log("Backup complete.");
|
|
299
|
+
}
|
|
300
|
+
catch (err) {
|
|
301
|
+
console.error("Backup failed:", err.message);
|
|
302
|
+
process.exit(1);
|
|
303
|
+
}
|
|
304
|
+
});
|
|
739
305
|
}
|
|
740
|
-
function
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
# Run once on a fresh VPS: bash deploy.sh
|
|
744
|
-
set -euo pipefail
|
|
745
|
-
|
|
746
|
-
DOMAIN="${domain}"
|
|
747
|
-
|
|
748
|
-
echo "Checking prerequisites..."
|
|
749
|
-
|
|
750
|
-
# Check Docker
|
|
751
|
-
if ! command -v docker &>/dev/null; then
|
|
752
|
-
echo "Docker not found. Installing..."
|
|
753
|
-
curl -fsSL https://get.docker.com | sh
|
|
754
|
-
usermod -aG docker "$USER"
|
|
755
|
-
newgrp docker
|
|
756
|
-
fi
|
|
757
|
-
|
|
758
|
-
# Check ports 80 and 443 are available
|
|
759
|
-
for port in 80 443; do
|
|
760
|
-
if ss -tlnp 2>/dev/null | grep -q ":$port " ; then
|
|
761
|
-
echo "Error: Port $port is already in use. Free it before running deploy.sh."
|
|
762
|
-
exit 1
|
|
763
|
-
fi
|
|
764
|
-
done
|
|
765
|
-
|
|
766
|
-
echo "Loading environment..."
|
|
767
|
-
if [ ! -f .env.production ]; then
|
|
768
|
-
echo "Error: .env.production not found in $(pwd)"
|
|
769
|
-
exit 1
|
|
770
|
-
fi
|
|
771
|
-
|
|
772
|
-
# Export env vars from .env.production
|
|
773
|
-
set -a; source .env.production; set +a
|
|
774
|
-
|
|
775
|
-
echo "Starting services..."
|
|
776
|
-
docker compose up -d --wait
|
|
777
|
-
|
|
778
|
-
echo "Waiting for health checks..."
|
|
779
|
-
timeout=120
|
|
780
|
-
elapsed=0
|
|
781
|
-
while ! docker compose ps --format json 2>/dev/null | grep -q '"Health":"healthy"'; do
|
|
782
|
-
sleep 5
|
|
783
|
-
elapsed=$((elapsed + 5))
|
|
784
|
-
if [ $elapsed -ge $timeout ]; then
|
|
785
|
-
echo "Timeout waiting for services to become healthy."
|
|
786
|
-
docker compose ps
|
|
787
|
-
exit 1
|
|
788
|
-
fi
|
|
789
|
-
done
|
|
790
|
-
|
|
791
|
-
echo ""
|
|
792
|
-
echo "Deployment complete!"
|
|
793
|
-
echo "Your app is live at: https://$DOMAIN"
|
|
794
|
-
`;
|
|
306
|
+
function logLegacyWarning(cmd) {
|
|
307
|
+
console.warn(`[supatype] self-host native ${cmd} is deprecated. ` +
|
|
308
|
+
"Use `supatype self-host compose` commands instead.");
|
|
795
309
|
}
|
|
796
310
|
//# sourceMappingURL=self-host.js.map
|