Add warp config generator
This commit is contained in:
@@ -7,13 +7,14 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin
|
|||||||
import io.flutter.plugin.common.MethodCall
|
import io.flutter.plugin.common.MethodCall
|
||||||
import io.flutter.plugin.common.MethodChannel
|
import io.flutter.plugin.common.MethodChannel
|
||||||
import io.nekohasekai.libbox.Libbox
|
import io.nekohasekai.libbox.Libbox
|
||||||
|
import io.nekohasekai.mobile.Mobile
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class MethodHandler(private val scope: CoroutineScope) : FlutterPlugin,
|
class MethodHandler(private val scope: CoroutineScope) : FlutterPlugin,
|
||||||
MethodChannel.MethodCallHandler {
|
MethodChannel.MethodCallHandler {
|
||||||
private var channel: MethodChannel? = null
|
private var channel: MethodChannel? = null
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@@ -30,13 +31,14 @@ class MethodHandler(private val scope: CoroutineScope) : FlutterPlugin,
|
|||||||
SelectOutbound("select_outbound"),
|
SelectOutbound("select_outbound"),
|
||||||
UrlTest("url_test"),
|
UrlTest("url_test"),
|
||||||
ClearLogs("clear_logs"),
|
ClearLogs("clear_logs"),
|
||||||
|
GenerateWarpConfig("generate_warp_config"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
|
||||||
channel = MethodChannel(
|
channel = MethodChannel(
|
||||||
flutterPluginBinding.binaryMessenger,
|
flutterPluginBinding.binaryMessenger,
|
||||||
channelName,
|
channelName,
|
||||||
)
|
)
|
||||||
channel!!.setMethodCallHandler(this)
|
channel!!.setMethodCallHandler(this)
|
||||||
}
|
}
|
||||||
@@ -150,10 +152,10 @@ class MethodHandler(private val scope: CoroutineScope) : FlutterPlugin,
|
|||||||
result.runCatching {
|
result.runCatching {
|
||||||
val args = call.arguments as Map<*, *>
|
val args = call.arguments as Map<*, *>
|
||||||
Libbox.newStandaloneCommandClient()
|
Libbox.newStandaloneCommandClient()
|
||||||
.selectOutbound(
|
.selectOutbound(
|
||||||
args["groupTag"] as String,
|
args["groupTag"] as String,
|
||||||
args["outboundTag"] as String
|
args["outboundTag"] as String
|
||||||
)
|
)
|
||||||
success(true)
|
success(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -164,9 +166,9 @@ class MethodHandler(private val scope: CoroutineScope) : FlutterPlugin,
|
|||||||
result.runCatching {
|
result.runCatching {
|
||||||
val args = call.arguments as Map<*, *>
|
val args = call.arguments as Map<*, *>
|
||||||
Libbox.newStandaloneCommandClient()
|
Libbox.newStandaloneCommandClient()
|
||||||
.urlTest(
|
.urlTest(
|
||||||
args["groupTag"] as String
|
args["groupTag"] as String
|
||||||
)
|
)
|
||||||
success(true)
|
success(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -181,6 +183,20 @@ class MethodHandler(private val scope: CoroutineScope) : FlutterPlugin,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Trigger.GenerateWarpConfig.method -> {
|
||||||
|
scope.launch(Dispatchers.IO) {
|
||||||
|
result.runCatching {
|
||||||
|
val args = call.arguments as Map<*, *>
|
||||||
|
val warpConfig = Mobile.generateWarpConfig(
|
||||||
|
args["license-key"] as String,
|
||||||
|
args["previous-account-id"] as String,
|
||||||
|
args["previous-access-token"] as String,
|
||||||
|
)
|
||||||
|
success(warpConfig)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
else -> result.notImplemented()
|
else -> result.notImplemented()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,6 +237,8 @@
|
|||||||
"title": "Cloudflare WARP Consent",
|
"title": "Cloudflare WARP Consent",
|
||||||
"description(rich)": "Cloudflare WARP is a free WireGuard VPN provider. By enabling this option you are agreeing to the Cloudflare WARP's ${tos(Terms of Service)} and ${privacy(Privacy Policy)}."
|
"description(rich)": "Cloudflare WARP is a free WireGuard VPN provider. By enabling this option you are agreeing to the Cloudflare WARP's ${tos(Terms of Service)} and ${privacy(Privacy Policy)}."
|
||||||
},
|
},
|
||||||
|
"generateWarpConfig": "Generate WARP config",
|
||||||
|
"missingWarpConfig": "Missing WARP config",
|
||||||
"pageTitle": "Config Options",
|
"pageTitle": "Config Options",
|
||||||
"logLevel": "Log Level",
|
"logLevel": "Log Level",
|
||||||
"resolveDestination": "Resolve Destination",
|
"resolveDestination": "Resolve Destination",
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'package:hiddify/core/preferences/preferences_provider.dart';
|
import 'package:hiddify/core/preferences/preferences_provider.dart';
|
||||||
import 'package:hiddify/features/config_option/data/config_option_repository.dart';
|
import 'package:hiddify/features/config_option/data/config_option_repository.dart';
|
||||||
import 'package:hiddify/features/geo_asset/data/geo_asset_data_providers.dart';
|
import 'package:hiddify/features/geo_asset/data/geo_asset_data_providers.dart';
|
||||||
|
import 'package:hiddify/singbox/service/singbox_service_provider.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
|
||||||
part 'config_option_data_providers.g.dart';
|
part 'config_option_data_providers.g.dart';
|
||||||
@@ -11,6 +12,7 @@ ConfigOptionRepository configOptionRepository(
|
|||||||
) {
|
) {
|
||||||
return ConfigOptionRepositoryImpl(
|
return ConfigOptionRepositoryImpl(
|
||||||
preferences: ref.watch(sharedPreferencesProvider).requireValue,
|
preferences: ref.watch(sharedPreferencesProvider).requireValue,
|
||||||
|
singbox: ref.watch(singboxServiceProvider),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ 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/features/geo_asset/data/geo_asset_repository.dart';
|
||||||
import 'package:hiddify/singbox/model/singbox_config_option.dart';
|
import 'package:hiddify/singbox/model/singbox_config_option.dart';
|
||||||
import 'package:hiddify/singbox/model/singbox_rule.dart';
|
import 'package:hiddify/singbox/model/singbox_rule.dart';
|
||||||
|
import 'package:hiddify/singbox/service/singbox_service.dart';
|
||||||
import 'package:hiddify/utils/utils.dart';
|
import 'package:hiddify/utils/utils.dart';
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
import 'package:shared_preferences/shared_preferences.dart';
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
@@ -17,6 +18,7 @@ abstract interface class ConfigOptionRepository {
|
|||||||
ConfigOptionPatch patch,
|
ConfigOptionPatch patch,
|
||||||
);
|
);
|
||||||
TaskEither<ConfigOptionFailure, Unit> resetConfigOption();
|
TaskEither<ConfigOptionFailure, Unit> resetConfigOption();
|
||||||
|
TaskEither<ConfigOptionFailure, Unit> generateWarpConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract interface class SingBoxConfigOptionRepository {
|
abstract interface class SingBoxConfigOptionRepository {
|
||||||
@@ -27,9 +29,13 @@ abstract interface class SingBoxConfigOptionRepository {
|
|||||||
class ConfigOptionRepositoryImpl
|
class ConfigOptionRepositoryImpl
|
||||||
with ExceptionHandler, InfraLogger
|
with ExceptionHandler, InfraLogger
|
||||||
implements ConfigOptionRepository {
|
implements ConfigOptionRepository {
|
||||||
ConfigOptionRepositoryImpl({required this.preferences});
|
ConfigOptionRepositoryImpl({
|
||||||
|
required this.preferences,
|
||||||
|
required this.singbox,
|
||||||
|
});
|
||||||
|
|
||||||
final SharedPreferences preferences;
|
final SharedPreferences preferences;
|
||||||
|
final SingboxService singbox;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Either<ConfigOptionFailure, ConfigOptionEntity> getConfigOption() {
|
Either<ConfigOptionFailure, ConfigOptionEntity> getConfigOption() {
|
||||||
@@ -107,6 +113,35 @@ class ConfigOptionRepositoryImpl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<ConfigOptionFailure, Unit> generateWarpConfig() {
|
||||||
|
return exceptionHandler(
|
||||||
|
() async {
|
||||||
|
final options = getConfigOption().getOrElse((l) => throw l);
|
||||||
|
return await singbox
|
||||||
|
.generateWarpConfig(
|
||||||
|
licenseKey: options.warpLicenseKey,
|
||||||
|
previousAccountId: options.warpAccountId,
|
||||||
|
previousAccessToken: options.warpAccessToken,
|
||||||
|
)
|
||||||
|
.mapLeft((l) => ConfigOptionFailure.unexpected(l))
|
||||||
|
.flatMap(
|
||||||
|
(warp) => updateConfigOption(
|
||||||
|
ConfigOptionPatch(
|
||||||
|
warpAccountId: warp.accountId,
|
||||||
|
warpAccessToken: warp.accessToken,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
(error, stackTrace) {
|
||||||
|
loggy.error(error);
|
||||||
|
return ConfigOptionUnexpectedFailure(error, stackTrace);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SingBoxConfigOptionRepositoryImpl
|
class SingBoxConfigOptionRepositoryImpl
|
||||||
|
|||||||
@@ -61,6 +61,8 @@ class ConfigOptionEntity with _$ConfigOptionEntity {
|
|||||||
@Default(false) bool enableWarp,
|
@Default(false) bool enableWarp,
|
||||||
@Default(WarpDetourMode.outbound) WarpDetourMode warpDetourMode,
|
@Default(WarpDetourMode.outbound) WarpDetourMode warpDetourMode,
|
||||||
@Default("") String warpLicenseKey,
|
@Default("") String warpLicenseKey,
|
||||||
|
@Default("") String warpAccountId,
|
||||||
|
@Default("") String warpAccessToken,
|
||||||
@Default("auto") String warpCleanIp,
|
@Default("auto") String warpCleanIp,
|
||||||
@Default(0) int warpPort,
|
@Default(0) int warpPort,
|
||||||
@OptionalRangeJsonConverter()
|
@OptionalRangeJsonConverter()
|
||||||
@@ -133,6 +135,8 @@ class ConfigOptionEntity with _$ConfigOptionEntity {
|
|||||||
enableWarp: patch.enableWarp ?? enableWarp,
|
enableWarp: patch.enableWarp ?? enableWarp,
|
||||||
warpDetourMode: patch.warpDetourMode ?? warpDetourMode,
|
warpDetourMode: patch.warpDetourMode ?? warpDetourMode,
|
||||||
warpLicenseKey: patch.warpLicenseKey ?? warpLicenseKey,
|
warpLicenseKey: patch.warpLicenseKey ?? warpLicenseKey,
|
||||||
|
warpAccountId: patch.warpAccountId ?? warpAccountId,
|
||||||
|
warpAccessToken: patch.warpAccessToken ?? warpAccessToken,
|
||||||
warpCleanIp: patch.warpCleanIp ?? warpCleanIp,
|
warpCleanIp: patch.warpCleanIp ?? warpCleanIp,
|
||||||
warpPort: patch.warpPort ?? warpPort,
|
warpPort: patch.warpPort ?? warpPort,
|
||||||
warpNoise: patch.warpNoise ?? warpNoise,
|
warpNoise: patch.warpNoise ?? warpNoise,
|
||||||
@@ -183,6 +187,8 @@ class ConfigOptionEntity with _$ConfigOptionEntity {
|
|||||||
enableWarp: enableWarp,
|
enableWarp: enableWarp,
|
||||||
warpDetourMode: warpDetourMode,
|
warpDetourMode: warpDetourMode,
|
||||||
warpLicenseKey: warpLicenseKey,
|
warpLicenseKey: warpLicenseKey,
|
||||||
|
warpAccountId: warpAccountId,
|
||||||
|
warpAccessToken: warpAccessToken,
|
||||||
warpCleanIp: warpCleanIp,
|
warpCleanIp: warpCleanIp,
|
||||||
warpPort: warpPort,
|
warpPort: warpPort,
|
||||||
warpNoise: warpNoise,
|
warpNoise: warpNoise,
|
||||||
@@ -237,6 +243,8 @@ class ConfigOptionPatch with _$ConfigOptionPatch {
|
|||||||
bool? enableWarp,
|
bool? enableWarp,
|
||||||
WarpDetourMode? warpDetourMode,
|
WarpDetourMode? warpDetourMode,
|
||||||
String? warpLicenseKey,
|
String? warpLicenseKey,
|
||||||
|
String? warpAccountId,
|
||||||
|
String? warpAccessToken,
|
||||||
String? warpCleanIp,
|
String? warpCleanIp,
|
||||||
int? warpPort,
|
int? warpPort,
|
||||||
@OptionalRangeJsonConverter() OptionalRange? warpNoise,
|
@OptionalRangeJsonConverter() OptionalRange? warpNoise,
|
||||||
|
|||||||
@@ -14,6 +14,9 @@ sealed class ConfigOptionFailure with _$ConfigOptionFailure, Failure {
|
|||||||
StackTrace? stackTrace,
|
StackTrace? stackTrace,
|
||||||
]) = ConfigOptionUnexpectedFailure;
|
]) = ConfigOptionUnexpectedFailure;
|
||||||
|
|
||||||
|
@With<ExpectedFailure>()
|
||||||
|
const factory ConfigOptionFailure.missingWarp() = MissingWarpConfigFailure;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
({String type, String? message}) present(TranslationsEn t) {
|
({String type, String? message}) present(TranslationsEn t) {
|
||||||
return switch (this) {
|
return switch (this) {
|
||||||
@@ -21,6 +24,10 @@ sealed class ConfigOptionFailure with _$ConfigOptionFailure, Failure {
|
|||||||
type: t.failure.unexpected,
|
type: t.failure.unexpected,
|
||||||
message: null,
|
message: null,
|
||||||
),
|
),
|
||||||
|
MissingWarpConfigFailure() => (
|
||||||
|
type: t.settings.config.missingWarpConfig,
|
||||||
|
message: null,
|
||||||
|
),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,71 @@
|
|||||||
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
import 'package:hiddify/core/preferences/preferences_provider.dart';
|
import 'package:hiddify/core/preferences/preferences_provider.dart';
|
||||||
|
import 'package:hiddify/features/config_option/data/config_option_data_providers.dart';
|
||||||
|
import 'package:hiddify/features/config_option/model/config_option_failure.dart';
|
||||||
|
import 'package:hiddify/utils/utils.dart';
|
||||||
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
import 'package:riverpod_annotation/riverpod_annotation.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
part 'warp_option_notifier.freezed.dart';
|
||||||
part 'warp_option_notifier.g.dart';
|
part 'warp_option_notifier.g.dart';
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
class WarpOptionNotifier extends _$WarpOptionNotifier {
|
class WarpOptionNotifier extends _$WarpOptionNotifier with AppLogger {
|
||||||
@override
|
@override
|
||||||
bool build() {
|
WarpOptions build() {
|
||||||
return ref
|
final consent = _prefs.getBool(warpConsentGiven) ?? false;
|
||||||
.read(sharedPreferencesProvider)
|
bool hasWarpConfig = false;
|
||||||
.requireValue
|
try {
|
||||||
.getBool(warpConsentGiven) ??
|
final accountId = _prefs.getString("warp-account-id");
|
||||||
false;
|
final accessToken = _prefs.getString("warp-access-token");
|
||||||
|
hasWarpConfig = accountId != null && accessToken != null;
|
||||||
|
} catch (e) {
|
||||||
|
loggy.warning(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
return WarpOptions(
|
||||||
|
consentGiven: consent,
|
||||||
|
configGeneration: hasWarpConfig
|
||||||
|
? const AsyncValue.data(unit)
|
||||||
|
: AsyncError(const MissingWarpConfigFailure(), StackTrace.current),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SharedPreferences get _prefs =>
|
||||||
|
ref.read(sharedPreferencesProvider).requireValue;
|
||||||
|
|
||||||
Future<void> agree() async {
|
Future<void> agree() async {
|
||||||
await ref
|
await ref
|
||||||
.read(sharedPreferencesProvider)
|
.read(sharedPreferencesProvider)
|
||||||
.requireValue
|
.requireValue
|
||||||
.setBool(warpConsentGiven, true);
|
.setBool(warpConsentGiven, true);
|
||||||
state = true;
|
state = state.copyWith(consentGiven: true);
|
||||||
|
await generateWarpConfig();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> generateWarpConfig() async {
|
||||||
|
if (state.configGeneration.isLoading) return;
|
||||||
|
state = state.copyWith(configGeneration: const AsyncLoading());
|
||||||
|
final result = await AsyncValue.guard(
|
||||||
|
() async => await ref
|
||||||
|
.read(configOptionRepositoryProvider)
|
||||||
|
.generateWarpConfig()
|
||||||
|
.getOrElse((l) {
|
||||||
|
loggy.warning("error generating warp config: $l", l);
|
||||||
|
throw l;
|
||||||
|
}).run(),
|
||||||
|
);
|
||||||
|
state = state.copyWith(configGeneration: result);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const warpConsentGiven = "warp_consent_given";
|
static const warpConsentGiven = "warp_consent_given";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class WarpOptions with _$WarpOptions {
|
||||||
|
const factory WarpOptions({
|
||||||
|
required bool consentGiven,
|
||||||
|
required AsyncValue<Unit> configGeneration,
|
||||||
|
}) = _WarpOptions;
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ class WarpOptionsTiles extends HookConsumerWidget {
|
|||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final t = ref.watch(translationsProvider);
|
final t = ref.watch(translationsProvider);
|
||||||
|
|
||||||
final warpPrefaceCompleted = ref.watch(warpOptionNotifierProvider);
|
final warpOptions = ref.watch(warpOptionNotifierProvider);
|
||||||
|
final warpPrefaceCompleted = warpOptions.consentGiven;
|
||||||
final canChangeOptions = warpPrefaceCompleted && options.enableWarp;
|
final canChangeOptions = warpPrefaceCompleted && options.enableWarp;
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
@@ -51,6 +52,26 @@ class WarpOptionsTiles extends HookConsumerWidget {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(t.settings.config.generateWarpConfig),
|
||||||
|
subtitle: canChangeOptions
|
||||||
|
? switch (warpOptions.configGeneration) {
|
||||||
|
AsyncLoading() => const LinearProgressIndicator(),
|
||||||
|
AsyncError() => Text(
|
||||||
|
t.settings.config.missingWarpConfig,
|
||||||
|
style:
|
||||||
|
TextStyle(color: Theme.of(context).colorScheme.error),
|
||||||
|
),
|
||||||
|
_ => null,
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
enabled: canChangeOptions,
|
||||||
|
onTap: () async {
|
||||||
|
await ref
|
||||||
|
.read(warpOptionNotifierProvider.notifier)
|
||||||
|
.generateWarpConfig();
|
||||||
|
},
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(t.settings.config.warpDetourMode),
|
title: Text(t.settings.config.warpDetourMode),
|
||||||
subtitle: Text(options.warpDetourMode.present(t)),
|
subtitle: Text(options.warpDetourMode.present(t)),
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -54,6 +54,8 @@ class SingboxConfigOption with _$SingboxConfigOption {
|
|||||||
required bool enableWarp,
|
required bool enableWarp,
|
||||||
required WarpDetourMode warpDetourMode,
|
required WarpDetourMode warpDetourMode,
|
||||||
required String warpLicenseKey,
|
required String warpLicenseKey,
|
||||||
|
required String warpAccountId,
|
||||||
|
required String warpAccessToken,
|
||||||
required String warpCleanIp,
|
required String warpCleanIp,
|
||||||
required int warpPort,
|
required int warpPort,
|
||||||
@OptionalRangeJsonConverter() required OptionalRange warpNoise,
|
@OptionalRangeJsonConverter() required OptionalRange warpNoise,
|
||||||
|
|||||||
17
lib/singbox/model/warp_account.dart
Normal file
17
lib/singbox/model/warp_account.dart
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:freezed_annotation/freezed_annotation.dart';
|
||||||
|
|
||||||
|
part 'warp_account.freezed.dart';
|
||||||
|
part 'warp_account.g.dart';
|
||||||
|
|
||||||
|
@freezed
|
||||||
|
class WarpAccount with _$WarpAccount {
|
||||||
|
const factory WarpAccount({
|
||||||
|
required String licenseKey,
|
||||||
|
required String accountId,
|
||||||
|
required String accessToken,
|
||||||
|
}) = _WarpAccount;
|
||||||
|
|
||||||
|
factory WarpAccount.fromJson(Map<String, dynamic> json) =>
|
||||||
|
_$WarpAccountFromJson(json);
|
||||||
|
}
|
||||||
@@ -13,6 +13,7 @@ import 'package:hiddify/singbox/model/singbox_config_option.dart';
|
|||||||
import 'package:hiddify/singbox/model/singbox_outbound.dart';
|
import 'package:hiddify/singbox/model/singbox_outbound.dart';
|
||||||
import 'package:hiddify/singbox/model/singbox_stats.dart';
|
import 'package:hiddify/singbox/model/singbox_stats.dart';
|
||||||
import 'package:hiddify/singbox/model/singbox_status.dart';
|
import 'package:hiddify/singbox/model/singbox_status.dart';
|
||||||
|
import 'package:hiddify/singbox/model/warp_account.dart';
|
||||||
import 'package:hiddify/singbox/service/singbox_service.dart';
|
import 'package:hiddify/singbox/service/singbox_service.dart';
|
||||||
import 'package:hiddify/utils/utils.dart';
|
import 'package:hiddify/utils/utils.dart';
|
||||||
import 'package:loggy/loggy.dart';
|
import 'package:loggy/loggy.dart';
|
||||||
@@ -454,4 +455,44 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
}
|
}
|
||||||
return _logBuffer;
|
return _logBuffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<String, WarpAccount> generateWarpConfig({
|
||||||
|
required String licenseKey,
|
||||||
|
required String previousAccountId,
|
||||||
|
required String previousAccessToken,
|
||||||
|
}) {
|
||||||
|
loggy.debug("generating warp config");
|
||||||
|
return TaskEither(
|
||||||
|
() => CombineWorker().execute(
|
||||||
|
() {
|
||||||
|
final response = _box
|
||||||
|
.generateWarpConfig(
|
||||||
|
licenseKey.toNativeUtf8().cast(),
|
||||||
|
previousAccountId.toNativeUtf8().cast(),
|
||||||
|
previousAccessToken.toNativeUtf8().cast(),
|
||||||
|
)
|
||||||
|
.cast<Utf8>()
|
||||||
|
.toDartString();
|
||||||
|
if (response.startsWith("error:")) {
|
||||||
|
return left(response.replaceFirst('error:', ""));
|
||||||
|
}
|
||||||
|
if (jsonDecode(response)
|
||||||
|
case {
|
||||||
|
"account-id": final String newAccountId,
|
||||||
|
"access-token": final String newAccessToken,
|
||||||
|
}) {
|
||||||
|
return right(
|
||||||
|
WarpAccount(
|
||||||
|
licenseKey: licenseKey,
|
||||||
|
accountId: newAccountId,
|
||||||
|
accessToken: newAccessToken,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return left("invalid response");
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import 'package:hiddify/singbox/model/singbox_config_option.dart';
|
|||||||
import 'package:hiddify/singbox/model/singbox_outbound.dart';
|
import 'package:hiddify/singbox/model/singbox_outbound.dart';
|
||||||
import 'package:hiddify/singbox/model/singbox_stats.dart';
|
import 'package:hiddify/singbox/model/singbox_stats.dart';
|
||||||
import 'package:hiddify/singbox/model/singbox_status.dart';
|
import 'package:hiddify/singbox/model/singbox_status.dart';
|
||||||
|
import 'package:hiddify/singbox/model/warp_account.dart';
|
||||||
import 'package:hiddify/singbox/service/singbox_service.dart';
|
import 'package:hiddify/singbox/service/singbox_service.dart';
|
||||||
import 'package:hiddify/utils/custom_loggers.dart';
|
import 'package:hiddify/utils/custom_loggers.dart';
|
||||||
import 'package:rxdart/rxdart.dart';
|
import 'package:rxdart/rxdart.dart';
|
||||||
@@ -263,4 +264,39 @@ class PlatformSingboxService with InfraLogger implements SingboxService {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<String, WarpAccount> generateWarpConfig({
|
||||||
|
required String licenseKey,
|
||||||
|
required String previousAccountId,
|
||||||
|
required String previousAccessToken,
|
||||||
|
}) {
|
||||||
|
return TaskEither(
|
||||||
|
() async {
|
||||||
|
loggy.debug("generating warp config");
|
||||||
|
final warpConfig = await methodChannel.invokeMethod(
|
||||||
|
"generate_warp_config",
|
||||||
|
{
|
||||||
|
"license-key": licenseKey,
|
||||||
|
"previous-account-id": previousAccountId,
|
||||||
|
"previous-access-token": previousAccessToken,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (jsonDecode(warpConfig as String)
|
||||||
|
case {
|
||||||
|
"account-id": final String newAccountId,
|
||||||
|
"access-token": final String newAccessToken,
|
||||||
|
}) {
|
||||||
|
return right(
|
||||||
|
WarpAccount(
|
||||||
|
licenseKey: licenseKey,
|
||||||
|
accountId: newAccountId,
|
||||||
|
accessToken: newAccessToken,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return left("invalid response");
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:hiddify/singbox/model/singbox_config_option.dart';
|
|||||||
import 'package:hiddify/singbox/model/singbox_outbound.dart';
|
import 'package:hiddify/singbox/model/singbox_outbound.dart';
|
||||||
import 'package:hiddify/singbox/model/singbox_stats.dart';
|
import 'package:hiddify/singbox/model/singbox_stats.dart';
|
||||||
import 'package:hiddify/singbox/model/singbox_status.dart';
|
import 'package:hiddify/singbox/model/singbox_status.dart';
|
||||||
|
import 'package:hiddify/singbox/model/warp_account.dart';
|
||||||
import 'package:hiddify/singbox/service/ffi_singbox_service.dart';
|
import 'package:hiddify/singbox/service/ffi_singbox_service.dart';
|
||||||
import 'package:hiddify/singbox/service/platform_singbox_service.dart';
|
import 'package:hiddify/singbox/service/platform_singbox_service.dart';
|
||||||
|
|
||||||
@@ -85,4 +86,10 @@ abstract interface class SingboxService {
|
|||||||
Stream<List<String>> watchLogs(String path);
|
Stream<List<String>> watchLogs(String path);
|
||||||
|
|
||||||
TaskEither<String, Unit> clearLogs();
|
TaskEither<String, Unit> clearLogs();
|
||||||
|
|
||||||
|
TaskEither<String, WarpAccount> generateWarpConfig({
|
||||||
|
required String licenseKey,
|
||||||
|
required String previousAccountId,
|
||||||
|
required String previousAccessToken,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
2
libcore
2
libcore
Submodule libcore updated: a006c94cdf...6672cd8104
Reference in New Issue
Block a user