Lewati ke konten

Gambaran Arsitektur

Catera adalah aplikasi single-page React yang di-build statis (Vite) dan dideploy sebagai SPA di Vercel. Backend aktifnya adalah Supabase (Postgres + Auth + Row Level Security + RPC + Edge Function). Sebuah jalur kompatibilitas Convex masih dipertahankan di kode untuk rollback, tapi tidak lagi jadi backend produksi default.

flowchart TB
UI["Browser: aplikasi React"]

subgraph Supabase ["Supabase (backend aktif)"]
  Auth["Auth"]
  DB[("Postgres + RLS + RPC")]
  EF["Edge Functions"]
end

ConvexBackend[("Convex (jalur rollback)")]

UI -->|"VITE_BACKEND_PROVIDER=supabase"| Auth
UI --> DB
UI --> EF
EF --> DB
UI -.->|"rollback saja"| ConvexBackend

Enam edge function yang berjalan di Supabase: ai-chat, ai-daily-summary, ai-demand-prediction, connection-test, portal-order-detail, dan push-notifications. Detail tiap fungsi ada di Referensi Edge Function.

Pemilihan backend ditentukan satu env var di build time, bukan deteksi runtime: VITE_BACKEND_PROVIDER=supabase mengaktifkan jalur Supabase; nilai lain jatuh ke Convex. Logikanya terpusat di satu modul kecil supaya tidak ada cabang provider yang tersebar di seluruh kode:

export const isSupabaseBackend = backendProvider === "supabase";
export function selectBackend<TSupabase>(supabase: TSupabase, convex: unknown): TSupabase {
return (isSupabaseBackend ? supabase : convex) as TSupabase;
}

src/App.tsx memakai flag yang sama untuk memilih antara memberi rute publik (portal, katalog publik, display, kiosk) akses langsung ke Supabase tanpa provider React apa pun (“providerless”), atau membungkusnya dengan ConvexReactProvider saat jalur rollback aktif.

  • Rute publik (tanpa login): /cek-pesanan, /portal*, /display/*, /katalog-publik, /pesan-publik, /kiosk/*. Dirender di luar layout aplikasi, tanpa chrome internal (sidebar, bottom nav).
  • Semua rute lain dibungkus <AppLayout> dan tiap elemen rute individual dibungkus <RouteGuard>.
  • RouteGuard (src/components/RouteGuard.tsx) menolak akses untuk: belum login, profil masih pending/blocked, atau role tidak terdaftar untuk path tersebut — lalu mengarahkan ke /pending-approval atau /forbidden.
  • Keputusan akses per-path dibaca dari peta statis ROUTE_ACCESS di src/lib/role-access.ts, dicocokkan dengan role yang sudah dinormalisasi lewat normalizeUserRole() (src/lib/roles.ts).

Detail model peran dan lapisan keamanan di sisi database dijelaskan di Model Keamanan.

Supabase Auth (auth.users) adalah sumber identitas. Trigger on_auth_user_created otomatis membuat baris public.profiles berstatus pending setiap kali ada user Auth baru — aktivasi role tetap aksi manual oleh pemilik/manager, bukan otomatis. Tabel profiles menyimpan role (termasuk alias peran lama) dan status (pending/active/blocked), dan menjadi rujukan semua fungsi RLS lain lewat auth.uid().


Sumber: src/App.tsx, src/lib/backend-provider.ts, src/lib/role-access.ts, src/lib/roles.ts, src/components/RouteGuard.tsx, supabase/migrations/0002_identity_and_rls.sql.