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

ambee/giterated

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

Basic tag search and range

Emilia - ⁨1⁩ year ago

parent: tbd commit: ⁨112ca96

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