API dan auth
Bagaimana frontend menjangkau backend, dan bagaimana backend memutuskan siapa boleh melakukan apa.
Cara halaman ini diverifikasi
Dibaca pada 2026-09-15 dari config GraphQL backend, main.ts, guard dan strategy, dan setup Apollo/axios kedua frontend.
Tiga jalan masuk
Backend membuka tiga surface, semuanya di bawah prefix global APP_PREFIX (be-api di lokal, gapi di prod):
- GraphQL di
/{prefix}/graphql— code-first (autoSchemaFile: true, tanpaschema.gqlyang di-commit). Semua read dan write apa pun tanpa file. Hanya sebagian modul yang ada diincludeGraphQL: admins, catalogs, users, orders, promotions, journals. - Controller REST — create/update yang meng-upload file (multipart
FormData), misalnyaPOST catalogs/products. Frontend admin memakai axios untuk ini; storefront jarang membutuhkannya. - REST
webhooks/*— callback masuk dari DOKU, Biteship dan WMS. Bukan GraphQL. Lihat Third parties.
Penamaan operasi: GraphQL storefront diberi prefix front* (frontProducts, frontMemberCart); GraphQL admin/CMS polos (products, createVoucher).
Ada satu ValidationPipe global (custom exceptionFactory yang meratakan error class-validator) dan tidak ada exception filter, interceptor, atau guard global. GraphQL punya formatError custom untuk error add-to-cart (mengembalikan message, code, dan cartId opsional).
Dua realm auth
User admin dan member storefront adalah realm JWT terpisah, masing-masing dengan secret dan strategy sendiri. Guard dipasang per-resolver — tidak ada guard global.
| Admin / CMS | Storefront / member | |
|---|---|---|
| Op login | login, loginWithGoogle | loginAsMember |
| Strategy | jwt (JWT_ACCESS_TOKEN_SECRET_KEY), jwt-refresh | jwt-member (JWT_ACCESS_TOKEN_STOREFRONT_SECRET_KEY), jwt-member-refresh |
| Guard | JwtAuthGuard, LastActivityGuard, RoleAbilityGuard | JwtMemberAuthGuard |
| Ekstra | Google OAuth (passport-google-oauth20); auto-logout saat idle | verifikasi email saat register; password bcrypt |
Resolver admin yang khas menumpuk ketiga guard: @UseGuards(JwtAuthGuard, LastActivityGuard, RoleAbilityGuard). @Public() melewatinya (login, reset-password, homepage yang menghadap storefront). LastActivityGuard menegakkan auto-logout saat idle setelah CMS_AUTO_LOGOUT_MAX_INACTIVE_SECONDS (default 1800).
RBAC (admin saja)
Role membawa sekumpulan menu, masing-masing dengan lima ability: view, add, update, delete, export. Modelnya roles ↔ roles_menus ↔ menus.
- Backend menegakkannya dengan
@RoleCheck({ slug, action })+RoleAbilityGuard(src/admins/guards/role-check.guard.ts), yang memeriksarole.menusmilik admin untukslugmenu dan ability itu. - Frontend admin mencerminkannya di sisi client dengan
useAccess(slug, action)(src/hooks/useAccess.tsx): ia menyembunyikan item sidebar dan mengarahkan halaman yang ditolak ke/404. Permission berasal dari querymebackend, bukan hard-coded.
Member tidak punya layer role/ability — sebuah member terautentikasi atau tidak.
Bagaimana frontend mengirim token
Kedua frontend menyimpan token di session JWT NextAuth, bukan di localStorage, dan mengirimnya sebagai Authorization: Bearer <token> pada setiap request.
- Storefront — provider Credentials NextAuth →
loginAsMember. Link Apollo dan client axios keduanya membaca session dan melampirkan bearer. Saat access token kedaluwarsa, callbackjwtmemanggilfrontRefreshToken; saat gagal, client memaksa sign-out ke/auth/signin?autologout=true. - Admin — Credentials NextAuth + Google. Link Apollo melampirkan bearer; client axios (
src/lib/http.ts) melakukan hal sama untuk upload. Ia auto-logout pada pesan backend"no activity"dan"Access token expired".
Client GraphQL admin memakai cache: "no-store", jadi ia tidak bergantung pada cache Apollo.