import json from flask import Blueprint, Response, redirect, render_template from sqlalchemy import select from l4d2web.auth import current_user, require_admin, require_login from l4d2web.db import session_scope from l4d2web.models import Blueprint as BlueprintModel from l4d2web.models import BlueprintOverlay, Job, Overlay, Server, User bp = Blueprint("pages", __name__) @bp.get("/dashboard") @require_login def dashboard() -> str: return render_template("dashboard.html") @bp.get("/admin") @require_admin def admin_home() -> str: return render_template("admin.html") @bp.post("/admin/install") @require_admin def enqueue_runtime_install() -> Response: user = current_user() assert user is not None with session_scope() as db: db.add(Job(user_id=user.id, server_id=None, operation="install", state="queued")) return redirect("/admin/jobs") @bp.get("/admin/users") @require_admin def admin_users() -> str: with session_scope() as db: users = db.scalars(select(User).order_by(User.username)).all() return render_template("admin_users.html", users=users) @bp.get("/admin/jobs") @require_admin def admin_jobs() -> str: with session_scope() as db: rows = db.execute( select(Job, User, Server) .join(User, User.id == Job.user_id) .outerjoin(Server, Server.id == Job.server_id) .order_by(Job.created_at.desc()) ).all() return render_template("admin_jobs.html", rows=rows) @bp.get("/servers") @require_login def servers_page() -> str: user = current_user() assert user is not None with session_scope() as db: rows = db.execute( select(Server, BlueprintModel) .join(BlueprintModel, BlueprintModel.id == Server.blueprint_id) .where(Server.user_id == user.id) .order_by(Server.name) ).all() return render_template("servers.html", rows=rows) @bp.get("/servers/") @require_login def server_detail(server_id: int): user = current_user() assert user is not None with session_scope() as db: server = db.scalar(select(Server).where(Server.id == server_id, Server.user_id == user.id)) if server is None: return Response(status=404) blueprint = db.scalar(select(BlueprintModel).where(BlueprintModel.id == server.blueprint_id)) recent_job_rows = db.execute( select(Job, User, Server) .join(User, User.id == Job.user_id) .outerjoin(Server, Server.id == Job.server_id) .where(Job.server_id == server.id) .order_by(Job.created_at.desc()) .limit(5) ).all() return render_template( "server_detail.html", server=server, blueprint=blueprint, recent_job_rows=recent_job_rows, ) @bp.get("/servers//jobs") @require_login def server_jobs_page(server_id: int): user = current_user() assert user is not None with session_scope() as db: server = db.scalar(select(Server).where(Server.id == server_id, Server.user_id == user.id)) if server is None: return Response(status=404) rows = db.execute( select(Job, User, Server) .join(User, User.id == Job.user_id) .outerjoin(Server, Server.id == Job.server_id) .where(Job.server_id == server.id) .order_by(Job.created_at.desc()) ).all() return render_template("server_jobs.html", server=server, rows=rows) @bp.get("/overlays") @require_login def overlays() -> str: with session_scope() as db: overlays = db.scalars(select(Overlay).order_by(Overlay.name)).all() return render_template("overlays.html", overlays=overlays) @bp.get("/blueprints") @require_login def blueprints_page() -> str: user = current_user() assert user is not None with session_scope() as db: blueprints = db.scalars( select(BlueprintModel).where(BlueprintModel.user_id == user.id).order_by(BlueprintModel.name) ).all() return render_template("blueprints.html", blueprints=blueprints) @bp.get("/blueprints/") @require_login def blueprint_page(blueprint_id: int): user = current_user() assert user is not None with session_scope() as db: blueprint = db.scalar(select(BlueprintModel).where(BlueprintModel.id == blueprint_id)) if blueprint is None: return Response(status=404) if blueprint.user_id != user.id: return Response(status=403) selected_overlays = db.scalars( select(Overlay) .join(BlueprintOverlay, BlueprintOverlay.overlay_id == Overlay.id) .where(BlueprintOverlay.blueprint_id == blueprint.id) .order_by(BlueprintOverlay.position) ).all() position_rows = db.execute( select(BlueprintOverlay.overlay_id, BlueprintOverlay.position) .where(BlueprintOverlay.blueprint_id == blueprint.id) ).all() all_overlays = db.scalars(select(Overlay).order_by(Overlay.name)).all() overlay_positions = {overlay_id: position + 1 for overlay_id, position in position_rows} return render_template( "blueprint_detail.html", blueprint=blueprint, selected_overlays=selected_overlays, all_overlays=all_overlays, selected_overlay_ids={overlay.id for overlay in selected_overlays}, overlay_positions=overlay_positions, arguments=json.loads(blueprint.arguments), config_lines=json.loads(blueprint.config), )