1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
9#[serde(rename_all = "lowercase")]
10pub enum Granularity {
11 Minute,
12 Hour,
13 Day,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
18pub struct MetricsAggregate {
19 pub id: Option<i64>,
20 pub timestamp: i64,
21 pub protocol: String,
22 pub method: Option<String>,
23 pub endpoint: Option<String>,
24 pub status_code: Option<i32>,
25 pub workspace_id: Option<String>,
26 pub environment: Option<String>,
27 pub request_count: i64,
28 pub error_count: i64,
29 pub latency_sum: f64,
30 pub latency_min: Option<f64>,
31 pub latency_max: Option<f64>,
32 pub latency_p50: Option<f64>,
33 pub latency_p95: Option<f64>,
34 pub latency_p99: Option<f64>,
35 pub bytes_sent: i64,
36 pub bytes_received: i64,
37 pub active_connections: Option<i64>,
38 pub created_at: Option<i64>,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
43pub struct HourMetricsAggregate {
44 pub id: Option<i64>,
45 pub timestamp: i64,
46 pub protocol: String,
47 pub method: Option<String>,
48 pub endpoint: Option<String>,
49 pub status_code: Option<i32>,
50 pub workspace_id: Option<String>,
51 pub environment: Option<String>,
52 pub request_count: i64,
53 pub error_count: i64,
54 pub latency_sum: f64,
55 pub latency_min: Option<f64>,
56 pub latency_max: Option<f64>,
57 pub latency_p50: Option<f64>,
58 pub latency_p95: Option<f64>,
59 pub latency_p99: Option<f64>,
60 pub bytes_sent: i64,
61 pub bytes_received: i64,
62 pub active_connections_avg: Option<f64>,
63 pub active_connections_max: Option<i64>,
64 pub created_at: Option<i64>,
65}
66
67#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
69pub struct DayMetricsAggregate {
70 pub id: Option<i64>,
71 pub date: String,
72 pub timestamp: i64,
73 pub protocol: String,
74 pub method: Option<String>,
75 pub endpoint: Option<String>,
76 pub status_code: Option<i32>,
77 pub workspace_id: Option<String>,
78 pub environment: Option<String>,
79 pub request_count: i64,
80 pub error_count: i64,
81 pub latency_sum: f64,
82 pub latency_min: Option<f64>,
83 pub latency_max: Option<f64>,
84 pub latency_p50: Option<f64>,
85 pub latency_p95: Option<f64>,
86 pub latency_p99: Option<f64>,
87 pub bytes_sent: i64,
88 pub bytes_received: i64,
89 pub active_connections_avg: Option<f64>,
90 pub active_connections_max: Option<i64>,
91 pub unique_clients: Option<i64>,
92 pub peak_hour: Option<i32>,
93 pub created_at: Option<i64>,
94}
95
96#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
98pub struct EndpointStats {
99 pub id: Option<i64>,
100 pub endpoint: String,
101 pub protocol: String,
102 pub method: Option<String>,
103 pub workspace_id: Option<String>,
104 pub environment: Option<String>,
105 pub total_requests: i64,
106 pub total_errors: i64,
107 pub avg_latency_ms: Option<f64>,
108 pub min_latency_ms: Option<f64>,
109 pub max_latency_ms: Option<f64>,
110 pub p95_latency_ms: Option<f64>,
111 pub status_codes: Option<String>, pub total_bytes_sent: i64,
113 pub total_bytes_received: i64,
114 pub first_seen: i64,
115 pub last_seen: i64,
116 pub updated_at: Option<i64>,
117}
118
119#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct StatusCodeBreakdown {
122 pub status_codes: HashMap<u16, i64>,
123}
124
125impl EndpointStats {
126 pub fn get_status_code_breakdown(&self) -> Result<StatusCodeBreakdown, serde_json::Error> {
128 if let Some(ref json) = self.status_codes {
129 let map: HashMap<String, i64> = serde_json::from_str(json)?;
130 let status_codes = map
131 .into_iter()
132 .filter_map(|(k, v)| k.parse::<u16>().ok().map(|code| (code, v)))
133 .collect();
134 Ok(StatusCodeBreakdown { status_codes })
135 } else {
136 Ok(StatusCodeBreakdown {
137 status_codes: HashMap::new(),
138 })
139 }
140 }
141
142 pub fn set_status_code_breakdown(
144 &mut self,
145 breakdown: &StatusCodeBreakdown,
146 ) -> Result<(), serde_json::Error> {
147 let map: HashMap<String, i64> =
148 breakdown.status_codes.iter().map(|(k, v)| (k.to_string(), *v)).collect();
149 self.status_codes = Some(serde_json::to_string(&map)?);
150 Ok(())
151 }
152}
153
154#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
156pub struct ErrorEvent {
157 pub id: Option<i64>,
158 pub timestamp: i64,
159 pub protocol: String,
160 pub method: Option<String>,
161 pub endpoint: Option<String>,
162 pub status_code: Option<i32>,
163 pub error_type: Option<String>,
164 pub error_message: Option<String>,
165 pub error_category: Option<String>,
166 pub request_id: Option<String>,
167 pub trace_id: Option<String>,
168 pub span_id: Option<String>,
169 pub client_ip: Option<String>,
170 pub user_agent: Option<String>,
171 pub workspace_id: Option<String>,
172 pub environment: Option<String>,
173 pub metadata: Option<String>, pub created_at: Option<i64>,
175}
176
177#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
179#[serde(rename_all = "snake_case")]
180pub enum ErrorCategory {
181 ClientError, ServerError, NetworkError,
184 TimeoutError,
185 Other,
186}
187
188impl ErrorCategory {
189 pub fn from_status_code(status_code: u16) -> Self {
191 match status_code {
192 400..=499 => ErrorCategory::ClientError,
193 500..=599 => ErrorCategory::ServerError,
194 _ => ErrorCategory::Other,
195 }
196 }
197
198 pub fn as_str(&self) -> &'static str {
200 match self {
201 ErrorCategory::ClientError => "client_error",
202 ErrorCategory::ServerError => "server_error",
203 ErrorCategory::NetworkError => "network_error",
204 ErrorCategory::TimeoutError => "timeout_error",
205 ErrorCategory::Other => "other",
206 }
207 }
208}
209
210#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
212pub struct ClientAnalytics {
213 pub id: Option<i64>,
214 pub timestamp: i64,
215 pub client_ip: String,
216 pub user_agent: Option<String>,
217 pub user_agent_family: Option<String>,
218 pub user_agent_version: Option<String>,
219 pub protocol: String,
220 pub workspace_id: Option<String>,
221 pub environment: Option<String>,
222 pub request_count: i64,
223 pub error_count: i64,
224 pub avg_latency_ms: Option<f64>,
225 pub bytes_sent: i64,
226 pub bytes_received: i64,
227 pub top_endpoints: Option<String>, pub created_at: Option<i64>,
229}
230
231#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
233pub struct TrafficPattern {
234 pub id: Option<i64>,
235 pub date: String,
236 pub hour: i32,
237 pub day_of_week: i32,
238 pub protocol: String,
239 pub workspace_id: Option<String>,
240 pub environment: Option<String>,
241 pub request_count: i64,
242 pub error_count: i64,
243 pub avg_latency_ms: Option<f64>,
244 pub unique_clients: Option<i64>,
245 pub created_at: Option<i64>,
246}
247
248#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)]
250pub struct AnalyticsSnapshot {
251 pub id: Option<i64>,
252 pub timestamp: i64,
253 pub snapshot_type: String,
254 pub total_requests: i64,
255 pub total_errors: i64,
256 pub avg_latency_ms: Option<f64>,
257 pub active_connections: Option<i64>,
258 pub protocol_stats: Option<String>, pub top_endpoints: Option<String>, pub memory_usage_bytes: Option<i64>,
261 pub cpu_usage_percent: Option<f64>,
262 pub thread_count: Option<i32>,
263 pub uptime_seconds: Option<i64>,
264 pub workspace_id: Option<String>,
265 pub environment: Option<String>,
266 pub created_at: Option<i64>,
267}
268
269#[derive(Debug, Clone, Default, Serialize, Deserialize)]
271pub struct AnalyticsFilter {
272 pub start_time: Option<i64>,
273 pub end_time: Option<i64>,
274 pub protocol: Option<String>,
275 pub endpoint: Option<String>,
276 pub method: Option<String>,
277 pub status_code: Option<i32>,
278 pub workspace_id: Option<String>,
279 pub environment: Option<String>,
280 pub limit: Option<i64>,
281}
282
283#[derive(Debug, Clone, Serialize, Deserialize)]
285pub struct OverviewMetrics {
286 pub total_requests: i64,
287 pub total_errors: i64,
288 pub error_rate: f64,
289 pub avg_latency_ms: f64,
290 pub p95_latency_ms: f64,
291 pub p99_latency_ms: f64,
292 pub active_connections: i64,
293 pub total_bytes_sent: i64,
294 pub total_bytes_received: i64,
295 pub requests_per_second: f64,
296 pub top_protocols: Vec<ProtocolStat>,
297 pub top_endpoints: Vec<EndpointStat>,
298}
299
300#[derive(Debug, Clone, Serialize, Deserialize)]
302pub struct ProtocolStat {
303 pub protocol: String,
304 pub request_count: i64,
305 pub error_count: i64,
306 pub avg_latency_ms: f64,
307}
308
309#[derive(Debug, Clone, Serialize, Deserialize)]
311pub struct EndpointStat {
312 pub endpoint: String,
313 pub protocol: String,
314 pub method: Option<String>,
315 pub request_count: i64,
316 pub error_count: i64,
317 pub error_rate: f64,
318 pub avg_latency_ms: f64,
319 pub p95_latency_ms: f64,
320}
321
322#[derive(Debug, Clone, Serialize, Deserialize)]
324pub struct TimeSeriesPoint {
325 pub timestamp: i64,
326 pub value: f64,
327}
328
329#[derive(Debug, Clone, Serialize, Deserialize)]
331pub struct TimeSeries {
332 pub label: String,
333 pub data: Vec<TimeSeriesPoint>,
334}
335
336#[derive(Debug, Clone, Serialize, Deserialize)]
338pub struct LatencyTrend {
339 pub timestamp: i64,
340 pub p50: f64,
341 pub p95: f64,
342 pub p99: f64,
343 pub avg: f64,
344 pub min: f64,
345 pub max: f64,
346}
347
348#[derive(Debug, Clone, Serialize, Deserialize)]
350pub struct ErrorSummary {
351 pub error_type: String,
352 pub error_category: String,
353 pub count: i64,
354 pub endpoints: Vec<String>,
355 pub last_occurrence: DateTime<Utc>,
356}
357
358#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
360#[serde(rename_all = "lowercase")]
361pub enum ExportFormat {
362 Csv,
363 Json,
364}