diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000000..950cd93d55 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,170 @@ +name: 问题反馈 +description: 程序崩溃、连接失败、查询异常、界面显示错误或其他可复现问题。 +title: "[Bug] " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + 感谢反馈。请尽量提供可复现的信息,尤其是数据库类型、版本、连接方式和错误日志。涉及敏感信息时请脱敏。 + + - type: checkboxes + id: checklist + attributes: + label: 提交前确认 + options: + - label: 我已搜索现有 issues,确认没有重复反馈。 + required: true + - label: 我已尝试使用最新版本或当前 main 分支复现。 + required: false + - label: 我提供的信息中不包含真实密码、Token、私钥或生产数据。 + required: true + + - type: dropdown + id: area + attributes: + label: 问题模块 + description: 选择最接近的功能区域。 + options: + - 连接管理 + - SSH 隧道 + - 结构浏览 + - SQL 查询编辑器 + - 数据表格 / 行内编辑 + - 数据导出 + - Redis 浏览器 + - MongoDB 浏览器 + - 查询历史 + - AI 助手 + - 主题 / 多语言 / 界面 + - 安装 / 启动 / 打包 + - 其他 + validations: + required: true + + - type: dropdown + id: database + attributes: + label: 数据库类型 + description: 如果与数据库无关,请选择“不适用”。 + multiple: true + options: + - MySQL + - PostgreSQL + - SQLite + - Redis + - MongoDB + - DuckDB + - ClickHouse + - SQL Server + - 不适用 + - 其他 + validations: + required: true + + - type: input + id: database-version + attributes: + label: 数据库版本 + description: 例如 MySQL 8.0.36、PostgreSQL 16、Redis 7.2、MongoDB 7.0。 + placeholder: "例如:PostgreSQL 16.2" + + - type: dropdown + id: connection-method + attributes: + label: 连接方式 + options: + - 直连 + - SSH 隧道 + - 本地文件 / SQLite + - 不适用 + - 其他 + validations: + required: true + + - type: textarea + id: description + attributes: + label: 问题描述 + description: 简洁说明实际发生了什么,以及它为什么不符合预期。 + placeholder: "例如:连接 PostgreSQL 后展开 schema 时一直 loading,控制台报错 ..." + validations: + required: true + + - type: textarea + id: steps + attributes: + label: 复现步骤 + description: 请写出从启动 DBX 到出现问题的最短路径。 + placeholder: | + 1. 打开 DBX + 2. 新建 PostgreSQL 连接,填写 ... + 3. 点击测试连接 / 保存 / 展开表 + 4. 出现 ... + validations: + required: true + + - type: textarea + id: expected + attributes: + label: 期望行为 + placeholder: "例如:应该正常展开 public schema 并显示表列表。" + validations: + required: true + + - type: textarea + id: actual + attributes: + label: 实际行为 + placeholder: "例如:界面无响应 / 弹出错误 / 数据显示为空 / 应用崩溃。" + validations: + required: true + + - type: textarea + id: sample + attributes: + label: 最小复现 SQL / 数据结构 + description: 如果问题和查询、表结构、Redis key、MongoDB document 有关,请提供脱敏后的最小样例。 + render: sql + placeholder: | + CREATE TABLE demo (...); + SELECT ...; + + - type: textarea + id: logs + attributes: + label: 错误日志 / 截图 + description: 可粘贴终端、DevTools、Tauri/Rust 日志,或直接拖入截图。 + render: shell + placeholder: "请粘贴错误堆栈、控制台输出或截图。" + + - type: input + id: app-version + attributes: + label: DBX 版本 / Commit + description: Release 版本号、安装包版本或 git commit。 + placeholder: "例如:v0.1.0 / main@abcdef1" + validations: + required: true + + - type: dropdown + id: os + attributes: + label: 操作系统 + multiple: true + options: + - macOS Apple Silicon + - macOS Intel + - Windows x64 + - Windows ARM64 + - Linux x64 + - Linux ARM64 + - 其他 + validations: + required: true + + - type: textarea + id: extra + attributes: + label: 补充信息 + description: 例如是否使用代理、VPN、Docker 数据库、远程云数据库、特殊字符账号或非默认端口。 diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000000..f99d700f21 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,5 @@ +blank_issues_enabled: false +contact_links: + - name: 文档 / 使用说明 + url: https://github.com/t8y2/dbx#readme + about: 提交 issue 前可先查看 README 中的安装、开发和 macOS 首次启动说明。 diff --git a/.github/ISSUE_TEMPLATE/database_compatibility.yml b/.github/ISSUE_TEMPLATE/database_compatibility.yml new file mode 100644 index 0000000000..e1d7cdc8c5 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/database_compatibility.yml @@ -0,0 +1,128 @@ +name: 数据库适配 / 兼容性问题 +description: 某个数据库、版本、驱动行为、SQL 方言或元数据读取不兼容。 +title: "[Compatibility] " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + 这个模板用于收集数据库兼容性细节,例如某个版本连接失败、schema 读取不完整、字段类型显示错误、SQL 方言不支持等。请勿提交真实连接凭据。 + + - type: checkboxes + id: checklist + attributes: + label: 提交前确认 + options: + - label: 我已确认连接信息、网络、账号权限本身可用。 + required: true + - label: 我已隐藏真实主机、用户名、密码、Token 和业务数据。 + required: true + + - type: dropdown + id: database + attributes: + label: 数据库类型 + options: + - MySQL + - PostgreSQL + - SQLite + - Redis + - MongoDB + - DuckDB + - ClickHouse + - SQL Server + - 其他 + validations: + required: true + + - type: input + id: database-version + attributes: + label: 数据库版本 / 发行版 + description: 例如 MariaDB、AWS RDS、Azure SQL、TiDB、兼容 PostgreSQL 的云数据库等也请注明。 + placeholder: "例如:MariaDB 10.11 / AWS RDS PostgreSQL 15.5" + validations: + required: true + + - type: dropdown + id: scenario + attributes: + label: 问题场景 + options: + - 测试连接失败 + - 保存连接失败 + - SSH 隧道连接失败 + - 展开数据库 / schema / 表失败 + - 字段 / 索引 / 外键 / 触发器读取错误 + - 查询执行错误 + - 查询结果显示错误 + - 行内编辑 / 保存错误 + - Redis key 浏览 / value 显示错误 + - MongoDB document 浏览 / CRUD 错误 + - 其他 + validations: + required: true + + - type: textarea + id: schema + attributes: + label: 最小表结构 / 数据样例 + description: 请提供能触发问题的最小结构或数据,务必脱敏。 + render: sql + placeholder: | + CREATE TABLE ... + INSERT INTO ... + + - type: textarea + id: query + attributes: + label: 触发问题的查询 / 操作 + description: 如果和 SQL、Redis 命令、MongoDB 查询有关,请贴出最小复现内容。 + render: sql + + - type: textarea + id: error + attributes: + label: 错误信息 + description: 粘贴 DBX 弹窗、终端、DevTools 或后端日志里的错误。 + render: shell + validations: + required: true + + - type: textarea + id: expected + attributes: + label: 正确行为 + description: 说明同样操作在数据库 CLI、其他客户端或预期中应该得到什么结果。 + validations: + required: true + + - type: input + id: app-version + attributes: + label: DBX 版本 / Commit + placeholder: "例如:v0.1.0 / main@abcdef1" + validations: + required: true + + - type: dropdown + id: os + attributes: + label: 操作系统 + multiple: true + options: + - macOS Apple Silicon + - macOS Intel + - Windows x64 + - Windows ARM64 + - Linux x64 + - Linux ARM64 + - 其他 + validations: + required: true + + - type: textarea + id: extra + attributes: + label: 补充信息 + description: 例如数据库部署方式、Docker 镜像、字符集、时区、SSL、代理、VPN、云厂商兼容层等。 diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000000..ebe8cb38bf --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,113 @@ +name: 功能建议 +description: 提议新功能、交互改进、性能优化或现有功能增强。 +title: "[Feature] " +labels: ["enhancement"] +body: + - type: markdown + attributes: + value: | + 好的功能建议最好说明使用场景、当前痛点和期望工作流。请尽量避免只写“支持 X”,多给一点上下文会更容易评估和实现。 + + - type: checkboxes + id: checklist + attributes: + label: 提交前确认 + options: + - label: 我已搜索现有 issues,确认没有重复建议。 + required: true + - label: 这个建议与 DBX 的数据库管理、查询、浏览、导出或桌面体验有关。 + required: true + + - type: dropdown + id: category + attributes: + label: 建议类型 + options: + - 新数据库支持 + - 连接管理 + - SSH 隧道 + - 结构浏览 + - SQL 编辑器 / 自动补全 + - 数据表格 / 编辑体验 + - 数据导入导出 + - Redis 功能 + - MongoDB 功能 + - 查询历史 + - AI 助手 + - 主题 / 多语言 / 界面 + - 性能优化 + - 安装 / 打包 / 更新 + - 其他 + validations: + required: true + + - type: dropdown + id: database + attributes: + label: 相关数据库 + multiple: true + options: + - MySQL + - PostgreSQL + - SQLite + - Redis + - MongoDB + - DuckDB + - ClickHouse + - SQL Server + - 不限数据库 + - 其他 + validations: + required: true + + - type: textarea + id: problem + attributes: + label: 当前痛点 + description: 现在做这件事有什么不方便、不能做或容易出错? + placeholder: "例如:每次查询后都要手动复制结果再转成 Markdown,流程比较繁琐。" + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: 期望方案 + description: 描述你希望 DBX 如何支持这个功能。 + placeholder: "例如:在查询结果右上角增加导出 Markdown 按钮,可选择是否包含表头。" + validations: + required: true + + - type: textarea + id: workflow + attributes: + label: 期望工作流 + description: 如果能描述用户操作路径,会更容易落地成产品设计。 + placeholder: | + 1. 打开连接 + 2. 执行查询 + 3. 点击 ... + 4. 得到 ... + + - type: textarea + id: alternatives + attributes: + label: 你试过的替代方案 + description: 例如目前如何绕过、是否使用其他工具完成、有什么缺点。 + + - type: textarea + id: references + attributes: + label: 参考截图 / 产品 / 文档 + description: 可以拖入截图,或贴出类似功能的链接。 + + - type: dropdown + id: priority + attributes: + label: 需求紧急程度 + options: + - 只是想法,不急 + - 会明显提升日常使用体验 + - 当前工作流被阻塞 + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/question.yml b/.github/ISSUE_TEMPLATE/question.yml new file mode 100644 index 0000000000..9eab72632d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/question.yml @@ -0,0 +1,69 @@ +name: 问题咨询 / 讨论 +description: 使用疑问、配置问题、设计讨论或不确定是否为 bug 的反馈。 +title: "[Question] " +labels: ["question"] +body: + - type: markdown + attributes: + value: | + 如果你不确定是 bug 还是使用问题,可以先从这里提交。请尽量说明你的目标、当前做法和卡住的位置。 + + - type: checkboxes + id: checklist + attributes: + label: 提交前确认 + options: + - label: 我已查看 README 和现有 issues。 + required: true + - label: 我不会在 issue 中提交真实密码、Token、私钥或生产数据。 + required: true + + - type: dropdown + id: topic + attributes: + label: 主题 + options: + - 安装 / 启动 + - 数据库连接 + - SSH 隧道 + - 查询 / SQL + - 数据浏览 / 编辑 + - Redis + - MongoDB + - 导入导出 + - AI 助手 + - 开发 / 构建 + - 其他 + validations: + required: true + + - type: textarea + id: goal + attributes: + label: 你想完成什么? + placeholder: "例如:我想通过 SSH 隧道连接内网 PostgreSQL,但不确定应该怎么填写配置。" + validations: + required: true + + - type: textarea + id: tried + attributes: + label: 你已经尝试了什么? + placeholder: "例如:已确认本机 psql 可以连接;DBX 中测试连接提示 ..." + + - type: textarea + id: context + attributes: + label: 相关环境 + description: 如果和数据库或系统有关,请提供数据库类型、版本、操作系统、DBX 版本等。 + placeholder: | + DBX: + 操作系统: + 数据库类型和版本: + 连接方式: + + - type: textarea + id: screenshots + attributes: + label: 截图 / 日志 + description: 可以拖入截图,或粘贴脱敏后的日志。 diff --git a/.gitignore b/.gitignore index d02376822b..a64365db7d 100644 --- a/.gitignore +++ b/.gitignore @@ -19,6 +19,7 @@ Thumbs.db # GitHub (keep workflows) .github/* !.github/workflows/ +!.github/ISSUE_TEMPLATE/ # Env .env diff --git a/src-tauri/src/commands/connection.rs b/src-tauri/src/commands/connection.rs index 452f31075e..8ea9ca444a 100644 --- a/src-tauri/src/commands/connection.rs +++ b/src-tauri/src/commands/connection.rs @@ -209,7 +209,7 @@ pub async fn connect_db( let url = if config.ssh_enabled && !config.ssh_host.is_empty() { let local_port = state.tunnels.start_tunnel( &id, &config.ssh_host, config.ssh_port, - &config.ssh_user, &config.ssh_key_path, + &config.ssh_user, &config.ssh_password, &config.ssh_key_path, &config.host, config.port, ).await?; config.connection_url_with_host("127.0.0.1", local_port) diff --git a/src-tauri/src/db/ssh_tunnel.rs b/src-tauri/src/db/ssh_tunnel.rs index e10270acb8..c4537d8474 100644 --- a/src-tauri/src/db/ssh_tunnel.rs +++ b/src-tauri/src/db/ssh_tunnel.rs @@ -19,13 +19,14 @@ impl TunnelManager { ssh_host: &str, ssh_port: u16, ssh_user: &str, + ssh_password: &str, ssh_key_path: &str, remote_host: &str, remote_port: u16, ) -> Result { let local_port = portpicker::pick_unused_port().ok_or("No available port")?; - let mut args = vec![ + let mut ssh_args = vec![ "-N".to_string(), "-o".to_string(), "StrictHostKeyChecking=no".to_string(), "-o".to_string(), "ServerAliveInterval=60".to_string(), @@ -34,20 +35,39 @@ impl TunnelManager { ]; if !ssh_key_path.is_empty() { - args.push("-i".to_string()); - args.push(ssh_key_path.to_string()); + ssh_args.push("-i".to_string()); + ssh_args.push(ssh_key_path.to_string()); } - args.push(format!("{ssh_user}@{ssh_host}")); + ssh_args.push(format!("{ssh_user}@{ssh_host}")); - let child = Command::new("ssh") - .args(&args) - .stdin(std::process::Stdio::null()) - .stdout(std::process::Stdio::null()) - .stderr(std::process::Stdio::piped()) - .kill_on_drop(true) - .spawn() - .map_err(|e| format!("Failed to start SSH tunnel: {e}"))?; + let child = if !ssh_password.is_empty() && ssh_key_path.is_empty() { + Command::new("sshpass") + .arg("-p").arg(ssh_password) + .arg("ssh") + .args(&ssh_args) + .stdin(std::process::Stdio::null()) + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::piped()) + .kill_on_drop(true) + .spawn() + .map_err(|e| { + if e.kind() == std::io::ErrorKind::NotFound { + "SSH password auth requires 'sshpass'. Install it: brew install sshpass (macOS) / apt install sshpass (Linux)".to_string() + } else { + format!("Failed to start SSH tunnel: {e}") + } + })? + } else { + Command::new("ssh") + .args(&ssh_args) + .stdin(std::process::Stdio::null()) + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::piped()) + .kill_on_drop(true) + .spawn() + .map_err(|e| format!("Failed to start SSH tunnel: {e}"))? + }; tokio::time::sleep(tokio::time::Duration::from_millis(1500)).await; @@ -64,5 +84,4 @@ impl TunnelManager { let _ = child.kill().await; } } - } diff --git a/src-tauri/src/models/connection.rs b/src-tauri/src/models/connection.rs index 59dd6cdc9e..0b0e7faf59 100644 --- a/src-tauri/src/models/connection.rs +++ b/src-tauri/src/models/connection.rs @@ -19,6 +19,8 @@ pub struct ConnectionConfig { #[serde(default)] pub ssh_user: String, #[serde(default)] + pub ssh_password: String, + #[serde(default)] pub ssh_key_path: String, } diff --git a/src/components/connection/ConnectionDialog.vue b/src/components/connection/ConnectionDialog.vue index 94fefb74a4..9871cdc38a 100644 --- a/src/components/connection/ConnectionDialog.vue +++ b/src/components/connection/ConnectionDialog.vue @@ -39,6 +39,7 @@ const defaultForm = (): Omit => ({ ssh_host: "", ssh_port: 22, ssh_user: "", + ssh_password: "", ssh_key_path: "", }); @@ -59,6 +60,7 @@ watch(() => props.editConfig, (config) => { ssh_host: config.ssh_host || "", ssh_port: config.ssh_port || 22, ssh_user: config.ssh_user || "", + ssh_password: config.ssh_password || "", ssh_key_path: config.ssh_key_path || "", }; } else { @@ -216,6 +218,10 @@ watch([() => editingId.value, () => open.value], () => { +
+ + +
diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts index 92c4ca73ff..91b1072fa2 100644 --- a/src/i18n/locales/en.ts +++ b/src/i18n/locales/en.ts @@ -36,6 +36,8 @@ export default { sshEnable: "Connect via SSH tunnel", sshHost: "SSH Host", sshUser: "SSH User", + sshPassword: "SSH Password", + sshPasswordPlaceholder: "Leave empty to use key", sshKeyPath: "Key Path", }, editor: { diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts index e9fb44b69f..a6a6cc0e3d 100644 --- a/src/i18n/locales/zh-CN.ts +++ b/src/i18n/locales/zh-CN.ts @@ -36,6 +36,8 @@ export default { sshEnable: "通过 SSH 隧道连接", sshHost: "SSH 主机", sshUser: "SSH 用户", + sshPassword: "SSH 密码", + sshPasswordPlaceholder: "留空则使用密钥", sshKeyPath: "密钥路径", }, editor: { diff --git a/src/types/database.ts b/src/types/database.ts index 294a6e78b2..14f3ebedcb 100644 --- a/src/types/database.ts +++ b/src/types/database.ts @@ -13,6 +13,7 @@ export interface ConnectionConfig { ssh_host?: string; ssh_port?: number; ssh_user?: string; + ssh_password?: string; ssh_key_path?: string; }