feat: InternalDNS switch

This commit is contained in:
xkeyC 2024-11-03 16:42:39 +08:00
parent 0c03050f5c
commit 3c59a2ca57
9 changed files with 270 additions and 42 deletions

View File

@ -1,5 +1,6 @@
import 'dart:convert'; import 'dart:convert';
import 'package:hive/hive.dart';
import 'package:starcitizen_doctor/common/conf/url_conf.dart'; import 'package:starcitizen_doctor/common/conf/url_conf.dart';
import 'package:starcitizen_doctor/common/io/rs_http.dart'; import 'package:starcitizen_doctor/common/io/rs_http.dart';
import 'package:starcitizen_doctor/data/app_placard_data.dart'; import 'package:starcitizen_doctor/data/app_placard_data.dart';
@ -82,7 +83,13 @@ class Api {
} }
static Future<String> getRepoData(String dir, String name) async { static Future<String> getRepoData(String dir, String name) async {
final r = await RSHttp.getText("${URLConf.apiRepoPath}/$dir/$name"); final r = await RSHttp.getText("${URLConf.apiRepoPath}/$dir/$name",withCustomDns: await isUseInternalDNS());
return r; return r;
} }
static Future<bool> isUseInternalDNS() async {
final userBox = await Hive.openBox("app_conf");
final isUseInternalDNS = userBox.get("isUseInternalDNS", defaultValue: false);
return isUseInternalDNS;
}
} }

View File

@ -5,4 +5,5 @@ class ConstConf {
static const gameChannels = ["LIVE", "PTU", "EPTU", "TECH-PREVIEW", "HOTFIX"]; static const gameChannels = ["LIVE", "PTU", "EPTU", "TECH-PREVIEW", "HOTFIX"];
static const isMSE = static const isMSE =
String.fromEnvironment("MSE", defaultValue: "false") == "true"; String.fromEnvironment("MSE", defaultValue: "false") == "true";
static const dohAddress = "https://223.6.6.6/resolve";
} }

View File

