// ===================================================================== // Arranque del contenedor // --------------------------------------------------------------------- // Espera a que MySQL responda, siembra la base si está vacía y entonces // levanta el portal. Así el primer despliegue queda listo para usarse sin // que nadie tenga que entrar a correr comandos. // // SEMBRAR_SIEMPRE=true vuelve a sembrar en cada arranque: sirve para // devolver la demostración a su estado inicial después de que la hayan // probado. Por omisión NO se hace, para no borrar las ediciones. // ===================================================================== import mysql from 'mysql2/promise'; import { spawn } from 'node:child_process'; import path from 'node:path'; import { config } from '../server/config.mjs'; const raiz = path.resolve(import.meta.dirname, '..'); const REINTENTOS = Number(process.env.ESPERA_DB_INTENTOS ?? 60); const PAUSA_MS = Number(process.env.ESPERA_DB_PAUSA_MS ?? 2000); const pausa = (ms) => new Promise((r) => setTimeout(r, ms)); async function esperarBase() { for (let i = 1; i <= REINTENTOS; i++) { try { const cn = await mysql.createConnection({ ...config.db, connectTimeout: 4000 }); await cn.query('SELECT 1'); await cn.end(); console.log(` ✔ MySQL responde (intento ${i})`); return true; } catch (err) { if (i === 1 || i % 5 === 0) { console.log(` … esperando a MySQL en ${config.db.host}:${config.db.port} (${i}/${REINTENTOS}) · ${err.code ?? err.message}`); } await pausa(PAUSA_MS); } } return false; } async function hayDatos() { const cn = await mysql.createConnection(config.db); try { const [filas] = await cn.query( `SELECT COUNT(*) AS n FROM information_schema.tables WHERE table_schema = ? AND table_name = 'programas'`, [config.db.database], ); if (!filas[0].n) return false; const [[{ n }]] = await cn.query('SELECT COUNT(*) AS n FROM programas'); return n > 0; } finally { await cn.end(); } } function correr(script) { return new Promise((resolver, rechazar) => { const hijo = spawn(process.execPath, [path.join(raiz, script)], { stdio: 'inherit', cwd: raiz }); hijo.on('exit', (codigo) => (codigo === 0 ? resolver() : rechazar(new Error(`${script} salió con código ${codigo}`)))); hijo.on('error', rechazar); }); } console.log('\n▸ Portal INSTA · arranque\n'); if (!await esperarBase()) { console.error(`\n✖ MySQL no respondió en ${config.db.host}:${config.db.port} después de ${REINTENTOS} intentos.\n`); process.exit(1); } const forzar = String(process.env.SEMBRAR_SIEMPRE ?? '').toLowerCase() === 'true'; if (forzar) { console.log(' SEMBRAR_SIEMPRE=true · se reconstruye la base desde cero'); await correr('db/seed.mjs'); } else if (await hayDatos()) { console.log(' ✔ la base ya tiene datos: se conservan las ediciones'); } else { console.log(' la base está vacía: se siembra por primera vez'); await correr('db/seed.mjs'); } console.log('\n▸ Levantando el portal\n'); // Se reemplaza este proceso por el servidor para que las señales de Docker // (SIGTERM al redeplegar) lleguen al proceso que de verdad atiende. const servidor = spawn(process.execPath, [path.join(raiz, 'server/index.mjs')], { stdio: 'inherit', cwd: raiz }); for (const senal of ['SIGTERM', 'SIGINT']) { process.on(senal, () => servidor.kill(senal)); } servidor.on('exit', (codigo) => process.exit(codigo ?? 0));