service.dart 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265
  1. import 'dart:io';
  2. import 'dart:async';
  3. import 'dart:convert';
  4. import 'package:get/get.dart';
  5. import 'package:naiyouwl/app/bean/ClashServiceInfo.dart';
  6. import 'package:naiyouwl/app/const/const.dart';
  7. import 'package:naiyouwl/app/controller/controllers.dart';
  8. import 'package:naiyouwl/app/utils/logger.dart';
  9. import 'package:naiyouwl/app/utils/shell.dart';
  10. import 'package:path/path.dart' as path;
  11. import 'package:http/http.dart' as http;
  12. import '../utils/utils.dart';
  13. final headers = {"User-Agent": "ccore-for-flutter/0.0.1"};
  14. class ServiceController extends GetxController {
  15. var url = "";
  16. var client = http.Client();
  17. var serviceMode = false.obs;
  18. var coreStatus = RunningState.stoped.obs;
  19. var serviceStatus = RunningState.stoped.obs;
  20. var installStatus = false.obs; // 新增变量用于记录安装/卸载状态
  21. bool get serviceIsRuning => serviceStatus.value == RunningState.running;
  22. Future<void> initConfig() async{
  23. url = "127.0.0.1:${controllers.config.config.value.servicePort}";
  24. //controllers.config.setSerivcePort(controllers.config.config.value.servicePort);
  25. }
  26. Future<void> startTunService() async {
  27. try {
  28. while (true) {
  29. final data = await fetchInfo() ?? ClashServiceInfo.fromJson({'code': -1, 'mode': '', 'status': '-1', 'version': '-1'});
  30. if (data.mode != 'service-mode') {
  31. if (serviceStatus.value == RunningState.running) {
  32. await stopService();
  33. }
  34. await install();
  35. } else {
  36. break;
  37. }
  38. }
  39. } catch (e) {
  40. serviceStatus.value = RunningState.error;
  41. log.debug(e.toString());
  42. }
  43. }
  44. Future<ClashServiceInfo?> fetchInfo() async {
  45. try {
  46. final res = await client.post(Uri.http(url, 'info'), headers: headers);
  47. var jsonResponse = jsonDecode(res.body) as Map<String, dynamic>;
  48. return ClashServiceInfo.fromJson(jsonResponse);
  49. } catch (e) {
  50. print('Exception: $e');
  51. return null;
  52. }
  53. }
  54. Future<void> fetchStartInit() async {
  55. controllers.config.config.value.selected = Files.makeInitProxyConfig.path;
  56. controllers.global.updateMsg("服务模式初始化...");
  57. await fetchStop();
  58. try {
  59. var ut = Uri.http(url, 'start');
  60. final body = json.encode({
  61. "coreName": Files.assetsCCore.path,
  62. "args": [
  63. '-d',
  64. Paths.config.path,
  65. '-f',
  66. path.join(Paths.config.path, controllers.config.config.value.selected)
  67. ]
  68. });
  69. final res = await client.post(ut, body: body, headers: headers);
  70. var jsonResponse = jsonDecode(res.body) as Map<String, dynamic>;
  71. if (jsonResponse["code"] != 0) {
  72. coreStatus.value = RunningState.error;
  73. throw jsonResponse["msg"];
  74. }
  75. controllers.global.updateMsg("服务模式初始化成功..");
  76. coreStatus.value = RunningState.running;
  77. } catch (e) {
  78. print('Exception: $e');
  79. coreStatus.value = RunningState.error;
  80. controllers.global.updateMsg("服务模式初始化失败..");
  81. }
  82. }
  83. //应该程序退出调用
  84. Future<void> fetchStop() async {
  85. try {
  86. var ut = Uri.http(url, 'stop');
  87. await client.post(ut, headers: headers);
  88. coreStatus.value = RunningState.stoped;
  89. } catch (e) {
  90. print('Exception: $e');
  91. }
  92. }
  93. Future<void> fetchSetProxy() async {
  94. try {
  95. var dns_ip = "";
  96. if (controllers.global.routeModesSelect.value == "tun") {
  97. dns_ip = "198.18.0.2";
  98. }
  99. var ut = Uri.http(url, 'on');
  100. final body = json.encode({
  101. "http_port": controllers.config.mixedPort.value,
  102. "https_port": controllers.config.mixedPort.value,
  103. "socks_port": controllers.config.mixedPort.value,
  104. "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",
  105. "dns_ip": dns_ip,
  106. "dns_type": "",
  107. "address": "127.0.0.1"
  108. });
  109. await client.post(ut, body: body, headers: headers);
  110. } catch (e) {
  111. print('Exception: $e');
  112. }
  113. }
  114. Future<void> fetchSetProxyStop() async {
  115. try {
  116. var dnsIp = "";
  117. if (controllers.global.routeModesSelect.value == "tun") {
  118. dnsIp = "223.5.5.5";
  119. }
  120. var ut = Uri.http(url, 'off');
  121. final body = json.encode({
  122. "bypass": "",
  123. "dns_ip": dnsIp
  124. });
  125. await client.post(ut, body: body, headers: headers);
  126. } catch (e) {
  127. print('Exception: $e');
  128. }
  129. }
  130. Future<void> waitServiceStart() async {
  131. while (true) {
  132. await Future.delayed(const Duration(milliseconds: 100));
  133. try {
  134. await isCanOperationService();
  135. break;
  136. } catch (_) {}
  137. }
  138. }
  139. Future<void> install() async {
  140. try {
  141. final res = await runAsAdmin(Files.assetsClashService.path, ["-port", "${controllers.config.config.value.servicePort}", "stop", "uninstall", "install", "start"]);
  142. log.debug('install', res.stdout, res.stderr);
  143. if (res.exitCode != 0) {
  144. throw res.stderr;
  145. }
  146. await waitServiceStart();
  147. serviceStatus.value = RunningState.running;
  148. installStatus.value = true; // 安装成功
  149. } catch (e) {
  150. installStatus.value = false; // 安装失败
  151. log.debug('安装失败: $e');
  152. throw e;
  153. }
  154. }
  155. Future<void> uninstall() async {
  156. try {
  157. await stopService();
  158. final res = await runAsAdmin(Files.assetsClashService.path, ["stop", "uninstall"]);
  159. log.debug('uninstall', res.stdout, res.stderr);
  160. if (res.exitCode != 0) throw res.stderr;
  161. installStatus.value = false; // 卸载成功
  162. serviceStatus.value = RunningState.stoped;
  163. if(controllers.service.coreStatus == RunningState.stoped){
  164. controllers.global.updateMsg("卸载服务完成.重新启动内核...");
  165. await controllers.service.startClashCore();
  166. }
  167. } catch (e) {
  168. installStatus.value = true; // 卸载失败
  169. log.debug('卸载失败: $e');
  170. throw e;
  171. }
  172. }
  173. Future<void> stopService() async {
  174. serviceStatus.value = RunningState.stopping;
  175. if (coreStatus.value == RunningState.running) await fetchStop();
  176. serviceStatus.value = RunningState.stoped;
  177. }
  178. Future<void> serviceModeSwitch(bool open) async {
  179. if (serviceStatus.value == RunningState.running) await stopService();
  180. await controllers.service.stopClashCore();
  181. await fetchStop();
  182. try {
  183. controllers.global.updateMsg(open ? "安装服务" : "卸载服务");
  184. open ? await install() : await uninstall();
  185. } catch (e) {
  186. print(e.toString());
  187. }
  188. if(open)
  189. {
  190. await fetchStartInit();
  191. }
  192. }
  193. Future<void> stopClash() async {
  194. controllers.config.config.value.selected = Files.makeInitProxyConfig.path;
  195. if (coreStatus.value == RunningState.running) {
  196. await controllers.config.readClashCoreApi();
  197. controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value);
  198. await controllers.core.changeConfig(path.join(Paths.config.path, controllers.config.config.value.selected));
  199. }
  200. }
  201. Future<void> reloadClashCore() async {
  202. try {
  203. controllers.config.config.value.selected = Files.makeProxyConfig.path;
  204. if (coreStatus.value == RunningState.running) {
  205. controllers.global.updateMsg("切换配置...");
  206. await controllers.config.readClashCoreApi();
  207. controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value);
  208. await controllers.core.changeConfig(path.join(Paths.config.path, controllers.config.config.value.selected));
  209. controllers.global.updateMsg("fetchReloadConfig${controllers.config.clashCoreApiAddress.value}...");
  210. }
  211. } catch (e) {
  212. controllers.global.updateMsg("重新配置...");
  213. await controllers.config.readClashCoreApi();
  214. controllers.core.setApi(controllers.config.clashCoreApiAddress.value, controllers.config.clashCoreApiSecret.value);
  215. await controllers.core.changeConfig(path.join(Paths.config.path, controllers.config.config.value.selected));
  216. }
  217. }
  218. Future<bool> isCanOperationService() async {
  219. try {
  220. final res = await client.post(Uri.http(url, 'info'), headers: headers);
  221. if(res.statusCode == 200)
  222. {
  223. serviceStatus.value = RunningState.running;
  224. }
  225. return installStatus.value = res.statusCode == 200;
  226. } catch (e) {
  227. serviceStatus.value = RunningState.stoped;
  228. installStatus.value = false;
  229. return false;
  230. print('Exception when checking service: $e');
  231. return false;
  232. }
  233. }
  234. }