0ee9ed65bf
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
56 lines
3.3 KiB
Markdown
56 lines
3.3 KiB
Markdown
# 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 <token>`). 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).
|