dev-manager-tauri/src-tauri/src/models.rs
lanrtop 289b558278 refactor(github): GitHub 全面退役——免登录 + 发布/导入/同步链路移除 + 凭据去 GitHub 化
D1 免登录改造:App 启动直接进主界面,删 LoginPage/github.rs/OAuth 全套命令、
   Sidebar 账户区与 GitHub OAuth 设置弹窗、健康检查 GitHub Token 项
D2 移除发布/导入/PR/CI 入口:删 PublishModal/ImportRepoModal、ProjectCard 的
   PR compare 链接与 Actions 状态灯、RepoRegistryModal 同步 tab、CicdModal runs tab、
   publish.rs 的 github_create_repo/git_push_to_github
D3 spacesSync 单机化:删 spacesSync/useSpacesSync/SpacesPage/DiscoveryPanel,
   拆除 App→Dashboard→ProjectCard 的 spacesJson 链
D4 git_ops 凭据链重写:ssh agent → git credential helper → gitea_instances token
   前缀匹配 → default,不再依赖 github_token

保留:server_software GitHub 镜像(Releases 下载加速)、gitea_migrate 迁移入口、
cicd.rs(待 Gitea Actions 改造卡);DB 表不删(R05 migration 兼容)

顺带:修复 servers.rs 两个存量测试的 schema 漂移(测试建表缺 deploy_type 列);
      包含会话前未提交的 agent-infra F3(get_agent_health 健康诊断,与 lib.rs/
      commands.ts 物理耦合无法拆分提交)

验证:cargo test 53 passed / typecheck 全绿 / vitest passWithNoTests
2026-07-02 13:15:56 +09:00

