1 use {
2 crate::{app_auth::AuthApp, ar_date_format, runner::Runner},
3 chrono::{DateTime, Utc},
4 serde::{Deserialize, Serialize},
5 serde_repr::{Deserialize_repr, Serialize_repr},
6 std::fmt::Display,
7 tsync::tsync,
8 };
9
10 /// How the project's files are delivered to the server.
11 ///
12 /// `Git` — the classic smbCloud flow: push to a remote git repo, the server
13 /// builds and restarts the process.
14 /// `Rsync` — files are transferred directly with rsync over SSH; no build step
15 /// runs on the server. Ideal for pre-built static sites or assets.
16 #[derive(Deserialize_repr, Serialize_repr, Debug, Clone, Copy, PartialEq, Eq, Default)]
17 #[repr(u8)]
18 #[tsync]
19 pub enum DeploymentMethod {
20 #[default]
21 Git = 0,
22 Rsync = 1,
23 }
24
25 impl Display for DeploymentMethod {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 match self {
28 DeploymentMethod::Git => write!(f, "Git"),
29 DeploymentMethod::Rsync => write!(f, "Rsync"),
30 }
31 }
32 }
33
34 #[derive(Deserialize, Debug, Serialize)]
35 pub struct Config {
36 pub current_project: Option<Project>,
37 pub current_auth_app: Option<AuthApp>,
38 }
39
40 #[derive(Deserialize, Serialize, Debug, Clone)]
41 #[tsync]
42 pub struct Project {
43 pub id: i32,
44 pub name: String,
45 pub runner: Runner,
46 /// Defaults to `Git` when absent (older API responses won't include the field).
47 #[serde(default)]
48 pub deployment_method: DeploymentMethod,
49 pub path: Option<String>,
50 pub repository: Option<String>,
51 pub description: Option<String>,
52 pub created_at: DateTime<Utc>,
53 pub updated_at: DateTime<Utc>,
54 /// Deployment kind, e.g. "vite-spa". Absent for server-side runners.
55 pub kind: Option<String>,
56 /// Local source directory to build from, e.g. "frontend/connected-devices".
57 /// Used by vite-spa deploys as the working directory for the build step.
58 /// Distinct from `path`, which is the remote destination on the server.
59 pub source: Option<String>,
60 /// Build output directory relative to `source`, e.g. "dist".
61 pub output: Option<String>,
62 /// Package manager to use for the build step, e.g. "pnpm".
63 pub package_manager: Option<String>,
64 /// PM2 process name to restart after a nextjs-ssr deploy, e.g. "my-app".
65 /// Matches the name passed to `pm2 start` on the server.
66 pub pm2_app: Option<String>,
67 }
68
69 impl Display for Project {
70 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
71 write!(f, "ID: {}, Name: {}", self.id, self.name,)
72 }
73 }
74 #[derive(Serialize, Debug, Deserialize, Clone)]
75 #[tsync]
76 pub struct ProjectCreate {
77 pub name: String,
78 pub repository: String,
79 pub description: String,
80 pub runner: Runner,
81 #[serde(default)]
82 pub deployment_method: DeploymentMethod,
83 }
84
85 #[derive(Deserialize, Serialize, Debug)]
86 #[tsync]
87 pub struct Deployment {
88 pub id: i32,
89 pub project_id: i32,
90 pub commit_hash: String,
91 pub status: DeploymentStatus,
92 #[serde(with = "ar_date_format")]
93 pub created_at: DateTime<Utc>,
94 #[serde(with = "ar_date_format")]
95 pub updated_at: DateTime<Utc>,
96 }
97
98 #[derive(Deserialize, Serialize, Debug)]
99 pub struct DeploymentPayload {
100 pub commit_hash: String,
101 pub status: DeploymentStatus,
102 }
103
104 #[derive(Deserialize_repr, Serialize_repr, Debug, Clone, Copy)] // Added Clone, Copy
105 #[repr(u8)]
106 #[tsync]
107 pub enum DeploymentStatus {
108 Started = 0,
109 Failed,
110 Done,
111 }
112
113 impl Display for DeploymentStatus {
114 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115 match self {
116 DeploymentStatus::Started => write!(f, "🚀"),
117 DeploymentStatus::Failed => write!(f, "❌"),
118 DeploymentStatus::Done => write!(f, "✅"),
119 }
120 }
121 }
122
123 #[cfg(test)]
124 mod tests {
125 use super::*;
126 use serde_json::json;
127 #[test]
128 fn test_project_create() {
129 let project_create = ProjectCreate {
130 name: "test".to_owned(),
131 repository: "test".to_owned(),
132 description: "test".to_owned(),
133 runner: Runner::NodeJs,
134 deployment_method: DeploymentMethod::Git,
135 };
136 let json = json!({
137 "name": "test",
138 "repository": "test",
139 "description": "test",
140 "runner": 0,
141 "deployment_method": 0
142 });
143 assert_eq!(serde_json::to_value(project_create).unwrap(), json);
144 }
145
146 #[test]
147 fn test_deployment_status_display() {
148 assert_eq!(format!("{}", DeploymentStatus::Started), "🚀");
149 assert_eq!(DeploymentStatus::Started.to_string(), "🚀");
150
151 assert_eq!(format!("{}", DeploymentStatus::Failed), "❌");
152 assert_eq!(DeploymentStatus::Failed.to_string(), "❌");
153
154 assert_eq!(format!("{}", DeploymentStatus::Done), "✅");
155 assert_eq!(DeploymentStatus::Done.to_string(), "✅");
156 }
157 }