@supatype/cli 0.1.0-alpha.8 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +2 -2
- package/.turbo/turbo-test.log +274 -69
- package/.turbo/turbo-typecheck.log +1 -1
- package/assets/supatype-logo-wordmark.ascii.txt +6 -0
- package/bin/dev-entry.ts +2 -1
- package/dist/app/framework.js +1 -3
- package/dist/app/framework.js.map +1 -1
- package/dist/app/proxy-dev-app.d.ts +14 -0
- package/dist/app/proxy-dev-app.d.ts.map +1 -1
- package/dist/app/proxy-dev-app.js +110 -6
- package/dist/app/proxy-dev-app.js.map +1 -1
- package/dist/app-config.d.ts +10 -0
- package/dist/app-config.d.ts.map +1 -1
- package/dist/app-config.js +72 -0
- package/dist/app-config.js.map +1 -1
- package/dist/assets/supatype-logo-wordmark.ascii.txt +6 -0
- package/dist/binary-cache.d.ts +19 -7
- package/dist/binary-cache.d.ts.map +1 -1
- package/dist/binary-cache.js +92 -46
- package/dist/binary-cache.js.map +1 -1
- package/dist/cli.d.ts +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +17 -2
- package/dist/cli.js.map +1 -1
- package/dist/commands/add.d.ts +3 -0
- package/dist/commands/add.d.ts.map +1 -0
- package/dist/commands/add.js +86 -0
- package/dist/commands/add.js.map +1 -0
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +39 -53
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/adopt.d.ts +3 -0
- package/dist/commands/adopt.d.ts.map +1 -0
- package/dist/commands/adopt.js +55 -0
- package/dist/commands/adopt.js.map +1 -0
- package/dist/commands/app.d.ts.map +1 -1
- package/dist/commands/app.js +20 -17
- package/dist/commands/app.js.map +1 -1
- package/dist/commands/cache.d.ts.map +1 -1
- package/dist/commands/cache.js +11 -10
- package/dist/commands/cache.js.map +1 -1
- package/dist/commands/cloud.d.ts +4 -9
- package/dist/commands/cloud.d.ts.map +1 -1
- package/dist/commands/cloud.js +75 -125
- package/dist/commands/cloud.js.map +1 -1
- package/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +37 -58
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +140 -96
- package/dist/commands/deploy.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +72 -39
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/diff.d.ts.map +1 -1
- package/dist/commands/diff.js +39 -39
- package/dist/commands/diff.js.map +1 -1
- package/dist/commands/doctor.d.ts +3 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +78 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/engine.d.ts.map +1 -1
- package/dist/commands/engine.js +5 -4
- package/dist/commands/engine.js.map +1 -1
- package/dist/commands/functions.d.ts.map +1 -1
- package/dist/commands/functions.js +172 -119
- package/dist/commands/functions.js.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +5 -4
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/init.d.ts +30 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +814 -107
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/introspect.d.ts +3 -0
- package/dist/commands/introspect.d.ts.map +1 -0
- package/dist/commands/introspect.js +35 -0
- package/dist/commands/introspect.js.map +1 -0
- package/dist/commands/keys.d.ts +15 -1
- package/dist/commands/keys.d.ts.map +1 -1
- package/dist/commands/keys.js +46 -10
- package/dist/commands/keys.js.map +1 -1
- package/dist/commands/link-helpers.d.ts +15 -0
- package/dist/commands/link-helpers.d.ts.map +1 -0
- package/dist/commands/link-helpers.js +225 -0
- package/dist/commands/link-helpers.js.map +1 -0
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +5 -4
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/migrate-from-v1.d.ts.map +1 -1
- package/dist/commands/migrate-from-v1.js +3 -2
- package/dist/commands/migrate-from-v1.js.map +1 -1
- package/dist/commands/migrate.d.ts.map +1 -1
- package/dist/commands/migrate.js +119 -26
- package/dist/commands/migrate.js.map +1 -1
- package/dist/commands/pg.d.ts.map +1 -1
- package/dist/commands/pg.js +11 -12
- package/dist/commands/pg.js.map +1 -1
- package/dist/commands/plugins.d.ts.map +1 -1
- package/dist/commands/plugins.js +55 -46
- package/dist/commands/plugins.js.map +1 -1
- package/dist/commands/pull.d.ts.map +1 -1
- package/dist/commands/pull.js +33 -5
- package/dist/commands/pull.js.map +1 -1
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +110 -137
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/seed.d.ts.map +1 -1
- package/dist/commands/seed.js +4 -3
- package/dist/commands/seed.js.map +1 -1
- package/dist/commands/self-host.d.ts +2 -2
- package/dist/commands/self-host.d.ts.map +1 -1
- package/dist/commands/self-host.js +65 -50
- package/dist/commands/self-host.js.map +1 -1
- package/dist/commands/self-update.d.ts.map +1 -1
- package/dist/commands/self-update.js +3 -2
- package/dist/commands/self-update.js.map +1 -1
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +95 -29
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/types.d.ts.map +1 -1
- package/dist/commands/types.js +3 -2
- package/dist/commands/types.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +54 -21
- package/dist/commands/update.js.map +1 -1
- package/dist/config.d.ts +2 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/dev-compose.d.ts +26 -0
- package/dist/dev-compose.d.ts.map +1 -1
- package/dist/dev-compose.js +328 -34
- package/dist/dev-compose.js.map +1 -1
- 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 +56 -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 +13 -0
- package/dist/dev-task-colors.d.ts.map +1 -0
- package/dist/dev-task-colors.js +43 -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 +5 -1
- package/dist/diff-output.d.ts.map +1 -1
- package/dist/diff-output.js +69 -0
- package/dist/diff-output.js.map +1 -1
- package/dist/docker-runtime.d.ts +30 -0
- package/dist/docker-runtime.d.ts.map +1 -0
- package/dist/docker-runtime.js +118 -0
- package/dist/docker-runtime.js.map +1 -0
- package/dist/engine-client.d.ts +10 -1
- package/dist/engine-client.d.ts.map +1 -1
- package/dist/engine-client.js +76 -17
- package/dist/engine-client.js.map +1 -1
- package/dist/engine-push-output.d.ts +17 -0
- package/dist/engine-push-output.d.ts.map +1 -0
- package/dist/engine-push-output.js +64 -0
- package/dist/engine-push-output.js.map +1 -0
- package/dist/ensure-binary.js +2 -2
- package/dist/ensure-binary.js.map +1 -1
- package/dist/gitignore.d.ts +8 -0
- package/dist/gitignore.d.ts.map +1 -0
- package/dist/gitignore.js +41 -0
- package/dist/gitignore.js.map +1 -0
- package/dist/kong-config.d.ts +9 -0
- package/dist/kong-config.d.ts.map +1 -1
- package/dist/kong-config.js +18 -1
- package/dist/kong-config.js.map +1 -1
- package/dist/link.d.ts +66 -0
- package/dist/link.d.ts.map +1 -0
- package/dist/link.js +160 -0
- package/dist/link.js.map +1 -0
- package/dist/process-manager.d.ts +8 -0
- package/dist/process-manager.d.ts.map +1 -1
- package/dist/process-manager.js +53 -9
- package/dist/process-manager.js.map +1 -1
- package/dist/project-config.d.ts +30 -3
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +37 -4
- package/dist/project-config.js.map +1 -1
- package/dist/prompts.d.ts +3 -0
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +3 -0
- package/dist/prompts.js.map +1 -0
- package/dist/pull-utils.d.ts +50 -14
- package/dist/pull-utils.d.ts.map +1 -1
- package/dist/pull-utils.js +152 -12
- package/dist/pull-utils.js.map +1 -1
- package/dist/resolve-target.d.ts +86 -0
- package/dist/resolve-target.d.ts.map +1 -0
- package/dist/resolve-target.js +291 -0
- package/dist/resolve-target.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.map +1 -1
- package/dist/runtime-routes.js +7 -0
- package/dist/runtime-routes.js.map +1 -1
- package/dist/schema-ast-v2.d.ts +1 -1
- package/dist/schema-ast-v2.d.ts.map +1 -1
- package/dist/schema-ast-v2.js +2 -2
- package/dist/schema-ast-v2.js.map +1 -1
- package/dist/schema-sources.d.ts +40 -0
- package/dist/schema-sources.d.ts.map +1 -0
- package/dist/schema-sources.js +183 -0
- package/dist/schema-sources.js.map +1 -0
- package/dist/scripts/postinstall.js +5 -1
- package/dist/scripts/postinstall.js.map +1 -1
- 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 +37 -1
- package/dist/self-host-compose.d.ts.map +1 -1
- package/dist/self-host-compose.js +233 -43
- package/dist/self-host-compose.js.map +1 -1
- package/dist/storage-provision.d.ts +4 -0
- package/dist/storage-provision.d.ts.map +1 -1
- package/dist/storage-provision.js +24 -2
- package/dist/storage-provision.js.map +1 -1
- 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.js +2 -2
- package/dist/systemd.js.map +1 -1
- package/dist/target-client.d.ts +10 -0
- package/dist/target-client.d.ts.map +1 -0
- package/dist/target-client.js +22 -0
- package/dist/target-client.js.map +1 -0
- package/dist/type-extractor.d.ts +11 -0
- package/dist/type-extractor.d.ts.map +1 -1
- package/dist/type-extractor.js +95 -8
- package/dist/type-extractor.js.map +1 -1
- package/dist/ui/brand.d.ts +9 -0
- package/dist/ui/brand.d.ts.map +1 -0
- package/dist/ui/brand.js +11 -0
- package/dist/ui/brand.js.map +1 -0
- package/dist/ui/confirm.d.ts +12 -0
- package/dist/ui/confirm.d.ts.map +1 -0
- package/dist/ui/confirm.js +28 -0
- package/dist/ui/confirm.js.map +1 -0
- package/dist/ui/fatal.d.ts +10 -0
- package/dist/ui/fatal.d.ts.map +1 -0
- package/dist/ui/fatal.js +34 -0
- package/dist/ui/fatal.js.map +1 -0
- package/dist/ui/index.d.ts +9 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +9 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/interactive.d.ts +3 -0
- package/dist/ui/interactive.d.ts.map +1 -0
- package/dist/ui/interactive.js +5 -0
- package/dist/ui/interactive.js.map +1 -0
- package/dist/ui/messages.d.ts +10 -0
- package/dist/ui/messages.d.ts.map +1 -0
- package/dist/ui/messages.js +35 -0
- package/dist/ui/messages.js.map +1 -0
- package/dist/ui/next-steps.d.ts +3 -0
- package/dist/ui/next-steps.d.ts.map +1 -0
- package/dist/ui/next-steps.js +10 -0
- package/dist/ui/next-steps.js.map +1 -0
- package/dist/ui/progress.d.ts +5 -0
- package/dist/ui/progress.d.ts.map +1 -0
- package/dist/ui/progress.js +24 -0
- package/dist/ui/progress.js.map +1 -0
- package/dist/ui/prompts.d.ts +14 -0
- package/dist/ui/prompts.d.ts.map +1 -0
- package/dist/ui/prompts.js +34 -0
- package/dist/ui/prompts.js.map +1 -0
- package/package.json +9 -4
- package/src/app/framework.ts +1 -3
- package/src/app/proxy-dev-app.ts +114 -6
- package/src/app-config.ts +80 -0
- package/src/binary-cache.ts +102 -52
- package/src/cli.ts +16 -2
- package/src/commands/add.ts +97 -0
- package/src/commands/admin.ts +39 -73
- package/src/commands/adopt.ts +82 -0
- package/src/commands/app.ts +20 -17
- package/src/commands/cache.ts +11 -10
- package/src/commands/cloud.ts +91 -142
- package/src/commands/db.ts +40 -63
- package/src/commands/deploy.ts +186 -126
- package/src/commands/dev.ts +95 -55
- package/src/commands/diff.ts +52 -43
- package/src/commands/doctor.ts +103 -0
- package/src/commands/engine.ts +5 -4
- package/src/commands/functions.ts +187 -123
- package/src/commands/generate.ts +5 -4
- package/src/commands/init.ts +996 -105
- package/src/commands/introspect.ts +48 -0
- package/src/commands/keys.ts +56 -14
- package/src/commands/link-helpers.ts +273 -0
- package/src/commands/logs.ts +5 -4
- package/src/commands/migrate-from-v1.ts +3 -2
- package/src/commands/migrate.ts +167 -27
- package/src/commands/pg.ts +13 -18
- package/src/commands/plugins.ts +55 -46
- package/src/commands/pull.ts +38 -9
- package/src/commands/push.ts +147 -174
- package/src/commands/seed.ts +5 -4
- package/src/commands/self-host.ts +85 -54
- package/src/commands/self-update.ts +3 -2
- package/src/commands/status.ts +102 -33
- package/src/commands/types.ts +3 -2
- package/src/commands/update.ts +59 -23
- package/src/config.ts +2 -1
- package/src/dev-compose.ts +426 -34
- package/src/dev-log-bus.ts +101 -0
- package/src/dev-log-filter.ts +32 -0
- package/src/dev-logo.ts +61 -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 +79 -1
- package/src/docker-runtime.ts +151 -0
- package/src/engine-client.ts +81 -17
- package/src/engine-push-output.ts +75 -0
- package/src/ensure-binary.ts +2 -2
- package/src/gitignore.ts +48 -0
- package/src/kong-config.ts +24 -1
- package/src/link.ts +243 -0
- package/src/process-manager.ts +66 -10
- package/src/project-config.ts +62 -7
- package/src/prompts.ts +2 -0
- package/src/pull-utils.ts +217 -23
- package/src/resolve-target.ts +419 -0
- package/src/restore-system-relation-targets.ts +45 -0
- package/src/runtime-routes.ts +7 -0
- package/src/schema-ast-v2.ts +2 -1
- package/src/schema-sources.ts +248 -0
- package/src/scripts/postinstall.ts +7 -1
- package/src/seed.ts +43 -0
- package/src/self-host-compose.ts +261 -46
- package/src/storage-provision.ts +33 -1
- package/src/supatype-eval-1781522769253.mts +1 -0
- package/src/systemd.ts +2 -2
- package/src/target-client.ts +40 -0
- package/src/type-extractor.ts +124 -11
- package/src/ui/README.md +17 -0
- package/src/ui/brand.ts +12 -0
- package/src/ui/confirm.ts +38 -0
- package/src/ui/fatal.ts +43 -0
- package/src/ui/index.ts +8 -0
- package/src/ui/interactive.ts +4 -0
- package/src/ui/messages.ts +43 -0
- package/src/ui/next-steps.ts +10 -0
- package/src/ui/progress.ts +28 -0
- package/src/ui/prompts.ts +40 -0
- package/tests/cli-help.test.ts +27 -2
- package/tests/config.test.ts +29 -2
- package/tests/dev-ui.test.ts +139 -0
- package/tests/docker-runtime.test.ts +236 -0
- package/tests/engine-push-output.test.ts +67 -0
- package/tests/init.test.ts +197 -18
- package/tests/link.test.ts +148 -0
- package/tests/minisign.test.ts +102 -0
- package/tests/proxy-dev-app.test.ts +45 -1
- package/tests/pull-utils.test.ts +5 -4
- package/tests/runtime-contract.test.ts +186 -2
- package/tests/schema-sources.test.ts +119 -0
- package/tests/storage-provision.test.ts +100 -0
- package/tests/ui-confirm.test.ts +41 -0
- package/tests/ui-messages.test.ts +66 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/binary-cache.ts
CHANGED
|
@@ -7,10 +7,13 @@
|
|
|
7
7
|
*
|
|
8
8
|
* Security model:
|
|
9
9
|
* 1. Download checksums.sha256 + checksums.sha256.minisig from CDN.
|
|
10
|
-
* 2. Verify Ed25519 minisign signature on the checksum file using the
|
|
11
|
-
*
|
|
10
|
+
* 2. Verify the Ed25519 minisign signature on the checksum file using the
|
|
11
|
+
* release public key (embedded at publish, overridable via
|
|
12
|
+
* SUPATYPE_RELEASE_PUBLIC_KEY).
|
|
12
13
|
* 3. Verify SHA256 of the downloaded binary against the signed checksum.
|
|
13
|
-
*
|
|
14
|
+
* Verification is mandatory and fails closed: if no public key is configured,
|
|
15
|
+
* the download errors out rather than silently degrading to SHA256-only.
|
|
16
|
+
* The only escape hatch is the explicit SUPATYPE_ALLOW_UNVERIFIED_DOWNLOADS=1.
|
|
14
17
|
*/
|
|
15
18
|
|
|
16
19
|
import { createHash, createPublicKey, verify as cryptoVerify } from "node:crypto"
|
|
@@ -23,6 +26,7 @@ import {
|
|
|
23
26
|
openSync,
|
|
24
27
|
readFileSync,
|
|
25
28
|
readSync,
|
|
29
|
+
rmdirSync,
|
|
26
30
|
statSync,
|
|
27
31
|
unlinkSync,
|
|
28
32
|
writeFileSync,
|
|
@@ -31,6 +35,7 @@ import { chmod } from "node:fs/promises"
|
|
|
31
35
|
import { homedir } from "node:os"
|
|
32
36
|
import { basename, join, resolve, isAbsolute } from "node:path"
|
|
33
37
|
import type { SupatypeProjectConfig } from "./project-config.js"
|
|
38
|
+
import { loadProjectLink, migrateLegacyLinkFiles } from "./link.js"
|
|
34
39
|
import { releasePublicKey } from "./release-public-key.js"
|
|
35
40
|
|
|
36
41
|
/**
|
|
@@ -81,12 +86,16 @@ export function describeActiveOverrides(config: SupatypeProjectConfig): string[]
|
|
|
81
86
|
|
|
82
87
|
/**
|
|
83
88
|
* True when this working tree is associated with a remote Supatype Cloud project:
|
|
84
|
-
* `project.ref
|
|
89
|
+
* `project.ref` or `.supatype/link.json` (cloud kind).
|
|
85
90
|
*/
|
|
86
91
|
export function isLinkedToCloudProject(cwd: string, config: SupatypeProjectConfig): boolean {
|
|
87
92
|
const ref = config.project.ref
|
|
88
93
|
if (typeof ref === "string" && ref.trim() !== "") return true
|
|
89
94
|
|
|
95
|
+
migrateLegacyLinkFiles(cwd)
|
|
96
|
+
const link = loadProjectLink(cwd)
|
|
97
|
+
if (link?.kind === "cloud" && link.projectRef.trim() !== "") return true
|
|
98
|
+
|
|
90
99
|
const linkedPath = join(cwd, ".supatype", "linked.json")
|
|
91
100
|
if (existsSync(linkedPath)) {
|
|
92
101
|
try {
|
|
@@ -112,7 +121,7 @@ export function isLinkedToCloudProject(cwd: string, config: SupatypeProjectConfi
|
|
|
112
121
|
|
|
113
122
|
export type { Component, ComponentVersions } from "./components.js"
|
|
114
123
|
export { BINARY_COMPONENTS } from "./components.js"
|
|
115
|
-
import { BINARY_COMPONENTS, type Component } from "./components.js"
|
|
124
|
+
import { BINARY_COMPONENTS, type Component, type ComponentVersions } from "./components.js"
|
|
116
125
|
|
|
117
126
|
export interface PlatformId {
|
|
118
127
|
os: "linux" | "darwin" | "windows"
|
|
@@ -130,17 +139,6 @@ export function postgresArchiveTag(version: string): string {
|
|
|
130
139
|
return version.split(".")[0]!
|
|
131
140
|
}
|
|
132
141
|
|
|
133
|
-
/**
|
|
134
|
-
* Supatype release signing public key (minisign format).
|
|
135
|
-
* Generated with: minisign -G
|
|
136
|
-
* Rotate by: generating a new pair, updating this constant, and updating
|
|
137
|
-
* the MINISIGN_PRIVATE_KEY GitHub Actions secret.
|
|
138
|
-
*
|
|
139
|
-
* ⚠ PLACEHOLDER — replace with actual public key before first release.
|
|
140
|
-
* When empty, minisign verification is skipped with a warning (SHA256 only).
|
|
141
|
-
*/
|
|
142
|
-
const SUPATYPE_RELEASE_PUBLIC_KEY = ""
|
|
143
|
-
|
|
144
142
|
// CDN path templates per component.
|
|
145
143
|
const CDN_PATHS: Record<Component, (version: string, platform: PlatformId) => string> = {
|
|
146
144
|
engine: (v, p) => `/engine/v${v}/supatype-engine-${p.os}-${p.arch}${p.os === "windows" ? ".exe" : ""}`,
|
|
@@ -226,7 +224,7 @@ export async function resolveBinary(
|
|
|
226
224
|
}
|
|
227
225
|
|
|
228
226
|
const overridePath = config.overrides?.[component === "postgres" ? "postgres_dir" : component]
|
|
229
|
-
const version =
|
|
227
|
+
const version = await resolveVersionFor(component, config)
|
|
230
228
|
|
|
231
229
|
if (version === VERSION_PIN_LOCAL && !overridePath) {
|
|
232
230
|
const key = component === "postgres" ? "postgres_dir" : component
|
|
@@ -335,14 +333,14 @@ export async function download(
|
|
|
335
333
|
|
|
336
334
|
console.log(`[supatype] Downloading ${component} v${version} (${platform.os}/${platform.arch})...`)
|
|
337
335
|
|
|
338
|
-
// ── Fetch checksums + optional minisig ────────────────────────────────────
|
|
339
|
-
const expectedChecksum = await withRetry(() =>
|
|
340
|
-
fetchChecksums(checksumsUrl, minisigUrl, name),
|
|
341
|
-
)
|
|
342
|
-
|
|
343
|
-
// ── Stream-download binary with progress ─────────────────────────────────
|
|
344
336
|
const tmpPath = destPath + ".tmp"
|
|
345
337
|
try {
|
|
338
|
+
// ── Fetch checksums + optional minisig (retried on transient failures) ───
|
|
339
|
+
const expectedChecksum = await withRetry(() =>
|
|
340
|
+
fetchChecksums(checksumsUrl, minisigUrl, name),
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
// ── Stream-download binary with progress (retried on transient failures) ─
|
|
346
344
|
await withRetry(() => streamToFileWithProgress(binaryUrl, tmpPath))
|
|
347
345
|
|
|
348
346
|
// ── Verify SHA256 ────────────────────────────────────────────────────────
|
|
@@ -354,9 +352,17 @@ export async function download(
|
|
|
354
352
|
if (process.platform !== "win32" && EXECUTABLE_COMPONENTS.has(component)) {
|
|
355
353
|
await chmod(destPath, 0o755)
|
|
356
354
|
}
|
|
355
|
+
} catch (err) {
|
|
356
|
+
// Never leave a partial binary or an empty version directory behind: a stale
|
|
357
|
+
// empty dir makes the next resolve look fine while silently lacking a binary.
|
|
358
|
+
try { if (existsSync(destPath)) unlinkSync(destPath) } catch { /* ignore */ }
|
|
359
|
+
try { rmdirSync(dir) } catch { /* dir not empty or already removed */ }
|
|
360
|
+
throw new Error(
|
|
361
|
+
`Failed to download ${component} v${version} from ${CDN_BASE}: ${(err as Error).message}`,
|
|
362
|
+
)
|
|
357
363
|
} finally {
|
|
358
364
|
if (existsSync(tmpPath)) {
|
|
359
|
-
try {
|
|
365
|
+
try { unlinkSync(tmpPath) } catch { /* ignore */ }
|
|
360
366
|
}
|
|
361
367
|
}
|
|
362
368
|
|
|
@@ -379,24 +385,36 @@ async function fetchChecksums(
|
|
|
379
385
|
const checksumsText = await csResp.text()
|
|
380
386
|
|
|
381
387
|
const pubKey = releasePublicKey()
|
|
382
|
-
if (pubKey) {
|
|
383
|
-
//
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
388
|
+
if (!pubKey) {
|
|
389
|
+
// Fail closed: a missing public key means we cannot verify authenticity, only
|
|
390
|
+
// integrity (SHA256). Published builds always embed the key, so this only
|
|
391
|
+
// happens in source/contributor builds — never silently downgrade.
|
|
392
|
+
if (process.env["SUPATYPE_ALLOW_UNVERIFIED_DOWNLOADS"] === "1") {
|
|
393
|
+
console.warn(
|
|
394
|
+
"[supatype] \u26a0 SUPATYPE_ALLOW_UNVERIFIED_DOWNLOADS=1 — no minisign public " +
|
|
395
|
+
"key configured; verifying SHA256 only (authenticity NOT checked).",
|
|
389
396
|
)
|
|
397
|
+
return extractChecksum(checksumsText, binaryFilename)
|
|
390
398
|
}
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
"skipping signature verification (SHA256 only).",
|
|
399
|
+
throw new Error(
|
|
400
|
+
"No minisign public key configured — cannot verify release authenticity.\n" +
|
|
401
|
+
"Published @supatype/cli builds embed the key automatically; if you are building " +
|
|
402
|
+
"from source, set SUPATYPE_RELEASE_PUBLIC_KEY to the release public key, or set " +
|
|
403
|
+
"SUPATYPE_ALLOW_UNVERIFIED_DOWNLOADS=1 to download with SHA256-only verification (unsafe).",
|
|
397
404
|
)
|
|
398
405
|
}
|
|
399
406
|
|
|
407
|
+
// Minisign signature is mandatory when a public key is configured.
|
|
408
|
+
const sigResp = await fetch(minisigUrl)
|
|
409
|
+
if (!sigResp.ok) {
|
|
410
|
+
throw new Error(
|
|
411
|
+
`Failed to fetch checksum signature from ${minisigUrl}: HTTP ${sigResp.status}\n` +
|
|
412
|
+
"Cannot verify release integrity. Aborting download.",
|
|
413
|
+
)
|
|
414
|
+
}
|
|
415
|
+
const sigText = await sigResp.text()
|
|
416
|
+
verifyMinisign(Buffer.from(checksumsText, "utf8"), sigText, pubKey)
|
|
417
|
+
|
|
400
418
|
return extractChecksum(checksumsText, binaryFilename)
|
|
401
419
|
}
|
|
402
420
|
|
|
@@ -420,10 +438,10 @@ async function fetchChecksums(
|
|
|
420
438
|
const ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex")
|
|
421
439
|
|
|
422
440
|
/**
|
|
423
|
-
* Verify a minisign signature
|
|
424
|
-
* Throws if
|
|
441
|
+
* Verify a minisign signature. Supports both Ed25519 legacy mode ("Ed", over the
|
|
442
|
+
* raw file) and prehashed mode ("ED", over BLAKE2b-512(file)). Throws if invalid.
|
|
425
443
|
*/
|
|
426
|
-
function verifyMinisign(fileBytes: Buffer, sigFileContent: string, pubKeyStr: string): void {
|
|
444
|
+
export function verifyMinisign(fileBytes: Buffer, sigFileContent: string, pubKeyStr: string): void {
|
|
427
445
|
// Parse public key: [2 algo][8 keyId][32 ed25519 key]
|
|
428
446
|
const pkLines = pubKeyStr.trim().split("\n")
|
|
429
447
|
const pkBytes = Buffer.from(pkLines[pkLines.length - 1]!.trim(), "base64")
|
|
@@ -445,14 +463,17 @@ function verifyMinisign(fileBytes: Buffer, sigFileContent: string, pubKeyStr: st
|
|
|
445
463
|
const sigKeyId = sigBytes.subarray(2, 10)
|
|
446
464
|
const signature = sigBytes.subarray(10, 74)
|
|
447
465
|
|
|
448
|
-
//
|
|
449
|
-
//
|
|
450
|
-
|
|
466
|
+
// Both Ed25519 modes are supported:
|
|
467
|
+
// "Ed" (0x45, 0x64) — legacy: signature is over the raw file bytes.
|
|
468
|
+
// "ED" (0x45, 0x44) — prehashed: signature is over BLAKE2b-512(file).
|
|
469
|
+
// Modern minisign (and our release pipeline) default to prehashed mode.
|
|
470
|
+
if (algo[0] !== 0x45 || (algo[1] !== 0x64 && algo[1] !== 0x44)) {
|
|
451
471
|
throw new Error(
|
|
452
|
-
"Unsupported minisign algorithm —
|
|
472
|
+
"Unsupported minisign algorithm — expected Ed25519 ('Ed' legacy or 'ED' prehashed).\n" +
|
|
453
473
|
`Got: 0x${algo[0]?.toString(16)}${algo[1]?.toString(16)}`,
|
|
454
474
|
)
|
|
455
475
|
}
|
|
476
|
+
const prehashed = algo[1] === 0x44
|
|
456
477
|
|
|
457
478
|
if (!sigKeyId.equals(pkKeyId)) {
|
|
458
479
|
throw new Error(
|
|
@@ -464,7 +485,13 @@ function verifyMinisign(fileBytes: Buffer, sigFileContent: string, pubKeyStr: st
|
|
|
464
485
|
const spkiDer = Buffer.concat([ED25519_SPKI_PREFIX, pkEd25519])
|
|
465
486
|
const keyObject = createPublicKey({ key: spkiDer, format: "der", type: "spki" })
|
|
466
487
|
|
|
467
|
-
|
|
488
|
+
// Pure Ed25519 (PureEdDSA) verifies over the message directly; for prehashed
|
|
489
|
+
// minisign the "message" is the BLAKE2b-512 digest of the file.
|
|
490
|
+
const signedData = prehashed
|
|
491
|
+
? createHash("blake2b512").update(fileBytes).digest()
|
|
492
|
+
: fileBytes
|
|
493
|
+
|
|
494
|
+
const valid = cryptoVerify(null, signedData, keyObject, signature)
|
|
468
495
|
if (!valid) {
|
|
469
496
|
throw new Error(
|
|
470
497
|
"Minisign signature verification FAILED — the checksum file may have been tampered with.\n" +
|
|
@@ -730,10 +757,30 @@ export function normalisePlatformPath(p: string): string {
|
|
|
730
757
|
return result
|
|
731
758
|
}
|
|
732
759
|
|
|
760
|
+
export function pinnedVersion(component: Component, config: SupatypeProjectConfig): string | undefined {
|
|
761
|
+
const version = config.versions?.[component]
|
|
762
|
+
if (typeof version !== "string") return undefined
|
|
763
|
+
const trimmed = version.trim()
|
|
764
|
+
return trimmed === "" ? undefined : trimmed
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
/** Pinned version from config, or latest from CDN when unset. */
|
|
768
|
+
export async function resolveVersionFor(
|
|
769
|
+
component: Component,
|
|
770
|
+
config: SupatypeProjectConfig,
|
|
771
|
+
): Promise<string> {
|
|
772
|
+
const pinned = pinnedVersion(component, config)
|
|
773
|
+
if (pinned) return pinned
|
|
774
|
+
return fetchLatestVersion(component)
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
/** @deprecated Prefer {@link pinnedVersion} or {@link resolveVersionFor}. */
|
|
733
778
|
export function versionFor(component: Component, config: SupatypeProjectConfig): string {
|
|
734
|
-
const version = config
|
|
735
|
-
if (
|
|
736
|
-
throw new Error(
|
|
779
|
+
const version = pinnedVersion(component, config)
|
|
780
|
+
if (!version) {
|
|
781
|
+
throw new Error(
|
|
782
|
+
`[supatype] versions.${component} is not pinned in supatype.config.ts (omit versions to use latest)`,
|
|
783
|
+
)
|
|
737
784
|
}
|
|
738
785
|
return version
|
|
739
786
|
}
|
|
@@ -780,7 +827,10 @@ export async function fetchAllLatestVersions(): Promise<Record<Component, string
|
|
|
780
827
|
* Verify all cached binaries for the current platform (used by integration CI).
|
|
781
828
|
* Throws if any cached component is missing or fails format checks.
|
|
782
829
|
*/
|
|
783
|
-
export function verifyCachedBinaries(versions:
|
|
830
|
+
export function verifyCachedBinaries(versions: Partial<ComponentVersions> | undefined): void {
|
|
831
|
+
if (!versions) {
|
|
832
|
+
throw new Error("[supatype] verifyCachedBinaries requires pinned versions")
|
|
833
|
+
}
|
|
784
834
|
const platform = currentPlatform()
|
|
785
835
|
for (const component of BINARY_COMPONENTS) {
|
|
786
836
|
const version = versions[component]
|
|
@@ -798,15 +848,15 @@ export function verifyCachedBinaries(versions: SupatypeProjectConfig["versions"]
|
|
|
798
848
|
}
|
|
799
849
|
|
|
800
850
|
export async function downloadAll(
|
|
801
|
-
versions:
|
|
851
|
+
versions: Partial<ComponentVersions> | undefined,
|
|
802
852
|
graceful = false,
|
|
803
853
|
): Promise<void> {
|
|
804
854
|
const platform = currentPlatform()
|
|
805
855
|
const components: Component[] = [...BINARY_COMPONENTS]
|
|
806
|
-
const
|
|
856
|
+
const latest = await fetchAllLatestVersions()
|
|
807
857
|
|
|
808
858
|
for (const component of components) {
|
|
809
|
-
const version =
|
|
859
|
+
const version = versions?.[component] ?? latest[component]
|
|
810
860
|
if (version === VERSION_PIN_LOCAL) continue
|
|
811
861
|
try {
|
|
812
862
|
await download(component, version, platform)
|
package/src/cli.ts
CHANGED
|
@@ -7,11 +7,15 @@ import { registerPg } from "./commands/pg.js"
|
|
|
7
7
|
import { registerPush } from "./commands/push.js"
|
|
8
8
|
import { registerDiff } from "./commands/diff.js"
|
|
9
9
|
import { registerPull } from "./commands/pull.js"
|
|
10
|
+
import { registerDoctor } from "./commands/doctor.js"
|
|
11
|
+
import { registerIntrospect } from "./commands/introspect.js"
|
|
12
|
+
import { registerAdopt } from "./commands/adopt.js"
|
|
10
13
|
import { registerGenerate } from "./commands/generate.js"
|
|
11
14
|
import { registerMigrate } from "./commands/migrate.js"
|
|
12
15
|
import { registerSeed } from "./commands/seed.js"
|
|
13
16
|
import { registerKeys } from "./commands/keys.js"
|
|
14
17
|
import { registerApp } from "./commands/app.js"
|
|
18
|
+
import { registerAdd } from "./commands/add.js"
|
|
15
19
|
import { registerSelfHost } from "./commands/self-host.js"
|
|
16
20
|
import { registerCloud } from "./commands/cloud.js"
|
|
17
21
|
import { registerEngine } from "./commands/engine.js"
|
|
@@ -25,8 +29,9 @@ import { registerPlugins } from "./commands/plugins.js"
|
|
|
25
29
|
import { registerTypes } from "./commands/types.js"
|
|
26
30
|
import { registerMigrateFromV1 } from "./commands/migrate-from-v1.js"
|
|
27
31
|
import { registerSelfUpdate } from "./commands/self-update.js"
|
|
32
|
+
import { reportCliFatal } from "./ui/fatal.js"
|
|
28
33
|
|
|
29
|
-
export function run(): void {
|
|
34
|
+
export async function run(): Promise<void> {
|
|
30
35
|
const program = new Command()
|
|
31
36
|
.name("supatype")
|
|
32
37
|
.description("Supatype — schema-first Postgres API")
|
|
@@ -41,11 +46,15 @@ export function run(): void {
|
|
|
41
46
|
registerPush(program)
|
|
42
47
|
registerDiff(program)
|
|
43
48
|
registerPull(program)
|
|
49
|
+
registerDoctor(program)
|
|
50
|
+
registerIntrospect(program)
|
|
51
|
+
registerAdopt(program)
|
|
44
52
|
registerGenerate(program)
|
|
45
53
|
registerMigrate(program)
|
|
46
54
|
registerSeed(program)
|
|
47
55
|
registerKeys(program)
|
|
48
56
|
registerApp(program)
|
|
57
|
+
registerAdd(program)
|
|
49
58
|
registerSelfHost(program)
|
|
50
59
|
registerCloud(program)
|
|
51
60
|
registerEngine(program)
|
|
@@ -59,5 +68,10 @@ export function run(): void {
|
|
|
59
68
|
registerTypes(program)
|
|
60
69
|
registerMigrateFromV1(program)
|
|
61
70
|
|
|
62
|
-
|
|
71
|
+
try {
|
|
72
|
+
await program.parseAsync(process.argv)
|
|
73
|
+
} catch (err) {
|
|
74
|
+
reportCliFatal(err)
|
|
75
|
+
process.exit(1)
|
|
76
|
+
}
|
|
63
77
|
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import type { Command } from "commander"
|
|
2
|
+
import * as p from "@clack/prompts"
|
|
3
|
+
import { loadConfig } from "../config.js"
|
|
4
|
+
import { selfHostTlsEnabled } from "../project-config.js"
|
|
5
|
+
import { updateServerConfigInProject } from "../app-config.js"
|
|
6
|
+
import { ensureNotCancelled, printLogo } from "../ui/prompts.js"
|
|
7
|
+
import { file, error, info, plain } from "../ui/messages.js"
|
|
8
|
+
import { nextSteps } from "../ui/next-steps.js"
|
|
9
|
+
|
|
10
|
+
export function registerAdd(program: Command): void {
|
|
11
|
+
const addCmd = program
|
|
12
|
+
.command("add")
|
|
13
|
+
.description("Add capabilities to an existing project")
|
|
14
|
+
|
|
15
|
+
addCmd
|
|
16
|
+
.command("domain [domain]")
|
|
17
|
+
.description("Add a custom domain with automatic HTTPS (self-host)")
|
|
18
|
+
.option("--email <email>", "Email for Let's Encrypt TLS certificates")
|
|
19
|
+
.action(async (domainArg: string | undefined, opts: { email?: string }) => {
|
|
20
|
+
await addDomain(domainArg, opts.email)
|
|
21
|
+
})
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async function promptDomain(initial?: string): Promise<string> {
|
|
25
|
+
const trimmed = initial?.trim()
|
|
26
|
+
if (trimmed) return trimmed
|
|
27
|
+
return ensureNotCancelled(
|
|
28
|
+
await p.text({
|
|
29
|
+
message: "Please provide domain:",
|
|
30
|
+
placeholder: "demo.supatype.com",
|
|
31
|
+
validate: (v) => ((v ?? "").trim().length === 0 ? "Domain is required" : undefined),
|
|
32
|
+
}),
|
|
33
|
+
).trim()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function promptEmail(initial?: string): Promise<string> {
|
|
37
|
+
const trimmed = initial?.trim()
|
|
38
|
+
if (trimmed) return trimmed
|
|
39
|
+
return ensureNotCancelled(
|
|
40
|
+
await p.text({
|
|
41
|
+
message: "Please provide email for TLS",
|
|
42
|
+
placeholder: "hello@supatype.com",
|
|
43
|
+
validate: (v) => ((v ?? "").trim().length === 0 ? "Email is required" : undefined),
|
|
44
|
+
}),
|
|
45
|
+
).trim()
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function addDomain(domainArg?: string, emailArg?: string): Promise<void> {
|
|
49
|
+
const cwd = process.cwd()
|
|
50
|
+
const interactive = !domainArg?.trim() || !emailArg?.trim()
|
|
51
|
+
|
|
52
|
+
if (interactive) {
|
|
53
|
+
printLogo()
|
|
54
|
+
p.intro("Add a custom domain")
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const domain = await promptDomain(domainArg)
|
|
58
|
+
const email = await promptEmail(emailArg)
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const configPath = updateServerConfigInProject(cwd, { domain, tlsEmail: email })
|
|
62
|
+
if (interactive) {
|
|
63
|
+
p.outro(`Updated ${configPath}`)
|
|
64
|
+
} else {
|
|
65
|
+
file("updated", configPath)
|
|
66
|
+
}
|
|
67
|
+
printDomainNextSteps(cwd, domain)
|
|
68
|
+
} catch (err) {
|
|
69
|
+
error((err as Error).message)
|
|
70
|
+
process.exit(1)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function printDomainNextSteps(cwd: string, domain: string): void {
|
|
75
|
+
let tlsActive = true
|
|
76
|
+
try {
|
|
77
|
+
tlsActive = selfHostTlsEnabled(loadConfig(cwd))
|
|
78
|
+
} catch {
|
|
79
|
+
// config re-load is best-effort for the warning below
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
info(`Domain set to ${domain} with automatic HTTPS.`)
|
|
83
|
+
if (!tlsActive) {
|
|
84
|
+
plain(
|
|
85
|
+
"\nNote: a supatype.local.config.ts override (server.mode=dev) is suppressing HTTPS locally.\n" +
|
|
86
|
+
"That file is gitignored, so HTTPS still activates on your production server.",
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
nextSteps("Go live:", [
|
|
90
|
+
`Point DNS: an A record for ${domain} -> your server's public IP`,
|
|
91
|
+
"Open ports 80 and 443 on the server firewall",
|
|
92
|
+
"supatype self-host compose up -d # Kong provisions HTTPS automatically",
|
|
93
|
+
`Platform URL: https://${domain}`,
|
|
94
|
+
])
|
|
95
|
+
plain(" App, REST, Auth, Storage, Realtime, Functions, and Studio — one HTTPS domain.")
|
|
96
|
+
plain(" Certificates persist in the valkey-data volume.\n")
|
|
97
|
+
}
|
package/src/commands/admin.ts
CHANGED
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
// {ref}_auth.users table. Used for initial setup and ongoing admin management.
|
|
5
5
|
|
|
6
6
|
import type { Command } from "commander"
|
|
7
|
-
import { createInterface } from "node:readline"
|
|
8
7
|
import { randomBytes, scrypt } from "node:crypto"
|
|
9
8
|
import { promisify } from "node:util"
|
|
10
9
|
import { loadConfig } from "../config.js"
|
|
11
10
|
import { connectionString } from "../project-config.js"
|
|
12
11
|
import { signJwt } from "../jwt.js"
|
|
12
|
+
import { confirm as uiConfirm } from "../ui/confirm.js"
|
|
13
|
+
import { error, info, plain } from "../ui/messages.js"
|
|
14
|
+
import { promptText } from "../ui/prompts.js"
|
|
13
15
|
|
|
14
16
|
const scryptAsync = promisify(scrypt)
|
|
15
17
|
|
|
@@ -36,22 +38,22 @@ export function registerAdmin(program: Command): void {
|
|
|
36
38
|
const config = loadConfig(cwd)
|
|
37
39
|
const connection = opts.connection ?? connectionString(config)
|
|
38
40
|
|
|
39
|
-
const email = opts.email ?? (await
|
|
41
|
+
const email = opts.email ?? (await promptText("Admin email"))
|
|
40
42
|
if (!email || !email.includes("@")) {
|
|
41
|
-
|
|
43
|
+
error("A valid email address is required.")
|
|
42
44
|
process.exit(1)
|
|
43
45
|
}
|
|
44
46
|
|
|
45
47
|
const password =
|
|
46
|
-
opts.password ?? (await
|
|
48
|
+
opts.password ?? (await promptText("Admin password (min 8 chars)"))
|
|
47
49
|
if (!password || password.length < 8) {
|
|
48
|
-
|
|
50
|
+
error("Password must be at least 8 characters.")
|
|
49
51
|
process.exit(1)
|
|
50
52
|
}
|
|
51
53
|
|
|
52
54
|
const role = opts.role
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
info(`Creating admin user: ${email} (role: ${role})...`)
|
|
55
57
|
|
|
56
58
|
// We use pg directly to insert into the auth.users table
|
|
57
59
|
const pg = await importPg()
|
|
@@ -88,12 +90,8 @@ export function registerAdmin(program: Command): void {
|
|
|
88
90
|
)
|
|
89
91
|
|
|
90
92
|
if (existing.rows.length > 0) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
)
|
|
94
|
-
console.log(
|
|
95
|
-
`To update their role, use: supatype admin set-role --email ${email} --role ${role}`,
|
|
96
|
-
)
|
|
93
|
+
error(`User with email "${email}" already exists.`)
|
|
94
|
+
info(`To update their role, use: supatype admin set-role --email ${email} --role ${role}`)
|
|
97
95
|
process.exit(1)
|
|
98
96
|
}
|
|
99
97
|
|
|
@@ -119,17 +117,15 @@ export function registerAdmin(program: Command): void {
|
|
|
119
117
|
|
|
120
118
|
const user = result.rows[0] as { id: string; email: string }
|
|
121
119
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
`\nThis user can now log in to the admin panel at /admin\n`,
|
|
128
|
-
)
|
|
120
|
+
info("Admin user created successfully.")
|
|
121
|
+
plain(` ID: ${user.id}`)
|
|
122
|
+
plain(` Email: ${user.email}`)
|
|
123
|
+
plain(` Role: ${role}`)
|
|
124
|
+
info("This user can now log in to the admin panel at /admin")
|
|
129
125
|
} catch (err) {
|
|
130
126
|
const message =
|
|
131
127
|
err instanceof Error ? err.message : "Unknown error"
|
|
132
|
-
|
|
128
|
+
error(`Failed to create admin user: ${message}`)
|
|
133
129
|
process.exit(1)
|
|
134
130
|
} finally {
|
|
135
131
|
await pool.end()
|
|
@@ -163,7 +159,7 @@ export function registerAdmin(program: Command): void {
|
|
|
163
159
|
)
|
|
164
160
|
|
|
165
161
|
if (result.rows.length === 0) {
|
|
166
|
-
|
|
162
|
+
error(`No user found with email "${opts.email}".`)
|
|
167
163
|
process.exit(1)
|
|
168
164
|
}
|
|
169
165
|
|
|
@@ -173,14 +169,14 @@ export function registerAdmin(program: Command): void {
|
|
|
173
169
|
raw_app_meta_data: Record<string, unknown>
|
|
174
170
|
}
|
|
175
171
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
172
|
+
info("Role updated successfully.")
|
|
173
|
+
plain(` ID: ${user.id}`)
|
|
174
|
+
plain(` Email: ${user.email}`)
|
|
175
|
+
plain(` Role: ${opts.role}`)
|
|
180
176
|
} catch (err) {
|
|
181
177
|
const message =
|
|
182
178
|
err instanceof Error ? err.message : "Unknown error"
|
|
183
|
-
|
|
179
|
+
error(`Failed to update role: ${message}`)
|
|
184
180
|
process.exit(1)
|
|
185
181
|
} finally {
|
|
186
182
|
await pool.end()
|
|
@@ -210,17 +206,15 @@ export function registerAdmin(program: Command): void {
|
|
|
210
206
|
)
|
|
211
207
|
|
|
212
208
|
if (result.rows.length === 0) {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
"Create one with: supatype admin create-user --email admin@example.com --role admin\n",
|
|
216
|
-
)
|
|
209
|
+
info("No admin users found.")
|
|
210
|
+
info("Create one with: supatype admin create-user --email admin@example.com --role admin")
|
|
217
211
|
return
|
|
218
212
|
}
|
|
219
213
|
|
|
220
|
-
|
|
214
|
+
plain(
|
|
221
215
|
"\n ID Email Role Created",
|
|
222
216
|
)
|
|
223
|
-
|
|
217
|
+
plain(" " + "-".repeat(100))
|
|
224
218
|
for (const row of result.rows) {
|
|
225
219
|
const r = row as {
|
|
226
220
|
id: string
|
|
@@ -229,15 +223,15 @@ export function registerAdmin(program: Command): void {
|
|
|
229
223
|
created_at: string
|
|
230
224
|
}
|
|
231
225
|
const date = new Date(r.created_at).toISOString().slice(0, 10)
|
|
232
|
-
|
|
226
|
+
plain(
|
|
233
227
|
` ${r.id} ${r.email.padEnd(30)} ${r.role.padEnd(12)} ${date}`,
|
|
234
228
|
)
|
|
235
229
|
}
|
|
236
|
-
|
|
230
|
+
plain()
|
|
237
231
|
} catch (err) {
|
|
238
232
|
const message =
|
|
239
233
|
err instanceof Error ? err.message : "Unknown error"
|
|
240
|
-
|
|
234
|
+
error(`Failed to list admin users: ${message}`)
|
|
241
235
|
process.exit(1)
|
|
242
236
|
} finally {
|
|
243
237
|
await pool.end()
|
|
@@ -278,30 +272,22 @@ export async function promptFirstAdminUser(
|
|
|
278
272
|
if (count > 0) return
|
|
279
273
|
|
|
280
274
|
// No admin users — prompt to create one
|
|
281
|
-
|
|
282
|
-
const createAdmin = await
|
|
283
|
-
" Create an admin user now? [y/N] ",
|
|
284
|
-
)
|
|
275
|
+
info("No admin users found for the admin panel.")
|
|
276
|
+
const createAdmin = await uiConfirm("Create an admin user now?")
|
|
285
277
|
if (!createAdmin) {
|
|
286
|
-
|
|
287
|
-
" Skipped. You can create one later with: supatype admin create-user\n",
|
|
288
|
-
)
|
|
278
|
+
info("Skipped. You can create one later with: supatype admin create-user")
|
|
289
279
|
return
|
|
290
280
|
}
|
|
291
281
|
|
|
292
|
-
const email = await
|
|
282
|
+
const email = await promptText("Admin email")
|
|
293
283
|
if (!email || !email.includes("@")) {
|
|
294
|
-
|
|
284
|
+
info("Invalid email. Skipping admin user creation.")
|
|
295
285
|
return
|
|
296
286
|
}
|
|
297
287
|
|
|
298
|
-
const password = await
|
|
299
|
-
" Admin password (min 8 chars): ",
|
|
300
|
-
)
|
|
288
|
+
const password = await promptText("Admin password (min 8 chars)")
|
|
301
289
|
if (!password || password.length < 8) {
|
|
302
|
-
|
|
303
|
-
" Password too short. Skipping admin user creation.\n",
|
|
304
|
-
)
|
|
290
|
+
info("Password too short. Skipping admin user creation.")
|
|
305
291
|
return
|
|
306
292
|
}
|
|
307
293
|
|
|
@@ -325,8 +311,8 @@ export async function promptFirstAdminUser(
|
|
|
325
311
|
[email.toLowerCase(), passwordHash, appMetadata],
|
|
326
312
|
)
|
|
327
313
|
|
|
328
|
-
|
|
329
|
-
|
|
314
|
+
info(`Admin user "${email}" created (role: admin).`)
|
|
315
|
+
info("Log in at /admin after starting the dev server.")
|
|
330
316
|
} catch {
|
|
331
317
|
// Non-fatal — if auth schema doesn't exist yet, skip silently
|
|
332
318
|
} finally {
|
|
@@ -340,9 +326,7 @@ async function importPg(): Promise<typeof import("pg")> {
|
|
|
340
326
|
try {
|
|
341
327
|
return await import("pg")
|
|
342
328
|
} catch {
|
|
343
|
-
|
|
344
|
-
"pg package is required for admin commands. Install it with: pnpm add pg",
|
|
345
|
-
)
|
|
329
|
+
error("pg package is required for admin commands. Install it with: pnpm add pg")
|
|
346
330
|
process.exit(1)
|
|
347
331
|
}
|
|
348
332
|
}
|
|
@@ -352,21 +336,3 @@ async function hashPassword(password: string): Promise<string> {
|
|
|
352
336
|
const derived = (await scryptAsync(password, salt, 64)) as Buffer
|
|
353
337
|
return `${salt}:${derived.toString("hex")}`
|
|
354
338
|
}
|
|
355
|
-
|
|
356
|
-
function prompt(question: string): Promise<string> {
|
|
357
|
-
const rl = createInterface({
|
|
358
|
-
input: process.stdin,
|
|
359
|
-
output: process.stdout,
|
|
360
|
-
})
|
|
361
|
-
return new Promise((resolve) => {
|
|
362
|
-
rl.question(question, (answer) => {
|
|
363
|
-
rl.close()
|
|
364
|
-
resolve(answer.trim())
|
|
365
|
-
})
|
|
366
|
-
})
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
async function confirm(question: string): Promise<boolean> {
|
|
370
|
-
const answer = await prompt(question)
|
|
371
|
-
return answer.toLowerCase() === "y"
|
|
372
|
-
}
|