#42 new: docker logs

This commit is contained in:
lollipopkit
2023-05-18 22:06:12 +08:00
parent a9fa981e96
commit 472599498e
16 changed files with 178 additions and 127 deletions

View File

@@ -581,6 +581,12 @@ abstract class S {
/// **'Loading files...'** /// **'Loading files...'**
String get loadingFiles; String get loadingFiles;
/// No description provided for @log.
///
/// In en, this message translates to:
/// **'Log'**
String get log;
/// No description provided for @loss. /// No description provided for @loss.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

View File

@@ -267,6 +267,9 @@ class SDe extends S {
@override @override
String get loadingFiles => 'Lädt Dateien...'; String get loadingFiles => 'Lädt Dateien...';
@override
String get log => 'Log';
@override @override
String get loss => 'loss'; String get loss => 'loss';

View File

@@ -267,6 +267,9 @@ class SEn extends S {
@override @override
String get loadingFiles => 'Loading files...'; String get loadingFiles => 'Loading files...';
@override
String get log => 'Log';
@override @override
String get loss => 'loss'; String get loss => 'loss';

View File

@@ -267,6 +267,9 @@ class SZh extends S {
@override @override
String get loadingFiles => '正在加载目录。。。'; String get loadingFiles => '正在加载目录。。。';
@override
String get log => '日志';
@override @override
String get loss => '丢包率'; String get loss => '丢包率';

View File

@@ -360,7 +360,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 = 310; CURRENT_PROJECT_VERSION = 313;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -368,7 +368,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.310; MARKETING_VERSION = 1.0.313;
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";
@@ -491,7 +491,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 = 310; CURRENT_PROJECT_VERSION = 313;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -499,7 +499,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.310; MARKETING_VERSION = 1.0.313;
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";
@@ -516,7 +516,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 = 310; CURRENT_PROJECT_VERSION = 313;
DEVELOPMENT_TEAM = BA88US33G6; DEVELOPMENT_TEAM = BA88US33G6;
ENABLE_BITCODE = NO; ENABLE_BITCODE = NO;
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist"; INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
@@ -524,7 +524,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0.310; MARKETING_VERSION = 1.0.313;
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";

View File

@@ -163,6 +163,14 @@ class DockerProvider extends BusyProvider {
return null; return null;
} }
Future<String> 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 // judge whether to use DOCKER_HOST / sudo
String _wrap(String cmd) { String _wrap(String cmd) {
final dockerHost = dockerStore.getDockerHost(hostId!); final dockerHost = dockerStore.getDockerHost(hostId!);

View File

@@ -64,14 +64,16 @@ class ServerProvider extends BusyProvider {
serverOrder.addAll(serverOrder_.toSet()); serverOrder.addAll(serverOrder_.toSet());
if (serverOrder.length != infos.length) { if (serverOrder.length != infos.length) {
final missed = infos final missed = infos
.where((e) => !serverOrder.contains(e.id)) .where(
.map((e) => e.id) (e) => !serverOrder.contains(e.id),
.toList(); )
.map((e) => e.id);
serverOrder.addAll(missed); serverOrder.addAll(missed);
} }
} else { } else {
serverOrder.addAll(_servers.keys); serverOrder.addAll(_servers.keys);
} }
_settingStore.serverOrder.put(serverOrder);
setBusyState(false); setBusyState(false);
notifyListeners(); notifyListeners();
} }

View File

@@ -2,8 +2,8 @@
class BuildData { class BuildData {
static const String name = "ServerBox"; 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 engine = "3.10.0";
static const String buildAt = "2023-05-14 17:02:42.374273"; static const String buildAt = "2023-05-16 17:56:08.999957";
static const int modifications = 20; static const int modifications = 6;
} }

View File

@@ -1,45 +1,105 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import '../../view/widget/dropdown_menu.dart'; enum ServerTabMenuType {
sftp,
snippet,
pkg,
docker,
edit;
class ServerTabMenuItems { IconData get icon {
static const List<DropdownBtnItem> firstItems = [sftp, snippet, pkg, docker]; switch (this) {
static const List<DropdownBtnItem> secondItems = [edit]; case ServerTabMenuType.sftp:
return Icons.insert_drive_file;
static const sftp = case ServerTabMenuType.snippet:
DropdownBtnItem(text: 'SFTP', icon: Icons.insert_drive_file); return Icons.code;
static const snippet = DropdownBtnItem(text: 'Snippet', icon: Icons.code); case ServerTabMenuType.pkg:
static const pkg = return Icons.system_security_update;
DropdownBtnItem(text: 'Pkg', icon: Icons.system_security_update); case ServerTabMenuType.docker:
static const docker = return Icons.view_agenda;
DropdownBtnItem(text: 'Docker', icon: Icons.view_agenda); case ServerTabMenuType.edit:
static const edit = DropdownBtnItem(text: 'Edit', icon: Icons.edit); return Icons.edit;
}
} }
class DockerMenuItems { String text(S s) {
static const rm = DropdownBtnItem(text: 'Remove', icon: Icons.delete); switch (this) {
static const start = DropdownBtnItem(text: 'Start', icon: Icons.play_arrow); case ServerTabMenuType.sftp:
static const stop = DropdownBtnItem(text: 'Stop', icon: Icons.stop); return 'SFTP';
static const restart = case ServerTabMenuType.snippet:
DropdownBtnItem(text: 'Restart', icon: Icons.restart_alt);
}
String getDropdownBtnText(S s, String text) {
switch (text) {
case 'Snippet':
return s.snippet; return s.snippet;
case 'Pkg': case ServerTabMenuType.pkg:
return s.pkg; return s.pkg;
case 'Remove': case ServerTabMenuType.docker:
return s.delete; return 'Docker';
case 'Start': case ServerTabMenuType.edit:
return s.start;
case 'Stop':
return s.stop;
case 'Edit':
return s.edit; return s.edit;
default:
return text;
} }
} }
PopupMenuItem<ServerTabMenuType> build(S s) => _build(this, icon, text(s));
}
enum DockerMenuType {
start,
stop,
restart,
rm,
logs;
static List<DockerMenuType> 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<DockerMenuType> build(S s) => _build(this, icon, text(s));
}
PopupMenuItem<T> _build<T>(T t, IconData icon, String text) {
return PopupMenuItem<T>(
value: t,
child: Row(
children: [
Icon(icon),
const SizedBox(
width: 10,
),
Text(text),
],
),
);
}

View File

@@ -80,6 +80,7 @@
"license": "Lizenzen", "license": "Lizenzen",
"light": "Hell", "light": "Hell",
"loadingFiles": "Lädt Dateien...", "loadingFiles": "Lädt Dateien...",
"log": "Log",
"loss": "loss", "loss": "loss",
"madeWithLove": "Erstellt mit ❤️ von {myGithub}", "madeWithLove": "Erstellt mit ❤️ von {myGithub}",
"max": "max", "max": "max",

View File

@@ -80,6 +80,7 @@
"license": "License", "license": "License",
"light": "Light", "light": "Light",
"loadingFiles": "Loading files...", "loadingFiles": "Loading files...",
"log": "Log",
"loss": "loss", "loss": "loss",
"madeWithLove": "Made with ❤️ by {myGithub}", "madeWithLove": "Made with ❤️ by {myGithub}",
"max": "max", "max": "max",

View File

@@ -80,6 +80,7 @@
"license": "开源证书", "license": "开源证书",
"light": "亮", "light": "亮",
"loadingFiles": "正在加载目录。。。", "loadingFiles": "正在加载目录。。。",
"log": "日志",
"loss": "丢包率", "loss": "丢包率",
"madeWithLove": "用❤️制作 by {myGithub}", "madeWithLove": "用❤️制作 by {myGithub}",
"max": "最大", "max": "最大",

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/l10n.dart'; import 'package:flutter_gen/gen_l10n/l10n.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:toolbox/core/extension/navigator.dart'; import 'package:toolbox/core/extension/navigator.dart';
import 'package:toolbox/core/utils/misc.dart';
import 'package:toolbox/view/widget/input_field.dart'; import 'package:toolbox/view/widget/input_field.dart';
import '../../core/utils/ui.dart'; import '../../core/utils/ui.dart';
@@ -15,7 +16,6 @@ import '../../data/res/ui.dart';
import '../../data/res/url.dart'; import '../../data/res/url.dart';
import '../../data/store/docker.dart'; import '../../data/store/docker.dart';
import '../../locator.dart'; import '../../locator.dart';
import '../widget/dropdown_menu.dart';
import '../widget/popup_menu.dart'; import '../widget/popup_menu.dart';
import '../widget/round_rect_card.dart'; import '../widget/round_rect_card.dart';
import '../widget/two_line_text.dart'; import '../widget/two_line_text.dart';
@@ -469,30 +469,16 @@ class _DockerManagePageState extends State<DockerManagePage> {
} }
Widget _buildMoreBtn(DockerPsItem dItem, bool busy) { Widget _buildMoreBtn(DockerPsItem dItem, bool busy) {
final item = dItem.running ? DockerMenuItems.stop : DockerMenuItems.start;
return PopupMenu( return PopupMenu(
items: [ items:
PopupMenuItem<DropdownBtnItem>( DockerMenuType.items(dItem.running).map((e) => e.build(_s)).toList(),
value: item, onSelected: (DockerMenuType item) async {
child: item.build(_s),
),
PopupMenuItem<DropdownBtnItem>(
value: DockerMenuItems.rm,
child: DockerMenuItems.rm.build(_s),
),
PopupMenuItem<DropdownBtnItem>(
value: DockerMenuItems.restart,
child: DockerMenuItems.restart.build(_s),
),
],
onSelected: (value) {
if (busy) { if (busy) {
showSnackBar(context, Text(_s.isBusy)); showSnackBar(context, Text(_s.isBusy));
return; return;
} }
final item = value as DropdownBtnItem;
switch (item) { switch (item) {
case DockerMenuItems.rm: case DockerMenuType.rm:
showRoundDialog( showRoundDialog(
context: context, context: context,
child: Text(_s.sureDelete(dItem.name)), child: Text(_s.sureDelete(dItem.name)),
@@ -507,12 +493,30 @@ class _DockerManagePageState extends State<DockerManagePage> {
], ],
); );
break; break;
case DockerMenuItems.start: case DockerMenuType.start:
_docker.start(dItem.containerId); _docker.start(dItem.containerId);
break; break;
case DockerMenuItems.stop: case DockerMenuType.stop:
_docker.stop(dItem.containerId); _docker.stop(dItem.containerId);
break; 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;
} }
}, },
); );

View File

@@ -19,7 +19,6 @@ import '../../../data/res/ui.dart';
import '../../../data/res/url.dart'; import '../../../data/res/url.dart';
import '../../../data/store/setting.dart'; import '../../../data/store/setting.dart';
import '../../../locator.dart'; import '../../../locator.dart';
import '../../widget/dropdown_menu.dart';
import '../../widget/popup_menu.dart'; import '../../widget/popup_menu.dart';
import '../../widget/round_rect_card.dart'; import '../../widget/round_rect_card.dart';
import '../../widget/url_text.dart'; import '../../widget/url_text.dart';
@@ -264,30 +263,16 @@ class _ServerPageState extends State<ServerPage>
Widget _buildMoreBtn(ServerPrivateInfo spi) { Widget _buildMoreBtn(ServerPrivateInfo spi) {
return PopupMenu( return PopupMenu(
items: <PopupMenuEntry>[ items: ServerTabMenuType.values.map((e) => e.build(_s)).toList(),
...ServerTabMenuItems.firstItems.map( onSelected: (ServerTabMenuType value) {
(item) => PopupMenuItem<DropdownBtnItem>( switch (value) {
value: item, case ServerTabMenuType.pkg:
child: item.build(_s),
),
),
const PopupMenuDivider(height: 1),
...ServerTabMenuItems.secondItems.map(
(item) => PopupMenuItem<DropdownBtnItem>(
value: item,
child: item.build(_s),
),
),
],
onSelected: (value) {
switch (value as DropdownBtnItem) {
case ServerTabMenuItems.pkg:
AppRoute(PkgManagePage(spi), 'pkg manage').go(context); AppRoute(PkgManagePage(spi), 'pkg manage').go(context);
break; break;
case ServerTabMenuItems.sftp: case ServerTabMenuType.sftp:
AppRoute(SFTPPage(spi), 'SFTP').go(context); AppRoute(SFTPPage(spi), 'SFTP').go(context);
break; break;
case ServerTabMenuItems.snippet: case ServerTabMenuType.snippet:
showSnippetDialog(context, _s, (s) async { showSnippetDialog(context, _s, (s) async {
final result = await _serverProvider.runSnippet(spi.id, s); final result = await _serverProvider.runSnippet(spi.id, s);
showRoundDialog( showRoundDialog(
@@ -302,10 +287,10 @@ class _ServerPageState extends State<ServerPage>
); );
}); });
break; break;
case ServerTabMenuItems.edit: case ServerTabMenuType.edit:
AppRoute(ServerEditPage(spi: spi), 'Edit server info').go(context); AppRoute(ServerEditPage(spi: spi), 'Edit server info').go(context);
break; break;
case ServerTabMenuItems.docker: case ServerTabMenuType.docker:
AppRoute(DockerManagePage(spi), 'Docker manage').go(context); AppRoute(DockerManagePage(spi), 'Docker manage').go(context);
break; break;
} }

View File

@@ -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),
),
],
);
}

View File

@@ -475,9 +475,9 @@
baseConfigurationReference = C1C758C41C4E208965A68933 /* Pods-RunnerTests.debug.xcconfig */; baseConfigurationReference = C1C758C41C4E208965A68933 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 310; CURRENT_PROJECT_VERSION = 313;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.310; MARKETING_VERSION = 1.0.313;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -490,9 +490,9 @@
baseConfigurationReference = 15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */; baseConfigurationReference = 15AF97DF993E8968098D6EBE /* Pods-RunnerTests.release.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 310; CURRENT_PROJECT_VERSION = 313;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.310; MARKETING_VERSION = 1.0.313;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -505,9 +505,9 @@
baseConfigurationReference = 7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */; baseConfigurationReference = 7CFA7DE7FABA75685DFB6948 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = { buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)"; BUNDLE_LOADER = "$(TEST_HOST)";
CURRENT_PROJECT_VERSION = 310; CURRENT_PROJECT_VERSION = 313;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0.310; MARKETING_VERSION = 1.0.313;
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;