import 'dart:io'; import 'dart:async'; import 'dart:convert'; import 'package:get/get.dart'; import 'package:naiyouwl/app/bean/ClashServiceInfo.dart'; import 'package:naiyouwl/app/clash/service/clash_service.dart'; import 'package:naiyouwl/app/const/const.dart'; import 'package:naiyouwl/app/controller/controllers.dart'; import 'package:naiyouwl/app/utils/logger.dart'; import 'package:naiyouwl/app/utils/shell.dart'; import 'package:path/path.dart' as path; import 'package:http/http.dart' as http; import '../clash/mode/clash_config.dart'; import '../common/constants.dart'; import '../utils/utils.dart'; final headers = {"User-Agent": "ccore-for-flutter/0.0.1"}; class ServiceController extends GetxController { var url = ""; var client = http.Client(); var serviceMode = false.obs; var coreStatus = RunningState.stoped.obs; var serviceStatus = RunningState.stoped.obs; var installStatus = false.obs; // 新增变量用于记录安装/卸载状态 bool get serviceIsRuning => serviceStatus.value == RunningState.running; bool get coreIsRuning => coreStatus.value == RunningState.running; Future initConfig() async{ url = "127.0.0.1:${controllers.config.config.value.servicePort}"; //controllers.config.setSerivcePort(controllers.config.config.value.servicePort); } Future startTunService() async { try { while (true) { final data = await fetchInfo() ?? ClashServiceInfo.fromJson({'code': -1, 'mode': '', 'status': '-1', 'version': '-1'}); if (data.mode != 'service-mode') { if (serviceStatus.value == RunningState.running) { await stopService(); } await install(); } else { break; } } } catch (e) { serviceStatus.value = RunningState.error; log.debug(e.toString()); } } Future fetchInfo() async { try { final res = await client.post(Uri.http(url, 'info'), headers: headers); var jsonResponse = jsonDecode(res.body) as Map; return ClashServiceInfo.fromJson(jsonResponse); } catch (e) { print('Exception: $e'); return null; } } Future fetchStartInit() async { await controllers.service.makeInitConfig(); controllers.config.config.value.selected = Files.makeInitProxyConfig.path; controllers.global.updateMsg("服务模式初始化..."); await fetchStop(); try { var ut = Uri.http(url, 'start'); final body = json.encode({ "coreName": Files.assetsCCore.path, "args": [ '-d', Paths.config.path, '-f', path.join(Paths.config.path, controllers.config.config.value.selected) ] }); final res = await client.post(ut, body: body, headers: headers); var jsonResponse = jsonDecode(res.body) as Map; if (jsonResponse["code"] != 0) { coreStatus.value = RunningState.error; throw jsonResponse["msg"]; } controllers.global.updateMsg("服务模式初始化成功.."); coreStatus.value = RunningState.running; } catch (e) { print('Exception: $e'); coreStatus.value = RunningState.error; controllers.global.updateMsg("服务模式初始化失败.."); } } //应该程序退出调用 Future fetchStop() async { try { var ut = Uri.http(url, 'stop'); await client.post(ut, headers: headers); coreStatus.value = RunningState.stoped; } catch (e) { print('Exception: $e'); } } Future fetchSetProxy() async { try { var dns_ip = ""; if (controllers.global.routeModesSelect.value == "tun") { dns_ip = "198.18.0.2"; } 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_ip, "dns_type": "", "address": "127.0.0.1" }); await client.post(ut, body: body, headers: headers); } catch (e) { print('Exception: $e'); } } Future fetchSetProxyStop() async { try { var dnsIp = ""; if (controllers.global.routeModesSelect.value == "tun") { dnsIp = "223.5.5.5"; } var ut = Uri.http(url, 'off'); final body = json.encode({ "bypass": "", "dns_ip": dnsIp }); await client.post(ut, body: body, headers: headers); } catch (e) { print('Exception: $e'); } } Future waitServiceStart() async { while (true) { await Future.delayed(const Duration(milliseconds: 100)); try { await isCanOperationService(); break; } catch (_) {} } } Future install() async { try { 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(); serviceStatus.value = RunningState.running; if (serviceStatus.value == RunningState.running) { await controllers.service.stopClashCore(); await Future.delayed(Duration(seconds: 3)); await fetchStartInit(); } installStatus.value = true; // 安装成功 } catch (e) { installStatus.value = false; // 安装失败 log.debug('安装失败: $e'); throw e; } } Future uninstall() async { try { serviceStatus.value = RunningState.uninstall; final res = await runAsAdmin(Files.assetsClashService.path, ["stop", "uninstall"]); log.debug('uninstall', res.stdout, res.stderr); if (res.exitCode != 0) throw res.stderr; installStatus.value = false; // 卸载成功 await Future.delayed(Duration(seconds: 2)); // final conn = await controllers.core.fetchConnection(); // if(conn != null) // { // for (final it in conn.connections) { // await controllers.core.fetchCloseConnections(it.id); // } // } await controllers.service.updatePorts(); await Future.delayed(Duration(seconds: 2)); await controllers.service.makeInitConfig(); await Future.delayed(Duration(seconds: 5)); await controllers.service.startClashCore(); serviceStatus.value = RunningState.stoped; } catch (e) { installStatus.value = true; // 卸载失败 log.debug('卸载失败: $e'); throw e; } } Future stopService() async { serviceStatus.value = RunningState.stopping; if (coreStatus.value == RunningState.running) await fetchStop(); serviceStatus.value = RunningState.stoped; } Future serviceModeSwitch(bool open) async { // if (serviceStatus.value == RunningState.running) await controllers.service.stopClashCore(); // if (serviceStatus.value == RunningState.running) await fetchStop(); serviceStatus.value = RunningState.starting; try { controllers.global.updateMsg(open ? "安装服务" : "卸载服务"); open ? await install() : await uninstall(); } catch (e) { print(e.toString()); } if(open) { await fetchStartInit(); } } Future stopClash() async { controllers.config.config.value.selected = Files.makeInitProxyConfig.path; if (coreStatus.value == RunningState.running) { await controllers.config.readClashCoreApi(); controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value); await controllers.core.changeConfig(path.join(Paths.config.path, controllers.config.config.value.selected)); } } Future reloadClashCore() async { try { controllers.config.config.value.selected = Files.makeProxyConfig.path; if (coreStatus.value == RunningState.running) { controllers.global.updateMsg("切换配置..."); await controllers.config.readClashCoreApi(); controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value); await controllers.core.changeConfig(path.join(Paths.config.path, controllers.config.config.value.selected)); controllers.global.updateMsg("fetchReloadConfig${controllers.config.clashCoreApiAddress.value}..."); } else { controllers.global.updateMsg("重新配置..."); await controllers.config.readClashCoreApi(); controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value); await controllers.core.changeConfig(path.join(Paths.config.path, controllers.config.config.value.selected)); } } catch (e) { controllers.global.updateMsg("重新配置..."); await controllers.config.readClashCoreApi(); controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value); await controllers.core.changeConfig(path.join(Paths.config.path, controllers.config.config.value.selected)); } } Future isCanOperationService() async { try { final res = await client.post(Uri.http(url, 'info'), headers: headers); if(res.statusCode == 200) { serviceStatus.value = RunningState.running; } return installStatus.value = res.statusCode == 200; } catch (e) { serviceStatus.value = RunningState.stoped; installStatus.value = false; return false; print('Exception when checking service: $e'); return false; } } Future isClashCoreRunning() async { try { final res = await client.post(Uri.http(url, 'info'), headers: headers); if (res.statusCode == 200) { var jsonResponse = jsonDecode(res.body) as Map; return jsonResponse['status'] == 'running'; } return false; } catch (e) { print('Exception when checking Clash core: $e'); return false; } } }