diff --git a/Dockerfile b/Dockerfile
index 2618499..52ff427 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,7 +2,7 @@ FROM python:3.11-slim
WORKDIR /app
-RUN pip install fastapi jinja2 uvicorn requests python-dateutil python-multipart
+RUN pip install fastapi jinja2 uvicorn requests python-dateutil
COPY main.py .
COPY reference_versions.json ./reference_versions.json
diff --git a/gen_ref_vers.py b/gen_ref_vers.py
deleted file mode 100644
index 88b59b2..0000000
--- a/gen_ref_vers.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import requests
-import re
-import json
-
-# Logischer Name (wie im UI) → Launchpad-Quellpaketname
-PACKAGES = {
- "python3": "python3-defaults",
- "nginx": "nginx",
- "openssh-server": "openssh"
-}
-
-# Ubuntu-Release (muss auf launchpad.net vorhanden sein)
-UBUNTU_RELEASE = "noble"
-
-def get_version(pkg_source, release=UBUNTU_RELEASE):
- try:
- url = f"https://launchpad.net/ubuntu/{release}/+source/{pkg_source}"
- r = requests.get(url, timeout=10)
- if r.status_code != 200:
- print(f"⚠️ Fehler {r.status_code} bei {url}")
- return None
-
- # Suche nach: Current version:
VERSION
- match = re.search(r'Current version:\s*([^<]+)', r.text)
- return match.group(1).strip() if match else None
- except Exception as e:
- print(f"❌ Fehler bei {pkg_source}: {e}")
- return None
-
-def generate():
- result = {}
- for display_name, source_name in PACKAGES.items():
- version = get_version(source_name)
- if version:
- result[display_name] = version
- else:
- print(f"❌ Keine Version gefunden für {display_name}")
- return result
-
-if __name__ == "__main__":
- versions = generate()
- with open("reference_versions.json", "w") as f:
- json.dump(versions, f, indent=2)
- print("✅ Referenz gespeichert in reference_versions.json")
diff --git a/main.py b/main.py
index aa88466..7c0f32d 100644
--- a/main.py
+++ b/main.py
@@ -1,10 +1,9 @@
-from fastapi import FastAPI, Request, Form
-from fastapi.responses import HTMLResponse, RedirectResponse
+from fastapi import FastAPI
+from fastapi.responses import HTMLResponse
from jinja2 import Template
import requests
import json
from dateutil import parser, tz
-from pathlib import Path
app = FastAPI()
@@ -12,6 +11,104 @@ API_URL = "http://192.168.18.4:8000/results/updatecheck/"
READ_API_KEY = "read_key_1"
REFERENCE_FILE = "reference_versions.json"
+# ================= Templates ==================
+
+MAIN_TEMPLATE = Template("""
+
+
+
+
+ UpdateLog Übersicht
+
+
+
+
+ UpdateLog Übersicht
+ {% for server, content in data.items() %}
+
+
+
🔍 Verlauf anzeigen
+
+
+ Paket | Installiert | Referenz | Status |
+ {% for pkg, info in content.packages.items() %}
+
+ {{ pkg }} |
+ {{ info.current }} |
+ {{ info.expected }} |
+ {{ '✅' if info.current == info.expected else '❌' }} |
+
+ {% endfor %}
+
+
+
+ {% endfor %}
+
+
+""")
+
+HISTORY_TEMPLATE = Template("""
+
+
+
+
+ Verlauf: {{ hostname }}
+
+
+
+ Update-Verlauf für {{ hostname }}
+ ⬅ Zurück zur Übersicht
+ {% for entry in history %}
+ {{ entry.timestamp }}
+
+ Paket | Installiert |
+ {% for pkg, ver in entry.packages.items() %}
+
+ {{ pkg }} |
+ {{ ver }} |
+
+ {% endfor %}
+
+ {% endfor %}
+
+
+""")
+
+# ================= Routes ==================
+
+@app.get("/", response_class=HTMLResponse)
+def index():
+ data = fetch_latest_per_host()
+ return MAIN_TEMPLATE.render(data=data)
+
+@app.get("/host/{hostname}", response_class=HTMLResponse)
+def host_history(hostname: str):
+ history = fetch_history_for_host(hostname)
+ return HISTORY_TEMPLATE.render(hostname=hostname, history=history)
+
+# ================= Datenfunktionen ==================
+
def fetch_raw_lines():
try:
response = requests.get(API_URL, headers={"Authorization": f"Bearer {READ_API_KEY}"}, timeout=5)
@@ -25,46 +122,52 @@ def fetch_reference():
try:
with open(REFERENCE_FILE) as f:
return json.load(f)
- except Exception:
+ except Exception as e:
+ print(f"❌ Fehler beim Laden der Referenzversionen: {e}")
return {}
def fetch_latest_per_host():
lines = fetch_raw_lines()
reference = fetch_reference()
- latest = {}
+ latest_by_host = {}
for line in lines:
parts = line.strip().split(",", 3)
if len(parts) < 4:
continue
timestamp, hostname, _, packages = parts
- if hostname not in latest or timestamp > latest[hostname]["timestamp"]:
- latest[hostname] = {
+ if hostname not in latest_by_host or timestamp > latest_by_host[hostname]["timestamp"]:
+ latest_by_host[hostname] = {
"timestamp": timestamp,
"raw": packages
}
result = {}
- for host, entry in latest.items():
+ for hostname, entry in latest_by_host.items():
+ # Zeitstempel umrechnen
try:
- dt = parser.isoparse(entry["timestamp"]).astimezone(tz.gettz("Europe/Berlin"))
- display_time = dt.strftime("%Y-%m-%d %H:%M:%S")
- except:
+ dt_utc = parser.isoparse(entry["timestamp"])
+ dt_local = dt_utc.astimezone(tz.gettz("Europe/Berlin"))
+ display_time = dt_local.strftime("%Y-%m-%d %H:%M:%S")
+ except Exception:
display_time = entry["timestamp"]
- pkgs = {}
- for p in entry["raw"].strip(";").split(";"):
- if "=" in p:
- k, v = p.split("=", 1)
- pkgs[k] = {
- "current": v,
- "expected": reference.get(k)
+ package_str = entry["raw"]
+ result[hostname] = {
+ "timestamp": display_time,
+ "packages": {}
+ }
+ for item in package_str.strip(";").split(";"):
+ if "=" not in item:
+ continue
+ pkg, version = item.split("=", 1)
+ expected = reference.get(pkg)
+ if expected:
+ result[hostname]["packages"][pkg] = {
+ "current": version,
+ "expected": expected
}
- result[host] = {
- "timestamp": display_time,
- "packages": pkgs
- }
return result
def fetch_history_for_host(hostname):
@@ -72,143 +175,20 @@ def fetch_history_for_host(hostname):
history = []
for line in lines:
parts = line.strip().split(",", 3)
- if len(parts) < 4 or parts[1] != hostname:
+ if len(parts) < 4:
continue
- timestamp, _, _, raw = parts
- pkgs = {}
- for p in raw.strip(";").split(";"):
- if "=" in p:
- k, v = p.split("=", 1)
- pkgs[k] = v
- history.append({"timestamp": timestamp, "packages": pkgs})
+ timestamp, host, _, packages = parts
+ if host != hostname:
+ continue
+ parsed = {}
+ for item in packages.strip(";").split(";"):
+ if "=" not in item:
+ continue
+ pkg, version = item.split("=", 1)
+ parsed[pkg] = version
+ history.append({
+ "timestamp": timestamp,
+ "packages": parsed
+ })
history.sort(key=lambda x: x["timestamp"], reverse=True)
return history
-
-@app.get("/", response_class=HTMLResponse)
-def index():
- data = fetch_latest_per_host()
- return MAIN_TEMPLATE.render(data=data)
-
-@app.get("/host/{hostname}", response_class=HTMLResponse)
-def host_view(hostname: str):
- return HOST_TEMPLATE.render(hostname=hostname, history=fetch_history_for_host(hostname))
-
-@app.get("/edit", response_class=HTMLResponse)
-def edit_view():
- try:
- data = Path(REFERENCE_FILE).read_text()
- except:
- data = "{}"
- return EDIT_TEMPLATE.render(content=data)
-
-@app.post("/edit", response_class=RedirectResponse)
-def edit_post(content: str = Form(...)):
- try:
- json.loads(content)
- Path(REFERENCE_FILE).write_text(content)
- except Exception as e:
- print(f"Fehler beim Schreiben der Referenzdatei: {e}")
- return RedirectResponse(url="/edit", status_code=303)
-
-# ========== Templates ==========
-
-MAIN_TEMPLATE = Template("""
-
-
-
-
- Update Übersicht
-
-
-
-
- Update Übersicht
-
- ⚙️ Referenz bearbeiten
- {% for host, d in data.items() %}
-
-
-
🔍 Verlauf
-
-
- Paket | Installiert | Referenz | Status |
- {% for p, info in d.packages.items() %}
-
- {{ p }} |
- {{ info.current }} |
- {{ info.expected or '–' }} |
-
- {% if not info.expected %}❓ fehlt
- {% elif info.current != info.expected %}❌
- {% else %}✅
- {% endif %}
- |
-
- {% endfor %}
-
-
-
- {% endfor %}
-
-
-""")
-
-HOST_TEMPLATE = Template("""
-
-
-{{ hostname }}
-
-
- Verlauf: {{ hostname }}
- ⬅ Zurück
- {% for h in history %}
- {{ h.timestamp }}
-
- Paket | Version |
- {% for p, v in h.packages.items() %}
- {{ p }} | {{ v }} |
- {% endfor %}
-
- {% endfor %}
-
-
-""")
-
-EDIT_TEMPLATE = Template("""
-
-
-Referenz bearbeiten
-
-
-Referenzdatei bearbeiten
-⬅ Zurück
-
-
-
-""")
diff --git a/reference_versions.json b/reference_versions.json
index 7854f28..987f8ef 100644
--- a/reference_versions.json
+++ b/reference_versions.json
@@ -1,5 +1,4 @@
{
"python3": "3.12.3-0ubuntu2",
- "nginx": "1.24.0-2ubuntu7.4",
- "openssh-server": "1:9.6p1-3ubuntu13.12"
-}
\ No newline at end of file
+ "openssh-server": "1:9.6p1-3ubuntu13.11"
+}
diff --git a/run_container.sh b/run_container.sh
index 4d69bc0..ca61608 100755
--- a/run_container.sh
+++ b/run_container.sh
@@ -13,5 +13,10 @@ docker build -t $IMAGE_NAME .
docker run -d \
--name $CONTAINER_NAME \
-p $PORT:8080 \
- -v "$(realpath ./reference_versions.json)":/app/reference_versions.json \
+ --read-only \
+ --tmpfs /tmp \
+ --cap-drop ALL \
+ --security-opt no-new-privileges \
$IMAGE_NAME
+
+echo "✅ Update-UI läuft unter http://localhost:$PORT"
diff --git a/send-update-log-win.ps1 b/send-update-log-win.ps1
deleted file mode 100644
index 7ef3731..0000000
--- a/send-update-log-win.ps1
+++ /dev/null
@@ -1,41 +0,0 @@
-# Konfiguration
-$apiUrl = "http://192.168.18.4:8000/api/updatecheck/"
-$apiToken = "9d207bf0-10f5-4d8f-a479-22ff5aeff8d1"
-
-# Hostname und Zeit
-$hostname = $env:COMPUTERNAME
-$timestamp = (Get-Date).ToString("yyyy-MM-ddTHH:mm:sszzz")
-
-# OS- und Kernel-Info
-$os = (Get-CimInstance Win32_OperatingSystem).Caption
-$kernel = (Get-CimInstance Win32_OperatingSystem).Version
-
-# Installierte Pakete mit DisplayName
-$raw = Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\* |
- Where-Object { $_.DisplayName -and ($_.DisplayName -match 'Python|OpenSSH|nginx') } |
- ForEach-Object { "$($_.DisplayName)=unknown" }
-
-# Fallback
-if (-not $raw) {
- $raw = @("none=none")
-}
-
-# Paketzeichenfolge zusammenbauen
-$packageString = "os=$os;kernel=$kernel;" + ($raw -join ";")
-
-# Body zusammenbauen
-$body = "$hostname,$timestamp,$packageString"
-
-# Header
-$headers = @{
- "Authorization" = "Bearer $apiToken"
-}
-
-# Abschicken
-try {
- Invoke-RestMethod -Uri $apiUrl -Method Post -Headers $headers -Body $body
- Write-Host "✅ Update-Log gesendet von $hostname"
-}
-catch {
- Write-Warning "❌ Fehler beim Senden: $_"
-}
diff --git a/send-update.log.sh b/send-update.log.sh
deleted file mode 100644
index 687bd88..0000000
--- a/send-update.log.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/bin/bash
-
-API_URL="http://192.168.18.4:8000/api/updatecheck/"
-API_TOKEN="9d207bf0-10f5-4d8f-a479-22ff5aeff8d1"
-
-HOSTNAME=$(hostname)
-TIMESTAMP=$(date -Iseconds)
-OS=$(lsb_release -ds 2>/dev/null || echo "Unknown")
-KERNEL=$(uname -r)
-
-# Pakete
-PACKAGES="python3|openssh-server|nginx"
-PACKAGE_VERSIONS=$(dpkg-query -W -f='${binary:Package}=${Version}\n' 2>/dev/null | grep -E "$PACKAGES" | tr '\n' ';')
-
-# Zusammenbauen
-BODY="$HOSTNAME,$TIMESTAMP,os=$OS;kernel=$KERNEL;$PACKAGE_VERSIONS"
-
-curl -s -X POST "$API_URL" \
- -H "Authorization: Bearer $API_TOKEN" \
- -d "$BODY"