feat(acs): modal atur SSID & sandi dari daftar perangkat
All checks were successful
CI / test (push) Successful in 1m36s
All checks were successful
CI / test (push) Successful in 1m36s
- tombol Atur WiFi di menu aksi - tampilkan SSID 2.4G/5G saat ini - endpoint simpan SSID & sandi terpisah - versi 0.6.11
This commit is contained in:
@@ -2,6 +2,13 @@
|
||||
|
||||
Format mengikuti [Keep a Changelog](https://keepachangelog.com/) dan [Semantic Versioning](https://semver.org/).
|
||||
|
||||
## [0.6.11] — 2026-11-06 — "acs-wifi-modal"
|
||||
|
||||
### Ditambahkan
|
||||
- Tombol **Atur WiFi (SSID / Sandi)** di daftar perangkat ACS.
|
||||
- Modal menampilkan SSID 2.4G/5G saat ini, simpan SSID dan sandi terpisah.
|
||||
- Endpoint `POST /admin/acs/api/device/:id/ssid` dan `/password`.
|
||||
|
||||
## [0.6.10] — 2026-11-06 — "acs-lookup"
|
||||
|
||||
### Diperbaiki
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/badge/lisensi-ISC-ADFF2F" alt="Lisensi: ISC">
|
||||
<img src="https://img.shields.io/badge/versi-0.6.10_acs--lookup-ADFF2F" alt="Versi 0.6.10 (acs-lookup)">
|
||||
<img src="https://img.shields.io/badge/versi-0.6.11_acs--modal-ADFF2F" alt="Versi 0.6.11 (acs-wifi-modal)">
|
||||
<img src="https://img.shields.io/badge/Node.js-%E2%89%A520-339933" alt="Node.js >= 20">
|
||||
<img src="https://img.shields.io/badge/docker-ready-2496ED?logo=docker&logoColor=white" alt="Docker">
|
||||
<a href="https://demo-openisp.oxpoe.xyz"><img src="https://img.shields.io/badge/demo-live-ADFF2F?logo=googlechrome&logoColor=black" alt="Demo"></a>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "open-isp",
|
||||
"version": "0.6.10",
|
||||
"version": "0.6.11",
|
||||
"description": "OPEN-ISP \u2014 sistem billing & manajemen ISP open-source (PWA pelanggan, multi-OLT, RADIUS, WhatsApp, QRIS)",
|
||||
"main": "app-customer.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -1601,6 +1601,36 @@ router.get('/api/wifi-settings/:deviceId', requireAdmin, async (req, res) => {
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/api/device/:deviceId/ssid', requireAdmin, express.json(), async (req, res) => {
|
||||
try {
|
||||
const deviceId = req.params.deviceId;
|
||||
const ssid = String(req.body.ssid || '').trim();
|
||||
if (!ssid || ssid.length < 2) {
|
||||
return res.status(400).json({ success: false, message: 'Nama WiFi (SSID) minimal 2 karakter' });
|
||||
}
|
||||
const ok = await customerDevice.updateSSID(deviceId, ssid);
|
||||
if (!ok) return res.status(500).json({ success: false, message: 'Gagal mengubah SSID. Pastikan perangkat aktif.' });
|
||||
return res.json({ success: true, message: `SSID diubah menjadi "${ssid}"` });
|
||||
} catch (err) {
|
||||
return res.status(500).json({ success: false, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/api/device/:deviceId/password', requireAdmin, express.json(), async (req, res) => {
|
||||
try {
|
||||
const deviceId = req.params.deviceId;
|
||||
const password = String(req.body.password || '').trim();
|
||||
if (!password || password.length < 8) {
|
||||
return res.status(400).json({ success: false, message: 'Sandi WiFi minimal 8 karakter' });
|
||||
}
|
||||
const ok = await customerDevice.updatePassword(deviceId, password);
|
||||
if (!ok) return res.status(500).json({ success: false, message: 'Gagal mengubah sandi WiFi. Pastikan perangkat aktif.' });
|
||||
return res.json({ success: true, message: 'Sandi WiFi berhasil diperbarui' });
|
||||
} catch (err) {
|
||||
return res.status(500).json({ success: false, message: err.message });
|
||||
}
|
||||
});
|
||||
|
||||
router.get('/api/add-wan-status/:deviceId', requireAdmin, async (req, res) => {
|
||||
try {
|
||||
const { deviceId } = req.params;
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.6.10
|
||||
0.6.11
|
||||
@@ -189,6 +189,7 @@
|
||||
<div class="act-menu">
|
||||
<button type="button" class="act-item primary" onclick="refreshDevice('<%= d.id %>')"><i class="bi bi-arrow-repeat"></i> Refresh Device</button>
|
||||
<button type="button" class="act-item" onclick="openAddWanModal('<%= d.id %>', '<%= d.acs_server_id || selectedAcsId %>', '<%= d.sn %>', '<%= d.manufacturer || "" %>', '<%= d.model || "" %>')"><i class="bi bi-diagram-3"></i> Add WAN (All-in-One)</button>
|
||||
<button type="button" class="act-item" onclick="openWifiModal('<%= d.id %>', '<%= d.acs_server_id || selectedAcsId %>', '<%= d.sn %>')"><i class="bi bi-wifi"></i> Atur WiFi (SSID / Sandi)</button>
|
||||
<div class="act-sep"></div>
|
||||
<button type="button" class="act-item danger" onclick="deleteDevice('<%= d.id %>', '<%= d.acs_server_id || selectedAcsId %>')"><i class="bi bi-trash"></i> <%= t('admin.acs.del_device','Hapus Perangkat') %></button>
|
||||
</div>
|
||||
@@ -276,6 +277,37 @@
|
||||
</div>
|
||||
|
||||
<!-- Unified Add WAN All-in-One Modal -->
|
||||
<div class="mo" id="wifiModal">
|
||||
<div class="mb" style="max-width:520px">
|
||||
<div class="mh">
|
||||
<span class="mt"><i class="bi bi-wifi"></i> Atur WiFi ONU</span>
|
||||
<button class="mc" onclick="closeModal('wifiModal')">×</button>
|
||||
</div>
|
||||
<div class="mbody">
|
||||
<input type="hidden" id="wifiDeviceId">
|
||||
<input type="hidden" id="wifiAcsId">
|
||||
<div style="background:var(--sub-bg);border:1px dashed var(--border);border-radius:10px;padding:12px;margin-bottom:14px;font-size:13px">
|
||||
<div><b>SN:</b> <span id="wifiSn" class="text-primary" style="font-weight:700">-</span></div>
|
||||
<div style="margin-top:4px"><b>SSID saat ini:</b> <span id="wifiCurrentSsid" style="color:var(--muted)">Memuat…</span></div>
|
||||
</div>
|
||||
<div class="fg">
|
||||
<label class="fl">SSID Baru (2.4G)</label>
|
||||
<input class="fc" id="wifiSsidInput" placeholder="Nama WiFi baru">
|
||||
<div style="font-size:11px;color:var(--muted);margin-top:4px">SSID 5G otomatis menjadi <code><nama>-5G</code> jika didukung perangkat. SSID2/Guest tidak diubah.</div>
|
||||
</div>
|
||||
<button type="button" class="btn btn-p btn-sm" onclick="saveWifiSsid(this)"><i class="bi bi-save"></i> Simpan SSID</button>
|
||||
<hr style="border-color:var(--border);margin:18px 0">
|
||||
<div class="fg">
|
||||
<label class="fl">Sandi WiFi Baru</label>
|
||||
<input class="fc" type="password" id="wifiPassInput" placeholder="Minimal 8 karakter">
|
||||
</div>
|
||||
<button type="button" class="btn btn-p btn-sm" onclick="saveWifiPassword(this)"><i class="bi bi-shield-lock"></i> Simpan Sandi</button>
|
||||
<div id="wifiMsg" style="margin-top:10px;font-size:12px;color:var(--muted)"></div>
|
||||
</div>
|
||||
<div class="mf"><button type="button" class="btn btn-g" onclick="closeModal('wifiModal')">Tutup</button></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mo" id="addWanModal">
|
||||
<div class="mb" style="max-width:650px">
|
||||
<div class="mh">
|
||||
@@ -1071,6 +1103,73 @@ async function bulkDelete() {
|
||||
}
|
||||
}
|
||||
|
||||
function openWifiModal(deviceId, acsId, sn) {
|
||||
document.getElementById('wifiDeviceId').value = deviceId;
|
||||
document.getElementById('wifiAcsId').value = acsId || '';
|
||||
document.getElementById('wifiSn').textContent = sn || deviceId;
|
||||
document.getElementById('wifiSsidInput').value = '';
|
||||
document.getElementById('wifiPassInput').value = '';
|
||||
document.getElementById('wifiCurrentSsid').textContent = 'Memuat…';
|
||||
document.getElementById('wifiMsg').textContent = '';
|
||||
openModal('wifiModal');
|
||||
fetch('/admin/acs/api/wifi-settings/' + encodeURIComponent(deviceId) + '?acsId=' + encodeURIComponent(acsId || ''))
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (d) {
|
||||
if (!d || !d.success) {
|
||||
document.getElementById('wifiCurrentSsid').textContent = (d && (d.message || d.error)) || 'Gagal memuat WiFi';
|
||||
return;
|
||||
}
|
||||
var bands = d.bands || [];
|
||||
var b1 = bands.find(function (b) { return String(b.index) === '1'; }) || bands[0] || null;
|
||||
var b5 = bands.find(function (b) { return String(b.index) === '5'; });
|
||||
var parts = [];
|
||||
if (b1) parts.push('2.4G: ' + String(b1.ssid || '-'));
|
||||
if (b5) parts.push('5G: ' + String(b5.ssid || '-'));
|
||||
document.getElementById('wifiCurrentSsid').textContent = parts.join(' • ') || '-';
|
||||
if (b1) document.getElementById('wifiSsidInput').value = String(b1.ssid || '').replace(/-5G$/, '');
|
||||
})
|
||||
.catch(function (e) { document.getElementById('wifiCurrentSsid').textContent = 'Error: ' + e.message; });
|
||||
}
|
||||
|
||||
async function saveWifiSsid(btn) {
|
||||
var id = document.getElementById('wifiDeviceId').value;
|
||||
var ssid = String(document.getElementById('wifiSsidInput').value || '').trim();
|
||||
var msg = document.getElementById('wifiMsg');
|
||||
if (ssid.length < 2) { msg.textContent = 'SSID minimal 2 karakter.'; return; }
|
||||
if (!confirm('Ubah SSID perangkat ini?')) return;
|
||||
var old = btn.innerHTML; btn.disabled = true; btn.innerHTML = '<i class="bi bi-hourglass-split"></i> Menyimpan…';
|
||||
msg.textContent = '';
|
||||
try {
|
||||
var r = await fetch('/admin/acs/api/device/' + encodeURIComponent(id) + '/ssid', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ssid: ssid })
|
||||
});
|
||||
var d = await r.json();
|
||||
msg.style.color = d.success ? 'var(--success)' : 'var(--danger)';
|
||||
msg.textContent = d.success ? d.message : ('Gagal: ' + (d.message || 'unknown'));
|
||||
} catch (e) { msg.style.color = 'var(--danger)'; msg.textContent = 'Error: ' + e.message; }
|
||||
finally { btn.disabled = false; btn.innerHTML = old; }
|
||||
}
|
||||
|
||||
async function saveWifiPassword(btn) {
|
||||
var id = document.getElementById('wifiDeviceId').value;
|
||||
var pass = String(document.getElementById('wifiPassInput').value || '').trim();
|
||||
var msg = document.getElementById('wifiMsg');
|
||||
if (pass.length < 8) { msg.textContent = 'Sandi minimal 8 karakter.'; return; }
|
||||
if (!confirm('Ubah sandi WiFi perangkat ini?')) return;
|
||||
var old = btn.innerHTML; btn.disabled = true; btn.innerHTML = '<i class="bi bi-hourglass-split"></i> Menyimpan…';
|
||||
msg.textContent = '';
|
||||
try {
|
||||
var r = await fetch('/admin/acs/api/device/' + encodeURIComponent(id) + '/password', {
|
||||
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ password: pass })
|
||||
});
|
||||
var d = await r.json();
|
||||
msg.style.color = d.success ? 'var(--success)' : 'var(--danger)';
|
||||
msg.textContent = d.success ? d.message : ('Gagal: ' + (d.message || 'unknown'));
|
||||
if (d.success) document.getElementById('wifiPassInput').value = '';
|
||||
} catch (e) { msg.style.color = 'var(--danger)'; msg.textContent = 'Error: ' + e.message; }
|
||||
finally { btn.disabled = false; btn.innerHTML = old; }
|
||||
}
|
||||
|
||||
async function deleteDevice(deviceId, acsId) {
|
||||
if (!confirm('Hapus perangkat ' + deviceId + ' dari database ACS?')) return;
|
||||
try {
|
||||
|
||||
Reference in New Issue
Block a user