chore: prepara el proyecto para el primer deploy a produccion

- seed-db.js: los datos de ejemplo (departamentos TI/Finanzas/RRHH/Comercial +
  usuario admin con clave=usuario) solo se cargan si NODE_ENV != production.
  En produccion el seed solo carga catalogos (roles, prioridades, estados,
  tipos de proyecto) — la organizacion real la crea el admin desde la UI.
- Nuevo backend/src/scripts/create-admin.js: crea el usuario Administrador
  inicial de produccion con una clave fuerte generada al azar (nunca
  clave=usuario, esa convencion es solo para dev).
- frontend: agrega "serve" como dependencia de produccion (arquetipo B: el
  frontend corre como proceso separado sirviendo el build con
  `serve -s dist`).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Milton Gonzalez
2026-09-05 22:38:01 -05:00
parent 286dfda6df
commit c7b270e6ea
5 changed files with 1019 additions and 4 deletions
+46
View File
@@ -0,0 +1,46 @@
// Crea (o re-activa) el usuario Administrador inicial con una clave fuerte generada al azar,
// mostrada UNA sola vez. Para produccion — NUNCA usar la convencion clave=usuario aqui.
// Uso: node src/scripts/create-admin.js "Nombre Apellido" correo@dominio.com
import crypto from 'node:crypto';
import bcrypt from 'bcryptjs';
import mysql from 'mysql2/promise';
import { env } from '../config/env.js';
const [name, email] = process.argv.slice(2);
if (!name || !email) {
console.error('Uso: node src/scripts/create-admin.js "Nombre Apellido" correo@dominio.com');
process.exit(1);
}
async function run() {
const db = await mysql.createConnection({
host: env.db.host,
port: env.db.port,
database: env.db.name,
user: env.db.user,
password: env.db.password,
});
const [adminRole] = await db.query("SELECT id FROM roles WHERE code = 'admin'");
if (!adminRole.length) throw new Error('Rol admin no existe — correr primero npm run seed');
const password = crypto.randomBytes(18).toString('base64').replace(/[/+=]/g, '').slice(0, 20);
const passwordHash = await bcrypt.hash(password, 12);
await db.query(
`INSERT INTO users (name, email, password_hash, role_id, department_id, position, is_active)
VALUES (?, ?, ?, ?, NULL, NULL, 1)
ON DUPLICATE KEY UPDATE password_hash = VALUES(password_hash), is_active = 1`,
[name, email, passwordHash, adminRole[0].id]
);
console.log('[create-admin] listo. Guarda esta clave ahora — no se muestra de nuevo:');
console.log(` email: ${email}`);
console.log(` clave: ${password}`);
await db.end();
}
run().catch((err) => {
console.error('[create-admin] error:', err.message);
process.exit(1);
});
+10 -1
View File
@@ -11,7 +11,7 @@ async function run() {
password: env.db.password,
});
// Roles fijos del MVP.
// Catalogos base — necesarios para que la app funcione en CUALQUIER entorno (dev y produccion).
const roles = [
['admin', 'Administrador'],
['it_manager', 'Jefe de TI'],
@@ -70,6 +70,15 @@ async function run() {
await db.query('INSERT IGNORE INTO project_types (name) VALUES (?)', [name]);
}
// Datos de ejemplo (departamentos + usuario admin de dev) — SOLO en entornos no productivos.
// En produccion los departamentos reales los crea el admin desde Organizacion, y el usuario
// admin inicial se crea aparte con scripts/create-admin.js (clave fuerte generada, no "clave=usuario").
if (env.nodeEnv === 'production') {
console.log('[seed] NODE_ENV=production: solo se cargaron catalogos. Departamentos y usuario admin de ejemplo NO se crean aqui — usar create-admin.js.');
await db.end();
return;
}
const departments = [
['TI', null],
['Finanzas', null],