← Projects · 2026-09-02 · active

PSBloggen migration

Migrated a ten-year PlayStation enthusiast blog off WordPress and grew it into a full IGDB-powered editorial platform: curated collections, a daily guess-the-game feature, PS Plus catalog tracking, and reader accounts, all self-hosted.

Read the case study Case study: PSBloggen migration →
  • Migrated 10+ years of WordPress content to Markdown via a custom XML → Turndown pipeline — 4,300+ posts, 1,700+ backfilled with IGDB metadata
  • Dual-stack: Astro SSR editorial site for reviews and editorial, React SPA for live IGDB game data, one shared auth and nav
  • Grew past the original scope into a full editorial platform: curated 'Samlingar' collections, a daily guess-the-game feature, PS Plus catalog tracking, author/developer profile pages
  • Reader-facing additions: shareable public game lists per user, year-in-review stats pages, a 'this day in gaming history' homepage widget, and a full PlayStation-themed visual redesign with a glowing SVG score arc
  • Community features: threaded comments with spam filtering, reader-submitted content reports, contact inbox with email replies — all moderated from the admin
  • Admin CMS matured into a real operations console: first-party + Umami analytics, scheduled R2 backups with one-click restore, a Discord review-pipeline bot, bulk publish/audit tooling
  • Rebuilt the content pipeline after diagnosing a real regression: dropped Astro's content collections for direct filesystem reads, cutting a content-heavy build from 15s/1.75GB to 4.5s/850MB
  • Found and fixed a CI build-cache bug (a GitHub Action silently tearing down a persistent build cache every run) that cut a 2-minute CI build down to under 40 seconds
  • Zero-downtime content publishing: admin saves a post, a debounced background rebuild atomically hot-swaps the site with a real filesystem lock — no restart, no visible downtime
  • Single-command Docker deploy with nginx routing both frontends, plus two independently-deployed sibling services (a review-pipeline bot and self-hosted analytics) over a shared Docker network

A small crew of PlayStation enthusiasts had been running a game blog for over ten years. The goal: get off WordPress, keep the content, and build something the team actually wanted to use. What started as a migration plus a game-discovery portal has since grown into a full self-hosted editorial platform — curated collections, a daily guessing game, PS Plus catalog tracking, reader accounts and moderation, and an admin console with its own analytics and operational tooling.

What it is

Editorial site (Astro): Reviews with scores, verdict badges, pros/cons, and platform tags. Editorial posts, curated “Samlingar” collections with mood/category browsing, author and developer profile pages, and a daily “guess the game” feature built from real review data. Runs as a built SSR Node process. Publishing is zero-downtime: the admin saves a post, a debounced background rebuild runs (~60–90s for the full ~4,300-post archive), and swaps the built site in atomically once it finishes — no restart, no visible interruption. Every saved .md file is also pushed to Git automatically, so Git stays the single source of persistent truth.

Game portal (React): Upcoming and recent PS4/PS5 releases pulled from IGDB, with cover art, trailers, screenshots, similar games, and full-text search. PS Plus catalog browsing with monthly navigation. User accounts with favourites, play-status tracking, and per-game notes.

Admin CMS: Grew from a Markdown-generating form into a real operations console — first-party and Umami analytics side by side, scheduled database backups to Cloudflare R2 with one-click restore, a script runner for bulk imports, content moderation for reader reports and comments, and integration with a Discord bot that handles review assignment and voting.

The migration

WordPress exports to XML. Each post is HTML — category metadata, tags, publish dates, author, and attached media all intact. A Node.js pipeline converted the export:

  • Parse XML with xml2js
  • Pre-process WordPress-specific markup (caption shortcodes → <figure>, oEmbed URLs → clean links)
  • Convert HTML to Markdown with Turndown
  • Rewrite internal links and download media locally
  • Write each post as .md with Astro frontmatter

Astro’s Zod schema validation caught malformed dates and missing fields at build time. The importer since grew into an ongoing pipeline — it also ingests new and scheduled posts straight from the old blog’s RSS feed, not just the one-time historical export.

Architecture

Express API proxies IGDB (keeping Twitch credentials server-side), handles JWT auth in httpOnly cookies, and serves recent editorial posts as JSON for the React homepage. An in-memory TTL cache wraps all IGDB responses.

SQLite (via better-sqlite3) stores user accounts, favourites, play statuses, comments, and the PS Plus catalog. Embedded in the Express process, backed up to Cloudflare R2 on a schedule.

nginx routes the two frontends through one domain in production, plus a same-origin /stats/ proxy to a sibling analytics deployment (avoids adblockers flagging a third-party analytics domain):

  • /_astro/* → Astro pre-built static assets
  • /reviews/, /editorial/ → Astro SSR Node process
  • /api/ → Express
  • /stats/ → Umami (separate deployment, shared Docker network)
  • /uploads/ → Cloudflare R2 (S3-compatible, no egress fees)
  • /* → React SPA

Tech stack

LayerTechnology
Game portalVite + React + TypeScript + Tailwind
Editorial siteAstro (built SSR, no client content-collection layer) + Tailwind
BackendExpress
DatabaseSQLite via better-sqlite3, scheduled backups to R2
AuthJWT in httpOnly cookies, bcrypt, hCaptcha on signup
Media storageCloudflare R2 (S3-compatible)
AnalyticsFirst-party pageview counter + self-hosted Umami (sibling deployment)
Productionnginx + Docker Compose, shared network with sibling services

Status

Live and in active daily development — well past the original beta. What launched as a migration plus a game portal has grown into the crew’s full editorial platform: collections, a daily guessing game, PS Plus tracking, comments, and an admin console with real operational tooling behind it.