Add PyCharm Dev run config and dev runner script

- 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
This commit is contained in:
Oliver Hofmann 2026-04-29 08:14:33 +02:00
parent dd8f69ecb6
commit cdaec894d8
3 changed files with 106 additions and 1 deletions

5
.gitignore vendored
View File

@ -14,7 +14,10 @@ __pycache__/
*.sqlite3
# IDE
.idea/
.idea/*
!.idea/runConfigurations/
.idea/runConfigurations/*
!.idea/runConfigurations/Dev.xml
.vscode/
# Frontend build

16
.idea/runConfigurations/Dev.xml generated Normal file
View File

@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Dev" type="PythonConfigurationType" factoryName="Python">
<module name="llm_quota" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/run_dev.py" />
<option name="PARAMETERS" value="" />
<method v="2" />
</configuration>
</component>

86
run_dev.py Normal file
View File

@ -0,0 +1,86 @@
#!/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()