diff --git a/.vscode/settings.json b/.vscode/settings.json
index d97cead..42b4f74 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -67,6 +67,7 @@
"hkcu",
"hooksconfig",
"hookspath",
+ "Hoverable",
"icns",
"idlelib",
"idletime",
diff --git a/src-tauri/src/traffic_stats.rs b/src-tauri/src/traffic_stats.rs
index 25b3d1d..e9ceb36 100644
--- a/src-tauri/src/traffic_stats.rs
+++ b/src-tauri/src/traffic_stats.rs
@@ -17,6 +17,19 @@ pub struct BandwidthDataPoint {
pub bytes_received: u64,
}
+/// Individual domain access data point for time-series tracking
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct DomainAccessPoint {
+ /// Unix timestamp in seconds
+ pub timestamp: u64,
+ /// Domain name
+ pub domain: String,
+ /// Bytes sent in this request
+ pub bytes_sent: u64,
+ /// Bytes received in this request
+ pub bytes_received: u64,
+}
+
/// Domain access information
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DomainAccess {
@@ -78,9 +91,12 @@ pub struct TrafficStats {
/// Bandwidth data points (time-series, 1 point per second, stored indefinitely)
#[serde(default)]
pub bandwidth_history: Vec,
- /// Domain access statistics
+ /// Domain access statistics (aggregated all-time)
#[serde(default)]
pub domains: HashMap,
+ /// Domain access history (time-series for filtering by period)
+ #[serde(default)]
+ pub domain_access_history: Vec,
/// Unique IPs accessed
#[serde(default)]
pub unique_ips: Vec,
@@ -99,6 +115,7 @@ impl TrafficStats {
total_requests: 0,
bandwidth_history: Vec::new(),
domains: HashMap::new(),
+ domain_access_history: Vec::new(),
unique_ips: Vec::new(),
}
}
@@ -163,6 +180,7 @@ impl TrafficStats {
let now = current_timestamp();
self.total_requests += 1;
+ // Update aggregated domain stats
let entry = self
.domains
.entry(domain.to_string())
@@ -179,6 +197,14 @@ impl TrafficStats {
entry.bytes_sent += bytes_sent;
entry.bytes_received += bytes_received;
entry.last_access = now;
+
+ // Add to domain access history for time-period filtering
+ self.domain_access_history.push(DomainAccessPoint {
+ timestamp: now,
+ domain: domain.to_string(),
+ bytes_sent,
+ bytes_received,
+ });
}
/// Record an IP address access
@@ -362,6 +388,14 @@ fn merge_traffic_stats(dest: &mut TrafficStats, src: &TrafficStats) {
entry.last_access = entry.last_access.max(access.last_access);
}
+ // Merge domain access history
+ let mut combined_domain_history: Vec = dest.domain_access_history.clone();
+ for point in &src.domain_access_history {
+ combined_domain_history.push(point.clone());
+ }
+ combined_domain_history.sort_by_key(|p| p.timestamp);
+ dest.domain_access_history = combined_domain_history;
+
// Merge unique IPs
for ip in &src.unique_ips {
if !dest.unique_ips.contains(ip) {
@@ -557,7 +591,9 @@ pub struct FilteredTrafficStats {
/// Period stats: bytes sent/received within the requested period
pub period_bytes_sent: u64,
pub period_bytes_received: u64,
- /// Domain access statistics (always full, as it's already aggregated)
+ /// Period requests within the requested period
+ pub period_requests: u64,
+ /// Domain access statistics filtered to requested time period
pub domains: HashMap,
/// Unique IPs accessed
pub unique_ips: Vec,
@@ -586,10 +622,45 @@ pub fn get_traffic_stats_for_period(
.cloned()
.collect();
- // Calculate period totals
+ // Calculate period totals for bandwidth
let period_bytes_sent: u64 = filtered_history.iter().map(|dp| dp.bytes_sent).sum();
let period_bytes_received: u64 = filtered_history.iter().map(|dp| dp.bytes_received).sum();
+ // Filter and aggregate domain stats for the period
+ let mut filtered_domains: HashMap = HashMap::new();
+ let mut period_requests: u64 = 0;
+
+ for access in stats
+ .domain_access_history
+ .iter()
+ .filter(|a| a.timestamp >= cutoff)
+ {
+ period_requests += 1;
+ let entry = filtered_domains
+ .entry(access.domain.clone())
+ .or_insert(DomainAccess {
+ domain: access.domain.clone(),
+ request_count: 0,
+ bytes_sent: 0,
+ bytes_received: 0,
+ first_access: access.timestamp,
+ last_access: access.timestamp,
+ });
+
+ entry.request_count += 1;
+ entry.bytes_sent += access.bytes_sent;
+ entry.bytes_received += access.bytes_received;
+ entry.first_access = entry.first_access.min(access.timestamp);
+ entry.last_access = entry.last_access.max(access.timestamp);
+ }
+
+ // If no domain_access_history exists (old data), fall back to all-time domains
+ let domains = if stats.domain_access_history.is_empty() {
+ stats.domains
+ } else {
+ filtered_domains
+ };
+
Some(FilteredTrafficStats {
profile_id: stats.profile_id,
session_start: stats.session_start,
@@ -600,7 +671,8 @@ pub fn get_traffic_stats_for_period(
bandwidth_history: filtered_history,
period_bytes_sent,
period_bytes_received,
- domains: stats.domains,
+ period_requests,
+ domains,
unique_ips: stats.unique_ips,
})
}
diff --git a/src/components/traffic-details-dialog.tsx b/src/components/traffic-details-dialog.tsx
index 138f3d0..5c020a2 100644
--- a/src/components/traffic-details-dialog.tsx
+++ b/src/components/traffic-details-dialog.tsx
@@ -352,9 +352,11 @@ export function TrafficDetailsDialog({
-
Total Requests
+
+ Requests ({timePeriod === "all" ? "total" : timePeriod})
+
- {(stats?.total_requests || 0).toLocaleString()}
+ {(stats?.period_requests || 0).toLocaleString()}
@@ -362,14 +364,14 @@ export function TrafficDetailsDialog({
{/* Total Stats (smaller, under period stats) */}
- Total:{" "}
+ All-time traffic:{" "}
{formatBytes(
(stats?.total_bytes_sent || 0) +
(stats?.total_bytes_received || 0),
)}
- Requests:{" "}
+ All-time requests:{" "}
{stats?.total_requests?.toLocaleString() || 0}
@@ -385,7 +387,8 @@ export function TrafficDetailsDialog({
{topDomainsByTraffic.length > 0 && (
- Top Domains by Traffic
+ Top Domains by Traffic (
+ {timePeriod === "all" ? "all time" : timePeriod})
@@ -428,7 +431,8 @@ export function TrafficDetailsDialog({
{topDomainsByRequests.length > 0 && (
- Top Domains by Requests
+ Top Domains by Requests (
+ {timePeriod === "all" ? "all time" : timePeriod})
diff --git a/src/types.ts b/src/types.ts
index 70841a1..d7574af 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -320,6 +320,7 @@ export interface FilteredTrafficStats {
bandwidth_history: BandwidthDataPoint[];
period_bytes_sent: number;
period_bytes_received: number;
+ period_requests: number;
domains: Record;
unique_ips: string[];
}