124 lines
4.4 KiB
Dart
124 lines
4.4 KiB
Dart
import 'dart:io';
|
|
import 'dart:convert';
|
|
|
|
class BandwidthService {
|
|
// In-memory collector state: collector_id -> peer_public_key -> {upload_last_seen, download_last_seen}
|
|
static final Map<String, Map<String, Map<String, int>>> _collectorStates = {};
|
|
|
|
/// Gets bandwidth stats for a collector
|
|
/// If collector_id is provided, returns incremental usage since last call
|
|
/// If no collector_id, returns total cumulative usage from WireGuard
|
|
static Future<Map<String, Map<String, int>>> getBandwidthStats(String? collectorId) async {
|
|
final currentStats = await _getCurrentWireGuardStats();
|
|
|
|
if (collectorId == null || collectorId.isEmpty) {
|
|
// No collector ID - return cumulative totals
|
|
return currentStats;
|
|
}
|
|
|
|
// Collector ID provided - calculate increments
|
|
return _calculateIncrementalStats(collectorId, currentStats);
|
|
}
|
|
|
|
/// Gets current WireGuard traffic statistics using 'wg show wg0 dump'
|
|
static Future<Map<String, Map<String, int>>> _getCurrentWireGuardStats() async {
|
|
try {
|
|
final result = await Process.run('wg', ['show', 'wg0', 'dump']);
|
|
if (result.exitCode != 0) {
|
|
throw Exception('Failed to get WireGuard stats: ${result.stderr}');
|
|
}
|
|
|
|
final output = result.stdout.toString().trim();
|
|
final lines = output.split('\n');
|
|
|
|
final stats = <String, Map<String, int>>{};
|
|
|
|
// Skip first line (server info) and process peer lines
|
|
for (int i = 1; i < lines.length; i++) {
|
|
final line = lines[i].trim();
|
|
if (line.isEmpty) continue;
|
|
|
|
final parts = line.split('\t');
|
|
if (parts.length >= 7) {
|
|
final publicKey = parts[0];
|
|
final uploadBytes = int.tryParse(parts[5]) ?? 0;
|
|
final downloadBytes = int.tryParse(parts[6]) ?? 0;
|
|
|
|
stats[publicKey] = {
|
|
'upload_bytes': uploadBytes,
|
|
'download_bytes': downloadBytes,
|
|
};
|
|
}
|
|
}
|
|
|
|
return stats;
|
|
} catch (e) {
|
|
print('Error getting WireGuard stats: $e');
|
|
return {};
|
|
}
|
|
}
|
|
|
|
/// Calculates incremental stats for a collector
|
|
static Map<String, Map<String, int>> _calculateIncrementalStats(
|
|
String collectorId,
|
|
Map<String, Map<String, int>> currentStats
|
|
) {
|
|
// Initialize collector state if it doesn't exist
|
|
_collectorStates[collectorId] ??= {};
|
|
|
|
final collectorState = _collectorStates[collectorId]!;
|
|
final incrementalStats = <String, Map<String, int>>{};
|
|
|
|
for (final entry in currentStats.entries) {
|
|
final publicKey = entry.key;
|
|
final currentUpload = entry.value['upload_bytes'] ?? 0;
|
|
final currentDownload = entry.value['download_bytes'] ?? 0;
|
|
|
|
// Get last seen values for this collector and peer
|
|
final lastSeen = collectorState[publicKey] ?? {'upload_bytes': 0, 'download_bytes': 0};
|
|
final lastSeenUpload = lastSeen['upload_bytes'] ?? 0;
|
|
final lastSeenDownload = lastSeen['download_bytes'] ?? 0;
|
|
|
|
// Calculate increments (handle potential WireGuard resets)
|
|
final uploadIncrement = _calculateSafeIncrement(currentUpload, lastSeenUpload);
|
|
final downloadIncrement = _calculateSafeIncrement(currentDownload, lastSeenDownload);
|
|
|
|
// Only include peers with non-zero usage (optimization)
|
|
if (uploadIncrement > 0 || downloadIncrement > 0) {
|
|
incrementalStats[publicKey] = {
|
|
'upload_bytes': uploadIncrement,
|
|
'download_bytes': downloadIncrement,
|
|
};
|
|
}
|
|
|
|
// Update collector state with current values
|
|
collectorState[publicKey] = {
|
|
'upload_bytes': currentUpload,
|
|
'download_bytes': currentDownload,
|
|
};
|
|
}
|
|
|
|
return incrementalStats;
|
|
}
|
|
|
|
/// Safely calculates increment, handling WireGuard resets
|
|
static int _calculateSafeIncrement(int current, int lastSeen) {
|
|
if (current >= lastSeen) {
|
|
return current - lastSeen;
|
|
} else {
|
|
// WireGuard likely reset (server restart) - use current value as increment
|
|
print('WireGuard reset detected: current=$current < lastSeen=$lastSeen');
|
|
return current;
|
|
}
|
|
}
|
|
|
|
/// Clears collector state (for debugging/maintenance)
|
|
static void clearCollectorState(String collectorId) {
|
|
_collectorStates.remove(collectorId);
|
|
}
|
|
|
|
/// Gets current collector state (for debugging)
|
|
static Map<String, Map<String, Map<String, int>>> getCollectorStates() {
|
|
return Map.from(_collectorStates);
|
|
}
|
|
} |