重构数据库连接管理,添加元数据支持

This commit is contained in:
2026-02-11 18:57:47 +08:00
parent ff2011ae24
commit 780a48d927
21 changed files with 1195 additions and 305 deletions

View File

@@ -7,6 +7,7 @@
from typing import Optional
from datetime import datetime
import logging
from uuid import UUID
logger = logging.getLogger(__name__)
@@ -38,18 +39,16 @@ class AuditAction:
async def log_audit_event(
action: str,
user_id: Optional[int] = None,
username: Optional[str] = None,
user_id: Optional[UUID] = None,
project_id: Optional[UUID] = None,
resource_type: Optional[str] = None,
resource_id: Optional[str] = None,
ip_address: Optional[str] = None,
user_agent: Optional[str] = None,
request_method: Optional[str] = None,
request_path: Optional[str] = None,
request_data: Optional[dict] = None,
response_status: Optional[int] = None,
error_message: Optional[str] = None,
db=None, # 新增:可选的数据库实例
session=None,
):
"""
记录审计日志
@@ -57,67 +56,60 @@ async def log_audit_event(
Args:
action: 操作类型
user_id: 用户ID
username: 用户名
project_id: 项目ID
resource_type: 资源类型
resource_id: 资源ID
ip_address: IP地址
user_agent: User-Agent
request_method: 请求方法
request_path: 请求路径
request_data: 请求数据(敏感字段需脱敏)
response_status: 响应状态码
error_message: 错误消息
db: 数据库实例(可选,如果不提供则尝试获取)
session: 元数据库会话(可选)
"""
from app.infra.db.metadata.database import SessionLocal
from app.infra.repositories.audit_repository import AuditRepository
try:
# 脱敏敏感数据
if request_data:
request_data = sanitize_sensitive_data(request_data)
# 如果没有提供数据库实例,尝试从全局获取
if db is None:
try:
from app.infra.db.postgresql.database import db as default_db
# 仅当连接池已初始化时使用
if default_db.pool:
db = default_db
except ImportError:
pass
# 如果仍然没有数据库实例
if db is None:
# 在某些上下文中可能无法获取,此时静默失败
logger.warning("No database instance provided for audit logging")
return
audit_repo = AuditRepository(db)
if request_data:
request_data = sanitize_sensitive_data(request_data)
if session is None:
async with SessionLocal() as session:
audit_repo = AuditRepository(session)
await audit_repo.create_log(
user_id=user_id,
project_id=project_id,
action=action,
resource_type=resource_type,
resource_id=resource_id,
ip_address=ip_address,
request_method=request_method,
request_path=request_path,
request_data=request_data,
response_status=response_status,
)
else:
audit_repo = AuditRepository(session)
await audit_repo.create_log(
user_id=user_id,
username=username,
project_id=project_id,
action=action,
resource_type=resource_type,
resource_id=resource_id,
ip_address=ip_address,
user_agent=user_agent,
request_method=request_method,
request_path=request_path,
request_data=request_data,
response_status=response_status,
error_message=error_message,
)
logger.info(
f"Audit log created: action={action}, user={username or user_id}, "
f"resource={resource_type}:{resource_id}"
)
except Exception as e:
# 审计日志失败不应影响业务流程
logger.error(f"Failed to create audit log: {e}", exc_info=True)
logger.info(
"Audit log created: action=%s, user=%s, project=%s, resource=%s:%s",
action,
user_id,
project_id,
resource_type,
resource_id,
)
def sanitize_sensitive_data(data: dict) -> dict:

View File

@@ -35,10 +35,41 @@ class Settings(BaseSettings):
INFLUXDB_ORG: str = "org"
INFLUXDB_BUCKET: str = "bucket"
# Metadata Database Config (PostgreSQL)
METADATA_DB_NAME: str = "system_hub"
METADATA_DB_HOST: str = "localhost"
METADATA_DB_PORT: str = "5432"
METADATA_DB_USER: str = "postgres"
METADATA_DB_PASSWORD: str = "password"
METADATA_DB_POOL_SIZE: int = 5
METADATA_DB_MAX_OVERFLOW: int = 10
PROJECT_PG_CACHE_SIZE: int = 50
PROJECT_TS_CACHE_SIZE: int = 50
PROJECT_PG_POOL_SIZE: int = 5
PROJECT_PG_MAX_OVERFLOW: int = 10
PROJECT_TS_POOL_MIN_SIZE: int = 1
PROJECT_TS_POOL_MAX_SIZE: int = 10
# Keycloak JWT (optional override)
KEYCLOAK_PUBLIC_KEY: str = ""
KEYCLOAK_ALGORITHM: str = "RS256"
# Auth bypass (temporary)
AUTH_DISABLED: bool = False
@property
def SQLALCHEMY_DATABASE_URI(self) -> str:
return f"postgresql://{self.DB_USER}:{self.DB_PASSWORD}@{self.DB_HOST}:{self.DB_PORT}/{self.DB_NAME}"
@property
def METADATA_DATABASE_URI(self) -> str:
return (
f"postgresql+psycopg://{self.METADATA_DB_USER}:{self.METADATA_DB_PASSWORD}"
f"@{self.METADATA_DB_HOST}:{self.METADATA_DB_PORT}/{self.METADATA_DB_NAME}"
)
class Config:
env_file = ".env"
extra = "ignore"