|
|
@@ -0,0 +1,516 @@
|
|
|
+import 'dart:convert';
|
|
|
+import 'dart:io';
|
|
|
+
|
|
|
+import 'package:dio/dio.dart';
|
|
|
+import 'package:dio/io.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_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_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<String> _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",
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 默认LOG API URL列表
|
|
|
+ final List<String> _defaultLogUrls = ['https://stat.fkey.win'];
|
|
|
+
|
|
|
+ // 默认FILE API URL列表
|
|
|
+ final List<String> _defaultFileUrls = [];
|
|
|
+
|
|
|
+ // 默认备用URL列表(txt文件地址)
|
|
|
+ final List<String> _defaultBackupApiUrls = [
|
|
|
+ 'https://drive.google.com/uc?export=download&id=1YMEZiAHPhlAg_xZqD7dSDt0WjlcogwVz',
|
|
|
+ 'https://fkey.win/.well-known/backup/backup_fk.data',
|
|
|
+ ];
|
|
|
+
|
|
|
+ // 当前使用的URL列表
|
|
|
+ List<String> _apiUrls = [];
|
|
|
+
|
|
|
+ // 当前使用的LogURL列表
|
|
|
+ List<String> _logUrls = [];
|
|
|
+
|
|
|
+ // 当前使用的FILE URL列表
|
|
|
+ List<String> _fileUrls = [];
|
|
|
+
|
|
|
+ // 当前使用的备用URL列表
|
|
|
+ List<String> _backupApiUrls = [];
|
|
|
+
|
|
|
+ // 当前尝试的索引
|
|
|
+ int _currentIndex = 0;
|
|
|
+
|
|
|
+ // 当前使用的LogURL索引
|
|
|
+ int _currentLogIndex = 0;
|
|
|
+
|
|
|
+ // 当前使用的FILE URL索引
|
|
|
+ int _currentFileIndex = 0;
|
|
|
+
|
|
|
+ // 当前正在使用的备用列表索引
|
|
|
+ int _currentBackupListIndex = 0;
|
|
|
+
|
|
|
+ // 添加标志位,标识是否使用的是硬编码URL
|
|
|
+ bool _isUsingHardcodedUrls = true;
|
|
|
+
|
|
|
+ // 添加标志位,记录硬编码请求次数
|
|
|
+ int _hardcodedRequestCount = 0;
|
|
|
+
|
|
|
+ // 初始化方法
|
|
|
+ Future<void> init() async {
|
|
|
+ initUrls();
|
|
|
+ // 尝试加载保存的URLs
|
|
|
+ await _loadSavedUrls();
|
|
|
+ }
|
|
|
+
|
|
|
+ void initUrls() {
|
|
|
+ if (Configs.debug) {
|
|
|
+ _apiUrls = [
|
|
|
+ // 'https://api.znomo.com', // 测试环境
|
|
|
+ "https://nomo-api.clickto.dev", // 开发环境
|
|
|
+ ];
|
|
|
+ _logUrls = [];
|
|
|
+ _fileUrls = [];
|
|
|
+ _backupApiUrls = [];
|
|
|
+ } else {
|
|
|
+ _apiUrls = List.from(_defaultApiUrls);
|
|
|
+ _logUrls = List.from(_defaultLogUrls);
|
|
|
+ _fileUrls = List.from(_defaultFileUrls);
|
|
|
+ _backupApiUrls = List.from(_defaultBackupApiUrls);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 修改加载保存的URL方法,返回是否成功加载
|
|
|
+ Future<void> _loadSavedUrls() async {
|
|
|
+ try {
|
|
|
+ final prefs = await SharedPreferences.getInstance();
|
|
|
+ _currentIndex = prefs.getInt(_STORAGE_API_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 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 (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<void> setApiUrls(List<String> 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');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加logUrl
|
|
|
+ Future<void> addLogUrls(List<String> urls) async {
|
|
|
+ try {
|
|
|
+ var logList = List<String>.from(_logUrls);
|
|
|
+ logList.insertAll(0, urls);
|
|
|
+ _logUrls = logList.toSet().toList();
|
|
|
+ await _saveLogUrls();
|
|
|
+ } catch (e) {
|
|
|
+ log('ApiDomains', 'Error add Log URL: $e');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 添加fileUrl
|
|
|
+ Future<void> addFileUrls(List<String> urls) async {
|
|
|
+ try {
|
|
|
+ var fileList = List<String>.from(_fileUrls);
|
|
|
+ fileList.insertAll(0, urls);
|
|
|
+ _fileUrls = fileList.toSet().toList();
|
|
|
+ await _saveFileUrls();
|
|
|
+ } catch (e) {
|
|
|
+ log('ApiDomains', 'Error add File URL: $e');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存当前URL列表到持久化存储
|
|
|
+ Future<void> _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');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存当前LogURL列表到持久化存储
|
|
|
+ Future<void> _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<void> _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<void> _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<void> _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');
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 保存LogURL硬编码索引
|
|
|
+ Future<void> _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<void> _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<void> updateFromLaunch(Launch launch) async {
|
|
|
+ try {
|
|
|
+ if (launch.appConfig?.apiUrls != null &&
|
|
|
+ launch.appConfig!.apiUrls!.isNotEmpty) {
|
|
|
+ _apiUrls = List<String>.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?.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 _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];
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取当前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<bool> isHardcodedUrls() async {
|
|
|
+ return _isUsingHardcodedUrls;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取下一次要尝试的URL
|
|
|
+ Future<String> 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取下一次要尝试的LogURL
|
|
|
+ Future<String> 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<String> 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<bool> _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<String, dynamic> map = jsonDecode(jsonText);
|
|
|
+ if (map['apiUrls'] != null) {
|
|
|
+ final list = List<String>.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<String> getAllLogUrls() {
|
|
|
+ return _logUrls;
|
|
|
+ }
|
|
|
+}
|