#!/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); });