Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
91b65fc769 | ||
|
|
ed557cf899 | ||
|
|
d02c9baf7b | ||
|
|
fed81ed5f9 | ||
|
|
a68121544d | ||
|
|
cdb5046147 | ||
|
|
cb60ceec10 | ||
|
|
0f612df70f | ||
|
|
6f97430e41 | ||
|
|
f27c02309e |
No files matched your search
@@ -2,6 +2,17 @@
|
||||
|
||||
Format mengikuti [Keep a Changelog](https://keepachangelog.com/) dan [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [Unreleased] — setelah v0.5.1
|
||||
|
||||
### Diperbaiki
|
||||
- Kolom **IP Address** di **GenieACS Pro** kini tampil saat **mode pencarian** (sebelumnya kosong).
|
||||
- Menu **Update GitHub** otomatis disembunyikan pada instalasi **tanpa `.git`** (container/Docker), tetap tampil pada instalasi git.
|
||||
|
||||
### Ditambahkan
|
||||
- **Badge kelengkapan data** di daftar pelanggan: centang hijau = **nomor HP + koordinat + ACS** lengkap; silang merah = ada yang kurang (tooltip menyebut yang belum).
|
||||
- **Backend speedtest bawaan** (`/speedtest/*`) kini aktif (LibreSpeed self-hosted, same-origin).
|
||||
- `isolir-redirect-server` dapat dijalankan **embedded** dalam proses app (opt-out via `ISOLIR_REDIRECT_EMBEDDED=0`).
|
||||
|
||||
## [0.5.1] — 2026-10-13 — "pickle-lime"
|
||||
|
||||
### Diubah
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
> 🪞 **Mirror.** Repo asli & pengembangan ada di **Gitea**: https://gitea.oxpoe.qzz.io/oxpoe/open-isp
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/assets/banner.png" alt="OPEN-ISP" width="100%">
|
||||
</p>
|
||||
@@ -108,7 +110,7 @@ Dukungan sukarela tetap diterima lewat menu **Dukung OPEN-ISP** di aplikasi.
|
||||
|
||||
## Teknologi
|
||||
|
||||
Node.js · Express · EJS · better-sqlite3 (SQLite) · Bootstrap 5 + Bootstrap Icons (vendor lokal) · Leaflet · PWA/Service Worker · sharp · net-snmp · MikroTik RouterOS API · Baileys / GoWA · Telegram Bot API.
|
||||
Node.js · Express · EJS · better-sqlite3 (SQLite) · Bootstrap 5 + Bootstrap Icons (vendor lokal) · Leaflet · PWA/Service Worker · sharp · net-snmp · MikroTik RouterOS API · GoWA (WhatsApp gateway) · Telegram Bot API.
|
||||
|
||||
## Lisensi & Kredit
|
||||
|
||||
|
||||
+8
-2
@@ -67,6 +67,7 @@ const app = express();
|
||||
|
||||
// Kompresi gzip (performa: respons HTML/CSS/JS lebih kecil)
|
||||
app.use(compression());
|
||||
app.use('/speedtest', require('./routes/speedtest'));
|
||||
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
const cookieSecure = getSetting('cookie_secure', isProduction);
|
||||
@@ -1106,7 +1107,7 @@ app.post('/api/webhook/v1/payment-notif', multer().any(), async (req, res) => {
|
||||
|
||||
// Variabel global untuk modul lain yang masih membaca konfigurasi (mis. skrip utilitas)
|
||||
global.appSettings = {
|
||||
port: getSetting('server_port', 4555),
|
||||
port: getSetting('server_port', 3001),
|
||||
host: getSetting('server_host', 'localhost'),
|
||||
genieacsUrl: getSetting('genieacs_url', 'http://localhost:7557'),
|
||||
genieacsUsername: getSetting('genieacs_username', ''),
|
||||
@@ -2210,7 +2211,7 @@ logger.info(`Attempting to start server on configured port: ${port}`);
|
||||
startServer(port);
|
||||
|
||||
if (getSetting('whatsapp_enabled', false)) {
|
||||
const gwType = getSetting('wa_gateway_type', 'baileys');
|
||||
const gwType = getSetting('wa_gateway_type', 'gowa');
|
||||
if (gwType === 'baileys') {
|
||||
import('./services/whatsappBot.mjs')
|
||||
.then((mod) => mod.startWhatsAppBot())
|
||||
@@ -2242,3 +2243,8 @@ app.use(errorHandler);
|
||||
|
||||
// Export app untuk testing
|
||||
module.exports = app;
|
||||
|
||||
// isolir redirect server embedded (port 8099) — untuk container/reverse-proxy; opt-out: ISOLIR_REDIRECT_EMBEDDED=0
|
||||
if (String(process.env.ISOLIR_REDIRECT_EMBEDDED || '1') !== '0') {
|
||||
try { require('./isolir-redirect-server.js'); } catch (e) { console.error('[isolir-redirect]', e.message); }
|
||||
}
|
||||
+1
-1
@@ -464,7 +464,7 @@ db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS wa_chat_messages (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
direction TEXT NOT NULL DEFAULT 'outbound',
|
||||
gateway TEXT NOT NULL DEFAULT 'baileys',
|
||||
gateway TEXT NOT NULL DEFAULT 'gowa',
|
||||
sender_phone TEXT,
|
||||
recipient_phone TEXT,
|
||||
customer_id INTEGER REFERENCES customers(id) ON DELETE SET NULL,
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// Lokasi file settings.json — dapat diarahkan via env SETTINGS_PATH
|
||||
// (dipakai agar settings bisa disimpan di direktori yang di-mount / persisten).
|
||||
const path = require('path');
|
||||
const settingsPath = process.env.SETTINGS_PATH
|
||||
? path.resolve(process.env.SETTINGS_PATH)
|
||||
: path.join(__dirname, '..', 'settings.json');
|
||||
module.exports = { settingsPath };
|
||||
@@ -17,7 +17,7 @@ let settingsCacheTime = 0;
|
||||
const CACHE_DURATION = 2000; // 2 detik
|
||||
|
||||
// File system watcher untuk auto-reload settings
|
||||
const settingsPath = path.join(__dirname, '../settings.json');
|
||||
const { settingsPath } = require('./paths');
|
||||
let watcher = null;
|
||||
|
||||
// Helper untuk baca settings.json secara dinamis
|
||||
@@ -99,7 +99,7 @@ function startSettingsWatcher() {
|
||||
|
||||
try {
|
||||
const s = getSettingsWithCache();
|
||||
const port = s.server_port ?? 4555;
|
||||
const port = s.server_port ?? 3001;
|
||||
const host = s.server_host || 'localhost';
|
||||
const gurl = s.genieacs_url || '(tidak diatur)';
|
||||
const company = s.company_header || '(default)';
|
||||
|
||||
@@ -1,274 +0,0 @@
|
||||
# Analisis Komprehensif Sistem, Logika Backend, dan UI/UX — Billing RT/RW Net
|
||||
|
||||
> **Target Project**: `/opt/billing-rtrw` (VPS `deneva` — `116.212.74.115`)
|
||||
> **Layanan**: `billing-rtrw.service` (Node.js v20+, port `3001`)
|
||||
> **Repository Asal**: `https://github.com/alijayanet/billing-rtrw`
|
||||
> **Repository Fork/Deploy**: `https://github.com/alijayanet/billing-rtrw`
|
||||
|
||||
---
|
||||
|
||||
## 1. Ringkasan Eksekutif & Gambaran Sistem
|
||||
|
||||
Billing RT/RW Net ini adalah sistem manajemen operasional ISP (Internet Service Provider) berbasis komunitas yang memadukan:
|
||||
1. **Billing & Invoicing**: Siklus tagihan bulanan, isolir otomatis, dan pembayaran QRIS dinamis/manual.
|
||||
2. **Network Automation**: Integrasi RouterOS MikroTik (PPPoE, Hotspot Voucher, Simple Queue, Address List).
|
||||
3. **Hardware Management (FTTH)**: Embedded ACS TR-069 Server (CWMP) untuk manajemen ONT klien dan SNMP OLT (EPON/GPON).
|
||||
4. **Customer Self-Service (PWA)**: Portal pelanggan mobile-first dengan auto-login 90 hari, ganti WiFi SSID/password mandiri, grafik pemakaian kuota/bandwidth, dan reboot modem.
|
||||
5. **Multi-Channel Bot**: Bot WhatsApp (Baileys socket) dan Bot Telegram untuk notifikasi, penagihan, dan tiket operasional.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph Klien_Pelanggan["Portal Klien & Interaksi"]
|
||||
CustPWA["Customer PWA (Tailwind + Lucide)"]
|
||||
PublicVoucher["Portal Voucher & QRIS"]
|
||||
WABot["WhatsApp Bot (Baileys)"]
|
||||
TeleBot["Telegram Bot Notifikasi"]
|
||||
end
|
||||
|
||||
subgraph Core_Backend["Express 4 Engine (Port 3001)"]
|
||||
App["app-customer.js (Main Entry)"]
|
||||
ACSServer["Embedded TR-069 Server (/acs)"]
|
||||
AuthSvc["Auth & Token Service (90 Hari)"]
|
||||
BillSvc["Billing & Invoice Engine"]
|
||||
MikroSvc["MikroTik RouterOS API"]
|
||||
OLTSvc["OLT Service (SNMP / Web)"]
|
||||
UsageSvc["Bandwidth Usage Tracker"]
|
||||
end
|
||||
|
||||
subgraph Data_Network["Infrastruktur Jaringan & Database"]
|
||||
DB[("SQLite WAL (better-sqlite3)")]
|
||||
MikroTik[("Router MikroTik (PPPoE / Hotspot)")]
|
||||
OLT[("OLT Fiber (Hioso/HSGQ/VSOL)")]
|
||||
CPE[("ONT / Modem Pelanggan (TR-069)")]
|
||||
end
|
||||
|
||||
CustPWA -->|HTTP/REST| App
|
||||
PublicVoucher -->|HTTP/REST| App
|
||||
WABot <--> App
|
||||
TeleBot <--> App
|
||||
App --> AuthSvc
|
||||
App --> BillSvc
|
||||
App --> MikroSvc
|
||||
App --> OLTSvc
|
||||
App --> UsageSvc
|
||||
CPE <-->|CWMP SOAP| ACSServer
|
||||
ACSServer <--> DB
|
||||
BillSvc <--> DB
|
||||
MikroSvc <--> MikroTik
|
||||
OLTSvc <--> OLT
|
||||
UsageSvc <--> MikroTik
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Analisis Backend & Logika Alur Fungsi
|
||||
|
||||
### 2.1 Arsitektur Server & Runtime
|
||||
* **Runtime**: Node.js `>=20.0.0` (Native ES Module + CommonJS hybrid).
|
||||
* **Database Engine**: SQLite 3 melalui `better-sqlite3` dengan mode **WAL (Write-Ahead Logging)**, foreign keys aktif, sinkronisasi file cepat dan hemat resource.
|
||||
* **Proses Lifecycle**: Dikelola oleh Systemd (`billing-rtrw.service`) dengan auto-restart `on-failure` (delay 5 detik), batas memori RAM `--max-old-space-size=512`.
|
||||
* **Proteksi Server**:
|
||||
* Penanganan `unhandledRejection` dan `uncaughtException` terpusat agar error koneksi (misal timeout MikroTik) tidak mematikan server Express.
|
||||
* Fallback port otomatis (`startServer` mencoba port +1000 jika port 3001 bentrok).
|
||||
* Proteksi CSRF berbasis validasi header `Origin` dan `Referer` (mengecualikan webhook dan endpoint SOAP ACS).
|
||||
|
||||
---
|
||||
|
||||
### 2.2 Modul-Modul Inti & Alur Logika
|
||||
|
||||
#### A. Layanan Billing, Invoice & Isolir (`services/billingService.js` & `services/cronService.js`)
|
||||
* **Logika Pembuatan Tagihan**:
|
||||
1. Cron berjalan setiap hari (`cronService.js`) memeriksa seluruh pelanggan aktif.
|
||||
2. Tagihan dibuat otomatis setiap siklus tanggal tagihan (`billing_date`) untuk periode bulan berjalan.
|
||||
3. Kode unik nominal QRIS (`qris_amount_unique`) diinjeksi 3 digit acak (1–999) agar mutasi bank/QRIS dapat dicocokkan otomatis tanpa payment gateway berbayar.
|
||||
* **Logika Isolir Otomatis**:
|
||||
1. Bila tanggal saat ini mencapai atau melewati `isolir_day` (default tanggal 20 tiap bulan) dan tagihan pelanggan masih `unpaid`:
|
||||
2. Status pelanggan di database diubah menjadi `isolated`.
|
||||
3. `mikrotikService` dipanggil untuk memindahkan secret PPPoE ke profile isolir atau memasukkan IP klien ke Address List `ISOLIR` di MikroTik Firewall.
|
||||
4. Sesi aktif diputus (`disconnectPPPoE`) sehingga pelanggan ter-redirect ke halaman Walled Garden (`/isolated`).
|
||||
|
||||
#### B. Layanan TR-069 ACS Server (`services/acsServerService.js` & `/acs`)
|
||||
* **Protokol**: CWMP (CPE WAN Management Protocol) via SOAP XML over HTTP POST.
|
||||
* **Alur Inform & Handshake**:
|
||||
1. Modem (ONT) mengirim HTTP POST `Inform` ke endpoint `/acs`.
|
||||
2. Server memeriksa header `Authorization`. Jika belum diautentikasi, server membalas dengan **HTTP 401 Unauthorized** (Digest Challenge).
|
||||
3. Modem mengirim kembali request dengan Digest Hash yang dihitung berdasarkan username (`skyfiber`) dan password acak perangkat.
|
||||
4. Server membaca parameter XML (`Device.DeviceInfo.*`, `Device.WANDevice.*`, `Device.Ethernet.*`, `Device.LANDevice.*`).
|
||||
5. Data kekuatan redaman optik (`RXPower`, `TXPower`), serial number, model, dan versi software disimpan ke tabel `acsdevices`.
|
||||
6. Server memeriksa antrean tugas (task queue) per perangkat: jika ada perintah ganti password WiFi atau reboot, server merespons dengan `SetParameterValues` atau `Reboot`.
|
||||
|
||||
#### C. Pengelolaan WiFi Multi-Vendor (`services/customerDeviceService.js`)
|
||||
* **Masalah Klasik**: Tiap vendor modem (Huawei, ZTE, FiberHome, Nokia) memiliki skema OID/TR-098 yang berbeda-beda. Mengirim parameter yang salah menyebabkan SOAP Fault 9007.
|
||||
* **Logika Adaptif Per-Vendor**:
|
||||
* **Huawei / FiberHome / Nokia**: Memprioritaskan `PreSharedKey.1.KeyPassphrase` dan menghindari pengiriman hex 64-digit bersamaan dengan plain string.
|
||||
* **ZTE / ZICG / CIOT**: Memprioritaskan `KeyPassphrase` top-level.
|
||||
* **Dual-Band SSID**: Mengatur `WLANConfiguration.1.SSID` (2.4 GHz) dan `WLANConfiguration.5.SSID` (5 GHz) secara independen tanpa merusak konfigurasi guest SSID.
|
||||
|
||||
#### D. Integrasi Jaringan MikroTik (`services/mikrotikService.js`)
|
||||
* **Koneksi**: RouterOS API (port default `25722`/`8728`) menggunakan library `routeros-client`.
|
||||
* **Fitur Utama**:
|
||||
* Sinkronisasi PPPoE: Read active connections, update secret passwords, kick active sessions.
|
||||
* Monitoring Traffic: Mengambil data byte RX/TX interface pelanggan untuk kalkulasi kuota.
|
||||
* Voucher Hotspot: Direct injection user hotspot, profile rate-limit, dan validitas voucher.
|
||||
|
||||
#### E. Autentikasi Pelanggan & Persistent Token (`services/customerAuthTokenService.js`)
|
||||
* **Alur Sesi PWA**:
|
||||
1. Pelanggan memasukkan nomor HP / ID Pelanggan.
|
||||
2. Saat login berhasil, backend men-generate token kriptografi acak `crypto.randomBytes(32).toString('hex')`.
|
||||
3. Token di-hash dengan SHA-256 dan disimpan di tabel SQLite `customer_auth_tokens` dengan masa berlaku 90 hari.
|
||||
4. Plain token dikirimkan ke browser sebagai cookie `customer_remember` (`httpOnly`, `secure`, `sameSite: lax`).
|
||||
5. Ketika pengguna membuka PWA (bahkan setelah browser/HP di-restart), middleware auto-login memverifikasi cookie ini dan merekonstruksi session secara transparan.
|
||||
|
||||
#### F. Pelacakan Kuota & Pemakaian Bandwidth (`services/usageService.js`)
|
||||
* **Logika Agregasi**:
|
||||
1. Mengambil byte delta dari MikroTik queue / interface.
|
||||
2. Menyimpan sampel agregat harian dan bulanan ke database.
|
||||
3. Menyediakan API untuk konsumsi frontend (Chart.js di dashboard pelanggan).
|
||||
|
||||
---
|
||||
|
||||
## 3. Analisis UI / UX, Styling, Typografi & Layout
|
||||
|
||||
Pemeriksaan mendalam terhadap template EJS dan file CSS/JS di `/opt/billing-rtrw/views/` dan `/opt/billing-rtrw/public/` mengungkap sejumlah temuan penting mengenai styling, engine, dan konsistensi desain:
|
||||
|
||||
### 3.1 Peta Engine & Library UI per Portal
|
||||
|
||||
| Portal / Halaman | Engine CSS Utama | Library Icon | Font Family Utama | Style Pendekatan |
|
||||
|---|---|---|---|---|
|
||||
| **Customer Dashboard** (`views/dashboard.ejs`) | **Tailwind CSS 3.4.17** (via CDN) | **Lucide Icons** (v1.48.0) + FontAwesome 6.4.0 | **Plus Jakarta Sans** (Google Fonts) | Modern App Shell / Glassmorphism Mobile-first |
|
||||
| **Customer Login** (`views/login.ejs`) | **Bootstrap 5.3.2** (CDN) | **Bootstrap Icons 1.11.3** | **System Font Stack** (`-apple-system`, Segoe UI, Roboto) | Centered Glass Card / Radial Background |
|
||||
| **Admin Portal** (`views/admin/*`) | **Custom CSS** (`/css/admin.css`) | **Bootstrap Icons 1.11.3** + FontAwesome | **Inter** (Google Fonts) | Desktop Enterprise Sidebar & Table Layout |
|
||||
| **Teknisi Portal** (`views/tech/*`) | **Bootstrap 5** + `/css/theme.css` | **Bootstrap Icons** | System Font / Inter | Mobile Bottom Navigation |
|
||||
| **Halaman Peta Jaringan** (`views/admin/map.ejs`) | **Leaflet 1.9.4** + `/css/admin.css` | FontAwesome + SVG Animasi | Inter | Fullscreen Interactive Map & Vector Layer |
|
||||
|
||||
---
|
||||
|
||||
### 3.2 Analisis Typografi & Hierarki Font
|
||||
|
||||
```
|
||||
[Customer Dashboard] Plus Jakarta Sans (300, 400, 500, 600, 700, 800)
|
||||
├── Title / Header : text-base sm:text-lg font-bold (Tracking tight)
|
||||
├── Section Labels : text-xs font-semibold text-slate-400 uppercase (Tracking wider)
|
||||
├── Body / Konten : text-sm font-normal text-slate-200 (Leading relaxed)
|
||||
└── Badges & Metadata : text-[10px] sm:text-xs font-medium
|
||||
|
||||
[Customer Login] System Font Stack (BlinkMacSystemFont, Segoe UI, Roboto)
|
||||
└── Ketidaksesuaian : Font berbeda dengan Dashboard; saat user login terjadi visual jump dari font sistem ke Plus Jakarta Sans.
|
||||
|
||||
[Admin Portal] Inter (300, 400, 500, 600, 700, 800)
|
||||
├── Sidebar Items : font-size 13px, font-weight 500
|
||||
├── Data Table Header : font-size 11px, text-transform uppercase, letter-spacing 0.5px
|
||||
└── Table Data Cells : font-size 13px, color var(--text: #e6edf3)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.3 Analisis Styling, Palette Warna & Tema Gelap/Terang
|
||||
|
||||
#### Palette Warna Dominan
|
||||
1. **Mode Gelap (Customer Dashboard)**:
|
||||
* Background Utama: `#0b0f19` (Deep Blue-Black)
|
||||
* Card & Panel: `#1e293b` (Slate 800) dengan border `#334155` (Slate 700)
|
||||
* Aksen Primer (Brand): Gradasi Teal `#14b8a6` ke Cyan `#06b6d4`
|
||||
* Aksen Sekunder: Indigo `#6366f1`
|
||||
* Status Indikator: Hijau Sukses `#10b981`, Merah Warning/Error `#f43f5e`, Amber `#f59e0b`
|
||||
2. **Mode Terang (Customer Dashboard & Global Theme)**:
|
||||
* Background Utama: `#f1f5f9` (Slate 100)
|
||||
* Card & Panel: `#ffffff` murni dengan border `rgba(15, 23, 42, 0.08)`
|
||||
* Teks Utama: `#0f172a` (Slate 900), Teks Muted: `#64748b` (Slate 500)
|
||||
3. **Mekanisme Tema (Theming Engine)**:
|
||||
* Terdapat dua sistem tema yang berjalan berdampingan:
|
||||
* `views/dashboard.ejs` menggunakan script lokal inline (`localStorage: skyfiber-customer-theme`) mengendalikan class `dark` pada `<html>` untuk Tailwind.
|
||||
* Portal lainnya menggunakan `/js/theme.js` dan `/css/theme.css` (`localStorage: app-theme`) mengendalikan atribut `data-theme="light|dark"`.
|
||||
* Pada rilis `v1.9.4`, kedua key ini sudah disinkronkan, namun styling mode terang di portal admin masih membutuhkan penyesuaian kontras pada beberapa tabel dan modal.
|
||||
|
||||
---
|
||||
|
||||
### 3.4 Analisis Layout, Spacing, Alignment & UX
|
||||
|
||||
#### 1. Customer Dashboard (Mobile App Shell)
|
||||
* **Alignment & Container**:
|
||||
* Lebar maksimum dibatasi ke `max-w-md` atau `max-w-lg` di tengah layar (`mx-auto`), memberikan rasa native mobile application saat dibuka di smartphone maupun desktop browser.
|
||||
* Padding konten konsisten (`px-4 py-3`), dengan padding bawah ekstra (`pb-28`) untuk memberi ruang bagi floating navigation dock.
|
||||
* **Floating Glass Dock (5 Tab Navigasi)**:
|
||||
* Terpasang fixed di bagian bawah layar: `fixed bottom-4 left-1/2 -translate-x-1/2`.
|
||||
* Menggunakan efek glassmorphism (`backdrop-blur-md`, background semi-transparan, border tipis halus).
|
||||
* Indikator aktif berupa **sliding pill** yang bergerak halus mengikuti tab yang diklik (`updatePillPosition`).
|
||||
* Tab navigasi:
|
||||
1. `tab-beranda` (Home, status paket, kuota, shortcut aksi)
|
||||
2. `tab-router` (Status ONT, redaman fiber optic, ganti WiFi, reboot)
|
||||
3. `tab-ppob` (Layanan isi pulsa & token listrik - status Coming Soon)
|
||||
4. `tab-tagihan` (Riwayat invoice, tombol bayar QRIS)
|
||||
5. `tab-profile` (Data akun, riwayat tiket kendala, tombol CS WhatsApp, logout)
|
||||
* **Kerapian & Touch Target**:
|
||||
* Seluruh tombol interaktif memiliki ukuran touch-target minimal `40x40px` (ramah sentuhan jari di layar HP).
|
||||
* Safe-area-insets untuk iPhone (`env(safe-area-inset-bottom)`) sudah ditambahkan pada `.dock-glass`.
|
||||
|
||||
#### 2. Admin & Staff Portal
|
||||
* **Layout**:
|
||||
* Sidebar navigasi di sisi kiri (`width: 240px`, fixed).
|
||||
* Konten utama di sebelah kanan dengan header fixed dan breadcrumb.
|
||||
* Responsive breakpoint: Pada layar kecil (mobile), sidebar disembunyikan dan diakses lewat toggle menu hamburger.
|
||||
|
||||
---
|
||||
|
||||
### 3.5 Temuan Evaluasi & Celah UI/UX (Bottlenecks)
|
||||
|
||||
1. **Fragmentasi Framework CSS**:
|
||||
* Halaman login menggunakan **Bootstrap 5**, sedangkan dashboard pelanggan menggunakan **Tailwind CSS**. Ini membebani browser pengguna karena harus mengunduh dua framework CSS besar saat pertama kali membuka aplikasi.
|
||||
2. **Ketergantungan CDN Runtime (Client-Side Tailwind)**:
|
||||
* Dashboard pelanggan menggunakan `<script src="https://cdn.tailwindcss.com/3.4.17"></script>`. CDN ini mem-parse class Tailwind saat runtime di perangkat klien (JIT compiler di browser), yang dapat memperlambat rendering awal (First Contentful Paint) pada ponsel berspesifikasi rendah.
|
||||
3. **Dual Icon Libraries**:
|
||||
* Halaman dashboard memuat **Lucide Icons** dan **FontAwesome 6.4.0** secara bersamaan. FontAwesome (file CSS + font ~400KB) hanya digunakan untuk segelintir ikon kecil, sementara sebagian besar UI sudah menggunakan SVG Lucide.
|
||||
4. **Log Error Berulang di Backend**:
|
||||
* Terdapat log `Telegram Polling Error` berkala pada journal server deneva karena token bot belum dikonfigurasi / timeout koneksi ke server Telegram API.
|
||||
|
||||
---
|
||||
|
||||
## 4. Rencana Kerja Terperinci (Execution Plan)
|
||||
|
||||
Rencana perubahan ini dibagi menjadi 4 fase berurutan dengan prinsip **zero-downtime** dan keamanan data:
|
||||
|
||||
```mermaid
|
||||
timeline
|
||||
title Timeline Rencana Peningkatan Billing RTRW
|
||||
Fase 1 : Konsolidasi Backend & Error Fix : Fix Polling Telegram : Optimalisasi Error Handling
|
||||
Fase 2 : Unifikasi Design System : Standarisasi Font (Plus Jakarta Sans) : Harmonisasi Halaman Login & Dashboard
|
||||
Fase 3 : Optimasi Asset & Performa : Eliminasi Redundansi Icon : Cache Profiling & PWA Hardening
|
||||
Fase 4 : Testing & Validasi : Uji Responsivitas Semua Layar : Uji Fungsi WiFi & Pembayaran Live
|
||||
```
|
||||
|
||||
### Fase 1: Perbaikan Stabilitas & Logika Backend
|
||||
1. **Fix Polling Telegram (`services/telegramBot.js`)**:
|
||||
* Tambahkan pengecekan kondisi: jika token Telegram kosong, nonaktifkan `polling: true` agar tidak membanjiri log server dengan polling error setiap 5 detik.
|
||||
2. **Peningkatan Ketahanan Koneksi MikroTik (`services/mikrotikService.js`)**:
|
||||
* Implementasikan retry backoff otomatis saat query queue/PPPoE timeout, menghindari uncaught exception yang tidak perlu.
|
||||
3. **Audit Query Database (`config/database.js`)**:
|
||||
* Periksa indeks pada tabel `pppoe_traffic_samples`, `invoices`, dan `customer_auth_tokens` untuk mempercepat query bulanan.
|
||||
|
||||
### Fase 2: Harmonisasi UI/UX & Standarisasi Tipografi
|
||||
1. **Standarisasi Tipografi**:
|
||||
* Terapkan font **Plus Jakarta Sans** ke seluruh halaman pelanggan, termasuk halaman Login (`views/login.ejs`), Register, dan Isolir (`views/isolated.ejs`), menggantikan system font default agar pengalaman visual konsisten sejak awal.
|
||||
2. **Harmonisasi Desain Halaman Login (`views/login.ejs`)**:
|
||||
* Rombak halaman login agar memiliki nuansa tema, sudut rounded, dan aksen warna Teal/Cyan yang identik dengan Dashboard Pelanggan.
|
||||
* Ganti elemen Bootstrap pada halaman login menjadi layout yang selaras dengan tema dashboard.
|
||||
3. **Penyempurnaan Form Ganti WiFi di Tab Router**:
|
||||
* Perjelas instruksi frekuensi 2.4 GHz vs 5 GHz.
|
||||
* Tambahkan toggle visibility (mata) untuk password WiFi dan validasi panjang password (minimal 8 karakter) di sisi klien sebelum submit.
|
||||
|
||||
### Fase 3: Optimasi Frontend & Efisiensi Aset
|
||||
1. **Rasionalisasi Icon**:
|
||||
* Migrasikan sisa ikon FontAwesome di Dashboard pelanggan ke **Lucide Icons** bawaan.
|
||||
* Hapus pemanggilan CSS FontAwesome di dashboard pelanggan untuk menghemat ukuran transfer data hingga ~400 KB.
|
||||
2. **Optimasi Mode Terang (Light Mode)**:
|
||||
* Periksa dan rapikan kontras teks di seluruh tab dashboard agar tidak ada teks putih yang tidak terbaca saat berganti ke background terang.
|
||||
* Pastikan border card dan form input terlihat tegas pada mode terang.
|
||||
|
||||
### Fase 4: Pengujian, Validasi & Deployment
|
||||
1. **Uji Coba Lintas Perangkat**:
|
||||
* Uji tampilan di mobile (Android Chrome, iOS Safari), tablet, dan desktop.
|
||||
* Uji fungsi PWA Add to Home Screen dan verifikasi auto-login setelah kill aplikasi.
|
||||
2. **Uji Fungsional End-to-End**:
|
||||
* Uji ganti SSID/password pada modem online.
|
||||
* Uji alur pembuatan invoice dan QRIS.
|
||||
* Restart service `billing-rtrw.service` dan verifikasi log bersih tanpa error.
|
||||
@@ -1,317 +0,0 @@
|
||||
# AUDIT FINDINGS SUMMARY - QUICK REFERENCE
|
||||
**Status:** Complete Audit
|
||||
**Date:** 14 August 2026
|
||||
**Format:** Executive Summary for Management
|
||||
|
||||
---
|
||||
|
||||
## 🎯 AUDIT OVERVIEW
|
||||
|
||||
| Metric | Status | Details |
|
||||
|--------|--------|---------|
|
||||
| **Security Score** | 5.5/10 | ⚠️ Critical issues found |
|
||||
| **Code Quality** | 7/10 | Good structure, needs hardening |
|
||||
| **Database Integrity** | 8/10 | Well-designed schema |
|
||||
| **Production Ready** | ❌ NO | Must fix critical issues first |
|
||||
| **Audit Completed** | ✅ YES | Full report available |
|
||||
|
||||
---
|
||||
|
||||
## 🔴 CRITICAL FINDINGS (MUST FIX IMMEDIATELY)
|
||||
|
||||
### 1. PASSWORD STORAGE (CRITICAL)
|
||||
- **Issue:** Passwords stored in plaintext in database
|
||||
- **Risk:** Full account compromise if database breached
|
||||
- **Files:** `services/techService.js`, `settings.json`
|
||||
- **Fix Time:** 4-6 hours
|
||||
- **Severity:** CRITICAL
|
||||
- **Status:** Not Fixed ❌
|
||||
|
||||
### 2. DEFAULT CREDENTIALS (CRITICAL)
|
||||
- **Issue:** Default admin password `admin123` in settings.json
|
||||
- **Risk:** Anyone can login to admin panel
|
||||
- **Fix Time:** 1 hour
|
||||
- **Severity:** CRITICAL
|
||||
- **Status:** Not Fixed ❌
|
||||
|
||||
### 3. UNENCRYPTED CREDENTIALS (CRITICAL)
|
||||
- **Issue:** API keys and passwords in plaintext in settings.json
|
||||
- **Risk:** Exposure in version control, server breach
|
||||
- **Files:** `settings.json`
|
||||
- **Fix Time:** 2-3 hours
|
||||
- **Severity:** CRITICAL
|
||||
- **Status:** Not Fixed ❌
|
||||
|
||||
### 4. WEAK PASSWORD HASHING (HIGH)
|
||||
- **Issue:** SHA256 and MD5 used instead of bcrypt/argon2
|
||||
- **Risk:** Password cracking in hours/minutes
|
||||
- **Files:** Multiple service files
|
||||
- **Fix Time:** 6-8 hours
|
||||
- **Severity:** HIGH
|
||||
- **Status:** Not Fixed ❌
|
||||
|
||||
---
|
||||
|
||||
## 🟠 HIGH PRIORITY FINDINGS
|
||||
|
||||
### 5. CSRF PROTECTION (LIMITED)
|
||||
- **Issue:** Only Referer/Origin validation, no explicit CSRF tokens
|
||||
- **Risk:** Cross-site request forgery attacks possible
|
||||
- **Fix Time:** 4-6 hours
|
||||
- **Severity:** HIGH
|
||||
|
||||
### 6. SESSION MANAGEMENT
|
||||
- **Issue:** In-memory session store, no regeneration on login
|
||||
- **Risk:** Session hijacking possible
|
||||
- **Fix Time:** 3-4 hours
|
||||
- **Severity:** HIGH
|
||||
|
||||
### 7. INPUT VALIDATION
|
||||
- **Issue:** Incomplete input validation and sanitization
|
||||
- **Risk:** XSS and injection attacks
|
||||
- **Fix Time:** 8-10 hours
|
||||
- **Severity:** HIGH
|
||||
|
||||
---
|
||||
|
||||
## 🟡 MEDIUM PRIORITY FINDINGS
|
||||
|
||||
### 8. FILE UPLOAD SECURITY
|
||||
- **Issue:** Only MIME type validation, no deep file scanning
|
||||
- **Risk:** Malicious file upload
|
||||
- **Fix Time:** 2-4 hours
|
||||
- **Severity:** MEDIUM
|
||||
|
||||
### 9. BACKUP & DISASTER RECOVERY
|
||||
- **Issue:** Auto-backup disabled in settings
|
||||
- **Risk:** Data loss in disaster scenario
|
||||
- **Fix Time:** 2-3 hours
|
||||
- **Severity:** MEDIUM
|
||||
|
||||
### 10. ERROR LOGGING
|
||||
- **Issue:** Some errors may expose sensitive info
|
||||
- **Risk:** Information disclosure
|
||||
- **Fix Time:** 2-3 hours
|
||||
- **Severity:** MEDIUM
|
||||
|
||||
---
|
||||
|
||||
## ✅ POSITIVE FINDINGS
|
||||
|
||||
### What's Working Well:
|
||||
- ✅ Database design is solid with proper constraints
|
||||
- ✅ Error handling middleware implemented
|
||||
- ✅ Winston logger properly configured
|
||||
- ✅ SQL queries use parameterized statements (SQL injection safe)
|
||||
- ✅ File upload has MIME type validation
|
||||
- ✅ Session cookies have httpOnly and sameSite flags
|
||||
- ✅ CORS/Origin checking implemented
|
||||
- ✅ Comprehensive feature set well-structured
|
||||
|
||||
---
|
||||
|
||||
## 📊 TIMELINE TO PRODUCTION READINESS
|
||||
|
||||
```
|
||||
Week 1 (CRITICAL):
|
||||
├─ Fix password hashing → 8 hours
|
||||
├─ Setup .env & remove hardcoded secrets → 4 hours
|
||||
├─ Implement CSRF tokens → 4 hours
|
||||
├─ Setup session store → 3 hours
|
||||
└─ Testing → 4 hours
|
||||
TOTAL: ~23 hours (3 days)
|
||||
|
||||
Week 2 (HIGH):
|
||||
├─ Input validation framework → 8 hours
|
||||
├─ Rate limiting → 4 hours
|
||||
├─ Backup automation → 3 hours
|
||||
├─ Security headers (helmet) → 2 hours
|
||||
└─ Testing & QA → 8 hours
|
||||
TOTAL: ~25 hours (3 days)
|
||||
|
||||
Week 3 (MEDIUM):
|
||||
├─ File upload scanning → 6 hours
|
||||
├─ Log review & cleanup → 4 hours
|
||||
├─ Documentation → 4 hours
|
||||
└─ Staging deployment → 4 hours
|
||||
TOTAL: ~18 hours (2 days)
|
||||
|
||||
GRAND TOTAL: ~66 hours (~2 weeks full-time)
|
||||
or ~3-4 weeks part-time (20 hrs/week)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💼 BUSINESS IMPACT
|
||||
|
||||
### Current State (If Deployed Now):
|
||||
- 🔴 **NOT RECOMMENDED** for production
|
||||
- Risk of account takeover is HIGH
|
||||
- Risk of data breach is CRITICAL
|
||||
- Compliance risk with regulations (OJK, PCI-DSS if payment processing)
|
||||
|
||||
### After Critical Fixes (1-2 weeks):
|
||||
- 🟢 **PRODUCTION READY** for initial deployment
|
||||
- Password security improved significantly
|
||||
- Credentials properly managed
|
||||
- Basic CSRF protection in place
|
||||
|
||||
### After All Fixes (3-4 weeks):
|
||||
- 🟢 **PRODUCTION HARDENED**
|
||||
- Enterprise-grade security
|
||||
- Compliance-ready
|
||||
- Audit-ready for external review
|
||||
|
||||
---
|
||||
|
||||
## 👥 RESOURCE REQUIREMENTS
|
||||
|
||||
### Team Needed:
|
||||
- **Backend Developer(s):** 1-2 (main focus)
|
||||
- **Security Reviewer:** 1 (part-time, 4-6 hours/week)
|
||||
- **QA/Tester:** 1 (part-time, 2-4 hours/week)
|
||||
- **DevOps/Infrastructure:** 0.5 (env setup, deployment)
|
||||
|
||||
### Skills Required:
|
||||
- Node.js/JavaScript (Backend)
|
||||
- Database migration experience
|
||||
- Security best practices knowledge
|
||||
- Testing & QA methodologies
|
||||
|
||||
---
|
||||
|
||||
## 🎯 RECOMMENDED ACTION PLAN
|
||||
|
||||
### Phase 1: IMMEDIATE (Days 1-2)
|
||||
1. ✅ Review this audit with team
|
||||
2. ✅ Create .env file with sensitive data
|
||||
3. ✅ Install required packages (bcrypt, helmet, etc)
|
||||
4. ✅ Backup current database
|
||||
|
||||
### Phase 2: CRITICAL FIXES (Days 3-5)
|
||||
1. ✅ Implement password hashing with bcrypt
|
||||
2. ✅ Migrate existing passwords
|
||||
3. ✅ Setup environment variables
|
||||
4. ✅ Remove hardcoded credentials
|
||||
|
||||
### Phase 3: TESTING & VALIDATION (Days 6-7)
|
||||
1. ✅ Test all authentication flows
|
||||
2. ✅ Verify password migration
|
||||
3. ✅ Test environment variable loading
|
||||
4. ✅ Deploy to staging
|
||||
|
||||
### Phase 4: HIGH PRIORITY FIXES (Week 2)
|
||||
1. ✅ Implement CSRF protection
|
||||
2. ✅ Setup session store
|
||||
3. ✅ Add rate limiting
|
||||
4. ✅ Input validation
|
||||
|
||||
### Phase 5: FINAL HARDENING (Week 3)
|
||||
1. ✅ Security headers (Helmet)
|
||||
2. ✅ File upload scanning
|
||||
3. ✅ Backup automation
|
||||
4. ✅ Final testing
|
||||
|
||||
### Phase 6: PRODUCTION DEPLOYMENT (End of Week 3)
|
||||
1. ✅ Security review checklist
|
||||
2. ✅ Final audit verification
|
||||
3. ✅ Deploy to production
|
||||
4. ✅ Monitoring setup
|
||||
|
||||
---
|
||||
|
||||
## 📋 DEPLOYMENT CHECKLIST
|
||||
|
||||
Before production deployment, verify:
|
||||
|
||||
- [ ] **Password Security**
|
||||
- [ ] Bcrypt implemented and tested
|
||||
- [ ] All passwords migrated to hashes
|
||||
- [ ] No plaintext passwords in code/config
|
||||
- [ ] Login flows working with hashed passwords
|
||||
|
||||
- [ ] **Credential Management**
|
||||
- [ ] .env file created and populated
|
||||
- [ ] Settings.json reviewed for sensitive data
|
||||
- [ ] .env added to .gitignore
|
||||
- [ ] Environment variables working
|
||||
|
||||
- [ ] **Access Control**
|
||||
- [ ] CSRF protection implemented
|
||||
- [ ] Rate limiting configured
|
||||
- [ ] Session management hardened
|
||||
- [ ] Role-based access control verified
|
||||
|
||||
- [ ] **Security Headers**
|
||||
- [ ] Helmet.js installed and configured
|
||||
- [ ] HTTPS enforced (if applicable)
|
||||
- [ ] Security headers present in responses
|
||||
|
||||
- [ ] **Data Protection**
|
||||
- [ ] Input validation implemented
|
||||
- [ ] Output sanitization verified
|
||||
- [ ] File uploads scanning configured
|
||||
- [ ] Database encryption (at rest if needed)
|
||||
|
||||
- [ ] **Logging & Monitoring**
|
||||
- [ ] Audit trail logged for sensitive operations
|
||||
- [ ] Error logging configured
|
||||
- [ ] Monitoring alerts setup
|
||||
- [ ] Log aggregation configured
|
||||
|
||||
- [ ] **Backup & Recovery**
|
||||
- [ ] Automated backup enabled
|
||||
- [ ] Backup encryption enabled
|
||||
- [ ] Recovery procedure tested
|
||||
- [ ] Backup retention policy set
|
||||
|
||||
- [ ] **Testing**
|
||||
- [ ] Unit tests passing
|
||||
- [ ] Integration tests passing
|
||||
- [ ] Security tests completed
|
||||
- [ ] Load testing completed
|
||||
- [ ] Penetration testing (optional but recommended)
|
||||
|
||||
- [ ] **Documentation**
|
||||
- [ ] Security practices documented
|
||||
- [ ] Incident response plan ready
|
||||
- [ ] Setup procedures documented
|
||||
- [ ] Troubleshooting guide prepared
|
||||
|
||||
---
|
||||
|
||||
## 🚨 KNOWN VULNERABILITIES
|
||||
|
||||
| Vulnerability | CVSS Score | Status | Action |
|
||||
|---|---|---|---|
|
||||
| Plaintext password storage | 9.8 | Open | CRITICAL - Fix immediately |
|
||||
| Default credentials | 8.7 | Open | CRITICAL - Fix immediately |
|
||||
| Unencrypted API keys | 8.9 | Open | CRITICAL - Fix immediately |
|
||||
| Weak password hashing | 7.5 | Open | HIGH - Fix this week |
|
||||
| Limited CSRF protection | 6.5 | Open | HIGH - Fix this week |
|
||||
| In-memory session store | 6.1 | Open | HIGH - Fix this week |
|
||||
| Incomplete input validation | 7.1 | Open | HIGH - Fix this week |
|
||||
|
||||
---
|
||||
|
||||
## 📞 CONTACT & SUPPORT
|
||||
|
||||
For questions or clarifications:
|
||||
1. Review detailed findings in `AUDIT_SECURITY_2026.md`
|
||||
2. Check implementation guide in `IMPLEMENTATION_GUIDE_SECURITY.md`
|
||||
3. Consult code samples in this document
|
||||
|
||||
---
|
||||
|
||||
## 📝 SIGN-OFF
|
||||
|
||||
**Audit Completed By:** Security & Code Quality Review Team
|
||||
**Date Completed:** 14 August 2026
|
||||
**Next Review:** 30 days after fixes implementation
|
||||
**Status:** READY FOR ACTION ✅
|
||||
|
||||
---
|
||||
|
||||
**RECOMMENDATION: DO NOT DEPLOY TO PRODUCTION UNTIL CRITICAL FIXES ARE COMPLETED**
|
||||
|
||||
For production deployment, all findings must be reviewed, and critical issues must be resolved.
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,833 +0,0 @@
|
||||
# AUDIT KEAMANAN & KUALITAS APLIKASI BILLING RTRWNET
|
||||
**Status:** ✅ AUDIT LENGKAP
|
||||
**Tanggal Audit:** 14 Agustus 2026
|
||||
**Auditor:** Security & Code Quality Review
|
||||
**Aplikasi Status:** 🟠 PRODUCTION WITH CRITICAL ISSUES
|
||||
|
||||
---
|
||||
|
||||
## 📋 RINGKASAN EKSEKUTIF
|
||||
|
||||
Aplikasi RTRWNET Billing System adalah sistem yang **comprehensive dan feature-rich** untuk manajemen ISP. Namun, audit menemukan **beberapa kerentanan keamanan KRITIS** yang harus segera diperbaiki sebelum production deployment atau jika sudah di production harus segera ditindaklanjuti.
|
||||
|
||||
### Skor Keamanan: 5.5/10 🔴
|
||||
- ✅ Aspek Positif: Struktur kode baik, error handling solid, logging terimplementasi
|
||||
- ⚠️ Aspek Kritis: Keamanan password, configuration management, input validation
|
||||
- ❌ Aspek Negatif: Plaintext credentials, default passwords, weak hashing
|
||||
|
||||
---
|
||||
|
||||
## 🔐 KEAMANAN KRITIS - SEGERA PERBAIKI
|
||||
|
||||
### 1. PASSWORD DISIMPAN DALAM PLAINTEXT ❌ CRITICAL
|
||||
|
||||
**Lokasi:**
|
||||
- [services/techService.js](services/techService.js#L4) - Password technician tersimpan plaintext di database
|
||||
- [settings.json](settings.json#L20-L21) - Admin password `admin123` dalam plaintext
|
||||
|
||||
**Masalah:**
|
||||
```javascript
|
||||
// ❌ TIDAK AMAN - Line 4 di techService.js
|
||||
const tech = db.prepare('SELECT * FROM technicians WHERE username = ? AND password = ? AND is_active = 1').get(username, password);
|
||||
// Password dibandingkan langsung tanpa hashing
|
||||
|
||||
// ❌ TIDAK AMAN - settings.json
|
||||
"admin_username": "admin",
|
||||
"admin_password": "admin123", // Password plaintext!
|
||||
```
|
||||
|
||||
**Risiko:**
|
||||
- Jika database bocor, semua password terekspos
|
||||
- Jika settings.json terlihat, credentials admin langsung ketahuan
|
||||
- Tidak compliance dengan standar keamanan (OWASP, ISO27001)
|
||||
- Database breach = total kompromis sistem
|
||||
|
||||
**Rekomendasi - HARUS DILAKUKAN:**
|
||||
|
||||
1. **Implementasi bcrypt untuk hashing password:**
|
||||
```javascript
|
||||
// Install bcrypt terlebih dahulu
|
||||
// npm install bcrypt
|
||||
|
||||
// Saat register/create user:
|
||||
const bcrypt = require('bcrypt');
|
||||
const hashedPassword = await bcrypt.hash(plainPassword, 10);
|
||||
// Simpan hashedPassword ke database
|
||||
|
||||
// Saat authenticate:
|
||||
const isValid = await bcrypt.compare(plainPassword, hashedPassword);
|
||||
if (isValid) {
|
||||
// Login berhasil
|
||||
}
|
||||
```
|
||||
|
||||
2. **Update database schema:**
|
||||
```sql
|
||||
-- Tambahkan kolom password_hash
|
||||
ALTER TABLE technicians ADD COLUMN password_hash TEXT;
|
||||
ALTER TABLE administrators ADD COLUMN password_hash TEXT;
|
||||
|
||||
-- Update existing passwords dengan hash
|
||||
-- (Lakukan migration script terpisah)
|
||||
```
|
||||
|
||||
3. **Jangan simpan password di settings.json:**
|
||||
```json
|
||||
// ❌ SEBELUM
|
||||
{
|
||||
"admin_password": "admin123"
|
||||
}
|
||||
|
||||
// ✅ SESUDAH - Gunakan database atau environment variable
|
||||
{
|
||||
"admin_user_id": 1 // Reference ke database saja
|
||||
}
|
||||
|
||||
// Atau gunakan .env file:
|
||||
ADMIN_PASSWORD_HASH="$2b$10$..."
|
||||
```
|
||||
|
||||
**Timeline:** IMMEDIATE - Sebelum production
|
||||
|
||||
---
|
||||
|
||||
### 2. DEFAULT CREDENTIALS (admin/admin123) ❌ CRITICAL
|
||||
|
||||
**Lokasi:** [settings.json](settings.json#L19-L21)
|
||||
|
||||
**Masalah:**
|
||||
- Default username/password yang sangat lemah
|
||||
- Siapapun yang tahu dokumentasi aplikasi bisa login
|
||||
- OWASP Top 10: A01:2021 - Broken Access Control
|
||||
|
||||
**Rekomendasi:**
|
||||
```bash
|
||||
# Script untuk force password change saat first-time setup
|
||||
node scripts/setup-admin-password.js
|
||||
|
||||
# Atau implementasikan setup wizard:
|
||||
- Saat pertama kali aplikasi dijalankan
|
||||
- Wajib setup admin username & password baru
|
||||
- Simpan di database dengan hash bcrypt
|
||||
- Tandai setup selesai di flag di database
|
||||
```
|
||||
|
||||
**Timeline:** IMMEDIATE
|
||||
|
||||
---
|
||||
|
||||
### 3. API KEY & CREDENTIALS DALAM PLAINTEXT ❌ CRITICAL
|
||||
|
||||
**Lokasi:** [settings.json](settings.json)
|
||||
```json
|
||||
"genieacs_password": "admin",
|
||||
"midtrans_server_key": "",
|
||||
"xendit_api_key": "",
|
||||
"tripay_private_key": "",
|
||||
"digiflazz_api_key": "",
|
||||
"radius_secret": "secret123"
|
||||
```
|
||||
|
||||
**Masalah:**
|
||||
- Semua credentials eksternal dalam plaintext di file JSON
|
||||
- File bisa terlihat di source code management
|
||||
- Risiko exposure jika server dikompromis
|
||||
- Tidak praktis untuk berbagai environment (dev/staging/production)
|
||||
|
||||
**Rekomendasi - HARUS DILAKUKAN:**
|
||||
|
||||
1. **Gunakan environment variables (.env):**
|
||||
```bash
|
||||
# File: .env (JANGAN commit ke git!)
|
||||
GENIEACS_URL=http://192.168.8.189:7557
|
||||
GENIEACS_USERNAME=admin
|
||||
GENIEACS_PASSWORD=SecurePassword123!
|
||||
MIDTRANS_SERVER_KEY=Mid-xxxxx-xxx
|
||||
XENDIT_API_KEY=xnd_development_xxxxx
|
||||
TRIPAY_API_KEY=xxxxx
|
||||
DIGIFLAZZ_API_KEY=xxxxx
|
||||
RADIUS_SECRET=RadiusSecret123!
|
||||
SESSION_SECRET=RandomSecure123!SecretHere
|
||||
ADMIN_API_KEY=AdminApiKey123!Secure
|
||||
```
|
||||
|
||||
2. **Implementasi di code:**
|
||||
```javascript
|
||||
// ✅ AMAN
|
||||
const settings = {
|
||||
genieacs_password: process.env.GENIEACS_PASSWORD || getSetting('genieacs_password'),
|
||||
midtrans_server_key: process.env.MIDTRANS_SERVER_KEY,
|
||||
xendit_api_key: process.env.XENDIT_API_KEY,
|
||||
// ... dll
|
||||
};
|
||||
```
|
||||
|
||||
3. **Update .gitignore:**
|
||||
```bash
|
||||
# Jangan commit file ini!
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
settings.json # Alternatif: commit template saja
|
||||
```
|
||||
|
||||
4. **Implementasi encryption untuk settings.json (jika diperlukan):**
|
||||
```javascript
|
||||
// Gunakan settingsEncryption.js yang sudah ada
|
||||
const { encryptValue, decryptValue } = require('./config/settingsEncryption');
|
||||
|
||||
// Encrypt credentials:
|
||||
const encrypted = encryptValue(credentialValue);
|
||||
|
||||
// Decrypt saat digunakan:
|
||||
const plainValue = decryptValue(encryptedValue);
|
||||
```
|
||||
|
||||
**Timeline:** IMMEDIATE
|
||||
|
||||
---
|
||||
|
||||
### 4. WEAK PASSWORD HASHING ⚠️ HIGH
|
||||
|
||||
**Lokasi Lainnya:**
|
||||
- [config/database.js](config/database.js#L627) - SHA256 untuk password
|
||||
- [services/agentService.js](services/agentService.js#L27) - MD5 hash
|
||||
- [routes/adminPortal.js](routes/adminPortal.js#L98) - MD5 hash
|
||||
|
||||
**Masalah:**
|
||||
```javascript
|
||||
// ❌ TIDAK AMAN - SHA256 bukan untuk password hashing
|
||||
const hash = crypto.createHash('sha256').update(input).digest('hex');
|
||||
|
||||
// ❌ TIDAK AMAN - MD5 sudah deprecated
|
||||
const sign = crypto.createHash('md5').update(username + apiKey).digest('hex');
|
||||
```
|
||||
|
||||
- SHA256/MD5 = algoritma hashing cepat (bisa di-brute force)
|
||||
- Tidak ada salt random
|
||||
- Tidak ada rate limiting pada hashing
|
||||
- Bisa di-crack dengan GPU dalam hitungan jam
|
||||
|
||||
**Rekomendasi:**
|
||||
```javascript
|
||||
// ✅ AMAN - Gunakan bcrypt atau Argon2
|
||||
const bcrypt = require('bcrypt');
|
||||
const hash = await bcrypt.hash(password, 10); // 10 = cost factor
|
||||
const isValid = await bcrypt.compare(plainPassword, hash);
|
||||
|
||||
// Atau Argon2 (lebih kuat):
|
||||
const argon2 = require('argon2');
|
||||
const hash = await argon2.hash(password);
|
||||
const isValid = await argon2.verify(hash, password);
|
||||
```
|
||||
|
||||
**Timeline:** HIGH - Dalam 1 minggu
|
||||
|
||||
---
|
||||
|
||||
## 🔒 KEAMANAN SEDANG - TINGKATKAN
|
||||
|
||||
### 5. SESSION MANAGEMENT ⚠️ MEDIUM
|
||||
|
||||
**Lokasi:** [app-customer.js](app-customer.js#L79-L90)
|
||||
|
||||
**Status Saat Ini:** ✅ Baik
|
||||
```javascript
|
||||
app.use(session({
|
||||
secret: getSetting('session_secret', 'default-secret'),
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
secure: Boolean(cookieSecure), // ✅ HTTPS only (jika production)
|
||||
httpOnly: true, // ✅ JavaScript tidak bisa akses
|
||||
sameSite: 'lax', // ✅ CSRF protection
|
||||
maxAge: 24 * 60 * 60 * 1000 // ✅ Timeout 24 jam
|
||||
},
|
||||
name: 'customer.sid'
|
||||
}));
|
||||
```
|
||||
|
||||
**Rekomendasi Improvement:**
|
||||
|
||||
1. **Gunakan session store eksternal:**
|
||||
```javascript
|
||||
// Jangan gunakan default memory store di production
|
||||
const SessionStore = require('better-sqlite3-session-store')(session);
|
||||
const store = new SessionStore({
|
||||
client: db,
|
||||
expired: {
|
||||
clear: true,
|
||||
intervalMs: 15 * 60 * 1000 // Clear expired sessions tiap 15 menit
|
||||
}
|
||||
});
|
||||
|
||||
app.use(session({
|
||||
store: store,
|
||||
// ... config lainnya
|
||||
}));
|
||||
```
|
||||
|
||||
2. **Regenerate session ID saat login:**
|
||||
```javascript
|
||||
// Saat login berhasil
|
||||
req.session.regenerate((err) => {
|
||||
if (err) return res.status(500).json({ error: 'Session error' });
|
||||
req.session.userId = user.id;
|
||||
req.session.userRole = user.role;
|
||||
// ... simpan data user lainnya
|
||||
res.redirect('/dashboard');
|
||||
});
|
||||
```
|
||||
|
||||
3. **Validasi session pada setiap request:**
|
||||
```javascript
|
||||
app.use((req, res, next) => {
|
||||
// Cek timeout session
|
||||
const lastActivity = req.session.lastActivity || Date.now();
|
||||
const inactivityTimeout = 30 * 60 * 1000; // 30 menit
|
||||
|
||||
if (Date.now() - lastActivity > inactivityTimeout) {
|
||||
req.session.destroy();
|
||||
return res.status(401).json({ error: 'Session expired' });
|
||||
}
|
||||
|
||||
req.session.lastActivity = Date.now();
|
||||
next();
|
||||
});
|
||||
```
|
||||
|
||||
**Timeline:** MEDIUM - 2 minggu
|
||||
|
||||
---
|
||||
|
||||
### 6. CSRF PROTECTION ⚠️ MEDIUM
|
||||
|
||||
**Status Saat Ini:** ⚠️ Terbatas
|
||||
```javascript
|
||||
// Hanya check Referer/Origin, tidak ada CSRF token explicit
|
||||
if (origin) {
|
||||
const originHost = new URL(origin).host;
|
||||
if (originHost !== host) {
|
||||
return res.status(403).json({ error: 'Forbidden - Invalid Origin' });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Masalah:**
|
||||
- Referer bisa di-spoof dalam beberapa kasus
|
||||
- Browser lama atau proxy bisa hilangkan Referer header
|
||||
- Tidak ada explicit CSRF token di form
|
||||
|
||||
**Rekomendasi:**
|
||||
|
||||
1. **Implementasi CSRF token library:**
|
||||
```javascript
|
||||
const csrf = require('csurf');
|
||||
const csrfProtection = csrf({ cookie: false }); // Gunakan session, bukan cookie
|
||||
|
||||
app.post('/admin/action', csrfProtection, (req, res) => {
|
||||
// CSRF token sudah tervalidasi otomatis
|
||||
// Proses request
|
||||
});
|
||||
```
|
||||
|
||||
2. **Di EJS templates:**
|
||||
```ejs
|
||||
<form method="POST" action="/admin/billing/generate">
|
||||
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
|
||||
<button type="submit">Generate Invoice</button>
|
||||
</form>
|
||||
```
|
||||
|
||||
**Timeline:** MEDIUM - 2 minggu
|
||||
|
||||
---
|
||||
|
||||
### 7. INPUT VALIDATION & SANITIZATION ⚠️ MEDIUM
|
||||
|
||||
**Issue:**
|
||||
Beberapa endpoint tidak melakukan validasi input yang ketat sebelum:
|
||||
- Memasukkan ke database
|
||||
- Menampilkan ke user (XSS risk)
|
||||
- Digunakan dalam query
|
||||
|
||||
**Contoh lokasi yang perlu improvement:**
|
||||
- [routes/adminPortal.js](routes/adminPortal.js) - Bulk operations
|
||||
- [routes/customerPortal.js](routes/customerPortal.js) - Customer data update
|
||||
- File upload handling
|
||||
|
||||
**Rekomendasi:**
|
||||
|
||||
1. **Gunakan input validation library:**
|
||||
```bash
|
||||
npm install joi express-validator
|
||||
```
|
||||
|
||||
2. **Implementasi validation:**
|
||||
```javascript
|
||||
const { body, validationResult } = require('express-validator');
|
||||
|
||||
router.post('/admin/customers/add', [
|
||||
body('name').trim().notEmpty().isLength({ max: 100 }),
|
||||
body('phone').trim().isMobilePhone('id-ID'),
|
||||
body('email').isEmail().normalizeEmail(),
|
||||
body('address').trim().isLength({ max: 255 }),
|
||||
body('package_id').isInt({ gt: 0 }),
|
||||
], (req, res) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() });
|
||||
}
|
||||
// Lanjutkan proses
|
||||
});
|
||||
```
|
||||
|
||||
3. **Sanitize output di EJS:**
|
||||
```ejs
|
||||
<!-- ❌ TIDAK AMAN - Bisa XSS -->
|
||||
<%= customer.name %>
|
||||
|
||||
<!-- ✅ AMAN - Sanitized -->
|
||||
<%- cleanHtml(customer.name) %>
|
||||
|
||||
<!-- atau gunakan escape by default di EJS config -->
|
||||
```
|
||||
|
||||
**Timeline:** MEDIUM - 2 minggu
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ KEAMANAN RENDAH - PERHATIAN
|
||||
|
||||
### 8. FILE UPLOAD SECURITY ⚠️ LOW
|
||||
|
||||
**Status Saat Ini:** ⚠️ Cukup
|
||||
```javascript
|
||||
// ✅ Ada validasi MIME type
|
||||
const allowedTypes = /jpeg|jpg|png|gif|webp/;
|
||||
const mimetype = allowedTypes.test(file.mimetype);
|
||||
|
||||
// ✅ Ada file size limit (5MB)
|
||||
limits: { fileSize: 5 * 1024 * 1024 }
|
||||
```
|
||||
|
||||
**Rekomendasi Improvement:**
|
||||
|
||||
1. **Validasi file lebih ketat:**
|
||||
```javascript
|
||||
const FileType = require('file-type');
|
||||
|
||||
// Validasi magic bytes (bukan hanya extension)
|
||||
const fileType = await FileType.fromBuffer(file.buffer);
|
||||
if (!['image/jpeg', 'image/png', 'image/webp'].includes(fileType?.mime)) {
|
||||
throw new Error('Invalid file type');
|
||||
}
|
||||
```
|
||||
|
||||
2. **Scan file dengan antivirus:**
|
||||
```javascript
|
||||
const ClamScan = require('clamscan');
|
||||
const clamscan = await new ClamScan().init({
|
||||
clamdscan: { host: 'localhost', port: 3310 }
|
||||
});
|
||||
|
||||
const { isInfected } = await clamscan.scanFile(filePath);
|
||||
if (isInfected) {
|
||||
fs.unlinkSync(filePath);
|
||||
throw new Error('Malicious file detected');
|
||||
}
|
||||
```
|
||||
|
||||
**Timeline:** LOW - Dalam 1 bulan
|
||||
|
||||
---
|
||||
|
||||
### 9. LOGGING & MONITORING ⚠️ LOW
|
||||
|
||||
**Status Saat Ini:** ✅ Baik
|
||||
- Winston logger terimplementasi dengan baik
|
||||
- Terpisah antara error.log, combined.log, exceptions.log
|
||||
- Console output di development
|
||||
|
||||
**Rekomendasi:**
|
||||
|
||||
1. **Audit trail untuk sensitive operations:**
|
||||
```javascript
|
||||
const auditTrailSvc = require('./services/auditTrailService');
|
||||
|
||||
// Log setiap action sensitive
|
||||
auditTrailSvc.logAction({
|
||||
user_id: req.session.adminId,
|
||||
action: 'invoice_generated',
|
||||
resource_type: 'invoice',
|
||||
resource_id: invoiceId,
|
||||
changes: { status: 'generated' },
|
||||
ip_address: req.ip,
|
||||
timestamp: new Date()
|
||||
});
|
||||
```
|
||||
|
||||
2. **Centralized log aggregation:**
|
||||
```javascript
|
||||
// Contoh: Kirim logs ke ELK Stack atau Datadog
|
||||
const winston = require('winston');
|
||||
const WinstonCloudWatch = require('winston-cloudwatch');
|
||||
|
||||
logger.add(new WinstonCloudWatch({
|
||||
logGroupName: 'billing-rtrwnet',
|
||||
logStreamName: 'production',
|
||||
awsRegion: 'ap-southeast-1'
|
||||
}));
|
||||
```
|
||||
|
||||
**Timeline:** LOW - Dalam 1 bulan
|
||||
|
||||
---
|
||||
|
||||
## 🎯 KUALITAS KODE
|
||||
|
||||
### 10. ERROR HANDLING ✅ BAIK
|
||||
|
||||
**Status:** ✅ Baik
|
||||
- Middleware error handler sudah terimplementasi
|
||||
- Custom error classes (ValidationError, UnauthorizedError, etc)
|
||||
- Proper HTTP status codes
|
||||
|
||||
**Minor Improvements:**
|
||||
```javascript
|
||||
// Pastikan semua error di-catch dan di-log
|
||||
router.get('/api/data', async (req, res, next) => {
|
||||
try {
|
||||
const data = await fetchData();
|
||||
res.json(data);
|
||||
} catch (err) {
|
||||
logger.error(`API Error: ${err.message}`, {
|
||||
stack: err.stack,
|
||||
endpoint: req.path,
|
||||
method: req.method,
|
||||
user_id: req.session?.userId
|
||||
});
|
||||
next(err); // Pass ke error handler middleware
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 11. DATABASE QUERIES ✅ BAIK
|
||||
|
||||
**Status:** ✅ Baik
|
||||
- Menggunakan parameterized queries (prepared statements)
|
||||
- Tidak ada string concatenation pada SQL queries
|
||||
- Foreign key constraints aktif
|
||||
|
||||
**Contoh aman:**
|
||||
```javascript
|
||||
// ✅ AMAN - Parameterized query
|
||||
const customer = db.prepare('SELECT * FROM customers WHERE id = ?').get(customerId);
|
||||
|
||||
// ✅ AMAN - Multiple parameters
|
||||
const result = db.prepare('SELECT * FROM customers WHERE router_id = ? AND status = ?').all(routerId, 'active');
|
||||
```
|
||||
|
||||
**Minor Issue - Dynamic SQL:**
|
||||
```javascript
|
||||
// ⚠️ PERLU REVIEW - Jika search input tidak di-sanitize
|
||||
const base = `SELECT * FROM customers WHERE ...${userInput}...`;
|
||||
|
||||
// ✅ SOLUSI - Gunakan parameterized dengan conditions array
|
||||
const whereClauses = [];
|
||||
const params = [];
|
||||
if (search) {
|
||||
whereClauses.push('name LIKE ?');
|
||||
params.push(`%${search}%`);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 12. ASYNC/AWAIT HANDLING ⚠️ MEDIUM
|
||||
|
||||
**Issue:**
|
||||
Beberapa function masih menggunakan callback style yang bisa error-prone.
|
||||
|
||||
**Contoh yang perlu improvement:**
|
||||
```javascript
|
||||
// ❌ CALLBACK STYLE
|
||||
fs.readFile(path, (err, data) => {
|
||||
if (err) handleError(err);
|
||||
// process data
|
||||
});
|
||||
|
||||
// ✅ ASYNC/AWAIT STYLE
|
||||
try {
|
||||
const data = await fs.promises.readFile(path);
|
||||
// process data
|
||||
} catch (err) {
|
||||
handleError(err);
|
||||
}
|
||||
```
|
||||
|
||||
**Timeline:** LOW - Refactoring gradual
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ DATABASE INTEGRITY
|
||||
|
||||
### 13. Database Schema ✅ SOLID
|
||||
|
||||
**Status:** ✅ Sudah baik
|
||||
- Foreign key constraints aktif
|
||||
- Proper data types
|
||||
- Indexes pada frequently queried fields
|
||||
- WAL mode untuk reliability
|
||||
|
||||
**Rekomendasi:**
|
||||
```sql
|
||||
-- Pastikan ada indexes pada frequently queried columns
|
||||
CREATE INDEX IF NOT EXISTS idx_customers_router_id ON customers(router_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_invoices_customer_id ON invoices(customer_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_invoices_status ON invoices(status);
|
||||
CREATE INDEX IF NOT EXISTS idx_pppoe_users_username ON pppoe_users(username);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 14. Backup & Recovery ⚠️ MEDIUM
|
||||
|
||||
**Status Saat Ini:** ⚠️ Ada backup service tapi belum clear
|
||||
- `backupService.js` sudah ada
|
||||
- `auto_backup_enabled: false` di settings.json (disabled!)
|
||||
|
||||
**Rekomendasi:**
|
||||
|
||||
```javascript
|
||||
// Enable automatic backup
|
||||
const settings = {
|
||||
"auto_backup_enabled": true,
|
||||
"auto_backup_interval": "0 2 * * *", // Jam 2 pagi setiap hari
|
||||
"auto_backup_retention_days": 30,
|
||||
"backup_encryption_enabled": true
|
||||
};
|
||||
|
||||
// Implementasi di cronService.js
|
||||
const backup = require('./services/backupService');
|
||||
schedule.scheduleJob('0 2 * * *', async () => {
|
||||
try {
|
||||
const result = await backup.createBackup();
|
||||
logger.info(`Backup created: ${result.filename}`);
|
||||
} catch (err) {
|
||||
logger.error(`Backup failed: ${err.message}`);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
**Timeline:** MEDIUM - 2 minggu
|
||||
|
||||
---
|
||||
|
||||
## 📋 ACTION ITEMS & TIMELINE
|
||||
|
||||
### IMMEDIATE (Hari ini - 48 jam) 🔴
|
||||
- [ ] Implementasi bcrypt untuk password hashing
|
||||
- [ ] Hapus default credentials dari settings.json
|
||||
- [ ] Setup .env file untuk credentials
|
||||
- [ ] Update .gitignore untuk .env dan settings.json
|
||||
- [ ] Create setup wizard untuk first-time admin password
|
||||
|
||||
### HIGH PRIORITY (Minggu 1) 🟠
|
||||
- [ ] Migrate semua password di database dengan bcrypt
|
||||
- [ ] Implementasi CSRF token protection
|
||||
- [ ] Setup session store eksternal (SQLite session)
|
||||
- [ ] Enable auto backup dalam cron jobs
|
||||
- [ ] Update API key management
|
||||
|
||||
### MEDIUM PRIORITY (Minggu 2-3) 🟡
|
||||
- [ ] Input validation dengan joi/express-validator
|
||||
- [ ] Enhanced file upload scanning
|
||||
- [ ] Session regeneration saat login
|
||||
- [ ] Centralized logging setup
|
||||
- [ ] Database query audit
|
||||
|
||||
### LOW PRIORITY (Bulan 1) 🟢
|
||||
- [ ] Antivirus integration untuk file upload
|
||||
- [ ] Log aggregation (ELK/Datadog)
|
||||
- [ ] Async/await refactoring
|
||||
- [ ] Performance optimization
|
||||
- [ ] Documentation update
|
||||
|
||||
---
|
||||
|
||||
## 📊 SECURITY CHECKLIST - PRODUCTION DEPLOYMENT
|
||||
|
||||
Sebelum go live ke production, pastikan:
|
||||
|
||||
- [ ] **Authentication**
|
||||
- [ ] Password menggunakan bcrypt (cost ≥ 10)
|
||||
- [ ] Admin password bukan default
|
||||
- [ ] Session timeout implemented
|
||||
- [ ] Session regeneration saat login
|
||||
|
||||
- [ ] **Authorization**
|
||||
- [ ] Role-based access control diterapkan
|
||||
- [ ] API endpoints check permission
|
||||
- [ ] Admin endpoints protected
|
||||
|
||||
- [ ] **Data Protection**
|
||||
- [ ] Sensitive data encrypted at rest
|
||||
- [ ] HTTPS enforced (secure cookie flag)
|
||||
- [ ] CSRF protection aktif
|
||||
- [ ] XSS protection aktif
|
||||
|
||||
- [ ] **Configuration**
|
||||
- [ ] Semua credentials di .env
|
||||
- [ ] .env tidak di-commit
|
||||
- [ ] settings.json di-review untuk sensitive data
|
||||
- [ ] API keys di-rotate
|
||||
|
||||
- [ ] **Logging & Monitoring**
|
||||
- [ ] Audit trail untuk sensitive operations
|
||||
- [ ] Error logging configured
|
||||
- [ ] Monitoring alerts setup
|
||||
|
||||
- [ ] **Backup & Recovery**
|
||||
- [ ] Automated backup enabled
|
||||
- [ ] Backup encryption enabled
|
||||
- [ ] Recovery procedure tested
|
||||
- [ ] Backup storage off-site
|
||||
|
||||
- [ ] **Network**
|
||||
- [ ] Firewall configured
|
||||
- [ ] Only needed ports open
|
||||
- [ ] Rate limiting implemented
|
||||
- [ ] DDoS protection (jika applicable)
|
||||
|
||||
- [ ] **Testing**
|
||||
- [ ] Security testing completed
|
||||
- [ ] Penetration testing done (jika budget ada)
|
||||
- [ ] Load testing completed
|
||||
- [ ] Recovery testing done
|
||||
|
||||
---
|
||||
|
||||
## 📞 REKOMENDASI LANJUTAN
|
||||
|
||||
### 1. Security Code Review
|
||||
Jadwalkan code review berkala (monthly) oleh developer berpengalaman untuk:
|
||||
- Review pull requests sebelum merge
|
||||
- Check untuk security vulnerabilities
|
||||
- Update dependencies
|
||||
|
||||
### 2. Dependency Management
|
||||
```bash
|
||||
# Regular security audit
|
||||
npm audit
|
||||
|
||||
# Update dependencies (carefully)
|
||||
npm update
|
||||
|
||||
# Check for outdated packages
|
||||
npm outdated
|
||||
```
|
||||
|
||||
### 3. Environment Management
|
||||
```bash
|
||||
# Development
|
||||
NODE_ENV=development npm start
|
||||
|
||||
# Staging
|
||||
NODE_ENV=staging npm start
|
||||
|
||||
# Production
|
||||
NODE_ENV=production npm start
|
||||
```
|
||||
|
||||
### 4. Rate Limiting
|
||||
```javascript
|
||||
const rateLimit = require('express-rate-limit');
|
||||
|
||||
const loginLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 menit
|
||||
max: 5, // 5 attempts
|
||||
message: 'Terlalu banyak login attempts, coba lagi nanti'
|
||||
});
|
||||
|
||||
router.post('/login', loginLimiter, authController.login);
|
||||
```
|
||||
|
||||
### 5. Security Headers
|
||||
```javascript
|
||||
const helmet = require('helmet');
|
||||
app.use(helmet()); // Adds various HTTP headers for security
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 PERFORMANCE OPTIMIZATION
|
||||
|
||||
### Database Performance
|
||||
```javascript
|
||||
// Add connection pooling
|
||||
const DatabasePool = require('pg-pool');
|
||||
|
||||
// Add query optimization
|
||||
EXPLAIN ANALYZE SELECT ...; // Check query plans
|
||||
```
|
||||
|
||||
### Caching Strategy
|
||||
```javascript
|
||||
// Implement Redis caching untuk frequently accessed data
|
||||
const redis = require('redis');
|
||||
const client = redis.createClient();
|
||||
|
||||
// Cache customer data
|
||||
const getCachedCustomer = async (id) => {
|
||||
const cached = await client.get(`customer:${id}`);
|
||||
if (cached) return JSON.parse(cached);
|
||||
|
||||
const customer = db.prepare('SELECT * FROM customers WHERE id = ?').get(id);
|
||||
await client.setex(`customer:${id}`, 3600, JSON.stringify(customer));
|
||||
return customer;
|
||||
};
|
||||
```
|
||||
|
||||
### API Response Compression
|
||||
```javascript
|
||||
const compression = require('compression');
|
||||
app.use(compression()); // Compress responses
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎓 DEVELOPER TRAINING
|
||||
|
||||
Recommended training topics untuk team:
|
||||
1. OWASP Top 10 vulnerabilities
|
||||
2. Secure coding practices
|
||||
3. Dependency vulnerability scanning
|
||||
4. Database security
|
||||
5. API security
|
||||
6. Authentication & Authorization best practices
|
||||
|
||||
---
|
||||
|
||||
## 📝 KESIMPULAN
|
||||
|
||||
Aplikasi RTRWNET Billing System memiliki **foundation yang kuat** dengan:
|
||||
- ✅ Arsitektur yang well-structured
|
||||
- ✅ Comprehensive feature set
|
||||
- ✅ Good error handling & logging
|
||||
- ✅ Proper database design
|
||||
|
||||
Namun ada **beberapa kerentanan keamanan KRITIS** yang harus diperbaiki:
|
||||
- 🔴 Password plaintext storage
|
||||
- 🔴 Default credentials
|
||||
- 🔴 Unencrypted credentials in config
|
||||
|
||||
Dengan mengikuti rekomendasi di audit ini, aplikasi dapat mencapai **production-ready security level** dalam waktu 2-3 minggu.
|
||||
|
||||
---
|
||||
|
||||
**Status Audit: COMPLETE**
|
||||
**Recommendation: DO NOT DEPLOY TO PRODUCTION** sampai critical issues di-fix.
|
||||
**Next Review:** 30 hari setelah fixes di-implementasi
|
||||
@@ -1,458 +0,0 @@
|
||||
# AUDIT SUMMARY - Aplikasi Billing B481
|
||||
|
||||
**Tanggal Audit:** Juni 2026
|
||||
**Status:** ✅ COMPLETE & DOCUMENTED
|
||||
**Aplikasi Status:** 🟢 PRODUCTION READY
|
||||
|
||||
---
|
||||
|
||||
## 📌 Executive Summary
|
||||
|
||||
Telah dilakukan audit **lengkap & teliti** terhadap aplikasi Billing RTRWnet (B481) yang sedang berjalan. Aplikasi ini merupakan sistem **comprehensive ISP billing & network management** dengan 15+ portal dan fitur-fitur advanced.
|
||||
|
||||
**Hasil:** Aplikasi dalam kondisi **BAIK & SIAP PRODUCTION** dengan beberapa rekomendasi improvement untuk keamanan & performance.
|
||||
|
||||
---
|
||||
|
||||
## 📦 Dokumentasi yang Telah Dibuat
|
||||
|
||||
### 1. **AUDIT_LENGKAP.md** (Komprehensif)
|
||||
- Informasi umum aplikasi
|
||||
- Arsitektur sistem lengkap
|
||||
- Database schema overview
|
||||
- Endpoints & routes (semua portal)
|
||||
- Fitur-fitur utama (15+ modul)
|
||||
- Keamanan & otentikasi
|
||||
- Frontend & UI
|
||||
- Configurasi sistem
|
||||
- Production checklist
|
||||
- Performance optimization
|
||||
- 📊 **Size:** ~50 KB (detailed reference)
|
||||
|
||||
### 2. **QUICK_REFERENCE.md** (Panduan Cepat)
|
||||
- Quick start commands
|
||||
- Common tasks (5 menit selesai)
|
||||
- Security settings (MUST DO)
|
||||
- Important endpoints
|
||||
- Database commands
|
||||
- Cron jobs schedule
|
||||
- Troubleshooting guide
|
||||
- Multi-language info
|
||||
- Pre-launch checklist
|
||||
- 📊 **Size:** ~10 KB (quick lookup)
|
||||
|
||||
### 3. **DATABASE_SCHEMA.md** (Struktur Database)
|
||||
- Tabel-tabel lengkap dengan struktur
|
||||
- Detailed field descriptions
|
||||
- Foreign key relationships
|
||||
- Query examples
|
||||
- Optimization tips
|
||||
- Backup & recovery procedures
|
||||
- 📊 **Size:** ~30 KB (database reference)
|
||||
|
||||
### 4. **BACKUP_CHECKLIST.md** (Backup & Recovery)
|
||||
- Pre-audit backup procedures
|
||||
- Step-by-step backup guide
|
||||
- Restore procedures (multiple scenarios)
|
||||
- Automated backup setup (cron)
|
||||
- Disaster recovery plan
|
||||
- Backup verification checklist
|
||||
- 📊 **Size:** ~20 KB (operational guide)
|
||||
|
||||
### 5. **AUDIT_SUMMARY.md** (File ini)
|
||||
- Executive overview
|
||||
- Quick links
|
||||
- Key statistics
|
||||
- Next steps
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Key Findings
|
||||
|
||||
### ✅ Strengths
|
||||
1. **Comprehensive Feature Set** - 15+ portal dengan integrasi lengkap
|
||||
2. **Well-Organized Architecture** - Services, routes, middleware pattern
|
||||
3. **Multi-Integration** - MikroTik, GenieACS, OLT, Payment Gateways
|
||||
4. **Automation Ready** - Cron jobs untuk billing, isolir, reminders
|
||||
5. **Lightweight** - SQLite, no external database required
|
||||
6. **Scalable Design** - Multi-router, multi-ACS, multi-gateway support
|
||||
7. **Built-in ACS** - TR-069 server tanpa GenieACS eksternal
|
||||
|
||||
### ⚠️ Areas for Improvement
|
||||
1. **Password Security** - Need bcrypt implementation (currently plaintext risk)
|
||||
2. **API Security** - Implement JWT or token-based authentication
|
||||
3. **Rate Limiting** - Prevent brute force attacks
|
||||
4. **Performance** - Add Redis session store, implement caching
|
||||
5. **Testing** - Add comprehensive test suite
|
||||
6. **Documentation** - API documentation (Swagger/OpenAPI)
|
||||
|
||||
---
|
||||
|
||||
## 📊 Application Statistics
|
||||
|
||||
### Codebase Size
|
||||
```
|
||||
Templates (EJS): 78 files
|
||||
Services: 27 modules
|
||||
Routes: 12 modules
|
||||
Configuration: 10 files
|
||||
Views/Partials: ~100+ components
|
||||
```
|
||||
|
||||
### Features Count
|
||||
| Category | Count |
|
||||
|----------|-------|
|
||||
| Portals | 6 (Customer, Admin, Tech, Agent, Collector, Finance) |
|
||||
| API Endpoints | 100+ |
|
||||
| Database Tables | 30+ |
|
||||
| Cron Jobs | 6 |
|
||||
| Payment Gateways | 4 |
|
||||
| External Integrations | 6+ |
|
||||
|
||||
### Database Typical Size
|
||||
```
|
||||
Small deployment: 10-50 MB
|
||||
Medium deployment: 50-150 MB
|
||||
Large deployment: 200-500 MB
|
||||
(Depends on data retention & transaction volume)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### 1. Review Documentation
|
||||
```
|
||||
Read in this order:
|
||||
1. QUICK_REFERENCE.md (5 min)
|
||||
2. AUDIT_LENGKAP.md (30 min)
|
||||
3. DATABASE_SCHEMA.md (20 min)
|
||||
4. BACKUP_CHECKLIST.md (10 min)
|
||||
```
|
||||
|
||||
### 2. Initial Backup
|
||||
```bash
|
||||
bash scripts/fix-database.sh # Optional: fix any DB issues
|
||||
node scripts/verify-database.js # Verify DB integrity
|
||||
npm start # Start application
|
||||
```
|
||||
|
||||
### 3. Configuration
|
||||
```bash
|
||||
# Edit settings.json:
|
||||
# - Change admin password
|
||||
# - Configure MikroTik connection
|
||||
# - Setup payment gateway
|
||||
# - Configure WhatsApp (optional)
|
||||
```
|
||||
|
||||
### 4. Verify All Portals
|
||||
```
|
||||
http://localhost:3001/admin/login ← Admin panel
|
||||
http://localhost:3001/customer/login ← Customer self-service
|
||||
http://localhost:3001/tech/login ← Technician portal
|
||||
http://localhost:3001/agent/login ← Agent sales
|
||||
http://localhost:3001/health ← Health check
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Security Checklist (CRITICAL)
|
||||
|
||||
**Before Production Deployment:**
|
||||
|
||||
- [ ] Change `session_secret` → Random 32+ characters
|
||||
- [ ] Change `admin_password` → Strong password
|
||||
- [ ] Change `admin_api_key` → Random string
|
||||
- [ ] Enable HTTPS (via reverse proxy: nginx)
|
||||
- [ ] Restrict admin panel access (/admin via IP whitelist)
|
||||
- [ ] Setup firewall rules
|
||||
- [ ] Implement bcrypt for password hashing
|
||||
- [ ] Setup audit logging
|
||||
- [ ] Test backup & restore procedure
|
||||
- [ ] Verify all external connections (MikroTik, GenieACS, OLT)
|
||||
|
||||
---
|
||||
|
||||
## 📋 Operational Runbook
|
||||
|
||||
### Daily Tasks
|
||||
```
|
||||
✓ Monitor CPU/RAM usage
|
||||
✓ Check /health endpoint
|
||||
✓ Review error logs
|
||||
✓ Monitor WhatsApp connection
|
||||
```
|
||||
|
||||
### Weekly Tasks
|
||||
```
|
||||
✓ Verify backups completed
|
||||
✓ Check database size
|
||||
✓ Review payment transactions
|
||||
✓ Test restore procedures (weekly)
|
||||
```
|
||||
|
||||
### Monthly Tasks
|
||||
```
|
||||
✓ Cleanup old audit trails
|
||||
✓ Cleanup old traffic samples
|
||||
✓ Review performance metrics
|
||||
✓ Update SSL certificates (if HTTPS)
|
||||
```
|
||||
|
||||
### Ad-Hoc Tasks
|
||||
```
|
||||
✓ Billing generation (manual)
|
||||
✓ Customer isolir (manual)
|
||||
✓ Settings updates
|
||||
✓ Emergency procedures
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Maintenance Procedures
|
||||
|
||||
### Adding New Customer
|
||||
```
|
||||
Admin Panel → Customers → Add New
|
||||
- Enter basic info (name, phone, address)
|
||||
- Assign package & MikroTik router
|
||||
- Set install date
|
||||
- System auto-generates invoice on billing cycle
|
||||
```
|
||||
|
||||
### Updating Payment Gateway
|
||||
```
|
||||
Admin Panel → Settings → Payment Gateway
|
||||
- Select gateway (Midtrans, Tripay, Xendit, Duitku)
|
||||
- Enter API credentials
|
||||
- Test connection
|
||||
- Save & restart if needed
|
||||
```
|
||||
|
||||
### Generating Vouchers
|
||||
```
|
||||
Admin Panel → MikroTik → Vouchers
|
||||
- Create batch (name, quantity, nominal)
|
||||
- Generate codes
|
||||
- Print codes
|
||||
- Distribute to agents
|
||||
```
|
||||
|
||||
### Monitoring System
|
||||
```
|
||||
Admin Panel → Monitoring
|
||||
- Check CPU/RAM usage
|
||||
- Check disk space
|
||||
- Check database size
|
||||
- Check connectivity status
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎓 Training Resources
|
||||
|
||||
### For Admin
|
||||
1. QUICK_REFERENCE.md → Common admin tasks
|
||||
2. AUDIT_LENGKAP.md section 5.1-5.5 → Billing, MikroTik, GenieACS
|
||||
3. Admin portal built-in help
|
||||
|
||||
### For Technician
|
||||
1. QUICK_REFERENCE.md → Tech portal features
|
||||
2. AUDIT_LENGKAP.md section 4.3 → Tech routes
|
||||
3. Ticket management system
|
||||
|
||||
### For Agent/Cashier
|
||||
1. Portal home page → Help section
|
||||
2. QUICK_REFERENCE.md → Agent features
|
||||
3. Dashboard tutorial
|
||||
|
||||
---
|
||||
|
||||
## 📞 Support & Escalation
|
||||
|
||||
### Level 1: Self-Service
|
||||
- Check QUICK_REFERENCE.md
|
||||
- Check built-in help in portals
|
||||
- Review error messages
|
||||
|
||||
### Level 2: Admin Support
|
||||
- Check AUDIT_LENGKAP.md troubleshooting
|
||||
- Review application logs: `pm2 logs`
|
||||
- Check database: `node scripts/verify-database.js`
|
||||
|
||||
### Level 3: Developer Support
|
||||
- Check GitHub: https://github.com/alijayanet/billing-rtrw
|
||||
- Contact: Ali Jaya Net (081947215703)
|
||||
- Email: alijayanet@gmail.com
|
||||
|
||||
---
|
||||
|
||||
## 📁 Document File Locations
|
||||
|
||||
All documentation saved in application root:
|
||||
|
||||
```
|
||||
billing-B481/
|
||||
├── AUDIT_LENGKAP.md ← Comprehensive audit (50 KB)
|
||||
├── QUICK_REFERENCE.md ← Quick start guide (10 KB)
|
||||
├── DATABASE_SCHEMA.md ← Database reference (30 KB)
|
||||
├── BACKUP_CHECKLIST.md ← Backup procedures (20 KB)
|
||||
└── AUDIT_SUMMARY.md ← This file (15 KB)
|
||||
|
||||
Total documentation: ~125 KB
|
||||
```
|
||||
|
||||
**Backup these files also!** Add to your backup script:
|
||||
|
||||
```bash
|
||||
tar -czf audit-docs-backup.tar.gz \
|
||||
AUDIT_LENGKAP.md \
|
||||
QUICK_REFERENCE.md \
|
||||
DATABASE_SCHEMA.md \
|
||||
BACKUP_CHECKLIST.md \
|
||||
AUDIT_SUMMARY.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✨ Next Steps
|
||||
|
||||
### Immediate (This Week)
|
||||
1. ✅ Review all documentation
|
||||
2. ✅ Create full backup (database + files + code)
|
||||
3. ✅ Store backup in safe location
|
||||
4. ✅ Test restore procedure
|
||||
5. ✅ Verify all portals functional
|
||||
|
||||
### Short-term (This Month)
|
||||
1. ⏳ Setup automated daily backup
|
||||
2. ⏳ Implement monitoring & alerting
|
||||
3. ⏳ Setup log rotation
|
||||
4. ⏳ Document any customizations
|
||||
5. ⏳ Create runbook in internal wiki
|
||||
|
||||
### Long-term (This Quarter)
|
||||
1. ⏳ Add comprehensive test suite
|
||||
2. ⏳ Implement bcrypt for passwords
|
||||
3. ⏳ Setup Redis session store
|
||||
4. ⏳ Create API documentation (Swagger)
|
||||
5. ⏳ Performance optimization & tuning
|
||||
|
||||
---
|
||||
|
||||
## 🎖️ Audit Completion Status
|
||||
|
||||
| Task | Status | Notes |
|
||||
|------|--------|-------|
|
||||
| Application Review | ✅ Complete | All features examined |
|
||||
| Code Structure | ✅ Complete | Well-organized & maintainable |
|
||||
| Database Schema | ✅ Complete | Proper design & relationships |
|
||||
| Security Analysis | ✅ Complete | Identified areas for improvement |
|
||||
| Documentation | ✅ Complete | 125 KB comprehensive docs created |
|
||||
| Backup Procedures | ✅ Complete | Ready to implement |
|
||||
| Testing Guide | ✅ Complete | Quick reference provided |
|
||||
| Troubleshooting | ✅ Complete | Common issues documented |
|
||||
|
||||
**Overall Status: 🟢 PRODUCTION READY**
|
||||
|
||||
---
|
||||
|
||||
## 📝 Document Versions
|
||||
|
||||
| Document | Version | Date | Status |
|
||||
|----------|---------|------|--------|
|
||||
| AUDIT_LENGKAP.md | 1.0 | 2026-06-19 | ✅ Final |
|
||||
| QUICK_REFERENCE.md | 1.0 | 2026-06-19 | ✅ Final |
|
||||
| DATABASE_SCHEMA.md | 1.0 | 2026-06-19 | ✅ Final |
|
||||
| BACKUP_CHECKLIST.md | 1.0 | 2026-06-19 | ✅ Final |
|
||||
| AUDIT_SUMMARY.md | 1.0 | 2026-06-19 | ✅ Final |
|
||||
|
||||
---
|
||||
|
||||
## 🔍 Audit Methodology
|
||||
|
||||
This audit was conducted using:
|
||||
1. **Code Review** - All source files examined
|
||||
2. **Architecture Analysis** - System design & patterns reviewed
|
||||
3. **Security Assessment** - Authentication, encryption, data protection
|
||||
4. **Database Analysis** - Schema, queries, relationships
|
||||
5. **Feature Mapping** - All portals & endpoints documented
|
||||
6. **Documentation** - Comprehensive guides created for operations
|
||||
|
||||
---
|
||||
|
||||
## ✅ Final Checklist
|
||||
|
||||
Before considering audit complete:
|
||||
|
||||
- ✅ Application analyzed thoroughly
|
||||
- ✅ All features documented
|
||||
- ✅ Database schema documented
|
||||
- ✅ Backup procedures defined
|
||||
- ✅ Security recommendations provided
|
||||
- ✅ Troubleshooting guide created
|
||||
- ✅ Quick reference created
|
||||
- ✅ Production readiness confirmed
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Success Criteria
|
||||
|
||||
**Application meets production standards when:**
|
||||
|
||||
- ✅ All documentation read & understood
|
||||
- ✅ Full backup created & tested
|
||||
- ✅ Security settings updated (passwords, secrets)
|
||||
- ✅ All portals verified working
|
||||
- ✅ Monitoring setup
|
||||
- ✅ Escalation procedures defined
|
||||
- ✅ Runbook available to operations team
|
||||
|
||||
---
|
||||
|
||||
## 📞 Questions or Issues?
|
||||
|
||||
**For questions about this audit:**
|
||||
- Review relevant documentation section
|
||||
- Check troubleshooting guides
|
||||
- Contact application owner: Ali Jaya Net (081947215703)
|
||||
|
||||
**For operational issues:**
|
||||
- Check QUICK_REFERENCE.md
|
||||
- Review application logs
|
||||
- Use health check: `GET /health`
|
||||
|
||||
---
|
||||
|
||||
## 🏁 Conclusion
|
||||
|
||||
Aplikasi Billing B481 (RTRWnet) telah selesai di-audit secara teliti. Sistem berada dalam kondisi **BAIK & SIAP UNTUK PRODUKSI** dengan dokumentasi lengkap untuk operasional.
|
||||
|
||||
**Dokumentasi yang telah dibuat mencakup:**
|
||||
- Comprehensive feature audit (50 KB)
|
||||
- Quick reference guide (10 KB)
|
||||
- Database schema documentation (30 KB)
|
||||
- Backup & recovery procedures (20 KB)
|
||||
- This summary (15 KB)
|
||||
|
||||
**Total: 125 KB documentation untuk operasional jangka panjang.**
|
||||
|
||||
Semua file dokumentasi tersimpan di root aplikasi dan siap untuk di-backup.
|
||||
|
||||
---
|
||||
|
||||
**Audit Completion Date:** June 19, 2026
|
||||
**Audit Version:** 1.0 - Final
|
||||
**Status:** ✅ APPROVED FOR PRODUCTION
|
||||
|
||||
---
|
||||
|
||||
**Jangan lupa untuk:**
|
||||
1. ✅ Backup semua dokumentasi ini
|
||||
2. ✅ Share dengan tim operasional
|
||||
3. ✅ Update dokumentasi saat ada perubahan
|
||||
4. ✅ Review & test ulang secara berkala
|
||||
|
||||
**Terima kasih telah menggunakan dokumentasi audit lengkap ini! 🎉**
|
||||
|
||||
@@ -63,7 +63,6 @@
|
||||
- `tickets` - Tiket support pelanggan
|
||||
|
||||
### 10. Payment & Digital
|
||||
- `digiflazz_transactions` - Transaksi pulsa/data Digiflazz
|
||||
- `vomit_log` - Log webhook pembayaran (generic)
|
||||
|
||||
---
|
||||
@@ -439,23 +438,6 @@ CREATE TABLE vouchers (
|
||||
|
||||
---
|
||||
|
||||
### digiflazz_transactions
|
||||
```sql
|
||||
CREATE TABLE digiflazz_transactions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
agent_id INTEGER NOT NULL REFERENCES agents(id),
|
||||
ref_id TEXT NOT NULL UNIQUE, -- Digiflazz reference ID
|
||||
phone_or_code TEXT NOT NULL, -- Tujuan nomor / kode
|
||||
product_sku TEXT NOT NULL,
|
||||
amount INTEGER NOT NULL,
|
||||
status TEXT DEFAULT 'pending', -- pending, success, failed
|
||||
response_data TEXT, -- JSON response
|
||||
created_at DATETIME DEFAULT (NOW_LOCAL())
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Query Examples
|
||||
|
||||
### Generate Invoice (Monthly)
|
||||
|
||||
@@ -1,494 +0,0 @@
|
||||
# 📚 INDEX DOKUMENTASI AUDIT - Aplikasi Billing B481
|
||||
|
||||
**Tanggal Audit:** Juni 2026
|
||||
**Total Dokumentasi:** 6 file (91 KB)
|
||||
**Status:** ✅ LENGKAP & SIAP PAKAI
|
||||
|
||||
---
|
||||
|
||||
## 📖 Panduan Membaca Dokumentasi
|
||||
|
||||
### Untuk Pengguna Pertama (5-15 menit)
|
||||
```
|
||||
START HERE:
|
||||
1. AUDIT_SUMMARY.md ← Ringkasan eksekutif (5 min)
|
||||
2. QUICK_REFERENCE.md ← Perintah dasar (10 min)
|
||||
3. README.md ← (Original README aplikasi)
|
||||
```
|
||||
|
||||
### Untuk Admin/Operator (30-60 menit)
|
||||
```
|
||||
RECOMMENDED PATH:
|
||||
1. QUICK_REFERENCE.md ← Common tasks & troubleshooting
|
||||
2. AUDIT_LENGKAP.md ← Section 4 (Endpoints & Routes)
|
||||
3. BACKUP_CHECKLIST.md ← Backup & recovery procedures
|
||||
4. DATABASE_SCHEMA.md ← Untuk query tertentu
|
||||
```
|
||||
|
||||
### Untuk Developer/Maintenance (1-2 jam)
|
||||
```
|
||||
COMPREHENSIVE PATH:
|
||||
1. AUDIT_LENGKAP.md ← Full architecture & features
|
||||
2. DATABASE_SCHEMA.md ← Complete schema with examples
|
||||
3. QUICK_REFERENCE.md ← Common tasks reference
|
||||
4. BACKUP_CHECKLIST.md ← Operational procedures
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📄 File Documentation Details
|
||||
|
||||
### 1. AUDIT_LENGKAP.md (45.57 KB)
|
||||
**Purpose:** Comprehensive audit report dengan semua detail
|
||||
|
||||
**Sections:**
|
||||
- 📍 Section 1: Informasi Umum Aplikasi
|
||||
- 📍 Section 2: Arsitektur Sistem (directory structure, flow)
|
||||
- 📍 Section 3: Database & Schema (overview)
|
||||
- 📍 Section 4: Endpoints & Routes (SEMUA API endpoints)
|
||||
- 4.1: Portal Pelanggan (/customer)
|
||||
- 4.2: Portal Admin (/admin)
|
||||
- 4.3: Portal Teknisi (/tech)
|
||||
- 4.4: Portal Agen (/agent)
|
||||
- 4.5: Portal Kolektor (/collector)
|
||||
- 4.6: Portal Finance (/finance)
|
||||
- 4.7: Public Routes
|
||||
- 4.8: Settings & Configuration API
|
||||
- 4.9: Voucher Payment API
|
||||
- 📍 Section 5: Fitur-Fitur Utama (15+ modul)
|
||||
- 5.1 Billing & Invoicing
|
||||
- 5.2 MikroTik Integration
|
||||
- 5.3 GenieACS & TR-069
|
||||
- 5.4 OLT & ONU Management
|
||||
- 5.5 GIS Mapping
|
||||
- 5.6 WhatsApp Automation
|
||||
- 5.7 Payment Gateways
|
||||
- 5.8 Agen & Sales
|
||||
- 5.9-5.15 Additional features
|
||||
- 📍 Section 6: Keamanan & Otentikasi
|
||||
- 📍 Section 7: Frontend & UI
|
||||
- 📍 Section 8: Internationalization (i18n)
|
||||
- 📍 Section 9: Configuration & Settings
|
||||
- 📍 Section 10: Dependencies & Libraries
|
||||
- 📍 Section 11: Operational Requirements
|
||||
- 📍 Section 12: Monitoring & Troubleshooting
|
||||
- 📍 Section 13: Production Checklist
|
||||
- 📍 Section 14: Performance Optimization
|
||||
- 📍 Section 15: File Size & Statistics
|
||||
- 📍 Section 16: Documentation & Resources
|
||||
- 📍 Section 17: Advanced Features & Integration
|
||||
- 📍 Section 18: Conclusion & Recommendations
|
||||
|
||||
**When to use:** Referensi lengkap untuk semua aspek aplikasi
|
||||
|
||||
**Example queries:**
|
||||
- "Apa saja endpoint admin?" → See section 4.2
|
||||
- "Bagaimana cara kerja billing?" → See section 5.1
|
||||
- "Apa saja database tables?" → See section 3.2
|
||||
- "Bagaimana setup MikroTik?" → See section 5.2
|
||||
|
||||
---
|
||||
|
||||
### 2. QUICK_REFERENCE.md (6.67 KB)
|
||||
**Purpose:** Quick lookup guide untuk task-task umum
|
||||
|
||||
**Sections:**
|
||||
- Quick Start (npm commands)
|
||||
- Portal access URLs
|
||||
- Key Files location
|
||||
- Common Tasks (5 tasks)
|
||||
- Change Port
|
||||
- Change Company Name
|
||||
- Enable WhatsApp
|
||||
- Setup MikroTik
|
||||
- Setup GenieACS
|
||||
- Security Settings (MUST DO)
|
||||
- Important Endpoints
|
||||
- Database Commands
|
||||
- Cron Jobs Schedule
|
||||
- Troubleshooting (7 common issues)
|
||||
- Multi-Language
|
||||
- Useful Scripts
|
||||
- Pre-Launch Checklist
|
||||
|
||||
**When to use:** Sehari-hari untuk quick answers
|
||||
|
||||
**Example queries:**
|
||||
- "Port berapa aplikasi?" → See "Quick Start"
|
||||
- "MikroTik tidak connect, apa solusinya?" → See "Troubleshooting"
|
||||
- "Bagaimana aktifkan WhatsApp?" → See "Common Tasks"
|
||||
- "Kapan billing dijalankan?" → See "Cron Jobs"
|
||||
|
||||
---
|
||||
|
||||
### 3. DATABASE_SCHEMA.md (15.75 KB)
|
||||
**Purpose:** Complete database schema documentation
|
||||
|
||||
**Sections:**
|
||||
- Schema Overview
|
||||
- Detailed Table Structures (20+ tables)
|
||||
- Table relationships & foreign keys
|
||||
- Query Examples (4 examples)
|
||||
- Optimization Tips
|
||||
- Backup & Recovery procedures
|
||||
- Index recommendations
|
||||
|
||||
**Tables Documented:**
|
||||
- settings, packages, customers, routers, olts, odps
|
||||
- invoices, payments, payment_approvals
|
||||
- pppoe_users, pppoe_profiles, pppoe_traffic_samples, pppoe_sessions
|
||||
- hotspot_users, hotspot_profiles, hotspot_sessions
|
||||
- voucher_batches, vouchers
|
||||
- acsdevices, acs_sessions, acs_device_params
|
||||
- onu_mapping, onu_provision, olu_ports
|
||||
- audit_trail, expenses, cash_in
|
||||
- technicians, cashiers, agents, collectors
|
||||
- inventory_items, attendance, tickets
|
||||
- digiflazz_transactions, vomit_log
|
||||
|
||||
**When to use:** Ketika perlu memahami struktur data atau membuat query
|
||||
|
||||
**Example queries:**
|
||||
- "Bagaimana struktur tabel customers?" → See "customers" section
|
||||
- "Query apa untuk cek overdue invoice?" → See "Query Examples"
|
||||
- "Tabel apa untuk tracking PPPoE usage?" → See "pppoe_traffic_samples"
|
||||
|
||||
---
|
||||
|
||||
### 4. BACKUP_CHECKLIST.md (10.58 KB)
|
||||
**Purpose:** Comprehensive backup & disaster recovery guide
|
||||
|
||||
**Sections:**
|
||||
- Pre-Audit Backup (CRITICAL)
|
||||
- Step 1-5: Database, Settings, Aplikasi, Uploads
|
||||
- Backup Checklist (verification checklist)
|
||||
- Restore Procedures (5 scenarios)
|
||||
- Restore Database Only
|
||||
- Restore Settings
|
||||
- Full Application Restore
|
||||
- Restore Upload Files
|
||||
- Backup Inventory (tracking template)
|
||||
- Secure Backup Storage (4 options)
|
||||
- Local Storage (USB)
|
||||
- Network Drive (NAS/SMB)
|
||||
- Cloud Storage (rclone, S3)
|
||||
- Encrypted Backup (GPG)
|
||||
- Automated Backup (Cron Job setup)
|
||||
- Disaster Recovery Plan (5 scenarios)
|
||||
- Database Corrupted
|
||||
- Aplikasi Crashed
|
||||
- Akses Denied
|
||||
- HDD Full
|
||||
- Settings Misconfiguration
|
||||
- Best Practices (DO & DON'T)
|
||||
- Recovery Time Objective (RTO/RPO)
|
||||
- Backup Verification Checklist
|
||||
|
||||
**When to use:** Sebelum & sesudah deployment, disaster recovery
|
||||
|
||||
**Example queries:**
|
||||
- "Gimana backup database?" → See "Backup Procedures" Step 1
|
||||
- "Gimana restore jika database corrupt?" → See "Disaster Recovery" Scenario 1
|
||||
- "Setup automated backup gimana?" → See "Automated Backup (Cron Job)"
|
||||
|
||||
---
|
||||
|
||||
### 5. AUDIT_SUMMARY.md (12.31 KB)
|
||||
**Purpose:** Executive summary & quick links
|
||||
|
||||
**Sections:**
|
||||
- Executive Summary
|
||||
- Dokumentasi yang Dibuat (daftar lengkap)
|
||||
- Key Findings (Strengths & Areas for Improvement)
|
||||
- Application Statistics
|
||||
- Quick Start (4 steps)
|
||||
- Security Checklist (CRITICAL)
|
||||
- Operational Runbook
|
||||
- Daily, Weekly, Monthly, Ad-Hoc tasks
|
||||
- Maintenance Procedures (5 procedures)
|
||||
- Training Resources
|
||||
- Support & Escalation (3 levels)
|
||||
- Document File Locations
|
||||
- Next Steps (Immediate, Short-term, Long-term)
|
||||
- Audit Completion Status
|
||||
- Document Versions
|
||||
- Audit Methodology
|
||||
- Final Checklist
|
||||
- Success Criteria
|
||||
|
||||
**When to use:** Overview & planning
|
||||
|
||||
**Example queries:**
|
||||
- "Aplikasi ready untuk production?" → See "Audit Completion Status"
|
||||
- "Apa saja dokumentasi yang dibuat?" → See "Dokumentasi yang Telah Dibuat"
|
||||
- "Langkah pertama gimana?" → See "Quick Start"
|
||||
|
||||
---
|
||||
|
||||
### 6. DOKUMENTASI_INDEX.md (This file)
|
||||
**Purpose:** Navigation guide untuk semua dokumentasi
|
||||
|
||||
**Sections:**
|
||||
- Panduan Membaca Dokumentasi (paths untuk berbagai user)
|
||||
- File Documentation Details (overview setiap file)
|
||||
- Quick Links & Search Tips
|
||||
- Related Documentation
|
||||
- How to Update Documentation
|
||||
- Troubleshooting Documentation Issues
|
||||
|
||||
---
|
||||
|
||||
## 🔗 Quick Links
|
||||
|
||||
### By Topic
|
||||
|
||||
#### Billing & Invoicing
|
||||
- AUDIT_LENGKAP.md → Section 5.1
|
||||
- DATABASE_SCHEMA.md → invoices, payments tables
|
||||
|
||||
#### MikroTik Management
|
||||
- QUICK_REFERENCE.md → "Setup MikroTik"
|
||||
- AUDIT_LENGKAP.md → Section 5.2
|
||||
- DATABASE_SCHEMA.md → routers, pppoe_users tables
|
||||
|
||||
#### Payment Gateways
|
||||
- AUDIT_LENGKAP.md → Section 5.7
|
||||
- QUICK_REFERENCE.md → "Setup Payment Gateway"
|
||||
|
||||
#### Database
|
||||
- DATABASE_SCHEMA.md → All sections
|
||||
- BACKUP_CHECKLIST.md → Backup procedures
|
||||
|
||||
#### Security
|
||||
- QUICK_REFERENCE.md → "Security Settings"
|
||||
- AUDIT_SUMMARY.md → "Security Checklist"
|
||||
- AUDIT_LENGKAP.md → Section 6
|
||||
|
||||
#### Troubleshooting
|
||||
- QUICK_REFERENCE.md → "Troubleshooting"
|
||||
- AUDIT_LENGKAP.md → Section 12
|
||||
|
||||
---
|
||||
|
||||
## 🔍 How to Search Documentation
|
||||
|
||||
### Using Text Editor Find (Ctrl+F)
|
||||
|
||||
**Search by feature:**
|
||||
```
|
||||
"billing" → Find billing-related content
|
||||
"whatsapp" → Find WhatsApp integration
|
||||
"endpoint" → Find API endpoints
|
||||
"table" → Find database tables
|
||||
"cron" → Find scheduled tasks
|
||||
"error" → Find error handling
|
||||
```
|
||||
|
||||
**Search by section:**
|
||||
```
|
||||
"## " (two hashes) → Find main sections
|
||||
"### " (three hashes) → Find subsections
|
||||
"- [ ]" → Find checklist items
|
||||
"| " → Find tables
|
||||
```
|
||||
|
||||
### Using grep (Linux/Mac)
|
||||
```bash
|
||||
# Find all mentions of "WhatsApp"
|
||||
grep -n "whatsapp" *.md
|
||||
|
||||
# Find all table structures
|
||||
grep -n "CREATE TABLE" DATABASE_SCHEMA.md
|
||||
|
||||
# Find all endpoints
|
||||
grep -n "GET\|POST\|PUT\|DELETE" AUDIT_LENGKAP.md
|
||||
|
||||
# Find all endpoints for /admin
|
||||
grep -n "/admin" AUDIT_LENGKAP.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 Documentation Cross-References
|
||||
|
||||
### Related Files to Read Together
|
||||
|
||||
**For Admin Portal:**
|
||||
- AUDIT_LENGKAP.md section 4.2
|
||||
- QUICK_REFERENCE.md "Common Tasks"
|
||||
- AUDIT_SUMMARY.md "Operational Runbook"
|
||||
|
||||
**For Customer Portal:**
|
||||
- AUDIT_LENGKAP.md section 4.1
|
||||
- AUDIT_LENGKAP.md section 5.1 (Billing)
|
||||
|
||||
**For Database Operations:**
|
||||
- DATABASE_SCHEMA.md (all sections)
|
||||
- BACKUP_CHECKLIST.md (all procedures)
|
||||
- QUICK_REFERENCE.md "Database Commands"
|
||||
|
||||
**For Security:**
|
||||
- QUICK_REFERENCE.md "Security Settings"
|
||||
- AUDIT_SUMMARY.md "Security Checklist"
|
||||
- AUDIT_LENGKAP.md section 6
|
||||
|
||||
**For Troubleshooting:**
|
||||
- QUICK_REFERENCE.md "Troubleshooting"
|
||||
- AUDIT_LENGKAP.md section 12
|
||||
- AUDIT_LENGKAP.md section 13
|
||||
|
||||
---
|
||||
|
||||
## 🔄 How to Update Documentation
|
||||
|
||||
When application changes, update relevant docs:
|
||||
|
||||
1. **New Feature Added:**
|
||||
- Update AUDIT_LENGKAP.md section 5 (Fitur-Fitur)
|
||||
- Update QUICK_REFERENCE.md if it's a common task
|
||||
|
||||
2. **New Endpoint Added:**
|
||||
- Update AUDIT_LENGKAP.md section 4 (Routes)
|
||||
- Add to QUICK_REFERENCE.md "Important Endpoints"
|
||||
|
||||
3. **Database Schema Changed:**
|
||||
- Update DATABASE_SCHEMA.md with new tables
|
||||
- Add query examples if applicable
|
||||
|
||||
4. **Backup Procedure Changed:**
|
||||
- Update BACKUP_CHECKLIST.md
|
||||
- Update QUICK_REFERENCE.md "Database Commands"
|
||||
|
||||
5. **Security Changes:**
|
||||
- Update AUDIT_SUMMARY.md "Security Checklist"
|
||||
- Update AUDIT_LENGKAP.md section 6
|
||||
|
||||
**Version Control:**
|
||||
```markdown
|
||||
Example header update:
|
||||
|
||||
### 6. AUDIT_SUMMARY.md (12.31 KB) → Last updated: 2026-06-20
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ❓ FAQ - Documentation
|
||||
|
||||
**Q: File mana yang harus dibaca duluan?**
|
||||
A: Mulai dengan AUDIT_SUMMARY.md (5 min), lalu QUICK_REFERENCE.md (10 min)
|
||||
|
||||
**Q: Gimana cari informasi tentang endpoint tertentu?**
|
||||
A: Cari di AUDIT_LENGKAP.md section 4, atau grep dengan `/endpoint-name`
|
||||
|
||||
**Q: Database tables untuk X ada di file mana?**
|
||||
A: DATABASE_SCHEMA.md, section "Detailed Table Structures"
|
||||
|
||||
**Q: Backup gimana?**
|
||||
A: Baca BACKUP_CHECKLIST.md dari atas sampai bawah
|
||||
|
||||
**Q: Troubleshooting error X gimana?**
|
||||
A: Lihat QUICK_REFERENCE.md "Troubleshooting", atau AUDIT_LENGKAP.md section 12
|
||||
|
||||
**Q: Dokumentasi sudah lengkap?**
|
||||
A: Ya, 6 file (91 KB) sudah cover semua aspek aplikasi
|
||||
|
||||
**Q: Dokumentasi perlu diupdate?**
|
||||
A: Ya, ikuti "How to Update Documentation" section di atas
|
||||
|
||||
---
|
||||
|
||||
## 📊 Documentation Statistics
|
||||
|
||||
| File | Size | Sections | Tables | Sections Covered |
|
||||
|------|------|----------|--------|------------------|
|
||||
| AUDIT_LENGKAP.md | 45 KB | 18 | 5+ | Features, API, Security |
|
||||
| QUICK_REFERENCE.md | 7 KB | 10 | 3+ | Common tasks, Quick links |
|
||||
| DATABASE_SCHEMA.md | 16 KB | 6 | 20+ | Database, Tables, Queries |
|
||||
| BACKUP_CHECKLIST.md | 11 KB | 8 | 2+ | Backup, Recovery, RTO/RPO |
|
||||
| AUDIT_SUMMARY.md | 12 KB | 15 | 2+ | Executive, Roadmap |
|
||||
| DOKUMENTASI_INDEX.md | 9 KB | 6 | - | Navigation, Index |
|
||||
|
||||
**Total:** 6 files, ~100 KB, 18+ sections, 30+ tables documented
|
||||
|
||||
---
|
||||
|
||||
## ✅ Verification Checklist
|
||||
|
||||
Pastikan dokumentasi lengkap:
|
||||
|
||||
- [x] AUDIT_LENGKAP.md → 18 sections ✓
|
||||
- [x] QUICK_REFERENCE.md → 10 sections ✓
|
||||
- [x] DATABASE_SCHEMA.md → 20+ tables ✓
|
||||
- [x] BACKUP_CHECKLIST.md → 8 sections ✓
|
||||
- [x] AUDIT_SUMMARY.md → 15 sections ✓
|
||||
- [x] DOKUMENTASI_INDEX.md → Navigation ✓
|
||||
|
||||
**All files created:** ✅ COMPLETE
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Documentation Roadmap
|
||||
|
||||
### Current Status (2026-06-19)
|
||||
- ✅ 6 documentation files created
|
||||
- ✅ ~100 KB comprehensive documentation
|
||||
- ✅ All major features documented
|
||||
- ✅ Backup procedures documented
|
||||
- ✅ Troubleshooting guide created
|
||||
|
||||
### Future Enhancements
|
||||
- ⏳ API Swagger/OpenAPI documentation
|
||||
- ⏳ Video tutorials for common tasks
|
||||
- ⏳ Deployment guide (Docker, K8s)
|
||||
- ⏳ Performance tuning guide
|
||||
- ⏳ Custom integration examples
|
||||
|
||||
---
|
||||
|
||||
## 🔐 Documentation Backup
|
||||
|
||||
**Backup documentation juga!**
|
||||
|
||||
```bash
|
||||
# Create backup
|
||||
tar -czf audit-docs-backup-$(date +%Y-%m-%d).tar.gz \
|
||||
AUDIT_LENGKAP.md \
|
||||
QUICK_REFERENCE.md \
|
||||
DATABASE_SCHEMA.md \
|
||||
BACKUP_CHECKLIST.md \
|
||||
AUDIT_SUMMARY.md \
|
||||
DOKUMENTASI_INDEX.md
|
||||
|
||||
# Store in safe location
|
||||
cp audit-docs-backup-*.tar.gz /mnt/backup/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📞 Documentation Support
|
||||
|
||||
**Questions about documentation?**
|
||||
|
||||
1. First, try searching the relevant file
|
||||
2. Check cross-references at the end of each section
|
||||
3. Review FAQ section (above)
|
||||
4. Contact: Ali Jaya Net (081947215703)
|
||||
|
||||
---
|
||||
|
||||
## 🏁 Next Steps
|
||||
|
||||
1. **READ:** Mulai dengan AUDIT_SUMMARY.md
|
||||
2. **UNDERSTAND:** Baca AUDIT_LENGKAP.md sections yang relevan
|
||||
3. **REFERENCE:** Simpan QUICK_REFERENCE.md untuk daily use
|
||||
4. **BACKUP:** Ikuti BACKUP_CHECKLIST.md procedures
|
||||
5. **MAINTAIN:** Update docs saat ada changes
|
||||
|
||||
---
|
||||
|
||||
**Documentation Version:** 1.0 - Complete
|
||||
**Last Updated:** June 19, 2026
|
||||
**Status:** ✅ READY FOR PRODUCTION
|
||||
|
||||
**Happy Reading! 📖**
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,19 +0,0 @@
|
||||
# [DESIGN SYSTEM MEMORY: Halogen Kit]
|
||||
|
||||
Terapkan aturan UI/UX dan gaya visual berikut untuk setiap permintaan pembuatan komponen UI, halaman web, atau antarmuka aplikasi:
|
||||
|
||||
## 1. Palette Warna (HEX)
|
||||
- **Background Utama**: `#161A21` (Dark Charcoal / Hampir Hitam)
|
||||
- **Surface / Container 1**: `#1F2420` (Dark Olive Gray)
|
||||
- **Surface / Container 2**: `#2A313C` (Slate Dark Gray)
|
||||
- **Primary Accent**: `#ADFF2F` (High-voltage Lime Green / Neon Accent)
|
||||
- **Primary Text & Light Accent**: `#F2F5F1` (Off-White / High Contrast Light)
|
||||
|
||||
## 2. Tipografi & Teks
|
||||
- Modern, clean sans-serif untuk judul/heading (misal: *Inter*, *Plus Jakarta Sans*, atau *SF Pro Display*).
|
||||
- Teks aksen/sub-detail menggunakan gaya Moleskine/Monospace (*JetBrains Mono*) atau all-caps kecil berjarak (*wide letter-spacing*).
|
||||
|
||||
## 3. Visual & Komponen
|
||||
- **Dark theme dengan kontras tinggi**.
|
||||
- Menggunakan efek **"ringed depth"**, morphing border-radius (sudut membulat halus `rounded-xl` / `rounded-2xl`), dan **glow lembut** (subtle drop-shadow / box-shadow `rgba(173, 255, 47, 0.25 - 0.4)`) pada elemen aksen neon (`#ADFF2F`).
|
||||
- **Garis pembatas (borders)** sangat tipis dan subtle (`1px solid #2A313C`) menggunakan warna permukaan yang sedikit lebih terang dari latar.
|
||||
@@ -1,757 +0,0 @@
|
||||
# IMPLEMENTATION GUIDE - SECURITY FIXES
|
||||
**Status:** READY TO IMPLEMENT
|
||||
**Tanggal:** 14 Agustus 2026
|
||||
**Target Completion:** 2-3 minggu
|
||||
|
||||
---
|
||||
|
||||
## 🚀 QUICK START - CRITICAL FIXES
|
||||
|
||||
### STEP 1: Install Required Packages
|
||||
```bash
|
||||
npm install bcrypt dotenv helmet express-rate-limit
|
||||
npm install --save-dev @types/bcrypt
|
||||
|
||||
# Optional but recommended:
|
||||
npm install express-validator joi
|
||||
npm install better-sqlite3-session-store
|
||||
```
|
||||
|
||||
### STEP 2: Create .env File
|
||||
```bash
|
||||
# Copy template
|
||||
cp .env.example .env
|
||||
|
||||
# Edit .env dengan credentials actual
|
||||
# Edit dengan text editor
|
||||
nano .env
|
||||
```
|
||||
|
||||
File `.env` template:
|
||||
```env
|
||||
# ======== SECURITY ========
|
||||
NODE_ENV=development
|
||||
SESSION_SECRET=GenerateRandomStringHere_$(openssl rand -hex 32)
|
||||
ADMIN_PASSWORD_HASH=Will_Be_Set_By_Setup_Script
|
||||
|
||||
# ======== GENIEACS ========
|
||||
GENIEACS_URL=http://192.168.8.189:7557
|
||||
GENIEACS_USERNAME=admin
|
||||
GENIEACS_PASSWORD=ChangeThisPassword
|
||||
|
||||
# ======== DATABASE ========
|
||||
DATABASE_PATH=./database/billing.db
|
||||
|
||||
# ======== PAYMENT GATEWAYS ========
|
||||
MIDTRANS_SERVER_KEY=Mid_server_xxxxx
|
||||
MIDTRANS_MODE=sandbox
|
||||
|
||||
XENDIT_API_KEY=xnd_development_xxxxx
|
||||
|
||||
TRIPAY_API_KEY=xxxxx
|
||||
TRIPAY_MERCHANT_CODE=xxxxx
|
||||
TRIPAY_MODE=sandbox
|
||||
|
||||
DUITKU_API_KEY=xxxxx
|
||||
DUITKU_MERCHANT_CODE=xxxxx
|
||||
|
||||
# ======== THIRD PARTY ========
|
||||
DIGIFLAZZ_USERNAME=xxxxx
|
||||
DIGIFLAZZ_API_KEY=xxxxx
|
||||
DIGIFLAZZ_WEBHOOK_SECRET=xxxxx
|
||||
|
||||
# ======== WHATSAPP ========
|
||||
WHATSAPP_ENABLED=true
|
||||
|
||||
# ======== TELEGRAM ========
|
||||
TELEGRAM_ENABLED=false
|
||||
TELEGRAM_BOT_TOKEN=xxxxx
|
||||
|
||||
# ======== RADIUS ========
|
||||
RADIUS_SECRET=secret123
|
||||
RADIUS_AUTH_PORT=1812
|
||||
RADIUS_ACCT_PORT=1813
|
||||
|
||||
# ======== LOGGING ========
|
||||
LOG_LEVEL=info
|
||||
```
|
||||
|
||||
### STEP 3: Update .gitignore
|
||||
```bash
|
||||
# Add these lines to .gitignore
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
settings.json # Optional: jika ada credentials di sini
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 FIX #1: Password Hashing with Bcrypt
|
||||
|
||||
### Part A: Create Password Helper Module
|
||||
|
||||
**File:** `utils/passwordHelper.js`
|
||||
```javascript
|
||||
const bcrypt = require('bcrypt');
|
||||
const SALT_ROUNDS = 10;
|
||||
|
||||
/**
|
||||
* Hash password dengan bcrypt
|
||||
* @param {string} plainPassword - Plain text password
|
||||
* @returns {Promise<string>} Hashed password
|
||||
*/
|
||||
async function hashPassword(plainPassword) {
|
||||
if (!plainPassword) throw new Error('Password cannot be empty');
|
||||
return await bcrypt.hash(plainPassword, SALT_ROUNDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify password
|
||||
* @param {string} plainPassword - Plain text password to verify
|
||||
* @param {string} hashedPassword - Hashed password from database
|
||||
* @returns {Promise<boolean>} True if password matches
|
||||
*/
|
||||
async function verifyPassword(plainPassword, hashedPassword) {
|
||||
if (!plainPassword || !hashedPassword) return false;
|
||||
return await bcrypt.compare(plainPassword, hashedPassword);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
hashPassword,
|
||||
verifyPassword
|
||||
};
|
||||
```
|
||||
|
||||
### Part B: Create Admin Setup Script
|
||||
|
||||
**File:** `scripts/setup-admin-password.js`
|
||||
```javascript
|
||||
#!/usr/bin/env node
|
||||
const readline = require('readline');
|
||||
const db = require('../config/database');
|
||||
const { hashPassword } = require('../utils/passwordHelper');
|
||||
const { logger } = require('../config/logger');
|
||||
|
||||
const rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
|
||||
const question = (prompt) => new Promise((resolve) => {
|
||||
rl.question(prompt, resolve);
|
||||
});
|
||||
|
||||
async function setupAdmin() {
|
||||
console.log('\n🔐 SETUP ADMIN PASSWORD\n');
|
||||
|
||||
try {
|
||||
// Check if admin exists
|
||||
const admin = db.prepare('SELECT id FROM administrators LIMIT 1').get();
|
||||
|
||||
if (!admin) {
|
||||
console.log('⚠️ No administrator found in database.');
|
||||
console.log('Please run database migration first.\n');
|
||||
rl.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const username = await question('Admin Username [admin]: ');
|
||||
const password = await question('Admin Password (min 8 chars): ');
|
||||
const passwordConfirm = await question('Confirm Password: ');
|
||||
|
||||
// Validation
|
||||
if (!username.trim() || !password) {
|
||||
console.log('❌ Username and password cannot be empty');
|
||||
rl.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (password !== passwordConfirm) {
|
||||
console.log('❌ Passwords do not match');
|
||||
rl.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (password.length < 8) {
|
||||
console.log('❌ Password must be at least 8 characters');
|
||||
rl.close();
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Hash password
|
||||
console.log('\n🔐 Hashing password...');
|
||||
const hashedPassword = await hashPassword(password);
|
||||
|
||||
// Update database
|
||||
const stmt = db.prepare('UPDATE administrators SET username = ?, password_hash = ? WHERE id = ?');
|
||||
stmt.run(username.trim(), hashedPassword, admin.id);
|
||||
|
||||
console.log('✅ Admin password updated successfully!');
|
||||
console.log(` Username: ${username.trim()}`);
|
||||
console.log('\nℹ️ Please keep your password secure and do NOT share with anyone.\n');
|
||||
|
||||
rl.close();
|
||||
process.exit(0);
|
||||
} catch (err) {
|
||||
logger.error('Setup failed:', err);
|
||||
console.error('❌ Error:', err.message);
|
||||
rl.close();
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
setupAdmin();
|
||||
```
|
||||
|
||||
### Part C: Update Technician Service
|
||||
|
||||
**File:** `services/techService.js`
|
||||
```javascript
|
||||
const db = require('../config/database');
|
||||
const { hashPassword, verifyPassword } = require('../utils/passwordHelper');
|
||||
|
||||
async function authenticate(username, password) {
|
||||
const tech = db.prepare('SELECT * FROM technicians WHERE username = ? AND is_active = 1').get(username);
|
||||
|
||||
if (!tech) return null;
|
||||
|
||||
// Verify password using bcrypt
|
||||
const isValid = await verifyPassword(password, tech.password_hash);
|
||||
|
||||
if (!isValid) return null;
|
||||
|
||||
// Return user data tanpa password
|
||||
const { password_hash, ...techData } = tech;
|
||||
return techData;
|
||||
}
|
||||
|
||||
async function createTechnician(data) {
|
||||
const hashedPassword = await hashPassword(data.password);
|
||||
const stmt = db.prepare(
|
||||
'INSERT INTO technicians (username, password_hash, name, phone, area) VALUES (?, ?, ?, ?, ?)'
|
||||
);
|
||||
return stmt.run(data.username, hashedPassword, data.name, data.phone || '', data.area || '');
|
||||
}
|
||||
|
||||
async function updatePassword(techId, newPassword) {
|
||||
const hashedPassword = await hashPassword(newPassword);
|
||||
return db.prepare('UPDATE technicians SET password_hash = ? WHERE id = ?').run(hashedPassword, techId);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
authenticate,
|
||||
createTechnician,
|
||||
updatePassword,
|
||||
// ... export fungsi lain yang ada
|
||||
};
|
||||
```
|
||||
|
||||
### Part D: Update Customer Portal Login
|
||||
|
||||
**File:** `routes/customerPortal.js` (di bagian POST /login)
|
||||
|
||||
```javascript
|
||||
// Find in the file and replace the login logic:
|
||||
|
||||
// ❌ OLD CODE
|
||||
const cust = db.prepare('SELECT * FROM customers WHERE ...')
|
||||
.get(loginId, password);
|
||||
|
||||
// ✅ NEW CODE
|
||||
const cust = db.prepare('SELECT * FROM customers WHERE login_id = ?')
|
||||
.get(loginId);
|
||||
|
||||
if (cust) {
|
||||
const { verifyPassword } = require('../utils/passwordHelper');
|
||||
const isValid = await verifyPassword(password, cust.password_hash);
|
||||
|
||||
if (!isValid) {
|
||||
return res.status(401).json({ error: 'Invalid credentials' });
|
||||
}
|
||||
|
||||
// Regenerate session ID untuk security
|
||||
req.session.regenerate((err) => {
|
||||
if (err) return res.status(500).json({ error: 'Session error' });
|
||||
req.session.customerId = cust.id;
|
||||
res.json({ success: true });
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Part E: Database Migration
|
||||
|
||||
**File:** `scripts/migrate-password-hashing.js`
|
||||
```javascript
|
||||
#!/usr/bin/env node
|
||||
const db = require('../config/database');
|
||||
const { hashPassword } = require('../utils/passwordHelper');
|
||||
const { logger } = require('../config/logger');
|
||||
|
||||
async function migrate() {
|
||||
console.log('🔄 Starting password hashing migration...\n');
|
||||
|
||||
try {
|
||||
// Migrate technicians
|
||||
console.log('📋 Migrating technicians...');
|
||||
const techs = db.prepare('SELECT id, password FROM technicians WHERE password_hash IS NULL').all();
|
||||
|
||||
for (const tech of techs) {
|
||||
if (tech.password) {
|
||||
const hashedPassword = await hashPassword(tech.password);
|
||||
db.prepare('UPDATE technicians SET password_hash = ? WHERE id = ?').run(hashedPassword, tech.id);
|
||||
console.log(` ✅ Tech ${tech.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate customers (if applicable)
|
||||
console.log('\n📋 Migrating customers...');
|
||||
const custs = db.prepare('SELECT id, password FROM customers WHERE password_hash IS NULL AND password IS NOT NULL').all();
|
||||
|
||||
for (const cust of custs) {
|
||||
if (cust.password) {
|
||||
const hashedPassword = await hashPassword(cust.password);
|
||||
db.prepare('UPDATE customers SET password_hash = ? WHERE id = ?').run(hashedPassword, cust.id);
|
||||
console.log(` ✅ Customer ${cust.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate administrators
|
||||
console.log('\n📋 Migrating administrators...');
|
||||
const admins = db.prepare('SELECT id, password FROM administrators WHERE password_hash IS NULL').all();
|
||||
|
||||
for (const admin of admins) {
|
||||
if (admin.password) {
|
||||
const hashedPassword = await hashPassword(admin.password);
|
||||
db.prepare('UPDATE administrators SET password_hash = ? WHERE id = ?').run(hashedPassword, admin.id);
|
||||
console.log(` ✅ Admin ${admin.id}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n✅ Migration completed successfully!');
|
||||
console.log('\n⚠️ IMPORTANT:');
|
||||
console.log(' 1. Delete plain password columns: ALTER TABLE technicians DROP COLUMN password;');
|
||||
console.log(' 2. Ensure all code uses password_hash column');
|
||||
console.log(' 3. Update settings.json to not store admin password\n');
|
||||
|
||||
logger.info('Password hashing migration completed');
|
||||
process.exit(0);
|
||||
} catch (err) {
|
||||
logger.error('Migration failed:', err);
|
||||
console.error('❌ Error:', err.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
migrate();
|
||||
```
|
||||
|
||||
### Part F: Update Database Schema
|
||||
|
||||
```sql
|
||||
-- Add password_hash column to tables (if not exists)
|
||||
ALTER TABLE technicians ADD COLUMN password_hash TEXT;
|
||||
ALTER TABLE administrators ADD COLUMN password_hash TEXT;
|
||||
ALTER TABLE customers ADD COLUMN password_hash TEXT;
|
||||
|
||||
-- After migration, drop old password columns:
|
||||
-- ALTER TABLE technicians DROP COLUMN password;
|
||||
-- ALTER TABLE administrators DROP COLUMN password;
|
||||
-- ALTER TABLE customers DROP COLUMN password;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗝️ FIX #2: Environment Variables & Config Management
|
||||
|
||||
### Part A: Create .env.example
|
||||
|
||||
**File:** `.env.example`
|
||||
```env
|
||||
# Copy this file to .env and fill in actual values
|
||||
# DO NOT commit .env to git!
|
||||
|
||||
# ======== APPLICATION ========
|
||||
NODE_ENV=development
|
||||
SERVER_PORT=3001
|
||||
SERVER_HOST=localhost
|
||||
|
||||
# ======== SECURITY ========
|
||||
SESSION_SECRET=change-me-to-random-string-minimum-32-chars
|
||||
COOKIE_SECURE=false # Set to true in production with HTTPS
|
||||
|
||||
# ======== ADMIN SETUP ========
|
||||
# Set these during first-time setup, will be migrated to database
|
||||
ADMIN_USERNAME=admin
|
||||
ADMIN_PASSWORD=change-me-to-strong-password-min-8-chars
|
||||
|
||||
# ======== GENIEACS ========
|
||||
GENIEACS_URL=http://192.168.8.189:7557
|
||||
GENIEACS_USERNAME=admin
|
||||
GENIEACS_PASSWORD=change-me
|
||||
|
||||
# ======== PAYMENT GATEWAYS ========
|
||||
MIDTRANS_ENABLED=true
|
||||
MIDTRANS_SERVER_KEY=Mid_server_xxxxx
|
||||
MIDTRANS_MODE=sandbox
|
||||
|
||||
XENDIT_ENABLED=true
|
||||
XENDIT_API_KEY=xnd_development_xxxxx
|
||||
|
||||
TRIPAY_ENABLED=false
|
||||
TRIPAY_API_KEY=xxxxx
|
||||
TRIPAY_PRIVATE_KEY=xxxxx
|
||||
TRIPAY_MERCHANT_CODE=xxxxx
|
||||
TRIPAY_MODE=sandbox
|
||||
|
||||
DUITKU_ENABLED=true
|
||||
DUITKU_API_KEY=xxxxx
|
||||
DUITKU_MERCHANT_CODE=xxxxx
|
||||
DUITKU_MODE=sandbox
|
||||
|
||||
# ======== DIGIFLAZZ ========
|
||||
DIGIFLAZZ_USERNAME=
|
||||
DIGIFLAZZ_API_KEY=
|
||||
DIGIFLAZZ_WEBHOOK_SECRET=
|
||||
|
||||
# ======== WHATSAPP ========
|
||||
WHATSAPP_ENABLED=true
|
||||
WHATSAPP_AUTH_FOLDER=auth_info_baileys
|
||||
|
||||
# ======== TELEGRAM ========
|
||||
TELEGRAM_ENABLED=false
|
||||
TELEGRAM_BOT_TOKEN=
|
||||
|
||||
# ======== RADIUS ========
|
||||
RADIUS_ENABLED=0
|
||||
RADIUS_SECRET=secret123
|
||||
|
||||
# ======== LOGGING ========
|
||||
LOG_LEVEL=info
|
||||
|
||||
# ======== DATABASE ========
|
||||
DATABASE_PATH=./database/billing.db
|
||||
|
||||
# ======== TIMEZONE ========
|
||||
TIMEZONE=Asia/Jakarta
|
||||
```
|
||||
|
||||
### Part B: Update config/settingsManager.js
|
||||
|
||||
```javascript
|
||||
// Add at the top of settingsManager.js:
|
||||
require('dotenv').config();
|
||||
|
||||
// Modify getSetting function to check .env first:
|
||||
function getSetting(key, defaultValue = null) {
|
||||
// 1. Check environment variable first (highest priority)
|
||||
const envKey = key.toUpperCase().replace(/([A-Z])/g, '_$1');
|
||||
if (process.env[envKey] !== undefined) {
|
||||
return process.env[envKey];
|
||||
}
|
||||
|
||||
// 2. Check settings.json
|
||||
const settings = getSettings();
|
||||
if (settings[key] !== undefined) {
|
||||
return settings[key];
|
||||
}
|
||||
|
||||
// 3. Return default value
|
||||
return defaultValue;
|
||||
}
|
||||
```
|
||||
|
||||
### Part C: Update .gitignore
|
||||
|
||||
```bash
|
||||
# Add to .gitignore:
|
||||
.env
|
||||
.env.local
|
||||
.env.*.local
|
||||
.env.development.local
|
||||
.env.staging.local
|
||||
.env.production.local
|
||||
|
||||
# Optional: exclude settings.json if it contains credentials
|
||||
# settings.json
|
||||
# Instead: settings.json.example (commit this template)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🛡️ FIX #3: Enhanced CSRF Protection
|
||||
|
||||
### Part A: Install CSURF
|
||||
|
||||
```bash
|
||||
npm install csurf
|
||||
```
|
||||
|
||||
### Part B: Add CSRF to app-customer.js
|
||||
|
||||
```javascript
|
||||
const csrf = require('csurf');
|
||||
|
||||
// Create CSRF protection middleware
|
||||
const csrfProtection = csrf({
|
||||
cookie: false // Use session instead of cookie
|
||||
});
|
||||
|
||||
// Apply to POST/PUT/DELETE routes
|
||||
app.post('/admin/:path*', csrfProtection, adminRoutes);
|
||||
app.post('/customer/:path*', csrfProtection, customerRoutes);
|
||||
```
|
||||
|
||||
### Part C: Add CSRF Token to Forms
|
||||
|
||||
```ejs
|
||||
<!-- In EJS templates, add hidden CSRF token field: -->
|
||||
<form method="POST" action="/admin/billing/generate">
|
||||
<input type="hidden" name="_csrf" value="<%= csrfToken %>">
|
||||
<!-- Other form fields -->
|
||||
<button type="submit">Generate Invoice</button>
|
||||
</form>
|
||||
|
||||
<!-- For AJAX requests, include in headers: -->
|
||||
<script>
|
||||
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
|
||||
|
||||
fetch('/api/action', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'X-CSRF-Token': csrfToken
|
||||
},
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚨 FIX #4: Rate Limiting
|
||||
|
||||
### Part A: Install & Setup
|
||||
|
||||
```bash
|
||||
npm install express-rate-limit
|
||||
```
|
||||
|
||||
### Part B: Create Rate Limiter Middleware
|
||||
|
||||
**File:** `middleware/rateLimiter.js`
|
||||
```javascript
|
||||
const rateLimit = require('express-rate-limit');
|
||||
|
||||
const loginRateLimiter = rateLimit({
|
||||
windowMs: 15 * 60 * 1000, // 15 minutes
|
||||
max: 5, // Limit each IP to 5 requests per windowMs
|
||||
message: 'Terlalu banyak percobaan login, coba lagi dalam beberapa menit',
|
||||
standardHeaders: true,
|
||||
legacyHeaders: false,
|
||||
});
|
||||
|
||||
const apiRateLimiter = rateLimit({
|
||||
windowMs: 1 * 60 * 1000, // 1 minute
|
||||
max: 100, // 100 requests per minute
|
||||
message: 'Terlalu banyak request ke API, coba lagi nanti'
|
||||
});
|
||||
|
||||
const webhookRateLimiter = rateLimit({
|
||||
windowMs: 1 * 60 * 1000,
|
||||
max: 1000, // Higher limit for webhooks
|
||||
skip: (req) => {
|
||||
// Skip rate limiting if webhook signature is valid
|
||||
return req.session?.webhookVerified;
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
loginRateLimiter,
|
||||
apiRateLimiter,
|
||||
webhookRateLimiter
|
||||
};
|
||||
```
|
||||
|
||||
### Part C: Apply Rate Limiters
|
||||
|
||||
```javascript
|
||||
// In routes
|
||||
const { loginRateLimiter, apiRateLimiter } = require('../middleware/rateLimiter');
|
||||
|
||||
// Apply to login endpoints
|
||||
router.post('/login', loginRateLimiter, loginController);
|
||||
router.post('/customer/login', loginRateLimiter, customerLoginController);
|
||||
|
||||
// Apply to API endpoints
|
||||
router.get('/api/data', apiRateLimiter, apiController);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔒 FIX #5: Session Management Improvements
|
||||
|
||||
### Part A: Setup Session Store
|
||||
|
||||
```bash
|
||||
npm install better-sqlite3-session-store
|
||||
```
|
||||
|
||||
### Part B: Update app-customer.js
|
||||
|
||||
```javascript
|
||||
const SessionStore = require('better-sqlite3-session-store')(session);
|
||||
|
||||
const store = new SessionStore({
|
||||
client: db,
|
||||
expired: {
|
||||
clear: true,
|
||||
intervalMs: 15 * 60 * 1000 // Clear expired sessions every 15 minutes
|
||||
}
|
||||
});
|
||||
|
||||
app.use(session({
|
||||
store: store, // Use database store instead of memory
|
||||
secret: process.env.SESSION_SECRET || getSetting('session_secret'),
|
||||
resave: false,
|
||||
saveUninitialized: false,
|
||||
cookie: {
|
||||
secure: process.env.COOKIE_SECURE === 'true',
|
||||
httpOnly: true,
|
||||
sameSite: 'strict', // More restrictive
|
||||
maxAge: 24 * 60 * 60 * 1000 // 24 hours
|
||||
},
|
||||
name: 'customer.sid'
|
||||
}));
|
||||
```
|
||||
|
||||
### Part C: Session Regeneration on Login
|
||||
|
||||
```javascript
|
||||
// After successful login:
|
||||
req.session.regenerate((err) => {
|
||||
if (err) {
|
||||
return res.status(500).json({ error: 'Session error' });
|
||||
}
|
||||
req.session.userId = user.id;
|
||||
req.session.userRole = user.role;
|
||||
req.session.loginTime = Date.now();
|
||||
|
||||
// Set inactivity timeout
|
||||
req.session.inactivityTimeout = 30 * 60 * 1000; // 30 minutes
|
||||
|
||||
res.json({ success: true, redirect: '/dashboard' });
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ VERIFICATION CHECKLIST
|
||||
|
||||
After implementing all fixes, verify:
|
||||
|
||||
- [ ] bcrypt password hashing implemented
|
||||
- [ ] Old plain passwords migrated to hashed
|
||||
- [ ] .env file created and populated
|
||||
- [ ] .env added to .gitignore
|
||||
- [ ] Settings.json reviewed for sensitive data
|
||||
- [ ] CSRF protection implemented
|
||||
- [ ] Rate limiting configured
|
||||
- [ ] Session store using database
|
||||
- [ ] Session regeneration on login working
|
||||
- [ ] All tests passing
|
||||
- [ ] Application running without errors
|
||||
|
||||
---
|
||||
|
||||
## 🧪 TESTING FIXES
|
||||
|
||||
### Test Password Hashing
|
||||
|
||||
```bash
|
||||
node -e "
|
||||
const { hashPassword, verifyPassword } = require('./utils/passwordHelper');
|
||||
(async () => {
|
||||
const hash = await hashPassword('test123456');
|
||||
const valid = await verifyPassword('test123456', hash);
|
||||
console.log('Hash:', hash);
|
||||
console.log('Valid:', valid);
|
||||
})();
|
||||
"
|
||||
```
|
||||
|
||||
### Test Environment Variables
|
||||
|
||||
```bash
|
||||
node -e "
|
||||
require('dotenv').config();
|
||||
console.log('NODE_ENV:', process.env.NODE_ENV);
|
||||
console.log('SESSION_SECRET:', process.env.SESSION_SECRET ? '✅ Set' : '❌ Not set');
|
||||
console.log('GENIEACS_PASSWORD:', process.env.GENIEACS_PASSWORD ? '✅ Set' : '❌ Not set');
|
||||
"
|
||||
```
|
||||
|
||||
### Test Login Flow
|
||||
|
||||
```bash
|
||||
# Start application
|
||||
npm start
|
||||
|
||||
# Open browser and test login with new credentials
|
||||
# Check console logs for any errors
|
||||
# Verify session is created in database
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📞 TROUBLESHOOTING
|
||||
|
||||
### Issue: "bcrypt is not defined"
|
||||
```bash
|
||||
Solution: npm install bcrypt
|
||||
```
|
||||
|
||||
### Issue: ".env file not loading"
|
||||
```bash
|
||||
Solution: Ensure dotenv is imported at the top of app-customer.js:
|
||||
require('dotenv').config();
|
||||
```
|
||||
|
||||
### Issue: "CSRF token mismatch"
|
||||
```bash
|
||||
Solution: Ensure csrfToken is included in all forms and AJAX requests
|
||||
```
|
||||
|
||||
### Issue: "Session not persisting"
|
||||
```bash
|
||||
Solution: Verify database schema has session table:
|
||||
sqlite3 database/billing.db ".schema sessions"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📝 NEXT STEPS
|
||||
|
||||
1. **Immediate (Today):**
|
||||
- Review this guide with team
|
||||
- Create .env file
|
||||
- Test bcrypt locally
|
||||
|
||||
2. **This Week:**
|
||||
- Implement bcrypt in all auth modules
|
||||
- Run password migration script
|
||||
- Deploy to staging for testing
|
||||
|
||||
3. **Next Week:**
|
||||
- Implement CSRF protection
|
||||
- Add rate limiting
|
||||
- Full security testing
|
||||
|
||||
4. **Following Week:**
|
||||
- Monitor production (if live)
|
||||
- Collect logs and monitoring data
|
||||
- Review and optimize
|
||||
|
||||
---
|
||||
|
||||
**Questions?** Review AUDIT_SECURITY_2026.md for detailed security analysis.
|
||||
@@ -1,279 +0,0 @@
|
||||
# Multi-Router Implementation - Verification Status
|
||||
|
||||
**Last Updated**: 2026-06-19
|
||||
**Status**: ✅ COMPLETE - Ready for production testing
|
||||
|
||||
---
|
||||
|
||||
## Implementation Summary
|
||||
|
||||
### 1. Core Features Implemented
|
||||
|
||||
#### Smart Router Detection (`getEffectiveRouterId()`)
|
||||
- **Location**: `services/customerService.js`
|
||||
- **Mode: Aktif (Active Multi-Router)**
|
||||
- Returns customer's explicit `router_id` (can be NULL)
|
||||
- Customers MUST explicitly choose router during setup
|
||||
- Best for environments with multiple independent routers
|
||||
|
||||
- **Mode: Nonaktif (Single Router Fallback)**
|
||||
- Returns customer's `router_id` if set
|
||||
- Falls back to `default_router_id` setting if customer has NULL
|
||||
- Auto-detects first available router if no default set
|
||||
- Best for migration from single-router to multi-router
|
||||
- Allows old customers (NULL router_id) to work transparently
|
||||
|
||||
#### Router Connection Fallback (`getConnection()`)
|
||||
- **Location**: `services/mikrotikService.js`
|
||||
- Priority 1: Use specified `routerId` from database
|
||||
- Priority 2: Auto-detect first active router from database
|
||||
- Priority 3: Fallback to `settings.json` (backward compatibility)
|
||||
- Includes TCP connectivity probing + cache (5s) for performance
|
||||
|
||||
#### Configuration
|
||||
- **Location**: `settings.json`
|
||||
- `multi_router_mode`: "active" | "disabled" (default: "disabled")
|
||||
- `default_router_id`: Optional ID for fallback router
|
||||
|
||||
---
|
||||
|
||||
## Integration Points - All Verified
|
||||
|
||||
### ✅ Customer Suspension (Isolir)
|
||||
**Admin Portal Route**: `POST /customers/:id/isolate`
|
||||
- Calls `customerSvc.suspendCustomer(id)`
|
||||
- Uses `getEffectiveRouterId()` internally
|
||||
- Updates MikroTik profile → updates database status
|
||||
- Sends WhatsApp notification to customer phone
|
||||
|
||||
**WhatsApp Bot Command**: `!isolir <customer_id>`
|
||||
- Calls `customerSvc.suspendCustomer(id)`
|
||||
- Same multi-router support as admin route
|
||||
|
||||
**Code Flow**:
|
||||
```
|
||||
Admin/Bot Request
|
||||
→ suspendCustomer(id)
|
||||
→ getCustomerById(id)
|
||||
→ getEffectiveRouterId(customer.router_id)
|
||||
→ mikrotikSvc.setPppoeProfile() [respects effectiveRouterId]
|
||||
→ updateCustomer() [database update]
|
||||
→ sendWA() [notification]
|
||||
```
|
||||
|
||||
### ✅ Customer Activation
|
||||
**Admin Portal Route**: `POST /customers/:id/unisolate`
|
||||
- Calls `customerSvc.activateCustomer(id)`
|
||||
- Uses `getEffectiveRouterId()` internally
|
||||
- Updates MikroTik profile → updates database status
|
||||
|
||||
**Code Flow**:
|
||||
```
|
||||
Admin Request
|
||||
→ activateCustomer(id)
|
||||
→ getCustomerById(id)
|
||||
→ getEffectiveRouterId(customer.router_id)
|
||||
→ mikrotikSvc.setPppoeProfile() [respects effectiveRouterId]
|
||||
→ updateCustomer() [database update]
|
||||
```
|
||||
|
||||
### ✅ Payment Processing Integration
|
||||
**Payment Service**: Routes at `/voucher-payment`, payment callbacks
|
||||
- Customer payment → `activateCustomer()` → multi-router support
|
||||
- Files: `services/paymentService.js`, `routes/voucherPaymentAPI.js`
|
||||
|
||||
### ✅ Export/Import Functionality
|
||||
**Admin Portal Routes**: `GET /customers/export`, `POST /customers/import`
|
||||
- Export includes `Router` column (maps to `router_name`)
|
||||
- Import supports both old files (NULL router) and new (router specified)
|
||||
- Backward compatible with existing export files
|
||||
- File: `routes/adminPortal.js` (lines 1900-2000)
|
||||
|
||||
### ✅ Portal Login Support
|
||||
|
||||
**Customer Portal** (`routes/customerPortal.js`)
|
||||
- PPPoE profile loading: Supports multi-router ✅
|
||||
- Queries include `r.name as router_name` join
|
||||
- Router filter applied by customer's effective router
|
||||
|
||||
**Tech Portal** (`routes/techPortal.js`)
|
||||
- PPPoE profiles with router query param support ✅
|
||||
- Route: `/tech/pppoe-profiles?routerId=1`
|
||||
- Validates customer matches requested router
|
||||
|
||||
**ACS Portal** (`routes/acsPortal.js`)
|
||||
- PPPoE profile loading for ONU management ✅
|
||||
- Respects customer's effective router
|
||||
|
||||
### ✅ Connection Types - All Supported
|
||||
|
||||
| Connection Type | Suspend Implementation | Activate Implementation | Router Validation |
|
||||
|---|---|---|---|
|
||||
| **PPPoE** | `setPppoeProfile(username, 'isolir', routerId)` | `setPppoeProfile(username, profileName, routerId)` | Required for isolir ✅ |
|
||||
| **Static IP** | `manageStaticIp({...isolate: true}, routerId)` | `manageStaticIp({...isolate: false}, routerId)` | Required ✅ |
|
||||
| **Hotspot** | `setHotspotUserDisabled(user, true, routerId)` | `upsertHotspotUser({...disabled: false}, routerId)` | Required ✅ |
|
||||
|
||||
---
|
||||
|
||||
## Current Configuration
|
||||
|
||||
### Database Routers
|
||||
```
|
||||
ID | Name | is_active
|
||||
----|----------------|----------
|
||||
1 | Main Router | 1
|
||||
2 | Main Router | 1
|
||||
3 | Main Router | 1
|
||||
```
|
||||
|
||||
### Application Settings
|
||||
```
|
||||
multi_router_mode: "disabled" (default) [from settings.json]
|
||||
default_router_id: "" (empty/unset) [from settings.json]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Testing Checklist
|
||||
|
||||
### Phase 1: Basic Multi-Router Operations
|
||||
- [ ] **Test 1.1**: Suspend customer with explicit router_id (Router 1)
|
||||
- Expected: Isolir works, customer can see they're suspended
|
||||
- Check: Database status = "suspended", WhatsApp notification received
|
||||
|
||||
- [ ] **Test 1.2**: Suspend customer with NULL router_id in "disabled" mode
|
||||
- Expected: Falls back to first router in database
|
||||
- Check: Customer isolir works even though router_id = NULL
|
||||
|
||||
- [ ] **Test 1.3**: Activate customer from suspended state
|
||||
- Expected: Customer can reconnect, service restored
|
||||
- Check: Database status = "active", PPPoE profile changed back
|
||||
|
||||
### Phase 2: Different Connection Types
|
||||
- [ ] **Test 2.1**: Suspend customer with Static IP connection
|
||||
- Expected: IP rate-limited to isolir profile
|
||||
- Check: MikroTik queue rules applied
|
||||
|
||||
- [ ] **Test 2.2**: Suspend customer with Hotspot connection
|
||||
- Expected: Hotspot user disabled = true
|
||||
- Check: Customer cannot login to hotspot
|
||||
|
||||
### Phase 3: WhatsApp Bot Integration
|
||||
- [ ] **Test 3.1**: Run `!isolir <customer_id>` command via WhatsApp
|
||||
- Expected: Customer isolir works, admin gets response
|
||||
- Check: Admin receives ✅ confirmation in WhatsApp
|
||||
|
||||
- [ ] **Test 3.2**: Run on customer with NULL router_id
|
||||
- Expected: Uses fallback router, works correctly
|
||||
- Check: Customer gets isolir'd despite NULL router
|
||||
|
||||
### Phase 4: Payment & Auto-Isolir
|
||||
- [ ] **Test 4.1**: Customer makes payment after isolir
|
||||
- Expected: Auto-activates via payment webhook
|
||||
- Check: Customer service restored immediately
|
||||
|
||||
- [ ] **Test 4.2**: Auto-isolir on missed payment (cron job)
|
||||
- Expected: Customers automatically suspended on isolir_day
|
||||
- Check: Log shows "Auto-isolir" entries
|
||||
|
||||
### Phase 5: Export/Import Cycle
|
||||
- [ ] **Test 5.1**: Export customers, verify "Router" column present
|
||||
- Expected: CSV has "Router" column with router names
|
||||
- Check: Column exists in exported file
|
||||
|
||||
- [ ] **Test 5.2**: Modify export, re-import with different router
|
||||
- Expected: Customer router_id updated in database
|
||||
- Check: New router_id reflected in customer edit view
|
||||
|
||||
### Phase 6: Mode Switching (Aktif ↔ Nonaktif)
|
||||
- [ ] **Test 6.1**: Switch from "Nonaktif" → "Aktif"
|
||||
- Expected: Mode changes, no auto-assignments (existing customers keep current router)
|
||||
- Check: Settings updated, app doesn't crash
|
||||
|
||||
- [ ] **Test 6.2**: Switch from "Aktif" → "Nonaktif"
|
||||
- Expected: Mode changes, customers without router_id get assigned default
|
||||
- Check: Auto-assignment completed, NULL router_id → router_id populated
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting Guide
|
||||
|
||||
### Symptom: Customer suspendsion not working
|
||||
**Check 1**: Verify customer has router_id set
|
||||
```bash
|
||||
SELECT id, name, router_id, pppoe_username FROM customers WHERE id = ?
|
||||
```
|
||||
- If NULL: Check multi_router_mode setting - must be "disabled" for fallback to work
|
||||
- If not NULL: Verify router exists and is active
|
||||
|
||||
**Check 2**: Verify MikroTik connectivity
|
||||
```bash
|
||||
node -e "const m = require('./services/mikrotikService'); m.checkConnection(1).then(ok => console.log('Router 1:', ok))"
|
||||
```
|
||||
|
||||
**Check 3**: Check application logs
|
||||
```bash
|
||||
tail -f logs/combined.log | grep -i "isolir\|suspend"
|
||||
tail -f logs/error.log
|
||||
```
|
||||
|
||||
### Symptom: WhatsApp isolir command fails
|
||||
**Check**: Verify WhatsApp is enabled and logged in
|
||||
- Check `settings.json`: `whatsapp_enabled: true`
|
||||
- Check logs for "whatsapp" entries
|
||||
- Verify phone number is in `whatsapp_admin_numbers`
|
||||
|
||||
### Symptom: Mode switching doesn't auto-assign routers
|
||||
**Check**: Review logs during mode switch
|
||||
```bash
|
||||
grep -i "auto" logs/combined.log
|
||||
```
|
||||
- Should see messages about finding default router
|
||||
- Verify at least one router has `is_active = 1`
|
||||
|
||||
---
|
||||
|
||||
## Performance Notes
|
||||
|
||||
### MikroTik Connection Caching
|
||||
- Connection probe cache: 5 seconds
|
||||
- PPPoE profiles cache: 15 seconds
|
||||
- PPPoE secrets cache: 5 seconds
|
||||
- Active sessions cache: 2 seconds (real-time)
|
||||
|
||||
### Optimization Implemented
|
||||
- SOAP fault reduction: ~40% fewer unnecessary queries
|
||||
- Removed invalid WiFi refresh attempts on CIOT ONUs
|
||||
- WANConnectionDevice loop reduced from 5 to 3
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. **Run Phase 1-3 Tests** on staging environment with multiple routers
|
||||
2. **Verify WhatsApp bot** works with multi-router customers
|
||||
3. **Test payment** auto-activation with mixed router customers
|
||||
4. **Monitor logs** for any connection errors during testing
|
||||
5. **Deploy** to production after all tests pass
|
||||
|
||||
---
|
||||
|
||||
## Code References
|
||||
|
||||
- Smart router logic: `services/customerService.js` lines 12-38
|
||||
- Suspend implementation: `services/customerService.js` lines 360-415
|
||||
- Activate implementation: `services/customerService.js` lines 417-468
|
||||
- MikroTik fallback: `services/mikrotikService.js` lines 169-240
|
||||
- Admin routes: `routes/adminPortal.js` lines 2024-2032, 2034-2042
|
||||
- WhatsApp bot: `services/whatsappBot.mjs` line 1187
|
||||
|
||||
---
|
||||
|
||||
## Rollback Plan
|
||||
|
||||
If issues occur in production:
|
||||
1. Set `multi_router_mode: "disabled"` in `settings.json`
|
||||
2. Set `default_router_id` to the ID of your primary router
|
||||
3. Restart application with `pm2 restart all`
|
||||
4. All NULL router_id customers will automatically use the default router
|
||||
5. Existing operations continue working (backward compatible)
|
||||
@@ -1,207 +0,0 @@
|
||||
# 📱 Panduan Lengkap & Dokumen Listing Google Play Store
|
||||
**Aplikasi:** Alijaya Customer & Billing Management System
|
||||
**Package Name:** `com.alijaya.customer`
|
||||
**File Bundle:** `AlijayaCustomer.aab` (5.56 MB)
|
||||
**Keystore:** `alijaya-release.jks`
|
||||
|
||||
Dokumen ini berisi seluruh teks deskripsi, kebijakan privasi, keamanan data (data safety), kredensial akun pengujian untuk tim Google, dan panduan langkah demi langkah yang siap di-*copy-paste* langsung ke **Google Play Console**.
|
||||
|
||||
---
|
||||
|
||||
## 1. 📋 Data Utama Listing Toko (Store Listing)
|
||||
|
||||
### A. Judul Aplikasi (App Title)
|
||||
> **Batas:** Maksimal 30 Karakter
|
||||
```text
|
||||
Alijaya Digital
|
||||
```
|
||||
*(Alternatif jika ingin nama lain: `Alijaya Billing`, `Alijaya Net`, atau `Alijaya Customer`)*
|
||||
|
||||
---
|
||||
|
||||
### B. Deskripsi Singkat (Short Description)
|
||||
> **Batas:** Maksimal 80 Karakter
|
||||
```text
|
||||
Aplikasi layanan internet pelanggan, tagihan, beli pulsa, dan absensi petugas.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### C. Deskripsi Lengkap (Full Description)
|
||||
> **Batas:** Maksimal 4.000 Karakter
|
||||
```text
|
||||
Alijaya Digital adalah aplikasi resmi layanan internet dan manajemen billing terpadu untuk pelanggan, agen, teknisi, kolektor, dan kasir jaringan Alijaya Digital Network.
|
||||
|
||||
Aplikasi ini memberikan kemudahan akses informasi layanan internet, pembayaran tagihan, pembelian paket data/pulsa, hingga operasional lapangan secara cepat, transparan, dan terpercaya.
|
||||
|
||||
Fitur Unggulan untuk Pelanggan:
|
||||
• Cek Tagihan & Status Langganan: Informasi tagihan bulanan real-time, jatuh tempo, dan rincian pemakaian paket internet.
|
||||
• Pembayaran Otomatis & QRIS: Pembayaran tagihan mudah dan instan menggunakan QRIS dinamis serta berbagai metode pembayaran digital lainnya.
|
||||
• Dompet & Top-Up Saldo: Isi saldo dompet akun untuk kemudahan transaksi berkala.
|
||||
• Pembelian Pulsa & PPOB: Pembelian pulsa seluler, paket internet operator, dan token listrik langsung dari aplikasi.
|
||||
• Cek Kecepatan & Tiket Gangguan: Uji kecepatan koneksi internet dan ajukan tiket keluhan gangguan langsung ke tim teknis dengan respon cepat.
|
||||
• Manajemen Router Wi-Fi: Pantau dan atur jaringan Wi-Fi rumah secara praktis.
|
||||
|
||||
Fitur Khusus Petugas & Mitra (Teknisi, Kolektor, Kasir, & Agen):
|
||||
• Absensi Kerja Berbasis Geo-Tagging: Sistem presensi kehadiran harian dengan validasi GPS akurat dan foto selfie ber-watermark otomatis (lokasi, tanggal, dan waktu).
|
||||
• Manajemen Tugas SPK Lapangan: Penanganan tiket gangguan pelanggan dan instalasi pasang baru secara terorganisir.
|
||||
• Manajemen Tagihan & Rute Kolektor: Navigasi rute penagihan berbasis peta Google Maps dan pencetakan struk pembayaran thermal via Bluetooth.
|
||||
• Loket Kasir & Mitra Agen: Pembelian voucher hotspot dan transaksi PPOB secara aman.
|
||||
|
||||
Unduh aplikasi Alijaya Digital sekarang dan nikmati kemudahan mengelola seluruh kebutuhan layanan internet Anda dalam satu genggaman!
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 🏷️ Kategori & Kontak Pengembang (Store Settings)
|
||||
|
||||
- **Jenis Aplikasi:** Aplikasi (Application)
|
||||
- **Kategori:** Alat (Tools) atau Produktivitas (Productivity) / Bisnis (Business)
|
||||
- **Email Dukungan (Support Email):** `alijayanet@gmail.com` *(atau email resmi Anda)*
|
||||
- **Nomor Telepon Dukungan:** `+6287820851413`
|
||||
- **Situs Web (Website):** `http://alijaya.net` *(atau domain server Anda)*
|
||||
|
||||
---
|
||||
|
||||
## 3. 🛡️ Kebijakan Privasi (Privacy Policy)
|
||||
> [!IMPORTANT]
|
||||
> Google Play mewajibkan URL Kebijakan Privasi aktif karena aplikasi menggunakan izin Kamera dan Lokasi. Anda dapat menaruh URL berikut:
|
||||
> `http://<domain-server-anda>/privacy-policy` atau gunakan draf teks resmi di bawah ini:
|
||||
|
||||
```markdown
|
||||
# KEBIJAKAN PRIVASI (PRIVACY POLICY) - ALIJAYA DIGITAL
|
||||
|
||||
Terakhir diperbarui: 30 Agustus 2026
|
||||
|
||||
Alijaya Digital Network ("kami") berkomitmen untuk melindungi dan menghormati privasi pengguna aplikasi mobile Alijaya Digital ("Aplikasi"). Kebijakan Privasi ini menjelaskan bagaimana kami mengumpulkan, menggunakan, dan melindungi data pribadi Anda.
|
||||
|
||||
1. INFORMASI YANG KAMI KUMPULKAN
|
||||
Kami hanya mengumpulkan informasi yang relevan dan dibutuhkan untuk menjalankan fitur aplikasi:
|
||||
a. Informasi Akun: Nama, nomor telepon/WhatsApp, alamat rumah/pemasangan internet, ID pelanggan, dan username PPPoE/Hotspot.
|
||||
b. Informasi Lokasi (GPS): Data koordinat lokasi (Latitude & Longitude) digunakan saat:
|
||||
- Pelanggan mengajukan instalasi pasang baru atau pemetaan lokasi ODP/tiang terdekat.
|
||||
- Petugas (Teknisi, Kolektor, Kasir) melakukan absensi kehadiran kerja berbasis validasi radius kantor (Geofencing).
|
||||
- Petugas kolektor membuka rute penagihan pelanggan melalui Google Maps.
|
||||
c. Kamera & Foto: Izin kamera digunakan untuk:
|
||||
- Pengambilan foto selfie wajah petugas saat absensi kehadiran dengan watermark Geo-Tagging posisi.
|
||||
- Pengunggahan foto bukti kendala teknis saat pelaporan tiket gangguan.
|
||||
- Pemindaian QR Code (Barcode voucher / QRIS).
|
||||
d. Akses Bluetooth: Digunakan oleh petugas kolektor dan kasir untuk menghubungkan printer thermal Bluetooth dalam pencetakan struk pembayaran.
|
||||
|
||||
2. PENGGUNAAN INFORMASI
|
||||
Informasi yang dikumpulkan digunakan semata-mata untuk:
|
||||
- Memverifikasi identitas pengguna dan mengelola akun langganan internet.
|
||||
- Memproses pembayaran tagihan dan riwayat transaksi.
|
||||
- Memvalidasi presensi kerja dan koordinat lokasi tugas lapangan karyawan resmi.
|
||||
- Menghubungkan komunikasi layanan pelanggan melalui WhatsApp.
|
||||
|
||||
3. KEAMANAN DATA
|
||||
Kami menerapkan standar keamanan teknis dan enkripsi data (HTTPS/SSL) dalam transmisi data antara aplikasi dan server. Kami tidak pernah menjual, menyewakan, atau membagikan data pribadi Anda kepada pihak ketiga mana pun untuk kepentingan pemasaran.
|
||||
|
||||
4. HAK PENGGUNA & PENGHAPUSAN DATA
|
||||
Pengguna memiliki hak untuk meminta rincian data yang tersimpan atau meminta penghapusan akun beserta data terkait dengan menghubungi kami melalui email dukungan: alijayanet@gmail.com atau WhatsApp resmi customer support kami.
|
||||
|
||||
5. HUBUNGI KAMI
|
||||
Jika Anda memiliki pertanyaan seputar Kebijakan Privasi ini, silakan hubungi:
|
||||
Alijaya Digital Network
|
||||
Email: alijayanet@gmail.com
|
||||
Telepon/WA: +62 878-2085-1413
|
||||
Alamat: Indramayu, Jawa Barat, Indonesia.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 🔒 Deklarasi Keamanan Data (Data Safety Questionnaire)
|
||||
|
||||
Saat mengisi menu **Keamanan Data (Data Safety)** di Google Play Console, pilih jawaban berikut:
|
||||
|
||||
| Pertanyaan Google Play | Jawaban yang Dipilih |
|
||||
| :--- | :--- |
|
||||
| **Apakah aplikasi mengumpulkan data pengguna?** | **Ya (Yes)** |
|
||||
| **Apakah semua data dienkripsi saat transit?** | **Ya (Yes, HTTPS/TLS)** |
|
||||
| **Apakah ada cara bagi pengguna untuk meminta penghapusan data?** | **Ya (Yes)** |
|
||||
|
||||
### Rincian Kategori Data yang Dikumpulkan:
|
||||
1. **Lokasi (Location)**:
|
||||
- *Lokasi Akurat (Precise Location)* & *Lokasi Perkiraan (Approximate Location)*.
|
||||
- **Tujuan:** Fungsionalitas aplikasi (App functionality) & Pengelolaan akun (Account management) untuk absensi karyawan dan rute pelanggan.
|
||||
- **Apakah dibagikan ke pihak ketiga?** Tidak (No).
|
||||
2. **Foto dan Video (Photos and Videos)**:
|
||||
- *Foto (Photos)*.
|
||||
- **Tujuan:** Fungsionalitas aplikasi (Absensi selfie karyawan dan lampiran tiket komplain).
|
||||
- **Apakah dibagikan ke pihak ketiga?** Tidak (No).
|
||||
3. **Info Pribadi (Personal Info)**:
|
||||
- *Nama (Name)*, *Nomor Telepon (Phone number)*, *Alamat (Address)*.
|
||||
- **Tujuan:** Fungsionalitas aplikasi & Komunikasi pelanggan.
|
||||
- **Apakah dibagikan ke pihak ketiga?** Tidak (No).
|
||||
|
||||
---
|
||||
|
||||
## 5. 🎯 Target Audiens & Rating Konten
|
||||
|
||||
1. **Target Usia (Target Age):**
|
||||
- Pilih: **18 tahun ke atas (18+)** *(karena mencakup pembayaran tagihan langganan internet rumah)*.
|
||||
2. **Aplikasi Berita (News App):** Pilih **Tidak (No)**.
|
||||
3. **Iklan (Ads):** Pilih **Tidak, aplikasi saya tidak mengandung iklan (No, my app does not contain ads)**.
|
||||
4. **Pelacak Kontak COVID-19:** Pilih **Tidak (No)**.
|
||||
5. **Aplikasi Keuangan:** Pilih **Tidak (Bukan institusi perbankan/pinjol)**, hanya sistem pembayaran tagihan ISP sendiri.
|
||||
|
||||
---
|
||||
|
||||
## 6. 👤 Akun Akses Pengujian untuk Reviewer Google (App Access)
|
||||
|
||||
Google mewajibkan penyediaan akun demo agar penguji dari pihak Google dapat masuk dan menguji fitur di dalam aplikasi tanpa terhalang login:
|
||||
|
||||
- **Opsi yang Dipilih:** *Sebagian atau semua fungsi dibatasi (All or some functionality is restricted)*
|
||||
- **Nama Kredensial:** `Akun Penguji Google Play`
|
||||
- **Username / ID Pelanggan:** `demo` *(atau buat akun khusus pengujian di admin)*
|
||||
- **Password:** `demo123`
|
||||
- **Instruksi Pengujian:**
|
||||
```text
|
||||
Aplikasi ini menggunakan koneksi ke server Alijaya Billing System.
|
||||
Gunakan username: demo dan password: demo123 untuk menguji portal pelanggan.
|
||||
Koneksi internet aktif diperlukan.
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 🔑 Kredensial Keystore & Spesifikasi Teknis
|
||||
|
||||
Simpan data teknis ini di tempat yang aman:
|
||||
|
||||
| Parameter | Nilai |
|
||||
| :--- | :--- |
|
||||
| **Package Name** | `com.alijaya.customer` |
|
||||
| **Target SDK** | `Android 15 (SDK 35)` |
|
||||
| **Min SDK** | `Android 7.0 (SDK 24)` |
|
||||
| **Version Code** | `2` |
|
||||
| **Version Name** | `1.2.0` |
|
||||
| **File Keystore** | `alijaya-release.jks` |
|
||||
| **Keystore Alias** | `alijaya` |
|
||||
| **Keystore Password** | `AlijayaRelease2026!` |
|
||||
| **Key Password** | `AlijayaRelease2026!` |
|
||||
| **Validity** | 10.000 Hari (~27 Tahun) |
|
||||
|
||||
---
|
||||
|
||||
## 8. 🎨 Kebutuhan Aset Grafis (Graphic Assets)
|
||||
|
||||
Sebelum menekan tombol rilis, siapkan file gambar berikut:
|
||||
|
||||
1. **Ikon Aplikasi (App Icon):**
|
||||
- Ukuran: `512 x 512 piksel`
|
||||
- Format: PNG 32-bit dengan transparansi
|
||||
- Ukuran file maksimal: 1 MB
|
||||
2. **Grafis Fitur (Feature Graphic):**
|
||||
- Ukuran: `1024 x 500 piksel`
|
||||
- Format: JPEG atau PNG 24-bit (tanpa transparansi)
|
||||
- Ukuran file maksimal: 15 MB
|
||||
3. **Tangkapan Layar Ponsel (Phone Screenshots):**
|
||||
- Minimal 2 screenshot, maksimal 8 screenshot.
|
||||
- Rasio 16:9 atau 9:16 (contoh resolusi ideal: `1080 x 1920 piksel` atau `1080 x 2400 piksel`).
|
||||
- Disarankan mengambil screenshot dari:
|
||||
1. Layar Beranda Pelanggan (Cek Tagihan & Beli Pulsa)
|
||||
2. Layar Pembayaran QRIS / Invoice
|
||||
3. Layar Speedtest & Tiket Gangguan
|
||||
4. Layar Dashboard Tugas Teknisi Lapangan
|
||||
5. Layar Absensi Selfie dengan Watermark Geo-Tagging
|
||||
@@ -1,346 +0,0 @@
|
||||
================================================================================
|
||||
AUDIT LENGKAP APLIKASI BILLING B481
|
||||
STATUS: ✅ SELESAI
|
||||
================================================================================
|
||||
|
||||
TANGGAL AUDIT: Juni 2026
|
||||
APLIKASI STATUS: 🟢 PRODUCTION READY
|
||||
|
||||
================================================================================
|
||||
DOKUMENTASI YANG TELAH DIBUAT (6 FILE - 104 KB TOTAL)
|
||||
================================================================================
|
||||
|
||||
1. AUDIT_LENGKAP.md (45.57 KB)
|
||||
Audit komprehensif dengan 18 section mencakup:
|
||||
- Informasi umum aplikasi
|
||||
- Arsitektur sistem lengkap
|
||||
- Database schema overview
|
||||
- Semua endpoints & routes (100+ API)
|
||||
- 15+ fitur utama yang didokumentasikan
|
||||
- Keamanan & otentikasi
|
||||
- Production checklist
|
||||
|
||||
📖 BACA INI UNTUK: Referensi lengkap semua aspek aplikasi
|
||||
|
||||
---
|
||||
|
||||
2. QUICK_REFERENCE.md (6.67 KB)
|
||||
Panduan cepat untuk task-task umum:
|
||||
- Quick start commands
|
||||
- Common configuration tasks
|
||||
- Security settings (MUST DO)
|
||||
- Important endpoints
|
||||
- Database commands
|
||||
- Troubleshooting (7 issues)
|
||||
- Pre-launch checklist
|
||||
|
||||
📖 BACA INI UNTUK: Daily operations & quick lookups
|
||||
|
||||
---
|
||||
|
||||
3. DATABASE_SCHEMA.md (15.75 KB)
|
||||
Dokumentasi lengkap database:
|
||||
- Schema overview
|
||||
- 30+ tabel dengan struktur lengkap
|
||||
- Foreign key relationships
|
||||
- Query examples
|
||||
- Optimization tips
|
||||
- Backup & recovery procedures
|
||||
|
||||
📖 BACA INI UNTUK: Database operations & queries
|
||||
|
||||
---
|
||||
|
||||
4. BACKUP_CHECKLIST.md (10.58 KB)
|
||||
Prosedur backup & disaster recovery:
|
||||
- Pre-audit backup procedures
|
||||
- Step-by-step backup guide
|
||||
- Restore procedures (5 scenarios)
|
||||
- Automated backup setup
|
||||
- Disaster recovery plan
|
||||
- Best practices
|
||||
|
||||
📖 BACA INI UNTUK: Backup planning & restoration
|
||||
|
||||
---
|
||||
|
||||
5. AUDIT_SUMMARY.md (12.31 KB)
|
||||
Executive summary & roadmap:
|
||||
- Key findings & recommendations
|
||||
- Security checklist (CRITICAL)
|
||||
- Operational runbook
|
||||
- Maintenance procedures
|
||||
- Success criteria
|
||||
- Next steps
|
||||
|
||||
📖 BACA INI UNTUK: Overview & planning
|
||||
|
||||
---
|
||||
|
||||
6. DOKUMENTASI_INDEX.md (13.69 KB)
|
||||
Navigation guide untuk semua dokumentasi:
|
||||
- Quick links by topic
|
||||
- How to search documentation
|
||||
- Cross-references
|
||||
- FAQ section
|
||||
- Documentation roadmap
|
||||
|
||||
📖 BACA INI UNTUK: Mencari informasi spesifik
|
||||
|
||||
================================================================================
|
||||
MULAI DARI MANA?
|
||||
================================================================================
|
||||
|
||||
UNTUK PEMULA (5-15 MENIT):
|
||||
1. Baca file ini (README_AUDIT.txt)
|
||||
2. Baca AUDIT_SUMMARY.md (5 menit) - ringkasan lengkap
|
||||
3. Baca QUICK_REFERENCE.md (10 menit) - perintah dasar
|
||||
|
||||
UNTUK ADMIN/OPERATOR (30-60 MENIT):
|
||||
1. QUICK_REFERENCE.md - common tasks
|
||||
2. AUDIT_LENGKAP.md section 4 - endpoints & routes
|
||||
3. BACKUP_CHECKLIST.md - backup procedures
|
||||
4. QUICK_REFERENCE.md troubleshooting - tips
|
||||
|
||||
UNTUK DEVELOPER/MAINTENANCE (1-2 JAM):
|
||||
1. AUDIT_LENGKAP.md - full architecture
|
||||
2. DATABASE_SCHEMA.md - database structure
|
||||
3. BACKUP_CHECKLIST.md - operational procedures
|
||||
4. DOKUMENTASI_INDEX.md - untuk searching
|
||||
|
||||
================================================================================
|
||||
FITUR UTAMA YANG DIDOKUMENTASIKAN
|
||||
================================================================================
|
||||
|
||||
✅ Portal Pelanggan (Self-service)
|
||||
✅ Portal Admin (Manajemen lengkap)
|
||||
✅ Portal Teknisi (Field support)
|
||||
✅ Portal Agen (Sales & pembayaran)
|
||||
✅ Portal Kolektor (Approval pembayaran)
|
||||
✅ Portal Finance (Laporan keuangan)
|
||||
|
||||
✅ Billing & Invoicing (Tagihan otomatis)
|
||||
✅ MikroTik Integration (PPPoE, Hotspot, Voucher)
|
||||
✅ GenieACS & TR-069 (CPE/ONU management)
|
||||
✅ OLT PON Management (SNMP-based)
|
||||
✅ GIS Mapping (Peta pelanggan & ODP)
|
||||
✅ WhatsApp Automation (Baileys)
|
||||
|
||||
✅ Payment Gateway (Midtrans, Tripay, Xendit, Duitku)
|
||||
✅ Inventory Management (Gudang)
|
||||
✅ Ticketing System (Support)
|
||||
✅ Attendance Geofencing (Kehadiran GPS)
|
||||
|
||||
================================================================================
|
||||
STATISTIK DOKUMENTASI
|
||||
================================================================================
|
||||
|
||||
Total files: 6
|
||||
Total size: 104.56 KB
|
||||
Sections documented: 18+
|
||||
Database tables: 30+
|
||||
API endpoints: 100+
|
||||
Features covered: 15+
|
||||
Backup procedures: 5 scenarios
|
||||
Troubleshooting issues: 15+
|
||||
|
||||
================================================================================
|
||||
CHECKLIST - APA YANG HARUS DILAKUKAN SEGERA
|
||||
================================================================================
|
||||
|
||||
CRITICAL (BEFORE PRODUCTION):
|
||||
□ Baca AUDIT_SUMMARY.md security checklist
|
||||
□ Change session_secret di settings.json
|
||||
□ Change admin password
|
||||
□ Ganti admin_api_key
|
||||
□ Enable HTTPS (via reverse proxy)
|
||||
□ Test backup & restore procedure
|
||||
|
||||
IMPORTANT (THIS WEEK):
|
||||
□ Baca semua dokumentasi yang relevan
|
||||
□ Create full backup (database + files + code)
|
||||
□ Store backup di lokasi aman
|
||||
□ Setup automated daily backup
|
||||
□ Test restore backup
|
||||
|
||||
MEDIUM (THIS MONTH):
|
||||
□ Setup monitoring & alerting
|
||||
□ Setup log rotation
|
||||
□ Document customizations
|
||||
□ Create operational runbook
|
||||
|
||||
================================================================================
|
||||
FILE LOCATIONS
|
||||
================================================================================
|
||||
|
||||
Semua file dokumentasi tersimpan di root aplikasi:
|
||||
|
||||
d:\billing-B481\
|
||||
├── AUDIT_LENGKAP.md (45.57 KB) ← Main reference
|
||||
├── QUICK_REFERENCE.md (6.67 KB) ← Daily use
|
||||
├── DATABASE_SCHEMA.md (15.75 KB) ← DB operations
|
||||
├── BACKUP_CHECKLIST.md (10.58 KB) ← Backup procedures
|
||||
├── AUDIT_SUMMARY.md (12.31 KB) ← Overview & planning
|
||||
├── DOKUMENTASI_INDEX.md (13.69 KB) ← Navigation
|
||||
├── README_AUDIT.txt (This file)
|
||||
└── [original files...]
|
||||
|
||||
================================================================================
|
||||
PENTING: BACKUP DOKUMENTASI INI JUGA!
|
||||
================================================================================
|
||||
|
||||
Jangan lupa backup semua file dokumentasi ke lokasi aman:
|
||||
|
||||
OPTION 1 - Manual Backup:
|
||||
cp AUDIT_*.md QUICK_REFERENCE.md DATABASE_SCHEMA.md BACKUP_CHECKLIST.md /backup/
|
||||
|
||||
OPTION 2 - Tar Backup:
|
||||
tar -czf audit-docs-backup-$(date +%Y-%m-%d).tar.gz \
|
||||
AUDIT_*.md QUICK_REFERENCE.md DATABASE_SCHEMA.md BACKUP_CHECKLIST.md
|
||||
|
||||
OPTION 3 - Via Script:
|
||||
Add to your backup script & cron job
|
||||
|
||||
================================================================================
|
||||
STRUKTUR DOKUMENTASI
|
||||
================================================================================
|
||||
|
||||
┌─ QUICK START
|
||||
│ ├─ AUDIT_SUMMARY.md (5 min overview)
|
||||
│ └─ QUICK_REFERENCE.md (10 min guide)
|
||||
│
|
||||
├─ DETAILED REFERENCE
|
||||
│ ├─ AUDIT_LENGKAP.md (Complete audit - 18 sections)
|
||||
│ ├─ DATABASE_SCHEMA.md (Database reference - 30+ tables)
|
||||
│ └─ DOKUMENTASI_INDEX.md (Navigation guide)
|
||||
│
|
||||
├─ OPERATIONAL
|
||||
│ └─ BACKUP_CHECKLIST.md (Backup & recovery)
|
||||
│
|
||||
└─ NAVIGATION
|
||||
└─ README_AUDIT.txt (This guide)
|
||||
|
||||
================================================================================
|
||||
QUICK LINKS - CARI JAWABAN CEPAT
|
||||
================================================================================
|
||||
|
||||
Q: Apa saja portal aplikasi?
|
||||
A: AUDIT_LENGKAP.md section 1.3
|
||||
|
||||
Q: Bagaimana setup MikroTik?
|
||||
A: QUICK_REFERENCE.md atau AUDIT_LENGKAP.md section 5.2
|
||||
|
||||
Q: Bagaimana backup database?
|
||||
A: BACKUP_CHECKLIST.md section "Backup Database"
|
||||
|
||||
Q: Apa saja table database?
|
||||
A: DATABASE_SCHEMA.md
|
||||
|
||||
Q: Bagaimana troubleshooting error X?
|
||||
A: QUICK_REFERENCE.md "Troubleshooting" section
|
||||
|
||||
Q: Aplikasi ready untuk production?
|
||||
A: AUDIT_SUMMARY.md "Audit Completion Status"
|
||||
|
||||
Q: Gimana setup automated backup?
|
||||
A: BACKUP_CHECKLIST.md "Automated Backup (Cron Job)"
|
||||
|
||||
Q: Apa saja security settings yang harus diubah?
|
||||
A: QUICK_REFERENCE.md "Security Settings (MUST DO)"
|
||||
|
||||
================================================================================
|
||||
VERIFIKASI SEMUA FILE SUDAH ADA
|
||||
================================================================================
|
||||
|
||||
✅ AUDIT_LENGKAP.md (45.57 KB)
|
||||
✅ QUICK_REFERENCE.md (6.67 KB)
|
||||
✅ DATABASE_SCHEMA.md (15.75 KB)
|
||||
✅ BACKUP_CHECKLIST.md (10.58 KB)
|
||||
✅ AUDIT_SUMMARY.md (12.31 KB)
|
||||
✅ DOKUMENTASI_INDEX.md (13.69 KB)
|
||||
✅ README_AUDIT.txt (This file)
|
||||
|
||||
TOTAL: 7 files, 104.56 KB dokumentasi lengkap
|
||||
|
||||
================================================================================
|
||||
NEXT STEPS (URUTAN PENTING)
|
||||
================================================================================
|
||||
|
||||
1. READ (This week)
|
||||
□ AUDIT_SUMMARY.md (5 min) - Wajib dibaca semua orang!
|
||||
□ QUICK_REFERENCE.md (10 min) - Reference untuk daily use
|
||||
□ AUDIT_LENGKAP.md (30 min) - Jika butuh detail
|
||||
□ DATABASE_SCHEMA.md (20 min) - Jika ada query khusus
|
||||
□ BACKUP_CHECKLIST.md (10 min) - Sebelum backup
|
||||
|
||||
2. BACKUP (Immediately)
|
||||
□ Create full backup (database + files + code)
|
||||
□ Store backup in safe location (USB/NAS/Cloud)
|
||||
□ Test restore procedure
|
||||
□ Document backup location
|
||||
|
||||
3. SECURE (This week)
|
||||
□ Follow security checklist di AUDIT_SUMMARY.md
|
||||
□ Change passwords & secrets
|
||||
□ Setup HTTPS
|
||||
□ Setup firewall rules
|
||||
|
||||
4. MAINTAIN (Ongoing)
|
||||
□ Setup automated daily backup
|
||||
□ Setup monitoring & alerting
|
||||
□ Review logs regularly
|
||||
□ Test restore monthly
|
||||
□ Update documentation when changes occur
|
||||
|
||||
================================================================================
|
||||
SUPPORT & RESOURCES
|
||||
================================================================================
|
||||
|
||||
Original README:
|
||||
README.md (Original aplikasi documentation)
|
||||
|
||||
GitHub:
|
||||
https://github.com/alijayanet/billing-rtrw
|
||||
|
||||
Contact:
|
||||
Ali Jaya Net
|
||||
Phone: 081947215703
|
||||
Email: alijayanet@gmail.com
|
||||
|
||||
================================================================================
|
||||
DOKUMEN STATUS
|
||||
================================================================================
|
||||
|
||||
Audit Date: Juni 2026
|
||||
Audit Version: 1.0 (Final)
|
||||
Status: ✅ COMPLETE & APPROVED FOR PRODUCTION
|
||||
Documentation: 6 files, 104 KB total
|
||||
Last Updated: 2026-06-19
|
||||
|
||||
================================================================================
|
||||
CATATAN PENTING
|
||||
================================================================================
|
||||
|
||||
1. Dokumentasi ini adalah hasil audit teliti & lengkap
|
||||
2. Semua file tersimpan di root aplikasi billing-B481
|
||||
3. Documentasi sudah production-ready & siap digunakan
|
||||
4. Update dokumentasi jika ada perubahan pada aplikasi
|
||||
5. Backup dokumentasi ini juga (jangan lupa!)
|
||||
6. Share dokumentasi dengan tim operasional
|
||||
7. Review dokumentasi secara berkala
|
||||
|
||||
================================================================================
|
||||
SELAMAT MENGGUNAKAN! 🎉
|
||||
================================================================================
|
||||
|
||||
Jika ada pertanyaan atau butuh informasi lebih lanjut:
|
||||
1. Cek dokumentasi terlebih dahulu (gunakan DOKUMENTASI_INDEX.md untuk search)
|
||||
2. Baca FAQ section di file-file yang relevan
|
||||
3. Hubungi tim technical support
|
||||
|
||||
Terima kasih telah menggunakan dokumentasi audit lengkap ini!
|
||||
|
||||
================================================================================
|
||||
Generated: 2026-06-19
|
||||
Status: ✅ AUDIT COMPLETE & DOCUMENTED
|
||||
|
||||
@@ -1,202 +0,0 @@
|
||||
# Rencana Desain & Modernisasi UI/UX — Billing RT/RW Net
|
||||
*(Mengadopsi Referensi uiverse.io, ColorlibHQ/gentelella, dan Google Fonts)*
|
||||
|
||||
> **Status Server Biznet**: ✅ **Berhasil Dimatikan & Dinonaktifkan** (`systemctl stop/disable`, `pm2 stop/delete`). Tidak ada lagi tabrakan bot Telegram ataupun service jaringan.
|
||||
> **Server Produksi Aktif**: VPS `deneva` (`116.212.74.115` — port `3001`).
|
||||
|
||||
---
|
||||
|
||||
## 1. Konsep Desain & Pendekatan Dua Arah (Dual-Persona)
|
||||
|
||||
Aplikasi ini melayani dua profil pengguna yang berbeda secara mendasar:
|
||||
1. **Pelanggan (Customer Portal & Landing/Login)**: Menggunakan smartphone, membutuhkan antarmuka yang modern, bersih, intuitif, cepat, dan kaya animasi mikro.
|
||||
* **Referensi Utama**: **`uiverse.io`** (Glassmorphism, animasi tombol neon/glow, switch toggle interaktif, floating dock, card 3D halus).
|
||||
2. **Admin & Teknisi (Admin & NOC Portal)**: Membutuhkan visualisasi data densitas tinggi (*high-density information*), pemantauan lalu lintas real-time, status ONU dalam jumlah ratusan, serta tabel data yang efisien.
|
||||
* **Referensi Utama**: **`ColorlibHQ/gentelella`** (Gentelella Admin — Tile KPI counters, multi-tab `x_panel` containers, collapsible sidebar, traffic charts, dense status badges).
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph UI_Reference_System["Sistem Referensi Desain"]
|
||||
UIVERSE["uiverse.io (Modern CSS Components)"]
|
||||
GENTELELLA["ColorlibHQ/gentelella (Admin Dashboard)"]
|
||||
GFONTS["Google Fonts (Universal Typography)"]
|
||||
end
|
||||
|
||||
subgraph Customer_Side["Portal Pelanggan (PWA & Login)"]
|
||||
CustLogin["views/login.ejs (Hero & Glass Card)"]
|
||||
CustDash["views/dashboard.ejs (5-Tab App Shell)"]
|
||||
CustIsolated["views/isolated.ejs (Walled Garden)"]
|
||||
end
|
||||
|
||||
subgraph Admin_Side["Portal Admin, NOC & Teknisi"]
|
||||
AdminDash["views/admin/dashboard.ejs (Tile Stats & Charts)"]
|
||||
AdminTables["views/admin/* (Customers, OLT, Billing)"]
|
||||
AdminSidebar["views/admin/partials/sidebar.ejs"]
|
||||
AdminMap["views/admin/map.ejs (Network Topology)"]
|
||||
end
|
||||
|
||||
UIVERSE -->|Glass Cards, Neon Toggles, Pill Buttons| Customer_Side
|
||||
GENTELELLA -->|Tile Counters, X-Panels, Dense Tables| Admin_Side
|
||||
GFONTS -->|Plus Jakarta Sans, Inter, JetBrains Mono| Customer_Side
|
||||
GFONTS --> Admin_Side
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Tipografi Universal (Google Fonts)
|
||||
|
||||
Untuk memastikan kompatibilitas rendering 100% di semua browser (Chrome, Safari iOS, Firefox, Edge, Android WebView) tanpa ketergantungan pada font sistem lokal:
|
||||
|
||||
| Peruntukan | Font Family | Bobot (Weights) | Karakteristik & Kegunaan |
|
||||
|---|---|---|---|
|
||||
| **Customer Portal & Landing** | **Plus Jakarta Sans** | `300, 400, 500, 600, 700, 800` | Modern geometric sans, sangat nyaman dibaca di layar AMOLED HP, angka tagihan tegas dan modern. |
|
||||
| **Admin & Operasional** | **Inter** | `300, 400, 500, 600, 700, 800` | Standar industri dashboard, metrik angka rapat (*tabular numbers*), hierarki teks rapi. |
|
||||
| **Data Teknis & Jaringan** | **JetBrains Mono** | `400, 500, 700` | Alamat IP, MAC Address, OID SNMP, Serial Number ONU, nilai redaman optik (dBm). |
|
||||
|
||||
### Standar Preconnect & Loading:
|
||||
```html
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&family=JetBrains+Mono:wght@400;500;700&family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Peta Komponen & Transformasi Desain
|
||||
|
||||
### 3.1 Portal Admin (Adopsi `ColorlibHQ/gentelella`)
|
||||
|
||||
#### A. Gentelella Top Metric Stats ("Tile Count")
|
||||
Pada dashboard admin (`views/admin/dashboard.ejs`), susunan metrik diganti dengan pola **Gentelella Tile Count**:
|
||||
* **Format**: Baris kartu metrik ringkas horizontal dengan ikon latar samar, angka besar tebal, label deskriptif, dan indikator persentase tren (hijau naik / merah turun).
|
||||
* **Komponen yang Diterapkan**:
|
||||
1. `Total Pelanggan`: Angka aktif vs total, delta pertumbuhan bulanan.
|
||||
2. `PPPoE Online`: Real-time active sessions dengan status dot hijau berkedip.
|
||||
3. `Total ONU & Signal`: Jumlah online/offline + ONU dengan redaman kritis (< -27 dBm).
|
||||
4. `Tagihan & Kas Masuk`: Pendapatan bulan berjalan vs tagihan tertunda (*unpaid*).
|
||||
|
||||
```
|
||||
┌─────────────────┬─────────────────┬─────────────────┬─────────────────┐
|
||||
│ TOTAL PELANGGAN │ PPPoE ONLINE │ STATUS FTTH │ PENDAPATAN BULAN │
|
||||
│ 1,248 │ 1,180 │ 102 / 105 │ Rp 42.500.000 │
|
||||
│ ▲ +12 bln ini │ ● 94.5% online │ ⚠️ 3 Redaman Low│ ▲ 88% Tertagih │
|
||||
└─────────────────┴─────────────────┴─────────────────┴─────────────────┘
|
||||
```
|
||||
|
||||
#### B. Kontainer Widget Bergaya `x_panel`
|
||||
* Seluruh card data admin menggunakan struktur `x_panel`:
|
||||
* `x_title`: Judul section dengan border bawah tipis, sub-teks muted, dan *toolbox* kanan (tombol refresh data via AJAX, filter tanggal, expand/collapse).
|
||||
* `x_content`: Area konten responsif dengan padding lega, kompatibel dengan mode gelap dan terang.
|
||||
|
||||
#### C. Tabel Data Responsif (*Dense Table*)
|
||||
* Border halus (`border-slate-800`), font `Inter` 13px, baris zebra-stripe halus saat hover.
|
||||
* Badge status terstandarisasi:
|
||||
* Online: Badge hijau lembut (`bg-emerald-500/10 text-emerald-400 border border-emerald-500/20`).
|
||||
* Offline / Suspend: Badge merah lembut (`bg-rose-500/10 text-rose-400 border border-rose-500/20`).
|
||||
* Isolir: Badge amber (`bg-amber-500/10 text-amber-400 border border-amber-500/20`).
|
||||
|
||||
---
|
||||
|
||||
### 3.2 Portal Pelanggan & Login (Adopsi `uiverse.io`)
|
||||
|
||||
#### A. Card Glassmorphism dengan Border Glow
|
||||
* Card profil router, tagihan, dan paket internet menggunakan teknik glassmorphism tingkat tinggi dari `uiverse.io`:
|
||||
* Background: `rgba(30, 41, 59, 0.75)` dengan `backdrop-filter: blur(16px)`.
|
||||
* Border: Gradasi tipis `linear-gradient(135deg, rgba(45, 212, 191, 0.3), rgba(99, 102, 241, 0.1), transparent)`.
|
||||
* Shadow: `0 10px 30px -10px rgba(0, 0, 0, 0.5)`.
|
||||
|
||||
#### B. Switch Toggle & Input Kustom (Form Ganti WiFi & Settings)
|
||||
* Mengganti checkbox standar dengan **animated toggle switch** gaya `uiverse.io`:
|
||||
* Toggle 2.4 GHz vs 5 GHz dengan slider pill bercahaya.
|
||||
* Input password dengan animated floating border dan icon eye reveal responsif.
|
||||
* Tombol submit: Efek hover *lift-up* (`translateY(-2px)`) dengan box-shadow glow warna Teal `#14b8a6`.
|
||||
|
||||
#### C. Floating Action Dock (PWA Mobile Navigation)
|
||||
* Penyempurnaan 5-tab dock navigasi bawah:
|
||||
* Efek frosted glass yang lebih pekat (`backdrop-blur-xl`).
|
||||
* Active pill bergradasi halus dengan transisi kurva cubic-bezier (`cubic-bezier(0.34, 1.56, 0.64, 1)`).
|
||||
* Touch feedback haptic visual saat tab ditekan di layar HP.
|
||||
|
||||
---
|
||||
|
||||
## 4. Design Tokens & Palette Warna
|
||||
|
||||
```css
|
||||
:root {
|
||||
/* Brand Core (Skyfiber Identity) */
|
||||
--brand-teal-50: #f0fdfa;
|
||||
--brand-teal-400: #2dd4bf;
|
||||
--brand-teal-500: #14b8a6;
|
||||
--brand-teal-600: #0d9488;
|
||||
--brand-indigo: #6366f1;
|
||||
--brand-cyan: #06b6d4;
|
||||
|
||||
/* Dark Theme Palette (Default) */
|
||||
--bg-dark-base: #0b0f19;
|
||||
--bg-dark-card: #1e293b;
|
||||
--bg-dark-panel: #161b26;
|
||||
--border-dark: rgba(148, 163, 184, 0.15);
|
||||
--text-dark-high: #f8fafc;
|
||||
--text-dark-med: #94a3b8;
|
||||
--text-dark-low: #64748b;
|
||||
|
||||
/* Light Theme Palette */
|
||||
--bg-light-base: #f8fafc;
|
||||
--bg-light-card: #ffffff;
|
||||
--bg-light-panel: #f1f5f9;
|
||||
--border-light: rgba(15, 23, 42, 0.08);
|
||||
--text-light-high:#0f172a;
|
||||
--text-light-med: #475569;
|
||||
--text-light-low: #94a3b8;
|
||||
|
||||
/* Status Colors */
|
||||
--status-online: #10b981;
|
||||
--status-offline: #f43f5e;
|
||||
--status-warning: #f59e0b;
|
||||
--status-info: #38bdf8;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Rencana Tahapan Eksekusi (Implementation Milestones)
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
title Roadmap Eksekusi Desain & Modernisasi
|
||||
dateFormat X
|
||||
axisFormat Fase %s
|
||||
|
||||
section Persiapan
|
||||
Matikan Service Biznet & Verifikasi Port :done, 0, 1
|
||||
Integrasi Google Fonts Universal :done, 1, 2
|
||||
|
||||
section Portal Pelanggan (uiverse.io)
|
||||
Modernisasi Komponen WiFi & Card Glass :active, 2, 4
|
||||
Input Micro-Animations & Button Glow : 4, 5
|
||||
Verifikasi PWA Mobile & Responsive Check : 5, 6
|
||||
|
||||
section Portal Admin (Gentelella)
|
||||
Implementasi Top Tile Stats Count : 6, 8
|
||||
Standardisasi X-Panel & Action Toolbox : 8, 9
|
||||
Penyelarasan Tabel Data & Badges : 9, 10
|
||||
|
||||
section Validasi & Deploy
|
||||
Pengujian Lintas Browser & Dark/Light Mode : 10, 11
|
||||
Restart Service & Verifikasi Uptime : 11, 12
|
||||
```
|
||||
|
||||
### Tahap 1: Penguatan Komponen Customer Portal (Uiverse Style)
|
||||
1. Terapkan card glassmorphism dengan glow gradient pada form ganti SSID & Password di `views/dashboard.ejs`.
|
||||
2. Sempurnakan animated button untuk aksi "Simpan Wi-Fi", "Beli Voucher", dan "Cek Tagihan".
|
||||
3. Pastikan visual state mode terang (*light mode*) memiliki kontras tajam pada teks muted dan input field.
|
||||
|
||||
### Tahap 2: Modernisasi Admin Dashboard (Gentelella Style)
|
||||
1. Perbarui bagian atas `views/admin/dashboard.ejs` dengan modul **Gentelella Tile Stats**:
|
||||
* Total Pelanggan, Status Sesi MikroTik Aktif, Status ONU (Online/Offline/Warning Redaman), dan Tagihan Lunas/Tertunda.
|
||||
2. Tata ulang card monitoring ke dalam format `x_panel` yang memiliki toolbar aksi (Refresh AJAX, filter status, expand).
|
||||
|
||||
### Tahap 3: Uji Coba Lintas Perangkat & Validasi Akhir
|
||||
1. Uji di mobile browser (Android Chrome & iOS Safari).
|
||||
2. Verifikasi seluruh endpoint AJAX, ganti SSID modem, dan refresh ONU tetap berfungsi tanpa gangguan.
|
||||
3. Pastikan log journalctl pada `billing-rtrw.service` di `deneva` tetap bersih dan stabil.
|
||||
@@ -1,729 +0,0 @@
|
||||
# RINGKASAN APLIKASI - RTRWNET BILLING & MANAGEMENT SYSTEM
|
||||
**Status:** ✅ FULLY EXPLORED & DOCUMENTED
|
||||
**Tanggal:** 14 Agustus 2026
|
||||
**Format:** Quick Reference untuk understanding aplikasi
|
||||
|
||||
---
|
||||
|
||||
## 🎯 APLIKASI OVERVIEW
|
||||
|
||||
### What is RTRWNET?
|
||||
**RTRWNET** adalah sistem manajemen terintegrasi untuk **ISP/RTRW** yang mengelola:
|
||||
- ✅ Billing & invoicing otomatis
|
||||
- ✅ Manajemen pelanggan & paket
|
||||
- ✅ Jaringan fisik (MikroTik, OLT, ODP, cable routing)
|
||||
- ✅ Device management (CPE/ONU via TR-069/GenieACS)
|
||||
- ✅ Payment gateway integration (Midtrans, Tripay, Xendit, Duitku)
|
||||
- ✅ Agent/reseller management
|
||||
- ✅ WhatsApp automation
|
||||
- ✅ Support ticketing
|
||||
- ✅ Peta jaringan & GIS
|
||||
- ✅ Monitoring & health check
|
||||
|
||||
**Single Platform:** Satu dashboard untuk admin, teknisi, agen, dan pelanggan.
|
||||
|
||||
---
|
||||
|
||||
## 📚 PORTAL UTAMA
|
||||
|
||||
| Portal | URL | User | Fungsi Utama |
|
||||
|--------|-----|------|--------------|
|
||||
| **Customer** | `/customer` | Pelanggan | Cek tagihan, bayar, ubah Wi-Fi, lihat status |
|
||||
| **Admin** | `/admin` | Admin | Dashboard, billing, customer, MikroTik, settings |
|
||||
| **Teknisi** | `/tech` | Teknisi | Tiket, peta, device monitoring, support |
|
||||
| **Agen** | `/agent` | Reseller/Agent | Bayar tagihan pelanggan, jual voucher, jual pulsa |
|
||||
| **Kolektor** | `/collector` | Collector | Cek & ajukan pembayaran (pending approval) |
|
||||
| **Finance** | `/finance` | Admin/Finance | Laporan keuangan & pengeluaran |
|
||||
| **Public** | `/` | Publik | Cek tagihan tanpa login, halaman info |
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ ARSITEKTUR TEKNIS
|
||||
|
||||
### Backend Services (34 services)
|
||||
```
|
||||
1. billingService.js → Invoice generation, promo, prorata
|
||||
2. customerService.js → CRUD customer, paket, isolir
|
||||
3. mikrotikService.js → PPPoE, hotspot, firewall, monitoring
|
||||
4. oltService.js → OLT management via SNMP
|
||||
5. whatsappService.js → WhatsApp gateway (Baileys)
|
||||
6. paymentService.js → Payment gateway integration
|
||||
7. voucherPaymentService.js → E-voucher & payment methods
|
||||
8. agentService.js → Agent & Digiflazz integration
|
||||
9. cronService.js → Scheduled tasks automation
|
||||
10. monitoringService.js → System health & metrics
|
||||
11. ticketService.js → Support ticketing
|
||||
12. inventoryService.js → Stock & asset management
|
||||
13. auditTrailService.js → Action logging
|
||||
14. backupService.js → Database backup
|
||||
15. onuProvisionService.js → ONU TR-069 provisioning
|
||||
... + 19 services lainnya (acsServerService, radiusServerService, dll)
|
||||
```
|
||||
|
||||
### Database (SQLite)
|
||||
```
|
||||
Total: 50+ tables
|
||||
|
||||
Kategori:
|
||||
- Master: packages, customers, routers, olts, odps
|
||||
- Billing: invoices, payments, payment_approvals
|
||||
- Network: pppoe_users, hotspot_users, vouchers
|
||||
- Device: acs_devices, onu_mapping
|
||||
- Internal: technicians, agents, collectors, audit_trail
|
||||
- Other: expenses, cash_in, tickets, inventory_items
|
||||
```
|
||||
|
||||
### Routes/Endpoints (250+ endpoints)
|
||||
```
|
||||
/customer/* → Customer portal (login, billing, device)
|
||||
/admin/* → Admin panel (all management)
|
||||
/tech/* → Technician portal (tickets, map, devices)
|
||||
/agent/* → Agent portal (payment, voucher, pulsa)
|
||||
/collector/* → Collector portal (approval workflow)
|
||||
/finance/* → Finance reporting
|
||||
/acs/* → Built-in TR-069 ACS server
|
||||
/api/webhook/* → Payment webhooks
|
||||
```
|
||||
|
||||
### Frontend (EJS Templates)
|
||||
```
|
||||
18 main views + partials:
|
||||
- customer/ → 6 views (billing, device, voucher, etc)
|
||||
- admin/ → Subdirectory dengan main routes
|
||||
- tech/ → 3 views (dashboard, tickets, monitoring)
|
||||
- agent/ → 3 views
|
||||
- collector/ → 2 views
|
||||
- Public pages → login, register, check_billing, tos, privacy
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💰 FITUR BILLING
|
||||
|
||||
### Invoice Cycle
|
||||
```
|
||||
1. Otomatis: Cron @ 1 Jan setiap bulan jam 00:01
|
||||
- Generate invoice untuk semua customer
|
||||
- Kalkulasi promo price (jika applicable)
|
||||
- Kalkulasi prorata (bulan pertama)
|
||||
- Kalkulasi tax (PPN 11%)
|
||||
- Status: 'unpaid'
|
||||
|
||||
2. Notifikasi WhatsApp otomatis ke customer
|
||||
|
||||
3. Customer bayar via:
|
||||
- Payment gateway (Midtrans, Tripay, Xendit, Duitku)
|
||||
- QRIS static (QR fixed dengan nominal unik)
|
||||
- Manual via admin/agen
|
||||
- Kolektor (pending approval)
|
||||
|
||||
4. Status berubah: 'unpaid' → 'paid'
|
||||
|
||||
5. Notifikasi paid + auto-unisolir jika dalam suspend
|
||||
|
||||
6. Late payment → Auto-isolir @ hari yang ditentukan
|
||||
```
|
||||
|
||||
### Promo & Prorata
|
||||
```
|
||||
Promo:
|
||||
- Paket A: Rp 350rb/bulan, promo Rp 250rb (3 siklus)
|
||||
- Pelanggan baru: dapat promo 3 bulan pertama
|
||||
- Counter ditrack di customer.promo_cycles_used
|
||||
- Reset otomatis saat ganti paket
|
||||
|
||||
Prorata:
|
||||
- Tanggal pasang: 15 Agustus
|
||||
- Invoice pertama: Rp 350rb * (16 hari / 31 hari) = Rp 180rb
|
||||
- Bulan berikutnya: Rp 350rb penuh
|
||||
```
|
||||
|
||||
### Payment Gateway
|
||||
```
|
||||
Aktif (bisa kombinasi):
|
||||
- Midtrans → CC, Transfer, E-wallet
|
||||
- Tripay → Bank Transfer, QRIS, E-wallet
|
||||
- Xendit → Virtual Account, QR
|
||||
- Duitku → Aggregator
|
||||
- QRIS Static → Fixed QR code
|
||||
|
||||
Webhook Generic:
|
||||
- Endpoint: POST /api/webhook/v1/payment-notif
|
||||
- Accept dari bank/e-wallet apapun
|
||||
- Auto-match nominal → mark paid
|
||||
- Log semua untuk audit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 👥 CUSTOMER MANAGEMENT
|
||||
|
||||
### Data per Customer
|
||||
```
|
||||
Identitas:
|
||||
- Nama, HP, Email, NIK, Alamat
|
||||
- GPS Latitude/Longitude
|
||||
- Area/Kabupaten
|
||||
|
||||
Jaringan:
|
||||
- Paket (Starter/Pro/Premium)
|
||||
- MikroTik Router assign
|
||||
- PPPoE username
|
||||
- Hotspot username (optional)
|
||||
- Static IP (optional)
|
||||
- OLT & ONU Port
|
||||
- GenieACS Tag
|
||||
|
||||
Status & Billing:
|
||||
- Status: active/suspended/inactive
|
||||
- Isolir day (tanggal isolir otomatis)
|
||||
- Auto-isolir enable/disable
|
||||
- Promo cycles used
|
||||
```
|
||||
|
||||
### Isolir Workflow
|
||||
```
|
||||
Manual Isolir:
|
||||
1. Admin klik "Isolir" di customer detail
|
||||
2. Sistem hapus/disable PPPoE user di MikroTik
|
||||
3. Status berubah: active → suspended
|
||||
4. Redirect ke page /isolated (jika online)
|
||||
|
||||
Auto-Isolir:
|
||||
1. Cron job setiap hari jam 02:00
|
||||
2. Cek customer: status=active + unpaid invoices + today >= isolate_day
|
||||
3. Suspend PPPoE ke MikroTik
|
||||
4. Notifikasi WhatsApp
|
||||
|
||||
Unisolir:
|
||||
1. Pelanggan bayar
|
||||
2. Admin klik "Buka Isolir"
|
||||
3. Restore PPPoE user
|
||||
4. Status: suspended → active
|
||||
5. Customer online lagi
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🌐 MIKROTIK INTEGRATION
|
||||
|
||||
### PPPoE Workflow
|
||||
```
|
||||
1. Create customer dengan PPPoE username
|
||||
2. Sistem generate PPPoE secret (password)
|
||||
3. Connect ke MikroTik Router API
|
||||
4. Add user ke PPPoE service
|
||||
5. Customer bisa login dengan username/password
|
||||
|
||||
Live Monitoring:
|
||||
- View active sessions
|
||||
- Bytes in/out per session
|
||||
- Session duration
|
||||
- IP address
|
||||
|
||||
FUP (Fair Usage Policy):
|
||||
- Paket: limit 100GB/bulan
|
||||
- Cron setiap jam cek usage
|
||||
- Jika > 100GB: ganti profile ke 1Mbps
|
||||
- Reset 1 Jan bulan berikutnya
|
||||
|
||||
Night Mode (Jam Kalong):
|
||||
- 00:00 - 06:00: Speed 20Mbps
|
||||
- 06:00 - 00:00: Speed 10Mbps
|
||||
- Cron @ 00:00 & 06:00 switch profile
|
||||
```
|
||||
|
||||
### Hotspot
|
||||
```
|
||||
Similar ke PPPoE:
|
||||
- Create user dengan limit (uptime/data)
|
||||
- Monitor active sessions
|
||||
- Bandwidth limiting
|
||||
```
|
||||
|
||||
### Voucher Hotspot
|
||||
```
|
||||
1. Admin create batch (qty 100, validity 30 hari)
|
||||
2. Sistem generate 100 kode voucher unik
|
||||
3. Sync ke MikroTik hotspot
|
||||
4. Customer pakai untuk login hotspot portal
|
||||
5. Agent bisa jual voucher ke pelanggan
|
||||
6. Track status: created/active/used/expired
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎛️ GENIEACS & TR-069
|
||||
|
||||
### Built-in ACS
|
||||
```
|
||||
Aplikasi punya ACS internal (lightweight):
|
||||
- Tidak perlu GenieACS eksternal
|
||||
- Support ZTE, Huawei, FiberHome, dll
|
||||
- Auto-detect device properties
|
||||
- Port: 7547 (configurable)
|
||||
```
|
||||
|
||||
### Device Management
|
||||
```
|
||||
Features:
|
||||
- List semua ONU/CPE
|
||||
- View device status (online/offline)
|
||||
- Change SSID / Wi-Fi password
|
||||
- Bulk SSID change
|
||||
- Reboot device
|
||||
- Provision WAN (VLAN, bridge, PPPoE)
|
||||
- View device logs
|
||||
- Parameter configuration
|
||||
```
|
||||
|
||||
### Workflow
|
||||
```
|
||||
1. ONU/CPE boot → connect ke ACS server
|
||||
2. Sistem auto-detect device properties
|
||||
3. Admin bisa configure via panel
|
||||
4. Change SSID → send ke device via TR-069
|
||||
5. Device update config otomatis
|
||||
6. Customer notif via WhatsApp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔌 OLT & ONU MANAGEMENT
|
||||
|
||||
### OLT Setup
|
||||
```
|
||||
Konfigurasi:
|
||||
- IP address OLT
|
||||
- SNMP community string
|
||||
- Telnet credentials (untuk config WAN)
|
||||
- Web interface credentials
|
||||
|
||||
Multi-OLT:
|
||||
- Bisa manage banyak OLT sekaligus
|
||||
- Per pelanggan assign ke OLT mana
|
||||
```
|
||||
|
||||
### ONU Monitoring
|
||||
```
|
||||
Via SNMP query:
|
||||
- Status (online/offline)
|
||||
- Serial number
|
||||
- Optical signal (dBm)
|
||||
- Distance ke OLT
|
||||
- RX/TX power
|
||||
|
||||
Operations:
|
||||
- Reboot ONU
|
||||
- Rename (friendly name)
|
||||
- Authorize ONU (EPON)
|
||||
- Configure WAN (via Telnet/TR-069/REST API)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💳 PAYMENT FLOW
|
||||
|
||||
### Customer Bayar Invoice
|
||||
```
|
||||
1. Customer login portal
|
||||
2. Lihat tagihan belum bayar
|
||||
3. Click "Bayar"
|
||||
4. Pilih metode pembayaran (dari gateway aktif)
|
||||
5. Redirect ke payment gateway
|
||||
6. Customer input data (CC/transfer/etc)
|
||||
7. Gateway proses
|
||||
8. Success → Webhook ke aplikasi
|
||||
9. Aplikasi verify amount & signature
|
||||
10. Mark invoice PAID
|
||||
11. Send WhatsApp confirmation
|
||||
12. Auto-unisolir jika customer dalam suspend
|
||||
13. Dashboard terupdate
|
||||
```
|
||||
|
||||
### Webhook Payment
|
||||
```
|
||||
Generic endpoint untuk bank/e-wallet:
|
||||
POST /api/webhook/v1/payment-notif
|
||||
|
||||
Bisa terima dari:
|
||||
- Bank (SMS gateway → webhook)
|
||||
- E-wallet (API callback)
|
||||
- QRIS (bank QRIS provider)
|
||||
|
||||
Sistem:
|
||||
- Parse amount dari notifikasi
|
||||
- Find invoice / voucher dengan nominal cocok
|
||||
- Verify jika nominal sudah digunakan (duplikasi)
|
||||
- Mark as PAID
|
||||
- Log untuk audit
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗺️ GIS MAPPING
|
||||
|
||||
### Peta Admin
|
||||
```
|
||||
Features:
|
||||
- Leaflet map (OpenStreetMap + Satellite)
|
||||
- Marker customer (warna: hijau/merah/biru)
|
||||
- Marker ODP (orange)
|
||||
- Draw cable path (customer → ODP)
|
||||
- Popup detail (klik marker)
|
||||
- Zoom/pan/locate
|
||||
|
||||
Interaksi:
|
||||
- Click customer → detail + isolir button
|
||||
- Click ODP → info + available ports
|
||||
- Draw polyline → cable path (simpan)
|
||||
- Real-time PPPoE traffic graph
|
||||
- Google Maps route (untuk tech)
|
||||
```
|
||||
|
||||
### Cable Path
|
||||
```
|
||||
Simpan rute dari customer ke ODP:
|
||||
- Array of [lat, lng] coordinates
|
||||
- Calculate distance (KM)
|
||||
- Description (aerial/underground/etc)
|
||||
- Edit/delete existing path
|
||||
```
|
||||
|
||||
### Tech GPS (Optional)
|
||||
```
|
||||
- Tech login portal dengan mobile
|
||||
- Sistem track GPS lokasi tech
|
||||
- Geofence alert (keluar/masuk area kantor)
|
||||
- Nearby customers di radius tertentu
|
||||
- Chat WhatsApp dari peta
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💬 WHATSAPP AUTOMATION
|
||||
|
||||
### Setup
|
||||
```
|
||||
1. Admin buka /admin/whatsapp
|
||||
2. Scan QR code dengan WhatsApp app
|
||||
3. Verify & approve
|
||||
4. Bot siap mengirim pesan
|
||||
|
||||
Gateway:
|
||||
- Baileys (Unofficial WebSocket)
|
||||
- Meta Cloud API (Official, jika setup)
|
||||
```
|
||||
|
||||
### Automated Messages
|
||||
```
|
||||
Invoice Created:
|
||||
"Tagihan Anda bulan ini siap!
|
||||
Nominal: Rp 350.000
|
||||
Bayar di: [link]
|
||||
Due: 10 Agustus"
|
||||
|
||||
Invoice Paid:
|
||||
"✅ Pembayaran diterima!
|
||||
Tagihan Rp 350.000 LUNAS
|
||||
Terima kasih!"
|
||||
|
||||
Isolir Reminder (H-1):
|
||||
"⚠️ Reminder: Jatuh tempo BESOK
|
||||
Tagihan Rp 350.000
|
||||
Bayar untuk hindari isolir"
|
||||
|
||||
Isolir Notice:
|
||||
"⚠️ Layanan Anda DIPUTUS
|
||||
Penyebab: Tagihan belum dibayar
|
||||
Hubungi [support]"
|
||||
|
||||
Unisolir:
|
||||
"✅ Layanan Anda kembali AKTIF
|
||||
Terima kasih pembayarannya!"
|
||||
```
|
||||
|
||||
### Admin Commands (via WhatsApp)
|
||||
```
|
||||
"BILLING" → Send laporan billing hari ini
|
||||
"PPPOE" → List active PPPoE sessions
|
||||
"BALANCE" → Cek saldo Digiflazz
|
||||
"TOPUP 1000000" → Top-up agent
|
||||
"INVOICE" → List unpaid invoices
|
||||
```
|
||||
|
||||
### Broadcast
|
||||
```
|
||||
Select customers → Create message → Schedule → Send
|
||||
- Set delay antar pesan (3-5 detik)
|
||||
- Track delivery status
|
||||
- Pause/resume/stop anytime
|
||||
- Retry failed messages
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 👔 AGENT MANAGEMENT
|
||||
|
||||
### Agent Fitur
|
||||
```
|
||||
Dashboard:
|
||||
- Total saldo (Rp)
|
||||
- Today's transaction
|
||||
- Commission earned
|
||||
- Top customers
|
||||
|
||||
Pembayaran:
|
||||
- Cari pelanggan
|
||||
- Lihat unpaid invoices
|
||||
- Bayar dari saldo agent
|
||||
- Print struk
|
||||
|
||||
Voucher:
|
||||
- Lihat inventory
|
||||
- Jual ke customer
|
||||
- Print code
|
||||
- Track sale
|
||||
|
||||
Digiflazz (Pulsa/Data):
|
||||
- Browse produk (Telkomsel, Indosat, dll)
|
||||
- Category → Brand → Nominal
|
||||
- Buy untuk customer
|
||||
- Webhook status update
|
||||
```
|
||||
|
||||
### Admin Agent Control
|
||||
```
|
||||
- Create/edit agent
|
||||
- Top-up balance
|
||||
- Set commission %
|
||||
- View transaction history
|
||||
- Special pricing per agent
|
||||
- Performance report
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎫 TICKETING SYSTEM
|
||||
|
||||
### Ticket Status Flow
|
||||
```
|
||||
open → in_progress → resolved → closed
|
||||
|
||||
Open:
|
||||
- Customer create di portal
|
||||
- Auto-assign ke admin/tech
|
||||
|
||||
In Progress:
|
||||
- Tech ambil ticket
|
||||
- Add notes/photos
|
||||
- Update status
|
||||
|
||||
Resolved:
|
||||
- Tech mark selesai
|
||||
- Notif ke customer
|
||||
|
||||
Closed:
|
||||
- Customer confirm selesai
|
||||
- Optional: give rating (1-5)
|
||||
```
|
||||
|
||||
### Admin Features
|
||||
```
|
||||
- Dashboard tickets (count per status)
|
||||
- Assign ke technician
|
||||
- Reassign jika perlu
|
||||
- View ticket history
|
||||
- SLA tracking
|
||||
- Performance report
|
||||
```
|
||||
|
||||
### Tech Features
|
||||
```
|
||||
- Pool tickets (assigned to me)
|
||||
- View detail
|
||||
- Take ticket
|
||||
- Upload photos (before/after)
|
||||
- Add resolution notes
|
||||
- Mark resolved
|
||||
```
|
||||
|
||||
### Customer Features
|
||||
```
|
||||
- Create ticket
|
||||
- View my tickets
|
||||
- See status update
|
||||
- Add comment/photo
|
||||
- Rate satisfaction
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ AUTOMATION (CRON JOBS)
|
||||
|
||||
| Waktu | Tugas | Detail |
|
||||
|-------|-------|--------|
|
||||
| 1 Jan @ 00:01 | Generate invoices | Monthly billing untuk semua customer |
|
||||
| Setiap hari @ 02:00 | Auto-isolir | Suspend customer overdue |
|
||||
| Setiap hari @ 09:00 | Reminder tagihan | WhatsApp reminder H-1 isolir |
|
||||
| 00:00 & 06:00 | Night mode | Ganti profile PPPoE malam/siang |
|
||||
| Setiap jam | FUP check | Check usage vs limit, ganti profile |
|
||||
| Setiap 10 menit | Usage sync | Sinkron PPPoE traffic dari MikroTik |
|
||||
| Setiap hari @ 02:00 | Database backup | Auto-backup dengan retention |
|
||||
|
||||
---
|
||||
|
||||
## 📊 DATABASE OVERVIEW
|
||||
|
||||
### Key Tables
|
||||
```
|
||||
customers
|
||||
├── Identitas (name, phone, email, nik)
|
||||
├── Jaringan (pppoe_username, hotspot_username, static_ip)
|
||||
├── Device (olt_id, onu_port, genieacs_tag)
|
||||
├── Billing (package_id, isolate_day, auto_isolate)
|
||||
└── Status (status, created_at, updated_at)
|
||||
|
||||
invoices
|
||||
├── Amount (amount, status, qris_amount_unique)
|
||||
├── Period (period_month, period_year)
|
||||
├── Payment (paid_at, payment_method)
|
||||
└── Notes (promo/prorata info)
|
||||
|
||||
payments
|
||||
├── Transaction data (gateway_id, signature, status)
|
||||
├── Customer info
|
||||
└── Invoice link
|
||||
|
||||
pppoe_users
|
||||
├── Credentials (username, secret)
|
||||
├── Profile & limits
|
||||
└── Session tracking
|
||||
|
||||
... + 40+ tables lainnya
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 SUMMARY - FITUR UTAMA YANG BERJALAN
|
||||
|
||||
✅ **Billing & Invoicing**
|
||||
- Auto generate bulanan
|
||||
- Promo cycles tracking
|
||||
- Prorata bulan pertama
|
||||
- Multi-gateway payment
|
||||
|
||||
✅ **Customer Lifecycle**
|
||||
- CRUD pelanggan
|
||||
- Paket management
|
||||
- Auto-isolir/unisolir
|
||||
- Status tracking
|
||||
|
||||
✅ **Network Management**
|
||||
- MikroTik PPPoE/Hotspot
|
||||
- Voucher hotspot
|
||||
- Firewall & rate limiting
|
||||
- Night mode automation
|
||||
|
||||
✅ **Device Management**
|
||||
- Built-in TR-069 ACS
|
||||
- GenieACS integration
|
||||
- SSID/password change
|
||||
- Device provisioning
|
||||
|
||||
✅ **OLT/ONU Management**
|
||||
- SNMP monitoring
|
||||
- Reboot/rename/authorize
|
||||
- WAN configuration
|
||||
- Multi-brand support
|
||||
|
||||
✅ **Payment Processing**
|
||||
- 4 payment gateways
|
||||
- QRIS static
|
||||
- Generic webhook
|
||||
- Payment verification
|
||||
|
||||
✅ **GIS Mapping**
|
||||
- Leaflet maps
|
||||
- Cable path tracking
|
||||
- GPS tech tracking
|
||||
- Real-time graphs
|
||||
|
||||
✅ **WhatsApp Automation**
|
||||
- Invoice notification
|
||||
- Payment reminder
|
||||
- Isolir notice
|
||||
- Broadcast messaging
|
||||
|
||||
✅ **Agent Management**
|
||||
- Pembayaran dari saldo
|
||||
- Voucher sales
|
||||
- Pulsa/data (Digiflazz)
|
||||
- Commission tracking
|
||||
|
||||
✅ **Support Ticketing**
|
||||
- Create/assign/resolve
|
||||
- Photo upload
|
||||
- Rating system
|
||||
- History tracking
|
||||
|
||||
✅ **Monitoring**
|
||||
- System health metrics
|
||||
- Service status
|
||||
- Alert system
|
||||
- Performance tracking
|
||||
|
||||
✅ **Automation**
|
||||
- 7 scheduled jobs
|
||||
- Configurable timing
|
||||
- Error handling
|
||||
- Logging
|
||||
|
||||
✅ **Multi-Portal**
|
||||
- Customer self-service
|
||||
- Admin full control
|
||||
- Technician field support
|
||||
- Agent sales
|
||||
- Collector approval
|
||||
|
||||
✅ **Compliance**
|
||||
- Audit trail logging
|
||||
- Role-based access
|
||||
- Session management
|
||||
- Data validation
|
||||
|
||||
---
|
||||
|
||||
## 🚀 PRODUCTION STATUS
|
||||
|
||||
**Aplikasi ini SUDAH BERJALAN** dan mencakup:
|
||||
- ✅ Complete billing system
|
||||
- ✅ Full customer management
|
||||
- ✅ Network device integration
|
||||
- ✅ Payment processing
|
||||
- ✅ Multi-user portals
|
||||
- ✅ Automation & scheduling
|
||||
- ✅ Monitoring & health check
|
||||
|
||||
**Siap untuk:**
|
||||
- ✅ Production deployment
|
||||
- ✅ Scaling operasi ISP
|
||||
- ✅ Integration ke sistem existing
|
||||
- ✅ Customization per requirements
|
||||
|
||||
---
|
||||
|
||||
## 📖 DOKUMENTASI TERSEDIA
|
||||
|
||||
1. **FITUR_APLIKASI_LENGKAP.md** ← Dokumentasi ini (16 fitur utama + detail)
|
||||
2. **AUDIT_LENGKAP.md** (Existing) ← Technical deep dive
|
||||
3. **DATABASE_SCHEMA.md** (Existing) ← Struktur data lengkap
|
||||
4. **QUICK_REFERENCE.md** (Existing) ← Common commands
|
||||
5. **AUDIT_SECURITY_2026.md** (Baru) ← Security audit findings
|
||||
6. **IMPLEMENTATION_GUIDE_SECURITY.md** (Baru) ← Fix guide
|
||||
7. **README.md** (Existing) ← Instalasi & setup
|
||||
|
||||
---
|
||||
|
||||
**Status:** ✅ Aplikasi komprehensif, well-documented, siap production
|
||||
**Next Step:** Implementasi security fixes (bcrypt, .env, CSRF) sebelum production
|
||||
@@ -1,81 +0,0 @@
|
||||
# RUJUKAN ARSITEKTUR & FITUR SISTEM BILLING RTRW-NET
|
||||
|
||||
Dokumen ini disimpan sebagai **rujukan utama** arsitektur, fitur backend, frontend, dan skema database sebelum dilakukan penambahan fitur RADIUS Server.
|
||||
|
||||
---
|
||||
|
||||
## 1. DOKUMENTASI MODUL & KOMPONEN EKSISTING
|
||||
|
||||
### A. Core Engine & Entry Point
|
||||
- **`app-customer.js`**: Main entry point Express.js v4.
|
||||
- Memuat middleware security: CORS, Referer/Origin CSRF Protection, Express Session, Body Parser limit 1MB.
|
||||
- Pengatur i18n multi-bahasa (`locales/`).
|
||||
- Menjalankan background service: Auto-backup (`scheduleAutoBackup`), Cron Billing (`cronService.js`), WhatsApp Bot, Telegram Bot.
|
||||
- Mengunci DNS ke IPv4 (`dns.setDefaultResultOrder('ipv4first')`) untuk mencegah timeout IPv6 di server produksi.
|
||||
|
||||
### B. Database & Stored Layer (`config/database.js`)
|
||||
- **Engine**: SQLite 3 menggunakan driver `better-sqlite3` (Native C++ extension, high performance).
|
||||
- **Mode**: `WAL` (Write-Ahead Logging) dengan `foreign_keys = ON`.
|
||||
- **Fungsi Kustom SQLite**: `NOW_LOCAL()` (memformat tanggal-waktu berdasarkan timezone lokal `Asia/Jakarta`).
|
||||
- **Tabel Utama**:
|
||||
1. `settings` (Dynamic Key-Value Store)
|
||||
2. `packages` (Paket Internet: harga, speed up/down, promo, jam kalong, FUP)
|
||||
3. `customers` (Data Pelanggan: no_pelanggan, nama, phone, pppoe_user, pppoe_secret, status, isolir_date, router_id, odp_id, genieacs_tag)
|
||||
4. `invoices` & `payments` (Tagihan & Pembayaran)
|
||||
5. `routers` (Daftar Router MikroTik: host, port 8728, username, password, status)
|
||||
6. `pppoe_users` & `pppoe_traffic_samples` & `pppoe_sessions`
|
||||
7. `hotspot_users` & `hotspot_profiles` & `hotspot_sessions`
|
||||
8. `voucher_batches` & `vouchers` & `public_voucher_orders`
|
||||
9. `olts`, `odps`, `onu_mapping`, `acsdevices` (Modul FTTH & TR-069 ACS)
|
||||
10. `technicians`, `cashiers`, `collectors`, `agents` (Akses multi-role)
|
||||
11. `audit_trail` (Log aktivitas pengguna)
|
||||
|
||||
### C. Services Layer (`services/`)
|
||||
1. **`mikrotikService.js`**:
|
||||
- Menghubungkan aplikasi ke RouterOS via API port 8728/8729 menggunakan `routeros-client`.
|
||||
- Mengelola PPPoE Secret (Add, Update, Delete, Toggle Enable/Disable).
|
||||
- Mengelola Hotspot User & Active Sessions.
|
||||
- Mengambil trafik interface & active connections secara realtime.
|
||||
- Mengatur isolir pelanggan via MikroTik API (mengubah profile / IP pool).
|
||||
2. **`customerService.js`**:
|
||||
- CRUD Pelanggan, komparasi data lokal vs router.
|
||||
- Penanganan siklus isolir & pemulihan status otomatis.
|
||||
3. **`billingService.js`**:
|
||||
- Penjadwalan & pembuatan invoice bulanan.
|
||||
- Pembayaran invoice (Cash, Transfer, QRIS, Gateway Midtrans/Tripay/Xendit).
|
||||
- Eksekusi auto-isolir saat jatuh tempo.
|
||||
4. **`cronService.js`**:
|
||||
- Menjalankan penagihan otomatis jam 00:05.
|
||||
- Cek auto-isolir harian jam 01:00.
|
||||
- Sampling statistik trafik jam 10 menit sekali.
|
||||
- Notifikasi reminder tagihan via WA / Telegram.
|
||||
5. **`oltService.js` & `onuProvisionService.js` & `acsServerService.js`**:
|
||||
- Integrasi OLT ZTE/Huawei/FiberHome via SNMP & Telnet.
|
||||
- TR-069 Auto-Provisioning CPE via GenieACS.
|
||||
|
||||
### D. Route & Portal Layer (`routes/`)
|
||||
1. **`/admin` (`adminPortal.js`)**: Portal Utama Admin (Dashboard, Pelanggan, Paket, Router, Transaksi, Laporan, OLT/ACS, Settings).
|
||||
2. **`/customer` (`customerPortal.js`)**: Portal Pelanggan Self-Service (Cek Tagihan, Bayar QRIS, Tiket Komplain).
|
||||
3. **`/agent` (`agentPortal.js`)**: Portal Agen (Jual Voucher, Topup Pulsa Digiflazz).
|
||||
4. **`/collector` (`collectorPortal.js`)**: Portal Kolektor Penagih Lapangan.
|
||||
5. **`/tech` (`techPortal.js`)**: Portal Teknisi (Tiket Gangguan, Pasang Baru, Mapping ODP).
|
||||
6. **`/finance` (`financePortal.js`)**: Portal Keuangan (Pemasukan & Pengeluaran Kas).
|
||||
|
||||
### E. Frontend & Views (`views/`)
|
||||
- Menggunakan **EJS (Embedded JavaScript)** dengan template responsive.
|
||||
- Menggunakan CSS Vanilla & Custom Theme (Dark/Light Mode), Bootstrap Icons / FontAwesome.
|
||||
- AJAX Fetch API untuk interaksi data tanpa reload halaman.
|
||||
|
||||
---
|
||||
|
||||
## 2. ATURAN INTEGRASI FITUR RADIUS (NON-BREAKING GUARANTEE)
|
||||
|
||||
1. **Prinsip Utama**: Seluruh modul MikroTik API eksisting **TIDAK BOLEH DIUBAH ATAU DIHAPUS**.
|
||||
2. **Fleksibilitas**: Sistem akan mendukung 2 mode otentikasi router:
|
||||
- **Mode 1 (Default Eksisting)**: MikroTik API (`routeros-client`).
|
||||
- **Mode 2 (Fitur Baru)**: RADIUS Server (UDP Port 1812/1813).
|
||||
- **Mode 3 (Hybrid)**: MikroTik API + RADIUS Server berjalan bersamaan.
|
||||
3. **Database Integration**: RADIUS Server akan langsung membaca & menulis ke `database/billing.db` eksisting:
|
||||
- Mengotentikasi kredensial pengguna dari tabel `customers` / `pppoe_users` / `hotspot_users`.
|
||||
- Mengirimkan atribut RADIUS standar (`Framed-IP-Address`, `Mikrotik-Rate-Limit`, `Session-Timeout`).
|
||||
- Menerima paket RADIUS Accounting (Start/Interim/Stop) dan mencatat penggunaan kuota/bandwidth ke tabel `pppoe_traffic_samples` & `radius_accounting_logs`.
|
||||
@@ -1,98 +0,0 @@
|
||||
# UI/UX Rules — Billing RTRW (SKYFIBER)
|
||||
|
||||
> Dokumen acuan wajib untuk **semua** pekerjaan UI/UX di project ini.
|
||||
> Setiap kali membuat / mengubah tampilan, ikuti aturan di bawah.
|
||||
|
||||
---
|
||||
|
||||
## Peran
|
||||
|
||||
**Act as an Expert Senior Frontend Developer.**
|
||||
|
||||
Your task is to generate a highly responsive, modern, and fluid web layout for
|
||||
**[Sebutkan apa yang ingin dibuat, misal: a Dashboard / Landing Page / Navigation Bar]**
|
||||
using **[Sebutkan teknologi, misal: HTML & Tailwind CSS / HTML & Vanilla CSS / EJS]**.
|
||||
|
||||
> Di project ini umumnya: **EJS + Vanilla CSS** (`public/css/admin.css`, `landing.css`,
|
||||
> `auth.css`, `admin-theme.css`) dan **Bootstrap Icons** (bukan emoji di UI).
|
||||
|
||||
---
|
||||
|
||||
## Strict Layout & Responsiveness Requirements
|
||||
|
||||
### 1. Fluid Typography & Spacing
|
||||
- Gunakan fungsi CSS **`clamp()`** untuk ukuran font, padding, dan margin agar skala
|
||||
mulus di semua ukuran layar (mobile → desktop 4K) **tanpa** hanya mengandalkan
|
||||
media query yang patah-patah.
|
||||
- Contoh: `font-size: clamp(0.95rem, 0.9rem + 0.5vw, 1.25rem);`
|
||||
`padding: clamp(12px, 2vw, 28px);`
|
||||
|
||||
### 2. Zero Overlap Guarantee
|
||||
Pastikan teks, kartu, tombol, dan ikon **tidak pernah** tumpang tindih atau keluar
|
||||
dari kontainernya:
|
||||
- `overflow-wrap: break-word;` atau `word-break: break-word;` untuk teks panjang
|
||||
(mis. link, nama panjang, kode).
|
||||
- `min-width: 0;` pada **flex/grid children** agar tidak meluber keluar parent.
|
||||
- Gunakan **`flex-wrap: wrap` + CSS `gap`** (bukan margin tetap) untuk jarak antar item.
|
||||
- Gambar & ikon harus responsif: `max-width: 100%; height: auto;` atau
|
||||
`aspect-ratio` + `object-fit: contain|cover`.
|
||||
|
||||
### 3. Flexible & Mappable Structure (CSS Grid / Flexbox)
|
||||
- Bangun layout yang **self-adapting**.
|
||||
- **CSS Grid** dengan `repeat(auto-fit, minmax(..., 1fr))` untuk grid kartu agar
|
||||
otomatis memetakan ke ruang layar yang tersedia.
|
||||
Contoh: `grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));`
|
||||
- **Flexbox** untuk perataan (alignment) & distribusi.
|
||||
- **Hindari tinggi tetap** (`height: 100px`); gunakan **`min-height`** agar konten
|
||||
bisa memanjang dengan aman.
|
||||
|
||||
### 4. Mobile-First Approach
|
||||
- Mulai dari layout **vertikal** yang solid untuk mobile.
|
||||
- Lalu naikkan dengan **media query** (`@media (min-width: ...)`) atau breakpoint
|
||||
Tailwind (`md:`, `lg:`) untuk memetakan ke **grid/flex horizontal** di layar besar.
|
||||
|
||||
### 5. Debug-Friendly & Minimal Bugs
|
||||
- Tulis HTML yang **bersih, semantik, dan dangkal** (hindari *div soup*).
|
||||
- Gunakan **CSS Custom Properties (Variabel) terpusat** untuk warna, spacing, dan
|
||||
font agar perubahan cepat & mudah.
|
||||
Contoh: `--primary: #ADFF2F; --bg: #0A0D14; --radius: 14px;`
|
||||
- Beri **komentar jelas** di kode yang menjelaskan logika layout.
|
||||
|
||||
---
|
||||
|
||||
## Output
|
||||
|
||||
Output **tepat kode yang diperlukan**, pastikan:
|
||||
- **siap dijalankan** (ready to run),
|
||||
- **bebas bug** (bug-free),
|
||||
- **mudah di-debug**.
|
||||
|
||||
---
|
||||
|
||||
## Catatan khusus project ini
|
||||
|
||||
- **Tema**: dark (default) + lime `#ADFF2F` di atas near-black `#0A0D14`; harus ada
|
||||
toggle dark/light di setiap halaman.
|
||||
- **Ikon**: **Bootstrap Icons** saja untuk UI (emoji hanya untuk teks WhatsApp).
|
||||
- **Brand**: nama & logo diambil dari settings (`brandName` / `brandLogo`), jangan hardcode.
|
||||
- **Bilingual (ID/EN)** via `t('key', 'fallback')` — identifier kode tetap Inggris.
|
||||
- **Admin**: markup sidebar & `admin.css`/`dashboard.ejs` diperlakukan "frozen";
|
||||
penyesuaian via overlay `admin-theme.css`.
|
||||
- **Scroll**: area konten admin = `.mw` (scroll container). Jangan bikin konten
|
||||
terpotong; `.page` dibiarkan tumbuh (`flex: 0 0 auto`) agar `.mw` bisa scroll.
|
||||
- **Modal**: satu scroll saja (`.mb` yang scroll, `.mbody` `overflow: visible`) —
|
||||
hindari *nested scroll* yang mengunci.
|
||||
- **Responsif**: gunakan `100dvh` (bukan `100vh`) untuk area penuh-layar di mobile.
|
||||
|
||||
---
|
||||
|
||||
## Checklist cepat sebelum selesai
|
||||
|
||||
- [ ] Ukuran font/spacing pakai `clamp()` di elemen utama.
|
||||
- [ ] Tidak ada teks/ikon yang meluber/overlap (cek `min-width:0`, `overflow-wrap`, `flex-wrap`).
|
||||
- [ ] Grid kartu pakai `repeat(auto-fit, minmax(..., 1fr))`.
|
||||
- [ ] Tidak ada `height` tetap pada blok konten (pakai `min-height`).
|
||||
- [ ] Mobile-first + breakpoint untuk layar besar.
|
||||
- [ ] HTML semantik & dangkal; warna/spacing via CSS variables.
|
||||
- [ ] Dark/light berfungsi; ikon Bootstrap Icons; bilingual ID/EN.
|
||||
- [ ] Scroll & modal normal di mobile **dan** desktop.
|
||||
@@ -1,567 +0,0 @@
|
||||
# Changelog Perubahan Kode — Billing RTRW
|
||||
|
||||
Project : `/opt/billing-rtrw`
|
||||
Host : `deneva`
|
||||
Service : `billing-rtrw` (systemd, port 3001)
|
||||
Owner : `billing:billing`
|
||||
|
||||
> Project ini **belum memakai Git**, jadi file ini adalah satu-satunya catatan
|
||||
> perubahan. Setiap perubahan kode selalu disertai backup file asli (lihat
|
||||
> bagian *Rollback* pada tiap entri).
|
||||
|
||||
Format: `## [versi] — tanggal` (terbaru di atas).
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.7] — 2026-09-28 16:55 — Full UI/UX Modernization & Database Optimization (Gentelella Tiles, uiverse.io Effects & Google Fonts)
|
||||
|
||||
### 1. Modernisasi Lengkap Seluruh Halaman Utama Admin (Gentelella Admin Style)
|
||||
- **views/admin/dashboard.ejs**: Implementasi Tile KPI Stat horizontal (Total ONU, Online, Offline, Warning Redaman) dan Tile Keuangan (Pendapatan, Pelanggan Aktif, Tagihan Belum Bayar, Total Piutang) dengan indikator tren visual.
|
||||
- **views/admin/customers.ejs**: Mengubah stat-grid filter pelanggan menjadi Gentelella Tile Cards interaktif (Total, Aktif, Disuspend, Nonaktif/Baru) dengan highlight filter aktif dan angka monospaced.
|
||||
- **views/admin/billing.ejs**: Mengubah summary tagihan menjadi Gentelella Tile Cards (Total Tagihan, Sudah Bayar, Belum Bayar, Rasio Lunas).
|
||||
- **views/admin/monitoring.ejs & views/admin/olts.ejs**: Menambahkan Google Fonts universal (Inter + JetBrains Mono).
|
||||
|
||||
### 2. Harmonisasi Desain Portal Pelanggan & Login (uiverse.io Style)
|
||||
- **views/login.ejs**: Menyelaraskan seluruh palette warna dari hardcoded Indigo ke tema resmi Skyfiber (Teal #0d9488 & Cyan #06b6d4), menambahkan Google Fonts Plus Jakarta Sans, dan uiverse elevated glass card.
|
||||
- **views/dashboard.ejs**: Menambahkan Google Fonts JetBrains Mono untuk pembacaan parameter teknis (IP, MAC, redaman dBm) dan efek micro-animation tombol .btn-uiverse-glow.
|
||||
|
||||
### 3. Optimasi Database SQLite
|
||||
- Eksekusi PRAGMA integrity_check (hasil: ok).
|
||||
- Eksekusi PRAGMA optimize untuk query planner performa tinggi.
|
||||
|
||||
### File disentuh
|
||||
- public/css/admin.css
|
||||
- views/admin/dashboard.ejs
|
||||
- views/admin/customers.ejs
|
||||
- views/admin/billing.ejs
|
||||
- views/admin/monitoring.ejs
|
||||
- views/admin/olts.ejs
|
||||
- views/login.ejs
|
||||
- views/dashboard.ejs
|
||||
- views/isolated.ejs
|
||||
- update-code.md
|
||||
|
||||
### Rollback
|
||||
```bash
|
||||
cp /opt/billing-rtrw/views/admin/customers.ejs.bak-gentelella /opt/billing-rtrw/views/admin/customers.ejs
|
||||
cp /opt/billing-rtrw/views/admin/billing.ejs.bak-gentelella /opt/billing-rtrw/views/admin/billing.ejs
|
||||
cp /opt/billing-rtrw/views/admin/dashboard.ejs.bak-gentelella /opt/billing-rtrw/views/admin/dashboard.ejs
|
||||
cp /opt/billing-rtrw/views/login.ejs.bak-plusjakarta /opt/billing-rtrw/views/login.ejs
|
||||
cp /opt/billing-rtrw/views/dashboard.ejs.bak-uiverse /opt/billing-rtrw/views/dashboard.ejs
|
||||
sudo systemctl restart billing-rtrw.service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.6] — 2026-09-28 16:35 — Modernisasi UI/UX (Gentelella Admin Tiles, uiverse.io Effects & Google Fonts) + Matikan Service Biznet
|
||||
|
||||
### 1. Pematian Layanan di Server Biznet (Pencegahan Tabrakan Service)
|
||||
- Service `billing-rtrw.service` di host `biznet` di-stop dan di-disable (`sudo systemctl stop & disable`).
|
||||
- Process PM2 di `biznet` di-stop dan di-delete.
|
||||
- Hasil: Menghilangkan konflik 409 pada Telegram Bot dan mencegah duplikasi trigger cron isolir/tagihan antar-server.
|
||||
|
||||
### 2. Adopsi Desain Gentelella Admin (views/admin/dashboard.ejs & public/css/admin.css)
|
||||
- Mengganti grid metrik lama dengan modul **Gentelella Tile Stats (`.tile_count` & `.tile_stats_count`)**:
|
||||
- Border samping berwarna aksen (`tile-primary`, `tile-success`, `tile-danger`, `tile-warning`).
|
||||
- Label `count_top` uppercase dengan ikon jelas.
|
||||
- Nilai metrik `count` berbobot 800 dengan font monospaced tabular.
|
||||
- Sub-label `count_bottom` dengan indikator tren (`trend-up`, `trend-down`, `trend-warn`).
|
||||
- Menambahkan class kontainer `.x_panel` & `.x_title` dan soft badges (`.badge-soft-success`, `.badge-soft-danger`, dll).
|
||||
|
||||
### 3. Adopsi Desain uiverse.io (views/dashboard.ejs)
|
||||
- Menambahkan efek glassmorphism tingkat tinggi dengan border glow gradient dan shadow elevasi.
|
||||
- Menambahkan animasi tombol mikro-interaktif (`.btn-uiverse-glow`) dengan hover lift-up (`translateY(-2px)`) dan klik aktif responsif.
|
||||
|
||||
### 4. Standarisasi Google Fonts Universal
|
||||
- Mengintegrasikan **Inter** (antarmuka admin & tabel data), **Plus Jakarta Sans** (portal pelanggan & public landing), dan **JetBrains Mono** (data teknis IP, MAC, serial, dBm) melalui Google Fonts dengan koneksi teroptimasi (`preconnect` dan `display=swap`).
|
||||
|
||||
### File disentuh
|
||||
- public/css/admin.css
|
||||
- views/admin/dashboard.ejs
|
||||
- views/dashboard.ejs
|
||||
- update-code.md
|
||||
|
||||
### Rollback
|
||||
```bash
|
||||
cp /opt/billing-rtrw/public/css/admin.css.bak-gentelella /opt/billing-rtrw/public/css/admin.css
|
||||
cp /opt/billing-rtrw/views/admin/dashboard.ejs.bak-gentelella /opt/billing-rtrw/views/admin/dashboard.ejs
|
||||
cp /opt/billing-rtrw/views/dashboard.ejs.bak-uiverse /opt/billing-rtrw/views/dashboard.ejs
|
||||
sudo systemctl restart billing-rtrw.service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.5] — 2026-09-28 16:20 — Fix Telegram Polling Flood, Unifikasi Tipografi (Plus Jakarta Sans) & Harmonize Login UI
|
||||
|
||||
### 1. Perbaikan Telegram Bot Polling Error Flood (services/telegramBot.js)
|
||||
- Log server sebelumnya dibanjiri pesan error setiap 8 detik: error: Telegram Polling Error:.
|
||||
- Penyebab: Terdeteksi error code 409 Conflict dari API Telegram karena bot token yang sama masih aktif dijalankan di server lama (biznet/migrasi).
|
||||
- Solusi:
|
||||
1. Menambahkan penanganan error detail (error?.response?.body?.description || error.message) agar terbaca jelas di logger.
|
||||
2. Implementasi throttling logger (maksimal 1 log per 2 menit untuk error berulang yang sama).
|
||||
3. Memisahkan level warning khusus bila terjadi 409 Conflict, sehingga journalctl bersih tanpa spam error.
|
||||
|
||||
### 2. Unifikasi Tipografi & Harmonisasi Desain (views/login.ejs & views/isolated.ejs)
|
||||
- Menyelaraskan seluruh font pada portal publik & login pelanggan ke **Plus Jakarta Sans** (Google Fonts).
|
||||
- Menyesuaikan palette warna primer & hover pada halaman login (views/login.ejs) menggunakan tema Teal/Cyan (#0d9488, #14b8a6, #2dd4bf) agar selaras dengan dashboard pelanggan.
|
||||
- Menambahkan **Plus Jakarta Sans** pada halaman terisolir (views/isolated.ejs).
|
||||
|
||||
### File disentuh
|
||||
- services/telegramBot.js
|
||||
- views/login.ejs
|
||||
- views/isolated.ejs
|
||||
- update-code.md
|
||||
|
||||
### Rollback
|
||||
```bash
|
||||
cp /opt/billing-rtrw/services/telegramBot.js.bak-pollfix /opt/billing-rtrw/services/telegramBot.js
|
||||
cp /opt/billing-rtrw/views/login.ejs.bak-plusjakarta /opt/billing-rtrw/views/login.ejs
|
||||
cp /opt/billing-rtrw/views/isolated.ejs.bak-plusjakarta /opt/billing-rtrw/views/isolated.ejs
|
||||
sudo systemctl restart billing-rtrw.service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.4] — 2026-09-27 18:35 — Fix Theme Toggle (Dark/Light Mode), FOUC Prevention & Light Theme Styling
|
||||
|
||||
### Masalah
|
||||
- Tombol toggle tema (#theme-icon di header dashboard pelanggan) tidak berfungsi sama sekali saat diklik.
|
||||
- Penyebab:
|
||||
1. Logika Negasi Terbalik di toggleTheme(): Saat mode awal gelap, contains('light') bernilai false sehingga !false adalah true. Kode terus-menerus menyimpan dan menerapkan mode 'dark', tidak pernah beralih ke 'light'.
|
||||
2. Class Collision pada <html>: <html class='dark'> memiliki class 'dark' permanen. applyTheme lama hanya menambahkan class 'light' tanpa menghapus class 'dark'. Tailwind CSS (darkMode: 'class') tetap mengaktifkan semua style dark:... selama class 'dark' ada di <html>.
|
||||
3. FOUC saat reload: Tidak ada script sinkronisasi langsung di <head>, sehingga halaman sempat berkedip gelap sebelum DOMContentLoaded berjalan.
|
||||
4. Missing data-theme: Tidak mengeset atribut data-theme='light'/'dark' yang diperlukan oleh theme.css.
|
||||
|
||||
### Solusi & Perbaikan
|
||||
1. Perbaikan toggleTheme() & applyTheme(mode):
|
||||
- Menghapus class 'dark' dan menambahkan 'light' saat beralih ke mode terang, dan sebaliknya saat mode gelap.
|
||||
- Menetapkan atribut data-theme pada <html> secara konsisten.
|
||||
- Sinkronisasi kedua key penyimpanan (skyfiber-customer-theme & app-theme).
|
||||
- Mengubah ikon toggle secara dinamis: fa-sun text-amber-500 pada mode terang, fa-moon text-amber-400 pada mode gelap.
|
||||
2. Inline Script di <head>:
|
||||
- Menambahkan skrip sinkronisasi tema instan di dalam <head> sebelum browser merender body untuk mencegah kedip (FOUC).
|
||||
3. Penyempurnaan Styling Mode Terang (Light Mode):
|
||||
- Floating glass dock disesuaikan dengan latar frosted transparan putih dan sliding pill bergradasi lembut.
|
||||
- Penyesuaian kontras input form, teks judul, card modal, dan gradien banner (tagihan lunas, tagihan aktif, info router).
|
||||
|
||||
### File disentuh
|
||||
- views/dashboard.ejs
|
||||
- update-code.md
|
||||
|
||||
### Rollback
|
||||
```bash
|
||||
cp /opt/billing-rtrw/views/dashboard.ejs.bak-themefix-20260927183449 /opt/billing-rtrw/views/dashboard.ejs
|
||||
systemctl restart billing-rtrw.service
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.3] — 2026-09-27 18:30 — Fix PWA Auto-Login, Persistent Remember Token & Session Lifetime
|
||||
|
||||
### Masalah
|
||||
- Ketika PWA di-install di HP dan user login, saat aplikasi PWA di-kill atau ditutup, user terlempar ke halaman login lagi.
|
||||
- Penyebab utama:
|
||||
1. `start_url` di `manifest.webmanifest` diset ke `/customer/login?source=pwa`.
|
||||
2. Rute `/customer/login` dan `/` memeriksa `req.session.customer`, sementara rute login hanya menyimpan `req.session.phone` dan `req.session.pppoe_username` (`customer` selalu undefined), sehingga selalu merender form login ulang.
|
||||
3. Masa berlaku cookie sesi bawaan hanya 24 jam dan disimpan di RAM (MemoryStore) tanpa token persistensi native.
|
||||
4. Service worker (`public/sw.js`) melakukan fallback navigasi ke `/customer/login` saat jaringan lambat/offline.
|
||||
|
||||
### Solusi & Perbaikan
|
||||
1. **PWA Start URL:** Mengubah `start_url` di `/manifest.webmanifest` menjadi `/customer/dashboard`.
|
||||
2. **Persistent Auto-Login Token (90 Hari):**
|
||||
- Layanan baru `services/customerAuthTokenService.js` dengan tabel SQLite `customer_auth_tokens`.
|
||||
- Mengeluarkan cookie `customer_remember` (90 hari, httpOnly, secure, lax) saat login langsung maupun verifikasi OTP.
|
||||
- Middleware auto-login di `routes/customerPortal.js` yang secara otomatis memulihkan sesi pelanggan ketika aplikasi dibuka kembali.
|
||||
3. **Penyelarasan Guard Sesi:**
|
||||
- Rute `/customer/` dan `/customer/login` kini memeriksa `req.session.phone || req.session.customer` sehingga pengguna yang sudah login langsung dialihkan ke `/customer/dashboard`.
|
||||
- Objek `req.session.customer` kini diisi lengkap pada direct login dan OTP.
|
||||
4. **Masa Berlaku Cookie Sesi:** Ditingkatkan dari 24 jam menjadi 30 hari.
|
||||
5. **Logout Bersih:** Rute `/customer/logout` (mendukung POST dan GET) mencabut token dari database dan menghapus cookie `customer_remember` serta `customer.sid`.
|
||||
6. **Fallback Offline SW v6:** Navigasi offline dialihkan ke `/offline.html` dengan tombol "Coba Lagi", bukan ke halaman login.
|
||||
7. **Penyimpanan Lokal Form:** Menyimpan ID pelanggan terakhir di `localStorage` pada `views/login.ejs`.
|
||||
|
||||
### File disentuh
|
||||
- `services/customerAuthTokenService.js` (file baru)
|
||||
- `app-customer.js`
|
||||
- `routes/customerPortal.js`
|
||||
- `public/sw.js`
|
||||
- `views/login.ejs`
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.2] — 2026-09-27 18:15 — Multi-Vendor WiFi SSID & Password Changer (Huawei / Nokia / FiberHome / ZTE)
|
||||
|
||||
### Perbaikan Ganti SSID & Password WiFi Multi-Vendor
|
||||
- Mengatasi kegagalan ganti password TR-069 akibat penumpukan multiple parameter paths dalam satu payload SOAP atomik.
|
||||
- Menghindari pengiriman `PreSharedKey.1.PreSharedKey` (format hex 64 digit) bersamaan dengan `KeyPassphrase`, yang sebelumnya memicu SOAP fault 9007 (Invalid Parameter Value) di modem.
|
||||
- **Deteksi Vendor Otomatis (`HWTC`, `FiberHome`, `Nokia`, `ZTE`, `ZICG`, `CIOT`, `CDTC`):**
|
||||
- Huawei / FiberHome / Nokia: memprioritaskan `PreSharedKey.1.KeyPassphrase` (menghindari fault 9007 karena penolakan `KeyPassphrase` top-level).
|
||||
- ZTE / ZICG / CIOT: memprioritaskan `KeyPassphrase`.
|
||||
- **SSID 2.4G & 5G:** Mengirim path tunggal yang tepat untuk 2.4 GHz (`WLANConfiguration.1.SSID`) dan 5 GHz (`WLANConfiguration.5.SSID`) jika perangkat dual-band, tanpa menimpa interface guest/sekunder.
|
||||
- Fallback cerdas berbasis standar TR-098 per vendor.
|
||||
|
||||
### File disentuh
|
||||
- `services/customerDeviceService.js`
|
||||
|
||||
### Rollback
|
||||
```bash
|
||||
cp /opt/billing-rtrw/services/customerDeviceService.js.bak-wififix /opt/billing-rtrw/services/customerDeviceService.js
|
||||
sudo systemctl restart billing-rtrw
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.1] — 2026-09-27 16:30 — 5-Tab Floating Dock, PPOB Soon, Profil Tiket & WhatsApp Anti-Ban
|
||||
|
||||
### 1. Floating Dock 5-Tab (Dashboard Pelanggan)
|
||||
- Menghilangkan tombol tengah (+) Aksi Cepat dan popup modal sheet.
|
||||
- Menjadikan navbar 5 tombol langsung: Beranda, Router, PPOB (badge gradient "Soon"), Tagihan (ping unread), dan Profil.
|
||||
- Pusat Bantuan & Pembuatan Tiket Kendala diintegrasikan ke dalam Tab Profil (`tab-profile`) dengan tombol cepat ke form pelaporan dan WhatsApp CS.
|
||||
- Ditambahkan tombol "Kembali ke Profil" di halaman tiket bantuan (`tab-bantuan`).
|
||||
- Banner "Layanan PPOB Segera Hadir" pada tab PPOB (`tab-ppob`).
|
||||
|
||||
### 2. Sinkronisasi Upstream & Fix Anti-Ban WhatsApp
|
||||
- Verifikasi repository upstream `alijayanet/billing-rtrw`: base commit `ffd3c62` sudah mencakup seluruh perbaikan OLT Hioso (gap-fill DDM, Buffer UTF-8 decoding, slow SNMP session) dan timezone midnight hourCycle h23.
|
||||
- Menambahkan proteksi anti-ban WhatsApp pada alert monitoring (`services/whatsappBot.mjs`):
|
||||
- Mematuhi toggle `genieacs_monitoring_enabled` (tidak kirim alert jika toggle OFF).
|
||||
- Jeda acak (8-20 detik) antar penerima saat kirim broadcast alert ke admin/teknisi.
|
||||
- Menjadwalkan pemeriksaan monitoring harian pada jam 07:00 pagi (`config/genieacs.js`) lewat `node-cron` untuk mencegah spam alert berulang.
|
||||
|
||||
### File disentuh
|
||||
- `views/dashboard.ejs`
|
||||
- `services/whatsappBot.mjs`
|
||||
- `config/genieacs.js`
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.0] — 2026-09-26 22:55 — Floating Dock Navbar + CDN Pin + SW v5
|
||||
|
||||
Perubahan UI besar (navbar) sekaligus perbaikan regresi PWA/offline.
|
||||
|
||||
### Navbar: floating glass dock
|
||||
- Bottom nav lama (waterdrop, 6 tombol horizontal) diganti **floating glass
|
||||
dock** sesuai referensi `adaptive_glassmorphism_floating_dock.html`.
|
||||
- Struktur: 4 tombol langsung (**Beranda, Router, Tagihan, Profil**) + 1 tombol
|
||||
tengah `+` yang membuka **quick-action sheet** berisi
|
||||
**PPOB, Bantuan, Reconnect, Poll ONU**.
|
||||
PPOB & Bantuan sengaja dipindah ke sheet agar dock tidak terlalu padat.
|
||||
- ** keenam tab tetap bisa diakses**, `id="tab-*"` tidak berubah:
|
||||
`tab-beranda`, `tab-router`, `tab-ppob`, `tab-tagihan`, `tab-bantuan`,
|
||||
`tab-profile`.
|
||||
- CSS baru: `.dock-glass`, `.dock-pill` (sliding pill), `.dock-item`,
|
||||
`.dock-icon`, quick-sheet overlay. `safe-area-inset-bottom` untuk iPhone.
|
||||
- JS baru: `updatePillPosition()`, `toggleQuickSheet()`, `openQuickSheet()`,
|
||||
`closeQuickSheet()`, state `currentTab`, `isSheetOpen`.
|
||||
`switchTab()` sekarang menandai `.dock-item` aktif dan memindahkan pill.
|
||||
- Support keyboard: angka `1`–`6` pindah tab, `Escape` menutup sheet.
|
||||
- Ikon memakai **Lucide** (`data-lucide`) + `lucide.createIcons()`.
|
||||
- CSS `.waterdrop-active` yang tidak terpakai lagi dibuang.
|
||||
|
||||
### Perbaikan regresi offline/PWA
|
||||
- **Semua CDN dashboard kini di-pin ke versi tetap** (sebelumnya `@latest` /
|
||||
tanpa versi → bisa berubah atau break tanpa notice):
|
||||
|
||||
||Aset|URL|
|
||||
|---|---|
|
||||
|Lucide|`https://unpkg.com/lucide@1.48.0/dist/umd/lucide.min.js`|
|
||||
|Chart.js|`https://cdn.jsdelivr.net/npm/chart.js@4.5.1/dist/chart.umd.min.js`|
|
||||
|Tailwind|`https://cdn.tailwindcss.com/3.4.17`|
|
||||
|
||||
`https://unpkg.com/lucide@latest` dan `https://cdn.tailwindcss.com`
|
||||
(tanpa versi) sama-sama **redirect 302** tiap load → di-pin ke URL final.
|
||||
- **Bug:** service worker hanya cache-first `cdn.jsdelivr.net`, sehingga
|
||||
Tailwind, FontAwesome, Google Fonts, dan Lucide **tidak tersedia saat
|
||||
offline** — layout PWA hancur. Sekarang `sw.js` punya array `CDN_HOSTS`:
|
||||
`cdn.jsdelivr.net`, `unpkg.com`, `cdnjs.cloudflare.com`,
|
||||
`cdn.tailwindcss.com`, `fonts.googleapis.com`, `fonts.gstatic.com`.
|
||||
- Cache bust: `CACHE_NAME` / `CDN_CACHE` `customer-pwa-v4` → **`-v5`**, plus
|
||||
5 URL baru masuk `PRECACHE_URLS`.
|
||||
|
||||
### File disentuh
|
||||
- `views/dashboard.ejs` (1481 baris)
|
||||
- `public/sw.js` (173 baris)
|
||||
|
||||
### Verifikasi
|
||||
- Render EJS OK, `div` seimbang `160/160`.
|
||||
- Live `/customer/dashboard` HTTP 200, `div` seimbang `184/184`.
|
||||
- Semua CDN membalas 200 tanpa redirect.
|
||||
- `systemctl is-active billing-rtrw` = `active`, journal tanpa error.
|
||||
|
||||
### Rollback
|
||||
```bash
|
||||
cd /opt/billing-rtrw
|
||||
cp views/dashboard.ejs.bak-twpin-225510 views/dashboard.ejs
|
||||
cp public/sw.js.bak-twpin-225510 public/sw.js
|
||||
sudo systemctl restart billing-rtrw
|
||||
```
|
||||
|
||||
### Catatan
|
||||
- Tampilan visual (sliding pill, animasi sheet, posisi icon) **belum dicek di
|
||||
browser sungguhan** — verifikasi di atas semuanya server-side.
|
||||
|
||||
---
|
||||
|
||||
## [v1.8.2] — 2026-09-26 22:30 — Fix flash message toast
|
||||
|
||||
- `notif` dari server sebelumnya tidak tampil. Kini di-render sebagai variabel
|
||||
`FLASH` (`{ text, type }`) lalu dimunculkan lewat `#toast-container`.
|
||||
- Pemetaan tipe: `danger → error`, `warning → info`, selain itu `info`.
|
||||
- Render diproteksi dari injeksi `</script>` via `.replace(/</g, '\u003c')`.
|
||||
|
||||
### File disentuh
|
||||
- `views/dashboard.ejs`
|
||||
|
||||
### Rollback
|
||||
```bash
|
||||
cp views/dashboard.ejs.bak-flashfix-20260926221014 views/dashboard.ejs
|
||||
sudo systemctl restart billing-rtrw
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [v1.8.1] — 2026-09-26 22:10 — Fix route `/customer/reboot` + ACS Connection Request 401
|
||||
|
||||
### 1. Route reboot
|
||||
- `POST /customer/reboot` gagal auth karena hanya memakai `session.phone`
|
||||
sebagai token device.
|
||||
- Kini memakai `buildCustomerDeviceTokens()` sehingga mencoba seluruh kandidat
|
||||
token (tag, username, serial, MAC, IP).
|
||||
|
||||
### 2. Connection Request 401 (fix kritis)
|
||||
- ACS membalas **HTTP 401** saat perangkat menjalankan Connection Request karena
|
||||
`ConnectionRequestUsername` / `ConnectionRequestPassword` belum diset.
|
||||
- Diperbaiki dengan `provisionConnectionRequestAuth(deviceId)` di
|
||||
`services/acsServerService.js`, dipanggil dari `handleInform()`.
|
||||
- Alur: Basic challenge → 401 → **Digest auth → HTTP 200**.
|
||||
- Default username `skyfiber`, password = `crypto.randomBytes(12)` hex per
|
||||
perangkat, disimpan di database.
|
||||
- Berlaku hanya untuk perangkat yang sedang online; 3 perangkat offline akan
|
||||
ter-provisioning otomatis saat melakukan Inform.
|
||||
|
||||
### Hasil
|
||||
- 102/105 perangkat sukses di-provisioning, 0 provisioning task gagal.
|
||||
- Perubahan SSID/reboot kini berlaku ~10 detik (sebelumnya gagal).
|
||||
- 3 perangkat offline akan ter-provisioning otomatis saat Inform berikutnya.
|
||||
|
||||
### File disentuh
|
||||
- `routes/customerPortal.js` (3967 baris)
|
||||
- `services/acsServerService.js` (1711 baris)
|
||||
|
||||
### Rollback
|
||||
```bash
|
||||
cd /opt/billing-rtrw
|
||||
cp routes/customerPortal.js.bak-rebootfix-20260926221014 routes/customerPortal.js
|
||||
cp services/acsServerService.js.bak-crfix-20260926221014 services/acsServerService.js
|
||||
sudo systemctl restart billing-rtrw
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [v1.8.0] — 2026-09-26 18:04–21:07 — Redesign dashboard (APPS-style)
|
||||
|
||||
Redesain tampilan dashboard pelanggan dengan gaya APPS.
|
||||
|
||||
### Yang ditambahkan
|
||||
- **Banner promo** dari database (`GET /customer/api/promo-slides`).
|
||||
- **Chart pemakaian isolate-day** (maks. 3 bulan riwayat), menggantikan
|
||||
polling traffic realtime yang sebelumnya membebani server.
|
||||
Helper: `buildUsageSummary(profile, activePackage)`.
|
||||
- **LibreSpeed** speedtest inline (sub-router `section-speedtest`).
|
||||
- **Quick-action Reconnect** (`POST /customer/customer/reconnect`) dan
|
||||
**Poll ONU** (`POST /customer/api/device/refresh`).
|
||||
- Sub-tab router: **WiFi**, **Speedtest**, **Pemakaian**.
|
||||
- Tampilan AXS/PPOB (`/customer/ppob`, `/customer/topup`).
|
||||
- Link WhatsApp admin otomatis ke nomor pertama di `settings.whatsapp_admin_numbers`.
|
||||
- Tema persisten via `localStorage`.
|
||||
- Tombol toggle lihat IP (`isIpVisible`).
|
||||
|
||||
### Perbaikan
|
||||
- Semua endpoint palsu `/api/customer/*` diarahkan ke route `/customer/*` asli.
|
||||
- Badge **GPON**: merah `Bad GPON` bila ONU tidak terdeteksi **atau** PPPoE offline.
|
||||
- Form WiFi/ONT otomatis disabled bila ONU absent, dengan pesan penjelasan.
|
||||
- Variabel yang dihitung defensif di ketiga cabang render:
|
||||
`hasUnpaid`, `latestUnpaid`, `latestInvoice`, `pppoeIP`, `pppoeUptime`,
|
||||
`stMaxMbps`.
|
||||
- Penghapusan modal Bootstrap yang rusak.
|
||||
- Layout mobile dirapikan.
|
||||
|
||||
### File disentuh
|
||||
- `views/dashboard.ejs`
|
||||
|
||||
### Rollback
|
||||
```bash
|
||||
cp views/dashboard.ejs.bak-prefn-20260926210734 views/dashboard.ejs
|
||||
# atau ke markup/endpoint stabil terakhir:
|
||||
cp views/dashboard.ejs.bak-pre-newdesign-20260926181219 views/dashboard.ejs
|
||||
sudo systemctl restart billing-rtrw
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## [v1.7.0] — 2026-09-26 12:55–14:24 — Self-registration, tabular layout, PWA
|
||||
|
||||
- **Self-registration** (`/customer/register`) + pairing tag perangkat
|
||||
(`POST /customer/change-tag`).
|
||||
- Tata letak tabel Payments/Fitur dikembalikan ke mode tabular
|
||||
(`.bak-tabrapikan`, `.bak-tabcss`).
|
||||
- Toggle lihat/sembunyikan password WiFi.
|
||||
- Tampilan jatuh tempo (due) tagihan.
|
||||
- Struktur `div` diperbaiki (tag tidak seimbang) — `.bak-divfix`.
|
||||
- Navbar sticky di mobile — `.bak-sticky`.
|
||||
- **Service worker** diperbaiki & di-cache ulang — `.bak-swfix`, `sw3`.
|
||||
- **`rxPower` ACS** ditangani di `services/acsServerService.js`.
|
||||
|
||||
### File disentuh
|
||||
- `views/dashboard.ejs`, `routes/customerPortal.js`, `public/sw.js`,
|
||||
`services/acsServerService.js`
|
||||
|
||||
### Rollback
|
||||
```bash
|
||||
cd /opt/billing-rtrw
|
||||
cp views/dashboard.ejs.bak-divfix-20260926142250 views/dashboard.ejs
|
||||
cp public/sw.js.bak-swfix-20260926142327 public/sw.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Daftar endpoint yang dipakai dashboard
|
||||
|
||||
Semua Calls dari `views/dashboard.ejs` (sudah diverifikasi terhadap router):
|
||||
|
||||
| Aksi | Method | Endpoint |
|
||||
|---|---|---|
|
||||
| Promo slides | GET | `/customer/api/promo-slides` |
|
||||
| Poll ONU / refresh | POST | `/customer/api/device/refresh` |
|
||||
| Reconnect | POST | `/customer/customer/reconnect` |
|
||||
| Ganti SSID | POST | `/customer/change-ssid` |
|
||||
| Ganti password | POST | `/customer/change-password` |
|
||||
| Reboot | POST | `/customer/reboot` |
|
||||
| Buat tiket (+foto) | POST | `/customer/tickets/create` (multipart) |
|
||||
| Bayar invoice | GET | `/customer/payment/create/:id` |
|
||||
| PPOB | GET | `/customer/ppob` |
|
||||
| Top up | GET | `/customer/topup` |
|
||||
| Logout | GET | `/customer/logout` |
|
||||
|
||||
> Catatan: **tidak ada** route `/customer/poll-onu`. Tombol "Poll ONU"
|
||||
> memanggil `POST /customer/api/device/refresh`.
|
||||
|
||||
---
|
||||
|
||||
## TODO / tindak lanjut
|
||||
|
||||
Detail lengkap ada di `/home/oxpoe/Documents/CATATAN-OPS-SKYFIBER.txt`
|
||||
section 11 (mode `600`, jangan dibagikan).
|
||||
|
||||
### Keamanan — perlu tindakan
|
||||
- [ ] Rotasi `telegram_bot_token` (via `@BotFather` → `/revoke`; token **baru**
|
||||
jangan dikirim ke chat mana pun).
|
||||
- [ ] Rotasi `admin_password`.
|
||||
- [ ] Rotasi `admin_api_key`.
|
||||
- [ ] Amankan backup `backups/settings_20260926_211113.json` (masih memuat
|
||||
token lama) atau hapus setelah rotasi.
|
||||
- [ ] Inisialisasi Git (`git init` + commit awal) supaya changelog berikutnya
|
||||
punya riwayat & diff.
|
||||
|
||||
### Fungsional
|
||||
- [ ] **Password WiFi pelanggan uji masih nilai sementara** dan harus
|
||||
dikembalikan ke nilai asli (nilai uji tercatat di notes section 11).
|
||||
- [ ] 3 perangkat belum punya CR credentials, akan provisioning otomatis saat
|
||||
online:
|
||||
- `001141-CL601-ZTEGCBBFB774`
|
||||
- `B4E46B-SIGMA Z99LX XPON-ZICG27766207`
|
||||
- `B4E46B-SIGMA Z99LX XPON-ZICG27766F6A`
|
||||
- [ ] Alur pembayaran belum diuji end-to-end (butuh invoice uji).
|
||||
- [ ] Cek visual floating dock di browser/HP sungguhan.
|
||||
- [ ] Test PWA mode offline (SW v5) — belum pernah diuji.
|
||||
- [ ] Keputusan: apakah FUP enforcement mengikuti isolate-day.
|
||||
- [ ] Parity peta admin desktop vs mobile.
|
||||
- [ ] Pemetaan kecepatan WiFi 2.4GHz vs 5GHz.
|
||||
|
||||
---
|
||||
|
||||
## Cara rollback umum
|
||||
|
||||
```bash
|
||||
cd /opt/billing-rtrw
|
||||
sudo systemctl stop billing-rtrw
|
||||
# salin backup yang dikehendali ke file aslinya
|
||||
sudo systemctl start billing-rtrw
|
||||
sudo journalctl -u billing-rtrw -n 50 --no-pager
|
||||
```
|
||||
|
||||
Backup tersedia (format `.bak-<label>-<timestamp>`):
|
||||
|
||||
| File | Backup |
|
||||
|---|---|
|
||||
| `views/dashboard.ejs` | `.bak-rxpower`, `.bak-selfapp-*`, `.bak-akun-*`, `.bak-due-*`, `.bak-tabcss-*`, `.bak-usage-ui-*`, `.bak-togglepass-*`, `.bak-tabrapikan-*`, `.bak-mobile-*`, `.bak-usageview-*`, `.bak-usagehelper-*`, `.bak-tailadmin-*`, `.bak-divfix-*`, `.bak-sticky-*`, `.bak-prefn-*`, `.bak-pre-newdesign-*`, `.bak-flashfix-*`, `.bak-dock-*`, `.bak-cdnpin-*`, `.bak-twpin-*` |
|
||||
| `routes/customerPortal.js` | `.bak-selfapp-*`, `.bak-usageview-*`, `.bak-usagehelper-*`, `.bak-akun-*`, `.bak-rebootfix-*` |
|
||||
| `services/acsServerService.js` | `.bak-rxpower-*`, `.bak-crfix-*` |
|
||||
| `public/sw.js` | `.bak-selfapp-*`, `.bak-sw3-*`, `.bak-swfix-*`, `.bak-v4-*`, `.bak-twpin-*` |
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.8] - 2026-09-28: Rombak Total UI/UX Multi-Portal (5-Color Modern Design System)
|
||||
|
||||
### Ringkasan Rilis
|
||||
Rombak visual dan interaksi total pada seluruh portal aplikasi (Landing Page/Pelanggan, Customer PWA Dashboard, Admin Portal, Teknisi, Kolektor, dan Agen) menggunakan palette 5-warna konsisten, tipografi Google Fonts universal, elemen cyber-glassmorphism terinspirasi dari uiverse.io, dan panel KPI dense terinspirasi dari Gentelella.
|
||||
|
||||
### Design System & Palette
|
||||
- **Dark 1 (Latar Utama)**: `#161A21`
|
||||
- **Dark 2 (Gelap Sedang / Surfaces & Cards)**: `#1F2420`
|
||||
- **Dark 3 (Abu-abu Gelap / Aksesori & Borders)**: `#2A313C`
|
||||
- **Accent Lime (Aksen Hijau Neon)**: `#ADFF2F`
|
||||
- **Light (Putih Terang / Teks)**: `#F2F5F1`
|
||||
- **Typography**:
|
||||
- `Plus Jakarta Sans`: Landing, PWA Pelanggan, Heading & Kartu
|
||||
- `Inter`: Admin tabel, form controls, dense grid
|
||||
- `JetBrains Mono`: Angka teknis (IP PPPoE, MAC, Serial, RX/TX Optical Power, Speed, Bandwidth)
|
||||
|
||||
### Modul yang Dirombak
|
||||
1. **Landing Page & Customer Login (`views/login.ejs`)**:
|
||||
- Cyber-glassmorphism card hero, paket internet (`#packages`), fitur (`#features`), layanan (`#services`), contact.
|
||||
- PWA Install Modal dengan status online/offline otomatis.
|
||||
2. **Halaman Isolir / Walled Garden (`views/isolated.ejs`)**:
|
||||
- Palette `#161A21`, `#1F2420`, `#2A313C`, dan aksen neon `#ADFF2F`.
|
||||
3. **Customer PWA Dashboard (`views/dashboard.ejs` & `views/partials/customer_bottom_nav.ejs`)**:
|
||||
- Floating Glass Dock 5-Tab (`#1F2420` glass) dengan sliding active pill neon lime (`#ADFF2F`).
|
||||
- Kartu status PPPoE/ONU dengan indikator neon pulse, telemetry optik, ganti SSID/password, speedtest gauge, dan Chart.js riwayat pemakaian.
|
||||
4. **Admin Portal (`public/css/admin.css` & `views/admin/*`)**:
|
||||
- Update CSS variables: `--bg: #161A21`, `--bg2: #1F2420`, `--bg3: #2A313C`, `--primary: #ADFF2F`, `--text: #F2F5F1`.
|
||||
- Sidebar Gentelella modern dengan indikator aktif neon lime.
|
||||
- KPI Tile Stats (`.tile_stats_count`) pada Dashboard, Customers, dan Billing.
|
||||
- Perbaikan dark theme pada `views/admin/olts.ejs`, `monitoring.ejs`, `settings.ejs`, dan `login.ejs`.
|
||||
5. **Technician Portal (`views/tech/*`)**:
|
||||
- `views/tech/partials/header.ejs`, `views/tech/partials/bottom_nav.ejs`, `views/tech/dashboard.ejs`, `monitoring.ejs`, `attendance.ejs`, `map.ejs`, dan `login.ejs`.
|
||||
6. **Collector & Agent Portals (`views/collector/*`, `views/agent/*`, `public/css/theme.css`)**:
|
||||
- `public/css/theme.css` sebagai global theme provider untuk tema gelap 5-warna.
|
||||
- Modernisasi login dan dashboard collector & agent.
|
||||
|
||||
### Verifikasi & Integritas
|
||||
- 88 dari 88 template EJS aktif berhasil dikompilasi dengan `ejs.compile` (0 error).
|
||||
- Seluruh endpoint HTTP menghasilkan kode `200 OK` / valid redirect.
|
||||
- Database SQLite integrity check: `ok`.
|
||||
- Service `billing-rtrw.service` berjalan stabil di Systemd VPS Deneva.
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.9] - 2026-09-28: Penghapusan Total Komponen Light Mode (Konsistensi Eksklusif Halogen Kit)
|
||||
|
||||
### Ringkasan Perubahan
|
||||
Menghapus seluruh komponen *light mode*, tombol pengalih tema (*theme toggle button / FAB*), script peralihan mode terang, dan aturan CSS override di semua portal aplikasi. Seluruh aplikasi kini konsisten 100% berjalan dalam **Mode Gelap Halogen Kit**:
|
||||
1. **`public/js/theme.js`**: Menghapus fungsi injeksi tombol floating tema (`injectThemeFab`), fungsi `toggleAppTheme`, dan memaksa `data-theme=dark` di localStorage secara permanen.
|
||||
2. **`public/css/theme.css`**: Menghapus seluruh blok aturan `html[data-theme=light]` dan styling tombol mengambang `.theme-fab`.
|
||||
3. **Admin Sidebar (`views/admin/partials/sidebar.ejs`)`**: Menghapus tombol toggle tema pada bagian brand/header sidebar dan menyederhanakan inisialisasi dark mode.
|
||||
4. **Customer PWA Dashboard (`views/dashboard.ejs`)`**: Menghapus tombol pengalih tema (`#theme-toggle-btn`) pada header topbar, menghapus fungsi `toggleTheme()` / `applyTheme()`, dan membersihkan seluruh override class CSS `html.light`.
|
||||
5. **Verifikasi**: Seluruh 88 template EJS tervalidasi 0 error dan service berjalan stabil.
|
||||
|
||||
---
|
||||
|
||||
## [v1.9.9] - 2026-09-28: Penghapusan Total Komponen Light Mode (Konsistensi Eksklusif Halogen Kit)
|
||||
|
||||
### Ringkasan Perubahan
|
||||
Menghapus seluruh komponen *light mode*, tombol pengalih tema (*theme toggle button / FAB*), script peralihan mode terang, dan aturan CSS override `html.light` di semua portal aplikasi. Seluruh aplikasi kini konsisten 100% berjalan dalam **Mode Gelap Halogen Kit**:
|
||||
1. **`public/js/theme.js`**: Menghapus fungsi injeksi tombol floating tema (`injectThemeFab`), fungsi `toggleAppTheme`, dan memaksa `data-theme="dark"` di localStorage secara permanen.
|
||||
2. **`public/css/theme.css`**: Menghapus seluruh blok aturan `html[data-theme="light"]` dan styling tombol mengambang `.theme-fab`.
|
||||
3. **Admin Sidebar (`views/admin/partials/sidebar.ejs`)**: Menghapus tombol toggle tema pada bagian brand/header sidebar dan menyederhanakan inisialisasi dark mode.
|
||||
4. **Customer PWA Dashboard (`views/dashboard.ejs`)**: Menghapus tombol pengalih tema (`#theme-toggle-btn`) pada header topbar, menghapus fungsi `toggleTheme()` / `applyTheme()`, dan membersihkan seluruh override class CSS `html.light`.
|
||||
5. **Verifikasi**: Seluruh 88 template EJS tervalidasi 0 error dan service berjalan stabil.
|
||||
@@ -14,7 +14,7 @@ let TARGET = process.env.ISOLIR_REDIRECT_URL || '';
|
||||
|
||||
// Coba ambil target dari settings.json bila tersedia
|
||||
try {
|
||||
const { getSetting } = require('/opt/billing-rtrw/config/settingsManager');
|
||||
const { getSetting } = require('./config/settingsManager');
|
||||
const s = getSetting('isolir_portal_url', '') || '';
|
||||
if (s) TARGET = s;
|
||||
} catch (_) { /* abaikan */ }
|
||||
@@ -37,6 +37,8 @@ const server = http.createServer((req, res) => {
|
||||
);
|
||||
});
|
||||
|
||||
server.on('error', (e) => console.error('[isolir-redirect] listen error:', e.message));
|
||||
|
||||
server.listen(PORT, '0.0.0.0', () => {
|
||||
console.log(`[isolir-redirect] listening on 0.0.0.0:${PORT} -> ${TARGET}`);
|
||||
});
|
||||
|
||||
+2
-1
@@ -1052,6 +1052,7 @@ router.get('/', async (req, res) => {
|
||||
model: d._deviceId?._ProductClass || '-',
|
||||
rx_power: rxPower,
|
||||
pppoe_ip: ip,
|
||||
ip: ip,
|
||||
pppoe_user: pppoeUser,
|
||||
customer_name: customerName,
|
||||
acs_server_id: server.id,
|
||||
@@ -1317,7 +1318,7 @@ router.put('/api/servers/legacy', requireAdmin, express.json(), async (req, res)
|
||||
if (clearPassword) currentSettings.genieacs_password = '';
|
||||
else if (String(passwordInput || '').trim()) currentSettings.genieacs_password = String(passwordInput || '');
|
||||
|
||||
const settingsPath = path.join(__dirname, '../settings.json');
|
||||
const settingsPath = require('../config/paths').settingsPath;
|
||||
fs.writeFileSync(settingsPath, JSON.stringify(currentSettings, null, 2), 'utf8');
|
||||
|
||||
res.json({ success: true, message: 'Default ACS berhasil diperbarui.' });
|
||||
|
||||
+11
-5
@@ -384,7 +384,9 @@ function getUpdateInfo(repoRoot) {
|
||||
|
||||
const inside = runCmd('git', ['rev-parse', '--is-inside-work-tree'], repoRoot);
|
||||
if (!inside.ok) {
|
||||
info.error = 'Folder ini belum menjadi git repository.';
|
||||
// Tanpa .git (Docker/container): update dilakukan via rebuild image (bukan git)
|
||||
info.container = true;
|
||||
info.containerMessage = 'Instalasi ini berjalan sebagai kontainer/gambar (tanpa .git). Pembaruan dilakukan dengan membangun ulang image dari repo terbaru.';
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -4554,6 +4556,10 @@ router.get('/update', requireAdminSession, requireSidebarMenuAccess('update'), r
|
||||
|
||||
router.post('/update/run', requireAdminSession, restrictToAdmin, (req, res) => {
|
||||
const repoRoot = path.resolve(__dirname, '..');
|
||||
if (!runCmd('git', ['rev-parse', '--is-inside-work-tree'], repoRoot).ok) {
|
||||
req.session._msg = { type: 'error', text: 'Instalasi kontainer (tanpa .git): pembaruan dilakukan dengan rebuild image, bukan lewat git.' };
|
||||
return res.redirect('/admin/update');
|
||||
}
|
||||
const log = [];
|
||||
const pushCmd = (label, r) => {
|
||||
log.push(`$ ${label}`.trim());
|
||||
@@ -5234,7 +5240,7 @@ router.post('/api/genieacs/settings', requireAdmin, async (req, res) => {
|
||||
// Save to file
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const settingsPath = path.join(__dirname, '../settings.json');
|
||||
const settingsPath = require('../config/paths').settingsPath;
|
||||
fs.writeFileSync(settingsPath, JSON.stringify(currentSettings, null, 2), 'utf8');
|
||||
|
||||
// Log audit
|
||||
@@ -6496,7 +6502,7 @@ router.get('/whatsapp/gowa/qr.png', requireAdminSession, async (req, res) => {
|
||||
});
|
||||
|
||||
router.get('/whatsapp', requireAdminSession, requireSidebarMenuAccess('whatsapp'), async (req, res) => {
|
||||
const waGatewayType = getSetting('wa_gateway_type', 'baileys');
|
||||
const waGatewayType = getSetting('wa_gateway_type', 'gowa');
|
||||
const metaSettings = {
|
||||
meta_phone_number_id: getSetting('meta_phone_number_id', ''),
|
||||
meta_waba_id: getSetting('meta_waba_id', ''),
|
||||
@@ -6611,7 +6617,7 @@ router.post('/whatsapp/test-fonnte', requireAdminSession, express.json(), async
|
||||
|
||||
// LIVE CHAT ROUTES
|
||||
router.get('/whatsapp/live-chat', requireAdminSession, requireSidebarMenuAccess('whatsapp'), async (req, res) => {
|
||||
const waGatewayType = getSetting('wa_gateway_type', 'baileys');
|
||||
const waGatewayType = getSetting('wa_gateway_type', 'gowa');
|
||||
const customers = customerSvc.getAllCustomers();
|
||||
res.render('admin/whatsapp_live_chat', {
|
||||
title: 'Live Chat WhatsApp',
|
||||
@@ -7075,7 +7081,7 @@ router.post('/whatsapp/run-reminder-now', requireAdminSession, async (req, res)
|
||||
|
||||
router.get('/api/whatsapp/status', requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const gatewayType = getSetting('wa_gateway_type', 'baileys');
|
||||
const gatewayType = getSetting('wa_gateway_type', 'gowa');
|
||||
if (gatewayType === 'fonnte') {
|
||||
const token = getSetting('fonnte_token', '');
|
||||
res.json({
|
||||
|
||||
@@ -2088,7 +2088,7 @@ router.get('/app/admin/olts', requireAdminApiAuth, (req, res) => {
|
||||
// ─── ADMIN NATIVE: WHATSAPP STATUS ───────────────────────────────────────────
|
||||
router.get('/app/admin/whatsapp/status', requireAdminApiAuth, async (req, res) => {
|
||||
try {
|
||||
const gwType = String(require('../config/settingsManager').getSetting('wa_gateway_type', 'baileys') || 'baileys').toLowerCase();
|
||||
const gwType = String(require('../config/settingsManager').getSetting('wa_gateway_type', 'gowa') || 'baileys').toLowerCase();
|
||||
let isConnected = false;
|
||||
let waNumber = '-';
|
||||
let waName = gwType.toUpperCase();
|
||||
@@ -2190,7 +2190,7 @@ router.post('/app/admin/settings/update', requireAdminApiAuth, (req, res) => {
|
||||
try {
|
||||
const allowed = ['company_header', 'company_subheader', 'company_phone', 'company_address', 'timezone', 'qris_enabled', 'auto_isolate_enabled', 'webhook_secret'];
|
||||
const updates = req.body || {};
|
||||
const settingsPath = require('path').join(__dirname, '../settings.json');
|
||||
const settingsPath = require('../config/paths').settingsPath;
|
||||
const current = JSON.parse(require('fs').readFileSync(settingsPath, 'utf8'));
|
||||
let changed = 0;
|
||||
for (const key of allowed) {
|
||||
|
||||
@@ -11,7 +11,7 @@ const { getSetting, getCurrentDateInTimezone, getNowLocalISO } = require('../con
|
||||
const projectRoot = path.join(__dirname, '..');
|
||||
const backupDir = path.join(projectRoot, 'backups');
|
||||
const dbPath = path.join(projectRoot, 'database', 'billing.db');
|
||||
const settingsPath = path.join(projectRoot, 'settings.json');
|
||||
const { settingsPath } = require('../config/paths');
|
||||
|
||||
// Pastikan direktori backup ada
|
||||
if (!fs.existsSync(backupDir)) {
|
||||
|
||||
@@ -62,7 +62,7 @@ async function runAutoBillingReminder() {
|
||||
const billingEnabled = getSetting('whatsapp_billing_to_customer_enabled', true);
|
||||
if (!enabled || !waEnabled || !billingEnabled) return { target: 0, sent: 0, failed: 0, skipped: 'disabled' };
|
||||
|
||||
const gatewayType = getSetting('wa_gateway_type', 'baileys');
|
||||
const gatewayType = getSetting('wa_gateway_type', 'gowa');
|
||||
const waSvc = require('./whatsappService');
|
||||
|
||||
if (gatewayType === 'meta') {
|
||||
|
||||
@@ -17,6 +17,7 @@ async function checkDependencies() {
|
||||
mikrotik: [],
|
||||
genieacs: { status: 'unknown', message: '' },
|
||||
whatsapp: { status: 'unknown', message: '' },
|
||||
telegram: { status: 'unknown', message: '' },
|
||||
paymentGateways: [],
|
||||
timestamp: new Date().toISOString()
|
||||
};
|
||||
@@ -104,39 +105,54 @@ async function checkDependencies() {
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Check WhatsApp Gateway (using whatsappStatus from whatsappBot)
|
||||
// 3. Check WhatsApp Gateway (GoWA / Baileys sesuai setting wa_gateway_type)
|
||||
try {
|
||||
// Import whatsappStatus dynamically to get real-time status
|
||||
const whatsappBotModule = await import('./whatsappBot.mjs');
|
||||
const waStatus = whatsappBotModule.whatsappStatus;
|
||||
|
||||
// Check connection status
|
||||
// Possible values: 'connecting', 'qr', 'open', 'loggedOut', 'close'
|
||||
const isOnline = waStatus.connection === 'open';
|
||||
const isQR = waStatus.connection === 'qr';
|
||||
const isLoggedOut = waStatus.connection === 'loggedOut';
|
||||
|
||||
let message = 'WhatsApp is disconnected';
|
||||
if (isOnline) {
|
||||
const phone = waStatus.user?.id ? String(waStatus.user.id).split(':')[0] : 'Unknown';
|
||||
message = `WhatsApp connected (${phone})`;
|
||||
} else if (isQR) {
|
||||
message = 'WhatsApp waiting for QR scan';
|
||||
} else if (isLoggedOut) {
|
||||
message = 'WhatsApp logged out - scan QR again';
|
||||
const { getSetting } = require('../config/settingsManager');
|
||||
const gw = String(getSetting('wa_gateway_type', 'gowa') || 'baileys').toLowerCase();
|
||||
if (gw === 'gowa') {
|
||||
const base = String(getSetting('gowa_base_url', 'http://127.0.0.1:3000') || '').replace(/\/+$/, '');
|
||||
const u = getSetting('gowa_username', '');
|
||||
const p = getSetting('gowa_password', '');
|
||||
const cfg = { timeout: 6000, validateStatus: (s) => s < 500 };
|
||||
if (u && p) cfg.auth = { username: u, password: p };
|
||||
const resp = await axios.get(`${base}/app/status`, cfg);
|
||||
const res = (resp.data && resp.data.results) || {};
|
||||
const isOnline = !!res.is_connected;
|
||||
results.whatsapp = {
|
||||
status: isOnline ? 'online' : 'offline',
|
||||
message: isOnline ? `GoWA connected (${res.jid || res.device_id || '-'})` : 'GoWA tidak terhubung'
|
||||
};
|
||||
} else {
|
||||
message = `WhatsApp ${waStatus.connection || 'disconnected'}`;
|
||||
const whatsappBotModule = await import('./whatsappBot.mjs');
|
||||
const waStatus = whatsappBotModule.whatsappStatus;
|
||||
const isOnline = waStatus && waStatus.connection === 'open';
|
||||
const isQR = waStatus && waStatus.connection === 'qr';
|
||||
const isLoggedOut = waStatus && waStatus.connection === 'loggedOut';
|
||||
let message = 'WhatsApp is disconnected';
|
||||
if (isOnline) { const phone = waStatus.user && waStatus.user.id ? String(waStatus.user.id).split(':')[0] : 'Unknown'; message = `WhatsApp connected (${phone})`; }
|
||||
else if (isQR) message = 'WhatsApp waiting for QR scan';
|
||||
else if (isLoggedOut) message = 'WhatsApp logged out - scan QR again';
|
||||
else message = `WhatsApp ${(waStatus && waStatus.connection) || 'disconnected'}`;
|
||||
results.whatsapp = { status: isOnline ? 'online' : 'offline', message };
|
||||
}
|
||||
|
||||
results.whatsapp = {
|
||||
status: isOnline ? 'online' : 'offline',
|
||||
message: message
|
||||
};
|
||||
} catch (err) {
|
||||
results.whatsapp = {
|
||||
status: 'offline',
|
||||
message: `WhatsApp check error: ${err.message}`
|
||||
};
|
||||
results.whatsapp = { status: 'offline', message: `WhatsApp check error: ${err.message}` };
|
||||
}
|
||||
|
||||
// 4. Check Telegram Bot
|
||||
try {
|
||||
const { getSetting } = require('../config/settingsManager');
|
||||
const enabled = !!getSetting('telegram_enabled', false);
|
||||
const token = String(getSetting('telegram_bot_token', '') || '').trim();
|
||||
if (!enabled || !token) {
|
||||
results.telegram = { status: 'offline', message: !enabled ? 'Telegram dinonaktifkan' : 'Token belum diisi' };
|
||||
} else {
|
||||
const resp = await axios.get(`https://api.telegram.org/bot${token}/getMe`, { timeout: 6000, validateStatus: (s) => s < 500 });
|
||||
if (resp.data && resp.data.ok) results.telegram = { status: 'online', message: 'Terhubung sebagai @' + ((resp.data.result && resp.data.result.username) || '-') };
|
||||
else results.telegram = { status: 'offline', message: (resp.data && resp.data.description) || 'getMe gagal' };
|
||||
}
|
||||
} catch (err) {
|
||||
results.telegram = { status: 'offline', message: `Telegram check error: ${err.message}` };
|
||||
}
|
||||
|
||||
return results;
|
||||
|
||||
@@ -111,7 +111,7 @@ async function sendFonnteMessage(toPhone, messageText, options = {}) {
|
||||
* Test Koneksi / Kirim Pesan Uji Coba Fonnte
|
||||
*/
|
||||
async function testFonnteConnection(targetPhone, customMessage) {
|
||||
const msg = customMessage || '🔔 *Tes Koneksi Fonnte WhatsApp Gateway Berhasil!*\n\nSistem Billing RTRW Net siap mengirimkan notifikasi otomatis melalui gateway Fonnte.';
|
||||
const msg = customMessage || '🔔 *Tes Koneksi Fonnte WhatsApp Gateway Berhasil!*\n\nOPEN-ISP siap mengirimkan notifikasi otomatis melalui gateway Fonnte.';
|
||||
return await sendFonnteMessage(targetPhone, msg);
|
||||
}
|
||||
|
||||
|
||||
@@ -1555,13 +1555,13 @@ async function generateIsolirPortalScript() {
|
||||
let port = 443;
|
||||
let isHttps = true;
|
||||
try {
|
||||
const u = new URL(normalized || 'http://127.0.0.1:4555');
|
||||
const u = new URL(normalized || 'http://127.0.0.1:3001');
|
||||
hostname = u.hostname;
|
||||
port = u.port ? parseInt(u.port, 10) : (u.protocol === 'https:' ? 443 : 80);
|
||||
isHttps = u.protocol === 'https:';
|
||||
} catch {
|
||||
hostname = 'GANTI-host-portal-billing';
|
||||
port = 4555;
|
||||
port = 3001;
|
||||
isHttps = false;
|
||||
}
|
||||
|
||||
@@ -1724,7 +1724,7 @@ async function setupIsolirFirewall(routerId = null) {
|
||||
try {
|
||||
const raw = String(settings.app_url || '').trim();
|
||||
const normalized = raw && /^https?:\/\//i.test(raw) ? raw : (raw ? `https://${raw}` : '');
|
||||
const u = new URL(normalized || 'http://127.0.0.1:4555');
|
||||
const u = new URL(normalized || 'http://127.0.0.1:3001');
|
||||
const isHttps = u.protocol === 'https:';
|
||||
const port = u.port ? parseInt(u.port, 10) : (isHttps ? 443 : 80);
|
||||
httpNatPort = isHttps ? 80 : port;
|
||||
@@ -2042,7 +2042,7 @@ async function getBackup(routerId = null) {
|
||||
rscLines.push(`# Router : ${routerName} (${routerHost})`);
|
||||
rscLines.push(`# Tanggal : ${nowStr}`);
|
||||
rscLines.push(`# Format : RouterOS Script (.rsc) ROS7 Ready`);
|
||||
rscLines.push(`# Generator: Billing RTRW System`);
|
||||
rscLines.push(`# Generator: OPEN-ISP Billing System`);
|
||||
rscLines.push(`# ===================================================\n`);
|
||||
|
||||
// 1. IP Pools
|
||||
|
||||
@@ -8,7 +8,7 @@ const path = require('path');
|
||||
const { getSetting } = require('../config/settingsManager');
|
||||
|
||||
function gateway() {
|
||||
return String(getSetting('wa_gateway_type', 'baileys') || 'baileys').toLowerCase();
|
||||
return String(getSetting('wa_gateway_type', 'gowa') || 'baileys').toLowerCase();
|
||||
}
|
||||
|
||||
function spx(t) {
|
||||
|
||||
@@ -933,7 +933,7 @@ export const whatsappStatus = {
|
||||
_connection: 'connecting',
|
||||
get connection() {
|
||||
try {
|
||||
const gw = getSetting('wa_gateway_type', 'baileys');
|
||||
const gw = getSetting('wa_gateway_type', 'gowa');
|
||||
if (gw === 'fonnte') {
|
||||
const token = getSetting('fonnte_token', '');
|
||||
return token ? 'open' : 'close';
|
||||
@@ -954,7 +954,7 @@ export const whatsappStatus = {
|
||||
_user: null,
|
||||
get user() {
|
||||
try {
|
||||
const gw = getSetting('wa_gateway_type', 'baileys');
|
||||
const gw = getSetting('wa_gateway_type', 'gowa');
|
||||
if (gw === 'fonnte') {
|
||||
return { id: 'fonnte_gateway', name: 'Fonnte Gateway' };
|
||||
}
|
||||
@@ -973,7 +973,7 @@ export const whatsappStatus = {
|
||||
_phoneNumber: null,
|
||||
get phoneNumber() {
|
||||
try {
|
||||
const gw = getSetting('wa_gateway_type', 'baileys');
|
||||
const gw = getSetting('wa_gateway_type', 'gowa');
|
||||
if (gw === 'fonnte') return 'Fonnte Gateway';
|
||||
if (gw === 'meta') return getSetting('meta_phone_number_id', 'Meta API');
|
||||
if (gw === 'gowa') return getSetting('gowa_device_id', '') || 'GOWA Gateway';
|
||||
@@ -986,7 +986,7 @@ export const whatsappStatus = {
|
||||
_pushName: null,
|
||||
get pushName() {
|
||||
try {
|
||||
const gw = getSetting('wa_gateway_type', 'baileys');
|
||||
const gw = getSetting('wa_gateway_type', 'gowa');
|
||||
if (gw === 'fonnte') return 'Fonnte Gateway';
|
||||
if (gw === 'meta') return 'Meta Cloud API';
|
||||
if (gw === 'gowa') return 'GOWA Gateway';
|
||||
@@ -1032,7 +1032,7 @@ function loadWhatsappAdminSendList() {
|
||||
* @param {string} priority - Priority level: 'high', 'medium', 'low'
|
||||
*/
|
||||
export async function sendMonitoringAlert(message, priority = 'medium') {
|
||||
const gatewayType = getSetting('wa_gateway_type', 'baileys');
|
||||
const gatewayType = getSetting('wa_gateway_type', 'gowa');
|
||||
if (gatewayType === 'baileys' && (!currentSock || whatsappStatus._connection !== 'open')) {
|
||||
logger.warn('[WhatsApp] Bot belum siap (koneksi belum terbuka), tidak dapat mengirim alert monitoring');
|
||||
return { success: false, message: 'Bot belum siap' };
|
||||
@@ -1139,7 +1139,7 @@ export async function simulateHumanTyping(sock, jid, text = '') {
|
||||
}
|
||||
|
||||
export async function sendWA(to, text, options = {}) {
|
||||
const gatewayType = getSetting('wa_gateway_type', 'baileys');
|
||||
const gatewayType = getSetting('wa_gateway_type', 'gowa');
|
||||
|
||||
let finalText = text;
|
||||
if (options.spintax !== false && typeof text === 'string' && text.includes('{') && text.includes('}')) {
|
||||
@@ -1219,7 +1219,7 @@ export async function sendWA(to, text, options = {}) {
|
||||
}
|
||||
|
||||
export async function sendWAImage(to, imageBuffer, caption = '', options = {}) {
|
||||
const gatewayType = getSetting('wa_gateway_type', 'baileys');
|
||||
const gatewayType = getSetting('wa_gateway_type', 'gowa');
|
||||
|
||||
let finalCaption = caption;
|
||||
if (options.spintax !== false && typeof caption === 'string' && caption.includes('{') && caption.includes('}')) {
|
||||
@@ -1286,7 +1286,7 @@ export async function sendWAImage(to, imageBuffer, caption = '', options = {}) {
|
||||
}
|
||||
|
||||
export async function sendWADocument(to, documentBuffer, filename = 'Invoice.pdf', caption = '', mimetype = 'application/pdf', options = {}) {
|
||||
const gatewayType = getSetting('wa_gateway_type', 'baileys');
|
||||
const gatewayType = getSetting('wa_gateway_type', 'gowa');
|
||||
|
||||
let finalCaption = caption;
|
||||
if (options.spintax !== false && typeof caption === 'string' && caption.includes('{') && caption.includes('}')) {
|
||||
|
||||
@@ -140,7 +140,7 @@ function formatUnpaidInvoicesSummary(unpaidInvoices) {
|
||||
* @param {object} options Opsi tambahan: { templateName, parameters, langCode, url, filename }
|
||||
*/
|
||||
async function sendWhatsAppMessage(toPhone, messageText, options = {}) {
|
||||
const gatewayType = getSetting('wa_gateway_type', 'baileys'); // 'baileys', 'fonnte', or 'meta'
|
||||
const gatewayType = getSetting('wa_gateway_type', 'gowa'); // 'baileys', 'fonnte', or 'meta'
|
||||
|
||||
if (gatewayType === 'fonnte') {
|
||||
// Mode FONNTE API GATEWAY (Cloud / Self-Hosted)
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
B 4.8.1-R18
|
||||
0.5.1
|
||||
+1
-1
@@ -174,7 +174,7 @@
|
||||
<span class="text-muted" style="font-size:12px;">-</span>
|
||||
<% } %>
|
||||
</td>
|
||||
<td><code><%= d.ip %></code></td>
|
||||
<td><code><%= d.ip || d.pppoe_ip || '-' %></code></td>
|
||||
<td><%= d.rx_power %> dBm</td>
|
||||
<td><%= d.pppoe_user %></td>
|
||||
<td><span class="acs-server-tag"><%= d.acs_server_name %></span></td>
|
||||
|
||||
@@ -247,7 +247,7 @@
|
||||
<td class="text-muted"><%= i+1 %></td>
|
||||
<td class="fw6">
|
||||
<a href="javascript:void(0)" onclick="viewCustDetail(<%= i %>)" style="color:var(--primary);text-decoration:none;font-weight:700;display:inline-flex;align-items:center;gap:5px;cursor:pointer;" title="<%= t('admin.cust.tt_detail','Klik untuk lihat detail pelanggan & MikroTik') %>">
|
||||
<%= c.name %>
|
||||
<%= c.name %><% var _miss=[]; if(!c.phone)_miss.push('HP'); if(!c.lat||!c.lng)_miss.push('koordinat'); if(!c.genieacs_tag)_miss.push('ACS'); %><% if(_miss.length===0){ %><i class="bi bi-check-circle-fill" style="font-size:12px;color:#22c55e;vertical-align:middle;" title="<%= t('admin.customers.data_ok','Data lengkap') %>"></i><% } else { %><i class="bi bi-x-circle-fill" style="font-size:12px;color:#ef4444;vertical-align:middle;" title="<%= t('admin.customers.data_incomplete','Belum lengkap') %>: <%- _miss.join(', ') %>"></i><% } %>
|
||||
<i class="bi bi-info-circle-fill" style="font-size:12px;opacity:0.75;"></i>
|
||||
</a>
|
||||
<% if (c.customer_code) { %><div style="font-size:11px;font-weight:700;color:var(--muted);margin-top:2px;display:flex;align-items:center;gap:4px;" title="ID Pelanggan"><i class="bi bi-person-badge"></i> <%= c.customer_code %></div><% } %>
|
||||
|
||||
@@ -568,13 +568,21 @@
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>WhatsApp Bot</td>
|
||||
<td>WhatsApp Bot<div class="sc-label" style="font-size:11px"><%= dependencies.whatsapp.message %></div></td>
|
||||
<td class="text-right">
|
||||
<span class="badge <%= dependencies.whatsapp.status === 'online' ? 'bs' : 'bd' %>">
|
||||
<%= dependencies.whatsapp.status.toUpperCase() %>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Telegram Bot<div class="sc-label" style="font-size:11px"><%= dependencies.telegram ? dependencies.telegram.message : '' %></div></td>
|
||||
<td class="text-right">
|
||||
<span class="badge <%= (dependencies.telegram && dependencies.telegram.status === 'online') ? 'bs' : 'bd' %>">
|
||||
<%= dependencies.telegram ? dependencies.telegram.status.toUpperCase() : 'UNKNOWN' %>
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p class="sc-label mt16 mb8">Router MikroTik</p>
|
||||
|
||||
@@ -415,8 +415,8 @@
|
||||
/radius incoming set accept=yes port=3799
|
||||
|
||||
# 3. Tambahkan Server RADIUS Billing ke MikroTik
|
||||
/radius remove [find comment="RTRW-Billing-RADIUS"]
|
||||
/radius add service=ppp address=<%= typeof serverIp !== 'undefined' ? serverIp : '192.168.8.88' %> secret="<%= radiusStatus.secret %>" authentication-port=<%= radiusStatus.authPort %> accounting-port=<%= radiusStatus.acctPort %> timeout=3000ms comment="RTRW-Billing-RADIUS"
|
||||
/radius remove [find comment="OPEN-ISP-Billing-RADIUS"]
|
||||
/radius add service=ppp address=<%= typeof serverIp !== 'undefined' ? serverIp : '192.168.8.88' %> secret="<%= radiusStatus.secret %>" authentication-port=<%= radiusStatus.authPort %> accounting-port=<%= radiusStatus.acctPort %> timeout=3000ms comment="OPEN-ISP-Billing-RADIUS"
|
||||
|
||||
# 4. Tambahkan IP Address Gateway Subnet Isolir RADIUS
|
||||
/ip address remove [find comment="Gateway-Isolir-RADIUS"]
|
||||
|
||||
@@ -29,6 +29,17 @@
|
||||
<% if (info && info.error) { %>
|
||||
<div class="alert alert-d"><i class="bi bi-exclamation-circle"></i> <%= info.error %></div>
|
||||
<% } %>
|
||||
<% if (info && info.container) { %>
|
||||
<div class="alert" style="background:rgba(59,130,246,.08);border:1px solid rgba(59,130,246,.3);border-radius:10px;padding:12px 14px;font-size:13px;line-height:1.55">
|
||||
<b><i class="bi bi-box-seam"></i> <%= t('admin.update.container_title', 'Instalasi Container (tanpa .git)') %></b><br>
|
||||
<%= t('admin.update.container_msg', 'Pembaruan TIDAK lewat git. Bangun ulang image dari repo terbaru lalu ganti container:') %>
|
||||
<div style="margin-top:8px;font-family:monospace;font-size:12px;background:rgba(0,0,0,.25);padding:8px 10px;border-radius:8px">
|
||||
docker compose build && docker compose up -d<br>
|
||||
# atau: build image baru, lalu ganti tag/container
|
||||
</div>
|
||||
<%= t('admin.update.container_safe', 'Data (settings.json, database, uploads) tetap aman bila disimpan di volume/mount.') %>
|
||||
</div>
|
||||
<% } %>
|
||||
<div class="form-row" style="align-items:flex-start">
|
||||
<div class="fg">
|
||||
<label class="fl"><%= t('admin.update.server_version', 'Versi Server (version.txt)') %></label>
|
||||
@@ -51,6 +62,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% if (!(info && info.container)) { %>
|
||||
<div style="display:flex;gap:10px;flex-wrap:wrap;margin-top:12px">
|
||||
<form method="GET" action="/admin/update">
|
||||
<button type="submit" class="btn btn-g"><i class="bi bi-arrow-repeat"></i> <%= t('admin.update.check', 'Cek Versi') %></button>
|
||||
@@ -63,6 +75,7 @@
|
||||
<div style="margin-top:12px;color:var(--muted);font-size:12px;line-height:1.5">
|
||||
<%= t('admin.update.keep_files', 'File yang tidak ditimpa saat update: settings.json dan folder database. Setelah update selesai, restart aplikasi agar versi baru aktif.') %>
|
||||
</div>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -51,9 +51,9 @@
|
||||
<a href="/admin/whatsapp/live-chat" class="wa-tab <%= waTab==='livechat' ? 'active' : '' %>"><i class="bi bi-chat-dots-fill"></i> <%= t('admin.wa.tab_livechat','Live Chat') %></a>
|
||||
</div>
|
||||
|
||||
<!-- PENGATURAN HYBRID GATEWAY (BAILEYS VS FONNTE VS META API) -->
|
||||
<!-- PENGATURAN GATEWAY (GOWA / FONNTE / META API) -->
|
||||
<div class="card mb16" style="padding: 20px;">
|
||||
<h6 style="margin-bottom: 15px; font-weight: 700; color: var(--primary);"><i class="bi bi-sliders"></i> Mode WhatsApp Gateway (Baileys / Fonnte / GOWA / Meta API)</h6>
|
||||
<h6 style="margin-bottom: 15px; font-weight: 700; color: var(--primary);"><i class="bi bi-sliders"></i> Mode WhatsApp Gateway (GoWA / Fonnte / Meta API)</h6>
|
||||
|
||||
<form method="POST" action="/admin/whatsapp/gateway-settings">
|
||||
<div class="form-row mb16">
|
||||
@@ -334,14 +334,6 @@ async function sendTestGowa() {
|
||||
<h2 id="status-text"><%= t('admin.whatsapp.checking', 'Mengecek status...') %></h2>
|
||||
<p id="status-desc" class="text-muted"><%= t('admin.whatsapp.please_wait', 'Harap tunggu sebentar.') %></p>
|
||||
|
||||
<% if (waGatewayType === 'baileys') { %>
|
||||
<div id="qr-section" style="display:none">
|
||||
<p class="mb-3"><%= t('admin.whatsapp.qr_instruction', 'Silakan pindai QR Code ini dengan aplikasi WhatsApp Anda:') %></p>
|
||||
<div id="qr-container"></div>
|
||||
<p class="mt-3 text-sm text-muted"><%= t('admin.whatsapp.qr_auto_refresh', 'QR Code akan diperbarui secara otomatis.') %></p>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div id="info-section" style="display:none" class="wa-info">
|
||||
<div class="alert alert-s">
|
||||
<i class="bi bi-check-circle-fill"></i> <%= t('admin.whatsapp.connected_title', 'WhatsApp Terhubung') %>
|
||||
@@ -355,14 +347,6 @@ async function sendTestGowa() {
|
||||
<form method="POST" action="/admin/whatsapp/test-notification" onsubmit="return confirm('<%= t('admin.whatsapp.confirm_test', 'Kirim test notifikasi WhatsApp ke admin sekarang?') %>');">
|
||||
<button class="btn btn-p"><i class="bi bi-send-check"></i> <%= t('admin.whatsapp.test_notification', 'Kirim Test Notif') %></button>
|
||||
</form>
|
||||
<% if (waGatewayType === 'baileys') { %>
|
||||
<form method="POST" action="/admin/whatsapp/repair-keys" onsubmit="return confirm('Bersihkan cache pre-keys & sesi usang untuk mengatasi masalah pesan pending / Bad-MAC tanpa perlu scan QR ulang?');">
|
||||
<button class="btn btn-g" style="border-color: #3b82f6; color: #2563eb;"><i class="bi bi-wrench-adjustable"></i> Perbaiki Sesi (Anti Waiting)</button>
|
||||
</form>
|
||||
<form method="POST" action="/admin/whatsapp/reset" onsubmit="return confirm('<%= t('admin.whatsapp.confirm_reset', 'Apakah Anda yakin ingin menghapus sesi WhatsApp? Anda harus scan QR ulang setelah ini.') %>')">
|
||||
<button class="btn btn-d"><i class="bi bi-trash"></i> <%= t('admin.whatsapp.reset_session', 'Hapus Sesi / Reset') %></button>
|
||||
</form>
|
||||
<% } %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user