Una API de custodia documental que no confía en nadie por defecto
Sistemas externos se autentican, suben documentos, consultan su metadata y los descargan de forma protegida. Cada operación queda auditada. Este es el recorrido técnico completo.
Sistemas externos se autentican, suben documentos, consultan su metadata y los descargan de forma protegida. Cada operación queda auditada. Este es el recorrido técnico completo.
/api/v1/login, tokens temporales/api/*{{token}} soloLaravel 13 (PHP 8.3+), Sanctum para tokens, MariaDB. API-first: sin backend de gestión con interfaz gráfica salvo la demo técnica.
Autenticable vía Sanctum. uuid, abilities, allowed_ips, is_active, soft deletes. Tiene muchos StoredFile.
uuid público, path interno nunca expuesto, estado del ciclo de vida, scan_status, analysis_status, sha256_hash, soft deletes.
Auditoría de solo inserción: acción, IP, user agent, request_id, status_code, metadata. Pertenece a un StoredFile.
Permisos granulares por entity_type / entity_id: can_upload, can_read, can_list, can_download, can_delete.
Rechaza si el consumidor está inactivo aunque el token siga siendo válido.
Exige IP en whitelist si FILE_API_REQUIRE_IP_WHITELIST=true.
Fuerza Accept JSON y añade un request_id de correlación a cada petición.
MIME real (no solo extensión), coherencia extensión/MIME, tamaño máximo, extensiones bloqueadas siempre (.exe, .php, .sh...), ruta interna por UUID sin caracteres peligrosos. La respuesta nunca incluye el path de storage.
Esperando antivirus, si está activo.
Esperando análisis documental, si está activo.
Único estado desde el que StoredFilePolicy::download() autoriza.
Antivirus detectó infección. Descarga bloqueada.
No superó la validación de subida.
Soft delete lógico; purga física vía comando aparte.
AntivirusScannerService y ScanUploadedFileJob están implementados y testeados, pero sin un proveedor real (ClamAV u otro) conectado en esta fase — Se contempla una segunda versión con la integración de los proveedores.
FILE_API_SCAN_UPLOADS=false: el fichero pasa a available directo, sin cola.
Fail-closed deliberado: el fichero queda en pending_scan para siempre en vez de exponerse sin escanear.
Distinto del antivirus: no busca malware, intenta reconocer el tipo de documento (factura, contrato, nómina...) y marcar si requiere revisión humana. Stub listo, sin proveedor real, FILE_API_ANALYSIS_ENABLED=false por defecto.
Nunca expone path, disk, stored_name, sha256_hash ni api_consumer_id. Solo campos seguros.
GET /entities/{type}/{id}/files filtra resultados dentro del controller: sin permiso, responde 200 con lista vacía, no 403 — para no confirmar que existen ficheros ajenos.
EnsureTokenCanAccessEntity (alias api.consumer.entity:{columna}) queda disponible para futuras rutas que sí deban bloquear con 403 en vez de filtrar en silencio.
Login, logout, subida, descarga, borrado, tokens, escaneo, análisis y reanálisis se auditan en FileAccessLog vía FileAuditService: acción, IP, user agent, request_id, status_code y mensaje.
| Consulta | Quién puede verla |
|---|---|
| GET /files/{uuid}/audit-logs | Solo el propietario del documento (policy viewAudit), paginado, orden cronológico inverso |
| Comando | Uso |
|---|---|
| api-consumer:create | Crea consumidor con credenciales y abilities |
| api-consumer:rotate-token {uuid} | Revoca todos los tokens y emite uno nuevo |
| api-consumer:revoke-token {id} | Revoca un token puntual, pide confirmación |
| files:purge-deleted --days --dry-run | Purga física tras el periodo de retención |
| files:scan-pending | Procesa la cola de antivirus pendiente |
| files:analyze-pending | Procesa la cola de análisis pendiente |
| demo:create-consumer --force | Crea o rota el consumidor aislado de la demo técnica |
| Método | Ruta | Ability |
|---|---|---|
| POST | /login | público |
| POST | /logout | token válido |
| POST | /files | files:upload |
| POST | /files/batch | files:upload |
| GET | /files | files:list |
| GET | /files/{uuid} | files:read |
| GET | /files/{uuid}/download | files:download |
| POST | /files/{uuid}/reanalyze | files:reanalyze |
| GET | /files/{uuid}/audit-logs | files:audit |
| DELETE | /files/{uuid} | files:delete |
| GET | /entities/{type}/{id}/files | files:list |
Feature tests por endpoint (auth, subida, descarga, listado por entidad, análisis, seguridad entre consumidores, demo) y Unit tests de servicios (FilePathBuilder, MimeValidationService, AntivirusScannerService, DocumentAnalysisResultNormalizer). Factories dedicadas para ApiConsumer y StoredFile.
| # | Fase | Estado |
|---|---|---|
| 1 | Instalación y estructura base | completada |
| 2 | Modelos y migraciones | completada |
| 3 | Autenticación de consumidores | completada |
| 4 | Subida segura de ficheros | completada |
| 5 | Antivirus y gestión de estados | pospuesta (stub) |
| 6 | Descarga protegida y metadata | completada |
| 7 | Listado por entidad y auditoría | completada |
| 8 | Análisis documental automatizado | pospuesta (stub) |
| 9 | Comandos Artisan de gestión | completada |
| 10 | Tests automatizados | completada |
| 11 | Documentación API y Postman | completada |
| 12 | Demo técnica | completada |
| 13 | Presentación técnica del proyecto | completada |
| 14 | Índice de documentación | completada |
docs/openapi.yaml · Swagger UI en /api/documentation · Colección Postman con {{token}} y {{file_uuid}} automáticos
docs/api.md (uso para terceros) · docs/despliegue.md (servidor remoto) · README.md (instalación y comandos)
AGENTS.md y docs/technical/{PROJECT_CONTEXT,PIPELINE_RULES,KEY_FILES}.md
Pantalla /demo: login, subida, metadata, descarga y auditoría consumiendo la API real desde el navegador
La Fase 14 centraliza en README.md los enlaces a toda la documentación ampliada — API, despliegue, esta presentación, Swagger, Postman y docs técnicos — para que cualquiera nuevo en el proyecto sepa dónde mirar sin explorar el repo a mano. Con eso, el ciclo de documentación queda cerrado.