🚀 P02: Despliegue exitoso del sistema DASUTEN en pcv-dasu0

Infraestructura y Red:
- Tailscale v1.94.2 instalado en srv-dasu (IP: 100.116.210.36)
- Configurado como subnet router para 10.0.100.0/24
- Fix: Repositorios enterprise de Proxmox deshabilitados (.sources → .bak)

Despliegue del Sistema (pcv-dasu0):
- Transferencia SCP de runSysDasuten.zip via SSH puerto 7022
- Extracción en C:\Dasuten\runSysDasuten- Kermet.ini configurado: SERVER=sql-dasuten
- InstaladorComponentesKermet.exe (silent install)
- Fuentes TrueType registradas en Windows (Code39, I2OF5)
- Firewall TCP 1433 abierto en sql-dasuten
- Test-NetConnection: TcpTestSucceeded=True
- Login exitoso con usuario aalmiron 

ADN y Contexto:
- 05_ia.md: Regla 13 Auto-Contextualización Topológica
- 07_proyectos.md: Sección 'Topología y Red' obligatoria en manifiestos
- P2601_dasuten.md: Contexto de red documentado
- nodos/pcv-dasu0.md: Puerto SSH actualizado a 7022

Dashboard P2601:
- Hitos completados: 8 → 11 (P02.APP, NET01, TEST03)
- Progreso: 67% → 79%
- pcv-dasu0: Testing → Online
- Nuevo hito activo: UAT (verificación presencial)

Bitácoras y Documentación:
- Bitácora 04/03 y 05/03 completas
- Plan de despliegue en docs/plan/260305-1017
- Investigación de ingeniería inversa documentada
- ns8-candados: Instrucciones de uso para IA agregadas
- dtic-BITACORAs: CRUD completo, Dashboard P2601 integrado

Herramientas:
- w-zombi: Mejora anti-loop (max 5 ciclos)
- Payloads de despliegue SQL y sistema generados
This commit is contained in:
Ricardo Monla
2026-03-05 13:15:31 -03:00
parent 680dbe6f8d
commit 15725e0b3c
24826 changed files with 3499 additions and 3325954 deletions
+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"
}
}
}
}
+5
View File
@@ -0,0 +1,5 @@
{
"dependencies": {
"pg": "^8.19.0"
}
}
+94
View File
@@ -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,77 @@
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: 5, id_hito: 'P02.APP', titulo: 'Despliegue del Sistema DASUTEN', desc: 'Transferencia SCP, extracción, configuración de Kermet.ini → SERVER=sql-dasuten, instalación de componentes Kermet y fuentes TrueType. Login exitoso con usuario 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 de Tailscale v1.94.2 en el hipervisor. Configurado como subnet router para 10.0.100.0/24. IP Tailscale: 100.116.210.36. Acceso remoto exterior garantizado.', 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 en sql-dasuten. Test-NetConnection confirmó TcpTestSucceeded: True. Conexión ODBC validada vía login al sistema.', 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 del sistema por Andrea Almirón in-situ. Se confirmó la operatividad completa de los módulos y generación de reportes correctos.', 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 en los gabinetes / racks de la oficina DASUTeN.', est: '⏳', fec: null, h_p: 0, h_r: 0, nodo: 'srv-dasu' },
{ fase: 6, id_hito: 'ADDOM', titulo: 'Unión de Cliente pc-dasu0 al Dominio', desc: 'Unir la PC física del cliente local al directorio activo (dc-dasuten) y configurar DNS primarios apuntando al controlador 10.0.100.10.', est: '⏳', fec: null, h_p: 0, h_r: 0, nodo: 'pc-dasu0' },
{ fase: 6, id_hito: 'GPO', titulo: 'Políticas de Seguridad Local', desc: 'Configuración de GPOs de dominio para restringir acceso de usuarios regulares, perfiles locales, mapeo de unidades y carpetas del sistema DASUTEN.', est: '⏳', fec: null, h_p: 0, h_r: 0, nodo: 'dc-dasuten' }
];
// 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);
+16
View File
@@ -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)}...`);
});