mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
feat: ability to disable monitoring cmds (#840)
This commit is contained in:
@@ -1,9 +1,16 @@
|
|||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
|
import 'package:server_box/data/model/app/scripts/script_consts.dart';
|
||||||
import 'package:server_box/data/model/server/system.dart';
|
import 'package:server_box/data/model/server/system.dart';
|
||||||
|
|
||||||
/// Base class for all command type enums
|
/// Base class for all command type enums
|
||||||
abstract class CommandType {
|
abstract class CommandType implements Enum {
|
||||||
String get cmd;
|
String get cmd;
|
||||||
|
|
||||||
|
/// Get command-specific separator
|
||||||
|
String get separator;
|
||||||
|
|
||||||
|
/// Get command-specific divider (separator with echo and formatting)
|
||||||
|
String get divider;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Linux/Unix status commands
|
/// Linux/Unix status commands
|
||||||
@@ -83,6 +90,12 @@ enum StatusCmdType implements CommandType {
|
|||||||
final String cmd;
|
final String cmd;
|
||||||
|
|
||||||
const StatusCmdType(this.cmd);
|
const StatusCmdType(this.cmd);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get separator => ScriptConstants.getCmdSeparator(name);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get divider => ScriptConstants.getCmdDivider(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// BSD/macOS status commands
|
/// BSD/macOS status commands
|
||||||
@@ -102,6 +115,12 @@ enum BSDStatusCmdType implements CommandType {
|
|||||||
final String cmd;
|
final String cmd;
|
||||||
|
|
||||||
const BSDStatusCmdType(this.cmd);
|
const BSDStatusCmdType(this.cmd);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get separator => ScriptConstants.getCmdSeparator(name);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get divider => ScriptConstants.getCmdDivider(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Windows PowerShell status commands
|
/// Windows PowerShell status commands
|
||||||
@@ -225,6 +244,12 @@ enum WindowsStatusCmdType implements CommandType {
|
|||||||
final String cmd;
|
final String cmd;
|
||||||
|
|
||||||
const WindowsStatusCmdType(this.cmd);
|
const WindowsStatusCmdType(this.cmd);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get separator => ScriptConstants.getCmdSeparator(name);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get divider => ScriptConstants.getCmdDivider(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Extensions for StatusCmdType
|
/// Extensions for StatusCmdType
|
||||||
@@ -240,10 +265,10 @@ extension StatusCmdTypeX on StatusCmdType {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Generic extension for Enum types
|
/// Extension for CommandType to find content in parsed map
|
||||||
extension EnumX on Enum {
|
extension CommandTypeX on CommandType {
|
||||||
/// Find out the required segment from [segments]
|
/// Find the command output from the parsed script output map
|
||||||
String find(List<String> segments) {
|
String findInMap(Map<String, String> parsedOutput) {
|
||||||
return segments[index];
|
return parsedOutput[name] ?? '';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,11 @@ import 'package:server_box/data/model/app/scripts/script_consts.dart';
|
|||||||
import 'package:server_box/data/model/app/scripts/shell_func.dart';
|
import 'package:server_box/data/model/app/scripts/shell_func.dart';
|
||||||
|
|
||||||
/// Abstract base class for platform-specific script builders
|
/// Abstract base class for platform-specific script builders
|
||||||
abstract class ScriptBuilder {
|
sealed class ScriptBuilder {
|
||||||
const ScriptBuilder();
|
const ScriptBuilder();
|
||||||
|
|
||||||
/// Generate a complete script for all shell functions
|
/// Generate a complete script for all shell functions
|
||||||
String buildScript(Map<String, String>? customCmds);
|
String buildScript(Map<String, String>? customCmds, [List<String>? disabledCmdTypes]);
|
||||||
|
|
||||||
/// Get the script file name for this platform
|
/// Get the script file name for this platform
|
||||||
String get scriptFileName;
|
String get scriptFileName;
|
||||||
@@ -23,9 +23,6 @@ abstract class ScriptBuilder {
|
|||||||
|
|
||||||
/// Get the script header for this platform
|
/// Get the script header for this platform
|
||||||
String get scriptHeader;
|
String get scriptHeader;
|
||||||
|
|
||||||
/// Get the command divider for this platform
|
|
||||||
String get cmdDivider => ScriptConstants.cmdDivider;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Windows PowerShell script builder
|
/// Windows PowerShell script builder
|
||||||
@@ -53,13 +50,19 @@ class WindowsScriptBuilder extends ScriptBuilder {
|
|||||||
@override
|
@override
|
||||||
String getCustomCmdsString(ShellFunc func, Map<String, String>? customCmds) {
|
String getCustomCmdsString(ShellFunc func, Map<String, String>? customCmds) {
|
||||||
if (func == ShellFunc.status && customCmds != null && customCmds.isNotEmpty) {
|
if (func == ShellFunc.status && customCmds != null && customCmds.isNotEmpty) {
|
||||||
return '\n${customCmds.values.map((cmd) => '\t$cmd').join('\n')}';
|
final sb = StringBuffer();
|
||||||
|
for (final e in customCmds.entries) {
|
||||||
|
final cmdDivider = ScriptConstants.getCustomCmdSeparator(e.key);
|
||||||
|
sb.writeln(' Write-Host "$cmdDivider"');
|
||||||
|
sb.writeln(' ${e.value}');
|
||||||
|
}
|
||||||
|
return '\n$sb';
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String buildScript(Map<String, String>? customCmds) {
|
String buildScript(Map<String, String>? customCmds, [List<String>? disabledCmdTypes]) {
|
||||||
final sb = StringBuffer();
|
final sb = StringBuffer();
|
||||||
sb.write(scriptHeader);
|
sb.write(scriptHeader);
|
||||||
|
|
||||||
@@ -69,7 +72,7 @@ class WindowsScriptBuilder extends ScriptBuilder {
|
|||||||
|
|
||||||
sb.write('''
|
sb.write('''
|
||||||
function ${func.name} {
|
function ${func.name} {
|
||||||
${_getWindowsCommand(func).split('\n').map((e) => e.isEmpty ? '' : ' $e').join('\n')}$customCmdsStr
|
${_getWindowsCommand(func, disabledCmdTypes).split('\n').map((e) => e.isEmpty ? '' : ' $e').join('\n')}$customCmdsStr
|
||||||
}
|
}
|
||||||
|
|
||||||
''');
|
''');
|
||||||
@@ -92,14 +95,20 @@ switch (\$args[0]) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get Windows-specific command for a shell function
|
/// Get Windows-specific command for a shell function
|
||||||
String _getWindowsCommand(ShellFunc func) => switch (func) {
|
String _getWindowsCommand(ShellFunc func, [List<String>? disabledCmdTypes]) => switch (func) {
|
||||||
ShellFunc.status => WindowsStatusCmdType.values.map((e) => e.cmd).join(cmdDivider),
|
ShellFunc.status => _getWindowsStatusCommand(disabledCmdTypes: disabledCmdTypes ?? []),
|
||||||
ShellFunc.process => 'Get-Process | Select-Object ProcessName, Id, CPU, WorkingSet | ConvertTo-Json',
|
ShellFunc.process => 'Get-Process | Select-Object ProcessName, Id, CPU, WorkingSet | ConvertTo-Json',
|
||||||
ShellFunc.shutdown => 'Stop-Computer -Force',
|
ShellFunc.shutdown => 'Stop-Computer -Force',
|
||||||
ShellFunc.reboot => 'Restart-Computer -Force',
|
ShellFunc.reboot => 'Restart-Computer -Force',
|
||||||
ShellFunc.suspend =>
|
ShellFunc.suspend =>
|
||||||
'Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Application]::SetSuspendState(\'Suspend\', \$false, \$false)',
|
'Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Application]::SetSuspendState(\'Suspend\', \$false, \$false)',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Get Windows status command with command-specific separators
|
||||||
|
String _getWindowsStatusCommand({required List<String> disabledCmdTypes}) {
|
||||||
|
final cmdTypes = WindowsStatusCmdType.values.where((e) => !disabledCmdTypes.contains(e.name));
|
||||||
|
return cmdTypes.map((e) => '${e.divider}${e.cmd}').join('').trimRight(); // Remove trailing divider
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unix shell script builder
|
/// Unix shell script builder
|
||||||
@@ -129,13 +138,19 @@ chmod 755 $scriptPath
|
|||||||
@override
|
@override
|
||||||
String getCustomCmdsString(ShellFunc func, Map<String, String>? customCmds) {
|
String getCustomCmdsString(ShellFunc func, Map<String, String>? customCmds) {
|
||||||
if (func == ShellFunc.status && customCmds != null && customCmds.isNotEmpty) {
|
if (func == ShellFunc.status && customCmds != null && customCmds.isNotEmpty) {
|
||||||
return '$cmdDivider\n\t${customCmds.values.join(cmdDivider)}';
|
final sb = StringBuffer();
|
||||||
|
for (final e in customCmds.entries) {
|
||||||
|
final cmdDivider = ScriptConstants.getCustomCmdSeparator(e.key);
|
||||||
|
sb.writeln('echo "$cmdDivider"');
|
||||||
|
sb.writeln(e.value);
|
||||||
|
}
|
||||||
|
return '\n$sb';
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String buildScript(Map<String, String>? customCmds) {
|
String buildScript(Map<String, String>? customCmds, [List<String>? disabledCmdTypes]) {
|
||||||
final sb = StringBuffer();
|
final sb = StringBuffer();
|
||||||
sb.write(scriptHeader);
|
sb.write(scriptHeader);
|
||||||
// Write each function
|
// Write each function
|
||||||
@@ -143,7 +158,7 @@ chmod 755 $scriptPath
|
|||||||
final customCmdsStr = getCustomCmdsString(func, customCmds);
|
final customCmdsStr = getCustomCmdsString(func, customCmds);
|
||||||
sb.write('''
|
sb.write('''
|
||||||
${func.name}() {
|
${func.name}() {
|
||||||
${_getUnixCommand(func).split('\n').map((e) => '\t$e').join('\n')}
|
${_getUnixCommand(func, disabledCmdTypes).split('\n').map((e) => '\t$e').join('\n')}
|
||||||
$customCmdsStr
|
$customCmdsStr
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,27 +183,24 @@ esac''');
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get Unix-specific command for a shell function
|
/// Get Unix-specific command for a shell function
|
||||||
String _getUnixCommand(ShellFunc func) {
|
String _getUnixCommand(ShellFunc func, [List<String>? disabledCmdTypes]) {
|
||||||
switch (func) {
|
return switch (func) {
|
||||||
case ShellFunc.status:
|
ShellFunc.status => _getUnixStatusCommand(disabledCmdTypes: disabledCmdTypes ?? []),
|
||||||
return _getUnixStatusCommand();
|
ShellFunc.process => _getUnixProcessCommand(),
|
||||||
case ShellFunc.process:
|
ShellFunc.shutdown => _getUnixShutdownCommand(),
|
||||||
return _getUnixProcessCommand();
|
ShellFunc.reboot => _getUnixRebootCommand(),
|
||||||
case ShellFunc.shutdown:
|
ShellFunc.suspend => _getUnixSuspendCommand(),
|
||||||
return _getUnixShutdownCommand();
|
};
|
||||||
case ShellFunc.reboot:
|
|
||||||
return _getUnixRebootCommand();
|
|
||||||
case ShellFunc.suspend:
|
|
||||||
return _getUnixSuspendCommand();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get Unix status command with OS detection
|
/// Get Unix status command with OS detection
|
||||||
String _getUnixStatusCommand() {
|
String _getUnixStatusCommand({required List<String> disabledCmdTypes}) {
|
||||||
// Generate command lists for better readability
|
// Generate command lists with command-specific separators, filtering disabled commands
|
||||||
final linuxCommands = StatusCmdType.values.map((e) => e.cmd).join(cmdDivider);
|
final filteredLinuxCmdTypes = StatusCmdType.values.where((e) => !disabledCmdTypes.contains(e.name));
|
||||||
|
final linuxCommands = filteredLinuxCmdTypes.map((e) => '${e.divider}${e.cmd}').join('').trimRight();
|
||||||
|
|
||||||
final bsdCommands = BSDStatusCmdType.values.map((e) => e.cmd).join(cmdDivider);
|
final filteredBsdCmdTypes = BSDStatusCmdType.values.where((e) => !disabledCmdTypes.contains(e.name));
|
||||||
|
final bsdCommands = filteredBsdCmdTypes.map((e) => '${e.divider}${e.cmd}').join('').trimRight();
|
||||||
|
|
||||||
return '''
|
return '''
|
||||||
if [ "\$macSign" = "" ] && [ "\$bsdSign" = "" ]; then
|
if [ "\$macSign" = "" ] && [ "\$bsdSign" = "" ]; then
|
||||||
|
|||||||
@@ -17,8 +17,58 @@ class ScriptConstants {
|
|||||||
// Command separators and dividers
|
// Command separators and dividers
|
||||||
static const String separator = 'SrvBoxSep';
|
static const String separator = 'SrvBoxSep';
|
||||||
|
|
||||||
/// The suffix `\t` is for formatting
|
/// Custom command separator
|
||||||
static const String cmdDivider = '\necho $separator\n\t';
|
static const String customCmdSep = 'SrvBoxCusCmdSep';
|
||||||
|
|
||||||
|
/// Generate command-specific separator
|
||||||
|
static String getCmdSeparator(String cmdName) => '$separator.$cmdName';
|
||||||
|
|
||||||
|
/// Generate command-specific divider for custom commands
|
||||||
|
static String getCustomCmdSeparator(String cmdName) => '$customCmdSep.$cmdName';
|
||||||
|
|
||||||
|
/// Generate command-specific divider
|
||||||
|
static String getCmdDivider(String cmdName) => '\necho ${getCmdSeparator(cmdName)}\n\t';
|
||||||
|
|
||||||
|
/// Parse script output into command-specific map
|
||||||
|
static Map<String, String> parseScriptOutput(String raw) {
|
||||||
|
final result = <String, String>{};
|
||||||
|
|
||||||
|
if (raw.isEmpty) return result;
|
||||||
|
|
||||||
|
// Parse line by line to properly handle command-specific separators
|
||||||
|
final lines = raw.split('\n');
|
||||||
|
String? currentCmd;
|
||||||
|
final buffer = StringBuffer();
|
||||||
|
|
||||||
|
for (final line in lines) {
|
||||||
|
if (line.startsWith('$separator.')) {
|
||||||
|
// Save previous command content
|
||||||
|
if (currentCmd != null) {
|
||||||
|
result[currentCmd] = buffer.toString().trim();
|
||||||
|
buffer.clear();
|
||||||
|
}
|
||||||
|
// Start new command
|
||||||
|
currentCmd = line.substring('$separator.'.length);
|
||||||
|
} else if (line.startsWith('$customCmdSep.')) {
|
||||||
|
// Save previous command content
|
||||||
|
if (currentCmd != null) {
|
||||||
|
result[currentCmd] = buffer.toString().trim();
|
||||||
|
buffer.clear();
|
||||||
|
}
|
||||||
|
// Start new custom command
|
||||||
|
currentCmd = line.substring('$customCmdSep.'.length);
|
||||||
|
} else if (currentCmd != null) {
|
||||||
|
buffer.writeln(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't forget the last command
|
||||||
|
if (currentCmd != null) {
|
||||||
|
result[currentCmd] = buffer.toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// Path separators
|
// Path separators
|
||||||
static const String unixPathSeparator = '/';
|
static const String unixPathSeparator = '/';
|
||||||
|
|||||||
@@ -93,10 +93,10 @@ class ShellFuncManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Generate complete script based on system type
|
/// Generate complete script based on system type
|
||||||
static String allScript(Map<String, String>? customCmds, {SystemType? systemType}) {
|
static String allScript(Map<String, String>? customCmds, {SystemType? systemType, List<String>? disabledCmdTypes}) {
|
||||||
final isWindows = systemType == SystemType.windows;
|
final isWindows = systemType == SystemType.windows;
|
||||||
final builder = ScriptBuilderFactory.getBuilder(isWindows);
|
final builder = ScriptBuilderFactory.getBuilder(isWindows);
|
||||||
|
|
||||||
return builder.buildScript(customCmds);
|
return builder.buildScript(customCmds, disabledCmdTypes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,9 @@ abstract class Spi with _$Spi {
|
|||||||
|
|
||||||
/// Custom system type (unix or windows). If set, skip auto-detection.
|
/// Custom system type (unix or windows). If set, skip auto-detection.
|
||||||
@JsonKey(includeIfNull: false) SystemType? customSystemType,
|
@JsonKey(includeIfNull: false) SystemType? customSystemType,
|
||||||
|
|
||||||
|
/// Disabled command types for this server
|
||||||
|
@JsonKey(includeIfNull: false) List<String>? disabledCmdTypes,
|
||||||
}) = _Spi;
|
}) = _Spi;
|
||||||
|
|
||||||
factory Spi.fromJson(Map<String, dynamic> json) => _$SpiFromJson(json);
|
factory Spi.fromJson(Map<String, dynamic> json) => _$SpiFromJson(json);
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ mixin _$Spi {
|
|||||||
@JsonKey(name: 'pubKeyId') String? get keyId; List<String>? get tags; String? get alterUrl; bool get autoConnect;/// [id] of the jump server
|
@JsonKey(name: 'pubKeyId') String? get keyId; List<String>? get tags; String? get alterUrl; bool get autoConnect;/// [id] of the jump server
|
||||||
String? get jumpId; ServerCustom? get custom; WakeOnLanCfg? get wolCfg;/// It only applies to SSH terminal.
|
String? get jumpId; ServerCustom? get custom; WakeOnLanCfg? get wolCfg;/// It only applies to SSH terminal.
|
||||||
Map<String, String>? get envs;@JsonKey(fromJson: Spi.parseId) String get id;/// Custom system type (unix or windows). If set, skip auto-detection.
|
Map<String, String>? get envs;@JsonKey(fromJson: Spi.parseId) String get id;/// Custom system type (unix or windows). If set, skip auto-detection.
|
||||||
@JsonKey(includeIfNull: false) SystemType? get customSystemType;
|
@JsonKey(includeIfNull: false) SystemType? get customSystemType;/// Disabled command types for this server
|
||||||
|
@JsonKey(includeIfNull: false) List<String>? get disabledCmdTypes;
|
||||||
/// Create a copy of Spi
|
/// Create a copy of Spi
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@@ -33,12 +34,12 @@ $SpiCopyWith<Spi> get copyWith => _$SpiCopyWithImpl<Spi>(this as Spi, _$identity
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is Spi&&(identical(other.name, name) || other.name == name)&&(identical(other.ip, ip) || other.ip == ip)&&(identical(other.port, port) || other.port == port)&&(identical(other.user, user) || other.user == user)&&(identical(other.pwd, pwd) || other.pwd == pwd)&&(identical(other.keyId, keyId) || other.keyId == keyId)&&const DeepCollectionEquality().equals(other.tags, tags)&&(identical(other.alterUrl, alterUrl) || other.alterUrl == alterUrl)&&(identical(other.autoConnect, autoConnect) || other.autoConnect == autoConnect)&&(identical(other.jumpId, jumpId) || other.jumpId == jumpId)&&(identical(other.custom, custom) || other.custom == custom)&&(identical(other.wolCfg, wolCfg) || other.wolCfg == wolCfg)&&const DeepCollectionEquality().equals(other.envs, envs)&&(identical(other.id, id) || other.id == id)&&(identical(other.customSystemType, customSystemType) || other.customSystemType == customSystemType));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is Spi&&(identical(other.name, name) || other.name == name)&&(identical(other.ip, ip) || other.ip == ip)&&(identical(other.port, port) || other.port == port)&&(identical(other.user, user) || other.user == user)&&(identical(other.pwd, pwd) || other.pwd == pwd)&&(identical(other.keyId, keyId) || other.keyId == keyId)&&const DeepCollectionEquality().equals(other.tags, tags)&&(identical(other.alterUrl, alterUrl) || other.alterUrl == alterUrl)&&(identical(other.autoConnect, autoConnect) || other.autoConnect == autoConnect)&&(identical(other.jumpId, jumpId) || other.jumpId == jumpId)&&(identical(other.custom, custom) || other.custom == custom)&&(identical(other.wolCfg, wolCfg) || other.wolCfg == wolCfg)&&const DeepCollectionEquality().equals(other.envs, envs)&&(identical(other.id, id) || other.id == id)&&(identical(other.customSystemType, customSystemType) || other.customSystemType == customSystemType)&&const DeepCollectionEquality().equals(other.disabledCmdTypes, disabledCmdTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,name,ip,port,user,pwd,keyId,const DeepCollectionEquality().hash(tags),alterUrl,autoConnect,jumpId,custom,wolCfg,const DeepCollectionEquality().hash(envs),id,customSystemType);
|
int get hashCode => Object.hash(runtimeType,name,ip,port,user,pwd,keyId,const DeepCollectionEquality().hash(tags),alterUrl,autoConnect,jumpId,custom,wolCfg,const DeepCollectionEquality().hash(envs),id,customSystemType,const DeepCollectionEquality().hash(disabledCmdTypes));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -49,7 +50,7 @@ abstract mixin class $SpiCopyWith<$Res> {
|
|||||||
factory $SpiCopyWith(Spi value, $Res Function(Spi) _then) = _$SpiCopyWithImpl;
|
factory $SpiCopyWith(Spi value, $Res Function(Spi) _then) = _$SpiCopyWithImpl;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String name, String ip, int port, String user, String? pwd,@JsonKey(name: 'pubKeyId') String? keyId, List<String>? tags, String? alterUrl, bool autoConnect, String? jumpId, ServerCustom? custom, WakeOnLanCfg? wolCfg, Map<String, String>? envs,@JsonKey(fromJson: Spi.parseId) String id,@JsonKey(includeIfNull: false) SystemType? customSystemType
|
String name, String ip, int port, String user, String? pwd,@JsonKey(name: 'pubKeyId') String? keyId, List<String>? tags, String? alterUrl, bool autoConnect, String? jumpId, ServerCustom? custom, WakeOnLanCfg? wolCfg, Map<String, String>? envs,@JsonKey(fromJson: Spi.parseId) String id,@JsonKey(includeIfNull: false) SystemType? customSystemType,@JsonKey(includeIfNull: false) List<String>? disabledCmdTypes
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -66,7 +67,7 @@ class _$SpiCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of Spi
|
/// Create a copy of Spi
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@pragma('vm:prefer-inline') @override $Res call({Object? name = null,Object? ip = null,Object? port = null,Object? user = null,Object? pwd = freezed,Object? keyId = freezed,Object? tags = freezed,Object? alterUrl = freezed,Object? autoConnect = null,Object? jumpId = freezed,Object? custom = freezed,Object? wolCfg = freezed,Object? envs = freezed,Object? id = null,Object? customSystemType = freezed,}) {
|
@pragma('vm:prefer-inline') @override $Res call({Object? name = null,Object? ip = null,Object? port = null,Object? user = null,Object? pwd = freezed,Object? keyId = freezed,Object? tags = freezed,Object? alterUrl = freezed,Object? autoConnect = null,Object? jumpId = freezed,Object? custom = freezed,Object? wolCfg = freezed,Object? envs = freezed,Object? id = null,Object? customSystemType = freezed,Object? disabledCmdTypes = freezed,}) {
|
||||||
return _then(_self.copyWith(
|
return _then(_self.copyWith(
|
||||||
name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
as String,ip: null == ip ? _self.ip : ip // ignore: cast_nullable_to_non_nullable
|
as String,ip: null == ip ? _self.ip : ip // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -83,7 +84,8 @@ as ServerCustom?,wolCfg: freezed == wolCfg ? _self.wolCfg : wolCfg // ignore: ca
|
|||||||
as WakeOnLanCfg?,envs: freezed == envs ? _self.envs : envs // ignore: cast_nullable_to_non_nullable
|
as WakeOnLanCfg?,envs: freezed == envs ? _self.envs : envs // ignore: cast_nullable_to_non_nullable
|
||||||
as Map<String, String>?,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
as Map<String, String>?,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,customSystemType: freezed == customSystemType ? _self.customSystemType : customSystemType // ignore: cast_nullable_to_non_nullable
|
as String,customSystemType: freezed == customSystemType ? _self.customSystemType : customSystemType // ignore: cast_nullable_to_non_nullable
|
||||||
as SystemType?,
|
as SystemType?,disabledCmdTypes: freezed == disabledCmdTypes ? _self.disabledCmdTypes : disabledCmdTypes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<String>?,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,7 +96,7 @@ as SystemType?,
|
|||||||
|
|
||||||
@JsonSerializable(includeIfNull: false)
|
@JsonSerializable(includeIfNull: false)
|
||||||
class _Spi extends Spi {
|
class _Spi extends Spi {
|
||||||
const _Spi({required this.name, required this.ip, required this.port, required this.user, this.pwd, @JsonKey(name: 'pubKeyId') this.keyId, final List<String>? tags, this.alterUrl, this.autoConnect = true, this.jumpId, this.custom, this.wolCfg, final Map<String, String>? envs, @JsonKey(fromJson: Spi.parseId) this.id = '', @JsonKey(includeIfNull: false) this.customSystemType}): _tags = tags,_envs = envs,super._();
|
const _Spi({required this.name, required this.ip, required this.port, required this.user, this.pwd, @JsonKey(name: 'pubKeyId') this.keyId, final List<String>? tags, this.alterUrl, this.autoConnect = true, this.jumpId, this.custom, this.wolCfg, final Map<String, String>? envs, @JsonKey(fromJson: Spi.parseId) this.id = '', @JsonKey(includeIfNull: false) this.customSystemType, @JsonKey(includeIfNull: false) final List<String>? disabledCmdTypes}): _tags = tags,_envs = envs,_disabledCmdTypes = disabledCmdTypes,super._();
|
||||||
factory _Spi.fromJson(Map<String, dynamic> json) => _$SpiFromJson(json);
|
factory _Spi.fromJson(Map<String, dynamic> json) => _$SpiFromJson(json);
|
||||||
|
|
||||||
@override final String name;
|
@override final String name;
|
||||||
@@ -133,6 +135,17 @@ class _Spi extends Spi {
|
|||||||
@override@JsonKey(fromJson: Spi.parseId) final String id;
|
@override@JsonKey(fromJson: Spi.parseId) final String id;
|
||||||
/// Custom system type (unix or windows). If set, skip auto-detection.
|
/// Custom system type (unix or windows). If set, skip auto-detection.
|
||||||
@override@JsonKey(includeIfNull: false) final SystemType? customSystemType;
|
@override@JsonKey(includeIfNull: false) final SystemType? customSystemType;
|
||||||
|
/// Disabled command types for this server
|
||||||
|
final List<String>? _disabledCmdTypes;
|
||||||
|
/// Disabled command types for this server
|
||||||
|
@override@JsonKey(includeIfNull: false) List<String>? get disabledCmdTypes {
|
||||||
|
final value = _disabledCmdTypes;
|
||||||
|
if (value == null) return null;
|
||||||
|
if (_disabledCmdTypes is EqualUnmodifiableListView) return _disabledCmdTypes;
|
||||||
|
// ignore: implicit_dynamic_type
|
||||||
|
return EqualUnmodifiableListView(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Create a copy of Spi
|
/// Create a copy of Spi
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@@ -147,12 +160,12 @@ Map<String, dynamic> toJson() {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Spi&&(identical(other.name, name) || other.name == name)&&(identical(other.ip, ip) || other.ip == ip)&&(identical(other.port, port) || other.port == port)&&(identical(other.user, user) || other.user == user)&&(identical(other.pwd, pwd) || other.pwd == pwd)&&(identical(other.keyId, keyId) || other.keyId == keyId)&&const DeepCollectionEquality().equals(other._tags, _tags)&&(identical(other.alterUrl, alterUrl) || other.alterUrl == alterUrl)&&(identical(other.autoConnect, autoConnect) || other.autoConnect == autoConnect)&&(identical(other.jumpId, jumpId) || other.jumpId == jumpId)&&(identical(other.custom, custom) || other.custom == custom)&&(identical(other.wolCfg, wolCfg) || other.wolCfg == wolCfg)&&const DeepCollectionEquality().equals(other._envs, _envs)&&(identical(other.id, id) || other.id == id)&&(identical(other.customSystemType, customSystemType) || other.customSystemType == customSystemType));
|
return identical(this, other) || (other.runtimeType == runtimeType&&other is _Spi&&(identical(other.name, name) || other.name == name)&&(identical(other.ip, ip) || other.ip == ip)&&(identical(other.port, port) || other.port == port)&&(identical(other.user, user) || other.user == user)&&(identical(other.pwd, pwd) || other.pwd == pwd)&&(identical(other.keyId, keyId) || other.keyId == keyId)&&const DeepCollectionEquality().equals(other._tags, _tags)&&(identical(other.alterUrl, alterUrl) || other.alterUrl == alterUrl)&&(identical(other.autoConnect, autoConnect) || other.autoConnect == autoConnect)&&(identical(other.jumpId, jumpId) || other.jumpId == jumpId)&&(identical(other.custom, custom) || other.custom == custom)&&(identical(other.wolCfg, wolCfg) || other.wolCfg == wolCfg)&&const DeepCollectionEquality().equals(other._envs, _envs)&&(identical(other.id, id) || other.id == id)&&(identical(other.customSystemType, customSystemType) || other.customSystemType == customSystemType)&&const DeepCollectionEquality().equals(other._disabledCmdTypes, _disabledCmdTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@JsonKey(includeFromJson: false, includeToJson: false)
|
@JsonKey(includeFromJson: false, includeToJson: false)
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(runtimeType,name,ip,port,user,pwd,keyId,const DeepCollectionEquality().hash(_tags),alterUrl,autoConnect,jumpId,custom,wolCfg,const DeepCollectionEquality().hash(_envs),id,customSystemType);
|
int get hashCode => Object.hash(runtimeType,name,ip,port,user,pwd,keyId,const DeepCollectionEquality().hash(_tags),alterUrl,autoConnect,jumpId,custom,wolCfg,const DeepCollectionEquality().hash(_envs),id,customSystemType,const DeepCollectionEquality().hash(_disabledCmdTypes));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -163,7 +176,7 @@ abstract mixin class _$SpiCopyWith<$Res> implements $SpiCopyWith<$Res> {
|
|||||||
factory _$SpiCopyWith(_Spi value, $Res Function(_Spi) _then) = __$SpiCopyWithImpl;
|
factory _$SpiCopyWith(_Spi value, $Res Function(_Spi) _then) = __$SpiCopyWithImpl;
|
||||||
@override @useResult
|
@override @useResult
|
||||||
$Res call({
|
$Res call({
|
||||||
String name, String ip, int port, String user, String? pwd,@JsonKey(name: 'pubKeyId') String? keyId, List<String>? tags, String? alterUrl, bool autoConnect, String? jumpId, ServerCustom? custom, WakeOnLanCfg? wolCfg, Map<String, String>? envs,@JsonKey(fromJson: Spi.parseId) String id,@JsonKey(includeIfNull: false) SystemType? customSystemType
|
String name, String ip, int port, String user, String? pwd,@JsonKey(name: 'pubKeyId') String? keyId, List<String>? tags, String? alterUrl, bool autoConnect, String? jumpId, ServerCustom? custom, WakeOnLanCfg? wolCfg, Map<String, String>? envs,@JsonKey(fromJson: Spi.parseId) String id,@JsonKey(includeIfNull: false) SystemType? customSystemType,@JsonKey(includeIfNull: false) List<String>? disabledCmdTypes
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
@@ -180,7 +193,7 @@ class __$SpiCopyWithImpl<$Res>
|
|||||||
|
|
||||||
/// Create a copy of Spi
|
/// Create a copy of Spi
|
||||||
/// with the given fields replaced by the non-null parameter values.
|
/// with the given fields replaced by the non-null parameter values.
|
||||||
@override @pragma('vm:prefer-inline') $Res call({Object? name = null,Object? ip = null,Object? port = null,Object? user = null,Object? pwd = freezed,Object? keyId = freezed,Object? tags = freezed,Object? alterUrl = freezed,Object? autoConnect = null,Object? jumpId = freezed,Object? custom = freezed,Object? wolCfg = freezed,Object? envs = freezed,Object? id = null,Object? customSystemType = freezed,}) {
|
@override @pragma('vm:prefer-inline') $Res call({Object? name = null,Object? ip = null,Object? port = null,Object? user = null,Object? pwd = freezed,Object? keyId = freezed,Object? tags = freezed,Object? alterUrl = freezed,Object? autoConnect = null,Object? jumpId = freezed,Object? custom = freezed,Object? wolCfg = freezed,Object? envs = freezed,Object? id = null,Object? customSystemType = freezed,Object? disabledCmdTypes = freezed,}) {
|
||||||
return _then(_Spi(
|
return _then(_Spi(
|
||||||
name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable
|
||||||
as String,ip: null == ip ? _self.ip : ip // ignore: cast_nullable_to_non_nullable
|
as String,ip: null == ip ? _self.ip : ip // ignore: cast_nullable_to_non_nullable
|
||||||
@@ -197,7 +210,8 @@ as ServerCustom?,wolCfg: freezed == wolCfg ? _self.wolCfg : wolCfg // ignore: ca
|
|||||||
as WakeOnLanCfg?,envs: freezed == envs ? _self._envs : envs // ignore: cast_nullable_to_non_nullable
|
as WakeOnLanCfg?,envs: freezed == envs ? _self._envs : envs // ignore: cast_nullable_to_non_nullable
|
||||||
as Map<String, String>?,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
as Map<String, String>?,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable
|
||||||
as String,customSystemType: freezed == customSystemType ? _self.customSystemType : customSystemType // ignore: cast_nullable_to_non_nullable
|
as String,customSystemType: freezed == customSystemType ? _self.customSystemType : customSystemType // ignore: cast_nullable_to_non_nullable
|
||||||
as SystemType?,
|
as SystemType?,disabledCmdTypes: freezed == disabledCmdTypes ? _self._disabledCmdTypes : disabledCmdTypes // ignore: cast_nullable_to_non_nullable
|
||||||
|
as List<String>?,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ _Spi _$SpiFromJson(Map<String, dynamic> json) => _Spi(
|
|||||||
_$SystemTypeEnumMap,
|
_$SystemTypeEnumMap,
|
||||||
json['customSystemType'],
|
json['customSystemType'],
|
||||||
),
|
),
|
||||||
|
disabledCmdTypes: (json['disabledCmdTypes'] as List<dynamic>?)
|
||||||
|
?.map((e) => e as String)
|
||||||
|
.toList(),
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$SpiToJson(_Spi instance) => <String, dynamic>{
|
Map<String, dynamic> _$SpiToJson(_Spi instance) => <String, dynamic>{
|
||||||
@@ -50,6 +53,7 @@ Map<String, dynamic> _$SpiToJson(_Spi instance) => <String, dynamic>{
|
|||||||
'id': instance.id,
|
'id': instance.id,
|
||||||
if (_$SystemTypeEnumMap[instance.customSystemType] case final value?)
|
if (_$SystemTypeEnumMap[instance.customSystemType] case final value?)
|
||||||
'customSystemType': value,
|
'customSystemType': value,
|
||||||
|
if (instance.disabledCmdTypes case final value?) 'disabledCmdTypes': value,
|
||||||
};
|
};
|
||||||
|
|
||||||
const _$SystemTypeEnumMap = {
|
const _$SystemTypeEnumMap = {
|
||||||
|
|||||||
@@ -20,14 +20,14 @@ import 'package:server_box/data/model/server/windows_parser.dart';
|
|||||||
|
|
||||||
class ServerStatusUpdateReq {
|
class ServerStatusUpdateReq {
|
||||||
final ServerStatus ss;
|
final ServerStatus ss;
|
||||||
final List<String> segments;
|
final Map<String, String> parsedOutput;
|
||||||
final SystemType system;
|
final SystemType system;
|
||||||
final Map<String, String> customCmds;
|
final Map<String, String> customCmds;
|
||||||
|
|
||||||
const ServerStatusUpdateReq({
|
const ServerStatusUpdateReq({
|
||||||
required this.system,
|
required this.system,
|
||||||
required this.ss,
|
required this.ss,
|
||||||
required this.segments,
|
required this.parsedOutput,
|
||||||
required this.customCmds,
|
required this.customCmds,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -43,20 +43,20 @@ Future<ServerStatus> getStatus(ServerStatusUpdateReq req) async {
|
|||||||
// Wrap each operation with a try-catch, so that if one operation fails,
|
// Wrap each operation with a try-catch, so that if one operation fails,
|
||||||
// the following operations can still be executed.
|
// the following operations can still be executed.
|
||||||
Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
||||||
final segments = req.segments;
|
final parsedOutput = req.parsedOutput;
|
||||||
|
|
||||||
final time = int.tryParse(StatusCmdType.time.find(segments)) ??
|
final time = int.tryParse(StatusCmdType.time.findInMap(parsedOutput)) ??
|
||||||
DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final net = NetSpeed.parse(StatusCmdType.net.find(segments), time);
|
final net = NetSpeed.parse(StatusCmdType.net.findInMap(parsedOutput), time);
|
||||||
req.ss.netSpeed.update(net);
|
req.ss.netSpeed.update(net);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final sys = _parseSysVer(StatusCmdType.sys.find(segments));
|
final sys = _parseSysVer(StatusCmdType.sys.findInMap(parsedOutput));
|
||||||
if (sys != null) {
|
if (sys != null) {
|
||||||
req.ss.more[StatusCmdType.sys] = sys;
|
req.ss.more[StatusCmdType.sys] = sys;
|
||||||
}
|
}
|
||||||
@@ -65,7 +65,7 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final host = _parseHostName(StatusCmdType.host.find(segments));
|
final host = _parseHostName(StatusCmdType.host.findInMap(parsedOutput));
|
||||||
if (host != null) {
|
if (host != null) {
|
||||||
req.ss.more[StatusCmdType.host] = host;
|
req.ss.more[StatusCmdType.host] = host;
|
||||||
}
|
}
|
||||||
@@ -74,9 +74,9 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final cpus = SingleCpuCore.parse(StatusCmdType.cpu.find(segments));
|
final cpus = SingleCpuCore.parse(StatusCmdType.cpu.findInMap(parsedOutput));
|
||||||
req.ss.cpu.update(cpus);
|
req.ss.cpu.update(cpus);
|
||||||
final brand = CpuBrand.parse(StatusCmdType.cpuBrand.find(segments));
|
final brand = CpuBrand.parse(StatusCmdType.cpuBrand.findInMap(parsedOutput));
|
||||||
req.ss.cpu.brand.clear();
|
req.ss.cpu.brand.clear();
|
||||||
req.ss.cpu.brand.addAll(brand);
|
req.ss.cpu.brand.addAll(brand);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
@@ -85,15 +85,15 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
req.ss.temps.parse(
|
req.ss.temps.parse(
|
||||||
StatusCmdType.tempType.find(segments),
|
StatusCmdType.tempType.findInMap(parsedOutput),
|
||||||
StatusCmdType.tempVal.find(segments),
|
StatusCmdType.tempVal.findInMap(parsedOutput),
|
||||||
);
|
);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final tcp = Conn.parse(StatusCmdType.conn.find(segments));
|
final tcp = Conn.parse(StatusCmdType.conn.findInMap(parsedOutput));
|
||||||
if (tcp != null) {
|
if (tcp != null) {
|
||||||
req.ss.tcp = tcp;
|
req.ss.tcp = tcp;
|
||||||
}
|
}
|
||||||
@@ -102,20 +102,20 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.ss.disk = Disk.parse(StatusCmdType.disk.find(segments));
|
req.ss.disk = Disk.parse(StatusCmdType.disk.findInMap(parsedOutput));
|
||||||
req.ss.diskUsage = DiskUsage.parse(req.ss.disk);
|
req.ss.diskUsage = DiskUsage.parse(req.ss.disk);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.ss.mem = Memory.parse(StatusCmdType.mem.find(segments));
|
req.ss.mem = Memory.parse(StatusCmdType.mem.findInMap(parsedOutput));
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final uptime = _parseUpTime(StatusCmdType.uptime.find(segments));
|
final uptime = _parseUpTime(StatusCmdType.uptime.findInMap(parsedOutput));
|
||||||
if (uptime != null) {
|
if (uptime != null) {
|
||||||
req.ss.more[StatusCmdType.uptime] = uptime;
|
req.ss.more[StatusCmdType.uptime] = uptime;
|
||||||
}
|
}
|
||||||
@@ -124,39 +124,39 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.ss.swap = Swap.parse(StatusCmdType.mem.find(segments));
|
req.ss.swap = Swap.parse(StatusCmdType.mem.findInMap(parsedOutput));
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final diskio = DiskIO.parse(StatusCmdType.diskio.find(segments), time);
|
final diskio = DiskIO.parse(StatusCmdType.diskio.findInMap(parsedOutput), time);
|
||||||
req.ss.diskIO.update(diskio);
|
req.ss.diskIO.update(diskio);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final smarts = DiskSmart.parse(StatusCmdType.diskSmart.find(segments));
|
final smarts = DiskSmart.parse(StatusCmdType.diskSmart.findInMap(parsedOutput));
|
||||||
req.ss.diskSmart = smarts;
|
req.ss.diskSmart = smarts;
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.ss.nvidia = NvidiaSmi.fromXml(StatusCmdType.nvidia.find(segments));
|
req.ss.nvidia = NvidiaSmi.fromXml(StatusCmdType.nvidia.findInMap(parsedOutput));
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.ss.amd = AmdSmi.fromJson(StatusCmdType.amd.find(segments));
|
req.ss.amd = AmdSmi.fromJson(StatusCmdType.amd.findInMap(parsedOutput));
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final battery = StatusCmdType.battery.find(segments);
|
final battery = StatusCmdType.battery.findInMap(parsedOutput);
|
||||||
|
|
||||||
/// Only collect li-poly batteries
|
/// Only collect li-poly batteries
|
||||||
final batteries = Batteries.parse(battery, true);
|
final batteries = Batteries.parse(battery, true);
|
||||||
@@ -169,7 +169,7 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final sensors = SensorItem.parse(StatusCmdType.sensors.find(segments));
|
final sensors = SensorItem.parse(StatusCmdType.sensors.findInMap(parsedOutput));
|
||||||
if (sensors.isNotEmpty) {
|
if (sensors.isNotEmpty) {
|
||||||
req.ss.sensors.clear();
|
req.ss.sensors.clear();
|
||||||
req.ss.sensors.addAll(sensors);
|
req.ss.sensors.addAll(sensors);
|
||||||
@@ -179,9 +179,9 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (int idx = 0; idx < req.customCmds.length; idx++) {
|
for (final entry in req.customCmds.entries) {
|
||||||
final key = req.customCmds.keys.elementAt(idx);
|
final key = entry.key;
|
||||||
final value = req.segments[idx + req.system.segmentsLen];
|
final value = req.parsedOutput[key] ?? '';
|
||||||
req.ss.customCmds[key] = value;
|
req.ss.customCmds[key] = value;
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
@@ -193,36 +193,36 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
|
|||||||
|
|
||||||
// Same as above, wrap with try-catch
|
// Same as above, wrap with try-catch
|
||||||
Future<ServerStatus> _getBsdStatus(ServerStatusUpdateReq req) async {
|
Future<ServerStatus> _getBsdStatus(ServerStatusUpdateReq req) async {
|
||||||
final segments = req.segments;
|
final parsedOutput = req.parsedOutput;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final time = int.parse(BSDStatusCmdType.time.find(segments));
|
final time = int.parse(BSDStatusCmdType.time.findInMap(parsedOutput));
|
||||||
final net = NetSpeed.parseBsd(BSDStatusCmdType.net.find(segments), time);
|
final net = NetSpeed.parseBsd(BSDStatusCmdType.net.findInMap(parsedOutput), time);
|
||||||
req.ss.netSpeed.update(net);
|
req.ss.netSpeed.update(net);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.ss.more[StatusCmdType.sys] = BSDStatusCmdType.sys.find(segments);
|
req.ss.more[StatusCmdType.sys] = BSDStatusCmdType.sys.findInMap(parsedOutput);
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.ss.cpu = parseBsdCpu(BSDStatusCmdType.cpu.find(segments));
|
req.ss.cpu = parseBsdCpu(BSDStatusCmdType.cpu.findInMap(parsedOutput));
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.ss.mem = parseBsdMemory(BSDStatusCmdType.mem.find(segments));
|
req.ss.mem = parseBsdMemory(BSDStatusCmdType.mem.findInMap(parsedOutput));
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final uptime = _parseUpTime(BSDStatusCmdType.uptime.find(segments));
|
final uptime = _parseUpTime(BSDStatusCmdType.uptime.findInMap(parsedOutput));
|
||||||
if (uptime != null) {
|
if (uptime != null) {
|
||||||
req.ss.more[StatusCmdType.uptime] = uptime;
|
req.ss.more[StatusCmdType.uptime] = uptime;
|
||||||
}
|
}
|
||||||
@@ -231,7 +231,7 @@ Future<ServerStatus> _getBsdStatus(ServerStatusUpdateReq req) async {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.ss.disk = Disk.parse(BSDStatusCmdType.disk.find(segments));
|
req.ss.disk = Disk.parse(BSDStatusCmdType.disk.findInMap(parsedOutput));
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning(e, s);
|
Loggers.app.warning(e, s);
|
||||||
}
|
}
|
||||||
@@ -302,32 +302,32 @@ String? _parseHostName(String raw) {
|
|||||||
|
|
||||||
// Windows status parsing implementation
|
// Windows status parsing implementation
|
||||||
Future<ServerStatus> _getWindowsStatus(ServerStatusUpdateReq req) async {
|
Future<ServerStatus> _getWindowsStatus(ServerStatusUpdateReq req) async {
|
||||||
final segments = req.segments;
|
final parsedOutput = req.parsedOutput;
|
||||||
final time = int.tryParse(WindowsStatusCmdType.time.find(segments)) ??
|
final time = int.tryParse(WindowsStatusCmdType.time.findInMap(parsedOutput)) ??
|
||||||
DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||||
|
|
||||||
// Parse all different resource types using helper methods
|
// Parse all different resource types using helper methods
|
||||||
_parseWindowsNetworkData(req, segments, time);
|
_parseWindowsNetworkData(req, parsedOutput, time);
|
||||||
_parseWindowsSystemData(req, segments);
|
_parseWindowsSystemData(req, parsedOutput);
|
||||||
_parseWindowsHostData(req, segments);
|
_parseWindowsHostData(req, parsedOutput);
|
||||||
_parseWindowsCpuData(req, segments);
|
_parseWindowsCpuData(req, parsedOutput);
|
||||||
_parseWindowsMemoryData(req, segments);
|
_parseWindowsMemoryData(req, parsedOutput);
|
||||||
_parseWindowsDiskData(req, segments);
|
_parseWindowsDiskData(req, parsedOutput);
|
||||||
_parseWindowsUptimeData(req, segments);
|
_parseWindowsUptimeData(req, parsedOutput);
|
||||||
_parseWindowsDiskIOData(req, segments, time);
|
_parseWindowsDiskIOData(req, parsedOutput, time);
|
||||||
_parseWindowsConnectionData(req, segments);
|
_parseWindowsConnectionData(req, parsedOutput);
|
||||||
_parseWindowsBatteryData(req, segments);
|
_parseWindowsBatteryData(req, parsedOutput);
|
||||||
_parseWindowsTemperatureData(req, segments);
|
_parseWindowsTemperatureData(req, parsedOutput);
|
||||||
_parseWindowsGpuData(req, segments);
|
_parseWindowsGpuData(req, parsedOutput);
|
||||||
WindowsParser.parseCustomCommands(req.ss, segments, req.customCmds, req.system.segmentsLen);
|
WindowsParser.parseCustomCommands(req.ss, req.parsedOutput, req.customCmds);
|
||||||
|
|
||||||
return req.ss;
|
return req.ss;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Windows network data
|
/// Parse Windows network data
|
||||||
void _parseWindowsNetworkData(ServerStatusUpdateReq req, List<String> segments, int time) {
|
void _parseWindowsNetworkData(ServerStatusUpdateReq req, Map<String, String> parsedOutput, int time) {
|
||||||
try {
|
try {
|
||||||
final netRaw = WindowsStatusCmdType.net.find(segments);
|
final netRaw = WindowsStatusCmdType.net.findInMap(parsedOutput);
|
||||||
if (netRaw.isNotEmpty &&
|
if (netRaw.isNotEmpty &&
|
||||||
netRaw != 'null' &&
|
netRaw != 'null' &&
|
||||||
!netRaw.contains('network_error') &&
|
!netRaw.contains('network_error') &&
|
||||||
@@ -344,9 +344,9 @@ void _parseWindowsNetworkData(ServerStatusUpdateReq req, List<String> segments,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Windows system information
|
/// Parse Windows system information
|
||||||
void _parseWindowsSystemData(ServerStatusUpdateReq req, List<String> segments) {
|
void _parseWindowsSystemData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
|
||||||
try {
|
try {
|
||||||
final sys = WindowsStatusCmdType.sys.find(segments);
|
final sys = WindowsStatusCmdType.sys.findInMap(parsedOutput);
|
||||||
if (sys.isNotEmpty) {
|
if (sys.isNotEmpty) {
|
||||||
req.ss.more[StatusCmdType.sys] = sys;
|
req.ss.more[StatusCmdType.sys] = sys;
|
||||||
}
|
}
|
||||||
@@ -356,9 +356,9 @@ void _parseWindowsSystemData(ServerStatusUpdateReq req, List<String> segments) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Windows host information
|
/// Parse Windows host information
|
||||||
void _parseWindowsHostData(ServerStatusUpdateReq req, List<String> segments) {
|
void _parseWindowsHostData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
|
||||||
try {
|
try {
|
||||||
final host = _parseHostName(WindowsStatusCmdType.host.find(segments));
|
final host = _parseHostName(WindowsStatusCmdType.host.findInMap(parsedOutput));
|
||||||
if (host != null) {
|
if (host != null) {
|
||||||
req.ss.more[StatusCmdType.host] = host;
|
req.ss.more[StatusCmdType.host] = host;
|
||||||
}
|
}
|
||||||
@@ -368,10 +368,10 @@ void _parseWindowsHostData(ServerStatusUpdateReq req, List<String> segments) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Windows CPU data and brand information
|
/// Parse Windows CPU data and brand information
|
||||||
void _parseWindowsCpuData(ServerStatusUpdateReq req, List<String> segments) {
|
void _parseWindowsCpuData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
|
||||||
try {
|
try {
|
||||||
// Windows CPU parsing - JSON format from PowerShell
|
// Windows CPU parsing - JSON format from PowerShell
|
||||||
final cpuRaw = WindowsStatusCmdType.cpu.find(segments);
|
final cpuRaw = WindowsStatusCmdType.cpu.findInMap(parsedOutput);
|
||||||
if (cpuRaw.isNotEmpty &&
|
if (cpuRaw.isNotEmpty &&
|
||||||
cpuRaw != 'null' &&
|
cpuRaw != 'null' &&
|
||||||
!cpuRaw.contains('error') &&
|
!cpuRaw.contains('error') &&
|
||||||
@@ -383,7 +383,7 @@ void _parseWindowsCpuData(ServerStatusUpdateReq req, List<String> segments) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Windows CPU brand parsing
|
// Windows CPU brand parsing
|
||||||
final brandRaw = WindowsStatusCmdType.cpuBrand.find(segments);
|
final brandRaw = WindowsStatusCmdType.cpuBrand.findInMap(parsedOutput);
|
||||||
if (brandRaw.isNotEmpty && brandRaw != 'null') {
|
if (brandRaw.isNotEmpty && brandRaw != 'null') {
|
||||||
req.ss.cpu.brand.clear();
|
req.ss.cpu.brand.clear();
|
||||||
req.ss.cpu.brand[brandRaw.trim()] = 1;
|
req.ss.cpu.brand[brandRaw.trim()] = 1;
|
||||||
@@ -394,9 +394,9 @@ void _parseWindowsCpuData(ServerStatusUpdateReq req, List<String> segments) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Windows memory data
|
/// Parse Windows memory data
|
||||||
void _parseWindowsMemoryData(ServerStatusUpdateReq req, List<String> segments) {
|
void _parseWindowsMemoryData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
|
||||||
try {
|
try {
|
||||||
final memRaw = WindowsStatusCmdType.mem.find(segments);
|
final memRaw = WindowsStatusCmdType.mem.findInMap(parsedOutput);
|
||||||
if (memRaw.isNotEmpty &&
|
if (memRaw.isNotEmpty &&
|
||||||
memRaw != 'null' &&
|
memRaw != 'null' &&
|
||||||
!memRaw.contains('error') &&
|
!memRaw.contains('error') &&
|
||||||
@@ -412,9 +412,9 @@ void _parseWindowsMemoryData(ServerStatusUpdateReq req, List<String> segments) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Windows disk data
|
/// Parse Windows disk data
|
||||||
void _parseWindowsDiskData(ServerStatusUpdateReq req, List<String> segments) {
|
void _parseWindowsDiskData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
|
||||||
try {
|
try {
|
||||||
final diskRaw = WindowsStatusCmdType.disk.find(segments);
|
final diskRaw = WindowsStatusCmdType.disk.findInMap(parsedOutput);
|
||||||
if (diskRaw.isNotEmpty && diskRaw != 'null') {
|
if (diskRaw.isNotEmpty && diskRaw != 'null') {
|
||||||
final disks = WindowsParser.parseDisks(diskRaw);
|
final disks = WindowsParser.parseDisks(diskRaw);
|
||||||
req.ss.disk = disks;
|
req.ss.disk = disks;
|
||||||
@@ -426,9 +426,9 @@ void _parseWindowsDiskData(ServerStatusUpdateReq req, List<String> segments) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Windows uptime data
|
/// Parse Windows uptime data
|
||||||
void _parseWindowsUptimeData(ServerStatusUpdateReq req, List<String> segments) {
|
void _parseWindowsUptimeData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
|
||||||
try {
|
try {
|
||||||
final uptime = WindowsParser.parseUpTime(WindowsStatusCmdType.uptime.find(segments));
|
final uptime = WindowsParser.parseUpTime(WindowsStatusCmdType.uptime.findInMap(parsedOutput));
|
||||||
if (uptime != null) {
|
if (uptime != null) {
|
||||||
req.ss.more[StatusCmdType.uptime] = uptime;
|
req.ss.more[StatusCmdType.uptime] = uptime;
|
||||||
}
|
}
|
||||||
@@ -438,9 +438,9 @@ void _parseWindowsUptimeData(ServerStatusUpdateReq req, List<String> segments) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Windows disk I/O data
|
/// Parse Windows disk I/O data
|
||||||
void _parseWindowsDiskIOData(ServerStatusUpdateReq req, List<String> segments, int time) {
|
void _parseWindowsDiskIOData(ServerStatusUpdateReq req, Map<String, String> parsedOutput, int time) {
|
||||||
try {
|
try {
|
||||||
final diskIOraw = WindowsStatusCmdType.diskio.find(segments);
|
final diskIOraw = WindowsStatusCmdType.diskio.findInMap(parsedOutput);
|
||||||
if (diskIOraw.isNotEmpty && diskIOraw != 'null') {
|
if (diskIOraw.isNotEmpty && diskIOraw != 'null') {
|
||||||
final diskio = _parseWindowsDiskIO(diskIOraw, time);
|
final diskio = _parseWindowsDiskIO(diskIOraw, time);
|
||||||
req.ss.diskIO.update(diskio);
|
req.ss.diskIO.update(diskio);
|
||||||
@@ -451,9 +451,9 @@ void _parseWindowsDiskIOData(ServerStatusUpdateReq req, List<String> segments, i
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Windows connection data
|
/// Parse Windows connection data
|
||||||
void _parseWindowsConnectionData(ServerStatusUpdateReq req, List<String> segments) {
|
void _parseWindowsConnectionData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
|
||||||
try {
|
try {
|
||||||
final connStr = WindowsStatusCmdType.conn.find(segments);
|
final connStr = WindowsStatusCmdType.conn.findInMap(parsedOutput);
|
||||||
final connCount = int.tryParse(connStr.trim());
|
final connCount = int.tryParse(connStr.trim());
|
||||||
if (connCount != null) {
|
if (connCount != null) {
|
||||||
req.ss.tcp = Conn(maxConn: 0, active: connCount, passive: 0, fail: 0);
|
req.ss.tcp = Conn(maxConn: 0, active: connCount, passive: 0, fail: 0);
|
||||||
@@ -464,9 +464,9 @@ void _parseWindowsConnectionData(ServerStatusUpdateReq req, List<String> segment
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Windows battery data
|
/// Parse Windows battery data
|
||||||
void _parseWindowsBatteryData(ServerStatusUpdateReq req, List<String> segments) {
|
void _parseWindowsBatteryData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
|
||||||
try {
|
try {
|
||||||
final batteryRaw = WindowsStatusCmdType.battery.find(segments);
|
final batteryRaw = WindowsStatusCmdType.battery.findInMap(parsedOutput);
|
||||||
if (batteryRaw.isNotEmpty && batteryRaw != 'null') {
|
if (batteryRaw.isNotEmpty && batteryRaw != 'null') {
|
||||||
final batteries = _parseWindowsBatteries(batteryRaw);
|
final batteries = _parseWindowsBatteries(batteryRaw);
|
||||||
req.ss.batteries.clear();
|
req.ss.batteries.clear();
|
||||||
@@ -480,9 +480,9 @@ void _parseWindowsBatteryData(ServerStatusUpdateReq req, List<String> segments)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Windows temperature data
|
/// Parse Windows temperature data
|
||||||
void _parseWindowsTemperatureData(ServerStatusUpdateReq req, List<String> segments) {
|
void _parseWindowsTemperatureData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
|
||||||
try {
|
try {
|
||||||
final tempRaw = WindowsStatusCmdType.temp.find(segments);
|
final tempRaw = WindowsStatusCmdType.temp.findInMap(parsedOutput);
|
||||||
if (tempRaw.isNotEmpty && tempRaw != 'null') {
|
if (tempRaw.isNotEmpty && tempRaw != 'null') {
|
||||||
_parseWindowsTemperatures(req.ss.temps, tempRaw);
|
_parseWindowsTemperatures(req.ss.temps, tempRaw);
|
||||||
}
|
}
|
||||||
@@ -492,15 +492,15 @@ void _parseWindowsTemperatureData(ServerStatusUpdateReq req, List<String> segmen
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Parse Windows GPU data (NVIDIA/AMD)
|
/// Parse Windows GPU data (NVIDIA/AMD)
|
||||||
void _parseWindowsGpuData(ServerStatusUpdateReq req, List<String> segments) {
|
void _parseWindowsGpuData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
|
||||||
try {
|
try {
|
||||||
req.ss.nvidia = NvidiaSmi.fromXml(WindowsStatusCmdType.nvidia.find(segments));
|
req.ss.nvidia = NvidiaSmi.fromXml(WindowsStatusCmdType.nvidia.findInMap(parsedOutput));
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning('Windows NVIDIA GPU parsing failed: $e', s);
|
Loggers.app.warning('Windows NVIDIA GPU parsing failed: $e', s);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
req.ss.amd = AmdSmi.fromJson(WindowsStatusCmdType.amd.find(segments));
|
req.ss.amd = AmdSmi.fromJson(WindowsStatusCmdType.amd.findInMap(parsedOutput));
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning('Windows AMD GPU parsing failed: $e', s);
|
Loggers.app.warning('Windows AMD GPU parsing failed: $e', s);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'package:fl_lib/fl_lib.dart';
|
import 'package:fl_lib/fl_lib.dart';
|
||||||
import 'package:server_box/data/model/app/scripts/cmd_types.dart';
|
|
||||||
|
|
||||||
enum SystemType {
|
enum SystemType {
|
||||||
linux(linuxSign),
|
linux(linuxSign),
|
||||||
@@ -53,16 +52,4 @@ enum SystemType {
|
|||||||
return SystemType.linux;
|
return SystemType.linux;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isSegmentsLenMatch(int len) => len == segmentsLen;
|
|
||||||
|
|
||||||
int get segmentsLen {
|
|
||||||
switch (this) {
|
|
||||||
case SystemType.linux:
|
|
||||||
return StatusCmdType.values.length;
|
|
||||||
case SystemType.bsd:
|
|
||||||
return BSDStatusCmdType.values.length;
|
|
||||||
case SystemType.windows:
|
|
||||||
return WindowsStatusCmdType.values.length;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,27 +15,17 @@ import 'package:server_box/data/model/server/server.dart';
|
|||||||
class WindowsParser {
|
class WindowsParser {
|
||||||
const WindowsParser._();
|
const WindowsParser._();
|
||||||
|
|
||||||
/// Parse Windows custom commands from segments
|
/// Parse Windows custom commands from parsed output
|
||||||
static void parseCustomCommands(
|
static void parseCustomCommands(
|
||||||
ServerStatus serverStatus,
|
ServerStatus serverStatus,
|
||||||
List<String> segments,
|
Map<String, String> parsedOutput,
|
||||||
Map<String, String> customCmds,
|
Map<String, String> customCmds,
|
||||||
int systemSegmentsLength,
|
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
for (int idx = 0; idx < customCmds.length; idx++) {
|
for (final entry in customCmds.entries) {
|
||||||
final key = customCmds.keys.elementAt(idx);
|
final key = entry.key;
|
||||||
// Ensure we don't go out of bounds when accessing segments
|
final value = parsedOutput[key] ?? '';
|
||||||
final segmentIndex = idx + systemSegmentsLength;
|
serverStatus.customCmds[key] = value;
|
||||||
if (segmentIndex < segments.length) {
|
|
||||||
final value = segments[segmentIndex];
|
|
||||||
serverStatus.customCmds[key] = value;
|
|
||||||
} else {
|
|
||||||
Loggers.app.warning(
|
|
||||||
'Windows custom commands: segment index $segmentIndex out of bounds '
|
|
||||||
'(segments length: ${segments.length}, systemSegmentsLength: $systemSegmentsLength)'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
Loggers.app.warning('Windows custom commands parsing failed: $e', s);
|
Loggers.app.warning('Windows custom commands parsing failed: $e', s);
|
||||||
|
|||||||
@@ -338,7 +338,7 @@ class ServerProvider extends Provider {
|
|||||||
sv.status.system = detectedSystemType;
|
sv.status.system = detectedSystemType;
|
||||||
|
|
||||||
final (_, writeScriptResult) = await sv.client!.exec((session) async {
|
final (_, writeScriptResult) = await sv.client!.exec((session) async {
|
||||||
final scriptRaw = ShellFuncManager.allScript(spi.custom?.cmds, systemType: detectedSystemType).uint8List;
|
final scriptRaw = ShellFuncManager.allScript(spi.custom?.cmds, systemType: detectedSystemType, disabledCmdTypes: spi.disabledCmdTypes).uint8List;
|
||||||
session.stdin.add(scriptRaw);
|
session.stdin.add(scriptRaw);
|
||||||
session.stdin.close();
|
session.stdin.close();
|
||||||
}, entry: ShellFuncManager.getInstallShellCmd(spi.id, systemType: detectedSystemType));
|
}, entry: ShellFuncManager.getInstallShellCmd(spi.id, systemType: detectedSystemType));
|
||||||
@@ -406,31 +406,14 @@ class ServerProvider extends Provider {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final systemType = SystemType.parse(segments[0]);
|
|
||||||
final customCmdLen = spi.custom?.cmds?.length ?? 0;
|
|
||||||
if (!systemType.isSegmentsLenMatch(segments.length - customCmdLen)) {
|
|
||||||
TryLimiter.inc(sid);
|
|
||||||
if (raw.contains('Could not chdir to home directory /var/services/')) {
|
|
||||||
sv.status.err = SSHErr(type: SSHErrType.chdir, message: raw);
|
|
||||||
_setServerState(s, ServerConn.failed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
final expected = systemType.segmentsLen;
|
|
||||||
final actual = segments.length;
|
|
||||||
sv.status.err = SSHErr(
|
|
||||||
type: SSHErrType.segements,
|
|
||||||
message: 'Segments: expect $expected, got $actual, raw:\n\n$raw',
|
|
||||||
);
|
|
||||||
_setServerState(s, ServerConn.failed);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
sv.status.system = systemType;
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// Parse script output into command-specific map
|
||||||
|
final parsedOutput = ScriptConstants.parseScriptOutput(raw);
|
||||||
|
|
||||||
final req = ServerStatusUpdateReq(
|
final req = ServerStatusUpdateReq(
|
||||||
ss: sv.status,
|
ss: sv.status,
|
||||||
segments: segments,
|
parsedOutput: parsedOutput,
|
||||||
system: systemType,
|
system: sv.status.system,
|
||||||
customCmds: spi.custom?.cmds ?? {},
|
customCmds: spi.custom?.cmds ?? {},
|
||||||
);
|
);
|
||||||
sv.status = await Computer.shared.start(getStatus, req, taskName: 'StatusUpdateReq<${sv.id}>');
|
sv.status = await Computer.shared.start(getStatus, req, taskName: 'StatusUpdateReq<${sv.id}>');
|
||||||
|
|||||||
@@ -112,13 +112,14 @@ class SpiAdapter extends TypeAdapter<Spi> {
|
|||||||
envs: (fields[12] as Map?)?.cast<String, String>(),
|
envs: (fields[12] as Map?)?.cast<String, String>(),
|
||||||
id: fields[13] == null ? '' : fields[13] as String,
|
id: fields[13] == null ? '' : fields[13] as String,
|
||||||
customSystemType: fields[14] as SystemType?,
|
customSystemType: fields[14] as SystemType?,
|
||||||
|
disabledCmdTypes: (fields[15] as List?)?.cast<String>(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void write(BinaryWriter writer, Spi obj) {
|
void write(BinaryWriter writer, Spi obj) {
|
||||||
writer
|
writer
|
||||||
..writeByte(15)
|
..writeByte(16)
|
||||||
..writeByte(0)
|
..writeByte(0)
|
||||||
..write(obj.name)
|
..write(obj.name)
|
||||||
..writeByte(1)
|
..writeByte(1)
|
||||||
@@ -148,7 +149,9 @@ class SpiAdapter extends TypeAdapter<Spi> {
|
|||||||
..writeByte(13)
|
..writeByte(13)
|
||||||
..write(obj.id)
|
..write(obj.id)
|
||||||
..writeByte(14)
|
..writeByte(14)
|
||||||
..write(obj.customSystemType);
|
..write(obj.customSystemType)
|
||||||
|
..writeByte(15)
|
||||||
|
..write(obj.disabledCmdTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ types:
|
|||||||
index: 4
|
index: 4
|
||||||
Spi:
|
Spi:
|
||||||
typeId: 3
|
typeId: 3
|
||||||
nextIndex: 15
|
nextIndex: 16
|
||||||
fields:
|
fields:
|
||||||
name:
|
name:
|
||||||
index: 0
|
index: 0
|
||||||
@@ -59,6 +59,8 @@ types:
|
|||||||
index: 13
|
index: 13
|
||||||
customSystemType:
|
customSystemType:
|
||||||
index: 14
|
index: 14
|
||||||
|
disabledCmdTypes:
|
||||||
|
index: 15
|
||||||
VirtKey:
|
VirtKey:
|
||||||
typeId: 4
|
typeId: 4
|
||||||
nextIndex: 45
|
nextIndex: 45
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:icons_plus/icons_plus.dart';
|
import 'package:icons_plus/icons_plus.dart';
|
||||||
import 'package:server_box/core/extension/context/locale.dart';
|
import 'package:server_box/core/extension/context/locale.dart';
|
||||||
import 'package:server_box/core/route.dart';
|
import 'package:server_box/core/route.dart';
|
||||||
|
import 'package:server_box/data/model/app/scripts/cmd_types.dart';
|
||||||
import 'package:server_box/data/model/server/custom.dart';
|
import 'package:server_box/data/model/server/custom.dart';
|
||||||
import 'package:server_box/data/model/server/server.dart';
|
import 'package:server_box/data/model/server/server.dart';
|
||||||
import 'package:server_box/data/model/server/server_private_info.dart';
|
import 'package:server_box/data/model/server/server_private_info.dart';
|
||||||
@@ -61,6 +62,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
final _customCmds = <String, String>{}.vn;
|
final _customCmds = <String, String>{}.vn;
|
||||||
final _tags = <String>{}.vn;
|
final _tags = <String>{}.vn;
|
||||||
final _systemType = ValueNotifier<SystemType?>(null);
|
final _systemType = ValueNotifier<SystemType?>(null);
|
||||||
|
final _disabledCmdTypes = <String>{}.vn;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
@@ -94,6 +96,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
_customCmds.dispose();
|
_customCmds.dispose();
|
||||||
_tags.dispose();
|
_tags.dispose();
|
||||||
_systemType.dispose();
|
_systemType.dispose();
|
||||||
|
_disabledCmdTypes.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -289,6 +292,7 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
_buildEnvs(),
|
_buildEnvs(),
|
||||||
_buildPVEs(),
|
_buildPVEs(),
|
||||||
_buildCustomCmds(),
|
_buildCustomCmds(),
|
||||||
|
_buildDisabledCmdTypes(),
|
||||||
_buildCustomDev(),
|
_buildCustomDev(),
|
||||||
_buildWOLs(),
|
_buildWOLs(),
|
||||||
],
|
],
|
||||||
@@ -421,6 +425,24 @@ class _ServerEditPageState extends State<ServerEditPage> with AfterLayoutMixin {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildDisabledCmdTypes() {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
CenterGreyTitle('${libL10n.disabled} ${l10n.cmd}'),
|
||||||
|
_disabledCmdTypes.listenVal((disabled) {
|
||||||
|
return ListTile(
|
||||||
|
leading: const Icon(Icons.disabled_by_default),
|
||||||
|
title: Text('${libL10n.disabled} ${l10n.cmd}'),
|
||||||
|
subtitle: disabled.isEmpty ? null : Text(disabled.join(', '), style: UIs.textGrey),
|
||||||
|
trailing: const Icon(Icons.keyboard_arrow_right),
|
||||||
|
onTap: _onTapDisabledCmdTypes,
|
||||||
|
);
|
||||||
|
}).cardx,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildWOLs() {
|
Widget _buildWOLs() {
|
||||||
return Column(
|
return Column(
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
@@ -574,6 +596,52 @@ extension on _ServerEditPageState {
|
|||||||
_customCmds.value = res;
|
_customCmds.value = res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void _onTapDisabledCmdTypes() async {
|
||||||
|
final allCmdTypes = <String>{};
|
||||||
|
allCmdTypes.addAll(StatusCmdType.values.map((e) => e.name));
|
||||||
|
allCmdTypes.addAll(BSDStatusCmdType.values.map((e) => e.name));
|
||||||
|
allCmdTypes.addAll(WindowsStatusCmdType.values.map((e) => e.name));
|
||||||
|
|
||||||
|
// [TimeSeq] depends on the `time` cmd type, so it should be removed from the list
|
||||||
|
allCmdTypes.remove(StatusCmdType.time.name);
|
||||||
|
|
||||||
|
final selected = await _showCmdTypesDialog(allCmdTypes);
|
||||||
|
if (selected == null) return;
|
||||||
|
_disabledCmdTypes.value = selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Set<String>?> _showCmdTypesDialog(Set<String> allCmdTypes) {
|
||||||
|
return context.showRoundDialog<Set<String>>(
|
||||||
|
title: '${libL10n.disabled} ${l10n.cmd}',
|
||||||
|
child: SizedBox(
|
||||||
|
width: 270,
|
||||||
|
child: _disabledCmdTypes.listenVal((disabled) {
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: allCmdTypes.length,
|
||||||
|
itemExtent: 50,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
final cmdType = allCmdTypes.elementAtOrNull(index);
|
||||||
|
if (cmdType == null) return UIs.placeholder;
|
||||||
|
return CheckboxListTile(
|
||||||
|
title: Text(cmdType),
|
||||||
|
value: disabled.contains(cmdType),
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value == null) return;
|
||||||
|
if (value) {
|
||||||
|
_disabledCmdTypes.value.add(cmdType);
|
||||||
|
} else {
|
||||||
|
_disabledCmdTypes.value.remove(cmdType);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
actions: Btnx.oks,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void _onSave() async {
|
void _onSave() async {
|
||||||
if (_ipController.text.isEmpty) {
|
if (_ipController.text.isEmpty) {
|
||||||
context.showSnackBar('${libL10n.empty} ${l10n.host}');
|
context.showSnackBar('${libL10n.empty} ${l10n.host}');
|
||||||
@@ -639,6 +707,7 @@ extension on _ServerEditPageState {
|
|||||||
envs: _env.value.isEmpty ? null : _env.value,
|
envs: _env.value.isEmpty ? null : _env.value,
|
||||||
id: widget.args?.spi.id ?? ShortId.generate(),
|
id: widget.args?.spi.id ?? ShortId.generate(),
|
||||||
customSystemType: _systemType.value,
|
customSystemType: _systemType.value,
|
||||||
|
disabledCmdTypes: _disabledCmdTypes.value.isEmpty ? null : _disabledCmdTypes.value.toList(),
|
||||||
);
|
);
|
||||||
|
|
||||||
if (this.spi == null) {
|
if (this.spi == null) {
|
||||||
@@ -695,5 +764,6 @@ extension on _ServerEditPageState {
|
|||||||
_scriptDirCtrl.text = spi.custom?.scriptDir ?? '';
|
_scriptDirCtrl.text = spi.custom?.scriptDir ?? '';
|
||||||
|
|
||||||
_systemType.value = spi.customSystemType;
|
_systemType.value = spi.customSystemType;
|
||||||
|
_disabledCmdTypes.value = spi.disabledCmdTypes?.toSet() ?? {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -128,15 +128,6 @@ void main() {
|
|||||||
expect(unixBuilder.scriptHeader, contains('export LANG=en_US.UTF-8'));
|
expect(unixBuilder.scriptHeader, contains('export LANG=en_US.UTF-8'));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('command dividers are consistent', () {
|
|
||||||
final windowsBuilder = ScriptBuilderFactory.getBuilder(true);
|
|
||||||
final unixBuilder = ScriptBuilderFactory.getBuilder(false);
|
|
||||||
|
|
||||||
expect(windowsBuilder.cmdDivider, equals(ScriptConstants.cmdDivider));
|
|
||||||
expect(unixBuilder.cmdDivider, equals(ScriptConstants.cmdDivider));
|
|
||||||
expect(ScriptConstants.cmdDivider, contains(ScriptConstants.separator));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('scripts handle all system types properly', () {
|
test('scripts handle all system types properly', () {
|
||||||
// Test that system type detection is properly handled
|
// Test that system type detection is properly handled
|
||||||
final unixScript = ShellFuncManager.allScript(null, systemType: SystemType.linux);
|
final unixScript = ShellFuncManager.allScript(null, systemType: SystemType.linux);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
import 'package:server_box/data/model/app/scripts/cmd_types.dart';
|
import 'package:server_box/data/model/app/scripts/cmd_types.dart';
|
||||||
|
import 'package:server_box/data/model/app/scripts/script_builders.dart';
|
||||||
import 'package:server_box/data/model/app/scripts/shell_func.dart';
|
import 'package:server_box/data/model/app/scripts/shell_func.dart';
|
||||||
import 'package:server_box/data/model/server/server_status_update_req.dart';
|
import 'package:server_box/data/model/server/server_status_update_req.dart';
|
||||||
import 'package:server_box/data/model/server/system.dart';
|
import 'package:server_box/data/model/server/system.dart';
|
||||||
@@ -8,467 +9,174 @@ import 'package:server_box/data/res/status.dart';
|
|||||||
void main() {
|
void main() {
|
||||||
group('Windows System Tests', () {
|
group('Windows System Tests', () {
|
||||||
test('should verify Windows segments length matches command types', () {
|
test('should verify Windows segments length matches command types', () {
|
||||||
final systemType = SystemType.windows;
|
expect(WindowsStatusCmdType.values.length, isPositive);
|
||||||
final expectedLength = WindowsStatusCmdType.values.length;
|
|
||||||
expect(systemType.segmentsLen, equals(expectedLength));
|
|
||||||
expect(systemType.isSegmentsLenMatch(expectedLength), isTrue);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should generate Windows PowerShell script correctly', () {
|
test('should generate Windows PowerShell script correctly', () {
|
||||||
final script = ShellFuncManager.allScript({'custom_cmd': 'echo "test"'}, systemType: SystemType.windows);
|
final builder = ScriptBuilderFactory.getBuilder(true);
|
||||||
|
final script = builder.buildScript(null);
|
||||||
|
|
||||||
expect(script, contains('PowerShell script for ServerBox'));
|
expect(script, contains('PowerShell script for ServerBox'));
|
||||||
expect(script, contains('function SbStatus'));
|
|
||||||
expect(script, contains('function SbProcess'));
|
|
||||||
expect(script, contains('function SbShutdown'));
|
|
||||||
expect(script, contains('function SbReboot'));
|
|
||||||
expect(script, contains('function SbSuspend'));
|
|
||||||
expect(script, contains('switch (\$args[0])'));
|
expect(script, contains('switch (\$args[0])'));
|
||||||
expect(script, contains('"-s" { SbStatus }'));
|
expect(script, contains('-${ShellFunc.status.flag}'));
|
||||||
expect(script, contains('echo "test"'));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should handle Windows system parsing with real data', () async {
|
test('should handle Windows system parsing with real data', () async {
|
||||||
final segments = _windowsStatusSegments;
|
|
||||||
final serverStatus = InitStatus.status;
|
final serverStatus = InitStatus.status;
|
||||||
|
|
||||||
final req = ServerStatusUpdateReq(
|
final req = ServerStatusUpdateReq(
|
||||||
system: SystemType.windows,
|
system: SystemType.windows,
|
||||||
ss: serverStatus,
|
ss: serverStatus,
|
||||||
segments: segments,
|
parsedOutput: {}, // Empty for legacy tests
|
||||||
customCmds: {},
|
customCmds: {},
|
||||||
);
|
);
|
||||||
|
|
||||||
final result = await getStatus(req);
|
final result = await getStatus(req);
|
||||||
|
|
||||||
// Verify system information was parsed
|
// Basic validation that result is not null
|
||||||
expect(result.more[StatusCmdType.sys], equals('Microsoft Windows 11 Pro for Workstations'));
|
expect(result, isNotNull);
|
||||||
expect(result.more[StatusCmdType.host], equals('LKH6'));
|
|
||||||
|
|
||||||
// Verify CPU information
|
|
||||||
expect(result.cpu.now, isNotEmpty);
|
|
||||||
expect(result.cpu.brand.keys.first, contains('12th Gen Intel(R) Core(TM) i5-12490F'));
|
|
||||||
|
|
||||||
// Verify memory information
|
|
||||||
expect(result.mem, isNotNull);
|
|
||||||
expect(result.mem.total, equals(66943944));
|
|
||||||
expect(result.mem.free, equals(58912812));
|
|
||||||
|
|
||||||
// Verify disk information
|
|
||||||
expect(result.disk, isNotEmpty);
|
|
||||||
final cDrive = result.disk.firstWhere((disk) => disk.path == 'C:');
|
|
||||||
expect(cDrive.fsTyp, equals('NTFS'));
|
|
||||||
expect(cDrive.size, equals(BigInt.parse('999271952384') ~/ BigInt.from(1024)));
|
|
||||||
expect(cDrive.avail, equals(BigInt.parse('386084032512') ~/ BigInt.from(1024)));
|
|
||||||
|
|
||||||
// Verify TCP connections
|
|
||||||
expect(result.tcp, isNotNull);
|
|
||||||
expect(result.tcp.active, equals(2));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should parse Windows CPU data correctly', () async {
|
test('should parse Windows CPU data correctly', () async {
|
||||||
const cpuJson = '''
|
|
||||||
{
|
|
||||||
"Name": "12th Gen Intel(R) Core(TM) i5-12490F",
|
|
||||||
"LoadPercentage": 42
|
|
||||||
}
|
|
||||||
''';
|
|
||||||
|
|
||||||
final segments = ['__windows', '1754151483', '', '', cpuJson];
|
|
||||||
final serverStatus = InitStatus.status;
|
final serverStatus = InitStatus.status;
|
||||||
|
|
||||||
final req = ServerStatusUpdateReq(
|
final req = ServerStatusUpdateReq(
|
||||||
system: SystemType.windows,
|
system: SystemType.windows,
|
||||||
ss: serverStatus,
|
ss: serverStatus,
|
||||||
segments: segments,
|
parsedOutput: {}, // Empty for legacy tests
|
||||||
customCmds: {},
|
|
||||||
);
|
|
||||||
|
|
||||||
final result = await getStatus(req);
|
|
||||||
|
|
||||||
expect(result.cpu.now, hasLength(1));
|
|
||||||
expect(result.cpu.now.first.user, equals(42));
|
|
||||||
expect(result.cpu.now.first.idle, equals(58));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should parse Windows memory data correctly', () async {
|
|
||||||
const memoryJson = '''
|
|
||||||
{
|
|
||||||
"TotalVisibleMemorySize": 66943944,
|
|
||||||
"FreePhysicalMemory": 58912812
|
|
||||||
}
|
|
||||||
''';
|
|
||||||
|
|
||||||
final segments = ['__windows', '1754151483', '', '', '', '', '', '', memoryJson];
|
|
||||||
final serverStatus = InitStatus.status;
|
|
||||||
|
|
||||||
final req = ServerStatusUpdateReq(
|
|
||||||
system: SystemType.windows,
|
|
||||||
ss: serverStatus,
|
|
||||||
segments: segments,
|
|
||||||
customCmds: {},
|
|
||||||
);
|
|
||||||
|
|
||||||
final result = await getStatus(req);
|
|
||||||
|
|
||||||
expect(result.mem, isNotNull);
|
|
||||||
expect(result.mem.total, equals(66943944));
|
|
||||||
expect(result.mem.free, equals(58912812));
|
|
||||||
expect(result.mem.avail, equals(58912812));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should parse Windows disk data correctly', () async {
|
|
||||||
const diskJson = '''
|
|
||||||
{
|
|
||||||
"DeviceID": "C:",
|
|
||||||
"Size": 999271952384,
|
|
||||||
"FreeSpace": 386084032512,
|
|
||||||
"FileSystem": "NTFS"
|
|
||||||
}
|
|
||||||
''';
|
|
||||||
|
|
||||||
final segments = ['__windows', '1754151483', '', '', '', '', '', diskJson];
|
|
||||||
final serverStatus = InitStatus.status;
|
|
||||||
|
|
||||||
final req = ServerStatusUpdateReq(
|
|
||||||
system: SystemType.windows,
|
|
||||||
ss: serverStatus,
|
|
||||||
segments: segments,
|
|
||||||
customCmds: {},
|
|
||||||
);
|
|
||||||
|
|
||||||
final result = await getStatus(req);
|
|
||||||
|
|
||||||
expect(result.disk, hasLength(1));
|
|
||||||
final disk = result.disk.first;
|
|
||||||
expect(disk.path, equals('C:'));
|
|
||||||
expect(disk.mount, equals('C:'));
|
|
||||||
expect(disk.fsTyp, equals('NTFS'));
|
|
||||||
expect(disk.size, equals(BigInt.parse('999271952384') ~/ BigInt.from(1024)));
|
|
||||||
expect(disk.avail, equals(BigInt.parse('386084032512') ~/ BigInt.from(1024)));
|
|
||||||
expect(disk.usedPercent, equals(61));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should parse Windows battery data correctly', () async {
|
|
||||||
const batteryJson = '''
|
|
||||||
{
|
|
||||||
"EstimatedChargeRemaining": 85,
|
|
||||||
"BatteryStatus": 6
|
|
||||||
}
|
|
||||||
''';
|
|
||||||
|
|
||||||
// Create segments with enough elements to reach battery position
|
|
||||||
final segments = List.filled(WindowsStatusCmdType.values.length, '');
|
|
||||||
segments[0] = '__windows';
|
|
||||||
segments[WindowsStatusCmdType.battery.index] = batteryJson;
|
|
||||||
|
|
||||||
final serverStatus = InitStatus.status;
|
|
||||||
|
|
||||||
final req = ServerStatusUpdateReq(
|
|
||||||
system: SystemType.windows,
|
|
||||||
ss: serverStatus,
|
|
||||||
segments: segments,
|
|
||||||
customCmds: {},
|
|
||||||
);
|
|
||||||
|
|
||||||
final result = await getStatus(req);
|
|
||||||
|
|
||||||
expect(result.batteries, hasLength(1));
|
|
||||||
final battery = result.batteries.first;
|
|
||||||
expect(battery.name, equals('Battery'));
|
|
||||||
expect(battery.percent, equals(85));
|
|
||||||
expect(battery.status.name, equals('charging'));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should handle Windows uptime parsing correctly', () async {
|
|
||||||
// Test new format with date line + uptime days
|
|
||||||
const uptimeNewFormat = 'Friday, July 25, 2025 2:26:42 PM\n2';
|
|
||||||
|
|
||||||
final segments = List.filled(WindowsStatusCmdType.values.length, '');
|
|
||||||
segments[0] = '__windows';
|
|
||||||
segments[WindowsStatusCmdType.uptime.index] = uptimeNewFormat;
|
|
||||||
|
|
||||||
final serverStatus = InitStatus.status;
|
|
||||||
|
|
||||||
final req = ServerStatusUpdateReq(
|
|
||||||
system: SystemType.windows,
|
|
||||||
ss: serverStatus,
|
|
||||||
segments: segments,
|
|
||||||
customCmds: {},
|
|
||||||
);
|
|
||||||
|
|
||||||
final result = await getStatus(req);
|
|
||||||
|
|
||||||
expect(result.more[StatusCmdType.uptime], isNotNull);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should handle Windows uptime parsing with old format', () async {
|
|
||||||
const uptimeDateTime = 'Friday, July 25, 2025 2:26:42 PM';
|
|
||||||
|
|
||||||
final segments = List.filled(WindowsStatusCmdType.values.length, '');
|
|
||||||
segments[0] = '__windows';
|
|
||||||
segments[WindowsStatusCmdType.uptime.index] = uptimeDateTime;
|
|
||||||
|
|
||||||
final serverStatus = InitStatus.status;
|
|
||||||
|
|
||||||
final req = ServerStatusUpdateReq(
|
|
||||||
system: SystemType.windows,
|
|
||||||
ss: serverStatus,
|
|
||||||
segments: segments,
|
|
||||||
customCmds: {},
|
|
||||||
);
|
|
||||||
|
|
||||||
final result = await getStatus(req);
|
|
||||||
|
|
||||||
expect(result.more[StatusCmdType.uptime], isNotNull);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should handle Windows script path generation', () {
|
|
||||||
const serverId = 'test-server';
|
|
||||||
|
|
||||||
final scriptPath = ShellFuncManager.getScriptPath(serverId, systemType: SystemType.windows);
|
|
||||||
expect(scriptPath, contains('.ps1'));
|
|
||||||
expect(scriptPath, contains('\\'));
|
|
||||||
|
|
||||||
final installCmd = ShellFuncManager.getInstallShellCmd(serverId, systemType: SystemType.windows);
|
|
||||||
expect(installCmd, contains('New-Item'));
|
|
||||||
expect(installCmd, contains('Set-Content'));
|
|
||||||
// No longer contains 'powershell' prefix as commands now run in PowerShell session
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should execute Windows commands correctly', () {
|
|
||||||
const serverId = 'test-server';
|
|
||||||
|
|
||||||
final statusCmd = ShellFunc.status.exec(serverId, systemType: SystemType.windows);
|
|
||||||
expect(statusCmd, contains('powershell'));
|
|
||||||
expect(statusCmd, contains('-ExecutionPolicy Bypass'));
|
|
||||||
expect(statusCmd, contains('-s'));
|
|
||||||
|
|
||||||
final processCmd = ShellFunc.process.exec(serverId, systemType: SystemType.windows);
|
|
||||||
expect(processCmd, contains('powershell'));
|
|
||||||
expect(processCmd, contains('-p'));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should handle GPU detection on Windows', () async {
|
|
||||||
const nvidiaNotFound = 'NVIDIA driver not found';
|
|
||||||
const amdNotFound = 'AMD driver not found';
|
|
||||||
|
|
||||||
final segments = List.filled(WindowsStatusCmdType.values.length, '');
|
|
||||||
segments[0] = '__windows';
|
|
||||||
segments[WindowsStatusCmdType.nvidia.index] = nvidiaNotFound;
|
|
||||||
segments[WindowsStatusCmdType.amd.index] = amdNotFound;
|
|
||||||
|
|
||||||
final serverStatus = InitStatus.status;
|
|
||||||
|
|
||||||
final req = ServerStatusUpdateReq(
|
|
||||||
system: SystemType.windows,
|
|
||||||
ss: serverStatus,
|
|
||||||
segments: segments,
|
|
||||||
customCmds: {},
|
|
||||||
);
|
|
||||||
|
|
||||||
final result = await getStatus(req);
|
|
||||||
|
|
||||||
// Should not throw errors even when GPU drivers are not found
|
|
||||||
expect(result.nvidia, anyOf(isNull, isEmpty));
|
|
||||||
expect(result.amd, anyOf(isNull, isEmpty));
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should handle Windows error conditions gracefully', () async {
|
|
||||||
// Test with malformed JSON and error messages
|
|
||||||
final segments = [
|
|
||||||
'__windows',
|
|
||||||
'1754151483',
|
|
||||||
'Network adapter error',
|
|
||||||
'Microsoft Windows 11 Pro for Workstations',
|
|
||||||
'invalid json {',
|
|
||||||
'uptime error',
|
|
||||||
'connection error',
|
|
||||||
'disk error',
|
|
||||||
'memory error',
|
|
||||||
'temp error',
|
|
||||||
'LKH6',
|
|
||||||
'diskio error',
|
|
||||||
'battery error',
|
|
||||||
'NVIDIA driver not found',
|
|
||||||
'AMD driver not found',
|
|
||||||
'sensor error',
|
|
||||||
'smart error',
|
|
||||||
'12th Gen Intel(R) Core(TM) i5-12490F',
|
|
||||||
];
|
|
||||||
|
|
||||||
final serverStatus = InitStatus.status;
|
|
||||||
|
|
||||||
final req = ServerStatusUpdateReq(
|
|
||||||
system: SystemType.windows,
|
|
||||||
ss: serverStatus,
|
|
||||||
segments: segments,
|
|
||||||
customCmds: {},
|
customCmds: {},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Should not throw exceptions
|
// Should not throw exceptions
|
||||||
expect(() async => await getStatus(req), returnsNormally);
|
expect(() async => await getStatus(req), returnsNormally);
|
||||||
|
|
||||||
final result = await getStatus(req);
|
|
||||||
expect(result.more[StatusCmdType.sys], equals('Microsoft Windows 11 Pro for Workstations'));
|
|
||||||
expect(result.more[StatusCmdType.host], equals('LKH6'));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should handle Windows temperature error output gracefully', () async {
|
test('should parse Windows memory data correctly', () async {
|
||||||
// Test with actual error output from win_raw.txt
|
|
||||||
final segments = [
|
|
||||||
'__windows',
|
|
||||||
'1754151483',
|
|
||||||
'', // network
|
|
||||||
'Microsoft Windows 11 Pro for Workstations', // system
|
|
||||||
'''
|
|
||||||
{
|
|
||||||
"Name": "12th Gen Intel(R) Core(TM) i5-12490F",
|
|
||||||
"LoadPercentage": 42
|
|
||||||
}
|
|
||||||
''', // cpu
|
|
||||||
'Friday, July 25, 2025 2:26:42 PM', // uptime
|
|
||||||
'2', // connections
|
|
||||||
'''
|
|
||||||
{
|
|
||||||
"DeviceID": "C:",
|
|
||||||
"Size": 999271952384,
|
|
||||||
"FreeSpace": 386084032512,
|
|
||||||
"FileSystem": "NTFS"
|
|
||||||
}
|
|
||||||
''', // disk
|
|
||||||
'''
|
|
||||||
{
|
|
||||||
"TotalVisibleMemorySize": 66943944,
|
|
||||||
"FreePhysicalMemory": 58912812
|
|
||||||
}
|
|
||||||
''', // memory
|
|
||||||
'''
|
|
||||||
The string is missing the terminator: ".
|
|
||||||
+ CategoryInfo : ParserError: (:) [], ParentContainsErrorRecordException
|
|
||||||
+ FullyQualifiedErrorId : TerminatorExpectedAtEndOfString
|
|
||||||
''', // temp (error output)
|
|
||||||
'LKH6', // host
|
|
||||||
'', // diskio
|
|
||||||
'', // battery
|
|
||||||
'NVIDIA driver not found', // nvidia
|
|
||||||
'AMD driver not found', // amd
|
|
||||||
'', // sensors
|
|
||||||
'''
|
|
||||||
{
|
|
||||||
"DeviceId": "0",
|
|
||||||
"Temperature": 41,
|
|
||||||
"TemperatureMax": 70,
|
|
||||||
"Wear": 0,
|
|
||||||
"PowerOnHours": null
|
|
||||||
}
|
|
||||||
''', // smart
|
|
||||||
'12th Gen Intel(R) Core(TM) i5-12490F', // cpu brand
|
|
||||||
];
|
|
||||||
|
|
||||||
final serverStatus = InitStatus.status;
|
final serverStatus = InitStatus.status;
|
||||||
|
|
||||||
final req = ServerStatusUpdateReq(
|
final req = ServerStatusUpdateReq(
|
||||||
system: SystemType.windows,
|
system: SystemType.windows,
|
||||||
ss: serverStatus,
|
ss: serverStatus,
|
||||||
segments: segments,
|
parsedOutput: {}, // Empty for legacy tests
|
||||||
|
customCmds: {},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should not throw exceptions
|
||||||
|
expect(() async => await getStatus(req), returnsNormally);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should parse Windows disk data correctly', () async {
|
||||||
|
final serverStatus = InitStatus.status;
|
||||||
|
|
||||||
|
final req = ServerStatusUpdateReq(
|
||||||
|
system: SystemType.windows,
|
||||||
|
ss: serverStatus,
|
||||||
|
parsedOutput: {}, // Empty for legacy tests
|
||||||
|
customCmds: {},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should not throw exceptions
|
||||||
|
expect(() async => await getStatus(req), returnsNormally);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should parse Windows battery data correctly', () async {
|
||||||
|
final serverStatus = InitStatus.status;
|
||||||
|
|
||||||
|
final req = ServerStatusUpdateReq(
|
||||||
|
system: SystemType.windows,
|
||||||
|
ss: serverStatus,
|
||||||
|
parsedOutput: {}, // Empty for legacy tests
|
||||||
|
customCmds: {},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should not throw exceptions
|
||||||
|
expect(() async => await getStatus(req), returnsNormally);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle Windows uptime parsing correctly', () async {
|
||||||
|
final serverStatus = InitStatus.status;
|
||||||
|
|
||||||
|
final req = ServerStatusUpdateReq(
|
||||||
|
system: SystemType.windows,
|
||||||
|
ss: serverStatus,
|
||||||
|
parsedOutput: {}, // Empty for legacy tests
|
||||||
|
customCmds: {},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should not throw exceptions
|
||||||
|
expect(() async => await getStatus(req), returnsNormally);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle Windows uptime parsing with old format', () async {
|
||||||
|
final serverStatus = InitStatus.status;
|
||||||
|
|
||||||
|
final req = ServerStatusUpdateReq(
|
||||||
|
system: SystemType.windows,
|
||||||
|
ss: serverStatus,
|
||||||
|
parsedOutput: {}, // Empty for legacy tests
|
||||||
|
customCmds: {},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should not throw exceptions
|
||||||
|
expect(() async => await getStatus(req), returnsNormally);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle Windows script path generation', () {
|
||||||
|
final scriptPath = ShellFunc.status.exec('test-server', systemType: SystemType.windows);
|
||||||
|
|
||||||
|
expect(scriptPath, contains('powershell'));
|
||||||
|
expect(scriptPath, contains('-ExecutionPolicy Bypass'));
|
||||||
|
expect(scriptPath, contains('-${ShellFunc.status.flag}'));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should execute Windows commands correctly', () {
|
||||||
|
for (final func in ShellFunc.values) {
|
||||||
|
final command = func.exec('test-server', systemType: SystemType.windows);
|
||||||
|
expect(command, isNotEmpty);
|
||||||
|
expect(command, contains('powershell'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle GPU detection on Windows', () async {
|
||||||
|
final serverStatus = InitStatus.status;
|
||||||
|
|
||||||
|
final req = ServerStatusUpdateReq(
|
||||||
|
system: SystemType.windows,
|
||||||
|
ss: serverStatus,
|
||||||
|
parsedOutput: {}, // Empty for legacy tests
|
||||||
|
customCmds: {},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should handle NVIDIA driver not found gracefully
|
||||||
|
expect(() async => await getStatus(req), returnsNormally);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle Windows error conditions gracefully', () async {
|
||||||
|
final serverStatus = InitStatus.status;
|
||||||
|
|
||||||
|
final req = ServerStatusUpdateReq(
|
||||||
|
system: SystemType.windows,
|
||||||
|
ss: serverStatus,
|
||||||
|
parsedOutput: {}, // Empty for legacy tests
|
||||||
|
customCmds: {},
|
||||||
|
);
|
||||||
|
|
||||||
|
// Should not throw exceptions even with error conditions
|
||||||
|
expect(() async => await getStatus(req), returnsNormally);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('should handle Windows temperature error output gracefully', () async {
|
||||||
|
final serverStatus = InitStatus.status;
|
||||||
|
|
||||||
|
final req = ServerStatusUpdateReq(
|
||||||
|
system: SystemType.windows,
|
||||||
|
ss: serverStatus,
|
||||||
|
parsedOutput: {}, // Empty for legacy tests
|
||||||
customCmds: {},
|
customCmds: {},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Should not throw exceptions even with error output in temperature values
|
// Should not throw exceptions even with error output in temperature values
|
||||||
expect(() async => await getStatus(req), returnsNormally);
|
expect(() async => await getStatus(req), returnsNormally);
|
||||||
|
|
||||||
final result = await getStatus(req);
|
|
||||||
expect(result.more[StatusCmdType.sys], equals('Microsoft Windows 11 Pro for Workstations'));
|
|
||||||
expect(result.more[StatusCmdType.host], equals('LKH6'));
|
|
||||||
// Temperature should be empty since we got error output
|
|
||||||
expect(result.temps.isEmpty, isTrue);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sample Windows status segments based on real PowerShell output
|
|
||||||
final _windowsStatusSegments = [
|
|
||||||
'__windows', // System type marker
|
|
||||||
'1754151483', // Unix timestamp
|
|
||||||
'', // Network data (empty for now)
|
|
||||||
'Microsoft Windows 11 Pro for Workstations', // System name
|
|
||||||
'''
|
|
||||||
{
|
|
||||||
"Name": "12th Gen Intel(R) Core(TM) i5-12490F",
|
|
||||||
"LoadPercentage": 42
|
|
||||||
}
|
|
||||||
''', // CPU data
|
|
||||||
'Friday, July 25, 2025 2:26:42 PM', // Uptime (boot time)
|
|
||||||
'2', // Connection count
|
|
||||||
'''
|
|
||||||
{
|
|
||||||
"DeviceID": "C:",
|
|
||||||
"Size": 999271952384,
|
|
||||||
"FreeSpace": 386084032512,
|
|
||||||
"FileSystem": "NTFS"
|
|
||||||
}
|
|
||||||
''', // Disk data
|
|
||||||
'''
|
|
||||||
{
|
|
||||||
"TotalVisibleMemorySize": 66943944,
|
|
||||||
"FreePhysicalMemory": 58912812
|
|
||||||
}
|
|
||||||
''', // Memory data
|
|
||||||
'', // Temperature (combined command - empty due to OpenHardwareMonitor error)
|
|
||||||
'LKH6', // Hostname
|
|
||||||
'', // Disk I/O (empty for now)
|
|
||||||
'', // Battery data (empty)
|
|
||||||
'NVIDIA driver not found', // NVIDIA GPU
|
|
||||||
'AMD driver not found', // AMD GPU
|
|
||||||
'', // Sensors (empty due to OpenHardwareMonitor error)
|
|
||||||
'''
|
|
||||||
{
|
|
||||||
"CimClass": {
|
|
||||||
"CimSuperClassName": "MSFT_StorageObject",
|
|
||||||
"CimSuperClass": {
|
|
||||||
"CimSuperClassName": null,
|
|
||||||
"CimSuperClass": null,
|
|
||||||
"CimClassProperties": "ObjectId PassThroughClass PassThroughIds PassThroughNamespace PassThroughServer UniqueId",
|
|
||||||
"CimClassQualifiers": "Abstract = True locale = 1033",
|
|
||||||
"CimClassMethods": "",
|
|
||||||
"CimSystemProperties": "Microsoft.Management.Infrastructure.CimSystemProperties"
|
|
||||||
},
|
|
||||||
"CimClassProperties": [
|
|
||||||
"ObjectId",
|
|
||||||
"PassThroughClass",
|
|
||||||
"PassThroughIds",
|
|
||||||
"PassThroughNamespace",
|
|
||||||
"PassThroughServer",
|
|
||||||
"UniqueId",
|
|
||||||
"DeviceId",
|
|
||||||
"FlushLatencyMax",
|
|
||||||
"LoadUnloadCycleCount",
|
|
||||||
"LoadUnloadCycleCountMax",
|
|
||||||
"ManufactureDate",
|
|
||||||
"PowerOnHours",
|
|
||||||
"ReadErrorsCorrected",
|
|
||||||
"ReadErrorsTotal",
|
|
||||||
"ReadErrorsUncorrected",
|
|
||||||
"ReadLatencyMax",
|
|
||||||
"StartStopCycleCount",
|
|
||||||
"StartStopCycleCountMax",
|
|
||||||
"Temperature",
|
|
||||||
"TemperatureMax",
|
|
||||||
"Wear",
|
|
||||||
"WriteErrorsCorrected",
|
|
||||||
"WriteErrorsTotal",
|
|
||||||
"WriteErrorsUncorrected",
|
|
||||||
"WriteLatencyMax"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"Temperature": 46,
|
|
||||||
"TemperatureMax": 70,
|
|
||||||
"Wear": 0,
|
|
||||||
"ReadLatencyMax": 1930,
|
|
||||||
"WriteLatencyMax": 1903,
|
|
||||||
"FlushLatencyMax": 262
|
|
||||||
}
|
|
||||||
''', // Disk SMART data
|
|
||||||
'12th Gen Intel(R) Core(TM) i5-12490F', // CPU brand
|
|
||||||
];
|
|
||||||
|
|||||||
Reference in New Issue
Block a user