初始化提交
This commit is contained in:
Executable
+117
@@ -0,0 +1,117 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
// import 'package:kaer_with_panels/core/localization/translations.dart';
|
||||
import 'package:kaer_with_panels/utils/platform_utils.dart';
|
||||
|
||||
@JsonEnum(valueField: 'key')
|
||||
enum ServiceMode {
|
||||
proxy("proxy"),
|
||||
systemProxy("system-proxy"),
|
||||
tun("vpn"),
|
||||
tunService("vpn-service");
|
||||
|
||||
const ServiceMode(this.key);
|
||||
|
||||
final String key;
|
||||
|
||||
static ServiceMode get defaultMode =>
|
||||
PlatformUtils.isDesktop ? systemProxy : tun;
|
||||
|
||||
/// supported service mode based on platform, use this instead of [values] in UI
|
||||
static List<ServiceMode> get choices {
|
||||
if (Platform.isWindows || Platform.isLinux) {
|
||||
return values;
|
||||
} else if (Platform.isMacOS) {
|
||||
return [proxy, systemProxy, tun];
|
||||
}
|
||||
// mobile
|
||||
return [proxy, tun];
|
||||
}
|
||||
|
||||
bool get isExperimental => switch (this) {
|
||||
tun => PlatformUtils.isDesktop,
|
||||
tunService => PlatformUtils.isDesktop,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
// String present(TranslationsEn t) => switch (this) {
|
||||
// proxy => t.config.serviceModes.proxy,
|
||||
// systemProxy => t.config.serviceModes.systemProxy,
|
||||
// tun =>
|
||||
// "${t.config.serviceModes.tun}${isExperimental ? " (${t.settings.experimental})" : ""}",
|
||||
// tunService =>
|
||||
// "${t.config.serviceModes.tunService}${isExperimental ? " (${t.settings.experimental})" : ""}",
|
||||
// };
|
||||
|
||||
// String presentShort(TranslationsEn t) => switch (this) {
|
||||
// proxy => t.config.shortServiceModes.proxy,
|
||||
// systemProxy => t.config.shortServiceModes.systemProxy,
|
||||
// tun => t.config.shortServiceModes.tun,
|
||||
// tunService => t.config.shortServiceModes.tunService,
|
||||
// };
|
||||
}
|
||||
|
||||
@JsonEnum(valueField: 'key')
|
||||
enum IPv6Mode {
|
||||
disable("ipv4_only"),
|
||||
enable("prefer_ipv4"),
|
||||
prefer("prefer_ipv6"),
|
||||
only("ipv6_only");
|
||||
|
||||
const IPv6Mode(this.key);
|
||||
|
||||
final String key;
|
||||
|
||||
// String present(TranslationsEn t) => switch (this) {
|
||||
// disable => t.config.ipv6Modes.disable,
|
||||
// enable => t.config.ipv6Modes.enable,
|
||||
// prefer => t.config.ipv6Modes.prefer,
|
||||
// only => t.config.ipv6Modes.only,
|
||||
// };
|
||||
}
|
||||
|
||||
@JsonEnum(valueField: 'key')
|
||||
enum DomainStrategy {
|
||||
auto(""),
|
||||
preferIpv6("prefer_ipv6"),
|
||||
preferIpv4("prefer_ipv4"),
|
||||
ipv4Only("ipv4_only"),
|
||||
ipv6Only("ipv6_only");
|
||||
|
||||
const DomainStrategy(this.key);
|
||||
|
||||
final String key;
|
||||
|
||||
String get displayName => switch (this) {
|
||||
auto => "auto",
|
||||
_ => key,
|
||||
};
|
||||
}
|
||||
|
||||
enum TunImplementation {
|
||||
mixed,
|
||||
system,
|
||||
gvisor;
|
||||
}
|
||||
|
||||
enum MuxProtocol {
|
||||
h2mux,
|
||||
smux,
|
||||
yamux;
|
||||
}
|
||||
|
||||
@JsonEnum(valueField: 'key')
|
||||
enum WarpDetourMode {
|
||||
proxyOverWarp("proxy_over_warp"),
|
||||
warpOverProxy("warp_over_proxy");
|
||||
|
||||
const WarpDetourMode(this.key);
|
||||
|
||||
final String key;
|
||||
|
||||
// String present(TranslationsEn t) => switch (this) {
|
||||
// proxyOverWarp => t.config.warpDetourModes.proxyOverWarp,
|
||||
// warpOverProxy => t.config.warpDetourModes.warpOverProxy,
|
||||
// };
|
||||
}
|
||||
Executable
+116
@@ -0,0 +1,116 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:kaer_with_panels/core/model/optional_range.dart';
|
||||
import 'package:kaer_with_panels/core/utils/json_converters.dart';
|
||||
import 'package:kaer_with_panels/features/log/model/log_level.dart';
|
||||
import 'package:kaer_with_panels/singbox/model/singbox_config_enum.dart';
|
||||
import 'package:kaer_with_panels/singbox/model/singbox_rule.dart';
|
||||
|
||||
part 'singbox_config_option.freezed.dart';
|
||||
part 'singbox_config_option.g.dart';
|
||||
|
||||
@freezed
|
||||
class SingboxConfigOption with _$SingboxConfigOption {
|
||||
const SingboxConfigOption._();
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.kebab)
|
||||
const factory SingboxConfigOption({
|
||||
required String region,
|
||||
required bool blockAds,
|
||||
required bool useXrayCoreWhenPossible,
|
||||
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 tproxyPort,
|
||||
required int localDnsPort,
|
||||
required TunImplementation tunImplementation,
|
||||
required int mtu,
|
||||
required bool strictRoute,
|
||||
required String connectionTestUrl,
|
||||
@IntervalInSecondsConverter() 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 String geoipPath,
|
||||
// required String geositePath,
|
||||
required List<SingboxRule> rules,
|
||||
required SingboxMuxOption mux,
|
||||
required SingboxTlsTricks tlsTricks,
|
||||
required SingboxWarpOption warp,
|
||||
required SingboxWarpOption warp2,
|
||||
}) = _SingboxConfigOption;
|
||||
|
||||
String format() {
|
||||
const encoder = JsonEncoder.withIndent(' ');
|
||||
return encoder.convert(toJson());
|
||||
}
|
||||
|
||||
factory SingboxConfigOption.fromJson(Map<String, dynamic> json) =>
|
||||
_$SingboxConfigOptionFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SingboxWarpOption with _$SingboxWarpOption {
|
||||
@JsonSerializable(fieldRename: FieldRename.kebab)
|
||||
const factory SingboxWarpOption({
|
||||
required bool enable,
|
||||
required WarpDetourMode mode,
|
||||
required String wireguardConfig,
|
||||
required String licenseKey,
|
||||
required String accountId,
|
||||
required String accessToken,
|
||||
required String cleanIp,
|
||||
required int cleanPort,
|
||||
@OptionalRangeJsonConverter() required OptionalRange noise,
|
||||
@OptionalRangeJsonConverter() required OptionalRange noiseSize,
|
||||
@OptionalRangeJsonConverter() required OptionalRange noiseDelay,
|
||||
@OptionalRangeJsonConverter() required String noiseMode,
|
||||
}) = _SingboxWarpOption;
|
||||
|
||||
factory SingboxWarpOption.fromJson(Map<String, dynamic> json) =>
|
||||
_$SingboxWarpOptionFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SingboxMuxOption with _$SingboxMuxOption {
|
||||
@JsonSerializable(fieldRename: FieldRename.kebab)
|
||||
const factory SingboxMuxOption({
|
||||
required bool enable,
|
||||
required bool padding,
|
||||
required int maxStreams,
|
||||
required MuxProtocol protocol,
|
||||
}) = _SingboxMuxOption;
|
||||
|
||||
factory SingboxMuxOption.fromJson(Map<String, dynamic> json) =>
|
||||
_$SingboxMuxOptionFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SingboxTlsTricks with _$SingboxTlsTricks {
|
||||
@JsonSerializable(fieldRename: FieldRename.kebab)
|
||||
const factory SingboxTlsTricks({
|
||||
required bool enableFragment,
|
||||
@OptionalRangeJsonConverter() required OptionalRange fragmentSize,
|
||||
@OptionalRangeJsonConverter() required OptionalRange fragmentSleep,
|
||||
required bool mixedSniCase,
|
||||
required bool enablePadding,
|
||||
@OptionalRangeJsonConverter() required OptionalRange paddingSize,
|
||||
}) = _SingboxTlsTricks;
|
||||
|
||||
factory SingboxTlsTricks.fromJson(Map<String, dynamic> json) =>
|
||||
_$SingboxTlsTricksFromJson(json);
|
||||
}
|
||||
Executable
+40
@@ -0,0 +1,40 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
import 'package:kaer_with_panels/singbox/model/singbox_proxy_type.dart';
|
||||
|
||||
part 'singbox_outbound.freezed.dart';
|
||||
part 'singbox_outbound.g.dart';
|
||||
|
||||
@freezed
|
||||
class SingboxOutboundGroup with _$SingboxOutboundGroup {
|
||||
@JsonSerializable(fieldRename: FieldRename.kebab)
|
||||
const factory SingboxOutboundGroup({
|
||||
required String tag,
|
||||
@JsonKey(fromJson: _typeFromJson) required ProxyType type,
|
||||
required String selected,
|
||||
@Default([]) List<SingboxOutboundGroupItem> items,
|
||||
}) = _SingboxOutboundGroup;
|
||||
|
||||
factory SingboxOutboundGroup.fromJson(Map<String, dynamic> json) =>
|
||||
_$SingboxOutboundGroupFromJson(json);
|
||||
}
|
||||
|
||||
@freezed
|
||||
class SingboxOutboundGroupItem with _$SingboxOutboundGroupItem {
|
||||
const SingboxOutboundGroupItem._();
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.kebab)
|
||||
const factory SingboxOutboundGroupItem({
|
||||
required String tag,
|
||||
@JsonKey(fromJson: _typeFromJson) required ProxyType type,
|
||||
required int urlTestDelay,
|
||||
}) = _SingboxOutboundGroupItem;
|
||||
|
||||
factory SingboxOutboundGroupItem.fromJson(Map<String, dynamic> json) =>
|
||||
_$SingboxOutboundGroupItemFromJson(json);
|
||||
}
|
||||
|
||||
final Map<String, ProxyType> _keyMap =
|
||||
Map.fromEntries(ProxyType.values.map((e) => MapEntry(e.key, e)));
|
||||
|
||||
ProxyType _typeFromJson(dynamic type) => ProxyType.fromJson(type);
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
enum ProxyType {
|
||||
direct("Direct"),
|
||||
block("Block"),
|
||||
dns("DNS"),
|
||||
socks("SOCKS"),
|
||||
http("HTTP"),
|
||||
shadowsocks("Shadowsocks"),
|
||||
vmess("VMess"),
|
||||
trojan("Trojan"),
|
||||
naive("Naive"),
|
||||
wireguard("WireGuard"),
|
||||
hysteria("Hysteria"),
|
||||
tor("Tor"),
|
||||
ssh("SSH"),
|
||||
shadowtls("ShadowTLS"),
|
||||
shadowsocksr("ShadowsocksR"),
|
||||
vless("VLESS"),
|
||||
tuic("TUIC"),
|
||||
hysteria2("Hysteria2"),
|
||||
|
||||
selector("Selector"),
|
||||
urltest("URLTest"),
|
||||
warp("Warp"),
|
||||
|
||||
xvless("xVLESS"),
|
||||
xvmess("xVMess"),
|
||||
xtrojan("xTrojan"),
|
||||
xfreedom("xFragment"),
|
||||
xshadowsocks("xShadowsocks"),
|
||||
xsocks("xSocks"),
|
||||
invalid("Invalid"),
|
||||
unknown("Unknown");
|
||||
|
||||
const ProxyType(this.label);
|
||||
|
||||
final String label;
|
||||
|
||||
String get key => name;
|
||||
|
||||
static List<ProxyType> groupValues = [selector, urltest];
|
||||
|
||||
bool get isGroup => ProxyType.groupValues.contains(this);
|
||||
static final Map<String, ProxyType> _keyMap = Map.fromEntries(ProxyType.values.map((e) => MapEntry(e.key, e)));
|
||||
static ProxyType fromJson(dynamic type) => _keyMap[(type as String?)?.toLowerCase()] ?? ProxyType.unknown;
|
||||
}
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'singbox_rule.freezed.dart';
|
||||
part 'singbox_rule.g.dart';
|
||||
|
||||
@freezed
|
||||
class SingboxRule with _$SingboxRule {
|
||||
const SingboxRule._();
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.kebab)
|
||||
const factory SingboxRule({
|
||||
String? ruleSetUrl,
|
||||
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);
|
||||
}
|
||||
|
||||
enum RuleOutbound { proxy, bypass, block }
|
||||
|
||||
@JsonEnum(valueField: 'key')
|
||||
enum RuleNetwork {
|
||||
tcpAndUdp(""),
|
||||
tcp("tcp"),
|
||||
udp("udp");
|
||||
|
||||
const RuleNetwork(this.key);
|
||||
|
||||
final String? key;
|
||||
}
|
||||
Executable
+22
@@ -0,0 +1,22 @@
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'singbox_stats.freezed.dart';
|
||||
part 'singbox_stats.g.dart';
|
||||
|
||||
@freezed
|
||||
class SingboxStats with _$SingboxStats {
|
||||
const SingboxStats._();
|
||||
|
||||
@JsonSerializable(fieldRename: FieldRename.kebab)
|
||||
const factory SingboxStats({
|
||||
required int connectionsIn,
|
||||
required int connectionsOut,
|
||||
required int uplink,
|
||||
required int downlink,
|
||||
required int uplinkTotal,
|
||||
required int downlinkTotal,
|
||||
}) = _SingboxStats;
|
||||
|
||||
factory SingboxStats.fromJson(Map<String, dynamic> json) =>
|
||||
_$SingboxStatsFromJson(json);
|
||||
}
|
||||
Executable
+50
@@ -0,0 +1,50 @@
|
||||
import 'package:dartx/dartx.dart';
|
||||
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||
|
||||
part 'singbox_status.freezed.dart';
|
||||
|
||||
@freezed
|
||||
sealed class SingboxStatus with _$SingboxStatus {
|
||||
const SingboxStatus._();
|
||||
|
||||
const factory SingboxStatus.stopped({
|
||||
SingboxAlert? alert,
|
||||
String? message,
|
||||
}) = SingboxStopped;
|
||||
const factory SingboxStatus.starting() = SingboxStarting;
|
||||
const factory SingboxStatus.started() = SingboxStarted;
|
||||
const factory SingboxStatus.stopping() = SingboxStopping;
|
||||
|
||||
factory SingboxStatus.fromEvent(dynamic event) {
|
||||
switch (event) {
|
||||
case {
|
||||
"status": "Stopped",
|
||||
"alert": final String? alertStr,
|
||||
"message": final String? messageStr,
|
||||
}:
|
||||
final alert = SingboxAlert.values.firstOrNullWhere(
|
||||
(e) => alertStr?.toLowerCase() == e.name.toLowerCase(),
|
||||
);
|
||||
return SingboxStatus.stopped(alert: alert, message: messageStr);
|
||||
case {"status": "Stopped"}:
|
||||
return const SingboxStatus.stopped();
|
||||
case {"status": "Starting"}:
|
||||
return const SingboxStarting();
|
||||
case {"status": "Started"}:
|
||||
return const SingboxStarted();
|
||||
case {"status": "Stopping"}:
|
||||
return const SingboxStopping();
|
||||
default:
|
||||
throw Exception("unexpected status [$event]");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum SingboxAlert {
|
||||
requestVPNPermission,
|
||||
requestNotificationPermission,
|
||||
emptyConfiguration,
|
||||
startCommandServer,
|
||||
createService,
|
||||
startService;
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
import 'dart:convert';
|
||||
|
||||
typedef WarpResponse = ({
|
||||
String log,
|
||||
String accountId,
|
||||
String accessToken,
|
||||
String wireguardConfig,
|
||||
});
|
||||
|
||||
WarpResponse warpFromJson(dynamic json) {
|
||||
if (json
|
||||
case {
|
||||
"account-id": final String newAccountId,
|
||||
"access-token": final String newAccessToken,
|
||||
"log": final String log,
|
||||
"config": final Map<String, dynamic> wireguardConfig,
|
||||
}) {
|
||||
return (
|
||||
log: log,
|
||||
accountId: newAccountId,
|
||||
accessToken: newAccessToken,
|
||||
wireguardConfig: jsonEncode(wireguardConfig),
|
||||
);
|
||||
}
|
||||
throw Exception("invalid response");
|
||||
}
|
||||
Reference in New Issue
Block a user