feat: 站点 URL 白名单/黑名单管理 API

- GET/POST /admin/site/url-rules、DELETE /admin/site/url-rules?rule_type&host
- 存储于 site.url_whitelist / site.url_blacklist(SystemConfig,公共 /site/config 可读)
- 域名规范化校验;写审计
This commit is contained in:
Pine
2026-09-07 22:19:26 +08:00
parent ff383c0811
commit 7cce5cd14a
2 changed files with 94 additions and 1 deletions
+88 -1
View File
@@ -11,7 +11,7 @@ from fastapi import APIRouter, Depends, File, HTTPException, Request, Response,
from pydantic import BaseModel
from ..dependencies import get_db
from ..schemas.operator import TaskCreateRequest, TaskUpdateRequest, TaskStatusRequest, TaskAssignRequest, TaskRecommendRequest, TaskSelectRecommendRequest, ProviderCreateRequest, ProviderUpdateRequest, ContentCreateRequest, ContentStatusRequest, ContentUpdateRequest, ContentReviewRequest, ConfigUpdateRequest, ComputePingResponse, ComputeBalanceRequest, ComputeProvisionRequest, ComputeProvisionResponse, CourseCreateRequest, CourseStatusRequest, CourseUpdateRequest, ActivityCreateRequest, ActivityStatusRequest, ActivityUpdateRequest, BookingUpdateRequest, TestCreateRequest, TestStatusRequest
from ..schemas.operator import TaskCreateRequest, TaskUpdateRequest, TaskStatusRequest, TaskAssignRequest, TaskRecommendRequest, TaskSelectRecommendRequest, ProviderCreateRequest, ProviderUpdateRequest, ContentCreateRequest, ContentStatusRequest, ContentUpdateRequest, ContentReviewRequest, ConfigUpdateRequest, UrlRuleRequest, ComputePingResponse, ComputeBalanceRequest, ComputeProvisionRequest, ComputeProvisionResponse, CourseCreateRequest, CourseStatusRequest, CourseUpdateRequest, ActivityCreateRequest, ActivityStatusRequest, ActivityUpdateRequest, BookingUpdateRequest, TestCreateRequest, TestStatusRequest
from ...rbac import require_permission, require_roles, write_audit
from ...infrastructure.repositories import Database
from ...services import compute_client
@@ -619,6 +619,93 @@ async def list_config(
return await db.config.all()
# ── 站点 URL 白名单 / 黑名单(应用内网页容器认证标识)──────────────────────
URL_WHITELIST_KEY = "site.url_whitelist"
URL_BLACKLIST_KEY = "site.url_blacklist"
def _split_host_list(raw: str | None) -> list[str]:
if not raw:
return []
return [
h for h in (x.strip() for x in raw.replace("", ",").split(",")) if h
]
def _normalize_host(host: str) -> str:
h = host.strip().lower()
h = h.replace("https://", "").replace("http://", "")
h = h.split("/")[0].split(":")[0].strip()
if not h or any(ch.isspace() for ch in h):
raise HTTPException(status_code=422, detail="域名格式不正确")
return h
async def _get_url_rules(db: Database) -> dict:
return {
"whitelist": _split_host_list(await db.config.get(URL_WHITELIST_KEY)),
"blacklist": _split_host_list(await db.config.get(URL_BLACKLIST_KEY)),
}
async def _save_url_rules(db: Database, rules: dict) -> None:
await db.config.set(URL_WHITELIST_KEY, ",".join(rules["whitelist"]),
description="应用内网页容器白名单(域名)")
await db.config.set(URL_BLACKLIST_KEY, ",".join(rules["blacklist"]),
description="应用内网页容器黑名单(域名,命中禁止打开)")
@router.get("/site/url-rules", summary="站点 URL 白名单/黑名单列表")
async def list_url_rules(
db: Database = Depends(get_db),
_u: dict = Depends(require_roles("operator")),
):
return await _get_url_rules(db)
@router.post("/site/url-rules", summary="新增白名单/黑名单域名")
async def add_url_rule(
req: UrlRuleRequest,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_permission("action:config.manage")),
):
if req.rule_type not in ("whitelist", "blacklist"):
raise HTTPException(status_code=422, detail="rule_type 仅支持 whitelist/blacklist")
host = _normalize_host(req.host)
rules = await _get_url_rules(db)
key = req.rule_type
if host in rules[key]:
raise HTTPException(status_code=409, detail="该域名已在名单中")
rules[key].append(host)
await _save_url_rules(db, rules)
await write_audit(db, action="url_rule.add", resource="site", resource_id=f"{key}:{host}",
detail=req.host, user=actor, request=request)
return rules
@router.delete("/site/url-rules", summary="删除白名单/黑名单域名")
async def remove_url_rule(
rule_type: str,
host: str,
request: Request,
db: Database = Depends(get_db),
actor: dict = Depends(require_permission("action:config.manage")),
):
if rule_type not in ("whitelist", "blacklist"):
raise HTTPException(status_code=422, detail="rule_type 仅支持 whitelist/blacklist")
host = _normalize_host(host)
rules = await _get_url_rules(db)
key = rule_type
if host not in rules[key]:
raise HTTPException(status_code=404, detail="该域名不在名单中")
rules[key].remove(host)
await _save_url_rules(db, rules)
await write_audit(db, action="url_rule.remove", resource="site", resource_id=f"{key}:{host}",
detail=host, user=actor, request=request)
return rules
@router.put("/config/{key}", summary="更新系统配置")
async def set_config(
key: str,
+6
View File
@@ -164,6 +164,12 @@ class ConfigUpdateRequest(BaseModel):
description: str = ""
class UrlRuleRequest(BaseModel):
"""站点 URL 白名单 / 黑名单单条操作。"""
rule_type: str # whitelist | blacklist
host: str
# ── 任务管理 ──────────────────────────────────────────────────────────────