# Conectores de datos externos — contrato v1 El sistema **no se conecta directo** a las bases de datos de los clientes. En cada servidor origen se instala un **conector**: una API HTTP de solo lectura que expone tablas elegidas. El sistema habla siempre el mismo contrato, sin importar la tecnología de origen (FoxPro, SQL Server, Excel…). | Conector | Carpeta | Estado | |---|---|---| | FoxPro (DBF/FPT/CDX) | `foxpro/` | 1.1.0 | ## Reglas que todo conector debe cumplir 1. **Solo lectura.** Únicamente `GET`; cualquier otro método → `405`. Nunca escribe en el origen. 2. **Token Bearer** (`Authorization: Bearer `). El conector guarda solo el SHA-256; el sistema guarda el token cifrado (AES-256-GCM). Se admiten varios hashes para rotar sin cortar. 3. **Lista de IPs permitidas** (backend del sistema), en el conector y en el firewall del servidor. 4. **El administrador del origen decide qué sale:** tablas permitidas, campos permitidos y campos denegados (sueldos, claves…). Lo no permitido no existe para el sistema (`404`/`400`). 5. **Sin SQL ni rutas libres:** tablas y campos se validan contra la lista real del origen. 6. **Protege al origen:** límite de peticiones por minuto, lecturas simultáneas, tamaño de página y tiempo máximo por petición. Responde `429`/`503` en vez de cargar el servidor. 7. **Log de acceso** sin tokens ni valores buscados (pueden ser datos personales). ## Endpoints | Método y ruta | Respuesta | |---|---| | `GET /v1/health` | `{ ok, connector, contract: "v1", version, time }` | | `GET /v1/sources` | `{ sources: [{ id, name }] }` — carpetas/bases configuradas | | `GET /v1/sources/:source/tables` | `{ tables: [{ name, records, fields, version, modifiedAt }] }` | | `GET /v1/sources/:source/tables/:table/schema` | `{ table, records, version, modifiedAt, fields: [{ name, type, size, decimals }] }` | | `GET /v1/sources/:source/tables/:table/rows` | `{ table, version, modifiedAt, maxRecno, total, offset, limit, fields, rows }` | Parámetros de `rows`: - `fields=A,B` columnas a devolver (por defecto todas las permitidas). - `limit` (1…máximo del conector, def. 100) y `offset` para paginar. - `q=texto` y `qFields=A,B`: contiene, sin distinguir mayúsculas ni tildes. - `eq.CAMPO=valor`: igualdad exacta (sin distinguir mayúsculas). Se pueden combinar varios. - `includeDeleted=1`: incluye los registros marcados como borrados (`_deleted`). - Sincronización incremental (v1.1): `afterRecno=N` devuelve solo lo agregado después del registro N; `changedField=CAMPO&changedSince=valor` solo lo modificado después de ese valor (campo de fecha/hora del origen, comparado tal como lo devuelve el conector). Con ambos, basta cumplir uno. La respuesta trae `maxRecno` para guardar como próximo `afterRecno`. Una sincronización completa periódica recoge los borrados. Cada fila trae `_recno` (posición en el origen). `version` cambia cuando el origen se modifica: el sistema la compara para saber si hace falta volver a sincronizar. Tipos de dato en JSON: texto recortado (vacío → `null`), números, booleanos, fecha `YYYY-MM-DD`, fecha-hora ISO 8601. Errores: `{ error: "mensaje" }` con `400` parámetro inválido, `401` token, `403` IP, `404` fuente/tabla, `405` método, `423` tabla bloqueada en el origen, `429` exceso de peticiones, `503` conector ocupado, `500` error interno (detalle solo en el log del conector).