A single-page Astro portfolio that is fully self-editable: every section, the
colors, the fonts, a Markdown blog, and a links feed are managed from a
built-in /admin, with no code changes and no redeploys. Astro SSR and a
teenybase backend run inside one Cloudflare Worker,
sharing a single D1 database and R2 bucket.
Live demo → astro-monograph-teenybase.theserverless.dev — sign in to the admin and edit content, colors, fonts, blog posts and links live.
This is the dynamic, CMS-backed evolution of the static Astro Monograph theme. See the meta blog post on the live demo for a tour of the CMS and how the single-Worker architecture fits together.
The public demo (banner, exposed credentials, daily database reset) lives on the
demobranch;mainis the clean template.
- Astro SSR renders the site and reads the published content for each section straight from Cloudflare D1 during render (no HTTP round-trip).
- teenybase provides the REST API, JWT auth with row-level rules, R2 file
uploads, and the admin — mounted at
/api/*inside the same Worker (src/server/teeny.ts,src/pages/api/[...path].ts). - The committed
src/data/*.yamlis both the seed and the fallback, so a fresh clone always renders even before the database is seeded (src/lib/content.ts). - Edits in
/adminsave as drafts; you Preview, then Publish (publish copiesdraft→published). - Project detail pages at
/projects/<slug>(optional Markdown write-up per project), a print-ready/cvrésumé generated from the same content, and edge-generated Open Graph cards for every page (/og/*.png, via satori + resvg) so shared links look like the site.
src/
├── components/ # Hero, About, Experience, Projects, Skills, Education,
│ # Contact, Footer, BlogTeaser, LinksTeaser, CustomSections
├── data/ # YAML seed + fallback content (edit or seed into D1)
├── layouts/ # BaseLayout (SEO/theme), PageLayout (sub-page chrome)
├── lib/ # data loader, content (D1↔YAML), blog, links, rss, slug, og, admin SPA
├── pages/ # index, blog/, links, cv, projects/[slug], admin/,
│ # api/[...path], og/[...path], rss.xml, links.xml,
│ # sitemap.xml, llms.txt, robots.txt
├── server/ # teeny.ts (mounts teenybase inside the Worker)
└── styles/ # global.css (Tailwind theme + animations)
blog-backend/ # teenybase config + generated D1 migrations + seed scripts
scripts/ # setup.mjs, seed-content.mjs
You need a (free) Cloudflare account and Node 18+.
git clone https://github.com/theserverlessdev/astro-monograph-teenybase
cd astro-monograph-teenybase
npm install
npx wrangler login
# Provisions D1 + R2, applies migrations, sets secrets, deploys, creates your
# admin user, and seeds content. Prints your live URL + credentials.
npm run setupnpm run setup (see scripts/setup.mjs) is idempotent and prints your admin
login at the end (override with ADMIN_EMAIL=you@example.com ADMIN_PASSWORD=…).
Custom domain (must be a zone on your Cloudflare account):
DOMAIN=yourdomain.com npm run setupTo re-seed content from YAML after editing it: npm run seed:content (add
-- --force to overwrite existing sections).
npm install
cp blog-backend/.dev.vars.example .dev.vars # then edit
npm run devPushes to the default branch auto-build and deploy via Cloudflare Workers
Builds (no GitHub Actions). See blog-backend/CI-CD.md
for the one-time dashboard connection steps.
MIT — see LICENSE. Built with Astro, Tailwind CSS, teenybase, and icons by Lord Icon.