Require password to access logs page

This commit is contained in:
Peter Stockings
2025-12-23 23:16:56 +11:00
parent dc20afd0f3
commit 45bee0504b
5 changed files with 501 additions and 101 deletions

65
app.py
View File

@@ -2,15 +2,18 @@ import os
import json
import subprocess
from datetime import datetime
from flask import Flask, jsonify, render_template
from flask import Flask, jsonify, render_template, request, session, redirect, url_for
from functools import wraps
import re
app = Flask(__name__)
app.secret_key = os.getenv("SECRET_KEY", "change-this-in-production-please")
POLL_SECONDS = int(os.getenv("POLL_SECONDS", "10"))
APP_DOMAIN = os.getenv("APP_DOMAIN", "peterstockings.com")
DOCKER = os.getenv("DOCKER_BIN", "/usr/bin/docker")
SHOW_INFRA = os.getenv("SHOW_INFRA", "1") == "1"
LOGS_PASSWORD = os.getenv("LOGS_PASSWORD", "dokkustatus123") # Change via environment variable
_UNIT = {
"b": 1,
@@ -200,7 +203,6 @@ def collect():
"mem_limit": s.get("mem_limit", ""),
"mem_pct": s.get("mem_pct", ""),
"restarts": docker_inspect_restart_count(name),
"logs": get_container_logs(name, lines=50),
}
if is_app_web_container(name):
@@ -286,6 +288,32 @@ def collect():
"warnings": warnings,
}
def collect_logs_only():
"""
Lightweight version that only collects app names and logs.
Much faster than collect() since it skips stats, metrics, and system info.
"""
ps_rows = docker_ps_all()
apps = []
for r in ps_rows:
name = r["name"]
if is_app_web_container(name):
app_name = infer_app_name(name)
apps.append({
"app": app_name,
"container": name,
"logs": get_container_logs(name, lines=50),
})
# Sort by app name
apps.sort(key=lambda x: x["app"])
return {
"apps": apps,
}
def parse_human_bytes(s: str) -> int:
# Handles "58.84MiB", "145.1MB", "423B"
s = s.strip()
@@ -317,3 +345,36 @@ def partial_apps():
@app.get("/api/status")
def api_status():
return jsonify(collect())
# Authentication decorator
def login_required(f):
@wraps(f)
def decorated_function(*args, **kwargs):
if not session.get('logged_in'):
return redirect(url_for('login'))
return f(*args, **kwargs)
return decorated_function
# Login routes
@app.route("/login", methods=["GET", "POST"])
def login():
if request.method == "POST":
password = request.form.get("password", "")
if password == LOGS_PASSWORD:
session['logged_in'] = True
return redirect(url_for('logs'))
else:
return render_template("login.html", error="Invalid password")
return render_template("login.html", error=None)
@app.get("/logout")
def logout():
session.pop('logged_in', None)
return redirect(url_for('index'))
# Protected logs page
@app.get("/logs")
@login_required
def logs():
data = collect_logs_only()
return render_template("logs.html", data=data, poll_seconds=POLL_SECONDS)