Top-level structure
| Path | Purpose |
|---|---|
app/ | App Router routes, layouts, and API routes |
components/ | Reusable application components |
components/ui/ | shadcn/ui primitives |
config/ | Central config for site, navigation, pricing, design presets, roadmap, status, and open startup |
content/blog/ | MDX blog posts |
emails/ | React Email templates |
hooks/ | Client hooks such as auth, payment, subscription, and AI chat |
lib/ | Shared helpers, integrations, SEO, validations, and rate limiting |
lib/data/ | Data access layer for profiles, payments, subscriptions, admin, audit logs, and webhook events |
lib/payments/ | Midtrans and Doku integrations |
lib/ai/ | AI provider, middleware, and usage tracking |
lib/storage/ | Avatar storage helpers |
supabase/migrations/ | SQL migrations |
tests/ | Unit, DOM, and route tests |
e2e/ | Playwright smoke tests |
How the app is divided
Route groups
app/(marketing)/for public pagesapp/(dashboard)/for logged-in user areasapp/auth/for authentication flows
Rendering model
- Server components are the default.
- Client components are used for interactive forms, theme switching, design switching, charts, and browser-only hooks.
- The browser Supabase client is created in
lib/supabase/client.ts. - The server Supabase client is created per request in
lib/supabase/server.ts.
Auth flow
- The user opens an auth page.
- The client form calls Supabase auth APIs.
proxy.tsandlib/supabase/proxy.tsrefresh session cookies on later requests.- Protected routes such as
/dashboardand/admincheck whether the user is signed in. /adminalso checks theadminrole fromuser_roles.
Billing and payment flow
- The user picks a plan from the billing UI.
- The client calls
POST /api/payments. - The server reads the price from
config/subscriptions.ts. - The server inserts a
paymentsrecord withPENDINGstatus. - The server returns either a Midtrans Snap token or a Doku checkout URL.
- The payment provider sends events to the matching webhook route.
- The webhook route verifies the signature, records the event, and activates the subscription on successful payment.
Retry-safe webhook flow
Midtrans and Doku webhooks do not write state without protections. The repository uses:- the
webhook_eventstable, - an idempotent claim RPC,
processedorfailedstatuses,- audit logs for traceability.
Profile and avatar flow
- The user requests a signed upload URL from
POST /api/profile/avatar. - The file is uploaded into the
avatarsbucket. - The user saves avatar references through
POST /api/profile. - If the user replaces an avatar, the old object is cleaned up.
Data access layer
The repository does not place every query directly inside route files. Much of the access logic is split intolib/data/*, such as:
payments.tssubscriptions.tsprofiles.tsadmin.tsaudit-logs.tswebhook-events.tsuser-roles.ts
Rate limiting
Public forms, payments, and AI uselib/rate-limit.ts.
- If
SUPABASE_SERVICE_ROLE_KEYis available, the repository uses a persistent Supabase-backed rate-limit store. - If not, it falls back to an in-memory local store.
- Responses include
X-RateLimit-*headers.
When this page is most useful
This page matters most when you:- want to refactor folders,
- want to add a new route,
- need to understand why auth session cookies stay synchronized,
- need to debug billing or webhook behavior.