Skip to main content

systemprompt_cli/commands/analytics/traffic/
mod.rs

1//! Traffic analytics: source, geographic, device, and bot-traffic breakdowns.
2//!
3//! Defines the [`TrafficCommands`] subcommand tree and the typed output shapes
4//! ([`TrafficSourcesOutput`], [`GeoOutput`], [`DevicesOutput`], [`BotsOutput`])
5//! rendered by the `analytics traffic` commands.
6
7mod bots;
8mod devices;
9mod geo;
10mod sources;
11
12use anyhow::Result;
13use clap::Subcommand;
14use schemars::JsonSchema;
15use serde::{Deserialize, Serialize};
16
17use crate::context::CommandContext;
18use crate::shared::render_result;
19
20#[derive(Debug, Subcommand)]
21pub enum TrafficCommands {
22    #[command(about = "Traffic source breakdown", alias = "list")]
23    Sources(sources::SourcesArgs),
24
25    #[command(about = "Geographic distribution")]
26    Geo(geo::GeoArgs),
27
28    #[command(about = "Device and browser breakdown")]
29    Devices(devices::DevicesArgs),
30
31    #[command(about = "Bot traffic analysis")]
32    Bots(bots::BotsArgs),
33}
34
35#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
36pub struct TrafficSourceRow {
37    pub source: String,
38    pub session_count: i64,
39    pub percentage: f64,
40}
41
42#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
43pub struct TrafficSourcesOutput {
44    pub period: String,
45    pub sources: Vec<TrafficSourceRow>,
46    pub total_sessions: i64,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
50pub struct GeoRow {
51    pub country: String,
52    pub session_count: i64,
53    pub percentage: f64,
54}
55
56#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
57pub struct GeoOutput {
58    pub period: String,
59    pub countries: Vec<GeoRow>,
60    pub total_sessions: i64,
61}
62
63#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
64pub struct DeviceRow {
65    pub device_type: String,
66    pub browser: String,
67    pub session_count: i64,
68    pub percentage: f64,
69}
70
71#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
72pub struct DevicesOutput {
73    pub period: String,
74    pub devices: Vec<DeviceRow>,
75    pub total_sessions: i64,
76}
77
78#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
79pub struct BotRow {
80    pub bot_type: String,
81    pub request_count: i64,
82    pub percentage: f64,
83}
84
85#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
86pub struct BotsOutput {
87    pub period: String,
88    pub human_sessions: i64,
89    pub bot_sessions: i64,
90    pub bot_percentage: f64,
91    pub bot_breakdown: Vec<BotRow>,
92}
93
94pub async fn execute(command: TrafficCommands, ctx: &CommandContext) -> Result<()> {
95    let db_ctx = ctx.database().await?;
96    match command {
97        TrafficCommands::Sources(args) => {
98            let result = sources::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
99            render_result(&result, &ctx.cli);
100            Ok(())
101        },
102        TrafficCommands::Geo(args) => {
103            let result = geo::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
104            render_result(&result, &ctx.cli);
105            Ok(())
106        },
107        TrafficCommands::Devices(args) => {
108            let result = devices::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
109            render_result(&result, &ctx.cli);
110            Ok(())
111        },
112        TrafficCommands::Bots(args) => {
113            let result = bots::execute_with_pool(args, &db_ctx, &ctx.cli).await?;
114            render_result(&result, &ctx.cli);
115            Ok(())
116        },
117    }
118}