""" 过滤规则和规则组管理 API。 """ from typing import Annotated, Any, Optional from fastapi import Depends, Query from app.api.context import get_host_runtime from app.api.dependencies.auth import ( get_current_active_superuser_async, get_current_active_user_async, ) from app.api.principal import ApiPrincipal from app.api.response import ResponseAPIRouter from app.application.filtering import FilterRuleService from app.application.rules import RuleGroupMutationConflictError from app.schemas.common import JsonObject as _SchemaJsonObject from app.schemas.response import Response as _SchemaResponse from app.schemas.rule import ( CustomFilterRuleCreateRequest as _SchemaCustomFilterRuleCreateRequest, ) from app.schemas.rule import ( CustomFilterRuleReorderRequest as _SchemaCustomFilterRuleReorderRequest, ) from app.schemas.rule import ( CustomFilterRuleUpdateRequest as _SchemaCustomFilterRuleUpdateRequest, ) from app.schemas.rule import ( FilterRuleGroupCreateRequest as _SchemaFilterRuleGroupCreateRequest, ) from app.schemas.rule import ( FilterRuleGroupReorderRequest as _SchemaFilterRuleGroupReorderRequest, ) from app.schemas.rule import ( FilterRuleGroupUpdateRequest as _SchemaFilterRuleGroupUpdateRequest, ) from app.startup.composition.context import HostRuntime router = ResponseAPIRouter() def _service(runtime: HostRuntime) -> FilterRuleService: """ 从宿主组合根收窄出规则 API 所需端口。 """ return FilterRuleService( runtime.agent.subscriptions, runtime.agent.async_rule_group_mutation_scope, runtime.system.publish_config_changed, ) @router.get( "/builtin", summary="查询内置过滤规则", response_model=_SchemaResponse[_SchemaJsonObject], ) async def query_builtin_rules( rule_ids: Annotated[ Optional[list[str]], Query( description=( "要返回的内置规则标识;可在查询字符串中重复传入 rule_ids,省略时列出全部内置规则。" ) ), ] = None, _: ApiPrincipal = Depends(get_current_active_user_async), ) -> _SchemaResponse[Any]: """ 返回内置规则及规则串语法。 """ return _SchemaResponse( success=True, data=FilterRuleService.query_builtin(rule_ids), ) @router.get( "/custom", summary="查询自定义过滤规则", response_model=_SchemaResponse[_SchemaJsonObject], ) async def query_custom_rules( rule_ids: Annotated[ Optional[list[str]], Query( description=( "要返回的自定义规则标识;可在查询字符串中重复传入 rule_ids,省略时列出全部自定义规则。" ) ), ] = None, include_group_refs: bool = True, _: ApiPrincipal = Depends(get_current_active_user_async), ) -> _SchemaResponse[Any]: """ 返回自定义规则和可选规则组引用。 """ return _SchemaResponse( success=True, data=FilterRuleService.query_custom( rule_ids, include_group_refs=include_group_refs, ), ) @router.get( "/groups", summary="查询过滤规则组", response_model=_SchemaResponse[_SchemaJsonObject], ) async def query_rule_groups( group_names: Annotated[ Optional[list[str]], Query( description=( "要返回的规则组名称;可在查询字符串中重复传入 group_names,省略时列出全部规则组。" ) ), ] = None, include_usage: bool = True, _: ApiPrincipal = Depends(get_current_active_user_async), runtime: HostRuntime = Depends(get_host_runtime), ) -> _SchemaResponse[Any]: """ 返回规则组、解析层级和可选引用位置。 """ return _SchemaResponse( success=True, data=await _service(runtime).query_groups( group_names, include_usage=include_usage, ), ) @router.post( "/custom", summary="新增自定义过滤规则", response_model=_SchemaResponse[_SchemaJsonObject], ) async def add_custom_rule( payload: _SchemaCustomFilterRuleCreateRequest, _: ApiPrincipal = Depends(get_current_active_superuser_async), runtime: HostRuntime = Depends(get_host_runtime), ) -> _SchemaResponse[Any]: """ 校验并新增一条自定义过滤规则。 """ try: data = await _service(runtime).add_custom(**payload.model_dump()) except (ValueError, RuleGroupMutationConflictError) as error: return _SchemaResponse(success=False, message=str(error)) return _SchemaResponse(success=True, message=data.get("message") or "", data=data) @router.put( "/custom/reorder", summary="调整自定义过滤规则顺序", response_model=_SchemaResponse[_SchemaJsonObject], ) async def reorder_custom_rules( payload: _SchemaCustomFilterRuleReorderRequest, _: ApiPrincipal = Depends(get_current_active_superuser_async), runtime: HostRuntime = Depends(get_host_runtime), ) -> _SchemaResponse[Any]: """ 只调整现有自定义规则顺序并拒绝过期集合覆盖。 """ try: data = await _service(runtime).reorder_custom( payload.rule_ids, expected_rule_ids=payload.expected_rule_ids, ) except (ValueError, RuleGroupMutationConflictError) as error: return _SchemaResponse(success=False, message=str(error)) return _SchemaResponse(success=True, message=data.get("message") or "", data=data) @router.put( "/custom/{rule_id}", summary="更新自定义过滤规则", response_model=_SchemaResponse[_SchemaJsonObject], ) async def update_custom_rule( rule_id: str, payload: _SchemaCustomFilterRuleUpdateRequest, _: ApiPrincipal = Depends(get_current_active_superuser_async), runtime: HostRuntime = Depends(get_host_runtime), ) -> _SchemaResponse[Any]: """ 更新自定义规则并在改名时原子重写引用。 """ try: data = await _service(runtime).update_custom( current_rule_id=rule_id, **payload.model_dump(), ) except (ValueError, RuleGroupMutationConflictError) as error: return _SchemaResponse(success=False, message=str(error)) return _SchemaResponse(success=True, message=data.get("message") or "", data=data) @router.delete( "/custom/{rule_id}", summary="删除自定义过滤规则", response_model=_SchemaResponse[_SchemaJsonObject], ) async def delete_custom_rule( rule_id: str, _: ApiPrincipal = Depends(get_current_active_superuser_async), runtime: HostRuntime = Depends(get_host_runtime), ) -> _SchemaResponse[Any]: """ 删除一条未被规则组引用的自定义过滤规则。 """ try: data = await _service(runtime).delete_custom(rule_id) except (ValueError, RuleGroupMutationConflictError) as error: return _SchemaResponse(success=False, message=str(error)) return _SchemaResponse(success=True, message=data.get("message") or "", data=data) @router.post( "/groups", summary="新增过滤规则组", response_model=_SchemaResponse[_SchemaJsonObject], ) async def add_rule_group( payload: _SchemaFilterRuleGroupCreateRequest, _: ApiPrincipal = Depends(get_current_active_superuser_async), runtime: HostRuntime = Depends(get_host_runtime), ) -> _SchemaResponse[Any]: """ 新增一个已经完成语法与引用校验的规则组。 """ try: data = await _service(runtime).add_group(**payload.model_dump()) except (ValueError, RuleGroupMutationConflictError) as error: return _SchemaResponse(success=False, message=str(error)) return _SchemaResponse(success=True, message=data.get("message") or "", data=data) @router.put( "/groups/reorder", summary="调整过滤规则组顺序", response_model=_SchemaResponse[_SchemaJsonObject], ) async def reorder_rule_groups( payload: _SchemaFilterRuleGroupReorderRequest, _: ApiPrincipal = Depends(get_current_active_superuser_async), runtime: HostRuntime = Depends(get_host_runtime), ) -> _SchemaResponse[Any]: """ 只调整现有规则组顺序并拒绝过期集合覆盖。 """ try: data = await _service(runtime).reorder_groups( payload.group_names, expected_group_names=payload.expected_group_names, ) except (ValueError, RuleGroupMutationConflictError) as error: return _SchemaResponse(success=False, message=str(error)) return _SchemaResponse(success=True, message=data.get("message") or "", data=data) @router.put( "/groups/{name:path}", summary="更新过滤规则组", response_model=_SchemaResponse[_SchemaJsonObject], ) async def update_rule_group( name: str, payload: _SchemaFilterRuleGroupUpdateRequest, _: ApiPrincipal = Depends(get_current_active_superuser_async), runtime: HostRuntime = Depends(get_host_runtime), ) -> _SchemaResponse[Any]: """ 更新规则组并原子重写全部名称引用。 """ try: data = await _service(runtime).update_group( current_name=name, **payload.model_dump(), ) except (ValueError, RuleGroupMutationConflictError) as error: return _SchemaResponse(success=False, message=str(error)) return _SchemaResponse(success=True, message=data.get("message") or "", data=data) @router.delete( "/groups/{name:path}", summary="删除过滤规则组", response_model=_SchemaResponse[_SchemaJsonObject], ) async def delete_rule_group( name: str, _: ApiPrincipal = Depends(get_current_active_superuser_async), runtime: HostRuntime = Depends(get_host_runtime), ) -> _SchemaResponse[Any]: """ 删除规则组并原子清理全部全局和订阅引用。 """ try: data = await _service(runtime).delete_group(name) except (ValueError, RuleGroupMutationConflictError) as error: return _SchemaResponse(success=False, message=str(error)) return _SchemaResponse(success=True, message=data.get("message") or "", data=data)