#!/usr/bin/env python3 """ButtonTask v2 web configurator (Flask). Application factory that wires together the modular blueprints: - auth : login / logout / session - api : buttons, layout, background, feedback, settings, icons - network : Ethernet configuration via nmcli - updater : OTA software update with rollback Reads/writes the same JSON config used by the Qt5 application. """ import os import time from flask import Flask, g, jsonify, render_template, request from api import api_bp from auth import auth_bp, load_or_create_secret, login_required from config_store import list_icons, load_config from network import network_bp from system_info import apply_saved_brightness, record_request, system_bp from updater import current_version, updater_bp def create_app(): app = Flask(__name__, template_folder="templates", static_folder="static") app.secret_key = load_or_create_secret() app.config.update( SESSION_COOKIE_HTTPONLY=True, SESSION_COOKIE_SAMESITE="Lax", PERMANENT_SESSION_LIFETIME=60 * 60 * 8, # 8 hours MAX_CONTENT_LENGTH=512 * 1024 * 1024, # 512 MB upload cap (OTA packages) ) app.register_blueprint(auth_bp) app.register_blueprint(api_bp) app.register_blueprint(network_bp) app.register_blueprint(updater_bp) app.register_blueprint(system_bp) # Restore LCD brightness from config (best-effort; needs bt-brightness + sudoers). apply_saved_brightness() @app.before_request def _request_start(): g._req_start = time.time() @app.after_request def _request_log(response): started = getattr(g, "_req_start", None) if started is not None: record_request( request.method, request.path, response.status_code, (time.time() - started) * 1000.0, request.remote_addr or "", ) return response @app.route("/") @login_required def index(): cfg = load_config() return render_template("index.html", config=cfg, icons=list_icons(), version=current_version()) @app.route("/healthz") def healthz(): # Unauthenticated, lightweight: used by the OTA health-check. return jsonify({"ok": True, "ts": time.time(), "version": current_version().get("version")}) return app app = create_app() def main(): cfg = load_config() port = int(cfg.get("settings", {}).get("webPort", 8080)) host = os.environ.get("BUTTONTASK_HOST", "0.0.0.0") app.run(host=host, port=port, debug=False) if __name__ == "__main__": main()