Skip to content

Centralizar la documentación: la landing como fuente única, consumida por el bot de Discord #3

@Saul-Gomez-J

Description

@Saul-Gomez-J

Contexto

La documentación está duplicada y desincronizada entre dos sitios:

  • nan/ (este repo, landing) — fuente de verdad real, contenido correcto y actualizado.
  • nan-discord-bot/ — sirve documentación desde bot/docs/knowledge/, una copia obsoleta y parcial (3 archivos vs 7 páginas en la landing).

Ejemplo de deriva ya en producción:

  • Bot intro.md: "solicita tu API Key al Staff".
  • Landing index.astro: "genera tu API Key desde la sección API Keys de la plataforma".

Objetivo: la landing como fuente única, con un contrato HTTP que el bot (y cualquier futuro cliente) consume sin parsear presentación.


Solución

Migrar el contenido de src/pages/docs/*.astro a Astro Content Collections en src/content/docs/*.md. Exponer dos endpoints públicos en el mismo Worker:

  • GET /api/docs/manifest.json — índice con slug, título, hash SHA-256 y URL de cada archivo.
  • GET /api/docs/{slug}.md — Markdown crudo con frontmatter.

El bot hace pull periódico al manifest, compara hashes con su SQLite (lógica ya existente) y solo re-embebe lo que cambió.

Flujo de datos

        ┌──────────────────────────────────────────┐
        │ nan/ (este repo)                         │
        │                                          │
        │  src/content/docs/                       │
        │   ├── intro.md           ◄── fuente      │
        │   ├── getting-started.md     única       │
        │   ├── api.md                 de verdad   │
        │   ├── models.md                          │
        │   ├── examples.md                        │
        │   ├── agents.md                          │
        │   └── apps.md                            │
        │                                          │
        │   editor → git commit → PR → merge a main│
        └──────────────────┬───────────────────────┘
                           │ wrangler deploy (CI)
                           ▼
   ┌─────────────────────────────────────────────────┐
   │ Cloudflare Worker (nan.builders)                │
   │                                                 │
   │  /docs/[slug]            HTML (Docs.astro)      │
   │  /api/docs/manifest.json JSON índice            │
   │  /api/docs/{slug}.md     Markdown crudo         │
   │                                                 │
   │  Cache API: ETag/Last-Modified por archivo      │
   └─────────┬───────────────────────────┬───────────┘
             │                           │
   navegador │                           │ HTTPS poll (15 min)
             ▼                           ▼
        usuario web              ┌─────────────────────┐
                                 │ nan-discord-bot     │
                                 │                     │
                                 │ DocsClient          │
                                 │  - fetch manifest   │
                                 │  - diff vs hashes   │
                                 │  - fetch .md changed│
                                 │  - re-embed         │
                                 │ SimpleVectorStore   │
                                 └─────────────────────┘

Contratos HTTP

GET /api/docs/manifest.json

{
  "version": "2026-05-23T18:22:11Z",
  "generatedAt": "2026-05-23T18:22:11Z",
  "entries": [
    {
      "slug": "intro",
      "title": "Bienvenido a NaN",
      "description": "Visión general...",
      "order": 0,
      "contentHash": "sha256:9f2a...c4",
      "contentUrl": "/api/docs/intro.md",
      "lastModified": "2026-05-20T09:15:00Z"
    }
  ]
}

GET /api/docs/{slug}.md

Content-Type: text/markdown; charset=utf-8
ETag: "sha256:9f2a...c4"
Last-Modified: Wed, 20 May 2026 09:15:00 GMT
Cache-Control: public, max-age=60, s-maxage=300

---
title: Bienvenido a NaN
description: ...
order: 0
---

# Bienvenido a NaN

(cuerpo Markdown plano)

Reglas:

  • Solo Markdown estándar en el cuerpo.
  • contentHash es del cuerpo (sin frontmatter): cambios cosméticos de frontmatter no provocan re-embed.
  • version del manifest cambia si y solo si algún contentHash cambia.
  • Manifest atómico: nunca 500 parcial; 404 si slug inválido.

Versionado y caché

Capa Mecanismo
Edge (Cloudflare) s-maxage=300 en cuerpos, s-maxage=60 en manifest. Invalidación por Last-Modified.
Bot Persiste manifest.version y contentHash por archivo en doc_hashes. Solo descarga .md cuyo hash cambió.
Resiliencia Bot cachea último manifest + cuerpos en disco. Si la landing falla, sirve la última versión conocida.

Cambios concretos en este repo

Se añade:

  • src/content/config.ts — colección docs con schema (title, description, order).
  • src/content/docs/*.md — 7 archivos extraídos de los .astro actuales.
  • src/pages/docs/[...slug].astro — dynamic route único que renderiza la colección con Docs.astro.
  • src/pages/api/docs/manifest.json.ts — endpoint del índice.
  • src/pages/api/docs/[slug].md.ts — endpoint del cuerpo.

Se elimina:

  • src/pages/docs/{index,getting-started,api,models,examples,agents,apps}.astro.

Se refactoriza:

  • CodeBlock.astro y RateLimits.astro se invocan desde el pipeline de rendering (rehype / mapping de componentes), no inline por página.
  • Docs.astro recibe frontmatter de la entrada de colección en lugar de props sueltas.

Plan de migración por fases (sin downtime)

Fase 0 — Preparación
Extraer prosa de los 7 .astro a .md en src/content/docs/. Implementar [...slug].astro + endpoints. Verificar paridad visual con astro dev. PR coexiste con páginas viejas durante revisión.

Fase 1 — Cutover de la landing
Merge: las .md reemplazan a las .astro en el mismo deploy. Endpoints /api/docs/* empiezan a servir contenido real. El bot sigue con su corpus viejo — no hay desincronización nueva.

Fase 2 — Doble lectura del bot (modo sombra)
Bot con DocsClient habilitado, pero respuestas siguen viniendo de los .md locales. Loguea diferencias contra el corpus remoto. 24–48h de observación.

Fase 3 — Cutover del bot
Bot pasa a alimentarse exclusivamente del DocsClient. Tarea de refresh cada 15 min activa.

Fase 4 — Limpieza
Borrar bot/docs/knowledge/*. Documentar el flujo en ARCHITECTURE.md y README del bot.


Decisiones pendientes

  1. Markdown puro vs MDX en src/content/docs/. Recomendado: Markdown puro (el endpoint sirve exactamente lo que hay en disco, el bot no necesita degradación).
  2. Refresh del bot. Recomendado: pull cada 15 min (sin webhooks ni secrets compartidos).
  3. Fallback offline en el bot. Recomendado: cache en disco; si la landing cae, el bot sigue respondiendo.
  4. /api/docs/* público o autenticado. Recomendado: público (los docs ya lo son en HTML; habilita futuros consumidores).
  5. i18n. Single-locale (es) por ahora o el manifest incluye locale desde ya.

Issue espejo

Crear issue equivalente en nan-discord-bot para la parte del cliente (Fases 2 y 3).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions