Example audit — performed on the public demo repository dovito-public/dovito-ad-display-demo at commit d4a9016. Reproduces the same methodology we apply to paid client engagements. All findings verifiable in the linked repo · live demo.

SaaS Readiness Assessment

April 9, 2026 · Refresh PUBLIC CASE STUDY
Phase 6 Deep-Dive · Refreshed

SaaS Readiness

Can the Dovito Ad Display demo become a multi-tenant SaaS? A 7-dimension gap analysis. Refreshed at commit d4a9016.

10%Overall Readiness
7Dimensions
$150–300KConversion Cost
4 phasesImplementation
Executive Summary

Headline: A full backend rebuild is required

The demo is a frontend-only fork with no database, no auth provider, no billing, and no server. On the SaaS-readiness rubric, it scores 10% overall — a number that is not a failure, but a direct and expected consequence of the demo's scope. This refresh was produced after a round of targeted fixes that improved the demo's frontend posture (React correctness, asset paths, query client defaults) but did not — and could not — change anything about the seven SaaS dimensions below.

The value of the assessment is not the headline score. Even when the answer is "this cannot become a SaaS without a full backend rebuild," the structured gap analysis below tells a prospect exactly what needs to be built, in what order, and roughly what it will cost. That is the deliverable clients are paying for: a priced, phased, dependency-ordered plan — not a letter grade.
10%Overall Readiness
7Dimensions Assessed
$150KLow-end Conversion
$300KHigh-end Conversion

Readiness by Dimension

1. Multi-Tenancy
0%
2. User Management
5%
3. Billing & Subscriptions
0%
4. Customization
30%
5. API Design
20%
6. Onboarding
15%
7. Monitoring & Ops
0%

Dimension 1 of 7

Multi-Tenancy

Can the system isolate data, users, and settings between multiple customer organizations?

Score

No tenancy substrate exists

0%

The demo has no backend. All state lives in a single browser localStorage key (dovito-demo-v2 — bumped from v1 when the demo-image URLs were migrated to basePath-prefixed paths), which is globally scoped to the visitor. There is no concept of an organization, no tenant column on any mock "table," and every admin user sees every record. This is not a failing of the demo — it is structurally impossible to multi-tenant a static frontend without a backend.

What Exists

  • Single global data namespace under one localStorage key
  • Schema types (src/lib/schema.ts) derived from the production codebase — include no tenant fields
  • User records exist but are not scoped to any org

Gaps

  • No organization or tenant entity
  • No tenant_id column on any record
  • No query-level scoping layer
  • No cross-tenant aggregation for platform admins
  • No tenant-scoped auth tokens or session claims
  • No row-level security
