CreaRack-SL

Endpoint /api/tools/deps — API REST de Dependencias

Propósito

Endpoint Pages Function (Cloudflare Workers) que expone el estado de frescura de dependencias del sistema: lee la tabla deps_freshness ([[entity—migrations—table—deps-freshness]]) y permite que el colector (scripts/deps_freshness.py en CreaRack-Pro) publique nuevos snapshots vía POST.

Ubicación

  • Archivo: functions/api/tools/deps/index.ts
  • Ruta: /api/tools/deps
  • Entorno: Cloudflare Pages Functions
  • Database: D1 (DB)

Autenticación

  • GET: CF Access vía _middleware.ts (navegador del staff con acceso a Workspace).
  • POST: Bearer token (MCP_TOKENS) vía _middleware.ts (el colector de GitHub Actions).

Métodos

GET /api/tools/deps

Propósito: Obtener el snapshot actual de frescura de dependencias, ordenado por avisos de seguridad primero y luego por mayor desfase.

Response (JSON):

{
  "checked_at": "2026-08-28T13:00:00Z",
  "count": 147,
  "items": [
    {
      "repo": "crearack-pro",
      "ecosystem": "pypi",
      "manifest": "requirements.txt",
      "package": "redis",
      "pinned": "==7.4.1",
      "installed": "7.4.1",
      "latest": "8.2.0",
      "lag_kind": "major",
      "is_dev": 0,
      "vuln_count": 0,
      "vuln_ids": null,
      "vuln_severity": null,
      "manifests": "requirements.txt",
      "resolved": "7.4.1",
      "installed_source": "prod",
      "unbounded": 0,
      "unbounded_major": 0,
      "fix_within_spec": null,
      "hold_reason": "La serie 8.x corta el channel layer en vivo",
      "hold_since": "2026-08-28",
      "hold_ref": "task #204 · redis-py #2807",
      "hold_until": "cuando redis-py #2807 se cierre",
      "checked_at": "2026-08-28T13:00:00Z"
    }
  ]
}

Campos de respuesta:

  • checked_at (ISO 8601): timestamp del snapshot (NULL si tabla vacía).
  • count (int): número total de filas.
  • items (array): ver [[entity—migrations—table—deps-freshness]] para el detalle de cada columna — el SELECT devuelve la fila completa, incluidos los campos de seguridad (0048), del motor fiable (0052) y de retenciones (0053: hold_reason/hold_since/hold_ref/hold_until, no nulo = fila retenida a propósito).

Orden interno (ORDER BY, en este orden): 1) filas con vuln_count > 0 primero, 2) vuln_severity (critical → high → moderate/medium → low → resto), 3) lag_kind (major → minor → patch → unknown → resto), 4) repo, 5) lower(package) (alfabético, sin distinguir mayúsculas). El orden no distingue retenidas — eso lo filtra la UI (pestaña «Retenidas»).

POST /api/tools/deps

Propósito: Reemplazar completamente el snapshot (operación idempotente: DELETE + INSERT batch).

Request (JSON): mismo shape que un item del GET (ver arriba), dentro de {"items": [...]}.

Campos requeridos por fila: repo, ecosystem, manifest, package. Las filas sin alguno de los cuatro se ignoran silenciosamente (continue).

Campos opcionales (admiten null/ausente):

  • pinned, installed, latest.
  • lag_kind: si falta o no está en ['major','minor','patch','uptodate','unknown'] → 'unknown'.
  • is_dev: boolean → 0/1.
  • vuln_count (default 0 si no es un número finito), vuln_ids, vuln_severity (si no está en el set válido → null, no 'unknown').
  • Motor fiable (task #275, payloads viejos entran con todo null/0):
    • manifests / resolved: string no vacía o null (el colector manda '' cuando no hay valor; se normaliza a null).
    • installed_source: si no está en ['prod','runner','lockfile'] → null.
    • unbounded / unbounded_major: boolean → 0/1 (cualquier valor que no sea true literal → 0).
    • fix_within_spec: tri-estado real — solo se guarda 0/1 si el valor entrante es un boolean explícito; cualquier otra cosa (ausente, null, basura) → NULL en BD. Distingue “OSV no publica versión con fix” (NULL) de “hay que cambiar el pin” (0).
  • Retenciones deliberadas (migración 0053, task #275): hold_reason, hold_ref, hold_until — texto no vacío o null (text()). hold_since — solo se guarda si casa con YYYY-MM-DD (isoDay()); cualquier otro formato → null. Fila retenida = hold_reason no nulo; los otros tres son contexto opcional. Un payload viejo sin estos cuatro campos entra con todos a null (fila no retenida).

Response (JSON): {"inserted": N, "checked_at": "..."}. 400 si body.items no es array, o si trae más de 5000 filas (cap de tamaño, WS6 B5: el snapshot real ronda unos cientos de paquetes, así que 5000 es margen amplio y evita un batch D1 desmesurado — DELETE + N INSERT atómicos — si llegara un body gigante).

Comportamiento: DELETE FROM deps_freshness → INSERT batch (env.DB.batch()) → responde inserts + timestamp.

Dependencias

  • D1: tabla deps_freshness — ver [[entity—migrations—table—deps-freshness]] (creada en 0042, ampliada en 0048, 0052 y 0053).
  • _middleware.ts: valida auth (CF Access o Bearer token).

Consumidores

  • Navegador: DepsPanel.tsx (React) en GET, badges de seguridad/fuente/techo/retención delegados a DepsBadges.tsx, y el desplegable de ayuda a DepsExplainer.tsx.
  • Página: /tools/deps (src/pages/tools/deps/index.astro) monta DepsPanel como isla React.
  • Hub de tools: ToolsHub.tsx llama también al GET y agrega los counts (total de filas y cuántas están desactualizadas según lag_kind, excluyendo las retenidas) para la tarjeta de /tools.
  • Colector: scripts/deps_freshness.py (CreaRack-Pro) en POST para publicar snapshot semanal. Desde v2.0 obtiene installed_source: "prod" consultando [[entity—api—endpoint—installed-packages]]; si no responde, cae a "runner". Desde v1.86.10, scripts/deps_holds.py rellena los cuatro campos hold_* contra deps_holds.json antes de publicar.

Notas de diseño

  • Snapshot, no histórico: el POST reemplaza completamente el estado. No hay histórico de cambios.
  • Batch atómico: todas las inserciones ocurren en una sola transacción.
  • Tolerancia a datos parciales: un item sin versión final o sin los campos del motor fiable/retenciones no rompe el endpoint; entra con NULL/0 y la UI lo trata como “sin dato” o “no retenida”.
  • installed_source y lag_kind se validan en el handler, no con CHECK en la migración (SQLite/D1 no lo aplica de forma fiable en un ALTER TABLE ADD COLUMN).

Véase también

  • [[entity—migrations—table—deps-freshness]]
  • [[feature—workspace—tool-dependencias]]
  • [[runbook—workspace-tech—tool-dependencias]]
  • [[concept—saas—observability]]
  • [[entity—api—endpoint—installed-packages]]