|
@@ -20,13 +20,16 @@ import 'package:flutter/foundation.dart';
|
|
|
import 'package:bot_toast/bot_toast.dart';
|
|
|
|
|
|
import 'package:web_socket_channel/io.dart';
|
|
|
-
|
|
|
+import 'package:http/http.dart' as http;
|
|
|
|
|
|
|
|
|
final headers = {"User-Agent": "ccore-for-flutter/0.0.1"};
|
|
|
|
|
|
class ServiceController extends GetxController {
|
|
|
- late final _dio = Dio(BaseOptions(baseUrl: 'http://127.0.0.1:9899', headers: headers));
|
|
|
+ //late final _dio = Dio(BaseOptions(baseUrl: 'http://127.0.0.1:9899', headers: headers));
|
|
|
+ var ip = "127.0.0.1";
|
|
|
+ var url = "";
|
|
|
+ var client = http.Client();
|
|
|
var serviceMode = false.obs;
|
|
|
var coreStatus = RunningState.stoped.obs;
|
|
|
var serviceStatus = RunningState.stoped.obs;
|
|
@@ -41,19 +44,21 @@ class ServiceController extends GetxController {
|
|
|
bool get isCanOperationCore =>
|
|
|
serviceStatus.value == RunningState.running && ![RunningState.starting, RunningState.stopping].contains(coreStatus.value);
|
|
|
bool get coreIsRuning => coreStatus.value == RunningState.running;
|
|
|
+ bool get serviceIsRuning => serviceStatus.value == RunningState.running;
|
|
|
ServiceController(
|
|
|
|
|
|
);
|
|
|
|
|
|
Future<void> initConfig() async{
|
|
|
- _dio.options.baseUrl = 'http://127.0.0.1:${controllers.config.config.value.servicePort}';
|
|
|
+ url = "$ip:${controllers.config.config.value.servicePort}";
|
|
|
+ controllers.config.setSerivcePort(controllers.config.config.value.servicePort);
|
|
|
}
|
|
|
|
|
|
Future<void> startTunService() async {
|
|
|
|
|
|
try {
|
|
|
while (true) {
|
|
|
- final data = await fetchInfo();
|
|
|
+ final data = await fetchInfo() ?? ClashServiceInfo.fromJson({'code': -1 , 'mode' : '' , 'status': '-1','version' : '-1'});
|
|
|
if( data.mode != 'service-mode')
|
|
|
{
|
|
|
if (serviceStatus.value == RunningState.running) {
|
|
@@ -75,41 +80,32 @@ class ServiceController extends GetxController {
|
|
|
}
|
|
|
|
|
|
Future<void> isService() async {
|
|
|
- //controllers.global.updateMsg("判断是不是 service-mode");
|
|
|
- //serviceStatus.value = RunningState.starting;
|
|
|
- if (Platform.isLinux) {
|
|
|
- await fixBinaryExecutePermissions(Files.assetsClashService);
|
|
|
- await fixBinaryExecutePermissions(Files.assetsClashCore);
|
|
|
- }
|
|
|
+
|
|
|
try {
|
|
|
- final data = await fetchInfo();
|
|
|
+ final data = await fetchInfo() ?? ClashServiceInfo.fromJson({'code': -1 , 'mode' : '' , 'status': '-1','version' : '-1'});
|
|
|
if(data.mode == 'service-mode'){
|
|
|
serviceMode.value = true;
|
|
|
controllers.global.updateMsg("服务模式");
|
|
|
- //await serviceModeSwitch(false);
|
|
|
+ } else {
|
|
|
+ serviceMode.value = false;
|
|
|
+ controllers.global.updateMsg("用户模式");
|
|
|
}
|
|
|
|
|
|
} catch (_) {
|
|
|
|
|
|
}
|
|
|
- //serviceStatus.value = RunningState.running;
|
|
|
}
|
|
|
|
|
|
Future<void> startService() async {
|
|
|
- //controllers.global.updateMsg("开启服务");
|
|
|
+
|
|
|
serviceStatus.value = RunningState.starting;
|
|
|
- if (Platform.isLinux) {
|
|
|
- await fixBinaryExecutePermissions(Files.assetsClashService);
|
|
|
- await fixBinaryExecutePermissions(Files.assetsClashCore);
|
|
|
- }
|
|
|
- // bool isAvailable = await isPortAvailable(controllers.config.servicePort.value);
|
|
|
- // if (!isAvailable) {
|
|
|
- // controllers.global.updateMsg("端口${controllers.config.servicePort.value}被占用,启动服务失败。");
|
|
|
- // serviceStatus.value = RunningState.error;
|
|
|
- // return; // 端口被占用,返回失败
|
|
|
- // }
|
|
|
try {
|
|
|
final data = await fetchInfo();
|
|
|
+ if(data == null){
|
|
|
+ await startUserModeService();
|
|
|
+ controllers.global.updateMsg("开启用户服务");
|
|
|
+ return;
|
|
|
+ }
|
|
|
serviceMode.value = data.mode == 'service-mode';
|
|
|
controllers.global.updateMsg("服务模式");
|
|
|
} catch (e) {
|
|
@@ -129,38 +125,58 @@ class ServiceController extends GetxController {
|
|
|
}
|
|
|
|
|
|
Future<void> startUserModeService() async {
|
|
|
+ serviceStatus.value = RunningState.stopping;
|
|
|
serviceMode.value = false;
|
|
|
+ final timeout = const Duration(seconds: 30); // 设置超时时间为30秒
|
|
|
+ final checkInterval = const Duration(milliseconds: 200);
|
|
|
+ var startTime = DateTime.now();
|
|
|
try {
|
|
|
+ final isRun = await controllers.global.onIsProcessRunning(Files.assetsClashService.path);
|
|
|
+ if(isRun == true){
|
|
|
+ await isService();
|
|
|
+ serviceStatus.value = RunningState.running;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
int? exitCode;
|
|
|
clashServiceProcess = await Process.start(Files.assetsClashService.path, ['-port','${controllers.config.config.value.servicePort}','user-mode'], mode: ProcessStartMode.inheritStdio);
|
|
|
clashServiceProcess!.exitCode.then((code) => exitCode = code);
|
|
|
-
|
|
|
- while (true) {
|
|
|
- await Future.delayed(const Duration(milliseconds: 200));
|
|
|
- if (exitCode == 101) {
|
|
|
- BotToast.showText(text: 'clash-service exit with code: $exitCode,After 10 seconds, try to restart');
|
|
|
- log.error('After 10 seconds, try to restart');
|
|
|
- await Future.delayed(const Duration(seconds: 10));
|
|
|
- await startUserModeService();
|
|
|
- break;
|
|
|
- } else if (exitCode != null) {
|
|
|
- serviceStatus.value = RunningState.error;
|
|
|
- break;
|
|
|
- }
|
|
|
+ while (DateTime.now().difference(startTime) < timeout) {
|
|
|
try {
|
|
|
- await _dio.post('/info');
|
|
|
+ controllers.global.updateMsg("等待内核启动..");
|
|
|
+ final ret = await fetchInfo();
|
|
|
+ if(ret != null){
|
|
|
+ if(ret.code == 0){
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
break;
|
|
|
- } catch (_) {}
|
|
|
+ } catch (_) {
|
|
|
+ // 如果fetchHello失败,等待200毫秒后重试
|
|
|
+ await Future.delayed(checkInterval);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否超时
|
|
|
+ if (DateTime.now().difference(startTime) >= timeout) {
|
|
|
+ // 如果超时,更新状态并抛出异常
|
|
|
+ coreStatus.value = RunningState.error;
|
|
|
+ controllers.global.updateMsg("内核启动超时,重新点击加速后尝试。");
|
|
|
+ return; // 提前退出函数
|
|
|
}
|
|
|
+
|
|
|
+ await fetchStartInit();
|
|
|
+ serviceStatus.value = RunningState.running;
|
|
|
} catch (e) {
|
|
|
serviceStatus.value = RunningState.error;
|
|
|
- BotToast.showText(text: e.toString());
|
|
|
+ //BotToast.showText(text: e.toString());
|
|
|
+ print(e.toString());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
Future<void> stopService() async {
|
|
|
serviceStatus.value = RunningState.stopping;
|
|
|
- if (coreStatus.value == RunningState.running) await stopClashCore();
|
|
|
+ if (coreStatus.value == RunningState.running) await fetchStop();
|
|
|
if (!serviceMode.value) {
|
|
|
if (clashServiceProcess != null) {
|
|
|
clashServiceProcess!.kill();
|
|
@@ -177,7 +193,7 @@ class ServiceController extends GetxController {
|
|
|
while (true) {
|
|
|
await Future.delayed(const Duration(milliseconds: 100));
|
|
|
try {
|
|
|
- await _dio.post('/info');
|
|
|
+ await client.post(Uri.http(url,'info'),headers: headers);
|
|
|
break;
|
|
|
} catch (_) {}
|
|
|
}
|
|
@@ -188,41 +204,155 @@ class ServiceController extends GetxController {
|
|
|
while (true) {
|
|
|
await Future.delayed(const Duration(milliseconds: 100));
|
|
|
try {
|
|
|
- await _dio.post('/info');
|
|
|
+ await client.post(Uri.http(url,'info'),headers: headers);
|
|
|
} catch (e) {
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- Future<ClashServiceInfo> fetchInfo() async {
|
|
|
- final res = await _dio.post('/info');
|
|
|
- return ClashServiceInfo.fromJson(res.data);
|
|
|
+ Future<ClashServiceInfo?> fetchInfo() async {
|
|
|
+ try {
|
|
|
+ final res = await client.post(Uri.http(url,'info'),headers: headers);
|
|
|
+ var jsonResponse =
|
|
|
+ jsonDecode(res.body) as Map<String, dynamic>;
|
|
|
+ return ClashServiceInfo.fromJson(jsonResponse);
|
|
|
+ } on http.ClientException catch (e) {
|
|
|
+ // 处理客户端异常,例如没有网络连接
|
|
|
+ print('Client Exception: ${e.message}');
|
|
|
+ return null;
|
|
|
+ } on Exception catch (e) {
|
|
|
+ // 处理其他类型的异常
|
|
|
+ print('Exception: $e');
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
}
|
|
|
|
|
|
IOWebSocketChannel fetchLogWs() {
|
|
|
return IOWebSocketChannel.connect(Uri.parse('ws://127.0.0.1:${controllers.config.config.value.servicePort}/logs'), headers: headers);
|
|
|
}
|
|
|
|
|
|
- Future<void> fetchStart(String name) async {
|
|
|
- await fetchStop();
|
|
|
- final res = await _dio.post<String>('/start', data: {
|
|
|
- "args": ['-d', Paths.config.path, '-f', path.join(Paths.config.path, name)]
|
|
|
+ Future<void> fetchStartInit() async {
|
|
|
+ controllers.config.config.value.selected = 'init_proxy.yaml';
|
|
|
+ controllers.global.updateMsg("启动内核---${controllers.config.config.value.selected}");
|
|
|
+ if( controllers.config.config.value.selected == 'init_proxy.yaml'){
|
|
|
+ controllers.global.updateMsg("启动内核初始化");
|
|
|
+ } else {
|
|
|
+ controllers.global.updateMsg("启动内核");
|
|
|
+ }
|
|
|
+ await fetchStop();
|
|
|
+
|
|
|
+ try{
|
|
|
+ var ut = Uri.http(url,'start');
|
|
|
+ final body = json.encode({
|
|
|
+ "args": [
|
|
|
+ '-d',
|
|
|
+ Paths.config.path,
|
|
|
+ '-f',
|
|
|
+ path.join(Paths.config.path, controllers.config.config.value.selected)
|
|
|
+ ]
|
|
|
});
|
|
|
- if (json.decode(res.data!)["code"] != 0) throw json.decode(res.data!)["msg"];
|
|
|
+ final res = await client.post(ut,body: body,headers: headers);
|
|
|
+ var jsonResponse =
|
|
|
+ jsonDecode(res.body) as Map<String, dynamic>;
|
|
|
+ if (jsonResponse["code"] != 0) {
|
|
|
+ coreStatus.value = RunningState.error;
|
|
|
+ throw jsonResponse["msg"];
|
|
|
+ }
|
|
|
+ coreStatus.value = RunningState.running;
|
|
|
+ } on http.ClientException catch (e) {
|
|
|
+ // 处理客户端异常,例如没有网络连接
|
|
|
+ print('Client Exception: ${e.message}');
|
|
|
+ } on Exception catch (e) {
|
|
|
+ // 处理其他类型的异常
|
|
|
+ print('Exception: $e');
|
|
|
}
|
|
|
|
|
|
+ }
|
|
|
+
|
|
|
+ Future<void> fetchStart() async {
|
|
|
+ controllers.config.config.value.selected = 'proxy.yaml';
|
|
|
+ controllers.global.updateMsg("启动内核---${controllers.config.config.value.selected}");
|
|
|
+ if( controllers.config.config.value.selected == 'init_proxy.yaml'){
|
|
|
+ controllers.global.updateMsg("启动内核初始化");
|
|
|
+ } else {
|
|
|
+ controllers.global.updateMsg("启动内核");
|
|
|
+ }
|
|
|
+ await fetchStop();
|
|
|
+
|
|
|
+ try{
|
|
|
+ var ut = Uri.http(url,'start');
|
|
|
+ final res = await client.post(ut,body: {"args": ['-d', Paths.config.path, '-f', path.join(Paths.config.path, controllers.config.config.value.selected)]},headers: headers);
|
|
|
+ var jsonResponse =
|
|
|
+ jsonDecode(res.body) as Map<String, dynamic>;
|
|
|
+ if (jsonResponse["code"] != 0) throw jsonResponse["msg"];
|
|
|
+ } on http.ClientException catch (e) {
|
|
|
+ // 处理客户端异常,例如没有网络连接
|
|
|
+ print('Client Exception: ${e.message}');
|
|
|
+ } on Exception catch (e) {
|
|
|
+ // 处理其他类型的异常
|
|
|
+ print('Exception: $e');
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ Future<void> fetchSetProxy() async {
|
|
|
+ try {
|
|
|
+ var ut = Uri.http(url,'on');
|
|
|
+ final body = json.encode({
|
|
|
+ "http_port" : controllers.config.mixedPort.value,
|
|
|
+ "https_port" : controllers.config.mixedPort.value,
|
|
|
+ "socks_port" :controllers.config.mixedPort.value,
|
|
|
+ "bypass": "127.0.0.1, 192.168.0.0/16, 10.0.0.0/8, 172.16.0.0/12, 100.64.0.0/10, 17.0.0.0/8, localhost, *.local, *.crashlytics.com, seed-sequoia.siri.apple.com, sequoia.apple.com,xd.apple.com",
|
|
|
+ "dns_ip": "",
|
|
|
+ "dns_type":"",
|
|
|
+ "address":"127.0.0.1"
|
|
|
+ });
|
|
|
+ await client.post(ut,body: body,headers: headers);
|
|
|
+ } on http.ClientException catch (e) {
|
|
|
+ // 处理客户端异常,例如没有网络连接
|
|
|
+ print('Client Exception: ${e.message}');
|
|
|
+ } on Exception catch (e) {
|
|
|
+ // 处理其他类型的异常
|
|
|
+ print('Exception: $e');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Future<void> fetchSetProxyStop() async {
|
|
|
+
|
|
|
+ try {
|
|
|
+ var ut = Uri.http(url,'off');
|
|
|
+ final body = json.encode({
|
|
|
+ "bypass": "",
|
|
|
+ "dns_ip":""
|
|
|
+ });
|
|
|
+ await client.post(ut,body:body,headers: headers);
|
|
|
+ } on http.ClientException catch (e) {
|
|
|
+ // 处理客户端异常,例如没有网络连接
|
|
|
+ print('Client Exception: ${e.message}');
|
|
|
+ } on Exception catch (e) {
|
|
|
+ // 处理其他类型的异常
|
|
|
+ print('Exception: $e');
|
|
|
+ }
|
|
|
+ }
|
|
|
Future<void> fetchStop() async {
|
|
|
try {
|
|
|
- await _dio.post('/stop');
|
|
|
- } catch (e) {
|
|
|
- return;
|
|
|
+ var ut = Uri.http(url,'stop');
|
|
|
+ await client.post(ut,headers:headers);
|
|
|
+ } on http.ClientException catch (e) {
|
|
|
+ // 处理客户端异常,例如没有网络连接
|
|
|
+ print('Client Exception: ${e.message}');
|
|
|
+ } on Exception catch (e) {
|
|
|
+ // 处理其他类型的异常
|
|
|
+ print('Exception: $e');
|
|
|
}
|
|
|
}
|
|
|
|
|
|
Future<void> install() async {
|
|
|
- final res = await runAsAdmin(Files.assetsClashService.path, ["-port","${controllers.config.config.value.servicePort}","stop", "uninstall", "install", "start"]);
|
|
|
await initConfig();
|
|
|
+ final res = await runAsAdmin(Files.assetsClashService.path, ["-port","${controllers.config.config.value.servicePort}","stop", "uninstall", "install", "start"]);
|
|
|
+
|
|
|
log.debug('install', res.stdout, res.stderr);
|
|
|
if (res.exitCode != 0) throw res.stderr;
|
|
|
await waitServiceStart();
|
|
@@ -246,12 +376,11 @@ class ServiceController extends GetxController {
|
|
|
}
|
|
|
if(open){
|
|
|
await startService();
|
|
|
- await startClashCore();
|
|
|
+ //await startClashCore();
|
|
|
}else{
|
|
|
serviceMode.value = false;
|
|
|
- await startClashCore();
|
|
|
}
|
|
|
- //await startClashCore();
|
|
|
+
|
|
|
}
|
|
|
Future<bool> isPortAvailable(int port) async {
|
|
|
try {
|
|
@@ -271,16 +400,7 @@ class ServiceController extends GetxController {
|
|
|
final checkInterval = const Duration(milliseconds: 200);
|
|
|
var startTime = DateTime.now();
|
|
|
await controllers.config.readClashCoreApi();
|
|
|
- // bool isAvailable = await isPortAvailable(controllers.config.mixedPort.value);
|
|
|
- // if (!isAvailable) {
|
|
|
- // controllers.global.updateMsg("端口 被占用,启动内核失败,等待几秒后重新测试。");
|
|
|
- // return false; // 端口被占用,返回失败
|
|
|
- // }
|
|
|
- // isAvailable = await isPortAvailable(controllers.config.ApiAddressPort.value);
|
|
|
- // if (!isAvailable) {
|
|
|
- // controllers.global.updateMsg("端口 被占用,启动内核失败,等待几秒后重新测试。");
|
|
|
- // return false; // 端口被占用,返回失败
|
|
|
- // }
|
|
|
+
|
|
|
|
|
|
try {
|
|
|
controllers.global.updateMsg("启动内核---${controllers.config.config.value.selected}");
|
|
@@ -292,7 +412,7 @@ class ServiceController extends GetxController {
|
|
|
coreStatus.value = RunningState.starting;
|
|
|
|
|
|
if(serviceMode.value == true){
|
|
|
- await fetchStart(controllers.config.config.value.selected);
|
|
|
+ await fetchStart();
|
|
|
}
|
|
|
else
|
|
|
{
|
|
@@ -332,7 +452,6 @@ class ServiceController extends GetxController {
|
|
|
await controllers.core.updateConfig();
|
|
|
coreStatus.value = RunningState.running;
|
|
|
controllers.global.updateMsg("内核状态:${coreStatus.value == RunningState.running} ");
|
|
|
- controllers.global.updateDate();
|
|
|
return true;
|
|
|
} catch (e) {
|
|
|
log.error("core -- $e");
|
|
@@ -390,6 +509,8 @@ class ServiceController extends GetxController {
|
|
|
await controllers.core.changeConfig(path.join(Paths.config.path, controllers.config.config.value.selected));
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
Future<void> reloadClashCore() async {
|
|
|
try
|
|
|
{
|