create-kontrolia-auth 1.1.2 → 2.0.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/README.md +43 -7
- package/dist/index.js +133 -17
- package/dist/steps/application.d.ts +1 -1
- package/dist/steps/application.js +3 -3
- package/dist/steps/database.js +1 -1
- package/dist/steps/deployment.d.ts.map +1 -1
- package/dist/steps/deployment.js +17 -5
- package/dist/utils/docker.d.ts +8 -0
- package/dist/utils/docker.d.ts.map +1 -0
- package/dist/utils/docker.js +47 -0
- package/dist/utils/exec.d.ts +16 -0
- package/dist/utils/exec.d.ts.map +1 -0
- package/dist/utils/exec.js +49 -0
- package/dist/utils/preflight.d.ts +14 -0
- package/dist/utils/preflight.d.ts.map +1 -0
- package/dist/utils/preflight.js +49 -0
- package/dist/utils/scaffold.d.ts +21 -0
- package/dist/utils/scaffold.d.ts.map +1 -0
- package/dist/utils/scaffold.js +65 -0
- package/dist/utils/update.d.ts +12 -0
- package/dist/utils/update.d.ts.map +1 -0
- package/dist/utils/update.js +67 -0
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -1,15 +1,51 @@
|
|
|
1
1
|
# create-kontrolia-auth
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Instalador "todo en uno" de KontrolIA Auth. Pensado para que alguien **sin
|
|
4
|
+
conocimiento técnico** deje el sistema funcionando con un solo comando.
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
2. **¿Dónde despliegas `auth-server`/`admin-panel`?** Docker, Vercel, Coolify, Railway u otro — genera los `.env.local` correspondientes.
|
|
6
|
+
## Instalar (desde cero, sin clonar nada a mano)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
```bash
|
|
9
|
+
npx create-kontrolia-auth mi-app
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Eso hace, en orden:
|
|
13
|
+
|
|
14
|
+
1. **Revisa requisitos** (Node 20+, pnpm, y git/Docker según lo que elijas) y te
|
|
15
|
+
dice exactamente qué instalar si falta algo.
|
|
16
|
+
2. **Descarga** el repositorio en `./mi-app` e instala dependencias.
|
|
17
|
+
3. **Base de datos** — dos caminos independientes:
|
|
18
|
+
- _Ya tengo Supabase_ (Cloud o self-hosted): pegas URL + keys, sin Docker.
|
|
19
|
+
- _Crear uno nuevo self-hosted_: genera `docker/.env`, **levanta los
|
|
20
|
+
contenedores por ti** (`docker compose up -d`) y aplica las migraciones
|
|
21
|
+
en cuanto Postgres está listo.
|
|
22
|
+
4. **Primera aplicación** (opcional): registra tu app y su catálogo de permisos.
|
|
23
|
+
5. **Despliegue**: genera los `.env.local` de `auth-server` y `admin-panel` y te
|
|
24
|
+
da los pasos exactos para Docker, Vercel, Railway, Render, Coolify o manual.
|
|
25
|
+
|
|
26
|
+
Si ya estás **dentro del repo** (desarrollo), el instalador lo detecta y se
|
|
27
|
+
salta la descarga:
|
|
9
28
|
|
|
10
29
|
```bash
|
|
11
|
-
git clone <tu-fork-de-kontrolia-auth>
|
|
12
|
-
cd kontrolia-auth
|
|
13
|
-
pnpm install
|
|
14
30
|
pnpm --filter create-kontrolia-auth dev
|
|
15
31
|
```
|
|
32
|
+
|
|
33
|
+
## Subcomandos
|
|
34
|
+
|
|
35
|
+
| Comando | Qué hace |
|
|
36
|
+
| ------------------------------------- | ----------------------------------------------------------------------------- |
|
|
37
|
+
| `npx create-kontrolia-auth [carpeta]` | Instalación completa (el flujo de arriba). |
|
|
38
|
+
| `npx create-kontrolia-auth update` | Descarga el código nuevo (`git pull`) sobre una copia ya instalada, instala dependencias, y aplica las migraciones nuevas. |
|
|
39
|
+
| `npx create-kontrolia-auth migrate` | Aplica/re-aplica las migraciones contra una base existente. |
|
|
40
|
+
| `npx create-kontrolia-auth doctor` | Solo revisa que tengas Node, pnpm, git y Docker. |
|
|
41
|
+
|
|
42
|
+
Las migraciones son **idempotentes** (solo tocan el schema `kontrolia_auth`), así
|
|
43
|
+
que `migrate` es seguro de correr las veces que necesites.
|
|
44
|
+
|
|
45
|
+
`update` es para quien instaló con `npx create-kontrolia-auth mi-app` y ya
|
|
46
|
+
tiene esa carpeta corriendo — no descarta cambios locales (se detiene si hay
|
|
47
|
+
algo sin guardar) y solo avanza si puede hacer fast-forward sobre `origin/main`.
|
|
48
|
+
Si usas los paquetes `@kontrolia/*` como dependencias npm en tu propia
|
|
49
|
+
aplicación (en vez de tener este repo clonado), actualízalos como a cualquier
|
|
50
|
+
otra dependencia (`pnpm update @kontrolia/db` etc.) y vuelve a correr tu
|
|
51
|
+
propio paso de migración — `update` no aplica a ese caso.
|
package/dist/index.js
CHANGED
|
@@ -4,34 +4,150 @@ import { migrate } from "@kontrolia/db";
|
|
|
4
4
|
import { askApplicationStep } from "./steps/application.js";
|
|
5
5
|
import { askDatabaseStep } from "./steps/database.js";
|
|
6
6
|
import { askDeploymentStep } from "./steps/deployment.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
p.note("Corre `docker compose -f docker/docker-compose.yml up -d` y espera a que Postgres esté healthy antes de continuar con las migraciones.", "Antes de migrar");
|
|
14
|
-
const proceed = await p.confirm({ message: "¿Ya levantaste el docker-compose y Postgres está arriba?" });
|
|
15
|
-
if (p.isCancel(proceed) || !proceed) {
|
|
16
|
-
p.outro("Cuando esté listo, corre de nuevo: pnpm --filter create-kontrolia-auth dev");
|
|
17
|
-
return;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
7
|
+
import { bringUpAndMigrate } from "./utils/docker.js";
|
|
8
|
+
import { runPreflight } from "./utils/preflight.js";
|
|
9
|
+
import { ensureRepo, isInsideRepo } from "./utils/scaffold.js";
|
|
10
|
+
import { pullLatest } from "./utils/update.js";
|
|
11
|
+
/** Applies the schema against an already-reachable database, with a spinner. */
|
|
12
|
+
async function migrateWithSpinner(connectionString) {
|
|
20
13
|
const s = p.spinner();
|
|
21
|
-
s.start("Aplicando migraciones del schema
|
|
14
|
+
s.start("Aplicando migraciones del schema kontrolia_auth");
|
|
22
15
|
try {
|
|
23
|
-
await migrate({ connectionString
|
|
16
|
+
await migrate({ connectionString });
|
|
24
17
|
s.stop("Migraciones aplicadas");
|
|
18
|
+
return true;
|
|
25
19
|
}
|
|
26
20
|
catch (error) {
|
|
27
21
|
s.stop("Fallaron las migraciones");
|
|
28
22
|
p.log.error(error.message);
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/** `create-kontrolia-auth migrate` — re-run migrations against any DB later. */
|
|
27
|
+
async function runMigrateCommand() {
|
|
28
|
+
console.clear();
|
|
29
|
+
p.intro("KontrolIA Auth — migrar base de datos");
|
|
30
|
+
const url = await p.text({
|
|
31
|
+
message: "Connection string de Postgres",
|
|
32
|
+
placeholder: "postgres://postgres:...@host:5432/postgres",
|
|
33
|
+
});
|
|
34
|
+
if (p.isCancel(url) || !url) {
|
|
35
|
+
p.cancel("Cancelado.");
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
const ok = await migrateWithSpinner(url);
|
|
39
|
+
if (!ok)
|
|
40
|
+
process.exitCode = 1;
|
|
41
|
+
p.outro(ok ? "Base de datos al día." : "Revisa la connection string y vuelve a intentar.");
|
|
42
|
+
}
|
|
43
|
+
/** `create-kontrolia-auth doctor` — just the requirements check. */
|
|
44
|
+
async function runDoctor() {
|
|
45
|
+
console.clear();
|
|
46
|
+
p.intro("KontrolIA Auth — diagnóstico del entorno");
|
|
47
|
+
const ok = runPreflight({ git: true, docker: true });
|
|
48
|
+
p.outro(ok ? "Todo listo para instalar." : "Instala lo marcado con ✗ y vuelve a correr.");
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* `create-kontrolia-auth update` — pulls the latest code into an existing
|
|
52
|
+
* self-hosted clone and re-applies migrations. Only makes sense from inside
|
|
53
|
+
* a clone (this is not how someone consuming @kontrolia/* as npm
|
|
54
|
+
* dependencies in their own app updates — for that, bumping the package
|
|
55
|
+
* versions and re-running the app's own migrate step is the right path).
|
|
56
|
+
*/
|
|
57
|
+
async function runUpdateCommand() {
|
|
58
|
+
console.clear();
|
|
59
|
+
p.intro("KontrolIA Auth — actualizar instalación");
|
|
60
|
+
if (!isInsideRepo(process.cwd())) {
|
|
61
|
+
p.log.error("Este comando actualiza una copia clonada de KontrolIA Auth — corre esto dentro de la carpeta donde instalaste todo (la que tiene docker/docker-compose.yml).");
|
|
62
|
+
p.outro("Nada que hacer aquí.");
|
|
63
|
+
process.exitCode = 1;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (!runPreflight({ git: true })) {
|
|
67
|
+
p.outro("Instala lo que falta (arriba) y vuelve a correr.");
|
|
68
|
+
process.exitCode = 1;
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
const updated = await pullLatest(process.cwd());
|
|
72
|
+
if (!updated) {
|
|
29
73
|
process.exitCode = 1;
|
|
30
74
|
return;
|
|
31
75
|
}
|
|
32
|
-
await
|
|
76
|
+
const url = await p.text({
|
|
77
|
+
message: "Connection string de Postgres (para aplicar las migraciones nuevas, si hay)",
|
|
78
|
+
placeholder: "postgres://postgres:...@host:5432/postgres",
|
|
79
|
+
});
|
|
80
|
+
if (p.isCancel(url) || !url) {
|
|
81
|
+
p.outro("Código actualizado. Corre `npx create-kontrolia-auth migrate` cuando quieras aplicar la base de datos.");
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const migrated = await migrateWithSpinner(url);
|
|
85
|
+
p.outro(migrated
|
|
86
|
+
? "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."
|
|
87
|
+
: "El código ya está actualizado, pero revisa el error de las migraciones antes de redesplegar.");
|
|
88
|
+
}
|
|
89
|
+
/** Default flow: preflight → (scaffold if outside repo) → DB → migrate → app → deploy. */
|
|
90
|
+
async function runInstall(targetDirArg) {
|
|
91
|
+
console.clear();
|
|
92
|
+
p.intro("KontrolIA Auth — instalador");
|
|
93
|
+
const inRepo = isInsideRepo(process.cwd());
|
|
94
|
+
if (!runPreflight({ git: !inRepo })) {
|
|
95
|
+
p.outro("Instala lo que falta (arriba) y vuelve a correr el instalador.");
|
|
96
|
+
process.exitCode = 1;
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const scaffold = await ensureRepo(process.cwd(), targetDirArg);
|
|
100
|
+
if (!scaffold) {
|
|
101
|
+
process.exitCode = 1;
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const { repoRoot, scaffolded, dirName } = scaffold;
|
|
105
|
+
const db = await askDatabaseStep(repoRoot);
|
|
106
|
+
let migrated = false;
|
|
107
|
+
if (db.mode === "new-self-hosted") {
|
|
108
|
+
const auto = await p.confirm({
|
|
109
|
+
message: "¿Levanto los contenedores (docker compose up -d) y aplico las migraciones por ti ahora?",
|
|
110
|
+
initialValue: true,
|
|
111
|
+
});
|
|
112
|
+
if (p.isCancel(auto)) {
|
|
113
|
+
p.cancel("Instalación cancelada.");
|
|
114
|
+
process.exit(0);
|
|
115
|
+
}
|
|
116
|
+
if (auto) {
|
|
117
|
+
migrated = await bringUpAndMigrate(repoRoot, db.databaseUrl);
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
p.note("Cuando quieras:\n\n docker compose -f docker/docker-compose.yml up -d\n npx create-kontrolia-auth migrate", "Lo harás a mano");
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
migrated = await migrateWithSpinner(db.databaseUrl);
|
|
125
|
+
}
|
|
126
|
+
// Registering the first application writes to kontrolia_auth tables, so it
|
|
127
|
+
// only makes sense once the schema exists. Env generation below is always
|
|
128
|
+
// useful (it never touches the DB), so it runs either way.
|
|
129
|
+
if (migrated) {
|
|
130
|
+
await askApplicationStep(db);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
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");
|
|
134
|
+
}
|
|
33
135
|
await askDeploymentStep(repoRoot, db);
|
|
34
|
-
|
|
136
|
+
const cdHint = scaffolded && dirName ? `cd ${dirName} && ` : "";
|
|
137
|
+
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.`);
|
|
138
|
+
}
|
|
139
|
+
async function main() {
|
|
140
|
+
const args = process.argv.slice(2);
|
|
141
|
+
const first = args[0];
|
|
142
|
+
if (first === "migrate")
|
|
143
|
+
return runMigrateCommand();
|
|
144
|
+
if (first === "doctor")
|
|
145
|
+
return runDoctor();
|
|
146
|
+
if (first === "update")
|
|
147
|
+
return runUpdateCommand();
|
|
148
|
+
// `create <dir>` / `install <dir>` / `<dir>` / (nothing) all reach install.
|
|
149
|
+
const dir = first === "create" || first === "install" ? args[1] : first;
|
|
150
|
+
return runInstall(dir);
|
|
35
151
|
}
|
|
36
152
|
main().catch((error) => {
|
|
37
153
|
console.error(error);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { DatabaseAnswer } from "./database.js";
|
|
2
2
|
/**
|
|
3
3
|
* Optional final step: registers a first application and its permission
|
|
4
|
-
* catalog.
|
|
4
|
+
* catalog. kontrolia_auth.applications/permissions have no write policy for
|
|
5
5
|
* regular users (see migrations/0010_rls_policies.sql) and no admin API
|
|
6
6
|
* exists yet for it, so this writes directly against Postgres — the same
|
|
7
7
|
* platform-admin path migrate() already uses to apply schema changes.
|
|
@@ -11,7 +11,7 @@ function slugify(name) {
|
|
|
11
11
|
}
|
|
12
12
|
/**
|
|
13
13
|
* Optional final step: registers a first application and its permission
|
|
14
|
-
* catalog.
|
|
14
|
+
* catalog. kontrolia_auth.applications/permissions have no write policy for
|
|
15
15
|
* regular users (see migrations/0010_rls_policies.sql) and no admin API
|
|
16
16
|
* exists yet for it, so this writes directly against Postgres — the same
|
|
17
17
|
* platform-admin path migrate() already uses to apply schema changes.
|
|
@@ -26,7 +26,7 @@ export async function askApplicationStep(db) {
|
|
|
26
26
|
process.exit(0);
|
|
27
27
|
}
|
|
28
28
|
if (!wantsApp) {
|
|
29
|
-
p.note("Puedes registrar aplicaciones más tarde corriendo este wizard de nuevo, o insertando directamente en
|
|
29
|
+
p.note("Puedes registrar aplicaciones más tarde corriendo este wizard de nuevo, o insertando directamente en kontrolia_auth.applications / kontrolia_auth.permissions.", "Omitido");
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
32
|
const name = await textOrExit("Nombre de la aplicación", "Facturación");
|
|
@@ -93,7 +93,7 @@ export async function askApplicationStep(db) {
|
|
|
93
93
|
p.note(`${slug} ya existía — su clave de sincronización (si tiene una) no cambió. Si la perdiste, tendrás que borrar ` +
|
|
94
94
|
"y volver a registrar la aplicación para generar una nueva.", "Aplicación existente");
|
|
95
95
|
}
|
|
96
|
-
p.note("Para habilitarla en una organización
|
|
96
|
+
p.note("Para habilitarla en una organización, entra a admin-panel → \"Aplicaciones\" — cualquier owner/admin puede activarla ahí con un clic.", "Siguiente paso");
|
|
97
97
|
}
|
|
98
98
|
async function textOrExit(message, placeholder) {
|
|
99
99
|
const value = await p.text({ message, placeholder });
|
package/dist/steps/database.js
CHANGED
|
@@ -31,7 +31,7 @@ export async function askDatabaseStep(repoRoot) {
|
|
|
31
31
|
const anonKey = await textOrExit("Anon/public key");
|
|
32
32
|
const serviceRoleKey = await textOrExit("Service role key (server-only, nunca la expongas al navegador)");
|
|
33
33
|
const databaseUrl = await textOrExit("Connection string de Postgres", "postgres://postgres:...@db.tu-proyecto.supabase.co:5432/postgres");
|
|
34
|
-
p.note("Si tu proyecto es Supabase Cloud, activa el Custom Access Token Hook manualmente en Dashboard → Authentication → Hooks → selecciona
|
|
34
|
+
p.note("Si tu proyecto es Supabase Cloud, activa el Custom Access Token Hook manualmente en Dashboard → Authentication → Hooks → selecciona kontrolia_auth.custom_access_token_hook. No se puede automatizar por API.", "Paso manual pendiente");
|
|
35
35
|
return { mode, databaseUrl, supabaseUrl, anonKey, serviceRoleKey };
|
|
36
36
|
}
|
|
37
37
|
const jwtSecret = generateJwtSecret();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/steps/deployment.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/steps/deployment.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AA6BpD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAkI3F"}
|
package/dist/steps/deployment.js
CHANGED
|
@@ -2,10 +2,21 @@ import * as p from "@clack/prompts";
|
|
|
2
2
|
import { updateEnvValue, writeEnvFile } from "../utils/files.js";
|
|
3
3
|
import { registerOAuthClient } from "../utils/oauth-client.js";
|
|
4
4
|
const NEXT_STEPS = {
|
|
5
|
-
docker: "
|
|
6
|
-
vercel: "
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
docker: "Ya está: los servicios auth-server y admin-panel viven en el mismo docker/docker-compose.yml. Corre `docker compose -f docker/docker-compose.yml up -d` para tenerlos arriba.",
|
|
6
|
+
vercel: "1) Instala la CLI: npm i -g vercel\n" +
|
|
7
|
+
"2) En apps/auth-server corre `vercel` y sigue el asistente (crea 1 proyecto).\n" +
|
|
8
|
+
"3) En apps/admin-panel corre `vercel` (crea otro proyecto).\n" +
|
|
9
|
+
"4) En cada proyecto → Settings → Environment Variables, pega las variables de su .env.local (las acabo de generar).\n" +
|
|
10
|
+
"5) `vercel --prod` en cada carpeta para publicar.",
|
|
11
|
+
railway: "1) Instala la CLI: npm i -g @railway/cli && railway login\n" +
|
|
12
|
+
"2) En apps/auth-server: `railway init` y luego `railway up`.\n" +
|
|
13
|
+
"3) Repite en apps/admin-panel.\n" +
|
|
14
|
+
"4) En cada servicio (dashboard de Railway → Variables) pega su .env.local.",
|
|
15
|
+
render: "1) Sube el repo a GitHub.\n" +
|
|
16
|
+
"2) En Render → New → Web Service, apúntalo a apps/auth-server (Build: `pnpm install && pnpm build`, Start: `pnpm start`).\n" +
|
|
17
|
+
"3) Repite para apps/admin-panel.\n" +
|
|
18
|
+
"4) En cada servicio → Environment, pega su .env.local.",
|
|
19
|
+
coolify: "Crea dos recursos 'Next.js App' en Coolify apuntando a apps/auth-server y apps/admin-panel, y pega en cada uno las variables de su .env.local.",
|
|
9
20
|
manual: "Corre `pnpm build && pnpm start` en apps/auth-server y apps/admin-panel en el servidor/K8s de tu elección, usando los .env.local generados.",
|
|
10
21
|
};
|
|
11
22
|
/**
|
|
@@ -19,8 +30,9 @@ export async function askDeploymentStep(repoRoot, db) {
|
|
|
19
30
|
options: [
|
|
20
31
|
{ value: "docker", label: "Docker (incluido en el mismo docker-compose)" },
|
|
21
32
|
{ value: "vercel", label: "Vercel" },
|
|
22
|
-
{ value: "coolify", label: "Coolify" },
|
|
23
33
|
{ value: "railway", label: "Railway" },
|
|
34
|
+
{ value: "render", label: "Render" },
|
|
35
|
+
{ value: "coolify", label: "Coolify" },
|
|
24
36
|
{ value: "manual", label: "Otro / Kubernetes / manual" },
|
|
25
37
|
],
|
|
26
38
|
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-hosted happy path: brings the docker-compose stack up in the
|
|
3
|
+
* background, then keeps retrying the migrations while Postgres finishes
|
|
4
|
+
* booting (a fresh container isn't ready to accept connections the instant
|
|
5
|
+
* `up -d` returns). Returns true once the schema is applied.
|
|
6
|
+
*/
|
|
7
|
+
export declare function bringUpAndMigrate(repoRoot: string, databaseUrl: string): Promise<boolean>;
|
|
8
|
+
//# sourceMappingURL=docker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"docker.d.ts","sourceRoot":"","sources":["../../src/utils/docker.ts"],"names":[],"mappings":"AAMA;;;;;GAKG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAsC/F"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { migrate } from "@kontrolia/db";
|
|
3
|
+
import { commandExists, run } from "./exec.js";
|
|
4
|
+
const COMPOSE_FILE = "docker/docker-compose.yml";
|
|
5
|
+
/**
|
|
6
|
+
* Self-hosted happy path: brings the docker-compose stack up in the
|
|
7
|
+
* background, then keeps retrying the migrations while Postgres finishes
|
|
8
|
+
* booting (a fresh container isn't ready to accept connections the instant
|
|
9
|
+
* `up -d` returns). Returns true once the schema is applied.
|
|
10
|
+
*/
|
|
11
|
+
export async function bringUpAndMigrate(repoRoot, databaseUrl) {
|
|
12
|
+
if (!commandExists("docker")) {
|
|
13
|
+
p.log.error("Docker no está instalado o no está corriendo. Abre Docker Desktop y vuelve a intentar.");
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
const up = p.spinner();
|
|
17
|
+
up.start("Levantando Postgres + GoTrue con docker compose");
|
|
18
|
+
try {
|
|
19
|
+
await run("docker", ["compose", "-f", COMPOSE_FILE, "up", "-d"], { cwd: repoRoot });
|
|
20
|
+
up.stop("Contenedores arriba");
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
up.stop("No se pudo levantar docker compose");
|
|
24
|
+
p.log.error(error.message);
|
|
25
|
+
p.log.info("¿Docker Desktop está abierto? Ábrelo y vuelve a correr el instalador.");
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
const mig = p.spinner();
|
|
29
|
+
mig.start("Esperando a que Postgres acepte conexiones y aplicando migraciones");
|
|
30
|
+
let lastError = null;
|
|
31
|
+
for (let attempt = 1; attempt <= 30; attempt++) {
|
|
32
|
+
try {
|
|
33
|
+
await migrate({ connectionString: databaseUrl });
|
|
34
|
+
mig.stop("Migraciones aplicadas");
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
lastError = error;
|
|
39
|
+
await new Promise((r) => setTimeout(r, 2000));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
mig.stop("Postgres no respondió a tiempo (60s)");
|
|
43
|
+
if (lastError)
|
|
44
|
+
p.log.error(lastError.message);
|
|
45
|
+
p.note("Los contenedores quedaron arriba. Cuando Postgres termine de iniciar corre:\n\n npx create-kontrolia-auth migrate", "Reintenta las migraciones");
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface RunOptions {
|
|
2
|
+
cwd?: string;
|
|
3
|
+
env?: NodeJS.ProcessEnv;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Runs a command to completion, buffering its output. Rejects with the
|
|
7
|
+
* captured stderr/stdout on a non-zero exit. Uses a shell so Windows can
|
|
8
|
+
* resolve the `.cmd` shims for pnpm/docker/git; args with spaces are quoted
|
|
9
|
+
* so a target directory like `mi carpeta` survives that round-trip.
|
|
10
|
+
*/
|
|
11
|
+
export declare function run(command: string, args: string[], options?: RunOptions): Promise<string>;
|
|
12
|
+
/** True if a command resolves on PATH. */
|
|
13
|
+
export declare function commandExists(command: string): boolean;
|
|
14
|
+
/** Best-effort first line of `command --version`, or null if it isn't installed. */
|
|
15
|
+
export declare function tryVersion(command: string, arg?: string): string | null;
|
|
16
|
+
//# sourceMappingURL=exec.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/utils/exec.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAED;;;;;GAKG;AACH,wBAAgB,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,OAAO,GAAE,UAAe,GAAG,OAAO,CAAC,MAAM,CAAC,CAkB9F;AAED,0CAA0C;AAC1C,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAQtD;AAED,oFAAoF;AACpF,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,SAAc,GAAG,MAAM,GAAG,IAAI,CAO5E"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { execSync, spawn } from "node:child_process";
|
|
2
|
+
/**
|
|
3
|
+
* Runs a command to completion, buffering its output. Rejects with the
|
|
4
|
+
* captured stderr/stdout on a non-zero exit. Uses a shell so Windows can
|
|
5
|
+
* resolve the `.cmd` shims for pnpm/docker/git; args with spaces are quoted
|
|
6
|
+
* so a target directory like `mi carpeta` survives that round-trip.
|
|
7
|
+
*/
|
|
8
|
+
export function run(command, args, options = {}) {
|
|
9
|
+
const quoted = args.map((a) => (/[\s"]/.test(a) ? `"${a.replace(/"/g, '\\"')}"` : a)).join(" ");
|
|
10
|
+
return new Promise((resolve, reject) => {
|
|
11
|
+
const child = spawn(`${command} ${quoted}`.trim(), {
|
|
12
|
+
cwd: options.cwd,
|
|
13
|
+
env: { ...process.env, ...options.env },
|
|
14
|
+
shell: true,
|
|
15
|
+
});
|
|
16
|
+
let out = "";
|
|
17
|
+
let err = "";
|
|
18
|
+
child.stdout?.on("data", (d) => (out += d.toString()));
|
|
19
|
+
child.stderr?.on("data", (d) => (err += d.toString()));
|
|
20
|
+
child.on("error", reject);
|
|
21
|
+
child.on("close", (code) => {
|
|
22
|
+
if (code === 0)
|
|
23
|
+
resolve(out);
|
|
24
|
+
else
|
|
25
|
+
reject(new Error(err.trim() || out.trim() || `${command} terminó con código ${code}`));
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/** True if a command resolves on PATH. */
|
|
30
|
+
export function commandExists(command) {
|
|
31
|
+
try {
|
|
32
|
+
const probe = process.platform === "win32" ? "where" : "command -v";
|
|
33
|
+
execSync(`${probe} ${command}`, { stdio: "ignore" });
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Best-effort first line of `command --version`, or null if it isn't installed. */
|
|
41
|
+
export function tryVersion(command, arg = "--version") {
|
|
42
|
+
try {
|
|
43
|
+
const output = execSync(`${command} ${arg}`, { stdio: ["ignore", "pipe", "ignore"] }).toString();
|
|
44
|
+
return (output.trim().split("\n")[0] ?? "").trim() || null;
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface PreflightNeeds {
|
|
2
|
+
/** Needs git to clone the repo (turnkey install from outside the repo). */
|
|
3
|
+
git?: boolean;
|
|
4
|
+
/** Needs Docker for the self-hosted path. */
|
|
5
|
+
docker?: boolean;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Friendly, non-technical requirements check. Prints a ✓/✗ table and, for
|
|
9
|
+
* anything missing, exactly how to install it. Returns false if a required
|
|
10
|
+
* tool is missing so the caller can stop before confusing the user with a
|
|
11
|
+
* deep stack trace.
|
|
12
|
+
*/
|
|
13
|
+
export declare function runPreflight(needs?: PreflightNeeds): boolean;
|
|
14
|
+
//# sourceMappingURL=preflight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.d.ts","sourceRoot":"","sources":["../../src/utils/preflight.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,cAAc;IAC7B,2EAA2E;IAC3E,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,6CAA6C;IAC7C,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AASD;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,GAAE,cAAmB,GAAG,OAAO,CA8ChE"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { tryVersion } from "./exec.js";
|
|
3
|
+
/**
|
|
4
|
+
* Friendly, non-technical requirements check. Prints a ✓/✗ table and, for
|
|
5
|
+
* anything missing, exactly how to install it. Returns false if a required
|
|
6
|
+
* tool is missing so the caller can stop before confusing the user with a
|
|
7
|
+
* deep stack trace.
|
|
8
|
+
*/
|
|
9
|
+
export function runPreflight(needs = {}) {
|
|
10
|
+
const checks = [];
|
|
11
|
+
const nodeMajor = Number(process.versions.node.split(".")[0]);
|
|
12
|
+
checks.push({
|
|
13
|
+
label: "Node.js",
|
|
14
|
+
ok: nodeMajor >= 20,
|
|
15
|
+
detail: `v${process.versions.node}`,
|
|
16
|
+
hint: "Instala Node 20 o superior desde https://nodejs.org (elige la versión LTS).",
|
|
17
|
+
});
|
|
18
|
+
const pnpm = tryVersion("pnpm");
|
|
19
|
+
checks.push({
|
|
20
|
+
label: "pnpm",
|
|
21
|
+
ok: Boolean(pnpm),
|
|
22
|
+
detail: pnpm ?? "no encontrado",
|
|
23
|
+
hint: "Instálalo con: npm install -g pnpm",
|
|
24
|
+
});
|
|
25
|
+
if (needs.git) {
|
|
26
|
+
const git = tryVersion("git");
|
|
27
|
+
checks.push({
|
|
28
|
+
label: "git",
|
|
29
|
+
ok: Boolean(git),
|
|
30
|
+
detail: git ?? "no encontrado",
|
|
31
|
+
hint: "Instala Git desde https://git-scm.com/downloads",
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
if (needs.docker) {
|
|
35
|
+
const docker = tryVersion("docker");
|
|
36
|
+
checks.push({
|
|
37
|
+
label: "Docker",
|
|
38
|
+
ok: Boolean(docker),
|
|
39
|
+
detail: docker ?? "no encontrado",
|
|
40
|
+
hint: "Instala Docker Desktop desde https://www.docker.com/products/docker-desktop y ábrelo antes de continuar.",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
const table = checks.map((c) => `${c.ok ? "✓" : "✗"} ${c.label.padEnd(9)} ${c.detail}`).join("\n");
|
|
44
|
+
p.note(table, "Requisitos");
|
|
45
|
+
const missing = checks.filter((c) => !c.ok);
|
|
46
|
+
for (const m of missing)
|
|
47
|
+
p.log.error(`Falta ${m.label}. ${m.hint}`);
|
|
48
|
+
return missing.length === 0;
|
|
49
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Heuristic: are we already standing inside a checkout of the monorepo? */
|
|
2
|
+
export declare function isInsideRepo(dir: string): boolean;
|
|
3
|
+
export interface ScaffoldResult {
|
|
4
|
+
repoRoot: string;
|
|
5
|
+
/** true when we cloned a fresh copy (vs. running inside an existing one). */
|
|
6
|
+
scaffolded: boolean;
|
|
7
|
+
/** the folder name we cloned into, for the closing "cd" hint. */
|
|
8
|
+
dirName?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Turnkey entrypoint: guarantees there's a working copy of KontrolIA Auth to
|
|
12
|
+
* configure. Run from inside the repo (dev, or an already-cloned copy) it's a
|
|
13
|
+
* no-op. Run from anywhere else (`npx create-kontrolia-auth mi-app`) it does a
|
|
14
|
+
* shallow clone into the target folder and installs dependencies, so a
|
|
15
|
+
* non-technical user never has to know what git or pnpm are.
|
|
16
|
+
*
|
|
17
|
+
* Returns null on any failure the user needs to fix (folder exists, clone or
|
|
18
|
+
* install failed) — the caller stops with a friendly message.
|
|
19
|
+
*/
|
|
20
|
+
export declare function ensureRepo(cwd: string, targetDirArg?: string): Promise<ScaffoldResult | null>;
|
|
21
|
+
//# sourceMappingURL=scaffold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/utils/scaffold.ts"],"names":[],"mappings":"AAOA,4EAA4E;AAC5E,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAEjD;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,UAAU,EAAE,OAAO,CAAC;IACpB,iEAAiE;IACjE,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,GAAG,IAAI,CAAC,CA+CnG"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import * as p from "@clack/prompts";
|
|
4
|
+
import { run } from "./exec.js";
|
|
5
|
+
const REPO_URL = "https://github.com/rauldolores/auth.git";
|
|
6
|
+
/** Heuristic: are we already standing inside a checkout of the monorepo? */
|
|
7
|
+
export function isInsideRepo(dir) {
|
|
8
|
+
return existsSync(join(dir, "docker", "docker-compose.yml")) && existsSync(join(dir, "packages", "cli"));
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Turnkey entrypoint: guarantees there's a working copy of KontrolIA Auth to
|
|
12
|
+
* configure. Run from inside the repo (dev, or an already-cloned copy) it's a
|
|
13
|
+
* no-op. Run from anywhere else (`npx create-kontrolia-auth mi-app`) it does a
|
|
14
|
+
* shallow clone into the target folder and installs dependencies, so a
|
|
15
|
+
* non-technical user never has to know what git or pnpm are.
|
|
16
|
+
*
|
|
17
|
+
* Returns null on any failure the user needs to fix (folder exists, clone or
|
|
18
|
+
* install failed) — the caller stops with a friendly message.
|
|
19
|
+
*/
|
|
20
|
+
export async function ensureRepo(cwd, targetDirArg) {
|
|
21
|
+
if (isInsideRepo(cwd))
|
|
22
|
+
return { repoRoot: cwd, scaffolded: false };
|
|
23
|
+
let dir = targetDirArg;
|
|
24
|
+
if (!dir) {
|
|
25
|
+
const answer = await p.text({
|
|
26
|
+
message: "¿En qué carpeta quieres instalar KontrolIA Auth?",
|
|
27
|
+
placeholder: "kontrolia-auth",
|
|
28
|
+
defaultValue: "kontrolia-auth",
|
|
29
|
+
});
|
|
30
|
+
if (p.isCancel(answer)) {
|
|
31
|
+
p.cancel("Instalación cancelada.");
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
dir = answer || "kontrolia-auth";
|
|
35
|
+
}
|
|
36
|
+
const target = join(cwd, dir);
|
|
37
|
+
if (existsSync(target)) {
|
|
38
|
+
p.log.error(`La carpeta "${dir}" ya existe. Elige otro nombre, o bórrala y vuelve a intentar.`);
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const clone = p.spinner();
|
|
42
|
+
clone.start(`Descargando KontrolIA Auth en ./${dir}`);
|
|
43
|
+
try {
|
|
44
|
+
await run("git", ["clone", "--depth", "1", REPO_URL, target]);
|
|
45
|
+
clone.stop(`Código descargado en ./${dir}`);
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
clone.stop("No se pudo descargar el repositorio");
|
|
49
|
+
p.log.error(error.message);
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
const install = p.spinner();
|
|
53
|
+
install.start("Instalando dependencias (pnpm install) — puede tardar un par de minutos");
|
|
54
|
+
try {
|
|
55
|
+
await run("pnpm", ["install"], { cwd: target });
|
|
56
|
+
install.stop("Dependencias instaladas");
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
install.stop("Falló pnpm install");
|
|
60
|
+
p.log.error(error.message);
|
|
61
|
+
p.note(`Entra a la carpeta y corre pnpm install a mano:\n\n cd ${dir}\n pnpm install`, "Puedes reintentar");
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return { repoRoot: target, scaffolded: true, dirName: dir };
|
|
65
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pulls the latest KontrolIA Auth code into an existing self-hosted clone.
|
|
3
|
+
* Deliberately conservative: refuses to touch a working tree with local
|
|
4
|
+
* changes (never discards anything), and only fast-forwards — if history
|
|
5
|
+
* has diverged (a local commit, a force-push upstream), it stops and tells
|
|
6
|
+
* the operator to sort it out with git directly instead of guessing.
|
|
7
|
+
*
|
|
8
|
+
* Returns true if the working tree ends up on the latest commit (whether or
|
|
9
|
+
* not it actually moved), false if it stopped short of that for any reason.
|
|
10
|
+
*/
|
|
11
|
+
export declare function pullLatest(repoRoot: string): Promise<boolean>;
|
|
12
|
+
//# sourceMappingURL=update.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"update.d.ts","sourceRoot":"","sources":["../../src/utils/update.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAgEnE"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { run } from "./exec.js";
|
|
3
|
+
/**
|
|
4
|
+
* Pulls the latest KontrolIA Auth code into an existing self-hosted clone.
|
|
5
|
+
* Deliberately conservative: refuses to touch a working tree with local
|
|
6
|
+
* changes (never discards anything), and only fast-forwards — if history
|
|
7
|
+
* has diverged (a local commit, a force-push upstream), it stops and tells
|
|
8
|
+
* the operator to sort it out with git directly instead of guessing.
|
|
9
|
+
*
|
|
10
|
+
* Returns true if the working tree ends up on the latest commit (whether or
|
|
11
|
+
* not it actually moved), false if it stopped short of that for any reason.
|
|
12
|
+
*/
|
|
13
|
+
export async function pullLatest(repoRoot) {
|
|
14
|
+
const dirty = (await run("git", ["status", "--porcelain"], { cwd: repoRoot })).trim();
|
|
15
|
+
if (dirty) {
|
|
16
|
+
p.log.error("Hay cambios sin guardar en esta copia — no los voy a tocar. Guárdalos (git commit / git stash) y vuelve a correr `npx create-kontrolia-auth update`.");
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
const fetch = p.spinner();
|
|
20
|
+
fetch.start("Buscando actualizaciones");
|
|
21
|
+
try {
|
|
22
|
+
await run("git", ["fetch", "origin"], { cwd: repoRoot });
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
fetch.stop("No se pudo conectar a GitHub");
|
|
26
|
+
p.log.error(error.message);
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const behindRaw = await run("git", ["rev-list", "--count", "HEAD..origin/main"], { cwd: repoRoot });
|
|
30
|
+
const behind = Number(behindRaw.trim());
|
|
31
|
+
if (behind === 0) {
|
|
32
|
+
fetch.stop("Ya tienes la última versión");
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
fetch.stop(`${behind} cambio${behind === 1 ? "" : "s"} nuevo${behind === 1 ? "" : "s"} disponible${behind === 1 ? "" : "s"}`);
|
|
36
|
+
const log = await run("git", ["log", "--oneline", "HEAD..origin/main"], { cwd: repoRoot });
|
|
37
|
+
p.note(log.trim(), "Qué se va a aplicar");
|
|
38
|
+
const proceed = await p.confirm({ message: "¿Descargar y aplicar estos cambios ahora?", initialValue: true });
|
|
39
|
+
if (p.isCancel(proceed) || !proceed) {
|
|
40
|
+
p.cancel("Actualización cancelada.");
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const pull = p.spinner();
|
|
44
|
+
pull.start("Descargando el código nuevo");
|
|
45
|
+
try {
|
|
46
|
+
await run("git", ["pull", "--ff-only", "origin", "main"], { cwd: repoRoot });
|
|
47
|
+
pull.stop("Código actualizado");
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
pull.stop("No se pudo actualizar automáticamente");
|
|
51
|
+
p.log.error(error.message);
|
|
52
|
+
p.note("El historial local y el de GitHub ya no coinciden (por ejemplo, si hiciste un commit propio). Resuélvelo a mano con git, o clona de nuevo en una carpeta aparte.", "Requiere intervención manual");
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const install = p.spinner();
|
|
56
|
+
install.start("Instalando dependencias nuevas (pnpm install)");
|
|
57
|
+
try {
|
|
58
|
+
await run("pnpm", ["install"], { cwd: repoRoot });
|
|
59
|
+
install.stop("Dependencias al día");
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
install.stop("Falló pnpm install");
|
|
63
|
+
p.log.error(error.message);
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
return true;
|
|
67
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-kontrolia-auth",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"license": "MIT",
|
|
5
|
-
"description": "
|
|
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": [
|
|
7
7
|
"kontrolia",
|
|
8
8
|
"auth",
|
|
@@ -23,6 +23,9 @@
|
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
25
|
"type": "module",
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=20.0.0"
|
|
28
|
+
},
|
|
26
29
|
"bin": {
|
|
27
30
|
"create-kontrolia-auth": "./dist/index.js"
|
|
28
31
|
},
|
|
@@ -32,7 +35,7 @@
|
|
|
32
35
|
"dependencies": {
|
|
33
36
|
"@clack/prompts": "^0.9.1",
|
|
34
37
|
"jose": "^5.9.6",
|
|
35
|
-
"@kontrolia/db": "
|
|
38
|
+
"@kontrolia/db": "2.0.0"
|
|
36
39
|
},
|
|
37
40
|
"devDependencies": {
|
|
38
41
|
"@types/node": "^22.10.2",
|