diff --git a/deploy/pzmanager.service b/deploy/pzmanager.service index b4e1c58..1815edd 100644 --- a/deploy/pzmanager.service +++ b/deploy/pzmanager.service @@ -19,7 +19,11 @@ KillMode=mixed TimeoutStopSec=300 # Всё своё панель держит в /opt/pzmanager, домашние каталоги ей не нужны. -NoNewPrivileges=true +# +# NoNewPrivileges здесь намеренно не выставлен: он запрещает процессу и его +# потомкам повышать привилегии, а значит ломает sudo — и вместе с ним кнопки +# обновления и перезапуска панели. Прикрытием служит sudoers: сервисному +# пользователю разрешена ровно одна команда, pzmanager-admin. PrivateTmp=true ProtectSystem=full ProtectHome=true diff --git a/web/static/app.js b/web/static/app.js index 6543bb8..41e78bd 100644 --- a/web/static/app.js +++ b/web/static/app.js @@ -1241,7 +1241,7 @@ async function loadUpdateInfo() { const notes = $("#update-notes"); notes.hidden = !(info.available && info.notes); - if (!notes.hidden) $("#update-notes-body").textContent = info.notes; + if (!notes.hidden) renderReleaseNotes(info.notes, info.url); $("#btn-manager-restart").disabled = !info.can_apply; $("#btn-manager-restart").title = info.can_apply @@ -1249,6 +1249,47 @@ async function loadUpdateInfo() { : "Недоступно: панель не может управлять своей службой"; } +// Описание релиза приходит из Gitea разметкой Markdown, и показывать её как +// есть неудобно: первым идёт раздел про установку, который в уже работающей +// панели ни к чему. Берём только список изменений. +function renderReleaseNotes(text, url) { + const body = $("#update-notes-body"); + body.replaceChildren(); + + const changesSection = text.split(/^##\s+Изменения\s*$/m)[1]; + const items = (changesSection ?? text) + .split("\n") + .map((line) => line.trim()) + .filter((line) => line.startsWith("- ")) + .map((line) => line.slice(2).trim()) + .filter(Boolean); + + if (items.length) { + const list = document.createElement("ul"); + items.forEach((item) => { + const li = document.createElement("li"); + li.textContent = item; + list.appendChild(li); + }); + body.appendChild(list); + } else { + // Непривычный формат описания — показываем как есть, лишь бы не потерять. + const raw = document.createElement("pre"); + raw.textContent = text.trim(); + body.appendChild(raw); + } + + if (url) { + const link = document.createElement("a"); + link.href = url; + link.target = "_blank"; + link.rel = "noopener noreferrer"; + link.className = "release-link"; + link.textContent = "Полное описание релиза"; + body.appendChild(link); + } +} + $("#btn-update-check").addEventListener("click", async () => { const button = $("#btn-update-check"); button.disabled = true; diff --git a/web/static/index.html b/web/static/index.html index 380aaa1..54056a8 100644 --- a/web/static/index.html +++ b/web/static/index.html @@ -284,7 +284,7 @@