Required work: Introduce a real persistence layer with an organizations table; add organization_id FK to every business record; implement a query-scoping layer (the production code's IStorage pattern is a good target); add platform-admin vs org-admin role tiers. Effort: 2–4 weeks of focused backend work.

Component-by-Component Multi-Tenancy Audit

Every component below assumes single-tenant global state. Each would need to be rewired once a tenant context exists.

Component / PageTenancy AssumptionRemediation
admin/page.tsxLists all slides, applications, users globallyScope queries by tenant; add platform-admin escape hatch
dashboard/page.tsxShows "user's own" records, but "user" = browser sessionDerive user from real session; scope by their org
display/page.tsxSingle display surface, single brandPer-tenant branding + display settings keyed on org
brand-kit/page.tsxHardcoded to one brandPer-tenant brand kit record
mock-api.ts admin routesAll /api/admin/* endpoints return global collectionsEnforce org scoping at the data layer
mock-store.tsSingle root store objectReplace with real DB; ensure every query filters by tenant
Audit log viewerShows global audit entriesScope entries by org; allow platform-admin cross-tenant view
Webhooks settingsSingle global webhook listScope per org

Dimension 2 of 7

User Management

Identity, roles, permissions, SSO, MFA, invitations, audit.

Score

Cosmetic sign-in only

5%

The demo has a login form and a rudimentary role model — but role is determined by string-matching the email address in mock-auth.ts:21. The refreshed login page at src/app/login/page.tsx has been stripped down to a single "Sign in as Admin" button that is honest about demo mode, but the underlying identity model is unchanged: no password, no verification, no session token, no expiry.

What Exists

  • Login, register, forgot-password, reset-password, verify-email pages (all UI-only)
  • Three-tier role model: user / admin / super_admin
  • React Query-based session observer in mock-auth.ts

Gaps

  • No real password storage or verification
  • No session tokens / JWTs / cookies
  • No email verification
  • No SSO (Google / Microsoft / SAML / OIDC)
  • No MFA / TOTP / WebAuthn
  • No invitation flow for org members
  • No audit trail for auth events
  • No API keys for programmatic access
Required work: Reintroduce NextAuth v5 (the production codebase already uses it); implement credentials + Google OAuth; add invitation flow; add API key model; implement audit trail. Effort: 1–2 weeks.

Dimension 3 of 7

Billing & Subscriptions

Payment processing, subscription lifecycle, invoicing, dunning, tax.

Score

Stripe amputated; fake card UI

0%

Stripe was explicitly removed from the demo as part of the backend amputation. The collect-payment/page.tsx page was rewritten to render a fake card input (it collects nothing, charges nothing). Stubs exist in mock-api.ts for create-checkout-session, create-payment-intent, create-subscription, and validate-coupon — all return canned success responses. One hardcoded demo coupon code (LAUNCH20) exists.

What Exists

  • Checkout UI flow (non-functional)
  • Mock endpoints returning canned responses
  • Hardcoded LAUNCH20 coupon for demo
  • Billing portal link button (stub)

Gaps

  • No real Stripe integration
  • No subscription lifecycle state machine
  • No webhook handlers (no signing verification possible)
  • No invoice generation
  • No dunning (failed-payment recovery)
  • No proration / upgrade / downgrade logic
  • No tax calculation (Stripe Tax / Avalara)
  • No multi-currency support
  • No metered usage billing
Required work: Reintroduce Stripe Checkout + Elements + webhook handling; define platform subscription tiers; implement dunning via Stripe Smart Retries; add Stripe Tax. The production codebase already has most of this — it was deliberately removed. Effort: 1–2 weeks to reintroduce + harden for multi-tenant.

Dimension 4 of 7

Customization

Per-tenant branding, display settings, email templates, custom domains.

Score

Display settings exist, globally scoped

30%

Partial credit: the demo genuinely has a settings panel (/api/display-settings) backed by mock persistence. Users can change display behavior and those settings persist to localStorage. There is also a Brand Kit page (brand-kit/page.tsx) that surfaces the visual system. Both are single-tenant and global, but the mechanism for per-tenant customization exists and would extend naturally.

What Exists

  • Key/value display settings system (display_settings collection)
  • Settings UI in admin panel
  • Brand Kit reference page
  • Theme support via next-themes

Gaps

  • Settings are global, not per-tenant
  • No per-tenant logo / brand colors upload
  • No custom email template editor
  • No custom domain support
  • No white-label mode
  • No localization / i18n scaffolding
Required work: Key the settings table by organization_id; add logo/color upload (needs storage backend); build email template overrides; add custom-domain DNS verification flow. Effort: 1 week once tenancy exists.

Dimension 5 of 7

API Design

Is the API surface well-structured, documented, versioned, and ready for third-party integration?

Score

REST shape is good; no real API exists

20%

The mock router in mock-api.ts implements about 50 endpoints across slides, applications, display settings, users, webhooks, audit log, and billing stubs. The URL structure is RESTful and consistent (GET /api/slides, POST /api/slides/:id, etc.). What does not exist is a real API backed by anything durable, nor any form of API documentation, versioning, or authentication.

What Exists

  • ~50 mock endpoints with consistent REST-style URLs
  • Query/body parsing via fetch interceptor
  • Canned JSON responses
  • Webhooks CRUD UI (stub)

Gaps

  • No real HTTP endpoints (static export)
  • No OpenAPI / Swagger spec
  • No API versioning (/api/v1)
  • No API key auth
  • No rate limiting
  • No outbound webhook delivery
  • No developer portal
Required work: Reintroduce Next.js API routes backed by Postgres; generate OpenAPI spec from Zod schemas; add API key model; implement webhook dispatcher with retry; build dev portal. Effort: 1–2 weeks for real endpoints + OpenAPI; another week for dev portal.

Dimension 6 of 7

Onboarding

Self-service signup, activation, first-run experience, setup wizard, invitations.

Score

Login form exists; nothing downstream

15%

A registration page exists and accepts input, but registration is a no-op in the mock layer. There is no organization creation, no invitation flow, no setup wizard, no activation email, no first-run tour. The demo is explicitly oriented around "look at the product while already signed in," not "experience account creation."

What Exists

  • Register, login, forgot-password, verify-email pages
  • Email verification UI (no real email)

Gaps

  • No organization creation flow
  • No setup wizard / first-run tour
  • No team invitation flow
  • No activation email (no email infrastructure)
  • No sample-data seeding on new tenant
  • No progress checklist
  • No trial-to-paid conversion flow
Required work: Depends on real auth + email. Build org creation wizard, invite flow, and activation checklist. Effort: 1 week after auth/billing exist.

Dimension 7 of 7

Monitoring & Operations

Logging, metrics, error tracking, uptime monitoring, alerting, runbook.

Score

No telemetry whatsoever

0%

A static site on GitHub Pages has no logs, no metrics, no traces, no error reports, and no uptime instrumentation. There is no console.error forwarding, no Sentry, no PostHog, no OpenTelemetry. If a user hits a bug on the live demo, no one will ever know.

What Exists

  • GitHub Pages default access logs (not accessible to the app)

Gaps

  • No error tracking (Sentry, Rollbar)
  • No frontend analytics (PostHog, Plausible)
  • No structured logging
  • No metrics / dashboards
  • No uptime monitoring
  • No alerting / on-call
  • No runbook / incident response
  • No tracing (OpenTelemetry)
Required work: Frontend: add Sentry and PostHog. Backend (once it exists): structured JSON logging, Prometheus metrics, error aggregation, uptime probe, incident runbook. Effort: 3–5 days to bolt on basic frontend telemetry; another week to instrument a real backend.

Scorecard

Dimension Scorecard

DimensionScoreEffort to 80%Priority
Multi-Tenancy0%2–4 weeksP0 — blocking
User Management5%1–2 weeksP0 — blocking
Billing & Subscriptions0%1–2 weeksP0 — blocking
Monitoring & Ops0%1–2 weeksP1
Onboarding15%1 weekP1
API Design20%1–2 weeksP2
Customization30%1 weekP2

Implementation Order

Four-Phase Conversion Plan

Phased so each layer unblocks the next. Phases 1 and 2 are non-negotiable prerequisites for any real launch; Phases 3 and 4 are productisation.

Phase 1 – Foundation (Weeks 1–4)

Real Backend + Database

Rebuild the persistence layer the demo amputated. This phase unblocks every subsequent phase.

Reintroduce Next.js API routes Postgres + Drizzle schema (from production codebase) Replace mock-store with real DB layer Image storage backend (Supabase / S3) Email transactional layer
Phase 2 – Auth + Tenancy (Weeks 5–8)

NextAuth + Organization Scoping

Turn the product into a multi-tenant system. Requires Phase 1.

NextAuth v5 + credentials + Google OAuth Add organizations table + FK on all business records Query scoping layer Role hierarchy: platform admin / org admin / org member Invitation flow + email activation
Phase 3 – Billing (Weeks 9–10)

Stripe Integration

Reintroduce the Stripe layer that was amputated, this time for platform-level subscriptions.

Stripe Checkout + Elements Webhook handlers with signature verification Platform subscription tiers Dunning via Stripe Smart Retries Stripe Tax Billing portal integration
Phase 4 – Platform Features (Weeks 11–16)

Customization, Monitoring, APIs

Everything that makes the product feel like a real SaaS rather than a multi-tenant version of the demo.

Per-tenant branding / logos / colors Custom email templates Custom domains API keys + OpenAPI spec + dev portal Outbound webhook dispatcher Sentry + PostHog + structured logging Uptime monitoring + alerting Onboarding wizard
Total timeline: 16 weeks (~4 months) with a single full-stack engineer working efficiently. Total cost at mid-market rates: $150K–$300K on top of the ~$100K replacement value of the demo itself. A realistic end-to-end budget to go from "demo on GitHub Pages" to "launchable SaaS" lands around $250K–$400K.

← Back to Main Audit Report