Files
opsxsql/backend/app/api/v1/endpoints/audit_rule.py
T
logan bab55d3927 feat: 审核规则模版+设置页面整合+去掉角色切换demo
后端:
- AuditRule 新增 value 字段, rule_type 扩展 DDL/DML
- MySQL/PG/Redis 预设规则种子接口 POST /audit-rules/seed/{db_type}
- 公告管理 CRUD (announcements)
- 消息推送 CRUD+测试 (notifications: 钉钉/飞书/邮件)
- LDAP 配置 CRUD+测试连接 (ldap-config)
- 164 tests passing (+31 新增)

前端:
- AuditRulesPage 重写: DB类型Tab+DDL/DML分组+参数编辑
- SettingsPage: 系统设置主页(审核规则/公告/推送/LDAP)
- NotificationPage: 钉钉/飞书/SMTP配置
- LdapConfigPage: LDAP设置+测试连接
- AnnouncementPage: 公告Markdown编辑
- 去掉左下角角色切换demo
- 侧边栏合并为: 系统设置(含审核规则+公告+推送+LDAP)
2026-05-22 22:26:10 +08:00

174 lines
5.7 KiB
Python

"""Audit rule management endpoints."""
from typing import Optional, List
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy import select, func
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.database import get_db
from app.core.security import require_role
from app.models.user import User
from app.models.audit_rule import AuditRule
from app.schemas.audit_rule import AuditRuleCreate, AuditRuleUpdate, AuditRuleResponse
from app.services.audit_rule_seeds import SEED_RULES_MAP
router = APIRouter(prefix="/audit-rules", tags=["审核规则"])
@router.get("", response_model=List[AuditRuleResponse])
async def list_audit_rules(
rule_type: Optional[str] = None,
severity: Optional[str] = None,
db_type: Optional[str] = None,
is_enabled: Optional[bool] = None,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin", "dba")),
):
stmt = select(AuditRule).order_by(AuditRule.id)
if rule_type:
stmt = stmt.where(AuditRule.rule_type == rule_type)
if severity:
stmt = stmt.where(AuditRule.severity == severity)
if db_type:
stmt = stmt.where((AuditRule.db_type == db_type) | (AuditRule.db_type == "all"))
if is_enabled is not None:
stmt = stmt.where(AuditRule.is_enabled == is_enabled)
result = await db.execute(stmt)
return result.scalars().all()
@router.post("", response_model=AuditRuleResponse, status_code=201)
async def create_audit_rule(
data: AuditRuleCreate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin")),
):
exists = await db.execute(select(AuditRule).where(AuditRule.name == data.name))
if exists.scalar_one_or_none():
raise HTTPException(status_code=400, detail="规则名称已存在")
rule = AuditRule(**data.model_dump())
db.add(rule)
await db.commit()
await db.refresh(rule)
return rule
@router.get("/stats/summary")
async def rule_stats(
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin", "dba")),
):
total_stmt = select(func.count(AuditRule.id))
total = (await db.execute(total_stmt)).scalar()
enabled_stmt = select(func.count(AuditRule.id)).where(AuditRule.is_enabled == True)
enabled = (await db.execute(enabled_stmt)).scalar()
type_stmt = select(AuditRule.rule_type, func.count(AuditRule.id)).group_by(AuditRule.rule_type)
type_result = (await db.execute(type_stmt)).all()
by_type = {row[0]: row[1] for row in type_result}
return {"total": total, "enabled": enabled, "disabled": total - enabled, "by_type": by_type}
@router.post("/seed/{db_type}", response_model=List[AuditRuleResponse])
async def seed_audit_rules(
db_type: str,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin")),
):
"""批量初始化预设审核规则种子数据。"""
db_type_lower = db_type.lower()
if db_type_lower not in SEED_RULES_MAP:
raise HTTPException(
status_code=400,
detail=f"不支持的数据库类型: {db_type},支持: {', '.join(SEED_RULES_MAP.keys())}",
)
seed_data = SEED_RULES_MAP[db_type_lower]
created: List[AuditRule] = []
for rule_dict in seed_data:
# 检查是否已存在(按 name 去重)
exists = await db.execute(select(AuditRule).where(AuditRule.name == rule_dict["name"]))
if exists.scalar_one_or_none():
continue # 跳过已存在的规则
rule = AuditRule(**rule_dict)
db.add(rule)
created.append(rule)
await db.commit()
for rule in created:
await db.refresh(rule)
return created
@router.get("/{rule_id}", response_model=AuditRuleResponse)
async def get_audit_rule(
rule_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin", "dba")),
):
result = await db.execute(select(AuditRule).where(AuditRule.id == rule_id))
rule = result.scalar_one_or_none()
if not rule:
raise HTTPException(status_code=404, detail="规则不存在")
return rule
@router.put("/{rule_id}", response_model=AuditRuleResponse)
async def update_audit_rule(
rule_id: int,
data: AuditRuleUpdate,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin")),
):
result = await db.execute(select(AuditRule).where(AuditRule.id == rule_id))
rule = result.scalar_one_or_none()
if not rule:
raise HTTPException(status_code=404, detail="规则不存在")
update_data = data.model_dump(exclude_unset=True)
for key, value in update_data.items():
setattr(rule, key, value)
await db.commit()
await db.refresh(rule)
return rule
@router.delete("/{rule_id}")
async def delete_audit_rule(
rule_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin")),
):
result = await db.execute(select(AuditRule).where(AuditRule.id == rule_id))
rule = result.scalar_one_or_none()
if not rule:
raise HTTPException(status_code=404, detail="规则不存在")
await db.delete(rule)
await db.commit()
return {"message": "规则已删除"}
@router.post("/{rule_id}/toggle", response_model=AuditRuleResponse)
async def toggle_audit_rule(
rule_id: int,
db: AsyncSession = Depends(get_db),
current_user: User = Depends(require_role("admin", "dba")),
):
result = await db.execute(select(AuditRule).where(AuditRule.id == rule_id))
rule = result.scalar_one_or_none()
if not rule:
raise HTTPException(status_code=404, detail="规则不存在")
rule.is_enabled = not rule.is_enabled
await db.commit()
await db.refresh(rule)
return rule