diff --git a/.gitignore b/.gitignore index 4904111..d6a564e 100644 --- a/.gitignore +++ b/.gitignore @@ -86,3 +86,6 @@ public/downloads/logo-* # penanda restore tertunda (runtime) database/.restore-pending + +# penanda update menunggu restart +.restart-required diff --git a/CHANGELOG.md b/CHANGELOG.md index 49c09b8..6ceaf7d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ Format mengikuti [Keep a Changelog](https://keepachangelog.com/) dan [Semantic Versioning](https://semver.org/). +## [0.5.19] — 2026-11-06 — "restart-notice" + +### Ditambahkan +- Setelah update sukses, aplikasi menulis penanda `.restart-required`. +- **Floating notification** di semua halaman admin: "Billing perlu restart" berisi versi yang sudah diterapkan dan tautan cara restart. +- Banner di halaman Update menjelaskan bahwa versi sudah terbaru tetapi kode lama masih berjalan sampai restart. +- Penanda otomatis dihapus saat proses aplikasi baru start, jadi notifikasi hilang setelah restart. + ## [0.5.18] — 2026-11-06 — "gowa-autodevice" ### Diperbaiki diff --git a/README.md b/README.md index a2bd9c0..bfad427 100644 --- a/README.md +++ b/README.md @@ -11,7 +11,7 @@

Lisensi: ISC - Versi 0.5.18 (gowa-autodevice) + Versi 0.5.19 (restart-notice) Node.js >= 20 Docker Demo diff --git a/app-customer.js b/app-customer.js index 702c284..11c8017 100644 --- a/app-customer.js +++ b/app-customer.js @@ -61,6 +61,8 @@ const { SUPPORTED_LANGS, FALLBACK_LANG, normalizeLang, t } = require('./config/i if (typeof ensureDefaultSettings === 'function') { ensureDefaultSettings(); try { require('./config/settingsManager').ensureGowaWebhookSecret(); } catch (e) {} + // Proses baru = kode terbaru sudah dimuat; hapus penanda restart. + try { fs.rmSync(path.join(__dirname, '.restart-required'), { force: true }); } catch (e) {} } // Inisialisasi aplikasi Express diff --git a/package.json b/package.json index 80b1d9b..e776816 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "open-isp", - "version": "0.5.18", + "version": "0.5.19", "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 1cd8623..4386303 100644 --- a/routes/adminPortal.js +++ b/routes/adminPortal.js @@ -4598,16 +4598,34 @@ router.post('/settings/regenerate-icons', requireAdminSession, async (req, res) router.get('/update', requireAdminSession, requireSidebarMenuAccess('update'), restrictToAdmin, (req, res) => { const repoRoot = path.resolve(__dirname, '..'); const info = getUpdateInfo(repoRoot); + let restartPending = null; + try { + const markerPath = path.join(repoRoot, '.restart-required'); + if (fs.existsSync(markerPath)) restartPending = JSON.parse(fs.readFileSync(markerPath, 'utf8')); + } catch (e) { restartPending = null; } res.render('admin/update', { title: 'Update Aplikasi', company: company(), activePage: 'update', msg: flashMsg(req), info, + restartPending, log: popUpdateLog(req) }); }); +router.get('/api/restart-required', requireAdminSession, (req, res) => { + const repoRoot = path.resolve(__dirname, '..'); + try { + const markerPath = path.join(repoRoot, '.restart-required'); + if (fs.existsSync(markerPath)) { + const d = JSON.parse(fs.readFileSync(markerPath, 'utf8')); + return res.json({ pending: true, version: d.version || '', from: d.from || '', at: d.at || '' }); + } + } catch (e) {} + return res.json({ pending: false }); +}); + router.post('/update/run', requireAdminSession, restrictToAdmin, (req, res) => { const repoRoot = path.resolve(__dirname, '..'); if (!runCmd('git', ['rev-parse', '--is-inside-work-tree'], repoRoot).ok) { @@ -4753,7 +4771,17 @@ router.post('/update/run', requireAdminSession, restrictToAdmin, (req, res) => { } const localAfter = readTextFileSafe(versionPath) || '-'; - req.session._msg = { type: 'success', text: `Update selesai. Versi: ${localBefore} → ${localAfter}. Silakan restart aplikasi.` }; + try { + fs.writeFileSync( + path.join(repoRoot, '.restart-required'), + JSON.stringify({ version: localAfter, from: localBefore, at: new Date().toISOString() }), + 'utf8' + ); + log.push('$ restart-required marker ditulis'); + } catch (markerErr) { + log.push('$ restart-required marker gagal: ' + String(markerErr?.message || markerErr)); + } + req.session._msg = { type: 'success', text: `Update selesai. Versi: ${localBefore} → ${localAfter}. Restart aplikasi diperlukan agar perubahan aktif.` }; req.session._updateLog = log.join('\n'); } catch (e) { if (repoChanged) { diff --git a/version.txt b/version.txt index d2d81b7..aa49d36 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -0.5.18 \ No newline at end of file +0.5.19 \ No newline at end of file diff --git a/views/admin/partials/sidebar.ejs b/views/admin/partials/sidebar.ejs index 9f394b5..e15719d 100644 --- a/views/admin/partials/sidebar.ejs +++ b/views/admin/partials/sidebar.ejs @@ -241,3 +241,31 @@ document.addEventListener('DOMContentLoaded', () => { return false; }; + +

+ diff --git a/views/admin/update.ejs b/views/admin/update.ejs index f41f198..1044351 100644 --- a/views/admin/update.ejs +++ b/views/admin/update.ejs @@ -23,6 +23,18 @@ <% } %> + <% if (typeof restartPending !== 'undefined' && restartPending) { %> +
+ +
+ Restart diperlukan. + Update ke versi <%= restartPending.version || '-' %> sudah diterapkan ke file, + tetapi aplikasi masih menjalankan kode lama. Fitur/fix baru belum aktif sampai aplikasi di-restart. + Lihat cara restart. +
+
+ <% } %> +
<%= t('admin.update.status_title', 'Status Versi') %>
@@ -79,7 +91,7 @@
-
+
<%= t('admin.update.restart_title', 'Cara Restart Aplikasi') %>