From 6e5bb5eac17ebb4d95690e6d2046031e5396639f Mon Sep 17 00:00:00 2001 From: root Date: Fri, 9 Oct 2026 19:25:57 +0200 Subject: [PATCH] =?UTF-8?q?feat(ops):=20v=C3=A6rtens=20apt-sikkerhedsopdat?= =?UTF-8?q?eringer=20p=C3=A5=20driftssiden?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Proxmox VE får 'nyeste' fra værtens apt-kandidat, ny sektion med sikkerhedspakker/genstartsbehov, ntfy ved nye sikkerhedsopdateringer. _norm stripper release-præfiks (qBittorrent). Claude-Session: https://claude.ai/code/session_01AUj1LSMmrowcomkn1ciAgj --- main.py | 3 ++- templates/admin_ops.html | 2 +- templates/admin_ops_body.html | 21 +++++++++++++++++++++ versions.py | 16 +++++++++++++--- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/main.py b/main.py index 81b3230..0b89de4 100644 --- a/main.py +++ b/main.py @@ -245,7 +245,8 @@ async def ops_content(request: Request): version, rp, vers = await asyncio.gather( svc.assets_version(), svc.rp_status(), versions.all_versions()) return templates.TemplateResponse(request, "admin_ops_body.html", { - "request": request, "version": version, "rp": rp, "versions": vers}) + "request": request, "version": version, "rp": rp, "versions": vers, + "host": versions.host_updates()}) @app.get("/admin/stats", response_class=HTMLResponse) diff --git a/templates/admin_ops.html b/templates/admin_ops.html index 5e78a6e..1dc2045 100644 --- a/templates/admin_ops.html +++ b/templates/admin_ops.html @@ -97,7 +97,7 @@ var fstate = { q:'', mode:'all', level:'all' }; // Filter ligger i shell (persistent) og genanvendes efter hver poll → fokus/værdi bevares. function applyFilter(){ - var rows = box.querySelectorAll('.vtable tbody tr'), shown = 0; + var rows = box.querySelectorAll('.vtable tbody tr[data-name]'), shown = 0; rows.forEach(function(tr){ var name = tr.getAttribute('data-name') || '', st = tr.getAttribute('data-status'), lvl = tr.getAttribute('data-level'), diff --git a/templates/admin_ops_body.html b/templates/admin_ops_body.html index f65cdec..952b938 100644 --- a/templates/admin_ops_body.html +++ b/templates/admin_ops_body.html @@ -41,6 +41,27 @@ +{% if host %} +

Proxmox-vært (apt) + {% if host.security %}🔒 {{ host.security }} sikkerhedsopdatering{{ 'er' if host.security != 1 }} + {% else %}ingen sikkerhedsopdateringer{% endif %} + {% if host.upgradable %}{{ host.upgradable }} pakker i alt{% endif %} + {% if host.reboot_required %}genstart krævet{% if host.new_kernel %} (ny kerne){% endif %}{% endif %} +

+

Kerne {{ host.kernel_running }}{% if host.new_kernel %} → {{ host.new_kernel }}{% endif %} + · apt-lister opdateret {{ (host.apt_checked or '?')|replace('T',' ') }} + · opdatér med apt update && apt dist-upgrade på værten

+{% if host.security_pkgs %} +
Vis sikkerhedspakker ({{ host.security }}) + + + {% for p in host.security_pkgs %} + + {% endfor %} +
PakkeInstalleretNy
{{ p.name }}{{ p['from'] or '—' }}{{ p.to }}
+{% endif %} +{% endif %} +

Static-assets

{{ version if version else '—' }}
diff --git a/versions.py b/versions.py index ee1894e..6741a8f 100644 --- a/versions.py +++ b/versions.py @@ -74,8 +74,8 @@ SERVICES = [ def _norm(v): if not v: return "" - # behold dato-versioner (fx NPMplus 2026-04-21-r2) — strip kun v-præfiks + build-metadata - return re.sub(r"^[vV]", "", str(v).strip()).split("+")[0].strip().lower() + # behold dato-versioner (fx NPMplus 2026-04-21-r2) — strip kun v-/release-præfiks + build-metadata + return re.sub(r"^(release-)?[vV]?", "", str(v).strip()).split("+")[0].strip().lower() async def _latest(src, client, store): @@ -229,7 +229,8 @@ async def _one(name, kind, repo, client, local, store): cur, lat_builtin = await _current(kind, client) if not cur: cur = local.get(name) # vært-indsamlet snapshot for LXC-tjenester - latest = lat_builtin or await _latest(repo, client, store) + # _latest i snapshot'et = nyeste fra værten selv (fx apt-kandidat for pve-manager) + latest = lat_builtin or await _latest(repo, client, store) or local.get("_latest", {}).get(name) eol = await _eol(name, cur, client) level = None if cur and latest: @@ -263,6 +264,10 @@ async def _maybe_alert(res, client): if e and e.get("state") in ("eol", "soon"): sig = f"eol:{v['name']}:{e.get('date')}" cur.add(sig); detail[sig] = f"⏳ {v['name']} EOL {e.get('date')}" + h = host_updates() + if h and h.get("security"): + sig = "sec:host:" + ",".join(sorted(f"{p['name']}={p['to']}" for p in h["security_pkgs"])) + cur.add(sig); detail[sig] = f"🔒 Proxmox-vært: {h['security']} sikkerhedsopdateringer" try: stored = set(json.loads(ALERT_STATE.read_text("utf-8"))) except Exception: @@ -285,6 +290,11 @@ async def _maybe_alert(res, client): ALERT_STATE.write_text(json.dumps(sorted(cur)), "utf-8") +def host_updates(): + """Værtens apt-status (indsamlet af portal-versions.py) eller None.""" + return _load_local().get("_host") + + async def all_versions(): hit = cache.get("ver:all") if hit is not None: