diff --git a/app.py b/app.py index 38baaca..6ddf714 100644 --- a/app.py +++ b/app.py @@ -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) diff --git a/templates/apps_table.html b/templates/apps_table.html index 2e35896..58838e1 100644 --- a/templates/apps_table.html +++ b/templates/apps_table.html @@ -366,102 +366,4 @@ - {% endif %} - - -