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

ambee/giterated

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

Base protocol refactor complete

Amber - ⁨2⁩ years ago

parent: tbd commit: ⁨079d544

Showing ⁨⁨32⁩ changed files⁩ with ⁨⁨649⁩ insertions⁩ and ⁨⁨653⁩ deletions⁩

Cargo.lock

View file
@@ -350,6 +350,41 @@ dependencies = [
350 350 ]
351 351
352 352 [[package]]
353 name = "darling"
354 version = "0.20.3"
355 source = "registry+https://github.com/rust-lang/crates.io-index"
356 checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e"
357 dependencies = [
358 "darling_core",
359 "darling_macro",
360 ]
361
362 [[package]]
363 name = "darling_core"
364 version = "0.20.3"
365 source = "registry+https://github.com/rust-lang/crates.io-index"
366 checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621"
367 dependencies = [
368 "fnv",
369 "ident_case",
370 "proc-macro2",
371 "quote",
372 "strsim",
373 "syn 2.0.29",
374 ]
375
376 [[package]]
377 name = "darling_macro"
378 version = "0.20.3"
379 source = "registry+https://github.com/rust-lang/crates.io-index"
380 checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5"
381 dependencies = [
382 "darling_core",
383 "quote",
384 "syn 2.0.29",
385 ]
386
387 [[package]]
353 388 name = "data-encoding"
354 389 version = "2.4.0"
355 390 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -390,6 +425,9 @@ name = "deranged"
390 425 version = "0.3.8"
391 426 source = "registry+https://github.com/rust-lang/crates.io-index"
392 427 checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946"
428 dependencies = [
429 "serde",
430 ]
393 431
394 432 [[package]]
395 433 name = "digest"
@@ -729,6 +767,7 @@ dependencies = [
729 767 "semver",
730 768 "serde",
731 769 "serde_json",
770 "serde_with",
732 771 "sqlx",
733 772 "thiserror",
734 773 "toml",
@@ -922,6 +961,12 @@ dependencies = [
922 961 ]
923 962
924 963 [[package]]
964 name = "ident_case"
965 version = "1.0.1"
966 source = "registry+https://github.com/rust-lang/crates.io-index"
967 checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
968
969 [[package]]
925 970 name = "idna"
926 971 version = "0.4.0"
927 972 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -939,6 +984,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
939 984 dependencies = [
940 985 "autocfg",
941 986 "hashbrown 0.12.3",
987 "serde",
942 988 ]
943 989
944 990 [[package]]
@@ -949,6 +995,7 @@ checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d"
949 995 dependencies = [
950 996 "equivalent",
951 997 "hashbrown 0.14.0",
998 "serde",
952 999 ]
953 1000
954 1001 [[package]]
@@ -1799,6 +1846,35 @@ dependencies = [
1799 1846 ]
1800 1847
1801 1848 [[package]]
1849 name = "serde_with"
1850 version = "3.3.0"
1851 source = "registry+https://github.com/rust-lang/crates.io-index"
1852 checksum = "1ca3b16a3d82c4088f343b7480a93550b3eabe1a358569c2dfe38bbcead07237"
1853 dependencies = [
1854 "base64 0.21.3",
1855 "chrono",
1856 "hex",
1857 "indexmap 1.9.3",
1858 "indexmap 2.0.0",
1859 "serde",
1860 "serde_json",
1861 "serde_with_macros",
1862 "time 0.3.28",
1863 ]
1864
1865 [[package]]
1866 name = "serde_with_macros"
1867 version = "3.3.0"
1868 source = "registry+https://github.com/rust-lang/crates.io-index"
1869 checksum = "2e6be15c453eb305019bfa438b1593c731f36a289a7853f7707ee29e870b3b3c"
1870 dependencies = [
1871 "darling",
1872 "proc-macro2",
1873 "quote",
1874 "syn 2.0.29",
1875 ]
1876
1877 [[package]]
1802 1878 name = "sha1"
1803 1879 version = "0.10.5"
1804 1880 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2142,6 +2218,12 @@ dependencies = [
2142 2218 ]
2143 2219
2144 2220 [[package]]
2221 name = "strsim"
2222 version = "0.10.0"
2223 source = "registry+https://github.com/rust-lang/crates.io-index"
2224 checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
2225
2226 [[package]]
2145 2227 name = "subtle"
2146 2228 version = "2.5.0"
2147 2229 source = "registry+https://github.com/rust-lang/crates.io-index"

giterated-daemon/migrations/20230913232601_repository_metadata.sql

View file
@@ -0,0 +1,8 @@
1 CREATE TABLE IF NOT EXISTS repository_metadata
2 (
3 repository TEXT NOT NULL,
4 name TEXT NOT NULL,
5 value TEXT NOT NULL
6 );
7
8 CREATE UNIQUE INDEX unique_per_repository_metadata ON repository_metadata (repository, name);
8 \ No newline at end of file

giterated-daemon/migrations/20230913232625_user_metadata.sql

View file
@@ -0,0 +1,17 @@
1 CREATE TABLE IF NOT EXISTS user_metadata
2 (
3 username TEXT NOT NULL,
4 name TEXT NOT NULL,
5 value TEXT NOT NULL
6 );
7
8 CREATE UNIQUE INDEX unique_per_user_metadata ON user_metadata (username, name);
9
10 CREATE TABLE IF NOT EXISTS user_settings
11 (
12 username TEXT NOT NULL,
13 name TEXT NOT NULL,
14 value TEXT NOT NULL
15 );
16
17 CREATE UNIQUE INDEX unique_per_user_settings ON user_settings (username, name);
17 \ No newline at end of file

giterated-daemon/src/backend/git.rs

View file
@@ -9,7 +9,8 @@ use giterated_models::{
9 9 Commit, IssueLabel, Repository, RepositoryIssue, RepositorySummary,
10 10 RepositoryTreeEntry, RepositoryVisibility,
11 11 },
12 user::User,
12 settings::{AnySetting, RepositoryDescription, RepositoryVisibilitySetting, Setting},
13 user::{User, UserParseError},
13 14 },
14 15 operation::{
15 16 instance::RepositoryCreateRequest,
@@ -18,12 +19,18 @@ use giterated_models::{
18 19 RepositoryIssuesCountRequest, RepositoryIssuesRequest,
19 20 },
20 21 },
22 values::AnyValue,
21 23 };
24 use serde_json::Value;
22 25 use sqlx::{Either, PgPool};
23 use std::path::{Path, PathBuf};
26 use std::{
27 path::{Path, PathBuf},
28 sync::Arc,
29 };
24 30 use thiserror::Error;
31 use tokio::sync::Mutex;
25 32
26 use super::{IssuesBackend, RepositoryBackend};
33 use super::{IssuesBackend, MetadataBackend, RepositoryBackend};
27 34
28 35 // TODO: Handle this
29 36 //region database structures
@@ -98,6 +105,7 @@ pub struct GitBackend {
98 105 pub pg_pool: PgPool,
99 106 pub repository_folder: String,
100 107 pub instance: Instance,
108 pub settings_provider: Arc<Mutex<dyn MetadataBackend + Send>>,
101 109 }
102 110
103 111 impl GitBackend {
@@ -105,11 +113,13 @@ impl GitBackend {
105 113 pg_pool: &PgPool,
106 114 repository_folder: &str,
107 115 instance: impl ToOwned<Owned = Instance>,
116 settings_provider: Arc<Mutex<dyn MetadataBackend + Send>>,
108 117 ) -> Self {
109 118 Self {
110 119 pg_pool: pg_pool.clone(),
111 120 repository_folder: repository_folder.to_string(),
112 121 instance: instance.to_owned(),
122 settings_provider,
113 123 }
114 124 }
115 125
@@ -286,6 +296,52 @@ impl RepositoryBackend for GitBackend {
286 296 }
287 297 }
288 298
299 async fn get_value(
300 &mut self,
301 repository: &Repository,
302 name: &str,
303 ) -> Result<AnyValue<Repository>, Error> {
304 Ok(match name {
305 "description" => unsafe {
306 AnyValue::from_raw(
307 self.get_setting(repository, RepositoryDescription::name())
308 .await?
309 .0,
310 )
311 },
312 "visibility" => unsafe {
313 AnyValue::from_raw(
314 self.get_setting(repository, RepositoryVisibilitySetting::name())
315 .await?
316 .0,
317 )
318 },
319 _ => {
320 return Err(UserParseError.into());
321 }
322 })
323 }
324 async fn get_setting(
325 &mut self,
326 repository: &Repository,
327 name: &str,
328 ) -> Result<AnySetting, Error> {
329 let mut provider = self.settings_provider.lock().await;
330
331 Ok(provider.repository_get(repository, name).await?)
332 }
333 async fn write_setting(
334 &mut self,
335 repository: &Repository,
336 name: &str,
337 setting: &Value,
338 ) -> Result<(), Error> {
339 let mut provider = self.settings_provider.lock().await;
340
341 provider
342 .repository_write(repository, name, AnySetting(setting.clone()))
343 .await
344 }
289 345 // async fn repository_info(
290 346 // &mut self,
291 347 // requester: Option<&User>,
@@ -519,3 +575,10 @@ impl IssuesBackend for GitBackend {
519 575 todo!()
520 576 }
521 577 }
578
579 #[derive(Debug, sqlx::FromRow)]
580 struct RepositoryMetadata {
581 pub repository: String,
582 pub name: String,
583 pub value: String,
584 }

