Tech stack
Next.js (App Router)
Version 16 with the App Router, React Server Components, and
output: 'standalone' for containerized deployment. TypeScript throughout.Supabase
Provides PostgreSQL (data), Auth (sessions), and Storage (file uploads) as a managed backend. Accessed via
@supabase/supabase-js.Tailwind CSS
Version 4 via PostCSS. Custom theme variables and shared utility classes (
.glass-card, etc.) are defined in src/app/globals.css.Vercel
Recommended deployment target. Standalone output mode means the app can also run in any Docker-compatible environment.
qrcode.react (wallet QR codes).
Directory structure
Authentication flow
Simple Money uses Supabase Auth with browser-persisted sessions (localStorage, keysb-auth-token-money).
Sign-up / sign-in
The user submits credentials on
/login or /signup. Supabase Auth validates them and writes a JWT session to localStorage via the supabase-js client.Session detection
On every page load,
AuthContext calls supabase.auth.getSession(). If a valid session exists it fetches the corresponding row from profiles and exposes it via the useAuth() hook.Protected route enforcement
The
(user) route group layout (src/app/(user)/layout.tsx) checks AuthContext on the client. If there is no active session the user is redirected to /login.Admin panel isolation
The middleware in
src/middleware.ts rewrites requests to the admin. subdomain into the /admin route segment, and redirects direct /admin access on the main domain to the subdomain. Admin auth is validated inside src/app/admin/layout.tsx.The middleware runs at the Edge and cannot read localStorage. As a result it does not perform token validation — that responsibility falls entirely to the client-side layouts. If you migrate to cookie-based sessions using
@supabase/ssr, you can uncomment the server-side auth check in src/middleware.ts.Data flow
Read operations
Client components query Supabase directly using the singleton client fromsrc/lib/supabase.ts:
Write operations (user actions)
Task completions, deposit submissions, and withdrawal requests also go through thesupabase-js client directly, writing rows that satisfy the RLS insert policies. Commission calculations use Supabase RPC calls to run atomic balance updates in the database.
Write operations (admin actions)
Sensitive mutations — creating users, deleting accounts, assigning bundles — are handled by Next.js API routes under/api/admin/. These routes instantiate a separate Supabase client with the SUPABASE_SERVICE_ROLE_KEY, which bypasses RLS:
File storage
Deposit payment receipts are uploaded to a Supabase Storage bucket nameddeposit_proofs. The upload happens client-side at /deposit using the anon key; a storage policy restricts reads to the file owner and admins.
transactions row as a reference for admin review.
Context providers
All providers are mounted insrc/app/layout.tsx via the <Providers> wrapper component. They are available to every page in the app via their respective hooks.
| Context | Hook | Responsibility |
|---|---|---|
AuthContext | useAuth() | Supabase session, Profile object, sign-out helper |
CurrencyContext | useCurrency() | Format numbers as currency strings based on user preference |
LanguageContext | useLanguage() | Translate UI strings based on profile.language |
ThemeContext | useTheme() | Toggle and persist dark / light mode |
NotificationContext | useNotifications() | In-app notification queue (read, dismiss, mark-all-read) |
SettingsContext | useSettings() | General user preference helpers |