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

ambee/giterated

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

Update GitBackend to use new models

Type: Fix

emilia - ⁨2⁩ years ago

parent: tbd commit: ⁨f657cbe

Showing ⁨⁨6⁩ changed files⁩ with ⁨⁨127⁩ insertions⁩ and ⁨⁨121⁩ deletions⁩

Cargo.lock

View file
@@ -603,9 +603,9 @@ dependencies = [
603 603
604 604 [[package]]
605 605 name = "hashlink"
606 version = "0.8.3"
606 version = "0.8.4"
607 607 source = "registry+https://github.com/rust-lang/crates.io-index"
608 checksum = "312f66718a2d7789ffef4f4b7b213138ed9f1eb3aa1d0d82fc99f88fb3ffd26f"
608 checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7"
609 609 dependencies = [
610 610 "hashbrown 0.14.0",
611 611 ]
@@ -915,9 +915,9 @@ checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503"
915 915
916 916 [[package]]
917 917 name = "lock_api"
918 version = "0.4.9"
918 version = "0.4.10"
919 919 source = "registry+https://github.com/rust-lang/crates.io-index"
920 checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
920 checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16"
921 921 dependencies = [
922 922 "autocfg",
923 923 "scopeguard",
@@ -940,9 +940,9 @@ dependencies = [
940 940
941 941 [[package]]
942 942 name = "memchr"
943 version = "2.5.0"
943 version = "2.6.0"
944 944 source = "registry+https://github.com/rust-lang/crates.io-index"
945 checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
945 checksum = "76fc44e2588d5b436dbc3c6cf62aef290f90dab6235744a93dfe1cc18f451e2c"
946 946
947 947 [[package]]
948 948 name = "mime"
@@ -1100,11 +1100,11 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d"
1100 1100
1101 1101 [[package]]
1102 1102 name = "openssl"
1103 version = "0.10.56"
1103 version = "0.10.57"
1104 1104 source = "registry+https://github.com/rust-lang/crates.io-index"
1105 checksum = "729b745ad4a5575dd06a3e1af1414bd330ee561c01b3899eb584baeaa8def17e"
1105 checksum = "bac25ee399abb46215765b1cb35bc0212377e58a061560d8b29b024fd0430e7c"
1106 1106 dependencies = [
1107 "bitflags 1.3.2",
1107 "bitflags 2.4.0",
1108 1108 "cfg-if",
1109 1109 "foreign-types",
1110 1110 "libc",
@@ -1132,9 +1132,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
1132 1132
1133 1133 [[package]]
1134 1134 name = "openssl-sys"
1135 version = "0.9.91"
1135 version = "0.9.92"
1136 1136 source = "registry+https://github.com/rust-lang/crates.io-index"
1137 checksum = "866b5f16f90776b9bb8dc1e1802ac6f0513de3a7a7465867bfbc563dc737faac"
1137 checksum = "db7e971c2c2bba161b2d2fdf37080177eff520b3bc044787c7f1f5f9e78d869b"
1138 1138 dependencies = [
1139 1139 "cc",
1140 1140 "libc",
@@ -1223,9 +1223,9 @@ dependencies = [
1223 1223
1224 1224 [[package]]
1225 1225 name = "pin-project-lite"
1226 version = "0.2.12"
1226 version = "0.2.13"
1227 1227 source = "registry+https://github.com/rust-lang/crates.io-index"
1228 checksum = "12cc1b0bf1727a77a54b6654e7b5f1af8604923edc8b81885f8ec92f9e3f0a05"
1228 checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
1229 1229
1230 1230 [[package]]
1231 1231 name = "pin-utils"
@@ -1406,9 +1406,9 @@ checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
1406 1406
1407 1407 [[package]]
1408 1408 name = "rustix"
1409 version = "0.38.9"
1409 version = "0.38.10"
1410 1410 source = "registry+https://github.com/rust-lang/crates.io-index"
1411 checksum = "9bfe0f2582b4931a45d1fa608f8a8722e8b3c7ac54dd6d5f3b3212791fedef49"
1411 checksum = "ed6248e1caa625eb708e266e06159f135e8c26f2bb7ceb72dc4b2766d0340964"
1412 1412 dependencies = [
1413 1413 "bitflags 2.4.0",
1414 1414 "errno",
@@ -1434,9 +1434,9 @@ dependencies = [
1434 1434
1435 1435 [[package]]
1436 1436 name = "scopeguard"
1437 version = "1.1.0"
1437 version = "1.2.0"
1438 1438 source = "registry+https://github.com/rust-lang/crates.io-index"
1439 checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
1439 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
1440 1440
1441 1441 [[package]]
1442 1442 name = "security-framework"
@@ -1463,18 +1463,18 @@ dependencies = [
1463 1463
1464 1464 [[package]]
1465 1465 name = "serde"
1466 version = "1.0.186"
1466 version = "1.0.188"
1467 1467 source = "registry+https://github.com/rust-lang/crates.io-index"
1468 checksum = "9f5db24220c009de9bd45e69fb2938f4b6d2df856aa9304ce377b3180f83b7c1"
1468 checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
1469 1469 dependencies = [
1470 1470 "serde_derive",
1471 1471 ]
1472 1472
1473 1473 [[package]]
1474 1474 name = "serde_derive"
1475 version = "1.0.186"
1475 version = "1.0.188"
1476 1476 source = "registry+https://github.com/rust-lang/crates.io-index"
1477 checksum = "5ad697f7e0b65af4983a4ce8f56ed5b357e8d3c36651bf6a7e13639c17b8e670"
1477 checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
1478 1478 dependencies = [
1479 1479 "proc-macro2",
1480 1480 "quote",
@@ -2229,9 +2229,9 @@ checksum = "a156c684c91ea7d62626509bce3cb4e1d9ed5c4d978f7b4352658f96a4c26b4a"
2229 2229
2230 2230 [[package]]
2231 2231 name = "url"
2232 version = "2.4.0"
2232 version = "2.4.1"
2233 2233 source = "registry+https://github.com/rust-lang/crates.io-index"
2234 checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb"
2234 checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5"
2235 2235 dependencies = [
2236 2236 "form_urlencoded",
2237 2237 "idna",

migrations/20230829014608_combine_user_instance_url_repositories.sql

View file
@@ -0,0 +1,5 @@
1 ALTER TABLE repositories
2 DROP COLUMN instance_url;
3
4 ALTER TABLE repositories
5 RENAME COLUMN username TO owner_user;

src/backend/git.rs

View file
@@ -5,7 +5,7 @@ use std::error::Error;
5 5 use std::path::{Path, PathBuf};
6 6 use thiserror::Error;
7 7
8 use crate::messages::UserAuthenticated;
8 use crate::messages::ValidatedUserAuthenticated;
9 9 use crate::model::instance::Instance;
10 10 use crate::model::repository::{
11 11 Commit, RepositoryObjectType, RepositoryTreeEntry, RepositoryVisibility,
@@ -19,6 +19,7 @@ use crate::{
19 19 },
20 20 model::repository::RepositoryView,
21 21 };
22 use crate::model::user::User;
22 23
23 24 use super::{IssuesBackend, RepositoryBackend};
24 25
@@ -28,8 +29,7 @@ use super::{IssuesBackend, RepositoryBackend};
28 29 /// Repository in the database
29 30 #[derive(Debug, sqlx::FromRow)]
30 31 pub struct GitRepository {
31 pub username: String,
32 pub instance_url: String,
32 pub owner_user: User,
33 33 pub name: String,
34 34 pub description: Option<String>,
35 35 pub visibility: RepositoryVisibility,
@@ -39,10 +39,10 @@ pub struct GitRepository {
39 39 impl GitRepository {
40 40 // Separate function because "Private" will be expanded later
41 41 /// Checks if the user is allowed to view this repository
42 pub fn can_user_view_repository(&self, instance_url: &str, username: Option<&str>) -> bool {
42 pub fn can_user_view_repository(&self, user: Option<&User>) -> bool {
43 43 !(matches!(self.visibility, RepositoryVisibility::Private)
44 && self.instance_url != instance_url
45 && self.username != username.map_or("", |username| username))
44 && self.owner_user.instance.url != user.map_or("", |user| user.instance.url.as_str())
45 && self.owner_user.username != user.map_or("", |user| user.username.as_str()))
46 46 }
47 47
48 48 // This is in it's own function because I assume I'll have to add logic to this later
@@ -52,7 +52,7 @@ impl GitRepository {
52 52 ) -> Result<git2::Repository, GitBackendError> {
53 53 match git2::Repository::open(format!(
54 54 "{}/{}/{}/{}",
55 repository_directory, self.instance_url, self.username, self.name
55 repository_directory, self.owner_user.instance.url, self.owner_user.username, self.name
56 56 )) {
57 57 Ok(repository) => Ok(repository),
58 58 Err(err) => {
@@ -73,17 +73,14 @@ pub enum GitBackendError {
73 73 FailedCreatingRepository(git2::Error),
74 74 #[error("Failed inserting into the database")]
75 75 FailedInsertingIntoDatabase(sqlx::Error),
76 #[error("")]
77 // #[error("Failed finding repository {instance_url:?}/{username:?}/{name:?}")]
76 #[error("Failed finding repository {owner_user:?}/{name:?}")]
78 77 RepositoryNotFound {
79 // instance_url: String,
80 // username: String,
78 owner_user: String,
81 79 name: String,
82 80 },
83 #[error("Repository {instance_url:?}/{username:?}/{name:?} already exists")]
81 #[error("Repository {owner_user:?}/{name:?} already exists")]
84 82 RepositoryAlreadyExists {
85 instance_url: String,
86 username: String,
83 owner_user: String,
87 84 name: String,
88 85 },
89 86 #[error("Repository couldn't be deleted from the disk")]
@@ -113,36 +110,33 @@ impl GitBackend {
113 110 }
114 111 }
115 112
116 pub async fn find_by_instance_username_name(
113 pub async fn find_by_owner_user_name(
117 114 &self,
118 // instance_url: &str,
119 // username: &str,
115 user: &User,
120 116 repository_name: &str,
121 117 ) -> Result<GitRepository, GitBackendError> {
122 118 if let Ok(repository) = sqlx::query_as!(GitRepository,
123 r#"SELECT instance_url, username, name, description, visibility as "visibility: _", default_branch FROM repositories WHERE name = $1"#,
124 /*instance_url, username,*/ repository_name)
119 r#"SELECT owner_user, name, description, visibility as "visibility: _", default_branch FROM repositories WHERE owner_user = $1 AND name = $2"#,
120 user.to_string(), repository_name)
125 121 .fetch_one(&self.pg_pool.clone())
126 122 .await {
127 123 Ok(repository)
128 124 } else {
129 125 Err(GitBackendError::RepositoryNotFound {
130 // instance_url: instance_url.to_string(),
131 // username: username.to_string(),
126 owner_user: user.to_string(),
132 127 name: repository_name.to_string(),
133 128 })
134 129 }
135 130 }
136 131
137 pub async fn delete_by_instance_username_name(
132 pub async fn delete_by_owner_user_name(
138 133 &self,
139 instance_url: &str,
140 username: &str,
134 user: &User,
141 135 repository_name: &str,
142 136 ) -> Result<u64, GitBackendError> {
143 137 if let Err(err) = std::fs::remove_dir_all(PathBuf::from(format!(
144 138 "{}/{}/{}/{}",
145 self.repository_folder, instance_url, username, repository_name
139 self.repository_folder, user.instance.url, user.username, repository_name
146 140 ))) {
147 141 let err = GitBackendError::CouldNotDeleteFromDisk(err);
148 142 error!(
@@ -155,9 +149,8 @@ impl GitBackend {
155 149
156 150 // Delete the repository from the database
157 151 match sqlx::query!(
158 "DELETE FROM repositories WHERE instance_url = $1 AND username = $2 AND name = $3",
159 instance_url,
160 username,
152 "DELETE FROM repositories WHERE owner_user = $1 AND name = $2",
153 user.to_string(),
161 154 repository_name
162 155 )
163 156 .execute(&self.pg_pool.clone())
@@ -215,39 +208,37 @@ impl GitBackend {
215 208 impl RepositoryBackend for GitBackend {
216 209 async fn create_repository(
217 210 &mut self,
218 raw_request: &UserAuthenticated<CreateRepositoryRequest>,
211 raw_request: &ValidatedUserAuthenticated<CreateRepositoryRequest>,
219 212 ) -> Result<CreateRepositoryResponse, Box<dyn Error + Send>> {
220 213 let request = raw_request.inner().await;
221 214
222 let public_key = public_key(&Instance {
223 url: String::from("giterated.dev"),
224 })
225 .await
226 .unwrap();
227
228 match raw_request.validate(public_key).await {
229 Ok(_) => info!("Request was validated"),
230 Err(err) => {
231 error!("Failed to validate request: {:?}", err);
232 panic!();
233 }
234 }
235
236 info!("Request was valid!");
215 // let public_key = public_key(&Instance {
216 // url: String::from("giterated.dev"),
217 // })
218 // .await
219 // .unwrap();
220 //
221 // match raw_request.validate(public_key).await {
222 // Ok(_) => info!("Request was validated"),
223 // Err(err) => {
224 // error!("Failed to validate request: {:?}", err);
225 // panic!();
226 // }
227 // }
228 //
229 // info!("Request was valid!");
237 230
238 231 // Check if repository already exists in the database
239 if let Ok(_repository) = self
240 .find_by_instance_username_name(
241 // request.owner.instance.url.as_str(),
242 // request.owner.username.as_str(),
232 if let Ok(repository) = self
233 .find_by_owner_user_name(
234 &request.owner,
243 235 &request.name,
244 236 )
245 237 .await
246 238 {
247 239 let err = GitBackendError::RepositoryAlreadyExists {
248 instance_url: request.owner.instance.url.clone(),
249 username: request.owner.instance.url.clone(),
250 name: request.name.clone(),
240 owner_user: repository.owner_user.to_string(),
241 name: repository.name,
251 242 };
252 243 error!("{:?}", err);
253 244
@@ -256,8 +247,8 @@ impl RepositoryBackend for GitBackend {
256 247
257 248 // Insert the repository into the database
258 249 let _ = match sqlx::query_as!(GitRepository,
259 r#"INSERT INTO repositories VALUES ($1, $2, $3, $4, $5, $6) RETURNING username, instance_url, name, description, visibility as "visibility: _", default_branch"#,
260 request.owner.username, request.owner.instance.url, request.name, request.description, request.visibility as _, "master")
250 r#"INSERT INTO repositories VALUES ($1, $2, $3, $4, $5) RETURNING owner_user, name, description, visibility as "visibility: _", default_branch"#,
251 request.owner.to_string(), request.name, request.description, request.visibility as _, "master")
261 252 .fetch_one(&self.pg_pool.clone())
262 253 .await {
263 254 Ok(repository) => repository,
@@ -290,9 +281,8 @@ impl RepositoryBackend for GitBackend {
290 281
291 282 // Delete repository from database
292 283 if let Err(err) = self
293 .delete_by_instance_username_name(
294 request.owner.instance.url.as_str(),
295 request.owner.username.as_str(),
284 .delete_by_owner_user_name(
285 &request.owner,
296 286 request.name.as_str(),
297 287 )
298 288 .await
@@ -309,12 +299,15 @@ impl RepositoryBackend for GitBackend {
309 299
310 300 async fn repository_info(
311 301 &mut self,
312 request: &RepositoryInfoRequest,
302 // TODO: Allow non-authenticated???
303 raw_request: &ValidatedUserAuthenticated<RepositoryInfoRequest>,
313 304 ) -> Result<RepositoryView, Box<dyn Error + Send>> {
305 let request = raw_request.inner().await;
306
314 307 let repository = match self
315 .find_by_instance_username_name(
308 .find_by_owner_user_name(
316 309 // &request.owner.instance.url,
317 // &request.owner.username,
310 &request.repository.owner,
318 311 &request.repository.name,
319 312 )
320 313 .await
@@ -323,16 +316,14 @@ impl RepositoryBackend for GitBackend {
323 316 Err(err) => return Err(Box::new(err)),
324 317 };
325 318
326 // if !repository.can_user_view_repository(
327 // request.owner.instance.url.as_str(),
328 // Some(request.owner.username.as_str()),
329 // ) {
330 // return Err(Box::new(GitBackendError::RepositoryNotFound {
331 // instance_url: request.owner.instance.url.clone(),
332 // username: request.owner.username.clone(),
333 // name: request.name.clone(),
334 // }));
335 // }
319 if !repository.can_user_view_repository(
320 Some(&raw_request.user),
321 ) {
322 return Err(Box::new(GitBackendError::RepositoryNotFound {
323 owner_user: request.repository.owner.to_string(),
324 name: request.repository.name.clone(),
325 }));
326 }
336 327
337 328 let git = match repository.open_git2_repository(&self.repository_folder) {
338 329 Ok(git) => git,
@@ -347,6 +338,7 @@ impl RepositoryBackend for GitBackend {
347 338 // Nothing in database, render empty tree.
348 339 return Ok(RepositoryView {
349 340 name: repository.name,
341 owner: request.repository.owner.clone(),
350 342 description: repository.description,
351 343 visibility: repository.visibility,
352 344 default_branch: repository.default_branch,
@@ -413,27 +405,27 @@ impl RepositoryBackend for GitBackend {
413 405 let mut tree_entry =
414 406 RepositoryTreeEntry::new(entry.name().unwrap(), object_type, entry.filemode());
415 407
416 // if request.extra_metadata {
417 // // Get the file size if It's a blob
418 // let object = entry.to_object(&git).unwrap();
419 // if let Some(blob) = object.as_blob() {
420 // tree_entry.size = Some(blob.size());
421 // }
422
423 // // Could possibly be done better
424 // let path = if let Some(path) = current_path.split_once('/') {
425 // format!("{}/{}", path.1, entry.name().unwrap())
426 // } else {
427 // entry.name().unwrap().to_string()
428 // };
429
430 // // Get the last commit made to the entry
431 // if let Ok(last_commit) =
432 // GitBackend::get_last_commit_of_file(&path, &git, commit)
433 // {
434 // tree_entry.last_commit = Some(last_commit);
435 // }
436 // }
408 if request.extra_metadata {
409 // Get the file size if It's a blob
410 let object = entry.to_object(&git).unwrap();
411 if let Some(blob) = object.as_blob() {
412 tree_entry.size = Some(blob.size());
413 }
414
415 // Could possibly be done better
416 let path = if let Some(path) = current_path.split_once('/') {
417 format!("{}/{}", path.1, entry.name().unwrap())
418 } else {
419 entry.name().unwrap().to_string()
420 };
421
422 // Get the last commit made to the entry
423 if let Ok(last_commit) =
424 GitBackend::get_last_commit_of_file(&path, &git, commit)
425 {
426 tree_entry.last_commit = Some(last_commit);
427 }
428 }
437 429
438 430 tree_entry
439 431 })
@@ -447,6 +439,7 @@ impl RepositoryBackend for GitBackend {
447 439
448 440 Ok(RepositoryView {
449 441 name: repository.name,
442 owner: request.repository.owner.clone(),
450 443 description: repository.description,
451 444 visibility: repository.visibility,
452 445 default_branch: repository.default_branch,
@@ -458,7 +451,7 @@ impl RepositoryBackend for GitBackend {
458 451
459 452 fn repository_file_inspect(
460 453 &mut self,
461 _request: &RepositoryFileInspectRequest,
454 _request: &ValidatedUserAuthenticated<RepositoryFileInspectRequest>,
462 455 ) -> Result<RepositoryFileInspectionResponse, Box<dyn Error + Send>> {
463 456 todo!()
464 457 }
@@ -467,21 +460,21 @@ impl RepositoryBackend for GitBackend {
467 460 impl IssuesBackend for GitBackend {
468 461 fn issues_count(
469 462 &mut self,
470 _request: &RepositoryIssuesCountRequest,
463 _request: &ValidatedUserAuthenticated<RepositoryIssuesCountRequest>,
471 464 ) -> Result<RepositoryIssuesCountResponse, Box<dyn Error + Send>> {
472 465 todo!()
473 466 }
474 467
475 468 fn issue_labels(
476 469 &mut self,
477 _request: &RepositoryIssueLabelsRequest,
470 _request: &ValidatedUserAuthenticated<RepositoryIssueLabelsRequest>,
478 471 ) -> Result<RepositoryIssueLabelsResponse, Box<dyn Error + Send>> {
479 472 todo!()
480 473 }
481 474
482 475 fn issues(
483 476 &mut self,
484 _request: &RepositoryIssuesRequest,
477 _request: &ValidatedUserAuthenticated<RepositoryIssuesRequest>,
485 478 ) -> Result<RepositoryIssuesResponse, Box<dyn Error + Send>> {
486 479 todo!()
487 480 }

src/messages/mod.rs

View file
@@ -118,7 +118,7 @@ impl<T: Serialize> InstanceAuthenticated<T> {
118 118 pub struct ValidatedUserAuthenticated<T: Serialize> {
119 119 #[serde(flatten)]
120 120 message: T,
121 user: User,
121 pub(crate) user: User,
122 122 }
123 123
124 124 impl<T> Clone for ValidatedUserAuthenticated<T>

src/messages/repository.rs

View file
@@ -116,6 +116,7 @@ pub struct RepositoryIssue {
116 116 #[derive(Clone, Serialize, Deserialize)]
117 117 pub struct RepositoryInfoRequest {
118 118 pub repository: Repository,
119 pub extra_metadata: bool,
119 120 pub rev: Option<String>,
120 121 pub path: Option<String>,
121 122 }

src/model/user.rs

View file
@@ -1,3 +1,4 @@
1 use std::fmt::{Display, Formatter};
1 2 use std::str::FromStr;
2 3
3 4 use serde::{Deserialize, Serialize};
@@ -10,9 +11,15 @@ pub struct User {
10 11 pub instance: Instance,
11 12 }
12 13
13 impl ToString for User {
14 fn to_string(&self) -> String {
15 format!("{}:{}", self.username, self.instance.url)
14 impl Display for User {
15 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
16 write!(f, "{}:{}", self.username, self.instance.url)
17 }
18 }
19
20 impl From<String> for User {
21 fn from(user_string: String) -> Self {
22 User::from_str(&user_string).unwrap()
16 23 }
17 24 }
18 25