- PHP 40.9%
- HTML 21.4%
- Blade 20.3%
- Vue 10.4%
- Java 4.1%
- Other 2.8%
|
|
||
|---|---|---|
| API | ||
| arsip | ||
| assets | ||
| framework | ||
| MOBILE | ||
| template | ||
| .gitignore | ||
| memory.md | ||
| PRD.md | ||
| README.md | ||
| TechStack.md | ||
Local Push Notification — Intranet (Tanpa FCM)
Aplikasi intranet Android untuk distribusi notifikasi tanpa internet / Firebase Cloud Messaging. Server lokal (PHP 7.4 + JSON file/SQLite) + klien hybrid (Vue 3 + Capacitor) dengan mekanisme polling dan native push via @capacitor/local-notifications. Login Keycloak SSO (username/password), atribut pegawai per akun, target NUP, halaman detail, dan reverse proxy.
Repo:
https://git.pi.interka.my.id/alfatha/appnotifikasi.git· Dokumen:PRD.md·TechStack.md·memory.md·API/README.md·API/admin.html
Fitur Utama (PRD)
- Login Keycloak SSO — form username/password fullscreen di mobile. Token via proxy
POST /api/auth/login(hindari CORS WebView), refresh via/api/auth/refresh, atribut via/api/auth/userinfo(Account API:nup,nama,unitkerja,jabatan,statuspegawaiper akun). - Polling Sinkronisasi — klien
fetchberkala ke server lokal (default 10s) untuk cek pesan baru. SupportX-Forwarded-*untuk reverse proxy. - Native Push — pesan baru → notifikasi Android (status bar) via LocalNotifications. Klik push/daftar → halaman detail. Izin diminta saat app dibuka.
- Simpan Lokal — pesan dipertahankan di
localStorage(notif_store, maks 200), hanya ditandai dibaca (ack_cache). Merge + prune per user, reset saat ganti user. Tombol hapus dibuang. - Permissions & Baterai — request izin notifikasi (Android 13+). Cek battery exemption (
isIgnoringBatteryOptimizations) di Pengaturan + tombol Minta Izin agar polling latar tidak dimatikan sistem. - Pegawai (Employees) — CRUD
nama, nup, unit_kerja, jabatan, status_pegawaidengantokenauto-generate. Atribut tampil dari Keycloak (fallback cocokkan email DB). - Broadcast Ter-target + Visibilitas —
POST /notifications/broadcastdengandevice_ids,unit_kerja/jabatan(AND),target_nups(Excel kolomnup), atau kosong = semua device (flagis_broadcast). Daftar hanya tampilkan broadcast/pesan yang ditujukan ke user tersebut. - Halaman Detail —
GET /notifications/{id}: judul, isi penuh, prioritas, waktu, lampiran, tujuan, status dibaca. - Admin Panel —
API/admin.html(Vue CDN + Tailwind, tanpa login) untuk CRUD pegawai, kirim notifikasi (Semua/NUP Excel/Kelompok), dan Log Notifikasi (tujuan & lampiran). - Reverse Proxy Ready — Nginx/Apache contoh config di
API/nginx.conf.example&API/apache.conf.example. HandleX-Forwarded-Proto,Authorizationpassthrough, dan prefix/apistrip.
Non-Fungsional: interval polling seimbang baterai vs latency; API routable di AP yang sama; kompatibel PHP 7.4 (tanpa match/str_starts_with/fn=>).
Tech Stack
| Komponen | Teknologi | Catatan |
|---|---|---|
| UI Mobile | Vue 3 + custom CSS tema Papan | serif/sans/mono, tanpa framework CSS |
| UI Admin | Vue CDN + Tailwind | tanpa login, API/admin.html |
| Mobile Engine | Capacitor 6 + @capacitor/local-notifications + BackgroundPolling (native) |
wrapper web → APK |
| Backend API | PHP 7.4+ Native | ringan, tanpa framework, JSON + CORS, switch(true) untuk 7.4 |
| Database | JSON file (database/*.json, driver json) |
file-based; bisa switch sqlite/mysql/pgsql |
| Auth | Keycloak OIDC + Bearer API token | mobile login via proxy /api/auth/*; API pakai api_token/employee token |
| Build | Vite + Gradle | APK debug via ./gradlew assembleDebug |
| Proxy | Nginx/Apache | reverse proxy terminasi |
Metode: Backend-First API-Driven — 5 fase (API → UI → Polling → Native → APK + Reverse Proxy). Detail di TechStack.md.
Struktur Project
.
├── PRD.md, TechStack.md, memory.md, README.md
├── API/
│ ├── index.php # Router + semua endpoint (PHP 7.4 compat, switch)
│ ├── admin.html # Admin Panel (Vue CDN, CRUD pegawai + broadcast)
│ ├── config/database.php # api_token + driver (sqlite/mysql/pgsql)
│ ├── nginx.conf.example # Nginx reverse proxy contoh
│ ├── apache.conf.example # Apache reverse proxy contoh
│ ├── .htaccess # URL rewrite + Authorization header
│ ├── run.sh, test.php, cli_test.php
│ └── database/notifikasi.db # auto-create (di-ignore git)
├── MOBILE/
│ ├── src/
│ │ ├── App.vue # UI + polling + native notification + Settings Dialog + profil pegawai
│ │ ├── main.js, config.js # config default (fallback)
│ │ ├── settings.js # dynamic config via localStorage (tanpa rebuild)
│ │ └── ...
│ ├── capacitor.config.js, vite.config.js, package.json
│ ├── dist/ # build output
│ └── android/ # Capacitor Android project (gradle)
└── .gitignore
Quick Start
1) Backend API
cd API
# konfigurasi
nano config/database.php # atur api_token & driver
# jalankan (dev) - langsung
php -S 0.0.0.0:8080 -t . # atau ./run.sh
# cek
curl http://localhost:8080/health # atau /api/health
# → {"status":"ok","timestamp":"..."}
# reverse proxy (prod)
# nginx: cp nginx.conf.example /etc/nginx/sites-available/appnotifikasi && nginx -s reload
# apache: cp apache.conf.example /etc/apache2/sites-available/appnotifikasi.conf && a2ensite appnotifikasi && systemctl reload apache2
# cek via proxy
curl http://notif.local/api/health
Konfigurasi config/database.php:
return [
'api_token' => 'notif-secret-key-2026', // ganti di production
'driver' => 'sqlite', // sqlite | mysql | pgsql
'sqlite' => ['path' => __DIR__.'/../database/notifikasi.db', ...],
'mysql' => ['host'=>'localhost','database'=>'notifikasi', ...],
];
2) Mobile App (Web)
cd MOBILE
npm install
# dev di browser
npm run dev # → http://localhost:5173 (cek Device Toolbar)
# build production
npm run build # → dist/
npx cap sync android # sync web → android/app/src/main/assets/public
3) Build APK & Install via ADB
Prasyarat: JDK 17+ dengan jlink (system JDK sering headless tanpa jlink).
# jika jlink tidak ada, download JDK 17:
# /tmp/jdk-17.0.2/bin/jlink
export JAVA_HOME=/tmp/jdk-17.0.2
# atau sdkman: export JAVA_HOME=$HOME/.sdkman/candidates/java/current
cd MOBILE/android
./gradlew clean assembleDebug
# APK → android/app/build/outputs/apk/debug/app-debug.apk
# install ke device
adb devices
adb -s 10.10.11.3:5555 install -r android/app/build/outputs/apk/debug/app-debug.apk
adb -s 10.10.11.3:5555 shell am start -n com.localnotif.app/.MainActivity
AGP / SDK: project pakai AGP 8.0.0, compileSdk 33, targetSdk 33, Capacitor Android 5.7.8 (sync tanpa google-services).
Konfigurasi Dinamis (Tanpa Rebuild APK)
App tidak perlu rebuild untuk ganti server:
src/settings.jssimpanserver_url,server_pollingdilocalStorage(fallbackconfig.js).- UI: tap ⚙️ → dialog Pengaturan → isi
URL API,Polling (detik)→ Simpan / Reset. Bagian Optimasi Baterai tampilkan status + tombol Minta Izin. - Login Keycloak fullscreen saat belum login; profil terisi dari atribut Keycloak (fallback data employee by email).
- Indikator
Online/OfflineviaGET /health.
Contoh URL:
- Direct:
http://10.10.11.40:8080/api(IP mesin API, binding0.0.0.0) - Via proxy:
https://notif.local/api(Nginx terminasi,X-Forwarded-Proto: https)
Admin Panel
Buka http://10.10.11.40:8080/admin.html atau via proxy https://notif.local/admin.html. Tanpa login, token API langsung di kode.
- Konfigurasi Server: URL API (disimpan
localStorage). - Kirim Notifikasi: Pilih Tujuan (
Semua Device/Berdasarkan NUP (Excel)/Kelompok (Unit/Jabatan)), isi Judul/Isi/Link/Prioritas, kirim viaPOST /notifications/broadcast. - Data Pegawai: CRUD tabel, toggle blur token, regenerasi token, salin token.
- Log Notifikasi: Tabel dengan kolom Judul/Isi, Prioritas, Tujuan, Lampiran (link
target="_blank"), Waktu.
API Endpoints
Base: http://<server-ip>:8080/api atau via proxy https://<domain>/api · Header: Authorization: Bearer <token> (kecuali /health, /admin.html, /auth/login, /auth/refresh).
| Method | Path | Auth | Body / Query | Response |
|---|---|---|---|---|
| GET | /health |
— | — | {status:"ok",timestamp} |
| GET | /admin.html |
— | — | HTML Admin Panel |
| POST | /auth/login |
— | {username,password} |
{success,tokens,user{nup,nama,...}} |
| POST | /auth/refresh |
— | {refresh_token} |
{success,tokens,user} |
| GET | /auth/userinfo |
Keycloak Bearer | header token Keycloak | {success,user{...atribut}} |
| POST | /register |
Bearer | {device_id, device_name, platform} |
{success:true} |
| GET | /notifications?device_id=X |
Bearer | query device_id |
{notifications:[...],count} (hanya broadcast/target user) |
| GET | /notifications?employee_token=X |
Bearer (employee) | query employee_token |
{notifications:[...],count} |
| GET | /notifications/{id} |
Bearer | query device_id/employee_token |
{notification:{...detail,target_summary}} |
| POST | /notifications/{id}/acknowledge |
Bearer (employee/global) | {device_id} |
{success:true} |
| POST | /notifications |
Bearer | {title,body,priority,link?,data?} |
{success:true,notification_id} |
| POST | /notifications/broadcast |
Bearer | {title,body,priority,link?,device_ids?,unit_kerja?,jabatan?,target_nups?} |
{success:true,notification_id,devices_targeted} |
| GET | /admin/notifications |
Bearer | — | {notifications:[...with targets],count} |
| GET | /admin/devices |
Bearer | — | {devices:[...],count} |
| GET | /admin/stats |
Bearer | — | {stats:{notifications,devices,pending}} |
| GET | /employees |
Bearer | — | {employees:[...],count} |
| GET | /employees?nup=X / ?email=X |
Bearer | query filter | {employees:[...],count} |
| GET | /employees?device_id=X |
Bearer (employee) | query device_id=token |
{employees:[1],count:1} |
| POST | /employees |
Bearer | {nama,nup,unit_kerja,jabatan,status_pegawai} |
{success:true,id,token} |
| PUT | /employees |
Bearer | {id,...} |
{success:true} |
| DELETE | /employees |
Bearer | {id} |
{success:true} |
| POST | /employees/{id}/regenerate-token |
Bearer | — | {success:true,token} |
cURL:
TOKEN=notif-secret-key-2026
curl http://10.10.11.40:8080/api/health
curl -H "Authorization: Bearer $TOKEN" "http://10.10.11.40:8080/api/notifications?device_id=phone-001"
curl -X POST http://10.10.11.40:8080/api/notifications/broadcast -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" -d '{"title":"Hello","body":"World","priority":"high","link":"https://example.com/file.pdf","unit_kerja":["IT Development"]}'
# via proxy
curl -H "Authorization: Bearer $TOKEN" https://notif.local/api/health
Test script: php API/test.php / php API/cli_test.php.
Pengujian
- Backend:
curl/ Postman; skriptest.php; DB SQLite auto-create + index. Test broadcast kelompok & link & reverse proxy headers. - Frontend:
npm run dev+ Device Toolbar; cek polling 10s tanpa refresh; admin.html via browser. - Native: izin
LocalNotifications.requestPermissions()+ first-launch battery dialog;schedule()setelah polling dapat data baru;php -luntuk PHP 7.4 compat. - Fisik:
npx cap sync→assembleDebug→ install → background → buat notifikasi viaPOST /notifications/broadcast→ cek status bar + link. - Reverse Proxy:
nginx -t && nginx -s reload+ cekcurl -H "X-Forwarded-Proto: https" http://127.0.0.1:8080/health.
Troubleshooting
- APK gagal build
jlink not found/core-for-system-modules.jar→ pakai JDK 17 full denganjlink(/tmp/jdk-17.0.2),export JAVA_HOME=..., AGP 8.0.0. INSTALL_FAILED_VERIFICATION_FAILUREatau AVCfuse→ jangan push ke/sdcard/Download, pakaiadb install -r /path/apklangsung atau/data/local/tmp.- Device tidak connect ke API → cek
ip -4 addr show(mis.10.10.11.40), pastikan device & server satu AP,php -S 0.0.0.0:8080atau via proxyhttps://notif.local/api, URL di Settings Dialog benar, firewall allow 8080/80/443. - HTTP Cleartext →
android/app/src/main/AndroidManifest.xmltambahandroid:usesCleartextTraffic="true"jika pakaihttp. - Peer dep conflict Capacitor →
npm install --legacy-peer-deps. - Token 401 → samakan
api_tokendiAPI/config/database.phpdanMOBILE/src/config.js(atau pakai employee token). - Login gagal / CORS Keycloak di WebView → mobile wajib lewat proxy
POST /api/auth/login(langsung ke Keycloak diblokir CORS originhttp://localhost). - Push tidak muncul → cek izin notifikasi OS (
appops get ... POST_NOTIFICATION), minta via app; cekdumpsys notification. - Service mati di background (Vivo/Oppo) → Pengaturan → Optimasi Baterai → Minta Izin (whitelist
dumpsys deviceidle). INSTALL_FAILED_VERIFICATION_FAILURE(fresh install) →adb shell settings put global verifier_verify_adb_installs 0+package_verifier_enable 0.- Parse error
T_DOUBLE_ARROWline 199 → server PHP 7.4, pastikanindex.phppakaiswitch(true)bukanmatch(sudah fix dife60db1). - Reverse proxy 404 /api/admin.html → akses via
/admin.htmlbukan/api/admin.htmljikaproxy_pass http://127.0.0.1:8080/strip prefix; ceknginx.conf.example. - Authorization header hilang di Apache →
CGIPassAuth On+Rewrite [E=HTTP_AUTHORIZATION]di.htaccess(sudah ada).
Changelog
Lihat memory.md untuk checklist, build notes, install status, dan changelog rinci.
Lisensi
Internal / Intranet — sesuaikan kebutuhan.