Skip to content

feat(demo-app): demo.sonicjs.com app with auto-reseed#986

Open
lane711 wants to merge 19 commits into
mainfrom
lane711/demo-app-seed-plan
Open

feat(demo-app): demo.sonicjs.com app with auto-reseed#986
lane711 wants to merge 19 commits into
mainfrom
lane711/demo-app-seed-plan

Conversation

@lane711

@lane711 lane711 commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

New workspace app for the public demo at demo.sonicjs.com. Always runs the latest main and wipes + reseeds its data on every deploy and every 2 hours, so visitors can freely edit content, upload media, and explore the admin without permanently changing anything.

What's included

  • 4 code-defined collections (no DB tables): blog_post, page, testimonial, faq — plus sample content and SVG media assets seeded to R2.
  • Demo-login prefill — login page pre-fills admin@sonicjs.com / sonicjs! via the demo-login plugin, hard-gated to ENVIRONMENT=demo.
  • Reset machinerydemo-seed plugin exposes POST /__demo/reseed (bearer-token + env gated) and a 0 */2 * * * cron, both sharing one runReseed (full wipe-all → R2 purge → seed types/media/content → re-activate login).
  • CI.github/workflows/deploy-demo.yml: on push to main, build core → type-check → reseed integration test → migrate → deploy → seed admin → reseed.
  • Localnpm run seed:demo (getPlatformProxy → runReseed).

Tests

  • Real-DB integration demo-app/src/plugins/demo-seed/__tests__/reseed.sqlite.test.ts — better-sqlite3 D1 shim + in-memory R2 stub; asserts summary counts, published-on-create, q_media_* generated columns, full-reset idempotency, and the exact routes/auth.ts demo-login gate query. Runs in CI before deploy.
  • E2E tests/e2e/82-demo-seed.spec.ts — credential prefill, seeded public API, reseed-endpoint auth (skipped unless DEMO_BASE_URL is set).

This integration test caught a real bug: ensureTypes seeded the 4 collection document_types with source:'user', which violates the document_types CHECK (code|plugin|system) — INSERT OR IGNORE silently dropped the rows. Masked in production (bootstrap registers the types first) but broke the fresh-DB fallback. Fixed to source:'system'.

Core changes (outside demo-app)

  • routes/auth.ts — the demo-login gate queried a plugins table that doesn't exist on greenfield (always threw → prefill permanently dead). Repointed at the document-model plugin status (type_id='plugin', data.status='active').
  • index.ts — export MediaDocumentService + MediaUploadMeta so the demo seed can upload media through the document model.

Before first deploy (operator)

Fill REPLACE_WITH_* D1/KV ids in demo-app/wrangler.toml; wrangler secret put BETTER_AUTH_SECRET DEMO_SEED_TOKEN; add CLOUDFLARE_API_TOKEN / CLOUDFLARE_ACCOUNT_ID / DEMO_SEED_TOKEN to GitHub Actions secrets. Steps in demo-app/README.md.

🤖 Generated with Claude Code

lane711 and others added 2 commits June 30, 2026 18:20
New workspace app for the public demo at demo.sonicjs.com. Always runs the
latest main and wipes + reseeds its data on every deploy and every 2 hours,
so visitors can freely edit content without permanently changing anything.

demo-app/:
- 4 code-defined collections (blog_post, page, testimonial, faq) plus sample
  content and SVG media seeded to R2
- demo-login prefill plugin (admin@sonicjs.com / sonicjs!), gated to
  ENVIRONMENT=demo
- demo-seed plugin: POST /__demo/reseed (bearer-token + env gated) and a
  "0 */2 * * *" cron, both sharing runReseed (full wipe-all -> R2 purge ->
  seed types/media/content -> re-activate login)
- scripts/seed-demo.ts for local reseed via getPlatformProxy
- README with operator provisioning + deploy steps

CI: .github/workflows/deploy-demo.yml — on push to main, builds core,
type-checks, applies D1 migrations, deploys, seeds admin, then reseeds.

E2E: tests/e2e/82-demo-seed.spec.ts (DEMO_BASE_URL-gated).

Core changes:
- routes/auth.ts: the demo-login gate queried a `plugins` table that does not
  exist on greenfield (always threw -> prefill permanently dead). Repoint at
  the document-model plugin status (type_id='plugin', data.status='active').
- index.ts: export MediaDocumentService + MediaUploadMeta so the demo seed can
  upload media through the document model.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add demo-app/src/plugins/demo-seed/__tests__/reseed.sqlite.test.ts — real
SQLite coverage for runReseed via core's better-sqlite3 D1 shim + an in-memory
R2 stub (closes the R10 gap; E2E 82 only covers the live path). Asserts summary
counts, published-on-create, q_media_* generated columns, full-reset idempotency
(second run wipes visitor edits and rebuilds 1:1), and the exact document row
routes/auth.ts gates the demo-login prefill on.

The test caught a real bug: ensureTypes seeded the 4 collection document_types
with source:'user', which violates the document_types CHECK
(source IN 'code','plugin','system'). INSERT OR IGNORE then silently dropped
those rows. Masked in production (bootstrap registers the types as 'system'
before reseed runs) but broke the fresh-DB fallback (local npm run seed:demo).
Fixed to source:'system', matching autoRegisterCollectionDocumentTypes.