giterated-daemon/src/backend/mod.rs

View file
@@ -46,6 +46,18 @@ pub trait RepositoryBackend {
46 46 requester: Option<&User>,
47 47 user: &User,
48 48 ) -> Result<Vec<RepositorySummary>, Error>;
49 async fn get_value(
50 &mut self,
51 user: &Repository,
52 name: &str,
53 ) -> Result<AnyValue<Repository>, Error>;
54 async fn get_setting(&mut self, user: &Repository, name: &str) -> Result<AnySetting, Error>;
55 async fn write_setting(
56 &mut self,
57 user: &Repository,
58 name: &str,
59 setting: &Value,
60 ) -> Result<(), Error>;
49 61 async fn exists(&mut self, repository: &Repository) -> Result<bool, Error>;
50 62 }
51 63
@@ -95,18 +107,23 @@ pub trait UserBackend: AuthBackend {
95 107 }
96 108
97 109 #[async_trait::async_trait]
98 pub trait SettingsBackend: Send + Sync {
99 async fn user_get(&mut self, user: &User) -> Result<Vec<(String, Value)>, Error>;
100 async fn user_write(&mut self, user: &User, settings: &[(String, String)])
101 -> Result<(), Error>;
102
110 pub trait MetadataBackend {
111 async fn user_get(&mut self, user: &User, name: &str) -> Result<AnySetting, Error>;
112 async fn user_write(
113 &mut self,
114 user: &User,
115 name: &str,
116 setting: AnySetting,
117 ) -> Result<(), Error>;
103 118 async fn repository_get(
104 119 &mut self,
105 120 repository: &Repository,
106 ) -> Result<Vec<(String, Value)>, Error>;
121 name: &str,
122 ) -> Result<AnySetting, Error>;
107 123 async fn repository_write(
108 124 &mut self,
109 125 repository: &Repository,
110 settings: &[(String, String)],
126 name: &str,
127 setting: AnySetting,
111 128 ) -> Result<(), Error>;
112 129 }

giterated-daemon/src/backend/settings.rs

