Compare commits
3 commits
029caa8565
...
68a6b0a8d7
Author | SHA1 | Date | |
---|---|---|---|
68a6b0a8d7 | |||
afe55d3c37 | |||
06515b8d43 |
7 changed files with 269 additions and 148 deletions
|
@ -2,7 +2,7 @@ FROM python:3.11-slim
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
RUN pip install fastapi jinja2 uvicorn requests python-dateutil
|
RUN pip install fastapi jinja2 uvicorn requests python-dateutil python-multipart
|
||||||
|
|
||||||
COPY main.py .
|
COPY main.py .
|
||||||
COPY reference_versions.json ./reference_versions.json
|
COPY reference_versions.json ./reference_versions.json
|
||||||
|
|
44
gen_ref_vers.py
Normal file
44
gen_ref_vers.py
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
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: <dd>VERSION</dd>
|
||||||
|
match = re.search(r'Current version:</dt>\s*<dd>([^<]+)</dd>', 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")
|
298
main.py
298
main.py
|
@ -1,9 +1,10 @@
|
||||||
from fastapi import FastAPI
|
from fastapi import FastAPI, Request, Form
|
||||||
from fastapi.responses import HTMLResponse
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||||
from jinja2 import Template
|
from jinja2 import Template
|
||||||
import requests
|
import requests
|
||||||
import json
|
import json
|
||||||
from dateutil import parser, tz
|
from dateutil import parser, tz
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
|
|
||||||
|
@ -11,104 +12,6 @@ API_URL = "http://192.168.18.4:8000/results/updatecheck/"
|
||||||
READ_API_KEY = "read_key_1"
|
READ_API_KEY = "read_key_1"
|
||||||
REFERENCE_FILE = "reference_versions.json"
|
REFERENCE_FILE = "reference_versions.json"
|
||||||
|
|
||||||
# ================= Templates ==================
|
|
||||||
|
|
||||||
MAIN_TEMPLATE = Template("""
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>UpdateLog Übersicht</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: sans-serif; margin: 2rem; }
|
|
||||||
button { margin: 0.2rem 0; padding: 0.4rem 1rem; cursor: pointer; }
|
|
||||||
table { border-collapse: collapse; width: 100%%; margin-top: 0.5rem; }
|
|
||||||
th, td { border: 1px solid #ccc; padding: 6px 12px; text-align: left; }
|
|
||||||
th { background-color: #f4f4f4; }
|
|
||||||
.ok { background-color: #d4edda; }
|
|
||||||
.outdated { background-color: #f8d7da; }
|
|
||||||
.host-block { margin-bottom: 2rem; }
|
|
||||||
</style>
|
|
||||||
<script>
|
|
||||||
function toggle(hostId) {
|
|
||||||
const el = document.getElementById(hostId);
|
|
||||||
el.style.display = (el.style.display === "none") ? "block" : "none";
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>UpdateLog Übersicht</h1>
|
|
||||||
{% for server, content in data.items() %}
|
|
||||||
<div class="host-block">
|
|
||||||
<button onclick="toggle('host-{{ server }}')">
|
|
||||||
{{ server }} (Stand: {{ content.timestamp }})
|
|
||||||
</button>
|
|
||||||
<a href="/host/{{ server }}" style="margin-left: 1rem;">🔍 Verlauf anzeigen</a>
|
|
||||||
<div id="host-{{ server }}" style="display:none">
|
|
||||||
<table>
|
|
||||||
<tr><th>Paket</th><th>Installiert</th><th>Referenz</th><th>Status</th></tr>
|
|
||||||
{% for pkg, info in content.packages.items() %}
|
|
||||||
<tr class="{{ 'ok' if info.current == info.expected else 'outdated' }}">
|
|
||||||
<td>{{ pkg }}</td>
|
|
||||||
<td>{{ info.current }}</td>
|
|
||||||
<td>{{ info.expected }}</td>
|
|
||||||
<td>{{ '✅' if info.current == info.expected else '❌' }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
""")
|
|
||||||
|
|
||||||
HISTORY_TEMPLATE = Template("""
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>Verlauf: {{ hostname }}</title>
|
|
||||||
<style>
|
|
||||||
body { font-family: sans-serif; margin: 2rem; }
|
|
||||||
table { border-collapse: collapse; width: 100%%; margin-top: 1rem; }
|
|
||||||
th, td { border: 1px solid #ccc; padding: 6px 12px; text-align: left; }
|
|
||||||
th { background-color: #f4f4f4; }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<h1>Update-Verlauf für {{ hostname }}</h1>
|
|
||||||
<a href="/">⬅ Zurück zur Übersicht</a>
|
|
||||||
{% for entry in history %}
|
|
||||||
<h3>{{ entry.timestamp }}</h3>
|
|
||||||
<table>
|
|
||||||
<tr><th>Paket</th><th>Installiert</th></tr>
|
|
||||||
{% for pkg, ver in entry.packages.items() %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ pkg }}</td>
|
|
||||||
<td>{{ ver }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
|
||||||
</table>
|
|
||||||
{% endfor %}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
""")
|
|
||||||
|
|
||||||
# ================= 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():
|
def fetch_raw_lines():
|
||||||
try:
|
try:
|
||||||
response = requests.get(API_URL, headers={"Authorization": f"Bearer {READ_API_KEY}"}, timeout=5)
|
response = requests.get(API_URL, headers={"Authorization": f"Bearer {READ_API_KEY}"}, timeout=5)
|
||||||
|
@ -122,52 +25,46 @@ def fetch_reference():
|
||||||
try:
|
try:
|
||||||
with open(REFERENCE_FILE) as f:
|
with open(REFERENCE_FILE) as f:
|
||||||
return json.load(f)
|
return json.load(f)
|
||||||
except Exception as e:
|
except Exception:
|
||||||
print(f"❌ Fehler beim Laden der Referenzversionen: {e}")
|
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
def fetch_latest_per_host():
|
def fetch_latest_per_host():
|
||||||
lines = fetch_raw_lines()
|
lines = fetch_raw_lines()
|
||||||
reference = fetch_reference()
|
reference = fetch_reference()
|
||||||
latest_by_host = {}
|
latest = {}
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
parts = line.strip().split(",", 3)
|
parts = line.strip().split(",", 3)
|
||||||
if len(parts) < 4:
|
if len(parts) < 4:
|
||||||
continue
|
continue
|
||||||
timestamp, hostname, _, packages = parts
|
timestamp, hostname, _, packages = parts
|
||||||
if hostname not in latest_by_host or timestamp > latest_by_host[hostname]["timestamp"]:
|
if hostname not in latest or timestamp > latest[hostname]["timestamp"]:
|
||||||
latest_by_host[hostname] = {
|
latest[hostname] = {
|
||||||
"timestamp": timestamp,
|
"timestamp": timestamp,
|
||||||
"raw": packages
|
"raw": packages
|
||||||
}
|
}
|
||||||
|
|
||||||
result = {}
|
result = {}
|
||||||
for hostname, entry in latest_by_host.items():
|
for host, entry in latest.items():
|
||||||
# Zeitstempel umrechnen
|
|
||||||
try:
|
try:
|
||||||
dt_utc = parser.isoparse(entry["timestamp"])
|
dt = parser.isoparse(entry["timestamp"]).astimezone(tz.gettz("Europe/Berlin"))
|
||||||
dt_local = dt_utc.astimezone(tz.gettz("Europe/Berlin"))
|
display_time = dt.strftime("%Y-%m-%d %H:%M:%S")
|
||||||
display_time = dt_local.strftime("%Y-%m-%d %H:%M:%S")
|
except:
|
||||||
except Exception:
|
|
||||||
display_time = entry["timestamp"]
|
display_time = entry["timestamp"]
|
||||||
|
|
||||||
package_str = entry["raw"]
|
pkgs = {}
|
||||||
result[hostname] = {
|
for p in entry["raw"].strip(";").split(";"):
|
||||||
"timestamp": display_time,
|
if "=" in p:
|
||||||
"packages": {}
|
k, v = p.split("=", 1)
|
||||||
}
|
pkgs[k] = {
|
||||||
for item in package_str.strip(";").split(";"):
|
"current": v,
|
||||||
if "=" not in item:
|
"expected": reference.get(k)
|
||||||
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
|
return result
|
||||||
|
|
||||||
def fetch_history_for_host(hostname):
|
def fetch_history_for_host(hostname):
|
||||||
|
@ -175,20 +72,143 @@ def fetch_history_for_host(hostname):
|
||||||
history = []
|
history = []
|
||||||
for line in lines:
|
for line in lines:
|
||||||
parts = line.strip().split(",", 3)
|
parts = line.strip().split(",", 3)
|
||||||
if len(parts) < 4:
|
if len(parts) < 4 or parts[1] != hostname:
|
||||||
continue
|
continue
|
||||||
timestamp, host, _, packages = parts
|
timestamp, _, _, raw = parts
|
||||||
if host != hostname:
|
pkgs = {}
|
||||||
continue
|
for p in raw.strip(";").split(";"):
|
||||||
parsed = {}
|
if "=" in p:
|
||||||
for item in packages.strip(";").split(";"):
|
k, v = p.split("=", 1)
|
||||||
if "=" not in item:
|
pkgs[k] = v
|
||||||
continue
|
history.append({"timestamp": timestamp, "packages": pkgs})
|
||||||
pkg, version = item.split("=", 1)
|
|
||||||
parsed[pkg] = version
|
|
||||||
history.append({
|
|
||||||
"timestamp": timestamp,
|
|
||||||
"packages": parsed
|
|
||||||
})
|
|
||||||
history.sort(key=lambda x: x["timestamp"], reverse=True)
|
history.sort(key=lambda x: x["timestamp"], reverse=True)
|
||||||
return history
|
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("""
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Update Übersicht</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: sans-serif; margin: 2rem; }
|
||||||
|
.ok { background: #d4edda; }
|
||||||
|
.outdated { background: #f8d7da; }
|
||||||
|
.missing { background: #fff3cd; }
|
||||||
|
table { border-collapse: collapse; width: 100%; margin-top: 1rem; }
|
||||||
|
th, td { border: 1px solid #ccc; padding: 4px 10px; }
|
||||||
|
#search { margin-bottom: 1rem; padding: 4px; width: 250px; }
|
||||||
|
</style>
|
||||||
|
<script>
|
||||||
|
function toggle(id) {
|
||||||
|
const el = document.getElementById(id);
|
||||||
|
el.style.display = el.style.display === "none" ? "block" : "none";
|
||||||
|
}
|
||||||
|
function filterTable() {
|
||||||
|
let q = document.getElementById("search").value.toLowerCase();
|
||||||
|
document.querySelectorAll(".host-block").forEach(div => {
|
||||||
|
div.style.display = div.innerText.toLowerCase().includes(q) ? "block" : "none";
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Update Übersicht</h1>
|
||||||
|
<input id="search" placeholder="🔍 Host oder Paket..." onkeyup="filterTable()">
|
||||||
|
<a href="/edit">⚙️ Referenz bearbeiten</a>
|
||||||
|
{% for host, d in data.items() %}
|
||||||
|
<div class="host-block">
|
||||||
|
<button onclick="toggle('b-{{ host }}')">{{ host }} (Stand: {{ d.timestamp }})</button>
|
||||||
|
<a href="/host/{{ host }}">🔍 Verlauf</a>
|
||||||
|
<div id="b-{{ host }}" style="display:none">
|
||||||
|
<table>
|
||||||
|
<tr><th>Paket</th><th>Installiert</th><th>Referenz</th><th>Status</th></tr>
|
||||||
|
{% for p, info in d.packages.items() %}
|
||||||
|
<tr class="{% if not info.expected %}missing{% elif info.current != info.expected %}outdated{% else %}ok{% endif %}">
|
||||||
|
<td>{{ p }}</td>
|
||||||
|
<td>{{ info.current }}</td>
|
||||||
|
<td>{{ info.expected or '–' }}</td>
|
||||||
|
<td>
|
||||||
|
{% if not info.expected %}❓ fehlt
|
||||||
|
{% elif info.current != info.expected %}❌
|
||||||
|
{% else %}✅
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""")
|
||||||
|
|
||||||
|
HOST_TEMPLATE = Template("""
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head><meta charset="utf-8"><title>{{ hostname }}</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: sans-serif; margin: 2rem; }
|
||||||
|
table { border-collapse: collapse; width: 100%; margin-top: 1rem; }
|
||||||
|
th, td { border: 1px solid #ccc; padding: 4px 10px; }
|
||||||
|
</style></head>
|
||||||
|
<body>
|
||||||
|
<h1>Verlauf: {{ hostname }}</h1>
|
||||||
|
<a href="/">⬅ Zurück</a>
|
||||||
|
{% for h in history %}
|
||||||
|
<h3>{{ h.timestamp }}</h3>
|
||||||
|
<table>
|
||||||
|
<tr><th>Paket</th><th>Version</th></tr>
|
||||||
|
{% for p, v in h.packages.items() %}
|
||||||
|
<tr><td>{{ p }}</td><td>{{ v }}</td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</table>
|
||||||
|
{% endfor %}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""")
|
||||||
|
|
||||||
|
EDIT_TEMPLATE = Template("""
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head><meta charset="utf-8"><title>Referenz bearbeiten</title>
|
||||||
|
<style>body { font-family: sans-serif; margin: 2rem; }</style>
|
||||||
|
</head><body>
|
||||||
|
<h1>Referenzdatei bearbeiten</h1>
|
||||||
|
<a href="/">⬅ Zurück</a>
|
||||||
|
<form method="post">
|
||||||
|
<textarea name="content" style="width:100%;height:400px;">{{ content }}</textarea><br>
|
||||||
|
<button type="submit">💾 Speichern</button>
|
||||||
|
</form>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
""")
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
{
|
{
|
||||||
"python3": "3.12.3-0ubuntu2",
|
"python3": "3.12.3-0ubuntu2",
|
||||||
"openssh-server": "1:9.6p1-3ubuntu13.11"
|
"nginx": "1.24.0-2ubuntu7.4",
|
||||||
|
"openssh-server": "1:9.6p1-3ubuntu13.12"
|
||||||
}
|
}
|
|
@ -13,10 +13,5 @@ docker build -t $IMAGE_NAME .
|
||||||
docker run -d \
|
docker run -d \
|
||||||
--name $CONTAINER_NAME \
|
--name $CONTAINER_NAME \
|
||||||
-p $PORT:8080 \
|
-p $PORT:8080 \
|
||||||
--read-only \
|
-v "$(realpath ./reference_versions.json)":/app/reference_versions.json \
|
||||||
--tmpfs /tmp \
|
|
||||||
--cap-drop ALL \
|
|
||||||
--security-opt no-new-privileges \
|
|
||||||
$IMAGE_NAME
|
$IMAGE_NAME
|
||||||
|
|
||||||
echo "✅ Update-UI läuft unter http://localhost:$PORT"
|
|
||||||
|
|
41
send-update-log-win.ps1
Normal file
41
send-update-log-win.ps1
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
# 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: $_"
|
||||||
|
}
|
20
send-update.log.sh
Normal file
20
send-update.log.sh
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
#!/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"
|
Loading…
Add table
Add a link
Reference in a new issue