Reestructuración modular integral de servicios, reorganización de gestión en docs/ y limpieza profunda del repositorio

This commit is contained in:
Ricardo Monla
2026-03-06 18:03:07 -03:00
parent 15725e0b3c
commit ac9bdf516b
286 changed files with 36187 additions and 112775 deletions
@@ -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
View File
@@ -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)}...`);
});