mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
refactor: SSHClientX.exec
This commit is contained in:
@@ -690,7 +690,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 1058;
|
||||
CURRENT_PROJECT_VERSION = 1060;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -700,7 +700,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1058;
|
||||
MARKETING_VERSION = 1.0.1060;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -826,7 +826,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 1058;
|
||||
CURRENT_PROJECT_VERSION = 1060;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -836,7 +836,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1058;
|
||||
MARKETING_VERSION = 1.0.1060;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -854,7 +854,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 1058;
|
||||
CURRENT_PROJECT_VERSION = 1060;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -864,7 +864,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1058;
|
||||
MARKETING_VERSION = 1.0.1060;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -885,7 +885,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1058;
|
||||
CURRENT_PROJECT_VERSION = 1060;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -898,7 +898,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1058;
|
||||
MARKETING_VERSION = 1.0.1060;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
@@ -924,7 +924,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1058;
|
||||
CURRENT_PROJECT_VERSION = 1060;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -937,7 +937,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1058;
|
||||
MARKETING_VERSION = 1.0.1060;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -960,7 +960,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1058;
|
||||
CURRENT_PROJECT_VERSION = 1060;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -973,7 +973,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1058;
|
||||
MARKETING_VERSION = 1.0.1060;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -996,7 +996,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1058;
|
||||
CURRENT_PROJECT_VERSION = 1060;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1008,7 +1008,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1058;
|
||||
MARKETING_VERSION = 1.0.1060;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||
@@ -1037,7 +1037,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1058;
|
||||
CURRENT_PROJECT_VERSION = 1060;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1049,7 +1049,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1058;
|
||||
MARKETING_VERSION = 1.0.1060;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||
PRODUCT_NAME = ServerBox;
|
||||
@@ -1075,7 +1075,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1058;
|
||||
CURRENT_PROJECT_VERSION = 1060;
|
||||
DEVELOPMENT_ASSET_PATHS = "";
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1087,7 +1087,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.1058;
|
||||
MARKETING_VERSION = 1.0.1060;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
|
||||
PRODUCT_NAME = ServerBox;
|
||||
|
||||
@@ -13,65 +13,66 @@ typedef OnStdin = void Function(SSHSession session);
|
||||
typedef PwdRequestFunc = Future<String?> Function(String? user);
|
||||
|
||||
extension SSHClientX on SSHClient {
|
||||
/// TODO: delete [exec]
|
||||
Future<SSHSession> exec(
|
||||
String cmd, {
|
||||
OnStdout? onStderr,
|
||||
Future<(SSHSession, String)> exec(
|
||||
OnStdin onStdin, {
|
||||
String? entry,
|
||||
SSHPtyConfig? pty,
|
||||
OnStdout? onStdout,
|
||||
OnStdin? stdin,
|
||||
bool redirectToBash = false, // not working yet. do not use
|
||||
OnStdout? onStderr,
|
||||
bool stdout = true,
|
||||
bool stderr = true,
|
||||
Map<String, String>? env,
|
||||
}) async {
|
||||
final session = await execute(redirectToBash ? 'head -1 | bash' : cmd);
|
||||
|
||||
if (redirectToBash) {
|
||||
session.stdin.add('$cmd\n'.uint8List);
|
||||
}
|
||||
final session = await execute(
|
||||
entry ?? 'cat | sh',
|
||||
pty: pty,
|
||||
environment: env,
|
||||
);
|
||||
|
||||
final result = BytesBuilder(copy: false);
|
||||
final stdoutDone = Completer<void>();
|
||||
final stderrDone = Completer<void>();
|
||||
|
||||
if (onStdout != null) {
|
||||
session.stdout.listen(
|
||||
(e) => onStdout(e.string, session),
|
||||
onDone: stdoutDone.complete,
|
||||
);
|
||||
} else {
|
||||
stdoutDone.complete();
|
||||
}
|
||||
session.stdout.listen(
|
||||
(e) {
|
||||
onStdout?.call(e.string, session);
|
||||
if (stdout) result.add(e);
|
||||
},
|
||||
onDone: stdoutDone.complete,
|
||||
onError: stderrDone.completeError,
|
||||
);
|
||||
|
||||
if (onStderr != null) {
|
||||
session.stderr.listen(
|
||||
(e) => onStderr(e.string, session),
|
||||
onDone: stderrDone.complete,
|
||||
);
|
||||
} else {
|
||||
stderrDone.complete();
|
||||
}
|
||||
session.stderr.listen(
|
||||
(e) {
|
||||
onStderr?.call(e.string, session);
|
||||
if (stderr) result.add(e);
|
||||
},
|
||||
onDone: stderrDone.complete,
|
||||
onError: stderrDone.completeError,
|
||||
);
|
||||
|
||||
if (stdin != null) {
|
||||
stdin(session);
|
||||
}
|
||||
onStdin(session);
|
||||
|
||||
await stdoutDone.future;
|
||||
await stderrDone.future;
|
||||
|
||||
session.close();
|
||||
return session;
|
||||
return (session, result.takeBytes().string);
|
||||
}
|
||||
|
||||
Future<int?> execWithPwd(
|
||||
String cmd, {
|
||||
String script, {
|
||||
String? entry,
|
||||
BuildContext? context,
|
||||
OnStdout? onStdout,
|
||||
OnStdout? onStderr,
|
||||
OnStdin? stdin,
|
||||
bool redirectToBash = false, // not working yet. do not use
|
||||
required String id,
|
||||
}) async {
|
||||
var isRequestingPwd = false;
|
||||
final session = await exec(
|
||||
cmd,
|
||||
redirectToBash: redirectToBash,
|
||||
final (session, _) = await exec(
|
||||
(sess) {
|
||||
sess.stdin.add('$script\n'.uint8List);
|
||||
sess.stdin.close();
|
||||
},
|
||||
onStderr: (data, session) async {
|
||||
onStderr?.call(data, session);
|
||||
if (isRequestingPwd) return;
|
||||
@@ -84,88 +85,38 @@ extension SSHClientX on SSHClient {
|
||||
? await context.showPwdDialog(title: user, id: id)
|
||||
: null;
|
||||
if (pwd == null || pwd.isEmpty) {
|
||||
session.kill(SSHSignal.TERM);
|
||||
session.stdin.close();
|
||||
} else {
|
||||
session.stdin.add('$pwd\n'.uint8List);
|
||||
}
|
||||
isRequestingPwd = false;
|
||||
}
|
||||
},
|
||||
onStdout: (data, sink) async {
|
||||
onStdout?.call(data, sink);
|
||||
},
|
||||
stdin: stdin,
|
||||
onStdout: onStdout,
|
||||
entry: entry,
|
||||
);
|
||||
return session.exitCode;
|
||||
}
|
||||
|
||||
Future<Uint8List> runForOutput(
|
||||
String command, {
|
||||
bool runInPty = false,
|
||||
Future<String> execForOutput(
|
||||
String script, {
|
||||
SSHPtyConfig? pty,
|
||||
bool stdout = true,
|
||||
bool stderr = true,
|
||||
Map<String, String>? environment,
|
||||
Future<void> Function(SSHSession)? action,
|
||||
String? entry,
|
||||
Map<String, String>? env,
|
||||
}) async {
|
||||
final session = await execute(
|
||||
command,
|
||||
pty: runInPty ? const SSHPtyConfig() : null,
|
||||
environment: environment,
|
||||
final ret = await exec(
|
||||
(session) {
|
||||
session.stdin.add('$script\n'.uint8List);
|
||||
session.stdin.close();
|
||||
},
|
||||
pty: pty,
|
||||
env: env,
|
||||
stdout: stdout,
|
||||
stderr: stderr,
|
||||
entry: entry,
|
||||
);
|
||||
|
||||
final result = BytesBuilder(copy: false);
|
||||
final stdoutDone = Completer<void>();
|
||||
final stderrDone = Completer<void>();
|
||||
|
||||
session.stdout.listen(
|
||||
stdout ? result.add : (_) {},
|
||||
onDone: stdoutDone.complete,
|
||||
onError: stderrDone.completeError,
|
||||
);
|
||||
|
||||
session.stderr.listen(
|
||||
stderr ? result.add : (_) {},
|
||||
onDone: stderrDone.complete,
|
||||
onError: stderrDone.completeError,
|
||||
);
|
||||
|
||||
if (action != null) await action(session);
|
||||
|
||||
await stdoutDone.future;
|
||||
await stderrDone.future;
|
||||
|
||||
return result.takeBytes();
|
||||
}
|
||||
|
||||
Future<String> runScriptIn(
|
||||
String cmd, {
|
||||
String shell = '/bin/sh',
|
||||
bool stdout = true,
|
||||
bool stderr = true,
|
||||
}) async {
|
||||
final session = await execute('cat | $shell');
|
||||
|
||||
final result = BytesBuilder(copy: false);
|
||||
final stdoutDone = Completer<void>();
|
||||
final stderrDone = Completer<void>();
|
||||
|
||||
session.stdout.listen(
|
||||
stdout ? result.add : (_) {},
|
||||
onDone: stdoutDone.complete,
|
||||
onError: stderrDone.completeError,
|
||||
);
|
||||
session.stderr.listen(
|
||||
stderr ? result.add : (_) {},
|
||||
onDone: stderrDone.complete,
|
||||
onError: stderrDone.completeError,
|
||||
);
|
||||
|
||||
session.stdin.add('$cmd\n'.uint8List);
|
||||
session.stdin.close();
|
||||
|
||||
await stdoutDone.future;
|
||||
await stderrDone.future;
|
||||
|
||||
return result.takeBytes().string;
|
||||
return ret.$2;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +95,8 @@ Future<SSHClient> genClient(
|
||||
try {
|
||||
final ipPort = spi.fromStringUrl();
|
||||
return await SSHSocket.connect(
|
||||
ipPort.ip,
|
||||
ipPort.port,
|
||||
ipPort.$1,
|
||||
ipPort.$2,
|
||||
timeout: timeout,
|
||||
);
|
||||
} catch (e) {
|
||||
|
||||
@@ -31,8 +31,8 @@ class ServerPrivateInfo {
|
||||
final List<String>? tags;
|
||||
@HiveField(7)
|
||||
final String? alterUrl;
|
||||
@HiveField(8)
|
||||
final bool? autoConnect;
|
||||
@HiveField(8, defaultValue: true)
|
||||
final bool autoConnect;
|
||||
|
||||
/// [id] of the jump server
|
||||
@HiveField(9)
|
||||
@@ -59,7 +59,7 @@ class ServerPrivateInfo {
|
||||
this.keyId,
|
||||
this.tags,
|
||||
this.alterUrl,
|
||||
this.autoConnect,
|
||||
this.autoConnect = true,
|
||||
this.jumpId,
|
||||
this.custom,
|
||||
this.wolCfg,
|
||||
@@ -75,7 +75,7 @@ class ServerPrivateInfo {
|
||||
final keyId = json['pubKeyId'] as String?;
|
||||
final tags = (json['tags'] as List?)?.cast<String>();
|
||||
final alterUrl = json['alterUrl'] as String?;
|
||||
final autoConnect = json['autoConnect'] as bool?;
|
||||
final autoConnect = json['autoConnect'] as bool? ?? true;
|
||||
final jumpId = json['jumpId'] as String?;
|
||||
final custom = json['customCmd'] == null
|
||||
? null
|
||||
@@ -128,9 +128,7 @@ class ServerPrivateInfo {
|
||||
if (alterUrl != null) {
|
||||
data['alterUrl'] = alterUrl;
|
||||
}
|
||||
if (autoConnect != null) {
|
||||
data['autoConnect'] = autoConnect;
|
||||
}
|
||||
data['autoConnect'] = autoConnect;
|
||||
if (jumpId != null) {
|
||||
data['jumpId'] = jumpId;
|
||||
}
|
||||
@@ -160,7 +158,7 @@ class ServerPrivateInfo {
|
||||
custom?.cmds != old.custom?.cmds;
|
||||
}
|
||||
|
||||
IpPort fromStringUrl() {
|
||||
(String, int) fromStringUrl() {
|
||||
if (alterUrl == null) {
|
||||
throw SSHErr(type: SSHErrType.connect, message: 'alterUrl is null');
|
||||
}
|
||||
@@ -177,7 +175,7 @@ class ServerPrivateInfo {
|
||||
if (port <= 0 || port > 65535) {
|
||||
throw SSHErr(type: SSHErrType.connect, message: 'alterUrl port error');
|
||||
}
|
||||
return IpPort(ip_, port_);
|
||||
return (ip_, port_);
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -206,11 +204,6 @@ class ServerPrivateInfo {
|
||||
logoUrl: 'https://example.com/logo.png',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class IpPort {
|
||||
final String ip;
|
||||
final int port;
|
||||
|
||||
IpPort(this.ip, this.port);
|
||||
bool get isRoot => user == 'root';
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
|
||||
keyId: fields[5] as String?,
|
||||
tags: (fields[6] as List?)?.cast<String>(),
|
||||
alterUrl: fields[7] as String?,
|
||||
autoConnect: fields[8] as bool?,
|
||||
autoConnect: fields[8] == null ? true : fields[8] as bool,
|
||||
jumpId: fields[9] as String?,
|
||||
custom: fields[10] as ServerCustom?,
|
||||
wolCfg: fields[11] as WakeOnLanCfg?,
|
||||
|
||||
@@ -34,7 +34,7 @@ class ServerProvider extends ChangeNotifier {
|
||||
final _manualDisconnectedIds = <String>{};
|
||||
|
||||
Future<void> load() async {
|
||||
// Issue #147
|
||||
// #147
|
||||
// Clear all servers because of restarting app will cause duplicate servers
|
||||
final oldServers = Map<String, Server>.from(_servers);
|
||||
_servers.clear();
|
||||
@@ -46,7 +46,7 @@ class ServerProvider extends ChangeNotifier {
|
||||
final originServer = oldServers[spi.id];
|
||||
final newServer = genServer(spi);
|
||||
|
||||
/// Issues #258
|
||||
/// #258
|
||||
/// If not [shouldReconnect], then keep the old state.
|
||||
if (originServer != null && !originServer.spi.shouldReconnect(spi)) {
|
||||
newServer.conn = originServer.conn;
|
||||
@@ -112,20 +112,20 @@ class ServerProvider extends ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
|
||||
await Future.wait(_servers.values.map((s) => _connectFn(s, onlyFailed)));
|
||||
}
|
||||
await Future.wait(_servers.values.map((s) async {
|
||||
if (onlyFailed) {
|
||||
if (s.conn != ServerConn.failed) return;
|
||||
TryLimiter.reset(s.spi.id);
|
||||
}
|
||||
|
||||
Future<void> _connectFn(Server s, bool onlyFailed) async {
|
||||
if (onlyFailed) {
|
||||
if (s.conn != ServerConn.failed) return;
|
||||
TryLimiter.reset(s.spi.id);
|
||||
}
|
||||
if (_manualDisconnectedIds.contains(s.spi.id)) return;
|
||||
|
||||
if (!(s.spi.autoConnect ?? true) && s.conn == ServerConn.disconnected ||
|
||||
_manualDisconnectedIds.contains(s.spi.id)) {
|
||||
return;
|
||||
}
|
||||
return await _getData(s.spi);
|
||||
if (s.conn == ServerConn.disconnected && !s.spi.autoConnect) {
|
||||
return;
|
||||
}
|
||||
|
||||
return await _getData(s.spi);
|
||||
}));
|
||||
}
|
||||
|
||||
Future<void> startAutoRefresh() async {
|
||||
@@ -301,16 +301,16 @@ class ServerProvider extends ChangeNotifier {
|
||||
final scriptRaw = ShellFunc.allScript(spi.custom?.cmds).uint8List;
|
||||
|
||||
try {
|
||||
final writeScriptResult = await s.client!.runForOutput(
|
||||
ShellFunc.getInstallShellCmd(spi.id),
|
||||
action: (session) async {
|
||||
final (_, writeScriptResult) = await s.client!.exec(
|
||||
(session) async {
|
||||
session.stdin.add(scriptRaw);
|
||||
session.stdin.close();
|
||||
},
|
||||
entry: ShellFunc.getInstallShellCmd(spi.id),
|
||||
);
|
||||
if (writeScriptResult.isNotEmpty) {
|
||||
ShellFunc.switchScriptDir(spi.id);
|
||||
throw String.fromCharCodes(writeScriptResult);
|
||||
throw writeScriptResult;
|
||||
}
|
||||
} on SSHAuthAbortError catch (e) {
|
||||
TryLimiter.inc(sid);
|
||||
|
||||
@@ -8,28 +8,27 @@ import 'package:server_box/data/res/provider.dart';
|
||||
|
||||
final class SystemdProvider {
|
||||
late final SSHClient _client;
|
||||
late final bool isRoot;
|
||||
|
||||
SystemdProvider.init(ServerPrivateInfo spi) {
|
||||
isRoot = spi.isRoot;
|
||||
_client = Pros.server.pick(spi: spi)!.client!;
|
||||
getUnits();
|
||||
}
|
||||
|
||||
final isBusy = false.vn;
|
||||
final isRoot = false.vn;
|
||||
final units = <SystemdUnit>[].vn;
|
||||
|
||||
Future<void> getUnits() async {
|
||||
isBusy.value = true;
|
||||
|
||||
try {
|
||||
final result = await _client.runScriptIn(_getUnitsCmd);
|
||||
final result = await _client.execForOutput(_getUnitsCmd);
|
||||
final units = result.split('\n');
|
||||
final isRootRaw = units.firstOrNull;
|
||||
isRoot.value = isRootRaw == '0';
|
||||
|
||||
final userUnits = <String>[];
|
||||
final systemUnits = <String>[];
|
||||
for (final unit in units.skip(1)) {
|
||||
for (final unit in units) {
|
||||
if (unit.startsWith('/etc/systemd/system')) {
|
||||
systemUnits.add(unit);
|
||||
} else if (unit.startsWith('~/.config/systemd/user')) {
|
||||
@@ -64,7 +63,7 @@ for unit in ${unitNames_.join(' ')}; do
|
||||
echo -n "${ShellFunc.seperator}\n\$state"
|
||||
done
|
||||
''';
|
||||
final result = await _client.runScriptIn(script);
|
||||
final result = await _client.execForOutput(script);
|
||||
final units = result.split(ShellFunc.seperator);
|
||||
|
||||
final parsedUnits = <SystemdUnit>[];
|
||||
@@ -124,17 +123,8 @@ done
|
||||
});
|
||||
return parsedUnits;
|
||||
}
|
||||
}
|
||||
|
||||
String _getIniVal(String line) {
|
||||
return line.split('=').last;
|
||||
}
|
||||
|
||||
const _getUnitsCmd = '''
|
||||
# If root, get system & user units, otherwise get user units
|
||||
uid=\$(id -u)
|
||||
echo \$uid
|
||||
|
||||
late final _getUnitsCmd = '''
|
||||
get_files() {
|
||||
unit_type=\$1
|
||||
base_dir=\$2
|
||||
@@ -149,14 +139,12 @@ get_files() {
|
||||
|
||||
get_type_files() {
|
||||
unit_type=\$1
|
||||
|
||||
base_dir=""
|
||||
if [ "\$uid" -eq 0 ]; then
|
||||
get_files \$unit_type /etc/systemd/system
|
||||
get_files \$unit_type ~/.config/systemd/user
|
||||
else
|
||||
get_files \$unit_type ~/.config/systemd/user
|
||||
fi
|
||||
|
||||
${isRoot ? """
|
||||
get_files \$unit_type /etc/systemd/system
|
||||
get_files \$unit_type ~/.config/systemd/user""" : """
|
||||
get_files \$unit_type ~/.config/systemd/user"""}
|
||||
}
|
||||
|
||||
types="service socket mount timer"
|
||||
@@ -165,3 +153,8 @@ for type in \$types; do
|
||||
get_type_files \$type
|
||||
done
|
||||
''';
|
||||
}
|
||||
|
||||
String _getIniVal(String line) {
|
||||
return line.split('=').last;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
// This file is generated by fl_build. Do not edit.
|
||||
// ignore_for_file: prefer_single_quotes
|
||||
|
||||
class BuildData {
|
||||
static const String name = 'ServerBox';
|
||||
static const int build = 1058;
|
||||
static const int script = 56;
|
||||
static const String name = "ServerBox";
|
||||
static const int build = 1060;
|
||||
static const int script = 57;
|
||||
}
|
||||
|
||||
@@ -156,9 +156,8 @@ class BackupPage extends StatelessWidget {
|
||||
trailing: ListenableBuilder(
|
||||
listenable: webdavLoading,
|
||||
builder: (_, __) {
|
||||
if (webdavLoading.value) {
|
||||
return UIs.centerSizedLoadingSmall;
|
||||
}
|
||||
if (webdavLoading.value) return SizedLoading.centerSmall;
|
||||
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
|
||||
@@ -199,7 +199,7 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage> {
|
||||
return;
|
||||
}
|
||||
FocusScope.of(context).unfocus();
|
||||
_loading.value = UIs.centerSizedLoading;
|
||||
_loading.value = SizedLoading.centerMedium;
|
||||
try {
|
||||
final decrypted = await Computer.shared.start(decyptPem, [key, pwd]);
|
||||
final pki = PrivateKeyInfo(id: name, key: decrypted);
|
||||
|
||||
@@ -606,7 +606,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
||||
_tags.value = spi.tags?.toSet() ?? {};
|
||||
|
||||
_altUrlController.text = spi.alterUrl ?? '';
|
||||
_autoConnect.value = spi.autoConnect ?? true;
|
||||
_autoConnect.value = spi.autoConnect;
|
||||
_jumpServer.value = spi.jumpId;
|
||||
|
||||
final custom = spi.custom;
|
||||
|
||||
@@ -64,24 +64,22 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
}
|
||||
|
||||
Widget _buildSortMenu() {
|
||||
final options = [
|
||||
(_SortType.name, libL10n.name),
|
||||
(_SortType.size, l10n.size),
|
||||
(_SortType.time, l10n.time),
|
||||
];
|
||||
return ValBuilder(
|
||||
listenable: _sortOption,
|
||||
builder: (value) {
|
||||
return PopupMenuButton<_SortType>(
|
||||
icon: const Icon(Icons.sort),
|
||||
itemBuilder: (context) {
|
||||
final currentSelectedOption = _sortOption.value;
|
||||
final options = [
|
||||
(_SortType.name, libL10n.name),
|
||||
(_SortType.size, l10n.size),
|
||||
(_SortType.time, l10n.time),
|
||||
];
|
||||
return options.map((r) {
|
||||
final (type, name) = r;
|
||||
final selected = type == currentSelectedOption.sortBy;
|
||||
final title = selected
|
||||
? "$name (${currentSelectedOption.reversed ? '-' : '+'})"
|
||||
: name;
|
||||
final selected = type == value.sortBy;
|
||||
final title =
|
||||
selected ? "$name (${value.reversed ? '-' : '+'})" : name;
|
||||
return PopupMenuItem(
|
||||
value: type,
|
||||
child: Text(
|
||||
@@ -607,7 +605,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
|
||||
/// Issue #97
|
||||
/// In order to compatible with the Synology NAS
|
||||
/// which not has '.' and '..' in listdir
|
||||
if (fs.isNotEmpty && fs.firstOrNull?.filename == '.') {
|
||||
if (fs.firstOrNull?.filename == '.') {
|
||||
fs.removeAt(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,9 +54,9 @@ final class _SystemdPageState extends State<SystemdPage> {
|
||||
(isBusy) => AnimatedContainer(
|
||||
duration: Durations.medium1,
|
||||
curve: Curves.fastEaseInToSlowEaseOut,
|
||||
height: isBusy ? 50 : 0,
|
||||
height: isBusy ? 30 : 0,
|
||||
child: isBusy
|
||||
? UIs.centerSizedLoadingSmall.paddingOnly(bottom: 7)
|
||||
? SizedLoading.centerSmall.paddingOnly(bottom: 7)
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
),
|
||||
@@ -71,8 +71,7 @@ final class _SystemdPageState extends State<SystemdPage> {
|
||||
(units) {
|
||||
if (units.isEmpty) {
|
||||
return SliverToBoxAdapter(
|
||||
child: ListTile(title: Text(libL10n.empty))
|
||||
.cardx
|
||||
child: CenterGreyTitle(libL10n.empty)
|
||||
.paddingSymmetric(horizontal: 13),
|
||||
);
|
||||
}
|
||||
@@ -103,7 +102,7 @@ final class _SystemdPageState extends State<SystemdPage> {
|
||||
return PopupMenu(
|
||||
items: unit.availableFuncs.map(_buildUnitFuncBtn).toList(),
|
||||
onSelected: (val) async {
|
||||
final cmd = unit.getCmd(func: val, isRoot: _pro.isRoot.value);
|
||||
final cmd = unit.getCmd(func: val, isRoot: _pro.isRoot);
|
||||
final sure = await context.showRoundDialog(
|
||||
title: libL10n.attention,
|
||||
child: SimpleMarkdown(data: '```shell\n$cmd\n```'),
|
||||
|
||||
@@ -471,7 +471,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1058;
|
||||
CURRENT_PROJECT_VERSION = 1060;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
|
||||
@@ -481,7 +481,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 1.0.1058;
|
||||
MARKETING_VERSION = 1.0.1060;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "Server Box";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -608,7 +608,7 @@
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1058;
|
||||
CURRENT_PROJECT_VERSION = 1060;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
|
||||
@@ -618,7 +618,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 1.0.1058;
|
||||
MARKETING_VERSION = 1.0.1060;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "Server Box";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -638,7 +638,7 @@
|
||||
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
|
||||
CODE_SIGN_STYLE = Manual;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1058;
|
||||
CURRENT_PROJECT_VERSION = 1060;
|
||||
DEVELOPMENT_TEAM = "";
|
||||
"DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
@@ -649,7 +649,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 10.15;
|
||||
MARKETING_VERSION = 1.0.1058;
|
||||
MARKETING_VERSION = 1.0.1060;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "Server Box";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
||||
@@ -373,8 +373,8 @@ packages:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
path: "."
|
||||
ref: "v1.0.41"
|
||||
resolved-ref: "6f1f4e8c9fecb3a43725e19e6747d4e2a727ea89"
|
||||
ref: "v1.0.42"
|
||||
resolved-ref: "2cae3388a66581169638c3b3f5fc73dd7b5b4bfd"
|
||||
url: "https://github.com/lppcg/fl_build.git"
|
||||
source: git
|
||||
version: "1.0.0"
|
||||
@@ -390,8 +390,8 @@ packages:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "."
|
||||
ref: "v1.0.132"
|
||||
resolved-ref: f68453c64cfa3b363bcd6b5e7d76cad7fa7724c6
|
||||
ref: "v1.0.134"
|
||||
resolved-ref: "24b9778e8fb482ee233b70bcc4a38587aa7f76b1"
|
||||
url: "https://github.com/lppcg/fl_lib"
|
||||
source: git
|
||||
version: "0.0.1"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: server_box
|
||||
description: server status & toolbox app.
|
||||
publish_to: 'none'
|
||||
version: 1.0.1058+1058
|
||||
version: 1.0.1060+1060
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0"
|
||||
@@ -60,7 +60,7 @@ dependencies:
|
||||
fl_lib:
|
||||
git:
|
||||
url: https://github.com/lppcg/fl_lib
|
||||
ref: v1.0.132
|
||||
ref: v1.0.134
|
||||
|
||||
dependency_overrides:
|
||||
# dartssh2:
|
||||
@@ -82,7 +82,7 @@ dev_dependencies:
|
||||
fl_build:
|
||||
git:
|
||||
url: https://github.com/lppcg/fl_build.git
|
||||
ref: v1.0.41
|
||||
ref: v1.0.42
|
||||
|
||||
flutter:
|
||||
generate: true
|
||||
|
||||
Reference in New Issue
Block a user