| """
|
| Port management API.
|
|
|
| Single Responsibility: only handles port CRUD for zones.
|
| Reverse proxy logic is in proxy.py — separate reason to change.
|
| """
|
|
|
| from fastapi import APIRouter, Depends, Form, HTTPException
|
|
|
| from auth import AuthUser, get_current_user
|
| from config import MIN_PORT, MAX_PORT
|
| from storage import load_meta, save_meta, check_zone_owner
|
|
|
| router = APIRouter(prefix="/api/zones/{zone_name}/ports", tags=["ports"])
|
|
|
|
|
| def _validate_port(port: int):
|
| if not (MIN_PORT <= port <= MAX_PORT):
|
| raise ValueError(f"Port must be between {MIN_PORT} and {MAX_PORT}")
|
|
|
|
|
| def _validate_zone(meta: dict, zone_name: str):
|
| if zone_name not in meta:
|
| raise ValueError(f"Zone '{zone_name}' does not exist")
|
|
|
|
|
| @router.get("")
|
| def list_ports(zone_name: str, user: AuthUser = Depends(get_current_user)):
|
| try:
|
| check_zone_owner(zone_name, user.sub, user.role)
|
| meta = load_meta()
|
| _validate_zone(meta, zone_name)
|
| return meta[zone_name].get("ports", [])
|
| except ValueError as e:
|
| raise HTTPException(400, str(e))
|
|
|
|
|
| @router.post("")
|
| def add_port(zone_name: str, port: int = Form(...), label: str = Form(""), user: AuthUser = Depends(get_current_user)):
|
| try:
|
| _validate_port(port)
|
| check_zone_owner(zone_name, user.sub, user.role)
|
| meta = load_meta()
|
| _validate_zone(meta, zone_name)
|
|
|
| ports = meta[zone_name].setdefault("ports", [])
|
| for p in ports:
|
| if p["port"] == port:
|
| raise ValueError(f"Port {port} already mapped in zone '{zone_name}'")
|
|
|
| entry = {"port": port, "label": label or f"Port {port}"}
|
| ports.append(entry)
|
| save_meta(meta)
|
| return entry
|
| except ValueError as e:
|
| raise HTTPException(400, str(e))
|
|
|
|
|
| @router.delete("/{port}")
|
| def remove_port(zone_name: str, port: int, user: AuthUser = Depends(get_current_user)):
|
| try:
|
| check_zone_owner(zone_name, user.sub, user.role)
|
| meta = load_meta()
|
| _validate_zone(meta, zone_name)
|
|
|
| ports = meta[zone_name].get("ports", [])
|
| before = len(ports)
|
| meta[zone_name]["ports"] = [p for p in ports if p["port"] != port]
|
| if len(meta[zone_name]["ports"]) == before:
|
| raise ValueError(f"Port {port} not found in zone '{zone_name}'")
|
| save_meta(meta)
|
| return {"ok": True}
|
| except ValueError as e:
|
| raise HTTPException(400, str(e))
|
|
|