#!/usr/bin/env python """Launcher: runs the Django ASGI server locally and opens it in a native window.""" import os import sys import threading import time os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings") # Qt WebEngine crashes on some GPU/driver combinations unless it renders on CPU. os.environ.setdefault("QTWEBENGINE_CHROMIUM_FLAGS", "--disable-gpu") def start_server(host: str, port: int): import django django.setup() from django.core.management import call_command # --run-syncdb builds the tables on a first launch with no migration files. call_command("migrate", "--run-syncdb", verbosity=0) import uvicorn uvicorn.run( "backend.asgi:application", host=host, port=port, log_level="warning", ) def wait_for_server(url: str, timeout: float = 10.0) -> None: """Poll until the server answers, or raise carrying the last refusal.""" from urllib.request import urlopen deadline = time.time() + timeout last_error: OSError | None = None while time.time() < deadline: try: urlopen(url, timeout=1) return except OSError as e: last_error = e time.sleep(0.1) raise TimeoutError( f"Django server did not answer {url} within {timeout}s" ) from last_error def main(): host = "127.0.0.1" port = 8765 server = threading.Thread(target=start_server, args=(host, port), daemon=True) server.start() base_url = f"http://{host}:{port}" wait_for_server(f"{base_url}/api/mizan/session/") print(f"Backend running at {base_url}") if "--headless" in sys.argv: print("Headless mode — server running. Press Ctrl+C to stop.") try: while True: time.sleep(1) except KeyboardInterrupt: print("Interrupted — stopping the server.", file=sys.stderr) return import webview webview.create_window( title="mizan Desktop", url=base_url, width=1024, height=768, min_size=(640, 480), ) webview.start() if __name__ == "__main__": main()