@supatype/cli 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/.turbo/turbo-test.log +144 -94
- package/.turbo/turbo-typecheck.log +1 -1
- package/deno.d.ts +52 -0
- package/dist/augmentation-generator.d.ts +22 -0
- package/dist/augmentation-generator.d.ts.map +1 -1
- package/dist/augmentation-generator.js +18 -4
- package/dist/augmentation-generator.js.map +1 -1
- package/dist/binary-cache.d.ts +14 -0
- package/dist/binary-cache.d.ts.map +1 -1
- package/dist/binary-cache.js +33 -13
- package/dist/binary-cache.js.map +1 -1
- package/dist/cache-clean.d.ts +15 -0
- package/dist/cache-clean.d.ts.map +1 -0
- package/dist/cache-clean.js +76 -0
- package/dist/cache-clean.js.map +1 -0
- package/dist/cache-pins.d.ts +51 -0
- package/dist/cache-pins.d.ts.map +1 -0
- package/dist/cache-pins.js +184 -0
- package/dist/cache-pins.js.map +1 -0
- package/dist/cli-install-method.d.ts +19 -0
- package/dist/cli-install-method.d.ts.map +1 -0
- package/dist/cli-install-method.js +60 -0
- package/dist/cli-install-method.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -1
- package/dist/cloud-credentials.d.ts +34 -0
- package/dist/cloud-credentials.d.ts.map +1 -0
- package/dist/cloud-credentials.js +139 -0
- package/dist/cloud-credentials.js.map +1 -0
- package/dist/commands/admin.d.ts +11 -0
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +121 -19
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/cache.d.ts.map +1 -1
- package/dist/commands/cache.js +67 -71
- package/dist/commands/cache.js.map +1 -1
- package/dist/commands/cloud.d.ts.map +1 -1
- package/dist/commands/cloud.js +32 -8
- package/dist/commands/cloud.js.map +1 -1
- package/dist/commands/db.d.ts +1 -0
- package/dist/commands/db.d.ts.map +1 -1
- package/dist/commands/db.js +147 -1
- package/dist/commands/db.js.map +1 -1
- package/dist/commands/dev.d.ts.map +1 -1
- package/dist/commands/dev.js +50 -10
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.d.ts +28 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +60 -3
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/functions.d.ts +7 -0
- package/dist/commands/functions.d.ts.map +1 -1
- package/dist/commands/functions.js +40 -6
- package/dist/commands/functions.js.map +1 -1
- package/dist/commands/generate.d.ts.map +1 -1
- package/dist/commands/generate.js +5 -1
- package/dist/commands/generate.js.map +1 -1
- package/dist/commands/hooks.d.ts +12 -0
- package/dist/commands/hooks.d.ts.map +1 -0
- package/dist/commands/hooks.js +118 -0
- package/dist/commands/hooks.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +41 -7
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/internal.d.ts +6 -0
- package/dist/commands/internal.d.ts.map +1 -0
- package/dist/commands/internal.js +22 -0
- package/dist/commands/internal.js.map +1 -0
- package/dist/commands/link-helpers.d.ts.map +1 -1
- package/dist/commands/link-helpers.js +8 -2
- package/dist/commands/link-helpers.js.map +1 -1
- package/dist/commands/login.d.ts +7 -0
- package/dist/commands/login.d.ts.map +1 -0
- package/dist/commands/login.js +110 -0
- package/dist/commands/login.js.map +1 -0
- package/dist/commands/push.d.ts.map +1 -1
- package/dist/commands/push.js +110 -7
- package/dist/commands/push.js.map +1 -1
- package/dist/commands/self-update.d.ts +7 -0
- package/dist/commands/self-update.d.ts.map +1 -1
- package/dist/commands/self-update.js +18 -11
- package/dist/commands/self-update.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +18 -3
- package/dist/config.js.map +1 -1
- package/dist/db-preflight.d.ts +78 -0
- package/dist/db-preflight.d.ts.map +1 -0
- package/dist/db-preflight.js +452 -0
- package/dist/db-preflight.js.map +1 -0
- package/dist/dev-compose.d.ts +1 -1
- package/dist/dev-compose.d.ts.map +1 -1
- package/dist/dev-compose.js +127 -72
- package/dist/dev-compose.js.map +1 -1
- package/dist/env-file.d.ts +40 -2
- package/dist/env-file.d.ts.map +1 -1
- package/dist/env-file.js +132 -11
- package/dist/env-file.js.map +1 -1
- package/dist/field-masking-tier.d.ts +50 -0
- package/dist/field-masking-tier.d.ts.map +1 -0
- package/dist/field-masking-tier.js +91 -0
- package/dist/field-masking-tier.js.map +1 -0
- package/dist/functions-deno-types.d.ts +24 -0
- package/dist/functions-deno-types.d.ts.map +1 -0
- package/dist/functions-deno-types.js +155 -0
- package/dist/functions-deno-types.js.map +1 -0
- package/dist/hook-upload.d.ts +30 -0
- package/dist/hook-upload.d.ts.map +1 -0
- package/dist/hook-upload.js +86 -0
- package/dist/hook-upload.js.map +1 -0
- package/dist/hooks-generator.d.ts +8 -0
- package/dist/hooks-generator.d.ts.map +1 -0
- package/dist/hooks-generator.js +332 -0
- package/dist/hooks-generator.js.map +1 -0
- package/dist/link.d.ts +4 -0
- package/dist/link.d.ts.map +1 -1
- package/dist/link.js +2 -0
- package/dist/link.js.map +1 -1
- package/dist/local-secrets.d.ts +71 -0
- package/dist/local-secrets.d.ts.map +1 -0
- package/dist/local-secrets.js +100 -0
- package/dist/local-secrets.js.map +1 -0
- package/dist/model-hooks.d.ts +73 -0
- package/dist/model-hooks.d.ts.map +1 -0
- package/dist/model-hooks.js +211 -0
- package/dist/model-hooks.js.map +1 -0
- package/dist/postgres-ctl.d.ts +11 -0
- package/dist/postgres-ctl.d.ts.map +1 -1
- package/dist/postgres-ctl.js +25 -2
- package/dist/postgres-ctl.js.map +1 -1
- package/dist/project-config.d.ts +121 -1
- package/dist/project-config.d.ts.map +1 -1
- package/dist/project-config.js +150 -1
- package/dist/project-config.js.map +1 -1
- package/dist/resolve-target.d.ts +4 -0
- package/dist/resolve-target.d.ts.map +1 -1
- package/dist/resolve-target.js +69 -73
- package/dist/resolve-target.js.map +1 -1
- package/dist/schema-ast-v2.d.ts +2 -1
- package/dist/schema-ast-v2.d.ts.map +1 -1
- package/dist/schema-ast-v2.js +4 -2
- package/dist/schema-ast-v2.js.map +1 -1
- package/dist/schema-push-lock.d.ts.map +1 -1
- package/dist/schema-push-lock.js +43 -16
- package/dist/schema-push-lock.js.map +1 -1
- package/dist/self-host-compose.d.ts +24 -0
- package/dist/self-host-compose.d.ts.map +1 -1
- package/dist/self-host-compose.js +301 -69
- package/dist/self-host-compose.js.map +1 -1
- package/dist/service-role-check.d.ts +21 -0
- package/dist/service-role-check.d.ts.map +1 -0
- package/dist/service-role-check.js +85 -0
- package/dist/service-role-check.js.map +1 -0
- package/dist/target-client.d.ts +17 -0
- package/dist/target-client.d.ts.map +1 -1
- package/dist/target-client.js +69 -17
- package/dist/target-client.js.map +1 -1
- package/dist/type-extractor.d.ts.map +1 -1
- package/dist/type-extractor.js +736 -32
- package/dist/type-extractor.js.map +1 -1
- package/dist/ui/runtime/command-chrome.js +1 -1
- package/dist/ui/runtime/command-chrome.js.map +1 -1
- package/dist/ui/runtime/stdin-after-ink.d.ts.map +1 -1
- package/dist/ui/runtime/stdin-after-ink.js +3 -1
- package/dist/ui/runtime/stdin-after-ink.js.map +1 -1
- package/package.json +4 -1
- package/scripts/generate-homebrew-formula.mjs +121 -0
- package/src/augmentation-generator.ts +18 -5
- package/src/binary-cache.ts +47 -15
- package/src/cache-clean.ts +81 -0
- package/src/cache-pins.ts +243 -0
- package/src/cli-install-method.ts +67 -0
- package/src/cli.ts +6 -0
- package/src/cloud-credentials.ts +183 -0
- package/src/commands/admin.ts +140 -25
- package/src/commands/cache.ts +90 -75
- package/src/commands/cloud.ts +32 -8
- package/src/commands/db.ts +162 -1
- package/src/commands/dev.ts +57 -8
- package/src/commands/doctor.ts +69 -3
- package/src/commands/functions.ts +42 -5
- package/src/commands/generate.ts +5 -1
- package/src/commands/hooks.ts +138 -0
- package/src/commands/init.ts +42 -8
- package/src/commands/internal.ts +23 -0
- package/src/commands/link-helpers.ts +9 -2
- package/src/commands/login.ts +127 -0
- package/src/commands/push.ts +124 -7
- package/src/commands/self-update.ts +85 -13
- package/src/config.ts +22 -3
- package/src/db-preflight.ts +575 -0
- package/src/dev-compose.ts +142 -73
- package/src/env-file.ts +157 -11
- package/src/field-masking-tier.ts +107 -0
- package/src/functions-deno-types.ts +188 -0
- package/src/hook-upload.ts +107 -0
- package/src/hooks-generator.ts +352 -0
- package/src/link.ts +7 -0
- package/src/local-secrets.ts +104 -0
- package/src/model-hooks.ts +260 -0
- package/src/postgres-ctl.ts +29 -2
- package/src/project-config.ts +236 -2
- package/src/resolve-target.ts +113 -68
- package/src/schema-ast-v2.ts +5 -2
- package/src/schema-push-lock.ts +56 -17
- package/src/self-host-compose.ts +344 -68
- package/src/service-role-check.ts +111 -0
- package/src/target-client.ts +105 -18
- package/src/type-extractor.ts +883 -28
- package/src/ui/runtime/command-chrome.ts +1 -1
- package/src/ui/runtime/stdin-after-ink.ts +3 -1
- package/tests/access-composition.test.ts +667 -0
- package/tests/admin-studio-membership.test.ts +121 -0
- package/tests/api-schemas.test.ts +83 -0
- package/tests/archive-name-alias.test.ts +42 -0
- package/tests/augmentation-generator.test.ts +28 -0
- package/tests/cache-clean.test.ts +206 -0
- package/tests/cli-help.test.ts +3 -0
- package/tests/cli-install-method.test.ts +66 -0
- package/tests/command-chrome-coverage.test.ts +31 -26
- package/tests/command-chrome-wrap.test.ts +1 -0
- package/tests/db-preflight.test.ts +72 -0
- package/tests/doctor-render.test.ts +62 -0
- package/tests/doctor-service-role.test.ts +55 -0
- package/tests/env-file.test.ts +121 -0
- package/tests/external-database-compose.test.ts +184 -0
- package/tests/external-database-config.test.ts +154 -0
- package/tests/field-masking-tier.test.ts +184 -0
- package/tests/functions-deno-types.test.ts +197 -0
- package/tests/homebrew-formula.test.ts +58 -0
- package/tests/hook-upload.test.ts +125 -0
- package/tests/hooks-generator.test.ts +212 -0
- package/tests/init.test.ts +3 -0
- package/tests/local-secrets.test.ts +92 -0
- package/tests/model-hooks.test.ts +322 -0
- package/tests/runtime-contract.test.ts +113 -1
- package/tests/service-role-check.test.ts +109 -0
- package/tests/service-role-routes.test.ts +51 -0
- package/tests/target-client-refresh.test.ts +94 -0
- package/tests/type-extractor.test.ts +187 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/tests/engine-distribution.test.ts +0 -418
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detect how the supatype CLI was installed (npm, Homebrew, standalone CDN).
|
|
3
|
+
*/
|
|
4
|
+
function normalizePath(path) {
|
|
5
|
+
return path.replace(/\\/g, "/");
|
|
6
|
+
}
|
|
7
|
+
/** The running binary sits in an npm or workspace layout. */
|
|
8
|
+
function hasNpmInstallPath() {
|
|
9
|
+
const main = normalizePath(process.argv[1] ?? "");
|
|
10
|
+
return (main.includes("node_modules") ||
|
|
11
|
+
main.includes("/dist/cli") ||
|
|
12
|
+
main.includes("/bin/supatype.js"));
|
|
13
|
+
}
|
|
14
|
+
/** Launched through an npm/pnpm/yarn script, whatever the binary's own layout. */
|
|
15
|
+
function ranUnderPackageManager() {
|
|
16
|
+
return Boolean(process.env["npm_execpath"]) || Boolean(process.env["npm_lifecycle_event"]);
|
|
17
|
+
}
|
|
18
|
+
/** npm global, project dependency, or monorepo workspace build. */
|
|
19
|
+
export function looksLikeNpmOrWorkspaceCLI() {
|
|
20
|
+
return hasNpmInstallPath() || ranUnderPackageManager();
|
|
21
|
+
}
|
|
22
|
+
/** Homebrew / Linuxbrew Cellar layout. */
|
|
23
|
+
export function looksLikeHomebrewCLI() {
|
|
24
|
+
const main = normalizePath(process.argv[1] ?? "");
|
|
25
|
+
if (!main)
|
|
26
|
+
return false;
|
|
27
|
+
if (/(?:^|\/)Cellar\/supatype\//.test(main))
|
|
28
|
+
return true;
|
|
29
|
+
const cellar = process.env["HOMEBREW_CELLAR"];
|
|
30
|
+
if (cellar && normalizePath(main).startsWith(normalizePath(cellar) + "/") && main.includes("/supatype/")) {
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Which installer owns the running binary — this decides what `self-update`
|
|
37
|
+
* tells the user to run, so it has to describe the binary on disk.
|
|
38
|
+
*
|
|
39
|
+
* Path shape wins over `npm_*` in the environment: those are set whenever the
|
|
40
|
+
* CLI is invoked from a package script, which says nothing about how the binary
|
|
41
|
+
* got there. Trusting them meant a Homebrew or curl install called from an npm
|
|
42
|
+
* script was told to update itself with npm.
|
|
43
|
+
*/
|
|
44
|
+
export function detectCliInstallMethod() {
|
|
45
|
+
if (hasNpmInstallPath())
|
|
46
|
+
return "npm";
|
|
47
|
+
if (looksLikeHomebrewCLI())
|
|
48
|
+
return "homebrew";
|
|
49
|
+
if (isStandalonePath())
|
|
50
|
+
return "standalone";
|
|
51
|
+
if (ranUnderPackageManager())
|
|
52
|
+
return "npm";
|
|
53
|
+
return "standalone";
|
|
54
|
+
}
|
|
55
|
+
/** The `curl | sh` installer's default target. */
|
|
56
|
+
function isStandalonePath() {
|
|
57
|
+
const main = normalizePath(process.argv[1] ?? "");
|
|
58
|
+
return main.includes("/.supatype/bin/");
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=cli-install-method.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-install-method.js","sourceRoot":"","sources":["../src/cli-install-method.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,SAAS,aAAa,CAAC,IAAY;IACjC,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;AACjC,CAAC;AAED,6DAA6D;AAC7D,SAAS,iBAAiB;IACxB,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;IACjD,OAAO,CACL,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC;QAC7B,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC;QAC1B,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAClC,CAAA;AACH,CAAC;AAED,kFAAkF;AAClF,SAAS,sBAAsB;IAC7B,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC,CAAA;AAC5F,CAAC;AAED,mEAAmE;AACnE,MAAM,UAAU,0BAA0B;IACxC,OAAO,iBAAiB,EAAE,IAAI,sBAAsB,EAAE,CAAA;AACxD,CAAC;AAED,0CAA0C;AAC1C,MAAM,UAAU,oBAAoB;IAClC,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;IACjD,IAAI,CAAC,IAAI;QAAE,OAAO,KAAK,CAAA;IAEvB,IAAI,4BAA4B,CAAC,IAAI,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAA;IAExD,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC,CAAA;IAC7C,IAAI,MAAM,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC,MAAM,CAAC,GAAG,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;QACzG,OAAO,IAAI,CAAA;IACb,CAAC;IAED,OAAO,KAAK,CAAA;AACd,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,sBAAsB;IACpC,IAAI,iBAAiB,EAAE;QAAE,OAAO,KAAK,CAAA;IACrC,IAAI,oBAAoB,EAAE;QAAE,OAAO,UAAU,CAAA;IAC7C,IAAI,gBAAgB,EAAE;QAAE,OAAO,YAAY,CAAA;IAC3C,IAAI,sBAAsB,EAAE;QAAE,OAAO,KAAK,CAAA;IAC1C,OAAO,YAAY,CAAA;AACrB,CAAC;AAED,kDAAkD;AAClD,SAAS,gBAAgB;IACvB,MAAM,IAAI,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAA;IACjD,OAAO,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAA;AACzC,CAAC"}
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAsCA,wBAAsB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAgDzC"}
|
package/dist/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ import { registerApp } from "./commands/app.js";
|
|
|
18
18
|
import { registerAdd } from "./commands/add.js";
|
|
19
19
|
import { registerSelfHost } from "./commands/self-host.js";
|
|
20
20
|
import { registerCloud } from "./commands/cloud.js";
|
|
21
|
+
import { registerLogin } from "./commands/login.js";
|
|
21
22
|
import { registerEngine } from "./commands/engine.js";
|
|
22
23
|
import { registerDb } from "./commands/db.js";
|
|
23
24
|
import { registerDeploy } from "./commands/deploy.js";
|
|
@@ -25,10 +26,12 @@ import { registerStatus } from "./commands/status.js";
|
|
|
25
26
|
import { registerLogs } from "./commands/logs.js";
|
|
26
27
|
import { registerAdmin } from "./commands/admin.js";
|
|
27
28
|
import { registerFunctions } from "./commands/functions.js";
|
|
29
|
+
import { registerHooks } from "./commands/hooks.js";
|
|
28
30
|
import { registerPlugins } from "./commands/plugins.js";
|
|
29
31
|
import { registerTypes } from "./commands/types.js";
|
|
30
32
|
import { registerMigrateFromV1 } from "./commands/migrate-from-v1.js";
|
|
31
33
|
import { registerSelfUpdate } from "./commands/self-update.js";
|
|
34
|
+
import { registerInternalCommands } from "./commands/internal.js";
|
|
32
35
|
import { cliPackageVersion } from "./cli-package-version.js";
|
|
33
36
|
import { reportCliFatal } from "./ui/fatal.js";
|
|
34
37
|
import { wrapProgramActionsWithChrome } from "./ui/runtime/command-chrome.js";
|
|
@@ -41,6 +44,7 @@ export async function run() {
|
|
|
41
44
|
registerDev(program);
|
|
42
45
|
registerCache(program);
|
|
43
46
|
registerSelfUpdate(program);
|
|
47
|
+
registerInternalCommands(program);
|
|
44
48
|
registerUpdate(program);
|
|
45
49
|
registerPg(program);
|
|
46
50
|
registerPush(program);
|
|
@@ -56,6 +60,7 @@ export async function run() {
|
|
|
56
60
|
registerApp(program);
|
|
57
61
|
registerAdd(program);
|
|
58
62
|
registerSelfHost(program);
|
|
63
|
+
registerLogin(program);
|
|
59
64
|
registerCloud(program);
|
|
60
65
|
registerEngine(program);
|
|
61
66
|
registerDb(program);
|
|
@@ -64,6 +69,7 @@ export async function run() {
|
|
|
64
69
|
registerLogs(program);
|
|
65
70
|
registerAdmin(program);
|
|
66
71
|
registerFunctions(program);
|
|
72
|
+
registerHooks(program);
|
|
67
73
|
registerPlugins(program);
|
|
68
74
|
registerTypes(program);
|
|
69
75
|
registerMigrateFromV1(program);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type ProjectLink } from "./link.js";
|
|
2
|
+
export declare const CREDENTIALS_VERSION: 1;
|
|
3
|
+
export interface CloudCredentials {
|
|
4
|
+
version: typeof CREDENTIALS_VERSION;
|
|
5
|
+
apiUrl: string;
|
|
6
|
+
accessToken: string;
|
|
7
|
+
refreshToken?: string;
|
|
8
|
+
email?: string;
|
|
9
|
+
updatedAt: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function credentialsPath(): string;
|
|
12
|
+
export declare function loadCloudCredentials(): CloudCredentials | null;
|
|
13
|
+
export declare function saveCloudCredentials(creds: CloudCredentials): void;
|
|
14
|
+
export declare function clearCloudCredentials(): void;
|
|
15
|
+
/** Prefer link tokens; fall back to ~/.supatype/credentials.json for cloud. */
|
|
16
|
+
export declare function resolveCloudAccessToken(link: ProjectLink): string | undefined;
|
|
17
|
+
export declare function resolveCloudRefreshToken(link: ProjectLink): string | undefined;
|
|
18
|
+
/** Write tokens to global credentials and the current project's cloud link (if any). */
|
|
19
|
+
export declare function persistCloudSession(cwd: string, params: {
|
|
20
|
+
apiUrl: string;
|
|
21
|
+
accessToken: string;
|
|
22
|
+
refreshToken?: string;
|
|
23
|
+
email?: string;
|
|
24
|
+
}): void;
|
|
25
|
+
export interface CloudLoginResult {
|
|
26
|
+
accessToken: string;
|
|
27
|
+
refreshToken?: string;
|
|
28
|
+
expiresIn?: number;
|
|
29
|
+
email?: string;
|
|
30
|
+
}
|
|
31
|
+
/** Password login against control-plane `/auth/login` (not `/api/v1`). */
|
|
32
|
+
export declare function cloudPasswordLogin(apiUrl: string, email: string, password: string): Promise<CloudLoginResult>;
|
|
33
|
+
export declare function cloudRefreshSession(apiUrl: string, refreshToken: string): Promise<CloudLoginResult>;
|
|
34
|
+
//# sourceMappingURL=cloud-credentials.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-credentials.d.ts","sourceRoot":"","sources":["../src/cloud-credentials.ts"],"names":[],"mappings":"AAOA,OAAO,EAGL,KAAK,WAAW,EACjB,MAAM,WAAW,CAAA;AAElB,eAAO,MAAM,mBAAmB,EAAG,CAAU,CAAA;AAE7C,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,OAAO,mBAAmB,CAAA;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,eAAe,IAAI,MAAM,CAExC;AAED,wBAAgB,oBAAoB,IAAI,gBAAgB,GAAG,IAAI,CAU9D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAIlE;AAED,wBAAgB,qBAAqB,IAAI,IAAI,CAQ5C;AAED,+EAA+E;AAC/E,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAS7E;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAS9E;AAED,wFAAwF;AACxF,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,MAAM,EACX,MAAM,EAAE;IACN,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,GACA,IAAI,CA2BN;AAED,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED,0EAA0E;AAC1E,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,gBAAgB,CAAC,CA0B3B;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,MAAM,EACd,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,gBAAgB,CAAC,CAsB3B"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cloud session credentials (access + refresh) for CLI ↔ control plane.
|
|
3
|
+
* Self-host links keep using long-lived SERVICE_ROLE_KEY and ignore this store.
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { loadProjectLink, saveProjectLink, } from "./link.js";
|
|
9
|
+
export const CREDENTIALS_VERSION = 1;
|
|
10
|
+
export function credentialsPath() {
|
|
11
|
+
return join(homedir(), ".supatype", "credentials.json");
|
|
12
|
+
}
|
|
13
|
+
export function loadCloudCredentials() {
|
|
14
|
+
const path = credentialsPath();
|
|
15
|
+
if (!existsSync(path))
|
|
16
|
+
return null;
|
|
17
|
+
try {
|
|
18
|
+
const raw = JSON.parse(readFileSync(path, "utf8"));
|
|
19
|
+
if (!raw.accessToken || !raw.apiUrl)
|
|
20
|
+
return null;
|
|
21
|
+
return raw;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export function saveCloudCredentials(creds) {
|
|
28
|
+
const dir = join(homedir(), ".supatype");
|
|
29
|
+
mkdirSync(dir, { recursive: true });
|
|
30
|
+
writeFileSync(credentialsPath(), `${JSON.stringify(creds, null, 2)}\n`, "utf8");
|
|
31
|
+
}
|
|
32
|
+
export function clearCloudCredentials() {
|
|
33
|
+
const path = credentialsPath();
|
|
34
|
+
if (!existsSync(path))
|
|
35
|
+
return;
|
|
36
|
+
try {
|
|
37
|
+
unlinkSync(path);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
/* ignore */
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
/** Prefer link tokens; fall back to ~/.supatype/credentials.json for cloud. */
|
|
44
|
+
export function resolveCloudAccessToken(link) {
|
|
45
|
+
if (link.token)
|
|
46
|
+
return link.token;
|
|
47
|
+
if (link.kind !== "cloud")
|
|
48
|
+
return undefined;
|
|
49
|
+
const creds = loadCloudCredentials();
|
|
50
|
+
if (!creds)
|
|
51
|
+
return undefined;
|
|
52
|
+
const linkApi = (link.cloudApiUrl ?? "").replace(/\/$/, "");
|
|
53
|
+
const credApi = creds.apiUrl.replace(/\/$/, "");
|
|
54
|
+
if (linkApi && credApi && linkApi !== credApi)
|
|
55
|
+
return undefined;
|
|
56
|
+
return creds.accessToken;
|
|
57
|
+
}
|
|
58
|
+
export function resolveCloudRefreshToken(link) {
|
|
59
|
+
if (link.refreshToken)
|
|
60
|
+
return link.refreshToken;
|
|
61
|
+
if (link.kind !== "cloud")
|
|
62
|
+
return undefined;
|
|
63
|
+
const creds = loadCloudCredentials();
|
|
64
|
+
if (!creds?.refreshToken)
|
|
65
|
+
return undefined;
|
|
66
|
+
const linkApi = (link.cloudApiUrl ?? "").replace(/\/$/, "");
|
|
67
|
+
const credApi = creds.apiUrl.replace(/\/$/, "");
|
|
68
|
+
if (linkApi && credApi && linkApi !== credApi)
|
|
69
|
+
return undefined;
|
|
70
|
+
return creds.refreshToken;
|
|
71
|
+
}
|
|
72
|
+
/** Write tokens to global credentials and the current project's cloud link (if any). */
|
|
73
|
+
export function persistCloudSession(cwd, params) {
|
|
74
|
+
const apiUrl = params.apiUrl.replace(/\/$/, "");
|
|
75
|
+
const existing = loadCloudCredentials();
|
|
76
|
+
saveCloudCredentials({
|
|
77
|
+
version: CREDENTIALS_VERSION,
|
|
78
|
+
apiUrl,
|
|
79
|
+
accessToken: params.accessToken,
|
|
80
|
+
updatedAt: new Date().toISOString(),
|
|
81
|
+
...(params.refreshToken !== undefined
|
|
82
|
+
? { refreshToken: params.refreshToken }
|
|
83
|
+
: existing?.refreshToken !== undefined
|
|
84
|
+
? { refreshToken: existing.refreshToken }
|
|
85
|
+
: {}),
|
|
86
|
+
...(params.email !== undefined
|
|
87
|
+
? { email: params.email }
|
|
88
|
+
: existing?.email !== undefined
|
|
89
|
+
? { email: existing.email }
|
|
90
|
+
: {}),
|
|
91
|
+
});
|
|
92
|
+
const link = loadProjectLink(cwd);
|
|
93
|
+
if (!link || link.kind !== "cloud")
|
|
94
|
+
return;
|
|
95
|
+
link.token = params.accessToken;
|
|
96
|
+
if (params.refreshToken !== undefined) {
|
|
97
|
+
link.refreshToken = params.refreshToken;
|
|
98
|
+
}
|
|
99
|
+
saveProjectLink(cwd, link);
|
|
100
|
+
}
|
|
101
|
+
/** Password login against control-plane `/auth/login` (not `/api/v1`). */
|
|
102
|
+
export async function cloudPasswordLogin(apiUrl, email, password) {
|
|
103
|
+
const base = apiUrl.replace(/\/$/, "");
|
|
104
|
+
const res = await fetch(`${base}/auth/login`, {
|
|
105
|
+
method: "POST",
|
|
106
|
+
headers: { "Content-Type": "application/json" },
|
|
107
|
+
body: JSON.stringify({ email: email.toLowerCase(), password }),
|
|
108
|
+
});
|
|
109
|
+
const json = (await res.json().catch(() => ({})));
|
|
110
|
+
if (!res.ok || !json.access_token) {
|
|
111
|
+
throw new Error(json.message ?? json.error ?? `Login failed (${res.status})`);
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
accessToken: json.access_token,
|
|
115
|
+
...(json.refresh_token !== undefined ? { refreshToken: json.refresh_token } : {}),
|
|
116
|
+
...(json.expires_in !== undefined ? { expiresIn: json.expires_in } : {}),
|
|
117
|
+
...(json.user?.email !== undefined
|
|
118
|
+
? { email: json.user.email }
|
|
119
|
+
: { email: email.toLowerCase() }),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
export async function cloudRefreshSession(apiUrl, refreshToken) {
|
|
123
|
+
const base = apiUrl.replace(/\/$/, "");
|
|
124
|
+
const res = await fetch(`${base}/auth/refresh`, {
|
|
125
|
+
method: "POST",
|
|
126
|
+
headers: { "Content-Type": "application/json" },
|
|
127
|
+
body: JSON.stringify({ refresh_token: refreshToken }),
|
|
128
|
+
});
|
|
129
|
+
const json = (await res.json().catch(() => ({})));
|
|
130
|
+
if (!res.ok || !json.access_token) {
|
|
131
|
+
throw new Error(json.message ?? json.error ?? `Token refresh failed (${res.status})`);
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
accessToken: json.access_token,
|
|
135
|
+
...(json.refresh_token !== undefined ? { refreshToken: json.refresh_token } : {}),
|
|
136
|
+
...(json.expires_in !== undefined ? { expiresIn: json.expires_in } : {}),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=cloud-credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud-credentials.js","sourceRoot":"","sources":["../src/cloud-credentials.ts"],"names":[],"mappings":"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"}
|
package/dist/commands/admin.d.ts
CHANGED
|
@@ -3,6 +3,15 @@ import type { QueryResult } from "pg";
|
|
|
3
3
|
import { type SupatypeProjectConfig } from "../project-config.js";
|
|
4
4
|
export declare const ADMIN_EMAIL_ENV = "SUPATYPE_ADMIN_EMAIL";
|
|
5
5
|
export declare const ADMIN_PASSWORD_ENV = "SUPATYPE_ADMIN_PASSWORD";
|
|
6
|
+
/**
|
|
7
|
+
* Studio roles a membership row may hold, most privileged first.
|
|
8
|
+
*
|
|
9
|
+
* Must match `studioRolePermissions` in supatype-server and
|
|
10
|
+
* `STUDIO_ROLE_PERMISSIONS` in the control plane: capability checks refuse a role
|
|
11
|
+
* they do not recognise, so an unvalidated `--role` here would create a user who
|
|
12
|
+
* is locked out of the very panel they were made for.
|
|
13
|
+
*/
|
|
14
|
+
export declare const STUDIO_ROLES: readonly ["admin", "developer", "editor"];
|
|
6
15
|
/** GoTrue scopes users to the nil instance id in single-tenant/self-host mode. */
|
|
7
16
|
export declare const GOTRUE_NIL_INSTANCE_ID = "00000000-0000-0000-0000-000000000000";
|
|
8
17
|
/** Audience claim used by GoTrue when looking up users (matches GOTRUE_JWT_AUD in compose). */
|
|
@@ -35,6 +44,8 @@ export declare function ensureFirstAdminUser(connection: string, options?: Ensur
|
|
|
35
44
|
* is not published to the host).
|
|
36
45
|
*/
|
|
37
46
|
export declare function ensureFirstAdminUserForProject(cwd: string, config: SupatypeProjectConfig, options?: EnsureFirstAdminOptions): Promise<void>;
|
|
47
|
+
/** Exported for tests: the DB seam both the pool and compose paths share. */
|
|
48
|
+
export declare function ensureFirstAdminWithQuery(query: DbQuery, options: EnsureFirstAdminOptions): Promise<void>;
|
|
38
49
|
export declare function clearAdminSeedPassword(cwd: string): void;
|
|
39
50
|
export declare function hashPasswordForAuth(password: string): Promise<string>;
|
|
40
51
|
/** Postgres password for compose `exec psql` (db is not published to the host). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/commands/admin.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKxC,OAAO,KAAK,EAAQ,WAAW,EAAE,MAAM,IAAI,CAAA;AAE3C,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAA;AAa7B,eAAO,MAAM,eAAe,yBAAyB,CAAA;AACrD,eAAO,MAAM,kBAAkB,4BAA4B,CAAA;AAI3D,kFAAkF;AAClF,eAAO,MAAM,sBAAsB,yCAAyC,CAAA;AAE5E,+FAA+F;AAC/F,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,KAAK,OAAO,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,WAAW,CAAC,CAAA;AACxE,KAAK,mBAAmB,GAAG,oBAAoB,GAAG,cAAc,CAAA;AAEhE,uFAAuF;AACvF,wBAAsB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAc/F;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;CACnD;AAYD,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"admin.d.ts","sourceRoot":"","sources":["../../src/commands/admin.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAKxC,OAAO,KAAK,EAAQ,WAAW,EAAE,MAAM,IAAI,CAAA;AAE3C,OAAO,EAGL,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAA;AAa7B,eAAO,MAAM,eAAe,yBAAyB,CAAA;AACrD,eAAO,MAAM,kBAAkB,4BAA4B,CAAA;AAI3D;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,2CAA4C,CAAA;AAYrE,kFAAkF;AAClF,eAAO,MAAM,sBAAsB,yCAAyC,CAAA;AAE5E,+FAA+F;AAC/F,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAEhD;AAED,KAAK,OAAO,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,WAAW,CAAC,CAAA;AACxE,KAAK,mBAAmB,GAAG,oBAAoB,GAAG,cAAc,CAAA;AAEhE,uFAAuF;AACvF,wBAAsB,4BAA4B,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAc/F;AAED,MAAM,WAAW,uBAAuB;IACtC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAA;CACnD;AAYD,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CA4LpD;AAED,2CAA2C;AAC3C,eAAO,MAAM,oBAAoB,6BAAuB,CAAA;AAExD;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,EAClB,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CAaf;AAED;;;GAGG;AACH,wBAAsB,8BAA8B,CAClD,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,qBAAqB,EAC7B,OAAO,GAAE,uBAA4B,GACpC,OAAO,CAAC,IAAI,CAAC,CA2Bf;AAED,6EAA6E;AAC7E,wBAAsB,yBAAyB,CAC7C,KAAK,EAAE,OAAO,EACd,OAAO,EAAE,uBAAuB,GAC/B,OAAO,CAAC,IAAI,CAAC,CA8Bf;AAwDD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAIxD;AAED,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAE3E;AAqMD,mFAAmF;AACnF,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3D"}
|
package/dist/commands/admin.js
CHANGED
|
@@ -20,6 +20,23 @@ import { getActiveDevSession, } from "../dev-session.js";
|
|
|
20
20
|
export const ADMIN_EMAIL_ENV = "SUPATYPE_ADMIN_EMAIL";
|
|
21
21
|
export const ADMIN_PASSWORD_ENV = "SUPATYPE_ADMIN_PASSWORD";
|
|
22
22
|
const BCRYPT_ROUNDS = 10;
|
|
23
|
+
/**
|
|
24
|
+
* Studio roles a membership row may hold, most privileged first.
|
|
25
|
+
*
|
|
26
|
+
* Must match `studioRolePermissions` in supatype-server and
|
|
27
|
+
* `STUDIO_ROLE_PERMISSIONS` in the control plane: capability checks refuse a role
|
|
28
|
+
* they do not recognise, so an unvalidated `--role` here would create a user who
|
|
29
|
+
* is locked out of the very panel they were made for.
|
|
30
|
+
*/
|
|
31
|
+
export const STUDIO_ROLES = ["admin", "developer", "editor"];
|
|
32
|
+
function assertStudioRole(role) {
|
|
33
|
+
if (STUDIO_ROLES.includes(role))
|
|
34
|
+
return;
|
|
35
|
+
error(`Unknown Studio role "${role}". Choose one of: ${STUDIO_ROLES.join(", ")}.\n` +
|
|
36
|
+
" Studio roles are Supatype's own; your application's roles are separate " +
|
|
37
|
+
"and belong in your access rules.");
|
|
38
|
+
process.exit(1);
|
|
39
|
+
}
|
|
23
40
|
/** GoTrue scopes users to the nil instance id in single-tenant/self-host mode. */
|
|
24
41
|
export const GOTRUE_NIL_INSTANCE_ID = "00000000-0000-0000-0000-000000000000";
|
|
25
42
|
/** Audience claim used by GoTrue when looking up users (matches GOTRUE_JWT_AUD in compose). */
|
|
@@ -57,7 +74,7 @@ export function registerAdmin(program) {
|
|
|
57
74
|
.description("Create an admin user for the admin panel")
|
|
58
75
|
.option("--email <email>", "Admin user email address")
|
|
59
76
|
.option("--password <password>", "Admin user password (prompted if not provided)")
|
|
60
|
-
.option("--role <role>",
|
|
77
|
+
.option("--role <role>", `Studio role to assign (${STUDIO_ROLES.join(" | ")})`, "admin")
|
|
61
78
|
.option("--connection <url>", "Database connection URL (overrides config)")
|
|
62
79
|
.action(async (opts) => {
|
|
63
80
|
const cwd = process.cwd();
|
|
@@ -74,6 +91,7 @@ export function registerAdmin(program) {
|
|
|
74
91
|
process.exit(1);
|
|
75
92
|
}
|
|
76
93
|
const role = opts.role;
|
|
94
|
+
assertStudioRole(role);
|
|
77
95
|
info(`Creating admin user: ${email} (role: ${role})...`);
|
|
78
96
|
const compose = opts.connection === undefined ? resolveDockerComposeContext(cwd, config) : null;
|
|
79
97
|
try {
|
|
@@ -103,20 +121,31 @@ export function registerAdmin(program) {
|
|
|
103
121
|
.command("set-role")
|
|
104
122
|
.description("Change an existing user's admin role")
|
|
105
123
|
.requiredOption("--email <email>", "User email address")
|
|
106
|
-
.requiredOption("--role <role>",
|
|
124
|
+
.requiredOption("--role <role>", `New Studio role (${STUDIO_ROLES.join(" | ")})`)
|
|
107
125
|
.option("--connection <url>", "Database connection URL (overrides config)")
|
|
108
126
|
.action(async (opts) => {
|
|
127
|
+
assertStudioRole(opts.role);
|
|
109
128
|
const cwd = process.cwd();
|
|
110
129
|
const config = loadConfig(cwd);
|
|
111
130
|
const connection = resolveAdminConnection(cwd, config, opts.connection);
|
|
112
131
|
const pg = await importPg();
|
|
113
132
|
const pool = new pg.Pool({ connectionString: connection, max: 2 });
|
|
114
133
|
try {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
134
|
+
// Studio roles live in `_supatype.studio_members`, not in
|
|
135
|
+
// `app_metadata`: that key is the developer's namespace for their own
|
|
136
|
+
// app roles, and writing Studio access there means assigning an app
|
|
137
|
+
// role could hand out admin UI access by accident.
|
|
138
|
+
await ensureStudioMembersTable((sql, params) => pool.query(sql, params));
|
|
139
|
+
const result = await pool.query(`WITH target AS (
|
|
140
|
+
SELECT id, email FROM auth.users WHERE email = $2
|
|
141
|
+
), upsert AS (
|
|
142
|
+
INSERT INTO _supatype.studio_members (user_id, role)
|
|
143
|
+
SELECT id, $1 FROM target
|
|
144
|
+
ON CONFLICT (user_id) DO UPDATE
|
|
145
|
+
SET role = EXCLUDED.role, updated_at = now()
|
|
146
|
+
RETURNING user_id
|
|
147
|
+
)
|
|
148
|
+
SELECT id, email FROM target`, [opts.role, opts.email.toLowerCase()]);
|
|
120
149
|
if (result.rows.length === 0) {
|
|
121
150
|
error(`No user found with email "${opts.email}".`);
|
|
122
151
|
process.exit(1);
|
|
@@ -138,7 +167,7 @@ export function registerAdmin(program) {
|
|
|
138
167
|
});
|
|
139
168
|
adminCmd
|
|
140
169
|
.command("list-users")
|
|
141
|
-
.description("List users with
|
|
170
|
+
.description("List users with Studio access")
|
|
142
171
|
.option("--connection <url>", "Database connection URL (overrides config)")
|
|
143
172
|
.action(async (opts) => {
|
|
144
173
|
const cwd = process.cwd();
|
|
@@ -147,11 +176,16 @@ export function registerAdmin(program) {
|
|
|
147
176
|
const pg = await importPg();
|
|
148
177
|
const pool = new pg.Pool({ connectionString: connection, max: 2 });
|
|
149
178
|
try {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
179
|
+
// Studio access is membership, not a claim — list the grant that
|
|
180
|
+
// actually decides admission. LEFT JOIN, because a membership held by a
|
|
181
|
+
// Supatype Cloud account has no row in this project's `auth.users`;
|
|
182
|
+
// hiding those would make `list-users` disagree with who can log in.
|
|
183
|
+
const result = await pool.query(`SELECT COALESCE(m.user_id, m.platform_user_id) AS id,
|
|
184
|
+
COALESCE(u.email, '(cloud account)') AS email,
|
|
185
|
+
m.role, m.created_at
|
|
186
|
+
FROM _supatype.studio_members m
|
|
187
|
+
LEFT JOIN auth.users u ON u.id = m.user_id
|
|
188
|
+
ORDER BY m.created_at ASC`);
|
|
155
189
|
if (result.rows.length === 0) {
|
|
156
190
|
info("No admin users found.");
|
|
157
191
|
info("Create one with: supatype admin create-user --email admin@example.com --role admin");
|
|
@@ -219,7 +253,8 @@ export async function ensureFirstAdminUserForProject(cwd, config, options = {})
|
|
|
219
253
|
: options.connection ?? readEnvValue(root, "DATABASE_URL", connectionString(config));
|
|
220
254
|
await ensureFirstAdminUser(connection, merged);
|
|
221
255
|
}
|
|
222
|
-
|
|
256
|
+
/** Exported for tests: the DB seam both the pool and compose paths share. */
|
|
257
|
+
export async function ensureFirstAdminWithQuery(query, options) {
|
|
223
258
|
const cwd = options.cwd ? resolve(options.cwd) : process.cwd();
|
|
224
259
|
if (!(await authUsersTableExists(query)))
|
|
225
260
|
return;
|
|
@@ -292,7 +327,9 @@ async function resolveAdminCredentials(options, cwd) {
|
|
|
292
327
|
return { email, password };
|
|
293
328
|
}
|
|
294
329
|
export function clearAdminSeedPassword(cwd) {
|
|
295
|
-
|
|
330
|
+
// Unconditional: this is a one-time seed password being retired, and whoever wrote it wanted it
|
|
331
|
+
// gone once used.
|
|
332
|
+
upsertEnvFile(cwd, {}, { remove: [ADMIN_PASSWORD_ENV] });
|
|
296
333
|
}
|
|
297
334
|
export async function hashPasswordForAuth(password) {
|
|
298
335
|
return bcrypt.hash(password, BCRYPT_ROUNDS);
|
|
@@ -312,8 +349,11 @@ async function createAdminUser(db, email, password, role, opts = {}) {
|
|
|
312
349
|
throw new Error(`User with email "${email}" already exists.`);
|
|
313
350
|
}
|
|
314
351
|
const passwordHash = await hashPasswordForAuth(password);
|
|
352
|
+
// No `role` here: `app_metadata` is the developer's namespace for their own
|
|
353
|
+
// application roles, and Studio access is granted through
|
|
354
|
+
// `_supatype.studio_members` instead. Writing it in both places means an app
|
|
355
|
+
// role assignment can silently confer admin UI access.
|
|
315
356
|
const appMetadata = JSON.stringify({
|
|
316
|
-
role,
|
|
317
357
|
provider: "email",
|
|
318
358
|
providers: ["email"],
|
|
319
359
|
});
|
|
@@ -337,6 +377,7 @@ async function createAdminUser(db, email, password, role, opts = {}) {
|
|
|
337
377
|
now(), now()
|
|
338
378
|
) RETURNING id, email`, [GOTRUE_NIL_INSTANCE_ID, aud, normalized, passwordHash, appMetadata, userMetadata]);
|
|
339
379
|
const user = result.rows[0];
|
|
380
|
+
await recordStudioMembership(query, user.id, role);
|
|
340
381
|
if (!opts.quiet) {
|
|
341
382
|
info("Admin user created successfully.");
|
|
342
383
|
plain(` ID: ${user.id}`);
|
|
@@ -345,6 +386,23 @@ async function createAdminUser(db, email, password, role, opts = {}) {
|
|
|
345
386
|
}
|
|
346
387
|
return user;
|
|
347
388
|
}
|
|
389
|
+
/**
|
|
390
|
+
* Record Studio access in `_supatype.studio_members`.
|
|
391
|
+
*
|
|
392
|
+
* Studio capability is deliberately not a JWT claim: `app_metadata` belongs to
|
|
393
|
+
* the developer's own app roles, and letting it grant Studio access means a
|
|
394
|
+
* developer assigning an app role could hand out admin UI access by accident.
|
|
395
|
+
*
|
|
396
|
+
* This is now the *only* grant, so a failure here is fatal to the command — a
|
|
397
|
+
* user created without a membership row cannot reach Studio at all, and
|
|
398
|
+
* reporting success would leave no clue why.
|
|
399
|
+
*/
|
|
400
|
+
async function recordStudioMembership(query, userId, role) {
|
|
401
|
+
await ensureStudioMembersTable(query);
|
|
402
|
+
await query(`INSERT INTO _supatype.studio_members (user_id, role)
|
|
403
|
+
VALUES ($1, $2)
|
|
404
|
+
ON CONFLICT (user_id) DO UPDATE SET role = EXCLUDED.role, updated_at = now()`, [userId, role]);
|
|
405
|
+
}
|
|
348
406
|
async function ensureAuthUsersTable(pool) {
|
|
349
407
|
await pool.query(`
|
|
350
408
|
CREATE SCHEMA IF NOT EXISTS auth;
|
|
@@ -381,13 +439,57 @@ async function authUsersTableExists(query) {
|
|
|
381
439
|
) as exists`);
|
|
382
440
|
return Boolean(result.rows[0]?.exists);
|
|
383
441
|
}
|
|
442
|
+
/**
|
|
443
|
+
* Whether anyone can already reach Studio.
|
|
444
|
+
*
|
|
445
|
+
* Counts membership rows, not `app_metadata` claims: the claim no longer grants
|
|
446
|
+
* anything, so counting it would report an admin exists while nobody can log in.
|
|
447
|
+
* A missing table means the project has never been pushed — no admins.
|
|
448
|
+
*/
|
|
384
449
|
async function hasAdminUsers(query) {
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
450
|
+
// Postgres resolves relations at parse time, so the table has to be checked
|
|
451
|
+
// separately rather than guarded inside the count query. Absent means the
|
|
452
|
+
// project has never been pushed — no admins, and `createAdminUser` will
|
|
453
|
+
// create the table itself.
|
|
454
|
+
if (!(await studioMembersTableExists(query)))
|
|
455
|
+
return false;
|
|
456
|
+
// Inner JOIN on purpose, unlike `list-users`: a membership held by a cloud
|
|
457
|
+
// account cannot log in to a self-hosted GoTrue, so a project exported from
|
|
458
|
+
// cloud with only those grants genuinely still needs a first admin.
|
|
459
|
+
const adminCount = await query(`SELECT COUNT(*)::int as count
|
|
460
|
+
FROM _supatype.studio_members m
|
|
461
|
+
JOIN auth.users u ON u.id = m.user_id
|
|
462
|
+
WHERE m.role <> 'authenticated'`);
|
|
388
463
|
const count = adminCount.rows[0]?.count ?? 0;
|
|
389
464
|
return count > 0;
|
|
390
465
|
}
|
|
466
|
+
/**
|
|
467
|
+
* A project whose engine predates the membership table, or one that has never
|
|
468
|
+
* been pushed, still needs working `admin create-user` / `admin set-role`.
|
|
469
|
+
* Mirrors the engine's own definition (`create_studio_members_table_sql`).
|
|
470
|
+
*
|
|
471
|
+
* A row is held by exactly one identity: `user_id` for a project user (what the
|
|
472
|
+
* CLI grants), or `platform_user_id` for a Supatype Cloud account. The engine
|
|
473
|
+
* migrates older single-identity tables; this only has to create the current
|
|
474
|
+
* shape when there is nothing there at all.
|
|
475
|
+
*/
|
|
476
|
+
async function ensureStudioMembersTable(query) {
|
|
477
|
+
await query(`CREATE SCHEMA IF NOT EXISTS _supatype`);
|
|
478
|
+
await query(`CREATE TABLE IF NOT EXISTS _supatype.studio_members (
|
|
479
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
480
|
+
user_id UUID UNIQUE,
|
|
481
|
+
platform_user_id UUID UNIQUE,
|
|
482
|
+
role TEXT NOT NULL,
|
|
483
|
+
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
484
|
+
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
485
|
+
CONSTRAINT studio_members_one_identity
|
|
486
|
+
CHECK (num_nonnulls(user_id, platform_user_id) = 1)
|
|
487
|
+
)`);
|
|
488
|
+
}
|
|
489
|
+
async function studioMembersTableExists(query) {
|
|
490
|
+
const result = await query(`SELECT to_regclass('_supatype.studio_members') IS NOT NULL as exists`);
|
|
491
|
+
return Boolean(result.rows[0]?.exists);
|
|
492
|
+
}
|
|
391
493
|
/** Postgres password for compose `exec psql` (db is not published to the host). */
|
|
392
494
|
export function composePostgresPassword(cwd) {
|
|
393
495
|
return readEnvValue(cwd, "POSTGRES_PASSWORD", "postgres");
|