Files
proxypin/lib/network/components/manager/report_server_manager.dart
Pandaver ddad789d6f feat: add split report mode for report server
支持分离上报模式,开启后 request 和 response 分两次独立上报,
通过 _id 关联。解决 response 超时或无响应时请求无法上报的问题。
2026-04-21 12:53:44 +08:00

166 lines
3.9 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'dart:convert';
import '../../../storage/path.dart';
import '../../util/logger.dart';
class ReportServerManager {
static ReportServerManager? _instance;
List<ReportServer> _list = [];
///单例
static Future<ReportServerManager> get instance async {
if (_instance == null) {
_instance = ReportServerManager._internal();
await _instance!.loadConfig();
}
return _instance!;
}
// Private constructor
ReportServerManager._internal();
/// Get configured report servers
List<ReportServer> get servers => _list;
Future<ReportServer?> matchServer(String url) async {
final list = servers;
for (var server in list) {
if (server.match(url)) {
return server;
}
}
return null;
}
Future<void> add(ReportServer server) async {
_list.add(server);
await _flush();
}
Future<void> removeAt(int index) async {
final list = servers;
list.removeAt(index);
await _flush();
}
Future<void> update(int index, ReportServer server) async {
final list = servers;
server.updateUrlReg();
list[index] = server;
await _flush();
}
Future<void> toggleEnabled(int index, bool enabled) async {
final list = servers;
list[index] = list[index].copyWith(enabled: enabled);
await _flush();
}
Future<void> loadConfig() async {
var list = <ReportServer>[];
final file = await Paths.getPath("report_servers.json");
if (await file.exists()) {
final content = await file.readAsString();
if (content.trim().isNotEmpty) {
try {
final decoded = jsonDecode(content) as List<dynamic>;
list = decoded.map((e) => ReportServer.fromJson(e as Map<String, dynamic>)).toList();
} catch (e, t) {
logger.e('上报服务器配置解析失败', error: e, stackTrace: t);
}
}
}
_list = list;
}
Future<void> _flush() async {
final file = await Paths.getPath("report_servers.json");
final list = servers;
await file.writeAsString(jsonEncode(list.map((e) => e.toJson()).toList()));
}
}
class ReportServer {
final String name;
final String matchUrl;
/// 服务器URL
final String serverUrl;
/// 是否启用
final bool enabled;
/// 压缩方式none/gzip默认 none
final String? compression;
/// 分离上报request和response分开上报
final bool splitReport;
RegExp _urlReg;
ReportServer({
required this.name,
required this.matchUrl,
required this.serverUrl,
this.enabled = true,
this.compression,
this.splitReport = false,
}) : _urlReg = RegExp(matchUrl.replaceAll("*", ".*").replaceFirst('?', '\\?'));
bool match(String url) {
if (enabled) {
return _urlReg.hasMatch(url);
}
return false;
}
void updateUrlReg() {
_urlReg = RegExp(matchUrl.replaceAll("*", ".*").replaceFirst('?', '\\?'));
}
ReportServer copyWith({
String? name,
String? serverUrl,
bool? enabled,
String? matchUrl,
String? matchType,
String? compression,
bool? splitReport,
Map<String, String>? headers,
}) {
return ReportServer(
name: name ?? this.name,
matchUrl: matchUrl ?? this.matchUrl,
serverUrl: serverUrl ?? this.serverUrl,
enabled: enabled ?? this.enabled,
compression: compression ?? this.compression,
splitReport: splitReport ?? this.splitReport,
);
}
factory ReportServer.fromJson(Map<String, dynamic> json) {
return ReportServer(
name: json['name'] ?? '',
matchUrl: json['matchUrl'] ?? '',
serverUrl: json['serverUrl'] ?? '',
enabled: json['enabled'] ?? true,
compression: (json['compression'] ?? 'none') as String,
splitReport: json['splitReport'] ?? false,
);
}
Map<String, dynamic> toJson() {
return {
'name': name,
'matchUrl': matchUrl,
'serverUrl': serverUrl,
'enabled': enabled,
'compression': compression,
'splitReport': splitReport,
};
}
}