refactor(git,issues): unified remotes/lfs/branch-rename/star/watch; viewer state in RepoDto.me and IssueDto.viewer_watch
ZhenYi committed
Modified lib/git/src/handler/branches/rename.rs lib/git/src/handler/branches/rename.rs +18 −11 View lib/git/src/handler/branches/rename.rs
@@ -1,4 +1,5 @@
1use axum::extract::{Path, Query, State};
1use axum::Json;
2use axum::extract::{Path, State};
23use export::rest::{AppResponseData, NoData};
34use session::Session;
45
@@ -7,22 +8,23 @@
78use crate::service::GitService;
89
910#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
10pub struct RenameBranchPath {
11pub struct BranchPath {
1112 pub space_name: String,
1213 pub repo_name: String,
13 pub from: String,
14 pub name: String,
1415}
1516
16#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
17pub struct RenameBranchQuery {
18 pub to: String,
17#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
18pub struct RenameBranchCmd {
19 pub new_name: String,
1920}
2021
2122#[utoipa::path(
2223 patch,
23 path = "/repos/{space_name}/{repo_name}/branches/{from}",
24 path = "/repos/{space_name}/{repo_name}/branches/{name}",
2425 tag = "branches",
25 params(RenameBranchPath, RenameBranchQuery),
26 params(BranchPath),
27 request_body = RenameBranchCmd,
2628 responses(
2729 (status = 200, description = "rename branch", body = AppResponseData<NoData>)
2830 )
@@ -30,8 +32,8 @@
3032pub async fn rename_branch(
3133 State(service): State<GitService>,
3234 session: Session,
33 Path(path): Path<RenameBranchPath>,
34 Query(query): Query<RenameBranchQuery>,
35 Path(path): Path<BranchPath>,
36 Json(cmd): Json<RenameBranchCmd>,
3537) -> Result<AppResponseData<NoData>, ApiError> {
3638 let _ = require_writer(
3739 &service,
@@ -41,7 +43,12 @@
4143 )
4244 .await?;
4345 service
44 .rename_branch(path.space_name, path.repo_name, path.from, query.to)
46 .rename_branch(
47 path.space_name,
48 path.repo_name,
49 path.name,
50 cmd.new_name,
51 )
4552 .await
4653 .map_err(GitApiError::from)?;
4754 Ok(AppResponseData::ok(NoData))
Modified lib/git/src/handler/lfs/unlock.rs lib/git/src/handler/lfs/unlock.rs +10 −12 View lib/git/src/handler/lfs/unlock.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use axum::extract::{Path, Query, State};
32use export::rest::{AppResponseData, NoData};
43use session::Session;
54
@@ -7,30 +6,29 @@
76use crate::handler::{RepoPath, require_repo_admin, require_writer};
87use crate::service::GitService;
98
10#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
11pub struct LfsUnlockBody {
9#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
10pub struct LfsUnlockPathQuery {
1211 pub path: String,
1312 #[serde(default)]
1413 pub force: Option<bool>,
1514}
1615
1716#[utoipa::path(
18 post,
19 path = "/repos/{space_name}/{repo_name}/lfs/locks/unlock",
17 delete,
18 path = "/repos/{space_name}/{repo_name}/lfs/locks",
2019 tag = "lfs",
21 params(RepoPath),
22 request_body = LfsUnlockBody,
20 params(RepoPath, LfsUnlockPathQuery),
2321 responses(
24 (status = 200, description = "unlock lfs path", body = AppResponseData<NoData>)
22 (status = 200, description = "remove lfs lock by path", body = AppResponseData<NoData>)
2523 )
2624)]
2725pub async fn unlock(
2826 State(service): State<GitService>,
2927 session: Session,
3028 Path(path): Path<RepoPath>,
31 Json(body): Json<LfsUnlockBody>,
29 Query(q): Query<LfsUnlockPathQuery>,
3230) -> Result<AppResponseData<NoData>, ApiError> {
33 let force = body.force.unwrap_or(false);
31 let force = q.force.unwrap_or(false);
3432 let uid = if force {
3533 require_repo_admin(
3634 &service,
@@ -51,7 +49,7 @@
5149 uid
5250 };
5351 service
54 .lfs_unlock(path.space_name, path.repo_name, uid, body.path, force)
52 .lfs_unlock(path.space_name, path.repo_name, uid, q.path, force)
5553 .await
5654 .map_err(GitApiError::from)?;
5755 Ok(AppResponseData::ok(NoData))
Modified lib/git/src/handler/lfs/unlock_by_id.rs lib/git/src/handler/lfs/unlock_by_id.rs +9 −11 View lib/git/src/handler/lfs/unlock_by_id.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use axum::extract::{Path, Query, State};
32use export::rest::{AppResponseData, NoData};
43use session::Session;
54
@@ -14,29 +13,28 @@
1413 pub lock_id: String,
1514}
1615
17#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
18pub struct LfsUnlockByIdBody {
16#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
17pub struct LfsUnlockQuery {
1918 #[serde(default)]
2019 pub force: Option<bool>,
2120}
2221
2322#[utoipa::path(
24 post,
25 path = "/repos/{space_name}/{repo_name}/lfs/locks/{lock_id}/unlock",
23 delete,
24 path = "/repos/{space_name}/{repo_name}/lfs/locks/{lock_id}",
2625 tag = "lfs",
27 params(LfsLockIdPath),
28 request_body = LfsUnlockByIdBody,
26 params(LfsLockIdPath, LfsUnlockQuery),
2927 responses(
30 (status = 200, description = "unlock lfs lock by id", body = AppResponseData<NoData>)
28 (status = 200, description = "remove lfs lock by id", body = AppResponseData<NoData>)
3129 )
3230)]
3331pub async fn unlock_by_id(
3432 State(service): State<GitService>,
3533 session: Session,
3634 Path(path): Path<LfsLockIdPath>,
37 Json(body): Json<LfsUnlockByIdBody>,
35 Query(q): Query<LfsUnlockQuery>,
3836) -> Result<AppResponseData<NoData>, ApiError> {
39 let force = body.force.unwrap_or(false);
37 let force = q.force.unwrap_or(false);
4038 let uid = if force {
4139 require_repo_admin(
4240 &service,
Modified lib/git/src/handler/remotes/add.rs lib/git/src/handler/remotes/add.rs +4 −4 View lib/git/src/handler/remotes/add.rs
@@ -20,16 +20,16 @@
2020}
2121
2222#[utoipa::path(
23 post,
23 put,
2424 path = "/repos/{space_name}/{repo_name}/remotes/{name}",
2525 tag = "remotes",
2626 params(RemotePath),
2727 request_body = RemoteAddBody,
2828 responses(
29 (status = 200, description = "add remote", body = AppResponseData<NoData>)
29 (status = 200, description = "remote created or url updated (idempotent)", body = AppResponseData<NoData>)
3030 )
3131)]
32pub async fn add(
32pub async fn upsert_remote(
3333 State(service): State<GitService>,
3434 session: Session,
3535 Path(path): Path<RemotePath>,
@@ -43,7 +43,7 @@
4343 )
4444 .await?;
4545 service
46 .remote_add(path.space_name, path.repo_name, path.name, body.url)
46 .remote_upsert(path.space_name, path.repo_name, path.name, body.url)
4747 .await
4848 .map_err(GitApiError::from)?;
4949 Ok(AppResponseData::ok(NoData))
Added lib/git/src/handler/remotes/list.rs lib/git/src/handler/remotes/list.rs +35 −0 View lib/git/src/handler/remotes/list.rs
@@ -0,0 +1,35 @@
1use axum::extract::{Path, State};
2use export::rest::AppResponseData;
3use session::Session;
4
5use crate::errors::{ApiError, GitApiError};
6use crate::handler::ensure_read_access;
7use crate::handler::RepoPath;
8use crate::service::GitService;
9use crate::service::remotes::RemoteDto;
10
11#[utoipa::path(
12 get,
13 path = "/repos/{space_name}/{repo_name}/remotes",
14 tag = "remotes",
15 params(RepoPath),
16 responses(
17 (status = 200, description = "configured remotes", body = AppResponseData<Vec<RemoteDto>>)
18 )
19)]
20pub async fn list_remotes(
21 State(service): State<GitService>,
22 session: Session,
23 Path(path): Path<RepoPath>,
24) -> Result<AppResponseData<Vec<RemoteDto>>, ApiError> {
25 let ctx = service
26 .context(path.space_name.clone(), path.repo_name.clone())
27 .await
28 .map_err(GitApiError::from)?;
29 ensure_read_access(&service, &session, &ctx).await?;
30 let rows = service
31 .remote_list(path.space_name, path.repo_name)
32 .await
33 .map_err(GitApiError::from)?;
34 Ok(AppResponseData::ok(rows))
35}
Modified lib/git/src/handler/remotes/mod.rs lib/git/src/handler/remotes/mod.rs +1 −0 View lib/git/src/handler/remotes/mod.rs
@@ -1,4 +1,5 @@
11pub mod add;
2pub mod list;
23pub mod clone_import;
34pub mod fetch;
45pub mod push;
Modified lib/git/src/handler/repos/get.rs lib/git/src/handler/repos/get.rs +10 −2 View lib/git/src/handler/repos/get.rs
@@ -21,16 +21,24 @@
2121 session: Session,
2222 Path(path): Path<RepoPath>,
2323) -> Result<AppResponseData<RepoDto>, ApiError> {
24 resolve_reader(
24 let (ctx, viewer) = resolve_reader(
2525 &service,
2626 &session,
2727 path.space_name.clone(),
2828 path.repo_name.clone(),
2929 )
3030 .await?;
31 let out = service
31 let mut out = service
3232 .get_repo(path.space_name, path.repo_name)
3333 .await
3434 .map_err(GitApiError::from)?;
35 if let Some(uid) = viewer {
36 out.me = Some(
37 service
38 .repo_viewer_state(ctx.model.id, uid)
39 .await
40 .map_err(GitApiError::from)?,
41 );
42 }
3543 Ok(AppResponseData::ok(out))
3644}
Modified lib/git/src/handler/stars.rs lib/git/src/handler/stars.rs +9 −39 View lib/git/src/handler/stars.rs
@@ -1,11 +1,11 @@
11use axum::extract::{Path, Query, State};
22use export::rest::AppResponseData;
3use serde::{Deserialize, Serialize};
3use serde::Deserialize;
44use session::Session;
55use utoipa::IntoParams;
66
77use crate::errors::{ApiError, GitApiError};
8use crate::handler::{RepoPath, require_writer};
8use crate::handler::{RepoPath, resolve_reader};
99use crate::service::GitService;
1010use crate::service::stars::{RepoStarResp, StargazerDto};
1111
@@ -19,11 +19,6 @@
1919pub struct StarQuery {
2020 pub limit: Option<u64>,
2121 pub offset: Option<u64>,
22}
23
24#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
25pub struct StarStatusResp {
26 pub starred: bool,
2722}
2823
2924#[utoipa::path(
@@ -40,13 +35,14 @@
4035 session: Session,
4136 Path(path): Path<RepoPath>,
4237) -> Result<AppResponseData<RepoStarResp>, ApiError> {
43 let (ctx, uid) = require_writer(
38 let (ctx, viewer) = resolve_reader(
4439 &service,
4540 &session,
4641 path.space_name.clone(),
4742 path.repo_name.clone(),
4843 )
4944 .await?;
45 let uid = viewer.ok_or_else(|| ApiError::from(GitApiError::Unauthorized))?;
5046 let out = service
5147 .star_repo(&ctx, uid)
5248 .await
@@ -68,13 +64,14 @@
6864 session: Session,
6965 Path(path): Path<RepoPath>,
7066) -> Result<AppResponseData<RepoStarResp>, ApiError> {
71 let (ctx, uid) = require_writer(
67 let (ctx, viewer) = resolve_reader(
7268 &service,
7369 &session,
7470 path.space_name.clone(),
7571 path.repo_name.clone(),
7672 )
7773 .await?;
74 let uid = viewer.ok_or_else(|| ApiError::from(GitApiError::Unauthorized))?;
7875 let out = service
7976 .unstar_repo(&ctx, uid)
8077 .await
@@ -84,34 +81,6 @@
8481
8582#[utoipa::path(
8683 get,
87 path = "/repos/{space_name}/{repo_name}/star",
88 tag = "repos",
89 params(RepoPath),
90 responses(
91 (status = 200, description = "check star status", body = AppResponseData<StarStatusResp>)
92 )
93)]
94pub async fn is_starred(
95 State(service): State<GitService>,
96 session: Session,
97 Path(path): Path<RepoPath>,
98) -> Result<AppResponseData<StarStatusResp>, ApiError> {
99 let (ctx, uid) = require_writer(
100 &service,
101 &session,
102 path.space_name.clone(),
103 path.repo_name.clone(),
104 )
105 .await?;
106 let starred = service
107 .is_starred(&ctx, uid)
108 .await
109 .map_err(GitApiError::from)?;
110 Ok(AppResponseData::ok(StarStatusResp { starred }))
111}
112
113#[utoipa::path(
114 get,
11584 path = "/repos/{space_name}/{repo_name}/stargazers",
11685 tag = "repos",
11786 params(RepoPath, StarQuery),
@@ -125,13 +94,14 @@
12594 Path(path): Path<RepoPath>,
12695 Query(q): Query<StarQuery>,
12796) -> Result<AppResponseData<Vec<StargazerDto>>, ApiError> {
128 let (ctx, _uid) = require_writer(
97 let ctx = resolve_reader(
12998 &service,
13099 &session,
131100 path.space_name.clone(),
132101 path.repo_name.clone(),
133102 )
134 .await?;
103 .await?
104 .0;
135105 let limit = q.limit.unwrap_or(50).clamp(1, 100);
136106 let offset = q
137107 .offset
Modified lib/git/src/handler/watches.rs lib/git/src/handler/watches.rs +6 −66 View lib/git/src/handler/watches.rs
@@ -7,7 +7,7 @@
77use utoipa::IntoParams;
88
99use crate::errors::{ApiError, GitApiError};
10use crate::handler::{RepoPath, require_writer};
10use crate::handler::{RepoPath, resolve_reader};
1111use crate::service::GitService;
1212use crate::service::watches::{RepoWatchDto, UpsertWatchCmd, WatcherDto};
1313
@@ -34,13 +34,14 @@
3434 Path(path): Path<RepoPath>,
3535 Json(cmd): Json<UpsertWatchCmd>,
3636) -> Result<AppResponseData<RepoWatchDto>, ApiError> {
37 let (ctx, uid) = require_writer(
37 let (ctx, viewer) = resolve_reader(
3838 &service,
3939 &session,
4040 path.space_name.clone(),
4141 path.repo_name.clone(),
4242 )
4343 .await?;
44 let uid = viewer.ok_or_else(|| ApiError::from(GitApiError::Unauthorized))?;
4445 let out = service
4546 .upsert_watch(&ctx, uid, cmd)
4647 .await
@@ -49,68 +50,6 @@
4950}
5051
5152#[utoipa::path(
52 delete,
53 path = "/repos/{space_name}/{repo_name}/watch",
54 tag = "repos",
55 params(RepoPath),
56 responses(
57 (status = 200, description = "unwatch repo", body = AppResponseData<RepoWatchDto>)
58 )
59)]
60pub async fn unwatch(
61 State(service): State<GitService>,
62 session: Session,
63 Path(path): Path<RepoPath>,
64) -> Result<AppResponseData<RepoWatchDto>, ApiError> {
65 let (ctx, uid) = require_writer(
66 &service,
67 &session,
68 path.space_name.clone(),
69 path.repo_name.clone(),
70 )
71 .await?;
72 service
73 .unwatch(&ctx, uid)
74 .await
75 .map_err(GitApiError::from)?;
76 Ok(AppResponseData::ok(RepoWatchDto {
77 user_id: uid,
78 repo_id: ctx.model.id,
79 mode: WatchMode::Unwatched,
80 created_at: time::OffsetDateTime::now_utc(),
81 updated_at: time::OffsetDateTime::now_utc(),
82 }))
83}
84
85#[utoipa::path(
86 get,
87 path = "/repos/{space_name}/{repo_name}/watch",
88 tag = "repos",
89 params(RepoPath),
90 responses(
91 (status = 200, description = "current watch mode", body = AppResponseData<Option<WatchMode>>)
92 )
93)]
94pub async fn get_watch_mode(
95 State(service): State<GitService>,
96 session: Session,
97 Path(path): Path<RepoPath>,
98) -> Result<AppResponseData<Option<WatchMode>>, ApiError> {
99 let (ctx, uid) = require_writer(
100 &service,
101 &session,
102 path.space_name.clone(),
103 path.repo_name.clone(),
104 )
105 .await?;
106 let mode = service
107 .get_watch_mode(&ctx, uid)
108 .await
109 .map_err(GitApiError::from)?;
110 Ok(AppResponseData::ok(mode))
111}
112
113#[utoipa::path(
11453 get,
11554 path = "/repos/{space_name}/{repo_name}/watchers",
11655 tag = "repos",
@@ -125,13 +64,14 @@
12564 Path(path): Path<RepoPath>,
12665 Query(q): Query<WatchQuery>,
12766) -> Result<AppResponseData<Vec<WatcherDto>>, ApiError> {
128 let (ctx, _uid) = require_writer(
67 let ctx = resolve_reader(
12968 &service,
13069 &session,
13170 path.space_name.clone(),
13271 path.repo_name.clone(),
13372 )
134 .await?;
73 .await?
74 .0;
13575 let limit = q.limit.unwrap_or(50).clamp(1, 100);
13676 let offset = q
13777 .offset
Modified lib/git/src/service/remotes.rs lib/git/src/service/remotes.rs +76 −0 View lib/git/src/service/remotes.rs
@@ -173,6 +173,75 @@
173173 .map_err(map_baby)
174174 }
175175
176 pub async fn remote_upsert(
177 &self,
178 space_name: String,
179 repo_name: String,
180 name: String,
181 url: String,
182 ) -> Result<(), GitError> {
183 validate_remote_name(&name)?;
184 validate_remote_url(&url)?;
185 let ctx = self.context(space_name, repo_name).await?;
186 let baby = self.baby(&ctx);
187 if baby.get_remote_address(&name).await.map_err(map_baby)?.is_some() {
188 run_git_soft(
189 "remotes::set_url",
190 &ctx.repo_root,
191 vec!["remote".to_string(), "set-url".to_string(), name, url],
192 &[],
193 30,
194 )
195 .await?;
196 Ok(())
197 } else {
198 baby.add_remote(&name, &url, gitbaby::remote::types::RemoteOption::default())
199 .await
200 .map_err(map_baby)
201 }
202 }
203
204 pub async fn remote_list(
205 &self,
206 space_name: String,
207 repo_name: String,
208 ) -> Result<Vec<RemoteDto>, GitError> {
209 let ctx = self.context(space_name, repo_name).await?;
210 let out = run_git_soft(
211 "remotes::list",
212 &ctx.repo_root,
213 vec!["remote".to_string(), "-v".to_string()],
214 &[],
215 30,
216 )
217 .await?;
218 let text = String::from_utf8_lossy(&out.stdout);
219 let mut map: std::collections::HashMap<String, RemoteDto> =
220 std::collections::HashMap::new();
221 for line in text.lines() {
222 let mut parts = line.split_whitespace();
223 let (Some(name), Some(url), Some(kind)) = (parts.next(), parts.next(), parts.next())
224 else {
225 continue;
226 };
227 let entry = map
228 .entry(name.to_string())
229 .or_insert_with(|| RemoteDto {
230 name: name.to_string(),
231 fetch_url: None,
232 push_url: None,
233 });
234 if kind.starts_with("(fetch") {
235 entry.fetch_url = Some(url.to_string());
236 } else if kind.starts_with("(push") {
237 entry.push_url = Some(url.to_string());
238 }
239 }
240 let mut rows: Vec<RemoteDto> = map.into_values().collect();
241 rows.sort_by(|a, b| a.name.cmp(&b.name));
242 Ok(rows)
243 }
244
176245 pub async fn remote_remove(
177246 &self,
178247 space_name: String,
@@ -185,6 +254,13 @@
185254 }
186255}
187256
257#[derive(Debug, Clone, serde::Serialize, utoipa::ToSchema)]
258pub struct RemoteDto {
259 pub name: String,
260 pub fetch_url: Option<String>,
261 pub push_url: Option<String>,
262}
263
188264async fn count_refs(dir: &std::path::Path) -> Result<i64, GitError> {
189265 let timeout = 60;
190266 let out = run_git_soft(
Modified lib/git/src/service/repos.rs lib/git/src/service/repos.rs +34 −0 View lib/git/src/service/repos.rs
@@ -53,9 +53,17 @@
5353 pub updated_at: OffsetDateTime,
5454 pub last_pushed_at: Option<OffsetDateTime>,
5555 pub archived_at: Option<OffsetDateTime>,
56 #[serde(default)]
57 pub me: Option<RepoMeDto>,
5658}
5759
5860#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
61pub struct RepoMeDto {
62 pub starred: bool,
63 pub watch_mode: Option<models::common::watch_mode::WatchMode>,
64}
65
66#[derive(Debug, Clone, Serialize, utoipa::ToSchema)]
5967pub struct RepoSummaryDto {
6068 pub id: Uuid,
6169 pub name: String,
@@ -129,6 +137,7 @@
129137 updated_at: m.updated_at,
130138 last_pushed_at: m.last_pushed_at,
131139 archived_at: m.archived_at,
140 me: None,
132141 }
133142 }
134143}
@@ -375,6 +384,31 @@
375384 Ok(RepoDto::from(ctx.model))
376385 }
377386
387 pub async fn repo_viewer_state(
388 &self,
389 repo_id: Uuid,
390 uid: Uuid,
391 ) -> Result<RepoMeDto, GitError> {
392 use models::repo::repo_star;
393 use models::repo::repo_watch;
394 let starred = repo_star::Entity::find()
395 .filter(repo_star::Column::RepoId.eq(repo_id))
396 .filter(repo_star::Column::UserId.eq(uid))
397 .one(self.db_read())
398 .await?
399 .is_some();
400 let watch_mode = repo_watch::Entity::find()
401 .filter(repo_watch::Column::RepoId.eq(repo_id))
402 .filter(repo_watch::Column::UserId.eq(uid))
403 .one(self.db_read())
404 .await?
405 .map(|w| w.mode);
406 Ok(RepoMeDto {
407 starred,
408 watch_mode,
409 })
410 }
411
378412 pub async fn get_repo_for_html(
379413 &self,
380414 ctx: std::sync::Arc<crate::context::facade::GitContext>,
Modified lib/git/src/openapi.rs lib/git/src/openapi.rs +7 −7 View lib/git/src/openapi.rs
@@ -54,7 +54,8 @@
5454 handler::pipelines::list_runs::list_runs,
5555 handler::pipelines::schedule::schedule,
5656 handler::pipelines::trigger::trigger,
57 handler::remotes::add::add,
57 handler::remotes::add::upsert_remote,
58 handler::remotes::list::list_remotes,
5859 handler::remotes::clone_import::clone_import,
5960 handler::remotes::fetch::fetch,
6061 handler::remotes::push::push,
@@ -67,7 +68,6 @@
6768 handler::repos::update::update_repo,
6869 handler::signatures::verify_commit::verify_commit,
6970 handler::signatures::verify_tag::verify_tag,
70 handler::stars::is_starred,
7171 handler::stars::list_stargazers,
7272 handler::stars::star_repo,
7373 handler::stars::unstar_repo,
@@ -85,9 +85,7 @@
8585 handler::topics::list_topics,
8686 handler::topics::remove_topic_from_repo,
8787 handler::trees::read::get_tree,
88 handler::watches::get_watch_mode,
8988 handler::watches::list_watchers,
90 handler::watches::unwatch,
9189 handler::watches::upsert_watch,
9290 handler::whitelists::add_branch_whitelist,
9391 handler::whitelists::add_tag_whitelist,
@@ -152,12 +150,17 @@
152150 crate::service::refs::BranchInfoDto,
153151 crate::service::refs::HeadInfoDto,
154152 crate::service::refs::UpdateRefResultDto,
153 crate::service::remotes::RemoteDto,
155154 crate::service::remotes::FetchResultDto,
155 crate::service::remotes::RemoteDto,
156156 crate::service::remotes::ImportResultDto,
157 crate::service::remotes::RemoteDto,
157158 crate::service::remotes::PushResultDto,
159 crate::service::remotes::RemoteDto,
158160 crate::service::remotes::RemoteAuthDto,
159161 crate::service::repos::CreateRepoCmd,
160162 crate::service::repos::RepoDto,
163 crate::service::repos::RepoMeDto,
161164 crate::service::repos::RepoSummaryDto,
162165 crate::service::repos::UpdateRepoCmd,
163166 crate::service::signatures::VerifyResultDto,
@@ -187,8 +190,6 @@
187190 crate::handler::hooks::execute_script::HookExecBody,
188191 crate::handler::lfs::cleanup_unreferenced::LfsCleanupResponse,
189192 crate::handler::lfs::lock::LfsLockBody,
190 crate::handler::lfs::unlock::LfsUnlockBody,
191 crate::handler::lfs::unlock_by_id::LfsUnlockByIdBody,
192193 crate::handler::patches::apply::PatchApplyBody,
193194 crate::handler::pipelines::cancel::PipelineCancelBody,
194195 crate::handler::pipelines::schedule::PipelineScheduleBody,
@@ -197,7 +198,6 @@
197198 crate::handler::remotes::clone_import::RemoteImportBody,
198199 crate::handler::remotes::fetch::RemoteFetchBody,
199200 crate::handler::remotes::push::RemotePushBody,
200 crate::handler::stars::StarStatusResp,
201201 ))
202202)]
203203pub struct GitApiDoc;
Modified lib/git/src/routes.rs lib/git/src/routes.rs +13 −12 View lib/git/src/routes.rs
@@ -39,8 +39,8 @@
3939use crate::handler::lfs::cleanup_unreferenced::cleanup_unreferenced;
4040use crate::handler::lfs::list_locks::list_locks;
4141use crate::handler::lfs::lock::lock;
42use crate::handler::lfs::unlock::unlock;
43use crate::handler::lfs::unlock_by_id::unlock_by_id;
42use crate::handler::lfs::unlock::unlock as unlock_by_path;
43
4444use crate::handler::lfs::verify_locks::verify_locks;
4545use crate::handler::licenses::{get_license, list_licenses, upsert_license};
4646use crate::handler::maintenance::fsck::fsck;
@@ -67,7 +67,8 @@
6767use crate::handler::refs::list::refs_list;
6868use crate::handler::refs::resolve::refs_resolve;
6969use crate::handler::refs::update::refs_update;
70use crate::handler::remotes::add::add as remote_add;
70use crate::handler::remotes::add::upsert_remote;
71use crate::handler::remotes::list::list_remotes;
7172use crate::handler::remotes::clone_import::clone_import as remote_clone_import;
7273use crate::handler::remotes::fetch::fetch as remote_fetch;
7374use crate::handler::remotes::push::push as remote_push;
@@ -79,7 +80,7 @@
7980use crate::handler::repos::update::update_repo as repo_update;
8081use crate::handler::signatures::verify_commit::verify_commit;
8182use crate::handler::signatures::verify_tag::verify_tag;
82use crate::handler::stars::{is_starred, list_stargazers, star_repo, unstar_repo};
83use crate::handler::stars::{list_stargazers, star_repo, unstar_repo};
8384use crate::handler::submodules::list::submodule_list as submodules_list;
8485use crate::handler::submodules::resolve::submodule_resolve as submodules_resolve;
8586use crate::handler::tags::create::create_tag as tag_create;
@@ -91,7 +92,7 @@
9192 remove_topic_from_repo,
9293};
9394use crate::handler::trees::read::get_tree;
94use crate::handler::watches::{get_watch_mode, list_watchers, unwatch, upsert_watch};
95use crate::handler::watches::{list_watchers, upsert_watch};
9596use crate::handler::whitelists::{
9697 add_branch_whitelist, add_tag_whitelist, list_branch_whitelists, list_tag_whitelists,
9798 remove_branch_whitelist, remove_tag_whitelist,
@@ -223,20 +224,20 @@
223224 .route("/{space_name}/{repo_name}/remotes/push", post(remote_push))
224225 .route(
225226 "/{space_name}/{repo_name}/remotes/{name}",
226 post(remote_add).delete(remote_remove),
227 put(upsert_remote).delete(remote_remove),
227228 )
229 .route("/{space_name}/{repo_name}/remotes", get(list_remotes))
228230 .route(
229231 "/{space_name}/{repo_name}/lfs/locks",
230 get(list_locks).post(lock),
232 get(list_locks).post(lock).delete(unlock_by_path),
231233 )
232 .route("/{space_name}/{repo_name}/lfs/locks/unlock", post(unlock))
233234 .route(
234235 "/{space_name}/{repo_name}/lfs/locks/verify",
235236 post(verify_locks),
236237 )
237238 .route(
238 "/{space_name}/{repo_name}/lfs/locks/{lock_id}/unlock",
239 post(unlock_by_id),
239 "/{space_name}/{repo_name}/lfs/locks/{lock_id}",
240 delete(crate::handler::lfs::unlock_by_id::unlock_by_id),
240241 )
241242 .route(
242243 "/{space_name}/{repo_name}/hooks/{script}",
@@ -276,12 +277,12 @@
276277 )
277278 .route(
278279 "/{space_name}/{repo_name}/star",
279 put(star_repo).delete(unstar_repo).get(is_starred),
280 put(star_repo).delete(unstar_repo),
280281 )
281282 .route("/{space_name}/{repo_name}/stargazers", get(list_stargazers))
282283 .route(
283284 "/{space_name}/{repo_name}/watch",
284 put(upsert_watch).delete(unwatch).get(get_watch_mode),
285 put(upsert_watch),
285286 )
286287 .route("/{space_name}/{repo_name}/watchers", get(list_watchers))
287288 .route("/{space_name}/{repo_name}/topics", get(list_repo_topics))
Deleted lib/issues/src/handlers/watches/delete_watch.rs lib/issues/src/handlers/watches/delete_watch.rs +0 −32
@@ -1,32 +0,0 @@
1use axum::extract::{Path, State};
2use export::rest::{AppResponseData, NoData};
3use session::Session;
4
5use crate::errors::ApiError;
6use crate::services::service::IssueService;
7use crate::services::watches::WatchPath;
8use session::share::session::require_uid;
9
10#[utoipa::path(
11 delete,
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/watch",
13 tag = "issues",
14 params(WatchPath),
15 responses(
16 (status = 200, description = "delete watch", body = AppResponseData<NoData>),
17 (status = 403, description = "forbidden", body = AppResponseData<NoData>),
18 (status = 404, description = "not found", body = AppResponseData<NoData>)
19 )
20)]
21pub async fn delete_watch(
22 State(service): State<IssueService>,
23 session: Session,
24 Path(path): Path<WatchPath>,
25) -> Result<AppResponseData<NoData>, ApiError> {
26 let uid = require_uid(&session)?;
27 let issue_id = service.resolve_issue_ref(uid, &path.space_name, path.board_id, &path.issue_id).await?;
28 service
29 .delete_watch(uid, &path.space_name, path.board_id, issue_id)
30 .await?;
31 Ok(AppResponseData::ok(NoData))
32}
Deleted lib/issues/src/handlers/watches/get_watch.rs lib/issues/src/handlers/watches/get_watch.rs +0 −32
@@ -1,32 +0,0 @@
1use axum::extract::{Path, State};
2use export::rest::{AppResponseData, NoData};
3use session::Session;
4
5use crate::errors::ApiError;
6use crate::services::service::IssueService;
7use crate::services::watches::{WatchDto, WatchPath};
8use session::share::session::require_uid;
9
10#[utoipa::path(
11 get,
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/watch",
13 tag = "issues",
14 params(WatchPath),
15 responses(
16 (status = 200, description = "get watch status", body = AppResponseData<WatchDto>),
17 (status = 403, description = "forbidden", body = AppResponseData<NoData>),
18 (status = 404, description = "not found", body = AppResponseData<NoData>)
19 )
20)]
21pub async fn get_watch(
22 State(service): State<IssueService>,
23 session: Session,
24 Path(path): Path<WatchPath>,
25) -> Result<AppResponseData<WatchDto>, ApiError> {
26 let uid = require_uid(&session)?;
27 let issue_id = service.resolve_issue_ref(uid, &path.space_name, path.board_id, &path.issue_id).await?;
28 let dto = service
29 .get_watch(uid, &path.space_name, path.board_id, issue_id)
30 .await?;
31 Ok(AppResponseData::ok(dto))
32}
Modified lib/issues/src/handlers/watches/mod.rs lib/issues/src/handlers/watches/mod.rs +0 −2 View lib/issues/src/handlers/watches/mod.rs
@@ -1,3 +1 @@
1pub mod delete_watch;
2pub mod get_watch;
31pub mod upsert_watch;
Modified lib/issues/src/services/issues.rs lib/issues/src/services/issues.rs +14 −5 View lib/issues/src/services/issues.rs
@@ -55,6 +55,7 @@
5555 pub priority: IssuePriority,
5656 pub due_at: Option<OffsetDateTime>,
5757 pub assignees: Vec<UserBriefDto>,
58 pub viewer_watch: Option<models::common::watch_mode::WatchMode>,
5859 pub state: IssueState,
5960 pub created_at: OffsetDateTime,
6061 pub updated_at: Option<OffsetDateTime>,
@@ -263,6 +264,7 @@
263264
264265 pub(crate) async fn build_issue_dto(
265266 &self,
267 caller: Uuid,
266268 item: issue_item::Model,
267269 ) -> Result<IssueDto, IssueError> {
268270 let user_map = self.load_user_briefs(&[item.author_id]).await?;
@@ -277,7 +279,14 @@
277279 } else {
278280 IssueState::Open
279281 };
282 let viewer_watch = issue_watch::Entity::find()
283 .filter(issue_watch::Column::IssueId.eq(item.id))
284 .filter(issue_watch::Column::UserId.eq(caller))
285 .one(self.db().read())
286 .await?
287 .map(|w| w.mode);
280288 Ok(IssueDto {
289 viewer_watch,
281290 id: item.id,
282291 board_id: item.collection_id,
283292 number: item.autoincrement_id,
@@ -528,7 +537,7 @@
528537 }
529538 }
530539
531 self.build_issue_dto(inserted).await
540 self.build_issue_dto(caller, inserted).await
532541 }
533542
534543 pub async fn list_issues(
@@ -672,7 +681,7 @@
672681 let board = lookup_board(self, &space, board_id, caller).await?;
673682 let item = lookup_issue(self, &board, issue_id).await?;
674683
675 self.build_issue_dto(item).await
684 self.build_issue_dto(caller, item).await
676685 }
677686
678687 pub async fn update_issue(
@@ -951,7 +960,7 @@
951960 })
952961 .await?;
953962
954 self.build_issue_dto(updated).await
963 self.build_issue_dto(caller, updated).await
955964 }
956965
957966}
@@ -1085,7 +1094,7 @@
10851094
10861095 let is_currently_closed = item.closed_at.is_some();
10871096 if is_currently_closed == closed {
1088 return self.build_issue_dto(item).await;
1097 return self.build_issue_dto(caller, item).await;
10891098 }
10901099
10911100 let now = OffsetDateTime::now_utc();
@@ -1109,6 +1118,6 @@
11091118 })
11101119 .await?;
11111120
1112 self.build_issue_dto(updated).await
1121 self.build_issue_dto(caller, updated).await
11131122 }
11141123}
Modified lib/issues/src/openapi.rs lib/issues/src/openapi.rs +0 −2 View lib/issues/src/openapi.rs
@@ -44,9 +44,7 @@
4444 crate::handlers::reactions::list_comment_reactions::list_comment_reactions,
4545 crate::handlers::reactions::add_comment_reaction::add_comment_reaction,
4646 crate::handlers::reactions::remove_comment_reaction::remove_comment_reaction,
47 crate::handlers::watches::get_watch::get_watch,
4847 crate::handlers::watches::upsert_watch::upsert_watch,
49 crate::handlers::watches::delete_watch::delete_watch,
5048 crate::handlers::members::list_members::list_members,
5149 crate::handlers::members::create_member::create_member,
5250 crate::handlers::members::delete_member::delete_member,
Modified lib/issues/src/routes.rs lib/issues/src/routes.rs +2 −4 View lib/issues/src/routes.rs
@@ -1,5 +1,5 @@
11use axum::Router;
2use axum::routing::{delete, get, patch, post};
2use axum::routing::{delete, get, patch, post, put};
33
44use crate::handlers::boards::create_board::create_board;
55use crate::handlers::boards::delete_board::delete_board;
@@ -57,8 +57,6 @@
5757use crate::handlers::stopwatch::start_stopwatch::start_stopwatch;
5858use crate::handlers::stopwatch::stop_stopwatch::stop_stopwatch;
5959use crate::handlers::tracked_time::list_tracked_time::list_tracked_time;
60use crate::handlers::watches::delete_watch::delete_watch;
61use crate::handlers::watches::get_watch::get_watch;
6260use crate::handlers::watches::upsert_watch::upsert_watch;
6361use crate::services::service::IssueService;
6462use crate::share::guard::issue_guard;
@@ -148,7 +146,7 @@
148146 )
149147 .route(
150148 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/watch",
151 get(get_watch).put(upsert_watch).delete(delete_watch),
149 put(upsert_watch),
152150 )
153151 .route(
154152 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/members",