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 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
- p.outro("Listo.");
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. Arranca en local con \`${cdHint}pnpm dev\`, crea tu primer usuario/organización en /register, y usa @kontrolia/react en tus apps.`);
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 declare function askDeploymentStep(repoRoot: string, db: DatabaseAnswer): Promise<void>;
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;AAiLpD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAsJ3F"}
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"}
@@ -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.2",
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.0"
38
+ "@kontrolia/db": "2.1.2"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@types/node": "^22.10.2",