feat: unified gateway route table (static · proxy · remote)
The gateway does two things — reverse-proxy services and serve static
frontends — but the dashboard/API route table only ever showed the proxy
routes, so 'serving a frontend' and 'proxying a service' looked like unrelated
features and castle-app/power-graph-app were invisible in the route view.
Now there's one concept: a route maps an address (path '/foo' or host 'foo.lan')
to a target of one kind — static (a built dist served by file_server), proxy
(a local service port), or remote (a service on another node).
- core: caddyfile.py gains compute_routes() — the single source of truth for
the route list; generate_caddyfile_from_registry renders it (output byte-for-
byte identical, verified against the live Caddyfile). Adds a GatewayRoute
dataclass.
- api: GatewayRoute model → {address, kind, target, name, node}; GET /gateway
builds from compute_routes (incl. config for static frontends + mesh for
remote), so the table matches what Caddy actually does.
- app: Gateway panel shows Address · Kind · Target for every route (static
frontends + host routes now appear); program detail shows 'Reachable at
/foo/ · served (static)' for static frontends.
- cli: 'castle gateway status' prints the full route table.
- docs: registry.md/design.md/CLAUDE.md describe routes as one concept with
three target kinds.
core 94 / cli 24 / api 52 green; ruff + app build clean; Caddyfile unchanged.
This commit is contained in:
@@ -116,17 +116,30 @@ def _gateway_reload() -> int:
|
||||
|
||||
|
||||
def _gateway_status() -> int:
|
||||
"""Show gateway status via systemd."""
|
||||
"""Show gateway status + the full route table (static, proxy, remote)."""
|
||||
result = subprocess.run(
|
||||
["systemctl", "--user", "is-active", GATEWAY_UNIT],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
)
|
||||
status = result.stdout.strip()
|
||||
print(f"Gateway: {'running' if status == 'active' else status}")
|
||||
|
||||
if status == "active":
|
||||
print("Gateway: running")
|
||||
else:
|
||||
print(f"Gateway: {status}")
|
||||
if not REGISTRY_PATH.exists():
|
||||
print(" (no registry — run 'castle deploy')")
|
||||
return 0
|
||||
|
||||
from castle_core.generators.caddyfile import compute_routes
|
||||
|
||||
routes = compute_routes(load_registry())
|
||||
if not routes:
|
||||
print(" No routes configured.")
|
||||
return 0
|
||||
|
||||
# Each route: address → target, tagged by kind. static = files served in
|
||||
# place; proxy/remote = reverse-proxied to a process.
|
||||
print(f"\n {'ADDRESS':24} {'KIND':7} TARGET")
|
||||
for r in routes:
|
||||
target = r.target.replace("localhost:", ":") if r.kind != "static" else r.target
|
||||
print(f" {r.address:24} {r.kind:7} {target}")
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user