import 'dart:convert'; import 'dart:io'; import 'package:dio/dio.dart'; import 'package:dio/io.dart'; import 'package:nomo/app/api/router/api_router.dart'; import 'package:shared_preferences/shared_preferences.dart'; import '../../utils/crypto.dart'; import '../../utils/developer/ix_developer_tools.dart'; import '../../utils/log/logger.dart'; import '../api/core/api_core.dart'; import '../api/file/api_file.dart'; import '../api/log/api_log.dart'; import 'configs.dart'; import '../data/models/launch/launch.dart'; import 'keys.dart'; class ApiDomains { static final ApiDomains _instance = ApiDomains._internal(); static ApiDomains get instance => _instance; factory ApiDomains() => _instance; ApiDomains._internal(); // 常量定义 static const String _STORAGE_API_URLS = 'api_urls'; static const String _STORAGE_ROUTER_URLS = 'router_urls'; static const String _STORAGE_LOG_URLS = 'log_urls'; static const String _STORAGE_FILE_URLS = 'file_urls'; static const String _STORAGE_BACKUP_URLS = 'backup_api_urls'; static const String _STORAGE_API_URLS_INDEX = 'api_urls_index'; static const String _STORAGE_ROUTER_URLS_INDEX = 'router_urls_index'; static const String _STORAGE_LOG_URLS_INDEX = 'log_urls_index'; static const String _STORAGE_FILE_URLS_INDEX = 'file_urls_index'; static const String _STORAGE_IS_HARDCODED = 'is_hardcoded'; static const int RETRY_COUNT_PER_LAUNCH = 2; final Dio dio = Dio( BaseOptions( connectTimeout: const Duration(seconds: 30), receiveTimeout: const Duration(seconds: 30), sendTimeout: const Duration(seconds: 30), ), ); // 标记是否已经添加了监控拦截器 bool _monitorInterceptorAdded = false; setProxy(String proxy) { dio.httpClientAdapter = IOHttpClientAdapter( createHttpClient: () { final client = HttpClient(); client.findProxy = (uri) => proxy; client.badCertificateCallback = (X509Certificate cert, String host, int port) => true; return client; }, ); } // 默认API URL列表 final List _defaultApiUrls = [ "https://d2aphju2iq7g2g.cloudfront.net", "https://api.turboaccel.website", "https://d1rvevafipy7o6.cloudfront.net", "https://api.speedboost.website", "https://dqmkongldmyxf.cloudfront.net", "https://api.fastforward.website", "https://d2cvqygi5xlqp3.cloudfront.net", "https://api.odyxighs.com", "https://service.fkey.club", ]; // 默认Router URL列表 final List _defaultRouterUrls = []; // 默认LOG API URL列表 final List _defaultLogUrls = ['https://stat.fkey.win']; // 默认FILE API URL列表 final List _defaultFileUrls = []; // 默认备用URL列表(txt文件地址) final List _defaultBackupApiUrls = [ 'https://drive.google.com/uc?export=download&id=1YMEZiAHPhlAg_xZqD7dSDt0WjlcogwVz', 'https://fkey.win/.well-known/backup/backup_fk.data', ]; // 当前使用的URL列表 List _apiUrls = []; // 当前使用的Router URL列表 List _routerUrls = []; // 当前使用的LogURL列表 List _logUrls = []; // 当前使用的FILE URL列表 List _fileUrls = []; // 当前使用的备用URL列表 List _backupApiUrls = []; // 当前尝试的索引 int _currentIndex = 0; // 当前使用的Router URL索引 int _currentRouterIndex = 0; // 当前使用的LogURL索引 int _currentLogIndex = 0; // 当前使用的FILE URL索引 int _currentFileIndex = 0; // 当前正在使用的备用列表索引 int _currentBackupListIndex = 0; // 添加标志位,标识是否使用的是硬编码URL bool _isUsingHardcodedUrls = true; // 添加标志位,记录硬编码请求次数 int _hardcodedRequestCount = 0; // 初始化方法 Future init() async { initUrls(); // 尝试加载保存的URLs await _loadSavedUrls(); } void initUrls() { if (Configs.debug) { _apiUrls = [ 'https://api.znomo.com', // 测试环境 // "https://nomo-api.clickto.dev", // 开发环境 ]; _routerUrls = []; _logUrls = []; _fileUrls = []; _backupApiUrls = []; } else { _apiUrls = List.from(_defaultApiUrls); _routerUrls = List.from(_defaultRouterUrls); _logUrls = List.from(_defaultLogUrls); _fileUrls = List.from(_defaultFileUrls); _backupApiUrls = List.from(_defaultBackupApiUrls); } } // 修改加载保存的URL方法,返回是否成功加载 Future _loadSavedUrls() async { try { final prefs = await SharedPreferences.getInstance(); _currentIndex = prefs.getInt(_STORAGE_API_URLS_INDEX) ?? 0; _currentRouterIndex = prefs.getInt(_STORAGE_ROUTER_URLS_INDEX) ?? 0; _currentLogIndex = prefs.getInt(_STORAGE_LOG_URLS_INDEX) ?? 0; _currentFileIndex = prefs.getInt(_STORAGE_FILE_URLS_INDEX) ?? 0; _isUsingHardcodedUrls = prefs.getBool(_STORAGE_IS_HARDCODED) ?? true; final savedApiUrls = prefs.getStringList(_STORAGE_API_URLS); final savedRouterUrls = prefs.getStringList(_STORAGE_ROUTER_URLS); final savedBackupUrls = prefs.getStringList(_STORAGE_BACKUP_URLS); final savedLogUrls = prefs.getStringList(_STORAGE_LOG_URLS); final savedFileUrls = prefs.getStringList(_STORAGE_FILE_URLS); if (savedApiUrls != null && savedApiUrls.isNotEmpty) { _apiUrls = savedApiUrls; } if (savedRouterUrls != null && savedRouterUrls.isNotEmpty) { _routerUrls = savedRouterUrls; } if (savedBackupUrls != null && savedBackupUrls.isNotEmpty) { _backupApiUrls = savedBackupUrls; } if (savedLogUrls != null && savedLogUrls.isNotEmpty) { _logUrls = savedLogUrls; } if (savedFileUrls != null && savedFileUrls.isNotEmpty) { _fileUrls = savedFileUrls; } log( 'ApiDomains', 'Loaded saved URLs: ${_apiUrls.length} APIs, ${_backupApiUrls.length} backups, ${_logUrls.length} logs, ${_fileUrls.length} files', ); } catch (e) { log('ApiDomains', 'Error loading saved URLs: $e'); } } Future setApiUrls(List urls) async { try { final prefs = await SharedPreferences.getInstance(); await prefs.setStringList(_STORAGE_API_URLS, urls); _currentIndex = 0; await prefs.setInt(_STORAGE_API_URLS_INDEX, _currentIndex); ApiCore().setbaseUrl(urls[_currentIndex]); } catch (e) { log('ApiDomains', 'Error saving URLs: $e'); } } // 添加Router URL Future addRouterUrls(List urls) async { try { var routerList = List.from(_routerUrls); routerList.insertAll(0, urls); _routerUrls = routerList.toSet().toList(); await _saveRouterUrls(); } catch (e) { log('ApiDomains', 'Error add Router URL: $e'); } } // 添加logUrl Future addLogUrls(List urls) async { try { var logList = List.from(_logUrls); logList.insertAll(0, urls); _logUrls = logList.toSet().toList(); await _saveLogUrls(); } catch (e) { log('ApiDomains', 'Error add Log URL: $e'); } } // 添加fileUrl Future addFileUrls(List urls) async { try { var fileList = List.from(_fileUrls); fileList.insertAll(0, urls); _fileUrls = fileList.toSet().toList(); await _saveFileUrls(); } catch (e) { log('ApiDomains', 'Error add File URL: $e'); } } // 保存当前URL列表到持久化存储 Future _saveApiUrls() async { try { final prefs = await SharedPreferences.getInstance(); await prefs.setStringList(_STORAGE_API_URLS, _apiUrls); await prefs.setStringList(_STORAGE_BACKUP_URLS, _backupApiUrls); } catch (e) { log('ApiDomains', 'Error saving URLs: $e'); } } // 保存当前Router URL列表到持久化存储 Future _saveRouterUrls() async { try { final prefs = await SharedPreferences.getInstance(); await prefs.setStringList(_STORAGE_ROUTER_URLS, _routerUrls); } catch (e) { log('ApiDomains', 'Error saving Router URLs: $e'); } } // 保存当前LogURL列表到持久化存储 Future _saveLogUrls() async { try { final prefs = await SharedPreferences.getInstance(); await prefs.setStringList(_STORAGE_LOG_URLS, _logUrls); } catch (e) { log('ApiDomains', 'Error saving Log URLs: $e'); } } // 保存当前FILE URL列表到持久化存储 Future _saveFileUrls() async { try { final prefs = await SharedPreferences.getInstance(); await prefs.setStringList(_STORAGE_FILE_URLS, _fileUrls); } catch (e) { log('ApiDomains', 'Error saving File URLs: $e'); } } // 保存硬编码状态 Future _saveIsHardcodedUrls() async { try { final prefs = await SharedPreferences.getInstance(); await prefs.setBool(_STORAGE_IS_HARDCODED, _isUsingHardcodedUrls); } catch (e) { log('ApiDomains', 'Error saving is hardcoded URLs: $e'); } } // 保存硬编码索引 Future _saveApiUrlsIndex() async { try { final prefs = await SharedPreferences.getInstance(); await prefs.setInt(_STORAGE_API_URLS_INDEX, _currentIndex); } catch (e) { log('ApiDomains', 'Error saving hardcoded index: $e'); } } // 保存硬编码索引 Future _saveRouterUrlsIndex() async { try { final prefs = await SharedPreferences.getInstance(); await prefs.setInt(_STORAGE_ROUTER_URLS_INDEX, _currentRouterIndex); } catch (e) { log('ApiDomains', 'Error saving Router URLs index: $e'); } } // 保存LogURL硬编码索引 Future _saveLogUrlsIndex() async { try { final prefs = await SharedPreferences.getInstance(); await prefs.setInt(_STORAGE_LOG_URLS_INDEX, _currentLogIndex); } catch (e) { log('ApiDomains', 'Error saving Log URLs index: $e'); } } // 保存FILE URL硬编码索引 Future _saveFileUrlsIndex() async { try { final prefs = await SharedPreferences.getInstance(); await prefs.setInt(_STORAGE_FILE_URLS_INDEX, _currentFileIndex); } catch (e) { log('ApiDomains', 'Error saving File URLs index: $e'); } } // 从Launch数据更新URL列表 Future updateFromLaunch(Launch launch) async { try { if (launch.appConfig?.apiUrls != null && launch.appConfig!.apiUrls!.isNotEmpty) { _apiUrls = List.from(launch.appConfig!.apiUrls!); // 如果ApiCore的baseUrl不在launch的apiUrls中,或者使用硬编码的url,则设置ApiCore的baseUrl为launch的apiUrls的第一个 if (!_apiUrls.contains(ApiCore().baseUrl) || _isUsingHardcodedUrls) { ApiCore().setbaseUrl(_apiUrls[0]); } final baseUrl = ApiCore().baseUrl; _apiUrls.insert(0, baseUrl); _apiUrls = _apiUrls.toSet().toList(); _currentIndex = 0; log('ApiDomains', 'Add ApiCore baseUrl to index 0: $baseUrl'); } if (launch.appConfig?.routerApiUrls != null && launch.appConfig!.routerApiUrls!.isNotEmpty) { _routerUrls = launch.appConfig!.routerApiUrls!; final baseUrl = _routerUrls[0]; ApiRouter().setbaseUrl(baseUrl); _currentRouterIndex = 0; log('ApiDomains', 'Add Router baseUrl to index 0: $baseUrl'); } if (launch.appConfig?.backupApiUrls != null && launch.appConfig!.backupApiUrls!.isNotEmpty) { _backupApiUrls = launch.appConfig!.backupApiUrls!; _currentBackupListIndex = 0; } if (launch.appConfig?.appStatUrls != null && launch.appConfig!.appStatUrls!.isNotEmpty) { _logUrls = launch.appConfig!.appStatUrls!; // 设置ApiLog的baseUrl为launch的appStatUrls的第一个 final baseUrl = _logUrls[0]; ApiLog().setbaseUrl(baseUrl); _currentLogIndex = 0; log('ApiDomains', 'Add ApiLog baseUrl to index 0: $baseUrl'); } if (launch.appConfig?.logFileUploadUrls != null && launch.appConfig!.logFileUploadUrls!.isNotEmpty) { _fileUrls = launch.appConfig!.logFileUploadUrls!; // 设置ApiFile的baseUrl为launch的logFileUploadUrls的第一个 final baseUrl = _fileUrls[0]; ApiFile().setbaseUrl(baseUrl); _currentFileIndex = 0; log('ApiDomains', 'Add ApiFile baseUrl to index 0: $baseUrl'); } _isUsingHardcodedUrls = false; await _saveIsHardcodedUrls(); await _saveApiUrls(); await _saveApiUrlsIndex(); await _saveRouterUrls(); await _saveRouterUrlsIndex(); await _saveLogUrls(); await _saveLogUrlsIndex(); await _saveFileUrls(); await _saveFileUrlsIndex(); log('ApiDomains', 'Updated URLs from launch data'); } catch (e) { log('ApiDomains', 'Error updating URLs from launch data: $e'); } } // 获取当前ApiURL String getApiUrl() { if (_currentIndex >= _apiUrls.length) { _currentIndex = 0; } return _apiUrls[_currentIndex]; } // 获取当前Router URL String getRouterUrl() { if (_currentRouterIndex >= _routerUrls.length) { _currentRouterIndex = 0; } return _routerUrls[_currentRouterIndex]; } // 获取当前LogURL String getLogUrl() { if (_currentLogIndex >= _logUrls.length) { _currentLogIndex = 0; } return _logUrls[_currentLogIndex]; } // 获取当前FILE URL String getFileUrl() { if (_currentFileIndex >= _fileUrls.length) { _currentFileIndex = 0; } return _fileUrls[_currentFileIndex]; } Future isHardcodedUrls() async { return _isUsingHardcodedUrls; } // 获取下一次要尝试的URL Future getNextApiUrl() async { // 使用硬编码URLs的情况 if (_isUsingHardcodedUrls) { // 如果硬编码请求次数大于等于最大重试次数,返回空字符串 if (_hardcodedRequestCount >= RETRY_COUNT_PER_LAUNCH) { _hardcodedRequestCount = 0; return ''; } // 获取下一个索引 int nextIndex = _currentIndex + 1; if (nextIndex >= _apiUrls.length) { if (_hardcodedRequestCount < RETRY_COUNT_PER_LAUNCH) { final isSuccess = await _switchToBackupList(); if (isSuccess) { return getApiUrl(); } else { _hardcodedRequestCount = 0; return ''; } } return ''; } final url = _apiUrls[nextIndex]; _currentIndex++; _hardcodedRequestCount++; await _saveApiUrlsIndex(); return url; } // 使用Launch数据的情况 else { // 如果当前列表已用完 int nextIndex = _currentIndex + 1; if (nextIndex >= _apiUrls.length) { // 尝试切换到备用列表 final isSuccess = await _switchToBackupList(); if (isSuccess) { return getApiUrl(); } else { return ''; } } // 返回当前URL final url = _apiUrls[nextIndex]; _currentIndex++; await _saveApiUrlsIndex(); return url; } } // 获取下一次要尝试的Router URL Future getNextRouterUrl() async { // 获取下一个索引 int nextIndex = _currentRouterIndex + 1; if (nextIndex >= _routerUrls.length) { _currentRouterIndex = 0; await _saveRouterUrlsIndex(); return ''; } // 返回所有剩余的URL final url = _routerUrls[nextIndex]; _currentRouterIndex++; await _saveRouterUrlsIndex(); return url; } // 获取下一次要尝试的LogURL Future getNextLogUrl() async { // 获取下一个索引 int nextIndex = _currentLogIndex + 1; if (nextIndex >= _logUrls.length) { _currentLogIndex = 0; await _saveLogUrlsIndex(); return ''; } // 返回所有剩余的URL final url = _logUrls[nextIndex]; _currentLogIndex++; await _saveLogUrlsIndex(); return url; } // 获取下一次要尝试的FILE URL Future getNextFileUrl() async { // 获取下一个索引 int nextIndex = _currentFileIndex + 1; if (nextIndex >= _fileUrls.length) { _currentFileIndex = 0; await _saveFileUrlsIndex(); return ''; } // 返回所有剩余的URL final url = _fileUrls[nextIndex]; _currentFileIndex++; await _saveFileUrlsIndex(); return url; } // 切换到备用列表 Future _switchToBackupList() async { while (_currentBackupListIndex < _backupApiUrls.length) { try { if (_currentBackupListIndex >= _backupApiUrls.length) { log('ApiDomains', 'Error: Backup URL index out of bounds'); break; } final backupUrl = _backupApiUrls[_currentBackupListIndex]; log('ApiDomains', 'Trying backup URL: $backupUrl'); // 添加talker日志(避免重复添加) if (!_monitorInterceptorAdded) { dio.interceptors.add(SimpleNoSignApiMonitorInterceptor()); _monitorInterceptorAdded = true; } final response = await dio.get(backupUrl); if (response.statusCode == 200) { log('Before decryption: ${response.data}'); final decryptedBytes = Crypto.decryptBytes( response.data.toString().trim(), Keys.aesKey, Keys.aesIv, ); final jsonText = utf8.decode(decryptedBytes); log('Decryption results: $jsonText'); final Map map = jsonDecode(jsonText); if (map['apiUrls'] != null) { final list = List.from(map['apiUrls']); _apiUrls = list; _currentIndex = 0; // 当前备用文件成功,标志可以尝试下一个 _currentBackupListIndex++; await _saveApiUrls(); await _saveApiUrlsIndex(); log('ApiDomains', 'Switched to backup list: ${list.length} URLs'); return true; } } // 当前备用文件失败,尝试下一个 _currentBackupListIndex++; } catch (e) { log('ApiDomains', 'Error switching to backup list: $e'); _currentBackupListIndex++; } } // 如果所有备用URL都失败了,重置为硬编码模式 _isUsingHardcodedUrls = true; _currentIndex = 0; _currentBackupListIndex = 0; initUrls(); await _saveIsHardcodedUrls(); await _saveApiUrls(); await _saveApiUrlsIndex(); return false; } // 获取所有logUrl List getAllLogUrls() { return _logUrls; } }