codigo0/backend/scripts/migrate-glossary-from-frontend.js

113 lines
3.4 KiB
JavaScript
Raw Normal View History

#!/usr/bin/env node
/**
* TICKET-012: Migrar glosarios del frontend al backend.
* Lee backend/scripts/fixtures/glossary-migration.json e inserta en tes_content.glossary_terms.
*
* Requisitos: tabla glossary_terms creada (npm run migrate:glossary).
* Opcional: generar fixture antes con npx tsx backend/scripts/generate-glossary-fixture.ts (desde raíz del repo).
*
* Uso: cd backend && node scripts/migrate-glossary-from-frontend.js
*/
import { readFile } from 'fs/promises';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { query } from '../config/database.js';
import 'dotenv/config';
const __dirname = dirname(fileURLToPath(import.meta.url));
const fixturePath = join(__dirname, 'fixtures', 'glossary-migration.json');
const SYSTEM_USER_ID = '00000000-0000-0000-0000-000000000001';
async function getFirstUserId() {
const tables = ['tes_content.users', 'emerges_content.users'];
for (const table of tables) {
try {
const result = await query(
`SELECT id FROM ${table} WHERE is_active = true LIMIT 1`
);
if (result.rows.length > 0) {
return result.rows[0].id;
}
} catch {
continue;
}
}
return SYSTEM_USER_ID;
}
async function termExists(term, category) {
const result = await query(
`SELECT 1 FROM tes_content.glossary_terms WHERE LOWER(term) = LOWER($1) AND category = $2 LIMIT 1`,
[term, category]
);
return result.rows.length > 0;
}
async function main() {
console.log('🔧 TICKET-012: Migración de glosarios frontend → backend\n');
let data;
try {
const raw = await readFile(fixturePath, 'utf-8');
data = JSON.parse(raw);
} catch (err) {
console.error('❌ No se encontró o no se pudo leer', fixturePath);
console.error(' Genera el fixture desde la raíz del repo:');
console.error(' npx tsx backend/scripts/generate-glossary-fixture.ts\n');
process.exit(1);
}
const terms = data.terms || [];
if (terms.length === 0) {
console.log('⚠️ El fixture no contiene términos. Nada que migrar.');
process.exit(0);
}
const userId = await getFirstUserId();
if (userId === SYSTEM_USER_ID) {
console.log('⚠️ No se encontró usuario en BD; usando ID de sistema para created_by.\n');
}
let inserted = 0;
let skipped = 0;
for (const row of terms) {
const { term, abbreviation, category, definition, context, source } = row;
if (!term || !definition || !category) {
skipped++;
continue;
}
if (!['pharmaceutical', 'anatomical', 'clinical', 'procedural'].includes(category)) {
skipped++;
continue;
}
const exists = await termExists(term, category);
if (exists) {
skipped++;
continue;
}
await query(
`INSERT INTO tes_content.glossary_terms (
id, term, abbreviation, category, definition, context, source, status,
created_at, updated_at, created_by, updated_by
) VALUES (
uuid_generate_v4(), $1, $2, $3, $4, $5, $6, 'published'::tes_content.content_status,
NOW(), NOW(), $7, $7
)`,
[term, abbreviation || null, category, definition, context || null, source || null, userId]
);
inserted++;
}
console.log(`✅ Migración completada: ${inserted} insertados, ${skipped} omitidos (ya existían o inválidos).\n`);
}
main().catch((err) => {
console.error('❌ Error:', err.message);
process.exit(1);
});