More robust server startup, startup logo and info screen, renewed devmode script.

This commit is contained in:
2025-12-05 19:06:42 +00:00
parent c1204ca020
commit 127e06179b
10 changed files with 287 additions and 99 deletions
Regular → Executable
+58 -31
View File
@@ -1,16 +1,19 @@
#!/usr/bin/env python3
"""Development server script for Paskia.
#!/usr/bin/env -S uv run
"""Run Vite development server for frontend and FastAPI backend with auto-reload.
This script is only available when running from the git repository source,
not from the installed package. It starts both the Vite frontend dev server
and the FastAPI backend with auto-reload enabled.
Usage:
python scripts/dev.py [options...]
uv run scripts/dev.py [host:port] [options...]
All options are forwarded to `paskia serve`.
The optional host:port argument sets where the Vite frontend listens.
All other options are forwarded to `paskia serve`.
Backend always listens on localhost:4402.
"""
import argparse
import atexit
import os
import shutil
@@ -21,14 +24,10 @@ from pathlib import Path
from sys import stderr
from threading import Thread
# Set dev mode environment variable BEFORE importing anything from paskia
os.environ["PASKIA_DEVMODE"] = "1"
from paskia.fastapi.__main__ import parse_endpoint
# Ensure the package is importable when running from repo root
sys.path.insert(0, str(Path(__file__).parent.parent))
DEFAULT_DEV_PORT = 4402
DEV_SERVER = "http://localhost:4403"
DEFAULT_VITE_PORT = 4403 # overrides by CLI option
BACKEND_PORT = 4402 # hardcoded, also in vite.config.ts
NO_FRONTEND_TOOL = """\
┃ ⚠️ deno, npm or bunx needed to run the frontend server.
@@ -48,17 +47,19 @@ BUN_BUG = """\
NO_FRONTEND = """\
Note: only static build of the frontend is served at localhost:4402.
The page will not update with frontend code changes.
The backend will still try reaching Vite at {vite_url}
for various frontend assets, so make sure to start it manually.
"""
def run_vite():
def run_vite(vite_url: str, vite_host: str | None, vite_port: int):
"""Spawn the frontend dev server (deno, npm, or bunx) as a background process."""
devpath = Path(__file__).parent.parent / "frontend"
if not (devpath / "package.json").exists():
stderr.write(f"┃ ⚠️ Frontend source not found at {devpath}\n")
stderr.write(NO_FRONTEND)
stderr.write(
f"┃ ⚠️ Frontend source not found at {devpath}\n"
+ NO_FRONTEND.format(vite_url=vite_url)
)
return
options = [
@@ -74,24 +75,31 @@ def run_vite():
tool_name = option[0]
break
# Add Vite CLI args for host/port
vite_args = ["--port", str(vite_port)]
if vite_host:
vite_args.extend(["--host", vite_host])
vite_process = None
def start_vite():
nonlocal vite_process
if cmd is None:
stderr.write(NO_FRONTEND_TOOL)
stderr.write(NO_FRONTEND)
stderr.write(NO_FRONTEND_TOOL + NO_FRONTEND.format(vite_url=vite_url))
return
assert tool_name is not None
try:
if tool_name == "bunx":
stderr.write(BUN_BUG)
stderr.write(f">>> {' '.join([tool_name, *cmd[1:]])}\n")
vite_process = subprocess.Popen(cmd, cwd=str(devpath), shell=False)
full_cmd = cmd + vite_args
stderr.write(f">>> {' '.join([tool_name, *full_cmd[1:]])}\n")
vite_process = subprocess.Popen(full_cmd, cwd=str(devpath), shell=False)
except Exception as e:
stderr.write(f"┃ ⚠️ Vite couldn't start: {e}\n")
stderr.write(NO_FRONTEND)
stderr.write(
f"┃ ⚠️ Vite couldn't start: {e}\n"
+ NO_FRONTEND.format(vite_url=vite_url)
)
def cleanup():
if vite_process:
@@ -108,20 +116,39 @@ def run_vite():
def main():
# Start Vite dev server first
run_vite()
# Parse optional hostport argument for Vite frontend
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument("hostport", nargs="?", default=None)
args, remaining = parser.parse_known_args()
# Set default origin for Vite if not specified
if "--origin" not in sys.argv:
os.environ.setdefault("PASKIA_ORIGIN", DEV_SERVER)
# Parse Vite endpoint
vite_host, vite_port, vite_uds, all_ifaces = parse_endpoint(
args.hostport, DEFAULT_VITE_PORT
)
# Build argv for the main CLI
# Dev mode always listens on localhost:4402 (security: prevents public exposure)
# User args come after, allowing overrides of other options
sys.argv = ["paskia", "serve", f"localhost:{DEFAULT_DEV_PORT}"] + sys.argv[1:]
if vite_uds:
raise SystemExit("┃ ⚠️ Unix sockets are not supported for Vite frontend")
# Handle all-interfaces case (:port syntax)
# Vite uses 0.0.0.0 to listen on all interfaces (IPv4 only, sufficient for dev)
if all_ifaces:
vite_host = "0.0.0.0"
# Build Vite URL for PASKIA_DEVMODE (always use localhost for URL)
vite_url = f"http://localhost:{vite_port}"
# Start Vite dev server
run_vite(vite_url, vite_host, vite_port)
# Set dev mode with Vite URL
os.environ["PASKIA_DEVMODE"] = vite_url
# Import CLI after environment is set up
from paskia.fastapi.__main__ import main as cli_main
# Build argv for the main CLI in Dev mode
# Backend always listens on localhost only (Vite proxies API requests)
sys.argv = ["paskia", "serve", f"localhost:{BACKEND_PORT}"] + remaining
cli_main()