Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

32 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Editor de contenidos (parroquia-editor)

Editor web schema-driven para el contenido gestionado por parroquia-config-api (el Worker + bucket R2). El formulario no está hardcodeado: se construye en tiempo de ejecución a partir de un pages.yml que se descarga desde una URL configurable, así que si el esquema cambia, el editor cambia con él sin tocar código.

Arranque rápido

npm install
npm run dev      # http://localhost:5173
npm run build    # genera docs/.vitepress/dist (estático, se puede subir a cualquier hosting)

Al abrir el editor te pedirá:

  1. Token de escritura del sitio (obligatorio).
  2. En "Opciones avanzadas": la URL del worker (API), la URL pública de lectura de datos, y la URL del pages.yml. Se guardan en localStorage para no tener que repetirlas cada vez (igual de sensibles que el propio token, que también se guarda ahí).

El editor llama a GET /whoami con el token para resolver el :slug automáticamente — no hace falta que el usuario lo escriba.

Cambios necesarios en el Worker

El worker original (worker/index.js en tu proyecto) no tenía forma de resolver token -> slug, así que se le ha añadido un único endpoint nuevo:

GET /whoami   (bearer token)  ->  { "slug": "..." }

Es exactamente la misma búsqueda que ya hace authorize() en auth.json, solo que sin comparar contra un slug conocido de antemano. No cambia nada del modelo de seguridad existente (tokens opacos, invarianza de no-decodificación en el servidor, etc.).

Como me indicaste que la lectura de ficheros se sirve públicamente desde data.parroquia.app (o el host que configures), el editor no llama al worker para leer contenido — solo para /whoami, /sites/:slug/list y el PUT de escritura. El fichero worker/index.js incluido aquí refleja eso (sin ninguna ruta GET /sites/:slug/:token, tal y como estaba el original). El único añadido real es /whoami. Está marcado con EDITOR PATCH en los comentarios para que sea fácil de localizar y aplicar a tu despliegue.

worker/migrate.js se incluye sin cambios, tal cual lo pasaste, solo como referencia de la codificación de tokens (el editor la reimplementa en docs/.vitepress/theme/lib/codec.js, byte a byte idéntica).

Arquitectura del editor

docs/.vitepress/theme/
  lib/
    codec.js          encode/validación de filename plano (charset url-safe; / → -; idéntico a migrate.js)
    frontmatter.js     parseo/serialización YAML frontmatter para .md
    schema.js           normaliza pages.yml: resuelve `component:`, calcula
                        valores por defecto, interpola resúmenes collapsible
    content-index.js    pages.yml + lista de tokens -> lista ordenada de
                        ficheros editables, índice de referencias, índice
                        de medios
    api.js               llamadas HTTP (worker + host público de datos)
    store.js             estado reactivo global: sesión, fichero abierto,
                        dirty-tracking, autosave a localStorage, guardar

  components/
    LoginView.vue        pantalla de login
    FileBrowser.vue       barra lateral, agrupada y ordenada según pages.yml
    FieldsGroup.vue       pinta una lista de campos contra un objeto
    FieldRenderer.vue      dispatch de un campo: object / object-list /
                        block (lista polimórfica) / lista de escalares / hoja
    ScalarInput.vue        tipos hoja: string, text, rich-text, number,
                        boolean, date, select, image, reference
    SelectField.vue        select simple/múltiple, con o sin "creatable"
    ImagePickerModal.vue   selector de imágenes existentes + subida
    EditorApp.vue          layout raíz (login o editor)

Cómo se resuelve component:

Cualquier campo o variante de bloque con component: <nombre> hereda type, options, fields o blocks del componente referenciado en components:; sus propias claves (label, list, hidden, default, fields...) siempre tienen prioridad. Esto cubre tanto el uso como "atajo de tipo" (tags, font, color...) como el uso como "objeto reutilizable" (hero usado dentro de blocks:).

Tipos soportados

string, text, rich-text (editor enriquecido básico basado en contenteditable, no un editor WYSIWYG completo), number, boolean, date, select (valores string u objetos {value,label}, simple/múltiple, con o sin creatable), image (simple/múltiple, selector + subida), object (simple o repetible, con resumen collapsible interpolado tipo {fields.title}), block (lista polimórfica: cada elemento recuerda su variante bajo la clave type), reference (simple/múltiple, apuntando a otra collection por su nombre de fichero decodificado), y listas simples de escalares (list: true / list: { collapsible: {...} } sobre string, number, date, etc).

Cómo se listan/ordenan los ficheros

Se sigue estrictamente el orden de content: en pages.yml, nunca el orden en que el bucket devuelve los tokens. Para type: collection, los ficheros se ordenan alfabéticamente por su ruta decodificada dentro de esa carpeta. Al usuario se le muestra siempre el nombre decodificado, nunca el token (filename plano codificado). Los ficheros de media no aparecen en este listado principal (solo son accesibles vía el selector de imágenes), y ningún fichero fuera del esquema se ofrece para editar.

Guardado local y botón "Guardar"

Cada cambio se serializa (JSON con indentado, o YAML frontmatter para .md) y se guarda en localStorage con un pequeño debounce, bajo una clave por sitio+fichero. El botón "Guardar cambios" solo se resalta cuando el contenido serializado difiere del último snapshot remoto conocido. Al guardar, se hace PUT al worker con el token de escritura y se limpia el borrador local (queda como nuevo "guardado remoto" de referencia).

Si vuelves a abrir un fichero con cambios sin guardar pendientes, el editor te avisa y los recupera automáticametne desde localStorage.

Limitaciones conocidas / decisiones a revisar

  • reference: la etiqueta mostrada es el nombre de fichero decodificado (sin extensión), no el title real del documento referenciado — cargar el title de cada elemento de la colección exigiría descargar todos los ficheros de esa colección solo para construir la lista, así que se ha dejado así por coste. Si quieres el título real, es un cambio localizado en buildCollectionRefIndex (content-index.js): habría que hacer fetch de cada fichero de la colección y leer su campo primary.
  • rich-text usa document.execCommand, que está obsoleto pero ampliamente soportado; es intencionadamente básico (negrita, cursiva, listas, enlace, limpiar formato). Si el HTML resultante necesita ser más controlado, esto es candidato a sustituirse por un editor real (Tiptap, ProseMirror...).
  • Reordenar listas/bloques usa botones ↑/↓, no drag-and-drop.
  • Clave discriminadora de bloques: cada elemento de un campo block se guarda como { type: "<nombre-del-bloque>", ...campos }. Si tu generador estático espera otra convención, es una única constante (BLOCK_TYPE_KEY en lib/schema.js).
  • El endpoint /whoami es nuevo: hay que desplegar el worker parcheado (worker/index.js) antes de que el login funcione contra producción.
  • No se ha podido probar contra el Worker/API reales (no hay red disponible hacia Cloudflare desde este entorno) — sí se ha verificado que npm run build compila sin errores. Conviene hacer una primera prueba manual con un sitio de test antes de usarlo en producción.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages