#!/usr/bin/env python3 """Start the local GovOPlaN release console.""" from __future__ import annotations import argparse import ipaddress from pathlib import Path import secrets import sys from govoplan_release.workspace import DEFAULT_WORKSPACE_ROOT def loopback_host(value: str) -> str: try: address = ipaddress.ip_address(value) except ValueError as exc: raise argparse.ArgumentTypeError( "release console host must be a numeric loopback address" ) from exc if not address.is_loopback: raise argparse.ArgumentTypeError( "release console refuses non-loopback hosts without an authenticated " "TLS deployment boundary" ) return value def main() -> int: parser = argparse.ArgumentParser(description=__doc__) parser.add_argument( "--host", type=loopback_host, default="127.0.0.1", help="Numeric loopback bind address. Defaults to 127.0.0.1.", ) parser.add_argument("--port", type=int, default=8765, help="Bind port. Defaults to 8765.") parser.add_argument("--workspace-root", type=Path, default=DEFAULT_WORKSPACE_ROOT) args = parser.parse_args() try: import uvicorn except ImportError: print("uvicorn is required. Install the meta development environment with `pip install -r requirements-dev.txt`.", file=sys.stderr) return 2 from server.app import create_app workspace_root = args.workspace_root.expanduser().resolve() token = secrets.token_urlsafe(24) app = create_app(workspace_root=workspace_root, token=token) display_host = f"[{args.host}]" if ":" in args.host else args.host url = f"http://{display_host}:{args.port}/" # URL fragments are never sent in HTTP requests. The WebUI transfers # this one-time bootstrap token to sessionStorage and clears the hash. url = f"{url}#token={token}" print("GovOPlaN release console") print(f" workspace: {workspace_root}") print(f" url: {url}") print(" mode: local release dashboard; mutating actions require explicit confirmation") uvicorn.run(app, host=args.host, port=args.port, log_level="info") return 0 if __name__ == "__main__": raise SystemExit(main())