diff --git a/src/frontend/flask_app/app.py b/src/frontend/flask_app/app.py index d1942be..6a096b0 100644 --- a/src/frontend/flask_app/app.py +++ b/src/frontend/flask_app/app.py @@ -74,12 +74,14 @@ def create_app() -> Flask: from blueprints.maker import maker_bp from blueprints.statistics import statistics_bp from blueprints.admin import admin_bp + from blueprints.monitor import monitor_bp # #57 live station monitor app.register_blueprint(auth_bp) app.register_blueprint(measure_bp, url_prefix="/measure") app.register_blueprint(maker_bp, url_prefix="/maker") app.register_blueprint(statistics_bp, url_prefix="/statistics") app.register_blueprint(admin_bp, url_prefix="/admin") + app.register_blueprint(monitor_bp, url_prefix="/monitor") # #57 @app.before_request def _check_secret_key(): diff --git a/src/frontend/flask_app/blueprints/monitor.py b/src/frontend/flask_app/blueprints/monitor.py new file mode 100644 index 0000000..4db419d --- /dev/null +++ b/src/frontend/flask_app/blueprints/monitor.py @@ -0,0 +1,45 @@ +"""Live monitor of the open production runs (#57). + +Page at /monitor for admin and Supervisor (capoturno); the page polls a JSON proxy +of GET /api/production-runs/open every 10 seconds. +""" +from functools import wraps + +from flask import Blueprint, abort, jsonify, render_template, session + +from blueprints.auth import login_required +from services.api_client import api_client + +monitor_bp = Blueprint("monitor", __name__) + + +def monitor_required(f): + """Admin or Supervisor only: the same people the backend lets read /open.""" + @wraps(f) + def decorated(*args, **kwargs): + user = session.get("user", {}) + if not (user.get("is_admin") or "Supervisor" in user.get("roles", [])): + abort(403) + return f(*args, **kwargs) + return decorated + + +@monitor_bp.route("", strict_slashes=False) +@login_required +@monitor_required +def index(): + return render_template("monitor/index.html") + + +@monitor_bp.route("/api/runs") +@login_required +@monitor_required +def api_runs(): + """JSON proxy polled by the page.""" + resp = api_client.get("/api/production-runs/open") + if isinstance(resp, dict) and resp.get("error"): + body = {"error": True, "detail": resp.get("detail", "")} + if resp.get("session_expired"): + body["session_expired"] = True + return jsonify(body), resp.get("status_code", 500) + return jsonify(resp) diff --git a/src/frontend/flask_app/templates/monitor/index.html b/src/frontend/flask_app/templates/monitor/index.html new file mode 100644 index 0000000..3960be6 --- /dev/null +++ b/src/frontend/flask_app/templates/monitor/index.html @@ -0,0 +1,105 @@ +{% extends "base.html" %} + +{% block title %}{{ _("Monitor stazioni") }} — TieMeasureFlow{% endblock %} + +{% block content %} +
{{ _("Produzioni aperte, aggiornate ogni 10 secondi") }}
+