alroyso 1 year ago
parent
commit
bf1c654b75

+ 15 - 0
assets/bin/run-as-admin.bat

@@ -0,0 +1,15 @@
+set command=%1
+@REM remove: quotes
+set command=%command:"=%
+set args=%2
+
+:param
+if "%3"=="" (
+    goto end
+)
+set args=%args% %3
+shift /0
+goto param
+
+:end
+mshta vbscript:createobject("shell.application").shellexecute("%command%","%args%","","runas",0)(window.close)

+ 60 - 0
lib/app/component/dialogs.dart

@@ -0,0 +1,60 @@
+import 'package:get/get.dart';
+import 'package:flutter/material.dart';
+import 'package:styled_widget/styled_widget.dart';
+
+
+Future<bool?> showNormalDialog(
+    BuildContext context, {
+      String? content,
+      Widget? child,
+      required String title,
+      required String cancelText,
+      required String enterText,
+      bool Function()? validator,
+    }) {
+  assert(content != null || child != null);
+
+  return showDialog(
+    context: context,
+    builder: (c) => AlertDialog(
+      title: Text(title).textColor(Theme.of(context).primaryColor).textShadow(blurRadius: 6, offset: const Offset(0, 2)),
+      content: child ?? Text(content!).textColor(const Color(0xff54759a)),
+      actions: [
+        TextButton(
+          onPressed: () => Navigator.pop(c, false),
+          style: ButtonStyle(
+            minimumSize: MaterialStateProperty.all(const Size(70, 36)),
+            shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))),
+            side: MaterialStateProperty.all(const BorderSide(color: Color(0x1a000000), width: 1)),
+          ),
+          child: Text(cancelText),
+        ),
+        TextButton(
+          onPressed: () => validator?.call() == false ? null : Navigator.pop(c, true),
+          style: ButtonStyle(
+            minimumSize: MaterialStateProperty.all(const Size(70, 36)),
+            backgroundColor: MaterialStateProperty.all(Theme.of(context).primaryColor),
+            shape: MaterialStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(16))),
+            side: MaterialStateProperty.all(const BorderSide(color: Color(0x1a000000), width: 1)),
+          ),
+          child: Text(enterText).textColor(Colors.white),
+        ).boxShadow(color: const Color(0x802c8af8), blurRadius: 8, offset: const Offset(0, 2)),
+      ],
+    ),
+  );
+}
+
+// Future<ConfigSub?> showEditProfileDialog(BuildContext context,
+//     {ConfigSub? sub, required String title, bool Function(ConfigSub sub)? validator}) async {
+//   final child = EditProfile(name: sub?.name, url: sub?.url);
+//   final res = await showNormalDialog(
+//     context,
+//     title: title,
+//     child: child,
+//     enterText: 'model_ok'.tr,
+//     cancelText: 'model_cancel'.tr,
+//     validator: () => validator?.call(ConfigSub(name: child.nameInputController.text, url: child.urlInputController.text)) ?? true,
+//   );
+//   if (res != true) return null;
+//   return ConfigSub(name: child.nameInputController.text, url: child.urlInputController.text, updateTime: sub?.updateTime);
+// }

+ 66 - 8
lib/app/controller/GlobalController.dart

@@ -2,6 +2,7 @@ import 'dart:io';
 import 'package:flutter/widgets.dart';
 import 'package:get/get.dart';
 import 'package:naiyouwl/app/bean/proxie.dart';
+import 'package:naiyouwl/app/component/dialogs.dart';
 import 'package:naiyouwl/app/controller/controllers.dart';
 import 'package:naiyouwl/app/data/model/NodeMode.dart';
 import 'package:naiyouwl/app/network/api_service.dart';