- demo-app: add vitest devDep + test/test:watch scripts; exclude test files
  from the app tsc program (they are @ts-nocheck and run by vitest, and import
  core's shim from outside rootDir)
- CI: run npm test --workspace=demo-app before deploy in deploy-demo.yml

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lane711 and others added 2 commits July 2, 2026 14:48
Provision sonicjs-demo D1 (dc87c94a), KV (91de57b1), R2
(sonicjs-demo-media) and deploy to Cloudflare Workers.
demo.sonicjs.com custom domain registered; workers.dev live.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without this Better Auth derived base URL from the request, which
caused session cookies to be scoped incorrectly on demo.sonicjs.com.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
lane711 and others added 4 commits July 6, 2026 09:39
…BAC reseed

- Activate mediaPlugin by default in demo-app
- Export renderLoginPage + LoginPageData from @sonicjs-cms/core
- Short-circuit GET/HEAD /auth/login (no query) with a module-init pre-rendered
  constant (LOGIN_HTML) — bypasses entire Hono stack (bootstrap D1 queries,
  Better Auth session lookup, demo-login D1 query). Cold load: 10s → 0.12s.
  Edge-cached via Cache-Control: public, s-maxage=600; version-scoped key
  (_v=SONICJS_VERSION) auto-busts on deploy. Security: never caches
  app.fetch response — would replay session cookies cross-user.
- Add Bootstrap KV fast-path: _bootstrap:v{SONICJS_VERSION} in CACHE_KV;
  subsequent cold starts do 1 KV read instead of 8+ serial D1 queries.
- Add Smart Placement (wrangler.toml) to co-locate Worker with D1.
- Add 28 BLOG_HERO_IMAGES to seed/assets/images.ts; register as media_asset
  documents on every reseed (R2 objects survive reseed under blog/ prefix).
- Add RBAC user role assignments to reseed step 7 so admin/editor can access
  portal after 2h data reset (requireRbac reads rbac_user_roles docs, not
  auth_user.role).
- Add E2E spec 95-login-page-cache.spec.ts pinning no-Set-Cookie, public-
  cacheable, dynamic-fallthrough, and POST end-to-end invariants.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add "Upload blog hero images to R2" step between Worker deploy and
health-check. Loops over www/public/images/blog/*/hero.png and puts each
under blog/<slug>/hero.png in sonicjs-demo-media. Idempotent — re-uploads
keep bucket in sync when images change. Runs before reseed so
mediaSvc.createFromUpload finds objects already in R2.

Also: add www/public/images/blog/** to path filter (image changes trigger
redeploy), bump timeout to 25 min for 28 uploads.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without --remote, wrangler targets the local R2 emulator instead of the
production bucket. CI runners have no local wrangler dev server, so this
would silently succeed locally but fail in CI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…strap

Without defaultActive:true the plugin manifest-registry entry wasn't
picked up by PluginBootstrapService, so no plugins DB row was created
and the admin UI showed Media Manager as uninstalled even though
mediaPlugin was wired in demoPlugins.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
demo-app was added as a workspace but package-lock.json was not
regenerated, causing `npm ci` to fail in CI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The lock file at HEAD was generated with npm 11 (Node 24) and had an
exact pin `"better-sqlite3": "12.11.1"` in the packages/core section,
but packages/core/package.json has the range `^12.10.0`. This
inconsistency caused npm ci (Node 22 / npm 10) to report
`Missing: better-sqlite3@12.11.1 from lock file`.

Regenerated with `npm install --package-lock-only` on Node 22 / npm 10
(same as CI). packages/core section now has `"^12.10.0"` (range),
matching the pattern on main that passes CI.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
npm install --package-lock-only on macOS only includes darwin-arm64
rollup binary. CI (Ubuntu x64) needs @rollup/rollup-linux-x64-gnu.
Copied all 24 missing platform entries from main's lock file (same
rollup@4.62.2 version).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…behavior

Three issues fixed:

1. admin-content-docbacked + api-content-crud: tests expected COUNT=2 after
   publish, but versioning=false causes publish() to delete the old published
   row (pure-history purge at documents.ts:336-345). Correct assertion is 1.

2. media_asset showing in content dropdowns: duplicate registration in
   document-types-seed.ts overwrote the first entry's internal:true flag.
   Removed duplicate; merged public:read and publish grant into the
   canonical first registration.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…post

- Add versioning: true to blog_post document type so publish() preserves
  old versions instead of deleting them (matches main's 6b96827)
- Revert toBe(1) → toBe(2) in admin-content-docbacked and api-content-crud
  integration tests to match versioning=true behavior
- Quarantine 6 email-plugin hook/route tests written for a different API
  design (matches main's 6b96827 quarantine)
- Fix email-service-singleton import path in 5 email plugin test files:
  services/email-service-singleton → services/email/email-service-singleton
  (matches main's 76ffca2)

All 111 test files now pass (7 skipped via quarantine).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Resolve conflicts:
- package-lock.json: take main (CI now uses npm install, not npm ci)
- migrations-bundle.ts: regenerated after taking main's version
- manifest-registry.ts: keep branch timestamp (newer)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ions

admin-layout-catalyst: Users/Plugins/Settings are now admin-only (b5383b9).
Tests using baseData (no user) no longer render them. Add adminData fixture with
role=admin for tests that assert on admin-only menu items.

admin-users-profile: PUT /admin/users/:id now calls RbacService.getRoles() for
dynamic role validation (f8310b2). Add the getRoles mock response at call 0
and update prepare-call count assertion from 2 → 3.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both branches fixed the same test (admin-only menu items need admin user).
Kept main's inline approach, removed redundant adminData fixture.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both branches fixed the same RbacService.getRoles() prepare-call count.
Kept main's comment wording ("Only 3 prepare calls").

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shallow clones (fetch-depth:1) have no HEAD~1. The pull_request_target
branch already fell back to HEAD~1 but without || echo "", causing exit
128 when that also fails. Add 2>/dev/null || echo "" guard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant