alroyso 1 year ago
parent
commit
534d6af612

+ 3 - 0
lib/app/common/constants.dart

@@ -2,4 +2,7 @@
 
 const kSysConfig = "/api/client/v3/getconfig";
 const kLogin = "/api/client/v3/login";
+const KReg = "/api/client/v2/register";
+const KLogout = "/api/client/v2/logout";
+const KAuthUser = "/api/client/v2/authUser";
 const kNode = '';

+ 8 - 4
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/common/constants.dart';
 import 'package:naiyouwl/app/component/dialogs.dart';
 import 'package:naiyouwl/app/controller/controllers.dart';
 import 'package:naiyouwl/app/data/model/NodeMode.dart';
@@ -108,6 +109,9 @@ class GlobalController extends GetxController {
 
   }
 
+
+
+
   Future<void> updateNode() async {
     NodeMode? targetNode;
     if (selectedNode.value == null) {
@@ -117,7 +121,7 @@ class GlobalController extends GetxController {
     }
     if (targetNode != null){
       selectNode(targetNode);
-      await swift(targetNode.name);
+      await swift(targetNode.name ?? "");
 
     }
   }
@@ -177,7 +181,7 @@ class GlobalController extends GetxController {
   Future<NodeMode> findNodeWithMinUsers(List<NodeMode> nodes) async {
     return nodes
         .where((node) => node.countryCode == "hk")
-        .reduce((a, b) => a.onlineUsers < b.onlineUsers ? a : b);
+        .reduce((a, b) => a.onlineUsers! < b.onlineUsers! ? a : b);
   }
 
 
@@ -258,7 +262,7 @@ class GlobalController extends GetxController {
 
   Future<void> handleSetSelectProxieGroup(NodeMode proxie, String value) async {
     if (proxie.name == value) return;
-    await controllers.core.fetchSetProxieGroup(proxie.name, value);
+    await controllers.core.fetchSetProxieGroup(proxie.name ?? "", value);
     await updateDate();
     final conn = await controllers.core.fetchConnection();
     for (final it in conn.connections) {
@@ -291,7 +295,7 @@ class GlobalController extends GetxController {
   Future<void> _storeSelectedNode(NodeMode node) async {
     final prefs = await SharedPreferences.getInstance();
     // 为简化起见,我们只存储node的ID,但您可以根据需要存储更多信息
-    prefs.setInt('selectedNodeId', node.id);
+    prefs.setInt('selectedNodeId', node.id ?? -1);
     await loadSelectedNode();
   }
 

+ 2 - 2
lib/app/controller/tray.dart

@@ -72,7 +72,7 @@ class TrayController extends GetxController with TrayListener {
             MenuItem(label: 'powershell', onClick: handleClickCopyCommandLineProxy),
           ])),
       MenuItem.separator(),
-      MenuItem(label: 'tray_about'.tr, onClick: handleClickAbout),
+     // MenuItem(label: 'tray_about'.tr, onClick: handleClickAbout),
       MenuItem(label: 'tray_exit'.tr, onClick: handleClickExit),
     ]);
     await trayManager.setContextMenu(trayMenu);
@@ -115,7 +115,7 @@ class TrayController extends GetxController with TrayListener {
   }
 
   Future<void> handleClickAbout(MenuItem menuItem) async {
-    await launchUrl(Uri.parse('https://github.com/csj8520/clash_for_flutter'));
+    //await launchUrl(Uri.parse('https://github.com/csj8520/clash_for_flutter'));
   }
 
   Future<void> handleClickExit(MenuItem menuItem) async {

+ 9 - 9
lib/app/data/model/NodeMode.dart

@@ -1,21 +1,21 @@
 import 'package:dart_json_mapper/dart_json_mapper.dart';
 @jsonSerializable
 class NodeMode {
-  final int id;
-  final String name;
-  final String host;
-  final String group;
-  final String type;
-  final int port;
+  final int? id;
+  final String? name;
+  final String? host;
+  final String? group;
+  final String? type;
+  final int? port;
   final String? passwd;
   final String? sni;
-  final int udp;
+  final int? udp;
   final String? ip;
 
   @JsonProperty(name: 'online_users')
-  final int onlineUsers;
+  final int? onlineUsers;
   @JsonProperty(name: 'country_code')
-  final String countryCode;
+  final String? countryCode;
   @JsonProperty(name: 'uuid')
   final String? uuid;
   final String? method;

+ 55 - 8
lib/app/modules/home/controllers/home_controller.dart

@@ -4,12 +4,15 @@ import 'dart:io';
 
 import 'package:dart_json_mapper/dart_json_mapper.dart';
 import 'package:get/get.dart';
+import 'package:naiyouwl/app/common/constants.dart';
 import 'package:naiyouwl/app/controller/GlobalController.dart';
 import 'package:naiyouwl/app/controller/controllers.dart';
 import 'package:naiyouwl/app/controller/service.dart';
+import 'package:naiyouwl/app/modules/login/views/login_view.dart';
 import 'package:naiyouwl/app/utils/system_proxy.dart';
 import 'package:naiyouwl/app/utils/utils.dart';
 import 'package:tray_manager/tray_manager.dart';
+import 'package:url_launcher/url_launcher.dart';
 import 'package:window_manager/window_manager.dart';
 import '../../../common/LogHelper.dart';
 import '../../../common/SharedPreferencesUtil.dart';
@@ -36,7 +39,7 @@ class HomeController extends GetxController {
   var localUsers = LocalUser().obs;
   var userMode = User().obs;
   var errorMsg = ''.obs;
-
+  var UsersysConfig = SysConfig().obs;
   var connectStatus = Rx<ConnectionStatus>(ConnectionStatus.disconnected);
   var nodeModes = <NodeMode>[];
   late final GlobalController globalController = controllers.global;
@@ -48,15 +51,15 @@ class HomeController extends GetxController {
     ImageType.RENEWAL: "assets/images/main/renewal.png",
   };
 
-  void onImageTap(ImageType type) {
+  void onImageTap(ImageType type) async {
     if(type == ImageType.CUSTOMER){
-
+      await launchUrl(Uri.parse(UsersysConfig.value.userLoginUrlKe ?? ""));
     } else if (type == ImageType.PROMOTION){
-
+      await launchUrl(Uri.parse(UsersysConfig.value.affUrl ?? ""));
     } else if (type == ImageType.TUTORIAL){
-
+      await launchUrl(Uri.parse(UsersysConfig.value.userTicket ?? ""));
     } else if (type == ImageType.RENEWAL){
-
+      await launchUrl(Uri.parse(UsersysConfig.value.userBuy ?? ""));
     }
     LogHelper().d("${imageMap[type]} tapped as ${type.toString().split('.').last}");
   }
@@ -88,6 +91,12 @@ class HomeController extends GetxController {
   }
 
   Future<void> handleButtonClick() async {
+    if(userMode.value.enable != 1){
+      errorMsg.value = "用户是禁用的,无法连接";
+      return;
+    }
+
+
     if(connectStatus.value == ConnectionStatus.connecting ){
       return;
     }
@@ -144,7 +153,17 @@ class HomeController extends GetxController {
 
   }
 
-
+  Future<void> fetchAuthUser() async {
+    try {
+      final ret  = await ApiService().fetchAuthUser(KAuthUser);
+      controllers.global.selectedNode.value = ret;
+      controllers.global.selectNode(ret);
+      handleButtonClick();
+    } catch (e) {
+      errorMsg.value = e.toString();
+    } finally {
+    }
+  }
 
   Future<void> fetchSysConfig() async {
     try {
@@ -164,6 +183,7 @@ class HomeController extends GetxController {
       isLoading.value = true;
       userMode.value = await ApiService().userinfo("/api/client/v4/userinfo");
       await globalController.fetchNodes();
+      await fetchlUserUrl(localUsers.value.email ?? "",localUsers.value.password ?? "");
       //await controllers.global.makeProxy();
       //await controllers.global.initService();
     // //生成配置
@@ -189,14 +209,41 @@ class HomeController extends GetxController {
     }
   }
 
-  //await SharedPreferencesUtil().setObject("localUser", userModes.toJson());
+  Future<void> outlogin() async {
+    if(connectStatus.value == ConnectionStatus.connecting || connectStatus.value == ConnectionStatus.stopped){
+      errorMsg.value = "先断开,在退出登录";
+      return ;
+    }
+    await SharedPreferencesUtil().delete("token");
+    try {
+      isLoading.value = true;
+      await ApiService().fetchLogout(KLogout);
+    } catch (e) {
+      errorMsg.value = e.toString();
+    } finally {
+      isLoading.value = false;
+    }
+    Get.offNamed(Routes.LOGIN);
+  }
 
+  //await SharedPreferencesUtil().setObject("localUser", userModes.toJson());
+  Future<void> fetchlUserUrl(String username,String password) async {
+    try {
+      isLoading.value = true;
+      UsersysConfig.value  = await ApiService().fetchUserSysConfig(kSysConfig,{"email":username,"password":password});
+    } catch (e) {
+      errorMsg.value = e.toString();
+    } finally {
+      isLoading.value = false;
+    }
+  }
   Future<void> fetchLocalUser() async {
     try {
       String? userdata  = await SharedPreferencesUtil().getString("localUser");
       if(userdata  != null){
         localUsers.value = JsonMapper.deserialize<LocalUser>(userdata)!;
       }
+      await fetchSysConfig();
     } catch (e) {
       errorMsg.value = e.toString();
     } finally {

+ 4 - 2
lib/app/modules/home/views/home_view.dart

@@ -37,7 +37,9 @@ class HomeView extends GetView<HomeController> {
                     controller.fetchNode();
                   },
                 ),
-                IconButton(onPressed: (){}, icon: const Icon(Icons.exit_to_app))
+                IconButton(onPressed: (){
+                  controller.outlogin();
+                }, icon: const Icon(Icons.exit_to_app))
               ],
             ),
           ],),
@@ -106,7 +108,7 @@ class HomeView extends GetView<HomeController> {
                 Obx(() {
                   return ConnectionWidget(
                     status: controller.connectStatus.value, onTap: () {
-                          controller.handleButtonClick();
+                          controller.fetchAuthUser();
                   },);
                 }),
                 Padding(

+ 30 - 1
lib/app/modules/login/controllers/login_controller.dart

@@ -17,8 +17,13 @@ class LoginController extends GetxController {
   var localUsers = LocalUser().obs;
   var errorMsg = ''.obs;
 
-  Future<void> fetchLogin(username,password) async {
+  Future<void> fetchLogin(String username, String password) async {
     try {
+      if(username.isEmpty || password.isEmpty){
+        errorMsg.value = "账号密码不能为空";
+        return;
+      }
+
       isLoading.value = true;
       loginModes.value = await ApiService().login(kLogin,data: {"email":username,"password":password});
 
@@ -36,6 +41,30 @@ class LoginController extends GetxController {
     }
   }
 
+
+  Future<void> fetchRegLogin(String username, String password) async {
+    try {
+      if(username.isEmpty || password.isEmpty){
+        errorMsg.value = "账号密码不能为空";
+        return;
+      }
+      isLoading.value = true;
+      loginModes.value = await ApiService().reg(KReg,data: {"email":username,"password":password});
+
+      if(loginModes.value.accessToken != null){
+        await SharedPreferencesUtil().setString("token", loginModes.value.accessToken.toString());
+        var userModes = LocalUser(email: username,password: password,accessToken: loginModes.value.accessToken.toString());
+        await SharedPreferencesUtil().setString("localUser", JsonMapper.serialize(userModes));
+      }
+      Get.offNamed(Routes.HOME,arguments: loginModes.value);
+
+    } catch (e) {
+      errorMsg.value = e.toString();
+    } finally {
+      isLoading.value = false;
+    }
+  }
+
   Future<void> fetchLocalUser() async {
     try {
       String? data  = await SharedPreferencesUtil().getString("localUser");

+ 25 - 4
lib/app/modules/login/views/login_view.dart

@@ -34,7 +34,9 @@ class LoginView extends GetView<LoginController> {
                 onLogin: (username, password) {
                   // 在这里处理登录逻辑,例如调用API
                   controller.fetchLogin(username,password);
-                });
+                },onRegin: (username, password) {
+                  controller.fetchRegLogin(username,password);
+              },);
           })
       ),
     );
@@ -43,10 +45,11 @@ class LoginView extends GetView<LoginController> {
 
 class LoginScreen extends StatefulWidget {
   final Function(String username, String password) onLogin;
+  final Function(String username, String password) onRegin;
   final bool isLoading;
   final String username;
   final String password;
-  LoginScreen({required this.isLoading, required this.onLogin, required this.username, required this.password});
+  LoginScreen({required this.isLoading, required this.onLogin,required this.onRegin, required this.username, required this.password});
 
   @override
   _LoginScreenState createState() => _LoginScreenState();
@@ -59,10 +62,10 @@ class _LoginScreenState extends State<LoginScreen> {
   @override
   Widget build(BuildContext context) {
     return Padding(
-      padding: const EdgeInsets.only(bottom: 30),
+      padding: const EdgeInsets.only(bottom: 0),
       child: Center(
         child: Padding(
-          padding: const EdgeInsets.fromLTRB(55, 0, 55, 0),
+          padding: const EdgeInsets.fromLTRB(55, 40, 55, 0),
           child: Column(
             mainAxisAlignment: MainAxisAlignment.center,
             children: [
@@ -102,6 +105,24 @@ class _LoginScreenState extends State<LoginScreen> {
                   ) : const Text('登录'),
                 ),
               ),
+
+              const SizedBox(height: 20),
+              SizedBox(
+                width: 200,
+                height: 40,
+                child: ElevatedButton(
+                  onPressed: () {
+                    if (!widget.isLoading) {
+                      final username = _usernameController.text;
+                      final password = _passwordController.text;
+                      widget.onRegin(username, password);
+                    }
+                  },
+                  child: widget.isLoading ? const CircularProgressIndicator(
+                    color: Colors.white,
+                  ) : const Text('注册'),
+                ),
+              ),
             ],
           ),
         ),

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

@@ -32,7 +32,7 @@ class NodeController extends GetxController {
 
 
   Future<void> tcpPing(NodeMode node) async {
-    int? nodeId = node.id;
+    int? nodeId = node.id!;
 
     isLoadingMap[nodeId] = true;  // 开始ping时设置为true
     isLoadingMap.refresh();      // 通知观察者
@@ -69,7 +69,7 @@ class NodeController extends GetxController {
   void selectNode(NodeMode node) {
     controllers.global.selectedNode.value = node;
     _storeSelectedNode(node);
-    controllers.global.swift(node.name);
+    controllers.global.swift(node.name!);
     Get.back();
     //selectedIndex.value = nodeModes.indexWhere((item) => item.id == node.id);
   }
@@ -77,7 +77,7 @@ class NodeController extends GetxController {
   Future<void> _storeSelectedNode(NodeMode node) async {
     final prefs = await SharedPreferences.getInstance();
     // 为简化起见,我们只存储node的ID,但您可以根据需要存储更多信息
-    prefs.setInt('selectedNodeId', node.id);
+    prefs.setInt('selectedNodeId', node.id!);
 
     await controllers.global.loadSelectedNode();
 
@@ -97,7 +97,7 @@ class NodeController extends GetxController {
   NodeMode selectBestNode(List<NodeMode> nodes) {
     return nodes.where((node) => node.countryCode == 'HK') //筛选地区是HK的节点
         .reduce((value, element) =>
-    value.onlineUsers< element.onlineUsers? value : element); //选择人数最小的
+    value.onlineUsers!< element.onlineUsers!? value : element); //选择人数最小的
   }
 
   void selectMinOnlineUsersNodeInRegion(String region) {

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

@@ -118,7 +118,7 @@ class NodeView extends GetView<NodeController> {
                                   title: Text(node.name.toString()),
                                   //tileColor: controller.selectedNode.value?.id == node.id ? Colors.blueAccent : null,
                                   // 如果选中则更改背景颜色
-                                  subtitle: Text(node.type),
+                                  subtitle: Text(node.type ?? ""),
                                   trailing: Row(
                                     mainAxisSize: MainAxisSize.min,
                                     children: [

+ 44 - 0
lib/app/network/api_service.dart

@@ -26,6 +26,38 @@ class ApiService {
     }
   }
 
+  //authUser
+  Future<NodeMode> fetchAuthUser(String path) async {
+    final  data = await _requestWrapper(() => _dioClient.get(path));
+    final result = JsonMapper.deserialize<NodeMode>(data);
+    if (result != null) {
+      return result;
+    } else {
+      throw Exception("Failed API response is NUll");
+    }
+  }
+
+
+  Future<SysConfig> fetchLogout(String path) async {
+    final  data = await _requestWrapper(() => _dioClient.get(path));
+    final result = JsonMapper.deserialize<SysConfig>(data);
+    if (result != null) {
+      return result;
+    } else {
+      throw Exception("Failed API response is NUll");
+    }
+  }
+
+  Future<SysConfig> fetchUserSysConfig(String path, Map<String, dynamic> data) async {
+    final  ret = await _requestWrapper(() => _dioClient.get(path,queryParameters: data));
+    final result = JsonMapper.deserialize<SysConfig>(ret);
+    if (result != null) {
+      return result;
+    } else {
+      throw Exception("Failed API response is NUll");
+    }
+  }
+
   Future<LoginMode> login(String path, {Map<String, dynamic>? data}) async {
 
       final  retData =await _requestWrapper(() => _dioClient.post(path,data: data));
@@ -38,6 +70,18 @@ class ApiService {
 
   }
 
+  Future<LoginMode> reg(String path, {Map<String, dynamic>? data}) async {
+
+    final  retData =await _requestWrapper(() => _dioClient.post(path,data: data));
+    final result = JsonMapper.deserialize<LoginMode>(retData);
+    if (result != null) {
+      return result;
+    } else {
+      throw Exception("Failed API response is NUll");
+    }
+
+  }
+
 
   Future<User> userinfo(String path) async {
 

+ 10 - 10
lib/app/network/dio_client.dart

@@ -24,16 +24,16 @@ class DioClient {
     ));
 
     // 仅在调试模式下添加日志拦截器
-    // assert(() {
-    //   _dio.interceptors.add(LogInterceptor(
-    //     request: true,
-    //     requestBody: true,
-    //     responseBody: true,
-    //     error: true,
-    //     logPrint: _logger.d, // 使用 Logger 插件打印日志
-    //   ));
-    //   return true;
-    // }());
+    assert(() {
+      _dio.interceptors.add(LogInterceptor(
+        request: true,
+        requestBody: true,
+        responseBody: true,
+        error: true,
+        logPrint: _logger.d, // 使用 Logger 插件打印日志
+      ));
+      return true;
+    }());
 
     //token
     _dio.interceptors.add(TokenInterceptor());