apiVersion: capsule.dev/v0.1 kind: Capsule name: yingjieli-admin-ui version: 1.0.0 type: subsystem domain: yingjieli.site maintainers: - name: Quake email: quake0day@gmail.com purpose: summary: | Password-protected single-page admin panel for editing all site content: hero, bio, exhibitions, contact, and the works catalogue (with client-side image resize before upload). owns: - admin/index.html (admin SPA shell) - admin/admin.js (login flow, editors, client-side image resize, save) - admin/admin.css (admin-only styling) - the editing UX (what fields are exposed, save semantics) does_not_own: - session validation (calls /api/auth, lets the server decide) - content schema (consumes the shape returned by /api/data) - image upload mechanics (POSTs to /api/upload; doesn't talk to R2) interfaces: provides: - kind: http_api name: admin-root entrypoint: src/admin/index.html requires: - kind: http_api name: auth-login from_capsule: yingjieli-admin-auth - kind: http_api name: auth-status from_capsule: yingjieli-admin-auth - kind: http_api name: data-read from_capsule: yingjieli-content-store - kind: http_api name: data-write from_capsule: yingjieli-content-store - kind: http_api name: image-upload from_capsule: yingjieli-image-store dependencies: capsules: - name: yingjieli-admin-auth version: ">=1.0.0 <2.0.0" - name: yingjieli-content-store version: ">=1.0.0 <2.0.0" - name: yingjieli-image-store version: ">=1.0.0 <2.0.0" runtime: - cloudflare-pages: "*" agent: summary_for_ai: | Single-page admin protected by password login. On load calls GET /api/auth — if not authenticated, shows login box; otherwise the editor. Every save is a full PUT /api/data with the entire blob (no partial updates). Images are resized client-side BEFORE upload. avoid: - Sending partial updates (the API only accepts full-blob PUT). - Decoding the yl_admin cookie or reading SESSION_SECRET in the browser. - Letting the user pick the final image filename — the server generates it. verification: health_checks: - id: admin-html-present command: node -e "require('fs').statSync('src/admin/index.html')" - id: admin-js-present command: node -e "require('fs').statSync('src/admin/admin.js')" invariants: - The admin UI never bundles ADMIN_PASSWORD or SESSION_SECRET into client code. - The admin UI calls /api/auth GET before showing the editor. - Saving from the admin UI always replaces the full content blob, never partial. x-reconstruct: install: install.json