feature/integracion-cloudrestore-targets (#9)

integraciones para cloud recovery

Reviewed-on: #9
Co-authored-by: hreyes <hreyes@aduanasoft.com.mx>
Co-committed-by: hreyes <hreyes@aduanasoft.com.mx>
This commit is contained in:
2026-06-09 14:58:48 +00:00
committed by acazares
parent 3e5557c034
commit b8be307892
30 changed files with 2426 additions and 67 deletions

View File

@@ -0,0 +1,98 @@
/**
* Espera a que PostgreSQL responda y que el esquema CloudRestore (Alembic a24c)
* esté aplicado antes de arrancar el panel HTTPS.
*/
import { fileURLToPath } from 'url';
import pg from 'pg';
const { Pool } = pg;
const REQUIRED_TABLES = ['restore_targets', 'cloudrestore_status', 'restore_job_logs'];
function envInt(name, fallback) {
const raw = process.env[name];
if (!raw) return fallback;
const n = Number.parseInt(raw, 10);
return Number.isFinite(n) && n > 0 ? n : fallback;
}
function poolConfig() {
return {
host: process.env.DB_POSTGRES_HOST || 'localhost',
port: Number.parseInt(process.env.DB_POSTGRES_PORT || '5432', 10),
database: process.env.DB_POSTGRES_DB || 'a24c',
user: process.env.DB_POSTGRES_USER || 'postgres',
password: process.env.DB_POSTGRES_PASS || 'postgres',
max: 1,
connectionTimeoutMillis: 5000,
};
}
async function postgresReady(pool) {
await pool.query('SELECT 1');
}
async function tablesReady(pool) {
const r = await pool.query(
`
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'a24c'
AND table_name = ANY($1::text[])
`,
[REQUIRED_TABLES]
);
return r.rows.length === REQUIRED_TABLES.length;
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
export async function waitForA24cSchema() {
const maxAttempts = envInt('A24C_SCHEMA_WAIT_ATTEMPTS', 60);
const intervalMs = envInt('A24C_SCHEMA_WAIT_INTERVAL_MS', 2000);
const pool = new Pool(poolConfig());
console.log('==========================================');
console.log('Panel — esperando esquema a24c (CloudRestore)');
console.log(` host: ${poolConfig().host}:${poolConfig().port}`);
console.log(` db: ${poolConfig().database}`);
console.log('==========================================');
try {
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
try {
await postgresReady(pool);
if (await tablesReady(pool)) {
console.log('✓ PostgreSQL y esquema CloudRestore listos');
return;
}
console.log(
` intento ${attempt}/${maxAttempts}: tablas CRA pendientes (levanta a24c-backend si aún no corre)`
);
} catch (err) {
const msg = err instanceof Error ? err.message : String(err);
console.log(` intento ${attempt}/${maxAttempts}: ${msg}`);
}
if (attempt < maxAttempts) {
await sleep(intervalMs);
}
}
throw new Error(
'Timeout esperando esquema CloudRestore. Orden recomendado: ' +
'a24c-postgres → a24c-backend (alembic upgrade head) → panel'
);
} finally {
await pool.end();
}
}
if (process.argv[1] === fileURLToPath(import.meta.url)) {
waitForA24cSchema().catch((err) => {
console.error('❌', err instanceof Error ? err.message : err);
process.exit(1);
});
}