| """
|
| 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")
|
|
|
|
|
| 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))
|
|
|