1use axum::{http::StatusCode, Json};
4use otelite_core::api::ErrorResponse;
5
6pub mod admin;
7pub mod genai;
8pub mod health;
9pub mod help;
10pub mod logs;
11pub mod metrics;
12pub mod resource_keys;
13pub mod sessions;
14pub mod stats;
15pub mod traces;
16
17pub use genai::get_token_usage;
18pub use health::health_check;
19pub use help::api_help;
20
21pub(crate) fn validate_time_range(
22 start_time: Option<i64>,
23 end_time: Option<i64>,
24) -> Result<(), (StatusCode, Json<ErrorResponse>)> {
25 if let Some(start_time) = start_time {
26 if start_time < 0 {
27 return Err(time_range_error("start_time must be non-negative"));
28 }
29 }
30
31 if let Some(end_time) = end_time {
32 if end_time < 0 {
33 return Err(time_range_error("end_time must be non-negative"));
34 }
35 }
36
37 if let (Some(start_time), Some(end_time)) = (start_time, end_time) {
38 if start_time >= end_time {
39 return Err(time_range_error("start_time must be less than end_time"));
40 }
41 }
42
43 Ok(())
44}
45
46fn time_range_error(message: &'static str) -> (StatusCode, Json<ErrorResponse>) {
47 (
48 StatusCode::BAD_REQUEST,
49 Json(ErrorResponse::bad_request(message)),
50 )
51}
52
53pub(crate) fn parse_query_param(
58 raw: &str,
59) -> Result<Vec<otelite_core::query::QueryPredicate>, (StatusCode, Json<ErrorResponse>)> {
60 let mut out = Vec::new();
61 for part in raw.split("&&").map(str::trim).filter(|p| !p.is_empty()) {
62 out.extend(otelite_core::query::parse_query(part).map_err(|e| {
63 (
64 StatusCode::BAD_REQUEST,
65 Json(ErrorResponse::bad_request(format!(
66 "Invalid query '{part}': {e}"
67 ))),
68 )
69 })?);
70 }
71 Ok(out)
72}