refactor: 移除审批和回写功能模块

移除了整个审批(approvals)和官方回写(writebacks)功能模块,
包括相关模型、路由、服务和配置项。更新了数据库迁移文件,
删除了相关的审批请求表和官方回写运行表。同时从API路由器中
移除了相应的路由,并调整了安全常量和字段验证器以匹配变更。
```
This commit is contained in:
2026-07-08 17:08:59 +08:00
parent 19e59e83cc
commit 0a153b264a
48 changed files with 135 additions and 2225 deletions

View File

@@ -21,6 +21,7 @@ class Settings(BaseSettings):
app_name: str = "Company AI Management Platform"
app_env: str = "local"
debug: bool = False
read_only_mode: bool = True
api_prefix: str = "/api/v1"
api_key: str | None = None
api_actor: str = ActorValue.API
@@ -28,9 +29,6 @@ class Settings(BaseSettings):
audit_api_key: str | None = None
audit_api_actor: str = ActorValue.AUDITOR
audit_api_keys: list[dict[str, Any]] = Field(default_factory=list)
approval_api_key: str | None = None
approval_api_actor: str = ActorValue.APPROVER
approval_api_keys: list[dict[str, Any]] = Field(default_factory=list)
cors_origins: list[str] = Field(default_factory=lambda: ["*"])
mask_sensitive_responses: bool = True
masked_response_fields: list[str] = Field(default_factory=list)
@@ -50,8 +48,6 @@ class Settings(BaseSettings):
feishu_verification_token: str | None = None
feishu_encrypt_key: str | None = None
feishu_default_chat_id: str | None = None
feishu_approval_approver_ids: Annotated[list[str], NoDecode] = Field(default_factory=list)
model_provider: str = DEFAULT_MODEL_PROVIDER
openclaw_base_url: str = "http://127.0.0.1:2070"
openclaw_http_url: str | None = None
@@ -84,11 +80,6 @@ class Settings(BaseSettings):
legacy_project_sync_cron_minute: int = 0
legacy_task_sync_cron_hour: int = 2
legacy_task_sync_cron_minute: int = 30
official_writeback_enabled: bool = False
official_api_base_url: str | None = None
official_api_token: str | None = None
official_api_timeout_seconds: float = 10.0
@field_validator("cors_origins", mode="before")
@classmethod
def parse_cors_origins(cls, value: Any) -> list[str]:
@@ -107,7 +98,6 @@ class Settings(BaseSettings):
@field_validator(
"openclaw_allowed_tools",
"openclaw_allowed_actions",
"feishu_approval_approver_ids",
mode="before",
)
@classmethod
@@ -143,7 +133,7 @@ class Settings(BaseSettings):
return [str(item).strip() for item in data if str(item).strip()]
return [item.strip() for item in text.split(",") if item.strip()]
@field_validator("api_keys", "audit_api_keys", "approval_api_keys", mode="before")
@field_validator("api_keys", "audit_api_keys", mode="before")
@classmethod
def parse_service_keys(cls, value: Any) -> list[dict[str, Any]]:
if value is None or value == "":

View File

@@ -4,7 +4,6 @@ from enum import StrEnum
class ActorValue(StrEnum):
API = "api"
AUDITOR = "auditor"
APPROVER = "approver"
SYSTEM = "system"
SCHEDULER = "scheduler"
FEISHU = "feishu"
@@ -14,7 +13,6 @@ class HttpHeader(StrEnum):
AUTHORIZATION = "Authorization"
X_API_KEY = "X-API-Key"
X_AUDIT_API_KEY = "X-Audit-API-Key"
X_APPROVAL_API_KEY = "X-Approval-API-Key"
X_REQUEST_ID = "X-Request-ID"
@@ -29,8 +27,6 @@ class ApiStatus(StrEnum):
class SecurityErrorDetail(StrEnum):
API_KEY_REQUIRED = "API_KEY is required"
INVALID_API_KEY = "Invalid API key"
APPROVAL_API_KEY_REQUIRED = "APPROVAL_API_KEY is required"
INVALID_APPROVAL_API_KEY = "Invalid approval API key"
AUDIT_API_KEY_REQUIRED = "AUDIT_API_KEY is required"
INVALID_AUDIT_API_KEY = "Invalid audit API key"

View File

@@ -0,0 +1,16 @@
from fastapi import HTTPException, status
from app.core.config import get_settings
READ_ONLY_OPERATION_DISABLED = "This service is read-only; data mutation operations are disabled"
def require_operations_enabled(detail: str = READ_ONLY_OPERATION_DISABLED) -> None:
"""Reject mutation-oriented endpoints when the product is running read-only."""
if get_settings().read_only_mode:
raise HTTPException(
status_code=status.HTTP_405_METHOD_NOT_ALLOWED,
detail=detail,
)

View File

@@ -40,34 +40,6 @@ def require_api_key(
return principal
def require_approval_api_key(
x_approval_api_key: str | None = Header(
default=None,
alias=HttpHeader.X_APPROVAL_API_KEY,
),
) -> ApiPrincipal:
"""Validate the approval API key and return the approval principal."""
settings = get_settings()
if not settings.approval_api_key and not _has_enabled_keys(settings.approval_api_keys):
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail=SecurityErrorDetail.APPROVAL_API_KEY_REQUIRED,
)
principal = _match_service_key(
x_approval_api_key,
settings.approval_api_key,
settings.approval_api_actor,
settings.approval_api_keys,
)
if principal is None:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail=SecurityErrorDetail.INVALID_APPROVAL_API_KEY,
)
return principal
def require_audit_api_key(
x_audit_api_key: str | None = Header(
default=None,