# Happy Tails Astro sites and internal CMS This repository backs the production Happy Tails sites at `3031023.xyz` and `blog.3031023.xyz`. Both are static Astro builds deployed as separate Cloudflare Workers. The retained Cloudflare Pages project remains the primary site rollback target. The Decap CMS workflow is operational for internal SloanOps use. It is not a public feature or a client-editing offer. The approved internal editor is `sloanops.admin`; do not invite additional editors without a separate access, support, and security review. ## Repository layout - `sites/www/` contains primary-site Markdown and site data. - `sites/blog/` contains the Journal Markdown and site data. - `public/admin/` contains the shared, no-index Decap admin shell and config. - `public/images/uploads/` is the only CMS-writable media directory. - `sites.json` is the operator-controlled build manifest, not public intake. - `src/` contains shared Astro presentation code. - `schemas/`, `scripts/`, and `fixtures/` enforce the content and deployment contracts. ## Internal CMS workflow Use `https://3031023.xyz/admin/` for normal internal editing. Decap commits content and approved raster media directly to the protected `cms` staging branch; it never writes to `main`. 1. Make and review content changes on `cms`. 2. Confirm the `cms` Worker version previews and content validation results. 3. Open a pull request from `cms` to `main`. 4. Merge only after the required CMS validation, both site builds, and SloanOps review pass. 5. Relevant `main` changes deploy the production primary and/or blog Worker. 6. Synchronize the resulting `main` merge commit back into `cms` through a protected-branch pull request before the next editing cycle. Operator-authored CMS application or configuration work also starts from the current `cms` state. Use a short branch and a pull request into protected `cms`, then promote the reviewed `cms` state to `main`. Direct protected-file pushes to `cms` and all direct pushes to `main` are intentionally rejected. Only the stable CMS and production admin origins are registered with Gitea. Logging in from a branch-specific Worker preview alias therefore produces `Unregistered Redirect URI`; this is expected. Never solve it with a wildcard OAuth redirect or wildcard CORS origin. Use branch previews to inspect rendered site output and an approved admin origin to test editing. See the [CMS operating record](docs/decap-cms-pilot.md) and [Gitea settings](docs/decap-cms-gitea-setup.md) for the complete boundaries. ## Content and media behavior The CMS exposes primary FAQs, services, team members, fixed primary page images, and Journal posts. Draft content is excluded from pages and sitemaps. Journal publish and optional updated dates use Decap's supported `datetime` widget in date-only UTC mode; new posts default the publish date to the current UTC date. CMS image sources must be approved JPG, PNG, WebP, or AVIF files in the flat uploads directory and may be at most 25 MiB. Every build re-encodes public copies to at most 1920 pixels and 500 KiB and strips embedded metadata. ## Local checks Use Node 22.12 or newer within the Node 22 release line. ```bash npm ci npm run verify:cms SITE_KEY=www npm run build:site SITE_KEY=www npm run verify:build SITE_KEY=blog npm run build:site SITE_KEY=blog npm run verify:build ``` Each selected site builds to `dist//`. The Worker configurations use only static assets and a custom 404 page. They contain no custom domains, routes, account IDs, or secrets. `npm run verify:cms` checks the fixed Gitea backend and `cms` branch, all CMS content contracts, media boundaries, the pinned admin shell, supported widgets, and the stable secret-free pull-request status context. ## Preview deployment Non-`main` pushes upload no-index Cloudflare Worker versions with readable branch aliases; they do not move production traffic. Relevant `main` pushes deploy `3031023-xyz-www` and/or `3031023-xyz-blog` automatically after building and verifying the selected site. Routine workflows contain no DNS, custom domain, route, account ID, or token configuration. The production domain bindings, `www` redirect, retained Pages rollback, and recovery steps are recorded in the [primary cutover runbook](docs/cutover-runbook.md) and [blog production runbook](docs/blog-production-runbook.md).