@@ -16,6 +17,9 @@ class GlobalController extends GetxController {
 
   late BuildContext context;
   final List<String> modes = ['rule', 'global'];
+  final List<String> routeModes = ['sys', 'tun'];
+
+  var routeModesSelect = "sys".obs;
   var nodeModes = <NodeMode>[].obs;
   var isLoading = false.obs;
   var errorMsg = ''.obs;
@@ -55,9 +59,9 @@ class GlobalController extends GetxController {
 
     await controllers.service.initConfig();
     // init service
-    await controllers.service.startService();
+    await controllers.service.install();
     if (controllers.service.serviceStatus.value != RunningState.running) return;
-
+   // await controllers.service.serviceModeSwitch(true);
     // init clash core
 
     // await controllers.service.startClashCore();
@@ -68,16 +72,64 @@ class GlobalController extends GetxController {
     initRegularlyUpdate();
   }
 
+  Future<void> updateRoute(String route) async {
+    if(allowStatusUpdate){
+
+      return;
+    }
+    // if(route == "tun"){
+    //   final res = await showNormalDialog( context,content: '启用网卡模式需要管理员',title: '提示', cancelText: '取消', enterText: '确认安装');
+    //   if (res != true) return;
+    //   controllers.service.serviceModeSwitch(true);
+    // }
+    routeModesSelect.value = route;
+  }
   Future<void> fetchNodes() async {
     nodeModes.value = await ApiService().getNode("/api/client/v4/nodes?vless=1");
+    // await makeProxy();
+    // if(controllers.service.coreStatus.value == RunningState.stoped){
+    //   await controllers.service.reloadClashCore();
+    // }
+    // if (controllers.service.coreStatus.value != RunningState.running) return;
+    // await controllers.core.updateVersion();
+    // await updateDate();
+    //
+    // NodeMode? targetNode;
+    // if (selectedNode.value == null) {
+    //   targetNode = await findNodeWithMinUsers(nodeModes);
+    // } else {
+    //   targetNode = selectedNode.value;
+    // }
+    // if (targetNode != null){
+    //   selectNode(targetNode);
+    //   ProxieProxiesItem? targetProxie = await findProxieByName(targetNode.name);
+    //   if (targetProxie != null) {
+    //     handleSetProxieGroup(targetProxie, targetNode.name);
+    //   }
+    // }
+
+  }
+
+  Future<void> startSysMode() async {
+
     await makeProxy();
-    if(controllers.service.coreStatus.value == RunningState.stoped){
-      await controllers.service.reloadClashCore();
+    NodeMode? targetNode;
+    if (selectedNode.value == null) {
+      targetNode = await findNodeWithMinUsers(nodeModes);
+    } else {
+      targetNode = selectedNode.value;
+    }
+    if (targetNode != null){
+      selectNode(targetNode);
+      ProxieProxiesItem? targetProxie = await findProxieByName(targetNode.name);
+      if (targetProxie != null) {
+        handleSetProxieGroup(targetProxie, targetNode.name);
+      }
     }
-    if (controllers.service.coreStatus.value != RunningState.running) return;
-    await controllers.core.updateVersion();
     await updateDate();
-
+  }
+  Future<void> startTunMode() async {
+    await makeProxy();
     NodeMode? targetNode;
     if (selectedNode.value == null) {
       targetNode = await findNodeWithMinUsers(nodeModes);
@@ -91,8 +143,14 @@ class GlobalController extends GetxController {
         handleSetProxieGroup(targetProxie, targetNode.name);
       }
     }
+    await updateDate();
+  }
 
-
+  Future<void> swift(String name) async {
+    ProxieProxiesItem? targetProxie = await findProxieByName(name);
+    if (targetProxie != null) {
+      handleSetProxieGroup(targetProxie, name);
+    }
   }
   // Future<ProxieProxiesItem> findProxieByName(String name) async {
   //   return proxieGroups.firstWhere((proxie) => proxie['name'] == name, orElse: () => null);

+ 53 - 1
lib/app/controller/config.dart

@@ -51,8 +51,9 @@ class ConfigController extends GetxController {
     //config.value.port = port;
 
     if (config.value.subs.isEmpty) {
-      if (!await Files.configExample.exists())
+      if (!await Files.configExample.exists()) {
         await Files.assetsExample.copy(Files.configExample.path);
+      }
       config.value.subs.add(ConfigSub(name: 'example.yaml', url: '', updateTime: 0));
       config.value.selected = 'example.yaml';
     }
@@ -78,6 +79,16 @@ class ConfigController extends GetxController {
     }
   }
   Future<void> makeClashConfig(List<NodeMode> nodes) async{
+    Files.makeProxyConfig.deleteSync(recursive: true);
+    var stack = "system";
+    if( Platform.isWindows){
+      stack = "gvisor";
+    }
+    var dnsPort = 1553;
+    if(clashCoreTunEnable.value == true)
+    {
+      dnsPort = 53;
+    }
     var mixedport  = 9888;
     bool bg = await isPortOccupied(mixedport);
     if(bg) {
@@ -111,6 +122,47 @@ bind-address: '*'
 mode: Rule
 log-level: info
 external-controller: '127.0.0.1:$extePort'
+unified-delay: false
+geodata-mode: true
+tcp-concurrent: false
+find-process-mode: strict
+global-client-fingerprint: chrome
+
+dns:
+  nameserver:
+    - 114.114.114.114
+    - 119.29.29.29
+    - https://doh.pub/dns-query
+    - https://dns.alidns.com/dns-query
+  fallback:
+    - https://dns.cloudflare.com/dns-query
+    - "[2001:da8::666]:53"
+    - https://public.dns.iij.jp/dns-query
+    - https://jp.tiar.app/dns-query
+    - https://jp.tiarap.org/dns-query
+    - tls://dot.tiar.app
+  enable: true
+  ipv6: false
+  # enhanced-mode: redir-host
+  enhanced-mode: fake-ip
+  fake-ip-range: 198.18.0.1/16
+  listen: 0.0.0.0:$dnsPort
+  fake-ip-filter:
+    - "*.lan"
+  default-nameserver:
+    - 114.114.114.114
+    - 119.29.29.29
+    - "[2001:da8::666]:53"
+
+tun:
+  enable: ${clashCoreTunEnable.value}
+  stack: $stack
+  # stack: gvisor
+  dns-hijack:
+    - 198.18.0.2:53 # when `fake-ip-range` is 198.18.0.1/16, should hijack 198.18.0.2:53
+  auto-route: true # auto set global route for Windows
+  # It is recommended to use `interface-name`
+  auto-detect-interface: true # auto detect interface, conflict with `interface-name`
 proxies:
 ${proxies.join('\n')}
 $proxyGroups

+ 6 - 0
lib/app/i18n/i18n.dart

@@ -67,6 +67,9 @@ class I18n extends Translations {
           "rule_provider_title": "Providers",
           "rule_provider_update_time": "Last updated at",
           "rule_rule_count": "Rule count",
+          //route
+          "route_sys_tile": "sysProxy",
+          "route_tun_title": "tunProxy",
           // connection
           "connection_title": "Connections",
           "connection_keep_closed": "Keep closed connections",
@@ -179,6 +182,9 @@ class I18n extends Translations {
           "rule_provider_title": "规则集",
           "rule_provider_update_time": "最后更新于",
           "rule_rule_count": "规则条数",
+          //route
+          "route_sys_tile": "系统代理",
+          "route_tun_title": "网卡模式",
           // connection
           "connection_title": "连接",
           "connection_keep_closed": "保留关闭连接",

+ 18 - 1
lib/app/modules/home/controllers/home_controller.dart

@@ -73,6 +73,7 @@ class HomeController extends GetxController {
       await Future.delayed(const Duration(seconds: 5));
       updateStatus(ConnectionStatus.stopped);
       await controllers.global.updateDate();
+      await controllers.global.systemProxySwitch(true);
     } else {
       updateStatus(ConnectionStatus.disconnected);
     }
@@ -87,9 +88,25 @@ class HomeController extends GetxController {
       controllers.global.allowStatusUpdate = false;
       // // 停止服务
       // await controllers.service.stopClashCore();
+      controllers.config.clashCoreTunEnable.value = false;
+      controllers.service.reloadClashCore();
+
+      await controllers.global.systemProxySwitch(false);
+      await controllers.global.updateDate();
       return;
     } else {
-
+      var routeModes = controllers.global.routeModesSelect.value;
+      if(routeModes == "tun"){
+        controllers.global.startTunMode();
+        controllers.config.clashCoreTunEnable.value = true;
+        controllers.service.serviceModeSwitch(true);
+        //controllers.service.reloadClashCore();
+
+      } else {
+        controllers.global.startSysMode();
+        controllers.config.clashCoreTunEnable.value = false;
+        controllers.service.reloadClashCore();
+      }
       controllers.global.allowStatusUpdate = true;
       updateStatus(ConnectionStatus.connecting);
       await Future.delayed(const Duration(seconds: 5));

+ 14 - 1
lib/app/modules/home/views/home_view.dart

@@ -37,7 +37,7 @@ class HomeView extends GetView<HomeController> {
                     controller.fetchNode();
                   },
                 ),
-                const Text('刷新节点'), // 这里是您的标题
+                IconButton(onPressed: (){}, icon: const Icon(Icons.exit_to_app))
               ],
             ),
           ],),
@@ -137,6 +137,19 @@ class HomeView extends GetView<HomeController> {
                     value: controllers.global.modes.indexOf(controllers.core.config.value.mode),
                     onSelect: disabled ? null : (idx) => controllers.core.fetchConfigUpdate({'mode': controllers.global.modes[idx]}),
                   ),
+                ),
+                const SizedBox(height: 20,),
+                Align(
+                  alignment: Alignment.center,
+                  child:ButtonSelect(
+                    labels: ['route_sys_tile'.tr,'route_tun_title'.tr,],
+                    value: controllers.global.routeModes.indexOf(controllers.global.routeModesSelect.value),
+                    onSelect: disabled ? null : (idx) => {
+
+
+                      controllers.global.updateRoute(controllers.global.routeModes[idx])
+                    },
+                  ),
                 )
               ],
             );

