- proxy: asyncio TCP proxy with handshake parser, domain whitelist, transparent backend tunneling, SQLite logging, mtime hot reload - api: FastAPI routes for config/domains/logs/status + restart trigger - frontend: React + Vite NPM-style dashboard (dashboard/domains/logs/settings) - nginx: reverse proxy for /api -> api:8000 and / -> frontend:3000 - docker-compose: full stack with shared data volume - replace spec mc-domain-filter.md with README.md
70 lines
2.0 KiB
Python
70 lines
2.0 KiB
Python
"""허용 도메인 CRUD."""
|
|
from __future__ import annotations
|
|
|
|
from fastapi import APIRouter, HTTPException, Response
|
|
from pydantic import BaseModel
|
|
|
|
from config_io import load_config, save_config
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class DomainCreate(BaseModel):
|
|
domain: str
|
|
enabled: bool = True
|
|
note: str = ""
|
|
|
|
|
|
class DomainPatch(BaseModel):
|
|
enabled: bool | None = None
|
|
note: str | None = None
|
|
|
|
|
|
@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 = {"domain": name, "enabled": body.enabled, "note": body.note}
|
|
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
|
|
save_config(cfg)
|
|
return d
|
|
raise HTTPException(status_code=404, detail="domain not found")
|