apiVersion: capsule.dev/v0.1 kind: Capsule name: yingjieli-content-store version: 1.0.0 type: subsystem domain: yingjieli.site maintainers: - name: Quake email: quake0day@gmail.com purpose: summary: | Single source of truth for all editable site content of yingjieliartist.com: hero, bio, exhibitions, contact, and the works catalog. Backed by Cloudflare KV (binding YL_DATA) with an in-code DEFAULT_DATA fallback for first boot. The DEFAULT_DATA value is injected at reconstruct time from site-data.json, so the same capsule can power any artist's portfolio. owns: - the schema of the site-content blob (hero, bio, exhibitions, contact, works[]) - GET /api/data (public read, 30s edge cache + stale-while-revalidate) - PUT /api/data (admin-only full replace) - POST /api/data?seed=1 (admin-only reset to DEFAULT_DATA) - the next-work-number helper (zero-padded 3-digit num) does_not_own: - storage of image bytes (see yingjieli-image-store) - who is allowed to write (see yingjieli-admin-auth) - the values inside DEFAULT_DATA (those come from site-data.json at reconstruct) interfaces: provides: - kind: http_api name: data-read entrypoint: src/api/data.js description: GET /api/data → full content JSON. Public. - kind: http_api name: data-write entrypoint: src/api/data.js description: PUT /api/data → replace full content blob. Admin only. - kind: library name: data-helpers entrypoint: src/_lib/data.js description: readData, writeData, nextWorkNum, DEFAULT_DATA. requires: - kind: library name: auth-helpers from_capsule: yingjieli-admin-auth description: isAuthed(request, env) is called for PUT and seed. - kind: env name: YL_DATA description: Cloudflare KV namespace binding. dependencies: capsules: - name: yingjieli-admin-auth version: ">=1.0.0 <2.0.0" runtime: - node: ">=18" - cloudflare-pages: "*" agent: summary_for_ai: | Single source of truth for editable content. Everything visible on the public site that an admin can edit lives in one JSON blob at KV key site:data:v1. The blob has a fixed top-level shape — hero, bio, exhibitions, contact, works — and PUT validates that shape exists before writing. avoid: - Sharding the content blob across multiple KV keys. - Reading or writing KV from anywhere outside this capsule's helpers. - Storing raw image bytes in this blob; only filename references. verification: health_checks: - id: data-lib-syntax-valid command: node --check src/_lib/data.js - id: data-api-syntax-valid command: node --check src/api/data.js invariants: - The content blob always has exactly these top-level keys; PUT rejects anything missing. - DEFAULT_DATA is what an empty KV returns — there is no other fallback path. - "Image filenames in `works[].file` reference keys served by yingjieli-image-store." compatibility: tested_with: - capsule: yingjieli-admin-auth versions: ">=1.0.0 <2.0.0" x-reconstruct: install: install.json notes: | DEFAULT_DATA is templated. install.json's data_injections directive tells the reconstructor to replace `/* @CAPSULE_DATA */ {}` in the generated _lib/data.js with the JSON contents of the data input (e.g. site-data.json).