+ 1 - 1
lib/app/modules/node/controllers/node_controller.dart

@@ -69,7 +69,7 @@ class NodeController extends GetxController {
   void selectNode(NodeMode node) {
     controllers.global.selectedNode.value = node;
     _storeSelectedNode(node);
-
+    controllers.global.swift(node.name);
     Get.back();
     //selectedIndex.value = nodeModes.indexWhere((item) => item.id == node.id);
   }

+ 2 - 2
lib/app/modules/node/views/node_view.dart

@@ -75,11 +75,11 @@ class NodeView extends GetView<NodeController> {
                       children: [
                         ElevatedButton(
                           onPressed: () => controller.filterNodesWithLeastUsersInHK(),
-                          child: Text("人数最少"),
+                          child: const Text("人数最少"),
                         ),
                         ElevatedButton(
                           onPressed: () => controller.showAllNodes(),
-                          child: Text("显示所有节点"),
+                          child: const Text("显示所有节点"),
                         ),
                         // ElevatedButton(
                         //   onPressed: () => controller.showSelectedFirst(),

+ 9 - 0
scripts/init.dart

@@ -126,6 +126,15 @@ Future downloadCountryMmdb() async {
   final String geoipFilePath = path.join(depDir.path, 'Country.mmdb');
   await dio.download('https://github.com/Dreamacro/maxmind-geoip/releases/latest/download/Country.mmdb', geoipFilePath);
   print('Download Success');
+
+
+  final String geositedat = path.join(depDir.path, 'geosite.dat');
+  await dio.download('https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat', geositedat);
+  print('Download Success');
+
+  final String geoip = path.join(depDir.path, 'geoip.dat');
+  await dio.download('https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat', geoip);
+  print('Download Success');
 }
 
 Future downloadWintun() async {