mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 15:24:35 +01:00
support docker image management
This commit is contained in:
47
lib/data/model/docker/image.dart
Normal file
47
lib/data/model/docker/image.dart
Normal file
@@ -0,0 +1,47 @@
|
||||
final _dockerImageReg = RegExp(r'(\S+) +(\S+) +(\S+) +(.+) +(\S+)');
|
||||
class DockerImage {
|
||||
final String repo;
|
||||
final String tag;
|
||||
final String id;
|
||||
final String created;
|
||||
final String size;
|
||||
|
||||
static final Map<String, DockerImage> _cache = <String, DockerImage>{};
|
||||
|
||||
DockerImage({
|
||||
required this.repo,
|
||||
required this.tag,
|
||||
required this.id,
|
||||
required this.created,
|
||||
required this.size,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'repo': repo,
|
||||
'tag': tag,
|
||||
'id': id,
|
||||
'created': created,
|
||||
'size': size,
|
||||
};
|
||||
}
|
||||
|
||||
factory DockerImage.fromRawStr(String raw) {
|
||||
return _cache.putIfAbsent(raw, () => _parse(raw));
|
||||
}
|
||||
|
||||
static DockerImage _parse(String raw) {
|
||||
final match = _dockerImageReg.firstMatch(raw);
|
||||
if (match == null) {
|
||||
throw Exception('Invalid docker image: $raw');
|
||||
}
|
||||
return DockerImage(
|
||||
repo: match.group(1)!,
|
||||
tag: match.group(2)!,
|
||||
id: match.group(3)!,
|
||||
created: match.group(4)!,
|
||||
size: match.group(5)!,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ 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/res/error.dart';
|
||||
import 'package:toolbox/data/store/docker.dart';
|
||||
@@ -25,11 +26,13 @@ class DockerProvider extends BusyProvider {
|
||||
SSHClient? client;
|
||||
String? userName;
|
||||
List<DockerPsItem>? items;
|
||||
List<DockerImage>? images;
|
||||
String? version;
|
||||
String? edition;
|
||||
DockerErr? error;
|
||||
PwdRequestFunc? onPwdReq;
|
||||
String? hostId;
|
||||
String? runLog;
|
||||
bool isRequestingPwd = false;
|
||||
|
||||
void init(SSHClient client, String userName, PwdRequestFunc onPwdReq,
|
||||
@@ -43,7 +46,7 @@ class DockerProvider extends BusyProvider {
|
||||
void clear() {
|
||||
client = userName = error = items = version = edition = onPwdReq = null;
|
||||
isRequestingPwd = false;
|
||||
hostId = null;
|
||||
hostId = runLog = images = null;
|
||||
}
|
||||
|
||||
Future<void> refresh() async {
|
||||
@@ -62,14 +65,7 @@ class DockerProvider extends BusyProvider {
|
||||
}
|
||||
|
||||
try {
|
||||
// judge whether to use DOCKER_HOST / sudo
|
||||
final dockerHost = locator<DockerStore>().getDockerHost(hostId!);
|
||||
final cmd = () {
|
||||
if (dockerHost == null || dockerHost.isEmpty) {
|
||||
return 'sudo -S $_dockerPS'.withLangExport;
|
||||
}
|
||||
return 'export DOCKER_HOST=$dockerHost && $_dockerPS'.withLangExport;
|
||||
}();
|
||||
final cmd = _wrap(_dockerPS);
|
||||
|
||||
// run docker ps
|
||||
var raw = '';
|
||||
@@ -84,6 +80,19 @@ class DockerProvider extends BusyProvider {
|
||||
lines.removeAt(0);
|
||||
lines.removeWhere((element) => element.isEmpty);
|
||||
items = lines.map((e) => DockerPsItem.fromRawString(e)).toList();
|
||||
|
||||
final imageCmd = _wrap('docker image ls');
|
||||
raw = '';
|
||||
await client!.exec(
|
||||
imageCmd,
|
||||
onStderr: _onPwd,
|
||||
onStdout: (data, _) => raw = '$raw$data',
|
||||
);
|
||||
|
||||
final imageLines = raw.split('\n');
|
||||
imageLines.removeAt(0);
|
||||
imageLines.removeWhere((element) => element.isEmpty);
|
||||
images = imageLines.map((e) => DockerImage.fromRawStr(e)).toList();
|
||||
} catch (e) {
|
||||
error = DockerErr(type: DockerErrType.unknown, message: e.toString());
|
||||
rethrow;
|
||||
@@ -119,26 +128,36 @@ class DockerProvider extends BusyProvider {
|
||||
}
|
||||
setBusyState();
|
||||
|
||||
runLog = '';
|
||||
final errs = <String>[];
|
||||
final code = await client!.exec(
|
||||
_wrapHost(cmd),
|
||||
onStderr: _onPwd,
|
||||
_wrap(cmd),
|
||||
onStderr: (data, sink) {
|
||||
_onPwd(data, sink);
|
||||
errs.add(data);
|
||||
},
|
||||
onStdout: (data, _) {
|
||||
runLog = '$runLog$data';
|
||||
notifyListeners();
|
||||
},
|
||||
);
|
||||
runLog = null;
|
||||
|
||||
if (code != 0) {
|
||||
setBusyState(false);
|
||||
return DockerErr(type: DockerErrType.unknown, message: errs.join('\n'));
|
||||
return DockerErr(type: DockerErrType.unknown, message: errs.join('\n').trim());
|
||||
}
|
||||
await refresh();
|
||||
setBusyState(false);
|
||||
return null;
|
||||
}
|
||||
|
||||
String _wrapHost(String cmd) {
|
||||
// judge whether to use DOCKER_HOST / sudo
|
||||
String _wrap(String cmd) {
|
||||
final dockerHost = locator<DockerStore>().getDockerHost(hostId!);
|
||||
if (dockerHost == null || dockerHost.isEmpty) {
|
||||
return 'sudo $cmd';
|
||||
return 'sudo $cmd'.withLangExport;
|
||||
}
|
||||
return 'export DOCKER_HOST=$dockerHost && $cmd';
|
||||
return 'export DOCKER_HOST=$dockerHost && $cmd'.withLangExport;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,37 +22,39 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
|
||||
static String m0(fileName) => "Download [${fileName}] to local?";
|
||||
|
||||
static String m1(runningCount, stoppedCount) =>
|
||||
static String m1(count) => "${count} images";
|
||||
|
||||
static String m2(runningCount, stoppedCount) =>
|
||||
"${runningCount} running, ${stoppedCount} container stopped.";
|
||||
|
||||
static String m2(count) => "${count} container running.";
|
||||
static String m3(count) => "${count} container running.";
|
||||
|
||||
static String m3(percent, size) => "${percent}% of ${size}";
|
||||
static String m4(percent, size) => "${percent}% of ${size}";
|
||||
|
||||
static String m4(count) => "Found ${count} update";
|
||||
static String m5(count) => "Found ${count} update";
|
||||
|
||||
static String m5(code) => "request failed, status code: ${code}";
|
||||
static String m6(code) => "request failed, status code: ${code}";
|
||||
|
||||
static String m6(url) =>
|
||||
static String m7(url) =>
|
||||
"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}.";
|
||||
|
||||
static String m7(myGithub) => "\nMade with ❤️ by ${myGithub}";
|
||||
static String m8(myGithub) => "\nMade with ❤️ by ${myGithub}";
|
||||
|
||||
static String m8(url) => "Please report bugs on ${url}";
|
||||
static String m9(url) => "Please report bugs on ${url}";
|
||||
|
||||
static String m9(date) => "Are you sure to restore from ${date} ?";
|
||||
static String m10(date) => "Are you sure to restore from ${date} ?";
|
||||
|
||||
static String m10(time) => "Spent time: ${time}";
|
||||
static String m11(time) => "Spent time: ${time}";
|
||||
|
||||
static String m11(name) => "Are you sure to delete [${name}]?";
|
||||
static String m12(name) => "Are you sure to delete [${name}]?";
|
||||
|
||||
static String m12(server) => "Are you sure to delete server [${server}]?";
|
||||
static String m13(server) => "Are you sure to delete server [${server}]?";
|
||||
|
||||
static String m13(build) => "Found: v1.0.${build}, click to update";
|
||||
static String m14(build) => "Found: v1.0.${build}, click to update";
|
||||
|
||||
static String m14(build) => "Current: v1.0.${build}";
|
||||
static String m15(build) => "Current: v1.0.${build}";
|
||||
|
||||
static String m15(build) => "Current: v1.0.${build}, is up to date";
|
||||
static String m16(build) => "Current: v1.0.${build}, is up to date";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
@@ -102,14 +104,15 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"dockerEmptyRunningItems": MessageLookupByLibrary.simpleMessage(
|
||||
"No running container. \nIt may be that the env DOCKER_HOST is not read correctly. You can found it by running `echo \$DOCKER_HOST` in terminal."),
|
||||
"dockerImage": MessageLookupByLibrary.simpleMessage("Image"),
|
||||
"dockerImagesFmt": m1,
|
||||
"dockerNotInstalled":
|
||||
MessageLookupByLibrary.simpleMessage("Docker not installed"),
|
||||
"dockerStatusRunningAndStoppedFmt": m1,
|
||||
"dockerStatusRunningFmt": m2,
|
||||
"dockerStatusRunningAndStoppedFmt": m2,
|
||||
"dockerStatusRunningFmt": m3,
|
||||
"download": MessageLookupByLibrary.simpleMessage("Download"),
|
||||
"downloadFinished":
|
||||
MessageLookupByLibrary.simpleMessage("Download finished"),
|
||||
"downloadStatus": m3,
|
||||
"downloadStatus": m4,
|
||||
"edit": MessageLookupByLibrary.simpleMessage("Edit"),
|
||||
"encode": MessageLookupByLibrary.simpleMessage("Encode"),
|
||||
"error": MessageLookupByLibrary.simpleMessage("Error"),
|
||||
@@ -124,12 +127,13 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"fieldMustNotEmpty": MessageLookupByLibrary.simpleMessage(
|
||||
"These fields must not be empty."),
|
||||
"files": MessageLookupByLibrary.simpleMessage("Files"),
|
||||
"foundNUpdate": m4,
|
||||
"foundNUpdate": m5,
|
||||
"go": MessageLookupByLibrary.simpleMessage("Go"),
|
||||
"goSftpDlPage":
|
||||
MessageLookupByLibrary.simpleMessage("Go to SFTP download page?"),
|
||||
"host": MessageLookupByLibrary.simpleMessage("Host"),
|
||||
"httpFailedWithCode": m5,
|
||||
"httpFailedWithCode": m6,
|
||||
"imagesList": MessageLookupByLibrary.simpleMessage("Images list"),
|
||||
"import": MessageLookupByLibrary.simpleMessage("Import"),
|
||||
"importAndExport":
|
||||
MessageLookupByLibrary.simpleMessage("Import and Export"),
|
||||
@@ -141,7 +145,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"invalidJson": MessageLookupByLibrary.simpleMessage("Invalid JSON"),
|
||||
"invalidVersion":
|
||||
MessageLookupByLibrary.simpleMessage("Invalid version"),
|
||||
"invalidVersionHelp": m6,
|
||||
"invalidVersionHelp": m7,
|
||||
"isBusy": MessageLookupByLibrary.simpleMessage("Is busy now"),
|
||||
"keepForeground":
|
||||
MessageLookupByLibrary.simpleMessage("Keep app foreground!"),
|
||||
@@ -152,7 +156,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"loadingFiles":
|
||||
MessageLookupByLibrary.simpleMessage("Loading files..."),
|
||||
"loss": MessageLookupByLibrary.simpleMessage("loss"),
|
||||
"madeWithLove": m7,
|
||||
"madeWithLove": m8,
|
||||
"max": MessageLookupByLibrary.simpleMessage("max"),
|
||||
"min": MessageLookupByLibrary.simpleMessage("min"),
|
||||
"ms": MessageLookupByLibrary.simpleMessage("ms"),
|
||||
@@ -188,11 +192,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"privateKey": MessageLookupByLibrary.simpleMessage("Private Key"),
|
||||
"pwd": MessageLookupByLibrary.simpleMessage("Password"),
|
||||
"rename": MessageLookupByLibrary.simpleMessage("Rename"),
|
||||
"reportBugsOnGithubIssue": m8,
|
||||
"reportBugsOnGithubIssue": m9,
|
||||
"restore": MessageLookupByLibrary.simpleMessage("Restore"),
|
||||
"restoreSuccess": MessageLookupByLibrary.simpleMessage(
|
||||
"Restore success. Restart app to apply."),
|
||||
"restoreSureWithDate": m9,
|
||||
"restoreSureWithDate": m10,
|
||||
"result": MessageLookupByLibrary.simpleMessage("Result"),
|
||||
"run": MessageLookupByLibrary.simpleMessage("Run"),
|
||||
"save": MessageLookupByLibrary.simpleMessage("Save"),
|
||||
@@ -216,13 +220,13 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"sftpSSHConnected":
|
||||
MessageLookupByLibrary.simpleMessage("SFTP Connected"),
|
||||
"snippet": MessageLookupByLibrary.simpleMessage("Snippet"),
|
||||
"spentTime": m10,
|
||||
"spentTime": m11,
|
||||
"start": MessageLookupByLibrary.simpleMessage("Start"),
|
||||
"stop": MessageLookupByLibrary.simpleMessage("Stop"),
|
||||
"sureDelete": m11,
|
||||
"sureDelete": m12,
|
||||
"sureNoPwd": MessageLookupByLibrary.simpleMessage(
|
||||
"Are you sure to use no password?"),
|
||||
"sureToDeleteServer": m12,
|
||||
"sureToDeleteServer": m13,
|
||||
"ttl": MessageLookupByLibrary.simpleMessage("ttl"),
|
||||
"unknown": MessageLookupByLibrary.simpleMessage("unknown"),
|
||||
"unknownError": MessageLookupByLibrary.simpleMessage("Unknown error"),
|
||||
@@ -237,9 +241,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"upsideDown": MessageLookupByLibrary.simpleMessage("Upside Down"),
|
||||
"urlOrJson": MessageLookupByLibrary.simpleMessage("URL or JSON"),
|
||||
"user": MessageLookupByLibrary.simpleMessage("User"),
|
||||
"versionHaveUpdate": m13,
|
||||
"versionUnknownUpdate": m14,
|
||||
"versionUpdated": m15,
|
||||
"versionHaveUpdate": m14,
|
||||
"versionUnknownUpdate": m15,
|
||||
"versionUpdated": m16,
|
||||
"waitConnection": MessageLookupByLibrary.simpleMessage(
|
||||
"Please wait for the connection to be established."),
|
||||
"willTakEeffectImmediately":
|
||||
|
||||
@@ -22,37 +22,39 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
|
||||
static String m0(fileName) => "下载 [${fileName}] 到本地?";
|
||||
|
||||
static String m1(runningCount, stoppedCount) =>
|
||||
static String m1(count) => "共 ${count} 个镜像";
|
||||
|
||||
static String m2(runningCount, stoppedCount) =>
|
||||
"${runningCount}个正在运行, ${stoppedCount}个已停止";
|
||||
|
||||
static String m2(count) => "${count}个容器正在运行";
|
||||
static String m3(count) => "${count}个容器正在运行";
|
||||
|
||||
static String m3(percent, size) => "${size} 的 ${percent}%";
|
||||
static String m4(percent, size) => "${size} 的 ${percent}%";
|
||||
|
||||
static String m4(count) => "找到 ${count} 个更新";
|
||||
static String m5(count) => "找到 ${count} 个更新";
|
||||
|
||||
static String m5(code) => "请求失败, 状态码: ${code}";
|
||||
static String m6(code) => "请求失败, 状态码: ${code}";
|
||||
|
||||
static String m6(url) =>
|
||||
static String m7(url) =>
|
||||
"请确保正确安装了docker,或者使用的非自编译版本。如果没有以上问题,请在 ${url} 提交问题。";
|
||||
|
||||
static String m7(myGithub) => "\n用❤️制作 by ${myGithub}";
|
||||
static String m8(myGithub) => "\n用❤️制作 by ${myGithub}";
|
||||
|
||||
static String m8(url) => "请到 ${url} 提交问题";
|
||||
static String m9(url) => "请到 ${url} 提交问题";
|
||||
|
||||
static String m9(date) => "确定恢复 ${date} 的备份吗?";
|
||||
static String m10(date) => "确定恢复 ${date} 的备份吗?";
|
||||
|
||||
static String m10(time) => "耗时: ${time}";
|
||||
static String m11(time) => "耗时: ${time}";
|
||||
|
||||
static String m11(name) => "确定删除[${name}]?";
|
||||
static String m12(name) => "确定删除[${name}]?";
|
||||
|
||||
static String m12(server) => "你确定要删除服务器 [${server}] 吗?";
|
||||
static String m13(server) => "你确定要删除服务器 [${server}] 吗?";
|
||||
|
||||
static String m13(build) => "找到新版本:v1.0.${build}, 点击更新";
|
||||
static String m14(build) => "找到新版本:v1.0.${build}, 点击更新";
|
||||
|
||||
static String m14(build) => "当前:v1.0.${build}";
|
||||
static String m15(build) => "当前:v1.0.${build}";
|
||||
|
||||
static String m15(build) => "当前:v1.0.${build}, 已是最新版本";
|
||||
static String m16(build) => "当前:v1.0.${build}, 已是最新版本";
|
||||
|
||||
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||
@@ -95,12 +97,13 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"dockerEmptyRunningItems": MessageLookupByLibrary.simpleMessage(
|
||||
"没有正在运行的容器。\n这可能是因为环境变量 DOCKER_HOST 没有被正确读取。你可以通过在终端内运行 `echo \$DOCKER_HOST` 来获取。"),
|
||||
"dockerImage": MessageLookupByLibrary.simpleMessage("镜像"),
|
||||
"dockerImagesFmt": m1,
|
||||
"dockerNotInstalled": MessageLookupByLibrary.simpleMessage("Docker未安装"),
|
||||
"dockerStatusRunningAndStoppedFmt": m1,
|
||||
"dockerStatusRunningFmt": m2,
|
||||
"dockerStatusRunningAndStoppedFmt": m2,
|
||||
"dockerStatusRunningFmt": m3,
|
||||
"download": MessageLookupByLibrary.simpleMessage("下载"),
|
||||
"downloadFinished": MessageLookupByLibrary.simpleMessage("下载完成!"),
|
||||
"downloadStatus": m3,
|
||||
"downloadStatus": m4,
|
||||
"edit": MessageLookupByLibrary.simpleMessage("编辑"),
|
||||
"encode": MessageLookupByLibrary.simpleMessage("编码"),
|
||||
"error": MessageLookupByLibrary.simpleMessage("出错了"),
|
||||
@@ -113,11 +116,12 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
MessageLookupByLibrary.simpleMessage("如果你有任何问题,请在GitHub反馈"),
|
||||
"fieldMustNotEmpty": MessageLookupByLibrary.simpleMessage("这些输入框不能为空。"),
|
||||
"files": MessageLookupByLibrary.simpleMessage("文件"),
|
||||
"foundNUpdate": m4,
|
||||
"foundNUpdate": m5,
|
||||
"go": MessageLookupByLibrary.simpleMessage("开始"),
|
||||
"goSftpDlPage": MessageLookupByLibrary.simpleMessage("前往下载页?"),
|
||||
"host": MessageLookupByLibrary.simpleMessage("主机"),
|
||||
"httpFailedWithCode": m5,
|
||||
"httpFailedWithCode": m6,
|
||||
"imagesList": MessageLookupByLibrary.simpleMessage("镜像列表"),
|
||||
"import": MessageLookupByLibrary.simpleMessage("导入"),
|
||||
"importAndExport": MessageLookupByLibrary.simpleMessage("导入或导出"),
|
||||
"inputDomainHere": MessageLookupByLibrary.simpleMessage("在这里输入域名"),
|
||||
@@ -126,7 +130,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"请先 https://docs.docker.com/engine/install docker"),
|
||||
"invalidJson": MessageLookupByLibrary.simpleMessage("无效的json,存在格式问题"),
|
||||
"invalidVersion": MessageLookupByLibrary.simpleMessage("不支持的版本"),
|
||||
"invalidVersionHelp": m6,
|
||||
"invalidVersionHelp": m7,
|
||||
"isBusy": MessageLookupByLibrary.simpleMessage("当前正忙"),
|
||||
"keepForeground": MessageLookupByLibrary.simpleMessage("请保持应用处于前台!"),
|
||||
"keyAuth": MessageLookupByLibrary.simpleMessage("公钥认证"),
|
||||
@@ -135,7 +139,7 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"license": MessageLookupByLibrary.simpleMessage("开源证书"),
|
||||
"loadingFiles": MessageLookupByLibrary.simpleMessage("正在加载目录。。。"),
|
||||
"loss": MessageLookupByLibrary.simpleMessage("丢包率"),
|
||||
"madeWithLove": m7,
|
||||
"madeWithLove": m8,
|
||||
"max": MessageLookupByLibrary.simpleMessage("最大"),
|
||||
"min": MessageLookupByLibrary.simpleMessage("最小"),
|
||||
"ms": MessageLookupByLibrary.simpleMessage("毫秒"),
|
||||
@@ -164,11 +168,11 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"privateKey": MessageLookupByLibrary.simpleMessage("私钥"),
|
||||
"pwd": MessageLookupByLibrary.simpleMessage("密码"),
|
||||
"rename": MessageLookupByLibrary.simpleMessage("重命名"),
|
||||
"reportBugsOnGithubIssue": m8,
|
||||
"reportBugsOnGithubIssue": m9,
|
||||
"restore": MessageLookupByLibrary.simpleMessage("恢复"),
|
||||
"restoreSuccess":
|
||||
MessageLookupByLibrary.simpleMessage("恢复成功,需要重启App来应用更改"),
|
||||
"restoreSureWithDate": m9,
|
||||
"restoreSureWithDate": m10,
|
||||
"result": MessageLookupByLibrary.simpleMessage("结果"),
|
||||
"run": MessageLookupByLibrary.simpleMessage("运行"),
|
||||
"save": MessageLookupByLibrary.simpleMessage("保存"),
|
||||
@@ -187,12 +191,12 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"sftpSSHConnected":
|
||||
MessageLookupByLibrary.simpleMessage("SFTP 已连接,即将开始下载..."),
|
||||
"snippet": MessageLookupByLibrary.simpleMessage("代码片段"),
|
||||
"spentTime": m10,
|
||||
"spentTime": m11,
|
||||
"start": MessageLookupByLibrary.simpleMessage("开始"),
|
||||
"stop": MessageLookupByLibrary.simpleMessage("停止"),
|
||||
"sureDelete": m11,
|
||||
"sureDelete": m12,
|
||||
"sureNoPwd": MessageLookupByLibrary.simpleMessage("确认使用无密码?"),
|
||||
"sureToDeleteServer": m12,
|
||||
"sureToDeleteServer": m13,
|
||||
"ttl": MessageLookupByLibrary.simpleMessage("缓存时间"),
|
||||
"unknown": MessageLookupByLibrary.simpleMessage("未知"),
|
||||
"unknownError": MessageLookupByLibrary.simpleMessage("未知错误"),
|
||||
@@ -206,9 +210,9 @@ class MessageLookup extends MessageLookupByLibrary {
|
||||
"upsideDown": MessageLookupByLibrary.simpleMessage("上下交换"),
|
||||
"urlOrJson": MessageLookupByLibrary.simpleMessage("链接或JSON"),
|
||||
"user": MessageLookupByLibrary.simpleMessage("用户"),
|
||||
"versionHaveUpdate": m13,
|
||||
"versionUnknownUpdate": m14,
|
||||
"versionUpdated": m15,
|
||||
"versionHaveUpdate": m14,
|
||||
"versionUnknownUpdate": m15,
|
||||
"versionUpdated": m16,
|
||||
"waitConnection": MessageLookupByLibrary.simpleMessage("请等待连接建立"),
|
||||
"willTakEeffectImmediately":
|
||||
MessageLookupByLibrary.simpleMessage("更改将会立即生效")
|
||||
|
||||
@@ -1460,6 +1460,26 @@ class S {
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `Images list`
|
||||
String get imagesList {
|
||||
return Intl.message(
|
||||
'Images list',
|
||||
name: 'imagesList',
|
||||
desc: '',
|
||||
args: [],
|
||||
);
|
||||
}
|
||||
|
||||
/// `{count} images`
|
||||
String dockerImagesFmt(Object count) {
|
||||
return Intl.message(
|
||||
'$count images',
|
||||
name: 'dockerImagesFmt',
|
||||
desc: '',
|
||||
args: [count],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||
|
||||
@@ -139,5 +139,7 @@
|
||||
"dockerContainerName": "Container name",
|
||||
"extraArgs": "Extra args",
|
||||
"preview": "Preview",
|
||||
"isBusy": "Is busy now"
|
||||
"isBusy": "Is busy now",
|
||||
"imagesList": "Images list",
|
||||
"dockerImagesFmt": "{count} images"
|
||||
}
|
||||
@@ -139,5 +139,7 @@
|
||||
"dockerContainerName": "容器名",
|
||||
"extraArgs": "额外参数",
|
||||
"preview": "预览",
|
||||
"isBusy": "当前正忙"
|
||||
"isBusy": "当前正忙",
|
||||
"imagesList": "镜像列表",
|
||||
"dockerImagesFmt": "共 {count} 个镜像"
|
||||
}
|
||||
@@ -165,9 +165,9 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
suffix = '$args $image';
|
||||
}
|
||||
if (name.isEmpty) {
|
||||
return 'docker run -d $suffix';
|
||||
return 'docker run -itd $suffix';
|
||||
}
|
||||
return 'docker run -d --name $name $suffix';
|
||||
return 'docker run -itd --name $name $suffix';
|
||||
}
|
||||
|
||||
String? getErrMsg(DockerErr err) {
|
||||
@@ -253,25 +253,62 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
_buildLoading(docker),
|
||||
_buildVersion(docker.edition ?? s.unknown, docker.version ?? s.unknown),
|
||||
_buildPsItems(running, docker),
|
||||
_buildImages(docker),
|
||||
_buildEditHost(running, docker),
|
||||
].map((e) => RoundRectCard(e)).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildImages(DockerProvider docker) {
|
||||
if (docker.images == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
return ExpansionTile(
|
||||
title: Text(s.imagesList),
|
||||
subtitle: Text(
|
||||
s.dockerImagesFmt(docker.images!.length),
|
||||
style: greyTextStyle,
|
||||
),
|
||||
children: docker.images!
|
||||
.map(
|
||||
(e) => ListTile(
|
||||
title: Text(e.repo),
|
||||
subtitle: Text('${e.tag} - ${e.size}'),
|
||||
trailing: IconButton(
|
||||
icon: const Icon(Icons.delete),
|
||||
onPressed: () async {
|
||||
final result = await _docker.run('docker rmi ${e.id} -f');
|
||||
if (result != null) {
|
||||
showSnackBar(
|
||||
context, Text(getErrMsg(result) ?? s.unknownError));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLoading(DockerProvider docker) {
|
||||
if (docker.isBusy) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.all(17),
|
||||
child: Center(
|
||||
if (!docker.isBusy) return const SizedBox();
|
||||
final haveLog = docker.runLog != null;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(17),
|
||||
child: Column(
|
||||
children: [
|
||||
const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
haveLog ? const SizedBox(height: 17) : const SizedBox(),
|
||||
haveLog ? Text(docker.runLog!) : const SizedBox()
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
Widget _buildEditHost(List<DockerPsItem> running, DockerProvider docker) {
|
||||
if (running.isEmpty) {
|
||||
if (running.isNotEmpty) return const SizedBox();
|
||||
return Padding(
|
||||
padding: const EdgeInsets.fromLTRB(17, 17, 17, 0),
|
||||
child: Column(
|
||||
@@ -287,8 +324,6 @@ class _DockerManagePageState extends State<DockerManagePage> {
|
||||
),
|
||||
);
|
||||
}
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
Future<void> _showEditHostDialog(DockerProvider docker) async {
|
||||
await showRoundDialog(
|
||||
|
||||
Reference in New Issue
Block a user