Skip to main content

max / makenotwork

4.4 KB · 135 lines History Blame Raw
1 //! Repo settings: description, visibility, project linking, deletion.
2
3 use axum::{
4 extract::{Path, State},
5 http::StatusCode,
6 response::{IntoResponse, Redirect},
7 Form,
8 };
9 use serde::Deserialize;
10 use tower_sessions::Session;
11
12 use crate::{
13 auth::AuthUser,
14 db::{self, ProjectId, Visibility},
15 error::{AppError, Result},
16 helpers::get_csrf_token,
17 templates::*,
18 validation,
19 AppState,
20 };
21
22 use super::default_ref;
23
24 /// `GET /git/{owner}/{repo}/settings`: settings form (owner only).
25 #[tracing::instrument(skip_all, name = "git_issues::repo_settings_form")]
26 pub(super) async fn repo_settings_form(
27 State(state): State<AppState>,
28 session: Session,
29 AuthUser(user): AuthUser,
30 Path((owner, repo_name)): Path<(String, String)>,
31 ) -> Result<impl IntoResponse> {
32 let resolved = super::resolve_repo(&state, &owner, &repo_name, Some(user.id)).await?;
33
34 if user.id != resolved.db_user.id {
35 return Err(AppError::Forbidden);
36 }
37
38 let projects = db::projects::get_projects_by_user(&state.db, user.id).await?;
39 let linked_project_id = resolved.db_repo.project_id.map(|pid| pid.to_string()).unwrap_or_default();
40 let (open_issue_count, _) = db::issues::get_issue_counts(&state.db, resolved.db_repo.id).await.unwrap_or((0, 0));
41 let current_ref = default_ref(&state, &owner, &repo_name);
42 let csrf_token = get_csrf_token(&session).await;
43
44 Ok(GitRepoSettingsTemplate {
45 csrf_token,
46 session_user: Some(user),
47 owner,
48 repo_name,
49 current_ref,
50 repo: resolved.db_repo,
51 open_issue_count,
52 projects,
53 linked_project_id,
54 })
55 }
56
57 #[derive(Deserialize)]
58 pub(super) struct RepoSettingsForm {
59 description: String,
60 visibility: Visibility,
61 project_id: Option<String>,
62 }
63
64 /// `POST /git/{owner}/{repo}/settings`: save settings (owner only).
65 #[tracing::instrument(skip_all, name = "git_issues::repo_settings_save")]
66 pub(super) async fn repo_settings_save(
67 State(state): State<AppState>,
68 AuthUser(user): AuthUser,
69 Path((owner, repo_name)): Path<(String, String)>,
70 Form(form): Form<RepoSettingsForm>,
71 ) -> Result<impl IntoResponse> {
72 user.check_not_suspended()?;
73 let resolved = super::resolve_repo(&state, &owner, &repo_name, Some(user.id)).await?;
74
75 if user.id != resolved.db_user.id {
76 return Err(AppError::Forbidden);
77 }
78
79 let description = form.description.trim();
80 validation::validate_repo_description(description)?;
81
82 // Update description + visibility (enum deserialization handles validation)
83 db::git_repos::update_repo_settings(&state.db, resolved.db_repo.id, description, form.visibility).await?;
84
85 // Handle project linking
86 let new_project_id: Option<ProjectId> = form
87 .project_id
88 .as_deref()
89 .filter(|s| !s.is_empty())
90 .and_then(|s| s.parse::<ProjectId>().ok());
91
92 match (resolved.db_repo.project_id, new_project_id) {
93 (Some(_), None) => {
94 // Unlink
95 db::git_repos::unlink_repo_from_project(&state.db, resolved.db_repo.id).await?;
96 }
97 (None, Some(pid)) | (Some(_), Some(pid)) if resolved.db_repo.project_id != Some(pid) => {
98 // Link or change link — verify the project belongs to this user
99 let project = db::projects::get_project_by_id(&state.db, pid)
100 .await?
101 .ok_or(AppError::validation("Project not found".to_string()))?;
102 if project.user_id != user.id {
103 return Err(AppError::Forbidden);
104 }
105 db::git_repos::link_repo_to_project(&state.db, resolved.db_repo.id, pid).await?;
106 }
107 _ => {} // No change
108 }
109
110 Ok(Redirect::to(&format!("/git/{}/{}/settings", owner, repo_name)))
111 }
112
113 /// `POST /git/{owner}/{repo}/settings/delete`: delete repo (owner only).
114 #[tracing::instrument(skip_all, name = "git_issues::repo_settings_delete")]
115 pub(super) async fn repo_settings_delete(
116 State(state): State<AppState>,
117 AuthUser(user): AuthUser,
118 Path((owner, repo_name)): Path<(String, String)>,
119 ) -> Result<impl IntoResponse> {
120 user.check_not_suspended()?;
121 let resolved = super::resolve_repo(&state, &owner, &repo_name, Some(user.id)).await?;
122
123 if user.id != resolved.db_user.id {
124 return Err(AppError::Forbidden);
125 }
126
127 db::git_repos::delete_repo(&state.db, resolved.db_repo.id).await?;
128
129 Ok((
130 StatusCode::OK,
131 [("HX-Redirect", format!("/git/{}", owner))],
132 "",
133 ))
134 }
135