Architecture Diagram

This document gives a high-level view of how Tutor fits together: the browser, the single Next.js application (pages and API), the serverless PostgreSQL database, object storage, the license server, and integration providers. Tutor is a tutoring & education booking marketplace delivered as one deployable Next.js 16 app — there is no separate API server, no ORM, and no message broker.

System Diagram

TEXT
                          ┌──────────────────────────────────────────────┐
                          │                  BROWSER                      │
                          │  Public storefront · Customer · Host (tutor) ·        │
                          │  Admin panel · Influencer · Installer         │
                          └───────────────────────┬──────────────────────┘
                                                  │  HTTPS (page loads + fetch /api/v1/*)
                                                  ▼
        ┌───────────────────────────────────────────────────────────────────────┐
        │                    NEXT.JS 16 APP  (App Router, React 19)              │
        │                                                                       │
        │   src/app                                                             │
        │   ├─ (site)            public storefront + lesson catalogue + booking │
        │   ├─ (auth)            customer / host account auth                    │
        │   ├─ admin/(panel)     admin panel        (cookie: admin_token)        │
        │   ├─ customer/(app)    customer portal    (cookie: member_session)     │
        │   ├─ host/(app)        host (tutor) portal (cookie: member_session)          │
        │   ├─ influencer/(app)  affiliate portal   (cookie: influencer_session) │
        │   ├─ install           first-run setup wizard                         │
        │   └─ api/v1/*          ROUTE HANDLERS = the API (~290 route.ts files)  │
        │                                                                       │
        │   each handler →  auth/permission  →  ensure<Domain>Schema()  →  SQL   │
        │                                                                       │
        │   src/lib   db · auth (4 sessions) · booking spine · r2 · secrets · i18n │
        │   src/core + src/product   channels · plugins · payments · adapters   │
        │                            + integration/feature add-ons              │
        └───┬───────────────┬────────────────┬────────────────┬────────────────┘
            │ SQL (HTTPS)   │ S3 API         │ HTTPS          │ HTTPS / SMTP
            ▼               ▼                ▼                ▼
     ┌────────────┐  ┌─────────────┐  ┌──────────────┐  ┌─────────────────────┐
     │ PostgreSQL │  │ Cloudflare  │  │   License    │  │  Integration         │
     │  (Neon,    │  │  R2 / S3    │  │   server     │  │  providers           │
     │ serverless)│  │  storage    │  │ (CreativeCape│  │  (payment gateways / │
     │            │  │             │  │  RS256, off- │  │  email / sms / video /│
     │ raw SQL,   │  │ uploads,    │  │  line verify)│  │  chat / analytics /  │
     │ no ORM     │  │ media, i18n │  │              │  │  captcha) via add-ons │
     └────────────┘  └─────────────┘  └──────────────┘  └─────────────────────┘

Client

The browser is served the storefront plus four portals from the same application, separated by App Router route groups (and gated by their respective cookies):

  • Public storefront (src/app/(site)) — home, lesson catalogue/search, lesson detail, booking & checkout, blogs, FAQ, gallery, about, contact, how-it-works, become-a-tutor, legal pages.
  • Customer portal (src/app/customer/(app)) — dashboard, bookings, payments, messages, notifications, reviews, wishlist, support, settings. Gated by member_session.
  • Host (tutor) portal (src/app/host/(app)) — dashboard, listings (lessons), calendar/sessions, bookings, inbox, insights, payouts, billing (subscription), reviews, settings. A host is a signed-in customer who owns an active hosts row, so it also uses member_session.
  • Admin panel (src/app/admin/(panel)) — listings, bookings, hosts, customers, refunds, reviews, coupons, extras, CMS, theme builder, master-data, settings, license. Gated by admin_token.
  • Influencer / affiliate portal (src/app/influencer/(app)) — dashboard, referral links, clicks/visits, commissions, bookings, payouts, marketing materials, analytics. Gated by influencer_session.

All UI is rendered by Next.js server and client components; all data access goes through /api/v1/*.

API

The API is route handlers, not a standalone server. Every route.ts under src/app/api is an endpoint; the repository contains ~290 of them, versioned under /api/v1, with /api/install and /api/license outside the version prefix. An auto-generated OpenAPI document (scripts/gen-openapi.ts) is served at /api/openapi.json and rendered by Scalar at /api/docs/v1 — both admin-gated.

Representative /api/v1 resource segments:

TEXT
portal · customer · host · influencer · admin ·
listings · bookings · amenities · extras · coupons · reviews ·
blogs · pages · faqs · gallery · hero-sliders · testimonials · brand · destinations ·
countries · currencies · languages · locations · i18n · forms · booking-settings ·
calendar-config · captcha · chat · chatbot · livechat · video · analytics · plugins · cron · ext

Each handler typically (1) checks auth (getSession() admin, getCustomerId() customer, getHostId() host, getInfluencerId() influencer) and per-route permissions on admin routes, (2) calls the relevant ensure*Schema(), (3) validates and re-quotes server-side, (4) runs raw SQL, and (5) returns JSON. The public/mobile API is the non-admin surface; admin endpoints are excluded from the API reference.

Layer Diagram — Core vs Product

The extensibility system splits stable framework code (src/core) from swappable add-ons (src/product). Add-ons are aggregated at build time into generated index files, which the core registries import.

TEXT
┌──────────────────────────────── src/core (stable contracts + registries) ───────────────┐
│                                                                                          │
│  channels/registry.ts  → CHANNELS  = mergeChannels(                                      │
│       email (FREE: SMTP), payments (FREE built-ins), storage (FREE: R2/S3/GCS),          │
│       analytics, video, calendar, captcha, sms, chat,  …ADDON_CHANNELS )                 │
│                                                                                          │
│  plugins/registry.ts   → FEATURES  = [ …ADDON_FEATURES ]   (FeaturePlugin contract:      │
│       nav · admin/portal/public pages · api · hooks · ensureSchema · settings)           │
│                                                                                          │
│  payments/registry.ts  → PAYMENT_DRIVERS = [ …built-in drivers, …serverAdapters(payment)]│
│                                                                                          │
│  adapters/client.ts·server.ts → clientAdapters(kind) / serverAdapters(kind)              │
│       resolve a provider implementation by kind + provider at runtime                    │
└───────────────────────────────────────────▲────────────────────────────────────────────┘
                                             │ imports the GENERATED indexes
┌────────────────────────── src/product (add-ons + generated aggregation) ─────────────────┐
│                                                                                          │
│  addons/<id>/    addon.json  +  index.ts  (+ schema.ts / api.ts / *.tsx)                 │
│     provider add-ons → payments-stripe, payments-mollie, email-sendgrid,                 │
│                        analytics-posthog, chatbot-anthropic, calendar-google_calendar, … │
│     feature add-ons  → announcements, audit-logs, digest, api, maps                      │
│                                                                                          │
│  addons/index.ts            [GENERATED] → ADDON_CHANNELS                                  │
│  features/index.ts          [GENERATED] → ADDON_FEATURES                                 │
│  adapters-server/index.ts   [GENERATED] → ADDON_SERVER_ADAPTERS  (by kind)               │
│  adapters-client/index.ts   [GENERATED] → ADDON_CLIENT_ADAPTERS  (by kind)               │
│                          (produced by scripts/bundle-addons.mjs — do not hand-edit)      │
└──────────────────────────────────────────────────────────────────────────────────────────┘

The same relationships expressed as a Mermaid graph:

MERMAID
graph TD
  B[Browser] -->|HTTPS| APP[Next.js 16 App]
  subgraph APP[Next.js 16 App Router]
    SITE["(site) storefront + booking"]
    ADMIN["admin/(panel)"]
    CUST["customer portal"]
    HOST["host portal"]
    INFL["influencer portal"]
    API["api/v1/* route handlers"]
    CORE[src/core registries]
    PROD[src/product add-ons]
  end
  PROD -->|generated indexes| CORE
  API -->|raw SQL| DB[(PostgreSQL / Neon)]
  API -->|S3 SDK| R2[(Cloudflare R2 / S3)]
  APP -->|offline RS256 verify + heartbeat| LIC[License server]
  API -->|via add-ons| INTG[Payments / Email / SMS / Video / Analytics]

Database

  • Engine: PostgreSQL, hosted on Neon, reached through @neondatabase/serverless (HTTP-based driver) from src/lib/db.ts.
  • No ORM, no migration tool. Tables are created and evolved by idempotent ensure*Schema() functions (CREATE TABLE IF NOT EXISTS + ALTER TABLE … ADD COLUMN IF NOT EXISTS).
  • The model centres on the booking spinelistings (lessons) → experience_sessions (lesson times with seats_total/seats_booked) → bookings (keyed on session_id + seats + booking_mode) — plus booking_extras/booking_payments/booking_refunds, amenities/extras/ coupons/gift_cards, hosts + subscriptions + earnings/payouts, customers, influencers, a CMS, and i18n reference data. See the Database Documentation.

Storage

  • File uploads are sent to Cloudflare R2 (or any S3-compatible bucket) through the AWS S3 SDK in src/lib/r2.ts. Per-language UI translation JSON is also stored in R2.
  • The active storage provider and credentials are read from the integration_connections table (channel STORAGE), encrypted with AES-256-GCM, with R2_* environment variables as a fallback.
  • Images can be transformed before upload; clients receive the public object URL.

Third-Party Services

Provider Used for
Neon Managed serverless PostgreSQL
Cloudflare R2 / S3-compatible Object storage (lesson media, images, i18n JSON, documents)
CreativeCape license server Domain license activation + heartbeat (RS256, verified offline)
Payment gateways Booking checkout — PayPal REST etc. (built-in) + Stripe, Mollie, Paystack… add-ons
SMTP / email providers Transactional email (verification, OTP, booking notifications)
SMS / WhatsApp OTP + booking/cancellation notifications (add-ons)
Video hosts Lesson media (add-ons)
Analytics / pixels GA4/GTM, Meta Pixel, PostHog, Clarity, TikTok — add-ons
Captcha / live chat / chatbot reCAPTCHA; Crisp/Intercom/Tawk…; OpenAI/Anthropic/OpenRouter — add-ons
AI translation OpenAI / Anthropic — machine-translate dynamic content, cached in translations

Which providers are active is resolved at runtime from admin-configured integration_connections, and provider code ships in the corresponding add-on — so integrations activate without changing core code. Secrets are stored encrypted and never returned to the client in plaintext.


© CreativeCape Solutions · creative-cape.com · support@creative-cape.com