352 lines
11 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use rusqlite::Row;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Project {
/// workspace.id也是旧 projects.id向后兼容所有关联表
pub id: String,
/// profile.id用于跨空间共享更新
pub profile_id: Option<String>,
/// 等于 id前端可用于区分语义
pub workspace_id: Option<String>,
/// 关联的 git_repos.id仓库锚点
pub repo_id: Option<String>,
pub name: String,
pub description: Option<String>,
#[serde(rename = "type")]
pub type_: Option<String>,
// ── profile 级(项目档案,跨空间共享)────────────────────────
pub status: Option<String>,
pub priority: Option<String>,
pub tech_stack: Option<String>,
pub tags: Option<String>,
pub repo_url: Option<String>,
pub upstream_url: Option<String>,
pub default_branch: Option<String>,
pub staging_url: Option<String>,
pub prod_url: Option<String>,
pub server_note: Option<String>,
// ── workspace 级(每空间独立)───────────────────────────────
pub space_id: Option<String>,
pub wsl_path: Option<String>,
pub win_path: Option<String>,
pub platform: Option<String>,
pub recent_focus: Option<String>,
pub startup_commands: Option<String>,
pub last_push_at: Option<String>,
pub updated_at: Option<String>,
}
impl Project {
/// 从 project_workspaces JOIN project_profiles 的查询结果构造
pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
Ok(Self {
id: row.get("id")?,
profile_id: row.get("profile_id").ok(),
workspace_id: row.get("workspace_id").ok(),
repo_id: row.get("repo_id").ok(),
name: row.get("name")?,
description: row.get("description").ok(),
type_: row.get("type").ok(),
status: row.get("status").ok(),
priority: row.get("priority").ok(),
tech_stack: row.get("tech_stack").ok(),
tags: row.get("tags").ok(),
repo_url: row.get("repo_url").ok(),
upstream_url: row.get("upstream_url").ok(),
default_branch: row.get("default_branch").ok(),
staging_url: row.get("staging_url").ok(),
prod_url: row.get("prod_url").ok(),
server_note: row.get("server_note").ok(),
space_id: row.get("space_id").ok(),
wsl_path: row.get("wsl_path").ok(),
win_path: row.get("win_path").ok(),
platform: row.get("platform").ok(),
recent_focus: row.get("recent_focus").ok(),
startup_commands: row.get("startup_commands").ok(),
last_push_at: row.get("last_push_at").ok(),
updated_at: row.get("updated_at").ok(),
})
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ActivityLog {
pub id: i64,
pub project_id: String,
pub summary: String,
pub created_at: Option<String>,
}
impl ActivityLog {
pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
Ok(Self {
id: row.get("id")?,
project_id: row.get("project_id")?,
summary: row.get("summary")?,
created_at: row.get("created_at")?,
})
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ProductGroup {
pub id: String,
pub name: String,
pub description: Option<String>,
pub space_id: Option<String>,
pub sort_order: i32,
}
impl ProductGroup {
pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
Ok(Self {
id: row.get("id")?,
name: row.get("name")?,
description: row.get("description")?,
space_id: row.get("space_id").ok(),
sort_order: row.get("sort_order").unwrap_or(0),
})
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ProjectGroupMap {
pub project_id: String,
pub group_id: String,
pub role: Option<String>,
}
impl ProjectGroupMap {
pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
Ok(Self {
project_id: row.get("project_id")?,
group_id: row.get("group_id")?,
role: row.get("role")?,
})
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Space {
pub id: String,
pub name: String,
pub is_current: bool,
pub created_at: Option<String>,
}
impl Space {
pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
Ok(Self {
id: row.get("id")?,
name: row.get("name")?,
is_current: row.get::<_, i64>("is_current")? != 0,
created_at: row.get("created_at")?,
})
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DeployCommand {
pub id: String,
pub project_id: String,
pub name: String,
pub command: String,
pub run_in: String,
}
impl DeployCommand {
pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
Ok(Self {
id: row.get("id")?,
project_id: row.get("project_id")?,
name: row.get("name")?,
command: row.get("command")?,
run_in: row.get("run_in")?,
})
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct CicdConfig {
pub id: String,
pub project_id: String,
pub config_type: String,
pub trigger_branch: String,
pub build_command: Option<String>,
pub node_version: Option<String>,
pub server_host: Option<String>,
pub server_user: Option<String>,
pub deploy_path: Option<String>,
pub start_command: Option<String>,
pub oss_endpoint: Option<String>,
pub created_at: Option<String>,
}
impl CicdConfig {
pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
Ok(Self {
id: row.get("id")?,
project_id: row.get("project_id")?,
config_type: row.get("config_type")?,
trigger_branch: row.get("trigger_branch")?,
build_command: row.get("build_command").ok(),
node_version: row.get("node_version").ok(),
server_host: row.get("server_host").ok(),
server_user: row.get("server_user").ok(),
deploy_path: row.get("deploy_path").ok(),
start_command: row.get("start_command").ok(),
oss_endpoint: row.get("oss_endpoint").ok(),
created_at: row.get("created_at").ok(),
})
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct WorkflowResult {
pub content: String,
pub path: String,
pub secrets: Vec<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GitRepo {
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 staging_url: Option<String>,
pub prod_url: Option<String>,
pub server_note: Option<String>,
pub status: Option<String>,
pub priority: Option<String>,
pub tech_stack: Option<String>,
pub last_synced_at: Option<String>,
pub created_at: Option<String>,
}
impl GitRepo {
pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
Ok(Self {
id: row.get("id")?,
repo_url: row.get("repo_url")?,
name: row.get("name")?,
description: row.get("description").ok(),
default_branch: row.get("default_branch").ok(),
upstream_url: row.get("upstream_url").ok(),
staging_url: row.get("staging_url").ok(),
prod_url: row.get("prod_url").ok(),
server_note: row.get("server_note").ok(),
status: row.get("status").ok(),
priority: row.get("priority").ok(),
tech_stack: row.get("tech_stack").ok(),
last_synced_at: row.get("last_synced_at").ok(),
created_at: row.get("created_at").ok(),
})
}
}
// ── 源码库 ─────────────────────────────────────────────────────────────────
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SourceCategory {
pub id: String,
pub name: String,
pub icon: String,
pub color: String,
pub sort_order: i32,
}
impl SourceCategory {
pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
Ok(Self {
id: row.get("id")?,
name: row.get("name")?,
icon: row.get::<_, String>("icon").unwrap_or_else(|_| "📁".into()),
color: row.get::<_, String>("color").unwrap_or_else(|_| "#6B7280".into()),
sort_order: row.get("sort_order").unwrap_or(0),
})
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SourceProject {
pub id: String,
pub name: String,
pub description: Option<String>,
pub category_id: Option<String>,
pub language: Option<String>,
pub tags: Option<String>,
pub repo_url: Option<String>,
pub introduction: Option<String>,
pub readme: Option<String>,
pub notes: Option<String>,
pub created_at: Option<String>,
pub updated_at: Option<String>,
// join 字段
pub category_name: Option<String>,
pub version_count: Option<i32>,
}
impl SourceProject {
pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
Ok(Self {
id: row.get("id")?,
name: row.get("name")?,
description: row.get("description").ok(),
category_id: row.get("category_id").ok(),
language: row.get("language").ok(),
tags: row.get("tags").ok(),
repo_url: row.get("repo_url").ok(),
introduction: row.get("introduction").ok(),
readme: row.get("readme").ok(),
notes: row.get("notes").ok(),
created_at: row.get("created_at").ok(),
updated_at: row.get("updated_at").ok(),
category_name: row.get("category_name").ok(),
version_count: row.get("version_count").ok(),
})
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SourceVersion {
pub id: String,
pub project_id: String,
pub version: String,
pub file_path: String,
pub archive_path: Option<String>,
pub file_size: i64,
pub is_primary: bool,
pub dir_tree: Option<String>,
pub notes: Option<String>,
pub imported_at: Option<String>,
}
impl SourceVersion {
pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
Ok(Self {
id: row.get("id")?,
project_id: row.get("project_id")?,
version: row.get("version")?,
file_path: row.get("file_path")?,
archive_path: row.get("archive_path").ok(),
file_size: row.get("file_size").unwrap_or(0),
is_primary: row.get::<_, i64>("is_primary").unwrap_or(0) != 0,
dir_tree: row.get("dir_tree").ok(),
notes: row.get("notes").ok(),
imported_at: row.get("imported_at").ok(),
})
}
}
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct DirEntry {
pub name: String,
#[serde(rename = "type")]
pub type_: String,
pub children_count: Option<i32>,
}