- Introduced WSL editor launch modes in SettingsPage with options for internal, remote, and UNC path launches. - Enhanced settings saving to include the new WSL launch mode. - Added an update section to check for application updates, download, and install them with progress tracking. - Updated Toast component to handle error messages with a copy button for convenience. - Expanded commands to include project tags and templates management, along with WSL proxy synchronization features.
192 lines
6.2 KiB
Rust
192 lines
6.2 KiB
Rust
use crate::db::pool;
|
||
use crate::models::GitRepo;
|
||
use rusqlite::params;
|
||
use serde::Deserialize;
|
||
|
||
// ── 输入结构体 ─────────────────────────────────────────────────────────────────
|
||
|
||
#[derive(Debug, Deserialize)]
|
||
pub struct ImportGitRepoInput {
|
||
pub id: String,
|
||
pub repo_url: String,
|
||
pub name: String,
|
||
pub description: Option<String>,
|
||
pub default_branch: Option<String>,
|
||
pub upstream_url: Option<String>,
|
||
pub tech_stack: Option<String>,
|
||
}
|
||
|
||
#[derive(Debug, Deserialize)]
|
||
pub struct UpdateGitRepoDeployInput {
|
||
pub staging_url: Option<String>,
|
||
pub prod_url: Option<String>,
|
||
pub server_note: Option<String>,
|
||
pub upstream_url: Option<String>,
|
||
pub tech_stack: Option<String>,
|
||
pub status: Option<String>,
|
||
pub priority: Option<String>,
|
||
}
|
||
|
||
#[derive(Debug, Deserialize)]
|
||
pub struct MountRepoInput {
|
||
/// 新 workspace 的 id(前端传 UUID)
|
||
pub workspace_id: String,
|
||
pub repo_id: String,
|
||
pub space_id: Option<String>,
|
||
pub win_path: Option<String>,
|
||
pub wsl_path: Option<String>,
|
||
pub platform: Option<String>,
|
||
}
|
||
|
||
// ── 命令实现 ───────────────────────────────────────────────────────────────────
|
||
|
||
/// 获取所有已注册仓库
|
||
#[tauri::command]
|
||
pub fn get_git_repos() -> Result<Vec<GitRepo>, String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
let mut stmt = conn
|
||
.prepare("SELECT * FROM git_repos ORDER BY created_at DESC")
|
||
.map_err(|e| e.to_string())?;
|
||
let rows = stmt
|
||
.query_map([], GitRepo::from_row)
|
||
.map_err(|e| e.to_string())?
|
||
.collect::<rusqlite::Result<Vec<_>>>()
|
||
.map_err(|e| e.to_string())?;
|
||
Ok(rows)
|
||
}
|
||
|
||
/// 批量导入 GitHub 仓库(INSERT OR IGNORE,幂等)
|
||
#[tauri::command]
|
||
pub fn import_git_repos(repos: Vec<ImportGitRepoInput>) -> Result<Vec<String>, String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
let mut imported = Vec::new();
|
||
for r in repos {
|
||
let affected = conn
|
||
.execute(
|
||
"INSERT OR IGNORE INTO git_repos
|
||
(id, repo_url, name, description, default_branch, upstream_url, tech_stack,
|
||
last_synced_at)
|
||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, datetime('now'))",
|
||
params![
|
||
r.id,
|
||
r.repo_url,
|
||
r.name,
|
||
r.description,
|
||
r.default_branch,
|
||
r.upstream_url,
|
||
r.tech_stack,
|
||
],
|
||
)
|
||
.map_err(|e| e.to_string())?;
|
||
if affected > 0 {
|
||
imported.push(r.id);
|
||
}
|
||
}
|
||
Ok(imported)
|
||
}
|
||
|
||
/// 更新仓库部署信息(staging_url, prod_url, server_note, upstream_url 等)
|
||
#[tauri::command]
|
||
pub fn update_git_repo_deploy(id: String, input: UpdateGitRepoDeployInput) -> Result<(), String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
conn.execute(
|
||
"UPDATE git_repos SET
|
||
staging_url = COALESCE(?2, staging_url),
|
||
prod_url = COALESCE(?3, prod_url),
|
||
server_note = COALESCE(?4, server_note),
|
||
upstream_url = COALESCE(?5, upstream_url),
|
||
tech_stack = COALESCE(?6, tech_stack),
|
||
status = COALESCE(?7, status),
|
||
priority = COALESCE(?8, priority)
|
||
WHERE id = ?1",
|
||
params![
|
||
id,
|
||
input.staging_url,
|
||
input.prod_url,
|
||
input.server_note,
|
||
input.upstream_url,
|
||
input.tech_stack,
|
||
input.status,
|
||
input.priority,
|
||
],
|
||
)
|
||
.map_err(|e| e.to_string())?;
|
||
Ok(())
|
||
}
|
||
|
||
/// 将仓库挂载到某个空间(创建 workspace + profile)
|
||
#[tauri::command]
|
||
pub fn mount_repo(input: MountRepoInput) -> Result<(), String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
|
||
// 读取 git_repo 基础信息
|
||
let repo: GitRepo = conn
|
||
.query_row(
|
||
"SELECT * FROM git_repos WHERE id = ?1",
|
||
params![input.repo_id],
|
||
GitRepo::from_row,
|
||
)
|
||
.map_err(|e| format!("仓库不存在: {e}"))?;
|
||
|
||
let profile_id = format!("prof-{}", input.workspace_id);
|
||
|
||
// 创建 project_profile(若已存在则忽略)
|
||
conn.execute(
|
||
"INSERT OR IGNORE INTO project_profiles
|
||
(id, name, description, repo_url, upstream_url, default_branch,
|
||
staging_url, prod_url, server_note, status, priority, tech_stack)
|
||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8, ?9, ?10, ?11, ?12)",
|
||
params![
|
||
profile_id,
|
||
repo.name,
|
||
repo.description,
|
||
repo.repo_url,
|
||
repo.upstream_url,
|
||
repo.default_branch,
|
||
repo.staging_url,
|
||
repo.prod_url,
|
||
repo.server_note,
|
||
repo.status.as_deref().unwrap_or("active"),
|
||
repo.priority.as_deref().unwrap_or("mid"),
|
||
repo.tech_stack,
|
||
],
|
||
)
|
||
.map_err(|e| e.to_string())?;
|
||
|
||
let platform = input.platform.as_deref().unwrap_or("windows");
|
||
|
||
// 创建 project_workspace(若同一 space 已挂载则报错)
|
||
conn.execute(
|
||
"INSERT INTO project_workspaces
|
||
(id, profile_id, repo_id, space_id, win_path, wsl_path, platform)
|
||
VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)",
|
||
params![
|
||
input.workspace_id,
|
||
profile_id,
|
||
input.repo_id,
|
||
input.space_id,
|
||
input.win_path,
|
||
input.wsl_path,
|
||
platform,
|
||
],
|
||
)
|
||
.map_err(|e| {
|
||
if e.to_string().contains("UNIQUE") {
|
||
"该仓库已在此空间中挂载".to_string()
|
||
} else {
|
||
e.to_string()
|
||
}
|
||
})?;
|
||
|
||
Ok(())
|
||
}
|
||
|
||
/// 删除仓库注册记录(同时删除关联的 workspace/profile)
|
||
#[tauri::command]
|
||
pub fn delete_git_repo(id: String) -> Result<(), String> {
|
||
let conn = pool().get().map_err(|e| e.to_string())?;
|
||
conn.execute("DELETE FROM git_repos WHERE id = ?1", params![id])
|
||
.map_err(|e| e.to_string())?;
|
||
Ok(())
|
||
}
|