service.dart 10 KB

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