from flask import Blueprint, Response, jsonify, redirect, request from sqlalchemy import select from sqlalchemy.exc import IntegrityError from l4d2web.auth import current_user, require_login from l4d2web.db import session_scope from l4d2web.models import Blueprint as BlueprintModel from l4d2web.models import Job, Server bp = Blueprint("server", __name__) @bp.post("/servers") @require_login def create_server() -> Response: user = current_user() assert user is not None json_response = request.is_json payload = request.get_json(silent=True) if json_response else request.form with session_scope() as db: blueprint = db.scalar( select(BlueprintModel).where( BlueprintModel.id == int(payload["blueprint_id"]), BlueprintModel.user_id == user.id, ) ) if blueprint is None: return Response("blueprint not found", status=404) server = Server( user_id=user.id, blueprint_id=blueprint.id, name=str(payload["name"]), port=int(payload["port"]), desired_state="stopped", actual_state="unknown", last_error="", ) db.add(server) try: db.flush() except IntegrityError: db.rollback() return Response("port already in use", status=409) server_id = server.id if json_response: return jsonify({"id": server_id}), 201 return redirect(f"/servers/{server_id}") @bp.patch("/servers/") @require_login def update_server(server_id: int) -> Response: user = current_user() assert user is not None payload = request.get_json(silent=True) or {} 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 == int(payload["blueprint_id"]), BlueprintModel.user_id == user.id, ) ) if blueprint is None: return Response("blueprint not found", status=404) server.blueprint_id = blueprint.id return jsonify({"id": server_id}), 200 LIFECYCLE_OPERATIONS = {"initialize", "start", "stop", "delete"} @bp.post("/servers//") @require_login def enqueue_server_operation(server_id: int, operation: str) -> Response: user = current_user() assert user is not None if operation not in LIFECYCLE_OPERATIONS: return Response(status=404) 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) db.add(Job(user_id=user.id, server_id=server.id, operation=operation, state="queued")) if operation == "start": server.desired_state = "running" if operation in {"stop", "delete"}: server.desired_state = "stopped" return redirect(f"/servers/{server_id}")