Skip to content

[WIP] Common Content Model#112

Closed
michael wants to merge 144 commits into
mainfrom
ccm
Closed

[WIP] Common Content Model#112
michael wants to merge 144 commits into
mainfrom
ccm

Conversation

@michael

@michael michael commented Jun 1, 2026

Copy link
Copy Markdown
Owner

No description provided.

@vercel

vercel Bot commented Jun 1, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
editable-website Ready Ready Preview, Comment Jul 10, 2026 12:02pm

michael added 25 commits July 9, 2026 20:20
This live-updates the favicon after you changed and saved page.image on
the home page.
- First 5 failed logins are free, then each failure locks login
  for 30s, doubling per failure, capped at 1 hour (~24 guesses/day)
- Lockout is checked before the password, and a successful login
  resets the counter
- Throttle state persists as a login_throttle key in site_settings,
  so restarts don't reset it (global lock — there is only one admin)
- Password comparison now uses crypto.timingSafeEqual over SHA-256
  digests instead of !==
- Asset responses now send Content-Security-Policy: sandbox and
  X-Content-Type-Options: nosniff, so an uploaded SVG opened directly
  can't run scripts against the site origin
- The upload endpoint verifies that the stored bytes hash to the
  claimed X-Content-Hash, enforcing the content-addressing invariant
  (asset id = SHA-256 of stored bytes)
- The client now hashes the re-encoded blob it actually uploads,
  instead of the original file, so raster asset ids match their
  stored bytes
- Failed or empty writes clean up partial files, including variants,
  which previously could leave truncated files being served
- Size limiting stays with the deployment's BODY_SIZE_LIMIT — no
  second application-level limit
Replace the four hand-rolled BEGIN/COMMIT/ROLLBACK blocks with a
single with_transaction(fn) in db.js:
- save_document, delete_page and update_page_slug in api.remote.js
  now wrap their write logic in the helper; the sql template tag
  existed only for the transaction statements and is gone
- update_page_slug returns the new slug from the transaction callback
  instead of mutating an outer variable across the try boundary
- migrate.js uses the helper too; migrations now run under
  BEGIN IMMEDIATE instead of plain BEGIN (stronger, and equivalent
  in practice since migrations run single-threaded at startup)
Stability pass: one source of truth for asset format knowledge, and
real HTTP statuses on client-actionable API failures.

- UPLOAD_MIME_TO_EXT and EXT_TO_MIME now live in config.js; the
  upload endpoint, serving route (incl. the variant original lookup)
  and the client's content-type selection all derive from them
- page_metadata.js reuses the shared ASSET_ID_REGEX instead of its
  own copy
- get_document responds 404 for unknown slugs, delete_page 400/404,
  save_document 409 on duplicate create; internal invariant
  violations intentionally remain plain errors (500)
The handle hook previously wrote to the sessions table and set a
fresh cookie on every request carrying a valid admin session —
including each asset request on a page load. The last renewal time
is derivable from the stored expiry (expires - session_duration), so
sliding renewal now only fires when it's older than a day. Session
lifetime behavior is unchanged apart from expiring at most a day earlier
in the theoretical worst case.
The orphan GC kept unreferenced assets for a grace period measured
from upload time (file mtime), so a long-lived asset removed from a
page was purged by the very next save. The grace clock now starts
when an asset loses its last reference: save_document and delete_page
capture the referenced set before their transaction, and assets that
dropped out get their mtime touched before the GC pass.

- mtime now means "uploaded or last dereferenced, whichever is later"
- grace period is env-configurable via ASSET_GRACE_PERIOD_DAYS
  (default 7) and documented in the README: it is the safe window for
  rolling back a database backup against the live assets folder
- undoing an image removal and saving within the window re-references
  the still-present file, healing the undo-after-save edge case
@michael

michael commented Jul 10, 2026

Copy link
Copy Markdown
Owner Author

Close in favor of #134

@michael michael closed this Jul 10, 2026
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.

2 participants