create-kontrolia-auth 1.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/LICENSE +21 -0
- package/README.md +15 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/steps/application.d.ts +10 -0
- package/dist/steps/application.d.ts.map +1 -0
- package/dist/steps/application.js +92 -0
- package/dist/steps/database.d.ts +13 -0
- package/dist/steps/database.d.ts.map +1 -0
- package/dist/steps/database.js +70 -0
- package/dist/steps/deployment.d.ts +8 -0
- package/dist/steps/deployment.d.ts.map +1 -0
- package/dist/steps/deployment.js +44 -0
- package/dist/utils/files.d.ts +6 -0
- package/dist/utils/files.d.ts.map +1 -0
- package/dist/utils/files.js +11 -0
- package/dist/utils/secrets.d.ts +3 -0
- package/dist/utils/secrets.d.ts.map +1 -0
- package/dist/utils/secrets.js +5 -0
- package/dist/utils/supabase-keys.d.ts +10 -0
- package/dist/utils/supabase-keys.d.ts.map +1 -0
- package/dist/utils/supabase-keys.js +20 -0
- package/package.json +51 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 KontrolIA Auth Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# create-kontrolia-auth
|
|
2
|
+
|
|
3
|
+
Wizard de instalación. Dos preguntas independientes (ver la sección "Desacoplamiento clave" del plan de arquitectura):
|
|
4
|
+
|
|
5
|
+
1. **¿De dónde sale tu Supabase?** Proyecto existente (pegas URL/keys, sin Docker) o uno nuevo self-hosted (genera `docker/.env` con secretos reales).
|
|
6
|
+
2. **¿Dónde despliegas `auth-server`/`admin-panel`?** Docker, Vercel, Coolify, Railway u otro — genera los `.env.local` correspondientes.
|
|
7
|
+
|
|
8
|
+
## Uso
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
git clone <tu-fork-de-kontrolia-auth>
|
|
12
|
+
cd kontrolia-auth
|
|
13
|
+
pnpm install
|
|
14
|
+
pnpm --filter create-kontrolia-auth dev
|
|
15
|
+
```
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import * as p from "@clack/prompts";
|
|
3
|
+
import { migrate } from "@kontrolia/db";
|
|
4
|
+
import { askApplicationStep } from "./steps/application.js";
|
|
5
|
+
import { askDatabaseStep } from "./steps/database.js";
|
|
6
|
+
import { askDeploymentStep } from "./steps/deployment.js";
|
|
7
|
+
async function main() {
|
|
8
|
+
const repoRoot = process.cwd();
|
|
9
|
+
console.clear();
|
|
10
|
+
p.intro("KontrolIA Auth — instalador");
|
|
11
|
+
const db = await askDatabaseStep(repoRoot);
|
|
12
|
+
if (db.mode === "new-self-hosted") {
|
|
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
|
+
}
|
|
20
|
+
const s = p.spinner();
|
|
21
|
+
s.start("Aplicando migraciones del schema kontrolia");
|
|
22
|
+
try {
|
|
23
|
+
await migrate({ connectionString: db.databaseUrl });
|
|
24
|
+
s.stop("Migraciones aplicadas");
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
s.stop("Fallaron las migraciones");
|
|
28
|
+
p.log.error(error.message);
|
|
29
|
+
process.exitCode = 1;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
await askApplicationStep(db);
|
|
33
|
+
await askDeploymentStep(repoRoot, db);
|
|
34
|
+
p.outro("Listo. Crea tu primer usuario/organización desde /register en auth-server, y usa @kontrolia/react en tus apps.");
|
|
35
|
+
}
|
|
36
|
+
main().catch((error) => {
|
|
37
|
+
console.error(error);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
});
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DatabaseAnswer } from "./database.js";
|
|
2
|
+
/**
|
|
3
|
+
* Optional final step: registers a first application and its permission
|
|
4
|
+
* catalog. kontrolia.applications/permissions have no write policy for
|
|
5
|
+
* regular users (see migrations/0010_rls_policies.sql) and no admin API
|
|
6
|
+
* exists yet for it, so this writes directly against Postgres — the same
|
|
7
|
+
* platform-admin path migrate() already uses to apply schema changes.
|
|
8
|
+
*/
|
|
9
|
+
export declare function askApplicationStep(db: DatabaseAnswer): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=application.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"application.d.ts","sourceRoot":"","sources":["../../src/steps/application.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAYpD;;;;;;GAMG;AACH,wBAAsB,kBAAkB,CAAC,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CA6E1E"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { registerApplication } from "@kontrolia/db";
|
|
3
|
+
function slugify(name) {
|
|
4
|
+
return name
|
|
5
|
+
.trim()
|
|
6
|
+
.toLowerCase()
|
|
7
|
+
.normalize("NFD")
|
|
8
|
+
.replace(/[̀-ͯ]/g, "")
|
|
9
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
10
|
+
.replace(/(^-+|-+$)/g, "");
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Optional final step: registers a first application and its permission
|
|
14
|
+
* catalog. kontrolia.applications/permissions have no write policy for
|
|
15
|
+
* regular users (see migrations/0010_rls_policies.sql) and no admin API
|
|
16
|
+
* exists yet for it, so this writes directly against Postgres — the same
|
|
17
|
+
* platform-admin path migrate() already uses to apply schema changes.
|
|
18
|
+
*/
|
|
19
|
+
export async function askApplicationStep(db) {
|
|
20
|
+
const wantsApp = await p.confirm({
|
|
21
|
+
message: "¿Quieres registrar tu primera aplicación (con su catálogo de permisos) ahora?",
|
|
22
|
+
initialValue: true,
|
|
23
|
+
});
|
|
24
|
+
if (p.isCancel(wantsApp)) {
|
|
25
|
+
p.cancel("Instalación cancelada.");
|
|
26
|
+
process.exit(0);
|
|
27
|
+
}
|
|
28
|
+
if (!wantsApp) {
|
|
29
|
+
p.note("Puedes registrar aplicaciones más tarde corriendo este wizard de nuevo, o insertando directamente en kontrolia.applications / kontrolia.permissions.", "Omitido");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const name = await textOrExit("Nombre de la aplicación", "Facturación");
|
|
33
|
+
const slug = slugify(name);
|
|
34
|
+
const environment = await p.select({
|
|
35
|
+
message: "Entorno",
|
|
36
|
+
options: [
|
|
37
|
+
{ value: "development", label: "development" },
|
|
38
|
+
{ value: "staging", label: "staging" },
|
|
39
|
+
{ value: "production", label: "production" },
|
|
40
|
+
],
|
|
41
|
+
initialValue: "development",
|
|
42
|
+
});
|
|
43
|
+
if (p.isCancel(environment)) {
|
|
44
|
+
p.cancel("Instalación cancelada.");
|
|
45
|
+
process.exit(0);
|
|
46
|
+
}
|
|
47
|
+
p.note(`Cada permiso queda como ${slug}.<recurso>.<acción> — p. ej. facturas / crear.`, "Catálogo de permisos");
|
|
48
|
+
const permissions = [];
|
|
49
|
+
let addAnother = true;
|
|
50
|
+
while (addAnother) {
|
|
51
|
+
const resource = await textOrExit("Recurso", "facturas");
|
|
52
|
+
const action = await textOrExit("Acción", "crear");
|
|
53
|
+
const description = await p.text({ message: "Descripción (opcional)", placeholder: "Crear facturas" });
|
|
54
|
+
if (p.isCancel(description)) {
|
|
55
|
+
p.cancel("Instalación cancelada.");
|
|
56
|
+
process.exit(0);
|
|
57
|
+
}
|
|
58
|
+
permissions.push({ resource, action, description: description || undefined });
|
|
59
|
+
const more = await p.confirm({ message: "¿Agregar otro permiso?", initialValue: false });
|
|
60
|
+
if (p.isCancel(more)) {
|
|
61
|
+
p.cancel("Instalación cancelada.");
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
64
|
+
addAnother = more;
|
|
65
|
+
}
|
|
66
|
+
const s = p.spinner();
|
|
67
|
+
s.start(`Registrando ${name} (${slug}) y ${permissions.length} permiso(s)`);
|
|
68
|
+
try {
|
|
69
|
+
const { permissionKeys } = await registerApplication({
|
|
70
|
+
connectionString: db.databaseUrl,
|
|
71
|
+
name,
|
|
72
|
+
slug,
|
|
73
|
+
environment,
|
|
74
|
+
permissions,
|
|
75
|
+
});
|
|
76
|
+
s.stop(`Aplicación ${slug} registrada: ${permissionKeys.join(", ")}`);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
s.stop("No se pudo registrar la aplicación");
|
|
80
|
+
p.log.error(error.message);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
p.note("Para habilitarla en una organización (kontrolia.application_organizations) y asignarla a un rol, hazlo por ahora directo en la base de datos — el admin panel todavía solo permite verla, no darla de alta.", "Siguiente paso");
|
|
84
|
+
}
|
|
85
|
+
async function textOrExit(message, placeholder) {
|
|
86
|
+
const value = await p.text({ message, placeholder });
|
|
87
|
+
if (p.isCancel(value)) {
|
|
88
|
+
p.cancel("Instalación cancelada.");
|
|
89
|
+
process.exit(0);
|
|
90
|
+
}
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface DatabaseAnswer {
|
|
2
|
+
mode: "existing" | "new-self-hosted";
|
|
3
|
+
databaseUrl: string;
|
|
4
|
+
supabaseUrl: string;
|
|
5
|
+
anonKey: string;
|
|
6
|
+
serviceRoleKey: string;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Question A from the architecture plan: where does Postgres + GoTrue come
|
|
10
|
+
* from? Two fully independent paths — neither assumes Docker.
|
|
11
|
+
*/
|
|
12
|
+
export declare function askDatabaseStep(repoRoot: string): Promise<DatabaseAnswer>;
|
|
13
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../../src/steps/database.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,UAAU,GAAG,iBAAiB,CAAC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAmE/E"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { generateJwtSecret } from "../utils/secrets.js";
|
|
3
|
+
import { generateSupabaseKeys } from "../utils/supabase-keys.js";
|
|
4
|
+
import { writeEnvFile } from "../utils/files.js";
|
|
5
|
+
/**
|
|
6
|
+
* Question A from the architecture plan: where does Postgres + GoTrue come
|
|
7
|
+
* from? Two fully independent paths — neither assumes Docker.
|
|
8
|
+
*/
|
|
9
|
+
export async function askDatabaseStep(repoRoot) {
|
|
10
|
+
const mode = await p.select({
|
|
11
|
+
message: "¿De dónde sale tu Supabase (Postgres + Auth)?",
|
|
12
|
+
options: [
|
|
13
|
+
{
|
|
14
|
+
value: "existing",
|
|
15
|
+
label: "Ya tengo un proyecto Supabase (Cloud o self-hosted)",
|
|
16
|
+
hint: "pegas la URL y las keys, sin Docker",
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
value: "new-self-hosted",
|
|
20
|
+
label: "Crear uno nuevo self-hosted con Docker",
|
|
21
|
+
hint: "levanta Postgres + GoTrue + PostgREST + Kong localmente",
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
});
|
|
25
|
+
if (p.isCancel(mode)) {
|
|
26
|
+
p.cancel("Instalación cancelada.");
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
if (mode === "existing") {
|
|
30
|
+
const supabaseUrl = await textOrExit("URL de tu proyecto Supabase", "https://tu-proyecto.supabase.co");
|
|
31
|
+
const anonKey = await textOrExit("Anon/public key");
|
|
32
|
+
const serviceRoleKey = await textOrExit("Service role key (server-only, nunca la expongas al navegador)");
|
|
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 kontrolia.custom_access_token_hook. No se puede automatizar por API.", "Paso manual pendiente");
|
|
35
|
+
return { mode, databaseUrl, supabaseUrl, anonKey, serviceRoleKey };
|
|
36
|
+
}
|
|
37
|
+
const jwtSecret = generateJwtSecret();
|
|
38
|
+
const { anonKey, serviceRoleKey } = await generateSupabaseKeys(jwtSecret);
|
|
39
|
+
const postgresPassword = generateJwtSecret().slice(0, 24);
|
|
40
|
+
const s = p.spinner();
|
|
41
|
+
s.start("Generando docker/.env");
|
|
42
|
+
await writeEnvFile(`${repoRoot}/docker/.env`, {
|
|
43
|
+
POSTGRES_PASSWORD: postgresPassword,
|
|
44
|
+
POSTGRES_PORT: "5432",
|
|
45
|
+
JWT_SECRET: jwtSecret,
|
|
46
|
+
ANON_KEY: anonKey,
|
|
47
|
+
SERVICE_ROLE_KEY: serviceRoleKey,
|
|
48
|
+
SUPABASE_PORT: "8000",
|
|
49
|
+
SUPABASE_PUBLIC_URL: "http://localhost:8000",
|
|
50
|
+
SITE_URL: "http://localhost:3000",
|
|
51
|
+
GOTRUE_MAILER_AUTOCONFIRM: "true",
|
|
52
|
+
});
|
|
53
|
+
s.stop("docker/.env generado");
|
|
54
|
+
p.note("docker compose -f docker/docker-compose.yml up -d\n(corre esto ahora, o después — el wizard no lo hace por ti para que puedas revisar docker/.env primero)", "Siguiente paso");
|
|
55
|
+
return {
|
|
56
|
+
mode,
|
|
57
|
+
databaseUrl: `postgres://postgres:${postgresPassword}@localhost:5432/postgres`,
|
|
58
|
+
supabaseUrl: "http://localhost:8000",
|
|
59
|
+
anonKey,
|
|
60
|
+
serviceRoleKey,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
async function textOrExit(message, placeholder) {
|
|
64
|
+
const value = await p.text({ message, placeholder });
|
|
65
|
+
if (p.isCancel(value)) {
|
|
66
|
+
p.cancel("Instalación cancelada.");
|
|
67
|
+
process.exit(0);
|
|
68
|
+
}
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { DatabaseAnswer } from "./database.js";
|
|
2
|
+
/**
|
|
3
|
+
* Question B from the architecture plan: where do auth-server/admin-panel
|
|
4
|
+
* run? Fully independent from the database question above — this only
|
|
5
|
+
* ever generates env files, it never touches the database connection.
|
|
6
|
+
*/
|
|
7
|
+
export declare function askDeploymentStep(repoRoot: string, db: DatabaseAnswer): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=deployment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/steps/deployment.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAYpD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmC3F"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as p from "@clack/prompts";
|
|
2
|
+
import { writeEnvFile } from "../utils/files.js";
|
|
3
|
+
const NEXT_STEPS = {
|
|
4
|
+
docker: "Se despliegan como parte del mismo docker/docker-compose.yml (servicios auth-server y admin-panel).",
|
|
5
|
+
vercel: "vercel deploy en apps/auth-server y apps/admin-panel por separado, configurando las mismas variables en el dashboard de Vercel.",
|
|
6
|
+
coolify: "Crea dos recursos 'Next.js App' en Coolify apuntando a apps/auth-server y apps/admin-panel, con las variables de .env.local generadas.",
|
|
7
|
+
railway: "railway up en apps/auth-server y apps/admin-panel, o conéctalos como servicios desde el repo en el dashboard de Railway.",
|
|
8
|
+
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.",
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Question B from the architecture plan: where do auth-server/admin-panel
|
|
12
|
+
* run? Fully independent from the database question above — this only
|
|
13
|
+
* ever generates env files, it never touches the database connection.
|
|
14
|
+
*/
|
|
15
|
+
export async function askDeploymentStep(repoRoot, db) {
|
|
16
|
+
const target = await p.select({
|
|
17
|
+
message: "¿Dónde vas a desplegar auth-server / admin-panel?",
|
|
18
|
+
options: [
|
|
19
|
+
{ value: "docker", label: "Docker (incluido en el mismo docker-compose)" },
|
|
20
|
+
{ value: "vercel", label: "Vercel" },
|
|
21
|
+
{ value: "coolify", label: "Coolify" },
|
|
22
|
+
{ value: "railway", label: "Railway" },
|
|
23
|
+
{ value: "manual", label: "Otro / Kubernetes / manual" },
|
|
24
|
+
],
|
|
25
|
+
});
|
|
26
|
+
if (p.isCancel(target)) {
|
|
27
|
+
p.cancel("Instalación cancelada.");
|
|
28
|
+
process.exit(0);
|
|
29
|
+
}
|
|
30
|
+
const s = p.spinner();
|
|
31
|
+
s.start("Generando apps/auth-server/.env.local y apps/admin-panel/.env.local");
|
|
32
|
+
await writeEnvFile(`${repoRoot}/apps/auth-server/.env.local`, {
|
|
33
|
+
NEXT_PUBLIC_SUPABASE_URL: db.supabaseUrl,
|
|
34
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY: db.anonKey,
|
|
35
|
+
SUPABASE_URL: db.supabaseUrl,
|
|
36
|
+
SUPABASE_SERVICE_ROLE_KEY: db.serviceRoleKey,
|
|
37
|
+
});
|
|
38
|
+
await writeEnvFile(`${repoRoot}/apps/admin-panel/.env.local`, {
|
|
39
|
+
NEXT_PUBLIC_SUPABASE_URL: db.supabaseUrl,
|
|
40
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY: db.anonKey,
|
|
41
|
+
});
|
|
42
|
+
s.stop(".env.local generados en auth-server y admin-panel");
|
|
43
|
+
p.note(NEXT_STEPS[target], `Despliegue: ${target}`);
|
|
44
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../src/utils/files.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,SAAS;IACxB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,wBAAsB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAKjF;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEhD"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { writeFile } from "node:fs/promises";
|
|
3
|
+
export async function writeEnvFile(path, values) {
|
|
4
|
+
const contents = Object.entries(values)
|
|
5
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
6
|
+
.join("\n");
|
|
7
|
+
await writeFile(path, `${contents}\n`, "utf8");
|
|
8
|
+
}
|
|
9
|
+
export function fileExists(path) {
|
|
10
|
+
return existsSync(path);
|
|
11
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"secrets.d.ts","sourceRoot":"","sources":["../../src/utils/secrets.ts"],"names":[],"mappings":"AAEA,4EAA4E;AAC5E,wBAAgB,iBAAiB,IAAI,MAAM,CAE1C"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mints local anon/service_role keys for the self-hosted docker-compose
|
|
3
|
+
* path — these are just HS256 JWTs signed with the project's JWT_SECRET,
|
|
4
|
+
* the same scheme Supabase's own key generator uses.
|
|
5
|
+
*/
|
|
6
|
+
export declare function generateSupabaseKeys(jwtSecret: string): Promise<{
|
|
7
|
+
anonKey: string;
|
|
8
|
+
serviceRoleKey: string;
|
|
9
|
+
}>;
|
|
10
|
+
//# sourceMappingURL=supabase-keys.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"supabase-keys.d.ts","sourceRoot":"","sources":["../../src/utils/supabase-keys.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AACH,wBAAsB,oBAAoB,CAAC,SAAS,EAAE,MAAM;;;GAgB3D"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SignJWT } from "jose";
|
|
2
|
+
/**
|
|
3
|
+
* Mints local anon/service_role keys for the self-hosted docker-compose
|
|
4
|
+
* path — these are just HS256 JWTs signed with the project's JWT_SECRET,
|
|
5
|
+
* the same scheme Supabase's own key generator uses.
|
|
6
|
+
*/
|
|
7
|
+
export async function generateSupabaseKeys(jwtSecret) {
|
|
8
|
+
const secret = new TextEncoder().encode(jwtSecret);
|
|
9
|
+
const issuedAt = Math.floor(Date.now() / 1000);
|
|
10
|
+
const tenYears = 10 * 365 * 24 * 60 * 60;
|
|
11
|
+
const sign = (role) => new SignJWT({ role, iss: "kontrolia-auth" })
|
|
12
|
+
.setProtectedHeader({ alg: "HS256" })
|
|
13
|
+
.setIssuedAt(issuedAt)
|
|
14
|
+
.setExpirationTime(issuedAt + tenYears)
|
|
15
|
+
.sign(secret);
|
|
16
|
+
return {
|
|
17
|
+
anonKey: await sign("anon"),
|
|
18
|
+
serviceRoleKey: await sign("service_role"),
|
|
19
|
+
};
|
|
20
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "create-kontrolia-auth",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"description": "Installer wizard for KontrolIA Auth. Two independent questions: (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, Coolify, Railway).",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"kontrolia",
|
|
8
|
+
"auth",
|
|
9
|
+
"cli",
|
|
10
|
+
"installer",
|
|
11
|
+
"iam",
|
|
12
|
+
"supabase"
|
|
13
|
+
],
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/rauldolores/auth.git",
|
|
17
|
+
"directory": "packages/cli"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/rauldolores/auth#readme",
|
|
20
|
+
"bugs": "https://github.com/rauldolores/auth/issues",
|
|
21
|
+
"author": "KontrolIA",
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"type": "module",
|
|
26
|
+
"bin": {
|
|
27
|
+
"create-kontrolia-auth": "./dist/index.js"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@clack/prompts": "^0.9.1",
|
|
34
|
+
"jose": "^5.9.6",
|
|
35
|
+
"@kontrolia/db": "1.0.0"
|
|
36
|
+
},
|
|
37
|
+
"devDependencies": {
|
|
38
|
+
"@types/node": "^22.10.2",
|
|
39
|
+
"eslint": "^9.17.0",
|
|
40
|
+
"tsx": "^4.19.2",
|
|
41
|
+
"typescript": "^5.7.2",
|
|
42
|
+
"@kontrolia/config": "0.1.0"
|
|
43
|
+
},
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc -p tsconfig.json",
|
|
46
|
+
"dev": "tsx src/index.ts",
|
|
47
|
+
"lint": "eslint .",
|
|
48
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
49
|
+
"clean": "rimraf dist"
|
|
50
|
+
}
|
|
51
|
+
}
|