feat(api): add admin user management and update issue paths for consistency
ZhenYi committed
This change is very large, so some diffs are not shown. Clone the repository to see the full change.
Modified app/apihost/src/main.rs app/apihost/src/main.rs +7 −0 View app/apihost/src/main.rs
@@ -24,6 +24,9 @@
2424 .context("building AppState")?;
2525 info!("dependencies ready");
2626
27 let meter_provider = webapi::metrics::init_meter_provider()
28 .map_err(|err| anyhow::anyhow!("failed to init meter provider: {err}"))?;
29
2730 tokio::spawn(chat::worker::run_background_tasks(state.clone()));
2831
2932 let api_router: Router = router(state)
@@ -49,6 +52,10 @@
4952 .await
5053 .context("axum server error")?;
5154
55 if let Err(err) = meter_provider.shutdown() {
56 warn!(error = %err, "failed to shut down meter provider");
57 }
58
5259 info!("apihost shut down");
5360 Ok(())
5461}
Modified config/default.toml config/default.toml +1 −0 View config/default.toml
@@ -224,6 +224,7 @@
224224enabled = true
225225bind_addr = "0.0.0.0"
226226port = 3000
227admin_uids = []
227228
228229[githost]
229230enabled = true
Modified lib/auth/src/handlers/active_suspension.rs lib/auth/src/handlers/active_suspension.rs +1 −1 View lib/auth/src/handlers/active_suspension.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 get,
12 path = "/suspension",
12 path = "/me/suspension",
1313 tag = "suspension",
1414 responses(
1515 (status = 200, description = "active suspension for the current user, none when absent", body = AppResponseData<Option<SuspensionDto>>)
Modified lib/auth/src/handlers/cancel_deletion.rs lib/auth/src/handlers/cancel_deletion.rs +1 −1 View lib/auth/src/handlers/cancel_deletion.rs
@@ -8,7 +8,7 @@
88
99#[utoipa::path(
1010 post,
11 path = "/deletion/cancel",
11 path = "/me/deletion",
1212 tag = "deletion",
1313 responses(
1414 (status = 200, description = "deletion request cancelled", body = AppResponseData<NoData>)
Modified lib/auth/src/handlers/create_report.rs lib/auth/src/handlers/create_report.rs +1 −1 View lib/auth/src/handlers/create_report.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 post,
12 path = "/reports",
12 path = "/me/reports",
1313 tag = "reports",
1414 request_body = AppRequestData<CreateReportCmd>,
1515 responses(
Modified lib/auth/src/handlers/delete_notification.rs lib/auth/src/handlers/delete_notification.rs +1 −1 View lib/auth/src/handlers/delete_notification.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 delete,
12 path = "/notifications/{notification_id}",
12 path = "/me/notifications/{notification_id}",
1313 tag = "notifications",
1414 params(
1515 ("notification_id" = Uuid, Path, description = "notification id")
Modified lib/auth/src/handlers/deletion_status.rs lib/auth/src/handlers/deletion_status.rs +1 −1 View lib/auth/src/handlers/deletion_status.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 get,
12 path = "/deletion",
12 path = "/me/deletion",
1313 tag = "deletion",
1414 responses(
1515 (status = 200, description = "latest deletion request for the current user", body = AppResponseData<Option<DeletionDto>>)
Modified lib/auth/src/handlers/get_preferences.rs lib/auth/src/handlers/get_preferences.rs +1 −1 View lib/auth/src/handlers/get_preferences.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 get,
12 path = "/auth/preferences",
12 path = "/me/preferences",
1313 tag = "account",
1414 responses(
1515 (status = 200, description = "preferences for the current user, defaults when unset", body = AppResponseData<UserPreferencesDto>)
Modified lib/auth/src/handlers/get_user.rs lib/auth/src/handlers/get_user.rs +4 −4 View lib/auth/src/handlers/get_user.rs
@@ -8,10 +8,10 @@
88
99#[utoipa::path(
1010 get,
11 path = "/users/{uid}",
11 path = "/users/{user_id}",
1212 tag = "users",
1313 params(
14 ("uid" = Uuid, Path, description = "user id")
14 ("user_id" = Uuid, Path, description = "user id")
1515 ),
1616 responses(
1717 (status = 200, description = "public user identity", body = AppResponseData<UserPublic>)
@@ -19,8 +19,8 @@
1919)]
2020pub async fn get_user(
2121 State(service): State<AuthService>,
22 Path(uid): Path<Uuid>,
22 Path(user_id): Path<Uuid>,
2323) -> Result<AppResponseData<UserPublic>, ApiError> {
24 let user = service.public_user(uid).await.map_err(ApiError::from)?;
24 let user = service.public_user(user_id).await.map_err(ApiError::from)?;
2525 Ok(AppResponseData::ok(user))
2626}
Modified lib/auth/src/handlers/list_notifications.rs lib/auth/src/handlers/list_notifications.rs +1 −1 View lib/auth/src/handlers/list_notifications.rs
@@ -17,7 +17,7 @@
1717
1818#[utoipa::path(
1919 get,
20 path = "/notifications",
20 path = "/me/notifications",
2121 tag = "notifications",
2222 params(ListNotificationsQuery),
2323 responses(
Modified lib/auth/src/handlers/mark_all_notifications_read.rs lib/auth/src/handlers/mark_all_notifications_read.rs +1 −1 View lib/auth/src/handlers/mark_all_notifications_read.rs
@@ -8,7 +8,7 @@
88
99#[utoipa::path(
1010 put,
11 path = "/notifications/read-all",
11 path = "/me/notifications/read-state",
1212 tag = "notifications",
1313 responses(
1414 (status = 200, description = "all notifications marked as read", body = AppResponseData<NoData>)
Modified lib/auth/src/handlers/mark_notification_read.rs lib/auth/src/handlers/mark_notification_read.rs +1 −1 View lib/auth/src/handlers/mark_notification_read.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 put,
12 path = "/notifications/{notification_id}/read",
12 path = "/me/notifications/{notification_id}/read",
1313 tag = "notifications",
1414 params(
1515 ("notification_id" = Uuid, Path, description = "notification id")
Modified lib/auth/src/handlers/me.rs lib/auth/src/handlers/me.rs +1 −1 View lib/auth/src/handlers/me.rs
@@ -17,7 +17,7 @@
1717
1818#[utoipa::path(
1919 get,
20 path = "/auth/me",
20 path = "/me",
2121 tag = "account",
2222 responses(
2323 (status = 200, description = "identity and profile of the active user", body = AppResponseData<MeResp>)
Modified lib/auth/src/handlers/mod.rs lib/auth/src/handlers/mod.rs +0 −1 View lib/auth/src/handlers/mod.rs
@@ -40,7 +40,6 @@
4040pub mod totp_setup;
4141pub mod totp_status;
4242pub mod unread_notifications_count;
43pub mod update_birthday;
4443pub mod update_preferences;
4544pub mod update_profile;
4645pub mod verify_email;
Modified lib/auth/src/handlers/request_deletion.rs lib/auth/src/handlers/request_deletion.rs +1 −1 View lib/auth/src/handlers/request_deletion.rs
@@ -16,7 +16,7 @@
1616
1717#[utoipa::path(
1818 post,
19 path = "/deletion",
19 path = "/me/deletion",
2020 tag = "deletion",
2121 request_body = AppRequestData<RequestDeletionCmd>,
2222 responses(
Modified lib/auth/src/handlers/unread_notifications_count.rs lib/auth/src/handlers/unread_notifications_count.rs +1 −1 View lib/auth/src/handlers/unread_notifications_count.rs
@@ -15,7 +15,7 @@
1515
1616#[utoipa::path(
1717 get,
18 path = "/notifications/unread-count",
18 path = "/me/notifications/unread-count",
1919 tag = "notifications",
2020 responses(
2121 (status = 200, description = "number of unread notifications", body = AppResponseData<UnreadCountResp>)
Deleted lib/auth/src/handlers/update_birthday.rs lib/auth/src/handlers/update_birthday.rs +0 −37
@@ -1,37 +0,0 @@
1use axum::extract::State;
2use export::rest::{AppRequest, AppRequestData, AppResponseData, NoData};
3use serde::{Deserialize, Serialize};
4use session::Session;
5use time::Date;
6use utoipa::ToSchema;
7
8use crate::errors::ApiError;
9use crate::services::AuthService;
10use session::share::session::require_uid;
11
12#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
13pub struct UpdateBirthdayCmd {
14 pub date_of_birth: Date,
15}
16
17#[utoipa::path(
18 put,
19 path = "/auth/me/birthday",
20 tag = "account",
21 request_body = AppRequestData<UpdateBirthdayCmd>,
22 responses(
23 (status = 200, description = "birthday stored", body = AppResponseData<NoData>)
24 )
25)]
26pub async fn update_birthday(
27 State(service): State<AuthService>,
28 session: Session,
29 payload: AppRequest<UpdateBirthdayCmd>,
30) -> Result<AppResponseData<NoData>, ApiError> {
31 let uid = require_uid(&session)?;
32 service
33 .update_birthday(uid, payload.into_inner().date_of_birth)
34 .await
35 .map_err(ApiError::from)?;
36 Ok(AppResponseData::empty())
37}
Modified lib/auth/src/handlers/update_preferences.rs lib/auth/src/handlers/update_preferences.rs +1 −1 View lib/auth/src/handlers/update_preferences.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 patch,
12 path = "/auth/preferences",
12 path = "/me/preferences",
1313 tag = "account",
1414 request_body = AppRequestData<UpdatePreferencesCmd>,
1515 responses(
Modified lib/auth/src/handlers/update_profile.rs lib/auth/src/handlers/update_profile.rs +1 −1 View lib/auth/src/handlers/update_profile.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 patch,
12 path = "/auth/me",
12 path = "/me",
1313 tag = "account",
1414 request_body = AppRequestData<UpdateProfileCmd>,
1515 responses(
Modified lib/auth/src/services/profile.rs lib/auth/src/services/profile.rs +8 −46 View lib/auth/src/services/profile.rs
@@ -55,6 +55,7 @@
5555 pub website: Option<String>,
5656 pub location: Option<String>,
5757 pub company: Option<String>,
58 pub date_of_birth: Option<Date>,
5859 pub show_email: Option<bool>,
5960 pub show_location: Option<bool>,
6061 pub show_real_name: Option<bool>,
@@ -128,6 +129,9 @@
128129 if let Some(timezone) = cmd.timezone {
129130 user_active.timezone = Set(Some(timezone));
130131 }
132 if let Some(dob) = cmd.date_of_birth {
133 validate_age(Some(dob), self.config().auth.min_age)?;
134 }
131135 user_active.updated_at = Set(now);
132136
133137 TransactionTrait::transaction(db.primary(), |txn| {
@@ -155,6 +159,9 @@
155159 if let Some(value) = cmd.company {
156160 active.company = Set(Some(value));
157161 }
162 if let Some(value) = cmd.date_of_birth {
163 active.date_of_birth = Set(Some(value));
164 }
158165 if let Some(value) = cmd.show_email {
159166 active.show_email = Set(value);
160167 }
@@ -179,7 +186,7 @@
179186 location: Set(cmd.location),
180187 company: Set(cmd.company),
181188 pronouns_privacy: Set(None),
182 date_of_birth: Set(None),
189 date_of_birth: Set(cmd.date_of_birth),
183190 banner_url: Set(None),
184191 banner_hash: Set(None),
185192 banner_color: Set(None),
@@ -203,50 +210,5 @@
203210 })
204211 .await
205212 .map_err(AuthError::from)
206 }
207
208 pub async fn update_birthday(&self, uid: Uuid, date_of_birth: Date) -> Result<bool, AuthError> {
209 validate_age(Some(date_of_birth), self.config().auth.min_age)?;
210 let db = self.db();
211 let now = OffsetDateTime::now_utc();
212 let existing = user_profiles::Entity::find_by_id(uid)
213 .one(db.write())
214 .await?;
215 if let Some(row) = existing {
216 let mut active = row.into_active_model();
217 active.date_of_birth = Set(Some(date_of_birth));
218 active.updated_at = Set(now);
219 active.update(db.write()).await?;
220 Ok(true)
221 } else {
222 let row = user_profiles::ActiveModel {
223 user_id: Set(uid),
224 real_name: Set(None),
225 bio: Set(None),
226 pronouns: Set(None),
227 website: Set(None),
228 location: Set(None),
229 company: Set(None),
230 pronouns_privacy: Set(None),
231 date_of_birth: Set(Some(date_of_birth)),
232 banner_url: Set(None),
233 banner_hash: Set(None),
234 banner_color: Set(None),
235 accent_color: Set(None),
236 timezone_privacy_flags: Set(None),
237 custom_status_emoji: Set(None),
238 custom_status_text: Set(None),
239 custom_status_expires_at: Set(None),
240 show_email: Set(false),
241 show_location: Set(false),
242 show_real_name: Set(false),
243 show_activity: Set(false),
244 privacy_agreed_at: Set(None),
245 created_at: Set(now),
246 updated_at: Set(now),
247 };
248 row.insert(db.write()).await?;
249 Ok(false)
250 }
251213 }
252214}
Modified lib/auth/src/share/guard.rs lib/auth/src/share/guard.rs +19 −1 View lib/auth/src/share/guard.rs
@@ -1,7 +1,9 @@
11use axum::extract::{Request, State};
2use axum::http::StatusCode;
23use axum::middleware::Next;
3use axum::response::Response;
4use axum::response::{IntoResponse, Response};
45use session::Session;
6use session::SessionUser;
57use session::share::guard::session_guard;
68
79use crate::services::AuthService;
@@ -14,3 +16,19 @@
1416) -> Response {
1517 session_guard::<AuthService>(State(service), session, request, next).await
1618}
19
20pub async fn admin_guard(
21 State(service): State<AuthService>,
22 session: Session,
23 request: Request,
24 next: Next,
25) -> Response {
26 match session.uid() {
27 Some(uid) if service.config().apihost.is_platform_admin(&uid.to_string()) => {}
28 Some(_) => {
29 return (StatusCode::FORBIDDEN, "platform admin required").into_response();
30 }
31 None => {}
32 }
33 session_guard::<AuthService>(State(service), session, request, next).await
34}
Modified lib/auth/src/share/types.rs lib/auth/src/share/types.rs +1 −14 View lib/auth/src/share/types.rs
@@ -1,5 +1,4 @@
11use serde::{Deserialize, Serialize};
2use utoipa::IntoParams;
32use uuid::Uuid;
43
54#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
@@ -22,19 +21,7 @@
2221 pub avatar_url: String,
2322}
2423
25#[derive(Debug, Clone, Deserialize, IntoParams)]
26pub struct ListQuery {
27 pub limit: Option<u64>,
28 pub offset: Option<u64>,
29}
30
31impl ListQuery {
32 pub fn page(&self) -> (u64, u64) {
33 let limit = self.limit.unwrap_or(50).clamp(1, 100);
34 let offset = self.offset.unwrap_or(0);
35 (limit, offset)
36 }
37}
24pub type ListQuery = export::pagination::PageQuery;
3825
3926#[derive(Debug, Clone, Serialize, Deserialize, utoipa::ToSchema)]
4027pub struct AgreementDoc {
Modified lib/auth/src/errors.rs lib/auth/src/errors.rs +7 −7 View lib/auth/src/errors.rs
@@ -150,16 +150,16 @@
150150impl From<AuthError> for ApiError {
151151 fn from(error: AuthError) -> Self {
152152 match error {
153 AuthError::BadRequest(msg) => Self::BadRequest(AppResponseData::err(400, msg)),
154 AuthError::PowFailed => Self::BadRequest(Self::payload(400, "proof of work failed")),
153 AuthError::BadRequest(msg) => Self::BadRequest(AppResponseData::err(400, msg).with_error_code("bad_request")),
154 AuthError::PowFailed => Self::BadRequest(Self::payload(400, "proof of work failed").with_error_code("pow_failed")),
155155 AuthError::InvalidCredentials => {
156156 Self::InvalidCredentials(Self::payload(400, "invalid credentials"))
157157 }
158 AuthError::Unauthorized => Self::Unauthorized(Self::payload(401, "unauthorized")),
159 AuthError::Forbidden => Self::Forbidden(Self::payload(403, "forbidden")),
160 AuthError::NotFound => Self::NotFound(Self::payload(404, "not found")),
161 AuthError::Conflict(msg) => Self::Conflict(AppResponseData::err(409, msg)),
162 AuthError::RateLimited => Self::RateLimited(Self::payload(429, "rate limit exceeded")),
158 AuthError::Unauthorized => Self::Unauthorized(Self::payload(401, "unauthorized").with_error_code("unauthorized")),
159 AuthError::Forbidden => Self::Forbidden(Self::payload(403, "forbidden").with_error_code("forbidden")),
160 AuthError::NotFound => Self::NotFound(Self::payload(404, "not found").with_error_code("not_found")),
161 AuthError::Conflict(msg) => Self::Conflict(AppResponseData::err(409, msg).with_error_code("conflict")),
162 AuthError::RateLimited => Self::RateLimited(Self::payload(429, "rate limit exceeded").with_error_code("rate_limited")),
163163 AuthError::Internal(message) => {
164164 tracing::warn!(error = %message, "auth handler failed");
165165 Self::Internal(Self::payload(500, "internal server error"))
Modified lib/auth/src/openapi.rs lib/auth/src/openapi.rs +0 −3 View lib/auth/src/openapi.rs
@@ -14,7 +14,6 @@
1414use crate::handlers::pow_challenge::PowChallengeResp;
1515use crate::handlers::request_deletion::RequestDeletionCmd;
1616use crate::handlers::unread_notifications_count::UnreadCountResp;
17use crate::handlers::update_birthday::UpdateBirthdayCmd;
1817use crate::services::access_keys::{AccessKeyDto, CreateAccessKeyCmd, CreateAccessKeyResp};
1918use crate::services::agreements::AcceptAgreementResp;
2019use crate::services::deletion::DeletionDto;
@@ -84,7 +83,6 @@
8483 handlers::totp_setup::totp_setup,
8584 handlers::totp_status::totp_status,
8685 handlers::unread_notifications_count::unread_notifications_count,
87 handlers::update_birthday::update_birthday,
8886 handlers::update_preferences::update_preferences,
8987 handlers::update_profile::update_profile,
9088 handlers::verify_email::verify_email,
@@ -127,7 +125,6 @@
127125 UserMeDto,
128126 ProfileDto,
129127 UpdateProfileCmd,
130 UpdateBirthdayCmd,
131128 UserPreferencesDto,
132129 UpdatePreferencesCmd,
133130 AcceptAgreementCmd,
Modified lib/auth/src/routes.rs lib/auth/src/routes.rs +28 −18 View lib/auth/src/routes.rs
@@ -44,7 +44,6 @@
4444use crate::handlers::totp_setup::totp_setup;
4545use crate::handlers::totp_status::totp_status;
4646use crate::handlers::unread_notifications_count::unread_notifications_count;
47use crate::handlers::update_birthday::update_birthday;
4847use crate::handlers::update_preferences::update_preferences;
4948use crate::handlers::update_profile::update_profile;
5049use crate::handlers::verify_email::verify_email;
@@ -60,15 +59,12 @@
6059 .route("/auth/login/2fa", post(login_2fa))
6160 .route("/auth/logout", post(logout))
6261 .route("/auth/email/verify", post(verify_email))
63 .route("/auth/email/verify/page", get(verify_email_page))
6462 .route("/auth/email/resend", post(resend_code))
6563 .route("/auth/email/reclaim", post(reclaim))
6664 .route("/auth/password/forgot", post(forgot_password))
6765 .route("/auth/password/reset", post(reset_password))
68 .route("/auth/password/reset/page", get(reset_password_page))
6966 .route("/auth/agreements", get(list_agreements))
70 .route("/auth/agreements/{key}/page", get(agreement_page))
71 .route("/users/{uid}", get(get_user));
67 .route("/users/{user_id}", get(get_user));
7268
7369 let authed = Router::new()
7470 .route("/auth/email/change", post(request_email_change))
@@ -93,32 +89,46 @@
9389 .route("/auth/access-keys/{key_id}", delete(delete_access_key))
9490 .route("/auth/ssh-keys", get(list_ssh_keys).post(add_ssh_key))
9591 .route("/auth/ssh-keys/{key_id}", delete(delete_ssh_key))
96 .route("/auth/me", get(me).patch(update_profile))
97 .route("/auth/me/birthday", put(update_birthday))
92 .route("/me", get(me).patch(update_profile))
9893 .route(
99 "/auth/preferences",
94 "/me/preferences",
10095 get(get_preferences).patch(update_preferences),
10196 )
102 .route("/auth/agreements/{key}", put(accept_agreement))
103 .route("/notifications", get(list_notifications))
97 .route("/me/agreements/{key}", put(accept_agreement))
98 .route("/me/notifications", get(list_notifications))
10499 .route(
105 "/notifications/unread-count",
100 "/me/notifications/unread-count",
106101 get(unread_notifications_count),
107102 )
108 .route("/notifications/read-all", put(mark_all_notifications_read))
109103 .route(
110 "/notifications/{notification_id}",
104 "/me/notifications/read-state",
105 put(mark_all_notifications_read),
106 )
107 .route(
108 "/me/notifications/{notification_id}",
111109 delete(delete_notification),
112110 )
113111 .route(
114 "/notifications/{notification_id}/read",
112 "/me/notifications/{notification_id}/read",
115113 put(mark_notification_read),
116114 )
117 .route("/reports", post(create_report))
118 .route("/deletion", get(deletion_status).post(request_deletion))
119 .route("/deletion/cancel", post(cancel_deletion))
120 .route("/suspension", get(active_suspension))
115 .route("/me/reports", post(create_report))
116 .route(
117 "/me/deletion",
118 get(deletion_status)
119 .post(request_deletion)
120 .delete(cancel_deletion),
121 )
122 .route("/me/suspension", get(active_suspension))
121123 .route_layer(middleware::from_fn_with_state(service, auth_guard));
122124
123125 public.merge(authed)
124126}
127
128pub fn pages_router(service: AuthService) -> Router {
129 Router::new()
130 .route("/email/verify", get(verify_email_page))
131 .route("/password/reset", get(reset_password_page))
132 .route("/agreements/{key}", get(agreement_page))
133 .with_state(service)
134}
Modified lib/chat/src/errors.rs lib/chat/src/errors.rs +6 −6 View lib/chat/src/errors.rs
@@ -161,12 +161,12 @@
161161impl From<ChatError> for ApiError {
162162 fn from(error: ChatError) -> Self {
163163 match error {
164 ChatError::BadRequest(msg) => Self::BadRequest(AppResponseData::err(400, msg)),
165 ChatError::Unauthorized => Self::Unauthorized(Self::payload(401, "unauthorized")),
166 ChatError::Forbidden => Self::Forbidden(Self::payload(403, "forbidden")),
167 ChatError::NotFound => Self::NotFound(Self::payload(404, "not found")),
168 ChatError::Conflict(msg) => Self::Conflict(AppResponseData::err(409, msg)),
169 ChatError::RateLimited(msg) => Self::TooManyRequests(AppResponseData::err(429, msg)),
164 ChatError::BadRequest(msg) => Self::BadRequest(AppResponseData::err(400, msg).with_error_code("bad_request")),
165 ChatError::Unauthorized => Self::Unauthorized(Self::payload(401, "unauthorized").with_error_code("unauthorized")),
166 ChatError::Forbidden => Self::Forbidden(Self::payload(403, "forbidden").with_error_code("forbidden")),
167 ChatError::NotFound => Self::NotFound(Self::payload(404, "not found").with_error_code("not_found")),
168 ChatError::Conflict(msg) => Self::Conflict(AppResponseData::err(409, msg).with_error_code("conflict")),
169 ChatError::RateLimited(msg) => Self::TooManyRequests(AppResponseData::err(429, msg).with_error_code("rate_limited")),
170170 ChatError::Internal(message) => {
171171 tracing::warn!(error = %message, "chat handler failed");
172172 Self::Internal(Self::payload(500, "internal server error"))
Modified lib/config/src/apihost.rs lib/config/src/apihost.rs +5 −0 View lib/config/src/apihost.rs
@@ -8,9 +8,14 @@
88 pub enabled: Option<bool>,
99 pub bind_addr: Option<String>,
1010 pub port: Option<u16>,
11 pub admin_uids: Vec<String>,
1112}
1213
1314impl ApihostConfig {
15 pub fn is_platform_admin(&self, uid: &str) -> bool {
16 self.admin_uids.iter().any(|id| id == uid)
17 }
18
1419 pub fn enabled(&self) -> bool {
1520 self.enabled.unwrap_or(true)
1621 }
Modified lib/export/src/lib.rs lib/export/src/lib.rs +2 −0 View lib/export/src/lib.rs
@@ -1,5 +1,7 @@
11pub mod database;
22pub mod error;
3pub mod middleware;
4pub mod pagination;
35pub mod redis;
46pub mod rest;
57pub mod storage;
Added lib/export/src/middleware.rs lib/export/src/middleware.rs +131 −0 View lib/export/src/middleware.rs
@@ -0,0 +1,131 @@
1use std::future::Future;
2use std::pin::Pin;
3use std::task::{Context, Poll};
4
5use axum::body::Body;
6use axum::http::{HeaderName, HeaderValue, Request, Response, header};
7use bytes::Bytes;
8use http_body_util::BodyExt;
9use serde_json::Value;
10use tower::{Layer, Service};
11use uuid::Uuid;
12
13pub const REQUEST_ID_HEADER: HeaderName = HeaderName::from_static("x-request-id");
14pub const LOG_ID_HEADER: HeaderName = HeaderName::from_static("x-log-id");
15
16const MAX_INBOUND_ID_LEN: usize = 128;
17
18#[derive(Debug, Clone)]
19pub struct RequestId(pub String);
20
21#[derive(Debug, Clone)]
22pub struct LogId(pub String);
23
24#[derive(Debug, Clone, Copy, Default)]
25pub struct AppTraceLayer;
26
27impl AppTraceLayer {
28 pub fn new() -> Self {
29 Self
30 }
31}
32
33impl<S> Layer<S> for AppTraceLayer {
34 type Service = AppTraceService<S>;
35
36 fn layer(&self, inner: S) -> Self::Service {
37 AppTraceService { inner }
38 }
39}
40
41#[derive(Debug, Clone)]
42pub struct AppTraceService<S> {
43 inner: S,
44}
45
46impl<S> Service<Request<Body>> for AppTraceService<S>
47where
48 S: Service<Request<Body>, Response = Response<Body>> + Send + 'static,
49 S::Future: Send + 'static,
50{
51 type Response = S::Response;
52 type Error = S::Error;
53 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
54
55 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
56 self.inner.poll_ready(cx)
57 }
58
59 fn call(&mut self, mut req: Request<Body>) -> Self::Future {
60 let request_id =
61 inbound_id(req.headers().get(&REQUEST_ID_HEADER)).unwrap_or_else(new_id);
62 let log_id = new_id();
63
64 req.extensions_mut().insert(RequestId(request_id.clone()));
65 req.extensions_mut().insert(LogId(log_id.clone()));
66
67 let fut = self.inner.call(req);
68 Box::pin(async move {
69 let mut res = fut.await?;
70 if let Ok(value) = HeaderValue::try_from(request_id.as_str()) {
71 res.headers_mut().insert(REQUEST_ID_HEADER, value);
72 }
73 if let Ok(value) = HeaderValue::try_from(log_id.as_str()) {
74 res.headers_mut().insert(LOG_ID_HEADER, value);
75 }
76 inject_ids(&mut res, &request_id, &log_id).await;
77 Ok(res)
78 })
79 }
80}
81
82fn new_id() -> String {
83 Uuid::now_v7().to_string()
84}
85
86fn inbound_id(value: Option<&HeaderValue>) -> Option<String> {
87 let value = value?.to_str().ok()?.trim();
88 if value.is_empty() || value.len() > MAX_INBOUND_ID_LEN {
89 return None;
90 }
91 Some(value.to_string())
92}
93
94async fn inject_ids(res: &mut Response<Body>, request_id: &str, log_id: &str) {
95 let is_json = res
96 .headers()
97 .get(header::CONTENT_TYPE)
98 .and_then(|value| value.to_str().ok())
99 .is_some_and(|value| value.starts_with("application/json"));
100 if !is_json {
101 return;
102 }
103
104 let (mut parts, body) = std::mem::take(res).into_parts();
105 let bytes = match body.collect().await {
106 Ok(collected) => collected.to_bytes(),
107 Err(_) => {
108 *res = Response::from_parts(parts, Body::empty());
109 return;
110 }
111 };
112
113 let output = match serde_json::from_slice::<Value>(&bytes) {
114 Ok(Value::Object(mut map))
115 if map.contains_key("request_id") && map.contains_key("log_id") =>
116 {
117 map.insert("request_id".to_string(), Value::String(request_id.to_string()));
118 map.insert("log_id".to_string(), Value::String(log_id.to_string()));
119 match serde_json::to_vec(&map) {
120 Ok(rewritten) => Bytes::from(rewritten),
121 Err(_) => bytes,
122 }
123 }
124 _ => bytes,
125 };
126
127 if let Ok(length) = HeaderValue::try_from(output.len().to_string()) {
128 parts.headers.insert(header::CONTENT_LENGTH, length);
129 }
130 *res = Response::from_parts(parts, Body::from(output));
131}
Added lib/export/src/pagination.rs lib/export/src/pagination.rs +33 −0 View lib/export/src/pagination.rs
@@ -0,0 +1,33 @@
1use serde::{Deserialize, Serialize};
2use utoipa::{IntoParams, ToSchema};
3
4pub const DEFAULT_PAGE_LIMIT: u64 = 50;
5pub const MAX_PAGE_LIMIT: u64 = 100;
6
7#[derive(Debug, Clone, Copy, Deserialize, IntoParams, ToSchema)]
8pub struct PageQuery {
9 pub limit: Option<u64>,
10 pub offset: Option<u64>,
11}
12
13impl PageQuery {
14 pub fn limit(&self) -> u64 {
15 self.limit.unwrap_or(DEFAULT_PAGE_LIMIT).clamp(1, MAX_PAGE_LIMIT)
16 }
17
18 pub fn offset(&self) -> u64 {
19 self.offset.unwrap_or(0)
20 }
21
22 pub fn page(&self) -> (u64, u64) {
23 (self.limit(), self.offset())
24 }
25}
26
27#[derive(Debug, Clone, Serialize, ToSchema)]
28pub struct Page<T> {
29 pub items: Vec<T>,
30 pub total: i64,
31 pub limit: u64,
32 pub offset: u64,
33}
Modified lib/export/src/rest.rs lib/export/src/rest.rs +8 −31 View lib/export/src/rest.rs
@@ -17,9 +17,8 @@
1717 pub message: String,
1818 pub data: Option<T>,
1919 pub error: Option<String>,
20 pub duration: String,
20 pub error_code: Option<String>,
2121 pub request_id: String,
22 pub at: String,
2322 pub log_id: String,
2423}
2524
@@ -30,6 +29,7 @@
3029 pub data: Option<T>,
3130}
3231
32
3333impl<T> AppResponseData<T>
3434where
3535 T: Serialize,
@@ -40,9 +40,8 @@
4040 message: "success".to_string(),
4141 data: Some(data),
4242 error: None,
43 duration: String::new(),
43 error_code: None,
4444 request_id: String::new(),
45 at: String::new(),
4645 log_id: String::new(),
4746 }
4847 }
@@ -53,9 +52,8 @@
5352 message: message.into(),
5453 data: None,
5554 error: None,
56 duration: String::new(),
55 error_code: None,
5756 request_id: String::new(),
58 at: String::new(),
5957 log_id: String::new(),
6058 }
6159 }
@@ -70,9 +68,8 @@
7068 message: message.into(),
7169 data: None,
7270 error: Some(detail.into()),
73 duration: String::new(),
71 error_code: None,
7472 request_id: String::new(),
75 at: String::new(),
7673 log_id: String::new(),
7774 }
7875 }
@@ -92,8 +89,8 @@
9289 self
9390 }
9491
95 pub fn with_duration(mut self, duration: impl Into<String>) -> Self {
96 self.duration = duration.into();
92 pub fn with_error_code(mut self, code: impl Into<String>) -> Self {
93 self.error_code = Some(code.into());
9794 self
9895 }
9996
@@ -102,11 +99,6 @@
10299 self
103100 }
104101
105 pub fn with_at(mut self, at: impl Into<String>) -> Self {
106 self.at = at.into();
107 self
108 }
109
110102 pub fn with_log_id(mut self, log_id: impl Into<String>) -> Self {
111103 self.log_id = log_id.into();
112104 self
@@ -120,23 +112,8 @@
120112 message: "success".to_string(),
121113 data: None,
122114 error: None,
123 duration: String::new(),
124 request_id: String::new(),
125 at: String::new(),
126 log_id: String::new(),
127 }
128 }
129
130 pub fn err_plain(code: i32, message: impl Into<String>) -> Self {
131 let msg = message.into();
132 AppResponseData {
133 code,
134 message: msg.clone(),
135 data: None,
136 error: Some(msg),
137 duration: String::new(),
115 error_code: None,
138116 request_id: String::new(),
139 at: String::new(),
140117 log_id: String::new(),
141118 }
142119 }
Modified lib/export/Cargo.toml lib/export/Cargo.toml +7 −1 View lib/export/Cargo.toml
@@ -13,4 +13,10 @@
1313axum = { workspace = true }
1414serde = { workspace = true, features = ["derive"] }
1515tracing = { workspace = true }
16utoipa = { workspace = true }
No newline at end of file
16utoipa = { workspace = true }
17rand.workspace = true
18tower = { workspace = true }
19serde_json = { workspace = true }
20uuid = { workspace = true }
21bytes = { workspace = true }
22http-body-util = { workspace = true }
No newline at end of file
Modified lib/git/src/handler/branches/set_default.rs lib/git/src/handler/branches/set_default.rs +1 −1 View lib/git/src/handler/branches/set_default.rs
@@ -14,7 +14,7 @@
1414}
1515
1616#[utoipa::path(
17 post,
17 put,
1818 path = "/repos/{space_name}/{repo_name}/branches/{name}/default",
1919 tag = "branches",
2020 params(BranchPath),
Modified lib/git/src/handler/hooks/execute_script.rs lib/git/src/handler/hooks/execute_script.rs +2 −2 View lib/git/src/handler/hooks/execute_script.rs
@@ -6,13 +6,13 @@
66use crate::errors::{ApiError, GitApiError};
77use crate::handler::require_writer;
88use crate::service::GitService;
9use crate::service::hooks::ScriptOutputDto;
9use crate::service::hooks::{HookEventKind, ScriptOutputDto};
1010
1111#[derive(Debug, Clone, serde::Deserialize, utoipa::IntoParams)]
1212pub struct HookScriptPath {
1313 pub space_name: String,
1414 pub repo_name: String,
15 pub script: String,
15 pub script: HookEventKind,
1616}
1717
1818#[derive(Debug, Clone, serde::Deserialize, utoipa::ToSchema)]
Modified lib/git/src/service/hooks.rs lib/git/src/service/hooks.rs +15 −10 View lib/git/src/service/hooks.rs
@@ -4,7 +4,7 @@
44use crate::service::gitcmd::run_exec_cmd;
55use crate::share::errors::GitError;
66
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, utoipa::ToSchema)]
88#[serde(rename_all = "kebab-case")]
99pub enum HookEventKind {
1010 PreReceive,
@@ -67,22 +67,27 @@
6767 &self,
6868 space_name: String,
6969 repo_name: String,
70 script: String,
70 script: HookEventKind,
7171 args: Vec<String>,
7272 ) -> Result<ScriptOutputDto, GitError> {
73 if script.is_empty()
74 || script.contains('/')
75 || script.contains('\\')
76 || script.contains("..")
77 || script.starts_with('-')
78 {
73 if !self.hook_supported(script) {
7974 return Err(GitError::InvalidArgument(format!(
80 "invalid script name `{script}`"
75 "unsupported hook script `{script:?}`"
8176 )));
8277 }
78 if args.iter().any(|a| a.len() > 1024) || args.len() > 16 {
79 return Err(GitError::InvalidArgument(
80 "invalid hook arguments".to_string(),
81 ));
82 }
83 let filename = match script {
84 HookEventKind::PreReceive => "pre-receive",
85 HookEventKind::PostReceive => "post-receive",
86 _ => unreachable!("rejected by hook_supported"),
87 };
8388 let ctx = self.context(space_name, repo_name).await?;
8489 let dir = ctx.repo_dir().join("hooks");
85 let prog = dir.join(&script);
90 let prog = dir.join(filename);
8691 let timeout = self.state.config.git.git_command_timeout_secs;
8792 let out = run_exec_cmd(
8893 "hooks::execute_script",
Modified lib/git/src/openapi.rs lib/git/src/openapi.rs +1 −21 View lib/git/src/openapi.rs
@@ -33,16 +33,12 @@
3333 handler::commits::rev_parse::rev_parse,
3434 handler::commits::set_commit_status::set_commit_status,
3535 handler::commits::stats::commit_stats,
36 handler::commits::sync_commit::sync_commit,
3736 handler::commits::tree_entries::get_tree_entries,
3837 handler::conflicts::check::conflict_check,
3938 handler::conflicts::check_threeway::conflict_check_threeway,
4039 handler::conflicts::conflicted_files::conflicted_files,
4140 handler::conflicts::detail::conflict_detail,
4241 handler::content::caller_signature::caller_signature,
43 handler::content::commit_object::commit_object,
44 handler::content::write_blob::write_blob,
45 handler::content::write_tree_from_files::write_tree_from_files,
4642 handler::diffs::files_changed_between::files_changed_between,
4743 handler::diffs::for_commit::diff_for_commit,
4844 handler::diffs::range_diff::range_diff,
@@ -54,7 +50,6 @@
5450 handler::licenses::get_license,
5551 handler::licenses::list_licenses,
5652 handler::licenses::upsert_license,
57 handler::lfs::cleanup_unreferenced::cleanup_unreferenced,
5853 handler::lfs::list_locks::list_locks,
5954 handler::lfs::lock::lock,
6055 handler::lfs::unlock::unlock,
@@ -64,27 +59,11 @@
6459 handler::logs::graph::log_graph,
6560 handler::logs::last_commit::log_last_commit,
6661 handler::logs::list::log_list,
67 handler::maintenance::fsck::fsck,
68 handler::maintenance::prune::prune,
69 handler::packs::ingest::ingest,
70 handler::packs::repack::repack,
71 handler::packs::stats::stats,
72 handler::patches::apply::patch_apply,
7362 handler::patches::generate::patch_generate,
7463 handler::patches::parse::patch_parse,
7564 handler::path_log::path_log,
76 handler::pipelines::cancel::cancel,
7765 handler::pipelines::schedule::schedule,
7866 handler::pipelines::trigger::trigger,
79 handler::refs::branches::refs_branches,
80 handler::refs::delete::refs_delete,
81 handler::refs::filtered::refs_list_filtered,
82 handler::refs::get::refs_get,
83 handler::refs::head::refs_head,
84 handler::refs::is_object_in_ref::refs_is_object_in_ref,
85 handler::refs::list::refs_list,
86 handler::refs::resolve::refs_resolve,
87 handler::refs::update::refs_update,
8867 handler::remotes::add::add,
8968 handler::remotes::clone_import::clone_import,
9069 handler::remotes::fetch::fetch,
@@ -152,6 +131,7 @@
152131 ),
153132 components(schemas(
154133 export::rest::NoData,
134 crate::service::hooks::HookEventKind,
155135 models::common::watch_mode::WatchMode,
156136 models::git::git_commit_status::CommitStatusState,
157137 models::git::git_tags::TagType,
Modified lib/git/src/routes.rs lib/git/src/routes.rs +67 −44 View lib/git/src/routes.rs
@@ -123,6 +123,13 @@
123123 .layer(Extension(schema))
124124}
125125
126pub fn internal_router(service: GitService) -> Router<GitService> {
127 let schema = crate::graphql::build_schema(service.state.clone());
128 Router::new()
129 .nest("/repos", repos_internal_router())
130 .layer(Extension(schema))
131}
132
126133fn repos_router() -> Router<GitService> {
127134 Router::new()
128135 .route("/{space_name}", post(repo_create).get(repo_list))
@@ -141,7 +148,7 @@
141148 )
142149 .route(
143150 "/{space_name}/{repo_name}/branches/{name}/default",
144 post(branch_set_default),
151 put(branch_set_default),
145152 )
146153 .route(
147154 "/{space_name}/{repo_name}/tags",
@@ -155,25 +162,6 @@
155162 "/{space_name}/{repo_name}/tags/{name}/verify",
156163 get(tag_verify),
157164 )
158 .route("/{space_name}/{repo_name}/refs", get(refs_list))
159 .route("/{space_name}/{repo_name}/refs/head", get(refs_head))
160 .route(
161 "/{space_name}/{repo_name}/refs/branches",
162 get(refs_branches),
163 )
164 .route("/{space_name}/{repo_name}/refs/filtered", get(filtered))
165 .route(
166 "/{space_name}/{repo_name}/refs/contains",
167 get(is_object_in_ref),
168 )
169 .route(
170 "/{space_name}/{repo_name}/refs/{name}",
171 get(refs_get).patch(refs_update).delete(refs_delete),
172 )
173 .route(
174 "/{space_name}/{repo_name}/refs/{name}/resolve",
175 get(refs_resolve),
176 )
177165 .route("/{space_name}/{repo_name}/commits", get(list_commits))
178166 .route(
179167 "/{space_name}/{repo_name}/commits/{revision}",
@@ -188,10 +176,6 @@
188176 "/{space_name}/{repo_name}/commits/ancestor",
189177 get(is_ancestor),
190178 )
191 .route(
192 "/{space_name}/{repo_name}/commits/{revision}/sync",
193 post(sync_commit),
194 )
195179 .route("/{space_name}/{repo_name}/rev/{revision}", get(rev_parse))
196180 .route(
197181 "/{space_name}/{repo_name}/commit-statuses",
@@ -267,7 +251,6 @@
267251 "/{space_name}/{repo_name}/patch/generate",
268252 get(patch_generate),
269253 )
270 .route("/{space_name}/{repo_name}/patch/apply", post(patch_apply))
271254 .route("/_/patch/parse", post(patch_parse))
272255 .route(
273256 "/{space_name}/{repo_name}/conflicts/check",
@@ -311,10 +294,6 @@
311294 delete(unlock_by_id),
312295 )
313296 .route(
314 "/{space_name}/{repo_name}/lfs/cleanup",
315 post(cleanup_unreferenced),
316 )
317 .route(
318297 "/{space_name}/{repo_name}/hooks/{script}",
319298 post(execute_script),
320299 )
@@ -323,22 +302,7 @@
323302 "/{space_name}/{repo_name}/pipelines/schedule",
324303 post(schedule),
325304 )
326 .route("/_/pipelines/cancel", post(cancel))
327 .route("/{space_name}/{repo_name}/packs/stats", get(pack_stats))
328 .route("/{space_name}/{repo_name}/packs/ingest", post(pack_ingest))
329 .route("/{space_name}/{repo_name}/packs/repack", post(pack_repack))
330 .route("/{space_name}/{repo_name}/maintenance/prune", post(prune))
331 .route("/{space_name}/{repo_name}/maintenance/fsck", post(fsck))
332305 .route("/_/contents/signature", post(caller_signature))
333 .route("/{space_name}/{repo_name}/contents/blobs", post(write_blob))
334 .route(
335 "/{space_name}/{repo_name}/contents/trees",
336 post(write_tree_from_files),
337 )
338 .route(
339 "/{space_name}/{repo_name}/contents/commits",
340 post(commit_object),
341 )
342306 .route(
343307 "/{space_name}/{repo_name}/analysis/languages",
344308 get(languages_read).post(languages),
@@ -400,6 +364,65 @@
400364 )
401365}
402366
367fn repos_internal_router() -> Router<GitService> {
368 Router::new()
369 .route("/{space_name}/{repo_name}/refs", get(refs_list))
370 .route("/{space_name}/{repo_name}/refs/head", get(refs_head))
371 .route(
372 "/{space_name}/{repo_name}/refs/branches",
373 get(refs_branches),
374 )
375 .route("/{space_name}/{repo_name}/refs/filtered", get(filtered))
376 .route(
377 "/{space_name}/{repo_name}/refs/contains",
378 get(is_object_in_ref),
379 )
380 .route(
381 "/{space_name}/{repo_name}/refs/{name}",
382 get(refs_get).patch(refs_update).delete(refs_delete),
383 )
384 .route(
385 "/{space_name}/{repo_name}/refs/{name}/resolve",
386 get(refs_resolve),
387 )
388 .route(
389 "/{space_name}/{repo_name}/commits/{revision}/sync",
390 post(sync_commit),
391 )
392 .route(
393 "/{space_name}/{repo_name}/patch/apply",
394 post(patch_apply),
395 )
396 .route("/_/pipelines/cancel", post(cancel))
397 .route("/{space_name}/{repo_name}/packs/stats", get(pack_stats))
398 .route("/{space_name}/{repo_name}/packs/ingest", post(pack_ingest))
399 .route("/{space_name}/{repo_name}/packs/repack", post(pack_repack))
400 .route(
401 "/{space_name}/{repo_name}/maintenance/prune",
402 post(prune),
403 )
404 .route(
405 "/{space_name}/{repo_name}/maintenance/fsck",
406 post(fsck),
407 )
408 .route(
409 "/{space_name}/{repo_name}/lfs/cleanup",
410 post(cleanup_unreferenced),
411 )
412 .route(
413 "/{space_name}/{repo_name}/contents/blobs",
414 post(write_blob),
415 )
416 .route(
417 "/{space_name}/{repo_name}/contents/trees",
418 post(write_tree_from_files),
419 )
420 .route(
421 "/{space_name}/{repo_name}/contents/commits",
422 post(commit_object),
423 )
424}
425
403426fn topics_router() -> Router<GitService> {
404427 Router::new()
405428 .route("/", post(create_topic).get(list_topics))
Modified lib/issues/src/handlers/boards/create_board.rs lib/issues/src/handlers/boards/create_board.rs +1 −1 View lib/issues/src/handlers/boards/create_board.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 post,
13 path = "/issues/{space_name}",
13 path = "/issues/{space_name}/boards",
1414 tag = "issues",
1515 params(SpaceNamePath),
1616 request_body = CreateBoardCmd,
Modified lib/issues/src/handlers/boards/delete_board.rs lib/issues/src/handlers/boards/delete_board.rs +1 −1 View lib/issues/src/handlers/boards/delete_board.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 delete,
12 path = "/issues/{space_name}/{board_id}",
12 path = "/issues/{space_name}/boards/{board_id}",
1313 tag = "issues",
1414 params(BoardPath),
1515 responses(
Modified lib/issues/src/handlers/boards/get_board.rs lib/issues/src/handlers/boards/get_board.rs +1 −1 View lib/issues/src/handlers/boards/get_board.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 get,
12 path = "/issues/{space_name}/{board_id}",
12 path = "/issues/{space_name}/boards/{board_id}",
1313 tag = "issues",
1414 params(BoardPath),
1515 responses(
Modified lib/issues/src/handlers/boards/list_boards.rs lib/issues/src/handlers/boards/list_boards.rs +1 −1 View lib/issues/src/handlers/boards/list_boards.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 get,
13 path = "/issues/{space_name}",
13 path = "/issues/{space_name}/boards",
1414 tag = "issues",
1515 params(SpaceNamePath, ListBoardQuery),
1616 responses(
Modified lib/issues/src/handlers/boards/update_board.rs lib/issues/src/handlers/boards/update_board.rs +1 −1 View lib/issues/src/handlers/boards/update_board.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 patch,
12 path = "/issues/{space_name}/{board_id}",
12 path = "/issues/{space_name}/boards/{board_id}",
1313 tag = "issues",
1414 params(BoardPath),
1515 request_body = UpdateBoardCmd,
Modified lib/issues/src/handlers/comments/create_comment.rs lib/issues/src/handlers/comments/create_comment.rs +1 −1 View lib/issues/src/handlers/comments/create_comment.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 post,
13 path = "/issues/{space_name}/{board_id}/{issue_id}/comments",
13 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/comments",
1414 tag = "issues",
1515 params(IssuePath),
1616 request_body = CreateCommentCmd,
Modified lib/issues/src/handlers/comments/delete_comment.rs lib/issues/src/handlers/comments/delete_comment.rs +1 −1 View lib/issues/src/handlers/comments/delete_comment.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 delete,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/comments/{comment_id}",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/comments/{comment_id}",
1313 tag = "issues",
1414 params(CommentPath),
1515 responses(
Modified lib/issues/src/handlers/comments/get_comment.rs lib/issues/src/handlers/comments/get_comment.rs +1 −1 View lib/issues/src/handlers/comments/get_comment.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 get,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/comments/{comment_id}",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/comments/{comment_id}",
1313 tag = "issues",
1414 params(CommentPath),
1515 responses(
Modified lib/issues/src/handlers/comments/list_comments.rs lib/issues/src/handlers/comments/list_comments.rs +1 −1 View lib/issues/src/handlers/comments/list_comments.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 get,
13 path = "/issues/{space_name}/{board_id}/{issue_id}/comments",
13 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/comments",
1414 tag = "issues",
1515 params(IssuePath, ListCommentQuery),
1616 responses(
Modified lib/issues/src/handlers/comments/update_comment.rs lib/issues/src/handlers/comments/update_comment.rs +1 −1 View lib/issues/src/handlers/comments/update_comment.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 patch,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/comments/{comment_id}",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/comments/{comment_id}",
1313 tag = "issues",
1414 params(CommentPath),
1515 request_body = UpdateCommentCmd,
Modified lib/issues/src/handlers/content_history/list_content_history.rs lib/issues/src/handlers/content_history/list_content_history.rs +1 −1 View lib/issues/src/handlers/content_history/list_content_history.rs
@@ -11,7 +11,7 @@
1111
1212#[utoipa::path(
1313 get,
14 path = "/issues/{space_name}/{board_id}/{issue_id}/history",
14 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/history",
1515 tag = "issues",
1616 params(ContentHistoryPath, ListContentHistoryQuery),
1717 responses(
Modified lib/issues/src/handlers/dependencies/create_dependency.rs lib/issues/src/handlers/dependencies/create_dependency.rs +1 −1 View lib/issues/src/handlers/dependencies/create_dependency.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 post,
13 path = "/issues/{space_name}/{board_id}/{issue_id}/dependencies",
13 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/dependencies",
1414 tag = "issues",
1515 params(IssuePath),
1616 request_body = CreateDependencyCmd,
Modified lib/issues/src/handlers/dependencies/delete_dependency.rs lib/issues/src/handlers/dependencies/delete_dependency.rs +1 −1 View lib/issues/src/handlers/dependencies/delete_dependency.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 delete,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/dependencies/{dep_id}",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/dependencies/{dep_id}",
1313 tag = "issues",
1414 params(DependencyPath),
1515 responses(
Modified lib/issues/src/handlers/dependencies/list_dependencies.rs lib/issues/src/handlers/dependencies/list_dependencies.rs +1 −1 View lib/issues/src/handlers/dependencies/list_dependencies.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 get,
13 path = "/issues/{space_name}/{board_id}/{issue_id}/dependencies",
13 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/dependencies",
1414 tag = "issues",
1515 params(IssuePath),
1616 responses(
Modified lib/issues/src/handlers/events/list_events.rs lib/issues/src/handlers/events/list_events.rs +1 −1 View lib/issues/src/handlers/events/list_events.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 get,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/events",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/events",
1313 tag = "issues",
1414 params(EventPath, ListEventQuery),
1515 responses(
Modified lib/issues/src/handlers/issue_users/list_issue_users.rs lib/issues/src/handlers/issue_users/list_issue_users.rs +1 −1 View lib/issues/src/handlers/issue_users/list_issue_users.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 get,
13 path = "/issues/{space_name}/{board_id}/{issue_id}/users",
13 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/users",
1414 tag = "issues",
1515 params(IssuePath, ListIssueUserQuery),
1616 responses(
Modified lib/issues/src/handlers/issue_users/update_issue_user.rs lib/issues/src/handlers/issue_users/update_issue_user.rs +2 −2 View lib/issues/src/handlers/issue_users/update_issue_user.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 patch,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/users/{uid}",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/users/{user_id}",
1313 tag = "issues",
1414 params(IssueUserPath),
1515 request_body = UpdateIssueUserCmd,
@@ -32,7 +32,7 @@
3232 &path.space_name,
3333 path.board_id,
3434 path.issue_id,
35 path.uid,
35 path.user_id,
3636 cmd,
3737 )
3838 .await?;
Modified lib/issues/src/handlers/issues/close_issue.rs lib/issues/src/handlers/issues/close_issue.rs +1 −1 View lib/issues/src/handlers/issues/close_issue.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 post,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/close",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/close",
1313 tag = "issues",
1414 params(IssuePath),
1515 responses(
Modified lib/issues/src/handlers/issues/create_issue.rs lib/issues/src/handlers/issues/create_issue.rs +1 −1 View lib/issues/src/handlers/issues/create_issue.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 post,
13 path = "/issues/{space_name}/{board_id}/issues",
13 path = "/issues/{space_name}/boards/{board_id}/issues",
1414 tag = "issues",
1515 params(BoardPath),
1616 request_body = CreateIssueCmd,
Modified lib/issues/src/handlers/issues/delete_issue.rs lib/issues/src/handlers/issues/delete_issue.rs +1 −1 View lib/issues/src/handlers/issues/delete_issue.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 delete,
12 path = "/issues/{space_name}/{board_id}/{issue_id}",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}",
1313 tag = "issues",
1414 params(IssuePath),
1515 responses(
Modified lib/issues/src/handlers/issues/get_issue.rs lib/issues/src/handlers/issues/get_issue.rs +1 −1 View lib/issues/src/handlers/issues/get_issue.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 get,
12 path = "/issues/{space_name}/{board_id}/{issue_id}",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}",
1313 tag = "issues",
1414 params(IssuePath),
1515 responses(
Modified lib/issues/src/handlers/issues/list_issues.rs lib/issues/src/handlers/issues/list_issues.rs +1 −1 View lib/issues/src/handlers/issues/list_issues.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 get,
13 path = "/issues/{space_name}/{board_id}/issues",
13 path = "/issues/{space_name}/boards/{board_id}/issues",
1414 tag = "issues",
1515 params(BoardPath, ListIssueQuery),
1616 responses(
Modified lib/issues/src/handlers/issues/reopen_issue.rs lib/issues/src/handlers/issues/reopen_issue.rs +1 −1 View lib/issues/src/handlers/issues/reopen_issue.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 post,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/reopen",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/reopen",
1313 tag = "issues",
1414 params(IssuePath),
1515 responses(
Modified lib/issues/src/handlers/issues/update_issue.rs lib/issues/src/handlers/issues/update_issue.rs +1 −1 View lib/issues/src/handlers/issues/update_issue.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 patch,
12 path = "/issues/{space_name}/{board_id}/{issue_id}",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}",
1313 tag = "issues",
1414 params(IssuePath),
1515 request_body = UpdateIssueCmd,
Modified lib/issues/src/handlers/labels/bind_label.rs lib/issues/src/handlers/labels/bind_label.rs +1 −1 View lib/issues/src/handlers/labels/bind_label.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 post,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/labels/{label_id}",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/labels/{label_id}",
1313 tag = "issues",
1414 params(IssueLabelPath),
1515 responses(
Modified lib/issues/src/handlers/labels/create_label.rs lib/issues/src/handlers/labels/create_label.rs +1 −1 View lib/issues/src/handlers/labels/create_label.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 post,
13 path = "/issues/{space_name}/{board_id}/labels",
13 path = "/issues/{space_name}/boards/{board_id}/labels",
1414 tag = "issues",
1515 params(BoardPath),
1616 request_body = CreateLabelCmd,
Modified lib/issues/src/handlers/labels/delete_label.rs lib/issues/src/handlers/labels/delete_label.rs +1 −1 View lib/issues/src/handlers/labels/delete_label.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 delete,
12 path = "/issues/{space_name}/{board_id}/labels/{label_id}",
12 path = "/issues/{space_name}/boards/{board_id}/labels/{label_id}",
1313 tag = "issues",
1414 params(LabelPath),
1515 responses(
Modified lib/issues/src/handlers/labels/get_label.rs lib/issues/src/handlers/labels/get_label.rs +1 −1 View lib/issues/src/handlers/labels/get_label.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 get,
12 path = "/issues/{space_name}/{board_id}/labels/{label_id}",
12 path = "/issues/{space_name}/boards/{board_id}/labels/{label_id}",
1313 tag = "issues",
1414 params(LabelPath),
1515 responses(
Modified lib/issues/src/handlers/labels/list_labels.rs lib/issues/src/handlers/labels/list_labels.rs +1 −1 View lib/issues/src/handlers/labels/list_labels.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 get,
13 path = "/issues/{space_name}/{board_id}/labels",
13 path = "/issues/{space_name}/boards/{board_id}/labels",
1414 tag = "issues",
1515 params(BoardPath, ListLabelQuery),
1616 responses(
Modified lib/issues/src/handlers/labels/list_labels_for_issue.rs lib/issues/src/handlers/labels/list_labels_for_issue.rs +1 −1 View lib/issues/src/handlers/labels/list_labels_for_issue.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 get,
13 path = "/issues/{space_name}/{board_id}/{issue_id}/labels",
13 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/labels",
1414 tag = "issues",
1515 params(IssuePath),
1616 responses(
Modified lib/issues/src/handlers/labels/unbind_label.rs lib/issues/src/handlers/labels/unbind_label.rs +1 −1 View lib/issues/src/handlers/labels/unbind_label.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 delete,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/labels/{label_id}",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/labels/{label_id}",
1313 tag = "issues",
1414 params(IssueLabelPath),
1515 responses(
Modified lib/issues/src/handlers/labels/update_label.rs lib/issues/src/handlers/labels/update_label.rs +1 −1 View lib/issues/src/handlers/labels/update_label.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 patch,
12 path = "/issues/{space_name}/{board_id}/labels/{label_id}",
12 path = "/issues/{space_name}/boards/{board_id}/labels/{label_id}",
1313 tag = "issues",
1414 params(LabelPath),
1515 request_body = UpdateLabelCmd,
Modified lib/issues/src/handlers/members/create_member.rs lib/issues/src/handlers/members/create_member.rs +1 −1 View lib/issues/src/handlers/members/create_member.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 post,
13 path = "/issues/{space_name}/{board_id}/{issue_id}/members",
13 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/members",
1414 tag = "issues",
1515 params(IssuePath),
1616 request_body = CreateIssueMemberCmd,
Modified lib/issues/src/handlers/members/delete_member.rs lib/issues/src/handlers/members/delete_member.rs +1 −1 View lib/issues/src/handlers/members/delete_member.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 delete,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/members/{member_id}",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/members/{member_id}",
1313 tag = "issues",
1414 params(IssueMemberPath),
1515 responses(
Modified lib/issues/src/handlers/members/list_members.rs lib/issues/src/handlers/members/list_members.rs +1 −1 View lib/issues/src/handlers/members/list_members.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 get,
13 path = "/issues/{space_name}/{board_id}/{issue_id}/members",
13 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/members",
1414 tag = "issues",
1515 params(IssuePath, ListIssueMemberQuery),
1616 responses(
Modified lib/issues/src/handlers/milestones/create_milestone.rs lib/issues/src/handlers/milestones/create_milestone.rs +1 −1 View lib/issues/src/handlers/milestones/create_milestone.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 post,
13 path = "/issues/{space_name}/{board_id}/milestones",
13 path = "/issues/{space_name}/boards/{board_id}/milestones",
1414 tag = "issues",
1515 params(BoardPath),
1616 request_body = CreateMilestoneCmd,
Modified lib/issues/src/handlers/milestones/delete_milestone.rs lib/issues/src/handlers/milestones/delete_milestone.rs +1 −1 View lib/issues/src/handlers/milestones/delete_milestone.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 delete,
12 path = "/issues/{space_name}/{board_id}/milestones/{milestone_id}",
12 path = "/issues/{space_name}/boards/{board_id}/milestones/{milestone_id}",
1313 tag = "issues",
1414 params(MilestonePath),
1515 responses(
Modified lib/issues/src/handlers/milestones/get_milestone.rs lib/issues/src/handlers/milestones/get_milestone.rs +1 −1 View lib/issues/src/handlers/milestones/get_milestone.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 get,
12 path = "/issues/{space_name}/{board_id}/milestones/{milestone_id}",
12 path = "/issues/{space_name}/boards/{board_id}/milestones/{milestone_id}",
1313 tag = "issues",
1414 params(MilestonePath),
1515 responses(
Modified lib/issues/src/handlers/milestones/list_milestones.rs lib/issues/src/handlers/milestones/list_milestones.rs +1 −1 View lib/issues/src/handlers/milestones/list_milestones.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 get,
13 path = "/issues/{space_name}/{board_id}/milestones",
13 path = "/issues/{space_name}/boards/{board_id}/milestones",
1414 tag = "issues",
1515 params(BoardPath, ListMilestoneQuery),
1616 responses(
Modified lib/issues/src/handlers/milestones/update_milestone.rs lib/issues/src/handlers/milestones/update_milestone.rs +1 −1 View lib/issues/src/handlers/milestones/update_milestone.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 patch,
12 path = "/issues/{space_name}/{board_id}/milestones/{milestone_id}",
12 path = "/issues/{space_name}/boards/{board_id}/milestones/{milestone_id}",
1313 tag = "issues",
1414 params(MilestonePath),
1515 request_body = UpdateMilestoneCmd,
Modified lib/issues/src/handlers/pins/create_pin.rs lib/issues/src/handlers/pins/create_pin.rs +1 −1 View lib/issues/src/handlers/pins/create_pin.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 post,
13 path = "/issues/{space_name}/{board_id}/pins",
13 path = "/issues/{space_name}/boards/{board_id}/pins",
1414 tag = "issues",
1515 params(BoardPath),
1616 request_body = CreatePinCmd,
Modified lib/issues/src/handlers/pins/delete_pin.rs lib/issues/src/handlers/pins/delete_pin.rs +1 −1 View lib/issues/src/handlers/pins/delete_pin.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 delete,
12 path = "/issues/{space_name}/{board_id}/pins/{pin_id}",
12 path = "/issues/{space_name}/boards/{board_id}/pins/{pin_id}",
1313 tag = "issues",
1414 params(PinPath),
1515 responses(
Modified lib/issues/src/handlers/pins/list_pins.rs lib/issues/src/handlers/pins/list_pins.rs +1 −1 View lib/issues/src/handlers/pins/list_pins.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 get,
13 path = "/issues/{space_name}/{board_id}/pins",
13 path = "/issues/{space_name}/boards/{board_id}/pins",
1414 tag = "issues",
1515 params(BoardPath),
1616 responses(
Modified lib/issues/src/handlers/reactions/create_comment_reaction.rs lib/issues/src/handlers/reactions/create_comment_reaction.rs +1 −1 View lib/issues/src/handlers/reactions/create_comment_reaction.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 post,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/comments/{comment_id}/reactions",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/comments/{comment_id}/reactions",
1313 tag = "issues",
1414 params(CommentReactionPath),
1515 request_body = CreateReactionCmd,
Modified lib/issues/src/handlers/reactions/create_reaction.rs lib/issues/src/handlers/reactions/create_reaction.rs +1 −1 View lib/issues/src/handlers/reactions/create_reaction.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 post,
13 path = "/issues/{space_name}/{board_id}/{issue_id}/reactions",
13 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/reactions",
1414 tag = "issues",
1515 params(IssuePath),
1616 request_body = CreateReactionCmd,
Modified lib/issues/src/handlers/reactions/delete_reaction.rs lib/issues/src/handlers/reactions/delete_reaction.rs +1 −1 View lib/issues/src/handlers/reactions/delete_reaction.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 delete,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/reactions/{reaction_id}",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/reactions/{reaction_id}",
1313 tag = "issues",
1414 params(ReactionPath),
1515 responses(
Modified lib/issues/src/handlers/reactions/list_comment_reactions.rs lib/issues/src/handlers/reactions/list_comment_reactions.rs +1 −1 View lib/issues/src/handlers/reactions/list_comment_reactions.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 get,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/comments/{comment_id}/reactions",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/comments/{comment_id}/reactions",
1313 tag = "issues",
1414 params(CommentReactionPath),
1515 responses(
Modified lib/issues/src/handlers/reactions/list_reactions.rs lib/issues/src/handlers/reactions/list_reactions.rs +1 −1 View lib/issues/src/handlers/reactions/list_reactions.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 get,
13 path = "/issues/{space_name}/{board_id}/{issue_id}/reactions",
13 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/reactions",
1414 tag = "issues",
1515 params(IssuePath),
1616 responses(
Modified lib/issues/src/handlers/repo_relations/create_repo_relation.rs lib/issues/src/handlers/repo_relations/create_repo_relation.rs +1 −1 View lib/issues/src/handlers/repo_relations/create_repo_relation.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 post,
13 path = "/issues/{space_name}/{board_id}/{issue_id}/repos",
13 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/repos",
1414 tag = "issues",
1515 params(IssuePath),
1616 request_body = CreateRepoRelCmd,
Modified lib/issues/src/handlers/repo_relations/delete_repo_relation.rs lib/issues/src/handlers/repo_relations/delete_repo_relation.rs +1 −1 View lib/issues/src/handlers/repo_relations/delete_repo_relation.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 delete,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/repos/{rel_id}",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/repos/{rel_id}",
1313 tag = "issues",
1414 params(RepoRelPath),
1515 responses(
Modified lib/issues/src/handlers/repo_relations/list_repo_relations.rs lib/issues/src/handlers/repo_relations/list_repo_relations.rs +1 −1 View lib/issues/src/handlers/repo_relations/list_repo_relations.rs
@@ -10,7 +10,7 @@
1010
1111#[utoipa::path(
1212 get,
13 path = "/issues/{space_name}/{board_id}/{issue_id}/repos",
13 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/repos",
1414 tag = "issues",
1515 params(IssuePath),
1616 responses(
Modified lib/issues/src/handlers/stopwatch/get_stopwatch.rs lib/issues/src/handlers/stopwatch/get_stopwatch.rs +1 −1 View lib/issues/src/handlers/stopwatch/get_stopwatch.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 get,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/stopwatch",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/stopwatch",
1313 tag = "issues",
1414 params(StopwatchPath),
1515 responses(
Modified lib/issues/src/handlers/stopwatch/start_stopwatch.rs lib/issues/src/handlers/stopwatch/start_stopwatch.rs +1 −1 View lib/issues/src/handlers/stopwatch/start_stopwatch.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 post,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/stopwatch/start",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/stopwatch/start",
1313 tag = "issues",
1414 params(StopwatchPath),
1515 responses(
Modified lib/issues/src/handlers/stopwatch/stop_stopwatch.rs lib/issues/src/handlers/stopwatch/stop_stopwatch.rs +1 −1 View lib/issues/src/handlers/stopwatch/stop_stopwatch.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 post,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/stopwatch/stop",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/stopwatch/stop",
1313 tag = "issues",
1414 params(StopwatchPath),
1515 responses(
Modified lib/issues/src/handlers/tracked_time/list_tracked_time.rs lib/issues/src/handlers/tracked_time/list_tracked_time.rs +1 −1 View lib/issues/src/handlers/tracked_time/list_tracked_time.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 get,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/times",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/times",
1313 tag = "issues",
1414 params(TrackedTimePath, ListTrackedTimeQuery),
1515 responses(
Modified lib/issues/src/handlers/watches/delete_watch.rs lib/issues/src/handlers/watches/delete_watch.rs +1 −1 View lib/issues/src/handlers/watches/delete_watch.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 delete,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/watch",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/watch",
1313 tag = "issues",
1414 params(WatchPath),
1515 responses(
Modified lib/issues/src/handlers/watches/get_watch.rs lib/issues/src/handlers/watches/get_watch.rs +1 −1 View lib/issues/src/handlers/watches/get_watch.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 get,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/watch",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/watch",
1313 tag = "issues",
1414 params(WatchPath),
1515 responses(
Modified lib/issues/src/handlers/watches/upsert_watch.rs lib/issues/src/handlers/watches/upsert_watch.rs +1 −1 View lib/issues/src/handlers/watches/upsert_watch.rs
@@ -9,7 +9,7 @@
99
1010#[utoipa::path(
1111 put,
12 path = "/issues/{space_name}/{board_id}/{issue_id}/watch",
12 path = "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/watch",
1313 tag = "issues",
1414 params(WatchPath),
1515 request_body = UpsertWatchCmd,
Modified lib/issues/src/services/issue_users.rs lib/issues/src/services/issue_users.rs +1 −1 View lib/issues/src/services/issue_users.rs
@@ -46,7 +46,7 @@
4646 pub space_name: String,
4747 pub board_id: Uuid,
4848 pub issue_id: Uuid,
49 pub uid: Uuid,
49 pub user_id: Uuid,
5050}
5151
5252impl IssueService {
Modified lib/issues/src/errors.rs lib/issues/src/errors.rs +5 −5 View lib/issues/src/errors.rs
@@ -138,11 +138,11 @@
138138impl From<IssueError> for ApiError {
139139 fn from(error: IssueError) -> Self {
140140 match error {
141 IssueError::BadRequest(msg) => Self::BadRequest(AppResponseData::err(400, msg)),
142 IssueError::Unauthorized => Self::Unauthorized(Self::payload(401, "unauthorized")),
143 IssueError::Forbidden => Self::Forbidden(Self::payload(403, "forbidden")),
144 IssueError::NotFound => Self::NotFound(Self::payload(404, "not found")),
145 IssueError::Conflict(msg) => Self::Conflict(AppResponseData::err(409, msg)),
141 IssueError::BadRequest(msg) => Self::BadRequest(AppResponseData::err(400, msg).with_error_code("bad_request")),
142 IssueError::Unauthorized => Self::Unauthorized(Self::payload(401, "unauthorized").with_error_code("unauthorized")),
143 IssueError::Forbidden => Self::Forbidden(Self::payload(403, "forbidden").with_error_code("forbidden")),
144 IssueError::NotFound => Self::NotFound(Self::payload(404, "not found").with_error_code("not_found")),
145 IssueError::Conflict(msg) => Self::Conflict(AppResponseData::err(409, msg).with_error_code("conflict")),
146146 IssueError::Internal(message) => {
147147 tracing::warn!(error = %message, "issues handler failed");
148148 Self::Internal(Self::payload(500, "internal server error"))
Modified lib/issues/src/routes.rs lib/issues/src/routes.rs +34 −34 View lib/issues/src/routes.rs
@@ -64,141 +64,141 @@
6464
6565pub fn router(service: IssueService) -> Router<IssueService> {
6666 Router::new()
67 .route("/issues/{space_name}", post(create_board).get(list_boards))
67 .route("/issues/{space_name}/boards", post(create_board).get(list_boards))
6868 .route(
69 "/issues/{space_name}/{board_id}",
69 "/issues/{space_name}/boards/{board_id}",
7070 get(get_board).patch(update_board).delete(delete_board),
7171 )
7272 .route(
73 "/issues/{space_name}/{board_id}/issues",
73 "/issues/{space_name}/boards/{board_id}/issues",
7474 get(list_issues).post(create_issue),
7575 )
7676 .route(
77 "/issues/{space_name}/{board_id}/{issue_id}",
77 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}",
7878 get(get_issue).patch(update_issue).delete(delete_issue),
7979 )
8080 .route(
81 "/issues/{space_name}/{board_id}/{issue_id}/close",
81 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/close",
8282 post(close_issue),
8383 )
8484 .route(
85 "/issues/{space_name}/{board_id}/{issue_id}/reopen",
85 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/reopen",
8686 post(reopen_issue),
8787 )
8888 .route(
89 "/issues/{space_name}/{board_id}/{issue_id}/comments",
89 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/comments",
9090 get(list_comments).post(create_comment),
9191 )
9292 .route(
93 "/issues/{space_name}/{board_id}/{issue_id}/comments/{comment_id}",
93 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/comments/{comment_id}",
9494 get(get_comment)
9595 .patch(update_comment)
9696 .delete(delete_comment),
9797 )
9898 .route(
99 "/issues/{space_name}/{board_id}/labels",
99 "/issues/{space_name}/boards/{board_id}/labels",
100100 get(list_labels).post(create_label),
101101 )
102102 .route(
103 "/issues/{space_name}/{board_id}/labels/{label_id}",
103 "/issues/{space_name}/boards/{board_id}/labels/{label_id}",
104104 get(get_label).patch(update_label).delete(delete_label),
105105 )
106106 .route(
107 "/issues/{space_name}/{board_id}/{issue_id}/labels",
107 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/labels",
108108 get(list_labels_for_issue),
109109 )
110110 .route(
111 "/issues/{space_name}/{board_id}/{issue_id}/labels/{label_id}",
111 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/labels/{label_id}",
112112 post(bind_label).delete(unbind_label),
113113 )
114114 .route(
115 "/issues/{space_name}/{board_id}/milestones",
115 "/issues/{space_name}/boards/{board_id}/milestones",
116116 get(list_milestones).post(create_milestone),
117117 )
118118 .route(
119 "/issues/{space_name}/{board_id}/milestones/{milestone_id}",
119 "/issues/{space_name}/boards/{board_id}/milestones/{milestone_id}",
120120 get(get_milestone)
121121 .patch(update_milestone)
122122 .delete(delete_milestone),
123123 )
124124 .route(
125 "/issues/{space_name}/{board_id}/{issue_id}/reactions",
125 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/reactions",
126126 get(list_reactions).post(create_reaction),
127127 )
128128 .route(
129 "/issues/{space_name}/{board_id}/{issue_id}/reactions/{reaction_id}",
129 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/reactions/{reaction_id}",
130130 delete(delete_reaction),
131131 )
132132 .route(
133 "/issues/{space_name}/{board_id}/{issue_id}/comments/{comment_id}/reactions",
133 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/comments/{comment_id}/reactions",
134134 get(list_comment_reactions).post(create_comment_reaction),
135135 )
136136 .route(
137 "/issues/{space_name}/{board_id}/{issue_id}/watch",
137 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/watch",
138138 get(get_watch).put(upsert_watch).delete(delete_watch),
139139 )
140140 .route(
141 "/issues/{space_name}/{board_id}/{issue_id}/members",
141 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/members",
142142 get(list_members).post(create_member),
143143 )
144144 .route(
145 "/issues/{space_name}/{board_id}/{issue_id}/members/{member_id}",
145 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/members/{member_id}",
146146 delete(delete_member),
147147 )
148148 .route(
149 "/issues/{space_name}/{board_id}/pins",
149 "/issues/{space_name}/boards/{board_id}/pins",
150150 get(list_pins).post(create_pin),
151151 )
152152 .route(
153 "/issues/{space_name}/{board_id}/pins/{pin_id}",
153 "/issues/{space_name}/boards/{board_id}/pins/{pin_id}",
154154 delete(delete_pin),
155155 )
156156 .route(
157 "/issues/{space_name}/{board_id}/{issue_id}/events",
157 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/events",
158158 get(list_events),
159159 )
160160 .route(
161 "/issues/{space_name}/{board_id}/{issue_id}/dependencies",
161 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/dependencies",
162162 get(list_dependencies).post(create_dependency),
163163 )
164164 .route(
165 "/issues/{space_name}/{board_id}/{issue_id}/dependencies/{dep_id}",
165 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/dependencies/{dep_id}",
166166 delete(delete_dependency),
167167 )
168168 .route(
169 "/issues/{space_name}/{board_id}/{issue_id}/history",
169 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/history",
170170 get(list_content_history),
171171 )
172172 .route(
173 "/issues/{space_name}/{board_id}/{issue_id}/stopwatch",
173 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/stopwatch",
174174 get(get_stopwatch),
175175 )
176176 .route(
177 "/issues/{space_name}/{board_id}/{issue_id}/stopwatch/start",
177 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/stopwatch/start",
178178 post(start_stopwatch),
179179 )
180180 .route(
181 "/issues/{space_name}/{board_id}/{issue_id}/stopwatch/stop",
181 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/stopwatch/stop",
182182 post(stop_stopwatch),
183183 )
184184 .route(
185 "/issues/{space_name}/{board_id}/{issue_id}/times",
185 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/times",
186186 get(list_tracked_time),
187187 )
188188 .route(
189 "/issues/{space_name}/{board_id}/{issue_id}/users",
189 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/users",
190190 get(list_issue_users),
191191 )
192192 .route(
193 "/issues/{space_name}/{board_id}/{issue_id}/users/{uid}",
193 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/users/{user_id}",
194194 patch(update_issue_user),
195195 )
196196 .route(
197 "/issues/{space_name}/{board_id}/{issue_id}/repos",
197 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/repos",
198198 get(list_repo_relations).post(create_repo_relation),
199199 )
200200 .route(
201 "/issues/{space_name}/{board_id}/{issue_id}/repos/{rel_id}",
201 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/repos/{rel_id}",
202202 delete(delete_repo_relation),
203203 )
204204 .route_layer(axum::middleware::from_fn_with_state(
Modified lib/space/src/services/spaces.rs lib/space/src/services/spaces.rs +8 −11 View lib/space/src/services/spaces.rs
@@ -115,17 +115,9 @@
115115 pub welcome_object: Option<Option<Uuid>>,
116116}
117117
118#[derive(Debug, Clone, Serialize, ToSchema)]
119pub struct SpacePage {
120 pub items: Vec<SpaceDto>,
121 pub total: i64,
122}
118pub type SpacePage = export::pagination::Page<SpaceDto>;
123119
124#[derive(Debug, Clone, Deserialize, IntoParams, ToSchema)]
125pub struct PageQuery {
126 pub limit: Option<u64>,
127 pub offset: Option<u64>,
128}
120pub use export::pagination::PageQuery;
129121
130122#[derive(Debug, Clone, Deserialize, IntoParams, ToSchema)]
131123pub struct SpaceNamePath {
@@ -513,6 +505,11 @@
513505 })
514506 .collect();
515507
516 Ok(SpacePage { items, total })
508 Ok(SpacePage {
509 items,
510 total,
511 limit,
512 offset,
513 })
517514 }
518515}
Modified lib/space/src/errors.rs lib/space/src/errors.rs +5 −5 View lib/space/src/errors.rs
@@ -131,11 +131,11 @@
131131impl From<SpaceError> for ApiError {
132132 fn from(error: SpaceError) -> Self {
133133 match error {
134 SpaceError::BadRequest(msg) => Self::BadRequest(AppResponseData::err(400, msg)),
135 SpaceError::Unauthorized => Self::Unauthorized(Self::payload(401, "unauthorized")),
136 SpaceError::Forbidden => Self::Forbidden(Self::payload(403, "forbidden")),
137 SpaceError::NotFound => Self::NotFound(Self::payload(404, "not found")),
138 SpaceError::Conflict(msg) => Self::Conflict(AppResponseData::err(409, msg)),
134 SpaceError::BadRequest(msg) => Self::BadRequest(AppResponseData::err(400, msg).with_error_code("bad_request")),
135 SpaceError::Unauthorized => Self::Unauthorized(Self::payload(401, "unauthorized").with_error_code("unauthorized")),
136 SpaceError::Forbidden => Self::Forbidden(Self::payload(403, "forbidden").with_error_code("forbidden")),
137 SpaceError::NotFound => Self::NotFound(Self::payload(404, "not found").with_error_code("not_found")),
138 SpaceError::Conflict(msg) => Self::Conflict(AppResponseData::err(409, msg).with_error_code("conflict")),
139139 SpaceError::Internal(message) => {
140140 tracing::warn!(error = %message, "space handler failed");
141141 Self::Internal(Self::payload(500, "internal server error"))
Modified lib/space/src/openapi.rs lib/space/src/openapi.rs +1 −1 View lib/space/src/openapi.rs
@@ -78,7 +78,7 @@
7878 AppResponseData<crate::services::dir::UserOverwriteDto>,
7979 AppResponseData<Vec<crate::services::dir::UserOverwriteDto>>,
8080 crate::services::spaces::SpaceDto,
81 crate::services::spaces::SpacePage,
81 export::pagination::Page<crate::services::spaces::SpaceDto>,
8282 crate::services::spaces::CreateSpaceCmd,
8383 crate::services::spaces::UpdateSpaceCmd,
8484 crate::services::spaces::UserBriefDto,
Modified lib/user/src/share/page.rs lib/user/src/share/page.rs +1 −23 View lib/user/src/share/page.rs
@@ -1,23 +1 @@
1use serde::Deserialize;
2use utoipa::{IntoParams, ToSchema};
3
4pub const DEFAULT_PAGE_LIMIT: u64 = 50;
5pub const MAX_PAGE_LIMIT: u64 = 100;
6
7#[derive(Debug, Clone, Deserialize, IntoParams, ToSchema)]
8pub struct PageQuery {
9 pub limit: Option<u64>,
10 pub offset: Option<u64>,
11}
12
13impl PageQuery {
14 pub fn limit(&self) -> u64 {
15 self.limit
16 .unwrap_or(DEFAULT_PAGE_LIMIT)
17 .clamp(1, MAX_PAGE_LIMIT)
18 }
19
20 pub fn offset(&self) -> u64 {
21 self.offset.unwrap_or(0)
22 }
23}
1pub use export::pagination::{PageQuery, Page, DEFAULT_PAGE_LIMIT, MAX_PAGE_LIMIT};
Modified lib/user/src/share/types.rs lib/user/src/share/types.rs +2 −14 View lib/user/src/share/types.rs
@@ -1,5 +1,5 @@
11use serde::{Deserialize, Serialize};
2use utoipa::{IntoParams, ToSchema};
2use utoipa::ToSchema;
33use uuid::Uuid;
44
55#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, ToSchema)]
@@ -10,16 +10,4 @@
1010 pub avatar_url: String,
1111}
1212
13#[derive(Debug, Clone, Serialize, Deserialize, ToSchema, IntoParams)]
14pub struct ListQuery {
15 pub limit: Option<u64>,
16 pub offset: Option<u64>,
17}
18
19impl ListQuery {
20 pub fn page(&self) -> (u64, u64) {
21 let limit = self.limit.unwrap_or(50).clamp(1, 100);
22 let offset = self.offset.unwrap_or(0);
23 (limit, offset)
24 }
25}
13pub type ListQuery = export::pagination::PageQuery;
Modified lib/user/src/errors.rs lib/user/src/errors.rs +5 −5 View lib/user/src/errors.rs
@@ -137,11 +137,11 @@
137137impl From<UserError> for ApiError {
138138 fn from(error: UserError) -> Self {
139139 match error {
140 UserError::BadRequest(msg) => Self::BadRequest(AppResponseData::err(400, msg)),
141 UserError::Unauthorized => Self::Unauthorized(Self::payload(401, "unauthorized")),
142 UserError::Forbidden => Self::Forbidden(Self::payload(403, "forbidden")),
143 UserError::NotFound => Self::NotFound(Self::payload(404, "not found")),
144 UserError::Conflict(msg) => Self::Conflict(AppResponseData::err(409, msg)),
140 UserError::BadRequest(msg) => Self::BadRequest(AppResponseData::err(400, msg).with_error_code("bad_request")),
141 UserError::Unauthorized => Self::Unauthorized(Self::payload(401, "unauthorized").with_error_code("unauthorized")),
142 UserError::Forbidden => Self::Forbidden(Self::payload(403, "forbidden").with_error_code("forbidden")),
143 UserError::NotFound => Self::NotFound(Self::payload(404, "not found").with_error_code("not_found")),
144 UserError::Conflict(msg) => Self::Conflict(AppResponseData::err(409, msg).with_error_code("conflict")),
145145 UserError::Internal(message) => {
146146 tracing::warn!(error = %message, "user handler failed");
147147 Self::Internal(Self::payload(500, "internal server error"))
Modified lib/webapi/src/lib.rs lib/webapi/src/lib.rs +20 −5 View lib/webapi/src/lib.rs
@@ -1,13 +1,15 @@
1pub mod metrics;
12pub mod openapi;
23
34use axum::Router;
45use export::AppState;
6use export::middleware::AppTraceLayer;
57use session::{SessionLayer, store};
68use tower_http::limit::RequestBodyLimitLayer;
79use tower_http::trace::{DefaultMakeSpan, DefaultOnRequest, DefaultOnResponse, TraceLayer};
810use tracing::Level;
911
10/// Max chat JSON request body (covers the 512 KiB base64 emoji upload).
12
1113const MAX_REQUEST_BODY_BYTES: usize = 2 * 1024 * 1024;
1214
1315pub async fn router(state: AppState) -> Result<Router, axum_session::SessionError> {
@@ -20,13 +22,24 @@
2022 let user = user::services::service::UserService::new(state.clone());
2123 let git = git::service::GitService::new(state);
2224
23 let router: Router = Router::new()
24 .merge(auth::routes::router(auth.clone()).with_state(auth))
25 let public: Router = Router::new()
26 .merge(auth::routes::router(auth.clone()).with_state(auth.clone()))
2527 .merge(chat::routes::router(chat.clone()).with_state(chat))
2628 .merge(issues::routes::router(issues.clone()).with_state(issues))
2729 .merge(space::routes::router(space.clone()).with_state(space))
2830 .merge(user::routes::router(user.clone()).with_state(user))
29 .merge(git::routes::router(git.clone()).with_state(git))
31 .merge(git::routes::router(git.clone()).with_state(git.clone()));
32 let internal: Router = Router::new()
33 .merge(git::routes::internal_router(git.clone()).with_state(git))
34 .route_layer(axum::middleware::from_fn_with_state(
35 auth.clone(),
36 auth::share::guard::admin_guard,
37 ));
38
39 let router: Router = Router::new()
40 .nest("/api/v1", public)
41 .nest("/api/v1/internal", internal)
42 .nest("/pages", auth::routes::pages_router(auth.clone()))
3043 .layer(SessionLayer::new(store))
3144 .layer(RequestBodyLimitLayer::new(MAX_REQUEST_BODY_BYTES))
3245 .layer(
@@ -38,7 +51,9 @@
3851 )
3952 .on_request(DefaultOnRequest::new().level(Level::INFO))
4053 .on_response(DefaultOnResponse::new().level(Level::INFO)),
41 );
54 )
55 .layer(metrics::metrics_layer())
56 .layer(AppTraceLayer::new());
4257
4358 Ok(router)
4459}
Added lib/webapi/src/metrics.rs lib/webapi/src/metrics.rs +28 −0 View lib/webapi/src/metrics.rs
@@ -0,0 +1,28 @@
1use std::time::Duration;
2
3use axum_otel_metrics::{HttpMetricsLayer, HttpMetricsLayerBuilder};
4use opentelemetry::global;
5use opentelemetry_otlp::MetricExporter;
6use opentelemetry_sdk::metrics::{PeriodicReader, SdkMeterProvider, Temporality};
7
8const EXPORT_INTERVAL: Duration = Duration::from_secs(30);
9
10pub fn init_meter_provider() -> Result<SdkMeterProvider, String> {
11 let exporter = MetricExporter::builder()
12 .with_http()
13 .with_temporality(Temporality::default())
14 .build()
15 .map_err(|e| e.to_string())?;
16
17 let reader = PeriodicReader::builder(exporter)
18 .with_interval(EXPORT_INTERVAL)
19 .build();
20
21 let provider = SdkMeterProvider::builder().with_reader(reader).build();
22 global::set_meter_provider(provider.clone());
23 Ok(provider)
24}
25
26pub fn metrics_layer() -> HttpMetricsLayer {
27 HttpMetricsLayerBuilder::new().build()
28}
Modified lib/webapi/src/openapi.rs lib/webapi/src/openapi.rs +1 −0 View lib/webapi/src/openapi.rs
@@ -12,5 +12,6 @@
1212 doc.merge(space::openapi::SpaceApiDoc::openapi());
1313 doc.merge(user::openapi::UserApiDoc::openapi());
1414 doc.merge(git::openapi::GitApiDoc::openapi());
15 doc.servers = Some(vec![utoipa::openapi::Server::new("/api/v1")]);
1516 doc
1617}
Modified lib/webapi/Cargo.toml lib/webapi/Cargo.toml +5 −1 View lib/webapi/Cargo.toml
@@ -16,4 +16,8 @@
1616axum_session = { workspace = true }
1717tower-http = { workspace = true }
1818tracing = { workspace = true }
19utoipa = { workspace = true }
No newline at end of file
19utoipa = { workspace = true }
20axum-otel-metrics = { workspace = true }
21opentelemetry = { workspace = true }
22opentelemetry_sdk = { workspace = true }
23opentelemetry-otlp = { workspace = true }
No newline at end of file
Modified Cargo.lock Cargo.lock +203 −1 View Cargo.lock
@@ -716,6 +716,20 @@
716716]
717717
718718[[package]]
719name = "axum-otel-metrics"
720version = "0.14.1"
721source = "registry+https://github.com/rust-lang/crates.io-index"
722checksum = "d0d74d40b1ff9bd3e8f782aa6798d22ff6a7d1b53049e0e239f5332dab2dd83d"
723dependencies = [
724 "axum",
725 "http-body 1.1.0",
726 "opentelemetry",
727 "opentelemetry-semantic-conventions",
728 "pin-project-lite",
729 "tower",
730]
731
732[[package]]
719733name = "axum_session"
720734version = "0.21.0"
721735source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2043,15 +2057,21 @@
20432057version = "1.0.0"
20442058dependencies = [
20452059 "axum",
2060 "bytes",
20462061 "config",
20472062 "email",
2063 "http-body-util",
2064 "rand 0.8.7",
20482065 "redis",
20492066 "sea-orm",
20502067 "serde",
2068 "serde_json",
20512069 "storage",
20522070 "tokio",
2071 "tower",
20532072 "tracing",
20542073 "utoipa",
2074 "uuid",
20552075]
20562076
20572077[[package]]
@@ -3925,6 +3945,7 @@
39253945 "pin-project-lite",
39263946 "smallvec",
39273947 "tokio",
3948 "want",
39283949]
39293950
39303951[[package]]
@@ -3957,13 +3978,21 @@
39573978source = "registry+https://github.com/rust-lang/crates.io-index"
39583979checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0"
39593980dependencies = [
3981 "base64 0.22.1",
39603982 "bytes",
3983 "futures-channel",
3984 "futures-util",
39613985 "http 1.5.0",
39623986 "http-body 1.1.0",
39633987 "hyper 1.11.0",
3988 "ipnet",
3989 "libc",
3990 "percent-encoding 2.3.2",
39643991 "pin-project-lite",
3992 "socket2 0.6.5",
39653993 "tokio",
39663994 "tower-service",
3995 "tracing",
39673996]
39683997
39693998[[package]]
@@ -4530,6 +4559,12 @@
45304559]
45314560
45324561[[package]]
4562name = "ipnet"
4563version = "2.12.1"
4564source = "registry+https://github.com/rust-lang/crates.io-index"
4565checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78"
4566
4567[[package]]
45334568name = "issues"
45344569version = "1.0.0"
45354570dependencies = [
@@ -5400,6 +5435,84 @@
54005435]
54015436
54025437[[package]]
5438name = "opentelemetry"
5439version = "0.32.0"
5440source = "registry+https://github.com/rust-lang/crates.io-index"
5441checksum = "b0142c63252a9e054e68a4c61a5778f7b14f576274d593f8ce883d191a099682"
5442dependencies = [
5443 "futures-core",
5444 "futures-sink",
5445 "js-sys",
5446 "pin-project-lite",
5447 "thiserror 2.0.20",
5448 "tracing",
5449]
5450
5451[[package]]
5452name = "opentelemetry-http"
5453version = "0.32.0"
5454source = "registry+https://github.com/rust-lang/crates.io-index"
5455checksum = "5683015d09e2df236ef005b17f6f196f0d5f6313c4fa43a7b6a53b52776e4331"
5456dependencies = [
5457 "async-trait",
5458 "bytes",
5459 "http 1.5.0",
5460 "opentelemetry",
5461 "reqwest",
5462]
5463
5464[[package]]
5465name = "opentelemetry-otlp"
5466version = "0.32.0"
5467source = "registry+https://github.com/rust-lang/crates.io-index"
5468checksum = "9966929966d17620d7c316c643ba62631826e10021409357772d5eea84f62c35"
5469dependencies = [
5470 "http 1.5.0",
5471 "opentelemetry",
5472 "opentelemetry-http",
5473 "opentelemetry-proto",
5474 "opentelemetry_sdk",
5475 "prost",
5476 "reqwest",
5477 "thiserror 2.0.20",
5478]
5479
5480[[package]]
5481name = "opentelemetry-proto"
5482version = "0.32.0"
5483source = "registry+https://github.com/rust-lang/crates.io-index"
5484checksum = "56d658ba1faf63f7b9c492cfbe6e0ec365440a16132d3270c1065f7b33f1b638"
5485dependencies = [
5486 "opentelemetry",
5487 "opentelemetry_sdk",
5488 "prost",
5489]
5490
5491[[package]]
5492name = "opentelemetry-semantic-conventions"
5493version = "0.32.1"
5494source = "registry+https://github.com/rust-lang/crates.io-index"
5495checksum = "c913ac17a6c451661ee255f4625d143e51647ae78ebd969b75e41c4442f4fe47"
5496
5497[[package]]
5498name = "opentelemetry_sdk"
5499version = "0.32.1"
5500source = "registry+https://github.com/rust-lang/crates.io-index"
5501checksum = "9b59f80e1ac4d5ff7a2db8fb6c80badb7f0f3f858211fba08dd9aaec750894f9"
5502dependencies = [
5503 "futures-channel",
5504 "futures-executor",
5505 "futures-util",
5506 "opentelemetry",
5507 "percent-encoding 2.3.2",
5508 "portable-atomic",
5509 "rand 0.9.5",
5510 "thiserror 2.0.20",
5511 "tokio",
5512 "tokio-stream",
5513]
5514
5515[[package]]
54035516name = "option-ext"
54045517version = "0.2.0"
54055518source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -6018,6 +6131,29 @@
60186131]
60196132
60206133[[package]]
6134name = "prost"
6135version = "0.14.4"
6136source = "registry+https://github.com/rust-lang/crates.io-index"
6137checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1"
6138dependencies = [
6139 "bytes",
6140 "prost-derive",
6141]
6142
6143[[package]]
6144name = "prost-derive"
6145version = "0.14.4"
6146source = "registry+https://github.com/rust-lang/crates.io-index"
6147checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf"
6148dependencies = [
6149 "anyhow",
6150 "itertools 0.14.0",
6151 "proc-macro2",
6152 "quote",
6153 "syn 2.0.119",
6154]
6155
6156[[package]]
60216157name = "psm"
60226158version = "0.1.32"
60236159source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -6315,6 +6451,37 @@
63156451]
63166452
63176453[[package]]
6454name = "reqwest"
6455version = "0.13.4"
6456source = "registry+https://github.com/rust-lang/crates.io-index"
6457checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3"
6458dependencies = [
6459 "base64 0.22.1",
6460 "bytes",
6461 "futures-channel",
6462 "futures-core",
6463 "futures-util",
6464 "http 1.5.0",
6465 "http-body 1.1.0",
6466 "http-body-util",
6467 "hyper 1.11.0",
6468 "hyper-util",
6469 "js-sys",
6470 "log 0.4.33",
6471 "percent-encoding 2.3.2",
6472 "pin-project-lite",
6473 "sync_wrapper",
6474 "tokio",
6475 "tower",
6476 "tower-http 0.6.11",
6477 "tower-service",
6478 "url 2.5.8",
6479 "wasm-bindgen",
6480 "wasm-bindgen-futures",
6481 "web-sys",
6482]
6483
6484[[package]]
63186485name = "rfc6979"
63196486version = "0.4.0"
63206487source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -7775,6 +7942,9 @@
77757942version = "1.0.2"
77767943source = "registry+https://github.com/rust-lang/crates.io-index"
77777944checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263"
7945dependencies = [
7946 "futures-core",
7947]
77787948
77797949[[package]]
77807950name = "synchost"
@@ -8126,6 +8296,24 @@
81268296
81278297[[package]]
81288298name = "tower-http"
8299version = "0.6.11"
8300source = "registry+https://github.com/rust-lang/crates.io-index"
8301checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840"
8302dependencies = [
8303 "bitflags 2.13.1",
8304 "bytes",
8305 "futures-util",
8306 "http 1.5.0",
8307 "http-body 1.1.0",
8308 "pin-project-lite",
8309 "tower",
8310 "tower-layer",
8311 "tower-service",
8312 "url 2.5.8",
8313]
8314
8315[[package]]
8316name = "tower-http"
81298317version = "0.7.0"
81308318source = "registry+https://github.com/rust-lang/crates.io-index"
81318319checksum = "b11f75e912b0c2be01b63d8cf8057b8c3f97cf34abb3d431a3a4c8675498e233"
@@ -8616,6 +8804,16 @@
86168804]
86178805
86188806[[package]]
8807name = "web-sys"
8808version = "0.3.104"
8809source = "registry+https://github.com/rust-lang/crates.io-index"
8810checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30"
8811dependencies = [
8812 "js-sys",
8813 "wasm-bindgen",
8814]
8815
8816[[package]]
86198817name = "web-time"
86208818version = "1.1.0"
86218819source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -8631,14 +8829,18 @@
86318829dependencies = [
86328830 "auth",
86338831 "axum",
8832 "axum-otel-metrics",
86348833 "axum_session",
86358834 "chat",
86368835 "export",
86378836 "git",
86388837 "issues",
8838 "opentelemetry",
8839 "opentelemetry-otlp",
8840 "opentelemetry_sdk",
86398841 "session",
86408842 "space",
8641 "tower-http",
8843 "tower-http 0.7.0",
86428844 "tracing",
86438845 "user",
86448846 "utoipa",
Modified Cargo.toml Cargo.toml +5 −0 View Cargo.toml
@@ -55,7 +55,12 @@
5555serde_json = "1.0.151"
5656axum = { version = "0.8.9", features = ["ws"] }
5757axum_session = { version = "0.21.0", features = ["key-store"] }
58tower = "0.5.3"
5859tower-http = { version = "0.7.0", features = ["trace", "limit"] }
60axum-otel-metrics = "0.14.1"
61opentelemetry = "0.32"
62opentelemetry_sdk = { version = "0.32.1", features = ["rt-tokio"] }
63opentelemetry-otlp = "0.32"
5964tracing = "0.1.44"
6065figment = { version = "0.10", features = ["toml", "env"] }
6166dotenvy = "0.15"
Modified openapi.json openapi.json View openapi.json
Diff too large to display.