dev-manager-tauri/src-tauri/src/models.rs
lanrtop 806205b8b0
All checks were successful
Push & PR Check / check (push) Successful in 1m0s
Push & PR Check / check (pull_request) Successful in 58s
feat(cicd): add safe agent workflow projection
Add preview-and-apply workflow commands with digest confirmation, project boundary checks, and MCP/Tauri projections. Sync onboarding capability packs and Codex skill guidance.

Feature-Confirmed: true

Rules-Applied: R01,R02,R06,R07,R12,R13
2026-09-22 23:27:34 +09:00

373 lines
12 KiB
Rust
Raw Permalink 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, PartialEq, Eq)]
pub struct WorkflowPlan {
pub project_id: String,
pub workflow_type: String,
pub content: String,
pub path: String,
pub content_digest: String,
pub change: String,
pub required_secrets: Vec<String>,
pub node_source: Option<String>,
pub pnpm_source: Option<String>,
}
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct WorkflowApplyResult {
pub plan: WorkflowPlan,
pub written: bool,
}
#[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>,
}