Salman Adnan

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.

Solo, in production

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.

Draft Press: a moving artwork of what the product does, not a live view of it. Blank pages feed into the press, finished blog posts come out the other side, and the credits a client spends on each one orbit the machine. Live and interactive: drag it to orbit, scroll or pinch to zoom. Open full screen
The davonex.com homepage, a Digitalise Agency client site.
davonex.com, a Digitalise Agency client site.
132 / 132tests pass
56%line coverage
2free posts per 24 hours

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

  • Django 4.2
  • PostgreSQL
  • Gunicorn + nginx
  • Docker
  • Node / Puppeteer
  • optional Celery / Redis / Twilio / Sentry
  • n8n

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.

Book a call

Let's talk about what you're building.

Pick a slot below. No forms, no back-and-forth emails.