@ -1,3 +1,5 @@
import 'package:starcitizen_doctor/api/api.dart';
import 'package:starcitizen_doctor/common/io/doh_client.dart';
import 'package:starcitizen_doctor/common/io/rs_http.dart'; import 'package:starcitizen_doctor/common/io/rs_http.dart';
import 'package:starcitizen_doctor/common/rust/http_package.dart'; import 'package:starcitizen_doctor/common/rust/http_package.dart';
import 'package:starcitizen_doctor/common/utils/log.dart'; import 'package:starcitizen_doctor/common/utils/log.dart';
@ -38,16 +40,14 @@ class URLConf {
static Future<bool> checkHost() async { static Future<bool> checkHost() async {
// 使 DNS // 使 DNS
final gitApiList = final gitApiList = _genFinalList(await dnsLookupTxt("git.dns.scbox.org"));
_genFinalList(await RSHttp.dnsLookupTxt("git.dns.scbox.org"));
dPrint("DNS gitApiList ==== $gitApiList"); dPrint("DNS gitApiList ==== $gitApiList");
final fasterGit = await getFasterUrl(gitApiList); final fasterGit = await getFasterUrl(gitApiList);
dPrint("gitApiList.Faster ==== $fasterGit"); dPrint("gitApiList.Faster ==== $fasterGit");
if (fasterGit != null) { if (fasterGit != null) {
gitApiHome = fasterGit; gitApiHome = fasterGit;
} }
final rssApiList = final rssApiList = _genFinalList(await dnsLookupTxt("rss.dns.scbox.org"));
_genFinalList(await RSHttp.dnsLookupTxt("rss.dns.scbox.org"));
final fasterRss = await getFasterUrl(rssApiList); final fasterRss = await getFasterUrl(rssApiList);
dPrint("DNS rssApiList ==== $rssApiList"); dPrint("DNS rssApiList ==== $rssApiList");
dPrint("rssApiList.Faster ==== $fasterRss"); dPrint("rssApiList.Faster ==== $fasterRss");
@ -58,6 +58,15 @@ class URLConf {
return isUrlCheckPass; return isUrlCheckPass;
} }
static Future<List<String>> dnsLookupTxt(String host) async {
if (await Api.isUseInternalDNS()) {
dPrint("[URLConf] use internal DNS LookupTxt $host");
return RSHttp.dnsLookupTxt(host);
}
dPrint("[URLConf] use DOH LookupTxt $host");
return (await DohClient.resolveTXT(host)) ?? [];
}
static Future<String?> getFasterUrl(List<String> urls) async { static Future<String?> getFasterUrl(List<String> urls) async {
String firstUrl = ""; String firstUrl = "";
int callLen = 0; int callLen = 0;

View File

@ -0,0 +1,46 @@
import 'dart:convert';
import 'package:starcitizen_doctor/common/conf/const_conf.dart';
import 'package:starcitizen_doctor/common/io/rs_http.dart';
import 'package:starcitizen_doctor/common/utils/log.dart';
import 'package:starcitizen_doctor/data/doh_client_response_data.dart';
class DohClient {
static Future<DohClientResponseData?> resolve(
String domain, String type) async {
try {
final r = await RSHttp.getText(
"${ConstConf.dohAddress}?name=$domain&type=$type");
final data = DohClientResponseData.fromJson(json.decode(r));
return data;
} catch (e) {
dPrint("DohClient.resolve error: $e");
return null;
}
}
static Future<List<String>?> resolveIP(String domain, String type) async {
final data = await resolve(domain, type);
if (data == null) return [];
return data.answer?.map((e) => _removeDataPadding(e.data)).toList();
}
static Future<List<String>?> resolveTXT(String domain) async {
final data = await resolve(domain, "TXT");
if (data == null) return [];
return data.answer?.map((e) => _removeDataPadding(e.data)).toList();
}
static String _removeDataPadding(String? data) {
// data demo: {"data":"\"https://git.scbox.xkeyc.cn,https://gitapi.scbox.org\""}
if (data == null) return "";
data = data.trim();
if (data.startsWith("\"")){
data = data.substring(1);
}
if (data.endsWith("\"")){
data = data.substring(0, data.length - 1);
}
return data;
}
}

View File

@ -0,0 +1,109 @@
class DohClientResponseData {
DohClientResponseData({
this.status,
this.tc,
this.rd,
this.ra,
this.ad,
this.cd,
this.question,
this.answer,
});
DohClientResponseData.fromJson(dynamic json) {
status = json['Status'];
tc = json['TC'];
rd = json['RD'];
ra = json['RA'];
ad = json['AD'];
cd = json['CD'];
question = json['Question'] != null
? DohClientResponseQuestionData.fromJson(json['Question'])
: null;
if (json['Answer'] != null) {
answer = [];
json['Answer'].forEach((v) {
answer?.add(DohClientResponseAnswerData.fromJson(v));
});
}
}
num? status;
bool? tc;
bool? rd;
bool? ra;
bool? ad;
bool? cd;
DohClientResponseQuestionData? question;
List<DohClientResponseAnswerData>? answer;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['Status'] = status;
map['TC'] = tc;
map['RD'] = rd;
map['RA'] = ra;
map['AD'] = ad;
map['CD'] = cd;
if (question != null) {
map['Question'] = question?.toJson();
}
if (answer != null) {
map['Answer'] = answer?.map((v) => v.toJson()).toList();
}
return map;
}
}
class DohClientResponseAnswerData {
DohClientResponseAnswerData({
this.name,
this.ttl,
this.type,
this.data,
});
DohClientResponseAnswerData.fromJson(dynamic json) {
name = json['name'];
ttl = json['TTL'];
type = json['type'];
data = json['data'];
}
String? name;
num? ttl;
num? type;
String? data;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['name'] = name;
map['TTL'] = ttl;
map['type'] = type;
map['data'] = data;
return map;
}
}
class DohClientResponseQuestionData {
DohClientResponseQuestionData({
this.name,
this.type,
});
DohClientResponseQuestionData.fromJson(dynamic json) {
name = json['name'];
type = json['type'];
}
String? name;
num? type;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['name'] = name;
map['type'] = type;
return map;
}
}

View File

@ -15,6 +15,12 @@ class SettingsUI extends HookConsumerWidget {
final appGlobalState = ref.watch(appGlobalModelProvider); final appGlobalState = ref.watch(appGlobalModelProvider);
final appGlobalModel = ref.read(appGlobalModelProvider.notifier); final appGlobalModel = ref.read(appGlobalModelProvider.notifier);
return ListView(padding: const EdgeInsets.all(16), children: [ return ListView(padding: const EdgeInsets.all(16), children: [
makeTitle("应用"),
makeSettingsItem(const Icon(FluentIcons.link, size: 20),
S.current.setting_action_create_settings_shortcut,
subTitle: S.current.setting_action_create_desktop_shortcut,
onTap: () => model.addShortCut(context)),
const SizedBox(height: 12),
makeSettingsItem( makeSettingsItem(
const Icon(FontAwesomeIcons.language, size: 20), const Icon(FontAwesomeIcons.language, size: 20),
S.current.settings_app_language, S.current.settings_app_language,
@ -26,11 +32,32 @@ class SettingsUI extends HookConsumerWidget {
showGoIcon: false, showGoIcon: false,
), ),
const SizedBox(height: 12), const SizedBox(height: 12),
makeSettingsItem(const Icon(FluentIcons.link, size: 20), makeSettingsItem(
S.current.setting_action_create_settings_shortcut, const Icon(FontAwesomeIcons.networkWired, size: 20), "使用内置 DNS",
subTitle: S.current.setting_action_create_desktop_shortcut, subTitle: "开启后可能解决部分地区 DNS 污染的问题",
onTap: () => model.addShortCut(context)), switchStatus: sate.isUseInternalDNS,
onSwitch: model.onChangeUseInternalDNS,
onTap: () => model.onChangeUseInternalDNS(!sate.isUseInternalDNS)),
const SizedBox(height: 12), const SizedBox(height: 12),
makeSettingsItem(const Icon(FluentIcons.delete, size: 20),
S.current.setting_action_clear_translation_file_cache,
subTitle: S.current.setting_action_info_cache_clearing_info(
(sate.locationCacheSize / 1024 / 1024).toStringAsFixed(2)),
onTap: () => model.cleanLocationCache(context)),
const SizedBox(height: 12),
makeSettingsItem(const Icon(FluentIcons.speed_high, size: 20),
S.current.setting_action_tool_site_access_acceleration,
onTap: () =>
model.onChangeToolSiteMirror(!sate.isEnableToolSiteMirrors),
subTitle: S.current.setting_action_info_mirror_server_info,
onSwitch: model.onChangeToolSiteMirror,
switchStatus: sate.isEnableToolSiteMirrors),
const SizedBox(height: 12),
makeSettingsItem(const Icon(FluentIcons.document_set, size: 20),
S.current.setting_action_view_log,
onTap: () => model.showLogs(),
subTitle: S.current.setting_action_info_view_log_file),
makeTitle("功能"),
makeSettingsItem(const Icon(FontAwesomeIcons.microchip, size: 20), makeSettingsItem(const Icon(FontAwesomeIcons.microchip, size: 20),
S.current.setting_action_ignore_efficiency_cores_on_launch, S.current.setting_action_ignore_efficiency_cores_on_launch,
subTitle: S.current subTitle: S.current
@ -57,27 +84,19 @@ class SettingsUI extends HookConsumerWidget {
model.delName("custom_game_path"); model.delName("custom_game_path");
}), }),
const SizedBox(height: 12), const SizedBox(height: 12),
makeSettingsItem(const Icon(FluentIcons.delete, size: 20),
S.current.setting_action_clear_translation_file_cache,
subTitle: S.current.setting_action_info_cache_clearing_info(
(sate.locationCacheSize / 1024 / 1024).toStringAsFixed(2)),
onTap: () => model.cleanLocationCache(context)),
const SizedBox(height: 12),
makeSettingsItem(const Icon(FluentIcons.speed_high, size: 20),
S.current.setting_action_tool_site_access_acceleration,
onTap: () =>
model.onChangeToolSiteMirror(!sate.isEnableToolSiteMirrors),
subTitle: S.current.setting_action_info_mirror_server_info,
onSwitch: model.onChangeToolSiteMirror,
switchStatus: sate.isEnableToolSiteMirrors),
const SizedBox(height: 12),
makeSettingsItem(const Icon(FluentIcons.document_set, size: 20),
S.current.setting_action_view_log,
onTap: () => model.showLogs(),
subTitle: S.current.setting_action_info_view_log_file),
]); ]);
} }
Widget makeTitle(String title) {
return Padding(
padding: const EdgeInsets.only(top: 12, bottom: 12),
child: Text(
title,
style: TextStyle(fontSize: 24),
),
);
}
Widget makeSettingsItem( Widget makeSettingsItem(
Widget icon, Widget icon,
String title, { String title, {

View File

@ -25,6 +25,7 @@ class SettingsUIState with _$SettingsUIState {
String? customLauncherPath, String? customLauncherPath,
String? customGamePath, String? customGamePath,
@Default(0) int locationCacheSize, @Default(0) int locationCacheSize,
@Default(false) bool isUseInternalDNS,
}) = _SettingsUIState; }) = _SettingsUIState;
} }
@ -38,10 +39,11 @@ class SettingsUIModel extends _$SettingsUIModel {
} }
void _initState() async { void _initState() async {
_updateGameLaunchECore(); await _updateGameLaunchECore();
_loadCustomPath(); await _loadCustomPath();
_loadLocationCacheSize(); await _loadLocationCacheSize();
_loadToolSiteMirrorState(); await _loadToolSiteMirrorState();
await _loadUseInternalDNS();
} }
Future<void> setGameLaunchECore(BuildContext context) async { Future<void> setGameLaunchECore(BuildContext context) async {
@ -113,7 +115,7 @@ class SettingsUIModel extends _$SettingsUIModel {
await confBox.put(pathKey, dir); await confBox.put(pathKey, dir);
} }
_loadCustomPath() async { Future _loadCustomPath() async {
final confBox = await Hive.openBox("app_conf"); final confBox = await Hive.openBox("app_conf");
final customLauncherPath = confBox.get("custom_launcher_path"); final customLauncherPath = confBox.get("custom_launcher_path");
final customGamePath = confBox.get("custom_game_path"); final customGamePath = confBox.get("custom_game_path");
@ -127,7 +129,7 @@ class SettingsUIModel extends _$SettingsUIModel {
_initState(); _initState();
} }
_loadLocationCacheSize() async { Future _loadLocationCacheSize() async {
final len1 = await SystemHelper.getDirLen( final len1 = await SystemHelper.getDirLen(
"${appGlobalState.applicationSupportDir}/Localizations"); "${appGlobalState.applicationSupportDir}/Localizations");
final len2 = await SystemHelper.getDirLen( final len2 = await SystemHelper.getDirLen(
@ -182,7 +184,7 @@ class SettingsUIModel extends _$SettingsUIModel {
showToast(context, S.current.setting_action_info_shortcut_created); showToast(context, S.current.setting_action_info_shortcut_created);
} }
_loadToolSiteMirrorState() async { Future _loadToolSiteMirrorState() async {
final userBox = await Hive.openBox("app_conf"); final userBox = await Hive.openBox("app_conf");
final isEnableToolSiteMirrors = final isEnableToolSiteMirrors =
userBox.get("isEnableToolSiteMirrors", defaultValue: false); userBox.get("isEnableToolSiteMirrors", defaultValue: false);
@ -200,4 +202,16 @@ class SettingsUIModel extends _$SettingsUIModel {
SystemHelper.openDir(getDPrintFile()?.absolute.path.replaceAll("/", "\\"), SystemHelper.openDir(getDPrintFile()?.absolute.path.replaceAll("/", "\\"),
isFile: true); isFile: true);
} }
void onChangeUseInternalDNS(bool? b) {
final userBox = Hive.box("app_conf");
userBox.put("isUseInternalDNS", b ?? false);
_initState();
}
Future _loadUseInternalDNS() async {
final userBox = await Hive.openBox("app_conf");
final isUseInternalDNS = userBox.get("isUseInternalDNS", defaultValue: false);
state = state.copyWith(isUseInternalDNS: isUseInternalDNS);
}
} }

View File

@ -21,6 +21,7 @@ mixin _$SettingsUIState {
String? get customLauncherPath => throw _privateConstructorUsedError; String? get customLauncherPath => throw _privateConstructorUsedError;
String? get customGamePath => throw _privateConstructorUsedError; String? get customGamePath => throw _privateConstructorUsedError;
int get locationCacheSize => throw _privateConstructorUsedError; int get locationCacheSize => throw _privateConstructorUsedError;
bool get isUseInternalDNS => throw _privateConstructorUsedError;
/// Create a copy of SettingsUIState /// Create a copy of SettingsUIState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -40,7 +41,8 @@ abstract class $SettingsUIStateCopyWith<$Res> {
String inputGameLaunchECore, String inputGameLaunchECore,
String? customLauncherPath, String? customLauncherPath,
String? customGamePath, String? customGamePath,
int locationCacheSize}); int locationCacheSize,
bool isUseInternalDNS});
} }
/// @nodoc /// @nodoc
@ -63,6 +65,7 @@ class _$SettingsUIStateCopyWithImpl<$Res, $Val extends SettingsUIState>
Object? customLauncherPath = freezed, Object? customLauncherPath = freezed,
Object? customGamePath = freezed, Object? customGamePath = freezed,
Object? locationCacheSize = null, Object? locationCacheSize = null,
Object? isUseInternalDNS = null,
}) { }) {
return _then(_value.copyWith( return _then(_value.copyWith(
isEnableToolSiteMirrors: null == isEnableToolSiteMirrors isEnableToolSiteMirrors: null == isEnableToolSiteMirrors
@ -85,6 +88,10 @@ class _$SettingsUIStateCopyWithImpl<$Res, $Val extends SettingsUIState>
? _value.locationCacheSize ? _value.locationCacheSize
: locationCacheSize // ignore: cast_nullable_to_non_nullable : locationCacheSize // ignore: cast_nullable_to_non_nullable
as int, as int,
isUseInternalDNS: null == isUseInternalDNS
? _value.isUseInternalDNS
: isUseInternalDNS // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val); ) as $Val);
} }
} }
@ -102,7 +109,8 @@ abstract class _$$SettingsUIStateImplCopyWith<$Res>
String inputGameLaunchECore, String inputGameLaunchECore,
String? customLauncherPath, String? customLauncherPath,
String? customGamePath, String? customGamePath,
int locationCacheSize}); int locationCacheSize,
bool isUseInternalDNS});
} }
/// @nodoc /// @nodoc
@ -123,6 +131,7 @@ class __$$SettingsUIStateImplCopyWithImpl<$Res>
Object? customLauncherPath = freezed, Object? customLauncherPath = freezed,
Object? customGamePath = freezed, Object? customGamePath = freezed,
Object? locationCacheSize = null, Object? locationCacheSize = null,
Object? isUseInternalDNS = null,
}) { }) {
return _then(_$SettingsUIStateImpl( return _then(_$SettingsUIStateImpl(
isEnableToolSiteMirrors: null == isEnableToolSiteMirrors isEnableToolSiteMirrors: null == isEnableToolSiteMirrors
@ -145,6 +154,10 @@ class __$$SettingsUIStateImplCopyWithImpl<$Res>
? _value.locationCacheSize ? _value.locationCacheSize
: locationCacheSize // ignore: cast_nullable_to_non_nullable : locationCacheSize // ignore: cast_nullable_to_non_nullable
as int, as int,
isUseInternalDNS: null == isUseInternalDNS
? _value.isUseInternalDNS
: isUseInternalDNS // ignore: cast_nullable_to_non_nullable
as bool,
)); ));
} }
} }
@ -157,7 +170,8 @@ class _$SettingsUIStateImpl implements _SettingsUIState {
this.inputGameLaunchECore = "0", this.inputGameLaunchECore = "0",
this.customLauncherPath, this.customLauncherPath,
this.customGamePath, this.customGamePath,
this.locationCacheSize = 0}); this.locationCacheSize = 0,
this.isUseInternalDNS = false});
@override @override
@JsonKey() @JsonKey()
@ -172,10 +186,13 @@ class _$SettingsUIStateImpl implements _SettingsUIState {
@override @override
@JsonKey() @JsonKey()
final int locationCacheSize; final int locationCacheSize;
@override
@JsonKey()
final bool isUseInternalDNS;
@override @override
String toString() { String toString() {
return 'SettingsUIState(isEnableToolSiteMirrors: $isEnableToolSiteMirrors, inputGameLaunchECore: $inputGameLaunchECore, customLauncherPath: $customLauncherPath, customGamePath: $customGamePath, locationCacheSize: $locationCacheSize)'; return 'SettingsUIState(isEnableToolSiteMirrors: $isEnableToolSiteMirrors, inputGameLaunchECore: $inputGameLaunchECore, customLauncherPath: $customLauncherPath, customGamePath: $customGamePath, locationCacheSize: $locationCacheSize, isUseInternalDNS: $isUseInternalDNS)';
} }
@override @override
@ -193,7 +210,9 @@ class _$SettingsUIStateImpl implements _SettingsUIState {
(identical(other.customGamePath, customGamePath) || (identical(other.customGamePath, customGamePath) ||
other.customGamePath == customGamePath) && other.customGamePath == customGamePath) &&
(identical(other.locationCacheSize, locationCacheSize) || (identical(other.locationCacheSize, locationCacheSize) ||
other.locationCacheSize == locationCacheSize)); other.locationCacheSize == locationCacheSize) &&
(identical(other.isUseInternalDNS, isUseInternalDNS) ||
other.isUseInternalDNS == isUseInternalDNS));
} }
@override @override
@ -203,7 +222,8 @@ class _$SettingsUIStateImpl implements _SettingsUIState {
inputGameLaunchECore, inputGameLaunchECore,
customLauncherPath, customLauncherPath,
customGamePath, customGamePath,
locationCacheSize); locationCacheSize,
isUseInternalDNS);
/// Create a copy of SettingsUIState /// Create a copy of SettingsUIState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.
@ -221,7 +241,8 @@ abstract class _SettingsUIState implements SettingsUIState {
final String inputGameLaunchECore, final String inputGameLaunchECore,
final String? customLauncherPath, final String? customLauncherPath,
final String? customGamePath, final String? customGamePath,
final int locationCacheSize}) = _$SettingsUIStateImpl; final int locationCacheSize,
final bool isUseInternalDNS}) = _$SettingsUIStateImpl;
@override @override
bool get isEnableToolSiteMirrors; bool get isEnableToolSiteMirrors;
@ -233,6 +254,8 @@ abstract class _SettingsUIState implements SettingsUIState {
String? get customGamePath; String? get customGamePath;
@override @override
int get locationCacheSize; int get locationCacheSize;
@override
bool get isUseInternalDNS;
/// Create a copy of SettingsUIState /// Create a copy of SettingsUIState
/// with the given fields replaced by the non-null parameter values. /// with the given fields replaced by the non-null parameter values.

View File

@ -6,7 +6,7 @@ part of 'settings_ui_model.dart';
// RiverpodGenerator // RiverpodGenerator
// ************************************************************************** // **************************************************************************
String _$settingsUIModelHash() => r'c625c9c743ba160bedd62a5c9f3c435422c94a42'; String _$settingsUIModelHash() => r'de58885742e29aae6b1226c16c03655a6a6b018d';
/// See also [SettingsUIModel]. /// See also [SettingsUIModel].
@ProviderFor(SettingsUIModel) @ProviderFor(SettingsUIModel)