Reestructuración modular integral de servicios, reorganización de gestión en docs/ y limpieza profunda del repositorio
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
const { Client } = require('pg');
|
||||
|
||||
const client = new Client({
|
||||
user: 'dtic_user',
|
||||
host: '127.0.0.1',
|
||||
database: 'dtic_bitacoras',
|
||||
password: 'dtic_pass_2026',
|
||||
port: 5433,
|
||||
});
|
||||
|
||||
async function run() {
|
||||
await client.connect();
|
||||
console.log("Connected to DB");
|
||||
|
||||
const hitos = await client.query("SELECT f.numero as fase, h.id_hito, h.titulo FROM bitacoras.hitos h JOIN bitacoras.fases f ON h.fase_id = f.id ORDER BY f.numero, h.id_hito");
|
||||
console.table(hitos.rows);
|
||||
|
||||
await client.end();
|
||||
}
|
||||
run().catch(console.error);
|
||||
@@ -0,0 +1,37 @@
|
||||
const { Client } = require('pg');
|
||||
|
||||
const client = new Client({
|
||||
user: 'dtic_user',
|
||||
host: '127.0.0.1',
|
||||
database: 'dtic_bitacoras',
|
||||
password: 'dtic_pass_2026',
|
||||
port: 5433,
|
||||
});
|
||||
|
||||
async function run() {
|
||||
await client.connect();
|
||||
console.log("Connected to DB");
|
||||
|
||||
// Mapeo de IDs antiguos a nuevos
|
||||
const mapeos = [
|
||||
{ old: 'BKP02', new: 'f05001' },
|
||||
{ old: 'DB02', new: 'f05002' },
|
||||
{ old: 'MON01', new: 'f05003' },
|
||||
{ old: 'DOC01', new: 'f05004' },
|
||||
{ old: 'PERF01', new: 'f05005' },
|
||||
];
|
||||
|
||||
for (const mapeo of mapeos) {
|
||||
console.log(`Updating ${mapeo.old} to ${mapeo.new}...`);
|
||||
const res = await client.query(
|
||||
"UPDATE bitacoras.hitos SET id_hito = $1 WHERE id_hito = $2 RETURNING id",
|
||||
[mapeo.new, mapeo.old]
|
||||
);
|
||||
console.log(`Updated ${res.rowCount} row(s)`);
|
||||
}
|
||||
|
||||
console.log("Migration completed");
|
||||
await client.end();
|
||||
}
|
||||
|
||||
run().catch(console.error);
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
{
|
||||
"name": "tools",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"dependencies": {
|
||||
"pg": "^8.19.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.19.0",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.19.0.tgz",
|
||||
"integrity": "sha512-QIcLGi508BAHkQ3pJNptsFz5WQMlpGbuBGBaIaXsWK8mel2kQ/rThYI+DbgjUvZrIr7MiuEuc9LcChJoEZK1xQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pg-connection-string": "^2.11.0",
|
||||
"pg-pool": "^3.12.0",
|
||||
"pg-protocol": "^1.12.0",
|
||||
"pg-types": "2.2.0",
|
||||
"pgpass": "1.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 16.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"pg-cloudflare": "^1.3.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg-native": ">=3.0.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pg-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pg-cloudflare": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.3.0.tgz",
|
||||
"integrity": "sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==",
|
||||
"license": "MIT",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pg-connection-string": {
|
||||
"version": "2.11.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.11.0.tgz",
|
||||
"integrity": "sha512-kecgoJwhOpxYU21rZjULrmrBJ698U2RxXofKVzOn5UDj61BPj/qMb7diYUR1nLScCDbrztQFl1TaQZT0t1EtzQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pg-int8": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-pool": {
|
||||
"version": "3.12.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.12.0.tgz",
|
||||
"integrity": "sha512-eIJ0DES8BLaziFHW7VgJEBPi5hg3Nyng5iKpYtj3wbcAUV9A1wLgWiY7ajf/f/oO1wfxt83phXPY8Emztg7ITg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"pg": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-protocol": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.12.0.tgz",
|
||||
"integrity": "sha512-uOANXNRACNdElMXJ0tPz6RBM0XQ61nONGAwlt8da5zs/iUOOCLBQOHSXnrC6fMsvtjxbOJrZZl5IScGv+7mpbg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"pg-int8": "1.0.1",
|
||||
"postgres-array": "~2.0.0",
|
||||
"postgres-bytea": "~1.0.0",
|
||||
"postgres-date": "~1.0.4",
|
||||
"postgres-interval": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pgpass": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-bytea": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.1.tgz",
|
||||
"integrity": "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-date": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-interval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"pg": "^8.19.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
const fs = require('fs');
|
||||
const { Pool } = require('pg');
|
||||
|
||||
const pool = new Pool({
|
||||
connectionString: "postgres://dtic_user:dtic_pass_2026@localhost:5433/dtic_bitacoras"
|
||||
});
|
||||
|
||||
async function syncFile(filePath) {
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
const lines = content.split('\n');
|
||||
const dateStr = filePath.match(/(\d{4}-\d{2}-\d{2})\.md/)[1];
|
||||
|
||||
console.log(`Sincronizando bitácora: ${dateStr}`);
|
||||
|
||||
// Asegurar bitácora
|
||||
const bResult = await pool.query(
|
||||
'INSERT INTO bitacoras.bitacoras (fecha) VALUES ($1) ON CONFLICT (fecha) DO UPDATE SET fecha = EXCLUDED.fecha RETURNING id',
|
||||
[dateStr]
|
||||
);
|
||||
const bitacoraId = bResult.rows[0].id;
|
||||
|
||||
// Obtener mapeo de nodos
|
||||
const { rows: nodos } = await pool.query('SELECT id, nombre FROM bitacoras.nodos');
|
||||
const nodoMap = {};
|
||||
nodos.forEach(n => { nodoMap[n.nombre] = n.id; });
|
||||
|
||||
// Limpiar entradas previas
|
||||
await pool.query('DELETE FROM bitacoras.entradas WHERE bitacora_id = $1', [bitacoraId]);
|
||||
|
||||
let currentNodoId = null;
|
||||
let inDetailedActivities = false;
|
||||
let entriesFound = 0;
|
||||
|
||||
for (let line of lines) {
|
||||
// Detectar inicio de sección de actividades detalladas
|
||||
if (line.includes('Actividades Detalladas')) {
|
||||
inDetailedActivities = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inDetailedActivities) continue;
|
||||
|
||||
// Detectar cambio de nodo
|
||||
if (line.startsWith('### ')) {
|
||||
const nodoName = line.replace('### ', '').trim().split(' ')[0];
|
||||
currentNodoId = nodoMap[nodoName] || null;
|
||||
if (currentNodoId) console.log(`Cambiando a nodo: ${nodoName} (ID: ${currentNodoId})`);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!currentNodoId) continue;
|
||||
|
||||
// Detectar entrada de tabla
|
||||
const regex = /\|\s*(\d{2}:\d{2})\s*\|\s*(\d{2}:\d{2})\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|/;
|
||||
const match = line.match(regex);
|
||||
|
||||
if (match) {
|
||||
let inicio = match[1];
|
||||
let fin = match[2];
|
||||
let descRaw = match[3];
|
||||
let estadoRaw = match[4];
|
||||
|
||||
const esIA = descRaw.includes('(IA)');
|
||||
let descripcion = descRaw.replace('(IA)', '').replace(/\*\*/g, '').replace(/__/g, '').trim();
|
||||
// A veces el MD tiene ** al principio
|
||||
if (descripcion.startsWith('**')) descripcion = descripcion.substring(2);
|
||||
|
||||
const statusMatch = estadoRaw.match(/\[([A-Z])\] (.*)/);
|
||||
const modo = statusMatch ? statusMatch[1] : 'P';
|
||||
const estado = statusMatch ? statusMatch[2] : estadoRaw;
|
||||
|
||||
await pool.query(
|
||||
`INSERT INTO bitacoras.entradas (inicio, fin, descripcion, estado, modo, es_ia, bitacora_id, nodo_id)
|
||||
VALUES ($1, $2, $3, $4, $5, $6, $7, $8)`,
|
||||
[inicio, fin, descripcion, estado, modo, esIA, bitacoraId, currentNodoId]
|
||||
);
|
||||
entriesFound++;
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`Sincronización finalizada. Entradas cargadas: ${entriesFound}`);
|
||||
await pool.end();
|
||||
}
|
||||
|
||||
const file = process.argv[2];
|
||||
if (!file) {
|
||||
console.error('Uso: node sync_bitacora.js <path_to_md>');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
syncFile(file).catch(err => {
|
||||
console.error(err);
|
||||
process.exit(1);
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
const { Client } = require('pg');
|
||||
|
||||
const client = new Client({
|
||||
user: 'dtic_user',
|
||||
host: '127.0.0.1',
|
||||
database: 'dtic_bitacoras',
|
||||
password: 'dtic_pass_2026',
|
||||
port: 5433,
|
||||
});
|
||||
|
||||
async function run() {
|
||||
await client.connect();
|
||||
console.log("Connected to DB");
|
||||
|
||||
const codigo = 'P2601';
|
||||
|
||||
// Get Project ID
|
||||
const pRes = await client.query("SELECT id FROM bitacoras.proyectos WHERE codigo = $1", [codigo]);
|
||||
if (pRes.rowCount === 0) {
|
||||
console.error("Proyecto no encontrado");
|
||||
process.exit(1);
|
||||
}
|
||||
const pid = pRes.rows[0].id;
|
||||
|
||||
// Fases IDs
|
||||
const fRes = await client.query("SELECT id, numero FROM bitacoras.fases WHERE proyecto_id = $1", [pid]);
|
||||
const fases = {};
|
||||
fRes.rows.forEach(f => fases[f.numero] = f.id);
|
||||
|
||||
// Nodos IDs
|
||||
const nRes = await client.query("SELECT id, nombre FROM bitacoras.nodos");
|
||||
const nodos = {};
|
||||
nRes.rows.forEach(n => nodos[n.nombre] = n.id);
|
||||
|
||||
// Updates a realizar según el HTML
|
||||
const hitos = [
|
||||
{ fase: 1, id_hito: 'A00-A01', titulo: 'Acondicionamiento Hardware + Proxmox VE', desc: 'Instalación física del servidor srv-dasu y despliegue de Proxmox VE 9.1.', est: '✅', fec: '2026-02-23', h_p: 2.5, h_r: 0, nodo: 'srv-dasu' },
|
||||
{ fase: 2, id_hito: 'A05', titulo: 'VM Windows Server Core → DC', desc: 'Creación de VM, instalación de OS, promoción a Controlador de Dominio (dc-dasuten).', est: '✅', fec: '2026-02-24', h_p: 0.5, h_r: 14, nodo: 'dc-dasuten' },
|
||||
{ fase: 2, id_hito: 'A06', titulo: 'Dashboard P2601', desc: 'Despliegue del panel web con telemetría operativa, glassmorphism y métricas.', est: '✅', fec: '2026-02-24', h_p: 0, h_r: 2, nodo: 'srv-ns8' },
|
||||
{ fase: 3, id_hito: 'DB01', titulo: 'SQL Server 2019 en sql-dasuten', desc: 'Instalación de SQL Server 2019 con collation Modern_Spanish_CI_AS.', est: '✅', fec: '2026-02-25', h_p: 0, h_r: 8, nodo: 'sql-dasuten' },
|
||||
{ fase: 4, id_hito: 'SSH', titulo: 'OpenSSH Server en VMs Windows', desc: 'Despliegue de SSH nativo en dc-dasuten y sql-dasuten. Puerto 7022, firewall configurado.', est: '✅', fec: '2026-02-26', h_p: 0, h_r: 3, nodo: 'dc-dasuten' },
|
||||
{ fase: 4, id_hito: 'BKP', titulo: 'Resguardo VZDump', desc: 'Backup offline (stop mode) de las VMs DC y SQL vía VZDump.', est: '✅', fec: '2026-02-26', h_p: 0, h_r: 1, nodo: 'srv-dasu' },
|
||||
{ fase: 4, id_hito: 'TEST', titulo: 'VM de Pruebas pcv-dasu0', desc: 'Despliegue de Win 10 LTSC, SSH, toolkit W-Zombi. Unión al dominio.', est: '✅', fec: '2026-02-27', h_p: 0, h_r: 6, nodo: 'pcv-dasu0' },
|
||||
{ fase: 3, id_hito: 'P02.DB', titulo: 'Restauración Base de Datos', desc: 'Backup bksysdasuten_Full.bak (~9GB) restaurado. Login sysadmin creado.', est: '✅', fec: '2026-03-04', h_p: 0, h_r: 1, nodo: 'sql-dasuten' },
|
||||
{ fase: 5, id_hito: 'P02.APP', titulo: 'Despliegue del Sistema DASUTEN', desc: 'Configuración Kermet.ini, componentes y fuentes. Login exitoso con aalmiron.', est: '✅', fec: '2026-03-05', h_p: 1, h_r: 2, nodo: 'pcv-dasu0' },
|
||||
{ fase: 5, id_hito: 'NET01', titulo: 'Tailscale VPN en srv-dasu', desc: 'Instalación y configuración como subnet router para 10.0.100.0/24.', est: '✅', fec: '2026-03-05', h_p: 0, h_r: 0.5, nodo: 'srv-dasu' },
|
||||
{ fase: 5, id_hito: 'TEST03', titulo: 'Conectividad SQL desde Cliente', desc: 'Apertura de firewall TCP 1433 y validación ODBC.', est: '✅', fec: '2026-03-05', h_p: 0, h_r: 1, nodo: 'pcv-dasu0' },
|
||||
{ fase: 6, id_hito: 'UAT', titulo: 'Verificación Presencial por Usuario', desc: 'Validación funcional in-situ con Andrea Almirón. Resultado exitoso.', est: '✅', fec: '2026-03-05', h_p: 1, h_r: 0, nodo: 'pc-dasu0' },
|
||||
{ fase: 6, id_hito: 'FÍSICO', titulo: '🧱 Instalación Física de srv-dasu', desc: 'Traslado e instalación definitiva del servidor físico Proxmox.', est: '⏳', fec: null, h_p: 0, h_r: 0, nodo: 'srv-dasu' },
|
||||
{ fase: 6, id_hito: 'ADDOM', titulo: '🤝 AD-Join: pc-dasu0 al Dominio', desc: 'Unir la PC física del cliente local al directorio activo (dc-dasuten).', est: '⏳', fec: null, h_p: 0, h_r: 0, nodo: 'pc-dasu0' },
|
||||
{ fase: 6, id_hito: 'GPO', titulo: '🛡️ Políticas de Seguridad (GPO)', desc: 'Configuración de GPOs de dominio para restringir acceso y mapeos.', est: '⏳', fec: null, h_p: 0, h_r: 0, nodo: 'dc-dasuten' },
|
||||
{ fase: 6, id_hito: 'S02', titulo: '🔐 Hardenización SSH', desc: 'Llaves SSH, passphrases, auditoría y elevación de seguridad.', est: '⏳', fec: null, h_p: 0, h_r: 0, nodo: 'dc-dasuten' },
|
||||
{ fase: 6, id_hito: 'CLI', titulo: '💻 Configuración Final pc-dasu0', desc: 'PC física de la oficina DASUTeN como cliente del dominio.', est: '⏳', fec: null, h_p: 0, h_r: 0, nodo: 'pc-dasu0' }
|
||||
];
|
||||
|
||||
// Primero marcamos P02.DB, P02.APP viejos u otros como completados/borrados si es necesario
|
||||
await client.query("UPDATE bitacoras.hitos SET estado = '✅', fecha = '2026-03-04' WHERE id_hito = 'P02.DB'");
|
||||
await client.query("DELETE FROM bitacoras.hitos WHERE id_hito IN ('MIG', 'P02.APP')");
|
||||
|
||||
for (const h of hitos) {
|
||||
if (!fases[h.fase]) {
|
||||
console.log(`Fase ${h.fase} no existe, creándola...`);
|
||||
const faseI = await client.query("INSERT INTO bitacoras.fases (proyecto_id, numero, titulo) VALUES ($1, $2, $3) RETURNING id", [pid, h.fase, `Fase ${h.fase}`]);
|
||||
fases[h.fase] = faseI.rows[0].id;
|
||||
}
|
||||
|
||||
const nodo_id = nodos[h.nodo] || null;
|
||||
|
||||
// Verificar si existe el hito
|
||||
const exRes = await client.query("SELECT id FROM bitacoras.hitos WHERE id_hito = $1 AND fase_id = $2", [h.id_hito, fases[h.fase]]);
|
||||
|
||||
if (exRes.rowCount > 0) {
|
||||
console.log(`Updating ${h.id_hito}...`);
|
||||
await client.query(`UPDATE bitacoras.hitos SET titulo=$1, descripcion=$2, estado=$3, fecha=$4, horas_presencial=$5, horas_remoto=$6, nodo_id=$7 WHERE id=$8`,
|
||||
[h.titulo, h.desc, h.est, h.fec, h.h_p, h.h_r, nodo_id, exRes.rows[0].id]);
|
||||
} else {
|
||||
console.log(`Inserting ${h.id_hito}...`);
|
||||
await client.query(`INSERT INTO bitacoras.hitos (fase_id, id_hito, titulo, descripcion, estado, fecha, horas_presencial, horas_remoto, nodo_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)`,
|
||||
[fases[h.fase], h.id_hito, h.titulo, h.desc, h.est, h.fec, h.h_p, h.h_r, nodo_id]);
|
||||
}
|
||||
}
|
||||
|
||||
console.log("Done");
|
||||
await client.end();
|
||||
}
|
||||
|
||||
run().catch(console.error);
|
||||
@@ -0,0 +1,16 @@
|
||||
const section = `srv-ns8
|
||||
|
||||
#### ⏳ - SINC01 - Inicio de Jornada Presencial
|
||||
🚀 Inicio de jornada presencial (08:00 - 13:00). Sincronización de contexto y tareas administrativas.
|
||||
|
||||
| I | F | Descripción | E |
|
||||
| :--- | :--- | :--- | :--- |
|
||||
| 12:35 | 12:50 | (IA) **P2603 - Sistema de Bitácoras**: Creación completa de \`dtic-BITACORAs/\`. Backend API REST (3 rutas CRUD), esquema SQL I-F-D-E, frontend React 18 dark mode, Docker Compose. 3 contenedores operativos. | [P] ✅ |
|
||||
| 11:15 | 11:37 | (IA) **Categorización de Nodos**: Separación de nodos exclusivos vs. soporte en P2601 y P2602. ADN 07 actualizado con el nuevo estándar. | [P] ✅ |`;
|
||||
|
||||
const regex = /\|\s*(\d{2}:\d{2})\s*\|\s*(\d{2}:\d{2})\s*\|\s*(.*?)\s*\|\s*(.*?)\s*\|/g;
|
||||
const matches = [...section.matchAll(regex)];
|
||||
console.log(`Encontradas: ${matches.length}`);
|
||||
matches.forEach((m, i) => {
|
||||
console.log(`Match ${i}: ${m[1]} - ${m[2]} - ${m[3].substring(0, 20)}...`);
|
||||
});
|
||||
Reference in New Issue
Block a user