Top-level structure
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.