Watch EPIRent version and archive its download packages

- Poll the public download page for "Aktuelle Version <nr> vom <date>",
  report changes through the normal change feed and notification mail,
  and show the current version as a card on the overview. Runs on its own
  interval (default 30 min); the first run only records a baseline.
- Archive epirentServer.zip / epirentClient.zip per version number so we
  keep a download cache and history: streamed to disk with SHA-256 and
  size, fetched in a background thread so ticket polling is unaffected,
  with a free-space check before downloading (~1.1 GB per version).
- Retention is configurable via EPIRENT_DOWNLOAD_KEEP, where 0 means
  unlimited; a successful archive is reported as a change and by mail.
- New Downloads page listing the archived versions with checksums and
  links to fetch the cached files.
- Notification lines now omit the arrow when there is no previous value
  and drop the "#" for entries without a ticket.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-20 15:12:48 +02:00
co-authored by Claude Opus 4.8
parent 528d66b2b0
commit bdcb17285c
13 changed files with 575 additions and 10 deletions
+19 -1
View File
@@ -5,7 +5,7 @@ import logging
import time
from datetime import datetime, timezone
from . import config, conversation, db, notify
from . import config, conversation, db, downloads, epirent, notify
from .zammad import ZammadClient, ZammadError
log = logging.getLogger("episupport.poller")
@@ -138,6 +138,18 @@ def _detect_changes(conn, client, new_t: dict, old_row) -> list[dict]:
return found
def _archive_downloads() -> None:
"""Download-Pakete der aktuellen EPIRent-Version sichern (im Hintergrund)."""
if not config.EPIRENT_DOWNLOAD_ENABLED:
return
cur = epirent.current()
if not cur or not cur.get("version"):
return
if downloads.is_complete(cur["version"]):
return
downloads.archive_async(cur["version"])
def sync_ticket(client: ZammadClient, ticket_id: int) -> None:
"""Ein einzelnes Ticket (+ Artikel) sofort in die DB übernehmen.
@@ -205,6 +217,12 @@ def poll_once(client: ZammadClient) -> int:
all_changes.extend(ch)
# Backfill NACH der Haupttransaktion (eigene, kurze Verbindungen)
_backfill_articles(client)
# EPIRent-Version auf der Download-Seite prüfen (eigenes Intervall)
version_change = epirent.check()
if version_change:
all_changes.append(version_change)
# Pakete dieser Version im Hintergrund archivieren, falls noch nicht da
_archive_downloads()
except ZammadError as e:
status = "error"
error = str(e)