""" Zone CRUD API. Single Responsibility: only handles zone create/list/delete. File management is in files.py, port management in ports.py. """ import shutil from datetime import datetime import httpx from fastapi import APIRouter, Depends, Form, HTTPException from auth import AuthUser, get_current_user from config import DATA_DIR, ADMIN_API_URL from storage import load_meta, save_meta, validate_zone_name, check_zone_owner from routers.terminal import kill_terminal router = APIRouter(prefix="/api/zones", tags=["zones"]) def _get_max_zones() -> int: """Fetch zone limit from Admin Worker config.""" if not ADMIN_API_URL: return 0 try: resp = httpx.get(f"{ADMIN_API_URL}/config", timeout=5) if resp.status_code == 200: return resp.json().get("max_zones", 0) except Exception: pass return 0 @router.get("") def list_zones(user: AuthUser = Depends(get_current_user)): meta = load_meta() return [ { "name": name, "created": info.get("created", ""), "description": info.get("description", ""), "exists": (DATA_DIR / name).is_dir(), } for name, info in meta.items() if user.role == "admin" or info.get("owner_id") == user.sub ] @router.post("") def create_zone(name: str = Form(...), description: str = Form(""), user: AuthUser = Depends(get_current_user)): try: validate_zone_name(name) zone_path = DATA_DIR / name if zone_path.exists(): raise ValueError(f"Zone '{name}' đã tồn tại") # Check zone limit (per-user for non-admin) max_zones = _get_max_zones() if max_zones > 0: meta = load_meta() user_zones = [n for n, info in meta.items() if info.get("owner_id") == user.sub] if len(user_zones) >= max_zones: raise ValueError(f"Đã đạt giới hạn {max_zones} zones") zone_path.mkdir(parents=True) (zone_path / "README.md").write_text( f"# {name}\n\nZone được tạo lúc {datetime.now().isoformat()}\n" ) meta = load_meta() meta[name] = { "created": datetime.now().isoformat(), "description": description, "owner_id": user.sub, "owner_name": user.username, } save_meta(meta) return {"name": name, "path": str(zone_path)} except ValueError as e: raise HTTPException(400, str(e)) @router.delete("/{zone_name}") def delete_zone(zone_name: str, user: AuthUser = Depends(get_current_user)): try: validate_zone_name(zone_name) check_zone_owner(zone_name, user.sub, user.role) zone_path = DATA_DIR / zone_name if not zone_path.exists(): raise ValueError(f"Zone '{zone_name}' không tồn tại") kill_terminal(zone_name) shutil.rmtree(zone_path) meta = load_meta() meta.pop(zone_name, None) save_meta(meta) return {"ok": True} except ValueError as e: raise HTTPException(400, str(e))