MiniCMS

Node.js 20 Fastify 4 SQLite better-sqlite3 Eta TipTap sharp bcrypt Apache systemd

What MiniCMS Gives You

MiniCMS lets you write, edit, schedule and re-skin an entire portfolio site from a single browser tab. No FTP, no opening files in a code editor, no waiting for a build to finish. You sign in, change what you want to change, click Publish, and seconds later the public site reflects it — loading as fast as any hand-coded HTML page, because that is exactly what it serves. The complexity stays out of your way: there is one sidebar, one publish button, and one help panel that answers questions inline as you edit.

The Problem It Solves

A hand-coded HTML portfolio is fast and clean, but the moment you want to add a blog post, fix a typo in a project description, or change the accent colour, you are back in a code editor, syncing assets, and pushing a release. Over time the friction wins — you stop updating the site because every small change feels like an event. The shop-window you built starts looking out of date and that is the worst possible signal for a portfolio.

The Approach

MiniCMS lives at the same domain as the site. You sign in, write or edit something, drop in an image, hit Publish — and the public version of the site is regenerated as ordinary HTML, CSS and images. Those static files are served directly by the webserver, so visitors get the exact same instant load profile as a hand-coded site. The admin only does work while you are actively editing; the public side has no runtime cost and nothing to break under load.

Sanitised Rich-Text Editing

Drafts & Scheduled Publishing

Themes & Typography

Media Pipeline

Safety & Reliability

Publishing is safe by design: the new version of the site is assembled in a staging directory and only swaps in when it's complete. If anything fails halfway through a rebuild, your visitors carry on seeing the previous version — they never land on a half-built page. A daily backup runs automatically in the small hours, keeping two weeks of database + uploads history. Logging in uses bcrypt-hashed passwords; every form carries a CSRF token; no single endpoint can be hammered thanks to per-route rate limiting.

Hosted Demo

Try MiniCMS live — write a post, swap the theme, upload an image, schedule something for tomorrow, hit Publish. You get a private per-visitor sandbox: your changes are visible only to you and are wiped after 30 minutes of inactivity, so nothing you do affects anyone else.

Admin login: demo / demo. Only a few sandboxes run at once — if they're all in use you'll see a brief notice; try again in a couple of minutes.

What's Inside

  • Single sidebar that groups every editing module — Pages, Work portfolio, Blog, Menu, Media, Fonts, Themes, Settings — plus an inline Help centre so you never have to leave the admin to look something up
  • Rich-text editor with formatting, links, image insert, YouTube embed and code blocks — plus an HTML view for the rare time you need raw markup
  • Everything you paste or type is cleaned before going live: scripts, event handlers and unsafe URLs are stripped automatically
  • Three-state lifecycle per post or page — Draft, Published, Scheduled — with scheduled items auto-going-live without you needing to be online
  • Theme editor with five built-in palettes, live colour preview, and one-file import/export — back up or share a look in seconds
  • Three independent font slots (body, code, headings) with six families included and your own WOFF2 uploads welcome
  • Smart image handling: web-optimised re-encode, EXIF stripped, modern format paired with a fallback automatically
  • One-click copy on every code block in the published site
  • Wire up Google Analytics in two clicks — paste the Measurement ID into Settings, no code edits
  • Atomic publish: a botched rebuild can never break the live site — the previous version stays in place until the new one is complete
  • Automatic daily backup of database and uploads, with two weeks of history retained
  • Hardened single-admin login — bcrypt password, signed session cookie, CSRF tokens, rate limiting
  • No build step, no frameworks — the admin and the generated site are both plain modern JavaScript and HTML

My Role

Sole author. Full-stack work — Fastify route layer, SQLite schema and migrations (idempotent ensureColumn helper for SQLite's ADD COLUMN limitations), service layer with embedded prepared statements, Eta templates for both the admin and the regenerated public site, the TipTap integration with server-side sanitisation, the publish pipeline, themes/fonts editors, scheduled-publish via systemd, the Apache reverse-proxy snippet, and the deploy/backup units. Tests use the native node --test runner — no transpile, no bundler, no framework boilerplate.

Technology Used

Node.js 20 Fastify 4 SQLite better-sqlite3 11 Eta 3 TipTap 2 sanitize-html sharp bcrypt @fastify/csrf-protection @fastify/session @fastify/rate-limit Apache 2.4 systemd timers Tailscale Serve node --test

Challenges and Learning

Two interesting bits. First, the storage convention for the three-state lifecycle: instead of adding a status column, I reinterpret the existing published integer together with a nullable published_at — draft is (0, NULL), published is (1, NULL|past), scheduled is (1, future). That keeps pre-existing rows behaving identically and avoids a wider migration. Second, the auto-publish trigger needed a guard so the static site is not regenerated every five minutes for nothing. The runner compares the most recent successful publish_log timestamp against any due rows' published_at — if nothing has crossed the line since the last publish, the script exits silently. Lex-comparing timestamps required normalising SQLite's CURRENT_TIMESTAMP output to ISO-8601 UTC, because 'T' > ' ' made same-instant strings look different in WHERE predicates.

The End Result

A site visitors cannot tell apart from a hand-coded one — same HTML, same load speed, same lack of runtime weight — but with all the comfort of a database behind it. You can write a post on the train and schedule it for Monday morning; you can swap the entire colour palette in thirty seconds; you can drop in a screenshot and have it served in a modern format without thinking about it. Used in production for attv.uk-adjacent sites and as the starting point for every new portfolio I build.

What's Next — Proposed Improvements

MiniCMS works as a single-author tool today. The ideas below would extend what it can do without making the day-to-day editing experience any heavier. Vote for the ones you'd want first:

Multiple authors with permissions

  • Invite editors who can write and save drafts, while only you can publish
  • Per-author audit log — see who changed what, and when
  • Each author has their own profile shown on the blog posts they write

Full-text search across everything

  • One search box in the admin that finds any post, page or media item by its content — not just title
  • The same search built into the public site for visitors
  • Powered by SQLite's built-in full-text search — nothing extra to install or maintain

Multi-language pages

  • Edit the same page in several languages, linked together in the admin
  • The public site adds an hreflang switcher automatically so visitors land on their language
  • Per-language URLs — /en/, /pl/, etc. — with the right one chosen by default

Comments with moderation

  • Optional comment thread on any blog post or project page
  • Held for review before going live — spam never reaches readers
  • Reply by email and your reply appears on the page

Mobile-first admin editor

  • Phone-friendly editor with a sticky toolbar that doesn't fight the on-screen keyboard
  • Upload straight from the phone camera or photo library
  • Save and publish from one thumb-reachable button

Version history & one-click undo

  • Every save creates a hidden snapshot — go back to any earlier version of any page
  • Side-by-side diff between two versions so you can see exactly what changed
  • Restore in one click — the live site updates on the next publish