diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n.dart b/.dart_tool/flutter_gen/gen_l10n/l10n.dart index 987e3f9c..c8468ff6 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n.dart @@ -722,6 +722,12 @@ abstract class S { /// **'Is busy now'** String get isBusy; + /// No description provided for @jumpServer. + /// + /// In en, this message translates to: + /// **'Jump server'** + String get jumpServer; + /// No description provided for @keepForeground. /// /// In en, this message translates to: diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart index 3badc23a..4cf53113 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart @@ -336,6 +336,9 @@ class SDe extends S { @override String get isBusy => 'Is busy now'; + @override + String get jumpServer => 'Server springen'; + @override String get keepForeground => 'Stelle sicher, dass die App geöffnet bleibt.'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart index ba5ecdbd..822c16fe 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart @@ -336,6 +336,9 @@ class SEn extends S { @override String get isBusy => 'Is busy now'; + @override + String get jumpServer => 'Jump server'; + @override String get keepForeground => 'Keep app foreground!'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart index 1ba4ee38..da8df9c0 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_id.dart @@ -336,6 +336,9 @@ class SId extends S { @override String get isBusy => 'Sibuk sekarang'; + @override + String get jumpServer => 'Lompat server'; + @override String get keepForeground => 'Simpan Aplikasi Foreground!'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart index e6638d47..6ab7b73f 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart @@ -336,6 +336,9 @@ class SZh extends S { @override String get isBusy => '当前正忙'; + @override + String get jumpServer => '跳板服务器'; + @override String get keepForeground => '请保持应用处于前台!'; @@ -1095,6 +1098,9 @@ class SZhTw extends SZh { @override String get isBusy => '當前正忙'; + @override + String get jumpServer => '跳板服務器'; + @override String get keepForeground => '請保持應用處於前台!'; diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 334c5e7f..56c5c013 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -586,7 +586,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 603; + CURRENT_PROJECT_VERSION = 606; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -596,7 +596,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.603; + MARKETING_VERSION = 1.0.606; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -720,7 +720,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 603; + CURRENT_PROJECT_VERSION = 606; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -730,7 +730,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.603; + MARKETING_VERSION = 1.0.606; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -748,7 +748,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 603; + CURRENT_PROJECT_VERSION = 606; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -758,7 +758,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.603; + MARKETING_VERSION = 1.0.606; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -779,7 +779,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 603; + CURRENT_PROJECT_VERSION = 606; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -792,7 +792,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.603; + MARKETING_VERSION = 1.0.606; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; @@ -818,7 +818,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 603; + CURRENT_PROJECT_VERSION = 606; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -831,7 +831,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.603; + MARKETING_VERSION = 1.0.606; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -854,7 +854,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 603; + CURRENT_PROJECT_VERSION = 606; DEVELOPMENT_TEAM = BA88US33G6; GCC_C_LANGUAGE_STANDARD = gnu11; GENERATE_INFOPLIST_FILE = YES; @@ -867,7 +867,7 @@ "@executable_path/Frameworks", "@executable_path/../../Frameworks", ); - MARKETING_VERSION = 1.0.603; + MARKETING_VERSION = 1.0.606; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -890,7 +890,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 603; + CURRENT_PROJECT_VERSION = 606; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -902,7 +902,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.603; + MARKETING_VERSION = 1.0.606; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; @@ -931,7 +931,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 603; + CURRENT_PROJECT_VERSION = 606; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -943,7 +943,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.603; + MARKETING_VERSION = 1.0.606; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_NAME = ServerBox; @@ -969,7 +969,7 @@ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 603; + CURRENT_PROJECT_VERSION = 606; DEVELOPMENT_ASSET_PATHS = ""; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_PREVIEWS = YES; @@ -981,7 +981,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.603; + MARKETING_VERSION = 1.0.606; MTL_FAST_MATH = YES; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.WatchEnd; PRODUCT_NAME = ServerBox; diff --git a/lib/core/utils/server.dart b/lib/core/utils/server.dart index 2e67d44a..048767f8 100644 --- a/lib/core/utils/server.dart +++ b/lib/core/utils/server.dart @@ -45,16 +45,18 @@ Future genClient( ServerPrivateInfo spi, { void Function(GenSSHClientStatus)? onStatus, String? privateKey, - Duration? timeout, + Duration timeout = const Duration(seconds: 5), + + /// [ServerPrivateInfo] of the jump server + ServerPrivateInfo? jumpSpi, }) async { onStatus?.call(GenSSHClientStatus.socket); - late SSHSocket socket; - final duration = timeout ?? const Duration(seconds: 5); + SSHSocket? socket; try { socket = await SSHSocket.connect( spi.ip, spi.port, - timeout: duration, + timeout: timeout, ); } catch (e) { if (spi.alterUrl == null) rethrow; @@ -63,17 +65,29 @@ Future genClient( socket = await SSHSocket.connect( ipPort.ip, ipPort.port, - timeout: duration, + timeout: timeout, ); } catch (e) { rethrow; } } + final forward = await () async { + if (jumpSpi != null) { + final jumpClient = await genClient( + jumpSpi, + privateKey: privateKey, + timeout: timeout, + ); + // Use `0.0.0.0` as localhost to use all interfaces. + return await jumpClient.forwardLocal(spi.ip, spi.port, localHost: '0.0.0.0'); + } + }(); + if (spi.pubKeyId == null) { onStatus?.call(GenSSHClientStatus.pwd); return SSHClient( - socket, + forward ?? socket, username: spi.user, onPasswordRequest: () => spi.pwd, ); @@ -82,7 +96,7 @@ Future genClient( onStatus?.call(GenSSHClientStatus.key); return SSHClient( - socket, + forward ?? 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 ca2590c4..494a822a 100644 --- a/lib/data/model/server/server_private_info.dart +++ b/lib/data/model/server/server_private_info.dart @@ -27,6 +27,10 @@ class ServerPrivateInfo { @HiveField(8) final bool? autoConnect; + /// [id] of the jump server + @HiveField(9) + final String? jumpId; + final String id; const ServerPrivateInfo({ @@ -39,9 +43,9 @@ class ServerPrivateInfo { this.tags, this.alterUrl, this.autoConnect, + this.jumpId, }) : id = '$user@$ip:$port'; - /// TODO: if any field is changed, remember to update [id] [name] ServerPrivateInfo.fromJson(Map json) : ip = json["ip"].toString(), port = json["port"] ?? 22, @@ -54,7 +58,8 @@ class ServerPrivateInfo { pubKeyId = json["pubKeyId"]?.toString(), tags = json["tags"]?.cast(), alterUrl = json["alterUrl"]?.toString(), - autoConnect = json["autoConnect"]; + autoConnect = json["autoConnect"], + jumpId = json["jumpId"]; Map toJson() { final Map data = {}; @@ -67,16 +72,19 @@ class ServerPrivateInfo { data["tags"] = tags; data["alterUrl"] = alterUrl; data["autoConnect"] = autoConnect; + data["jumpId"] = jumpId; return data; } Server? get server => Pros.server.pick(spi: this); + Server? get jumpServer => Pros.server.pick(id: jumpId); bool shouldReconnect(ServerPrivateInfo old) { return id != old.id || pwd != old.pwd || pubKeyId != old.pubKeyId || - alterUrl != old.alterUrl; + alterUrl != old.alterUrl || + jumpId != old.jumpId; } _IpPort fromStringUrl() { diff --git a/lib/data/model/server/server_private_info.g.dart b/lib/data/model/server/server_private_info.g.dart index 5bd66e2f..873f5949 100644 --- a/lib/data/model/server/server_private_info.g.dart +++ b/lib/data/model/server/server_private_info.g.dart @@ -26,13 +26,14 @@ class ServerPrivateInfoAdapter extends TypeAdapter { tags: (fields[6] as List?)?.cast(), alterUrl: fields[7] as String?, autoConnect: fields[8] as bool?, + jumpId: fields[9] as String?, ); } @override void write(BinaryWriter writer, ServerPrivateInfo obj) { writer - ..writeByte(9) + ..writeByte(10) ..writeByte(0) ..write(obj.name) ..writeByte(1) @@ -50,7 +51,9 @@ class ServerPrivateInfoAdapter extends TypeAdapter { ..writeByte(7) ..write(obj.alterUrl) ..writeByte(8) - ..write(obj.autoConnect); + ..write(obj.autoConnect) + ..writeByte(9) + ..write(obj.jumpId); } @override diff --git a/lib/data/provider/server.dart b/lib/data/provider/server.dart index e90845d1..3549899d 100644 --- a/lib/data/provider/server.dart +++ b/lib/data/provider/server.dart @@ -117,7 +117,8 @@ class ServerProvider extends ChangeNotifier { await _getData(spi); return; } - await Future.wait(_servers.values.map((s) async { + + Future connectFn(Server s) async { if (onlyFailed) { if (s.state != ServerState.failed) return; _limiter.reset(s.spi.id); @@ -133,7 +134,19 @@ class ServerProvider extends ChangeNotifier { return; } return await _getData(s.spi); - })); + } + + final directServers = []; + final proxyServers = []; + for (final s in _servers.values) { + if (s.spi.jumpId == null) { + directServers.add(s); + } else { + proxyServers.add(s); + } + } + await Future.wait(directServers.map(connectFn)); + await Future.wait(proxyServers.map(connectFn)); } Future startAutoRefresh() async { @@ -223,10 +236,6 @@ class ServerProvider extends ChangeNotifier { // Only reconnect if neccessary if (newSpi.shouldReconnect(old)) { - _servers[newSpi.id]?.client = await genClient( - newSpi, - timeout: Stores.setting.timeoutD, - ); refreshData(spi: newSpi); } @@ -262,6 +271,7 @@ class ServerProvider extends ChangeNotifier { s.client = await genClient( spi, timeout: Stores.setting.timeoutD, + jumpSpi: spi.jumpId == null ? null : Stores.server.box.get(spi.jumpId), ); } catch (e) { _limiter.inc(sid); diff --git a/lib/data/res/build_data.dart b/lib/data/res/build_data.dart index bcfcf343..578a50ae 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 = 603; + static const int build = 606; static const String engine = "3.13.7"; - static const String buildAt = "2023-10-18 20:37:38"; - static const int modifications = 7; + static const String buildAt = "2023-10-21 19:26:34"; + static const int modifications = 4; static const int script = 22; } diff --git a/lib/data/res/github_id.dart b/lib/data/res/github_id.dart index 0b24c5b5..06921a8a 100644 --- a/lib/data/res/github_id.dart +++ b/lib/data/res/github_id.dart @@ -39,5 +39,7 @@ class GithubIds { 'fanzhebufan1', 'wcbing', 'balh55y', + 'wc7086', + 'michaelsara' }; } diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 0d403680..9410c8cf 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -104,6 +104,7 @@ "invalidVersion": "Ungültige Version", "invalidVersionHelp": "Bitte stelle sicher, dass Docker korrekt installiert ist oder dass du eine nicht selbstkompilierte Version verwendest. Wenn du die oben genannten Probleme nicht hast, melde bitte einen Fehler auf {url}.", "isBusy": "Is busy now", + "jumpServer": "Server springen", "keepForeground": "Stelle sicher, dass die App geöffnet bleibt.", "keyAuth": "Schlüsselauthentifzierung", "keyboardCompatibility": "Mögliche Verbesserungen bei der Kompatibilität der Eingabemethoden", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index cc8727d0..7d2cbc46 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -104,6 +104,7 @@ "invalidVersion": "Invalid version", "invalidVersionHelp": "Please make sure that docker is installed correctly, or that you are using a non-self-compiled version. If you don't have the above issues, please submit an issue on {url}.", "isBusy": "Is busy now", + "jumpServer": "Jump server", "keepForeground": "Keep app foreground!", "keyAuth": "Key Auth", "keyboardCompatibility": "Possible to improve input method compatibility", diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index 247457eb..3a0ac844 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -104,6 +104,7 @@ "invalidVersion": "Versi tidak valid", "invalidVersionHelp": "Pastikan Docker diinstal dengan benar, atau Anda menggunakan versi yang tidak dikompilasi. Jika Anda tidak memiliki masalah di atas, silakan kirimkan masalah pada {url}.", "isBusy": "Sibuk sekarang", + "jumpServer": "Lompat server", "keepForeground": "Simpan Aplikasi Foreground!", "keyAuth": "Auth kunci", "keyboardCompatibility": "Mungkin untuk meningkatkan kompatibilitas metode input", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 738fd397..e08d348e 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -104,6 +104,7 @@ "invalidVersion": "不支持的版本", "invalidVersionHelp": "请确保正确安装了docker,或者使用的非自编译版本。如果没有以上问题,请在 {url} 提交问题。", "isBusy": "当前正忙", + "jumpServer": "跳板服务器", "keepForeground": "请保持应用处于前台!", "keyAuth": "密钥认证", "keyboardCompatibility": "可能会改善输入法兼容性", diff --git a/lib/l10n/app_zh_tw.arb b/lib/l10n/app_zh_tw.arb index c075217e..50aca3b1 100644 --- a/lib/l10n/app_zh_tw.arb +++ b/lib/l10n/app_zh_tw.arb @@ -104,6 +104,7 @@ "invalidVersion": "不支持的版本", "invalidVersionHelp": "請確保正確安裝了docker,或者使用的非自編譯版本。如果沒有以上問題,請在 {url} 提交問題。", "isBusy": "當前正忙", + "jumpServer": "跳板服務器", "keepForeground": "請保持應用處於前台!", "keyAuth": "密鑰認證", "keyboardCompatibility": "可能會改善輸入法兼容性", diff --git a/lib/main.dart b/lib/main.dart index fff3be2f..2b0c3c6c 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -95,11 +95,11 @@ void _setupProviders() { Future _initHive() async { await Hive.initFlutter(); // 以 typeId 为顺序 - Hive.registerAdapter(PrivateKeyInfoAdapter()); - Hive.registerAdapter(SnippetAdapter()); - Hive.registerAdapter(ServerPrivateInfoAdapter()); - Hive.registerAdapter(VirtKeyAdapter()); - Hive.registerAdapter(NetViewTypeAdapter()); + Hive.registerAdapter(PrivateKeyInfoAdapter()); // 1 + Hive.registerAdapter(SnippetAdapter()); // 2 + Hive.registerAdapter(ServerPrivateInfoAdapter()); // 3 + Hive.registerAdapter(VirtKeyAdapter()); // 4 + Hive.registerAdapter(NetViewTypeAdapter()); // 5 } void _setupLogger() { diff --git a/lib/view/page/server/edit.dart b/lib/view/page/server/edit.dart index 5793bbae..813c8b9d 100644 --- a/lib/view/page/server/edit.dart +++ b/lib/view/page/server/edit.dart @@ -5,6 +5,7 @@ import 'package:toolbox/core/extension/context/dialog.dart'; import 'package:toolbox/core/extension/context/locale.dart'; import 'package:toolbox/core/extension/context/snackbar.dart'; import 'package:toolbox/data/res/provider.dart'; +import 'package:toolbox/view/widget/expand_tile.dart'; import '../../../core/route.dart'; import '../../../data/model/server/private_key_info.dart'; @@ -43,6 +44,7 @@ class _ServerEditPageState extends State { final _keyIdx = ValueNotifier(null); final _autoConnect = ValueNotifier(true); + final _jumpServer = ValueNotifier(null); var _tags = []; @@ -72,6 +74,9 @@ class _ServerEditPageState extends State { if (widget.spi?.autoConnect != null) { _autoConnect.value = widget.spi!.autoConnect!; } + if (widget.spi?.jumpId != null) { + _jumpServer.value = widget.spi!.jumpId; + } } } @@ -189,6 +194,7 @@ class _ServerEditPageState extends State { onRenameTag: Pros.server.renameTag, ), _buildAuth(), + _buildJumpServer(), ListTile( title: Text(l10n.autoConnect), trailing: ValueBuilder( @@ -316,6 +322,45 @@ class _ServerEditPageState extends State { ); } + Widget _buildJumpServer() { + return ValueBuilder( + listenable: _jumpServer, + build: () { + final children = Pros.server.servers + .where((element) => element.spi.jumpId == null) + .where((element) => element.spi.id != widget.spi?.id) + .map( + (e) => ListTile( + title: Text(e.spi.name), + subtitle: Text(e.spi.id, style: UIs.textGrey), + trailing: Radio( + groupValue: _jumpServer.value, + value: e.spi.id, + onChanged: (val) => _jumpServer.value = val, + ), + onTap: () { + _jumpServer.value = e.spi.id; + }, + ), + ) + .toList(); + children.add(ListTile( + title: Text(l10n.clear), + trailing: const Icon(Icons.clear), + onTap: () => _jumpServer.value = null, + )); + return CardX( + ExpandTile( + leading: const Icon(Icons.map), + initiallyExpanded: _jumpServer.value != null, + title: Text(l10n.jumpServer), + children: children, + ), + ); + }, + ); + } + void _onSave() async { if (_ipController.text.isEmpty) { context.showSnackBar(l10n.plzEnterHost); @@ -364,6 +409,7 @@ class _ServerEditPageState extends State { tags: _tags, alterUrl: _altUrlController.text.isEmpty ? null : _altUrlController.text, autoConnect: _autoConnect.value, + jumpId: _jumpServer.value, ); if (widget.spi == null) { diff --git a/lib/view/widget/expand_tile.dart b/lib/view/widget/expand_tile.dart index c703b22a..99f1415f 100644 --- a/lib/view/widget/expand_tile.dart +++ b/lib/view/widget/expand_tile.dart @@ -5,6 +5,7 @@ const _shape = Border(); class ExpandTile extends ExpansionTile { const ExpandTile({ super.key, + super.leading, required super.title, super.children, super.subtitle, diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index c2c0baee..98e60508 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -57,7 +57,7 @@ 15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* server_box.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = server_box.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* ServerBox.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ServerBox.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -117,7 +117,7 @@ 33CC10EE2044A3C60003C045 /* Products */ = { isa = PBXGroup; children = ( - 33CC10ED2044A3C60003C045 /* server_box.app */, + 33CC10ED2044A3C60003C045 /* ServerBox.app */, ); name = Products; sourceTree = ""; @@ -202,7 +202,7 @@ ); name = Runner; productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* server_box.app */; + productReference = 33CC10ED2044A3C60003C045 /* ServerBox.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -299,7 +299,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; + shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire\n"; }; 439BC61F32B3B58C253BB26F /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; @@ -432,6 +432,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 606; DEVELOPMENT_TEAM = BA88US33G6; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Server Box"; @@ -441,7 +442,9 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; + MARKETING_VERSION = 1.0.606; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; + PRODUCT_NAME = ServerBox; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; @@ -564,6 +567,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 606; DEVELOPMENT_TEAM = BA88US33G6; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Server Box"; @@ -573,7 +577,9 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; + MARKETING_VERSION = 1.0.606; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; + PRODUCT_NAME = ServerBox; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; @@ -590,6 +596,7 @@ "CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development"; CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; + CURRENT_PROJECT_VERSION = 606; DEVELOPMENT_TEAM = BA88US33G6; INFOPLIST_FILE = Runner/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = "Server Box"; @@ -599,7 +606,9 @@ "@executable_path/../Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; + MARKETING_VERSION = 1.0.606; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; + PRODUCT_NAME = ServerBox; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_VERSION = 5.0; }; diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index c36dcc0c..dd822961 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -15,7 +15,7 @@ @@ -31,7 +31,7 @@ @@ -65,7 +65,7 @@ @@ -82,7 +82,7 @@ diff --git a/macos/Runner/Info.plist b/macos/Runner/Info.plist index 4789daa6..e01386de 100644 --- a/macos/Runner/Info.plist +++ b/macos/Runner/Info.plist @@ -20,6 +20,8 @@ $(FLUTTER_BUILD_NAME) CFBundleVersion $(FLUTTER_BUILD_NUMBER) + LSApplicationCategoryType + public.app-category.developer-tools LSMinimumSystemVersion $(MACOSX_DEPLOYMENT_TARGET) NSHumanReadableCopyright diff --git a/pubspec.lock b/pubspec.lock index 82f0c18c..dd8a4d96 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -245,11 +245,12 @@ packages: dartssh2: dependency: "direct main" description: - name: dartssh2 - sha256: "53a230c7dd6f487b704ceef1b29323ad64d19be89e786ccbc81e157a70417a56" - url: "https://pub.dev" - source: hosted - version: "2.8.2" + path: "." + ref: master + resolved-ref: "9e7d3eafb02de28e080be92a5db1761d8251aa66" + url: "https://github.com/TerminalStudio/dartssh2" + source: git + version: "2.9.1-pre" dio: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 01979cb8..ae8e3f09 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,10 @@ dependencies: after_layout: ^1.1.0 url_launcher: ^6.1.8 countly_flutter: ^23.6.0 - dartssh2: ^2.8.2 + dartssh2: + git: + ref: master + url: https://github.com/TerminalStudio/dartssh2 logging: ^1.0.2 circle_chart: git: