feat: add backup-status service (port 9999, push API for borgmatic)
This commit is contained in:
parent
519eb66bef
commit
6ad9b1a93f
3 changed files with 79 additions and 0 deletions
5
backup-status/Dockerfile
Normal file
5
backup-status/Dockerfile
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
FROM python:3.12-slim
|
||||||
|
RUN pip install flask
|
||||||
|
WORKDIR /app
|
||||||
|
COPY app.py .
|
||||||
|
CMD ["python", "app.py"]
|
||||||
64
backup-status/app.py
Normal file
64
backup-status/app.py
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Backup Status API
|
||||||
|
Hosts push after borgmatic backup, n8n polls this endpoint.
|
||||||
|
GET / → JSON summary of all hosts
|
||||||
|
POST /push?host=<name>&status=ok|error&msg=<text> → update host status
|
||||||
|
"""
|
||||||
|
from flask import Flask, request, jsonify
|
||||||
|
import json, os, time
|
||||||
|
|
||||||
|
app = Flask(__name__)
|
||||||
|
DATA_FILE = "/data/backup_status.json"
|
||||||
|
|
||||||
|
def load():
|
||||||
|
if os.path.exists(DATA_FILE):
|
||||||
|
with open(DATA_FILE) as f:
|
||||||
|
return json.load(f)
|
||||||
|
return {}
|
||||||
|
|
||||||
|
def save(data):
|
||||||
|
os.makedirs(os.path.dirname(DATA_FILE), exist_ok=True)
|
||||||
|
with open(DATA_FILE, "w") as f:
|
||||||
|
json.dump(data, f, indent=2)
|
||||||
|
|
||||||
|
@app.route("/")
|
||||||
|
def status():
|
||||||
|
data = load()
|
||||||
|
now = time.time()
|
||||||
|
result = {}
|
||||||
|
for host, info in data.items():
|
||||||
|
age_h = (now - info.get("ts", 0)) / 3600
|
||||||
|
if age_h > 26:
|
||||||
|
s = "KEIN BACKUP"
|
||||||
|
elif info.get("status") == "error":
|
||||||
|
s = "FEHLER"
|
||||||
|
else:
|
||||||
|
s = "OK"
|
||||||
|
result[host] = {
|
||||||
|
"status": s,
|
||||||
|
"last_backup": info.get("time", "unbekannt"),
|
||||||
|
"msg": info.get("msg", ""),
|
||||||
|
"age_h": round(age_h, 1)
|
||||||
|
}
|
||||||
|
return jsonify(result)
|
||||||
|
|
||||||
|
@app.route("/push", methods=["POST", "GET"])
|
||||||
|
def push():
|
||||||
|
host = request.args.get("host") or request.form.get("host")
|
||||||
|
status = request.args.get("status", "ok")
|
||||||
|
msg = request.args.get("msg", "")
|
||||||
|
if not host:
|
||||||
|
return jsonify({"error": "host parameter required"}), 400
|
||||||
|
data = load()
|
||||||
|
data[host] = {
|
||||||
|
"status": status,
|
||||||
|
"msg": msg,
|
||||||
|
"ts": time.time(),
|
||||||
|
"time": time.strftime("%Y-%m-%d %H:%M")
|
||||||
|
}
|
||||||
|
save(data)
|
||||||
|
return jsonify({"ok": True, "host": host, "status": status})
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
app.run(host="0.0.0.0", port=9999)
|
||||||
10
compose.yaml
10
compose.yaml
|
|
@ -152,3 +152,13 @@ services:
|
||||||
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($|/)'
|
- '--collector.filesystem.ignored-mount-points=^/(sys|proc|dev|host|etc)($|/)'
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|
||||||
|
backup-status:
|
||||||
|
build: ./backup-status
|
||||||
|
container_name: backup-status
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "9999:9999"
|
||||||
|
volumes:
|
||||||
|
- /app-config/backup_status_data:/data
|
||||||
|
networks:
|
||||||
|
- monitoring_network
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue