diff --git a/lib/core/utils/server.dart b/lib/core/utils/server.dart index 4ad1d093..3c5998ac 100644 --- a/lib/core/utils/server.dart +++ b/lib/core/utils/server.dart @@ -44,63 +44,78 @@ String getPrivateKey(String id) { Future genClient( ServerPrivateInfo spi, { void Function(GenSSHClientStatus)? onStatus, + + /// Must pass this param when use multi-thread and key login String? privateKey, + + /// Must pass this param when use multi-thread and key login + String? jumpPrivateKey, Duration timeout = const Duration(seconds: 5), /// [ServerPrivateInfo] of the jump server + /// + /// Must pass this param when use multi-thread and key login ServerPrivateInfo? jumpSpi, - String? jumpPrivateKey, }) async { onStatus?.call(GenSSHClientStatus.socket); - SSHSocket? socket; - try { - socket = await SSHSocket.connect( - spi.ip, - spi.port, - timeout: timeout, - ); - } catch (e) { - if (spi.alterUrl == null) rethrow; - try { - final ipPort = spi.fromStringUrl(); - socket = await SSHSocket.connect( - ipPort.ip, - ipPort.port, - timeout: timeout, - ); - } catch (e) { - rethrow; - } - } - final forward = await () async { - if (jumpSpi != null) { + final socket = await () async { + // Proxy + final jumpSpi_ = () { + // Multi-thread or key login + if (jumpSpi != null) return jumpSpi; + // Main thread + if (spi.jumpId != null) return Stores.server.box.get(spi.jumpId); + }(); + if (jumpSpi_ != null) { final jumpClient = await genClient( - jumpSpi, + jumpSpi_, privateKey: jumpPrivateKey, timeout: timeout, ); - // Use `0.0.0.0` as localhost to use all interfaces. + return await jumpClient.forwardLocal( spi.ip, spi.port, ); } + + // Direct + try { + return await SSHSocket.connect( + spi.ip, + spi.port, + timeout: timeout, + ); + } catch (e) { + if (spi.alterUrl == null) rethrow; + try { + final ipPort = spi.fromStringUrl(); + return await SSHSocket.connect( + ipPort.ip, + ipPort.port, + timeout: timeout, + ); + } catch (e) { + rethrow; + } + } }(); - if (spi.pubKeyId == null) { + final keyId = spi.keyId; + if (keyId == null) { onStatus?.call(GenSSHClientStatus.pwd); return SSHClient( - forward ?? socket, + socket, username: spi.user, onPasswordRequest: () => spi.pwd, ); } - privateKey ??= getPrivateKey(spi.pubKeyId!); + privateKey ??= getPrivateKey(keyId); onStatus?.call(GenSSHClientStatus.key); return SSHClient( - forward ?? socket, + socket, username: spi.user, identities: await compute(loadIndentity, privateKey), ); diff --git a/lib/data/model/server/server_private_info.dart b/lib/data/model/server/server_private_info.dart index 494a822a..8a6b397e 100644 --- a/lib/data/model/server/server_private_info.dart +++ b/lib/data/model/server/server_private_info.dart @@ -18,8 +18,9 @@ class ServerPrivateInfo { final String user; @HiveField(4) final String? pwd; + /// [id] of private key @HiveField(5) - final String? pubKeyId; + final String? keyId; @HiveField(6) final List? tags; @HiveField(7) @@ -39,7 +40,7 @@ class ServerPrivateInfo { required this.port, required this.user, required this.pwd, - this.pubKeyId, + this.keyId, this.tags, this.alterUrl, this.autoConnect, @@ -55,7 +56,7 @@ class ServerPrivateInfo { name = json["name"]?.toString() ?? '${json["user"]?.toString() ?? 'root'}@${json["ip"].toString()}:${json["port"] ?? 22}', pwd = json["authorization"]?.toString(), - pubKeyId = json["pubKeyId"]?.toString(), + keyId = json["pubKeyId"]?.toString(), tags = json["tags"]?.cast(), alterUrl = json["alterUrl"]?.toString(), autoConnect = json["autoConnect"], @@ -68,7 +69,7 @@ class ServerPrivateInfo { data["port"] = port; data["user"] = user; data["authorization"] = pwd; - data["pubKeyId"] = pubKeyId; + data["pubKeyId"] = keyId; data["tags"] = tags; data["alterUrl"] = alterUrl; data["autoConnect"] = autoConnect; @@ -82,7 +83,7 @@ class ServerPrivateInfo { bool shouldReconnect(ServerPrivateInfo old) { return id != old.id || pwd != old.pwd || - pubKeyId != old.pubKeyId || + keyId != old.keyId || alterUrl != old.alterUrl || jumpId != old.jumpId; } diff --git a/lib/data/model/server/server_private_info.g.dart b/lib/data/model/server/server_private_info.g.dart index 873f5949..9abae0bc 100644 --- a/lib/data/model/server/server_private_info.g.dart +++ b/lib/data/model/server/server_private_info.g.dart @@ -22,7 +22,7 @@ class ServerPrivateInfoAdapter extends TypeAdapter { port: fields[2] as int, user: fields[3] as String, pwd: fields[4] as String?, - pubKeyId: fields[5] as String?, + keyId: fields[5] as String?, tags: (fields[6] as List?)?.cast(), alterUrl: fields[7] as String?, autoConnect: fields[8] as bool?, @@ -45,7 +45,7 @@ class ServerPrivateInfoAdapter extends TypeAdapter { ..writeByte(4) ..write(obj.pwd) ..writeByte(5) - ..write(obj.pubKeyId) + ..write(obj.keyId) ..writeByte(6) ..write(obj.tags) ..writeByte(7) diff --git a/lib/data/model/sftp/req.dart b/lib/data/model/sftp/req.dart index 07264948..cfb4bd54 100644 --- a/lib/data/model/sftp/req.dart +++ b/lib/data/model/sftp/req.dart @@ -1,5 +1,8 @@ import 'dart:async'; +import 'package:toolbox/data/res/logger.dart'; +import 'package:toolbox/data/res/store.dart'; + import '../../../core/utils/server.dart'; import '../server/server_private_info.dart'; import 'worker.dart'; @@ -10,6 +13,8 @@ class SftpReq { final String localPath; final SftpReqType type; String? privateKey; + ServerPrivateInfo? jumpSpi; + String? jumpPrivateKey; SftpReq( this.spi, @@ -17,8 +22,13 @@ class SftpReq { this.localPath, this.type, ) { - if (spi.pubKeyId != null) { - privateKey = getPrivateKey(spi.pubKeyId!); + final keyId = spi.keyId; + if (keyId != null) { + privateKey = getPrivateKey(keyId); + } + if (spi.jumpId != null) { + jumpSpi = Stores.server.box.get(spi.jumpId); + jumpPrivateKey = Stores.key.get(jumpSpi?.keyId)?.key; } } } @@ -83,7 +93,9 @@ class SftpReqStatus { break; default: error = Exception('sftp worker event: $event'); + Loggers.app.warning(error); dispose(); + break; } notifyListeners(); } diff --git a/lib/data/model/sftp/worker.dart b/lib/data/model/sftp/worker.dart index 03c6f6c1..014299f6 100644 --- a/lib/data/model/sftp/worker.dart +++ b/lib/data/model/sftp/worker.dart @@ -81,7 +81,12 @@ Future _download( try { mainSendPort.send(SftpWorkerStatus.preparing); final watch = Stopwatch()..start(); - final client = await genClient(req.spi, privateKey: req.privateKey); + final client = await genClient( + req.spi, + privateKey: req.privateKey, + jumpSpi: req.jumpSpi, + jumpPrivateKey: req.jumpPrivateKey, + ); mainSendPort.send(SftpWorkerStatus.sshConnectted); /// Create the directory if not exists @@ -131,7 +136,12 @@ Future _upload( try { mainSendPort.send(SftpWorkerStatus.preparing); final watch = Stopwatch()..start(); - final client = await genClient(req.spi, privateKey: req.privateKey); + final client = await genClient( + req.spi, + privateKey: req.privateKey, + jumpSpi: req.jumpSpi, + jumpPrivateKey: req.jumpPrivateKey, + ); mainSendPort.send(SftpWorkerStatus.sshConnectted); final local = File(req.localPath); diff --git a/lib/data/provider/server.dart b/lib/data/provider/server.dart index bb8fac93..44c11cf1 100644 --- a/lib/data/provider/server.dart +++ b/lib/data/provider/server.dart @@ -280,14 +280,11 @@ class ServerProvider extends ChangeNotifier { _setServerState(s, ServerState.connecting); final time1 = DateTime.now(); - final jumpSpi = - spi.jumpId == null ? null : Stores.server.box.get(spi.jumpId); try { s.client = await genClient( spi, timeout: Stores.setting.timeoutD, - jumpSpi: jumpSpi, ); } catch (e) { _limiter.inc(sid); @@ -304,8 +301,8 @@ class ServerProvider extends ChangeNotifier { if (spi.jumpId == null) { Loggers.app.info('Connected to ${spi.name} in $spentTime ms.'); } else { - Loggers.app.info( - 'Connected to ${spi.name} via ${jumpSpi?.name} in $spentTime ms.'); + Loggers.app + .info('Connected to ${spi.name} via jump server in $spentTime ms.'); } _setServerState(s, ServerState.connected); diff --git a/lib/data/res/build_data.dart b/lib/data/res/build_data.dart index 5901b3c5..b85e23f5 100644 --- a/lib/data/res/build_data.dart +++ b/lib/data/res/build_data.dart @@ -2,9 +2,9 @@ class BuildData { static const String name = "ServerBox"; - static const int build = 612; + static const int build = 616; static const String engine = "3.13.8"; - static const String buildAt = "2023-10-28 17:13:40"; + static const String buildAt = "2023-10-30 14:48:00"; static const int modifications = 5; - static const int script = 22; + static const int script = 23; } diff --git a/lib/view/page/server/edit.dart b/lib/view/page/server/edit.dart index 813c8b9d..adad62c8 100644 --- a/lib/view/page/server/edit.dart +++ b/lib/view/page/server/edit.dart @@ -52,31 +52,26 @@ class _ServerEditPageState extends State { void initState() { super.initState(); - if (widget.spi != null) { - _nameController.text = widget.spi?.name ?? ''; - _ipController.text = widget.spi?.ip ?? ''; - _portController.text = (widget.spi?.port ?? 22).toString(); - _usernameController.text = widget.spi?.user ?? ''; - if (widget.spi?.pubKeyId == null) { - _passwordController.text = widget.spi?.pwd ?? ''; + final spi = widget.spi; + if (spi != null) { + _nameController.text = spi.name; + _ipController.text = spi.ip; + _portController.text = spi.port.toString(); + _usernameController.text = spi.user; + if (spi.keyId == null) { + _passwordController.text = spi.pwd ?? ''; } else { _keyIdx.value = Pros.key.pkis.indexWhere( - (e) => e.id == widget.spi!.pubKeyId, + (e) => e.id == widget.spi!.keyId, ); } - if (widget.spi?.tags != null) { - /// List in dart is passed by pointer, so you need to copy it here - _tags.addAll(widget.spi!.tags!); - } - if (widget.spi?.alterUrl != null) { - _altUrlController.text = widget.spi!.alterUrl!; - } - if (widget.spi?.autoConnect != null) { - _autoConnect.value = widget.spi!.autoConnect!; - } - if (widget.spi?.jumpId != null) { - _jumpServer.value = widget.spi!.jumpId; - } + + /// List in dart is passed by pointer, so you need to copy it here + _tags.addAll(spi.tags ?? []); + + _altUrlController.text = spi.alterUrl ?? ''; + _autoConnect.value = spi.autoConnect ?? true; + _jumpServer.value = spi.jumpId; } } @@ -403,7 +398,7 @@ class _ServerEditPageState extends State { port: int.parse(_portController.text), user: _usernameController.text, pwd: _passwordController.text.isEmpty ? null : _passwordController.text, - pubKeyId: _keyIdx.value != null + keyId: _keyIdx.value != null ? Pros.key.pkis.elementAt(_keyIdx.value!).id : null, tags: _tags, diff --git a/lib/view/widget/server_func_btns.dart b/lib/view/widget/server_func_btns.dart index dec1846c..7ce29a1c 100644 --- a/lib/view/widget/server_func_btns.dart +++ b/lib/view/widget/server_func_btns.dart @@ -151,18 +151,18 @@ Future _gotoSSH( } final path = await () async { - final tempKeyFileName = 'srvbox_pk_${spi.pubKeyId}'; + final tempKeyFileName = 'srvbox_pk_${spi.keyId}'; /// For security reason, save the private key file to app doc path return joinPath(await Paths.doc, tempKeyFileName); }(); final file = File(path); - final shouldGenKey = spi.pubKeyId != null; + final shouldGenKey = spi.keyId != null; if (shouldGenKey) { if (await file.exists()) { await file.delete(); } - await file.writeAsString(getPrivateKey(spi.pubKeyId!)); + await file.writeAsString(getPrivateKey(spi.keyId!)); extraArgs.addAll(["-i", path]); }