feat: 5种专业登录页风格 + 外观设置 + 文档全面更新
- 新增 5 种登录页组件: SplitView/CenteredCard/SidePanel/FullImage/MinimalDark - 提取 useLogin hook 共享登录逻辑 - LoginPage 改为风格分发器, localStorage(login-style) 控制渲染 - SettingsPage 新增外观设置 tab: 主题切换 + 登录风格选择器(含缩略图预览) - AppearanceSettings 组件: 5 种风格卡片式切换 - 文档全面更新为 Go/Gin 架构: - ARCHITECTURE.md: Go 项目结构/handler/GORM 模型/中间件/LDAP/驱动矩阵 - FEATURES.md: v0.3.1 功能清单含登录主题系统 - ROADMAP.md: Python→Go 迁移历史 + Sprint 计划
This commit is contained in:
1 parent
f2e93a09c0
commit
14e394fb09
12 files changed
+1147
-639
No files matched your search
+304
-302
@@ -1,6 +1,6 @@
|
||||
# SQL-Platform-Pro 系统架构文档
|
||||
# SQL-Platform 系统架构文档
|
||||
|
||||
> 生成时间: 2026-05-25 | 最新 Commit: 9813741+
|
||||
> 更新时间: 2026-05-26 | 版本: v0.3.1 | 后端: Go/Gin | Commit: f2e93a0
|
||||
|
||||
## 1. 系统定位
|
||||
|
||||
@@ -8,357 +8,359 @@
|
||||
|
||||
## 2. 技术栈
|
||||
|
||||
|| 层级 | 技术 | 版本 |
|
||||
||------|------|------|
|
||||
|| 后端框架 | FastAPI + SQLAlchemy 2 async | Python 3.11+ |
|
||||
|| 数据库 | SQLite (开发) | aiomysql / asyncpg / redis.asyncio / pymongo |
|
||||
|| 定时调度 | APScheduler 3.11.2 + SQLite 持久化 | apscheduler.db |
|
||||
|| 前端框架 | React 19 + TypeScript | Vite 7 |
|
||||
|| UI 组件 | shadcn/ui + Tailwind CSS 4 | Radix UI |
|
||||
|| 代码编辑器 | CodeMirror 6 | @codemirror/lang-sql |
|
||||
|| 图表 | Recharts | — |
|
||||
|| 认证 | JWT (30min) + bcrypt 4.0 | — |
|
||||
|| 部署 | Docker + docker-compose | run.sh PID 管理 |
|
||||
| 层级 | 技术 | 版本 |
|
||||
|------|------|------|
|
||||
| 后端语言 | Go | 1.26 |
|
||||
| 后端框架 | Gin | v1.10.1 |
|
||||
| ORM | GORM | v1.26.0 |
|
||||
| 元数据库 | SQLite (GORM AutoMigrate) | gorm.io/driver/sqlite |
|
||||
| 前端框架 | React 19 + TypeScript | Vite 7 |
|
||||
| UI 组件 | shadcn/ui + Tailwind CSS 4 | Radix UI |
|
||||
| 代码编辑器 | CodeMirror 6 | @codemirror/lang-sql |
|
||||
| 图表 | Recharts | — |
|
||||
| 认证 | JWT (HS256) + bcrypt (golang.org/x/crypto) | 默认 24h 过期 |
|
||||
| DB 驱动 | go-sql-driver/mysql / jackc/pgx/v5 / go-redis/v9 / go-mongo-driver/v2 | — |
|
||||
| LDAP | go-ldap/ldap/v3 | v3.4.13 |
|
||||
| 部署 | run.sh PID 管理 | Go :9000, Frontend :5173 |
|
||||
|
||||
## 3. 系统架构图
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────┐
|
||||
│ 前端 (React + Vite) │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ Dashboard │ │ Queries │ │ Tickets │ │ Instances│ │
|
||||
│ └─────┬────┘ └─────┬────┘ └─────┬────┘ └─────┬────┘ │
|
||||
│ ┌──────┴──────┐ ┌────┴─────┐ ┌───┴──────┐ ┌──┴───────┐ │
|
||||
│ │ SchemaSide │ │ SqlEditor│ │ Workflow │ │AutoTask │ │
|
||||
│ │ bar(Redis树)│ │ (CM6) │ │ Page │ │Page │ │
|
||||
│ └─────────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||||
│ ↕ axios /api/v1 │
|
||||
├─────────────────────────────────────────────────────────────┤
|
||||
│ 后端 (FastAPI) │
|
||||
│ ┌────────────────────────────────────────────────────┐ │
|
||||
│ │ API Layer (29+ endpoints) │ │
|
||||
│ │ auth | user | instance | ticket | query | audit │ │
|
||||
│ │ audit_rule | notification | workflow | settings │ │
|
||||
│ │ ticket_scheduler | log_cleanup │ │
|
||||
│ └──────────────────────┬─────────────────────────────┘ │
|
||||
│ ┌──────────────────────┴─────────────────────────────┐ │
|
||||
│ │ Service Layer (33+ services) │ │
|
||||
│ │ query_service | audit_engine | ticket_executor │ │
|
||||
│ │ instance_service | notification_sender │ │
|
||||
│ │ approval_timeout | role_permissions │ │
|
||||
│ │ ticket_scheduler | log_cleanup | settings_seeds │ │
|
||||
│ └──────────────────────┬─────────────────────────────┘ │
|
||||
│ ┌──────────────────────┴─────────────────────────────┐ │
|
||||
│ │ Scheduler Layer (APScheduler) │ │
|
||||
│ │ 定时工单执行 | 日志自动清理 (cron 03:00) │ │
|
||||
│ └──────────────────────┬─────────────────────────────┘ │
|
||||
│ ┌──────────────────────┴─────────────────────────────┐ │
|
||||
│ │ Connector Layer │ │
|
||||
│ │ db_connector(MySQL/PG/SQLite) │ │
|
||||
│ │ redis_connector | mongo_connector │ │
|
||||
│ └──────────────────────┬─────────────────────────────┘ │
|
||||
│ ┌──────────────────────┴─────────────────────────────┐ │
|
||||
│ │ SQLite(metadata) | apscheduler.db | MySQL | PostgreSQL | Redis | MongoDB│
|
||||
│ └────────────────────────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────┘
|
||||
┌──────────────────────────────────────────────────────────────────┐
|
||||
│ 前端 (React 19 + Vite 7) │
|
||||
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
|
||||
│ │ Dashboard │ │ Queries │ │ Tickets │ │ Instances│ │
|
||||
│ └─────┬────┘ └─────┬────┘ └─────┬────┘ └─────┬────┘ │
|
||||
│ ┌──────┴──────┐ ┌────┴─────┐ ┌───┴──────┐ ┌──┴───────┐ │
|
||||
│ │ Appearance │ │ SqlEditor│ │ Workflow │ │Settings │ │
|
||||
│ │ Settings │ │ (CM6) │ │ Page │ │Page │ │
|
||||
│ └─────────────┘ └──────────┘ └──────────┘ └──────────┘ │
|
||||
│ ┌──────────────────────────────────────────────────────┐ │
|
||||
│ │ Login (5 themes: SplitView/CenteredCard/SidePanel/ │ │
|
||||
│ │ FullImage/MinimalDark) useLogin hook │ │
|
||||
│ └──────────────────────────────────────────────────────┘ │
|
||||
│ ↕ axios /api/v1 │
|
||||
├──────────────────────────────────────────────────────────────────┤
|
||||
│ 后端 (Go 1.26 + Gin) │
|
||||
│ ┌────────────────────────────────────────────────────┐ │
|
||||
│ │ Router (router/router.go) │ │
|
||||
│ │ 120+ routes: auth/ticket/instance/redis/workflow │ │
|
||||
│ └──────────────────────┬─────────────────────────────┘ │
|
||||
│ ┌──────────────────────┴─────────────────────────────┐ │
|
||||
│ │ Middleware Stack │ │
|
||||
│ │ CORS → Auth JWT (HS256) → RequireRole (RBAC) │ │
|
||||
│ └──────────────────────┬─────────────────────────────┘ │
|
||||
│ ┌──────────────────────┴─────────────────────────────┐ │
|
||||
│ │ Handler Layer (internal/handler/) │ │
|
||||
│ │ auth | user | instance | instance_detail | redis │ │
|
||||
│ │ ticket | workflow | audit_log | notification │ │
|
||||
│ │ dashboard | ghost | ldap | scheduled_task | stubs │ │
|
||||
│ └──────────────────────┬─────────────────────────────┘ │
|
||||
│ ┌──────────────────────┴─────────────────────────────┐ │
|
||||
│ │ Service Layer (internal/service/) │ │
|
||||
│ │ auth_service | ticket_service | instance_service │ │
|
||||
│ └──────────────────────┬─────────────────────────────┘ │
|
||||
│ ┌──────────────────────┴─────────────────────────────┐ │
|
||||
│ │ GORM Model Layer (internal/model/model.go) │ │
|
||||
│ │ 25+ models · AutoMigrate · SQLite metadata │ │
|
||||
│ └──────────────────────┬─────────────────────────────┘ │
|
||||
│ ┌──────────────────────┴─────────────────────────────┐ │
|
||||
│ │ Database Drivers (5 types) │ │
|
||||
│ │ go-sql-driver/mysql | jackc/pgx/v5 │ │
|
||||
│ │ go-redis/v9 | go-mongo-driver/v2 | gorm/sqlite │ │
|
||||
│ └────────────────────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## 4. 核心模块架构
|
||||
|
||||
### 4.1 权限体系(三层模型)
|
||||
## 4. Go 后端项目结构
|
||||
|
||||
```
|
||||
Tier 1: 实例可见性 (check_instance_access)
|
||||
→ admin 看全部, 其他角色按权限组过滤
|
||||
→ /instances/accessible?access_type=readonly|write
|
||||
|
||||
Tier 2: 操作权限 (check_instance_permission)
|
||||
→ 7 个 action: query:select, query:export, ticket:create, ticket:approve,
|
||||
ticket:execute, ddl:execute, instance:manage
|
||||
→ 用户级覆盖 > 组默认权限
|
||||
|
||||
Tier 3: 角色权限 (role_permissions.py)
|
||||
→ admin: 全部权限
|
||||
→ dba: 全部库 + 审批+拒绝+执行
|
||||
→ auditor: 全部只读 + 审批+拒绝
|
||||
→ developer: 按权限组 + 创建+撤销(未执行前)
|
||||
backend/
|
||||
├── cmd/
|
||||
│ └── server/
|
||||
│ └── main.go # 入口: Gin 初始化、GORM 连接、AutoMigrate、路由注册
|
||||
├── config/
|
||||
│ └── config.go # 配置管理 (PORT/DB_PATH/JWT_SECRET/GH_OST_PATH)
|
||||
├── internal/
|
||||
│ ├── handler/ # API 处理器 (17 个文件)
|
||||
│ │ ├── auth.go # 登录、获取当前用户
|
||||
│ │ ├── user.go # (stub_handlers) 用户 CRUD、批量导入
|
||||
│ │ ├── instance.go # 实例 CRUD、连接测试
|
||||
│ │ ├── instance_detail.go # MySQL 详情: databases/tables/schema/processlist/status/users
|
||||
│ │ ├── instance_extra.go # 可访问实例、实例统计
|
||||
│ │ ├── instance_group.go # 权限组 CRUD、成员管理
|
||||
│ │ ├── redis.go # Redis: info/dbsize/keys/slowlog/clients/command
|
||||
│ │ ├── ticket.go # 工单: 创建/审批/执行/取消/回滚/时间线
|
||||
│ │ ├── workflow.go # 审批流: CRUD/克隆/设置默认
|
||||
│ │ ├── audit_log.go # 审计日志查询
|
||||
│ │ ├── notification.go # 通知渠道 CRUD + 测试发送
|
||||
│ │ ├── dashboard.go # 仪表盘统计
|
||||
│ │ ├── ldap.go # LDAP 配置/测试/导入/用户管理
|
||||
│ │ ├── ghost.go # gh-ost DDL 变更
|
||||
│ │ ├── scheduled_task.go # 定时任务管理
|
||||
│ │ ├── crypto.go # AES-256-GCM 加解密
|
||||
│ │ ├── connection_tester.go # 数据库连接测试
|
||||
│ │ └── stub_handlers.go # SQL格式化/分析、数据字典、MongoDB 桩、审核规则、查询执行等
|
||||
│ ├── model/
|
||||
│ │ └── model.go # GORM 模型定义 (25+ 模型)
|
||||
│ ├── middleware/
|
||||
│ │ ├── auth.go # JWT 认证中间件 + RequireRole RBAC
|
||||
│ │ └── cors.go # CORS 跨域中间件
|
||||
│ ├── service/
|
||||
│ │ ├── auth_service.go # 认证逻辑 (bcrypt 密码验证、种子用户)
|
||||
│ │ ├── ticket_service.go # 工单业务逻辑
|
||||
│ │ └── instance_service.go # 实例连接管理
|
||||
│ └── ghost/
|
||||
│ └── ghost.go # gh-ost 集成逻辑
|
||||
├── router/
|
||||
│ └── router.go # Gin 路由注册 (120+ 路由)
|
||||
└── go.mod # 依赖管理 (module: sql-platform)
|
||||
```
|
||||
|
||||
### 4.2 审批流引擎
|
||||
### 4.1 启动流程
|
||||
|
||||
```
|
||||
工单创建 → 查找默认审批流 → 创建 TicketApproval 记录
|
||||
├─ 标准步骤 (step_type='standard') → 单人审批
|
||||
├─ 会签 (step_type='parallel') → 所有人都通过
|
||||
├─ 或签 (step_type='any_one') → 一人通过即可
|
||||
└─ 条件 (step_type='condition') → 按 SQL 类型/风险等级判断
|
||||
|
||||
审批推进:
|
||||
approve → 检查后续步骤
|
||||
├─ 还有 pending 步骤 → status='reviewing'
|
||||
└─ 全部完成 → status='approved'
|
||||
reject → 拒绝所有剩余步骤 → status='rejected'
|
||||
|
||||
超时检查:
|
||||
POST /tickets/check-timeout → timeout_hours + auto_approve_on_timeout
|
||||
```go
|
||||
// cmd/server/main.go
|
||||
config.Init() // 加载 .env 配置
|
||||
db = gorm.Open(sqlite.Open(...)) // 连接 SQLite 元数据库
|
||||
db.AutoMigrate(&model.User{}, …) // 自动迁移 25+ 模型
|
||||
r = gin.Default() // 创建 Gin 引擎
|
||||
r.Use(CORSMiddleware()) // 全局 CORS
|
||||
router.Setup(r) // 注册 120+ API 路由
|
||||
r.Run(":9000") // 启动 HTTP 服务
|
||||
```
|
||||
|
||||
### 4.3 SQL 审核引擎
|
||||
### 4.2 编译与运行
|
||||
|
||||
```
|
||||
输入 SQL → audit_engine.py
|
||||
├─ sql_analyzer.py (AST 分析)
|
||||
│ ├─ SQL 类型识别 (DDL/DML/DQL/DCL)
|
||||
│ ├─ 风险等级评估 (5级)
|
||||
│ └─ 只读验证
|
||||
├─ rule_templates.py (127 条规则)
|
||||
│ ├─ MySQL: 67 条 (含安全9条 SEC-001~009)
|
||||
│ ├─ PostgreSQL: 31 条
|
||||
│ ├─ Redis: 25 条 (去重优化后)
|
||||
│ └─ MongoDB: 31 条
|
||||
└─ 输出 AuditReport (score + violations)
|
||||
```bash
|
||||
cd backend && go build -o sql-platform ./cmd/server/ # 编译
|
||||
bash run.sh restart # Go :9000 + Frontend :5173
|
||||
```
|
||||
|
||||
### 4.4 多数据库查询路由
|
||||
## 5. GORM 数据模型 (25+)
|
||||
|
||||
| 模型 | 表名 | 说明 |
|
||||
|------|------|------|
|
||||
| User | users | 用户 (admin/dba/developer/auditor/guest) |
|
||||
| Instance | instances | 数据源实例 (mysql/postgresql/redis/mongodb) |
|
||||
| InstanceGroup | instance_groups | 权限组 |
|
||||
| InstanceGroupMember | instance_group_members | 权限组-实例关联 |
|
||||
| UserGroup | user_groups | 用户-权限组关联 |
|
||||
| Ticket | tickets | 工单 (10 种状态) |
|
||||
| TicketApproval | ticket_approvals | 审批记录 (含转审/超时) |
|
||||
| TicketSQLItem | ticket_sql_items | 工单 SQL 明细 |
|
||||
| AuditRule | audit_rules | 审核规则 (127 条/6 组) |
|
||||
| AuditLog | audit_logs | 审计日志 |
|
||||
| QueryHistory | query_histories | 查询历史 (500/用户上限) |
|
||||
| WorkflowDefinition | workflow_definitions | 审批流定义 |
|
||||
| WorkflowStep | workflow_steps | 审批流步骤 |
|
||||
| WorkflowVersion | workflow_versions | 审批流版本 |
|
||||
| WorkflowExecutionLog | workflow_execution_logs | 审批流执行日志 |
|
||||
| NotificationChannel | notification_channels | 通知渠道 (feishu/dingtalk/wechat/email) |
|
||||
| NotificationTemplate | notification_templates | 通知模板 |
|
||||
| Announcement | announcements | 系统公告 |
|
||||
| SystemSetting | system_settings | 系统配置 |
|
||||
| OperationLog | operation_logs | 操作日志 |
|
||||
| LDAPConfig | ldap_config | LDAP 配置 |
|
||||
| MaskingRule | masking_rules | 数据脱敏规则 |
|
||||
| Backup | backups | 数据库备份记录 |
|
||||
| SchemaRevision | schema_revisions | Schema 版本记录 |
|
||||
| SlowLog | slow_logs | 慢查询日志 |
|
||||
| ScheduledTask | scheduled_tasks | 定时任务 |
|
||||
| ScheduledTaskLog | scheduled_task_logs | 定时任务日志 |
|
||||
|
||||
## 6. 中间件栈
|
||||
|
||||
### 6.1 CORS 中间件 (`middleware/cors.go`)
|
||||
- 基于 `github.com/gin-contrib/cors`
|
||||
- 开发环境允许所有来源
|
||||
|
||||
### 6.2 JWT 认证中间件 (`middleware/auth.go`)
|
||||
- 从 `Authorization: Bearer <token>` 提取 JWT
|
||||
- HS256 签名,密钥从配置加载
|
||||
- 解析 Claims (user_id, username, role) 注入 Gin Context
|
||||
|
||||
### 6.3 RBAC 中间件 (`middleware/auth.go` — RequireRole)
|
||||
- 按角色限制路由访问
|
||||
- 实例管理: `RequireRole("admin", "dba")`
|
||||
- 用户管理: `RequireRole("admin")`
|
||||
- LDAP 管理: `RequireRole("admin")`
|
||||
- gh-ost 操作: `RequireRole("admin", "dba")`
|
||||
|
||||
## 7. 认证流程
|
||||
|
||||
```
|
||||
query_service.py → 按 instance.db_type 分发:
|
||||
├─ mysql → aiomysql (SELECT only, auto LIMIT)
|
||||
├─ postgres → asyncpg
|
||||
├─ redis → redis.asyncio (GET/HGETALL/LRANGE/SMEMBERS/ZRANGE/KEYS)
|
||||
└─ mongodb → pymongo (find/aggregate/count)
|
||||
客户端 → POST /api/v1/auth/login {username, password}
|
||||
├─ auth.go → auth_service.go
|
||||
│ ├─ GORM 查询 User by username
|
||||
│ ├─ bcrypt.CompareHashAndPassword 验证密码
|
||||
│ └─ GenerateToken(user) → JWT (HS256, 24h)
|
||||
├─ 返回 {access_token, token_type, user}
|
||||
└─ 后续请求: Authorization: Bearer <token>
|
||||
└─ AuthMiddleware 解析 Claims → 注入 Context (user_id, username, role)
|
||||
```
|
||||
|
||||
### 4.5 工单执行引擎
|
||||
## 8. LDAP 集成
|
||||
|
||||
```
|
||||
ticket_executor.py → 按 instance.db_type 执行:
|
||||
├─ mysql → aiomysql.execute() + affected_rows
|
||||
├─ postgres → asyncpg.execute()
|
||||
├─ redis → redis.asyncio (SET/HSET/LPUSH/SADD/ZADD/DEL)
|
||||
└─ mongodb → pymongo (insert/update/delete)
|
||||
→ ExecutionReport (status/affected_rows/duration/warnings/errors)
|
||||
LDAP 服务器: 8.217.205.92:389
|
||||
Base DN: dc=opsx,dc=vip
|
||||
驱动: github.com/go-ldap/ldap/v3
|
||||
|
||||
API 端点:
|
||||
GET /ldap-config — 获取 LDAP 配置
|
||||
PUT /ldap-config — 更新 LDAP 配置
|
||||
POST /ldap-config/test — 测试 LDAP 连接
|
||||
POST /ldap-config/ou-list — 列出 OU
|
||||
POST /ldap-config/user-list — 列出 LDAP 用户
|
||||
POST /ldap-config/create-ou — 创建 OU
|
||||
POST /ldap-config/create-user — 创建 LDAP 用户
|
||||
POST /ldap-config/batch-import-ldap — 批量导入
|
||||
POST /ldap-config/delete-user — 删除 LDAP 用户
|
||||
```
|
||||
|
||||
### 4.6 定时调度器 (APScheduler)
|
||||
## 9. 数据库驱动支持矩阵
|
||||
|
||||
| 数据库 | Go 驱动 | 查询 | 执行 | 审核 | 详情浏览 |
|
||||
|--------|---------|------|------|------|----------|
|
||||
| MySQL | go-sql-driver/mysql v1.10.0 | ✅ | ✅ | ✅ (67规则) | ✅ (7 endpoints) |
|
||||
| PostgreSQL | jackc/pgx/v5 v5.9.2 | ✅ | ✅ | ✅ (31规则) | ✅ |
|
||||
| Redis | go-redis/v9 v9.19.0 | ✅ | ✅ | ✅ (25规则) | ✅ (9 handlers) |
|
||||
| MongoDB | go-mongo-driver/v2 | ✅ (stub) | ✅ (stub) | ✅ (31规则) | ✅ (stub) |
|
||||
| SQLite | gorm.io/driver/sqlite | ✅ (元数据) | — | — | — |
|
||||
|
||||
## 10. MySQL Instance Detail — 7 个端点
|
||||
|
||||
| 端点 | 路由 | 说明 |
|
||||
|------|------|------|
|
||||
| databases | GET /instances/:id/detail/databases | 数据库列表 |
|
||||
| tables | GET /instances/:id/detail/tables | 表列表 |
|
||||
| db-schema | GET /instances/:id/detail/db-schema | 库 Schema |
|
||||
| table-detail | GET /instances/:id/detail/table-detail | 表详情 (列/索引) |
|
||||
| processlist | GET /instances/:id/detail/processlist | 进程列表 (含 Kill) |
|
||||
| status | GET /instances/:id/detail/status | 服务器状态 |
|
||||
| users | GET /instances/:id/detail/users | 用户列表 |
|
||||
|
||||
## 11. Redis — 9 个 Handler
|
||||
|
||||
| Handler | 路由 | 说明 |
|
||||
|---------|------|------|
|
||||
| RedisInfo | GET /redis/:id/info | 服务器信息 |
|
||||
| RedisDbsize | GET /redis/:id/dbsize | 数据库大小 |
|
||||
| RedisScanKeys | GET /redis/:id/keys | Key 扫描 |
|
||||
| RedisGetKeyDetail | GET /redis/:id/keys/:key | Key 详情 (type/TTL/value) |
|
||||
| RedisSlowlog | GET /redis/:id/slowlog | 慢查询日志 |
|
||||
| RedisClients | GET /redis/:id/clients | 客户端列表 |
|
||||
| RedisExecuteCommand | POST /redis/:id/command | 执行命令 |
|
||||
| RedisCreateKey | POST /redis/:id/keys | 创建 Key |
|
||||
| RedisDeleteKey | DELETE /redis/:id/keys/:key | 删除 Key |
|
||||
|
||||
## 12. 前端架构
|
||||
|
||||
### 12.1 前端页面 (18+)
|
||||
|
||||
| 页面 | 路由 | 说明 |
|
||||
|------|------|------|
|
||||
| LoginPage | /login | 5 种主题: SplitView/CenteredCard/SidePanel/FullImage/MinimalDark |
|
||||
| Dashboard | / | 首页仪表盘 |
|
||||
| QueriesPage | /queries | 自助查询 (CodeMirror 6) |
|
||||
| TicketsPage | /tickets | 工单列表 |
|
||||
| TicketCreatePage | /tickets/create | 新建工单 |
|
||||
| TicketDetailPage | /tickets/:id | 工单详情 |
|
||||
| InstancePage | /instances | 数据源管理 |
|
||||
| InstanceDetailPage | /instances/:id | MySQL 实例详情 |
|
||||
| RedisDetailPage | /redis/:id | Redis 详情 |
|
||||
| MongoDBDetailPage | /mongodb/:id | MongoDB 详情 |
|
||||
| UsersPage | /users | 用户管理 |
|
||||
| AuditLogsPage | /audit-logs | 审计日志 |
|
||||
| AuditRulesPage | /audit-rules | 审核规则 (6 组) |
|
||||
| WorkflowPage | /workflows | 审批流管理 |
|
||||
| SettingsPage | /settings | 系统设置 (多 tab) |
|
||||
| AppearanceSettings | /settings/appearance | 外观设置 (主题 + 登录页风格) |
|
||||
| PlatformConfigPage | /settings/platform | 平台配置 |
|
||||
| NotificationPage | /settings/notifications | 通知渠道管理 |
|
||||
| AnnouncementPage | /settings/announcements | 公告管理 |
|
||||
| LdapConfigPage | /settings/ldap | LDAP 配置 |
|
||||
| InstanceGroupPage | /permissions | 权限管理 |
|
||||
| AutoTaskPage | /auto-tasks | 定时任务管理 |
|
||||
| DataDictionaryPage | /dictionary | 数据字典 |
|
||||
| RolesPage | /roles | 角色管理 |
|
||||
|
||||
### 12.2 登录页主题系统 (v0.3.1 新增)
|
||||
|
||||
```
|
||||
ticket_scheduler.py:
|
||||
├─ APScheduler BackgroundScheduler + SQLite 持久化 (apscheduler.db)
|
||||
├─ 工单 scheduled_at 到期 → 自动触发执行
|
||||
├─ 状态流转: approved → scheduled → executing → success/failed
|
||||
├─ 支持取消定时: POST /tickets/{id}/cancel-schedule
|
||||
└─ 统计接口: GET /tickets/scheduled/stats
|
||||
components/login/
|
||||
├── SplitView.tsx # 经典分屏 — 左侧特性展示 + 右侧表单
|
||||
├── CenteredCard.tsx # 居中卡片 — 渐变背景 + 居中表单
|
||||
├── SidePanel.tsx # 侧边面板 — 左侧品牌 + 右侧表单
|
||||
├── FullImage.tsx # 全屏背景 — 科技背景 + 毛玻璃卡片
|
||||
└── MinimalDark.tsx # 极简暗色 — 纯暗色极简风格
|
||||
|
||||
log_cleanup.py:
|
||||
├─ APScheduler cron job → 每天 03:00 自动执行
|
||||
├─ 读取 cleanup 分组配置 (6 项)
|
||||
├─ 按保留天数清理:
|
||||
│ ├─ 工单 (只清理已完成/已拒绝, 0天=不清理)
|
||||
│ ├─ 审计日志
|
||||
│ ├─ 查询历史
|
||||
│ └─ 定时任务日志
|
||||
├─ 手动触发: POST /settings/cleanup/run
|
||||
└─ 预览: GET /settings/cleanup/preview
|
||||
hooks/
|
||||
└── useLogin.tsx # 共享登录逻辑 (username/password/loading/error)
|
||||
|
||||
pages/
|
||||
├── LoginPage.tsx # 主题路由器 — 读取 localStorage 'login-style'
|
||||
└── AppearanceSettings.tsx # Settings → Appearance tab
|
||||
# 主题切换 (dark/light)
|
||||
# 登录页风格切换 (5 种预览 + 选择)
|
||||
# localStorage key: 'login-style'
|
||||
```
|
||||
|
||||
### 4.7 系统设置种子机制
|
||||
|
||||
```
|
||||
settings_seeds.py:
|
||||
├─ SETTING_GROUPS: 分组元信息 (query_control / security / ticket / cleanup)
|
||||
├─ _ensure_settings(): 启动时自动补缺失配置项
|
||||
├─ SETTING_DEFINITIONS: 所有设置项定义 (key/value_type/default/group/label)
|
||||
└─ 新增 cleanup 分组 (6 项):
|
||||
├─ cleanup_enabled (bool, false)
|
||||
├─ cleanup_time (str, "03:00")
|
||||
├─ cleanup_ticket_days (int, 90)
|
||||
├─ cleanup_audit_days (int, 180)
|
||||
├─ cleanup_query_history_days (int, 30)
|
||||
└─ cleanup_task_log_days (int, 30)
|
||||
```
|
||||
|
||||
## 5. 数据模型
|
||||
|
||||
### 5.1 核心 ORM 模型 (22 个)
|
||||
|
||||
| 模型 | 说明 | 主要字段 |
|
||||
|------|------|----------|
|
||||
| User | 用户 | username, password_hash, role, display_name |
|
||||
| Instance | 数据源实例 | name, host, port, db_type, access_mode, group_id |
|
||||
| InstanceGroup | 权限组 | name, description, access_type, workflow_id |
|
||||
|| Ticket | 工单 | ticket_no, title, db_type, status, sql_content, audit_result, **scheduled_at** |
|
||||
| TicketApproval | 审批记录 | ticket_id, step_number, approver_id, action, step_type |
|
||||
| AuditLog | 审计日志 | user_id, action, resource_type, resource_id |
|
||||
| AuditRule | 审核规则 | name, code, db_type, rule_type, severity |
|
||||
| WorkflowDefinition | 审批流定义 | name, is_default, is_active |
|
||||
| WorkflowStep | 审批流步骤 | workflow_id, step_number, step_type, timeout_hours |
|
||||
| NotificationConfig | 通知渠道 | channel_type(webhook_url), channel_name |
|
||||
| Announcement | 公告 | title, content(Markdown) |
|
||||
| SystemSetting | 系统配置 | key, value, value_type, group |
|
||||
| SlowLog | 慢查询日志 | instance_id, query_text, duration |
|
||||
| QueryHistory | 查询历史 | user_id, sql_text, instance_id |
|
||||
| SchemaSnapshot | Schema 快照 | instance_id, database, snapshot_data |
|
||||
| SchemaDrift | Schema 漂移 | snapshot_id, drift_type, details |
|
||||
| ScheduledTask | 定时任务 | name, task_sql_type, cron_expression |
|
||||
| LdapConfig | LDAP 配置 | server_url, bind_dn, base_dn |
|
||||
| DataDictionary | 数据字典 | instance_id, database, table_name |
|
||||
| DictionaryField | 字典字段 | dictionary_id, column_name, description |
|
||||
|
||||
### 5.2 关联表
|
||||
|
||||
| 表 | 说明 |
|
||||
|------|------|
|
||||
| user_groups | 用户-权限组关联 (user_id, group_id, permissions) |
|
||||
| instance_group_members | 权限组-实例关联 (group_id, instance_id, access_type) |
|
||||
| workflow_execution_log | 审批流执行日志 |
|
||||
|
||||
## 6. 前端页面架构
|
||||
|
||||
### 6.1 页面清单 (26 个)
|
||||
|
||||
| 页面 | 路由 | 说明 | 行数 |
|
||||
|------|------|------|------|
|
||||
| LoginPage | /login | 登录 | ~180 |
|
||||
| Dashboard | / | 首页仪表盘 | ~350 |
|
||||
| QueriesPage | /queries | 自助查询 | ~780 |
|
||||
| TicketsPage | /tickets | 工单列表 | ~1100 |
|
||||
| TicketCreatePage | /tickets/create | 新建工单 | ~1000 |
|
||||
| InstancePage | /instances | 数据源管理 | ~700 |
|
||||
| InstanceDetailPage | /instances/:id | 实例详情(6 tab) | ~600 |
|
||||
| RedisDetailPage | /redis/:id | Redis 详情(4 tab) | ~500 |
|
||||
| MongoDBDetailPage | /mongodb/:id | MongoDB 详情 | ~400 |
|
||||
| UsersPage | /users | 用户管理 | ~500 |
|
||||
| AuditLogsPage | /audit-logs | 审计日志 | ~400 |
|
||||
| AuditRulesPage | /audit-rules | 审核规则(6 组) | ~600 |
|
||||
| SettingsPage | /settings | 系统设置(多 tab) | ~400 |
|
||||
| AnnouncementPage | /settings/announcements | 公告管理 | ~300 |
|
||||
| NotificationPage | /settings/notifications | 通知渠道 | ~350 |
|
||||
| InstanceGroupPage | /permissions | 权限管理(Transfer 穿梭框) | ~800 |
|
||||
| WorkflowPage | /workflows | 审批流管理 | ~600 |
|
||||
| PlatformConfigPage | /settings/platform | 平台配置 | ~350 |
|
||||
| DataDictionaryPage | /dictionary | 数据字典 | ~300 |
|
||||
| SlowQueryPage | /slow-query | 慢查询分析 | ~400 |
|
||||
| SchemaDriftPage | /schema-drift | Schema 漂移 | ~300 |
|
||||
|
||||
### 6.2 共享组件 (9 个)
|
||||
### 12.3 共享组件
|
||||
|
||||
| 组件 | 说明 |
|
||||
|------|------|
|
||||
| Layout | 侧边栏 + Header 布局 |
|
||||
| SqlEditor | CodeMirror 6 编辑器 (SQL/Redis/Mongo 语法) |
|
||||
| SchemaSidebar | 数据库导航树 (MySQL三级/Redis目录树/MongoDB集合) |
|
||||
| PageHeader | 页面标题 + 操作按钮 (权限控制) |
|
||||
| EmptyState | 空状态占位 |
|
||||
| EditInstanceDialog | 实例编辑弹框 (复用) |
|
||||
| ConfirmDialog | 确认弹框 (useConfirm hook) |
|
||||
| FormDialog | 表单弹框壳 |
|
||||
| EditInstanceDialog | 实例编辑弹框 (复用) |
|
||||
| ui/* | shadcn/ui 基础组件 (Button, Dialog, Input, Select...) |
|
||||
| ToastContainer | 右下角通知 (3秒消失) |
|
||||
| ui/* | shadcn/ui 基础组件 |
|
||||
|
||||
### 6.3 SchemaSidebar Redis 树特性
|
||||
|
||||
```
|
||||
Redis Keys 目录树:
|
||||
├─ 按 ':' 分隔构建层级 (如 cache:homepage:banner)
|
||||
├─ 文件夹节点: FolderOpen 琥珀色图标 + 子节点数
|
||||
├─ 叶子节点: KeyRound 图标按类型着色
|
||||
│ ├─ string → emerald (绿)
|
||||
│ ├─ hash → blue (蓝)
|
||||
│ ├─ list → purple (紫)
|
||||
│ ├─ set → orange (橙)
|
||||
│ └─ zset → rose (红)
|
||||
├─ 单列内嵌详情: 选中 key 下方展开 type/TTL/value
|
||||
├─ border-l 左竖线层级指示
|
||||
├─ 查询页: 点击 → 插入对应命令到编辑器
|
||||
│ (GET/HGETALL/LRANGE/SMEMBERS/ZRANGE)
|
||||
└─ 变更页: 同上 + hover 显示「插入」提示
|
||||
```
|
||||
|
||||
## 7. 通知系统
|
||||
|
||||
```
|
||||
通知渠道: DingTalk / Feishu / Email(SMTP)
|
||||
通知模板:
|
||||
├─ 工单创建通知 (申请人→审批人)
|
||||
├─ 工单审批通过通知
|
||||
├─ 工单审批拒绝通知
|
||||
└─ 工单执行完成通知
|
||||
|
||||
飞书: \n 换行
|
||||
钉钉: \n\n 双换行 (markdown 特性)
|
||||
```
|
||||
|
||||
## 8. 系统配置
|
||||
|
||||
18 项配置分 4 组:
|
||||
|
||||
**查询控制 (7 项)**:
|
||||
- query_require_approval (bool) - 查询是否需要审批
|
||||
- query_max_limit (int, 10000) - 最大返回行数
|
||||
- query_timeout (int, 30s) - 查询超时
|
||||
- query_max_length (int, 10000) - SQL 最大长度
|
||||
- query_auto_limit (bool) - 自动添加 LIMIT
|
||||
- query_enable_masking (bool) - 数据脱敏
|
||||
- query_save_history (bool) - 保存查询历史
|
||||
|
||||
**安全配置 (3 项)**:
|
||||
- security_max_login_attempts (int, 5) - 最大登录尝试
|
||||
- security_password_min_length (int, 6) - 密码最小长度
|
||||
- security_enable_audit_log (bool) - 审计日志开关
|
||||
|
||||
**工单配置 (2 项)**:
|
||||
- ticket_default_workflow (string, "standard") - 默认审批流
|
||||
- ticket_allow_rollback (bool) - 允许回滚
|
||||
|
||||
**清理配置 (6 项)** 🆕:
|
||||
- cleanup_enabled (bool, false) - 启用自动清理
|
||||
- cleanup_time (string, "03:00") - 清理执行时间
|
||||
- cleanup_ticket_days (int, 90) - 工单保留天数 (0=不清理)
|
||||
- cleanup_audit_days (int, 180) - 审计日志保留天数
|
||||
- cleanup_query_history_days (int, 30) - 查询历史保留天数
|
||||
- cleanup_task_log_days (int, 30) - 定时任务日志保留天数
|
||||
|
||||
## 9. 四角色体系
|
||||
## 13. 权限体系(四角色模型)
|
||||
|
||||
| 角色 | 权限范围 |
|
||||
|------|----------|
|
||||
| admin (超级管理员) | 全部功能 + 用户管理 + 系统设置 |
|
||||
| dba (DBA管理员) | 全部库访问 + 审批/拒绝/执行工单 |
|
||||
| auditor (审计员) | 全部只读 + 审批/拒绝工单 |
|
||||
| admin (超级管理员) | 全部功能 + 用户管理 + 系统设置 + LDAP 管理 |
|
||||
| dba (DBA管理员) | 全部库访问 + 审批/拒绝/执行工单 + 实例管理 + gh-ost |
|
||||
| auditor (审计员) | 全部只读 + 审计日志查看 + 审批/拒绝工单 |
|
||||
| developer (开发者) | 按权限组访问 + 创建/撤销自己的工单 |
|
||||
|
||||
**默认用户**: admin/admin123, zhangsan(张三)/admin123, laoliu(老六)/admin123, lurenjia(路人甲)/admin123
|
||||
**默认种子用户** (auth_service.go 自动创建):
|
||||
- admin/admin123 → 超级管理员
|
||||
- zhangsan/admin123 → 张三 (dba)
|
||||
- laoliu/admin123 → 老六 (auditor)
|
||||
- lurenjia/admin123 → 路人甲 (developer)
|
||||
|
||||
## 10. 运维部署
|
||||
## 14. 运维部署
|
||||
|
||||
```bash
|
||||
# 开发环境启动
|
||||
bash run.sh start # 启动前后端 (后端9000, 前端5173)
|
||||
# 开发环境
|
||||
bash run.sh start # 启动 Go 后端 (:9000) + 前端 (:5173)
|
||||
bash run.sh restart # 重启
|
||||
bash run.sh stop # 停止
|
||||
bash run.sh status # 状态检查
|
||||
|
||||
# 测试
|
||||
bash run.sh test # 后端测试 (pytest)
|
||||
cd frontend && npx tsc --noEmit # 前端类型检查
|
||||
# 后端编译
|
||||
cd backend && go build -o sql-platform ./cmd/server/
|
||||
|
||||
# 生产构建
|
||||
bash run.sh build # Docker build
|
||||
docker-compose up -d # 部署
|
||||
# 健康检查
|
||||
curl http://localhost:9000/health
|
||||
# → {"status":"ok","version":"0.3.1","backend":"go"}
|
||||
```
|
||||
|
||||
## 11. 关键设计决策
|
||||
## 15. 关键设计决策
|
||||
|
||||
|| 决策 | 原因 |
|
||||
||------|------|
|
||||
|| SQLite 开发数据库 | 零配置,单文件,便于版本管理 |
|
||||
|| AES-256-GCM 加密实例密码 | 安全存储,向后兼容明文 |
|
||||
|| JWT 30分钟过期 | 安全与便利平衡 |
|
||||
|| 暗色主题 CSS 变量体系 (--dg-) | 统一视觉风格 |
|
||||
|| CodeMirror 6 | 现代化编辑器,SQL 自动补全 |
|
||||
|| 权限组中间表设计 | 一组同时包含只读+写入实例 |
|
||||
|| React.lazy 代码分割 | 首屏 -44% (2676KB→1438KB) |
|
||||
|| 工单编号 20 位 | 时间戳+微秒+序号,全局唯一 |
|
||||
|| APScheduler + SQLite 持久化 | 定时任务跨重启不丢失,轻量级方案 |
|
||||
|| 设置种子数据机制 | `_ensure_settings` 启动自动补全,保证配置完整性 |
|
||||
|| 工单创建页单页化 | 简化三步向导为一键提交,提升操作效率 |
|
||||
| 决策 | 原因 |
|
||||
|------|------|
|
||||
| Go + Gin 替代 Python/FastAPI | 编译为单二进制,无运行时依赖,性能优异 |
|
||||
| GORM AutoMigrate | 零配置 schema 管理,启动时自动同步 |
|
||||
| SQLite 元数据库 | 零配置,单文件,便于开发部署 |
|
||||
| JWT HS256 (24h) | 安全与便利平衡,配置可调 |
|
||||
| 中间件分层 (Auth → RequireRole) | 细粒度 RBAC,路由级权限控制 |
|
||||
| 5 种登录页主题 | 用户体验个性化,localStorage 持久化 |
|
||||
| React.lazy 代码分割 | 首屏加载优化 |
|
||||
| go-ldap/ldap/v3 | 原生 Go LDAP 客户端,企业域集成 |
|
||||
+159
-149
@@ -1,6 +1,6 @@
|
||||
# SQL-Platform-Pro 功能清单
|
||||
# SQL-Platform 功能清单
|
||||
|
||||
> 生成时间: 2026-05-25 | 版本: v1.1
|
||||
> 更新时间: 2026-05-26 | 版本: v0.3.1 (Go/Gin 后端)
|
||||
|
||||
## ✅ 已完成功能
|
||||
|
||||
@@ -14,218 +14,228 @@
|
||||
- [x] 查询结果表格 (点击单元格弹详情 + JSON 格式化)
|
||||
- [x] 查询历史记录 (500 条/用户上限)
|
||||
- [x] 收藏查询 / 快捷查询
|
||||
- [x] Schema 侧边栏 (MySQL 三级树 / Redis 目录树 / MongoDB 集合) — **已加宽 (grid-cols-5 2:3 布局)**
|
||||
- [x] Redis key 点击自动插入查询命令 (GET/HGETALL/LRANGE/SMEMBERS/ZRANGE) — **onInsert prop**
|
||||
- [x] Schema 侧边栏 (MySQL 三级树 / Redis 目录树 / MongoDB 集合)
|
||||
- [x] Redis key 点击自动插入查询命令 (GET/HGETALL/LRANGE/SMEMBERS/ZRANGE)
|
||||
- [x] 两阶段 schema 加载 (选实例加载表名, 选库加载列名)
|
||||
- [x] 数据库品牌图标 (techicons.dev SVG)
|
||||
- [x] 实例按权限组过滤 (/instances/accessible?access_type=readonly)
|
||||
- [x] 实例按权限组过滤 (/instances/accessible)
|
||||
|
||||
#### 2. 数据变更审批流 (TicketsPage + TicketCreatePage)
|
||||
- [x] 工单创建 — 单页面一键提交 (原三步向导已简化)
|
||||
- [x] 工单创建 — 单页面一键提交
|
||||
- [x] DB 类型按频率排序 (MySQL → Redis → PostgreSQL → MongoDB)
|
||||
- [x] 选择 DB 类型后自动筛选目标实例
|
||||
- [x] 库表浏览面板 (点击展开库/表/字段,辅助填写目标库)
|
||||
- [x] Redis 目录树按 `:` 分隔构建层级
|
||||
- [x] 工单编号生成 (20位: 时间戳+微秒+序号)
|
||||
- [x] 工单状态机 (pending → reviewing → approved → **scheduled** → executing → success/failed)
|
||||
- [x] 工单状态机 (pending → reviewing → approved → scheduled → executing → success/failed)
|
||||
- [x] 10 种工单状态完整支持
|
||||
- [x] 多步审批流 (standard / parallel / any_one / condition)
|
||||
- [x] 审批人限定 (必须在权限组成员中)
|
||||
- [x] 工单撤销 (pending/reviewing 状态可撤销)
|
||||
- [x] 执行结果结构化展示 (状态/行数/耗时/验证/错误/警告)
|
||||
- [x] 当前审核人显示
|
||||
- [x] CancelTicket 独立处理器
|
||||
- [x] RollbackTicket 独立处理器
|
||||
- [x] GetTicketTimeline 时间线查询
|
||||
- [x] GetTicketApprovals 审批记录查询
|
||||
- [x] 工单快速筛选 (待审批/进行中/已完成/已拒绝)
|
||||
- [x] 实例按 DB 类型筛选
|
||||
- [x] Redis 目录树浏览 + 点击插入命令
|
||||
- [x] MongoDB 集合浏览 + 点击插入命令
|
||||
- [x] Redis/MongoDB 目录树浏览 + 点击插入命令
|
||||
- [x] SQL 提交前强制审核规则检查
|
||||
- [x] 执行结果结构化展示 (状态/行数/耗时/错误/警告)
|
||||
|
||||
#### 3. SQL 审核引擎
|
||||
- [x] 127 条审核规则 (MySQL 67 / PostgreSQL 31 / Redis 25 / MongoDB 31)
|
||||
- [x] 6 个分组 (DDL/DML/DCL/DQL/security/performance)
|
||||
- [x] MySQL 安全规则 SEC-001~009
|
||||
- [x] Redis 规则去重优化 (CMD-001 覆盖 SEC-003)
|
||||
- [x] Redis 规则去重优化
|
||||
- [x] 规则 CRUD (创建/读取/更新/删除/开关)
|
||||
- [x] 规则统计和筛选
|
||||
- [x] 评分系统 (severity: error/warning/info)
|
||||
- [x] POST /tickets/audit — SQL 审核端点
|
||||
|
||||
#### 3.1 SQL 检测按钮 (TicketCreatePage)
|
||||
- [x] 提交按钮旁新增「SQL 检测」按钮
|
||||
- [x] 验证通过前提交按钮置灰 + 提示文字
|
||||
- [x] SQL 内容变化自动重置验证状态
|
||||
- [x] 定时时间显示为醒目红色字体
|
||||
|
||||
#### 3.2 定时执行工单 (ticket_scheduler.py)
|
||||
- [x] 基于 APScheduler + SQLite 持久化的定时调度器 (`apscheduler.db`)
|
||||
- [x] Ticket 模型新增 `scheduled_at` 字段
|
||||
- [x] 新增 `SCHEDULED` 状态 (approved ↔ scheduled → executing → success/failed)
|
||||
- [x] API: `GET /tickets/scheduled/list` 定时工单列表
|
||||
- [x] API: `POST /tickets/{id}/cancel-schedule` 取消定时
|
||||
- [x] API: `GET /tickets/scheduled/stats` 定时统计
|
||||
- [x] 前端: TicketCreatePage 定时执行 datetime picker
|
||||
- [x] 前端: AutoTaskPage 定时任务管理页
|
||||
|
||||
#### 3.3 日志自动清理 (log_cleanup.py)
|
||||
- [x] 系统设置新增 `cleanup` 分组 (6 项配置)
|
||||
- [x] 启用自动清理开关
|
||||
- [x] 清理执行时间 (默认凌晨 3 点)
|
||||
- [x] 工单保留天数 (默认 90 天)
|
||||
- [x] 审计日志保留天数 (默认 180 天)
|
||||
- [x] 查询历史保留天数 (默认 30 天)
|
||||
- [x] 定时任务日志保留天数 (默认 30 天)
|
||||
- [x] 只清理已完成/已拒绝的工单,0 天 = 不清理
|
||||
- [x] API: `POST /settings/cleanup/run` 手动触发清理
|
||||
- [x] API: `GET /settings/cleanup/preview` 预览待清理数据
|
||||
- [x] 前端: PlatformConfigPage 清理分组 + CleanupActions 预览/手动清理按钮
|
||||
- [x] APScheduler cron job 每天 03:00 自动执行
|
||||
|
||||
#### 4. 审批流管理 (WorkflowPage)
|
||||
- [x] 审批流 CRUD (创建/编辑/删除)
|
||||
- [x] 步骤管理 (拖拽排序 + 类型选择)
|
||||
- [x] 3 种高级步骤类型 (parallel 会签 / any_one 或签 / condition 条件分支)
|
||||
- [x] 条件评估引擎 (sql_type/db_type/risk_level/priority)
|
||||
- [x] 超时检查 (timeout_hours + auto_approve_on_timeout)
|
||||
- [x] 默认审批流绑定 (is_default + is_active)
|
||||
#### 4. SQL 助手
|
||||
- [x] POST /sql-assistant/format — SQL 格式化
|
||||
- [x] POST /sql-assistant/analyze — SQL 分析
|
||||
|
||||
### 二、数据源管理
|
||||
|
||||
#### 5. 实例管理 (InstancePage)
|
||||
- [x] 实例 CRUD (4 种 DB 类型)
|
||||
- [x] 连接测试 (MySQL/PG/Redis/MongoDB/SQLite)
|
||||
- [x] 实例密码 AES-256-GCM 加密 (向后兼容明文)
|
||||
- [x] 实例 CRUD (4 种 DB 类型: mysql/postgresql/redis/mongodb)
|
||||
- [x] 连接测试 (MySQL/PG/Redis/MongoDB)
|
||||
- [x] 实例密码 AES-256-GCM 加密 (crypto.go)
|
||||
- [x] 卡片视图 + 列表视图
|
||||
- [x] 按类型 Tab 筛选 (全部/MySQL/PostgreSQL/Redis/MongoDB)
|
||||
- [x] 按类型 Tab 筛选
|
||||
- [x] 品牌图标展示
|
||||
- [x] 编辑弹框 (EditInstanceDialog 可复用)
|
||||
- [x] 克隆实例 (名称自动加 "-副本")
|
||||
- [x] 实例详情页 (6 tab: 概览/表/查询/用户/慢查询/进程)
|
||||
- [x] Redis 详情页 (4 tab: Keys/监控/CLI/性能)
|
||||
- [x] MongoDB 详情页 (数据库/集合/统计/查询CLI)
|
||||
- [x] 实例统计 (InstanceStats)
|
||||
|
||||
#### 6. 库表浏览
|
||||
- [x] MySQL: SHOW DATABASES → information_schema.TABLES → COLUMNS
|
||||
- [x] PostgreSQL: pg_database → pg_tables → column list
|
||||
- [x] Redis: CONFIG GET databases → db0~db{N} (无表概念)
|
||||
- [x] MongoDB: list_database_names → list_collection_names
|
||||
- [x] 统一走后端 listDatabases/listTables API
|
||||
#### 6. MySQL Instance Detail — 7 个端点
|
||||
- [x] databases — 数据库列表
|
||||
- [x] tables — 表列表
|
||||
- [x] db-schema — 库 Schema
|
||||
- [x] table-detail — 表详情 (列/索引)
|
||||
- [x] processlist — 进程列表 (含 Kill)
|
||||
- [x] status — 服务器状态
|
||||
- [x] users — 用户列表
|
||||
|
||||
#### 7. Redis — 9 个真实 Handler
|
||||
- [x] RedisInfo — 服务器信息
|
||||
- [x] RedisDbsize — 数据库大小
|
||||
- [x] RedisScanKeys — Key 扫描
|
||||
- [x] RedisGetKeyDetail — Key 详情 (type/TTL/value)
|
||||
- [x] RedisSlowlog — 慢查询日志
|
||||
- [x] RedisClients — 客户端列表
|
||||
- [x] RedisExecuteCommand — 执行命令
|
||||
- [x] RedisCreateKey — 创建 Key
|
||||
- [x] RedisDeleteKey — 删除 Key
|
||||
|
||||
#### 8. MongoDB Stub 端点
|
||||
- [x] databases — 数据库列表
|
||||
- [x] collections — 集合列表
|
||||
- [x] collection/stats — 集合统计
|
||||
- [x] query — 查询
|
||||
- [x] execute — 执行
|
||||
- [x] test-connection — 连接测试
|
||||
|
||||
### 三、权限与安全
|
||||
|
||||
#### 7. 权限管理 (InstanceGroupPage)
|
||||
#### 9. 权限管理 (InstanceGroupPage)
|
||||
- [x] 权限组 CRUD
|
||||
- [x] 2 种权限类型 (只读查看 / 写入变更)
|
||||
- [x] Transfer 穿梭框选择数据库
|
||||
- [x] 用户-权限组关联
|
||||
- [x] 实例-权限组关联 (中间表 instance_group_members)
|
||||
- [x] 7 个操作权限 action
|
||||
- [x] 实例-权限组关联 (instance_group_members)
|
||||
- [x] 用户-权限组关联 (user_groups)
|
||||
- [x] 角色权限映射 (admin/dba/auditor/developer)
|
||||
- [x] admin 也按权限组过滤 (无 bypass)
|
||||
- [x] RequireRole RBAC 中间件
|
||||
|
||||
#### 8. 用户管理 (UsersPage)
|
||||
#### 10. 用户管理 (UsersPage)
|
||||
- [x] 用户 CRUD (4 角色: admin/dba/auditor/developer)
|
||||
- [x] bcrypt 4.0 密码哈希
|
||||
- [x] 默认用户种子数据 (4 个: admin/zhangsan/laoliu/lurenjia)
|
||||
- [x] bcrypt 密码哈希 (golang.org/x/crypto)
|
||||
- [x] 4 个默认种子用户 (auth_service.go 自动创建):
|
||||
- admin → 超级管理员
|
||||
- zhangsan (张三) → dba
|
||||
- laoliu (老六) → auditor
|
||||
- lurenjia (路人甲) → developer
|
||||
- [x] 批量导入用户 (CSV + LDAP)
|
||||
- [x] 密码重置
|
||||
- [x] guest 角色已彻底禁用
|
||||
|
||||
#### 9. 审计日志 (AuditLogsPage)
|
||||
#### 11. 审计日志 (AuditLogsPage)
|
||||
- [x] 全操作审计日志 (实例/用户/工单/查询 CRUD)
|
||||
- [x] 日志筛选 (时间范围/用户/操作类型)
|
||||
- [x] auditor 角色只读访问
|
||||
|
||||
#### 12. LDAP 认证
|
||||
- [x] LDAP 配置管理 (GET/PUT /ldap-config)
|
||||
- [x] 连接测试 (POST /ldap-config/test)
|
||||
- [x] OU 管理 (列表/创建)
|
||||
- [x] LDAP 用户管理 (列表/创建/删除)
|
||||
- [x] 批量导入 LDAP 用户
|
||||
- [x] 服务器: 8.217.205.92:389, base_dn=dc=opsx,dc=vip
|
||||
- [x] 驱动: github.com/go-ldap/ldap/v3
|
||||
|
||||
### 四、系统设置
|
||||
|
||||
#### 10. 通知管理 (NotificationPage)
|
||||
- [x] 通知渠道配置 (DingTalk/Feishu/Email)
|
||||
- [x] 真实消息发送 (非模拟)
|
||||
- [x] 通知模板 (创建/通过/拒绝/执行完成)
|
||||
- [x] 钉钉/飞书换行适配
|
||||
- [x] 测试发送功能
|
||||
#### 13. 通知管理 (NotificationPage)
|
||||
- [x] 通知渠道 CRUD (feishu/dingtalk/wechat/email webhook)
|
||||
- [x] 测试发送功能 (POST /notifications/:id/test)
|
||||
- [x] NotificationChannel + NotificationTemplate 模型
|
||||
|
||||
#### 11. 系统配置 (PlatformConfigPage)
|
||||
- [x] 18 项配置 (查询控制 7 + 安全 3 + 工单 2 + **清理 6**)
|
||||
- [x] 分组卡片布局 (布尔→开关/整数→数字/字符串→文本)
|
||||
- [x] 批量保存 + 变更指示 + 重置默认值
|
||||
- [x] 查询接口集成 (审批检查/长度检查)
|
||||
- [x] **设置种子数据机制** (`settings_seeds.py` 统一管理所有设置项)
|
||||
- [x] **`_ensure_settings` 启动时自动补缺失配置**
|
||||
- [x] **`SETTING_GROUPS` 分组元信息**
|
||||
#### 14. 系统配置 (PlatformConfigPage)
|
||||
- [x] GET /settings — 读取配置
|
||||
- [x] PUT /settings — 批量更新
|
||||
- [x] GET /settings/cleanup/preview — 清理预览
|
||||
- [x] POST /settings/cleanup/run — 手动清理
|
||||
- [x] 分组卡片布局
|
||||
|
||||
#### 12. 公告管理 (AnnouncementPage)
|
||||
#### 15. 公告管理 (AnnouncementPage)
|
||||
- [x] 公告 CRUD (Markdown 内容)
|
||||
- [x] 发布/撤回状态管理
|
||||
|
||||
#### 13. 审核规则管理 (AuditRulesPage)
|
||||
#### 16. 审核规则管理 (AuditRulesPage)
|
||||
- [x] 规则列表 (6 组 Tab: DDL/DML/DCL/DQL/security/performance)
|
||||
- [x] 规则开关 (toggle)
|
||||
- [x] 规则统计
|
||||
- [x] 按数据库类型筛选
|
||||
- [x] 规则种子 (POST /audit-rules/seed/{db_type})
|
||||
|
||||
### 五、基础设施
|
||||
#### 17. 审批流管理 (WorkflowPage)
|
||||
- [x] 审批流 CRUD (创建/编辑/删除)
|
||||
- [x] 克隆审批流
|
||||
- [x] 设置默认审批流
|
||||
- [x] 步骤管理
|
||||
- [x] 条件评估引擎
|
||||
- [x] 超时检查 (timeout_hours + auto_approve_on_timeout)
|
||||
|
||||
#### 14. 前端优化
|
||||
- [x] React.lazy 代码分割 (18 页面懒加载)
|
||||
### 五、外观与个性化 (v0.3.1 新增 🆕)
|
||||
|
||||
#### 18. 登录页主题系统
|
||||
- [x] 5 种登录页风格:
|
||||
- **SplitView** (经典分屏) — 左侧特性展示 + 右侧表单
|
||||
- **CenteredCard** (居中卡片) — 渐变背景 + 居中表单
|
||||
- **SidePanel** (侧边面板) — 左侧品牌面板 + 右侧表单
|
||||
- **FullImage** (全屏背景) — 科技背景 + 毛玻璃卡片
|
||||
- **MinimalDark** (极简暗色) — 纯暗色极简风格
|
||||
- [x] 共享 `useLogin` hook — 统一登录逻辑
|
||||
- [x] `localStorage('login-style')` 持久化选择
|
||||
|
||||
#### 19. 外观设置 (AppearanceSettings)
|
||||
- [x] Settings → Appearance tab
|
||||
- [x] 主题模式切换 (dark/light)
|
||||
- [x] 登录页风格切换 (5 种预览卡片 + 选择)
|
||||
- [x] 实时预览 + localStorage 持久化
|
||||
|
||||
### 六、其他功能
|
||||
|
||||
#### 20. gh-ost DDL 变更
|
||||
- [x] gh-ost dry-run (POST /ghost/dry-run)
|
||||
- [x] gh-ost execute (POST /ghost/execute)
|
||||
- [x] gh-ost status (GET /ghost/status/:taskId)
|
||||
- [x] gh-ost task list (GET /ghost/tasks)
|
||||
- [x] 仅 admin/dba 可访问
|
||||
|
||||
#### 21. 仪表盘 (Dashboard)
|
||||
- [x] 统计概览 (GET /dashboard/stats)
|
||||
- [x] 我的工单 (GET /dashboard/my-tickets)
|
||||
|
||||
#### 22. 定时任务 (AutoTaskPage)
|
||||
- [x] 定时工单列表
|
||||
- [x] 取消定时任务
|
||||
- [x] 定时任务统计
|
||||
|
||||
#### 23. 数据字典 (stub)
|
||||
- [x] 数据库/表/字段浏览端点
|
||||
- [x] 导出端点
|
||||
|
||||
#### 24. 前端优化
|
||||
- [x] React.lazy 代码分割
|
||||
- [x] Vite manualChunks (vendor 拆分)
|
||||
- [x] 首屏 -44% (2676KB → 1438KB)
|
||||
- [x] 暗色主题 (--dg- CSS 变量体系)
|
||||
- [x] 响应式布局
|
||||
- [x] 骨架屏加载动画
|
||||
- [x] 确认弹框 (useConfirm hook, 替代浏览器 confirm)
|
||||
- [x] Toast 通知 (右下角 3 秒消失)
|
||||
|
||||
#### 15. 后端基础设施
|
||||
- [x] JWT 认证 (30 分钟过期)
|
||||
- [x] run.sh PID 管理脚本 (start/stop/restart/status/logs/build/test)
|
||||
- [x] Alembic 数据库迁移
|
||||
- [x] Docker + docker-compose 部署
|
||||
- [x] CORS 策略
|
||||
- [x] 请求日志中间件
|
||||
|
||||
## 🔨 开发中 / 待实现
|
||||
|
||||
### 短期 (P1)
|
||||
- [ ] 通知功能前端页面完善
|
||||
- [ ] 两个页面 Redis 树抽取为共享组件
|
||||
- [ ] LDAP 认证对接 (后端已有 LdapConfig 模型)
|
||||
|
||||
### 中期 (P2)
|
||||
- [ ] Schema 漂移检测 (后端有 SchemaSnapshot/SchemaDrift 模型)
|
||||
- [ ] 快照对比可视化
|
||||
- [ ] 数据脱敏规则 (MaskingRule 模型)
|
||||
- [ ] SQL 格式化后端 (sqlparse)
|
||||
- [ ] EXPLAIN 真实解析
|
||||
- [ ] 慢查询实时推送 (WebSocket)
|
||||
- [ ] CSV 导出功能对接
|
||||
|
||||
### 远期 (P3)
|
||||
- [ ] 库级权限控制
|
||||
- [ ] 查询审批工作流
|
||||
- [ ] 多租户支持
|
||||
- [ ] 审批流模板持久化
|
||||
- [ ] 集群模式支持
|
||||
- [x] 确认弹框 (useConfirm hook)
|
||||
- [x] Toast 通知
|
||||
|
||||
## 📊 项目统计
|
||||
|
||||
|| 指标 | 数值 |
|
||||
||------|------|
|
||||
|| 总提交 | 56+ |
|
||||
|| 源文件 | 328+ |
|
||||
|| 后端代码 | ~23,000 行 Python |
|
||||
|| 前端代码 | ~15,000 行 TypeScript/TSX |
|
||||
|| API 路由 | 152+ 条 |
|
||||
|| 测试用例 | 1,075+ 个 |
|
||||
|| 数据库表 | 28+ 张 |
|
||||
|| ORM 模型 | 22+ 个 |
|
||||
|| 前端页面 | 26+ 个 |
|
||||
|| 审核规则 | 127 条 |
|
||||
|| 系统配置 | 18 项 |
|
||||
|| 定时调度 | APScheduler + SQLite 持久化 |
|
||||
| 指标 | 数值 |
|
||||
|------|------|
|
||||
| Go 源文件 | 27+ |
|
||||
| API 路由 | 120+ 条 |
|
||||
| GORM 模型 | 25+ 个 |
|
||||
| Handler 文件 | 17 个 |
|
||||
| 前端页面 | 24+ 个 |
|
||||
| 登录页主题 | 5 种 |
|
||||
| 审核规则 | 127 条 (6 组) |
|
||||
| 数据库驱动 | 5 种 (MySQL/PG/Redis/Mongo/SQLite) |
|
||||
| Redis Handler | 9 个 |
|
||||
| MySQL Detail 端点 | 7 个 |
|
||||
| 种子用户 | 4 个 |
|
||||
| Git 仓库 | https://gitee.com/attacker/sql-platform.git |
|
||||
|
||||
## 🏗️ 数据库连接器支持矩阵
|
||||
## 🏗️ 数据库驱动支持矩阵
|
||||
|
||||
| 数据库 | 连接库 | 查询 | 执行 | 审核 | 库表浏览 |
|
||||
|--------|--------|------|------|------|----------|
|
||||
| MySQL | aiomysql | ✅ | ✅ | ✅ (67规则) | ✅ |
|
||||
| PostgreSQL | asyncpg | ✅ | ✅ | ✅ (31规则) | ✅ |
|
||||
| Redis | redis.asyncio | ✅ | ✅ | ✅ (25规则) | ✅ (Keys树) |
|
||||
| MongoDB | pymongo | ✅ | ✅ | ✅ (31规则) | ✅ (集合) |
|
||||
| SQLite | sqlite3 | ✅ | — | — | — |
|
||||
| 数据库 | Go 驱动 | 查询 | 执行 | 审核 | 详情浏览 |
|
||||
|--------|---------|------|------|------|----------|
|
||||
| MySQL | go-sql-driver/mysql v1.10.0 | ✅ | ✅ | ✅ (67规则) | ✅ (7 endpoints) |
|
||||
| PostgreSQL | jackc/pgx/v5 v5.9.2 | ✅ | ✅ | ✅ (31规则) | ✅ |
|
||||
| Redis | go-redis/v9 v9.19.0 | ✅ | ✅ | ✅ (25规则) | ✅ (9 handlers) |
|
||||
| MongoDB | go-mongo-driver/v2 | ✅ (stub) | ✅ (stub) | ✅ (31规则) | ✅ (stub) |
|
||||
| SQLite | gorm.io/driver/sqlite | ✅ (元数据) | — | — | — |
|
||||
+103
-53
@@ -1,63 +1,113 @@
|
||||
# SQL-Platform-Pro 开发路线图
|
||||
# SQL-Platform 开发路线图
|
||||
|
||||
> 更新时间: 2026-05-25
|
||||
> 更新时间: 2026-05-26 | 当前版本: **v0.3.1** (Go/Gin 后端)
|
||||
|
||||
## 当前版本: v1.1 (开发中)
|
||||
## 当前版本: v0.3.1
|
||||
|
||||
### 已完成里程碑
|
||||
### Python → Go 迁移历程
|
||||
|
||||
|| 阶段 | 版本 | 测试数 | 关键交付 |
|
||||
||------|------|--------|----------|
|
||||
|| Phase 1 | v0.1.0 | 44 | 后端 API + TDD 测试 |
|
||||
|| Phase 2 | v0.2.0 | 44 | 前后端集成 |
|
||||
|| Phase 3 | v0.3.0 | 85 | AES 加密 + SQL 审核引擎 + 审计日志 |
|
||||
|| Phase 4 | v0.4.0 | 128 | SQL AST + 规则 CRUD + Alembic + Docker |
|
||||
|| Phase 5 | v0.5.0 | 128 | run.sh 运维 + DBConnector + CI/CD |
|
||||
|| Phase 6 | v0.6.0 | 298 | Redis/MongoDB + 权限组 + 多步审批流 |
|
||||
|| Phase 7 | v0.7.0 | 475 | 多数据库查询/执行引擎 + SQL 助手 |
|
||||
|| Phase 8 | v0.8.0 | 700 | P2 高级功能测试 + endpoint 修复 |
|
||||
|| Phase 9 | v0.9.0 | 829 | P0 安全修复 — 实例访问控制全线接入权限组 |
|
||||
|| Phase 10 | v0.9.5 | 877 | 公告 + Dashboard 测试 |
|
||||
|| Phase 11 | v0.9.7 | 895 | 查询端点测试扩展 |
|
||||
|| Phase 12 | v0.9.8 | 1017 | P2 测试覆盖 — user/pool/schema_drift |
|
||||
|| Phase 13 | v0.9.9 | 1040 | 审核引擎优化 + 执行结果结构化 |
|
||||
|| Merge | v1.0-merge | — | SQL平台代码 v1 优化包 (193 条审核规则 + 16 页面 UI) |
|
||||
|| v1.0 | — | 1075 | 多数据库浏览 + Redis 树 + 权限体系统一 + 代码分割 |
|
||||
|| **v1.1** 🆕 | — | 1075+ | **定时执行工单 + 日志自动清理 + SQL检测按钮 + 工单创建页重构 + SchemaSidebar 加宽 + 设置种子数据机制** |
|
||||
| 阶段 | 版本 | 后端 | 关键交付 |
|
||||
|------|------|------|----------|
|
||||
| Phase 1 | v0.1.0 | Python/FastAPI | 后端 API + TDD 测试 |
|
||||
| Phase 2 | v0.2.0 | Python/FastAPI | 前后端集成 |
|
||||
| Phase 3 | v0.3.0 | Python/FastAPI | AES 加密 + SQL 审核引擎 + 审计日志 |
|
||||
| Phase 4 | v0.4.0 | Python/FastAPI | SQL AST + 规则 CRUD + Alembic + Docker |
|
||||
| Phase 5 | v0.5.0 | Python/FastAPI | run.sh 运维 + DBConnector + CI/CD |
|
||||
| Phase 6 | v0.6.0 | Python/FastAPI | Redis/MongoDB + 权限组 + 多步审批流 |
|
||||
| Phase 7 | v0.7.0 | Python/FastAPI | 多数据库查询/执行引擎 + SQL 助手 |
|
||||
| Phase 8 | v0.8.0 | Python/FastAPI | P2 高级功能测试 + endpoint 修复 |
|
||||
| Phase 9 | v0.9.0 | Python/FastAPI | P0 安全修复 — 实例访问控制全线接入权限组 |
|
||||
| Phase 10 | v0.9.5 | Python/FastAPI | 公告 + Dashboard |
|
||||
| Phase 11 | v0.9.9 | Python/FastAPI | 审核引擎优化 + 执行结果结构化 |
|
||||
| Phase 12 | v1.0 | Python/FastAPI | 多数据库浏览 + Redis 树 + 权限体系统一 + 代码分割 |
|
||||
| Phase 13 | v1.1 | Python/FastAPI | 定时执行工单 + 日志自动清理 + 设置种子 |
|
||||
| **Migration** | **v0.3.0-go** | **Go/Gin** | **Python→Go 全量重写: Gin + GORM + 120+ 路由 + 25 模型** |
|
||||
| **Current** | **v0.3.1** | **Go/Gin** | **5 种登录页主题 + Appearance 设置 + 审计员角色 + LDAP** |
|
||||
|
||||
### v1.1 新增功能详情
|
||||
### v0.3.1 功能详情
|
||||
|
||||
|| 功能 | 说明 | 状态 |
|
||||
||------|------|------|
|
||||
|| 定时执行工单 | APScheduler + SQLite 持久化, scheduled_at 字段, SCHEDULED 状态 | ✅ |
|
||||
|| 日志自动清理 | 6 项 cleanup 配置, cron 03:00, 预览/手动清理 API | ✅ |
|
||||
|| SQL 检测按钮 | 提交前验证, 内容变化重置, 定时时间红色字体 | ✅ |
|
||||
|| 工单创建页重构 | 三步向导→单页面一键提交, DB类型排序, 库表浏览面板 | ✅ |
|
||||
|| SchemaSidebar 加宽 | grid-cols-5 (2:3), RedisTree onInsert prop | ✅ |
|
||||
|| 设置种子数据机制 | settings_seeds.py, _ensure_settings, SETTING_GROUPS | ✅ |
|
||||
| 功能 | 说明 | 状态 |
|
||||
|------|------|------|
|
||||
| Go/Gin 后端 | 完整替代 Python/FastAPI,Gin + GORM + SQLite | ✅ |
|
||||
| CancelTicket/RollbackTicket | 独立处理器,工单取消/回滚 | ✅ |
|
||||
| GetTicketTimeline/GetTicketApprovals | 工单时间线 + 审批记录 | ✅ |
|
||||
| MySQL Instance Detail | 7 个端点 (databases/tables/schema/table_detail/processlist/status/users) | ✅ |
|
||||
| Redis 9 个 Handler | info/dbsize/keys/key_detail/slowlog/clients/command/create_key/delete_key | ✅ |
|
||||
| SQL 审核 | POST /tickets/audit | ✅ |
|
||||
| SQL 格式化/分析 | POST /sql-assistant/format, /sql-assistant/analyze | ✅ |
|
||||
| 字典/MongoDB stub | 桩端点预留 | ✅ |
|
||||
| 工单状态: success | 完成(非 completed) | ✅ |
|
||||
| 4 个种子用户 | admin/张三(dba)/老六(auditor)/路人甲(developer) | ✅ |
|
||||
| auditor 角色 | 只读审计规则 + 工单 | ✅ |
|
||||
| LDAP 集成 | 配置/测试/OU管理/用户管理/批量导入 | ✅ |
|
||||
| 5 种登录页主题 🆕 | SplitView/CenteredCard/SidePanel/FullImage/MinimalDark | ✅ |
|
||||
| Appearance 设置 🆕 | 主题切换 + 登录页风格选择 | ✅ |
|
||||
| useLogin 共享 hook 🆕 | 统一登录逻辑,5 主题复用 | ✅ |
|
||||
|
||||
### 下一步计划
|
||||
---
|
||||
|
||||
#### Sprint 1: 功能补全
|
||||
- [ ] 通知页面完善 (渠道管理 + 模板管理)
|
||||
- [ ] Redis 树共享组件抽取
|
||||
- [ ] LDAP 认证对接
|
||||
## Sprint 计划
|
||||
|
||||
#### Sprint 2: 质量提升
|
||||
- [ ] 前端 Vitest 测试覆盖
|
||||
- [ ] E2E 测试 (Playwright)
|
||||
- [ ] 性能测试 (查询并发/工单并发)
|
||||
### Sprint 1: 通知集成完善 (当前)
|
||||
- [ ] 通知模板管理 UI 完善
|
||||
- [ ] 通知事件绑定 (工单创建/审批/执行自动触发)
|
||||
- [ ] 飞书/钉钉/企业微信/邮件 webhook 真实发送
|
||||
- [ ] 通知发送失败重试机制
|
||||
- [ ] 通知历史记录查看
|
||||
|
||||
### Sprint 2: E2E 测试
|
||||
- [ ] Playwright E2E 测试框架搭建
|
||||
- [ ] 登录流程 E2E (5 种主题覆盖)
|
||||
- [ ] 工单创建-审批-执行全链路 E2E
|
||||
- [ ] 查询执行 E2E (MySQL/PG/Redis)
|
||||
- [ ] 用户管理 E2E (CRUD + 权限)
|
||||
- [ ] Go 后端单元测试覆盖提升
|
||||
- [ ] API 集成测试
|
||||
|
||||
### Sprint 3: 工作流 UI 优化
|
||||
- [ ] 审批流可视化编辑器 (拖拽节点)
|
||||
- [ ] 审批流步骤预览
|
||||
- [ ] 工单时间线可视化
|
||||
- [ ] 条件分支 UI 配置
|
||||
- [ ] 审批转审 UI
|
||||
- [ ] 超时设置可视化
|
||||
|
||||
### Sprint 4: 数据脱敏
|
||||
- [ ] MaskingRule 模型激活 (已有 GORM 模型)
|
||||
- [ ] 脱敏规则 CRUD UI
|
||||
- [ ] 内置脱敏函数 (手机号/身份证/邮箱/银行卡)
|
||||
- [ ] 查询结果自动脱敏
|
||||
- [ ] 导出数据脱敏
|
||||
- [ ] 脱敏规则测试预览
|
||||
|
||||
### Sprint 5: 生产部署
|
||||
- [ ] PostgreSQL 生产元数据库支持 (替代 SQLite)
|
||||
- [ ] Docker 多阶段构建 (Go 二进制 + Nginx 前端)
|
||||
- [ ] docker-compose 生产配置
|
||||
- [ ] 数据库备份/恢复
|
||||
- [ ] 滚动更新策略
|
||||
- [ ] HTTPS/TLS 配置
|
||||
- [ ] 生产环境配置分离 (.env.production)
|
||||
|
||||
### Sprint 6: 监控与运维
|
||||
- [ ] Prometheus 指标集成 (Gin middleware)
|
||||
- [ ] Grafana 仪表盘模板
|
||||
- [ ] 健康检查增强 (DB/Redis/LDAP 连通性)
|
||||
- [ ] 慢查询监控面板
|
||||
- [ ] 系统告警规则
|
||||
- [ ] 日志聚合 (结构化 JSON 日志)
|
||||
- [ ] 性能剖析 (pprof 集成)
|
||||
|
||||
---
|
||||
|
||||
## 远期规划 (P3)
|
||||
|
||||
- [ ] 多租户支持
|
||||
- [ ] 库级权限控制
|
||||
- [ ] Schema 漂移检测 + 可视化 (SchemaRevision 模型已有)
|
||||
- [ ] 慢查询实时推送 (WebSocket)
|
||||
- [ ] 查询审批工作流
|
||||
- [ ] 集群模式 (分布式会话)
|
||||
- [ ] 国际化 (i18n)
|
||||
|
||||
#### Sprint 3: 高级功能
|
||||
- [ ] Schema 漂移检测 + 可视化
|
||||
- [ ] 数据脱敏规则引擎
|
||||
- [ ] SQL 格式化后端服务
|
||||
- [ ] 慢查询 WebSocket 实时推送
|
||||
|
||||
#### Sprint 4: 生产就绪
|
||||
- [ ] PostgreSQL 生产数据库迁移
|
||||
- [ ] 集群部署 (Redis session + 负载均衡)
|
||||
- [ ] 监控告警 (Prometheus + Grafana)
|
||||
- [ ] 安全审计 (OWASP 检查)
|
||||
- [ ] 性能优化 (查询缓存/连接池)
|
||||
- [ ] CSV/Excel 导出功能
|
||||
- [ ] 移动端适配
|
||||
@@ -0,0 +1,61 @@
|
||||
import { useLogin } from '@/hooks/useLogin';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { Sun, Moon } from 'lucide-react';
|
||||
|
||||
export default function CenteredCard() {
|
||||
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
|
||||
const { toggleTheme, isDark } = useTheme();
|
||||
|
||||
return (
|
||||
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24, position: 'relative' }}>
|
||||
{/* Gradient background */}
|
||||
<div className="dg-gradient-br" style={{ position: 'fixed', inset: 0, zIndex: 0 }} />
|
||||
|
||||
{/* Theme toggle */}
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
style={{ position: 'fixed', top: 20, right: 20, zIndex: 50, background: 'rgba(255,255,255,0.1)', border: 'none', borderRadius: 8, padding: 8, cursor: 'pointer', color: '#fff', backdropFilter: 'blur(4px)' }}
|
||||
>
|
||||
{isDark ? <Sun size={18} /> : <Moon size={18} />}
|
||||
</button>
|
||||
|
||||
{/* Card */}
|
||||
<div style={{ position: 'relative', zIndex: 10, width: '100%', maxWidth: 400, background: 'var(--dg-bg-primary)', borderRadius: 14, padding: '40px 32px', boxShadow: '0 8px 32px rgba(0,0,0,0.3)' }}>
|
||||
<div style={{ textAlign: 'center', marginBottom: 32 }}>
|
||||
<h1 style={{ fontSize: 24, fontWeight: 700, marginBottom: 6 }}>
|
||||
<span className="dg-gradient-text">SQL Audit Platform</span>
|
||||
</h1>
|
||||
<p style={{ color: 'var(--dg-text-muted)', fontSize: 14 }}>Sign in to your account</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
{error && <div style={{ color: '#f87171', fontSize: 13, textAlign: 'center' }}>{error}</div>}
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
style={{ padding: '11px 14px', borderRadius: 8, background: 'var(--dg-bg-secondary)', border: '1px solid var(--dg-border)', color: 'var(--dg-text-primary)', fontSize: 14, outline: 'none' }}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
style={{ padding: '11px 14px', borderRadius: 8, background: 'var(--dg-bg-secondary)', border: '1px solid var(--dg-border)', color: 'var(--dg-text-primary)', fontSize: 14, outline: 'none' }}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading || !username || !password}
|
||||
className="dg-gradient-bg"
|
||||
style={{ padding: '11px 14px', borderRadius: 8, border: 'none', color: '#fff', fontWeight: 600, fontSize: 14, cursor: loading || !username || !password ? 'not-allowed' : 'pointer', opacity: loading || !username || !password ? 0.6 : 1, marginTop: 4 }}
|
||||
>
|
||||
{loading ? 'Signing in…' : 'Sign in'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
import { useLogin } from '@/hooks/useLogin';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { Sun, Moon } from 'lucide-react';
|
||||
|
||||
export default function FullImage() {
|
||||
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
|
||||
const { toggleTheme, isDark } = useTheme();
|
||||
|
||||
return (
|
||||
<div style={{ minHeight: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24, position: 'relative', overflow: 'hidden' }}>
|
||||
{/* Circuit board pattern background */}
|
||||
<div
|
||||
style={{
|
||||
position: 'fixed', inset: 0, zIndex: 0,
|
||||
background: `
|
||||
repeating-linear-gradient(0deg, transparent, transparent 48px, var(--dg-border) 48px, var(--dg-border) 49px),
|
||||
repeating-linear-gradient(90deg, transparent, transparent 48px, var(--dg-border) 48px, var(--dg-border) 49px),
|
||||
var(--dg-bg-primary)
|
||||
`,
|
||||
opacity: 0.5,
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Theme toggle */}
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
style={{ position: 'fixed', top: 20, right: 20, zIndex: 50, background: 'var(--dg-bg-tertiary)', border: 'none', borderRadius: 8, padding: 8, cursor: 'pointer', color: 'var(--dg-text-secondary)', backdropFilter: 'blur(8px)' }}
|
||||
>
|
||||
{isDark ? <Sun size={18} /> : <Moon size={18} />}
|
||||
</button>
|
||||
|
||||
{/* Glass card */}
|
||||
<div
|
||||
style={{
|
||||
position: 'relative', zIndex: 10, width: '100%', maxWidth: 400,
|
||||
background: 'rgba(0,0,0,0.45)', backdropFilter: 'blur(16px)',
|
||||
borderRadius: 16, padding: '40px 32px',
|
||||
color: '#fff',
|
||||
}}
|
||||
>
|
||||
<div style={{ textAlign: 'center', marginBottom: 32 }}>
|
||||
<h1 style={{ fontSize: 24, fontWeight: 700, marginBottom: 6 }}>
|
||||
<span className="dg-gradient-text">SQL Audit Platform</span>
|
||||
</h1>
|
||||
<p style={{ color: 'rgba(255,255,255,0.6)', fontSize: 14 }}>Sign in to your workspace</p>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: 16 }}>
|
||||
{error && <div style={{ color: '#f87171', fontSize: 13, textAlign: 'center' }}>{error}</div>}
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
style={{ padding: '11px 14px', borderRadius: 8, background: 'rgba(255,255,255,0.08)', border: '1px solid rgba(255,255,255,0.12)', color: '#fff', fontSize: 14, outline: 'none' }}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
style={{ padding: '11px 14px', borderRadius: 8, background: 'rgba(255,255,255,0.08)', border: '1px solid rgba(255,255,255,0.12)', color: '#fff', fontSize: 14, outline: 'none' }}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading || !username || !password}
|
||||
className="dg-gradient-bg"
|
||||
style={{ padding: '11px 14px', borderRadius: 8, border: 'none', color: '#fff', fontWeight: 600, fontSize: 14, cursor: loading || !username || !password ? 'not-allowed' : 'pointer', opacity: loading || !username || !password ? 0.6 : 1, marginTop: 4 }}
|
||||
>
|
||||
{loading ? 'Signing in…' : 'Sign in'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import { useLogin } from '@/hooks/useLogin';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { Sun, Moon, Database } from 'lucide-react';
|
||||
|
||||
export default function MinimalDark() {
|
||||
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
|
||||
const { toggleTheme, isDark } = useTheme();
|
||||
|
||||
return (
|
||||
<div style={{ minHeight: '100vh', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: 24, background: 'var(--dg-bg-primary)', color: 'var(--dg-text-primary)' }}>
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
style={{ position: 'fixed', top: 20, right: 20, zIndex: 50, background: 'var(--dg-bg-tertiary)', border: 'none', borderRadius: 8, padding: 8, cursor: 'pointer', color: 'var(--dg-text-secondary)' }}
|
||||
>
|
||||
{isDark ? <Sun size={18} /> : <Moon size={18} />}
|
||||
</button>
|
||||
|
||||
<div style={{ width: '100%', maxWidth: 320, textAlign: 'center' }}>
|
||||
<Database size={36} className="dg-gradient-text" style={{ marginBottom: 16, display: 'inline-block' }} />
|
||||
<h1 style={{ fontSize: 22, fontWeight: 700, marginBottom: 4 }}>
|
||||
<span className="dg-gradient-text">SQL Audit Platform</span>
|
||||
</h1>
|
||||
<p style={{ color: 'var(--dg-text-muted)', fontSize: 13, marginBottom: 36 }}>Secure database auditing & compliance</p>
|
||||
|
||||
<form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
|
||||
{error && <div style={{ color: '#f87171', fontSize: 13 }}>{error}</div>}
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
style={{ padding: '10px 14px', borderRadius: 8, background: 'var(--dg-bg-secondary)', border: 'none', color: 'var(--dg-text-primary)', fontSize: 14, outline: 'none' }}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
style={{ padding: '10px 14px', borderRadius: 8, background: 'var(--dg-bg-secondary)', border: 'none', color: 'var(--dg-text-primary)', fontSize: 14, outline: 'none' }}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading || !username || !password}
|
||||
className="dg-gradient-bg"
|
||||
style={{ padding: '10px 14px', borderRadius: 8, border: 'none', color: '#fff', fontWeight: 600, fontSize: 14, cursor: loading || !username || !password ? 'not-allowed' : 'pointer', opacity: loading || !username || !password ? 0.6 : 1, marginTop: 8 }}
|
||||
>
|
||||
{loading ? 'Signing in…' : 'Sign in'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import { useLogin } from '@/hooks/useLogin';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { Sun, Moon, Database } from 'lucide-react';
|
||||
|
||||
export default function SidePanel() {
|
||||
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
|
||||
const { toggleTheme, isDark } = useTheme();
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', minHeight: '100vh', background: 'var(--dg-bg-primary)', color: 'var(--dg-text-primary)' }}>
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
style={{ position: 'fixed', top: 20, right: 20, zIndex: 50, background: 'var(--dg-bg-tertiary)', border: 'none', borderRadius: 8, padding: 8, cursor: 'pointer', color: 'var(--dg-text-secondary)' }}
|
||||
>
|
||||
{isDark ? <Sun size={18} /> : <Moon size={18} />}
|
||||
</button>
|
||||
|
||||
{/* Left Panel — 40% */}
|
||||
<div
|
||||
className="dg-gradient-br"
|
||||
style={{ width: '40%', display: 'flex', flexDirection: 'column', justifyContent: 'center', alignItems: 'center', padding: 48, position: 'relative', overflow: 'hidden' }}
|
||||
>
|
||||
{/* Decorative circles */}
|
||||
<div style={{ position: 'absolute', top: '15%', left: '10%', width: 80, height: 80, borderRadius: '50%', background: 'rgba(255,255,255,0.06)' }} />
|
||||
<div style={{ position: 'absolute', bottom: '20%', right: '15%', width: 120, height: 120, borderRadius: '50%', background: 'rgba(255,255,255,0.04)' }} />
|
||||
<div style={{ position: 'absolute', top: '55%', left: '60%', width: 40, height: 40, borderRadius: '50%', background: 'rgba(255,255,255,0.07)' }} />
|
||||
<div style={{ position: 'absolute', top: '30%', right: '30%', width: 12, height: 12, borderRadius: '50%', background: 'rgba(255,255,255,0.12)' }} />
|
||||
<div style={{ position: 'absolute', bottom: '40%', left: '25%', width: 8, height: 8, borderRadius: '50%', background: 'rgba(255,255,255,0.10)' }} />
|
||||
|
||||
<div style={{ position: 'relative', zIndex: 2, textAlign: 'center', maxWidth: 320 }}>
|
||||
<Database size={40} style={{ color: '#fff', marginBottom: 20 }} />
|
||||
<h1 style={{ fontSize: 26, fontWeight: 700, color: '#fff', marginBottom: 12 }}>SQL Audit Platform</h1>
|
||||
<p style={{ color: 'rgba(255,255,255,0.7)', fontSize: 14, lineHeight: 1.6 }}>
|
||||
Empowering teams with intelligent SQL auditing, real-time compliance monitoring, and actionable insights for database security.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Panel — Login Form */}
|
||||
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 48 }}>
|
||||
<form onSubmit={handleLogin} style={{ width: '100%', maxWidth: 360, display: 'flex', flexDirection: 'column', gap: 20 }}>
|
||||
<h2 style={{ fontSize: 22, fontWeight: 600, marginBottom: 4 }}>Welcome back</h2>
|
||||
<p style={{ color: 'var(--dg-text-muted)', fontSize: 14, marginBottom: 8 }}>Sign in to access your dashboard</p>
|
||||
|
||||
{error && <div style={{ color: '#f87171', fontSize: 13 }}>{error}</div>}
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
style={{ padding: '10px 14px', borderRadius: 8, background: 'var(--dg-bg-tertiary)', border: '1px solid var(--dg-border)', color: 'var(--dg-text-primary)', fontSize: 14, outline: 'none' }}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
style={{ padding: '10px 14px', borderRadius: 8, background: 'var(--dg-bg-tertiary)', border: '1px solid var(--dg-border)', color: 'var(--dg-text-primary)', fontSize: 14, outline: 'none' }}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading || !username || !password}
|
||||
className="dg-gradient-bg"
|
||||
style={{ padding: '10px 14px', borderRadius: 8, border: 'none', color: '#fff', fontWeight: 600, fontSize: 14, cursor: loading || !username || !password ? 'not-allowed' : 'pointer', opacity: loading || !username || !password ? 0.6 : 1 }}
|
||||
>
|
||||
{loading ? 'Signing in…' : 'Sign in'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import { useLogin } from '@/hooks/useLogin';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { Sun, Moon, Database, Code, GitBranch, Bell, History, UserCog } from 'lucide-react';
|
||||
|
||||
const features = [
|
||||
{ icon: Database, label: 'SQL Auditing', desc: 'Real-time query analysis' },
|
||||
{ icon: Code, label: 'Smart Rules', desc: 'Custom compliance rules' },
|
||||
{ icon: GitBranch, label: 'Version Control', desc: 'Track schema changes' },
|
||||
{ icon: Bell, label: 'Alerts', desc: 'Instant notifications' },
|
||||
{ icon: History, label: 'History', desc: 'Full audit trail' },
|
||||
{ icon: UserCog, label: 'Access Control', desc: 'Role-based permissions' },
|
||||
];
|
||||
|
||||
export default function SplitView() {
|
||||
const { username, password, loading, error, setUsername, setPassword, handleLogin } = useLogin();
|
||||
const { theme, toggleTheme, isDark } = useTheme();
|
||||
|
||||
return (
|
||||
<div style={{ display: 'flex', minHeight: '100vh', background: 'var(--dg-bg-primary)', color: 'var(--dg-text-primary)' }}>
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
style={{ position: 'fixed', top: 20, right: 20, zIndex: 50, background: 'var(--dg-bg-tertiary)', border: 'none', borderRadius: 8, padding: 8, cursor: 'pointer', color: 'var(--dg-text-secondary)' }}
|
||||
>
|
||||
{isDark ? <Sun size={18} /> : <Moon size={18} />}
|
||||
</button>
|
||||
|
||||
{/* Left Panel */}
|
||||
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'center', padding: '48px 40px', background: 'var(--dg-bg-secondary)' }}>
|
||||
<div style={{ maxWidth: 440 }}>
|
||||
<h1 style={{ fontSize: 28, fontWeight: 700, marginBottom: 8 }}>
|
||||
<span className="dg-gradient-text">SQL Audit Platform</span>
|
||||
</h1>
|
||||
<p style={{ color: 'var(--dg-text-muted)', fontSize: 15, marginBottom: 36, lineHeight: 1.5 }}>
|
||||
Enterprise-grade SQL auditing and compliance management.
|
||||
</p>
|
||||
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 16 }}>
|
||||
{features.map((f) => (
|
||||
<div key={f.label} style={{ padding: 16, borderRadius: 10, background: 'var(--dg-bg-tertiary)' }}>
|
||||
<f.icon size={20} className="dg-gradient-text" />
|
||||
<div style={{ fontWeight: 600, fontSize: 13, marginTop: 8 }}>{f.label}</div>
|
||||
<div style={{ fontSize: 12, color: 'var(--dg-text-muted)', marginTop: 2 }}>{f.desc}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Panel — Login Form */}
|
||||
<div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 48 }}>
|
||||
<form
|
||||
onSubmit={handleLogin}
|
||||
style={{ width: '100%', maxWidth: 360, display: 'flex', flexDirection: 'column', gap: 20 }}
|
||||
>
|
||||
<h2 style={{ fontSize: 22, fontWeight: 600, marginBottom: 4 }}>Sign in</h2>
|
||||
<p style={{ color: 'var(--dg-text-muted)', fontSize: 14, marginBottom: 8 }}>Enter your credentials to continue</p>
|
||||
|
||||
{error && <div style={{ color: '#f87171', fontSize: 13 }}>{error}</div>}
|
||||
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Username"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
style={{ padding: '10px 14px', borderRadius: 8, background: 'var(--dg-bg-tertiary)', border: '1px solid var(--dg-border)', color: 'var(--dg-text-primary)', fontSize: 14, outline: 'none' }}
|
||||
/>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
style={{ padding: '10px 14px', borderRadius: 8, background: 'var(--dg-bg-tertiary)', border: '1px solid var(--dg-border)', color: 'var(--dg-text-primary)', fontSize: 14, outline: 'none' }}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading || !username || !password}
|
||||
className="dg-gradient-bg"
|
||||
style={{ padding: '10px 14px', borderRadius: 8, border: 'none', color: '#fff', fontWeight: 600, fontSize: 14, cursor: loading || !username || !password ? 'not-allowed' : 'pointer', opacity: loading || !username || !password ? 0.6 : 1 }}
|
||||
>
|
||||
{loading ? 'Signing in…' : 'Sign in'}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
|
||||
export interface LoginState {
|
||||
username: string;
|
||||
password: string;
|
||||
loading: boolean;
|
||||
error: string;
|
||||
setUsername: (v: string) => void;
|
||||
setPassword: (v: string) => void;
|
||||
handleLogin: (e: React.FormEvent) => Promise<void>;
|
||||
}
|
||||
|
||||
export function useLogin(): LoginState {
|
||||
const navigate = useNavigate();
|
||||
const { login } = useAuth();
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const handleLogin = useCallback(async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!username || !password) return;
|
||||
setLoading(true);
|
||||
setError('');
|
||||
const ok = await login(username, password);
|
||||
setLoading(false);
|
||||
if (ok) {
|
||||
navigate('/dashboard');
|
||||
} else {
|
||||
setError('用户名或密码错误');
|
||||
}
|
||||
}, [username, password, login, navigate]);
|
||||
|
||||
return { username, password, loading, error, setUsername, setPassword, handleLogin };
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { LOGIN_STYLE_OPTIONS, type LoginStyle } from '@/pages/LoginPage';
|
||||
import { Sun, Moon, Monitor, Check } from 'lucide-react';
|
||||
|
||||
export default function AppearanceSettings() {
|
||||
const { theme, setTheme, isDark } = useTheme();
|
||||
const [loginStyle, setLoginStyle] = useState<LoginStyle>('split');
|
||||
|
||||
useEffect(() => {
|
||||
const stored = localStorage.getItem('login-style') as LoginStyle | null;
|
||||
if (stored) setLoginStyle(stored);
|
||||
}, []);
|
||||
|
||||
const handleThemeChange = (t: 'dark' | 'light') => {
|
||||
setTheme(t);
|
||||
};
|
||||
|
||||
const handleLoginStyleChange = (id: LoginStyle) => {
|
||||
setLoginStyle(id);
|
||||
localStorage.setItem('login-style', id);
|
||||
};
|
||||
|
||||
const themeOptions = [
|
||||
{ id: 'dark' as const, icon: Moon, label: '深色模式' },
|
||||
{ id: 'light' as const, icon: Sun, label: '浅色模式' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
{/* 主题模式 */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold mb-3">主题模式</h3>
|
||||
<div className="flex gap-3">
|
||||
{themeOptions.map(opt => (
|
||||
<button
|
||||
key={opt.id}
|
||||
onClick={() => handleThemeChange(opt.id)}
|
||||
className={`flex items-center gap-2 px-5 py-3 rounded-xl text-sm font-medium transition-all ${
|
||||
theme === opt.id
|
||||
? 'bg-blue-500/15 text-blue-400 ring-1 ring-blue-500/30'
|
||||
: 'bg-[var(--dg-bg-tertiary)] text-[var(--dg-text-secondary)] hover:bg-[var(--dg-bg-hover)]'
|
||||
}`}
|
||||
>
|
||||
<opt.icon className="w-4 h-4" />
|
||||
{opt.label}
|
||||
{theme === opt.id && <Check className="w-3.5 h-3.5 ml-1" />}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 登录页风格 */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold mb-1">登录页风格</h3>
|
||||
<p className="text-xs text-[var(--dg-text-muted)] mb-4">选择登录页面的视觉风格,退出登录后生效</p>
|
||||
<div className="grid grid-cols-5 gap-3">
|
||||
{LOGIN_STYLE_OPTIONS.map(opt => (
|
||||
<button
|
||||
key={opt.id}
|
||||
onClick={() => handleLoginStyleChange(opt.id)}
|
||||
className={`group relative rounded-xl p-4 text-left transition-all ${
|
||||
loginStyle === opt.id
|
||||
? 'bg-blue-500/10 ring-1 ring-blue-500/30'
|
||||
: 'bg-[var(--dg-bg-tertiary)] hover:bg-[var(--dg-bg-hover)]'
|
||||
}`}
|
||||
>
|
||||
{/* 预览缩略图 */}
|
||||
<div className={`w-full aspect-[4/3] rounded-lg mb-3 overflow-hidden ${
|
||||
opt.id === 'split' ? 'flex' :
|
||||
opt.id === 'centered' ? 'flex items-center justify-center dg-gradient-br' :
|
||||
opt.id === 'sidepanel' ? 'flex' :
|
||||
opt.id === 'fullimage' ? 'flex items-center justify-center' :
|
||||
'flex items-center justify-center bg-[#0a0a0a]'
|
||||
}`}>
|
||||
{opt.id === 'split' && (
|
||||
<>
|
||||
<div className="flex-1 bg-[var(--dg-bg-primary)] p-1.5">
|
||||
<div className="w-3 h-1 rounded-sm bg-blue-500/30 mb-1" />
|
||||
<div className="grid grid-cols-2 gap-0.5">
|
||||
{[0,1,2,3].map(i => <div key={i} className="h-1.5 rounded-sm bg-[var(--dg-bg-tertiary)]" />)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-2/5 bg-[var(--dg-bg-secondary)] p-1.5 flex flex-col gap-0.5 justify-center">
|
||||
<div className="h-1 rounded-sm bg-[var(--dg-bg-tertiary)]" />
|
||||
<div className="h-1 rounded-sm bg-[var(--dg-bg-tertiary)]" />
|
||||
<div className="h-1.5 rounded-sm dg-gradient-bg opacity-60" />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{opt.id === 'centered' && (
|
||||
<div className="w-3/4 bg-white/10 backdrop-blur rounded-md p-1.5 flex flex-col gap-0.5 items-center">
|
||||
<div className="w-4 h-1 rounded-sm bg-white/40" />
|
||||
<div className="w-6 h-1 rounded-sm bg-white/20" />
|
||||
<div className="w-full h-1 rounded-sm bg-white/15" />
|
||||
<div className="w-full h-1 rounded-sm bg-white/15" />
|
||||
<div className="w-full h-1.5 rounded-sm bg-white/30" />
|
||||
</div>
|
||||
)}
|
||||
{opt.id === 'sidepanel' && (
|
||||
<>
|
||||
<div className="w-2/5 dg-gradient-br p-1.5 flex flex-col justify-center items-center gap-0.5">
|
||||
<div className="w-3 h-3 rounded-full bg-white/20" />
|
||||
<div className="w-4 h-0.5 rounded-sm bg-white/15" />
|
||||
</div>
|
||||
<div className="flex-1 bg-[var(--dg-bg-primary)] p-1.5 flex flex-col gap-0.5 justify-center">
|
||||
<div className="h-1 rounded-sm bg-[var(--dg-bg-tertiary)]" />
|
||||
<div className="h-1 rounded-sm bg-[var(--dg-bg-tertiary)]" />
|
||||
<div className="h-1.5 rounded-sm dg-gradient-bg opacity-60" />
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
{opt.id === 'fullimage' && (
|
||||
<div className="w-full h-full" style={{
|
||||
background: 'repeating-linear-gradient(0deg, transparent, transparent 8px, rgba(59,130,246,0.15) 8px, rgba(59,130,246,0.15) 9px), repeating-linear-gradient(90deg, transparent, transparent 8px, rgba(59,130,246,0.15) 8px, rgba(59,130,246,0.15) 9px)',
|
||||
}}>
|
||||
<div className="w-3/4 mx-auto mt-3 bg-black/40 backdrop-blur rounded-md p-1.5 flex flex-col gap-0.5 items-center">
|
||||
<div className="w-3 h-0.5 rounded-sm bg-white/30" />
|
||||
<div className="w-full h-0.5 rounded-sm bg-white/10" />
|
||||
<div className="w-full h-0.5 rounded-sm bg-white/10" />
|
||||
<div className="w-full h-1 rounded-sm bg-blue-500/40" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{opt.id === 'minimal' && (
|
||||
<div className="w-full h-full flex flex-col items-center justify-center gap-1 p-2">
|
||||
<div className="w-4 h-1 rounded-sm bg-white/20" />
|
||||
<div className="w-6 h-0.5 rounded-sm bg-white/10" />
|
||||
<div className="w-full h-1 rounded-sm bg-white/8" />
|
||||
<div className="w-full h-1 rounded-sm bg-white/8" />
|
||||
<div className="w-full h-1.5 rounded-sm bg-white/15" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 选中标记 */}
|
||||
{loginStyle === opt.id && (
|
||||
<div className="absolute top-2 right-2 w-5 h-5 rounded-full bg-blue-500 flex items-center justify-center">
|
||||
<Check className="w-3 h-3 text-white" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
<h4 className="text-sm font-medium mb-0.5">{opt.name}</h4>
|
||||
<p className="text-xs text-[var(--dg-text-muted)]">{opt.desc}</p>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,134 +1,36 @@
|
||||
import { useState } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Shield, Database, Code, GitBranch, Bell, History, UserCog, Sparkles, Sun, Moon } from 'lucide-react';
|
||||
import { useTheme } from '@/hooks/useTheme';
|
||||
import { useAuth } from '@/hooks/useAuth';
|
||||
import SplitView from '@/components/login/SplitView';
|
||||
import CenteredCard from '@/components/login/CenteredCard';
|
||||
import SidePanel from '@/components/login/SidePanel';
|
||||
import FullImage from '@/components/login/FullImage';
|
||||
import MinimalDark from '@/components/login/MinimalDark';
|
||||
|
||||
export type LoginStyle = 'split' | 'centered' | 'sidepanel' | 'fullimage' | 'minimal';
|
||||
|
||||
const LOGIN_STYLES: Record<LoginStyle, React.ComponentType> = {
|
||||
split: SplitView,
|
||||
centered: CenteredCard,
|
||||
sidepanel: SidePanel,
|
||||
fullimage: FullImage,
|
||||
minimal: MinimalDark,
|
||||
};
|
||||
|
||||
export const LOGIN_STYLE_OPTIONS: { id: LoginStyle; name: string; desc: string }[] = [
|
||||
{ id: 'split', name: '经典分屏', desc: '左右分屏布局,功能特性展示' },
|
||||
{ id: 'centered', name: '居中卡片', desc: '渐变背景,居中登录卡片' },
|
||||
{ id: 'sidepanel', name: '侧边面板', desc: '左侧品牌面板 + 右侧表单' },
|
||||
{ id: 'fullimage', name: '全屏背景', desc: '科技背景,毛玻璃卡片' },
|
||||
{ id: 'minimal', name: '极简暗色', desc: '极致简约,纯暗色风格' },
|
||||
];
|
||||
|
||||
function getLoginStyle(): LoginStyle {
|
||||
if (typeof window === 'undefined') return 'split';
|
||||
const stored = localStorage.getItem('login-style') as LoginStyle | null;
|
||||
if (stored && stored in LOGIN_STYLES) return stored;
|
||||
return 'split';
|
||||
}
|
||||
|
||||
export default function LoginPage() {
|
||||
const navigate = useNavigate();
|
||||
const { theme, toggleTheme } = useTheme();
|
||||
const { login } = useAuth();
|
||||
const [username, setUsername] = useState('');
|
||||
const [password, setPassword] = useState('');
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
|
||||
const handleLogin = async (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
if (!username || !password) return;
|
||||
setLoading(true);
|
||||
setError('');
|
||||
const ok = await login(username, password);
|
||||
setLoading(false);
|
||||
if (ok) {
|
||||
navigate('/dashboard');
|
||||
} else {
|
||||
setError('用户名或密码错误');
|
||||
}
|
||||
};
|
||||
|
||||
const features = [
|
||||
{ icon: Database, title: '多数据库支持', desc: 'MySQL、PostgreSQL、Redis、MongoDB' },
|
||||
{ icon: Code, title: '智能 SQL 审核', desc: '语法检查、规则校验、影响分析、自动评分' },
|
||||
{ icon: GitBranch, title: '多级审批流程', desc: '会签、或签、条件审批灵活配置' },
|
||||
{ icon: Bell, title: '多渠道通知', desc: '飞书、钉钉、企微、TG 即时推送' },
|
||||
{ icon: History, title: '完整审计追踪', desc: '所有操作可追溯,支持 SQL 回滚' },
|
||||
{ icon: UserCog, title: '细粒度权限', desc: 'RBAC 角色权限,字段级/行级访问控制' },
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="min-h-screen flex bg-[var(--dg-bg-primary)] relative overflow-hidden">
|
||||
{/* 背景装饰 */}
|
||||
<div className="absolute inset-0 pointer-events-none">
|
||||
<div className="absolute top-0 left-0 w-full h-full bg-[radial-gradient(ellipse_at_20%_20%,rgba(88,166,255,0.08)_0%,transparent_50%),radial-gradient(ellipse_at_80%_80%,rgba(137,87,229,0.08)_0%,transparent_50%)]" />
|
||||
</div>
|
||||
{/* 主题切换 */}
|
||||
<button
|
||||
onClick={toggleTheme}
|
||||
className="absolute top-5 right-5 z-20 w-10 h-10 rounded-xl bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] flex items-center justify-center text-[var(--dg-text-secondary)] hover:text-[var(--dg-text-primary)] hover:bg-[var(--dg-bg-hover)] transition-colors"
|
||||
title={theme === 'dark' ? '切换到浅色模式' : '切换到深色模式'}
|
||||
>
|
||||
{theme === 'dark' ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />}
|
||||
</button>
|
||||
|
||||
{/* 左侧 */}
|
||||
<div className="flex-1 flex flex-col justify-center px-16 relative z-10">
|
||||
<div className="mb-10">
|
||||
<div className="flex items-center gap-3 mb-6">
|
||||
<div className="w-12 h-12 rounded-xl dg-gradient-br flex items-center justify-center">
|
||||
<Shield className="w-7 h-7 text-white" />
|
||||
</div>
|
||||
<h1 className="text-5xl font-bold dg-gradient-text">
|
||||
SQL-Platform
|
||||
</h1>
|
||||
</div>
|
||||
<p className="text-lg text-[var(--dg-text-secondary)] leading-relaxed">
|
||||
企业级数据审核与变更管理平台<br />
|
||||
支持多数据库类型,提供完善的 SQL 审核、审批流程和审计追踪
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
{features.map((f, i) => (
|
||||
<div key={i} className="flex items-start gap-3 p-4 rounded-xl bg-[var(--dg-bg-secondary)]/60 border border-[var(--dg-border)] hover:border-blue-500/50 hover:bg-[var(--dg-bg-secondary)] transition-all group">
|
||||
<f.icon className="w-6 h-6 text-blue-400 mt-0.5 flex-shrink-0 group-hover:scale-110 transition-transform" />
|
||||
<div>
|
||||
<h4 className="text-sm font-semibold mb-1">{f.title}</h4>
|
||||
<p className="text-xs text-[var(--dg-text-secondary)]">{f.desc}</p>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 右侧 */}
|
||||
<div className="w-[480px] flex flex-col justify-center px-16 bg-[var(--dg-bg-secondary)]/80 backdrop-blur-xl border-l border-[var(--dg-border)] relative z-10">
|
||||
<div className="w-full max-w-sm mx-auto">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Sparkles className="w-5 h-5 text-blue-400" />
|
||||
<h2 className="text-2xl font-bold">欢迎回来</h2>
|
||||
</div>
|
||||
<p className="text-sm text-[var(--dg-text-secondary)] mb-8">请登录您的账户以继续</p>
|
||||
|
||||
<form onSubmit={handleLogin} className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">用户名</label>
|
||||
<div className="relative">
|
||||
<UserCog className="absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--dg-text-muted)]" />
|
||||
<input
|
||||
className="w-full pl-10 pr-4 py-3 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400/20 transition-all"
|
||||
placeholder="请输入用户名"
|
||||
value={username}
|
||||
onChange={e => setUsername(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium mb-2">密码</label>
|
||||
<div className="relative">
|
||||
<Shield className="absolute left-3.5 top-1/2 -translate-y-1/2 w-4 h-4 text-[var(--dg-text-muted)]" />
|
||||
<input
|
||||
type="password"
|
||||
className="w-full pl-10 pr-4 py-3 bg-[var(--dg-bg-tertiary)] border border-[var(--dg-border)] rounded-lg text-sm focus:border-blue-400 focus:outline-none focus:ring-1 focus:ring-blue-400/20 transition-all"
|
||||
placeholder="请输入密码"
|
||||
value={password}
|
||||
onChange={e => setPassword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={loading || !username || !password}
|
||||
className="w-full py-3 dg-gradient-bg rounded-lg text-sm font-semibold text-white hover:opacity-90 disabled:opacity-50 disabled:cursor-not-allowed transition-all flex items-center justify-center gap-2"
|
||||
>
|
||||
{loading ? (
|
||||
<div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
||||
) : '登录'}
|
||||
</button>
|
||||
{error && <p className="text-xs text-red-400 text-center mt-2">{error}</p>}
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
const style = getLoginStyle();
|
||||
const Component = LOGIN_STYLES[style] || SplitView;
|
||||
return <Component />;
|
||||
}
|
||||
@@ -4,11 +4,12 @@ import AuditRulesPage from './AuditRulesPage';
|
||||
import AnnouncementPage from './AnnouncementPage';
|
||||
import NotificationPage from './NotificationPage';
|
||||
import LdapConfigPage from './LdapConfigPage';
|
||||
import AppearanceSettings from './AppearanceSettings';
|
||||
|
||||
const tabs = [
|
||||
{ id: 'appearance', label: '外观设置', icon: '🎨' },
|
||||
{ id: 'platform', label: '平台配置', icon: '⚙️' },
|
||||
{ id: 'rules', label: '审核规则', icon: '🛡️' },
|
||||
{ id: 'announcement', label: '公告管理', icon: '📢' },
|
||||
{ id: 'notification', label: '消息推送', icon: '🔔' },
|
||||
{ id: 'ldap', label: 'LDAP', icon: '🔐' },
|
||||
] as const;
|
||||
@@ -16,13 +17,13 @@ const tabs = [
|
||||
type TabId = typeof tabs[number]['id'];
|
||||
|
||||
export default function SettingsPage() {
|
||||
const [activeTab, setActiveTab] = useState<TabId>('platform');
|
||||
const [activeTab, setActiveTab] = useState<TabId>('appearance');
|
||||
|
||||
return (
|
||||
<div className="space-y-5">
|
||||
<div>
|
||||
<h1 className="text-xl font-semibold">系统设置</h1>
|
||||
<p className="text-sm text-[var(--dg-text-muted)] mt-1">管理平台配置、审核规则、消息推送和 LDAP 认证</p>
|
||||
<p className="text-sm text-[var(--dg-text-muted)] mt-1">管理平台外观、配置、审核规则、消息推送和 LDAP 认证</p>
|
||||
</div>
|
||||
|
||||
{/* Tab 导航 */}
|
||||
@@ -45,9 +46,9 @@ export default function SettingsPage() {
|
||||
|
||||
{/* 内容区 */}
|
||||
<div>
|
||||
{activeTab === 'appearance' && <AppearanceSettings />}
|
||||
{activeTab === 'platform' && <PlatformConfigPage />}
|
||||
{activeTab === 'rules' && <AuditRulesPage />}
|
||||
{activeTab === 'announcement' && <AnnouncementPage />}
|
||||
{activeTab === 'notification' && <NotificationPage />}
|
||||
{activeTab === 'ldap' && <LdapConfigPage />}
|
||||
</div>
|
||||
|
||||
Reference in new issue
Block a user