Files
umbrix/lib/singbox/service/platform_singbox_service.dart

266 lines
6.9 KiB
Dart
Raw Normal View History

2023-09-01 15:00:41 +03:30
import 'dart:convert';
import 'dart:io';
2023-09-01 15:00:41 +03:30
2023-08-19 22:27:23 +03:30
import 'package:flutter/services.dart';
import 'package:fpdart/fpdart.dart';
2023-12-01 12:56:24 +03:30
import 'package:hiddify/core/model/directories.dart';
import 'package:hiddify/singbox/model/singbox_config_option.dart';
import 'package:hiddify/singbox/model/singbox_outbound.dart';
import 'package:hiddify/singbox/model/singbox_stats.dart';
import 'package:hiddify/singbox/model/singbox_status.dart';
import 'package:hiddify/singbox/service/singbox_service.dart';
import 'package:hiddify/utils/custom_loggers.dart';
2023-09-10 20:25:04 +03:30
import 'package:rxdart/rxdart.dart';
2023-08-19 22:27:23 +03:30
2023-12-01 12:56:24 +03:30
class PlatformSingboxService with InfraLogger implements SingboxService {
2023-09-10 20:25:04 +03:30
late final _methodChannel = const MethodChannel("com.hiddify.app/method");
2023-12-01 12:56:24 +03:30
late final _statusChannel =
2023-12-01 14:22:00 +03:30
const EventChannel("com.hiddify.app/service.status", JSONMethodCodec());
2023-09-10 20:25:04 +03:30
late final _alertsChannel =
2023-12-01 14:22:00 +03:30
const EventChannel("com.hiddify.app/service.alerts", JSONMethodCodec());
2023-09-10 20:25:04 +03:30
late final _logsChannel = const EventChannel("com.hiddify.app/service.logs");
2023-12-01 12:56:24 +03:30
late final ValueStream<SingboxStatus> _status;
2023-09-10 20:25:04 +03:30
@override
Future<void> init() async {
loggy.debug("initializing");
2023-12-01 14:22:00 +03:30
final status =
_statusChannel.receiveBroadcastStream().map(SingboxStatus.fromEvent);
final alerts =
_alertsChannel.receiveBroadcastStream().map(SingboxStatus.fromEvent);
2023-12-26 22:26:06 +03:30
2023-12-01 12:56:24 +03:30
_status = ValueConnectableStream(Rx.merge([status, alerts])).autoConnect();
await _status.first;
2023-09-10 20:25:04 +03:30
}
2023-08-19 22:27:23 +03:30
@override
TaskEither<String, Unit> setup(
2023-12-01 12:56:24 +03:30
Directories directories,
2023-10-27 17:45:38 +03:30
bool debug,
) {
return TaskEither(
() async {
if (!Platform.isIOS) {
return right(unit);
}
await _methodChannel.invokeMethod("setup");
return right(unit);
},
);
}
2023-08-19 22:27:23 +03:30
@override
2023-12-01 12:56:24 +03:30
TaskEither<String, Unit> validateConfigByPath(
2023-09-22 23:52:20 +03:30
String path,
String tempPath,
bool debug,
) {
2023-08-19 22:27:23 +03:30
return TaskEither(
() async {
final message = await _methodChannel.invokeMethod<String>(
"parse_config",
2023-09-22 23:52:20 +03:30
{"path": path, "tempPath": tempPath, "debug": debug},
2023-08-19 22:27:23 +03:30
);
if (message == null || message.isEmpty) return right(unit);
return left(message);
},
);
}
2023-09-01 15:00:41 +03:30
@override
2023-12-01 12:56:24 +03:30
TaskEither<String, Unit> changeOptions(SingboxConfigOption options) {
2023-09-01 15:00:41 +03:30
return TaskEither(
() async {
await _methodChannel.invokeMethod(
"change_config_options",
jsonEncode(options.toJson()),
);
return right(unit);
},
);
}
2023-11-12 12:52:54 +03:30
@override
2023-12-01 12:56:24 +03:30
TaskEither<String, String> generateFullConfigByPath(
2023-11-12 12:52:54 +03:30
String path,
) {
return TaskEither(
() async {
final configJson = await _methodChannel.invokeMethod<String>(
"generate_config",
{"path": path},
);
if (configJson == null || configJson.isEmpty) {
return left("null response");
}
return right(configJson);
},
);
}
2023-08-19 22:27:23 +03:30
@override
2023-12-14 14:50:10 +03:30
TaskEither<String, Unit> start(
String path,
String name,
bool disableMemoryLimit,
) {
2023-08-19 22:27:23 +03:30
return TaskEither(
() async {
2023-09-10 20:25:04 +03:30
loggy.debug("starting");
2023-08-19 22:27:23 +03:30
await _methodChannel.invokeMethod(
2023-09-10 20:25:04 +03:30
"start",
2023-12-14 14:50:10 +03:30
{"path": path, "name": name},
2023-08-19 22:27:23 +03:30
);
return right(unit);
},
);
}
@override
2023-09-10 20:25:04 +03:30
TaskEither<String, Unit> stop() {
2023-08-19 22:27:23 +03:30
return TaskEither(
() async {
2023-09-10 20:25:04 +03:30
loggy.debug("stopping");
await _methodChannel.invokeMethod("stop");
2023-08-19 22:27:23 +03:30
return right(unit);
},
);
}
@override
2023-12-14 14:50:10 +03:30
TaskEither<String, Unit> restart(
String path,
String name,
bool disableMemoryLimit,
) {
2023-08-19 22:27:23 +03:30
return TaskEither(
() async {
2023-09-10 20:25:04 +03:30
loggy.debug("restarting");
await _methodChannel.invokeMethod(
"restart",
2023-12-14 14:50:10 +03:30
{"path": path, "name": name},
2023-09-10 20:25:04 +03:30
);
2023-08-19 22:27:23 +03:30
return right(unit);
},
);
}
2024-01-18 22:53:17 +03:30
@override
TaskEither<String, Unit> resetTunnel() {
return TaskEither(
() async {
// only available on iOS (and macOS later)
if (!Platform.isIOS) {
throw UnimplementedError(
"reset tunnel function unavailable on platform",
);
}
loggy.debug("resetting tunnel");
await _methodChannel.invokeMethod("reset");
return right(unit);
},
);
}
2023-08-29 19:32:31 +03:30
@override
2023-12-01 12:56:24 +03:30
Stream<List<SingboxOutboundGroup>> watchOutbounds() {
2023-08-29 19:32:31 +03:30
const channel = EventChannel("com.hiddify.app/groups");
loggy.debug("watching outbounds");
return channel.receiveBroadcastStream().map(
(event) {
if (event case String _) {
2023-12-01 12:56:24 +03:30
return (jsonDecode(event) as List).map((e) {
return SingboxOutboundGroup.fromJson(e as Map<String, dynamic>);
}).toList();
2023-08-29 19:32:31 +03:30
}
2023-10-05 22:47:24 +03:30
loggy.error("[group client] unexpected type, msg: $event");
2023-08-29 19:32:31 +03:30
throw "invalid type";
},
);
}
2024-02-09 12:02:52 +03:30
@override
Stream<List<SingboxOutboundGroup>> watchActiveOutbounds() {
const channel = EventChannel("com.hiddify.app/active-groups");
loggy.debug("watching active outbounds");
return channel.receiveBroadcastStream().map(
(event) {
if (event case String _) {
return (jsonDecode(event) as List).map((e) {
return SingboxOutboundGroup.fromJson(e as Map<String, dynamic>);
}).toList();
}
loggy.error("[active group client] unexpected type, msg: $event");
throw "invalid type";
},
);
}
2023-08-28 13:15:57 +03:30
@override
2023-12-01 12:56:24 +03:30
Stream<SingboxStatus> watchStatus() => _status;
2023-09-10 20:25:04 +03:30
@override
2023-12-01 12:56:24 +03:30
Stream<SingboxStats> watchStats() {
2023-12-14 12:12:12 +03:30
const channel = EventChannel("com.hiddify.app/stats", JSONMethodCodec());
loggy.debug("watching stats");
return channel.receiveBroadcastStream().map(
(event) {
if (event case Map<String, dynamic> _) {
return SingboxStats.fromJson(event);
}
2024-01-18 23:07:29 +03:30
loggy.error(
"[stats client] unexpected type(${event.runtimeType}), msg: $event",
);
2023-12-14 12:12:12 +03:30
throw "invalid type";
},
);
2023-08-28 13:15:57 +03:30
}
2023-08-29 19:32:31 +03:30
@override
TaskEither<String, Unit> selectOutbound(String groupTag, String outboundTag) {
return TaskEither(
() async {
loggy.debug("selecting outbound");
await _methodChannel.invokeMethod(
"select_outbound",
{"groupTag": groupTag, "outboundTag": outboundTag},
);
return right(unit);
},
);
}
@override
TaskEither<String, Unit> urlTest(String groupTag) {
return TaskEither(
() async {
await _methodChannel.invokeMethod(
"url_test",
{"groupTag": groupTag},
);
return right(unit);
},
);
}
2023-08-19 22:27:23 +03:30
@override
Stream<List<String>> watchLogs(String path) async* {
yield* _logsChannel
.receiveBroadcastStream()
.map((event) => (event as List).map((e) => e as String).toList());
}
@override
TaskEither<String, Unit> clearLogs() {
return TaskEither(
() async {
await _methodChannel.invokeMethod("clear_logs");
return right(unit);
2023-08-19 22:27:23 +03:30
},
);
}
}