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