#!/usr/bin/env python3 """Dev runner: startet Proxy, Admin-API und Frontend gemeinsam.""" import os import sys import signal import subprocess import threading from pathlib import Path def load_env(path: Path): if not path.exists(): return for line in path.read_text(encoding='utf-8').splitlines(): line = line.strip() if line and not line.startswith('#') and '=' in line: key, _, val = line.partition('=') os.environ.setdefault(key.strip(), val.strip().strip('"\'')) def pipe_output(proc: subprocess.Popen, label: str, color: str): reset = '\033[0m' prefix = f'\033[{color}m[{label}]{reset} ' for raw in iter(proc.stdout.readline, b''): sys.stdout.write(prefix + raw.decode(errors='replace')) sys.stdout.flush() def main(): root = Path(__file__).parent load_env(root / '.env') if not os.environ.get('ADMIN_PASSWORD'): print('Fehler: ADMIN_PASSWORD nicht gesetzt (siehe .env)') sys.exit(1) python = root / '.venv' / 'bin' / 'python' backend = root / 'backend' frontend = root / 'frontend' proxy_host = os.environ.get('PROXY_HOST', '0.0.0.0') proxy_port = os.environ.get('PROXY_PORT', '8000') admin_port = os.environ.get('ADMIN_PORT', '8001') print('Initialisiere Datenbank...') subprocess.run([str(python), 'init_db.py'], cwd=backend, check=True) print(f'Starte Proxy → http://{proxy_host}:{proxy_port}') print(f'Starte Admin-API → http://127.0.0.1:{admin_port}') print('Starte Frontend → http://localhost:5173') env = {**os.environ, 'PYTHONUNBUFFERED': '1'} procs = [ (subprocess.Popen( [str(python), '-m', 'uvicorn', 'main:app', '--reload', '--host', proxy_host, '--port', proxy_port], cwd=backend, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, ), 'Proxy ', '34'), # blau (subprocess.Popen( [str(python), '-m', 'uvicorn', 'admin:app', '--reload', '--host', '127.0.0.1', '--port', admin_port], cwd=backend, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, ), 'Admin ', '33'), # gelb (subprocess.Popen( ['npm', 'run', 'dev'], cwd=frontend, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, ), 'Frontend', '32'), # grün ] for proc, label, color in procs: threading.Thread(target=pipe_output, args=(proc, label, color), daemon=True).start() def stop(*_): for p, _, _ in procs: p.terminate() signal.signal(signal.SIGINT, stop) signal.signal(signal.SIGTERM, stop) for p, _, _ in procs: p.wait() if __name__ == '__main__': main()