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

ambee/giterated

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

Simple branch staleness implementation

Emilia - ⁨1⁩ year ago

parent: tbd commit: ⁨efc3f02

⁨giterated-daemon/src/database_backend/mod.rs⁩ - ⁨6371⁩ 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_branches, repository_get_statistics, repository_info,
30 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
100 builder
101 }
102 }
103
104 impl Debug for DatabaseBackend {
105 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 f.debug_struct("DatabaseBackend").finish()
107 }
108 }
109
110 #[async_trait::async_trait]
111 impl MetadataProvider for DatabaseBackend {
112 fn provides_for(&self, object: &dyn Any) -> bool {
113 object.is::<Instance>() || object.is::<Repository>() || object.is::<User>()
114 }
115
116 async fn write(
117 &self,
118 object: AnyObject,
119 _object_meta: &ObjectMeta,
120 setting: AnySetting,
121 setting_meta: &SettingMeta,
122 ) -> Result<(), anyhow::Error> {
123 if let Some(repository) = object.downcast_ref::<Repository>() {
124 sqlx::query!("INSERT INTO repository_settings VALUES ($1, $2, $3) ON CONFLICT (repository, name) DO UPDATE SET value = $3",
125 repository.to_string(), setting_meta.name, serde_json::to_string(&(setting_meta.serialize)(setting).unwrap())?)
126 .execute(&self.pool).await?;
127
128 Ok(())
129 } else if let Some(user) = object.downcast_ref::<User>() {
130 sqlx::query!("INSERT INTO user_settings VALUES ($1, $2, $3) ON CONFLICT (username, name) DO UPDATE SET value = $3",
131 user.username, setting_meta.name, serde_json::to_string(&(setting_meta.serialize)(setting).unwrap())?)
132 .execute(&self.pool).await?;
133
134 Ok(())
135 } else {
136 unreachable!()
137 }
138 }
139
140 async fn read(
141 &self,
142 object: AnyObject,
143 _object_meta: &ObjectMeta,
144 setting_meta: &SettingMeta,
145 ) -> Result<Value, anyhow::Error> {
146 if let Some(repository) = object.downcast_ref::<Repository>() {
147 let row = sqlx::query_as!(
148 RepositorySettingRow,
149 "SELECT * FROM repository_settings WHERE repository = $1 AND name = $2",
150 repository.to_string(),
151 setting_meta.name
152 )
153 .fetch_one(&self.pool)
154 .await?;
155
156 let setting =
157 serde_json::from_str(&row.value).context("deserializing setting from database")?;
158
159 Ok(setting)
160 } else if let Some(user) = object.downcast_ref::<User>() {
161 info!("User for {}", setting_meta.name);
162 let row = sqlx::query_as!(
163 UserSettingRow,
164 "SELECT * FROM user_settings WHERE username = $1 AND name = $2",
165 user.username,
166 setting_meta.name
167 )
168 .fetch_one(&self.pool)
169 .await?;
170
171 let setting =
172 serde_json::from_str(&row.value).context("deserializing setting from database")?;
173
174 Ok(setting)
175 } else {
176 unreachable!()
177 }
178 }
179 }
180