From a2888810ade7183c2c5fbb6b3362c23cc4495baa Mon Sep 17 00:00:00 2001 From: oxpoe Date: Fri, 6 Nov 2026 05:08:30 +0700 Subject: [PATCH] feat(qr-custom): hapus satu & reset histori dengan password admin - endpoint delete/reset, verifikasi admin_password - tombol hapus di detail + reset di header histori - versi 0.6.5 --- CHANGELOG.md | 6 ++++++ README.md | 2 +- package.json | 2 +- routes/adminPortal.js | 24 ++++++++++++++++++++++++ services/customQrService.js | 12 ++++++++++++ version.txt | 2 +- views/admin/custom_qr.ejs | 12 +++++++++++- 7 files changed, 56 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fac1da..1a24f9e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,12 @@ Format mengikuti [Keep a Changelog](https://keepachangelog.com/) dan [Semantic Versioning](https://semver.org/). +## [0.6.5] — 2026-11-06 — "qr-custom-delete" + +### Ditambahkan +- **Hapus satu QR custom** dari detail (wajib password admin). +- **Reset seluruh histori QR custom** dari header tabel (wajib password admin, konfirmasi). + ## [0.6.4] — 2026-11-06 — "qr-custom-ux" ### Diperbaiki diff --git a/README.md b/README.md index 704cb73..e474680 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

Lisensi: ISC - Versi 0.6.4 (qr-custom-ux) + Versi 0.6.5 (qr-custom-delete) Node.js >= 20 Docker Demo diff --git a/package.json b/package.json index ae247c3..4fe0624 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-isp", - "version": "0.6.4", + "version": "0.6.5", "description": "OPEN-ISP \u2014 sistem billing & manajemen ISP open-source (PWA pelanggan, multi-OLT, RADIUS, WhatsApp, QRIS)", "main": "app-customer.js", "scripts": { diff --git a/routes/adminPortal.js b/routes/adminPortal.js index bc4ddfb..9106f62 100644 --- a/routes/adminPortal.js +++ b/routes/adminPortal.js @@ -4062,6 +4062,30 @@ router.post('/custom-qr/:id/manual-paid', requireAdminSession, requireSidebarMen res.redirect('/admin/custom-qr?id=' + Number(req.params.id)); }); +function _checkAdminPassword(req) { + const pass = String((req.body && req.body.password) || ''); + const expected = String(getSetting('admin_password', 'demo-isp123') || 'demo-isp123'); + return !!expected && pass === expected; +} + +router.post('/custom-qr/:id/delete', requireAdminSession, restrictToAdmin, requireSidebarMenuAccess('custom_qr'), express.urlencoded({ extended: true }), (req, res) => { + try { + if (!_checkAdminPassword(req)) throw new Error('Password admin salah.'); + const n = require('../services/customQrService').remove(req.params.id); + req.session._msg = n ? { type: 'success', text: 'QR custom dihapus.' } : { type: 'error', text: 'Data tidak ditemukan.' }; + } catch (e) { req.session._msg = { type: 'error', text: e.message }; } + res.redirect('/admin/custom-qr'); +}); + +router.post('/custom-qr/reset', requireAdminSession, restrictToAdmin, requireSidebarMenuAccess('custom_qr'), express.urlencoded({ extended: true }), (req, res) => { + try { + if (!_checkAdminPassword(req)) throw new Error('Password admin salah.'); + const n = require('../services/customQrService').clearAll(); + req.session._msg = { type: 'success', text: `Histori QR custom direset (${n} data dihapus).` }; + } catch (e) { req.session._msg = { type: 'error', text: e.message }; } + res.redirect('/admin/custom-qr'); +}); + router.post('/custom-qr/:id/regenerate', requireAdminSession, requireSidebarMenuAccess('custom_qr'), express.urlencoded({ extended: true }), (req, res) => { try { const row = require('../services/customQrService').regenerate(req.params.id, resolvePaidByName(req, 'Admin')); diff --git a/services/customQrService.js b/services/customQrService.js index 8b218ba..bd4a9ba 100644 --- a/services/customQrService.js +++ b/services/customQrService.js @@ -118,6 +118,16 @@ function acceptWarning(id, actor, note) { return getById(id); } +function remove(id) { + const r = db.prepare('DELETE FROM custom_qr_payments WHERE id=?').run(Number(id) || 0); + return r.changes || 0; +} + +function clearAll() { + const r = db.prepare('DELETE FROM custom_qr_payments').run(); + return r.changes || 0; +} + function regenerate(id, actor) { const old = getById(id); if (!old) throw new Error('Data tidak ditemukan.'); @@ -196,6 +206,8 @@ module.exports = { markManualPaid, acceptWarning, regenerate, + remove, + clearAll, reject, markPaidFromWebhook, markWarningFromWebhook, diff --git a/version.txt b/version.txt index eb514eb..e0ea44c 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.6.4 \ No newline at end of file +0.6.5 \ No newline at end of file diff --git a/views/admin/custom_qr.ejs b/views/admin/custom_qr.ejs index cacc8c4..f87a07c 100644 --- a/views/admin/custom_qr.ejs +++ b/views/admin/custom_qr.ejs @@ -110,13 +110,23 @@ <% } %> +

+ + +
<% } %>
-
Histori QR Custom
+
+
Histori QR Custom
+
+ + +
+
JudulKategoriNominalStatusDibuatKedaluwarsaAksi