alroyso 1 month ago
parent
commit
bebcbf9880
2 changed files with 97 additions and 31 deletions
  1. 42 15
      lib/app/clash/service/clash_service.dart
  2. 55 16
      lib/app/controller/GlobalController.dart

+ 42 - 15
lib/app/clash/service/clash_service.dart

@@ -2,8 +2,10 @@ import 'dart:convert';
 import 'dart:io';
 import 'dart:async';
 import 'package:get/get.dart';
+import 'package:yaml/yaml.dart';
 import 'package:yaml_edit/yaml_edit.dart';
 import 'package:path/path.dart' as path;
+import '../../common/LogHelper.dart';
 import '../../common/constants.dart';
 import '../../const/const.dart';
 import '../../controller/controllers.dart';
@@ -80,10 +82,16 @@ class ClashService extends GetxController {
           dnsHijack: ['any:53'],
         ),
         proxies: [],
-
+        proxyGroups: [
+          ProxyGroup(
+            name: 'proxy',
+            type: 'select',
+            proxies: ['DIRECT'],
+          ),
+        ],
         rules: [
           'GEOIP,CN,DIRECT',
-          'MATCH,DIRECT'
+          'MATCH,proxy'
         ]
     );
     try {
@@ -190,27 +198,46 @@ class ClashService extends GetxController {
 
   Future<void> reloadClashCore() async {
     try {
-      // if(coreStatus.value == RunningState.stoped){
-      //   await updatePorts();
-      // }
-
       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));
+        
+        // 在切换配置前确保配置文件有效
+        final configFile = File(path.join(Paths.config.path, controllers.config.config.value.selected));
+        if (!await configFile.exists()) {
+          throw Exception("配置文件不存在");
+        }
+        
+        // 确保配置文件包含必要的代理组
+        final configStr = await configFile.readAsString();
+        final yamlEditor = YamlEditor(configStr);
+        final configMap = yamlEditor.parseAt([]) as YamlMap;
+        
+        if (configMap['proxy-groups'] == null || 
+            (configMap['proxy-groups'] as YamlList).isEmpty) {
+          throw Exception("配置文件缺少代理组");
+        }
+        
+        await controllers.core.changeConfig(configFile.path);
         controllers.global.updateMsg("fetchReloadConfig${controllers.config.clashCoreApiAddress.value}...");
       }
     } catch (e) {
-      // if(coreStatus.value == RunningState.stoped){
-      //   await updatePorts();
-      // }
-
-      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));
+      LogHelper().e("切换配置失败: $e");
+      
+      // 如果切换失败,尝试回退到初始配置
+      try {
+        controllers.config.config.value.selected = Files.makeInitProxyConfig.path;
+        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 (fallbackError) {
+        LogHelper().e("回退到初始配置失败: $fallbackError");
+        throw Exception("配置切换失败");
+      }
     }
   }
 

+ 55 - 16
lib/app/controller/GlobalController.dart

@@ -94,6 +94,12 @@ class GlobalController extends GetxController {
     // 如果核心未运行,尝试启动它
     if (!allStopped) {
       try {
+        // 先停止所有可能的进程
+        await stopAllCore();
+        
+        // 等待端口释放
+        await waitForPortsAvailable();
+        
         // 确保配置已初始化
         if (!controllers.config.isInitialized.value) {
           await controllers.config.initConfig();
@@ -129,6 +135,37 @@ class GlobalController extends GetxController {
     return allStopped;
   }
 
+  Future<void> waitForPortsAvailable() async {
+    final ports = [
+      controllers.config.mixedPort.value,
+      controllers.config.apiAddressPort.value,
+      controllers.config.servicePort.value
+    ];
+
+    final maxAttempts = 10;
+    var attempts = 0;
+
+    while (attempts < maxAttempts) {
+      bool allPortsAvailable = true;
+      for (final port in ports) {
+        if (!(await isPortAvailable(port))) {
+          allPortsAvailable = false;
+          LogHelper().d('等待端口 $port 释放...');
+          break;
+        }
+      }
+      
+      if (allPortsAvailable) {
+        return;
+      }
+
+      attempts++;
+      await Future.delayed(Duration(milliseconds: 500));
+    }
+
+    throw Exception('等待端口释放超时');
+  }
+
   Future<bool> isProcessRunning(String processName) async {
     try {
       String command = Platform.isWindows ? 'tasklist' : 'ps aux';
@@ -366,27 +403,29 @@ class GlobalController extends GetxController {
   }
 
   Future<void> stopAllCore() async {
-    await systemProxySwitch(false);
-    if(Platform.isWindows){
-      await controllers.service.stopClashCore();
-    } else if(Platform.isMacOS) {
-      if (controllers.cc_service.serviceIsRuning) {
-        await controllers.cc_service.fetchStop();
-      } else {
-        await controllers.service.stopClashCore();
-      }
-
-    }
-    if(!controllers.service.serviceMode.value){
+    try {
+      await systemProxySwitch(false);
+      
+      // 停止所有可能的进程
       if(Platform.isWindows){
+        await controllers.service.stopClashCore();
         await onKillProcess(path.basename(Files.assetsClashService.path));
+      } else if(Platform.isMacOS) {
+        if (controllers.cc_service.serviceIsRuning) {
+          await controllers.cc_service.fetchStop();
+        } else {
+          await controllers.service.stopClashCore();
+        }
       }
+      
+      // 确保进程完全停止
       await killProcess(path.basename(Files.assetsCCore.path));
       await killProcess(path.basename(Files.assetsClashService.path));
-    }
-
-    if(!controllers.service.serviceMode.value){
-      await killProcess(path.basename(Files.assetsClashService.path));
+      
+      // 等待一段时间确保进程完全退出
+      await Future.delayed(Duration(milliseconds: 500));
+    } catch (e) {
+      LogHelper().e("停止内核失败: $e");
     }
   }