Dashboard API Architecture
services/dashboard_api adalah dedicated backend untuk apps/merchant_dashboard (Flutter web admin). Production-grade Go service yang melayani operator/admin Kesles — terpisah dari merchant_core_api yang melayani mobile_user dan OLTP money-path.
Status: 🟢 LIVE production — ~130 file Go, ~44.000 LOC, monolitik package app.
Lihat services/dashboard_api/README.md untuk dokumentasi operasional lengkap.
Boundary
dashboard_api owns
- Admin/operator dashboard session & auth (facade ke auth_service + core_api)
- Master data CRUD: partners, reps, credentials, merchants, devices, terminal stock
- Sales workflow: quotations, sales orders, shipping orders
- Purchasing: vendors, PO, GRN, invoices, landed cost, WAC
- Settings: shipping origins/rates/fee, company profile, bank accounts, system settings, QRIS config, payment policy
- Reports & planning: device WAC, growth projection, deployment ramp, revenue by merchant/partner
- Internal dashboard KPI & summary (native query sejak 2026-05-18)
- Users-access: dashboard users, sessions, devices, platform access, roles, permissions, RBAC, audit logs
- Marketing: promo content (native) + promo schedules (proxy marketing_service) + broadcasts (proxy firebase_service)
- PSP integration: inbound HMAC webhook dari payment.kesles.com + outbound dispatch
- Dev tools: WA/FCM/partner/phone-auth tester, email bulk, beta testers (super-admin + ENABLE_DEV_TOOLS)
dashboard_api does NOT own
- Mobile user auth (OTP, JWT) — milik
merchant_core_api - Transaction write path (QRIS, payment callback) — milik
payment_service - Order lifecycle state machine — milik
order_service - Product catalog mutations — milik
inventory_service - Push notification / WA / email queue — milik masing-masing notification service
- Partner domain mutations (cutover 2026-06-02) — milik
partner_service - PSP tester + PSP External tester (cutover 2026-06-16) — dipindah ke
integration_api:8092; handlerdashboard_dev_psp_tester.gosudah dihapus
Service Structure (aktual)
services/dashboard_api/
├── cmd/
│ ├── server/ — HTTP server entry point (main.go)
│ ├── psp-encrypt/ — CLI: enkripsi PSP API secret ke "enc:v1:..." format
│ └── psp-sign-post/ — CLI: test HMAC signature untuk PSP integration
├── internal/app/ — Seluruh business logic, monolitik package app (~130 file)
├── .env.example
├── .env.development
├── .env.production
├── README.md
├── go.mod
└── go.sum
Tidak ada sub-package di internal/ — semua flat di internal/app/. Ini desain eksplisit (bukan hutang teknis).
Lihat README.md §Code organization di services/dashboard_api/README.md untuk daftar lengkap file per-kategori.
Service Clients
dashboard_api memanggil service lain via HTTP (bukan direct DB ke domain milik service lain):
| Client | Field di Server | Service | Port | Kapan nil |
|---|---|---|---|---|
authAdminClient | s.authAdmin | auth_service | 8081 | AUTH_SERVICE_BASE_URL kosong |
corePSPClient | s.corePSPClient | merchant_core_api | 8080 | CORE_API_BASE_URL kosong |
inventoryClient | s.inventoryClient | inventory_service | 8084 | INVENTORY_SERVICE_BASE_URL kosong |
orderClient | s.orderClient | order_service | 8083 | ORDER_SERVICE_BASE_URL kosong |
partnerServiceClient | s.partnerClient | partner_service | 8086 | PARTNER_SERVICE_BASE_URL kosong |
paymentServiceClient | s.paymentClient | payment_service | 8085 | PAYMENT_SERVICE_BASE_URL kosong |
Semua client nil-safe — kalau nil, handler fallback ke SQL path lokal (untuk domain yang masih punya SQL fallback) atau return error/503.
Auth header: X-Internal-API-Key untuk semua internal call.
Data Flow
apps/merchant_dashboard (Flutter Web, operator Kesles)
│
│ HTTPS (JWT dashboard session)
▼
services/dashboard_api (:8082)
│
├─► merchant_core_api (:8080) via s.httpClient + s.corePSPClient
│ /dashboard/auth/* ← auth facade (login, OTP, me, logout)
│ /internal/dashboard/txn-trend ← OUT-OF-SCOPE forwarder (blocked daily_agg)
│ /internal/dashboard/merchant-ranking ← OUT-OF-SCOPE forwarder
│
├─► auth_service (:8081) via s.authAdmin (authAdminClient)
│ /internal/admin/users/* ← admin user lookup, staff management
│
├─► order_service (:8083) via s.orderClient
│ /internal/quotations/* ← quotation dual-write ke order domain
│
├─► inventory_service (:8084) via s.inventoryClient
│ /internal/* ← product catalog reads (Phase 7C)
│
├─► payment_service (:8085) via s.paymentClient
│ /internal/transactions ← recent transactions (Phase 3 reader)
│ + /api/dashboard/transactions (Transaction History)
│ → enrich merchant_name+merchant_code via merchant.merchants
│
├─► partner_service (:8086) via s.partnerClient (partnerServiceClient)
│ /internal/partners/* ← partner CRUD + reps + credentials + webhooks
│ (cutover 2026-06-02; SQL fallback masih ada)
│
├─► firebase_service (:8093) via s.httpClient + INTERNAL_NOTIFICATION_API_KEY
│ /internal/broadcasts/* ← broadcast pengumuman (proxy)
│
├─► marketing_service (:8095) via s.httpClient
│ /internal/promo-schedules/* ← jadwal kirim promo (proxy)
│
├─► db_kesles_merchant (direct SQL via s.db)
│ merchant.* ← merchants, devices, sales, purchasing, settings
│ psp.* ← PSP merchant data + HMAC keys
│ dashboard.* ← dashboard users, sessions, RBAC
│ partner.* ← SQL fallback (jika partnerClient nil)
│ (+ db_reference via s.referenceDB — opsional)
│
└─► db_kesles_merchant_planning (direct SQL via s.planningDB — PLANNING_POSTGRES_DSN)
planning.* ← planning defaults, scenarios (sole writer; mirror code removed 2026-06-16)
Auth & Middleware
Dashboard auth endpoints (/dashboard/auth/*) — prefix tanpa /api/, proxied ke merchant_core_api:
POST /dashboard/auth/login
POST /dashboard/auth/verify-otp
POST /dashboard/auth/forgot-password
POST /dashboard/auth/reset-password
GET /dashboard/auth/me
POST /dashboard/auth/logout
Nginx strip /merchant/dashboard-api/ prefix sebelum forward ke :8082. Jadi browser kirim /merchant/dashboard-api/dashboard/auth/login → Go menerima /dashboard/auth/login.
Middleware chain: withSecurityHeaders → withCORS → mux
Dashboard RBAC: withDashboardAuth middleware validate JWT + inject profile ke context. Handler-handler tertentu panggil requirePermission() untuk RBAC fine-grained.
PSP Integration
dashboard_api adalah satu-satunya entry point untuk event dari payment.kesles.com:
payment.kesles.com
│ HMAC-SHA256 + IP allowlist
▼
/api/psp/v1/* (psp_auth_middleware.go — verifikasi HMAC per tenant)
├── psp_merchants.go — lookup merchant data
├── psp_event_receiver.go — terima payment event + dispatch notifikasi
└── psp_push_notifier.go — forward FCM push ke mobile_user
Outbound (kesles → payment.kesles.com) dikontrol via PSP_OUTBOUND_MODE (default disabled).
Internal Dashboard KPI / Summary
8 dari 10 endpoint /internal/dashboard/* sudah native (langsung query s.db) sejak Phase 3 extraction selesai 2026-05-18. 2 endpoint tetap forwarder ke merchant_core_api:
| Endpoint | Status | Alasan |
|---|---|---|
/internal/dashboard/kpi | ✅ Direct | — |
/internal/dashboard/summary | ✅ Direct | — |
/internal/dashboard/overview | ✅ Direct | — |
/internal/dashboard/pending-merchants | ✅ Direct | — |
/internal/dashboard/recent-transactions | ✅ Direct | — |
/internal/dashboard/device-summary | ✅ Direct | — |
/internal/dashboard/reports/revenue-by-merchant | ✅ Direct | — |
/internal/dashboard/reports/merchant-transactions | ✅ Direct | — |
/internal/dashboard/reports/revenue-by-partner | ✅ Direct | endpoint baru, post-extraction |
/internal/dashboard/txn-trend | ⏸ Forward ke core_api | blocked transactions_daily_agg |
/internal/dashboard/merchant-ranking | ⏸ Forward ke core_api | blocked transactions_daily_agg |
Transport dikontrol via env INTERNAL_DASHBOARD_TRANSPORT=direct (production).
Transaction History — /api/dashboard/transactions
Endpoint terpisah dari /internal/dashboard/* — melayani panel Transaction History di Flutter dashboard. Sumber data: payment_service via paymentClient, diperkaya (enrichTxnMerchantNames) dengan merchant_name + merchant_code dari merchant.merchants (db_kesles_merchant).
Filter merchant_id, bukan outlet_id: transaksi sandbox/PSP tester tidak memiliki outlet_id (kolom NULL), sehingga filter outlet_id menghasilkan 0 baris. Outlet selector di Flutter mengirim merchant_id dari outlet yang dipilih — bukan outlet_id outlet itu sendiri. Detail: services/dashboard_api/README.md §Transaction History.
Chart KPI hanya hitung transaksi sukses: tiga widget visualisasi di dashboard_sections.dart — daily bar chart, channel donut, dan top merchants — sudah difilter transactionStatus == 'success'.
Deployment
| Item | Nilai |
|---|---|
| Systemd unit | merchant-dashboard-api.service |
| Folder VM | merchant_dashboard_api/ |
| Port | 8082 |
| Binary path VM | merchant_dashboard_api/dashboard-api |
| Environment | APP_ENV=production (wajib di .env line 1) |
| Nginx upstream (kesles.com) | proxy_pass http://localhost:8082/; via /merchant/dashboard-api/ |
| Nginx upstream (api-merchant) | proxy_pass http://localhost:8082; via /api/psp/v1/ (tanpa trailing slash — path preserved) |
Nginx Security Hardening (Audit #4 — 2026-06-11)
Tiga file nginx aktual di VM:
| File VM | Scope |
|---|---|
/etc/nginx/nginx.conf | limit_req_zone — 3 zone: api_general (10r/s), api_auth (5r/s), psp_hook (30r/s) |
/etc/nginx/sites-available/kesles.com.conf | 7 security headers (HSTS, X-Frame, X-Content, XSS, Referrer, Permissions, CSP) + limit_req di 8 location block |
/etc/nginx/sites-available/api-merchant.kesles.com.conf | 6 security headers (HSTS, X-Frame, X-Content, XSS, Referrer, CSP default-src 'none') + limit_req + client_max_body_size di 2 location |
Local ref: deploy/nginx/kesles.com.conf, deploy/nginx/api-merchant.kesles.com.conf, deploy/nginx/kesles-merchant.conf (combined view).
Dokumen Terkait
services/dashboard_api/README.md— dokumentasi operasional lengkapdocs/services/dashboard/cleanup-plan.md— cleanup plan aktif (Tier 2.3–4.5 masih pending)docs/services/dashboard/extraction-plan.md— internal dashboard extraction (Phase 0–3 DONE 2026-05-18)docs/architecture/whatsapp-dual-dispatch.md— WA dual-channel patternrunbooks/setup-api-merchant-subdomain.md— api-merchant.kesles.com setup + status LIVE 2026-06-11