Commit 109d047
feat(v2.0a): forms — first slice of v2.0 engagement (#43)
This is the first of four v2.0 PRs. Adds public-submittable forms
with a CMS editor and an in-CMS moderation inbox.
Schema (Drizzle migration 0007)
-------------------------------
- forms: id, key (URL-safe, unique), label, fields (JSON array of
FormField), enabled (bool), success_messages (per-locale JSON),
created_by (FK users SET NULL), createdAt, updatedAt.
- form_submissions: id, form_id (FK CASCADE), data (JSON), ip_hash
(truncated SHA-256, 16 chars — never raw IP), status enum
(new/read/spam/archived), note, submittedAt.
Provider
--------
- ContentProvider gains 11 new methods: list/get/getByKey/create/
update/delete for forms, list/get/create/update/delete for
submissions, plus countRecentSubmissions for rate limiting.
- D1 implementation in providers/d1.ts.
Public endpoint (POST /api/forms/[key])
---------------------------------------
- Reads the form by key. Returns 404 if missing, 410 if disabled.
- Parses multipart/url-encoded body.
- Validates against form.fields:
- honeypot (`_hp` field, expected empty),
- per-field required + maxLength,
- email kind: cheap regex `.+@.+\..+`,
- checkbox: required-checkbox is GDPR consent pattern.
- Rate limit: 3 submissions per minute per (form, ipHash).
IP is hashed via SHA-256 truncated to 16 hex chars; raw IP is
never stored.
- Audit: writes a form.submit row with actorId=null (public).
CMS UI
------
- /cms/forms — list table with label, public endpoint URL, field
count, enabled badge.
- /cms/forms/new + /cms/forms/[id] — shared FormEditor.svelte.
Field list with add (text/email/textarea/checkbox), reorder
(up/down), remove, per-field name + label + required toggle.
- /cms/forms/[id] also embeds a submissions inbox underneath the
editor: collapsible rows with status badges, mark-as
(new/read/spam/archived), delete. Submissions are scoped to the
current form via a defense-in-depth check on every action.
- Sidebar gets a new "Forms" entry under the taxonomy group, gated
to editor+.
Audit
-----
- New AuditAction members: form.create / form.update / form.delete /
form.submit. Wired into create / update / delete actions and the
public submission endpoint.
i18n: 25 new cms_forms_* keys (EN + TH).
Migration 0007 already applied to live D1.
This PR doesn't touch the public site rendering — adding a
\<form\> tag to a page or article is the editor's job. Future v2.x
might ship a markdown shortcode that renders a form inline; out of
scope here.
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>1 parent 68b91a5 commit 109d047
19 files changed
Lines changed: 3375 additions & 1 deletion
File tree
- drizzle
- meta
- messages
- src
- lib
- components/cms
- server
- audit
- content
- providers
- forms
- routes
- (cms)/cms/forms
- [id]
- new
- api/forms/[key]
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
0 commit comments