```
feat: 添加数据库迁移脚本并更新Dockerfile配置 - 在Dockerfile中添加alembic配置文件和目录的复制指令 - 更新alembic/env.py注册新的模块模型:events、workflows、writebacks - 生成完整的初始数据库schema迁移脚本,包含以下表: - approval_requests, attendance_records, audit_logs, domain_events - expenses, feishu_event_receipts, fund_accounts, legacy_sync_runs - official_writeback_runs, performance_metrics, policies, procurements - projects, report_push_runs, risk_event_actions, risk_events - standards, suppliers, work_reports, work_tasks, workflow_actions - workflow_instances等21个数据表结构定义 - 在API路由器中添加新模块的路由:events、workflows、writebacks、observability ```
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
import json
|
||||
from functools import lru_cache
|
||||
from typing import Any
|
||||
from typing import Annotated, Any
|
||||
|
||||
from pydantic import Field, field_validator
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
from pydantic_settings import BaseSettings, NoDecode, SettingsConfigDict
|
||||
|
||||
from app.core.constants import (
|
||||
ActorValue,
|
||||
@@ -50,6 +50,7 @@ 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"
|
||||
@@ -57,8 +58,8 @@ class Settings(BaseSettings):
|
||||
openclaw_ws_url: str | None = None
|
||||
openclaw_api_key: str | None = None
|
||||
openclaw_gateway_token: str | None = None
|
||||
openclaw_allowed_tools: list[str] = Field(default_factory=list)
|
||||
openclaw_allowed_actions: list[str] = Field(
|
||||
openclaw_allowed_tools: Annotated[list[str], NoDecode] = Field(default_factory=list)
|
||||
openclaw_allowed_actions: Annotated[list[str], NoDecode] = Field(
|
||||
default_factory=lambda: [DEFAULT_OPENCLAW_ACTION_JSON]
|
||||
)
|
||||
hermes_base_url: str = "http://127.0.0.1:2073/v1"
|
||||
@@ -83,6 +84,10 @@ 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
|
||||
@@ -99,12 +104,27 @@ 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("openclaw_allowed_tools", "openclaw_allowed_actions", mode="before")
|
||||
@field_validator(
|
||||
"openclaw_allowed_tools",
|
||||
"openclaw_allowed_actions",
|
||||
"feishu_approval_approver_ids",
|
||||
mode="before",
|
||||
)
|
||||
@classmethod
|
||||
def parse_csv_list(cls, value: str | list[str]) -> list[str]:
|
||||
def parse_csv_list(cls, value: str | list[str] | None) -> list[str]:
|
||||
if value is None:
|
||||
return []
|
||||
if isinstance(value, list):
|
||||
return value
|
||||
return [item.strip() for item in value.split(",") if item.strip()]
|
||||
return [str(item).strip() for item in value if str(item).strip()]
|
||||
text = value.strip()
|
||||
if not text:
|
||||
return []
|
||||
if text.startswith("["):
|
||||
data = json.loads(text)
|
||||
if not isinstance(data, list):
|
||||
raise ValueError(ConfigErrorDetail.CORS_ORIGINS_FORMAT)
|
||||
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("masked_response_fields", mode="before")
|
||||
@classmethod
|
||||
|
||||
@@ -15,6 +15,7 @@ class HttpHeader(StrEnum):
|
||||
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"
|
||||
|
||||
|
||||
class ApiResponseKey(StrEnum):
|
||||
|
||||
18
app/core/middleware.py
Normal file
18
app/core/middleware.py
Normal file
@@ -0,0 +1,18 @@
|
||||
import uuid
|
||||
from collections.abc import Callable
|
||||
|
||||
from fastapi import Request, Response
|
||||
|
||||
from app.core.constants import HttpHeader
|
||||
from app.core.request_context import reset_request_id, set_request_id
|
||||
|
||||
|
||||
async def request_id_middleware(request: Request, call_next: Callable) -> Response:
|
||||
request_id = request.headers.get(HttpHeader.X_REQUEST_ID) or uuid.uuid4().hex
|
||||
token = set_request_id(request_id)
|
||||
try:
|
||||
response = await call_next(request)
|
||||
response.headers[HttpHeader.X_REQUEST_ID] = request_id
|
||||
return response
|
||||
finally:
|
||||
reset_request_id(token)
|
||||
16
app/core/request_context.py
Normal file
16
app/core/request_context.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from contextvars import ContextVar, Token
|
||||
|
||||
|
||||
_request_id: ContextVar[str | None] = ContextVar("request_id", default=None)
|
||||
|
||||
|
||||
def set_request_id(request_id: str) -> Token:
|
||||
return _request_id.set(request_id)
|
||||
|
||||
|
||||
def reset_request_id(token: Token) -> None:
|
||||
_request_id.reset(token)
|
||||
|
||||
|
||||
def get_request_id() -> str | None:
|
||||
return _request_id.get()
|
||||
Reference in New Issue
Block a user