mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-18 07:44:26 +01:00
opt.: speed up docker page
This commit is contained in:
@@ -16,7 +16,7 @@ abstract final class ContainerPs {
|
||||
String? net;
|
||||
String? disk;
|
||||
|
||||
factory ContainerPs.fromRawJson(String s, ContainerType typ) => typ.ps(s);
|
||||
factory ContainerPs.fromRaw(String s, ContainerType typ) => typ.ps(s);
|
||||
|
||||
void parseStats(String s);
|
||||
}
|
||||
@@ -110,8 +110,6 @@ final class PodmanPs implements ContainerPs {
|
||||
}
|
||||
|
||||
final class DockerPs implements ContainerPs {
|
||||
final String? command;
|
||||
final String? createdAt;
|
||||
@override
|
||||
final String? id;
|
||||
@override
|
||||
@@ -129,8 +127,6 @@ final class DockerPs implements ContainerPs {
|
||||
String? disk;
|
||||
|
||||
DockerPs({
|
||||
this.command,
|
||||
this.createdAt,
|
||||
this.id,
|
||||
this.image,
|
||||
this.names,
|
||||
@@ -141,10 +137,10 @@ final class DockerPs implements ContainerPs {
|
||||
String? get name => names;
|
||||
|
||||
@override
|
||||
String? get cmd => command;
|
||||
String? get cmd => null;
|
||||
|
||||
@override
|
||||
bool get running => state == 'running';
|
||||
bool get running => state?.contains('Up ') ?? false;
|
||||
|
||||
@override
|
||||
void parseStats(String s) {
|
||||
@@ -155,26 +151,15 @@ final class DockerPs implements ContainerPs {
|
||||
disk = stats['BlockIO'];
|
||||
}
|
||||
|
||||
factory DockerPs.fromRawJson(String str) =>
|
||||
DockerPs.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory DockerPs.fromJson(Map<String, dynamic> json) => DockerPs(
|
||||
command: json["Command"],
|
||||
createdAt: json["CreatedAt"],
|
||||
id: json["ID"],
|
||||
image: json["Image"],
|
||||
names: json["Names"],
|
||||
state: json["State"],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
"Command": command,
|
||||
"CreatedAt": createdAt,
|
||||
"ID": id,
|
||||
"Image": image,
|
||||
"Names": names,
|
||||
"State": state,
|
||||
};
|
||||
/// CONTAINER ID NAMES IMAGE STATUS
|
||||
/// a049d689e7a1 aria2-pro p3terx/aria2-pro Up 3 weeks
|
||||
factory DockerPs.parse(String raw) {
|
||||
final parts = raw.split(RegExp(r'\s{2,}'));
|
||||
return DockerPs(
|
||||
id: parts[0],
|
||||
names: parts[1],
|
||||
image: parts[2],
|
||||
state: parts[3],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ enum ContainerType {
|
||||
;
|
||||
|
||||
ContainerPs Function(String str) get ps => switch (this) {
|
||||
ContainerType.docker => DockerPs.fromRawJson,
|
||||
ContainerType.docker => DockerPs.parse,
|
||||
ContainerType.podman => PodmanPs.fromRawJson,
|
||||
};
|
||||
|
||||
|
||||
@@ -26,7 +26,8 @@ class ContainerProvider extends ChangeNotifier {
|
||||
ContainerErr? error;
|
||||
String? runLog;
|
||||
ContainerType type;
|
||||
bool sudo = false;
|
||||
var sudoCompleter = Completer<bool>();
|
||||
bool isBusy = false;
|
||||
|
||||
ContainerProvider({
|
||||
required this.client,
|
||||
@@ -41,6 +42,7 @@ class ContainerProvider extends ChangeNotifier {
|
||||
this.type = type;
|
||||
Stores.container.setType(type, hostId);
|
||||
error = runLog = items = images = version = null;
|
||||
sudoCompleter = Completer<bool>();
|
||||
notifyListeners();
|
||||
await refresh();
|
||||
}
|
||||
@@ -60,17 +62,27 @@ class ContainerProvider extends ChangeNotifier {
|
||||
// return value;
|
||||
// }
|
||||
|
||||
Future<bool> _requiresSudo() async {
|
||||
final psResult = await client?.run(_wrap(ContainerCmdType.ps.exec(type)));
|
||||
if (psResult == null) return true;
|
||||
if (psResult.string.toLowerCase().contains("permission denied")) {
|
||||
return true;
|
||||
void _requiresSudo() async {
|
||||
/// Podman is rootless
|
||||
if (type == ContainerType.podman) return sudoCompleter.complete(false);
|
||||
if (!Stores.setting.containerTrySudo.fetch()) {
|
||||
return sudoCompleter.complete(false);
|
||||
}
|
||||
return false;
|
||||
|
||||
final res = await client?.run(_wrap(ContainerCmdType.images.exec(type)));
|
||||
if (res?.string.toLowerCase().contains("permission denied") ?? false) {
|
||||
return sudoCompleter.complete(true);
|
||||
}
|
||||
return sudoCompleter.complete(false);
|
||||
}
|
||||
|
||||
Future<void> refresh({bool isAuto = false}) async {
|
||||
sudo = await _requiresSudo() && Stores.setting.containerTrySudo.fetch();
|
||||
if (isBusy) return;
|
||||
isBusy = true;
|
||||
|
||||
if (!sudoCompleter.isCompleted) _requiresSudo();
|
||||
|
||||
final sudo = await sudoCompleter.future;
|
||||
|
||||
/// If sudo is required and auto refresh is enabled, skip the refresh.
|
||||
/// Or this will ask for pwd again and again.
|
||||
@@ -78,17 +90,22 @@ class ContainerProvider extends ChangeNotifier {
|
||||
final includeStats = Stores.setting.containerParseStat.fetch();
|
||||
|
||||
var raw = '';
|
||||
final cmd = _wrap(ContainerCmdType.execAll(
|
||||
type,
|
||||
sudo: sudo,
|
||||
includeStats: includeStats,
|
||||
));
|
||||
final code = await client?.execWithPwd(
|
||||
_wrap(ContainerCmdType.execAll(
|
||||
type,
|
||||
sudo: sudo,
|
||||
includeStats: includeStats,
|
||||
)),
|
||||
cmd,
|
||||
context: context,
|
||||
onStdout: (data, _) => raw = '$raw$data',
|
||||
id: hostId,
|
||||
);
|
||||
|
||||
isBusy = false;
|
||||
|
||||
if (!context.mounted) return;
|
||||
|
||||
/// Code 127 means command not found
|
||||
if (code == 127 || raw.contains(_dockerNotFound)) {
|
||||
error = ContainerErr(type: ContainerErrType.notInstalled);
|
||||
@@ -126,8 +143,12 @@ class ContainerProvider extends ChangeNotifier {
|
||||
final psRaw = ContainerCmdType.ps.find(segments);
|
||||
try {
|
||||
final lines = psRaw.split('\n');
|
||||
if (type == ContainerType.docker) {
|
||||
/// Due to the fetched data is not in json format, skip table header
|
||||
lines.removeWhere((element) => element.contains('CONTAINER ID'));
|
||||
}
|
||||
lines.removeWhere((element) => element.isEmpty);
|
||||
items = lines.map((e) => ContainerPs.fromRawJson(e, type)).toList();
|
||||
items = lines.map((e) => ContainerPs.fromRaw(e, type)).toList();
|
||||
} catch (e, trace) {
|
||||
error = ContainerErr(
|
||||
type: ContainerErrType.parsePs,
|
||||
@@ -203,7 +224,7 @@ class ContainerProvider extends ChangeNotifier {
|
||||
runLog = '';
|
||||
final errs = <String>[];
|
||||
final code = await client?.execWithPwd(
|
||||
_wrap(sudo ? 'sudo -S $cmd' : cmd),
|
||||
_wrap((await sudoCompleter.future) ? 'sudo -S $cmd' : cmd),
|
||||
context: context,
|
||||
onStdout: (data, _) {
|
||||
runLog = '$runLog$data';
|
||||
@@ -254,7 +275,12 @@ enum ContainerCmdType {
|
||||
final prefix = sudo ? 'sudo -S ${type.name}' : type.name;
|
||||
return switch (this) {
|
||||
ContainerCmdType.version => '$prefix version $_jsonFmt',
|
||||
ContainerCmdType.ps => '$prefix ps -a $_jsonFmt',
|
||||
ContainerCmdType.ps => switch (type) {
|
||||
/// Use [_jsonFmt] in Docker will cause the operation to slow down.
|
||||
ContainerType.docker => '$prefix ps -a --format "table '
|
||||
'{{printf \\"${"%-30.30s " * 4}\\" .ID .Names .Image .Status}}"',
|
||||
ContainerType.podman => '$prefix ps -a $_jsonFmt',
|
||||
},
|
||||
ContainerCmdType.stats =>
|
||||
includeStats ? '$prefix stats --no-stream $_jsonFmt' : 'echo PASS',
|
||||
ContainerCmdType.images => '$prefix image ls $_jsonFmt',
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
// This file is generated by make script. Do not edit.
|
||||
// This file is generated by fl_build. Do not edit.
|
||||
|
||||
class BuildData {
|
||||
static const String name = "ServerBox";
|
||||
static const int build = 918;
|
||||
static const int build = 923;
|
||||
static const String engine = "3.22.1";
|
||||
static const String buildAt = "2024-05-25 19:17:18";
|
||||
static const int modifications = 2;
|
||||
static const String buildAt = "2024-06-01 22:31:14";
|
||||
static const int modifications = 4;
|
||||
static const int script = 48;
|
||||
}
|
||||
|
||||
@@ -18,4 +18,6 @@ abstract final class Miscs {
|
||||
static const pkgName = 'tech.lolli.toolbox';
|
||||
|
||||
static const jsonEncoder = JsonEncoder.withIndent(' ');
|
||||
|
||||
static const bakFileName = 'srvbox_backup.json';
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user