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 'package:hive/hive.dart';
import 'package:starcitizen_doctor/common/conf/url_conf.dart';
import 'package:starcitizen_doctor/common/io/rs_http.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 {
final r = await RSHttp.getText("${URLConf.apiRepoPath}/$dir/$name");
final r = await RSHttp.getText("${URLConf.apiRepoPath}/$dir/$name",withCustomDns: await isUseInternalDNS());
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 isMSE =
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/rust/http_package.dart';
import 'package:starcitizen_doctor/common/utils/log.dart';
@ -38,16 +40,14 @@ class URLConf {
static Future<bool> checkHost() async {
// 使 DNS
final gitApiList =
_genFinalList(await RSHttp.dnsLookupTxt("git.dns.scbox.org"));
final gitApiList = _genFinalList(await dnsLookupTxt("git.dns.scbox.org"));
dPrint("DNS gitApiList ==== $gitApiList");
final fasterGit = await getFasterUrl(gitApiList);
dPrint("gitApiList.Faster ==== $fasterGit");
if (fasterGit != null) {
gitApiHome = fasterGit;
}
final rssApiList =
_genFinalList(await RSHttp.dnsLookupTxt("rss.dns.scbox.org"));
final rssApiList = _genFinalList(await dnsLookupTxt("rss.dns.scbox.org"));
final fasterRss = await getFasterUrl(rssApiList);
dPrint("DNS rssApiList ==== $rssApiList");
dPrint("rssApiList.Faster ==== $fasterRss");
@ -58,6 +58,15 @@ class URLConf {
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 {
String firstUrl = "";
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 appGlobalModel = ref.read(appGlobalModelProvider.notifier);
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(
const Icon(FontAwesomeIcons.language, size: 20),
S.current.settings_app_language,
@ -26,11 +32,32 @@ class SettingsUI extends HookConsumerWidget {
showGoIcon: false,
),
const SizedBox(height: 12),
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)),
makeSettingsItem(
const Icon(FontAwesomeIcons.networkWired, size: 20), "使用内置 DNS",
subTitle: "开启后可能解决部分地区 DNS 污染的问题",
switchStatus: sate.isUseInternalDNS,
onSwitch: model.onChangeUseInternalDNS,
onTap: () => model.onChangeUseInternalDNS(!sate.isUseInternalDNS)),
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),
S.current.setting_action_ignore_efficiency_cores_on_launch,
subTitle: S.current
@ -57,27 +84,19 @@ class SettingsUI extends HookConsumerWidget {
model.delName("custom_game_path");
}),
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 icon,
String title, {

View File

@ -25,6 +25,7 @@ class SettingsUIState with _$SettingsUIState {
String? customLauncherPath,
String? customGamePath,
@Default(0) int locationCacheSize,
@Default(false) bool isUseInternalDNS,
}) = _SettingsUIState;
}
@ -38,10 +39,11 @@ class SettingsUIModel extends _$SettingsUIModel {
}
void _initState() async {
_updateGameLaunchECore();
_loadCustomPath();
_loadLocationCacheSize();
_loadToolSiteMirrorState();
await _updateGameLaunchECore();
await _loadCustomPath();
await _loadLocationCacheSize();
await _loadToolSiteMirrorState();
await _loadUseInternalDNS();
}
Future<void> setGameLaunchECore(BuildContext context) async {
@ -113,7 +115,7 @@ class SettingsUIModel extends _$SettingsUIModel {
await confBox.put(pathKey, dir);
}
_loadCustomPath() async {
Future _loadCustomPath() async {
final confBox = await Hive.openBox("app_conf");
final customLauncherPath = confBox.get("custom_launcher_path");
final customGamePath = confBox.get("custom_game_path");
@ -127,7 +129,7 @@ class SettingsUIModel extends _$SettingsUIModel {
_initState();
}
_loadLocationCacheSize() async {
Future _loadLocationCacheSize() async {
final len1 = await SystemHelper.getDirLen(
"${appGlobalState.applicationSupportDir}/Localizations");
final len2 = await SystemHelper.getDirLen(
@ -182,7 +184,7 @@ class SettingsUIModel extends _$SettingsUIModel {
showToast(context, S.current.setting_action_info_shortcut_created);
}
_loadToolSiteMirrorState() async {
Future _loadToolSiteMirrorState() async {
final userBox = await Hive.openBox("app_conf");
final isEnableToolSiteMirrors =
userBox.get("isEnableToolSiteMirrors", defaultValue: false);
@ -200,4 +202,16 @@ class SettingsUIModel extends _$SettingsUIModel {
SystemHelper.openDir(getDPrintFile()?.absolute.path.replaceAll("/", "\\"),
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 customGamePath => throw _privateConstructorUsedError;
int get locationCacheSize => throw _privateConstructorUsedError;
bool get isUseInternalDNS => throw _privateConstructorUsedError;
/// Create a copy of SettingsUIState
/// with the given fields replaced by the non-null parameter values.
@ -40,7 +41,8 @@ abstract class $SettingsUIStateCopyWith<$Res> {
String inputGameLaunchECore,
String? customLauncherPath,
String? customGamePath,
int locationCacheSize});
int locationCacheSize,
bool isUseInternalDNS});
}
/// @nodoc
@ -63,6 +65,7 @@ class _$SettingsUIStateCopyWithImpl<$Res, $Val extends SettingsUIState>
Object? customLauncherPath = freezed,
Object? customGamePath = freezed,
Object? locationCacheSize = null,
Object? isUseInternalDNS = null,
}) {
return _then(_value.copyWith(
isEnableToolSiteMirrors: null == isEnableToolSiteMirrors
@ -85,6 +88,10 @@ class _$SettingsUIStateCopyWithImpl<$Res, $Val extends SettingsUIState>
? _value.locationCacheSize
: locationCacheSize // ignore: cast_nullable_to_non_nullable
as int,
isUseInternalDNS: null == isUseInternalDNS
? _value.isUseInternalDNS
: isUseInternalDNS // ignore: cast_nullable_to_non_nullable
as bool,
) as $Val);
}
}
@ -102,7 +109,8 @@ abstract class _$$SettingsUIStateImplCopyWith<$Res>
String inputGameLaunchECore,
String? customLauncherPath,
String? customGamePath,
int locationCacheSize});
int locationCacheSize,
bool isUseInternalDNS});
}
/// @nodoc
@ -123,6 +131,7 @@ class __$$SettingsUIStateImplCopyWithImpl<$Res>
Object? customLauncherPath = freezed,
Object? customGamePath = freezed,
Object? locationCacheSize = null,
Object? isUseInternalDNS = null,
}) {
return _then(_$SettingsUIStateImpl(
isEnableToolSiteMirrors: null == isEnableToolSiteMirrors
@ -145,6 +154,10 @@ class __$$SettingsUIStateImplCopyWithImpl<$Res>
? _value.locationCacheSize
: locationCacheSize // ignore: cast_nullable_to_non_nullable
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.customLauncherPath,
this.customGamePath,
this.locationCacheSize = 0});
this.locationCacheSize = 0,
this.isUseInternalDNS = false});
@override
@JsonKey()
@ -172,10 +186,13 @@ class _$SettingsUIStateImpl implements _SettingsUIState {
@override
@JsonKey()
final int locationCacheSize;
@override
@JsonKey()
final bool isUseInternalDNS;
@override
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
@ -193,7 +210,9 @@ class _$SettingsUIStateImpl implements _SettingsUIState {
(identical(other.customGamePath, customGamePath) ||
other.customGamePath == customGamePath) &&
(identical(other.locationCacheSize, locationCacheSize) ||
other.locationCacheSize == locationCacheSize));
other.locationCacheSize == locationCacheSize) &&
(identical(other.isUseInternalDNS, isUseInternalDNS) ||
other.isUseInternalDNS == isUseInternalDNS));
}
@override
@ -203,7 +222,8 @@ class _$SettingsUIStateImpl implements _SettingsUIState {
inputGameLaunchECore,
customLauncherPath,
customGamePath,
locationCacheSize);
locationCacheSize,
isUseInternalDNS);
/// Create a copy of SettingsUIState
/// 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? customLauncherPath,
final String? customGamePath,
final int locationCacheSize}) = _$SettingsUIStateImpl;
final int locationCacheSize,
final bool isUseInternalDNS}) = _$SettingsUIStateImpl;
@override
bool get isEnableToolSiteMirrors;
@ -233,6 +254,8 @@ abstract class _SettingsUIState implements SettingsUIState {
String? get customGamePath;
@override
int get locationCacheSize;
@override
bool get isUseInternalDNS;
/// Create a copy of SettingsUIState
/// with the given fields replaced by the non-null parameter values.

View File

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