mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
opt. & new
- opt.: docker page perf - new: docker stats
This commit is contained in:
@@ -470,7 +470,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 403;
|
||||
CURRENT_PROJECT_VERSION = 404;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -478,7 +478,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.403;
|
||||
MARKETING_VERSION = 1.0.404;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -602,7 +602,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 403;
|
||||
CURRENT_PROJECT_VERSION = 404;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -610,7 +610,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.403;
|
||||
MARKETING_VERSION = 1.0.404;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -628,7 +628,7 @@
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
|
||||
CURRENT_PROJECT_VERSION = 403;
|
||||
CURRENT_PROJECT_VERSION = 404;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = "Runner/Info-$(CONFIGURATION).plist";
|
||||
@@ -636,7 +636,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.403;
|
||||
MARKETING_VERSION = 1.0.404;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
@@ -657,7 +657,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 403;
|
||||
CURRENT_PROJECT_VERSION = 404;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -670,7 +670,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.403;
|
||||
MARKETING_VERSION = 1.0.404;
|
||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
@@ -696,7 +696,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 403;
|
||||
CURRENT_PROJECT_VERSION = 404;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -709,7 +709,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.403;
|
||||
MARKETING_VERSION = 1.0.404;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
@@ -732,7 +732,7 @@
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 403;
|
||||
CURRENT_PROJECT_VERSION = 404;
|
||||
DEVELOPMENT_TEAM = BA88US33G6;
|
||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -745,7 +745,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 1.0.403;
|
||||
MARKETING_VERSION = 1.0.404;
|
||||
MTL_FAST_MATH = YES;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.lollipopkit.toolbox.StatusWidget;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
|
||||
@@ -36,7 +36,8 @@ enum DockerErrType {
|
||||
noClient,
|
||||
notInstalled,
|
||||
invalidVersion,
|
||||
cmdNoPrefix
|
||||
cmdNoPrefix,
|
||||
segmentsNotMatch,
|
||||
}
|
||||
|
||||
class DockerErr extends Err<DockerErrType> {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import '../../res/server_cmd.dart';
|
||||
|
||||
class AppShellFunc {
|
||||
final String name;
|
||||
final String cmd;
|
||||
final String flag;
|
||||
|
||||
const AppShellFunc(this.name, this.cmd, this.flag);
|
||||
|
||||
String get exec => 'sh $shellPath -$flag';
|
||||
}
|
||||
|
||||
typedef AppShellFuncs = List<AppShellFunc>;
|
||||
@@ -39,3 +43,8 @@ esac
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
// enum AppShellFuncType {
|
||||
// status,
|
||||
// docker;
|
||||
// }
|
||||
|
||||
@@ -8,6 +8,10 @@ class DockerPsItem {
|
||||
late String status;
|
||||
late String ports;
|
||||
late String name;
|
||||
String? cpu;
|
||||
String? mem;
|
||||
String? net;
|
||||
String? disk;
|
||||
|
||||
DockerPsItem(
|
||||
this.containerId,
|
||||
@@ -37,6 +41,20 @@ class DockerPsItem {
|
||||
}
|
||||
}
|
||||
|
||||
void parseStats(String rawString) {
|
||||
if (rawString.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final parts = rawString.split(_seperator);
|
||||
if (parts.length != 8) {
|
||||
return;
|
||||
}
|
||||
cpu = parts[2];
|
||||
mem = parts[3];
|
||||
net = parts[5];
|
||||
disk = parts[6];
|
||||
}
|
||||
|
||||
bool get running => status.contains('Up ');
|
||||
|
||||
@override
|
||||
|
||||
@@ -5,11 +5,11 @@ import 'package:dartssh2/dartssh2.dart';
|
||||
import 'package:logging/logging.dart';
|
||||
import 'package:toolbox/core/extension/ssh_client.dart';
|
||||
import 'package:toolbox/core/extension/stringx.dart';
|
||||
import 'package:toolbox/core/extension/uint8list.dart';
|
||||
import 'package:toolbox/core/provider_base.dart';
|
||||
import 'package:toolbox/data/model/docker/image.dart';
|
||||
import 'package:toolbox/data/model/docker/ps.dart';
|
||||
import 'package:toolbox/data/model/app/error.dart';
|
||||
import 'package:toolbox/data/res/server_cmd.dart';
|
||||
import 'package:toolbox/data/store/docker.dart';
|
||||
import 'package:toolbox/locator.dart';
|
||||
|
||||
@@ -18,9 +18,6 @@ final _versionReg = RegExp(r'(Version:)\s+([0-9]+\.[0-9]+\.[0-9]+)');
|
||||
final _editionReg = RegExp(r'(Client:)\s+(.+-.+)');
|
||||
final _dockerPrefixReg = RegExp(r'(sudo )?docker ');
|
||||
|
||||
const _dockerPS = 'docker ps -a';
|
||||
const _dockerImgs = 'docker images';
|
||||
|
||||
final _logger = Logger('DOCKER');
|
||||
|
||||
class DockerProvider extends BusyProvider {
|
||||
@@ -54,49 +51,59 @@ class DockerProvider extends BusyProvider {
|
||||
|
||||
Future<void> refresh() async {
|
||||
if (isBusy) return;
|
||||
final verRaw = await client!.run('docker version'.withLangExport).string;
|
||||
if (verRaw.contains(_dockerNotFound)) {
|
||||
setBusyState();
|
||||
|
||||
var raw = '';
|
||||
await client!.exec(
|
||||
shellFuncDocker.exec,
|
||||
onStderr: _onPwd,
|
||||
onStdout: (data, _) => raw = '$raw$data',
|
||||
);
|
||||
|
||||
if (raw.contains(_dockerNotFound)) {
|
||||
error = DockerErr(type: DockerErrType.notInstalled);
|
||||
setBusyState(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Check result segments count
|
||||
final segments = raw.split(seperator);
|
||||
if (segments.length != dockerCmds.length) {
|
||||
error = DockerErr(type: DockerErrType.segmentsNotMatch);
|
||||
setBusyState(false);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse docker version
|
||||
final verRaw = segments[0];
|
||||
try {
|
||||
version = _versionReg.firstMatch(verRaw)?.group(2);
|
||||
edition = _editionReg.firstMatch(verRaw)?.group(2);
|
||||
} catch (e) {
|
||||
error = DockerErr(type: DockerErrType.unknown, message: e.toString());
|
||||
rethrow;
|
||||
}
|
||||
|
||||
// Parse docker ps
|
||||
final psRaw = segments[1];
|
||||
try {
|
||||
setBusyState();
|
||||
final cmd = _wrap(_dockerPS);
|
||||
|
||||
// run docker ps
|
||||
var raw = '';
|
||||
await client!.exec(
|
||||
cmd,
|
||||
onStderr: _onPwd,
|
||||
onStdout: (data, _) => raw = '$raw$data',
|
||||
);
|
||||
|
||||
// parse result
|
||||
final lines = raw.split('\n');
|
||||
lines.removeAt(0);
|
||||
final lines = psRaw.split('\n');
|
||||
lines.removeWhere((element) => element.isEmpty);
|
||||
lines.removeAt(0);
|
||||
items = lines.map((e) => DockerPsItem.fromRawString(e)).toList();
|
||||
} catch (e) {
|
||||
error = DockerErr(type: DockerErrType.unknown, message: e.toString());
|
||||
rethrow;
|
||||
} finally {
|
||||
setBusyState(false);
|
||||
}
|
||||
|
||||
final imageCmd = _wrap(_dockerImgs);
|
||||
raw = '';
|
||||
await client!.exec(
|
||||
imageCmd,
|
||||
onStderr: _onPwd,
|
||||
onStdout: (data, _) => raw = '$raw$data',
|
||||
);
|
||||
|
||||
final imageLines = raw.split('\n');
|
||||
imageLines.removeAt(0);
|
||||
// Parse docker images
|
||||
final imageRaw = segments[3];
|
||||
try {
|
||||
final imageLines = imageRaw.split('\n');
|
||||
imageLines.removeWhere((element) => element.isEmpty);
|
||||
imageLines.removeAt(0);
|
||||
images = imageLines.map((e) => DockerImage.fromRawStr(e)).toList();
|
||||
} catch (e) {
|
||||
error = DockerErr(type: DockerErrType.unknown, message: e.toString());
|
||||
@@ -104,6 +111,25 @@ class DockerProvider extends BusyProvider {
|
||||
} finally {
|
||||
setBusyState(false);
|
||||
}
|
||||
|
||||
// Parse docker stats
|
||||
final statsRaw = segments[2];
|
||||
try {
|
||||
final statsLines = statsRaw.split('\n');
|
||||
statsLines.removeWhere((element) => element.isEmpty);
|
||||
statsLines.removeAt(0);
|
||||
for (var item in items!) {
|
||||
final statsLine = statsLines.firstWhere(
|
||||
(element) => element.contains(item.containerId),
|
||||
orElse: () => '',
|
||||
);
|
||||
item.parseStats(statsLine);
|
||||
}
|
||||
} catch (e) {
|
||||
error = DockerErr(type: DockerErrType.unknown, message: e.toString());
|
||||
} finally {
|
||||
setBusyState(false);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onPwd(String event, StreamSink<Uint8List> stdin) async {
|
||||
@@ -163,11 +189,11 @@ class DockerProvider extends BusyProvider {
|
||||
return null;
|
||||
}
|
||||
|
||||
// judge whether to use DOCKER_HOST / sudo
|
||||
// judge whether to use DOCKER_HOST
|
||||
String _wrap(String cmd) {
|
||||
final dockerHost = dockerStore.getDockerHost(hostId!);
|
||||
if (dockerHost == null || dockerHost.isEmpty) {
|
||||
return 'sudo $cmd'.withLangExport;
|
||||
return cmd.withLangExport;
|
||||
}
|
||||
return 'export DOCKER_HOST=$dockerHost && $cmd'.withLangExport;
|
||||
}
|
||||
|
||||
@@ -245,8 +245,7 @@ class ServerProvider extends BusyProvider {
|
||||
|
||||
if (s.client == null) return;
|
||||
// run script to get server status
|
||||
raw =
|
||||
await s.client!.run("sh $shellPath -${shellFuncStatus.flag}").string;
|
||||
raw = await s.client!.run(shellFuncStatus.exec).string;
|
||||
segments = raw.split(seperator).map((e) => e.trim()).toList();
|
||||
if (raw.isEmpty || segments.length != CmdType.values.length) {
|
||||
s.state = ServerState.failed;
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
class BuildData {
|
||||
static const String name = "ServerBox";
|
||||
static const int build = 403;
|
||||
static const int build = 404;
|
||||
static const String engine = "3.10.6";
|
||||
static const String buildAt = "2023-08-02 19:59:01.356049";
|
||||
static const String buildAt = "2023-08-02 20:14:57.880607";
|
||||
static const int modifications = 4;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ const shellPath = '$serverBoxDir/mobile_app.sh';
|
||||
const echoPWD = 'echo \$PWD';
|
||||
|
||||
enum CmdType {
|
||||
export,
|
||||
net,
|
||||
sys,
|
||||
cpu,
|
||||
@@ -23,7 +22,6 @@ enum CmdType {
|
||||
}
|
||||
|
||||
const _cmdList = [
|
||||
'export LANG=en_US.utf-8',
|
||||
'cat /proc/net/dev && date +%s',
|
||||
'cat /etc/os-release | grep PRETTY_NAME',
|
||||
'cat /proc/stat | grep cpu',
|
||||
@@ -43,30 +41,32 @@ final shellFuncStatus = AppShellFunc(
|
||||
's',
|
||||
);
|
||||
|
||||
// Check if `htop` is installed.
|
||||
// Then app open SSH term and use `htop` or `ps` to see process.
|
||||
const shellFuncProcess = AppShellFunc(
|
||||
'process',
|
||||
'''
|
||||
if command -v htop &> /dev/null
|
||||
then
|
||||
htop
|
||||
else
|
||||
top
|
||||
fi
|
||||
''',
|
||||
'p',
|
||||
const dockerCmds = [
|
||||
'docker version',
|
||||
'docker ps -a',
|
||||
'docker stats --no-stream',
|
||||
'docker image ls',
|
||||
];
|
||||
|
||||
final shellFuncDocker = AppShellFunc(
|
||||
// `dockeR` -> avoid conflict with `docker` command
|
||||
// 以防止循环递归
|
||||
'dockeR',
|
||||
dockerCmds.join('\necho $seperator\n'),
|
||||
'd',
|
||||
);
|
||||
|
||||
final _generated = [
|
||||
shellFuncStatus,
|
||||
shellFuncProcess,
|
||||
shellFuncDocker,
|
||||
].generate;
|
||||
|
||||
final shellCmd = """
|
||||
# Script for app `${BuildData.name} v1.0.${BuildData.build}`
|
||||
# Delete this file while app is running will cause app crash
|
||||
|
||||
export LANG=en_US.utf-8
|
||||
|
||||
$_generated
|
||||
""";
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import 'package:nil/nil.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:toolbox/core/extension/navigator.dart';
|
||||
import 'package:toolbox/core/route.dart';
|
||||
import 'package:toolbox/data/model/docker/image.dart';
|
||||
import 'package:toolbox/view/page/ssh/term.dart';
|
||||
import 'package:toolbox/view/widget/input_field.dart';
|
||||
|
||||
@@ -69,13 +70,13 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
title: TwoLineText(up: 'Docker', down: widget.spi.name),
|
||||
actions: [
|
||||
IconButton(
|
||||
onPressed: () => _docker.refresh(),
|
||||
onPressed: _docker.refresh,
|
||||
icon: const Icon(Icons.refresh),
|
||||
)
|
||||
],
|
||||
),
|
||||
body: _buildMain(),
|
||||
floatingActionButton: _buildFAB(),
|
||||
floatingActionButton: _docker.error == null ? _buildFAB() : null,
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -177,6 +178,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
}
|
||||
|
||||
void onSubmitted() {
|
||||
context.pop();
|
||||
if (_textController.text == '') {
|
||||
showRoundDialog(
|
||||
context: context,
|
||||
@@ -191,7 +193,6 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
);
|
||||
return;
|
||||
}
|
||||
context.pop();
|
||||
}
|
||||
|
||||
Future<String> onPwdRequest() async {
|
||||
@@ -215,7 +216,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
child: Text(_s.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => onSubmitted(),
|
||||
onPressed: onSubmitted,
|
||||
child: Text(
|
||||
_s.ok,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
@@ -249,7 +250,11 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
);
|
||||
}
|
||||
if (_docker.items == null || _docker.images == null) {
|
||||
_docker.refresh();
|
||||
Future.delayed(const Duration(milliseconds: 177), () {
|
||||
if (mounted) {
|
||||
_docker.refresh();
|
||||
}
|
||||
});
|
||||
return centerLoading;
|
||||
}
|
||||
|
||||
@@ -260,7 +265,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
_docker.edition ?? _s.unknown,
|
||||
_docker.version ?? _s.unknown,
|
||||
),
|
||||
_buildPsItems(),
|
||||
..._buildPsItems(),
|
||||
_buildImages(),
|
||||
_buildEditHost(),
|
||||
].map((e) => RoundRectCard(e)));
|
||||
@@ -275,50 +280,7 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
if (_docker.images == null) {
|
||||
return nil;
|
||||
}
|
||||
final items = _docker.images!
|
||||
.map(
|
||||
(e) => ListTile(
|
||||
title: Text(e.repo),
|
||||
subtitle: Text('${e.tag} - ${e.size}', style: grey),
|
||||
trailing: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
alignment: Alignment.centerRight,
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () async {
|
||||
showRoundDialog(
|
||||
context: context,
|
||||
title: Text(_s.attention),
|
||||
child: Text(_s.sureDelete(e.repo)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => context.pop(),
|
||||
child: Text(_s.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
context.pop();
|
||||
final result = await _docker.run(
|
||||
'docker rmi ${e.id} -f',
|
||||
);
|
||||
if (result != null) {
|
||||
showSnackBar(
|
||||
context,
|
||||
Text(result.message ?? _s.unknownError),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
_s.ok,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
final items = _docker.images!.map(_buildImageItem).toList();
|
||||
items.insert(
|
||||
0,
|
||||
ListTile(
|
||||
@@ -332,6 +294,51 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
return Column(children: items);
|
||||
}
|
||||
|
||||
Widget _buildImageItem(DockerImage e) {
|
||||
return ListTile(
|
||||
title: Text(e.repo),
|
||||
subtitle: Text('${e.tag} - ${e.size}', style: grey),
|
||||
trailing: IconButton(
|
||||
padding: EdgeInsets.zero,
|
||||
alignment: Alignment.centerRight,
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () => _showImageRmDialog(e),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showImageRmDialog(DockerImage e) {
|
||||
showRoundDialog(
|
||||
context: context,
|
||||
title: Text(_s.attention),
|
||||
child: Text(_s.sureDelete(e.repo)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => context.pop(),
|
||||
child: Text(_s.cancel),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () async {
|
||||
context.pop();
|
||||
final result = await _docker.run(
|
||||
'docker rmi ${e.id} -f',
|
||||
);
|
||||
if (result != null) {
|
||||
showSnackBar(
|
||||
context,
|
||||
Text(result.message ?? _s.unknownError),
|
||||
);
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
_s.ok,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLoading() {
|
||||
if (!_docker.isBusy) return nil;
|
||||
return Padding(
|
||||
@@ -440,20 +447,8 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPsItems() {
|
||||
final items = _docker.items!.map(
|
||||
(item) {
|
||||
return ListTile(
|
||||
title: Text(item.name),
|
||||
isThreeLine: true,
|
||||
subtitle: Text(
|
||||
'${item.image}\n${item.status}',
|
||||
style: textSize13Grey,
|
||||
),
|
||||
trailing: _buildMoreBtn(item, _docker.isBusy),
|
||||
);
|
||||
},
|
||||
).toList();
|
||||
List<Widget> _buildPsItems() {
|
||||
final items = _docker.items!.map(_buildPsItem).toList();
|
||||
items.insert(
|
||||
0,
|
||||
ListTile(
|
||||
@@ -461,8 +456,39 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
subtitle: Text(_buildSubtitle(_docker.items!), style: grey),
|
||||
),
|
||||
);
|
||||
return items;
|
||||
}
|
||||
|
||||
Widget _buildPsItem(DockerPsItem item) {
|
||||
return Column(
|
||||
children: items,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(item.name),
|
||||
subtitle: Text(
|
||||
'${item.image}\n${item.status}',
|
||||
style: textSize13Grey,
|
||||
),
|
||||
trailing: _buildMoreBtn(item, _docker.isBusy),
|
||||
),
|
||||
_buildPsItemStat(item),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildPsItemStat(DockerPsItem item) {
|
||||
if (!item.running) return const SizedBox();
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(left: 17, bottom: 11, right: 17),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(item.cpu ?? _s.unknown, style: grey),
|
||||
Text(item.mem ?? _s.unknown, style: grey),
|
||||
Text(item.net ?? _s.unknown, style: grey),
|
||||
Text(item.disk ?? _s.unknown, style: grey),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -475,9 +475,9 @@
|
||||
baseConfigurationReference = C1C758C41C4E208965A68933 /* Pods-RunnerTests.debug.xcconfig */;
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CURRENT_PROJECT_VERSION = 403;
|
||||
CURRENT_PROJECT_VERSION = 404;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0.403;
|
||||
MARKETING_VERSION = 1.0.404;
|
||||
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 = 403;
|
||||
CURRENT_PROJECT_VERSION = 404;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0.403;
|
||||
MARKETING_VERSION = 1.0.404;
|
||||
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 = 403;
|
||||
CURRENT_PROJECT_VERSION = 404;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0.403;
|
||||
MARKETING_VERSION = 1.0.404;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = tech.lolli.serverBox.RunnerTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_VERSION = 5.0;
|
||||
|
||||
Reference in New Issue
Block a user