foxygit / RPA-Remisser Log in
commits tags

/run_webui.py · 3.38 KB

raw
"""Startar flödesbyggarens webui på http://127.0.0.1:8765.

    python run_webui.py            # startar; vägrar om porten redan används
    python run_webui.py --force    # dödar en redan körande instans och tar över
    python run_webui.py --port 9000

Endast lokal åtkomst (127.0.0.1) - webui:t styr en riktig webbläsare mot din
inloggade session och ska inte exponeras på nätverket.
"""

import argparse
import socket
import subprocess
import sys
import time

HOST = "127.0.0.1"


def _port_in_use(port: int) -> bool:
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 0)
        try:
            s.bind((HOST, port))
            return False
        except OSError:
            return True


def _pids_on_port(port: int) -> list[int]:
    """PID:erna som lyssnar på porten. Bäst effort, plattformsspecifikt."""
    pids: set[int] = set()
    try:
        if sys.platform == "win32":
            out = subprocess.run(
                ["netstat", "-ano", "-p", "tcp"], capture_output=True, text=True, timeout=5
            ).stdout
            for line in out.splitlines():
                parts = line.split()
                if len(parts) >= 5 and parts[0].upper() == "TCP" and parts[1].endswith(f":{port}") \
                        and parts[3].upper() == "LISTENING":
                    pids.add(int(parts[4]))
        else:
            out = subprocess.run(
                ["lsof", "-ti", f"tcp:{port}", "-sTCP:LISTEN"], capture_output=True, text=True, timeout=5
            ).stdout
            pids.update(int(x) for x in out.split())
    except Exception:  # noqa: BLE001
        pass
    return sorted(pids)


def _kill(pids: list[int]) -> None:
    for pid in pids:
        try:
            if sys.platform == "win32":
                subprocess.run(["taskkill", "/F", "/PID", str(pid)], capture_output=True, timeout=5)
            else:
                subprocess.run(["kill", "-9", str(pid)], capture_output=True, timeout=5)
            print(f"Stängde process {pid}.")
        except Exception as exc:  # noqa: BLE001
            print(f"Kunde inte stänga {pid}: {exc}", file=sys.stderr)


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("--port", type=int, default=8765)
    parser.add_argument("--force", action="store_true",
                        help="Döda en redan körande instans på porten och ta över")
    args = parser.parse_args()

    if _port_in_use(args.port):
        pids = _pids_on_port(args.port)
        who = f" (PID {', '.join(map(str, pids))})" if pids else ""
        if not args.force:
            print(
                f"Port {args.port} används redan{who}.\n"
                f"  - webui:t kör troligen redan: öppna http://{HOST}:{args.port}\n"
                f"  - annars: kör 'python run_webui.py --force' för att ta över, "
                f"eller '--port <annan port>'.",
                file=sys.stderr,
            )
            sys.exit(1)
        if pids:
            _kill(pids)
            time.sleep(1)
        if _port_in_use(args.port):
            print(f"Port {args.port} är fortfarande upptagen. Avbryter.", file=sys.stderr)
            sys.exit(1)

    import uvicorn

    uvicorn.run("webui.app:app", host=HOST, port=args.port, reload=False)


if __name__ == "__main__":
    main()