@supatype/cli 0.1.0-alpha.10
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 +4 -0
- package/.turbo/turbo-test.log +221 -0
- package/.turbo/turbo-typecheck.log +4 -0
- package/assets/supatype-logo-wordmark.ascii.txt +6 -0
- package/bin/dev-entry.ts +2 -0
- package/bin/supatype.js +5 -0
- package/dist/app/framework.d.ts +44 -0
- package/dist/app/framework.d.ts.map +1 -0
- package/dist/app/framework.js +200 -0
- package/dist/app/framework.js.map +1 -0
- 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 +54 -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/assets/supatype-logo-wordmark.ascii.txt +6 -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 +98 -0
- package/dist/binary-cache.d.ts.map +1 -0
- package/dist/binary-cache.js +687 -0
- package/dist/binary-cache.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +61 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/admin.d.ts +4 -0
- package/dist/commands/admin.d.ts.map +1 -0
- package/dist/commands/admin.js +271 -0
- package/dist/commands/admin.js.map +1 -0
- package/dist/commands/app.d.ts +3 -0
- package/dist/commands/app.d.ts.map +1 -0
- package/dist/commands/app.js +82 -0
- package/dist/commands/app.js.map +1 -0
- 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 +23 -0
- package/dist/commands/cloud.d.ts.map +1 -0
- package/dist/commands/cloud.js +254 -0
- package/dist/commands/cloud.js.map +1 -0
- package/dist/commands/db.d.ts +8 -0
- package/dist/commands/db.d.ts.map +1 -0
- package/dist/commands/db.js +116 -0
- package/dist/commands/db.js.map +1 -0
- package/dist/commands/deploy-types.d.ts +14 -0
- package/dist/commands/deploy-types.d.ts.map +1 -0
- package/dist/commands/deploy-types.js +38 -0
- package/dist/commands/deploy-types.js.map +1 -0
- package/dist/commands/deploy.d.ts +15 -0
- package/dist/commands/deploy.d.ts.map +1 -0
- package/dist/commands/deploy.js +322 -0
- package/dist/commands/deploy.js.map +1 -0
- package/dist/commands/dev.d.ts +14 -0
- package/dist/commands/dev.d.ts.map +1 -0
- package/dist/commands/dev.js +806 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/diff.d.ts +3 -0
- package/dist/commands/diff.d.ts.map +1 -0
- package/dist/commands/diff.js +54 -0
- package/dist/commands/diff.js.map +1 -0
- package/dist/commands/engine.d.ts +7 -0
- package/dist/commands/engine.d.ts.map +1 -0
- package/dist/commands/engine.js +27 -0
- package/dist/commands/engine.js.map +1 -0
- package/dist/commands/functions.d.ts +3 -0
- package/dist/commands/functions.d.ts.map +1 -0
- package/dist/commands/functions.js +749 -0
- package/dist/commands/functions.js.map +1 -0
- package/dist/commands/generate.d.ts +3 -0
- package/dist/commands/generate.d.ts.map +1 -0
- package/dist/commands/generate.js +38 -0
- package/dist/commands/generate.js.map +1 -0
- package/dist/commands/init.d.ts +7 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +228 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/keys.d.ts +4 -0
- package/dist/commands/keys.d.ts.map +1 -0
- package/dist/commands/keys.js +57 -0
- package/dist/commands/keys.js.map +1 -0
- package/dist/commands/logs.d.ts +6 -0
- package/dist/commands/logs.d.ts.map +1 -0
- package/dist/commands/logs.js +52 -0
- package/dist/commands/logs.js.map +1 -0
- 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 +3 -0
- package/dist/commands/migrate.d.ts.map +1 -0
- package/dist/commands/migrate.js +75 -0
- package/dist/commands/migrate.js.map +1 -0
- 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/plugins.d.ts +3 -0
- package/dist/commands/plugins.d.ts.map +1 -0
- package/dist/commands/plugins.js +431 -0
- package/dist/commands/plugins.js.map +1 -0
- package/dist/commands/pull.d.ts +3 -0
- package/dist/commands/pull.d.ts.map +1 -0
- package/dist/commands/pull.js +12 -0
- package/dist/commands/pull.js.map +1 -0
- package/dist/commands/push.d.ts +3 -0
- package/dist/commands/push.d.ts.map +1 -0
- package/dist/commands/push.js +179 -0
- package/dist/commands/push.js.map +1 -0
- package/dist/commands/seed.d.ts +5 -0
- package/dist/commands/seed.d.ts.map +1 -0
- package/dist/commands/seed.js +55 -0
- package/dist/commands/seed.js.map +1 -0
- package/dist/commands/self-host.d.ts +9 -0
- package/dist/commands/self-host.d.ts.map +1 -0
- package/dist/commands/self-host.js +310 -0
- package/dist/commands/self-host.js.map +1 -0
- 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 +6 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +70 -0
- package/dist/commands/status.js.map +1 -0
- 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 +118 -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 +65 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +134 -0
- package/dist/config.js.map +1 -0
- package/dist/dev-compose.d.ts +19 -0
- package/dist/dev-compose.d.ts.map +1 -0
- package/dist/dev-compose.js +468 -0
- package/dist/dev-compose.js.map +1 -0
- package/dist/dev-log-bus.d.ts +30 -0
- package/dist/dev-log-bus.d.ts.map +1 -0
- package/dist/dev-log-bus.js +87 -0
- package/dist/dev-log-bus.js.map +1 -0
- package/dist/dev-log-filter.d.ts +10 -0
- package/dist/dev-log-filter.d.ts.map +1 -0
- package/dist/dev-log-filter.js +36 -0
- package/dist/dev-log-filter.js.map +1 -0
- package/dist/dev-logo.d.ts +12 -0
- package/dist/dev-logo.d.ts.map +1 -0
- package/dist/dev-logo.js +57 -0
- package/dist/dev-logo.js.map +1 -0
- package/dist/dev-session.d.ts +26 -0
- package/dist/dev-session.d.ts.map +1 -0
- package/dist/dev-session.js +106 -0
- package/dist/dev-session.js.map +1 -0
- package/dist/dev-shutdown.d.ts +9 -0
- package/dist/dev-shutdown.d.ts.map +1 -0
- package/dist/dev-shutdown.js +50 -0
- package/dist/dev-shutdown.js.map +1 -0
- package/dist/dev-task-colors.d.ts +14 -0
- package/dist/dev-task-colors.d.ts.map +1 -0
- package/dist/dev-task-colors.js +44 -0
- package/dist/dev-task-colors.js.map +1 -0
- package/dist/dev-tui.d.ts +24 -0
- package/dist/dev-tui.d.ts.map +1 -0
- package/dist/dev-tui.js +188 -0
- package/dist/dev-tui.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/engine-push-output.d.ts +16 -0
- package/dist/engine-push-output.d.ts.map +1 -0
- package/dist/engine-push-output.js +61 -0
- package/dist/engine-push-output.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 +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/jwt.d.ts +3 -0
- package/dist/jwt.d.ts.map +1 -0
- package/dist/jwt.js +13 -0
- package/dist/jwt.js.map +1 -0
- 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 +49 -0
- package/dist/process-manager.d.ts.map +1 -0
- package/dist/process-manager.js +177 -0
- package/dist/process-manager.js.map +1 -0
- package/dist/project-config.d.ts +238 -0
- package/dist/project-config.d.ts.map +1 -0
- package/dist/project-config.js +159 -0
- package/dist/project-config.js.map +1 -0
- package/dist/pull-utils.d.ts +31 -0
- package/dist/pull-utils.d.ts.map +1 -0
- package/dist/pull-utils.js +77 -0
- package/dist/pull-utils.js.map +1 -0
- 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/restore-system-relation-targets.d.ts +3 -0
- package/dist/restore-system-relation-targets.d.ts.map +1 -0
- package/dist/restore-system-relation-targets.js +45 -0
- package/dist/restore-system-relation-targets.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 +11 -0
- package/dist/scripts/postinstall.d.ts.map +1 -0
- package/dist/scripts/postinstall.js +47 -0
- package/dist/scripts/postinstall.js.map +1 -0
- package/dist/seed.d.ts +8 -0
- package/dist/seed.d.ts.map +1 -0
- package/dist/seed.js +32 -0
- package/dist/seed.js.map +1 -0
- package/dist/self-host-compose.d.ts +43 -0
- package/dist/self-host-compose.d.ts.map +1 -0
- package/dist/self-host-compose.js +400 -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/supatype-eval-1781522769253.d.mts +2 -0
- package/dist/supatype-eval-1781522769253.d.mts.map +1 -0
- package/dist/supatype-eval-1781522769253.mjs +3 -0
- package/dist/supatype-eval-1781522769253.mjs.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 +18 -0
- package/dist/tsx-runner.d.ts.map +1 -0
- package/dist/tsx-runner.js +69 -0
- package/dist/tsx-runner.js.map +1 -0
- 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 +41 -0
- package/releases/deno/VERSION +1 -0
- package/scripts/mirror-deno-release.sh +76 -0
- package/src/TYPE-RESOLUTION.md +294 -0
- package/src/app/framework.ts +249 -0
- package/src/app/proxy-dev-app.ts +68 -0
- package/src/app-config.ts +128 -0
- package/src/augmentation-generator.ts +126 -0
- package/src/binary-cache.ts +845 -0
- package/src/cli.ts +63 -0
- package/src/commands/admin.ts +372 -0
- package/src/commands/app.ts +97 -0
- package/src/commands/cache.ts +117 -0
- package/src/commands/cloud.ts +325 -0
- package/src/commands/db.ts +136 -0
- package/src/commands/deploy-types.ts +49 -0
- package/src/commands/deploy.ts +400 -0
- package/src/commands/dev.ts +1009 -0
- package/src/commands/diff.ts +63 -0
- package/src/commands/engine.ts +30 -0
- package/src/commands/functions.ts +901 -0
- package/src/commands/generate.ts +44 -0
- package/src/commands/init.ts +253 -0
- package/src/commands/keys.ts +66 -0
- package/src/commands/logs.ts +58 -0
- package/src/commands/migrate-from-v1.ts +131 -0
- package/src/commands/migrate.ts +87 -0
- package/src/commands/pg.ts +133 -0
- package/src/commands/plugins.ts +508 -0
- package/src/commands/pull.ts +17 -0
- package/src/commands/push.ts +226 -0
- package/src/commands/seed.ts +68 -0
- package/src/commands/self-host.ts +364 -0
- package/src/commands/self-update.ts +45 -0
- package/src/commands/status.ts +84 -0
- package/src/commands/types.ts +76 -0
- package/src/commands/update.ts +136 -0
- package/src/components.ts +6 -0
- package/src/config.ts +223 -0
- package/src/dev-compose.ts +583 -0
- package/src/dev-log-bus.ts +101 -0
- package/src/dev-log-filter.ts +32 -0
- package/src/dev-logo.ts +62 -0
- package/src/dev-session.ts +130 -0
- package/src/dev-shutdown.ts +54 -0
- package/src/dev-task-colors.ts +47 -0
- package/src/dev-tui.ts +232 -0
- package/src/diff-output.ts +12 -0
- package/src/docker-postgres.ts +295 -0
- package/src/engine-client.ts +236 -0
- package/src/engine-push-output.ts +71 -0
- package/src/ensure-binary.ts +28 -0
- package/src/functions-router-gen.ts +224 -0
- package/src/index.ts +11 -0
- package/src/jwt.ts +14 -0
- 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 +220 -0
- package/src/project-config.ts +388 -0
- package/src/pull-utils.ts +81 -0
- package/src/release-pins.ts +31 -0
- package/src/release-public-key.ts +12 -0
- package/src/restore-system-relation-targets.ts +45 -0
- package/src/runtime-routes.ts +291 -0
- package/src/schema-ast-v2.ts +324 -0
- package/src/scripts/postinstall.ts +51 -0
- package/src/seed.ts +43 -0
- package/src/self-host-compose.ts +452 -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/supatype-eval-1781522769253.mts +1 -0
- package/src/systemd.ts +137 -0
- package/src/tsx-runner.ts +89 -0
- 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 +133 -0
- package/tests/config.test.ts +252 -0
- package/tests/dev-ui.test.ts +139 -0
- package/tests/docker-postgres.test.ts +39 -0
- package/tests/engine-distribution.test.ts +418 -0
- package/tests/engine-push-output.test.ts +67 -0
- package/tests/ensure-binary.test.ts +59 -0
- package/tests/init.test.ts +127 -0
- package/tests/keys.test.ts +160 -0
- 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 +150 -0
- package/tests/release-pins.test.ts +28 -0
- package/tests/runtime-contract.test.ts +370 -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/tsx-runner.test.ts +66 -0
- package/tests/type-extractor.test.ts +985 -0
- package/tests/type-resolver.test.ts +59 -0
- package/tsconfig.json +10 -0
- package/tsconfig.tsbuildinfo +1 -0
- package/vitest.config.ts +12 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
import type { Command } from "commander"
|
|
2
|
+
import { mkdirSync, writeFileSync } from "node:fs"
|
|
3
|
+
import { createInterface } from "node:readline"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
import { loadConfig, loadSchemaAst } from "../config.js"
|
|
6
|
+
import { connectionString, resolveRuntimeProvider, schemaPathFromProject, serverBaseUrl } from "../project-config.js"
|
|
7
|
+
import { isCloudLinked, pushSchemaToLinkedProject } from "./cloud.js"
|
|
8
|
+
import { ensureEngine, engineRequest, type DiffResult, type Operation } from "../engine-client.js"
|
|
9
|
+
import { printDiffWarnings } from "../diff-output.js"
|
|
10
|
+
import { signJwt } from "../jwt.js"
|
|
11
|
+
import { provisionBuckets } from "../storage-provision.js"
|
|
12
|
+
import { promptFirstAdminUser } from "./admin.js"
|
|
13
|
+
import { withAdminRoles } from "../studio-admin-roles.js"
|
|
14
|
+
import { restoreSystemRelationTargets } from "../restore-system-relation-targets.js"
|
|
15
|
+
import type { SupatypeProjectConfig } from "../project-config.js"
|
|
16
|
+
|
|
17
|
+
const DEV_JWT_SECRET = "super-secret-jwt-token-with-at-least-32-characters-long"
|
|
18
|
+
|
|
19
|
+
export function registerPush(program: Command): void {
|
|
20
|
+
program
|
|
21
|
+
.command("push")
|
|
22
|
+
.description(
|
|
23
|
+
"Push schema to the database: diff, prompt for destructive changes, apply migration, generate types",
|
|
24
|
+
)
|
|
25
|
+
.option("--yes", "Skip confirmation prompts for destructive changes")
|
|
26
|
+
.option("--connection <url>", "Database connection URL (overrides config)")
|
|
27
|
+
.action(async (opts: { yes?: boolean; connection?: string }) => {
|
|
28
|
+
const cwd = process.cwd()
|
|
29
|
+
|
|
30
|
+
if (isCloudLinked(cwd)) {
|
|
31
|
+
if (opts.connection) {
|
|
32
|
+
console.error("--connection is not allowed when linked to a cloud project (credentials stay server-side).")
|
|
33
|
+
process.exit(1)
|
|
34
|
+
}
|
|
35
|
+
await pushSchemaToLinkedProject(cwd, { force: opts.yes ?? true })
|
|
36
|
+
return
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const config = loadConfig(cwd)
|
|
40
|
+
|
|
41
|
+
// Docker provider: the compose Postgres isn't published to the host, so
|
|
42
|
+
// apply the schema through the in-compose schema-engine (unless the user
|
|
43
|
+
// gave an explicit --connection to a reachable database).
|
|
44
|
+
if (!opts.connection && resolveRuntimeProvider(config) === "docker") {
|
|
45
|
+
const { pushSchemaDocker } = await import("../dev-compose.js")
|
|
46
|
+
await pushSchemaDocker(cwd, config)
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const connection = opts.connection ?? connectionString(config)
|
|
51
|
+
|
|
52
|
+
await ensureEngine()
|
|
53
|
+
|
|
54
|
+
console.log("Loading schema...")
|
|
55
|
+
const ast = loadSchemaAst(schemaPathFromProject(config, cwd), cwd)
|
|
56
|
+
|
|
57
|
+
console.log("Diffing against database...")
|
|
58
|
+
const diff = await engineRequest<DiffResult>("/diff", {
|
|
59
|
+
ast,
|
|
60
|
+
database_url: connection,
|
|
61
|
+
schema: "public",
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
const ops = diff.operations ?? []
|
|
65
|
+
printDiffWarnings(diff)
|
|
66
|
+
|
|
67
|
+
if (ops.length === 0) {
|
|
68
|
+
console.log(
|
|
69
|
+
"Schema matches the database (no DDL). Syncing Studio metadata...",
|
|
70
|
+
)
|
|
71
|
+
} else {
|
|
72
|
+
printDiff(ops)
|
|
73
|
+
|
|
74
|
+
const risky = ops.filter(
|
|
75
|
+
(o) => o.risk === "cautious" || o.risk === "destructive" || o.risk === "warn" || o.risk === "danger",
|
|
76
|
+
)
|
|
77
|
+
if (risky.length > 0 && !opts.yes) {
|
|
78
|
+
const confirmed = await confirm(
|
|
79
|
+
`\n${risky.length} risky operation(s) above (type changes or data loss). Proceed? [y/N] `,
|
|
80
|
+
)
|
|
81
|
+
if (!confirmed) {
|
|
82
|
+
console.log("Aborted.")
|
|
83
|
+
return
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log(ops.length > 0 ? "\nApplying migration..." : "\nSyncing with engine...")
|
|
89
|
+
const pushResult = await engineRequest<{
|
|
90
|
+
message?: string
|
|
91
|
+
status?: string
|
|
92
|
+
admin_refreshed?: boolean
|
|
93
|
+
}>("/push", {
|
|
94
|
+
ast,
|
|
95
|
+
database_url: connection,
|
|
96
|
+
schema: "public",
|
|
97
|
+
force: true,
|
|
98
|
+
})
|
|
99
|
+
if (pushResult.status === "up_to_date") {
|
|
100
|
+
console.log(
|
|
101
|
+
pushResult.admin_refreshed
|
|
102
|
+
? "Database schema unchanged — Studio metadata synced."
|
|
103
|
+
: "Schema is up to date.",
|
|
104
|
+
)
|
|
105
|
+
} else {
|
|
106
|
+
console.log(pushResult.message ?? "Migration applied.")
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
await writeLocalAdminConfig(ast, config)
|
|
110
|
+
|
|
111
|
+
// After a DDL migration, check if this is the first push and offer to create an
|
|
112
|
+
// admin user if none exist (Gap Appendices task 48).
|
|
113
|
+
if (ops.length > 0) {
|
|
114
|
+
await promptFirstAdminUser(connection)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Provision storage buckets declared in the schema.
|
|
118
|
+
const baseUrl = serverBaseUrl(config)
|
|
119
|
+
const serviceRoleKey =
|
|
120
|
+
process.env["SUPATYPE_SERVICE_ROLE_KEY"] ??
|
|
121
|
+
(config.server.mode === "dev"
|
|
122
|
+
? signJwt({ role: "service_role", iss: "supatype", iat: Math.floor(Date.now() / 1000) }, DEV_JWT_SECRET)
|
|
123
|
+
: undefined)
|
|
124
|
+
|
|
125
|
+
if (baseUrl && serviceRoleKey) {
|
|
126
|
+
const parsedAst = await engineRequest<{
|
|
127
|
+
storageBuckets?: Array<{
|
|
128
|
+
id: string
|
|
129
|
+
public: boolean
|
|
130
|
+
accessMode?: "public" | "private" | "custom"
|
|
131
|
+
allowedMimeTypes?: string[]
|
|
132
|
+
fileSizeLimit?: number
|
|
133
|
+
s3BucketPolicy?: string
|
|
134
|
+
}>
|
|
135
|
+
}>("/parse", { ast })
|
|
136
|
+
const buckets = (parsedAst.storageBuckets ?? []).map((b) => ({
|
|
137
|
+
id: b.id,
|
|
138
|
+
public: b.public,
|
|
139
|
+
...(b.accessMode !== undefined && { access_mode: b.accessMode }),
|
|
140
|
+
...(b.allowedMimeTypes != null && { allowed_mime_types: b.allowedMimeTypes }),
|
|
141
|
+
...(b.fileSizeLimit != null && { file_size_limit: b.fileSizeLimit }),
|
|
142
|
+
...(b.s3BucketPolicy != null &&
|
|
143
|
+
b.s3BucketPolicy !== "" && { s3_bucket_policy: b.s3BucketPolicy }),
|
|
144
|
+
}))
|
|
145
|
+
if (buckets.length > 0) {
|
|
146
|
+
console.log("Provisioning storage buckets...")
|
|
147
|
+
await provisionBuckets(`${baseUrl}/storage/v1`, serviceRoleKey, buckets)
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (config.output?.types ?? config.output?.client) {
|
|
152
|
+
console.log("Generating types...")
|
|
153
|
+
const genBody: Record<string, unknown> = { ast, lang: "typescript" }
|
|
154
|
+
if (config.output?.types) genBody["types_path"] = config.output.types
|
|
155
|
+
if (config.output?.client) genBody["client_path"] = config.output.client
|
|
156
|
+
|
|
157
|
+
const genResult = await engineRequest<{ code?: string; message?: string }>("/generate", genBody)
|
|
158
|
+
console.log(genResult.message ?? "Types generated.")
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const studioBase = baseUrl?.replace(/\/$/, "") ?? ""
|
|
162
|
+
if (studioBase) {
|
|
163
|
+
console.log(`\nStudio: ${studioBase}/studio/ — sign in with the admin user you created.`)
|
|
164
|
+
} else {
|
|
165
|
+
console.log("\nDone.")
|
|
166
|
+
}
|
|
167
|
+
})
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function printDiff(ops: Operation[]): void {
|
|
171
|
+
const symbol: Record<string, string> = {
|
|
172
|
+
safe: "+",
|
|
173
|
+
warn: "~",
|
|
174
|
+
cautious: "~",
|
|
175
|
+
danger: "!",
|
|
176
|
+
destructive: "!",
|
|
177
|
+
}
|
|
178
|
+
console.log(`\n${ops.length} change(s) planned:\n`)
|
|
179
|
+
for (const op of ops) {
|
|
180
|
+
const riskKey = op.risk ?? "safe"
|
|
181
|
+
const s = symbol[riskKey] ?? "?"
|
|
182
|
+
const label = op.warning ?? op.description ?? formatOperation(op)
|
|
183
|
+
console.log(` [${s}] ${label}`)
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function formatOperation(op: Operation): string {
|
|
188
|
+
if (typeof op.description === "string" && op.description.trim().length > 0) {
|
|
189
|
+
return op.description
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const kind = typeof op.kind === "string" ? op.kind : "operation"
|
|
193
|
+
const raw = op as unknown as Record<string, unknown>
|
|
194
|
+
const table = raw["table"]
|
|
195
|
+
const column = raw["column"]
|
|
196
|
+
|
|
197
|
+
if (typeof table === "string" && typeof column === "string") {
|
|
198
|
+
return `${kind} ${table}.${column}`
|
|
199
|
+
}
|
|
200
|
+
if (typeof table === "string") {
|
|
201
|
+
return `${kind} ${table}`
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Last resort: show operation kind with compact payload.
|
|
205
|
+
return `${kind} ${JSON.stringify(op)}`
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function confirm(prompt: string): Promise<boolean> {
|
|
209
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout })
|
|
210
|
+
return new Promise((resolve) => {
|
|
211
|
+
rl.question(prompt, (answer) => {
|
|
212
|
+
rl.close()
|
|
213
|
+
resolve(answer.toLowerCase() === "y")
|
|
214
|
+
})
|
|
215
|
+
})
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/** Write `.supatype/admin-config.json` for local Studio (same layout as `supatype dev`). */
|
|
219
|
+
async function writeLocalAdminConfig(ast: unknown, config: SupatypeProjectConfig): Promise<void> {
|
|
220
|
+
const cwd = process.cwd()
|
|
221
|
+
const dir = join(cwd, ".supatype")
|
|
222
|
+
mkdirSync(dir, { recursive: true })
|
|
223
|
+
const admin = withAdminRoles(await engineRequest<unknown>("/admin", { ast }), config)
|
|
224
|
+
restoreSystemRelationTargets(admin, ast)
|
|
225
|
+
writeFileSync(join(dir, "admin-config.json"), `${JSON.stringify(admin, null, 2)}\n`)
|
|
226
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { Command } from "commander"
|
|
2
|
+
import { existsSync, readdirSync } from "node:fs"
|
|
3
|
+
import { join, resolve } from "node:path"
|
|
4
|
+
import { isLinkedToCloudProject } from "../binary-cache.js"
|
|
5
|
+
import { loadConfig } from "../config.js"
|
|
6
|
+
import { projectRootFromConfig } from "../project-config.js"
|
|
7
|
+
import { runTsFile } from "../tsx-runner.js"
|
|
8
|
+
|
|
9
|
+
const SEED_EXT = /\.(ts|mts|tsx)$/
|
|
10
|
+
|
|
11
|
+
/** Seed entries under `seeds/`, sorted by filename (Phase 10.6 C19). */
|
|
12
|
+
export function discoverSeedsDir(cwd: string, seedsDir: string): string[] {
|
|
13
|
+
if (!existsSync(seedsDir)) return []
|
|
14
|
+
const names = readdirSync(seedsDir).filter((n) => SEED_EXT.test(n))
|
|
15
|
+
names.sort((a, b) => a.localeCompare(b))
|
|
16
|
+
return names.map((n) => join(seedsDir, n))
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function registerSeed(program: Command): void {
|
|
20
|
+
program
|
|
21
|
+
.command("seed [file]")
|
|
22
|
+
.description(
|
|
23
|
+
"Run database seeds: optional single file; else all seeds/*.ts (alphabetical); else seed.ts",
|
|
24
|
+
)
|
|
25
|
+
.option(
|
|
26
|
+
"--force",
|
|
27
|
+
"Allow running when the project is linked to Supatype Cloud (dangerous)",
|
|
28
|
+
false,
|
|
29
|
+
)
|
|
30
|
+
.action(async (file: string | undefined, opts: { force: boolean }) => {
|
|
31
|
+
const cwd = process.cwd()
|
|
32
|
+
const config = loadConfig(cwd)
|
|
33
|
+
if (isLinkedToCloudProject(cwd, config) && !opts.force) {
|
|
34
|
+
console.error(
|
|
35
|
+
"[supatype] This project is linked to Supatype Cloud. Refusing to run seeds locally.\n" +
|
|
36
|
+
" Pass --force only if you intend to target this linked project (advanced).",
|
|
37
|
+
)
|
|
38
|
+
process.exit(1)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const root = projectRootFromConfig(config, cwd)
|
|
42
|
+
const seedsDir = join(root, "seeds")
|
|
43
|
+
|
|
44
|
+
let paths: string[]
|
|
45
|
+
if (file !== undefined && file.trim() !== "") {
|
|
46
|
+
paths = [resolve(cwd, file)]
|
|
47
|
+
} else {
|
|
48
|
+
paths = discoverSeedsDir(cwd, seedsDir)
|
|
49
|
+
if (paths.length === 0) {
|
|
50
|
+
paths = [resolve(root, "seed.ts")]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const missing = paths.filter((p) => !existsSync(p))
|
|
55
|
+
if (missing.length > 0) {
|
|
56
|
+
console.error(`Seed file(s) not found:\n ${missing.join("\n ")}`)
|
|
57
|
+
process.exit(1)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
for (const seedFile of paths) {
|
|
61
|
+
console.log(`[supatype] Running ${seedFile}...`)
|
|
62
|
+
const result = runTsFile(seedFile, { cwd, stdio: "inherit" })
|
|
63
|
+
if (result.exitCode !== 0) {
|
|
64
|
+
process.exit(result.exitCode)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
})
|
|
68
|
+
}
|
|
@@ -0,0 +1,364 @@
|
|
|
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
|
+
|
|
8
|
+
import { Command } from "commander"
|
|
9
|
+
import { existsSync, readFileSync, mkdirSync, copyFileSync, writeFileSync } from "node:fs"
|
|
10
|
+
import { join, resolve } from "node:path"
|
|
11
|
+
import { homedir } from "node:os"
|
|
12
|
+
import { spawnSync } from "node:child_process"
|
|
13
|
+
import { gzipSync } from "node:zlib"
|
|
14
|
+
import { loadConfig } from "../config.js"
|
|
15
|
+
import { connectionString } from "../project-config.js"
|
|
16
|
+
import { resolveBinary } from "../binary-cache.js"
|
|
17
|
+
import { generateUnits } from "../systemd.js"
|
|
18
|
+
import { readPid } from "../process-manager.js"
|
|
19
|
+
import { localStorageEnv } from "../local-storage.js"
|
|
20
|
+
import { runDockerCompose, writeSelfHostCompose } from "../self-host-compose.js"
|
|
21
|
+
|
|
22
|
+
export function registerSelfHost(program: Command): void {
|
|
23
|
+
const selfHostCmd = program
|
|
24
|
+
.command("self-host")
|
|
25
|
+
.description("Manage self-hosted deployments (Docker Compose only)")
|
|
26
|
+
|
|
27
|
+
const composeCmd = selfHostCmd
|
|
28
|
+
.command("compose")
|
|
29
|
+
.description("Manage compose-based self-host runtime")
|
|
30
|
+
|
|
31
|
+
composeCmd
|
|
32
|
+
.command("render")
|
|
33
|
+
.description("Render deterministic self-host compose artifacts")
|
|
34
|
+
.action(() => {
|
|
35
|
+
const cwd = process.cwd()
|
|
36
|
+
const config = loadConfig(cwd)
|
|
37
|
+
const out = writeSelfHostCompose(cwd, config)
|
|
38
|
+
console.log(`Wrote ${out.composePath}`)
|
|
39
|
+
console.log(`Wrote ${out.kongPath}`)
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
composeCmd
|
|
43
|
+
.command("up")
|
|
44
|
+
.description("Render and start compose services")
|
|
45
|
+
.option("-d, --detach", "Start in detached mode", true)
|
|
46
|
+
.action((opts: { detach?: boolean }) => {
|
|
47
|
+
const cwd = process.cwd()
|
|
48
|
+
const config = loadConfig(cwd)
|
|
49
|
+
const out = writeSelfHostCompose(cwd, config)
|
|
50
|
+
const status = runDockerCompose(out.composePath, opts.detach ? ["up", "-d"] : ["up"], cwd)
|
|
51
|
+
process.exitCode = status
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
composeCmd
|
|
55
|
+
.command("down")
|
|
56
|
+
.description("Stop compose services")
|
|
57
|
+
.action(() => {
|
|
58
|
+
const cwd = process.cwd()
|
|
59
|
+
const config = loadConfig(cwd)
|
|
60
|
+
const out = writeSelfHostCompose(cwd, config)
|
|
61
|
+
process.exitCode = runDockerCompose(out.composePath, ["down"], cwd)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
composeCmd
|
|
65
|
+
.command("status")
|
|
66
|
+
.description("Show compose service status")
|
|
67
|
+
.action(() => {
|
|
68
|
+
const cwd = process.cwd()
|
|
69
|
+
const config = loadConfig(cwd)
|
|
70
|
+
const out = writeSelfHostCompose(cwd, config)
|
|
71
|
+
process.exitCode = runDockerCompose(out.composePath, ["ps"], cwd)
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
composeCmd
|
|
75
|
+
.command("logs")
|
|
76
|
+
.description("Tail compose logs")
|
|
77
|
+
.option("--service <name>", "Filter to one service")
|
|
78
|
+
.option("-f, --follow", "Follow log output", true)
|
|
79
|
+
.action((opts: { service?: string; follow?: boolean }) => {
|
|
80
|
+
const cwd = process.cwd()
|
|
81
|
+
const config = loadConfig(cwd)
|
|
82
|
+
const out = writeSelfHostCompose(cwd, config)
|
|
83
|
+
const args = ["logs"]
|
|
84
|
+
if (opts.follow) args.push("-f")
|
|
85
|
+
if (opts.service) args.push(opts.service)
|
|
86
|
+
process.exitCode = runDockerCompose(out.composePath, args, cwd)
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
// ── Legacy native/systemd helpers (hidden; use compose for self-host) ─────
|
|
90
|
+
|
|
91
|
+
const legacyCmd = new Command("native")
|
|
92
|
+
selfHostCmd.addCommand(legacyCmd, { hidden: true })
|
|
93
|
+
|
|
94
|
+
legacyCmd
|
|
95
|
+
.command(
|
|
96
|
+
"install-service",
|
|
97
|
+
"Generate systemd unit files and (on Linux) install + enable them",
|
|
98
|
+
)
|
|
99
|
+
.option("--output-dir <path>", "Write unit files here instead of /etc/systemd/system/")
|
|
100
|
+
.option("--user <name>", "User to run services as")
|
|
101
|
+
.option("--no-enable", "Generate unit files but do not enable/start them")
|
|
102
|
+
.action(
|
|
103
|
+
async (opts: {
|
|
104
|
+
outputDir?: string
|
|
105
|
+
user?: string
|
|
106
|
+
enable: boolean
|
|
107
|
+
}) => {
|
|
108
|
+
logLegacyWarning("install-service")
|
|
109
|
+
const cwd = process.cwd()
|
|
110
|
+
const config = loadConfig(cwd)
|
|
111
|
+
|
|
112
|
+
const systemdDir = opts.outputDir ?? ".supatype/systemd"
|
|
113
|
+
const absSystemdDir = resolve(cwd, systemdDir)
|
|
114
|
+
|
|
115
|
+
console.log("Generating systemd unit files...")
|
|
116
|
+
const { postgres, server } = generateUnits(config, cwd, {
|
|
117
|
+
outputDir: absSystemdDir,
|
|
118
|
+
...(opts.user !== undefined && { user: opts.user }),
|
|
119
|
+
})
|
|
120
|
+
console.log(` wrote ${postgres}`)
|
|
121
|
+
console.log(` wrote ${server}`)
|
|
122
|
+
|
|
123
|
+
if (!opts.enable) {
|
|
124
|
+
console.log(
|
|
125
|
+
`\nTo install manually:\n` +
|
|
126
|
+
` sudo cp ${postgres} /etc/systemd/system/\n` +
|
|
127
|
+
` sudo cp ${server} /etc/systemd/system/\n` +
|
|
128
|
+
` sudo systemctl daemon-reload\n` +
|
|
129
|
+
` sudo systemctl enable --now supatype-postgres supatype-server`,
|
|
130
|
+
)
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (process.platform !== "linux") {
|
|
135
|
+
console.log(
|
|
136
|
+
"\nNote: systemd unit installation is only supported on Linux.\n" +
|
|
137
|
+
`Unit files are at ${absSystemdDir}/`,
|
|
138
|
+
)
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Install to /etc/systemd/system/
|
|
143
|
+
console.log("\nInstalling to /etc/systemd/system/ (requires sudo)...")
|
|
144
|
+
const units = [
|
|
145
|
+
{ src: postgres, dest: "/etc/systemd/system/supatype-postgres.service" },
|
|
146
|
+
{ src: server, dest: "/etc/systemd/system/supatype-server.service" },
|
|
147
|
+
]
|
|
148
|
+
for (const { src, dest } of units) {
|
|
149
|
+
const cp = spawnSync("sudo", ["cp", src, dest], { stdio: "inherit" })
|
|
150
|
+
if (cp.status !== 0) {
|
|
151
|
+
console.error(`Failed to copy ${src} to ${dest}`)
|
|
152
|
+
process.exit(1)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const daemonReload = spawnSync("sudo", ["systemctl", "daemon-reload"], { stdio: "inherit" })
|
|
157
|
+
if (daemonReload.status !== 0) { process.exit(1) }
|
|
158
|
+
|
|
159
|
+
const enable = spawnSync(
|
|
160
|
+
"sudo",
|
|
161
|
+
["systemctl", "enable", "--now", "supatype-postgres", "supatype-server"],
|
|
162
|
+
{ stdio: "inherit" },
|
|
163
|
+
)
|
|
164
|
+
if (enable.status !== 0) { process.exit(1) }
|
|
165
|
+
|
|
166
|
+
console.log("\nServices installed and started.")
|
|
167
|
+
console.log(" supatype-postgres.service")
|
|
168
|
+
console.log(" supatype-server.service")
|
|
169
|
+
},
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
// ── serve ──────────────────────────────────────────────────────────────────
|
|
173
|
+
|
|
174
|
+
legacyCmd
|
|
175
|
+
.command("serve", "Start supatype-server in the foreground (for standalone mode)")
|
|
176
|
+
.option("--port <port>", "Override port from config")
|
|
177
|
+
.action(async (opts: { port?: string }) => {
|
|
178
|
+
logLegacyWarning("serve")
|
|
179
|
+
const cwd = process.cwd()
|
|
180
|
+
const config = loadConfig(cwd)
|
|
181
|
+
|
|
182
|
+
const serverBin = await resolveBinary("server", config)
|
|
183
|
+
const port = opts.port ?? String(config.server.port ?? 54321)
|
|
184
|
+
|
|
185
|
+
const args = [
|
|
186
|
+
"--port", port,
|
|
187
|
+
"--mode", config.server.mode,
|
|
188
|
+
...(config.server.domain ? ["--domain", config.server.domain] : []),
|
|
189
|
+
]
|
|
190
|
+
|
|
191
|
+
const stateDir = join(homedir(), ".supatype", "projects", config.project.name)
|
|
192
|
+
const storageEnv = config.storage?.provider !== "s3" ? localStorageEnv(stateDir) : {}
|
|
193
|
+
|
|
194
|
+
console.log(`Starting supatype-server on port ${port}...`)
|
|
195
|
+
const result = spawnSync(serverBin, args, {
|
|
196
|
+
stdio: "inherit",
|
|
197
|
+
cwd,
|
|
198
|
+
env: { ...process.env, ...storageEnv },
|
|
199
|
+
})
|
|
200
|
+
process.exitCode = result.status ?? 1
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
// ── reload ─────────────────────────────────────────────────────────────────
|
|
204
|
+
|
|
205
|
+
legacyCmd
|
|
206
|
+
.command("reload", "Reload the running supatype-server (SIGHUP for config reload)")
|
|
207
|
+
.action(() => {
|
|
208
|
+
logLegacyWarning("reload")
|
|
209
|
+
const cwd = process.cwd()
|
|
210
|
+
const config = loadConfig(cwd)
|
|
211
|
+
const stateDir = join(homedir(), ".supatype", "projects", config.project.name)
|
|
212
|
+
const pid = readPid(join(stateDir, "pid"), "server")
|
|
213
|
+
|
|
214
|
+
if (!pid) {
|
|
215
|
+
// Try systemctl if running as a service
|
|
216
|
+
if (process.platform === "linux") {
|
|
217
|
+
const result = spawnSync("systemctl", ["reload", "supatype-server"], { stdio: "inherit" })
|
|
218
|
+
process.exitCode = result.status ?? 1
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
console.error("Server does not appear to be running (no PID file found).")
|
|
222
|
+
process.exitCode = 1
|
|
223
|
+
return
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
try {
|
|
227
|
+
process.kill(pid, "SIGHUP")
|
|
228
|
+
console.log(`Sent SIGHUP to supatype-server (pid ${pid}).`)
|
|
229
|
+
} catch (err) {
|
|
230
|
+
console.error(`Failed to signal pid ${pid}:`, (err as Error).message)
|
|
231
|
+
process.exitCode = 1
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
|
|
235
|
+
// ── status ─────────────────────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
legacyCmd
|
|
238
|
+
.command("status", "Show running status of supatype services")
|
|
239
|
+
.action(() => {
|
|
240
|
+
logLegacyWarning("status")
|
|
241
|
+
const cwd = process.cwd()
|
|
242
|
+
const config = loadConfig(cwd)
|
|
243
|
+
const stateDir = join(homedir(), ".supatype", "projects", config.project.name)
|
|
244
|
+
|
|
245
|
+
console.log(`Project: ${config.project.name}\n`)
|
|
246
|
+
|
|
247
|
+
if (process.platform === "linux" && existsSync("/run/systemd/system")) {
|
|
248
|
+
// systemd is active
|
|
249
|
+
for (const svc of ["supatype-postgres", "supatype-server"]) {
|
|
250
|
+
const result = spawnSync("systemctl", ["status", "--no-pager", "--lines=0", svc], {
|
|
251
|
+
encoding: "utf8",
|
|
252
|
+
})
|
|
253
|
+
const active = result.stdout?.includes("active (running)") ? "running" : "stopped"
|
|
254
|
+
console.log(` ${svc}: ${active}`)
|
|
255
|
+
}
|
|
256
|
+
} else {
|
|
257
|
+
// PID file check
|
|
258
|
+
const serverPid = readPid(join(stateDir, "pid"), "server")
|
|
259
|
+
const pgPid = readPid(join(stateDir, "pid"), "postgres")
|
|
260
|
+
console.log(` postgres: ${pgPid ? `running (pid ${pgPid})` : "stopped"}`)
|
|
261
|
+
console.log(` supatype-server: ${serverPid ? `running (pid ${serverPid})` : "stopped"}`)
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const logDir = join(stateDir, "logs")
|
|
265
|
+
if (existsSync(logDir)) {
|
|
266
|
+
console.log(`\nLogs: ${logDir}`)
|
|
267
|
+
}
|
|
268
|
+
})
|
|
269
|
+
|
|
270
|
+
// ── logs ───────────────────────────────────────────────────────────────────
|
|
271
|
+
|
|
272
|
+
selfHostCmd
|
|
273
|
+
.command("logs", "Tail supatype service logs", { hidden: true })
|
|
274
|
+
.option("--service <name>", "Show logs for: postgres | server")
|
|
275
|
+
.option("--lines <n>", "Number of lines to show", "50")
|
|
276
|
+
.option("-f, --follow", "Follow log output")
|
|
277
|
+
.action((opts: { service?: string; lines: string; follow?: boolean }) => {
|
|
278
|
+
logLegacyWarning("logs")
|
|
279
|
+
const cwd = process.cwd()
|
|
280
|
+
const config = loadConfig(cwd)
|
|
281
|
+
const stateDir = join(homedir(), ".supatype", "projects", config.project.name)
|
|
282
|
+
const logDir = join(stateDir, "logs")
|
|
283
|
+
|
|
284
|
+
if (process.platform === "linux" && existsSync("/run/systemd/system")) {
|
|
285
|
+
const args = ["--no-pager", "--lines", opts.lines]
|
|
286
|
+
if (opts.follow) args.push("--follow")
|
|
287
|
+
if (opts.service) args.push(`-u`, `supatype-${opts.service}`)
|
|
288
|
+
else args.push("-u", "supatype-postgres", "-u", "supatype-server")
|
|
289
|
+
spawnSync("journalctl", args, { stdio: "inherit" })
|
|
290
|
+
return
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// File-based logs
|
|
294
|
+
const targets: Array<{ label: string; file: string }> = []
|
|
295
|
+
if (!opts.service || opts.service === "postgres") {
|
|
296
|
+
targets.push({ label: "postgres", file: join(logDir, "postgres.log") })
|
|
297
|
+
}
|
|
298
|
+
if (!opts.service || opts.service === "server") {
|
|
299
|
+
targets.push({ label: "server", file: join(logDir, "server.log") })
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
for (const { label, file } of targets) {
|
|
303
|
+
if (!existsSync(file)) {
|
|
304
|
+
console.log(`[${label}] log file not found: ${file}`)
|
|
305
|
+
continue
|
|
306
|
+
}
|
|
307
|
+
if (opts.follow) {
|
|
308
|
+
const tail = spawnSync("tail", ["-f", "-n", opts.lines, file], { stdio: "inherit" })
|
|
309
|
+
process.exitCode = tail.status ?? 0
|
|
310
|
+
} else {
|
|
311
|
+
const n = parseInt(opts.lines, 10)
|
|
312
|
+
const content = readFileSync(file, "utf8")
|
|
313
|
+
const lines = content.split("\n")
|
|
314
|
+
console.log(lines.slice(-n).join("\n"))
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
// ── backup ─────────────────────────────────────────────────────────────────
|
|
320
|
+
|
|
321
|
+
legacyCmd
|
|
322
|
+
.command("backup", "Create a Postgres dump of the project database")
|
|
323
|
+
.option("--output <path>", "Output file path (default: ./backups/backup-<timestamp>.sql.gz)")
|
|
324
|
+
.option("--connection <url>", "Database connection URL (overrides config)")
|
|
325
|
+
.action((opts: { output?: string; connection?: string }) => {
|
|
326
|
+
logLegacyWarning("backup")
|
|
327
|
+
const cwd = process.cwd()
|
|
328
|
+
const config = loadConfig(cwd)
|
|
329
|
+
const conn = opts.connection ?? connectionString(config)
|
|
330
|
+
const outFile = opts.output ?? resolve(
|
|
331
|
+
cwd,
|
|
332
|
+
"backups",
|
|
333
|
+
`backup-${new Date().toISOString().replace(/[:.]/g, "-")}.sql.gz`,
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
mkdirSync(resolve(outFile, ".."), { recursive: true })
|
|
337
|
+
|
|
338
|
+
console.log(`Backing up database to ${outFile}...`)
|
|
339
|
+
try {
|
|
340
|
+
// Avoid shell interpolation of user-supplied values.
|
|
341
|
+
const pgDump = spawnSync("pg_dump", [conn], {
|
|
342
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
343
|
+
})
|
|
344
|
+
if (pgDump.status !== 0) {
|
|
345
|
+
const stderr = pgDump.stderr?.toString("utf8") ?? ""
|
|
346
|
+
throw new Error(stderr.trim() || "pg_dump failed")
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const compressed = gzipSync(pgDump.stdout)
|
|
350
|
+
writeFileSync(outFile, compressed)
|
|
351
|
+
console.log("Backup complete.")
|
|
352
|
+
} catch (err) {
|
|
353
|
+
console.error("Backup failed:", (err as Error).message)
|
|
354
|
+
process.exit(1)
|
|
355
|
+
}
|
|
356
|
+
})
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
function logLegacyWarning(cmd: string): void {
|
|
360
|
+
console.warn(
|
|
361
|
+
`[supatype] self-host native ${cmd} is deprecated. ` +
|
|
362
|
+
"Use `supatype self-host compose` commands instead.",
|
|
363
|
+
)
|
|
364
|
+
}
|