opsx
36724bba94
fix(queries): MySQL 实例误调 Redis 接口 500 + 点表生成 SELECT + 光标多行闪烁
...
=== 一、/queries 打开 MySQL 实例报 500 ===
现象:GET /api/v1/redis/5/dbs → 500
后端日志 redis: invalid reply: "J\x00\x00\x00\n"
("J\x00\x00\x00" 是 MySQL 握手包,说明用 Redis 协议连了 3306)
根因:SchemaSidebar 是 mysql/redis 共用的组件,其中
const redisDbs = useRedisDbs(Number(instanceId));
是无条件调用(React hooks 不能条件调用),而 useRedisDbs 内部
只看 instanceId 就请求 /redis/{id}/dbs →
MySQL 实例也发 Redis 请求 → 后端用 Redis 协议连 MySQL → 500。
修复(前后端双保险):
1. useRedisDbs 增加 enabled 参数(默认 true),false 时不发请求并复位 [0]/false
2. SchemaSidebar 传 isRedis:useRedisDbs(Number(instanceId), isRedis)
3. redis.go getRedisClient 前置校验 DBType != "redis" 直接返回
4. resp 包新增 UserFacingError / UserFacing(),InternalErr 识别后返回
400 + 可读原因(不再把所有错误笼统包装成 500 "服务器内部错误")
验证:
GET /redis/5/dbs(MySQL)→ 400
{"code":"bad_request","detail":"实例「pre-mysql-只读」的类型是 mysql,
不是 redis。Redis 接口只适用于 redis 实例;MySQL/PostgreSQL 请使用
/api/v1/instances/5/databases 等对应接口"}
GET /instances/5/databases → 200
GET /redis/4/dbs(Redis 集群)→ 200 {"cluster":true,"dbs":[0]}
=== 二、左侧栏点表不生成 SELECT ===
原交互:单击=展开/折叠,双击=执行 SELECT,Alt+点击=插入表名。
需求:单击表 → 在右侧编辑器生成 SELECT * FROM <db>.<table>
修复:
1. SchemaSidebar 表节点:箭头图标包 data-chevron 单独触发展开/折叠;
点表名区域 → onInsert(previewSelectSql(...) + ';\n')
Alt+点击仍为只插表名;双击仍为直接执行
2. QueriesPage onInsert:生成的文本以 SELECT 开头时「替换」编辑器内容
(原来一律追加,会和上一条语句拼成非法 SQL);表名/字段名仍追加
=== 三、SQL 编辑器光标同时在多行闪烁 ===
根因:.cm-cursor 强制了 height:1.25em !important + marginTop:0.15em,
而 .cm-line 行高是 1.55em,光标高度溢出到相邻行,视觉上像两行同时闪。
修复:光标样式只保留颜色与 1.5px 宽度,height/marginTop 交回 CodeMirror 自行计算。
镜像:lghost/opsxsql:latest + v0.1
2026-10-27 00:58:22 +00:00
opsx
0427b87412
fix(instance): access_mode 值 read/readonly 不一致导致「读」实例被过滤
...
现象:instance-groups 里给 MySQL 实例授予「只读查询」权限后,
/queries 页面仍然选不到该实例,admin 也无法访问。
根因(枚举值前后端不一致):
前端 CreateInstanceForm「读」按钮写入 access_mode = "read"
后端 AccessibleInstances 只匹配 ["readwrite", "readonly"]
→ "read" 永远不命中,选了「读」的实例(MySQL)被过滤掉;
Redis 实例是 "readwrite" 所以恰好能查,掩盖了该 bug。
修复:后端 readonly 过滤增加 "read",兼容两种历史写法:
[]string{"readwrite", "readonly", "read"}
写过滤仍为 ["readwrite", "write"]("read" 实例正确地不能写)
验证(admin):
GET /instances/accessible?access_type=readonly → 5 个(含 MySQL id=5, access_mode=read)
GET /instances/accessible?access_type=write → 4 个(MySQL 正确排除)
镜像:lghost/opsxsql:latest + v0.1 = sha256:6b1a7121da71
2026-10-26 18:30:47 +00:00
opsx
f19a84f37e
fix(redis): 集群拓扑解析错误,跨机集群节点被强制指向种子机
...
【现象】Redis 集群实例查 keys 报 500:
Redis cluster connection failed: dial tcp 172.19.16.16:6005: connect: connection refused
【根因】handler/redis.go 解析 CLUSTER SLOTS:
nodeAddr := slot.Nodes[0].Addr // Redis 上报的真实地址 172.19.16.30:6005
if idx := strings.LastIndex(nodeAddr, ":"); idx >= 0 {
nodeAddr = instance.Host + nodeAddr[idx:] // 强制改写成 种子机主机 + 原端口
}
该逻辑假设「集群所有节点在同一台机器、只是端口不同」。
对跨机集群(16/30/59 三台)会把所有节点地址破坏成
172.19.16.16:6002 / :6003 / :6005 —— 这些端口在 16 上根本不存在 → refused。
【修复】
仅当 Redis 上报空 IP / 127.0.0.1 / 0.0.0.0 时才用配置 host 兜底(NAT/端口映射场景),
其余情况一律保留 Redis 上报的真实主机地址。
【附带数据修复(非代码)】
实例 5 host: pre-mysql.astx.club → 172.19.16.2
原因:pre-mysql.astx.club 公网 DNS 解析到 43.135.53.180(另一环境 MySQL),
认证失败 → 前端数据库下拉为空、无法选择数据库。
【验证】
GET /api/v1/redis/4/keys?db=0&pattern=* → 200
{"cluster":true,"keys":[{"key":"24H_rate_limit_10001_...","type":"string","ttl":42901000},
{"key":"CEX:FOLLOW:FOLLOWERS:TRADFI:97127836","type":"hash"},...]}
GET /api/v1/redis/4/dbs → 200 {"cluster":true,"dbs":[0]}
GET /api/v1/instances/5/databases → 200 返回 25 个库
["cex_activity","cex_admin","cex_agent",...,"cex_user","cex_xxl_job"]
镜像 sha256:165992ef79d5
2026-10-26 18:01:44 +00:00
opsx
d584b64813
feat(security): 默认账号随机初始密码(不再硬编码 admin123)
...
参考 lokistack 的实现:
1. 首次启动生成 16 位随机密码(大小写+数字,排除 0/O/1/l/I 易混淆字符)
- 4 个默认账号共用:admin / dba / auditor / developer
- 写入数据目录 initial-admin-password.txt(0600)
- 打印到启动日志(含账号列表、密码、文件路径、docker/kubectl 获取命令)
2. 支持 ADMIN_INIT_PASSWORD 环境变量覆盖(自动化部署 / 需要固定密码时)
3. 生成失败直接 log.Fatalf 退出,不降级到公开默认口令
4. 批量导入用户 BatchImportUsers:空密码不再默认 admin123,
改为随机 14 位(新增 genPassword),保证每个导入用户密码不可预测
5. 幂等:已存在的账号不重建、不改密码;只有缺失的账号才创建
6. imagePullPolicy: IfNotPresent → Always
原因:latest tag 配 IfNotPresent 会导致节点一直复用缓存的旧镜像,
新 push 的镜像不生效(本次实测踩到:Pod imageID 仍是旧 digest)
7. 文档
- README 新增「首次部署获取默认账号密码」章节(含 4 账号角色表 + 3 种获取方式)
- docker-compose.yml 加 ADMIN_INIT_PASSWORD 环境变量
- kubernetes/deployment.yaml 加 env 注释
验证(清空 DB 后全新 seed):
镜像 sha256:eaf0affd8d93
/app/data/initial-admin-password.txt 199B 权限 0600
内容含 "适用账号: [admin dba auditor developer]" + 随机密码
启动日志完整打印获取方式
登录实测:admin / dba / developer 用新密码均返回 access_token;
admin123 登录失败(已失效)
二进制 strings:admin123 命中 0
2026-10-26 17:37:21 +00:00
Logan.Li
13fb3aceb3
feat(ldap): ad/openldap dual compat and config gating
...
- multi-round user filters (config filter first, then
objectClass=inetOrgPerson, then AD sAMAccountName/userPrincipalName
fallback) applied to login search, user listing and batch import;
previously pure-AD directories found no users
- attribute fallback chains (uid/sAMAccountName/userPrincipalName,
cn/displayName, telephoneNumber/mobile) via ldapEntryAttr
- import errors explain likely cause instead of empty result
- AES-encrypted admin password kept (aiops stores plaintext)
- frontend gating per aiops pattern: LDAP account management panel
hidden with hint until a config is actually saved (was gated on
unsaved form values); batch-import modal hides to/from-LDAP tabs
when unconfigured, local CSV always available
2026-10-26 20:08:46 +08:00
Logan.Li
2c96902fbd
fix: make postgresql actually usable (4 critical bugs)
...
- CORS whitelist only allowed origin localhost:5173; browsers send Origin
on same-origin POST too, so every deployment on any other port/origin
got empty 403 on ALL POST/PUT/DELETE (curl tests masked it) - now
echoes any origin (bearer-token auth, no cookie CSRF surface)
- connection_tester pg DSN had a literal *** password placeholder - pg
connection test and query path could never authenticate
- instance detail endpoints (databases/tables/db-schema/table-detail)
ran MySQL SHOW statements against pg - added pg branches
(pg_database/pg_class/information_schema/pg_indexes, search_path
semantics, PRI detection); schema filter uses non-system schemas
- frontend normalizeDbType missed the literal 'postgresql' value so pg
instances were treated as mysql (backtick quoting, no column hints);
genSql no longer emits db.table prefix for pg (schema semantics)
- also: query page footer warnings condensed to a single line per db type
verified live: mysql table tree/context menu/preview-execute; pg table
tree/columns/PK, double-click preview with masking working (email/phone
masked), query history persisted
2026-10-26 19:47:08 +08:00
Logan.Li
c8155e2d33
fix(ticket): implicit executor for default-flow tickets
...
- list is_executor and ExecuteTicket both required an execute-step
approval record; tickets created without a workflow (default flow)
had none, so the execute button never appeared and the API returned
403 for everyone - admin/dba are now implicit executors (approved
status only, consistent in both list enrichment and execution check)
- status labels: pending/reviewing/approved were all shown as 审核中;
now 待初审/审批中/待执行 so the ready-to-execute state is visible
- verified end-to-end on test backend: create -> quick review dialog
with comment -> impact-summary execute confirm -> success, detail
page stage steps reach 完成 with execute result visible
2026-10-26 19:14:35 +08: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
6604fdf48e
feat(backend): redis key scan service - cursor pagination + cluster + CLI fix
...
Modeled on opsxdb's redis scan design:
- opaque base64 scan cursor {pattern,db,type,nodes,index,cursor}; server
validates search conditions match, rejects stale cursors (400)
- soft page cap 200 keys (whole scan batches returned, no key loss),
max 20 SCAN calls per page; next_cursor until all shards exhausted
- pattern rule: no glob chars -> *kw* contains match; non-glob input
also probed with TYPE for exact hit (returned as exact, slot-routed
so cluster/NAT safe)
- pipeline batch TYPE/TTL replaces per-key serial N+1; type filter now
effective server-side
- cluster: per-master direct SCAN merged across shards (MasterAddrs on
wrapper), DB0-only, MOVED errors hint at switching deploy mode;
SlowLog/ClientList aggregate across masters (were cluster-broken)
- GET /redis/:id/dbs (0-15, cluster -> [0])
- POST /redis/:id/keys/:key/field: hash row-level HSET/HDEL (no whole-
key rewrite)
- RedisExecuteCommand fixed (was always-403): accepts {db, command}
full-string, quote-aware tokenizer, whitelist on first word, expanded
read-only set
- RedisUpdateKey preserves previous TTL unless explicitly set/cleared
(was silently dropping TTL); string type skips the DEL
Verified live against local redis: 200/page no overlap across pages,
cursor 400 on condition change, exact hit, type filter, hash field
edit round-trip, CLI DBSIZE/HGET ok, SET rejected
2026-10-26 18:27:11 +08:00
Logan.Li
d867c276d4
chore(frontend): engineering baseline per guide ch.8
...
- toolchain: vitest+jsdom (15 tests green: permission pure functions
exhaustive, storage migration), prettier (printWidth 120 to fit the
dense existing style + 700-line page budget), commitlint conventional
- npm scripts: typecheck / format / test / lint:fix; build fingerprint
__APP_VERSION__ (version+git hash+build time) shown in Settings > about
- git hooks via core.hooksPath=frontend/.husky: pre-commit lint-staged
(prettier + eslint --fix best-effort), commit-msg commitlint
- permission pure functions extracted to lib/permissions.ts (single
source consumed by useAuth/useLogin); AuditLogsPage filters on URL
- backend: resp package unit tests; health version constant aligned
with frontend package.json
2026-10-26 17:58:59 +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
hermes-$(hostname)
0fcdbc5f6c
update
2026-07-08 08:53:48 +00:00
Logan.Li
617c15dbfe
feat: harden query read-only workflow
2026-07-07 17:20:44 +08:00
Logan.Li
58bbc394e9
feat: enhance redis and production IA
2026-07-07 10:39:33 +08:00
Logan
644981eef4
feat: improve datasource query audit workflows
2026-07-06 23:01:04 +08:00
logan
74748e4914
fix: 审批流程三个bug修复
...
1. 点通过不再重复创建审批记录(后端无NULL记录时返回400而非自动创建)
2. 非提交人不再显示撤销按钮(列表页增加applicant_id校验)
3. 已审批工单不再显示通过/拒绝按钮(is_approver只计算pending记录)
4. 前端审批失败时弹出错误提示而非静默忽略
2026-06-03 17:17:12 +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
8fbcb8647d
feat: SQL安全防护体系 — 危险SQL黑名单 + 事务回滚 + LIMIT修复
...
🔴 P0 安全漏洞修复:
- MySQL: 黑名单拦截 DROP DATABASE/DROP SCHEMA
- MySQL: 拦截无WHERE的UPDATE/DELETE(全表操作)
- MySQL: 多语句中含危险SQL也会被检测
- Redis: 拦截 FLUSHALL/FLUSHDB/CONFIG/DEBUG/SHUTDOWN/SCRIPT/SAVE/BGSAVE/KEYS/CLUSTER/REPLICAOF
🔄 事务支持:
- MySQL多语句执行包裹在事务中
- 任一SQL失败自动回滚全部已执行语句
- 验证通过: INSERT成功+UNIQUE冲突=全部回滚
🔧 Bug修复:
- query.go: LIMIT只追加SELECT语句(SHOW/CREATE等不再追加LIMIT报错)
- SHOW VARIABLES/STATUS 恢复正常
📊 测试验证:
- 29/29 安全拦截测试全部通过
- MySQL 8项危险操作全部拦截
- Redis 11项危险命令全部拦截
- 10项正常SQL全部放行
- 事务回滚验证通过
- 类型错误/参数错误正确报告
2026-06-03 02:49:07 +08:00
logan
8ceb249274
fix: MySQL DDL+DML同批EXPLAIN预检bug + Redis集群MOVED重定向 + 清晰错误提示
...
MySQL修复:
- 批次含DDL时跳过整个EXPLAIN预检(避免CREATE TABLE未执行时INSERT的EXPLAIN失败)
- 纯DML批次仍正常做EXPLAIN预检
Redis集群修复:
- 单节点客户端遇到MOVED时自动映射外部IP+端口重试
- 重试失败给出清晰提示(Docker端口未暴露)
- 新增 isClusterInstance/parseMovedTarget 辅助函数
测试验证通过:
- MySQL 5条DDL+DML ✅ (建表+INSERT+UPDATE+加索引)
- MySQL 纯DML+EXPLAIN预检 ✅
- MySQL dba(logan)执行 ✅
- Redis单机 SET写入 ✅
- Redis集群 ⚠️ Docker内部端口未暴露(基础设施限制)
2026-06-03 02:40:15 +08:00
logan
bec2cafec2
feat: 执行结果结构化-每条SQL记录status/affected_rows/duration_ms + 去掉工单详情优先级字段
...
后端:
- 新增 SqlExecItem/SqlExecResult 结构体
- MySQL/Redis/gh-ost 三条执行路径全部改为 JSON 结构化结果
- 每条 SQL 记录完整语句、状态、影响行数、耗时(ms)、错误信息
- 总耗时/开始结束时间一并记录
前端:
- ExecuteResult 接口匹配后端 SqlExecResult
- 表格展示每条 SQL 执行结果(序号/SQL/状态/影响行数/耗时/错误)
- 汇总卡片: 状态/总耗时/SQL数量
- 去掉工单详情中的优先级和变更原因字段
- 保留 plain text fallback 兼容旧数据
E2E验证: INSERT+UPDATE+ALTER 三语句全部成功, 结构化结果正确
2026-06-03 02:15:37 +08:00
logan
1364c737ef
fix: 工单列表权限过滤-developer/auditor也能看到待审工单
...
- developer之前只能看applicant_id=自己的工单,导致审批人看不到待审工单
- 改为admin看全部,其他角色(包括developer/dba/auditor)都能看到自己提交+自己作为审批人的工单
- E2E全流程验证通过: testtest提交→test123初审→test234复审→admin执行→success
2026-06-03 02:06:20 +08:00
logan
bd8100bfe0
fix: 撤销工单支持approved状态 + DDL跳过EXPLAIN预检 + 审批菜单仅admin/dba + 审批状态流转修复 + 权限组实例绑定修复
2026-06-03 01:53:21 +08:00
logan
b1a3d74661
feat: 角色权限体系改造 — developer可审批, auditor纯只读
...
角色权限矩阵:
- admin: 全部权限
- dba: 数据源管理+工单全流程+审批+执行
- developer: 查询+提交工单+审批(不能执行)
- auditor: 只读(审计日志+审核规则+审批流程查看)
后端改动:
- ticket.go: 修复GetCurrentUser返回值顺序bug(userID,username,role)
- ticket.go: CreateTicket拦截auditor提交工单
- ticket.go: ApproveTicket允许admin/dba/developer
- ticket.go: ExecuteTicket仅允许admin/dba
- router.go: query执行限制admin/dba/developer
- router.go: workflow写操作限制admin/dba
- router.go: audit-rules写操作限制admin/dba
前端改动:
- menu.ts: tickets去掉auditor, workflow加developer+auditor, audit-logs加auditor
- useAuth.tsx: routePermissionMap和actionPermissionMap同步更新
- TicketDetailPage: canApprove加developer角色
DB迁移: test123/test234从auditor→developer
2026-06-03 01:19:24 +08:00
logan
97fd4ff069
fix: 数据变更工单拦截只读SQL — 前端提交/检测+后端创建三层拦截SELECT/SHOW/DESC/EXPLAIN
2026-06-03 00:48:09 +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
8af868b3ca
fix: 数据源编辑密码保留 + Redis ACL 认证支持
...
后端:
- UpdateInstance: 空密码和'******'都不更新密码字段,防止编辑后丢失密码
- testRedis/getRedisClient/getRedisMetrics: 增加 Username 参数支持 Redis 6.0+ ACL 认证
- username 为 'default' 或空时走传统密码认证模式
前端:
- EditInstanceDialog: Redis 用户名字段提示'6.0+ ACL 可选',密码 placeholder 改为'不修改请留空'
- CreateInstanceForm: Redis 类型也显示用户名输入框(可选),创建/测试连接时 Redis username 为空则不传
- Redis 提示文案从'无需用户名'改为'6.0+ ACL 支持用户名+密码认证'
2026-05-31 23:55:18 +08:00
logan
bef2f98f28
feat: 审批/执行角色权限校验 + LDAP用户禁止重置密码
...
- ticket.go: ApproveTicket 前置角色检查(admin/dba/auditor),ExecuteTicket 前置角色检查(admin/dba)
- ticket.go: 创建工单时自动从 instance.workflow_id 读取关联审批流程
- UsersPage.tsx: LDAP 用户重置密码按钮置灰(disabled+opacity-30+提示文案)
- 权限模型: 审核=管理员/DBA/审核员, 执行=管理员/DBA, 提交=所有角色
2026-05-31 22:20:24 +08:00
logan
b4c982d565
feat: 工单执行人直接执行(自动通过) + 审批时间线 + 执行结果展示
...
后端:
- 创建工单时检测是否有审核步骤,无审核步骤直接设为 reviewing
- ExecuteTicket 允许 reviewing 状态,自动通过 execute 审批记录再执行
- 验证执行人权限(非执行人返回 403)
- 列表 API 返回 has_audit_step/is_executor/is_approver 元信息
- 详情 API 返回 approvals 带 approver_name/actual_approver_name
前端:
- 按钮权限精确化:通过/拒绝只对审核人显示,执行只对执行人显示
- 详情弹框加载完整审批数据,展示操作时间线
- 时间线:提交人→每个步骤的指派人+实际操作人+操作时间+备注
- 执行结果文本格式 fallback 显示
- 失败工单显示失败原因
2026-05-31 21:58:52 +08:00
logan
cdb52aa4a9
feat: 工单审批流程支持 workflow 指定用户审批 + Redis 命令执行
...
1. 工单创建时读取 workflow_steps 的 approver_ids 创建审批记录
- 有指定用户:为每个用户创建审批记录(任一通过即可)
- 无指定用户:按 approver_role 角色匹配 fallback
- 无 workflow:默认 admin 审批
2. Redis 工单执行:复用 getRedisClient + parseCommandParts 执行写命令
- 支持 SET/DEL/HSET 等所有写操作
- 解析 db0/db1 格式的目标库
- 返回 [OK] 命令 → 结果
2026-05-31 21:40:30 +08:00
logan
24c633d9f0
feat: ZRANGE WITHSCORES JSON自动解析为结构化表格 + Redis审计规则 + 前端安全检查
...
- ZRANGE WITHSCORES 自动检测JSON member,展开为 open/close/high/low/volume 等独立列
- Redis 审计: 只读命令安全,写命令warning,FLUSHALL等危险命令error
- TicketCreatePage: auditResult.findings 添加 undefined 安全检查
- 审计API支持 instance_id 自动解析 db_type
2026-05-31 02:07:10 +08:00
logan
9c5cb04cfa
fix: Redis集群查询+metrics修复 — 单节点连接替代ClusterClient
...
- crypto.go: decryptPassword 自动处理 enc: 前缀,避免调用方遗漏
- redis.go: getRedisClient 统一使用 redis.NewClient 单节点连接
ClusterClient 拓扑发现(CLUSTER SLOTS)在Docker内网IP不可达时超时
redisClientWrapper 全部方法改为使用 Client (不再走 ClusterClient)
ScanAllKeys 简化为单节点 SCAN
- connection_tester.go: getRedisMetrics 统一单节点连接采集metrics
删除 isCluster 分支的 ClusterClient 代码,避免5秒超时
2026-05-31 01:30:10 +08:00
logan
e9a700b9d9
fix: Redis 查询格式修复
...
- rows nil → 空数组: rowsOrDefault() 确保 HGETALL/LRANGE 空集合返回 [] 而非 null
- 扩展只读白名单: PING/CONFIG/CLIENT/SLOWLOG/CLUSTER/MEMORY/OBJECT/DEBUG 等
- 前端 sortedResult: Array.isArray() 检查防止 raw_text 模式报 not iterable
2026-05-31 00:34:38 +08:00
logan
e8fd5f6b63
fix: LDAP导入用户 auth_source 修复
...
- BatchImportLdapUsers: 设置 AuthSource='ldap' + HashedPassword='-' + IsActive=true
- 修正已有 zhangsan/lisi/wangwu 用户 auth_source 从 local 改为 ldap
2026-05-31 00:18:43 +08:00
logan
31db229d70
feat: MySQL/PG 实时指标采集 + 版本检测修复 + 密码加密存储
...
- 新增 getMySQLMetrics(): SHOW GLOBAL STATUS 采集连接数/QPS/延迟/运行时间
- 新增 getPostgresMetrics(): pg_stat_activity 采集活跃连接+运行时间
- 修复 extractVersion(): 支持 Redis 'version: 7.4.9, mode: standalone' 格式(逗号截断)
- CreateInstance/UpdateInstance: 创建/更新时自动加密密码(enc: 前缀)
- InstanceMetrics handler: MySQL/PG 分别调用专属 metrics 函数
2026-05-31 00:15:18 +08:00
logan
433e7e2909
feat: ARDM风格查询页优化 — 可拖拽Sidebar + Redis差异化结果展示 + KeyHeader\n\n1. Sidebar可拖拽宽度(200-600px, localStorage持久化)\n2. Redis raw_text展示加ARDM KeyHeader: key名+类型标签+TTL+格式标签+复制按钮\n3. HGETALL结果用field/value紧凑双列展示替代通用表格\n4. 后端raw_text模式自动查询key TYPE和TTL返回raw_type/raw_ttl
2026-05-30 18:20:22 +08:00
logan
4e6f9eb8fd
fix: Redis集群SCAN遍历所有master节点 + 查询结果布局调整\n\n1. ScanAllKeys(): 集群模式用ForEachMaster遍历所有master节点SCAN合并去重\n2. RedisScanKeys/KEYS命令均改用ScanAllKeys,确保集群不遗漏key\n3. QueriesPage布局: sidebar独立左侧,查询结果只在编辑器下方不被sidebar占据宽度
2026-05-30 15:28:38 +08:00
logan
430a667455
fix: Redis命令解析支持引号参数(parseCommandParts替代strings.Fields)\n\nstrings.Fields()会将引号当作参数的一部分,导致HGET带引号查询失败\n新增parseCommandParts()函数支持双引号和单引号包裹的参数
2026-05-30 14:47:21 +08:00
logan
c51f619a7d
feat: 实现LDAP创建OU和用户(LDAP Add)+弹框风格统一
2026-05-30 10:24:29 +08:00
logan
7b5f95e702
fix: LDAP批量导入用户auth_source设为ldap而非默认local
2026-05-30 02:34:29 +08:00
logan
3af65f2350
feat: Redis cluster support + version auto-detect + metrics API
2026-05-30 01:44:58 +08:00
hermes-$(hostname)
8384006ae9
feat: 用户管理列表增加来源字段(auth_source),支持本地/LDAP筛选
2026-05-29 17:05:56 +00: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
86eea25031
fix: 审批接口自动补全缺失的approval记录
...
旧ticket可能没有关联approval记录(创建时代码不同),
导致approve/reject报400 No pending approval。
现在admin/dba角色会自动创建approval记录再审批。
2026-05-29 18:46:51 +08:00
logan
5366d5c963
refactor: 重写测试套件对齐Go后端API + 修复superadmin权限
...
- 删除 29 个旧 Python 后端单元测试 (from app.* 导入已不适用)
- 重写 19 个集成测试文件对齐 Go/Gin 后端实际 API 行为:
- 创建端点返回 201 (非 200)
- Settings PUT 需要数组格式
- SQL 审核 field 是 sql_content (非 sql)
- 实例/审核规则列表不支持服务端过滤, 改为客户端过滤
- 实例详情 MySQL 不可达时返回 500 (容错断言)
- 修复 RequireRole 中间件: superadmin 等同 admin 权限
- 测试结果: 81 passed, 4 skipped, 0 failed
2026-05-29 16:12:32 +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
c6ef67adba
fix: 修复权限组用户看不到实例的bug — AccessibleInstances第二次Pluck覆盖第一次结果,改为单条OR查询
2026-05-28 20:50:57 +08:00
logan
ed43f6f398
feat: 实现用户批量导入(LDAP+本地+CSV模板)
...
- BatchImportLdapUsers: 从LDAP导入用户到本地DB
解析 dn/cn/uid/mail 字段,默认密码 admin123,角色 developer
重复用户自动跳过
- BatchImportUsers: 本地CSV批量导入
支持 username/display_name/email/role/password
skip_existing 选项控制重复行为
- ImportTemplate: 真实CSV模板下载(UTF-8 BOM)
- 3个endpoint从placeholder改为真实实现
2026-05-28 16:26:53 +08:00
logan
7f09ef8e1a
fix: Redis侧边栏扫描也自动补通配符
...
RedisScanKeys 端点的 pattern 参数无通配符时自动追加 *,
与查询页 KEYS 命令行为一致,输入 match 即匹配 match*
2026-05-28 16:13:50 +08:00
logan
b8df2c0027
fix: Redis KEYS命令自动补通配符(*)
...
输入 KEYS match 自动变为 match* 模糊匹配,
无需手动加 *,同时匹配 ? [] 等 Redis 原生通配符
2026-05-28 16:07:16 +08:00