Added automatic handling of FastAPI debug mode. Use templating more completely to enable project name as env prefix etc.
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
# auto-upgrade@fastapi-vue-setup - remove this if you modify this file
|
||||
import argparse
|
||||
import os
|
||||
|
||||
from fastapi_vue import server
|
||||
|
||||
DEFAULT_PORT = TEMPLATE_DEFAULT_PORT
|
||||
DEVMODE = bool(os.getenv("ENVPREFIX_FRONTEND_URL"))
|
||||
|
||||
|
||||
def main():
|
||||
@@ -19,6 +21,7 @@ def main():
|
||||
"MODULE_NAME.APP_MODULE:APP_VAR",
|
||||
listen=args.listen,
|
||||
default_port=DEFAULT_PORT,
|
||||
reload=DEVMODE,
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -3,9 +3,10 @@ from pathlib import Path
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi_vue import Frontend
|
||||
from MODULE_NAME.__main__ import DEVMODE
|
||||
|
||||
# Vue Frontend static files
|
||||
frontend = Frontend(Path(__file__).with_name("frontend-build"), cached=["/assets/"])
|
||||
frontend = Frontend(Path(__file__).with_name("frontend-build"))
|
||||
|
||||
|
||||
@asynccontextmanager
|
||||
@@ -15,7 +16,7 @@ async def lifespan(app: FastAPI):
|
||||
yield
|
||||
|
||||
|
||||
app = FastAPI(title="PROJECT_TITLE", lifespan=lifespan)
|
||||
app = FastAPI(title="PROJECT_TITLE", debug=DEVMODE, lifespan=lifespan)
|
||||
|
||||
|
||||
# Add API routes here...
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
/**
|
||||
* FastAPI-Vue Vite Plugin
|
||||
* auto-upgrade@fastapi-vue-setup -- remove this if you edit the plugin
|
||||
*
|
||||
* Configures Vite for FastAPI backend integration:
|
||||
* - Proxies /api/* requests to the FastAPI backend
|
||||
* - Builds to the Python module's frontend-build directory
|
||||
*
|
||||
* Environment variables (with defaults):
|
||||
* FASTAPI_VUE_BACKEND_URL=http://localhost:5180 - Backend API URL for proxying
|
||||
* Options:
|
||||
* paths - Array of paths to proxy (default: ["/api"])
|
||||
*/
|
||||
|
||||
const backendUrl = process.env.FASTAPI_VUE_BACKEND_URL || "http://localhost:5180"
|
||||
|
||||
export default function fastapiVue({ paths = ["/api"] } = {}) {
|
||||
const backendUrl = process.env.ENVPREFIX_BACKEND_URL || "http://localhost:TEMPLATE_DEFAULT_PORT"
|
||||
|
||||
// Build proxy configuration for each path
|
||||
const proxy = {}
|
||||
for (const path of paths) {
|
||||
@@ -23,7 +24,7 @@ export default function fastapiVue({ paths = ["/api"] } = {}) {
|
||||
}
|
||||
|
||||
return {
|
||||
name: "fastapi-vite",
|
||||
name: "vite-plugin-fastapi-MODULE_NAME",
|
||||
config: () => ({
|
||||
server: { proxy },
|
||||
build: {
|
||||
|
||||
@@ -16,7 +16,6 @@ from devutil import ( # type: ignore
|
||||
check_ports_free,
|
||||
logger,
|
||||
ready,
|
||||
setup_fastapi,
|
||||
setup_vite,
|
||||
)
|
||||
|
||||
@@ -24,7 +23,9 @@ DEFAULT_VITE_PORT = TEMPLATE_VITE_PORT
|
||||
DEFAULT_DEV_PORT = TEMPLATE_DEV_PORT
|
||||
|
||||
|
||||
async def run_devserver(frontend: str, backend: str) -> None:
|
||||
async def run_devserver(
|
||||
frontend: str, backend: str, extra_args: list[str] | None = None
|
||||
) -> None:
|
||||
reporoot = Path(__file__).parent.parent
|
||||
front = reporoot / "frontend"
|
||||
if not (front / "package.json").exists():
|
||||
@@ -32,18 +33,16 @@ async def run_devserver(frontend: str, backend: str) -> None:
|
||||
raise SystemExit(1)
|
||||
|
||||
viteurl, npm_install, vite = setup_vite(frontend, DEFAULT_VITE_PORT)
|
||||
backurl, uvicorn = setup_fastapi(
|
||||
backend, "MODULE_NAME.APP_MODULE:APP_VAR", DEFAULT_DEV_PORT
|
||||
)
|
||||
backurl, MODULE_NAME = setup_cli("PROJECT_CLI", backend, DEFAULT_DEV_PORT)
|
||||
|
||||
# Tell the everyone where the frontend and backend are (vite proxy, etc)
|
||||
os.environ["FASTAPI_VUE_FRONTEND_URL"] = viteurl
|
||||
os.environ["FASTAPI_VUE_BACKEND_URL"] = backurl
|
||||
os.environ["ENVPREFIX_FRONTEND_URL"] = viteurl
|
||||
os.environ["ENVPREFIX_BACKEND_URL"] = backurl
|
||||
|
||||
async with ProcessGroup() as pg:
|
||||
npm_i = await pg.spawn(*npm_install, cwd=front)
|
||||
await check_ports_free(viteurl, backurl)
|
||||
await pg.spawn(*uvicorn)
|
||||
await pg.spawn(*MODULE_NAME, *(extra_args or []))
|
||||
await pg.wait(npm_i, ready(backurl, path="/api/health?from=devserver.py"))
|
||||
await pg.spawn(*vite, cwd=front)
|
||||
|
||||
@@ -65,15 +64,13 @@ def main():
|
||||
metavar="host:port",
|
||||
help=f"FastAPI backend endpoint (default: localhost:{DEFAULT_DEV_PORT})",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
args, extra_args = parser.parse_known_args()
|
||||
with suppress(KeyboardInterrupt):
|
||||
asyncio.run(run_devserver(args.frontend, args.backend))
|
||||
asyncio.run(run_devserver(args.frontend, args.backend, extra_args))
|
||||
|
||||
|
||||
HELP_EPILOG = """
|
||||
scripts/devserver.py # Default ports on localhost
|
||||
scripts/devserver.py 3000 # Vite on localhost:3000
|
||||
scripts/devserver.py :3000 --backend 8000 # *:3000, localhost:8000
|
||||
scripts/devserver.py [args to PROJECT_CLI]
|
||||
|
||||
JS_RUNTIME environment variable can be used to select the JS runtime:
|
||||
npm, deno, bun, or full path to the runtime executable (node maps to npm).
|
||||
|
||||
@@ -188,3 +188,29 @@ def setup_fastapi(
|
||||
"--forwarded-allow-ips=*",
|
||||
]
|
||||
return f"http://{host}:{port}", cmd
|
||||
|
||||
|
||||
def setup_cli(
|
||||
cli: str, endpoint: str, default_port: int = 8000
|
||||
) -> tuple[str, list[str]]:
|
||||
"""Parse backend endpoint and build CLI command.
|
||||
|
||||
Returns (url, cli_cmd).
|
||||
Raises SystemExit(1) on invalid config.
|
||||
"""
|
||||
endpoints = parse_endpoint(endpoint, default_port)
|
||||
|
||||
if "uds" in endpoints[0]:
|
||||
logger.warning("Unix sockets not supported with vite devserver")
|
||||
raise SystemExit(1)
|
||||
|
||||
host = endpoints[0]["host"]
|
||||
port = endpoints[0]["port"]
|
||||
|
||||
cmd = [
|
||||
sys.executable,
|
||||
"-m",
|
||||
cli,
|
||||
f"--listen={host}:{port}",
|
||||
]
|
||||
return f"http://{host}:{port}", cmd
|
||||
|
||||
Reference in New Issue
Block a user