Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
0d971d9
feat(i18n): 迁移快捷键设置页 (#97)
qianmoQ Jun 15, 2026
7360b48
feat(i18n): 迁移缓存设置页 (#97)
qianmoQ Jun 15, 2026
b63dc92
feat(i18n): 迁移日志设置页 (#97)
qianmoQ Jun 15, 2026
fc40c81
feat(i18n): 迁移顶部工具栏 AppHeader (#97)
qianmoQ Jun 16, 2026
259a322
feat(i18n): 迁移底部状态栏 (#97)
qianmoQ Jun 16, 2026
3e3432b
feat(i18n): 迁移 Git 面板 (#97)
qianmoQ Jun 16, 2026
5078e86
feat(i18n): 迁移跳转到行与快速打开对话框 (#97)
qianmoQ Jun 16, 2026
be431ea
feat(i18n): 迁移搜索/替换面板 (#97)
qianmoQ Jun 16, 2026
af7985b
feat(i18n): 迁移符号大纲对话框 (#97)
qianmoQ Jun 16, 2026
f4449fb
feat(i18n): 迁移 AI 对话面板 (#97)
qianmoQ Jun 16, 2026
24ee29d
feat(i18n): 语言包改为 JSON 初始化 + 数据库存储,新增「语言包」设置页 (#97)
qianmoQ Jun 16, 2026
b4bf7cb
fix(i18n): 语言包合并改为数据库优先、JSON 填充缺失键
qianmoQ Jun 16, 2026
ba117c7
fix(settings): 设置 Tab 导航超宽时改为横向滚动
qianmoQ Jun 16, 2026
8e7276d
style(i18n): 语言包设置页铺满宽度,去掉右侧留白
qianmoQ Jun 16, 2026
39cfac8
feat(i18n): 迁移命令面板与编辑器标签 (#97)
qianmoQ Jun 16, 2026
df98f8d
feat(i18n): 迁移命令面板命令名 (#97)
qianmoQ Jun 16, 2026
30a5ffc
feat(i18n): 迁移快捷键动作名 (#97)
qianmoQ Jun 16, 2026
52c09fd
feat(i18n): 迁移侧栏文件树 (#97)
qianmoQ Jun 16, 2026
cc813d8
feat(i18n): 迁移控制台输出面板 (#97)
qianmoQ Jun 16, 2026
0122d41
feat(i18n): 迁移各输出视图 (#97)
qianmoQ Jun 16, 2026
0baf5b9
refactor(db): 数据库连接抽取为独立表 db_connections,自动迁移旧 KV 数据
qianmoQ Jun 16, 2026
a104bb8
feat(i18n): 迁移执行历史/SQL视图/关于/更新/终端等组件 (#97)
qianmoQ Jun 16, 2026
7fd782e
feat(i18n): 迁移设置页(数据库/语言服务/语言/环境)与代码片段 (#97)
qianmoQ Jun 16, 2026
e9f13d1
feat(i18n): 迁移图表面板与图表组件 (#97)
qianmoQ Jun 16, 2026
5e75b1f
feat(i18n): 迁移主界面 App.vue (#97)
qianmoQ Jun 16, 2026
af7ba16
feat(i18n): 补充设置页通用/网络的 toast 文案 (#97)
qianmoQ Jun 16, 2026
eff2993
feat(i18n): 迁移各 composable 的运行/文件/配置/日志提示 (#97)
qianmoQ Jun 17, 2026
f14cde3
feat(i18n): 迁移基础 UI 组件与 JSON/Xlsx 残留文案 (#97)
qianmoQ Jun 17, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pnpm-lock.yaml
*.json
!config*.json
!src/components/charts/geo/*.json
!src/i18n/locales/*.json
go.mod
.RData
.Rhistory
Expand Down
143 changes: 143 additions & 0 deletions src-tauri/src/db_connections.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
//! 数据库连接的独立存储表(codeforge.sqlite 中的 db_connections)。
//! 从 KV 的 sql-connections JSON blob 抽出,便于将来按需扩展/分页/检索。

use crate::execution::get_codeforge_db_path;
use rusqlite::{Connection, OptionalExtension, params};
use serde::{Deserialize, Serialize};
use std::sync::Mutex as StdMutex;
use tauri::State;

#[derive(Serialize, Deserialize, Clone)]
pub struct DbConnection {
pub id: String,
pub name: String,
pub kind: String,
#[serde(default)]
pub file: Option<String>,
#[serde(default)]
pub host: Option<String>,
#[serde(default)]
pub port: Option<u16>,
#[serde(default)]
pub user: Option<String>,
#[serde(default)]
pub password: Option<String>,
#[serde(default)]
pub database: Option<String>,
}

pub struct DbConnStore {
conn: StdMutex<Connection>,
}

impl DbConnStore {
pub fn new() -> Result<Self, String> {
let db_path = get_codeforge_db_path()?;
let conn = Connection::open(&db_path).map_err(|e| format!("打开数据库失败: {}", e))?;
let _ = conn.pragma_update(None, "journal_mode", "WAL");
let _ = conn.pragma_update(None, "synchronous", "NORMAL");
let _ = conn.busy_timeout(std::time::Duration::from_secs(5));
conn.execute(
"CREATE TABLE IF NOT EXISTS db_connections (
id TEXT PRIMARY KEY,
name TEXT NOT NULL,
kind TEXT NOT NULL,
file TEXT,
host TEXT,
port INTEGER,
user TEXT,
password TEXT,
database TEXT,
sort_order INTEGER NOT NULL DEFAULT 0
)",
[],
)
.map_err(|e| format!("初始化连接表失败: {}", e))?;
Ok(Self {
conn: StdMutex::new(conn),
})
}
}

/// 列出全部连接(按 sort_order, name)
#[tauri::command]
pub async fn db_connections_list(
state: State<'_, DbConnStore>,
) -> Result<Vec<DbConnection>, String> {
let conn = state.conn.lock().map_err(|_| "数据库锁错误".to_string())?;
let mut stmt = conn
.prepare(
"SELECT id, name, kind, file, host, port, user, password, database
FROM db_connections ORDER BY sort_order, name",
)
.map_err(|e| format!("查询连接失败: {}", e))?;
let rows = stmt
.query_map([], |row| {
Ok(DbConnection {
id: row.get(0)?,
name: row.get(1)?,
kind: row.get(2)?,
file: row.get(3)?,
host: row.get(4)?,
port: row.get::<_, Option<i64>>(5)?.map(|v| v as u16),
user: row.get(6)?,
password: row.get(7)?,
database: row.get(8)?,
})
})
.map_err(|e| format!("读取连接失败: {}", e))?;
let mut out = Vec::new();
for r in rows {
out.push(r.map_err(|e| format!("读取连接失败: {}", e))?);
}
Ok(out)
}

/// 新增或更新一个连接(按 id upsert,更新时保留原有排序)
#[tauri::command]
pub async fn db_connection_save(
c: DbConnection,
state: State<'_, DbConnStore>,
) -> Result<(), String> {
let conn = state.conn.lock().map_err(|_| "数据库锁错误".to_string())?;
let exists = conn
.query_row(
"SELECT 1 FROM db_connections WHERE id = ?1",
params![c.id],
|_| Ok(()),
)
.optional()
.map_err(|e| format!("查询连接失败: {}", e))?
.is_some();
if exists {
conn.execute(
"UPDATE db_connections SET name=?2, kind=?3, file=?4, host=?5, port=?6, user=?7, password=?8, database=?9 WHERE id=?1",
params![c.id, c.name, c.kind, c.file, c.host, c.port, c.user, c.password, c.database],
)
.map_err(|e| format!("更新连接失败: {}", e))?;
} else {
let next: i64 = conn
.query_row(
"SELECT COALESCE(MAX(sort_order), 0) + 1 FROM db_connections",
[],
|r| r.get(0),
)
.unwrap_or(1);
conn.execute(
"INSERT INTO db_connections (id, name, kind, file, host, port, user, password, database, sort_order)
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10)",
params![c.id, c.name, c.kind, c.file, c.host, c.port, c.user, c.password, c.database, next],
)
.map_err(|e| format!("保存连接失败: {}", e))?;
}
Ok(())
}

/// 删除一个连接
#[tauri::command]
pub async fn db_connection_delete(id: String, state: State<'_, DbConnStore>) -> Result<(), String> {
let conn = state.conn.lock().map_err(|_| "数据库锁错误".to_string())?;
conn.execute("DELETE FROM db_connections WHERE id = ?1", params![id])
.map_err(|e| format!("删除连接失败: {}", e))?;
Ok(())
}
9 changes: 9 additions & 0 deletions src-tauri/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ mod cache;
mod config;
mod custom_plugin_commands;
mod db;
mod db_connections;
mod env_commands;
mod env_manager;
mod env_providers;
Expand Down Expand Up @@ -38,6 +39,9 @@ use crate::custom_plugin_commands::{
update_custom_plugin,
};
use crate::db::{run_sql, run_sql_paged};
use crate::db_connections::{
DbConnStore, db_connection_delete, db_connection_save, db_connections_list,
};
use crate::env_commands::{
EnvironmentManagerState, download_and_install_version, get_environment_info,
get_supported_environment_languages, switch_environment_version, uninstall_environment_version,
Expand Down Expand Up @@ -100,6 +104,7 @@ fn main() {
.manage(AiHistory::new().expect("failed to initialize ai history database"))
.manage(Snippets::new().expect("failed to initialize snippets database"))
.manage(KvStore::new().expect("failed to initialize kv store database"))
.manage(DbConnStore::new().expect("failed to initialize db connections database"))
.manage(TerminalState::new())
.manage(LspState::new())
.manage(ExecutionPluginManagerState::new(PluginManager::new()))
Expand Down Expand Up @@ -225,6 +230,10 @@ fn main() {
kv_get_all,
kv_set,
kv_delete,
// 数据库连接(独立表)
db_connections_list,
db_connection_save,
db_connection_delete,
// 集成终端
terminal_create,
terminal_write,
Expand Down
Loading
Loading