refactor: SSHClientX.exec

This commit is contained in:
lollipopkit🏳️‍⚧️
2024-08-15 11:27:22 +08:00
parent 267b0b0a69
commit 195ddd2bcc
16 changed files with 155 additions and 221 deletions

View File

@@ -690,7 +690,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 1058; CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -700,7 +700,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.1058; MARKETING_VERSION = 1.0.1060;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -826,7 +826,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 1058; CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -836,7 +836,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.1058; MARKETING_VERSION = 1.0.1060;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -854,7 +854,7 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = 1058; CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -864,7 +864,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.1058; MARKETING_VERSION = 1.0.1060;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
@@ -885,7 +885,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1058; CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -898,7 +898,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.1058; MARKETING_VERSION = 1.0.1060;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
@@ -924,7 +924,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1058; CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -937,7 +937,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.1058; MARKETING_VERSION = 1.0.1060;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -960,7 +960,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1058; CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
GCC_C_LANGUAGE_STANDARD = gnu11; GCC_C_LANGUAGE_STANDARD = gnu11;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
@@ -973,7 +973,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
"@executable_path/../../Frameworks", "@executable_path/../../Frameworks",
); );
MARKETING_VERSION = 1.0.1058; MARKETING_VERSION = 1.0.1060;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
@@ -996,7 +996,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1058; CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -1008,7 +1008,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.1058; MARKETING_VERSION = 1.0.1060;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
@@ -1037,7 +1037,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1058; CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -1049,7 +1049,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.1058; MARKETING_VERSION = 1.0.1060;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox; PRODUCT_NAME = ServerBox;
@@ -1075,7 +1075,7 @@
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1058; CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_ASSET_PATHS = "";
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
@@ -1087,7 +1087,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.1058; MARKETING_VERSION = 1.0.1060;
MTL_FAST_MATH = YES; MTL_FAST_MATH = YES;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd;
PRODUCT_NAME = ServerBox; PRODUCT_NAME = ServerBox;

View File

@@ -13,65 +13,66 @@ typedef OnStdin = void Function(SSHSession session);
typedef PwdRequestFunc = Future<String?> Function(String? user); typedef PwdRequestFunc = Future<String?> Function(String? user);
extension SSHClientX on SSHClient { extension SSHClientX on SSHClient {
/// TODO: delete [exec] Future<(SSHSession, String)> exec(
Future<SSHSession> exec( OnStdin onStdin, {
String cmd, { String? entry,
OnStdout? onStderr, SSHPtyConfig? pty,
OnStdout? onStdout, OnStdout? onStdout,
OnStdin? stdin, OnStdout? onStderr,
bool redirectToBash = false, // not working yet. do not use bool stdout = true,
bool stderr = true,
Map<String, String>? env,
}) async { }) async {
final session = await execute(redirectToBash ? 'head -1 | bash' : cmd); final session = await execute(
entry ?? 'cat | sh',
if (redirectToBash) { pty: pty,
session.stdin.add('$cmd\n'.uint8List); environment: env,
} );
final result = BytesBuilder(copy: false);
final stdoutDone = Completer<void>(); final stdoutDone = Completer<void>();
final stderrDone = Completer<void>(); final stderrDone = Completer<void>();
if (onStdout != null) {
session.stdout.listen( session.stdout.listen(
(e) => onStdout(e.string, session), (e) {
onStdout?.call(e.string, session);
if (stdout) result.add(e);
},
onDone: stdoutDone.complete, onDone: stdoutDone.complete,
onError: stderrDone.completeError,
); );
} else {
stdoutDone.complete();
}
if (onStderr != null) {
session.stderr.listen( session.stderr.listen(
(e) => onStderr(e.string, session), (e) {
onStderr?.call(e.string, session);
if (stderr) result.add(e);
},
onDone: stderrDone.complete, onDone: stderrDone.complete,
onError: stderrDone.completeError,
); );
} else {
stderrDone.complete();
}
if (stdin != null) { onStdin(session);
stdin(session);
}
await stdoutDone.future; await stdoutDone.future;
await stderrDone.future; await stderrDone.future;
session.close(); return (session, result.takeBytes().string);
return session;
} }
Future<int?> execWithPwd( Future<int?> execWithPwd(
String cmd, { String script, {
String? entry,
BuildContext? context, BuildContext? context,
OnStdout? onStdout, OnStdout? onStdout,
OnStdout? onStderr, OnStdout? onStderr,
OnStdin? stdin,
bool redirectToBash = false, // not working yet. do not use
required String id, required String id,
}) async { }) async {
var isRequestingPwd = false; var isRequestingPwd = false;
final session = await exec( final (session, _) = await exec(
cmd, (sess) {
redirectToBash: redirectToBash, sess.stdin.add('$script\n'.uint8List);
sess.stdin.close();
},
onStderr: (data, session) async { onStderr: (data, session) async {
onStderr?.call(data, session); onStderr?.call(data, session);
if (isRequestingPwd) return; if (isRequestingPwd) return;
@@ -84,88 +85,38 @@ extension SSHClientX on SSHClient {
? await context.showPwdDialog(title: user, id: id) ? await context.showPwdDialog(title: user, id: id)
: null; : null;
if (pwd == null || pwd.isEmpty) { if (pwd == null || pwd.isEmpty) {
session.kill(SSHSignal.TERM); session.stdin.close();
} else { } else {
session.stdin.add('$pwd\n'.uint8List); session.stdin.add('$pwd\n'.uint8List);
} }
isRequestingPwd = false; isRequestingPwd = false;
} }
}, },
onStdout: (data, sink) async { onStdout: onStdout,
onStdout?.call(data, sink); entry: entry,
},
stdin: stdin,
); );
return session.exitCode; return session.exitCode;
} }
Future<Uint8List> runForOutput( Future<String> execForOutput(
String command, { String script, {
bool runInPty = false, SSHPtyConfig? pty,
bool stdout = true, bool stdout = true,
bool stderr = true, bool stderr = true,
Map<String, String>? environment, String? entry,
Future<void> Function(SSHSession)? action, Map<String, String>? env,
}) async { }) async {
final session = await execute( final ret = await exec(
command, (session) {
pty: runInPty ? const SSHPtyConfig() : null, session.stdin.add('$script\n'.uint8List);
environment: environment,
);
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(); session.stdin.close();
},
await stdoutDone.future; pty: pty,
await stderrDone.future; env: env,
stdout: stdout,
return result.takeBytes().string; stderr: stderr,
entry: entry,
);
return ret.$2;
} }
} }