View file
@@ -1,98 +1,84 @@
1 1 use anyhow::Error;
2 use futures_util::StreamExt;
3 use giterated_models::model::{repository::Repository, user::User};
4 use serde_json::Value;
5 use sqlx::{Either, PgPool};
6 2
7 use super::SettingsBackend;
3 use giterated_models::model::{repository::Repository, settings::AnySetting, user::User};
4
5 use sqlx::PgPool;
6
7 use super::MetadataBackend;
8 8
9 9 pub struct DatabaseSettings {
10 10 pub pg_pool: PgPool,
11 11 }
12 12
13 13 #[async_trait::async_trait]
14 impl SettingsBackend for DatabaseSettings {
15 async fn user_get(&mut self, user: &User) -> Result<Vec<(String, Value)>, Error> {
16 let settings = sqlx::query_as!(
17 UserSettingRow,
18 r#"SELECT * FROM user_settings WHERE username = $1"#,
19 user.username
20 )
21 .fetch_many(&self.pg_pool)
22 .filter_map(|result| async move {
23 if let Ok(Either::Right(row)) = result {
24 Some(row)
25 } else {
26 None
27 }
28 })
29 .filter_map(|row| async move {
30 if let Ok(value) = serde_json::from_str(&row.value) {
31 Some((row.name, value))
32 } else {
33 None
34 }
35 })
36 .collect::<Vec<_>>()
37 .await;
38
39 Ok(settings)
14 impl MetadataBackend for DatabaseSettings {
15 async fn user_get(&mut self, _user: &User, _name: &str) -> Result<AnySetting, Error> {
16 todo!()
40 17 }
41 18 async fn user_write(
42 19 &mut self,
43 user: &User,
44 settings: &[(String, String)],
20 _user: &User,
21 _name: &str,
22 _value: AnySetting,
45 23 ) -> Result<(), Error> {
46 for (name, value) in settings {
47 sqlx::query!("INSERT INTO user_settings VALUES ($1, $2, $3) ON CONFLICT (username, name) DO UPDATE SET value = $3",
48 user.username, name, value)
49 .execute(&self.pg_pool).await?;
50 }
24 // for (name, value) in settings {
25 // sqlx::query!("INSERT INTO user_settings VALUES ($1, $2, $3) ON CONFLICT (username, name) DO UPDATE SET value = $3",
26 // user.username, name, value)
27 // .execute(&self.pg_pool).await?;
28 // }
51 29
52 Ok(())
30 // Ok(())
31
32 todo!()
53 33 }
54 34
55 35 async fn repository_get(
56 36 &mut self,
57 repository: &Repository,
58 ) -> Result<Vec<(String, Value)>, Error> {
59 let settings = sqlx::query_as!(
60 RepositorySettingRow,
61 r#"SELECT * FROM repository_settings WHERE repository = $1"#,
62 repository.to_string()
63 )
64 .fetch_many(&self.pg_pool)
65 .filter_map(|result| async move {
66 if let Ok(Either::Right(row)) = result {
67 Some(row)
68 } else {
69 None
70 }
71 })
72 .filter_map(|row| async move {
73 if let Ok(value) = serde_json::from_str(&row.value) {
74 Some((row.name, value))
75 } else {
76 None
77 }
78 })
79 .collect::<Vec<_>>()
80 .await;
37 _repository: &Repository,
38 _name: &str,
39 ) -> Result<AnySetting, Error> {
40 // let settings = sqlx::query_as!(
41 // RepositorySettingRow,
42 // r#"SELECT * FROM repository_settings WHERE repository = $1"#,
43 // repository.to_string()
44 // )
45 // .fetch_many(&self.pg_pool)
46 // .filter_map(|result| async move {
47 // if let Ok(Either::Right(row)) = result {
48 // Some(row)
49 // } else {
50 // None
51 // }
52 // })
53 // .filter_map(|row| async move {
54 // if let Ok(value) = serde_json::from_str(&row.value) {
55 // Some((row.name, value))
56 // } else {
57 // None
58 // }
59 // })
60 // .collect::<Vec<_>>()
61 // .await;
62
63 // Ok(settings)
81 64
82 Ok(settings)
65 todo!()
83 66 }
84 67 async fn repository_write(
85 68 &mut self,
86 repository: &Repository,
87 settings: &[(String, String)],
69 _repository: &Repository,
70 _name: &str,
71 _value: AnySetting,
88 72 ) -> Result<(), Error> {
89 for (name, value) in settings {
90 sqlx::query!("INSERT INTO repository_settings VALUES ($1, $2, $3) ON CONFLICT (repository, name) DO UPDATE SET value = $3",
91 repository.to_string(), name, value)
92 .execute(&self.pg_pool).await?;
93 }
73 // for (name, value) in settings {
74 // sqlx::query!("INSERT INTO repository_settings VALUES ($1, $2, $3) ON CONFLICT (repository, name) DO UPDATE SET value = $3",
75 // repository.to_string(), name, value)
76 // .execute(&self.pg_pool).await?;
77 // }
78
79 // Ok(())
94 80
95 Ok(())
81 todo!()
96 82 }
97 83 }
98 84

giterated-daemon/src/backend/user.rs

View file
@@ -7,8 +7,10 @@ use argon2::{password_hash::SaltString, Argon2, PasswordHash, PasswordHasher, Pa
7 7 use base64::{engine::general_purpose::STANDARD, Engine as _};
8 8 use giterated_models::{
9 9 model::{
10 authenticated::UserAuthenticationToken, instance::Instance, settings::AnySetting,
11 user::User,
10 authenticated::UserAuthenticationToken,
11 instance::Instance,
12 settings::{AnySetting, Setting, UserBio, UserDisplayName},
13 user::{User, UserParseError},
12 14 },
13 15 operation::instance::{AuthenticationTokenRequest, RegisterAccountRequest},
14 16 values::AnyValue,
@@ -26,13 +28,13 @@ use tokio::sync::Mutex;
26 28
27 29 use crate::authentication::AuthenticationTokenGranter;
28 30
29 use super::{AuthBackend, SettingsBackend, UserBackend};
31 use super::{AuthBackend, MetadataBackend, UserBackend};
30 32
31 33 pub struct UserAuth {
32 34 pub pg_pool: PgPool,
33 35 pub this_instance: Instance,
34 36 pub auth_granter: Arc<Mutex<AuthenticationTokenGranter>>,
35 pub settings_provider: Arc<Mutex<dyn SettingsBackend>>,
37 pub settings_provider: Arc<Mutex<dyn MetadataBackend + Send>>,
36 38 }
37 39
38 40 impl UserAuth {
@@ -40,7 +42,7 @@ impl UserAuth {
40 42 pool: PgPool,
41 43 this_instance: &Instance,
42 44 granter: Arc<Mutex<AuthenticationTokenGranter>>,
43 settings_provider: Arc<Mutex<dyn SettingsBackend>>,
45 settings_provider: Arc<Mutex<dyn MetadataBackend + Send>>,
44 46 ) -> Self {
45 47 Self {
46 48 pg_pool: pool,
@@ -53,20 +55,38 @@ impl UserAuth {
53 55
54 56 #[async_trait::async_trait]
55 57 impl UserBackend for UserAuth {
56 async fn get_value(&mut self, _user: &User, _name: &str) -> Result<AnyValue<User>, Error> {
57 todo!()
58 async fn get_value(&mut self, user: &User, name: &str) -> Result<AnyValue<User>, Error> {
59 Ok(match name {
60 "display_name" => unsafe {
61 AnyValue::from_raw(self.get_setting(user, UserDisplayName::name()).await?.0)
62 },
63 "bio" => unsafe {
64 AnyValue::from_raw(self.get_setting(user, UserBio::name()).await?.0)
65 },
66 _ => {
67 return Err(UserParseError.into());
68 }
69 })
58 70 }
59 async fn get_setting(&mut self, _user: &User, _name: &str) -> Result<AnySetting, Error> {
60 todo!()
71 async fn get_setting(&mut self, user: &User, name: &str) -> Result<AnySetting, Error> {
72 let mut provider = self.settings_provider.lock().await;
73
74 Ok(provider.user_get(user, name).await?)
61 75 }
76
62 77 async fn write_setting(
63 78 &mut self,
64 _user: &User,
65 _name: &str,
66 _setting: &Value,
79 user: &User,
80 name: &str,
81 setting: &Value,
67 82 ) -> Result<(), Error> {
68 todo!()
83 let mut provider = self.settings_provider.lock().await;
84
85 provider
86 .user_write(user, name, AnySetting(setting.clone()))
87 .await
69 88 }
89
70 90 async fn exists(&mut self, user: &User) -> Result<bool, Error> {
71 91 Ok(sqlx::query_as!(
72 92 UserRow,
@@ -210,6 +230,13 @@ struct UserRow {
210 230 pub enc_private_key: Vec<u8>,
211 231 }
212 232
233 #[derive(Debug, sqlx::FromRow)]
234 struct UserValue {
235 pub username: String,
236 pub name: String,
237 pub value: String,
238 }
239
213 240 #[derive(Debug, thiserror::Error)]
214 241 pub enum AuthenticationError {
215 242 #[error("invalid password")]

giterated-daemon/src/connection/wrapper.rs

View file
@@ -18,7 +18,7 @@ use toml::Table;
18 18
19 19 use crate::{
20 20 authentication::AuthenticationTokenGranter,
21 backend::{RepositoryBackend, SettingsBackend, UserBackend},
21 backend::{MetadataBackend, RepositoryBackend, UserBackend},
22 22 database_backend::Foobackend,
23 23 federation::connections::InstanceConnections,
24 24 keys::PublicKeyCache,
@@ -32,7 +32,7 @@ pub async fn connection_wrapper(
32 32 repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>,
33 33 user_backend: Arc<Mutex<dyn UserBackend + Send>>,
34 34 auth_granter: Arc<Mutex<AuthenticationTokenGranter>>,
35 settings_backend: Arc<Mutex<dyn SettingsBackend>>,
35 settings_backend: Arc<Mutex<dyn MetadataBackend + Send>>,
36 36 addr: SocketAddr,
37 37 instance: impl ToOwned<Owned = Instance>,
38 38 instance_connections: Arc<Mutex<InstanceConnections>>,
@@ -210,7 +210,7 @@ pub struct ConnectionState {
210 210 pub repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>,
211 211 pub user_backend: Arc<Mutex<dyn UserBackend + Send>>,
212 212 pub auth_granter: Arc<Mutex<AuthenticationTokenGranter>>,
213 pub settings_backend: Arc<Mutex<dyn SettingsBackend>>,
213 pub settings_backend: Arc<Mutex<dyn MetadataBackend + Send>>,
214 214 pub addr: SocketAddr,
215 215 pub instance: Instance,
216 216 pub handshaked: Arc<AtomicBool>,

giterated-daemon/src/database_backend/handler.rs

View file
@@ -117,16 +117,6 @@ impl<S: Send + Sync + Clone + 'static> OperationWrapper<S> {
117 117 }
118 118 }
119 119
120 fn test_operation(
121 _object: &User,
122 _operation: SetSetting<AnySetting>,
123 _state: (),
124 ) -> Pin<Box<dyn Future<Output = Result<(), OperationError<SetSettingError>>> + Send + 'static>> {
125 todo!()
126 }
127
128 fn foo() {}
129
130 120 pub fn user_get_value(
131 121 object: &User,
132 122 operation: GetValue<AnyValue<User>>,
@@ -147,43 +137,98 @@ pub fn user_get_value(
147 137 }
148 138
149 139 pub fn user_get_setting(
150 _object: &User,
151 _operation: GetSetting<AnySetting>,
152 _state: DatabaseBackend,
140 object: &User,
141 operation: GetSetting<AnySetting>,
142 state: DatabaseBackend,
153 143 ) -> BoxFuture<'static, Result<AnySetting, OperationError<GetSettingError>>> {
154 todo!()
144 let object = object.clone();
145
146 async move {
147 let mut user_backend = state.user_backend.lock().await;
148 let value = user_backend
149 .get_setting(&object, &operation.setting_name)
150 .await
151 .map_err(|e| OperationError::Internal(e.to_string()))?;
152
153 Ok(value)
154 }
155 .boxed()
155 156 }
156 157
157 158 pub fn user_set_setting(
158 _object: &User,
159 _operation: SetSetting<AnySetting>,
160 _state: DatabaseBackend,
159 object: &User,
160 operation: SetSetting<AnySetting>,
161 state: DatabaseBackend,
161 162 ) -> BoxFuture<'static, Result<(), OperationError<SetSettingError>>> {
162 todo!()
163 let object = object.clone();
164
165 async move {
166 let mut user_backend = state.user_backend.lock().await;
167 let value = user_backend
168 .write_setting(&object, &operation.setting_name, &operation.value.0)
169 .await
170 .map_err(|e| OperationError::Internal(e.to_string()))?;
171
172 Ok(value)
173 }
174 .boxed()
163 175 }
164 176
165 177 pub fn repository_get_value(
166 _object: &Repository,
167 _operation: GetValue<AnyValue<Repository>>,
168 _state: DatabaseBackend,
178 object: &Repository,
179 operation: GetValue<AnyValue<Repository>>,
180 state: DatabaseBackend,
169 181 ) -> BoxFuture<'static, Result<AnyValue<Repository>, OperationError<GetValueError>>> {
170 todo!()
182 let object = object.clone();
183
184 async move {
185 let mut repository_backend = state.repository_backend.lock().await;
186 let value = repository_backend
187 .get_value(&object, &operation.value_name)
188 .await
189 .map_err(|e| OperationError::Internal(e.to_string()))?;
190
191 Ok(value)
192 }
193 .boxed()
171 194 }
172 195
173 196 pub fn repository_get_setting(
174 _object: &Repository,
175 _operation: GetSetting<AnySetting>,
176 _state: DatabaseBackend,
197 object: &Repository,
198 operation: GetSetting<AnySetting>,
199 state: DatabaseBackend,
177 200 ) -> BoxFuture<'static, Result<AnySetting, OperationError<GetSettingError>>> {
178 todo!()
201 let object = object.clone();
202
203 async move {
204 let mut repository_backend = state.repository_backend.lock().await;
205 let value = repository_backend
206 .get_setting(&object, &operation.setting_name)
207 .await
208 .map_err(|e| OperationError::Internal(e.to_string()))?;
209
210 Ok(value)
211 }
212 .boxed()
179 213 }
180 214
181 215 pub fn repository_set_setting(
182 _object: &Repository,
183 _operation: SetSetting<AnySetting>,
184 _state: DatabaseBackend,
216 object: &Repository,
217 operation: SetSetting<AnySetting>,
218 state: DatabaseBackend,
185 219 ) -> BoxFuture<'static, Result<(), OperationError<SetSettingError>>> {
186 todo!()
220 let object = object.clone();
221
222 async move {
223 let mut repository_backend = state.repository_backend.lock().await;
224 let value = repository_backend
225 .write_setting(&object, &operation.setting_name, &operation.value.0)
226 .await
227 .map_err(|e| OperationError::Internal(e.to_string()))?;
228
229 Ok(value)
230 }
231 .boxed()
187 232 }
188 233
189 234 pub struct OperationHandlers<S: Send + Sync + Clone> {
@@ -225,14 +270,13 @@ impl<S: Send + Sync + Clone + 'static> OperationHandlers<S> {
225 270 ) -> Result<Vec<u8>, OperationError<Vec<u8>>> {
226 271 if let Some(handler) = self.operations.get_mut(operation_name) {
227 272 handler
228 .handle(
229 AnyObject(serde_json::to_string(object).unwrap()),
230 operation,
231 state,
232 )
273 .handle(AnyObject(object.to_string()), operation, state)
233 274 .await
234 275 } else {
235 panic!()
276 Err(OperationError::Internal(format!(
277 "unknown operation: {}",
278 operation_name
279 )))
236 280 }
237 281 }
238 282 }

giterated-daemon/src/database_backend/mod.rs

View file
@@ -2,7 +2,6 @@ pub mod handler;
2 2
3 3 use std::{str::FromStr, sync::Arc};
4 4
5 use futures_util::TryFutureExt;
6 5 use giterated_models::{
7 6 error::OperationError,
8 7 model::{instance::Instance, repository::Repository, user::User},
@@ -63,9 +62,8 @@ impl ObjectBackend for DatabaseBackend {
63 62 ) -> Result<D::Success, OperationError<D::Failure>> {
64 63 let serialized =
65 64 serde_json::to_value(operation).map_err(|e| OperationError::Internal(e.to_string()))?;
66 let object_name = object.to_string();
67
68 if let Ok(user) = User::from_str(&object_name) {
65 let object = object.to_string();
66 if let Ok(user) = User::from_str(&object) {
69 67 let mut handler = OperationHandlers::default();
70 68
71 69 handler
@@ -93,7 +91,7 @@ impl ObjectBackend for DatabaseBackend {
93 91 )),
94 92 },
95 93 }
96 } else if let Ok(_repository) = Repository::from_str(&object_name) {
94 } else if let Ok(repository) = Repository::from_str(&object) {
97 95 let mut handler = OperationHandlers::default();
98 96
99 97 handler
@@ -101,9 +99,27 @@ impl ObjectBackend for DatabaseBackend {
101 99 .insert(repository_get_setting)
102 100 .insert(repository_set_setting);
103 101
104 // handler.handle(&repository, D::operation_name(), bincode::deserialize(&serialized).unwrap(), DatabaseBackendState).await;
105 todo!()
106 } else if Instance::from_str(&object_name).is_ok() {
102 match handler
103 .handle(
104 &repository,
105 D::operation_name(),
106 serde_json::from_value(serialized.clone()).unwrap(),
107 self.clone(),
108 )
109 .await
110 {
111 Ok(result) => Ok(serde_json::from_slice(&result)
112 .map_err(|e| OperationError::Internal(e.to_string()))?),
113 Err(err) => match err {
114 OperationError::Internal(internal) => Err(OperationError::Internal(internal)),
115 OperationError::Unhandled => Err(OperationError::Unhandled),
116 OperationError::Operation(err) => Err(OperationError::Operation(
117 serde_json::from_slice(&err)
118 .map_err(|e| OperationError::Internal(e.to_string()))?,
119 )),
120 },
121 }
122 } else if serde_json::from_str::<Instance>(&object).is_ok() {
107 123 Err(OperationError::Unhandled)
108 124 } else {
109 125 Err(OperationError::Unhandled)
@@ -159,10 +175,16 @@ impl ObjectBackend for DatabaseBackend {
159 175 }
160 176 }
161 177
178 // These tests verify that the essential handling of the database backend is
179 // functional and correct.
180 #[cfg(test)]
162 181 mod test {
163 182 use std::{str::FromStr, sync::Arc};
164 183
165 184 use anyhow::Error;
185 use giterated_models::model::settings::UserDisplayName;
186 use giterated_models::operation::ObjectBackend;
187 use giterated_models::values::repository::Description;
166 188 use giterated_models::{
167 189 model::{
168 190 authenticated::UserAuthenticationToken,
@@ -176,10 +198,11 @@ mod test {
176 198 AuthenticationTokenRequest, RegisterAccountRequest, RepositoryCreateRequest,
177 199 },
178 200 repository::RepositoryFileInspectRequest,
179 GiteratedObjectValue, ObjectBackend,
201 GiteratedObjectValue,
180 202 },
181 203 values::{user::DisplayName, AnyValue},
182 204 };
205
183 206 use serde_json::Value;
184 207 use tokio::sync::Mutex;
185 208
@@ -199,7 +222,10 @@ mod test {
199 222 .unwrap())
200 223 }
201 224 async fn get_setting(&mut self, _user: &User, _name: &str) -> Result<AnySetting, Error> {
202 todo!()
225 Ok(serde_json::from_slice(
226 &serde_json::to_vec(&DisplayName(String::from("test"))).unwrap(),
227 )
228 .unwrap())
203 229 }
204 230 async fn write_setting(
205 231 &mut self,
@@ -207,7 +233,7 @@ mod test {
207 233 _name: &str,
208 234 _setting: &Value,
209 235 ) -> Result<(), Error> {
210 todo!()
236 Ok(())
211 237 }
212 238 async fn exists(&mut self, user: &User) -> Result<bool, Error> {
213 239 Ok(user == &User::from_str("test_user:test.giterated.dev").unwrap())
@@ -257,8 +283,43 @@ mod test {
257 283 ) -> Result<Vec<RepositorySummary>, Error> {
258 284 todo!()
259 285 }
260 async fn exists(&mut self, _repository: &Repository) -> Result<bool, Error> {
261 todo!()
286
287 async fn get_value(
288 &mut self,
289 _repository: &Repository,
290 _name: &str,
291 ) -> Result<AnyValue<Repository>, Error> {
292 Ok(serde_json::from_slice(
293 &serde_json::to_vec(&Description(String::from("test"))).unwrap(),
294 )
295 .unwrap())
296 }
297 async fn get_setting(
298 &mut self,
299 _repository: &Repository,
300 _name: &str,
301 ) -> Result<AnySetting, Error> {
302 Ok(serde_json::from_slice(
303 &serde_json::to_vec(&Description(String::from("test"))).unwrap(),
304 )
305 .unwrap())
306 }
307 async fn write_setting(
308 &mut self,
309 _repository: &Repository,
310 _name: &str,
311 _setting: &Value,
312 ) -> Result<(), Error> {
313 Ok(())
314 }
315
316 async fn exists(&mut self, repository: &Repository) -> Result<bool, Error> {
317 // Ok(true)
318 Ok(repository
319 == &Repository::from_str(
320 "test_user:test.giterated.dev/[email protected]",
321 )
322 .unwrap())
262 323 }
263 324 }
264 325
@@ -283,4 +344,77 @@ mod test {
283 344 .await
284 345 .expect("object value should have been returned");
285 346 }
347
348 #[tokio::test]
349 async fn test_user_get_setting() {
350 let backend = test_backend();
351
352 let mut user = backend
353 .get_object::<User>("test_user:test.giterated.dev")
354 .await
355 .expect("object should have been returned");
356
357 user.get_setting::<UserDisplayName>()
358 .await
359 .expect("object value should have been returned");
360 }
361
362 #[tokio::test]
363 async fn test_user_set_setting() {
364 let backend = test_backend();
365
366 let mut user = backend
367 .get_object::<User>("test_user:test.giterated.dev")
368 .await
369 .expect("object should have been returned");
370
371 user.set_setting::<UserDisplayName>(UserDisplayName(String::from("test")))
372 .await
373 .expect("object value should have been returned");
374 }
375
376 #[tokio::test]
377 async fn test_respository_get() {
378 let backend = test_backend();
379
380 let mut repository = backend
381 .get_object::<Repository>("test_user:test.giterated.dev/[email protected]")
382 .await
383 .expect("object should have been returned");
384
385 repository
386 .get::<Description>()
387 .await
388 .expect("object value should have been returned");
389 }
390
391 #[tokio::test]
392 async fn test_repository_get_setting() {
393 let backend = test_backend();
394
395 let mut repository = backend
396 .get_object::<Repository>("test_user:test.giterated.dev/[email protected]")
397 .await
398 .expect("object should have been returned");
399
400 repository
401 .get_setting::<Description>()
402 .await
403 .expect("object value should have been returned");
404 }
405
406 #[tokio::test]
407 async fn test_repository_set_setting() {
408 let backend = test_backend();
409
410 let mut repository = backend
411 .get_object::<Repository>("test_user:test.giterated.dev/[email protected]")
412 .await
413 .expect("object should have been returned");
414
415 repository
416 .set_setting::<Description>(Description(String::from("test")))
417 .await
418 .expect("object value should have been returned");
419 }
286 420 }

giterated-daemon/src/main.rs

View file
@@ -62,6 +62,7 @@ async fn main() -> Result<(), Error> {
62 62 ),
63 63 instance: Instance::from_str(config["giterated"]["instance"].as_str().unwrap())
64 64 .unwrap(),
65 settings_provider: settings.clone(),
65 66 }));
66 67
67 68 let token_granter = Arc::new(Mutex::new(AuthenticationTokenGranter {

giterated-models/Cargo.toml

View file
@@ -23,6 +23,7 @@ toml = { version = "0.7" }
23 23 git2 = "0.17"
24 24 chrono = { version = "0.4", features = [ "serde" ] }
25 25 async-trait = "0.1"
26 serde_with = "3.3.0"
26 27
27 28 # Git backend
28 29 sqlx = { version = "0.7", default-features = false, features = [ "macros", "chrono" ] }

giterated-models/src/model/instance.rs

View file
@@ -1,4 +1,4 @@
1 use std::str::FromStr;
1 use std::{fmt::Display, str::FromStr};
2 2
3 3 use serde::{Deserialize, Serialize};
4 4 use thiserror::Error;
@@ -42,9 +42,9 @@ impl GiteratedObject for Instance {
42 42 }
43 43 }
44 44
45 impl ToString for Instance {
46 fn to_string(&self) -> String {
47 self.url.clone()
45 impl Display for Instance {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 f.write_str(&self.url)
48 48 }
49 49 }
50 50

giterated-models/src/model/repository.rs

View file
@@ -21,6 +21,9 @@ use super::{instance::Instance, user::User};
21 21 /// be valid:
22 22 ///
23 23 /// ```
24 //# use giterated_models::model::repository::Repository;
25 //# use giterated_models::model::instance::Instance;
26 //# use giterated_models::model::user::User;
24 27 /// let repository = Repository {
25 28 /// owner: User::from_str("barson:giterated.dev").unwrap(),
26 29 /// name: String::from("foo"),
@@ -38,24 +41,24 @@ pub struct Repository {
38 41 pub instance: Instance,
39 42 }
40 43
44 impl Display for Repository {
45 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
46 f.write_str(&format!("{}/{}@{}", self.owner, self.name, self.instance))
47 }
48 }
49
41 50 impl GiteratedObject for Repository {
42 51 fn object_name() -> &'static str {
43 52 "repository"
44 53 }
45 54
46 55 fn from_object_str(object_str: &str) -> Result<Self, anyhow::Error> {
47 Ok(Repository::from_str(object_str).unwrap())
48 }
49 }
50
51 impl ToString for Repository {
52 fn to_string(&self) -> String {
53 format!("{}/{}@{}", self.owner, self.name, self.instance.to_string())
56 Ok(Repository::from_str(object_str)?)
54 57 }
55 58 }
56 59
57 60 impl TryFrom<String> for Repository {
58 type Error = ();
61 type Error = RepositoryParseError;
59 62
60 63 fn try_from(value: String) -> Result<Self, Self::Error> {
61 64 Self::from_str(&value)
@@ -63,7 +66,7 @@ impl TryFrom<String> for Repository {
63 66 }
64 67
65 68 impl FromStr for Repository {
66 type Err = ();
69 type Err = RepositoryParseError;
67 70
68 71 fn from_str(s: &str) -> Result<Self, Self::Err> {
69 72 let mut by_ampersand = s.split('@');
@@ -81,6 +84,9 @@ impl FromStr for Repository {
81 84 }
82 85 }
83 86
87 #[derive(Debug, thiserror::Error)]
88 pub enum RepositoryParseError {}
89
84 90 /// Visibility of the repository to the general eye
85 91 #[derive(PartialEq, Eq, Debug, Hash, Serialize, Deserialize, Clone, sqlx::Type)]
86 92 #[sqlx(type_name = "visibility", rename_all = "lowercase")]

giterated-models/src/model/settings.rs

View file
@@ -6,7 +6,7 @@ pub trait Setting: Serialize + DeserializeOwned {
6 6 }
7 7
8 8 #[derive(Debug, Clone, Serialize, Deserialize)]
9 pub struct AnySetting(Value);
9 pub struct AnySetting(pub Value);
10 10
11 11 impl Setting for AnySetting {
12 12 fn name() -> &'static str {
@@ -23,7 +23,7 @@ impl Setting for UserBio {
23 23 }
24 24 }
25 25
26 #[derive(Debug, Serialize, Deserialize)]
26 #[derive(Debug, Serialize, Deserialize, Clone)]
27 27 pub struct UserDisplayName(pub String);
28 28
29 29 impl Setting for UserDisplayName {
@@ -40,3 +40,21 @@ impl Setting for UserDisplayImage {
40 40 "Profile Image"
41 41 }
42 42 }
43
44 #[derive(Debug, Serialize, Deserialize)]
45 pub struct RepositoryDescription(pub String);
46
47 impl Setting for RepositoryDescription {
48 fn name() -> &'static str {
49 "Repository Description"
50 }
51 }
52
53 #[derive(Debug, Serialize, Deserialize)]
54 pub struct RepositoryVisibilitySetting(pub String);
55
56 impl Setting for RepositoryVisibilitySetting {
57 fn name() -> &'static str {
58 "Repository Visibility"
59 }
60 }

giterated-models/src/model/user.rs

View file
@@ -57,9 +57,13 @@ impl From<String> for User {
57 57 }
58 58
59 59 impl FromStr for User {
60 type Err = ();
60 type Err = UserParseError;
61 61
62 62 fn from_str(s: &str) -> Result<Self, Self::Err> {
63 if s.contains('/') {
64 return Err(UserParseError);
65 }
66
63 67 let mut colon_split = s.split(':');
64 68 let username = colon_split.next().unwrap().to_string();
65 69 let instance = Instance::from_str(colon_split.next().unwrap()).unwrap();
@@ -68,6 +72,10 @@ impl FromStr for User {
68 72 }
69 73 }
70 74
75 #[derive(thiserror::Error, Debug)]
76 #[error("failed to parse user")]
77 pub struct UserParseError;
78
71 79 #[derive(Clone, Debug, Serialize, Deserialize)]
72 80 pub struct Password(pub String);
73 81

giterated-models/src/operation/mod.rs

View file
@@ -1,4 +1,10 @@
1 use std::{any::type_name, fmt::Debug, marker::PhantomData};
1 use std::{
2 any::type_name,
3 convert::Infallible,
4 fmt::{Debug, Display},
5 marker::PhantomData,
6 str::FromStr,
7 };
2 8
3 9 use anyhow::Error;
4 10 use serde::{de::DeserializeOwned, Deserialize, Serialize};
@@ -14,7 +20,7 @@ pub mod instance;
14 20 pub mod repository;
15 21 pub mod user;
16 22
17 pub trait GiteratedObject: Send + Serialize + DeserializeOwned + ToString {
23 pub trait GiteratedObject: Send + Display + FromStr {
18 24 fn object_name() -> &'static str;
19 25
20 26 fn from_object_str(object_str: &str) -> Result<Self, Error>;
@@ -121,11 +127,38 @@ impl<O: GiteratedObject + Send, V: GiteratedObjectValue<Object = O> + Send> Gite
121 127 #[derive(Serialize)]
122 128 #[serde(bound(deserialize = "O: GiteratedObject, V: GiteratedOperation<O>"))]
123 129 pub struct GiteratedMessage<O: GiteratedObject, V: GiteratedOperation<O>> {
130 #[serde(with = "string")]
124 131 pub object: O,
125 132 pub operation: String,
126 133 pub payload: V,
127 134 }
128 135
136 mod string {
137 use std::fmt::Display;
138 use std::str::FromStr;
139
140 use serde::{de, Deserialize, Deserializer, Serializer};
141
142 pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
143 where
144 T: Display,
145 S: Serializer,
146 {
147 serializer.collect_str(value)
148 }
149
150 pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
151 where
152 T: FromStr,
153 T::Err: Display,
154 D: Deserializer<'de>,
155 {
156 String::deserialize(deserializer)?
157 .parse()
158 .map_err(de::Error::custom)
159 }
160 }
161
129 162 impl GiteratedMessage<AnyObject, AnyOperation> {
130 163 pub fn try_into<O: GiteratedObject, V: GiteratedOperation<O>>(
131 164 &self,
@@ -187,9 +220,17 @@ impl GiteratedObject for AnyObject {
187 220 }
188 221 }
189 222
190 impl ToString for AnyObject {
191 fn to_string(&self) -> String {
192 self.0.to_string()
223 impl Display for AnyObject {
224 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225 f.write_str(&self.0)
226 }
227 }
228
229 impl FromStr for AnyObject {
230 type Err = Infallible;
231
232 fn from_str(s: &str) -> Result<Self, Self::Err> {
233 Ok(Self(s.to_owned()))
193 234 }
194 235 }
195 236

giterated-models/src/values/mod.rs

View file
@@ -39,6 +39,10 @@ pub struct GetSetting<S: Setting + std::fmt::Debug + Clone> {
39 39 impl<O: GiteratedObject, S: Setting + Send + DeserializeOwned + Debug + Clone> GiteratedOperation<O>
40 40 for GetSetting<S>
41 41 {
42 fn operation_name() -> &'static str {
43 "get_setting"
44 }
45
42 46 type Success = S;
43 47
44 48 type Failure = GetSettingError;
@@ -54,6 +58,10 @@ pub struct SetSetting<S: Setting> {
54 58 }
55 59
56 60 impl<O: GiteratedObject, S: Setting + Send> GiteratedOperation<O> for SetSetting<S> {
61 fn operation_name() -> &'static str {
62 "set_setting"
63 }
64
57 65 type Success = ();
58 66
59 67 type Failure = SetSettingError;
@@ -70,6 +78,15 @@ pub struct AnyValue<O> {
70 78 _marker: PhantomData<O>,
71 79 }
72 80
81 impl<O: GiteratedObject> AnyValue<O> {
82 pub unsafe fn from_raw(value: Value) -> Self {
83 Self {
84 value,
85 _marker: Default::default(),
86 }
87 }
88 }
89
73 90 impl<O: GiteratedObject> GiteratedObjectValue for AnyValue<O> {
74 91 type Object = O;
75 92

giterated-models/src/values/repository.rs

View file
@@ -1,7 +1,10 @@
1 1 use serde::{Deserialize, Serialize};
2 2
3 3 use crate::{
4 model::repository::{Repository, RepositoryVisibility},
4 model::{
5 repository::{Repository, RepositoryVisibility},
6 settings::Setting,
7 },
5 8 operation::GiteratedObjectValue,
6 9 };
7 10
@@ -28,6 +31,12 @@ impl GiteratedObjectValue for Description {
28 31 }
29 32 }
30 33
34 impl Setting for Description {
35 fn name() -> &'static str {
36 "description"
37 }
38 }
39
31 40 #[derive(Debug, Hash, Clone, PartialEq, Eq, Serialize, Deserialize)]
32 41 pub struct Visibility(pub RepositoryVisibility);
33 42

migrations/20230913232554_repository_metadata.sql

View file
@@ -0,0 +1 @@
1 -- Add migration script here