feat(frontend): request logs; refactor frontend components

This commit is contained in:
2025-12-01 18:48:22 +08:00
parent d783cf2591
commit a9cb9510c5
28 changed files with 649 additions and 160 deletions

View File

@@ -2,7 +2,7 @@ use std::str;
use std::sync::Arc;
use axum::{
extract::{OriginalUri, State},
extract::State,
http::{Request, StatusCode},
middleware::Next,
response::{IntoResponse, Response},
@@ -11,7 +11,7 @@ use http_body_util::BodyExt;
use serde_json::Value;
use tracing::{debug, info, warn};
use crate::{AppState, crypto};
use crate::{AppState, crypto, db};
enum ResponseBody {
Original(String),
@@ -20,7 +20,6 @@ enum ResponseBody {
pub async fn middleware(
State(state): State<Arc<AppState>>,
OriginalUri(uri): OriginalUri,
req: Request<axum::body::Body>,
next: Next,
) -> impl IntoResponse {
@@ -37,9 +36,7 @@ pub async fn middleware(
}
};
let path = uri.path();
let (decrypted_request_for_log, method) = match str::from_utf8(&body_bytes)
let (decrypted_request, method) = match str::from_utf8(&body_bytes)
.map_err(anyhow::Error::from)
.and_then(|body| process_and_log_request(body, &state.key, &state.iv))
{
@@ -78,9 +75,8 @@ pub async fn middleware(
let resp_body_text = String::from_utf8(body_bytes.clone().to_vec()).unwrap_or_default();
// Check for generic method interception first
let response_body_to_log = if let Some(method_str) = &method {
if let Ok(Some(intercepted)) =
maybe_intercept_response(method_str, &resp_body_text, &state).await
let response_body = if let Some(method_str) = &method {
if let Ok(Some(intercepted)) = intercept_response(method_str, &resp_body_text, &state).await
{
info!("Intercepting response for method: {}", method_str);
ResponseBody::Original(intercepted)
@@ -108,28 +104,46 @@ pub async fn middleware(
ResponseBody::Original(resp_body_text.clone())
};
let (decrypted_response_for_log, final_response_body) = match response_body_to_log {
let (decrypted_response, final_response_body) = match response_body {
ResponseBody::Original(body_text) => {
let decrypted = decrypt_and_format(&body_text, &state.key, &state.iv)
.unwrap_or_else(|_| "Could not decrypt or format response".to_string());
let decrypted =
decrypt_and_format(&body_text, &state.key, &state.iv).unwrap_or_else(|_| {
Value::String("Could not decrypt or format response".to_string())
});
(decrypted, body_text)
}
ResponseBody::Modified(body_value) => {
let pretty_printed = serde_json::to_string_pretty(&body_value).unwrap_or_default();
ResponseBody::Modified(response_body_value) => {
let pretty_printed =
serde_json::to_string_pretty(&response_body_value).unwrap_or_default();
let encrypted = crypto::encrypt(&pretty_printed, &state.key, &state.iv)
.unwrap_or_else(|_| "Failed to encrypt modified response".to_string());
(pretty_printed, encrypted)
(response_body_value, encrypted)
}
};
debug!(
"{}\nRequest:\n{}\nResponse:\n{}\n{}",
path,
serde_json::to_string_pretty(&decrypted_request_for_log).unwrap_or_default(),
decrypted_response_for_log,
"\nRequest:\n{}\nResponse:\n{}\n{}",
serde_json::to_string_pretty(&decrypted_request).unwrap_or_default(),
serde_json::to_string_pretty(&decrypted_response).unwrap_or_default(),
"-".repeat(80),
);
if let Some(method) = method {
// TODO: interception action
if let Err(e) = db::repositories::logs::create(
&state.db,
method,
decrypted_request,
decrypted_response,
"".into(),
"".into(),
)
.await
{
warn!("Failed to log request: {}", e);
}
}
let mut response_builder = Response::builder().status(resp_parts.status);
if !resp_parts.headers.is_empty() {
*response_builder.headers_mut().unwrap() = resp_parts.headers;
@@ -220,13 +234,12 @@ async fn handle_getcommand_response(
Ok(response_json)
}
fn decrypt_and_format(body_text: &str, key: &str, iv: &str) -> anyhow::Result<String> {
fn decrypt_and_format(body_text: &str, key: &str, iv: &str) -> anyhow::Result<Value> {
let decrypted = crypto::decrypt(body_text, key, iv)?;
let formatted: Value = serde_json::from_str(&decrypted)?;
Ok(serde_json::to_string_pretty(&formatted)?)
Ok(serde_json::from_str(&decrypted)?)
}
async fn maybe_intercept_response(
async fn intercept_response(
method: &str,
_original_response: &str,
state: &Arc<AppState>,