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_servers(&self, user_id: UserId) -> crate::Result<Vec<dev_server::Model>> {
23 self.transaction(|tx| async move {
24 Ok(dev_server::Entity::find()
25 .filter(dev_server::Column::UserId.eq(user_id))
26 .all(&*tx)
27 .await?)
28 })
29 .await
30 }
31
32 pub async fn dev_server_projects_update(
33 &self,
34 user_id: UserId,
35 ) -> crate::Result<proto::DevServerProjectsUpdate> {
36 self.transaction(|tx| async move {
37 self.dev_server_projects_update_internal(user_id, &tx).await
38 })
39 .await
40 }
41
42 pub async fn dev_server_projects_update_internal(
43 &self,
44 user_id: UserId,
45 tx: &DatabaseTransaction,
46 ) -> crate::Result<proto::DevServerProjectsUpdate> {
47 let dev_servers = dev_server::Entity::find()
48 .filter(dev_server::Column::UserId.eq(user_id))
49 .all(tx)
50 .await?;
51
52 let dev_server_projects = dev_server_project::Entity::find()
53 .filter(
54 dev_server_project::Column::DevServerId
55 .is_in(dev_servers.iter().map(|d| d.id).collect::<Vec<_>>()),
56 )
57 .find_also_related(super::project::Entity)
58 .all(tx)
59 .await?;
60
61 Ok(proto::DevServerProjectsUpdate {
62 dev_servers: dev_servers
63 .into_iter()
64 .map(|d| d.to_proto(proto::DevServerStatus::Offline))
65 .collect(),
66 dev_server_projects: dev_server_projects
67 .into_iter()
68 .map(|(dev_server_project, project)| dev_server_project.to_proto(project))
69 .collect(),
70 })
71 }
72
73 pub async fn create_dev_server(
74 &self,
75 name: &str,
76 hashed_access_token: &str,
77 user_id: UserId,
78 ) -> crate::Result<(dev_server::Model, proto::DevServerProjectsUpdate)> {
79 self.transaction(|tx| async move {
80 if name.trim().is_empty() {
81 return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?;
82 }
83
84 let dev_server = dev_server::Entity::insert(dev_server::ActiveModel {
85 id: ActiveValue::NotSet,
86 hashed_token: ActiveValue::Set(hashed_access_token.to_string()),
87 name: ActiveValue::Set(name.trim().to_string()),
88 user_id: ActiveValue::Set(user_id),
89 })
90 .exec_with_returning(&*tx)
91 .await?;
92
93 let dev_server_projects = self
94 .dev_server_projects_update_internal(user_id, &tx)
95 .await?;
96
97 Ok((dev_server, dev_server_projects))
98 })
99 .await
100 }
101
102 pub async fn update_dev_server_token(
103 &self,
104 id: DevServerId,
105 hashed_token: &str,
106 user_id: UserId,
107 ) -> crate::Result<proto::DevServerProjectsUpdate> {
108 self.transaction(|tx| async move {
109 let Some(dev_server) = dev_server::Entity::find_by_id(id).one(&*tx).await? else {
110 return Err(anyhow::anyhow!("no dev server with id {}", id))?;
111 };
112 if dev_server.user_id != user_id {
113 return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?;
114 }
115
116 dev_server::Entity::update(dev_server::ActiveModel {
117 hashed_token: ActiveValue::Set(hashed_token.to_string()),
118 ..dev_server.clone().into_active_model()
119 })
120 .exec(&*tx)
121 .await?;
122
123 let dev_server_projects = self
124 .dev_server_projects_update_internal(user_id, &tx)
125 .await?;
126
127 Ok(dev_server_projects)
128 })
129 .await
130 }
131
132 pub async fn rename_dev_server(
133 &self,
134 id: DevServerId,
135 name: &str,
136 user_id: UserId,
137 ) -> crate::Result<proto::DevServerProjectsUpdate> {
138 self.transaction(|tx| async move {
139 let Some(dev_server) = dev_server::Entity::find_by_id(id).one(&*tx).await? else {
140 return Err(anyhow::anyhow!("no dev server with id {}", id))?;
141 };
142 if dev_server.user_id != user_id || name.trim().is_empty() {
143 return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?;
144 }
145
146 dev_server::Entity::update(dev_server::ActiveModel {
147 name: ActiveValue::Set(name.trim().to_string()),
148 ..dev_server.clone().into_active_model()
149 })
150 .exec(&*tx)
151 .await?;
152
153 let dev_server_projects = self
154 .dev_server_projects_update_internal(user_id, &tx)
155 .await?;
156
157 Ok(dev_server_projects)
158 })
159 .await
160 }
161
162 pub async fn delete_dev_server(
163 &self,
164 id: DevServerId,
165 user_id: UserId,
166 ) -> crate::Result<proto::DevServerProjectsUpdate> {
167 self.transaction(|tx| async move {
168 let Some(dev_server) = dev_server::Entity::find_by_id(id).one(&*tx).await? else {
169 return Err(anyhow::anyhow!("no dev server with id {}", id))?;
170 };
171 if dev_server.user_id != user_id {
172 return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?;
173 }
174
175 dev_server_project::Entity::delete_many()
176 .filter(dev_server_project::Column::DevServerId.eq(id))
177 .exec(&*tx)
178 .await?;
179
180 dev_server::Entity::delete(dev_server.into_active_model())
181 .exec(&*tx)
182 .await?;
183
184 let dev_server_projects = self
185 .dev_server_projects_update_internal(user_id, &tx)
186 .await?;
187
188 Ok(dev_server_projects)
189 })
190 .await
191 }
192}