Files
Aqr-K bcbf79f030 fix(media): preserve recognition and rule mutation identities (#7144)
* fix(media): preserve recognition and rule mutation identities

* test: cover typed classification facts with alias policies

---------

Co-authored-by: jxxghp <jxxghp@gmail.com>
2026-10-23 07:33:27 +08:00

310 lines
10 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
过滤规则和规则组管理 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)