Re-architected with zero downtime
Moved from Streamlit and SQLite to Next.js, FastAPI and Postgres one screen at a time (the strangler-fig pattern), with a script to migrate the data.
Case study · Full-stack web · 2025 – now
An AI publishing studio that plans a month of posts, produces the images and Reels, publishes them on time to Instagram, Facebook and WordPress, then reads the results back and suggests tomorrow's tasks.


01 · The content cycle
It replaced manual posting and scattered planning with one loop. Each stage feeds the next, and the results shape the next round.
1 · Plan
Five-layer AI pipeline
Brand → Month → Week → Series → Platform, plus a QA agent that checks every draft is on brand.
2 · Produce
Images and Reels
Image variations, a canvas editor with series templates, and 9:16 Reels with motion, captions and music.
3 · Publish
On time, unattended
Draft → Ready → Queued → Published. Posts, carousels and Reels go out to Instagram and Facebook on schedule.
4 · Learn
Growth engine
Daily Instagram, App Store and Google Play numbers. A weekly AI review finds the bottleneck and turns it into 3–5 daily tasks.
02 · Architecture
A Next.js app, a FastAPI backend and a scheduler daemon share one Postgres database. Tailscale puts it behind HTTPS for my own devices, with nothing exposed to the public internet.
My Mac
03 · Hard problems
Those were the two hardest parts of the project.
Moved from Streamlit and SQLite to Next.js, FastAPI and Postgres one screen at a time (the strangler-fig pattern), with a script to migrate the data.
Replaced fixed launchd jobs with a persistent APScheduler daemon. Times change on the fly, and a post still goes out if the Mac wakes within an hour.
Facebook long-lived tokens lapse after about 60 days and publishing just stops. The daemon now refreshes every account's token each week.
JWT over bcrypt hashes, an OAuth flow with CSRF state checks, and access tokens that stay on the server, never in the browser.
Each growth source runs and logs on its own, so an expired Facebook token doesn't stop the App Store or Google Play numbers.
Postgres is the source of truth and Firestore a mirror. Each status carries a timestamp and the newest wins, so a tap on the phone is never overwritten.
Stack
Next.js 16 · React 19 · TypeScript · Tailwind · Python · FastAPI · APScheduler · PostgreSQL · MoviePy · Pillow · Fabric.js · Firebase · Meta Graph API v22 · OpenRouter · Tailscale