Firebase Service adalah service notifikasi push (FCM) dan verifikasi Firebase Phone Auth untuk Kesles Merchant. Service live di production, loopback-only, dan menjadi satu-satunya pemilik Firebase Admin SDK di seluruh sistem — caller lain (core_api, dashboard_api, payment_service, marketing_service) berkomunikasi via HTTP internal.
1. Identitas Service
| Atribut | Nilai |
|---|
| Nama service | firebase_service |
| Port | 8093 (loopback 127.0.0.1) |
| Main DB | db_kesles_merchant_notification (schema notification) |
| VM dir | /home/enalfarid/kesles_merchant/merchant_firebase/ |
| Binary | firebase-service (Linux ELF amd64) |
| Systemd unit | firebase-service.service |
| Repo path | services/firebase_service/ |
Firebase Admin SDK berada satu-satunya di services/firebase_service/internal/push/fcm.go. merchant_core_api tidak punya Firebase SDK; semua FCM call dari core_api lewat HTTP via merchant_core_api/internal/fcmservice/client.go ke firebase_service:8093.
2. Tanggung Jawab
- Kirim push FCM (single + bulk) dengan auto-deactivate token saat
UNREGISTERED.
- Kelola registry push token (
notification.fcm_push_tokens) — register/refresh idempotent, list per user/merchant, soft-deactivate.
- Verifikasi Firebase ID token untuk Phone Auth (RS256 + Google JWKS) dan tulis audit ke
notification.phone_verifications.
- Broadcast push (app_update / announcement / maintenance) dengan worker dispatcher in-process.
- Per-user-global notification preferences (opt-out, lintas merchant).
- KPI agregat (online users, active today) untuk dashboard summary.
- Push alert transaksi & settlement yang dipanggil payment_service.
3. Endpoint
Semua endpoint /internal/* butuh header X-Internal-API-Key (constant-time compare). /health dan /ready publik.
| Method | Path | Auth | Tujuan |
|---|
GET | /health | none | Liveness |
GET | /ready | none | Readiness — DB ping + Firebase config check |
POST | /internal/fcm/send | API key | Kirim push 1 token (idempotent middleware), auto-deactivate jika UNREGISTERED |
POST | /internal/fcm/send-bulk | API key | Kirim push banyak token (idempotent middleware) |
POST | /internal/fcm/tokens | API key | Register / refresh push token (idempotent) |
GET | /internal/fcm/tokens?user_id=X / ?merchant_id=X | API key | List token aktif per user/merchant |
POST | /internal/fcm/tokens/lookup | API key | Bulk lookup token by user_ids |
GET | /internal/fcm/tokens/active | API key | Enumerate token aktif (paginated) untuk audience resolver |
GET | /internal/fcm/tokens/device | API key | Known-device check by user_id+device_id |
DELETE | /internal/fcm/tokens/{push_token} | API key | Soft-deactivate token |
GET | /internal/fcm/kpi | API key | KPI agregat online_users + active_today_users |
GET/POST/dst | /internal/fcm/templates · /internal/fcm/templates/{id} | API key | CRUD katalog template (via dashboard_api proxy) |
GET | /internal/fcm/message-logs | API key | Message log read-only untuk panel dashboard |
GET | /internal/fcm/overview | API key | KPI overview untuk panel Firebase Overview & Health |
GET | /internal/fcm/send-volume | API key | Timeseries volume kirim untuk chart |
GET | /internal/fcm/provider-health | API key | Health provider/platform |
POST | /internal/firebase-auth/verify-phone-token | API key | Verifikasi Firebase ID token Phone Auth (RS256 + JWKS) |
GET/PUT | /internal/users/{user_id}/notification-prefs | API key | Per-user-global opt-out (user_notification_prefs) |
POST/GET | /internal/broadcasts | API key | Broadcast CRUD create / list |
GET/DELETE | /internal/broadcasts/{id} · /internal/broadcasts/{id}/sends | API key | Detail / cancel / audit log per delivery |
POST | /internal/push/transaction-alert | API key | Push "Pembayaran Masuk" (dipanggil payment_service) |
POST | /internal/push/settlement-complete | API key | Push "Settlement Berhasil" (dipanggil payment_service) |
4. Integrasi & Routing
Firebase Admin SDK adalah single-owner di firebase_service. Caller lain memakai HTTP internal:
| Caller | Mekanisme | Endpoint yang dipakai |
|---|
merchant_core_api | internal/fcmservice/client.go (HTTP) | /internal/fcm/send, /send-bulk, /tokens, /tokens/lookup, /tokens/device, /internal/firebase-auth/verify-phone-token |
dashboard_api | proxy + KPI client | /internal/fcm/kpi, /templates, /message-logs, /overview, /send-volume, /provider-health, /internal/broadcasts* (role gate {superadmin, admin}) |
payment_service | HTTP async (fire-and-forget) | /internal/push/transaction-alert, /internal/push/settlement-complete |
marketing_service | worker internal/worker/fcmclient.go (FCM_SERVICE_BASE_URL default http://127.0.0.1:8093) | /internal/fcm/tokens/active, /internal/fcm/send-bulk |
Broadcast push diproses oleh worker in-process (internal/worker/dispatcher.go): poll notification.fcm_broadcasts (SELECT FOR UPDATE SKIP LOCKED, status queued → sending), resolve audience (all / platform / app_version_lt / user_ids), cek user_notification_prefs, dispatch via push.Client.Send in-process, lalu finalize. Worker diaktifkan oleh BROADCAST_WORKER_ENABLED=true.
5. Konfigurasi (env)
| Env | Wajib (production) | Keterangan |
|---|
APP_ENV | ya | production di VM |
APP_PORT | — | default 8093 |
POSTGRES_DSN | ya | DSN db_kesles_merchant_notification |
INTERNAL_NOTIFICATION_API_KEY | ya | Shared secret X-Internal-API-Key; identik dengan caller |
FIREBASE_PROJECT_ID | ya | kesles-merchant |
FIREBASE_CLIENT_EMAIL | ya | Service account Admin SDK |
FIREBASE_PRIVATE_KEY | ya | Service account private key |
FIREBASE_JWKS_URL | — | Override DILARANG di production (fail-fast); dev only |
REDIS_ADDR / REDIS_USERNAME / REDIS_PASSWORD / REDIS_DB | — | Idempotency store; kosong → middleware no-op |
BROADCAST_WORKER_ENABLED | — | true aktifkan worker broadcast (default off) |
validateConfig fail-fast saat boot di production jika salah satu dari INTERNAL_NOTIFICATION_API_KEY, FIREBASE_PROJECT_ID, FIREBASE_CLIENT_EMAIL, FIREBASE_PRIVATE_KEY, POSTGRES_DSN kosong, atau jika FIREBASE_JWKS_URL di-set.
6. Security Hardening (konfigurasi saat ini)
- Internal auth:
X-Internal-API-Key dibandingkan dengan subtle.ConstantTimeCompare (timing-safe). Key kosong → semua endpoint internal reject 503.
- HTTP timeouts:
ReadHeaderTimeout 5s, ReadTimeout 30s, WriteTimeout 30s, IdleTimeout 120s.
- Body limit:
maxBodySizeMiddleware membatasi body POST/PUT/PATCH maksimal 1 MB.
- DB pool:
MaxOpenConns 25, MaxIdleConns 5, ConnMaxLifetime 5m.
- Driver:
pgx/v5 via stdlib adapter (sql.Open("pgx", dsn)).
- Idempotency: middleware Redis-backed pada
/internal/fcm/send + /send-bulk (namespace idempo:fcm); fail-open jika Redis tidak reachable.
- Logging: slog structured event (mis.
fcm_send dengan taxonomy classifyFCMError), tanpa PII.
- Graceful shutdown: SIGTERM/SIGINT memberi window in-flight request sebelum stop.
Observability — taxonomy classifyFCMError
| Class | Trigger |
|---|
ok | err == nil |
fcm_unregistered | token revoked / app uninstalled |
fcm_auth | HTTP 401/403 — masalah kredensial service account |
fcm_quota | HTTP 429 — FCM rate limit |
fcm_5xx | HTTP 5xx — FCM transient |
fcm_4xx | HTTP 4xx lain |
fcm_oauth | token endpoint gagal |
network_timeout | error timeout/deadline/i/o |
network | error dial/connection/tls/no such host |
not_configured | kredensial kosong |
unknown | fallback |
7. Database (schema notification di db_kesles_merchant_notification)
| Tabel | Fungsi |
|---|
fcm_push_tokens | Sole source of truth registry push token. Tenant-aware (tenant_id), tanpa FK cross-DB |
phone_verifications | Audit verifikasi Phone Auth (append-only, best-effort) |
fcm_broadcasts + fcm_broadcast_sends | Broadcast + delivery log, UNIQUE (broadcast_id, user_id) idempotency |
user_notification_prefs | Opt-out per-user-global lintas merchant (beda dengan merchant.merchant_user_notification_prefs per-merchant per-event) |
8. VM Deployment
| Atribut | Nilai |
|---|
| Host VM | kesles-merchant |
| Folder | /home/enalfarid/kesles_merchant/merchant_firebase/ |
| Binary | firebase-service |
| Systemd unit | firebase-service.service |
| Port | 127.0.0.1:8093 (loopback) |
| Logs | journalctl -u firebase-service.service |
9. Referensi