Compare commits

...

1279 Commits

Author SHA1 Message Date
Syngnat
e404fa3ac0 🐛 fix(data-grid): 兼容无 navigator 的服务端渲染
- 通过 globalThis 安全注入浏览器 navigator,避免 Node 20 渲染期抛错
- 补充无 navigator 环境下的 DataGrid 服务端渲染回归测试
- 保持浏览器剪贴板行为不变
2026-07-19 00:22:02 +08:00
Syngnat
7913811549 ️ perf(data-grid): 优化大数据量横纵向滚动性能
- 为固定行高结果集启用常数时间纵向窗口计算和横向列虚拟化
- 合并横向滚动预览与空闲提交,减少滚动期间重复渲染
- 固化 rc-table 与 rc-virtual-list 补丁并同步 Wails、Docker 和 CI 安装链路
- 补充滚动边界、页内查找及调度回归测试
2026-07-19 00:10:25 +08:00
Syngnat
af49d15563 🐛 fix(table-metadata): 修复统计字段被误识别为表名 (#666)
## 背景

DBGetTables 会同时返回 Table、Rows、Data_length 和 Index_length 等字段。部分前端入口通过
Object.values(row)[0] 获取表名,Go map 序列化后的字段顺序变化时,行数或存储大小会被误当成表名。

## 变更内容

- 新增共享表元数据解析器,优先读取明确的表名字段
- 兼容 Table、table_name、tableName、name、MySQL Tables_in_*、字符串行和旧单字段响应
- 多字段响应缺少明确表名时不再猜测首个字段
- 统一 AI 关联上下文、AI 数据库工具、查询补全、ER 图、侧边栏、全库搜索、数据同步、批量导出、导出工作台和表概览的解析逻辑
- 补充统计字段顺序、兼容格式、空值和重复名称的回归测试

## 影响范围

仅调整前端对 DBGetTables 返回值的表名解析,不修改后端 API、数据库查询或表统计逻辑。

## 验证

- 12 个相关 Vitest 文件,共 62 条测试通过
- npm run build 通过,8669 个模块完成生产构建
- git diff --check 通过
2026-07-18 21:49:58 +08:00
AutumnNazi
6e58240c38 🐛 fix(table-metadata): 修复统计字段被误识别为表名
- 新增共享表元数据解析器,优先读取明确的表名字段
- 统一 AI、查询补全、侧边栏、导出和表概览等调用路径
- 补充字段顺序、兼容格式及去重行为的回归测试
2026-07-18 21:03:25 +08:00
Syngnat
3aea725f7b feat(import-export): 支持 SQL 导出前删除同名对象
- 新增默认关闭的 DROP IF EXISTS SQL 导出选项和数据删除提示
- 覆盖数据库、Schema、单表、批量导出及工作台入口
- 按方言生成逆序 DROP 前导段并保持纯数据与旧 API 兼容

Fixes #514
2026-07-18 18:20:30 +08:00
Syngnat
fd093b3325 feat(clickhouse): 支持自定义连接兼容 JDBC DSN
- 解析 ClickHouse 与 JDBC 风格 DSN 并映射协议、TLS、认证及数据库参数
- 统一连接缓存、DDL、数据同步与导出 driver-agent 生命周期
- 保持 DSN 加密存储并补充前后端回归测试和多语言提示

Fixes #383
2026-07-18 17:48:19 +08:00
Syngnat
0136b87311 🐛 fix(proxy): 避免全局代理影响数据库连接
移除数据库和 Redis 连接对全局代理的隐式继承

保留连接自身代理与 HTTP 隧道配置并修正缓存释放链路

同步六种语言的代理作用域说明并增加回归测试

Fixes #347
2026-07-18 16:43:39 +08:00
Syngnat
aeecffb517 feat(table-designer): 补齐 MySQL 空间字段类型
- 补充 Geometry、Point、MultiPoint 等八种 MySQL 空间类型候选
- 保留自定义字段类型表达式在建表和改表 SQL 中的原样输出
- 增加空间类型候选及 POINT SRID 属性回归测试

Fixes #341
2026-07-18 16:15:33 +08:00
Syngnat
5f5b28ced3 🐛 fix(schema): 修复 PostgreSQL 模式备份恢复与重命名
- 模式备份脚本写入安全转义的 CREATE SCHEMA IF NOT EXISTS
- 支持仅大小写变化的 PostgreSQL 模式重命名
- 补充备份头顺序、非 PostgreSQL 隔离和模式 DDL 回归测试

Fixes #480
2026-07-18 15:51:48 +08:00
Syngnat
15fba1d89f feat(import-export): 优化数据表导入导出能力
- 修复表格格式导出时空值显示为 null 的问题
- 支持按列导出并保持字段顺序
- 支持导入列与数据库字段映射及冲突校验
- 隔离并发导入任务进度并完善元数据回退

Fixes #646
2026-07-18 15:17:54 +08:00
Syngnat
f08bcb5e10 🐛 fix(monaco-editor): 修复 WebKit 输入法组合输入时异常滚动
- 仅在原生 WebKit 环境稳定 Monaco 输入法组合期间的文本区滚动位置
- 在组合结束、失焦和编辑器销毁时及时停止并清理监听器
- 补充 WebKit、Chromium 隔离及销毁清理回归测试
2026-07-18 14:00:19 +08:00
Syngnat
7017db418b 🐛 fix(updater): 修复 MSI 更新后任务栏图标失效与桌面快捷方式重建
- 自动迁移引用失效 MSI 缓存图标的旧任务栏固定项并刷新 Shell
- 在在线更新前备份桌面快捷方式状态,成功或失败后安全恢复
- 将桌面快捷方式拆分为条件安装组件并补充 Windows 回归测试
2026-07-18 13:48:35 +08:00
Syngnat
6cd2835066 feat(ai): 支持 Codex 与 Claude 订阅登录
- 新增基于本机官方 CLI 登录状态的 Codex 与 Claude 订阅 Provider
- 隔离 API Key、工具与项目配置,完善登录校验和错误处理
- 补充设置界面、国际化及前后端回归测试

Fixes #651
2026-07-18 11:57:25 +08:00
Syngnat
07a4fe1fc9 🐛 fix(query-editor): 修复 MySQL 关键字需美化后才着色
- 根据当前连接方言为 MySQL 系查询启用 Monaco MySQL 语法
- 将补全、悬浮、斜杠命令和代码片段同步注册到 MySQL 语言
- 增加方言映射和首屏着色回归测试

Fixes #612
2026-07-18 10:32:51 +08:00
Syngnat
1ee8f7f980 🐛 fix(ci): 修复 Docker 冒烟测试端口冲突
- Web Server 冒烟测试使用随机宿主端口
- 动态解析映射端口并复用健康检查与鉴权验证
2026-07-18 09:51:19 +08:00
Syngnat
d0a6a97f71 🐛 fix(sql-file): 修复整库备份导入失败
Fixes #662
2026-07-17 23:37:53 +08:00
Syngnat
e24942083a 🐛 fix(sidebar): 顶部新建查询绑定选中数据库
Fixes #665
2026-07-17 22:40:36 +08:00
Syngnat
e7857bb2e4 🐛 fix(ci): 修复跨 Runner 驱动版本误判
使用构建阶段 SHA 绑定 provenance 校验驱动发布资产。

保留原生二进制元数据和 legacy fallback 校验,并固定发布任务 Go 环境。

Fixes #622
2026-07-17 21:50:15 +08:00
Syngnat
8f52cbc319 🐛 fix(query-editor): 修复选区输入首字符丢失
- 为 Monaco 可打印输入兜底补充非空选区替换恢复
- 区分原生成功、仅删除选区和完全丢失,避免重复插入
- 覆盖连续输入、重选文本和模型事件光标竞态
- 新增 &lt; 单次替换为 < 的回归测试
2026-07-17 17:39:02 +08:00
Syngnat
ca8be18531 🐛 fix(ci): 校验驱动代理发布产物版本来源
- 原生平台执行 metadata 校验真实 driver type 与 revision
- 跨平台构建生成与二进制 SHA256 绑定的 provenance
- 复用旧资产时要求清单 SHA 与当前源码 revision 同时匹配
- 将 provenance 接入开发版和正式版发布聚合流程
- 发布链变更触发全平台 driver-agent 重建
2026-07-17 17:07:37 +08:00
Syngnat
54f18cdd2f 🐛 fix(driver-agent): 阻止运行不匹配的驱动代理
- 连接前复用安装阶段的严格 revision 与 metadata 校验
- 对旧版或无法验证的 agent 返回明确重装错误而非继续运行
- 覆盖 stale、metadata 失败、当前版本和自定义驱动边界
- 隔离 SQL 审计测试对本机已安装 agent 的依赖
2026-07-17 17:06:44 +08:00
Syngnat
0ca8b1960c 🐛 fix(milvus): 兼容不支持数据库枚举的服务
- 数据库列表接口不可用时验证当前数据库的集合枚举能力
- 验证成功后回退到已配置数据库并继续加载集合
- 补充测试连接成功但侧栏展开失败的回归用例
2026-07-17 17:06:08 +08:00
Syngnat
3f4b247faa feat(native-window): 完善原生独立窗口与跨屏拖拽
- 支持全部工作台标签与 AI 对话在多显示器中原生独立展示
- 修复拖拽指针捕获,恢复关闭、右键菜单和跨窗口释放识别
- 拆分子窗启动入口并按需加载语言与工作台内容
- 内容与 Monaco 完成可见绘制后再提交迁移,消除白屏和加载闪烁
- 加固结果编辑、宿主同步及关闭、超时和焦点竞态
2026-07-17 16:06:24 +08:00
Syngnat
a86f5c6078 🐛 fix(data-grid): 保留独立窗口完整结果快照
- 数据变更回传不再受界面隐藏列过滤
- 同步编辑与删除后的完整结果行数据
- 补充独立窗口隐藏列快照回归测试
2026-07-17 16:05:24 +08:00
Syngnat
38a18b9b8e 🐛 fix(ai): 稳定流式会话终止与窗口迁移
- 跟踪并等待全部流生产者退出,避免重叠请求残留
- 窗口迁移期间阻止新请求并保留最终流式消息
- 锁定会话切换与重试操作,状态落稳后再解除发送态
- 同步终止握手绑定并补充回归测试
2026-07-17 16:04:33 +08:00
Syngnat
f2d622b53f 🐛 fix(ai): 规范 OpenAI system 消息顺序
Fixes #663
2026-07-16 22:39:33 +08:00
Syngnat
fa87c2ee9f Merge pull request #658 from AutumnNazi/fix/msi-shortcut-icon 2026-07-16 21:16:02 +08:00
Syngnat
43b33ca42a Merge pull request #664 from AutumnNazi/fix/middle-click-close-tab 2026-07-16 21:11:36 +08:00
Syngnat
fa9d21be8b feat(native-window): 支持标签页拖出为原生独立窗口
- 支持 SQL、数据和结果标签拖出到跨显示器原生窗口
- 通过受认证 loopback bridge 复用主进程后端与状态
- 支持子窗口继续拆窗、聚焦、关闭、还原与异常退出恢复
- 补充多窗口协议、状态同步和跨平台回归测试
2026-07-16 20:25:08 +08:00
Syngnat
d3dee295d6 🐛 fix(query-results): 修复 SQLite 与 SQL Server 查询结果空页
- SQLite 只读查询优先普通 Query,并安全降级不支持的多结果集接口
- SQL Server 在结果集边界前恢复未扫描数据,保持 data/affectedRows 顺序
- 普通与事务查询过滤状态空壳,修正结果展示与审计返回行数
- 更新 SQL Server driver-agent revision 并补充 SQLite、MSSQL 回归测试
2026-07-16 20:24:39 +08:00
AutumnNazi
35427d08a8 feat(tab): 支持鼠标中键关闭标签页
- 在标签标题上识别鼠标中键并阻止默认滚动行为
- 复用现有标签关闭流程,保留 SQL 文件未保存确认
- 修正停靠标签关闭范围断言并增加中键交互回归测试
2026-07-16 20:19:34 +08:00
Syngnat
193d68398b 🔧 chore(license): 补齐开源许可与发布声明
- 新增项目版权及上游 NOTICE
- 让桌面、容器、驱动发布包携带法律文档
- 补充 Wails 和前端许可证元数据
- 增加发布资产回归校验
2026-07-16 17:24:16 +08:00
Syngnat
d14ac65db9 🐛 fix(connection): 修复多数据源连接打开异常
- 修复 Milvus 等数据源类型被错误回退为 MySQL
- 为 ClickHouse 22.8 增加旧版 JSON HTTP 兼容回退
- 使用轻量别名接口验证 Elasticsearch 索引枚举
- 更新可选驱动 revision 并补充回归测试
2026-07-16 17:23:16 +08:00
Syngnat
c90dcce859 🐛 fix(driver-agent): 修复驱动重装循环与旧资产复用
- 在线重装与本地导入统一使用 staging 并严格校验 agent revision
- 旧候选失败时继续回退,激活异常时恢复原驱动与运行文件
- 修复公共驱动源码变更被误判为单一驱动的问题并补齐 Trino
- 显式传播依赖枚举失败,避免生成部分 revision 指纹
- 将驱动发布契约回归接入开发版与正式版构建流程
2026-07-16 15:57:49 +08:00
Syngnat
dfa6836580 🐛 fix(sidebar): 修复元数据展示开关未过滤嵌套对象
- 修复子节点数量不变时误复用未过滤父节点的问题
- 保留标签、连接、数据库与 Schema 包装层中的深层过滤结果
- 补充多层侧栏树对象显隐回归测试
2026-07-16 15:56:15 +08:00
AutumnNazi
8f4bd9a3a6 🐛 fix(installer): 修复 MSI 更新后快捷方式图标失效
- 让开始菜单和桌面快捷方式直接使用 GoNavi.exe 内嵌图标
- 避免 Major Upgrade 后 Shell 缓存继续引用已删除的 MSI 图标资源
- 增加 Windows 安装产物回归测试,锁定快捷方式图标来源
2026-07-16 14:38:20 +08:00
Syngnat
0080cb33ac 🐛 fix(table-overview): 修复未知存储大小误显示为零
将 Oracle、达梦和默认方言不支持的存储统计改为 NULL 占位。

补充 Oracle 与默认方言回归测试,保留 SQLite 真实零值的 0 B 展示。
2026-07-16 13:10:29 +08:00
AutumnNazi
2400823ca4 🐛 fix(sqlite): 修复表概览行数与大小统计
- 通过 driver-agent 为 SQLite 表列表补充精确行数与存储占用
- 使用 dbstat 区分数据页和索引页并支持相对大小展示
- 区分未知统计与真实零值并补充前后端回归测试
2026-07-16 12:44:18 +08:00
Syngnat
695fad283c 🐛 fix(ci): 补全前端 Node 文件系统类型声明
- 为测试环境的 node:fs shim 增加 readdirSync 返回类型
- 避免本机上级 @types/node 掩盖隔离构建错误
- 恢复 Dev Build 与 Web Server Docker 镜像前端编译
2026-07-16 12:11:29 +08:00
Syngnat
6de7807f01 🐛 fix(query-editor): 修复 Oracle 同名同义词补全
- 合并 PR #654,并按 OWNER 与名称保留同义词元数据
- 无限定名优先登录用户,缺失时回退 PUBLIC,显式 schema 精确匹配
- 保持 GetTables 仅返回物理表,避免影响导出、ER 与 ROWID 判断
- 补充 owner 解析、PUBLIC 回退及物理表查询回归测试
2026-07-16 11:49:15 +08:00
Syngnat
3de7e1bb40 feat(settings): 将工具中心整合至设置中心
- 侧栏移除独立工具入口并统一使用设置齿轮
- 设置中心扩展为六组导航并复用原有工具内页
- 统一安全更新、连接配置、AI 设置与工具页面的返回路径
- 清理快捷键捕获、连接密码弹窗和 AI 修复上下文等瞬态状态
- 更新设置、侧栏、SQL 审计与国际化回归测试
2026-07-16 11:17:59 +08:00
Syngnat
ac4b520295 🐛 fix(brand): 统一图标资源并适配 macOS Dock
- 将 40 个重复 PNG 变体收敛为 10 个 WebP 品牌资源
- 品牌选择器、关于页、favicon 与 Dock 统一复用所选图标
- 按 824px 安全区和 184px 标准圆角合成 macOS Dock 图标
- 补充资源映射、品牌展示和 Dock 合成回归测试
2026-07-16 11:16:47 +08:00
Syngnat
823fd54ef0 📝 docs(readme): 替换透明背景品牌 Logo
- 中英文 README 统一引用仓库内品牌 Logo
- 使用带透明通道的 512px 图片移除底色
- 保持 GitHub 页面与仓库预览直接可用
2026-07-16 11:14:48 +08:00
半袭纸鸢
5413089db0 Merge 4df1dac2c2 into 5d91deaade 2026-07-16 11:04:02 +08:00
autumn
4df1dac2c2 feat(data-grid): 支持查询结果导出 INSERT SQL
- 导出弹窗新增 INSERT SQL 格式并支持选中行、当前页和全部结果
- 单表查询自动映射真实表名与字段类型,连表结果使用带方言引号的 <table_name> 占位符
- 复用流式导出与批量 INSERT 生成逻辑,兼容 PostgreSQL 布尔字面量
- 补充前后端回归测试,覆盖单表、连表、别名和占位表场景
2026-07-16 10:58:00 +08:00
Syngnat
5d91deaade 🔧 chore(frontend): 同步 package 校验文件
- 将 package.json.md5 同步为当前 frontend/package.json 的真实 MD5
- 消除工作台更新遗留的过期校验值
- 不改动依赖声明或自动生成的 TS 绑定
2026-07-16 08:44:19 +08:00
Syngnat
b781a1197e 📝 docs(readme): 修复 GitHub 截图展示 2026-07-16 08:29:14 +08:00
Syngnat
176b100274 🐛 fix(app): 修复品牌图标与 SQLite 查询展示
- 修复 Windows、macOS 与关于页的品牌图标展示及资源裁切问题
- 补齐 SQLite 表记录数与 SELECT 查询结果渲染
- 增加品牌图标和 SQLite 回归测试
2026-07-15 22:53:47 +08:00
Syngnat
d87e314880 🎨 style(brand): 吉祥物品牌图标可选,并同步 macOS Dock
新增 10 款品牌吉祥物资源与设置页选择器;标题栏/关于页/favicon 跟随切换,
macOS 通过原生 API 更新 Dock 图标(满铺浅蓝 Dock 专用图)。
同步默认 appicon 与 README 品牌展示。
2026-07-15 19:55:20 +08:00
yx755141
9a693ec4b1 fix: GetTables 增加 UNION ALL all_synonyms
imp_basicinfo 名下只有同义词没有实际表,仅查 all_tables 返回空。
增加 all_synonyms 后 sbdev 输入 imp_basicinfo. 时能提示 AC01/AC02。
2026-07-15 17:29:42 +08:00
yx755141
1bf8271562 fix: 去掉OWNER=USER限制,ALL_SYNONYMS本身已权限过滤 2026-07-15 16:58:07 +08:00
tianqijiuyun-latiao
8a5209c084 ️ perf(data-grid): 优化大数据量横向滚动性能 2026-07-15 15:26:53 +08:00
tianqijiuyun-latiao
89c25e3019 🐛 fix(updater): 阻止 Windows 多实例并发安装更新 2026-07-15 15:26:53 +08:00
辣条
c8e7495f50 查询同义词,查询所有有权限的,不止当前租户 (#653) 2026-07-15 14:25:27 +08:00
yx755141
1b4e47d709 查询同义词,查询所有有权限的,不止当前租户 2026-07-15 14:20:57 +08:00
Syngnat
6548f939a2 🎨 style(brand): 替换 GoNavi 品牌 Logo 为 orbit-core
- 更新仓库根与前端 public 的 logo.svg 为轨道核心标识
- 同步 build/appicon.png(1024)及 macOS icns、Windows ico
- 视觉语义:核心圆为数据中枢,双轨道为跨库导航,卫星点为连接节点
2026-07-15 13:58:44 +08:00
tianqijiuyun-latiao
1458b57c0d Merge branch 'refs/heads/dev' into feature/20260602_connection_driver_i18n 2026-07-15 13:32:04 +08:00
tianqijiuyun-latiao
1c1adb8331 🐛 fix(redis): 防止命名空间行误触批量选择 2026-07-15 13:31:48 +08:00
tianqijiuyun-latiao
6f65f9e7cc 🐛 fix(sidebar): 修复 OceanBase Oracle 只读对象加载 2026-07-15 13:17:40 +08:00
Syngnat
5b8e7b538c 🐛 fix(query-editor): 修复视图与同义词补全
- 跨用户视图补全避免重复 owner 前缀
- 按结果集字段名去重视图列候选
- 加载 Oracle 私有及 PUBLIC 同义词并解析目标字段
- 补充跨用户视图与同义词回归测试
2026-07-15 11:37:36 +08:00
Syngnat
a0bb19b8bb feat(sidebar): 支持自定义对象分类显示
新增侧栏对象分类开关与仅显示表快捷设置
配置持久化并即时应用到所有连接
补充筛选、持久化与国际化回归测试
2026-07-14 21:53:57 +08:00
Syngnat
3fa4f8e900 🐛 fix(query-editor): 修复 OceanBase 视图查询与输入丢字
- 仅对元数据确认的 OceanBase Oracle 基表注入 ROWID
- 视图和同义词查询改为只读,避免触发 OBE-01445
- 在 SQL 补全中加入普通视图和物化视图候选
- 修复 beforeinput 已拦截时首个可打印字符被跳过的问题
- 补充视图、同义词和输入回归测试
2026-07-14 17:11:07 +08:00
Syngnat
8a1c1842ee feat(saved-query): 支持多级已存查询分组
- 新增已存 SQL 分组的持久化模型与后端管理接口
- 支持创建、编辑、嵌套、排序、移动和删除分组
- 在侧栏树和菜单中提供分组管理与未分组查询视图
- 同步 Wails 绑定、六种语言文案及前后端回归测试
2026-07-14 17:09:27 +08:00
Syngnat
7886cdc613 🐛 fix(sidebar): 排除已归属连接的分组选项
- 创建分组时仅展示未归属其他分组的连接
- 编辑分组时保留当前分组已有成员
- 无可选连接时显示明确空状态并补充多语言文案
- 增加连接筛选回归测试
2026-07-14 17:06:35 +08:00
Syngnat
454b6e0236 🐛 fix(dameng): 修复自增字段识别
通过达梦系统列元数据识别 IDENTITY 与 AUTO_INCREMENT。\n\n系统表不可访问时保留基础字段查询,并补充调用链回归测试。
2026-07-14 14:17:30 +08:00
Syngnat
c1e07dddaa feat(sidebar): 支持多层 Host 分组
- 支持分组父子层级与 Host/子分组混排排序
- 兼容旧分组配置并清理循环引用和重复归属
- 增加子分组创建、父分组选项及层级拖拽
- 删除分组时原位提升直属 Host 和子分组
2026-07-14 13:43:54 +08:00
Syngnat
1b3137d947 🐛 fix(transaction): 修复匿名SQL块自动提交
- 区分匿名 BEGIN...END 块与显式事务控制语句
- 统一前后端方言判定并挂起 Oracle、达梦和 SQL Server 块内 DML
- 补充编辑器、事务控制器及 SQL Server 回滚链路回归测试
2026-07-14 12:22:07 +08:00
Syngnat
72423e80a8 🐛 fix(datagrid): 修复 macOS 横向滚动拖拽抖动
- 拖动期间延迟虚拟表异步视觉重绘,减少重复样式写入
- 使用滚动空闲窗口保护外部原生滑块,避免偏移回写
- 松开拖拽后统一对齐表头、表体和滑块
- 补充横向滚动同步回归断言
2026-07-14 11:46:25 +08:00
Syngnat
5fd12d5dd8 🐛 fix(update): 同步 Windows Portable 更新版本
- 版本化 Portable 更新后切换到新包文件名并清理旧文件
- 下载完成进度采用后端实际返回的版本与资产
- 补充前端竞态和 Windows PowerShell 集成回归测试
2026-07-14 10:02:36 +08:00
Syngnat
c9ddd8919e feat(workbench): 丰富主页快捷资源与工作入口 2026-07-13 22:52:01 +08:00
Syngnat
b22a0894b8 🐛 fix(update): 修复隐藏下载进度后弹窗重开 2026-07-13 21:54:43 +08:00
Syngnat
e17e21b105 feat(workbench): 支持 Schema 筛选和历史快捷打开 2026-07-13 21:54:10 +08:00
Syngnat
57172f1296 feat(mcp-http): 持久化服务开关并支持启动恢复
保存启用偏好及运行参数,重启自动恢复;分离运行态与启用意图,补齐失败重试、并发收敛和回归测试。
2026-07-13 20:23:54 +08:00
Syngnat
2d5f246486 🐛 fix(kafka): 修复主题预览偏移量越界
- 使用绝对 offset 定位 Kafka 分区消息
- 避免 earliest offset 非零时重复叠加
- 增加直连预览读取的回归测试
2026-07-13 17:52:26 +08:00
Syngnat
0077c6dba7 ️ perf(sqlserver): 延长空闲连接复用窗口
- 将 SQL Server 单条空闲连接保留时间由 30 秒延长至 30 分钟
- 保持最大连接数和 30 分钟生命周期轮换策略不变
- Oracle、OceanBase 与其他数据库继续沿用原连接池策略
- 增加空闲窗口与连接复用边界回归测试
2026-07-13 17:30:23 +08:00
Syngnat
1423ac0902 🐛 fix(sql-editor): 修复退出保存重复生成查询副本
- 退出保存临时查询时复用原查询页签 ID
- 重启后通过稳定 ID 识别已经保存的同一条查询
- 增加连续退出不重复生成历史副本的回归测试
2026-07-13 17:30:02 +08:00
Syngnat
6958299ec2 🐛 fix(oceanbase): 修复 Oracle 模式单元格事务提交
- 单元格变更提交与预览使用所选 schema 限定目标表
- 对限定表名按分段方式引用,避免将 schema 与表名整体转义
- 为全列定位条件中的日期和时间值补充显式 Oracle 转换
- 增加 OceanBase Oracle 事务提交与 SQL 预览回归测试
2026-07-13 17:29:41 +08:00
Syngnat
6329345e73 🐛 fix(query-results): 修复分页统计与字段排序交互
- 为分页栏显示明确的首页和尾页入口,并接入真实的总数统计与取消回调
- 支持精确总数查询并隔离取消请求和过期响应,翻页时保留已统计总数
- 完整结果采用本地稳定排序,分页结果按数据库方言重新查询第一页
- 修复单元格编辑模式阻断列头排序箭头并补充回归测试
2026-07-13 17:29:15 +08:00
Syngnat
ef0845a9c5 🐛 fix(tabs): 修复短连接标签占用固定间距
- 将连接标签由固定 flex 基准改为按内容宽度收缩
- 保留 92px 最大宽度以避免长连接名称撑开标签
- 增加短连接名称间距回归测试
2026-07-13 17:28:33 +08:00
Syngnat
509962a773 feat(pagination): 增加首尾跳转并恢复查询翻页
- 自动限行结果保持未知总数语义并恢复后续页探测
- V2 与旧版分页增加首页和尾页快捷按钮
- 未知总页数时禁用尾页避免跳转到推算页
- 补齐六种语言文案及分页边界回归测试
2026-07-13 12:56:35 +08:00
Syngnat
b9e890379e feat(ai): 支持 OpenAI Responses 协议
- 设置中心为 OpenAI 预设增加 Chat 与 Responses 协议选择
- 实现 Responses API 同步、流式、推理摘要及工具调用适配
- 支持会话状态回放与上游能力不兼容时的自动降级
- 完善地址、模型列表和思考强度识别并补充回归测试
2026-07-13 12:55:58 +08:00
Syngnat
933e601816 🐛 fix(snippet): 保留代码片段尾部空白
- 持久化时完整保留代码片段正文中的尾部空格与换行
- 继续拒绝仅包含空白字符的无效片段
- 补充保存、存储及重新加载后的正文一致性测试
2026-07-13 12:55:22 +08:00
Syngnat
b23e768973 🐛 fix(query-editor): 修复 Oracle 序列查询自动限行报错
- 识别可执行 SQL 中的 NEXTVAL 与 CURRVAL 序列伪列
- Oracle、达梦及 OceanBase Oracle 序列查询跳过 ROWNUM 子查询包装
- 排除字符串和注释中的同名文本误判
- 补充自动限行与真实执行链路回归测试
2026-07-13 12:54:43 +08:00
Syngnat
4bee3cefa6 ️ perf(query-editor): 优化输入与选区交互流畅度
- 为 Monaco 编辑器及语言服务接入独立 Worker
- 限制大规模对象元数据与列装饰扫描范围
- 仅在按下导航修饰键时更新悬停状态
- 避免同一数据库上下文重复重建装饰并补充回归测试
2026-07-13 12:54:02 +08:00
Syngnat
1ae2b74279 🐛 fix(sql-parser): 修复尾随注释导致事务与只读判定异常
- 过滤分号后的纯注释语句并保留数据库可执行版本注释
- 前后端按数据库方言统一处理双横线、井号与块注释
- 修复事务选择、只读保护、SQL 审计及 AI 风险分析误判
- 补充流式 SQL、事务执行与方言解析回归测试
2026-07-13 12:52:56 +08:00
Syngnat
00473e7ac0 🐛 fix(theme): 提升连接状态与 SQL 关键字辨识度
- 为亮暗主题和内置预设增加独立连接状态语义色
- 强化 Host、数据库状态点及双行页签字段对比度
- 为 Monaco SQL 关键字补充高对比加粗规则
- 增加基础主题与内置预设的对比度回归测试
2026-07-13 09:54:23 +08:00
Syngnat
7bdce19d01 feat(release): 发布 Windows Portable 与 MSI 双产物
- Windows AMD64 与 ARM64 同时生成 Portable EXE 和 MSI 安装包
- 统一 stable 与 dev 的 MSI 产品身份并支持通道切换升级
- 发布清单同时包含两类资产及对应 SHA256 校验信息
- 在 Windows workflow 中执行双模式更新与 WiX 契约测试
2026-07-13 09:25:21 +08:00
Syngnat
431f740476 feat(update-ui): 区分 MSI 与 Portable 更新交互
- 接收后端安装方式与包类型并隔离同版本更新缓存
- 按 MSI 与 Portable 动态展示下载、安装和重启状态
- 保留未保存 SQL 的退出保护并统一两处安装入口
- 补齐六语言文案及前端更新交互测试
2026-07-13 09:24:44 +08:00
Syngnat
6e2e3afc85 feat(update): 支持 Windows MSI 与 Portable 分流更新
- 根据当前安装标记精确选择 MSI 或 Portable 更新包
- MSI 更新下载到用户缓存并通过 msiexec 保留原目录安装重启
- 下载与安装前校验包类型并隔离不同安装方式的缓存
- 补充安装模式、脚本执行和更新清理测试
2026-07-13 09:24:13 +08:00
Syngnat
4d90ed776f 🐛 fix(sql-audit): 修复固定操作列悬停内容穿透
- 覆盖审计表格固定操作列的不透明背景
- 使用不透明混合色保留行悬停反馈
- 增加固定列内容穿透回归测试
2026-07-12 16:28:17 +08:00
Syngnat
7c8e8d8dd3 feat(sql-audit): 新增 SQL 审计中心并完善事务追踪
- 新增脱敏审计存储、筛选、保留策略、完整性校验与 JSON/CSV 导出
- 覆盖查询编辑器、事务、导入同步、对象操作、AI/MCP 与 Web 运行时入口
- 完善事务完整语句日志、Windows 快捷键映射及 SQL 分析布局
- 补充多语言、健康状态、数据目录迁移和回归测试
2026-07-12 15:24:27 +08:00
Syngnat
0b33b3a683 🐛 fix(query-editor): 修复 SQL Server 多查询冗余影响行数结果
- 将 SQL Server 成对返回的数据结果与 affectedRows 归属同一语句
- 保留 DML 影响行数与存储过程真实多结果集
- 补充后端正反用例及 V2 结果标签回归测试
2026-07-12 09:37:29 +08:00
Syngnat
ef5e344c4f feat(theme): 新增自定义主题与多套低刺激预设
- 支持导入、管理和持久化自定义 CSS 主题
- 新增六套内置主题并提供低刺激舒适暗色
- 完善 V2 主题令牌、多语言、可访问性与回归测试
2026-07-11 23:50:24 +08:00
Syngnat
034e968888 feat(sql-analysis): 完善慢 SQL 监控与执行计划诊断
- 完善慢查询采集、聚合、迁移与跨进程持久化

- 加固只读诊断边界并补齐多数据库执行计划解析

- 优化慢 SQL 面板、计划图、响应式布局与交互体验

- 补充后端、前端及 SQL Server 会话回归测试
2026-07-11 16:25:07 +08:00
Syngnat
5c80f6e9a2 ️ perf(sidebar): 优化左侧树搜索响应
延迟筛选配置持久化,避免输入期间触发全局状态更新。

稳定树标题渲染回调,减少可见节点重复协调。

补充搜索持久化与标题渲染回归测试。
2026-07-11 14:33:15 +08:00
Syngnat
cf979f436c feat(data-grid): 支持表头整列选择
- 单元格编辑模式下点击可写列表头选中整列数据

- 保留排序、筛选、外键跳转和列宽调整交互

- 补充表头整列选择回归测试

Fixes #622
2026-07-10 22:37:39 +08:00
Syngnat
328c9dbb28 feat(redis): 支持 List 单个值删除
- 为 Redis List 数据行增加删除操作与确认提示

- 新增 RedisListRemove RPC,使用 LREM 删除一个匹配元素

- 补充 Redis 命令、前端交互和本地化回归测试

Fixes #639
2026-07-10 22:21:08 +08:00
Syngnat
9870959ca6 🐛 fix(ui): 修复旧版编辑器布局并迁移默认界面至 V2
- 修复旧版 SQL 编辑器在弹性布局中的遮挡问题

- 将既有用户的界面偏好一次性迁移至 V2 并保留后续手动选择

- 覆盖持久化迁移与旧版编辑器布局回归测试
2026-07-10 22:00:05 +08:00
Syngnat
1ac9103735 🐛 fix(data-grid): 补全数据编辑事务语句 2026-07-10 21:21:42 +08:00
Syngnat
69be54c1cf 🐛 fix(web): 补齐浏览器端连接导入导出 2026-07-10 20:57:44 +08:00
Syngnat
689467ec91 🐛 fix(datagrid): 修复结果横向滚动条拖拽卡顿
- 合并同一帧内的外部横向滚动位置,避免每个原生事件触发布局写入
- 拖拽期间禁止虚拟表异步偏移反写外部滑块,松开后统一对齐
- 覆盖丢失指针捕获、窗口失焦、结果集隐藏和卸载时的同步状态清理
- 补充横向滚动同步回归断言
2026-07-10 18:10:27 +08:00
Syngnat
be09808d1f 🐛 fix(query-editor): 修复快捷执行与 Windows 输入丢字
- 快捷执行仅由 Monaco 编辑器处理,保留 Shift+Home 反向选区和光标位置
- 缓冲并恢复 Windows 下丢失的可打印输入和延迟 IME 提交
- 降低 AI 行内补全预检与元数据刷新造成的编辑器输入抖动
- 补充快捷键、输入回退和 AI 补全回归测试
2026-07-10 18:09:56 +08:00
Syngnat
18f40753dd feat(milvus): 新增 Milvus 向量数据库支持
- 内置 REST v2 驱动,支持集合浏览、向量检索和实体写入
- 支持认证、TLS、SSH 代理及多数据库连接切换
- 增加连接配置、图标、多语言文案和使用说明
- 补齐 JSON 查询路由与前后端回归测试
2026-07-10 15:37:54 +08:00
Syngnat
b23c1ba932 🐛 fix(query-results): 修复复杂查询固定列失效
为无物理表引用的结果集生成独立固定列作用域。

右键固定和取消固定不再依赖 tableName,保留只读编辑边界。

覆盖多行单表和关联查询的固定列回归。
2026-07-10 14:46:38 +08:00
Syngnat
e1fb5b12d4 🐛 fix(update): 修复 Windows 在线更新中文路径失效
- 将 Windows 更新脚本统一为 PowerShell,并通过 Unicode 环境变量传递中文与特殊字符路径
- 保留当前可执行文件路径,补充替换重试、启动健康检查、失败回滚和严格 ZIP 选择
- 更新前复用未保存 SQL 确认流程,并延长退出兜底时间
- 新增 Windows 原生集成测试并接入 dev/release 构建
2026-07-10 13:31:34 +08:00
Syngnat
06bfbc4a84 🐛 fix(window): 修复 Windows 后台可见时输入卡顿
- Windows 默认关闭透明 WebView、窗口透明与 Acrylic 背景
- 保留 macOS 透明效果并补充跨平台窗口视觉配置回归测试
- 同步更新多语言性能模式提示与开发文档
2026-07-10 11:16:34 +08:00
Syngnat
8d186fc066 feat(web-auth): 支持六位密码与环境变量托管
- 密码策略统一为至少六个可见字符,覆盖初始化与设置中心改密
- 支持 GONAVI_WEB_PASSWORD 启动同步并保留既有 2FA 与会话配置
- 设置中心识别环境托管状态并禁用临时改密
- Compose、env 示例及双语文档补充密码配置和容器重建说明
- 增加认证持久化、环境覆盖、前端状态与 Docker 登录回归校验
2026-07-10 10:34:03 +08:00
Syngnat
0a9aec5929 🐛 fix(docker): 修复容器驱动重装提示不消失
- Web 与 MCP 镜像编译前按目标平台生成驱动 revision
- 校验生成步骤位于源码复制后和 Go 编译前
- Docker 镜像工作流增加运行时 Dockerfile 回归检查
2026-07-10 10:04:38 +08:00
Syngnat
155ba0f1f7 🐛 fix(mcp): 修复在线更新后外部客户端连接失效
- 启动时自动迁移 Claude Code 与 Codex 中失效的 GoNavi MCP 路径
- 兼容 Windows 版本化 EXE 改名及旧进程占用导致的残留路径
- 限制迁移范围,保留仍有效的稳定路径与自定义命令
- 补充路径迁移和误覆盖边界回归测试
2026-07-10 09:44:26 +08:00
Syngnat
52592ffd6c 🐛 fix(query-editor): 修复 Oracle 查询自动限行语法
- 将 Oracle/Dameng 查询编辑器自动限行从 FETCH FIRST 改为 ROWNUM 兼容写法

- 识别 ROWNUM 自动封顶 SQL,避免结果分页继续基于截断 SQL 查询

- 补充自动限行与结果分页回归测试,覆盖 Oracle/Dameng 分支
2026-07-09 17:37:09 +08:00
Syngnat
92aedde121 🐛 fix(db): 优化 SQLServer 首次查询耗时
- 为 SQLServer 连接池保留 1 条空闲连接,减少反复冷连接开销

- 复用 Oracle/OceanBase 的默认空闲连接常量,其他默认数据库池策略保持不变

- 新增连接池行为测试,覆盖 SQLServer 复用与默认池关闭 idle 连接
2026-07-09 17:18:12 +08:00
Syngnat
b2df94a3ba 🐛 fix(window): 修复 mac 窗口恢复越界 (#643)
## 问题描述

macOS 多屏环境下,普通窗口恢复/修正位置时可能被定位到当前屏幕外,尤其是在外接屏位于主屏左侧或上方、浏览器
`screen.availLeft/availTop` 出现全局偏移时更容易触发。

## 复现条件说明

该问题不是所有 macOS 用户都能稳定复现,更依赖特定显示器布局和窗口状态。高概率触发条件包括:

- 使用 macOS 多屏。
- 外接屏位于主屏左侧或上方,导致 `screen.availLeft` / `screen.availTop` 为负数或非零。
- GoNavi 处于普通窗口态,而不是最大化或全屏。
- 应用保存过窗口位置,并在重启、切屏、拔插显示器、focus/resize 后触发窗口 bounds 恢复或修正。

单屏 mac、外接屏仅位于主屏右侧、始终最大化/全屏打开,或未保存过异常窗口位置的用户通常不容易触发。

## 根本原因

Wails 的 `WindowSetPosition(x, y)` 在 macOS 上接收的是当前 monitor
可见区域内的局部坐标,而不是全局桌面坐标。原逻辑把 `window.screen.availLeft/availTop`
作为可见区起点传入恢复算法,导致保存的局部坐标和恢复时的全局偏移混用。

## 修复方案

- 新增 `resolveWailsWindowVisibleViewport`,集中处理 Wails 主窗口恢复使用的可见区域。
- macOS 下将可见区原点固定为当前 monitor 局部 `(0, 0)`,避免把浏览器 screen 全局偏移继续传给 Wails。
- 补充单元测试覆盖负 `availLeft` 的外接屏场景。
- 更新 App 窗口恢复源码断言测试。

## 变更文件

| 文件 | 修改说明 | 变更行数 |
|---|---|---|
| `frontend/src/App.tsx` | macOS 下使用 Wails 局部坐标解析窗口可见区 | +7 -7 |
| `frontend/src/utils/wailsWindowViewport.ts` | 新增 Wails 窗口可见区解析工具 | +52
|
| `frontend/src/utils/wailsWindowViewport.test.ts` | 覆盖 macOS 局部原点和
fallback 场景 | +43 |
| `frontend/src/App.tool-center.test.ts` | 同步窗口恢复源码断言 | +2 -1 |

## 合并注意事项

- **影响功能点**:桌面端普通窗口启动恢复、focus/resize 后窗口 bounds 修正。
- **验证方式**:
- `npm test -- --run src/utils/wailsWindowViewport.test.ts
src/utils/windowRestoreBounds.test.ts
src/utils/windowStartupLayout.test.ts src/App.tool-center.test.ts`
  - `npm run build`
- **回归风险**:低。变更集中在窗口可见区解析;非 macOS 平台保留原 `screen.availLeft/availTop` 处理。
- **回滚方式**:revert 此 PR 即可。
2026-07-09 16:31:44 +08:00
Syngnat
19572a3cc9 feature(query-result): 支持SQL执行结果差异比对 2026-07-09 16:28:59 +08:00
Syngnat
8d7e7a6ddf feat(query-editor): 结果页列元数据跨库加载并实心化保存按钮
- 结果集携带 metadataDbName/TableName,列类型注释从真实库表加载
- 只读查询也解析 tableRef,避免结果页丢元数据
- 选择列底色与固定列对齐;保存按钮改为蓝色实心主按钮样式
- 独立结果窗/还原路径同步元数据字段
2026-07-09 15:37:27 +08:00
Syngnat
af9f490172 feat(datagrid): 完善左固定列体验并统一选中主题色
- 虚拟表横滚:表头 sticky + 表体 translateX 补偿,勾选/行号/钉住列同步固定
- 支持列头钉住左侧、钉住列显示图标;序号列可拖宽且不参与排序
- 行选中与单元格选区统一 V2 绿色,固定列底色对齐 --gn-bg-panel
- 查询结果页启用列头筛选,并在客户端按条件过滤当前结果集
2026-07-09 15:37:20 +08:00
DurianPankek
099443fddc 🐛 fix(window): 修复 mac 窗口恢复越界 2026-07-09 15:25:48 +08:00
Syngnat
947277bdec 🐛 fix(query-editor): 修复限定名超链接并支持跨库/schema 解析
- 两段名兼容 MySQL 库.表与 PG/SQL Server 的 schema.表
- 三段名支持 database.schema.object,常见 schema 不当成库拉取
- SQL 变更时按引用库防抖补拉元数据并刷新装饰
- 修复元数据 key 提前占死导致取消后不再拉取、超链接全灭
- 补充限定名解析与收集逻辑单测
2026-07-09 14:23:09 +08:00
Syngnat
b1bd4bb5ec feat(datagrid): 数据预览与 SQL 结果支持行号显示及设置开关
- 新增 appearance.showDataTableRowNumber,默认开启,主题设置可切换
- 数据预览与查询结果表统一跟随该偏好显示行号列
- 行号列收窄为固定窄宽,少列时多余宽度只加到数据列避免撑宽
- 补充 i18n 与相关单测
2026-07-09 14:23:09 +08:00
Syngnat
d210b3f39c 🐛 fix(window): 加固 Windows 冷启动半窗,原生最大化并兜底铺满工作区
- Windows 启动 WindowStartState 改为 Maximised,默认尺寸提升到 1440×900
- 无记忆/历史 1024×768/覆盖率过低的普通窗记忆改为启动最大化
- Maximise 多次失败时铺满工作区兜底,避免残留浮动半窗
- 用户刻意拉大的普通窗仍先 Unmaximise 再恢复尺寸位置
2026-07-09 14:02:53 +08:00
Syngnat
901b436107 🐛 fix(connection-package): 导出/导入连接包携带 Redis DB 别名
- 连接包 payload 与导出选项支持 redisDbAliases(连接 ID → db 序号 → 别名)
- 导入返回 ConnectionPackageImportResult,一并恢复别名映射
- 前端导出时注入 appearance.redisDbAliases,导入后 merge 回 appearance
- 补充别名往返测试与 mergeRedisDbAliases 单元测试
2026-07-09 13:57:32 +08:00
Syngnat
097826508a 🐛 fix(update): 修复 dev→latest 安装后丢包且仍停在旧版的问题
- 安装成功且 relaunch 发出后再删除下载包,失败保留 latest 包便于手动安装
- macOS open 使用路径形式,避免 -a 传完整路径导致重启失败
- 明确覆盖「当前运行的 .app」(桌面 dev 包),避免误以为只更新了 Applications
- Windows 同样先启动目标再删 SOURCE,防止重启失败时只剩旧包
2026-07-09 13:45:17 +08:00
Syngnat
cdfbdd5dd5 feat(update): 下载进度到 100% 后由用户点击「重启应用更新」
- 取消 macOS 下载完成后自动安装/退出,Win/Mac 统一交互
- 进度弹窗在 100% 显示就绪提示,主按钮改为「重启应用更新」
- 关于页安装入口同步文案;点击后才执行安装脚本并重启
- 补充多语言与前端回归测试
2026-07-09 13:38:07 +08:00
Syngnat
d31bb978f2 🐛 fix(update/macos): 加固在线更新自动安装脚本与进程隔离
- 安装脚本 Setsid 脱离主进程,避免 Quit 后脚本被连带结束
- 等待宿主退出增加 120s 超时,防止无限挂起
- 支持 dmg/zip 解包定位 .app,稳健解析 Contents/MacOS 可执行文件
- 不再 rm -rf 脚本所在 STAGED 目录,降低安装中途自删风险
- 替换失败明确走管理员权限回退,增强 relaunch(open -n -a / nohup)
- 补充 macOS 更新脚本单测
2026-07-09 13:35:41 +08:00
Syngnat
d067e2c00a feat(update): dev 通道同步生成 latest-dev.json 静态清单
- dev-build 发版流程上传 latest-dev.json 到 dev-latest Release
- 客户端 dev 通道优先读取 releases/download/dev-latest/latest-dev.json
- build-release 本地打包按版本自动选择 latest / latest-dev
- SHA256SUMS 生成时跳过清单文件本身
2026-07-09 12:38:18 +08:00
Syngnat
3beab030f4 feat(update): 检查更新优先静态 latest.json,避免用户撞 GitHub API 限流
- 客户端优先下载 releases/latest/download/latest.json,再回退 REST API
- 成功结果写入磁盘缓存,限流/断网时可 stale 回退
- 静默检查增加节流;限流文案改为用户无需配置 Token
- CI 与 build-release 发版时生成 latest.json 清单
- 补充静态清单/缓存/节流单测与生成脚本测试
2026-07-09 12:35:12 +08:00
Syngnat
566339f09f 🐛 fix(window): 加固 Windows 启动最大化与窗口尺寸记忆落盘
- windowState/windowBounds 与启动偏好一致,立即写入本地持久化
- 启动最大化首次立即执行并加重试,成功后固化 maximized 状态
- 仅在 hydration 完成后恢复,避免默认 1024×768 半窗被误记
- 补充 store 与启动恢复相关测试断言
2026-07-09 12:35:12 +08:00
Syngnat
dec9b8317c ♻️ refactor(settings/ai): 聊天入口打开 AI 设置改走设置中心
- 从 AI 聊天面板打开设置时进入设置中心服务-AI 页
- 移除独立 AISettingsModal 挂载,复用 AISettingsContent
- 安全更新修复跳转同步走设置中心,关闭后可回到修复流程
- 更新设置中心与工具中心相关源码级测试断言
2026-07-09 12:19:22 +08:00
Syngnat
c77d338ab1 feat(ai-chat): 支持侧栏/独立窗默认打开、双向切换与尺寸记忆
- 新增 AI 独立浮动窗,标题栏可拖拽缩放并与侧栏双向切换
- 设置中可配置默认打开方式(侧栏/独立窗),偏好持久化
- 记忆独立窗上次位置与尺寸,再次打开自动恢复
- 优化输入区默认高度与底部模型/思考/用量工具条布局
- 去掉独立窗外层重复标题栏,由面板 header 统一承载操作
2026-07-09 12:18:51 +08:00
Syngnat
bc3f76dd73 feat(ai-chat): 思考强度移入聊天面板并按次请求下发
- 面板底部新增思考强度选择,按服务商 profile 动态选项
- ChatSendOptions 支持 thinkingIntensity,仅本次发送生效
- 从供应商表单移除思考强度配置,避免与面板控制重复
- 补齐多语言文案与相关前后端测试
2026-07-09 12:17:40 +08:00
Syngnat
4fcd7ff61f feat(ai): 支持 MCP 少量查数与分服务商思考强度,修复 DeepSeek Anthropic
- MCP HTTP 默认可注册 execute_sql,行数默认 50/上限 200,设置页可切换仅结构模式
- 思考强度按 OpenAI/Anthropic/DeepSeek/Gemini 体系映射档位与请求参数
- 自定义端点 DeepSeek Anthropic 自动纠正 base 路径并回传 thinking 块
- 补充前后端回归测试与多语言文案
2026-07-09 11:34:38 +08:00
Syngnat
f930ffe153 🐛 fix(docker/web-server): 修复前端构建缺失 shared/i18n 导致 CI 失败
- frontend 阶段改为 monorepo 布局 WORKDIR /src/frontend
- COPY shared/ 到 /src/shared,恢复 catalog/messages/translate 相对路径解析
- dist 产物路径同步为 /src/frontend/dist
- 新增 Dockerfile 布局回归校验脚本,避免再次漏拷 shared
2026-07-09 11:16:30 +08:00
Syngnat
26c22379ae 🐛 fix(window/theme/updater/query-editor): 修复 Windows 冷启动半窗与主题/更新/补全体验
- 窗口:最大化恢复加重试与启动宽限,无记忆尺寸时 Windows 居中大窗,冷启动主动 WebView2 settle
- 主题:v2 设置中心主题外观改扁平自定义控件,legacy 保持侧栏卡片布局
- 更新:启动发现更新改开设置中心关于页,下载失败支持 assets API 回退并硬化检查链路
- 补全:suggest 列表字段名优先完整显示,元数据空间不足时再省略
- 测试:补充 window/theme/updater/补全相关回归断言
2026-07-09 11:13:42 +08:00
Syngnat
4c9bff5e8c feat(web-server): 支持 Docker 部署浏览器 Web UI
- 新增 Dockerfile.web-server:前端构建 + 纯 Go 主程序 web-server 模式
- 提供 Compose/env 示例与本地源码构建 override
- CI 增加 gonavi-web-server 镜像构建、冒烟与 GHCR 发布
- 文档补充 Web 容器部署、数据卷与安全暴露说明
2026-07-09 09:56:56 +08:00
Syngnat
8808a5aa77 🐛 fix(dameng): 修复设计表加载字段因 comment 保留字别名失败
- GetColumns/GetAllColumns 将 AS comment 改为 AS col_comment
- 达梦 COMMENT 为保留字,AS comment 会触发 Error -2007 语法分析出错
- 结果映射优先读取 COL_COMMENT,兼容 COMMENT/COMMENTS
- 补充单测锁定禁止使用 AS comment 别名
2026-07-09 09:48:52 +08:00
Syngnat
6fb6674803 feat(tab/window): 完善Tab拖出独立窗口跟手预览与入场动效
- 结果Tab支持拖出拆分,拖拽过程抑制文本选中
- 抽取 DetachDragPreview:进度条/跟手预览/阈值高亮
- 工作区主Tab拖出复用同一套预览动效,松手位置跟手
- 浮动窗增加 scale 入场动画,增强拆出反馈
2026-07-09 09:44:53 +08:00
Syngnat
c1cf19ef67 🐛 fix(sidebar/kingbase): 修复函数列表重复叠加导致越刷越多
- 元数据 fallback:完整目录查询首次成功后停止,避免多条 SQL 结果叠加
- 函数去重:按 schema+name+type 大小写不敏感去重,折叠同名 overload
- 树节点 key 区分 func/proc,避免同名函数/过程 key 冲突引发虚拟列表刷屏
- 补充 Kingbase/MySQL 函数元数据加载单测锁定回归
2026-07-09 09:20:33 +08:00
Syngnat
de868a2763 feat(tab/window): 支持主工作区与结果Tab拆出为应用内浮动窗口并还原
- 新增 detached 会话态:主工作区窗口与查询结果窗口独立管理
- 主Tab支持右键拆出与垂直拖出,浮动窗可拖拽缩放并还原/关闭
- 结果Tab右键拆出为浮动结果窗,支持还原回源查询结果区
- 抽离 WorkbenchTabContent,QueryEditor 结果会话缓存避免拆出丢失
- 补充六语言文案与 detach/store 相关单元测试
2026-07-09 09:08:50 +08:00
Syngnat
5fc33102a9 🐛 fix(updater): 修复关于页正式版信息兜底
- 为关于页补充固定项目元数据兜底
- 过滤 Unknown/未知 等无效版本与作者信息
- 增加正式版 AppInfo 缺失时的回归测试
2026-07-08 21:37:10 +08:00
Syngnat
f74ad02b9d 🐛 fix(query-editor): 限制 macOS 搜索仅响应 Cmd+F
- 拦截 Monaco 内置 Cmd+E find-with-selection 行为
- 保留默认 Cmd+E 当前语句选择逻辑,禁用或改键时不触发搜索
- 增加 SQL 编辑器快捷键回归测试并加固 Monaco 测试包装
2026-07-08 20:30:31 +08:00
Syngnat
dca1e8a251 feat(updater): 优化关于页更新信息展示
- 关于页直接进入 GoNavi 内容详情,减少二次点击
- 将更新通道改为分段选择器并优化描述布局
- 保留后端发布发布时间元数据并在版本信息中展示
2026-07-08 20:30:05 +08:00
Syngnat
22fab86010 🐛 fix(ui): 修复数据表按钮与侧栏滚动条显示
- 修复 v2 分页“统计总数”按钮被压缩为图标宽度的问题

- 首次打开时隐藏树主体横向溢出,保留独立横向滚动条占位

- 将自定义字段显示里的全局隐藏列文案改为多语言翻译

- 增加数据表按钮和侧栏横向滚动条布局回归断言
2026-07-08 17:37:21 +08:00
Syngnat
da895ea502 feat(settings): 统一设置中心内页与代理交互
- 将主题设置、AI 设置和关于页纳入设置中心详情页,补齐返回和取消交互

- 重构全局代理设置为草稿应用模式,新增常用预设、URL 测试入口和测试结果展示

- 按参考样式重做关于页版本与项目入口布局,并收紧顶部品牌卡尺寸

- 提取 AISettingsContent 供设置中心内嵌复用,控制详情页内部滚动边界

- 补齐多语言文案和设置中心静态回归断言
2026-07-08 17:37:05 +08:00
Syngnat
b94168957e 🐛 fix(update/proxy): 稳定更新检查与代理运行时
- 更新检查前恢复持久化全局代理,避免重启后代理状态未进入运行时

- 将 DNS、EOF 和临时网络异常转为本地化错误,并对可重试网络错误做短重试

- 优先读取 GitHub asset digest,缺失时再回退 SHA256SUMS 校验文件

- 新增全局代理连接测试后端接口,支持草稿代理、已保存密码复用和清除密码意图

- 更新 Wails 绑定、浏览器 mock 与代理草稿序列化测试
2026-07-08 17:36:42 +08:00
Syngnat
c792a30932 🐛 fix(query-result): 修复SQL执行后结果面板崩溃
- 提前解析当前激活结果页 key
- 避免构造结果 tab 时读取未初始化变量
- 修复 MySQL SELECT 成功返回后编辑器闪退问题
2026-07-08 14:16:36 +08:00
Syngnat
dd01d1c3f9 fix(update): auto-install macOS updates after download 2026-07-08 13:49:00 +08:00
Syngnat
23c895e839 test(update): auto install macOS package after download 2026-07-08 13:43:13 +08:00
Syngnat
7a5960e737 fix(update): auto install macOS package after download 2026-07-08 13:41:45 +08:00
Syngnat
5c6110a57e feat(query-result): support global hidden columns 2026-07-08 12:29:58 +08:00
Syngnat
17843ffd71 chore: remove temporary file 2026-07-08 12:26:21 +08:00
Syngnat
7296ab0425 tmp stop 2026-07-08 12:26:05 +08:00
Syngnat
47664023ee chore: remove temporary file 2026-07-08 12:25:57 +08:00
Syngnat
33918c6837 no 2026-07-08 12:25:42 +08:00
Syngnat
66a6052f88 chore: remove temporary file 2026-07-08 12:25:35 +08:00
Syngnat
31b5b838a2 chore: tmp create pr 2026-07-08 12:25:23 +08:00
Syngnat
575a3d88b9 chore: remove temporary PR trigger file 2026-07-08 12:25:07 +08:00
Syngnat
bef811ce13 tmp 2026-07-08 12:24:53 +08:00
Syngnat
dee5620fc9 test(data-grid): cover global hidden column helpers 2026-07-08 12:24:27 +08:00
Syngnat
b1174861d4 feat(query-result): apply global hidden columns 2026-07-08 12:24:12 +08:00
Syngnat
a1f296ee08 feat(data-grid): expose global hidden column configuration 2026-07-08 12:20:54 +08:00
Syngnat
3e34795ef5 feat(data-grid): add global hidden column storage helpers 2026-07-08 12:19:47 +08:00
Syngnat
f73d8dcb97 fix(oceanbase): tolerate Oracle sequence metadata differences 2026-07-08 12:18:44 +08:00
Syngnat
82ad393a05 perf(oceanbase): use FETCH FIRST for Oracle auto limit 2026-07-08 12:18:32 +08:00
Syngnat
257406120d fix(editor): recover dropped printable Monaco input 2026-07-08 12:18:20 +08:00
Syngnat
9ba3216fa8 test(oceanbase): cover Oracle sequence metadata fallbacks 2026-07-08 12:05:48 +08:00
Syngnat
fca3f0cffb fix(oceanbase): make Oracle sequence metadata queries tolerant 2026-07-08 12:05:14 +08:00
Syngnat
5712b39cf9 test(oceanbase): expect Oracle auto limit FETCH FIRST 2026-07-08 11:52:06 +08:00
Syngnat
07dc1436af perf(oceanbase): use FETCH FIRST for Oracle auto limit 2026-07-08 11:51:05 +08:00
Syngnat
9e52397732 fix(editor): recover dropped printable Monaco input 2026-07-08 11:35:00 +08:00
Syngnat
20ff4f3687 fix(query-result): show capped result counts as exact totals 2026-07-08 11:32:01 +08:00
Syngnat
73ce3039ce fix(oceanbase): improve query limit and Oracle object links 2026-07-08 11:31:29 +08:00
Syngnat
c27a038fc5 test(query-result): auto limit cap has exact page total 2026-07-08 11:08:03 +08:00
Syngnat
c3c4efc6ec fix(query-result): avoid not-counted summary for auto limit cap 2026-07-08 11:07:31 +08:00
Syngnat
61296bc855 chore: remove OceanBase follow-up note from focused fix 2026-07-08 10:44:43 +08:00
Syngnat
78da0076cf chore: remove temporary OceanBase follow-up note 2026-07-08 10:44:34 +08:00
Syngnat
279b38fec3 docs(oceanbase): note first implementation batch 2026-07-08 10:44:06 +08:00
Syngnat
0e348fcca4 test(query-editor): allow literal 100-row limit label 2026-07-08 10:43:46 +08:00
Syngnat
7d79fae84d fix(query-editor): use literal label for 100-row option 2026-07-08 10:43:02 +08:00
Syngnat
f642e0325d chore: remove temporary max rows i18n note 2026-07-08 10:40:20 +08:00
Syngnat
43a408f1c9 docs(query-editor): note 100-row option i18n key 2026-07-08 10:40:05 +08:00
Syngnat
b97b5cf6e2 docs(oceanbase): track remaining follow-up issues 2026-07-08 10:39:55 +08:00
Syngnat
6c269a99cf fix(oceanbase): support multi-part Oracle table links 2026-07-08 10:39:32 +08:00
Syngnat
24841f2dd9 test(query-editor): require 100-row limit label key 2026-07-08 10:37:10 +08:00
Syngnat
c2e60c1e52 feat(query-editor): add 100-row limit option 2026-07-08 10:36:38 +08:00
Syngnat
7a87d43bdc chore: remove temporary macOS CI annotation cleanup workflow 2026-07-08 09:43:21 +08:00
Syngnat
5fa1c90674 chore: trigger macOS CI annotation cleanup workflow 2026-07-08 09:42:18 +08:00
Syngnat
c79225ef27 chore: add temporary macOS CI annotation cleanup workflow 2026-07-08 09:41:44 +08:00
Syngnat
0bd8fdea1f chore: remove temporary macOS CI annotation cleanup workflow 2026-07-08 09:40:30 +08:00
Syngnat
18dbd043ae chore: temporarily apply macOS CI annotation cleanup 2026-07-08 09:39:28 +08:00
Syngnat
a7535dec2b fix(update): repair Go syntax in Windows update cleanup 2026-07-08 08:46:42 +08:00
Syngnat
dbd4b7f763 fix(update): repair Go multiline boolean expression 2026-07-08 08:44:15 +08:00
Syngnat
68eacbbcf7 fix(update): preserve latest Windows portable filename 2026-07-07 22:47:05 +08:00
Syngnat
cecefce84d fix(update): keep script builder test compatibility 2026-07-07 21:35:02 +08:00
Syngnat
2406c1ff71 fix(update): relaunch versioned portable exe with new filename 2026-07-07 21:28:21 +08:00
Syngnat
a4a0ac25cf fix(update): clean Windows update packages and logs 2026-07-07 20:54:13 +08:00
Syngnat
fbb23f0b9a fix(update): import logger for cleanup launcher 2026-07-07 20:50:57 +08:00
Syngnat
016e2bc70e chore(update): remove unused cleanup imports 2026-07-07 20:46:14 +08:00
Syngnat
070c2328e0 test(update): cover Windows cleanup helpers 2026-07-07 20:43:40 +08:00
Syngnat
c117da42e4 fix(update): clean staged Windows packages and logs 2026-07-07 20:42:15 +08:00
Syngnat
950856d86f 合并拉取请求 #627
fix: improve OceanBase Oracle performance, navigation, and count UX
2026-07-07 17:51:42 +08:00
Syngnat
93a3240862 fix(query-editor): fallback ctrl-click for OB Oracle schema tables 2026-07-07 17:47:45 +08:00
Syngnat
1116a3736b fix(data-grid): bridge pagination count action to toolbar 2026-07-07 17:45:37 +08:00
Syngnat
19af57e46b chore: remove temporary patch workflow 2026-07-07 17:36:48 +08:00
Syngnat
401c33c505 chore: apply OB Oracle performance navigation count patches 2026-07-07 17:33:59 +08:00
Syngnat
8c968c1a87 feat(data-grid): expose total count action in pagination 2026-07-07 17:31:53 +08:00
Syngnat
4fee58999d perf(db): keep one idle Oracle-style connection 2026-07-07 17:29:17 +08:00
Syngnat
bd0752df96 test(oceanbase): keep Oracle default timeout unset 2026-07-07 17:28:57 +08:00
Syngnat
47a15a6674 fix(oceanbase): stop inflating Oracle query timeout by default 2026-07-07 17:28:10 +08:00
Syngnat
2a64bad76e fix(oceanbase): reduce Oracle long-query bad connection failures 2026-07-07 16:56:05 +08:00
Syngnat
7ee8698165 test(query-editor): cover bad connection timeout semantics 2026-07-07 16:50:57 +08:00
Syngnat
9ae35e5fec fix(query-editor): classify driver bad connection as timeout 2026-07-07 16:49:39 +08:00
Syngnat
b618927454 test(oceanbase): cover Oracle query timeout default 2026-07-07 16:48:27 +08:00
Syngnat
e8ad34d2f0 fix(oceanbase): extend default Oracle query timeout 2026-07-07 16:47:50 +08:00
Syngnat
007fdc9a5d fix(oceanbase): set current schema for Oracle tenant queries 2026-07-07 15:44:14 +08:00
Syngnat
61b62fe5af test(oceanbase): cover Oracle current schema init 2026-07-07 15:41:22 +08:00
Syngnat
d574e6b396 fix(oceanbase): set Oracle current schema for selected tenant 2026-07-07 15:40:36 +08:00
Syngnat
f798b183e6 fix(query-editor): prevent AI inline completion from trapping cursor 2026-07-07 15:14:30 +08:00
Syngnat
e8ef4b824a fix(query-editor): prevent AI inline completion from trapping cursor 2026-07-07 15:10:45 +08:00
Syngnat
d87f8bf36a ♻️ perf(query-editor): 消除大库元数据下补全路径的全量扫描
延续上一轮 AI 补全卡顿修复:AI 元数据预热把整库列灌入共享缓存后,仍有三处热点会
阻塞主线程——buildQueryEditorAiContext 每次请求全量重建合并 Map、普通补全
findPreloadedColumns/relevantColumns 对全量列逐条正则扫描、ghost 可见期间
scroll/layout 每帧全量重渲染 overlay。改为:AI 上下文按依赖引用相等缓存(并保持
数组身份稳定使下游索引跨请求复用)、共享列按表名/标识符建 WeakMap 索引、
重定位用 rAF 合并。
2026-07-06 22:25:39 +08:00
Syngnat
50b4169148 🐛 fix(query-editor): 修复大库下SQL AI补全阻塞主线程导致全局卡顿
根因:全库列元数据加载后,每次内联补全在主线程对全部列做 O(列数×表数) 正则匹配,
且补全上下文被重复构建两次,8万列规模下单次请求耗时约900ms;同时 table_name 意图
每次补全都真实查库。改为按表名末段建索引(WeakMap按请求缓存)、复用已收敛上下文、
warmup 成功后会话内缓存,单次请求耗时降至约12ms。
2026-07-06 20:29:23 +08:00
Syngnat
4a63a175e4 🐛 fix(update): Windows在线更新改为覆盖当前exe并自动重启 2026-07-06 19:36:43 +08:00
Syngnat
2508b7935a 🐛 fix(update): 修复Windows在线更新复用旧安装包 2026-07-06 17:40:45 +08:00
Syngnat
22697baa37 🐛 fix(query-editor): 修复SQL AI补全误触普通补全 2026-07-06 17:26:41 +08:00
Syngnat
2fc587fd31 🐛 fix(frontend): 修复开发启动前端构建报错
- 修正 SQL 草稿快照定时器句柄类型兼容浏览器与 Node 环境
- 删除连接类型目录里重复的 trino 分支避免构建告警
- 配合可用 GOPROXY 验证 wails dev 已能完成前端编译与打包
2026-07-06 17:12:46 +08:00
Syngnat
2a3bd5bd97 🐛 fix(db): 修复Oracle同义词字段注释回填
- 元数据查不到列定义时补查 ALL_SYNONYMS 解析真实表
- 对解析出的 owner 和 table 继续回查列注释与主键信息
- 新增 SBDEV 指向 DEV.PERSON_INFO 的字段注释回归测试
2026-07-06 16:59:46 +08:00
Syngnat
ac68729734 🐛 fix(query-editor): 修复Oracle匿名块事务托管缺失
- 区分 BEGIN 事务控制语句与 Oracle 匿名块起始
- 让包含 DML 的 Oracle like 匿名块进入 SQL 编辑器托管事务
- 补充匿名块进入事务与回滚收口的后端回归测试
2026-07-06 16:59:17 +08:00
Syngnat
19c10f2369 🐛 fix(db): 修复 OceanBase Oracle 空结果并收敛提示
- OceanBase Oracle 只读查询优先走普通 Query 路径
- 补齐普通执行与托管事务两条回归测试
- 调整多语句回退提示避免误导为协议不支持
- 同步前端注释与多语言文案
2026-07-06 15:38:42 +08:00
Syngnat
8af8dd1ae0 🐛 fix(update): 修正 Windows 在线更新落盘与重启逻辑
- Windows 更新包直接下载到当前 EXE 目录
- staging 脚本目录移到临时目录避免暴露隐藏目录
- 安装时直接拉起新下载 EXE 并保留 zip 兼容分支
- 补充 Windows 更新路径回归测试
2026-07-06 15:37:56 +08:00
Syngnat
258fccd56f 🐛 fix(query-editor): 修复 OceanBase 执行误用登录Schema
- 保持 OceanBase Oracle 默认登录 schema 解析不变
- 未限定表名改写时增加登录 schema 到所选 schema 的回退候选
- 补充 SBDEVREAD/SBDEV 与 ORCLPDB1 场景回归测试

Fixes #615
2026-07-06 10:30:00 +08:00
Syngnat
93dc696e90 🐛 fix(db): 修复达梦DDL缺失字段注释 Fixes #615
- 补齐达梦列元数据查询中的字段注释读取
- 保留原始 CREATE TABLE 结果并追加 COMMENT ON COLUMN 语句
- 修正达梦列定义映射时 COMMENT 丢失的问题
- 新增达梦 DDL 注释补全回归测试
2026-07-06 08:56:41 +08:00
Syngnat
00d70d2934 🐛 fix(update): 修复 Windows 更新安装卡住并将安装包落到应用目录
- Windows 安装前预检当前安装目录写权限,避免脚本启动后主进程先退出
- 已下载更新补充打开安装目录入口,便于手动执行安装包
- 更新工作区优先改为当前应用运行目录,并补充回归测试与多语言文案
2026-07-05 20:06:51 +08:00
Syngnat
2f39767211 🐛 fix(update): 修复下载进度标题显示内部键并消除通道切换竞态
- 下载进度状态拆分 key 与 version 字段,弹窗标题恢复显示纯版本号
- SetUpdateChannel 将检查、持久化与缓存清理并入同一临界区,消除下载窗口期竞态
- 通道配置文件改为临时文件加重命名的原子写入
- 移除 useAppUpdateManager 未使用的 isMacRuntime 参数
2026-07-05 17:13:28 +08:00
Syngnat
5beb4c3ce4 feat(data-grid): 无主键表支持全列匹配编辑并移除只读限制
- 新增 all-columns 全列匹配行定位策略,无主键/唯一索引时不再强制只读
- 元数据加载失败、索引查询失败等误报场景全部降级为全列匹配编辑
- 修复 resolveQueryLocatorPlan 同步解析异常导致 SQL 结果页签不出现的问题
- 行安全由后端影响行数校验兜底,非单行命中即回滚整个事务
- 同步更新六语言文案与相关测试用例
2026-07-05 16:00:25 +08:00
Syngnat
8280f342a8 🐛 fix(mcp-server): 修复 arm64 镜像误装 amd64 二进制
- 移除 TARGETOS/TARGETARCH 的默认值,BuildKit 仅在无值声明时注入实际目标平台
- 原默认值 amd64 使 arm64 镜像内二进制架构错误,运行时报 exec format error
2026-07-05 12:13:56 +08:00
Syngnat
e5baf9ead6 feat(update): 支持 latest 与 dev 更新通道切换
- 新增更新通道持久化与按通道检查 GitHub Release
- 关于页支持切换通道并隔离本地更新缓存状态
- 补充多语言文案、Wails 绑定与前后端测试覆盖
2026-07-05 12:08:45 +08:00
Syngnat
06c4222aff ️ perf(ci): Docker 镜像改为原生 runner 分平台构建
- 构建矩阵拆分为 2 镜像 × 2 平台,arm64 使用 ubuntu-24.04-arm 原生 runner,移除 QEMU 模拟
- 各平台独立冒烟测试后以 digest 推送,merge job 合成多平台 manifest 并打标签
- 构建缓存 scope 按镜像-平台划分,避免跨平台缓存混用
2026-07-05 12:03:57 +08:00
Syngnat
f57f9913d5 🐛 fix(ci): 修复 build-env 镜像 npm 符号链接被 COPY 解引用
- 移除对 npm/npx/corepack 的直接 COPY,改为拷贝 node_modules 后重建符号链接
- COPY 会解引用符号链接,导致脚本内相对路径 require 解析失败
2026-07-05 11:40:14 +08:00
Syngnat
579891a53a 🐛 fix(ci): 修复 build-env 镜像 login shell 下 Go 工具链缺失
- 新增 /etc/profile.d 脚本,在 login shell 重置 PATH 后恢复 Go 工具链路径
- 修正 ENV PATH 中无效的 /root/go/bin 为 GOPATH 实际路径 /go/bin
2026-07-05 11:19:56 +08:00
Syngnat
061f78b897 🐛 fix(ci): 修复 mcp-server 本地 replace 模块分层构建
- 在 go mod download 前补拷贝 third_party/highgo-pq 与 third_party/go-irisnative 的 go.mod 依赖信息\n- 保留 Docker layer 缓存,同时兼容本地 replace 模块的依赖解析\n
2026-07-05 11:00:23 +08:00
Syngnat
6671915cf7 🐛 fix(ci): 修复 Docker 镜像构建基础环境
- 将 mcp-server 构建镜像升级到 Go 1.25,匹配当前 go.mod 版本要求\n- 调整 build-env 的 Node 资产复制方式,避免覆盖 Go 工具链\n- 显式补齐 /usr/local/go/bin 到 PATH,修复 smoke test 中 go 不可用\n
2026-07-05 10:57:07 +08:00
Syngnat
0cfcfa77d3 feat(mcp-server): 新增容器化部署支持
- 新增 MCP Server 的 Dockerfile、Compose 环境示例与 GHCR 镜像流水线\n- 补充 Podman Quadlet、Kubernetes Kustomize 与 Helm Chart 部署样例\n- 完善 MCP Server 独立 README,补充本地与远端 Agent 接入说明\n\nFixes #618
2026-07-04 21:57:21 +08:00
Syngnat
9e6a56a77b 📝 docs(readme): 补充 AI MCP 与数据源能力说明
- 补全中英文主 README 的 AI 与 MCP 工作流说明\n- 新增安全边界说明,覆盖 schema-only 与 allowMutating 约束\n- 补齐向量库、消息队列、国产数据库及时序库等数据源清单\n\nRefs #618
2026-07-04 21:56:10 +08:00
Syngnat
714c6fc553 feat(web-server): 新增浏览器访问认证与初始化流程
- 新增 web-server 运行模式与浏览器端运行时桥接
- 支持管理员密码、会话策略、Google Authenticator 与恢复码
- 设置中心补充浏览器访问认证状态与改密入口
- 优化初始化向导,未启用 2FA 时跳过验证器步骤

Refs #618
2026-07-04 21:50:52 +08:00
Syngnat
d7e15f5eb0 feat(release): 补齐 Linux arm64 构建与驱动发布链路
- 增加 dev/release 工作流 Linux arm64 构建矩阵与驱动分流
- 补齐在线更新资产识别及 driver release manifest/asset 校验
- 兼容 Windows 本地 bash/python 解析并补充对应测试
2026-07-04 10:15:13 +08:00
Syngnat
b388b08226 feat(sql-file-execution): 外部SQL执行切换工作台并优化大文件链路
- 前端将运行外部SQL改为工作台Tab并复用导出式进度视图

- 后端新增仅选择文件元数据接口并优化流式读取与批量拼接

- 补充执行Runner、国际化文案和定向测试
2026-07-03 22:11:49 +08:00
Syngnat
3bf42a169c 🐛 fix(sidebar): 修复侧边栏拖拽顺序重启后丢失
- 保留启动恢复阶段未回填连接时的 sidebarRootOrder
- 避免 hydration 过早裁剪连接根节点排序 token
- 补充重启后连接回填场景回归测试
2026-07-03 21:39:40 +08:00
Syngnat
29ee21e9d7 feat(query-editor): 增强 SQL 编辑器 AI 内联补全与独立模型配置
- 新增独立内联补全模型配置与服务端透传
- 优化 SQL 编辑器 Alt+\\ 触发、ghost 延续与对象位补全
- 引入基于已存查询和执行日志的 SQL 记忆补全
- 补充前后端本地化、快捷键与补全回归测试
2026-07-03 20:44:33 +08:00
Syngnat
5eafd2aa3c feat(query-editor): 支持 AI 行内补全与文本生成 SQL
- 新增 SQL 编辑器 AI 辅助服务,接入已配置供应商并生成行内补全

- 增加 Text-to-SQL 弹窗与插入、替换选区、替换全文应用模式

- 补齐多语言文案和定向测试覆盖
2026-07-02 21:36:44 +08:00
Syngnat
b0f077ffbb 🐛 fix(table-designer): 优化结构设计器元数据加载性能
- 字段元数据优先加载并拆分各页签 loading 状态

- 收窄 Oracle 与达梦约束字典查询范围

- 补充结构设计器与字典查询回归测试
2026-07-02 21:15:40 +08:00
Syngnat
0fe3060cf1 🎨 style(query-editor): 优化补全候选元数据显示
- 候选行未选中时也显示元数据,避免只有当前选中项可见
- 将字段名与元数据改为紧凑排列,减少候选列表空间浪费
- 增加表备注、字段类型备注、当前库隔离和候选样式回归断言
2026-07-02 17:50:56 +08:00
Syngnat
c2eaf27c20 🐛 fix(query-editor): 修复补全元数据来源与库范围
- 限定普通表名补全只使用当前选中库,避免其他库表污染候选列表
- 表名补全补充表备注,并在懒加载时回填共享表元数据
- 字段补全详情统一展示表名、类型和备注,文档区补充库表上下文
2026-07-02 17:50:48 +08:00
Syngnat
275d490ca7 🐛 fix(sidebar): 修复 Oracle 表元数据显示缺失
- Oracle/Dameng 表元数据查询移除 ALL_SEGMENTS 依赖
- 使用 ALL_TABLES.BLOCKS 轻量估算表大小,避免权限或大库聚合失败
- 保留表注释、行数、创建时间和最后 DDL 时间展示
- 补充 Oracle-like 元数据 SQL 回归断言
2026-07-02 15:21:45 +08:00
Syngnat
0707d60f0b 🐛 fix(sidebar): 放宽左侧树拖拽宽度上限
- 统一侧栏宽度常量,最大宽度放宽到 960px
- 拖拽时按窗口宽度预留主工作区空间
- 同步 v2 与 legacy 侧栏 CSS 最大宽度
- 持久化侧栏宽度时允许保存更宽配置并补充测试
2026-07-02 15:20:59 +08:00
Syngnat
9b5e785d67 feat(sidebar): 支持表元数据拖拽排序
- 设置中心元数据项支持拖拽调整展示顺序
- 新增 sidebarTableMetadataFieldOrder 持久化并按顺序渲染
- 保持表注释旧配置迁移与元数据开关兼容
- 补充排序、持久化和侧栏渲染回归测试
2026-07-02 15:19:44 +08:00
Syngnat
69d9f41085 feat(connection): 支持生产保护默认折叠
- 为连接配置分区卡片增加可折叠渲染能力

- 生产连接保护默认收起,仅保留标题说明和当前策略标签

- 补充中英文折叠态与展开态回归测试
2026-07-02 14:12:47 +08:00
Syngnat
ffb4d16950 🐛 fix(definition-viewer): 修复对象修改保留完整定义
- 统一对象查看页定义展示,补齐可复制的 CREATE OR REPLACE DDL

- 打开对象修改前清理同标签旧草稿,避免旧草稿覆盖完整注释

- 补充 Oracle 函数、过程和包定义的回归测试
2026-07-02 14:12:22 +08:00
Syngnat
68d4081fcf feat(sidebar): 支持表元数据显示配置
- 新增侧栏表元数据字段配置并兼容旧版表备注开关
- 设置中心新增侧栏表元数据入口并移除树上重复显示开关
- 批量补齐表大小、创建时间、修改时间等元数据加载与渲染
- 统一表元数据格式化逻辑并修复 V2 左树横向滚动裁切
- 补充设置中心、store、侧栏元数据相关回归测试与多语言文案
2026-07-02 12:54:43 +08:00
Syngnat
92eb4266da 🐛 fix(query-editor): 统一对象查看与修改字号
- object-edit 查询态补齐与查看页一致的 Monaco 字号和行高配置
- 统一对象查看与修改的行号宽度,避免视觉尺寸不一致
- 补充 QueryEditor 与 DefinitionViewer Monaco 选项回归测试
2026-07-02 10:47:08 +08:00
Syngnat
b220dd051f 🐛 fix(definition-viewer): 复用对象修改原标签页
- 查看页对象修改直接复用当前标签切换到 object-edit 编辑态
- 侧边栏视图和函数编辑入口统一补齐 queryMode 标记
- 补充 DefinitionViewer 与 store 回归测试锁定标签复用行为
2026-07-02 10:45:59 +08:00
Syngnat
e466e87fcc 🐛 fix(sidebar): 修正树节点加载与展开时序 2026-07-02 09:40:09 +08:00
Syngnat
0428a30071 🐛 fix(data-viewer): 延迟对象设计页的数据预览加载 2026-07-02 09:39:52 +08:00
Syngnat
faaf7169da 🐛 fix(query-editor): 修复 SQL 片段补全与插入交互
- 插入 SQL 片段后主动关闭片段弹窗,保持右键插入行为一致
- 提升 Monaco 补全详情区最小高度,放大片段说明展示区域
- 列补全解析改为基于当前语句引用上下文,修复当前语句前半段补全丢列问题
- 为 SQL 草稿持久化补齐 window 定时器兜底,避免测试环境 clearTimeout 缺失
- 更新 SQL 片段弹窗关闭和补全说明高度相关回归测试
2026-07-01 18:11:41 +08:00
Syngnat
04a61a50dc 🐛 fix(workbench): 统一 v2 工具中心与工作区主题
- 显式传递 uiVersion 构建 overlay theme,避免设置中心和工具中心混用旧版黄蓝色
- 统一 v2 模式下 Ant Design 主色和选中态为绿色语义
- 移除工具中心详情头部重复的返回上一步按钮
- 修复侧边树对象筛选反复切换时的 rc-tree 复用漂移
- 调整 v2 左侧树横向滚动条与首页空态布局,释放工作区展示空间
- 补充工具中心、主题 token 和侧边树 remount 的回归测试
2026-07-01 18:11:08 +08:00
Syngnat
749e543e37 🐛 fix(query-editor): 修复 SQL 片段插入与弹窗交互
- 在 QueryEditor 右键菜单中稳定注册插入 SQL 片段动作,并随语言切换刷新文案,避免入口偶发消失
- SQL 片段选择器改为统一的可拖拽可缩放弹窗,优先直连 Monaco snippetController 插入并保留回退链路
- 选中片段后保持弹窗打开以便连续插入,同时补充入口注册、延迟 onMount 与多语言文案回归测试
2026-07-01 11:03:48 +08:00
Syngnat
d94335dc62 feat(query-editor): 支持自定义新建查询默认 SQL
- 在 appearance 持久化设置中新增 newQuerySqlTemplate,支持默认回退与空白模板
- 新建空白查询标签页按用户模板初始化,不影响外部 SQL、保存查询和只读标签
- 在工具中心补充模板编辑与恢复默认入口,并完善 store、编辑器和 i18n 回归测试
2026-07-01 09:44:22 +08:00
Syngnat
e135956eba 🐛 fix(definition-viewer): 修复对象定义页滚动行号重叠
- 在 DefinitionViewer 只读 Monaco 实例上显式关闭 stickyScroll,避免滚动后出现悬浮代码行与行号叠层
- 同步关闭 TriggerViewer 的 stickyScroll,统一对象定义类只读查看器滚动表现
- 新增对象定义查看器 Monaco 选项回归测试,锁定只读定义页必须禁用 stickyScroll
2026-07-01 09:17:54 +08:00
Syngnat
66b14766a3 🐛 fix(query-editor): 修复补全范围与光标执行
- 仅按当前语句与光标前范围构建别名和表引用,避免跨语句泄露其他库表字段补全
- 在新语句起始关键字场景优先提升 SQL 关键字候选,避免关键字被字段建议压到后面
- Ctrl/Cmd+Enter 无选中内容时回退执行光标所在语句,不再误报没有可选择的 SQL 语句
- 补充补全排序、列范围和光标末尾执行的回归测试
2026-07-01 09:17:33 +08:00
Syngnat
923e789211 🐛 fix(elasticsearch): 修复 ES SQL 末尾分号导致空结果 Fixes #605
- 清理 parseESSQL 解析出的 WHERE 和 ORDER BY 子句尾部分号
- 避免 range 条件把数值误当成字符串传给 Elasticsearch
- 补充带分号的 ES SQL 端到端与解析回归测试
2026-06-30 22:28:17 +08:00
Syngnat
52eacdd9df 🐛 fix(sidebar): 修复表分组折叠后二次展开消失 Fixes #606
- 限制侧边栏折叠时的子树释放范围,只清理可重新懒加载的 connection/database 节点

- 保留 tables object-group 的已加载子节点,避免二次点击展开后表列表被清空

- 补充针对大表分组折叠场景的回归测试
2026-06-30 21:46:13 +08:00
Syngnat
a9bae6bb3f 🐛 fix(explorer): 补齐 Redis 部分导入与库节点加载态
- 新增 Redis 导入预览与按 key 勾选导入能力

- 后端支持导入文件预读、选中 key 过滤与本地化错误提示

- 打开数据库节点时同步展示加载态并补齐前后端回归测试

Fixes #607

Fixes #609
2026-06-30 21:17:00 +08:00
Syngnat
b0d8ccbf23 🐛 fix(definition-viewer): 修复对象定义页行号重影 2026-06-30 20:31:44 +08:00
Syngnat
4b18075314 🐛 fix(db): 修复达梦读查询结果空页
- 统一按解析后的驱动类型判断只读查询结果策略

- 达梦及 dm/dm8 自定义驱动只读查询优先走普通 Query

- 补充事务执行路径与达梦结果集回归测试
2026-06-30 20:06:57 +08:00
Syngnat
784d7d60e2 🐛 fix(query-editor): 修复Monaco搜索框与悬浮提示遮挡
- 移除 QueryEditor 中临时的 find widget DOM 监听与 offset zone 逻辑
- 为 V2 查询编辑器的 Monaco stage 增加搜索框可见态顶部安全区并放开必要溢出
- 调整分栏高度计算与回归测试,确保搜索框间距只作用于 V2 query-editor
2026-06-30 17:25:48 +08:00
Syngnat
ece1119c31 feat(query-editor): 增强行级快捷键并优化搜索框
- 接管 Cmd/Ctrl+E,选择当前行并复制,避免落到宿主搜索
- 新增 Cmd/Ctrl+D 复制当前行到下一行,并同步快捷键冲突校验
- 增加 macOS SQL 菜单桥接,确保原生 Cmd+E 回落到编辑器
- 保持 Monaco 内置搜索并下移,避免遮挡 SQL 第一行
- 补齐多语言文案与 QueryEditor/shortcut/main 回归测试
2026-06-30 15:25:01 +08:00
Syngnat
b9ffcd2959 🐛 fix(sidebar): 支持调节V2左侧导航按钮大小 2026-06-29 21:25:13 +08:00
Syngnat
d900bda756 test(sqlserver): 补强索引预览与DDL回归覆盖
Fixes #379
2026-06-29 20:47:01 +08:00
Syngnat
8ac435dc6d 🐛 fix(sidebar): 修复达梦表分组计数丢失
Fixes #412
2026-06-29 20:43:15 +08:00
Syngnat
99942e37d6 feat(database-ui): 补充表概览元数据并调整数据库菜单
Fixes #395
2026-06-29 20:35:59 +08:00
Syngnat
d2a535e6ff 🐛 fix(mysql): 补充 PolarDB-X 数据库列表回退查询
Fixes #455
2026-06-29 20:25:30 +08:00
Syngnat
61c2e524b4 feat(redis-viewer): 新增加载全部 Key 入口
Fixes #603
2026-06-29 20:17:01 +08:00
Syngnat
b434247838 🐛 fix(ddl): 补齐 kingbase 表DDL索引输出
- 在 SHOW CREATE TABLE 不可用时,fallback 额外拉取索引元数据并生成 CREATE INDEX 语句\n- 跳过与主键重复的索引,避免在 fallback DDL 中重复输出主键索引\n- 补充 kingbase DDL fallback 的索引回归测试\n\nFixes #601
2026-06-29 20:09:01 +08:00
Syngnat
b3f69ac401 🐛 fix(sidebar): 调整暗黑模式横向滚动条样式
- 为 v2 侧栏树横向虚拟滚动条补充暗色轨道与边界样式\n- 保持滚动条尺寸与 thumb 交互不变,仅修正暗黑模式视觉协调性\n- 补充侧栏主题样式断言覆盖\n\nFixes #602
2026-06-29 20:04:17 +08:00
Syngnat
93006d8423 feat(query-editor): 新增编辑器内搜索入口
- 在查询编辑器工具栏新增搜索入口并直连 Monaco 查找\n- 接管 Ctrl/Cmd+F,优先打开编辑器内搜索而不是浏览器默认查找\n- 补充多语言文案与静态测试覆盖\n\nFixes #539
2026-06-29 20:00:27 +08:00
Syngnat
a7fdab5bb2 feat(theme): 新增跟随系统主题模式
- 增加主题偏好持久化并兼容旧版主题状态恢复
- 监听系统深浅色变化并同步窗口与应用主题
- 补齐主题设置文案与持久化相关测试

Fixes #407
2026-06-29 19:52:22 +08:00
Syngnat
bedd1eceb5 🐛 fix(query-editor): 修复异常退出后SQL草稿丢失
- 为查询页签增加 crash-recovery 草稿快照并按 160ms 落盘
- 启动恢复时补齐缺失的 query tabs 与连接上下文
- 保存或关闭查询后清理临时草稿并补充恢复回归测试
2026-06-29 19:21:37 +08:00
Syngnat
dac9e1f571 🐛 fix(sidebar): 恢复批量库表危险操作
- 恢复批量表和批量库按钮到原批量操作弹窗\n- 补回批量删除表和删除库的危险确认与执行逻辑\n- 删除表仅处理 table 对象,避免视图误走 DropTable\n- 补齐多语言文案与入口/删除回归测试
2026-06-29 17:09:01 +08:00
Syngnat
aed87665f4 🐛 fix(query-results): 区分执行消息内容区域
- 为执行消息正文滚动区增加独立边框和浅背景
- 保留标题与复制按钮在顶部区域,正文内容单独成框
- 补充消息块样式断言,覆盖边框、背景和内边距
2026-06-29 15:18:35 +08:00
Syngnat
04bbab3d7e 🐛 fix(query-results): 修复金仓带模式表主键识别
- PG 类数据库保留 schema.table 作为查询结果表名和元数据表名
- 元数据连接继续使用当前数据库,避免把 schema 误当 database
- 补充金仓列、索引和前端可编辑定位回归测试
2026-06-29 15:18:13 +08:00
Syngnat
4798d3e8ec 🐛 fix(db): 修复金仓读查询结果空页
- 调整 PG 类数据库只读查询路径,优先使用普通 Query 返回真实结果集
- 避免金仓原生多结果集返回空行列时吞掉可展示数据
- 补充金仓读查询与原生空结果回退测试覆盖
2026-06-29 15:17:47 +08:00
Syngnat
c940e930bc feat(connection): 支持导入Navicat NCX连接 2026-06-29 14:16:17 +08:00
Syngnat
8280306f82 🔧 chore(wails): 同步数据库对象前端绑定 2026-06-29 13:45:34 +08:00
Syngnat
59e12b0867 🐛 fix(ui): 修复危险主按钮文字对比度 2026-06-29 13:38:07 +08:00
Syngnat
ded531a3c2 🐛 fix(sql-editor): 修复新建查询关闭应用未提示保存 2026-06-29 13:02:23 +08:00
Syngnat
3a471bc1aa 🐛 fix(mcp): 修复执行SQL结果不可见
- execute_sql 文本 Content 输出可读的 SQL 执行摘要和 Markdown 结果表\n- 保留 structuredContent 结构化结果,兼容机器解析客户端\n- 新增 in-memory MCP 调用回归测试,覆盖 select 1 可见结果
2026-06-29 12:32:07 +08:00
Syngnat
ea4f88a20d feat(sql-editor): 关闭应用时提示保存未保存SQL
- 关闭应用前拦截 Wails 退出事件,前端弹出确认退出、保存退出和取消三按钮\n- 保存退出支持外部 SQL 文件和已保存查询,未命名临时查询保留草稿恢复语义\n- 补充退出保护后端测试、前端保存目标测试和多语言文案
2026-06-29 11:16:55 +08:00
Syngnat
8e857b9aee 🐛 fix(query-results): 修复多结果集回退空结果页 2026-06-29 11:09:40 +08:00
Syngnat
f55a332ead feat(mcp): 扩展数据库对象结构读取工具
- 新增 get_views/get_objects 工具并让 get_tables 附带视图列表
- 后端统一返回表、视图、触发器、函数、过程、序列、包、事件和队列类对象
- 补充 MCP 服务测试、对象解析测试、远程 schema-only 文档和多语言文案
2026-06-29 10:25:37 +08:00
Syngnat
893a0f70fb 🐛 fix(query-results): 修复原生空结果集导致结果页缺失
- 后端对只读查询的原生多结果集空列空行结果增加逐条执行回退
- 前端执行成功后显式合并结果集并激活最新结果 tab
- 新增空原生结果集对象的回归测试,避免 SELECT 成功但结果页缺失
2026-06-29 10:23:58 +08:00
Syngnat
fdb05fb8d3 🐛 fix(i18n): 修复系统语言跟随不实时更新
Refs #335
2026-06-28 20:15:56 +08:00
Syngnat
730d4bb1ab 🐛 fix(tabs): 修复对象编辑页关闭后未回到来源标签
- 为 SQL 超链接打开的对象标签记录来源标签

- 关闭当前对象标签时优先回到来源标签

- 补充表对象与对象编辑标签关闭回退测试
2026-06-28 20:03:15 +08:00
Syngnat
fee703e879 🐛 fix(sqlserver): 修复消息循环缺失时吞掉查询结果
- 在 SQL Server 消息循环未产出结果集时兜底扫描 rows

- 保留 SELECT 返回的行数据和空结果列元数据

- 补充 SQL Server fallback 扫描回归测试
2026-06-28 16:41:12 +08:00
Syngnat
4f2f7003c8 ️ perf(ai-chat): 降低流式思考输出渲染开销
- 合并 AI 流式 token 刷新,减少高频状态写入

- 避免纯流式更新重排会话列表,并收窄当前会话订阅

- 补充 thinking 合并刷新和会话列表稳定性回归测试
2026-06-28 16:03:22 +08:00
Syngnat
97540206b3 feat(query-editor): 新增 SQL 美化快捷键 2026-06-28 12:08:40 +08:00
Syngnat
793e078676 🐛 fix(ci): 修复 Doris driver-agent revision 校验失败
- 统一 release/dev 构建流程中的 Doris/diros 驱动归一
- 保持 Doris 资产名为 doris,构建 tag 使用 gonavi_diros_driver
- 优化 driver-agent revision 校验失败日志,避免输出 JSON 解析 traceback
2026-06-28 08:35:05 +08:00
Syngnat
7453600ee5 feat(data-grid): 支持数据视图列头筛选 #490
- 列头筛选复用工具栏筛选状态,应用后同步条件但不自动打开筛选面板

- 修复列头筛选应用、清除和操作符下拉交互

- 补充筛选状态同步、主键查询和列头交互回归测试
2026-06-27 22:58:10 +08:00
Syngnat
6c53ef4eff feat(query-editor): 记住查询编辑区结果区比例
- 持久化查询编辑器与结果面板的分割比例
- 新开查询 Tab 时按已保存比例恢复编辑区高度
- 补充分割比例计算与 QueryEditor 回归测试

Fixes #538
2026-06-27 18:21:24 +08:00
Syngnat
60eb696859 🐛 fix(sidebar): 修复事件定义编辑入口 #540
- 事件右键编辑定义改为打开 object-edit 查询

- 拉取 CREATE EVENT 作为可编辑 SQL,避免只显示 SHOW CREATE EVENT

- 补充事件定义编辑与菜单入口回归测试

Fixes #540
2026-06-27 17:58:54 +08:00
Syngnat
e456925c23 feat(sidebar): 优化表备注悬浮信息展示
- 读取不同数据源表备注并写入左侧表节点
- 支持表分组菜单切换备注显示
- 表节点悬浮复用 Tab 信息卡并移除原生双提示

Fixes #569
2026-06-27 17:43:11 +08:00
Syngnat
038ecc8b70 🐛 fix(export): 兼容换行 SELECT 查询导出
- SELECT/WITH 只读判断支持关键字后换行
- 覆盖多表 JOIN 查询结果导出场景

Fixes #583
2026-06-27 16:41:35 +08:00
Syngnat
3bfcd15dc0 🐛 fix(data-grid): 允许未知总数结果跳页
- 未知总数分页保留跳页控件
- 跳页输入不再被当前估算页数截断

Fixes #584
2026-06-27 16:41:12 +08:00
Syngnat
6abdb8684f 🐛 fix(sidebar): 折叠海量对象树时释放子节点
- 折叠海量数据库/对象分组时清理子树缓存和后代展开态
- 避免虚拟树在大批量函数、过程等对象折叠后残留渲染

Fixes #587
2026-06-27 11:37:55 +08:00
Syngnat
080ae0986a 🐛 fix(window): 适配分辨率变化后的窗口边界
- 运行期检测普通窗口是否超出当前可用屏幕并自动收回
- 启动恢复时同步裁剪超过当前屏幕的窗口尺寸

Fixes #594
2026-06-27 11:17:42 +08:00
Syngnat
bfb61c8449 feat(redis): 优化数据库别名与计数展示
- Redis DB 标题不再重复拼接 key 数量
- 将别名单独渲染为浅色文本并支持设置后即时刷新

Fixes #593
2026-06-27 10:58:24 +08:00
Syngnat
23d7511f55 🐛 fix(sidebar): 单击打开数据库对象节点
- 支持视图、函数、触发器等对象单击直接打开定义 tab
- 覆盖查询日志打开后 CK/SQLite 查询结果仍切回数据结果

Fixes #595
2026-06-27 10:51:50 +08:00
Syngnat
2d73bcc6de 🐛 fix(query-editor): 跳过TDengine托管事务
Fixes #592
2026-06-27 10:41:28 +08:00
Syngnat
a24f4a2bc1 🐛 fix(query-editor): 限制快捷键仅执行选中SQL
Fixes #596
2026-06-27 10:36:46 +08:00
Syngnat
bf51003c66 🐛 fix(query-results): 修复执行消息面板布局异常 2026-06-27 10:21:39 +08:00
Syngnat
2df9dce78b 🐛 fix(query-results): 优化SQL执行消息展示
- 执行消息拆分标题栏和滚动正文区域,避免 SQL 文本被遮挡

- 消息正文关闭自动换行并保留原始空白,支持横向滚动查看完整 SQL

- 补充 SQLServer 消息展示和结果面板结构回归断言
2026-06-26 17:29:03 +08:00
Syngnat
3ed45fab41 🐛 fix(sidebar): 修复表默认打开行为与左树滚动
- 工具设置新增双击表名行为开关,默认打开表数据,可切换到对象设计

- 左侧树、表概览卡片视图和列表视图统一按开关打开数据页内对象设计

- 调整 V2 左侧树横向滚动条布局,使滚动条保持在底部可见
2026-06-26 17:28:49 +08:00
Syngnat
7c9cf95698 🐛 fix(query-editor): 修复对象跳转与存储过程补全
- 对象超链接支持表跳转到数据页内对象设计,并兼容 Ctrl/Cmd 修饰键状态丢失场景

- MySQL 补全增加 CALL 关键字和存储过程/函数名称提示,支持 CALL routine 跳转编辑

- DataGrid 支持按 tab 请求初始切换到对象设计视图,补充相关回归测试
2026-06-26 17:28:09 +08:00
Syngnat
af2c358822 🐛 fix(sidebar): 修复左侧树长名称滚动仍截断
- 调整侧栏树节点内容宽度,避免标题层继续被 ellipsis 截断

- 同步 V2 树标题和标签样式,横向滚动后可查看完整名称

- 补充样式回归测试,覆盖节点内容、标题和标签完整宽度
2026-06-26 15:43:43 +08:00
Syngnat
1226e544ec 🐛 fix(data-grid): 修复文本模式字段注释显示
- 文本记录视图接入列元数据与字段显示开关
- 字段名下方同步显示类型和注释,保持与表格列头设置一致
- DataGridShell 将查询结果列元数据传入文本视图
- 补充文本模式显示和隐藏字段元数据回归测试
2026-06-26 10:08:04 +08:00
Syngnat
faa7ed1ae2 🐛 fix(query-editor): 修复选择器与事务提示浮层
- host 和数据库下拉选项关闭原生 title,避免和自定义 Tooltip 重复显示
- 事务模式说明默认向上弹出,并保留边界自动避让
- 移除下拉打开时强制隐藏事务说明的状态控制
- 补充工具栏和事务设置回归断言
2026-06-26 10:06:56 +08:00
Syngnat
22cdeb677b 🐛 fix(query-editor): 修复对象超链接误定位左树 2026-06-25 22:29:09 +08:00
Syngnat
b210d078d0 test(data-grid): 补充 DDL 视图回归覆盖
覆盖切表常驻、侧栏布局继承和加载态保持

覆盖活动 DDL 入口隐藏重开与侧栏拖拽预览

覆盖 DDL 文本选择时保持横向视角的交互行为
2026-06-25 21:36:27 +08:00
Syngnat
ce30de20b6 🐛 fix(data-grid): 修复 DDL 视图常驻与侧栏交互
记忆 DDL 底部/侧栏布局并支持切表常驻刷新

修复侧栏关闭、拖拽预览和首次加载布局抖动

优化 DDL 行号 gutter,并保留文本选择时的横向视角
2026-06-25 21:36:14 +08:00
Syngnat
8dde4c3c6d 🐛 fix(tool-center): 移除内嵌工具重复标题头
- 工具中心详情页统一承载入口标题和说明
- 数据同步、表结构比对和数据比对嵌入时隐藏内部 hero
- 数据目录、快捷键、连接包和安全更新嵌入时隐藏内部 Modal 标题
- 补充工具中心全 pane 标题归属回归断言
2026-06-25 18:42:53 +08:00
Syngnat
bdb60a656a 🐛 fix(sidebar): 修复 V2 当前连接新建查询入口
- 在当前连接头部增加更明显的新建查询按钮
- 使用文档图标区分连接创建入口,避免与加号按钮混淆
- 通过连接树节点上下文触发新建查询,修复点击无响应
- 补充 V2 侧边栏入口位置与图标回归断言
2026-06-25 18:42:31 +08:00
Syngnat
8a56367d02 🐛 fix(query-editor): 优化工具栏长名称提示与片段弹窗布局
- SQL 编辑器连接和数据库选择器支持悬浮延迟显示完整名称
- SQL 片段管理嵌入工具中心时隐藏重复标题
- 缩小片段操作按钮并拆分滚动区域,避免底部内容被遮挡
- 补充工具栏和片段弹窗源码级回归断言
2026-06-25 18:42:11 +08:00
Syngnat
aebe9bab54 🐛 fix(sql-log): 统一 V2 SQL 日志入口
- V2 左侧入口改为打开当前工作区内嵌 SQL 日志,legacy 继续使用底部全局面板
- 表数据页新增 sqlLog 视图并复用嵌入式 LogPanel,避免无 SQL 编辑器时无法查看日志
- 移除 V2 侧栏底部重复 SQL 日志按钮,保留慢查询入口并补充回归测试
2026-06-25 17:39:57 +08:00
Syngnat
9ab31a7614 🐛 fix(oracle): 修复存储过程斜杠分隔执行截断
- 支持 SQL*Plus 斜杠分隔符后的可选分号,避免 Oracle 过程执行出空语句
- 光标落在过程异常尾部或斜杠分隔行时,仍选择完整 PL/SQL 定义执行
- 补充前端语句选择、QueryEditor 执行和后端 split/DBQueryMulti 回归测试
2026-06-25 17:37:32 +08:00
Syngnat
f6556f25d5 🐛 fix(oracle): 修复过程CASE分割导致执行截断 2026-06-25 13:52:20 +08:00
Syngnat
16a8a763f4 🐛 fix(oracle): 接入对象跳转并修复过程修改执行
- SQL 编辑器补齐 Oracle 序列和存储包元数据、hover 提示与 Ctrl/Cmd 点击跳转
- 对象编辑 SQL 保留 SQLPlus 斜杠分隔符,避免生成 /; 导致 ORA-00900
- 补充导航、对象编辑执行和多语言目录回归测试
2026-06-25 11:36:24 +08:00
Syngnat
4b1cd1b727 🐛 fix(oracle): 修复触发器脚本显示为空
- 触发器列表查询补齐 OWNER、TABLE_NAME、TRIGGER_BODY 等 Oracle 元数据字段
- 优先使用 DBMS_METADATA.GET_DDL 返回完整 CREATE TRIGGER 脚本
- 在 DDL 不可用时基于 USER_TRIGGERS/ALL_TRIGGERS 重建可编辑触发器语句
- 补充 Oracle 触发器 DDL 获取与回退重建回归测试
2026-06-25 10:50:07 +08:00
Syngnat
37ccaf7743 🐛 fix(oracle): 修复存储包定义截断与斜杠误执行
- 对象树支持序列和存储包双击及右键查看定义
- Oracle package 同时读取 PACKAGE 与 PACKAGE BODY 并生成可编辑 SQL
- SQL 拆分器跳过 SQL*Plus 独立斜杠和注释分隔符
- 补充前后端 PL/SQL 拆分与定义查看回归测试
2026-06-25 10:12:28 +08:00
Syngnat
06a984f39d 🐛 fix(elasticsearch): 修复 ES SQL 分号结尾查询无结果
- 兼容 SELECT FROM 索引名后直接接分号的查询语句
- 避免解析失败后退回全索引 query_string 导致空结果
- 补充 ES SQL 转 _search 的回归测试

Fixes #590
2026-06-24 23:36:37 +08:00
Syngnat
6d1c034052 🐛 fix(sidebar): 修复长表名横向滚动截断
- 取消 V2 对象树等宽标签自截断,支持横向滚动展示完整名称

- 提高对象树横向滚动宽度上限并补充回归测试
2026-06-24 22:42:04 +08:00
Syngnat
672d05d124 🐛 fix(sidebar): 恢复连接加载中的转圈状态
- 扩展连接状态为 loading/success/error

- 在连接根节点加载开始时显示 pending,成功后才置绿

- 同步 V2 active host 与 legacy Badge 状态显示并补测试
2026-06-24 22:31:13 +08:00
Syngnat
322d0a7cb8 feat(sidebar): 支持 Oracle 序列和存储包对象树
- 新增 Oracle/Dameng 序列与存储包元数据加载

- 同步对象树节点、V2 筛选、搜索、复制和拖拽支持

- 补充多语言文案和侧边栏回归测试
2026-06-24 22:26:39 +08:00
Syngnat
5725e78931 🐛 fix(mcp): 修复外部 MCP 读取连接不完整
- 启动时自动同步旧本地连接到后端安全仓库

- 启动 HTTP MCP 或安装外部 MCP 前执行连接同步

- 增加安全配置同步回归测试

Fixes #591
2026-06-24 21:37:52 +08:00
Syngnat
69b6072e37 🐛 fix(query-editor): 修复 OceanBase Oracle 查询默认 schema 误判
- 优先使用当前选中的 Oracle-like schema 解析未限定表名

- 为 OceanBase Oracle 只读账号查询补齐业务 schema 限定

- 增加回归用例覆盖登录用户与选中 schema 不一致场景
2026-06-24 21:25:22 +08:00
Syngnat
1a9d417c0a 🐛 fix(query-editor): 修复过程脚本斜杠分隔符误执行
- 前端语句选择跳过独立 SQL*Plus 斜杠分隔符

- 后端 SQL 拆分和流式文件执行保持过程体完整

- 增加 Oracle 过程脚本执行回归测试
2026-06-24 21:10:59 +08:00
Syngnat
8a552c4cb3 🐛 fix(query-editor): 优化事务下拉显示与浮层交互
- 自动提交延迟选项改为多语言完整文案,避免短标签语义不清
- 收窄并校准 V2 工具栏事务下拉宽度,兼顾不截断与紧凑布局
- 事务模式 Select 展开时自动隐藏 DBeaver 参考 Tooltip,避免浮层互相遮挡
- 补充事务设置行为测试和布局守护测试
2026-06-24 17:50:57 +08:00
Syngnat
9da9a36cf3 🐛 fix(snippet): 修复SQL片段入口与弹窗布局
- 将 SQL 编辑器代码片段管理入口改为打开工具中心 SQL 片段面板
- 关闭旧独立片段弹窗入口,避免同一功能出现两个入口形态
- 限制片段管理弹窗内容区高度并固定底部操作行,避免按钮被语法参考内容挤出
2026-06-24 17:49:48 +08:00
Syngnat
d08ab62f92 🐛 fix(data-grid): 修复刷新后未提交修改被清空 2026-06-24 15:56:07 +08:00
辣条
1cb112bcdf 1. 收口多语言能力 2. 修复查询编辑器、侧边栏、DDL 展示等业务问题 (#589)
## 主要改动

### 1. 多语言能力收口
- 收口 6 种语言下的业务文案与交互文案
- 覆盖连接驱动、查询编辑器、导出流程、部分 AI 工具面板等场景
- 补齐多语言回归测试,减少语言切换后文案残留中文或不同步的问题

### 2. 查询编辑器相关修复
- 修复新建查询标签页在语言切换后标题不同步的问题
- 修复查询结果集不展示、总数长期停留在“正在统计中”的问题
- 修复有限分页场景下仍可继续翻到不存在页码并展示异常数据的问题
- 修复 SQL 编辑器挂起事务内执行后续查询时,无法读到未提交修改的问题
- 修复 SQL Server 单条查询尾随 `affectedRows` 结果被误渲染为额外结果页签的问题
- 补齐 SQL Server 多结果集归属与过滤逻辑,避免冗余结果干扰展示

### 3. 侧边栏与视图相关修复
- 修复数据库连接下视图节点展开/滚动后渲染错乱、内容铺满侧栏的问题
- 修复收起视图栏后页面仍残留视图内容的问题
- 修复视图元数据重复导致的侧边栏树结构异常问题
- 补齐相关测试桩与回归测试

### 4. Oracle / DDL 相关修复
- 修复 Oracle 建表 DDL 追加注释时缺少语句结束分号与空行分隔的问题
- 避免 `CREATE TABLE` 与 `COMMENT ON TABLE/COLUMN` 粘连,提升复制后可直接执行的稳定性

### 5. 其他界面修复
- 修复语言设置面板选项显示截断问题
- 收口部分 dev 合并后的多语言遗漏与提示文案不一致问题

## 验证情况

已完成的本地验证包括:

- 前端定向测试
- `npm test -- QueryEditor.external-sql-save.test.tsx
sqlEditorTransaction.test.ts`
- `npm test -- QueryEditor.results-and-drop.test.tsx
QueryEditor.external-sql-save.test.tsx sqlEditorTransaction.test.ts
useSqlEditorTransactionController.test.tsx`
- `npm test -- QueryEditor.results-and-drop.test.tsx ddlFormat.test.ts`

- 后端定向测试
- `go test ./internal/app -run
'TestDBQueryMultiTransactionalKeepsDMLTransactionOpenUntilCommit|TestDBQueryMultiInTransactionReusesPendingManagedSessionForReadQueries'`
- `go test ./internal/db -run 'Test.*GetCreateStatement|Test.*Oracle.*'`

## 已知说明

- `internal/app` 下部分 SQL Server 相关定向测试在当前机器上受“SQL Server 纯 Go
驱动未启用”环境限制,失败原因为本地驱动依赖,不是本次改动引入的问题
- 本 PR 改动面较大,建议合并前重点关注:
  - 多语言切换后的查询编辑器与侧边栏行为
  - SQL Server 结果集展示
  - Oracle DDL 预览与复制执行
  - 挂起事务中的查询结果一致性

## 风险点

- 查询编辑器结果集合并、分页、事务复用逻辑改动较集中
- 多语言文案收口涉及范围较广,建议合并后做一次核心路径冒烟验证
2026-06-24 14:15:31 +08:00
Syngnat
2fb4851d1f 🐛 fix(query-editor): 补齐SQL Server冗余结果集过滤
- 为 SQL Server 原生多结果集缺失 statementIndex 的场景补齐结果归属
- 修复单条 SELECT 查询尾随 affectedRows 结果被误渲染为额外结果页签的问题
- 补充前后端回归测试并保持存储过程多结果集展示不变
2026-06-24 13:58:12 +08:00
Syngnat
32c3ba017e 🐛 fix(oracle): 修复DDL注释拼接缺少分号与换行
- 为 Oracle 建表 DDL 追加注释前补齐语句结束符并保留空行分隔
- 修复 COMMENT ON TABLE/COLUMN 与 CREATE TABLE 粘连导致复制后无法直接执行的问题
- 补充后端 Oracle 建表语句与前端 DDL 格式化回归测试
2026-06-24 13:57:49 +08:00
Syngnat
d09b968cc0 🐛 fix(query-editor): 补齐SQL Server冗余结果集过滤
- 为 SQL Server 原生多结果集缺失 statementIndex 的场景补齐结果归属
- 修复单条 SELECT 查询尾随 affectedRows 结果被误渲染为额外结果页签的问题
- 补充前后端回归测试并保持存储过程多结果集展示不变
2026-06-24 11:57:19 +08:00
Syngnat
90d84da849 🐛 fix(oracle): 修复DDL注释拼接缺少分号与换行
- 为 Oracle 建表 DDL 追加注释前补齐语句结束符并保留空行分隔
- 修复 COMMENT ON TABLE/COLUMN 与 CREATE TABLE 粘连导致复制后无法直接执行的问题
- 补充后端 Oracle 建表语句与前端 DDL 格式化回归测试
2026-06-24 11:40:18 +08:00
tianqijiuyun-latiao
d2c4160c6d 🐛 fix(query-editor): 修复挂起事务内查询结果不一致 2026-06-24 11:37:34 +08:00
tianqijiuyun-latiao
2b4beae9df test(i18n): 校准多语言守卫测试源码引用位置 2026-06-24 11:17:43 +08:00
tianqijiuyun-latiao
7dab6f2e33 🐛 fix(query-editor): 修复结果集阻塞与未知总数分页误导
- 为查询结果定位元数据探测增加软超时降级,避免租户元数据卡死阻塞主查询结果渲染
- 将未知总数分页切换为顺序翻页模式,并修正文案仅在真实统计时显示正在统计中
- 补充查询结果与分页回归测试,覆盖元数据超时和 legacy 未知总数分页场景
2026-06-24 10:47:37 +08:00
tianqijiuyun-latiao
1aab48783d 🐛 fix(query-tab): 修复新建查询标签页语言切换不同步
- 提取共享的新建查询默认标题识别逻辑
- 按当前语言重算默认查询和数据库作用域查询标题
- 补充跨语言标题回归测试
2026-06-24 10:26:05 +08:00
tianqijiuyun-latiao
5089e6b294 test(sidebar): 补齐视图元数据加载测试桩返回字段 2026-06-24 10:17:05 +08:00
tianqijiuyun-latiao
3be08cb6cd 🐛 fix(sidebar): 修复视图元数据重复导致侧边栏树渲染错乱
- MySQL 视图元数据缺失 schema 时回填当前数据库
- 避免多条回退查询生成重复视图节点
- 补充侧边栏视图去重回归测试
2026-06-24 10:14:22 +08:00
tianqijiuyun-latiao
6c4833dcd1 🐛 fix(settings): 修复语言设置面板选项显示截断 2026-06-24 10:07:52 +08:00
tianqijiuyun-latiao
ce17629a8d Merge remote-tracking branch 'origin/dev' into feature/20260602_connection_driver_i18n 2026-06-24 08:08:53 +08:00
Syngnat
5e15c4dd2d 🐛 fix(query-editor): 隐藏 SQL Server 冗余影响行数结果集 2026-06-24 00:22:31 +08:00
tianqijiuyun-latiao
c04d82ee30 Merge remote-tracking branch 'origin/dev' into feature/20260602_connection_driver_i18n
# Conflicts:
#	frontend/src/components/QueryEditor.results-and-drop.test.tsx
2026-06-23 23:58:25 +08:00
tianqijiuyun-latiao
7962aea008 🐛 test(query-editor): 修复 SQL Server 消息结果断言 2026-06-23 23:44:06 +08:00
Syngnat
5493b62bb9 🐛 fix(query-editor): 修复 SQL Server 结果消息缩进并校正回归测试 2026-06-23 23:42:30 +08:00
tianqijiuyun-latiao
86c39ac36f Merge branch 'dev' into feature/20260602_connection_driver_i18n 2026-06-23 23:34:16 +08:00
Syngnat
6bf05c9ed7 🐛 fix(update): 修复桌面端更新安装与重启流程
- macOS 下载完成后改为直接走安装更新链路,不再只打开安装目录
- Windows 更新脚本在重启时显式带上目标工作目录,提升安装后拉起稳定性
- 删除失效的 winget 发布工作流,并补充前端与脚本回归测试

Fixes #585
2026-06-23 22:18:07 +08:00
Syngnat
a2c1b4a7d8 🐛 fix(query-editor): 修复外部SQL快捷保存失效
- 放宽活跃 QueryEditor 在文档级快捷键目标下的保存触发条件
- 修复桌面端 Ctrl/Cmd+S 事件落到 document 时未真正写盘的问题
- 保持普通查询保存行为不变,并补充外部 SQL 文件快捷保存回归测试
2026-06-23 20:18:06 +08:00
Syngnat
07b3b908f9 🐛 fix(mongodb): 修复编辑态字符串类型丢失
- 为 MongoDB 编辑态补充字段名驱动的保守类型推断
- 统一 DataGrid 基准数据为类型化值,覆盖 JSON、行编辑和单元格编辑
- 保持 pMid 等普通字符串字段不被误判为 ObjectId
- 补充 Mongo helper、DataViewer 主键定位与 DataGrid 提交回归测试
2026-06-23 19:48:43 +08:00
tianqijiuyun-latiao
71989af586 🐛 fix(i18n): 收口 dev 合并后的业务提示多语言遗漏 2026-06-23 18:26:20 +08:00
Syngnat
adacf0b5c5 feat(connection): 支持生产连接多项保护策略
- 新增数据编辑、结构编辑、脚本执行和数据导入四类连接级保护配置
- 升级生产连接保护弹窗为多选卡片,并修复选项对齐与勾选态显示
- 按保护类型收口 QueryEditor、DataGrid、表设计、导入与同步目标入口
- 后端统一拦截 SQL 或 Mongo 写操作、结果编辑、结构变更和导入写入
- AI 本地工具与 RPC 执行链路透传连接保护配置并复用后端守卫
- 补充多语言文案、定向测试与需求追踪记录
2026-06-23 17:42:54 +08:00
tianqijiuyun-latiao
09ae3d74c4 🐛 fix(i18n): 合并 dev 后补齐只读保护多语言 2026-06-23 17:05:35 +08:00
tianqijiuyun-latiao
839fdd7d66 Merge branch 'dev' into feature/20260602_connection_driver_i18n
# Conflicts:
#	shared/i18n/de-DE.json
#	shared/i18n/en-US.json
#	shared/i18n/ja-JP.json
#	shared/i18n/ru-RU.json
#	shared/i18n/zh-CN.json
#	shared/i18n/zh-TW.json
2026-06-23 16:23:43 +08:00
Syngnat
b0a9a995fb feat(connection): 新增生产连接只读保护 2026-06-23 15:33:11 +08:00
Syngnat
3205d131c9 🐛 fix(query-editor): 修复消息结果前缀与复制全选交互
- 统一清洗 SQL Server 消息前缀并覆盖结果刷新与分页回填链路
- 将消息结果区改为只读文本区,补充一键复制入口
- 放行编辑器外可编辑区域的 cmd/ctrl+a,避免消息内容全选被抢占
- 补充结果面板交互与国际化回归,确保构建通过
2026-06-23 14:43:13 +08:00
tianqijiuyun-latiao
52d193b2e8 fix(i18n): 收口 dev 合并后的驱动与 QueryEditor 本地化 2026-06-23 14:04:25 +08:00
tianqijiuyun-latiao
d9e52c734a Merge remote-tracking branch 'origin/dev' into feature/20260602_connection_driver_i18n 2026-06-23 13:14:43 +08:00
tianqijiuyun-latiao
3ce85617da feat(i18n): 收口导出前端多语言 2026-06-23 13:14:22 +08:00
tianqijiuyun-latiao
0ba984b277 Merge remote-tracking branch 'origin/dev' into feature/20260602_connection_driver_i18n
# Conflicts:
#	frontend/src/App.tsx
#	frontend/src/components/AISettingsModal.tsx
#	frontend/src/components/ConnectionModal.edit-password.test.tsx
#	frontend/src/components/ConnectionModal.tsx
#	frontend/src/components/DataSyncModal.i18n.test.ts
#	frontend/src/components/DataSyncModal.tsx
#	frontend/src/components/QueryEditor.external-sql-save.test.tsx
#	frontend/src/components/QueryEditor.tsx
#	frontend/src/components/Sidebar.locate-toolbar.test.tsx
#	frontend/src/components/Sidebar.tsx
#	frontend/src/components/SnippetSettingsModal.tsx
#	frontend/src/components/TableOverview.tsx
#	frontend/src/components/ai/AIChatHeader.test.tsx
#	frontend/src/components/ai/AISettingsProvidersSection.tsx
#	frontend/src/components/ai/aiChatPayloadDispatch.ts
#	frontend/src/components/ai/aiChatReadiness.ts
#	frontend/src/components/ai/aiSettingsModalConfig.tsx
#	frontend/src/components/ai/messageBubble/AIMessageCodeBlock.tsx
#	frontend/src/components/sidebarV2Utils.ts
#	frontend/src/i18n/catalog.test.ts
#	frontend/src/utils/connectionTypeCatalog.test.ts
#	frontend/src/utils/connectionTypeCatalog.ts
#	frontend/src/utils/tabDisplay.ts
#	internal/ai/provider/custom.go
#	internal/ai/service/service.go
#	internal/app/methods_driver.go
#	internal/app/methods_file.go
#	internal/db/custom_impl.go
#	internal/db/iris_impl.go
#	internal/db/mariadb_impl.go
#	internal/db/sqlserver_impl.go
#	shared/i18n/de-DE.json
#	shared/i18n/en-US.json
#	shared/i18n/ja-JP.json
#	shared/i18n/ru-RU.json
#	shared/i18n/zh-CN.json
#	shared/i18n/zh-TW.json
2026-06-23 12:41:27 +08:00
Syngnat
8da8cc7f91 🐛 fix(mongodb): 修复 DataGrid 编辑后 BSON 类型丢失
- 为 MongoDB 结果展示、单元格编辑和行编辑接入类型感知格式化与解析
- 支持 ObjectId、日期、Int32、Int64、Double、Decimal128、UUID 等常见类型保真
- 统一 v1/v2 驱动查询结果的 Extended JSON 输出与 ApplyChanges BSON 恢复
- 补充前端提交链路与后端类型转换回归测试
2026-06-23 12:14:27 +08:00
Syngnat
bc63311003 🐛 fix(oracle): 修复普通查询重复列自动别名缺失
- 在 QueryEditor 查询计划阶段识别显式列与 alias.* 的重复列冲突
- Oracle 执行前自动为冲突显式列补充 _1 风格唯一别名
- 让 locator 与后续追加表达式复用改写后的可执行 SQL
- 补充普通查询重复列自动别名的 Oracle 回归测试
2026-06-23 10:46:35 +08:00
Syngnat
3a00ae1f44 🔧 chore(wails): 同步数据同步 targetSchema TS 绑定
- 为 sync 请求模型补充 targetSchema 字段映射
- 同步自动生成的 frontend wailsjs models.ts 绑定
- 更新 package.json.md5 生成校验文件
2026-06-23 09:55:41 +08:00
Syngnat
e8cad189be 🐛 fix(sqlserver): 修复普通查询结果被原生多结果集吃空
- 对只读 SQL 的原生多结果集空返回增加顺序回退兜底
- 避免 optional driver-agent 成功返回空结果时前端只剩日志无结果集
- 补充 SQLServer 读查询空结果回退回归测试
2026-06-23 09:46:44 +08:00
Syngnat
495a985ae1 🐛 fix(sqlserver): 修复可选驱动查询消息透传缺失
- 为 optional-driver-agent 的 query 和 queryMulti 响应补充 messages 字段
- 在可选驱动 DB 客户端透传 SQL Server 查询提示信息与多结果集
- 补充 agent 与数据库层回归测试并更新 driver agent revision
2026-06-23 08:48:42 +08:00
Syngnat
8f1e6cf379 ️ perf(frontend): 优化长时运行下的搜索与缓存占用
- 为 V2 cmd+k 搜索预建索引并限制初始/宽泛结果数量
- 清理冷数据库树和 DataViewer 长生命周期快照缓存
- 收紧运行时 SQL 日志预算并在 hydration 时压缩旧缓存
2026-06-22 22:36:39 +08:00
Matt Van Horn
05e8dab710 Merge 0ba2bfe645 into 4999fd544d 2026-06-22 00:33:23 -07:00
Matt Van Horn
0ba2bfe645 fix(datasource): ClickHouse 22.8 HTTP 握手兼容 displayName 缺失
clickhouse-go 在 HTTP 握手阶段执行 SELECT displayName(), version(),
revision(), timezone(),而 ClickHouse 22.8 没有 displayName() 函数,
返回 Code 46 UNKNOWN_FUNCTION,导致即便已移除 client_protocol_version
的兼容重试路径仍然连接失败。

扩展现有 HTTP 兼容脚手架:新增 displayName Code 46 检测,复用同一条
兼容重试分支;兼容模式下的 RoundTripper 改写握手探测请求体,将
displayName() 替换为各版本通用的 hostName(),其余请求体原样放行。

Refs #479
2026-06-22 00:19:49 -07:00
tianqijiuyun-latiao
f282da3bcb feat(i18n): 收口多语言功能业务代码 2026-06-22 15:12:42 +08:00
tianqijiuyun-latiao
eba689754c test(ai): 对齐多语言 fallback 测试基线 2026-06-22 13:46:42 +08:00
tianqijiuyun-latiao
d13c153f5e feat(i18n): 收口数据库驱动多语言代码
- 提交 internal/db 多驱动用户可见错误与状态文案多语言化

- 补齐数据库驱动多语言测试与六语言 catalog

- 修复 frontend i18n catalog 的 4 个失效 guard
2026-06-22 10:09:45 +08:00
Syngnat
4999fd544d 🐛 fix(data-sync): 完善多种目标库的 schema 同步链路
- 扩展数据同步目标端 schema 选择与元数据加载,覆盖 SQL Server、IRIS、DuckDB 等独立 schema 场景
- 修正同步链路中的目标表 schema 归一化与 query/apply 表名解析,避免落到错误模式
- 补充前后端回归测试与多语言文案,覆盖 schema 选择、别名识别和结果预览路径

Fixes #571
2026-06-21 22:46:57 +08:00
Syngnat
36233ba9aa Merge pull request #580 from feat/573-redis-db-alias
feat(redis): per-database aliases in the sidebar
2026-06-21 16:42:36 +08:00
Matt Van Horn
4408bce159 feat(redis): support per-database alias in the Redis viewer tree 2026-06-21 00:44:22 -07:00
Syngnat
e7b8e78f9c 🐛 fix(query-editor): 修复当前语句快捷选择在 CRLF 文本下错位
- 统一当前语句选择与执行路径的归一化 offset/position 换算
- 避免 Windows CRLF 文本下 SQL 语句选区错位
- 补充 QueryEditor 当前语句选择回归测试

Fixes #575
2026-06-21 15:08:34 +08:00
Syngnat
29e7e365f1 📝 docs(readme): 补充 Trino 联邦查询支持说明
- 在中英文 README 的支持数据源列表中补充 Trino\n- 明确 Trino 作为跨多数据源联邦查询入口\n\nFixes #577
2026-06-21 14:11:36 +08:00
Syngnat
8ea7ecc477 feat(trino): 新增 Trino 可选驱动接入并补齐查询支持
- 后端新增 Trino 数据库实现与 optional driver-agent provider
- 前端补齐 catalog.schema 连接配置、URI 解析与能力开关
- SQL 编辑器对 Trino 禁用托管事务并补充前后端测试
2026-06-21 13:54:42 +08:00
Syngnat
99b75378c3 feat(data-grid): 完善 ER 图多层关系展开与字段浏览
- 支持按层扩展关联关系并重置为一层视图
- 支持节点字段展开收起与全部字段切换
- 补充 ER 图模型、Hook 与界面回归测试
2026-06-21 11:37:45 +08:00
Syngnat
5f56859898 🐛 fix(query-editor): 兜底 SQL 编辑器中文输入首次不上屏
- 补充 Monaco IME 组合输入提交兜底
- 统一拦截候选键避免快捷键链路抢占
- 增加 QueryEditor 中文输入回归测试

Fixes #578
2026-06-21 11:30:17 +08:00
Syngnat
7f2445a6f5 🐛 fix(monaco-editor): 禁用 EditContext 修复中文输入异常
- 在 MonacoEditor 中统一关闭 editContext
- 修复单引号场景下中文输入首次上屏异常
- 补充编辑器选项回归断言

Close #578
2026-06-20 17:05:38 +08:00
Syngnat
b3a54b3ff8 feat(data-grid): 新增完整 ER 图视图并收口导出回归
- 新增完整 ER 图建模、布局与交互 hook
- 在元数据视图接入 ER 图并补齐样式与多语言文案
- 修正查询结果导出测试交互与 Wails 导出 mock
2026-06-20 15:45:58 +08:00
Syngnat
c8c8497a2f feat(query-editor): 收敛 SQL 分析工作台与结果区日志体验
- 新增 SQL 分析工作台,统一承载慢 SQL 和 SQL 诊断视图
- 将 SQL 执行日志收进结果区首个日志标签并在失败时展示错误摘要
- 调整侧边栏入口、标签展示、多语言文案与定向前端测试覆盖
2026-06-20 14:09:58 +08:00
Syngnat
04019135a0 🐛 fix(sql-diagnose): 兼容新版 MySQL JSON EXPLAIN 解析
- 兼容 query_plan 包装和新版 JSON V2 执行计划结构
- 补齐 covering index、table scan 等节点类型与统计归一化
- 增加新版 MySQL EXPLAIN 解析测试并修正 TotalCost 汇总逻辑
2026-06-20 14:08:02 +08:00
Syngnat
e7935db84b feat(db-connection): 新增连接后台定时探活保活能力
- 为连接配置新增 keepAlive 开关与探活间隔
- 在后端缓存连接上增加后台定时 Ping 调度与失效剔除
- 补充前端表单、Wails 模型映射与定向测试覆盖

Close #579
2026-06-20 11:24:44 +08:00
Syngnat
0f67305100 ♻️ refactor(codebase): 拆分大文件并收敛模块职责
- 拆分 ConnectionModal、DataGrid、QueryEditor、App 等前端大文件

- 抽出 driver assets、v2 workbench 样式和 source-as-test 读取辅助

- 更新相关测试以覆盖拆分后的源码位置
2026-06-19 23:10:00 +08:00
Syngnat
6179c3fbd9 ♻️ refactor(sidebar): 拆分动作与搜索逻辑 2026-06-19 18:46:08 +08:00
Syngnat
13705f9098 ♻️ refactor(sidebar): 抽出 V2 右键菜单逻辑 2026-06-19 18:08:54 +08:00
Syngnat
1dea343aa2 ♻️ refactor(sidebar): 抽出 V2 树标题渲染 2026-06-19 18:03:28 +08:00
Syngnat
293a8ff3e9 ♻️ refactor(sidebar): 抽出实体操作弹窗 2026-06-19 17:56:12 +08:00
Syngnat
6e422aea33 ♻️ refactor(sidebar): 抽出树节点加载器 2026-06-19 17:32:45 +08:00
Syngnat
39e52469f2 ♻️ refactor(sidebar): 抽出批量操作弹窗 2026-06-19 17:23:53 +08:00
Syngnat
3ff5141184 ♻️ refactor(sidebar): 抽出外部 SQL 文件流程 2026-06-19 17:13:24 +08:00
Syngnat
db31513c0b ♻️ refactor(sidebar): 抽出批量导出状态逻辑 2026-06-19 16:55:43 +08:00
Syngnat
5da2c7ff1a ♻️ refactor(sidebar): 抽出元数据加载工具 2026-06-19 16:45:15 +08:00
Syngnat
d109f2891f ♻️ refactor(sidebar): 复用 V2 侧栏工具函数 2026-06-19 16:33:16 +08:00
Syngnat
f946951580 ♻️ refactor(sidebar): 迁出 legacy 节点菜单构建逻辑 2026-06-19 16:21:36 +08:00
Syngnat
f6ebfc2e44 ♻️ refactor(sidebar): 清理 V2 rail 残留死代码
- 删除未被调用的 rail connection button/group 渲染闭包

- 移除对应的折叠、拖拽状态和 badge/host 辅助函数

- 保留当前可达的 active connection 状态展示和树拖拽排序逻辑
2026-06-19 15:16:31 +08:00
Syngnat
540dbc2a28 ♻️ refactor(sidebar): 抽出 Command Search 面板组件
- 新建 SidebarSearchPanel,承接 V2 命令搜索弹层的行、分组和空状态渲染

- Sidebar.tsx 保留搜索状态、过滤和执行逻辑,仅通过 typed props 传入子组件

- 保持 @ 对象搜索、? AI 提问、同步过滤与键盘导航行为不变
2026-06-19 14:55:21 +08:00
Syngnat
2c8128724e ♻️ refactor(sidebar): 抽出 ConnectionRail 为独立子组件
- 新建 sidebar/SidebarConnectionRail.tsx:V2 连接栏(新建分组/批量导出/SQL 文件/定位/AI/工具/设置 7 个按钮)
- Props 用聚合对象(labels + handlers + canLocateActiveTab)避免 18+ 个独立 props drilling
- Sidebar.tsx 删除 renderV2ConnectionRail(-93 行),加 v2ConnectionRailProps 构造 + <SidebarConnectionRail /> 调用
- Sidebar.tsx 从 10187 减至 10122 行
2026-06-19 14:29:57 +08:00
Syngnat
528f3c51a0 ♻️ refactor(sidebar): 迁出命令搜索相关类型与 shouldLoadSidebarNodeOnExpand
- 迁出 V2CommandSearchMode/V2CommandSearchQuery 类型与 parseV2CommandSearchQuery(支持 @/?前缀切换 object/ai 模式)
- 迁出 shouldLoadSidebarNodeOnExpand(节点懒加载判定)
- SidebarNodeLike 加可选 key 字段以适配测试用法
- Sidebar.tsx 从 10235 减至 10187 行,sidebarHelpers.ts 增至 215 行
2026-06-19 14:19:06 +08:00
Syngnat
a4d94624cd ♻️ refactor(sidebar): 继续抽离 resolveV2ObjectGroupTitle 等 2 个工具函数
- 新增 SidebarNodeLike 结构化类型,替代 Pick<TreeNode, ...> 解除对 Sidebar 内部类型的依赖
- 迁出 resolveV2ObjectGroupTitle(对象分组标题本地化)与 resolveSidebarTableNameForCopy(节点表名提取)
- Sidebar.tsx 从 10243 减至 10235 行
2026-06-19 14:15:35 +08:00
Syngnat
87bd16c4ba ♻️ refactor(sidebar): 抽离独立工具函数到 sidebarHelpers 模块
- 新建 sidebar/sidebarHelpers.ts:迁出 6 个无内部类型依赖的纯函数(formatSidebarRowCount/hasSidebarLazyChildren/getV2RailConnectionGroupBadgeText 等)+ V2ExplorerFilter 类型 + V2_RAIL_UNGROUPED 常量
- Sidebar.tsx 通过 import + re-export 双向引用,外部测试文件的 `from './Sidebar'` 保持兼容
- 文件规模:Sidebar.tsx 从 10275 减至 10243 行,建立 sidebar/ 子目录作为后续拆分的目标归宿
2026-06-19 14:11:54 +08:00
Syngnat
dc54d24d2b feat(sidebar): 新增 Sidebar 慢 SQL 历史浮动入口
- 新建 SlowQueryRailButton:独立小组件,从 store 直接读 tabs/connections 解析激活连接
- 挂载方式:浮动在 Sidebar 右下角,不修改 Sidebar.tsx 内部代码(避免改 10275 行的大文件)
- 体验优化:无激活连接时按钮自动禁用并 tooltip 提示;与 Ctrl+Shift+L 快捷键路径并存
- bundle 保持:SlowQueryPanel 继续走 lazy 加载独立 chunk,不进入主 bundle
2026-06-19 13:56:15 +08:00
Syngnat
8457f6c4b7 feat(shortcuts): 把 SQL 诊断与慢查询快捷键注册到管理系统并加菜单入口
- 注册到系统:diagnoseQuery 与 showSlowQueries 加入 ShortcutAction,可在快捷键管理面板自定义
- 修复冲突:原硬编码 Ctrl+Shift+D 与 toggleTheme 冲突、Ctrl+Shift+H 与 toggleLogPanel 冲突;改为 Ctrl+Shift+P / Ctrl+Shift+L
- 菜单入口:QueryEditor 的"更多"下拉加 SQL 诊断、慢 SQL 历史两个菜单项,附带快捷键提示
- i18n 同步:6 种语言补齐 label/description
2026-06-19 13:43:12 +08:00
Syngnat
a2d83744b5 feat(explain): 扩展索引建议规则引擎至 15 条
- 新增规则:LIKE 前缀通配、函数包裹列、笛卡尔积风险、OR 条件无索引、大 OFFSET 分页、SELECT * + JOIN 模式
- 阈值常量:large_offset(10000)、cartesian_product(100000)、wide_table(20 列)
- 测试覆盖:新增 6 个用例验证规则触发与抑制(含边界场景)
2026-06-19 13:43:01 +08:00
Syngnat
946450874f 🔧 chore(wails): 同步 GetSlowQueries 与 ClearSlowQueries 的 TS 绑定
- Wails 工具自动重新生成,把手动插入的绑定重排到字母序正确位置
- 同步 package.json.md5 构建指纹
2026-06-19 13:25:32 +08:00
Syngnat
577a417292 feat(explain-ui): 新增慢 SQL 历史面板与 Ctrl+Shift+H 快捷键
- 面板组件:SlowQueryPanel 含 TopN 列表 + 耗时/扫描行数/时间三种排序 + 清空操作
- 入口接入:QueryEditor 加 Ctrl+Shift+H 快捷键,点击条目回填 SQL 到编辑器
- Wails 绑定:手动同步 GetSlowQueries 与 ClearSlowQueries 到 App.js/App.d.ts
- 颜色提示:耗时 >5s 红色、>1s 橙色,便于一眼识别重查询
2026-06-19 13:23:02 +08:00
Syngnat
a74065bdbb feat(explain): 新增慢 SQL 历史存储与 DBQueryMulti 执行埋点
- 存储层:JSONL 单连接单文件,5MB 自动滚动,TopN 排序 + SQL 指纹去重
- 执行埋点:DBQueryMulti 用 named return + defer 异步记录,成功后自动写入历史
- 阈值过滤:默认 500ms 以下查询跳过记录,避免历史爆炸
- 查询入口:GetSlowQueries 按 duration/rowsRead/recent 排序,ClearSlowQueries 支持清空
- SQL 指纹:字面量/大小写归一化后 sha256,同模板不同参数视为同一查询
- 测试覆盖:新增 13 个单元测试覆盖存储/滚动/排序/去重/指纹
2026-06-19 13:17:39 +08:00
Syngnat
0c320234fd feat(explain-ui): 将诊断工作台接入 QueryEditor 并支持快捷键触发
- 快捷键绑定:QueryEditor 监听 Ctrl+Shift+D(Mac 为 Cmd+Shift+D)打开诊断面板
- 配置解析:从 currentConnectionId 复用 SavedConnection 模式解析 ConnectionConfig
- lazy 加载:ExplainWorkbench 通过 React.lazy + Suspense 隔离,避免 reactflow 进入主 bundle
- 端到端联通:用户在编辑器写 SQL 后按快捷键即可触发 DiagnoseQuery 并可视化结果
2026-06-19 13:11:20 +08:00
Syngnat
f5ae2e51f9 feat(explain-ui): 新增执行计划图渲染组件与索引建议侧栏
- 依赖引入:新增 reactflow + dagre 用于执行计划 DAG 自动布局
- 构建配置:vite manualChunks 拆分 reactflow/dagre/recharts 独立 chunk,便于按需加载
- 类型镜像:utils/explainTypes.ts 镜像后端 ExplainResult/Node/Stats/IndexSuggestion,含颜色与格式化 helper
- 图渲染:ExplainGraph 自定义节点按 opType 着色 + 警告 flag 边框高亮 + dagre TB 布局
- 侧栏组件:ExplainSidebar 含统计条、节点详情、索引建议按 severity 排序
- 主容器:ExplainWorkbench 含 Modal + 执行计划/原文双 tab,调用 DiagnoseQuery 端到端联通
2026-06-19 13:04:49 +08:00
Syngnat
8e24e40fdd feat(explain): 补齐 Oracle/SQLServer/ClickHouse 解析器与索引建议规则引擎
- 方言解析:新增 Oracle DBMS_XPLAN 表格、SQLServer SHOWPLAN_XML、ClickHouse EXPLAIN JSON 解析器
- 规则引擎:新增 10 条跨方言规则(全表扫描、缺索引 JOIN、filesort、估算偏差、缓冲命中、Nested Loop 高扇出等)
- 入口接入:DiagnoseQuery 返回的 Suggestions 自动填充规则匹配结果
- 容错增强:SQLServer strip 默认命名空间与 XML 声明;Oracle 表格列与独立 Predicate 段双源融合
- 测试覆盖:新增 27 个用例覆盖三方言解析与规则触发场景
2026-06-19 12:45:15 +08:00
Syngnat
85648b1e5a 🔧 chore(wails): 同步 DiagnoseQuery 自动生成的 TS 绑定
- 重新生成 App.d.ts / App.js:暴露 DiagnoseQuery(connectionConfig, dbName, sql) 入口
- 更新 models.ts:新增 connection.ExplainResult / ExplainNode / ExplainStats 等类型
- 同步 Service.d.ts / Service.js / package.json.md5(构建缓存指纹)
2026-06-19 12:31:42 +08:00
Syngnat
b997788437 feat(explain): 新增 SQL 诊断工作台后端 EXPLAIN 基建
- 数据结构:新增 ExplainResult/Node/Stats/IndexSuggestion/DiagnoseReport 等归一化模型,跨方言通用
- 接口扩展:Database 接口新增 ExplainExecer 可选能力,支持驱动自带 EXPLAIN 实现
- 核心入口:DiagnoseQuery 支持 SELECT/WITH 白名单校验、方言调度、原生与 fallback 两条执行路径
- 方言适配:buildExplainQuery 覆盖 MySQL/PostgreSQL/SQLite/Oracle/SQLServer/ClickHouse 7 大主流
- 解析器:MySQL FORMAT=JSON 含表格 fallback、PostgreSQL ANALYZE BUFFERS JSON、SQLite EQP 层级解析
- 测试覆盖:新增 27 个单元测试覆盖 SQL 构造与三方言解析器
2026-06-19 12:30:56 +08:00
Syngnat
542bafe6c4 🔧 chore(gitignore): 忽略 optional-driver-agent 构建产物 2026-06-19 12:07:33 +08:00
Syngnat
98965a56e1 🐛 fix(memory): 修复大数据量导出导致进程内存飙升至 16G 的问题
- GC 策略:主进程与 driver-agent 启动时收紧 SetGCPercent 至 50
- 周期回收:scan_rows 与 callStreamQuery 每 5 万行触发 runtime.GC
- 自适应限流:driver-agent 引入 GOMEMLIMIT 自适应策略,2GB 起步按 1GB 步长抬升至 8GB 上限
- 批次调优:流式批次由 256 行缩减至 64 行,降低 JSON 编解码瞬时峰值
2026-06-19 12:05:02 +08:00
Syngnat
21c427bc39 🐛 fix(connection): 优化多数据源连接数占用
- 测试连接改为隔离连接,成功后立即关闭并避免写入全局缓存
- 新增通用 SQL 连接池配置,限制网络型数据源空闲连接长期占用
- Redis 测试连接改为临时客户端并立即释放
- MySQL 连接数超限时释放同实例缓存连接并重试
- 补充连接释放、缓存重试和连接池参数回归测试
2026-06-18 20:29:19 +08:00
Syngnat
6b67bb24b4 feat(tool-center): 优化工具中心分组交互与通用弹窗
- 重构工具中心为侧边分组导航,固定弹窗高度并支持内部滚动
- 新增通用可拖拽可缩放 Modal,统一主要弹窗打开体验
- 为工具中心内嵌入口补充返回上一步交互与底部操作区
- 补充多语言文案和工具中心/Modal/i18n 回归测试
2026-06-18 20:28:47 +08:00
Syngnat
06dd9507ee feat(ai): 补齐 Cursor 与 CodeBuddy 会话态聊天链路
- 新增 SessionChatProvider 接口,补齐非流式对话的会话态复用能力
- 为 Cursor Agent 和 CodeBuddy CLI 同步实现流式与非流式会话续接及状态持久化
- CustomProvider 补充会话态透传,统一 custom provider 的会话复用行为
- Service 新增 AIChatSendInSession,聊天主链路非流式回退改走带 session 的发送接口
- 保留原 AIChatSend 无状态语义,避免标题生成和记忆压缩污染主会话上下文
- 补充前后端定向测试,覆盖会话恢复、续接发送和前端回退分流
2026-06-18 13:35:08 +08:00
Syngnat
b588235b62 feat(ai): 接入 Cursor Cloud Agents API
- 新增 cursor-agent provider,支持创建 agent、轮询 run 状态和 SSE 流式响应
- 接入 AITestProvider 与 AIListModels,支持 Cursor 官方 /v1/models 连通性测试和模型发现
- 在 AI 设置中新增 Cursor 供应商预设,固定 cursor-agent 协议并补齐默认端点配置
- 调整 provider readiness 与 insights 规则,允许 Cursor 未显式选模型时走官方默认模型
- 补充后端 provider/service 测试和前端 preset、表单、readiness 相关用例

Close #576
2026-06-18 12:35:58 +08:00
Syngnat
f457f6aaca feat(ai): 接入 CodeBuddy CLI 并兼容官方登录态
- 新增 CodeBuddy CLI provider,支持 codebuddy/cbc 命令调用与输出解析
- 将 Base URL、API Key/Auth Token、自定义请求头映射到 CodeBuddy CLI 环境变量
- 扩展 custom provider 路由与测试链路,兼容空 Base URL 和 CLI 默认模型选择
- AI 设置新增 CodeBuddy 预设,并补齐 preset 回显识别与匹配逻辑
- 修正就绪态、模型列表与表单校验,允许留空凭证直接复用本机已登录账号
- 补充前后端定向测试并覆盖 CodeBuddy 配置展示文案

Close #574
2026-06-18 12:06:58 +08:00
Syngnat
c8fe90cbee ️ perf(import-export): 降低 OceanBase 导出链路内存占用
- 为 optional driver-agent 补齐 streamQuery 分片协议,避免大结果集整批缓冲到内存
- 在 OceanBase 整表导出和查询结果导出前强校验 driver-agent revision,旧版代理直接拦截并提示重装
- 为 driver-agent 增加大查询和流式导出完成后的 GC/FreeOSMemory 回收逻辑
- 补充导出前校验、流式分片消费和 agent 内存回收的定向测试
- 更新 driver-agent revisions 以匹配新的流式导出协议
2026-06-18 11:32:08 +08:00
Syngnat
6bd87fa568 🐛 fix(export-workbench): 补齐整表导出百分比进度
- 为整表导出链路补充 COUNT(*) 预统计,并把总行数写回首个导出进度事件
- 兼容解析多种总数返回类型,避免后端已知总量时前端仍降级为不定进度条
- 补充后端导出总数解析与前端 runner 状态切换回归测试
2026-06-18 10:58:01 +08:00
Syngnat
293fc6e0fe 🐛 fix(data-grid): 修复字段元数据偶发缺失
- 为字段元数据提取补充可用性判断并在空类型/空备注时自动重试
- 刷新结果集时同步清理字段、外键和唯一键缓存并强制补拉元数据
- 补充 DataGrid 头部元数据回归测试,覆盖首次空结果重试与刷新重载场景
2026-06-18 10:57:51 +08:00
Syngnat
2a8ae05363 🐛 fix(export-workbench): 修正未知总数进度展示并优化 XLSX 收尾阶段
- 修正总行数为 0 时仍被当作已知总数的问题,避免导出进度百分比失真
- 调整导出进度条判定逻辑,未知总数时改为展示实时写入进度
- 统一 Sidebar、TableOverview 和导出工作台的预计行数口径,仅在总数大于 0 时视为已知
- 优化 XLSX 收尾阶段的 ZIP 压缩策略和拷贝缓冲,降低 Windows 大文件导出封装耗时
- 细化 finalizing 阶段文案,明确显示 XLSX 正在封装压缩
- 补充导出进度状态与零总数场景的回归测试
2026-06-18 10:15:00 +08:00
Syngnat
9613f6b624 🐛 fix(frontend): 预构建本地化依赖避免开发启动代理失败 2026-06-18 09:58:06 +08:00
Syngnat
ee78b9b57c ️ perf(import-export): 降低大文件导入导出内存占用
- xlsx 导出改为临时 sheet 加 zip 直写,避免整包缓冲到内存
- xlsx 导入改为 zip xml 流式解析,并将 shared strings 落到临时文件
- 大任务完成后按行数和文件大小阈值触发内存回收
- 补充导入导出流式链路的测试与基准覆盖
2026-06-18 09:21:01 +08:00
Syngnat
5ce4dddd7a ️ perf(ci): 减少 driver-agent 检测与平台 diff 的重复计算
- driver_agents job 一次性产出各平台 driver 集合,build 矩阵直接消费结果\n- detect 阶段补充 setup-go 与缓存,降低 revision 检测的冷启动成本\n- diff-driver-agent-revisions 支持候选 drivers 过滤,并行生成 base/head revisions\n- 补充脚本测试,覆盖候选 driver 过滤与无关 driver 忽略场景
2026-06-17 17:44:40 +08:00
Syngnat
e67285fde1 ️ perf(import): 重构导入链路并支持流式批量写入
- 后端新增流式导入流水线,避免预览和导入阶段整文件驻留内存\n- 导入执行优先复用 BatchApplier 按批提交,并在批量失败时回退单行定位错误\n- 导入进度事件兼容未预扫总行数场景,沿用预览总数稳定展示进度\n- 补充导入预览、批量回退和前端进度展示的最小回归测试
2026-06-17 17:26:57 +08:00
Syngnat
4e31d47936 feat(export-workbench): 支持批量导出工作台并优化 SQL 导出性能
- 侧边栏批量表/批量库入口改为直接打开导出工作台,统一导出配置与进度视图
- 导出工作台新增 batch-tables / batch-databases 模式,支持连接、数据库、对象选择与独立历史记录键
- 连接、数据库、对象下拉项补齐完整名展示与悬浮提示,避免长名称被截断后不可识别
- 后端新增批量对象/批量库导出 WithOptions 链路,统一返回输出文件/目录与进度信息
- SQL dump 数据导出改为按方言批量写入,MySQL/PG 等使用多值 VALUES,Oracle/达梦使用 INSERT ALL
- 补充导出工作台与 SQL dump 的回归测试和 benchmark,覆盖批量模式与批量写入语义
2026-06-17 16:50:05 +08:00
Syngnat
954d126a8f test(sidebar): 适配多语言后的 locate-toolbar 测试 2026-06-17 15:36:58 +08:00
Syngnat
5b31ab7435 feat(export-workbench): 新增导出工作台与进度历史
- 新增表级导出工作台标签页,统一承载导出范围、格式和 XLSX sheet 行数配置
- 结果集、表概览、侧栏和右键菜单统一接入导出工作台与带进度的导出入口
- 导出进度改为事件驱动,未知总数时展示不定进度和实时已写入行数
- 持久化每张表的导出历史并复用同一导出标签,重启后仍可查看最近任务
- 调整导出页签标题、状态胶囊和历史列表,补充工作台与状态流测试覆盖
2026-06-17 14:40:49 +08:00
Syngnat
b3c321be67 ️ perf(export): 重构大结果集导出链路并支持流式写入
- 新增 ExportFileOptions 统一承载导出格式、进度任务和 XLSX sheet 行数上限
- 查询导出改为流式写入文件,避免一次性缓存整批结果导致高内存占用
- 增加值数组快速路径并复用扫描与写入缓冲,减少逐行 map 分配开销
- 为 ClickHouse、自定义驱动、达梦、SQLServer 和 TDengine 补齐 StreamQuery 支持
- 导出时间字符串仅在形似时间时再解析,避免普通文本被误判改写
- 补充 XLSX 分 sheet、流式导出和基准测试覆盖
2026-06-17 14:24:35 +08:00
辣条
d7ad83f0d5 完善多模块多语言 (#572)
- 补齐前后端多模块多语言文案、共享词典与 i18n guard,减少硬编码展示文案。
- 覆盖 QueryEditor、Sidebar 外部 SQL、安全更新、DataGrid、Redis、AI/工具入口等重点场景。
2026-06-17 13:56:01 +08:00
tianqijiuyun-latiao
3006429a9a 🔧 chore(merge): 合并最新 dev 并解决 DataGrid 冲突 2026-06-17 13:52:50 +08:00
tianqijiuyun-latiao
9364c48ef0 feat(i18n): 完善多模块多语言适配与发版验证
扩展前后端多语言文案与共享词典。增加多模块 i18n 回归测试与 guard。收口外部 SQL 菜单和弹窗多语言文案。
2026-06-17 13:17:33 +08:00
Syngnat
3e140c1bc6 🐛 fix(ai-safety): 修正完全模式执行口径与本地工具失败判定
- 修正完全模式下 DML 与过程调用的安全提示和限制说明
- 区分连接探针失败与可恢复 SQL 执行错误,避免数据探针被误终止
- 修复本地 execute_sql 写语句结果返回 affectedRows
- 补充 AI 安全、本地工具执行与 SQL 限制回归测试
2026-06-17 09:49:59 +08:00
Syngnat
7ff3e00759 🐛 fix(query-editor): 修复外部 SQL 标签状态与 OceanBase 查询改写
- 修复外部 SQL 文件删除后标签残留及关闭异常
- 修复 OceanBase Oracle 查询注入隐藏 ROWID 时的表名改写
- 修复小写表名执行时的精确引用并保留日志中的原始 SQL
- 补充查询编辑器相关回归测试
2026-06-17 09:49:39 +08:00
Syngnat
0632c5242c 🐛 fix(oceanbase/data-grid): 修复 Oracle 时间字段显示编辑与结果视图异常
- 修复 OceanBase Oracle DATE 与 TIMESTAMP 的解码、展示和编辑精度丢失问题
- 修复查询结果与数据视图的行号显示、分页页数和日期列展示口径
- 打通 Oracle 与 OceanBase 会话执行链路的扫描方言透传
- 补齐 DBQuery、DataGrid temporal 和 OceanBase 结果链路回归测试
2026-06-17 09:49:15 +08:00
tianqijiuyun-latiao
76b0163bd3 Merge branch 'dev' into feature/20260602_connection_driver_i18n
# Conflicts:
#	frontend/package.json.md5
#	frontend/src/App.tsx
#	frontend/src/components/AIChatPanel.message-boundary.test.tsx
#	frontend/src/components/AIChatPanel.tsx
#	frontend/src/components/AISettingsModal.tsx
#	frontend/src/components/ConnectionModal.tsx
#	frontend/src/components/DataGrid.ddl.test.tsx
#	frontend/src/components/DataGrid.layout.test.tsx
#	frontend/src/components/DataGrid.tsx
#	frontend/src/components/DataGridColumnTitle.test.tsx
#	frontend/src/components/DataGridLegacyCellContextMenu.tsx
#	frontend/src/components/DataGridSecondaryActions.tsx
#	frontend/src/components/DataGridToolbarFrame.tsx
#	frontend/src/components/DataSyncModal.tsx
#	frontend/src/components/DataViewer.tsx
#	frontend/src/components/DefinitionViewer.tsx
#	frontend/src/components/DriverManagerModal.tsx
#	frontend/src/components/QueryEditor.external-sql-save.test.tsx
#	frontend/src/components/QueryEditor.tsx
#	frontend/src/components/RedisViewer.tsx
#	frontend/src/components/Sidebar.locate-toolbar.test.tsx
#	frontend/src/components/Sidebar.tsx
#	frontend/src/components/TabManager.hover.test.tsx
#	frontend/src/components/TabManager.tsx
#	frontend/src/components/TableDesigner.tsx
#	frontend/src/components/V2TableContextMenu.tsx
#	frontend/src/components/ai/AIChatHeader.tsx
#	frontend/src/components/ai/AIHistoryDrawer.tsx
#	frontend/src/main.tsx
#	frontend/src/store.ts
#	frontend/src/utils/aiComposerNotice.test.ts
#	frontend/src/utils/aiComposerNotice.ts
#	frontend/src/utils/connectionModalPresentation.ts
#	frontend/src/utils/driverImportGuidance.ts
#	frontend/src/utils/externalSqlTree.test.ts
#	frontend/src/utils/externalSqlTree.ts
#	frontend/src/utils/sqlDialect.ts
#	internal/ai/service/service.go
2026-06-16 18:35:11 +08:00
Syngnat
6421662f5d 🐛 fix(oracle): 修复裸表查询结果字段元数据缺失
- Oracle-like 裸表查询默认使用连接用户名作为 schema 加载字段元数据
- 修复未写 schema 时结果表头不显示字段类型和注释的问题
- 保持显式 schema.table 查询优先级不变
- 补充查询结果表引用解析和 QueryEditor 回归测试
2026-06-16 14:05:28 +08:00
Syngnat
54195e0591 🐛 fix(sqlserver): 修复对象 SQL 定义获取失败
- SQL Server 对象定义改为通过 sys.all_sql_modules 按库、schema、对象名精确查询
- 增加 sp_helptext 兼容候选,支持拼接多行 Text 返回完整定义
- 统一修复视图、函数/存储过程、触发器定义查看与对象修改入口
- 补充 SQL Server 对象定义查询和组件回归测试
2026-06-16 12:54:39 +08:00
tianqijiuyun-latiao
5fc29a6fd3 feat(i18n): 推进多语言剩余切片闭环
- 补齐 DataGrid、DataViewer、DefinitionViewer、JVM 等模块多语言文案与回归测试
- 收口 JVM 前后端展示、诊断、监控和资源呈现相关多语言路径
- 更新六语言共享词典并保留 raw 边界
2026-06-16 12:40:33 +08:00
Syngnat
f41a15c7b8 feat(data-sync/oceanbase): 拆分比对入口并修复 OceanBase Oracle 连接
- 数据同步:新增表结构比对、数据比对两个独立工具入口
- 比对模式:为 DataSyncModal 增加只读入口展示与模式化文案
- OceanBase:Oracle 租户改用 OB Oracle 专用 MySQL-wire 连接路径
- 连接表单:允许 OceanBase Oracle Service Name 留空,仅 TNS 场景需要填写
- 驱动提示:revision 不匹配提示收敛到驱动管理,不再在普通数据源入口弹出
- 测试覆盖:补充数据比对入口、OceanBase Oracle、driver-agent 提示边界测试
2026-06-16 12:15:16 +08:00
Syngnat
938bc53966 🐛 fix(mysql): 修复 DATE 字段显示为 datetime
- 查询扫描链路透传数据库方言,区分 MySQL 与 Oracle DATE 语义
- MySQL/MariaDB/自定义 mysql 驱动的 DATE/NEWDATE 只展示 YYYY-MM-DD
- 保留 DATETIME/TIMESTAMP 和 Oracle DATE 的时间信息
- 补充值规整与扫描链路回归测试
Close #565
2026-06-16 09:25:16 +08:00
Syngnat
093b3cae1f 🐛 fix(postgres): 修复删除数据库误判当前连接占用
- PostgreSQL 类数据库 DROP DATABASE 自动切换到维护库执行
- 避免前端传入目标库名时被误判为当前连接正在使用
- 同步修复 ALTER DATABASE RENAME 的同类误判
- 补充 PostgreSQL 删除和重命名数据库回归测试
Close #567
2026-06-16 09:07:19 +08:00
Syngnat
0816702084 🐛 fix(external-sql): 修复外部 SQL 文件丢失后标签无法关闭
- 后端读取 SQL 文件失败时返回 file_not_found 结构化错误码
- 前端识别文件被删除或移动的场景,允许用户确认关闭标签
- 保留权限、网络盘异常等非缺失错误的关闭拦截,避免误丢草稿
- 补充前后端测试覆盖缺失文件识别与标签关闭提示
Close #566
2026-06-16 08:48:43 +08:00
Syngnat
c70eb7157f test(oceanbase): 脱敏 Oracle 租户连接测试数据
- 将测试中的内网地址、账号和服务名替换为虚构 fixture
- 统一 OceanBase Oracle 与 SSH 跳板机测试常量
- 保留连接预探测与 SSH 诊断断言语义
2026-06-16 08:37:18 +08:00
Syngnat
23f95d7dc8 feat(query-editor): 支持还原 SQL 美化前内容
- 美化 SQL 前保存最近一次原始内容快照
- 在格式设置菜单提供还原上次美化入口
- 持久化查询标签页的美化恢复快照
- 补充编辑器与状态恢复回归测试
2026-06-16 07:10:04 +08:00
Syngnat
682017ba96 🐛 fix(oceanbase): 修复 Oracle 租户 SSH 预探测超时
- 拆分 OceanBase Oracle 预探测的拨号超时与握手读取超时
- SSH 跳板机场景下使用完整连接超时,避免内网目标被误判不可达
- 保留 MySQL handshake 短读取超时,避免 TNS 端口测试连接变慢
- 补充 SSH 预探测超时与短读取行为回归测试
2026-06-15 17:56:34 +08:00
Syngnat
891c8c1200 🐛 fix(sql-snippet): 修复片段管理编辑与按钮布局
- 支持自定义 SQL 片段按 id 更新,避免修改时重复新增
- 将片段语法说明改为可编辑并随片段持久化
- 将保存、删除、重置、关闭按钮统一收敛到底部操作栏
- 调整操作按钮为大号尺寸并增加最小宽度和底部间距
- 补充片段编辑、布局结构和持久化回归测试
2026-06-15 17:28:48 +08:00
Syngnat
a611c1c04b 🐛 fix(oceanbase): 修复 Oracle 租户跳板机连接预探测失败
- 修复 OceanBase Oracle 预探测未走 SSH 隧道导致内网 IP 被本机直连误判不可达的问题
- 预探测阶段复用完整连接配置,支持通过 SSH 跳板机访问目标地址
- 区分本机 TCP 不可达与 SSH 跳板机访问失败,优化错误提示
- 保留 OBClient 与 TNS 双路径路由逻辑,避免协议判断回退
- 补充 OceanBase Oracle SSH 预探测与网络失败回归测试
2026-06-15 16:13:15 +08:00
tianqijiuyun-latiao
558966a129 feat(i18n): 推进六语言多语言体系与扫描门禁
- 新增共享六语言词典、前端 i18n 运行时与语言设置入口

- 推进连接、驱动、数据网格、查询、AI、Redis、表设计等模块文案本地化

- 补充 raw 边界、SQL/驱动/更新场景测试与 i18n 扫描工具
2026-06-15 14:35:08 +08:00
Syngnat
2f354d2267 feat(saved-query): 新增已存查询独立查看入口
- 侧边栏新增“全部已存查询”根节点,不依赖连接实例或加载数据库
- 按连接、数据库和未匹配状态分组展示后端已加载查询
- 使用独立树节点 key,避免与数据库节点下的同一查询冲突
- 重命名和删除按真实 query id 同步更新所有展示副本
- 补充独立入口分组结构测试,覆盖已匹配和未匹配查询
2026-06-15 14:12:39 +08:00
Syngnat
eca9601ab0 feat(saved-query): 支持已存查询后端持久化与连接重绑
- 后端新增 saved_queries.json 仓库,保存、导入、删除和重绑统一走 Wails 方法
- 启动时导入旧 lite-db-storage 中的 savedQueries 和连接快照,成功后清理旧字段
- 新增连接指纹匹配,唯一强匹配自动重绑,歧义场景保留为未匹配
- 侧边栏新增未匹配已存查询分组,并支持手动绑定到目标连接
- 前端保存、重命名、删除查询改为后端持久化,并补充浏览器 mock
- 补充后端与前端迁移回归测试
2026-06-15 12:20:57 +08:00
Syngnat
0b9f0448c8 ️ perf(database): 优化查询元数据加载和连接释放
- 查询编辑器仅预取当前库及 SQL 显式引用库的元数据
- 断开侧边栏连接时主动释放同实例后端缓存连接
- 完善 Redis 连接释放和 Wails 前端绑定
- 修复 SQL Server 存储过程消息结果显示
- 调整查询工具栏布局并补充回归测试
Close #541
2026-06-15 07:21:00 +08:00
Syngnat
675aae16e9 🐛 fix(query-editor): 修复事务提交按钮缺少悬浮反馈
- 调整提交按钮 hover 和 focus 态的背景与阴影样式
- 同步增强提交计数徽标的悬浮视觉反馈
- 补充提交按钮悬浮样式回归测试
2026-06-14 22:56:21 +08:00
Syngnat
9b0e7937f9 🐛 fix(query-editor): 修复小窗口下 SQL 编辑器工具栏换行问题
- 调整 v2 工具栏为单行布局并启用横向滚动
- 保持选择区、事务区和操作区在窄窗口下不换行
- 补充 SQL 编辑器工具栏布局回归测试
2026-06-14 22:47:42 +08:00
Syngnat
03eb26d999 🐛 fix(query-editor): 修复切换查询页后 SQL 美化方言误判
- 同步查询页的 connectionId 与 dbName 状态
- 美化时按活动连接和 tab 上下文兜底选择格式化方言
- 补充 PostgreSQL ::date 场景回归测试
Close #494
2026-06-14 21:52:14 +08:00
Syngnat
f2ffeeaf45 🐛 fix(sql-editor): 修复存储过程与返回结果写语句的结果识别
- 补齐 SQL 分类逻辑,识别 SQL Server 裸存储过程调用、RETURNING/OUTPUT、SELECT INTO 及消息块场景
- 调整多语句执行与批量写入分支,避免返回行或服务端消息被 Exec 路径吞掉
- 为 PostgreSQL、OpenGauss、Kingbase、HighGo 补充 notice 回传能力并增加回归测试
2026-06-14 21:37:02 +08:00
Syngnat
d7632e29a6 🐛 fix(query-editor): 优化事务工具栏排版并修复 Wails 开发页加载
- 将手动事务提交回滚按钮并入主工具栏,移除重复入口和未提交文案
- 调整 v2 查询工具栏顺序,收起结果区入口并统一更多、AI、设置交互
- 修正 Wails dev 地址为 127.0.0.1,避免 index.html not found
2026-06-14 18:49:22 +08:00
Syngnat
a750266e1c 🐛 fix(sqlserver): 修复托管事务下 UPDATE 误报执行失败
- 统一处理 SQL Server Exec 路径的 RowsAffected 返回
- 兼容 BEGIN/COMMIT/ROLLBACK/SAVE 等事务控制语句无影响行数场景
- 补充 SQL Server 事务控制语句与 DML 的回归测试
2026-06-14 18:03:06 +08:00
Syngnat
5f892d29c8 feat(schema): 支持模式编辑删除及按模式导出备份
- 新增 PostgreSQL 系模式重命名与删除能力
- 侧栏模式节点补充右键菜单、编辑弹窗和删除确认
- 导出表结构与备份表数据支持按模式过滤表和视图
- 同步补充 Wails 绑定与前后端定向测试
Close #526
2026-06-14 17:48:29 +08:00
Syngnat
f3e11961dc 🐛 fix(tdengine): 修复旧版 TDengine 元数据查询与驱动版本选择异常
- 放开 TDengine 已安装驱动的历史版本切换入口
- 兼容低版本 SHOW TABLES FROM 语法差异
- 修复表概览加载时报 [0x2600] syntax error near
- 新增后端兼容与前端交互回归测试
- Close #531
2026-06-14 17:22:02 +08:00
Syngnat
9e224d0067 🐛 fix(query-editor): 修复跨库查询字段补全缺失
- 统一 QueryEditor 中库表标识符与表引用解析规则
- 修复 MySQL 反引号及中划线库名场景下的 WHERE 字段补全
- 新增跨库字段补全回归测试
Close #533
2026-06-14 16:59:34 +08:00
Syngnat
3da3a3fb13 🐛 fix(mysql): 兼容 MyCAT 场景下数据库列表解析逻辑
- 扩展数据库名字段识别,兼容 SCHEMA、database_name 等返回列名
- 按驱动返回列顺序兜底提取单列结果,避免非标准列名导致误判为空
- 补充 MyCAT 风格回归测试,覆盖 SHOW DATABASES 与当前库回退逻辑
Close #552
2026-06-14 16:36:42 +08:00
Syngnat
70b469d349 🐛 fix(query-editor): 修复 SQL 美化未按数据库方言选择格式化器
- 为 QueryEditor 美化入口按当前连接类型动态选择 sql-formatter language
- 让 postgres、kingbase、highgo、vastbase、opengauss、gaussdb 使用 postgresql 方言
- 让 oracle、dameng 使用 plsql,避免固定 mysql 方言导致语法解析失败
- 补充 PostgreSQL 窗口函数与类型转换 SQL 的美化回归测试

Close #561
2026-06-14 16:07:52 +08:00
Syngnat
5310ec7c44 feat(query-editor): 为 Postgres 兼容方言补全增加标识符自动引用
- 在 SQL 编辑器补全中识别需要保留大小写的对象名
- 自动为大写表名和字段名插入双引号标识符
- 保持 MySQL 等其它方言现有补全行为不变
- 补充 QueryEditor 相关测试覆盖

Close #562
2026-06-14 15:54:00 +08:00
Syngnat
6cb5998cd6 feat(datagrid): 为表格编辑增加单元格级撤销能力
- 在 V2 与旧版单元格菜单中增加撤销当前修改入口
- 复用现有改单保存链路回退单元格值与脏标记
- 修复刷新后本地改单状态未完全清理的问题
- 补充相关布局与菜单回归测试

Close #563
2026-06-14 15:41:26 +08:00
Syngnat
6bbe5ad30d 🐛 fix(database-icons): 替换数据源官方图标并修正透明底展示
- 为 OceanBase、GaussDB、GoldenDB、Kafka、RocketMQ 等数据源替换官方品牌资源
- 扩展 DatabaseIcons 对 svg/png/ico 的统一加载与底色边框控制
- 补充图标资源断言测试并移除未使用的字母块 fallback
2026-06-14 14:43:03 +08:00
Syngnat
cd6d034d6c 🐛 fix(icons): 替换 RabbitMQ 为官方标识 2026-06-14 13:53:08 +08:00
Syngnat
df3a49becf feat(icons): 补齐新增数据源品牌图标资源 2026-06-14 13:07:57 +08:00
Syngnat
ca9eb65fdd 🐛 fix(connection-modal): 修复 MQTT 无认证用户名误判必填 2026-06-14 12:59:05 +08:00
Syngnat
8d5a24992a 🐛 fix(sql-editor): 修复事务执行会话与工具栏布局交互 2026-06-14 12:40:31 +08:00
Syngnat
7a85c30752 feat(rocketmq): 新增 RocketMQ 数据源连接与测试发消息支持 2026-06-14 12:19:43 +08:00
Syngnat
0fa8afd517 feat(mqtt): 新增 MQTT 数据源连接与测试发消息支持 2026-06-14 11:38:05 +08:00
Syngnat
d805f288ae feat(rabbitmq): 新增 RabbitMQ 数据源连接与测试发消息支持
- 新增 RabbitMQ 管理 API 数据源实现,支持 vhost、queue、exchange 浏览与队列预览

- 统一消息发送弹窗,支持 Kafka Topic 与 RabbitMQ Queue 的测试发送命令生成

- 补齐连接表单、能力矩阵、SQL 方言、图标与前后端回归测试覆盖
2026-06-14 10:49:11 +08:00
Syngnat
12fbc7ecf4 feat(goldendb): 新增 GoldenDB 数据库连接支持
Refs #477
2026-06-13 21:42:18 +08:00
Syngnat
0ff17dc27c feat(kafka): 新增 Kafka 数据源连接支持
Refs #387
2026-06-13 21:11:08 +08:00
Syngnat
d2f68acae8 feat(gaussdb): 新增 GaussDB 数据库连接支持
Refs #497
2026-06-13 19:34:52 +08:00
Syngnat
f3dfffb8d1 feat(iotdb): 新增 Apache IoTDB 时序库连接支持
Refs #546
2026-06-13 18:23:56 +08:00
Syngnat
c805b16fcd feat(qdrant): 新增 Qdrant 向量库连接支持
- 后端新增 Qdrant REST 连接、collection 元数据、scroll/search 查询与 upsert/delete/payload 更新

- 前端新增 Qdrant 类型、连接配置、图标、方言和能力矩阵

- 测试覆盖 mock REST、真实服务 smoke 和前端配置

Refs #555
2026-06-13 17:03:20 +08:00
Syngnat
56126e22f2 feat(chroma): 新增 Chroma 向量库连接支持
- 后端新增 Chroma REST 连接、元数据浏览、JSON/SELECT 查询与 upsert/delete 写入

- 前端新增 Chroma 类型、连接配置、图标、方言和能力矩阵

- 测试覆盖 v1/v2 兼容、真实服务 smoke 和前端配置

Refs #560
2026-06-13 16:47:25 +08:00
Syngnat
d3836da9cb 🐛 fix(table-designer): 修复触发器编辑测试在 CI 中解析失败
- 移除测试中的 __dirname 依赖
- 改用 import.meta.url 定位 TableDesigner 源文件
- 修复 Ubuntu 前端构建阶段 TypeScript 编译错误
2026-06-12 18:07:03 +08:00
Syngnat
ae2b27c4b4 🐛 fix(table-designer): 优化表设计触发器修改入口
- 修改触发器从固定弹窗改为对象编辑 SQL 标签页
- 生成删除旧触发器和创建新触发器脚本,便于执行前审查
- 抽出触发器编辑 SQL 构造工具,统一 TriggerViewer 与 TableDesigner 逻辑
- 保留新增触发器原弹窗路径,降低行为变更范围
- 新增触发器编辑入口与 SQL 构造回归测试
Refs #557
2026-06-12 17:39:34 +08:00
Syngnat
8519748512 🐛 fix(redis): 修复超过16个数据库无法展示
- 后端改为通过 CONFIG GET databases 动态获取 Redis 数据库数量
- 放宽单机和 Sentinel 模式的 RedisDB 索引限制,支持 db16 及以上
- 前端连接配置和持久化不再裁剪高编号 Redis 数据库
- 连接测试成功后按服务端返回的真实数据库列表展示可选 DB
- 增加 Redis db31 展示、切换、保存和 URI 解析回归测试
Refs #558
2026-06-12 17:22:09 +08:00
Syngnat
70134cd77f 🐛 fix(ci/driver): 修复可选驱动构建脚本语法错误
- 删除 dev/release workflow 中多余的 fi
- 修复 optional driver 构建阶段 bash 解析失败
- 覆盖全平台驱动资产 CI 的语法校验问题
2026-06-12 17:20:27 +08:00
Syngnat
356baa1e38 🐛 fix(ci/driver): 禁用 Windows AMD 驱动 UPX 压缩
- 在驱动产物压缩脚本中跳过 windows/amd64 平台
- 保留 Linux 驱动产物 UPX 压缩与 metadata 自检逻辑
- 补充 Windows amd64 跳过压缩且文件不变的脚本测试
2026-06-12 17:00:09 +08:00
Syngnat
5fcc04a200 🐛 fix(sql-editor): 修复补全提示下连续输入光标跳转
- 调整 SQL 补全触发策略,避免普通字母输入高频触发 provider
- 支持 Monaco 补全取消 token,丢弃连续输入时的过期异步请求
- 将 SQL snippet provider 纳入统一 disposable 管理,避免重复注册残留
- 补充 QueryEditor 回归测试覆盖补全触发、取消和释放链路
Refs #504
2026-06-12 16:25:23 +08:00
Syngnat
77a306beb2 🐛 fix(window): 修复外接显示器恢复后字体模糊
- 恢复策略:Windows 最小化恢复时不再依赖 viewport drift 才触发修复
- 渲染刷新:普通窗口执行 1px 尺寸 nudge,强制 WebView2/DWM 重建渲染 surface
- 体验保护:最大化窗口继续保留 zoom reset + resize,避免可见重复最大化动画
- 测试覆盖:补充 restore 无 drift 场景与自动修复路径断言
Refs #495
2026-06-12 15:58:30 +08:00
Syngnat
f5ee61f589 🐛 fix(ci/driver): 修复驱动 manifest 跨平台校验误判
- 修复 validate-driver-release-manifest 复用同一 revision 文件路径的问题
- 按平台生成后立即解析 revision 快照,避免全部平台读到最后一次生成结果
- 补充 darwin/linux/windows 跨平台 manifest 校验回归测试
- 验证线上 dev manifest 可通过 89 个资产 revision 校验
2026-06-12 15:57:04 +08:00
Syngnat
c99cdb5d0d 🐛 fix(driver-manager/mongodb): 恢复旧版 MongoDB 连接兼容
- 默认版本:将 MongoDB 推荐 driver-agent 切到 1.17.9
- 安装策略:v1 兼容驱动优先尝试发布资产,不再强制源码构建
- 状态提示:已安装 2.x 时提示重装以兼容 MongoDB 4.0
- 前端交互:重装驱动优先选择推荐兼容版本
- 测试覆盖:补充版本选择、下载候选和重装传参回归
2026-06-12 15:18:57 +08:00
Syngnat
c3a3387ee3 feat(ai): 优化 AI 对话体验与 MCP 接入配置
- AI 请求:增强 OpenAI 兼容接口降级逻辑,文本模型自动省略图片并在 400 场景重试
- MCP 接入:支持自定义 HTTP 服务监听地址、端口和 Authorization Bearer Token
- MCP 生命周期:停止服务后保留授权信息,并将主动关闭子进程视为正常停止
- 交互优化:移除 AI 对话导出入口,支持关闭常驻状态提示并收敛设置弹窗 toast 宽度
- UI 调整:优化 AI 输入框边框、聚焦态和 Authorization 运行中只读可查看体验
- 测试覆盖:补充 OpenAI 降级、MCP HTTP、AI Header 和设置面板相关用例
2026-06-12 14:51:37 +08:00
Syngnat
c189125aa4 🐛 fix(redis): 修复多节点连接编辑回填主节点
- 编辑 Redis Sentinel/Cluster 配置时优先保留已保存 host:port 作为主节点

- 合并 hosts 附加节点并去重,避免主节点被附加节点替换

- 补充连接弹窗 Redis 多节点回填回归断言
2026-06-12 13:04:56 +08:00
Syngnat
4cc8ab6482 🐛 fix(driver-manager/sql-editor): 优化驱动代理更新提示和事务提交控件
- 调整 driver-agent revision 为提示性校验,允许旧代理继续安装使用并保留需重装提示

- 精简 SQL 编辑器 DML 事务模式与自动提交档位展示

- 补充旧 revision 安装回归和事务控件断言
2026-06-12 12:57:47 +08:00
Syngnat
d1aa06d537 feat(ai): 支持聊天附件解析并优化数据库对象操作 2026-06-12 12:30:28 +08:00
Syngnat
d5688d31f6 🐛 fix(driver): 允许旧驱动代理继续连接并保留重装提示 2026-06-12 11:41:26 +08:00
Syngnat
ff8bf20680 🐛 fix(ai): 复用待响应气泡承载发送失败状态 2026-06-12 11:21:05 +08:00
Syngnat
5061ec081a 🐛 fix(driver): 修复驱动代理 revision 误触发重装并优化事务提示 2026-06-12 11:07:41 +08:00
Syngnat
e83c9f5553 feat(ai): 增强 Redis 拓扑诊断能力
- 增加 Redis Sentinel/Cluster 状态分级、阻断原因、后端适配器和脱敏 URI 示例

- 区分配置拓扑与后端实际拓扑,修正多节点单机配置的 ClusterClient 诊断

- 补充 AI 工具目录流程与 Redis 哨兵/集群提示文案
2026-06-12 10:05:01 +08:00
Syngnat
8eb4bf3954 ♻️ refactor(redis): 抽离 Redis 连接 URI 与拓扑装配逻辑 2026-06-12 09:43:52 +08:00
Syngnat
3d91079020 Merge pull request #556 from Lang-Qiu/auto-pr/type_annotations-1781208363 2026-06-12 09:22:11 +08:00
Syngnat
ab74270cd4 🧹 chore(types): 规范 Monaco 语言包类型声明 2026-06-12 09:20:37 +08:00
Syngnat
b815e7b296 feat(mcp): 增强新增服务参数逐项提示
- 新增 MCP 参数逐项说明,覆盖未知参数、位置参数和常见运行时参数

- 对敏感参数值做脱敏展示,避免提示区泄露 token 或 api key

- 将逐项说明拆分到独立 util,并接入 inspect_mcp_draft 诊断输出
2026-06-12 09:09:47 +08:00
Syngnat
fce50b513c 🐛 fix(sql-editor): 修复 Oracle 事务结束并补充 Redis 拓扑提示
- SQL 编辑器:Oracle 托管事务优先使用 transaction provider 完成提交和回滚

- Redis:拆分 Key 浏览工具栏并展示 Cluster/Sentinel 拓扑上下文

- 测试:补充 Oracle 事务结束和 Redis 拓扑头部回归用例
2026-06-12 08:48:08 +08:00
Syngnat
781a80e03f feat(ai): 增强代码热点拆分诊断 2026-06-12 08:24:13 +08:00
Syngnat
1058da653d feat(ai): 增强 MCP 草稿参数诊断 2026-06-12 07:57:38 +08:00
Syngnat
0573155285 🐛 fix(sql-editor): 防止事务重复提交误报失败 2026-06-12 07:45:32 +08:00
Syngnat
3427a8844a feat(mcp): 增强新增服务业务参数提示 2026-06-12 07:44:51 +08:00
Syngnat
e353fda7a2 ♻️ refactor(connection): 统一连接驱动类型归一化 2026-06-12 06:58:41 +08:00
Syngnat
f956991bda ♻️ refactor(connection): 拆分连接类型能力配置 2026-06-12 04:26:27 +08:00
Syngnat
0bc6941df6 🐛 fix(oracle): 修复表格编辑事务提交失败 2026-06-12 04:18:29 +08:00
Syngnat
c64b1fbb72 ♻️ refactor(connection): 拆分连接类型目录配置
- 抽出连接类型分组、默认端口和提示文案到独立 catalog

- ConnectionModal 仅负责渲染数据源图标和选择流程

- 补充 catalog 单元测试并更新连接弹窗源码快照测试
2026-06-12 04:07:02 +08:00
Auto PR Bot
72143f6a8f Improve code quality: auto-pr/type_annotations-1781208363 2026-06-12 04:06:04 +08:00
Syngnat
d5d4d4fabc feat(ai): 新增 Redis 拓扑诊断探针
- 新增 inspect_redis_topology 内置工具,输出 Redis 单机、Sentinel、Cluster 脱敏拓扑与风险提示

- 接入本地工具执行链、工具目录、系统引导和工具调用状态文案

- 补充工具注册、目录渲染、执行器和拓扑规则测试
2026-06-12 03:55:26 +08:00
Syngnat
03e08bec32 🐛 fix(redis): 修复 Sentinel 切换数据库配置丢失
- 切换 Redis DB 时复用完整 Connect 逻辑,保留 Sentinel、TLS、SSH 等连接参数

- 补充 Sentinel 切 DB 与 Redis RPC 配置字段回归测试
2026-06-12 03:42:12 +08:00
Syngnat
233894f027 feat(mcp): 增强启动命令参数拆分提示
- 识别 command 字段误填整行 MCP 启动命令

- 提供一键拆分 command 和 args 的表单操作

- 补充参数提示工具与组件回归测试
2026-06-12 03:30:17 +08:00
Syngnat
8f0bd61c14 ♻️ refactor(connection): 拆分 MongoDB 连接配置区
- 将 MongoDB 拓扑、SRV、策略和副本集表单拆入独立组件

- 更新连接弹窗源码断言覆盖拆分后的 Mongo 字段

- 已通过前端定向测试、build 和浏览器 Mongo 表单冒烟
2026-06-12 02:58:05 +08:00
Syngnat
8a0dc3a7d3 🐛 fix(transaction): 修复 Oracle 托管事务提交回滚失败
- Oracle 托管事务改为固定物理连接执行 COMMIT/ROLLBACK

- SQL 编辑器事务按归一化方言判断 Oracle 兼容协议

- 补充 Oracle 与 OceanBase Oracle 事务回归测试
2026-06-12 02:51:01 +08:00
Syngnat
453e13c88d feat(ai): 新增代码热点诊断探针
- 注册 inspect_codebase_hotspots 内置工具并接入本地执行器

- 在工具目录、系统提示和斜杠菜单中暴露大文件治理入口

- 增加工具注册、执行器、目录和斜杠菜单测试
2026-06-12 02:30:09 +08:00
Syngnat
4daaa22cba feat(mcp): 支持一键补齐缺失启动参数
- 在 MCP 参数提示中按当前 command 生成缺失必填参数

- 支持一键追加 docker、npx、python 等常见启动参数

- 增加组件交互测试覆盖参数补齐行为
2026-06-12 02:16:26 +08:00
Syngnat
97f062773b ♻️ refactor(connection): 拆分 Redis 连接配置区
- 抽离 Redis 单机、集群、哨兵配置区到独立组件

- 保留 Redis 密码、Sentinel 密钥和 DB 范围设置行为

- 同步更新连接弹窗源码级回归检查
2026-06-12 02:05:10 +08:00
Syngnat
fb73769063 🐛 fix(oracle): 修复 SQL 编辑器事务提交失败
- Oracle DML 托管事务改用固定连接隐式事务

- 提交和回滚通过 COMMIT/ROLLBACK 结束事务

- 覆盖提交、回滚和执行失败回滚场景
2026-06-12 01:42:14 +08:00
Syngnat
4cac8ef3c9 feat(mcp): 优化新增服务模板入口
- 模板入口移入一行命令快速新增面板

- 增加启动命令预览,降低 command 和 args 拆分成本

- 移除设置页重复模板区块并补充交互测试
2026-06-12 01:41:31 +08:00
Syngnat
156fce531c feat(redis): 支持 Redis Sentinel 连接模式 2026-06-12 01:04:43 +08:00
Syngnat
480edbe501 feat(ai): 增强 AI 诊断快捷命令 2026-06-11 23:03:34 +08:00
Syngnat
81fab81d1b 🐛 fix(sql): 修复 Oracle 托管事务提交回滚失败 2026-06-11 22:49:34 +08:00
Syngnat
cba8ff394c feat(ai): 优化内置工具目录检索与参数提示
- 为内置工具目录增加关键词搜索和结果计数

- 参数提示补充类型、默认值、枚举和示例信息

- 补充目录渲染和参数摘要提取测试
2026-06-11 22:29:37 +08:00
Syngnat
6f4e80c749 feat(mcp): 新增运行期失败诊断探针
- 解析 gonavi.log 中 MCP 启动、发现和调用失败信号

- 结合已保存 MCP 服务与工具发现状态输出原因和 nextActions

- 补充系统引导、工具目录、状态标签和回归测试
2026-06-11 22:01:26 +08:00
Syngnat
a9eed57cf7 feat(mcp): 增强环境变量用途提示
- 新增 MCP 环境变量 key 识别与风险提示

- 在新增 MCP 表单展示 env 用途、占位值和 Docker 边界提醒

- 在 inspect_mcp_draft 输出脱敏 envHints 供 AI 解释参数
2026-06-11 21:34:04 +08:00
Syngnat
890d693102 feat(ai): 增强上游请求 payload 结构诊断
- 新增 inspect_ai_upstream_logs 的 payload 结构摘要,识别模型、消息角色、工具数量和请求选项

- 补充 includePayloadSummary 参数提示和系统引导

- 补充上游日志探针回归测试
2026-06-11 21:16:39 +08:00
Syngnat
9038fe1bdf feat(ai): 增强 MCP 草稿校验输出
- 抽取 MCP 草稿 seed 构建逻辑供 UI 和内置工具复用

- inspect_mcp_draft 返回脱敏 suggestedServerSeed

- 同步 slash 命令、系统指导和回归测试
2026-06-11 20:53:43 +08:00
Syngnat
ca1c8559cf 🐛 fix(query-editor): 修复 Oracle 事务提交回滚失败
- 分离 Oracle 托管事务与单次查询执行上下文

- 避免查询结束后取消 BeginTx 上下文导致事务提前回滚

- 补充 sql.ErrTxDone 回归测试覆盖 Oracle 提交路径
2026-06-11 20:38:50 +08:00
Syngnat
bd2bd49e6d feat(ai): 支持 MCP 一行命令快速新增
- 新增 MCP 完整启动命令快速解析入口

- 将解析结果转换为可编辑服务草稿

- 补充快速新增与设置页渲染测试
2026-06-11 20:04:25 +08:00
Syngnat
c1d27448bc ♻️ refactor(ai): 拆分 MCP 客户端接入面板
- 将客户端选择和状态详情拆为独立组件

- 保留本机安装、远程桥接和快速配置展示行为

- 补充选择区和状态区渲染测试
2026-06-11 19:47:11 +08:00
Syngnat
e6d2685521 ♻️ refactor(ai): 拆分远程 MCP 快速配置面板
- 将 OpenClaw/Hermans 远程接入说明抽成独立展示组件

- 保留 schema-only、Bearer Token 和云端配置说明

- 补充远程快速配置渲染测试
2026-06-11 19:28:38 +08:00
Syngnat
8006844b9f 🐛 fix(datagrid): 修复 Oracle DATE 编辑丢失时间
- Oracle-like DATE 字段按日期时间编辑,保留时分秒
- 普通 date 字段维持纯日期编辑行为
- 补充时间编辑器回归测试
2026-06-11 18:27:44 +08:00
Syngnat
5d4989f68f feat(ai): 增加 MCP HTTP 服务与 Docker 配置诊断
- AI 设置页新增 GoNavi MCP HTTP 服务开关与状态展示
- 后端新增 HTTP MCP 子进程生命周期管理和鉴权配置
- 增加 Docker MCP 配置诊断工具与参数提示校验
2026-06-11 18:27:13 +08:00
Syngnat
b7e50118f0 Merge remote-tracking branch 'origin/pr-554' into dev 2026-06-11 18:12:59 +08:00
Arthur
5fc0a6504a fix(ui)旧版UI左侧列表无法通过鼠标扩大问题修复 2026-06-11 16:04:04 +08:00
Syngnat
c9053bccc5 feat(ai): 完善 MCP Docker 启动参数指引
- 新增 Docker MCP 启动模板和参数顺序提示

- 校验 docker run、-i 和镜像名等易漏参数

- 同步 MCP 设置页说明、空状态和单元测试
2026-06-11 15:45:52 +08:00
Syngnat
e4672062f8 🐛 fix(query-editor): 支持 Oracle SQL 编辑器托管事务
- 新增 driver transaction 执行器,支持不适合文本 BEGIN 的数据库

- Oracle SQL 编辑器 DML 托管事务改用 database/sql Tx 提交和回滚

- 补充 Oracle 托管事务提交和失败回滚回归测试
2026-06-11 15:45:13 +08:00
Syngnat
06583abad9 feat(query-editor): 完善片段说明事务状态和结果分页
- SQL 片段新增可编辑语法说明并用于补全详情

- 事务模式改为中文展示并显示未提交变更语句数

- 查询结果支持分页翻页与重新查询全部导出
2026-06-11 14:55:35 +08:00
Syngnat
ce568362c6 ♻️ refactor(sidebar): 拆分侧边栏核心工具函数 2026-06-11 14:15:30 +08:00
Syngnat
ed67a72b68 ♻️ refactor(ai): 拆分 AI 本地检查工具执行器 2026-06-11 13:47:17 +08:00
Syngnat
7850f49429 ♻️ refactor(ai): 拆分 AI 内置检查工具元数据模块 2026-06-11 13:37:48 +08:00
Syngnat
440172aae8 feat(ai): 记录 Claude CLI 上游请求入参日志
- 统一 Claude CLI 聊天请求写入 AI 上游请求日志

- 记录脱敏后的 prompt、参数、模型和工具名称

- 补充 CLI 上游日志解析与脱敏测试
2026-06-11 13:04:59 +08:00
Syngnat
19989e4c26 feat(mcp): 增强新增服务参数填写提示 2026-06-11 12:37:02 +08:00
Syngnat
4265d7cfa9 feat(ai): 新增上游请求日志自查工具 2026-06-11 12:00:17 +08:00
Syngnat
2d562ccfd6 feat(ai): 记录上游请求入参与会话日志
- 增加 OpenAI、Anthropic、Gemini 上游请求体与响应状态日志

- 为 AIChatSend 和 AIChatStream 补充会话生命周期日志

- 增加请求日志脱敏与 URL 密钥过滤测试
2026-06-11 11:07:22 +08:00
Syngnat
74a422a5e2 🐛 fix(sync): 修复 Oracle 同步连接与 MySQL 备份导出异常
- 分离 Oracle/OceanBase Oracle 同步连接 Service Name 与选中 schema

- 兼容旧同步请求中 database 被 schema 覆盖的情况

- 规范 MySQL/MariaDB SHOW CREATE TABLE 标识符引用

Refs #549

Refs #518
2026-06-11 10:24:48 +08:00
Syngnat
450d1d66b4 feat(ai): 完善远程 MCP 结构模式与面板稳定性
- MCP HTTP 支持 schema-only 模式,远程配置默认不暴露 execute_sql

- OpenClaw/Hermans 向导补充安全边界与结构模式命令

- 拆分 AI 面板错误边界和 Linux CJK 字体提示组件
2026-06-11 09:26:54 +08:00
Syngnat
4a944ad23f feat(ai): 完善远程 MCP 指引与排障体验 2026-06-11 08:31:20 +08:00
Syngnat
26fb650e04 feat(ai): 增强 MCP 远程接入与上下文诊断 2026-06-11 07:29:04 +08:00
Syngnat
d3278bb4c4 feat(mcp): 强化远程 Agent 接入配置指引 2026-06-11 01:11:59 +08:00
Syngnat
b438881a50 🐛 fix(ci): 修复跨仓库驱动发布鉴权失败 2026-06-11 00:22:15 +08:00
Syngnat
b11b662071 feat(ai): 完善工具目录与远程 MCP 接入指引
- 新增 inspect_ai_tool_catalog 工具,返回内置探针流程、参数提示和 MCP 工具摘要

- 拆分 AI 内置工具目录配置,降低 AIBuiltinToolsCatalog 体积

- 补充 OpenClaw/Hermans 远程 MCP Streamable HTTP 配置说明

- 增加 Linux CJK 字体缺失检测与 Ubuntu 安装提示
2026-06-10 23:52:19 +08:00
Syngnat
11156c941c feat(mcp): 增加远程 Agent 接入检查工具
- 新增 inspect_mcp_remote_access 内置工具,输出 Streamable HTTP MCP、Bearer Token、隧道和安全边界指引

- 注册工具目录与工具元信息,覆盖 OpenClaw/Hermans 云端 Agent 访问 Windows GoNavi 场景

- 补充远程 MCP 快照和本地工具执行器测试,清理 SQL 编辑器冗余补全常量
2026-06-10 22:19:24 +08:00
Syngnat
0394667680 feat(mcp): 支持远程 Agent 接入和 HTTP 模式 2026-06-10 21:28:00 +08:00
Syngnat
856a5158e4 ♻️ refactor(query-editor): 抽离 SQL 事务控制逻辑 2026-06-10 20:36:25 +08:00
Syngnat
fb00f47031 feat(query-editor): 对齐 DBeaver 风格事务提交模式 2026-06-10 20:24:45 +08:00
Syngnat
55a52bb0f3 feat(ai): 增加 MCP 工具 arguments 示例 2026-06-10 20:07:19 +08:00
Syngnat
630044b740 feat(ai): 显示 MCP 工具参数摘要 2026-06-10 19:58:18 +08:00
Syngnat
69f51f8ec8 ♻️ refactor(query-editor): 拆分 SQL 事务提交设置控件 2026-06-10 19:47:33 +08:00
Syngnat
ab053ef7d1 ♻️ refactor(query-editor): 拆分 SQL 事务工具栏组件
- 抽离 SQL 编辑器待提交事务提示与提交回滚按钮

- 保持 QueryEditor 事务状态与回调逻辑不变

- 同步组件结构测试并验证构建
2026-06-10 19:22:47 +08:00
Syngnat
89639e36bc 🐛 fix(query-editor): 修正 SQL 编辑器 DML 事务识别
- 统一前后端 DML 与数据修改 CTE 的受管事务判断

- 保留数据修改 CTE 返回行并补充事务回归测试

- 明确 SQL 编辑器事务提交策略文案
2026-06-10 19:13:54 +08:00
Syngnat
cf8f9be8dc feat(ai): 新增 SQL 编辑器事务状态探针
- 新增 inspect_sql_editor_transaction 内置探针,返回提交模式、待提交事务和当前 SQL 页签事务语义

- 将 SQL 编辑器待提交事务状态登记到 store,供 AI 只读诊断使用

- 增加 /tx 斜杠菜单、工具目录、系统引导和回归测试
2026-06-10 18:53:24 +08:00
Syngnat
156631c263 feat(ai): 完善 MCP 新增字段填写提示
- 增加 command、args、env、timeout 的应填与勿填对照

- 抽取 MCP 字段指南卡片复用速查与表单说明

- 补充 AI 设置 MCP 区域渲染测试
2026-06-10 18:19:10 +08:00
Syngnat
d8da8d6abf 🐛 fix(editor): 修正 SQL 编辑器 DML 事务提交语义
- SQL 编辑器 DML 固定进入托管事务

- 区分 WITH SELECT 和 WITH DML 的事务判定

- 调整提交方式文案并补充前后端回归测试
2026-06-10 18:05:46 +08:00
Syngnat
61d71cf1d0 feat(editor): 支持 SQL 编辑器增删改事务提交
- 为 SQL 编辑器 DML 新增后端托管事务会话和提交回滚接口

- 增加手动提交与自动提交设置,并显示待提交状态

- 补充前后端事务执行、提交、回滚和自动提交测试
2026-06-10 17:18:34 +08:00
Syngnat
7eb086cade feat(ai): 新增 MCP 草稿校验探针
- 新增 inspect_mcp_draft 内置工具,支持完整命令和分字段草稿校验

- 补充 MCP 新增指引、斜杠菜单和工具目录流程

- 增加工具注册、执行器和系统提示相关测试
2026-06-10 17:17:37 +08:00
Syngnat
c9d0bce153 🐛 fix(workbench): 修复结果区切换与对象设计字段定义
- 修复手动隐藏结果区后快捷键无法再次显示的问题

- 补全 Dameng/Oracle-like 字段长度、精度和可空标识

- 增加 SQL 编辑器、字段元数据和对象设计回归测试
2026-06-10 15:51:03 +08:00
Syngnat
1d1d8d21cd feat(ai): 新增 MCP 工具参数探针 2026-06-10 15:19:23 +08:00
Syngnat
c4153202ba feat(editor): 完善 SQL 编辑与数据编辑交互
- 结果区状态按 SQL Tab 独立保存,快捷键可恢复手动隐藏面板

- 对象设计保留完整字段类型和可空信息,完善兼容驱动 DDL 元数据

- 数据编辑新增手动/自动提交设置和自动提交倒计时

- 修复 schema 视图定位时找不到左侧树节点的问题
2026-06-10 14:27:40 +08:00
Syngnat
8ddd8a726d feat(ai): 新增 AI 消息流诊断探针
- 新增 inspect_ai_message_flow 内置工具

- 识别连续 assistant 气泡、空消息和未闭环工具调用

- 同步工具目录、系统引导、执行状态文案和回归测试
2026-06-10 12:59:09 +08:00
Syngnat
e16082af9a 🐛 fix(sidebar): 修复 GDB 兼容库视图定位失败
- 增加 MySQL 兼容视图元数据查询回退

- 统一编辑器和左侧树的视图元数据查询

- 放宽视图分组下缺失节点类型时的可视定位兜底
2026-06-10 12:14:27 +08:00
Syngnat
2c7962f5d3 feat(ai): 完善 MCP 新增参数指引
- 新增 npx MCP 服务模板和启动预览

- 补充 command/args 表单说明与内置指引快照

- 覆盖 MCP 参数草稿、指引和 AI 配置检查测试
2026-06-10 12:13:45 +08:00
Syngnat
d6f552d539 🐛 fix(sidebar): 修复国产兼容库视图定位失败
- 统一 Sidebar 与 SQL 编辑器的元数据方言解析

- 兼容 GDB/GoldenDB/GreatDB 等 MySQL 兼容驱动的视图元数据

- 放宽左侧树视图定位对 objectType 节点的识别
2026-06-10 11:07:50 +08:00
Syngnat
8f86c4419b ♻️ refactor(ai-tests): 拆分连接与本地资产探针测试
- 将连接能力、外部 SQL、本地查询资产探针测试拆入独立文件

- 继续缩减 aiLocalToolExecutor.test.ts,降低单文件维护成本

- 通过定向测试和生产构建验证
2026-06-10 10:39:27 +08:00
Syngnat
b4affbc1d5 ♻️ refactor(ai-tests): 拆分 AI 配置探针测试
- 将 AI runtime、provider、safety、MCP 与 guidance 探针测试移入独立文件

- 缩减 aiLocalToolExecutor.test.ts 体积,保持执行器行为不变

- 通过定向测试和生产构建验证
2026-06-10 10:00:37 +08:00
Syngnat
0834d8cb3d feat(ai): 将渲染异常纳入应用健康总览
- 在 inspect_app_health 汇总最近一次 AI 回复气泡渲染异常

- 同步内置工具目录和系统探针引导

- 补充应用健康和本地工具执行器测试
2026-06-10 09:24:07 +08:00
Syngnat
d0b9a7f50c feat(ai): 增强 MCP 配置诊断能力
- 复用新增 MCP 表单校验逻辑,向 inspect_mcp_setup 输出服务配置问题

- 将 MCP 配置问题汇总到 AI 设置体检,补充后续处理建议

- 补充 MCP 快照、AI 体检和本地工具执行器测试
2026-06-10 08:33:06 +08:00
Syngnat
7df524e9ef feat(mcp): 增加 MCP 服务配置实时校验
- 新增 MCP 服务草稿校验工具,识别空命令、整行命令、无效环境变量和异常超时

- 在 MCP 服务表单展示配置检查结果,并阻止明显无效配置直接测试或保存

- 补充 MCP 参数校验和卡片渲染回归测试
2026-06-10 02:01:33 +08:00
Syngnat
cb90a4ad01 🐛 fix(sidebar): 修复视图定位慢加载误报
- 兼容 table-prefixed key 的视图兜底定位

- 区分对象树仍在加载与真实未找到状态

- 补充 sidebarLocate 与 Sidebar 定位回归测试
2026-06-10 01:13:29 +08:00
Syngnat
01d8fe44ce feat(ai-tools): 新增 AI 应用健康总览探针 2026-06-10 00:41:48 +08:00
Syngnat
73e93e955c 🐛 fix(sidebar): 修复视图缺失元数据时定位失败 2026-06-09 23:51:36 +08:00
Syngnat
c742b4d61e 🐛 fix(sidebar): 修复视图定位误报未找到
- 视图定位失败时增加表分支可视节点兜底

- 命令搜索对象定位传入精确树节点 key

- 补充国产库视图与元数据缺失场景回归测试
2026-06-09 23:26:15 +08:00
Syngnat
ce06bea744 ♻️ refactor(ai-tools): 拆分 SQL 风险探针执行器
- 将 inspect_sql_risk 执行逻辑从聚合探针执行器中拆出

- 将 SQL 风险工具调用测试迁移到独立测试文件

- 保持本地工具调用行为不变并降低后续扩展成本
2026-06-09 22:31:30 +08:00
Syngnat
48de0b83c4 feat(ai-tools): 新增 SQL 风险预检并优化视图定位
- 新增 inspect_sql_risk 内置工具,识别多语句、写入、DDL、无 WHERE 和安全策略风险

- 在 AI 设置内置工具目录和系统提示中补充 SQL 风险预检链路

- 修复同名视图定位时优先当前数据库 schema 的匹配逻辑
2026-06-09 21:56:28 +08:00
Syngnat
f0afff68c4 ♻️ refactor(ai-chat): 拆分本地工具调用链
- 抽出 useAIChatLocalTools 承载工具执行、熔断和回灌模型逻辑

- 补齐重试消息的工具上下文依赖,避免配置变更后使用旧闭包

- 增加 hook 行为测试并同步 MCP 指南断言
2026-06-09 21:18:39 +08:00
Syngnat
67e0cc752b feat(ai-mcp): 增加 MCP 参数填写速查
- 在 MCP 设置页常驻展示 command / args / env / timeout 字段说明
- 复用现有字段指南,补充示例和填写边界
- 增加无服务场景下的渲染断言
2026-06-09 20:31:59 +08:00
Syngnat
d78c4481f0 🐛 fix(sidebar): 兼容国产库视图定位
- 兼容 MySQL 协议国产库返回的 SYSTEM VIEW / BASE VIEW 类型
- 同步 SQL 编辑器与左侧树的视图元数据识别逻辑
- 增加节点元数据缺失时的唯一可视标识兜底定位
2026-06-09 20:31:24 +08:00
Syngnat
75b60f94d2 🐛 fix(sidebar): 修复视图定位误报未找到 2026-06-09 19:38:41 +08:00
Syngnat
c17d867aa6 🐛 fix(sidebar): 兜底定位表分支中的视图节点 2026-06-09 18:45:30 +08:00
Syngnat
327a78f1cb feat(ai-mcp): 支持 Windows 完整命令自动拆分 2026-06-09 18:44:39 +08:00
Syngnat
af51ead948 🐛 fix(sidebar): 修复视图裸名定位失败 2026-06-09 17:51:43 +08:00
Syngnat
9fab48e64f ♻️ refactor(ai-chat): 拆分会话标题生成逻辑
- 将自动标题生成从 AIChatPanel 抽到独立 hook

- 补充标题生成、清洗和稳定回调测试
2026-06-09 17:24:26 +08:00
Syngnat
acfa112415 🐛 fix(ai-chat): 修复流式回复分裂为多个气泡
- 持久化流式回复状态,避免重渲染后丢失当前 assistant 消息

- 补充回归测试覆盖 chunk 追加到同一气泡
2026-06-09 16:45:39 +08:00
Syngnat
79094d4f3b feat(ai-mcp): 补充 MCP 服务配置排错指引 2026-06-09 16:21:03 +08:00
Syngnat
da7559426c ♻️ refactor(ai-mcp): 拆分外部客户端安装状态逻辑 2026-06-09 15:39:30 +08:00
Syngnat
c8f11d7258 ♻️ refactor(query-editor): 拆分 SQL 结果区渲染组件 2026-06-09 14:57:29 +08:00
Syngnat
c45961f027 🐛 fix(db): 保留多写语句结果并修复 MySQL 字符集参数
- 多条写语句改为逐条返回 affectedRows,避免只显示最后一条结果

- 为写语句结果补充 statementIndex,保持语句与结果映射

- 保留 MySQL charset fallback 逗号,避免驱动解析成 %2C
2026-06-09 14:13:35 +08:00
Syngnat
a6105f4807 feat(query-editor): 支持结果区默认隐藏和快捷键切换
- 默认隐藏 SQL 结果区,执行成功或失败后自动展开

- 增加结果区显示/隐藏按钮和 Win/Mac 快捷键提示

- 在结果表工具栏提供隐藏入口并补充前端回归测试
2026-06-09 14:13:06 +08:00
Syngnat
77b58baff7 ♻️ refactor(sidebar): 拆分 V2 侧栏工具逻辑 2026-06-09 13:39:11 +08:00
Syngnat
8543f1dc65 ♻️ refactor(ai-message): 拆分 Markdown 代码块渲染逻辑 2026-06-09 13:08:01 +08:00
Syngnat
17a3d72852 feat(ai-mcp): 优化外部客户端安装选择状态 2026-06-09 12:11:03 +08:00
Syngnat
b5ba49ff8f ♻️ refactor(ai-tools): 拆分数据库工具执行器逻辑 2026-06-09 11:19:15 +08:00
Syngnat
83972d29b7 ♻️ refactor(ai-chat): 拆分输入区动作按钮组件 2026-06-09 10:43:32 +08:00
Syngnat
58a9adad34 ♻️ refactor(ai-context): 拆分系统上下文探针提示模块 2026-06-09 10:06:19 +08:00
Syngnat
7d1e066997 feat(ai-tools): 新增连接失败诊断探针并接入快捷命令
- 新增基于 gonavi.log 的连接失败总结探针与结构化根因分类\n- 接入 slash 命令、内置工具目录、状态文案和系统提示\n- 补齐本地执行、insight 解析、指令筛选和注册链路测试
2026-06-09 09:45:24 +08:00
Syngnat
9be10beadc 🐛 fix(ai-mcp): 澄清外部客户端接入文案并统一状态语义
- 将外部客户端安装区统一表述为接入流程\n- 按客户端状态调整主按钮文案与重复操作禁用态\n- 同步 MCP 设置导航描述与相关测试断言
2026-06-09 09:23:39 +08:00
Syngnat
8529fbd9e2 ♻️ refactor(ai-mcp): 拆分 MCP 服务卡片并收敛表单逻辑 2026-06-09 08:49:00 +08:00
Syngnat
86095b5bf1 feat(ai-chat): 新增渲染异常探针并拆分聊天面板逻辑 2026-06-09 08:29:02 +08:00
Syngnat
c081d23cc4 ♻️ refactor(ai-settings): 拆分 AI 设置预设与服务桥接配置 2026-06-09 08:04:06 +08:00
Syngnat
6841e69008 feat(ai-tools): 新增日志与快捷键探针并完善 MCP 配置引导 2026-06-09 07:46:38 +08:00
Syngnat
ee5623d290 feat(ai-mcp): 增强 MCP 新增指引与内置工具提示 2026-06-09 06:33:28 +08:00
Syngnat
ae3e08d5f6 🐛 fix(ai-mcp): 强化外部客户端接入说明与单选安装状态 2026-06-09 05:47:55 +08:00
Syngnat
15e0766bbb feat(ai-chat): 新增诊断类 slash 命令并拆分输入区状态 2026-06-09 05:29:06 +08:00
Syngnat
25fb3502e1 feat(ai-tools): 新增AI配置体检探针并拆分本地快照执行器
- 新增 inspect_ai_setup_health 统一诊断供应商、聊天前置、MCP 接入和提示词技能状态
- 拆分 AI 配置类本地快照执行器与共享运行时类型,收缩 aiSnapshotInspectionToolExecutor 体积
- 补充内置工具目录、系统提示链路、定向测试与构建验证
2026-06-09 04:56:30 +08:00
Syngnat
6e7b8ceb39 feat(ai-tools): 新增最近 SQL 活动总结探针
- 新增 inspect_recent_sql_activity 总结最近 SQL 活动的读写与报错分布
- 抽离 SQL 日志洞察模块并复用 recent logs 快照逻辑
- 补齐工具目录、系统提示、状态文案与测试覆盖
2026-06-09 04:22:31 +08:00
Syngnat
b4f46aeecd feat(ai-tools): 新增外部 SQL 文件探针 2026-06-09 03:51:04 +08:00
Syngnat
4162a6491b feat(ai-tools): 新增外部 SQL 目录探针
- 新增外部 SQL 目录快照构建与本地工具执行入口
- 补充内置工具目录、系统提示和状态文案
- 覆盖 AI 面板、工具注册与探针链路测试
2026-06-09 03:26:04 +08:00
Syngnat
f7648413ed ♻️ refactor(ai-chat): 拆分流式消息订阅 Hook 2026-06-09 03:05:00 +08:00
Syngnat
9dde59a6c7 🐛 fix(ai-mcp): 明确外部客户端接入语义与状态提示 2026-06-09 03:01:25 +08:00
Syngnat
f7c20f6d79 ♻️ refactor(ai-chat): 抽离聊天消息分发助手 2026-06-09 02:21:52 +08:00
Syngnat
747cabe447 feat(ai-tools): 新增 AI 历史会话探针 2026-06-09 02:11:35 +08:00
Syngnat
b723b16671 🐛 fix(ai-mcp): 统一外部客户端安装状态文案 2026-06-09 02:07:30 +08:00
Syngnat
bffad0c3a3 ♻️ refactor(ai-chat): 抽离运行时资源加载与设置同步
- 新增 useAIChatRuntimeResources 管理供应商、模型、MCP 工具和 Skills 加载
- 收拢 AI 设置事件监听与模型列表刷新逻辑,减少面板内部副作用堆叠
- 保持 AI 面板行为不变,并通过定向测试、构建和真实页面路径复验
2026-06-09 01:44:20 +08:00
Syngnat
0a48f70643 ♻️ refactor(ai-chat): 拆分面板会话视图与派生状态逻辑
- 抽离 AIChatPanelConversationView 承载欢迎态、历史态和洞察态渲染
- 下沉连接推断、上下文统计和会话裁剪等派生逻辑到独立模块
- 补充守卫测试并验证 AI 面板定向测试、构建和真实页面切换
2026-06-09 01:31:42 +08:00
Syngnat
0a229e8156 feat(ai-tools): 新增能力探针并优化 MCP 接入指引
- 新增 inspect_connection_capabilities 内置探针与工具目录入口\n- 优化 MCP 外部客户端接入状态表达和重复写入保护\n- 同步调整 AI 设置相关测试与系统提示
2026-06-09 00:59:25 +08:00
Syngnat
f7ed6f8e61 ♻️ refactor(ai-tools): 拆分本地工具执行链路
- 抽离 AI 本地工具默认运行时封装

- 拆分数据库工具执行器并保留现有返回协议

- 收敛 aiLocalToolExecutor 为工具路由与结果包装
2026-06-09 00:16:57 +08:00
Syngnat
20e817b203 feat(ai-tools): 新增安全边界探针并拆分工具注册 2026-06-08 23:50:15 +08:00
Syngnat
1284c8c8ef ♻️ refactor(ai-settings): 抽离 MCP 客户端安装状态逻辑 2026-06-08 23:18:12 +08:00
Syngnat
7ac9b89b7d feat(ai-mcp): 补充新增服务字段速查与超时捷径 2026-06-08 22:56:56 +08:00
Syngnat
c0e29a08ba feat(ai-settings): 优化 MCP 新手引导并新增连接盘点探针 2026-06-08 22:42:12 +08:00
Syngnat
cc788d1b25 feat(ai-mcp): 增强完整命令拆分预览 2026-06-08 22:04:24 +08:00
Syngnat
7fa23e72c0 feat(ai-chat): 增强发送前状态提示并新增就绪探针 2026-06-08 21:47:10 +08:00
Syngnat
5ce5d03d69 feat(ai-tools): 新增供应商与模型配置探针 2026-06-08 20:59:15 +08:00
Syngnat
4ac6a9e798 feat(ai-tools): 新增 AI 提示与技能配置探针 2026-06-08 20:41:45 +08:00
Syngnat
472686e8ff feat(ai-tools): 新增 MCP 配置探针并拆分本地执行器 2026-06-08 20:15:29 +08:00
Syngnat
dc38602d32 feat(ai-tools): 新增 AI 运行时探针 2026-06-08 19:50:39 +08:00
Syngnat
5c867fd121 feat(ai-mcp): 补充外部客户端命令检测状态 2026-06-08 19:33:05 +08:00
Syngnat
d7879d9ef0 feat(ai-tools): 新增历史查询与片段探针
- 新增 inspect_saved_queries 与 inspect_sql_snippets 内置工具
- 拆出本地 SQL 资产快照 helper,并补齐执行器与测试覆盖
- 补充工具目录展示、系统提示和执行状态文案
2026-06-08 19:11:23 +08:00
Syngnat
8aa2932878 🐛 fix(ci): 修复 DuckDB Windows 可选驱动构建的 CGO 参数污染 2026-06-08 18:53:55 +08:00
Syngnat
f19ff5fdd5 feat(ai-tools): 新增当前连接探针并拆分 AIChatPanel 运行时模块 2026-06-08 18:53:12 +08:00
Syngnat
7cce1ce30a 🐛 fix(ci): 修正 DuckDB Windows 动态链接参数
- 统一 Windows DuckDB 动态库构建使用的 CGO LDFLAGS
- 为 workflow 中的 DuckDB 库目录补充 GCC 友好的路径格式
- 补充 DuckDB Windows 动态链接参数的回归测试
2026-06-08 18:20:54 +08:00
Syngnat
92e9b0ef75 🐛 fix(ai-mcp): 优化外部客户端默认选中逻辑
- 抽离 MCP 客户端状态归一化与命令格式化工具
- 优先默认选中已接入但需要更新的外部客户端
- 补充状态选择与启动命令格式化测试
2026-06-08 18:15:08 +08:00
Syngnat
a54a357e4b 🐛 fix(ci): 修复 DuckDB Windows 导入库生成链路
- 改为从 duckdb.dll 生成 MinGW 可用的导入库文件
- 同步修复 dev/release workflow 与本机源码构建的 DuckDB Windows 依赖准备逻辑
- 新增导入库生成命令与 buildutil 单测
2026-06-08 17:59:58 +08:00
Syngnat
2e5c3473e1 feat(ai-chat): 新增上下文探针并拆分输入区组件
- 新增 inspect_ai_context 内置工具与系统提示引导
- 拆分 AIChatInput 的上下文、附件、提示条和斜杠命令模块
- 补充工具目录、执行器与输入区相关测试
2026-06-08 17:58:52 +08:00
Syngnat
5f3d1d9880 ♻️ refactor(ai-chat): 抽离系统上下文提示构建逻辑 2026-06-08 17:25:50 +08:00
Syngnat
a575fb49db 🐛 fix(ci): 修复DuckDB动态库同路径复制问题 2026-06-08 17:14:27 +08:00
Syngnat
53b4fcb842 feat(ai-mcp): 补强MCP参数填写引导 2026-06-08 17:06:58 +08:00
Syngnat
2c95009d1f feat(ai-tools): 新增工作区页签探针并拆分界面洞察模块 2026-06-08 17:05:29 +08:00
Syngnat
5b8bbd672e feat(ai-mcp): 优化外部客户端接入引导与状态识别
- 调整 Claude Code 和 Codex 的安装文案与状态标签,明确是把 GoNavi 暴露给外部客户端使用
- 优化 MCP 客户端默认选择逻辑,优先聚焦未接入或需更新的目标并避免刷新后乱跳
- 同步补齐前端 mock、后端状态文案和定向测试,确保安装区交互与状态展示一致
2026-06-08 16:29:53 +08:00
Syngnat
54f1f6970c 🐛 fix(ci): 增强DuckDB库下载容错
- 为 Windows DuckDB 动态库下载增加 curl 重试和超时控制
- 增加 zip 校验与 duckdb.dll/duckdb.lib 存在性检查
- 下载失败时清理临时目录后重试,避免空文件继续参与链接
- 同步修复 dev-build 与 release 工作流的相同脆弱点
2026-06-08 16:14:05 +08:00
Syngnat
8a1e65640e feat(ai-tools): 新增最近SQL日志探针
- 新增 inspect_recent_sql_logs 内置工具用于回看最近 SQL 执行历史
- 接入本地工具执行链,支持按成功或失败状态筛选日志
- 更新 AI 设置内置工具目录、流程说明和工具状态文案
- 完成 vitest、生产构建与预览页内置工具目录验证
2026-06-08 16:10:09 +08:00
Syngnat
0312dfbb16 ♻️ refactor(ai-chat): 拆分洞察与历史模式视图
- 提取 AI 面板的自动洞察与内联历史为独立展示组件
- 保持会话切换与发送主链留在 AIChatPanel 中
- 补充模式内容与消息边界相关回归测试
- 完成 vitest、生产构建与本地预览切换验证
2026-06-08 15:50:40 +08:00
Syngnat
02afeba564 🐛 fix(ci): 为driver-agent压缩增加metadata自检回退
- 在 UPX 压缩脚本里增加 driver-agent metadata 烟测
- 压缩后若 metadata 探测失败则自动恢复原始二进制
- 避免单个驱动因 UPX 压缩后失效导致整个平台构建失败
- 补充压缩脚本的 smoke test 覆盖恢复路径
2026-06-08 09:49:38 +08:00
Syngnat
1a2462ef17 🐛 fix(ai-settings): 修正MCP环境变量录入反馈
- 抽离环境变量草稿解析工具,区分有效项和无效行
- 保留用户原始输入,避免无效行被静默吞掉
- 在 MCP 服务卡片中显示识别数量与无效行提示
- 补充环境变量解析与卡片提示测试
2026-06-08 09:29:40 +08:00
Syngnat
c76b634739 ♻️ refactor(ai-settings): 拆分供应商配置视图组件
- 外提供应商列表与编辑表单组件,收敛 AISettingsModal 体积

- 保留 API Key 掩码、预设卡片切换与返回编辑流

- 补充定向测试、前端构建和真实预览验证
2026-06-08 09:14:55 +08:00
Syngnat
fc88e21811 feat(ai-settings): 补充全库结构快照并拆分AI设置侧栏
- 新增 inspect_database_bundle 内置工具与状态文案

- 拆分 AI 设置导航、安全控制和上下文卡片组件

- 补充定向测试、前端构建和真实预览验证
2026-06-08 09:00:59 +08:00
Syngnat
e1cebb1c9a feat(ai-settings): 优化MCP录入引导并补充结构快照工具
- 新增完整命令自动拆分与提示词设置分区,降低 MCP 配置门槛

- 新增 inspect_table_bundle 内置工具并补充状态文案

- 补齐定向测试、前端构建与预览验证
2026-06-08 08:24:27 +08:00
Syngnat
7d7b775fe0 feat(ai-settings): 优化MCP安装引导并补充表样例预览工具
- 拆分 Skills 区块并补充 MCP 启动模板\n- 改造 Claude/Codex 安装目标选择与状态展示\n- 新增 preview_table_rows 内置工具与目录说明
2026-06-08 08:00:43 +08:00
Syngnat
6c53fb14a6 feat(ai-mcp): 补全跨表字段探针并拆分 MCP 设置区块
- 新增 get_all_columns 内置工具和 MCP server 只读探针
- MCP 服务表单增加实际启动命令预览并补强参数提示
- 抽离 AISettingsMCPSection 并补齐前后端测试
2026-06-08 07:29:52 +08:00
Syngnat
aad0f447c0 🐛 fix(ci): 为前端产物下载增加重试保护
- 为 build 阶段的 frontend-dist 下载增加失败清理和一次自动重试
- 将前端产物固定下载到 frontend-artifact 目录并在解压前校验 tar 文件存在
- 同步加固 dev 与 release 工作流,降低单个 runner 下载抖动导致的误报失败
2026-06-08 06:54:00 +08:00
Syngnat
9d3c77755d ♻️ refactor(ai-message): 拆分消息气泡渲染并补齐展示测试
- 抽离 AIMessageMarkdown 与 AIMessageStatusBlocks,拆分代码块、思考态和探针状态渲染职责
- 优化探针结果查找链路,使用 Map 减少消息渲染时的重复扫描
- 新增消息气泡与 Markdown 代码块测试,并完成 build 与浏览器验证
2026-06-08 06:45:40 +08:00
Syngnat
67dd178166 ♻️ refactor(ai-input): 拆分上下文弹窗并完善命令反馈
- 抽离 AI 输入区的上下文选表弹窗和斜杠菜单,降低 AIChatInput 体积与重复逻辑
- 合并图片粘贴与 slash 状态处理,减少 V1/V2 两套输入分支的重复代码
- 新增 /命令无匹配空状态提示,并补做测试、构建与真实页面验证
2026-06-07 23:19:38 +08:00
Syngnat
802385464d ♻️ refactor(ai-chat): 抽离本地工具执行器并补齐测试
- 将 AIChatPanel 中本地工具执行 switch 抽成独立 helper,降低主面板耦合度
- 为表结构、SQL 安全拦截和工具结果映射补充独立单测
- 保持 AI 面板交互不变,补做构建与浏览器冒烟验证
2026-06-07 22:40:07 +08:00
Syngnat
eff2f7f63a feat(ai-mcp): 补全结构探针并优化客户端接入体验
- 新增 get_indexes、get_foreign_keys、get_triggers 内置工具与 MCP Server 对应实现
- 拆分 AI 设置中的 MCP 接入面板和服务卡片,补充参数提示与客户端状态展示
- 补齐前后端测试与真实页面验证,降低 AI 设置区域的臃肿度
2026-06-07 22:06:24 +08:00
Syngnat
7039eae9c7 🐛 fix(ai-chat): 统一历史模式会话排序
- 面板内历史模式按最近更新时间优先展示会话

- 保持与历史侧栏一致的最近会话浏览体验

- 增加静态测试防止排序逻辑回退
2026-06-07 21:10:39 +08:00
Syngnat
fbed6580fa feat(ai-chat): 增加提示动作并完善历史侧栏体验
- 缺少供应商或模型时在提示区提供可执行入口

- 历史侧栏按更新时间排序并在重新打开时重置搜索

- 替换 Drawer 废弃属性并补充定向测试
2026-06-07 21:05:09 +08:00
Syngnat
7cdd2bd6c0 🐛 fix(ci): 补齐 MCP SQL 检查实现文件
- 将 internal/app/sql_inspect.go 纳入版本控制

- 让 Wails 绑定生成和 mcpserver 依赖的 SQLInspection 类型重新闭合
2026-06-07 20:36:42 +08:00
Syngnat
651c452fe1 🐛 fix(ci): 让 Go 版本跟随 go.mod
- 将 dev-build 的两处 setup-go 改为读取 go.mod

- 同步修正 release workflow,避免构建 Go 版本再次漂移
2026-06-07 20:32:18 +08:00
Syngnat
5b843ee25b feat(ai-mcp): 完善外部客户端安装链路并收紧 SQL 安全控制
- 新增 GoNavi MCP stdio server 与 Claude/Codex 用户级安装入口

- 增加安装状态检测、刷新复制能力和浏览器联调 mock

- 外部 execute_sql 对齐 GoNavi safetyLevel 并补充前端/后端验证
2026-06-07 20:27:50 +08:00
Syngnat
f7d71c6c5c feat(ai): 增加用户提示词与 MCP/Skills 扩展能力
- 支持用户级自定义提示词加载保存与聊天会话注入

- 新增 MCP 服务配置、工具发现与按次调用链路

- 增加 Skills 配置、作用域注入与前后端类型同步

- 补充配置存储与前端行为测试并更新依赖
2026-06-07 17:56:45 +08:00
Syngnat
dda8bbb6e3 🐛 fix(mysql): 修复 GDB 连接参数不兼容导致的握手失败
- 优化 MySQL 兼容 DSN 默认参数
- 在连接验证阶段增加 multiStatements 兼容回退
- 补充相关单元测试覆盖

Refs #543
2026-06-07 14:50:42 +08:00
Syngnat
6932abe674 🐛 fix(ddl): 修复 DDL 格式化方言类型导致的前端构建失败 2026-06-07 13:51:30 +08:00
Syngnat
79c5bfb3d4 feat(connection-import): 支持导入 Navicat NCX 连接与密码
- 新增 Navicat NCX XML 解析与 v1/v2 密码解密能力
- 接入后端连接导入链路并补充导入失败提示
- 前端补充 NCX 格式识别、缺失密码提示与定向测试
2026-06-07 13:38:53 +08:00
Syngnat
ace6e18da8 🐛 fix(sql-editor): 移除对象信息未命中误报提示
- 取消对象信息未命中时的提示弹窗
- 保留已识别对象的 hover 与超链接行为
- 补充未命中静默回归测试
2026-06-07 12:12:02 +08:00
Syngnat
a5b27820cb feat(ddl): 为 DDL 视图增加按方言格式化展示能力
- 新增通用 DDL 格式化工具
- DataGrid 查看 DDL 时按数据源方言输出可读 SQL
- 覆盖 DuckDB DDL 展示与工具层测试
2026-06-05 22:21:40 +08:00
Syngnat
d2189e1442 🐛 fix(duckdb): 修复 DuckDB 查询误用连接超时导致中断
- 新增 DuckDB 查询上下文策略,避免将连接超时直接作为查询执行超时
- 调整 DBQueryWithCancel、DBQueryMulti、DBQueryIsolated 统一走查询上下文工厂
- 补充 DuckDB 查询不继承连接超时与网络型数据库保留 deadline 的回归测试
2026-06-05 21:35:03 +08:00
Syngnat
36a80951a0 🐛 fix(ci/driver): 修复驱动发布错配与 dev 驱动下载命中旧资产
- 发布链路新增 driver release 资产自检,校验已发布二进制与 manifest revision/sha256 一致
- dev-build 与 release 工作流在复用已发布驱动前先校验实际资产,发现旧二进制混入时强制全量重建
- driver manifest 新增 sha256 字段,避免仅凭 source commit 与 manifest 一致就误判发布有效
- 驱动变更检测与全量重建判定纳入 release asset 校验脚本变更,确保链路修复提交会触发自愈重建
- 驱动下载优先使用 GitHub release asset API 地址,并对资产接口按 octet-stream 下载,降低 dev-latest 同名资产命中旧缓存的风险
- 保持 DuckDB 无主键编辑修复不回退,并通过 internal/db 与前端相关回归测试
2026-06-05 20:00:27 +08:00
Syngnat
2ea88c03d3 🐛 fix(ci): 修复 Windows revision 哈希构建失败 2026-06-05 19:10:22 +08:00
Syngnat
5d9499e4dd 🐛 fix(ci): 修复驱动 revision 更新后复用旧构建缓存 2026-06-05 18:56:15 +08:00
Syngnat
fe479778d1 🐛 fix(ci): 修复驱动发布链路变更漏掉全量重建 2026-06-05 18:17:16 +08:00
Syngnat
81eaeb5d5f 🐛 fix(ci): 修复 dev 驱动全量重建基线未透传
- 将 compare_base 从 driver 检测阶段透传到 build job
- 强制全量重建时不再回退到旧 source_commit 做平台 diff
- 避免 dev driver release 成功发布但仍夹带旧 standalone 资产
2026-06-05 17:22:09 +08:00
Syngnat
ea53430d70 🐛 fix(ci): 修复驱动总包打包与 manifest 生成失败
- 抽离 driver release 打包脚本,避免内联脚本删掉已生成 manifest
- 修复 release-assets 场景下 tools 相对路径错误,统一通过仓库根脚本打包
- manifest 改为按源码和目标平台重算 revision,不再执行跨平台 driver 二进制
- 补充 driver release 打包与 manifest 生成回归测试
2026-06-05 16:53:01 +08:00
Syngnat
be26970761 🐛 fix(ci): 修复脏 driver release 资产导致 revision 错配 2026-06-05 16:10:05 +08:00
Syngnat
f7dd90a5d1 🐛 fix(ci): 修复 DuckDB 变更后驱动重建漏判
- 新增 diff-driver-agent-revisions 脚本,直接比对 base/head 在各平台实际生成出的 driver revision 差异
- dev/release workflow 的驱动检测改为源码归因结果叠加各平台 revision diff union,避免只改 DuckDB 时漏重建真实已变更的 driver assets
- 将 CI Go 版本固定到 1.24.3,减少 runner patch 漂移导致的 revision 指纹抖动
- 补充 DuckDB 变更命中与前端变更不误触发的 revision diff 回归测试
2026-06-05 15:03:44 +08:00
Syngnat
2254b76232 🐛 fix(duckdb): 修复无主键结果无法安全编辑
- 为 DuckDB 查询结果和表预览补充隐藏 rowid 定位列,允许无主键表安全提交修改
- DataGrid 提交变更时仅将 rowid 用作定位条件,避免把隐藏定位列写回业务字段
- DuckDB ApplyChanges 对 duckdb-rowid 改用未加引号的 rowid 条件,修复更新和删除失效
- 补充前后端回归测试,覆盖 QueryEditor、DataViewer、rowLocator 与 ApplyChanges 链路
2026-06-05 14:05:18 +08:00
Syngnat
ea1737ab8d 🐛 fix(ci): 修复工作流改动漏触发驱动重建 2026-06-05 12:36:39 +08:00
Syngnat
01bee3d8e4 🐛 fix(ci): 修复 driver revision 跨平台错配
- 改为在 dev/release 主程序构建前按当前平台全量生成 driver-agent revision 指纹
- 避免只重算部分驱动时混入其他平台旧 revision,导致最新 dev 应用误判全量 driver mismatch
- 为 windows/amd64 始终准备 DuckDB CGO toolchain,保证全量 revision 生成可执行
- 补充 revision 平台相关回归脚本,覆盖 darwin/arm64 与 windows/amd64 生成差异
2026-06-05 11:59:30 +08:00
Syngnat
805ab8b3d8 🐛 fix(table-designer): 修复 DuckDB 表设计主键保存失效
- 为 DuckDB 表结构变更补充 ADD PRIMARY KEY 预览 SQL
- 保存前拦截已有主键表的主键替换与删除,避免假成功
- 补充 DuckDB 主键变更判定与 schema SQL 回归测试
2026-06-05 11:25:44 +08:00
Syngnat
6742495c6f 🐛 fix(frontend/ci): 修复对象修改卡顿与 Windows ARM 驱动校验失败
- QueryEditor 为对象修改标签增加 object-edit 轻量模式,跳过重型元数据抓取和对象装饰扫描
- DefinitionViewer 与 TriggerViewer 打开的对象修改标签统一透传 queryMode,避免重新进入普通查询链路
- TriggerViewer 补全 MySQL/Oracle 类触发器 DDL 重建逻辑,修复对象修改打开语法不完整
- 补充对象修改与触发器 DDL 回归测试,覆盖轻量模式和元数据补全场景
- verify-driver-agent-revisions 脚本改为跨架构校验,避免在 x64 runner 直接执行 windows/arm64 二进制
- 新增 Windows ARM CI 校验追踪文档,保留架构校验与 host-native probe 证据
2026-06-05 10:34:18 +08:00
Syngnat
3964ecbe88 🐛 fix(ci): 修复 DuckDB Windows 驱动打包变量未定义
- 修复 dev workflow 中 DuckDB 专属驱动包生成阶段的变量作用域问题
- 修复 release workflow 中同样的 DuckDB zip 打包变量传递错误
- 避免 Windows 全量驱动构建时因 DUCKDB_ZIP_PATH 未定义导致 CI 失败
- 保持 DuckDB driver zip 与 dll 打包流程不变,仅修正 shell 环境变量用法
2026-06-05 09:38:47 +08:00
Syngnat
ebdf7b5a6c 🐛 fix(ci): 修复 dev CI 漏掉驱动补偿构建
- dev 构建改为优先使用已发布 driver release 对应源码提交作为检测基线
- 避免驱动相关提交的 CI 失败后,后续仅修 workflow 时被误判为无需重建驱动
- 当已发布驱动基线无法解析时,保守回退为全量驱动构建
- 补充驱动变更检测补偿场景回归测试
- 新增 driver release source 解析测试,覆盖 release body 与 target_commitish 场景
2026-06-05 09:10:49 +08:00
Syngnat
26ce75a547 🐛 fix(ci): 修复驱动工作流 heredoc 缩进
- 修复 dev-build 中 DuckDB Windows 驱动包 Python heredoc 缩进
- 同步修复 release workflow 同类缩进问题
- 避免 GitHub Actions 将 Python 代码误解析为 YAML 导致工作流无效
2026-06-05 08:40:28 +08:00
Syngnat
53811969c5 🐛 fix(driver-manager): 修复驱动 revision 错配与安装卡住
- 修复 revision 生成文件变更未触发 driver-agent 构建的问题
- 检测脚本自身变更时保守触发全量 driver-agent 构建
- 调整 dev 构建驱动安装策略为发布包优先、源码构建兜底
- 为驱动总包下载和源码构建增加超时边界
- 为驱动管理安装流程增加前端看门狗并补充回归测试
2026-06-05 08:34:38 +08:00
Syngnat
2438899ff5 🐛 fix(driver-manager): 修复驱动安装交互与 DuckDB Windows 发布链路
- 修复单驱动安装期间右侧目录操作被错误禁用的问题
- 调整 DuckDB Windows 优先下载专属 zip 并兼容带 query 的签名链接
- 补齐本地构建与 CI 发布的 duckdb-driver.zip 产物及回归测试
2026-06-05 07:15:16 +08:00
Syngnat
a718c41d5d test(app): 补强 DuckDB 定义刷新与主键回归
- 补充 DuckDB 对象修改链路的真实 DDL 刷新回归测试

- 为 app 层连接门禁增加可替换测试 seam,避免 fake metadata 测试被驱动校验拦截

- 修正 DuckDB metadata 测试的串行与断言稳定性
2026-06-04 22:25:08 +08:00
Syngnat
274c32ebdd 🐛 fix(frontend): 修复 DuckDB 对象编辑与安全修改回归
- 修复 DuckDB qualified table 在查询结果页丢失 schema 导致无法识别主键的问题

- 打开对象修改前强制刷新最新定义,并避免切换对象失败时沿用旧定义

- 为 DuckDB 元数据链路补充前后端回归测试,并给 app 层真实 runtime 测试增加环境门槛
2026-06-04 22:00:55 +08:00
Syngnat
a664f1a869 🐛 fix(driver): 精简驱动安装失败日志
驱动代理 revision 校验失败时不再在内外层重复拼接同一下载来源。

统一安装尝试错误格式,并折叠完全相同的失败条目。

补充 URL 前缀去重和安装尝试错误去重回归测试。
2026-06-04 17:02:46 +08:00
Syngnat
455813e53c 🐛 fix(driver): 修复驱动安装进度回退
驱动安装进度按单次会话做单调归一,避免旧下载事件覆盖新进度。

有单文件预编译资产时跳过驱动总包兜底,减少进度回退和安装失败面。

补充前端进度状态机与后端总包兜底回归测试。
2026-06-04 16:57:00 +08:00
Syngnat
30d1c080a0 🐛 fix(sql-editor): 补全对象定义片段修改模板
- 视图定义以 VIEW 片段开头时转换为 CREATE OR REPLACE VIEW,避免重复拼接 VIEW

- 触发器定义以 TRIGGER 或触发时机片段开头时自动补 CREATE OR REPLACE TRIGGER

- 增加视图和触发器对象修改模板回归测试
2026-06-04 16:18:58 +08:00
Syngnat
f7217583a3 🐛 fix(sql-editor): 修复对象跳转卡死与过程模板缺失CREATE
- 跳过大 SQL 的全局对象装饰扫描,避免 Ctrl/Cmd 点击对象时读取整篇编辑器文本

- 存储过程和函数源码片段缺少 CREATE 时自动补 CREATE OR REPLACE

- 增加过程修改模板与大 SQL 对象跳转回归测试
2026-06-04 16:05:40 +08:00
Syngnat
8f7c790700 🐛 fix(sql-editor): 修复存储过程定义执行截断 2026-06-04 15:46:09 +08:00
Syngnat
4cef232271 🐛 fix(sql-editor): 修复大段INSERT脚本导致全局卡顿
- 对 SQL 字符串字面量和注释做屏蔽,避免对象高亮/hover 扫描 values 中的大量测试数据

- 复用候选 token 收集结果刷新对象装饰,减少无效对象解析

- 补充 INSERT 脚本候选收集和字符串内 hover 的回归测试
2026-06-04 15:10:26 +08:00
Syngnat
5b602bff75 🐛 fix(duckdb): 修复唯一索引元数据安全编辑定位
- DuckDB 显式唯一索引表达式返回字符串包裹标识符时,统一归一化为真实列名

- 补充 DuckDB 主键、唯一约束、显式唯一索引的真实驱动回归测试

- 将 duckdb_metadata.go 纳入 DuckDB driver-agent revision 计算,确保重装驱动后加载新元数据逻辑
2026-06-04 13:52:05 +08:00
Syngnat
4ad1d15781 🐛 fix(driver/sidebar): 修复驱动代理 revision 校验与外部SQL目录重复显示
- driver:下载或总包中的 driver-agent revision 过旧时跳过该候选并继续 fallback

- driver:新增发布资产 revision 校验脚本并接入 dev/release CI

- sidebar:修复 v2 表/视图等筛选下重复显示外部 SQL 目录

- test:补充 driver-agent fallback 与侧栏筛选回归测试
2026-06-04 13:37:09 +08:00
Syngnat
f25a449e20 feat(sql-editor): 增加对象定义修改入口
- 视图、事件、函数/存储过程定义页增加对象修改按钮

- 触发器定义页增加对象修改按钮,并在定义片段场景提示补全 CREATE TRIGGER

- 对象修改入口统一打开 query 标签,复用现有 SQL 执行与连接上下文

- 新增定义页对象修改入口组件回归测试
2026-06-04 11:42:15 +08:00
Syngnat
9d39440438 🐛 fix(sql-editor): 修复对象超链接侧栏定位与样式
- 侧栏定位 fallback 统一按限定名解析,兼容 schema.object 与对象名、大小写差异

- 补充视图、函数、触发器超链接定位事件与树节点匹配回归测试

- 将 SQL 编辑器超链接改为蓝色实线下划线,并补充暗色主题样式断言
2026-06-04 11:41:54 +08:00
Syngnat
02faa4586b 🐛 fix(metadata): 修复多数据源主键唯一索引识别
- 统一 PG-like 数据源字段和索引元数据查询,支持 search_path 可见表

- 兼容 snake_case、布尔别名和字符串唯一索引标记

- 修复 DuckDB main/memory 路径解析,避免误判外部 catalog

- 补充前后端回归测试,覆盖可编辑结果定位和元数据重试路径
2026-06-04 10:49:16 +08:00
Syngnat
9acb1c69f7 feat(sql-editor): 增加SQL错误中文语义提示
- 新增 SQL 执行错误语义化规则,覆盖语法、对象、字段、约束和连接类错误

- 执行失败和刷新失败展示中文语义、处理建议与原始错误

- 补充工具函数与 QueryEditor 回归测试,确保英文报错可读化
2026-06-04 10:48:17 +08:00
Syngnat
a9d515f160 🐛 fix(sql-editor): 修复表名悬停元数据重复展示
- 去除 Cmd/Ctrl 导航高亮 decoration 上的 hoverMessage

- 统一由 SQL hover provider 输出对象元数据

- 补充回归断言,确保表元数据只展示一次
2026-06-04 10:47:30 +08:00
Syngnat
37a094c351 🐛 fix(duckdb): 修复元数据兼容与在线安装回退 2026-06-04 08:27:25 +08:00
Syngnat
f5166ac3fc 🐛 fix(sql-editor): 修复结果消息展示与数据目录迁移稳定性 2026-06-04 07:09:42 +08:00
Syngnat
23ac30086f 🐛 fix(tdengine): 修复低版本驱动连接与表元数据兼容问题
- 修复 TDengine 历史驱动源码构建未按所选版本切换依赖的问题
- 为 DESCRIBE 与 SHOW CREATE 增加旧版本语法降级,避免表详情加载报错
- 为表概览补充 TDengine 专用查询分支,避免误查 information_schema
- 补充 TDengine 兼容性与驱动构建回归测试

Refs #531
2026-06-03 21:33:15 +08:00
Syngnat
c6f6f76489 Merge pull request #530 from origin/pr-530 2026-06-03 20:41:49 +08:00
Syngnat
cbe4645bc6 🐛 fix(store): 修复启动最大化设置未及时持久化的问题
- 为 startupFullscreen 增加即时持久化补写
- 避免用户修改设置后立即退出导致下次启动仍按旧窗口状态恢复
- 补充回归测试覆盖立即重启场景
2026-06-03 20:22:38 +08:00
Syngnat
82cac0b12e 🐛 fix(sql-editor): 修复对象元数据与跳转交互异常
- 修复 SQL 元数据 hover provider 多实例重复注册导致内容重复显示
- 修复侧栏对象拖入编辑器后 Monaco 原生拖拽虚线残留
- 修复跨库拖拽对象丢失来源库导致后续无法跳转
- 兼容 macOS Cmd 点击时 Monaco 未提供 leftButton 的事件结构
- 补充 hover 去重、拖拽插入、对象跳转和 Cmd 点击回归测试
2026-06-03 19:52:32 +08:00
Syngnat
1ae44941dd 🐛 fix(sql-editor): 修复脚本执行拆分与元数据只读提示
- Oracle 匿名块:识别 BEGIN/DECLARE...END 块,避免按内部分号错误拆分
- 执行路径:PL/SQL 块跳过批量写入路径,保持单条语句语义
- SQL 文件:同步修复流式 SQL 文件拆分逻辑
- 查询结果:系统元数据表保持只读但不再弹业务表主键提示
- 测试覆盖:补充前后端拆分、执行和 information_schema 回归用例
2026-06-03 17:11:05 +08:00
Syngnat
4b23c013d9 feat(designer): 将对象设计整合进数据视图并统一设计表交互样式 2026-06-03 15:27:54 +08:00
lixiaodong
cf9b7d9d10 feat(elasticsearch): 生产级 ES 驱动 — 写入支持、分页、多版本兼容
- go-elasticsearch/v8 官方 SDK,支持 ES 6.x/7.x/8.x
- SQL → ES DSL 转换、DevTools 查询、JSON DSL
- _bulk API 批量写入(INSERT/UPDATE/DELETE)
- 别名写入、精确分页、COUNT 统计
- API Key / SSH / SSL 连接支持
- 表设计器只读适配、ES 品牌图标
- 30+ 单元测试全部通过
2026-06-03 10:15:38 +08:00
Syngnat
eeaf3c658b 🐛 fix(duckdb): 修复唯一索引识别与多库对象解析
- 合并 DuckDB 约束与索引元数据,恢复唯一索引表的可编辑判定
- 修复 attach 多库场景下 catalog/schema/table 定位混乱问题
- 统一前后端 qualified name 解析,支持带点和带引号对象名
- 补充 DuckDB 元数据与编辑链路回归测试
2026-06-02 21:12:59 +08:00
Syngnat
8fba42adbf 🐛 fix(sql-editor): 修复对象超链接定位并支持侧栏拖拽插入
- 修复视图、触发器、过程超链接打开后的左树定位失败
- 修正触发器对象内容不显示及错误提示文案
- 支持左侧对象名拖拽插入 SQL 编辑器
2026-06-02 20:29:42 +08:00
Syngnat
dd8af73887 🐛 fix(sidebar): 修复 v2 搜索关闭交互异常
- 关闭命令搜索前提交同步筛选值,避免输入框清空覆盖侧栏筛选

- 限制弹窗打开期间才同步命令输入到侧栏持久筛选

- 增加全局 ESC 关闭监听,修复焦点离开弹窗后无法关闭

- 补充回归测试覆盖筛选保留和全局 ESC 关闭规则
2026-06-02 18:22:07 +08:00
Syngnat
3eb9fd0acb 🐛 fix(sidebar): 修复 v2 侧栏横向滚动条位置 2026-06-02 18:06:31 +08:00
Syngnat
c315ea9c96 feat(elasticsearch): 补齐新建连接入口
- 前端连接弹窗新增 Elasticsearch 入口、默认端口、URI 示例和默认索引配置

- 补齐 Elasticsearch 图标、数据源能力、SQL dialect 和只读查询策略

- 后端驱动管理注册 Elasticsearch 版本、模块路径、构建标签和默认安装入口

- 增加连接展示、能力识别和驱动定义测试覆盖
2026-06-02 15:31:00 +08:00
Syngnat
864ad8a371 ♻️ refactor(elasticsearch): 改用轻量 REST 客户端
- 使用标准库 HTTP 客户端实现 ES ping、索引、mapping 和查询请求

- 保留代理、TLS、超时和 BasicAuth 配置能力

- 移除 go-elasticsearch SDK 及间接依赖,降低 dev 构建下载风险

- 更新 Elasticsearch 后端单测适配 REST 客户端
2026-06-02 15:30:32 +08:00
Syngnat
05d1bc22c6 🐛 fix(driver-agent): 修复 ES 驱动代理 revision 同步
- 补齐 Elasticsearch 在 revision 生成、变更检测和发布补齐脚本中的注册

- 生成 Elasticsearch 真实 driver-agent revision,避免 src-local 占位通过

- 本地测试版本改用 dev-latest driver-agent,避免退到旧 latest 包

- 增加 revision 测试覆盖,禁止可选驱动使用本地占位 revision
2026-06-02 15:30:02 +08:00
Syngnat
e4a8c53079 🐛 fix(window): 修复 Windows 恢复窗口后字体模糊
- Windows 从任务栏恢复窗口时直接触发 WebView2 zoom reset
- restore 场景不再依赖 viewport ratio drift 判断
- 覆盖最大化和非最大化窗口的恢复修复路径
- 保留最大化窗口零动画修复,避免二次最大化抖动
- 补充窗口恢复策略和 App 自动修复路径测试
2026-06-02 14:04:00 +08:00
Syngnat
c72542c92c feat(ai): upgrade MiniMax default model to M3 2026-06-02 13:42:48 +08:00
Syngnat
7f83954714 feat(elasticsearch): 新增 Elasticsearch 驱动支持 2026-06-02 13:42:22 +08:00
Syngnat
8c88017703 ️ perf(sidebar): 优化 v2 命令搜索输入和结果展示
- 修复中文输入法组合输入时按 Enter 误关闭搜索弹窗
- 限制搜索弹窗关闭方式为 ESC 或有效结果确认
- 移除关键词搜索下已加载表结果的固定条数截断
- 同步筛选开启时使用 deferred 值和防抖持久化,降低输入卡顿
- 补充命令搜索 Enter 判定和表匹配完整性测试
2026-06-02 13:40:48 +08:00
Syngnat
3a2db112f3 feat(sidebar): 增强 v2 侧栏搜索持久筛选
- 新增 v2 侧栏搜索模式配置,支持新版命令搜索和旧版侧栏筛选切换
- 命令搜索面板增加同步筛选开关和重置筛选按钮
- 侧栏顶部支持展示并清空已同步筛选词
- 补充 appearance 持久化字段清洗和回归测试
2026-06-02 13:40:20 +08:00
octo-patch
f6877ecfef feat(ai): upgrade MiniMax default model to M3
Add MiniMax-M3 to the MiniMax (Anthropic-compatible) provider model
list and set it as the new default. Retain MiniMax-M2.7 and
MiniMax-M2.7-highspeed for compatibility, and remove the deprecated
M2.5 / M2.5-highspeed / M2.1 / M2.1-highspeed / M2 entries.

Updates the matching backend static-model unit test and the frontend
provider preset (default model + model list).
2026-06-02 12:51:10 +08:00
lixiaodong
17ebc50b83 feat(elasticsearch): 新增 Elasticsearch 驱动支持
- 新增 ElasticsearchDB 实现 Database 接口全部 13 个方法
- 支持 HTTP/HTTPS 连接、Basic Auth、SSH 隧道、代理、SSL 回退
- 查询支持 JSON DSL 和 query_string 两种模式
- 元数据浏览:索引列表、mapping 字段、settings、aliases
- 完整单元测试覆盖(httptest mock,33+ 子测试)
- 注册为可选 Go 驱动,遵循现有 driver-agent 架构

Closes #521
2026-06-02 12:35:46 +08:00
Syngnat
d800f1ce84 🐛 fix(window): 修复 macOS 标题栏双击进入全屏
- 将标题栏双击与 macOS 原生全屏路径解耦

- 双击标题栏时仅执行窗口最大化或还原

- 保留快捷键触发 macOS 原生全屏的能力

- 增加工具栏测试断言覆盖双击和快捷键路径差异
2026-06-02 12:32:32 +08:00
Syngnat
c000bc3c69 🐛 fix(data-grid): 修复底部状态栏窄屏信息被隐藏
- 状态栏外层改为横向滚动,避免 AI 面板开启或小屏时功能被遮挡

- main/right 区域保持 max-content 宽度,避免分页和操作区被压缩

- 移除窄屏下隐藏行数和未提交状态的媒体规则

- 补充布局测试断言,防止状态数字再次被隐藏
2026-06-02 12:32:19 +08:00
Syngnat
c277791ef3 🐛 fix(data-grid): 修复单元格编辑自动首字母大写
- 编辑修复:普通内联单元格编辑输入框增加 noAutoCapInputProps

- 虚拟表格:同步为虚拟行内编辑输入框关闭自动大写

- 输入体验:禁用 autoCapitalize、autoCorrect 和 spellCheck,避免 true 被提示为 True

- 回归测试:新增 DataGrid 布局断言,覆盖两个内联编辑入口
2026-06-02 12:02:28 +08:00
Syngnat
7612657ded 💄 style(query-editor): 调整 v2 查询工具栏布局样式
- 为查询工具栏控件增加 v2 专用 class,移除 v2 下 inline 固定宽度依赖

- 使用内容宽度约束选择区,避免最大行数后出现多余空白

- 覆盖 Ant Design Button.Group 负 margin 和伪元素合并效果

- 增加 CSS 静态断言覆盖对齐、间距和响应式布局
2026-06-02 11:54:06 +08:00
Syngnat
e421662576 🐛 fix(ui): 修复数据表头和侧栏滚动显示
- 修复虚拟数据表横向滚动后表头与数据列错位

- 修复亮色主题字段元数据悬浮提示可读性

- 优化 v2 侧栏外部 SQL 菜单和定位入口文案

- 使用 rc-tree 横向滚动宽度估算并加粗侧栏树滚动条
2026-06-02 11:17:22 +08:00
Syngnat
2afddf497b 🐛 fix(query-editor): 优化 SQL 补全和结果页交互
- 修复新建查询页输入后表名补全失效,支持当前库懒加载与模糊匹配

- 限制长 SQL 实时装饰和持久化草稿,降低输入卡顿

- 执行相同格式化 SQL 时复用结果页并聚焦对应结果标签

- 查询结果标签增加右键关闭菜单并优化标签样式和选中文字行为
2026-06-02 11:16:52 +08:00
Syngnat
c405eb08b5 feat(tabs): 支持标签展示配置并提示保存 SQL 文件
- 新增标签展示元素配置,支持单行、双行布局和元素排序

- 在设置面板提供标签展示入口并持久化用户配置

- 标签右键菜单增加标签设置入口并优化悬浮信息展示

- 关闭外部 SQL 文件标签前检测未保存草稿并支持保存后关闭
2026-06-02 11:16:25 +08:00
Syngnat
e6dd986115 feat(external-sql): 完善外部 SQL 目录文件管理
- 新增外部 SQL 文件的新建、重命名、删除和目录管理接口

- 后端限制 SQL 目录只加载 .sql 文件并补充目录操作测试

- 前端补齐 Wails 类型、浏览器 mock 和外部 SQL 树过滤逻辑

- 支持从外部 SQL 文件标签定位到侧栏目录节点
2026-06-02 11:15:30 +08:00
Syngnat
bf3e21f15c 🐛 fix(datagrid): 修复 DDL 测试图标重复 mock
- 移除 DataGrid DDL 测试中重复的 AimOutlined mock 属性

- 修复 TypeScript TS1117 编译错误

- 验证 DDL 测试与前端构建通过
2026-06-01 12:11:17 +08:00
Syngnat
09139c2553 feat(datagrid): 增加分页跳页并适配窄屏
- 分页条新增跳页输入与提交按钮,支持回车和点击跳转

- 跳页页码自动限制在有效页码范围内,避免越界分页请求

- 为 v2 状态栏增加容器级响应式规则,适配 AI 面板打开后的窄宽场景

- 分页区域增加横向滚动兜底,避免小尺寸屏幕下控件被挤压变形

- 补充 DataGrid 布局回归测试,覆盖跳页控件和窄屏样式规则
2026-06-01 12:05:25 +08:00
Syngnat
b85e7491a9 feat(shortcuts): 新增标签页切换快捷键
- 新增切换到下一个标签页动作,默认 Ctrl+Tab
- 新增切换到上一个标签页动作,默认 Ctrl+Shift+Tab
- 接入全局快捷键处理,按当前标签顺序首尾循环切换
- 补充快捷键默认值与全局执行链路测试

Refs #399
2026-06-01 12:05:25 +08:00
Syngnat
2fee3d1389 🐛 fix(shortcuts): 调整新建查询默认快捷键
- 将新建查询页默认快捷键改为 macOS Cmd+N、Windows Ctrl+N
- 将新建数据源默认快捷键顺延为 Cmd/Ctrl+Shift+N
- 补充默认快捷键唯一性校验,避免动作默认撞键
2026-06-01 12:05:25 +08:00
Syngnat
999efa5947 🐛 fix(shortcuts): 同步侧边栏搜索快捷键提示
- 侧边栏 v2 搜索入口改为读取用户快捷键配置
- 修复搜索入口固定显示默认 ⌘K 的问题
- 按 macOS 语义使用 Cmd+F 作为查找类快捷键
- 移除快捷键描述中的硬编码默认组合
- 补充快捷键展示与平台冲突判断测试
2026-06-01 12:04:08 +08:00
Syngnat
35b7fdf96b 🐛 fix(ui): 修复暗色主题确认弹窗文字可读性
- 为 v2 主题下的 Modal.confirm 标题和内容补充前景色

- 修复删除表确认弹窗在暗色主题下文字不可读的问题

- 新增确认弹窗主题回归测试
2026-06-01 11:59:36 +08:00
Syngnat
5ffaa4361e 🐛 fix(metadata): 修复 Oracle 字段元数据显示缺失
- Oracle 元数据查询为字段名、类型、默认值、注释等列补齐稳定别名

- 新增字段定义归一化工具,兼容 name/Name/COLUMN_NAME 等返回形态

- 修复 DataGrid、DataViewer、QueryEditor、TableDesigner 对字段元数据的读取

- 补充 Oracle 字段注释、表头元数据和主键定位回归测试
2026-06-01 11:59:36 +08:00
Syngnat
63db9fecb3 feat(query-editor): 支持查询重命名导出与保存快捷键
- 支持已保存查询重命名并同步当前标签标题

- 新增 SQL 文件导出接口、Wails 绑定和浏览器 mock

- 补充 Ctrl/Cmd+S 保存查询与 Ctrl+, 快捷键入口修复

- 覆盖 SQL 编辑器保存、导出和快捷键回归测试
2026-05-31 22:32:48 +08:00
Syngnat
e687ae2819 feat(sidebar): 优化对象菜单与旧版布局交互
- 为已存查询右键菜单补充重命名能力并同步已打开标签

- 优化 v2 侧栏与表概览右键菜单定位,避免底部遮挡

- 精简旧版数据视图工具栏布局并统一快捷键显示

- 补充侧栏与表概览菜单回归测试
2026-05-31 22:31:47 +08:00
Syngnat
4cfa4bc63f 🐛 fix(data-grid): 修复数据视图交互与右键菜单问题
- 修复当前页查找高亮、清空与 ESC 取消行为

- 优化单元格编辑器尺寸与选中状态取消逻辑

- 收敛工具栏重复操作并修复右键菜单遮挡

- 补充数据网格布局与右键菜单测试覆盖
2026-05-31 22:30:54 +08:00
Syngnat
73f3e2cf73 feat(query-editor): 增强 SQL 编辑器对象悬浮与快捷查看能力
- 美化 SQL 改为写入 Monaco undo 栈,支持 Ctrl+Z 回退到格式化前

- 新增表名字段名库名语义着色,并在元数据加载后自动刷新装饰

- 支持鼠标悬浮和 Ctrl/Cmd+Q 查看对象信息,兼容 Ctrl/Cmd 点击跳转提示

- 补充 QueryEditor 定向测试覆盖对象 hover、快捷查看和撤销行为

Refs #506
2026-05-31 15:30:09 +08:00
Syngnat
6f132db328 🐛 fix(iris): 修复 InterSystems IRIS 连接后表元数据为空
- 兼容 IRIS INFORMATION_SCHEMA 返回的紧凑列名格式
- 修复表、列、索引元数据读取时字段取值为空的问题
- 保持系统 schema 过滤逻辑,避免误展示内置对象
- 补充 IRIS metadata 回归测试覆盖表列表与列索引解析
- Refs #505
2026-05-31 14:18:40 +08:00
Syngnat
b8053ff368 🐛 fix(data-grid): 修复筛选应用后横向滚动导致字段值错位
- 数据刷新后重置滚动恢复标记,允许重新同步横向偏移
- 虚拟表格恢复滚动时统一走 applyVirtualHorizontalOffset,避免表头与单元格错位
- 补充 DataGrid 横向滚动恢复回归断言
- Refs #508
2026-05-31 14:01:24 +08:00
Syngnat
9ba457c91f 💄 style(query-editor): 补充对象跳转提示样式
- 为 QueryEditor Ctrl/Cmd 对象跳转提示补充虚线下划线样式
- 保持跳转命中时的可点击反馈与测试断言一致
2026-05-31 13:41:16 +08:00
Syngnat
255e484dcf feat(sidebar): 同步标签上下文并补充对象树统计信息
- 切换和关闭标签时同步 activeContext,避免新建查询误用 host 或数据库
- 侧边栏表节点展示行数统计,数据库节点展示表数量
- 旧版 sidebar 工具栏改为稳定五列布局,v1 不再混入 v2 置顶分组
- 补充 sidebar 与 store 回归测试
2026-05-31 13:34:21 +08:00
Syngnat
e5fb03bbcd 🐛 fix(data-grid): 修复当前页查找高亮残留并压缩旧版结果工具栏
- 当前页查找改为即时响应,清空或按 Esc 后立即取消高亮
- 查找渲染版本元数据改为可透传,避免高亮状态残留
- 旧版结果工具栏调整为紧凑单行布局并移除重复分页信息
- JSON 和文本视图隐藏当前页查找入口
2026-05-31 13:33:46 +08:00
Syngnat
bea16b72df feat(data-grid): 优化字段跳转列匹配与回车定位
- 提取字段跳转目标解析逻辑,优先精确匹配再回退模糊匹配
- 旧版跳转列保留补全下拉,选中或回车即可定位
- 移除旧版多余跳转按钮,统一为输入即补全的交互
- 补充 DataGrid 与 dataGridFind 定向回归覆盖
- Refs #509
2026-05-31 13:32:50 +08:00
Syngnat
bea938bc34 ♻️ refactor(query-editor): 移除新建查询页冗余工作区头部
Refs #502
2026-05-30 22:55:40 +08:00
Syngnat
b516acb173 🐛 fix(query-editor): 修复连续按 Ctrl/Cmd 时对象跳转失效 2026-05-30 22:52:53 +08:00
Syngnat
ee96125385 feat(query-editor): 扩展 SQL 编辑器对象跳转到视图触发器和存储过程
- 为 QueryEditor 补充视图、物化视图、触发器和函数元数据解析
- 支持 Ctrl/Cmd 点击打开对应对象定义页并同步当前 host/db 上下文
- 扩展 sidebarLocate 对触发器和函数的定位能力
- 补充 QueryEditor 与 sidebarLocate 定向测试覆盖
2026-05-30 21:44:42 +08:00
Syngnat
6934285d83 🐛 fix(tab-manager): 精简标签冗余信息并优化悬浮卡触发与对齐
- 去掉重复连接后缀、圆点和表类型图标,收敛标签信息密度
- 调整悬浮信息布局为对齐网格,提升字段可读性
- 延长悬浮卡触发时间,减少切换 Tab 时的误触发
2026-05-30 17:27:40 +08:00
Syngnat
5a52b141ed 🐛 fix(ai-panel): 隔离面板与消息级渲染异常避免整块白屏
- 为 AI 面板保留本地错误边界与重新加载兜底
- 为单条消息增加渲染隔离,异常消息不再拖垮整段对话
- 补充面板与消息渲染错误上下文,便于后续定位
2026-05-30 17:26:52 +08:00
Syngnat
fdcbadf918 🐛 fix(connection-modal): 支持编辑态回填已保存密码并保持默认遮罩
- 编辑连接前主动拉取可编辑配置,恢复主密码与 SSH 等已保存密钥
- 支持 AI 供应商编辑态回填 API Key,并保持默认遮罩展示
- 修正 AI 设置长错误提示换行展示,避免测试连接报错被裁切

Refs #489
2026-05-30 17:25:58 +08:00
Syngnat
ebda018e13 🐛 fix(sidebar): 统一新版侧边栏数据库图标尺寸 2026-05-29 16:43:44 +08:00
Syngnat
f653a6eb79 test(font): 补齐字体配置与新版排版回归校验
- 增加字体配置能力相关断言,覆盖字体变量发布、字体列表加载与搜索匹配入口
- 新增 Monaco 排版测试,校验新版界面下代码编辑器和数据编辑器的字体与字号回归
- 保持快捷操作与外观设置的关键文案、结构和字体落点可回归验证
2026-05-29 14:44:39 +08:00
Syngnat
307bcc95d1 🐛 fix(ui): 统一新版界面字体并调整左侧快捷操作布局
- 统一新版界面字体变量在侧边栏、AI 面板、日志、DDL、Redis 与数据视图中的落地使用
- 调整 v2 左侧 rail 布局,将新建组、批量操作表、批量操作库、运行外部 SQL 文件、定位当前打开表迁移到顶部主操作区
- 收敛新版侧边栏树节点、连接信息、字段表头与字段描述的字体与字重表现
- 让 Monaco 编辑器、数据预览和代码展示区域跟随新的 UI/Mono 字体配置
- Refs #510
2026-05-29 14:43:32 +08:00
Syngnat
a7f8ce36df feat(font): 新增系统字体枚举与全局字体配置能力
- 新增 Go 侧已安装字体扫描接口,支持前端读取系统真实字体列表
- 接入 Wails 字体查询导出,补齐 App.d.ts 与 App.js 调用声明
- 新增字体选项构建与匹配工具,区分 UI 字体与等宽字体候选
- 外观设置支持按平台加载字体列表,并支持搜索匹配与默认字体回退
- Store 增加自定义 UI 字体与代码字体配置,持久化全局字体选择
2026-05-29 14:41:56 +08:00
Syngnat
f5f5bbf5eb 🐛 fix(sidebar): 修复新版左侧分组与 Host 拖拽排序
- 新增 sidebarRootOrder 持久化左侧根节点顺序
- 支持分组与未分组 Host 在新版左侧根层混排
- 统一 v2 rail 与树视图拖拽写回根层排序
- 拖拽期间抑制误选中与 Host 误切换
- 补充 Sidebar 与 store 拖拽排序回归测试
2026-05-29 08:39:25 +08:00
Syngnat
8131ea8fc8 🐛 fix(ui): 修复新版数据视图布局与 AI 面板加载容错
- 修复新版数据视图底部分页、列快速定位与当前页查找的对齐和压缩问题
- 优化窄屏下 AI 面板布局,避免挤压工作区并增加懒加载失败重试兜底
- 补充窗口运行时、AI 面板布局与 UI 回归测试,更新相关样式快照
2026-05-28 07:05:48 +08:00
Syngnat
fac826b335 🐛 fix(sidebar): 隐藏达梦等数据源不支持的数据库管理入口
- 新增数据库级 DDL 能力判定,统一收敛新建库、重命名库、删库菜单显示
- 修正 Sidebar V1/V2 右键菜单,避免达梦和 Oracle-like 数据源暴露误导入口
- 补充能力与菜单回归测试,覆盖达梦、Oracle 和 OceanBase Oracle 协议

Refs #496
2026-05-27 20:13:19 +08:00
Syngnat
e069ddf8fa 🐛 fix(ui): 修复命令面板新建查询无响应
- 补充 gonavi:create-query-tab 全局事件监听
- 统一复用 handleNewQuery 创建查询标签页
- 恢复起始工作台与命令面板的新建查询入口
- 增加事件监听回归断言避免后续再次丢失
2026-05-27 19:56:23 +08:00
Syngnat
ccd12742d3 ️ perf(ui): 优化数据页滚动与编辑响应
- 优化 DataGrid 虚拟滚动横向同步与外部滚动条宽度计算
- 降低 v2 数据表内容容器的重绘与持久化写入开销
- 拆分 Tab 内容渲染并收敛 QueryEditor 对活跃标签的订阅
- 修复虚拟编辑态与单元格右键菜单的共享渲染路径
- 调整 v2 数据表编辑态样式并补齐性能复现 harness 对照能力
- 补充 DataGrid 布局与滚动相关回归测试
2026-05-27 19:56:14 +08:00
Syngnat
17695c361d 🐛 fix(metadata): 修复列索引读取连接失效重试
- 为 DBGetColumns 和 DBGetIndexes 增加缓存连接失效后的重建与重试逻辑

- 补充 metadata 读取失败与重建失败日志,便于定位大表同步和主键识别异常

- 新增 metadata retry 单测覆盖列定义与索引定义两条读取链路
2026-05-27 08:44:33 +08:00
Syngnat
0c8c9a9f12 ♻️ refactor(DataGrid): 拆分数据网格视图与交互状态
- 拆分 DataGrid 的筛选、DDL 视图、模态编辑和预览面板状态

- 抽离表头信息、分页栏、视图切换、辅助操作和旧版单元格右键菜单组件

- 优化虚拟单元格渲染判定与横向滚轮意图识别,减少滚动和编辑阶段的无效重绘

- 新增 DataGrid 性能复现页并补齐布局、DDL、列标题与滚动相关测试
2026-05-27 08:43:51 +08:00
Syngnat
aa1e8d8a40 Merge pull request #492 from folltoshe/dev 2026-05-26 09:32:36 +08:00
Syngnat
0d9344ff19 🐛 fix(redis): 修复命令页暗色主题显示异常
- 主题适配:Redis 命令输入区、工具栏、拖拽条和输出区统一接入 workbench 主题
- 编辑器修复:Monaco 命令输入框按暗色/亮色切换 transparent 主题
- 输出修复:暗色主题下输出区使用深色背景与可见文字颜色
- 布局修复:限制输入区拖拽高度,避免压缩底部输出区
- 测试覆盖:新增 Redis 命令页布局回归测试
2026-05-26 09:29:52 +08:00
Syngnat
98418ec5c3 🐛 fix(ui): 修复侧边栏拖拽预览线位置异常
- 拖拽修复:右键点击侧边栏宽度区域不再触发拖拽预览线

- 定位修复:预览线改为基于 Sider 实际 DOM 右边界定位

- 宽度修复:拖拽计算读取 CSS min/max 宽度限制,避免状态宽度与实际渲染宽度不一致

- 回归测试:补充右键阻断和预览线真实边界定位测试
2026-05-26 09:07:03 +08:00
Syngnat
5ab50db51c ️ perf(sync): 优化大表同步分页与批量写入
- 同步分析和预览改为分页扫描差异,避免一次性加载源表和目标表

- 直接导入与源查询同步支持分页读取和分批提交,降低低内存机器 OOM 风险

- 各数据库 ApplyChanges 统一使用参数化批量 INSERT,减少大表同步 SQL 超时

- MySQL 批量写入按行数和参数数量拆分,兼容超宽表场景

- 补充批量插入、分页差异和源查询同步回归测试
2026-05-26 08:27:15 +08:00
Syngnat
aa2177d35a 🐛 fix(ui): 修复 v2 数据视图交互回归
- 筛选优化:隔离 WHERE 输入剪贴板事件并让字号跟随全局设置

- 表视图优化:补齐表头和单元格新版右键菜单及行列复制能力

- 置顶同步:卡片视图、列表视图和左侧对象树统一展示置顶分组

- 数据视图优化:调整分页、字段显示、DDL 侧栏和横向滚动同步体验

- 测试覆盖:补充 DataGrid、Sidebar 和表概览置顶分组回归测试
2026-05-26 08:26:52 +08:00
Syngnat
9118406de3 🐛 fix(shortcuts): 修复全局快捷键配置未生效
- 快捷键执行链路补齐新建数据源和打开 AI 面板动作

- 将创建数据源入口改为稳定回调,避免全局监听依赖丢失

- 补充快捷键管理器动作与实际处理逻辑一致性测试
2026-05-26 08:26:28 +08:00
folltoshe
ef47b27886 feat: 限制窗口的最小大小 2026-05-26 02:19:31 +08:00
Syngnat
654178c8cd 🐛 fix(ui): 修复新版 UI 布局回归并恢复切换样式
- 修复 v2 下 App 外层旧版左侧控件叠加问题,由新版 Sidebar 完整接管左侧布局
- 隔离旧版 AI 悬浮入口和 SQL 日志入口,避免影响新版 UI
- 恢复主题设置中界面版本切换的双卡片样式,移除胶囊分段控件
- 补齐 v2 主题样式、全局字体变量和弹窗按需挂载逻辑
- 增加回归测试锁定新版左侧布局和界面版本切换样式
2026-05-25 10:09:05 +08:00
Syngnat
f73415827c 🔧 chore(ci): 适配 GitHub Actions Windows 新镜像
- 将 Windows 构建 runner 切换为 windows-2025-vs2026

- 覆盖 dev build、release 与 winget 发布流程

- 提前验证 VS 2026 镜像兼容性
2026-05-24 12:42:41 +08:00
Syngnat
d414a38877 🐛 fix(shardingsphere): 修复代理分片表展示为物理表
- 元数据取表接入 ShardingSphere 逻辑表规则

- 兼容 PostgreSQL、MySQL、MariaDB 协议入口

- 补充分片表折叠和降级测试

Refs #410
2026-05-24 12:00:48 +08:00
Syngnat
85a0f9d007 feat(mysql): 新增左侧事件对象展示
- 加载 MySQL 事件元数据并展示事件分组

- 支持双击事件查看定义

- 兼容旧版侧边栏与新版 UI 筛选

Refs #411
2026-05-24 11:38:26 +08:00
Syngnat
358d799af8 🐛 fix(mysql): 兼容 allowMultiQueries 连接参数
- 将 JDBC allowMultiQueries 参数映射为 MySQL driver 支持的 multiStatements

- 修复自定义 MySQL DSN 透传导致旧版本 MySQL 连接失败的问题

- 更新 MySQL 兼容 driver-agent revision

Refs #441
2026-05-24 10:59:52 +08:00
Syngnat
cf0a216329 🐛 fix(datasource): 修复 SQL Server 分页与 ClickHouse 22.8 连接兼容
- SQL Server 表数据分页改用旧版本兼容语法,避免 FETCH NEXT 报错

- ClickHouse HTTP 连接支持移除 client_protocol_version 后兼容重试

- 补充 SQL 分页与 ClickHouse 连接兼容回归测试

Refs #479
2026-05-23 19:14:40 +08:00
Syngnat
8615265ee1 feat(postgres): 新增数据库节点新建模式功能
- 后端新增 CreateSchema 接口,支持在选中 PostgreSQL 数据库下创建 schema

- 侧边栏旧版菜单和新版菜单均增加新建模式入口

- 创建成功后刷新对象树,并支持空模式显示

- 补充 Wails 绑定与创建模式相关测试

Refs #480
2026-05-23 18:32:51 +08:00
Syngnat
ec23d72332 🐛 fix(TabManager): 修复数据视图高度异常
- 补齐标签页工作台 flex 高度链

- 确保旧版 UI 与新版 UI 下 DataGrid 都能撑满父级

- 补充工作台高度布局回归测试
2026-05-23 18:04:18 +08:00
Syngnat
b3f6c45bc1 🐛 fix(DataGrid): 修复筛选字段名显示不完整
- 扩展筛选与排序字段下拉宽度

- 为字段选项补充完整 title 与省略显示

- 补充字段名完整展示回归测试

Refs #481
2026-05-23 18:04:06 +08:00
Syngnat
56b3112a07 🐛 fix(oracle): 修复表结构注释读取与保存报错
- 补齐 Oracle 表字段注释元数据读取

- 在表结构 DDL 中追加表和字段注释信息

- 规范表设计器 Oracle DDL 执行前的分号处理

Refs #482
2026-05-23 17:41:46 +08:00
Syngnat
b9c743d67e feat(query-editor): 增强 SQL 编辑器执行与历史体验
- 支持仅执行选中 SQL、光标所在语句和增量新增语句

- 持久化查询草稿,避免重启后丢失历史 SQL

- 在表字段提示中展示注释信息

- 修复清空默认 SQL 后被自动回填的问题

Refs #483
2026-05-23 17:07:47 +08:00
Syngnat
09af56b1c2 feat(DataGrid): 支持外键字段表头跳转关联表
- 表头增强:外键字段显示跳转入口并提示关联表信息

- 交互优化:点击外键字段打开关联表标签页,避免触发表头排序

- 兼容验证:补充 legacy 与 v2 UI 下的跳转行为测试

Refs #486
2026-05-23 13:36:40 +08:00
Syngnat
872b089b15 ️ perf(sql-import): 优化 SQL 文件流式导入性能
- 使用批量执行减少大 SQL 文件导入的数据库往返

- 引入独立导入会话,保留导入过程中的会话状态

- 批量失败时回滚并降级逐条执行,避免中断后续导入

- 补充 SQL 文件导入与流式拆分回归测试

Refs #487
2026-05-23 12:58:38 +08:00
Syngnat
fd33c31b72 🔧 chore(ci): 升级 GitHub Actions 到 Node 24 运行时 2026-05-23 11:54:48 +08:00
Syngnat
8b8a00b666 🐛 fix(frontend): 修复 dev 构建类型错误
- 补齐 v2 外观配置与侧栏置顶状态的 store 类型和持久化兼容

- 按当前平台解析和录制快捷键配置,适配 mac/windows 双平台结构

- 恢复 AI 入口布局工具导出,修复 App 引用缺失

- 更新 store 快捷键持久化测试断言
2026-05-23 11:20:31 +08:00
Syngnat
24d9db4c51 feat(ui): 完成新版 UI 全量改造
- 整体布局:按新版 UI 重构左侧导航、对象树、连接分组和右键菜单体系

- 数据视图:优化 DDL 侧栏、横向滚动、筛选输入、编辑入口和虚拟表格体验

- AI 面板:重构新版入口、输入区、模型选择、快捷键和悬浮布局

- 标签与快捷键:补齐 Tab 悬浮信息、复制交互和 Mac/Windows 快捷键配置

- 工程质量:新增 v2 主题样式、菜单组件、外观工具和回归测试覆盖
2026-05-22 17:41:06 +08:00
Syngnat
1d90aed187 feat(DataGrid): 优化字符串字段筛选默认匹配方式
- 字符串类型字段新增筛选条件时默认使用包含匹配

- 切换字段时仅更新未手动修改的默认操作符

- 补充筛选操作符类型判断回归测试

Refs #475
2026-05-18 20:55:18 +08:00
Syngnat
7fe72c42b2 feat(DataGrid): 支持拖选单元格自动进入编辑模式
- 优化单元格编辑器进入与退出逻辑

- 支持拖选阈值识别,避免普通点击误触拖选

- 补充点击外部区域自动退出单元格编辑模式

Refs #473
2026-05-18 20:42:41 +08:00
Syngnat
b880b5416f 🐛 fix(connection): 修复 IRIS 连接类型保存后回退为 MySQL
- 将 IRIS 纳入前端连接类型白名单与默认端口配置

- 补齐常见数据源类型别名归一化,避免未知别名回退为 MySQL

- 增加 IRIS 连接保存、导入、自动 Limit 和表数据清空回归测试

- 补齐前后端 IRIS truncate 支持

Refs #476
2026-05-18 20:14:31 +08:00
Syngnat
7b895474ef 🐛 fix(DataGrid): 修复金仓 bit 类型值显示异常
- 按列类型将 bit/varbit 的十六进制值显示为十进制标志
- 同步表格、当前页查找和文本视图的显示逻辑
- 补充 bit 类型显示回归测试

Refs #472
2026-05-18 19:45:52 +08:00
Syngnat
e3515b9eb2 🐛 fix(windows): 修复闪退与驱动代理安装失败
- 修复 WebView2 zoom factor 跨线程调用风险,切回窗口线程执行并增加 recover 与超时保护
- 完善 Redis 命令结果 JSON-safe 兜底,避免复杂返回值格式化触发程序崩溃
- 调整 Windows driver-agent 校验逻辑,仅读取 PE Machine 字段判断架构兼容性
- 避免 COFF string table EOF 被误判为无效 Windows 可执行文件,修复驱动在线安装和本地导入失败
- 补充窗口缩放、Redis 返回值和驱动代理 PE 校验回归测试
2026-05-18 10:28:18 +08:00
Syngnat
c66e8e7b49 ️ perf(ci): 优化 driver-agent 变更检测范围
- 按 driver token 和依赖路径归因共享脚本与 go.mod 变更
- 新增源码 diff 归因逻辑,减少无关 driver-agent 构建
- 保留无法归因场景的全量构建兜底,避免漏构建风险
2026-05-17 11:46:27 +08:00
Syngnat
992d2dee45 feat(iris): 新增 InterSystems IRIS 数据源支持
- 后端新增 IRIS 连接、查询、DDL、索引元数据和 DataGrid 编辑能力
- 接入 optional driver-agent、构建标签、revision 生成和变更检测流程
- 前端新增 IRIS 连接入口、方言映射、能力配置和图标展示
- 修复 IRIS 主键识别、事务开启错误处理和驱动连接关闭问题
- 补充后端、前端和构建脚本相关回归测试
Refs #408
2026-05-17 10:32:08 +08:00
Syngnat
0cde96844d 🐛 fix(windows): 修复在线更新挂起与 WebView2 启动闪退
- 隐藏并释放 Windows 更新脚本进程,避免在线更新打开 cmd 并挂起
- 为更新脚本等待宿主进程退出增加超时保护
- 收窄自动 WebView2 zoom reset 触发条件并补充异常兜底
- 补充 Windows 更新启动与窗口缩放回归测试
Refs #468
2026-05-16 22:13:24 +08:00
Syngnat
6c36bd0a08 🔧 chore(ci): 补齐 Wails 前端构建前置资源目录
- 为 dev 和 release workflow 增加 frontend/dist 初始化
- 保证 Wails module 生成阶段可通过资源嵌入检查
2026-05-16 13:44:44 +08:00
Syngnat
d791303967 ️ perf(ci): 优化 DriverAgents 按需构建流程
- 增加 driver-agent 变更检测任务
- 跳过未变更 driver 的构建与 revision 生成
- 复用前端构建产物,减少矩阵任务重复耗时
2026-05-16 13:38:00 +08:00
Syngnat
0ff3f99c18 🐛 fix(ci): 修复 Windows 前端依赖安装失败
- 修正 Wails 前端安装脚本在 Windows 下启动 npm 失败的问题
- 统一从脚本路径解析 frontend 目录,避免 cwd 变化导致 package.json 定位错误
- 增加 CI 安装诊断日志与 npm 失败状态输出
2026-05-16 11:29:20 +08:00
Syngnat
cfbfda4de3 ️ perf(webview): 降低首屏加载与 WebView2 内存占用
- Monaco Editor 改为首次使用时按需初始化
- AI 面板改为懒加载,延后加载 Markdown 和图表渲染依赖
- 增加 Windows 低内存视觉模式,支持关闭透明 WebView 和 Acrylic
- 补充低内存启动说明与模式解析测试
2026-05-16 11:18:48 +08:00
Syngnat
a5be4cc3ae ️ perf(dev): 优化 Wails 开发启动与 CI 构建耗时
- 新增 Wails 快速开发启动脚本,跳过非必要构建与绑定生成
- 优化前端依赖安装状态判断,减少重复 npm install
- 固定 CI Wails CLI 版本并增加 node_modules 缓存
- 更新开发文档中的快速启动说明
2026-05-16 11:02:43 +08:00
Syngnat
959f32327d 🐛 fix(ci): 提升 Wails 前端依赖安装稳定性
- 启用 GitHub Actions npm 缓存
- 使用 package-lock 驱动前端依赖安装
- 增加 npm fetch 重试参数降低网络抖动影响
2026-05-16 10:33:49 +08:00
Syngnat
1dd1cb9e44 🐛 fix(sqlserver): 修复表 DDL 与索引创建语句生成
- DDL:为 SQL Server 表结构补充 CREATE TABLE fallback 生成
- 索引:在已有索引选择和新增索引弹窗中展示 CREATE INDEX 语句
- 测试:补充 SQL Server DDL fallback 与索引 SQL 预览回归测试
2026-05-16 08:46:51 +08:00
Syngnat
16836375c4 🐛 fix(table-menu): 补齐表相关右键快捷操作
- 表分组右键菜单新增新建表入口
- 表概览卡片和列表右键菜单新增复制表名
- 对齐左侧树与表视图的右键菜单体验
2026-05-15 22:33:31 +08:00
Syngnat
71fca7fb86 🐛 fix(export): 修复 PostgreSQL 布尔字段备份类型错误
- 导出修复:PostgreSQL 系列 bool 字段 INSERT 输出 true/false
- 兼容处理:支持 bool、boolean、pg_catalog.bool 类型识别
- 回归覆盖:补充备份 SQL 布尔字段导出测试
Refs #444
2026-05-15 22:23:41 +08:00
Syngnat
b707c74203 feat(connection): 支持连接 SSL 证书文件配置
- 新增 CA 证书、客户端证书和私钥路径配置
- 为 MySQL、PostgreSQL、ClickHouse、MongoDB、Redis 等连接接入 TLS 证书
- 修正 SSL 模式下证书校验、明文回退和 DER 证书兼容问题
- 补充证书路径保存、RPC 传递和 DSN 生成回归测试
Refs #463
2026-05-15 22:04:20 +08:00
Syngnat
acb119d80e 🐛 fix(query-editor): 修复 Oracle 查询结果编辑提交失败
- 规范化 Oracle/Dameng 未加引号表名大小写
- 按元数据列名映射查询结果可写字段
- 补充查询结果编辑提交回归测试
Refs #464
2026-05-15 21:02:00 +08:00
Syngnat
b9f9a8fca2 feat(sync): 扩展跨库迁移自动建表能力
- 新增 MySQL、PG-like、ClickHouse、MongoDB 同类库迁移规划器
- 支持可映射库对自动建表、补字段及兼容索引迁移
- 修复 MongoDB 创建集合时建表 SQL 为空的执行判断
- 避免 PG-like 主键索引重复迁移并保留默认值表达式
- 更新 Data Sync 自动建表能力提示与回归测试
Refs #465
2026-05-15 20:33:42 +08:00
Syngnat
f2c8122c46 🐛 fix(starrocks): 修复主键元数据识别导致表只读
- 改用 information_schema.columns 读取 StarRocks COLUMN_KEY
- 将主键列规范标记为 PRI,恢复安全行定位能力
- 补充 StarRocks 列元数据解析测试并刷新 driver agent revision
2026-05-15 19:51:33 +08:00
Syngnat
9b1351db23 🔁 sync(dev): 合并 PR#466 代码片段管理工具入口 (#466) 2026-05-15 17:31:46 +08:00
Syngnat
569edbb11a feat(starrocks): 新增 StarRocks 数据源与高级对象能力
- 后端接入:新增独立 starrocks 可选驱动,复用 MySQL wire 协议并支持默认 9030 端口
- 驱动管理:补齐 manifest、build tag、revision、driver-agent provider 和构建脚本
- 前端接入:新增 StarRocks 连接类型、图标、能力矩阵、URI 解析、保存回显和 SQL 自动 LIMIT
- 方言增强:新增 StarRocks 类型、关键字、函数补全和专属建表 SQL 生成
- 高级对象:支持物化视图对象浏览、Rollup 模板、外部 Catalog 模板和高级表设计器参数
- CI 发布:将 StarRocks driver-agent 纳入 dev/release 构建与 release 资产校验
2026-05-15 17:30:08 +08:00
Syngnat
2580e4d6f3 🐛 fix(window): 直接调 WebView2 zoom factor 零感知修复 Windows 字体异常
- 新增 ResetWebViewZoom RPC:从 ctx 反射拿 Wails 内部 *edge.Chromium,调 PutZoomFactor(1.0) 强制 WebView2 重算 D2D/DirectWrite 字体度量,完全不动窗口零动画
- 自动路径:maximised + restore + drift 时直接调 backend reset,告别 9848b8b2 之后字体偶发变大的取舍
- 手动路径:保留 Ctrl+Shift+0 快捷键作为兜底(优先 WebView2 reset,失败回退 toggle)
- 撤回上一版 CSS zoom nudge:实测在 WebView2 上不能修字体度量(度量缓存在 D2D 层不在 Chromium layout 层)
- 反射做了 3 层签名校验(frontend value / chromium 字段 / PutZoomFactor 方法签名),wails 升级破坏接口时返回 error 不让进程崩溃
- 新增 windows-only 反射逻辑测试 4 个、跨平台 RPC 行为测试 2 个、Ctrl+Shift+0 快捷键注册测试
2026-05-15 16:01:18 +08:00
Syngnat
32d51f3c25 🐛 fix(redis): 修复 HGETALL 在命令行下程序闪退
- 根因:go-redis v9 默认 RESP3 协议,HGETALL 返回 map[interface{}]interface{},encoding/json 不支持非 string key 的 map,原值穿透到 Wails RPC 导致 Windows 进程退出
- formatCommandResult 新增 map[interface{}]interface{} 分支,平展为交错 [k1, v1, k2, v2, ...] array,与 RESP2 输出形式一致,前端 array 渲染零改动
- 递归调用确保 XINFO STREAM 等嵌套 RESP3 map 也被平展
- 在函数 doc comment 固化"为什么这样做"防止后人删除
- 新增 3 个单元测试:扁平化 + JSON 可序列化、嵌套 map 递归处理、scalar 与 []byte 不被改变
2026-05-15 15:25:38 +08:00
Syngnat
8b90c0b3f0 🐛 fix(oceanbase): 修复预探测漏判导致 Oracle 路径走 TNS 死路
- 预探测放宽 payload 上限 1024→65536 字节并删除 protocol_version 严格检查,避免 OB 4.x 携带能力位扩展的 handshake 被误判为非 MySQL wire
- Connect Oracle 路由:预探测失败时不再单走 TNS(在 OB MySQL wire 端口上必然失败),改为串行尝试 OBClient capability 路径 → TNS 路径,两路都失败时合并错误信息
- 移除 annotateOceanBaseOracleConnectError 里"GoNavi 暂未实现 OBClient 协议"过时文案,改为说明 OBClient 路径已实现且路由层会优先尝试
- 删除随 probe 放宽而失去意义的 IgnoresNonMySQLProtocol 测试,新增 AcceptsLargerPayload 锁定 64KB 上限内识别 OB 的能力
- 刷新 oceanbase driver-agent revision
2026-05-15 15:18:22 +08:00
Syngnat
067cbd5ab2 🐛 fix(window): 用 CSS zoom nudge 修复任务栏恢复字体变大且不引入重复最大化
- 撤回上次错误的 toggle 改动:恢复 9848b8b2 的 restore 不重新最大化取舍,避免用户在任务栏点击恢复时看到窗口"被弹两次"
- 新增 applyWindowsViewportZoomNudge:通过短暂将 documentElement.style.zoom 设为 1.0001 并在两帧内重置,强制 Chromium 重算 layout metrics 修复字体变大,零可见动画、不动窗口
- maximised + drift + restore 路径从仅 dispatch resize 改为先 zoom nudge 再 dispatch resize
- 锁定 windowStateUi.test.ts 中 shouldToggleMaximisedWindowForScaleFix('restore', true)=false 取舍并补注释禁止再次反转
- windowsScaleFix.test.ts 加 jsdom 环境,新增双帧 zoom nudge 行为测试
2026-05-15 15:03:25 +08:00
Syngnat
c7b8663c06 🐛 fix(oceanbase): 新增 OBClient capability 注入打通 Oracle 租户连接
- 双轨路由:Oracle 协议路径按 mysql wire 端口预探测自动选择,OB MySQL wire 端口走 OBClient capability 注入(复刻 Navicat),其他端口走标准 Oracle TNS
- 默认注入 4 组 OBClient capability attribute(_client_name=OceanBase Connector/J、_client_version、__ob_client_attribute_capability_flag、ob_capability_flag),用户在 ConnectionParams 设置的同名键优先级更高
- 恢复 applyOracleChangesMySQLWire:OBClient 路径写操作使用 mysql "?" 占位符 + Oracle 双引号引用标识符,配合 sql_mode='ANSI_QUOTES' 让服务端按 Oracle 解析
- 删除旧的 errOceanBaseMySQLWireOnOracleRoute fail-fast 死路提示,重写文件头注释固化反转决策(基于用户报告 Navicat 用 OceanBase 数据源同端口连通的真实证据)
- 前端 ConnectionModal 文案对齐:去掉「必须 OBProxy Oracle listener」的误导,改为说明自动路由 + connectionAttributes 调试入口
- 新增 5 个单元测试覆盖默认注入、用户覆盖、DSN 透传、mysql wire 占位符;刷新 OceanBase agent revision
2026-05-15 10:54:37 +08:00
TonyJiangWJ
0c1a800f16 feat(snippets): 添加代码片段管理工具入口
- 工具中心新增代码片段管理入口,与快捷键管理保持同级展示

- 复用现有 SnippetSettingsModal 打开逻辑,保留查询编辑器原入口

- 补充工具中心入口回归测试,防止菜单入口丢失
2026-05-15 00:09:04 +08:00
Syngnat
6c1f56d50e 🐛 fix(oceanbase): 新增 OBClient capability 注入打通 Oracle 租户连接
- 双轨路由:Oracle 协议路径按 mysql wire 端口预探测自动选择,OB MySQL wire 端口走 OBClient capability 注入(复刻 Navicat),其他端口走标准 Oracle TNS
- 默认注入 4 组 OBClient capability attribute(_client_name=OceanBase Connector/J、_client_version、__ob_client_attribute_capability_flag、ob_capability_flag),用户在 ConnectionParams 设置的同名键优先级更高
- 恢复 applyOracleChangesMySQLWire:OBClient 路径写操作使用 mysql "?" 占位符 + Oracle 双引号引用标识符,配合 sql_mode='ANSI_QUOTES' 让服务端按 Oracle 解析
- 删除旧的 errOceanBaseMySQLWireOnOracleRoute fail-fast 死路提示,重写文件头注释固化反转决策(基于用户报告 Navicat 用 OceanBase 数据源同端口连通的真实证据)
- 前端 ConnectionModal 文案对齐:去掉「必须 OBProxy Oracle listener」的误导,改为说明自动路由 + connectionAttributes 调试入口
- 新增 5 个单元测试覆盖默认注入、用户覆盖、DSN 透传、mysql wire 占位符;刷新 OceanBase agent revision
2026-05-14 17:50:23 +08:00
Syngnat
235bc99846 🐛 fix(window): 修复 Windows 任务栏恢复最大化窗口后字体保持过大
- 问题根因:9848b8b2 禁用 maximised+restore 的 toggle 路径属于过度修复,导致从任务栏点击恢复最大化窗口时 viewport drift 无人修复
- maximised 状态下 Windows API 拒绝 SetSize nudge,唯一可行的修复是 Unmaximise → Maximise 切一次,此前被剪掉
- shouldToggleMaximisedWindowForScaleFix 在 restore + drift 时重新允许 toggle,注释说明去重保护链路
- 重复 toggle 由 inFlight 互斥 + 700ms 冷却 + ratio-change 在 minimisedSeen 上下文合并到 activationTimer 共同防御
- 拆分测试断言:shouldApplyWindowsScaleFix 与 shouldToggleMaximisedWindowForScaleFix 各自独立覆盖 restore 场景
2026-05-14 12:31:19 +08:00
Syngnat
f94a0429d5 🐛 fix(oceanbase): 解决 Oracle 租户 MySQL wire 下双引号被误解析与列元数据静默失败
- DSN 注入 sql_mode='ANSI_QUOTES':让元数据查询的 AS "OWNER" 与 ApplyChanges 的 "schema"."table" 在 MySQL wire 上被识别为标识符
- sql_mode 加入 mysql driver 参数白名单,避免被 mergeMySQLConnectionParam 过滤丢弃
- 加载 Oracle 列元数据失败不再静默,改为返回带 ALL_TAB_COLUMNS 诊断提示的明确错误
- 修复 stripOceanBaseConnectionParamsForCache 未剥离 # 片段导致与 resolveOceanBaseProtocolParam 行为不一致
- 锁定 mysql ParseDSN 对 sys@oracle001#cluster:p@ss 类租户凭据切分的 invariant,防止未来误加 url.QueryEscape
- 同步 OceanBase agent revision,强制旧 driver-agent 被运行时校验拒绝
2026-05-14 12:09:19 +08:00
Syngnat
17331ddbaa 🐛 fix(oceanbase): 修复 Oracle 租户连接误走 go-ora
- 连接层改为通过 OceanBase MySQL 兼容协议建立 Oracle 租户连接
- 保留 Oracle 元数据包装,避免表结构和 schema 查询退回 MySQL 方言
- 修复 Oracle 租户数据编辑在 MySQL wire 下的占位符格式
- 更新 OceanBase driver-agent revision,确保 dev 包触发驱动刷新
2026-05-14 11:47:32 +08:00
Syngnat
527ecd37e1 🐛 fix(oceanbase): 增强 Oracle 协议连接校验与诊断
- 运行时校验可选 driver-agent revision,避免旧代理继续被复用
- OceanBase agent revision 纳入 oracle_impl.go 指纹并重新生成
- OceanBase Oracle 保留 URI 中的 Oracle 连接参数
- Oracle DSN 默认写入连接和读取超时,并输出脱敏诊断摘要
- 补充 revision、Oracle DSN、OceanBase Oracle 参数提升测试
2026-05-14 10:30:17 +08:00
Syngnat
6456658576 feat(query-editor): 新增选择当前语句快捷键
- 快捷键配置:新增选择当前语句动作,默认绑定 Ctrl+E
- 编辑器接入:在 Monaco 查询编辑器中注册选择当前语句 action
- 语句识别:新增 SQL 语句范围解析,支持按光标定位当前语句
- 兼容处理:忽略字符串、注释和 dollar quote 内的分号
- 测试覆盖:补充快捷键默认配置和语句选择解析单元测试
Refs #404
2026-05-14 09:19:28 +08:00
Syngnat
f8abe60dc2 🐛 fix(oceanbase): 修复 OceanBase 协议模式识别与缓存隔离
- 支持 MySQL/Oracle 租户协议在前后端统一解析
- 拒绝 Native 协议并避免误回退为 MySQL
- 修复 Oracle 模式下元数据、DDL、SQL 方言识别
- 修复连接缓存键与实际协议解析优先级不一致问题
- 补充前后端协议解析与缓存隔离回归测试
2026-05-13 22:51:01 +08:00
Syngnat
01eb2c25e0 feat(mongodb): 支持文档可视化编辑与删除
- 前端表格预览使用 _id 构建 MongoDB 行定位,并隐藏 typed ObjectID locator

- 后端 ApplyChanges 支持 MongoDB 更新、单删和批量删除,区分 ObjectID 与字符串 _id

- 补充 DataViewer、DataGrid 与双版本 Mongo driver 回归测试

Refs #458
2026-05-13 21:48:14 +08:00
Syngnat
2ad2f26b2b 🐛 fix(ddl): 修复金仓建表语句缺少字段备注
- 在 fallback DDL 中追加字段备注语句
- 生成 COMMENT ON COLUMN 并处理单引号转义
- 补充金仓字段备注回归测试
Refs #459
2026-05-13 20:38:43 +08:00
Syngnat
75185f5e66 feat(sidebar): 新增表节点右键复制表名功能
- 左侧表节点右键菜单增加复制表名入口
- 复制真实表名并提供中文剪贴板提示
- 补充表名解析逻辑测试
Refs #460
2026-05-13 20:28:18 +08:00
Syngnat
0bcb8ce6c3 🐛 fix(ai-chat): 修复输入法候选阶段回车误发送
- 识别 IME keyCode/which 229 的候选输入事件
- 避免候选词确认触发 AI 对话发送
- 补充发送快捷键回归测试
Refs #461
2026-05-13 20:19:44 +08:00
Syngnat
b2b1e6b944 🐛 fix(connection): 收敛数据库连接参数白名单
- MySQL 兼容 JDBC 参数映射并丢弃 allowPublicKeyRetrieval 等无效参数
- 为 PostgreSQL 系、SQL Server、Oracle、达梦、TDengine 接入驱动参数白名单
- 补充连接参数归一化、别名映射和未知参数过滤回归测试
2026-05-13 17:51:02 +08:00
Syngnat
e6a1333f83 🐛 fix(ci): 兼容 dev-latest 标签不存在时的删除失败
- 修复 dev CI 首次发布时删除不存在 tag ref 返回 422 导致失败
- 同步处理 GoNavi 主仓库和 DriverAgents 仓库的 dev-latest reset 步骤
- 保留非 Reference does not exist 异常继续抛出,避免吞掉真实发布错误
2026-05-13 14:54:44 +08:00
Syngnat
bf7b9092df 🐛 fix(kingbase): 统一金仓标识符引用策略
- 标识符处理:下沉 Kingbase 引用逻辑,普通小写 schema/table 不再强制双引号包裹
- 表操作修复:修复截断、清空、导入、导出等路径生成异常双引号 SQL
- 同步链路修复:统一数据同步、预览、迁移建表中的 Kingbase schema.table 拼接规则
- 自定义驱动兼容:补齐 kingbase8/kingbasees/kingbasev8 别名归一与写入路径处理
- 回归覆盖:新增 ldf_server.andon_events、转义引号、保留字和大小写标识符测试
2026-05-13 10:25:25 +08:00
Syngnat
1f3cc2c686 feat(driver): 拆分驱动资产到独立发布仓库
- 驱动下载和版本查询统一切换到 Syngnat/GoNavi-DriverAgents
- release/dev-build 将单驱动资产、总包、索引和校验文件发布到独立仓库
- GoNavi 主仓库 Release 仅保留主程序资产和主程序校验文件
- dev 构建驱动资产使用 dev-latest 预发布供测试版下载
- 补充驱动仓库 URL、dev tag 和总包兜底相关测试
2026-05-13 09:21:50 +08:00
Syngnat
e09391a286 feat(driver): 支持单驱动资产优先发布
- 发布流程:release/dev-build 同步输出单驱动 driver-agent 顶层资产
- 安装优化:复用现有直链优先逻辑,降低单驱动安装下载体积
- 兜底保留:继续生成 GoNavi-DriverAgents.zip 和索引文件
- 安全校验:新增驱动资产同名冲突检查,避免发布时静默覆盖
2026-05-13 08:29:28 +08:00
Syngnat
6d5d49ef50 🐛 fix(redis): 修复命名空间行点击误选中
- 命名空间行点击改为展开或折叠分组

- 阻止行点击冒泡触发 Tree 复选/选中逻辑

- 增加 Redis 树行点击交互回归测试

Refs #457
2026-05-12 22:09:51 +08:00
Syngnat
9848b8b295 🐛 fix(window): 避免 Windows 恢复窗口时重复最大化
- 恢复窗口场景不再触发最大化窗口切换修复

- 恢复期间 DPR 变化延迟到窗口状态稳定后处理

- 更新任务栏恢复窗口缩放策略测试
2026-05-12 21:48:41 +08:00
Syngnat
0fea730908 🐛 fix(window): 修复 Windows 恢复窗口后字体缩放异常
- 记录最小化和隐藏状态以识别任务栏恢复场景

- 恢复窗口时使用 restore 缩放修复路径校正 viewport drift

- 增加任务栏恢复窗口缩放逻辑测试
2026-05-12 21:47:24 +08:00
Syngnat
10a695ba0f 🐛 fix(data-viewer): 修复表数据刷新后总数缓存过期
- 刷新时校验已知总数是否满足当前页 hasMore 信号

- 旧总数过期时清空 countKey 并重新统计总数

- 增加表数据增长后的分页回归测试
2026-05-12 21:45:44 +08:00
Syngnat
65567221ac feat(driver): 完善驱动批量管理并优化总包安装
- 驱动管理支持批量安装、重装需更新和删除外置驱动

- 批量任务增加总进度展示,并实时刷新已完成驱动状态

- 后端复用驱动总包下载缓存,支持并发等待和长超时下载

- 开发态优先本地构建 driver-agent,避免发布包 revision 不匹配

- DuckDB 构建自动探测 UCRT64 gcc 工具链

- 驱动总包构建接入 UPX 压缩以降低发布体积
2026-05-12 07:17:28 +08:00
Syngnat
0f891be026 🎨 style(query-editor): 收紧查询页标签与工具栏间距
- 去除主标签页与结果标签页默认下边距
- 缩小查询工具栏顶部留白
- 优化查询编辑器纵向空间利用
Refs #420
2026-05-11 20:38:42 +08:00
Syngnat
b22d28b79c 🐛 fix(oracle): 修复 Oracle/Dameng 打开表时缺少 schema 前缀导致 ORA-00942
- 问题根因:GetTables 在 dbName 为空时走 user_tables 分支返回纯表名,下游 SQL 缺少 owner 前缀,引用未授权 schema 的表时报 ORA-00942
- SQL 修复:user_tables 分支改用 USER 伪列拼接 owner,确保始终返回 OWNER.TABLE_NAME 格式
- 驱动兼容:列别名用双引号包裹强制大写(AS "OWNER" / AS "TABLE_NAME"),避免不同驱动返回不一致 case 导致 row map 取值失败
- 边界保护:增加 TABLE_NAME 为 NULL 的跳过逻辑,避免污染表名输出
- 达梦对齐:DamengDB.GetTables 同步修复,保持与 Oracle 实现一致
- 测试覆盖:新增 3 个回归用例(all_tables 路径、user_tables 路径、NULL 值跳过),扩展 recording driver 支持 mock 任意查询结果
Refs #445
2026-05-11 19:46:24 +08:00
Syngnat
2d9d5f0e98 feat(data-grid): 支持右键复制字段名称
- 新增单元格右键菜单“复制字段名称”
- 将表格复制成功提示改为中文
- 补充字段名称解析回归测试
2026-05-10 20:55:16 +08:00
Syngnat
7dc9da0fd0 🐛 fix(dameng): 修复特殊字符密码导致连接认证失败
- 调整达梦 DSN 生成逻辑,密码按驱动解析规则原样传入
- 移除默认 escapeProcess 参数示例,避免误导配置
- 补充特殊字符密码与问号密码的回归测试
Refs #446
2026-05-10 20:26:19 +08:00
Syngnat
a11d39f981 Merge pull request #452 from TonyJiangWJ/feature/shortcuts-conflict
🐛 fix(shortcuts): 修复编辑器快捷键冲突处理
2026-05-10 19:55:24 +08:00
Syngnat
4ce920cc86 Merge pull request #453 from TonyJiangWJ/feature/datagrid-enhance
 feat(data-grid): 增强数据表编辑与展示体验
2026-05-10 19:26:58 +08:00
TonyJiangWJ
1965564386 feat(data-grid): 增强数据表编辑与展示体验
- 新增变更预览能力,支持在提交前查看删除、更新和新增对应的 SQL 语句
- 增加表格密度配置,统一控制默认列宽、行高、字号与单元格内边距
- 优化 DataGrid 编辑状态展示,区分新增、修改和删除行列的视觉反馈
- 调整导出入口与 Wails 前端绑定,补齐变更预览相关调用与测试覆盖
2026-05-10 19:00:47 +08:00
TonyJiangWJ
f3d325ddab 🐛 fix(shortcuts): 修复编辑器快捷键冲突处理
- 新增保留快捷键冲突检测,区分浏览器、Monaco 编辑器和数据表格等不同冲突来源。
- 在快捷键设置弹窗中展示冲突提示,并在录入冲突快捷键时给出覆盖或可能失效的反馈。
- 将执行 SQL 快捷键注册到 Monaco 内部 keybinding,确保可覆盖编辑器默认快捷键并触发当前活跃查询。
- 增加快捷键冲突检测和 Monaco keybinding 转换的单元测试,覆盖常见组合键与边界情况。
2026-05-10 18:43:36 +08:00
Syngnat
c0ae40c638 🐛 fix(mysql): 修复旧版 Windows 无法解析 Asia/Shanghai 时区
- 嵌入 Go IANA 时区数据,兼容 Windows Server 2012 等缺少 zoneinfo 的环境
- 保持 MySQL serverTimezone=GMT+8 到 loc=Asia/Shanghai 的时间语义
- 增加 MySQL DSN 时区解析回归测试
Refs #449
2026-05-10 17:29:11 +08:00
Syngnat
947bdbbe0c Merge pull request #451 from TonyJiangWJ/feature/sql-snippets
# Conflicts:
#	frontend/package.json.md5
2026-05-10 12:46:45 +08:00
Syngnat
c99287dc10 Merge pull request #450 from jsfaint/fix/pg_schema 2026-05-10 12:44:19 +08:00
Syngnat
49c20bef89 🐛 fix(data-grid): 修复快捷 WHERE 自动补全回车行为
- 调整快捷 WHERE 输入框 Enter 处理,避免抢占 AutoComplete 选中事件
- 方向键高亮建议项后回车优先选择字段
- 增加快捷 WHERE 回车行为回归测试
2026-05-10 12:41:08 +08:00
Syngnat
d26d7d2ff0 🐛 fix(data-grid): 修复数据输出列序与时间精度问题
- 统一复制、导出、JSON/Text 视图按表格展示列序输出
- 表级导出改用显式列查询,避免 SELECT * 丢失界面列序
- 保留 datetime(3) 等时间字段的小数秒展示与复制输出
Refs #434
2026-05-10 12:32:41 +08:00
TonyJiangWJ
30f3ac86aa feat(query-editor): 支持 SQL 片段配置 2026-05-10 08:59:25 +08:00
Jia Sui
741fba4c27 🐛 fix(postgres): 修复 LIKE 'pg_%' 误匹配 pgsrpschema 等非系统 schema
LIKE 模式中 '_' 是单字符通配符,'pg_%' 不仅匹配 pg_catalog/pg_toast,
还会匹配 pgsrpschema 等以 'pgs' 开头的 schema,导致这些 schema
下的表被 GetTables 漏掉,侧边栏不显示 schema 分组。
改用 LIKE 'pg|_%' ESCAPE '|','_' 仅匹配字面量下划线。
2026-05-09 19:11:18 +08:00
Syngnat
baed7a2721 🐛 fix(sidebar): 修复树节点左侧图标对齐
- 调整树节点内容区布局,固定展开符和图标宽度
- 保持树节点标题、展开符和图标左侧对齐稳定
- 补充侧边栏树横向滚动 CSS 回归测试
2026-05-09 16:08:49 +08:00
Syngnat
4ad074a90c 🐛 fix(window): 修复 Windows 最大化还原后文字变大
- 将缩放修正改为去抖检查,避免 focus/resize/visibilitychange 连续触发
- 最大化/还原改为显式切换窗口状态,减少重复 toggle 带来的抖动
- 补充 Windows 缩放修正相关工具测试
2026-05-09 16:08:31 +08:00
Syngnat
6a0f3f3a73 feat(sidebar): 支持当前表定位到左侧菜单
- 新增左侧工具栏定位按钮,支持按当前激活标签定位表/视图
- 抽离 sidebarLocate 工具函数,统一定位请求解析、路径匹配和 schema 分组
- 侧边栏接收定位事件后自动展开、选中并滚动到目标节点
- 移除 DataGrid 内部定位入口,补充定位与工具栏回归测试
2026-05-09 16:08:03 +08:00
Syngnat
ecdbe09c6c 🐛 fix(sidebar): 优化侧边栏拖拽热区并减少误触
- 将右侧边缘分隔条改为独立拖拽带
- 给树内容右侧预留缓冲区,避免拖宽时误点连接、库或表
- 拖拽期间锁定光标并禁用选中,提升拖动稳定性
- 保持原有宽度边界和拖拽反馈不变
2026-05-09 11:31:15 +08:00
Syngnat
8d8366c190 🐛 fix(query-editor): 修复 Oracle 星号查询定位列别名非法
- Oracle `SELECT *` 改写时使用合法源表别名 `gonavi_query_source`
- 让自动注入的 `ROWID` 绑定到源表别名,避免 `ORA-00911`
- 保留显式字段查询的 `ROWID` 追加逻辑
- 新增回归测试覆盖 `SELECT * FROM EDC_LOG` 的执行 SQL
- 校验生成 SQL 不再包含非法自动别名
2026-05-09 11:11:40 +08:00
Syngnat
faef619413 🐛 fix(mac-window): 修复查询替换框在 macOS 无法关闭
- 放行编辑器和输入控件内的 Escape 按键事件

- 保留 macOS 原生全屏下普通区域的 Escape 抑制逻辑

- 补充 Mac 窗口快捷键回归测试

Refs #433
2026-05-08 23:00:23 +08:00
Syngnat
0c2b112234 🐛 fix(duckdb): 修复 Windows 扩展下载平台不匹配问题
- 改用官方 duckdb.dll 动态库构建 Windows DuckDB driver-agent

- 安装、总包和本地导入流程同步携带运行时依赖

- 更新 DuckDB driver-agent revision 并补充安装链路测试

Refs #430
2026-05-08 22:50:03 +08:00
Syngnat
ff0661d285 🐛 fix(sqlserver): 修复新建数据库语法兼容问题
Refs #438

- SQL Server 创建数据库改用方言标识符

- 补齐 mssql/sql_server 别名归一

- 增加回归测试
2026-05-08 21:41:01 +08:00
Syngnat
5052c7fa6f 🐛 fix(doris): 修复数据库重命名与字段变更预览
Refs #439
- Doris 重命名数据库改走原生 ALTER DATABASE RENAME
- Doris 字段/注释预览改为兼容语法,移除 AFTER/FIRST 和无效 NONE
- 补充相关回归测试
2026-05-08 21:24:47 +08:00
Syngnat
ab420e3d24 🐛 fix(driver-manager): 统一驱动管理页明暗主题底色
Refs #440
2026-05-08 20:28:41 +08:00
Syngnat
1616ba8ae4 🐛 fix(DataGrid): 修复聚合查询结果无法复制的问题
- 为查询结果页新增独立复制入口
- 支持 CSV、JSON、Markdown 复制当前结果集
- 补充聚合列复制与按钮可点击回归测试
2026-05-06 21:47:16 +08:00
Syngnat
da9a76715a 🐛 fix(driver): 修复驱动代理校验与 DuckDB 表预览超时
- 校验可选 driver-agent revision,避免重装后复用旧代理
- DuckDB 表预览默认不再追加兜底 ORDER BY
- 优化 DuckDB 超时中断提示并补充回归测试
2026-05-06 19:32:55 +08:00
Syngnat
3c68325132 🐛 fix(oceanbase): 修复 Oracle 协议保存与连接链路
- 测试连接统一走 RPC 配置构造,确保 OceanBase Oracle 协议生效

- 保存连接时同步写入 oceanBaseProtocol 与 protocol 参数

- 编辑回显支持从显式字段、连接参数和 URI 恢复协议

- 双击连接时清理旧树缓存,避免复用 MySQL 协议子节点

- 补充 OceanBase 协议解析与缓存 key 隔离测试
2026-04-30 17:27:17 +08:00
Syngnat
5f9adcac37 🐛 fix(ai): 兼容 DeepSeek reasoning 内容响应
- 增加 reasoning_content 字段解析与前后端类型定义

- 兼容 DeepSeek 流式和非流式响应中的推理内容

- 统一 AI 消息 payload 映射,避免历史消息丢失推理内容

- 补充 OpenAI 兼容 Provider 与前端消息映射测试
2026-04-30 17:26:36 +08:00
Syngnat
d2dad75167 ♻️ refactor(oceanbase): 完善双协议连接链路
- 抽象 OceanBase 协议解析与运行态参数注入
- 复用 OracleDB 实现 OceanBase Oracle 租户连接能力
- 调整 DDL、schema、SQL 方言和数据源能力判断
- 补充协议优先级、缓存隔离和 RPC 参数测试
- 支持按指定 driver 自动生成 agent revision
2026-04-30 15:05:05 +08:00
Syngnat
98c62fd6bd 🎨 style(driver): 重做驱动管理页面布局与交互
- 页面结构:将驱动表格改为卡片列表,移除横向滚动依赖
- 信息展示:新增顶部状态统计,清晰区分全部、已启用、需重装、未启用
- 重装提示:将长原因文案收敛为摘要展示,并支持展开查看完整原因
- 操作优化:集中展示版本、进度、安装、重装、移除、本地导入和日志入口
- 响应式适配:窄屏下驱动卡片自动堆叠,避免内容挤压
2026-04-30 13:35:07 +08:00
Syngnat
7fd6d78c83 feat(driver): 新增 OceanBase 与 OpenGauss Agent 数据源
- 数据源支持:新增 OceanBase 与 OpenGauss optional driver-agent 实现
- 连接适配:复用 MySQL/PostgreSQL 兼容链路并补齐查询、DDL、同步能力
- 前端入口:补充连接表单、侧边栏、图标、SQL 方言和危险操作识别
- 驱动管理:更新 driver manifest、安装提示和 revision 自动生成链路
- 构建发布:支持多平台 driver-agent 打包并优化 release 构建失败提示
2026-04-30 13:13:01 +08:00
Syngnat
c92959f3e8 feat(connection): 支持多数据源额外连接参数配置
- 前端连接表单新增额外连接参数入口,支持 URI query 格式录入与解析回填
- MySQL 兼容驱动支持 JDBC 常见参数映射,修复 UTF-8 字符集与 serverTimezone 兼容问题
- 扩展 Oracle、PostgreSQL 兼容、SQL Server、ClickHouse、MongoDB、达梦、TDengine 参数合并
- 按不同驱动通道处理 DSN、URI、Options 与 Settings,避免统一透传导致连接异常
- 修复编辑已保存连接时解析无认证 URI 会清空已有账号密码的问题
- 补充连接参数透传、缓存隔离、DSN 合并与 URI 回填回归测试
2026-04-30 10:57:52 +08:00
Syngnat
c65e429072 🐛 fix(oracle): 兼容旧版本自动限行语法
- Oracle/Dameng 自动限行改为 ROWNUM 外层包裹
- 避免旧版本 Oracle 不支持 FETCH FIRST 导致 ORA-00933
- 保留尾部分号与注释,避免执行语句结构丢失
- 跳过 FOR UPDATE 语句自动包裹,避免改变锁语义
- 补充 Oracle/Dameng 自动限行回归测试
Refs #429
2026-04-30 08:33:24 +08:00
Syngnat
c1ebce4ef5 feat(query-editor): 放宽单表查询结果列级编辑边界
- 查询编辑:支持简单表列与表达式列混合展示
- 编辑安全:仅允许真实表列编辑,表达式列保持只读
- 提交流程:支持结果列别名映射回真实表字段
- 测试覆盖:补充聚合查询静默只读与列级提交用例
2026-04-29 20:07:22 +08:00
Syngnat
c927e33c8c feat(driver): 提醒重装旧版驱动代理
- optional-driver-agent 新增 metadata 方法返回 driverType、agentRevision 与协议版本
- 安装和本地导入驱动后记录 agentRevision,并在驱动状态中比对是否需要更新
- 驱动管理、连接表单和已有连接加载入口提示重装旧版 agent
- 补充旧 revision 检测和 custom 连接使用统计回归测试
2026-04-29 17:26:16 +08:00
Syngnat
824aafbdea 🔧 chore(driver): 自动生成驱动代理 revision
- 新增脚本按 optional driver-agent 源码依赖生成 revision 指纹
- 构建脚本与 dev/release workflow 在打包前自动刷新 revision
- 生成驱动 revision 映射并补充 optional driver 覆盖校验
2026-04-29 17:26:09 +08:00
Syngnat
0c1586d7a4 🐛 fix(clickhouse): 修复协议选择与连接错误提示
- 支持 ClickHouse 手动 HTTP/Native 协议优先级,避免 URI scheme 覆盖用户选择
- Auto 模式识别 Native/HTTP 协议误配错误并自动尝试备用协议
- 净化连接失败中的二进制乱码,补充测试连接参数校验和排查日志
- 前端表单增加 ClickHouse 协议选择并同步类型、缓存 key 与持久化兼容
Refs #425
2026-04-29 17:25:54 +08:00
Syngnat
b1ef52f62e feat(data-grid): 支持无主键表安全编辑
- 定位策略:新增主键、唯一索引和 Oracle ROWID 三类安全行定位能力
- 查询编辑器:简单单表 SELECT 自动补充隐藏定位列,复杂结果保持只读
- 表预览:无主键表可通过唯一索引或 Oracle ROWID 安全编辑
- 提交流程:移除无主键整行 WHERE fallback,隐藏定位列不参与展示和写入
- 后端保护:Oracle、MySQL、PostgreSQL 更新删除必须恰好影响 1 行
- 测试覆盖:补充 QueryEditor、DataViewer、DataGrid 和 ApplyChanges 相关用例
Refs #419
2026-04-29 12:33:35 +08:00
Syngnat
05a913ccb2 🐛 fix(query-editor): 修复多数据源大查询限流失效
- SQL限流:抽取查询自动限流工具,修复 SELECT 判断大小写不一致导致限制未生效
- 方言适配:按 Oracle/Dameng、SQL Server、MySQL/PostgreSQL 等方言分别注入行数限制
- 自定义驱动:支持 custom 连接根据 driver 解析 Oracle、PostgreSQL、SQL Server 等方言
- MongoDB修复:修正 db.collection.find() 解析边界,并对 find/只读 aggregate 下推 limit
- Oracle优化:DSN 增加 PREFETCH_ROWS 和 LOB FETCH 参数,减少大结果集拉取开销
- 测试覆盖:补充 SQL 方言矩阵、MongoDB 限流和 Oracle DSN 参数测试
Refs #424
2026-04-29 10:29:34 +08:00
Syngnat
f51dbcfb2c 🐛 fix(oracle): 修复查询结果编辑提交后数据还原
Oracle GetColumns 未返回主键列标记,前端 pkColumns 为空后退化为
全列 WHERE 条件,Oracle 空字符串即 NULL 语义导致 UPDATE 匹配 0 行。

LEFT JOIN all_constraints + all_cons_columns 检测主键列并赋值 Key="PRI",
与达梦驱动实现方式一致。
2026-04-29 09:41:25 +08:00
Syngnat
5f7578c5ea feat(ai): 支持录制聊天发送快捷键
- 工具中心新增 AI 聊天发送快捷键,默认 Enter 并支持 Ctrl/Cmd/Alt+Enter
- AI 输入框按录制绑定发送,保留 Shift+Enter 换行和输入法 composing 保护
- 修复 shortcutOptions 启动刷新覆盖录制值的问题,并校验脏持久化快捷键
- 补充快捷键、输入框提示和持久化回归测试
- 撤回 macOS Caps Lock 浮层无效前端规避,恢复输入控件 no-auto-cap 属性
- 新增需求进度追踪文档记录验证结果
2026-04-28 18:12:42 +08:00
Syngnat
56eaca9081 🐛 fix(data-grid): 修复 schema 数据源 DDL 查看异常
- 表页入口:查看 DDL 不再依赖 dbName,支持金仓/PG 等 schema 数据源
- 标识符解析:新增 quote-safe qualified name 拆分,避免引号内点号被误拆
- DDL 兼容:PG、HighGo、VastBase 使用安全拆分处理 schema.table
- 自定义驱动:补齐 custom HighGo DDL 查询时的数据库上下文
- 测试覆盖:新增 schema 表、视图 fallback、dotted 标识符等回归用例
2026-04-28 14:57:52 +08:00
Syngnat
51675f9d05 🐛 fix(ai): 修复多方言执行与 DDL 降级
- SQL 执行:移除 AI 工具和代码块预览中硬编码的 LIMIT 50
- 方言适配:按连接类型和自定义驱动别名生成只读 SQL 预览限流语句
- Oracle 兼容:Oracle、自定义 Oracle 和达梦改用 ROWNUM 语法限制行数
- 权限降级:获取表 DDL 失败时自动降级为字段元数据摘要
- 上下文优化:手动添加表结构上下文时复用同一套 DDL 降级逻辑
- 测试覆盖:新增 AI SQL 限流和表结构降级单元测试
Refs #418
2026-04-28 14:03:48 +08:00
Syngnat
f5f87189df 🐛 fix(oracle): 修复查询结果编辑提交日期格式报错
- 参数处理:提交事务前加载 Oracle 表字段类型,用于识别 DATE 和 TIMESTAMP 字段
- 更新修复:UPDATE 的 SET 值和 WHERE 条件统一转换日期时间参数
- 场景覆盖:修复新建查询结果编辑后提交事务触发 ORA-01861 的问题
- 类型绑定:将 Oracle 日期时间字符串解析为 time.Time,避免依赖数据库会话日期格式
- 兼容处理:支持 RFC3339、带时区和常见本地日期时间格式
- 测试覆盖:新增 Oracle ApplyChanges recording driver 回归测试
Refs #419
2026-04-28 13:39:32 +08:00
Syngnat
ef634075ab 🐛 fix(external-sql): 修复外部 SQL 文件保存不写回源文件
- 保存逻辑:外部 SQL 文件标签页携带 filePath,保存时写回原始磁盘文件
- 后端接口:新增 WriteSQLFile 能力,支持覆盖已有 SQL 文件并保留原文件权限
- 状态隔离:外部文件保存失败时不创建 savedQuery,避免写入 localStorage 副本
- 兼容行为:非文件标签页继续沿用原有 savedQuery 快速保存逻辑
- 文案优化:将数据库下入口改为“外部 SQL 目录”,减少与单文件打开入口的歧义
- 测试覆盖:补充前端保存分支、后端写文件边界和外部 SQL 目录文案测试
Refs #422
2026-04-28 13:26:55 +08:00
Syngnat
a07eea7815 feat(data-grid): 新增表数据页 DDL 查看与当前页查找
- 表数据页新增查看 DDL 入口,支持直接打开只读 SQL 预览弹窗
- 当前页查找支持大小写不敏感高亮,仅作用于已加载数据和显示列
- 查找结果新增上一个、下一个导航,并自动聚焦选中匹配单元格
- DDL 请求增加上下文过期保护,避免切表后展示旧表结构
- 补充 DataGrid 布局、DDL 交互和查找工具函数单元测试
Refs #417
2026-04-28 12:39:51 +08:00
Syngnat
5886b1ded8 🔧 chore(frontend): 同步 package 校验文件 2026-04-28 10:36:07 +08:00
Syngnat
299a80dd5a 🐛 fix(frontend): 修复 macOS Caps Lock 输入浮层 2026-04-28 10:21:19 +08:00
Syngnat
225e9e61ed 🐛 fix(kingbase): 修复表操作标识符引用 2026-04-28 10:21:19 +08:00
Syngnat
fa4f2a938a 🐛 fix(jvm): 绑定前端变更执行到预览上下文
将 JVM 资源变更执行绑定到最近一次成功预览和连接配置指纹,并遮蔽敏感快照、payload 示例和 AI 上下文中的敏感值。
2026-04-28 09:42:48 +08:00
Syngnat
ec2eefc9d2 🐛 fix(jvm): 加固诊断命令策略与输出脱敏
在服务端阻断只读连接中的高风险和多行诊断命令,并对诊断事件与错误消息统一脱敏,避免凭证、Authorization 和 PEM 片段泄漏。
2026-04-28 09:42:41 +08:00
Syngnat
58ee269855 🐛 fix(jvm): 收紧 JMX domain allowlist 校验
在 helper runtime 中对直接 ObjectName、资源浏览、变更预览和监控路径统一执行 domain allowlist,阻断默认域别名和空白后缀绕过。
2026-04-28 09:42:29 +08:00
Syngnat
ffc4f2c2d9 🐛 fix(jvm): 强化变更确认令牌校验
将 JVM 变更确认从可重算校验值升级为服务端发放的一次性令牌,避免未预览、重放或上下文变更后继续执行高风险变更。
2026-04-28 09:42:21 +08:00
Syngnat
1b31c54917 🐛 fix(redis): 修复精确搜索无法命中命名空间
- 精确搜索识别无通配符的 Redis literal pattern
- 同时查询完整 Key 与同名命名空间前缀
- 修复输入 Agent 无法显示 Agent 文件夹的问题
- 避免误匹配 AgentCapacity、AgentState 等相似前缀
- 补充 glob literal 与命名空间搜索回归测试
- 更新 Redis 精确搜索输入提示文案
2026-04-27 11:31:20 +08:00
Syngnat
3665639300 🐛 fix(data-sync): 修复已保存连接同步时未恢复密文
- Data Sync 分析/预览/同步入口统一恢复源库和目标库连接密文
- 避免已保存 PostgreSQL 连接因空密码触发 28P01
- 保留前端选择的源/目标数据库覆盖值
- 增加保存连接密文恢复回归测试
Refs #413
2026-04-26 20:55:20 +08:00
Syngnat
3b9116e259 perf(table-overview): 优化大量表搜索渲染性能
- 预计算表概览搜索索引与排序键
- 使用 deferred value 降低搜索输入阻塞
- 限制大结果集首批渲染数量并支持继续加载
- 增加表概览过滤与渲染上限回归测试
2026-04-26 20:42:14 +08:00
Syngnat
a06f45da28 feat(redis): 新增 Key 精确搜索模式
- 增加 Redis Key 模糊/精确搜索切换
- 精确模式不再追加通配符并保留大小写敏感匹配
- 转义 Redis glob 特殊字符避免误匹配
- 补充搜索模式回归测试
2026-04-26 20:34:07 +08:00
Syngnat
21222cf9f4 🐛 fix(redis): 修复自动模式 JSON 大整数精度丢失
- 保留 Redis JSON 值中的大整数原始字面量
- 避免自动格式化时通过 JSON.stringify 改写超出安全范围的数字
- 补充自动模式大整数与字符串转义展示回归测试
Refs #400
2026-04-26 20:15:13 +08:00
Syngnat
30301cd637 feat(data-grid): 新增快速 WHERE 筛选输入与补全能力
- 新增表格筛选面板快速 WHERE 条件输入
- 支持字段、操作符和关键字自动补全
- 查询、分页统计与筛选导出合并快速 WHERE 条件
- 修复补全过程中的字段引号丢失和重复追加问题
Refs #354
2026-04-26 20:06:15 +08:00
Syngnat
55829bce86 🐛 fix(connection): 修复连接颜色重启丢失并同步标签页展示
- 恢复连接清洗流程中的图标类型与颜色字段
- 标签页增加连接色标识,便于区分多连接会话
- 抽取连接视觉解析并补充回归测试
Refs #334
2026-04-26 19:33:12 +08:00
Syngnat
2b340f3136 feat(data-grid): 增加复制行和粘贴行操作
- 表数据工具栏新增复制行、粘贴行按钮
- 支持将选中行复制为新增行草稿,提交前可继续检查编辑
- 抽离行复制粘贴数据构造逻辑并补充回归测试
Refs #332
2026-04-26 19:09:25 +08:00
Syngnat
9eb06f6f96 feat(data-grid): 增加复制行和粘贴行操作
- 表数据工具栏新增复制行、粘贴行按钮
- 支持将选中行复制为新增行草稿,提交前可继续检查编辑
- 抽离行复制粘贴数据构造逻辑并补充回归测试
Refs #332
2026-04-26 19:09:10 +08:00
Syngnat
01dd62f4e2 🐛 fix(table-designer): 去除 SQL 变更重复标记
- 移除 Monaco glyph margin 变更标记通道

- 仅保留 line decorations 左侧单一变更标记

- 补充防重复标记回归测试

Refs #324
2026-04-26 18:04:01 +08:00
Syngnat
09ecc841ab 🐛 fix(table-designer): 突出显示 SQL 变更行
- 识别新增、删除、重命名、属性修改等变更 SQL 行

- 使用 Monaco decorations 仅标记变更行,保留基础 SQL 语法高亮

- 补充变更行识别与装饰渲染回归测试

Refs #324
2026-04-26 17:53:22 +08:00
Syngnat
3a0c5201a0 feat(table-designer): 高亮显示 SQL 变更预览
- SQL 变更弹窗接入只读 SQL 高亮预览组件

- 注册明暗主题下的 SQL token 颜色

- 补充 SQL 预览高亮回归测试
2026-04-26 17:38:42 +08:00
Syngnat
5f6acc25da 🔧 chore(gitignore): 忽略 Playwright MCP 临时目录
- 将 .playwright-mcp/ 加入仓库忽略规则

- 避免本地浏览器工具临时文件进入提交状态
2026-04-26 17:24:45 +08:00
Syngnat
5bbeb7f373 feat(jvm/connection): 优化诊断工作台与连接配置体验
- JVM 诊断工作台改为会话优先布局,未建会话前隐藏命令输入

- 优化命令模板、实时输出、审计历史和能力检查卡片展示

- 连接配置表单引入按数据源分组的卡片化布局

- 补充连接配置布局和 JVM 诊断工作台回归测试
2026-04-26 17:18:10 +08:00
Syngnat
df4fcab90b 🐛 fix(sql): 适配多数据源 SQL 方言生成
- 表设计 DDL 按 Oracle/Dameng、SQL Server、PG-family、SQLite/DuckDB、ClickHouse/TDengine 分支生成

- 新增统一 SQL 方言工具,驱动字段类型候选和 SQL 自动补全

- 修复 Oracle/Dameng DATE/TIMESTAMP 删除条件字面量

- 补充多方言 DDL、补全和 Oracle 删除回归测试

Refs #402

Refs #409
2026-04-26 17:14:07 +08:00
Syngnat
f16e2f15c2 🐛 fix(jvm): 加固诊断与变更安全边界
- 诊断 SSE 支持空心跳事件,避免无输出时解码失败

- Arthas Tunnel 增加会话过期清理、配置漂移校验和取消兜底

- Provider 合约清理 Base URL 查询参数和片段,避免探测泄露敏感信息

- JVM 变更请求强制校验原因并规范化写入审计字段
2026-04-26 14:34:43 +08:00
Syngnat
38e71119a4 feat(jvm-diagnostic): 优化诊断控制台命令体验
- 诊断命令输入使用编辑器外观并支持 Arthas 命令补全

- 新增命令执行 pending 输出、前端终态兜底和历史刷新

- 会话、输出、历史记录统一展示中文语义状态

- 补充诊断控制台和补全展示测试
2026-04-26 14:34:23 +08:00
Syngnat
ff2b86819d feat(jvm-ui): 完善 JVM 工作台与监控入口
- 新增 JVM 持续监控仪表盘、图表、状态卡和详情面板

- 统一概览、资源浏览、审计页面的 JVM 工作台布局

- Sidebar 和 TabManager 支持监控入口、诊断入口兜底和上下文切换

- 补充前端状态模型、展示文案和组件回归测试
2026-04-26 14:34:02 +08:00
Syngnat
9d08b185d0 feat(jvm): 新增持续监控与采样链路
- 后端新增监控会话管理,支持启动、停止和历史查询

- JMX、Endpoint、Agent Provider 补齐监控快照采集能力

- JMX helper 增加内存、GC、线程、类加载采样并更新内嵌运行时

- 生成 Wails 监控接口绑定并补充后端回归测试
2026-04-26 14:33:41 +08:00
Syngnat
a43c84f968 🔧 chore(dev): 合并 JVM 缓存可视化编辑分支
- 合并 JVM 连接、资源治理、诊断控制台与 Arthas Tunnel 能力
- 合并测试版号统一与 macOS 无交互 ZIP 打包调整
- 基于最新 origin/dev 完成合并并通过前后端最小验证
2026-04-24 16:52:03 +08:00
Syngnat
14c6510835 🔧 fix(release/version): 对齐测试版号并移除Mac交互打包
- build-release 优先读取 GONAVI_VERSION 与 version/dev-version.txt
- 新增共享测试版号文件,统一开发态与发布脚本版本来源
- internal/app 版本解析增加 dev-version 回退与回归测试
- macOS 发布改为 ZIP 归档,不再触发 create-dmg 与 Finder 排版
- 补充发布脚本调整的需求追踪文档
2026-04-24 16:48:09 +08:00
Syngnat
6f14e827ab feat(jvm): 完成资源治理与诊断增强
- 新增 JMX/Endpoint/Agent 三种 JVM 连接模式与配置归一化链路
- 支持资源浏览、变更预览、写入应用、审计记录与只读约束
- 接入 AI 结构化写入计划与诊断计划回填能力
- 新增 Agent Bridge、Arthas Tunnel、JMX Helper 诊断传输实现
- 增加诊断控制台、命令模板、输出历史与自动补全交互
- 补齐前后端契约、运行夹具与 JVM 相关回归测试
2026-04-24 16:45:34 +08:00
Syngnat
d9b4c6a21b 🐛 fix(jvm): 固定 AI 重试链路的 JVM 上下文
- 为 JVM AI 回复的重新生成流程继承原始页签上下文并透传到新消息
- 让重试、催促重发和工具回合续跑都按原 JVM 上下文构建 system prompt
- 避免切换页签后重试 JVM 计划时出现上下文错位或定向能力丢失
- 重新通过前端全量测试、前端构建与 wails 生产构建验证
2026-04-23 13:40:29 +08:00
Syngnat
d2c3e3e779 🐛 fix(jvm): 修正 AI 计划映射与页签定向应用
- 为 JVM AI 计划补充显式草稿映射,避免 payload 包装层直接透传到现有变更契约
- 将 updateValue 映射为当前 JVM 写入链路的 put,并限制为 JSON 对象 payload
- 为 AI 聊天消息绑定 JVM 来源上下文,按 tab/connection/provider/resource 定向应用计划
- 补充 JVM AI 计划解析、契约映射和目标页签解析单测
- 更新需求追踪并回填 go test、前端测试、构建与 wails build 验证结果
2026-04-23 13:02:04 +08:00
Syngnat
3cb2d494cc feat(jvm): 接入 AI 结构化变更计划
- 新增 JVM AI 计划解析器与 fenced json 契约测试
- 为 JVM 资源页注入 AI 计划生成 prompt 并支持回填草稿
- 在 AI 对话上下文中补充 JVM 资源约束与应用入口
2026-04-23 12:42:02 +08:00
Syngnat
9a61622568 feat(jvm): 增加 JVM 写入预览与审计
- 打通 JVM 变更预览、执行确认与审计记录链路
- 增加 Guard 校验、模式约束与审计写入失败回传
- 补齐审计页签、预览弹窗和 Task 5 回归覆盖
2026-04-23 12:14:36 +08:00
Syngnat
21f2b29d1d feat(jvm): 打通 JVM 只读资源浏览链路
- 后端新增 JVMListResources 与 JVMGetValue 接口并补齐回归测试
- Sidebar 基于能力探测展示 JVM 模式节点并懒加载资源节点
- TabManager 接入 JVMOverview、JVMResourceBrowser 与模式徽标展示
- 补齐 JVM Tab 元数据与连接持久化 sanitize 逻辑
- 更新需求追踪文档并记录 Task 4 验证结果与残余风险
2026-04-23 11:21:36 +08:00
Syngnat
7ddb49a81d 🐛 fix(jvm): 修正连接表单模式回填与超时同步
- 保留编辑态 JVM 连接的原始 preferredMode,避免旧配置被静默降级
- 将 JVM 可见超时统一同步到 Endpoint 探测配置
- 抽取 JVM 可编辑模式判定与回填逻辑,统一 ConnectionModal 行为
- 补充 JVM 模式与超时纯函数测试,覆盖 unsupported preferredMode 分支
- 更新需求追踪文档,记录 Task 3 实现、复审与验证结果
2026-04-23 10:20:47 +08:00
Syngnat
9bb7ece2dd 🐛 fix(frontend):收敛JVM模式选项与标题文案 2026-04-23 09:42:37 +08:00
Syngnat
177dafacc9 feat(frontend):接入JVM连接表单与展示元数据 2026-04-23 09:23:28 +08:00
Syngnat
03a1506686 feat(jvm): 增加连接测试与能力探测 API
- 新增 JVM provider 工厂与 JMX、Endpoint 骨架实现
- 暴露 TestJVMConnection 和 JVMProbeCapabilities 并统一 QueryResult 返回
- 刷新 Wails 绑定与 JVM 连接模型,补齐前后端方法签名
- 补充 App 编排测试与 provider 契约测试,避免假成功和静默成功
- 更新需求追踪,记录 Task 2 审查结论与验证证据
2026-04-22 17:52:28 +08:00
Syngnat
15b1ad24d1 feat(jvm): 落地 JVM 连接契约与配置归一化
- 新增 JVM 连接配置与共享 DTO,补齐 JMX 和 Endpoint 契约
- 实现后端归一化规则,支持默认只读、模式回退和 JMX 端口兜底
- 新增前端 JVM 默认值与配置构建工具,统一模式环境和端口收敛
- 补充 Go 与 Vitest 用例并更新需求追踪,记录 Task 1 验证证据
2026-04-22 17:20:00 +08:00
Syngnat
f584270209 📝 docs(jvm): 沉淀 JVM Connector MVP 实施计划
- 按 Task 拆分连接契约、Provider、前端工作台与 AI 集成实现路径
- 明确前后端文件边界、TDD 顺序、Wails 绑定刷新与回归命令
- 补齐共享 DTO、provider factory 和审计落盘等关键实现细节
- 同步需求追踪进入实施计划阶段
2026-04-22 16:50:40 +08:00
Syngnat
fe9d02734f 📝 docs(jvm): 沉淀 JVM 缓存可视化编辑设计
- 新增 JVM Connector 的统一入口、多 Provider 与能力协商方案
- 明确 JMX 与 Management Endpoint 为 MVP,Agent 仅保留扩展位
- 定义资源模型、AI 协同、Guard Layer、审计与分期边界
- 同步需求追踪中的范围、风险、决策与验证记录
2026-04-22 16:50:40 +08:00
Syngnat
65a9f4352e feat(sql-files): 支持外部 SQL 目录树与双击打开
- 新增 SQL 目录选择、枚举与按路径读取接口,复用大文件执行能力
- Sidebar 增加外部 SQL 文件目录树、目录管理入口与双击打开查询标签
- 补充 external SQL 持久化与前后端回归测试

Fixes #319
2026-04-17 21:02:48 +08:00
Syngnat
f3b78f9763 🐛 fix(driver): 明确JDBC Jar导入限制并补充Kingbase指引
- 后端在驱动包选择与本地导入前拦截 JDBC Jar,并返回替代说明
- 驱动管理统一改为“导入驱动包”,补充不支持 JDBC Jar 的提示
- 自定义连接补充 kingbase8 等驱动别名与 Go 驱动说明
- 新增后端与前端回归测试

Refs #317
2026-04-17 20:41:58 +08:00
Syngnat
0bccdeed8c feat(ui): 优化侧边栏设置中心与数据表交互
- 收敛左上角入口为工具和设置中心,并调整新建连接操作优先级
- 优化表设计器 SQL 预览高亮和刷新前未保存字段变更确认
- 下移数据页次级操作并将编辑行收口到单元格右键菜单
- 补充侧边栏布局、表设计器草稿检测和数据页布局回归测试

Refs #324
2026-04-17 20:09:46 +08:00
Syngnat
39f6fbbe1f 🐛 fix(export): 修正带注释的 JOIN 查询结果导出校验
- 导出前缀判断增加前置 SQL 注释清理,避免合法 SELECT 被误判
- ExportQuery 统一复用 looksLikeSelectOrWith 逻辑,消除重复校验分支
- 补充带前置注释的 INNER JOIN 导出回归测试

Fixes #391
2026-04-17 19:01:39 +08:00
Syngnat
8a1a9a8fb8 🐛 fix(mongodb): 支持 Mongo shell 快捷查询命令
- 为 show dbs 和 show databases 转换 listDatabases JSON 命令
- 为 show collections 和 show tables 转换 listCollections JSON 命令
- 补充 Mongo shell 快捷命令回归测试并验证前端构建

Fixes #390
2026-04-17 18:56:01 +08:00
Syngnat
dca5f629b2 🐛 fix(dameng): 修正表格更新无法识别主键列
- 达梦列元数据查询补充主键关联并返回 column_key
- GetColumns 正确映射主键标记,避免表格更新退化为整行 WHERE
- 补充达梦列元数据回归测试,并验证带驱动 tag 的实现编译通过

Fixes #389
2026-04-17 18:42:47 +08:00
Syngnat
8eae39c2c2 🐛 fix(redis-viewer): 修正 Redis 值自动与 UTF-8 展示不一致
- 新增 redisValueDisplay 工具,统一自动、UTF-8 与十六进制模式的展示判断
- 修正已解码 Unicode 文本被重复按字节解码导致的乱码问题
- 补充 Redis 值展示回归测试,并让各数据类型复用同一套展示逻辑

Fixes #386
2026-04-17 18:31:04 +08:00
Syngnat
9613b2a8eb 🐛 fix(window): 修正启动窗口恢复到不可见区域
- 启动恢复普通窗口时先校验持久化 bounds 是否仍与可视区域相交
- 完全掉出可视区域时自动回正并回写新的窗口位置到 store
- 补充窗口恢复 helper 回归测试并验证前端构建通过

Fixes #384
2026-04-17 18:19:42 +08:00
Syngnat
4fd679ce42 🐛 fix(sqlserver): 修正 uniqueidentifier 展示为十六进制字节
- 查询值规整新增 uniqueidentifier 识别并复用 go-mssqldb GUID 格式化
- 避免 SQL Server 查询结果把 GUID 展示为原始 0x 字节串
- 补充 uniqueidentifier 原始字节回归测试并覆盖驱动返回值路径

Fixes #381
2026-04-17 18:10:51 +08:00
Syngnat
e56a72eb9f 🐛 fix(redis): 修正 hash 详情读取依赖 HGETALL
- 为 hash 读取增加 HGETALL 权限受限时的 HSCAN 降级路径
- RedisGetValue 与 GetHash 统一复用 fallback 并保留长度元数据
- 补充普通用户权限受限与非权限错误回归测试

Fixes #380
2026-04-17 18:07:50 +08:00
Syngnat
0fda09a19f 🔧 chore(dev): 合并 open issue backlog 修复分支
- 合并已按 issue 拆分提交的 backlog 修复与 SQL 结果集同步能力
- 解决 DataGrid、Sidebar 以及 legacy WebKit 存储迁移测试的合并冲突
- 保留 dev 分支当前结构并移除已废弃的 issue backlog 跟踪文档
2026-04-17 17:52:14 +08:00
Syngnat
33b78fb583 🐛 fix(sync): 同步 SQL 结果集同步前端模型绑定
- 为数据同步请求模型补齐 sourceQuery 字段生成声明
- 使前端生成绑定与 SQL 结果集同步后端参数保持一致
- 补齐 issue #321 功能收尾所需的 Wails 模型产物

Fixes #321
2026-04-17 17:45:10 +08:00
Syngnat
40416fb4df 🐛 fix(redis): 同步 hash 字段删除接口前端绑定
- 同步 Wails 前端声明中的 RedisDeleteHashField 参数类型
- 使生成绑定与后端兼容字符串和数组入参的实现保持一致
- 补齐 issue #343 修复后的前端接口声明

Fixes #343
2026-04-17 17:45:05 +08:00
Syngnat
651eec1617 feat(sync): 新增 SQL 结果集数据同步能力
- 同步引擎新增查询结果集同步分支,支持单目标表差异分析、预览与执行
- 数据同步工作台增加 SQL 结果集模式,并补充目标表与查询校验
- 补充后端同步链路与前端请求构造回归测试,并更新 backlog 记录

Fixes #321
2026-04-17 16:31:55 +08:00
Syngnat
9dc58acb39 🐛 fix(table-designer): 修正 MySQL 列改名预览 SQL
- 将 MySQL 列改名从 MODIFY COLUMN 切换为 CHANGE COLUMN 语法
- 保留类型变更与列位置子句的既有生成逻辑
- 补充回归测试并更新 issue backlog 记录

Fixes #373
2026-04-17 15:06:09 +08:00
Syngnat
f3193f0933 🐛 fix(ai): 修正 SQL 代码块 Markdown 换行渲染
- 为 AI markdown 渲染补充 fenced code block 预处理
- 修正 opening/closing fence 缺少换行时的代码块解析失败
- 补充回归测试并更新 issue backlog 记录

Fixes #369
2026-04-17 14:37:36 +08:00
Syngnat
7cb46f9f69 🐛 fix(window): 修正最大化窗口恢复焦点后重复动画
- 收敛 Windows 最大化窗口的激活修复逻辑,避免返回前台时重复 toggle
- 标题栏按钮按窗口状态切换 maximize/restore 图标并立即同步 store
- 补充窗口状态规则测试并更新 issue backlog 记录

Fixes #368
2026-04-17 14:18:38 +08:00
Syngnat
04c4613e4d 🐛 fix(datagrid): 修正日期字段设置值被误存为 NULL
- 抽取时间字段保存 helper 并统一 picker 类型与格式化逻辑
- 单元格保存优先使用 picker 实时值,避免 Form 同步滞后把日期误判为空
- 补充前端回归测试并更新 issue backlog 记录

Fixes #363
2026-04-17 13:46:38 +08:00
Syngnat
8a10519f9b 🐛 fix(query): 修正新建查询未引用 PostgreSQL 大写表名
- 抽取表查询模板 helper 并统一复用方言标识符引用逻辑
- 修正 Sidebar 与 TableOverview 的表节点新建查询入口
- 补充前端回归测试并更新 issue backlog 记录

Fixes #349
2026-04-17 13:30:07 +08:00
Syngnat
d57081ecfb 🐛 fix(query): 修正查询结果同名列被覆盖问题
- 为查询结果扫描增加稳定列名归一化,重复列自动追加序号后缀
- 统一返回字段列表与行数据键名,避免同名列值被后写覆盖
- 补充 scanRows 回归测试并更新 issue backlog 记录

Fixes #348
2026-04-17 13:24:50 +08:00
Syngnat
035f536e8d 🐛 fix(tdengine): 补齐超级表元数据查询
- 表列表合并 SHOW TABLES 与 SHOW STABLES 结果
- 返回前统一去重并排序,确保超级表可见
- 增加 TDEngine 表列表回归测试

Fixes #346
2026-04-17 13:14:08 +08:00
Syngnat
22e4299d3e 🐛 fix(redis): 修正 hash 字段删除参数序列化错误
- 前端统一按数组传递 hash 字段删除参数
- 后端兼容单字符串与数组两种删除入参
- 补充 Redis hash 字段删除回归测试

Fixes #343
2026-04-17 12:45:21 +08:00
Syngnat
384aea132c 🐛 fix(sync): 修正仅同步结构未生效
- 让已存在目标表场景复用通用补字段逻辑生成结构变更 SQL
- 为分析与预览结果补充结构差异计数与结构 SQL 明细
- 补充结构同步回归测试并更新 backlog 记录

Fixes #342
2026-04-17 12:35:23 +08:00
Syngnat
890478eb7b 🐛 fix(clickhouse): 修正 8132 端口连接失败
- 将 8132 纳入 ClickHouse HTTP 端口识别范围
- 同步修正协议切换日志与错误提示中的端口说明
- 补充连接协议识别回归测试并更新 backlog 记录

Fixes #338
2026-04-17 12:27:20 +08:00
Syngnat
8c79f2af0c 🐛 fix(update): 修正 Linux 变体自动更新失效
- 更新资产选择逻辑按当前 Linux 可执行文件变体匹配 release 包
- Linux 更新脚本优先查找与当前二进制同名的新文件
- 补充自动更新回归测试并更新 backlog 记录

Fixes #337
2026-04-17 12:17:11 +08:00
Syngnat
a2cad9f7ce 🐛 fix(ai): 修正 Anthropic 兼容供应商问答失败
- 为 AnthropicProvider.Chat 与 ChatStream 补充工具调用降级回退
- 首次携带 tools 请求在 400/422/404 时自动去掉 tools 重试一次
- 补充兼容供应商问答回归测试并更新 backlog 记录

Fixes #333
2026-04-17 12:02:23 +08:00
Syngnat
af90936fcc 🐛 fix(frontend): 修复 Redis 搜索匹配与输入交互体验
- Redis Key 搜索默认补全包含匹配并支持 ASCII 大小写不敏感
- Redis 标签页增加连接名与 host 摘要,区分同名 db 标签
- 抽取 inputAutoCap、redisSearchPattern、tabDisplay 共享工具并补充回归测试
- 覆盖连接配置、Redis 搜索、表设计、表概览和数据表筛选输入的自动纠正问题
- 在 macOS 文本输入面板关闭局部毛玻璃,修复输入法切换出现透明框
2026-04-16 18:07:38 +08:00
Syngnat
d3a1c017da 🐛 fix(driver): 修复可选驱动在线安装回归问题
Refs #388

- 修复 builtin 默认安装版本判定错误
- 恢复驱动总包 bundle 兜底路径
- 优化 Kingbase 安装策略,避免发行版优先本地构建
- 增强驱动安装日志与回归测试
2026-04-16 15:05:16 +08:00
Syngnat
a90423c04c Merge pull request #385 from Jonclex/dev 2026-04-15 15:17:46 +08:00
Jonclex
6e23053ac6 Merge branch 'Syngnat:dev' into dev 2026-04-15 14:47:18 +08:00
jonclex
9b50e9c9c8 fix(custom+mysql):CustomDB(driver=mysql)路径没有同步修改,custom链接打开时schema报错,双击表查询报错 refs bug#385 2026-04-15 14:42:45 +08:00
Syngnat
4c76202d2c Merge pull request #382 from anyanfei/feature/add_import_xml_dev 2026-04-15 13:50:49 +08:00
anyanfei
9c5b1a033a fix(import connect):统一测试用例文案;仅判断mysql-workbench-xml 2026-04-15 13:40:17 +08:00
jonclex
c631feef91 fix(ui): 表概览排除视图 refs bug#375 2026-04-15 10:27:22 +08:00
jonclex
737896627a fix(mysql): 表列表排除视图 refs bug#375 2026-04-15 10:06:44 +08:00
anyanfei
47235e1390 fix(import connect):修改导入时提示,而不是在连接时提示 2026-04-15 09:53:29 +08:00
anyanfei
b6121fe1f8 - 背景与问题 :以前没有支持官方工具mysqlworkbench的xml导入,现在支持了
- 变更点:新增mysqlworkbench的xml文件导入,并当没有密码时,提示用户,而不是直接使用空密码进行直接连接,更友好
  - 影响范围:仅导入受到影响
  - 验证方式:点击导入,用mysqlworkbench的xml进行导入即可
2026-04-14 18:50:40 +08:00
Syngnat
f78b132c7c 修改mysql编辑视图的时候保存失败。 (#377) 2026-04-13 17:13:08 +08:00
Jonclex
1adef17366 Merge branch 'Syngnat:dev' into dev 2026-04-13 15:49:53 +08:00
jonclex
ada9bbf03e fix(mysql): 修复视图编辑时的DDL头部兼容 2026-04-13 15:39:08 +08:00
Syngnat
266f217bfd 合并拉取请求 #371
fix: Oracle/DM数据库侧边栏视图不显示和默认模式显示问题
2026-04-13 12:51:42 +08:00
Jonclex
54d46453df Merge branch 'Syngnat:dev' into dev 2026-04-13 12:41:53 +08:00
Syngnat
c7cf9526de 🐛 fix(security): 修复 macOS 无法打开应用及三平台依赖系统钥匙串的问题
- 密文存储:新增 dailysecret 本地存储引擎,连接/代理/AI 密钥不再依赖系统钥匙串
- 启动迁移:自动将已有钥匙串密文迁移到本地 JSON,用户无感知
- WebKit 迁移:从旧版 Wails WebKit LocalStorage 中恢复连接与代理数据
- DMG 修复:移除 --sandbox-safe 避免扩展属性污染签名,新增 xattr 清理与签名校验
- 安全适配:钥匙串不可用时标记完成而非回滚,消除无钥匙串环境下的阻塞
- 出口脱敏:所有连接/代理 API 返回前统一 sanitize 防止密文泄漏
2026-04-13 12:40:25 +08:00
jonclex
d849cd49af fix: Oracle/DM数据库侧边栏不显示'默认模式'节点 2026-04-13 12:39:14 +08:00
Syngnat
604aaad69d 合并拉取请求 #366
fix(sidebar): normalize mysql view names (#365)
2026-04-13 10:35:00 +08:00
jonclex
605e266eab fix(sidebar): normalize mysql view names (#365) 2026-04-13 10:25:03 +08:00
辣条
2569a3779a feat(connection-package): 支持连接恢复包双模式加密导入导出 (#361)
## 变更说明

  - 为连接恢复包新增 v2 双模式加密导入导出
  - 默认使用应用内置密钥加密 secrets 字段,无需用户输入文件密码
  - 可选增加文件保护密码,形成双层加密
  - 保留 v1 导入兼容,并兼容 legacy JSON 明文导入
  - 前端新增 v2 格式识别与导出弹窗选项适配
  - 合并过程中已处理与最新 dev 的冲突,确保现有安全更新链路不被破坏

  ## 回归验证

  - `go test -count=1 ./...`
  - `npm test`
  - `npm run build`
- `wails build -platform windows/amd64 -clean -o
GoNavi-windows-amd64-test -ldflags "-s -w -X
GoNavi-Wails/internal/app.AppVersion=dev-d150780-merge-test"`

  ## 人工验证

  - 明文配置导入通过
  - v2 无文件密码导出后可直接导入
  - v2 带文件密码导出后需密码导入
  - 导入旧版恢复包与 legacy JSON 均正常

  ## 备注

  - 本 PR 基于最新 `dev` 进行冲突整合
2026-04-12 12:48:47 +08:00
Syngnat
bb6271246b 🐛 fix(mac): 禁用正式包默认窗口诊断以规避启动无窗体问题
- 将 macOS 原生窗口诊断改为默认关闭
- 仅在显式设置 GONAVI_ENABLE_MAC_WINDOW_DIAGNOSTICS 时启用后端诊断
- 仅在前端开发环境启用窗口诊断采集
- 避免正式构建在启动阶段附加额外窗口状态探测与日志观察
- 为诊断开关补充前后端最小回归测试

Refs: #360
2026-04-12 12:46:15 +08:00
Syngnat
8e0d1b0a80 📝 docs(contributing): 修正 dev 分支贡献流程说明
- 修正文档中的默认分支与集成分支描述
- 调整贡献分支创建基线为 dev
- 调整外部 Pull Request 目标分支为 dev
- 同步 README 中英文贡献说明
- 更新 release 后 main 回流 dev 的维护说明

Refs: #352
2026-04-12 12:34:50 +08:00
tianqijiuyun-latiao
d150780879 Merge branch 'feature/20260408_security-update' into merge/feature-20260408-security-update-onto-dev
# Conflicts:
#	frontend/src/App.tsx
#	frontend/wailsjs/go/app/App.d.ts
#	frontend/wailsjs/go/app/App.js
2026-04-12 09:40:28 +08:00
tianqijiuyun-latiao
52d2ee7592 feat(connection-package): 支持连接恢复包双模式加密导入导出
- 新增 v2 连接恢复包 appKey 与文件密码双模式加密链路
- 扩展前后端导入导出流程并兼容 v1 与 legacy 格式
- 修复无文件密码恢复包导入误弹密码框导致的流程阻塞
2026-04-11 23:51:43 +08:00
Syngnat
2410aad849 feat(table): 支持截断表与清空表操作
Fixes #351
2026-04-11 22:53:04 +08:00
Syngnat
33b21cc5ee 🐛 fix(driver): 兼容跨平台 Go 路径回退测试 2026-04-11 22:36:21 +08:00
Syngnat
1a0ba9a499 🐛 fix(sidebar): 避免默认显示横向滚动条
Fixes #329
2026-04-11 22:27:26 +08:00
Syngnat
7a2563b83b feat(data-grid): 支持拖选单元格直接复制到剪贴板
Fixes #322
2026-04-11 22:10:48 +08:00
Syngnat
632e57ea60 feat(data-grid): 支持双击列边界自适应宽度
Fixes #330
2026-04-11 22:05:53 +08:00
Syngnat
ca76440981 🐛 fix(connection): 收紧稳定期数据库连接自动重试
Fixes #331
2026-04-11 21:58:16 +08:00
Syngnat
af5e84213f 🐛 fix(driver): 扩展 TDengine 历史版本选择范围
Fixes #325
2026-04-11 21:53:53 +08:00
Syngnat
fcade0f860 feat(sidebar): 支持窄侧栏横向滚动查看
Fixes #329
2026-04-11 21:53:52 +08:00
Syngnat
1c2377bc62 🐛 fix(driver): 修复达梦驱动安装误走无效直链
Fixes #320
2026-04-11 21:53:52 +08:00
Syngnat
426ef3bcf6 🐛 fix(update): 修复 Windows 更新脚本安装失败
Fixes #328
2026-04-11 21:53:52 +08:00
Syngnat
fb500ee33b 🐛 fix(mysql): 回退当前数据库列表查询
Fixes #327
2026-04-11 21:53:52 +08:00
Syngnat
89d79ff10c 🐛 fix(mysql): 修复 bit 列写入归一化
Fixes #318
2026-04-11 21:53:52 +08:00
Syngnat
aa1bb5b886 🐛 fix(kingbase): 回退当前数据库元数据查询
Fixes #316
2026-04-11 21:53:52 +08:00
Syngnat
5038ae5c9b 🐛 fix(window): 修复 Windows 恢复焦点后界面缩放异常
Fixes #315
2026-04-11 21:53:52 +08:00
Syngnat
83fe3d4ed9 🐛 fix(driver): 提升批量 INSERT 执行效率
Fixes #311
2026-04-11 21:53:51 +08:00
Syngnat
808c773134 feat(table-overview): 优化库内表概览为逐行展示
Fixes #310
2026-04-11 21:53:51 +08:00
Syngnat
5d86ee7c76 🐛 fix(clickhouse): 获取数据库列表失败时回退当前库
Fixes #308
2026-04-11 21:53:51 +08:00
Syngnat
8297829be6 feat(driver): 增加驱动目录直达入口与手动导入提示
Fixes #306
2026-04-11 21:53:51 +08:00
Syngnat
f696f52470 🐛 fix(table-designer): 修复金仓新增字段保存失败
Fixes #305
2026-04-11 21:53:51 +08:00
Syngnat
60b63d7a22 feat(icon): 补充 SQL Server 数据库图标
Fixes #287
2026-04-11 21:53:50 +08:00
Syngnat
1f617f9d53 feat(storage): 支持自定义数据目录与显式迁移
Fixes #242
2026-04-11 21:53:50 +08:00
tianqijiuyun-latiao
1751e14d20 🐛 fix(security): 修复安全更新重检卡死与 Redis 密文兼容 2026-04-11 20:12:23 +08:00
tianqijiuyun-latiao
82e06bd94d 🐛 fix(security): 完善密文升级导入覆盖与安全更新链路
- 完善连接恢复包与 legacy 导入覆盖语义及密文兼容处理

- 修复安全更新详情高亮反馈与相关前后端链路

- 补强 keyring 误判边界与安全更新回归测试
2026-04-11 16:53:03 +08:00
DurianPankek
c810d999bd Merge 803c33b306 into 0009c98c7e 2026-04-11 13:23:51 +08:00
folltoshe
0009c98c7e feat(window): 在全屏状态下时隐藏圆角 2026-04-11 04:40:35 +08:00
tianqijiuyun-latiao
070ff72ad8 feat(security): 完成密文升级与连接恢复包导入导出 2026-04-10 21:29:45 +08:00
DurianPankek
803c33b306 🐛 fix(window): 修复 mac 原生全屏下输入时窗口丢失 2026-04-10 19:43:15 +08:00
Syngnat
1d882d089f 🐛 fix(driver): 修复可选驱动构建时 Go PATH 检测误判 (#353)
## 背景
在 `dev-ac6ef06` 构建中,安装 SQL Server 等可选驱动时,GoNavi 在部分 macOS 环境会误报“当前环境未安装
Go”。
实际问题并非未安装 Go,而是应用从图形界面启动时没有继承终端中的 PATH,导致 `brew` 安装的 Go(如
`/opt/homebrew/bin/go`)无法被 `exec.LookPath("go")` 发现,进而阻塞可选驱动代理的本地构建流程。

材料参考:

<img width="2142" height="1460" alt="连接失败"
src="https://github.com/user-attachments/assets/0844cf97-5720-4677-a806-65e056fa9766"
/>
<img width="289" height="74" alt="image"
src="https://github.com/user-attachments/assets/3e98e482-f74d-4b68-8605-b712fbdb98c1"
/>


## 关键修改
- 为可选驱动源码构建新增 `go` 可执行文件解析逻辑,避免仅依赖当前进程 PATH
- 增加常见 Go 安装路径兜底:
  - `/opt/homebrew/bin/go`
  - `/usr/local/go/bin/go`
  - `/usr/local/bin/go`
- 在常见路径未命中时,再回退到登录 shell 中执行 `command -v go`
- 解析 shell 输出时逐行筛选真实存在的路径,避免 shell 启动脚本输出额外提示导致误判
- 为 Go 探测逻辑补充单元测试,覆盖:
  - shell 列表去重与顺序
  - 常见路径回退
  - shell 回退
  - 噪音输出过滤

## 影响范围
- 仅影响可选驱动代理的源码构建阶段
- 不影响已内置驱动
- 不影响普通数据库连接、前端界面和其他业务逻辑
- 主要改善 macOS 图形界面启动应用时的 Go 环境探测兼容性

## 验证方式
已执行:
```bash
go test ./internal/app
```
## 修复效果
<img width="1114" height="784" alt="已连接1"
src="https://github.com/user-attachments/assets/72f4bb89-6c0b-4632-9098-3ce5b865e288"
/>
<img width="1032" height="791" alt="已连接2"
src="https://github.com/user-attachments/assets/6330cff2-c13b-4a9b-852d-8fc234819f81"
/>

## 验证点:
- 终端内已安装 Go 且可执行时,保持现有行为
- GUI 进程未继承 PATH 时,可通过常见目录或 shell 回退找到 Go
- shell 启动脚本存在额外输出时,仍可解析到真实 Go 路径

## 风险与回滚
### 风险:
- 仅新增本地命令探测与路径兜底逻辑,影响面较小
- 若用户使用非常规 Go 安装方式,仍可能需要后续补充手动指定 Go 路径的正式方案

### 回滚:
- 可直接回退本 PR 中 internal/app/methods_driver.go 与对应测试变更

## 备注
当前使用中还观察到“驱动下载链路域名不可达”在已有网络代理时可能出现误报,但该问题既不影响当前 PATH
修复的有效性,也并不阻塞下载,所以未纳入本次修改范围。
2026-04-09 17:17:15 +08:00
DurianPankek
19da7fc66c 🐛 fix(driver): improve Go PATH detection for optional driver builds 2026-04-09 16:16:22 +08:00
Syngnat
c1877ea013 🐛 fix(ci): 修复 dev-latest 历史 release 残留导致的重复发布问题
- 将 dev release 清理逻辑改为枚举所有历史 release
- 删除全部 tag_name=dev-latest 的 draft 与 prerelease 残留
- 避免只删除单个 release 时遗留旧的 Dev Build 页面项
- 保留后续 tag ref 清理与重新发布流程
- 确保 dev 发布前仓库中只保留一个有效的 dev-latest release
2026-04-09 13:34:41 +08:00
Syngnat
60dbb8a559 🐛 fix(ci): 修复 dev 预发布构建时间格式并收敛并发运行
- 为 dev-build workflow 增加同分支并发互斥配置
- 避免多个 dev 运行同时操作 dev-latest release
- 新增构建时间格式化步骤,将时间统一输出为 yyyy-MM-dd HH:mm:ss
- 将 release 文案中的构建时间改为引用格式化结果
- 保持现有 dev 版本号与 release/tag 清理逻辑不变
2026-04-09 13:31:01 +08:00
Syngnat
67fe3e3017 🐛 fix(ci): 修复 dev 预发布因重复 tag 导致的发布失败
- 将 dev-latest 预发布前的清理步骤改为 actions/github-script
- 显式按 GitHub API 删除已存在的 release 与 tags/dev-latest 引用
- 移除对未维护 delete-tag-and-release 动作的依赖
- 保持 dev-latest 固定预发布标签语义不变
- 避免 softprops/action-gh-release 在 finalizing release 阶段因 tag_name already_exists 失败
2026-04-09 12:30:13 +08:00
tianqijiuyun-latiao
1a042321d2 🐛 fix(connection): 修复失败连接高频重试并暂停后台自动元数据拉取 #331
- 后端为失败数据库连接增加冷却窗口,避免短时间内重复真实建连
- 补充失败冷却回归测试,覆盖重复失败、冷却后重试和成功后清理场景
- 前端在后台态暂停查询页、侧边栏和表概览的自动元数据拉取
- 保持手动刷新、手动展开等显式操作行为不变
2026-04-08 22:31:50 +08:00
Syngnat
35944d58f8 合并拉取请求 #344
🐛 fix(connection-modal): 修复连接配置输入框自动首字母大写问题
2026-04-08 19:39:07 +08:00
DurianPankek
5c2509c37f 🐛 fix(connection-modal): 修复连接配置输入框自动首字母大写问题 2026-04-08 19:11:18 +08:00
Syngnat
8e1b01b550 Revert "🐛 fix(connection-modal): 修复连接配置输入框自动首字母大写问题 (#344)"
This reverts commit 29fa5eb6df.
2026-04-08 10:41:34 +08:00
Syngnat
29fa5eb6df 🐛 fix(connection-modal): 修复连接配置输入框自动首字母大写问题 (#344)
合并外部贡献者 DurianPancake 的 PR #344 修改(解决冲突后)。

主要改动:
- 新增 noAutoCapInputProps 常量,统一关闭 autoCapitalize/autoCorrect/spellCheck
- 在所有文本输入组件(Input/Input.Password/Input.TextArea)应用该属性
- 增加弹窗级 MutationObserver 兜底,对动态 DOM 元素补充禁用属性

影响范围: 仅前端连接配置弹窗内的文本输入行为,后端逻辑不变
2026-04-08 10:40:16 +08:00
Syngnat
7c6391af3d ️ perf(ai-chat): 优化会话持久化序列化方式
- 序列化方式:AISaveSession 从 MarshalIndent 改为 Marshal
- 存储优化:移除不必要的缩进格式化,减少磁盘占用
- 性能提升:紧凑 JSON 序列化速度更快,减少内存分配
2026-04-08 10:30:51 +08:00
Syngnat
5746796bc2 🐛 fix(export): 修复导出时间时区误偏移 (#345)
## 背景
导出查询结果时,时间字段在部分场景出现错误时区偏移。典型表现为数据库中正确的本地时间在导出后被额外偏移(例如 +8 小时),影响
JSON/文本类导出的可用性与可信度。

## 变更内容
- 修复导出时间解析逻辑,区分“带时区时间字符串”和“无时区时间字符串”的处理方式:
  - 带时区值按其时区语义解析;
  - 无时区值按本地语义解析,避免误按 UTC 导致二次偏移。
- 统一导出时间格式化行为,避免在导出阶段再次进行不必要的时区换算,确保 `timestamp without time zone`
等场景保持原始钟表时间。
- 补充回归测试,覆盖以下关键路径:
  - 无时区时间字符串导出不偏移;
  - RFC3339 字符串解析后格式化行为稳定;
  - `time.Time` 导出保持预期钟表时间;
  - JSON 导出时间字段行为一致。

## 影响范围
- 主要影响导出链路中的时间字段格式化(CSV/JSON/MD/HTML/XLSX 对应后端写出逻辑)。
- 不涉及连接协议、SQL 执行流程和驱动安装机制。

## 验证方式
- 已通过:
  - `go test ./internal/app`
  - `go test -race ./internal/app`
  - `go test ./...`

## 风险与说明
- 已确认并修复本次问题对应的导出时区偏移路径。
- 当前系统仍存在“基于值推断时间语义”的历史设计约束;这里的“元数据驱动”是指基于数据库列定义类型(如 `timestamp
with/without time zone`、`datetimeoffset` 等)来决定是否允许时区换算。
- 上述历史约束并非本次修改引入。后续建议按数据库类型矩阵(DB matrix)逐库适配元数据策略,以降低跨数据库兼容风险与误判风险。

## 相关截图
- 问题对比:问题1、问题2
<img width="419" height="170" alt="问题1"
src="https://github.com/user-attachments/assets/a4d9f949-1f5c-4dcc-b3fa-13082347fec3"
/>
<img width="736" height="130" alt="问题2"
src="https://github.com/user-attachments/assets/b1d5b9e4-7f79-4929-875c-a422d1fbe51b"
/>

---
- 修复后:修复1、修复2
<img width="548" height="130" alt="修复1"
src="https://github.com/user-attachments/assets/1ee0a91d-2dec-4060-9c8e-9817f437dae7"
/>
<img width="486" height="128" alt="修复2"
src="https://github.com/user-attachments/assets/baa8cb25-b08a-4f31-94d8-a4a50753fb97"
/>
2026-04-08 10:23:27 +08:00
DurianPankek
3ec7c9be9d 🐛 fix(export): 修复导出时间时区误偏移 2026-04-07 19:38:16 +08:00
辣条
ac6ef06413 feat(app): 合并配置密文存储、数据表增强与驱动相关修复 (#339)
## 背景
  本次合并汇总了 5 类功能,并对冲突处理后的代码进行了回归审查,目标是将配置密文存储能力、数据表体验增强及驱动相关修复一并并入。

  ## 本次变更
  1. 修复 Data Viewer 多列排序状态残留,避免排序条件切换后失效。
  2. 收紧 MongoDB 可选驱动支持区间,仅支持 1.17.x 与 2.x,并补齐对应版本识别与导入校验。
  3. 完成配置密文存储前后端闭环,包括:
     - 新增密钥存储基础设施与状态枚举
     - 拆分 AI Provider 元数据与密钥存储
     - 暴露连接配置、代理配置相关密钥存储 API
     - 前端状态迁移为不保存明文密钥
     - 通过连接配置 ID 路由 RPC 配置
     - 修复密文编辑与状态残留问题
  4. 增强 DataGrid 显示能力,补充展示策略并支持行级 SQL 复制。
  5. 修复本地驱动导入版本识别与数据库连接校验遗漏,补齐 ClickHouse 等相关校验路径。

  ## 附带修复
  - 修复 Claude CLI 在 Windows 下的测试稳定性问题。

  ## 验证情况
  - `go test ./...`
  - `go build ./...`
- `npm test -- src/store.test.ts src/utils/dataGridDisplay.test.ts
src/components/dataGridCopyInsert.test.ts
src/utils/connectionRpcConfig.test.ts
src/utils/connectionSecretDraft.test.ts src/utils/
providerSecretDraft.test.ts src/utils/customConnectionDsn.test.ts
src/utils/aiProviderEditorState.test.ts
src/utils/browserMockConnections.test.ts
src/utils/dataViewerAutoFetch.test.ts`
  - `npm run build`

  ## 说明
  - 其他功能主要依据提交差异、代码检查与自动化测试完成回归确认。
  - 当前未发现因冲突处理导致的明确编译问题、功能失效或目标偏离
2026-04-05 12:42:59 +08:00
tianqijiuyun-latiao
ac0b6c05e8 🐛 fix(database): 修复本地驱动导入版本识别与连接校验遗漏
- MongoDB 本地导入按所选版本解析目录与压缩包

- ClickHouse 连接测试补充 query path 校验

- 补充驱动版本与查询路径回归测试
2026-04-05 12:09:06 +08:00
tianqijiuyun-latiao
37b3c78049 feat(datagrid): 增强数据表显示与行级SQL复制
- 新增 DataGrid 竖向分隔线与列宽模式配置并持久化\n- 支持复制 INSERT/UPDATE/DELETE 并按主键或唯一键生成条件\n- 补充外观配置与 SQL 复制相关测试
2026-04-05 12:06:40 +08:00
tianqijiuyun-latiao
255cc14bf6 🐛 fix(config-secret-storage): 修复密文编辑与状态残留问题
- 修复自定义连接编辑时已保存 DSN 无法留空沿用的问题
- 重置 AI 供应商编辑态与清空密钥开关,避免关闭后状态残留
- 对齐浏览器 mock 复制连接的 config.id 语义并补充回归测试
2026-04-05 11:59:38 +08:00
tianqijiuyun-latiao
4718755208 feat(security): 完成配置密文存储前后端闭环
- 补齐连接与代理密文字段的保留替换清空语义

- 接通保存复制删除导入接口并返回 secretless 视图

- 刷新 Wails 绑定并补充实现留痕文档
2026-04-05 11:52:59 +08:00
tianqijiuyun-latiao
91b5b85904 ♻️ refactor(security): 通过连接配置 ID 路由 RPC 配置 2026-04-05 11:42:28 +08:00
tianqijiuyun-latiao
c842201bf4 feat(security): 前端状态迁移至无明文密钥存储 2026-04-05 11:40:20 +08:00
tianqijiuyun-latiao
263db6bf30 feat(security): 暴露连接配置与代理的密钥存储 API 2026-04-05 11:39:54 +08:00
tianqijiuyun-latiao
b5e8f5c022 feat(security): 新增连接配置与代理的密钥仓库 2026-04-05 11:39:34 +08:00
tianqijiuyun-latiao
b62d22395b feat(security): 拆分 AI 供应商元数据与密钥存储 2026-04-05 11:39:15 +08:00
tianqijiuyun-latiao
f74270d585 🐛 fix(security): 新增密钥存储状态枚举 2026-04-05 11:38:56 +08:00
tianqijiuyun-latiao
ef64a24e01 feat(security): 新增密钥存储基础架构 2026-04-05 11:38:41 +08:00
tianqijiuyun-latiao
c1266c225a 🐛 fix(ai/provider): 修复 Claude CLI 在 Windows 上的测试稳定性 2026-04-05 11:33:39 +08:00
tianqijiuyun-latiao
acee1a06e8 fix(driver): 收紧 MongoDB 驱动支持区间 2026-04-05 11:32:41 +08:00
tianqijiuyun-latiao
eddb9f38c9 🐛 fix(data-viewer): 修复多列排序状态残留导致排序失效
- 将表格排序状态改为按当前 sorter 结果重建\n- 避免取消或切换多列排序后保留失效字段\n- 抽取排序状态归一化工具供数据表复用
2026-04-05 11:32:37 +08:00
Syngnat
fbda6917f7 🐛 fix(ai-chat): 修复 DeepSeek 回显引导提示词并优化收敛策略
- 删除 ≥5 轮注入 system 提示的逻辑(部分模型会将其当作对话内容输出)
- 改为 ≥10 轮时移除 tools 参数,从物理层面终止工具调用循环
2026-04-02 11:04:57 +08:00
Syngnat
b022cd63e5 🐛 fix(ai-chat): 修复重新生成时缺少状态过渡动画的问题
- handleRetryMessage 补齐 connecting 过渡消息(波纹动画),与 handleSend 流程一致
- 重试时同步重置工具调用计数器,防止继承旧计数导致过早熔断
2026-04-02 10:56:27 +08:00
Syngnat
9eb42565f1 🐛 fix(ai-chat): 修复工具调用无限循环与写操作误报执行失败问题
- 循环熔断:新增全局工具调用总轮次上限(15轮),防止 DeepSeek 等模型无限循环
- 软引导:工具调用 ≥5 轮时注入 system 提示引导模型尽快收敛输出
- LIMIT 修复:execute_sql 不再对 UPDATE/DELETE/INSERT 等写操作追加 LIMIT 50
- 语法防御:去除 SQL 末尾分号防止拼接出 "; LIMIT 50" 的无效语法
2026-04-02 10:49:11 +08:00
Syngnat
6d533167da feat(sidebar/table-overview): 优化右键菜单交互,增加危险操作二级分类防误触
- 菜单增强:为数据库、表、视图、函数等底层对象节点新增「危险操作」二级子菜单
- 误触防护:将明确破坏性的「删除表」、「删除数据库」等入口移至更深层级进行视觉隔离
- UI 交互:引入 WarningOutlined 图标单独高亮标识风险区域
- 统一作用域:同步变更至侧边栏连接树 (Sidebar) 和表数据概览 (TableOverview) 的上下文菜单
2026-04-02 10:11:33 +08:00
Syngnat
f992ad72e6 feat(mongodb): 支持无认证模式连接低版本 MongoDB 实例
- 连接表单:验证方式新增"无认证 (None)"选项,MongoDB 用户名改为非必填
- URI 构建:当 MongoAuthMechanism 为 NONE 时跳过 user/password/authSource/authMechanism
- 兼容优化:无用户名时不再默认设置 authSource=admin,避免驱动对无密码实例发起认证
- 双版本同步:mongodb_impl.go 与 mongodb_impl_v1.go 同步修改
- refs #303
2026-04-01 16:46:27 +08:00
Syngnat
5c0f6f8ff4 🐛 fix(data-grid): 修复数据预览面板日期格式化、JSON切换失效及幽灵变更计数问题
- 日期时间字段预览时通过 normalizeDateTimeString 格式化带时区的 ISO 格式
- 切换单元格时始终更新预览值,用 dataPanelOriginalRef 替代 suppress 机制判断 dirty
- handleCellSave 增加根源级变更检测,与原始 data 逐字段比较后才写入 modifiedRows
- 英文消息 "No changes to commit" 改为中文 "没有可提交的变更"
- refs #301
2026-04-01 16:21:57 +08:00
Syngnat
1eb517f083 ♻️ refactor(table-designer): 按索引类别精确分类索引方法类型选项
- MySQL InnoDB:所有索引类别均为固定方法(BTREE/FULLTEXT/RTREE),移除无意义的"默认"选项
- PostgreSQL:普通索引保留全部方法选项,主键和唯一索引固定为 BTREE
- 新增 getFixedIndexType 辅助函数,切换索引类别时自动设置对应的固定方法类型
- getIndexTypeOptions 接受 kind 参数,按类别动态返回精确的选项列表
- 切换类别时若当前方法不在新选项中,自动重置为合法值
- refs #299
2026-04-01 16:04:22 +08:00
Syngnat
02fa0aef46 🔥 remove(table-designer): 移除 MySQL 索引类型中不支持的 HASH 和 RTREE 选项
- MySQL InnoDB 引擎不支持手动创建 HASH/RTREE 索引,执行后会静默降级为 BTREE
- 从 MYSQL_INDEX_TYPE_OPTIONS 中移除 HASH 和 RTREE,避免用户误选导致修改"不生效"
- MySQL 下仅保留 DEFAULT/BTREE/FULLTEXT/SPATIAL 四种索引类型
- refs #298
2026-04-01 15:54:29 +08:00
Syngnat
f7107a1625 🐛 fix(data-grid): 修复单元格编辑值丢失及日期选择器滚动偏移问题
- 移除 Form.Item 的 preserve={false},修复嵌套字段名下编辑后值变为 undefined 的问题
- 将表单值初始化移至 useEffect([editing]),确保每次编辑时从 record 重新读取并覆盖旧值
- 新增 cellRef 绑定单元格 DOM,用于定位滚动容器
- DatePicker/TimePicker 面板打开时在 ant-table-wrapper 上拦截 wheel 事件,阻止表格滚动导致选择器漂移
- 面板关闭时自动移除 wheel 事件监听,恢复正常滚动
- refs #297
2026-04-01 15:45:50 +08:00
Syngnat
08ab06c038 feat(sidebar/table-overview): 优化侧边栏交互并新增表概览列表视图
- 修复连接刷新后数据库节点无法再次展开的问题,刷新时清除子节点 expandedKeys/loadedKeys/loadingRef
- 表概览由双击改为单击"表(N)"分组节点打开,双击仅触发展开/折叠
- 使用 clickTimerRef 延时防抖区分单击与双击事件,避免双击同时打开表概览
- 表概览新增列表视图模式,展示表名、注释、行数、数据大小、索引大小、引擎等列
- 工具栏新增卡片/列表视图切换按钮,两种视图共享搜索、排序和右键菜单
- refs #296
- refs #324
2026-04-01 15:29:42 +08:00
Syngnat
3402b56fdb 🎨 style(data-grid): 重构筛选面板为 flex 分区布局
- 外层改为 flex column,拆分为可滚动内容区(maxHeight: 200px)和固定操作栏
- "添加排序"从内容区提升到操作栏,条件渲染依赖 onSort 存在性
- "添加条件"使用 primary ghost 按钮增强辨识度
- refs #295
2026-04-01 15:03:02 +08:00
Syngnat
2c2baca69f 🐛 fix(data-grid): 修复日期时间字段二次编辑时日历残留上次选择日期标记
- Form.Item 默认 preserve={true},DatePicker 卸载后表单仍保留旧 dayjs 值
- 再次进入编辑时 DatePicker 读取到残留值,导致日历面板显示上次选择的日期圆圈
- 设置 preserve={false} 确保每次编辑态卸载后清除字段值,消除残留标记
- refs #290
2026-04-01 14:53:44 +08:00
Syngnat
e464c2cce1 🐛 fix(data-grid): 修复日期时间类型字段编辑交互问题并中文化日期选择器
- 修复"此刻"按钮点击后自动提交的问题,改为自定义按钮仅填值、需点击"确定"才保存
- 修复 datetime 编辑态点击外部后不退出的问题,通过 onBlur + pickerOpenRef 兜底
- 全局配置 dayjs 中文 locale,日期选择器月份/星期等文本显示为中文
- 为 time/date/year 类型 picker 添加 onBlur 兜底,确保焦点离开后退出编辑
- save 函数增加 editing 守卫和 catch 兜底,防止重复保存或异常时卡死编辑态
- refs #289
2026-04-01 14:49:28 +08:00
Syngnat
15f72c013d 📝 docs(readme): 新增项目 Star 增长趋势图与动态状态徽章
- 状态徽章:顶部引入 Shields.io 徽章,实时展示当前总 Star 数与全资源累计下载量
- 增长趋势:底部区域新增 Star History 的动态增长曲线图表
- 兼容性修复:将 HTML `<picture>` 语法回退为标准 Markdown 图片格式,解决部分本地开发工具的预览问题
- 国际化同步:中美双语(README.md 与 README.zh-CN.md)同步部署展示更新
2026-04-01 14:04:26 +08:00
Syngnat
c2c8870841 📝 docs(readme): 新增项目 Star 增长趋势图与动态状态徽章
- 状态徽章:顶部引入 Shields.io 徽章,实时展示当前总 Star 数与全资源累计下载量
- 增长趋势:底部区域新增 Star History 的动态增长曲线图表
- 兼容性修复:将 HTML `<picture>` 语法回退为标准 Markdown 图片格式,解决部分本地开发工具的预览问题
- 国际化同步:中美双语(README.md 与 README.zh-CN.md)同步部署展示更新
2026-04-01 14:03:14 +08:00
Syngnat
4f7ac7149a 📝 docs(readme): 新增项目 Star 增长趋势图与动态状态徽章
- 状态徽章:顶部引入 Shields.io 徽章,实时展示当前总 Star 数与全资源累计下载量
- 增长趋势:底部区域新增 Star History 的动态增长曲线图表
- 兼容性修复:将 HTML `<picture>` 语法回退为标准 Markdown 图片格式,解决部分本地开发工具的预览问题
- 国际化同步:中美双语(README.md 与 README.zh-CN.md)同步部署展示更新
2026-04-01 13:52:57 +08:00
tianqijiuyun-latiao
8d8af530a7 Merge remote-tracking branch 'upstream/dev' into feature/20260327_opt
# Conflicts:
#	frontend/src/components/DataGrid.tsx
2026-03-31 12:36:20 +08:00
tianqijiuyun-latiao
29b96719d5 🐛 fix(sql): 修复时间字段复制与导出SQL格式 2026-03-31 12:29:03 +08:00
Syngnat
9c96246320 feat(postgres): PostgreSQL 支持不带 schema 前缀的表名补全与执行
- 后端优化:连接成功后自动查询所有用户 schema 并将 search_path 写入 DSN 重建连接池
- 连接池修复:SET search_path 仅对单个连接生效,改为 DSN 级别配置使所有连接生效
- 表名补全:前端智能匹配 schema.table 中的纯表名部分,输入表名即可触发补全
- 同名表处理:跨 schema 存在同名表时补全自动显示 schema.table 格式以区分
- 列补全增强:FROM/JOIN 引用纯表名时关联列提示和别名列提示均可正确匹配
2026-03-31 12:09:33 +08:00
Syngnat
31644dee6b 🐛 fix(dameng): 修复达梦数据源侧边栏无法展开数据库节点的问题
- 权限适配:取消对 SYSDBA schema 的默认过滤,并增加 `SELECT USER FROM DUAL` 兜底查询
- 树节点容错:Sidebar 当数据库为空时不再阻断加载状态,允许用户重试刷新并增加明确提示
- 类型修正:修复 RedisMonitor 组件 `NodeJS.Timeout` 在 Vite 下的编译报错
- 测试覆盖:补充达梦 SYSDBA 过滤及兜底查询逻辑的单元测试
2026-03-30 19:46:05 +08:00
Syngnat
aa9d8d243a feat(redis/monitor/oracle/data-viewer): 新增 Redis 实例监控并优化 Oracle 大表预览体验
- 新增 RedisMonitor 面板,展示 QPS、内存、CPU、连接数和键数量趋势图
- 引入 recharts 依赖并补齐监控图表所需前端包与锁文件
- Sidebar 新增 Redis 实例监控入口,TabManager 与 TabData 接入 redis-monitor 页签类型
- RedisCommandEditor 支持多行脚本块解析、选区执行、耗时记录与终端化结果展示
- Oracle 表预览移除自动精确 COUNT(*),避免打开大表时额外阻塞
- 无筛选整表预览接入 ALL_TABLES.NUM_ROWS 近似总数展示,并拆分近似总数与近似总页数语义
2026-03-30 16:48:19 +08:00
Syngnat
6e55d63877 📝 docs(readme): 更新AI助手功能描述与界面截图,并添加友情链接
- 核心特性:补充 AI 智能助手的多模型支持、表结构上下文和快捷指令介绍
- 界面更新:移除旧版截图,替换为全新的 AI 对话、模型配置与上下文选择界面截图
- 友情链接:在文档底部补充 linux.do 及 AI全书 链接
- 多语言:同步更新中英文双语版 README 细节内容
2026-03-30 10:43:46 +08:00
tianqijiuyun-latiao
c126c4b731 Merge remote-tracking branch 'upstream/dev' into feature/20260327_opt 2026-03-29 22:34:39 +08:00
tianqijiuyun-latiao
c85de27aac perf(query): 批量写语句走一次性 Exec 减少网络往返,修复大量 INSERT 执行慢问题
- 新增 BatchWriteExecer 可选接口(ExecBatchContext)
- MySQL/MariaDB/Doris/PostgreSQL/SQLite/DuckDB 实现该接口
- DBQueryMulti 检测到纯写操作时走批量路径,500 条 INSERT 从 500 次网络往返降至 1 次
- 混合语句(SELECT + INSERT)及不支持该接口的驱动继续走原有逐条执行路径
2026-03-29 12:17:37 +08:00
Syngnat
eeef0f06ed 🐛 fix(app): 修复供应商预设识别并兼容Wails开发模式资源加载
- 抽离供应商预设匹配逻辑,避免自定义 OpenAI 端点误识别为千问 Coding Plan
- 调整 AI 设置弹窗的预设回填逻辑,并补充预设识别回归测试
- 通过 dev/prod build tag 拆分前端资源装配,避免开发模式依赖 frontend/dist
2026-03-28 17:40:27 +08:00
Syngnat
fcd4d4026c 🔧 chore(gitignore): 移除本地追踪文档并补充忽略规则
- 从版本控制中移除 docs/superpowers 下的计划与设计文档
- 从版本控制中移除 docs/需求追踪 下的本地进度追踪文档
- 补充忽略规则,避免本地需求追踪与 superpowers 文档再次误提交
2026-03-28 17:35:21 +08:00
Syngnat
a7bee7f3b6 feat(ai-entry): 优化AI助手贴边入口交互体验
- 将 AI 助手入口从侧栏工具区迁移为主内容区右侧贴边标签
- 调整打开态贴边标签锚点到面板左外沿,避免遮挡头部操作区
- 重排侧栏顶部工具布局,恢复四项按钮的稳定网格结构
- 新增 aiEntryLayout 布局辅助与回归测试,覆盖打开态附着位置
2026-03-28 16:48:06 +08:00
tianqijiuyun-latiao
ed4a7b96d4 🐛 fix(query): 修复千万级表查询超时、表头备注类型不显示及datetime INSERT格式问题 refs #307
- QueryEditor: SQL编辑器查询 timeout 下限设为 120s,防止大表全量查询被 30s 超时取消
- QueryEditor: 放宽表名提取正则,支持 SELECT col1,col2 FROM table 形式,修复表头备注/类型不显示
- DataGrid: handleCopyInsert 对 datetime 值调用 normalizeDateTimeString,消除 RFC3339 格式中的 T 和时区后缀
2026-03-27 18:39:09 +08:00
Syngnat
09d013f27d 🐛 fix(app): 为稳定期首次连接增加瞬时网络重试保护 (#309)
## 问题背景
在 app 启动后等待 20s 以上,再手动触发数据库连接时,遇到瞬时网络错误(如 `no route to
host`)会立即失败,用户体感为“没有重试”。

相关讨论与上下文参考:
- https://github.com/Syngnat/GoNavi/pull/294

## 问题描述
此前重试保护逻辑主要以“应用启动窗口(20s)”为边界:
- 启动窗口内:瞬时网络失败会自动重试
- 启动窗口外:即使是瞬时网络失败也不重试
这导致“用户首次手动连接发生在稳定期”时,行为与预期不一致。

## 本地复现关键日志(节选)
```log
2026/03/27 15:21:04.792462 [INFO] 应用启动完成(首次连接保护窗口=20s,最多重试=4 次)
2026/03/27 15:22:29.196794 [INFO] 获取数据库连接:... 启动阶段=稳定期(age=1m24.405s)
2026/03/27 15:22:29.208920 [ERROR] 建立数据库连接失败:... connect: no route to host
2026/03/27 15:22:29.212453 [ERROR] DBGetDatabases 获取连接失败:... connect: no route to host


2026/03/27 16:07:45.463959 [INFO] 获取数据库连接:... 启动阶段=稳定期(age=21s)
2026/03/27 16:07:45.470744 [ERROR] 建立数据库连接失败:... connect: no route to host
2026/03/27 16:07:45.473604 [WARN] 检测到瞬时网络失败,准备重试连接:... 尝试=1/4 延迟=800ms
2026/03/27 16:07:46.277658 [INFO] 获取数据库连接:... 启动阶段=稳定期(age=21.814s)
2026/03/27 16:07:46.281761 [INFO] 创建数据库驱动实例:... 尝试=2/4
2026/03/27 16:07:46.284741 [ERROR] 建立数据库连接失败:... connect: no route to host
2026/03/27 16:20:59.298636 [INFO] 应用启动完成(首次连接保护窗口=20s,最多重试=4 次)
2026/03/27 16:23:26.180978 [INFO] 获取数据库连接:... 启动阶段=稳定期(age=2m26.883s)
2026/03/27 16:23:26.201478 [INFO] 数据库连接成功并写入缓存:...
```

## 变更内容
- 调整连接重试判定逻辑:
  - 启动窗口内:保持原有重试预算(最多 4 次)
  - 启动窗口外:若为瞬时网络错误,补充一次保护重试(总计 2 次尝试)
  - 非瞬时错误(如认证失败)在稳定期不重试
- 日志文案泛化,避免“仅启动期”误导:
  - 数据库连接在重试后成功
  - 检测到瞬时网络失败,准备重试连接
## 测试与验证
### 新增/更新单元测试覆盖以下场景:
- 启动期瞬时错误重试并成功
- 稳定期瞬时错误重试一次并成功
- 稳定期瞬时错误持续失败时,仅重试一次后停止
- 稳定期非瞬时错误不重试
- 稳定期重试路径输出重试提示日志
- 启动期瞬时错误失败时使用完整重试预算
### 本地执行:
- go test ./internal/app -run StartupRetry -count=1
- go test ./internal/app -count=1
### 影响范围
- 连接建立重试策略(internal/app/app.go)
- 启动重试相关测试(internal/app/app_startup_connect_retry_test.go)
## 风险与回滚
- 风险:稳定期瞬时网络错误会增加一次重试等待(约 800ms)
- 回滚:可回退本 PR 即恢复“仅启动窗口重试”的旧策略
2026-03-27 17:30:14 +08:00
Syngnat
09aa526570 🐛 fix(ai/provider/chat-ui): 修复千问 Coding Plan 预设与 Claude CLI 报错
- 统一千问 Coding Plan 到 claude-cli 链路
- 修正旧配置识别与模型列表逻辑
- 透传 Claude CLI 鉴权失败和错误事件
- 移除误杀正常回复的启动定时器
2026-03-27 17:02:51 +08:00
DurianPankek
5844cd7c01 🐛 fix(app): 为稳定期首次连接增加瞬时网络重试保护 2026-03-27 16:27:46 +08:00
Syngnat
4f74c44147 🐛 fix(ai/provider/chat-ui): 修复AI供应商兼容性并优化聊天提示交互
- 修复通义千问百炼 Anthropic 兼容鉴权头与健康检查请求
- 拆分通义千问百炼通用与 Coding Plan 双入口,调整预设回填与模型策略
- 修复火山 Coding Plan 模型列表过滤逻辑,避免混入无关模型
- 统一 OpenAI 兼容供应商路径与模型列表处理,补充相关服务层测试
- 优化 AI 设置供应商卡片布局,统一高度并收紧文本展示
- 将聊天区模型校验提示改为输入框上方的内联提示卡,补充前端回归测试
2026-03-27 14:29:03 +08:00
Syngnat
a5fdfefa2d 🐛 fix(ai/volcengine): 修复火山引擎兼容路径并拆分双预设
- OpenAI 兼容 URL 归一化改为保留已有 v3 和 v4 版本段,避免火山与智谱地址被错误补 /v1
- 对误填 /chat/completions 和 /models 的地址先回退到 base URL,再拼接目标端点
- 模型列表与连通性检测复用统一端点解析逻辑,修复火山 Coding Plan 等兼容服务请求
- AI 设置页拆分火山方舟与火山 Coding Plan 两个预设,并按完整路径精确匹配回显
- 修正模型下拉默认值行为,未选模型时保持占位态,避免误用动态列表首项
- 补充 provider 与 service 回归测试,并新增需求追踪文档
2026-03-27 12:04:55 +08:00
Syngnat
37ac13b94e 🐛 fix(ai/wails-binding): 修复生命周期绑定生成类型错误
- 收敛 App 与 AI Service 的内部生命周期方法,避免被 Wails 误导出到前端
- 将启动初始化改为包级生命周期接线,保持主程序启动流程不变
- 隐藏内部清理方法,移除生成绑定中的无效 context/time 类型声明
- 同步更新 frontend/wailsjs 绑定文件,清理 Service 与 App 的错误导出
- 调整相关测试调用,确保内部方法重命名后行为一致
2026-03-27 11:42:57 +08:00
Syngnat
d4d685b076 feat(ci/ai): 新增 dev 分支自动构建工作流并增强 Claude CLI Windows 兼容性
- CI 新增:添加 dev-build.yml,push dev 分支自动触发全平台构建并发布 Pre-release
- CI 清理:删除已废弃的 test-build-all-platforms.yml 和 test-macos-build.yml
- Claude CLI:新增 Windows 环境自动探测 Git Bash 路径(ProgramFiles/LocalAppData 多候选)
- Claude CLI:setEnv 改为可返回 error,环境变量操作提纯为 buildClaudeCLIEnv 纯函数
- Claude CLI:新增 upsertEnv/fileExists/joinWindowsPath 等工具函数
2026-03-27 10:49:39 +08:00
Syngnat
9f6d524e3d 🐛 fix(ai/provider): 修复 Kimi 与 MiniMax 供应商兼容路由
- 调整 Kimi 预设为 Anthropic 兼容入口并修正 Moonshot 域名回显
- 修复 Anthropic 请求地址归一化,确保聊天请求正确落到 /v1/messages
- 修正 Kimi 模型列表与测试连接路由,固定使用 Moonshot /v1/models
- 修正 MiniMax 默认模型与兼容模型集合,避免请求不存在的 /anthropic/v1/models
- 为 MiniMax 健康检查改用最小化 messages 请求,并兼容旧模型名配置
- 补充 Kimi 与 MiniMax 供应商回归测试,更新需求追踪文档
2026-03-26 22:26:25 +08:00
Syngnat
a89289f1cc Merge branch 'dev' into feature/ai-integration-ygf-20260323 2026-03-26 20:29:20 +08:00
Syngnat
b958ff6481 🐛 fix(ai/query-editor/mac-window): 修复模型兼容性并优化即时执行与窗口交互
- AI 兼容性:为 Anthropic Provider 补齐 tools/tool_use/tool_result 协议转换,支持工具调用与流式工具结果解析
- 降级策略:OpenAI 兼容接口在 tools 请求返回 400/422/404 时自动回退为纯文本模式
- 配置修复:调整 MiniMax 预设为 Anthropic 兼容端点并更新默认模型列表
- 状态隔离:AI 聊天面板停止将动态模型列表写回供应商配置,避免污染静态 models 数据
- 编辑器修复:QueryEditor 在 runImmediately 场景下避免重复追加 SQL,改为直接选中并执行
- 交互优化:修复 macOS 原生窗口控制切换与标题栏点击行为,避免窗口按钮状态异常
2026-03-26 17:57:29 +08:00
Syngnat
98e9e5686d feat(ai): 发布全新 AI Copilot 助手面板与工作区智能打通
- 核心架构:新增独立 AI 会话中枢,集成主流大模型生态(含私有部署中继版)的无感衔接发问
- 智能诊断:打破信息孤岛,大模型可通过关联工作区实时数据表 DDL 和错误栈,充当专属 DBA 排错及代码编写
- 视觉与多模态:支持极简发图读图交互体验,智能补全模型所需的缺省预警 Prompt,并兼容不规范中转端点图文并茂
- UI 与性能:重构聊天浮层挂靠逻辑与渲染阻断,应对长时间巨量问答引发的卡段内存泄漏,会话自动保存归档
2026-03-26 16:02:08 +08:00
Syngnat
93446e060e 🐛 fix(table-designer): 修复索引编辑丢失与勾选异常 (#302)
## 问题描述


问题1:设计表中修改索引时,当前实现采用“先删除旧索引,再创建新索引”的流程。当新索引创建过程中出现异常时,旧索引已经被删除,最终会导致原有索引丢失。见https://github.com/Syngnat/GoNavi/issues/300
问题2:自测时发现

索引列表还存在选择交互异常:

- 只有一条索引被选中时,checkbox 偶发无法取消
- “修改”按钮会因选中状态异常而偶发不可用
- checkbox 与整行点击在某些情况下表现不一致


## 问题原因
### 1. 索引编辑失败后丢失原索引
索引修改流程是拆成两条 DDL 顺序执行:
1. 删除旧索引
2. 创建新索引
执行层没有事务保护,也没有失败补偿逻辑。
因此当第 1 步成功、第 2 步失败时,原索引会被直接删掉。

### 2. 索引勾选状态异常
索引表存在两套同时修改选中状态的交互:
- checkbox 自己维护一套 toggle
- 整行点击也维护一套 toggle
两套逻辑共同修改 `selectedIndexKeys`,会导致事件命中时出现互相抵消,从而出现:
- checkbox 偶发点不动
- 单选状态不稳定
- “修改”按钮偶发不可用

## 修复方案
### 1. 增加索引编辑失败恢复机制
- 抽出统一的 DDL 顺序执行逻辑,明确拿到失败语句位置
- 修改索引时,若旧索引删除成功但新索引创建失败,则自动尝试按旧定义恢复原索引
- 若无法恢复,则给出明确错误提示
- 同时增加“无实际变更”判断,避免无意义执行破坏性 DDL
### 2. 统一索引选择交互入口
- 将索引选中状态收敛到单一的 `toggleIndexSelection` 入口
- checkbox 区域改为只走同一套状态切换逻辑
- 阻断 checkbox 区域事件冒泡,避免和整行点击双重触发
- 消除重复选中与单选取消不稳定问题
### 3. 补充单元测试
新增针对索引相关 helper 的单元测试,覆盖:
- 索引行到编辑表单的归一化
- 无变更编辑识别
- 选择切换不重复
- 单选场景下反复点击可稳定选中/取消
- 仅在“删除旧索引成功、创建新索引失败”时触发恢复判断

## 验证效果
### 已验证
- 修改索引时,若新索引创建失败,会尝试恢复原索引
- 单条索引选中后,可稳定通过 checkbox 取消选中
- 多选/取消后,单选状态仍然稳定
- “修改”按钮随单选状态稳定启用/禁用

### 单元测试
执行命令:

```bash
npm test -- src/components/tableDesignerIndexUtils.test.ts
```

## 回归执行结果:

### 问题1
- 索引bug#300_上报问题现象
<img width="1119" height="433" alt="索引bug#300_上报问题现象"
src="https://github.com/user-attachments/assets/61831c2f-5840-4d0d-ab71-d6c82d0db63e"
/>

- 索引bug#300_修复效果截图
<img width="1500" height="460" alt="索引bug#300_修复效果截图"
src="https://github.com/user-attachments/assets/277fd339-9bc4-4cfb-9e0f-d2365e334cdd"
/>

### 问题2
- 索引修改前端事件问题现象截图,有时看着是正常的,实则是两套前端事件冲突

<img width="324" height="283" alt="索引修改前端事件问题"
src="https://github.com/user-attachments/assets/849c362c-4ce3-46b6-9a33-f7348be9c581"
/>

<img width="491" height="348" alt="索引修改前端事件问题2_有时看着是正常的"
src="https://github.com/user-attachments/assets/855a1ed7-1365-44cc-a2f9-6993c3d761e0"
/>

<img width="707" height="406" alt="索引修改前端事件问题3_checkbox事件冲突"
src="https://github.com/user-attachments/assets/3c5fc75f-9eb2-470e-8b0c-976b8eaf5a94"
/>

- 索引修改前端事件问题修复效果
<img width="2308" height="792" alt="索引修改前端事件问题修复效果"
src="https://github.com/user-attachments/assets/f22e8145-58fd-4ba1-9d29-e81a879af64d"
/>

### 影响范围说明
本次修改影响设计表中的“索引”页签交互与索引编辑执行流程,主要包括:
- 索引修改
- 索引单选/多选
- “修改”按钮启用状态
- 索引失败后的恢复处理
不影响:
- 普通表结构保存流程
- 外键维护逻辑
- 触发器维护逻辑
- 非索引相关页面交互

### 风险说明
- 索引恢复依赖旧索引定义能正确还原为创建 SQL
- 当前修复已覆盖前端交互和失败补偿逻辑,但不同数据库方言下仍建议结合实际库型回归验证一次索引修改流程
2026-03-26 12:16:45 +08:00
DurianPankek
ecc8ff1197 🐛 fix(table-designer): 修复索引编辑丢失与勾选异常 2026-03-23 17:32:11 +08:00
Syngnat
82369b4070 Merge branch 'dev' into pr-294 2026-03-22 20:58:21 +08:00
Syngnat
1bda751ada feat(ai-chat): 全面升级AI聊天面板并优化交互体验
- 消息管理:新增聊天气泡的重试、编辑与单条删除功能及相对应的持久化状态函数
- 快捷操作:支持长文一键滑动到底端,并在代码块内增加SQL一键送入编辑器的快捷执行机制
- 视觉优化:深化AI回复背景沉浸感,重绘AI洞察按钮并移除设置面板所有的冗余紫色调
- 设置调优:放宽模型初始必填限制,新增内置系统提示词(Builtin Prompt)全览面板
2026-03-22 20:54:29 +08:00
DurianPankek
7bc358d612 🐛 fix(connect): 修复首次启动数据库连接偶发失败 2026-03-21 16:17:29 +08:00
Syngnat
36a57f9601 feat(shortcut): 将 macOS 全屏切换快捷键注册到快捷键管理面板
- 新增 toggleMacFullscreen action 到 shortcuts.ts
- 新增 platformOnly 字段支持按平台过滤快捷键显示
- 默认绑定 Ctrl+Meta+F,仅 macOS 下显示
- 移除 App.tsx 中的硬编码全屏快捷键判断,统一走 shortcuts 系统
2026-03-20 21:44:12 +08:00
Syngnat
e85c561f1e feat(mac-window): 支持切换 macOS 原生窗口控制与原生全屏行为 (#288)
## 背景
当前 GoNavi 使用自定义无边框标题栏与右上角窗口按钮,在 macOS 下与系统原生窗口交互习惯存在明显差异:
- 缺少左上角原生红黄绿窗口控制按钮
- 绿色按钮不具备 macOS 原生全屏 / Space 语义
- 标题栏交互和系统应用不够一致

这个 PR 为 macOS 增加了可切换的原生窗口控制模式,在尽量不影响现有跨平台行为的前提下,补齐 macOS 用户更熟悉的窗口体验。

## 变更内容
- 在 `主题 -> 外观参数` 中新增 `使用 macOS 原生窗口控制` 开关
- 启用后:
  - 显示 macOS 左上角原生红黄绿按钮
  - 隐藏现有右上角自定义窗口按钮
  - 为标题栏内容预留原生按钮安全区域
  - 优先使用 macOS 原生全屏行为
  - 支持 `Control + Command + F` 切换全屏
- 修复原生全屏下按 `Esc` 会意外退出全屏的问题
- 补充窗口行为、边界条件和相关工具函数单元测试

## 影响范围
- 仅影响 macOS 下启用该开关时的窗口样式与交互
- Windows/Linux 默认行为不变
- Windows 构建已验证通过

## 验证结果
已完成以下验证:
- [x] `npm run test`
- [x] `npm run build`
- [x] `go test ./...`
- [x] macOS 手工验证通过
- [x] Windows 构建验证通过

### macOS 手工验证项
- [x] 设置页可见 `使用 macOS 原生窗口控制`
- [x] 开关关闭时,保留当前自定义标题栏与右上角按钮
- [x] 开关开启时,右上角自定义按钮隐藏
- [x] 开启后显示左上角原生红黄绿按钮
- [x] 绿色按钮进入原生全屏
- [x] 原生全屏进入独立 Space
- [x] `Control + Command + F` 可切换全屏
- [x] 原生全屏下按 `Esc` 不再意外退出全屏
- [x] 浅色 / 深色主题下显示正常
- [x] 模糊与透明效果在普通窗口和全屏下均可正常工作
- [x] 最小化行为正常

## 截图 / 演示
### 历史窗口样式
- `MAC_历史版本窗口.png`
<img width="1920" height="1080" alt="MAC_历史版本窗口"
src="https://github.com/user-attachments/assets/4bd9176f-9d7e-43d1-9e1a-c7a6bfc0e28c"
/>

### 设置项与菜单
- `MAC_菜单控制.png`
<img width="1278" height="909" alt="MAC_菜单控制"
src="https://github.com/user-attachments/assets/520da1b5-af59-4f1a-ba5d-36abdc03ef60"
/>

- `MAC_菜单控制_Dark.png`
<img width="1119" height="861" alt="MAC_菜单控制_Dark"
src="https://github.com/user-attachments/assets/b21af50e-b583-4895-b316-cc21b7498a51"
/>

- `MAC_恢复默认.png`
<img width="1526" height="922" alt="MAC_恢复默认"
src="https://github.com/user-attachments/assets/0129f69d-b2ca-45eb-847a-6b6cb37ca576"
/>

### 原生窗口控制效果
- `MAC_窗口组件原生控制.png`
<img width="1236" height="849" alt="MAC_窗口组件原生控制"
src="https://github.com/user-attachments/assets/003dba09-d0a8-4999-8241-f2d1db078d1b"
/>

- `MAC_窗口组件原生控制2.png`
<img width="1920" height="834" alt="MAC_窗口组件原生控制2"
src="https://github.com/user-attachments/assets/241c94a6-955f-41f8-9b1d-b9a40d0a5251"
/>

- `MAC_切换后变化.png`
<img width="1920" height="1080" alt="MAC_切换后变化"
src="https://github.com/user-attachments/assets/52d8977b-9c64-4413-85d9-f94bdcdc0e53"
/>

### 全屏、快捷键与 Space 行为
- `MAC_快捷键.png`
<img width="1227" height="846" alt="MAC_快捷键"
src="https://github.com/user-attachments/assets/2972cee3-3621-42f1-bd05-1e24eaded5ef"
/>

- `MAC_支持SPACE切换.png`
<img width="353" height="251" alt="MAC_支持SPACE切换"
src="https://github.com/user-attachments/assets/044974a6-64c4-4d0c-8ba9-3445af77f8e4"
/>

- `MAC_最大化.png`
<img width="1920" height="1079" alt="MAC_最大化"
src="https://github.com/user-attachments/assets/dbdf4cd4-0abd-4142-9c81-08c8c23af73b"
/>

### 模糊与透明效果
- `MAC_模糊与透明.png`
<img width="1267" height="954" alt="MAC_模糊与透明"
src="https://github.com/user-attachments/assets/f5a3a377-805e-4d5f-a3f0-fa588d77d9f7"
/>

- `MAC_模糊与透明_全屏.png`
<img width="1920" height="1080" alt="MAC_模糊与透明_全屏"
src="https://github.com/user-attachments/assets/e20642ba-b828-47d0-a154-c23a7b15643d"
/>

### 其他窗口行为
- `MAC_窗口最小化.png`
<img width="276" height="129" alt="MAC_窗口最小化"
src="https://github.com/user-attachments/assets/d7f758a0-072e-4c47-95e6-9539075f1d71"
/>

- `MAC_设置启动全屏-重新打开.png`
<img width="1920" height="1080" alt="MAC_设置启动全屏-重新打开"
src="https://github.com/user-attachments/assets/b033d102-5062-46cb-9c41-c6fe330df007"
/>

### Windows 回归验证
- `WINDOWS_菜单.png`
<img width="1920" height="1040" alt="WINDOWS_菜单"
src="https://github.com/user-attachments/assets/3a295470-c1c6-42f5-a265-2cd38e9224cf"
/>


- `WINDOWS_全屏.png`
<img width="1920" height="1040" alt="WINDOWS_全屏"
src="https://github.com/user-attachments/assets/b254dc81-0c42-4024-9f91-3e029bfe29a0"
/>

## 说明
- 本次实现优先保证 macOS 原生窗口交互一致性,而不是模拟系统按钮视觉
- 当前方案对非 macOS 平台保持兼容
- 如果窗口样式在切换当次未完全刷新,重启应用后可获得稳定表现
2026-03-20 21:18:43 +08:00
DurianPankek
2677364d0e feat(mac-window): 支持切换 macOS 原生窗口控制与原生全屏行为 2026-03-20 18:23:16 +08:00
Syngnat
da28207168 🐛 fix(ci): 修复 Release 构建资产丢失,checkout 隔离到独立目录
- checkout path 改为 repo-for-changelog 避免 git 操作干扰 release-assets
- Generate Changelog 步骤进入 checkout 子目录执行 git log
2026-03-20 16:51:42 +08:00
Syngnat
87cfbee6d3 🐛 fix(ci): 修复 Release 构建资产丢失问题
- Checkout 步骤添加 clean: false 防止 git clean 删除已下载的 release-assets
2026-03-20 16:23:13 +08:00
Syngnat
0100b771b0 🔧 ci(release): 优化 Release Notes 自动生成,按 commit 前缀分类展示详细变更
- 替换 generate_release_notes 为 git log 提取 commit message
- 按 emoji 前缀分 6 组:新功能、🐛修复、性能、♻️重构、🌐国际化、🔧其他
- 底部附加 compare 链接,空分类自动跳过
2026-03-20 16:07:25 +08:00
Syngnat
1758d6f918 feat(table-designer): 补全设计表字段类型列表,按数据库方言分组
- 新增 DB_TYPE_OPTIONS 按 MySQL/PostgreSQL/SQL Server/SQLite/Oracle 分组
- MySQL:补充数值(float/double/smallint/mediumint)、字符串(tinytext/mediumtext/longtext)、
  二进制(blob/tinyblob/mediumblob/longblob)、其他(enum/set/bit/year)
- PostgreSQL:补充 serial/boolean/timestamptz/jsonb/uuid/inet 等
- SQL Server:补充 float/real/money/nvarchar/datetime2/uniqueidentifier 等
- AutoComplete options 从固定 COMMON_TYPES 改为 DB_TYPE_OPTIONS[getDbType()] 动态获取
- refs #281
2026-03-20 16:00:35 +08:00
Syngnat
b86cfcacaa 🌐 fix(editor): 加载 monaco-editor 中文 NLS 语言包修复右键菜单英文显示
- 在 main.tsx 中 import 'monaco-editor/esm/nls.messages.zh-cn'
- NLS 必须在 monaco-editor 主包之前导入才能生效
- 覆盖所有 Monaco Editor 实例的内置菜单(Cut→剪切、Copy→复制等)
- refs #269
2026-03-20 15:52:38 +08:00
Syngnat
7d543e06c6 🐛 fix(export): 导出数据日期时间格式化为本地时区 yyyy-MM-dd HH:mm:ss
- formatExportCellText default 分支增加字符串日期时间解析与格式化
- normalizeExportJSONValue 新增 time.Time 和字符串日期时间处理
- 覆盖 CSV/JSON/XLSX/HTML/Markdown 全部导出格式
- refs #270
2026-03-20 15:44:53 +08:00
Syngnat
17e4e3ad1c feat(data-grid): 新增底部数据预览面板支持长数据字段完整查看与编辑
- 工具栏新增「数据预览」切换按钮,点击展开/收起底部面板
- 单击单元格自动更新面板内容,完整展示长文本和 JSON 数据
- 面板使用 Monaco Editor,JSON 数据自动语法高亮
- 编辑模式下支持直接修改并保存,只读模式下 Editor 设为 readOnly
- 支持 JSON 一键格式化功能
- 通过 ref 追踪面板状态避免 mergedColumns 过度重渲染
- refs #271
2026-03-20 15:37:17 +08:00
Syngnat
84579b83c9 feat(TableDesigner): 优化设计表滚动体验并支持索引批量删除
- 滚动优化:修复 Tab 切换时 ResizeObserver 高度归零导致表格异常滚动
- 零高度守卫:移除 activeKey 依赖,跳过 display:none 时的零高度观测
- 触发器统一:触发器 Tab 补充 scroll={{ y: tableHeight }} 与索引/外键保持一致
- 批量删除:handleDeleteIndex 支持多选索引批量生成 DROP SQL 合并执行
- 交互优化:删除确认弹窗展示选中索引数量和名称列表
- 状态清理:批量删除成功后自动清空 selectedIndexKeys
- refs #273
2026-03-20 15:21:47 +08:00
Syngnat
7ddef7096b 🐛 fix(editor): 修复DDL视图stickyScroll首行冻结透明背景导致字符重叠
- 根因定位:TableDesigner和TriggerViewer中局部主题定义覆盖了全局配置
- 全局主题新增editorStickyScroll.background不透明背景色
- 移除TableDesigner.tsx中重复的透明主题定义(26行)
- 移除TriggerViewer.tsx中重复的透明主题定义(26行)
- 清理未使用的loader import
- refs #274
2026-03-20 15:00:00 +08:00
Syngnat
557178f182 🐛 fix(query-editor): 修复SQL查询结果行数限制和显示问题
- 移除每次执行SQL重复弹出"结果集已自动限制"的warning提示
- 用户手写LIMIT时尊重原始结果,不再被前端maxRows截断
- 结果集tab标签显示精确行数,去掉"1000+"的+号后缀
- refs #275
2026-03-20 14:50:18 +08:00
Syngnat
a1b546ddd9 feat(data-grid): 日期时间类型字段集成DatePicker选择器
- 类型识别:根据列元数据自动识别datetime/date/time/year类型
- inline编辑:日期时间列双击弹出DatePicker替代纯文本Input
- 行编辑器:日期时间字段使用DatePicker组件
- 交互优化:datetime类型需点"确定"按钮才保存,date/time/year即选即保存
- 取消支持:datetime选择器点击外部自动取消编辑,不保存
- 值转换:编辑时字符串↔dayjs自动转换,无效日期回退为文本输入
- refs #276
2026-03-20 14:35:45 +08:00
Syngnat
da5e879409 🐛 fix(data-grid): 修复复制为INSERT/CSV/Markdown字段乱序及特殊字符未转义
- INSERT:使用 columnNames 保持 DDL 字段顺序,值中单引号转义为 ''
- CSV:使用 columnNames 保持字段顺序,值中双引号转义为 "",增加表头行
- Markdown:使用 columnNames 保持字段顺序,转义管道符和换行,增加表头行和分隔行
- refs #277
2026-03-20 14:10:23 +08:00
Syngnat
8935ad2905 🐛 fix(query-editor): 修复多Tab场景下SQL智能提示读取错误数据库上下文
- 根因:completion provider 只注册一次,闭包捕获首个 Tab 的组件 ref,切换 Tab 后仍读取旧上下文
- 修复:新增模块级共享变量,所有 QueryEditor 实例在成为活跃 Tab 时同步状态
- 共享变量:currentDb、connectionId、tables、allColumns、visibleDbs、columnsCache
- provider 闭包改为读取共享变量,确保始终使用当前活跃 Tab 的数据库上下文
- metadata 加载和数据库列表获取后同步更新共享变量
- refs #278
2026-03-20 13:59:38 +08:00
Syngnat
cd5a0e85e8 feat(data-grid): 筛选面板新增多字段排序功能并支持启用禁用
- 排序扩展:SortInfo 类型从单字段扩展为数组,SQL 和 MongoDB 均支持多字段 ORDER BY
- 筛选面板:新增排序配置区域,支持动态添加/删除多个排序字段及启用/禁用
- 表头联动:启用 Ant Design 多列排序模式,表头排序图标与筛选面板双向同步
- 增量更新:表头点击排序时在现有排序数组中增量更新,不覆盖其他字段
- 循环优化:表头排序从"升序→降序→取消"改为"升序↔降序"切换
- 布局优化:操作按钮栏增加分隔符分组,排序区域与按钮间增加视觉分隔
- refs #279
2026-03-20 13:22:10 +08:00
Syngnat
ccb9f09452 🐛 fix(store): 修复保存查询后再次打开产生重复Tab
- 新增语义去重:addTab对query类型按savedQueryId匹配已有Tab
- 匹配条件覆盖savedQueryId相同或Tab id等于savedQueryId两种场景
- 命中已有Tab时复用并激活,避免重复创建
- refs #280
2026-03-20 12:59:24 +08:00
Syngnat
5afd80c559 feat(about/update): 优化macOS下载更新交互与关于弹窗按钮布局
- 自动打开目录:macOS下载完成后根据用户是否点了"隐藏到后台"决定是否自动打开下载目录
- 文件校验兜底:打开安装目录失败时清除已下载状态,允许重新下载
- 缓存同步修复:checkForUpdates以后端downloaded字段为准,清除过期的本地ref缓存
- 关于弹窗重构:已下载状态直接显示"打开安装目录"主操作按钮,无需经下载进度中转
- 按钮互斥优化:下载中隐藏"下载更新"和"本次不再提示",显示"下载进度"
- 按钮排版调整:主操作按钮置右侧高亮,各状态下按钮层次分明
2026-03-20 12:55:16 +08:00
Syngnat
1b36f60821 🐛 fix(query-editor/data-grid): 修复只读查询结果右键菜单失效及提交事务后数据丢失
- 右键菜单修复:移除 handleContextMenu 的 editable 守卫,只读模式也能弹出右键菜单
- 非编辑单元格绑定:EditableCell 非编辑模式增加 onContextMenu 包装,确保右键事件触发
- mergedColumns 统一:所有列通过 onCell 绑定 onContextMenu,不再跳过非 editable 列
- 表名正则增强:支持多行 SQL 和 schema.table 写法,复杂 SELECT 也能提取表名获得编辑能力
- 精准重查询:新增 handleReloadResult 函数,提交事务后只用当前结果集 SQL 重查,避免整个编辑器 SQL 二次处理导致数据丢失
- refs #267
2026-03-20 12:11:09 +08:00
Syngnat
eaa76d8f04 feat(connection): 新增数据库连接图标功能并修复达梦数据库列表为空
- 图标组件:新增 DatabaseIcons.tsx,10 种品牌 SVG logo + 7 种彩色文字标签覆盖全部数据源
- 品牌资源:下载 MySQL/PG/Redis/MongoDB/ClickHouse/SQLite/MariaDB/Doris/Sphinx/DuckDB 的 SVG(CC0 许可)
- 类型扩展:SavedConnection 新增 iconType/iconColor 支持自定义图标和颜色
- 外观配置:ConnectionModal 新增"外观"配置区(图标选择器 + 14 色选择器 + 预览面板)
- 数据源选择:Step1 数据源类型卡片全部统一为品牌图标
- 达梦修复:dameng_metadata.go 增加原生 SQL 查询和诊断日志,改善数据库列表获取
- refs #114
- refs #266
2026-03-20 11:19:08 +08:00
Syngnat
0f717706b0 🐛 fix(TableOverview/DataGrid): 修复表概览重复打开Tab及隐藏列修改失效
- Tab去重:表概览 buildTableStatusSQL 对 postgres/kingbase/vastbase/highgo/sqlserver 返回 schema.table 格式表名,与侧边栏一致
- Tab ID统一:移除 openTable 中多余的 table- 前缀,使 Tab ID 格式匹配
- 语义去重:addTab 新增 connectionId+dbName+tableName 语义匹配作为安全网
- 数据修复:handleCommit 和 applyRowEditor 将 displayColumnNames 改为 columnNames,确保隐藏列修改被正确提交
- refs #264
- refs #265
2026-03-20 10:33:51 +08:00
Syngnat
8950081a6c 🐛 fix(QueryEditor): 修复多 Tab 导致 SQL 自动补全项重复的问题
- registerCompletionItemProvider 为 monaco.languages 全局 API,多 Tab 实例重复注册导致补全项成倍重复
- 添加模块级标志 sqlCompletionRegistered 确保全局只注册一次
- Provider 内部通过 ref 读取当前上下文,单次注册不影响多 Tab 的上下文感知
- refs #261
2026-03-19 21:14:11 +08:00
Syngnat
3bf8758418 ️ perf(App/handleNewQuery): 缓存 handleNewQuery 并消除 Tab ID 碰撞风险
- 将 handleNewQuery 改为 useCallback,减少 useEffect 中事件监听器的无效重绑定
- Tab ID 生成方式改为 Date.now() + 随机后缀,与项目既有模式一致
2026-03-19 20:46:46 +08:00
Syngnat
561d3810da 🐛 fix(data-grid): 修复窄表场景表头与数据列错位 2026-03-19 18:16:51 +08:00
Syngnat
18cb66b893 🐛 fix(query-editor/data-grid): 修复UPDATE影响行数为0及虚拟表Shift+滚轮横向滚动失效
- 后端修复:DBQueryMulti 包含写操作时跳过原生 QueryMulti,走逐条 Exec 路径获取 RowsAffected
- 结果展示:UPDATE/INSERT/DELETE 结果改为简洁的执行成功提示,不再展示 DataGrid 全套操作按钮
- Tab标签:写操作结果集标签改为「结果 N ✓」替代原来的行数计数
- 横向滚动:修复虚拟表守卫检查选择器不匹配(.rc-virtual-list-holder → .ant-table-tbody-virtual-holder)
- 事件处理:使用 event.isTrusted 区分合成事件,通过 applyVirtualHorizontalOffset 驱动 rc-virtual-list
- 目标检查:isTableDataAreaTarget 改为黑名单模式,兼容 rc-virtual-list 包裹元素
2026-03-19 17:13:38 +08:00
Syngnat
ab61e703b1 🐛 fix(data-grid): 修复空数据表Shift+滚轮横向滚动失效
- 目标匹配:isTableDataAreaTarget 新增 .ant-table-placeholder 选择器覆盖空表占位元素
- 虚拟回退:虚拟模式下 rc-virtual-list-holder 不存在时,回退到手动滚动表头并同步外部滚动条
- 精准匹配:仅添加 .ant-table-placeholder,避免 .ant-table-header 导致有数据表头体滚动不同步
2026-03-19 14:32:12 +08:00
Syngnat
7933b4c315 feat(window): 实现窗口尺寸位置与侧边栏宽度持久化记忆
- 窗口状态:新增 windowState 记录全屏/最大化/普通状态,关闭后重开自动恢复
- 窗口尺寸:普通窗口模式下每2秒自动保存宽高和坐标位置
- 侧边栏宽度:sidebarWidth 从 useState 迁移至 zustand store 持久化
- 状态恢复:启动时根据保存的状态决定全屏/最大化/恢复具体尺寸位置
- 数据校验:新增 sanitizeWindowBounds/sanitizeWindowState/sanitizeSidebarWidth 校验函数
- 兼容处理:startupFullscreen 设置优先级高于自动记忆的窗口状态
- refs #259
2026-03-19 12:26:44 +08:00
Syngnat
c99f857d0a feat(TableOverview): 新增表平铺视图概览功能
- 新建 TableOverview 组件:卡片网格展示表名、注释、行数、数据大小、引擎
- 数据获取:通过 DBQuery 发 SHOW TABLE STATUS 等 SQL 适配多数据库方言
- 交互功能:搜索过滤、按名称/行数/大小排序、双击打开DataGrid、Tooltip悬浮全名
- 右键菜单:与 Sidebar 完全一致(新建查询/设计表/复制结构/备份/重命名/删除/导出)
- 入口集成:双击侧边栏"表(N)"分组节点打开概览Tab,注册table-overview类型
- UI细节:统计指标固定列宽对齐,卡片hover高亮边框
2026-03-19 11:58:12 +08:00
Syngnat
2c3f4a1032 ♻️ refactor(TableDesigner): 重构选择交互为手动Checkbox并优化渲染性能
- 交互重构:移除rowSelection依赖,改用手动Checkbox列避免Ant Design内部对齐差异
- 列隔离:Checkbox和Sort列脱离resizableColumns,不经过ResizableTitle处理
- 对齐修复:.ant-input padding-left归零,消除borderless Input导致的th/td文字偏移
- 性能优化:resizableColumns/sortColumn等用useMemo稳定引用,Tab切换startTransition降级
- 动画加速:ink-bar添加will-change:transform独立合成层,过渡缩短至0.15s
2026-03-19 11:33:30 +08:00
Syngnat
72de16995a feat(TableDesigner): 索引表支持列宽拖拽和Checkbox多选全选
- 拖拽调整:索引表header支持拖拽调整列宽,带三角形角标与DataGrid一致
- 多选重构:索引选择从Radio单选改为Checkbox多选,支持全选/取消全选/半选指示
- 选择列固定:Checkbox列固定48px宽度,不参与拖拽resize,header和body对齐一致
- 按钮逻辑:编辑按钮要求恰好选中1个索引,删除按钮要求选中≥1个索引
- 样式优化:索引表header禁用文字选中和光标效果,保持干净交互体验
2026-03-19 10:25:06 +08:00
Syngnat
0adc8411fa 🐛 fix(data-grid/table-designer/about): 修复空表横向滚动、索引编辑回显及关于弹窗按钮间距
- 空表滚动:虚拟模式下空数据表缺少 virtual-holder 元素时,回退到直接滚动表头实现横向滚动
- 索引回显:修复修改索引后再次编辑时被删除的字段仍然显示的问题,selectedIndex 随 groupedIndexes 同步更新
- 按钮间距:关于弹窗 footer 增加 flex-wrap 和 gap,解决关闭按钮与上方操作按钮行重叠
- refs #258
2026-03-19 08:59:49 +08:00
杨国锋
8efa7e2de6 🔧 fix(App/handleNewQuery): 修复新建查询默认数据库选择错误
- 验证当前 tab 的 connectionId 仍存在于 connections 列表中才复用上下文
- activeContext 作为次优先回退,同样验证 connectionId 有效性
- 避免关闭连接 A 后新建查询仍默认选中数据库 A 的问题
- refs #241
2026-03-18 21:24:45 +08:00
杨国锋
ecee206304 feat(Sidebar/FindInDatabaseModal): 新增全局数据库搜索功能
- 数据库右键菜单新增「在数据库中搜索」入口
- 逐表搜索文本列,支持包含/精确匹配两种模式
- 智能过滤非文本列(int/blob/date 等自动跳过)
- 兼容 MySQL LIMIT / SQL Server TOP / Oracle FETCH FIRST
- 结果以汇总表格展示,支持展开查看匹配行详情
- refs #240
2026-03-18 21:16:23 +08:00
杨国锋
299dceb01c 🔧 fix(QueryEditor): 修复最大返回行数对 SQL Server 等数据源不生效的问题
- 启用 applyAutoLimit 在 SQL 层面自动注入行数限制
- SQL Server 使用 TOP N,Oracle/Dameng 使用 FETCH FIRST N ROWS ONLY
- 已有 LIMIT/TOP/FETCH/ROWNUM 时自动跳过,不重复注入
- 移除相关 DEBT 标记
- refs #236
2026-03-18 21:02:54 +08:00
杨国锋
5cad761bdd feat(QueryEditor): 增加 SQL 内置函数自动补全提示
- 新增约 120 个常用函数(聚合/字符串/日期/JSON/窗口等分类)
- 以 Function 图标区分,选中自动插入括号
- 适用于所有支持的数据源类型
- refs #248
2026-03-18 20:53:50 +08:00
杨国锋
b8728170ec 🐛 fix(CreateDatabase): 修复 Oracle 新建数据库时因缺少 Service Name 报错
- 前端 Oracle/达梦连接保留原始 database 字段而非清空
- 后端添加 Oracle/达梦不支持此入口创建的友好提示
- refs #223
2026-03-18 20:45:07 +08:00
杨国锋
4ce4cdaad8 🐛 fix(TableDesigner): 修复 MySQL 索引编辑保存时多语句执行失败
- executeSchemaSql 将拼接的 DDL 按分号换行拆分后逐条执行
2026-03-18 20:32:00 +08:00
杨国锋
cc7ef12029 🐛 fix(TableDesigner): 修复深色主题下 SQL 变更确认弹窗文字不可见
- 将 <pre> 的硬编码浅色背景/边框替换为 darkMode 适配的颜色值
- refs #251
2026-03-18 20:23:38 +08:00
杨国锋
5b6403f266 🐛 fix(update): 修复 Win10 自动更新时文件被占用导致替换失败
- 冷却期:进程退出后增加 3 秒等待,确保 Win10 内核释放 exe 文件句柄
- 替换策略:新增 rename-before-replace 机制,先重命名旧文件再复制新文件
- 退避重试:替换固定 1 秒间隔为指数退避(1s→2s→3s→5s),总等待约 36 秒
- 残留清理:替换成功后删除 .old 残留文件
- 测试覆盖:新增 TestBuildWindowsScriptWin10Fixes 验证全部修复点
2026-03-18 20:16:09 +08:00
Syngnat
caceb2868d 🐛 fix(data-grid): 修复右键菜单被窗口裁剪和全选checkbox未对齐
- 单元格右键菜单增加视口边界检测,底部/右侧空间不足时自动偏移
- 菜单容器添加 maxHeight + overflowY auto,确保所有选项可滚动访问
- 修复表头选择列 TH 无 class(虚拟模式),用 :first-child 统一 padding 和对齐
- 行右键菜单 Dropdown 挂载到 document.body 并启用 autoAdjustOverflow
2026-03-18 18:01:29 +08:00
Syngnat
e7b9ff4a10 ♻️ refactor(data-grid): 优化右键菜单定位算法与工具栏按钮优先级
- 单元格菜单 position:fixed 增加 viewport 边界碰撞检测与动态 maxHeight
- 行菜单 Dropdown 通过 getPopupContainer 脱离容器 overflow 限制
- 工具栏按钮按使用频率重排:刷新 → 筛选 → [编辑区] → 导入/导出
2026-03-18 17:43:10 +08:00
Syngnat
76f65cb96c 🐛 fix(ci): 修复 Chocolatey UPX 包不可用导致 Windows 构建失败
- Chocolatey NuGet 仓库无法解析 upx 包,触发 NuGetResolverInputException
- 改为 Invoke-WebRequest 从 GitHub Releases 下载 upx-4.2.4-win64.zip
- 使用 GITHUB_PATH 环境文件注入 UPX 路径,后续步骤可直接调用
- 消除对 Chocolatey 包注册表的外部依赖,提高 CI 稳定性
2026-03-18 17:29:24 +08:00
Syngnat
8bdc6e8086 ️ perf(data-grid): memoize CSS模板与样式变量,优化render热路径
- P0:gridCssText useMemo化,依赖[themeStyles, gridId, tableBodyBottomPadding]
- P1:80+行主题变量收敛到themeStyles useMemo,依赖[darkMode, opacity, blur]
- P2:CELL_ELLIPSIS_STYLE/VIRTUAL_CELL_WRAPPER_STYLE提升为模块级常量
- P5:合并getBoundingClientRect为单次调用,减少强制重排
- P6:useRef替代displayColumnNames依赖,切断normalizeGridFilterConditions级联
2026-03-18 17:20:53 +08:00
Syngnat
1eb2f6dffe 🐛 fix(data-grid): 修复虚拟表格横向滚动时标头与数据列错位
- 废弃 marginLeft hack,改用合成 WheelEvent 驱动 rc-virtual-list 内部滚动状态
- 虚拟模式 wheel 事件交由 rc-virtual-list 原生处理,rc-table 自动同步 header
- 外部滚动条同步改为 WheelEvent + rAF 异步链路
- refs #249
2026-03-18 16:13:40 +08:00
Syngnat
5c5e1fc68f 🐛 fix(redis,mongodb): 修复Redis集群数据显示不全和MongoDB指定数据库连接失败
- Redis集群ScanKeys改用ForEachMaster逐master节点扫描合并去重
- MongoDB authSource未显式设置时始终默认admin而非Database值
- 同步修复mongodb_impl.go和mongodb_impl_v1.go
- refs #246
2026-03-18 15:51:19 +08:00
Syngnat
fb70f1420c feat(sql-file): 支持大 SQL 文件后端流式执行,解决 WebView2 崩溃
- 新增流式 SQL 拆分器 sql_split_stream.go(逐行状态机)
- OpenSQLFile 超过 50MB 返回文件路径而非内容
- 新增 ExecuteSQLFile 后端流式读取+拆分+逐条执行+事件推送进度
- 新增 CancelSQLFileExecution 支持中途取消
- 前端增加 SQL 文件执行进度 Modal(进度条/计数/取消/结果展示)
- refs #238
2026-03-18 15:33:37 +08:00
Syngnat
d75596921c feat(sidebar): 增加已保存查询删除功能并扩展运行外部SQL文件入口
- 已存查询节点增加右键菜单:打开查询 / 删除查询(含确认弹窗)
- 连接节点右键菜单增加"运行外部SQL文件"入口
- 侧栏工具栏增加"运行外部SQL文件"按钮,使用当前活跃连接上下文
- 统一所有 SQL 文件相关文案为"运行外部SQL文件"
- 表头字段备注过长溢出修复(overflow: hidden)
- 列宽调整与拖拽排序隔离(onPointerDown stopPropagation + isResizingRef guard)
- refs #247
2026-03-18 15:01:56 +08:00
Syngnat
d251594fd9 🐛 fix(data-grid): 修复字段备注过长时溢出重叠到相邻列的问题
- 为 .gonavi-sortable-header-cell 添加 overflow: hidden 限制 th 溢出
- 为 .sortable-header-cell-drag-handle 添加 overflow: hidden 限制内容溢出
- 配合已有 text-overflow: ellipsis 实现长文本截断显示
- 完整备注仍可通过 Tooltip 悬浮查看
- refs #239
2026-03-18 14:47:40 +08:00
Syngnat
7598bf372b 🐛 fix(data-grid): 修复拖拽调整列宽时意外触发列排序拖拽的问题
- ResizableTitle 的 resize handle 增加 onPointerDown stopPropagation
- 阻止 pointerdown 事件冒泡到 @dnd-kit 的 PointerSensor
- handleDragEnd 增加 isResizingRef 防御性检查,双重保险
- 确保列宽调整与列排序两个操作完全隔离
2026-03-18 14:43:26 +08:00
Syngnat
64021ffd2a 🐛 fix(batch-truncate/query): 修复批量清空表安全隐患并优化多语句执行错误反馈
- 安全加固:TruncateTables 增加审计日志(Warnf 级别)和参数校验(上限 200 张)
- 容错增强:批量清空部分失败时返回已执行 SQL 列表并提示已清空表不可恢复
- 错误优化:DBQueryMulti 逐条执行失败时附带语句序号和已成功条数
- 性能优化:splitSQLStatements 从 string 拼接改为 strings.Builder,消除 O(n²) 分配
- 转义修复:splitSQLStatements 支持 SQL 标准转义单引号 '' 防止误拆分
- 前端修复:handleBatchClear 统一取消判断字符串为 '已取消' 并移除冗余变量声明
- refs #244
2026-03-18 14:32:11 +08:00
Syngnat
fbd785400f 批量表操作支持清空数据 (#253)
批量表操作支持清空数据. 
已支持数据库 mysql / mongodb
2026-03-18 14:16:24 +08:00
wjm
b573fd95cc 接口漏提了 2026-03-18 11:18:51 +08:00
wjm
a097d96380 批量表操作支持清空数据. mysql / mongodb 2026-03-18 11:05:44 +08:00
杨国锋
6ee0fea110 feat(multi-query): 适配 MariaDB/SQLServer/DiROS 多结果集并增加回退提示
- MariaDB: DSN 添加 multiStatements=true,实现 QueryMulti/QueryMultiContext
- SQL Server: 实现 QueryMulti/QueryMultiContext(go-mssqldb 原生支持批处理)
- DiROS: DSN 添加 multiStatements=true(继承 MySQLDB 的方法)
- Sphinx: 自动继承 MySQLDB 多结果集支持,无需额外改动
- 不支持原生多语句的数据源执行多条 SQL 时,前端展示 info 提示
- refs #235
2026-03-17 22:53:24 +08:00
杨国锋
e6b822c967 ♻️ refactor(QueryEditor): 清理未使用变量及IDE警告
- 移除未使用的 DBQuery 导入和 currentQueryId 解构
- 简化正则表达式 [\w] 为 \w(4处)
- 移除变量初始值冗余和 nextKey 中间变量
- 为异步调用添加 void 前缀消除 Promise 忽略警告
- 为暂未使用的 getLeadingKeyword/applyAutoLimit 添加 DEBT 标记
2026-03-17 22:39:55 +08:00
杨国锋
0ab10d2e80 feat(query): 支持多条SQL语句执行返回多结果集
- 新增 ResultSetData 结构体承载单个结果集数据
- 新增 MultiResultQuerier/MultiResultQuerierContext 可选接口
- 新增 scanMultiRows 函数利用 NextResultSet() 遍历所有结果集
- MySQL 驱动 DSN 开启 multiStatements=true 并实现多结果集接口
- 新增 DBQueryMulti Wails 方法,支持驱动原生多结果集及自动回退逐条执行
- 新增 Go 版 SQL 拆分函数 splitSQLStatements 及 10 个单元测试
- 前端 QueryEditor handleRun 改为一次性调用 DBQueryMulti
- MongoDB 保持独立的逐条执行路径不受影响
- refs #235
2026-03-17 22:21:49 +08:00
杨国锋
064cdc34be ♻️ refactor(全局): 统一错误消息中文化,补充 godoc 与测试,修复横向滚动和 Vite 代理
错误消息中文化:
- 19 个驱动实现文件中 connection not open / table name required 等英文消息替换为中文
- methods_file.go / methods_db.go / methods_driver.go 英文消息中文化
- 前端 App/Sidebar/DataGrid/ConnectionModal/DriverManagerModal 同步替换 "Cancelled" → "已取消"

文档与测试:
- database.go Database/BatchApplier 接口、types.go 12 个类型、driver_support.go 导出函数补充中文 godoc
- 新增 logger_test.go(ErrorChain 5 个用例)和 methods_db_conn_test.go(连接管理 7 个用例)

Bug 修复:
- DataGrid: 将 liveTargets 空检查移至非虚拟路径,修复外部横向滚动条拖动时内容不跟随
- vite.config.ts: server.host 指定 127.0.0.1,修复 IPv6 回环被拦截导致 Wails 代理 502

基础设施:
- .gitignore 新增 .gemini/ 规则,tmpclaude-* 改为 **/tmpclaude-* 覆盖子目录
2026-03-17 21:44:50 +08:00
Syngnat
c62f4b7d3c ♻️ refactor(query-editor/sidebar): 优化查询新建保存链路与 SQL 补全排序体验
- 表右键“新建查询”默认填充 SQL 模板为 SELECT * FROM <table>;
- Query Tab 增加 savedQueryId,首次保存后将“新建查询”转为已保存查询形态
- 保存按钮改为快速保存:已保存查询直接覆盖保存,不再弹命名对话框
- 首次保存(无保存身份)时保留命名弹窗,保存后同步更新当前 tab 标题与 query
- 已保存查询从侧栏打开时携带 savedQueryId,保持保存链路一致
- SQL 自动补全增加前缀过滤与上下文分组排序,关键字在非表名上下文下优先展示
- refs #232
2026-03-13 17:20:33 +08:00
Syngnat
304a4926d2 🔧 fix(query-editor/sidebar): 修复全选错位并完善侧栏拖拽自适应
- 修复新建查询页 Ctrl/Cmd+A 命中空白区域的问题,改为强制触发 Monaco 全选
- 限制全选拦截仅作用于当前活动查询页,避免影响其他编辑区
- 修复 sidebarWidth 声明时序导致的运行时 ReferenceError
- 侧栏拖窄时工具按钮按宽度分档自适应(4列→2列→图标模式)
- 新建查询/新建连接在窄宽度下改为单列,避免图文重叠
- 补充按钮防溢出样式,保证窄宽度可读与可点击
2026-03-13 16:22:33 +08:00
Syngnat
cabf84a041 🔧 fix(frontend/ci): 移除前端测试对 node:assert 的类型依赖
- 修复 darwin/arm64 构建中 tsc 无法解析 node:assert 的 TS2307 报错
- 将 dataGridLayout.test.ts 中的 node:assert 替换为本地 assertEqual
- 将 redisViewerWorkbenchTheme.test.ts 中的 node:assert 替换为本地断言函数
- 将 overlayWorkbenchTheme.test.ts 中的 node:assert 替换为本地断言函数
- 保持原有断言语义不变,避免引入新的运行时依赖
- 本地验证 npm --prefix frontend run build 通过
2026-03-13 15:36:09 +08:00
Syngnat
9b02720169 🔧 fix(data-grid): 修复虚拟表格滚动条遮挡并统一横向同步链路
- 修复数据视图横向滚动条遮挡最后一行内容的问题
- 为虚拟表格接入外部横向滚动条,移除内部重复横向滚动轨道
- 统一拖拽滚动条与鼠标滑轮的横向同步逻辑,修复内容移动但滚动条不跟随
- 调整横向滚动条底部停靠间距,避免继续压住表格内容
- 提升纵向滚动条 thumb 对比度并增加弱轨道底色,改善深色主题下可见性
- 新增 DataGrid 布局计算辅助函数与最小测试用例
- refs #220
2026-03-13 15:27:18 +08:00
Syngnat
eb36dcc5a2 🔧 fix(redis/ui): 统一 Redis 工作台交互样式并修复 Tree 节点异常高亮
- Redis 页面重构为工作台样式,统一左右面板、工具条和详情区层级
- 接入 light/dark/透明模式主题参数,修复 Redis 页面与全局主题不一致问题
- 新增文件夹递归勾选、全选全部、分组全选/取消全选能力
- 支持 Redis Key 右键菜单重命名并同步更新树节点、选中态和详情面板
- 修复 type=none 时读取失败问题,过期或已删除 Key 自动提示并移出列表
- 接管 Redis Tree 展开箭头渲染,修复 switcher 命中区错位和悬浮白线问题
- 统一工具、代理、主题、关于、筛选、新建组和新建连接等弹层主题
- refs #231
2026-03-13 14:51:20 +08:00
Syngnat
1a3f137438 🔧 fix(db/kingbase): 统一 search_path 构建并修复双引号重复转义
- 新增 buildKingbaseSearchPathCommon,统一 search_path 规范化与拼装逻辑
- schema 名称先做 normalize + 去重,避免已带引号值被二次转义为 ""schema""
- getSearchPathStr 改为收集原始 schema 后走公共构建流程
- optional-driver-agent 复用同一构建函数,消除两套实现偏差
- 对 public 做大小写归一,确保 search_path 输出稳定
- 新增 TestBuildKingbaseSearchPathCommon 覆盖 quoted/escaped/dedupe 场景
2026-03-13 11:22:35 +08:00
杨国锋
5f94cd3911 🔧 fix(tab-manager): 修复切换Tab导致表数据编辑与筛选状态丢失
- 移除非活动业务Tab内容置空逻辑,避免DataViewer/DataGrid卸载重建
- 设置 destroyInactiveTabPane=false,确保切换Tab不销毁页面
- 在 DataViewer 统一快照持久化并增加卸载兜底写回
- 保持切换Tab不自动刷新,仅手动刷新或显式状态变化触发加载
- refs #218
2026-03-12 23:25:32 +08:00
杨国锋
bb257c35bc feat(data-grid): 新增同表多列跨行复制粘贴能力
- 在单元格编辑模式新增复制缓冲区,保存源行与多列值
- 新增“复制选区列值”操作,仅允许同一行多列选区复制
- 新增“粘贴到选中行”操作,按同名列批量写入并自动排除源行
- 复用 addedRows/modifiedRows 变更路径,保持提交事务与回滚逻辑一致
- 单元格右键菜单增加“粘贴已复制列(同名列)”入口
- 切换连接/库/表时自动清空复制缓冲区,避免跨上下文误粘贴
- refs #217
2026-03-12 23:14:52 +08:00
杨国锋
1dabac1a65 🔧 fix(window): 修复Windows启动全屏锁死并补齐标题栏退出全屏逻辑 2026-03-12 19:38:54 +08:00
杨国锋
e013288967 🔧 fix(ci/release-winget): 修复 Node20 弃用告警并强制启用 Node24 运行时
- 在 release-winget workflow 增加 FORCE_JAVASCRIPT_ACTIONS_TO_NODE24=true
- 与现有 release/test workflow 的 Node24 配置保持一致
- 避免 actions/checkout、setup-go、setup-node 触发 Node20 弃用告警
2026-03-12 19:23:46 +08:00
杨国锋
d467322ebe 🔧 fix(release/macos): 移除 macOS 打包链路的 UPX 压缩逻辑
- 删除 release 与手动测试工作流中的 macOS UPX 安装与压缩步骤
- build-release.sh 不再对 macOS arm64/amd64 主程序执行 UPX
- 保留 Windows 与 Linux 的 UPX 压缩策略
2026-03-12 19:00:21 +08:00
Syngnat
e26a456eae 🔧 fix(release/ci): 修复跨平台UPX兼容并处理Windows ARM64打包失败
- CI 工作流统一启用 Node24 JavaScript 运行时,消除 Node20 退役告警干扰
- macOS 打包阶段为 UPX 增加 --force-macos,修复 Mach-O 压缩失败
- Windows 打包按架构分流:arm64 跳过 UPX 并保留原始 EXE,amd64 继续强制压缩
- Windows 压缩流程新增 $LASTEXITCODE 显式校验,避免命令失败被误判为成功
- 本地 build-release.sh 同步 macOS/Windows 的 UPX 兼容策略与错误处理逻辑
2026-03-12 17:54:09 +08:00
Syngnat
501ad9e9a3 Merge branch 'fix/ssh-issue-20260310-ygf' into dev
# Conflicts:
#	internal/db/kingbase_impl.go
2026-03-12 17:30:48 +08:00
Syngnat
482a7fce2e 🔧 fix(release/sidebar): 统一跨平台UPX压缩并修复PG函数列表查询兼容性
- 构建脚本新增通用 UPX 压缩函数,覆盖 macOS、Linux、Windows 产物
- 本地打包改为强制压缩策略:未安装 upx、压缩失败或校验失败直接终止
- macOS 打包在签名前压缩 .app 主程序并执行 upx -t 校验
- Linux 打包在生成 tar.gz 前压缩可执行文件并执行 upx -t 校验
- GitHub Release 与测试构建流程补齐 macOS/Linux/Windows 的 upx 安装与压缩步骤
- PostgreSQL/PG-like 函数元数据查询增加多路兼容 SQL,修复函数列表不显示问题
- refs #221
- refs #222
2026-03-12 17:30:16 +08:00
Syngnat
e6af5f966b 🔧 fix(driver/kingbase,mongodb): 修复外置驱动事务引用与连接测试链路问题
- 金仓外置驱动链路增加表名与变更字段归一化,修复 ApplyChanges 场景下双引号转义异常导致的 SQL 语法错误
- 新增金仓公共标识符工具并复用到 kingbase_impl 与 optional_driver_agent_impl,统一处理多重转义、schema.table 拆分与引用规范
- 金仓代理连接后自动探测并设置 search_path,降低查询时必须手写 schema 前缀的概率
- MongoDB 连接参数改为显式 host/hosts 优先,避免被 URI 中 localhost 覆盖;代理链路保留目标地址不再改写为本地地址
- 连接测试增加前后端超时收敛与日志增强,避免长时间转圈;连接错误文案在未启用 TLS 时移除误导性的“SSL”前缀
- 统一日志级别为 INFO/WARN/ERROR,默认日志目录收敛到 ~/.GoNavi/Logs,并补充驱动构建脚本 build-driver-agents.sh
2026-03-12 16:45:46 +08:00
凌封
eef973b7fc fix: KingBase 连接后自动设置 search_path,修复自定义 schema 下表查询报 relation does not exist 的问题 (#215) 2026-03-12 10:04:49 +08:00
Syngnat
d8b6b4ef8d 🔧 fix(release,ssh): 修复 SSH 误判连接成功并纠正 DMG 打包结构
- SSH 缓存 key 纳入认证指纹(password/keyPath),避免改错凭证仍复用旧连接/端口转发
- MySQL/MariaDB/Doris:SSH 隧道建立失败直接返回错误,不再回退直连导致测试误判成功
- 新增最小单测覆盖 SSH cache key 与 UseSSH 异常路径
- build-release.sh:create-dmg 使用 staging 目录作为 source,避免 DMG 根目录变成 Contents
- refs #213
2026-03-11 14:36:36 +08:00
Syngnat
4d58cc6e26 🐛 fix(connection/redis): 修复 Redis URI 用户名处理导致认证失败
- Redis URI 解析回填 user 字段,兼容 redis://user:pass@... 与 redis://:pass@...
- 生成 URI 时按需输出 user/password,避免丢失用户名信息
- Redis 类型默认用户名置空,并在构建配置时清理历史默认 root
- 避免 go-redis 触发 ACL AUTH(user, pass) 导致 WRONGPASS
- refs #212
2026-03-11 14:04:37 +08:00
Syngnat
b0bdddad9b 🔧 fix(release,db/kingbase_impl): 修复金仓默认 schema 并静默生成 DMG
- Kingbase:在 current_schema() 为 public 时探测候选 schema,并通过 DSN search_path 重连,兼容未限定 schema 的查询
- 候选优先级:数据库名/用户名同名 schema(存在性校验),否则仅在“唯一用户 schema 有表”场景兜底
- 避免连接污染:每次 Connect 重置探测结果,重连成功后替换连接并关闭旧连接
- 打包脚本:create-dmg 增加 --sandbox-safe,避免构建时自动弹出/打开挂载窗口
- 产物格式:强制 --format UDZO,并将 rw.*.dmg/UDRW 中间产物转换为可分发 DMG
- 校验门禁:增加 hdiutil verify,失败时保留 .app 便于排查,同时修正卷图标探测并补 ad-hoc 签名
2026-03-11 13:39:41 +08:00
Syngnat
a73ca36a32 🔧 fix(db/kingbase_impl): 修复标识符无条件加双引号导致SQL语法报错
- quoteKingbaseIdent 改为条件引用,仅对大写字母、保留字、特殊字符的标识符添加双引号
- 新增 kingbaseIdentNeedsQuote 判断标识符是否需要引用
- 新增 isKingbaseReservedWord 检测常见SQL保留字
- 补充 TestQuoteKingbaseIdent、TestKingbaseIdentNeedsQuote 单测覆盖各场景
- refs #176
2026-03-11 10:23:41 +08:00
Syngnat
92e9381fcc 🎨 style(DataGrid): 清理冗余代码与静态分析告警
- 类型重构:通过修正 React Context 的函数签名解决了 void 类型的链式调用错误
- 代码精简:利用 Nullish Coalescing (??) 优化组件配置项降级逻辑,剥离无意义的隐式 undefined 赋值
- 工具链适配:适配 IDE 拼写检查与 Promise strict rules,确保全文件零警
2026-03-11 09:19:49 +08:00
Syngnat
c4c7e379d1 feat(DataGrid): 增加表格列的动态显示与隐藏控制
- 字段面板新增列可见性筛选,支持列表内快速搜索、按需勾选与一键重置
- 新增持久化状态,自动记忆每张数据表的个性化隐藏列配置
- 优化数据提交链路,确保列的隐藏仅影响视图交互,不干扰增删改及复制功能
2026-03-10 16:45:35 +08:00
Syngnat
695713c779 feat(DataGrid): 实现数据视图列标题拖拽排序及顺序记忆
- 功能集成:接入 @dnd-kit 实现表头水平拖拽排序,支持多列位置灵活调整
- 持久化:Store 新增 tableColumnOrders 状态,支持按“连接-库-表”多维度记忆自定义列序
- 交互优化:重构表头 DOM 结构并消除内边距,实现“悬停手型、按住抓取”的精准指针反馈
- 性能提升:通过 React.memo 减少重渲染,并启用 will-change 硬件加速确保 60FPS 流畅度
- 稳定性:增强 Wails 环境接口调用的异常捕获,并补全前端独立开发环境下的 API Stub
2026-03-10 15:49:22 +08:00
Syngnat
ca49b37dc7 🔧 fix(DataGrid): 默认开启虚拟滚动并修复多选单元格高亮失效问题
- 移除根据数据量和列数动态判断是否开启虚拟滚动的阈值限制,改为在表格视图下默认全量开启,彻底解决卡顿问题
- 修复 `updateCellSelection` 在查找坐标节点时硬编码 `td` 选择器的问题,改为精确匹配 `.ant-table-cell`,兼容虚拟滚动时的 `div` 渲染模式
- 修复因透明窗口特性导致的 `transparent !important` 把高亮样式强行覆盖的问题,拔高了多选状态下背景与边框 CSS 的优先级
- 解决单元格内外多重属性嵌套导致的高亮右侧留白现象,使得高亮框完全贴合表格单元格边缘
- 适配主题色响应(暗黑模式使用黄色深色高亮,白昼模式使用默认蓝色高亮)
2026-03-10 11:17:03 +08:00
Syngnat
c8c0c5f20a feat(DataGrid): 统一表格右键菜单交互体验
- 彻底移除功能较少的行级右键菜单 ContextMenuRow,统一使用功能更丰富的单元格右键菜单
- 优化虚拟滚动模式和只读模式下的渲染,支持触发单元格右键菜单
- 菜单展示自适应:在只读或不可修改数据的场景下自动隐藏「设置为 NULL」与「填充到选中行」等编辑项
- refs #209
2026-03-10 10:58:27 +08:00
Syngnat
d61d7ec39b 🐛 fix(sqlserver): 修复 SQL Server 查看表数据时分页语法和标识符引用错误
- quoteIdentPart 缺少 sqlserver 分支,标识符使用双引号而非 [bracket]
- buildPaginatedSelectSQL 增加 mssql 别名兜底,避免 dbType 变体导致走 default 分支
- 修复后标识符使用 [bracket],分页使用 OFFSET FETCH NEXT 语法
- refs #204
2026-03-10 10:50:16 +08:00
Syngnat
e964c8ecf8 🐛 fix(DataGrid): 修复虚拟滚动模式下右键菜单失效
- 行级和单元格级右键菜单的启用条件互斥,虚拟滚动模式下两者同时失效
- enableLargeResultOptimizedEditing 关闭了内联编辑但未回退启用行级菜单
- 修改 useContextMenuRow 和 enableRowContextMenu 条件,虚拟模式下启用行级菜单
- 更新 dataContextValue 的 useMemo 依赖数组
- refs #209
2026-03-10 10:42:34 +08:00
Syngnat
7644462180 🐛 fix(mongodb): 修复单机模式连接副本集实例时地址被替换为内网地址
- getURI 在 topology=single 时未设置 directConnection=true
- 驱动连接目标地址后自动跟随副本集成员发现,切换到 localhost:27017
- 在 mongodb_impl.go 和 mongodb_impl_v1.go 中添加 directConnection=true
- 仅在 topology 非 replica、无 replicaSet、非 SRV 时生效
- refs #205
2026-03-10 10:32:31 +08:00
Syngnat
3bd02e2e09 🐛 fix(connection): 修复新建连接时标签切换导致表单数据丢失
- 在 SSH 标签页测试连接时,基础信息的 host 回退为默认值 localhost
- 在基础信息标签页保存时,SSH 配置丢失
- 保存结果仅包含当前选中标签页的字段
- refs #208
2026-03-10 10:27:13 +08:00
Syngnat
0daf702d25 feat(data-sync): 扩展跨库迁移链路并优化数据同步交互
- 统一同库同步与跨库迁移入口,补充模式区分与风险提示
- 扩展 ClickHouse 与 PG-like 双向迁移,并新增 PG-like、ClickHouse、TDengine 到 MongoDB 的迁移路由
- 完善 TDengine 目标端建表规划、回归测试与需求追踪文档
- refs #51
2026-03-09 17:22:26 +08:00
Syngnat
058c74e49a 🐛 fix(dameng): 修复达梦连接成功后数据库列表为空问题
- 调整达梦数据库列表获取策略,优先回退查询当前 schema 与当前用户
- 保留可见用户与 owner 聚合逻辑,兼容低权限账号场景
- 补充前端空列表提示与后端单元测试,降低排查成本
- close #203
2026-03-09 11:02:00 +08:00
杨国锋
b85c7529ec feat(datasource): 支持 DuckDB Parquet 文件模式并优化弹窗打开链路
- 统一 DuckDB 文件库与 Parquet 文件接入能力
- 补充 URI、文件选择、只读挂载与连接缓存键处理
- 去掉数据源卡片点击前的同步驱动查询,修复打开卡顿
- refs #166
2026-03-08 18:42:27 +08:00
杨国锋
e521d2125f feat(datasource): 支持 DuckDB Parquet 文件模式并优化弹窗打开链路
- 统一 DuckDB 文件库与 Parquet 文件接入能力
- 补充 URI、文件选择、只读挂载与连接缓存键处理
- 去掉数据源卡片点击前的同步驱动查询,修复打开卡顿
2026-03-08 18:41:05 +08:00
辣条
450fdfa59e 🐛 fix(oracle-query): 修复 Oracle 表数据分页 SQL 兼容问题 refs #196 (#202) 2026-03-08 00:42:48 +08:00
TSS
c87b15b22a feat: 统一筛选条件逻辑按钮宽度 (#201) 2026-03-07 21:45:26 +08:00
Syngnat
797ba27d20 Merge remote-tracking branch 'origin/main' into dev
# Conflicts:
#	.github/workflows/test-build-all-platforms.yml
#	frontend/src/components/ConnectionModal.tsx
#	internal/db/query_value.go
#	internal/db/query_value_test.go
2026-03-07 17:10:17 +08:00
Syngnat
ed1f40e04a ♻️ refactor(frontend-sync): 优化桌面交互细节并移除 main 回灌 dev 自动化
- 优化新建连接、主题设置、侧边栏工具区与 SQL 日志的界面表现
- 调整分页、筛选、透明模式与弹窗样式,统一整体交互层次
- 收口外观参数生效逻辑并补齐多组件适配
- 删除 sync-main-to-dev 工作流并同步维护者手动回灌说明
2026-03-07 17:01:49 +08:00
辣条
2b190e564f feat(multi-db,query,ci): 增强多数据源兼容性、查询体验与全平台测试构建流程 (#197)
* feat(http-tunnel): 支持独立 HTTP 隧道连接并覆盖多数据源

refs #168

* fix(kingbase-data-grid): 修复金仓打开表卡顿并降低对象渲染开销

refs #178

* fix(kingbase-transaction): 修复金仓事务提交重复引号导致语法错误

refs #176

* fix(driver-agent): 修复老版本 Win10 升级后金仓驱动代理启动失败

refs #177

* chore(ci): 新增手动触发的 macOS 测试构建工作流

* chore(ci): 允许测试工作流在当前分支自动触发

* fix(query-editor): 修复 SQL 编辑中光标随机跳到末尾 refs #185

* feat(data-sync): 增加差异 SQL 预览能力便于审核 refs #174

* fix(clickhouse-connect): 自动识别并回退 HTTP/Native 协议连接 refs #181

* fix(oracle-metadata): 修复视图与函数加载按 schema 过滤异常 refs #155

* fix(dameng-databases): 修复显示全部库时数据库列表不完整 refs #154

* fix(connection,db-list): 统一处理空列表返回并修复达梦连接测试报错 refs #157

* fix(kingbase): 补齐主键识别并优化宽表卡顿 refs #176 refs #178

* fix(query-execution): 支持带前置注释的读查询结果识别

* chore(ci): 新增全平台测试包手动构建工作流

* fix(ci): 修复全平台测试包 artifact 命名冲突

* fix(data-viewer): 保持切换标签后的表格滚动位置

* fix(datetime-display): 修复零日期显示被错误转换 refs #189

* fix(window-scale): 修复任务栏切换后字体异常放大 refs #193

* fix(data-grid-scroll): 修复数据区触摸板横向滚动失效 refs #175

* fix(db-query-value): 清理 query_value 合并冲突并保持零日期处理

* chore(ci): 删除旧的 macOS 单平台测试工作流
2026-03-07 13:40:50 +08:00
github-actions[bot]
1c050aefd0 🔁 chore(sync): 回灌 main 到 dev (#195)
* - feat(connection,metadata,kingbase): 增强多数据源连接能力并修复金仓/达梦/Oracle/ClickHouse兼容性问题 (#188)

* feat(http-tunnel): 支持独立 HTTP 隧道连接并覆盖多数据源

refs #168

* fix(kingbase-data-grid): 修复金仓打开表卡顿并降低对象渲染开销

refs #178

* fix(kingbase-transaction): 修复金仓事务提交重复引号导致语法错误

refs #176

* fix(driver-agent): 修复老版本 Win10 升级后金仓驱动代理启动失败

refs #177

* chore(ci): 新增手动触发的 macOS 测试构建工作流

* chore(ci): 允许测试工作流在当前分支自动触发

* fix(query-editor): 修复 SQL 编辑中光标随机跳到末尾 refs #185

* feat(data-sync): 增加差异 SQL 预览能力便于审核 refs #174

* fix(clickhouse-connect): 自动识别并回退 HTTP/Native 协议连接 refs #181

* fix(oracle-metadata): 修复视图与函数加载按 schema 过滤异常 refs #155

* fix(dameng-databases): 修复显示全部库时数据库列表不完整 refs #154

* fix(connection,db-list): 统一处理空列表返回并修复达梦连接测试报错 refs #157

* Release/0.5.3 (#191)

* - chore(ci): 新增全平台测试包手动构建工作流 tianqijiuyun-latiao 今天 下午4:26 (#194)

* feat(http-tunnel): 支持独立 HTTP 隧道连接并覆盖多数据源

refs #168

* fix(kingbase-data-grid): 修复金仓打开表卡顿并降低对象渲染开销

refs #178

* fix(kingbase-transaction): 修复金仓事务提交重复引号导致语法错误

refs #176

* fix(driver-agent): 修复老版本 Win10 升级后金仓驱动代理启动失败

refs #177

* chore(ci): 新增手动触发的 macOS 测试构建工作流

* chore(ci): 允许测试工作流在当前分支自动触发

* fix(query-editor): 修复 SQL 编辑中光标随机跳到末尾 refs #185

* feat(data-sync): 增加差异 SQL 预览能力便于审核 refs #174

* fix(clickhouse-connect): 自动识别并回退 HTTP/Native 协议连接 refs #181

* fix(oracle-metadata): 修复视图与函数加载按 schema 过滤异常 refs #155

* fix(dameng-databases): 修复显示全部库时数据库列表不完整 refs #154

* fix(connection,db-list): 统一处理空列表返回并修复达梦连接测试报错 refs #157

* fix(kingbase): 补齐主键识别并优化宽表卡顿 refs #176 refs #178

* fix(query-execution): 支持带前置注释的读查询结果识别

* chore(ci): 新增全平台测试包手动构建工作流

---------

Co-authored-by: 辣条 <69459608+tianqijiuyun-latiao@users.noreply.github.com>
Co-authored-by: Syngnat <92659908+Syngnat@users.noreply.github.com>
2026-03-06 17:36:28 +08:00
辣条
75a5a322e0 - chore(ci): 新增全平台测试包手动构建工作流 tianqijiuyun-latiao 今天 下午4:26 (#194)
* feat(http-tunnel): 支持独立 HTTP 隧道连接并覆盖多数据源

refs #168

* fix(kingbase-data-grid): 修复金仓打开表卡顿并降低对象渲染开销

refs #178

* fix(kingbase-transaction): 修复金仓事务提交重复引号导致语法错误

refs #176

* fix(driver-agent): 修复老版本 Win10 升级后金仓驱动代理启动失败

refs #177

* chore(ci): 新增手动触发的 macOS 测试构建工作流

* chore(ci): 允许测试工作流在当前分支自动触发

* fix(query-editor): 修复 SQL 编辑中光标随机跳到末尾 refs #185

* feat(data-sync): 增加差异 SQL 预览能力便于审核 refs #174

* fix(clickhouse-connect): 自动识别并回退 HTTP/Native 协议连接 refs #181

* fix(oracle-metadata): 修复视图与函数加载按 schema 过滤异常 refs #155

* fix(dameng-databases): 修复显示全部库时数据库列表不完整 refs #154

* fix(connection,db-list): 统一处理空列表返回并修复达梦连接测试报错 refs #157

* fix(kingbase): 补齐主键识别并优化宽表卡顿 refs #176 refs #178

* fix(query-execution): 支持带前置注释的读查询结果识别

* chore(ci): 新增全平台测试包手动构建工作流
2026-03-06 17:32:14 +08:00
Syngnat
61d6197fe3 Merge branch 'fix/editor-sql-error-20260306-ygf' into dev 2026-03-06 14:57:06 +08:00
Syngnat
6157161293 🐛 fix(branch-sync): 修复 main 回灌 dev 时 mergeable 异步计算导致漏开自动合并
- 增加 mergeable 状态轮询,避免新建同步 PR 后立即返回 UNKNOWN
- 在合并状态未稳定时输出中文告警与执行摘要
- 保持冲突分支、待计算分支与自动合并分支的处理路径清晰
2026-03-06 14:56:43 +08:00
github-actions[bot]
0f843a7dcf 🔁 chore(sync): 回灌 main 到 dev (#192)
* - feat(connection,metadata,kingbase): 增强多数据源连接能力并修复金仓/达梦/Oracle/ClickHouse兼容性问题 (#188)

* feat(http-tunnel): 支持独立 HTTP 隧道连接并覆盖多数据源

refs #168

* fix(kingbase-data-grid): 修复金仓打开表卡顿并降低对象渲染开销

refs #178

* fix(kingbase-transaction): 修复金仓事务提交重复引号导致语法错误

refs #176

* fix(driver-agent): 修复老版本 Win10 升级后金仓驱动代理启动失败

refs #177

* chore(ci): 新增手动触发的 macOS 测试构建工作流

* chore(ci): 允许测试工作流在当前分支自动触发

* fix(query-editor): 修复 SQL 编辑中光标随机跳到末尾 refs #185

* feat(data-sync): 增加差异 SQL 预览能力便于审核 refs #174

* fix(clickhouse-connect): 自动识别并回退 HTTP/Native 协议连接 refs #181

* fix(oracle-metadata): 修复视图与函数加载按 schema 过滤异常 refs #155

* fix(dameng-databases): 修复显示全部库时数据库列表不完整 refs #154

* fix(connection,db-list): 统一处理空列表返回并修复达梦连接测试报错 refs #157

* Release/0.5.3 (#191)

---------

Co-authored-by: 辣条 <69459608+tianqijiuyun-latiao@users.noreply.github.com>
Co-authored-by: Syngnat <92659908+Syngnat@users.noreply.github.com>
2026-03-06 14:31:15 +08:00
Syngnat
fb65b553e9 Release/0.5.3 (#191) 2026-03-06 14:30:07 +08:00
Syngnat
1a5bf79dd3 Merge branch 'fix/editor-sql-error-20260306-ygf' into dev 2026-03-06 14:27:39 +08:00
Syngnat
dea096d4c2 feat(release-notes): 支持自动生成 Release 更新说明并区分配置文件命名 2026-03-06 14:26:08 +08:00
github-actions[bot]
04f8b266d3 - feat(connection,metadata,kingbase): 增强多数据源连接能力并修复金仓/达梦/Oracle/ClickHouse兼容性问题 (#188) (#190)
* feat(http-tunnel): 支持独立 HTTP 隧道连接并覆盖多数据源

refs #168

* fix(kingbase-data-grid): 修复金仓打开表卡顿并降低对象渲染开销

refs #178

* fix(kingbase-transaction): 修复金仓事务提交重复引号导致语法错误

refs #176

* fix(driver-agent): 修复老版本 Win10 升级后金仓驱动代理启动失败

refs #177

* chore(ci): 新增手动触发的 macOS 测试构建工作流

* chore(ci): 允许测试工作流在当前分支自动触发

* fix(query-editor): 修复 SQL 编辑中光标随机跳到末尾 refs #185

* feat(data-sync): 增加差异 SQL 预览能力便于审核 refs #174

* fix(clickhouse-connect): 自动识别并回退 HTTP/Native 协议连接 refs #181

* fix(oracle-metadata): 修复视图与函数加载按 schema 过滤异常 refs #155

* fix(dameng-databases): 修复显示全部库时数据库列表不完整 refs #154

* fix(connection,db-list): 统一处理空列表返回并修复达梦连接测试报错 refs #157

Co-authored-by: 辣条 <69459608+tianqijiuyun-latiao@users.noreply.github.com>
2026-03-06 13:57:11 +08:00
辣条
b53227cb15 - feat(connection,metadata,kingbase): 增强多数据源连接能力并修复金仓/达梦/Oracle/ClickHouse兼容性问题 (#188)
* feat(http-tunnel): 支持独立 HTTP 隧道连接并覆盖多数据源

refs #168

* fix(kingbase-data-grid): 修复金仓打开表卡顿并降低对象渲染开销

refs #178

* fix(kingbase-transaction): 修复金仓事务提交重复引号导致语法错误

refs #176

* fix(driver-agent): 修复老版本 Win10 升级后金仓驱动代理启动失败

refs #177

* chore(ci): 新增手动触发的 macOS 测试构建工作流

* chore(ci): 允许测试工作流在当前分支自动触发

* fix(query-editor): 修复 SQL 编辑中光标随机跳到末尾 refs #185

* feat(data-sync): 增加差异 SQL 预览能力便于审核 refs #174

* fix(clickhouse-connect): 自动识别并回退 HTTP/Native 协议连接 refs #181

* fix(oracle-metadata): 修复视图与函数加载按 schema 过滤异常 refs #155

* fix(dameng-databases): 修复显示全部库时数据库列表不完整 refs #154

* fix(connection,db-list): 统一处理空列表返回并修复达梦连接测试报错 refs #157
2026-03-06 13:55:13 +08:00
Syngnat
0246d7fae5 Merge remote-tracking branch 'origin/main' into dev
# Conflicts:
#	CONTRIBUTING.md
#	CONTRIBUTING.zh-CN.md
2026-03-06 11:17:18 +08:00
Syngnat
4aa177ed37 🔧 chore(branch-sync): 补充 main 回灌 dev 权限前置条件并增加失败告警 2026-03-06 11:05:27 +08:00
Syngnat
4f5a7bd94b feat(branch-sync): 新增 main 回灌 dev 自动同步工作流并同步中英文贡献指南 2026-03-06 09:40:49 +08:00
Syngnat
00c6f9871f Release/0.5.2 (#183) 2026-03-05 17:17:03 +08:00
Syngnat
6a4b397ecc Merge branch 'feature/suport-clickhouse-20260227-ygf' into dev 2026-03-05 17:15:16 +08:00
Syngnat
3973038aea Merge branch 'main' into dev
# Conflicts:
#	frontend/src/App.tsx
#	frontend/src/components/ConnectionModal.tsx
#	frontend/src/components/DataGrid.tsx
#	frontend/src/components/DataViewer.tsx
#	frontend/src/components/QueryEditor.tsx
#	internal/app/methods_driver.go
#	internal/app/methods_file_export_test.go
#	internal/db/clickhouse_impl.go
#	internal/db/oracle_impl.go
#	internal/redis/redis_impl.go
2026-03-05 17:11:41 +08:00
辣条
71b41459e7 feat(mongodb,connection-tree,query-editor,sidebar,sqlserver,table-designer,ssl): 完成 MongoDB v1/v2 驱动切换与复制连接,增强快捷键/搜索/筛选与设计表体验,并修复 SQLServer、SSL 及连接稳定性问题 (#180)
* feat(mongodb-driver,connection-tree): 支持 MongoDB v1/v2 切换并新增复制连接

* fix(mongodb-query): 修复 MongoDB 筛选不生效并兼容 shell 语法执行

refs #153

* fix(query-editor): 修复 SQLServer 自动补全回车重复 dbo 前缀

refs #159

* fix(sqlserver-table-designer): 修复设计表读取列时错误使用 schema 作为数据库名

refs #156

* feat(shortcuts): 增加快捷键设置并支持 SQL 执行/侧边栏搜索

refs #158

* fix(sidebar-search): 优化范围搜索匹配与交互

refs #158

* fix(filter,connection-recovery): 保持筛选状态并修复连接失效卡死

refs #165

同步修复连接失效后侧栏持续转圈、断开后无法恢复的问题

* feat(table-designer): 统一设计表界面风格并优化字段新增交互

- 统一设计表页面与数据面板的视觉风格,覆盖工具栏、Tabs、表格与编辑区域

- 移除默认硬边框,改为透明背景与细分隔线,提升整体观感一致性

- 添加字段后自动滚动到新行并高亮,且自动聚焦输入框

- 新增" 在选中字段后添加\,支持按选中字段位置插入字段

* feat(data-grid-filter): 筛选字段支持快捷搜索

- 在筛选条件字段下拉启用可搜索(showSearch)

- 支持字段名大小写不敏感模糊匹配

- 表字段较多时可快速定位目标字段,减少下拉查找耗时

refs #171

* fix(db-ssl): 支持多数据源 SSL/TLS 连接并补齐达梦证书配置

refs #167

* fix(sidebar): 修复数据库加载时 null.map 导致表加载失败

* fix(query-editor): 合并运行按钮并保留 SQL 停止执行入口
2026-03-05 16:52:06 +08:00
ljyf5593
69942bb77e * feat: SQL执行中时,增加取消执行功能 (#172)
Co-authored-by: liujie <469282686@qq.com>
2026-03-05 15:28:34 +08:00
ljyf5593
f372b20a68 fix: 修复连接导出功能生成空JSON数组的问题 (#169)
Co-authored-by: liujie <469282686@qq.com>
2026-03-05 12:01:58 +08:00
Toskysun
e6da986927 feat: 新增 HTML 导出功能 (#164)
- 后端:在 writeRowsToFile 中新增 html 分支
- 后端:实现 writeRowsToHTML 函数,生成包含内嵌 CSS 的独立 HTML 文件
- 后端:实现 formatExportHTMLCell 函数,进行 HTML 转义和换行处理
- 后端:新增测试用例验证 XSS 转义、样式存在、换行处理、空值显示
- 前端:在 DataGrid 所有导出菜单中新增 HTML 选项(右键菜单、工具栏、单元格菜单)
- 前端:在 Sidebar 表节点右键菜单中新增 HTML 选项
- 样式:响应式表格设计,支持斑马纹、悬停效果、表头吸顶
- 安全:所有用户数据经过 HTML 转义,防止 XSS 攻击
2026-03-04 17:46:18 +08:00
Toskysun
4570516678 feat: 表筛选结果一键导出功能 (#161)
* 🔧 chore(gitignore): 忽略 AI 上下文文档避免版本控制污染

添加 CLAUDE.md 及其子目录变体到 .gitignore,防止 AI 辅助开发过程中生成的临时上下文文件被意外提交到仓库。

- 忽略根目录 CLAUDE.md
- 忽略所有子目录下的 CLAUDE.md 文件

* feat: 表筛选结果一键导出功能

- 新增表浏览模式下筛选结果的导出功能
- DataViewer 生成包含筛选条件的完整 SQL
- DataGrid 动态显示分组导出菜单(筛选结果 + 全表)
- 支持 CSV、Excel、JSON、Markdown 四种格式
- 添加未提交修改的警告提示
- 复用现有 ExportQuery 后端方法,无需后端修改

实现细节:
- 使用 buildWhereSQL 和 buildOrderBySQL 构建 SQL
- 支持 MySQL/MariaDB 的 sort buffer 优化
- 分组菜单设计避免用户误操作
- 导出文件名包含 _filtered 后缀

关闭 #issue
2026-03-04 13:54:51 +08:00
凌封
8c91d8929b Feature/add aibook (#160)
* feat: 增加技术圈连接

* feat: 增强水平滚动条在大量数据下支持鼠标滚轮 (refs #146)

* feat: 新增连接标签分组功能,支持创建/编辑/删除标签、拖拽归组、右键移至标签 refs #148
2026-03-04 11:50:34 +08:00
Syngnat
786835c9bc 📝 docs(contributing): 补充中英文贡献指南并统一 README 入口
- 新增英文版 CONTRIBUTING.md 作为正式贡献文档
- 新增中文版 CONTRIBUTING.zh-CN.md 作为中文贡献说明
- 调整 README 和 README.zh-CN 的贡献入口指向对应语言文档
2026-03-03 15:49:58 +08:00
Syngnat
f2fc7cbd05 Release/0.5.1 (#152)
* 🐛 fix(data-viewer): 修复ClickHouse尾部分页异常并增强DuckDB复杂类型兼容

- DataViewer 新增 ClickHouse 反向分页策略,修复最后页与倒数页查询失败
- DuckDB 查询失败时按列类型生成安全 SELECT,复杂类型转 VARCHAR 重试
- 分页状态统一使用 currentPage 回填,避免页码与总数推导不一致
- 增强查询异常日志与重试路径,降低大表场景卡顿与误报

*  feat(frontend-driver): 驱动管理支持快速搜索并优化信息展示

- 新增搜索框,支持按 DuckDB/ClickHouse 等关键字快速定位驱动
- 显示“匹配 x / y”统计与无结果提示
- 优化头部区域排版,提升透明/暗色场景下的视觉对齐

* 🔧 fix(connection-modal): 修复多数据源URI导入解析并校正Oracle服务名校验

- 新增单主机URI解析映射,兼容 postgres/postgresql、sqlserver、redis、tdengine、dameng(dm)、kingbase、highgo、vastbase、clickhouse、oracle
- 抽取 parseSingleHostUri 复用逻辑,统一 host/port/user/password/database 回填行为
- Oracle 连接新增服务名必填校验,移除“服务名为空回退用户名”的隐式逻辑
- 连接弹窗补充 Oracle 服务名输入项与 URI 示例

* 🐛 fix(query-export): 修复查询结果导出卡住并统一按数据源能力控制导出路径

- 查询结果页导出增加稳定兜底,异常时确保 loading 关闭避免持续转圈
- DataGrid 导出逻辑按数据源能力分流,优先走后端 ExportQuery 并保留结果集导出降级
- QueryEditor 传递结果导出 SQL,保证查询结果导出范围与当前结果一致
- 后端补充 ExportData/ExportQuery 关键日志,提升导出链路可观测性

* 🐛 fix(precision): 修复查询链路与分页统计的大整数精度丢失

- 代理响应数据解码改为 UseNumber,避免默认 float64 吞精度
- 统一归一化 json.Number 与超界整数,超出 JS 安全范围转字符串
- 修复 DataViewer 总数解析,超大值不再误转 Number 参与分页
- refs #142

* 🐛 fix(driver-manager): 修复驱动管理网络告警重复并强化代理引导

- 新增下载链路域名探测,区分“GitHub可达但驱动下载链路不可达”
- 网络不可达场景仅保留红色强提醒,移除重复二级告警
- 强提醒增加“打开全局代理设置”入口,优先引导使用 GoNavi 全局代理
- 统一网络检测与目录说明提示图标尺寸,修复加载期视觉不一致
- refs #141

* ♻️ refactor(frontend-interaction): 统一标签拖拽与暗色主题交互实现

- 重构Tab拖拽排序实现,统一为可配置拖拽引擎
- 规范拖拽与点击事件边界,提升交互一致性
- 统一多组件暗色透明样式策略,减少硬编码色值
- 提升Redis/表格/连接面板在透明模式下的观感一致性
- refs #144

* ♻️ refactor(update-state): 重构在线更新状态流并按版本统一进度展示

- 重构更新检查与下载状态同步流程,减少前后端状态分叉
- 进度展示严格绑定 latestVersion,避免跨版本状态串用
- 优化 about 打开场景的静默检查状态回填逻辑
- 统一下载弹窗关闭/后台隐藏行为
- 保持现有安装流程并补齐目录打开能力

* 🎨 style(sidebar-log): 将SQL执行日志入口调整为悬浮胶囊样式

- 移除侧栏底部整条日志入口容器
- 新增悬浮按钮阴影/边框/透明背景并适配明暗主题
- 为树区域预留底部空间避免入口遮挡内容

*  feat(redis-cluster): 支持集群模式逻辑多库隔离与 0-15 库切换

- 前端恢复 Redis 集群场景下 db0-db15 的数据库选择与展示
- 后端新增集群逻辑库命名空间前缀映射,统一 key/pattern 读写隔离
- 覆盖扫描、读取、写入、删除、重命名等核心操作的键映射规则
- 集群命令通道支持 SELECT 逻辑切库与 FLUSHDB 逻辑库清空
- refs #145

*  feat(DataGrid): 大数据表虚拟滚动性能优化及UI一致性修复

- 启用动态虚拟滚动(数据量≥500行自动切换),解决万行数据表卡顿问题
- 虚拟模式下EditableCell改用div渲染,CSS选择器从元素级改为类级适配虚拟DOM
- 修复虚拟模式双水平滚动条:样式化rc-virtual-list内置滚动条为胶囊外观,禁用自定义外部滚动条
- 为rc-virtual-list水平滚动条添加鼠标滚轮支持(MutationObserver + marginLeft驱动)
- 修复白色主题透明模式下列名悬浮Tooltip对比度不足的问题
- 新增白色主题全局滚动条样式适配透明模式(App.css)
- App.tsx主题token与组件样式优化
- refs #147

* 🔧 chore(app): 清理 App.tsx 类型告警并收敛前端壳层实现

- 清除未使用代码和冗余状态
- 替换弃用 API 以消除 IDE 提示
- 显式处理浮动 Promise 避免告警
- 保持现有更新检查和代理设置行为不变

* 🔧 fix(ci): 修复 Windows AMD64 下 DuckDB 驱动构建链路

- 将 DuckDB 工具链准备切换为优先使用 MSYS2
- 增加 gcc 和 g++ 存在性校验与版本验证
- 在 MSYS2 异常时回退 Chocolatey 安装 MinGW
- 保持 Windows ARM64 跳过 DuckDB 构建与平台支持一致

* 🔧 fix(ci): 修复 Windows AMD64 下 DuckDB 驱动构建工具链

- 将 DuckDB 编译链从 MINGW64 切换为 MSYS2 UCRT64
- 修正 Windows AMD64 的 gcc 和 g++ 探测路径
- 增加 DuckDB 编译器版本校验步骤

---------

Co-authored-by: Syngnat <yangguofeng919@gmail.com>
2026-03-03 15:25:25 +08:00
Syngnat
462ca57907 🔧 fix(ci): 修复 Windows AMD64 下 DuckDB 驱动构建工具链
- 将 DuckDB 编译链从 MINGW64 切换为 MSYS2 UCRT64
- 修正 Windows AMD64 的 gcc 和 g++ 探测路径
- 增加 DuckDB 编译器版本校验步骤
2026-03-03 15:22:02 +08:00
Syngnat
4bfdb2cb6c Release/0.5.1 (#150)
* 🐛 fix(data-viewer): 修复ClickHouse尾部分页异常并增强DuckDB复杂类型兼容

- DataViewer 新增 ClickHouse 反向分页策略,修复最后页与倒数页查询失败
- DuckDB 查询失败时按列类型生成安全 SELECT,复杂类型转 VARCHAR 重试
- 分页状态统一使用 currentPage 回填,避免页码与总数推导不一致
- 增强查询异常日志与重试路径,降低大表场景卡顿与误报

*  feat(frontend-driver): 驱动管理支持快速搜索并优化信息展示

- 新增搜索框,支持按 DuckDB/ClickHouse 等关键字快速定位驱动
- 显示“匹配 x / y”统计与无结果提示
- 优化头部区域排版,提升透明/暗色场景下的视觉对齐

* 🔧 fix(connection-modal): 修复多数据源URI导入解析并校正Oracle服务名校验

- 新增单主机URI解析映射,兼容 postgres/postgresql、sqlserver、redis、tdengine、dameng(dm)、kingbase、highgo、vastbase、clickhouse、oracle
- 抽取 parseSingleHostUri 复用逻辑,统一 host/port/user/password/database 回填行为
- Oracle 连接新增服务名必填校验,移除“服务名为空回退用户名”的隐式逻辑
- 连接弹窗补充 Oracle 服务名输入项与 URI 示例

* 🐛 fix(query-export): 修复查询结果导出卡住并统一按数据源能力控制导出路径

- 查询结果页导出增加稳定兜底,异常时确保 loading 关闭避免持续转圈
- DataGrid 导出逻辑按数据源能力分流,优先走后端 ExportQuery 并保留结果集导出降级
- QueryEditor 传递结果导出 SQL,保证查询结果导出范围与当前结果一致
- 后端补充 ExportData/ExportQuery 关键日志,提升导出链路可观测性

* 🐛 fix(precision): 修复查询链路与分页统计的大整数精度丢失

- 代理响应数据解码改为 UseNumber,避免默认 float64 吞精度
- 统一归一化 json.Number 与超界整数,超出 JS 安全范围转字符串
- 修复 DataViewer 总数解析,超大值不再误转 Number 参与分页
- refs #142

* 🐛 fix(driver-manager): 修复驱动管理网络告警重复并强化代理引导

- 新增下载链路域名探测,区分“GitHub可达但驱动下载链路不可达”
- 网络不可达场景仅保留红色强提醒,移除重复二级告警
- 强提醒增加“打开全局代理设置”入口,优先引导使用 GoNavi 全局代理
- 统一网络检测与目录说明提示图标尺寸,修复加载期视觉不一致
- refs #141

* ♻️ refactor(frontend-interaction): 统一标签拖拽与暗色主题交互实现

- 重构Tab拖拽排序实现,统一为可配置拖拽引擎
- 规范拖拽与点击事件边界,提升交互一致性
- 统一多组件暗色透明样式策略,减少硬编码色值
- 提升Redis/表格/连接面板在透明模式下的观感一致性
- refs #144

* ♻️ refactor(update-state): 重构在线更新状态流并按版本统一进度展示

- 重构更新检查与下载状态同步流程,减少前后端状态分叉
- 进度展示严格绑定 latestVersion,避免跨版本状态串用
- 优化 about 打开场景的静默检查状态回填逻辑
- 统一下载弹窗关闭/后台隐藏行为
- 保持现有安装流程并补齐目录打开能力

* 🎨 style(sidebar-log): 将SQL执行日志入口调整为悬浮胶囊样式

- 移除侧栏底部整条日志入口容器
- 新增悬浮按钮阴影/边框/透明背景并适配明暗主题
- 为树区域预留底部空间避免入口遮挡内容

*  feat(redis-cluster): 支持集群模式逻辑多库隔离与 0-15 库切换

- 前端恢复 Redis 集群场景下 db0-db15 的数据库选择与展示
- 后端新增集群逻辑库命名空间前缀映射,统一 key/pattern 读写隔离
- 覆盖扫描、读取、写入、删除、重命名等核心操作的键映射规则
- 集群命令通道支持 SELECT 逻辑切库与 FLUSHDB 逻辑库清空
- refs #145

*  feat(DataGrid): 大数据表虚拟滚动性能优化及UI一致性修复

- 启用动态虚拟滚动(数据量≥500行自动切换),解决万行数据表卡顿问题
- 虚拟模式下EditableCell改用div渲染,CSS选择器从元素级改为类级适配虚拟DOM
- 修复虚拟模式双水平滚动条:样式化rc-virtual-list内置滚动条为胶囊外观,禁用自定义外部滚动条
- 为rc-virtual-list水平滚动条添加鼠标滚轮支持(MutationObserver + marginLeft驱动)
- 修复白色主题透明模式下列名悬浮Tooltip对比度不足的问题
- 新增白色主题全局滚动条样式适配透明模式(App.css)
- App.tsx主题token与组件样式优化
- refs #147

* 🔧 chore(app): 清理 App.tsx 类型告警并收敛前端壳层实现

- 清除未使用代码和冗余状态
- 替换弃用 API 以消除 IDE 提示
- 显式处理浮动 Promise 避免告警
- 保持现有更新检查和代理设置行为不变

* 🔧 fix(ci): 修复 Windows AMD64 下 DuckDB 驱动构建链路

- 将 DuckDB 工具链准备切换为优先使用 MSYS2
- 增加 gcc 和 g++ 存在性校验与版本验证
- 在 MSYS2 异常时回退 Chocolatey 安装 MinGW
- 保持 Windows ARM64 跳过 DuckDB 构建与平台支持一致
2026-03-03 15:06:16 +08:00
Syngnat
6918b56ed9 Merge remote-tracking branch 'origin/feature/suport-clickhouse-20260227-ygf' into feature/suport-clickhouse-20260227-ygf 2026-03-03 14:58:48 +08:00
Syngnat
1afb8850ad 🔧 fix(ci): 修复 Windows AMD64 下 DuckDB 驱动构建链路
- 将 DuckDB 工具链准备切换为优先使用 MSYS2
- 增加 gcc 和 g++ 存在性校验与版本验证
- 在 MSYS2 异常时回退 Chocolatey 安装 MinGW
- 保持 Windows ARM64 跳过 DuckDB 构建与平台支持一致
2026-03-03 14:58:37 +08:00
Syngnat
3284eeba17 🔧 fix(ci): 修复 Windows AMD64 下 DuckDB 驱动构建链路
- 将 DuckDB 工具链准备切换为优先使用 MSYS2
- 增加 gcc 和 g++ 存在性校验与版本验证
- 在 MSYS2 异常时回退 Chocolatey 安装 MinGW
- 保持 Windows ARM64 跳过 DuckDB 构建与平台支持一致
2026-03-03 14:58:19 +08:00
Syngnat
494484eb92 Release/0.5.1 (#149)
* 🐛 fix(data-viewer): 修复ClickHouse尾部分页异常并增强DuckDB复杂类型兼容

- DataViewer 新增 ClickHouse 反向分页策略,修复最后页与倒数页查询失败
- DuckDB 查询失败时按列类型生成安全 SELECT,复杂类型转 VARCHAR 重试
- 分页状态统一使用 currentPage 回填,避免页码与总数推导不一致
- 增强查询异常日志与重试路径,降低大表场景卡顿与误报

*  feat(frontend-driver): 驱动管理支持快速搜索并优化信息展示

- 新增搜索框,支持按 DuckDB/ClickHouse 等关键字快速定位驱动
- 显示“匹配 x / y”统计与无结果提示
- 优化头部区域排版,提升透明/暗色场景下的视觉对齐

* 🔧 fix(connection-modal): 修复多数据源URI导入解析并校正Oracle服务名校验

- 新增单主机URI解析映射,兼容 postgres/postgresql、sqlserver、redis、tdengine、dameng(dm)、kingbase、highgo、vastbase、clickhouse、oracle
- 抽取 parseSingleHostUri 复用逻辑,统一 host/port/user/password/database 回填行为
- Oracle 连接新增服务名必填校验,移除“服务名为空回退用户名”的隐式逻辑
- 连接弹窗补充 Oracle 服务名输入项与 URI 示例

* 🐛 fix(query-export): 修复查询结果导出卡住并统一按数据源能力控制导出路径

- 查询结果页导出增加稳定兜底,异常时确保 loading 关闭避免持续转圈
- DataGrid 导出逻辑按数据源能力分流,优先走后端 ExportQuery 并保留结果集导出降级
- QueryEditor 传递结果导出 SQL,保证查询结果导出范围与当前结果一致
- 后端补充 ExportData/ExportQuery 关键日志,提升导出链路可观测性

* 🐛 fix(precision): 修复查询链路与分页统计的大整数精度丢失

- 代理响应数据解码改为 UseNumber,避免默认 float64 吞精度
- 统一归一化 json.Number 与超界整数,超出 JS 安全范围转字符串
- 修复 DataViewer 总数解析,超大值不再误转 Number 参与分页
- refs #142

* 🐛 fix(driver-manager): 修复驱动管理网络告警重复并强化代理引导

- 新增下载链路域名探测,区分“GitHub可达但驱动下载链路不可达”
- 网络不可达场景仅保留红色强提醒,移除重复二级告警
- 强提醒增加“打开全局代理设置”入口,优先引导使用 GoNavi 全局代理
- 统一网络检测与目录说明提示图标尺寸,修复加载期视觉不一致
- refs #141

* ♻️ refactor(frontend-interaction): 统一标签拖拽与暗色主题交互实现

- 重构Tab拖拽排序实现,统一为可配置拖拽引擎
- 规范拖拽与点击事件边界,提升交互一致性
- 统一多组件暗色透明样式策略,减少硬编码色值
- 提升Redis/表格/连接面板在透明模式下的观感一致性
- refs #144

* ♻️ refactor(update-state): 重构在线更新状态流并按版本统一进度展示

- 重构更新检查与下载状态同步流程,减少前后端状态分叉
- 进度展示严格绑定 latestVersion,避免跨版本状态串用
- 优化 about 打开场景的静默检查状态回填逻辑
- 统一下载弹窗关闭/后台隐藏行为
- 保持现有安装流程并补齐目录打开能力

* 🎨 style(sidebar-log): 将SQL执行日志入口调整为悬浮胶囊样式

- 移除侧栏底部整条日志入口容器
- 新增悬浮按钮阴影/边框/透明背景并适配明暗主题
- 为树区域预留底部空间避免入口遮挡内容

*  feat(redis-cluster): 支持集群模式逻辑多库隔离与 0-15 库切换

- 前端恢复 Redis 集群场景下 db0-db15 的数据库选择与展示
- 后端新增集群逻辑库命名空间前缀映射,统一 key/pattern 读写隔离
- 覆盖扫描、读取、写入、删除、重命名等核心操作的键映射规则
- 集群命令通道支持 SELECT 逻辑切库与 FLUSHDB 逻辑库清空
- refs #145

*  feat(DataGrid): 大数据表虚拟滚动性能优化及UI一致性修复

- 启用动态虚拟滚动(数据量≥500行自动切换),解决万行数据表卡顿问题
- 虚拟模式下EditableCell改用div渲染,CSS选择器从元素级改为类级适配虚拟DOM
- 修复虚拟模式双水平滚动条:样式化rc-virtual-list内置滚动条为胶囊外观,禁用自定义外部滚动条
- 为rc-virtual-list水平滚动条添加鼠标滚轮支持(MutationObserver + marginLeft驱动)
- 修复白色主题透明模式下列名悬浮Tooltip对比度不足的问题
- 新增白色主题全局滚动条样式适配透明模式(App.css)
- App.tsx主题token与组件样式优化
- refs #147

* 🔧 chore(app): 清理 App.tsx 类型告警并收敛前端壳层实现

- 清除未使用代码和冗余状态
- 替换弃用 API 以消除 IDE 提示
- 显式处理浮动 Promise 避免告警
- 保持现有更新检查和代理设置行为不变

---------

Co-authored-by: Syngnat <yangguofeng919@gmail.com>
2026-03-03 14:35:17 +08:00
Syngnat
6156884455 Merge branch 'feature/suport-clickhouse-20260227-ygf' into dev1 2026-03-03 14:23:04 +08:00
Syngnat
a54b8906a3 Revert "feat: 增加关于内容技术圈"
This reverts commit 9a684cd82c.
2026-03-03 14:18:53 +08:00
Syngnat
f477feab2f 🔧 chore(app): 清理 App.tsx 类型告警并收敛前端壳层实现
- 清除未使用代码和冗余状态
- 替换弃用 API 以消除 IDE 提示
- 显式处理浮动 Promise 避免告警
- 保持现有更新检查和代理设置行为不变
2026-03-03 14:11:35 +08:00
Syngnat
e76e174bfe feat(DataGrid): 大数据表虚拟滚动性能优化及UI一致性修复
- 启用动态虚拟滚动(数据量≥500行自动切换),解决万行数据表卡顿问题
- 虚拟模式下EditableCell改用div渲染,CSS选择器从元素级改为类级适配虚拟DOM
- 修复虚拟模式双水平滚动条:样式化rc-virtual-list内置滚动条为胶囊外观,禁用自定义外部滚动条
- 为rc-virtual-list水平滚动条添加鼠标滚轮支持(MutationObserver + marginLeft驱动)
- 修复白色主题透明模式下列名悬浮Tooltip对比度不足的问题
- 新增白色主题全局滚动条样式适配透明模式(App.css)
- App.tsx主题token与组件样式优化
- refs #147
2026-03-03 13:49:31 +08:00
Syngnat
b904c0b107 feat(redis-cluster): 支持集群模式逻辑多库隔离与 0-15 库切换
- 前端恢复 Redis 集群场景下 db0-db15 的数据库选择与展示
- 后端新增集群逻辑库命名空间前缀映射,统一 key/pattern 读写隔离
- 覆盖扫描、读取、写入、删除、重命名等核心操作的键映射规则
- 集群命令通道支持 SELECT 逻辑切库与 FLUSHDB 逻辑库清空
- refs #145
2026-03-03 09:42:49 +08:00
Syngnat
c02e7c12e8 🎨 style(sidebar-log): 将SQL执行日志入口调整为悬浮胶囊样式
- 移除侧栏底部整条日志入口容器
- 新增悬浮按钮阴影/边框/透明背景并适配明暗主题
- 为树区域预留底部空间避免入口遮挡内容
2026-03-02 17:45:09 +08:00
Syngnat
a87c801e66 ♻️ refactor(update-state): 重构在线更新状态流并按版本统一进度展示
- 重构更新检查与下载状态同步流程,减少前后端状态分叉
- 进度展示严格绑定 latestVersion,避免跨版本状态串用
- 优化 about 打开场景的静默检查状态回填逻辑
- 统一下载弹窗关闭/后台隐藏行为
- 保持现有安装流程并补齐目录打开能力
2026-03-02 17:26:40 +08:00
Syngnat
7f00139847 ♻️ refactor(frontend-interaction): 统一标签拖拽与暗色主题交互实现
- 重构Tab拖拽排序实现,统一为可配置拖拽引擎
- 规范拖拽与点击事件边界,提升交互一致性
- 统一多组件暗色透明样式策略,减少硬编码色值
- 提升Redis/表格/连接面板在透明模式下的观感一致性
- refs #144
2026-03-02 16:34:09 +08:00
Syngnat
78c5351399 🐛 fix(driver-manager): 修复驱动管理网络告警重复并强化代理引导
- 新增下载链路域名探测,区分“GitHub可达但驱动下载链路不可达”
- 网络不可达场景仅保留红色强提醒,移除重复二级告警
- 强提醒增加“打开全局代理设置”入口,优先引导使用 GoNavi 全局代理
- 统一网络检测与目录说明提示图标尺寸,修复加载期视觉不一致
- refs #141
2026-03-02 15:58:58 +08:00
Syngnat
e2acfa51eb Merge pull request #143 from fengin/feature/addAibook
feat: 增加关于内容技术圈
2026-03-02 14:46:03 +08:00
fengin
9a684cd82c feat: 增加关于内容技术圈 2026-03-02 14:42:42 +08:00
Syngnat
e3b142053f 🐛 fix(precision): 修复查询链路与分页统计的大整数精度丢失
- 代理响应数据解码改为 UseNumber,避免默认 float64 吞精度
- 统一归一化 json.Number 与超界整数,超出 JS 安全范围转字符串
- 修复 DataViewer 总数解析,超大值不再误转 Number 参与分页
- refs #142
2026-03-02 14:40:59 +08:00
Syngnat
3ca898a950 🐛 fix(query-export): 修复查询结果导出卡住并统一按数据源能力控制导出路径
- 查询结果页导出增加稳定兜底,异常时确保 loading 关闭避免持续转圈
- DataGrid 导出逻辑按数据源能力分流,优先走后端 ExportQuery 并保留结果集导出降级
- QueryEditor 传递结果导出 SQL,保证查询结果导出范围与当前结果一致
- 后端补充 ExportData/ExportQuery 关键日志,提升导出链路可观测性
2026-03-02 14:18:44 +08:00
Syngnat
84688e995a 🔧 fix(connection-modal): 修复多数据源URI导入解析并校正Oracle服务名校验
- 新增单主机URI解析映射,兼容 postgres/postgresql、sqlserver、redis、tdengine、dameng(dm)、kingbase、highgo、vastbase、clickhouse、oracle
- 抽取 parseSingleHostUri 复用逻辑,统一 host/port/user/password/database 回填行为
- Oracle 连接新增服务名必填校验,移除“服务名为空回退用户名”的隐式逻辑
- 连接弹窗补充 Oracle 服务名输入项与 URI 示例
2026-03-02 11:46:59 +08:00
Syngnat
4d0940636d feat(frontend-driver): 驱动管理支持快速搜索并优化信息展示
- 新增搜索框,支持按 DuckDB/ClickHouse 等关键字快速定位驱动
- 显示“匹配 x / y”统计与无结果提示
- 优化头部区域排版,提升透明/暗色场景下的视觉对齐
2026-03-02 11:10:48 +08:00
Syngnat
26b79adc5f 🐛 fix(data-viewer): 修复ClickHouse尾部分页异常并增强DuckDB复杂类型兼容
- DataViewer 新增 ClickHouse 反向分页策略,修复最后页与倒数页查询失败
- DuckDB 查询失败时按列类型生成安全 SELECT,复杂类型转 VARCHAR 重试
- 分页状态统一使用 currentPage 回填,避免页码与总数推导不一致
- 增强查询异常日志与重试路径,降低大表场景卡顿与误报
2026-03-02 10:49:23 +08:00
Syngnat
90aa3561be Merge pull request #140 from Syngnat/release/0.5.0
Release/0.5.0
2026-02-28 15:57:40 +08:00
Syngnat
ec59023736 📝 docs(i18n-readme): 建立README中英双文档结构并统一能力描述
- 英文主 README 覆盖项目定位、性能特性与安装说明
- 中文 README 覆盖同等信息密度,避免内容断层
- 中英文文档统一支持数据源表格与驱动模式说明
- 完成语言切换链接配置,便于读者快速切换
2026-02-28 15:54:46 +08:00
Syngnat
4a96cb93d2 🎨 style(connection-modal): 优化新建连接弹窗尺寸与分类栏视觉一致性
- 统一调整弹窗 step1/step2 尺寸参数,改善布局观感
- 增加 step1 内容区最小高度,减少拥挤感
- 分类栏分割线改为主题感知颜色,消除深色模式下突兀白线
2026-02-28 15:36:26 +08:00
Syngnat
4c322db9d0 💥 breaking(driver-manager): 统一 Doris 驱动命名并移除 diros 历史包兼容
- 前后端统一 Doris 展示与连接命名,修复 diros 拼写问题
- GitHub Actions 驱动产物改为 doris-driver-agent-* 命名
- 构建流程保持内部 gonavi_diros_driver 映射,避免构建链路中断
- 驱动安装/下载/解压链路仅识别 doris 资产名,不再兼容 diros 历史包
- 内置与文档 manifest 下载地址统一为 builtin://activate/doris
- close #132
2026-02-28 15:27:58 +08:00
Syngnat
ed18c8285f 🐛 fix(db-compat): 修复PG系建表语句兼容并优化DuckDB大表总数统计
- 统一 DBShowCreateTable 与导出链路的 DDL 兜底逻辑,修复 Kingbase/Postgres 占位语句问题
- 增强 custom driver 到 postgres/kingbase/highgo/vastbase 的映射并补充回归测试
- DuckDB 关闭自动后台 COUNT(*),避免大文件场景翻页与查询卡顿
- 新增近似总数展示、手动精确统计与取消统计交互
- 新增 DBQueryIsolated 独立连接查询能力并同步前端 wailsjs 接口
- refs #136
2026-02-28 15:00:13 +08:00
Syngnat
5f8cedabd8 🐛 fix(update-proxy): 修复本地代理下检查更新 TLS 证书未知颁发者失败
- 在全局代理 HTTP 传输层增加本地回环代理兼容回退能力
- 回退触发条件限制为 unknown authority 且仅 GET/HEAD 请求
- 保留默认 TLS 校验策略并输出告警日志便于审计定位
- refs #139
2026-02-28 13:55:42 +08:00
Syngnat
20923989b9 🐛 fix(connection-modal): 修复透明暗色模式下 SSH/代理配置区块白底问题
- 为连接弹窗接入主题与透明度状态,按模式动态计算区块背景
- 将 SSH 与代理配置容器统一替换为自适应样式并补齐边框层次
- 保持连接测试与保存逻辑不变,仅修复显示层
2026-02-28 13:37:19 +08:00
Syngnat
210106cde7 🐛 fix(driver-modal): 修复驱动日志弹窗在透明暗色主题下对比度异常
- 将日志内容容器改为 dark/light 双模式自适应样式
- 使用全局外观透明度参数参与日志背景渲染
- 保持驱动安装与日志采集逻辑不变,仅修复显示层
2026-02-28 13:27:49 +08:00
Syngnat
87aac277ec 🎨 style(redis-viewer): 对齐 Redis 拖拽分割条与侧边栏宽度调整样式
- 分割条宽度调整为与 host 侧边栏一致
- 分割条背景统一为 transparent,去除 hover 强对比效果
- 保持拖拽命中区与提示文案,提升整体样式一致性
2026-02-28 12:53:06 +08:00
Syngnat
4de3f408c5 🐛 fix(redis-scan): 修复大数据量下命名空间加载不完整问题
- 前后端 Redis SCAN 游标统一为字符串传递,避免 Number 精度丢失
- RedisScanKeys 增加 string/number 游标兼容解析,异常游标降级并告警
- 新增游标解析单测
- refs #135
2026-02-28 12:32:22 +08:00
Syngnat
439625a49c 🔧 fix(duckdb-pagination): 修复 DuckDB 总数异常导致分页不可用
- 修正 DataViewer 在 hasMore 与 totalKnown 冲突时的分页状态处理
- 增强 DuckDB COUNT(*) 结果解析,兼容字段名与数值类型差异
- 将分页兜底逻辑收敛为 DuckDB 专用,避免影响其他数据库
- 修复 total=0 时分页文案显示异常
- refs #136
2026-02-28 12:14:34 +08:00
Syngnat
884d72f3d3 ♻️ refactor(clickhouse): 使用结构化 Options 替代 DSN 连接构造
- 用 buildClickHouseOptions 收敛连接参数生成逻辑
- 将连接入口改为 clickhouse.OpenDB(Options)
- 清理 DSN 中的 write_timeout/read_timeout/dial_timeout 透传路径
- 同步重写 ClickHouse 相关测试断言
- refs #138
2026-02-28 11:56:59 +08:00
Syngnat
98c1600e13 feat(driver-manager): 增强驱动管理本地导入并统一滚动交互体验
- 新增驱动目录批量导入入口,支持覆盖已安装开关与去重处理
- 行内本地导入聚焦单文件场景,目录导入与单文件导入流程统一
- 已安装驱动版本选择锁定,避免安装后误改版本
- 补充驱动下载网络检测与日志可见性,提升问题定位效率
- 重构驱动管理横向滚动条实现,修复双滚动条/消失/位置异常问题
2026-02-28 11:33:21 +08:00
Syngnat
eb594b7741 Merge pull request #134 from Syngnat/release/0.4.9
release/0.4.9
2026-02-27 17:39:17 +08:00
Syngnat
587ed3444b ️ perf(ci-assets): 完整化驱动打包资产覆盖范围
- 将 clickhouse 纳入可选驱动构建数组
- 提升发布资产完整性与可用性
- 减少驱动安装阶段因资产缺失导致的失败
2026-02-27 17:37:40 +08:00
Syngnat
e366a61910 Merge pull request #133 from Syngnat/release/0.4.9
Release/0.4.9
2026-02-27 17:24:09 +08:00
Syngnat
5986b71c4d ️ perf(redis-datagrid): 优化大数据场景下搜索与右键菜单响应性能
- RedisViewer 引入树节点轻量化、虚拟滚动与大 keyspace 性能模式,降低 Key 列表卡顿
- Redis 搜索按模式分级加载并增加请求乱序保护,避免搜索结果回写抖动
- Redis 后端 ScanKeys 为搜索模式增加时间预算与轮次上限,优先返回可继续分页结果
- DataGrid 稳定 Context/rowSelection/onRow 引用并增加 shouldCellUpdate,减少右键触发全表重渲染
2026-02-27 17:22:38 +08:00
Syngnat
cb18bc3067 feat(driver-proxy): 新增ClickHouse数据源并提供全局代理独立入口
- 新增 ClickHouse 可选驱动实现与 optional-driver-agent provider,补齐驱动注册与清单配置
- 补齐 ClickHouse 连接与 SQL 适配:连接默认端口/用户、LIMIT、标识符引用、只读编辑限制
- 新增全局代理后端能力与前端持久化配置,更新检查和驱动网络请求统一走代理客户端
2026-02-27 16:39:13 +08:00
Syngnat
d676ac9084 Merge remote-tracking branch 'origin/main' 2026-02-27 14:25:02 +08:00
Syngnat
7fcbcb2471 Merge branch 'release/0.4.8' 2026-02-27 14:24:44 +08:00
Syngnat
c680e50e74 Merge pull request #131 from Syngnat/release/0.4.8
Release/0.4.8
2026-02-27 14:24:16 +08:00
Syngnat
9685102229 feat(sidebar-batch-table): 批量操作表新增对象筛选与作用范围控制
- 批量操作表弹窗新增关键字筛选(忽略大小写包含匹配)
- 新增类型筛选(全部对象/仅表/仅视图)
- 新增勾选作用范围切换(当前筛选结果/全部对象)
- 全选、取消全选、反选逻辑按作用范围执行
- 筛选区域展示命中计数与无匹配空态提示
- refs #130
2026-02-27 14:21:14 +08:00
Syngnat
3505b4428a Merge remote-tracking branch 'origin/fix/windows-issue-20260226-ygf' into fix/windows-issue-20260226-ygf 2026-02-27 13:57:04 +08:00
Syngnat
9ebdf7f053 feat(appearance): 新增启动时全屏开关并支持启动窗口状态自动应用
- 在外观设置中提供用户可控的启动全屏偏好项
- 持久化保存用户偏好,重启后自动恢复
- 启动阶段按偏好自动执行全屏,失败时回退最大化
- 保持现有标题栏窗口操作行为不变
- refs #129
2026-02-27 13:56:36 +08:00
Syngnat
9ad852c10b 🐛 fix(redis-viewer): 修复大数据量场景 Key 加载不完整问题
- 后端 ScanKeys 改为按目标数量多轮聚合扫描,不再只依赖单轮返回结果
- 新增扫描目标数/步长/轮次上限,避免扫描过少或无限循环
- 前端首屏加载、搜索、刷新统一按较大批次请求,避免回退到几百条
- 加载更多改为按固定批次继续拉取并保持去重合并
- refs #129
2026-02-27 13:56:36 +08:00
Syngnat
2a8fff4d93 feat(driver-manager): 增强驱动管理网络诊断与本地导入能力
- 新增 CheckDriverNetworkStatus,探测 GitHub API/Release/Go Proxy 可达性并返回代理环境信息。
- 驱动管理弹窗新增网络检测结果、驱动目录复用说明、本地导入入口与日志查看。
- 操作日志支持同签名进度覆盖更新,下载百分比动态刷新,不再重复新增日志行。
- 修正弹窗滚动行为与表格滚动体验。
- refs #128
2026-02-27 13:56:36 +08:00
Syngnat
eca560b4e5 🐛 fix(data-grid): 修复单元格编辑器拖拽越界不自动滚动
- 在 DataGrid 拖拽选区流程新增边缘自动滚动能力(横向+纵向)
- 拖拽过程中增加鼠标位置跟踪并通过 RAF 循环驱动滚动
- 通过 elementFromPoint 兜底命中单元格,保证越界拖拽时选区持续更新
- 在 mouseup、模式切换和退出编辑器时统一清理 RAF 与拖拽状态
- refs #127
2026-02-27 13:56:36 +08:00
Syngnat
2f475dddc0 🐛 fix(windows-upgrade): 修复Windows升级后连接列表丢失问题
- 启动参数新增固定 WebviewUserDataPath 到 %APPDATA%/GoNavi/WebView2
- 首次启动自动迁移历史 WebView 数据目录
- 保留现有存储键,避免破坏已落盘配置
- 前端持久化读取增加历史结构兼容
- refs #125
2026-02-27 13:56:35 +08:00
Syngnat
ad9d8a12be feat(appearance): 新增启动时全屏开关并支持启动窗口状态自动应用
- 在外观设置中提供用户可控的启动全屏偏好项
- 持久化保存用户偏好,重启后自动恢复
- 启动阶段按偏好自动执行全屏,失败时回退最大化
- 保持现有标题栏窗口操作行为不变
- refs #129
2026-02-27 13:55:37 +08:00
Syngnat
095b22951e 🐛 fix(redis-viewer): 修复大数据量场景 Key 加载不完整问题
- 后端 ScanKeys 改为按目标数量多轮聚合扫描,不再只依赖单轮返回结果
- 新增扫描目标数/步长/轮次上限,避免扫描过少或无限循环
- 前端首屏加载、搜索、刷新统一按较大批次请求,避免回退到几百条
- 加载更多改为按固定批次继续拉取并保持去重合并
- refs #129
2026-02-27 13:26:28 +08:00
Syngnat
7350a011e3 feat(driver-manager): 增强驱动管理网络诊断与本地导入能力
- 新增 CheckDriverNetworkStatus,探测 GitHub API/Release/Go Proxy 可达性并返回代理环境信息。
- 驱动管理弹窗新增网络检测结果、驱动目录复用说明、本地导入入口与日志查看。
- 操作日志支持同签名进度覆盖更新,下载百分比动态刷新,不再重复新增日志行。
- 修正弹窗滚动行为与表格滚动体验。
- refs #128
2026-02-27 12:29:54 +08:00
Syngnat
53b5802add 🐛 fix(data-grid): 修复单元格编辑器拖拽越界不自动滚动
- 在 DataGrid 拖拽选区流程新增边缘自动滚动能力(横向+纵向)
- 拖拽过程中增加鼠标位置跟踪并通过 RAF 循环驱动滚动
- 通过 elementFromPoint 兜底命中单元格,保证越界拖拽时选区持续更新
- 在 mouseup、模式切换和退出编辑器时统一清理 RAF 与拖拽状态
- refs #127
2026-02-27 10:57:05 +08:00
Syngnat
54e7077317 🐛 fix(windows-upgrade): 修复Windows升级后连接列表丢失问题
- 启动参数新增固定 WebviewUserDataPath 到 %APPDATA%/GoNavi/WebView2
- 首次启动自动迁移历史 WebView 数据目录
- 保留现有存储键,避免破坏已落盘配置
- 前端持久化读取增加历史结构兼容
2026-02-27 10:45:57 +08:00
1711 changed files with 567238 additions and 17011 deletions

18
.containerignore Normal file
View File

@@ -0,0 +1,18 @@
.git
.github
.idea
.ace-tool
.playwright-mcp
.superpowers
.worktrees
bin
build
dist
frontend/dist
node_modules
frontend/node_modules
tools/__pycache__
GoNavi-Wails
GoNavi-Wails.exe
optional-driver-agent
optional-driver-agent.exe

18
.dockerignore Normal file
View File

@@ -0,0 +1,18 @@
.git
.github
.idea
.ace-tool
.playwright-mcp
.superpowers
.worktrees
bin
build
dist
frontend/dist
node_modules
frontend/node_modules
tools/__pycache__
GoNavi-Wails
GoNavi-Wails.exe
optional-driver-agent
optional-driver-agent.exe

26
.github/release.yaml vendored Normal file
View File

@@ -0,0 +1,26 @@
changelog:
categories:
- title: 新功能
labels:
- feature
- enhancement
- feat
- title: 问题修复
labels:
- bug
- fix
- title: 文档与流程
labels:
- docs
- documentation
- ci
- workflow
- chore
- title: 重构与优化
labels:
- refactor
- perf
- optimization
- title: 其他更新
labels:
- '*'

1423
.github/workflows/dev-build.yml vendored Normal file

File diff suppressed because it is too large Load Diff

260
.github/workflows/docker-images.yml vendored Normal file
View File

@@ -0,0 +1,260 @@
name: Docker Images
on:
push:
branches:
- dev
tags:
- 'v*'
concurrency:
group: docker-images-${{ github.ref }}
cancel-in-progress: true
permissions:
contents: read
packages: write
jobs:
build:
name: Build ${{ matrix.image_name }} (${{ matrix.platform }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
image_name:
- gonavi-mcp-server
- gonavi-web-server
- gonavi-build-env
platform:
- linux/amd64
- linux/arm64
include:
- image_name: gonavi-mcp-server
dockerfile: Dockerfile.mcp-server
description: GoNavi MCP Server container image
- image_name: gonavi-web-server
dockerfile: Dockerfile.web-server
description: GoNavi Web Server (browser UI) container image
- image_name: gonavi-build-env
dockerfile: Dockerfile.build-env
description: GoNavi Linux build environment image
- platform: linux/amd64
runner: ubuntu-latest
- platform: linux/arm64
runner: ubuntu-24.04-arm
steps:
- name: Checkout code
uses: actions/checkout@v5
- name: Validate runtime Dockerfiles
if: matrix.image_name == 'gonavi-web-server'
shell: bash
run: |
set -euo pipefail
bash tools/validate-web-server-dockerfile.test.sh
env_file="$(mktemp)"
trap 'rm -f "$env_file"' EXIT
printf 'GONAVI_HOST_DATA_ROOT=%s\nGONAVI_WEB_PASSWORD=123456\n' "${RUNNER_TEMP}/gonavi-data" >"$env_file"
rendered="$(docker compose --env-file "$env_file" -f docker-compose.web-server.yml config)"
grep -Eq "GONAVI_WEB_PASSWORD: ['\"]?123456['\"]?$" <<<"$rendered"
- name: Prepare build variables
id: prep
shell: bash
run: |
set -euo pipefail
echo "owner_lc=$(printf '%s' "${GITHUB_REPOSITORY_OWNER}" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
echo "platform_pair=$(printf '%s' "${{ matrix.platform }}" | tr '/' '-')" >> "$GITHUB_OUTPUT"
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ steps.prep.outputs.owner_lc }}/${{ matrix.image_name }}
labels: |
org.opencontainers.image.title=${{ matrix.image_name }}
org.opencontainers.image.description=${{ matrix.description }}
org.opencontainers.image.licenses=Apache-2.0
- name: Build smoke image
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile }}
platforms: ${{ matrix.platform }}
load: true
tags: codex-smoke/${{ matrix.image_name }}:local
cache-from: type=gha,scope=${{ matrix.image_name }}-${{ steps.prep.outputs.platform_pair }}
- name: Smoke test MCP Server image
if: matrix.image_name == 'gonavi-mcp-server'
shell: bash
run: |
set -euo pipefail
cid="$(docker run -d -p 8765:8765 -e GONAVI_MCP_HTTP_TOKEN=smoke-token codex-smoke/${{ matrix.image_name }}:local http)"
trap 'docker rm -f "$cid" >/dev/null 2>&1 || true' EXIT
for _ in 1 2 3 4 5 6 7 8 9 10; do
if curl --fail --silent http://127.0.0.1:8765/healthz >/dev/null; then
exit 0
fi
sleep 1
done
docker logs "$cid"
echo "MCP Server image smoke test failed" >&2
exit 1
- name: Smoke test Web Server image
if: matrix.image_name == 'gonavi-web-server'
shell: bash
run: |
set -euo pipefail
cookie_jar="$(mktemp)"
cid=""
trap 'rm -f "$cookie_jar"; if [[ -n "$cid" ]]; then docker rm -f "$cid" >/dev/null 2>&1 || true; fi' EXIT
cid="$(docker run -d -p 127.0.0.1::34116 -e GONAVI_WEB_ADDR=0.0.0.0:34116 -e GONAVI_WEB_PASSWORD=123456 codex-smoke/${{ matrix.image_name }}:local web-server)"
host_port="$(docker port "$cid" 34116/tcp | awk -F: 'NR == 1 { print $NF }')"
if [[ ! "$host_port" =~ ^[0-9]+$ ]]; then
docker logs "$cid"
echo "Failed to resolve the Web Server smoke-test port" >&2
exit 1
fi
base_url="http://127.0.0.1:${host_port}"
ready=false
for _ in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do
if curl --fail --silent "${base_url}/__gonavi/healthz" >/dev/null; then
ready=true
break
fi
sleep 1
done
if [[ "$ready" != "true" ]]; then
docker logs "$cid"
echo "Web Server image smoke test failed" >&2
exit 1
fi
status="$(curl --fail --silent --show-error "${base_url}/__gonavi/auth/status")"
grep -Fq '"configured":true' <<<"$status"
login="$(curl --fail --silent --show-error -c "$cookie_jar" -H 'Content-Type: application/json' -d '{"password":"123456","code":""}' "${base_url}/__gonavi/auth/login")"
grep -Fq '"success":true' <<<"$login"
app_status="$(curl --silent --show-error -o /dev/null -w '%{http_code}' -b "$cookie_jar" "${base_url}/")"
[[ "$app_status" == "200" ]]
- name: Smoke test build environment image
if: matrix.image_name == 'gonavi-build-env'
shell: bash
run: |
set -euo pipefail
docker run --rm codex-smoke/${{ matrix.image_name }}:local \
bash -lc 'go version && node -v && npm -v && wails version'
- name: Push by digest
id: push
uses: docker/build-push-action@v6
with:
context: .
file: ${{ matrix.dockerfile }}
platforms: ${{ matrix.platform }}
labels: ${{ steps.meta.outputs.labels }}
tags: ghcr.io/${{ steps.prep.outputs.owner_lc }}/${{ matrix.image_name }}
outputs: type=image,push-by-digest=true,name-canonical=true,push=true
cache-from: type=gha,scope=${{ matrix.image_name }}-${{ steps.prep.outputs.platform_pair }}
cache-to: type=gha,mode=max,scope=${{ matrix.image_name }}-${{ steps.prep.outputs.platform_pair }}
- name: Export digest
shell: bash
run: |
set -euo pipefail
mkdir -p "${{ runner.temp }}/digests"
digest="${{ steps.push.outputs.digest }}"
touch "${{ runner.temp }}/digests/${digest#sha256:}"
- name: Upload digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ matrix.image_name }}-${{ steps.prep.outputs.platform_pair }}
path: ${{ runner.temp }}/digests/*
if-no-files-found: error
retention-days: 1
merge:
name: Publish ${{ matrix.image_name }}
runs-on: ubuntu-latest
needs: build
strategy:
fail-fast: false
matrix:
include:
- image_name: gonavi-mcp-server
description: GoNavi MCP Server container image
- image_name: gonavi-web-server
description: GoNavi Web Server (browser UI) container image
- image_name: gonavi-build-env
description: GoNavi Linux build environment image
steps:
- name: Normalize image namespace
id: vars
shell: bash
run: |
set -euo pipefail
echo "owner_lc=$(printf '%s' "${GITHUB_REPOSITORY_OWNER}" | tr '[:upper:]' '[:lower:]')" >> "$GITHUB_OUTPUT"
- name: Download digests
uses: actions/download-artifact@v4
with:
path: ${{ runner.temp }}/digests
pattern: digests-${{ matrix.image_name }}-*
merge-multiple: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ steps.vars.outputs.owner_lc }}/${{ matrix.image_name }}
flavor: |
latest=false
tags: |
type=raw,value=dev-latest,enable=${{ github.ref == 'refs/heads/dev' }}
type=sha,format=short,prefix=dev-,enable=${{ github.ref == 'refs/heads/dev' }}
type=semver,pattern={{version}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=semver,pattern={{major}}.{{minor}},enable=${{ startsWith(github.ref, 'refs/tags/v') }}
type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') }}
labels: |
org.opencontainers.image.title=${{ matrix.image_name }}
org.opencontainers.image.description=${{ matrix.description }}
org.opencontainers.image.licenses=Apache-2.0
- name: Create manifest list and push
working-directory: ${{ runner.temp }}/digests
shell: bash
run: |
set -euo pipefail
docker buildx imagetools create \
$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf 'ghcr.io/${{ steps.vars.outputs.owner_lc }}/${{ matrix.image_name }}@sha256:%s ' *)
- name: Inspect image
shell: bash
run: |
set -euo pipefail
docker buildx imagetools inspect "ghcr.io/${{ steps.vars.outputs.owner_lc }}/${{ matrix.image_name }}:${{ steps.meta.outputs.version }}"

View File

@@ -1,22 +0,0 @@
name: Publish to WinGet
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
release_tag:
required: true
description: 'Tag of release you want to publish'
type: string
jobs:
publish:
runs-on: windows-latest
steps:
- uses: vedantmgoyal9/winget-releaser@v2
with:
identifier: Syngnat.GoNavi
installers-regex: 'GoNavi-windows-(amd64|arm64)\.exe$'
release-tag: ${{ inputs.release_tag || github.ref_name }}
token: ${{ secrets.WINGET_TOKEN }}

File diff suppressed because it is too large Load Diff

20
.gitignore vendored
View File

@@ -1,7 +1,7 @@
# IDE
.idea/
*.iml
.gitignore
# build / release artifacts
frontend/release/
**/release/
@@ -10,12 +10,28 @@ build/bin/
# wails / node artifacts (按需)
node_modules/
frontend/wailsjs/tsconfig.json
dist/
.DS_Store
.gemini-clipboard
.env
docker.*.env
GoNavi-Wails
GoNavi-Wails.exe
optional-driver-agent
optional-driver-agent.exe
.ace-tool/
.superpowers/
.claude/
tmpclaude-*
.gemini/
.playwright-mcp/
**/tmpclaude-*
docs/superpowers/
docs/需求追踪/
CLAUDE.md
**/CLAUDE.md
.worktrees
docs
.tmp_superpowers_edit

106
AI_EXTENSIONS_ROADMAP.md Normal file
View File

@@ -0,0 +1,106 @@
# AI 扩展能力路线
当前 GoNavi 的 AI 链路是:
1. 前端 `AIChatPanel` 组装 system messages。
2. 前端声明本地固定工具 `LOCAL_TOOLS`
3. 后端 `aiservice.Service` 只负责 Provider 配置、安全级别与模型转发。
这套结构已经足够承接“用户级提示词”,但要继续承接 MCP 和 Skills需要先把“提示词 / 工具 / 技能”三层职责拆开。
## 1. 用户级自定义提示词
已落地的方向:
- 配置存储在 `ai_config.json``userPromptSettings`
-`AISettingsModal` 提供编辑入口。
-`AIChatPanel` 在运行时追加为 system message。
建议长期保持 4 个层级:
- `global`: 所有 AI 会话统一追加。
- `database`: 数据库 / SQL 场景追加。
- `jvm`: JVM 资源浏览与分析场景追加。
- `jvmDiagnostic`: JVM 诊断命令规划场景追加。
这样既能满足“个人习惯”定制,也不会把所有场景揉成一条超长总提示词。
## 2. MCP 能力开放
目标不是把 MCP 做成新的聊天面板,而是把它变成“外部工具源”。
建议后续拆成三层:
1. `tool registry`
- 统一收口内置工具、本地扩展工具、MCP 工具。
- 对模型只暴露统一的 `tools[]`
2. `mcp server config`
- 保存 server 名称、transport、启动命令或 URL、超时、启用状态。
- 由后端维护生命周期与连通性。
3. `mcp runtime bridge`
- 负责 `list tools / call tool / errors / timeout / auth`
### MCP 是否需要单独 GitHub 仓库
不需要把“GoNavi 对 MCP 的支持”单独拆仓库。
更合理的边界是:
- `GoNavi 主仓库`
- 维护 MCP client、配置、UI、工具注册和运行时桥接。
- `单独仓库(可选)`
- 只在你要发布一个可复用的 MCP Server 时才有价值。
- 例如 `gonavi-mcp-sql-tools``gonavi-mcp-jvm-agent` 这类独立 server。
结论:
- “客户端支持 MCP” 不需要新仓库。
- “某个独立 MCP Server” 是否拆仓库,取决于它要不要单独发布、复用或部署。
## 3. Skills 设计
Skills 不建议直接等同于“另一种提示词”。
更合适的定义是:
- `skill manifest`
- 名称、说明、适用场景、是否默认启用。
- `skill prompt`
- 该技能追加的 system prompt / few-shot / 输出约束。
- `skill tool requirements`
- 该技能依赖哪些内置工具或 MCP 工具。
- `skill shortcuts`
- 可选地给欢迎卡片、斜杠命令或快速动作提供入口。
一个 Skill 本质上应该是“提示词 + 工具依赖 + 使用入口”的组合,而不是单独一段文案。
### Skills 是否需要单独 GitHub 仓库
第一阶段不需要。
建议顺序:
1. 先在 GoNavi 主仓库内把 Skills manifest/runtime 跑通。
2. 等格式稳定后再考虑增加“本地目录导入”或“Git 仓库导入”。
只有当你明确要做下面两件事时,独立仓库才值得:
- 把 Skills 当作社区共享资产分发。
- 让不同团队独立维护自己的 skill pack。
## 建议的下一步实现顺序
1. 抽出统一 `ToolRegistry`,让 `LOCAL_TOOLS` 不再硬编码在聊天面板内部。
2. 在 AI 设置中新增 `MCP Servers` 配置页。
3. 后端先支持最小 transport
- `stdio`
- `http/sse`(如果后续确认需要)
4. 在 AI 设置中新增 `Skills` 配置页。
5. 让 Skill 以 manifest 形式声明:
- `id`
- `name`
- `description`
- `systemPrompt`
- `requiredTools`
- `scopes`
6. 再决定是否增加“从 Git 仓库同步 MCP/Skills 包”的分发能力。

143
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1,143 @@
# Contributing Guide
Thank you for contributing to this project.
This repository uses `dev` as the default integration branch, while stable releases are published from `main` through `release/*` branches.
---
## Branch Model
- `dev`: default branch and day-to-day integration branch
- `main`: stable release branch
- `release/*`: release preparation branches for maintainers
- Recommended branch names for external contributors:
- `fix/*`: bug fixes
- `feature/*`: new features or enhancements
Maintainer release flow:
```text
feature/* / fix/* -> dev -> release/* -> main -> tag(vX.Y.Z)
```
---
## How External Contributors Should Open Pull Requests
Whether your branch is `fix/*` or `feature/*`, external contributors should **open pull requests directly against `dev`**.
Reasons:
- `dev` is the active integration branch, so changes can be reviewed in the same lane as ongoing work
- contributors align with the branch that triggers day-to-day validation and dev builds
- maintainers can cut `release/*` branches from `dev` without re-syncing external changes first
Recommended flow:
1. Fork this repository
2. Sync your fork with `dev` and create a branch from `dev` (`fix/*` or `feature/*` is recommended)
3. Make your changes and perform basic self-checks
4. Push the branch to your fork
5. Open a pull request against the `dev` branch of this repository
---
## Pull Request Requirements
Please keep each pull request focused, reviewable, and easy to validate.
Recommended expectations:
- one pull request should address one logical change
- use a clear title that explains the purpose
- include the following in the description:
- background and problem statement
- key changes
- impact scope
- validation method
- include screenshots or recordings for UI changes when helpful
- explicitly mention risk and rollback notes for compatibility, data, or build-chain changes
---
## Merge Strategy for Maintainers
Pull requests merged into `dev` should generally use **Squash and merge**.
Reasons:
- keeps `dev` history readable and easier to audit during active iteration
- maps each PR to a single integration commit on `dev`
- reduces cherry-pick and conflict cost before creating `release/*`
---
## Maintainer Sync Rules
Because external pull requests are merged directly into `dev`, maintainers should treat `dev` as the source branch for daily collaboration and release preparation.
### 1. Create `release/*` from `dev`
Before a release, create a release branch from `dev`, for example:
```bash
git checkout dev
git pull
git checkout -b release/v0.6.0
git push -u origin release/v0.6.0
```
### 2. Release from `release/*` back to `main`
When release preparation is complete, merge the release branch back into `main` and create a tag:
```bash
git checkout main
git pull
git merge release/v0.6.0
git push
git tag v0.6.0
git push origin v0.6.0
```
### 3. Sync `main` back to `dev` after release
After the release, sync `main` back into `dev` so the next iteration starts from the released code line:
```bash
git checkout dev
git pull
git merge main
git push
```
---
## Commit Message Recommendation
Keep commit messages clear and easy to audit.
Recommended format:
```text
emoji type(scope): concise description
```
Examples:
```text
🔧 fix(ci): fix DuckDB driver toolchain on Windows AMD64
✨ feat(redis): add Stream data browsing support
♻️ refactor(datagrid): optimize large-table horizontal scrolling and rendering
```
---
## Additional Notes
- Please include validation results for documentation, build-chain, or driver compatibility changes
- For larger changes, opening an issue or draft PR first is recommended
- Maintainers may ask contributors to narrow the scope if the change conflicts with the current project direction
Thank you for contributing.

143
CONTRIBUTING.zh-CN.md Normal file
View File

@@ -0,0 +1,143 @@
# 贡献指南
感谢你对本项目的贡献。
本项目当前采用“`dev` 作为默认集成分支,`main` 作为稳定发布分支,`release/*` 负责发版准备”的协作模型。为减少分支漂移与 PR 处理成本,请在提交贡献前先阅读本指南。
---
## 分支模型
- `dev`:默认分支,也是日常开发集成分支
- `main`:稳定发布分支
- `release/*`:发布准备分支,主要供维护者使用
- 外部贡献者建议使用以下分支命名:
- `fix/*`:问题修复
- `feature/*`:功能新增或增强
维护者发布流转如下:
```text
feature/* / fix/* -> dev -> release/* -> main -> tag(vX.Y.Z)
```
---
## 外部贡献者如何提 Pull Request
无论是 `fix/*` 还是 `feature/*`**外部贡献者统一直接向 `dev` 发起 Pull Request**。
这样做的原因:
- `dev` 是当前日常集成分支,评审与合入路径和维护者开发流程一致
- 外部贡献会直接进入触发日常校验和 dev 构建的分支
- 维护者可以直接从 `dev``release/*`,减少额外同步步骤
建议流程:
1. Fork 本仓库
2. 先同步你 fork 中的 `dev`,再从 `dev` 创建分支(建议命名为 `fix/*``feature/*`
3. 完成代码修改,并进行必要自检
4. 推送到你的远程分支
5. 向本仓库的 `dev` 分支发起 Pull Request
---
## Pull Request 要求
请尽量保证 PR 单一、清晰、可审核。
建议遵循以下要求:
- 一个 PR 只解决一类问题,避免混入无关改动
- 标题清晰说明改动目的
- 描述中说明:
- 背景与问题
- 变更点
- 影响范围
- 验证方式
- 如涉及 UI 调整,建议附截图或录屏
- 如涉及兼容性、数据变更或构建链路调整,请明确说明风险和回滚方式
---
## PR 合并策略(维护者)
`dev` 分支上的 PR 建议使用 **Squash and merge**
原因:
- 保持 `dev` 集成历史清晰、便于审查
- 每个 PR 在 `dev` 上对应一个明确的集成提交
- 降低发版前整理与冲突处理成本
---
## 维护者同步规则
由于外部 PR 会直接合入 `dev`,维护者应将 `dev` 作为日常协作与发版准备的主线分支。
### 1. 发版前从 dev 切 release/*
发布前由维护者基于 `dev` 创建发布分支,例如:
```bash
git checkout dev
git pull
git checkout -b release/v0.6.0
git push -u origin release/v0.6.0
```
### 2. release/* → main 发版
发布准备完成后,将 `release/*` 合并回 `main`,并打标签发布:
```bash
git checkout main
git pull
git merge release/v0.6.0
git push
git tag v0.6.0
git push origin v0.6.0
```
### 3. main 回流到 dev发版后必做
发布完成后,需要将 `main` 回流到 `dev`,确保下一轮开发从已发布代码线继续推进:
```bash
git checkout dev
git pull
git merge main
git push
```
---
## 提交建议
建议保持提交信息简洁、明确,便于维护者审查与后续追踪。
推荐格式:
```text
emoji type(scope): 中文描述
```
示例:
```text
🔧 fix(ci): 修复 Windows AMD64 下 DuckDB 驱动构建工具链
✨ feat(redis): 新增 Stream 类型数据浏览支持
♻️ refactor(datagrid): 优化大表横向滚动与渲染结构
```
---
## 其他说明
- 文档、构建链路、驱动兼容性相关改动,请尽量附带验证结果
- 若改动较大,建议先提 Issue 或 Draft PR先对齐方案再实施
- 如提交内容与项目当前架构方向冲突,维护者可能要求收敛范围后再合并
感谢你的贡献。

58
Dockerfile.build-env Normal file
View File

@@ -0,0 +1,58 @@
# syntax=docker/dockerfile:1.7
ARG GO_IMAGE=golang:1.25-bookworm
ARG NODE_IMAGE=node:20-bookworm
FROM ${NODE_IMAGE} AS node
FROM ${GO_IMAGE}
ARG WEBKIT_API=4.0
ARG WAILS_VERSION=v2.11.0
ENV DEBIAN_FRONTEND=noninteractive
ENV PATH=/go/bin:/usr/local/go/bin:${PATH}
# Debian's /etc/profile resets PATH in login shells (bash -l), dropping the Go
# toolchain; restore it via profile.d which is sourced after that reset.
RUN printf 'export PATH=/go/bin:/usr/local/go/bin:$PATH\n' > /etc/profile.d/10-gonavi-build-env.sh
# Copy only Node.js runtime assets so the Go toolchain from the base image
# stays intact for smoke tests and interactive build shells.
COPY --from=node /usr/local/bin/node /usr/local/bin/node
COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
# npm/npx/corepack are symlinks in the node image; COPY dereferences them into
# standalone scripts whose relative requires break, so recreate the symlinks.
RUN ln -sf ../lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm \
&& ln -sf ../lib/node_modules/npm/bin/npx-cli.js /usr/local/bin/npx \
&& ln -sf ../lib/node_modules/corepack/dist/corepack.js /usr/local/bin/corepack
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
bash \
build-essential \
ca-certificates \
curl \
file \
git \
make \
pkg-config \
python3 \
unzip \
zip \
libgtk-3-dev \
&& if [ "${WEBKIT_API}" = "4.1" ]; then \
apt-get install -y --no-install-recommends libwebkit2gtk-4.1-dev libsoup-3.0-dev; \
else \
apt-get install -y --no-install-recommends libwebkit2gtk-4.0-dev; \
fi \
&& go install github.com/wailsapp/wails/v2/cmd/wails@${WAILS_VERSION} \
&& rm -rf /var/lib/apt/lists/*
RUN mkdir -p /usr/share/doc/gonavi
COPY LICENSE NOTICE /usr/share/doc/gonavi/
WORKDIR /workspace
CMD ["bash"]

49
Dockerfile.mcp-server Normal file
View File

@@ -0,0 +1,49 @@
# syntax=docker/dockerfile:1.7
FROM --platform=$BUILDPLATFORM golang:1.25-bookworm AS builder
# Declared without defaults so BuildKit injects the real target platform;
# a default like TARGETARCH=amd64 would win and ship amd64 binaries to arm64.
ARG TARGETOS
ARG TARGETARCH
WORKDIR /src
COPY go.mod go.sum ./
COPY third_party/highgo-pq/go.mod ./third_party/highgo-pq/go.mod
COPY third_party/go-irisnative/go.mod third_party/go-irisnative/go.sum ./third_party/go-irisnative/
RUN go mod download
COPY . .
RUN ./tools/generate-driver-agent-revisions.sh --platform "${TARGETOS}/${TARGETARCH}"
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
go build -trimpath -ldflags="-s -w" -o /out/gonavi-mcp-server ./cmd/gonavi-mcp-server
FROM debian:bookworm-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates tzdata \
&& rm -rf /var/lib/apt/lists/* \
&& useradd --system --create-home --home-dir /var/lib/gonavi --uid 10001 gonavi \
&& mkdir -p /data /var/lib/gonavi/logs /usr/share/doc/gonavi \
&& chown -R gonavi:gonavi /data /var/lib/gonavi
COPY --from=builder /out/gonavi-mcp-server /usr/local/bin/gonavi-mcp-server
COPY --from=builder /src/LICENSE /usr/share/doc/gonavi/LICENSE
COPY --from=builder /src/NOTICE /usr/share/doc/gonavi/NOTICE
ENV HOME=/var/lib/gonavi \
GONAVI_DATA_ROOT=/data \
GONAVI_LOG_DIR=/var/lib/gonavi/logs \
GONAVI_MCP_HTTP_ADDR=0.0.0.0:8765 \
GONAVI_MCP_HTTP_PATH=/mcp \
GONAVI_MCP_SCHEMA_ONLY=true
VOLUME ["/data"]
EXPOSE 8765
USER gonavi
ENTRYPOINT ["/usr/local/bin/gonavi-mcp-server"]
CMD ["http"]

65
Dockerfile.web-server Normal file
View File

@@ -0,0 +1,65 @@
# syntax=docker/dockerfile:1.7
#
# GoNavi Web Server浏览器访问版镜像。
# 复用主程序 web-server 模式 + 嵌入 frontend/dist不依赖桌面 WebView/CGO。
FROM --platform=$BUILDPLATFORM node:20-bookworm AS frontend
# 使用 monorepo 布局frontend 源码通过相对路径 ../../../shared/i18n 引用仓库 shared/
# (见 frontend/src/i18n/catalog.ts、shared/i18n/messages.ts、shared/i18n/translate.ts
# 仅 COPY frontend/ 会导致 tsc 报 Cannot find module '../../../shared/i18n/*'。
WORKDIR /src/frontend
COPY frontend/package.json frontend/package-lock.json ./
COPY frontend/patches ./patches
RUN npm ci --no-audit --no-fund
COPY frontend/ ./
COPY shared/ /src/shared/
# wailsjs 绑定已入库;容器内只构建静态前端
RUN npm run build
FROM --platform=$BUILDPLATFORM golang:1.25-bookworm AS builder
ARG TARGETOS
ARG TARGETARCH
WORKDIR /src
COPY go.mod go.sum ./
COPY third_party/highgo-pq/go.mod ./third_party/highgo-pq/go.mod
COPY third_party/go-irisnative/go.mod third_party/go-irisnative/go.sum ./third_party/go-irisnative/
RUN go mod download
COPY . .
RUN ./tools/generate-driver-agent-revisions.sh --platform "${TARGETOS}/${TARGETARCH}"
COPY --from=frontend /src/frontend/dist ./frontend/dist
# 纯 Go 构建主程序web-server 模式不需要 Wails WebView / CGO
RUN CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH} \
go build -trimpath -ldflags="-s -w" -o /out/gonavi .
FROM debian:bookworm-slim
RUN apt-get update \
&& apt-get install -y --no-install-recommends ca-certificates tzdata curl \
&& rm -rf /var/lib/apt/lists/* \
&& useradd --system --create-home --home-dir /var/lib/gonavi --uid 10001 gonavi \
&& mkdir -p /data /var/lib/gonavi/logs /usr/share/doc/gonavi \
&& chown -R gonavi:gonavi /data /var/lib/gonavi
COPY --from=builder /out/gonavi /usr/local/bin/gonavi
COPY --from=builder /src/LICENSE /usr/share/doc/gonavi/LICENSE
COPY --from=builder /src/NOTICE /usr/share/doc/gonavi/NOTICE
ENV HOME=/var/lib/gonavi \
GONAVI_DATA_ROOT=/data \
GONAVI_LOG_DIR=/var/lib/gonavi/logs \
GONAVI_WEB_ADDR=0.0.0.0:34116
VOLUME ["/data"]
EXPOSE 34116
USER gonavi
# 未通过环境变量配置密码时,首次访问 /setup 初始化;认证状态落在数据目录 web_auth.json
ENTRYPOINT ["/usr/local/bin/gonavi"]
CMD ["web-server"]

181
NOTICE Normal file
View File

@@ -0,0 +1,181 @@
GoNavi
Copyright 2026 Syngnat
This product includes software developed by the following projects.
Apache RocketMQ
Copyright 2016-2022 The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
Apache IoTDB
Copyright 2018-2020 The Apache Software Foundation.
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
============================================================================
IoTDB project uses 4 Chinese Patents:
* 201711384490X
* 201810111712.9
* 201711322631.5
* 201711319331.1
According to the Apache 2.0 License. The owner of the patents, Tsinghua University,
grant the users the right to the use of patent under the requirement of Apache 2.0 License.
============================================================================
Apache Thrift
Copyright (C) 2006 - 2019, The Apache Software Foundation
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
go-yaml
Copyright 2011-2016 Canonical Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Eclipse Paho MQTT Go
This content is produced and maintained by the Eclipse Paho project.
* Project home: https://www.eclipse.org/paho/
Note that a separate mqtt v5 client (https://github.com/eclipse/paho.golang) also
exists. It is a full rewrite and deliberately incompatible with this library.
Eclipse Mosquitto trademarks of the Eclipse Foundation. Eclipse, and the
Eclipse Logo are registered trademarks of the Eclipse Foundation.
Paho is a trademark of the Eclipse Foundation. Eclipse, and the Eclipse Logo are
registered trademarks of the Eclipse Foundation.
All content is the property of the respective authors or their employers.
For more information regarding authorship of content, please consult the
listed source code repository logs.
This program and the accompanying materials are made available under the terms of the
Eclipse Public License v2.0 and Eclipse Distribution License v1.0 which accompany this
distribution.
The Eclipse Public License is available at
https://www.eclipse.org/legal/epl-2.0/
and the Eclipse Distribution License is available at
http://www.eclipse.org/org/documents/edl-v10.php.
For an explanation of what dual-licensing means to you, see:
https://www.eclipse.org/legal/eplfaq.php#DUALLIC
SPDX-License-Identifier: EPL-2.0 or BSD-3-Clause
The project maintains the following source code repository:
* https://github.com/eclipse/paho.mqtt.golang
This project makes use of the following third-party projects:
Go Programming Language and Standard Library
* License: BSD-style license (https://golang.org/LICENSE)
Go Networking
* License: BSD 3-Clause style license and patent grant.
* Project: https://cs.opensource.google/go/x/net
Go Sync
* License: BSD 3-Clause style license and patent grant.
* Project: https://cs.opensource.google/go/x/sync/
Gorilla Websockets v1.4.2
* License: BSD 2-Clause "Simplified" License
* Project: https://github.com/gorilla/websocket
Content may contain encryption software. The country in which you are currently
may have restrictions on the import, possession, and use, and/or re-export to
another country. BEFORE using any encryption software, please check the country's
laws, regulations and policies concerning the import, possession, and use, and/or
re-export of encryption software, to see if this is permitted.
Vendored HighGo pq-sm3 (derived from lib/pq)
Copyright (c) 2011-2013, 'pq' Contributors
Portions Copyright (C) 2011 Blake Mizerany
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
The vendored SCRAM implementation includes the following BSD 3-Clause notice:
Copyright (c) 2014 - Gustavo Niemeyer <gustavo@niemeyer.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors
may be used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Vendored InterSystems IRIS native driver
Copyright (c) 2025 Dmitry Maslennikov
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

461
README.md
View File

@@ -1,160 +1,343 @@
# GoNavi - 现代化的轻量级数据库管理工具
<p align="center">
<img src="assets/readme-logo.png" width="128" alt="GoNavi Logo" />
</p>
[![Go Version](https://img.shields.io/github/go-mod/go-version/Syngnat/GoNavi)](https://go.dev/)
[![Wails Version](https://img.shields.io/badge/Wails-v2-red)](https://wails.io)
[![React Version](https://img.shields.io/badge/React-v18-blue)](https://reactjs.org/)
[![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](LICENSE)
[![Build Status](https://img.shields.io/github/actions/workflow/status/Syngnat/GoNavi/release.yml?label=Build)](https://github.com/Syngnat/GoNavi/actions)
<h1 align="center">GoNavi</h1>
**GoNavi** 是一款基于 **Wails (Go)****React** 构建的现代化、高性能、跨平台数据库管理客户端。它旨在提供如原生应用般流畅的用户体验,同时保持极低的资源占用。
<p align="center">
<b>Navigate every data source — native speed, agent-ready, zero Electron bloat.</b>
</p>
相比于 Electron 应用GoNavi 的体积更小(~10MB启动速度更快内存占用更低。
<p align="center">
A high-performance cross-platform database client built with
<a href="https://wails.io">Wails</a> (Go) + <a href="https://react.dev">React</a>.
Desktop-first. MCP-ready. ~10MB class binaries.
</p>
<h2 align="center">📸 项目截图</h2>
<p align="center">
<a href="https://github.com/Syngnat/GoNavi/releases"><img src="https://img.shields.io/github/v/release/Syngnat/GoNavi?style=for-the-badge&color=8B5CF6" alt="Release" /></a>
<a href="https://github.com/Syngnat/GoNavi/releases"><img src="https://img.shields.io/github/downloads/Syngnat/GoNavi/total?style=for-the-badge&color=6366F1&label=downloads" alt="Downloads" /></a>
<a href="https://github.com/Syngnat/GoNavi/stargazers"><img src="https://img.shields.io/github/stars/Syngnat/GoNavi?style=for-the-badge&color=F59E0B" alt="Stars" /></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-22C55E?style=for-the-badge" alt="License" /></a>
</p>
<div align="center">
<img width="25%" alt="image" src="https://github.com/user-attachments/assets/341cda98-79a5-4198-90f3-1335131ccde0" />
<img width="25%" alt="image" src="https://github.com/user-attachments/assets/224a74e7-65df-4aef-9710-d8e82e3a70c1" />
<img width="25%" alt="image" src="https://github.com/user-attachments/assets/ec522145-5ceb-4481-ae46-a9251c89bdfc" />
<br />
<img width="25%" alt="image" src="https://github.com/user-attachments/assets/330ce49b-45f1-4919-ae14-75f7d47e5f73" />
<img width="14%" alt="image" src="https://github.com/user-attachments/assets/d15fa9e9-5486-423b-a0e9-53b467e45432" />
<img width="25%" alt="image" src="https://github.com/user-attachments/assets/f0c57590-d987-4ecf-89b2-64efad60b6d7" />
</div>
<p align="center">
<a href="https://go.dev"><img src="https://img.shields.io/github/go-mod/go-version/Syngnat/GoNavi?style=flat-square&logo=go&logoColor=white&label=Go" alt="Go" /></a>
<a href="https://wails.io"><img src="https://img.shields.io/badge/Wails-v2-red?style=flat-square" alt="Wails" /></a>
<a href="https://reactjs.org"><img src="https://img.shields.io/badge/React-18-61DAFB?style=flat-square&logo=react&logoColor=white" alt="React" /></a>
<a href="https://github.com/Syngnat/GoNavi/actions"><img src="https://img.shields.io/github/actions/workflow/status/Syngnat/GoNavi/release.yml?style=flat-square&label=Build" alt="Build" /></a>
</p>
<p align="center">
<b>Language</b>: English · <a href="README.zh-CN.md">简体中文</a>
&nbsp;·&nbsp;
<a href="https://github.com/Syngnat/GoNavi/releases"><b>⬇ Download</b></a>
·
<a href="#-quick-start"><b>⚡ Quick Start</b></a>
·
<a href="#-key-features"><b>✨ Features</b></a>
·
<a href="#-mcp--agents"><b>🤖 MCP</b></a>
</p>
---
## ✨ 核心特性
## Why GoNavi?
### 🚀 极致性能
- **零卡顿交互**:采用独创的 "幽灵拖拽" (Ghost Resizing) 技术,在包含数万行数据的表格中调整列宽,依然保持 60fps+ 的丝滑体验。
- **虚拟滚动**:轻松处理海量数据展示,拒绝卡顿。
Most database GUIs are Electron shells with megabytes of tax. GoNavi takes a different path:
### 🔌 多数据库支持
- **MySQL**:完整支持,涵盖数据编辑、结构管理与导入导出。
- **PostgreSQL**:数据查看与编辑支持,事务提交能力持续完善。
- **SQLite**:本地文件数据库支持。
- **Oracle**:基础数据访问与编辑支持。
- **Dameng达梦**:基础数据访问与编辑支持。
- **Kingbase人大金仓**:基础数据访问与编辑支持。
- **TDengine**:时序数据库连接、库表浏览与 SQL 查询支持。
- **Redis**Key/Value 浏览、命令执行、视图与编码切换。
- **自定义驱动**:支持配置 Driver/DSN 接入更多数据源。
- **SSH 隧道**:内置 SSH 隧道支持,安全连接内网数据库。
| | Typical Electron client | **GoNavi** |
|---|---|---|
| Runtime | Chromium + Node | **Go + native WebView** |
| Binary size | Hundreds of MB | **~10MB class** |
| Startup | Heavy | **Fast** |
| Memory | High baseline | **Lean** |
| AI / Agents | Bolt-on or absent | **First-class MCP + multi-provider AI** |
| Data sources | Mostly RDBMS | **SQL · Cache · Vector · MQ · Search · Time-series · Domestic DBs** |
### 📊 强大的数据管理 (DataGrid)
- **所见即所得编辑**:直接在表格中双击单元格修改数据。
- **批量事务操作**:支持批量新增、修改、删除,一键提交或回滚事务。
- **大字段编辑**:双击大字段自动打开弹窗编辑器,避免卡顿。
- **右键上下文菜单**:快速设置 NULL、复制/导出等操作。
- **智能上下文**:自动识别单表查询,解锁编辑功能;复杂查询自动切换为只读模式。
- **批量导出/备份**:支持表与数据库的批量导出/备份。
- **数据导出**:支持 CSV、Excel (XLSX)、JSON、Markdown 等格式。
### 🧰 批量导出/备份
- **数据库批量导出**:支持结构导出与结构+数据备份。
- **表批量导出**:支持多表一键导出/备份。
- **智能上下文检测**:自动判断目标范围,避免误操作。
### 🧩 Redis 视图与编码
- **视图模式切换**:自动/原始文本/UTF-8/十六进制多模式显示。
- **智能解码**:针对二进制值进行 UTF-8 质量判定与中文字符识别。
- **命令执行**:内置命令面板快速操作。
### 🔄 数据同步与导入导出
- **连接配置导入/导出**:支持配置 JSON 导入导出,便于团队共享。
- **数据同步**:内置数据同步面板,支持跨库同步任务配置。
### 🆙 在线更新
- **自动更新**:启动/定时/手动检查更新,自动下载并提示重启完成更新。
### 🧾 可观测性
- **SQL 执行日志**:实时查看 SQL 与执行耗时,便于排障与优化。
### 📝 智能 SQL 编辑器
- **Monaco Editor 内核**:集成 VS Code 同款编辑器,体验极佳。
- **智能补全**:自动感知当前连接上下文,提供数据库、表名、字段名的实时补全。
- **多标签页**:支持多窗口并行操作,像浏览器一样管理你的查询会话。
### 🎨 现代化 UI
- **Ant Design 5**:企业级 UI 设计语言。
- **暗黑模式**:内置深色/浅色主题切换,适应不同光照环境。
- **响应式布局**:灵活的侧边栏与布局调整。
> **One cockpit for MySQL, Postgres, Redis, Kafka, Milvus, OceanBase, ClickHouse…**
> Query, edit, audit, sync — and hand structured context to coding agents without leaking passwords off-host.
---
## 🛠️ 技术栈
## At a Glance
* **后端 (Backend)**: Go 1.24 + Wails v2
* **前端 (Frontend)**: React 18 + TypeScript + Vite
* **UI 框架**: Ant Design 5
* **状态管理**: Zustand
* **编辑器**: Monaco Editor
```text
┌──────────────────────────────────────────────────────────────────────┐
│ GoNavi Workbench │
│ ┌─────────────┐ ┌──────────────────┐ ┌─────────────────────────┐ │
│ │ Connections │ │ Monaco SQL + AI │ │ Virtualized DataGrid │ │
│ │ SSH / Proxy │ │ Schema context │ │ Batch edit · Export │ │
│ │ Drivers │ │ Slash commands │ │ Txn submit / rollback │ │
│ └─────────────┘ └────────┬─────────┘ └─────────────────────────┘ │
│ │ │
│ ┌─────────────▼─────────────┐ │
│ │ Go core · Audit · Sync │ │
│ │ MCP HTTP · Web Server │ │
│ └───────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
```
### Product screenshots
Each image is a **full GoNavi application window**, scaled proportionally for README display.
<p align="center">
<img src="https://raw.githubusercontent.com/Syngnat/GoNavi/dev/assets/screenshots/01-home-workbench.png" alt="GoNavi full window — connections, queries, and workbench" width="560" />
&nbsp;
<img src="https://raw.githubusercontent.com/Syngnat/GoNavi/dev/assets/screenshots/04-ai-assistant.png" alt="GoNavi full window — AI assistant with schema context" width="560" />
</p>
<p align="center">
<img src="https://raw.githubusercontent.com/Syngnat/GoNavi/dev/assets/screenshots/06-new-connection.png" alt="GoNavi full window — new connection data-source selector" width="560" />
</p>
<p align="center"><sub>Real desktop captures · full window</sub></p>
---
## 📦 安装与运行
## ✨ Key Features
### 前置要求
* [Go](https://go.dev/dl/) 1.21+
* [Node.js](https://nodejs.org/) 18+
* [Wails CLI](https://wails.io/docs/gettingstarted/installation): `go install github.com/wailsapp/wails/v2/cmd/wails@latest`
<table>
<tr>
<td width="50%" valign="top">
### 开发模式
### 🤖 AI that knows your schema
- OpenAI · Gemini · Claude · custom OpenAI-compatible APIs
- Attach live table schemas as context
- Slash commands: generate SQL, explain, optimize, review
- **MCP**: install into Claude Code / Codex, or Streamable HTTP for remote agents
- Secrets stay on the GoNavi host — agents get tools, not raw passwords
</td>
<td width="50%" valign="top">
### ⚡ Built for large data
- Virtualized DataGrid for heavy result sets
- In-place cell edit · batch CRUD · transaction submit/rollback
- Large-field popup editor · smart read/write modes
- Export: CSV · XLSX · JSON · Markdown
- Monaco editor with context-aware completion
</td>
</tr>
<tr>
<td width="50%" valign="top">
### 🔌 Connectivity without drama
- URI generate / parse
- SSH tunnel · proxy
- Connection JSON import / export
- On-demand optional driver agents
- Custom Driver + DSN extensibility
</td>
<td width="50%" valign="top">
### 🛡️ Observable & shippable
- SQL execution logs with timing
- Audit center (redacted by default, retention, export)
- Desktop + experimental **Web Server** mode
- Docker / K8s / Helm / Podman packaging
- Auto update checks · multi-arch releases
</td>
</tr>
</table>
### 🧩 Stack
`Go 1.24` · `Wails v2` · `React 18` · `TypeScript` · `Vite` · `Ant Design 5` · `Zustand` · `Monaco`
---
## 🗄 Supported Data Sources
> **Built-in** — ready out of the box · **Optional agent** — install via Driver Manager
| | |
|---|---|
| **Built-in** | MySQL · GoldenDB · PostgreSQL · Oracle · Redis · Chroma · Qdrant · Milvus · RocketMQ · MQTT · Kafka · RabbitMQ |
| **Optional** | MariaDB · Doris · StarRocks · Sphinx · SQL Server · SQLite · DuckDB · OceanBase · Dameng · Kingbase · HighGo · Vastbase · OpenGauss · GaussDB · IRIS · MongoDB · TDengine · IoTDB · ClickHouse · Trino · Elasticsearch · Custom Driver/DSN |
<details>
<summary><b>Full capability matrix</b></summary>
| Category | Data Source | Driver Mode | Typical Capabilities |
|---|---|---|---|
| Relational | MySQL | Built-in | Schema browsing, SQL query, data editing, export/backup |
| Domestic DB | GoldenDB | Built-in | MySQL-compatible query workflow and distributed transaction scenarios |
| Relational | PostgreSQL | Built-in | Schema browsing, SQL query, data editing, object management |
| Relational | Oracle | Built-in | Query execution, object browsing, data editing |
| Cache | Redis | Built-in | Key browsing, command execution, encoding/view switch |
| Vector Database | Chroma | Built-in | Collection browsing, vector retrieval, metadata filtering |
| Vector Database | Qdrant | Built-in | Collection browsing, vector search, payload filtering |
| Vector Database | Milvus | Built-in | Collection browsing, vector search, scalar filtering |
| Message Queue | RocketMQ | Built-in | Topic browsing, consumer-group inspection, message-oriented workflow |
| Message Queue | MQTT | Built-in | Broker and topic-filter workflow with QoS-aware connection settings |
| Message Queue | Kafka | Built-in | Topic browsing, broker metadata, consumer-group workflow |
| Message Queue | RabbitMQ | Built-in | Queue/exchange browsing, virtual host inspection, management API workflow |
| Relational | MariaDB | Optional driver agent | Querying, object management, data editing |
| Relational | Doris | Optional driver agent | Querying, object browsing, SQL execution |
| Columnar Analytics | StarRocks | Optional driver agent | Querying, object browsing, SQL execution |
| Search | Sphinx | Optional driver agent | SphinxQL querying and object browsing |
| Relational | SQL Server | Optional driver agent | Schema browsing, SQL query, object management |
| File-based | SQLite | Optional driver agent | Local DB browsing, editing, export |
| File-based | DuckDB | Optional driver agent | Large-table query, pagination, file-DB workflow |
| Domestic DB | OceanBase | Optional driver agent | MySQL / Oracle tenant access, object browsing, query workflow |
| Domestic DB | Dameng | Optional driver agent | Querying, object browsing, data editing |
| Domestic DB | Kingbase | Optional driver agent | Querying, object browsing, data editing |
| Domestic DB | HighGo | Optional driver agent | Querying, object browsing, data editing |
| Domestic DB | Vastbase | Optional driver agent | Querying, object browsing, data editing |
| Domestic DB | OpenGauss | Optional driver agent | PostgreSQL-like schema browsing, SQL query, object management |
| Domestic DB | GaussDB | Optional driver agent | PostgreSQL-like schema browsing, SQL query, object management |
| Multi-model | InterSystems IRIS | Optional driver agent | Namespace browsing, SQL query, object management |
| Document | MongoDB | Optional driver agent | Document query, collection browsing, connection management |
| Time-series | TDengine | Optional driver agent | Time-series schema browsing and querying |
| Time-series | Apache IoTDB | Optional driver agent | Storage group / device / timeseries browsing and querying |
| Columnar Analytics | ClickHouse | Optional driver agent | Analytical query, object browsing, SQL execution |
| Federated Query | Trino | Optional driver agent | Cross-source SQL via multiple catalogs, `catalog.schema` browsing, SQL execution |
| Search | Elasticsearch | Optional driver agent | Index browsing, mapping inspection, JSON DSL / query_string search |
| Extensibility | Custom Driver/DSN | Custom | Extend to more data sources via Driver + DSN |
</details>
---
## 🚀 Quick Start
### Prerequisites
- [Go](https://go.dev/dl/) 1.21+
- [Node.js](https://nodejs.org/) 18+
- [Wails CLI](https://wails.io/docs/gettingstarted/installation)
```bash
go install github.com/wailsapp/wails/v2/cmd/wails@v2.11.0
```
### Develop
```bash
# 克隆项目
git clone https://github.com/Syngnat/GoNavi.git
cd GoNavi
# 启动开发服务器 (支持热重载)
wails dev
wails dev # full hot reload
node tools/wails-fast-dev.mjs # faster when Go exports unchanged
node tools/wails-fast-dev.mjs --refresh-bindings # after Go export signature changes
```
### 编译构建
### Build
```bash
# 构建当前平台的可执行文件
wails build
# 清理并构建 (推荐发布前使用)
wails build -clean
wails build -clean # clean build before release
```
构建产物将位于 `build/bin` 目录下。
Artifacts → `build/bin`.
### 跨平台编译 (GitHub Actions)
### Prefer a binary?
本项目内置了 GitHub Actions 流水线Push `v*` 格式的 Tag 即可自动触发构建并发布 Release。
支持构建:
* macOS (AMD64 / ARM64)
* Windows (AMD64)
* Linux (AMD64提供 WebKitGTK 4.0 与 4.1 变体产物)
Grab the latest build from **[Releases](https://github.com/Syngnat/GoNavi/releases)**
(macOS AMD64/ARM64 · Windows AMD64 · Linux WebKitGTK 4.0/4.1).
---
## ❓ 常见问题 (Troubleshooting)
## 🌐 Web Server (Experimental)
### macOS 提示 "应用已损坏,无法打开"
Same Go backend + React UI over HTTP — **not** a containerized Wails window.
由于本项目尚未购买 Apple 开发者证书进行签名NotarizationmacOS 的 Gatekeeper 安全机制可能会拦截应用的运行。请按照以下步骤解决:
```powershell
go build .
.\GoNavi-Wails.exe web-server --addr 127.0.0.1:34116
```
1. 将下载的 `GoNavi.app` 拖入 **应用程序** 文件夹。
2. 打开 **终端 (Terminal)**
3. 复制并执行以下命令(输入密码时不会显示):
```bash
sudo xattr -rd com.apple.quarantine /Applications/GoNavi.app
```
4. 或者:在 Finder 中右键点击应用图标,按住 `Control` 键选择 **打开**,然后在弹出的窗口中再次点击 **打开**。
- First visit → `/setup` (admin password; optional Google Authenticator)
- Bridge: `window.go.*` / `window.runtime.*` → HTTP / SSE
- Sessions, recovery codes, login rate limits
### Linux 启动报错缺少 `libwebkit2gtk` / `libjavascriptcoregtk`
#### Docker / Podman
GoNavi 的 Linux 二进制依赖系统 WebKitGTK 运行库。不同发行版默认版本不同:
```bash
cp docker.web-server.env.example docker.web-server.env
# set GONAVI_HOST_DATA_ROOT (absolute path)
# optional: GONAVI_WEB_PASSWORD (min 6 chars)
docker compose --env-file docker.web-server.env -f docker-compose.web-server.yml up -d
```
- Debian 13 / Ubuntu 24.04 及更新版本:通常为 WebKitGTK 4.1
- Ubuntu 22.04 / Debian 12 等:通常为 WebKitGTK 4.0
Open `http://127.0.0.1:34116`. Mount the active data root at `/data`
(`connections.json`, `daily_secrets.json`, optional `drivers/`).
Auth state → `web_auth.json`.
如果启动时报错(如 `libwebkit2gtk-4.0.so.37: cannot open shared object file`),请按系统安装对应依赖后重试:
> **Do not expose an unhardened web entry to the public internet.** Use reverse proxy + HTTPS in production.
Health: `GET /__gonavi/healthz`
Local source build: add `-f docker-compose.web-server.local.yml --build`.
After changing env passwords:
```bash
docker compose --env-file docker.web-server.env -f docker-compose.web-server.yml up -d --force-recreate
```
(`docker restart` does **not** reload env files.)
---
## 🤖 MCP & Agents
Ship schema tools to agents without shipping your vault:
```bash
cp docker.mcp-server.env.example docker.mcp-server.env
docker compose --env-file docker.mcp-server.env -f docker-compose.mcp-server.yml up -d
```
| Surface | Entry |
|---|---|
| MCP container | `docker-compose.mcp-server.yml``ghcr.io/syngnat/gonavi-mcp-server` |
| Web UI container | `docker-compose.web-server.yml``ghcr.io/syngnat/gonavi-web-server` |
| Podman / Quadlet | [deploy/podman/gonavi-mcp-server](deploy/podman/gonavi-mcp-server) |
| Kubernetes | [deploy/k8s/gonavi-mcp-server](deploy/k8s/gonavi-mcp-server) |
| Helm | [deploy/helm/gonavi-mcp-server](deploy/helm/gonavi-mcp-server) |
| Build-only image | `Dockerfile.build-env``ghcr.io/syngnat/gonavi-build-env` |
Safety defaults: remote `schema-only` omits `execute_sql`; mutating SQL requires explicit `allowMutating=true`.
Details: [cmd/gonavi-mcp-server/README.md](cmd/gonavi-mcp-server/README.md).
### Linux build environment only
```bash
docker build -f Dockerfile.build-env -t gonavi-build-env:local .
docker run --rm -it -v "$PWD:/workspace" -w /workspace gonavi-build-env:local bash
```
---
## 📦 Release Pipeline
Push a `v*` tag → GitHub Actions builds multi-arch releases.
Notes auto-generated from merged PRs via `.github/release.yaml`.
---
## 🛠 Troubleshooting
<details>
<summary><b>macOS: “App is damaged and cant be opened”</b></summary>
Without Apple notarization, Gatekeeper may block the app:
```bash
sudo xattr -rd com.apple.quarantine /Applications/GoNavi.app
```
Or right-click → Open (Control-click flow). Move the app to **Applications** first.
</details>
<details>
<summary><b>Linux: missing WebKitGTK</b></summary>
```bash
# Debian 13 / Ubuntu 24.04+
@@ -166,20 +349,52 @@ sudo apt-get update
sudo apt-get install -y libgtk-3-0 libwebkit2gtk-4.0-37 libjavascriptcoregtk-4.0-18
```
如果你使用的是 Release 中带 `-WebKit41` 后缀的 Linux 产物,请优先在 Debian 13 / Ubuntu 24.04+ 上使用;普通 Linux 产物更适合 WebKitGTK 4.0 运行环境。
Artifacts with `-WebKit41` prefer Debian 13 / Ubuntu 24.04+.
</details>
<details>
<summary><b>Linux: Chinese glyphs as tofu boxes</b></summary>
```bash
sudo apt-get update
sudo apt-get install -y fonts-noto-cjk fonts-wqy-microhei
fc-cache -fv
```
</details>
---
## 🤝 贡献指南
## 🤝 Contributing
欢迎提交 Issue 和 Pull Request
Issues and PRs welcome. Branch from **`dev`**, PR against **`dev`**.
1. Fork 本仓库
2. 创建你的特性分支 (`git checkout -b feature/AmazingFeature`)
3. 提交你的改动 (`git commit -m 'feat: Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 开启一个 Pull Request
- [CONTRIBUTING.md](CONTRIBUTING.md)
## 📄 开源协议
---
本项目采用 [Apache-2.0 协议](LICENSE) 开源。
## ⭐ Star History
<a href="https://www.star-history.com/?repos=Syngnat%2FGoNavi&type=date&legend=top-left">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/image?repos=Syngnat/GoNavi&type=date&theme=dark&legend=top-left" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/image?repos=Syngnat/GoNavi&type=date&legend=top-left" />
<img alt="Star History Chart" src="https://api.star-history.com/image?repos=Syngnat/GoNavi&type=date&legend=top-left" />
</picture>
</a>
---
## Links
- [linux.do](https://linux.do/)
- [AIBook](https://aibook.ren/)
## License
[Apache-2.0](LICENSE)
<p align="center">
<sub>Built for people who live in SQL, schemas, and agent loops.</sub>
</p>

399
README.zh-CN.md Normal file
View File

@@ -0,0 +1,399 @@
<p align="center">
<img src="assets/readme-logo.png" width="128" alt="GoNavi Logo" />
</p>
<h1 align="center">GoNavi</h1>
<p align="center">
<b>一套客户端,航行全部数据源 — 原生性能 · Agent 就绪 · 告别 Electron 膨胀。</b>
</p>
<p align="center">
基于 <a href="https://wails.io">Wails</a>Go+ <a href="https://react.dev">React</a> 的跨平台数据库工作台。
桌面优先MCP 原生,二进制约 <b>10MB</b> 量级。
</p>
<p align="center">
<a href="https://github.com/Syngnat/GoNavi/releases"><img src="https://img.shields.io/github/v/release/Syngnat/GoNavi?style=for-the-badge&color=8B5CF6" alt="Release" /></a>
<a href="https://github.com/Syngnat/GoNavi/releases"><img src="https://img.shields.io/github/downloads/Syngnat/GoNavi/total?style=for-the-badge&color=6366F1&label=downloads" alt="Downloads" /></a>
<a href="https://github.com/Syngnat/GoNavi/stargazers"><img src="https://img.shields.io/github/stars/Syngnat/GoNavi?style=for-the-badge&color=F59E0B" alt="Stars" /></a>
<a href="LICENSE"><img src="https://img.shields.io/badge/License-Apache%202.0-22C55E?style=for-the-badge" alt="License" /></a>
</p>
<p align="center">
<a href="https://go.dev"><img src="https://img.shields.io/github/go-mod/go-version/Syngnat/GoNavi?style=flat-square&logo=go&logoColor=white&label=Go" alt="Go" /></a>
<a href="https://wails.io"><img src="https://img.shields.io/badge/Wails-v2-red?style=flat-square" alt="Wails" /></a>
<a href="https://reactjs.org"><img src="https://img.shields.io/badge/React-18-61DAFB?style=flat-square&logo=react&logoColor=white" alt="React" /></a>
<a href="https://github.com/Syngnat/GoNavi/actions"><img src="https://img.shields.io/github/actions/workflow/status/Syngnat/GoNavi/release.yml?style=flat-square&label=Build" alt="Build" /></a>
</p>
<p align="center">
<b>语言</b><a href="README.md">English</a> · 简体中文
&nbsp;·&nbsp;
<a href="https://github.com/Syngnat/GoNavi/releases"><b>⬇ 下载</b></a>
·
<a href="#-快速开始"><b>⚡ 快速开始</b></a>
·
<a href="#-核心能力"><b>✨ 特性</b></a>
·
<a href="#-mcp--agents"><b>🤖 MCP</b></a>
</p>
---
## 为什么是 GoNavi
市面上大量数据库 GUI 仍是 Electron 壳体积大、启动慢、内存高。GoNavi 换了一条路:
| | 常见 Electron 客户端 | **GoNavi** |
|---|---|---|
| 运行时 | Chromium + Node | **Go + 系统 WebView** |
| 体积 | 动辄数百 MB | **约 10MB 量级** |
| 启动 | 偏重 | **更快** |
| 内存 | 基线高 | **更轻** |
| AI / Agent | 外挂或缺失 | **MCP + 多模型一等公民** |
| 数据源 | 以 RDBMS 为主 | **SQL · 缓存 · 向量 · 消息 · 搜索 · 时序 · 国产库** |
> **MySQL、Postgres、Redis、Kafka、Milvus、OceanBase、ClickHouse… 一个工作台打通。**
> 查询、编辑、审计、同步;把结构化上下文交给编码 Agent密码仍留在本机。
---
## 一眼看懂
```text
┌──────────────────────────────────────────────────────────────────────┐
│ GoNavi 工作台 │
│ ┌─────────────┐ ┌──────────────────┐ ┌─────────────────────────┐ │
│ │ 连接管理 │ │ Monaco SQL + AI │ │ 虚拟滚动 DataGrid │ │
│ │ SSH / 代理 │ │ 表结构上下文 │ │ 批量编辑 · 导出 │ │
│ │ 驱动代理 │ │ 快捷指令 │ │ 事务提交 / 回滚 │ │
│ └─────────────┘ └────────┬─────────┘ └─────────────────────────┘ │
│ │ │
│ ┌─────────────▼─────────────┐ │
│ │ Go 核心 · 审计 · 同步 │ │
│ │ MCP HTTP · Web Server │ │
│ └───────────────────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
```
### 产品截图
每张均为 **完整 GoNavi 应用窗口**,再通过 README 宽度控件等比例缩小展示。
<p align="center">
<img src="https://raw.githubusercontent.com/Syngnat/GoNavi/dev/assets/screenshots/01-home-workbench.png" alt="GoNavi 全窗口 — 连接、查询与工作台" width="560" />
&nbsp;
<img src="https://raw.githubusercontent.com/Syngnat/GoNavi/dev/assets/screenshots/04-ai-assistant.png" alt="GoNavi 全窗口 — 带表结构上下文的 AI 助手" width="560" />
</p>
<p align="center">
<img src="https://raw.githubusercontent.com/Syngnat/GoNavi/dev/assets/screenshots/06-new-connection.png" alt="GoNavi 全窗口 — 新建连接的数据源选择器" width="560" />
</p>
<p align="center"><sub>真实桌面全窗口截图</sub></p>
---
## ✨ 核心能力
<table>
<tr>
<td width="50%" valign="top">
### 🤖 懂表结构的 AI
- OpenAI · Gemini · Claude · 自定义 OpenAI 兼容 API
- 将当前库表结构注入对话上下文
- 快捷指令:生成 SQL、解释、优化、表设计评审
- **MCP**:一键装到 Claude Code / Codex或 Streamable HTTP 给远端 Agent
- 连接与密码留在运行 GoNavi 的主机上
</td>
<td width="50%" valign="top">
### ⚡ 大数据仍流畅
- 虚拟滚动 DataGrid扛住大结果集
- 单元格编辑 · 批量增删改 · 事务提交/回滚
- 大字段弹窗 · 按查询智能切换读写
- 导出CSV · XLSX · JSON · Markdown
- Monaco + 库/表/字段上下文补全
</td>
</tr>
<tr>
<td width="50%" valign="top">
### 🔌 连接与驱动
- URI 生成 / 解析
- SSH 隧道 · 代理
- 连接配置 JSON 导入导出
- 可选驱动代理按需安装
- Custom Driver + DSN 扩展
</td>
<td width="50%" valign="top">
### 🛡️ 可观测 & 可交付
- SQL 执行日志(含耗时)
- 审计中心(默认脱敏、保留策略、导出)
- 桌面端 + 实验性 **Web Server**
- Docker / K8s / Helm / Podman
- 更新检查 · 多架构发布
</td>
</tr>
</table>
### 🧩 技术栈
`Go 1.24` · `Wails v2` · `React 18` · `TypeScript` · `Vite` · `Ant Design 5` · `Zustand` · `Monaco`
---
## 🗄 支持的数据源
> **内置**:开箱即用 · **可选驱动代理**:在驱动管理中安装启用
| | |
|---|---|
| **内置** | MySQL · GoldenDB · PostgreSQL · Oracle · Redis · Chroma · Qdrant · Milvus · RocketMQ · MQTT · Kafka · RabbitMQ |
| **可选** | MariaDB · Doris · StarRocks · Sphinx · SQL Server · SQLite · DuckDB · OceanBase · 达梦 · 人大金仓 · 瀚高 · 海量 · openGauss · GaussDB · IRIS · MongoDB · TDengine · IoTDB · ClickHouse · Trino · Elasticsearch · Custom Driver/DSN |
<details>
<summary><b>完整能力矩阵</b></summary>
| 类别 | 数据源 | 驱动模式 | 典型能力 |
|---|---|---|---|
| 关系型 | MySQL | 内置 | 库表浏览、SQL 查询、数据编辑、导出/备份 |
| 国产数据库 | GoldenDB | 内置 | MySQL 兼容查询工作流、分布式事务场景 |
| 关系型 | PostgreSQL | 内置 | 库表浏览、SQL 查询、数据编辑、对象管理 |
| 关系型 | Oracle | 内置 | 连接查询、对象浏览、数据编辑 |
| 缓存 | Redis | 内置 | Key 浏览、命令执行、编码/视图切换 |
| 向量数据库 | Chroma | 内置 | Collection 浏览、向量检索、元数据过滤 |
| 向量数据库 | Qdrant | 内置 | Collection 浏览、向量搜索、Payload 过滤 |
| 向量数据库 | Milvus | 内置 | Collection 浏览、向量搜索、标量过滤 |
| 消息队列 | RocketMQ | 内置 | Topic 浏览、消费组检查、消息型工作流 |
| 消息队列 | MQTT | 内置 | Broker / Topic Filter 工作流与 QoS 连接配置 |
| 消息队列 | Kafka | 内置 | Topic 浏览、Broker 元数据、消费组工作流 |
| 消息队列 | RabbitMQ | 内置 | Queue / Exchange 浏览、Virtual Host 检查、Management API 工作流 |
| 关系型 | MariaDB | 可选驱动代理 | 连接查询、对象管理、数据编辑 |
| 关系型 | Doris | 可选驱动代理 | 连接查询、对象浏览、SQL 执行 |
| 列式分析 | StarRocks | 可选驱动代理 | 连接查询、对象浏览、SQL 执行 |
| 搜索 | Sphinx | 可选驱动代理 | SphinxQL 查询与对象浏览 |
| 关系型 | SQL Server | 可选驱动代理 | 库表浏览、SQL 查询、对象管理 |
| 文件型 | SQLite | 可选驱动代理 | 本地文件库浏览、编辑、导出 |
| 文件型 | DuckDB | 可选驱动代理 | 大表查询、分页浏览、文件库管理 |
| 国产数据库 | OceanBase | 可选驱动代理 | MySQL / Oracle 租户接入、对象浏览、查询工作流 |
| 国产数据库 | Dameng | 可选驱动代理 | 连接查询、对象浏览、数据编辑 |
| 国产数据库 | Kingbase | 可选驱动代理 | 连接查询、对象浏览、数据编辑 |
| 国产数据库 | HighGo | 可选驱动代理 | 连接查询、对象浏览、数据编辑 |
| 国产数据库 | Vastbase | 可选驱动代理 | 连接查询、对象浏览、数据编辑 |
| 国产数据库 | OpenGauss | 可选驱动代理 | 类 PostgreSQL 的库表浏览、SQL 查询、对象管理 |
| 国产数据库 | GaussDB | 可选驱动代理 | 类 PostgreSQL 的库表浏览、SQL 查询、对象管理 |
| 多模型数据库 | InterSystems IRIS | 可选驱动代理 | Namespace 浏览、SQL 查询、对象管理 |
| 文档型 | MongoDB | 可选驱动代理 | 文档查询、集合浏览、连接管理 |
| 时序 | TDengine | 可选驱动代理 | 时序库表浏览、查询分析 |
| 时序 | Apache IoTDB | 可选驱动代理 | Storage Group / Device / Timeseries 浏览与查询 |
| 列式分析 | ClickHouse | 可选驱动代理 | 分析查询、对象浏览、SQL 执行 |
| 联邦查询 | Trino | 可选驱动代理 | 跨多数据源联邦 SQL、`catalog.schema` 浏览、SQL 执行 |
| 搜索 | Elasticsearch | 可选驱动代理 | 索引浏览、Mapping 检查、JSON DSL / query_string 查询 |
| 扩展接入 | Custom Driver/DSN | 自定义 | 通过 Driver + DSN 接入更多数据源 |
</details>
---
## 🚀 快速开始
### 前置要求
- [Go](https://go.dev/dl/) 1.21+
- [Node.js](https://nodejs.org/) 18+
- [Wails CLI](https://wails.io/docs/gettingstarted/installation)
```bash
go install github.com/wailsapp/wails/v2/cmd/wails@v2.11.0
```
### 开发
```bash
git clone https://github.com/Syngnat/GoNavi.git
cd GoNavi
wails dev # 完整热重载
node tools/wails-fast-dev.mjs # Go 导出签名未变时更快
node tools/wails-fast-dev.mjs --refresh-bindings # 修改导出方法签名后刷新绑定
```
### 构建
```bash
wails build
wails build -clean # 发布前推荐
```
产物位于 `build/bin`
### 只想直接用?
前往 **[Releases](https://github.com/Syngnat/GoNavi/releases)** 下载
macOS AMD64/ARM64 · Windows AMD64 · Linux WebKitGTK 4.0/4.1)。
---
## 🌐 Web Server实验中
同一套 Go 后端 + React 前端提供浏览器访问(**不是**把 Wails 桌面窗口容器化)。
```powershell
go build .
.\GoNavi-Wails.exe web-server --addr 127.0.0.1:34116
```
- 首次访问进入 `/setup`(管理员密码;可选 Google Authenticator
- 桥接:`window.go.*` / `window.runtime.*` → HTTP / SSE
- Session、恢复码、登录限流
#### Docker / Podman
```bash
cp docker.web-server.env.example docker.web-server.env
# 设置 GONAVI_HOST_DATA_ROOT 为绝对路径
# 可选GONAVI_WEB_PASSWORD至少 6 位)
docker compose --env-file docker.web-server.env -f docker-compose.web-server.yml up -d
```
打开 `http://127.0.0.1:34116`。将活动数据目录挂载到 `/data`
`connections.json``daily_secrets.json`,可选 `drivers/`)。
认证状态写入 `web_auth.json`
> **不要把未加固的 Web 入口直接暴露到公网。** 生产环境请配合反向代理与 HTTPS。
健康检查:`GET /__gonavi/healthz`
本地源码构建:叠加 `-f docker-compose.web-server.local.yml --build`
修改 env 密码后需重建容器:
```bash
docker compose --env-file docker.web-server.env -f docker-compose.web-server.yml up -d --force-recreate
```
`docker restart` **不会**重新加载 env 文件。)
---
## 🤖 MCP & Agents
把结构工具交给 Agent而不是把密钥交出去
```bash
cp docker.mcp-server.env.example docker.mcp-server.env
docker compose --env-file docker.mcp-server.env -f docker-compose.mcp-server.yml up -d
```
| 形态 | 入口 |
|---|---|
| MCP 容器 | `docker-compose.mcp-server.yml``ghcr.io/syngnat/gonavi-mcp-server` |
| Web UI 容器 | `docker-compose.web-server.yml``ghcr.io/syngnat/gonavi-web-server` |
| Podman / Quadlet | [deploy/podman/gonavi-mcp-server](deploy/podman/gonavi-mcp-server) |
| Kubernetes | [deploy/k8s/gonavi-mcp-server](deploy/k8s/gonavi-mcp-server) |
| Helm | [deploy/helm/gonavi-mcp-server](deploy/helm/gonavi-mcp-server) |
| 仅构建镜像 | `Dockerfile.build-env``ghcr.io/syngnat/gonavi-build-env` |
安全默认:远端 `schema-only` 不暴露 `execute_sql`;变更类 SQL 需显式 `allowMutating=true`
详见:[cmd/gonavi-mcp-server/README.md](cmd/gonavi-mcp-server/README.md)。
### 仅 Linux 构建环境
```bash
docker build -f Dockerfile.build-env -t gonavi-build-env:local .
docker run --rm -it -v "$PWD:/workspace" -w /workspace gonavi-build-env:local bash
```
---
## 📦 发布流水线
推送 `v*` Tag → GitHub Actions 自动多架构构建。
Release 说明按 `.github/release.yaml` 从已合并 PR 生成。
---
## 🛠 常见问题
<details>
<summary><b>macOS提示「应用已损坏无法打开」</b></summary>
未做 Apple Notarization 时Gatekeeper 可能拦截:
```bash
sudo xattr -rd com.apple.quarantine /Applications/GoNavi.app
```
也可在 Finder 中右键打开。建议先移到「应用程序」。
</details>
<details>
<summary><b>Linux缺少 WebKitGTK</b></summary>
```bash
# Debian 13 / Ubuntu 24.04+
sudo apt-get update
sudo apt-get install -y libgtk-3-0 libwebkit2gtk-4.1-0 libjavascriptcoregtk-4.1-0
# Ubuntu 22.04 / Debian 12
sudo apt-get update
sudo apt-get install -y libgtk-3-0 libwebkit2gtk-4.0-37 libjavascriptcoregtk-4.0-18
```
`-WebKit41` 后缀的产物更适配 Debian 13 / Ubuntu 24.04+。
</details>
<details>
<summary><b>Linux中文显示为方框</b></summary>
```bash
sudo apt-get update
sudo apt-get install -y fonts-noto-cjk fonts-wqy-microhei
fc-cache -fv
```
</details>
---
## 🤝 贡献
欢迎 Issue 与 PR。请从 **`dev`** 拉分支,并向 **`dev`** 提 PR。
- [CONTRIBUTING.zh-CN.md](CONTRIBUTING.zh-CN.md)
---
## ⭐ Star History
<a href="https://www.star-history.com/?repos=Syngnat%2FGoNavi&type=date&legend=top-left">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/image?repos=Syngnat/GoNavi&type=date&theme=dark&legend=top-left" />
<source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/image?repos=Syngnat/GoNavi&type=date&legend=top-left" />
<img alt="Star History Chart" src="https://api.star-history.com/image?repos=Syngnat/GoNavi&type=date&legend=top-left" />
</picture>
</a>
---
## 友情链接
- [linux.do](https://linux.do/)
- [AI全书](https://aibook.ren/)
## 开源协议
[Apache-2.0](LICENSE)
<p align="center">
<sub>为泡在 SQL、表结构与 Agent 循环里的人而造。</sub>
</p>

BIN
assets/readme-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 180 KiB

9
assets_dev.go Normal file
View File

@@ -0,0 +1,9 @@
//go:build dev
package main
import "os"
// 开发模式下由 Wails DevServer 提供前端资源,这里只提供一个稳定的占位 FS
// 避免编译时依赖 frontend/dist 被并发重建。
var assets = os.DirFS(".")

13
assets_prod.go Normal file
View File

@@ -0,0 +1,13 @@
//go:build !dev
package main
import (
"embed"
"io/fs"
)
//go:embed all:frontend/dist
var embeddedAssets embed.FS
var assets fs.FS = embeddedAssets

461
build-driver-agents.sh Executable file
View File

@@ -0,0 +1,461 @@
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
DEFAULT_DRIVERS=(mariadb oceanbase doris starrocks sphinx sqlserver sqlite duckdb dameng kingbase highgo vastbase opengauss gaussdb iris mongodb tdengine iotdb clickhouse elasticsearch trino)
DEFAULT_PLATFORMS=(darwin/amd64 darwin/arm64 windows/amd64 windows/arm64 linux/amd64 linux/arm64)
DUCKDB_WINDOWS_LIBRARY_VERSION="v1.4.4"
DUCKDB_WINDOWS_LIBRARY_URL="https://github.com/duckdb/duckdb/releases/download/${DUCKDB_WINDOWS_LIBRARY_VERSION}/libduckdb-windows-amd64.zip"
DUCKDB_WINDOWS_SUPPORT_DLL="duckdb.dll"
usage() {
cat <<'EOF'
用法:
./build-driver-agents.sh [选项]
选项:
--drivers <列表> 指定驱动列表逗号分隔例如kingbase,mongodb
--platform <目标> 目标平台current、all、GOOS/GOARCH或逗号分隔列表
默认 current当前 Go 环境)
--out-dir <目录> 输出目录根路径默认dist/driver-agents
--bundle-name <文件名> 驱动总包 zip 名称默认GoNavi-DriverAgents.zip
--strict 任一驱动构建失败即中断(默认失败后继续,最后汇总)
--upx 要求使用 UPX 压缩支持的平台产物(默认 auto有 upx 则压缩)
--no-upx 禁用 UPX 压缩
-h, --help 显示帮助
示例:
./build-driver-agents.sh
./build-driver-agents.sh --drivers kingbase
./build-driver-agents.sh --platform windows/amd64 --drivers kingbase,mongodb
./build-driver-agents.sh --platform all
./build-driver-agents.sh --platform darwin/arm64,windows/amd64,linux/amd64
EOF
}
normalize_driver() {
local name
name="$(echo "${1:-}" | tr '[:upper:]' '[:lower:]' | xargs)"
case "$name" in
doris|diros) echo "doris" ;;
open_gauss|open-gauss) echo "opengauss" ;;
gaussdb|gauss_db|gauss-db) echo "gaussdb" ;;
elasticsearch|elastic) echo "elasticsearch" ;;
mariadb|oceanbase|starrocks|sphinx|sqlserver|sqlite|duckdb|dameng|kingbase|highgo|vastbase|opengauss|gaussdb|iris|mongodb|tdengine|iotdb|clickhouse|trino)
echo "$name"
;;
*)
return 1
;;
esac
}
build_driver_name() {
case "$1" in
doris) echo "diros" ;;
*) echo "$1" ;;
esac
}
platform_dir_name() {
case "$1" in
windows) echo "Windows" ;;
darwin) echo "MacOS" ;;
linux) echo "Linux" ;;
*) echo "Unknown" ;;
esac
}
current_platform() {
echo "$(go env GOOS)/$(go env GOARCH)"
}
append_platform() {
local candidate
candidate="$1"
if [[ "$platform_seen" == *"|$candidate|"* ]]; then
return 0
fi
platforms+=("$candidate")
platform_seen="${platform_seen}${candidate}|"
}
normalize_platform() {
local value goos goarch platform_dir
value="$(printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')"
case "$value" in
current|"")
current_platform
;;
*/*)
goos="${value%%/*}"
goarch="${value##*/}"
platform_dir="$(platform_dir_name "$goos")"
if [[ -z "$goos" || -z "$goarch" || "$platform_dir" == "Unknown" ]]; then
return 1
fi
echo "$goos/$goarch"
;;
*)
return 1
;;
esac
}
zip_bundle() {
local bundle_zip_path="$1"
local bundle_stage_dir="$2"
local -a bundle_dirs=()
local dir
for dir in "$bundle_stage_dir"/*; do
[[ -d "$dir" ]] || continue
bundle_dirs+=("$(basename "$dir")")
done
if [[ ${#bundle_dirs[@]} -eq 0 ]]; then
echo "❌ 驱动总包 staging 目录为空。"
exit 1
fi
rm -f "$bundle_zip_path"
if command -v zip >/dev/null 2>&1; then
(
cd "$bundle_stage_dir"
zip -qry "$bundle_zip_path" "${bundle_dirs[@]}"
)
elif command -v python3 >/dev/null 2>&1; then
BUNDLE_STAGE_DIR="$bundle_stage_dir" BUNDLE_ZIP_PATH="$bundle_zip_path" python3 - <<'PY'
import os
import zipfile
from pathlib import Path
stage = Path(os.environ["BUNDLE_STAGE_DIR"])
target = Path(os.environ["BUNDLE_ZIP_PATH"])
with zipfile.ZipFile(target, "w", compression=zipfile.ZIP_DEFLATED) as zf:
for path in stage.rglob("*"):
if path.is_file():
zf.write(path, path.relative_to(stage).as_posix())
PY
else
echo "❌ 未找到 zip 或 python3无法生成驱动总包 zip。"
exit 1
fi
}
zip_duckdb_windows_package() {
local bundle_stage_dir="$1"
local zip_path="$2"
rm -f "$zip_path"
if command -v python3 >/dev/null 2>&1; then
BUNDLE_STAGE_DIR="$bundle_stage_dir" BUNDLE_ZIP_PATH="$zip_path" python3 - <<'PY'
import os
import zipfile
stage_dir = os.environ["BUNDLE_STAGE_DIR"]
zip_path = os.environ["BUNDLE_ZIP_PATH"]
entries = [
("Windows/duckdb-driver-agent-windows-amd64.exe", os.path.join(stage_dir, "Windows", "duckdb-driver-agent-windows-amd64.exe")),
("Windows/duckdb.dll", os.path.join(stage_dir, "Windows", "duckdb.dll")),
]
with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
for arcname, src in entries:
if not os.path.isfile(src):
raise FileNotFoundError(src)
zf.write(src, arcname)
PY
elif command -v zip >/dev/null 2>&1; then
(
cd "$bundle_stage_dir"
zip -qry "$zip_path" "Windows/duckdb-driver-agent-windows-amd64.exe" "Windows/duckdb.dll"
)
else
echo "❌ 未找到 python3 或 zip无法生成 DuckDB Windows 专属驱动包。"
exit 1
fi
}
prepare_duckdb_windows_library() {
local cache_root="$1"
local lib_dir="$cache_root/duckdb-windows-${DUCKDB_WINDOWS_LIBRARY_VERSION}"
local zip_path="$cache_root/libduckdb-windows-amd64.zip"
if [[ -f "$lib_dir/duckdb.dll" && -f "$lib_dir/duckdb.lib" ]]; then
printf '%s\n' "$lib_dir"
return 0
fi
mkdir -p "$lib_dir"
echo "⬇️ 下载 DuckDB Windows 官方动态库:$DUCKDB_WINDOWS_LIBRARY_URL" >&2
if command -v curl >/dev/null 2>&1; then
curl -fsSL "$DUCKDB_WINDOWS_LIBRARY_URL" -o "$zip_path"
elif command -v wget >/dev/null 2>&1; then
wget -q "$DUCKDB_WINDOWS_LIBRARY_URL" -O "$zip_path"
else
echo "❌ 未找到 curl 或 wget无法下载 DuckDB Windows 动态库。" >&2
return 1
fi
if command -v unzip >/dev/null 2>&1; then
unzip -qo "$zip_path" -d "$lib_dir"
elif command -v python3 >/dev/null 2>&1; then
DUCKDB_LIB_ZIP="$zip_path" DUCKDB_LIB_DIR="$lib_dir" python3 - <<'PY'
import os
import zipfile
zip_path = os.environ["DUCKDB_LIB_ZIP"]
target = os.environ["DUCKDB_LIB_DIR"]
with zipfile.ZipFile(zip_path) as zf:
zf.extractall(target)
PY
else
echo "❌ 未找到 unzip 或 python3无法解压 DuckDB Windows 动态库。" >&2
return 1
fi
if [[ ! -f "$lib_dir/duckdb.dll" || ! -f "$lib_dir/duckdb.lib" ]]; then
echo "❌ DuckDB Windows 动态库包缺少 duckdb.dll 或 duckdb.lib。" >&2
return 1
fi
cp "$lib_dir/duckdb.lib" "$lib_dir/libduckdb.dll.a"
cp "$lib_dir/duckdb.lib" "$lib_dir/libduckdb.a"
printf '%s\n' "$lib_dir"
}
join_by_comma() {
local IFS=,
echo "$*"
}
driver_csv=""
target_platform=""
out_root="dist/driver-agents"
bundle_name="GoNavi-DriverAgents.zip"
duckdb_windows_zip_name="duckdb-driver.zip"
strict_mode="false"
upx_mode="${GONAVI_DRIVER_AGENT_UPX:-auto}"
while [[ $# -gt 0 ]]; do
case "$1" in
--drivers)
driver_csv="${2:-}"
shift 2
;;
--platform)
target_platform="${2:-}"
shift 2
;;
--out-dir)
out_root="${2:-}"
shift 2
;;
--bundle-name)
bundle_name="${2:-}"
shift 2
;;
--strict)
strict_mode="true"
shift
;;
--upx)
upx_mode="required"
shift
;;
--no-upx)
upx_mode="off"
shift
;;
-h|--help)
usage
exit 0
;;
*)
echo "❌ 未知参数:$1"
usage
exit 1
;;
esac
done
if ! command -v go >/dev/null 2>&1; then
echo "❌ 未找到 Go请先安装 Go 并确保 go 在 PATH 中。"
exit 1
fi
declare -a drivers=()
if [[ -n "$driver_csv" ]]; then
IFS=',' read -r -a raw_drivers <<<"$driver_csv"
for item in "${raw_drivers[@]}"; do
normalized="$(normalize_driver "$item")" || {
echo "❌ 不支持的驱动:$item"
exit 1
}
drivers+=("$normalized")
done
else
drivers=("${DEFAULT_DRIVERS[@]}")
fi
revision_driver_csv="$(join_by_comma "${drivers[@]}")"
declare -a platforms=()
platform_seen="|"
if [[ -z "$target_platform" ]]; then
target_platform="current"
fi
IFS=',' read -r -a raw_platforms <<<"$target_platform"
for item in "${raw_platforms[@]}"; do
normalized_platform="$(printf '%s' "$item" | tr '[:upper:]' '[:lower:]' | tr -d '[:space:]')"
if [[ "$normalized_platform" == "all" ]]; then
for default_platform in "${DEFAULT_PLATFORMS[@]}"; do
append_platform "$default_platform"
done
continue
fi
normalized_platform="$(normalize_platform "$item")" || {
echo "❌ --platform 参数格式错误,应为 current、all、GOOS/GOARCH 或逗号分隔列表,例如 darwin/arm64,windows/amd64"
exit 1
}
append_platform "$normalized_platform"
done
if [[ ${#platforms[@]} -eq 0 ]]; then
echo "❌ 未指定有效目标平台。"
exit 1
fi
mkdir -p "$out_root"
out_root_abs="$(cd "$out_root" && pwd)"
bundle_stage_dir="$(mktemp -d "${TMPDIR:-/tmp}/gonavi-driver-bundle.XXXXXX")"
cleanup() {
rm -rf "$bundle_stage_dir"
}
trap cleanup EXIT
if [[ ${#platforms[@]} -eq 1 ]]; then
single_platform="${platforms[0]}"
single_platform_key="${single_platform/\//-}"
single_output_dir="${out_root%/}/$single_platform_key"
mkdir -p "$single_output_dir"
bundle_zip_path="$(cd "$single_output_dir" && pwd)/$bundle_name"
else
bundle_zip_path="$out_root_abs/$bundle_name"
fi
declare -a built_assets=()
declare -a failed_drivers=()
declare -a skipped_drivers=()
echo "🚀 开始构建 optional-driver-agent"
echo " 平台:${platforms[*]}"
echo " 输出根目录:$out_root_abs"
echo " 驱动列表:${drivers[*]}"
for platform in "${platforms[@]}"; do
goos="${platform%%/*}"
goarch="${platform##*/}"
platform_key="${goos}-${goarch}"
platform_dir="$(platform_dir_name "$goos")"
output_dir="${out_root%/}/${platform_key}"
bundle_platform_dir="$bundle_stage_dir/$platform_dir"
mkdir -p "$output_dir" "$bundle_platform_dir"
output_dir_abs="$(cd "$output_dir" && pwd)"
echo ""
echo "🧭 生成 driver-agent revision 指纹:$platform"
"$SCRIPT_DIR/tools/generate-driver-agent-revisions.sh" --platform "$platform" --drivers "$revision_driver_csv"
for driver in "${drivers[@]}"; do
if [[ "$driver" == "duckdb" && "$goos" == "windows" && "$goarch" != "amd64" ]]; then
echo "⚠️ 跳过 duckdb$platform 仅支持 windows/amd64"
skipped_drivers+=("duckdb($platform)")
continue
fi
build_driver="$(build_driver_name "$driver")"
tag="gonavi_${build_driver}_driver"
build_tags="$tag"
asset_name="${driver}-driver-agent-${goos}-${goarch}"
if [[ "$goos" == "windows" ]]; then
asset_name="${asset_name}.exe"
fi
output_path="$output_dir_abs/$asset_name"
cgo_enabled=0
if [[ "$driver" == "duckdb" ]]; then
cgo_enabled=1
fi
duckdb_lib_dir=""
if [[ "$driver" == "duckdb" && "$goos" == "windows" && "$goarch" == "amd64" ]]; then
duckdb_lib_dir="$(prepare_duckdb_windows_library "$bundle_stage_dir")"
build_tags="$build_tags duckdb_use_lib"
fi
echo "🔧 构建 $driver -> $asset_name (platform=$platform, tags=$build_tags, CGO_ENABLED=$cgo_enabled)"
set +e
if [[ -n "$duckdb_lib_dir" ]]; then
CGO_ENABLED="$cgo_enabled" GOOS="$goos" GOARCH="$goarch" GOTOOLCHAIN=auto \
CGO_LDFLAGS="-L${duckdb_lib_dir} -lduckdb" PATH="${duckdb_lib_dir}:$PATH" \
go build -tags "$build_tags" -trimpath -ldflags "-s -w" -o "$output_path" ./cmd/optional-driver-agent
else
CGO_ENABLED="$cgo_enabled" GOOS="$goos" GOARCH="$goarch" GOTOOLCHAIN=auto \
go build -tags "$build_tags" -trimpath -ldflags "-s -w" -o "$output_path" ./cmd/optional-driver-agent
fi
build_exit=$?
set -e
if [[ $build_exit -ne 0 ]]; then
echo "❌ 构建失败:$driver ($platform)"
failed_drivers+=("$driver($platform)")
if [[ "$strict_mode" == "true" ]]; then
exit $build_exit
fi
continue
fi
GONAVI_DRIVER_AGENT_UPX="$upx_mode" "$SCRIPT_DIR/tools/compress-driver-artifact.sh" "$output_path" "$platform" "$platform_dir/$asset_name"
cp "$output_path" "$bundle_platform_dir/$asset_name"
if [[ -n "$duckdb_lib_dir" ]]; then
cp "$duckdb_lib_dir/$DUCKDB_WINDOWS_SUPPORT_DLL" "$output_dir_abs/$DUCKDB_WINDOWS_SUPPORT_DLL"
GONAVI_DRIVER_AGENT_UPX="$upx_mode" "$SCRIPT_DIR/tools/compress-driver-artifact.sh" "$output_dir_abs/$DUCKDB_WINDOWS_SUPPORT_DLL" "$platform" "$platform_dir/$DUCKDB_WINDOWS_SUPPORT_DLL"
cp "$output_dir_abs/$DUCKDB_WINDOWS_SUPPORT_DLL" "$bundle_platform_dir/$DUCKDB_WINDOWS_SUPPORT_DLL"
built_assets+=("$platform_dir/$DUCKDB_WINDOWS_SUPPORT_DLL")
fi
built_assets+=("$platform_dir/$asset_name")
done
done
if [[ ${#built_assets[@]} -eq 0 ]]; then
echo "❌ 未成功构建任何驱动代理。"
exit 1
fi
zip_bundle "$bundle_zip_path" "$bundle_stage_dir"
duckdb_asset_path="$out_root_abs/windows-amd64/duckdb-driver-agent-windows-amd64.exe"
duckdb_dll_path="$out_root_abs/windows-amd64/$DUCKDB_WINDOWS_SUPPORT_DLL"
if [[ -f "$duckdb_asset_path" && -f "$duckdb_dll_path" ]]; then
duckdb_zip_path="$out_root_abs/windows-amd64/$duckdb_windows_zip_name"
zip_duckdb_windows_package "$bundle_stage_dir" "$duckdb_zip_path"
built_assets+=("Windows/$duckdb_windows_zip_name")
fi
echo ""
echo "✅ 构建完成"
echo " 单文件输出根目录:$out_root_abs"
echo " 驱动总包:$bundle_zip_path"
echo " 已构建:${built_assets[*]}"
if [[ ${#skipped_drivers[@]} -gt 0 ]]; then
echo " 已跳过:${skipped_drivers[*]}"
fi
if [[ ${#failed_drivers[@]} -gt 0 ]]; then
echo "⚠️ 构建失败驱动:${failed_drivers[*]}"
exit 2
fi

View File

@@ -1,16 +1,42 @@
#!/bin/bash
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$SCRIPT_DIR"
# 配置
APP_NAME="GoNavi"
DIST_DIR="dist"
BUILD_BIN_DIR="build/bin"
DEFAULT_BINARY_NAME="GoNavi" # 对应 wails.json 中的 outputfilename
DEV_VERSION_FILE="version/dev-version.txt"
DEFAULT_DEV_VERSION="0.0.1-test"
# 提取版本号
VERSION=$(grep '"version":' frontend/package.json | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | tr -d '[[:space:]]')
if [ -z "$VERSION" ]; then
VERSION="0.0.0"
fi
resolve_build_version() {
if [ -n "${GONAVI_VERSION:-}" ]; then
printf '%s\n' "${GONAVI_VERSION}"
return
fi
if [ -f "$DEV_VERSION_FILE" ]; then
local dev_version
dev_version=$(head -n 1 "$DEV_VERSION_FILE" | tr -d '\r' | tr -d '[:space:]')
if [ -n "$dev_version" ]; then
printf '%s\n' "$dev_version"
return
fi
fi
local package_version
package_version=$(grep '"version":' frontend/package.json | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | tr -d '[:space:]')
if [ -n "$package_version" ]; then
printf '%s\n' "$package_version"
return
fi
printf '%s\n' "$DEFAULT_DEV_VERSION"
}
VERSION="$(resolve_build_version)"
echo " 检测到版本号: $VERSION"
LDFLAGS="-s -w -X GoNavi-Wails/internal/app.AppVersion=$VERSION"
@@ -20,124 +46,182 @@ RED='\033[0;31m'
YELLOW='\033[1;33m'
NC='\033[0m'
BUILD_FAILURES=()
record_build_failure() {
local target="$1"
BUILD_FAILURES+=("$target")
}
get_file_size_bytes() {
local target="$1"
if [ ! -f "$target" ]; then
echo 0
return
fi
if stat -f%z "$target" >/dev/null 2>&1; then
stat -f%z "$target"
return
fi
if stat -c%s "$target" >/dev/null 2>&1; then
stat -c%s "$target"
return
fi
wc -c <"$target" | tr -d '[:space:]'
}
format_size_mb() {
local bytes="${1:-0}"
awk -v b="$bytes" 'BEGIN { printf "%.2fMB", b / 1024 / 1024 }'
}
try_compress_binary_with_upx() {
local exe_path="$1"
local label="$2"
if [ ! -f "$exe_path" ]; then
echo -e "${RED} ❌ 未找到 ${label} 文件:$exe_path${NC}"
exit 1
fi
if ! command -v upx >/dev/null 2>&1; then
echo -e "${RED} ❌ 未找到 upx${label} 必须进行压缩后才能继续打包。${NC}"
case "$(uname -s)" in
Darwin)
echo " 安装命令: brew install upx"
;;
Linux)
echo " 安装命令: sudo apt-get install -y upx-ucl (或对应发行版包管理器)"
;;
esac
exit 1
fi
local before_bytes after_bytes
before_bytes=$(get_file_size_bytes "$exe_path")
echo " 🗜️ 正在使用 UPX 压缩 ${label}..."
if upx --best --lzma --force "$exe_path" >/dev/null 2>&1; then
if ! upx -t "$exe_path" >/dev/null 2>&1; then
echo -e "${RED} ❌ UPX 校验失败:${label}${NC}"
exit 1
fi
after_bytes=$(get_file_size_bytes "$exe_path")
if [ "$after_bytes" -lt "$before_bytes" ]; then
local saved_bytes=$((before_bytes - after_bytes))
echo " ✅ UPX 压缩完成: $(format_size_mb "$before_bytes") -> $(format_size_mb "$after_bytes"),减少 $(format_size_mb "$saved_bytes")"
else
echo " UPX 压缩完成: $(format_size_mb "$before_bytes") -> $(format_size_mb "$after_bytes")"
fi
else
echo -e "${RED} ❌ UPX 压缩失败:${label}${NC}"
exit 1
fi
}
clear_macos_bundle_xattrs() {
local bundle_path="$1"
if [ -z "$bundle_path" ] || [ ! -e "$bundle_path" ]; then
return
fi
if command -v xattr >/dev/null 2>&1; then
xattr -cr "$bundle_path" >/dev/null 2>&1 || true
fi
}
package_macos_bundle_zip() {
local app_path="$1"
local archive_path="$2"
local archive_abs
if [ ! -d "$app_path" ]; then
echo -e "${RED} ❌ 未找到 macOS 应用包:$app_path${NC}"
exit 1
fi
archive_abs="$(cd "$(dirname "$archive_path")" && pwd)/$(basename "$archive_path")"
rm -f "$archive_path"
if command -v ditto >/dev/null 2>&1; then
ditto -c -k --sequesterRsrc --keepParent "$app_path" "$archive_abs"
elif command -v zip >/dev/null 2>&1; then
(
cd "$(dirname "$app_path")" && \
zip -qry "$archive_abs" "$(basename "$app_path")"
)
else
echo -e "${RED} ❌ 未找到 ditto/zip无法打包 macOS 应用。${NC}"
exit 1
fi
if [ ! -f "$archive_abs" ]; then
echo -e "${RED} ❌ macOS 应用归档失败:$archive_abs${NC}"
exit 1
fi
}
package_macos_release() {
local platform="$1"
local archive_suffix="$2"
echo -e "${GREEN}🍎 正在构建 macOS (${platform})...${NC}"
generate_driver_agent_revisions "darwin/${platform}"
wails build -platform "darwin/${platform}" -clean -ldflags "$LDFLAGS"
if [ $? -ne 0 ]; then
echo -e "${RED} ❌ macOS ${platform} 构建失败。${NC}"
record_build_failure "macOS ${platform}"
return
fi
local app_src="$BUILD_BIN_DIR/$DEFAULT_BINARY_NAME.app"
local app_dest_name="${APP_NAME}-${VERSION}-${archive_suffix}.app"
local zip_name="${APP_NAME}-${VERSION}-${archive_suffix}.zip"
mv "$app_src" "$DIST_DIR/$app_dest_name"
local app_bin_path
app_bin_path=$(find "$DIST_DIR/$app_dest_name/Contents/MacOS" -maxdepth 1 -type f -print -quit)
if [ -z "$app_bin_path" ] || [ ! -f "$app_bin_path" ]; then
echo -e "${RED} ❌ 未找到 macOS ${platform} 主程序文件。${NC}"
exit 1
fi
echo -e "${YELLOW} ⚠️ macOS ${platform} 改为无交互 ZIP 打包,不再生成 DMG。${NC}"
echo " 🔏 正在对 .app 进行 ad-hoc 签名 (${platform})..."
clear_macos_bundle_xattrs "$DIST_DIR/$app_dest_name"
codesign --force --deep --sign - "$DIST_DIR/$app_dest_name"
echo " 📦 正在打包 macOS 应用归档 (${platform})..."
package_macos_bundle_zip "$DIST_DIR/$app_dest_name" "$DIST_DIR/$zip_name"
rm -rf "$DIST_DIR/$app_dest_name"
echo " ✅ 已生成 $zip_name"
}
generate_driver_agent_revisions() {
local platform="$1"
echo " 🧭 正在生成 driver-agent revision 指纹 (${platform})..."
./tools/generate-driver-agent-revisions.sh --platform "$platform"
}
echo -e "${GREEN}🚀 开始构建 $APP_NAME $VERSION...${NC}"
# 清理并创建输出目录
rm -rf $DIST_DIR
mkdir -p $DIST_DIR
rm -rf "$DIST_DIR"
mkdir -p "$DIST_DIR"
# --- macOS ARM64 构建 ---
echo -e "${GREEN}🍎 正在构建 macOS (arm64)...${NC}"
wails build -platform darwin/arm64 -clean -ldflags "$LDFLAGS"
if [ $? -eq 0 ]; then
APP_SRC="$BUILD_BIN_DIR/$DEFAULT_BINARY_NAME.app"
APP_DEST_NAME="${APP_NAME}-${VERSION}-mac-arm64.app"
DMG_NAME="${APP_NAME}-${VERSION}-mac-arm64.dmg"
# 移动 .app 到 dist
mv "$APP_SRC" "$DIST_DIR/$APP_DEST_NAME"
# 创建 DMG
if command -v create-dmg &> /dev/null; then
echo " 📦 正在打包 DMG (arm64)..."
# 移除已存在的 DMG (以防万一)
rm -f "$DIST_DIR/$DMG_NAME"
create-dmg \
--volname "${APP_NAME} ${VERSION}" \
--volicon "build/appicon.icns" \
--window-pos 200 120 \
--window-size 800 400 \
--icon-size 100 \
--icon "$APP_DEST_NAME" 200 190 \
--hide-extension "$APP_DEST_NAME" \
--app-drop-link 600 185 \
"$DIST_DIR/$DMG_NAME" \
"$DIST_DIR/$APP_DEST_NAME"
# 检查是否生成了 rw.* 的临时文件并重命名 (create-dmg 有时会有此行为)
if [ ! -f "$DIST_DIR/$DMG_NAME" ]; then
RW_FILE=$(find "$DIST_DIR" -name "rw.*.dmg" -print -quit)
if [ -n "$RW_FILE" ]; then
echo -e "${YELLOW} ⚠️ 检测到临时文件名,正在重命名...${NC}"
mv "$RW_FILE" "$DIST_DIR/$DMG_NAME"
fi
fi
# 删除中间的 .app 文件,保持目录整洁
rm -rf "$DIST_DIR/$APP_DEST_NAME"
if [ -f "$DIST_DIR/$DMG_NAME" ]; then
echo " ✅ 已生成 $DMG_NAME"
else
echo -e "${RED} ❌ DMG 生成失败,请检查 create-dmg 输出。${NC}"
fi
else
echo -e "${YELLOW} ⚠️ 未找到 create-dmg 工具,跳过 DMG 打包,仅保留 .app。${NC}"
echo " 安装命令: brew install create-dmg"
fi
else
echo -e "${RED} ❌ macOS arm64 构建失败。${NC}"
fi
# --- macOS AMD64 构建 ---
echo -e "${GREEN}🍎 正在构建 macOS (amd64)...${NC}"
wails build -platform darwin/amd64 -clean -ldflags "$LDFLAGS"
if [ $? -eq 0 ]; then
APP_SRC="$BUILD_BIN_DIR/$DEFAULT_BINARY_NAME.app"
APP_DEST_NAME="${APP_NAME}-${VERSION}-mac-amd64.app"
DMG_NAME="${APP_NAME}-${VERSION}-mac-amd64.dmg"
mv "$APP_SRC" "$DIST_DIR/$APP_DEST_NAME"
if command -v create-dmg &> /dev/null; then
echo " 📦 正在打包 DMG (amd64)..."
rm -f "$DIST_DIR/$DMG_NAME"
create-dmg \
--volname "${APP_NAME} ${VERSION}" \
--volicon "build/appicon.icns" \
--window-pos 200 120 \
--window-size 800 400 \
--icon-size 100 \
--icon "$APP_DEST_NAME" 200 190 \
--hide-extension "$APP_DEST_NAME" \
--app-drop-link 600 185 \
"$DIST_DIR/$DMG_NAME" \
"$DIST_DIR/$APP_DEST_NAME"
# 检查是否生成了 rw.* 的临时文件并重命名
if [ ! -f "$DIST_DIR/$DMG_NAME" ]; then
RW_FILE=$(find "$DIST_DIR" -name "rw.*.dmg" -print -quit)
if [ -n "$RW_FILE" ]; then
echo -e "${YELLOW} ⚠️ 检测到临时文件名,正在重命名...${NC}"
mv "$RW_FILE" "$DIST_DIR/$DMG_NAME"
fi
fi
rm -rf "$DIST_DIR/$APP_DEST_NAME"
if [ -f "$DIST_DIR/$DMG_NAME" ]; then
echo " ✅ 已生成 $DMG_NAME"
else
echo -e "${RED} ❌ DMG 生成失败。${NC}"
fi
else
echo -e "${YELLOW} ⚠️ 未找到 create-dmg 工具。${NC}"
fi
else
echo -e "${RED} ❌ macOS amd64 构建失败。${NC}"
fi
package_macos_release "arm64" "mac-arm64"
package_macos_release "amd64" "mac-amd64"
# --- Windows AMD64 构建 ---
echo -e "${GREEN}🪟 正在构建 Windows (amd64)...${NC}"
if command -v x86_64-w64-mingw32-gcc &> /dev/null; then
generate_driver_agent_revisions "windows/amd64"
wails build -platform windows/amd64 -clean -ldflags "$LDFLAGS"
if [ $? -eq 0 ]; then
mv "$BUILD_BIN_DIR/${DEFAULT_BINARY_NAME}.exe" "$DIST_DIR/${APP_NAME}-${VERSION}-windows-amd64.exe"
TARGET_EXE="$DIST_DIR/${APP_NAME}-${VERSION}-windows-amd64.exe"
mv "$BUILD_BIN_DIR/${DEFAULT_BINARY_NAME}.exe" "$TARGET_EXE"
try_compress_binary_with_upx "$TARGET_EXE" "Windows amd64 可执行文件"
echo " ✅ 已生成 ${APP_NAME}-${VERSION}-windows-amd64.exe"
else
echo -e "${RED} ❌ Windows amd64 构建失败。${NC}"
record_build_failure "Windows amd64"
fi
else
echo -e "${YELLOW} ⚠️ 未找到 MinGW 工具 (x86_64-w64-mingw32-gcc),跳过 Windows amd64 构建。${NC}"
@@ -146,12 +230,16 @@ fi
# --- Windows ARM64 构建 ---
echo -e "${GREEN}🪟 正在构建 Windows (arm64)...${NC}"
if command -v aarch64-w64-mingw32-gcc &> /dev/null; then
generate_driver_agent_revisions "windows/arm64"
wails build -platform windows/arm64 -clean -ldflags "$LDFLAGS"
if [ $? -eq 0 ]; then
mv "$BUILD_BIN_DIR/${DEFAULT_BINARY_NAME}.exe" "$DIST_DIR/${APP_NAME}-${VERSION}-windows-arm64.exe"
TARGET_EXE="$DIST_DIR/${APP_NAME}-${VERSION}-windows-arm64.exe"
mv "$BUILD_BIN_DIR/${DEFAULT_BINARY_NAME}.exe" "$TARGET_EXE"
echo -e "${YELLOW} ⚠️ 当前 UPX 不支持 win64/arm64跳过 Windows arm64 压缩。${NC}"
echo " ✅ 已生成 ${APP_NAME}-${VERSION}-windows-arm64.exe"
else
echo -e "${RED} ❌ Windows arm64 构建失败。${NC}"
record_build_failure "Windows arm64"
fi
else
echo -e "${YELLOW} ⚠️ 未找到 MinGW ARM64 工具 (aarch64-w64-mingw32-gcc),跳过 Windows arm64 构建。${NC}"
@@ -166,10 +254,13 @@ CURRENT_ARCH=$(uname -m)
if [ "$CURRENT_OS" = "Linux" ] && [ "$CURRENT_ARCH" = "x86_64" ]; then
# 本机 Linux amd64直接构建
generate_driver_agent_revisions "linux/amd64"
wails build -platform linux/amd64 -clean -ldflags "$LDFLAGS"
if [ $? -eq 0 ]; then
mv "$BUILD_BIN_DIR/${DEFAULT_BINARY_NAME}" "$DIST_DIR/${APP_NAME}-${VERSION}-linux-amd64"
chmod +x "$DIST_DIR/${APP_NAME}-${VERSION}-linux-amd64"
TARGET_LINUX_BIN="$DIST_DIR/${APP_NAME}-${VERSION}-linux-amd64"
mv "$BUILD_BIN_DIR/${DEFAULT_BINARY_NAME}" "$TARGET_LINUX_BIN"
chmod +x "$TARGET_LINUX_BIN"
try_compress_binary_with_upx "$TARGET_LINUX_BIN" "Linux amd64 可执行文件"
# 打包为 tar.gz
cd "$DIST_DIR"
tar -czvf "${APP_NAME}-${VERSION}-linux-amd64.tar.gz" "${APP_NAME}-${VERSION}-linux-amd64"
@@ -178,16 +269,20 @@ if [ "$CURRENT_OS" = "Linux" ] && [ "$CURRENT_ARCH" = "x86_64" ]; then
echo " ✅ 已生成 ${APP_NAME}-${VERSION}-linux-amd64.tar.gz"
else
echo -e "${RED} ❌ Linux amd64 构建失败。${NC}"
record_build_failure "Linux amd64"
fi
elif command -v x86_64-linux-gnu-gcc &> /dev/null; then
# macOS 或其他系统,尝试交叉编译
export CC=x86_64-linux-gnu-gcc
export CXX=x86_64-linux-gnu-g++
export CGO_ENABLED=1
generate_driver_agent_revisions "linux/amd64"
wails build -platform linux/amd64 -clean -ldflags "$LDFLAGS"
if [ $? -eq 0 ]; then
mv "$BUILD_BIN_DIR/${DEFAULT_BINARY_NAME}" "$DIST_DIR/${APP_NAME}-${VERSION}-linux-amd64"
chmod +x "$DIST_DIR/${APP_NAME}-${VERSION}-linux-amd64"
TARGET_LINUX_BIN="$DIST_DIR/${APP_NAME}-${VERSION}-linux-amd64"
mv "$BUILD_BIN_DIR/${DEFAULT_BINARY_NAME}" "$TARGET_LINUX_BIN"
chmod +x "$TARGET_LINUX_BIN"
try_compress_binary_with_upx "$TARGET_LINUX_BIN" "Linux amd64 可执行文件"
cd "$DIST_DIR"
tar -czvf "${APP_NAME}-${VERSION}-linux-amd64.tar.gz" "${APP_NAME}-${VERSION}-linux-amd64"
rm "${APP_NAME}-${VERSION}-linux-amd64"
@@ -195,6 +290,7 @@ elif command -v x86_64-linux-gnu-gcc &> /dev/null; then
echo " ✅ 已生成 ${APP_NAME}-${VERSION}-linux-amd64.tar.gz"
else
echo -e "${RED} ❌ Linux amd64 交叉编译失败。${NC}"
record_build_failure "Linux amd64"
fi
unset CC CXX CGO_ENABLED
else
@@ -206,10 +302,13 @@ fi
echo -e "${GREEN}🐧 正在构建 Linux (arm64)...${NC}"
if [ "$CURRENT_OS" = "Linux" ] && [ "$CURRENT_ARCH" = "aarch64" ]; then
# 本机 Linux arm64直接构建
generate_driver_agent_revisions "linux/arm64"
wails build -platform linux/arm64 -clean -ldflags "$LDFLAGS"
if [ $? -eq 0 ]; then
mv "$BUILD_BIN_DIR/${DEFAULT_BINARY_NAME}" "$DIST_DIR/${APP_NAME}-${VERSION}-linux-arm64"
chmod +x "$DIST_DIR/${APP_NAME}-${VERSION}-linux-arm64"
TARGET_LINUX_BIN="$DIST_DIR/${APP_NAME}-${VERSION}-linux-arm64"
mv "$BUILD_BIN_DIR/${DEFAULT_BINARY_NAME}" "$TARGET_LINUX_BIN"
chmod +x "$TARGET_LINUX_BIN"
try_compress_binary_with_upx "$TARGET_LINUX_BIN" "Linux arm64 可执行文件"
cd "$DIST_DIR"
tar -czvf "${APP_NAME}-${VERSION}-linux-arm64.tar.gz" "${APP_NAME}-${VERSION}-linux-arm64"
rm "${APP_NAME}-${VERSION}-linux-arm64"
@@ -217,16 +316,20 @@ if [ "$CURRENT_OS" = "Linux" ] && [ "$CURRENT_ARCH" = "aarch64" ]; then
echo " ✅ 已生成 ${APP_NAME}-${VERSION}-linux-arm64.tar.gz"
else
echo -e "${RED} ❌ Linux arm64 构建失败。${NC}"
record_build_failure "Linux arm64"
fi
elif command -v aarch64-linux-gnu-gcc &> /dev/null; then
# 交叉编译
export CC=aarch64-linux-gnu-gcc
export CXX=aarch64-linux-gnu-g++
export CGO_ENABLED=1
generate_driver_agent_revisions "linux/arm64"
wails build -platform linux/arm64 -clean -ldflags "$LDFLAGS"
if [ $? -eq 0 ]; then
mv "$BUILD_BIN_DIR/${DEFAULT_BINARY_NAME}" "$DIST_DIR/${APP_NAME}-${VERSION}-linux-arm64"
chmod +x "$DIST_DIR/${APP_NAME}-${VERSION}-linux-arm64"
TARGET_LINUX_BIN="$DIST_DIR/${APP_NAME}-${VERSION}-linux-arm64"
mv "$BUILD_BIN_DIR/${DEFAULT_BINARY_NAME}" "$TARGET_LINUX_BIN"
chmod +x "$TARGET_LINUX_BIN"
try_compress_binary_with_upx "$TARGET_LINUX_BIN" "Linux arm64 可执行文件"
cd "$DIST_DIR"
tar -czvf "${APP_NAME}-${VERSION}-linux-arm64.tar.gz" "${APP_NAME}-${VERSION}-linux-arm64"
rm "${APP_NAME}-${VERSION}-linux-arm64"
@@ -234,6 +337,7 @@ elif command -v aarch64-linux-gnu-gcc &> /dev/null; then
echo " ✅ 已生成 ${APP_NAME}-${VERSION}-linux-arm64.tar.gz"
else
echo -e "${RED} ❌ Linux arm64 交叉编译失败。${NC}"
record_build_failure "Linux arm64"
fi
unset CC CXX CGO_ENABLED
else
@@ -251,6 +355,9 @@ if command -v sha256sum &> /dev/null; then
: > SHA256SUMS
for f in *; do
[ -f "$f" ] || continue
case "$f" in
SHA256SUMS|latest.json|latest-dev.json) continue ;;
esac
sha256sum "$f" >> SHA256SUMS
done
cd ..
@@ -259,6 +366,9 @@ elif command -v shasum &> /dev/null; then
: > SHA256SUMS
for f in *; do
[ -f "$f" ] || continue
case "$f" in
SHA256SUMS|latest.json|latest-dev.json) continue ;;
esac
shasum -a 256 "$f" >> SHA256SUMS
done
cd ..
@@ -266,13 +376,51 @@ else
echo -e "${YELLOW} ⚠️ 未找到 sha256sum/shasum跳过校验文件生成。${NC}"
fi
echo -e "${GREEN}📄 生成静态更新清单...${NC}"
if command -v python3 &> /dev/null; then
# 正式版latest.jsondev/test 版本latest-dev.json对应客户端 dev 通道)
case "$VERSION" in
dev-*|*-dev*|*dev*|*test*)
TAG_FOR_MANIFEST="dev-latest"
CHANNEL_FOR_MANIFEST="dev"
OUT_MANIFEST="$DIST_DIR/latest-dev.json"
;;
*)
TAG_FOR_MANIFEST="v${VERSION#v}"
CHANNEL_FOR_MANIFEST="latest"
OUT_MANIFEST="$DIST_DIR/latest.json"
;;
esac
if python3 tools/generate-update-latest-manifest.py \
--assets-dir "$DIST_DIR" \
--version "$VERSION" \
--tag "$TAG_FOR_MANIFEST" \
--channel "$CHANNEL_FOR_MANIFEST" \
--output "$OUT_MANIFEST"; then
echo -e "${GREEN} ✅ 已生成 $OUT_MANIFEST (channel=${CHANNEL_FOR_MANIFEST})${NC}"
else
echo -e "${YELLOW} ⚠️ 生成更新清单失败(不影响本地产物,正式/dev 发版由 CI 生成)${NC}"
fi
else
echo -e "${YELLOW} ⚠️ 未找到 python3跳过更新清单${NC}"
fi
echo ""
echo -e "${GREEN}🎉 所有任务完成!构建产物在 'dist/' 目录下:${NC}"
if [ "${#BUILD_FAILURES[@]}" -gt 0 ]; then
echo -e "${RED}❌ 构建未完全成功,失败平台:${BUILD_FAILURES[*]}${NC}"
echo -e "${YELLOW}📦 已成功生成的产物在 'dist/' 目录下:${NC}"
else
echo -e "${GREEN}🎉 所有任务完成!构建产物在 'dist/' 目录下:${NC}"
fi
ls -lh "$DIST_DIR"
echo ""
echo -e "${GREEN}📋 支持的平台:${NC}"
echo " • macOS (Intel/Apple Silicon): .dmg"
echo " • macOS (Intel/Apple Silicon): .zip"
echo " • Windows (x64/ARM64): .exe"
echo " • Linux (x64/ARM64): .tar.gz"
echo ""
echo -e "${YELLOW}💡 提示Linux AppImage 包请使用 GitHub Actions CI/CD 构建。${NC}"
if [ "${#BUILD_FAILURES[@]}" -gt 0 ]; then
exit 1
fi

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 406 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 524 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 85 KiB

100
build/windows/installer.wxs Normal file
View File

@@ -0,0 +1,100 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs"
xmlns:ui="http://wixtoolset.org/schemas/v4/wxs/ui">
<Package Name="$(var.ProductName)"
Manufacturer="Syngnat"
Language="1033"
Version="$(var.ProductVersion)"
ProductCode="*"
UpgradeCode="$(var.UpgradeCode)"
Scope="perMachine"
InstallerVersion="500"
Compressed="yes">
<MajorUpgrade AllowDowngrades="yes"
Schedule="afterInstallInitialize" />
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
<Icon Id="GoNaviIcon" SourceFile="$(var.IconPath)" />
<Property Id="ARPPRODUCTICON" Value="GoNaviIcon" />
<Property Id="ARPURLINFOABOUT" Value="https://github.com/Syngnat/GoNavi" />
<Property Id="INSTALLDESKTOPSHORTCUT" Value="1" Secure="yes" />
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLFOLDER]" After="CostFinalize" />
<WixVariable Id="WixUILicenseRtf" Value="$(var.LicenseRtf)" />
<Feature Id="MainFeature" Title="$(var.ProductName)" Level="1">
<ComponentRef Id="MainExecutable" />
<ComponentRef Id="DesktopShortcutComponent" />
</Feature>
<Component Id="MainExecutable"
Directory="INSTALLFOLDER"
Guid="$(var.ComponentGuid)"
Bitness="always64">
<File Id="GoNaviExe"
Source="$(var.SourceExe)"
Name="GoNavi.exe"
KeyPath="yes">
<!-- Keep shortcut icons bound to the stable executable path. Referencing the MSI Icon table
can leave Windows shell caches pointing at the removed product cache after a major upgrade. -->
<Shortcut Id="StartMenuShortcut"
Directory="ProgramMenuFolder"
Name="$(var.ProductName)"
WorkingDirectory="INSTALLFOLDER"
Advertise="no" />
</File>
<File Id="GoNaviMsiInstallMarker"
Source="$(var.InstallMarker)"
Name=".gonavi-msi-install" />
<File Id="GoNaviLicense"
Source="$(var.LicenseFile)"
Name="LICENSE.txt" />
<File Id="GoNaviNotice"
Source="$(var.NoticeFile)"
Name="NOTICE.txt" />
<RegistryValue Root="HKLM"
Key="Software\Syngnat\$(var.RegistryKeyName)"
Name="InstallType"
Value="MSI"
Type="string"
KeyPath="no" />
<RegistryValue Root="HKLM"
Key="Software\Syngnat\$(var.RegistryKeyName)"
Name="InstallPath"
Value="[INSTALLFOLDER]GoNavi.exe"
Type="string"
KeyPath="no" />
</Component>
<!-- Online updates pass the user's pre-update desktop shortcut state. Keeping this resource
in its own component lets a major upgrade remove an unwanted old shortcut without
recreating it, while first-time and manual installs retain the existing default. -->
<Component Id="DesktopShortcutComponent"
Directory="DesktopFolder"
Guid="$(var.DesktopShortcutComponentGuid)"
Bitness="always64"
Condition="INSTALLDESKTOPSHORTCUT = 1">
<Shortcut Id="DesktopShortcut"
Name="$(var.ProductName)"
Target="[#GoNaviExe]"
WorkingDirectory="INSTALLFOLDER"
Advertise="no" />
<RegistryValue Root="HKLM"
Key="Software\Syngnat\$(var.RegistryKeyName)"
Name="DesktopShortcutInstalled"
Value="1"
Type="integer"
KeyPath="yes" />
</Component>
<ui:WixUI Id="WixUI_InstallDir"
InstallDirectory="INSTALLFOLDER"
ExtendedPathValidation="yes" />
</Package>
<Fragment>
<StandardDirectory Id="ProgramFiles6432Folder">
<Directory Id="INSTALLFOLDER" Name="$(var.InstallFolderName)" />
</StandardDirectory>
<StandardDirectory Id="ProgramMenuFolder" />
<StandardDirectory Id="DesktopFolder" />
</Fragment>
</Wix>

View File

@@ -0,0 +1,381 @@
# GoNavi MCP Server
`gonavi-mcp-server` 会把 GoNavi 已保存连接背后的数据库能力通过 MCP 暴露给外部客户端。本机客户端默认使用 `stdio`;云端 Agent 可使用显式开启的 Streamable HTTP 模式。
## 当前提供的 tools
- `get_connections`
- 返回 GoNavi 已保存连接的 `id/name/type/target/defaultDatabase` 等摘要信息
- `get_databases`
- 入参:`connectionId`
- `get_tables`
- 入参:`connectionId`、可选 `dbName`
- 返回表列表,并在 `views` 字段附带视图列表,兼容旧客户端只调用 `get_tables` 的场景
- `get_views`
- 入参:`connectionId`、可选 `dbName`
- 返回视图列表
- `get_objects`
- 入参:`connectionId`、可选 `dbName`、可选 `objectTypes`
- 返回表、视图、触发器、函数、过程、序列、包、事件,以及消息队列类 `topic/queue/exchange` 等对象清单
- `get_columns`
- 入参:`connectionId`、可选 `dbName``tableName`
- `get_table_ddl`
- 入参:`connectionId`、可选 `dbName``tableName`
- `execute_sql`
- 入参:`connectionId`、可选 `dbName``sql`
- 默认只允许只读 SQL
- 如果 SQL 包含 DDL/DML必须显式传 `allowMutating=true`
- `maxRowsPerResult` 用来限制单个结果集返回的行数,默认 `200`
远程 Agent 只需要结构元数据时,启动 HTTP 模式请加 `--schema-only`。该模式不注册 `execute_sql`,只保留连接摘要、对象清单、表/视图、字段、索引、外键、触发器和 DDL 工具。
## 运行方式
开发态直接运行:
```powershell
go run ./cmd/gonavi-mcp-server
```
显式运行本机 `stdio`
```powershell
go run ./cmd/gonavi-mcp-server stdio
```
也可以先编译:
```powershell
go build -o .\bin\gonavi-mcp-server.exe .\cmd\gonavi-mcp-server
```
## Docker / Podman / Compose
当前容器化支持仅覆盖 `gonavi-mcp-server`,不包含 Wails 桌面 GUI。
当前支持矩阵:
- Docker Desktop / Linux 服务器 / NAS直接使用 Compose 或 `docker run`
- Podman / Quadlet使用 `deploy/podman/gonavi-mcp-server`
- Kubernetes使用 `deploy/k8s/gonavi-mcp-server`
- Helm使用 `deploy/helm/gonavi-mcp-server`
- 仅构建环境:使用仓库根目录 `Dockerfile.build-env`
- 桌面 GUI 浏览器访问版:当前不提供,此仓库主应用仍是 Wails 桌面程序,不是现成的 Web 服务
仓库根目录已提供以下文件:
- `Dockerfile.mcp-server`
- `docker-compose.mcp-server.yml`
- `docker.mcp-server.env.example`
- `deploy/podman/gonavi-mcp-server/*`
推荐流程:
```bash
cp docker.mcp-server.env.example docker.mcp-server.env
docker compose --env-file docker.mcp-server.env -f docker-compose.mcp-server.yml up -d
```
默认 Compose 会拉取 GHCR 预构建镜像。如果你要基于当前工作区源码本地构建,再叠加:
```bash
docker compose --env-file docker.mcp-server.env \
-f docker-compose.mcp-server.yml \
-f docker-compose.mcp-server.local.yml \
up -d --build
```
其中 `GONAVI_HOST_DATA_ROOT` 必须指向 GoNavi 当前活动数据目录。该目录内至少应包含:
- `connections.json`
- `daily_secrets.json`
- `drivers/`(如果目标连接依赖可选 driver agent
容器内默认会设置:
- `GONAVI_DATA_ROOT=/data`
- `GONAVI_MCP_HTTP_ADDR=0.0.0.0:8765`
- `GONAVI_MCP_HTTP_PATH=/mcp`
`GONAVI_DATA_ROOT` 会覆盖默认活动数据目录解析逻辑,避免宿主机路径与容器内路径不一致时依赖 `storage_root.json` 的绝对路径。
如果你只想手动构建镜像:
```bash
docker build -f Dockerfile.mcp-server -t gonavi-mcp-server:local .
docker run --rm -p 8765:8765 \
-e GONAVI_MCP_HTTP_TOKEN=replace-with-a-random-token \
-e GONAVI_MCP_SCHEMA_ONLY=true \
-e GONAVI_DATA_ROOT=/data \
-v /absolute/path/to/gonavi-data:/data \
gonavi-mcp-server:local http
```
如果你直接使用已发布镜像:
```bash
docker run --rm -p 8765:8765 \
-e GONAVI_MCP_HTTP_TOKEN=replace-with-a-random-token \
-e GONAVI_MCP_SCHEMA_ONLY=true \
-e GONAVI_DATA_ROOT=/data \
-v /absolute/path/to/gonavi-data:/data \
ghcr.io/syngnat/gonavi-mcp-server:latest http
```
### Podman
仓库内还提供了 Podman 原生部署样例:
- `deploy/podman/gonavi-mcp-server/gonavi-mcp-server.env.example`
- `deploy/podman/gonavi-mcp-server/gonavi-mcp-server.container`
- `deploy/podman/gonavi-mcp-server/README.md`
直接运行已发布镜像:
```bash
cp deploy/podman/gonavi-mcp-server/gonavi-mcp-server.env.example ./gonavi-mcp-server.env
podman run -d --name gonavi-mcp-server --replace \
-p 8765:8765 \
--env-file ./gonavi-mcp-server.env \
-v /absolute/path/to/gonavi-data:/data:Z \
ghcr.io/syngnat/gonavi-mcp-server:latest http
```
如果你要基于当前源码本地构建:
```bash
podman build -f Dockerfile.mcp-server -t localhost/gonavi-mcp-server:local .
podman run -d --name gonavi-mcp-server --replace \
-p 8765:8765 \
--env-file ./gonavi-mcp-server.env \
-v /absolute/path/to/gonavi-data:/data:Z \
localhost/gonavi-mcp-server:local http
```
其中:
- `gonavi-mcp-server.env``deploy/podman/gonavi-mcp-server/gonavi-mcp-server.env.example` 初始化
- `:Z` 适用于开启 SELinux 的宿主机;未启用 SELinux 可去掉
- 更适合长期运行的方式见 [deploy/podman/gonavi-mcp-server/README.md](../../deploy/podman/gonavi-mcp-server/README.md) 中的 Quadlet 示例
`podman compose` 本身依赖外部 compose provider所以仓库对 Podman 的主支持路径是 `podman run` 与 Quadlet而不是假设所有环境都能直接复用 Compose。
## Kubernetes
仓库内置了最小 K8s 示例:
- `deploy/k8s/gonavi-mcp-server/kustomization.yaml`
- `deploy/k8s/gonavi-mcp-server/base/deployment.yaml`
- `deploy/k8s/gonavi-mcp-server/base/service.yaml`
- `deploy/k8s/gonavi-mcp-server/README.md`
- `deploy/k8s/gonavi-mcp-server/overlays/*`
推荐先从现有 GoNavi 数据目录生成 Secret
```bash
kubectl create namespace gonavi
kubectl -n gonavi create secret generic gonavi-mcp-server-data \
--from-file=connections.json=/absolute/path/to/gonavi-data/connections.json \
--from-file=daily_secrets.json=/absolute/path/to/gonavi-data/daily_secrets.json \
--from-literal=GONAVI_MCP_HTTP_TOKEN=replace-with-a-random-token
kubectl apply -k deploy/k8s/gonavi-mcp-server
```
如果需要 NAS hostPath、可选 driver agent PVC、Ingress或两者组合可直接使用 `overlays/nas-hostpath``overlays/drivers-pvc``overlays/ingress``overlays/ingress-with-drivers-pvc`
更完整的说明见 [deploy/k8s/gonavi-mcp-server/README.md](../../deploy/k8s/gonavi-mcp-server/README.md)。
## Helm
如果你希望把镜像、Secret、Ingress、hostPath / PVC 挂载做成参数化部署,而不是维护多份 Kustomize overlay可直接使用
- `deploy/helm/gonavi-mcp-server`
快速安装:
```bash
helm upgrade --install gonavi-mcp-server deploy/helm/gonavi-mcp-server -n gonavi --create-namespace
```
Chart 详细说明见 [deploy/helm/gonavi-mcp-server/README.md](../../deploy/helm/gonavi-mcp-server/README.md)。
## Docker / Podman Build Environment
如果你的目标不是运行 MCP而是给 Linux 服务器 / NAS / CI 准备一套可重复的 Wails 构建环境,可直接使用仓库根目录的 `Dockerfile.build-env`
```bash
docker build -f Dockerfile.build-env -t gonavi-build-env:local .
docker run --rm -it -v "$PWD:/workspace" -w /workspace gonavi-build-env:local bash
```
如果你使用 Podman也可以直接执行
```bash
podman build -f Dockerfile.build-env -t localhost/gonavi-build-env:local .
podman run --rm -it -v "$PWD:/workspace" -w /workspace localhost/gonavi-build-env:local bash
```
镜像内已预装 Go、Node、Wails CLI、GTK3 与 WebKitGTK 开发依赖,适合执行:
```bash
wails build
```
这个镜像默认安装 WebKitGTK 4.0 构建依赖,适合作为通用 Linux / NAS 构建环境。镜像基座支持多架构,`amd64` / `arm64` 会跟随容器平台。
预构建镜像会发布到 GHCR
- `ghcr.io/syngnat/gonavi-mcp-server:latest`
- `ghcr.io/syngnat/gonavi-build-env:latest`
它只负责构建 Linux 产物,不会把 Wails 主程序变成浏览器版服务。
远程 Agent 使用 Streamable HTTP 时必须设置 bearer token
```powershell
$env:GONAVI_MCP_HTTP_TOKEN = "<随机token>"
go run ./cmd/gonavi-mcp-server http --addr 127.0.0.1:8765 --path /mcp --schema-only
```
安装包主程序也支持同样模式:
```powershell
& "C:\Program Files\GoNavi\GoNavi.exe" mcp-server http --addr 127.0.0.1:8765 --path /mcp --token "<随机token>" --schema-only
```
默认建议只监听 `127.0.0.1`,再通过 SSH 隧道、反向代理或内网网关暴露给云端 Agent。不要在没有 TLS、防火墙和鉴权的情况下直接监听公网地址。
无图形界面或需要把配置交给云端 Agent 时,可直接生成 OpenClaw / Hermans 等远程 MCP 配置:
```powershell
& "C:\Program Files\GoNavi\GoNavi.exe" mcp-server remote-config --client openclaw --url "https://<你的域名或隧道地址>/mcp" --token "<随机token>" --schema-only
```
独立 server 开发态也支持同样能力:
```powershell
go run ./cmd/gonavi-mcp-server remote-config --client hermans --url "https://<你的域名或隧道地址>/mcp" --token "<随机token>" --schema-only
```
## Claude Code / Codex / OpenClaw / Hermans
正式安装包场景,推荐直接在 GoNavi 里使用“AI 设置 -> MCP 服务 -> 安装到 Claude Code / 安装到 Codex”。
它会自动把当前安装的 `GoNavi.exe` 写入 Claude Code 的用户级 `~/.claude.json`,命令形态类似:
```json
{
"mcpServers": {
"gonavi": {
"type": "stdio",
"command": "C:\\Program Files\\GoNavi\\GoNavi.exe",
"args": ["mcp-server"],
"env": {}
}
}
}
```
这样用户不需要自己找本机 `gonavi-mcp-server.exe` 路径,安装包本体就能直接作为 MCP 入口。
Codex 当前使用 `~/.codex/config.toml`GoNavi 会写入类似下面这段:
```toml
[mcp_servers.gonavi]
command = 'C:\Program Files\GoNavi\GoNavi.exe'
args = ['mcp-server']
startup_timeout_sec = 60
```
仓库开发态如果要在本机 `Claude Code CLI` 里稳定使用这个 MCP仍然推荐走仓库内包装脚本
```powershell
.\tools\claude-gonavi-mcp.ps1 -p "必须调用 gonavi MCP 的 get_connections 工具"
```
或者:
```cmd
tools\claude-gonavi-mcp.cmd -p "必须调用 gonavi MCP 的 get_connections 工具"
```
这个脚本会先构建 `bin\gonavi-mcp-server.exe`,再通过 `--mcp-config``--strict-mcp-config` 把 GoNavi MCP 单独注入当前 Claude 会话,避免默认混合 MCP 加载时序导致的首轮工具未挂载问题。
OpenClaw、Hermans 这类部署在云端或远端 Linux 的 Agent不能直接使用 Windows 本机的 `stdio` 命令。GoNavi 的连接信息和数据库密码仍应留在 Windows 本机,由 GoNavi MCP 读取保存连接和系统凭据;远端 Agent 只拿到 MCP tools 和 `connectionId`
推荐接入形态:
1. Windows 本机运行 GoNavi并保持能访问已保存的数据库连接。
2. 在 Windows 本机启动 `GoNavi.exe mcp-server http --addr 127.0.0.1:8765 --path /mcp --token <随机token> --schema-only`
3. 通过 SSH 隧道、反向代理或内网网关把 `http://127.0.0.1:8765/mcp` 暴露为云端 Agent 可访问的 HTTPS 地址。
4. 在 OpenClaw / Hermans 中添加远程 MCP Servertransport 选择 Streamable HTTPURL 指向 `/mcp` 地址,并设置请求头 `Authorization: Bearer <随机token>`
5. 先调用 `get_connections` 获取 `connectionId`,再调用 `get_databases``get_objects``get_tables``get_views``get_columns``get_table_ddl` 等工具读取结构。
如果目标 Agent 支持 `mcpServers` JSON可按下面的通用片段配置
```json
{
"mcpServers": {
"gonavi": {
"type": "streamable-http",
"url": "https://<你的域名或隧道地址>/mcp",
"headers": {
"Authorization": "Bearer <随机token>"
}
}
}
}
```
不要把数据库 `host/user/password` 写入云端 Agent 的配置文件。默认 `--schema-only` 不暴露 `execute_sql`;如果你明确需要远程执行 SQL可以去掉该参数此时 `execute_sql` 仍受 GoNavi AI 安全设置控制,写操作必须显式传 `allowMutating=true`
## MCP 客户端配置示例
开发态:
```json
{
"mcpServers": {
"gonavi": {
"command": "go",
"args": ["run", "./cmd/gonavi-mcp-server"]
}
}
}
```
Windows 独立 server 编译产物(开发态):
```json
{
"mcpServers": {
"gonavi": {
"command": "D:\\Work\\CodeRepos\\GoNavi\\bin\\gonavi-mcp-server.exe",
"args": []
}
}
}
```
Windows 已安装 GoNavi推荐给最终用户
```json
{
"mcpServers": {
"gonavi": {
"type": "stdio",
"command": "C:\\Program Files\\GoNavi\\GoNavi.exe",
"args": ["mcp-server"],
"env": {}
}
}
}
```
## 使用说明
- 先调用 `get_connections`,拿到 `connectionId`
- 之后所有数据库工具都只传 `connectionId`,由 GoNavi 服务端内部解析保存连接和密钥
- 如果 `dbName` 为空,会优先使用该保存连接里的默认数据库
- Server 会读取 GoNavi 当前活动数据目录里的连接配置,并通过系统 keyring/凭据管理器解析密文
- 如果本机凭据存储不可用,依赖密钥的连接会返回对应错误

View File

@@ -0,0 +1,42 @@
package main
import (
"context"
"fmt"
"log"
"os"
"strings"
"GoNavi-Wails/internal/mcpserver"
)
func main() {
ctx := context.Background()
err := run(ctx, os.Args[1:])
if err != nil {
log.Printf("GoNavi MCP Server 退出: %v", err)
}
}
func run(ctx context.Context, args []string) error {
if len(args) == 0 {
return mcpserver.RunAppStdioServer(ctx)
}
mode := strings.ToLower(strings.TrimSpace(args[0]))
switch mode {
case "stdio", "--stdio":
return mcpserver.RunAppStdioServer(ctx)
case "http", "--http", "streamable-http", "--streamable-http":
options, err := mcpserver.ParseHTTPServerOptions(args[1:])
if err != nil {
return err
}
log.Printf("GoNavi MCP Streamable HTTP Server 启动addr=%s path=%s schemaOnly=%v", options.Addr, options.Path, options.SchemaOnly)
return mcpserver.RunAppStreamableHTTPServer(ctx, options)
case "remote-config", "--remote-config":
return mcpserver.WriteRemoteMCPClientConfig(os.Stdout, args[1:])
default:
return fmt.Errorf("未知 MCP server 模式: %s支持 stdio/http/remote-config", args[0])
}
}

339
cmd/manualtestseed/main.go Normal file
View File

@@ -0,0 +1,339 @@
package main
import (
"encoding/json"
"flag"
"fmt"
"os"
"path/filepath"
"strings"
"time"
"GoNavi-Wails/internal/ai"
aiservice "GoNavi-Wails/internal/ai/service"
"GoNavi-Wails/internal/app"
"GoNavi-Wails/internal/connection"
"GoNavi-Wails/internal/secretstore"
)
const (
modeSeedSecureStorage = "seed-secure-storage"
modeSeedAIUpdate = "seed-ai-update"
)
const (
testConnectionID = "manualtest-postgres"
testSecureProviderID = "manualtest-secure-provider"
testPendingProviderID = "manualtest-pending-provider"
testBackupDirName = "manual-test-backups"
connectionsFileName = "connections.json"
globalProxyFileName = "global_proxy.json"
aiConfigFileName = "ai_config.json"
securityUpdateFileName = "config-security-update.json"
)
type backupManifest struct {
CreatedAt string `json:"createdAt"`
ConfigDir string `json:"configDir"`
Files []backupManifestFile `json:"files"`
}
type backupManifestFile struct {
RelativePath string `json:"relativePath"`
Existed bool `json:"existed"`
}
type storedAIConfig struct {
SchemaVersion int `json:"schemaVersion,omitempty"`
Providers []ai.ProviderConfig `json:"providers"`
ActiveProvider string `json:"activeProvider"`
SafetyLevel string `json:"safetyLevel"`
ContextLevel string `json:"contextLevel"`
}
func main() {
mode := flag.String("mode", modeSeedSecureStorage, "seed mode: seed-secure-storage | seed-ai-update")
flag.Parse()
configDir, err := resolveConfigDir()
if err != nil {
fatalf("resolve config dir failed: %v", err)
}
store := secretstore.NewKeyringStore()
if err := store.HealthCheck(); err != nil {
fatalf("secret store unavailable: %v", err)
}
backupDir, err := backupConfigFiles(configDir)
if err != nil {
fatalf("backup config files failed: %v", err)
}
switch strings.TrimSpace(*mode) {
case modeSeedSecureStorage:
if err := seedSecureStorage(configDir, store); err != nil {
fatalf("seed secure storage failed: %v", err)
}
fmt.Printf("mode=%s\nbackup=%s\nconnectionId=%s\nproviderId=%s\n", modeSeedSecureStorage, backupDir, testConnectionID, testSecureProviderID)
case modeSeedAIUpdate:
if err := seedAIUpdate(configDir, store); err != nil {
fatalf("seed ai update failed: %v", err)
}
fmt.Printf("mode=%s\nbackup=%s\npendingProviderId=%s\n", modeSeedAIUpdate, backupDir, testPendingProviderID)
default:
fatalf("unsupported mode: %s", *mode)
}
}
func fatalf(format string, args ...any) {
fmt.Fprintf(os.Stderr, format+"\n", args...)
os.Exit(1)
}
func resolveConfigDir() (string, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return "", err
}
return filepath.Join(homeDir, ".gonavi"), nil
}
func backupConfigFiles(configDir string) (string, error) {
backupDir := filepath.Join(configDir, testBackupDirName, time.Now().Format("20060102-150405"))
files := []string{
connectionsFileName,
globalProxyFileName,
aiConfigFileName,
filepath.Join("migrations", securityUpdateFileName),
}
manifest := backupManifest{
CreatedAt: time.Now().Format(time.RFC3339),
ConfigDir: configDir,
Files: make([]backupManifestFile, 0, len(files)),
}
for _, relativePath := range files {
srcPath := filepath.Join(configDir, relativePath)
info, err := os.Stat(srcPath)
if err != nil {
if os.IsNotExist(err) {
manifest.Files = append(manifest.Files, backupManifestFile{
RelativePath: relativePath,
Existed: false,
})
continue
}
return "", err
}
if info.IsDir() {
continue
}
dstPath := filepath.Join(backupDir, relativePath)
if err := os.MkdirAll(filepath.Dir(dstPath), 0o755); err != nil {
return "", err
}
data, err := os.ReadFile(srcPath)
if err != nil {
return "", err
}
if err := os.WriteFile(dstPath, data, 0o644); err != nil {
return "", err
}
manifest.Files = append(manifest.Files, backupManifestFile{
RelativePath: relativePath,
Existed: true,
})
}
if err := os.MkdirAll(backupDir, 0o755); err != nil {
return "", err
}
manifestData, err := json.MarshalIndent(manifest, "", " ")
if err != nil {
return "", err
}
if err := os.WriteFile(filepath.Join(backupDir, "manifest.json"), manifestData, 0o644); err != nil {
return "", err
}
return backupDir, nil
}
func seedSecureStorage(configDir string, store secretstore.SecretStore) error {
if err := cleanupKnownTestSecrets(store); err != nil {
return err
}
appService := app.NewAppWithSecretStore(store)
_ = appService.DeleteConnection(testConnectionID)
if _, err := appService.SaveConnection(connection.SavedConnectionInput{
ID: testConnectionID,
Name: "手工测试 PostgreSQL",
Config: connection.ConnectionConfig{
ID: testConnectionID,
Type: "postgres",
Host: "127.0.0.1",
Port: 5432,
User: "postgres",
Password: "manualtest-pg-secret",
Database: "postgres",
},
}); err != nil {
return err
}
if _, err := appService.SaveGlobalProxy(connection.SaveGlobalProxyInput{
Enabled: true,
Type: "http",
Host: "127.0.0.1",
Port: 7890,
User: "manual-test",
Password: "manualtest-proxy-secret",
}); err != nil {
return err
}
storeConfig := aiservice.NewProviderConfigStore(configDir, store)
snapshot, err := storeConfig.LoadRuntime()
if err != nil {
return err
}
snapshot.Providers = filterProviders(snapshot.Providers, testSecureProviderID, testPendingProviderID)
snapshot.Providers = append(snapshot.Providers, ai.ProviderConfig{
ID: testSecureProviderID,
Type: "custom",
Name: "手工测试 Secure Provider",
APIKey: "manualtest-ai-secret",
BaseURL: "https://api.openai.com/v1",
Model: "gpt-4o-mini",
APIFormat: "openai",
Headers: map[string]string{
"Authorization": "Bearer manualtest-header-secret",
"X-Trace-Id": "manualtest-visible",
},
MaxTokens: 2048,
Temperature: 0.2,
})
if snapshot.SafetyLevel == "" {
snapshot.SafetyLevel = ai.PermissionReadOnly
}
if snapshot.ContextLevel == "" {
snapshot.ContextLevel = ai.ContextSchemaOnly
}
return storeConfig.Save(snapshot)
}
func seedAIUpdate(configDir string, store secretstore.SecretStore) error {
if err := cleanupKnownTestSecrets(store); err != nil {
return err
}
configPath := filepath.Join(configDir, aiConfigFileName)
cfg, err := readStoredAIConfig(configPath)
if err != nil {
return err
}
cfg.Providers = filterProviders(cfg.Providers, testSecureProviderID, testPendingProviderID)
cfg.Providers = append(cfg.Providers, ai.ProviderConfig{
ID: testPendingProviderID,
Type: "custom",
Name: "手工测试 待迁移 AI",
APIKey: "manualtest-ai-update-secret",
BaseURL: "https://api.openai.com/v1",
Model: "gpt-4o-mini",
APIFormat: "openai",
MaxTokens: 1024,
})
if cfg.SchemaVersion == 0 {
cfg.SchemaVersion = 2
}
if cfg.Providers == nil {
cfg.Providers = []ai.ProviderConfig{}
}
if err := os.MkdirAll(configDir, 0o755); err != nil {
return err
}
data, err := json.MarshalIndent(cfg, "", " ")
if err != nil {
return err
}
return os.WriteFile(configPath, data, 0o644)
}
func readStoredAIConfig(configPath string) (storedAIConfig, error) {
cfg := storedAIConfig{
Providers: []ai.ProviderConfig{},
SafetyLevel: string(ai.PermissionReadOnly),
ContextLevel: string(ai.ContextSchemaOnly),
SchemaVersion: 2,
ActiveProvider: "",
}
data, err := os.ReadFile(configPath)
if err != nil {
if os.IsNotExist(err) {
return cfg, nil
}
return storedAIConfig{}, err
}
if err := json.Unmarshal(data, &cfg); err != nil {
return storedAIConfig{}, err
}
if cfg.Providers == nil {
cfg.Providers = []ai.ProviderConfig{}
}
return cfg, nil
}
func filterProviders(providers []ai.ProviderConfig, excludedIDs ...string) []ai.ProviderConfig {
excluded := make(map[string]struct{}, len(excludedIDs))
for _, id := range excludedIDs {
excluded[strings.TrimSpace(id)] = struct{}{}
}
filtered := make([]ai.ProviderConfig, 0, len(providers))
for _, provider := range providers {
if _, skip := excluded[strings.TrimSpace(provider.ID)]; skip {
continue
}
filtered = append(filtered, provider)
}
return filtered
}
func cleanupKnownTestSecrets(store secretstore.SecretStore) error {
type secretRef struct {
kind string
id string
}
refs := []secretRef{
{kind: "connection", id: testConnectionID},
{kind: "global-proxy", id: "default"},
{kind: "ai-provider", id: testSecureProviderID},
{kind: "ai-provider", id: testPendingProviderID},
}
for _, item := range refs {
ref, err := secretstore.BuildRef(item.kind, item.id)
if err != nil {
return err
}
if err := store.Delete(ref); err != nil && !isIgnorableDeleteError(err) {
return err
}
}
return nil
}
func isIgnorableDeleteError(err error) bool {
if err == nil || os.IsNotExist(err) {
return true
}
message := strings.ToLower(strings.TrimSpace(err.Error()))
return strings.Contains(message, "could not be found") ||
strings.Contains(message, "not be found in the keyring") ||
strings.Contains(message, "element not found")
}

View File

@@ -0,0 +1,27 @@
package main
import (
"flag"
"fmt"
"os"
"GoNavi-Wails/internal/buildutil"
)
func main() {
var (
dllPath string
dlltoolPath string
outputLib string
)
flag.StringVar(&dllPath, "dll", "", "Path to the source DLL")
flag.StringVar(&dlltoolPath, "dlltool", "", "Optional path to dlltool executable")
flag.StringVar(&outputLib, "output-lib", "", "Output import library path")
flag.Parse()
if err := buildutil.GenerateWindowsImportLibraryFromDLL(dllPath, dlltoolPath, outputLib); err != nil {
fmt.Fprintf(os.Stderr, "generate mingw import library failed: %v\n", err)
os.Exit(1)
}
}

View File

@@ -2,10 +2,17 @@ package main
import (
"bufio"
"context"
"encoding/json"
"fmt"
"os"
"reflect"
"runtime"
"runtime/debug"
"strconv"
"strings"
"sync/atomic"
"time"
"GoNavi-Wails/internal/connection"
"GoNavi-Wails/internal/db"
@@ -14,8 +21,10 @@ import (
type agentRequest struct {
ID int64 `json:"id"`
Method string `json:"method"`
SessionID string `json:"sessionId,omitempty"`
Config *connection.ConnectionConfig `json:"config,omitempty"`
Query string `json:"query,omitempty"`
TimeoutMs int64 `json:"timeoutMs,omitempty"`
DBName string `json:"dbName,omitempty"`
TableName string `json:"tableName,omitempty"`
Changes *connection.ChangeSet `json:"changes,omitempty"`
@@ -27,14 +36,21 @@ type agentResponse struct {
Error string `json:"error,omitempty"`
Data interface{} `json:"data,omitempty"`
Fields []string `json:"fields,omitempty"`
Messages []string `json:"messages,omitempty"`
ChunkType string `json:"chunkType,omitempty"`
RowsAffected int64 `json:"rowsAffected,omitempty"`
}
const (
agentMethodConnect = "connect"
agentMethodClose = "close"
agentMethodMetadata = "metadata"
agentMethodPing = "ping"
agentMethodOpenSession = "openSession"
agentMethodCloseSession = "closeSession"
agentMethodQuery = "query"
agentMethodQueryMulti = "queryMulti"
agentMethodStreamQuery = "streamQuery"
agentMethodExec = "exec"
agentMethodGetDatabases = "getDatabases"
agentMethodGetTables = "getTables"
@@ -47,23 +63,69 @@ const (
agentMethodApplyChanges = "applyChanges"
)
var (
agentDriverType string
agentDatabaseFactory func() db.Database
const legacyClickHouseDefaultTimeout = 2 * time.Hour
const (
agentChunkColumns = "columns"
agentChunkRows = "rows"
agentChunkDone = "done"
// agentStreamBatchSize 控制 driver-agent 向主进程发送 row chunk 的批次大小。
// 调小到 64单批 JSON 编码 + 主进程解码的瞬时内存峰值降为原来的 1/4
// 代价是 IPC 次数变为 4 倍,但每批仅一次 stdin/stdout 行读写,整体影响可忽略。
// 重要:减小批次不能根除内存峰值,仍需配合 SetGCPercent + 周期 GC见 main
agentStreamBatchSize = 64
agentMemoryTrimRowsThreshold = 100000
agentMemoryTrimMinInterval = 3 * time.Second
)
var (
agentDriverType string
agentDatabaseFactory func() db.Database
agentMemoryTrimRunning atomic.Bool
agentMemoryTrimLastAt atomic.Int64
runAgentMemoryTrimAsync = func(fn func()) {
go fn()
}
agentMemoryTrimFn = func() {
runtime.GC()
debug.FreeOSMemory()
}
)
type agentRuntime struct {
inst db.Database
sessions map[string]db.StatementExecer
nextSessionID int64
}
func main() {
if agentDatabaseFactory == nil || strings.TrimSpace(agentDriverType) == "" {
fmt.Fprintf(os.Stderr, "未配置驱动代理 provider请使用 gonavi_<driver>_driver 标签构建\n")
os.Exit(2)
}
// driver-agent 是独立进程,主进程无法控制其 GC 行为。
// 大结果集88W+ 行)通过 JSON-lines 跨进程传输时,每行有 5-8 倍内存副本;
// Go 默认 GOGC=100 + Windows MADV_FREE 不归还 RSS会导致 driver-agent 进程
// 内存峰值达到数据总量的 10+ 倍(用户实测 88W 普通业务表撑到 8G+)。
//
// GC 策略组合:
// - SetGCPercent(50):堆增长 50% 即触发 GC比默认 100 更早收敛
// - InitMemorySoftLimit起始 2GB运行时由 MaybeGrowMemoryLimit 自适应抬升到最多 8GB
// (起步保守 + 按需扩张,避免静态 2GB 限制在大表场景触发 GC 硬模式降速 15-25%
//
// 代价CPU 开销增加约 5-10%。导出场景是 I/O 密集型,可忽略。
debug.SetGCPercent(50)
db.InitMemorySoftLimit(db.MemorySoftLimitInitialBytes)
scanner := bufio.NewScanner(os.Stdin)
scanner.Buffer(make([]byte, 0, 16<<10), 8<<20)
writer := bufio.NewWriter(os.Stdout)
defer writer.Flush()
var inst db.Database
runtimeState := &agentRuntime{
sessions: make(map[string]db.StatementExecer),
}
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if line == "" {
@@ -80,23 +142,32 @@ func main() {
continue
}
resp := handleRequest(&inst, req)
if strings.TrimSpace(req.Method) == agentMethodStreamQuery {
if err := handleStreamRequest(runtimeState, req, writer); err != nil {
fmt.Fprintf(os.Stderr, "写入流式响应失败:%v\n", err)
break
}
continue
}
resp := handleRequest(runtimeState, req)
if err := writeResponse(writer, resp); err != nil {
fmt.Fprintf(os.Stderr, "写入响应失败:%v\n", err)
break
}
if strings.TrimSpace(req.Method) == agentMethodQuery {
maybeReleaseAgentMemory("query-response", countAgentResponseRows(resp.Data))
}
}
if inst != nil {
_ = inst.Close()
}
runtimeState.close()
if err := scanner.Err(); err != nil {
fmt.Fprintf(os.Stderr, "读取请求失败:%v\n", err)
}
}
func handleRequest(inst *db.Database, req agentRequest) agentResponse {
func handleRequest(runtimeState *agentRuntime, req agentRequest) agentResponse {
resp := agentResponse{ID: req.ID, Success: true}
method := strings.TrimSpace(req.Method)
@@ -105,9 +176,7 @@ func handleRequest(inst *db.Database, req agentRequest) agentResponse {
if req.Config == nil {
return fail(resp, "连接配置为空")
}
if *inst != nil {
_ = (*inst).Close()
}
runtimeState.close()
next := agentDatabaseFactory()
if next == nil {
return fail(resp, "驱动代理初始化失败")
@@ -115,84 +184,162 @@ func handleRequest(inst *db.Database, req agentRequest) agentResponse {
if err := next.Connect(*req.Config); err != nil {
return fail(resp, err.Error())
}
*inst = next
runtimeState.inst = next
return resp
case agentMethodClose:
if *inst != nil {
if err := (*inst).Close(); err != nil {
if runtimeState.inst != nil {
if err := runtimeState.close(); err != nil {
return fail(resp, err.Error())
}
*inst = nil
}
return resp
case agentMethodMetadata:
resp.Data = map[string]string{
"driverType": strings.TrimSpace(agentDriverType),
"agentRevision": db.OptionalDriverAgentRevision(agentDriverType),
"protocolSchema": "json-lines-v1",
}
return resp
case agentMethodOpenSession:
if runtimeState.inst == nil {
return fail(resp, "connection not open")
}
provider, ok := runtimeState.inst.(db.SessionExecerProvider)
if !ok {
return fail(resp, fmt.Sprintf("当前数据源(%s不支持 SQL 编辑器托管事务", strings.TrimSpace(agentDriverType)))
}
openCtx := context.Background()
var cancel context.CancelFunc
if req.TimeoutMs > 0 {
openCtx, cancel = context.WithTimeout(context.Background(), time.Duration(req.TimeoutMs)*time.Millisecond)
defer cancel()
}
session, err := provider.OpenSessionExecer(openCtx)
if err != nil {
return fail(resp, err.Error())
}
sessionID := runtimeState.nextID()
runtimeState.sessions[sessionID] = session
resp.Data = sessionID
return resp
case agentMethodCloseSession:
if err := runtimeState.closeSession(req.SessionID); err != nil {
return fail(resp, err.Error())
}
return resp
}
if *inst == nil {
if runtimeState.inst == nil {
return fail(resp, "connection not open")
}
if session, ok, err := runtimeState.session(req.SessionID); err != nil {
return fail(resp, err.Error())
} else if ok {
switch method {
case agentMethodQuery:
data, fields, messages, err := queryStatementWithMessagesOptionalTimeout(session, req.Query, req.TimeoutMs)
if err != nil {
return fail(resp, err.Error())
}
resp.Data = data
resp.Fields = fields
resp.Messages = messages
case agentMethodQueryMulti:
data, messages, supported, err := queryMultiStatementWithMessagesOptionalTimeout(session, req.Query, req.TimeoutMs)
if err != nil {
return fail(resp, err.Error())
}
if !supported {
return fail(resp, "当前事务会话不支持多结果集查询")
}
resp.Data = data
resp.Messages = messages
case agentMethodExec:
affected, err := execStatementWithOptionalTimeout(session, req.Query, req.TimeoutMs)
if err != nil {
return fail(resp, err.Error())
}
resp.RowsAffected = affected
default:
return fail(resp, "当前事务会话不支持该方法")
}
return resp
}
switch method {
case agentMethodPing:
if err := (*inst).Ping(); err != nil {
if err := runtimeState.inst.Ping(); err != nil {
return fail(resp, err.Error())
}
case agentMethodQuery:
data, fields, err := (*inst).Query(req.Query)
data, fields, messages, err := queryWithMessagesOptionalTimeout(runtimeState.inst, req.Query, req.TimeoutMs)
if err != nil {
return fail(resp, err.Error())
}
resp.Data = data
resp.Fields = fields
resp.Messages = messages
case agentMethodQueryMulti:
data, messages, supported, err := queryMultiWithMessagesOptionalTimeout(runtimeState.inst, req.Query, req.TimeoutMs)
if err != nil {
return fail(resp, err.Error())
}
if !supported {
return fail(resp, "当前驱动不支持原生多结果集查询")
}
resp.Data = data
resp.Messages = messages
case agentMethodExec:
affected, err := (*inst).Exec(req.Query)
affected, err := execWithOptionalTimeout(runtimeState.inst, req.Query, req.TimeoutMs)
if err != nil {
return fail(resp, err.Error())
}
resp.RowsAffected = affected
case agentMethodGetDatabases:
data, err := (*inst).GetDatabases()
data, err := runtimeState.inst.GetDatabases()
if err != nil {
return fail(resp, err.Error())
}
resp.Data = data
case agentMethodGetTables:
data, err := (*inst).GetTables(req.DBName)
data, err := runtimeState.inst.GetTables(req.DBName)
if err != nil {
return fail(resp, err.Error())
}
resp.Data = data
case agentMethodGetCreateStmt:
data, err := (*inst).GetCreateStatement(req.DBName, req.TableName)
data, err := runtimeState.inst.GetCreateStatement(req.DBName, req.TableName)
if err != nil {
return fail(resp, err.Error())
}
resp.Data = data
case agentMethodGetColumns:
data, err := (*inst).GetColumns(req.DBName, req.TableName)
data, err := runtimeState.inst.GetColumns(req.DBName, req.TableName)
if err != nil {
return fail(resp, err.Error())
}
resp.Data = data
case agentMethodGetAllColumns:
data, err := (*inst).GetAllColumns(req.DBName)
data, err := runtimeState.inst.GetAllColumns(req.DBName)
if err != nil {
return fail(resp, err.Error())
}
resp.Data = data
case agentMethodGetIndexes:
data, err := (*inst).GetIndexes(req.DBName, req.TableName)
data, err := runtimeState.inst.GetIndexes(req.DBName, req.TableName)
if err != nil {
return fail(resp, err.Error())
}
resp.Data = data
case agentMethodGetForeignKey:
data, err := (*inst).GetForeignKeys(req.DBName, req.TableName)
data, err := runtimeState.inst.GetForeignKeys(req.DBName, req.TableName)
if err != nil {
return fail(resp, err.Error())
}
resp.Data = data
case agentMethodGetTriggers:
data, err := (*inst).GetTriggers(req.DBName, req.TableName)
data, err := runtimeState.inst.GetTriggers(req.DBName, req.TableName)
if err != nil {
return fail(resp, err.Error())
}
@@ -201,7 +348,7 @@ func handleRequest(inst *db.Database, req agentRequest) agentResponse {
if req.Changes == nil {
return fail(resp, "变更集为空")
}
applier, ok := (*inst).(interface {
applier, ok := runtimeState.inst.(interface {
ApplyChanges(tableName string, changes connection.ChangeSet) error
})
if !ok {
@@ -217,8 +364,175 @@ func handleRequest(inst *db.Database, req agentRequest) agentResponse {
return resp
}
type agentStreamResponseWriter struct {
writer *bufio.Writer
requestID int64
columns []string
rows [][]interface{}
rowCount int64
}
func newAgentStreamResponseWriter(writer *bufio.Writer, requestID int64) *agentStreamResponseWriter {
return &agentStreamResponseWriter{
writer: writer,
requestID: requestID,
}
}
func (w *agentStreamResponseWriter) SetColumns(columns []string) error {
w.columns = append([]string(nil), columns...)
return writeResponse(w.writer, agentResponse{
ID: w.requestID,
Success: true,
ChunkType: agentChunkColumns,
Fields: w.columns,
})
}
func (w *agentStreamResponseWriter) ConsumeRow(row map[string]interface{}) error {
if len(w.columns) == 0 {
return fmt.Errorf("流式查询缺少列定义")
}
values := make([]interface{}, len(w.columns))
for idx, column := range w.columns {
values[idx] = row[column]
}
return w.ConsumeRowValues(values)
}
func (w *agentStreamResponseWriter) ConsumeRowValues(values []interface{}) error {
row := append([]interface{}(nil), values...)
w.rows = append(w.rows, row)
w.rowCount++
if len(w.rows) < agentStreamBatchSize {
return nil
}
return w.flushRows()
}
func (w *agentStreamResponseWriter) flushRows() error {
if len(w.rows) == 0 {
return nil
}
rows := w.rows
w.rows = nil
return writeResponse(w.writer, agentResponse{
ID: w.requestID,
Success: true,
ChunkType: agentChunkRows,
Data: rows,
})
}
func (w *agentStreamResponseWriter) finish() error {
return w.flushRows()
}
func handleStreamRequest(runtimeState *agentRuntime, req agentRequest, writer *bufio.Writer) error {
resp := agentResponse{ID: req.ID, Success: true}
if runtimeState.inst == nil {
return writeResponse(writer, fail(resp, "connection not open"))
}
streamWriter := newAgentStreamResponseWriter(writer, req.ID)
if session, ok, err := runtimeState.session(req.SessionID); err != nil {
return writeResponse(writer, fail(resp, err.Error()))
} else if ok {
if err := streamStatementWithOptionalTimeout(session, req.Query, req.TimeoutMs, streamWriter); err != nil {
_ = streamWriter.finish()
return writeResponse(writer, fail(resp, err.Error()))
}
if err := streamWriter.finish(); err != nil {
return err
}
if err := writeResponse(writer, agentResponse{ID: req.ID, Success: true, ChunkType: agentChunkDone}); err != nil {
return err
}
maybeReleaseAgentMemory("stream-query-session", streamWriter.rowCount)
return nil
}
if err := streamDatabaseWithOptionalTimeout(runtimeState.inst, req.Query, req.TimeoutMs, streamWriter); err != nil {
_ = streamWriter.finish()
return writeResponse(writer, fail(resp, err.Error()))
}
if err := streamWriter.finish(); err != nil {
return err
}
if err := writeResponse(writer, agentResponse{ID: req.ID, Success: true, ChunkType: agentChunkDone}); err != nil {
return err
}
maybeReleaseAgentMemory("stream-query-db", streamWriter.rowCount)
return nil
}
func (r *agentRuntime) nextID() string {
r.ensureSessionMap()
r.nextSessionID++
return "session-" + strconv.FormatInt(r.nextSessionID, 10)
}
func (r *agentRuntime) session(sessionID string) (db.StatementExecer, bool, error) {
r.ensureSessionMap()
sessionID = strings.TrimSpace(sessionID)
if sessionID == "" {
return nil, false, nil
}
session, ok := r.sessions[sessionID]
if !ok || session == nil {
return nil, false, fmt.Errorf("事务会话不存在或已结束")
}
return session, true, nil
}
func (r *agentRuntime) closeSession(sessionID string) error {
r.ensureSessionMap()
sessionID = strings.TrimSpace(sessionID)
if sessionID == "" {
return fmt.Errorf("事务会话 ID 不能为空")
}
session, ok := r.sessions[sessionID]
if ok {
delete(r.sessions, sessionID)
}
if !ok || session == nil {
return fmt.Errorf("事务会话不存在或已结束")
}
return session.Close()
}
func (r *agentRuntime) close() error {
var closeErr error
r.ensureSessionMap()
for sessionID, session := range r.sessions {
delete(r.sessions, sessionID)
if session != nil {
if err := session.Close(); err != nil && closeErr == nil {
closeErr = err
}
}
}
if r.inst != nil {
if err := r.inst.Close(); err != nil && closeErr == nil {
closeErr = err
}
r.inst = nil
}
return closeErr
}
func (r *agentRuntime) ensureSessionMap() {
if r.sessions == nil {
r.sessions = make(map[string]db.StatementExecer)
}
}
func writeResponse(writer *bufio.Writer, resp agentResponse) error {
payload, err := json.Marshal(resp)
// 对响应数据做统一 JSON 安全归一化:
// 将 map[any]any如 duckdb.Map递归转换为 map[string]any避免序列化失败导致代理进程退出。
safeResp := resp
safeResp.Data = normalizeAgentResponseData(resp.Data)
payload, err := json.Marshal(safeResp)
if err != nil {
return err
}
@@ -234,3 +548,336 @@ func fail(resp agentResponse, errText string) agentResponse {
resp.Error = strings.TrimSpace(errText)
return resp
}
func normalizeAgentResponseData(v interface{}) interface{} {
if v == nil {
return nil
}
rv := reflect.ValueOf(v)
switch rv.Kind() {
case reflect.Pointer, reflect.Interface:
if rv.IsNil() {
return nil
}
return normalizeAgentResponseData(rv.Elem().Interface())
case reflect.Map:
if rv.IsNil() {
return nil
}
out := make(map[string]interface{}, rv.Len())
iter := rv.MapRange()
for iter.Next() {
out[fmt.Sprint(iter.Key().Interface())] = normalizeAgentResponseData(iter.Value().Interface())
}
return out
case reflect.Slice:
if rv.IsNil() {
return nil
}
// 保持 []byte 原样,避免改变现有二进制列的 JSON 编码行为base64
if rv.Type().Elem().Kind() == reflect.Uint8 {
return v
}
size := rv.Len()
items := make([]interface{}, size)
for i := 0; i < size; i++ {
items[i] = normalizeAgentResponseData(rv.Index(i).Interface())
}
return items
case reflect.Array:
size := rv.Len()
items := make([]interface{}, size)
for i := 0; i < size; i++ {
items[i] = normalizeAgentResponseData(rv.Index(i).Interface())
}
return items
default:
return v
}
}
type agentQueryRunner interface {
Query(string) ([]map[string]interface{}, []string, error)
}
type agentQueryContextRunner interface {
QueryContext(context.Context, string) ([]map[string]interface{}, []string, error)
}
type agentQueryMessageRunner interface {
QueryWithMessages(query string) ([]map[string]interface{}, []string, []string, error)
}
type agentQueryMessageContextRunner interface {
QueryContextWithMessages(context.Context, string) ([]map[string]interface{}, []string, []string, error)
}
type agentMultiResultMessageRunner interface {
QueryMultiWithMessages(query string) ([]connection.ResultSetData, []string, error)
}
type agentMultiResultMessageContextRunner interface {
QueryMultiContextWithMessages(context.Context, string) ([]connection.ResultSetData, []string, error)
}
type agentMultiResultRunner interface {
QueryMulti(query string) ([]connection.ResultSetData, error)
}
type agentMultiResultContextRunner interface {
QueryMultiContext(context.Context, string) ([]connection.ResultSetData, error)
}
type agentExecRunner interface {
Exec(string) (int64, error)
}
type agentExecContextRunner interface {
ExecContext(context.Context, string) (int64, error)
}
func queryWithMessagesOptionalTimeout(inst agentQueryRunner, query string, timeoutMs int64) ([]map[string]interface{}, []string, []string, error) {
effectiveTimeoutMs := timeoutMs
if effectiveTimeoutMs <= 0 && strings.EqualFold(strings.TrimSpace(agentDriverType), "clickhouse") {
effectiveTimeoutMs = int64(legacyClickHouseDefaultTimeout / time.Millisecond)
}
if effectiveTimeoutMs <= 0 {
if q, ok := inst.(agentQueryMessageRunner); ok {
return q.QueryWithMessages(query)
}
data, fields, err := inst.Query(query)
return data, fields, nil, err
}
if q, ok := inst.(agentQueryMessageContextRunner); ok {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(effectiveTimeoutMs)*time.Millisecond)
defer cancel()
return q.QueryContextWithMessages(ctx, query)
}
if q, ok := inst.(agentQueryContextRunner); ok {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(effectiveTimeoutMs)*time.Millisecond)
defer cancel()
data, fields, err := q.QueryContext(ctx, query)
return data, fields, nil, err
}
if q, ok := inst.(agentQueryMessageRunner); ok {
return q.QueryWithMessages(query)
}
data, fields, err := inst.Query(query)
return data, fields, nil, err
}
func queryWithOptionalTimeout(inst agentQueryRunner, query string, timeoutMs int64) ([]map[string]interface{}, []string, error) {
data, fields, _, err := queryWithMessagesOptionalTimeout(inst, query, timeoutMs)
return data, fields, err
}
func queryStatementWithOptionalTimeout(inst db.StatementExecer, query string, timeoutMs int64) ([]map[string]interface{}, []string, error) {
queryRunner, ok := inst.(agentQueryRunner)
if !ok {
return nil, nil, fmt.Errorf("当前事务会话不支持查询语句")
}
return queryWithOptionalTimeout(queryRunner, query, timeoutMs)
}
func queryStatementWithMessagesOptionalTimeout(inst db.StatementExecer, query string, timeoutMs int64) ([]map[string]interface{}, []string, []string, error) {
queryRunner, ok := inst.(agentQueryRunner)
if !ok {
return nil, nil, nil, fmt.Errorf("当前事务会话不支持查询语句")
}
return queryWithMessagesOptionalTimeout(queryRunner, query, timeoutMs)
}
func queryMultiWithMessagesOptionalTimeout(inst db.Database, query string, timeoutMs int64) ([]connection.ResultSetData, []string, bool, error) {
effectiveTimeoutMs := timeoutMs
if effectiveTimeoutMs <= 0 && strings.EqualFold(strings.TrimSpace(agentDriverType), "clickhouse") {
effectiveTimeoutMs = int64(legacyClickHouseDefaultTimeout / time.Millisecond)
}
if effectiveTimeoutMs > 0 {
if q, ok := inst.(agentMultiResultMessageContextRunner); ok {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(effectiveTimeoutMs)*time.Millisecond)
defer cancel()
data, messages, err := q.QueryMultiContextWithMessages(ctx, query)
return data, messages, true, err
}
if q, ok := inst.(agentMultiResultContextRunner); ok {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(effectiveTimeoutMs)*time.Millisecond)
defer cancel()
data, err := q.QueryMultiContext(ctx, query)
return data, nil, true, err
}
}
if q, ok := inst.(agentMultiResultMessageRunner); ok {
data, messages, err := q.QueryMultiWithMessages(query)
return data, messages, true, err
}
if q, ok := inst.(agentMultiResultRunner); ok {
data, err := q.QueryMulti(query)
return data, nil, true, err
}
return nil, nil, false, nil
}
func queryMultiStatementWithMessagesOptionalTimeout(inst db.StatementExecer, query string, timeoutMs int64) ([]connection.ResultSetData, []string, bool, error) {
effectiveTimeoutMs := timeoutMs
if effectiveTimeoutMs <= 0 && strings.EqualFold(strings.TrimSpace(agentDriverType), "clickhouse") {
effectiveTimeoutMs = int64(legacyClickHouseDefaultTimeout / time.Millisecond)
}
if effectiveTimeoutMs > 0 {
if q, ok := inst.(agentMultiResultMessageContextRunner); ok {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(effectiveTimeoutMs)*time.Millisecond)
defer cancel()
data, messages, err := q.QueryMultiContextWithMessages(ctx, query)
return data, messages, true, err
}
if q, ok := inst.(agentMultiResultContextRunner); ok {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(effectiveTimeoutMs)*time.Millisecond)
defer cancel()
data, err := q.QueryMultiContext(ctx, query)
return data, nil, true, err
}
}
if q, ok := inst.(agentMultiResultMessageRunner); ok {
data, messages, err := q.QueryMultiWithMessages(query)
return data, messages, true, err
}
if q, ok := inst.(agentMultiResultRunner); ok {
data, err := q.QueryMulti(query)
return data, nil, true, err
}
return nil, nil, false, nil
}
func streamWithOptionalTimeout(inst db.StreamQueryExecer, query string, timeoutMs int64, consumer db.QueryStreamConsumer) error {
effectiveTimeoutMs := timeoutMs
if effectiveTimeoutMs <= 0 && strings.EqualFold(strings.TrimSpace(agentDriverType), "clickhouse") {
effectiveTimeoutMs = int64(legacyClickHouseDefaultTimeout / time.Millisecond)
}
if effectiveTimeoutMs <= 0 {
return inst.StreamQuery(query, consumer)
}
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(effectiveTimeoutMs)*time.Millisecond)
defer cancel()
return inst.StreamQueryContext(ctx, query, consumer)
}
func streamBufferedQueryResult(fields []string, data []map[string]interface{}, consumer db.QueryStreamConsumer) error {
if err := consumer.SetColumns(fields); err != nil {
return err
}
if valueConsumer, ok := consumer.(db.QueryStreamValueConsumer); ok {
for _, row := range data {
values := make([]interface{}, len(fields))
for idx, field := range fields {
values[idx] = row[field]
}
if err := valueConsumer.ConsumeRowValues(values); err != nil {
return err
}
}
return nil
}
for _, row := range data {
if err := consumer.ConsumeRow(row); err != nil {
return err
}
}
return nil
}
func streamStatementWithOptionalTimeout(inst db.StatementExecer, query string, timeoutMs int64, consumer db.QueryStreamConsumer) error {
if streamer, ok := inst.(db.StreamQueryExecer); ok {
return streamWithOptionalTimeout(streamer, query, timeoutMs, consumer)
}
data, fields, err := queryStatementWithOptionalTimeout(inst, query, timeoutMs)
if err != nil {
return err
}
return streamBufferedQueryResult(fields, data, consumer)
}
func streamDatabaseWithOptionalTimeout(inst db.Database, query string, timeoutMs int64, consumer db.QueryStreamConsumer) error {
if streamer, ok := inst.(db.StreamQueryExecer); ok {
return streamWithOptionalTimeout(streamer, query, timeoutMs, consumer)
}
if provider, ok := inst.(db.SessionExecerProvider); ok {
openCtx := context.Background()
var cancel context.CancelFunc
effectiveTimeoutMs := timeoutMs
if effectiveTimeoutMs <= 0 && strings.EqualFold(strings.TrimSpace(agentDriverType), "clickhouse") {
effectiveTimeoutMs = int64(legacyClickHouseDefaultTimeout / time.Millisecond)
}
if effectiveTimeoutMs > 0 {
openCtx, cancel = context.WithTimeout(context.Background(), time.Duration(effectiveTimeoutMs)*time.Millisecond)
defer cancel()
}
session, err := provider.OpenSessionExecer(openCtx)
if err == nil {
defer session.Close()
return streamStatementWithOptionalTimeout(session, query, timeoutMs, consumer)
}
}
data, fields, err := queryWithOptionalTimeout(inst, query, timeoutMs)
if err != nil {
return err
}
return streamBufferedQueryResult(fields, data, consumer)
}
func execWithOptionalTimeout(inst agentExecRunner, query string, timeoutMs int64) (int64, error) {
effectiveTimeoutMs := timeoutMs
if effectiveTimeoutMs <= 0 && strings.EqualFold(strings.TrimSpace(agentDriverType), "clickhouse") {
effectiveTimeoutMs = int64(legacyClickHouseDefaultTimeout / time.Millisecond)
}
if effectiveTimeoutMs <= 0 {
return inst.Exec(query)
}
if e, ok := inst.(agentExecContextRunner); ok {
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(effectiveTimeoutMs)*time.Millisecond)
defer cancel()
return e.ExecContext(ctx, query)
}
return inst.Exec(query)
}
func execStatementWithOptionalTimeout(inst db.StatementExecer, query string, timeoutMs int64) (int64, error) {
return execWithOptionalTimeout(inst, query, timeoutMs)
}
func countAgentResponseRows(data interface{}) int64 {
rows, ok := data.([]map[string]interface{})
if !ok {
return 0
}
return int64(len(rows))
}
func maybeReleaseAgentMemory(reason string, rows int64) {
if rows < agentMemoryTrimRowsThreshold {
return
}
if !agentMemoryTrimRunning.CompareAndSwap(false, true) {
return
}
runAgentMemoryTrimAsync(func() {
defer agentMemoryTrimRunning.Store(false)
if delay := nextAgentMemoryTrimDelay(); delay > 0 {
time.Sleep(delay)
}
agentMemoryTrimFn()
agentMemoryTrimLastAt.Store(time.Now().UnixNano())
})
}
func nextAgentMemoryTrimDelay() time.Duration {
lastUnixNano := agentMemoryTrimLastAt.Load()
if lastUnixNano <= 0 {
return 0
}
elapsed := time.Since(time.Unix(0, lastUnixNano))
if elapsed >= agentMemoryTrimMinInterval {
return 0
}
return agentMemoryTrimMinInterval - elapsed
}

View File

@@ -0,0 +1,601 @@
package main
import (
"bufio"
"bytes"
"context"
"encoding/json"
"errors"
"strings"
"testing"
"time"
"GoNavi-Wails/internal/connection"
"GoNavi-Wails/internal/db"
)
type duckMapLike map[any]any
func TestWriteResponse_NormalizesMapAnyAny(t *testing.T) {
resp := agentResponse{
ID: 1,
Success: true,
Data: []map[string]interface{}{
{
"id": int64(7),
"meta": duckMapLike{"k": "v", 2: "two"},
},
},
}
var out bytes.Buffer
writer := bufio.NewWriter(&out)
if err := writeResponse(writer, resp); err != nil {
t.Fatalf("writeResponse 返回错误: %v", err)
}
var decoded struct {
Data []map[string]interface{} `json:"data"`
}
if err := json.Unmarshal(bytes.TrimSpace(out.Bytes()), &decoded); err != nil {
t.Fatalf("解码响应失败: %v", err)
}
if len(decoded.Data) != 1 {
t.Fatalf("期望 1 行数据,实际 %d", len(decoded.Data))
}
meta, ok := decoded.Data[0]["meta"].(map[string]interface{})
if !ok {
t.Fatalf("meta 字段类型异常: %T", decoded.Data[0]["meta"])
}
if meta["k"] != "v" {
t.Fatalf("字符串 key 转换异常: %v", meta["k"])
}
if meta["2"] != "two" {
t.Fatalf("数字 key 未字符串化: %v", meta["2"])
}
}
func TestNormalizeAgentResponseData_KeepByteSlice(t *testing.T) {
raw := []byte{0x61, 0x62, 0x63}
normalized := normalizeAgentResponseData(raw)
out, ok := normalized.([]byte)
if !ok {
t.Fatalf("期望 []byte实际 %T", normalized)
}
if !bytes.Equal(out, raw) {
t.Fatalf("[]byte 内容被意外改写: %v", out)
}
}
func TestHandleRequestMetadataReportsAgentRevision(t *testing.T) {
previousDriverType := agentDriverType
previousFactory := agentDatabaseFactory
t.Cleanup(func() {
agentDriverType = previousDriverType
agentDatabaseFactory = previousFactory
})
agentDriverType = "clickhouse"
agentDatabaseFactory = func() db.Database { return nil }
runtimeState := &agentRuntime{sessions: make(map[string]db.StatementExecer)}
resp := handleRequest(runtimeState, agentRequest{ID: 7, Method: agentMethodMetadata})
if !resp.Success {
t.Fatalf("metadata request failed: %s", resp.Error)
}
data, ok := resp.Data.(map[string]string)
if !ok {
t.Fatalf("metadata response data type = %T", resp.Data)
}
if data["driverType"] != "clickhouse" {
t.Fatalf("unexpected driver type: %q", data["driverType"])
}
if data["agentRevision"] != db.OptionalDriverAgentRevision("clickhouse") {
t.Fatalf("unexpected agent revision: %q", data["agentRevision"])
}
}
type fakeAgentTimeoutDB struct {
queryCalled bool
queryContextCalled bool
execCalled bool
execContextCalled bool
deadlineSet bool
queryMessages []string
multiResults []connection.ResultSetData
multiMessages []string
}
func (f *fakeAgentTimeoutDB) Connect(config connection.ConnectionConfig) error { return nil }
func (f *fakeAgentTimeoutDB) Close() error { return nil }
func (f *fakeAgentTimeoutDB) Ping() error { return nil }
func (f *fakeAgentTimeoutDB) Query(query string) ([]map[string]interface{}, []string, error) {
f.queryCalled = true
return nil, nil, errors.New("query should not be called")
}
func (f *fakeAgentTimeoutDB) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
f.queryContextCalled = true
if _, ok := ctx.Deadline(); ok {
f.deadlineSet = true
}
return []map[string]interface{}{{"ok": 1}}, []string{"ok"}, nil
}
func (f *fakeAgentTimeoutDB) QueryWithMessages(query string) ([]map[string]interface{}, []string, []string, error) {
data, fields, err := f.QueryContext(context.Background(), query)
return data, fields, append([]string(nil), f.queryMessages...), err
}
func (f *fakeAgentTimeoutDB) QueryContextWithMessages(ctx context.Context, query string) ([]map[string]interface{}, []string, []string, error) {
data, fields, err := f.QueryContext(ctx, query)
return data, fields, append([]string(nil), f.queryMessages...), err
}
func (f *fakeAgentTimeoutDB) Exec(query string) (int64, error) {
f.execCalled = true
return 0, errors.New("exec should not be called")
}
func (f *fakeAgentTimeoutDB) ExecContext(ctx context.Context, query string) (int64, error) {
f.execContextCalled = true
if _, ok := ctx.Deadline(); ok {
f.deadlineSet = true
}
return 3, nil
}
func (f *fakeAgentTimeoutDB) GetDatabases() ([]string, error) { return nil, nil }
func (f *fakeAgentTimeoutDB) GetTables(dbName string) ([]string, error) {
return nil, nil
}
func (f *fakeAgentTimeoutDB) GetCreateStatement(dbName, tableName string) (string, error) {
return "", nil
}
func (f *fakeAgentTimeoutDB) GetColumns(dbName, tableName string) ([]connection.ColumnDefinition, error) {
return nil, nil
}
func (f *fakeAgentTimeoutDB) GetAllColumns(dbName string) ([]connection.ColumnDefinitionWithTable, error) {
return nil, nil
}
func (f *fakeAgentTimeoutDB) GetIndexes(dbName, tableName string) ([]connection.IndexDefinition, error) {
return nil, nil
}
func (f *fakeAgentTimeoutDB) GetForeignKeys(dbName, tableName string) ([]connection.ForeignKeyDefinition, error) {
return nil, nil
}
func (f *fakeAgentTimeoutDB) GetTriggers(dbName, tableName string) ([]connection.TriggerDefinition, error) {
return nil, nil
}
func (f *fakeAgentTimeoutDB) QueryMultiWithMessages(query string) ([]connection.ResultSetData, []string, error) {
return append([]connection.ResultSetData(nil), f.multiResults...), append([]string(nil), f.multiMessages...), nil
}
func (f *fakeAgentTimeoutDB) QueryMultiContextWithMessages(ctx context.Context, query string) ([]connection.ResultSetData, []string, error) {
if _, ok := ctx.Deadline(); ok {
f.deadlineSet = true
}
return f.QueryMultiWithMessages(query)
}
type fakeAgentSessionDB struct {
fakeAgentTimeoutDB
session *fakeAgentStatementSession
}
func (f *fakeAgentSessionDB) OpenSessionExecer(ctx context.Context) (db.StatementExecer, error) {
f.session = &fakeAgentStatementSession{}
return f.session, nil
}
type fakeAgentStatementSession struct {
queryCalls int
execCalls int
closed bool
messages []string
}
func (f *fakeAgentStatementSession) Query(query string) ([]map[string]interface{}, []string, error) {
return f.QueryContext(context.Background(), query)
}
func (f *fakeAgentStatementSession) QueryContext(ctx context.Context, query string) ([]map[string]interface{}, []string, error) {
f.queryCalls++
return []map[string]interface{}{{"session_ok": 1}}, []string{"session_ok"}, nil
}
func (f *fakeAgentStatementSession) QueryWithMessages(query string) ([]map[string]interface{}, []string, []string, error) {
data, fields, err := f.QueryContext(context.Background(), query)
return data, fields, append([]string(nil), f.messages...), err
}
func (f *fakeAgentStatementSession) QueryContextWithMessages(ctx context.Context, query string) ([]map[string]interface{}, []string, []string, error) {
data, fields, err := f.QueryContext(ctx, query)
return data, fields, append([]string(nil), f.messages...), err
}
func (f *fakeAgentStatementSession) Exec(query string) (int64, error) {
return f.ExecContext(context.Background(), query)
}
func (f *fakeAgentStatementSession) ExecContext(ctx context.Context, query string) (int64, error) {
f.execCalls++
return 9, nil
}
func (f *fakeAgentStatementSession) Close() error {
f.closed = true
return nil
}
type fakeAgentStreamSession struct {
closed bool
streamCalls int
deadlineSet bool
}
func (f *fakeAgentStreamSession) Exec(query string) (int64, error) {
return 0, nil
}
func (f *fakeAgentStreamSession) ExecContext(ctx context.Context, query string) (int64, error) {
return 0, nil
}
func (f *fakeAgentStreamSession) Close() error {
f.closed = true
return nil
}
func (f *fakeAgentStreamSession) StreamQuery(query string, consumer db.QueryStreamConsumer) error {
return f.StreamQueryContext(context.Background(), query, consumer)
}
func (f *fakeAgentStreamSession) StreamQueryContext(ctx context.Context, query string, consumer db.QueryStreamConsumer) error {
f.streamCalls++
if _, ok := ctx.Deadline(); ok {
f.deadlineSet = true
}
if err := consumer.SetColumns([]string{"id", "name"}); err != nil {
return err
}
if valueConsumer, ok := consumer.(db.QueryStreamValueConsumer); ok {
if err := valueConsumer.ConsumeRowValues([]interface{}{1, "alice"}); err != nil {
return err
}
if err := valueConsumer.ConsumeRowValues([]interface{}{2, "bob"}); err != nil {
return err
}
return nil
}
if err := consumer.ConsumeRow(map[string]interface{}{"id": 1, "name": "alice"}); err != nil {
return err
}
return consumer.ConsumeRow(map[string]interface{}{"id": 2, "name": "bob"})
}
type fakeAgentSessionStreamDB struct {
fakeAgentTimeoutDB
session *fakeAgentStreamSession
openCalls int
}
func (f *fakeAgentSessionStreamDB) OpenSessionExecer(ctx context.Context) (db.StatementExecer, error) {
f.openCalls++
f.session = &fakeAgentStreamSession{}
return f.session, nil
}
func TestQueryWithOptionalTimeout_UsesQueryContext(t *testing.T) {
fake := &fakeAgentTimeoutDB{}
data, fields, err := queryWithOptionalTimeout(fake, "SELECT 1", int64((2 * time.Second).Milliseconds()))
if err != nil {
t.Fatalf("queryWithOptionalTimeout 返回错误: %v", err)
}
if !fake.queryContextCalled || fake.queryCalled {
t.Fatalf("query 调用路径异常QueryContext=%v Query=%v", fake.queryContextCalled, fake.queryCalled)
}
if !fake.deadlineSet {
t.Fatal("queryWithOptionalTimeout 未设置 deadline")
}
if len(data) != 1 || len(fields) != 1 || fields[0] != "ok" {
t.Fatalf("queryWithOptionalTimeout 返回数据异常: data=%v fields=%v", data, fields)
}
}
func TestExecWithOptionalTimeout_UsesExecContext(t *testing.T) {
fake := &fakeAgentTimeoutDB{}
affected, err := execWithOptionalTimeout(fake, "DELETE FROM t", int64((2 * time.Second).Milliseconds()))
if err != nil {
t.Fatalf("execWithOptionalTimeout 返回错误: %v", err)
}
if !fake.execContextCalled || fake.execCalled {
t.Fatalf("exec 调用路径异常ExecContext=%v Exec=%v", fake.execContextCalled, fake.execCalled)
}
if !fake.deadlineSet {
t.Fatal("execWithOptionalTimeout 未设置 deadline")
}
if affected != 3 {
t.Fatalf("受影响行数异常want=3 got=%d", affected)
}
}
func TestQueryWithOptionalTimeout_ClickHouseLegacyModeUsesQueryContext(t *testing.T) {
old := agentDriverType
agentDriverType = "clickhouse"
defer func() { agentDriverType = old }()
fake := &fakeAgentTimeoutDB{}
_, _, err := queryWithOptionalTimeout(fake, "SELECT 1", 0)
if err != nil {
t.Fatalf("queryWithOptionalTimeout 返回错误: %v", err)
}
if !fake.queryContextCalled || fake.queryCalled {
t.Fatalf("clickhouse legacy query 调用路径异常QueryContext=%v Query=%v", fake.queryContextCalled, fake.queryCalled)
}
}
func TestHandleRequest_QueryIncludesServerMessages(t *testing.T) {
old := agentDriverType
defer func() { agentDriverType = old }()
agentDriverType = "sqlserver"
fake := &fakeAgentTimeoutDB{
queryMessages: []string{"PRINT sql line 1", "PRINT sql line 2"},
}
runtimeState := &agentRuntime{inst: fake, sessions: make(map[string]db.StatementExecer)}
resp := handleRequest(runtimeState, agentRequest{
ID: 11,
Method: agentMethodQuery,
Query: "exec dbo.p_get_select",
TimeoutMs: int64((2 * time.Second).Milliseconds()),
})
if !resp.Success {
t.Fatalf("query request failed: %s", resp.Error)
}
if len(resp.Messages) != 2 || resp.Messages[0] != "PRINT sql line 1" {
t.Fatalf("expected query messages to be preserved, got %#v", resp.Messages)
}
}
func TestHandleRequest_QueryMultiIncludesResultSetsAndMessages(t *testing.T) {
old := agentDriverType
defer func() { agentDriverType = old }()
agentDriverType = "sqlserver"
fake := &fakeAgentTimeoutDB{
multiResults: []connection.ResultSetData{
{
StatementIndex: 1,
Rows: []map[string]interface{}{{"name": "master"}},
Columns: []string{"name"},
},
{
StatementIndex: 1,
Rows: []map[string]interface{}{},
Columns: []string{},
Messages: []string{"PRINT generated sql"},
},
},
multiMessages: []string{"batch top-level message"},
}
runtimeState := &agentRuntime{inst: fake, sessions: make(map[string]db.StatementExecer)}
resp := handleRequest(runtimeState, agentRequest{
ID: 12,
Method: agentMethodQueryMulti,
Query: "exec dbo.p_get_select",
TimeoutMs: int64((2 * time.Second).Milliseconds()),
})
if !resp.Success {
t.Fatalf("queryMulti request failed: %s", resp.Error)
}
if len(resp.Messages) != 1 || resp.Messages[0] != "batch top-level message" {
t.Fatalf("expected top-level messages to be preserved, got %#v", resp.Messages)
}
resultSets, ok := resp.Data.([]connection.ResultSetData)
if !ok {
t.Fatalf("expected []connection.ResultSetData, got %T", resp.Data)
}
if len(resultSets) != 2 {
t.Fatalf("expected 2 result sets, got %#v", resultSets)
}
if len(resultSets[1].Messages) != 1 || resultSets[1].Messages[0] != "PRINT generated sql" {
t.Fatalf("expected message-only result set to be preserved, got %#v", resultSets[1])
}
}
func TestHandleRequest_UsesPinnedSessionForSessionScopedQueryAndExec(t *testing.T) {
old := agentDriverType
defer func() { agentDriverType = old }()
agentDriverType = "sqlserver"
fake := &fakeAgentSessionDB{}
runtimeState := &agentRuntime{
inst: fake,
sessions: make(map[string]db.StatementExecer),
}
openResp := handleRequest(runtimeState, agentRequest{ID: 1, Method: agentMethodOpenSession})
if !openResp.Success {
t.Fatalf("openSession failed: %s", openResp.Error)
}
sessionID, ok := openResp.Data.(string)
if !ok || strings.TrimSpace(sessionID) == "" {
t.Fatalf("unexpected session id payload: %#v", openResp.Data)
}
if fake.session == nil {
t.Fatal("expected OpenSessionExecer to create a pinned session")
}
queryResp := handleRequest(runtimeState, agentRequest{
ID: 2,
Method: agentMethodQuery,
SessionID: sessionID,
Query: "SELECT 1",
})
if !queryResp.Success {
t.Fatalf("session query failed: %s", queryResp.Error)
}
if len(queryResp.Messages) != 0 {
t.Fatalf("expected empty default session messages, got %#v", queryResp.Messages)
}
if fake.queryCalled || fake.queryContextCalled {
t.Fatalf("expected session query to bypass database-level query path, got Query=%v QueryContext=%v", fake.queryCalled, fake.queryContextCalled)
}
if fake.session.queryCalls != 1 {
t.Fatalf("expected pinned session queryCalls=1, got %d", fake.session.queryCalls)
}
execResp := handleRequest(runtimeState, agentRequest{
ID: 3,
Method: agentMethodExec,
SessionID: sessionID,
Query: "UPDATE t SET v = 1",
})
if !execResp.Success {
t.Fatalf("session exec failed: %s", execResp.Error)
}
if fake.execCalled || fake.execContextCalled {
t.Fatalf("expected session exec to bypass database-level exec path, got Exec=%v ExecContext=%v", fake.execCalled, fake.execContextCalled)
}
if fake.session.execCalls != 1 {
t.Fatalf("expected pinned session execCalls=1, got %d", fake.session.execCalls)
}
closeResp := handleRequest(runtimeState, agentRequest{
ID: 4,
Method: agentMethodCloseSession,
SessionID: sessionID,
})
if !closeResp.Success {
t.Fatalf("closeSession failed: %s", closeResp.Error)
}
if !fake.session.closed {
t.Fatal("expected pinned session to close")
}
}
func TestHandleStreamRequest_UsesSessionStreamerAndWritesChunks(t *testing.T) {
old := agentDriverType
originalAsync := runAgentMemoryTrimAsync
originalTrim := agentMemoryTrimFn
originalLastAt := agentMemoryTrimLastAt.Load()
defer func() { agentDriverType = old }()
defer func() {
runAgentMemoryTrimAsync = originalAsync
agentMemoryTrimFn = originalTrim
agentMemoryTrimRunning.Store(false)
agentMemoryTrimLastAt.Store(originalLastAt)
}()
agentDriverType = "oceanbase"
agentMemoryTrimRunning.Store(false)
agentMemoryTrimLastAt.Store(0)
fake := &fakeAgentSessionStreamDB{}
runtimeState := &agentRuntime{
inst: fake,
sessions: make(map[string]db.StatementExecer),
}
trimmed := 0
runAgentMemoryTrimAsync = func(fn func()) {
fn()
}
agentMemoryTrimFn = func() {
trimmed++
}
var out bytes.Buffer
writer := bufio.NewWriter(&out)
if err := handleStreamRequest(runtimeState, agentRequest{
ID: 9,
Method: agentMethodStreamQuery,
Query: "SELECT * FROM person_info",
TimeoutMs: int64((2 * time.Second).Milliseconds()),
}, writer); err != nil {
t.Fatalf("handleStreamRequest 返回错误: %v", err)
}
if fake.openCalls != 1 {
t.Fatalf("expected OpenSessionExecer called once, got %d", fake.openCalls)
}
if fake.session == nil || fake.session.streamCalls != 1 {
t.Fatalf("expected session streamer used once, session=%#v", fake.session)
}
if !fake.session.deadlineSet {
t.Fatal("expected stream query context deadline to be set")
}
if !fake.session.closed {
t.Fatal("expected session to close after streaming")
}
if fake.queryCalled || fake.queryContextCalled {
t.Fatalf("unexpected fallback query path, Query=%v QueryContext=%v", fake.queryCalled, fake.queryContextCalled)
}
lines := strings.Split(strings.TrimSpace(out.String()), "\n")
if len(lines) != 3 {
t.Fatalf("expected 3 stream responses, got %d: %q", len(lines), out.String())
}
var columnsResp struct {
Success bool `json:"success"`
ChunkType string `json:"chunkType"`
Fields []string `json:"fields"`
}
if err := json.Unmarshal([]byte(lines[0]), &columnsResp); err != nil {
t.Fatalf("decode columns response failed: %v", err)
}
if !columnsResp.Success || columnsResp.ChunkType != agentChunkColumns || len(columnsResp.Fields) != 2 {
t.Fatalf("unexpected columns response: %#v", columnsResp)
}
var rowsResp struct {
Success bool `json:"success"`
ChunkType string `json:"chunkType"`
Data [][]interface{} `json:"data"`
}
if err := json.Unmarshal([]byte(lines[1]), &rowsResp); err != nil {
t.Fatalf("decode rows response failed: %v", err)
}
if !rowsResp.Success || rowsResp.ChunkType != agentChunkRows || len(rowsResp.Data) != 2 {
t.Fatalf("unexpected rows response: %#v", rowsResp)
}
if got := rowsResp.Data[1][1]; got != "bob" {
t.Fatalf("unexpected streamed row payload: %v", rowsResp.Data)
}
var doneResp struct {
Success bool `json:"success"`
ChunkType string `json:"chunkType"`
}
if err := json.Unmarshal([]byte(lines[2]), &doneResp); err != nil {
t.Fatalf("decode done response failed: %v", err)
}
if !doneResp.Success || doneResp.ChunkType != agentChunkDone {
t.Fatalf("unexpected done response: %#v", doneResp)
}
if trimmed != 0 {
t.Fatalf("小流式任务不应触发内存回收got=%d", trimmed)
}
}
func TestMaybeReleaseAgentMemory_TriggersTrimForLargeJobs(t *testing.T) {
originalAsync := runAgentMemoryTrimAsync
originalTrim := agentMemoryTrimFn
originalLastAt := agentMemoryTrimLastAt.Load()
t.Cleanup(func() {
runAgentMemoryTrimAsync = originalAsync
agentMemoryTrimFn = originalTrim
agentMemoryTrimRunning.Store(false)
agentMemoryTrimLastAt.Store(originalLastAt)
})
agentMemoryTrimRunning.Store(false)
agentMemoryTrimLastAt.Store(0)
triggered := 0
runAgentMemoryTrimAsync = func(fn func()) {
fn()
}
agentMemoryTrimFn = func() {
triggered++
}
maybeReleaseAgentMemory("test-large-query", agentMemoryTrimRowsThreshold)
if triggered != 1 {
t.Fatalf("大查询完成后应触发一次内存回收got=%d", triggered)
}
}

View File

@@ -0,0 +1,12 @@
//go:build gonavi_clickhouse_driver
package main
import "GoNavi-Wails/internal/db"
func init() {
agentDriverType = "clickhouse"
agentDatabaseFactory = func() db.Database {
return &db.ClickHouseDB{}
}
}

View File

@@ -0,0 +1,12 @@
//go:build gonavi_elasticsearch_driver
package main
import "GoNavi-Wails/internal/db"
func init() {
agentDriverType = "elasticsearch"
agentDatabaseFactory = func() db.Database {
return &db.ElasticsearchDB{}
}
}

View File

@@ -0,0 +1,12 @@
//go:build gonavi_gaussdb_driver
package main
import "GoNavi-Wails/internal/db"
func init() {
agentDriverType = "gaussdb"
agentDatabaseFactory = func() db.Database {
return &db.GaussDB{}
}
}

View File

@@ -0,0 +1,12 @@
//go:build gonavi_iotdb_driver
package main
import "GoNavi-Wails/internal/db"
func init() {
agentDriverType = "iotdb"
agentDatabaseFactory = func() db.Database {
return &db.IoTDBDB{}
}
}

View File

@@ -0,0 +1,12 @@
//go:build gonavi_iris_driver
package main
import "GoNavi-Wails/internal/db"
func init() {
agentDriverType = "iris"
agentDatabaseFactory = func() db.Database {
return &db.IrisDB{}
}
}

View File

@@ -0,0 +1,12 @@
//go:build gonavi_mongodb_driver_v1
package main
import "GoNavi-Wails/internal/db"
func init() {
agentDriverType = "mongodb"
agentDatabaseFactory = func() db.Database {
return &db.MongoDBV1{}
}
}

View File

@@ -0,0 +1,12 @@
//go:build gonavi_oceanbase_driver
package main
import "GoNavi-Wails/internal/db"
func init() {
agentDriverType = "oceanbase"
agentDatabaseFactory = func() db.Database {
return &db.OceanBaseDB{}
}
}

View File

@@ -0,0 +1,12 @@
//go:build gonavi_opengauss_driver
package main
import "GoNavi-Wails/internal/db"
func init() {
agentDriverType = "opengauss"
agentDatabaseFactory = func() db.Database {
return &db.OpenGaussDB{}
}
}

View File

@@ -0,0 +1,12 @@
//go:build gonavi_starrocks_driver
package main
import "GoNavi-Wails/internal/db"
func init() {
agentDriverType = "starrocks"
agentDatabaseFactory = func() db.Database {
return &db.StarRocksDB{}
}
}

View File

@@ -0,0 +1,12 @@
//go:build gonavi_trino_driver
package main
import "GoNavi-Wails/internal/db"
func init() {
agentDriverType = "trino"
agentDatabaseFactory = func() db.Database {
return &db.TrinoDB{}
}
}

View File

@@ -0,0 +1,7 @@
apiVersion: v2
name: gonavi-mcp-server
description: Helm chart for deploying GoNavi MCP Server
type: application
version: 0.1.0
appVersion: "latest"
kubeVersion: ">=1.24.0-0"

View File

@@ -0,0 +1,94 @@
# GoNavi MCP Server Helm Chart
这个 Chart 用来部署 `gonavi-mcp-server`,覆盖当前仓库 K8s 示例里的几类常见场景:
- 基于现有 Secret 直接部署
- 通过 Helm values 内联创建测试用 Secret
- 启用 Ingress
- 追加 `drivers/` PVC
- NAS / k3s 单机节点直接挂宿主机数据目录
Chart 目录:
- `Chart.yaml`
- `values.yaml`
- `values-examples/*.yaml`
- `templates/*.yaml`
## 1. 默认安装
默认值假设你已经有一个现成 Secret
- 名称:`gonavi-mcp-server-data`
- 至少包含:
- `connections.json`
- `daily_secrets.json`
- `GONAVI_MCP_HTTP_TOKEN`
安装命令:
```bash
helm upgrade --install gonavi-mcp-server deploy/helm/gonavi-mcp-server -n gonavi --create-namespace
```
## 2. 直接创建测试用 Secret
如果只是本地验证模板或快速试跑,可以使用示例 values
```bash
helm upgrade --install gonavi-mcp-server deploy/helm/gonavi-mcp-server \
-n gonavi --create-namespace \
-f deploy/helm/gonavi-mcp-server/values-examples/inline-secret.yaml
```
这个示例只适合最小联调,不适合真实生产连接数据。
## 3. 常用示例
启用 Ingress
```bash
helm upgrade --install gonavi-mcp-server deploy/helm/gonavi-mcp-server \
-n gonavi --create-namespace \
-f deploy/helm/gonavi-mcp-server/values-examples/ingress.yaml
```
挂载 drivers PVC
```bash
helm upgrade --install gonavi-mcp-server deploy/helm/gonavi-mcp-server \
-n gonavi --create-namespace \
-f deploy/helm/gonavi-mcp-server/values-examples/drivers-pvc.yaml
```
同时启用 Ingress 与 drivers PVC
```bash
helm upgrade --install gonavi-mcp-server deploy/helm/gonavi-mcp-server \
-n gonavi --create-namespace \
-f deploy/helm/gonavi-mcp-server/values-examples/ingress-with-drivers-pvc.yaml
```
NAS / k3s hostPath
```bash
helm upgrade --install gonavi-mcp-server deploy/helm/gonavi-mcp-server \
-n gonavi --create-namespace \
-f deploy/helm/gonavi-mcp-server/values-examples/nas-hostpath.yaml
```
## 4. 关键参数
- `image.repository` / `image.tag`
- `secret.create`
- `secret.name`
- `data.mode=secret|hostPath|pvc`
- `drivers.mode=none|pvc|hostPath`
- `ingress.enabled`
## 5. 约束
- `data.mode=secret`Secret 必须能提供 `connections.json``daily_secrets.json`
- `data.mode=hostPath` 时,宿主机目录需要包含 GoNavi 活动数据目录内容
- `drivers.mode!=none` 时,会在 `/data/drivers` 追加独立挂载
- Chart 默认只部署 MCP Server不部署桌面 GUI

View File

@@ -0,0 +1,49 @@
{{- define "gonavi-mcp-server.name" -}}
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- define "gonavi-mcp-server.fullname" -}}
{{- if .Values.fullnameOverride -}}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- $name := include "gonavi-mcp-server.name" . -}}
{{- if contains $name .Release.Name -}}
{{- .Release.Name | trunc 63 | trimSuffix "-" -}}
{{- else -}}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}}
{{- end -}}
{{- end -}}
{{- end -}}
{{- define "gonavi-mcp-server.chart" -}}
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" -}}
{{- end -}}
{{- define "gonavi-mcp-server.labels" -}}
helm.sh/chart: {{ include "gonavi-mcp-server.chart" . }}
app.kubernetes.io/name: {{ include "gonavi-mcp-server.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
{{- end -}}
{{- define "gonavi-mcp-server.selectorLabels" -}}
app.kubernetes.io/name: {{ include "gonavi-mcp-server.name" . }}
app.kubernetes.io/instance: {{ .Release.Name }}
{{- end -}}
{{- define "gonavi-mcp-server.namespace" -}}
{{- default .Release.Namespace .Values.namespaceOverride -}}
{{- end -}}
{{- define "gonavi-mcp-server.secretName" -}}
{{- if .Values.secret.name -}}
{{- .Values.secret.name -}}
{{- else -}}
{{- include "gonavi-mcp-server.fullname" . -}}
{{- end -}}
{{- end -}}
{{- define "gonavi-mcp-server.serviceName" -}}
{{- include "gonavi-mcp-server.fullname" . -}}
{{- end -}}

View File

@@ -0,0 +1,130 @@
{{- $dataMode := .Values.data.mode -}}
{{- $driversMode := .Values.drivers.mode -}}
{{- if not (or (eq $dataMode "secret") (eq $dataMode "hostPath") (eq $dataMode "pvc")) -}}
{{- fail "data.mode must be one of: secret, hostPath, pvc" -}}
{{- end -}}
{{- if not (or (eq $driversMode "none") (eq $driversMode "pvc") (eq $driversMode "hostPath")) -}}
{{- fail "drivers.mode must be one of: none, pvc, hostPath" -}}
{{- end -}}
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "gonavi-mcp-server.fullname" . }}
namespace: {{ include "gonavi-mcp-server.namespace" . }}
labels:
{{- include "gonavi-mcp-server.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "gonavi-mcp-server.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "gonavi-mcp-server.selectorLabels" . | nindent 8 }}
{{- with .Values.podAnnotations }}
annotations:
{{- toYaml . | nindent 8 }}
{{- end }}
spec:
securityContext:
{{- toYaml .Values.podSecurityContext | nindent 8 }}
containers:
- name: gonavi-mcp-server
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
args:
- http
{{- range .Values.mcp.extraArgs }}
- {{ . | quote }}
{{- end }}
ports:
- name: http
containerPort: {{ .Values.service.port }}
protocol: TCP
env:
- name: GONAVI_DATA_ROOT
value: {{ .Values.mcp.dataRoot | quote }}
- name: GONAVI_LOG_DIR
value: {{ .Values.mcp.logDir | quote }}
- name: GONAVI_MCP_HTTP_ADDR
value: {{ .Values.mcp.httpAddr | quote }}
- name: GONAVI_MCP_HTTP_PATH
value: {{ .Values.mcp.httpPath | quote }}
- name: GONAVI_MCP_SCHEMA_ONLY
value: {{ ternary "true" "false" .Values.mcp.schemaOnly | quote }}
- name: GONAVI_MCP_HTTP_TOKEN
valueFrom:
secretKeyRef:
name: {{ include "gonavi-mcp-server.secretName" . }}
key: {{ .Values.secret.tokenKey }}
{{- with .Values.mcp.extraEnv }}
{{- toYaml . | nindent 12 }}
{{- end }}
volumeMounts:
- name: gonavi-data
mountPath: {{ .Values.data.mountPath | quote }}
readOnly: true
{{- if ne $driversMode "none" }}
- name: gonavi-drivers
mountPath: {{ .Values.drivers.mountPath | quote }}
readOnly: true
{{- end }}
readinessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 3
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 10
periodSeconds: 20
resources:
{{- toYaml .Values.resources | nindent 12 }}
securityContext:
{{- toYaml .Values.containerSecurityContext | nindent 12 }}
volumes:
- name: gonavi-data
{{- if eq $dataMode "secret" }}
projected:
sources:
- secret:
name: {{ include "gonavi-mcp-server.secretName" . }}
items:
- key: {{ .Values.secret.connectionsJsonKey }}
path: connections.json
- key: {{ .Values.secret.dailySecretsJsonKey }}
path: daily_secrets.json
{{- else if eq $dataMode "hostPath" }}
hostPath:
path: {{ required "data.hostPath.path is required when data.mode=hostPath" .Values.data.hostPath.path | quote }}
type: {{ default "Directory" .Values.data.hostPath.type | quote }}
{{- else if eq $dataMode "pvc" }}
persistentVolumeClaim:
claimName: {{ required "data.pvc.claimName is required when data.mode=pvc" .Values.data.pvc.claimName | quote }}
{{- end }}
{{- if eq $driversMode "pvc" }}
- name: gonavi-drivers
persistentVolumeClaim:
claimName: {{ required "drivers.pvc.claimName is required when drivers.mode=pvc" .Values.drivers.pvc.claimName | quote }}
{{- else if eq $driversMode "hostPath" }}
- name: gonavi-drivers
hostPath:
path: {{ required "drivers.hostPath.path is required when drivers.mode=hostPath" .Values.drivers.hostPath.path | quote }}
type: {{ default "Directory" .Values.drivers.hostPath.type | quote }}
{{- end }}
{{- with .Values.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}

View File

@@ -0,0 +1,34 @@
{{- if .Values.ingress.enabled }}
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: {{ include "gonavi-mcp-server.fullname" . }}
namespace: {{ include "gonavi-mcp-server.namespace" . }}
labels:
{{- include "gonavi-mcp-server.labels" . | nindent 4 }}
{{- with .Values.ingress.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
ingressClassName: {{ .Values.ingress.className | quote }}
{{- with .Values.ingress.tls }}
tls:
{{- toYaml . | nindent 4 }}
{{- end }}
rules:
{{- range .Values.ingress.hosts }}
- host: {{ .host | quote }}
http:
paths:
{{- range .paths }}
- path: {{ .path | quote }}
pathType: {{ .pathType | quote }}
backend:
service:
name: {{ include "gonavi-mcp-server.serviceName" $ }}
port:
number: {{ $.Values.service.port }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,27 @@
{{- if .Values.secret.create }}
apiVersion: v1
kind: Secret
metadata:
name: {{ include "gonavi-mcp-server.secretName" . }}
namespace: {{ include "gonavi-mcp-server.namespace" . }}
labels:
{{- include "gonavi-mcp-server.labels" . | nindent 4 }}
type: Opaque
stringData:
{{ .Values.secret.tokenKey }}: {{ required "secret.stringData.token is required when secret.create=true" .Values.secret.stringData.token | quote }}
{{- if eq .Values.data.mode "secret" }}
{{ .Values.secret.connectionsJsonKey }}: |
{{ required "secret.stringData.connectionsJson is required when data.mode=secret and secret.create=true" .Values.secret.stringData.connectionsJson | nindent 4 }}
{{ .Values.secret.dailySecretsJsonKey }}: |
{{ required "secret.stringData.dailySecretsJson is required when data.mode=secret and secret.create=true" .Values.secret.stringData.dailySecretsJson | nindent 4 }}
{{- else }}
{{- if .Values.secret.stringData.connectionsJson }}
{{ .Values.secret.connectionsJsonKey }}: |
{{ .Values.secret.stringData.connectionsJson | nindent 4 }}
{{- end }}
{{- if .Values.secret.stringData.dailySecretsJson }}
{{ .Values.secret.dailySecretsJsonKey }}: |
{{ .Values.secret.stringData.dailySecretsJson | nindent 4 }}
{{- end }}
{{- end }}
{{- end }}

View File

@@ -0,0 +1,20 @@
apiVersion: v1
kind: Service
metadata:
name: {{ include "gonavi-mcp-server.serviceName" . }}
namespace: {{ include "gonavi-mcp-server.namespace" . }}
labels:
{{- include "gonavi-mcp-server.labels" . | nindent 4 }}
{{- with .Values.service.annotations }}
annotations:
{{- toYaml . | nindent 4 }}
{{- end }}
spec:
type: {{ .Values.service.type }}
selector:
{{- include "gonavi-mcp-server.selectorLabels" . | nindent 4 }}
ports:
- name: http
port: {{ .Values.service.port }}
targetPort: http
protocol: TCP

View File

@@ -0,0 +1,4 @@
drivers:
mode: pvc
pvc:
claimName: gonavi-drivers

View File

@@ -0,0 +1,19 @@
ingress:
enabled: true
className: nginx
hosts:
- host: gonavi-mcp.example.com
paths:
- path: /mcp
pathType: Prefix
- path: /healthz
pathType: Prefix
tls:
- secretName: gonavi-mcp-server-tls
hosts:
- gonavi-mcp.example.com
drivers:
mode: pvc
pvc:
claimName: gonavi-drivers

View File

@@ -0,0 +1,14 @@
ingress:
enabled: true
className: nginx
hosts:
- host: gonavi-mcp.example.com
paths:
- path: /mcp
pathType: Prefix
- path: /healthz
pathType: Prefix
tls:
- secretName: gonavi-mcp-server-tls
hosts:
- gonavi-mcp.example.com

View File

@@ -0,0 +1,13 @@
secret:
create: true
stringData:
token: replace-with-a-random-token
connectionsJson: |
{
"connections": []
}
dailySecretsJson: |
{
"schemaVersion": 1,
"connections": {}
}

View File

@@ -0,0 +1,10 @@
data:
mode: hostPath
hostPath:
path: /volume1/docker/gonavi/data
type: Directory
secret:
create: true
stringData:
token: replace-with-a-random-token

View File

@@ -0,0 +1,99 @@
nameOverride: ""
fullnameOverride: ""
namespaceOverride: ""
replicaCount: 1
image:
repository: ghcr.io/syngnat/gonavi-mcp-server
tag: latest
pullPolicy: IfNotPresent
service:
type: ClusterIP
port: 8765
annotations: {}
mcp:
dataRoot: /data
logDir: /var/lib/gonavi/logs
httpAddr: 0.0.0.0:8765
httpPath: /mcp
schemaOnly: true
extraArgs: []
extraEnv: []
secret:
create: false
name: gonavi-mcp-server-data
tokenKey: GONAVI_MCP_HTTP_TOKEN
connectionsJsonKey: connections.json
dailySecretsJsonKey: daily_secrets.json
stringData:
token: ""
connectionsJson: ""
dailySecretsJson: ""
data:
mode: secret
mountPath: /data
hostPath:
path: /volume1/docker/gonavi/data
type: Directory
pvc:
claimName: ""
drivers:
mode: none
mountPath: /data/drivers
pvc:
claimName: gonavi-drivers
hostPath:
path: /volume1/docker/gonavi/drivers
type: Directory
ingress:
enabled: false
className: nginx
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
hosts:
- host: gonavi-mcp.example.com
paths:
- path: /mcp
pathType: Prefix
- path: /healthz
pathType: Prefix
tls:
- secretName: gonavi-mcp-server-tls
hosts:
- gonavi-mcp.example.com
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
podAnnotations: {}
podSecurityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
seccompProfile:
type: RuntimeDefault
containerSecurityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
nodeSelector: {}
tolerations: []
affinity: {}

View File

@@ -0,0 +1,151 @@
# GoNavi MCP Server Kubernetes 示例
这个目录提供 `gonavi-mcp-server` 的最小 K8s 部署清单,适合:
- Linux 服务器上的单集群部署
- NAS 自带 K8s / k3s / k8s-lite 环境
- 需要把 GoNavi MCP 通过 Ingress / Gateway 暴露给远端 Agent 的场景
如果你希望通过 values 参数统一控制镜像、Secret、Ingress、PVC/hostPath而不是维护多份 overlay请直接使用 [deploy/helm/gonavi-mcp-server](../../helm/gonavi-mcp-server)。
目录结构:
- `kustomization.yaml`:基础部署入口
- `base/kustomization.yaml`:基础资源集合
- `overlays/nas-hostpath`:直接挂 NAS / 单机节点上的 GoNavi 数据目录
- `base/deployment.yaml` / `base/service.yaml`:基础资源
- `overlays/drivers-pvc`:为 `/data/drivers` 增加 PVC 挂载
- `overlays/ingress`:增加 Ingress 暴露 `/mcp`
- `overlays/ingress-with-drivers-pvc`:同时启用 Ingress 和 drivers PVC
## 前提
容器仍然依赖 GoNavi 的活动数据目录。至少要准备:
- `connections.json`
- `daily_secrets.json`
如果目标连接依赖可选 driver agent还需要额外挂载 `/data/drivers`
`base/deployment.yaml` 默认镜像指向:
```text
ghcr.io/syngnat/gonavi-mcp-server:latest
```
如果你要跟随 `dev` 分支,可以改成:
```text
ghcr.io/syngnat/gonavi-mcp-server:dev-latest
```
如果你要使用自建镜像,再按下面方式覆盖 `image`
- 在当前节点可访问的 Docker / Podman / containerd 环境中构建并导入这个镜像
- 把镜像推送到你的私有仓库,并同步修改 `base/deployment.yaml` 里的 `image`
例如:
```bash
docker build -f Dockerfile.mcp-server -t gonavi-mcp-server:local .
# or
podman build -f Dockerfile.mcp-server -t localhost/gonavi-mcp-server:local .
```
## 1. 创建 Secret
推荐直接从现有 GoNavi 数据文件生成:
```bash
kubectl create secret generic gonavi-mcp-server-data \
--from-file=connections.json=/absolute/path/to/gonavi-data/connections.json \
--from-file=daily_secrets.json=/absolute/path/to/gonavi-data/daily_secrets.json \
--from-literal=GONAVI_MCP_HTTP_TOKEN=replace-with-a-random-token
```
如需单独 namespace请先执行
```bash
kubectl create namespace gonavi
kubectl -n gonavi create secret generic gonavi-mcp-server-data \
--from-file=connections.json=/absolute/path/to/gonavi-data/connections.json \
--from-file=daily_secrets.json=/absolute/path/to/gonavi-data/daily_secrets.json \
--from-literal=GONAVI_MCP_HTTP_TOKEN=replace-with-a-random-token
```
## 2. 应用清单
```bash
kubectl apply -k deploy/k8s/gonavi-mcp-server
```
如果使用独立 namespace请先把 YAML 里的 `namespace` 改成你的目标值,或自行用 `kustomize` / Helm 做二次封装。
清单内已经带了基础安全上下文和默认资源配额;如果你的连接数量、对象规模或并发访问更高,建议按实际负载调整 `resources`
### 常用 Overlay
仅基础部署:
```bash
kubectl apply -k deploy/k8s/gonavi-mcp-server
```
需要可选 driver agent
```bash
kubectl apply -k deploy/k8s/gonavi-mcp-server/overlays/drivers-pvc
```
NAS / k3s 单机节点直接挂宿主机目录:
```bash
kubectl apply -k deploy/k8s/gonavi-mcp-server/overlays/nas-hostpath
```
需要对外暴露 `/mcp`
```bash
kubectl apply -k deploy/k8s/gonavi-mcp-server/overlays/ingress
```
同时需要 Ingress 与 drivers PVC
```bash
kubectl apply -k deploy/k8s/gonavi-mcp-server/overlays/ingress-with-drivers-pvc
```
应用 overlay 前,记得先改里面的占位值,例如:
- `overlays/ingress/ingress.yaml` 里的域名、TLS Secret、`ingressClassName`
- `overlays/drivers-pvc/patch-deployment.yaml` 里的 PVC 名称 `gonavi-drivers`
- `overlays/nas-hostpath/patch-deployment.yaml` 里的宿主机目录 `/volume1/docker/gonavi/data`
`nas-hostpath` overlay 会直接把整个 GoNavi 数据目录挂到 `/data`,因此 `connections.json``daily_secrets.json``drivers/` 都从宿主机目录读取;此时仍建议保留 `GONAVI_MCP_HTTP_TOKEN` 的 Secret 注入,不要把 token 直接硬编码进 Deployment。
## 3. 校验
```bash
kubectl get pods -n gonavi
kubectl get svc -n gonavi
kubectl port-forward -n gonavi svc/gonavi-mcp-server 8765:8765
curl -H "Authorization: Bearer replace-with-a-random-token" http://127.0.0.1:8765/healthz
```
`/healthz` 返回 `ok` 说明 Pod 已对外提供 HTTP 服务。
## 4. 暴露给远端 Agent
- 集群内使用:直接访问 `http://gonavi-mcp-server.gonavi.svc.cluster.local:8765/mcp`
- 集群外使用:通过 Ingress / Gateway / 反向代理暴露 `/mcp`
- 远端 Agent 请求头:`Authorization: Bearer <你的 token>`
## 5. drivers 挂载
当前示例默认只挂载 `connections.json``daily_secrets.json`。如果保存连接里包含 Dameng、ClickHouse、DuckDB 等依赖 driver agent 的数据源,请在 Deployment 中额外补一个卷,把宿主机或 PVC 中的 `drivers/` 目录挂载到:
```text
/data/drivers
```
否则对应连接会因为缺少 driver agent 无法使用。

View File

@@ -0,0 +1,89 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: gonavi-mcp-server
namespace: gonavi
labels:
app.kubernetes.io/name: gonavi-mcp-server
spec:
replicas: 1
selector:
matchLabels:
app.kubernetes.io/name: gonavi-mcp-server
template:
metadata:
labels:
app.kubernetes.io/name: gonavi-mcp-server
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: gonavi-mcp-server
# 如需跟随 dev 分支,可改成 ghcr.io/syngnat/gonavi-mcp-server:dev-latest
image: ghcr.io/syngnat/gonavi-mcp-server:latest
imagePullPolicy: IfNotPresent
args:
- http
ports:
- name: http
containerPort: 8765
protocol: TCP
env:
- name: GONAVI_DATA_ROOT
value: /data
- name: GONAVI_LOG_DIR
value: /var/lib/gonavi/logs
- name: GONAVI_MCP_HTTP_ADDR
value: 0.0.0.0:8765
- name: GONAVI_MCP_HTTP_PATH
value: /mcp
- name: GONAVI_MCP_SCHEMA_ONLY
value: "true"
- name: GONAVI_MCP_HTTP_TOKEN
valueFrom:
secretKeyRef:
name: gonavi-mcp-server-data
key: GONAVI_MCP_HTTP_TOKEN
volumeMounts:
- name: gonavi-data
mountPath: /data
readOnly: true
readinessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 3
periodSeconds: 10
livenessProbe:
httpGet:
path: /healthz
port: http
initialDelaySeconds: 10
periodSeconds: 20
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
volumes:
- name: gonavi-data
projected:
sources:
- secret:
name: gonavi-mcp-server-data
items:
- key: connections.json
path: connections.json
- key: daily_secrets.json
path: daily_secrets.json

View File

@@ -0,0 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
- service.yaml

View File

@@ -0,0 +1,15 @@
apiVersion: v1
kind: Service
metadata:
name: gonavi-mcp-server
namespace: gonavi
labels:
app.kubernetes.io/name: gonavi-mcp-server
spec:
selector:
app.kubernetes.io/name: gonavi-mcp-server
ports:
- name: http
port: 8765
targetPort: http
protocol: TCP

View File

@@ -0,0 +1,5 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- base

View File

@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: patch-deployment.yaml

View File

@@ -0,0 +1,18 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: gonavi-mcp-server
namespace: gonavi
spec:
template:
spec:
containers:
- name: gonavi-mcp-server
volumeMounts:
- name: gonavi-drivers
mountPath: /data/drivers
readOnly: true
volumes:
- name: gonavi-drivers
persistentVolumeClaim:
claimName: gonavi-drivers

View File

@@ -0,0 +1,33 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gonavi-mcp-server
namespace: gonavi
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
spec:
ingressClassName: nginx
tls:
- hosts:
- gonavi-mcp.example.com
secretName: gonavi-mcp-server-tls
rules:
- host: gonavi-mcp.example.com
http:
paths:
- path: /mcp
pathType: Prefix
backend:
service:
name: gonavi-mcp-server
port:
number: 8765
- path: /healthz
pathType: Prefix
backend:
service:
name: gonavi-mcp-server
port:
number: 8765

View File

@@ -0,0 +1,9 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- ingress.yaml
patches:
- path: patch-deployment.yaml

View File

@@ -0,0 +1,18 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: gonavi-mcp-server
namespace: gonavi
spec:
template:
spec:
containers:
- name: gonavi-mcp-server
volumeMounts:
- name: gonavi-drivers
mountPath: /data/drivers
readOnly: true
volumes:
- name: gonavi-drivers
persistentVolumeClaim:
claimName: gonavi-drivers

View File

@@ -0,0 +1,33 @@
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: gonavi-mcp-server
namespace: gonavi
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTP"
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
nginx.ingress.kubernetes.io/proxy-send-timeout: "3600"
spec:
ingressClassName: nginx
tls:
- hosts:
- gonavi-mcp.example.com
secretName: gonavi-mcp-server-tls
rules:
- host: gonavi-mcp.example.com
http:
paths:
- path: /mcp
pathType: Prefix
backend:
service:
name: gonavi-mcp-server
port:
number: 8765
- path: /healthz
pathType: Prefix
backend:
service:
name: gonavi-mcp-server
port:
number: 8765

View File

@@ -0,0 +1,6 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
- ingress.yaml

View File

@@ -0,0 +1,8 @@
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patches:
- path: patch-deployment.yaml

View File

@@ -0,0 +1,14 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: gonavi-mcp-server
namespace: gonavi
spec:
template:
spec:
volumes:
- $patch: replace
name: gonavi-data
hostPath:
path: /volume1/docker/gonavi/data
type: Directory

View File

@@ -0,0 +1,101 @@
# GoNavi MCP Server Podman 示例
这个目录提供 `gonavi-mcp-server` 的 Podman 原生部署入口,覆盖两类常见场景:
- 直接用 `podman run` 在 Linux 服务器 / NAS 上启动
- 用 rootless Quadlet + systemd 做常驻服务
目录内容:
- `gonavi-mcp-server.env.example`:给 `podman run` / Quadlet 共用的容器环境变量示例
- `gonavi-mcp-server.container`Quadlet 示例
前提仍然一样:宿主机 GoNavi 活动数据目录内至少应包含:
- `connections.json`
- `daily_secrets.json`
如果目标连接依赖可选 driver agent还要保证同一数据目录下已有 `drivers/`
## 1. 直接运行已发布镜像
```bash
cp deploy/podman/gonavi-mcp-server/gonavi-mcp-server.env.example ./gonavi-mcp-server.env
```
`GONAVI_MCP_HTTP_TOKEN` 改成随机值后运行:
```bash
podman run -d --name gonavi-mcp-server --replace \
-p 8765:8765 \
--env-file ./gonavi-mcp-server.env \
-v /absolute/path/to/gonavi-data:/data:Z \
ghcr.io/syngnat/gonavi-mcp-server:latest http
```
查看状态:
```bash
podman ps
podman logs -f gonavi-mcp-server
```
如果你的宿主机没有启用 SELinux可把挂载参数末尾的 `:Z` 去掉;如果启用了 SELinux建议保留它。
## 2. 本地源码构建镜像
仓库根目录已经提供 `.containerignore`Podman 会优先读取它来裁剪构建上下文。
```bash
podman build -f Dockerfile.mcp-server -t localhost/gonavi-mcp-server:local .
podman run -d --name gonavi-mcp-server --replace \
-p 8765:8765 \
--env-file ./gonavi-mcp-server.env \
-v /absolute/path/to/gonavi-data:/data:Z \
localhost/gonavi-mcp-server:local http
```
## 3. Rootless Quadlet 常驻服务
Podman Quadlet 适合 Linux 服务器 / NAS 上做 rootless 常驻服务。
把文件放到用户级目录:
```bash
mkdir -p ~/.config/containers/systemd
cp deploy/podman/gonavi-mcp-server/gonavi-mcp-server.container ~/.config/containers/systemd/
cp deploy/podman/gonavi-mcp-server/gonavi-mcp-server.env.example ~/.config/containers/systemd/gonavi-mcp-server.env
```
然后修改两处:
- `~/.config/containers/systemd/gonavi-mcp-server.container` 里的 `Volume=/absolute/path/to/gonavi-data:/data:Z`
- `~/.config/containers/systemd/gonavi-mcp-server.env` 里的 `GONAVI_MCP_HTTP_TOKEN`
启动服务:
```bash
systemctl --user daemon-reload
systemctl --user enable --now gonavi-mcp-server.service
systemctl --user status gonavi-mcp-server.service
journalctl --user -u gonavi-mcp-server.service -f
```
如果你希望用户退出登录后服务仍然保持运行,再执行:
```bash
loginctl enable-linger "$USER"
```
Rootless Quadlet 默认搜索路径是 `~/.config/containers/systemd/`;如果你要做系统级 rootful 部署,可改放到 `/etc/containers/systemd/`
## 4. 关于 Compose
Podman 的 `podman compose` 依赖外部 compose provider。也就是说是否能直接复用仓库根目录的 `docker-compose.mcp-server.yml`,取决于你的 Podman 环境是否已经安装并配置了对应 provider。
因此,这个仓库对 Podman 的主支持路径是:
- `podman run`
- Quadlet推荐长期运行
如果你的环境已经有可用的 compose provider再去复用根目录 Compose 文件即可。

View File

@@ -0,0 +1,22 @@
[Unit]
Description=GoNavi MCP Server (rootless Podman)
Wants=network-online.target
After=network-online.target
[Container]
ContainerName=gonavi-mcp-server
Image=ghcr.io/syngnat/gonavi-mcp-server:latest
Pull=newer
EnvironmentFile=./gonavi-mcp-server.env
# 修改成你的 GoNavi 活动数据目录SELinux 主机建议保留 :Z
Volume=/absolute/path/to/gonavi-data:/data:Z
PublishPort=8765:8765
Exec=http
LogDriver=journald
[Service]
Restart=always
TimeoutStartSec=900
[Install]
WantedBy=default.target

View File

@@ -0,0 +1,15 @@
# 容器内 GoNavi 活动数据根目录。通常不需要修改Volume= 已把宿主机目录挂到 /data。
GONAVI_DATA_ROOT=/data
# 日志目录。默认与镜像内目录保持一致。
GONAVI_LOG_DIR=/var/lib/gonavi/logs
# MCP HTTP 监听地址与路径。
GONAVI_MCP_HTTP_ADDR=0.0.0.0:8765
GONAVI_MCP_HTTP_PATH=/mcp
# 远程 MCP 客户端访问时必须携带的 Bearer Token
GONAVI_MCP_HTTP_TOKEN=replace-with-a-random-token
# true = 只暴露结构查询工具,不注册 execute_sql
GONAVI_MCP_SCHEMA_ONLY=true

View File

@@ -0,0 +1,7 @@
services:
gonavi-mcp-server:
build:
context: .
dockerfile: Dockerfile.mcp-server
image: ${GONAVI_MCP_IMAGE_LOCAL:-gonavi-mcp-server:local}
pull_policy: never

View File

@@ -0,0 +1,20 @@
services:
gonavi-mcp-server:
image: ${GONAVI_MCP_IMAGE:-ghcr.io/syngnat/gonavi-mcp-server:latest}
pull_policy: ${GONAVI_MCP_PULL_POLICY:-missing}
container_name: gonavi-mcp-server
restart: unless-stopped
ports:
- "${GONAVI_MCP_HTTP_PORT:-8765}:8765"
environment:
GONAVI_DATA_ROOT: /data
GONAVI_LOG_DIR: /var/lib/gonavi/logs
GONAVI_MCP_HTTP_ADDR: 0.0.0.0:8765
GONAVI_MCP_HTTP_PATH: ${GONAVI_MCP_HTTP_PATH:-/mcp}
GONAVI_MCP_HTTP_TOKEN: ${GONAVI_MCP_HTTP_TOKEN}
GONAVI_MCP_SCHEMA_ONLY: ${GONAVI_MCP_SCHEMA_ONLY:-true}
volumes:
- type: bind
source: ${GONAVI_HOST_DATA_ROOT}
target: /data
command: ["http"]

View File

@@ -0,0 +1,7 @@
services:
gonavi-web-server:
build:
context: .
dockerfile: Dockerfile.web-server
image: ${GONAVI_WEB_IMAGE_LOCAL:-gonavi-web-server:local}
pull_policy: never

View File

@@ -0,0 +1,24 @@
services:
gonavi-web-server:
image: ${GONAVI_WEB_IMAGE:-ghcr.io/syngnat/gonavi-web-server:latest}
pull_policy: ${GONAVI_WEB_PULL_POLICY:-missing}
container_name: gonavi-web-server
restart: unless-stopped
ports:
- "${GONAVI_WEB_HTTP_PORT:-34116}:34116"
environment:
GONAVI_DATA_ROOT: /data
GONAVI_LOG_DIR: /var/lib/gonavi/logs
GONAVI_WEB_ADDR: 0.0.0.0:34116
GONAVI_WEB_PASSWORD: ${GONAVI_WEB_PASSWORD:-}
volumes:
- type: bind
source: ${GONAVI_HOST_DATA_ROOT}
target: /data
healthcheck:
test: ["CMD", "curl", "-fsS", "http://127.0.0.1:34116/__gonavi/healthz"]
interval: 15s
timeout: 5s
retries: 5
start_period: 20s
command: ["web-server"]

View File

@@ -0,0 +1,20 @@
# 宿主机 GoNavi 活动数据目录,目录内至少应包含 connections.json 与 daily_secrets.json
GONAVI_HOST_DATA_ROOT=/absolute/path/to/gonavi-data
# 预构建镜像地址。稳定版通常使用 latest跟随 dev 分支可改成 ghcr.io/syngnat/gonavi-mcp-server:dev-latest
GONAVI_MCP_IMAGE=ghcr.io/syngnat/gonavi-mcp-server:latest
# 预构建镜像拉取策略always / missing / never
GONAVI_MCP_PULL_POLICY=missing
# 远程 MCP 客户端访问时必须携带的 Bearer Token
GONAVI_MCP_HTTP_TOKEN=replace-with-a-random-token
# 宿主机暴露端口
GONAVI_MCP_HTTP_PORT=8765
# MCP HTTP 路径
GONAVI_MCP_HTTP_PATH=/mcp
# true = 只暴露结构查询工具,不注册 execute_sql
GONAVI_MCP_SCHEMA_ONLY=true

View File

@@ -0,0 +1,19 @@
# 宿主机 GoNavi 活动数据目录。
# 建议至少包含connections.json、daily_secrets.json
# 如需可选驱动代理,一并挂载 drivers/。
# Web 认证状态会写入 web_auth.json未设置环境密码时首次访问 /setup 初始化。
GONAVI_HOST_DATA_ROOT=/absolute/path/to/gonavi-data
# 预构建镜像地址。稳定版通常使用 latest跟随 dev 分支可改成 ghcr.io/syngnat/gonavi-web-server:dev-latest
GONAVI_WEB_IMAGE=ghcr.io/syngnat/gonavi-web-server:latest
# 预构建镜像拉取策略always / missing / never
GONAVI_WEB_PULL_POLICY=missing
# 宿主机暴露端口(映射到容器 34116
GONAVI_WEB_HTTP_PORT=34116
# Web 管理员密码(至少 6 位)。留空时首次访问 /setup 初始化;公网部署请使用更强密码。
# 设置或修改后请执行 docker compose up -d --force-recreate请限制本文件的读取权限。
# 新部署如需启用 2FA请先留空并通过 /setup 初始化,再设置为同一密码并重新创建容器。
GONAVI_WEB_PASSWORD=

View File

@@ -7,11 +7,17 @@
"checksumPolicy": "off",
"downloadUrl": "builtin://activate/mariadb"
},
"diros": {
"oceanbase": {
"engine": "go",
"version": "1.9.3",
"checksumPolicy": "off",
"downloadUrl": "builtin://activate/diros"
"downloadUrl": "builtin://activate/oceanbase"
},
"doris": {
"engine": "go",
"version": "1.9.3",
"checksumPolicy": "off",
"downloadUrl": "builtin://activate/doris"
},
"sphinx": {
"engine": "go",
@@ -33,7 +39,7 @@
},
"duckdb": {
"engine": "go",
"version": "2.5.5",
"version": "2.5.6",
"checksumPolicy": "off",
"downloadUrl": "builtin://activate/duckdb"
},
@@ -61,6 +67,12 @@
"checksumPolicy": "off",
"downloadUrl": "builtin://activate/vastbase"
},
"opengauss": {
"engine": "go",
"version": "1.11.1",
"checksumPolicy": "off",
"downloadUrl": "builtin://activate/opengauss"
},
"mongodb": {
"engine": "go",
"version": "2.5.0",
@@ -73,11 +85,23 @@
"checksumPolicy": "off",
"downloadUrl": "builtin://activate/tdengine"
},
"clickhouse": {
"engine": "go",
"version": "2.43.1",
"checksumPolicy": "off",
"downloadUrl": "builtin://activate/clickhouse"
},
"postgres": {
"engine": "go",
"version": "1.11.1",
"checksumPolicy": "off",
"downloadUrl": "builtin://activate/postgres"
},
"elasticsearch": {
"engine": "go",
"version": "8.19.0",
"checksumPolicy": "off",
"downloadUrl": "builtin://activate/elasticsearch"
}
}
}

View File

@@ -0,0 +1,45 @@
# 需求进度追踪 - Oracle对象跳转与存储过程修改执行
## 1. 需求摘要
- 需求名称Oracle对象跳转与存储过程修改执行
- 提出日期2026-06-25
- 负责人Codex
- 目标SQL编辑器支持 Oracle 序列、存储包 Ctrl/Cmd 点击跳转;对象编辑执行 Oracle 存储过程时不再生成非法第二条语句。
- 非目标:不调整 Oracle 连接驱动、不改数据库 schema、不重构侧栏对象树。
## 2. 范围与验收
- 范围QueryEditor 对象元数据、hover/跳转逻辑、DefinitionViewer 对象编辑 SQL 生成、相关 i18n 和回归测试。
- 验收标准:序列/存储包在 SQL 编辑器可出现链接提示并打开定义页;带 SQLPlus `/` 的 Oracle PL/SQL 编辑 SQL 不生成 `/;`,执行时保留完整定义交给后端拆分。
- 依赖与约束:复用现有侧栏 sequence/package tab 类型;不混入 `frontend/wailsjs/go/models.ts` 既有未提交改动。
## 3. 里程碑与进度
- [x] 阶段 1需求澄清确认序列/存储包侧栏已可打开,但 SQL 编辑器跳转缺失;存储过程修改仍报 ORA-00900。
- [x] 阶段 2影响分析影响前端 QueryEditor、DefinitionViewer、i18n、测试后端拆分已有基础保护。
- [x] 阶段 3方案设计补齐元数据与跳转类型对象编辑 SQL 保留 SQLPlus `/` 语义。
- [x] 阶段 4实施计划先补导航闭环再修 PL/SQL 生成/执行,最后跑定向测试。
- [x] 阶段 5实现与自检已完成定向测试、QueryEditor 全文件测试、前端 build、后端 Oracle 拆分测试。
- [ ] 阶段 6评审与交付待提交推送。
- [ ] 阶段 7发布与观察发布后验证 Oracle 对象编辑与 SQL 编辑器跳转。
## 4. 变更清单
- 已完成:补齐 QueryEditor sequence/package 元数据、hover、点击打开定义页修复对象编辑 SQLPlus `/;`;执行路径兼容旧编辑页 `/;`
- 进行中:提交并推送。
- 待处理:发布后验证 Oracle 实库。
## 5. 风险与阻塞
- 风险:三段名称 `schema.package.proc``db.schema.table` 存在歧义。
- 阻塞:无。
- 缓解措施:仅当第一段不是可见数据库时,将三段名称按 Oracle schema-qualified sequence/package 解析。
## 6. 决策记录
- 决策 1序列和存储包复用侧栏现有 `sequence-def` / `package-def` tab 结构。
- 决策 2Oracle PL/SQL 定义执行时保留 SQLPlus `/` 分隔符,由后端拆分器去掉 delimiter避免前端用分号重组造成 ORA-00900。
## 7. 验证记录
- 验证项QueryEditor 导航回归测试、DefinitionViewer 对象编辑测试、SQL 语句拆分测试、i18n catalog 测试、前端 build、后端 Oracle 拆分测试。
- 结果:通过。
- 证据(日志/截图/链接):`npm test -- src/components/QueryEditor.external-sql-save.test.tsx``npm test -- src/components/DefinitionViewer.object-edit.test.tsx src/i18n/catalog.test.ts src/utils/sqlStatementSelection.test.ts``go test ./internal/app -run 'TestDBQueryMultiKeepsOracle|TestDBQueryMultiSkipsOracle'``npm run build`
## 8. 下一步
- 下一步行动:提交并推送到 dev。
- 负责人Codex

View File

@@ -0,0 +1,74 @@
# 需求进度追踪 - 生产连接只读保护
## 1. 需求摘要
- 需求名称:生产连接只读保护
- 提出日期2026-06-23
- 负责人Codex
- 目标:为 SQL 类数据库与 MongoDB 连接增加连接级只读保护启用后仅允许查询阻止写入、DDL、导入和同步目标操作
- 非目标:不为所有侧栏写操作都新增前端隐藏逻辑;不引入新的环境分级体系;不改造 JVM 只读能力
## 2. 范围与验收
- 范围:
- 连接配置模型、保存/回填与 RPC 序列化链路
- 连接弹窗只读开关、查询编辑器本地拦截、DataGrid 导入入口收口
- 后端 SQL/Mongo 查询判定与写操作统一守卫
- 验收标准:
- 支持的数据源出现“生产连接/只允许查询”开关
- 启用后普通查询仍可执行,非查询 SQL / Mongo 写命令被前后端阻止
- 导入、结构变更、清表、同步目标等关键写入口被后端拒绝
- 依赖与约束:
- 保持现有数据源能力判定与 QueryEditor 执行链路
- MongoDB 前端判定以保守拦截为主,最终正确性由后端守卫兜底
## 3. 里程碑与进度
- [x] 阶段 1需求澄清确认采用连接级 `readOnly` 布尔字段,不新建环境系统
- [x] 阶段 2影响分析梳理前端能力面板、查询执行、导入与后端写入口
- [x] 阶段 3方案设计确定“前端预拦截 + 后端最终守卫”双层保护
- [x] 阶段 4实施计划接入配置链路、能力判定、查询判定与写入口守卫
- [x] 阶段 5实现与自检补文案、测试与定向验证
- [x] 阶段 6评审与交付确认范围、风险、回滚点和验证命令
- [ ] 阶段 7发布与观察待体验验证
## 4. 变更清单
- 已完成:
- 新增连接级 `readOnly` 配置字段及前后端序列化支持
- 连接弹窗为 SQL 类数据库与 MongoDB 增加生产连接保护开关
- QueryEditor 增加本地非查询拦截DataGrid 导入入口在只读连接下禁用
- 后端为查询、DDL、导入、清表、同步等写入口增加统一只读守卫
- MongoDB 查询判定改为命令级白名单,不再把所有 JSON 命令都视为只读
- 补充前端/后端定向测试与需求追踪文档
- 进行中:
- 等待体验包验证连接弹窗、查询拦截和写操作拒绝文案
- 待处理:
- 如需进一步优化体验,再补侧栏对象级写菜单的前端隐藏/禁用
## 5. 风险与阻塞
- 风险:
- 前端 SQL/Mongo 只读判定是保守策略,边界命令可能仍需后端兜底
- 现有部分侧栏写菜单仍可能显示,但执行时会被后端拒绝
- 阻塞:
- 暂无
- 缓解措施:
- 关键写入口统一走后端守卫;前端只负责提前反馈与减少误操作
## 6. 决策记录
- 决策 1只对 SQL 类数据库和 MongoDB 支持连接级生产保护,其他数据源忽略 `readOnly`
- 决策 2采用顶层 `readOnly` 布尔字段,避免新增环境枚举和迁移成本
- 决策 3MongoDB 只读判定按命令白名单处理,防止把写命令误放行
## 7. 验证记录
- 验证项:
- 前端数据源能力判定、RPC 配置、连接配置测试
- 后端只读连接守卫与 SQL/Mongo 查询判定测试
- 结果:
- 通过
- 证据(日志/截图/链接):
- `go test ./internal/app -run 'Test(EnsureReadOnlyConnectionAllows|SupportsConnectionReadOnlyMode|IsReadOnlySQLQuery)'`
- `npm --prefix frontend test -- src/utils/dataSourceCapabilities.test.ts src/utils/connectionRpcConfig.test.ts src/components/connectionModal/connectionModalConfig.keepalive.test.ts`
## 8. 下一步
- 下一步行动:
- 用体验包回归验证生产连接下的查询、导入、建库删库、同步目标和 Mongo 命令拦截
- 如需更完整 UX再补侧栏写菜单的只读态收口
- 负责人:
- Codex

View File

@@ -0,0 +1,182 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>AI UI Brainstorming Prototypes</title>
<!-- React & ReactDOM -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- Babel -->
<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
<!-- Ant Design -->
<script src="https://unpkg.com/dayjs/dayjs.min.js"></script>
<script src="https://unpkg.com/antd/dist/antd.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/antd/dist/reset.css" />
<!-- Icons -->
<script src="https://unpkg.com/@ant-design/icons/dist/index.umd.js"></script>
<style>
body { padding: 40px; background: #f0f2f5; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial; }
.prototype-container { display: flex; gap: 40px; }
.prototype-column { flex: 1; max-width: 600px; background: white; border-radius: 12px; box-shadow: 0 4px 12px rgba(0,0,0,0.05); overflow: hidden; }
.prototype-header { padding: 16px 24px; border-bottom: 1px solid #f0f0f0; background: #fafafa; font-weight: bold; }
.prototype-body { padding: 24px; }
/* Default App Theme Colors (Light Mode) */
:root {
--gn-border: rgba(16,24,40,0.08);
--gn-bg: rgba(255,255,255,0.84);
--gn-text: #162033;
--gn-muted: rgba(16,24,40,0.55);
--gn-primary: #1677ff;
--gn-primary-bg: rgba(24,144,255,0.1);
}
/* V1 Styles: Professional List */
.v1-list-item {
display: flex; align-items: center; justify-content: space-between;
padding: 12px 16px; margin-bottom: 8px; border-radius: 8px;
border: 1px solid transparent; cursor: pointer; transition: all 0.2s;
}
.v1-list-item:hover { background: #f5f5f5; }
.v1-list-item.selected {
background: var(--gn-primary-bg); border-color: var(--gn-primary);
}
/* V2 Styles: Refined Cards (ConnectionModal Style) */
.v2-card-grid {
display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
}
.v2-card {
padding: 16px; border-radius: 12px; border: 1px solid var(--gn-border);
cursor: pointer; transition: all 0.2s; background: white;
box-shadow: inset 0 0 0 1px rgba(16,24,40,0.01);
}
.v2-card:hover { border-color: #d9d9d9; background: #fafafa; }
.v2-card.selected {
border-color: var(--gn-primary); box-shadow: 0 0 0 1px var(--gn-primary) inset;
}
.section-title { font-size: 13px; font-weight: 600; color: var(--gn-muted); margin-bottom: 12px; text-transform: uppercase; letter-spacing: 0.5px; }
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { useState } = React;
const { Input, Slider, Select, Button, Form, ConfigProvider } = antd;
const { ThunderboltOutlined, CloudOutlined, ExperimentOutlined, AppstoreOutlined, SettingOutlined, LinkOutlined, KeyOutlined } = icons;
const PROVIDERS = [
{ key: 'openai', label: 'OpenAI', icon: <ThunderboltOutlined />, desc: 'GPT-4o / o1' },
{ key: 'deepseek', label: 'DeepSeek', icon: <ThunderboltOutlined />, desc: 'V3 / R1' },
{ key: 'anthropic', label: 'Claude', icon: <ExperimentOutlined />, desc: 'Sonnet 3.5' },
{ key: 'custom', label: '自定义', icon: <AppstoreOutlined />, desc: '通用 API' },
];
const V1ListDesign = () => {
const [selected, setSelected] = useState('openai');
return (
<div className="prototype-column">
<div className="prototype-header">方案一IDE 专业列表风格 (更克制无彩色渐变)</div>
<div className="prototype-body">
<div className="section-title">提供商选择</div>
<div style={{ marginBottom: 24, padding: 8, background: '#fafafa', borderRadius: 10, border: '1px solid #f0f0f0' }}>
{PROVIDERS.map(p => (
<div key={p.key} className={`v1-list-item ${selected === p.key ? 'selected' : ''}`} onClick={() => setSelected(p.key)}>
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
<div style={{
width: 32, height: 32, borderRadius: 6, display: 'grid', placeItems: 'center',
background: selected === p.key ? '#1677ff' : '#e6f4ff',
color: selected === p.key ? '#fff' : '#1677ff', fontSize: 16
}}>
{p.icon}
</div>
<div>
<div style={{ fontWeight: 500, color: 'var(--gn-text)', fontSize: 14 }}>{p.label}</div>
<div style={{ fontSize: 12, color: 'var(--gn-muted)' }}>{p.desc}</div>
</div>
</div>
<div style={{ width: 16, height: 16, borderRadius: '50%', border: `2px solid ${selected === p.key ? 'var(--gn-primary)' : '#d9d9d9'}`, padding: 2 }}>
{selected === p.key && <div style={{ width: '100%', height: '100%', background: 'var(--gn-primary)', borderRadius: '50%' }} />}
</div>
</div>
))}
</div>
<div className="section-title">连接配置 (紧凑表单)</div>
<Form layout="vertical" size="middle">
<Form.Item label="API Endpoint">
<Input placeholder="https://api.openai.com/v1" prefix={<LinkOutlined style={{color: 'var(--gn-muted)'}}/>} />
</Form.Item>
<Form.Item label="API Key">
<Input.Password placeholder="sk-..." prefix={<KeyOutlined style={{color: 'var(--gn-muted)'}}/>} />
</Form.Item>
<Form.Item label="Model Name">
<Input placeholder="gpt-4o" prefix={<AppstoreOutlined style={{color: 'var(--gn-muted)'}}/>} />
</Form.Item>
</Form>
</div>
</div>
);
};
const V2CardDesign = () => {
const [selected, setSelected] = useState('openai');
return (
<div className="prototype-column">
<div className="prototype-header">方案二GoNavi 统一卡片风格 (类似 ConnectionModal)</div>
<div className="prototype-body">
<div className="section-title">选择服务提供商</div>
<div className="v2-card-grid" style={{ marginBottom: 24 }}>
{PROVIDERS.map(p => (
<div key={p.key} className={`v2-card ${selected === p.key ? 'selected' : ''}`} onClick={() => setSelected(p.key)}>
<div style={{ display: 'flex', alignItems: 'flex-start', gap: 12 }}>
<div style={{ color: selected === p.key ? 'var(--gn-primary)' : 'var(--gn-muted)', fontSize: 20, marginTop: 2 }}>
{p.icon}
</div>
<div>
<div style={{ fontWeight: 600, color: 'var(--gn-text)', fontSize: 14 }}>{p.label}</div>
<div style={{ fontSize: 12, color: 'var(--gn-muted)', marginTop: 4 }}>{p.desc}</div>
</div>
</div>
</div>
))}
</div>
<div style={{ padding: 20, borderRadius: 12, border: '1px solid var(--gn-border)', background: '#fafafa' }}>
<div className="section-title" style={{ marginTop: 0 }}>认证与设置</div>
<Form layout="horizontal" labelCol={{ span: 6 }} wrapperCol={{ span: 18 }} size="middle">
<Form.Item label="Endpoint" style={{ marginBottom: 16 }}>
<Input placeholder="https://api..." />
</Form.Item>
<Form.Item label="API Key" style={{ marginBottom: 16 }}>
<Input.Password placeholder="sk-..." />
</Form.Item>
<Form.Item label="模型名称" style={{ marginBottom: 0 }}>
<Input placeholder="例如 gpt-4o" />
</Form.Item>
</Form>
</div>
</div>
</div>
);
};
const App = () => (
<ConfigProvider theme={{ token: { colorPrimary: '#1677ff', borderRadius: 6 } }}>
<div style={{ marginBottom: 24 }}>
<h1 style={{ fontSize: 24, margin: 0 }}>AI 设置 UI 重构探讨</h1>
<p style={{ color: 'var(--gn-muted)' }}>当前设计带有太多渐变和鲜艳色彩"AI 味"以下是遵循 GoNavi 本身设计规范克制专业的两个方案</p>
</div>
<div className="prototype-container">
<V1ListDesign />
<V2CardDesign />
</div>
</ConfigProvider>
);
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
</script>
</body>
</html>

View File

@@ -5,9 +5,26 @@
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GoNavi</title>
<script>
if (typeof window !== 'undefined' && !window.go) {
window.go = {
app: {
App: new Proxy({}, { get: () => async () => ({ success: false }) })
}
};
}
if (typeof window !== 'undefined' && !window.runtime) {
window.runtime = new Proxy({}, {
get: (target, prop) => {
if (prop === 'Environment') return async () => ({ platform: 'darwin' });
return typeof prop === 'string' && prop.startsWith('WindowIs') ? () => false : () => {};
}
});
}
</script>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script type="module" src="/src/bootstrap.ts"></script>
</body>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -1,12 +1,16 @@
{
"name": "gonavi-client",
"private": true,
"version": "0.0.1",
"version": "0.6.5",
"license": "Apache-2.0",
"type": "module",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
"preview": "vite preview",
"test": "vitest run",
"postinstall": "patch-package --error-on-fail",
"i18n:scan": "go run ../tools/i18n-scan --root .."
},
"dependencies": {
"@ant-design/icons": "^5.2.6",
@@ -15,11 +19,21 @@
"@dnd-kit/sortable": "^10.0.0",
"@dnd-kit/utilities": "^3.2.2",
"@monaco-editor/react": "^4.6.0",
"@types/dagre": "^0.7.54",
"@types/react-syntax-highlighter": "^15.5.13",
"antd": "^5.12.0",
"clsx": "^2.1.0",
"dagre": "^0.8.5",
"fflate": "^0.8.3",
"mermaid": "^11.13.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-markdown": "^10.1.0",
"react-resizable": "^3.1.3",
"react-syntax-highlighter": "^16.1.1",
"reactflow": "^11.11.4",
"recharts": "^3.8.1",
"remark-gfm": "^4.0.1",
"sql-formatter": "^15.7.0",
"uuid": "^9.0.1",
"zustand": "^4.4.7"
@@ -28,9 +42,13 @@
"@types/react": "^18.2.43",
"@types/react-dom": "^18.2.17",
"@types/react-resizable": "^3.0.8",
"@types/react-test-renderer": "^18.0.7",
"@types/uuid": "^9.0.7",
"@vitejs/plugin-react": "^4.2.1",
"patch-package": "^8.0.1",
"react-test-renderer": "^18.2.0",
"typescript": "^5.2.2",
"vite": "^5.0.8"
"vite": "^5.0.8",
"vitest": "^3.2.4"
}
}
}

View File

@@ -1 +1 @@
5b8157374dae5f9340e31b2d0bd2c00e
339d5c974e6ab5cba2d5def171f5ce0a

View File

@@ -0,0 +1,461 @@
diff --git a/node_modules/rc-table/es/VirtualTable/BodyGrid.js b/node_modules/rc-table/es/VirtualTable/BodyGrid.js
index 42dcde8..2cca743 100644
--- a/node_modules/rc-table/es/VirtualTable/BodyGrid.js
+++ b/node_modules/rc-table/es/VirtualTable/BodyGrid.js
@@ -23,6 +23,8 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
sticky = _useContext2.sticky,
scrollY = _useContext2.scrollY,
listItemHeight = _useContext2.listItemHeight,
+ listItemHeightFixed = _useContext2.listItemHeightFixed,
+ listItemColumnVirtual = _useContext2.listItemColumnVirtual,
getComponent = _useContext2.getComponent,
onTablePropScroll = _useContext2.onScroll;
@@ -113,8 +115,14 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
var extraRender = function extraRender(info) {
+ // The column-window fast path explicitly requires rows without spans.
+ // Avoid scanning every column on each vertical window update.
+ if (listItemColumnVirtual) {
+ return null;
+ }
var start = info.start,
end = info.end,
getSize = info.getSize,
- offsetY = info.offsetY;
+ offsetY = info.offsetY,
+ offsetX = info.offsetX;
// Do nothing if no data
if (end < 0) {
@@ -198,6 +200,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
style: {
top: -offsetY + sizeInfo.top
},
+ offsetX: offsetX,
extra: true,
getHeight: getHeight
});
@@ -239,6 +242,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
className: tblPrefixCls,
height: scrollY,
itemHeight: listItemHeight || 24,
+ itemHeightFixed: listItemHeightFixed,
data: flattenData,
itemKey: function itemKey(item) {
return getRowKey(item.record);
@@ -262,6 +266,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
data: item,
rowKey: rowKey,
index: index,
+ offsetX: itemProps.offsetX,
style: itemProps.style
});
}));
diff --git a/node_modules/rc-table/es/VirtualTable/BodyLine.js b/node_modules/rc-table/es/VirtualTable/BodyLine.js
index c760819..1a6c396 100644
--- a/node_modules/rc-table/es/VirtualTable/BodyLine.js
+++ b/node_modules/rc-table/es/VirtualTable/BodyLine.js
@@ -2,7 +2,7 @@ import _extends from "@babel/runtime/helpers/esm/extends";
import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
-var _excluded = ["data", "index", "className", "rowKey", "style", "extra", "getHeight"];
+var _excluded = ["data", "index", "className", "rowKey", "style", "offsetX", "extra", "getHeight"];
import { useContext } from '@rc-component/context';
import classNames from 'classnames';
import * as React from 'react';
@@ -10,7 +10,7 @@ import Cell from "../Cell";
import TableContext, { responseImmutable } from "../context/TableContext";
import useRowInfo from "../hooks/useRowInfo";
import VirtualCell from "./VirtualCell";
-import { StaticContext } from "./context";
+import { GridContext, StaticContext } from "./context";
import { computedExpandedClassName } from "../utils/expandUtil";
var BodyLine = /*#__PURE__*/React.forwardRef(function (props, ref) {
var data = props.data,
@@ -18,20 +18,25 @@ var BodyLine = /*#__PURE__*/React.forwardRef(function (props, ref) {
className = props.className,
rowKey = props.rowKey,
style = props.style,
+ offsetX = props.offsetX,
extra = props.extra,
getHeight = props.getHeight,
restProps = _objectWithoutProperties(props, _excluded);
var record = data.record,
indent = data.indent,
renderIndex = data.index;
- var _useContext = useContext(TableContext, ['prefixCls', 'flattenColumns', 'fixColumn', 'componentWidth', 'scrollX']),
+ var _useContext = useContext(TableContext, ['prefixCls', 'flattenColumns', 'fixColumn', 'componentWidth', 'scrollX', 'direction']),
scrollX = _useContext.scrollX,
flattenColumns = _useContext.flattenColumns,
prefixCls = _useContext.prefixCls,
fixColumn = _useContext.fixColumn,
- componentWidth = _useContext.componentWidth;
- var _useContext2 = useContext(StaticContext, ['getComponent']),
- getComponent = _useContext2.getComponent;
+ componentWidth = _useContext.componentWidth,
+ direction = _useContext.direction;
+ var _useContext2 = useContext(StaticContext, ['getComponent', 'listItemColumnVirtual']),
+ getComponent = _useContext2.getComponent,
+ listItemColumnVirtual = _useContext2.listItemColumnVirtual;
+ var _useContext3 = useContext(GridContext, ['columnsOffset']),
+ columnsOffset = _useContext3.columnsOffset;
var rowInfo = useRowInfo(record, rowKey, index, indent);
var RowComponent = getComponent(['body', 'row'], 'div');
var cellComponent = getComponent(['body', 'cell'], 'div');
@@ -71,12 +76,7 @@ var BodyLine = /*#__PURE__*/React.forwardRef(function (props, ref) {
rowStyle.position = 'absolute';
rowStyle.pointerEvents = 'none';
}
- var rowNode = /*#__PURE__*/React.createElement(RowComponent, _extends({}, rowProps, restProps, {
- "data-row-key": rowKey,
- ref: rowSupportExpand ? null : ref,
- className: classNames(className, "".concat(prefixCls, "-row"), rowProps === null || rowProps === void 0 ? void 0 : rowProps.className, _defineProperty({}, "".concat(prefixCls, "-row-extra"), extra)),
- style: _objectSpread(_objectSpread({}, rowStyle), rowProps === null || rowProps === void 0 ? void 0 : rowProps.style)
- }), flattenColumns.map(function (column, colIndex) {
+ var renderVirtualCell = function renderVirtualCell(column, colIndex) {
return /*#__PURE__*/React.createElement(VirtualCell, {
key: colIndex,
component: cellComponent,
@@ -90,7 +90,60 @@ var BodyLine = /*#__PURE__*/React.forwardRef(function (props, ref) {
inverse: extra,
getHeight: getHeight
});
- }));
+ };
+ var columnNodes;
+ if (listItemColumnVirtual && direction !== 'rtl' && !extra && columnsOffset && columnsOffset.length === flattenColumns.length) {
+ var mergedOffsetX = Math.max(0, offsetX || 0);
+ var viewportWidth = Math.max(0, componentWidth || 0);
+ var overscanWidth = Math.max(320, viewportWidth);
+ var visibleStart = Math.max(0, mergedOffsetX - overscanWidth);
+ var visibleEnd = mergedOffsetX + viewportWidth + overscanWidth;
+ var nodes = [];
+ var spacerStart = -1;
+ var spacerWidth = 0;
+ var flushSpacer = function flushSpacer() {
+ if (spacerWidth <= 0) return;
+ nodes.push(/*#__PURE__*/React.createElement(cellComponent, {
+ "aria-hidden": true,
+ className: "".concat(prefixCls, "-cell-virtual-spacer"),
+ key: "virtual-spacer-".concat(spacerStart),
+ style: {
+ border: 0,
+ flex: "0 0 ".concat(spacerWidth, "px"),
+ minWidth: 0,
+ padding: 0,
+ pointerEvents: 'none',
+ visibility: 'hidden',
+ width: "".concat(spacerWidth, "px")
+ }
+ }));
+ spacerStart = -1;
+ spacerWidth = 0;
+ };
+ flattenColumns.forEach(function (column, colIndex) {
+ var columnStart = colIndex > 0 ? columnsOffset[colIndex - 1] : 0;
+ var columnEnd = columnsOffset[colIndex] || columnStart;
+ var fixedColumn = column.fixed === true || column.fixed === 'left' || column.fixed === 'right';
+ var visibleColumn = fixedColumn || columnEnd >= visibleStart && columnStart <= visibleEnd;
+ if (visibleColumn) {
+ flushSpacer();
+ nodes.push(renderVirtualCell(column, colIndex));
+ } else {
+ if (spacerStart < 0) spacerStart = colIndex;
+ spacerWidth += Math.max(0, columnEnd - columnStart);
+ }
+ });
+ flushSpacer();
+ columnNodes = nodes;
+ } else {
+ columnNodes = flattenColumns.map(renderVirtualCell);
+ }
+ var rowNode = /*#__PURE__*/React.createElement(RowComponent, _extends({}, rowProps, restProps, {
+ "data-row-key": rowKey,
+ ref: rowSupportExpand ? null : ref,
+ className: classNames(className, "".concat(prefixCls, "-row"), rowProps === null || rowProps === void 0 ? void 0 : rowProps.className, _defineProperty({}, "".concat(prefixCls, "-row-extra"), extra)),
+ style: _objectSpread(_objectSpread({}, rowStyle), rowProps === null || rowProps === void 0 ? void 0 : rowProps.style)
+ }), columnNodes);
if (rowSupportExpand) {
return /*#__PURE__*/React.createElement("div", {
ref: ref
diff --git a/node_modules/rc-table/es/VirtualTable/context.d.ts b/node_modules/rc-table/es/VirtualTable/context.d.ts
index e558165..ccd74fc 100644
--- a/node_modules/rc-table/es/VirtualTable/context.d.ts
+++ b/node_modules/rc-table/es/VirtualTable/context.d.ts
@@ -3,6 +3,8 @@ import type { GetComponent, TableSticky } from '../interface';
export interface StaticContextProps {
scrollY: number;
listItemHeight: number;
+ listItemHeightFixed?: boolean;
+ listItemColumnVirtual?: boolean;
sticky: boolean | TableSticky;
getComponent: GetComponent;
onScroll?: React.UIEventHandler<HTMLDivElement>;
diff --git a/node_modules/rc-table/es/VirtualTable/index.d.ts b/node_modules/rc-table/es/VirtualTable/index.d.ts
index 31fe8e0..c5097b2 100644
--- a/node_modules/rc-table/es/VirtualTable/index.d.ts
+++ b/node_modules/rc-table/es/VirtualTable/index.d.ts
@@ -8,6 +8,9 @@ export interface VirtualTableProps<RecordType> extends Omit<TableProps<RecordTyp
y: number;
};
listItemHeight?: number;
+ listItemHeightFixed?: boolean;
+ /** LTR-only fast path; requires body cells without colSpan/rowSpan. */
+ listItemColumnVirtual?: boolean;
}
export type ForwardGenericVirtualTable = (<RecordType>(props: TableProps<RecordType> & React.RefAttributes<Reference>) => React.ReactElement) & {
displayName?: string;
diff --git a/node_modules/rc-table/es/VirtualTable/index.js b/node_modules/rc-table/es/VirtualTable/index.js
index fb00e9b..a778aa6 100644
--- a/node_modules/rc-table/es/VirtualTable/index.js
+++ b/node_modules/rc-table/es/VirtualTable/index.js
@@ -27,6 +27,8 @@ function VirtualTable(props, ref) {
prefixCls = _props$prefixCls === void 0 ? DEFAULT_PREFIX : _props$prefixCls,
className = props.className,
listItemHeight = props.listItemHeight,
+ listItemHeightFixed = props.listItemHeightFixed,
+ listItemColumnVirtual = props.listItemColumnVirtual,
components = props.components,
onScroll = props.onScroll;
var _ref = scroll || {},
@@ -61,10 +63,12 @@ function VirtualTable(props, ref) {
sticky: sticky,
scrollY: scrollY,
listItemHeight: listItemHeight,
+ listItemHeightFixed: listItemHeightFixed,
+ listItemColumnVirtual: listItemColumnVirtual,
getComponent: getComponent,
onScroll: onInternalScroll
};
- }, [sticky, scrollY, listItemHeight, getComponent, onInternalScroll]);
+ }, [sticky, scrollY, listItemHeight, listItemHeightFixed, listItemColumnVirtual, getComponent, onInternalScroll]);
// ========================== Render ==========================
return /*#__PURE__*/React.createElement(StaticContext.Provider, {
diff --git a/node_modules/rc-table/lib/VirtualTable/BodyGrid.js b/node_modules/rc-table/lib/VirtualTable/BodyGrid.js
index dee2539..b077789 100644
--- a/node_modules/rc-table/lib/VirtualTable/BodyGrid.js
+++ b/node_modules/rc-table/lib/VirtualTable/BodyGrid.js
@@ -33,6 +33,8 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
sticky = _useContext2.sticky,
scrollY = _useContext2.scrollY,
listItemHeight = _useContext2.listItemHeight,
+ listItemHeightFixed = _useContext2.listItemHeightFixed,
+ listItemColumnVirtual = _useContext2.listItemColumnVirtual,
getComponent = _useContext2.getComponent,
onTablePropScroll = _useContext2.onScroll;
@@ -123,8 +125,14 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
var extraRender = function extraRender(info) {
+ // The column-window fast path explicitly requires rows without spans.
+ // Avoid scanning every column on each vertical window update.
+ if (listItemColumnVirtual) {
+ return null;
+ }
var start = info.start,
end = info.end,
getSize = info.getSize,
- offsetY = info.offsetY;
+ offsetY = info.offsetY,
+ offsetX = info.offsetX;
// Do nothing if no data
if (end < 0) {
@@ -208,6 +210,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
style: {
top: -offsetY + sizeInfo.top
},
+ offsetX: offsetX,
extra: true,
getHeight: getHeight
});
@@ -249,6 +252,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
className: tblPrefixCls,
height: scrollY,
itemHeight: listItemHeight || 24,
+ itemHeightFixed: listItemHeightFixed,
data: flattenData,
itemKey: function itemKey(item) {
return getRowKey(item.record);
@@ -272,6 +276,7 @@ var Grid = /*#__PURE__*/React.forwardRef(function (props, ref) {
data: item,
rowKey: rowKey,
index: index,
+ offsetX: itemProps.offsetX,
style: itemProps.style
});
}));
diff --git a/node_modules/rc-table/lib/VirtualTable/BodyLine.js b/node_modules/rc-table/lib/VirtualTable/BodyLine.js
index e1275fb..d962380 100644
--- a/node_modules/rc-table/lib/VirtualTable/BodyLine.js
+++ b/node_modules/rc-table/lib/VirtualTable/BodyLine.js
@@ -19,7 +19,7 @@ var _useRowInfo = _interopRequireDefault(require("../hooks/useRowInfo"));
var _VirtualCell = _interopRequireDefault(require("./VirtualCell"));
var _context2 = require("./context");
var _expandUtil = require("../utils/expandUtil");
-var _excluded = ["data", "index", "className", "rowKey", "style", "extra", "getHeight"];
+var _excluded = ["data", "index", "className", "rowKey", "style", "offsetX", "extra", "getHeight"];
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
var BodyLine = /*#__PURE__*/React.forwardRef(function (props, ref) {
@@ -28,20 +28,25 @@ var BodyLine = /*#__PURE__*/React.forwardRef(function (props, ref) {
className = props.className,
rowKey = props.rowKey,
style = props.style,
+ offsetX = props.offsetX,
extra = props.extra,
getHeight = props.getHeight,
restProps = (0, _objectWithoutProperties2.default)(props, _excluded);
var record = data.record,
indent = data.indent,
renderIndex = data.index;
- var _useContext = (0, _context.useContext)(_TableContext.default, ['prefixCls', 'flattenColumns', 'fixColumn', 'componentWidth', 'scrollX']),
+ var _useContext = (0, _context.useContext)(_TableContext.default, ['prefixCls', 'flattenColumns', 'fixColumn', 'componentWidth', 'scrollX', 'direction']),
scrollX = _useContext.scrollX,
flattenColumns = _useContext.flattenColumns,
prefixCls = _useContext.prefixCls,
fixColumn = _useContext.fixColumn,
- componentWidth = _useContext.componentWidth;
- var _useContext2 = (0, _context.useContext)(_context2.StaticContext, ['getComponent']),
- getComponent = _useContext2.getComponent;
+ componentWidth = _useContext.componentWidth,
+ direction = _useContext.direction;
+ var _useContext2 = (0, _context.useContext)(_context2.StaticContext, ['getComponent', 'listItemColumnVirtual']),
+ getComponent = _useContext2.getComponent,
+ listItemColumnVirtual = _useContext2.listItemColumnVirtual;
+ var _useContext3 = (0, _context.useContext)(_context2.GridContext, ['columnsOffset']),
+ columnsOffset = _useContext3.columnsOffset;
var rowInfo = (0, _useRowInfo.default)(record, rowKey, index, indent);
var RowComponent = getComponent(['body', 'row'], 'div');
var cellComponent = getComponent(['body', 'cell'], 'div');
@@ -81,12 +86,7 @@ var BodyLine = /*#__PURE__*/React.forwardRef(function (props, ref) {
rowStyle.position = 'absolute';
rowStyle.pointerEvents = 'none';
}
- var rowNode = /*#__PURE__*/React.createElement(RowComponent, (0, _extends2.default)({}, rowProps, restProps, {
- "data-row-key": rowKey,
- ref: rowSupportExpand ? null : ref,
- className: (0, _classnames.default)(className, "".concat(prefixCls, "-row"), rowProps === null || rowProps === void 0 ? void 0 : rowProps.className, (0, _defineProperty2.default)({}, "".concat(prefixCls, "-row-extra"), extra)),
- style: (0, _objectSpread2.default)((0, _objectSpread2.default)({}, rowStyle), rowProps === null || rowProps === void 0 ? void 0 : rowProps.style)
- }), flattenColumns.map(function (column, colIndex) {
+ var renderVirtualCell = function renderVirtualCell(column, colIndex) {
return /*#__PURE__*/React.createElement(_VirtualCell.default, {
key: colIndex,
component: cellComponent,
@@ -100,7 +100,60 @@ var BodyLine = /*#__PURE__*/React.forwardRef(function (props, ref) {
inverse: extra,
getHeight: getHeight
});
- }));
+ };
+ var columnNodes;
+ if (listItemColumnVirtual && direction !== 'rtl' && !extra && columnsOffset && columnsOffset.length === flattenColumns.length) {
+ var mergedOffsetX = Math.max(0, offsetX || 0);
+ var viewportWidth = Math.max(0, componentWidth || 0);
+ var overscanWidth = Math.max(320, viewportWidth);
+ var visibleStart = Math.max(0, mergedOffsetX - overscanWidth);
+ var visibleEnd = mergedOffsetX + viewportWidth + overscanWidth;
+ var nodes = [];
+ var spacerStart = -1;
+ var spacerWidth = 0;
+ var flushSpacer = function flushSpacer() {
+ if (spacerWidth <= 0) return;
+ nodes.push(/*#__PURE__*/React.createElement(cellComponent, {
+ "aria-hidden": true,
+ className: "".concat(prefixCls, "-cell-virtual-spacer"),
+ key: "virtual-spacer-".concat(spacerStart),
+ style: {
+ border: 0,
+ flex: "0 0 ".concat(spacerWidth, "px"),
+ minWidth: 0,
+ padding: 0,
+ pointerEvents: 'none',
+ visibility: 'hidden',
+ width: "".concat(spacerWidth, "px")
+ }
+ }));
+ spacerStart = -1;
+ spacerWidth = 0;
+ };
+ flattenColumns.forEach(function (column, colIndex) {
+ var columnStart = colIndex > 0 ? columnsOffset[colIndex - 1] : 0;
+ var columnEnd = columnsOffset[colIndex] || columnStart;
+ var fixedColumn = column.fixed === true || column.fixed === 'left' || column.fixed === 'right';
+ var visibleColumn = fixedColumn || columnEnd >= visibleStart && columnStart <= visibleEnd;
+ if (visibleColumn) {
+ flushSpacer();
+ nodes.push(renderVirtualCell(column, colIndex));
+ } else {
+ if (spacerStart < 0) spacerStart = colIndex;
+ spacerWidth += Math.max(0, columnEnd - columnStart);
+ }
+ });
+ flushSpacer();
+ columnNodes = nodes;
+ } else {
+ columnNodes = flattenColumns.map(renderVirtualCell);
+ }
+ var rowNode = /*#__PURE__*/React.createElement(RowComponent, (0, _extends2.default)({}, rowProps, restProps, {
+ "data-row-key": rowKey,
+ ref: rowSupportExpand ? null : ref,
+ className: (0, _classnames.default)(className, "".concat(prefixCls, "-row"), rowProps === null || rowProps === void 0 ? void 0 : rowProps.className, (0, _defineProperty2.default)({}, "".concat(prefixCls, "-row-extra"), extra)),
+ style: (0, _objectSpread2.default)((0, _objectSpread2.default)({}, rowStyle), rowProps === null || rowProps === void 0 ? void 0 : rowProps.style)
+ }), columnNodes);
if (rowSupportExpand) {
return /*#__PURE__*/React.createElement("div", {
ref: ref
diff --git a/node_modules/rc-table/lib/VirtualTable/context.d.ts b/node_modules/rc-table/lib/VirtualTable/context.d.ts
index e558165..ccd74fc 100644
--- a/node_modules/rc-table/lib/VirtualTable/context.d.ts
+++ b/node_modules/rc-table/lib/VirtualTable/context.d.ts
@@ -3,6 +3,8 @@ import type { GetComponent, TableSticky } from '../interface';
export interface StaticContextProps {
scrollY: number;
listItemHeight: number;
+ listItemHeightFixed?: boolean;
+ listItemColumnVirtual?: boolean;
sticky: boolean | TableSticky;
getComponent: GetComponent;
onScroll?: React.UIEventHandler<HTMLDivElement>;
diff --git a/node_modules/rc-table/lib/VirtualTable/index.d.ts b/node_modules/rc-table/lib/VirtualTable/index.d.ts
index 31fe8e0..c5097b2 100644
--- a/node_modules/rc-table/lib/VirtualTable/index.d.ts
+++ b/node_modules/rc-table/lib/VirtualTable/index.d.ts
@@ -8,6 +8,9 @@ export interface VirtualTableProps<RecordType> extends Omit<TableProps<RecordTyp
y: number;
};
listItemHeight?: number;
+ listItemHeightFixed?: boolean;
+ /** LTR-only fast path; requires body cells without colSpan/rowSpan. */
+ listItemColumnVirtual?: boolean;
}
export type ForwardGenericVirtualTable = (<RecordType>(props: TableProps<RecordType> & React.RefAttributes<Reference>) => React.ReactElement) & {
displayName?: string;
diff --git a/node_modules/rc-table/lib/VirtualTable/index.js b/node_modules/rc-table/lib/VirtualTable/index.js
index 8e31a9b..24ab161 100644
--- a/node_modules/rc-table/lib/VirtualTable/index.js
+++ b/node_modules/rc-table/lib/VirtualTable/index.js
@@ -38,6 +38,8 @@ function VirtualTable(props, ref) {
prefixCls = _props$prefixCls === void 0 ? _Table.DEFAULT_PREFIX : _props$prefixCls,
className = props.className,
listItemHeight = props.listItemHeight,
+ listItemHeightFixed = props.listItemHeightFixed,
+ listItemColumnVirtual = props.listItemColumnVirtual,
components = props.components,
onScroll = props.onScroll;
var _ref = scroll || {},
@@ -72,10 +74,12 @@ function VirtualTable(props, ref) {
sticky: sticky,
scrollY: scrollY,
listItemHeight: listItemHeight,
+ listItemHeightFixed: listItemHeightFixed,
+ listItemColumnVirtual: listItemColumnVirtual,
getComponent: getComponent,
onScroll: onInternalScroll
};
- }, [sticky, scrollY, listItemHeight, getComponent, onInternalScroll]);
+ }, [sticky, scrollY, listItemHeight, listItemHeightFixed, listItemColumnVirtual, getComponent, onInternalScroll]);
// ========================== Render ==========================
return /*#__PURE__*/React.createElement(_context.StaticContext.Provider, {

View File

@@ -0,0 +1,336 @@
diff --git a/node_modules/rc-virtual-list/es/List.d.ts b/node_modules/rc-virtual-list/es/List.d.ts
index c517366..e414513 100644
--- a/node_modules/rc-virtual-list/es/List.d.ts
+++ b/node_modules/rc-virtual-list/es/List.d.ts
@@ -20,6 +20,8 @@ export interface ListProps<T> extends Omit<React.HTMLAttributes<any>, 'children'
data: T[];
height?: number;
itemHeight?: number;
+ /** Treat every item as exactly itemHeight and calculate the visible range in O(1). */
+ itemHeightFixed?: boolean;
/** If not match virtual scroll condition, Set List still use height of container. */
fullHeight?: boolean;
itemKey: React.Key | ((item: T) => React.Key);
diff --git a/node_modules/rc-virtual-list/es/List.js b/node_modules/rc-virtual-list/es/List.js
index 2926056..e917ba1 100644
--- a/node_modules/rc-virtual-list/es/List.js
+++ b/node_modules/rc-virtual-list/es/List.js
@@ -4,7 +4,7 @@ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
-var _excluded = ["prefixCls", "className", "height", "itemHeight", "fullHeight", "style", "data", "children", "itemKey", "virtual", "direction", "scrollWidth", "component", "onScroll", "onVirtualScroll", "onVisibleChange", "innerProps", "extraRender", "styles", "showScrollBar"];
+var _excluded = ["prefixCls", "className", "height", "itemHeight", "itemHeightFixed", "fullHeight", "style", "data", "children", "itemKey", "virtual", "direction", "scrollWidth", "component", "onScroll", "onVirtualScroll", "onVisibleChange", "innerProps", "extraRender", "styles", "showScrollBar"];
import classNames from 'classnames';
import ResizeObserver from 'rc-resize-observer';
import { useEvent } from 'rc-util';
@@ -35,6 +35,7 @@ export function RawList(props, ref) {
className = props.className,
height = props.height,
itemHeight = props.itemHeight,
+ itemHeightFixed = props.itemHeightFixed,
_props$fullHeight = props.fullHeight,
fullHeight = _props$fullHeight === void 0 ? true : _props$fullHeight,
style = props.style,
@@ -65,7 +66,7 @@ export function RawList(props, ref) {
}, [itemKey]);
// ================================ Height ================================
- var _useHeights = useHeights(getKey, null, null),
+ var _useHeights = useHeights(getKey, null, null, itemHeightFixed),
_useHeights2 = _slicedToArray(_useHeights, 4),
setInstanceRef = _useHeights2[0],
collectHeight = _useHeights2[1],
@@ -74,11 +75,12 @@ export function RawList(props, ref) {
// ================================= MISC =================================
var useVirtual = !!(virtual !== false && height && itemHeight);
- var containerHeight = React.useMemo(function () {
+ var measuredContainerHeight = React.useMemo(function () {
return Object.values(heights.maps).reduce(function (total, curr) {
return total + curr;
}, 0);
}, [heights.id, heights.maps]);
+ var containerHeight = itemHeightFixed ? itemHeight * (data || EMPTY_DATA).length : measuredContainerHeight;
var inVirtual = useVirtual && data && (Math.max(itemHeight * data.length, containerHeight) > height || !!scrollWidth);
var isRTL = direction === 'rtl';
var mergedClassName = classNames(prefixCls, _defineProperty({}, "".concat(prefixCls, "-rtl"), isRTL), className);
@@ -159,6 +161,28 @@ export function RawList(props, ref) {
offset: undefined
};
}
+ if (itemHeightFixed && itemHeight > 0) {
+ var fixedDataLen = mergedData.length;
+ var fixedScrollHeight = fixedDataLen * itemHeight;
+ if (fixedDataLen === 0) {
+ return {
+ scrollHeight: 0,
+ start: 0,
+ end: -1,
+ offset: 0
+ };
+ }
+ var fixedMaxScrollTop = Math.max(0, fixedScrollHeight - height);
+ var fixedOffsetTop = Math.max(0, Math.min(fixedMaxScrollTop, offsetTop));
+ var fixedStartIndex = Math.min(fixedDataLen - 1, Math.max(0, Math.ceil(fixedOffsetTop / itemHeight) - 1));
+ var fixedEndIndex = Math.min(fixedDataLen - 1, Math.floor((fixedOffsetTop + height) / itemHeight) + 1);
+ return {
+ scrollHeight: fixedScrollHeight,
+ start: fixedStartIndex,
+ end: fixedEndIndex,
+ offset: fixedStartIndex * itemHeight
+ };
+ }
var itemTop = 0;
var startIndex;
var startOffset;
@@ -201,7 +225,7 @@ export function RawList(props, ref) {
end: endIndex,
offset: startOffset
};
- }, [inVirtual, useVirtual, offsetTop, mergedData, heightUpdatedMark, height]),
+ }, [inVirtual, useVirtual, offsetTop, mergedData, heightUpdatedMark, height, itemHeightFixed, itemHeight]),
scrollHeight = _React$useMemo.scrollHeight,
start = _React$useMemo.start,
end = _React$useMemo.end,
@@ -213,6 +237,10 @@ export function RawList(props, ref) {
// Which will make scroll jump.
// Let's sync scroll top to avoid jump
React.useLayoutEffect(function () {
+ if (itemHeightFixed) {
+ heights.resetRecord();
+ return;
+ }
var changedRecord = heights.getRecord();
if (changedRecord.size === 1) {
var recordKey = Array.from(changedRecord.keys())[0];
@@ -232,7 +260,7 @@ export function RawList(props, ref) {
}
}
heights.resetRecord();
- }, [scrollHeight]);
+ }, [scrollHeight, itemHeightFixed, heightUpdatedMark]);
// ================================= Size =================================
var _React$useState = React.useState({
diff --git a/node_modules/rc-virtual-list/es/hooks/useHeights.d.ts b/node_modules/rc-virtual-list/es/hooks/useHeights.d.ts
--- a/node_modules/rc-virtual-list/es/hooks/useHeights.d.ts
+++ b/node_modules/rc-virtual-list/es/hooks/useHeights.d.ts
@@ -1,6 +1,6 @@
import type { GetKey } from '../interface';
import CacheMap from '../utils/CacheMap';
-export default function useHeights<T>(getKey: GetKey<T>, onItemAdd?: (item: T) => void, onItemRemove?: (item: T) => void): [
+export default function useHeights<T>(getKey: GetKey<T>, onItemAdd?: (item: T) => void, onItemRemove?: (item: T) => void, disabled?: boolean): [
setInstanceRef: (item: T, instance: HTMLElement) => void,
collectHeight: (sync?: boolean) => void,
cacheMap: CacheMap,
diff --git a/node_modules/rc-virtual-list/es/hooks/useHeights.js b/node_modules/rc-virtual-list/es/hooks/useHeights.js
index 0323aae..38e6e75 100644
--- a/node_modules/rc-virtual-list/es/hooks/useHeights.js
+++ b/node_modules/rc-virtual-list/es/hooks/useHeights.js
@@ -6,7 +6,7 @@ function parseNumber(value) {
var num = parseFloat(value);
return isNaN(num) ? 0 : num;
}
-export default function useHeights(getKey, onItemAdd, onItemRemove) {
+export default function useHeights(getKey, onItemAdd, onItemRemove, disabled) {
var _React$useState = React.useState(0),
_React$useState2 = _slicedToArray(_React$useState, 2),
updatedMark = _React$useState2[0],
@@ -14,12 +14,17 @@ export default function useHeights(getKey, onItemAdd, onItemRemove) {
var instanceRef = useRef(new Map());
var heightsRef = useRef(new CacheMap());
var promiseIdRef = useRef(0);
+ var disabledRef = useRef(disabled);
+ disabledRef.current = disabled;
function cancelRaf() {
promiseIdRef.current += 1;
}
function collectHeight() {
var sync = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
cancelRaf();
+ if (disabledRef.current) {
+ return;
+ }
var doCollect = function doCollect() {
var changed = false;
instanceRef.current.forEach(function (element, key) {
@@ -79,5 +84,10 @@ export default function useHeights(getKey, onItemAdd, onItemRemove) {
useEffect(function () {
return cancelRaf;
}, []);
+ useEffect(function () {
+ if (!disabled) {
+ collectHeight(true);
+ }
+ }, [disabled]);
return [setInstanceRef, collectHeight, heightsRef.current, updatedMark];
}
diff --git a/node_modules/rc-virtual-list/lib/List.d.ts b/node_modules/rc-virtual-list/lib/List.d.ts
index c517366..e414513 100644
--- a/node_modules/rc-virtual-list/lib/List.d.ts
+++ b/node_modules/rc-virtual-list/lib/List.d.ts
@@ -20,6 +20,8 @@ export interface ListProps<T> extends Omit<React.HTMLAttributes<any>, 'children'
data: T[];
height?: number;
itemHeight?: number;
+ /** Treat every item as exactly itemHeight and calculate the visible range in O(1). */
+ itemHeightFixed?: boolean;
/** If not match virtual scroll condition, Set List still use height of container. */
fullHeight?: boolean;
itemKey: React.Key | ((item: T) => React.Key);
diff --git a/node_modules/rc-virtual-list/lib/List.js b/node_modules/rc-virtual-list/lib/List.js
index f0cb64d..d24a9ad 100644
--- a/node_modules/rc-virtual-list/lib/List.js
+++ b/node_modules/rc-virtual-list/lib/List.js
@@ -32,7 +32,7 @@ var _useScrollDrag = _interopRequireDefault(require("./hooks/useScrollDrag"));
var _useScrollTo = _interopRequireDefault(require("./hooks/useScrollTo"));
var _ScrollBar = _interopRequireDefault(require("./ScrollBar"));
var _scrollbarUtil = require("./utils/scrollbarUtil");
-var _excluded = ["prefixCls", "className", "height", "itemHeight", "fullHeight", "style", "data", "children", "itemKey", "virtual", "direction", "scrollWidth", "component", "onScroll", "onVirtualScroll", "onVisibleChange", "innerProps", "extraRender", "styles", "showScrollBar"];
+var _excluded = ["prefixCls", "className", "height", "itemHeight", "itemHeightFixed", "fullHeight", "style", "data", "children", "itemKey", "virtual", "direction", "scrollWidth", "component", "onScroll", "onVirtualScroll", "onVisibleChange", "innerProps", "extraRender", "styles", "showScrollBar"];
var EMPTY_DATA = [];
var ScrollStyle = {
overflowY: 'auto',
@@ -44,6 +44,7 @@ function RawList(props, ref) {
className = props.className,
height = props.height,
itemHeight = props.itemHeight,
+ itemHeightFixed = props.itemHeightFixed,
_props$fullHeight = props.fullHeight,
fullHeight = _props$fullHeight === void 0 ? true : _props$fullHeight,
style = props.style,
@@ -74,7 +75,7 @@ function RawList(props, ref) {
}, [itemKey]);
// ================================ Height ================================
- var _useHeights = (0, _useHeights3.default)(getKey, null, null),
+ var _useHeights = (0, _useHeights3.default)(getKey, null, null, itemHeightFixed),
_useHeights2 = (0, _slicedToArray2.default)(_useHeights, 4),
setInstanceRef = _useHeights2[0],
collectHeight = _useHeights2[1],
@@ -83,11 +84,12 @@ function RawList(props, ref) {
// ================================= MISC =================================
var useVirtual = !!(virtual !== false && height && itemHeight);
- var containerHeight = React.useMemo(function () {
+ var measuredContainerHeight = React.useMemo(function () {
return Object.values(heights.maps).reduce(function (total, curr) {
return total + curr;
}, 0);
}, [heights.id, heights.maps]);
+ var containerHeight = itemHeightFixed ? itemHeight * (data || EMPTY_DATA).length : measuredContainerHeight;
var inVirtual = useVirtual && data && (Math.max(itemHeight * data.length, containerHeight) > height || !!scrollWidth);
var isRTL = direction === 'rtl';
var mergedClassName = (0, _classnames.default)(prefixCls, (0, _defineProperty2.default)({}, "".concat(prefixCls, "-rtl"), isRTL), className);
@@ -168,6 +170,28 @@ function RawList(props, ref) {
offset: undefined
};
}
+ if (itemHeightFixed && itemHeight > 0) {
+ var fixedDataLen = mergedData.length;
+ var fixedScrollHeight = fixedDataLen * itemHeight;
+ if (fixedDataLen === 0) {
+ return {
+ scrollHeight: 0,
+ start: 0,
+ end: -1,
+ offset: 0
+ };
+ }
+ var fixedMaxScrollTop = Math.max(0, fixedScrollHeight - height);
+ var fixedOffsetTop = Math.max(0, Math.min(fixedMaxScrollTop, offsetTop));
+ var fixedStartIndex = Math.min(fixedDataLen - 1, Math.max(0, Math.ceil(fixedOffsetTop / itemHeight) - 1));
+ var fixedEndIndex = Math.min(fixedDataLen - 1, Math.floor((fixedOffsetTop + height) / itemHeight) + 1);
+ return {
+ scrollHeight: fixedScrollHeight,
+ start: fixedStartIndex,
+ end: fixedEndIndex,
+ offset: fixedStartIndex * itemHeight
+ };
+ }
var itemTop = 0;
var startIndex;
var startOffset;
@@ -210,7 +234,7 @@ function RawList(props, ref) {
end: endIndex,
offset: startOffset
};
- }, [inVirtual, useVirtual, offsetTop, mergedData, heightUpdatedMark, height]),
+ }, [inVirtual, useVirtual, offsetTop, mergedData, heightUpdatedMark, height, itemHeightFixed, itemHeight]),
scrollHeight = _React$useMemo.scrollHeight,
start = _React$useMemo.start,
end = _React$useMemo.end,
@@ -222,6 +246,10 @@ function RawList(props, ref) {
// Which will make scroll jump.
// Let's sync scroll top to avoid jump
React.useLayoutEffect(function () {
+ if (itemHeightFixed) {
+ heights.resetRecord();
+ return;
+ }
var changedRecord = heights.getRecord();
if (changedRecord.size === 1) {
var recordKey = Array.from(changedRecord.keys())[0];
@@ -241,7 +269,7 @@ function RawList(props, ref) {
}
}
heights.resetRecord();
- }, [scrollHeight]);
+ }, [scrollHeight, itemHeightFixed, heightUpdatedMark]);
// ================================= Size =================================
var _React$useState = React.useState({
diff --git a/node_modules/rc-virtual-list/lib/hooks/useHeights.d.ts b/node_modules/rc-virtual-list/lib/hooks/useHeights.d.ts
--- a/node_modules/rc-virtual-list/lib/hooks/useHeights.d.ts
+++ b/node_modules/rc-virtual-list/lib/hooks/useHeights.d.ts
@@ -1,6 +1,6 @@
import type { GetKey } from '../interface';
import CacheMap from '../utils/CacheMap';
-export default function useHeights<T>(getKey: GetKey<T>, onItemAdd?: (item: T) => void, onItemRemove?: (item: T) => void): [
+export default function useHeights<T>(getKey: GetKey<T>, onItemAdd?: (item: T) => void, onItemRemove?: (item: T) => void, disabled?: boolean): [
setInstanceRef: (item: T, instance: HTMLElement) => void,
collectHeight: (sync?: boolean) => void,
cacheMap: CacheMap,
diff --git a/node_modules/rc-virtual-list/lib/hooks/useHeights.js b/node_modules/rc-virtual-list/lib/hooks/useHeights.js
index 471ffc6..a6d2ce5 100644
--- a/node_modules/rc-virtual-list/lib/hooks/useHeights.js
+++ b/node_modules/rc-virtual-list/lib/hooks/useHeights.js
@@ -14,7 +14,7 @@ function parseNumber(value) {
var num = parseFloat(value);
return isNaN(num) ? 0 : num;
}
-function useHeights(getKey, onItemAdd, onItemRemove) {
+function useHeights(getKey, onItemAdd, onItemRemove, disabled) {
var _React$useState = React.useState(0),
_React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2),
updatedMark = _React$useState2[0],
@@ -22,12 +22,17 @@ function useHeights(getKey, onItemAdd, onItemRemove) {
var instanceRef = (0, _react.useRef)(new Map());
var heightsRef = (0, _react.useRef)(new _CacheMap.default());
var promiseIdRef = (0, _react.useRef)(0);
+ var disabledRef = (0, _react.useRef)(disabled);
+ disabledRef.current = disabled;
function cancelRaf() {
promiseIdRef.current += 1;
}
function collectHeight() {
var sync = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
cancelRaf();
+ if (disabledRef.current) {
+ return;
+ }
var doCollect = function doCollect() {
var changed = false;
instanceRef.current.forEach(function (element, key) {
@@ -87,5 +92,10 @@ function useHeights(getKey, onItemAdd, onItemRemove) {
(0, _react.useEffect)(function () {
return cancelRaf;
}, []);
+ (0, _react.useEffect)(function () {
+ if (!disabled) {
+ collectHeight(true);
+ }
+ }, [disabled]);
return [setInstanceRef, collectHeight, heightsRef.current, updatedMark];
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Some files were not shown because too many files have changed in this diff Show More