Commit Graph
16 Commits
Author SHA1 Message Date
opsx 0c57e18f19 feat(log): Gin 改结构化 JSON 访问日志 + 保留期自动清理(含 VACUUM 回收)
=== 一、审计结论(用户问: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
2026-10-27 14:49:45 +00:00
Logan.Li f6d636fa44 feat(query): sql workspace conveniences per opsxdb patterns
- generic ContextMenu component (viewport flip, esc/outside close)
- SchemaSidebar table right-click menu: preview data, generate
  SELECT/INSERT/UPDATE/DELETE templates with pk-aware WHERE, copy table
  name; double-click table runs preview SELECT; column right-click
  copy/insert; dialect-aware quoting; preview SELECT carries no LIMIT
- SqlEditor selection-first execution (Mod-Enter runs selection else
  statement at cursor via quote/comment-aware splitter); pagination
  replays last executed statement
- query history persisted server-side: drawer fetches query-histories
  with keyword/mine/favorites filters, star toggle (new PUT favorite +
  mine filter endpoints), insert and insert-and-run
- custom templates in localStorage alongside built-ins
- unit tests: genSql + statement splitter (28 tests green)
2026-10-26 18:53:02 +08:00
Logan.Li 16f1f7d65f feat(backend): unified response helpers, refresh token, security hardening
- new resp package (OK/Page/Fail/BadRequest/.../InternalErr): unified error
  envelope {detail, code?}, sanitized 500s (full error logged server-side,
  generic message to client - stops DB error leakage), pagination envelope
  {items,total,page,page_size}; migrate 200+ inline c.JSON error sites
- refresh token: 7d rotation tokens (SHA-256 hashed at rest), public
  POST /auth/refresh with single-use rotation, POST /auth/logout revokes
  all user refresh tokens (endpoint previously missing - frontend 404)
- security: gh-ost command line password masked in task status payload;
  Instance.MarshalJSON guarantees encrypted passwords never leave the API
  (removes reliance on per-handler manual masking); production (DEBUG=false)
  with default JWT secret now auto-generates and persists a random secret
- safe.Go recover wrapper for background goroutines (instance probe,
  gh-ost async)
- delete dead service layer (zero callers)
- fix pre-existing go vet unkeyed bson.E literals
2026-10-26 17:21:00 +08:00
logan 3a6dd99633 feat: 审核规则驱动SQL检查 + db_type过滤 + Redis只读拦截 + 配置文档
1. 审核规则改为从audit_rules表动态匹配(去硬编码)
   - CreateTicket提交时查audit_rules WHERE db_type=? AND is_enabled=true
   - 正则匹配SQL,value=reject拦截,warn/review放行
   - UPDATE/DELETE无WHERE: Go RE2不支持(?!...)负向前瞻
     → 用value=where_check特殊标记+strings.Contains WHERE检测
   - 修复UPDATE/DELETE无WHERE正则(Go兼容)

2. ListAuditRules按db_type过滤
   - ghost.go: 添加c.Query('db_type') WHERE过滤
   - MySQL不显示KEYS规则,Redis不显示DROP规则

3. Redis只读命令拦截(工单提交)
   - 新增Redis只读命令白名单: GET/HGET/MGET/HGETALL/KEYS/SCAN等28个
   - 只读命令走工单→直接拦截,引导用户用查询功能

4. 补充audit_rules规则
   - Redis新增7条: DEBUG/SHUTDOWN/SCRIPT/SAVE/BGSAVE/BGREWRITEAOF/SLAVEOF/CLUSTER
   - MySQL新增1条: DROP SCHEMA
   - 总计34条规则(MySQL12/Redis11/PG7/MongoDB4)

5. docs/CONFIGURATION.md 配置系统文档
   - 三套配置体系: system_settings/audit_rules/ldap_configs
   - 每项配置: key/默认值/作用/代码引用位置
   - 未引用配置标注: executor_batch_size/executor_backup/query_max_rows_examined(待实现)
   - LDAP冗余配置: system_settings中7项ldap_*无代码引用(LDAP用独立表)

验证: 5/5审批流通过, 36/36安全测试通过, 27/27审核规则匹配正确
2026-06-03 03:17:22 +08:00
logan b2b689f791 feat: 工单执行集成gh-ost通道 + Dockerfile安装gh-ost + 密码解密修复
- ExecuteTicket: 检测 ghost_enabled + ALTER TABLE → 走 gh-ost 同步执行
- parseAlterTable: 解析 ALTER TABLE 提取表名和 alter 子句(支持反引号/db.table)
- buildGhOstTask: 修复密码传递(加密→解密后传给 gh-ost 命令行)
- Dockerfile: 运行阶段下载 gh-ost v1.1.6 Linux amd64 二进制
- ghost_enabled=false 默认关闭,工单流程正常走普通通道
- 端到端验证: ALTER TABLE+ghost_enabled=true → gh-ost; INSERT/关闭 → 普通
2026-06-01 01:47:08 +08:00
logan bc2092a980 feat: 平台配置全面生效 — 配置策略接入实际逻辑
- query组: ExecuteQuery读取query_limit_max/query_timeout/query_dangerous_pattern
  - 查询超时通过context.WithTimeout生效,db.QueryContext/QueryRowContext
  - 危险模式检测拦截无WHERE的DELETE/UPDATE/DROP/TRUNCATE
  - LIMIT上限从settings读取,不再硬编码1000

- security组: Login读取login_max_attempts/login_lockout_minutes
  - 新增User字段: failed_login_attempts + locked_until
  - 登录失败达到阈值自动锁定,成功时清除计数
  - JWT Token过期时间读取session_timeout_hours设置

- executor组: ExecuteTicket读取executor_timeout/executor_dry_run
  - 执行超时通过context.WithTimeout控制
  - 开启dry_run时先EXPLAIN预检查SQL语法
  - db.ExecContext替代db.Exec

- cleanup组: SettingsCleanupPreview/Run从占位改为真实实现
  - 预览统计4类数据(工单/查询历史/慢日志/审计日志)待清理数量
  - 执行清理按retention_days配置删除过期数据

- ghost组: 13项gh-ost配置加入seedDefaultSettings
  - enabled/path/mode/osc_priority/env/cloud/trigger/chunk/load/lag等
  - 已被ghost.go中getGhostConfig等函数读取

- 前端: PlatformConfigPage新增Ghost/OSC分组显示
2026-06-01 01:00:23 +08:00
logan 889cce140b refactor: 弹框架构重构 + 审核规则seed API + LDAP默认配置 + 全量初始数据
弹框架构:
- Layout header/sidebar z-[200] 隔离弹框层,弹框不再遮挡面包屑
- dialog.tsx 简化: 去掉 border/backdrop-blur,统一暗色主题变量
- ModalOverlay z-[100] 对齐 Radix Dialog overlay 层级
- EditInstanceDialog 同步 z-index 修复
- 修复 InstanceGroupPage/InstancePage/LdapConfigPage 缺失 import
- 修复 InstanceGroupPage 缺失 X icon import
- RedisDetailPage ModalOverlay 迁移为 FormDialog

审核规则:
- POST /audit-rules/seed/:dbType 初始化默认规则 (mysql 11/pg 7/redis 4/mongo 4)
- POST /audit-rules/:id/toggle 开关规则
- PUT /audit-rules/:id 编辑规则
- seed.go 启动时自动创建 26 条默认审核规则

LDAP 默认配置:
- seed.go 自动写入 9 条 LDAP 配置 (8.217.205.92:389, dc=opsx,dc=vip)
- 全新 DB 启动即就位,无需手动配置
2026-05-29 22:48:45 +08:00
logan 557d88d3ed feat: 数据脱敏体系重构 — 全局设置+masked_columns+前端去硬编码
后端:
- applyDataMasking 新增 masked_columns 返回字段,告诉前端哪些列被脱敏
- seedDefaultSettings 添加 data_masking 分组(启用开关+敏感字段列表)
- 脱敏逻辑: 全局敏感字段 + 实例级 mask_fields 合并,开关关闭则跳过

前端:
- QueriesPage 去掉硬编码 sensitiveKeywords 和 maskData()
- 改用后端返回的 masked_columns 显示'敏'标签
- 脱敏值由后端统一处理(替换为*)
- PlatformConfigPage 数据脱敏分组独立显示
- EditInstanceDialog 脱敏字段简化为逗号分隔输入

验证:
- 设置页开关可正常开关/保存
- 查询 ghost_test: email列被脱敏为*,列头显示'敏'标签
- 非敏感列(name, id, status)正常显示
2026-05-28 23:26:10 +08:00
logan 78986fe5bf feat: Native DDL智能推荐 + MySQL版本检测 + 云数据库自动跳过gh-ost
- 新增 getMySQLVersion(): 检测MySQL实例版本
- 新增 supportsInstantDDL(): 判断是否支持 INSTANT DDL (8.0.12+)
- analyze API新增字段: mysql_version, supports_instant, cloud_type
- 云数据库(aliyun_rds/polardb/aws_aurora): 自动推荐Native DDL,跳过gh-ost
- 自建MySQL: 根据版本+环境+行数综合推荐
- MySQL 8.4.9 检测通过, supports_instant=true
2026-05-27 00:24:59 +08:00
logan b2b13661fd feat: gh-ost 完整策略引擎 — 13项配置+环境差异+云数据库适配+DDL智能分析
配置项(13项):
- ghost_enabled: 全局开关(默认关闭)
- ghost_default_mode: 默认执行模式(dry_run/execute)
- ghost_osc_priority: OSC优先级(native_first/ghost_first/ghost_only)
- ghost_env_mode: 环境模式(development/production)
- ghost_cloud_type: 云数据库类型(self_hosted/aliyun_rds/polardb/aws_aurora)
- ghost_auto_trigger_rows: 自动触发行数阈值(万)
- ghost_auto_trigger_ddl: 自动触发DDL类型
- ghost_path/chunk_size/max_load/critical_load/max_lag_millis/allow_on_master

新增API:
- GET /ghost/config: 查看全部gh-ost配置
- POST /ghost/analyze: DDL智能分析(解析DDL类型+行数检查+策略推荐)
- 云数据库: --aliyun-rds / --aws 自动适配

策略逻辑:
- native_first + development → 推荐Native DDL
- native_first + production + DDL匹配 → 推荐gh-ost
- ghost_first/ghost_only → 强制gh-ost
- 行数超阈值 → 额外推荐gh-ost
2026-05-27 00:22:33 +08:00
logan 5d59ca427e feat: gh-ost 平台配置开关键 + 云数据库参数支持
- ghost_enabled: 总开关, 关闭时返回403禁止dry-run/execute
- ghost_path: gh-ost二进制路径, 从system_settings读取
- ghost_default_chunk_size: 每批拷贝行数
- ghost_max_load: 负载阈值限流
- ghost_allow_on_master: 单机MySQL允许直接执行
- handler改为从DB读配置, 不再依赖config包
- 5个配置项已插入system_settings(id=26-30)
2026-05-27 00:15:32 +08:00
logan 8aaf2271b8 feat: gh-ost真实集成验证通过 + 登录页全面中文化 + 外观设置跨浏览器同步修复
gh-ost集成:
- handler根据instance_id查DB获取连接信息(host/port/user/password)
- 修复gh-ost参数: 去掉不存在的--dry-run(默认noop), 加--execute才真执行
- 加--skip-port-validation(端口映射环境必须)
- 加--allow-on-master(单机MySQL直接执行)
- 加--initially-drop-ghost-table/old-table/socket-file(清理残留)
- GhOstTask增加Port字段, buildArgs同步更新
- dry-run真实验证通过: 连接1.94.55.133:9005, 3行表, 3.85s完成

登录页中文化:
- 5个登录组件(SplitView/CenteredCard/SidePanel/FullImage/MinimalDark)
- 46处英文文案全部替换为中文
- 平台标题统一为'SQL审核平台'

外观设置同步修复:
- GetPublicLoginStyle: GORM First()复用主键导致第二次查询失败
- 改用Take()+重置结构体, 修复theme始终返回dark的bug
- UpdateUserPreferences: 同步写入SystemSetting表(login_style+default_theme)
2026-05-27 00:06:13 +08:00
logan b59eea4391 fix: ListInstanceGroups 返回 enriched 数据含 instance IDs
- ListInstanceGroups 改用 enrichGroup 返回完整数据
- 包含 readonly_instance_ids/write_instance_ids 供前端穿梭框回显
2026-05-25 23:50:29 +08:00
logan 88e5f4e7ee feat: Go后端LDAP真实对接 — AES-256-GCM密码加解密 + 登录LDAP fallback
- 新增 crypto.go: AES-256-GCM 加解密,与 Python backend 兼容
- auth.go: Login 添加 LDAP fallback(本地优先→LDAP绑定→自动创建本地用户)
- ldap.go: 实现真实 LDAP 连接/用户搜索/密码验证/OU列表/用户列表
- 所有 LDAP 操作解密 admin_password 后使用
- 新增/更新密码存储时加密
- router.go: 补充 delete-user 路由
- 添加 go-ldap/v3 依赖
2026-05-25 23:32:15 +08:00
logan 622923821b feat: Go后端补全所有缺失API接口,消除前端404报错
新增接口:
- GET /instances/accessible — 查询页实例列表(按access_type筛选)
- GET /instances/:id/databases — 数据库列表(placeholder)
- GET /instances/:id/databases/:db/tables — 表列表(placeholder)
- POST /instances/test-connection — 新实例连接测试
- CRUD /instance-groups — 完整实例组管理(创建/更新/删除/查询单个)
- /instance-groups/:id/instances — 实例分配/移除
- /instance-groups/:id/users — 用户分配
- PUT /instance-groups/users/:userId/groups — 用户组更新
- GET/PUT /ldap-config — LDAP配置读写
- POST /ldap-config/test — LDAP连接测试(placeholder)
- POST /ldap-config/ou-list — OU列表(placeholder)
- POST /ldap-config/user-list — 用户列表(placeholder)
- PUT /settings — 批量更新设置
- GET /settings/cleanup/preview — 清理预览
- POST /settings/cleanup/run — 执行清理

修复:
- ListSettings 返回按group分组的dict(前端期望格式)
- ListInstanceGroups 返回enriched数据(含实例/用户计数)
- 前端所有页面0个404错误,查询页实例下拉框正常显示
2026-05-25 22:14:01 +08:00
logan ca308e277d feat: v0.3.1 — Go/Gin 后端重写 + gh-ost 集成
架构变更:
- Python/FastAPI → Go/Gin 重写(Python 后端保留在 backend-python/)
- GORM + SQLite(兼容现有数据库表结构)
- JWT 认证 + RBAC 权限中间件

Go 后端结构:
- cmd/server/main.go — 入口,端口 9000
- config/config.go — 环境变量配置
- internal/model/ — GORM 模型(30张表,完全兼容 SQLite)
- internal/handler/ — auth/instance/ticket/dashboard/ghost handler
- internal/middleware/ — JWT 认证 + CORS
- internal/ghost/ — gh-ost 集成模块(同步/异步执行,任务管理)
- router/router.go — API 路由注册(/api/v1/...)

gh-ost 集成:
- RunGhOst() 同步执行
- RunGhOstAsync() 异步执行 + 进度追踪
- TaskManager 管理多个并发任务
- API: POST /ghost/dry-run, /ghost/execute, GET /ghost/status/:taskId

核心 API 已对齐:
- POST /auth/login (JWT)
- GET /dashboard/stats, /dashboard/my-tickets
- GET /tickets/statistics, CRUD + approve/execute
- GET/POST/PUT/DELETE /instances
- GET /users, /audit-rules, /announcements, /settings

已验证:
- 登录 admin/admin123 ✅
- Dashboard 99工单/110查询/4实例 ✅
- 工单统计按角色过滤 ✅
2026-05-25 21:40:54 +08:00