No description
  • PHP 40.9%
  • HTML 21.4%
  • Blade 20.3%
  • Vue 10.4%
  • Java 4.1%
  • Other 2.8%
Find a file
2026-10-25 03:16:02 +07:00
API style(mobile): palet navy/emas login ke semua halaman + rebuild APK 2026-10-25 02:17:45 +07:00
arsip build: APK #3 + arsip memory + memory ringan 2026-09-13 03:06:21 +07:00
assets build: rebuild APK #4 + fallback refresh + assets/logo.png 2026-09-13 03:29:17 +07:00
framework style(web): terapkan palet navy/emas login ke semua halaman Blade 2026-10-25 02:34:39 +07:00
MOBILE style(mobile): palet navy/emas login ke semua halaman + rebuild APK 2026-10-25 02:17:45 +07:00
template feat(mobile): login BP Batam pakai logo assets + dist 2026-10-24 23:10:48 +07:00
.gitignore fix: track framework/database (migrations, seeders) in git 2026-10-05 01:47:03 +07:00
memory.md style(web): terapkan palet navy/emas login ke semua halaman Blade 2026-10-25 02:34:39 +07:00
PRD.md docs: update semua MD (SSO proxy, targeting NUP, detail, local push, admin tanpa SSO) 2026-09-10 00:01:43 +07:00
README.md docs: update semua MD (SSO proxy, targeting NUP, detail, local push, admin tanpa SSO) 2026-09-10 00:01:43 +07:00
TechStack.md docs: update semua MD (SSO proxy, targeting NUP, detail, local push, admin tanpa SSO) 2026-09-10 00:01:43 +07:00

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, statuspegawai per akun).
  • Polling Sinkronisasi — klien fetch berkala ke server lokal (default 10s) untuk cek pesan baru. Support X-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_pegawai dengan token auto-generate. Atribut tampil dari Keycloak (fallback cocokkan email DB).
  • Broadcast Ter-target + Visibilitas — POST /notifications/broadcast dengan device_ids, unit_kerja/jabatan (AND), target_nups (Excel kolom nup), atau kosong = semua device (flag is_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. Handle X-Forwarded-Proto, Authorization passthrough, dan prefix /api strip.

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.js simpan server_url, server_polling di localStorage (fallback config.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/Offline via GET /health.

Contoh URL:

  • Direct: http://10.10.11.40:8080/api (IP mesin API, binding 0.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 via POST /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; skrip test.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 -l untuk PHP 7.4 compat.
  • Fisik: npx cap sync → assembleDebug → install → background → buat notifikasi via POST /notifications/broadcast → cek status bar + link.
  • Reverse Proxy: nginx -t && nginx -s reload + cek curl -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 dengan jlink (/tmp/jdk-17.0.2), export JAVA_HOME=..., AGP 8.0.0.
  • INSTALL_FAILED_VERIFICATION_FAILURE atau AVC fuse → jangan push ke /sdcard/Download, pakai adb install -r /path/apk langsung 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:8080 atau via proxy https://notif.local/api, URL di Settings Dialog benar, firewall allow 8080/80/443.
  • HTTP Cleartext → android/app/src/main/AndroidManifest.xml tambah android:usesCleartextTraffic="true" jika pakai http.
  • Peer dep conflict Capacitor → npm install --legacy-peer-deps.
  • Token 401 → samakan api_token di API/config/database.php dan MOBILE/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 origin http://localhost).
  • Push tidak muncul → cek izin notifikasi OS (appops get ... POST_NOTIFICATION), minta via app; cek dumpsys 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_ARROW line 199 → server PHP 7.4, pastikan index.php pakai switch(true) bukan match (sudah fix di fe60db1).
  • Reverse proxy 404 /api/admin.html → akses via /admin.html bukan /api/admin.html jika proxy_pass http://127.0.0.1:8080/ strip prefix; cek nginx.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.