@supatype/cli 0.1.2 → 0.1.4
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 +111 -108
- package/.turbo/turbo-typecheck.log +1 -1
- package/dist/commands/admin.d.ts +12 -0
- package/dist/commands/admin.d.ts.map +1 -1
- package/dist/commands/admin.js +97 -23
- package/dist/commands/admin.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +287 -182
- package/dist/commands/init.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +12 -1
- package/dist/config.js.map +1 -1
- package/dist/dev-compose.js +3 -3
- package/dist/dev-compose.js.map +1 -1
- package/dist/dev-log-bus.d.ts +1 -1
- package/dist/dev-log-bus.d.ts.map +1 -1
- package/dist/dev-log-bus.js +5 -3
- package/dist/dev-log-bus.js.map +1 -1
- package/dist/dev-ready-panel.d.ts +2 -0
- package/dist/dev-ready-panel.d.ts.map +1 -1
- package/dist/dev-ready-panel.js +18 -0
- package/dist/dev-ready-panel.js.map +1 -1
- package/dist/init-project-detect.d.ts +14 -0
- package/dist/init-project-detect.d.ts.map +1 -0
- package/dist/init-project-detect.js +89 -0
- package/dist/init-project-detect.js.map +1 -0
- package/dist/type-extractor.js +44 -0
- package/dist/type-extractor.js.map +1 -1
- package/dist/ui/dev/DevDashboard.d.ts.map +1 -1
- package/dist/ui/dev/DevDashboard.js +11 -4
- package/dist/ui/dev/DevDashboard.js.map +1 -1
- package/dist/ui/dev/DevReadyPanelView.d.ts +2 -1
- package/dist/ui/dev/DevReadyPanelView.d.ts.map +1 -1
- package/dist/ui/dev/DevReadyPanelView.js +21 -2
- package/dist/ui/dev/DevReadyPanelView.js.map +1 -1
- package/dist/ui/flows/FlowApp.d.ts.map +1 -1
- package/dist/ui/flows/FlowApp.js +3 -1
- package/dist/ui/flows/FlowApp.js.map +1 -1
- package/dist/ui/flows/prompt-fields.d.ts.map +1 -1
- package/dist/ui/flows/prompt-fields.js +9 -5
- package/dist/ui/flows/prompt-fields.js.map +1 -1
- package/dist/ui/runtime/run-clack-flow.d.ts.map +1 -1
- package/dist/ui/runtime/run-clack-flow.js +15 -3
- package/dist/ui/runtime/run-clack-flow.js.map +1 -1
- package/dist/ui/runtime/stdin-after-ink.d.ts +3 -0
- package/dist/ui/runtime/stdin-after-ink.d.ts.map +1 -0
- package/dist/ui/runtime/stdin-after-ink.js +16 -0
- package/dist/ui/runtime/stdin-after-ink.js.map +1 -0
- package/package.json +1 -1
- package/src/commands/admin.ts +135 -26
- package/src/commands/init.ts +360 -213
- package/src/config.ts +13 -1
- package/src/dev-compose.ts +4 -4
- package/src/dev-log-bus.ts +5 -3
- package/src/dev-ready-panel.ts +17 -0
- package/src/init-project-detect.ts +99 -0
- package/src/type-extractor.ts +57 -0
- package/src/ui/dev/DevDashboard.tsx +14 -4
- package/src/ui/dev/DevReadyPanelView.tsx +76 -9
- package/src/ui/flows/FlowApp.tsx +3 -1
- package/src/ui/flows/prompt-fields.tsx +13 -7
- package/src/ui/runtime/run-clack-flow.tsx +16 -3
- package/src/ui/runtime/stdin-after-ink.ts +18 -0
- package/tests/admin-ensure.test.ts +80 -0
- package/tests/config.test.ts +19 -0
- package/tests/dev-ready-panel.test.ts +18 -1
- package/tests/init-project-detect.test.ts +53 -0
- package/tests/stdin-after-ink.test.ts +8 -0
- package/tests/type-extractor.test.ts +101 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -4,6 +4,7 @@ import { render } from "ink";
|
|
|
4
4
|
import { isInteractive } from "../interactive.js";
|
|
5
5
|
import { createClackApi } from "../clack-api.js";
|
|
6
6
|
import { FlowApp } from "../flows/FlowApp.js";
|
|
7
|
+
import { restoreStdinAfterInk } from "./stdin-after-ink.js";
|
|
7
8
|
function RunClackFlowRoot({ run, onComplete, onError }) {
|
|
8
9
|
const started = useRef(false);
|
|
9
10
|
return (_jsx(FlowApp, { bind: (controller) => {
|
|
@@ -22,13 +23,24 @@ export async function runClackFlow(run) {
|
|
|
22
23
|
}
|
|
23
24
|
return new Promise((resolve, reject) => {
|
|
24
25
|
const instance = render(_jsx(RunClackFlowRoot, { run: run, onComplete: (value) => {
|
|
25
|
-
instance
|
|
26
|
+
teardownInk(instance);
|
|
26
27
|
resolve(value);
|
|
27
28
|
}, onError: (err) => {
|
|
28
|
-
instance
|
|
29
|
+
teardownInk(instance);
|
|
29
30
|
reject(err);
|
|
30
|
-
} }));
|
|
31
|
+
} }), { patchConsole: false });
|
|
31
32
|
});
|
|
32
33
|
}
|
|
34
|
+
function teardownInk(instance) {
|
|
35
|
+
try {
|
|
36
|
+
instance.clear();
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// ignore — terminal may already be reset
|
|
40
|
+
}
|
|
41
|
+
instance.unmount();
|
|
42
|
+
instance.cleanup();
|
|
43
|
+
restoreStdinAfterInk();
|
|
44
|
+
}
|
|
33
45
|
export { CLACK_CANCEL } from "./cancel.js";
|
|
34
46
|
//# sourceMappingURL=run-clack-flow.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"run-clack-flow.js","sourceRoot":"","sources":["../../../src/ui/runtime/run-clack-flow.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AACrC,OAAO,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"run-clack-flow.js","sourceRoot":"","sources":["../../../src/ui/runtime/run-clack-flow.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AACrC,OAAO,EAAE,MAAM,EAAiB,MAAM,KAAK,CAAA;AAC3C,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AACjD,OAAO,EAAE,cAAc,EAAiB,MAAM,iBAAiB,CAAA;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAQ3D,SAAS,gBAAgB,CAAC,EAAE,GAAG,EAAE,UAAU,EAAE,OAAO,EAAyB;IAC3E,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IAE7B,OAAO,CACL,KAAC,OAAO,IACN,IAAI,EAAE,CAAC,UAAU,EAAE,EAAE;YACnB,IAAI,OAAO,CAAC,OAAO;gBAAE,OAAM;YAC3B,OAAO,CAAC,OAAO,GAAG,IAAI,CAAA;YACtB,KAAK,GAAG,CAAC,cAAc,CAAC,UAAU,CAAC,CAAC;iBACjC,IAAI,CAAC,UAAU,CAAC;iBAChB,KAAK,CAAC,OAAO,CAAC,CAAA;QACnB,CAAC,GACD,CACH,CAAA;AACH,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,YAAY,CAAI,GAAkC;IACtE,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;QACrB,MAAM,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAA;IACzD,CAAC;IAED,OAAO,IAAI,OAAO,CAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACxC,MAAM,QAAQ,GAAG,MAAM,CACrB,KAAC,gBAAgB,IACf,GAAG,EAAE,GAA0C,EAC/C,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE;gBACpB,WAAW,CAAC,QAAQ,CAAC,CAAA;gBACrB,OAAO,CAAC,KAAU,CAAC,CAAA;YACrB,CAAC,EACD,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;gBACf,WAAW,CAAC,QAAQ,CAAC,CAAA;gBACrB,MAAM,CAAC,GAAG,CAAC,CAAA;YACb,CAAC,GACD,EACF,EAAE,YAAY,EAAE,KAAK,EAAE,CACxB,CAAA;IACH,CAAC,CAAC,CAAA;AACJ,CAAC;AAED,SAAS,WAAW,CAAC,QAAkB;IACrC,IAAI,CAAC;QACH,QAAQ,CAAC,KAAK,EAAE,CAAA;IAClB,CAAC;IAAC,MAAM,CAAC;QACP,yCAAyC;IAC3C,CAAC;IACD,QAAQ,CAAC,OAAO,EAAE,CAAA;IAClB,QAAQ,CAAC,OAAO,EAAE,CAAA;IAClB,oBAAoB,EAAE,CAAA;AACxB,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdin-after-ink.d.ts","sourceRoot":"","sources":["../../../src/ui/runtime/stdin-after-ink.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,wBAAgB,oBAAoB,IAAI,IAAI,CAgB3C"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** Restore stdin after Ink unmount so one-shot commands (init, link) can exit cleanly. */
|
|
2
|
+
export function restoreStdinAfterInk() {
|
|
3
|
+
if (!process.stdin.isTTY)
|
|
4
|
+
return;
|
|
5
|
+
const stdin = process.stdin;
|
|
6
|
+
try {
|
|
7
|
+
if (stdin.isRaw)
|
|
8
|
+
stdin.setRawMode?.(false);
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
// ignore — stdin may already be restored
|
|
12
|
+
}
|
|
13
|
+
stdin.resume();
|
|
14
|
+
process.stdout.write("\n");
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=stdin-after-ink.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stdin-after-ink.js","sourceRoot":"","sources":["../../../src/ui/runtime/stdin-after-ink.ts"],"names":[],"mappings":"AAAA,0FAA0F;AAC1F,MAAM,UAAU,oBAAoB;IAClC,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK;QAAE,OAAM;IAEhC,MAAM,KAAK,GAAG,OAAO,CAAC,KAGrB,CAAA;IAED,IAAI,CAAC;QACH,IAAI,KAAK,CAAC,KAAK;YAAE,KAAK,CAAC,UAAU,EAAE,CAAC,KAAK,CAAC,CAAA;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,yCAAyC;IAC3C,CAAC;IAED,KAAK,CAAC,MAAM,EAAE,CAAA;IACd,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;AAC5B,CAAC"}
|
package/package.json
CHANGED
package/src/commands/admin.ts
CHANGED
|
@@ -17,6 +17,8 @@ import {
|
|
|
17
17
|
} from "../project-config.js"
|
|
18
18
|
import { readEnvValue, upsertEnvFile } from "../env-file.js"
|
|
19
19
|
import { hasEngineOverride } from "../binary-cache.js"
|
|
20
|
+
import { readDevSessionLock } from "../dev-session-lock.js"
|
|
21
|
+
import { composeProjectName } from "../self-host-compose.js"
|
|
20
22
|
import { confirm as uiConfirm } from "../ui/confirm.js"
|
|
21
23
|
import { error, info, plain } from "../ui/messages.js"
|
|
22
24
|
import { promptPassword, promptText } from "../ui/prompts.js"
|
|
@@ -30,6 +32,34 @@ export const ADMIN_PASSWORD_ENV = "SUPATYPE_ADMIN_PASSWORD"
|
|
|
30
32
|
|
|
31
33
|
const BCRYPT_ROUNDS = 10
|
|
32
34
|
|
|
35
|
+
/** GoTrue scopes users to the nil instance id in single-tenant/self-host mode. */
|
|
36
|
+
export const GOTRUE_NIL_INSTANCE_ID = "00000000-0000-0000-0000-000000000000"
|
|
37
|
+
|
|
38
|
+
/** Audience claim used by GoTrue when looking up users (matches GOTRUE_JWT_AUD in compose). */
|
|
39
|
+
export function gotrueJwtAud(cwd: string): string {
|
|
40
|
+
return readEnvValue(cwd, "GOTRUE_JWT_AUD", "authenticated")
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
type DbQuery = (sql: string, params?: unknown[]) => Promise<QueryResult>
|
|
44
|
+
type AuthConfirmedColumn = "email_confirmed_at" | "confirmed_at"
|
|
45
|
+
|
|
46
|
+
/** Prefer GoTrue's column when migrated; fall back to postgres init `confirmed_at`. */
|
|
47
|
+
export async function resolveAuthConfirmedAtColumn(query: DbQuery): Promise<AuthConfirmedColumn> {
|
|
48
|
+
const result = await query(
|
|
49
|
+
`SELECT column_name
|
|
50
|
+
FROM information_schema.columns
|
|
51
|
+
WHERE table_schema = 'auth'
|
|
52
|
+
AND table_name = 'users'
|
|
53
|
+
AND column_name IN ('email_confirmed_at', 'confirmed_at')
|
|
54
|
+
ORDER BY CASE column_name WHEN 'email_confirmed_at' THEN 0 ELSE 1 END
|
|
55
|
+
LIMIT 1`,
|
|
56
|
+
)
|
|
57
|
+
const row = result.rows[0] as { column_name?: string; value?: string } | undefined
|
|
58
|
+
const name = row?.column_name ?? row?.value
|
|
59
|
+
if (name === "email_confirmed_at" || name === "confirmed_at") return name
|
|
60
|
+
return "confirmed_at"
|
|
61
|
+
}
|
|
62
|
+
|
|
33
63
|
export interface EnsureFirstAdminOptions {
|
|
34
64
|
email?: string
|
|
35
65
|
password?: string
|
|
@@ -39,8 +69,6 @@ export interface EnsureFirstAdminOptions {
|
|
|
39
69
|
compose?: { project: string; composePath: string }
|
|
40
70
|
}
|
|
41
71
|
|
|
42
|
-
type DbQuery = (sql: string, params?: unknown[]) => Promise<QueryResult>
|
|
43
|
-
|
|
44
72
|
export function registerAdmin(program: Command): void {
|
|
45
73
|
const adminCmd = program
|
|
46
74
|
.command("admin")
|
|
@@ -81,20 +109,33 @@ export function registerAdmin(program: Command): void {
|
|
|
81
109
|
|
|
82
110
|
info(`Creating admin user: ${email} (role: ${role})...`)
|
|
83
111
|
|
|
84
|
-
const
|
|
85
|
-
|
|
112
|
+
const compose =
|
|
113
|
+
opts.connection === undefined ? resolveDockerComposeContext(cwd, config) : null
|
|
86
114
|
|
|
87
115
|
try {
|
|
88
|
-
|
|
89
|
-
|
|
116
|
+
if (compose) {
|
|
117
|
+
await createAdminUser(
|
|
118
|
+
(sql, params) => composeExecQuery(cwd, compose, sql, params),
|
|
119
|
+
email,
|
|
120
|
+
password,
|
|
121
|
+
role,
|
|
122
|
+
{ cwd },
|
|
123
|
+
)
|
|
124
|
+
} else {
|
|
125
|
+
const pg = await importPg()
|
|
126
|
+
const pool = new pg.Pool({ connectionString: connection, max: 2 })
|
|
127
|
+
try {
|
|
128
|
+
await ensureAuthUsersTable(pool)
|
|
129
|
+
await createAdminUser(pool, email, password, role, { cwd })
|
|
130
|
+
} finally {
|
|
131
|
+
await pool.end()
|
|
132
|
+
}
|
|
133
|
+
}
|
|
90
134
|
info("This user can now log in to the admin panel at /admin")
|
|
91
135
|
} catch (err) {
|
|
92
|
-
const message =
|
|
93
|
-
err instanceof Error ? err.message : "Unknown error"
|
|
136
|
+
const message = err instanceof Error ? err.message : "Unknown error"
|
|
94
137
|
error(`Failed to create admin user: ${message}`)
|
|
95
138
|
process.exit(1)
|
|
96
|
-
} finally {
|
|
97
|
-
await pool.end()
|
|
98
139
|
}
|
|
99
140
|
},
|
|
100
141
|
)
|
|
@@ -252,8 +293,9 @@ export async function ensureFirstAdminUserForProject(
|
|
|
252
293
|
(sql, params) => composeExecQuery(root, merged.compose!, sql, params),
|
|
253
294
|
merged,
|
|
254
295
|
)
|
|
255
|
-
} catch {
|
|
256
|
-
|
|
296
|
+
} catch (err) {
|
|
297
|
+
const message = err instanceof Error ? err.message : String(err)
|
|
298
|
+
error(`Could not ensure first admin user: ${message}`)
|
|
257
299
|
}
|
|
258
300
|
return
|
|
259
301
|
}
|
|
@@ -288,7 +330,10 @@ async function ensureFirstAdminWithQuery(
|
|
|
288
330
|
|
|
289
331
|
const role = options.role ?? "admin"
|
|
290
332
|
try {
|
|
291
|
-
await createAdminUser(query, credentials.email, credentials.password, role, {
|
|
333
|
+
await createAdminUser(query, credentials.email, credentials.password, role, {
|
|
334
|
+
quiet: true,
|
|
335
|
+
cwd,
|
|
336
|
+
})
|
|
292
337
|
} catch (err) {
|
|
293
338
|
const message = err instanceof Error ? err.message : String(err)
|
|
294
339
|
error(`Failed to create admin user: ${message}`)
|
|
@@ -365,7 +410,7 @@ async function createAdminUser(
|
|
|
365
410
|
email: string,
|
|
366
411
|
password: string,
|
|
367
412
|
role: string,
|
|
368
|
-
opts: { quiet?: boolean } = {},
|
|
413
|
+
opts: { quiet?: boolean; aud?: string; cwd?: string } = {},
|
|
369
414
|
): Promise<{ id: string; email: string }> {
|
|
370
415
|
const query: DbQuery =
|
|
371
416
|
typeof (db as Pool).query === "function"
|
|
@@ -373,9 +418,15 @@ async function createAdminUser(
|
|
|
373
418
|
: (db as DbQuery)
|
|
374
419
|
|
|
375
420
|
const normalized = email.toLowerCase()
|
|
376
|
-
const
|
|
377
|
-
|
|
378
|
-
|
|
421
|
+
const aud = opts.aud ?? (opts.cwd ? gotrueJwtAud(opts.cwd) : "authenticated")
|
|
422
|
+
const existing = await query(
|
|
423
|
+
`SELECT id FROM auth.users
|
|
424
|
+
WHERE instance_id = $1::uuid
|
|
425
|
+
AND LOWER(email) = $2
|
|
426
|
+
AND aud = $3
|
|
427
|
+
AND is_sso_user = false`,
|
|
428
|
+
[GOTRUE_NIL_INSTANCE_ID, normalized, aud],
|
|
429
|
+
)
|
|
379
430
|
if (existing.rows.length > 0) {
|
|
380
431
|
throw new Error(`User with email "${email}" already exists.`)
|
|
381
432
|
}
|
|
@@ -387,18 +438,27 @@ async function createAdminUser(
|
|
|
387
438
|
providers: ["email"],
|
|
388
439
|
})
|
|
389
440
|
const userMetadata = JSON.stringify({})
|
|
441
|
+
const confirmedCol = await resolveAuthConfirmedAtColumn(query)
|
|
390
442
|
|
|
391
443
|
const result = await query(
|
|
392
444
|
`INSERT INTO auth.users (
|
|
393
|
-
|
|
445
|
+
instance_id, id, aud, role, email, encrypted_password,
|
|
394
446
|
raw_app_meta_data, raw_user_meta_data,
|
|
395
|
-
|
|
447
|
+
${confirmedCol},
|
|
448
|
+
confirmation_token, recovery_token,
|
|
449
|
+
email_change_token_new, email_change, email_change_token_current,
|
|
450
|
+
phone_change, phone_change_token, reauthentication_token,
|
|
451
|
+
is_sso_user, is_anonymous,
|
|
452
|
+
created_at, updated_at
|
|
396
453
|
) VALUES (
|
|
397
|
-
gen_random_uuid(), $
|
|
398
|
-
$
|
|
399
|
-
now(),
|
|
454
|
+
$1::uuid, gen_random_uuid(), $2, 'authenticated', $3, $4,
|
|
455
|
+
$5::jsonb, $6::jsonb,
|
|
456
|
+
now(),
|
|
457
|
+
'', '', '', '', '', '', '', '',
|
|
458
|
+
false, false,
|
|
459
|
+
now(), now()
|
|
400
460
|
) RETURNING id, email`,
|
|
401
|
-
[normalized, passwordHash, appMetadata, userMetadata],
|
|
461
|
+
[GOTRUE_NIL_INSTANCE_ID, aud, normalized, passwordHash, appMetadata, userMetadata],
|
|
402
462
|
)
|
|
403
463
|
|
|
404
464
|
const user = result.rows[0] as { id: string; email: string }
|
|
@@ -417,7 +477,7 @@ async function ensureAuthUsersTable(pool: Pool): Promise<void> {
|
|
|
417
477
|
|
|
418
478
|
CREATE TABLE IF NOT EXISTS auth.users (
|
|
419
479
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
420
|
-
instance_id UUID,
|
|
480
|
+
instance_id UUID DEFAULT '00000000-0000-0000-0000-000000000000'::uuid,
|
|
421
481
|
aud TEXT DEFAULT 'authenticated',
|
|
422
482
|
role TEXT DEFAULT 'authenticated',
|
|
423
483
|
email TEXT UNIQUE,
|
|
@@ -430,7 +490,13 @@ async function ensureAuthUsersTable(pool: Pool): Promise<void> {
|
|
|
430
490
|
confirmation_token TEXT DEFAULT '',
|
|
431
491
|
recovery_token TEXT DEFAULT '',
|
|
432
492
|
email_change_token_new TEXT DEFAULT '',
|
|
433
|
-
|
|
493
|
+
email_change_token_current TEXT DEFAULT '',
|
|
494
|
+
email_change TEXT DEFAULT '',
|
|
495
|
+
phone_change TEXT DEFAULT '',
|
|
496
|
+
phone_change_token TEXT DEFAULT '',
|
|
497
|
+
reauthentication_token TEXT DEFAULT '',
|
|
498
|
+
is_sso_user BOOLEAN NOT NULL DEFAULT false,
|
|
499
|
+
is_anonymous BOOLEAN NOT NULL DEFAULT false
|
|
434
500
|
);
|
|
435
501
|
`)
|
|
436
502
|
}
|
|
@@ -455,6 +521,33 @@ async function hasAdminUsers(query: DbQuery): Promise<boolean> {
|
|
|
455
521
|
return count > 0
|
|
456
522
|
}
|
|
457
523
|
|
|
524
|
+
/** Postgres password for compose `exec psql` (db is not published to the host). */
|
|
525
|
+
export function composePostgresPassword(cwd: string): string {
|
|
526
|
+
return readEnvValue(cwd, "POSTGRES_PASSWORD", "postgres")
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function resolveDockerComposeContext(
|
|
530
|
+
cwd: string,
|
|
531
|
+
config: SupatypeProjectConfig,
|
|
532
|
+
): { project: string; composePath: string } | null {
|
|
533
|
+
if (resolveRuntimeProvider(config) !== "docker" || hasEngineOverride(config)) {
|
|
534
|
+
return null
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
const session = readDevSessionLock(cwd)
|
|
538
|
+
if (session?.composeProject && session.composePath && existsSync(session.composePath)) {
|
|
539
|
+
return { project: session.composeProject, composePath: session.composePath }
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const composePath = resolve(cwd, ".supatype/self-host/docker-compose.yml")
|
|
543
|
+
if (!existsSync(composePath)) return null
|
|
544
|
+
|
|
545
|
+
return {
|
|
546
|
+
project: composeProjectName(config.project?.name ?? "project"),
|
|
547
|
+
composePath,
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
458
551
|
function composeExecQuery(
|
|
459
552
|
cwd: string,
|
|
460
553
|
compose: { project: string; composePath: string },
|
|
@@ -463,6 +556,7 @@ function composeExecQuery(
|
|
|
463
556
|
): Promise<QueryResult> {
|
|
464
557
|
const db = readEnvValue(cwd, "POSTGRES_DB", "supatype")
|
|
465
558
|
const user = readEnvValue(cwd, "POSTGRES_USER", "supatype_admin")
|
|
559
|
+
const pgPassword = composePostgresPassword(cwd)
|
|
466
560
|
const envFile = join(cwd, ".env")
|
|
467
561
|
const composeDir = dirname(compose.composePath)
|
|
468
562
|
const args = [
|
|
@@ -477,7 +571,20 @@ function composeExecQuery(
|
|
|
477
571
|
if (existsSync(envFile)) args.push("--env-file", envFile)
|
|
478
572
|
|
|
479
573
|
if (params.length === 0) {
|
|
480
|
-
args.push(
|
|
574
|
+
args.push(
|
|
575
|
+
"exec",
|
|
576
|
+
"-T",
|
|
577
|
+
"-e",
|
|
578
|
+
`PGPASSWORD=${pgPassword}`,
|
|
579
|
+
"db",
|
|
580
|
+
"psql",
|
|
581
|
+
"-U",
|
|
582
|
+
user,
|
|
583
|
+
"-d",
|
|
584
|
+
db,
|
|
585
|
+
"-tAc",
|
|
586
|
+
sql,
|
|
587
|
+
)
|
|
481
588
|
const result = spawnSync("docker", args, { cwd: composeDir, encoding: "utf8" })
|
|
482
589
|
if (result.status !== 0) {
|
|
483
590
|
throw new Error((result.stderr ?? result.stdout ?? "compose psql failed").trim())
|
|
@@ -504,6 +611,8 @@ function composeExecQuery(
|
|
|
504
611
|
args.push(
|
|
505
612
|
"exec",
|
|
506
613
|
"-T",
|
|
614
|
+
"-e",
|
|
615
|
+
`PGPASSWORD=${pgPassword}`,
|
|
507
616
|
"db",
|
|
508
617
|
"psql",
|
|
509
618
|
"-U",
|