1use rpc::proto;
2use sea_orm::{
3 ActiveValue, ColumnTrait, DatabaseTransaction, EntityTrait, IntoActiveModel, QueryFilter,
4};
5
6use super::{dev_server, remote_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 remote_projects_update(
33 &self,
34 user_id: UserId,
35 ) -> crate::Result<proto::RemoteProjectsUpdate> {
36 self.transaction(
37 |tx| async move { self.remote_projects_update_internal(user_id, &tx).await },
38 )
39 .await
40 }
41
42 pub async fn remote_projects_update_internal(
43 &self,
44 user_id: UserId,
45 tx: &DatabaseTransaction,
46 ) -> crate::Result<proto::RemoteProjectsUpdate> {
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 remote_projects = remote_project::Entity::find()
53 .filter(
54 remote_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::RemoteProjectsUpdate {
62 dev_servers: dev_servers
63 .into_iter()
64 .map(|d| d.to_proto(proto::DevServerStatus::Offline))
65 .collect(),
66 remote_projects: remote_projects
67 .into_iter()
68 .map(|(remote_project, project)| remote_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::RemoteProjectsUpdate)> {
79 self.transaction(|tx| async move {
80 let dev_server = dev_server::Entity::insert(dev_server::ActiveModel {
81 id: ActiveValue::NotSet,
82 hashed_token: ActiveValue::Set(hashed_access_token.to_string()),
83 name: ActiveValue::Set(name.to_string()),
84 user_id: ActiveValue::Set(user_id),
85 })
86 .exec_with_returning(&*tx)
87 .await?;
88
89 let remote_projects = self.remote_projects_update_internal(user_id, &tx).await?;
90
91 Ok((dev_server, remote_projects))
92 })
93 .await
94 }
95
96 pub async fn delete_dev_server(
97 &self,
98 id: DevServerId,
99 user_id: UserId,
100 ) -> crate::Result<proto::RemoteProjectsUpdate> {
101 self.transaction(|tx| async move {
102 let Some(dev_server) = dev_server::Entity::find_by_id(id).one(&*tx).await? else {
103 return Err(anyhow::anyhow!("no dev server with id {}", id))?;
104 };
105 if dev_server.user_id != user_id {
106 return Err(anyhow::anyhow!(proto::ErrorCode::Forbidden))?;
107 }
108
109 remote_project::Entity::delete_many()
110 .filter(remote_project::Column::DevServerId.eq(id))
111 .exec(&*tx)
112 .await?;
113
114 dev_server::Entity::delete(dev_server.into_active_model())
115 .exec(&*tx)
116 .await?;
117
118 let remote_projects = self.remote_projects_update_internal(user_id, &tx).await?;
119
120 Ok(remote_projects)
121 })
122 .await
123 }
124}