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

ambee/giterated

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

`giterated_cache` initial implementation

# Giterated Stack - Added the ability for dynamic substack handlers to exist for operations relevant to caching. - Added type metadata to the dynamic types. # Giterated Cache - Created - Implemented caching and fetching from cache. Hell fucking yes!!!! It works so good. Are you snooping in the commit logs because you're curious about the history of giterated? Cool that it got so big... tell me I say hi :)

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨86afeef

⁨giterated-daemon/src/database_backend/mod.rs⁩ - ⁨6155⁩ 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, 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.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.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.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.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