Move towards having GitBackend split into files
parent: tbd commit: e55da0e
1 | use Error; |
2 | use async_trait; |
3 | |
4 | use BranchType; |
5 | use ; |
6 | |
7 | use Object; |
8 | use |
9 | AccessList, Commit, DefaultBranch, Description, IssueLabel, Repository, RepositoryBranch, |
10 | RepositoryBranchRequest, RepositoryBranchesRequest, RepositoryCommitBeforeRequest, |
11 | RepositoryCommitFromIdRequest, RepositoryDiff, RepositoryDiffPatchRequest, |
12 | RepositoryDiffRequest, RepositoryFile, RepositoryFileFromIdRequest, |
13 | RepositoryFileFromPathRequest, RepositoryFileInspectRequest, RepositoryIssue, |
14 | RepositoryIssueLabelsRequest, RepositoryIssuesCountRequest, RepositoryIssuesRequest, |
15 | RepositoryLastCommitOfFileRequest, RepositoryStatistics, RepositoryStatisticsRequest, |
16 | RepositoryTag, RepositoryTagsRequest, RepositoryTreeEntry, RepositoryVisibility, Visibility, |
17 | ; |
18 | |
19 | use User; |
20 | |
21 | use ; |
22 | |
23 | use PgPool; |
24 | use Deref; |
25 | use ; |
26 | use Error; |
27 | use OnceCell; |
28 | |
29 | |
30 | |
31 | |
32 | |
33 | |
34 | |
35 | use ; |
36 | |
37 | //region database structures |
38 | |
39 | /// Repository in the database |
40 | |
41 | |
42 | #[sqlx(try_from = "String")] |
43 | pub owner_user: User, |
44 | pub name: String, |
45 | pub description: , |
46 | pub visibility: RepositoryVisibility, |
47 | pub default_branch: String, |
48 | |
49 | |
50 | |
51 | // Separate function because "Private" will be expanded later |
52 | /// Checks if the user is allowed to view this repository |
53 | pub async |
54 | &self, |
55 | our_instance: &Instance, |
56 | user: & , |
57 | stack: &GiteratedStack, |
58 | |
59 | if matches! |
60 | return true; |
61 | |
62 | |
63 | // User must exist for any further checks to pass |
64 | let user = match user |
65 | Some => user, |
66 | None => return false, |
67 | ; |
68 | |
69 | if *user.deref == self.owner_user |
70 | // owner can always view |
71 | return true; |
72 | |
73 | |
74 | if matches! |
75 | // Check if the user can view\ |
76 | let access_list = stack |
77 | . |
78 | owner: self.owner_user.clone, |
79 | name: self.name.clone, |
80 | instance: our_instance.clone, |
81 | |
82 | .await |
83 | .unwrap; |
84 | |
85 | access_list |
86 | .0 |
87 | .iter |
88 | .any |
89 | else |
90 | false |
91 | |
92 | |
93 | |
94 | // This is in it's own function because I assume I'll have to add logic to this later |
95 | |
96 | &self, |
97 | repository_directory: &str, |
98 | |
99 | match open |
100 | "{}/{}/{}/{}" |
101 | repository_directory, self.owner_user.instance, self.owner_user.username, self.name |
102 | ) |
103 | Ok => Ok, |
104 | Err => |
105 | let err = FailedOpeningFromDisk; |
106 | error!; |
107 | |
108 | Err |
109 | |
110 | |
111 | |
112 | |
113 | |
114 | //endregion |
115 | |
116 | |
117 | |
118 | |
119 | FailedCreatingRepository, |
120 | |
121 | FailedInsertingIntoDatabase, |
122 | |
123 | RepositoryNotFound , |
124 | |
125 | RepositoryAlreadyExists , |
126 | |
127 | CouldNotDeleteFromDisk, |
128 | |
129 | FailedDeletingFromDatabase, |
130 | |
131 | FailedOpeningFromDisk, |
132 | |
133 | RefNotFound, |
134 | |
135 | HeadNotFound, |
136 | |
137 | DefaultNotFound, |
138 | |
139 | PathNotFound, |
140 | |
141 | BranchNotFound, |
142 | |
143 | LastCommitNotFound, |
144 | |
145 | InvalidObjectId, |
146 | |
147 | BlobNotFound, |
148 | |
149 | TreeNotFound, |
150 | |
151 | CommitNotFound, |
152 | |
153 | CommitParentNotFound, |
154 | |
155 | FailedDiffing, |
156 | |
157 | |
158 | |
159 | pg_pool: PgPool, |
160 | repository_folder: String, |
161 | instance: Instance, |
162 | stack: , |
163 | |
164 | |
165 | |
166 | |
167 | pg_pool: &PgPool, |
168 | repository_folder: &str, |
169 | instance: impl , |
170 | stack: , |
171 | |
172 | let instance = instance.to_owned; |
173 | |
174 | Self |
175 | pg_pool: pg_pool.clone, |
176 | // We make sure there's no end slash |
177 | repository_folder: repository_folder.trim_end_matches .to_string, |
178 | instance, |
179 | stack, |
180 | |
181 | |
182 | |
183 | pub async |
184 | &self, |
185 | user: &User, |
186 | repository_name: &str, |
187 | |
188 | // TODO: Patch for use with new GetValue system |
189 | if let Ok = query_as! |
190 | r#"SELECT owner_user, name, description, visibility as "visibility: _", default_branch FROM repositories WHERE owner_user = $1 AND name = $2"#, |
191 | user.to_string, repository_name |
192 | .fetch_one |
193 | .await |
194 | Ok |
195 | else |
196 | Err |
197 | owner_user: user.to_string, |
198 | name: repository_name.to_string, |
199 | |
200 | |
201 | |
202 | |
203 | pub async |
204 | &self, |
205 | user: &User, |
206 | repository_name: &str, |
207 | |
208 | if let Err = remove_dir_all |
209 | "{}/{}/{}/{}" |
210 | self.repository_folder, user.instance, user.username, repository_name |
211 | ) |
212 | let err = CouldNotDeleteFromDisk; |
213 | error! |
214 | "Couldn't delete repository from disk, this is bad! {:?}", |
215 | err |
216 | ; |
217 | |
218 | return Err; |
219 | |
220 | |
221 | // Delete the repository from the database |
222 | self.delete_from_database .await |
223 | |
224 | |
225 | /// Deletes the repository from the database |
226 | pub async |
227 | &self, |
228 | user: &User, |
229 | repository_name: &str, |
230 | |
231 | match query! |
232 | "DELETE FROM repositories WHERE owner_user = $1 AND name = $2", |
233 | user.to_string, |
234 | repository_name |
235 | |
236 | .execute |
237 | .await |
238 | |
239 | Ok => Ok, |
240 | Err => Err, |
241 | |
242 | |
243 | |
244 | pub async |
245 | &self, |
246 | owner: &User, |
247 | name: &str, |
248 | requester: & , |
249 | |
250 | let repository = match self |
251 | .find_by_owner_user_name |
252 | // &request.owner.instance.url, |
253 | owner, name, |
254 | |
255 | .await |
256 | |
257 | Ok => repository, |
258 | Err => return Err, |
259 | ; |
260 | |
261 | if let Some = requester |
262 | if !repository |
263 | .can_user_view_repository |
264 | &self.instance, |
265 | &Some, |
266 | self.stack.get .unwrap, |
267 | |
268 | .await |
269 | |
270 | return Err |
271 | owner_user: repository.owner_user.to_string, |
272 | name: repository.name.clone, |
273 | ; |
274 | |
275 | else if matches! |
276 | // Unauthenticated users can never view private repositories |
277 | |
278 | return Err |
279 | owner_user: repository.owner_user.to_string, |
280 | name: repository.name.clone, |
281 | ; |
282 | |
283 | |
284 | match repository.open_git2_repository |
285 | Ok => Ok, |
286 | Err => Err, |
287 | |
288 | |
289 | |
290 | /// Attempts to get the oid in this order: |
291 | /// 1. Full refname (refname_to_id) |
292 | /// 2. Short branch name (find_branch) |
293 | /// 3. Other (revparse_single) |
294 | |
295 | git: & Repository, |
296 | rev: , |
297 | default_branch: &DefaultBranch, |
298 | |
299 | // If the rev is None try and get the default branch instead |
300 | let rev = rev.unwrap_or; |
301 | |
302 | // TODO: This is far from ideal or speedy and would love for a better way to check this in the same order, but I can't find proper methods to do any of this. |
303 | trace!; |
304 | |
305 | // Try getting it as a refname (refs/heads/name) |
306 | if let Ok = git.refname_to_id |
307 | Ok |
308 | // Try finding it as a short branch name |
309 | else if let Ok = git.find_branch |
310 | // SHOULD be safe to unwrap |
311 | Ok |
312 | // As last resort, try revparsing (will catch short oid and tags) |
313 | else if let Ok = git.revparse_single |
314 | Ok |
315 | Some => |
316 | if let Ok = object.peel_to_commit |
317 | commit.id |
318 | else |
319 | object.id |
320 | |
321 | |
322 | _ => object.id, |
323 | |
324 | else |
325 | Err |
326 | |
327 | |
328 | |
329 | |
330 | |
331 | |
332 | async |
333 | &mut self, |
334 | _user: &AuthenticatedUser, |
335 | request: &RepositoryCreateRequest, |
336 | |
337 | // Check if repository already exists in the database |
338 | if let Ok = self |
339 | .find_by_owner_user_name |
340 | .await |
341 | |
342 | let err = RepositoryAlreadyExists |
343 | owner_user: repository.owner_user.to_string, |
344 | name: repository.name, |
345 | ; |
346 | error!; |
347 | |
348 | return Err; |
349 | |
350 | |
351 | // Insert the repository into the database |
352 | let _ = match query_as! |
353 | r#"INSERT INTO repositories VALUES ($1, $2, $3, $4, $5) RETURNING owner_user, name, description, visibility as "visibility: _", default_branch"#, |
354 | request.owner.to_string, request.name, request.description, request.visibility as _, "master" |
355 | .fetch_one |
356 | .await |
357 | Ok => repository, |
358 | Err => |
359 | let err = FailedInsertingIntoDatabase; |
360 | error!; |
361 | |
362 | return Err; |
363 | |
364 | ; |
365 | |
366 | // Create bare (server side) repository on disk |
367 | match init_bare |
368 | "{}/{}/{}/{}" |
369 | self.repository_folder, request.owner.instance, request.owner.username, request.name |
370 | ) |
371 | Ok => |
372 | debug! |
373 | "Created new repository with the name {}/{}/{}", |
374 | request.owner.instance, request.owner.username, request.name |
375 | ; |
376 | |
377 | let stack = self.stack.get .unwrap; |
378 | |
379 | let repository = Repository |
380 | owner: request.owner.clone, |
381 | name: request.name.clone, |
382 | instance: request.instance.as_ref .unwrap_or .clone, |
383 | ; |
384 | |
385 | stack |
386 | .write_setting |
387 | &repository, |
388 | Description, |
389 | |
390 | .await |
391 | .unwrap; |
392 | |
393 | stack |
394 | .write_setting |
395 | .await |
396 | .unwrap; |
397 | |
398 | stack |
399 | .write_setting |
400 | .await |
401 | .unwrap; |
402 | |
403 | Ok |
404 | |
405 | Err => |
406 | let err = FailedCreatingRepository; |
407 | error!; |
408 | |
409 | // Delete repository from database |
410 | self.delete_from_database |
411 | .await?; |
412 | |
413 | // ??? |
414 | Err |
415 | |
416 | |
417 | |
418 | |
419 | /// If the OID can't be found because there's no repository head, this will return an empty `Vec`. |
420 | async |
421 | &mut self, |
422 | requester: & , |
423 | repository_object: &mut , |
424 | OperationState | : ,
425 | request: &RepositoryFileInspectRequest, |
426 | |
427 | self.handle_repository_file_inspect |
428 | requester, |
429 | repository_object, |
430 | OperationState, |
431 | request, |
432 | |
433 | .await |
434 | |
435 | |
436 | async |
437 | &mut self, |
438 | requester: & , |
439 | repository_object: &mut , |
440 | OperationState | : ,
441 | request: &RepositoryFileFromIdRequest, |
442 | |
443 | self.handle_repository_file_from_id |
444 | requester, |
445 | repository_object, |
446 | OperationState, |
447 | request, |
448 | |
449 | .await |
450 | |
451 | |
452 | async |
453 | &mut self, |
454 | requester: & , |
455 | repository_object: &mut , |
456 | OperationState | : ,
457 | request: &RepositoryFileFromPathRequest, |
458 | |
459 | self.handle_repository_file_from_path |
460 | requester, |
461 | repository_object, |
462 | OperationState, |
463 | request, |
464 | |
465 | .await |
466 | |
467 | |
468 | async |
469 | &mut self, |
470 | requester: & , |
471 | repository_object: &mut , |
472 | OperationState | : ,
473 | request: &RepositoryCommitFromIdRequest, |
474 | |
475 | self.handle_repository_commit_from_id |
476 | requester, |
477 | repository_object, |
478 | OperationState, |
479 | request, |
480 | |
481 | .await |
482 | |
483 | |
484 | async |
485 | &mut self, |
486 | requester: & , |
487 | repository_object: &mut , |
488 | OperationState | : ,
489 | request: &RepositoryLastCommitOfFileRequest, |
490 | |
491 | self.repository_last_commit_of_file |
492 | requester, |
493 | repository_object, |
494 | OperationState, |
495 | request, |
496 | |
497 | .await |
498 | |
499 | |
500 | async |
501 | &mut self, |
502 | requester: & , |
503 | repository_object: &mut , |
504 | OperationState | : ,
505 | request: &RepositoryDiffRequest, |
506 | |
507 | self.handle_repository_diff |
508 | requester, |
509 | repository_object, |
510 | OperationState, |
511 | request, |
512 | |
513 | .await |
514 | |
515 | |
516 | async |
517 | &mut self, |
518 | requester: & , |
519 | repository_object: &mut , |
520 | OperationState | : ,
521 | request: &RepositoryDiffPatchRequest, |
522 | |
523 | self.handle_repository_diff_patch |
524 | requester, |
525 | repository_object, |
526 | OperationState, |
527 | request, |
528 | |
529 | .await |
530 | |
531 | |
532 | async |
533 | &mut self, |
534 | requester: & , |
535 | repository_object: &mut , |
536 | OperationState | : ,
537 | request: &RepositoryCommitBeforeRequest, |
538 | |
539 | self.handle_repository_commit_before |
540 | requester, |
541 | repository_object, |
542 | OperationState, |
543 | request, |
544 | |
545 | .await |
546 | |
547 | |
548 | // TODO: See where this would need to go in terms of being split up into a different file |
549 | /// Returns zero for all statistics if an OID wasn't found |
550 | async |
551 | &mut self, |
552 | requester: & , |
553 | repository_object: &mut , |
554 | OperationState | : ,
555 | request: &RepositoryStatisticsRequest, |
556 | |
557 | let repository = repository_object.object; |
558 | let git = self |
559 | .open_repository_and_check_permissions |
560 | .await?; |
561 | |
562 | let default_branch = repository_object |
563 | . |
564 | .await?; |
565 | let tree_id = |
566 | match Self get_oid_from_reference |
567 | Ok => oid, |
568 | Err => return Ok, |
569 | ; |
570 | |
571 | // Count the amount of branches and tags |
572 | let mut branches = 0; |
573 | let mut tags = 0; |
574 | if let Ok = git.references |
575 | for reference in references.flatten |
576 | if reference.is_branch |
577 | branches += 1; |
578 | else if reference.is_tag |
579 | tags += 1; |
580 | |
581 | |
582 | |
583 | |
584 | // Attempt to get the commit from the oid |
585 | let commits = if let Ok = git.find_commit |
586 | // Get the total commit count if we found the tree oid commit |
587 | ? | get_total_commit_count
588 | else |
589 | 0 |
590 | ; |
591 | |
592 | Ok |
593 | commits, |
594 | branches, |
595 | tags, |
596 | |
597 | |
598 | |
599 | /// .0: List of branches filtering by passed requirements. |
600 | /// .1: Total amount of branches after being filtered |
601 | async |
602 | &mut self, |
603 | requester: & , |
604 | repository_object: &mut , |
605 | OperationState | : ,
606 | request: &RepositoryBranchesRequest, |
607 | |
608 | self.handle_repository_get_branches |
609 | requester, |
610 | repository_object, |
611 | OperationState, |
612 | request, |
613 | |
614 | .await |
615 | |
616 | |
617 | async |
618 | &mut self, |
619 | requester: & , |
620 | repository_object: &mut , |
621 | OperationState | : ,
622 | request: &RepositoryBranchRequest, |
623 | |
624 | self.handle_repository_get_branch |
625 | requester, |
626 | repository_object, |
627 | OperationState, |
628 | request, |
629 | |
630 | .await |
631 | |
632 | |
633 | /// .0: List of tags in passed range |
634 | /// .1: Total amount of tags |
635 | async |
636 | &mut self, |
637 | requester: & , |
638 | repository_object: &mut , |
639 | OperationState | : ,
640 | request: &RepositoryTagsRequest, |
641 | |
642 | self.handle_repository_get_tags |
643 | requester, |
644 | repository_object, |
645 | OperationState, |
646 | request, |
647 | |
648 | .await |
649 | |
650 | |
651 | async |
652 | &mut self, |
653 | requester: & , |
654 | repository: &Repository, |
655 | |
656 | if let Ok = self |
657 | .find_by_owner_user_name |
658 | .await |
659 | |
660 | Ok |
661 | .can_user_view_repository |
662 | .await |
663 | else |
664 | Ok |
665 | |
666 | |
667 | |
668 | |
669 | |
670 | |
671 | &mut self, |
672 | _requester: & , |
673 | _request: &RepositoryIssuesCountRequest, |
674 | |
675 | todo! |
676 | |
677 | |
678 | |
679 | &mut self, |
680 | _requester: & , |
681 | _request: &RepositoryIssueLabelsRequest, |
682 | |
683 | todo! |
684 | |
685 | |
686 | |
687 | &mut self, |
688 | _requester: & , |
689 | _request: &RepositoryIssuesRequest, |
690 | |
691 | todo! |
692 | |
693 | |
694 | |
695 | |
696 | |
697 | |
698 | pub repository: String, |
699 | pub name: String, |
700 | pub value: String, |
701 | |
702 |