Codebase
Customization
Rebranding, extending, and where things live in the codebase.
Rebrand checklist
/admin/settings— name, logo text, tagline, brand color, links, pricing copy. This is most of a rebrand and touches no code..env—NEXT_PUBLIC_SITE_URLandNEXT_PUBLIC_SITE_NAME(metadata identity), then rebuild.- Replace the icon assets in
apps/web/app/:icon.png,apple-icon.png,opengraph-image.png. - The wordmark itself (the chevron SVG) lives in
apps/web/app/(marketing)/components/header/index.tsxandapps/web/app/admin/components/brand-mark.tsx.
Codebase map
apps/web/app/(marketing)/ public site (pages + components)
apps/web/app/admin/ operations console
apps/web/app/api/ REST API + MCP + cron pipelines
apps/web/lib/ service layer (tools, news, posts, settings, audit)
packages/database/ Prisma schema + client + seed
packages/design-system/ shadcn/ui components + theme tokens (shared)
packages/auth/ better-auth configuration
packages/payments/ Stripe client + keys
packages/seo/ metadata, JSON-LD, build-time identity
Design language
Both the public site and the admin console share packages/design-system. The rules that keep it coherent: emerald --primary, warm-dark mode, hairline ring-1 ring-border borders, rounded-lg/rounded-xl only, lucide icons at strokeWidth={1.75}, transitions at duration-200 ease-out. Follow them and new pages look native.
Adding a content type
- Model in
packages/database/prisma/schema.prisma→pnpm --filter @repo/database build→npx prisma db push. - Service functions in
apps/web/lib/. - Public page under
app/(marketing)/. - Admin page under
app/admin/(dashboard)/— copy an existing module (page + actions + client list); the shared kit (DataTable,PageHeader,StatusBadge,ConfirmButton, …) does most of the visual work. - Server actions: guard with
assertAdmin(), validate with zod, write,revalidatePath,logAudit.
Renaming or removing the sales pages
The template ships two pages for selling: app/(marketing)/template (the marketing pitch for the template itself) and app/(marketing)/pricing.
- If you don't resell anything, delete
app/(marketing)/template. The/pricingpage keeps working as your own pricing page — repoint it or its Checkout URL setting at your product. - Nav entries for both live in
app/(marketing)/components/header/nav-data.ts(the "Template" group) andapp/(marketing)/components/footer.tsx. Remove the entries you don't want after deleting a page. - To sell your own copy or product, leave the pages and just set the Checkout URL in
/admin/settings → Pricing— see Configuration.
Demo mode: a public sandbox of your admin
The console sells itself best when people can touch it. DEMO_MODE turns a deployment into a public sandbox: a one-click "Enter the demo console" button on /admin/login (using DEMO_EMAIL / DEMO_PASSWORD), a banner across the console, and a strip on the public site pointing visitors at /template. The demo owner has real write access — a scheduled snapshot restore is what keeps the sandbox clean.
Never set DEMO_MODE on your production site. The recipe:
- Deploy a second instance (e.g.
demo.your-domain.com) from a separate checkout with its own database andDEMO_MODE=true,DEMO_EMAIL,DEMO_PASSWORDin its env. A separate checkout matters: two processes must not share one.nextcache. - Prepare it once — run the setup wizard with exactly the demo credentials, load the seed, adjust settings until it demos well.
- Snapshot it:
pg_dump "$DEMO_DATABASE_URL" -F c -f demo-snapshot.dump - Schedule
scripts/reset-demo.shhourly — it restores the snapshot, clears the Next cache, and restarts the process (a few seconds of downtime). - Link it: set the Demo URL in
/admin/settingson your production site and/templategrows a "Try the admin demo" button.
Don't give the sandbox real API keys (DeepSeek, Stripe, R2) — without them the AI pipelines skip their editorial steps and the paid flows stay hidden, which is exactly right for a demo.
Monorepo notes
The single deploy unit is apps/web; apps/studio is a local Prisma Studio launcher for browsing the database. See Project structure for the full tour.