Aurora is a modern, production-ready multi-tenant SaaS website builder and booking platform designed for service-based businesses. It enables users to create beautiful websites, manage blog content, accept online bookings/inquiries, and go live on their own custom domains.
- Multi-Tenancy: Complete isolation of organizations (tenants) and website instances.
- Visual Section Builder: Edit pages using schema-driven sections, theme component layouts, and customize styles with background and text color controls.
- TipTap Blog Editor: A rich Word-style blog editor with auto-saving, draft/publish workflows, and built-in SEO metadata and OG image controls.
- Booking Engine: Integrated customer booking calendar, booking slots, stats, and automatic customer profile creation.
- Role-Based Access Control (RBAC): Define custom staff roles and assign granular permissions across 7 system modules.
- Static Website Publishing: Publishes website manifests to Cloudflare R2 / S3-compatible object storage.
- Custom Domain Routing: Resolve custom domains and subdomains dynamically, including nameserver validation and CDN cache purging.
- Device Fingerprinting: Fraud risk scoring for bookings and referrals.
flowchart TD
U["CMS User / Team"] --> Web["apps/website-builder-web (Next.js Dashboard)"]
V["Public Visitors"] --> Web
Web --> API["apps/website-builder-api (Express API)"]
API --> DB["PostgreSQL via Prisma"]
API --> R2["Cloudflare R2 / S3 Storage"]
API --> CF["Cloudflare Cache Purge APIs"]
Web --> R2
-
Dashboard & Visual Builder (
apps/website-builder-web):- Built on Next.js 14 utilizing the App Router.
- Serves as the dashboard (admin interface) for tenant organizations to manage booking workflows, staff roles, and services.
- Provides a visual page builder interface that syncs layouts and page content sections.
-
Backend REST API (
apps/website-builder-api):- An Express.js server written in TypeScript.
- Handles core business logic, including authentication, role-based permission enforcement, booking schedules, referral workflows, and device checking.
-
Data Scoping & Security:
- Powered by PostgreSQL and Prisma ORM.
- Implements strict tenant (organization) and instance (website) isolation.
- Utilizes a two-level JWT scheme: a session-level token for user authentication, and a tenant-scoped token for workspace actions.
-
Static Site Publishing & Routing:
- Publishing builds a static JSON manifest of pages and uploads it to Cloudflare R2 / S3-compatible storage.
- Dynamic domain routing index matches incoming hosts (subdomains or custom domains) and serves the pages using theme components from
@project-aurora/themes.
- Monorepo: Turborepo + pnpm workspaces
- API: Node.js + Express (TypeScript)
- Frontend / Website Builder: Next.js 14 (App Router)
- Database: PostgreSQL + Prisma ORM
- Auth: Custom JWT (session + tenant-scoped tokens)
- Styling: Tailwind CSS v4
- Node.js v18+
- PostgreSQL running locally
- pnpm package manager
npm install -g pnpmCopy the example and fill in your values:
cp .env.example .envKey variables:
DATABASE_URL=postgresql://postgres:YOUR_PASSWORD@localhost:5432/booking_engine
JWT_SECRET=your-secret-key-min-32-chars
JWT_REFRESH_SECRET=your-refresh-secret-key-min-32-chars
NEXT_PUBLIC_AUTH_IDLE_TIMEOUT_MINUTES=30
API_PORT=3002
API_BASE_URL=http://localhost:3002
SITE_DOMAIN=buildmyonlineweb.site
PLATFORM_HOST_BYPASS=
CMS_URL=http://localhost:3001
CORS_ORIGIN=http://localhost:3001,http://localhost:3002
NEXT_PUBLIC_SITE_DOMAIN=buildmyonlineweb.site
NEXT_PUBLIC_PUBLISHER_TAGLINE=Built Your Website with My Online Web
NEXT_PUBLIC_PUBLISHER_URL=https://buildmyonlineweb.site
CLOUDFLARE_API_TOKEN=
CLOUDFLARE_ZONE_ID=
R2_ENDPOINT=
R2_ACCOUNT_ID=
R2_ACCESS_KEY_ID=
R2_SECRET_ACCESS_KEY=
R2_BUCKET_NAME=
R2_PUBLIC_URL=
PUBLISHED_SITES_BASE_URL=
ROUTING_INDEX_CURRENT_URL=
ROUTING_INDEX_CACHE_TTL_MS=30000
WEB_PROXY_SHARED_SECRET=
NEXT_PUBLIC_PUBLISHED_SITES_BASE_URL=
NEXT_PUBLIC_ROUTING_INDEX_CURRENT_URL=
NEXT_PUBLIC_ROUTING_INDEX_CACHE_TTL_MS=30000
NEXT_PUBLIC_PLATFORM_HOST_BYPASS=
NEXT_PUBLIC_FINGERPRINT_ENABLED=true
FINGERPRINT_ENABLED=true
FINGERPRINT_MODE=log
DEVICE_CHECK_RATE_LIMIT_PER_MIN=10
IPINFO_TOKEN=SITE_DOMAIN is used by the API to persist each instance primary full domain ({subdomain}.{SITE_DOMAIN}), and NEXT_PUBLIC_SITE_DOMAIN is used by the Website Builder Web App to render subdomain suffixes in the UI. PLATFORM_HOST_BYPASS (and optional NEXT_PUBLIC_PLATFORM_HOST_BYPASS) lets you reserve platform hosts (for example staging.buildmyonlineweb.site,staging-api.buildmyonlineweb.site) so they are never treated as tenant published hosts. By default, staging.{SITE_DOMAIN} and staging-api.{SITE_DOMAIN} are already treated as reserved platform hosts. WEB_PROXY_SHARED_SECRET must match in the API and Website Builder Web runtimes for trusted /web host routing. CLOUDFLARE_API_TOKEN + CLOUDFLARE_ZONE_ID are optional and used only for CDN cache purge operations.
Note: If your database password contains special characters like
@, URL-encode them (e.g.,@becomes%40).
Prisma needs its own .env file next to the schema:
# Create file: packages/database/.envDATABASE_URL=postgresql://postgres:YOUR_PASSWORD@localhost:5432/booking_engineNext.js loads environment variables from its own directory:
# Create file: apps/website-builder-web/.env.localNEXT_PUBLIC_API_URL=http://localhost:3002
NEXT_PUBLIC_SITE_DOMAIN=buildmyonlineweb.site
NEXT_PUBLIC_PUBLISHER_TAGLINE=Built Your Website with My Online Web
NEXT_PUBLIC_PUBLISHER_URL=https://buildmyonlineweb.site
WEB_PROXY_SHARED_SECRET=replace-with-a-strong-shared-secret
NEXT_PUBLIC_PLATFORM_HOST_BYPASS=Run all commands from the project root.
pnpm installcd packages/database && npx prisma generate && cd ../..For first-time setup:
cd packages/database && npx prisma db push && cd ../..If you have conflicting old data, use
--force-reset(this wipes the database):cd packages/database && npx prisma db push --force-reset && cd ../..
pnpm db:seedThis creates 30 system permissions across 7 modules (bookings, services, customers, inquiries, settings, staff, roles).
Dry-run (shows what will be removed while preserving admin + system template data):
pnpm db:cleanup-usersExecute deletion:
pnpm db:cleanup-users -- --executeOptional: preserve additional users with DB_CLEANUP_KEEP_EMAILS in .env (comma-separated), or pass --keep-email=<email>.
Packages must be built in dependency order:
pnpm --filter @project-aurora/core build
pnpm --filter @project-aurora/database build
pnpm --filter @project-aurora/auth buildpnpm --filter @project-aurora/website-builder-api devpnpm --filter @project-aurora/website-builder-web devRun each command in a separate terminal window.
| Service | URL | Description |
|---|---|---|
| Website Builder Dashboard | http://localhost:3001 | Web Admin dashboard (Next.js) |
| API Server | http://localhost:3002 | Backend API (Express) |
| API Docs (Swagger) | http://localhost:3002/docs | Interactive API documentation |
| API Spec (JSON) | http://localhost:3002/docs.json | OpenAPI JSON specification |
| Health Check | http://localhost:3002/health | API health status |
# 1. Install
pnpm install
# 2. Set up environment files (see Environment Setup section above)
# 3. Database
cd packages/database && npx prisma generate && cd ../..
cd packages/database && npx prisma db push && cd ../..
pnpm db:seed
# 4. Build packages
pnpm --filter @project-aurora/core build
pnpm --filter @project-aurora/database build
pnpm --filter @project-aurora/auth build
# 5. Start servers (each in separate terminal)
pnpm --filter @project-aurora/website-builder-api dev # API → http://localhost:3002
pnpm --filter @project-aurora/website-builder-web dev # Web App → http://localhost:3001| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /auth/register |
Public | Register owner + first business instance |
| POST | /auth/login |
Public | Login with email and password |
| POST | /auth/refresh |
Public | Refresh access token |
| POST | /auth/forgot-password |
Public | Request password reset |
| POST | /auth/reset-password |
Public | Reset password with token |
| POST | /auth/logout |
Required | Revoke refresh token |
| GET | /auth/me |
Required | Get current user and tenants |
| POST | /auth/switch-tenant |
Required | Switch active business instance |
| Method | Endpoint | Permission | Description |
|---|---|---|---|
| GET | /cms/instances |
Auth only | List user's business instances |
| POST | /cms/instances |
Auth only | Create new instance |
| GET | /cms/instances/:id |
Auth only | Get instance details |
| PUT | /cms/instances/:id |
Owner only | Update instance |
| DELETE | /cms/instances/:id |
Owner only | Deactivate instance |
| PUT | /cms/instances/:id/domain-route |
Auth only | Map host route to an instance |
| DELETE | /cms/instances/:id/domain-route/:host |
Auth only | Remove mapped host route |
| Method | Endpoint | Permission | Description |
|---|---|---|---|
| GET | /cms/roles |
roles.view |
List all roles |
| POST | /cms/roles |
roles.create |
Create custom role |
| GET | /cms/roles/:id |
roles.view |
Get role details |
| PUT | /cms/roles/:id |
roles.update |
Update role and permissions |
| DELETE | /cms/roles/:id |
roles.delete |
Delete custom role |
| Method | Endpoint | Permission | Description |
|---|---|---|---|
| GET | /cms/staff |
staff.view |
List staff members |
| POST | /cms/staff |
staff.create |
Add staff member |
| GET | /cms/staff/:id |
staff.view |
Get staff details |
| PUT | /cms/staff/:id |
staff.update |
Update staff role/status |
| DELETE | /cms/staff/:id |
staff.delete |
Remove staff member |
| Method | Endpoint | Permission | Description |
|---|---|---|---|
| GET | /cms/permissions |
Auth required | List all permissions by module |
| Method | Endpoint | Permission | Description |
|---|---|---|---|
| GET | /cms/customers |
customers.view |
List customers |
| POST | /cms/customers |
customers.create |
Create customer |
| GET | /cms/services |
services.view |
List services |
| POST | /cms/services |
services.create |
Create service |
| GET | /cms/bookings |
bookings.view |
List bookings |
| POST | /cms/bookings |
bookings.create |
Create booking |
| GET | /cms/inquiries |
inquiries.view |
List inquiries |
| GET | /cms/referrals/me |
Auth required | Get/create current user's referral code and stats |
| POST | /cms/referrals/claim |
Auth required | Claim referral for the current account |
| Method | Endpoint | Auth | Description |
|---|---|---|---|
| POST | /api/device-check |
Optional | Device fingerprint risk scoring for referral abuse checks |
- Open the Website Builder Dashboard at http://localhost:3001
- You'll be redirected to the Register page
- Register with your email, password, full name, business name, and subdomain
- After registration, you'll land on the Dashboard
- From the sidebar, navigate to:
- Instances — view and create business instances
- Roles — create custom roles with specific permissions
- Staff — add team members and assign roles
- Use the Instance Switcher (top of sidebar) to switch between business instances
- Logout from the user menu at the bottom of the sidebar
├── apps/
│ ├── website-builder-api/ # Express API server (port 3002)
│ │ └── src/
│ │ ├── controllers/ # Route handlers
│ │ ├── middleware/ # Auth, tenant, error handling
│ │ ├── routes/ # Route definitions (cms/, web/, auth/)
│ │ └── validators/ # Zod request validation
│ │
│ └── website-builder-web/ # Next.js Website Builder web app (port 3001)
│ ├── app/ # App Router pages
│ │ ├── login/
│ │ ├── register/
│ │ ├── forgot-password/
│ │ ├── reset-password/
│ │ └── dashboard/ # Protected dashboard pages
│ ├── components/ # Sidebar, InstanceSwitcher
│ ├── contexts/ # AuthContext provider
│ └── lib/ # API client utility
│
├── packages/
│ ├── auth/ # JWT + password services
│ ├── core/ # Shared types, constants, logger
│ ├── database/ # Prisma schema, client, migrations
│ └── config/ # Shared TypeScript/ESLint config
│
├── .env # Root environment variables
├── .env.example # Template for .env
├── turbo.json # Turborepo config
└── package.json # Root workspace config
The platform uses a two-level JWT system:
- Session Token — Contains
userIdandemail. Issued on login before selecting an instance. - Tenant-Scoped Token — Contains
userId,email,tenantId, androle. Issued when selecting or switching a business instance.
RBAC (Role-Based Access Control):
- 4 default system roles per instance: Owner, Admin, Staff, Read Only
- Custom roles can be created with any combination of 30 permissions
- Owner role bypasses all permission checks
If you see EADDRINUSE errors:
# Windows — find and kill process on a port
netstat -ano | findstr :3002
taskkill /PID <pid> /F
# macOS/Linux
lsof -i :3002
kill -9 <pid>Make sure packages/database/.env exists with your DATABASE_URL.
Check that .env has CORS_ORIGIN set as comma-separated origins:
CORS_ORIGIN=http://localhost:3001,http://localhost:3002Ensure apps/website-builder-web/postcss.config.js uses @tailwindcss/postcss (not tailwindcss directly):
module.exports = {
plugins: {
'@tailwindcss/postcss': {},
},
};Ensure apps/website-builder-web/.env.local has:
NEXT_PUBLIC_API_URL=http://localhost:3002This project has been developed, refactored, and maintained in partnership with advanced AI agent systems, primarily using:
- Google Antigravity (Gemini): Utilized for visual layout builder updates, rich TipTap blog integration, sitemap generations, and multi-tenant scoping fixes.
- Claude Code: Utilized for architecture cleanups, package restructuring, and configuration optimizations.
