import json from pathlib import Path from sqlalchemy import select from l4d2host.instances import delete_instance, initialize_instance, start_instance, stop_instance from l4d2host.logs import stream_instance_logs from l4d2host.status import get_instance_status from l4d2host.steam_install import SteamInstaller from l4d2web.db import session_scope from l4d2web.models import Blueprint, BlueprintOverlay, Overlay, Server from l4d2web.services.spec_yaml import write_temp_spec def build_server_spec_payload(server: Server, blueprint: Blueprint, overlay_names: list[str]) -> dict: return { "port": server.port, "overlays": overlay_names, "arguments": json.loads(blueprint.arguments), "config": json.loads(blueprint.config), } def load_server_blueprint_bundle(server_id: int) -> tuple[Server, Blueprint, list[str]]: with session_scope() as db: server = db.scalar(select(Server).where(Server.id == server_id)) if server is None: raise ValueError("server not found") blueprint = db.scalar(select(Blueprint).where(Blueprint.id == server.blueprint_id)) if blueprint is None: raise ValueError("blueprint not found") rows = db.execute( select(Overlay.name) .join(BlueprintOverlay, BlueprintOverlay.overlay_id == Overlay.id) .where(BlueprintOverlay.blueprint_id == blueprint.id) .order_by(BlueprintOverlay.position) ).all() overlay_names = [row[0] for row in rows] return server, blueprint, overlay_names def install_runtime(on_stdout=None, on_stderr=None) -> None: SteamInstaller().install_or_update(on_stdout=on_stdout, on_stderr=on_stderr) def initialize_server(server_id: int, on_stdout=None, on_stderr=None) -> None: server, blueprint, overlay_names = load_server_blueprint_bundle(server_id) spec_path = write_temp_spec(build_server_spec_payload(server, blueprint, overlay_names)) try: initialize_instance(server.name, spec_path, on_stdout=on_stdout, on_stderr=on_stderr) finally: spec_path.unlink(missing_ok=True) def start_server(server_id: int, on_stdout=None, on_stderr=None) -> None: server, _, _ = load_server_blueprint_bundle(server_id) start_instance(server.name, on_stdout=on_stdout, on_stderr=on_stderr) def stop_server(server_id: int, on_stdout=None, on_stderr=None) -> None: server, _, _ = load_server_blueprint_bundle(server_id) stop_instance(server.name, on_stdout=on_stdout, on_stderr=on_stderr) def delete_server(server_id: int, on_stdout=None, on_stderr=None) -> None: server, _, _ = load_server_blueprint_bundle(server_id) delete_instance(server.name, on_stdout=on_stdout, on_stderr=on_stderr) def server_status(server_name: str): return get_instance_status(server_name) def stream_server_logs(server_name: str, *, lines: int = 200, follow: bool = True): return stream_instance_logs(server_name, lines=lines, follow=follow)