inventschario/docs/arquitectura-diagrama.html

343 lines
19 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="es">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Inventschario — Diagrama de Arquitectura</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'JetBrains Mono', monospace;
background: #020617;
min-height: 100vh;
padding: 2rem;
color: white;
}
.container { max-width: 1200px; margin: 0 auto; }
.header { margin-bottom: 2rem; }
.header-row { display: flex; align-items: center; gap: 1rem; margin-bottom: 0.5rem; }
.pulse-dot {
width: 12px; height: 12px; background: #22d3ee;
border-radius: 50%; animation: pulse 2s infinite;
}
@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.5; } }
h1 { font-size: 1.5rem; font-weight: 700; letter-spacing: -0.025em; }
.subtitle { color: #94a3b8; font-size: 0.875rem; margin-left: 1.75rem; }
.diagram-container {
background: rgba(15, 23, 42, 0.5);
border-radius: 1rem;
border: 1px solid #1e293b;
padding: 1.5rem;
overflow-x: auto;
}
svg { width: 100%; min-width: 900px; display: block; }
.cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 1rem;
margin-top: 2rem;
}
.card {
background: rgba(15, 23, 42, 0.5);
border-radius: 0.75rem;
border: 1px solid #1e293b;
padding: 1.25rem;
}
.card-header { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.75rem; }
.card-dot { width: 8px; height: 8px; border-radius: 50%; }
.card-dot.cyan { background: #22d3ee; }
.card-dot.emerald { background: #34d399; }
.card-dot.violet { background: #a78bfa; }
.card-dot.amber { background: #fbbf24; }
.card-dot.rose { background: #fb7185; }
.card h3 { font-size: 0.875rem; font-weight: 600; }
.card ul { list-style: none; color: #94a3b8; font-size: 0.75rem; }
.card li { margin-bottom: 0.375rem; }
.footer { text-align: center; margin-top: 1.5rem; color: #475569; font-size: 0.75rem; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<div class="header-row">
<div class="pulse-dot"></div>
<h1>Inventschario — Arquitectura del Sistema</h1>
</div>
<p class="subtitle">Aplicación de inventario de dispositivos electrónicos • Python + Flask + SQLite • Local-first</p>
</div>
<div class="diagram-container">
<svg viewBox="0 0 1000 700">
<defs>
<marker id="arrowhead" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
<polygon points="0 0, 10 3.5, 0 7" fill="#64748b" />
</marker>
<pattern id="grid" width="40" height="40" patternUnits="userSpaceOnUse">
<path d="M 40 0 L 0 0 0 40" fill="none" stroke="#1e293b" stroke-width="0.5"/>
</pattern>
</defs>
<!-- Background Grid -->
<rect width="100%" height="100%" fill="url(#grid)" />
<!-- ===== MACHINE BOUNDARY ===== -->
<rect x="20" y="20" width="960" height="660" rx="12" fill="rgba(251, 191, 36, 0.03)" stroke="#fbbf24" stroke-width="1" stroke-dasharray="8,4"/>
<text x="32" y="38" fill="#fbbf24" font-size="10" font-weight="600">Windows 11 Pro — Máquina Local (127.0.0.1)</text>
<!-- ===== BROWSER (FRONTEND) ===== -->
<rect x="40" y="55" width="920" height="180" rx="8" fill="rgba(8, 51, 68, 0.15)" stroke="#22d3ee" stroke-width="1" stroke-dasharray="4,4"/>
<text x="52" y="73" fill="#22d3ee" font-size="10" font-weight="600">NAVEGADOR (Edge)</text>
<!-- SPA Shell -->
<rect x="60" y="85" width="140" height="55" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
<text x="130" y="108" fill="white" font-size="11" font-weight="600" text-anchor="middle">SPA Shell</text>
<text x="130" y="124" fill="#94a3b8" font-size="8" text-anchor="middle">index.html + Router</text>
<!-- Components -->
<rect x="220" y="85" width="140" height="55" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
<text x="290" y="108" fill="white" font-size="11" font-weight="600" text-anchor="middle">Componentes</text>
<text x="290" y="124" fill="#94a3b8" font-size="8" text-anchor="middle">Table | Form | Modal</text>
<!-- API Client -->
<rect x="380" y="85" width="140" height="55" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
<text x="450" y="108" fill="white" font-size="11" font-weight="600" text-anchor="middle">API Client</text>
<text x="450" y="124" fill="#94a3b8" font-size="8" text-anchor="middle">fetch wrapper + errors</text>
<!-- Pages -->
<rect x="540" y="85" width="140" height="55" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
<text x="610" y="108" fill="white" font-size="11" font-weight="600" text-anchor="middle">Páginas</text>
<text x="610" y="124" fill="#94a3b8" font-size="8" text-anchor="middle">Dashboard | Inventory</text>
<!-- Setup Wizard -->
<rect x="700" y="85" width="140" height="55" rx="6" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1.5"/>
<text x="770" y="108" fill="white" font-size="11" font-weight="600" text-anchor="middle">Setup Wizard</text>
<text x="770" y="124" fill="#94a3b8" font-size="8" text-anchor="middle">6 pasos de config</text>
<!-- Frontend labels -->
<text x="60" y="170" fill="#22d3ee" font-size="9">HTML + CSS + Alpine.js</text>
<text x="60" y="185" fill="#94a3b8" font-size="8">Sin build step • System fonts • WCAG 2.1 AA</text>
<text x="60" y="200" fill="#94a3b8" font-size="8">Router hash-based • Componentes vanilla JS</text>
<!-- ===== HTTP ARROW ===== -->
<line x1="450" y1="145" x2="450" y2="260" stroke="#22d3ee" stroke-width="2" marker-end="url(#arrowhead)"/>
<rect x="395" y="190" width="110" height="22" rx="4" fill="#020617" stroke="#1e293b" stroke-width="1"/>
<text x="450" y="205" fill="#22d3ee" font-size="9" text-anchor="middle" font-weight="600">HTTP localhost</text>
<!-- ===== API GATEWAY ===== -->
<rect x="40" y="255" width="920" height="130" rx="8" fill="rgba(6, 78, 59, 0.15)" stroke="#34d399" stroke-width="1" stroke-dasharray="4,4"/>
<text x="52" y="273" fill="#34d399" font-size="10" font-weight="600">API GATEWAY — Flask REST/JSON</text>
<!-- Device Routes -->
<rect x="60" y="285" width="150" height="45" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
<text x="135" y="305" fill="white" font-size="10" font-weight="600" text-anchor="middle">/api/v1/devices</text>
<text x="135" y="319" fill="#94a3b8" font-size="8" text-anchor="middle">CRUD + History</text>
<!-- Import/Export Routes -->
<rect x="230" y="285" width="150" height="45" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
<text x="305" y="305" fill="white" font-size="10" font-weight="600" text-anchor="middle">/api/v1/import|export</text>
<text x="305" y="319" fill="#94a3b8" font-size="8" text-anchor="middle">CSV operations</text>
<!-- Backup Routes -->
<rect x="400" y="285" width="150" height="45" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
<text x="475" y="305" fill="white" font-size="10" font-weight="600" text-anchor="middle">/api/v1/backups</text>
<text x="475" y="319" fill="#94a3b8" font-size="8" text-anchor="middle">Create | Restore | List</text>
<!-- Config Routes -->
<rect x="570" y="285" width="150" height="45" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
<text x="645" y="305" fill="white" font-size="10" font-weight="600" text-anchor="middle">/api/v1/config</text>
<text x="645" y="319" fill="#94a3b8" font-size="8" text-anchor="middle">Settings + Labels</text>
<!-- Middleware -->
<rect x="740" y="285" width="200" height="45" rx="6" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1.5"/>
<text x="840" y="305" fill="white" font-size="10" font-weight="600" text-anchor="middle">Middleware</text>
<text x="840" y="319" fill="#94a3b8" font-size="8" text-anchor="middle">CORS • JSON parse • Errors</text>
<!-- Gateway details -->
<text x="60" y="355" fill="#34d399" font-size="9">Blueprint pattern • Solo escucha 127.0.0.1 • Sin exposición a red</text>
<!-- ===== SERVICE LAYER ===== -->
<rect x="40" y="400" width="920" height="100" rx="8" fill="rgba(251, 146, 60, 0.1)" stroke="#fb923c" stroke-width="1" stroke-dasharray="4,4"/>
<text x="52" y="418" fill="#fb923c" font-size="10" font-weight="600">CAPA DE SERVICIOS — Lógica de Negocio</text>
<rect x="60" y="430" width="140" height="45" rx="6" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1.5"/>
<text x="130" y="450" fill="white" font-size="10" font-weight="600" text-anchor="middle">DeviceService</text>
<text x="130" y="464" fill="#94a3b8" font-size="8" text-anchor="middle">CRUD + Lifecycle</text>
<rect x="220" y="430" width="140" height="45" rx="6" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1.5"/>
<text x="290" y="450" fill="white" font-size="10" font-weight="600" text-anchor="middle">BackupService</text>
<text x="290" y="464" fill="#94a3b8" font-size="8" text-anchor="middle">Schedule + Gzip</text>
<rect x="380" y="430" width="140" height="45" rx="6" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1.5"/>
<text x="450" y="450" fill="white" font-size="10" font-weight="600" text-anchor="middle">ImportService</text>
<text x="450" y="464" fill="#94a3b8" font-size="8" text-anchor="middle">CSV + Mapper</text>
<rect x="540" y="430" width="140" height="45" rx="6" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1.5"/>
<text x="610" y="450" fill="white" font-size="10" font-weight="600" text-anchor="middle">ExportService</text>
<text x="610" y="464" fill="#94a3b8" font-size="8" text-anchor="middle">CSV + Labels</text>
<rect x="700" y="430" width="140" height="45" rx="6" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1.5"/>
<text x="770" y="450" fill="white" font-size="10" font-weight="600" text-anchor="middle">ConfigService</text>
<text x="770" y="464" fill="#94a3b8" font-size="8" text-anchor="middle">KV Store + Labels</text>
<!-- Arrows from Gateway to Services -->
<line x1="130" y1="335" x2="130" y2="428" stroke="#fb923c" stroke-width="1.5" marker-end="url(#arrowhead)"/>
<line x1="305" y1="335" x2="290" y2="428" stroke="#fb923c" stroke-width="1.5" marker-end="url(#arrowhead)"/>
<line x1="475" y1="335" x2="450" y2="428" stroke="#fb923c" stroke-width="1.5" marker-end="url(#arrowhead)"/>
<line x1="645" y1="335" x2="610" y2="428" stroke="#fb923c" stroke-width="1.5" marker-end="url(#arrowhead)"/>
<line x1="840" y1="335" x2="770" y2="428" stroke="#fb923c" stroke-width="1.5" marker-end="url(#arrowhead)"/>
<!-- ===== DATA LAYER ===== -->
<rect x="40" y="515" width="920" height="140" rx="8" fill="rgba(76, 29, 149, 0.15)" stroke="#a78bfa" stroke-width="1" stroke-dasharray="4,4"/>
<text x="52" y="533" fill="#a78bfa" font-size="10" font-weight="600">CAPA DE DATOS — SQLite (WAL mode)</text>
<rect x="60" y="545" width="130" height="45" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
<text x="125" y="565" fill="white" font-size="10" font-weight="600" text-anchor="middle">DeviceRepo</text>
<text x="125" y="579" fill="#94a3b8" font-size="8" text-anchor="middle">devices table</text>
<rect x="210" y="545" width="130" height="45" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
<text x="275" y="565" fill="white" font-size="10" font-weight="600" text-anchor="middle">HistoryRepo</text>
<text x="275" y="579" fill="#94a3b8" font-size="8" text-anchor="middle">device_history</text>
<rect x="360" y="545" width="130" height="45" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
<text x="425" y="565" fill="white" font-size="10" font-weight="600" text-anchor="middle">ConfigRepo</text>
<text x="425" y="579" fill="#94a3b8" font-size="8" text-anchor="middle">app_config + labels</text>
<rect x="510" y="545" width="130" height="45" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
<text x="575" y="565" fill="white" font-size="10" font-weight="600" text-anchor="middle">BackupRepo</text>
<text x="575" y="579" fill="#94a3b8" font-size="8" text-anchor="middle">backup_log</text>
<!-- SQLite Database -->
<rect x="680" y="545" width="260" height="90" rx="6" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1.5"/>
<text x="810" y="565" fill="white" font-size="11" font-weight="600" text-anchor="middle">SQLite</text>
<text x="810" y="582" fill="#94a3b8" font-size="8" text-anchor="middle">inventschario.db</text>
<text x="810" y="598" fill="#a78bfa" font-size="8" text-anchor="middle">WAL mode • Foreign Keys</text>
<text x="810" y="614" fill="#94a3b8" font-size="8" text-anchor="middle">6 tablas • 4 índices únicos</text>
<text x="810" y="628" fill="#94a3b8" font-size="8" text-anchor="middle">%APPDATA%/inventschario/</text>
<!-- Arrows from Services to Repos -->
<line x1="130" y1="478" x2="125" y2="543" stroke="#a78bfa" stroke-width="1.5" marker-end="url(#arrowhead)"/>
<line x1="290" y1="478" x2="275" y2="543" stroke="#a78bfa" stroke-width="1.5" marker-end="url(#arrowhead)"/>
<line x1="450" y1="478" x2="425" y2="543" stroke="#a78bfa" stroke-width="1.5" marker-end="url(#arrowhead)"/>
<line x1="610" y1="478" x2="575" y2="543" stroke="#a78bfa" stroke-width="1.5" marker-end="url(#arrowhead)"/>
<!-- Arrows from Repos to SQLite -->
<line x1="190" y1="567" x2="678" y2="580" stroke="#a78bfa" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="340" y1="567" x2="678" y2="585" stroke="#a78bfa" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="490" y1="567" x2="678" y2="590" stroke="#a78bfa" stroke-width="1" stroke-dasharray="3,3"/>
<line x1="640" y1="567" x2="678" y2="595" stroke="#a78bfa" stroke-width="1" stroke-dasharray="3,3"/>
<!-- ===== LEGEND ===== -->
<text x="60" y="680" fill="white" font-size="10" font-weight="600">Leyenda:</text>
<rect x="130" y="672" width="14" height="8" rx="2" fill="rgba(8, 51, 68, 0.4)" stroke="#22d3ee" stroke-width="1"/>
<text x="150" y="680" fill="#94a3b8" font-size="8">Frontend</text>
<rect x="210" y="672" width="14" height="8" rx="2" fill="rgba(6, 78, 59, 0.4)" stroke="#34d399" stroke-width="1"/>
<text x="230" y="680" fill="#94a3b8" font-size="8">API Gateway</text>
<rect x="310" y="672" width="14" height="8" rx="2" fill="rgba(251, 146, 60, 0.3)" stroke="#fb923c" stroke-width="1"/>
<text x="330" y="680" fill="#94a3b8" font-size="8">Servicios</text>
<rect x="400" y="672" width="14" height="8" rx="2" fill="rgba(76, 29, 149, 0.4)" stroke="#a78bfa" stroke-width="1"/>
<text x="420" y="680" fill="#94a3b8" font-size="8">Datos</text>
<rect x="480" y="672" width="14" height="8" rx="2" fill="rgba(251, 191, 36, 0.05)" stroke="#fbbf24" stroke-width="1" stroke-dasharray="4,4"/>
<text x="500" y="680" fill="#94a3b8" font-size="8">Límite de máquina</text>
</svg>
</div>
<!-- Info Cards -->
<div class="cards">
<div class="card">
<div class="card-header">
<div class="card-dot cyan"></div>
<h3>Frontend</h3>
</div>
<ul>
<li>• HTML + CSS + Alpine.js (sin framework pesado)</li>
<li>• Router hash-based para SPA</li>
<li>• Componentes vanilla JS reutilizables</li>
<li>• WCAG 2.1 AA — contraste, teclado, labels</li>
<li>• CSS custom properties para theming</li>
</ul>
</div>
<div class="card">
<div class="card-header">
<div class="card-dot emerald"></div>
<h3>Backend</h3>
</div>
<ul>
<li>• Python 3.11+ con Flask</li>
<li>• API REST JSON sobre localhost</li>
<li>• Blueprint pattern por dominio</li>
<li>• Service layer separada de HTTP</li>
<li>• Schedule library para backups</li>
</ul>
</div>
<div class="card">
<div class="card-header">
<div class="card-dot violet"></div>
<h3>Datos</h3>
</div>
<ul>
<li>• SQLite con WAL mode</li>
<li>• Repository pattern por tabla</li>
<li>• Foreign keys + índices</li>
<li>• Migraciones versionadas</li>
<li>• Backups gzip en %APPDATA%</li>
</ul>
</div>
<div class="card">
<div class="card-header">
<div class="card-dot amber"></div>
<h3>Empaquetado</h3>
</div>
<ul>
<li>• PyInstaller → un único .exe (~30MB)</li>
<li>• Frontend empaquetado en el bundle</li>
<li>• Instalación en perfil de usuario</li>
<li>• Acceso directo en Menú Inicio</li>
<li>• Sin dependencias de runtime</li>
</ul>
</div>
<div class="card">
<div class="card-header">
<div class="card-dot rose"></div>
<h3>Seguridad</h3>
</div>
<ul>
<li>• Servidor solo en 127.0.0.1</li>
<li>• Sin exposición a la red</li>
<li>• Validación en backend (no confiar en frontend)</li>
<li>• Datos en %APPDATA% (aceso restringido)</li>
<li>• Foreign keys para integridad</li>
</ul>
</div>
<div class="card">
<div class="card-header">
<div class="card-dot cyan"></div>
<h3>Modularidad</h3>
</div>
<ul>
<li>• API REST como contrato frontend↔backend</li>
<li>• Frontend reemplazable sin tocar backend</li>
<li>• Plugin architecture para import/export</li>
<li>• Migración a .NET 8 o Go documentada</li>
<li>• Cada service es independiente</li>
</ul>
</div>
</div>
<p class="footer">
Inventschario v1.0.0 — Iteración 1 — 2026-08-03 — Python + Flask + SQLite
</p>
</div>
</body>
</html>