import 'dart:io' as io; import 'dart:math'; import 'dart:convert'; import 'package:fpdart/fpdart.dart'; import 'package:get/get.dart'; import 'package:kaer_with_panels/app/mixins/kr_app_bar_opacity_mixin.dart'; import 'package:kaer_with_panels/app/services/api_service/api.dart'; import 'package:kaer_with_panels/app/model/enum/kr_request_type.dart'; import 'package:kaer_with_panels/app/model/response/kr_is_register.dart'; import 'package:kaer_with_panels/app/model/response/kr_check_subscription.dart'; import 'package:kaer_with_panels/app/model/response/kr_login_data.dart'; import 'package:kaer_with_panels/app/network/base_response.dart'; import 'package:kaer_with_panels/app/network/http_error.dart'; import 'package:kaer_with_panels/app/network/http_util.dart'; import '../../utils/kr_common_util.dart'; import '../../utils/kr_log_util.dart'; import '../../utils/kr_aes_util.dart'; import '../kr_device_info_service.dart'; import '../kr_site_config_service.dart'; import '../../common/app_config.dart'; import 'package:dio/dio.dart' as dio; import 'package:kaer_with_panels/app/services/singbox_imp/kr_sing_box_imp.dart'; import 'package:kaer_with_panels/app/utils/kr_http_adapter_util.dart'; import 'package:flutter/foundation.dart'; class KRAuthApi { /// 检查账号是否已注册(仅支持邮箱) Future> kr_isRegister(String email) async { final Map data = {}; data['email'] = email; final deviceId = KRDeviceInfoService().deviceId ?? 'unknown'; KRLogUtil.kr_i('设备ID: $deviceId', tag: 'KRAuthApi'); data["identifier"] = deviceId; BaseResponse baseResponse = await HttpUtil.getInstance() .request(Api.kr_isRegister, data, method: HttpMethod.GET, isShowLoading: true); if (!baseResponse.isSuccess) { return left( HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); } return right(baseResponse.model.kr_isRegister); } /// 检查账号和邮箱是否都已经有订阅(只有两者都订阅才返回 true) Future> kr_checkSubscription(String email) async { final Map data = {}; data['email'] = email; // 获取当前设备 ID final deviceId = KRDeviceInfoService().deviceId ?? 'unknown'; KRLogUtil.kr_i('设备ID: $deviceId', tag: 'KRAuthApi'); data["identifier"] = deviceId; // 发起请求 BaseResponse baseResponse = await HttpUtil.getInstance() .request( Api.kr_checkSubscription, data, method: HttpMethod.POST, isShowLoading: true, ); // 请求失败 if (!baseResponse.isSuccess) { return left(HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); } final subscription = baseResponse.model; // 只有设备和邮箱都已订阅才返回 true return right(subscription.isFullySubscribed); } /// 忘记密码-设置新密码(仅支持邮箱) Future> kr_setNewPsdByForgetPsd( String email, String code, String password) async { final Map data = {}; data['email'] = email; data['password'] = password; data["code"] = code; data["identifier"] = KRDeviceInfoService().deviceId ?? 'unknown'; BaseResponse baseResponse = await HttpUtil.getInstance() .request(Api.kr_setNewPsdByForgetPsd, data, method: HttpMethod.POST, isShowLoading: true); if (!baseResponse.isSuccess) { return left( HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); } return right(baseResponse.model.kr_token.toString()); } /// 注册(仅支持邮箱+密码,验证码和邀请码可选) Future> kr_register( String email, String password, {String? code, String? inviteCode}) async { final Map data = {}; data['email'] = email; data["code"] = code; // data['password'] = password; // data["identifier"] = KRDeviceInfoService().deviceId ?? 'unknown'; // 验证码是可选的,只有在提供时才发送 // if (code != null && code.isNotEmpty) { // // } // 邀请码是可选的 // if (inviteCode != null && inviteCode.isNotEmpty) { // data["invite"] = inviteCode; // } BaseResponse baseResponse = await HttpUtil.getInstance() .request(Api.kr_register, data, method: HttpMethod.POST, isShowLoading: true); if (!baseResponse.isSuccess) { return left( HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); } return right(baseResponse.model.kr_token.toString()); } /// 手机号注册(手机号+区号+密码+验证码,邀请码可选) Future> kr_telephoneRegister( String telephone, String areaCode, String password, String code, {String? inviteCode}) async { final Map data = {}; data['telephone'] = telephone; data['telephone_area_code'] = areaCode; data['password'] = password; data['code'] = code; data["identifier"] = KRDeviceInfoService().deviceId ?? 'unknown'; // 邀请码是可选的 if (inviteCode != null && inviteCode.isNotEmpty) { data["invite"] = inviteCode; } // cf_token 为空字符串(如果后续需要可以添加) data["cf_token"] = ""; BaseResponse baseResponse = await HttpUtil.getInstance() .request('/v1/auth/register/telephone', data, method: HttpMethod.POST, isShowLoading: true); if (!baseResponse.isSuccess) { return left( HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); } return right(baseResponse.model.kr_token.toString()); } /// 验证验证码(仅支持邮箱) Future> kr_checkVerificationCode( String email, String code, int type) async { final Map data = {}; data['email'] = email; data['code'] = code; data['type'] = type; BaseResponse baseResponse = await HttpUtil.getInstance() .request(Api.kr_checkVerificationCode, data, method: HttpMethod.POST, isShowLoading: true); if (!baseResponse.isSuccess) { return left( HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); } if (baseResponse.model.kr_isRegister) { return right(true); } else { return left(HttpError(msg: "error.70001".tr, code: 70001)); } } /// 登录(仅支持邮箱+密码) Future> kr_login( String email, String password) async { final Map data = {}; data['email'] = email; data['password'] = password; final deviceId = KRDeviceInfoService().deviceId ?? 'unknown'; KRLogUtil.kr_i('设备ID: $deviceId', tag: 'KRAuthApi'); // data["identifier"] = deviceId; BaseResponse baseResponse = await HttpUtil.getInstance() .request(Api.kr_login, data, method: HttpMethod.POST, isShowLoading: true); if (!baseResponse.isSuccess) { return left( HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); } return right(baseResponse.model.kr_token.toString()); } /// 手机号密码登录(手机号+区号+密码) Future> kr_telephoneLogin( String telephone, String areaCode, String password) async { final Map data = {}; data['telephone'] = telephone; data['telephone_area_code'] = areaCode; data['password'] = password; final deviceId = KRDeviceInfoService().deviceId ?? 'unknown'; KRLogUtil.kr_i('设备ID: $deviceId', tag: 'KRAuthApi'); data["identifier"] = deviceId; BaseResponse baseResponse = await HttpUtil.getInstance() .request('/v1/auth/login/telephone', data, method: HttpMethod.POST, isShowLoading: true); if (!baseResponse.isSuccess) { return left( HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); } return right(baseResponse.model.kr_token.toString()); } /// 手机号验证码登录(手机号+区号+验证码) Future> kr_telephoneCodeLogin( String telephone, String areaCode, String code) async { final Map data = {}; data['telephone'] = telephone; data['telephone_area_code'] = areaCode; data['telephone_code'] = code; final deviceId = KRDeviceInfoService().deviceId ?? 'unknown'; KRLogUtil.kr_i('设备ID: $deviceId', tag: 'KRAuthApi'); data["identifier"] = deviceId; BaseResponse baseResponse = await HttpUtil.getInstance() .request('/v1/auth/login/telephone', data, method: HttpMethod.POST, isShowLoading: true); if (!baseResponse.isSuccess) { return left( HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); } return right(baseResponse.model.kr_token.toString()); } /// 发送验证码(仅支持邮箱) /// type: 1=登录, 2=注册, 3=重置密码 Future> kr_sendCode(String email, int type) async { final Map data = {}; data['email'] = email; data['type'] = type; BaseResponse baseResponse = await HttpUtil.getInstance() .request(Api.kr_sendCode, data, method: HttpMethod.POST, isShowLoading: true); if (!baseResponse.isSuccess) { return left( HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); } return right(true); } /// 发送短信验证码(支持手机号) /// type: 1=注册, 2=验证登录 Future> kr_sendSmsCode( String telephone, String areaCode, int type) async { final Map data = {}; data['telephone'] = telephone; data['telephone_area_code'] = areaCode; data['type'] = type; BaseResponse baseResponse = await HttpUtil.getInstance() .request('/v1/common/send_sms_code', data, method: HttpMethod.POST, isShowLoading: true); if (!baseResponse.isSuccess) { return left( HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); } return right(true); } /// 删除账号 Future> kr_deleteAccount(String email, String code) async { final Map data = {}; data['email'] = email; data['code'] = code; BaseResponse baseResponse = await HttpUtil.getInstance() .request(Api.kr_deleteAccount, data, method: HttpMethod.POST, isShowLoading: true); if (!baseResponse.isSuccess) { return left( HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); } return right(""); } /// 忘记密码-邮箱重置密码 Future> kr_resetPassword( String email, String code, String password) async { final Map data = {}; data['identifier'] = KRDeviceInfoService().deviceId ?? 'unknown'; data['email'] = email; data['password'] = password; data['code'] = code; data['cf_token'] = ""; // Cloudflare token,暂时留空 BaseResponse baseResponse = await HttpUtil.getInstance() .request(Api.kr_resetPassword, data, method: HttpMethod.POST, isShowLoading: true); if (!baseResponse.isSuccess) { return left( HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); } return right(baseResponse.model.kr_token.toString()); } /// 忘记密码-手机号重置密码 Future> kr_resetPasswordByTelephone( String telephone, String areaCode, String code, String password) async { final Map data = {}; data['identifier'] = KRDeviceInfoService().deviceId ?? 'unknown'; data['telephone'] = telephone; data['telephone_area_code'] = areaCode; data['password'] = password; data['code'] = code; data['cf_token'] = ""; // Cloudflare token,暂时留空 BaseResponse baseResponse = await HttpUtil.getInstance() .request(Api.kr_resetPasswordByTelephone, data, method: HttpMethod.POST, isShowLoading: true); if (!baseResponse.isSuccess) { return left( HttpError(msg: baseResponse.retMsg, code: baseResponse.retCode)); } return right(baseResponse.model.kr_token.toString()); } /// 设备登录(游客登录) Future> kr_deviceLogin() async { try { KRLogUtil.kr_i('🔐 开始设备登录', tag: 'KRAuthApi'); // 获取设备信息 final deviceInfoService = KRDeviceInfoService(); final deviceId = deviceInfoService.deviceId; final userAgent = deviceInfoService.getUserAgent(); if (deviceId == null) { print('❌ 设备ID为空,无法登录'); return left(HttpError(msg: '设备ID获取失败', code: -1)); } print('📱 设备ID: $deviceId'); print('📱 User-Agent: $userAgent'); KRLogUtil.kr_i('📱 设备ID: $deviceId', tag: 'KRAuthApi'); KRLogUtil.kr_i('📱 User-Agent: $userAgent', tag: 'KRAuthApi'); // 构建请求数据 Map data = { 'identifier': deviceId, 'user_agent': userAgent, }; print('📤 原始请求数据: $data'); // 检查是否需要加密 final siteConfigService = KRSiteConfigService(); final needEncryption = siteConfigService.isDeviceSecurityEnabled(); print('🔒 是否需要加密: $needEncryption'); KRLogUtil.kr_i('🔒 是否需要加密: $needEncryption', tag: 'KRAuthApi'); String? requestBody; if (needEncryption) { // 加密请求数据 print('🔐 加密请求数据...'); final encrypted = KRAesUtil.encryptJson(data, AppConfig.kr_encryptionKey); requestBody = '{"data":"${encrypted['data']}","time":"${encrypted['time']}"}'; print('🔐 加密后请求体: $requestBody'); KRLogUtil.kr_i('🔐 加密后请求体', tag: 'KRAuthApi'); } else { // 使用明文 requestBody = jsonEncode(data); print('📝 明文请求体: $requestBody'); } // 使用 Dio 直接发送请求(因为需要特殊的加密处理) final dioInstance = dio.Dio(); // ✅ 关键修复:统一使用配置好的 Adapter,包含 SSL 验证绕过和代理处理 dioInstance.httpClientAdapter = KRHttpAdapterUtil.createAdapter( useSingBoxProxy: false, // 设备登录建议先直连 forceDirect: true, timeout: const Duration(seconds: 10), ); final baseUrl = AppConfig.getInstance().baseUrl; final url = '$baseUrl${Api.kr_deviceLogin}'; print('📤 请求URL: $url'); KRLogUtil.kr_i('📤 请求URL: $url', tag: 'KRAuthApi'); // 设置请求头 final headers = { 'Content-Type': 'application/json', }; if (needEncryption) { headers['Login-Type'] = 'device'; } print('📤 请求头: $headers'); // 配置Dio实例的超时设置 dioInstance.options.connectTimeout = const Duration(seconds: 10); dioInstance.options.sendTimeout = const Duration(seconds: 10); dioInstance.options.receiveTimeout = const Duration(seconds: 10); final response = await dioInstance.post( url, data: requestBody, options: dio.Options( headers: headers, ), ); print('📥 响应状态码: ${response.statusCode}'); print('📥 响应数据: ${response.data}'); KRLogUtil.kr_i('📥 响应状态码: ${response.statusCode}', tag: 'KRAuthApi'); KRLogUtil.kr_i('📥 响应数据: ${response.data}', tag: 'KRAuthApi'); if (response.statusCode == 200) { Map responseData = response.data as Map; // 检查是否需要解密响应 if (needEncryption && responseData.containsKey('data')) { final dataField = responseData['data']; if (dataField is Map && dataField.containsKey('data') && dataField.containsKey('time')) { print('🔓 解密响应数据...'); final decrypted = KRAesUtil.decryptJson( dataField['data'] as String, dataField['time'] as String, AppConfig.kr_encryptionKey, ); responseData['data'] = decrypted; print('🔓 解密后数据: ${responseData['data']}'); KRLogUtil.kr_i('🔓 解密成功', tag: 'KRAuthApi'); } } if (responseData['code'] == 200) { final token = responseData['data']['token'] as String; print('✅ 设备登录成功'); print('🎫 Token: ${token.substring(0, min(20, token.length))}...'); print('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); KRLogUtil.kr_i('✅ 设备登录成功', tag: 'KRAuthApi'); return right(token); } else { final msg = responseData['msg'] ?? '登录失败'; print('❌ 登录失败: $msg'); return left(HttpError(msg: msg, code: responseData['code'])); } } else { print('❌ HTTP错误: ${response.statusCode}'); return left(HttpError(msg: 'HTTP错误', code: response.statusCode ?? -1)); } } on dio.DioException catch (e) { print('❌ Dio异常: ${e.type}'); print('❌ 错误信息: ${e.message}'); KRLogUtil.kr_e('❌ 设备登录Dio异常: ${e.message}', tag: 'KRAuthApi'); return left(HttpError(msg: '网络请求失败: ${e.message}', code: -1)); } catch (e, stackTrace) { print('❌ 设备登录异常: $e'); print('📚 堆栈跟踪: $stackTrace'); KRLogUtil.kr_e('❌ 设备登录异常: $e', tag: 'KRAuthApi'); return left(HttpError(msg: '设备登录失败: $e', code: -1)); } } String _kr_getUserAgent() { if (io.Platform.isAndroid) { return 'android'; } else if (io.Platform.isIOS) { return 'ios'; } else if (io.Platform.isMacOS) { return 'mac'; } else if (io.Platform.isWindows) { return 'windows'; } else if (io.Platform.isLinux) { return 'linux'; } else if (io.Platform.isFuchsia) { return 'harmony'; } else { return 'unknown'; } } }