feat: badge kelengkapan data pelanggan; fix IP search GenieACS; mount speedtest backend; isolir-redirect embedded; sembunyikan menu Update tanpa .git

This commit is contained in:
oxpoe committed 2026-10-13 03:01:16 +07:00
1 parent b715f48f8d
commit f27c02309e
7 files changed
+27 -3

No files matched your search

+11
View File
@@ -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
+6
View File
@@ -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);
@@ -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); }
}
+5 -1
View File
@@ -546,7 +546,9 @@ function forceUnlockCoreMenus() {
let states = rowStates ? JSON.parse(rowStates.value) : {};
let keys = rowKeys ? JSON.parse(rowKeys.value) : {};
const coreMenus = ['mikrotik', 'radius_server', 'whatsapp', 'broadcast', 'update', 'settings', 'backup', 'monitoring', 'audit_logs'];
const hasGit = fs.existsSync(path.join(__dirname, '..', '.git'));
const coreMenus = ['mikrotik', 'radius_server', 'whatsapp', 'broadcast', 'settings', 'backup', 'monitoring', 'audit_logs'];
if (hasGit) coreMenus.push('update');
const passwordHash = 'af03458614be223016c5bc1c0788eaefc6a6bfd0c11cdcd4bc520f49d0cb474a'; // hash kode aktivasi default — plaintext hanya di settings.json (activation_code)
const crypto = require('crypto');
@@ -569,6 +571,8 @@ function forceUnlockCoreMenus() {
}
}
if (!hasGit && states['update'] !== 'hidden') { states['update'] = 'hidden'; changed = true; }
if (changed) {
const now = new Date().toISOString();
db.prepare('INSERT OR REPLACE INTO app_settings (key, value, updated_at) VALUES (?, ?, ?)').run(SETTINGS_KEY, JSON.stringify(states), now);
+2
View File
@@ -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}`);
});
+1
View File
@@ -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,
+1 -1
View File
@@ -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>
+1 -1
View File
@@ -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><% } %>