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

ambee/giterated

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

Fix settings

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨c518454

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