agency-blog-saas
Agency Blog SaaS
This is the software that runs Salman's own agency. A client signs in, asks for a blog post on a topic, and the system writes it, stores it, and publishes it; each post costs 100 credits, refunded automatically if the writing fails. It is the agency's live product, so the code stays private and the linked demo is a scripted tour with made-up data.
Built and operated solo by Salman Adnan for his own agency. Its source is private since it's the agency's live product, not just data that can be scrubbed. The linked demo is a scripted product tour of the same features with fabricated data, not the real system.
Overview
The production codebase behind digitalise.agency: a static marketing site plus a Django SaaS that generates, stores, and publishes AI-written blog posts, gates premium workflows behind redemption codes, and gives clients a portal for progress updates.
It optimizes for one operator running real client work: everything is admin-manageable, failures refund credits, and destructive actions land in an audit log.
Who it serves: this is the live software of Salman's own agency, Digitalise Agency, and it runs that agency's real client work. Clients are not hard-coded. Staff create each client record in the portal at runtime, assign users to it, and post updates against it, so the roster lives in the production database rather than in the codebase. The public marketing site names the companies the agency has worked with, showing three client logos: Chronosa, Hi-Tec Bearings, and IQI.
Key features
- Blog generation: an authenticated user submits a topic, the server creates a PendingRequest and calls an n8n webhook, so generation survives the browser closing. 100 credits per post, deducted atomically and refunded on failure.
- PDF export of any post through a Node and Puppeteer subprocess.
- A custom user model with email verification codes and optional Twilio SMS phone verification.
- Credits and subscriptions redeemed via one-time backend-generated codes, with no payment processor in the loop.
- A client portal where staff assign users and post updates that export to PDF and PPTX.
- Ops: an audit log, a DB-backed cache, rotating file logs, CSP middleware, rate limiting with JSON 429s, and optional Sentry and Celery.
Results
- 132 of 132 tests pass against local PostgreSQL 18, with 56% line coverage gated at 50%.
- An internal audit found 53 issues (hardcoded secrets, CSRF exemptions, N+1 queries); views.py is the least-tested module at 31% coverage.
- It is in production on digitalise.agency, serving the agency's own client work on Docker, Gunicorn behind nginx, and PostgreSQL 17. The client portal is modelled end to end (Client, ClientAssignment, ClientInviteLink, ClientUpdate), with UUID invite tokens and updates that export to PDF and PPTX.
Tech stack
A challenge worth noting
The cache backend is DatabaseCache and context processors call it on every request, but migrate does not create the cache table, so a migrations-only deploy would 500 on the first page load. The fix makes createcachetable a first-class deploy step, and the lesson learned was that a guarded UPDATE with F() beats read-modify-write for anything that behaves like a balance.