From 472599498e006de7b41745b57a2d00c30dead50f Mon Sep 17 00:00:00 2001 From: lollipopkit Date: Thu, 18 May 2023 22:06:12 +0800 Subject: [PATCH] #42 new: docker `logs` --- .dart_tool/flutter_gen/gen_l10n/l10n.dart | 6 + .dart_tool/flutter_gen/gen_l10n/l10n_de.dart | 3 + .dart_tool/flutter_gen/gen_l10n/l10n_en.dart | 3 + .dart_tool/flutter_gen/gen_l10n/l10n_zh.dart | 3 + ios/Runner.xcodeproj/project.pbxproj | 12 +- lib/data/provider/docker.dart | 8 ++ lib/data/provider/server.dart | 8 +- lib/data/res/build_data.dart | 6 +- lib/data/res/menu.dart | 138 +++++++++++++------ lib/l10n/app_de.arb | 1 + lib/l10n/app_en.arb | 1 + lib/l10n/app_zh.arb | 1 + lib/view/page/docker.dart | 46 ++++--- lib/view/page/server/tab.dart | 31 ++--- lib/view/widget/dropdown_menu.dart | 26 ---- macos/Runner.xcodeproj/project.pbxproj | 12 +- 16 files changed, 178 insertions(+), 127 deletions(-) delete mode 100644 lib/view/widget/dropdown_menu.dart diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n.dart b/.dart_tool/flutter_gen/gen_l10n/l10n.dart index 405d65ea..f758673b 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n.dart @@ -581,6 +581,12 @@ abstract class S { /// **'Loading files...'** String get loadingFiles; + /// No description provided for @log. + /// + /// In en, this message translates to: + /// **'Log'** + String get log; + /// No description provided for @loss. /// /// 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 29746f46..9d55e7ac 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_de.dart @@ -267,6 +267,9 @@ class SDe extends S { @override String get loadingFiles => 'Lädt Dateien...'; + @override + String get log => 'Log'; + @override String get loss => 'loss'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart index 6a72e3ba..341a6478 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_en.dart @@ -267,6 +267,9 @@ class SEn extends S { @override String get loadingFiles => 'Loading files...'; + @override + String get log => 'Log'; + @override String get loss => 'loss'; diff --git a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart index 5fd34182..bfc77db7 100644 --- a/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart +++ b/.dart_tool/flutter_gen/gen_l10n/l10n_zh.dart @@ -267,6 +267,9 @@ class SZh extends S { @override String get loadingFiles => '正在加载目录。。。'; + @override + String get log => '日志'; + @override String get loss => '丢包率'; diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index e6e10f2c..39375b33 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -360,7 +360,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 313; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -368,7 +368,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.310; + MARKETING_VERSION = 1.0.313; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -491,7 +491,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 313; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -499,7 +499,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.310; + MARKETING_VERSION = 1.0.313; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; @@ -516,7 +516,7 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 313; DEVELOPMENT_TEAM = BA88US33G6; ENABLE_BITCODE = NO; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; @@ -524,7 +524,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.310; + MARKETING_VERSION = 1.0.313; PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; diff --git a/lib/data/provider/docker.dart b/lib/data/provider/docker.dart index 5f4efd2c..1176cc6f 100644 --- a/lib/data/provider/docker.dart +++ b/lib/data/provider/docker.dart @@ -163,6 +163,14 @@ class DockerProvider extends BusyProvider { return null; } + Future logs(String id) async { + setBusyState(); + final cmd = _wrap('docker logs $id'); + final result = await client!.run(cmd); + setBusyState(false); + return result.string; + } + // judge whether to use DOCKER_HOST / sudo String _wrap(String cmd) { final dockerHost = dockerStore.getDockerHost(hostId!); diff --git a/lib/data/provider/server.dart b/lib/data/provider/server.dart index b6bb2e9e..0f76cef9 100644 --- a/lib/data/provider/server.dart +++ b/lib/data/provider/server.dart @@ -64,14 +64,16 @@ class ServerProvider extends BusyProvider { serverOrder.addAll(serverOrder_.toSet()); if (serverOrder.length != infos.length) { final missed = infos - .where((e) => !serverOrder.contains(e.id)) - .map((e) => e.id) - .toList(); + .where( + (e) => !serverOrder.contains(e.id), + ) + .map((e) => e.id); serverOrder.addAll(missed); } } else { serverOrder.addAll(_servers.keys); } + _settingStore.serverOrder.put(serverOrder); setBusyState(false); notifyListeners(); } diff --git a/lib/data/res/build_data.dart b/lib/data/res/build_data.dart index 6d307696..af4d46c0 100644 --- a/lib/data/res/build_data.dart +++ b/lib/data/res/build_data.dart @@ -2,8 +2,8 @@ class BuildData { static const String name = "ServerBox"; - static const int build = 310; + static const int build = 313; static const String engine = "3.10.0"; - static const String buildAt = "2023-05-14 17:02:42.374273"; - static const int modifications = 20; + static const String buildAt = "2023-05-16 17:56:08.999957"; + static const int modifications = 6; } diff --git a/lib/data/res/menu.dart b/lib/data/res/menu.dart index fed7a0f6..a36c2876 100644 --- a/lib/data/res/menu.dart +++ b/lib/data/res/menu.dart @@ -1,45 +1,105 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; -import '../../view/widget/dropdown_menu.dart'; +enum ServerTabMenuType { + sftp, + snippet, + pkg, + docker, + edit; -class ServerTabMenuItems { - static const List firstItems = [sftp, snippet, pkg, docker]; - static const List secondItems = [edit]; - - static const sftp = - DropdownBtnItem(text: 'SFTP', icon: Icons.insert_drive_file); - static const snippet = DropdownBtnItem(text: 'Snippet', icon: Icons.code); - static const pkg = - DropdownBtnItem(text: 'Pkg', icon: Icons.system_security_update); - static const docker = - DropdownBtnItem(text: 'Docker', icon: Icons.view_agenda); - static const edit = DropdownBtnItem(text: 'Edit', icon: Icons.edit); -} - -class DockerMenuItems { - static const rm = DropdownBtnItem(text: 'Remove', icon: Icons.delete); - static const start = DropdownBtnItem(text: 'Start', icon: Icons.play_arrow); - static const stop = DropdownBtnItem(text: 'Stop', icon: Icons.stop); - static const restart = - DropdownBtnItem(text: 'Restart', icon: Icons.restart_alt); -} - -String getDropdownBtnText(S s, String text) { - switch (text) { - case 'Snippet': - return s.snippet; - case 'Pkg': - return s.pkg; - case 'Remove': - return s.delete; - case 'Start': - return s.start; - case 'Stop': - return s.stop; - case 'Edit': - return s.edit; - default: - return text; + IconData get icon { + switch (this) { + case ServerTabMenuType.sftp: + return Icons.insert_drive_file; + case ServerTabMenuType.snippet: + return Icons.code; + case ServerTabMenuType.pkg: + return Icons.system_security_update; + case ServerTabMenuType.docker: + return Icons.view_agenda; + case ServerTabMenuType.edit: + return Icons.edit; + } } + + String text(S s) { + switch (this) { + case ServerTabMenuType.sftp: + return 'SFTP'; + case ServerTabMenuType.snippet: + return s.snippet; + case ServerTabMenuType.pkg: + return s.pkg; + case ServerTabMenuType.docker: + return 'Docker'; + case ServerTabMenuType.edit: + return s.edit; + } + } + + PopupMenuItem build(S s) => _build(this, icon, text(s)); +} + +enum DockerMenuType { + start, + stop, + restart, + rm, + logs; + + static List items(bool running) { + if (running) { + return [stop, restart, rm, logs]; + } else { + return [start, rm, logs]; + } + } + + IconData get icon { + switch (this) { + case DockerMenuType.start: + return Icons.play_arrow; + case DockerMenuType.stop: + return Icons.stop; + case DockerMenuType.restart: + return Icons.restart_alt; + case DockerMenuType.rm: + return Icons.delete; + case DockerMenuType.logs: + return Icons.logo_dev; + } + } + + String text(S s) { + switch (this) { + case DockerMenuType.start: + return s.start; + case DockerMenuType.stop: + return s.stop; + case DockerMenuType.restart: + return s.restart; + case DockerMenuType.rm: + return s.delete; + case DockerMenuType.logs: + return s.log; + } + } + + PopupMenuItem build(S s) => _build(this, icon, text(s)); +} + +PopupMenuItem _build(T t, IconData icon, String text) { + return PopupMenuItem( + value: t, + child: Row( + children: [ + Icon(icon), + const SizedBox( + width: 10, + ), + Text(text), + ], + ), + ); } diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index f1b93d23..16c16c75 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -80,6 +80,7 @@ "license": "Lizenzen", "light": "Hell", "loadingFiles": "Lädt Dateien...", + "log": "Log", "loss": "loss", "madeWithLove": "Erstellt mit ❤️ von {myGithub}", "max": "max", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 72ca3857..df8e9231 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -80,6 +80,7 @@ "license": "License", "light": "Light", "loadingFiles": "Loading files...", + "log": "Log", "loss": "loss", "madeWithLove": "Made with ❤️ by {myGithub}", "max": "max", diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 75effee6..21da6ed1 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -80,6 +80,7 @@ "license": "开源证书", "light": "亮", "loadingFiles": "正在加载目录。。。", + "log": "日志", "loss": "丢包率", "madeWithLove": "用❤️制作 by {myGithub}", "max": "最大", diff --git a/lib/view/page/docker.dart b/lib/view/page/docker.dart index 7ec479a7..142d9ea6 100644 --- a/lib/view/page/docker.dart +++ b/lib/view/page/docker.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:provider/provider.dart'; import 'package:toolbox/core/extension/navigator.dart'; +import 'package:toolbox/core/utils/misc.dart'; import 'package:toolbox/view/widget/input_field.dart'; import '../../core/utils/ui.dart'; @@ -15,7 +16,6 @@ import '../../data/res/ui.dart'; import '../../data/res/url.dart'; import '../../data/store/docker.dart'; import '../../locator.dart'; -import '../widget/dropdown_menu.dart'; import '../widget/popup_menu.dart'; import '../widget/round_rect_card.dart'; import '../widget/two_line_text.dart'; @@ -469,30 +469,16 @@ class _DockerManagePageState extends State { } Widget _buildMoreBtn(DockerPsItem dItem, bool busy) { - final item = dItem.running ? DockerMenuItems.stop : DockerMenuItems.start; return PopupMenu( - items: [ - PopupMenuItem( - value: item, - child: item.build(_s), - ), - PopupMenuItem( - value: DockerMenuItems.rm, - child: DockerMenuItems.rm.build(_s), - ), - PopupMenuItem( - value: DockerMenuItems.restart, - child: DockerMenuItems.restart.build(_s), - ), - ], - onSelected: (value) { + items: + DockerMenuType.items(dItem.running).map((e) => e.build(_s)).toList(), + onSelected: (DockerMenuType item) async { if (busy) { showSnackBar(context, Text(_s.isBusy)); return; } - final item = value as DropdownBtnItem; switch (item) { - case DockerMenuItems.rm: + case DockerMenuType.rm: showRoundDialog( context: context, child: Text(_s.sureDelete(dItem.name)), @@ -507,12 +493,30 @@ class _DockerManagePageState extends State { ], ); break; - case DockerMenuItems.start: + case DockerMenuType.start: _docker.start(dItem.containerId); break; - case DockerMenuItems.stop: + case DockerMenuType.stop: _docker.stop(dItem.containerId); break; + case DockerMenuType.logs: + final logs = await _docker.logs(dItem.containerId); + showRoundDialog( + context: context, + child: SingleChildScrollView( + child: Text(logs), + ), + actions: [ + TextButton( + onPressed: () => copy2Clipboard(logs), + child: Text(_s.copy), + ) + ], + ); + break; + case DockerMenuType.restart: + _docker.restart(dItem.containerId); + break; } }, ); diff --git a/lib/view/page/server/tab.dart b/lib/view/page/server/tab.dart index ff382a0c..4407b879 100644 --- a/lib/view/page/server/tab.dart +++ b/lib/view/page/server/tab.dart @@ -19,7 +19,6 @@ import '../../../data/res/ui.dart'; import '../../../data/res/url.dart'; import '../../../data/store/setting.dart'; import '../../../locator.dart'; -import '../../widget/dropdown_menu.dart'; import '../../widget/popup_menu.dart'; import '../../widget/round_rect_card.dart'; import '../../widget/url_text.dart'; @@ -264,30 +263,16 @@ class _ServerPageState extends State Widget _buildMoreBtn(ServerPrivateInfo spi) { return PopupMenu( - items: [ - ...ServerTabMenuItems.firstItems.map( - (item) => PopupMenuItem( - value: item, - child: item.build(_s), - ), - ), - const PopupMenuDivider(height: 1), - ...ServerTabMenuItems.secondItems.map( - (item) => PopupMenuItem( - value: item, - child: item.build(_s), - ), - ), - ], - onSelected: (value) { - switch (value as DropdownBtnItem) { - case ServerTabMenuItems.pkg: + items: ServerTabMenuType.values.map((e) => e.build(_s)).toList(), + onSelected: (ServerTabMenuType value) { + switch (value) { + case ServerTabMenuType.pkg: AppRoute(PkgManagePage(spi), 'pkg manage').go(context); break; - case ServerTabMenuItems.sftp: + case ServerTabMenuType.sftp: AppRoute(SFTPPage(spi), 'SFTP').go(context); break; - case ServerTabMenuItems.snippet: + case ServerTabMenuType.snippet: showSnippetDialog(context, _s, (s) async { final result = await _serverProvider.runSnippet(spi.id, s); showRoundDialog( @@ -302,10 +287,10 @@ class _ServerPageState extends State ); }); break; - case ServerTabMenuItems.edit: + case ServerTabMenuType.edit: AppRoute(ServerEditPage(spi: spi), 'Edit server info').go(context); break; - case ServerTabMenuItems.docker: + case ServerTabMenuType.docker: AppRoute(DockerManagePage(spi), 'Docker manage').go(context); break; } diff --git a/lib/view/widget/dropdown_menu.dart b/lib/view/widget/dropdown_menu.dart deleted file mode 100644 index a2875be1..00000000 --- a/lib/view/widget/dropdown_menu.dart +++ /dev/null @@ -1,26 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_gen/gen_l10n/l10n.dart'; - -import '../../data/res/menu.dart'; - -class DropdownBtnItem { - final String text; - final IconData icon; - - const DropdownBtnItem({ - required this.text, - required this.icon, - }); - - Widget build(S s) => Row( - children: [ - Icon(icon), - const SizedBox( - width: 10, - ), - Text( - getDropdownBtnText(s, text), - ), - ], - ); -} diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 97cda78f..85683495 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -475,9 +475,9 @@ baseConfigurationReference = C1C758C41C4E208965A68933 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 313; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0.310; + MARKETING_VERSION = 1.0.313; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; @@ -490,9 +490,9 @@ baseConfigurationReference = 15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 313; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0.310; + MARKETING_VERSION = 1.0.313; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; @@ -505,9 +505,9 @@ baseConfigurationReference = 7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; - CURRENT_PROJECT_VERSION = 310; + CURRENT_PROJECT_VERSION = 313; GENERATE_INFOPLIST_FILE = YES; - MARKETING_VERSION = 1.0.310; + MARKETING_VERSION = 1.0.313; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0;