0c57e18f1919b29a56c6b39c20401b1852d887b5
=== 一、审计结论(用户问:gin 日志是否标准、会不会打爆磁盘)===
现状是标准的:gin.Default() 写 stdout,代码里没有任何
gin.DefaultWriter / log.SetOutput / lumberjack / os.OpenFile 写日志文件;
容器内 /var/log 是空的,find / -name "*.log" 无结果;
kubelet containerLogMaxSize=10Mi / containerLogMaxFiles=5 自动轮转,
实测 /var/log 只占 120G 磁盘的 0.8%。
但发现两个真问题:
1. 访问日志是 gin 默认文本格式,Loki 里没法按字段过滤
2. 审计日志/查询历史等只配了保留天数,**没有任何自动清理** ——
只有手动按钮,表会一直涨,SQLite 文件删了也不缩
=== 二、结构化 JSON 访问日志(middleware/accesslog.go)===
gin.Default() → gin.New() + AccessLog() + gin.Recovery() + CORSMiddleware
- 一行一个 JSON:ts/level/type/method/path/status/latency_ms/bytes/ip/ua
- 自动分级:5xx=error / 4xx=warn / 其余=info
- 鉴权后带 user_id / username,便于审计
- 健康检查与 /assets 静音(K8s 探针每 10s 一次,会淹没真实请求)
- Recovery 显式加回(gin.Default 里包含它,漏了 panic 会打挂进程)
⚠️ 关键细节:Go 的 map 序列化按 key 字母序,ts 会排到最后,
一行以 {"bytes":... 开头根本看不出时间。LogJSON 手工拼装,
把 ts/level/type/msg 固定在最前,其余排序输出。
=== 三、保留期自动清理(handler/cleanup.go)===
启动 2 分钟后跑一次,之后每 24h 一次;cleanup_auto_enabled=false 可关。
覆盖 8 张会无限增长的表(原来只管 4 张):
audit_logs / query_histories / slow_logs / tickets
+ operation_logs / workflow_execution_logs / scheduled_task_logs(新增)
+ refresh_tokens(按 expires_at < now,过期不清理会每次登录加一行)
设计取舍:
- 每批删 5000 行、批间 sleep 50ms:SQLite 单写者,一条 DELETE 全删会把库锁死
- 单表单轮最多 20 万行,剩下的留给下一轮
- deleted 后看 freelist/page_count,>25% 才 VACUUM(DELETE 不还磁盘,必须 VACUUM)
- 保留天数 <=0 视为不清理,避免误配把数据全删
手动接口同步改造:/settings/cleanup/{preview,run} 复用同一套逻辑,
返回结构改为 {deleted, total, vacuumed, freed_bytes, cost_ms}。
新增系统设置(seed 自动补,已实测 8 个 key 全部落库):
operation_log_retention_days=365 workflow_log_retention_days=180
scheduled_log_retention_days=180 cleanup_auto_enabled=true
=== 四、实测 ===
访问日志:{"ts":"2026-10-27T14:46:39.58Z","level":"info","type":"access","bytes":668,"ip":"...","latency_ms":78.6,"method":"POST","path":"/api/v1/auth/login","status":200,...}
health 日志行数: 0 ✅ [GIN] 文本行数: 0 ✅ 非 JSON 行: 4(仅启动)
cleanup preview: 8 张表的 count/days ✅
cleanup run: {"cost_ms":1,"deleted":{},"freed_bytes":0,"vacuumed":false,"total":0} ✅
镜像:lghost/opsxsql:latest + v0.1 = sha256:30593de3fc09
OpsxSQL — SQL 审核与数据变更审批平台
安全、高效的企业级数据库自助查询 + 数据变更审批平台
项目简介
OpsxSQL 是一个面向 DBA 和开发人员的数据库管理平台,核心定位:
- 自助查询 — 支持 MySQL / PostgreSQL / Redis / MongoDB 多类型数据库的在线查询
- 数据变更审批流 — SQL 变更工单提交 → 审核 → 执行的完整审批流程
- SQL 审核 — 内置 26+ 条审核规则,覆盖语法、性能、安全、命名规范等维度
- 实例管理 — 统一管理多种数据库实例,支持分组和标签
技术栈
| 层级 | 技术 |
|---|---|
| 后端 | Go 1.26 + Gin + GORM + SQLite |
| 前端 | React 19 + TypeScript + Vite + Tailwind CSS + shadcn/ui |
| 认证 | JWT + LDAP 集成 |
| 数据库 | SQLite(零依赖,开箱即用) |
功能模块
🗄️ 实例管理
- MySQL / PostgreSQL / Redis / MongoDB 实例注册与连接测试
- 实例分组管理
- 库表结构浏览(字段、索引详情)
📝 自助查询
- 在线 SQL 编辑器(CodeMirror)
- 语法高亮 + 自动补全
- 查询历史记录(上限 500 条/用户)
- 慢查询日志分析
✅ 工单审批
- SQL 变更工单创建(支持 DDL / DML)
- 多级审批流程(developer → dba → admin)
- 工单状态追踪与通知
- 执行结果确认
🔍 SQL 审核
- 26 条内置审核规则(MySQL 11 / PostgreSQL 7 / Redis 4 / MongoDB 4)
- 规则类型:syntax / performance / safety / security / naming / DDL / DML / DQL / DCL
- 规则启用/禁用 + 自定义配置
👥 用户与权限
- 四级角色:admin / dba / developer / auditor
- LDAP 集成登录
- 基于角色的数据隔离
🔔 通知渠道
- 飞书 Webhook 通知
- 钉钉 Webhook 通知
- 工单状态变更自动推送
📊 其他
- Dashboard 运营数据总览
- 审计日志
- 公告管理
- 系统设置
项目结构
sql-platform/
├── backend/ # Go 后端
│ ├── cmd/server/ # 入口 + seed
│ │ ├── main.go # 启动入口
│ │ └── seed.go # 默认数据初始化
│ ├── internal/
│ │ ├── handler/ # 请求处理器
│ │ ├── middleware/ # 中间件(认证/权限/日志)
│ │ ├── model/ # 数据模型
│ │ └── service/ # 业务逻辑
│ ├── config/ # 配置管理
│ └── router/ # 路由注册
├── frontend/ # React 前端
│ └── src/
│ ├── api/ # API 请求封装
│ ├── components/ # 通用组件
│ │ └── ui/ # shadcn/ui 组件
│ ├── pages/ # 页面组件(27 个)
│ └── lib/ # 工具函数
├── tests/ # 集成测试
├── run.sh # 启动/停止脚本
├── docker-compose.yml # Docker Compose 部署(单镜像)
├── Dockerfile # 单镜像多阶段构建(前端+后端+gh-ost)
└── kubernetes/ # K8s 部署(install.sh 一键 + 清单)
快速开始
前置要求
- Go 1.26+
- Node.js 18+
- npm 或 yarn
方式一:Docker Compose(推荐,单镜像前后端同源)
git clone https://gitee.com/opsx-tools/opsxsql.git
cd opsxsql
# 生成密钥(必填;SECRET_KEY 部署后不可更换,否则已加密的实例/LDAP 密码无法解密)
export SECRET_KEY=$(openssl rand -hex 32)
# 可选;不设则生产模式自动生成随机 JWT 密钥并持久化到数据卷
export JWT_SECRET=$(openssl rand -hex 32)
docker compose up -d --build
访问 http://localhost:9000(单镜像,Gin 同时托管前端静态资源与 API)。
自定义端口:OPSXSQL_PORT=18000 docker compose up -d。
安全基线:非 root 运行、no-new-privileges、cap_drop ALL、/health 健康检查、资源限额、日志轮转。
方式二:Kubernetes 一键部署
# 默认部署(镜像 opsxsql:latest)
bash kubernetes/install.sh
# 指定镜像 + 入口
IMAGE=registry.example.com/opsxsql:v1.0.0 bash kubernetes/install.sh --nodeport
IMAGE=registry.example.com/opsxsql:v1.1.0 bash kubernetes/install.sh --ingress opsxsql.example.com
install.sh 幂等:自动建命名空间/生成并复用密钥/应用 PVC+Deployment+Service,等待就绪后打印访问方式。
注意:SQLite 单写者,Deployment 固定 单副本 + Recreate(脚本已内置);详见 kubernetes/README.md。
方式三:本地开发
./run.sh start # 后端 9000 + 前端 5173
后端 http://localhost:9000,前端 dev server http://localhost:5173(vite 代理 /api)。
默认账号
首次启动自动初始化以下账号(密码均为 admin123):
| 用户名 | 角色 | 说明 |
|---|---|---|
| admin | admin | 超级管理员 |
| dba | dba | 数据库管理员 |
| auditor | auditor | 审计员(只读) |
| developer | developer | 开发者 |
测试
# 后端集成测试
cd tests
pip install pytest pytest-asyncio httpx
pytest -v
# 前端类型检查
cd frontend
npx tsc --noEmit
首次部署获取默认账号密码
首次启动会生成 16 位随机密码(不再固定 admin123),以下默认账号共用该密码:
| 账号 | 角色 | 说明 |
|---|---|---|
admin |
超级管理员 | 全权限 |
dba |
DBA管理员 | 数据库实例与变更 |
auditor |
审计员 | 审核工单 |
developer |
开发者 | 提交 SQL 工单 |
密码同时:
- 打印到启动日志(仅首次,日志轮转后就没了)
- 写入数据目录
initial-admin-password.txt(权限0600)
获取方式(推荐第 1 种)
# Docker Compose
docker compose exec <服务名> cat /app/data/initial-admin-password.txt
docker compose logs <服务名> | grep -A8 "已创建默认账号"
docker cp <容器名>:/app/data/initial-admin-password.txt ./
# Kubernetes
kubectl -n sql exec deploy/opsxsql -- cat /app/data/initial-admin-password.txt
kubectl -n sql logs deploy/opsxsql | grep -A8 "已创建默认账号"
kubectl -n sql cp <pod>:/app/data/initial-admin-password.txt ./
自定义密码(跳过随机生成)
# Docker Compose:部署前设置
export ADMIN_INIT_PASSWORD='your-strong-password'
docker compose up -d
# Kubernetes:在 deployment env 中加 ADMIN_INIT_PASSWORD(或用 Secret 注入)
说明
| 项 | 说明 |
|---|---|
| 幂等 | 已存在同名账号时不重建、不改密码;只有缺失的账号才创建 |
| 生成失败 | 随机密码生成失败时直接退出,不降级到公开默认口令 |
| 轮转 | 本平台暂无自助改密接口;改密请用管理员 POST /api/v1/users/:id/reset-password |
| 清理 | 确认所有账号改密后可删除 initial-admin-password.txt |
版本历史
| 版本 | 说明 |
|---|---|
| v0.4.0 | 弹框架构重构 + 审核规则 API + LDAP 配置 + 全量 seed |
| v0.3.2 | 通知渠道对接 + TicketCreatePage 重写 + 默认用户初始化 |
| v0.3.1 | Go 后端迁移 + 工单审批流程 + 实例管理 |
| v0.2.0 | 前端 UI 重构 + shadcn/ui 组件化 |
| v0.1.0 | 项目初始化 + 基础 CRUD |
代码统计
- 后端 Go:29 文件,8,000+ 行
- 前端 TypeScript:131 文件,27,000+ 行
- 集成测试:21 文件,1,100+ 行
- 总计 36,000+ 行
许可证
MIT License
Languages
TypeScript
72.5%
Go
23%
Shell
2.1%
Python
1.3%
CSS
0.5%
Other
0.6%