Skip to content
Clone

refactor: update imports to use export::http for consistency

ZhenYi committed
parent e1df092 commit b7cd2b1 Browse files

300 files changed

+1043 −1340

This change is very large, so some diffs are not shown. Clone the repository to see the full change.

File tree 300
Modified app/src/main.rs View app/src/main.rs
@@ -21,14 +21,13 @@
2121
2222#[tokio::main]
2323async fn serve() -> anyhow::Result<()> {
24 use axum::Router;
24 use actix_web::middleware::from_fn;
25 use actix_web::{App, HttpServer, web};
2526 use config::scoped::{ApihostConfig, ScopedConfig};
26 use tokio::net::TcpListener;
2727 use tokio_util::sync::CancellationToken;
2828 use tracing::{info, warn};
2929 use utoipa_swagger_ui::SwaggerUi;
3030 use webapi::openapi::api_doc;
31 use webapi::router;
3231
3332 export::telemetry::init_tracing();
3433
@@ -112,12 +111,13 @@
112111 );
113112 }
114113
115 let api_router: Router = router(state)
116 .await
117 .map_err(|err| anyhow::anyhow!("failed to build router: {err}"))?;
114 let verifier = webapi::verifier(&state);
115 if let Some(verifier) = &verifier {
116 let warm = verifier.clone();
117 tokio::spawn(async move { warm.warm_up().await });
118 }
118119 let doc = api_doc();
119 let api_router =
120 api_router.merge(SwaggerUi::new("/api/docs").url("/api/docs/openapi.json", doc));
120 let app_state = state.clone();
121121
122122 let bind_addr = cfg.apihost.bind_addr().to_string();
123123 let port = cfg.apihost.port();
@@ -125,20 +125,44 @@
125125 .parse()
126126 .with_context(|| format!("parsing bind address {bind_addr}:{port}"))?;
127127
128 let listener = TcpListener::bind(addr)
129 .await
130 .with_context(|| format!("binding {addr}"))?;
128 let server = HttpServer::new(move || {
129 let state = app_state.clone();
130 let mut app = App::new()
131 .app_data(web::Data::new(state.clone()))
132 .app_data(
133 web::JsonConfig::default()
134 .limit(webapi::MAX_REQUEST_BODY_BYTES)
135 .error_handler(export::http::json_error),
136 )
137 .app_data(web::PayloadConfig::new(webapi::MAX_REQUEST_BODY_BYTES));
138 if let Some(verifier) = verifier.clone() {
139 app = app.app_data(web::Data::new(verifier));
140 }
141 app.wrap(webapi::build_cors(&state.config.web))
142 .wrap(from_fn(export::middleware::trace_request))
143 .wrap(from_fn(export::telemetry::http_telemetry))
144 .wrap(from_fn(auth::oauth::revalidate::revalidate))
145 .service(web::resource("/api/docs").route(web::get().to(|| async {
146 actix_web::HttpResponse::Found()
147 .insert_header((actix_web::http::header::LOCATION, "/api/docs/"))
148 .finish()
149 })))
150 .service(SwaggerUi::new("/api/docs/{_:.*}").url("/api/docs/openapi.json", doc.clone()))
151 .service(webapi::scope(state))
152 })
153 .disable_signals()
154 .bind(addr)
155 .with_context(|| format!("binding {addr}"))?
156 .run();
131157 info!(%addr, "apihost listening");
132158
159 let handle = server.handle();
133160 let api_cancel = cancel.clone();
134 let api = async move {
135 let result = axum::serve(listener, api_router.into_make_service())
136 .with_graceful_shutdown(api_cancel.clone().cancelled_owned())
137 .await
138 .context("axum server error");
139 api_cancel.cancel();
140 result
141 };
161 tokio::spawn(async move {
162 api_cancel.cancelled().await;
163 handle.stop(true).await;
164 });
165 let api = async move { server.await.context("actix server error") };
142166 let identity_cancel = cancel.clone();
143167 let identity_server = async move {
144168 match identity {
Modified app/Cargo.toml View app/Cargo.toml
@@ -10,7 +10,7 @@
1010kenoauth = { workspace = true }
1111anyhow = { workspace = true }
1212auth = { workspace = true }
13axum = { workspace = true }
13actix-web = { workspace = true }
1414chat = { workspace = true }
1515clap = { workspace = true }
1616config = { workspace = true }
Modified docs/oauth.md View docs/oauth.md
@@ -212,7 +212,7 @@
212212 src/share.rs # 兼容路径
213213```
214214
215依赖:`axum`、`serde`、`serde_json`、`jsonwebtoken`、`reqwest`、`tokio`、`time`、`tracing`、`config`、`export`。
215依赖:`actix-web`、`serde`、`serde_json`、`jsonwebtoken`、`reqwest`、`tokio`、`time`、`tracing`、`config`、`export`。
216216
217217### 5.3 Claims
218218
Modified lib/accounts/src/proto_conv.rs View lib/accounts/src/proto_conv.rs
@@ -4,7 +4,8 @@
44};
55
66use crate::organization::{
7 OrgJoinMethod, OrgMemberRole, OrganizationStatus as DomainStatus, PermissionEffect as DomainEffect,
7 OrgJoinMethod, OrgMemberRole, OrganizationStatus as DomainStatus,
8 PermissionEffect as DomainEffect,
89};
910
1011impl From<DomainStatus> for OrganizationStatus {
Modified lib/auth/src/handlers/accept_agreement.rs View lib/auth/src/handlers/accept_agreement.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32
43use export::rest::{AppResponseData, NoData};
54use jwt::Session;
Modified lib/auth/src/handlers/account_deletion.rs View lib/auth/src/handlers/account_deletion.rs
@@ -1,4 +1,4 @@
1use axum::extract::State;
1use export::http::State;
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use jwt::share::session;
Modified lib/auth/src/handlers/agreement_page.rs View lib/auth/src/handlers/agreement_page.rs
@@ -1,6 +1,4 @@
1use axum::extract::{Path, State};
2use axum::http::HeaderMap;
3use axum::response::Html;
1use export::http::{HeaderMap, Html, Path, State};
42use i18n::catalog;
53
64use crate::errors::ApiError;
@@ -16,7 +14,7 @@
1614 .mail_locale(
1715 None,
1816 headers
19 .get(axum::http::header::ACCEPT_LANGUAGE)
17 .get(actix_web::http::header::ACCEPT_LANGUAGE)
2018 .and_then(|value| value.to_str().ok()),
2119 )
2220 .await;
Modified lib/auth/src/handlers/create_report.rs View lib/auth/src/handlers/create_report.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::State;
1use export::http::{Json, State};
32
43use export::rest::{AppResponseData, NoData};
54use jwt::Session;
Modified lib/auth/src/handlers/delete_notification.rs View lib/auth/src/handlers/delete_notification.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use uuid::Uuid;
Modified lib/auth/src/handlers/devices.rs View lib/auth/src/handlers/devices.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Query, State};
2use axum::response::Json;
1use export::http::{Query, State};
32use export::pagination::PageQuery;
43use export::rest::AppResponseData;
54use jwt::Session;
@@ -119,7 +118,7 @@
119118 State(service): State<AuthService>,
120119 session: Session,
121120 Query(query): Query<PageQuery>,
122) -> Result<Json<AppResponseData<Vec<DeviceDto>>>, ApiError> {
121) -> Result<AppResponseData<Vec<DeviceDto>>, ApiError> {
123122 let uid = session::require_uid(&session)?;
124123 let client = rpc::client(&service.state)?;
125124 let listed = client
@@ -180,5 +179,5 @@
180179 last_seen_at: row.last_seen_at,
181180 })
182181 .collect();
183 Ok(Json(AppResponseData::ok(devices)))
182 Ok(AppResponseData::ok(devices))
184183}
Modified lib/auth/src/handlers/get_preferences.rs View lib/auth/src/handlers/get_preferences.rs
@@ -1,4 +1,4 @@
1use axum::extract::State;
1use export::http::State;
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/auth/src/handlers/get_user.rs View lib/auth/src/handlers/get_user.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use jwt::share::session;
Modified lib/auth/src/handlers/jwt_request.rs View lib/auth/src/handlers/jwt_request.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Query, State};
1use export::http::{Query, State};
22use serde::{Deserialize, Serialize};
33use utoipa::ToSchema;
44
Modified lib/auth/src/handlers/list_agreements.rs View lib/auth/src/handlers/list_agreements.rs
@@ -1,4 +1,4 @@
1use axum::extract::State;
1use export::http::State;
22use export::rest::{AppResponseData, NoData};
33
44use crate::errors::ApiError;
Modified lib/auth/src/handlers/list_notifications.rs View lib/auth/src/handlers/list_notifications.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Query, State};
1use export::http::{Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use serde::Deserialize;
Modified lib/auth/src/handlers/mark_notifications_read.rs View lib/auth/src/handlers/mark_notifications_read.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::State;
1use export::http::{Json, State};
32use export::rest::{AppResponseData, NoData};
43use jwt::Session;
54use serde::Deserialize;
Modified lib/auth/src/handlers/me.rs View lib/auth/src/handlers/me.rs
@@ -1,4 +1,4 @@
1use axum::extract::State;
1use export::http::State;
22use export::rest::{AppResponseData, NoData};
33use serde::Serialize;
44use utoipa::ToSchema;
Modified lib/auth/src/handlers/password.rs View lib/auth/src/handlers/password.rs
@@ -1,6 +1,4 @@
1use axum::Json;
2use axum::extract::State;
3use axum::http::HeaderMap;
1use export::http::{HeaderMap, Json, State};
42use export::rest::{AppResponseData, NoData};
53use jwt::Session;
64use jwt::share::session;
Modified lib/auth/src/handlers/sessions.rs View lib/auth/src/handlers/sessions.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::pagination::{Page, PageQuery, paginate};
33use export::rest::{AppResponseData, NoData};
44use jwt::Session;
Modified lib/auth/src/handlers/two_factor.rs View lib/auth/src/handlers/two_factor.rs
@@ -1,6 +1,4 @@
1use axum::Json;
2use axum::extract::State;
3use axum::http::HeaderMap;
1use export::http::{HeaderMap, Json, State};
42use export::rest::{AppResponseData, NoData};
53use jwt::Session;
64use jwt::share::session;
Modified lib/auth/src/handlers/unread_notifications_count.rs View lib/auth/src/handlers/unread_notifications_count.rs
@@ -1,4 +1,4 @@
1use axum::extract::State;
1use export::http::State;
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use serde::Serialize;
Modified lib/auth/src/handlers/update_preferences.rs View lib/auth/src/handlers/update_preferences.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::State;
1use export::http::{Json, State};
32
43use export::rest::{AppResponseData, NoData};
54use jwt::Session;
Modified lib/auth/src/handlers/update_profile.rs View lib/auth/src/handlers/update_profile.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::State;
1use export::http::{Json, State};
32
43use export::rest::{AppResponseData, NoData};
54use jwt::Session;
Modified lib/auth/src/handlers/users.rs View lib/auth/src/handlers/users.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, Query, State};
1use export::http::{Json, Path, Query, State};
32use export::rest::{AppResponseData, NoData};
43use jwt::Session;
54use jwt::share::session;
Modified lib/auth/src/oauth/callback.rs View lib/auth/src/oauth/callback.rs
@@ -1,8 +1,6 @@
11use std::sync::Arc;
22
3use axum::Extension;
4use axum::extract::{Query, State};
5use axum::response::Response;
3use export::http::{Extension, Query, Response, State};
64use serde::Deserialize;
75
86use crate::errors::ApiError;
Modified lib/auth/src/oauth/flow.rs View lib/auth/src/oauth/flow.rs
@@ -1,6 +1,5 @@
1use axum::http::header::SET_COOKIE;
2use axum::http::{HeaderMap, HeaderValue};
3use axum::response::{IntoResponse, Redirect, Response};
1use actix_web::http::header::{HeaderMap, HeaderValue, SET_COOKIE};
2use export::http::{Redirect, Response};
43use serde::{Deserialize, Serialize};
54
65use config::oauth::OAuthConfig;
@@ -50,7 +49,10 @@
5049}
5150
5251pub fn read_cookie_header<'a>(headers: &'a HeaderMap, name: &str) -> Option<&'a str> {
53 let value = headers.get(axum::http::header::COOKIE)?.to_str().ok()?;
52 let value = headers
53 .get(actix_web::http::header::COOKIE)?
54 .to_str()
55 .ok()?;
5456 jwt::read_cookie(value, name)
5557}
5658
@@ -143,7 +145,7 @@
143145 .and_then(|value| value.to_str().ok())
144146 .or_else(|| {
145147 headers
146 .get(axum::http::header::HOST)
148 .get(actix_web::http::header::HOST)
147149 .and_then(|value| value.to_str().ok())
148150 })
149151 // 代理链可能给逗号分隔的列表,取第一跳。
Modified lib/auth/src/oauth/login.rs View lib/auth/src/oauth/login.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Query, State};
2use axum::response::{IntoResponse, Redirect, Response};
1use export::http::{Query, Redirect, Response, State};
32use serde::Deserialize;
43use url::form_urlencoded;
54
@@ -34,7 +33,7 @@
3433pub async fn signup(
3534 State(service): State<AuthService>,
3635 Query(query): Query<SignupQuery>,
37 headers: axum::http::HeaderMap,
36 headers: export::http::HeaderMap,
3837) -> Result<Response, ApiError> {
3938 let config = &service.config().oauth;
4039 if !config.enabled {
@@ -81,7 +80,7 @@
8180pub async fn login(
8281 State(service): State<AuthService>,
8382 Query(query): Query<LoginQuery>,
84 headers: axum::http::HeaderMap,
83 headers: export::http::HeaderMap,
8584) -> Result<Response, ApiError> {
8685 let config = &service.config().oauth;
8786 if !config.enabled {
Modified lib/auth/src/oauth/logout.rs View lib/auth/src/oauth/logout.rs
@@ -1,9 +1,6 @@
11use std::sync::Arc;
22
3use axum::Extension;
4use axum::extract::State;
5use axum::http::HeaderMap;
6use axum::response::{IntoResponse, Redirect, Response};
3use export::http::{Extension, HeaderMap, Redirect, Response, State};
74
85use crate::errors::ApiError;
96use crate::oauth::{append_cookie, read_cookie_header};
Modified lib/auth/src/oauth/refresh.rs View lib/auth/src/oauth/refresh.rs
@@ -1,9 +1,7 @@
11use std::sync::Arc;
22
3use axum::Extension;
4use axum::extract::State;
5use axum::http::{HeaderMap, StatusCode};
6use axum::response::{IntoResponse, Response};
3use actix_web::http::StatusCode;
4use export::http::{Extension, HeaderMap, Response, State};
75
86use crate::errors::ApiError;
97use crate::oauth::{REFRESH_LOCK_TTL_SECS, append_cookie, read_cookie_header, refresh_grant};
@@ -43,7 +41,7 @@
4341 let tokens = match result {
4442 Ok(tokens) => tokens,
4543 Err(_) => {
46 let mut response = StatusCode::UNAUTHORIZED.into_response();
44 let mut response = Response::build(StatusCode::UNAUTHORIZED).finish();
4745 append_cookie(&mut response, &jwt::clear_session_cookie(&config.oauth));
4846 append_cookie(&mut response, &jwt::clear_refresh_cookie(&config.oauth));
4947 return Ok(response);
@@ -54,7 +52,7 @@
5452 return Err(ApiError::from(crate::errors::AuthError::Unauthorized));
5553 }
5654
57 let mut response = StatusCode::NO_CONTENT.into_response();
55 let mut response = Response::build(StatusCode::NO_CONTENT).finish();
5856 append_cookie(
5957 &mut response,
6058 &jwt::session_cookie(
Modified lib/auth/src/oauth/revalidate.rs View lib/auth/src/oauth/revalidate.rs
@@ -1,10 +1,10 @@
11use std::sync::Arc;
22
3use axum::Extension;
4use axum::extract::{Request, State};
5use axum::http::StatusCode;
6use axum::middleware::Next;
7use axum::response::{IntoResponse, Response};
3use actix_web::body::{BoxBody, MessageBody};
4use actix_web::dev::{ServiceRequest, ServiceResponse};
5use actix_web::middleware::Next;
6use actix_web::web::Data;
7use actix_web::{Error, HttpResponse};
88
99use export::AppState;
1010
@@ -21,29 +21,44 @@
2121 "/swagger",
2222];
2323
24pub async fn revalidate(
25 State(state): State<AppState>,
26 Extension(verifier): Extension<Arc<jwt::JwtVerifier>>,
27 request: Request,
28 next: Next,
29) -> Response {
30 let path = request.uri().path();
31 if SKIP_PREFIXES.iter().any(|prefix| path.starts_with(prefix)) {
32 return next.run(request).await;
24pub async fn revalidate<B>(
25 request: ServiceRequest,
26 next: Next<B>,
27) -> Result<ServiceResponse<BoxBody>, Error>
28where
29 B: MessageBody + 'static,
30{
31 if SKIP_PREFIXES
32 .iter()
33 .any(|prefix| request.path().starts_with(prefix))
34 {
35 return Ok(next.call(request).await?.map_into_boxed_body());
3336 }
34 let Some(token) = read_cookie_header(request.headers(), &state.config.oauth.session_cookie)
35 .map(str::to_owned)
37 let Some(state) = request
38 .app_data::<Data<AppState>>()
39 .map(|value| value.get_ref().clone())
3640 else {
37 return next.run(request).await;
41 return Ok(next.call(request).await?.map_into_boxed_body());
3842 };
43 let Some(verifier) = request
44 .app_data::<Data<Arc<jwt::JwtVerifier>>>()
45 .map(|value| value.get_ref().clone())
46 else {
47 return Ok(next.call(request).await?.map_into_boxed_body());
48 };
49 let token = read_cookie_header(request.headers(), &state.config.oauth.session_cookie)
50 .map(str::to_owned);
51 let Some(token) = token else {
52 return Ok(next.call(request).await?.map_into_boxed_body());
53 };
3954 let Ok(claims) = verifier.verify_access(&token).await else {
40 return next.run(request).await;
55 return Ok(next.call(request).await?.map_into_boxed_body());
4156 };
42 let Some(session_id) = claims.sid.as_deref() else {
43 return next.run(request).await;
57 let Some(session_id) = claims.sid.as_deref().map(str::to_owned) else {
58 return Ok(next.call(request).await?.map_into_boxed_body());
4459 };
45 let Some(kenauth) = state.kenauth.as_ref() else {
46 return next.run(request).await;
60 let Some(kenauth) = state.kenauth.as_ref().cloned() else {
61 return Ok(next.call(request).await?.map_into_boxed_body());
4762 };
4863
4964 let key = format!("oauth:recheck:{}:{}", session_id, claims.security_version);
@@ -53,39 +68,35 @@
5368 .flatten()
5469 .is_some()
5570 {
56 return next.run(request).await;
71 return Ok(next.call(request).await?.map_into_boxed_body());
5772 }
5873
5974 match kenauth
60 .get_session(claims.sub, claims.security_version, session_id)
75 .get_session(claims.sub, claims.security_version, &session_id)
6176 .await
6277 {
6378 Ok(_) => {
6479 let _ =
6580 export::redis::set_string(&state.redis, &key, "1", state.config.oauth.recheck_secs)
6681 .await;
67 next.run(request).await
82 Ok(next.call(request).await?.map_into_boxed_body())
6883 }
6984 Err(kenauth::KenAuthError::Unauthenticated) | Err(kenauth::KenAuthError::NotFound) => {
70 let mut response = (
71 StatusCode::UNAUTHORIZED,
72 axum::Json(serde_json::json!({
73 "code": 401,
74 "message": "session_revoked",
75 "data": null,
76 "error": null,
77 "error_code": "session_revoked",
78 "request_id": null,
79 "log_id": null,
80 })),
81 )
82 .into_response();
85 let mut response = HttpResponse::Unauthorized().json(serde_json::json!({
86 "code": 401,
87 "message": "session_revoked",
88 "data": null,
89 "error": null,
90 "error_code": "session_revoked",
91 "request_id": null,
92 "log_id": null,
93 }));
8394 append_cookie(
8495 &mut response,
8596 &jwt::clear_session_cookie(&state.config.oauth),
8697 );
87 response
98 Ok(request.into_response(response))
8899 }
89 Err(_) => next.run(request).await,
100 Err(_) => Ok(next.call(request).await?.map_into_boxed_body()),
90101 }
91102}
Modified lib/auth/src/share/guard.rs View lib/auth/src/share/guard.rs
@@ -1,16 +1 @@
1use axum::extract::{Request, State};
2use axum::middleware::Next;
3use axum::response::Response;
4use jwt::Session;
5use jwt::share::guard::session_guard;
6
7use crate::services::AuthService;
8
9pub async fn auth_guard(
10 State(service): State<AuthService>,
11 session: Session,
12 request: Request,
13 next: Next,
14) -> Response {
15 session_guard::<AuthService>(State(service), session, request, next).await
16}
1pub use jwt::share::guard::session_guard as auth_guard;
Modified lib/auth/src/share/rpc.rs View lib/auth/src/share/rpc.rs
@@ -1,7 +1,7 @@
11use std::sync::Arc;
22
3use axum::http::HeaderMap;
43use export::AppState;
4use export::http::HeaderMap;
55use kenauth::KenAuthClient;
66use sha2::{Digest, Sha256};
77use time::OffsetDateTime;
Modified lib/auth/src/templates/common.rs View lib/auth/src/templates/common.rs
@@ -1,4 +1,4 @@
1use axum::response::Html;
1use export::http::Html;
22use i18n::UiLocale;
33use maud::{DOCTYPE, Markup, html};
44
Modified lib/auth/src/templates/page.rs View lib/auth/src/templates/page.rs
@@ -1,4 +1,4 @@
1use axum::response::Html;
1use export::http::Html;
22use i18n::{UiLocale, catalog};
33use maud::html;
44
Modified lib/auth/src/routes.rs View lib/auth/src/routes.rs
@@ -1,5 +1,5 @@
1use axum::routing::{delete, get, post, put};
2use axum::{Router, middleware};
1use export::http::Router;
2use export::http::routing::{delete, get, post, put};
33
44use crate::handlers::accept_agreement::accept_agreement;
55use crate::handlers::account_deletion::{
@@ -19,10 +19,9 @@
1919use crate::handlers::users;
2020use crate::oauth;
2121use crate::services::AuthService;
22use crate::share::guard::auth_guard;
2322
24pub fn router(service: AuthService) -> Router<AuthService> {
25 let public = Router::new()
23pub fn router(_service: AuthService) -> Router<AuthService> {
24 let public = Router::<AuthService>::new()
2625 .route("/auth/login", get(oauth::login::login))
2726 .route("/auth/signup", get(oauth::login::signup))
2827 .route("/auth/callback", get(oauth::callback::callback))
@@ -30,7 +29,7 @@
3029 .route("/auth/logout", post(oauth::logout::logout))
3130 .route("/auth/agreements", get(list_agreements));
3231
33 let authed = Router::new()
32 let authed = Router::<AuthService>::new()
3433 .route("/users", get(users::list_users_batch))
3534 .route(
3635 "/users/by-username/{username}",
@@ -103,13 +102,13 @@
103102 delete(crate::handlers::sessions::revoke_session),
104103 )
105104 .route("/me/devices", get(crate::handlers::devices::list_devices))
106 .route_layer(middleware::from_fn_with_state(service, auth_guard));
105 .wrap(export::http::Guard::Session);
107106
108107 public.merge(authed)
109108}
110109
111110pub fn pages_router(service: AuthService) -> Router {
112 Router::new()
111 Router::<()>::new()
113112 .route("/agreements/{key}", get(agreement_page))
114113 .with_state(service)
115114}
Modified lib/auth/Cargo.toml View lib/auth/Cargo.toml
@@ -12,7 +12,7 @@
1212export = { workspace = true }
1313models = { workspace = true }
1414space = { workspace = true }
15axum = { workspace = true }
15actix-web = { workspace = true }
1616sea-orm = { workspace = true }
1717tokio = { workspace = true }
1818redis = { workspace = true }
Modified lib/chat/src/handlers/attachments.rs View lib/chat/src/handlers/attachments.rs
@@ -1,6 +1,4 @@
1use axum::extract::{Json, Path, Query, State};
2use axum::http::{HeaderMap, StatusCode};
3use axum::response::{IntoResponse, Response};
1use export::http::{Json, Path, Query, Response, State};
42use export::rest::{AppResponseData, NoData};
53use imaging::layout::ImageVariant;
64use jwt::Session;
@@ -120,9 +118,10 @@
120118 query.variant.unwrap_or(ImageVariant::Original),
121119 )
122120 .await?;
123 let mut headers = HeaderMap::new();
121 let mut response = actix_web::HttpResponse::Ok().streaming(body);
122 let headers = response.headers_mut();
124123 for (name, value) in ChatService::attachment_content_headers(&row, content_type) {
125 if let Ok(value) = value.parse() {
124 if let Ok(value) = actix_web::http::header::HeaderValue::from_str(&value) {
126125 headers.insert(name, value);
127126 }
128127 }
@@ -131,9 +130,9 @@
131130 // as they were.
132131 if query.variant == Some(ImageVariant::Preview) {
133132 headers.insert(
134 axum::http::header::CACHE_CONTROL,
133 actix_web::http::header::CACHE_CONTROL,
135134 "private, max-age=300".parse().expect("static header value"),
136135 );
137136 }
138 Ok((StatusCode::OK, headers, body).into_response())
137 Ok(response)
139138}
Modified lib/chat/src/handlers/bookmarks.rs View lib/chat/src/handlers/bookmarks.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, Query, State};
1use export::http::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use jwt::share::session;
Modified lib/chat/src/handlers/connection.rs View lib/chat/src/handlers/connection.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, State};
1use export::http::{Json, State};
22use export::rest::{AppResponseData, NoData};
33use serde::Deserialize;
44use utoipa::ToSchema;
Modified lib/chat/src/handlers/dms.rs View lib/chat/src/handlers/dms.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, Query, State};
1use export::http::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use jwt::share::session;
Modified lib/chat/src/handlers/drafts.rs View lib/chat/src/handlers/drafts.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, Query, State};
1use export::http::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use jwt::share::session;
Modified lib/chat/src/handlers/emojis.rs View lib/chat/src/handlers/emojis.rs
@@ -1,6 +1,4 @@
1use axum::extract::{Json, Path, State};
2use axum::http::StatusCode;
3use axum::response::{IntoResponse, Response};
1use export::http::{Json, Path, Response, State};
42use export::rest::{AppResponseData, NoData};
53use jwt::Session;
64use jwt::share::session;
@@ -108,7 +106,7 @@
108106 let space = lookup_space_by_name(&service, &space_name).await?;
109107 require_space_member(&service, &space, uid).await?;
110108 let bytes = service.emoji_image(&space, emoji_id).await?;
111 Ok((StatusCode::OK, bytes).into_response())
109 Ok(actix_web::HttpResponse::Ok().body(bytes))
112110}
113111
114112#[utoipa::path(
Modified lib/chat/src/handlers/messages.rs View lib/chat/src/handlers/messages.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Json, Path, Query, State};
2use axum::response::IntoResponse;
1use export::http::{Json, Path, Query, State};
32use export::rest::{AppResponseData, NoData};
43use jwt::Session;
54use jwt::share::session;
@@ -32,10 +31,10 @@
3231 session: Session,
3332 Path(room_id): Path<Uuid>,
3433 Json(cmd): Json<SendMessageCmd>,
35) -> Result<axum::response::Response, ApiError> {
34) -> Result<AppResponseData<MessageDto>, ApiError> {
3635 let uid = session::require_uid(&session)?;
3736 let dto = service.send_message(uid, room_id, cmd).await?;
38 Ok(AppResponseData::ok(dto).into_response())
37 Ok(AppResponseData::ok(dto))
3938}
4039
4140#[utoipa::path(
Modified lib/chat/src/handlers/presence.rs View lib/chat/src/handlers/presence.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use jwt::share::session;
Modified lib/chat/src/handlers/reactions.rs View lib/chat/src/handlers/reactions.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use jwt::share::session;
Modified lib/chat/src/handlers/read_state.rs View lib/chat/src/handlers/read_state.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use jwt::share::session;
Modified lib/chat/src/handlers/reminders.rs View lib/chat/src/handlers/reminders.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use jwt::share::session;
Modified lib/chat/src/handlers/room_admin.rs View lib/chat/src/handlers/room_admin.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, Query, State};
1use export::http::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use jwt::share::session;
Modified lib/chat/src/handlers/rooms.rs View lib/chat/src/handlers/rooms.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, Query, State};
1use export::http::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use jwt::share::session;
Modified lib/chat/src/handlers/settings.rs View lib/chat/src/handlers/settings.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use jwt::share::session;
Modified lib/chat/src/handlers/threads.rs View lib/chat/src/handlers/threads.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, Query, State};
1use export::http::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use jwt::share::session;
Modified lib/chat/src/handlers/voice.rs View lib/chat/src/handlers/voice.rs
@@ -1,6 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, Query, State};
3use axum::http::HeaderMap;
1use export::http::{HeaderMap, Json, Path, Query, State};
42use export::rest::{AppResponseData, NoData};
53use jwt::Session;
64use jwt::share::session;
Modified lib/chat/src/handlers/webhooks.rs View lib/chat/src/handlers/webhooks.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, Query, State};
1use export::http::{Json, Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use jwt::share::session;
Modified lib/chat/src/services/attachments.rs View lib/chat/src/services/attachments.rs
@@ -1,5 +1,6 @@
1use axum::body::Body;
2use axum::http::header;
1use actix_web::http::header;
2use actix_web::web::Bytes;
3use futures::stream::BoxStream;
34use imaging::layout::{ImageVariant, PreviewState};
45use imaging::preset::PreviewPreset;
56use models::chat::attachments::{self, AttachmentStatus};
@@ -451,7 +452,14 @@
451452 caller: i64,
452453 attachment_id: Uuid,
453454 variant: ImageVariant,
454 ) -> Result<(attachments::Model, Body, String), ChatError> {
455 ) -> Result<
456 (
457 attachments::Model,
458 BoxStream<'static, Result<Bytes, std::io::Error>>,
459 String,
460 ),
461 ChatError,
462 > {
455463 let row = attachments::Entity::find_by_id(attachment_id)
456464 .one(self.db().read())
457465 .await?
@@ -462,10 +470,10 @@
462470 }
463471 let (key, content_type) = self.attachment_variant_locator(&row, variant).await;
464472 let stream = self.storage().client().get_stream(&key).await?;
465 let stream = futures::StreamExt::map(stream, |item| {
466 item.map_err(|e| std::io::Error::other(e.to_string()))
467 });
468 let body = Body::from_stream(stream);
473 let body = Box::pin(futures::StreamExt::map(stream, |item| {
474 item.map(Bytes::from)
475 .map_err(|e| std::io::Error::other(e.to_string()))
476 })) as BoxStream<'static, Result<Bytes, std::io::Error>>;
469477 Ok((row, body, content_type))
470478 }
471479
Modified lib/chat/src/share/guard.rs View lib/chat/src/share/guard.rs
@@ -1,16 +1 @@
1use axum::extract::{Request, State};
2use axum::middleware::Next;
3use axum::response::Response;
4use jwt::Session;
5use jwt::share::guard::session_guard;
6
7use crate::services::service::ChatService;
8
9pub async fn chat_guard(
10 State(service): State<ChatService>,
11 session: Session,
12 request: Request,
13 next: Next,
14) -> Response {
15 session_guard::<ChatService>(State(service), session, request, next).await
16}
1pub use jwt::share::guard::session_guard as chat_guard;
Modified lib/chat/src/routes.rs View lib/chat/src/routes.rs
@@ -1,5 +1,5 @@
1use axum::Router;
2use axum::routing::{delete, get, post, put};
1use export::http::Router;
2use export::http::routing::{delete, get, post, put};
33
44use crate::handlers::attachments::{
55 create_attachment, download_attachment, finalize_attachment, get_attachment,
@@ -36,33 +36,29 @@
3636};
3737use crate::handlers::webhooks::{create_webhook, delete_webhook, list_webhooks, post_via_webhook};
3838use crate::services::service::ChatService;
39use crate::share::guard::chat_guard;
4039
4140pub fn router(
42 service: ChatService,
41 _service: ChatService,
4342 space_service: space::services::service::SpaceService,
4443) -> Router<ChatService> {
45 let space_room_routes = Router::new()
44 let space_room_routes = Router::<ChatService>::new()
4645 .route(
4746 "/chat/spaces/{space_name}/rooms",
48 axum::routing::post(space::handlers::create_room::create_room),
47 export::http::routing::post(space::handlers::create_room::create_room),
4948 )
5049 .route(
5150 "/chat/rooms/{room_id}",
5251 delete(space::handlers::delete_room::delete_room),
5352 )
5453 .with_state(space_service)
55 .route_layer(axum::middleware::from_fn_with_state(
56 service.clone(),
57 chat_guard,
58 ));
54 .wrap(export::http::Guard::Session);
5955
60 let guarded = Router::new()
56 let guarded = Router::<ChatService>::new()
6157 .route("/chat/spaces/{space_name}/rooms", get(list_space_rooms))
6258 .route("/chat/rooms/{room_id}", get(get_room).patch(update_room))
6359 .route(
6460 "/chat/rooms/{room_id}/members/@me",
65 axum::routing::put(join_room).delete(leave_room),
61 export::http::routing::put(join_room).delete(leave_room),
6662 )
6763 .route(
6864 "/chat/rooms/{room_id}/members",
@@ -113,7 +109,7 @@
113109 )
114110 .route(
115111 "/chat/rooms/{room_id}/messages/{message_id}/pin",
116 axum::routing::put(set_message_pin),
112 export::http::routing::put(set_message_pin),
117113 )
118114 .route("/chat/rooms/{room_id}/pins", get(list_pins))
119115 .route(
@@ -136,11 +132,11 @@
136132 )
137133 .route(
138134 "/chat/threads/{thread_id}/members/@me",
139 axum::routing::put(follow_thread).delete(unfollow_thread),
135 export::http::routing::put(follow_thread).delete(unfollow_thread),
140136 )
141137 .route(
142138 "/chat/threads/{thread_id}/read-state",
143 axum::routing::put(mark_thread_read),
139 export::http::routing::put(mark_thread_read),
144140 )
145141 .route(
146142 "/chat/rooms/{room_id}/messages/{message_id}/reactions",
@@ -148,7 +144,7 @@
148144 )
149145 .route(
150146 "/chat/rooms/{room_id}/messages/{message_id}/reactions/@me",
151 axum::routing::put(add_reaction).delete(remove_reaction),
147 export::http::routing::put(add_reaction).delete(remove_reaction),
152148 )
153149 .route(
154150 "/chat/rooms/{room_id}/messages/{message_id}/reminders",
@@ -157,7 +153,7 @@
157153 .route("/chat/reminders", get(list_reminders))
158154 .route(
159155 "/chat/reminders/{reminder_id}",
160 axum::routing::delete(cancel_reminder)
156 export::http::routing::delete(cancel_reminder)
161157 .patch(crate::handlers::reminders::update_reminder),
162158 )
163159 .route(
@@ -169,7 +165,10 @@
169165 get(list_space_read_states),
170166 )
171167 .route("/chat/dms", get(list_dms).post(open_dm))
172 .route("/chat/dms/{room_id}", axum::routing::delete(close_dm))
168 .route(
169 "/chat/dms/{room_id}",
170 export::http::routing::delete(close_dm),
171 )
173172 .route("/chat/attachments", post(create_attachment))
174173 .route("/chat/attachments/{attachment_id}", get(get_attachment))
175174 .route(
@@ -198,7 +197,7 @@
198197 )
199198 .route(
200199 "/chat/rooms/{room_id}/bookmarks/{bookmark_id}",
201 axum::routing::patch(update_bookmark).delete(delete_bookmark),
200 export::http::routing::patch(update_bookmark).delete(delete_bookmark),
202201 )
203202 .route(
204203 "/chat/rooms/{room_id}/draft",
@@ -207,7 +206,7 @@
207206 .delete(crate::handlers::drafts::delete_draft),
208207 )
209208 .route("/chat/drafts", get(list_my_drafts))
210 .route("/chat/presence", axum::routing::put(set_presence))
209 .route("/chat/presence", export::http::routing::put(set_presence))
211210 .route("/chat/users/{user_id}/presence", get(get_presence))
212211 .route(
213212 "/chat/spaces/{space_name}/presences",
@@ -223,7 +222,8 @@
223222 )
224223 .route(
225224 "/chat/rooms/{room_id}/webhooks/{webhook_id}",
226 axum::routing::delete(delete_webhook).patch(crate::handlers::webhooks::update_webhook),
225 export::http::routing::delete(delete_webhook)
226 .patch(crate::handlers::webhooks::update_webhook),
227227 )
228228 .route(
229229 "/chat/rooms/{room_id}/outbound-webhooks",
@@ -232,7 +232,7 @@
232232 )
233233 .route(
234234 "/chat/rooms/{room_id}/outbound-webhooks/{webhook_id}",
235 axum::routing::patch(crate::handlers::webhooks::update_outbound_webhook)
235 export::http::routing::patch(crate::handlers::webhooks::update_outbound_webhook)
236236 .delete(crate::handlers::webhooks::delete_outbound_webhook),
237237 )
238238 .route("/chat/voice/config", get(voice_config))
@@ -282,12 +282,9 @@
282282 "/chat/rooms/{room_id}/voice/recording/stop",
283283 post(stop_recording),
284284 )
285 .route_layer(axum::middleware::from_fn_with_state(
286 service.clone(),
287 chat_guard,
288 ));
285 .wrap(export::http::Guard::Session);
289286
290 let open = Router::new()
287 let open = Router::<ChatService>::new()
291288 .route("/chat/hooks/{token}", post(post_via_webhook))
292289 .route("/chat/livekit/webhook", post(livekit_webhook));
293290
Modified lib/chat/Cargo.toml View lib/chat/Cargo.toml
@@ -16,7 +16,7 @@
1616kenauth = { workspace = true }
1717space = { workspace = true }
1818storage = { workspace = true }
19axum = { workspace = true }
19actix-web = { workspace = true }
2020sea-orm = { workspace = true }
2121redis = { workspace = true }
2222reqwest = { workspace = true }
Modified lib/config/src/identity/redis.rs View lib/config/src/identity/redis.rs
@@ -154,7 +154,10 @@
154154 // 这是可以出现在日志与 /status 里的字符串:密码只能以掩码形式出现。
155155 let credentials = match (&self.username, &self.password) {
156156 (Some(user), Some(_)) => {
157 format!("{}:<redacted>@", crate::identity::database::url_encode(user))
157 format!(
158 "{}:<redacted>@",
159 crate::identity::database::url_encode(user)
160 )
158161 }
159162 (None, Some(_)) => "<redacted>@".to_string(),
160163 (Some(user), None) => {
Modified lib/email/src/queue.rs View lib/email/src/queue.rs
@@ -12,9 +12,9 @@
1212use tracing::{Instrument, debug, error, info, warn};
1313use uuid::Uuid;
1414
15use config::identity::email::EmailPoolConfig;
1615use crate::error::EmailError;
1716use crate::events::{RenderAndSendEmailRequest, SendBatchEmailRequest, SendEmailRequest};
17use config::identity::email::EmailPoolConfig;
1818
1919/// 队列里的一条工作项。
2020#[derive(Debug, Clone)]
Modified lib/email/src/service.rs View lib/email/src/service.rs
@@ -6,7 +6,6 @@
66use tracing::{debug, info, trace, warn};
77use uuid::Uuid;
88
9use db::DbConn;
109use crate::error::EmailError;
1110use crate::events::{
1211 EmailAddress, EmailFailed, EmailSent, RenderAndSendEmailRequest, SendBatchEmailRequest,
@@ -18,6 +17,7 @@
1817use crate::transport::{EmailTransport, EmailTransportBuilder, build_message};
1918use ::events::event::{EmailEvent, RestOperate};
2019use ::events::{EventHandler, Labels, RuntimeEvent};
20use db::DbConn;
2121
2222/// 邮件服务:对外只做入队,实际投递由本地队列的 worker 完成。
2323#[derive(Clone)]
Modified lib/email/src/transport.rs View lib/email/src/transport.rs
@@ -1,7 +1,7 @@
11use std::time::{Duration, Instant};
22
3use base64::Engine;
34use config::identity::smtp::SmtpSecurity;
4use base64::Engine;
55use lettre::address::Address;
66use lettre::message::header::ContentType;
77use lettre::message::{
Modified lib/envelope/src/cors.rs View lib/envelope/src/cors.rs
@@ -3,12 +3,12 @@
33use std::sync::Arc;
44use std::task::{Context, Poll};
55
6use config::identity::web::WebConfig;
76use actix_web::body::{BoxBody, EitherBody, MessageBody};
87use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
98use actix_web::http::Method;
109use actix_web::http::header::{self, HeaderMap, HeaderValue};
1110use actix_web::{Error, HttpResponse};
11use config::identity::web::WebConfig;
1212use futures_util::future::LocalBoxFuture;
1313
1414#[derive(Debug, Clone)]
Modified lib/envelope/src/middleware.rs View lib/envelope/src/middleware.rs
@@ -2,11 +2,11 @@
22use std::rc::Rc;
33use std::task::{Context, Poll};
44
5use session::{REQUEST_ID_HEADER, RequestId, validate_request_id};
65use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
76use actix_web::http::header::{HeaderMap, HeaderName, HeaderValue};
87use actix_web::{Error, HttpMessage};
98use futures_util::future::LocalBoxFuture;
9use session::{REQUEST_ID_HEADER, RequestId, validate_request_id};
1010use uuid::Uuid;
1111
1212use crate::context;
Modified lib/events/src/event/mod.rs View lib/events/src/event/mod.rs
@@ -1,7 +1,3 @@
1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4
51#[macro_use]
62mod macros;
73
Modified lib/events/src/handler.rs View lib/events/src/handler.rs
@@ -1,8 +1,8 @@
11use serde::Serialize;
22use serde_json::Value;
33
4use crate::event::{EVENT_PREFIX, RuntimeEvent};
45use config::identity::rocketmq::RocketMqConfig;
5use crate::event::{EVENT_PREFIX, RuntimeEvent};
66use queue::identity::{Envelope, Labels, Publisher, QueueError, QueueResult, RocketMqConn};
77
88pub const DURABLE_DOMAINS: &[&str] = &[
Modified lib/events/src/lib.rs View lib/events/src/lib.rs
@@ -3,6 +3,6 @@
33pub mod outbox;
44pub(crate) mod route;
55
6pub use queue::identity::{Envelope, Labels};
76pub use event::*;
87pub use handler::{EventHandler, retry_publish};
8pub use queue::identity::{Envelope, Labels};
Modified lib/events/src/outbox.rs View lib/events/src/outbox.rs
@@ -2,11 +2,11 @@
22use std::time::{Duration, Instant};
33
44use accounts::event_outbox;
5use queue::identity::{DeliveryClass, Envelope, QueueError, SCHEMA_VERSION};
65use chrono::{DateTime, Duration as ChronoDuration, Utc};
76use futures_util::future;
87use opentelemetry::metrics::Gauge;
98use opentelemetry::{KeyValue, global};
9use queue::identity::{DeliveryClass, Envelope, QueueError, SCHEMA_VERSION};
1010use sea_orm::ActiveValue::Set;
1111use sea_orm::{
1212 ColumnTrait, ConnectionTrait, DatabaseConnection, DatabaseTransaction, DbBackend, DbErr,
Modified lib/export/src/api_error.rs View lib/export/src/api_error.rs
@@ -241,14 +241,32 @@
241241 }
242242 }
243243
244 impl ::axum::response::IntoResponse for $name {
245 fn into_response(self) -> ::axum::response::Response {
246 let (status, envelope) = match self {
247 $( Self::$variant(envelope) => ($status, *envelope), )*
244 impl ::std::fmt::Display for $name {
245 fn fmt(&self, formatter: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
246 match self {
247 $( Self::$variant(envelope) => formatter.write_str(&envelope.message), )*
248 }
249 }
250 }
251
252 impl ::actix_web::ResponseError for $name {
253 fn status_code(&self) -> ::actix_web::http::StatusCode {
254 let status = match self {
255 $( Self::$variant(_) => $status, )*
256 };
257 ::actix_web::http::StatusCode::from_u16(status)
258 .unwrap_or(::actix_web::http::StatusCode::INTERNAL_SERVER_ERROR)
259 }
260
261 fn error_response(&self) -> ::actix_web::HttpResponse {
262 let (status, mut envelope) = match self {
263 $( Self::$variant(envelope) => ($status, (**envelope).clone()), )*
248264 };
249 let status = ::axum::http::StatusCode::from_u16(status)
250 .unwrap_or(::axum::http::StatusCode::INTERNAL_SERVER_ERROR);
251 (status, ::axum::Json(envelope)).into_response()
265 envelope.request_id = $crate::middleware::request_id().unwrap_or_default();
266 envelope.log_id = $crate::middleware::log_id().unwrap_or_default();
267 let status = ::actix_web::http::StatusCode::from_u16(status)
268 .unwrap_or(::actix_web::http::StatusCode::INTERNAL_SERVER_ERROR);
269 ::actix_web::HttpResponse::build(status).json(envelope)
252270 }
253271 }
254272
@@ -264,45 +282,14 @@
264282 }
265283 }
266284
267 impl From<::axum::extract::rejection::PathRejection> for $name {
268 fn from(error: ::axum::extract::rejection::PathRejection) -> Self {
269 Self::build(
270 400,
271 $crate::rest::AppResponseData::err(400, "invalid request path")
272 .with_error(error.body_text()),
273 )
274 }
275 }
276
277 impl From<::axum::extract::rejection::JsonRejection> for $name {
278 fn from(error: ::axum::extract::rejection::JsonRejection) -> Self {
279 Self::build(
280 400,
281 $crate::rest::AppResponseData::err(400, "invalid request body")
282 .with_error(error.body_text())
283 .with_error_code("invalid_request_body"),
284 )
285 }
286 }
287
288 impl From<::axum::extract::rejection::QueryRejection> for $name {
289 fn from(error: ::axum::extract::rejection::QueryRejection) -> Self {
290 Self::build(
291 400,
292 $crate::rest::AppResponseData::err(400, "invalid query parameters")
293 .with_error(error.body_text()),
294 )
295 }
296 }
297
298285 impl From<::serde_json::Error> for $name {
299286 fn from(_error: ::serde_json::Error) -> Self {
300287 Self::build(400, Self::payload(400, "bad request"))
301288 }
302289 }
303290
304 impl From<(::axum::http::StatusCode, &'static str)> for $name {
305 fn from((status, message): (::axum::http::StatusCode, &'static str)) -> Self {
291 impl From<(::actix_web::http::StatusCode, &'static str)> for $name {
292 fn from((status, message): (::actix_web::http::StatusCode, &'static str)) -> Self {
306293 ::tracing::error!(%status, %message, "unhandled (status, message) converted to 500");
307294 Self::build(500, Self::payload(500, "internal server error"))
308295 }
Modified lib/export/src/health.rs View lib/export/src/health.rs
@@ -1,11 +1,9 @@
11use std::sync::Arc;
22
3use axum::Router;
4use axum::extract::State;
5use axum::http::StatusCode;
6use axum::response::{IntoResponse, Response};
3use actix_web::HttpResponse;
74use sea_orm::{ConnectionTrait, DbBackend, Statement};
85
6use crate::http::{Router, State, get};
97use crate::{AppDatabase, AppRedis, AppState};
108
119#[derive(Clone)]
@@ -15,11 +13,11 @@
1513 kenauth: Option<Arc<kenauth::KenAuthClient>>,
1614}
1715
18async fn healthz() -> Response {
19 (StatusCode::OK, "ok").into_response()
16async fn healthz() -> HttpResponse {
17 HttpResponse::Ok().body("ok")
2018}
2119
22async fn readyz(State(state): State<HealthState>) -> Response {
20async fn readyz(State(state): State<HealthState>) -> HttpResponse {
2321 let db_ok = match state.database.as_ref() {
2422 None => true,
2523 Some(db) => db
@@ -46,9 +44,9 @@
4644 Some(client) => client.check_health().await.is_ok(),
4745 };
4846 if db_ok && redis_ok && kenauth_ok {
49 (StatusCode::OK, "ready").into_response()
47 HttpResponse::Ok().body("ready")
5048 } else {
51 (StatusCode::SERVICE_UNAVAILABLE, "not ready").into_response()
49 HttpResponse::ServiceUnavailable().body("not ready")
5250 }
5351}
5452
@@ -57,9 +55,9 @@
5755 redis: AppRedis,
5856 kenauth: Option<Arc<kenauth::KenAuthClient>>,
5957) -> Router {
60 Router::new()
61 .route("/healthz", axum::routing::get(healthz))
62 .route("/readyz", axum::routing::get(readyz))
58 Router::<()>::new()
59 .route("/healthz", get(healthz))
60 .route("/readyz", get(readyz))
6361 .with_state(HealthState {
6462 database,
6563 redis,
Modified lib/export/src/lib.rs View lib/export/src/lib.rs
@@ -3,6 +3,7 @@
33pub mod error;
44pub mod events;
55pub mod health;
6pub mod http;
67pub mod imaging;
78pub mod metrics;
89pub mod middleware;
Modified lib/export/src/middleware.rs View lib/export/src/middleware.rs
@@ -1,13 +1,10 @@
11use std::future::Future;
2use std::pin::Pin;
3use std::task::{Context, Poll};
42
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};
3use actix_web::body::{BoxBody, MessageBody};
4use actix_web::dev::{ServiceRequest, ServiceResponse};
5use actix_web::http::header::{HeaderName, HeaderValue};
6use actix_web::middleware::Next;
7use actix_web::{Error, HttpMessage};
118use uuid::Uuid;
129
1310pub const REQUEST_ID_HEADER: HeaderName = HeaderName::from_static("x-request-id");
@@ -17,6 +14,7 @@
1714
1815tokio::task_local! {
1916 static REQUEST_ID: String;
17 static LOG_ID: String;
2018}
2119
2220#[derive(Debug, Clone)]
@@ -30,6 +28,11 @@
3028 REQUEST_ID.try_with(|id| id.clone()).ok()
3129}
3230
31/// The log id of the request currently being handled, when one is set.
32pub fn log_id() -> Option<String> {
33 LOG_ID.try_with(|id| id.clone()).ok()
34}
35
3336/// Runs `future` with `request_id` available to nested code, so queue
3437/// consumers keep the id of the request that produced the message.
3538pub async fn with_request_id<F: Future>(request_id: Option<&str>, future: F) -> F::Output {
@@ -40,116 +43,45 @@
4043}
4144
4245pub fn new_request_id() -> String {
43 new_id()
44}
45
46#[derive(Debug, Clone, Copy, Default)]
47pub struct AppTraceLayer;
48
49impl AppTraceLayer {
50 pub fn new() -> Self {
51 Self
52 }
53}
54
55impl<S> Layer<S> for AppTraceLayer {
56 type Service = AppTraceService<S>;
57
58 fn layer(&self, inner: S) -> Self::Service {
59 AppTraceService { inner }
60 }
46 Uuid::now_v7().to_string()
6147}
6248
63#[derive(Debug, Clone)]
64pub struct AppTraceService<S> {
65 inner: S,
66}
67
68impl<S> Service<Request<Body>> for AppTraceService<S>
49pub async fn trace_request<B>(
50 request: ServiceRequest,
51 next: Next<B>,
52) -> Result<ServiceResponse<BoxBody>, Error>
6953where
70 S: Service<Request<Body>, Response = Response<Body>> + Send + 'static,
71 S::Future: Send + 'static,
54 B: MessageBody + 'static,
7255{
73 type Response = S::Response;
74 type Error = S::Error;
75 type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send>>;
56 let request_id =
57 inbound_id(request.headers().get(&REQUEST_ID_HEADER)).unwrap_or_else(new_request_id);
58 let log_id = new_request_id();
7659
77 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
78 self.inner.poll_ready(cx)
60 request
61 .extensions_mut()
62 .insert(RequestId(request_id.clone()));
63 request.extensions_mut().insert(LogId(log_id.clone()));
64
65 let response = REQUEST_ID
66 .scope(
67 request_id.clone(),
68 LOG_ID.scope(log_id.clone(), next.call(request)),
69 )
70 .await?;
71 let mut response = response.map_into_boxed_body();
72 if let Ok(value) = HeaderValue::try_from(request_id.as_str()) {
73 response.headers_mut().insert(REQUEST_ID_HEADER, value);
7974 }
80
81 fn call(&mut self, mut req: Request<Body>) -> Self::Future {
82 let request_id = inbound_id(req.headers().get(&REQUEST_ID_HEADER)).unwrap_or_else(new_id);
83 let log_id = new_id();
84
85 req.extensions_mut().insert(RequestId(request_id.clone()));
86 req.extensions_mut().insert(LogId(log_id.clone()));
87
88 let fut = REQUEST_ID.scope(request_id.clone(), self.inner.call(req));
89 Box::pin(async move {
90 let mut res = fut.await?;
91 if let Ok(value) = HeaderValue::try_from(request_id.as_str()) {
92 res.headers_mut().insert(REQUEST_ID_HEADER, value);
93 }
94 if let Ok(value) = HeaderValue::try_from(log_id.as_str()) {
95 res.headers_mut().insert(LOG_ID_HEADER, value);
96 }
97 inject_ids(&mut res, &request_id, &log_id).await;
98 Ok(res)
99 })
75 if let Ok(value) = HeaderValue::try_from(log_id.as_str()) {
76 response.headers_mut().insert(LOG_ID_HEADER, value);
10077 }
78 Ok(response)
10179}
10280
103fn new_id() -> String {
104 Uuid::now_v7().to_string()
105}
106
10781fn inbound_id(value: Option<&HeaderValue>) -> Option<String> {
10882 let value = value?.to_str().ok()?.trim();
10983 if value.is_empty() || value.len() > MAX_INBOUND_ID_LEN {
11084 return None;
11185 }
11286 Some(value.to_string())
113}
114
115async fn inject_ids(res: &mut Response<Body>, request_id: &str, log_id: &str) {
116 let is_json = res
117 .headers()
118 .get(header::CONTENT_TYPE)
119 .and_then(|value| value.to_str().ok())
120 .is_some_and(|value| value.starts_with("application/json"));
121 if !is_json {
122 return;
123 }
124
125 let (mut parts, body) = std::mem::take(res).into_parts();
126 let bytes = match body.collect().await {
127 Ok(collected) => collected.to_bytes(),
128 Err(_) => {
129 *res = Response::from_parts(parts, Body::empty());
130 return;
131 }
132 };
133
134 let output = match serde_json::from_slice::<Value>(&bytes) {
135 Ok(Value::Object(mut map))
136 if map.contains_key("request_id") && map.contains_key("log_id") =>
137 {
138 map.insert(
139 "request_id".to_string(),
140 Value::String(request_id.to_string()),
141 );
142 map.insert("log_id".to_string(), Value::String(log_id.to_string()));
143 match serde_json::to_vec(&map) {
144 Ok(rewritten) => Bytes::from(rewritten),
145 Err(_) => bytes,
146 }
147 }
148 _ => bytes,
149 };
150
151 if let Ok(length) = HeaderValue::try_from(output.len().to_string()) {
152 parts.headers.insert(header::CONTENT_LENGTH, length);
153 }
154 *res = Response::from_parts(parts, Body::from(output));
15587}
Modified lib/export/src/rest.rs View lib/export/src/rest.rs
@@ -1,14 +1,14 @@
1use axum::Json;
2use axum::http::StatusCode;
3use axum::response::{IntoResponse, Response};
1use actix_web::body::BoxBody;
2use actix_web::http::StatusCode;
3use actix_web::{HttpRequest, HttpResponse, Responder};
44use serde::{Deserialize, Serialize};
55use std::fmt;
66use utoipa::ToSchema;
77
8#[derive(Debug, Serialize, Deserialize, ToSchema)]
8#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
99pub struct NoData;
1010
11#[derive(Serialize, Deserialize, Debug, ToSchema)]
11#[derive(Serialize, Deserialize, Debug, Clone, ToSchema)]
1212pub struct AppResponseData<T> {
1313 pub code: i32,
1414 pub message: String,
@@ -114,10 +114,18 @@
114114 }
115115}
116116
117impl<T: Serialize> IntoResponse for AppResponseData<T> {
118 fn into_response(self) -> Response {
117impl<T: Serialize> Responder for AppResponseData<T> {
118 type Body = BoxBody;
119
120 fn respond_to(mut self, _request: &HttpRequest) -> HttpResponse<Self::Body> {
119121 let status =
120122 StatusCode::from_u16(self.code as u16).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
121 (status, Json(self)).into_response()
123 if self.request_id.is_empty() {
124 self.request_id = crate::middleware::request_id().unwrap_or_default();
125 }
126 if self.log_id.is_empty() {
127 self.log_id = crate::middleware::log_id().unwrap_or_default();
128 }
129 HttpResponse::build(status).json(self)
122130 }
123131}
Modified lib/export/src/telemetry.rs View lib/export/src/telemetry.rs
@@ -1,8 +1,11 @@
11use std::sync::OnceLock;
22use std::time::Duration;
33
4use axum::extract::MatchedPath;
5use axum::http::{HeaderMap, Request, Response};
4use actix_web::body::{BoxBody, MessageBody};
5use actix_web::dev::{ServiceRequest, ServiceResponse};
6use actix_web::middleware::Next;
7use actix_web::{Error, HttpMessage, HttpRequest, HttpResponse};
8use http::{HeaderMap, Request, Response};
69use opentelemetry::propagation::TextMapCompositePropagator;
710use opentelemetry::trace::TracerProvider as _;
811use opentelemetry::{Context, global};
@@ -72,21 +75,6 @@
7275 Ok(provider)
7376}
7477
75/// HTTP server metrics for axum-style routers. Paths without a matched route
76/// (static assets, the SPA fallback) are skipped so raw URLs never become
77/// metric labels.
78pub fn metrics_layer() -> axum_otel_metrics::HttpMetricsLayer {
79 axum_otel_metrics::HttpMetricsLayerBuilder::new()
80 .with_skipper(axum_otel_metrics::PathSkipper::new(skip_metrics_path))
81 .build()
82}
83
84fn skip_metrics_path(path: &str) -> bool {
85 matches!(path, "/healthz" | "/readyz" | "/favicon.ico")
86 || path.starts_with("/assets/")
87 || path.starts_with("/.well-known/")
88}
89
9078#[derive(Clone, Copy)]
9179enum OtlpSignal {
9280 Traces,
@@ -172,32 +160,74 @@
172160 crate::middleware::request_id()
173161}
174162
175pub fn http_span<B>(request: &Request<B>) -> Span {
176 let method = request.method().as_str();
177 let route = request
178 .extensions()
179 .get::<MatchedPath>()
180 .map_or_else(|| request.uri().path(), |path| path.as_str());
163fn span_with(
164 method: &str,
165 path: &str,
166 route: &str,
167 request_id: Option<&str>,
168 headers: &HeaderMap,
169) -> Span {
181170 let name = format!("{method} {route}");
182171 let span = tracing::info_span!(
183172 "http.server",
184173 "otel.name" = %name,
185174 "otel.kind" = "server",
186175 "http.request.method" = %method,
187 "url.path" = %request.uri().path(),
176 "url.path" = %path,
188177 "http.route" = %route,
189178 "http.response.status_code" = tracing::field::Empty,
190179 "latency_ms" = tracing::field::Empty,
191180 "request_id" = tracing::field::Empty,
192181 );
193 if let Some(request_id) = request.extensions().get::<crate::middleware::RequestId>() {
194 span.record("request_id", request_id.0.as_str());
182 if let Some(request_id) = request_id {
183 span.record("request_id", request_id);
195184 }
196 set_remote_parent(&span, request.headers());
185 set_remote_parent(&span, headers);
197186 span
198187}
199188
200pub fn on_response<B>(response: &Response<B>, latency: Duration, span: &Span) {
189pub fn http_span_actix(request: &HttpRequest) -> Span {
190 let route = request
191 .match_pattern()
192 .unwrap_or_else(|| request.path().to_string());
193 let request_id = request
194 .extensions()
195 .get::<crate::middleware::RequestId>()
196 .map(|id| id.0.clone());
197 let mut headers = HeaderMap::new();
198 for (name, value) in request.headers() {
199 if let (Ok(name), Ok(value)) = (
200 http::header::HeaderName::from_bytes(name.as_str().as_bytes()),
201 http::HeaderValue::from_bytes(value.as_bytes()),
202 ) {
203 headers.insert(name, value);
204 }
205 }
206 span_with(
207 request.method().as_str(),
208 request.path(),
209 &route,
210 request_id.as_deref(),
211 &headers,
212 )
213}
214
215pub fn http_span<B>(request: &Request<B>) -> Span {
216 let path = request.uri().path();
217 let request_id = request
218 .extensions()
219 .get::<crate::middleware::RequestId>()
220 .map(|id| id.0.clone());
221 span_with(
222 request.method().as_str(),
223 path,
224 path,
225 request_id.as_deref(),
226 request.headers(),
227 )
228}
229
230pub fn on_response<B>(response: &HttpResponse<B>, latency: Duration, span: &Span) {
201231 let status = response.status().as_u16();
202232 span.record("http.response.status_code", i64::from(status));
203233 span.record("latency_ms", latency.as_millis() as i64);
@@ -207,45 +237,25 @@
207237 }
208238}
209239
210/// `tower_http::trace::MakeSpan` adapter shared by every axum/tonic host.
211#[derive(Clone, Copy, Default)]
212pub struct HttpSpan;
213
214impl<B> tower_http::trace::MakeSpan<B> for HttpSpan {
215 fn make_span(&mut self, request: &Request<B>) -> Span {
216 http_span(request)
217 }
218}
219
220/// `tower_http::trace::OnResponse` adapter keeping the request log line and
221/// recording the HTTP semantic-convention attributes on the span.
222#[derive(Clone, Copy, Default)]
223pub struct HttpOnResponse;
224
225impl<B> tower_http::trace::OnResponse<B> for HttpOnResponse {
226 fn on_response(self, response: &Response<B>, latency: Duration, span: &Span) {
227 on_response(response, latency, span);
228 tracing::info!(
229 parent: span,
230 status = response.status().as_u16(),
231 latency_ms = latency.as_millis() as u64,
232 "finished processing request",
233 );
234 }
235}
236
237pub type HttpTraceLayer = tower_http::trace::TraceLayer<
238 tower_http::classify::SharedClassifier<tower_http::classify::ServerErrorsAsFailures>,
239 HttpSpan,
240 tower_http::trace::DefaultOnRequest,
241 HttpOnResponse,
242>;
243
244/// Ready-made server trace layer for hosts that build plain axum routers.
245pub fn http_trace_layer() -> HttpTraceLayer {
246 tower_http::trace::TraceLayer::new_for_http()
247 .make_span_with(HttpSpan)
248 .on_response(HttpOnResponse)
240pub async fn http_telemetry<B>(
241 request: ServiceRequest,
242 next: Next<B>,
243) -> Result<ServiceResponse<BoxBody>, Error>
244where
245 B: MessageBody + 'static,
246{
247 let span = http_span_actix(request.request());
248 let started = std::time::Instant::now();
249 let response = next.call(request).instrument(span.clone()).await?;
250 let latency = started.elapsed();
251 on_response(response.response(), latency, &span);
252 tracing::info!(
253 parent: &span,
254 status = response.status().as_u16(),
255 latency_ms = latency.as_millis() as u64,
256 "finished processing request",
257 );
258 Ok(response.map_into_boxed_body())
249259}
250260
251261/// Body-agnostic tower layer for servers whose body type must stay untouched
@@ -316,7 +326,7 @@
316326 if let Some(request_id) = crate::middleware::request_id()
317327 && let Ok(value) = request_id.parse()
318328 {
319 headers.insert(crate::middleware::REQUEST_ID_HEADER, value);
329 headers.insert(http::header::HeaderName::from_static("x-request-id"), value);
320330 }
321331
322332 let result = request
Modified lib/export/Cargo.toml View lib/export/Cargo.toml
@@ -12,13 +12,14 @@
1212redis = { workspace = true }
1313tokio = { workspace = true, features = ["macros", "rt-multi-thread", "signal"] }
1414kenauth = { workspace = true }
15jwt = { workspace = true }
1516imaging = { workspace = true }
16axum = { workspace = true }
17actix-web = { workspace = true }
18http = "1.5.0"
1719serde = { workspace = true, features = ["derive"] }
1820sha2 = { workspace = true }
1921tracing = { workspace = true }
2022tracing-subscriber = { workspace = true }
21axum-otel-metrics = { workspace = true }
2223opentelemetry = { workspace = true }
2324opentelemetry_sdk = { workspace = true }
2425opentelemetry-http = { workspace = true }
@@ -27,10 +28,8 @@
2728tracing-opentelemetry = { workspace = true }
2829utoipa = { workspace = true }
2930tower = { workspace = true }
30tower-http = { workspace = true }
3131serde_json = { workspace = true }
3232uuid = { workspace = true }
3333bytes = { workspace = true }
34http-body-util = { workspace = true }
3534thiserror = { workspace = true }
3635time = { workspace = true }
Modified lib/git/src/handler/analysis/code_activity.rs View lib/git/src/handler/analysis/code_activity.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/analysis/contributors.rs View lib/git/src/handler/analysis/contributors.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/analysis/languages.rs View lib/git/src/handler/analysis/languages.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/analysis/languages_read.rs View lib/git/src/handler/analysis/languages_read.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/archives/create.rs View lib/git/src/handler/archives/create.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/archives/create_bundle.rs View lib/git/src/handler/archives/create_bundle.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/archives/download.rs View lib/git/src/handler/archives/download.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/blobs/content.rs View lib/git/src/handler/blobs/content.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/blobs/list_lfs_pointers.rs View lib/git/src/handler/blobs/list_lfs_pointers.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/blobs/metadata.rs View lib/git/src/handler/blobs/metadata.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/branch_protection/create_policy.rs View lib/git/src/handler/branch_protection/create_policy.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/branch_protection/delete_policy.rs View lib/git/src/handler/branch_protection/delete_policy.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Path, State};
2use axum::http::StatusCode;
1use export::http::{Path, State};
32use export::rest::{AppResponseData, NoData};
43use jwt::Session;
54
@@ -22,7 +21,7 @@
2221 State(service): State<GitService>,
2322 session: Session,
2423 Path(path): Path<PolicyPath>,
25) -> Result<(StatusCode, AppResponseData<NoData>), ApiError> {
24) -> Result<AppResponseData<NoData>, ApiError> {
2625 let _ = handler::require_repo_admin(
2726 &service,
2827 &session,
@@ -34,5 +33,5 @@
3433 .delete_branch_policy(path.space_name, path.repo_name, path.policy_id)
3534 .await
3635 .map_err(GitApiError::from)?;
37 Ok((StatusCode::OK, AppResponseData::ok(NoData)))
36 Ok(AppResponseData::ok(NoData))
3837}
Modified lib/git/src/handler/branch_protection/get_policy.rs View lib/git/src/handler/branch_protection/get_policy.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/branch_protection/list_policies.rs View lib/git/src/handler/branch_protection/list_policies.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/branch_protection/path.rs View lib/git/src/handler/branch_protection/path.rs
@@ -1,5 +1,8 @@
1use axum::extract::{FromRequestParts, Path};
2use axum::http::request::Parts;
1use std::future::Future;
2use std::pin::Pin;
3
4use actix_web::dev::Payload;
5use actix_web::{FromRequest, HttpRequest};
36use serde::Deserialize;
47use utoipa::IntoParams;
58use uuid::Uuid;
@@ -13,16 +16,17 @@
1316 pub policy_id: Uuid,
1417}
1518
16impl<S> FromRequestParts<S> for PolicyPath
17where
18 S: Send + Sync,
19{
20 type Rejection = ApiError;
19impl FromRequest for PolicyPath {
20 type Error = ApiError;
21 type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
2122
22 async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
23 Path::<PolicyPath>::from_request_parts(parts, state)
24 .await
25 .map(|Path(p)| p)
26 .map_err(|_| ApiError::bad_request())
23 fn from_request(request: &HttpRequest, _payload: &mut Payload) -> Self::Future {
24 let request = request.clone();
25 Box::pin(async move {
26 actix_web::web::Path::<PolicyPath>::from_request(&request, &mut Payload::None)
27 .await
28 .map(|value| value.into_inner())
29 .map_err(|_| ApiError::bad_request())
30 })
2731 }
2832}
Modified lib/git/src/handler/branch_protection/update_policy.rs View lib/git/src/handler/branch_protection/update_policy.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/branches/create.rs View lib/git/src/handler/branches/create.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/branches/delete.rs View lib/git/src/handler/branches/delete.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/git/src/handler/branches/get.rs View lib/git/src/handler/branches/get.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/branches/list.rs View lib/git/src/handler/branches/list.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/branches/rename.rs View lib/git/src/handler/branches/rename.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::{AppResponseData, NoData};
43use jwt::Session;
54
Modified lib/git/src/handler/branches/set_default.rs View lib/git/src/handler/branches/set_default.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/git/src/handler/code_comments/create_code_comment.rs View lib/git/src/handler/code_comments/create_code_comment.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/code_comments/list_code_comments.rs View lib/git/src/handler/code_comments/list_code_comments.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/code_comments/modify.rs View lib/git/src/handler/code_comments/modify.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::{AppResponseData, NoData};
43use jwt::Session;
54use uuid::Uuid;
Modified lib/git/src/handler/code_comments/resolve_code_comment.rs View lib/git/src/handler/code_comments/resolve_code_comment.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/commits/get.rs View lib/git/src/handler/commits/get.rs
@@ -1,5 +1,5 @@
11use crate::rpcgit::commit::types::CommitInfo;
2use axum::extract::{Path, State};
2use export::http::{Path, State};
33use export::rest::AppResponseData;
44use jwt::Session;
55
Modified lib/git/src/handler/commits/is_ancestor.rs View lib/git/src/handler/commits/is_ancestor.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44use serde::Serialize;
Modified lib/git/src/handler/commits/list.rs View lib/git/src/handler/commits/list.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/commits/list_commit_statuses.rs View lib/git/src/handler/commits/list_commit_statuses.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/commits/rev_parse.rs View lib/git/src/handler/commits/rev_parse.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/commits/set_commit_status.rs View lib/git/src/handler/commits/set_commit_status.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/commits/stats.rs View lib/git/src/handler/commits/stats.rs
@@ -1,7 +1,7 @@
11use std::path::PathBuf;
22
33use crate::rpcgit::commit::types::CommitStats;
4use axum::extract::{Path, Query, State};
4use export::http::{Path, Query, State};
55use export::rest::AppResponseData;
66use jwt::Session;
77
Modified lib/git/src/handler/commits/sync_commit.rs View lib/git/src/handler/commits/sync_commit.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44use serde::Serialize;
Modified lib/git/src/handler/conflicts/detail.rs View lib/git/src/handler/conflicts/detail.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/conflicts/get_conflicts.rs View lib/git/src/handler/conflicts/get_conflicts.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/content/caller_signature.rs View lib/git/src/handler/content/caller_signature.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::State;
1use export::http::{Json, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54use time::OffsetDateTime;
Modified lib/git/src/handler/content/commit_object.rs View lib/git/src/handler/content/commit_object.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/content/write_blob.rs View lib/git/src/handler/content/write_blob.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/content/write_tree_from_files.rs View lib/git/src/handler/content/write_tree_from_files.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/diffs/for_commit.rs View lib/git/src/handler/diffs/for_commit.rs
@@ -1,5 +1,5 @@
11use crate::rpcgit::diff::types::ChangedPath;
2use axum::extract::{Path, State};
2use export::http::{Path, State};
33use export::rest::AppResponseData;
44use jwt::Session;
55
Modified lib/git/src/handler/diffs/get_diff.rs View lib/git/src/handler/diffs/get_diff.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44use serde::Deserialize;
Modified lib/git/src/handler/diffs/range_diff.rs View lib/git/src/handler/diffs/range_diff.rs
@@ -1,5 +1,5 @@
11use crate::rpcgit::diff::types::RangeDiffSpec;
2use axum::extract::{Path, Query, State};
2use export::http::{Path, Query, State};
33use export::rest::AppResponseData;
44use jwt::Session;
55
Modified lib/git/src/handler/diffs/raw.rs View lib/git/src/handler/diffs/raw.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/hooks/execute_script.rs View lib/git/src/handler/hooks/execute_script.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/hooks/post_receive.rs View lib/git/src/handler/hooks/post_receive.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::State;
1use export::http::{Json, State};
32use export::rest::AppResponseData;
43
54use crate::errors::{ApiError, GitApiError};
Modified lib/git/src/handler/hooks/pre_receive.rs View lib/git/src/handler/hooks/pre_receive.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::State;
1use export::http::{Json, State};
32use export::rest::AppResponseData;
43
54use crate::errors::{ApiError, GitApiError};
Modified lib/git/src/handler/html/badge.rs View lib/git/src/handler/html/badge.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Path, State};
2use axum::response::Response;
1use export::http::{Path, Response, State};
32use maud::html;
43
54use crate::handler::html::{HtmlRepoPath, archived_class, format_size, resolve, response_html};
Modified lib/git/src/handler/html/blame.rs View lib/git/src/handler/html/blame.rs
@@ -1,8 +1,7 @@
11use std::path::PathBuf;
22
33use crate::rpcgit::commit::types::TreeEntryMode;
4use axum::extract::{Path, Query, State};
5use axum::response::Response;
4use export::http::{Path, Query, Response, State};
65use maud::html;
76
87use crate::handler::html::{
Modified lib/git/src/handler/html/blob_content.rs View lib/git/src/handler/html/blob_content.rs
@@ -1,6 +1,5 @@
11use crate::rpcgit::commit::types::TreeEntryMode;
2use axum::extract::{Path, Query, State};
3use axum::response::Response;
2use export::http::{Path, Query, Response, State};
43use maud::html;
54
65use crate::handler::html::{
Modified lib/git/src/handler/html/blob_header.rs View lib/git/src/handler/html/blob_header.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Path, Query, State};
2use axum::response::Response;
1use export::http::{Path, Query, Response, State};
32use maud::html;
43
54use crate::handler::html::{HtmlRepoPath, archived_class, resolve_view, response_html};
Modified lib/git/src/handler/html/blob_pages.rs View lib/git/src/handler/html/blob_pages.rs
@@ -1,7 +1,5 @@
1use axum::body::Body;
2use axum::extract::{Path, Query, State};
3use axum::http::{StatusCode, header};
4use axum::response::Response;
1use actix_web::http::header;
2use export::http::{Path, Query, Response, State};
53
64use crate::handler::html::{HtmlRepoPath, error, resolve_view};
75use crate::service::{GitService, pdf};
@@ -72,12 +70,14 @@
7270 };
7371
7472 let stream = futures::stream::unfold(receiver, |mut rx| async move {
75 rx.recv()
76 .await
77 .map(|chunk| (Ok::<Vec<u8>, std::io::Error>(chunk), rx))
73 rx.recv().await.map(|chunk| {
74 (
75 Ok::<actix_web::web::Bytes, std::io::Error>(actix_web::web::Bytes::from(chunk)),
76 rx,
77 )
78 })
7879 });
79 let mut resp = Response::new(Body::from_stream(stream));
80 *resp.status_mut() = StatusCode::OK;
80 let mut resp = actix_web::HttpResponse::Ok().streaming(stream);
8181 let headers = resp.headers_mut();
8282 headers.insert(
8383 header::CONTENT_TYPE,
@@ -87,6 +87,9 @@
8787 headers.insert(header::VARY, "Cookie".parse().unwrap());
8888 // Reverse proxies must hand each frame through as it is produced, otherwise
8989 // the streaming reader degrades into a spinner until the document ends.
90 headers.insert("x-accel-buffering", "no".parse().unwrap());
90 headers.insert(
91 header::HeaderName::from_static("x-accel-buffering"),
92 "no".parse().unwrap(),
93 );
9194 resp
9295}
Modified lib/git/src/handler/html/breadcrumbs.rs View lib/git/src/handler/html/breadcrumbs.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Path, Query, State};
2use axum::response::Response;
1use export::http::{Path, Query, Response, State};
32use maud::html;
43
54use crate::handler::html::{HtmlRepoPath, archived_class, error, resolve, response_html};
Modified lib/git/src/handler/html/clone_buttons.rs View lib/git/src/handler/html/clone_buttons.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Path, State};
2use axum::response::Response;
1use export::http::{Path, Response, State};
32use maud::html;
43
54use crate::handler::html::{HtmlRepoPath, clone_https_url, clone_ssh_url, resolve, response_html};
Modified lib/git/src/handler/html/commit_diff.rs View lib/git/src/handler/html/commit_diff.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Path, Query, State};
2use axum::response::Response;
1use export::http::{Path, Query, Response, State};
32use maud::{Markup, html};
43
54use crate::handler::html::{
Modified lib/git/src/handler/html/commits.rs View lib/git/src/handler/html/commits.rs
@@ -1,7 +1,6 @@
11use std::path::PathBuf;
22
3use axum::extract::{Path, Query, State};
4use axum::response::Response;
3use export::http::{Path, Query, Response, State};
54use maud::html;
65
76use crate::handler::html::{
Modified lib/git/src/handler/html/error.rs View lib/git/src/handler/html/error.rs
@@ -1,5 +1,5 @@
1use axum::http::StatusCode;
2use axum::response::{IntoResponse, Response};
1use actix_web::http::StatusCode;
2use export::http::Response;
33use maud::html;
44
55pub fn html_err(code: u16, msg: &str) -> Response {
@@ -19,20 +19,20 @@
1919 };
2020 let body = markup.into_string();
2121 let status = StatusCode::from_u16(code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
22 let mut resp = (status, body).into_response();
22 let mut resp = actix_web::HttpResponse::build(status).body(body);
2323 resp.headers_mut().insert(
24 axum::http::header::CONTENT_TYPE,
24 actix_web::http::header::CONTENT_TYPE,
2525 "text/html; charset=utf-8".parse().unwrap(),
2626 );
2727 resp.headers_mut().insert(
28 axum::http::header::CACHE_CONTROL,
28 actix_web::http::header::CACHE_CONTROL,
2929 "no-store".parse().unwrap(),
3030 );
3131 resp.headers_mut()
32 .insert(axum::http::header::VARY, "Cookie".parse().unwrap());
32 .insert(actix_web::http::header::VARY, "Cookie".parse().unwrap());
3333 if status == StatusCode::UNAUTHORIZED {
3434 resp.headers_mut().insert(
35 axum::http::header::WWW_AUTHENTICATE,
35 actix_web::http::header::WWW_AUTHENTICATE,
3636 "Basic realm=\"gitdata\"".parse().unwrap(),
3737 );
3838 }
Modified lib/git/src/handler/html/header.rs View lib/git/src/handler/html/header.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Path, State};
2use axum::response::Response;
1use export::http::{Path, Response, State};
32use maud::html;
43
54use crate::handler::html::{HtmlRepoPath, archived_class, resolve, response_html};
Modified lib/git/src/handler/html/languages.rs View lib/git/src/handler/html/languages.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Path, State};
2use axum::response::Response;
1use export::http::{Path, Response, State};
32use maud::html;
43
54use crate::handler::html::{HtmlRepoPath, archived_class, resolve, response_html};
Modified lib/git/src/handler/html/last_commit.rs View lib/git/src/handler/html/last_commit.rs
@@ -1,6 +1,5 @@
11use crate::rpcgit::tree::types::TreeEntryWithLatest;
2use axum::extract::{Path, Query, State};
3use axum::response::Response;
2use export::http::{Path, Query, Response, State};
43use maud::{Markup, html};
54
65use crate::handler::html::{
Modified lib/git/src/handler/html/mod.rs View lib/git/src/handler/html/mod.rs
@@ -1,7 +1,6 @@
11use std::sync::Arc;
22
3use axum::Router;
4use axum::response::{IntoResponse, Response};
3use export::http::{Response, Router};
54use maud::Markup;
65use time::OffsetDateTime;
76use time::format_description::well_known::Rfc3339;
@@ -58,62 +57,68 @@
5857 Router::new()
5958 .route(
6059 "/clone-buttons",
61 axum::routing::get(clone_buttons::render_clone_buttons),
60 export::http::routing::get(clone_buttons::render_clone_buttons),
6261 )
63 .route("/badge", axum::routing::get(badge::render_badge))
62 .route("/badge", export::http::routing::get(badge::render_badge))
6463 .route(
6564 "/quickstart",
66 axum::routing::get(quickstart::render_quickstart),
65 export::http::routing::get(quickstart::render_quickstart),
6766 )
68 .route("/header", axum::routing::get(header::render_header))
67 .route("/header", export::http::routing::get(header::render_header))
6968 .route(
7069 "/last-commit",
71 axum::routing::get(last_commit::render_last_commit),
70 export::http::routing::get(last_commit::render_last_commit),
7271 )
7372 .route(
7473 "/refs-summary",
75 axum::routing::get(refs_summary::render_refs_summary),
74 export::http::routing::get(refs_summary::render_refs_summary),
7675 )
7776 .route(
7877 "/refs-dropdown",
79 axum::routing::get(refs_dropdown::render_refs_dropdown),
78 export::http::routing::get(refs_dropdown::render_refs_dropdown),
8079 )
81 .route("/tree", axum::routing::get(tree::render_tree))
82 .route("/tree-row", axum::routing::get(tree_row::render_tree_row))
80 .route("/tree", export::http::routing::get(tree::render_tree))
8381 .route(
82 "/tree-row",
83 export::http::routing::get(tree_row::render_tree_row),
84 )
85 .route(
8486 "/breadcrumbs",
85 axum::routing::get(breadcrumbs::render_breadcrumbs),
87 export::http::routing::get(breadcrumbs::render_breadcrumbs),
8688 )
8789 .route(
8890 "/blob-header",
89 axum::routing::get(blob_header::render_blob_header),
91 export::http::routing::get(blob_header::render_blob_header),
9092 )
9193 .route(
9294 "/blob-content",
93 axum::routing::get(blob_content::render_blob_content),
95 export::http::routing::get(blob_content::render_blob_content),
9496 )
9597 .route(
9698 "/blob-pages",
97 axum::routing::get(blob_pages::render_blob_pages),
99 export::http::routing::get(blob_pages::render_blob_pages),
98100 )
99 .route("/readme", axum::routing::get(readme::render_readme))
100 .route("/commits", axum::routing::get(commits::render_commits))
101 .route("/readme", export::http::routing::get(readme::render_readme))
101102 .route(
103 "/commits",
104 export::http::routing::get(commits::render_commits),
105 )
106 .route(
102107 "/commit-diff",
103 axum::routing::get(commit_diff::render_commit_diff),
108 export::http::routing::get(commit_diff::render_commit_diff),
104109 )
105 .route("/blame", axum::routing::get(blame::render_blame))
110 .route("/blame", export::http::routing::get(blame::render_blame))
106111 .route(
107112 "/languages",
108 axum::routing::get(languages::render_languages),
113 export::http::routing::get(languages::render_languages),
109114 )
110115 .route(
111116 "/star-button",
112 axum::routing::get(star_button::render_star_button),
117 export::http::routing::get(star_button::render_star_button),
113118 )
114119 .route(
115120 "/watch-button",
116 axum::routing::get(watch_button::render_watch_button),
121 export::http::routing::get(watch_button::render_watch_button),
117122 )
118123}
119124
@@ -326,24 +331,24 @@
326331
327332pub fn response_html(markup: Markup, max_age: u32) -> Response {
328333 let body = markup.into_string();
329 let mut resp = (axum::http::StatusCode::OK, body).into_response();
334 let mut resp = actix_web::HttpResponse::Ok().body(body);
330335 resp.headers_mut().insert(
331 axum::http::header::CONTENT_TYPE,
336 actix_web::http::header::CONTENT_TYPE,
332337 "text/html; charset=utf-8".parse().unwrap(),
333338 );
334339 if max_age == 0 {
335340 resp.headers_mut().insert(
336 axum::http::header::CACHE_CONTROL,
341 actix_web::http::header::CACHE_CONTROL,
337342 "no-store".parse().unwrap(),
338343 );
339344 } else {
340345 resp.headers_mut().insert(
341 axum::http::header::CACHE_CONTROL,
346 actix_web::http::header::CACHE_CONTROL,
342347 format!("private, max-age={max_age}").parse().unwrap(),
343348 );
344349 }
345350 resp.headers_mut()
346 .insert(axum::http::header::VARY, "Cookie".parse().unwrap());
351 .insert(actix_web::http::header::VARY, "Cookie".parse().unwrap());
347352 resp
348353}
349354
Modified lib/git/src/handler/html/quickstart.rs View lib/git/src/handler/html/quickstart.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Path, State};
2use axum::response::Response;
1use export::http::{Path, Response, State};
32use maud::html;
43
54use crate::handler::html::{HtmlRepoPath, clone_https_url, clone_ssh_url, resolve, response_html};
Modified lib/git/src/handler/html/readme.rs View lib/git/src/handler/html/readme.rs
@@ -1,6 +1,5 @@
11use crate::rpcgit::commit::types::TreeEntryMode;
2use axum::extract::{Path, Query, State};
3use axum::response::Response;
2use export::http::{Path, Query, Response, State};
43use maud::{PreEscaped, html};
54
65use crate::handler::html::{
Modified lib/git/src/handler/html/refs_dropdown.rs View lib/git/src/handler/html/refs_dropdown.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Path, Query, State};
2use axum::response::Response;
1use export::http::{Path, Query, Response, State};
32use maud::html;
43
54use crate::handler::html::{HtmlRepoPath, archived_class, resolve, response_html};
Modified lib/git/src/handler/html/refs_summary.rs View lib/git/src/handler/html/refs_summary.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Path, State};
2use axum::response::Response;
1use export::http::{Path, Response, State};
32use maud::html;
43
54use crate::handler::html::{HtmlRepoPath, archived_class, resolve, response_html};
Modified lib/git/src/handler/html/star_button.rs View lib/git/src/handler/html/star_button.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Path, State};
2use axum::response::Response;
1use export::http::{Path, Response, State};
32use maud::html;
43
54use crate::handler::html::{HtmlRepoPath, archived_class, resolve, response_html};
Modified lib/git/src/handler/html/tree.rs View lib/git/src/handler/html/tree.rs
@@ -1,7 +1,6 @@
11use crate::rpcgit::commit::types::TreeEntryMode;
22use crate::rpcgit::tree::types::TreeEntryWithLatest;
3use axum::extract::{Path, Query, State};
4use axum::response::Response;
3use export::http::{Path, Query, Response, State};
54use maud::{Markup, html};
65
76use crate::handler::html::{
Modified lib/git/src/handler/html/tree_row.rs View lib/git/src/handler/html/tree_row.rs
@@ -1,6 +1,5 @@
11use crate::rpcgit::commit::types::TreeEntryMode;
2use axum::extract::{Path, Query, State};
3use axum::response::Response;
2use export::http::{Path, Query, Response, State};
43use maud::html;
54
65use crate::handler::html::{
Modified lib/git/src/handler/html/watch_button.rs View lib/git/src/handler/html/watch_button.rs
@@ -1,5 +1,4 @@
1use axum::extract::{Path, State};
2use axum::response::Response;
1use export::http::{Path, Response, State};
32use maud::html;
43use models::common::watch_mode::WatchMode;
54
Modified lib/git/src/handler/lfs/cleanup_unreferenced.rs View lib/git/src/handler/lfs/cleanup_unreferenced.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/lfs/list_locks.rs View lib/git/src/handler/lfs/list_locks.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/lfs/lock.rs View lib/git/src/handler/lfs/lock.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/lfs/unlock.rs View lib/git/src/handler/lfs/unlock.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/git/src/handler/lfs/unlock_by_id.rs View lib/git/src/handler/lfs/unlock_by_id.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/git/src/handler/lfs/verify_locks.rs View lib/git/src/handler/lfs/verify_locks.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/maintenance/fsck.rs View lib/git/src/handler/maintenance/fsck.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/maintenance/prune.rs View lib/git/src/handler/maintenance/prune.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/packs/ingest.rs View lib/git/src/handler/packs/ingest.rs
@@ -1,5 +1,4 @@
1use axum::body::Bytes;
2use axum::extract::{Path, State};
1use export::http::{Bytes, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/packs/repack.rs View lib/git/src/handler/packs/repack.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/git/src/handler/packs/stats.rs View lib/git/src/handler/packs/stats.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/patches/apply.rs View lib/git/src/handler/patches/apply.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/patches/generate.rs View lib/git/src/handler/patches/generate.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/patches/parse.rs View lib/git/src/handler/patches/parse.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::State;
1use export::http::{Json, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/refs/branches.rs View lib/git/src/handler/refs/branches.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/refs/delete.rs View lib/git/src/handler/refs/delete.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/git/src/handler/refs/filtered.rs View lib/git/src/handler/refs/filtered.rs
@@ -1,5 +1,5 @@
11use crate::rpcgit::refs::types::RefInfo;
2use axum::extract::{Path, Query, State};
2use export::http::{Path, Query, State};
33use export::rest::AppResponseData;
44use jwt::Session;
55
Modified lib/git/src/handler/refs/get.rs View lib/git/src/handler/refs/get.rs
@@ -1,5 +1,5 @@
11use crate::rpcgit::refs::types::RefInfo;
2use axum::extract::{Path, State};
2use export::http::{Path, State};
33use export::rest::AppResponseData;
44use jwt::Session;
55
Modified lib/git/src/handler/refs/head.rs View lib/git/src/handler/refs/head.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/refs/is_object_in_ref.rs View lib/git/src/handler/refs/is_object_in_ref.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44use serde::Serialize;
Modified lib/git/src/handler/refs/list.rs View lib/git/src/handler/refs/list.rs
@@ -1,5 +1,5 @@
11use crate::rpcgit::refs::types::RefInfo;
2use axum::extract::{Path, State};
2use export::http::{Path, State};
33use export::rest::AppResponseData;
44use jwt::Session;
55
Modified lib/git/src/handler/refs/resolve.rs View lib/git/src/handler/refs/resolve.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/refs/update.rs View lib/git/src/handler/refs/update.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54use serde::{Deserialize, Serialize};
Modified lib/git/src/handler/releases/create_release.rs View lib/git/src/handler/releases/create_release.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/releases/delete_release.rs View lib/git/src/handler/releases/delete_release.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/git/src/handler/releases/get_release.rs View lib/git/src/handler/releases/get_release.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/releases/list_releases.rs View lib/git/src/handler/releases/list_releases.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/releases/update_release.rs View lib/git/src/handler/releases/update_release.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/remotes/add.rs View lib/git/src/handler/remotes/add.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::{AppResponseData, NoData};
43use jwt::Session;
54
Modified lib/git/src/handler/remotes/clone_import.rs View lib/git/src/handler/remotes/clone_import.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/remotes/fetch.rs View lib/git/src/handler/remotes/fetch.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/remotes/list.rs View lib/git/src/handler/remotes/list.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/remotes/push.rs View lib/git/src/handler/remotes/push.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/remotes/remove.rs View lib/git/src/handler/remotes/remove.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/git/src/handler/repos/archive.rs View lib/git/src/handler/repos/archive.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/repos/create.rs View lib/git/src/handler/repos/create.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54
Modified lib/git/src/handler/blame.rs View lib/git/src/handler/blame.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/licenses.rs View lib/git/src/handler/licenses.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, Query, State};
1use export::http::{Json, Path, Query, State};
32use export::rest::AppResponseData;
43use serde::Deserialize;
54use utoipa::IntoParams;
Modified lib/git/src/handler/path_log.rs View lib/git/src/handler/path_log.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/git/src/handler/share.rs View lib/git/src/handler/share.rs
@@ -1,5 +1,8 @@
1use axum::extract::{FromRequestParts, Path};
2use axum::http::request::Parts;
1use std::future::Future;
2use std::pin::Pin;
3
4use actix_web::dev::Payload;
5use actix_web::{FromRequest, HttpRequest};
36use jwt::Session;
47use serde::Deserialize;
58use utoipa::IntoParams;
@@ -21,17 +24,18 @@
2124 pub revision: String,
2225}
2326
24impl<S> FromRequestParts<S> for RepoPath
25where
26 S: Send + Sync,
27{
28 type Rejection = ApiError;
27impl FromRequest for RepoPath {
28 type Error = ApiError;
29 type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
2930
30 async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
31 Path::<RepoPath>::from_request_parts(parts, state)
32 .await
33 .map(|Path(p)| p)
34 .map_err(|_| ApiError::bad_request())
31 fn from_request(request: &HttpRequest, _payload: &mut Payload) -> Self::Future {
32 let request = request.clone();
33 Box::pin(async move {
34 actix_web::web::Path::<RepoPath>::from_request(&request, &mut Payload::None)
35 .await
36 .map(|value| value.into_inner())
37 .map_err(|_| ApiError::bad_request())
38 })
3539 }
3640}
3741
Modified lib/git/src/handler/stars.rs View lib/git/src/handler/stars.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44use serde::Deserialize;
Modified lib/git/src/handler/topics.rs View lib/git/src/handler/topics.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, Query, State};
1use export::http::{Json, Path, Query, State};
32use export::rest::{AppResponseData, NoData};
43use jwt::Session;
54use serde::{Deserialize, Serialize};
Modified lib/git/src/handler/watches.rs View lib/git/src/handler/watches.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, Query, State};
1use export::http::{Json, Path, Query, State};
32use export::rest::AppResponseData;
43use jwt::Session;
54use models::common::watch_mode::WatchMode;
Modified lib/git/src/handler/whitelists.rs View lib/git/src/handler/whitelists.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::{AppResponseData, NoData};
43use serde::Deserialize;
54use utoipa::IntoParams;
Modified lib/git/src/share/access.rs View lib/git/src/share/access.rs
@@ -1,5 +1,8 @@
1use axum::extract::FromRequestParts;
2use axum::http::header;
1use std::future::Ready;
2
3use actix_web::dev::Payload;
4use actix_web::http::header;
5use actix_web::{FromRequest, HttpRequest};
36use base64::Engine as _;
47use models::repo::repo_collaborations::{self, CollaborationMode};
58use models::users::access_scope::AccessKeyScope;
@@ -15,32 +18,34 @@
1518 pub token: Option<String>,
1619}
1720
18impl<S: Send + Sync> FromRequestParts<S> for BasicAuth {
19 type Rejection = GitError;
21impl FromRequest for BasicAuth {
22 type Error = GitError;
23 type Future = Ready<Result<Self, Self::Error>>;
2024
21 async fn from_request_parts(
22 parts: &mut axum::http::request::Parts,
23 _state: &S,
24 ) -> Result<Self, Self::Rejection> {
25 let Some(value) = parts.headers.get(header::AUTHORIZATION) else {
26 return Ok(BasicAuth { token: None });
27 };
28 let raw = value
29 .to_str()
30 .map_err(|_| GitError::InvalidArgument("malformed authorization header".into()))?;
31 let Some(b64) = raw.strip_prefix("Basic ") else {
32 return Ok(BasicAuth { token: None });
33 };
34 let decoded = base64::engine::general_purpose::STANDARD
35 .decode(b64)
36 .map_err(|_| GitError::InvalidArgument("malformed basic credentials".into()))?;
37 let creds = String::from_utf8(decoded)
38 .map_err(|_| GitError::InvalidArgument("malformed basic credentials".into()))?;
39 let token = creds.split_once(':').map(|(_, pass)| pass.to_string());
40 Ok(BasicAuth { token })
25 fn from_request(request: &HttpRequest, _payload: &mut Payload) -> Self::Future {
26 std::future::ready(parse_basic_auth(request))
4127 }
4228}
4329
30fn parse_basic_auth(request: &HttpRequest) -> Result<BasicAuth, GitError> {
31 let Some(value) = request.headers().get(header::AUTHORIZATION) else {
32 return Ok(BasicAuth { token: None });
33 };
34 let raw = value
35 .to_str()
36 .map_err(|_| GitError::InvalidArgument("malformed authorization header".into()))?;
37 let Some(b64) = raw.strip_prefix("Basic ") else {
38 return Ok(BasicAuth { token: None });
39 };
40 let decoded = base64::engine::general_purpose::STANDARD
41 .decode(b64)
42 .map_err(|_| GitError::InvalidArgument("malformed basic credentials".into()))?;
43 let creds = String::from_utf8(decoded)
44 .map_err(|_| GitError::InvalidArgument("malformed basic credentials".into()))?;
45 let token = creds.split_once(':').map(|(_, pass)| pass.to_string());
46 Ok(BasicAuth { token })
47}
48
4449pub async fn verify_access_key(
4550 svc: &GitService,
4651 token: &str,
@@ -71,7 +76,7 @@
7176
7277/// HTTP write-path auth: token must be valid AND the resolved `uid` must
7378/// have write membership on the resolved `ctx` repo. Use everywhere the
74/// HTTP/axum path accepts mutating requests.
79/// HTTP path accepts mutating requests.
7580pub async fn require_writer(
7681 svc: &GitService,
7782 auth: &BasicAuth,
Modified lib/git/src/share/errors.rs View lib/git/src/share/errors.rs
@@ -80,10 +80,10 @@
8080 }
8181}
8282
83impl axum::response::IntoResponse for GitError {
84 fn into_response(self) -> axum::response::Response {
85 use axum::http::StatusCode;
86 let status = match &self {
83impl GitError {
84 fn status(&self) -> actix_web::http::StatusCode {
85 use actix_web::http::StatusCode;
86 match self {
8787 Self::NotFound => StatusCode::NOT_FOUND,
8888 Self::Unauthorized => StatusCode::UNAUTHORIZED,
8989 Self::Forbidden => StatusCode::FORBIDDEN,
@@ -91,8 +91,18 @@
9191 Self::InvalidArgument(_) => StatusCode::BAD_REQUEST,
9292 Self::Unimplemented(_) => StatusCode::NOT_IMPLEMENTED,
9393 Self::Cache(_) | Self::Git(_) | Self::Internal(_) => StatusCode::INTERNAL_SERVER_ERROR,
94 };
95 let body = match &self {
94 }
95 }
96}
97
98impl actix_web::ResponseError for GitError {
99 fn status_code(&self) -> actix_web::http::StatusCode {
100 self.status()
101 }
102
103 fn error_response(&self) -> actix_web::HttpResponse {
104 let status = self.status();
105 let body = match self {
96106 Self::Cache(_) | Self::Git(_) | Self::Internal(_) => {
97107 tracing::warn!(error = %self, "git internal error");
98108 "internal server error".to_string()
@@ -100,11 +110,11 @@
100110 Self::Unimplemented(_) => self.to_string(),
101111 other => other.to_string(),
102112 };
103 let mut resp = (status, body).into_response();
104 if status == StatusCode::UNAUTHORIZED {
113 let mut resp = actix_web::HttpResponse::build(status).body(body);
114 if status == actix_web::http::StatusCode::UNAUTHORIZED {
105115 resp.headers_mut().insert(
106 axum::http::header::WWW_AUTHENTICATE,
107 axum::http::HeaderValue::from_static("Basic realm=\"gitdata\""),
116 actix_web::http::header::WWW_AUTHENTICATE,
117 actix_web::http::header::HeaderValue::from_static("Basic realm=\"gitdata\""),
108118 );
109119 }
110120 resp
Modified lib/git/src/errors.rs View lib/git/src/errors.rs
@@ -1,9 +1,6 @@
11use std::fmt;
22
3use axum::Json;
4use axum::extract::rejection::{JsonRejection, PathRejection, QueryRejection};
5use axum::http::StatusCode;
6use axum::response::{IntoResponse, Response};
3use actix_web::http::StatusCode;
74use export::rest::{AppResponseData, NoData};
85use utoipa::IntoResponses;
96
@@ -94,17 +91,6 @@
9491 fn payload(code: i32, message: &'static str) -> Box<AppResponseData<NoData>> {
9592 Box::new(AppResponseData::err(code, message))
9693 }
97
98 fn decompose(self) -> (u16, AppResponseData<NoData>) {
99 match self {
100 Self::BadRequest(envelope) => (400, *envelope),
101 Self::Unauthorized(envelope) => (401, *envelope),
102 Self::Forbidden(envelope) => (403, *envelope),
103 Self::NotFound(envelope) => (404, *envelope),
104 Self::Conflict(envelope) => (409, *envelope),
105 Self::Internal(envelope) => (500, *envelope),
106 }
107 }
10894}
10995
11096impl ApiError {
@@ -133,11 +119,35 @@
133119 }
134120}
135121
136impl IntoResponse for ApiError {
137 fn into_response(self) -> Response {
138 let (code, envelope) = self.decompose();
139 let status = StatusCode::from_u16(code).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
140 (status, Json(envelope)).into_response()
122impl std::fmt::Display for ApiError {
123 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
124 let (_, envelope) = self.parts();
125 formatter.write_str(&envelope.message)
126 }
127}
128
129impl ApiError {
130 fn parts(&self) -> (u16, &AppResponseData<NoData>) {
131 match self {
132 Self::BadRequest(envelope) => (400, envelope),
133 Self::Unauthorized(envelope) => (401, envelope),
134 Self::Forbidden(envelope) => (403, envelope),
135 Self::NotFound(envelope) => (404, envelope),
136 Self::Conflict(envelope) => (409, envelope),
137 Self::Internal(envelope) => (500, envelope),
138 }
139 }
140}
141
142impl actix_web::ResponseError for ApiError {
143 fn status_code(&self) -> StatusCode {
144 StatusCode::from_u16(self.parts().0).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR)
145 }
146
147 fn error_response(&self) -> actix_web::HttpResponse {
148 let (status, envelope) = self.parts();
149 let status = StatusCode::from_u16(status).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
150 actix_web::HttpResponse::build(status).json(envelope)
141151 }
142152}
143153
@@ -156,31 +166,5 @@
156166 Self::Internal(Self::payload(500, "internal server error"))
157167 }
158168 }
159 }
160}
161
162impl From<PathRejection> for ApiError {
163 fn from(error: PathRejection) -> Self {
164 Self::BadRequest(Box::new(
165 AppResponseData::err(400, "invalid request path").with_error(error.body_text()),
166 ))
167 }
168}
169
170impl From<JsonRejection> for ApiError {
171 fn from(error: JsonRejection) -> Self {
172 Self::BadRequest(Box::new(
173 AppResponseData::err(400, "invalid request body")
174 .with_error(error.body_text())
175 .with_error_code("invalid_request_body"),
176 ))
177 }
178}
179
180impl From<QueryRejection> for ApiError {
181 fn from(error: QueryRejection) -> Self {
182 Self::BadRequest(Box::new(
183 AppResponseData::err(400, "invalid query parameters").with_error(error.body_text()),
184 ))
185169 }
186170}
Modified lib/git/src/routes.rs View lib/git/src/routes.rs
@@ -101,8 +101,8 @@
101101 remove_branch_whitelist, remove_tag_whitelist,
102102};
103103use crate::service::GitService;
104use axum::Router;
105use axum::routing::{delete, get, post, put};
104use export::http::Router;
105use export::http::routing::{delete, get, post, put};
106106
107107pub fn router(_service: GitService) -> Router<GitService> {
108108 Router::new()
@@ -171,7 +171,7 @@
171171 )
172172 .route(
173173 "/{space_name}/{repo_name}/code-comments/{comment_id}",
174 axum::routing::patch(update_code_comment).delete(delete_code_comment),
174 export::http::routing::patch(update_code_comment).delete(delete_code_comment),
175175 )
176176 .route(
177177 "/{space_name}/{repo_name}/code-comments/{comment_id}/resolve",
Modified lib/git/Cargo.toml View lib/git/Cargo.toml
@@ -23,9 +23,8 @@
2323globset = { workspace = true }
2424base64 = { workspace = true }
2525sha2 = { workspace = true }
26axum = { workspace = true }
26actix-web = { workspace = true }
2727futures = { workspace = true }
28http-body-util = { workspace = true }
2928tokio-util = { workspace = true }
3029redis = { workspace = true }
3130tracing = { workspace = true }
Modified lib/identity/src/client/local.rs View lib/identity/src/client/local.rs
@@ -3,8 +3,8 @@
33use tonic::{Request, Response, Status};
44use uuid::Uuid;
55
6use crate::auth::grpc::identity::IdentityApi;
67use crate::protos::auth::v1::identity_service_server::IdentityService;
7use crate::auth::grpc::identity::IdentityApi;
88
99pub struct IdentityClient {
1010 api: IdentityApi,
@@ -88,8 +88,8 @@
8888 }
8989}
9090
91use crate::auth::grpc::authority::AuthorityApi;
9192use crate::protos::auth::internal::v1::authority_service_server::AuthorityService;
92use crate::auth::grpc::authority::AuthorityApi;
9393
9494pub struct AuthorityClient {
9595 api: AuthorityApi,
@@ -104,7 +104,8 @@
104104 pub async fn redeem_socket_ticket(
105105 &mut self,
106106 mut request: Request<crate::protos::auth::internal::v1::RedeemSocketTicketRequest>,
107 ) -> Result<Response<crate::protos::auth::internal::v1::RedeemSocketTicketResponse>, Status> {
107 ) -> Result<Response<crate::protos::auth::internal::v1::RedeemSocketTicketResponse>, Status>
108 {
108109 request.extensions_mut().insert(RequestMeta {
109110 request_id: Uuid::now_v7().to_string(),
110111 caller_service: "kenapi".to_owned(),
@@ -174,7 +175,8 @@
174175 pub async fn cancel_account_deletion(
175176 &mut self,
176177 mut request: Request<crate::protos::auth::internal::v1::CancelAccountDeletionRequest>,
177 ) -> Result<Response<crate::protos::auth::internal::v1::CancelAccountDeletionResponse>, Status> {
178 ) -> Result<Response<crate::protos::auth::internal::v1::CancelAccountDeletionResponse>, Status>
179 {
178180 request.extensions_mut().insert(RequestMeta {
179181 request_id: Uuid::now_v7().to_string(),
180182 caller_service: "kenapi".to_owned(),
@@ -203,8 +205,8 @@
203205 }
204206}
205207
206use crate::protos::auth::internal::v1::email_service_server::EmailService;
207208use crate::auth::grpc::email::EmailApi;
209use crate::protos::auth::internal::v1::email_service_server::EmailService;
208210
209211pub struct EmailClient {
210212 api: EmailApi,
@@ -258,8 +260,8 @@
258260 }
259261}
260262
261use crate::protos::auth::v1::session_service_server::SessionService;
262263use crate::auth::grpc::session::SessionApi;
264use crate::protos::auth::v1::session_service_server::SessionService;
263265
264266pub struct SessionClient {
265267 api: SessionApi,
@@ -313,8 +315,8 @@
313315 }
314316}
315317
318use crate::auth::grpc::organization::OrganizationApi;
316319use crate::protos::auth::v1::organization_service_server::OrganizationService;
317use crate::auth::grpc::organization::OrganizationApi;
318320
319321pub struct OrganizationClient {
320322 api: OrganizationApi,
@@ -818,8 +820,8 @@
818820 }
819821}
820822
823use crate::auth::grpc::token::TokenApi;
821824use crate::protos::auth::v1::token_service_server::TokenService;
822use crate::auth::grpc::token::TokenApi;
823825
824826pub struct TokenClient {
825827 api: TokenApi,
@@ -863,8 +865,8 @@
863865 }
864866}
865867
868use crate::auth::grpc::authorization::AuthorizationApi;
866869use crate::protos::auth::v1::authorization_service_server::AuthorizationService;
867use crate::auth::grpc::authorization::AuthorizationApi;
868870
869871pub struct AuthorizationClient {
870872 api: AuthorizationApi,
@@ -918,8 +920,8 @@
918920 }
919921}
920922
923use crate::auth::grpc::access_key::AccessKeyApi;
921924use crate::protos::auth::v1::access_key_service_server::AccessKeyService;
922use crate::auth::grpc::access_key::AccessKeyApi;
923925
924926pub struct AccessKeyClient {
925927 api: AccessKeyApi,
@@ -983,8 +985,8 @@
983985 }
984986}
985987
988use crate::auth::grpc::ssh_key::SshKeyApi;
986989use crate::protos::auth::v1::ssh_key_service_server::SshKeyService;
987use crate::auth::grpc::ssh_key::SshKeyApi;
988990
989991pub struct SshKeyClient {
990992 api: SshKeyApi,
@@ -1038,8 +1040,8 @@
10381040 }
10391041}
10401042
1041use crate::protos::auth::v1::mfa_service_server::MfaService;
10421043use crate::auth::grpc::mfa::MfaApi;
1044use crate::protos::auth::v1::mfa_service_server::MfaService;
10431045
10441046pub struct MfaClient {
10451047 api: MfaApi,
@@ -1123,8 +1125,8 @@
11231125 }
11241126}
11251127
1126use crate::protos::auth::v1::agreement_service_server::AgreementService;
11271128use crate::auth::grpc::agreement::AgreementApi;
1129use crate::protos::auth::v1::agreement_service_server::AgreementService;
11281130
11291131pub struct AgreementClient {
11301132 api: AgreementApi,
Modified lib/identity/src/lib.rs View lib/identity/src/lib.rs
@@ -19,8 +19,7 @@
1919pub use envelope;
2020pub use events;
2121pub use kvcache;
22pub use oltp;
23pub use oltp::shutdown;
22pub use oltp::{self, shutdown};
2423pub use proto as protos;
2524pub use queue::identity as queue;
2625pub use session;
Modified lib/issues/src/services/attachments.rs View lib/issues/src/services/attachments.rs
@@ -5,8 +5,9 @@
55//! upload before the issue exists; submitting the issue links it. Only the
66//! uploader may finalize or delete, and reading a linked file follows the
77//! issue's own visibility rules.
8use axum::body::Body;
9use axum::http::header;
8use actix_web::http::header;
9use actix_web::web::Bytes;
10use futures::stream::BoxStream;
1011use sea_orm::{
1112 ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, PaginatorTrait, QueryFilter,
1213 QueryOrder, QuerySelect, Set,
@@ -182,7 +183,7 @@
182183 .presign_ttl_secs
183184 .min(604_800);
184185 // The storage client signs with the `http` 0.2 header map (see chat's
185 // attachment pipeline); axum's own re-export is the 1.x line.
186 // attachment pipeline); the workspace `http` 1.x line is separate.
186187 let mut put_headers = http::HeaderMap::new();
187188 if let Ok(value) = cmd.content_type.parse() {
188189 put_headers.insert(http::header::CONTENT_TYPE, value);
@@ -305,7 +306,14 @@
305306 &self,
306307 caller: i64,
307308 attachment_id: Uuid,
308 ) -> Result<(issue_attachment::Model, Body, String), IssueError> {
309 ) -> Result<
310 (
311 issue_attachment::Model,
312 BoxStream<'static, Result<Bytes, std::io::Error>>,
313 String,
314 ),
315 IssueError,
316 > {
309317 let row = issue_attachment::Entity::find_by_id(attachment_id)
310318 .one(self.db().read())
311319 .await?
@@ -325,7 +333,10 @@
325333 .get_stream(&row.s3_key)
326334 .await
327335 .map_err(storage_failure)?;
328 let body = Body::from_stream(stream);
336 let body = Box::pin(futures::StreamExt::map(stream, |item| {
337 item.map(Bytes::from)
338 .map_err(|e| std::io::Error::other(e.to_string()))
339 })) as BoxStream<'static, Result<Bytes, std::io::Error>>;
329340 let content_type = if is_unsafe_inline(&row.content_type) {
330341 "application/octet-stream".to_string()
331342 } else {
Modified lib/issues/src/share/guard.rs View lib/issues/src/share/guard.rs
@@ -1,16 +1 @@
1use axum::extract::{Request, State};
2use axum::middleware::Next;
3use axum::response::Response;
4use jwt::Session;
5use jwt::share::guard::session_guard;
6
7use crate::services::service::IssueService;
8
9pub async fn issue_guard(
10 State(service): State<IssueService>,
11 session: Session,
12 request: Request,
13 next: Next,
14) -> Response {
15 session_guard::<IssueService>(State(service), session, request, next).await
16}
1pub use jwt::share::guard::session_guard as issue_guard;
Modified lib/issues/src/routes.rs View lib/issues/src/routes.rs
@@ -1,5 +1,5 @@
1use axum::Router;
2use axum::routing::{delete, get, post, put};
1use export::http::Router;
2use export::http::routing::{delete, get, post, put};
33
44use crate::handlers::attachments::create_attachment::create_issue_attachment;
55use crate::handlers::attachments::delete_attachment::delete_issue_attachment;
@@ -64,9 +64,8 @@
6464use crate::handlers::tracked_time::list_tracked_time::list_tracked_time;
6565use crate::handlers::watches::upsert_watch::upsert_issue_watch;
6666use crate::services::service::IssueService;
67use crate::share::guard::issue_guard;
6867
69pub fn router(service: IssueService) -> Router<IssueService> {
68pub fn router(_service: IssueService) -> Router<IssueService> {
7069 Router::new()
7170 .route(
7271 "/issues/attachments",
@@ -159,7 +158,7 @@
159158 )
160159 .route(
161160 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/reactions/@me",
162 axum::routing::put(add_issue_reaction).delete(remove_issue_reaction),
161 export::http::routing::put(add_issue_reaction).delete(remove_issue_reaction),
163162 )
164163 .route(
165164 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/comments/{comment_id}/reactions",
@@ -167,7 +166,7 @@
167166 )
168167 .route(
169168 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/comments/{comment_id}/reactions/@me",
170 axum::routing::put(add_comment_reaction).delete(remove_comment_reaction),
169 export::http::routing::put(add_comment_reaction).delete(remove_comment_reaction),
171170 )
172171 .route(
173172 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/watch",
@@ -228,7 +227,7 @@
228227 )
229228 .route(
230229 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/times/{time_id}",
231 axum::routing::patch(
230 export::http::routing::patch(
232231 crate::handlers::tracked_time::update_tracked_time::update_tracked_time,
233232 )
234233 .delete(crate::handlers::tracked_time::delete_tracked_time::delete_tracked_time),
@@ -249,8 +248,5 @@
249248 "/issues/{space_name}/boards/{board_id}/issues/{issue_id}/repos/{rel_id}",
250249 delete(delete_repo_relation),
251250 )
252 .route_layer(axum::middleware::from_fn_with_state(
253 service.clone(),
254 issue_guard,
255 ))
251 .wrap(export::http::Guard::Session)
256252}
Modified lib/issues/Cargo.toml View lib/issues/Cargo.toml
@@ -4,6 +4,8 @@
44edition.workspace = true
55
66[dependencies]
7bytes = { workspace = true }
8futures = { workspace = true }
79async-trait = { workspace = true }
810config = { workspace = true }
911export = { workspace = true }
@@ -13,7 +15,7 @@
1315jwt = { workspace = true }
1416kenauth = { workspace = true }
1517space = { workspace = true }
16axum = { workspace = true }
18actix-web = { workspace = true }
1719http = { workspace = true }
1820sea-orm = { workspace = true }
1921serde = { workspace = true, features = ["derive"] }
Modified lib/jwt/src/error.rs View lib/jwt/src/error.rs
@@ -1,6 +1,5 @@
1use axum::Json;
2use axum::http::StatusCode;
3use axum::response::{IntoResponse, Response};
1use actix_web::http::StatusCode;
2use actix_web::{HttpResponse, ResponseError};
43use serde_json::json;
54
65#[derive(Debug, Clone)]
@@ -32,21 +31,21 @@
3231 }
3332}
3433
35impl IntoResponse for SessionError {
36 fn into_response(self) -> Response {
34impl ResponseError for SessionError {
35 fn status_code(&self) -> StatusCode {
36 StatusCode::UNAUTHORIZED
37 }
38
39 fn error_response(&self) -> HttpResponse {
3740 let message = self.code();
38 (
39 StatusCode::UNAUTHORIZED,
40 Json(json!({
41 "code": 401,
42 "message": message,
43 "data": null,
44 "error": null,
45 "error_code": message,
46 "request_id": null,
47 "log_id": null,
48 })),
49 )
50 .into_response()
41 HttpResponse::Unauthorized().json(json!({
42 "code": 401,
43 "message": message,
44 "data": null,
45 "error": null,
46 "error_code": message,
47 "request_id": null,
48 "log_id": null,
49 }))
5150 }
5251}
Modified lib/jwt/src/extract.rs View lib/jwt/src/extract.rs
@@ -1,11 +1,13 @@
1use std::convert::Infallible;
2use std::future::Future;
3use std::pin::Pin;
14use std::sync::Arc;
25
3use axum::extract::FromRequestParts;
4use axum::http::header::AUTHORIZATION;
5use axum::http::request::Parts;
6use actix_web::dev::Payload;
7use actix_web::http::header::AUTHORIZATION;
8use actix_web::{FromRequest, HttpMessage, HttpRequest, web};
69
710use crate::claims::AccessClaims;
8use crate::cookie;
911use crate::error::SessionError;
1012use crate::verifier::JwtVerifier;
1113
@@ -23,6 +25,49 @@
2325}
2426
2527impl Session {
28 pub async fn from_http_request(request: &HttpRequest) -> Self {
29 let Some(verifier) = request
30 .app_data::<web::Data<Arc<JwtVerifier>>>()
31 .map(|value| value.get_ref().clone())
32 .or_else(|| request.extensions().get::<Arc<JwtVerifier>>().cloned())
33 else {
34 return Self::anonymous();
35 };
36
37 let token = request
38 .cookie(verifier.session_cookie())
39 .map(|cookie| cookie.value().to_string())
40 .or_else(|| {
41 request
42 .headers()
43 .get(AUTHORIZATION)
44 .and_then(|value| value.to_str().ok())
45 .and_then(|value| value.strip_prefix("Bearer "))
46 .map(str::trim)
47 .filter(|value| !value.is_empty())
48 .map(str::to_owned)
49 });
50
51 let Some(token) = token else {
52 return Self::anonymous();
53 };
54
55 match verifier.verify_access(&token).await {
56 Ok(claims) => Self {
57 state: SessionState::Valid(claims),
58 raw: Some(token),
59 },
60 Err(SessionError::Expired) => Self {
61 state: SessionState::Expired,
62 raw: Some(token),
63 },
64 Err(error) => {
65 tracing::debug!(error = %error, "rejected bearer token");
66 Self::anonymous()
67 }
68 }
69 }
70
2671 pub fn anonymous() -> Self {
2772 Self {
2873 state: SessionState::Anonymous,
@@ -85,52 +130,12 @@
85130 }
86131}
87132
88impl<S> FromRequestParts<S> for Session
89where
90 S: Send + Sync,
91{
92 type Rejection = std::convert::Infallible;
133impl FromRequest for Session {
134 type Error = Infallible;
135 type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
93136
94 async fn from_request_parts(parts: &mut Parts, _state: &S) -> Result<Self, Self::Rejection> {
95 let Some(verifier) = parts.extensions.get::<Arc<JwtVerifier>>().cloned() else {
96 return Ok(Self::anonymous());
97 };
98
99 let token = parts
100 .headers
101 .get(axum::http::header::COOKIE)
102 .and_then(|value| value.to_str().ok())
103 .and_then(|header| {
104 cookie::read_cookie(header, verifier.session_cookie()).map(str::to_owned)
105 })
106 .or_else(|| {
107 parts
108 .headers
109 .get(AUTHORIZATION)
110 .and_then(|value| value.to_str().ok())
111 .and_then(|value| value.strip_prefix("Bearer "))
112 .map(str::trim)
113 .filter(|value| !value.is_empty())
114 .map(str::to_owned)
115 });
116
117 let Some(token) = token else {
118 return Ok(Self::anonymous());
119 };
120
121 match verifier.verify_access(&token).await {
122 Ok(claims) => Ok(Self {
123 state: SessionState::Valid(claims),
124 raw: Some(token),
125 }),
126 Err(SessionError::Expired) => Ok(Self {
127 state: SessionState::Expired,
128 raw: Some(token),
129 }),
130 Err(error) => {
131 tracing::debug!(error = %error, "rejected bearer token");
132 Ok(Self::anonymous())
133 }
134 }
137 fn from_request(request: &HttpRequest, _payload: &mut Payload) -> Self::Future {
138 let request = request.clone();
139 Box::pin(async move { Ok(Self::from_http_request(&request).await) })
135140 }
136141}
Modified lib/jwt/src/guard.rs View lib/jwt/src/guard.rs
@@ -1,6 +1,7 @@
1use axum::extract::{Request, State};
2use axum::middleware::Next;
3use axum::response::{IntoResponse, Response};
1use actix_web::Error;
2use actix_web::body::{BoxBody, MessageBody};
3use actix_web::dev::{ServiceRequest, ServiceResponse};
4use actix_web::middleware::Next;
45
56use crate::error::SessionError;
67use crate::extract::Session;
@@ -13,17 +14,16 @@
1314 }
1415}
1516
16pub async fn session_guard<S>(
17 State(_service): State<S>,
18 session: Session,
19 request: Request,
20 next: Next,
21) -> Response
17pub async fn session_guard<B>(
18 request: ServiceRequest,
19 next: Next<B>,
20) -> Result<ServiceResponse<BoxBody>, Error>
2221where
23 S: Send + Sync + 'static,
22 B: MessageBody + 'static,
2423{
24 let session = Session::from_http_request(request.request()).await;
2525 match require_uid(&session) {
26 Ok(_) => next.run(request).await,
27 Err(error) => error.into_response(),
26 Ok(_) => Ok(next.call(request).await?.map_into_boxed_body()),
27 Err(error) => Err(error.into()),
2828 }
2929}
Modified lib/jwt/Cargo.toml View lib/jwt/Cargo.toml
@@ -6,7 +6,7 @@
66[dependencies]
77kenoauth = { workspace = true }
88config = { workspace = true }
9axum = { workspace = true }
9actix-web = { workspace = true }
1010serde = { workspace = true, features = ["derive"] }
1111serde_json = { workspace = true }
1212jsonwebtoken = { workspace = true }
Modified lib/kenauth/src/lib.rs View lib/kenauth/src/lib.rs
@@ -1,2 +1,3 @@
1pub use kenoauth::client::{KenAuthClient, error::KenAuthError};
1pub use kenoauth::client::KenAuthClient;
2pub use kenoauth::client::error::KenAuthError;
23pub use kenoauth::protos::auth as proto;
Modified lib/kvcache/src/conn.rs View lib/kvcache/src/conn.rs
@@ -1,5 +1,5 @@
1use crate::error::{CacheError, CacheResult};
12use config::identity::redis::RedisConfig;
2use crate::error::{CacheError, CacheResult};
33use deadpool_redis::{
44 ConnectionAddr, ConnectionInfo, PoolConfig, RedisConnectionInfo, Runtime, Status, Timeouts,
55};
Modified lib/proto/src/lib.rs View lib/proto/src/lib.rs
@@ -1,3 +1,4 @@
1#![allow(unused_crate_dependencies)]
12#![allow(
23 clippy::doc_lazy_continuation,
34 clippy::doc_overindented_list_items,
@@ -7,6 +8,8 @@
78// proto 注释里的 `auth.<kind>` 之类写法会被 rustdoc 当成 HTML 标签,生成代码不归这里维护。
89#![allow(rustdoc::invalid_html_tags)]
910
11use utoipa as _;
12
1013pub mod pb {
1114 include!(concat!(env!("OUT_DIR"), "/_include.rs"));
1215}
Modified lib/pulls/src/handlers/inbox.rs View lib/pulls/src/handlers/inbox.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::AppResponseData;
33use jwt::Session;
44
Modified lib/pulls/src/share/path.rs View lib/pulls/src/share/path.rs
@@ -1,5 +1,8 @@
1use axum::extract::{FromRequestParts, Path};
2use axum::http::request::Parts;
1use std::future::Future;
2use std::pin::Pin;
3
4use actix_web::dev::Payload;
5use actix_web::{FromRequest, HttpRequest};
36use serde::Deserialize;
47use utoipa::IntoParams;
58
@@ -13,16 +16,17 @@
1316 pub pull_ref: String,
1417}
1518
16impl<S> FromRequestParts<S> for PullPath
17where
18 S: Send + Sync,
19{
20 type Rejection = ApiError;
19impl FromRequest for PullPath {
20 type Error = ApiError;
21 type Future = Pin<Box<dyn Future<Output = Result<Self, Self::Error>>>>;
2122
22 async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
23 Path::<PullPath>::from_request_parts(parts, state)
24 .await
25 .map(|Path(p)| p)
26 .map_err(|_| ApiError::bad_request("invalid request path"))
23 fn from_request(request: &HttpRequest, _payload: &mut Payload) -> Self::Future {
24 let request = request.clone();
25 Box::pin(async move {
26 actix_web::web::Path::<PullPath>::from_request(&request, &mut Payload::None)
27 .await
28 .map(|value| value.into_inner())
29 .map_err(|_| ApiError::bad_request("invalid request path"))
30 })
2731 }
2832}
Modified lib/pulls/src/routes.rs View lib/pulls/src/routes.rs
@@ -1,5 +1,5 @@
1use axum::Router;
2use axum::routing::{get, post, put};
1use export::http::Router;
2use export::http::routing::{get, post, put};
33
44use crate::handlers::assignees::add_pull_assignee::add_pull_assignee;
55use crate::handlers::assignees::list_pull_assignees::list_pull_assignees;
@@ -90,7 +90,7 @@
9090 )
9191 .route(
9292 &format!("{ITEM}/comments/{{comment_id}}"),
93 axum::routing::patch(update_pull_comment).delete(delete_pull_comment),
93 export::http::routing::patch(update_pull_comment).delete(delete_pull_comment),
9494 )
9595 .route(
9696 &format!("{ITEM}/comments/{{comment_id}}/resolve"),
@@ -102,7 +102,7 @@
102102 )
103103 .route(
104104 &format!("{ITEM}/requested-reviewers/{{user_id}}"),
105 axum::routing::delete(delete_requested_reviewer),
105 export::http::routing::delete(delete_requested_reviewer),
106106 )
107107 .route(
108108 &format!("{ITEM}/labels"),
@@ -110,7 +110,7 @@
110110 )
111111 .route(
112112 &format!("{ITEM}/labels/{{label_ref}}"),
113 axum::routing::delete(detach_pull_label),
113 export::http::routing::delete(detach_pull_label),
114114 )
115115 .route(&format!("{ITEM}/assignees"), get(list_pull_assignees))
116116 .route(
@@ -121,6 +121,6 @@
121121 .route(ISSUE_PULLS, get(list_issue_pulls).post(link_issue_pull))
122122 .route(
123123 &format!("{ISSUE_PULLS}/{{relation_id}}"),
124 axum::routing::delete(unlink_issue_pull),
124 export::http::routing::delete(unlink_issue_pull),
125125 )
126126}
Modified lib/pulls/Cargo.toml View lib/pulls/Cargo.toml
@@ -12,7 +12,7 @@
1212queue = { workspace = true }
1313jwt = { workspace = true }
1414space = { workspace = true }
15axum = { workspace = true }
15actix-web = { workspace = true }
1616sea-orm = { workspace = true }
1717serde = { workspace = true, features = ["derive"] }
1818serde_json = { workspace = true }
Modified lib/queue/src/identity/conn.rs View lib/queue/src/identity/conn.rs
@@ -4,8 +4,8 @@
44use rocketmq_common::common::message::message_single::Message;
55use tokio::sync::Mutex;
66
7use crate::identity::error::{QueueError, QueueResult};
78use config::identity::rocketmq::RocketMqConfig;
8use crate::identity::error::{QueueError, QueueResult};
99
1010pub const DEFAULT_MAX_MESSAGE_BYTES: usize = 4 * 1024 * 1024;
1111
Modified lib/session/src/middleware.rs View lib/session/src/middleware.rs
@@ -1,10 +1,10 @@
1use config::identity::session::{SameSite, SessionConfig};
21use actix_web::body::MessageBody;
32use actix_web::cookie::time::Duration;
43use actix_web::cookie::{Cookie, SameSite as CookieSameSite};
54use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform, forward_ready};
65use actix_web::http::header::{HeaderName, HeaderValue, USER_AGENT};
76use actix_web::{HttpMessage, HttpRequest, HttpResponse};
7use config::identity::session::{SameSite, SessionConfig};
88use std::fmt;
99use std::future::{Ready, ready};
1010use std::pin::Pin;
Modified lib/session/src/redis.rs View lib/session/src/redis.rs
@@ -1,8 +1,8 @@
11use std::sync::LazyLock;
22
3use actix_web::cookie::time::Duration;
34use kvcache::redis::{AsyncCommands, Script, ScriptInvocation};
45use kvcache::{Cache, CacheConnection};
5use actix_web::cookie::time::Duration;
66
77use config::identity::redis::RedisConfig;
88
Modified lib/space/src/handlers/accept_invite.rs View lib/space/src/handlers/accept_invite.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, State};
1use export::http::{Json, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/add_member.rs View lib/space/src/handlers/add_member.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/assign_group.rs View lib/space/src/handlers/assign_group.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/ban_user.rs View lib/space/src/handlers/ban_user.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/create_dir_node.rs View lib/space/src/handlers/create_dir_node.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/create_group.rs View lib/space/src/handlers/create_group.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/create_invite.rs View lib/space/src/handlers/create_invite.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/create_join_rule.rs View lib/space/src/handlers/create_join_rule.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/create_room.rs View lib/space/src/handlers/create_room.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/create_space.rs View lib/space/src/handlers/create_space.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, State};
1use export::http::{Json, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/delete_group.rs View lib/space/src/handlers/delete_group.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/delete_join_rule.rs View lib/space/src/handlers/delete_join_rule.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/delete_room.rs View lib/space/src/handlers/delete_room.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use uuid::Uuid;
Modified lib/space/src/handlers/delete_space.rs View lib/space/src/handlers/delete_space.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/delete_space_image.rs View lib/space/src/handlers/delete_space_image.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/explore_spaces.rs View lib/space/src/handlers/explore_spaces.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/get_ban.rs View lib/space/src/handlers/get_ban.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/get_group.rs View lib/space/src/handlers/get_group.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use serde::Deserialize;
Modified lib/space/src/handlers/get_member.rs View lib/space/src/handlers/get_member.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use serde::Deserialize;
Modified lib/space/src/handlers/get_space.rs View lib/space/src/handlers/get_space.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/get_space_dir_tree.rs View lib/space/src/handlers/get_space_dir_tree.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use serde::Deserialize;
Modified lib/space/src/handlers/get_space_image.rs View lib/space/src/handlers/get_space_image.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/get_space_image_file.rs View lib/space/src/handlers/get_space_image_file.rs
@@ -1,6 +1,5 @@
1use axum::extract::{Path, Query, State};
2use axum::http::{StatusCode, header};
3use axum::response::Response;
1use actix_web::http::header;
2use export::http::{Path, Query, Response, State};
43use imaging::layout::ImageVariant;
54use jwt::Session;
65use serde::Deserialize;
@@ -44,10 +43,8 @@
4443 query.variant.unwrap_or(ImageVariant::Preview),
4544 )
4645 .await?;
47 Ok(Response::builder()
48 .status(StatusCode::FOUND)
49 .header(header::LOCATION, url)
50 .header(header::CACHE_CONTROL, "private, max-age=300")
51 .body(axum::body::Body::empty())
52 .unwrap())
46 Ok(Response::Found()
47 .insert_header((header::LOCATION, url))
48 .insert_header((header::CACHE_CONTROL, "private, max-age=300"))
49 .finish())
5350}
Modified lib/space/src/handlers/join_group_self.rs View lib/space/src/handlers/join_group_self.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/join_requests.rs View lib/space/src/handlers/join_requests.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/leave_group_self.rs View lib/space/src/handlers/leave_group_self.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/list_bans.rs View lib/space/src/handlers/list_bans.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/list_dir_group_overwrites.rs View lib/space/src/handlers/list_dir_group_overwrites.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/list_dir_user_overwrites.rs View lib/space/src/handlers/list_dir_user_overwrites.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/list_groups.rs View lib/space/src/handlers/list_groups.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/list_invites.rs View lib/space/src/handlers/list_invites.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/list_join_rules.rs View lib/space/src/handlers/list_join_rules.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/list_members.rs View lib/space/src/handlers/list_members.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, Query, State};
1use export::http::{Path, Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/list_spaces.rs View lib/space/src/handlers/list_spaces.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Query, State};
1use export::http::{Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/move_dir_node.rs View lib/space/src/handlers/move_dir_node.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/reject_invite.rs View lib/space/src/handlers/reject_invite.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, State};
1use export::http::{Json, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/remove_dir_group_overwrite.rs View lib/space/src/handlers/remove_dir_group_overwrite.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/remove_dir_user_overwrite.rs View lib/space/src/handlers/remove_dir_user_overwrite.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/remove_member.rs View lib/space/src/handlers/remove_member.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/repair_dir_parents.rs View lib/space/src/handlers/repair_dir_parents.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/revoke_invite.rs View lib/space/src/handlers/revoke_invite.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use serde::Deserialize;
Modified lib/space/src/handlers/self_member.rs View lib/space/src/handlers/self_member.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/set_dir_group_overwrite.rs View lib/space/src/handlers/set_dir_group_overwrite.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use serde::Deserialize;
Modified lib/space/src/handlers/set_dir_user_overwrite.rs View lib/space/src/handlers/set_dir_user_overwrite.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use serde::Deserialize;
Modified lib/space/src/handlers/unassign_group.rs View lib/space/src/handlers/unassign_group.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use serde::Deserialize;
Modified lib/space/src/handlers/unban_user.rs View lib/space/src/handlers/unban_user.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/update_ban.rs View lib/space/src/handlers/update_ban.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/update_dir_node.rs View lib/space/src/handlers/update_dir_node.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/update_group.rs View lib/space/src/handlers/update_group.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/update_join_rule.rs View lib/space/src/handlers/update_join_rule.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/update_member.rs View lib/space/src/handlers/update_member.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/update_space.rs View lib/space/src/handlers/update_space.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Json, Path, State};
1use export::http::{Json, Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/space/src/handlers/upload_space_image.rs View lib/space/src/handlers/upload_space_image.rs
@@ -1,7 +1,5 @@
1use axum::body::Bytes;
2use axum::extract::{Path, Query, State};
3use axum::http::HeaderMap;
4use axum::http::header::CONTENT_TYPE;
1use actix_web::http::header::CONTENT_TYPE;
2use export::http::{Bytes, HeaderMap, Path, Query, State};
53use export::rest::{AppResponseData, NoData};
64use jwt::Session;
75
Modified lib/space/src/share/guard.rs View lib/space/src/share/guard.rs
@@ -1,16 +1 @@
1use axum::extract::{Request, State};
2use axum::middleware::Next;
3use axum::response::Response;
4use jwt::Session;
5use jwt::share::guard::session_guard;
6
7use crate::services::service::SpaceService;
8
9pub async fn space_guard(
10 State(service): State<SpaceService>,
11 session: Session,
12 request: Request,
13 next: Next,
14) -> Response {
15 session_guard::<SpaceService>(State(service), session, request, next).await
16}
1pub use jwt::share::guard::session_guard as space_guard;
Modified lib/space/src/routes.rs View lib/space/src/routes.rs
@@ -1,5 +1,5 @@
1use axum::Router;
2use axum::routing::{delete, get, patch, post, put};
1use export::http::Router;
2use export::http::routing::{delete, get, patch, post, put};
33
44use crate::handlers::accept_invite::accept_invite;
55use crate::handlers::add_member::add_member;
@@ -54,9 +54,8 @@
5454use crate::handlers::update_space::update_space;
5555use crate::handlers::upload_space_image::upload_space_image;
5656use crate::services::service::SpaceService;
57use crate::share::guard::space_guard;
5857
59pub fn router(service: SpaceService) -> Router<SpaceService> {
58pub fn router(_service: SpaceService) -> Router<SpaceService> {
6059 let public: Router<SpaceService> = Router::new()
6160 .route("/spaces/explore", get(explore_spaces))
6261 .route("/spaces/{space_name}", get(get_space))
@@ -67,7 +66,7 @@
6766 get(get_space_image_file),
6867 );
6968
70 let protected = Router::new()
69 let protected = Router::<SpaceService>::new()
7170 .route("/spaces", post(create_space).get(list_spaces))
7271 .route("/spaces/invites/accept", post(accept_invite))
7372 .route("/spaces/invites/reject", post(reject_invite))
@@ -78,11 +77,9 @@
7877 .route("/spaces/{space_name}/restore", post(restore_space))
7978 .route(
8079 "/spaces/{space_name}/images/{kind}",
81 put(upload_space_image).delete(delete_space_image).layer(
82 axum::extract::DefaultBodyLimit::max(
83 crate::services::images::MAX_SPACE_IMAGE_BYTES + 1024 * 1024,
84 ),
85 ),
80 put(upload_space_image)
81 .delete(delete_space_image)
82 .payload_limit(crate::services::images::MAX_SPACE_IMAGE_BYTES + 1024 * 1024),
8683 )
8784 .route(
8885 "/spaces/{space_name}/dir",
@@ -123,7 +120,7 @@
123120 )
124121 .route(
125122 "/spaces/{space_name}/members/@me",
126 axum::routing::patch(crate::handlers::self_member::update_self_member)
123 export::http::routing::patch(crate::handlers::self_member::update_self_member)
127124 .delete(crate::handlers::self_member::leave_space),
128125 )
129126 .route(
@@ -183,10 +180,7 @@
183180 "/spaces/{space_name}/bans/{user_id}",
184181 get(get_ban).patch(update_ban).delete(unban_user),
185182 )
186 .route_layer(axum::middleware::from_fn_with_state(
187 service.clone(),
188 space_guard,
189 ));
183 .wrap(export::http::Guard::Session);
190184
191185 public.merge(protected)
192186}
Modified lib/space/Cargo.toml View lib/space/Cargo.toml
@@ -15,7 +15,7 @@
1515kenauth = { workspace = true }
1616prost-types = { workspace = true }
1717storage = { workspace = true }
18axum = { workspace = true }
18actix-web = { workspace = true }
1919sea-orm = { workspace = true }
2020serde = { workspace = true, features = ["derive"] }
2121serde_json = { workspace = true }
Modified lib/token/src/authority.rs View lib/token/src/authority.rs
@@ -302,8 +302,7 @@
302302 username: None,
303303 token_type: "access".into(),
304304 };
305 let token =
306 crate::sign(&crate::signing_key(&key).unwrap(), "fixture", &claims).unwrap();
305 let token = crate::sign(&crate::signing_key(&key).unwrap(), "fixture", &claims).unwrap();
307306 assert!(!authenticate(&token).await.unwrap().is_admin);
308307 assert!(session(1, 1, "fixture-session").await.is_ok());
309308 assert!(session(1, 2, "fixture-session").await.is_err());
Modified lib/token/src/authority_grpc.rs View lib/token/src/authority_grpc.rs
@@ -4,13 +4,13 @@
44use std::sync::atomic::{AtomicI64, AtomicU32, Ordering};
55use std::time::Duration;
66
7use async_trait::async_trait;
8use chrono::{DateTime, TimeZone, Utc};
79use proto::auth::internal::v1::authority_service_client::AuthorityServiceClient;
810use proto::auth::internal::v1::{
911 AuthorityAccess, AuthorityUser, AuthorityUserPatch, BatchGetUserRequest, GetSessionRequest,
1012 GetUserRequest, UpdateUserRequest, ValidateRequest,
1113};
12use async_trait::async_trait;
13use chrono::{DateTime, TimeZone, Utc};
1414use tonic::Code;
1515use tonic::metadata::{Ascii, MetadataValue};
1616use tonic::transport::{Channel, Endpoint};
@@ -64,9 +64,7 @@
6464 "jwt.authority_grpc_url must name a host".into(),
6565 ));
6666 }
67 if scheme == "http"
68 && !config::is_loopback_host(endpoint.uri().host().unwrap_or_default())
69 {
67 if scheme == "http" && !config::is_loopback_host(endpoint.uri().host().unwrap_or_default()) {
7068 return Err(JwtError::Config(
7169 "jwt.authority_grpc_url must use https when it is not a loopback address".into(),
7270 ));
Modified lib/token/src/service.rs View lib/token/src/service.rs
@@ -53,10 +53,7 @@
5353
5454fn matches_digest(expected: &str, supplied: &str) -> bool {
5555 let left = format!("sha256:{:x}", Sha256::digest(supplied.as_bytes()));
56 crypt::constant_time_eq(
57 left.as_bytes(),
58 expected.to_ascii_lowercase().as_bytes(),
59 )
56 crypt::constant_time_eq(left.as_bytes(), expected.to_ascii_lowercase().as_bytes())
6057}
6158
6259pub fn attach(metadata: &mut tonic::metadata::MetadataMap) -> Result<(), JwtError> {
Modified lib/token/src/state.rs View lib/token/src/state.rs
@@ -24,8 +24,7 @@
2424 let Ok(url) = reqwest::Url::parse(url) else {
2525 return false;
2626 };
27 url.host_str()
28 .is_some_and(config::is_loopback_host)
27 url.host_str().is_some_and(config::is_loopback_host)
2928}
3029
3130static INSTALLED: OnceLock<JwtState> = OnceLock::new();
@@ -303,13 +302,7 @@
303302fn require_secure(url: &reqwest::Url, field: &str) -> Result<(), JwtError> {
304303 match url.scheme() {
305304 "https" => Ok(()),
306 "http"
307 if url
308 .host_str()
309 .is_some_and(config::is_loopback_host) =>
310 {
311 Ok(())
312 }
305 "http" if url.host_str().is_some_and(config::is_loopback_host) => Ok(()),
313306 "http" => Err(JwtError::Config(format!(
314307 "{field} must use https when it is not a loopback address"
315308 ))),
Modified lib/user/src/handlers/create_meme.rs View lib/user/src/handlers/create_meme.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::State;
1use export::http::{Json, State};
32use export::rest::{AppResponseData, NoData};
43use jwt::Session;
54
Modified lib/user/src/handlers/delete_avatar.rs View lib/user/src/handlers/delete_avatar.rs
@@ -1,4 +1,4 @@
1use axum::extract::State;
1use export::http::State;
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/user/src/handlers/delete_meme.rs View lib/user/src/handlers/delete_meme.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use uuid::Uuid;
Modified lib/user/src/handlers/delete_push_subscription.rs View lib/user/src/handlers/delete_push_subscription.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use uuid::Uuid;
Modified lib/user/src/handlers/get_avatar.rs View lib/user/src/handlers/get_avatar.rs
@@ -1,4 +1,4 @@
1use axum::extract::State;
1use export::http::State;
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/user/src/handlers/get_profile_detail.rs View lib/user/src/handlers/get_profile_detail.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/user/src/handlers/get_user_avatar.rs View lib/user/src/handlers/get_user_avatar.rs
@@ -1,6 +1,5 @@
1use axum::extract::{Path, Query, State};
2use axum::http::{StatusCode, header};
3use axum::response::Response;
1use actix_web::http::header;
2use export::http::{Path, Query, Response, State};
43use imaging::layout::ImageVariant;
54use jwt::Session;
65use jwt::share::session;
@@ -42,10 +41,8 @@
4241 let url = service
4342 .avatar_redirect_url(user_id, query.variant.unwrap_or(ImageVariant::Preview))
4443 .await?;
45 Ok(Response::builder()
46 .status(StatusCode::FOUND)
47 .header(header::LOCATION, url)
48 .header(header::CACHE_CONTROL, "private, max-age=300")
49 .body(axum::body::Body::empty())
50 .unwrap())
44 Ok(Response::Found()
45 .insert_header((header::LOCATION, url))
46 .insert_header((header::CACHE_CONTROL, "private, max-age=300"))
47 .finish())
5148}
Modified lib/user/src/handlers/list_memes.rs View lib/user/src/handlers/list_memes.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Query, State};
1use export::http::{Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/user/src/handlers/list_push_subscriptions.rs View lib/user/src/handlers/list_push_subscriptions.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Query, State};
1use export::http::{Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/user/src/handlers/list_saved_messages.rs View lib/user/src/handlers/list_saved_messages.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Query, State};
1use export::http::{Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/user/src/handlers/my_reports.rs View lib/user/src/handlers/my_reports.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Query, State};
1use export::http::{Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/user/src/handlers/my_spaces.rs View lib/user/src/handlers/my_spaces.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Query, State};
1use export::http::{Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/user/src/handlers/my_suspensions.rs View lib/user/src/handlers/my_suspensions.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Query, State};
1use export::http::{Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/user/src/handlers/push_config.rs View lib/user/src/handlers/push_config.rs
@@ -1,4 +1,4 @@
1use axum::extract::State;
1use export::http::State;
22use export::rest::AppResponseData;
33use serde::Serialize;
44use utoipa::ToSchema;
Modified lib/user/src/handlers/recent_mentions.rs View lib/user/src/handlers/recent_mentions.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Query, State};
1use export::http::{Query, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44
Modified lib/user/src/handlers/save_message.rs View lib/user/src/handlers/save_message.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::State;
1use export::http::{Json, State};
32use export::rest::{AppResponseData, NoData};
43use jwt::Session;
54
Modified lib/user/src/handlers/unsave_message.rs View lib/user/src/handlers/unsave_message.rs
@@ -1,4 +1,4 @@
1use axum::extract::{Path, State};
1use export::http::{Path, State};
22use export::rest::{AppResponseData, NoData};
33use jwt::Session;
44use uuid::Uuid;
Modified lib/user/src/handlers/update_meme.rs View lib/user/src/handlers/update_meme.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::{Path, State};
1use export::http::{Json, Path, State};
32use export::rest::{AppResponseData, NoData};
43use jwt::Session;
54use uuid::Uuid;
Modified lib/user/src/handlers/upload_avatar.rs View lib/user/src/handlers/upload_avatar.rs
@@ -1,7 +1,5 @@
1use axum::body::Bytes;
2use axum::extract::State;
3use axum::http::HeaderMap;
4use axum::http::header::CONTENT_TYPE;
1use actix_web::http::header::CONTENT_TYPE;
2use export::http::{Bytes, HeaderMap, State};
53use export::rest::{AppResponseData, NoData};
64use jwt::Session;
75
Modified lib/user/src/handlers/upsert_push_subscription.rs View lib/user/src/handlers/upsert_push_subscription.rs
@@ -1,5 +1,4 @@
1use axum::Json;
2use axum::extract::State;
1use export::http::{Json, State};
32use export::rest::{AppResponseData, NoData};
43use jwt::Session;
54
Modified lib/user/src/share/guard.rs View lib/user/src/share/guard.rs
@@ -1,16 +1 @@
1use axum::extract::{Request, State};
2use axum::middleware::Next;
3use axum::response::Response;
4use jwt::Session;
5use jwt::share::guard::session_guard;
6
7use crate::services::service::UserService;
8
9pub async fn user_guard(
10 State(service): State<UserService>,
11 session: Session,
12 request: Request,
13 next: Next,
14) -> Response {
15 session_guard::<UserService>(State(service), session, request, next).await
16}
1pub use jwt::share::guard::session_guard as user_guard;
Modified lib/user/src/routes.rs View lib/user/src/routes.rs
@@ -1,5 +1,5 @@
1use axum::Router;
2use axum::routing::{delete, get, put};
1use export::http::Router;
2use export::http::routing::{delete, get, put};
33
44use crate::handlers::create_meme::create_meme;
55use crate::handlers::delete_avatar::delete_avatar;
@@ -20,9 +20,8 @@
2020use crate::handlers::upload_avatar::upload_avatar;
2121use crate::handlers::upsert_push_subscription::upsert_push_subscription;
2222use crate::services::service::UserService;
23use crate::share::guard::user_guard;
2423
25pub fn router(service: UserService) -> Router<UserService> {
24pub fn router(_service: UserService) -> Router<UserService> {
2625 let public: Router<UserService> = Router::new()
2726 .route("/users/{user_id}/profile-detail", get(get_profile_detail))
2827 .route(
@@ -30,14 +29,14 @@
3029 get(crate::handlers::push_config::push_config),
3130 );
3231
33 let protected = Router::new()
32 let protected = Router::<UserService>::new()
3433 .route("/users/{user_id}/avatar", get(get_user_avatar))
3534 .route(
3635 "/me/avatar",
3736 get(get_avatar)
3837 .put(upload_avatar)
3938 .delete(delete_avatar)
40 .layer(axum::extract::DefaultBodyLimit::max(6 * 1024 * 1024)),
39 .payload_limit(6 * 1024 * 1024),
4140 )
4241 .route("/me/spaces", get(my_spaces))
4342 .route("/me/memes", get(list_memes).post(create_meme))
@@ -62,10 +61,7 @@
6261 )
6362 .route("/me/messages/saved/{message_id}", delete(unsave_message))
6463 .route("/me/suspensions", get(my_suspensions))
65 .route_layer(axum::middleware::from_fn_with_state(
66 service.clone(),
67 user_guard,
68 ));
64 .wrap(export::http::Guard::Session);
6965
7066 public.merge(protected)
7167}
Modified lib/user/Cargo.toml View lib/user/Cargo.toml
@@ -12,7 +12,7 @@
1212imaging = { workspace = true }
1313jwt = { workspace = true }
1414storage = { workspace = true }
15axum = { workspace = true }
15actix-web = { workspace = true }
1616sea-orm = { workspace = true }
1717redis = { workspace = true }
1818serde = { workspace = true, features = ["derive"] }
Modified lib/webapi/src/cleanup.rs View lib/webapi/src/cleanup.rs
@@ -9,9 +9,8 @@
99 */
1010use std::collections::HashMap;
1111
12use axum::extract::{Path, State};
13use axum::routing::{delete, get, post};
14use axum::{Json, Router};
12use export::http::routing::{delete, get, post};
13use export::http::{Json, Path, Router, State};
1514use export::rest::{AppResponseData, NoData};
1615use jwt::Session;
1716use jwt::share::session;
@@ -81,7 +80,7 @@
8180
8281pub fn router(space: SpaceService, issues: IssueService, git: GitService) -> Router {
8382 let state = CleanupState { space, issues, git };
84 Router::new()
83 Router::<()>::new()
8584 .route("/spaces/{space_name}/cleanup", get(list_cleanup_objects))
8685 .route(
8786 "/spaces/{space_name}/cleanup/{kind}/{object_id}/visibility",
Modified lib/webapi/src/lib.rs View lib/webapi/src/lib.rs
@@ -1,79 +1,39 @@
11pub mod cleanup;
22pub mod openapi;
33
4use axum::Router;
5use axum::extract::{Request, State};
6use axum::http::StatusCode;
7use axum::middleware::Next;
8use axum::response::{IntoResponse, Response};
9use export::AppState;
10use export::middleware::AppTraceLayer;
11use tower_http::limit::RequestBodyLimitLayer;
12use tower_http::trace::TraceLayer;
4use std::sync::Arc;
135
14const MAX_REQUEST_BODY_BYTES: usize = 2 * 1024 * 1024;
15pub const SERVICE_KEY_HEADER: &str = "x-gitdata-service-key";
6use actix_cors::Cors;
167
17fn constant_time_eq(left: &[u8], right: &[u8]) -> bool {
18 if left.len() != right.len() {
19 return false;
20 }
21 let mut diff = 0u8;
22 for (a, b) in left.iter().zip(right.iter()) {
23 diff |= a ^ b;
24 }
25 diff == 0
26}
8use actix_web::http::Method;
9use actix_web::{Scope, web};
10use export::AppState;
11use export::http::{Guard, Router};
2712
28async fn require_service_key(
29 State(expected): State<String>,
30 request: Request,
31 next: Next,
32) -> Response {
33 let provided = request
34 .headers()
35 .get(SERVICE_KEY_HEADER)
36 .and_then(|value| value.to_str().ok())
37 .unwrap_or_default();
38 if expected.is_empty() || !constant_time_eq(provided.as_bytes(), expected.as_bytes()) {
39 return (StatusCode::UNAUTHORIZED, "invalid service key").into_response();
40 }
41 next.run(request).await
42}
43
44fn build_cors_layer(web: &config::web::WebConfig) -> tower_http::cors::CorsLayer {
45 use axum::http::{HeaderName, header};
46 use tower_http::cors::{AllowOrigin, CorsLayer};
13pub const MAX_REQUEST_BODY_BYTES: usize = 2 * 1024 * 1024;
4714
48 if web.cors_allow_origins.is_empty() {
49 return CorsLayer::new();
15pub fn build_cors(web: &config::web::WebConfig) -> Cors {
16 let mut cors = Cors::default();
17 for origin in &web.cors_allow_origins {
18 cors = cors.allowed_origin(origin);
5019 }
51 let origins: Vec<axum::http::HeaderValue> = web
52 .cors_allow_origins
53 .iter()
54 .filter_map(|o| o.parse().ok())
55 .collect();
56 CorsLayer::new()
57 .allow_origin(AllowOrigin::list(origins))
58 .allow_credentials(web.cors_allow_credentials)
59 .allow_methods([
60 axum::http::Method::GET,
61 axum::http::Method::POST,
62 axum::http::Method::PUT,
63 axum::http::Method::PATCH,
64 axum::http::Method::DELETE,
65 axum::http::Method::OPTIONS,
20 let mut cors = cors
21 .allowed_methods([
22 Method::GET,
23 Method::POST,
24 Method::PUT,
25 Method::PATCH,
26 Method::DELETE,
27 Method::OPTIONS,
6628 ])
67 .allow_headers([
68 header::CONTENT_TYPE,
69 header::AUTHORIZATION,
70 HeaderName::from_static("x-request-id"),
71 ])
29 .allowed_headers(["content-type", "authorization", "x-request-id"]);
30 if web.cors_allow_credentials {
31 cors = cors.supports_credentials();
32 }
33 cors
7234}
73
74pub async fn router(state: AppState) -> Result<Router, jwt::SessionError> {
75 let cors = build_cors_layer(&state.config.web);
7635
36pub fn scope(state: AppState) -> Scope {
7737 let auth = auth::services::AuthService::new(state.clone());
7838 let chat = chat::services::service::ChatService::new(state.clone());
7939 let issues = issues::services::service::IssueService::new(state.clone());
@@ -82,31 +42,10 @@
8242 let git = git::service::GitService::new(state.clone());
8343 let pulls = pulls::services::service::PullService::new(state.clone());
8444 let internal_service_key = state.config.internal.service_key.clone();
85 let jwt_verifier = if state.config.oauth.enabled {
86 match jwt::JwtVerifier::with_identity(
87 &state.config.oauth,
88 state
89 .kenauth
90 .as_ref()
91 .map(|client| client.service().clone()),
92 ) {
93 Ok(verifier) => Some(verifier),
94 Err(error) => {
95 tracing::warn!(%error, "oauth is enabled but the JWT verifier could not be built");
96 None
97 }
98 }
99 } else {
100 None
101 };
102 let revalidate_state = state.clone();
103 let health_state = state;
10445
105 // 清理接口需要跨模块调用(space/issues/git),这里的服务都还没被
106 // 其他 router 的 with_state 消费掉,先构造再合并。
10746 let cleanup_router = cleanup::router(space.clone(), issues.clone(), git.clone());
10847
109 let public: Router = Router::new()
48 let public = Router::<()>::new()
11049 .merge(auth::routes::router(auth.clone()).with_state(auth.clone()))
11150 .merge(chat::routes::router(chat.clone(), space.clone()).with_state(chat.clone()))
11251 .merge(issues::routes::router(issues.clone()).with_state(issues))
@@ -115,42 +54,35 @@
11554 .merge(git::routes::router(git.clone()).with_state(git.clone()))
11655 .merge(pulls::routes::router().with_state(pulls))
11756 .merge(cleanup_router);
118 let internal: Router = Router::new()
57 let internal = Router::<()>::new()
11958 .merge(git::routes::internal_router(git.clone()).with_state(git))
12059 .merge(chat::routes::internal_router(chat.clone()).with_state(chat))
121 .route_layer(axum::middleware::from_fn_with_state(
122 internal_service_key,
123 require_service_key,
124 ));
60 .with_state(internal_service_key)
61 .wrap(Guard::ServiceKey);
12562
126 let router: Router = Router::new()
127 .nest("/api/v1", public)
63 Router::<()>::new()
12864 .nest("/api/v1/internal", internal)
65 .nest("/api/v1", public)
12966 .nest("/pages", auth::routes::pages_router(auth.clone()))
130 .merge(export::health::router(health_state))
131 .layer(cors)
132 .layer(RequestBodyLimitLayer::new(MAX_REQUEST_BODY_BYTES))
133 .layer(
134 TraceLayer::new_for_http()
135 .make_span_with(export::telemetry::HttpSpan)
136 .on_response(export::telemetry::HttpOnResponse),
137 )
138 .layer(export::telemetry::metrics_layer())
139 .layer(AppTraceLayer::new());
67 .merge(export::health::router(state))
68 .into_scope(web::scope(""))
69}
14070
141 let router = match jwt_verifier {
142 Some(verifier) => {
143 let warm = verifier.clone();
144 tokio::spawn(async move { warm.warm_up().await });
145 router
146 .layer(axum::middleware::from_fn_with_state(
147 revalidate_state,
148 auth::oauth::revalidate::revalidate,
149 ))
150 .layer(axum::Extension(verifier))
71pub fn verifier(state: &AppState) -> Option<Arc<jwt::JwtVerifier>> {
72 if !state.config.oauth.enabled {
73 return None;
74 }
75 match jwt::JwtVerifier::with_identity(
76 &state.config.oauth,
77 state
78 .kenauth
79 .as_ref()
80 .map(|client| client.service().clone()),
81 ) {
82 Ok(verifier) => Some(verifier),
83 Err(error) => {
84 tracing::warn!(%error, "oauth is enabled but the JWT verifier could not be built");
85 None
15186 }
152 None => router,
153 };
154
155 Ok(router)
87 }
15688}
Modified lib/webapi/Cargo.toml View lib/webapi/Cargo.toml
@@ -17,8 +17,8 @@
1717export = { workspace = true }
1818jwt = { workspace = true }
1919tokio = { workspace = true }
20axum = { workspace = true }
21tower-http = { workspace = true }
20actix-web = { workspace = true }
21actix-cors = { workspace = true }
2222tracing = { workspace = true }
2323utoipa = { workspace = true }
2424serde = { workspace = true, features = ["derive"] }
Modified Cargo.lock View Cargo.lock
@@ -32,6 +32,21 @@
3232]
3333
3434[[package]]
35name = "actix-cors"
36version = "0.7.2"
37source = "registry+https://github.com/rust-lang/crates.io-index"
38checksum = "2aff07ada3254fc02618cb7850da91dceb23b5dbda53c6676ccbb28ba504f150"
39dependencies = [
40 "actix-utils",
41 "actix-web",
42 "derive_more",
43 "futures-util",
44 "log 0.4.34",
45 "once_cell",
46 "smallvec",
47]
48
49[[package]]
3550name = "actix-http"
3651version = "3.20.0"
3752source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -758,10 +773,10 @@
758773name = "auth"
759774version = "1.0.0"
760775dependencies = [
776 "actix-web",
761777 "aes-gcm 0.10.3",
762778 "argon2",
763779 "async-trait",
764 "axum",
765780 "base64 0.23.1",
766781 "config 1.0.0",
767782 "deletion",
@@ -853,15 +868,11 @@
853868checksum = "31b698c5f9a010f6573133b09e0de5408834d0c82f8d7475a89fc1867a71cd90"
854869dependencies = [
855870 "axum-core",
856 "base64 0.22.1",
857871 "bytes",
858 "form_urlencoded",
859872 "futures-util",
860873 "http 1.5.0",
861874 "http-body 1.1.0",
862875 "http-body-util",
863 "hyper 1.12.0",
864 "hyper-util",
865876 "itoa",
866877 "matchit",
867878 "memchr",
@@ -869,17 +880,10 @@
869880 "percent-encoding 2.3.2",
870881 "pin-project-lite",
871882 "serde_core",
872 "serde_json",
873 "serde_path_to_error",
874 "serde_urlencoded",
875 "sha1 0.10.7",
876883 "sync_wrapper",
877 "tokio",
878 "tokio-tungstenite",
879884 "tower",
880885 "tower-layer",
881886 "tower-service",
882 "tracing",
883887]
884888
885889[[package]]
@@ -898,24 +902,9 @@
898902 "sync_wrapper",
899903 "tower-layer",
900904 "tower-service",
901 "tracing",
902905]
903906
904907[[package]]
905name = "axum-otel-metrics"
906version = "0.14.1"
907source = "registry+https://github.com/rust-lang/crates.io-index"
908checksum = "d0d74d40b1ff9bd3e8f782aa6798d22ff6a7d1b53049e0e239f5332dab2dd83d"
909dependencies = [
910 "axum",
911 "http-body 1.1.0",
912 "opentelemetry",
913 "opentelemetry-semantic-conventions",
914 "pin-project-lite",
915 "tower",
916]
917
918[[package]]
919908name = "backon"
920909version = "1.6.0"
921910source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1241,8 +1230,8 @@
12411230name = "chat"
12421231version = "1.0.0"
12431232dependencies = [
1233 "actix-web",
12441234 "async-trait",
1245 "axum",
12461235 "base64 0.23.1",
12471236 "bytes",
12481237 "config 1.0.0",
@@ -2406,12 +2395,12 @@
24062395name = "export"
24072396version = "1.0.0"
24082397dependencies = [
2409 "axum",
2410 "axum-otel-metrics",
2398 "actix-web",
24112399 "bytes",
24122400 "config 1.0.0",
2413 "http-body-util",
2401 "http 1.5.0",
24142402 "imaging",
2403 "jwt",
24152404 "kenauth",
24162405 "models",
24172406 "opentelemetry",
@@ -2430,7 +2419,6 @@
24302419 "time 0.3.55",
24312420 "tokio",
24322421 "tower",
2433 "tower-http 0.7.0",
24342422 "tracing",
24352423 "tracing-opentelemetry",
24362424 "tracing-subscriber",
@@ -2792,8 +2780,8 @@
27922780name = "git"
27932781version = "1.0.0"
27942782dependencies = [
2783 "actix-web",
27952784 "async-trait",
2796 "axum",
27972785 "base64 0.23.1",
27982786 "bytes",
27992787 "cache",
@@ -2803,7 +2791,6 @@
28032791 "futures",
28042792 "globset",
28052793 "hayro",
2806 "http-body-util",
28072794 "jwt",
28082795 "kenshell",
28092796 "maud",
@@ -3650,10 +3637,12 @@
36503637name = "issues"
36513638version = "1.0.0"
36523639dependencies = [
3640 "actix-web",
36533641 "async-trait",
3654 "axum",
3642 "bytes",
36553643 "config 1.0.0",
36563644 "export",
3645 "futures",
36573646 "http 0.2.12",
36583647 "i18n",
36593648 "jwt",
@@ -3842,7 +3831,7 @@
38423831name = "jwt"
38433832version = "1.0.0"
38443833dependencies = [
3845 "axum",
3834 "actix-web",
38463835 "base64 0.23.1",
38473836 "config 1.0.0",
38483837 "jsonwebtoken 9.3.1",
@@ -3910,9 +3899,9 @@
39103899name = "kenapi"
39113900version = "1.0.0"
39123901dependencies = [
3902 "actix-web",
39133903 "anyhow",
39143904 "auth",
3915 "axum",
39163905 "chat",
39173906 "clap",
39183907 "config 1.0.0",
@@ -4430,8 +4419,6 @@
44304419source = "registry+https://github.com/rust-lang/crates.io-index"
44314420checksum = "8156733e27020ea5c684db5beac5d1d611e1272ab17901a49466294b84fc217e"
44324421dependencies = [
4433 "axum-core",
4434 "http 1.5.0",
44354422 "itoa",
44364423 "maud_macros",
44374424]
@@ -5113,12 +5100,6 @@
51135100]
51145101
51155102[[package]]
5116name = "opentelemetry-semantic-conventions"
5117version = "0.32.1"
5118source = "registry+https://github.com/rust-lang/crates.io-index"
5119checksum = "c913ac17a6c451661ee255f4625d143e51647ae78ebd969b75e41c4442f4fe47"
5120
5121[[package]]
51225103name = "opentelemetry_sdk"
51235104version = "0.32.1"
51245105source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -5900,7 +5881,7 @@
59005881name = "pulls"
59015882version = "1.0.0"
59025883dependencies = [
5903 "axum",
5884 "actix-web",
59045885 "config 1.0.0",
59055886 "export",
59065887 "git",
@@ -6305,7 +6286,7 @@
63056286 "tokio",
63066287 "tokio-rustls",
63076288 "tower",
6308 "tower-http 0.6.11",
6289 "tower-http",
63096290 "tower-service",
63106291 "url 2.5.8",
63116292 "wasm-bindgen",
@@ -6350,7 +6331,7 @@
63506331 "tokio-rustls",
63516332 "tokio-util",
63526333 "tower",
6353 "tower-http 0.6.11",
6334 "tower-http",
63546335 "tower-service",
63556336 "url 2.5.8",
63566337 "wasm-bindgen",
@@ -7134,17 +7115,6 @@
71347115dependencies = [
71357116 "serde",
71367117 "serde_json",
7137]
7138
7139[[package]]
7140name = "serde_path_to_error"
7141version = "0.1.20"
7142source = "registry+https://github.com/rust-lang/crates.io-index"
7143checksum = "10a9ff822e371bb5403e391ecd83e182e0e77ba7f6fe0160b795797109d1b457"
7144dependencies = [
7145 "itoa",
7146 "serde",
7147 "serde_core",
71487118]
71497119
71507120[[package]]
@@ -7424,8 +7394,8 @@
74247394name = "space"
74257395version = "1.0.0"
74267396dependencies = [
7397 "actix-web",
74277398 "async-trait",
7428 "axum",
74297399 "config 1.0.0",
74307400 "deletion",
74317401 "export",
@@ -8332,24 +8302,6 @@
83328302]
83338303
83348304[[package]]
8335name = "tower-http"
8336version = "0.7.0"
8337source = "registry+https://github.com/rust-lang/crates.io-index"
8338checksum = "b11f75e912b0c2be01b63d8cf8057b8c3f97cf34abb3d431a3a4c8675498e233"
8339dependencies = [
8340 "bitflags 2.13.2",
8341 "bytes",
8342 "http 1.5.0",
8343 "http-body 1.1.0",
8344 "http-body-util",
8345 "percent-encoding 2.3.2",
8346 "pin-project-lite",
8347 "tower-layer",
8348 "tower-service",
8349 "tracing",
8350]
8351
8352[[package]]
83538305name = "tower-layer"
83548306version = "0.3.3"
83558307source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -8641,8 +8593,8 @@
86418593name = "user"
86428594version = "1.0.0"
86438595dependencies = [
8596 "actix-web",
86448597 "async-trait",
8645 "axum",
86468598 "base64 0.23.1",
86478599 "config 1.0.0",
86488600 "export",
@@ -8742,7 +8694,7 @@
87428694source = "registry+https://github.com/rust-lang/crates.io-index"
87438695checksum = "d047458f1b5b65237c2f6dc6db136945667f40a7668627b3490b9513a3d43a55"
87448696dependencies = [
8745 "axum",
8697 "actix-web",
87468698 "base64 0.22.1",
87478699 "mime_guess",
87488700 "regex",
@@ -8998,8 +8950,9 @@
89988950name = "webapi"
89998951version = "1.0.0"
90008952dependencies = [
8953 "actix-cors",
8954 "actix-web",
90018955 "auth",
9002 "axum",
90038956 "chat",
90048957 "config 1.0.0",
90058958 "export",
@@ -9014,7 +8967,6 @@
90148967 "space",
90158968 "time 0.3.55",
90168969 "tokio",
9017 "tower-http 0.7.0",
90188970 "tracing",
90198971 "user",
90208972 "utoipa 5.5.0",
Modified Cargo.toml View Cargo.toml
@@ -54,10 +54,9 @@
5454clap = { version = "4.6.7", features = ["derive"] }
5555serde = "1.0.229"
5656serde_json = "1.0.151"
57axum = { version = "0.8.9", features = ["ws"] }
57actix-web = "4.16.0"
58actix-cors = "0.7"
5859tower = "0.5.3"
59tower-http = { version = "0.7.0", features = ["trace", "limit", "cors"] }
60axum-otel-metrics = "0.14.1"
6160opentelemetry = "0.32"
6261opentelemetry_sdk = { version = "0.32.1", features = ["rt-tokio", "trace"] }
6362opentelemetry-http = "0.32"
@@ -85,15 +84,14 @@
8584bytes = "1"
8685futures = "0.3"
8786http = "0.2"
88http-body-util = "0.1"
8987tokio-util = { version = "0.7", features = ["io"] }
9088flate2 = "1"
9189globset = "0.4"
92utoipa = { version = "5.5", features = ["axum_extras", "time", "uuid"] }
93utoipa-swagger-ui = { version = "9", features = ["axum"] }
90utoipa = { version = "5.5", features = ["actix_extras", "time", "uuid"] }
91utoipa-swagger-ui = { version = "9", features = ["actix-web"] }
9492thiserror = "2.0.20"
9593hayro = { version = "0.7.1", features = ["embed-fonts", "embed-cmaps"] }
96maud = { version = "0.27", features = ["axum"] }
94maud = "0.27"
9795pulldown-cmark = { version = "0.13", default-features = false, features = ["html"] }
9896reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "json", "gzip", "brotli", "deflate"] }
9997livekit-api = { version = "0.7.1", default-features = false, features = ["services", "webhooks", "rustls-tls-webpki-roots"] }