More robust server startup, startup logo and info screen, renewed devmode script.
This commit is contained in:
Regular → Executable
+58
-31
@@ -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()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user