create-kontrolia-auth 2.0.0 → 2.1.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 +6 -2
- package/dist/steps/deployment.d.ts.map +1 -1
- package/dist/steps/deployment.js +84 -14
- package/dist/utils/update.d.ts.map +1 -1
- package/dist/utils/update.js +6 -1
- package/dist/utils/vercel-api.d.ts +35 -0
- package/dist/utils/vercel-api.d.ts.map +1 -0
- package/dist/utils/vercel-api.js +61 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,8 +20,12 @@ Eso hace, en orden:
|
|
|
20
20
|
contenedores por ti** (`docker compose up -d`) y aplica las migraciones
|
|
21
21
|
en cuanto Postgres está listo.
|
|
22
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
|
|
24
|
-
|
|
23
|
+
5. **Despliegue**: genera los `.env.local` de `auth-server` y `admin-panel`. Si
|
|
24
|
+
eliges Vercel y el repo ya está en GitHub, puede **crear los dos proyectos
|
|
25
|
+
por ti** vía la API de Vercel (carpeta y variables de entorno correctas de
|
|
26
|
+
una vez, sin tocar el dashboard ni la CLI de Vercel) — solo pide un API
|
|
27
|
+
token de vercel.com/account/tokens. Para Docker, Railway, Render o
|
|
28
|
+
Coolify te da los pasos exactos.
|
|
25
29
|
|
|
26
30
|
Si ya estás **dentro del repo** (desarrollo), el instalador lo detecta y se
|
|
27
31
|
salta la descarga:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/steps/deployment.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"deployment.d.ts","sourceRoot":"","sources":["../../src/steps/deployment.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AA6GpD;;;;GAIG;AACH,wBAAsB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAwI3F"}
|
package/dist/steps/deployment.js
CHANGED
|
@@ -1,17 +1,25 @@
|
|
|
1
1
|
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
|
+
import { createVercelProject, detectGitHubRepo } from "../utils/vercel-api.js";
|
|
4
5
|
const NEXT_STEPS = {
|
|
5
6
|
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: "
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
|
|
12
|
-
"
|
|
13
|
-
"
|
|
14
|
-
"
|
|
7
|
+
vercel: "Lo más confiable con un monorepo pnpm es conectar el repo desde el dashboard (no desplegar carpetas sueltas por CLI):\n" +
|
|
8
|
+
"1) vercel.com → Add New → Project → importa este repo de GitHub.\n" +
|
|
9
|
+
"2) Root Directory: apps/auth-server. Framework: Next.js (se detecta solo) — Vercel ve el pnpm-lock.yaml de la raíz del repo y usa pnpm automáticamente.\n" +
|
|
10
|
+
"3) Settings → Environment Variables, pega las variables de apps/auth-server/.env.local.\n" +
|
|
11
|
+
"4) Repite con un segundo proyecto: Root Directory apps/admin-panel y las variables de admin-panel/.env.local.\n" +
|
|
12
|
+
"\n" +
|
|
13
|
+
"Si prefieres la CLI (`npm i -g vercel`): corre `vercel` desde la RAÍZ del repo, nunca estando ya dentro de apps/auth-server — " +
|
|
14
|
+
"cuando pregunte en qué carpeta está el código, contesta apps/auth-server. Si corres `vercel` estando ya adentro de esa " +
|
|
15
|
+
"carpeta, solo sube esos archivos (sin el pnpm-lock.yaml ni los paquetes hermanos de la raíz) y el build falla con " +
|
|
16
|
+
'"npm error Unsupported URL Type workspace:". Repite desde la raíz para admin-panel.',
|
|
17
|
+
railway: "Recomendado: conecta el repo desde el dashboard de Railway (New Project → Deploy from GitHub repo), un servicio con " +
|
|
18
|
+
"Root Directory apps/auth-server y otro con apps/admin-panel — así Railway siempre parte del repo completo.\n" +
|
|
19
|
+
"Por CLI (`npm i -g @railway/cli && railway login`): corre `railway init`/`railway up` desde la RAÍZ del repo, nunca " +
|
|
20
|
+
"estando ya dentro de apps/auth-server — igual que con Vercel, si subes solo esa carpeta te falta el pnpm-lock.yaml de " +
|
|
21
|
+
"la raíz y el install falla contra los paquetes @kontrolia/* (workspace:*).\n" +
|
|
22
|
+
"En cada servicio (dashboard de Railway → Variables) pega su .env.local.",
|
|
15
23
|
render: "1) Sube el repo a GitHub.\n" +
|
|
16
24
|
"2) En Render → New → Web Service, apúntalo a apps/auth-server (Build: `pnpm install && pnpm build`, Start: `pnpm start`).\n" +
|
|
17
25
|
"3) Repite para apps/admin-panel.\n" +
|
|
@@ -19,6 +27,63 @@ const NEXT_STEPS = {
|
|
|
19
27
|
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.",
|
|
20
28
|
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.",
|
|
21
29
|
};
|
|
30
|
+
/**
|
|
31
|
+
* Offers to create both Vercel projects via the REST API instead of walking
|
|
32
|
+
* the user through the dashboard/CLI by hand — the same three fields that
|
|
33
|
+
* are easy to get wrong doing this manually (gitRepository, rootDirectory,
|
|
34
|
+
* environmentVariables) are just fields in one request body here. Returns
|
|
35
|
+
* true only if both projects were created, so the caller knows whether to
|
|
36
|
+
* still show the manual fallback instructions.
|
|
37
|
+
*/
|
|
38
|
+
async function tryAutoCreateVercelProjects(repoRoot, authServerEnv, adminPanelEnv) {
|
|
39
|
+
const repo = await detectGitHubRepo(repoRoot);
|
|
40
|
+
if (!repo) {
|
|
41
|
+
p.note("No encontré un remoto de GitHub en este repo (`git remote get-url origin`) — sin eso no puedo crear los " +
|
|
42
|
+
"proyectos de Vercel por ti. Sube el repo a GitHub y vuelve a correr el instalador, o sigue los pasos " +
|
|
43
|
+
"manuales de abajo.", "Creación automática no disponible");
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
const auto = await p.confirm({
|
|
47
|
+
message: `¿Quieres que cree los dos proyectos de Vercel automáticamente (conectados a ${repo}, con sus variables ya puestas)? Solo necesito un API token de Vercel.`,
|
|
48
|
+
initialValue: true,
|
|
49
|
+
});
|
|
50
|
+
if (p.isCancel(auto) || !auto)
|
|
51
|
+
return false;
|
|
52
|
+
const token = await p.password({
|
|
53
|
+
message: "API token de Vercel (vercel.com/account/tokens — no se guarda, solo se usa ahora)",
|
|
54
|
+
validate: (value) => (value.trim() ? undefined : "Requerido"),
|
|
55
|
+
});
|
|
56
|
+
if (p.isCancel(token))
|
|
57
|
+
return false;
|
|
58
|
+
const namePrefix = repo.split("/")[1] ?? "kontrolia-auth";
|
|
59
|
+
const authSpinner = p.spinner();
|
|
60
|
+
authSpinner.start("Creando proyecto de Vercel para auth-server");
|
|
61
|
+
const authResult = await createVercelProject({
|
|
62
|
+
token,
|
|
63
|
+
repo,
|
|
64
|
+
name: `${namePrefix}-auth-server`,
|
|
65
|
+
rootDirectory: "apps/auth-server",
|
|
66
|
+
env: authServerEnv,
|
|
67
|
+
});
|
|
68
|
+
authSpinner.stop(authResult.ok ? "Proyecto auth-server creado" : `Falló auth-server: ${authResult.error}`);
|
|
69
|
+
const adminSpinner = p.spinner();
|
|
70
|
+
adminSpinner.start("Creando proyecto de Vercel para admin-panel");
|
|
71
|
+
const adminResult = await createVercelProject({
|
|
72
|
+
token,
|
|
73
|
+
repo,
|
|
74
|
+
name: `${namePrefix}-admin-panel`,
|
|
75
|
+
rootDirectory: "apps/admin-panel",
|
|
76
|
+
env: adminPanelEnv,
|
|
77
|
+
});
|
|
78
|
+
adminSpinner.stop(adminResult.ok ? "Proyecto admin-panel creado" : `Falló admin-panel: ${adminResult.error}`);
|
|
79
|
+
if (authResult.ok && adminResult.ok) {
|
|
80
|
+
p.note("Los dos proyectos ya están conectados a tu repo, con la carpeta y las variables correctas. Entra a " +
|
|
81
|
+
"vercel.com/dashboard y dale \"Deploy\" a cada uno (o empuja un commit a tu rama principal) para el primer despliegue.", "Listo");
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
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");
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
22
87
|
/**
|
|
23
88
|
* Question B from the architecture plan: where do auth-server/admin-panel
|
|
24
89
|
* run? Fully independent from the database question above — this only
|
|
@@ -99,21 +164,23 @@ export async function askDeploymentStep(repoRoot, db) {
|
|
|
99
164
|
}
|
|
100
165
|
const s = p.spinner();
|
|
101
166
|
s.start("Generando apps/auth-server/.env.local y apps/admin-panel/.env.local");
|
|
102
|
-
|
|
167
|
+
const authServerEnv = {
|
|
103
168
|
NEXT_PUBLIC_SUPABASE_URL: db.supabaseUrl,
|
|
104
169
|
NEXT_PUBLIC_SUPABASE_ANON_KEY: db.anonKey,
|
|
105
170
|
SUPABASE_URL: db.supabaseUrl,
|
|
106
171
|
SUPABASE_SERVICE_ROLE_KEY: db.serviceRoleKey,
|
|
107
172
|
NEXT_PUBLIC_ADMIN_PANEL_URL: adminPanelUrl,
|
|
108
173
|
NEXT_PUBLIC_COOKIE_DOMAIN: cookieDomain,
|
|
109
|
-
}
|
|
110
|
-
await writeEnvFile(`${repoRoot}/apps/
|
|
174
|
+
};
|
|
175
|
+
await writeEnvFile(`${repoRoot}/apps/auth-server/.env.local`, authServerEnv);
|
|
176
|
+
const adminPanelEnv = {
|
|
111
177
|
NEXT_PUBLIC_SUPABASE_URL: db.supabaseUrl,
|
|
112
178
|
NEXT_PUBLIC_SUPABASE_ANON_KEY: db.anonKey,
|
|
113
179
|
NEXT_PUBLIC_AUTH_SERVER_URL: authServerUrl,
|
|
114
180
|
NEXT_PUBLIC_COOKIE_DOMAIN: cookieDomain,
|
|
115
181
|
NEXT_PUBLIC_OAUTH_CLIENT_ID: oauthClientId ?? "",
|
|
116
|
-
}
|
|
182
|
+
};
|
|
183
|
+
await writeEnvFile(`${repoRoot}/apps/admin-panel/.env.local`, adminPanelEnv);
|
|
117
184
|
if (db.mode === "new-self-hosted") {
|
|
118
185
|
// database.ts wrote SITE_URL="http://localhost:3000" as a fixed default
|
|
119
186
|
// *before* this step ever asked where auth-server actually lives —
|
|
@@ -134,5 +201,8 @@ export async function askDeploymentStep(repoRoot, db) {
|
|
|
134
201
|
p.note("docker/.env se actualizó con las URLs reales — como el contenedor de auth (GoTrue) ya estaba arriba con los " +
|
|
135
202
|
"valores por defecto, corre `docker compose -f docker/docker-compose.yml up -d` una vez más para que los tome.", "Reinicia el docker-compose");
|
|
136
203
|
}
|
|
137
|
-
|
|
204
|
+
const autoCreated = target === "vercel" && (await tryAutoCreateVercelProjects(repoRoot, authServerEnv, adminPanelEnv));
|
|
205
|
+
if (!autoCreated) {
|
|
206
|
+
p.note(NEXT_STEPS[target], `Despliegue: ${target}`);
|
|
207
|
+
}
|
|
138
208
|
}
|
|
@@ -1 +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,
|
|
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,CAqEnE"}
|
package/dist/utils/update.js
CHANGED
|
@@ -11,7 +11,12 @@ import { run } from "./exec.js";
|
|
|
11
11
|
* not it actually moved), false if it stopped short of that for any reason.
|
|
12
12
|
*/
|
|
13
13
|
export async function pullLatest(repoRoot) {
|
|
14
|
-
|
|
14
|
+
// Untracked files (a stray .vercel/ from a manual deploy, editor scratch
|
|
15
|
+
// files, anything not in .gitignore yet) are deliberately not treated as
|
|
16
|
+
// "dirty" here — git pull can't silently discard something it was never
|
|
17
|
+
// tracking, and it refuses on its own if an incoming file would collide
|
|
18
|
+
// with one. Only local edits to files git already tracks block the pull.
|
|
19
|
+
const dirty = (await run("git", ["status", "--porcelain", "--untracked-files=no"], { cwd: repoRoot })).trim();
|
|
15
20
|
if (dirty) {
|
|
16
21
|
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
22
|
return false;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { EnvValues } from "./files.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolves "owner/repo" from the git remote, so the wizard can offer to
|
|
4
|
+
* create Vercel projects automatically instead of walking the user through
|
|
5
|
+
* `vercel`/dashboard steps by hand. Only understands GitHub remotes (https
|
|
6
|
+
* or ssh) — Vercel's own gitRepository.type also supports gitlab/bitbucket,
|
|
7
|
+
* but this product's docs and CLI only ever assume GitHub.
|
|
8
|
+
*/
|
|
9
|
+
export declare function detectGitHubRepo(repoRoot: string): Promise<string | null>;
|
|
10
|
+
export interface CreateVercelProjectOptions {
|
|
11
|
+
token: string;
|
|
12
|
+
/** "owner/repo" on GitHub. */
|
|
13
|
+
repo: string;
|
|
14
|
+
name: string;
|
|
15
|
+
rootDirectory: string;
|
|
16
|
+
env: EnvValues;
|
|
17
|
+
}
|
|
18
|
+
export interface CreateVercelProjectResult {
|
|
19
|
+
ok: boolean;
|
|
20
|
+
/** Only present when ok is false. */
|
|
21
|
+
error?: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Creates a Vercel project via the REST API — connected to the GitHub repo,
|
|
25
|
+
* scoped to one app's folder in this monorepo, with its environment
|
|
26
|
+
* variables already set. This is what makes automatic setup possible: the
|
|
27
|
+
* same three things (gitRepository, rootDirectory, environmentVariables)
|
|
28
|
+
* that are easy to get wrong doing this by hand (see the "vercel CLI run
|
|
29
|
+
* from inside the app folder" failure mode) are just fields in one request
|
|
30
|
+
* body here.
|
|
31
|
+
*
|
|
32
|
+
* https://vercel.com/docs/rest-api/reference/endpoints/projects/create-a-new-project
|
|
33
|
+
*/
|
|
34
|
+
export declare function createVercelProject(options: CreateVercelProjectOptions): Promise<CreateVercelProjectResult>;
|
|
35
|
+
//# sourceMappingURL=vercel-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vercel-api.d.ts","sourceRoot":"","sources":["../../src/utils/vercel-api.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C;;;;;;GAMG;AACH,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAQ/E;AAED,MAAM,WAAW,0BAA0B;IACzC,KAAK,EAAE,MAAM,CAAC;IACd,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,MAAM,CAAC;IACtB,GAAG,EAAE,SAAS,CAAC;CAChB;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,OAAO,CAAC;IACZ,qCAAqC;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,0BAA0B,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAgCjH"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { run } from "./exec.js";
|
|
2
|
+
/**
|
|
3
|
+
* Resolves "owner/repo" from the git remote, so the wizard can offer to
|
|
4
|
+
* create Vercel projects automatically instead of walking the user through
|
|
5
|
+
* `vercel`/dashboard steps by hand. Only understands GitHub remotes (https
|
|
6
|
+
* or ssh) — Vercel's own gitRepository.type also supports gitlab/bitbucket,
|
|
7
|
+
* but this product's docs and CLI only ever assume GitHub.
|
|
8
|
+
*/
|
|
9
|
+
export async function detectGitHubRepo(repoRoot) {
|
|
10
|
+
try {
|
|
11
|
+
const url = (await run("git", ["remote", "get-url", "origin"], { cwd: repoRoot })).trim();
|
|
12
|
+
const match = url.match(/github\.com[:/]([^/]+)\/(.+?)(\.git)?$/);
|
|
13
|
+
return match ? `${match[1]}/${match[2]}` : null;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Creates a Vercel project via the REST API — connected to the GitHub repo,
|
|
21
|
+
* scoped to one app's folder in this monorepo, with its environment
|
|
22
|
+
* variables already set. This is what makes automatic setup possible: the
|
|
23
|
+
* same three things (gitRepository, rootDirectory, environmentVariables)
|
|
24
|
+
* that are easy to get wrong doing this by hand (see the "vercel CLI run
|
|
25
|
+
* from inside the app folder" failure mode) are just fields in one request
|
|
26
|
+
* body here.
|
|
27
|
+
*
|
|
28
|
+
* https://vercel.com/docs/rest-api/reference/endpoints/projects/create-a-new-project
|
|
29
|
+
*/
|
|
30
|
+
export async function createVercelProject(options) {
|
|
31
|
+
const environmentVariables = Object.entries(options.env).map(([key, value]) => ({
|
|
32
|
+
key,
|
|
33
|
+
value,
|
|
34
|
+
type: "encrypted",
|
|
35
|
+
target: ["production", "preview", "development"],
|
|
36
|
+
}));
|
|
37
|
+
let response;
|
|
38
|
+
try {
|
|
39
|
+
response = await fetch("https://api.vercel.com/v11/projects", {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers: {
|
|
42
|
+
Authorization: `Bearer ${options.token}`,
|
|
43
|
+
"Content-Type": "application/json",
|
|
44
|
+
},
|
|
45
|
+
body: JSON.stringify({
|
|
46
|
+
name: options.name,
|
|
47
|
+
framework: "nextjs",
|
|
48
|
+
gitRepository: { type: "github", repo: options.repo },
|
|
49
|
+
rootDirectory: options.rootDirectory,
|
|
50
|
+
environmentVariables,
|
|
51
|
+
}),
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
return { ok: false, error: error.message };
|
|
56
|
+
}
|
|
57
|
+
if (response.ok)
|
|
58
|
+
return { ok: true };
|
|
59
|
+
const body = (await response.json().catch(() => null));
|
|
60
|
+
return { ok: false, error: body?.error?.message ?? `HTTP ${response.status}` };
|
|
61
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-kontrolia-auth",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
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": [
|