feat(update): floating notif + banner saat restart diperlukan
All checks were successful
CI / test (push) Successful in 1m32s
All checks were successful
CI / test (push) Successful in 1m32s
- marker .restart-required ditulis setelah update sukses - API /admin/api/restart-required + polling 30s di sidebar - banner di halaman Update sampai restart - marker dihapus saat boot proses baru - versi 0.5.19
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -86,3 +86,6 @@ public/downloads/logo-*
|
||||
|
||||
# penanda restore tertunda (runtime)
|
||||
database/.restore-pending
|
||||
|
||||
# penanda update menunggu restart
|
||||
.restart-required
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.5.18_gowa--autodevice-ADFF2F" alt="Versi 0.5.18 (gowa-autodevice)">
|
||||
<img src="https://img.shields.io/badge/versi-0.5.19_restart--notice-ADFF2F" alt="Versi 0.5.19 (restart-notice)">
|
||||
<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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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": {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.5.18
|
||||
0.5.19
|
||||
@@ -241,3 +241,31 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
return false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<div id="restart-fab" style="display:none;position:fixed;right:18px;bottom:84px;z-index:9999;max-width:330px">
|
||||
<div style="background:#7c2d12;color:#fff;border:1px solid #f59e0b;border-radius:12px;padding:12px 14px;box-shadow:0 10px 30px rgba(0,0,0,.35);font-size:13px;line-height:1.45">
|
||||
<b><i class="bi bi-exclamation-triangle-fill"></i> Billing perlu restart</b>
|
||||
<div id="restart-fab-text" style="margin-top:4px;color:#fde68a">Update sudah diterapkan, tetapi kode lama masih berjalan.</div>
|
||||
<a href="/admin/update" style="display:inline-block;margin-top:8px;background:#f59e0b;color:#111;font-weight:700;padding:6px 10px;border-radius:8px;text-decoration:none">Cara restart</a>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
function checkRestartRequired() {
|
||||
fetch('/admin/api/restart-required', { cache: 'no-store' })
|
||||
.then(function (r) { return r.json(); })
|
||||
.then(function (d) {
|
||||
var el = document.getElementById('restart-fab');
|
||||
if (!el) return;
|
||||
el.style.display = (d && d.pending) ? 'block' : 'none';
|
||||
if (d && d.pending && d.version) {
|
||||
var t = document.getElementById('restart-fab-text');
|
||||
if (t) t.textContent = 'Update v' + d.version + ' sudah diterapkan. Restart aplikasi agar fitur/fix baru aktif.';
|
||||
}
|
||||
})
|
||||
.catch(function () {});
|
||||
}
|
||||
checkRestartRequired();
|
||||
setInterval(checkRestartRequired, 30000);
|
||||
})();
|
||||
</script>
|
||||
|
||||
@@ -23,6 +23,18 @@
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<% if (typeof restartPending !== 'undefined' && restartPending) { %>
|
||||
<div class="alert alert-d" style="display:flex;gap:10px;align-items:flex-start">
|
||||
<i class="bi bi-exclamation-triangle-fill" style="font-size:18px;margin-top:1px"></i>
|
||||
<div>
|
||||
<b>Restart diperlukan.</b>
|
||||
Update ke versi <b><%= restartPending.version || '-' %></b> sudah diterapkan ke file,
|
||||
tetapi aplikasi masih menjalankan kode lama. Fitur/fix baru belum aktif sampai aplikasi di-restart.
|
||||
<a href="#restart-help" style="color:inherit;text-decoration:underline;font-weight:700">Lihat cara restart</a>.
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="card mb16">
|
||||
<div class="card-hd"><h6><i class="bi bi-git"></i> <%= t('admin.update.status_title', 'Status Versi') %></h6></div>
|
||||
<div class="card-bd">
|
||||
@@ -79,7 +91,7 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb16">
|
||||
<div class="card mb16" id="restart-help">
|
||||
<div class="card-hd"><h6><i class="bi bi-arrow-repeat"></i> <%= t('admin.update.restart_title', 'Cara Restart Aplikasi') %></h6></div>
|
||||
<div class="card-bd" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:14px;font-size:12px">
|
||||
<div style="border:1px solid var(--border);border-radius:12px;padding:12px 14px;background:rgba(255,255,255,.02);line-height:1.75">
|
||||
|
||||
Reference in New Issue
Block a user