routa_server/api/
traces.rs1use axum::{
2 extract::{Query as QueryParams, State},
3 routing::get,
4 Json, Router,
5};
6use serde::Deserialize;
7
8use crate::error::ServerError;
9use crate::state::AppState;
10use routa_core::trace::{TraceQuery, TraceReader};
11
12pub fn router() -> Router<AppState> {
13 Router::new()
14 .route("/", get(query_traces).post(export_traces))
15 .route("/stats", get(get_trace_stats))
16 .route("/{id}", get(get_trace_by_id))
17}
18
19async fn query_traces(
31 State(_state): State<AppState>,
32 QueryParams(params): QueryParams<TraceQueryParams>,
33) -> Result<Json<serde_json::Value>, ServerError> {
34 let cwd = std::env::current_dir()
36 .map_err(|e| ServerError::Internal(format!("Failed to get cwd: {e}")))?;
37
38 let reader = TraceReader::new(&cwd);
39 let query = params.to_trace_query();
40
41 let traces = reader
42 .query(&query)
43 .await
44 .map_err(|e| ServerError::Internal(format!("Failed to query traces: {e}")))?;
45
46 Ok(Json(serde_json::json!({
47 "traces": traces,
48 "count": traces.len()
49 })))
50}
51
52async fn get_trace_stats(
54 State(_state): State<AppState>,
55) -> Result<Json<serde_json::Value>, ServerError> {
56 let cwd = std::env::current_dir()
57 .map_err(|e| ServerError::Internal(format!("Failed to get cwd: {e}")))?;
58
59 let reader = TraceReader::new(&cwd);
60 let stats = reader
61 .stats()
62 .await
63 .map_err(|e| ServerError::Internal(format!("Failed to get trace stats: {e}")))?;
64
65 Ok(Json(serde_json::json!({ "stats": stats })))
66}
67
68async fn get_trace_by_id(
70 State(_state): State<AppState>,
71 axum::extract::Path(id): axum::extract::Path<String>,
72) -> Result<Json<serde_json::Value>, ServerError> {
73 let cwd = std::env::current_dir()
74 .map_err(|e| ServerError::Internal(format!("Failed to get cwd: {e}")))?;
75
76 let reader = TraceReader::new(&cwd);
77 let trace = reader
78 .get_by_id(&id)
79 .await
80 .map_err(|e| ServerError::Internal(format!("Failed to get trace: {e}")))?;
81
82 match trace {
83 Some(trace) => Ok(Json(serde_json::json!({ "trace": trace }))),
84 None => Err(ServerError::NotFound(format!("Trace {id} not found"))),
85 }
86}
87
88async fn export_traces(
90 State(_state): State<AppState>,
91 QueryParams(params): QueryParams<TraceQueryParams>,
92) -> Result<Json<serde_json::Value>, ServerError> {
93 let cwd = std::env::current_dir()
94 .map_err(|e| ServerError::Internal(format!("Failed to get cwd: {e}")))?;
95
96 let reader = TraceReader::new(&cwd);
97 let query = params.to_trace_query();
98
99 let traces_json = reader
100 .export(&query)
101 .await
102 .map_err(|e| ServerError::Internal(format!("Failed to export traces: {e}")))?;
103
104 Ok(Json(serde_json::json!({
105 "export": traces_json,
106 "format": "agent-trace-json",
107 "version": "0.1.0"
108 })))
109}
110
111#[derive(Debug, Deserialize)]
113#[serde(rename_all = "camelCase")]
114struct TraceQueryParams {
115 session_id: Option<String>,
116 workspace_id: Option<String>,
117 file: Option<String>,
118 event_type: Option<String>,
119 start_date: Option<String>,
120 end_date: Option<String>,
121 limit: Option<usize>,
122 offset: Option<usize>,
123}
124
125impl TraceQueryParams {
126 fn to_trace_query(&self) -> TraceQuery {
127 TraceQuery {
128 session_id: self.session_id.clone(),
129 workspace_id: self.workspace_id.clone(),
130 file: self.file.clone(),
131 event_type: self.event_type.clone(),
132 start_date: self.start_date.clone(),
133 end_date: self.end_date.clone(),
134 limit: self.limit,
135 offset: self.offset,
136 }
137 }
138}