Files
FlClash/lib/core/interface.dart
T

621 lines
17 KiB
Dart
Raw Normal View History

2025-07-31 17:09:18 +08:00
import 'dart:async';
import 'package:fl_clash/common/common.dart';
import 'package:fl_clash/enum/enum.dart';
2025-07-31 17:09:18 +08:00
import 'package:fl_clash/models/models.dart';
2026-08-05 10:55:33 +08:00
import 'desktop/model.dart';
import 'method.dart';
2025-07-31 17:09:18 +08:00
mixin CoreInterface {
2026-08-05 10:55:33 +08:00
Future<CoreLifecycleResult> start();
Future<CoreLifecycleResult> restart();
2025-07-31 17:09:18 +08:00
2026-08-05 10:55:33 +08:00
Future<CoreLifecycleResult> stop();
2025-07-31 17:09:18 +08:00
2026-08-05 10:55:33 +08:00
Future<CoreLifecycleResult> close();
Future<bool> init(InitParams params);
2025-07-31 17:09:18 +08:00
Future<bool> get isInit;
Future<bool> forceGc();
2025-09-23 21:02:47 +08:00
Future<String> validateConfig(String path);
2025-07-31 17:09:18 +08:00
Future<String> decryptConfig(String path, String ageSecretKey);
Future<List<String>> validateProxies(List<Map<String, dynamic>> proxies);
Future<List<String>> validateFilters(List<String> filters);
Future<List<Map<String, dynamic>>> convertProxies(String path);
Future<List<String>> encodeShareLinks(List<Map<String, dynamic>> proxies);
Future<List<List<Map<String, dynamic>>>> decodeShareLinks(List<String> lines);
2026-08-05 10:55:33 +08:00
Future<Map<String, dynamic>> getConfig(String path);
2025-07-31 17:09:18 +08:00
Future<ProfileBase> getProfileBase(String path);
Future<Map<String, dynamic>> getProfileOutbounds(String path);
Future<String> buildProfile(SetupBase base);
Future<String> dumpRuleSet(String path);
Future<RuleSetInfo> compileRuleSet(String name, {String url = ''});
Future<Delay?> asyncTestDelay(String url, String proxyName);
2025-07-31 17:09:18 +08:00
Future<ProbeResult?> probe(ProbeParams params);
Future<OutboundIpResult?> outboundIp(OutboundIpParams params);
Future<List<ServiceCheckItem>> serviceCheck(ServiceCheckParams params);
2025-07-31 17:09:18 +08:00
Future<String> updateConfig(UpdateParams updateParams);
Future<String> setupConfig(SetupParams setupParams);
2025-12-16 11:23:09 +08:00
Future<ProxiesData> getProxies();
2025-07-31 17:09:18 +08:00
Future<ChangeProxyResult> changeProxy(ChangeProxyParams changeProxyParams);
Future<RouteSnapshot?> watchRoute(bool watch);
2025-07-31 17:09:18 +08:00
Future<bool> startListener();
Future<bool> stopListener();
2026-08-05 10:55:33 +08:00
Future<List<ExternalProvider>> getExternalProviders();
2025-07-31 17:09:18 +08:00
2026-08-05 10:55:33 +08:00
Future<ExternalProvider?> getExternalProvider(String externalProviderName);
2025-07-31 17:09:18 +08:00
2026-05-27 15:16:51 +08:00
Future<String> updateGeoData(String type);
2025-07-31 17:09:18 +08:00
Future<String> sideLoadExternalProvider({
required String providerName,
required String data,
});
Future<String> updateExternalProvider(String providerName);
2026-08-05 10:55:33 +08:00
FutureOr<Traffic> getTraffic(bool onlyStatisticsProxy);
2025-07-31 17:09:18 +08:00
2026-08-05 10:55:33 +08:00
FutureOr<Traffic> getTotalTraffic(bool onlyStatisticsProxy);
2025-07-31 17:09:18 +08:00
FutureOr<CoreMemoryStats?> getMemoryStats();
2025-07-31 17:09:18 +08:00
FutureOr<void> resetTraffic();
FutureOr<void> startLog();
FutureOr<void> stopLog();
Future<bool> crash();
2026-08-05 10:55:33 +08:00
FutureOr<List<TrackerInfo>> getConnections();
2025-07-31 17:09:18 +08:00
FutureOr<int> getConnectionCount();
2025-07-31 17:09:18 +08:00
FutureOr<bool> closeConnection(String id);
2026-08-05 10:55:33 +08:00
FutureOr<String> clearEffect(int profileId);
2025-07-31 17:09:18 +08:00
FutureOr<bool> closeConnections();
FutureOr<bool> resetConnections();
}
abstract class CoreHandlerInterface with CoreInterface {
2026-08-05 10:55:33 +08:00
Future<T?> _invokeMethod<T>({
required CoreMethod method,
Object? arguments,
2025-07-31 17:09:18 +08:00
Duration? timeout,
}) async {
return await handleWatch(
2026-02-02 10:14:13 +08:00
onStart: () {
2026-08-05 10:55:33 +08:00
commonPrint.log(
'Invoke method ${method.name} ${DateTime.now()} $arguments',
);
2026-02-02 10:14:13 +08:00
},
2025-09-23 21:02:47 +08:00
function: () async {
2026-08-05 10:55:33 +08:00
return invokeMethod<T>(
method: method,
arguments: arguments,
timeout: timeout,
);
2025-09-23 21:02:47 +08:00
},
2026-08-05 10:55:33 +08:00
onEnd: (result, elapsedMilliseconds) {
commonPrint.log(
'Invoke method ${method.name} completed in ${elapsedMilliseconds}ms',
);
2025-09-23 21:02:47 +08:00
},
);
2025-07-31 17:09:18 +08:00
}
2026-08-05 10:55:33 +08:00
Future<T?> invokeMethod<T>({
required CoreMethod method,
Object? arguments,
2025-07-31 17:09:18 +08:00
Duration? timeout,
});
Future<String> _invokeMessage({
required CoreMethod method,
Object? arguments,
Duration? timeout,
}) async {
final message = await _invokeMethod<String>(
method: method,
arguments: arguments,
timeout: timeout,
);
if (message == null) {
throw CoreMethodException(
code: 'no_response',
message: 'Core did not answer ${method.name}',
);
}
return message;
}
2025-07-31 17:09:18 +08:00
@override
Future<bool> init(InitParams params) async {
2026-08-05 10:55:33 +08:00
return await _invokeMethod<bool>(
method: CoreMethod.initClash,
arguments: params.toJson(),
2025-07-31 17:09:18 +08:00
) ??
false;
}
@override
Future<bool> get isInit async {
2026-08-05 10:55:33 +08:00
return await _invokeMethod<bool>(method: CoreMethod.getIsInit) ?? false;
2025-07-31 17:09:18 +08:00
}
@override
Future<bool> forceGc() async {
2026-08-05 10:55:33 +08:00
return await _invokeMethod<bool>(method: CoreMethod.forceGc) ?? false;
2025-07-31 17:09:18 +08:00
}
@override
2025-09-23 21:02:47 +08:00
Future<String> validateConfig(String path) async {
return _invokeMessage(method: CoreMethod.validateConfig, arguments: path);
2025-07-31 17:09:18 +08:00
}
@override
Future<String> decryptConfig(String path, String ageSecretKey) async {
return _invokeMessage(
method: CoreMethod.decryptConfig,
arguments: {'path': path, 'age-secret-key': ageSecretKey},
);
}
@override
Future<List<String>> validateProxies(
List<Map<String, dynamic>> proxies,
) async {
final data = await _invokeMethod<List<dynamic>>(
method: CoreMethod.validateProxies,
arguments: proxies,
);
if (data == null || data.length != proxies.length) {
throw CoreMethodException(
code: 'no_response',
message: 'Core did not answer ${CoreMethod.validateProxies.name}',
);
}
return data.map((item) => item?.toString() ?? '').toList();
}
@override
Future<List<String>> validateFilters(List<String> filters) async {
final data = await _invokeMethod<List<dynamic>>(
method: CoreMethod.validateFilters,
arguments: filters,
);
if (data == null || data.length != filters.length) {
throw CoreMethodException(
code: 'no_response',
message: 'Core did not answer ${CoreMethod.validateFilters.name}',
);
}
return data.map((item) => item?.toString() ?? '').toList();
}
@override
Future<List<Map<String, dynamic>>> convertProxies(String path) async {
final data = await _invokeMethod<List<dynamic>>(
method: CoreMethod.convertProxies,
arguments: path,
);
return [
for (final item in data ?? const [])
if (item is Map) Map<String, dynamic>.from(item),
];
}
@override
Future<List<String>> encodeShareLinks(
List<Map<String, dynamic>> proxies,
) async {
final data = await _invokeMethod<List<dynamic>>(
method: CoreMethod.encodeShareLinks,
arguments: proxies,
);
if (data == null || data.length != proxies.length) {
throw CoreMethodException(
code: 'no_response',
message: 'Core did not answer ${CoreMethod.encodeShareLinks.name}',
);
}
return data.map((item) => item?.toString() ?? '').toList();
}
@override
Future<List<List<Map<String, dynamic>>>> decodeShareLinks(
List<String> lines,
) async {
final data = await _invokeMethod<List<dynamic>>(
method: CoreMethod.decodeShareLinks,
arguments: lines,
);
if (data == null || data.length != lines.length) {
throw CoreMethodException(
code: 'no_response',
message: 'Core did not answer ${CoreMethod.decodeShareLinks.name}',
);
}
return [
for (final line in data)
[
for (final item in line is List ? line : const [])
if (item is Map) Map<String, dynamic>.from(item),
],
];
}
2025-07-31 17:09:18 +08:00
@override
Future<String> updateConfig(UpdateParams updateParams) async {
return _invokeMessage(
method: CoreMethod.updateConfig,
arguments: updateParams.toJson(),
);
2025-07-31 17:09:18 +08:00
}
@override
2026-08-05 10:55:33 +08:00
Future<Map<String, dynamic>> getConfig(String path) async {
final result = await _invokeMethod<Map<String, dynamic>>(
method: CoreMethod.getConfig,
arguments: path,
);
if (result == null) {
throw const CoreMethodException(
code: 'empty_result',
message: 'Core returned an empty config result',
);
}
return result;
2025-07-31 17:09:18 +08:00
}
@override
Future<ProfileBase> getProfileBase(String path) async {
final result = await _invokeMethod<Map<String, dynamic>>(
method: CoreMethod.getProfileBase,
arguments: path,
);
if (result == null) {
throw const CoreMethodException(
code: 'empty_result',
message: 'Core returned an empty profile base',
);
}
return ProfileBase.fromJson(result);
}
@override
Future<Map<String, dynamic>> getProfileOutbounds(String path) async {
final result = await _invokeMethod<Map<String, dynamic>>(
method: CoreMethod.getProfileOutbounds,
arguments: path,
);
if (result == null) {
throw const CoreMethodException(
code: 'empty_result',
message: 'Core returned no outbounds',
);
}
return result;
}
@override
Future<String> buildProfile(SetupBase base) async {
final result = await _invokeMethod<String>(
method: CoreMethod.buildProfile,
arguments: base.toJson(),
);
if (result == null || result.isEmpty) {
throw const CoreMethodException(
code: 'empty_result',
message: 'Core returned no built profile',
);
}
return result;
}
2025-07-31 17:09:18 +08:00
@override
Future<String> setupConfig(SetupParams setupParams) async {
return _invokeMessage(
method: CoreMethod.setupConfig,
arguments: setupParams.toJson(),
);
2025-07-31 17:09:18 +08:00
}
@override
Future<String> dumpRuleSet(String path) async {
return _invokeMessage(method: CoreMethod.dumpRuleSet, arguments: path);
}
@override
Future<RuleSetInfo> compileRuleSet(String name, {String url = ''}) async {
final data = await _invokeMethod<Map<String, dynamic>>(
method: CoreMethod.compileRuleSet,
arguments: {'name': name, 'url': url},
);
if (data == null) {
throw const CoreMethodException(
code: 'no_response',
message: 'Core did not answer compileRuleSet',
);
}
return (
behavior: RuleProviderBehavior.values.byName(data['behavior'] as String),
format: RuleProviderFormat.values.byName(data['format'] as String),
);
}
2025-07-31 17:09:18 +08:00
@override
Future<bool> crash() async {
2026-08-05 10:55:33 +08:00
return await _invokeMethod<bool>(method: CoreMethod.crash) ?? false;
2025-07-31 17:09:18 +08:00
}
@override
2025-12-16 11:23:09 +08:00
Future<ProxiesData> getProxies() async {
2026-08-05 10:55:33 +08:00
final data = await _invokeMethod<Map<String, dynamic>>(
method: CoreMethod.getProxies,
2025-12-16 11:23:09 +08:00
);
return data != null
? ProxiesData.fromJson(data)
2026-02-02 10:14:13 +08:00
: const ProxiesData(proxies: {}, all: []);
2025-07-31 17:09:18 +08:00
}
@override
Future<ChangeProxyResult> changeProxy(
ChangeProxyParams changeProxyParams,
) async {
final data = await _invokeMethod<Map<String, dynamic>>(
method: CoreMethod.changeProxy,
arguments: changeProxyParams.toJson(),
);
if (data == null) {
throw CoreMethodException(
code: 'no_response',
message: 'Core did not answer ${CoreMethod.changeProxy.name}',
);
}
return ChangeProxyResult.fromJson(data);
}
@override
Future<RouteSnapshot?> watchRoute(bool watch) async {
final data = await _invokeMethod<Map<String, dynamic>>(
method: CoreMethod.watchRoute,
arguments: watch,
);
return data == null ? null : RouteSnapshot.fromJson(data);
2025-07-31 17:09:18 +08:00
}
@override
2026-08-05 10:55:33 +08:00
Future<List<ExternalProvider>> getExternalProviders() async {
final data = await _invokeMethod<List<dynamic>>(
method: CoreMethod.getExternalProviders,
);
return data
?.whereType<Map>()
.map(
(item) =>
ExternalProvider.fromJson(Map<String, Object?>.from(item)),
)
.toList() ??
[];
2025-07-31 17:09:18 +08:00
}
@override
2026-08-05 10:55:33 +08:00
Future<ExternalProvider?> getExternalProvider(
String externalProviderName,
) async {
final data = await _invokeMethod<Map<String, dynamic>>(
method: CoreMethod.getExternalProvider,
arguments: externalProviderName,
);
return data == null ? null : ExternalProvider.fromJson(data);
2025-07-31 17:09:18 +08:00
}
@override
2026-05-27 15:16:51 +08:00
Future<String> updateGeoData(String type) async {
return _invokeMessage(method: CoreMethod.updateGeoData, arguments: type);
2025-07-31 17:09:18 +08:00
}
@override
Future<String> sideLoadExternalProvider({
required String providerName,
required String data,
}) async {
return _invokeMessage(
method: CoreMethod.sideLoadExternalProvider,
arguments: {'providerName': providerName, 'data': data},
);
2025-07-31 17:09:18 +08:00
}
@override
Future<String> updateExternalProvider(String providerName) async {
return _invokeMessage(
method: CoreMethod.updateExternalProvider,
arguments: providerName,
);
2025-07-31 17:09:18 +08:00
}
@override
2026-08-05 10:55:33 +08:00
Future<List<TrackerInfo>> getConnections() async {
final data = await _invokeMethod<Object>(method: CoreMethod.getConnections);
return data is List<TrackerInfo> ? data : trackerInfosOf(data);
2025-07-31 17:09:18 +08:00
}
@override
Future<int> getConnectionCount() async {
return await _invokeMethod<int>(method: CoreMethod.getConnectionCount) ?? 0;
}
2025-07-31 17:09:18 +08:00
@override
Future<bool> closeConnections() async {
2026-08-05 10:55:33 +08:00
return await _invokeMethod<bool>(method: CoreMethod.closeConnections) ??
false;
2025-07-31 17:09:18 +08:00
}
@override
Future<bool> resetConnections() async {
2026-08-05 10:55:33 +08:00
return await _invokeMethod<bool>(method: CoreMethod.resetConnections) ??
false;
2025-07-31 17:09:18 +08:00
}
@override
Future<bool> closeConnection(String id) async {
2026-08-05 10:55:33 +08:00
return await _invokeMethod<bool>(
method: CoreMethod.closeConnection,
arguments: id,
2025-07-31 17:09:18 +08:00
) ??
false;
}
@override
2026-08-05 10:55:33 +08:00
Future<Traffic> getTotalTraffic(bool onlyStatisticsProxy) async {
final data = await _invokeMethod<Map<String, dynamic>>(
method: CoreMethod.getTotalTraffic,
arguments: onlyStatisticsProxy,
);
return data == null ? const Traffic() : Traffic.fromJson(data);
2025-07-31 17:09:18 +08:00
}
@override
2026-08-05 10:55:33 +08:00
Future<Traffic> getTraffic(bool onlyStatisticsProxy) async {
final data = await _invokeMethod<Map<String, dynamic>>(
method: CoreMethod.getTraffic,
arguments: onlyStatisticsProxy,
);
return data == null ? const Traffic() : Traffic.fromJson(data);
2025-07-31 17:09:18 +08:00
}
@override
2026-08-05 10:55:33 +08:00
Future<String> clearEffect(int profileId) async {
return _invokeMessage(method: CoreMethod.clearEffect, arguments: profileId);
2025-07-31 17:09:18 +08:00
}
@override
FutureOr<void> resetTraffic() => _invokeUnobserved(CoreMethod.resetTraffic);
2025-07-31 17:09:18 +08:00
@override
FutureOr<void> startLog() => _invokeUnobserved(CoreMethod.startLog);
2025-07-31 17:09:18 +08:00
@override
FutureOr<void> stopLog() => _invokeUnobserved(CoreMethod.stopLog);
/// For calls whose callers never wait on them, so a Core that is gone or
/// restarting cannot surface as an unhandled asynchronous error.
Future<void> _invokeUnobserved(CoreMethod method) async {
try {
await _invokeMethod<Object?>(method: method);
} catch (error) {
commonPrint.log(
'${method.name} failed: $error',
logLevel: coreFailureLogLevel(error),
);
}
2025-07-31 17:09:18 +08:00
}
@override
Future<bool> startListener() async {
2026-08-05 10:55:33 +08:00
return await _invokeMethod<bool>(method: CoreMethod.startListener) ?? false;
2025-07-31 17:09:18 +08:00
}
@override
Future<bool> stopListener() async {
2026-08-05 10:55:33 +08:00
return await _invokeMethod<bool>(method: CoreMethod.stopListener) ?? false;
2025-07-31 17:09:18 +08:00
}
@override
Future<Delay?> asyncTestDelay(String url, String proxyName) async {
2025-07-31 17:09:18 +08:00
final delayParams = {
'proxy-name': proxyName,
'timeout': delayTestTimeoutDuration.inMilliseconds,
2025-07-31 17:09:18 +08:00
'test-url': url,
};
2026-08-05 10:55:33 +08:00
final data = await _invokeMethod<Map<String, dynamic>>(
method: CoreMethod.asyncTestDelay,
arguments: delayParams,
timeout: delayTestGuardDuration,
2026-08-05 10:55:33 +08:00
);
return data == null ? null : Delay.fromJson(data);
2025-07-31 17:09:18 +08:00
}
@override
Future<ProbeResult?> probe(ProbeParams params) async {
final data = await _invokeMethod<Map<String, dynamic>>(
method: CoreMethod.probe,
arguments: params.toJson(),
timeout: probeGuardDuration(params),
);
return data == null ? null : ProbeResult.fromJson(data);
}
@override
Future<OutboundIpResult?> outboundIp(OutboundIpParams params) async {
final data = await _invokeMethod<Map<String, dynamic>>(
method: CoreMethod.outboundIp,
arguments: params.toJson(),
timeout: coreGuardFor(params.timeout),
);
return data == null ? null : OutboundIpResult.fromJson(data);
}
@override
Future<List<ServiceCheckItem>> serviceCheck(ServiceCheckParams params) async {
final data = await _invokeMethod<List<dynamic>>(
method: CoreMethod.serviceCheck,
arguments: params.toJson(),
timeout: coreGuardFor(
params.timeout,
budgetFactor: serviceSweepBudgetFactor,
),
);
return data == null
? const []
: data
.map(
(item) =>
ServiceCheckItem.fromJson(item as Map<String, dynamic>),
)
.toList();
}
@override
Future<CoreMemoryStats?> getMemoryStats() async {
final data = await _invokeMethod<Map<String, dynamic>>(
method: CoreMethod.getMemoryStats,
);
return data == null ? null : CoreMemoryStats.fromJson(data);
2025-07-31 17:09:18 +08:00
}
}