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
+8 -3
View File
@@ -22,18 +22,23 @@ def send_change_mail(changes: list[dict]) -> None:
lines = []
for c in changes:
line = f"#{c['ticket_number']} {c['ticket_title']}\n {c['label']}: {c.get('old_value') or ''} -> {c.get('new_value') or ''}"
# Einträge ohne Ticketbezug (z. B. EPIRent-Version) ohne "#" ausgeben
head = f"#{c['ticket_number']} " if c.get("ticket_number") else ""
new = c.get("new_value") or ""
# ohne Vorwert (z. B. neues Ticket, Download-Archiv) keinen Pfeil setzen
value = f"{c['old_value']} -> {new}" if c.get("old_value") else new
line = f"{head}{c['ticket_title']}\n {c['label']}: {value}"
if c.get("detail"):
line += f"\n {c['detail']}"
lines.append(line)
body = (
f"Es gibt {len(changes)} neue Änderung(en) an euren EPI-Support-Tickets:\n\n"
f"Es gibt {len(changes)} neue Änderung(en):\n\n"
+ "\n\n".join(lines)
+ f"\n\nDashboard: {config.ZAMMAD_URL}\n(Quelle: EPI-Support-Monitor)"
)
msg = EmailMessage()
msg["Subject"] = f"[EPI-Support] {len(changes)} Ticket-Änderung(en)"
msg["Subject"] = f"[EPI-Support] {len(changes)} Änderung(en)"
msg["From"] = config.SMTP_FROM
msg["To"] = ", ".join(config.NOTIFY_TO)
msg.set_content(body)