mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2026-02-22 16:15:13 +01:00
feat: Windows compatibility (#836)
* feat: win compatibility * fix * fix: uptime parse * opt.: linux uptime accuracy * fix: windows temperature fetching * opt. * opt.: powershell exec * refactor: address PR review feedback and improve code quality ### Major Improvements: - **Refactored Windows status parsing**: Broke down large `_getWindowsStatus` method into 13 smaller, focused helper methods for better maintainability and readability - **Extracted system detection logic**: Created dedicated `SystemDetector` helper class to separate OS detection concerns from ServerProvider - **Improved concurrency handling**: Implemented proper synchronization for server updates using Future-based locks to prevent race conditions ### Bug Fixes: - **Fixed CPU percentage parsing**: Removed incorrect '*100' multiplication in BSD CPU parsing (values were already percentages) - **Enhanced memory parsing**: Added validation and error handling to BSD memory fallback parsing with proper logging - **Improved uptime parsing**: Added support for multiple Windows date formats and robust error handling with validation - **Fixed division by zero**: Added safety checks in Swap.usedPercent getter ### Code Quality Enhancements: - **Added comprehensive documentation**: Documented Windows CPU counter limitations and approach - **Strengthened error handling**: Added detailed logging and validation throughout parsing methods - **Improved robustness**: Enhanced BSD CPU parsing with percentage validation and warnings - **Better separation of concerns**: Each parsing method now has single responsibility ### Files Changed: - `lib/data/helper/system_detector.dart` (new): System detection helper - `lib/data/model/server/cpu.dart`: Fixed percentage parsing and added validation - `lib/data/model/server/memory.dart`: Enhanced fallback parsing and division-by-zero protection - `lib/data/model/server/server_status_update_req.dart`: Refactored into 13 focused parsing methods - `lib/data/provider/server.dart`: Improved synchronization and extracted system detection 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: parse & shell fn struct --------- Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -24,14 +24,7 @@ final class PodmanImg implements ContainerImg {
|
||||
final int? size;
|
||||
final int? containers;
|
||||
|
||||
PodmanImg({
|
||||
this.repository,
|
||||
this.tag,
|
||||
this.id,
|
||||
this.created,
|
||||
this.size,
|
||||
this.containers,
|
||||
});
|
||||
PodmanImg({this.repository, this.tag, this.id, this.created, this.size, this.containers});
|
||||
|
||||
@override
|
||||
String? get sizeMB => size?.bytes2Str;
|
||||
@@ -39,28 +32,27 @@ final class PodmanImg implements ContainerImg {
|
||||
@override
|
||||
int? get containersCount => containers;
|
||||
|
||||
factory PodmanImg.fromRawJson(String str) =>
|
||||
PodmanImg.fromJson(json.decode(str));
|
||||
factory PodmanImg.fromRawJson(String str) => PodmanImg.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory PodmanImg.fromJson(Map<String, dynamic> json) => PodmanImg(
|
||||
repository: json['repository'],
|
||||
tag: json['tag'],
|
||||
id: json['Id'],
|
||||
created: json['Created'],
|
||||
size: json['Size'],
|
||||
containers: json['Containers'],
|
||||
);
|
||||
repository: json['repository'],
|
||||
tag: json['tag'],
|
||||
id: json['Id'],
|
||||
created: json['Created'],
|
||||
size: json['Size'],
|
||||
containers: json['Containers'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'repository': repository,
|
||||
'tag': tag,
|
||||
'Id': id,
|
||||
'Created': created,
|
||||
'Size': size,
|
||||
'Containers': containers,
|
||||
};
|
||||
'repository': repository,
|
||||
'tag': tag,
|
||||
'Id': id,
|
||||
'Created': created,
|
||||
'Size': size,
|
||||
'Containers': containers,
|
||||
};
|
||||
}
|
||||
|
||||
final class DockerImg implements ContainerImg {
|
||||
@@ -87,11 +79,9 @@ final class DockerImg implements ContainerImg {
|
||||
String? get sizeMB => size;
|
||||
|
||||
@override
|
||||
int? get containersCount =>
|
||||
containers == 'N/A' ? 0 : int.tryParse(containers);
|
||||
int? get containersCount => containers == 'N/A' ? 0 : int.tryParse(containers);
|
||||
|
||||
factory DockerImg.fromRawJson(String str) =>
|
||||
DockerImg.fromJson(json.decode(str));
|
||||
factory DockerImg.fromRawJson(String str) => DockerImg.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
@@ -121,11 +111,11 @@ final class DockerImg implements ContainerImg {
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'Containers': containers,
|
||||
'CreatedAt': createdAt,
|
||||
'ID': id,
|
||||
'Repository': repository,
|
||||
'Size': size,
|
||||
'Tag': tag,
|
||||
};
|
||||
'Containers': containers,
|
||||
'CreatedAt': createdAt,
|
||||
'ID': id,
|
||||
'Repository': repository,
|
||||
'Size': size,
|
||||
'Tag': tag,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -42,15 +42,7 @@ final class PodmanPs implements ContainerPs {
|
||||
@override
|
||||
String? disk;
|
||||
|
||||
PodmanPs({
|
||||
this.command,
|
||||
this.created,
|
||||
this.exited,
|
||||
this.id,
|
||||
this.image,
|
||||
this.names,
|
||||
this.startedAt,
|
||||
});
|
||||
PodmanPs({this.command, this.created, this.exited, this.id, this.image, this.names, this.startedAt});
|
||||
|
||||
@override
|
||||
String? get name => names?.firstOrNull;
|
||||
@@ -78,36 +70,29 @@ final class PodmanPs implements ContainerPs {
|
||||
disk = '${l10n.read} $diskOut / ${l10n.write} $diskIn';
|
||||
}
|
||||
|
||||
factory PodmanPs.fromRawJson(String str) =>
|
||||
PodmanPs.fromJson(json.decode(str));
|
||||
factory PodmanPs.fromRawJson(String str) => PodmanPs.fromJson(json.decode(str));
|
||||
|
||||
String toRawJson() => json.encode(toJson());
|
||||
|
||||
factory PodmanPs.fromJson(Map<String, dynamic> json) => PodmanPs(
|
||||
command: json['Command'] == null
|
||||
? []
|
||||
: List<String>.from(json['Command']!.map((x) => x)),
|
||||
created:
|
||||
json['Created'] == null ? null : DateTime.parse(json['Created']),
|
||||
exited: json['Exited'],
|
||||
id: json['Id'],
|
||||
image: json['Image'],
|
||||
names: json['Names'] == null
|
||||
? []
|
||||
: List<String>.from(json['Names']!.map((x) => x)),
|
||||
startedAt: json['StartedAt'],
|
||||
);
|
||||
command: json['Command'] == null ? [] : List<String>.from(json['Command']!.map((x) => x)),
|
||||
created: json['Created'] == null ? null : DateTime.parse(json['Created']),
|
||||
exited: json['Exited'],
|
||||
id: json['Id'],
|
||||
image: json['Image'],
|
||||
names: json['Names'] == null ? [] : List<String>.from(json['Names']!.map((x) => x)),
|
||||
startedAt: json['StartedAt'],
|
||||
);
|
||||
|
||||
Map<String, dynamic> toJson() => {
|
||||
'Command':
|
||||
command == null ? [] : List<dynamic>.from(command!.map((x) => x)),
|
||||
'Created': created?.toIso8601String(),
|
||||
'Exited': exited,
|
||||
'Id': id,
|
||||
'Image': image,
|
||||
'Names': names == null ? [] : List<dynamic>.from(names!.map((x) => x)),
|
||||
'StartedAt': startedAt,
|
||||
};
|
||||
'Command': command == null ? [] : List<dynamic>.from(command!.map((x) => x)),
|
||||
'Created': created?.toIso8601String(),
|
||||
'Exited': exited,
|
||||
'Id': id,
|
||||
'Image': image,
|
||||
'Names': names == null ? [] : List<dynamic>.from(names!.map((x) => x)),
|
||||
'StartedAt': startedAt,
|
||||
};
|
||||
}
|
||||
|
||||
final class DockerPs implements ContainerPs {
|
||||
@@ -127,12 +112,7 @@ final class DockerPs implements ContainerPs {
|
||||
@override
|
||||
String? disk;
|
||||
|
||||
DockerPs({
|
||||
this.id,
|
||||
this.image,
|
||||
this.names,
|
||||
this.state,
|
||||
});
|
||||
DockerPs({this.id, this.image, this.names, this.state});
|
||||
|
||||
@override
|
||||
String? get name => names;
|
||||
@@ -159,11 +139,6 @@ final class DockerPs implements ContainerPs {
|
||||
/// a049d689e7a1 aria2-pro p3terx/aria2-pro Up 3 weeks
|
||||
factory DockerPs.parse(String raw) {
|
||||
final parts = raw.split(Miscs.multiBlankreg);
|
||||
return DockerPs(
|
||||
id: parts[0],
|
||||
state: parts[1],
|
||||
names: parts[2],
|
||||
image: parts[3].trim(),
|
||||
);
|
||||
return DockerPs(id: parts[0], state: parts[1], names: parts[2], image: parts[3].trim());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,16 +3,15 @@ import 'package:server_box/data/model/container/ps.dart';
|
||||
|
||||
enum ContainerType {
|
||||
docker,
|
||||
podman,
|
||||
;
|
||||
podman;
|
||||
|
||||
ContainerPs Function(String str) get ps => switch (this) {
|
||||
ContainerType.docker => DockerPs.parse,
|
||||
ContainerType.podman => PodmanPs.fromRawJson,
|
||||
};
|
||||
ContainerType.docker => DockerPs.parse,
|
||||
ContainerType.podman => PodmanPs.fromRawJson,
|
||||
};
|
||||
|
||||
ContainerImg Function(String str) get img => switch (this) {
|
||||
ContainerType.docker => DockerImg.fromRawJson,
|
||||
ContainerType.podman => PodmanImg.fromRawJson,
|
||||
};
|
||||
ContainerType.docker => DockerImg.fromRawJson,
|
||||
ContainerType.podman => PodmanImg.fromRawJson,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user