refactor: remove log_id references and update error handling for improved clarity
ZhenYi committed
Modified lib/auth/src/auth_session/csrf.rs lib/auth/src/auth_session/csrf.rs +0 −1 View lib/auth/src/auth_session/csrf.rs
@@ -60,7 +60,6 @@
6060 "error": null,
6161 "error_code": "csrf_rejected",
6262 "request_id": null,
63 "log_id": null,
6463 })))
6564 .map_into_boxed_body());
6665 }
Modified lib/auth/src/auth_session/revalidate.rs lib/auth/src/auth_session/revalidate.rs +0 −1 View lib/auth/src/auth_session/revalidate.rs
@@ -85,7 +85,6 @@
8585 "error": null,
8686 "error_code": "session_revoked",
8787 "request_id": null,
88 "log_id": null,
8988 }));
9089 append_cookie(
9190 &mut response,
Modified lib/envelope/src/response.rs lib/envelope/src/response.rs +6 −0 View lib/envelope/src/response.rs
@@ -159,6 +159,12 @@
159159 }
160160}
161161
162impl std::fmt::Display for Envelope<()> {
163 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
164 write!(f, "API error: code={}, message={}", self.code, self.message)
165 }
166}
167
162168impl<T: Serialize> Responder for Envelope<T> {
163169 type Body = BoxBody;
164170
Modified lib/export/src/api_error.rs lib/export/src/api_error.rs +0 −1 View lib/export/src/api_error.rs
@@ -263,7 +263,6 @@
263263 $( Self::$variant(envelope) => ($status, (**envelope).clone()), )*
264264 };
265265 envelope.request_id = $crate::middleware::request_id().unwrap_or_default();
266 envelope.log_id = $crate::middleware::log_id().unwrap_or_default();
267266 let status = ::actix_web::http::StatusCode::from_u16(status)
268267 .unwrap_or(::actix_web::http::StatusCode::INTERNAL_SERVER_ERROR);
269268 ::actix_web::HttpResponse::build(status).json(envelope)
Modified lib/export/src/http.rs lib/export/src/http.rs +2 −3 View lib/export/src/http.rs
@@ -82,9 +82,8 @@
8282 if let Some(detail) = &self.detail {
8383 envelope = envelope.with_error(detail.clone());
8484 }
85 let envelope = envelope
86 .with_request_id(crate::middleware::request_id().unwrap_or_default())
87 .with_log_id(crate::middleware::log_id().unwrap_or_default());
85 let envelope =
86 envelope.with_request_id(crate::middleware::request_id().unwrap_or_default());
8887 HttpResponse::build(self.status).json(envelope)
8988 }
9089}
Modified lib/export/src/middleware.rs lib/export/src/middleware.rs +12 −29 View lib/export/src/middleware.rs
@@ -8,36 +8,24 @@
88use uuid::Uuid;
99
1010pub const REQUEST_ID_HEADER: HeaderName = HeaderName::from_static("x-request-id");
11pub const LOG_ID_HEADER: HeaderName = HeaderName::from_static("x-log-id");
1211
1312const MAX_INBOUND_ID_LEN: usize = 128;
14
15tokio::task_local! {
16 static REQUEST_ID: String;
17 static LOG_ID: String;
18}
1913
2014#[derive(Debug, Clone)]
2115pub struct RequestId(pub String);
2216
23#[derive(Debug, Clone)]
24pub struct LogId(pub String);
25
2617/// The request id of the request currently being handled, when one is set.
2718pub fn request_id() -> Option<String> {
28 REQUEST_ID.try_with(|id| id.clone()).ok()
29}
30
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()
19 envelope::context::request_id()
3420}
3521
3622/// Runs `future` with `request_id` available to nested code, so queue
3723/// consumers keep the id of the request that produced the message.
3824pub async fn with_request_id<F: Future>(request_id: Option<&str>, future: F) -> F::Output {
3925 match request_id {
40 Some(request_id) => REQUEST_ID.scope(request_id.to_string(), future).await,
26 Some(request_id) => {
27 envelope::context::with_request_id(request_id.to_string(), future).await
28 }
4129 None => future.await,
4230 }
4331}
@@ -53,27 +41,22 @@
5341where
5442 B: MessageBody + 'static,
5543{
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();
44 let request_id = request
45 .extensions()
46 .get::<::session::RequestId>()
47 .map(|value| value.0.clone())
48 .or_else(|| inbound_id(request.headers().get(&REQUEST_ID_HEADER)))
49 .unwrap_or_else(new_request_id);
5950
6051 request
6152 .extensions_mut()
6253 .insert(RequestId(request_id.clone()));
63 request.extensions_mut().insert(LogId(log_id.clone()));
6454
65 let response = REQUEST_ID
66 .scope(
67 request_id.clone(),
68 LOG_ID.scope(log_id.clone(), next.call(request)),
69 )
70 .await?;
55 let response =
56 envelope::context::with_request_id(request_id.clone(), next.call(request)).await?;
7157 let mut response = response.map_into_boxed_body();
7258 if let Ok(value) = HeaderValue::try_from(request_id.as_str()) {
7359 response.headers_mut().insert(REQUEST_ID_HEADER, value);
74 }
75 if let Ok(value) = HeaderValue::try_from(log_id.as_str()) {
76 response.headers_mut().insert(LOG_ID_HEADER, value);
7760 }
7861 Ok(response)
7962}
Modified lib/export/src/rest.rs lib/export/src/rest.rs +1 −131 View lib/export/src/rest.rs
@@ -1,131 +1 @@
1use actix_web::body::BoxBody;
2use actix_web::http::StatusCode;
3use actix_web::{HttpRequest, HttpResponse, Responder};
4use serde::{Deserialize, Serialize};
5use std::fmt;
6use utoipa::ToSchema;
7
8#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
9pub struct NoData;
10
11#[derive(Serialize, Deserialize, Debug, Clone, ToSchema)]
12pub struct AppResponseData<T> {
13 pub code: i32,
14 pub message: String,
15 pub data: Option<T>,
16 pub error: Option<String>,
17 pub error_code: Option<String>,
18 pub request_id: String,
19 pub log_id: String,
20}
21
22impl<T> AppResponseData<T>
23where
24 T: Serialize,
25{
26 pub fn ok(data: T) -> Self {
27 AppResponseData {
28 code: 200,
29 message: "success".to_string(),
30 data: Some(data),
31 error: None,
32 error_code: None,
33 request_id: String::new(),
34 log_id: String::new(),
35 }
36 }
37
38 pub fn err(code: i32, message: impl Into<String>) -> Self {
39 AppResponseData {
40 code,
41 message: message.into(),
42 data: None,
43 error: None,
44 error_code: None,
45 request_id: String::new(),
46 log_id: String::new(),
47 }
48 }
49
50 pub fn err_with_detail(
51 code: i32,
52 message: impl Into<String>,
53 detail: impl Into<String>,
54 ) -> Self {
55 AppResponseData {
56 code,
57 message: message.into(),
58 data: None,
59 error: Some(detail.into()),
60 error_code: None,
61 request_id: String::new(),
62 log_id: String::new(),
63 }
64 }
65
66 pub fn with_code(mut self, code: i32) -> Self {
67 self.code = code;
68 self
69 }
70
71 pub fn with_message(mut self, message: impl Into<String>) -> Self {
72 self.message = message.into();
73 self
74 }
75
76 pub fn with_error(mut self, error: impl Into<String>) -> Self {
77 self.error = Some(error.into());
78 self
79 }
80
81 pub fn with_error_code(mut self, code: impl Into<String>) -> Self {
82 self.error_code = Some(code.into());
83 self
84 }
85
86 pub fn with_request_id(mut self, request_id: impl Into<String>) -> Self {
87 self.request_id = request_id.into();
88 self
89 }
90
91 pub fn with_log_id(mut self, log_id: impl Into<String>) -> Self {
92 self.log_id = log_id.into();
93 self
94 }
95}
96
97impl AppResponseData<NoData> {
98 pub fn empty() -> Self {
99 AppResponseData {
100 code: 200,
101 message: "success".to_string(),
102 data: None,
103 error: None,
104 error_code: None,
105 request_id: String::new(),
106 log_id: String::new(),
107 }
108 }
109}
110
111impl fmt::Display for AppResponseData<()> {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 write!(f, "API error: code={}, message={}", self.code, self.message)
114 }
115}
116
117impl<T: Serialize> Responder for AppResponseData<T> {
118 type Body = BoxBody;
119
120 fn respond_to(mut self, _request: &HttpRequest) -> HttpResponse<Self::Body> {
121 let status =
122 StatusCode::from_u16(self.code as u16).unwrap_or(StatusCode::INTERNAL_SERVER_ERROR);
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)
130 }
131}
1pub use envelope::{Envelope as AppResponseData, NoData};
Modified lib/export/Cargo.toml lib/export/Cargo.toml +1 −0 View lib/export/Cargo.toml
@@ -16,6 +16,7 @@
1616redis = { workspace = true }
1717tokio = { workspace = true, features = ["macros", "rt-multi-thread", "signal"] }
1818session = { workspace = true }
19envelope = { workspace = true }
1920imaging = { workspace = true }
2021actix-web = { workspace = true }
2122http = "1.5.0"
Modified lib/session/src/error.rs lib/session/src/error.rs +0 −1 View lib/session/src/error.rs
@@ -122,7 +122,6 @@
122122 "error": null,
123123 "error_code": error_code,
124124 "request_id": null,
125 "log_id": null,
126125 }))
127126 }
128127}
Modified lib/webapi/src/openapi.rs lib/webapi/src/openapi.rs +1 −1 View lib/webapi/src/openapi.rs
@@ -17,7 +17,7 @@
1717| `data` | payload, `null` for errors and empty results |
1818| `error` | field-level / technical detail (e.g. validation errors), may be `null` |
1919| `error_code` | stable machine-readable code on 4xx (e.g. `conflict`, `rate_limited`), may be `null` |
20| `request_id` / `log_id` | correlation ids, also returned as `X-Request-Id` / `X-Log-Id` headers |
20| `request_id` | correlation id, also returned as the `X-Request-Id` header |
2121
2222Successful `POST` creation returns `200` (not `201`); empty results use `200` with `data: null`.
2323- `POST /auth/logout` is idempotent: repeating it always succeeds with `200`.
Modified Cargo.lock Cargo.lock +1 −0 View Cargo.lock
@@ -2335,6 +2335,7 @@
23352335 "cache",
23362336 "config 1.0.0",
23372337 "email",
2338 "envelope",
23382339 "events",
23392340 "http 1.5.0",
23402341 "imaging",