Add repository settings
parent: tbd commit: f8eaf38
Showing 13 changed files with 258 insertions and 95 deletions
giterated-daemon/migrations/20230905192721_repository_settings.sql
@@ -0,0 +1,8 @@ | ||
1 | CREATE TABLE IF NOT EXISTS repository_settings | |
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 ON repository_settings (repository, name); | |
8 | \ No newline at end of file |
giterated-daemon/src/backend/mod.rs
@@ -1,6 +1,7 @@ | ||
1 | 1 | pub mod discovery; |
2 | 2 | pub mod git; |
3 | 3 | pub mod github; |
4 | pub mod settings; | |
4 | 5 | pub mod user; |
5 | 6 | |
6 | 7 | use anyhow::Error; |
@@ -26,7 +27,7 @@ use giterated_models::{ | ||
26 | 27 | }, |
27 | 28 | }, |
28 | 29 | model::{ |
29 | repository::{RepositorySummary, RepositoryView}, | |
30 | repository::{Repository, RepositorySummary, RepositoryView}, | |
30 | 31 | user::User, |
31 | 32 | }, |
32 | 33 | }; |
@@ -100,11 +101,21 @@ pub trait UserBackend: AuthBackend { | ||
100 | 101 | |
101 | 102 | async fn bio(&mut self, request: UserBioRequest) -> Result<UserBioResponse, Error>; |
102 | 103 | async fn exists(&mut self, user: &User) -> Result<bool, Error>; |
104 | } | |
105 | ||
106 | #[async_trait::async_trait] | |
107 | pub trait SettingsBackend: Send + Sync { | |
108 | async fn user_get(&mut self, user: &User) -> Result<Vec<(String, Value)>, Error>; | |
109 | async fn user_write(&mut self, user: &User, settings: &[(String, String)]) | |
110 | -> Result<(), Error>; | |
103 | 111 | |
104 | async fn settings(&mut self, user: &User) -> Result<Vec<(String, Value)>, Error>; | |
105 | async fn write_settings( | |
112 | async fn repository_get( | |
106 | 113 | &mut self, |
107 | user: &User, | |
114 | repository: &Repository, | |
115 | ) -> Result<Vec<(String, Value)>, Error>; | |
116 | async fn repository_write( | |
117 | &mut self, | |
118 | repository: &Repository, | |
108 | 119 | settings: &[(String, String)], |
109 | 120 | ) -> Result<(), Error>; |
110 | 121 | } |
giterated-daemon/src/backend/settings.rs
@@ -0,0 +1,113 @@ | ||
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 | ||
7 | use super::SettingsBackend; | |
8 | ||
9 | pub struct DatabaseSettings { | |
10 | pub pg_pool: PgPool, | |
11 | } | |
12 | ||
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) | |
40 | } | |
41 | async fn user_write( | |
42 | &mut self, | |
43 | user: &User, | |
44 | settings: &[(String, String)], | |
45 | ) -> 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 | } | |
51 | ||
52 | Ok(()) | |
53 | } | |
54 | ||
55 | async fn repository_get( | |
56 | &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; | |
81 | ||
82 | Ok(settings) | |
83 | } | |
84 | async fn repository_write( | |
85 | &mut self, | |
86 | repository: &Repository, | |
87 | settings: &[(String, String)], | |
88 | ) -> 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 | } | |
94 | ||
95 | Ok(()) | |
96 | } | |
97 | } | |
98 | ||
99 | #[allow(unused)] | |
100 | #[derive(Debug, sqlx::FromRow)] | |
101 | struct UserSettingRow { | |
102 | pub username: String, | |
103 | pub name: String, | |
104 | pub value: String, | |
105 | } | |
106 | ||
107 | #[allow(unused)] | |
108 | #[derive(Debug, sqlx::FromRow)] | |
109 | struct RepositorySettingRow { | |
110 | pub repository: String, | |
111 | pub name: String, | |
112 | pub value: String, | |
113 | } |
giterated-daemon/src/backend/user.rs
@@ -28,18 +28,19 @@ use rsa::{ | ||
28 | 28 | rand_core::OsRng, |
29 | 29 | RsaPrivateKey, RsaPublicKey, |
30 | 30 | }; |
31 | use serde_json::Value; | |
32 | use sqlx::{Either, PgPool}; | |
31 | ||
32 | use sqlx::PgPool; | |
33 | 33 | use tokio::sync::Mutex; |
34 | 34 | |
35 | 35 | use crate::authentication::AuthenticationTokenGranter; |
36 | 36 | |
37 | use super::{AuthBackend, UserBackend}; | |
37 | use super::{AuthBackend, SettingsBackend, UserBackend}; | |
38 | 38 | |
39 | 39 | pub struct UserAuth { |
40 | 40 | pub pg_pool: PgPool, |
41 | 41 | pub this_instance: Instance, |
42 | 42 | pub auth_granter: Arc<Mutex<AuthenticationTokenGranter>>, |
43 | pub settings_provider: Arc<Mutex<dyn SettingsBackend>>, | |
43 | 44 | } |
44 | 45 | |
45 | 46 | impl UserAuth { |
@@ -47,11 +48,13 @@ impl UserAuth { | ||
47 | 48 | pool: PgPool, |
48 | 49 | this_instance: &Instance, |
49 | 50 | granter: Arc<Mutex<AuthenticationTokenGranter>>, |
51 | settings_provider: Arc<Mutex<dyn SettingsBackend>>, | |
50 | 52 | ) -> Self { |
51 | 53 | Self { |
52 | 54 | pg_pool: pool, |
53 | 55 | this_instance: this_instance.clone(), |
54 | 56 | auth_granter: granter, |
57 | settings_provider, | |
55 | 58 | } |
56 | 59 | } |
57 | 60 | } |
@@ -62,7 +65,9 @@ impl UserBackend for UserAuth { | ||
62 | 65 | &mut self, |
63 | 66 | request: UserDisplayNameRequest, |
64 | 67 | ) -> Result<UserDisplayNameResponse, Error> { |
65 | let settings = self.settings(&request.user).await?; | |
68 | let mut settings_backend = self.settings_provider.lock().await; | |
69 | let settings = settings_backend.user_get(&request.user).await?; | |
70 | drop(settings_backend); | |
66 | 71 | |
67 | 72 | let name = settings |
68 | 73 | .iter() |
@@ -83,7 +88,10 @@ impl UserBackend for UserAuth { | ||
83 | 88 | &mut self, |
84 | 89 | request: UserDisplayImageRequest, |
85 | 90 | ) -> Result<UserDisplayImageResponse, anyhow::Error> { |
86 | let settings = self.settings(&request.user).await?; | |
91 | let mut settings_backend: tokio::sync::MutexGuard<'_, dyn SettingsBackend> = | |
92 | self.settings_provider.lock().await; | |
93 | let settings = settings_backend.user_get(&request.user).await?; | |
94 | drop(settings_backend); | |
87 | 95 | |
88 | 96 | let image = settings |
89 | 97 | .iter() |
@@ -101,7 +109,9 @@ impl UserBackend for UserAuth { | ||
101 | 109 | } |
102 | 110 | |
103 | 111 | async fn bio(&mut self, request: UserBioRequest) -> Result<UserBioResponse, Error> { |
104 | let settings = self.settings(&request.user).await?; | |
112 | let mut settings_backend = self.settings_provider.lock().await; | |
113 | let settings = settings_backend.user_get(&request.user).await?; | |
114 | drop(settings_backend); | |
105 | 115 | |
106 | 116 | let bio = settings |
107 | 117 | .iter() |
@@ -126,47 +136,6 @@ impl UserBackend for UserAuth { | ||
126 | 136 | .await |
127 | 137 | .is_err()) |
128 | 138 | } |
129 | ||
130 | async fn settings(&mut self, user: &User) -> Result<Vec<(String, Value)>, Error> { | |
131 | let settings = sqlx::query_as!( | |
132 | UserSettingRow, | |
133 | r#"SELECT * FROM user_settings WHERE username = $1"#, | |
134 | user.username | |
135 | ) | |
136 | .fetch_many(&self.pg_pool) | |
137 | .filter_map(|result| async move { | |
138 | if let Ok(Either::Right(row)) = result { | |
139 | Some(row) | |
140 | } else { | |
141 | None | |
142 | } | |
143 | }) | |
144 | .filter_map(|row| async move { | |
145 | if let Ok(value) = serde_json::from_str(&row.value) { | |
146 | Some((row.name, value)) | |
147 | } else { | |
148 | None | |
149 | } | |
150 | }) | |
151 | .collect::<Vec<_>>() | |
152 | .await; | |
153 | ||
154 | Ok(settings) | |
155 | } | |
156 | ||
157 | async fn write_settings( | |
158 | &mut self, | |
159 | user: &User, | |
160 | settings: &[(String, String)], | |
161 | ) -> Result<(), Error> { | |
162 | for (name, value) in settings { | |
163 | sqlx::query!("INSERT INTO user_settings VALUES ($1, $2, $3) ON CONFLICT (username, name) DO UPDATE SET value = $3", | |
164 | user.username, name, value) | |
165 | .execute(&self.pg_pool).await?; | |
166 | } | |
167 | ||
168 | Ok(()) | |
169 | } | |
170 | 139 | } |
171 | 140 | |
172 | 141 | #[async_trait::async_trait] |
@@ -268,11 +237,3 @@ struct UserRow { | ||
268 | 237 | pub public_key: String, |
269 | 238 | pub enc_private_key: Vec<u8>, |
270 | 239 | } |
271 | ||
272 | #[allow(unused)] | |
273 | #[derive(Debug, sqlx::FromRow)] | |
274 | struct UserSettingRow { | |
275 | pub username: String, | |
276 | pub name: String, | |
277 | pub value: String, | |
278 | } |
giterated-daemon/src/connection/forwarded.rs
@@ -1,10 +1,9 @@ | ||
1 | 1 | use futures_util::{SinkExt, StreamExt}; |
2 | 2 | use giterated_api::DaemonConnectionPool; |
3 | 3 | use giterated_models::{ |
4 | messages::error::ConnectionError, | |
5 | model::authenticated::{Authenticated, AuthenticatedPayload}, | |
4 | messages::error::ConnectionError, model::authenticated::AuthenticatedPayload, | |
6 | 5 | }; |
7 | use serde::Serialize; | |
6 | ||
8 | 7 | use tokio_tungstenite::tungstenite::Message; |
9 | 8 | |
10 | 9 | pub async fn wrap_forwarded(pool: &DaemonConnectionPool, message: AuthenticatedPayload) -> Message { |
giterated-daemon/src/connection/handshake.rs
@@ -1,4 +1,4 @@ | ||
1 | use std::{str::FromStr, sync::atomic::Ordering}; | |
1 | use std::sync::atomic::Ordering; | |
2 | 2 | |
3 | 3 | use anyhow::Error; |
4 | 4 | use giterated_models::messages::handshake::{ |
@@ -7,9 +7,8 @@ use giterated_models::messages::handshake::{ | ||
7 | 7 | use semver::Version; |
8 | 8 | |
9 | 9 | use crate::{ |
10 | connection::ConnectionError, | |
11 | message::{HandshakeMessage, Message, MessageHandler, NetworkMessage, State}, | |
12 | validate_version, version, | |
10 | message::{HandshakeMessage, MessageHandler, NetworkMessage, State}, | |
11 | version, | |
13 | 12 | }; |
14 | 13 | |
15 | 14 | use super::{wrapper::ConnectionState, HandlerUnhandled}; |
@@ -42,7 +41,7 @@ pub async fn handshake_handle( | ||
42 | 41 | } |
43 | 42 | |
44 | 43 | async fn initiate_handshake( |
45 | HandshakeMessage(initiation): HandshakeMessage<InitiateHandshake>, | |
44 | HandshakeMessage(_initiation): HandshakeMessage<InitiateHandshake>, | |
46 | 45 | State(connection_state): State<ConnectionState>, |
47 | 46 | ) -> Result<(), HandshakeError> { |
48 | 47 | info!("meow!"); |
@@ -82,7 +81,7 @@ async fn initiate_handshake( | ||
82 | 81 | } |
83 | 82 | |
84 | 83 | async fn handshake_response( |
85 | HandshakeMessage(initiation): HandshakeMessage<HandshakeResponse>, | |
84 | HandshakeMessage(_initiation): HandshakeMessage<HandshakeResponse>, | |
86 | 85 | State(connection_state): State<ConnectionState>, |
87 | 86 | ) -> Result<(), HandshakeError> { |
88 | 87 | connection_state |
@@ -115,7 +114,7 @@ async fn handshake_response( | ||
115 | 114 | } |
116 | 115 | |
117 | 116 | async fn handshake_finalize( |
118 | HandshakeMessage(finalize): HandshakeMessage<HandshakeFinalize>, | |
117 | HandshakeMessage(_finalize): HandshakeMessage<HandshakeFinalize>, | |
119 | 118 | State(connection_state): State<ConnectionState>, |
120 | 119 | ) -> Result<(), HandshakeError> { |
121 | 120 | connection_state.handshaked.store(true, Ordering::SeqCst); |
giterated-daemon/src/connection/user.rs
@@ -138,10 +138,9 @@ async fn user_settings( | ||
138 | 138 | return Err(UserError::InvalidUser(request.user)); |
139 | 139 | } |
140 | 140 | |
141 | let mut user_backend = connection_state.user_backend.lock().await; | |
142 | let mut settings = user_backend.settings(&request.user).await?; | |
143 | ||
144 | drop(user_backend); | |
141 | let mut settings_backend = connection_state.settings_backend.lock().await; | |
142 | let mut settings = settings_backend.user_get(&requesting_user).await?; | |
143 | drop(settings_backend); | |
145 | 144 | |
146 | 145 | let response = UserSettingsResponse { |
147 | 146 | settings: settings.drain(..).collect(), |
@@ -161,12 +160,11 @@ async fn write_user_settings( | ||
161 | 160 | return Err(UserError::InvalidUser(request.user)); |
162 | 161 | } |
163 | 162 | |
164 | let mut user_backend = connection_state.user_backend.lock().await; | |
165 | user_backend | |
166 | .write_settings(&request.user, &request.settings) | |
163 | let mut settings_backend = connection_state.settings_backend.lock().await; | |
164 | settings_backend | |
165 | .user_write(&request.user, &request.settings) | |
167 | 166 | .await?; |
168 | ||
169 | drop(user_backend); | |
167 | drop(settings_backend); | |
170 | 168 | |
171 | 169 | let response = UserWriteSettingsResponse {}; |
172 | 170 |
giterated-daemon/src/connection/wrapper.rs
@@ -1,5 +1,4 @@ | ||
1 | 1 | use std::{ |
2 | collections::HashMap, | |
3 | 2 | net::SocketAddr, |
4 | 3 | sync::{ |
5 | 4 | atomic::{AtomicBool, Ordering}, |
@@ -11,23 +10,17 @@ use anyhow::Error; | ||
11 | 10 | use futures_util::{SinkExt, StreamExt}; |
12 | 11 | use giterated_models::{ |
13 | 12 | messages::error::ConnectionError, |
14 | model::{ | |
15 | authenticated::{Authenticated, AuthenticatedPayload}, | |
16 | instance::Instance, | |
17 | }, | |
13 | model::{authenticated::AuthenticatedPayload, instance::Instance}, | |
18 | 14 | }; |
19 | use rsa::RsaPublicKey; | |
15 | ||
20 | 16 | use serde::Serialize; |
21 | use serde_json::Value; | |
22 | use tokio::{ | |
23 | net::TcpStream, | |
24 | sync::{Mutex, RwLock}, | |
25 | }; | |
17 | ||
18 | use tokio::{net::TcpStream, sync::Mutex}; | |
26 | 19 | use tokio_tungstenite::{tungstenite::Message, WebSocketStream}; |
27 | 20 | |
28 | 21 | use crate::{ |
29 | 22 | authentication::AuthenticationTokenGranter, |
30 | backend::{RepositoryBackend, UserBackend}, | |
23 | backend::{RepositoryBackend, SettingsBackend, UserBackend}, | |
31 | 24 | connection::forwarded::wrap_forwarded, |
32 | 25 | federation::connections::InstanceConnections, |
33 | 26 | keys::PublicKeyCache, |
@@ -45,6 +38,7 @@ pub async fn connection_wrapper( | ||
45 | 38 | repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>, |
46 | 39 | user_backend: Arc<Mutex<dyn UserBackend + Send>>, |
47 | 40 | auth_granter: Arc<Mutex<AuthenticationTokenGranter>>, |
41 | settings_backend: Arc<Mutex<dyn SettingsBackend>>, | |
48 | 42 | addr: SocketAddr, |
49 | 43 | instance: impl ToOwned<Owned = Instance>, |
50 | 44 | instance_connections: Arc<Mutex<InstanceConnections>>, |
@@ -55,6 +49,7 @@ pub async fn connection_wrapper( | ||
55 | 49 | repository_backend, |
56 | 50 | user_backend, |
57 | 51 | auth_granter, |
52 | settings_backend, | |
58 | 53 | addr, |
59 | 54 | instance: instance.to_owned(), |
60 | 55 | handshaked: Arc::new(AtomicBool::new(false)), |
@@ -179,6 +174,7 @@ pub struct ConnectionState { | ||
179 | 174 | pub repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>>, |
180 | 175 | pub user_backend: Arc<Mutex<dyn UserBackend + Send>>, |
181 | 176 | pub auth_granter: Arc<Mutex<AuthenticationTokenGranter>>, |
177 | pub settings_backend: Arc<Mutex<dyn SettingsBackend>>, | |
182 | 178 | pub addr: SocketAddr, |
183 | 179 | pub instance: Instance, |
184 | 180 | pub handshaked: Arc<AtomicBool>, |
giterated-daemon/src/main.rs
@@ -2,7 +2,9 @@ use anyhow::Error; | ||
2 | 2 | use connection::{Connections, RawConnection}; |
3 | 3 | use giterated_daemon::{ |
4 | 4 | authentication::AuthenticationTokenGranter, |
5 | backend::{git::GitBackend, user::UserAuth, RepositoryBackend, UserBackend}, | |
5 | backend::{ | |
6 | git::GitBackend, settings::DatabaseSettings, user::UserAuth, RepositoryBackend, UserBackend, | |
7 | }, | |
6 | 8 | connection::{self, wrapper::connection_wrapper}, |
7 | 9 | federation::connections::InstanceConnections, |
8 | 10 | }; |
@@ -46,6 +48,10 @@ async fn main() -> Result<(), Error> { | ||
46 | 48 | sqlx::migrate!().run(&db_pool).await?; |
47 | 49 | info!("Connected"); |
48 | 50 | |
51 | let settings = Arc::new(Mutex::new(DatabaseSettings { | |
52 | pg_pool: db_pool.clone(), | |
53 | })); | |
54 | ||
49 | 55 | let repository_backend: Arc<Mutex<dyn RepositoryBackend + Send>> = |
50 | 56 | Arc::new(Mutex::new(GitBackend { |
51 | 57 | pg_pool: db_pool.clone(), |
@@ -66,6 +72,7 @@ async fn main() -> Result<(), Error> { | ||
66 | 72 | db_pool.clone(), |
67 | 73 | &Instance::from_str("giterated.dev").unwrap(), |
68 | 74 | token_granter.clone(), |
75 | settings.clone(), | |
69 | 76 | ))); |
70 | 77 | |
71 | 78 | info!("Connected"); |
@@ -106,6 +113,7 @@ async fn main() -> Result<(), Error> { | ||
106 | 113 | repository_backend.clone(), |
107 | 114 | user_backend.clone(), |
108 | 115 | token_granter.clone(), |
116 | settings.clone(), | |
109 | 117 | address, |
110 | 118 | Instance::from_str("giterated.dev").unwrap(), |
111 | 119 | instance_connections.clone(), |
giterated-daemon/src/message.rs
@@ -1,9 +1,9 @@ | ||
1 | use std::{collections::HashMap, fmt::Debug, ops::Deref}; | |
1 | use std::{fmt::Debug, ops::Deref}; | |
2 | 2 | |
3 | 3 | use anyhow::Error; |
4 | 4 | use futures_util::Future; |
5 | 5 | use giterated_models::model::{ |
6 | authenticated::{Authenticated, AuthenticatedPayload, AuthenticationSource, UserTokenMetadata}, | |
6 | authenticated::{AuthenticatedPayload, AuthenticationSource, UserTokenMetadata}, | |
7 | 7 | instance::Instance, |
8 | 8 | user::User, |
9 | 9 | }; |
@@ -16,7 +16,6 @@ use rsa::{ | ||
16 | 16 | RsaPublicKey, |
17 | 17 | }; |
18 | 18 | use serde::{de::DeserializeOwned, Serialize}; |
19 | use serde_json::Value; | |
20 | 19 | |
21 | 20 | use crate::connection::wrapper::ConnectionState; |
22 | 21 |
giterated-models/src/messages/repository.rs
@@ -1,6 +1,10 @@ | ||
1 | use std::collections::HashMap; | |
2 | ||
3 | use serde::de::DeserializeOwned; | |
1 | 4 | use serde::{Deserialize, Serialize}; |
2 | 5 | |
3 | 6 | use crate::model::repository::RepositoryVisibility; |
7 | use crate::model::settings::Setting; | |
4 | 8 | use crate::model::{ |
5 | 9 | repository::{Commit, Repository, RepositoryTreeEntry}, |
6 | 10 | user::User, |
@@ -144,3 +148,62 @@ pub struct RepositoryInfoRequest { | ||
144 | 148 | /// Tree path being requested |
145 | 149 | pub path: Option<String>, |
146 | 150 | } |
151 | ||
152 | ||
153 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | |
154 | pub struct RepositorySettingsRequest { | |
155 | pub repository: Repository, | |
156 | } | |
157 | ||
158 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Default, Deserialize)] | |
159 | pub struct RepositorySettingsResponse { | |
160 | pub settings: HashMap<String, serde_json::Value>, | |
161 | } | |
162 | ||
163 | impl RepositorySettingsResponse { | |
164 | pub fn try_get<S: Setting + DeserializeOwned>(&self) -> Option<S> { | |
165 | let setting_member = self | |
166 | .settings | |
167 | .iter() | |
168 | .filter(|(key, _)| key.as_str() == S::name()) | |
169 | .next()?; | |
170 | ||
171 | serde_json::from_value(setting_member.1.clone()).ok() | |
172 | } | |
173 | } | |
174 | ||
175 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | |
176 | pub struct RepositoryWriteSettingsRequest { | |
177 | pub repository: Repository, | |
178 | pub settings: Vec<(String, String)>, | |
179 | } | |
180 | ||
181 | impl RepositoryWriteSettingsRequest { | |
182 | pub fn new(repository: impl ToOwned<Owned = Repository>) -> Self { | |
183 | Self { | |
184 | repository: repository.to_owned(), | |
185 | settings: Default::default(), | |
186 | } | |
187 | } | |
188 | ||
189 | pub fn set<S: Setting>(&mut self, setting: S) { | |
190 | self.settings.push(( | |
191 | S::name().to_string(), | |
192 | serde_json::to_string(&setting).unwrap(), | |
193 | )); | |
194 | } | |
195 | pub fn try_get<S: Setting + DeserializeOwned>(&self) -> Option<S> { | |
196 | let setting_member = self | |
197 | .settings | |
198 | .iter() | |
199 | .filter(|(key, _)| key.as_str() == S::name()) | |
200 | .next()?; | |
201 | ||
202 | serde_json::from_str(&setting_member.1).ok() | |
203 | } | |
204 | } | |
205 | ||
206 | #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | |
207 | pub struct UserWriteSettingsResponse { | |
208 | // IDK? | |
209 | } |
giterated-models/src/model/authenticated.rs
@@ -1,4 +1,4 @@ | ||
1 | use std::{any::type_name, fmt::Debug, ops::Deref}; | |
1 | use std::{any::type_name, fmt::Debug}; | |
2 | 2 | |
3 | 3 | use rsa::{ |
4 | 4 | pkcs1::DecodeRsaPrivateKey, |
giterated-models/src/model/repository.rs
@@ -42,6 +42,14 @@ impl ToString for Repository { | ||
42 | 42 | } |
43 | 43 | } |
44 | 44 | |
45 | impl TryFrom<String> for Repository { | |
46 | type Error = (); | |
47 | ||
48 | fn try_from(value: String) -> Result<Self, Self::Error> { | |
49 | Self::from_str(&value) | |
50 | } | |
51 | } | |
52 | ||
45 | 53 | impl FromStr for Repository { |
46 | 54 | type Err = (); |
47 | 55 |