Files
vpn/lib/services/singbox_service.dart
2026-04-20 01:34:03 +03:00

92 lines
2.2 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:quasar_jet/services/singbox_runtime.dart';
final class SingBoxService {
SingBoxService({SingBoxRuntime? runtime}) : _runtime = runtime ?? SingBoxRuntime();
final SingBoxRuntime _runtime;
final StreamController<String> _logController =
StreamController<String>.broadcast();
Process? _process;
Stream<String> get logs => _logController.stream;
bool get isRunning => _process != null;
Future<void> prepare() async {
await _runtime.ensureBinary();
}
Future<bool> start(String configJson) async {
if (_process != null) {
return true;
}
if (!Platform.isLinux) {
throw UnsupportedError('SingBoxService supports Linux only');
}
try {
jsonDecode(configJson);
} catch (_) {
_logController.add('Config is not valid JSON for sing-box');
return false;
}
final String binaryPath = await _runtime.ensureBinary();
final String configPath = await _runtime.configPath();
await File(configPath).writeAsString(configJson, flush: true);
final List<String> command = <String>[
'pkexec',
binaryPath,
'run',
'-c',
configPath,
];
try {
_process = await Process.start(command.first, command.sublist(1));
_process!.stdout
.transform(utf8.decoder)
.listen((String data) => _logController.add(data));
_process!.stderr
.transform(utf8.decoder)
.listen((String data) => _logController.add('ERROR: $data'));
_process!.exitCode.then((int code) {
_logController.add('sing-box exited with code $code');
_process = null;
});
return true;
} catch (error) {
_logController.add('Start failed: $error');
_process = null;
return false;
}
}
Future<void> stop() async {
final Process? process = _process;
if (process == null) {
return;
}
process.kill(ProcessSignal.sigterm);
await process.exitCode.timeout(const Duration(seconds: 3), onTimeout: () {
process.kill(ProcessSignal.sigkill);
return -1;
});
_process = null;
}
Future<void> dispose() async {
await stop();
await _logController.close();
}
}