Proxy to another Paskia #5
+16
-24
@@ -190,18 +190,6 @@ def cmd_migrate(args: argparse.Namespace) -> None:
|
||||
print(f"✅ {action} {db_file_path()} (domains: {', '.join(rp_ids)})")
|
||||
|
||||
|
||||
def _save_listen(db_path: Path, listen: list[str] | None) -> None:
|
||||
"""Persist the listen endpoints to the stored configuration."""
|
||||
kanta = Kanta(str(db_path), DB())
|
||||
|
||||
async def _write() -> None:
|
||||
async with kanta:
|
||||
with kanta.transaction("serve:save_listen"):
|
||||
kanta.data.config.listen = listen
|
||||
|
||||
asyncio.run(_write())
|
||||
|
||||
|
||||
def cmd_serve(args: argparse.Namespace) -> None:
|
||||
"""Open the combined database and serve all configured domains."""
|
||||
db_path = db_file_path()
|
||||
@@ -214,14 +202,20 @@ def cmd_serve(args: argparse.Namespace) -> None:
|
||||
)
|
||||
raise SystemExit(f"Database {db_path} not found — run 'paskia init' first.")
|
||||
|
||||
if args.save and args.listen is not None:
|
||||
# '--listen ""' clears the stored endpoints (back to the default)
|
||||
_save_listen(db_path, _split_multi(args.listen) or None)
|
||||
|
||||
config = _load_stored_config(db_path)
|
||||
|
||||
listen = _split_multi(args.listen) or config.listen
|
||||
configure_domains(listen=listen)
|
||||
# Effective serve parameters, teleported to the server process(es); the
|
||||
# app persists the listen endpoints to the database when save is set.
|
||||
cfg = serve_config()
|
||||
cfg.save = bool(args.save and args.listen is not None)
|
||||
if cfg.save:
|
||||
# '--listen ""' clears the stored endpoints (back to the default)
|
||||
cfg.listen = _split_multi(args.listen) or None
|
||||
else:
|
||||
cfg.listen = _split_multi(args.listen) or config.listen
|
||||
teleport() # Serialize bound config before spawning workers
|
||||
|
||||
configure_domains(listen=cfg.listen)
|
||||
try:
|
||||
registry = build_registry(config)
|
||||
except ValueError as e:
|
||||
@@ -229,18 +223,16 @@ def cmd_serve(args: argparse.Namespace) -> None:
|
||||
# Sanitization warnings (serving is best-effort; fixing the stored config
|
||||
# is the admin's job via the admin interface) are logged by build().
|
||||
|
||||
# Pass process-global serve parameters to the server process(es)
|
||||
serve_config().listen = listen
|
||||
teleport() # Serialize bound config before spawning workers
|
||||
|
||||
startupbox.print_startup_config(registry, listen=listen, default_port=DEFAULT_PORT)
|
||||
startupbox.print_startup_config(
|
||||
registry, listen=cfg.listen, default_port=DEFAULT_PORT
|
||||
)
|
||||
|
||||
# Run the server (spawns processes in dev mode)
|
||||
# tracerite, access logging and log config are handled by fastapi_vue.server;
|
||||
# we print our own startup config box, so disable the built-in one.
|
||||
server.run(
|
||||
"paskia.fastapi.mainapp:app",
|
||||
listen=listen,
|
||||
listen=cfg.listen,
|
||||
default_port=DEFAULT_PORT,
|
||||
server_header=False,
|
||||
startup_box=None,
|
||||
|
||||
@@ -29,10 +29,12 @@ _EXAMPLES_DIR = Path(__file__).parent.parent.parent / "examples"
|
||||
async def lifespan(app: FastAPI): # pragma: no cover - startup path
|
||||
"""Application lifespan: open the combined database and build the domain registry.
|
||||
|
||||
Process-global serve parameters (listen endpoints) are passed via the
|
||||
PASKIA_CONFIG JSON env variable (set by the CLI entrypoint) so that
|
||||
uvicorn reload / multiprocess workers derive site URLs the same way.
|
||||
Domain configuration is read from the database.
|
||||
Process-global serve parameters (listen endpoints, save flag) are passed
|
||||
via the PASKIA_CONFIG JSON env variable (set by the CLI entrypoint) so
|
||||
that uvicorn reload / multiprocess workers derive site URLs the same
|
||||
way. With the save flag set, the listen endpoints are persisted here —
|
||||
the CLI never opens the database read-write. Domain configuration is
|
||||
read from the database.
|
||||
"""
|
||||
cfg = serve_config()
|
||||
domains.configure(listen=cfg.listen)
|
||||
@@ -41,6 +43,9 @@ async def lifespan(app: FastAPI): # pragma: no cover - startup path
|
||||
Path(kanta.filename).parent.mkdir, parents=True, exist_ok=True
|
||||
)
|
||||
async with kanta:
|
||||
if cfg.save:
|
||||
with kanta.transaction("serve:save_listen"):
|
||||
db.data().config.listen = cfg.listen
|
||||
try:
|
||||
domains.init_registry(db.data().config)
|
||||
await remoteauth.init()
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
|
||||
Domain configuration lives in the database (``Config.domains``); the
|
||||
``PASKIA_CONFIG`` environment variable only carries the effective listen
|
||||
endpoints so that child processes (uvicorn reload / workers) derive site
|
||||
URLs the same way the parent did. The CLI entry point mutates the bound
|
||||
object before ``server.run()`` calls ``teleport()`` to pass it on.
|
||||
endpoints and whether to persist them, so that child processes (uvicorn
|
||||
reload / workers) derive site URLs the same way the parent did. The CLI
|
||||
entry point mutates the bound object before ``server.run()`` calls
|
||||
``teleport()`` to pass it on.
|
||||
"""
|
||||
|
||||
import msgspec
|
||||
@@ -15,6 +16,7 @@ class ServeConfig(msgspec.Struct):
|
||||
"""Process-global serve parameters."""
|
||||
|
||||
listen: list[str] | None = None
|
||||
save: bool = False # Persist listen to the stored config on startup
|
||||
|
||||
|
||||
def serve_config() -> ServeConfig:
|
||||
|
||||
+13
-3
@@ -172,6 +172,7 @@ def test_serve_uses_stored_config(run_cli, tmp_path):
|
||||
assert calls["listen"] is None # stored listen (None) used
|
||||
serve = msgspec.json.decode(os.environ["PASKIA_CONFIG"].encode(), type=ServeConfig)
|
||||
assert serve.listen is None
|
||||
assert serve.save is False
|
||||
|
||||
|
||||
def test_serve_listen_override_not_persisted(run_cli, tmp_path):
|
||||
@@ -181,24 +182,33 @@ def test_serve_listen_override_not_persisted(run_cli, tmp_path):
|
||||
assert calls["listen"] == ["4403"]
|
||||
serve = msgspec.json.decode(os.environ["PASKIA_CONFIG"].encode(), type=ServeConfig)
|
||||
assert serve.listen == ["4403"]
|
||||
assert serve.save is False
|
||||
# Stored config keeps the original listen value
|
||||
assert stored_config(tmp_path).listen == ["4402"]
|
||||
|
||||
|
||||
def test_serve_listen_save_persists(run_cli, tmp_path):
|
||||
"""--save teleports the save flag; the app persists, the CLI is read-only."""
|
||||
run_cli("init", "--listen", "4402")
|
||||
calls = run_cli("--listen", "4403", "--save")
|
||||
|
||||
assert calls["listen"] == ["4403"]
|
||||
assert stored_config(tmp_path).listen == ["4403"]
|
||||
serve = msgspec.json.decode(os.environ["PASKIA_CONFIG"].encode(), type=ServeConfig)
|
||||
assert serve.listen == ["4403"]
|
||||
assert serve.save is True
|
||||
# The CLI itself does not write the database
|
||||
assert stored_config(tmp_path).listen == ["4402"]
|
||||
|
||||
|
||||
def test_serve_listen_save_clear(run_cli, tmp_path):
|
||||
"""--listen "" --save clears the stored endpoints (back to default)."""
|
||||
"""--listen "" --save teleports a clear (back to default) for the app."""
|
||||
run_cli("init", "--listen", "4402")
|
||||
run_cli("--listen", "", "--save")
|
||||
|
||||
assert stored_config(tmp_path).listen is None
|
||||
serve = msgspec.json.decode(os.environ["PASKIA_CONFIG"].encode(), type=ServeConfig)
|
||||
assert serve.listen is None
|
||||
assert serve.save is True
|
||||
assert stored_config(tmp_path).listen == ["4402"]
|
||||
|
||||
|
||||
def test_serve_suggests_migrate_when_legacy_present(run_cli, tmp_path):
|
||||
|
||||
Reference in New Issue
Block a user