"""허용 도메인 CRUD.""" from __future__ import annotations from fastapi import APIRouter, HTTPException, Response from pydantic import BaseModel, Field from config_io import load_config, save_config router = APIRouter() class DomainBackend(BaseModel): host: str port: int = Field(ge=1, le=65535) class DomainCreate(BaseModel): domain: str enabled: bool = True note: str = "" backend: DomainBackend | None = None # 비우면 기본 backend 사용 class DomainPatch(BaseModel): enabled: bool | None = None note: str | None = None # 명시적으로 None 을 보내려면 클라이언트가 "backend": null 을 보내야 한다. # 빠뜨리면 기존 값 유지. backend: DomainBackend | None = None clear_backend: bool = False # True 면 기존 backend 삭제 → 기본값으로 복귀 @router.get("/domains") def list_domains() -> list[dict]: return load_config().get("allowed_domains", []) @router.post("/domains", status_code=201) def add_domain(body: DomainCreate) -> dict: cfg = load_config() domains = cfg.setdefault("allowed_domains", []) name = body.domain.strip().lower() if not name: raise HTTPException(status_code=400, detail="domain required") if any(d["domain"].lower() == name for d in domains): raise HTTPException(status_code=409, detail="domain already exists") entry: dict = {"domain": name, "enabled": body.enabled, "note": body.note} if body.backend is not None: entry["backend"] = body.backend.model_dump() domains.append(entry) save_config(cfg) return entry @router.delete("/domains/{domain}", status_code=204) def delete_domain(domain: str) -> Response: cfg = load_config() name = domain.strip().lower() before = len(cfg.get("allowed_domains", [])) cfg["allowed_domains"] = [ d for d in cfg.get("allowed_domains", []) if d["domain"].lower() != name ] if len(cfg["allowed_domains"]) == before: raise HTTPException(status_code=404, detail="domain not found") save_config(cfg) return Response(status_code=204) @router.patch("/domains/{domain}") def patch_domain(domain: str, body: DomainPatch) -> dict: cfg = load_config() name = domain.strip().lower() for d in cfg.get("allowed_domains", []): if d["domain"].lower() == name: if body.enabled is not None: d["enabled"] = body.enabled if body.note is not None: d["note"] = body.note if body.clear_backend: d.pop("backend", None) elif body.backend is not None: d["backend"] = body.backend.model_dump() save_config(cfg) return d raise HTTPException(status_code=404, detail="domain not found")