Admin Tools
One Cloudflare Worker and one small SPA that give a handful of volunteers a safer way to publish content and keep contact lists tidy.
What it does
- Importers. Paste an event or article URL, extract structured fields with an LLM, review the form, check for duplicates and publish into the site's CMS collections. Newsletter and resource importers follow the same pattern.
- Reconcile and Slack. Compare the site's contacts against the email provider, and separately against Slack membership, report drift and let a person apply each fix.
- Social. Plan a few weeks of Facebook posts from upcoming events and recent news, draft them in the Page's house style, fact-check them, and hold them for human approval before anything is scheduled.
- First comments. Queue a link comment for a post scheduled elsewhere; the post tool posts it once the post is live.
- Admin. An allowlist of admins and per-user tab access, so someone can see registrations without getting write access to events.
Design decisions
- A person always publishes. Approve, Schedule and Cancel refuse any request without a signed-in identity, because machine tokens are otherwise treated as unrestricted. Facebook scheduling ships in a test mode that renders everything and sends nothing.
- Fact check on every draft. Numbers, times and weekdays not in the source, stray links and unapproved hashtags raise warnings the reviewer must acknowledge. Links never go in the post body; they go in the first comment.
- A paced link queue. Links arrive in bursts, which tripped the free LLM tier's per-minute limits. There is one row per normalised URL, a one-row pacing table that jobs claim a slot from atomically, and a per-minute cron that drips due jobs. Confident finds are pre-drafted so accepting one is instant.
- Two writers, one pipeline. A local Claude runner takes jobs first; Gemma picks up whatever it has not taken after a hold period. The Worker builds every prompt and parses every answer, so both paths share the same code.
- Layered access. Cloudflare Access is the outer gate; tab-level permissions are a finer layer inside it. Feature flags can switch off Facebook scheduling and the sheet sync.
Under the hood
- Plain JavaScript Worker with D1 for state and three cron triggers (every minute, every two hours, daily). The SPA has no framework, one module per tab, and installs as a PWA.
- Extraction uses a Gemma model called through Google's Gemini API (the code also has an optional Ollama route for local development); Readability, linkedom and mdream for page cleaning; resvg-wasm to render post cards as images.
- Schema changes are numbered D1 migrations, applied by hand before the code that needs them is pushed.
Screenshots
Taken from a local copy with invented events, contacts and drafts; no real member or organisation data is shown.








