Add service restart
This commit is contained in:
@@ -90,8 +90,7 @@ Future<void> initAppServices(
|
|||||||
_loggy.debug("initializing app services");
|
_loggy.debug("initializing app services");
|
||||||
await Future.wait(
|
await Future.wait(
|
||||||
[
|
[
|
||||||
read(connectivityServiceProvider).init(),
|
read(singboxServiceProvider).init(),
|
||||||
read(notificationServiceProvider).init(),
|
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
_loggy.debug('initialized app services');
|
_loggy.debug('initialized app services');
|
||||||
|
|||||||
@@ -58,6 +58,5 @@ CoreFacade coreFacade(CoreFacadeRef ref) => CoreFacadeImpl(
|
|||||||
ref.watch(singboxServiceProvider),
|
ref.watch(singboxServiceProvider),
|
||||||
ref.watch(filesEditorServiceProvider),
|
ref.watch(filesEditorServiceProvider),
|
||||||
ref.watch(clashApiProvider),
|
ref.watch(clashApiProvider),
|
||||||
ref.watch(connectivityServiceProvider),
|
|
||||||
() => ref.read(configOptionsProvider),
|
() => ref.read(configOptionsProvider),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import 'package:hiddify/domain/constants.dart';
|
|||||||
import 'package:hiddify/domain/core_facade.dart';
|
import 'package:hiddify/domain/core_facade.dart';
|
||||||
import 'package:hiddify/domain/core_service_failure.dart';
|
import 'package:hiddify/domain/core_service_failure.dart';
|
||||||
import 'package:hiddify/domain/singbox/singbox.dart';
|
import 'package:hiddify/domain/singbox/singbox.dart';
|
||||||
import 'package:hiddify/services/connectivity/connectivity.dart';
|
|
||||||
import 'package:hiddify/services/files_editor_service.dart';
|
import 'package:hiddify/services/files_editor_service.dart';
|
||||||
import 'package:hiddify/services/singbox/singbox_service.dart';
|
import 'package:hiddify/services/singbox/singbox_service.dart';
|
||||||
import 'package:hiddify/utils/utils.dart';
|
import 'package:hiddify/utils/utils.dart';
|
||||||
@@ -19,14 +18,12 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade {
|
|||||||
this.singbox,
|
this.singbox,
|
||||||
this.filesEditor,
|
this.filesEditor,
|
||||||
this.clash,
|
this.clash,
|
||||||
this.connectivity,
|
|
||||||
this.configOptions,
|
this.configOptions,
|
||||||
);
|
);
|
||||||
|
|
||||||
final SingboxService singbox;
|
final SingboxService singbox;
|
||||||
final FilesEditorService filesEditor;
|
final FilesEditorService filesEditor;
|
||||||
final ClashApi clash;
|
final ClashApi clash;
|
||||||
final ConnectivityService connectivity;
|
|
||||||
final ConfigOptions Function() configOptions;
|
final ConfigOptions Function() configOptions;
|
||||||
|
|
||||||
bool _initialized = false;
|
bool _initialized = false;
|
||||||
@@ -89,16 +86,14 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TaskEither<CoreServiceFailure, Unit> changeConfig(String fileName) {
|
TaskEither<CoreServiceFailure, Unit> start(String fileName) {
|
||||||
return exceptionHandler(
|
return exceptionHandler(
|
||||||
() {
|
() {
|
||||||
final configPath = filesEditor.configPath(fileName);
|
final configPath = filesEditor.configPath(fileName);
|
||||||
loggy.debug("changing config to: $configPath");
|
|
||||||
return setup()
|
return setup()
|
||||||
.andThen(() => changeConfigOptions(configOptions()))
|
.andThen(() => changeConfigOptions(configOptions()))
|
||||||
.andThen(
|
.andThen(
|
||||||
() =>
|
() => singbox.start(configPath).mapLeft(CoreServiceFailure.start),
|
||||||
singbox.create(configPath).mapLeft(CoreServiceFailure.create),
|
|
||||||
)
|
)
|
||||||
.run();
|
.run();
|
||||||
},
|
},
|
||||||
@@ -107,17 +102,25 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TaskEither<CoreServiceFailure, Unit> start() {
|
TaskEither<CoreServiceFailure, Unit> stop() {
|
||||||
return exceptionHandler(
|
return exceptionHandler(
|
||||||
() => singbox.start().mapLeft(CoreServiceFailure.start).run(),
|
() => singbox.stop().mapLeft(CoreServiceFailure.other).run(),
|
||||||
CoreServiceFailure.unexpected,
|
CoreServiceFailure.unexpected,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TaskEither<CoreServiceFailure, Unit> stop() {
|
TaskEither<CoreServiceFailure, Unit> restart(String fileName) {
|
||||||
return exceptionHandler(
|
return exceptionHandler(
|
||||||
() => singbox.stop().mapLeft(CoreServiceFailure.other).run(),
|
() {
|
||||||
|
final configPath = filesEditor.configPath(fileName);
|
||||||
|
return changeConfigOptions(configOptions())
|
||||||
|
.andThen(
|
||||||
|
() =>
|
||||||
|
singbox.restart(configPath).mapLeft(CoreServiceFailure.start),
|
||||||
|
)
|
||||||
|
.run();
|
||||||
|
},
|
||||||
CoreServiceFailure.unexpected,
|
CoreServiceFailure.unexpected,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -160,7 +163,7 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<Either<CoreServiceFailure, CoreStatus>> watchCoreStatus() {
|
Stream<Either<CoreServiceFailure, CoreStatus>> watchCoreStatus() {
|
||||||
return singbox.watchStatus().map((event) {
|
return singbox.watchStats().map((event) {
|
||||||
final json = jsonDecode(event);
|
final json = jsonDecode(event);
|
||||||
return CoreStatus.fromJson(json as Map<String, dynamic>);
|
return CoreStatus.fromJson(json as Map<String, dynamic>);
|
||||||
}).handleExceptions(
|
}).handleExceptions(
|
||||||
@@ -239,29 +242,7 @@ class CoreFacadeImpl with ExceptionHandler, InfraLogger implements CoreFacade {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
|
||||||
TaskEither<CoreServiceFailure, Unit> connect() {
|
|
||||||
return exceptionHandler(
|
|
||||||
() async {
|
|
||||||
await connectivity.connect();
|
|
||||||
return right(unit);
|
|
||||||
},
|
|
||||||
CoreServiceFailure.unexpected,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
TaskEither<CoreServiceFailure, Unit> disconnect() {
|
|
||||||
return exceptionHandler(
|
|
||||||
() async {
|
|
||||||
await connectivity.disconnect();
|
|
||||||
return right(unit);
|
|
||||||
},
|
|
||||||
CoreServiceFailure.unexpected,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<ConnectionStatus> watchConnectionStatus() =>
|
Stream<ConnectionStatus> watchConnectionStatus() =>
|
||||||
connectivity.watchConnectionStatus();
|
singbox.watchConnectionStatus();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +0,0 @@
|
|||||||
import 'package:fpdart/fpdart.dart';
|
|
||||||
import 'package:hiddify/domain/connectivity/connection_status.dart';
|
|
||||||
import 'package:hiddify/domain/core_service_failure.dart';
|
|
||||||
|
|
||||||
abstract interface class ConnectionFacade {
|
|
||||||
TaskEither<CoreServiceFailure, Unit> connect();
|
|
||||||
|
|
||||||
TaskEither<CoreServiceFailure, Unit> disconnect();
|
|
||||||
|
|
||||||
Stream<ConnectionStatus> watchConnectionStatus();
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
import 'package:hiddify/domain/clash/clash.dart';
|
import 'package:hiddify/domain/clash/clash.dart';
|
||||||
import 'package:hiddify/domain/connectivity/connectivity.dart';
|
|
||||||
import 'package:hiddify/domain/singbox/singbox.dart';
|
import 'package:hiddify/domain/singbox/singbox.dart';
|
||||||
|
|
||||||
abstract interface class CoreFacade
|
abstract interface class CoreFacade implements SingboxFacade, ClashFacade {}
|
||||||
implements SingboxFacade, ClashFacade, ConnectionFacade {}
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'package:fpdart/fpdart.dart';
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
import 'package:hiddify/domain/connectivity/connectivity.dart';
|
||||||
import 'package:hiddify/domain/core_service_failure.dart';
|
import 'package:hiddify/domain/core_service_failure.dart';
|
||||||
import 'package:hiddify/domain/singbox/config_options.dart';
|
import 'package:hiddify/domain/singbox/config_options.dart';
|
||||||
import 'package:hiddify/domain/singbox/core_status.dart';
|
import 'package:hiddify/domain/singbox/core_status.dart';
|
||||||
@@ -13,12 +14,12 @@ abstract interface class SingboxFacade {
|
|||||||
ConfigOptions options,
|
ConfigOptions options,
|
||||||
);
|
);
|
||||||
|
|
||||||
TaskEither<CoreServiceFailure, Unit> changeConfig(String fileName);
|
TaskEither<CoreServiceFailure, Unit> start(String fileName);
|
||||||
|
|
||||||
TaskEither<CoreServiceFailure, Unit> start();
|
|
||||||
|
|
||||||
TaskEither<CoreServiceFailure, Unit> stop();
|
TaskEither<CoreServiceFailure, Unit> stop();
|
||||||
|
|
||||||
|
TaskEither<CoreServiceFailure, Unit> restart(String fileName);
|
||||||
|
|
||||||
Stream<Either<CoreServiceFailure, List<OutboundGroup>>> watchOutbounds();
|
Stream<Either<CoreServiceFailure, List<OutboundGroup>>> watchOutbounds();
|
||||||
|
|
||||||
TaskEither<CoreServiceFailure, Unit> selectOutbound(
|
TaskEither<CoreServiceFailure, Unit> selectOutbound(
|
||||||
@@ -28,6 +29,8 @@ abstract interface class SingboxFacade {
|
|||||||
|
|
||||||
TaskEither<CoreServiceFailure, Unit> urlTest(String groupTag);
|
TaskEither<CoreServiceFailure, Unit> urlTest(String groupTag);
|
||||||
|
|
||||||
|
Stream<ConnectionStatus> watchConnectionStatus();
|
||||||
|
|
||||||
Stream<Either<CoreServiceFailure, CoreStatus>> watchCoreStatus();
|
Stream<Either<CoreServiceFailure, CoreStatus>> watchCoreStatus();
|
||||||
|
|
||||||
Stream<Either<CoreServiceFailure, String>> watchLogs();
|
Stream<Either<CoreServiceFailure, String>> watchLogs();
|
||||||
|
|||||||
@@ -17,15 +17,14 @@ class ConnectivityController extends _$ConnectivityController with AppLogger {
|
|||||||
if (previous == null) return;
|
if (previous == null) return;
|
||||||
final shouldReconnect = previous != next;
|
final shouldReconnect = previous != next;
|
||||||
if (shouldReconnect) {
|
if (shouldReconnect) {
|
||||||
loggy.debug("active profile modified, reconnect");
|
await reconnect(next?.id);
|
||||||
await reconnect();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return _connectivity.watchConnectionStatus();
|
return _core.watchConnectionStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
CoreFacade get _connectivity => ref.watch(coreFacadeProvider);
|
CoreFacade get _core => ref.watch(coreFacadeProvider);
|
||||||
|
|
||||||
Future<void> toggleConnection() async {
|
Future<void> toggleConnection() async {
|
||||||
if (state case AsyncError()) {
|
if (state case AsyncError()) {
|
||||||
@@ -42,13 +41,16 @@ class ConnectivityController extends _$ConnectivityController with AppLogger {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> reconnect() async {
|
Future<void> reconnect(String? profileId) async {
|
||||||
if (state case AsyncData(:final value)) {
|
if (state case AsyncData(:final value) when value == const Connected()) {
|
||||||
if (value case Connected()) {
|
if (profileId == null) {
|
||||||
loggy.debug("reconnecting");
|
return _disconnect();
|
||||||
await _disconnect();
|
|
||||||
await _connect();
|
|
||||||
}
|
}
|
||||||
|
loggy.debug("reconnecting, profile: [$profileId]");
|
||||||
|
await _core.restart(profileId).mapLeft((l) {
|
||||||
|
loggy.warning("error reconnecting: $l");
|
||||||
|
state = AsyncError(l, StackTrace.current);
|
||||||
|
}).run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,17 +67,14 @@ class ConnectivityController extends _$ConnectivityController with AppLogger {
|
|||||||
|
|
||||||
Future<void> _connect() async {
|
Future<void> _connect() async {
|
||||||
final activeProfile = await ref.read(activeProfileProvider.future);
|
final activeProfile = await ref.read(activeProfileProvider.future);
|
||||||
await _connectivity
|
await _core.start(activeProfile!.id).mapLeft((l) {
|
||||||
.changeConfig(activeProfile!.id)
|
|
||||||
.andThen(_connectivity.connect)
|
|
||||||
.mapLeft((l) {
|
|
||||||
loggy.warning("error connecting: $l");
|
loggy.warning("error connecting: $l");
|
||||||
state = AsyncError(l, StackTrace.current);
|
state = AsyncError(l, StackTrace.current);
|
||||||
}).run();
|
}).run();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _disconnect() async {
|
Future<void> _disconnect() async {
|
||||||
await _connectivity.disconnect().mapLeft((l) {
|
await _core.stop().mapLeft((l) {
|
||||||
loggy.warning("error disconnecting: $l");
|
loggy.warning("error disconnecting: $l");
|
||||||
state = AsyncError(l, StackTrace.current);
|
state = AsyncError(l, StackTrace.current);
|
||||||
}).run();
|
}).run();
|
||||||
|
|||||||
@@ -875,21 +875,23 @@ class SingboxNativeLibrary {
|
|||||||
ffi.Pointer<ffi.Char> baseDir,
|
ffi.Pointer<ffi.Char> baseDir,
|
||||||
ffi.Pointer<ffi.Char> workingDir,
|
ffi.Pointer<ffi.Char> workingDir,
|
||||||
ffi.Pointer<ffi.Char> tempDir,
|
ffi.Pointer<ffi.Char> tempDir,
|
||||||
|
int statusPort,
|
||||||
) {
|
) {
|
||||||
return _setup(
|
return _setup(
|
||||||
baseDir,
|
baseDir,
|
||||||
workingDir,
|
workingDir,
|
||||||
tempDir,
|
tempDir,
|
||||||
|
statusPort,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
late final _setupPtr = _lookup<
|
late final _setupPtr = _lookup<
|
||||||
ffi.NativeFunction<
|
ffi.NativeFunction<
|
||||||
ffi.Void Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>,
|
ffi.Void Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>,
|
||||||
ffi.Pointer<ffi.Char>)>>('setup');
|
ffi.Pointer<ffi.Char>, ffi.LongLong)>>('setup');
|
||||||
late final _setup = _setupPtr.asFunction<
|
late final _setup = _setupPtr.asFunction<
|
||||||
void Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>,
|
void Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>,
|
||||||
ffi.Pointer<ffi.Char>)>();
|
ffi.Pointer<ffi.Char>, int)>();
|
||||||
|
|
||||||
ffi.Pointer<ffi.Char> parse(
|
ffi.Pointer<ffi.Char> parse(
|
||||||
ffi.Pointer<ffi.Char> path,
|
ffi.Pointer<ffi.Char> path,
|
||||||
@@ -920,28 +922,20 @@ class SingboxNativeLibrary {
|
|||||||
late final _changeConfigOptions = _changeConfigOptionsPtr
|
late final _changeConfigOptions = _changeConfigOptionsPtr
|
||||||
.asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>();
|
.asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>();
|
||||||
|
|
||||||
ffi.Pointer<ffi.Char> create(
|
ffi.Pointer<ffi.Char> start(
|
||||||
ffi.Pointer<ffi.Char> configPath,
|
ffi.Pointer<ffi.Char> configPath,
|
||||||
) {
|
) {
|
||||||
return _create(
|
return _start(
|
||||||
configPath,
|
configPath,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
late final _createPtr = _lookup<
|
late final _startPtr = _lookup<
|
||||||
ffi.NativeFunction<
|
ffi.NativeFunction<
|
||||||
ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>>('create');
|
ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>>('start');
|
||||||
late final _create = _createPtr
|
late final _start = _startPtr
|
||||||
.asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>();
|
.asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>();
|
||||||
|
|
||||||
ffi.Pointer<ffi.Char> start() {
|
|
||||||
return _start();
|
|
||||||
}
|
|
||||||
|
|
||||||
late final _startPtr =
|
|
||||||
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>('start');
|
|
||||||
late final _start = _startPtr.asFunction<ffi.Pointer<ffi.Char> Function()>();
|
|
||||||
|
|
||||||
ffi.Pointer<ffi.Char> stop() {
|
ffi.Pointer<ffi.Char> stop() {
|
||||||
return _stop();
|
return _stop();
|
||||||
}
|
}
|
||||||
@@ -950,6 +944,20 @@ class SingboxNativeLibrary {
|
|||||||
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>('stop');
|
_lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>('stop');
|
||||||
late final _stop = _stopPtr.asFunction<ffi.Pointer<ffi.Char> Function()>();
|
late final _stop = _stopPtr.asFunction<ffi.Pointer<ffi.Char> Function()>();
|
||||||
|
|
||||||
|
ffi.Pointer<ffi.Char> restart(
|
||||||
|
ffi.Pointer<ffi.Char> configPath,
|
||||||
|
) {
|
||||||
|
return _restart(
|
||||||
|
configPath,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
late final _restartPtr = _lookup<
|
||||||
|
ffi.NativeFunction<
|
||||||
|
ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>>('restart');
|
||||||
|
late final _restart = _restartPtr
|
||||||
|
.asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>();
|
||||||
|
|
||||||
ffi.Pointer<ffi.Char> startCommandClient(
|
ffi.Pointer<ffi.Char> startCommandClient(
|
||||||
int command,
|
int command,
|
||||||
int port,
|
int port,
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
export 'connectivity_service.dart';
|
|
||||||
export 'desktop_connectivity_service.dart';
|
|
||||||
export 'mobile_connectivity_service.dart';
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
import 'package:hiddify/domain/connectivity/connectivity.dart';
|
|
||||||
import 'package:hiddify/services/connectivity/desktop_connectivity_service.dart';
|
|
||||||
import 'package:hiddify/services/connectivity/mobile_connectivity_service.dart';
|
|
||||||
import 'package:hiddify/services/notification/notification.dart';
|
|
||||||
import 'package:hiddify/services/singbox/singbox_service.dart';
|
|
||||||
import 'package:hiddify/utils/utils.dart';
|
|
||||||
|
|
||||||
abstract class ConnectivityService {
|
|
||||||
factory ConnectivityService(
|
|
||||||
SingboxService singboxService,
|
|
||||||
NotificationService notificationService,
|
|
||||||
) {
|
|
||||||
if (PlatformUtils.isDesktop) {
|
|
||||||
return DesktopConnectivityService(singboxService);
|
|
||||||
}
|
|
||||||
return MobileConnectivityService(singboxService, notificationService);
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> init();
|
|
||||||
|
|
||||||
Stream<ConnectionStatus> watchConnectionStatus();
|
|
||||||
|
|
||||||
Future<void> connect();
|
|
||||||
|
|
||||||
Future<void> disconnect();
|
|
||||||
}
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
import 'package:hiddify/domain/connectivity/connectivity.dart';
|
|
||||||
import 'package:hiddify/domain/core_service_failure.dart';
|
|
||||||
import 'package:hiddify/services/connectivity/connectivity_service.dart';
|
|
||||||
import 'package:hiddify/services/singbox/singbox_service.dart';
|
|
||||||
import 'package:hiddify/utils/utils.dart';
|
|
||||||
import 'package:rxdart/rxdart.dart';
|
|
||||||
|
|
||||||
class DesktopConnectivityService
|
|
||||||
with InfraLogger
|
|
||||||
implements ConnectivityService {
|
|
||||||
DesktopConnectivityService(this._singboxService);
|
|
||||||
|
|
||||||
final SingboxService _singboxService;
|
|
||||||
|
|
||||||
late final BehaviorSubject<ConnectionStatus> _connectionStatus;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> init() async {
|
|
||||||
loggy.debug("initializing");
|
|
||||||
_connectionStatus =
|
|
||||||
BehaviorSubject.seeded(const ConnectionStatus.disconnected());
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<ConnectionStatus> watchConnectionStatus() => _connectionStatus;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> connect() async {
|
|
||||||
loggy.debug('connecting');
|
|
||||||
_connectionStatus.value = const ConnectionStatus.connecting();
|
|
||||||
await _singboxService.start().match(
|
|
||||||
(err) {
|
|
||||||
_connectionStatus.value = ConnectionStatus.disconnected(
|
|
||||||
CoreConnectionFailure(
|
|
||||||
CoreServiceStartFailure(err),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
},
|
|
||||||
(_) => _connectionStatus.value = const ConnectionStatus.connected(),
|
|
||||||
).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> disconnect() async {
|
|
||||||
loggy.debug("disconnecting");
|
|
||||||
_connectionStatus.value = const ConnectionStatus.disconnecting();
|
|
||||||
await _singboxService.stop().getOrElse((l) {
|
|
||||||
_connectionStatus.value = const ConnectionStatus.connected();
|
|
||||||
throw l;
|
|
||||||
}).run();
|
|
||||||
_connectionStatus.value = const ConnectionStatus.disconnected();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:hiddify/domain/connectivity/connectivity.dart';
|
|
||||||
import 'package:hiddify/domain/core_service_failure.dart';
|
|
||||||
import 'package:hiddify/services/connectivity/connectivity_service.dart';
|
|
||||||
import 'package:hiddify/services/notification/notification.dart';
|
|
||||||
import 'package:hiddify/services/singbox/singbox_service.dart';
|
|
||||||
import 'package:hiddify/utils/utils.dart';
|
|
||||||
import 'package:rxdart/rxdart.dart';
|
|
||||||
|
|
||||||
// TODO: rewrite
|
|
||||||
class MobileConnectivityService
|
|
||||||
with InfraLogger
|
|
||||||
implements ConnectivityService {
|
|
||||||
MobileConnectivityService(this.singbox, this.notifications);
|
|
||||||
|
|
||||||
final SingboxService singbox;
|
|
||||||
final NotificationService notifications;
|
|
||||||
|
|
||||||
late final EventChannel _statusChannel;
|
|
||||||
late final EventChannel _alertsChannel;
|
|
||||||
late final ValueStream<ConnectionStatus> _connectionStatus;
|
|
||||||
|
|
||||||
static CoreServiceFailure fromServiceAlert(String key, String? message) {
|
|
||||||
return switch (key) {
|
|
||||||
"EmptyConfiguration" => InvalidConfig(message),
|
|
||||||
"StartCommandServer" ||
|
|
||||||
"CreateService" =>
|
|
||||||
CoreServiceCreateFailure(message),
|
|
||||||
"StartService" => CoreServiceStartFailure(message),
|
|
||||||
_ => const CoreServiceOtherFailure(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
static ConnectionStatus fromServiceEvent(dynamic event) {
|
|
||||||
final status = event['status'] as String;
|
|
||||||
late ConnectionStatus connectionStatus;
|
|
||||||
switch (status) {
|
|
||||||
case "Stopped":
|
|
||||||
final failure = event["failure"] as String?;
|
|
||||||
final message = event["message"] as String?;
|
|
||||||
connectionStatus = ConnectionStatus.disconnected(
|
|
||||||
switch (failure) {
|
|
||||||
null => null,
|
|
||||||
"RequestVPNPermission" => MissingVpnPermission(message),
|
|
||||||
"RequestNotificationPermission" =>
|
|
||||||
MissingNotificationPermission(message),
|
|
||||||
"EmptyConfiguration" ||
|
|
||||||
"StartCommandServer" ||
|
|
||||||
"CreateService" ||
|
|
||||||
"StartService" =>
|
|
||||||
CoreConnectionFailure(fromServiceAlert(failure, message)),
|
|
||||||
_ => const UnexpectedConnectionFailure(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
case "Starting":
|
|
||||||
connectionStatus = const Connecting();
|
|
||||||
case "Started":
|
|
||||||
connectionStatus = const Connected();
|
|
||||||
case "Stopping":
|
|
||||||
connectionStatus = const Disconnecting();
|
|
||||||
}
|
|
||||||
return connectionStatus;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> init() async {
|
|
||||||
loggy.debug("initializing");
|
|
||||||
_statusChannel = const EventChannel("com.hiddify.app/service.status");
|
|
||||||
_alertsChannel = const EventChannel("com.hiddify.app/service.alerts");
|
|
||||||
final status =
|
|
||||||
_statusChannel.receiveBroadcastStream().map(fromServiceEvent);
|
|
||||||
final alerts =
|
|
||||||
_alertsChannel.receiveBroadcastStream().map(fromServiceEvent);
|
|
||||||
_connectionStatus =
|
|
||||||
ValueConnectableStream(Rx.merge([status, alerts])).autoConnect();
|
|
||||||
await _connectionStatus.first;
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Stream<ConnectionStatus> watchConnectionStatus() => _connectionStatus;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> connect() async {
|
|
||||||
loggy.debug("connecting");
|
|
||||||
await notifications.grantPermission();
|
|
||||||
await singbox.start().getOrElse((l) => throw l).run();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Future<void> disconnect() async {
|
|
||||||
loggy.debug("disconnecting");
|
|
||||||
await singbox.stop().getOrElse((l) => throw l).run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
import 'package:hiddify/services/connectivity/connectivity.dart';
|
|
||||||
import 'package:hiddify/services/files_editor_service.dart';
|
import 'package:hiddify/services/files_editor_service.dart';
|
||||||
import 'package:hiddify/services/notification/notification.dart';
|
|
||||||
import 'package:hiddify/services/platform_settings.dart';
|
import 'package:hiddify/services/platform_settings.dart';
|
||||||
import 'package:hiddify/services/runtime_details_service.dart';
|
import 'package:hiddify/services/runtime_details_service.dart';
|
||||||
import 'package:hiddify/services/singbox/singbox_service.dart';
|
import 'package:hiddify/services/singbox/singbox_service.dart';
|
||||||
@@ -8,10 +6,6 @@ import 'package:riverpod_annotation/riverpod_annotation.dart';
|
|||||||
|
|
||||||
part 'service_providers.g.dart';
|
part 'service_providers.g.dart';
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
|
||||||
NotificationService notificationService(NotificationServiceRef ref) =>
|
|
||||||
NotificationService();
|
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
FilesEditorService filesEditorService(FilesEditorServiceRef ref) =>
|
FilesEditorService filesEditorService(FilesEditorServiceRef ref) =>
|
||||||
FilesEditorService();
|
FilesEditorService();
|
||||||
@@ -23,13 +17,6 @@ RuntimeDetailsService runtimeDetailsService(RuntimeDetailsServiceRef ref) =>
|
|||||||
@Riverpod(keepAlive: true)
|
@Riverpod(keepAlive: true)
|
||||||
SingboxService singboxService(SingboxServiceRef ref) => SingboxService();
|
SingboxService singboxService(SingboxServiceRef ref) => SingboxService();
|
||||||
|
|
||||||
@Riverpod(keepAlive: true)
|
|
||||||
ConnectivityService connectivityService(ConnectivityServiceRef ref) =>
|
|
||||||
ConnectivityService(
|
|
||||||
ref.watch(singboxServiceProvider),
|
|
||||||
ref.watch(notificationServiceProvider),
|
|
||||||
);
|
|
||||||
|
|
||||||
@riverpod
|
@riverpod
|
||||||
PlatformSettings platformSettings(PlatformSettingsRef ref) =>
|
PlatformSettings platformSettings(PlatformSettingsRef ref) =>
|
||||||
PlatformSettings();
|
PlatformSettings();
|
||||||
|
|||||||
@@ -7,18 +7,25 @@ import 'dart:isolate';
|
|||||||
import 'package:combine/combine.dart';
|
import 'package:combine/combine.dart';
|
||||||
import 'package:ffi/ffi.dart';
|
import 'package:ffi/ffi.dart';
|
||||||
import 'package:fpdart/fpdart.dart';
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
import 'package:hiddify/domain/connectivity/connectivity.dart';
|
||||||
import 'package:hiddify/domain/singbox/config_options.dart';
|
import 'package:hiddify/domain/singbox/config_options.dart';
|
||||||
import 'package:hiddify/gen/singbox_generated_bindings.dart';
|
import 'package:hiddify/gen/singbox_generated_bindings.dart';
|
||||||
|
import 'package:hiddify/services/singbox/shared.dart';
|
||||||
import 'package:hiddify/services/singbox/singbox_service.dart';
|
import 'package:hiddify/services/singbox/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';
|
||||||
import 'package:path/path.dart' as p;
|
import 'package:path/path.dart' as p;
|
||||||
|
import 'package:rxdart/rxdart.dart';
|
||||||
|
|
||||||
final _logger = Loggy('FFISingboxService');
|
final _logger = Loggy('FFISingboxService');
|
||||||
|
|
||||||
class FFISingboxService with InfraLogger implements SingboxService {
|
class FFISingboxService
|
||||||
|
with ServiceStatus, InfraLogger
|
||||||
|
implements SingboxService {
|
||||||
static final SingboxNativeLibrary _box = _gen();
|
static final SingboxNativeLibrary _box = _gen();
|
||||||
|
|
||||||
|
late final ValueStream<ConnectionStatus> _connectionStatus;
|
||||||
|
late final ReceivePort _connectionStatusReceiver;
|
||||||
Stream<String>? _statusStream;
|
Stream<String>? _statusStream;
|
||||||
Stream<String>? _groupsStream;
|
Stream<String>? _groupsStream;
|
||||||
|
|
||||||
@@ -39,21 +46,37 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
return SingboxNativeLibrary(lib);
|
return SingboxNativeLibrary(lib);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> init() async {
|
||||||
|
loggy.debug("initializing");
|
||||||
|
_connectionStatusReceiver = ReceivePort('service status receiver');
|
||||||
|
final source = _connectionStatusReceiver
|
||||||
|
.asBroadcastStream()
|
||||||
|
.map((event) => jsonDecode(event as String) as Map<String, dynamic>)
|
||||||
|
.map(mapEventToStatus);
|
||||||
|
_connectionStatus = ValueConnectableStream.seeded(
|
||||||
|
source,
|
||||||
|
const ConnectionStatus.disconnected(),
|
||||||
|
).autoConnect();
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TaskEither<String, Unit> setup(
|
TaskEither<String, Unit> setup(
|
||||||
String baseDir,
|
String baseDir,
|
||||||
String workingDir,
|
String workingDir,
|
||||||
String tempDir,
|
String tempDir,
|
||||||
) {
|
) {
|
||||||
|
final port = _connectionStatusReceiver.sendPort.nativePort;
|
||||||
return TaskEither(
|
return TaskEither(
|
||||||
() => CombineWorker().execute(
|
() => CombineWorker().execute(
|
||||||
() {
|
() {
|
||||||
|
_box.setupOnce(NativeApi.initializeApiDLData);
|
||||||
_box.setup(
|
_box.setup(
|
||||||
baseDir.toNativeUtf8().cast(),
|
baseDir.toNativeUtf8().cast(),
|
||||||
workingDir.toNativeUtf8().cast(),
|
workingDir.toNativeUtf8().cast(),
|
||||||
tempDir.toNativeUtf8().cast(),
|
tempDir.toNativeUtf8().cast(),
|
||||||
|
port,
|
||||||
);
|
);
|
||||||
_box.setupOnce(NativeApi.initializeApiDLData);
|
|
||||||
return right(unit);
|
return right(unit);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -98,29 +121,14 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TaskEither<String, Unit> create(String configPath) {
|
TaskEither<String, Unit> start(String configPath) {
|
||||||
return TaskEither(
|
|
||||||
() => CombineWorker().execute(
|
|
||||||
() async {
|
|
||||||
final err = _box
|
|
||||||
.create(configPath.toNativeUtf8().cast())
|
|
||||||
.cast<Utf8>()
|
|
||||||
.toDartString();
|
|
||||||
if (err.isNotEmpty) {
|
|
||||||
return left(err);
|
|
||||||
}
|
|
||||||
return right(unit);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
TaskEither<String, Unit> start() {
|
|
||||||
return TaskEither(
|
return TaskEither(
|
||||||
() => CombineWorker().execute(
|
() => CombineWorker().execute(
|
||||||
() {
|
() {
|
||||||
final err = _box.start().cast<Utf8>().toDartString();
|
final err = _box
|
||||||
|
.start(configPath.toNativeUtf8().cast())
|
||||||
|
.cast<Utf8>()
|
||||||
|
.toDartString();
|
||||||
if (err.isNotEmpty) {
|
if (err.isNotEmpty) {
|
||||||
return left(err);
|
return left(err);
|
||||||
}
|
}
|
||||||
@@ -146,7 +154,28 @@ class FFISingboxService with InfraLogger implements SingboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<String> watchStatus() {
|
TaskEither<String, Unit> restart(String configPath) {
|
||||||
|
return TaskEither(
|
||||||
|
() => CombineWorker().execute(
|
||||||
|
() {
|
||||||
|
final err = _box
|
||||||
|
.restart(configPath.toNativeUtf8().cast())
|
||||||
|
.cast<Utf8>()
|
||||||
|
.toDartString();
|
||||||
|
if (err.isNotEmpty) {
|
||||||
|
return left(err);
|
||||||
|
}
|
||||||
|
return right(unit);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<ConnectionStatus> watchConnectionStatus() => _connectionStatus;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<String> watchStats() {
|
||||||
if (_statusStream != null) return _statusStream!;
|
if (_statusStream != null) return _statusStream!;
|
||||||
final receiver = ReceivePort('status receiver');
|
final receiver = ReceivePort('status receiver');
|
||||||
final statusStream = receiver.asBroadcastStream(
|
final statusStream = receiver.asBroadcastStream(
|
||||||
|
|||||||
@@ -2,15 +2,36 @@ import 'dart:convert';
|
|||||||
|
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:fpdart/fpdart.dart';
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
import 'package:hiddify/domain/connectivity/connection_status.dart';
|
||||||
import 'package:hiddify/domain/singbox/config_options.dart';
|
import 'package:hiddify/domain/singbox/config_options.dart';
|
||||||
|
import 'package:hiddify/services/singbox/shared.dart';
|
||||||
import 'package:hiddify/services/singbox/singbox_service.dart';
|
import 'package:hiddify/services/singbox/singbox_service.dart';
|
||||||
import 'package:hiddify/utils/utils.dart';
|
import 'package:hiddify/utils/utils.dart';
|
||||||
|
import 'package:rxdart/rxdart.dart';
|
||||||
|
|
||||||
class MobileSingboxService with InfraLogger implements SingboxService {
|
class MobileSingboxService
|
||||||
late final MethodChannel _methodChannel =
|
with ServiceStatus, InfraLogger
|
||||||
const MethodChannel("com.hiddify.app/method");
|
implements SingboxService {
|
||||||
late final EventChannel _logsChannel =
|
late final _methodChannel = const MethodChannel("com.hiddify.app/method");
|
||||||
const EventChannel("com.hiddify.app/service.logs");
|
late final _statusChannel =
|
||||||
|
const EventChannel("com.hiddify.app/service.status");
|
||||||
|
late final _alertsChannel =
|
||||||
|
const EventChannel("com.hiddify.app/service.alerts");
|
||||||
|
late final _logsChannel = const EventChannel("com.hiddify.app/service.logs");
|
||||||
|
|
||||||
|
late final ValueStream<ConnectionStatus> _connectionStatus;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> init() async {
|
||||||
|
loggy.debug("initializing");
|
||||||
|
final status =
|
||||||
|
_statusChannel.receiveBroadcastStream().map(mapEventToStatus);
|
||||||
|
final alerts =
|
||||||
|
_alertsChannel.receiveBroadcastStream().map(mapEventToStatus);
|
||||||
|
_connectionStatus =
|
||||||
|
ValueConnectableStream(Rx.merge([status, alerts])).autoConnect();
|
||||||
|
await _connectionStatus.first;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TaskEither<String, Unit> setup(
|
TaskEither<String, Unit> setup(
|
||||||
@@ -48,25 +69,14 @@ class MobileSingboxService with InfraLogger implements SingboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
TaskEither<String, Unit> create(String configPath) {
|
TaskEither<String, Unit> start(String configPath) {
|
||||||
return TaskEither(
|
|
||||||
() async {
|
|
||||||
loggy.debug("creating service for: $configPath");
|
|
||||||
await _methodChannel.invokeMethod(
|
|
||||||
"set_active_config_path",
|
|
||||||
{"path": configPath},
|
|
||||||
);
|
|
||||||
return right(unit);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
TaskEither<String, Unit> start() {
|
|
||||||
return TaskEither(
|
return TaskEither(
|
||||||
() async {
|
() async {
|
||||||
loggy.debug("starting");
|
loggy.debug("starting");
|
||||||
await _methodChannel.invokeMethod("start");
|
await _methodChannel.invokeMethod(
|
||||||
|
"start",
|
||||||
|
{"path": configPath},
|
||||||
|
);
|
||||||
return right(unit);
|
return right(unit);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@@ -83,6 +93,20 @@ class MobileSingboxService with InfraLogger implements SingboxService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
TaskEither<String, Unit> restart(String configPath) {
|
||||||
|
return TaskEither(
|
||||||
|
() async {
|
||||||
|
loggy.debug("restarting");
|
||||||
|
await _methodChannel.invokeMethod(
|
||||||
|
"restart",
|
||||||
|
{"path": configPath},
|
||||||
|
);
|
||||||
|
return right(unit);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<String> watchOutbounds() {
|
Stream<String> watchOutbounds() {
|
||||||
const channel = EventChannel("com.hiddify.app/groups");
|
const channel = EventChannel("com.hiddify.app/groups");
|
||||||
@@ -99,7 +123,10 @@ class MobileSingboxService with InfraLogger implements SingboxService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Stream<String> watchStatus() {
|
Stream<ConnectionStatus> watchConnectionStatus() => _connectionStatus;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Stream<String> watchStats() {
|
||||||
// TODO: implement watchStatus
|
// TODO: implement watchStatus
|
||||||
return const Stream.empty();
|
return const Stream.empty();
|
||||||
}
|
}
|
||||||
|
|||||||
46
lib/services/singbox/shared.dart
Normal file
46
lib/services/singbox/shared.dart
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
import 'package:hiddify/domain/connectivity/connectivity.dart';
|
||||||
|
import 'package:hiddify/domain/core_service_failure.dart';
|
||||||
|
|
||||||
|
mixin ServiceStatus {
|
||||||
|
ConnectionStatus mapEventToStatus(dynamic event) {
|
||||||
|
final status = event['status'] as String;
|
||||||
|
late ConnectionStatus connectionStatus;
|
||||||
|
switch (status) {
|
||||||
|
case "Stopped":
|
||||||
|
final failure = event["alert"] as String?;
|
||||||
|
final message = event["message"] as String?;
|
||||||
|
connectionStatus = ConnectionStatus.disconnected(
|
||||||
|
switch (failure) {
|
||||||
|
null => null,
|
||||||
|
"RequestVPNPermission" => MissingVpnPermission(message),
|
||||||
|
"RequestNotificationPermission" =>
|
||||||
|
MissingNotificationPermission(message),
|
||||||
|
"EmptyConfiguration" ||
|
||||||
|
"StartCommandServer" ||
|
||||||
|
"CreateService" ||
|
||||||
|
"StartService" =>
|
||||||
|
CoreConnectionFailure(fromServiceAlert(failure, message)),
|
||||||
|
_ => const UnexpectedConnectionFailure(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
case "Starting":
|
||||||
|
connectionStatus = const Connecting();
|
||||||
|
case "Started":
|
||||||
|
connectionStatus = const Connected();
|
||||||
|
case "Stopping":
|
||||||
|
connectionStatus = const Disconnecting();
|
||||||
|
}
|
||||||
|
return connectionStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreServiceFailure fromServiceAlert(String key, String? message) {
|
||||||
|
return switch (key) {
|
||||||
|
"EmptyConfiguration" => InvalidConfig(message),
|
||||||
|
"StartCommandServer" ||
|
||||||
|
"CreateService" =>
|
||||||
|
CoreServiceCreateFailure(message),
|
||||||
|
"StartService" => CoreServiceStartFailure(message),
|
||||||
|
_ => const CoreServiceOtherFailure(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:fpdart/fpdart.dart';
|
import 'package:fpdart/fpdart.dart';
|
||||||
|
import 'package:hiddify/domain/connectivity/connectivity.dart';
|
||||||
import 'package:hiddify/domain/singbox/singbox.dart';
|
import 'package:hiddify/domain/singbox/singbox.dart';
|
||||||
import 'package:hiddify/services/singbox/ffi_singbox_service.dart';
|
import 'package:hiddify/services/singbox/ffi_singbox_service.dart';
|
||||||
import 'package:hiddify/services/singbox/mobile_singbox_service.dart';
|
import 'package:hiddify/services/singbox/mobile_singbox_service.dart';
|
||||||
@@ -9,9 +10,13 @@ abstract interface class SingboxService {
|
|||||||
factory SingboxService() {
|
factory SingboxService() {
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
return MobileSingboxService();
|
return MobileSingboxService();
|
||||||
}
|
} else if (Platform.isLinux || Platform.isWindows || Platform.isMacOS) {
|
||||||
return FFISingboxService();
|
return FFISingboxService();
|
||||||
}
|
}
|
||||||
|
throw Exception("unsupported platform");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> init();
|
||||||
|
|
||||||
TaskEither<String, Unit> setup(
|
TaskEither<String, Unit> setup(
|
||||||
String baseDir,
|
String baseDir,
|
||||||
@@ -23,19 +28,21 @@ abstract interface class SingboxService {
|
|||||||
|
|
||||||
TaskEither<String, Unit> changeConfigOptions(ConfigOptions options);
|
TaskEither<String, Unit> changeConfigOptions(ConfigOptions options);
|
||||||
|
|
||||||
TaskEither<String, Unit> create(String configPath);
|
TaskEither<String, Unit> start(String configPath);
|
||||||
|
|
||||||
TaskEither<String, Unit> start();
|
|
||||||
|
|
||||||
TaskEither<String, Unit> stop();
|
TaskEither<String, Unit> stop();
|
||||||
|
|
||||||
|
TaskEither<String, Unit> restart(String configPath);
|
||||||
|
|
||||||
Stream<String> watchOutbounds();
|
Stream<String> watchOutbounds();
|
||||||
|
|
||||||
TaskEither<String, Unit> selectOutbound(String groupTag, String outboundTag);
|
TaskEither<String, Unit> selectOutbound(String groupTag, String outboundTag);
|
||||||
|
|
||||||
TaskEither<String, Unit> urlTest(String groupTag);
|
TaskEither<String, Unit> urlTest(String groupTag);
|
||||||
|
|
||||||
Stream<String> watchStatus();
|
Stream<ConnectionStatus> watchConnectionStatus();
|
||||||
|
|
||||||
|
Stream<String> watchStats();
|
||||||
|
|
||||||
Stream<String> watchLogs(String path);
|
Stream<String> watchLogs(String path);
|
||||||
}
|
}
|
||||||
|
|||||||
2
libcore
2
libcore
Submodule libcore updated: 30cb888fa7...c887815676
Reference in New Issue
Block a user