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};
16use systemprompt_runtime::DatabaseContext;
17
18use crate::CliConfig;
19use crate::shared::render_result;
20
21#[derive(Debug, Subcommand)]
22pub enum TrafficCommands {
23    #[command(about = "Traffic source breakdown", alias = "list")]
24    Sources(sources::SourcesArgs),
25
26    #[command(about = "Geographic distribution")]
27    Geo(geo::GeoArgs),
28
29    #[command(about = "Device and browser breakdown")]
30    Devices(devices::DevicesArgs),
31
32    #[command(about = "Bot traffic analysis")]
33    Bots(bots::BotsArgs),
34}
35
36#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
37pub struct TrafficSourceRow {
38    pub source: String,
39    pub session_count: i64,
40    pub percentage: f64,
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
44pub struct TrafficSourcesOutput {
45    pub period: String,
46    pub sources: Vec<TrafficSourceRow>,
47    pub total_sessions: i64,
48}
49
50#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
51pub struct GeoRow {
52    pub country: String,
53    pub session_count: i64,
54    pub percentage: f64,
55}
56
57#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
58pub struct GeoOutput {
59    pub period: String,
60    pub countries: Vec<GeoRow>,
61    pub total_sessions: i64,
62}
63
64#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
65pub struct DeviceRow {
66    pub device_type: String,
67    pub browser: String,
68    pub session_count: i64,
69    pub percentage: f64,
70}
71
72#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
73pub struct DevicesOutput {
74    pub period: String,
75    pub devices: Vec<DeviceRow>,
76    pub total_sessions: i64,
77}
78
79#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
80pub struct BotRow {
81    pub bot_type: String,
82    pub request_count: i64,
83    pub percentage: f64,
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
87pub struct BotsOutput {
88    pub period: String,
89    pub human_sessions: i64,
90    pub bot_sessions: i64,
91    pub bot_percentage: f64,
92    pub bot_breakdown: Vec<BotRow>,
93}
94
95pub async fn execute(command: TrafficCommands, config: &CliConfig) -> Result<()> {
96    match command {
97        TrafficCommands::Sources(args) => {
98            let result = sources::execute(args, config).await?;
99            render_result(&result);
100            Ok(())
101        },
102        TrafficCommands::Geo(args) => {
103            let result = geo::execute(args, config).await?;
104            render_result(&result);
105            Ok(())
106        },
107        TrafficCommands::Devices(args) => {
108            let result = devices::execute(args, config).await?;
109            render_result(&result);
110            Ok(())
111        },
112        TrafficCommands::Bots(args) => {
113            let result = bots::execute(args, config).await?;
114            render_result(&result);
115            Ok(())
116        },
117    }
118}
119
120pub async fn execute_with_pool(
121    command: TrafficCommands,
122    db_ctx: &DatabaseContext,
123    config: &CliConfig,
124) -> Result<()> {
125    match command {
126        TrafficCommands::Sources(args) => {
127            let result = sources::execute_with_pool(args, db_ctx, config).await?;
128            render_result(&result);
129            Ok(())
130        },
131        TrafficCommands::Geo(args) => {
132            let result = geo::execute_with_pool(args, db_ctx, config).await?;
133            render_result(&result);
134            Ok(())
135        },
136        TrafficCommands::Devices(args) => {
137            let result = devices::execute_with_pool(args, db_ctx, config).await?;
138            render_result(&result);
139            Ok(())
140        },
141        TrafficCommands::Bots(args) => {
142            let result = bots::execute_with_pool(args, db_ctx, config).await?;
143            render_result(&result);
144            Ok(())
145        },
146    }
147}