- run_dev.py: starts proxy, admin and frontend together with colored output per process; handles SIGTERM from PyCharm Stop button - .idea/runConfigurations/Dev.xml: PyCharm Python run config - .gitignore: allow Dev.xml via layered negation pattern - vite.config.js: open browser automatically on dev server start
87 lines
2.7 KiB
Python
87 lines
2.7 KiB
Python
#!/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()
|