dev-manager-tauri/src-tauri/src/models.rs
lanrtop 9660bcbc48 feat: 服务器管理体系 + 云产品扩展 + 源码库 + API密钥管理
新增多个核心功能模块(后端 Rust 命令 + 前端 Panel 全部接线完成):

- 服务器注册表:servers.rs + ServerManagerPanel、ProjectServerPanel(项目关联服务器)
- SSH Config 自动同步:ssh_config.rs
- 服务器软件管理:server_software.rs + ServerSoftwareBlock
- 宿主机应用管理:host_apps.rs + HostAppsPanel
- 服务配置管理:service_config.rs + ServiceConfigBlock
- 项目部署信息:deploy_info.rs + DeployInfoPanel
- API密钥管理:api_keys.rs + ApiKeysPanel
- 云产品管理:cloud_products.rs + CloudProductsPanel(含 Docker应用、云数据库实例子模块)
- 源码库:source_library.rs + SourceLibraryPage(分类管理 + 项目详情)

导航栏新增「源码库」入口;蓝图 manifest 同步补录 7 个新模块
2026-04-17 22:25:06 +09:00

373 lines
12 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 GitAccount {
pub id: String,
pub username: String,
pub avatar_url: Option<String>,
pub provider: String,
pub created_at: Option<String>,
}
impl GitAccount {
pub fn from_row(row: &Row<'_>) -> rusqlite::Result<Self> {
Ok(Self {
id: row.get("id")?,
username: row.get("username")?,
avatar_url: row.get("avatar_url")?,
provider: row.get("provider")?,
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>,
}