Change mapping and bug fixes
This commit is contained in:
@@ -15,11 +15,9 @@ DioHttpClient httpClient(HttpClientRef ref) {
|
||||
);
|
||||
|
||||
ref.listen(
|
||||
configOptionNotifierProvider,
|
||||
(_, next) {
|
||||
if (next case AsyncData(value: final options)) {
|
||||
client.setProxyPort(options.mixedPort);
|
||||
}
|
||||
configOptionNotifierProvider.selectAsync((data) => data.mixedPort),
|
||||
(_, next) async {
|
||||
client.setProxyPort(await next);
|
||||
},
|
||||
fireImmediately: true,
|
||||
);
|
||||
|
||||
54
lib/core/model/optional_range.dart
Normal file
54
lib/core/model/optional_range.dart
Normal file
@@ -0,0 +1,54 @@
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:hiddify/core/localization/translations.dart';
|
||||
|
||||
part 'optional_range.mapper.dart';
|
||||
|
||||
@MappableClass()
|
||||
class OptionalRange with OptionalRangeMappable {
|
||||
const OptionalRange({this.min, this.max});
|
||||
|
||||
final int? min;
|
||||
final int? max;
|
||||
|
||||
String format() => [min, max].whereNotNull().join("-");
|
||||
String present(TranslationsEn t) =>
|
||||
format().isEmpty ? t.general.notSet : format();
|
||||
|
||||
factory OptionalRange._fromString(
|
||||
String input, {
|
||||
bool allowEmpty = true,
|
||||
}) =>
|
||||
switch (input.split("-")) {
|
||||
[final String val] when val.isEmpty && allowEmpty =>
|
||||
const OptionalRange(),
|
||||
[final String min] => OptionalRange(min: int.parse(min)),
|
||||
[final String min, final String max] => OptionalRange(
|
||||
min: int.parse(min),
|
||||
max: int.parse(max),
|
||||
),
|
||||
_ => throw Exception("Invalid range: $input"),
|
||||
};
|
||||
|
||||
static OptionalRange? tryParse(
|
||||
String input, {
|
||||
bool allowEmpty = false,
|
||||
}) {
|
||||
try {
|
||||
return OptionalRange._fromString(input);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class OptionalRangeJsonMapper extends SimpleMapper<OptionalRange> {
|
||||
const OptionalRangeJsonMapper();
|
||||
|
||||
@override
|
||||
OptionalRange decode(dynamic value) =>
|
||||
OptionalRange._fromString(value as String);
|
||||
|
||||
@override
|
||||
dynamic encode(OptionalRange self) => self.format();
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:hiddify/core/localization/translations.dart';
|
||||
|
||||
part 'range.freezed.dart';
|
||||
|
||||
@freezed
|
||||
class RangeWithOptionalCeil with _$RangeWithOptionalCeil {
|
||||
const RangeWithOptionalCeil._();
|
||||
|
||||
const factory RangeWithOptionalCeil({
|
||||
int? min,
|
||||
int? max,
|
||||
}) = _RangeWithOptionalCeil;
|
||||
|
||||
String format() => [min, max].whereNotNull().join("-");
|
||||
String present(TranslationsEn t) =>
|
||||
format().isEmpty ? t.general.notSet : format();
|
||||
|
||||
factory RangeWithOptionalCeil._fromString(
|
||||
String input, {
|
||||
bool allowEmpty = true,
|
||||
}) =>
|
||||
switch (input.split("-")) {
|
||||
[final String val] when val.isEmpty && allowEmpty =>
|
||||
const RangeWithOptionalCeil(),
|
||||
[final String min] => RangeWithOptionalCeil(min: int.parse(min)),
|
||||
[final String min, final String max] => RangeWithOptionalCeil(
|
||||
min: int.parse(min),
|
||||
max: int.parse(max),
|
||||
),
|
||||
_ => throw Exception("Invalid range: $input"),
|
||||
};
|
||||
|
||||
static RangeWithOptionalCeil? tryParse(
|
||||
String input, {
|
||||
bool allowEmpty = false,
|
||||
}) {
|
||||
try {
|
||||
return RangeWithOptionalCeil._fromString(input);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RangeWithOptionalCeilJsonConverter
|
||||
implements JsonConverter<RangeWithOptionalCeil, String> {
|
||||
const RangeWithOptionalCeilJsonConverter();
|
||||
|
||||
@override
|
||||
RangeWithOptionalCeil fromJson(String json) =>
|
||||
RangeWithOptionalCeil._fromString(json);
|
||||
|
||||
@override
|
||||
String toJson(RangeWithOptionalCeil object) => object.format();
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
class IntervalInSecondsConverter implements JsonConverter<Duration, int> {
|
||||
const IntervalInSecondsConverter();
|
||||
class IntervalInSecondsMapper extends SimpleMapper<Duration> {
|
||||
const IntervalInSecondsMapper();
|
||||
|
||||
@override
|
||||
Duration fromJson(int json) => Duration(seconds: json);
|
||||
Duration decode(dynamic value) => Duration(seconds: value as int);
|
||||
|
||||
@override
|
||||
int toJson(Duration object) => object.inSeconds;
|
||||
dynamic encode(Duration self) => self.inSeconds;
|
||||
}
|
||||
|
||||
@@ -3,10 +3,8 @@ import 'package:hiddify/core/model/region.dart';
|
||||
import 'package:hiddify/core/utils/exception_handler.dart';
|
||||
import 'package:hiddify/features/config_option/model/config_option_entity.dart';
|
||||
import 'package:hiddify/features/config_option/model/config_option_failure.dart';
|
||||
import 'package:hiddify/features/config_option/model/config_option_patch.dart';
|
||||
import 'package:hiddify/features/geo_asset/data/geo_asset_path_resolver.dart';
|
||||
import 'package:hiddify/features/geo_asset/data/geo_asset_repository.dart';
|
||||
import 'package:hiddify/singbox/model/singbox_config_enum.dart';
|
||||
import 'package:hiddify/singbox/model/singbox_config_option.dart';
|
||||
import 'package:hiddify/singbox/model/singbox_rule.dart';
|
||||
import 'package:hiddify/utils/utils.dart';
|
||||
@@ -36,7 +34,7 @@ class ConfigOptionRepositoryImpl
|
||||
@override
|
||||
Either<ConfigOptionFailure, ConfigOptionEntity> getConfigOption() {
|
||||
try {
|
||||
final map = ConfigOptionEntity.initial.toJson();
|
||||
final map = ConfigOptionEntity.initial().toMap();
|
||||
for (final key in map.keys) {
|
||||
final persisted = preferences.get(key);
|
||||
if (persisted != null) {
|
||||
@@ -51,7 +49,7 @@ class ConfigOptionRepositoryImpl
|
||||
map[key] = persisted;
|
||||
}
|
||||
}
|
||||
final options = ConfigOptionEntity.fromJson(map);
|
||||
final options = ConfigOptionEntityMapper.fromMap(map);
|
||||
return right(options);
|
||||
} catch (error, stackTrace) {
|
||||
return left(ConfigOptionUnexpectedFailure(error, stackTrace));
|
||||
@@ -64,7 +62,7 @@ class ConfigOptionRepositoryImpl
|
||||
) {
|
||||
return exceptionHandler(
|
||||
() async {
|
||||
final map = patch.toJson();
|
||||
final map = patch.toMap();
|
||||
await updateByJson(map);
|
||||
return right(unit);
|
||||
},
|
||||
@@ -76,7 +74,7 @@ class ConfigOptionRepositoryImpl
|
||||
TaskEither<ConfigOptionFailure, Unit> resetConfigOption() {
|
||||
return exceptionHandler(
|
||||
() async {
|
||||
final map = ConfigOptionEntity.initial.toJson();
|
||||
final map = ConfigOptionEntity.initial().toMap();
|
||||
await updateByJson(map);
|
||||
return right(unit);
|
||||
},
|
||||
@@ -88,7 +86,7 @@ class ConfigOptionRepositoryImpl
|
||||
Future<void> updateByJson(
|
||||
Map<String, dynamic> options,
|
||||
) async {
|
||||
final map = ConfigOptionEntity.initial.toJson();
|
||||
final map = ConfigOptionEntity.initial().toMap();
|
||||
for (final key in map.keys) {
|
||||
final value = options[key];
|
||||
if (value != null) {
|
||||
@@ -172,48 +170,7 @@ class SingBoxConfigOptionRepositoryImpl
|
||||
|
||||
final persisted =
|
||||
optionsRepository.getConfigOption().getOrElse((l) => throw l);
|
||||
final singboxConfigOption = SingboxConfigOption(
|
||||
executeConfigAsIs: false,
|
||||
logLevel: persisted.logLevel,
|
||||
resolveDestination: persisted.resolveDestination,
|
||||
ipv6Mode: persisted.ipv6Mode,
|
||||
remoteDnsAddress: persisted.remoteDnsAddress,
|
||||
remoteDnsDomainStrategy: persisted.remoteDnsDomainStrategy,
|
||||
directDnsAddress: persisted.directDnsAddress,
|
||||
directDnsDomainStrategy: persisted.directDnsDomainStrategy,
|
||||
mixedPort: persisted.mixedPort,
|
||||
localDnsPort: persisted.localDnsPort,
|
||||
tunImplementation: persisted.tunImplementation,
|
||||
mtu: persisted.mtu,
|
||||
strictRoute: persisted.strictRoute,
|
||||
connectionTestUrl: persisted.connectionTestUrl,
|
||||
urlTestInterval: persisted.urlTestInterval,
|
||||
enableClashApi: persisted.enableClashApi,
|
||||
clashApiPort: persisted.clashApiPort,
|
||||
enableTun: persisted.serviceMode == ServiceMode.tun,
|
||||
enableTunService: persisted.serviceMode == ServiceMode.tunService,
|
||||
setSystemProxy: persisted.serviceMode == ServiceMode.systemProxy,
|
||||
bypassLan: persisted.bypassLan,
|
||||
allowConnectionFromLan: persisted.allowConnectionFromLan,
|
||||
enableFakeDns: persisted.enableFakeDns,
|
||||
enableDnsRouting: persisted.enableDnsRouting,
|
||||
independentDnsCache: persisted.independentDnsCache,
|
||||
enableTlsFragment: persisted.enableTlsFragment,
|
||||
tlsFragmentSize: persisted.tlsFragmentSize,
|
||||
tlsFragmentSleep: persisted.tlsFragmentSleep,
|
||||
enableTlsMixedSniCase: persisted.enableTlsMixedSniCase,
|
||||
enableTlsPadding: persisted.enableTlsPadding,
|
||||
tlsPaddingSize: persisted.tlsPaddingSize,
|
||||
enableMux: persisted.enableMux,
|
||||
muxPadding: persisted.muxPadding,
|
||||
muxMaxStreams: persisted.muxMaxStreams,
|
||||
muxProtocol: persisted.muxProtocol,
|
||||
enableWarp: persisted.enableWarp,
|
||||
warpDetourMode: persisted.warpDetourMode,
|
||||
warpLicenseKey: persisted.warpLicenseKey,
|
||||
warpCleanIp: persisted.warpCleanIp,
|
||||
warpPort: persisted.warpPort,
|
||||
warpNoise: persisted.warpNoise,
|
||||
final singboxConfigOption = persisted.toSingbox(
|
||||
geoipPath: geoAssetPathResolver.relativePath(
|
||||
geoAssets.geoip.providerName,
|
||||
geoAssets.geoip.fileName,
|
||||
|
||||
@@ -1,75 +1,107 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:hiddify/core/model/range.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:hiddify/core/model/optional_range.dart';
|
||||
import 'package:hiddify/core/utils/json_converters.dart';
|
||||
import 'package:hiddify/features/config_option/model/config_option_patch.dart';
|
||||
import 'package:hiddify/features/log/model/log_level.dart';
|
||||
import 'package:hiddify/singbox/model/singbox_config_enum.dart';
|
||||
import 'package:hiddify/singbox/model/singbox_config_option.dart';
|
||||
import 'package:hiddify/singbox/model/singbox_rule.dart';
|
||||
import 'package:hiddify/utils/platform_utils.dart';
|
||||
|
||||
part 'config_option_entity.freezed.dart';
|
||||
part 'config_option_entity.g.dart';
|
||||
part 'config_option_entity.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class ConfigOptionEntity with _$ConfigOptionEntity {
|
||||
const ConfigOptionEntity._();
|
||||
@MappableClass(
|
||||
caseStyle: CaseStyle.paramCase,
|
||||
includeCustomMappers: [
|
||||
OptionalRangeJsonMapper(),
|
||||
IntervalInSecondsMapper(),
|
||||
],
|
||||
)
|
||||
class ConfigOptionEntity with ConfigOptionEntityMappable {
|
||||
const ConfigOptionEntity({
|
||||
required this.serviceMode,
|
||||
this.logLevel = LogLevel.warn,
|
||||
this.resolveDestination = false,
|
||||
this.ipv6Mode = IPv6Mode.disable,
|
||||
this.remoteDnsAddress = "http://1.1.1.1",
|
||||
this.remoteDnsDomainStrategy = DomainStrategy.auto,
|
||||
this.directDnsAddress = "1.1.1.1",
|
||||
this.directDnsDomainStrategy = DomainStrategy.auto,
|
||||
this.mixedPort = 2334,
|
||||
this.localDnsPort = 6450,
|
||||
this.tunImplementation = TunImplementation.mixed,
|
||||
this.mtu = 9000,
|
||||
this.strictRoute = true,
|
||||
this.connectionTestUrl = "http://cp.cloudflare.com/",
|
||||
this.urlTestInterval = const Duration(minutes: 10),
|
||||
this.enableClashApi = true,
|
||||
this.clashApiPort = 6756,
|
||||
this.bypassLan = false,
|
||||
this.allowConnectionFromLan = false,
|
||||
this.enableFakeDns = false,
|
||||
this.enableDnsRouting = true,
|
||||
this.independentDnsCache = true,
|
||||
this.enableTlsFragment = false,
|
||||
this.tlsFragmentSize = const OptionalRange(min: 10, max: 100),
|
||||
this.tlsFragmentSleep = const OptionalRange(min: 50, max: 200),
|
||||
this.enableTlsMixedSniCase = false,
|
||||
this.enableTlsPadding = false,
|
||||
this.tlsPaddingSize = const OptionalRange(min: 100, max: 200),
|
||||
this.enableMux = false,
|
||||
this.muxPadding = false,
|
||||
this.muxMaxStreams = 8,
|
||||
this.muxProtocol = MuxProtocol.h2mux,
|
||||
this.enableWarp = false,
|
||||
this.warpDetourMode = WarpDetourMode.outbound,
|
||||
this.warpLicenseKey = "",
|
||||
this.warpCleanIp = "auto",
|
||||
this.warpPort = 0,
|
||||
this.warpNoise = const OptionalRange(),
|
||||
});
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.kebab)
|
||||
const factory ConfigOptionEntity({
|
||||
required ServiceMode serviceMode,
|
||||
@Default(LogLevel.warn) LogLevel logLevel,
|
||||
@Default(false) bool resolveDestination,
|
||||
@Default(IPv6Mode.disable) IPv6Mode ipv6Mode,
|
||||
@Default("udp://1.1.1.1") String remoteDnsAddress,
|
||||
@Default(DomainStrategy.auto) DomainStrategy remoteDnsDomainStrategy,
|
||||
@Default("1.1.1.1") String directDnsAddress,
|
||||
@Default(DomainStrategy.auto) DomainStrategy directDnsDomainStrategy,
|
||||
@Default(2334) int mixedPort,
|
||||
@Default(6450) int localDnsPort,
|
||||
@Default(TunImplementation.mixed) TunImplementation tunImplementation,
|
||||
@Default(9000) int mtu,
|
||||
@Default(true) bool strictRoute,
|
||||
@Default("http://cp.cloudflare.com/") String connectionTestUrl,
|
||||
@IntervalInSecondsConverter()
|
||||
@Default(Duration(minutes: 10))
|
||||
Duration urlTestInterval,
|
||||
@Default(true) bool enableClashApi,
|
||||
@Default(6756) int clashApiPort,
|
||||
@Default(false) bool bypassLan,
|
||||
@Default(false) bool allowConnectionFromLan,
|
||||
@Default(false) bool enableFakeDns,
|
||||
@Default(true) bool enableDnsRouting,
|
||||
@Default(true) bool independentDnsCache,
|
||||
@Default(false) bool enableTlsFragment,
|
||||
@RangeWithOptionalCeilJsonConverter()
|
||||
@Default(RangeWithOptionalCeil(min: 10, max: 100))
|
||||
RangeWithOptionalCeil tlsFragmentSize,
|
||||
@RangeWithOptionalCeilJsonConverter()
|
||||
@Default(RangeWithOptionalCeil(min: 50, max: 200))
|
||||
RangeWithOptionalCeil tlsFragmentSleep,
|
||||
@Default(false) bool enableTlsMixedSniCase,
|
||||
@Default(false) bool enableTlsPadding,
|
||||
@RangeWithOptionalCeilJsonConverter()
|
||||
@Default(RangeWithOptionalCeil(min: 100, max: 200))
|
||||
RangeWithOptionalCeil tlsPaddingSize,
|
||||
@Default(false) bool enableMux,
|
||||
@Default(false) bool muxPadding,
|
||||
@Default(8) int muxMaxStreams,
|
||||
@Default(MuxProtocol.h2mux) MuxProtocol muxProtocol,
|
||||
@Default(false) bool enableWarp,
|
||||
@Default(WarpDetourMode.outbound) WarpDetourMode warpDetourMode,
|
||||
@Default("") String warpLicenseKey,
|
||||
@Default("auto") String warpCleanIp,
|
||||
@Default(0) int warpPort,
|
||||
@RangeWithOptionalCeilJsonConverter()
|
||||
@Default(RangeWithOptionalCeil())
|
||||
RangeWithOptionalCeil warpNoise,
|
||||
}) = _ConfigOptionEntity;
|
||||
final ServiceMode serviceMode;
|
||||
final LogLevel logLevel;
|
||||
final bool resolveDestination;
|
||||
@MappableField(key: "ipv6-mode")
|
||||
final IPv6Mode ipv6Mode;
|
||||
final String remoteDnsAddress;
|
||||
final DomainStrategy remoteDnsDomainStrategy;
|
||||
final String directDnsAddress;
|
||||
final DomainStrategy directDnsDomainStrategy;
|
||||
final int mixedPort;
|
||||
final int localDnsPort;
|
||||
final TunImplementation tunImplementation;
|
||||
final int mtu;
|
||||
final bool strictRoute;
|
||||
final String connectionTestUrl;
|
||||
final Duration urlTestInterval;
|
||||
final bool enableClashApi;
|
||||
final int clashApiPort;
|
||||
final bool bypassLan;
|
||||
final bool allowConnectionFromLan;
|
||||
final bool enableFakeDns;
|
||||
final bool enableDnsRouting;
|
||||
final bool independentDnsCache;
|
||||
final bool enableTlsFragment;
|
||||
final OptionalRange tlsFragmentSize;
|
||||
final OptionalRange tlsFragmentSleep;
|
||||
final bool enableTlsMixedSniCase;
|
||||
final bool enableTlsPadding;
|
||||
final OptionalRange tlsPaddingSize;
|
||||
final bool enableMux;
|
||||
final bool muxPadding;
|
||||
final int muxMaxStreams;
|
||||
final MuxProtocol muxProtocol;
|
||||
final bool enableWarp;
|
||||
final WarpDetourMode warpDetourMode;
|
||||
final String warpLicenseKey;
|
||||
final String warpCleanIp;
|
||||
final int warpPort;
|
||||
final OptionalRange warpNoise;
|
||||
|
||||
static ConfigOptionEntity initial = ConfigOptionEntity(
|
||||
serviceMode: ServiceMode.defaultMode,
|
||||
);
|
||||
factory ConfigOptionEntity.initial() =>
|
||||
ConfigOptionEntity(serviceMode: ServiceMode.defaultMode);
|
||||
|
||||
bool hasExperimentalOptions() {
|
||||
if (PlatformUtils.isDesktop && serviceMode == ServiceMode.tun) {
|
||||
@@ -88,56 +120,157 @@ class ConfigOptionEntity with _$ConfigOptionEntity {
|
||||
|
||||
String format() {
|
||||
const encoder = JsonEncoder.withIndent(' ');
|
||||
return encoder.convert(toJson());
|
||||
return encoder.convert(toMap());
|
||||
}
|
||||
|
||||
ConfigOptionEntity patch(ConfigOptionPatch patch) {
|
||||
return copyWith(
|
||||
serviceMode: patch.serviceMode ?? serviceMode,
|
||||
logLevel: patch.logLevel ?? logLevel,
|
||||
resolveDestination: patch.resolveDestination ?? resolveDestination,
|
||||
ipv6Mode: patch.ipv6Mode ?? ipv6Mode,
|
||||
remoteDnsAddress: patch.remoteDnsAddress ?? remoteDnsAddress,
|
||||
remoteDnsDomainStrategy:
|
||||
patch.remoteDnsDomainStrategy ?? remoteDnsDomainStrategy,
|
||||
directDnsAddress: patch.directDnsAddress ?? directDnsAddress,
|
||||
directDnsDomainStrategy:
|
||||
patch.directDnsDomainStrategy ?? directDnsDomainStrategy,
|
||||
mixedPort: patch.mixedPort ?? mixedPort,
|
||||
localDnsPort: patch.localDnsPort ?? localDnsPort,
|
||||
tunImplementation: patch.tunImplementation ?? tunImplementation,
|
||||
mtu: patch.mtu ?? mtu,
|
||||
strictRoute: patch.strictRoute ?? strictRoute,
|
||||
connectionTestUrl: patch.connectionTestUrl ?? connectionTestUrl,
|
||||
urlTestInterval: patch.urlTestInterval ?? urlTestInterval,
|
||||
enableClashApi: patch.enableClashApi ?? enableClashApi,
|
||||
clashApiPort: patch.clashApiPort ?? clashApiPort,
|
||||
bypassLan: patch.bypassLan ?? bypassLan,
|
||||
allowConnectionFromLan:
|
||||
patch.allowConnectionFromLan ?? allowConnectionFromLan,
|
||||
enableFakeDns: patch.enableFakeDns ?? enableFakeDns,
|
||||
enableDnsRouting: patch.enableDnsRouting ?? enableDnsRouting,
|
||||
independentDnsCache: patch.independentDnsCache ?? independentDnsCache,
|
||||
enableTlsFragment: patch.enableTlsFragment ?? enableTlsFragment,
|
||||
tlsFragmentSize: patch.tlsFragmentSize ?? tlsFragmentSize,
|
||||
tlsFragmentSleep: patch.tlsFragmentSleep ?? tlsFragmentSleep,
|
||||
enableTlsMixedSniCase:
|
||||
patch.enableTlsMixedSniCase ?? enableTlsMixedSniCase,
|
||||
enableTlsPadding: patch.enableTlsPadding ?? enableTlsPadding,
|
||||
tlsPaddingSize: patch.tlsPaddingSize ?? tlsPaddingSize,
|
||||
enableMux: patch.enableMux ?? enableMux,
|
||||
muxPadding: patch.muxPadding ?? muxPadding,
|
||||
muxMaxStreams: patch.muxMaxStreams ?? muxMaxStreams,
|
||||
muxProtocol: patch.muxProtocol ?? muxProtocol,
|
||||
enableWarp: patch.enableWarp ?? enableWarp,
|
||||
warpDetourMode: patch.warpDetourMode ?? warpDetourMode,
|
||||
warpLicenseKey: patch.warpLicenseKey ?? warpLicenseKey,
|
||||
warpCleanIp: patch.warpCleanIp ?? warpCleanIp,
|
||||
warpPort: patch.warpPort ?? warpPort,
|
||||
warpNoise: patch.warpNoise ?? warpNoise,
|
||||
);
|
||||
return copyWith.$delta(patch.delta());
|
||||
}
|
||||
|
||||
factory ConfigOptionEntity.fromJson(Map<String, dynamic> json) =>
|
||||
_$ConfigOptionEntityFromJson(json);
|
||||
SingboxConfigOption toSingbox({
|
||||
required String geoipPath,
|
||||
required String geositePath,
|
||||
required List<SingboxRule> rules,
|
||||
}) {
|
||||
return SingboxConfigOption(
|
||||
executeConfigAsIs: false,
|
||||
logLevel: logLevel,
|
||||
resolveDestination: resolveDestination,
|
||||
ipv6Mode: ipv6Mode,
|
||||
remoteDnsAddress: remoteDnsAddress,
|
||||
remoteDnsDomainStrategy: remoteDnsDomainStrategy,
|
||||
directDnsAddress: directDnsAddress,
|
||||
directDnsDomainStrategy: directDnsDomainStrategy,
|
||||
mixedPort: mixedPort,
|
||||
localDnsPort: localDnsPort,
|
||||
tunImplementation: tunImplementation,
|
||||
mtu: mtu,
|
||||
strictRoute: strictRoute,
|
||||
connectionTestUrl: connectionTestUrl,
|
||||
urlTestInterval: urlTestInterval,
|
||||
enableClashApi: enableClashApi,
|
||||
clashApiPort: clashApiPort,
|
||||
enableTun: serviceMode == ServiceMode.tun,
|
||||
enableTunService: serviceMode == ServiceMode.tunService,
|
||||
setSystemProxy: serviceMode == ServiceMode.systemProxy,
|
||||
bypassLan: bypassLan,
|
||||
allowConnectionFromLan: allowConnectionFromLan,
|
||||
enableFakeDns: enableFakeDns,
|
||||
enableDnsRouting: enableDnsRouting,
|
||||
independentDnsCache: independentDnsCache,
|
||||
enableTlsFragment: enableTlsFragment,
|
||||
tlsFragmentSize: tlsFragmentSize,
|
||||
tlsFragmentSleep: tlsFragmentSleep,
|
||||
enableTlsMixedSniCase: enableTlsMixedSniCase,
|
||||
enableTlsPadding: enableTlsPadding,
|
||||
tlsPaddingSize: tlsPaddingSize,
|
||||
enableMux: enableMux,
|
||||
muxPadding: muxPadding,
|
||||
muxMaxStreams: muxMaxStreams,
|
||||
muxProtocol: muxProtocol,
|
||||
enableWarp: enableWarp,
|
||||
warpDetourMode: warpDetourMode,
|
||||
warpLicenseKey: warpLicenseKey,
|
||||
warpCleanIp: warpCleanIp,
|
||||
warpPort: warpPort,
|
||||
warpNoise: warpNoise,
|
||||
geoipPath: geoipPath,
|
||||
geositePath: geositePath,
|
||||
rules: rules,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@MappableClass(
|
||||
caseStyle: CaseStyle.paramCase,
|
||||
ignoreNull: true,
|
||||
includeCustomMappers: [
|
||||
OptionalRangeJsonMapper(),
|
||||
IntervalInSecondsMapper(),
|
||||
],
|
||||
)
|
||||
class ConfigOptionPatch with ConfigOptionPatchMappable {
|
||||
const ConfigOptionPatch({
|
||||
this.serviceMode,
|
||||
this.logLevel,
|
||||
this.resolveDestination,
|
||||
this.ipv6Mode,
|
||||
this.remoteDnsAddress,
|
||||
this.remoteDnsDomainStrategy,
|
||||
this.directDnsAddress,
|
||||
this.directDnsDomainStrategy,
|
||||
this.mixedPort,
|
||||
this.localDnsPort,
|
||||
this.tunImplementation,
|
||||
this.mtu,
|
||||
this.strictRoute,
|
||||
this.connectionTestUrl,
|
||||
this.urlTestInterval,
|
||||
this.enableClashApi,
|
||||
this.clashApiPort,
|
||||
this.bypassLan,
|
||||
this.allowConnectionFromLan,
|
||||
this.enableFakeDns,
|
||||
this.enableDnsRouting,
|
||||
this.independentDnsCache,
|
||||
this.enableTlsFragment,
|
||||
this.tlsFragmentSize,
|
||||
this.tlsFragmentSleep,
|
||||
this.enableTlsMixedSniCase,
|
||||
this.enableTlsPadding,
|
||||
this.tlsPaddingSize,
|
||||
this.enableMux,
|
||||
this.muxPadding,
|
||||
this.muxMaxStreams,
|
||||
this.muxProtocol,
|
||||
this.enableWarp,
|
||||
this.warpDetourMode,
|
||||
this.warpLicenseKey,
|
||||
this.warpCleanIp,
|
||||
this.warpPort,
|
||||
this.warpNoise,
|
||||
});
|
||||
|
||||
final ServiceMode? serviceMode;
|
||||
final LogLevel? logLevel;
|
||||
final bool? resolveDestination;
|
||||
@MappableField(key: "ipv6-mode")
|
||||
final IPv6Mode? ipv6Mode;
|
||||
final String? remoteDnsAddress;
|
||||
final DomainStrategy? remoteDnsDomainStrategy;
|
||||
final String? directDnsAddress;
|
||||
final DomainStrategy? directDnsDomainStrategy;
|
||||
final int? mixedPort;
|
||||
final int? localDnsPort;
|
||||
final TunImplementation? tunImplementation;
|
||||
final int? mtu;
|
||||
final bool? strictRoute;
|
||||
final String? connectionTestUrl;
|
||||
final Duration? urlTestInterval;
|
||||
final bool? enableClashApi;
|
||||
final int? clashApiPort;
|
||||
final bool? bypassLan;
|
||||
final bool? allowConnectionFromLan;
|
||||
final bool? enableFakeDns;
|
||||
final bool? enableDnsRouting;
|
||||
final bool? independentDnsCache;
|
||||
final bool? enableTlsFragment;
|
||||
final OptionalRange? tlsFragmentSize;
|
||||
final OptionalRange? tlsFragmentSleep;
|
||||
final bool? enableTlsMixedSniCase;
|
||||
final bool? enableTlsPadding;
|
||||
final OptionalRange? tlsPaddingSize;
|
||||
final bool? enableMux;
|
||||
final bool? muxPadding;
|
||||
final int? muxMaxStreams;
|
||||
final MuxProtocol? muxProtocol;
|
||||
final bool? enableWarp;
|
||||
final WarpDetourMode? warpDetourMode;
|
||||
final String? warpLicenseKey;
|
||||
final String? warpCleanIp;
|
||||
final int? warpPort;
|
||||
final OptionalRange? warpNoise;
|
||||
|
||||
Map<String, dynamic> delta() =>
|
||||
toMap()..removeWhere((key, value) => value == null);
|
||||
}
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:hiddify/core/model/range.dart';
|
||||
import 'package:hiddify/core/utils/json_converters.dart';
|
||||
import 'package:hiddify/features/log/model/log_level.dart';
|
||||
import 'package:hiddify/singbox/model/singbox_config_enum.dart';
|
||||
|
||||
part 'config_option_patch.freezed.dart';
|
||||
part 'config_option_patch.g.dart';
|
||||
|
||||
@freezed
|
||||
class ConfigOptionPatch with _$ConfigOptionPatch {
|
||||
const ConfigOptionPatch._();
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.kebab)
|
||||
const factory ConfigOptionPatch({
|
||||
ServiceMode? serviceMode,
|
||||
LogLevel? logLevel,
|
||||
bool? resolveDestination,
|
||||
IPv6Mode? ipv6Mode,
|
||||
String? remoteDnsAddress,
|
||||
DomainStrategy? remoteDnsDomainStrategy,
|
||||
String? directDnsAddress,
|
||||
DomainStrategy? directDnsDomainStrategy,
|
||||
int? mixedPort,
|
||||
int? localDnsPort,
|
||||
TunImplementation? tunImplementation,
|
||||
int? mtu,
|
||||
bool? strictRoute,
|
||||
String? connectionTestUrl,
|
||||
@IntervalInSecondsConverter() Duration? urlTestInterval,
|
||||
bool? enableClashApi,
|
||||
int? clashApiPort,
|
||||
bool? bypassLan,
|
||||
bool? allowConnectionFromLan,
|
||||
bool? enableFakeDns,
|
||||
bool? enableDnsRouting,
|
||||
bool? independentDnsCache,
|
||||
bool? enableTlsFragment,
|
||||
@RangeWithOptionalCeilJsonConverter()
|
||||
RangeWithOptionalCeil? tlsFragmentSize,
|
||||
@RangeWithOptionalCeilJsonConverter()
|
||||
RangeWithOptionalCeil? tlsFragmentSleep,
|
||||
bool? enableTlsMixedSniCase,
|
||||
bool? enableTlsPadding,
|
||||
@RangeWithOptionalCeilJsonConverter() RangeWithOptionalCeil? tlsPaddingSize,
|
||||
bool? enableMux,
|
||||
bool? muxPadding,
|
||||
int? muxMaxStreams,
|
||||
MuxProtocol? muxProtocol,
|
||||
bool? enableWarp,
|
||||
WarpDetourMode? warpDetourMode,
|
||||
String? warpLicenseKey,
|
||||
String? warpCleanIp,
|
||||
int? warpPort,
|
||||
@RangeWithOptionalCeilJsonConverter() RangeWithOptionalCeil? warpNoise,
|
||||
}) = _ConfigOptionPatch;
|
||||
|
||||
factory ConfigOptionPatch.fromJson(Map<String, dynamic> json) =>
|
||||
_$ConfigOptionPatchFromJson(json);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
import 'package:hiddify/features/config_option/data/config_option_data_providers.dart';
|
||||
import 'package:hiddify/features/config_option/model/config_option_entity.dart';
|
||||
import 'package:hiddify/features/config_option/model/config_option_patch.dart';
|
||||
import 'package:hiddify/utils/custom_loggers.dart';
|
||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||
|
||||
|
||||
@@ -5,12 +5,11 @@ import 'package:flutter/services.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hiddify/core/localization/translations.dart';
|
||||
import 'package:hiddify/core/model/failures.dart';
|
||||
import 'package:hiddify/core/model/range.dart';
|
||||
import 'package:hiddify/core/model/optional_range.dart';
|
||||
import 'package:hiddify/core/widget/adaptive_icon.dart';
|
||||
import 'package:hiddify/core/widget/tip_card.dart';
|
||||
import 'package:hiddify/features/common/nested_app_bar.dart';
|
||||
import 'package:hiddify/features/config_option/model/config_option_entity.dart';
|
||||
import 'package:hiddify/features/config_option/model/config_option_patch.dart';
|
||||
import 'package:hiddify/features/config_option/notifier/config_option_notifier.dart';
|
||||
import 'package:hiddify/features/config_option/overview/warp_options_widgets.dart';
|
||||
import 'package:hiddify/features/log/model/log_level.dart';
|
||||
@@ -28,7 +27,7 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
||||
Widget build(BuildContext context, WidgetRef ref) {
|
||||
final t = ref.watch(translationsProvider);
|
||||
|
||||
final defaultOptions = ConfigOptionEntity.initial;
|
||||
final defaultOptions = ConfigOptionEntity.initial();
|
||||
final asyncOptions = ref.watch(configOptionNotifierProvider);
|
||||
|
||||
Future<void> changeOption(ConfigOptionPatch patch) async {
|
||||
@@ -349,8 +348,7 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
||||
if (range == null) return;
|
||||
await changeOption(
|
||||
ConfigOptionPatch(
|
||||
tlsFragmentSize:
|
||||
RangeWithOptionalCeil.tryParse(range),
|
||||
tlsFragmentSize: OptionalRange.tryParse(range),
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -367,8 +365,7 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
||||
if (range == null) return;
|
||||
await changeOption(
|
||||
ConfigOptionPatch(
|
||||
tlsFragmentSleep:
|
||||
RangeWithOptionalCeil.tryParse(range),
|
||||
tlsFragmentSleep: OptionalRange.tryParse(range),
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -402,7 +399,7 @@ class ConfigOptionsPage extends HookConsumerWidget {
|
||||
if (range == null) return;
|
||||
await changeOption(
|
||||
ConfigOptionPatch(
|
||||
tlsPaddingSize: RangeWithOptionalCeil.tryParse(range),
|
||||
tlsPaddingSize: OptionalRange.tryParse(range),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -3,9 +3,8 @@ import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:hiddify/core/localization/translations.dart';
|
||||
import 'package:hiddify/core/model/constants.dart';
|
||||
import 'package:hiddify/core/model/range.dart';
|
||||
import 'package:hiddify/core/model/optional_range.dart';
|
||||
import 'package:hiddify/features/config_option/model/config_option_entity.dart';
|
||||
import 'package:hiddify/features/config_option/model/config_option_patch.dart';
|
||||
import 'package:hiddify/features/config_option/notifier/warp_option_notifier.dart';
|
||||
import 'package:hiddify/features/settings/widgets/settings_input_dialog.dart';
|
||||
import 'package:hiddify/singbox/model/singbox_config_enum.dart';
|
||||
@@ -134,10 +133,7 @@ class WarpOptionsTiles extends HookConsumerWidget {
|
||||
if (warpNoise == null) return;
|
||||
await onChange(
|
||||
ConfigOptionPatch(
|
||||
warpNoise: RangeWithOptionalCeil.tryParse(
|
||||
warpNoise,
|
||||
allowEmpty: true,
|
||||
),
|
||||
warpNoise: OptionalRange.tryParse(warpNoise, allowEmpty: true),
|
||||
),
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:ffi';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:hiddify/core/utils/ffi_utils.dart';
|
||||
import 'package:hiddify/utils/custom_loggers.dart';
|
||||
import 'package:hiddify/utils/utils.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import 'package:posix/posix.dart';
|
||||
import 'package:win32/win32.dart';
|
||||
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
part 'log_level.mapper.dart';
|
||||
|
||||
@MappableEnum()
|
||||
enum LogLevel {
|
||||
trace,
|
||||
debug,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/semantics.dart';
|
||||
import 'package:gap/gap.dart';
|
||||
import 'package:hiddify/core/localization/translations.dart';
|
||||
import 'package:hiddify/core/widget/animated_visibility.dart';
|
||||
|
||||
@@ -8,7 +8,6 @@ import 'package:hiddify/features/connection/notifier/connection_notifier.dart';
|
||||
import 'package:hiddify/features/proxy/data/proxy_data_providers.dart';
|
||||
import 'package:hiddify/features/proxy/model/proxy_entity.dart';
|
||||
import 'package:hiddify/features/proxy/model/proxy_failure.dart';
|
||||
import 'package:hiddify/singbox/model/singbox_proxy_type.dart';
|
||||
import 'package:hiddify/utils/pref_notifier.dart';
|
||||
import 'package:hiddify/utils/riverpod_utils.dart';
|
||||
import 'package:hiddify/utils/utils.dart';
|
||||
@@ -94,13 +93,13 @@ class ProxiesOverviewNotifier extends _$ProxiesOverviewNotifier with AppLogger {
|
||||
for (final group in proxies) {
|
||||
final sortedItems = switch (sortBy) {
|
||||
ProxiesSort.name => group.items.sortedWith((a, b) {
|
||||
if(a.type.isGroup && !b.type.isGroup) return -1;
|
||||
if(!a.type.isGroup && b.type.isGroup) return 1;
|
||||
if (a.type.isGroup && !b.type.isGroup) return -1;
|
||||
if (!a.type.isGroup && b.type.isGroup) return 1;
|
||||
return a.tag.compareTo(b.tag);
|
||||
}),
|
||||
}),
|
||||
ProxiesSort.delay => group.items.sortedWith((a, b) {
|
||||
if(a.type.isGroup && !b.type.isGroup) return -1;
|
||||
if(!a.type.isGroup && b.type.isGroup) return 1;
|
||||
if (a.type.isGroup && !b.type.isGroup) return -1;
|
||||
if (!a.type.isGroup && b.type.isGroup) return 1;
|
||||
|
||||
final ai = a.urlTestDelay;
|
||||
final bi = b.urlTestDelay;
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'dart:io';
|
||||
import 'package:hiddify/core/localization/translations.dart';
|
||||
import 'package:hiddify/core/model/constants.dart';
|
||||
import 'package:hiddify/core/router/router.dart';
|
||||
import 'package:hiddify/features/config_option/model/config_option_patch.dart';
|
||||
import 'package:hiddify/features/config_option/model/config_option_entity.dart';
|
||||
import 'package:hiddify/features/config_option/notifier/config_option_notifier.dart';
|
||||
import 'package:hiddify/features/connection/model/connection_status.dart';
|
||||
import 'package:hiddify/features/connection/notifier/connection_notifier.dart';
|
||||
|
||||
@@ -1,19 +1,24 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:hiddify/core/localization/translations.dart';
|
||||
import 'package:hiddify/utils/platform_utils.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
@JsonEnum(valueField: 'key')
|
||||
part 'singbox_config_enum.mapper.dart';
|
||||
|
||||
@MappableEnum()
|
||||
enum ServiceMode {
|
||||
proxy("proxy"),
|
||||
systemProxy("system-proxy"),
|
||||
tun("vpn"),
|
||||
tunService("vpn-service");
|
||||
@MappableValue("proxy")
|
||||
proxy,
|
||||
|
||||
const ServiceMode(this.key);
|
||||
@MappableValue("system-proxy")
|
||||
systemProxy,
|
||||
|
||||
final String key;
|
||||
@MappableValue("vpn")
|
||||
tun,
|
||||
|
||||
@MappableValue("vpn-service")
|
||||
tunService;
|
||||
|
||||
static ServiceMode get defaultMode =>
|
||||
PlatformUtils.isDesktop ? systemProxy : tun;
|
||||
@@ -39,16 +44,19 @@ enum ServiceMode {
|
||||
};
|
||||
}
|
||||
|
||||
@JsonEnum(valueField: 'key')
|
||||
@MappableEnum()
|
||||
enum IPv6Mode {
|
||||
disable("ipv4_only"),
|
||||
enable("prefer_ipv4"),
|
||||
prefer("prefer_ipv6"),
|
||||
only("ipv6_only");
|
||||
@MappableValue("ipv4_only")
|
||||
disable,
|
||||
|
||||
const IPv6Mode(this.key);
|
||||
@MappableValue("prefer_ipv4")
|
||||
enable,
|
||||
|
||||
final String key;
|
||||
@MappableValue("prefer_ipv6")
|
||||
prefer,
|
||||
|
||||
@MappableValue("ipv6_only")
|
||||
only;
|
||||
|
||||
String present(TranslationsEn t) => switch (this) {
|
||||
disable => t.settings.config.ipv6Modes.disable,
|
||||
@@ -58,12 +66,21 @@ enum IPv6Mode {
|
||||
};
|
||||
}
|
||||
|
||||
@JsonEnum(valueField: 'key')
|
||||
@MappableEnum()
|
||||
enum DomainStrategy {
|
||||
@MappableValue("")
|
||||
auto(""),
|
||||
|
||||
@MappableValue("prefer_ipv6")
|
||||
preferIpv6("prefer_ipv6"),
|
||||
|
||||
@MappableValue("prefer_ipv4")
|
||||
preferIpv4("prefer_ipv4"),
|
||||
|
||||
@MappableValue("ipv4_only")
|
||||
ipv4Only("ipv4_only"),
|
||||
|
||||
@MappableValue("ipv6_only")
|
||||
ipv6Only("ipv6_only");
|
||||
|
||||
const DomainStrategy(this.key);
|
||||
@@ -76,18 +93,21 @@ enum DomainStrategy {
|
||||
};
|
||||
}
|
||||
|
||||
@MappableEnum()
|
||||
enum TunImplementation {
|
||||
mixed,
|
||||
system,
|
||||
gVisor;
|
||||
}
|
||||
|
||||
@MappableEnum()
|
||||
enum MuxProtocol {
|
||||
h2mux,
|
||||
smux,
|
||||
yamux;
|
||||
}
|
||||
|
||||
@MappableEnum()
|
||||
enum WarpDetourMode {
|
||||
outbound,
|
||||
inbound;
|
||||
|
||||
@@ -1,86 +1,127 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:hiddify/core/model/range.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
import 'package:hiddify/core/model/optional_range.dart';
|
||||
import 'package:hiddify/features/log/model/log_level.dart';
|
||||
import 'package:hiddify/singbox/model/singbox_config_enum.dart';
|
||||
import 'package:hiddify/singbox/model/singbox_rule.dart';
|
||||
|
||||
part 'singbox_config_option.freezed.dart';
|
||||
part 'singbox_config_option.g.dart';
|
||||
part 'singbox_config_option.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class SingboxConfigOption with _$SingboxConfigOption {
|
||||
const SingboxConfigOption._();
|
||||
@MappableClass(
|
||||
caseStyle: CaseStyle.paramCase,
|
||||
includeCustomMappers: [
|
||||
OptionalRangeJsonMapper(),
|
||||
IntervalMapper(),
|
||||
],
|
||||
)
|
||||
class SingboxConfigOption with SingboxConfigOptionMappable {
|
||||
const SingboxConfigOption({
|
||||
required this.executeConfigAsIs,
|
||||
required this.logLevel,
|
||||
required this.resolveDestination,
|
||||
required this.ipv6Mode,
|
||||
required this.remoteDnsAddress,
|
||||
required this.remoteDnsDomainStrategy,
|
||||
required this.directDnsAddress,
|
||||
required this.directDnsDomainStrategy,
|
||||
required this.mixedPort,
|
||||
required this.localDnsPort,
|
||||
required this.tunImplementation,
|
||||
required this.mtu,
|
||||
required this.strictRoute,
|
||||
required this.connectionTestUrl,
|
||||
required this.urlTestInterval,
|
||||
required this.enableClashApi,
|
||||
required this.clashApiPort,
|
||||
required this.enableTun,
|
||||
required this.enableTunService,
|
||||
required this.setSystemProxy,
|
||||
required this.bypassLan,
|
||||
required this.allowConnectionFromLan,
|
||||
required this.enableFakeDns,
|
||||
required this.enableDnsRouting,
|
||||
required this.independentDnsCache,
|
||||
required this.enableTlsFragment,
|
||||
required this.tlsFragmentSize,
|
||||
required this.tlsFragmentSleep,
|
||||
required this.enableTlsMixedSniCase,
|
||||
required this.enableTlsPadding,
|
||||
required this.tlsPaddingSize,
|
||||
required this.enableMux,
|
||||
required this.muxPadding,
|
||||
required this.muxMaxStreams,
|
||||
required this.muxProtocol,
|
||||
required this.enableWarp,
|
||||
required this.warpDetourMode,
|
||||
required this.warpLicenseKey,
|
||||
required this.warpCleanIp,
|
||||
required this.warpPort,
|
||||
required this.warpNoise,
|
||||
required this.geoipPath,
|
||||
required this.geositePath,
|
||||
required this.rules,
|
||||
});
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.kebab)
|
||||
const factory SingboxConfigOption({
|
||||
required bool executeConfigAsIs,
|
||||
required LogLevel logLevel,
|
||||
required bool resolveDestination,
|
||||
required IPv6Mode ipv6Mode,
|
||||
required String remoteDnsAddress,
|
||||
required DomainStrategy remoteDnsDomainStrategy,
|
||||
required String directDnsAddress,
|
||||
required DomainStrategy directDnsDomainStrategy,
|
||||
required int mixedPort,
|
||||
required int localDnsPort,
|
||||
required TunImplementation tunImplementation,
|
||||
required int mtu,
|
||||
required bool strictRoute,
|
||||
required String connectionTestUrl,
|
||||
@IntervalConverter() required Duration urlTestInterval,
|
||||
required bool enableClashApi,
|
||||
required int clashApiPort,
|
||||
required bool enableTun,
|
||||
required bool enableTunService,
|
||||
required bool setSystemProxy,
|
||||
required bool bypassLan,
|
||||
required bool allowConnectionFromLan,
|
||||
required bool enableFakeDns,
|
||||
required bool enableDnsRouting,
|
||||
required bool independentDnsCache,
|
||||
required bool enableTlsFragment,
|
||||
@RangeWithOptionalCeilJsonConverter()
|
||||
required RangeWithOptionalCeil tlsFragmentSize,
|
||||
@RangeWithOptionalCeilJsonConverter()
|
||||
required RangeWithOptionalCeil tlsFragmentSleep,
|
||||
required bool enableTlsMixedSniCase,
|
||||
required bool enableTlsPadding,
|
||||
@RangeWithOptionalCeilJsonConverter()
|
||||
required RangeWithOptionalCeil tlsPaddingSize,
|
||||
required bool enableMux,
|
||||
required bool muxPadding,
|
||||
required int muxMaxStreams,
|
||||
required MuxProtocol muxProtocol,
|
||||
required bool enableWarp,
|
||||
required WarpDetourMode warpDetourMode,
|
||||
required String warpLicenseKey,
|
||||
required String warpCleanIp,
|
||||
required int warpPort,
|
||||
@RangeWithOptionalCeilJsonConverter()
|
||||
required RangeWithOptionalCeil warpNoise,
|
||||
required String geoipPath,
|
||||
required String geositePath,
|
||||
required List<SingboxRule> rules,
|
||||
}) = _SingboxConfigOption;
|
||||
final bool executeConfigAsIs;
|
||||
final LogLevel logLevel;
|
||||
final bool resolveDestination;
|
||||
@MappableField(key: "ipv6-mode")
|
||||
final IPv6Mode ipv6Mode;
|
||||
final String remoteDnsAddress;
|
||||
final DomainStrategy remoteDnsDomainStrategy;
|
||||
final String directDnsAddress;
|
||||
final DomainStrategy directDnsDomainStrategy;
|
||||
final int mixedPort;
|
||||
final int localDnsPort;
|
||||
final TunImplementation tunImplementation;
|
||||
final int mtu;
|
||||
final bool strictRoute;
|
||||
final String connectionTestUrl;
|
||||
final Duration urlTestInterval;
|
||||
final bool enableClashApi;
|
||||
final int clashApiPort;
|
||||
final bool enableTun;
|
||||
final bool enableTunService;
|
||||
final bool setSystemProxy;
|
||||
final bool bypassLan;
|
||||
final bool allowConnectionFromLan;
|
||||
final bool enableFakeDns;
|
||||
final bool enableDnsRouting;
|
||||
final bool independentDnsCache;
|
||||
final bool enableTlsFragment;
|
||||
final OptionalRange tlsFragmentSize;
|
||||
final OptionalRange tlsFragmentSleep;
|
||||
final bool enableTlsMixedSniCase;
|
||||
final bool enableTlsPadding;
|
||||
final OptionalRange tlsPaddingSize;
|
||||
final bool enableMux;
|
||||
final bool muxPadding;
|
||||
final int muxMaxStreams;
|
||||
final MuxProtocol muxProtocol;
|
||||
final bool enableWarp;
|
||||
final WarpDetourMode warpDetourMode;
|
||||
final String warpLicenseKey;
|
||||
final String warpCleanIp;
|
||||
final int warpPort;
|
||||
final OptionalRange warpNoise;
|
||||
final String geoipPath;
|
||||
final String geositePath;
|
||||
final List<SingboxRule> rules;
|
||||
|
||||
String format() {
|
||||
const encoder = JsonEncoder.withIndent(' ');
|
||||
return encoder.convert(toJson());
|
||||
return encoder.convert(toMap());
|
||||
}
|
||||
|
||||
factory SingboxConfigOption.fromJson(Map<String, dynamic> json) =>
|
||||
_$SingboxConfigOptionFromJson(json);
|
||||
}
|
||||
|
||||
class IntervalConverter implements JsonConverter<Duration, String> {
|
||||
const IntervalConverter();
|
||||
class IntervalMapper extends SimpleMapper<Duration> {
|
||||
const IntervalMapper();
|
||||
|
||||
@override
|
||||
Duration fromJson(String json) =>
|
||||
Duration(minutes: int.parse(json.replaceAll("m", "")));
|
||||
Duration decode(dynamic value) =>
|
||||
Duration(minutes: int.parse((value as String).replaceAll("m", "")));
|
||||
|
||||
@override
|
||||
String toJson(Duration object) => "${object.inMinutes}m";
|
||||
String encode(Duration self) => "${self.inMinutes}m";
|
||||
}
|
||||
|
||||
@@ -1,35 +1,37 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:dart_mappable/dart_mappable.dart';
|
||||
|
||||
part 'singbox_rule.freezed.dart';
|
||||
part 'singbox_rule.g.dart';
|
||||
part 'singbox_rule.mapper.dart';
|
||||
|
||||
@freezed
|
||||
class SingboxRule with _$SingboxRule {
|
||||
const SingboxRule._();
|
||||
@MappableClass()
|
||||
class SingboxRule with SingboxRuleMappable {
|
||||
const SingboxRule({
|
||||
this.domains,
|
||||
this.ip,
|
||||
this.port,
|
||||
this.protocol,
|
||||
this.network = RuleNetwork.tcpAndUdp,
|
||||
this.outbound = RuleOutbound.proxy,
|
||||
});
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.kebab)
|
||||
const factory SingboxRule({
|
||||
String? domains,
|
||||
String? ip,
|
||||
String? port,
|
||||
String? protocol,
|
||||
@Default(RuleNetwork.tcpAndUdp) RuleNetwork network,
|
||||
@Default(RuleOutbound.proxy) RuleOutbound outbound,
|
||||
}) = _SingboxRule;
|
||||
|
||||
factory SingboxRule.fromJson(Map<String, dynamic> json) =>
|
||||
_$SingboxRuleFromJson(json);
|
||||
final String? domains;
|
||||
final String? ip;
|
||||
final String? port;
|
||||
final String? protocol;
|
||||
final RuleNetwork network;
|
||||
final RuleOutbound outbound;
|
||||
}
|
||||
|
||||
@MappableEnum()
|
||||
enum RuleOutbound { proxy, bypass, block }
|
||||
|
||||
@JsonEnum(valueField: 'key')
|
||||
@MappableEnum()
|
||||
enum RuleNetwork {
|
||||
tcpAndUdp(""),
|
||||
tcp("tcp"),
|
||||
udp("udp");
|
||||
@MappableValue("")
|
||||
tcpAndUdp,
|
||||
|
||||
const RuleNetwork(this.key);
|
||||
@MappableValue("tcp")
|
||||
tcp,
|
||||
|
||||
final String? key;
|
||||
@MappableValue("udp")
|
||||
udp;
|
||||
}
|
||||
|
||||
@@ -121,7 +121,7 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
||||
return TaskEither(
|
||||
() => CombineWorker().execute(
|
||||
() {
|
||||
final json = jsonEncode(options.toJson());
|
||||
final json = options.toJson();
|
||||
final err = _box
|
||||
.changeConfigOptions(json.toNativeUtf8().cast())
|
||||
.cast<Utf8>()
|
||||
|
||||
@@ -80,7 +80,7 @@ class PlatformSingboxService with InfraLogger implements SingboxService {
|
||||
loggy.debug("changing options");
|
||||
await methodChannel.invokeMethod(
|
||||
"change_config_options",
|
||||
jsonEncode(options.toJson()),
|
||||
options.toJson(),
|
||||
);
|
||||
return right(unit);
|
||||
},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:fpdart/fpdart.dart';
|
||||
import 'package:hiddify/features/profile/data/profile_parser.dart';
|
||||
import 'package:hiddify/features/profile/data/profile_repository.dart';
|
||||
import 'package:hiddify/singbox/model/singbox_proxy_type.dart';
|
||||
|
||||
Reference in New Issue
Block a user