sync: 同步 sql-platform-pro 最新代码 — 权限体系安全修复 v0.8.1

- P0: 实例访问控制全线接入权限组 (829测试)
- P1: permissions字段激活 + 操作级权限检查 (843测试)
- 前端: 删除数据权限页面, 权限组合并为唯一入口
- 新增: CodeMirror编辑器 + 查询模板 + 慢查询分析
- 新增: MongoDB管理页面 + Dashboard增强
- 修复: Pydantic V2迁移 + 前端TS错误修复
This commit is contained in:
logan committed 2026-05-23 14:41:06 +08:00
1 parent 6fcd9465cf
commit 4c9427e546
105 files changed
+23680 -471

No files matched your search

+2 -3
View File
@@ -1,6 +1,6 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field
class AnnouncementCreate(BaseModel):
@@ -23,5 +23,4 @@ class AnnouncementResponse(BaseModel):
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
model_config = ConfigDict(from_attributes=True)
+2 -3
View File
@@ -1,5 +1,5 @@
from datetime import datetime
from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field
from typing import Optional, Literal
@@ -41,5 +41,4 @@ class AuditRuleResponse(BaseModel):
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
model_config = ConfigDict(from_attributes=True)
+2 -3
View File
@@ -1,5 +1,5 @@
from typing import Optional
from pydantic import BaseModel
from pydantic import BaseModel, ConfigDict
class LoginRequest(BaseModel):
@@ -20,5 +20,4 @@ class UserBrief(BaseModel):
role: str
email: Optional[str] = None
class Config:
from_attributes = True
model_config = ConfigDict(from_attributes=True)
+2 -3
View File
@@ -1,7 +1,7 @@
from datetime import datetime
from typing import Optional
from pydantic import BaseModel
from pydantic import BaseModel, ConfigDict
class BackupCreate(BaseModel):
@@ -24,5 +24,4 @@ class BackupResponse(BaseModel):
completed_at: Optional[datetime] = None
remark: Optional[str] = None
class Config:
from_attributes = True
model_config = ConfigDict(from_attributes=True)
+101
View File
@@ -0,0 +1,101 @@
"""执行相关 Schema — 工单执行报告和数据验证模型。"""
from typing import List, Optional
from pydantic import BaseModel, Field
class ExecutionReportResponse(BaseModel):
"""工单执行报告响应模型。
包含工单执行后的完整结果信息,用于 API 返回。
Attributes:
status: 执行状态(success / failed / partial)
affected_rows: 影响行数(SQL)或成功命令数(Redis)或修改文档数(MongoDB)
duration_ms: 执行耗时(毫秒)
validation_passed: 验证是否通过
warnings: 警告信息列表
errors: 错误信息列表
raw_output: 原始执行输出
before_snapshot_summary: 执行前快照摘要
after_snapshot_summary: 执行后快照摘要
"""
status: str = Field(..., description="执行状态: success / failed / partial")
affected_rows: int = Field(0, ge=0, description="影响行数")
duration_ms: int = Field(0, ge=0, description="执行耗时(毫秒)")
validation_passed: bool = Field(True, description="验证是否通过")
warnings: List[str] = Field(default_factory=list, description="警告信息")
errors: List[str] = Field(default_factory=list, description="错误信息")
raw_output: Optional[str] = Field(None, description="原始执行输出")
before_snapshot_summary: Optional[str] = Field(None, description="执行前快照摘要")
after_snapshot_summary: Optional[str] = Field(None, description="执行后快照摘要")
class Config:
json_schema_extra = {
"example": {
"status": "success",
"affected_rows": 3,
"duration_ms": 145,
"validation_passed": True,
"warnings": [],
"errors": [],
"raw_output": "[UPDATE users SET status='active' WHERE ...] 影响 3 行",
"before_snapshot_summary": "UPDATE 表 `users`: 将有 3 行被更新 (WHERE: status='inactive')",
"after_snapshot_summary": "表 `users`: 当前 150 行",
}
}
class RedisCommandRequest(BaseModel):
"""Redis 命令请求模型。"""
command: str = Field(..., min_length=1, description="Redis 命令")
db: int = Field(0, ge=0, le=15, description="数据库编号")
class RedisTicketContent(BaseModel):
"""Redis 工单内容模型(JSON 格式)。"""
commands: List[RedisCommandRequest] = Field(
..., min_length=1, description="命令列表"
)
class MongoOperationRequest(BaseModel):
"""MongoDB 操作请求模型。
支持的操作类型:
- insertOne / insertMany: 插入文档
- updateOne / updateMany: 更新文档
- deleteOne / deleteMany: 删除文档
- replaceOne: 替换文档
"""
collection: str = Field(..., min_length=1, description="集合名称")
operation: str = Field(
...,
pattern=r"^(insertOne|insertMany|updateOne|updateMany|deleteOne|deleteMany|replaceOne)$",
description="操作类型",
)
filter: Optional[dict] = Field(None, description="查询条件")
update: Optional[dict] = Field(None, description="更新内容")
document: Optional[dict] = Field(None, description="插入/替换的文档")
documents: Optional[List[dict]] = Field(None, description="批量插入的文档列表")
options: Optional[dict] = Field(None, description="额外选项")
class TicketExecuteResponse(BaseModel):
"""工单执行响应模型(API 返回)。"""
id: int = Field(..., description="工单ID")
title: str = Field(..., description="工单标题")
status: str = Field(..., description="工单状态")
db_type: str = Field(..., description="数据库类型")
execute_result: Optional[str] = Field(None, description="执行结果摘要")
execute_time: Optional[str] = Field(None, description="执行时间")
execution_report: Optional[ExecutionReportResponse] = Field(
None, description="详细执行报告"
)
message: Optional[str] = Field(None, description="提示消息")
+21
View File
@@ -0,0 +1,21 @@
"""影响分析 Pydantic Schemas。"""
from typing import List, Optional
from pydantic import BaseModel
class ImpactAnalysisRequest(BaseModel):
instance_id: int
database: str
sql_content: str
class ImpactAnalysisResponse(BaseModel):
sql_type: str
estimated_rows: int
estimated_duration_ms: int
table_size_mb: float
table_row_count: int
index_usage: List[str]
has_full_scan: bool
risk_level: str # low / medium / high / critical
warnings: List[str]
+2 -3
View File
@@ -1,5 +1,5 @@
from datetime import datetime
from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field
from typing import Optional, Literal, List, Any
@@ -64,8 +64,7 @@ class InstanceResponse(BaseModel):
mask_fields: Optional[List[dict]] = None
created_at: datetime
class Config:
from_attributes = True
model_config = ConfigDict(from_attributes=True)
class ConnectionTestResult(BaseModel):
+2 -3
View File
@@ -1,6 +1,6 @@
from datetime import datetime
from typing import Optional, List
from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field
class InstanceGroupCreate(BaseModel):
@@ -31,8 +31,7 @@ class InstanceGroupResponse(BaseModel):
instance_count: int = 0
user_count: int = 0
class Config:
from_attributes = True
model_config = ConfigDict(from_attributes=True)
class InstanceGroupDetailResponse(InstanceGroupResponse):
+2 -3
View File
@@ -1,6 +1,6 @@
from datetime import datetime
from typing import Optional, Dict, Any
from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field
class LdapConfigCreate(BaseModel):
@@ -38,8 +38,7 @@ class LdapConfigResponse(BaseModel):
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
model_config = ConfigDict(from_attributes=True)
class LdapTestRequest(BaseModel):
+59
View File
@@ -0,0 +1,59 @@
"""数据脱敏规则 Pydantic Schemas — 用于 MaskingRule CRUD 操作。"""
from datetime import datetime
from typing import Optional, Dict, Any, List
from pydantic import BaseModel, ConfigDict, Field
class MaskingRuleBase(BaseModel):
"""脱敏规则基础 Schema。"""
database: str = Field(..., min_length=1, max_length=100, description="数据库名称")
table_name: str = Field(..., min_length=1, max_length=100, description="表名,* 表示所有表")
column_name: str = Field(..., min_length=1, max_length=100, description="列名,* 表示所有列")
mask_type: str = Field(..., pattern=r"^(full|partial|hash|truncate|regex)$")
mask_config: Optional[Dict[str, Any]] = Field(default=None, description="脱敏配置(JSON)")
is_enabled: bool = Field(default=True, description="是否启用")
class MaskingRuleCreate(MaskingRuleBase):
"""创建脱敏规则请求 Schema。"""
pass
class MaskingRuleUpdate(BaseModel):
"""更新脱敏规则请求 Schema。"""
database: Optional[str] = Field(default=None, max_length=100)
table_name: Optional[str] = Field(default=None, max_length=100)
column_name: Optional[str] = Field(default=None, max_length=100)
mask_type: Optional[str] = Field(default=None, pattern=r"^(full|partial|hash|truncate|regex)$")
mask_config: Optional[Dict[str, Any]] = Field(default=None, description="脱敏配置(JSON)")
is_enabled: Optional[bool] = Field(default=None, description="是否启用")
class MaskingRuleResponse(MaskingRuleBase):
"""脱敏规则响应 Schema。"""
id: int
instance_id: int
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
model_config = ConfigDict(from_attributes=True)
class MaskingRuleBatchCreate(BaseModel):
"""批量创建脱敏规则请求 Schema。"""
rules: List[MaskingRuleCreate] = Field(..., min_length=1, max_length=100)
class MaskingRuleListResponse(BaseModel):
"""脱敏规则列表响应 Schema。"""
total: int
items: List[MaskingRuleResponse]
class MaskingRuleQueryParams(BaseModel):
"""脱敏规则查询参数 Schema。"""
database: Optional[str] = None
table_name: Optional[str] = None
keyword: Optional[str] = None
page: int = Field(default=1, ge=1)
page_size: int = Field(default=20, ge=1, le=100)
+2 -3
View File
@@ -1,6 +1,6 @@
from datetime import datetime
from typing import Optional, Dict, Any
from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field
class NotificationChannelCreate(BaseModel):
@@ -26,5 +26,4 @@ class NotificationChannelResponse(BaseModel):
created_at: datetime
updated_at: datetime
class Config:
from_attributes = True
model_config = ConfigDict(from_attributes=True)
+41 -8
View File
@@ -1,16 +1,49 @@
from typing import Optional, List, Dict, Any
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field
class QueryRequest(BaseModel):
"""统一查询请求模型。
支持 MySQL、PostgreSQL、Redis、MongoDB 四种数据库的查询请求。
Attributes:
instance_id: 数据源实例ID
database: 目标数据库名/Redis DB号/MongoDB database
sql: 查询语句(SQL/Redis命令/MongoDB查询JSON)
options: 额外查询选项
"""
instance_id: int
database: Optional[str] = None
sql: str = Field(..., min_length=1)
sql: str = Field(..., min_length=1, description="查询语句(SQL/Redis命令/MongoDB查询JSON)")
options: Optional[Dict[str, Any]] = Field(default=None, description="额外选项(如 limit, offset 等)")
class QueryResult(BaseModel):
columns: List[str]
rows: List[Dict[str, Any]]
total: int
elapsed_ms: int
truncated: bool = False
class UnifiedQueryResult(BaseModel):
"""统一查询结果模型。
兼容 MySQL、PostgreSQL、Redis、MongoDB 四种数据库的返回格式。
SQL 数据库返回标准表格格式,Redis/MongoDB 同时返回格式化表格和原始结果。
Attributes:
columns: SQL表格列名列表
rows: SQL表格行数据列表(字典格式)
raw_result: Redis/MongoDB原始结果
total: 总行数/总文档数
elapsed_ms: 执行耗时(毫秒)
truncated: 是否因超过行数限制而截断
db_type: 数据库类型(mysql/postgres/redis/mongo)
query_type: 查询类型(SELECT/GET/find等)
"""
columns: List[str] = Field(default=[], description="SQL表格列名")
rows: List[Dict[str, Any]] = Field(default=[], description="SQL表格行数据")
raw_result: Any = Field(default=None, description="Redis/MongoDB原始结果")
total: int = Field(default=0, description="总行数")
elapsed_ms: int = Field(default=0, description="执行耗时(毫秒)")
truncated: bool = Field(default=False, description="是否截断")
db_type: str = Field(default="", description="数据库类型")
query_type: str = Field(default="", description="查询类型")
# 保留旧名兼容
QueryResult = UnifiedQueryResult
+33
View File
@@ -0,0 +1,33 @@
"""回滚操作 Pydantic Schemas — 用于回滚 SQL 生成和预览。"""
from typing import Optional
from pydantic import BaseModel, Field
class RollbackPreviewRequest(BaseModel):
"""预览回滚 SQL 请求 Schema。"""
instance_id: int = Field(..., description="数据源实例ID")
database: str = Field(..., min_length=1, max_length=100, description="数据库名称")
sql: str = Field(..., min_length=1, description="原始 DML 语句")
class RollbackPreviewResponse(BaseModel):
"""预览回滚 SQL 响应 Schema。"""
original_sql: str = Field(..., description="原始 SQL")
rollback_sql: str = Field(..., description="生成的回滚 SQL")
sql_type: Optional[str] = Field(default=None, description="SQL 类型 (INSERT/UPDATE/DELETE)")
warning: Optional[str] = Field(default=None, description="警告信息")
class RollbackGenerateRequest(BaseModel):
"""为工单生成回滚 SQL 请求 Schema。"""
auto_save: bool = Field(default=True, description="是否自动保存到工单")
class RollbackGenerateResponse(BaseModel):
"""生成回滚 SQL 响应 Schema。"""
ticket_id: int = Field(..., description="工单ID")
original_sql: str = Field(..., description="原始 SQL")
rollback_sql: str = Field(..., description="生成的回滚 SQL")
sql_type: Optional[str] = Field(default=None, description="SQL 类型")
saved: bool = Field(default=False, description="是否已保存到工单")
warning: Optional[str] = Field(default=None, description="警告信息")
+2 -3
View File
@@ -1,5 +1,5 @@
from datetime import datetime
from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field
from typing import Optional, Literal
@@ -40,8 +40,7 @@ class TicketResponse(BaseModel):
review_time: Optional[datetime] = None
execute_time: Optional[datetime] = None
class Config:
from_attributes = True
model_config = ConfigDict(from_attributes=True)
class TicketListParams(BaseModel):
+2 -3
View File
@@ -1,5 +1,5 @@
from datetime import datetime
from pydantic import BaseModel, Field
from pydantic import BaseModel, ConfigDict, Field
from typing import Optional
@@ -31,8 +31,7 @@ class UserResponse(BaseModel):
last_login: Optional[datetime] = None
created_at: datetime
class Config:
from_attributes = True
model_config = ConfigDict(from_attributes=True)
class PasswordChange(BaseModel):
+78
View File
@@ -0,0 +1,78 @@
"""审批流程 Pydantic Schemas。"""
from typing import List, Optional
from pydantic import BaseModel, ConfigDict
from datetime import datetime
class WorkflowStepCreate(BaseModel):
step_number: int
name: str
step_type: str = "serial" # serial / parallel / any_one
approver_role: Optional[str] = None
approver_ids: Optional[str] = None # JSON list
is_required: bool = True
timeout_hours: int = 24
auto_approve_on_timeout: bool = False
class WorkflowStepResponse(WorkflowStepCreate):
id: int
workflow_id: int
model_config = ConfigDict(from_attributes=True)
class WorkflowCreate(BaseModel):
name: str
description: Optional[str] = None
is_default: bool = False
steps: List[WorkflowStepCreate]
class WorkflowUpdate(BaseModel):
name: Optional[str] = None
description: Optional[str] = None
is_active: Optional[bool] = None
class WorkflowResponse(BaseModel):
id: int
name: str
description: Optional[str]
is_default: bool
is_active: bool
created_at: Optional[datetime]
steps: List[WorkflowStepResponse]
model_config = ConfigDict(from_attributes=True)
class TicketApprovalResponse(BaseModel):
id: int
ticket_id: int
step_number: int
step_name: Optional[str]
approver_id: Optional[int]
approver_role: Optional[str]
approver_name: Optional[str] = None
action: str
comment: Optional[str]
created_at: Optional[datetime]
updated_at: Optional[datetime]
class ApprovalProgressResponse(BaseModel):
ticket_id: int
current_step: int
total_steps: int
status: str
steps: List[dict]
class ApprovalActionRequest(BaseModel):
comment: Optional[str] = ""
class TransferRequest(BaseModel):
to_user_id: int
comment: Optional[str] = ""