1use rpc::proto;
2use sea_orm::{
3 ActiveValue, ColumnTrait, DatabaseTransaction, EntityTrait, IntoActiveModel, QueryFilter,
4};
5
6use super::{dev_server, dev_server_project, Database, DevServerId, UserId};
7
8impl Database {
9 pub async fn get_dev_server(
10 &self,
11 dev_server_id: DevServerId,
12 ) -> crate::Result<dev_server::Model> {
13 self.transaction(|tx| async move {
14 Ok(dev_server::Entity::find_by_id(dev_server_id)
15 .one(&*tx)
16 .await?
17 .ok_or_else(|| anyhow::anyhow!("no dev server with id {}", dev_server_id))?)
18 })
19 .await
20 }
21
22 pub async fn get_dev_server_for_user(
23 &self,
24 dev_server_id: DevServerId,
25 user_id: UserId,
26 ) -> crate::Result<dev_server::Model> {
27 self.transaction(|tx| async move {
28 let server = dev_server::Entity::find_by_id(dev_server_id)
29 .one(&*tx)
30 .await?
31 .ok_or_else(|| anyhow::anyhow!("no dev server with id {}", dev_server_id))?;
32 if server.user_id != user_id {
33 return Err(anyhow::anyhow!(
34 "dev server {} is not owned by user {}",
35 dev_server_id,
36 user_id
37 ))?;
38 }
39 Ok(server)
40 })
41 .await
42 }
43
44 pub async fn get_dev_servers(&self, user_id: UserId) -> crate::Result<Vec<dev_server::Model>> {
45 self.transaction(|tx| async move {
46 Ok(dev_server::Entity::find()
47 .filter(dev_server::Column::UserId.eq(user_id))
48 .all(&*tx)
49 .await?)
50 })
51 .await
52 }
53
54 pub async fn dev_server_projects_update(
55 &self,
56 user_id: UserId,
57 ) -> crate::Result<proto::DevServerProjectsUpdate> {
58 self.transaction(|tx| async move {
59 self.dev_server_projects_update_internal(user_id, &tx).await
60 })
61 .await
62 }
63
64 pub async fn dev_server_projects_update_internal(
65 &self,
66 user_id: UserId,
67 tx: &DatabaseTransaction,
68 ) -> crate::Result<proto::DevServerProjectsUpdate> {
69 let dev_servers = dev_server::Entity::find()
70 .filter(dev_server::Column::UserId.eq(user_id))
71 .all(tx)
72 .await?;
73
74 let dev_server_projects = dev_server_project::Entity::find()
75 .filter(
76 dev_server_project::Column::DevServerId
77 .is_in(dev_servers.iter().map(|d| d.id).collect::<Vec<_>>()),
78 )
79 .find_also_related(super::project::Entity)
80 .all(tx)
81 .await?;
82
83 Ok(proto::DevServerProjectsUpdate {
84 dev_servers: dev_servers
85 .into_iter()
86 .map(|d| d.to_proto(proto::DevServerStatus::Offline))
87 .collect(),
88 dev_server_projects: dev_server_projects
89 .into_iter()
90 .map(|(dev_server_project, project)| dev_server_project.to_proto(project))
91 .collect(),
92 })
93 }
94
95 pub async fn create_dev_server(
96 &self,
97 name: &str,
98 ssh_connection_string: Option<&str>,
99 hashed_access_token: &str,
100 user_id: UserId,
101 ) -> crate::Result<(dev_server::Model, proto::DevServerProjectsUpdate)> {
102 self.transaction(|tx| async move {
103 if name.trim().is_empty() {
104 return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?;
105 }
106
107 let dev_server = dev_server::Entity::insert(dev_server::ActiveModel {
108 id: ActiveValue::NotSet,
109 hashed_token: ActiveValue::Set(hashed_access_token.to_string()),
110 name: ActiveValue::Set(name.trim().to_string()),
111 user_id: ActiveValue::Set(user_id),
112 ssh_connection_string: ActiveValue::Set(
113 ssh_connection_string.map(ToOwned::to_owned),
114 ),
115 })
116 .exec_with_returning(&*tx)
117 .await?;
118
119 let dev_server_projects = self
120 .dev_server_projects_update_internal(user_id, &tx)
121 .await?;
122
123 Ok((dev_server, dev_server_projects))
124 })
125 .await
126 }
127
128 pub async fn update_dev_server_token(
129 &self,
130 id: DevServerId,
131 hashed_token: &str,
132 user_id: UserId,
133 ) -> crate::Result<proto::DevServerProjectsUpdate> {
134 self.transaction(|tx| async move {
135 let Some(dev_server) = dev_server::Entity::find_by_id(id).one(&*tx).await? else {
136 return Err(anyhow::anyhow!("no dev server with id {}", id))?;
137 };
138 if dev_server.user_id != user_id {
139 return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?;
140 }
141
142 dev_server::Entity::update(dev_server::ActiveModel {
143 hashed_token: ActiveValue::Set(hashed_token.to_string()),
144 ..dev_server.clone().into_active_model()
145 })
146 .exec(&*tx)
147 .await?;
148
149 let dev_server_projects = self
150 .dev_server_projects_update_internal(user_id, &tx)
151 .await?;
152
153 Ok(dev_server_projects)
154 })
155 .await
156 }
157
158 pub async fn rename_dev_server(
159 &self,
160 id: DevServerId,
161 name: &str,
162 ssh_connection_string: Option<&str>,
163 user_id: UserId,
164 ) -> crate::Result<proto::DevServerProjectsUpdate> {
165 self.transaction(|tx| async move {
166 let Some(dev_server) = dev_server::Entity::find_by_id(id).one(&*tx).await? else {
167 return Err(anyhow::anyhow!("no dev server with id {}", id))?;
168 };
169 if dev_server.user_id != user_id || name.trim().is_empty() {
170 return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?;
171 }
172
173 dev_server::Entity::update(dev_server::ActiveModel {
174 name: ActiveValue::Set(name.trim().to_string()),
175 ssh_connection_string: ActiveValue::Set(
176 ssh_connection_string.map(ToOwned::to_owned),
177 ),
178 ..dev_server.clone().into_active_model()
179 })
180 .exec(&*tx)
181 .await?;
182
183 let dev_server_projects = self
184 .dev_server_projects_update_internal(user_id, &tx)
185 .await?;
186
187 Ok(dev_server_projects)
188 })
189 .await
190 }
191
192 pub async fn delete_dev_server(
193 &self,
194 id: DevServerId,
195 user_id: UserId,
196 ) -> crate::Result<proto::DevServerProjectsUpdate> {
197 self.transaction(|tx| async move {
198 let Some(dev_server) = dev_server::Entity::find_by_id(id).one(&*tx).await? else {
199 return Err(anyhow::anyhow!("no dev server with id {}", id))?;
200 };
201 if dev_server.user_id != user_id {
202 return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?;
203 }
204
205 dev_server_project::Entity::delete_many()
206 .filter(dev_server_project::Column::DevServerId.eq(id))
207 .exec(&*tx)
208 .await?;
209
210 dev_server::Entity::delete(dev_server.into_active_model())
211 .exec(&*tx)
212 .await?;
213
214 let dev_server_projects = self
215 .dev_server_projects_update_internal(user_id, &tx)
216 .await?;
217
218 Ok(dev_server_projects)
219 })
220 .await
221 }
222}