# Arquitectura de inventario — menú "Equipos" Diseño del módulo Equipos de Project. Se trajo de `linkedtrack.linkedcorp.net` (que sigue como producto aparte, sin cambios) y se reescribió siguiendo las reglas de Project: mismo login, roles, auditoría, UI Fluent y una sola base `project_linkedcorp`. ## Decisión de fondo | Opción | Veredicto | |---|---| | Servidor GLPI completo | Descartado: demasiada administración para solo inventariar. | | Agente 100% propio | Descartado: reescribir el sondeo de hardware (WMI, EDID, SNMP) es frágil y caro. | | **Agente GLPI oficial + colector propio dentro de Project** | **Elegido.** El agente recolecta; Project recibe, guarda y muestra. | ## Flujo ``` GLPI Agent (cada PC/servidor) --POST inventario--> https://project.linkedcorp.net/api/inventory (ingest.js: token -> inflar -> parsear JSON/XML -> normalizer.js -> upsert por deviceid -> diff en device_changes) Navegador (superadmin/admin/it_manager) --JWT--> /api/devices -> menú "Equipos" ``` - **Handshake:** el agente primero manda un `contact` (JSON) o `PROLOG` (XML). Se responde sin guardar nada (`{"status":"ok","expiration":"24"}` o `SEND…`). - **Ingesta autenticada por token**, no por JWT: tabla `inventory_tokens` (solo hash sha256). El agente lo manda como contraseña Basic (`user`/`password` del `agent.cfg`), `Bearer`, `X-Ingest-Token` o `?token=`. Sin token válido → `401`. - **Rutas:** `POST /api/inventory` (propia; pasa por el Custom Location `/api` de NPM sin cambios) y alias `POST /front/inventory.php` (ruta nativa de GLPI). Se montan ANTES de `express.json` porque usan cuerpo crudo. - **Límites:** cuerpo máx. 15 MB (`INVENTORY_MAX_BYTES`), inflado máx. 4× (anti zip-bomb). - **Upsert por `deviceid`** (estable), nunca por hostname. Componentes se reemplazan completos en una transacción; el diff (RAM, disco agregado/retirado, software instalado/desinstalado, SO, hostname) queda en `device_changes`. - **Baja lógica:** `status = retired`; un reporte nuevo NO reactiva un equipo dado de baja. - **Crudo para depurar:** último reporte por equipo en `storage/inventories/.json|xml` (se sobrescribe; descarga `GET /api/devices/:id/raw`). ## Normalización `backend/src/modules/devices/normalizer.js`: el JSON nativo y el XML (en mayúsculas) comparten nombres de campo, así que el XML se pasa a minúsculas y ambos usan el mismo mapeo. Todo campo ausente queda `null` (el formato varía por versión del agente y SO). Tipo de equipo (`desktop/laptop/server/virtual/printer/network/other`) se infiere de `chassis_type`, `vmsystem`, nombre del SO e `itemtype`. Lo que no tiene columna (impresoras/red SNMP, antivirus, video, batería) se guarda en `devices.extra_json`. ## Instalar el agente en un equipo 1. Superadmin/admin: menú **Equipos → Agentes → Generar token** (se muestra una sola vez). 2. En el equipo, instalar GLPI Agent (MSI en Windows; `.deb`/`.rpm`/tarball en Linux) con: ``` server = https://project.linkedcorp.net/api/inventory user = project password = tag = ``` Windows (instalador): `SERVER=… USER=project PASSWORD=`. 3. Forzar un envío: `glpi-agent --force` (Linux) / "Forzar inventario" en la página del agente (`http://localhost:62354`) en Windows. El equipo aparece en **Equipos** al primer POST. ## Instalador con un clic (Windows) Equipos → Agentes → "Instalador para Windows": genera `instalar-glpi-agent.bat` en el navegador con `SERVER=/api/inventory`, `USER=project`, `PASSWORD=` y `TAG` opcional. El .bat se auto-eleva, descarga `GLPI-Agent--x64.msi` de GitHub (versión fijada en `frontend/src/pages/Equipos/agentInstaller.js#GLPI_AGENT_VERSION`; o usa el .msi que esté junto al .bat), instala con `msiexec /qn` y `RUNNOW=1`, y verifica que el servicio `glpi-agent` corra. El token solo se conoce al crearlo, por eso el .bat se genera en la pantalla y no en el servidor. Linux: usar el instalador oficial `glpi-agent--linux-installer.pl --server=… --user=… --password=…`. ## Pendientes / por verificar con un agente real - Confirmar que el agente acepta la respuesta del handshake tal cual (si no, ajustar `ingest.js`). - Capturar un JSON real (`glpi-agent --local=/tmp/inv --json`) y contrastar campos con `normalizer.js` (hoy basado en el formato documentado del agente). - Impresoras/red por SNMP (`netinventory`): solo se guarda lo básico + `extra_json`; falta una vista propia cuando haya un caso real. - Panel de impresoras con contador de páginas/tóner y export CSV: fase 2.