# API REST — Art4Hotel Hub Servidor: `server.py` (Python stdlib `http.server`), puerto **4401**. Respuestas en JSON. Autenticación por **cookie de sesión** (firmada HMAC). --- ## Autenticación Todas las rutas requieren sesión **excepto** las públicas de auth. Sin sesión: las páginas HTML muestran el login; las rutas `/api/*` devuelven `401`. | Método | Ruta | Descripción | |---|---|---| | GET | `/api/needs-setup` | ¿Primera vez? → `{needs_setup: bool}` (pública) | | POST | `/api/setup` | Crea la cuenta admin inicial (solo si no hay usuarios). Body: `{username, password, nombre}` | | POST | `/api/login` | Inicia sesión. Body: `{username, password}` → set-cookie de sesión | | POST | `/api/logout` | Cierra sesión (borra cookie) | - Contraseñas: hash **PBKDF2-SHA256** (200k iteraciones + salt). - Sesión: cookie `a4h_session` firmada con HMAC (clave en `secret.key`), válida 14 días. --- ## CRUD genérico Un solo handler maneja todas las tablas vía el dict `TABLES` en `server.py`: | Método | Ruta | Descripción | |---|---|---| | GET | `/api/{tabla}` | Lista todos los registros | | POST | `/api/{tabla}` | Crea un registro (body = campos) | | PUT | `/api/{tabla}/{id}` | Actualiza un registro | | DELETE | `/api/{tabla}/{id}` | Elimina un registro | Tablas disponibles: `ordenes`, `oc`, `productos`, `proyectos`, `propuestas`, `catalogos`, `clientes`, `trabajos`, `modelos`, `materiales`, `inventario`, `compras`, `tareas`, `bitacora`. El dict define por tabla: `fields`, `int_fields`, `float_fields`, `nullable_fields` (para casteo/validación automática). --- ## Endpoints especiales | Método | Ruta | Descripción | |---|---|---| | GET | `/api/dashboard` | KPIs del dashboard (stages, clientes activos, alertas) | | GET | `/api/ventas` | Analítica de ventas: comparativo mensual, top clientes, tiempos de ciclo, pricing por producto | | GET | `/api/entregas` | Pedidos entregados (agrupables por fecha/cliente/OC) | | GET | `/api/oc` | OCs con sus líneas (`lineas`), totales y `progress` agregado | | POST | `/api/oc-split/{oc_id}` | Entrega parcial: crea OC hermana con pedidos seleccionados | | GET | `/api/file-counts` | `{entidad: {count, first_image}}` para TODAS las carpetas en 1 request. `first_image` prefiere fotos y excluye documentos | | POST | `/api/upload/{id}?tipo=X&label=Y` | Sube archivo(s). `tipo` define el prefijo; `label` opcional como parte descriptiva del nombre | | GET | `/api/files/{id}` | Lista archivos de una entidad | | DELETE | `/api/files/{id}/{nombre}` | Elimina un archivo | | POST | `/api/backup-now` | Dispara el respaldo en segundo plano | | GET | `/api/backups` | Lista los respaldos disponibles | --- ## Servir contenido | Ruta | Sirve | |---|---| | `/` o `/index.html` | La SPA (frontend completo) | | `/uploads/{entidad}/{archivo}` | Archivos subidos (requiere sesión) | --- ## Notas de implementación - **IDs legibles** (orden_id, oc_id) se generan con `MAX(num)+1`, no count — evita colisiones tras borrados. - **Validación de unicidad** en `orden_id` al insertar (devuelve 409 si existe). - **CORS**: headers `Access-Control-Allow-*` habilitados; `OPTIONS` responde preflight. - Sin dependencias externas: todo con `http.server`, `sqlite3`, `json`, `urllib`, `hashlib`, `hmac` (stdlib).