View File

@@ -95,8 +95,8 @@ Future<SSHClient> genClient(
try { try {
final ipPort = spi.fromStringUrl(); final ipPort = spi.fromStringUrl();
return await SSHSocket.connect( return await SSHSocket.connect(
ipPort.ip, ipPort.$1,
ipPort.port, ipPort.$2,
timeout: timeout, timeout: timeout,
); );
} catch (e) { } catch (e) {

View File

@@ -31,8 +31,8 @@ class ServerPrivateInfo {
final List<String>? tags; final List<String>? tags;
@HiveField(7) @HiveField(7)
final String? alterUrl; final String? alterUrl;
@HiveField(8) @HiveField(8, defaultValue: true)
final bool? autoConnect; final bool autoConnect;
/// [id] of the jump server /// [id] of the jump server
@HiveField(9) @HiveField(9)
@@ -59,7 +59,7 @@ class ServerPrivateInfo {
this.keyId, this.keyId,
this.tags, this.tags,
this.alterUrl, this.alterUrl,
this.autoConnect, this.autoConnect = true,
this.jumpId, this.jumpId,
this.custom, this.custom,
this.wolCfg, this.wolCfg,
@@ -75,7 +75,7 @@ class ServerPrivateInfo {
final keyId = json['pubKeyId'] as String?; final keyId = json['pubKeyId'] as String?;
final tags = (json['tags'] as List?)?.cast<String>(); final tags = (json['tags'] as List?)?.cast<String>();
final alterUrl = json['alterUrl'] as 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 jumpId = json['jumpId'] as String?;
final custom = json['customCmd'] == null final custom = json['customCmd'] == null
? null ? null
@@ -128,9 +128,7 @@ class ServerPrivateInfo {
if (alterUrl != null) { if (alterUrl != null) {
data['alterUrl'] = alterUrl; data['alterUrl'] = alterUrl;
} }
if (autoConnect != null) {
data['autoConnect'] = autoConnect; data['autoConnect'] = autoConnect;
}
if (jumpId != null) { if (jumpId != null) {
data['jumpId'] = jumpId; data['jumpId'] = jumpId;
} }
@@ -160,7 +158,7 @@ class ServerPrivateInfo {
custom?.cmds != old.custom?.cmds; custom?.cmds != old.custom?.cmds;
} }
IpPort fromStringUrl() { (String, int) fromStringUrl() {
if (alterUrl == null) { if (alterUrl == null) {
throw SSHErr(type: SSHErrType.connect, message: 'alterUrl is null'); throw SSHErr(type: SSHErrType.connect, message: 'alterUrl is null');
} }
@@ -177,7 +175,7 @@ class ServerPrivateInfo {
if (port <= 0 || port > 65535) { if (port <= 0 || port > 65535) {
throw SSHErr(type: SSHErrType.connect, message: 'alterUrl port error'); throw SSHErr(type: SSHErrType.connect, message: 'alterUrl port error');
} }
return IpPort(ip_, port_); return (ip_, port_);
} }
@override @override
@@ -206,11 +204,6 @@ class ServerPrivateInfo {
logoUrl: 'https://example.com/logo.png', logoUrl: 'https://example.com/logo.png',
), ),
); );
}
class IpPort { bool get isRoot => user == 'root';
final String ip;
final int port;
IpPort(this.ip, this.port);
} }

View File

@@ -25,7 +25,7 @@ class ServerPrivateInfoAdapter extends TypeAdapter<ServerPrivateInfo> {
keyId: fields[5] as String?, keyId: fields[5] as String?,
tags: (fields[6] as List?)?.cast<String>(), tags: (fields[6] as List?)?.cast<String>(),
alterUrl: fields[7] as 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?, jumpId: fields[9] as String?,
custom: fields[10] as ServerCustom?, custom: fields[10] as ServerCustom?,
wolCfg: fields[11] as WakeOnLanCfg?, wolCfg: fields[11] as WakeOnLanCfg?,

View File

@@ -34,7 +34,7 @@ class ServerProvider extends ChangeNotifier {
final _manualDisconnectedIds = <String>{}; final _manualDisconnectedIds = <String>{};
Future<void> load() async { Future<void> load() async {
// Issue #147 // #147
// Clear all servers because of restarting app will cause duplicate servers // Clear all servers because of restarting app will cause duplicate servers
final oldServers = Map<String, Server>.from(_servers); final oldServers = Map<String, Server>.from(_servers);
_servers.clear(); _servers.clear();
@@ -46,7 +46,7 @@ class ServerProvider extends ChangeNotifier {
final originServer = oldServers[spi.id]; final originServer = oldServers[spi.id];
final newServer = genServer(spi); final newServer = genServer(spi);
/// Issues #258 /// #258
/// If not [shouldReconnect], then keep the old state. /// If not [shouldReconnect], then keep the old state.
if (originServer != null && !originServer.spi.shouldReconnect(spi)) { if (originServer != null && !originServer.spi.shouldReconnect(spi)) {
newServer.conn = originServer.conn; newServer.conn = originServer.conn;
@@ -112,20 +112,20 @@ class ServerProvider extends ChangeNotifier {
return; return;
} }
await Future.wait(_servers.values.map((s) => _connectFn(s, onlyFailed))); await Future.wait(_servers.values.map((s) async {
}
Future<void> _connectFn(Server s, bool onlyFailed) async {
if (onlyFailed) { if (onlyFailed) {
if (s.conn != ServerConn.failed) return; if (s.conn != ServerConn.failed) return;
TryLimiter.reset(s.spi.id); TryLimiter.reset(s.spi.id);
} }
if (!(s.spi.autoConnect ?? true) && s.conn == ServerConn.disconnected || if (_manualDisconnectedIds.contains(s.spi.id)) return;
_manualDisconnectedIds.contains(s.spi.id)) {
if (s.conn == ServerConn.disconnected && !s.spi.autoConnect) {
return; return;
} }
return await _getData(s.spi); return await _getData(s.spi);
}));
} }
Future<void> startAutoRefresh() async { Future<void> startAutoRefresh() async {
@@ -301,16 +301,16 @@ class ServerProvider extends ChangeNotifier {
final scriptRaw = ShellFunc.allScript(spi.custom?.cmds).uint8List; final scriptRaw = ShellFunc.allScript(spi.custom?.cmds).uint8List;
try { try {
final writeScriptResult = await s.client!.runForOutput( final (_, writeScriptResult) = await s.client!.exec(
ShellFunc.getInstallShellCmd(spi.id), (session) async {
action: (session) async {
session.stdin.add(scriptRaw); session.stdin.add(scriptRaw);
session.stdin.close(); session.stdin.close();
}, },
entry: ShellFunc.getInstallShellCmd(spi.id),
); );
if (writeScriptResult.isNotEmpty) { if (writeScriptResult.isNotEmpty) {
ShellFunc.switchScriptDir(spi.id); ShellFunc.switchScriptDir(spi.id);
throw String.fromCharCodes(writeScriptResult); throw writeScriptResult;
} }
} on SSHAuthAbortError catch (e) { } on SSHAuthAbortError catch (e) {
TryLimiter.inc(sid); TryLimiter.inc(sid);

View File

@@ -8,28 +8,27 @@ import 'package:server_box/data/res/provider.dart';
final class SystemdProvider { final class SystemdProvider {
late final SSHClient _client; late final SSHClient _client;
late final bool isRoot;
SystemdProvider.init(ServerPrivateInfo spi) { SystemdProvider.init(ServerPrivateInfo spi) {
isRoot = spi.isRoot;
_client = Pros.server.pick(spi: spi)!.client!; _client = Pros.server.pick(spi: spi)!.client!;
getUnits(); getUnits();
} }
final isBusy = false.vn; final isBusy = false.vn;
final isRoot = false.vn;
final units = <SystemdUnit>[].vn; final units = <SystemdUnit>[].vn;
Future<void> getUnits() async { Future<void> getUnits() async {
isBusy.value = true; isBusy.value = true;
try { try {
final result = await _client.runScriptIn(_getUnitsCmd); final result = await _client.execForOutput(_getUnitsCmd);
final units = result.split('\n'); final units = result.split('\n');
final isRootRaw = units.firstOrNull;
isRoot.value = isRootRaw == '0';
final userUnits = <String>[]; final userUnits = <String>[];
final systemUnits = <String>[]; final systemUnits = <String>[];
for (final unit in units.skip(1)) { for (final unit in units) {
if (unit.startsWith('/etc/systemd/system')) { if (unit.startsWith('/etc/systemd/system')) {
systemUnits.add(unit); systemUnits.add(unit);
} else if (unit.startsWith('~/.config/systemd/user')) { } else if (unit.startsWith('~/.config/systemd/user')) {
@@ -64,7 +63,7 @@ for unit in ${unitNames_.join(' ')}; do
echo -n "${ShellFunc.seperator}\n\$state" echo -n "${ShellFunc.seperator}\n\$state"
done done
'''; ''';
final result = await _client.runScriptIn(script); final result = await _client.execForOutput(script);
final units = result.split(ShellFunc.seperator); final units = result.split(ShellFunc.seperator);
final parsedUnits = <SystemdUnit>[]; final parsedUnits = <SystemdUnit>[];
@@ -124,17 +123,8 @@ done
}); });
return parsedUnits; 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() { get_files() {
unit_type=\$1 unit_type=\$1
base_dir=\$2 base_dir=\$2
@@ -149,14 +139,12 @@ get_files() {
get_type_files() { get_type_files() {
unit_type=\$1 unit_type=\$1
base_dir="" base_dir=""
if [ "\$uid" -eq 0 ]; then
get_files \$unit_type /etc/systemd/system ${isRoot ? """
get_files \$unit_type ~/.config/systemd/user get_files \$unit_type /etc/systemd/system
else get_files \$unit_type ~/.config/systemd/user""" : """
get_files \$unit_type ~/.config/systemd/user get_files \$unit_type ~/.config/systemd/user"""}
fi
} }
types="service socket mount timer" types="service socket mount timer"
@@ -165,3 +153,8 @@ for type in \$types; do
get_type_files \$type get_type_files \$type
done done
'''; ''';
}
String _getIniVal(String line) {
return line.split('=').last;
}

View File

@@ -1,7 +1,8 @@
// This file is generated by fl_build. Do not edit. // This file is generated by fl_build. Do not edit.
// ignore_for_file: prefer_single_quotes
class BuildData { class BuildData {
static const String name = 'ServerBox'; static const String name = "ServerBox";
static const int build = 1058; static const int build = 1060;
static const int script = 56; static const int script = 57;
} }

View File

@@ -156,9 +156,8 @@ class BackupPage extends StatelessWidget {
trailing: ListenableBuilder( trailing: ListenableBuilder(
listenable: webdavLoading, listenable: webdavLoading,
builder: (_, __) { builder: (_, __) {
if (webdavLoading.value) { if (webdavLoading.value) return SizedLoading.centerSmall;
return UIs.centerSizedLoadingSmall;
}
return Row( return Row(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: [ children: [

View File

@@ -199,7 +199,7 @@ class _PrivateKeyEditPageState extends State<PrivateKeyEditPage> {
return; return;
} }
FocusScope.of(context).unfocus(); FocusScope.of(context).unfocus();
_loading.value = UIs.centerSizedLoading; _loading.value = SizedLoading.centerMedium;
try { try {
final decrypted = await Computer.shared.start(decyptPem, [key, pwd]); final decrypted = await Computer.shared.start(decyptPem, [key, pwd]);
final pki = PrivateKeyInfo(id: name, key: decrypted); final pki = PrivateKeyInfo(id: name, key: decrypted);

View File

@@ -606,7 +606,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
_tags.value = spi.tags?.toSet() ?? {}; _tags.value = spi.tags?.toSet() ?? {};
_altUrlController.text = spi.alterUrl ?? ''; _altUrlController.text = spi.alterUrl ?? '';
_autoConnect.value = spi.autoConnect ?? true; _autoConnect.value = spi.autoConnect;
_jumpServer.value = spi.jumpId; _jumpServer.value = spi.jumpId;
final custom = spi.custom; final custom = spi.custom;

View File

@@ -64,24 +64,22 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
} }
Widget _buildSortMenu() { Widget _buildSortMenu() {
final options = [
(_SortType.name, libL10n.name),
(_SortType.size, l10n.size),
(_SortType.time, l10n.time),
];
return ValBuilder( return ValBuilder(
listenable: _sortOption, listenable: _sortOption,
builder: (value) { builder: (value) {
return PopupMenuButton<_SortType>( return PopupMenuButton<_SortType>(
icon: const Icon(Icons.sort), icon: const Icon(Icons.sort),
itemBuilder: (context) { itemBuilder: (context) {
final currentSelectedOption = _sortOption.value;
final options = [
(_SortType.name, libL10n.name),
(_SortType.size, l10n.size),
(_SortType.time, l10n.time),
];
return options.map((r) { return options.map((r) {
final (type, name) = r; final (type, name) = r;
final selected = type == currentSelectedOption.sortBy; final selected = type == value.sortBy;
final title = selected final title =
? "$name (${currentSelectedOption.reversed ? '-' : '+'})" selected ? "$name (${value.reversed ? '-' : '+'})" : name;
: name;
return PopupMenuItem( return PopupMenuItem(
value: type, value: type,
child: Text( child: Text(
@@ -607,7 +605,7 @@ class _SftpPageState extends State<SftpPage> with AfterLayoutMixin {
/// Issue #97 /// Issue #97
/// In order to compatible with the Synology NAS /// In order to compatible with the Synology NAS
/// which not has '.' and '..' in listdir /// which not has '.' and '..' in listdir
if (fs.isNotEmpty && fs.firstOrNull?.filename == '.') { if (fs.firstOrNull?.filename == '.') {
fs.removeAt(0); fs.removeAt(0);
} }

View File

@@ -54,9 +54,9 @@ final class _SystemdPageState extends State<SystemdPage> {
(isBusy) => AnimatedContainer( (isBusy) => AnimatedContainer(
duration: Durations.medium1, duration: Durations.medium1,
curve: Curves.fastEaseInToSlowEaseOut, curve: Curves.fastEaseInToSlowEaseOut,
height: isBusy ? 50 : 0, height: isBusy ? 30 : 0,
child: isBusy child: isBusy
? UIs.centerSizedLoadingSmall.paddingOnly(bottom: 7) ? SizedLoading.centerSmall.paddingOnly(bottom: 7)
: const SizedBox.shrink(), : const SizedBox.shrink(),
), ),
), ),
@@ -71,8 +71,7 @@ final class _SystemdPageState extends State<SystemdPage> {
(units) { (units) {
if (units.isEmpty) { if (units.isEmpty) {
return SliverToBoxAdapter( return SliverToBoxAdapter(
child: ListTile(title: Text(libL10n.empty)) child: CenterGreyTitle(libL10n.empty)
.cardx
.paddingSymmetric(horizontal: 13), .paddingSymmetric(horizontal: 13),
); );
} }
@@ -103,7 +102,7 @@ final class _SystemdPageState extends State<SystemdPage> {
return PopupMenu( return PopupMenu(
items: unit.availableFuncs.map(_buildUnitFuncBtn).toList(), items: unit.availableFuncs.map(_buildUnitFuncBtn).toList(),
onSelected: (val) async { 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( final sure = await context.showRoundDialog(
title: libL10n.attention, title: libL10n.attention,
child: SimpleMarkdown(data: '```shell\n$cmd\n```'), child: SimpleMarkdown(data: '```shell\n$cmd\n```'),

View File

@@ -471,7 +471,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1058; CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Server Box"; INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
@@ -481,7 +481,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.15; MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.1058; MARKETING_VERSION = 1.0.1060;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box"; PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -608,7 +608,7 @@
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1058; CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "Server Box"; INFOPLIST_KEY_CFBundleDisplayName = "Server Box";
@@ -618,7 +618,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.15; MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.1058; MARKETING_VERSION = 1.0.1060;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box"; PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -638,7 +638,7 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application"; "CODE_SIGN_IDENTITY[sdk=macosx*]" = "3rd Party Mac Developer Application";
CODE_SIGN_STYLE = Manual; CODE_SIGN_STYLE = Manual;
COMBINE_HIDPI_IMAGES = YES; COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1058; CURRENT_PROJECT_VERSION = 1060;
DEVELOPMENT_TEAM = ""; DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6; "DEVELOPMENT_TEAM[sdk=macosx*]" = BA88US33G6;
INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_FILE = Runner/Info.plist;
@@ -649,7 +649,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 10.15; MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = 1.0.1058; MARKETING_VERSION = 1.0.1060;
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
PRODUCT_NAME = "Server Box"; PRODUCT_NAME = "Server Box";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";

View File

@@ -373,8 +373,8 @@ packages:
dependency: "direct dev" dependency: "direct dev"
description: description:
path: "." path: "."
ref: "v1.0.41" ref: "v1.0.42"
resolved-ref: "6f1f4e8c9fecb3a43725e19e6747d4e2a727ea89" resolved-ref: "2cae3388a66581169638c3b3f5fc73dd7b5b4bfd"
url: "https://github.com/lppcg/fl_build.git" url: "https://github.com/lppcg/fl_build.git"
source: git source: git
version: "1.0.0" version: "1.0.0"
@@ -390,8 +390,8 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
path: "." path: "."
ref: "v1.0.132" ref: "v1.0.134"
resolved-ref: f68453c64cfa3b363bcd6b5e7d76cad7fa7724c6 resolved-ref: "24b9778e8fb482ee233b70bcc4a38587aa7f76b1"
url: "https://github.com/lppcg/fl_lib" url: "https://github.com/lppcg/fl_lib"
source: git source: git
version: "0.0.1" version: "0.0.1"

View File

@@ -1,7 +1,7 @@
name: server_box name: server_box
description: server status & toolbox app. description: server status & toolbox app.
publish_to: 'none' publish_to: 'none'
version: 1.0.1058+1058 version: 1.0.1060+1060
environment: environment:
sdk: ">=3.0.0" sdk: ">=3.0.0"
@@ -60,7 +60,7 @@ dependencies:
fl_lib: fl_lib:
git: git:
url: https://github.com/lppcg/fl_lib url: https://github.com/lppcg/fl_lib
ref: v1.0.132 ref: v1.0.134
dependency_overrides: dependency_overrides:
# dartssh2: # dartssh2:
@@ -82,7 +82,7 @@ dev_dependencies:
fl_build: fl_build:
git: git:
url: https://github.com/lppcg/fl_build.git url: https://github.com/lppcg/fl_build.git
ref: v1.0.41 ref: v1.0.42
flutter: flutter:
generate: true generate: true