komplian 0.5.2 → 0.6.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 +2 -0
- package/komplian-db-all-dev.mjs +256 -0
- package/komplian-db.mjs +162 -100
- package/komplian-localhost.mjs +48 -36
- package/komplian-onboard.mjs +14 -7
- package/komplian-postman.mjs +17 -0
- package/package.json +3 -3
- package/KOMPLIAN_DATABASE_URLS.env.example +0 -28
package/README.md
CHANGED
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
2. Browser login: `gh auth login -h github.com -s repo -s read:org -w`
|
|
7
7
|
3. `npx komplian onboard --yes` (sin `@versión`: usa `latest` del registry público).
|
|
8
8
|
|
|
9
|
+
**Orden típico del equipo:** onboard → `postman login` / `postman --yes` → `mcp-tools --yes` → **`db:all:dev`** → `localhost --yes`. Ver **`ONBOARDING.md`**.
|
|
10
|
+
|
|
9
11
|
**`npm ERR! ETARGET` / “No matching version”** (también si falló **`postman`**, no solo `onboard`): es el mismo paquete npm. Usa **`npx komplian postman login`** / **`npx komplian postman --yes`** **sin** `@0.4.x`; comprueba `npm config get registry` → `https://registry.npmjs.org/`; `npm cache clean --force`. Detalle: `ONBOARDING.md` en el monorepo.
|
|
10
12
|
|
|
11
13
|
### Postman (colección + entornos)
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* db:all:dev — Verificación empleado @komplian.com (Postman GET /me, misma clave que postman login),
|
|
4
|
+
* guarda las 3 URLs de desarrollo en ~/.komplian/dev-databases.json (600) y las propaga a
|
|
5
|
+
* app|api|web|admin/.env.local. No usa archivos secretos en la raíz del monorepo.
|
|
6
|
+
*
|
|
7
|
+
* No interactivo: export KOMPLIAN_DEV_APP_DATABASE_URL, _ADMIN_, _WEB_ y npx komplian db:all:dev --yes
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
chmodSync,
|
|
12
|
+
existsSync,
|
|
13
|
+
mkdirSync,
|
|
14
|
+
readFileSync,
|
|
15
|
+
writeFileSync,
|
|
16
|
+
} from "node:fs";
|
|
17
|
+
import { join, resolve } from "node:path";
|
|
18
|
+
import { homedir } from "node:os";
|
|
19
|
+
import { createInterface } from "node:readline/promises";
|
|
20
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
21
|
+
|
|
22
|
+
import { assertKomplianEmployeePostman } from "./komplian-postman.mjs";
|
|
23
|
+
import {
|
|
24
|
+
applyOverridesToEnvContent,
|
|
25
|
+
formatEnvValue,
|
|
26
|
+
findWorkspaceRoot,
|
|
27
|
+
writeAtomic,
|
|
28
|
+
} from "./komplian-localhost.mjs";
|
|
29
|
+
import {
|
|
30
|
+
loadHomeDevDatabases,
|
|
31
|
+
maskDatabaseUrl,
|
|
32
|
+
} from "./komplian-db.mjs";
|
|
33
|
+
|
|
34
|
+
const c = {
|
|
35
|
+
reset: "\x1b[0m",
|
|
36
|
+
dim: "\x1b[2m",
|
|
37
|
+
bold: "\x1b[1m",
|
|
38
|
+
cyan: "\x1b[36m",
|
|
39
|
+
green: "\x1b[32m",
|
|
40
|
+
red: "\x1b[31m",
|
|
41
|
+
yellow: "\x1b[33m",
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function log(s = "") {
|
|
45
|
+
console.log(s);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const PLACEHOLDER = "komplian_localhost_placeholder";
|
|
49
|
+
const DEV_DB_NAME = "dev-databases.json";
|
|
50
|
+
|
|
51
|
+
function ensureKomplianHome() {
|
|
52
|
+
const dir = join(homedir(), ".komplian");
|
|
53
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
54
|
+
try {
|
|
55
|
+
chmodSync(dir, 0o700);
|
|
56
|
+
} catch {
|
|
57
|
+
/* ignore */
|
|
58
|
+
}
|
|
59
|
+
return dir;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function devDbPath() {
|
|
63
|
+
return join(ensureKomplianHome(), DEV_DB_NAME);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function writeSavedDevDatabases(data) {
|
|
67
|
+
const p = devDbPath();
|
|
68
|
+
const body = JSON.stringify(
|
|
69
|
+
{
|
|
70
|
+
schemaVersion: 1,
|
|
71
|
+
app: data.app,
|
|
72
|
+
admin: data.admin,
|
|
73
|
+
web: data.web,
|
|
74
|
+
savedAt: new Date().toISOString(),
|
|
75
|
+
},
|
|
76
|
+
null,
|
|
77
|
+
2
|
|
78
|
+
);
|
|
79
|
+
writeFileSync(p, body, { encoding: "utf8", mode: 0o600 });
|
|
80
|
+
try {
|
|
81
|
+
chmodSync(p, 0o600);
|
|
82
|
+
} catch {
|
|
83
|
+
/* ignore */
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isValidPostgresUrl(s) {
|
|
88
|
+
const t = (s || "").trim();
|
|
89
|
+
if (!t.startsWith("postgresql://") && !t.startsWith("postgres://")) return false;
|
|
90
|
+
if (t.includes(PLACEHOLDER)) return false;
|
|
91
|
+
try {
|
|
92
|
+
const u = new URL(t);
|
|
93
|
+
return !!u.hostname;
|
|
94
|
+
} catch {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function isValidTriplet(d) {
|
|
100
|
+
return (
|
|
101
|
+
isValidPostgresUrl(d.app) &&
|
|
102
|
+
isValidPostgresUrl(d.admin) &&
|
|
103
|
+
isValidPostgresUrl(d.web)
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function headerDbAllDev() {
|
|
108
|
+
return `# KOMPLIAN — development databases (komplian db:all:dev · no commitear)\n`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function patchEnvLocal(workspaceRoot, project, overrides) {
|
|
112
|
+
const envPath = join(workspaceRoot, project, ".env.local");
|
|
113
|
+
const examplePath = join(workspaceRoot, project, ".env.example");
|
|
114
|
+
let body;
|
|
115
|
+
let prefix = "";
|
|
116
|
+
if (existsSync(envPath)) {
|
|
117
|
+
body = applyOverridesToEnvContent(readFileSync(envPath, "utf8"), overrides);
|
|
118
|
+
} else if (existsSync(examplePath)) {
|
|
119
|
+
prefix = headerDbAllDev();
|
|
120
|
+
body = applyOverridesToEnvContent(readFileSync(examplePath, "utf8"), overrides);
|
|
121
|
+
} else {
|
|
122
|
+
prefix = headerDbAllDev();
|
|
123
|
+
body = Object.entries(overrides)
|
|
124
|
+
.map(([k, v]) => `${k}=${formatEnvValue(v)}`)
|
|
125
|
+
.join("\n");
|
|
126
|
+
}
|
|
127
|
+
writeAtomic(envPath, prefix + body);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function propagateToMonorepo(workspaceRoot, data) {
|
|
131
|
+
patchEnvLocal(workspaceRoot, "app", { DATABASE_URL: data.app });
|
|
132
|
+
patchEnvLocal(workspaceRoot, "admin", { DATABASE_URL: data.admin });
|
|
133
|
+
patchEnvLocal(workspaceRoot, "web", { DATABASE_URL: data.web });
|
|
134
|
+
patchEnvLocal(workspaceRoot, "api", {
|
|
135
|
+
APP_DATABASE_URL: data.app,
|
|
136
|
+
ADMIN_DATABASE_URL: data.admin,
|
|
137
|
+
WEB_DATABASE_URL: data.web,
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function collectUrlsFromEnv() {
|
|
142
|
+
return {
|
|
143
|
+
app: process.env.KOMPLIAN_DEV_APP_DATABASE_URL?.trim() || "",
|
|
144
|
+
admin: process.env.KOMPLIAN_DEV_ADMIN_DATABASE_URL?.trim() || "",
|
|
145
|
+
web: process.env.KOMPLIAN_DEV_WEB_DATABASE_URL?.trim() || "",
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function collectUrlsInteractive(rl) {
|
|
150
|
+
log(` ${c.dim}postgresql://… desde Neon (rama development) u homólogo.${c.reset}`);
|
|
151
|
+
const app = (await rl.question(`${c.bold}URL APP${c.reset} (app + API): `)).trim();
|
|
152
|
+
const admin = (await rl.question(`${c.bold}URL ADMIN${c.reset}: `)).trim();
|
|
153
|
+
const web = (await rl.question(`${c.bold}URL WEB${c.reset} (pilot): `)).trim();
|
|
154
|
+
return { app, admin, web };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function parseArgs(argv) {
|
|
158
|
+
const opts = {
|
|
159
|
+
yes: false,
|
|
160
|
+
force: false,
|
|
161
|
+
workspace: "",
|
|
162
|
+
help: false,
|
|
163
|
+
};
|
|
164
|
+
const rest = [];
|
|
165
|
+
for (let i = 0; i < argv.length; i++) {
|
|
166
|
+
const a = argv[i];
|
|
167
|
+
if (a === "--yes" || a === "-y") opts.yes = true;
|
|
168
|
+
else if (a === "--force") opts.force = true;
|
|
169
|
+
else if (a === "-w" || a === "--workspace") opts.workspace = argv[++i] || "";
|
|
170
|
+
else if (a === "-h" || a === "--help") opts.help = true;
|
|
171
|
+
else if (a.startsWith("-")) {
|
|
172
|
+
log(`${c.red}✗${c.reset} Opción desconocida: ${a}`);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
} else rest.push(a);
|
|
175
|
+
}
|
|
176
|
+
if (rest[0]) opts.workspace = rest[0];
|
|
177
|
+
return opts;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function usage() {
|
|
181
|
+
log(`Uso: npx komplian db:all:dev [opciones] [monorepo]`);
|
|
182
|
+
log(``);
|
|
183
|
+
log(` Tras ${c.bold}npx komplian postman login${c.reset}: verifica @komplian.com y configura las 3 bases de desarrollo.`);
|
|
184
|
+
log(` Guarda en ${c.dim}~/.komplian/dev-databases.json${c.reset} y actualiza ${c.dim}app|api|web|admin/.env.local${c.reset}.`);
|
|
185
|
+
log(``);
|
|
186
|
+
log(` Sin prompts: ${c.dim}KOMPLIAN_DEV_APP_DATABASE_URL${c.reset}, ${c.dim}_ADMIN_${c.reset}, ${c.dim}_WEB_${c.reset} + ${c.bold}--yes${c.reset}`);
|
|
187
|
+
log(` ${c.bold}--force${c.reset} Volver a pedir URLs (o usar env) aunque exista el JSON`);
|
|
188
|
+
log(` -w, --workspace Raíz del monorepo`);
|
|
189
|
+
log(` -h, --help`);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
export async function runDbAllDev(argv) {
|
|
193
|
+
const opts = parseArgs(argv);
|
|
194
|
+
if (opts.help) {
|
|
195
|
+
usage();
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const workspaceRoot = opts.workspace.trim()
|
|
200
|
+
? resolve(opts.workspace.replace(/^~(?=$|[/\\])/, process.env.HOME || ""))
|
|
201
|
+
: findWorkspaceRoot(process.cwd());
|
|
202
|
+
|
|
203
|
+
if (!existsSync(join(workspaceRoot, "api", "package.json"))) {
|
|
204
|
+
log(`${c.red}✗${c.reset} No parece un monorepo Komplian: ${workspaceRoot}`);
|
|
205
|
+
process.exit(1);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
log(`${c.cyan}━━ Komplian — bases de datos (development) ━━${c.reset}`);
|
|
209
|
+
await assertKomplianEmployeePostman();
|
|
210
|
+
|
|
211
|
+
const fromEnv = collectUrlsFromEnv();
|
|
212
|
+
const envOk = isValidTriplet(fromEnv);
|
|
213
|
+
const saved = loadHomeDevDatabases();
|
|
214
|
+
const savedOk = saved && isValidTriplet(saved);
|
|
215
|
+
|
|
216
|
+
/** @type {{ app: string; admin: string; web: string }} */
|
|
217
|
+
let data;
|
|
218
|
+
|
|
219
|
+
if (envOk) {
|
|
220
|
+
data = fromEnv;
|
|
221
|
+
log(`${c.green}✓${c.reset} URLs desde variables de entorno (KOMPLIAN_DEV_*_DATABASE_URL).`);
|
|
222
|
+
} else if (savedOk && !opts.force && !envOk) {
|
|
223
|
+
data = saved;
|
|
224
|
+
log(
|
|
225
|
+
`${c.dim}○${c.reset} Usando ${c.bold}~/.komplian/dev-databases.json${c.reset} (${c.dim}--force${c.reset} para cambiar).`
|
|
226
|
+
);
|
|
227
|
+
} else if (opts.yes && !envOk) {
|
|
228
|
+
log(
|
|
229
|
+
`${c.red}✗${c.reset} Con ${c.bold}--yes${c.reset} define las tres variables ${c.dim}KOMPLIAN_DEV_APP_DATABASE_URL${c.reset}, ${c.dim}_ADMIN_${c.reset}, ${c.dim}_WEB_${c.reset}, o ejecuta sin ${c.bold}--yes${c.reset} para modo interactivo.`
|
|
230
|
+
);
|
|
231
|
+
process.exit(1);
|
|
232
|
+
} else {
|
|
233
|
+
const rl = createInterface({ input, output });
|
|
234
|
+
log(``);
|
|
235
|
+
log(`${c.bold}Introduce las connection strings de desarrollo (3).${c.reset}`);
|
|
236
|
+
data = await collectUrlsInteractive(rl);
|
|
237
|
+
rl.close();
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (!isValidTriplet(data)) {
|
|
241
|
+
log(`${c.red}✗${c.reset} Las tres URLs deben ser postgres:// o postgresql:// válidas (sin placeholder).`);
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
writeSavedDevDatabases(data);
|
|
246
|
+
propagateToMonorepo(workspaceRoot, data);
|
|
247
|
+
|
|
248
|
+
log(``);
|
|
249
|
+
log(`${c.green}✓${c.reset} Guardado ${c.dim}~/.komplian/${DEV_DB_NAME}${c.reset} y .env.local en app, api, web, admin.`);
|
|
250
|
+
log(` ${c.dim}APP${c.reset} ${maskDatabaseUrl(data.app)}`);
|
|
251
|
+
log(` ${c.dim}ADMIN${c.reset} ${maskDatabaseUrl(data.admin)}`);
|
|
252
|
+
log(` ${c.dim}WEB${c.reset} ${maskDatabaseUrl(data.web)}`);
|
|
253
|
+
log(``);
|
|
254
|
+
log(`${c.dim}Siguiente: ${c.reset}${c.cyan}npx komplian localhost --yes${c.reset}`);
|
|
255
|
+
log(`${c.dim}psql: ${c.reset}${c.cyan}npx komplian db:app:development${c.reset} …`);
|
|
256
|
+
}
|
package/komplian-db.mjs
CHANGED
|
@@ -7,11 +7,8 @@
|
|
|
7
7
|
* npx komplian db:admin:staging
|
|
8
8
|
* npx komplian db:web:production
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* KOMPLIAN_DATABASE_URL_ADMIN_*
|
|
13
|
-
* KOMPLIAN_DATABASE_URL_WEB_*
|
|
14
|
-
* Legacy development: KOMPLIAN_LOCALHOST_*_DATABASE_URL
|
|
10
|
+
* Development: 1) .env.local por repo; 2) ~/.komplian/dev-databases.json (db:all:dev); 3) variables de sesión KOMPLIAN_*.
|
|
11
|
+
* Staging/producción: solo KOMPLIAN_DATABASE_URL_*_STAGING|_PRODUCTION (env o archivos en raíz), nunca .env.local.
|
|
15
12
|
*
|
|
16
13
|
* Producción: solo emails @komplian.com en KOMPLIAN_DATABASE_PRODUCTION_ALLOWLIST
|
|
17
14
|
* (por defecto josue.santana@komplian.com). Operador: KOMPLIAN_DATABASE_OPERATOR_EMAIL o git config user.email.
|
|
@@ -22,6 +19,7 @@
|
|
|
22
19
|
|
|
23
20
|
import { spawn, spawnSync } from "node:child_process";
|
|
24
21
|
import { existsSync, readFileSync } from "node:fs";
|
|
22
|
+
import { homedir } from "node:os";
|
|
25
23
|
import { dirname, join, resolve } from "node:path";
|
|
26
24
|
import { createInterface } from "node:readline/promises";
|
|
27
25
|
import { stdin as input, stdout as output } from "node:process";
|
|
@@ -56,23 +54,26 @@ const DEFAULT_PRODUCTION_ALLOWLIST = ["josue.santana@komplian.com"];
|
|
|
56
54
|
/** Misma cadena que `komplian-localhost.mjs` cuando no hay URL real. */
|
|
57
55
|
const LOCALHOST_DB_PLACEHOLDER = "komplian_localhost_placeholder";
|
|
58
56
|
|
|
59
|
-
function
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
export function isPlaceholderDbUrl(url) {
|
|
58
|
+
return !!(url && String(url).includes(LOCALHOST_DB_PLACEHOLDER));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Primera URL no vacía que no sea el placeholder de localhost. */
|
|
62
|
+
export function pickFirstNonPlaceholder(...candidates) {
|
|
63
|
+
for (const c of candidates) {
|
|
64
|
+
const u = (c ?? "").trim();
|
|
65
|
+
if (u && !isPlaceholderDbUrl(u)) return u;
|
|
66
|
+
}
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function assertNotPlaceholderDbUrl(url) {
|
|
71
|
+
if (!isPlaceholderDbUrl(url)) return;
|
|
62
72
|
log(
|
|
63
73
|
`${c.red}✗${c.reset} La URL es el ${c.bold}placeholder${c.reset} de \`komplian localhost\` (no apunta a una base de datos real).`
|
|
64
74
|
);
|
|
65
75
|
log(``);
|
|
66
|
-
log(` Pon una URL real
|
|
67
|
-
log(
|
|
68
|
-
` ${c.dim}KOMPLIAN_LOCALHOST_${key}_DATABASE_URL=postgresql://…${c.reset}`
|
|
69
|
-
);
|
|
70
|
-
log(
|
|
71
|
-
` o ${c.dim}KOMPLIAN_DATABASE_URL_${key}_${environment.toUpperCase()}=…${c.reset} en ${c.dim}KOMPLIAN_DATABASE_URLS.env${c.reset}`
|
|
72
|
-
);
|
|
73
|
-
log(
|
|
74
|
-
` (${c.dim}KOMPLIAN_LOCALHOST_SECRETS.env${c.reset} / ${c.dim}.komplian/${c.reset}…). Luego vuelve a ejecutar el comando.`
|
|
75
|
-
);
|
|
76
|
+
log(` Pon una URL real (${c.bold}npx komplian db:all:dev${c.reset}, ${c.dim}--url${c.reset}, o variables en sesión).`);
|
|
76
77
|
process.exit(1);
|
|
77
78
|
}
|
|
78
79
|
|
|
@@ -96,25 +97,21 @@ function parseEnvFile(content) {
|
|
|
96
97
|
return out;
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
} catch {
|
|
114
|
-
/* ignore */
|
|
115
|
-
}
|
|
100
|
+
/** ~/.komplian/dev-databases.json (creado por db:all:dev). No se leen secretos en la raíz del monorepo. */
|
|
101
|
+
export function loadHomeDevDatabases() {
|
|
102
|
+
const p = join(homedir(), ".komplian", "dev-databases.json");
|
|
103
|
+
if (!existsSync(p)) return null;
|
|
104
|
+
try {
|
|
105
|
+
const j = JSON.parse(readFileSync(p, "utf8"));
|
|
106
|
+
if (!j || typeof j !== "object") return null;
|
|
107
|
+
return {
|
|
108
|
+
app: String(j.app || "").trim(),
|
|
109
|
+
admin: String(j.admin || "").trim(),
|
|
110
|
+
web: String(j.web || "").trim(),
|
|
111
|
+
};
|
|
112
|
+
} catch {
|
|
113
|
+
return null;
|
|
116
114
|
}
|
|
117
|
-
return out;
|
|
118
115
|
}
|
|
119
116
|
|
|
120
117
|
function readEnvLocalKey(workspaceRoot, project, keys) {
|
|
@@ -149,7 +146,7 @@ function findWorkspaceRoot(start) {
|
|
|
149
146
|
return resolve(start);
|
|
150
147
|
}
|
|
151
148
|
|
|
152
|
-
function maskDatabaseUrl(url) {
|
|
149
|
+
export function maskDatabaseUrl(url) {
|
|
153
150
|
try {
|
|
154
151
|
const u = new URL(url);
|
|
155
152
|
if (u.password) u.password = "***";
|
|
@@ -168,7 +165,7 @@ function platformToDbKey(platform) {
|
|
|
168
165
|
return null;
|
|
169
166
|
}
|
|
170
167
|
|
|
171
|
-
function loadProductionAllowlist(
|
|
168
|
+
function loadProductionAllowlist() {
|
|
172
169
|
const raw = process.env.KOMPLIAN_DATABASE_PRODUCTION_ALLOWLIST?.trim();
|
|
173
170
|
if (raw) {
|
|
174
171
|
return raw
|
|
@@ -176,21 +173,6 @@ function loadProductionAllowlist(workspaceRoot) {
|
|
|
176
173
|
.map((s) => s.trim().toLowerCase())
|
|
177
174
|
.filter(Boolean);
|
|
178
175
|
}
|
|
179
|
-
const p = join(
|
|
180
|
-
workspaceRoot,
|
|
181
|
-
".komplian",
|
|
182
|
-
"KOMPLIAN_DATABASE_PRODUCTION_ALLOWLIST"
|
|
183
|
-
);
|
|
184
|
-
if (existsSync(p)) {
|
|
185
|
-
try {
|
|
186
|
-
return readFileSync(p, "utf8")
|
|
187
|
-
.split(/\r?\n/)
|
|
188
|
-
.map((l) => l.trim().toLowerCase())
|
|
189
|
-
.filter((l) => l && !l.startsWith("#"));
|
|
190
|
-
} catch {
|
|
191
|
-
/* ignore */
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
176
|
return DEFAULT_PRODUCTION_ALLOWLIST.map((e) => e.toLowerCase());
|
|
195
177
|
}
|
|
196
178
|
|
|
@@ -245,7 +227,7 @@ async function resolveOperatorEmail() {
|
|
|
245
227
|
return (ans || "").trim();
|
|
246
228
|
}
|
|
247
229
|
|
|
248
|
-
function
|
|
230
|
+
function assertProductionAccessLocalAllowlist(email, allowlist) {
|
|
249
231
|
const e = email.toLowerCase();
|
|
250
232
|
if (!e || !e.endsWith("@komplian.com")) {
|
|
251
233
|
log(
|
|
@@ -255,51 +237,123 @@ function assertProductionAccess(email, allowlist) {
|
|
|
255
237
|
}
|
|
256
238
|
if (!allowlist.includes(e)) {
|
|
257
239
|
log(
|
|
258
|
-
`${c.red}✗${c.reset} Producción:
|
|
240
|
+
`${c.red}✗${c.reset} Producción: email no autorizado (local). Usa Admin → ${c.bold}Prod DB CLI${c.reset} o ${c.dim}KOMPLIAN_DATABASE_PRODUCTION_ALLOWLIST${c.reset} / ${c.dim}KOMPLIAN_API_URL${c.reset}+${c.dim}ADMIN_API_KEY${c.reset}.`
|
|
259
241
|
);
|
|
260
242
|
process.exit(1);
|
|
261
243
|
}
|
|
262
244
|
}
|
|
263
245
|
|
|
246
|
+
/** Si hay KOMPLIAN_API_URL + ADMIN_API_KEY, la API central decide (lista en Admin, super admin). */
|
|
247
|
+
async function verifyProductionAccessViaApi(email) {
|
|
248
|
+
const base = (process.env.KOMPLIAN_API_URL || "").trim().replace(/\/$/, "");
|
|
249
|
+
const apiKey = (process.env.ADMIN_API_KEY || "").trim();
|
|
250
|
+
if (!base || !apiKey) return null;
|
|
251
|
+
try {
|
|
252
|
+
const ac = new AbortController();
|
|
253
|
+
const t = setTimeout(() => ac.abort(), 15000);
|
|
254
|
+
const r = await fetch(`${base}/api/admin/production-db-cli/verify`, {
|
|
255
|
+
method: "POST",
|
|
256
|
+
headers: {
|
|
257
|
+
"Content-Type": "application/json",
|
|
258
|
+
"X-API-Key": apiKey,
|
|
259
|
+
},
|
|
260
|
+
body: JSON.stringify({ email: email.toLowerCase() }),
|
|
261
|
+
signal: ac.signal,
|
|
262
|
+
});
|
|
263
|
+
clearTimeout(t);
|
|
264
|
+
const j = await r.json().catch(() => ({}));
|
|
265
|
+
if (!r.ok || j.success !== true || typeof j.allowed !== "boolean") return null;
|
|
266
|
+
return j.allowed;
|
|
267
|
+
} catch {
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
async function assertProductionAccessResolved(email, quiet) {
|
|
273
|
+
const e = (email || "").toLowerCase().trim();
|
|
274
|
+
if (!e || !e.endsWith("@komplian.com")) {
|
|
275
|
+
log(
|
|
276
|
+
`${c.red}✗${c.reset} Producción: solo cuentas ${c.bold}@komplian.com${c.reset}. Define ${c.dim}KOMPLIAN_DATABASE_OPERATOR_EMAIL${c.reset} o ${c.dim}git config user.email${c.reset}.`
|
|
277
|
+
);
|
|
278
|
+
process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
const verdict = await verifyProductionAccessViaApi(e);
|
|
281
|
+
if (verdict === true) {
|
|
282
|
+
if (!quiet) {
|
|
283
|
+
log(
|
|
284
|
+
`${c.dim}○${c.reset} Producción: allowlist desde ${c.bold}API central${c.reset} (Admin → Prod DB CLI).`
|
|
285
|
+
);
|
|
286
|
+
}
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
if (verdict === false) {
|
|
290
|
+
log(
|
|
291
|
+
`${c.red}✗${c.reset} Producción: este email no está en la lista (Admin → Prod DB CLI, solo super admin).`
|
|
292
|
+
);
|
|
293
|
+
process.exit(1);
|
|
294
|
+
}
|
|
295
|
+
assertProductionAccessLocalAllowlist(e, loadProductionAllowlist());
|
|
296
|
+
}
|
|
297
|
+
|
|
264
298
|
function resolveDatabaseUrl(workspaceRoot, platform, environment) {
|
|
265
299
|
const dbKey = platformToDbKey(platform);
|
|
266
300
|
if (!dbKey) return "";
|
|
267
|
-
const file = loadMergedEnvFiles(workspaceRoot);
|
|
268
301
|
const envU = environment.toUpperCase();
|
|
269
302
|
const primaryKey = `KOMPLIAN_DATABASE_URL_${dbKey}_${envU}`;
|
|
303
|
+
const home = loadHomeDevDatabases();
|
|
270
304
|
|
|
271
|
-
|
|
272
|
-
process.env[primaryKey]
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
url =
|
|
294
|
-
process.env.KOMPLIAN_LOCALHOST_WEB_DATABASE_URL?.trim() ||
|
|
295
|
-
file.KOMPLIAN_LOCALHOST_WEB_DATABASE_URL?.trim() ||
|
|
296
|
-
single ||
|
|
297
|
-
readEnvLocalKey(workspaceRoot, "web", ["DATABASE_URL"]) ||
|
|
298
|
-
readEnvLocalKey(workspaceRoot, "api", ["WEB_DATABASE_URL"]);
|
|
299
|
-
}
|
|
305
|
+
if (environment !== "development") {
|
|
306
|
+
return pickFirstNonPlaceholder(process.env[primaryKey]);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const single = (process.env.KOMPLIAN_LOCALHOST_DATABASE_URL || "").trim();
|
|
310
|
+
|
|
311
|
+
let fromRepos = "";
|
|
312
|
+
if (dbKey === "APP") {
|
|
313
|
+
fromRepos = pickFirstNonPlaceholder(
|
|
314
|
+
readEnvLocalKey(workspaceRoot, "app", ["DATABASE_URL"]),
|
|
315
|
+
readEnvLocalKey(workspaceRoot, "api", ["APP_DATABASE_URL"])
|
|
316
|
+
);
|
|
317
|
+
} else if (dbKey === "ADMIN") {
|
|
318
|
+
fromRepos = pickFirstNonPlaceholder(
|
|
319
|
+
readEnvLocalKey(workspaceRoot, "admin", ["DATABASE_URL"]),
|
|
320
|
+
readEnvLocalKey(workspaceRoot, "api", ["ADMIN_DATABASE_URL"])
|
|
321
|
+
);
|
|
322
|
+
} else if (dbKey === "WEB") {
|
|
323
|
+
fromRepos = pickFirstNonPlaceholder(
|
|
324
|
+
readEnvLocalKey(workspaceRoot, "web", ["DATABASE_URL"]),
|
|
325
|
+
readEnvLocalKey(workspaceRoot, "api", ["WEB_DATABASE_URL"])
|
|
326
|
+
);
|
|
300
327
|
}
|
|
301
328
|
|
|
302
|
-
|
|
329
|
+
if (dbKey === "APP") {
|
|
330
|
+
return pickFirstNonPlaceholder(
|
|
331
|
+
fromRepos,
|
|
332
|
+
process.env[primaryKey],
|
|
333
|
+
home?.app,
|
|
334
|
+
process.env.KOMPLIAN_LOCALHOST_APP_DATABASE_URL,
|
|
335
|
+
single
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
if (dbKey === "ADMIN") {
|
|
339
|
+
return pickFirstNonPlaceholder(
|
|
340
|
+
fromRepos,
|
|
341
|
+
process.env[primaryKey],
|
|
342
|
+
home?.admin,
|
|
343
|
+
process.env.KOMPLIAN_LOCALHOST_ADMIN_DATABASE_URL,
|
|
344
|
+
single
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
if (dbKey === "WEB") {
|
|
348
|
+
return pickFirstNonPlaceholder(
|
|
349
|
+
fromRepos,
|
|
350
|
+
process.env[primaryKey],
|
|
351
|
+
home?.web,
|
|
352
|
+
process.env.KOMPLIAN_LOCALHOST_WEB_DATABASE_URL,
|
|
353
|
+
single
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
return "";
|
|
303
357
|
}
|
|
304
358
|
|
|
305
359
|
function findPsqlBinary() {
|
|
@@ -376,15 +430,15 @@ function usageDb() {
|
|
|
376
430
|
log(` Plataformas: ${c.dim}app | admin | web | api-app | api-admin | api-web${c.reset}`);
|
|
377
431
|
log(` Entornos: ${c.dim}development | staging | production${c.reset}`);
|
|
378
432
|
log(``);
|
|
379
|
-
log(`
|
|
380
|
-
log(` ${c.dim}
|
|
381
|
-
log(` ${c.dim}
|
|
382
|
-
log(` Archivo opcional: ${c.dim}KOMPLIAN_DATABASE_URLS.env${c.reset} (gitignored)`);
|
|
433
|
+
log(` ${c.bold}db:all:dev${c.reset} Configura las 3 DB de desarrollo (Postman @komplian.com) → ~/.komplian + cada repo /.env.local`);
|
|
434
|
+
log(` Development: ${c.dim}.env.local por repo${c.reset}, ${c.dim}~/.komplian/dev-databases.json${c.reset}, variables en sesión.`);
|
|
435
|
+
log(` Staging/producción: solo ${c.dim}KOMPLIAN_DATABASE_URL_*_STAGING|_PRODUCTION${c.reset} en el entorno (nunca .env.local).`);
|
|
383
436
|
log(` Neon: ${c.dim}staging/producción${c.reset} exigen host *.neon.tech; ${c.dim}development${c.reset} permite Postgres local (aviso).`);
|
|
384
437
|
log(``);
|
|
385
438
|
log(` Producción:`);
|
|
386
|
-
log(` ${c.dim}KOMPLIAN_DATABASE_OPERATOR_EMAIL${c.reset} o git user.email`);
|
|
387
|
-
log(` ${c.
|
|
439
|
+
log(` ${c.dim}KOMPLIAN_DATABASE_OPERATOR_EMAIL${c.reset} o git user.email (@komplian.com)`);
|
|
440
|
+
log(` Preferido: ${c.bold}Admin → Prod DB CLI${c.reset} (super admin) + ${c.dim}KOMPLIAN_API_URL${c.reset} + ${c.dim}ADMIN_API_KEY${c.reset} en la sesión del operador.`);
|
|
441
|
+
log(` Sin API: ${c.dim}KOMPLIAN_DATABASE_PRODUCTION_ALLOWLIST${c.reset} (coma) en el entorno local.`);
|
|
388
442
|
log(``);
|
|
389
443
|
log(` -c, --command SQL no interactivo`);
|
|
390
444
|
log(` -u, --url URL explícita (sigue reglas Neon / producción)`);
|
|
@@ -417,14 +471,12 @@ export async function runDb(argv) {
|
|
|
417
471
|
? resolve(opts.workspace.replace(/^~(?=$|[/\\])/, process.env.HOME || ""))
|
|
418
472
|
: findWorkspaceRoot(process.cwd());
|
|
419
473
|
|
|
420
|
-
const allowlist = loadProductionAllowlist(workspaceRoot);
|
|
421
|
-
|
|
422
474
|
if (opts.environment === "production") {
|
|
423
475
|
const email = await resolveOperatorEmail();
|
|
424
|
-
|
|
476
|
+
await assertProductionAccessResolved(email, opts.quiet);
|
|
425
477
|
if (!opts.quiet) {
|
|
426
478
|
log(
|
|
427
|
-
`${c.yellow}⚠${c.reset} Producción · operador ${c.bold}${email.toLowerCase()}${c.reset}
|
|
479
|
+
`${c.yellow}⚠${c.reset} Producción · operador ${c.bold}${email.toLowerCase()}${c.reset}`
|
|
428
480
|
);
|
|
429
481
|
}
|
|
430
482
|
}
|
|
@@ -443,13 +495,23 @@ export async function runDb(argv) {
|
|
|
443
495
|
|
|
444
496
|
if (!url) {
|
|
445
497
|
log(`${c.red}✗${c.reset} Sin URL para ${opts.platform} / ${opts.environment}.`);
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
498
|
+
if (opts.environment === "development") {
|
|
499
|
+
const pk = platformToDbKey(opts.platform);
|
|
500
|
+
log(
|
|
501
|
+
` Pon la URL de Neon (rama dev) en ${c.bold}${opts.platform === "web" || opts.platform === "api-web" ? "web" : opts.platform === "admin" || opts.platform === "api-admin" ? "admin" : "app"}/.env.local${c.reset} (${c.dim}DATABASE_URL${c.reset}) o en ${c.dim}api/.env.local${c.reset} (${c.dim}${pk === "WEB" ? "WEB_DATABASE_URL" : pk === "ADMIN" ? "ADMIN_DATABASE_URL" : "APP_DATABASE_URL"}${c.reset}).`
|
|
502
|
+
);
|
|
503
|
+
log(
|
|
504
|
+
` ${c.dim}(Placeholders ignorados; ${c.bold}npx komplian db:all:dev${c.reset} o ${c.bold}KOMPLIAN_DATABASE_URL_${pk}_DEVELOPMENT${c.reset} en el entorno.)${c.reset}`
|
|
505
|
+
);
|
|
506
|
+
} else {
|
|
507
|
+
log(
|
|
508
|
+
` Exporta ${c.bold}KOMPLIAN_DATABASE_URL_${platformToDbKey(opts.platform)}_${opts.environment.toUpperCase()}${c.reset} en el entorno (CI / operador).`
|
|
509
|
+
);
|
|
510
|
+
}
|
|
449
511
|
process.exit(1);
|
|
450
512
|
}
|
|
451
513
|
|
|
452
|
-
assertNotPlaceholderDbUrl(url
|
|
514
|
+
assertNotPlaceholderDbUrl(url);
|
|
453
515
|
|
|
454
516
|
assertNeonHost(url, opts.environment);
|
|
455
517
|
|
package/komplian-localhost.mjs
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* Seguridad: los `.env*` no van en el paquete npm `komplian` (solo *.mjs + JSON + README).
|
|
7
7
|
* No imprime secretos. Tras generar, muestra tabla de URLs locales (puertos fijos).
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
* (
|
|
9
|
+
* Bases de datos: primero cada repo `/.env.local`; luego ~/.komplian/dev-databases.json (npx komplian db:all:dev);
|
|
10
|
+
* opcional en la sesión: KOMPLIAN_LOCALHOST_*_DATABASE_URL (sin archivos secretos en la raíz del monorepo).
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { randomBytes } from "node:crypto";
|
|
@@ -18,6 +18,11 @@ import { fileURLToPath } from "node:url";
|
|
|
18
18
|
import { createInterface } from "node:readline/promises";
|
|
19
19
|
import { stdin as input, stdout as output } from "node:process";
|
|
20
20
|
|
|
21
|
+
import {
|
|
22
|
+
loadHomeDevDatabases,
|
|
23
|
+
pickFirstNonPlaceholder,
|
|
24
|
+
} from "./komplian-db.mjs";
|
|
25
|
+
|
|
21
26
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
22
27
|
|
|
23
28
|
const c = {
|
|
@@ -61,41 +66,41 @@ function parseEnvFile(content) {
|
|
|
61
66
|
return out;
|
|
62
67
|
}
|
|
63
68
|
|
|
64
|
-
function
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (!existsSync(p)) continue;
|
|
73
|
-
try {
|
|
74
|
-
return parseEnvFile(readFileSync(p, "utf8"));
|
|
75
|
-
} catch {
|
|
76
|
-
/* ignore */
|
|
77
|
-
}
|
|
69
|
+
function readEnvLocalDbUrl(workspaceRoot, project, key) {
|
|
70
|
+
const p = join(workspaceRoot, project, ".env.local");
|
|
71
|
+
if (!existsSync(p)) return "";
|
|
72
|
+
try {
|
|
73
|
+
const m = parseEnvFile(readFileSync(p, "utf8"));
|
|
74
|
+
return String(m[key] || "").trim();
|
|
75
|
+
} catch {
|
|
76
|
+
return "";
|
|
78
77
|
}
|
|
79
|
-
return {};
|
|
80
78
|
}
|
|
81
79
|
|
|
82
80
|
function resolveDatabaseUrls(workspaceRoot) {
|
|
83
|
-
const
|
|
84
|
-
const single =
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
single
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
process.env.
|
|
97
|
-
|
|
98
|
-
|
|
81
|
+
const home = loadHomeDevDatabases();
|
|
82
|
+
const single = (process.env.KOMPLIAN_LOCALHOST_DATABASE_URL || "").trim();
|
|
83
|
+
const app = pickFirstNonPlaceholder(
|
|
84
|
+
readEnvLocalDbUrl(workspaceRoot, "app", "DATABASE_URL"),
|
|
85
|
+
readEnvLocalDbUrl(workspaceRoot, "api", "APP_DATABASE_URL"),
|
|
86
|
+
home?.app,
|
|
87
|
+
process.env.KOMPLIAN_LOCALHOST_APP_DATABASE_URL,
|
|
88
|
+
single
|
|
89
|
+
);
|
|
90
|
+
const admin = pickFirstNonPlaceholder(
|
|
91
|
+
readEnvLocalDbUrl(workspaceRoot, "admin", "DATABASE_URL"),
|
|
92
|
+
readEnvLocalDbUrl(workspaceRoot, "api", "ADMIN_DATABASE_URL"),
|
|
93
|
+
home?.admin,
|
|
94
|
+
process.env.KOMPLIAN_LOCALHOST_ADMIN_DATABASE_URL,
|
|
95
|
+
single
|
|
96
|
+
);
|
|
97
|
+
const web = pickFirstNonPlaceholder(
|
|
98
|
+
readEnvLocalDbUrl(workspaceRoot, "web", "DATABASE_URL"),
|
|
99
|
+
readEnvLocalDbUrl(workspaceRoot, "api", "WEB_DATABASE_URL"),
|
|
100
|
+
home?.web,
|
|
101
|
+
process.env.KOMPLIAN_LOCALHOST_WEB_DATABASE_URL,
|
|
102
|
+
single
|
|
103
|
+
);
|
|
99
104
|
const hasReal =
|
|
100
105
|
!!(app && admin && web) &&
|
|
101
106
|
!String(app).includes("komplian_localhost_placeholder");
|
|
@@ -421,7 +426,7 @@ function usageLocalhost() {
|
|
|
421
426
|
log(`Uso: npx komplian localhost [opciones] [carpeta-monorepo]`);
|
|
422
427
|
log(``);
|
|
423
428
|
log(` Genera .env.local completos (desde cada .env.example + secretos KOMPLIAN) y arranca todos los dev servers.`);
|
|
424
|
-
log(` ${c.dim}
|
|
429
|
+
log(` ${c.dim}DBs: npx komplian db:all:dev antes (o ~/.komplian/dev-databases.json / variables en sesión).${c.reset}`);
|
|
425
430
|
log(``);
|
|
426
431
|
log(` -y, --yes Sin confirmación`);
|
|
427
432
|
log(` --force Regenerar .env.local`);
|
|
@@ -506,9 +511,8 @@ export async function runLocalhost(argv) {
|
|
|
506
511
|
|
|
507
512
|
if (!db.hasReal && !opts.minimal) {
|
|
508
513
|
log(
|
|
509
|
-
`${c.yellow}⚠${c.reset} Sin
|
|
514
|
+
`${c.yellow}⚠${c.reset} Sin URLs de desarrollo reales. Ejecuta ${c.bold}npx komplian db:all:dev${c.reset} y luego vuelve a lanzar localhost (o ${c.bold}--minimal${c.reset}).`
|
|
510
515
|
);
|
|
511
|
-
log(`${c.dim} O ${c.bold}--minimal${c.reset} (solo app + web + docs).${c.reset}`);
|
|
512
516
|
log("");
|
|
513
517
|
}
|
|
514
518
|
|
|
@@ -573,3 +577,11 @@ export async function runLocalhost(argv) {
|
|
|
573
577
|
process.exit(code ?? 0);
|
|
574
578
|
});
|
|
575
579
|
}
|
|
580
|
+
|
|
581
|
+
export {
|
|
582
|
+
applyOverridesToEnvContent,
|
|
583
|
+
formatEnvValue,
|
|
584
|
+
parseEnvFile,
|
|
585
|
+
writeAtomic,
|
|
586
|
+
findWorkspaceRoot,
|
|
587
|
+
};
|
package/komplian-onboard.mjs
CHANGED
|
@@ -394,13 +394,15 @@ function npmInstallEach(workspace) {
|
|
|
394
394
|
}
|
|
395
395
|
|
|
396
396
|
function usage() {
|
|
397
|
-
log(`Uso: komplian onboard | postman |
|
|
398
|
-
log(`
|
|
399
|
-
log(` npx komplian
|
|
400
|
-
log(` npx komplian postman --yes
|
|
401
|
-
log(` npx komplian
|
|
402
|
-
log(` npx komplian
|
|
403
|
-
log(` npx komplian
|
|
397
|
+
log(`Uso: komplian onboard | postman | mcp-tools | db:all:dev | localhost | db …`);
|
|
398
|
+
log(` ${c.bold}Setup estándar (orden):${c.reset}`);
|
|
399
|
+
log(` 1. npx komplian onboard --yes`);
|
|
400
|
+
log(` 2. npx komplian postman login → npx komplian postman --yes ${c.dim}(@komplian.com)${c.reset}`);
|
|
401
|
+
log(` 3. npx komplian mcp-tools --yes`);
|
|
402
|
+
log(` 4. npx komplian db:all:dev ${c.dim}(Postman + 3 URLs dev → ~/.komplian + .env.local)${c.reset}`);
|
|
403
|
+
log(` 5. npx komplian localhost --yes`);
|
|
404
|
+
log(``);
|
|
405
|
+
log(` npx komplian db:app:development ${c.dim}(psql una DB)${c.reset}`);
|
|
404
406
|
log(``);
|
|
405
407
|
log(` Antes (una vez): gh auth login -h github.com -s repo -s read:org -w`);
|
|
406
408
|
log(` Requisitos: Node 18+, git, GitHub CLI (gh)`);
|
|
@@ -499,6 +501,11 @@ async function main() {
|
|
|
499
501
|
await runMcpTools(rawArgv.slice(1));
|
|
500
502
|
return;
|
|
501
503
|
}
|
|
504
|
+
if (rawArgv[0] === "db:all:dev") {
|
|
505
|
+
const { runDbAllDev } = await import("./komplian-db-all-dev.mjs");
|
|
506
|
+
await runDbAllDev(rawArgv.slice(1));
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
502
509
|
if (rawArgv[0]?.startsWith("db:")) {
|
|
503
510
|
const { runDb } = await import("./komplian-db.mjs");
|
|
504
511
|
const seg = rawArgv[0].split(":").filter(Boolean);
|
package/komplian-postman.mjs
CHANGED
|
@@ -938,3 +938,20 @@ export async function runPostman(argv) {
|
|
|
938
938
|
);
|
|
939
939
|
}
|
|
940
940
|
}
|
|
941
|
+
|
|
942
|
+
/**
|
|
943
|
+
* Misma verificación que `postman --yes`: GET /me y dominio @komplian.com.
|
|
944
|
+
* Requiere `npx komplian postman login` (o POSTMAN_API_KEY en la sesión).
|
|
945
|
+
*/
|
|
946
|
+
export async function assertKomplianEmployeePostman() {
|
|
947
|
+
const domain = (process.env.KOMPLIAN_EMAIL_DOMAIN || "komplian.com").trim();
|
|
948
|
+
const { key, source } = resolveApiKey();
|
|
949
|
+
if (!key) {
|
|
950
|
+
printMissingKeyHelp();
|
|
951
|
+
process.exit(1);
|
|
952
|
+
}
|
|
953
|
+
if (source && source !== "POSTMAN_API_KEY") {
|
|
954
|
+
log(`${c.dim}→ Postman API key desde: ${formatHomePath(source)}${c.reset}`);
|
|
955
|
+
}
|
|
956
|
+
return verifyKomplianEmail(key, domain);
|
|
957
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "komplian",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Komplian CLI: onboard, Postman, localhost, mcp-tools, db (psql). Node 18+. Published tarball has no .env / secrets.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"komplian-localhost.mjs",
|
|
16
16
|
"komplian-mcp-tools.mjs",
|
|
17
17
|
"komplian-db.mjs",
|
|
18
|
-
"
|
|
18
|
+
"komplian-db-all-dev.mjs",
|
|
19
19
|
"komplian-team-repos.json",
|
|
20
20
|
"README.md"
|
|
21
21
|
],
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"access": "public"
|
|
24
24
|
},
|
|
25
25
|
"scripts": {
|
|
26
|
-
"prepublishOnly": "node --check komplian-onboard.mjs && node --check komplian-postman.mjs && node --check komplian-localhost.mjs && node --check komplian-mcp-tools.mjs && node --check komplian-db.mjs"
|
|
26
|
+
"prepublishOnly": "node --check komplian-onboard.mjs && node --check komplian-postman.mjs && node --check komplian-localhost.mjs && node --check komplian-mcp-tools.mjs && node --check komplian-db.mjs && node --check komplian-db-all-dev.mjs"
|
|
27
27
|
},
|
|
28
28
|
"keywords": [
|
|
29
29
|
"komplian",
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# Copia a KOMPLIAN_DATABASE_URLS.env (raíz del monorepo) y rellena.
|
|
2
|
-
# No commitees URLs con contraseña. Hosts: Neon (*.neon.tech).
|
|
3
|
-
|
|
4
|
-
# --- App DB (Prisma en app/, raw SQL en api/) ---
|
|
5
|
-
KOMPLIAN_DATABASE_URL_APP_DEVELOPMENT=
|
|
6
|
-
KOMPLIAN_DATABASE_URL_APP_STAGING=
|
|
7
|
-
KOMPLIAN_DATABASE_URL_APP_PRODUCTION=
|
|
8
|
-
|
|
9
|
-
# --- Admin DB ---
|
|
10
|
-
KOMPLIAN_DATABASE_URL_ADMIN_DEVELOPMENT=
|
|
11
|
-
KOMPLIAN_DATABASE_URL_ADMIN_STAGING=
|
|
12
|
-
KOMPLIAN_DATABASE_URL_ADMIN_PRODUCTION=
|
|
13
|
-
|
|
14
|
-
# --- Web / pilot DB ---
|
|
15
|
-
KOMPLIAN_DATABASE_URL_WEB_DEVELOPMENT=postgresql://neondb_owner:npg_1crYb3PaCjlf@ep-frosty-bird-agql4oq5-pooler.c-2.eu-central-1.aws.neon.tech/neondb?sslmode=require&channel_binding=require
|
|
16
|
-
KOMPLIAN_DATABASE_URL_WEB_STAGING=
|
|
17
|
-
KOMPLIAN_DATABASE_URL_WEB_PRODUCTION=
|
|
18
|
-
|
|
19
|
-
# Producción: solo @komplian.com; lista separada por comas (opcional; por defecto josue.santana@komplian.com).
|
|
20
|
-
# KOMPLIAN_DATABASE_PRODUCTION_ALLOWLIST=josue.santana@komplian.com,otro@komplian.com
|
|
21
|
-
|
|
22
|
-
# Operador para comprobar acceso a producción (alternativa: git config user.email).
|
|
23
|
-
# KOMPLIAN_DATABASE_OPERATOR_EMAIL=josue.santana@komplian.com
|
|
24
|
-
|
|
25
|
-
# Staging/producción: host debe ser Neon (*.neon.tech), salvo:
|
|
26
|
-
# KOMPLIAN_DATABASE_ALLOW_NON_NEON=1
|
|
27
|
-
# Development: Postgres local está permitido (mensaje informativo). Para exigir Neon también en dev:
|
|
28
|
-
# KOMPLIAN_DATABASE_REQUIRE_NEON_DEVELOPMENT=1
|