A Cloudflare Workers + D1 powered blog with Mustache templates, Markdown content, Google OAuth admin, and R2 media storage.
- Public pages: home, about, projects, news, work-with-me, contact, archive, and article detail pages.
- Admin area: Google OAuth login, post/news create/edit/delete, tags, drafts, featured images, and scheduling.
- Sprite-backed Codex article and news drafting from
/admin/draft-article. - Markdown articles rendered server-side with RSS feed support.
- Media library backed by R2 with metadata stored in D1.
- Node.js + npm
- Cloudflare Wrangler CLI
- Cloudflare account with Workers, D1, and R2 enabled
Install dependencies:
npm installRun the dev server:
npm run devThe Worker is configured in wrangler.jsonc with D1 and R2 bindings.
Set the Google OAuth credentials:
wrangler secret put GOOGLE_CLIENT_ID
wrangler secret put GOOGLE_CLIENT_SECRETIf you need a fixed redirect URL (custom domain), set:
wrangler secret put GOOGLE_OAUTH_REDIRECT_URIAuthorized redirect URI examples:
https://<your-worker>.workers.dev/admin/callbackhttp://localhost:8787/admin/callback
Apply migrations:
wrangler d1 migrations apply bhart-orgSeed an authorized admin user (replace values as needed):
wrangler d1 execute bhart-org --command "INSERT INTO authorized_users (id, email, name, is_active, created_at) VALUES ('<uuid>', 'you@example.com', 'Your Name', 1, '2024-01-01T00:00:00.000Z');"Create the media bucket if it does not exist:
wrangler r2 bucket create bhart-orgImages uploaded in the admin are stored in R2 and indexed in D1.
The admin drafting workspace at /admin/draft-article launches Codex inside a Fly.io Sprite and submits drafts through the existing Blog Automation API. Article mode uses /api/codex/v1/posts; news mode uses /api/codex/v1/news.
Required Worker secrets:
wrangler secret put CODEX_API_TOKEN
wrangler secret put SPRITES_API_TOKEN
wrangler secret put BHART_ARTICLE_AGENT_ALLOWED_EMAILSOptional Worker vars are set in wrangler.jsonc:
BHART_ARTICLE_AGENT_SPRITE_NAMEdefaults tobhart-orgBHART_ARTICLE_AGENT_SPRITE_WORKDIRdefaults to/home/sprite/bhart-org/mainBHART_ARTICLE_AGENT_CODEX_HOMEdefaults to/home/sprite/.codex-bhart-orgBHART_ARTICLE_AGENT_SPRITES_API_BASEdefaults tohttps://api.sprites.dev
The Sprite needs the repo, dependencies, project-specific Codex auth at /home/sprite/.codex-bhart-org/auth.json, and /home/sprite/.config/secrets/codex.env containing CODEX_BHART_API_TOKEN and BHART_CODEX_API_BASE=https://bhart.org/api/codex/v1. Do not copy auth.json from another Sprite or project; create a fresh login inside this Sprite with mkdir -p /home/sprite/.codex-bhart-org && CODEX_HOME=/home/sprite/.codex-bhart-org codex login --device-auth.
/home page/aboutabout page/projectsprojects page/newsnews page/work-with-meservices page/contactcontact page/archivearchive page/articles/:slugarticle page/rss.xmlRSS feed/adminpost list (requires login)/admin/posts/newnew post/admin/posts/:idedit post/admin/news/newnew news item/admin/news/:idedit news item/admin/mediamedia library/admin/draft-articleSprite-backed Codex article/news draft workspace/media/<key>media asset delivery (public)
- The media list is sourced from D1. Existing objects in R2 are not shown until uploaded through the admin.
- Images are served via
/media/...and cached with long-lived headers.
wrangler deploy