create-kontrolia-auth 2.4.2 → 2.4.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/dist/index.js +46 -4
- package/dist/steps/deployment.d.ts +7 -1
- package/dist/steps/deployment.d.ts.map +1 -1
- package/dist/steps/deployment.js +1 -5
- package/dist/utils/open-browser.d.ts +11 -0
- package/dist/utils/open-browser.d.ts.map +1 -0
- package/dist/utils/open-browser.js +28 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -6,10 +6,23 @@ import { askDatabaseStep } from "./steps/database.js";
|
|
|
6
6
|
import { askDeploymentStep } from "./steps/deployment.js";
|
|
7
7
|
import { bringUpAndMigrate } from "./utils/docker.js";
|
|
8
8
|
import { readEnvFile } from "./utils/files.js";
|
|
9
|
+
import { openBrowser } from "./utils/open-browser.js";
|
|
9
10
|
import { runPreflight } from "./utils/preflight.js";
|
|
10
11
|
import { textOrExit } from "./utils/prompts.js";
|
|
11
12
|
import { ensureRepo, isInsideRepo } from "./utils/scaffold.js";
|
|
12
13
|
import { pullLatest } from "./utils/update.js";
|
|
14
|
+
/** Best-effort host extraction from a Postgres connection string, for the confirmation prompt below. Never throws — an unparseable string just skips the localhost check and confirms anyway. */
|
|
15
|
+
function connectionHost(connectionString) {
|
|
16
|
+
try {
|
|
17
|
+
return new URL(connectionString.replace(/^postgres(ql)?:/, "postgresql:")).hostname || null;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function isLocalHost(host) {
|
|
24
|
+
return host === null || host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "db";
|
|
25
|
+
}
|
|
13
26
|
/** Applies the schema against an already-reachable database, with a spinner. */
|
|
14
27
|
async function migrateWithSpinner(connectionString) {
|
|
15
28
|
const s = p.spinner();
|
|
@@ -37,6 +50,17 @@ async function runMigrateCommand() {
|
|
|
37
50
|
p.cancel("Cancelado.");
|
|
38
51
|
process.exit(0);
|
|
39
52
|
}
|
|
53
|
+
const host = connectionHost(url);
|
|
54
|
+
if (!isLocalHost(host)) {
|
|
55
|
+
const proceed = await p.confirm({
|
|
56
|
+
message: `Esta connection string apunta a "${host}", que no parece ser una base de datos local. ¿Aplicar las migraciones de kontrolia_auth ahí?`,
|
|
57
|
+
initialValue: false,
|
|
58
|
+
});
|
|
59
|
+
if (p.isCancel(proceed) || !proceed) {
|
|
60
|
+
p.cancel("Cancelado.");
|
|
61
|
+
process.exit(0);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
40
64
|
const ok = await migrateWithSpinner(url);
|
|
41
65
|
if (!ok)
|
|
42
66
|
process.exitCode = 1;
|
|
@@ -128,6 +152,17 @@ async function runUpdateCommand() {
|
|
|
128
152
|
p.outro("Código actualizado. Corre `npx create-kontrolia-auth migrate` cuando quieras aplicar la base de datos.");
|
|
129
153
|
return;
|
|
130
154
|
}
|
|
155
|
+
const updateHost = connectionHost(url);
|
|
156
|
+
if (!isLocalHost(updateHost)) {
|
|
157
|
+
const proceed = await p.confirm({
|
|
158
|
+
message: `Esta connection string apunta a "${updateHost}", que no parece ser una base de datos local. ¿Aplicar las migraciones nuevas ahí?`,
|
|
159
|
+
initialValue: false,
|
|
160
|
+
});
|
|
161
|
+
if (p.isCancel(proceed) || !proceed) {
|
|
162
|
+
p.outro("Código actualizado. Corre `npx create-kontrolia-auth migrate` cuando quieras aplicar la base de datos.");
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
131
166
|
const migrated = await migrateWithSpinner(url);
|
|
132
167
|
p.outro(migrated
|
|
133
168
|
? "Listo. Si despliegas con Docker: `docker compose -f docker/docker-compose.yml up -d --build`. Si usas Vercel/Railway/Render/Coolify, vuelve a desplegar (push a tu rama, o su CLI) para que tomen el código nuevo."
|
|
@@ -188,8 +223,11 @@ async function runDeployCommand() {
|
|
|
188
223
|
serviceRoleKey = await textOrExit("Service role key (server-only, nunca la expongas al navegador)");
|
|
189
224
|
}
|
|
190
225
|
const db = { mode: "existing", databaseUrl: "", supabaseUrl, anonKey, serviceRoleKey };
|
|
191
|
-
await askDeploymentStep(process.cwd(), db);
|
|
192
|
-
|
|
226
|
+
const deployment = await askDeploymentStep(process.cwd(), db);
|
|
227
|
+
openBrowser(deployment.authServerUrl);
|
|
228
|
+
openBrowser(deployment.adminPanelUrl);
|
|
229
|
+
p.outro(`Listo. Deberían haberse abierto dos pestañas: ${deployment.authServerUrl} y ${deployment.adminPanelUrl}. ` +
|
|
230
|
+
"Si no cargan porque el servidor no está corriendo todavía en esa URL, recárgalas una vez que lo esté.");
|
|
193
231
|
}
|
|
194
232
|
/** Default flow: preflight → (scaffold if outside repo) → DB → migrate → app → deploy. */
|
|
195
233
|
async function runInstall(targetDirArg) {
|
|
@@ -237,9 +275,13 @@ async function runInstall(targetDirArg) {
|
|
|
237
275
|
else {
|
|
238
276
|
p.note("Cuando la base de datos esté lista corre `npx create-kontrolia-auth migrate`, y registra tu primera aplicación desde admin-panel.", "Migraciones pendientes");
|
|
239
277
|
}
|
|
240
|
-
await askDeploymentStep(repoRoot, db);
|
|
278
|
+
const deployment = await askDeploymentStep(repoRoot, db);
|
|
279
|
+
openBrowser(deployment.authServerUrl);
|
|
280
|
+
openBrowser(deployment.adminPanelUrl);
|
|
241
281
|
const cdHint = scaffolded && dirName ? `cd ${dirName} && ` : "";
|
|
242
|
-
p.outro(`Listo.
|
|
282
|
+
p.outro(`Listo. Deberían haberse abierto dos pestañas — la de inicio de sesión (${deployment.authServerUrl}) y el panel de ` +
|
|
283
|
+
`administración (${deployment.adminPanelUrl}). Si no cargan porque los servidores aún no están arriba, corre ` +
|
|
284
|
+
`\`${cdHint}pnpm dev\` y recárgalas. Crea tu primer usuario/organización en /register, y usa @kontrolia/react en tus apps.`);
|
|
243
285
|
}
|
|
244
286
|
async function main() {
|
|
245
287
|
const args = process.argv.slice(2);
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import type { DatabaseAnswer } from "./database.js";
|
|
2
|
+
export type DeployTarget = "docker" | "vercel" | "railway" | "render" | "coolify" | "manual";
|
|
2
3
|
/**
|
|
3
4
|
* Question B from the architecture plan: where do auth-server/admin-panel
|
|
4
5
|
* run? Fully independent from the database question above — this only
|
|
5
6
|
* ever generates env files, it never touches the database connection.
|
|
6
7
|
*/
|
|
7
|
-
export
|
|
8
|
+
export interface DeploymentAnswer {
|
|
9
|
+
authServerUrl: string;
|
|
10
|
+
adminPanelUrl: string;
|
|
11
|
+
target: DeployTarget;
|
|
12
|
+
}
|
|
13
|
+
export declare function askDeploymentStep(repoRoot: string, db: DatabaseAnswer): Promise<DeploymentAnswer>;
|
|
8
14
|
//# sourceMappingURL=deployment.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/steps/deployment.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/steps/deployment.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEpD,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAC;AA+K7F;;;;GAIG;AACH,MAAM,WAAW,gBAAgB;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;IACtB,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAwJvG"}
|
package/dist/steps/deployment.js
CHANGED
|
@@ -142,11 +142,6 @@ async function tryAutoCreateVercelProjects(repoRoot, authServerEnv, adminPanelEn
|
|
|
142
142
|
p.note("Algo falló creando los proyectos — usa los pasos manuales de abajo para lo que no se haya creado.", "Revisa el error de arriba");
|
|
143
143
|
return false;
|
|
144
144
|
}
|
|
145
|
-
/**
|
|
146
|
-
* Question B from the architecture plan: where do auth-server/admin-panel
|
|
147
|
-
* run? Fully independent from the database question above — this only
|
|
148
|
-
* ever generates env files, it never touches the database connection.
|
|
149
|
-
*/
|
|
150
145
|
export async function askDeploymentStep(repoRoot, db) {
|
|
151
146
|
// A previous install/deploy already wrote these into .env.local — reuse
|
|
152
147
|
// them as editable defaults instead of making the user retype the same
|
|
@@ -276,4 +271,5 @@ export async function askDeploymentStep(repoRoot, db) {
|
|
|
276
271
|
if (!autoCreated) {
|
|
277
272
|
p.note(NEXT_STEPS[target], `Despliegue: ${target}`);
|
|
278
273
|
}
|
|
274
|
+
return { authServerUrl, adminPanelUrl, target };
|
|
279
275
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Best-effort browser open, no new dependency (avoids pulling in the `open`
|
|
3
|
+
* npm package for three lines of platform dispatch). Never throws — a
|
|
4
|
+
* failed/unsupported open shouldn't fail the installer over a convenience
|
|
5
|
+
* feature. Callers may open a URL before its server is actually listening
|
|
6
|
+
* (e.g. right after the wizard finishes, before the user runs `pnpm dev`) —
|
|
7
|
+
* that's expected; the tab just needs a manual refresh once it's up, same
|
|
8
|
+
* as most dev-server CLIs that open the browser optimistically.
|
|
9
|
+
*/
|
|
10
|
+
export declare function openBrowser(url: string): void;
|
|
11
|
+
//# sourceMappingURL=open-browser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-browser.d.ts","sourceRoot":"","sources":["../../src/utils/open-browser.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAiB7C"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Best-effort browser open, no new dependency (avoids pulling in the `open`
|
|
4
|
+
* npm package for three lines of platform dispatch). Never throws — a
|
|
5
|
+
* failed/unsupported open shouldn't fail the installer over a convenience
|
|
6
|
+
* feature. Callers may open a URL before its server is actually listening
|
|
7
|
+
* (e.g. right after the wizard finishes, before the user runs `pnpm dev`) —
|
|
8
|
+
* that's expected; the tab just needs a manual refresh once it's up, same
|
|
9
|
+
* as most dev-server CLIs that open the browser optimistically.
|
|
10
|
+
*/
|
|
11
|
+
export function openBrowser(url) {
|
|
12
|
+
try {
|
|
13
|
+
const child = process.platform === "win32"
|
|
14
|
+
? spawn("cmd", ["/c", "start", "", url], { detached: true, stdio: "ignore", windowsHide: true })
|
|
15
|
+
: process.platform === "darwin"
|
|
16
|
+
? spawn("open", [url], { detached: true, stdio: "ignore" })
|
|
17
|
+
: spawn("xdg-open", [url], { detached: true, stdio: "ignore" });
|
|
18
|
+
// Without a listener, a spawn failure (e.g. no xdg-open on a headless
|
|
19
|
+
// box) is an unhandled 'error' event — Node treats that as fatal and
|
|
20
|
+
// crashes the CLI right after a successful install. Swallow it: the
|
|
21
|
+
// URL is always printed as text too, so this is purely a convenience.
|
|
22
|
+
child.on("error", () => { });
|
|
23
|
+
child.unref();
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
// Best-effort only.
|
|
27
|
+
}
|
|
28
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-kontrolia-auth",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.4",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"description": "Turnkey installer for KontrolIA Auth. `npx create-kontrolia-auth mi-app` checks requirements, downloads the repo, installs deps, and runs a wizard: (1) connect to an existing Supabase project or create a new self-hosted one via Docker; (2) where to deploy auth-server/admin-panel (Docker, Vercel, Railway, Render, Coolify). Also exposes `update`, `migrate` and `doctor` subcommands.",
|
|
6
6
|
"keywords": [
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@clack/prompts": "^0.9.1",
|
|
37
37
|
"jose": "^5.9.6",
|
|
38
|
-
"@kontrolia/db": "2.1.
|
|
38
|
+
"@kontrolia/db": "2.1.2"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@types/node": "^22.10.2",
|