JavaScript is disabled, refresh for a better experience. ambee/giterated

ambee/giterated

Git repository hosting, collaboration, and discovery for the Fediverse.

Fix LatestCommit

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨3ff68ad

⁨giterated-daemon/src/database_backend/mod.rs⁩ - ⁨6186⁩ bytes
Raw
1 pub mod handler;
2 pub mod updates;
3
4 use std::any::Any;
5 use std::sync::Arc;
6
7 use anyhow::Context;
8
9 use giterated_models::instance::Instance;
10 use giterated_models::repository::{DefaultBranch, Description, Repository, Visibility};
11 use giterated_models::user::{Bio, DisplayName, User};
12 use giterated_stack::provider::MetadataProvider;
13 use giterated_stack::SettingMeta;
14 use giterated_stack::{AnyObject, AnySetting, GiteratedStack, ObjectMeta, SubstackBuilder};
15 use serde_json::Value;
16 use sqlx::PgPool;
17 use std::fmt::Debug;
18 use tokio::sync::{Mutex, OnceCell};
19
20 use crate::backend::settings::{RepositorySettingRow, UserSettingRow};
21 use crate::backend::{RepositoryBackend, UserBackend};
22
23 use self::handler::{
24 instance_authentication_request, instance_create_repository_request,
25 instance_registration_request, repository_commit_before, repository_commit_by_id,
26 repository_diff, repository_diff_patch, repository_file_from_id, repository_file_from_path,
27 repository_get_branches, repository_get_statistics, repository_info,
28 repository_last_commit_of_file, repository_latest_commit, user_get_repositories,
29 };
30
31 /// A backend implementation which attempts to resolve data from the instance's database.
32 #[derive(Clone)]
33 #[allow(unused)]
34 pub struct DatabaseBackend {
35 pub(self) our_instance: Instance,
36 pub(self) pool: PgPool,
37 pub(self) user_backend: Arc<Mutex<dyn UserBackend + Send>>,
38 pub(self) repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>,
39 pub stack: Arc<OnceCell<Arc<GiteratedStack>>>,
40 }
41
42 impl DatabaseBackend {
43 pub fn new(
44 instance: Instance,
45 user_backend: Arc<Mutex<dyn UserBackend + Send>>,
46 repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>,
47 pool: PgPool,
48 stack: Arc<OnceCell<Arc<GiteratedStack>>>,
49 ) -> Self {
50 Self {
51 our_instance: instance,
52 user_backend,
53 repository_backend,
54 pool,
55 stack,
56 }
57 }
58
59 pub fn into_substack(self) -> SubstackBuilder<Self> {
60 let mut builder = SubstackBuilder::<Self>::new(self.clone());
61
62 builder.object_metadata_provider(Box::new(self));
63
64 builder
65 .object::<Repository>()
66 .object::<User>()
67 .object::<Instance>();
68
69 // Register value settings, which are settings that directly correspond to
70 // value types.
71 builder
72 .value_setting::<User, DisplayName>()
73 .value_setting::<User, Bio>()
74 .value_setting::<Repository, Description>()
75 .value_setting::<Repository, Visibility>()
76 .value_setting::<Repository, DefaultBranch>();
77
78 builder.value(repository_latest_commit);
79
80 builder
81 .operation(user_get_repositories)
82 .operation(instance_registration_request)
83 .operation(instance_authentication_request)
84 .operation(instance_create_repository_request)
85 .operation(repository_info)
86 .operation(repository_get_statistics)
87 .operation(repository_file_from_id)
88 .operation(repository_file_from_path)
89 .operation(repository_last_commit_of_file)
90 .operation(repository_commit_by_id)
91 .operation(repository_diff)
92 .operation(repository_diff_patch)
93 .operation(repository_commit_before)
94 .operation(repository_get_branches);
95
96 builder
97 }
98 }
99
100 impl Debug for DatabaseBackend {
101 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102 f.debug_struct("DatabaseBackend").finish()
103 }
104 }
105
106 #[async_trait::async_trait]
107 impl MetadataProvider for DatabaseBackend {
108 fn provides_for(&self, object: &dyn Any) -> bool {
109 object.is::<Instance>() || object.is::<Repository>() || object.is::<User>()
110 }
111
112 async fn write(
113 &self,
114 object: AnyObject,
115 _object_meta: &ObjectMeta,
116 setting: AnySetting,
117 setting_meta: &SettingMeta,
118 ) -> Result<(), anyhow::Error> {
119 if let Some(repository) = object.0.downcast_ref::<Repository>() {
120 sqlx::query!("INSERT INTO repository_settings VALUES ($1, $2, $3) ON CONFLICT (repository, name) DO UPDATE SET value = $3",
121 repository.to_string(), setting_meta.name, serde_json::to_string(&(setting_meta.serialize)(setting).unwrap())?)
122 .execute(&self.pool).await?;
123
124 Ok(())
125 } else if let Some(user) = object.0.downcast_ref::<User>() {
126 sqlx::query!("INSERT INTO user_settings VALUES ($1, $2, $3) ON CONFLICT (username, name) DO UPDATE SET value = $3",
127 user.username, setting_meta.name, serde_json::to_string(&(setting_meta.serialize)(setting).unwrap())?)
128 .execute(&self.pool).await?;
129
130 Ok(())
131 } else {
132 unreachable!()
133 }
134 }
135
136 async fn read(
137 &self,
138 object: AnyObject,
139 _object_meta: &ObjectMeta,
140 setting_meta: &SettingMeta,
141 ) -> Result<Value, anyhow::Error> {
142 if let Some(repository) = object.0.downcast_ref::<Repository>() {
143 let row = sqlx::query_as!(
144 RepositorySettingRow,
145 "SELECT * FROM repository_settings WHERE repository = $1 AND name = $2",
146 repository.to_string(),
147 setting_meta.name
148 )
149 .fetch_one(&self.pool)
150 .await?;
151
152 let setting =
153 serde_json::from_str(&row.value).context("deserializing setting from database")?;
154
155 Ok(setting)
156 } else if let Some(user) = object.0.downcast_ref::<User>() {
157 info!("User for {}", setting_meta.name);
158 let row = sqlx::query_as!(
159 UserSettingRow,
160 "SELECT * FROM user_settings WHERE username = $1 AND name = $2",
161 user.username,
162 setting_meta.name
163 )
164 .fetch_one(&self.pool)
165 .await?;
166
167 let setting =
168 serde_json::from_str(&row.value).context("deserializing setting from database")?;
169
170 Ok(setting)
171 } else {
172 unreachable!()
173 }
174 }
175 }
176