feat: ability to disable monitoring cmds (#840)

This commit is contained in:
lollipopkit🏳️‍⚧️
2025-08-09 12:37:30 +08:00
committed by GitHub
parent 9c9648656d
commit 95f8e571c1
16 changed files with 451 additions and 609 deletions

View File

@@ -1,9 +1,16 @@
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';
/// Base class for all command type enums
abstract class CommandType {
abstract class CommandType implements Enum {
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
@@ -83,6 +90,12 @@ enum StatusCmdType implements CommandType {
final String cmd;
const StatusCmdType(this.cmd);
@override
String get separator => ScriptConstants.getCmdSeparator(name);
@override
String get divider => ScriptConstants.getCmdDivider(name);
}
/// BSD/macOS status commands
@@ -102,6 +115,12 @@ enum BSDStatusCmdType implements CommandType {
final String cmd;
const BSDStatusCmdType(this.cmd);
@override
String get separator => ScriptConstants.getCmdSeparator(name);
@override
String get divider => ScriptConstants.getCmdDivider(name);
}
/// Windows PowerShell status commands
@@ -225,6 +244,12 @@ enum WindowsStatusCmdType implements CommandType {
final String cmd;
const WindowsStatusCmdType(this.cmd);
@override
String get separator => ScriptConstants.getCmdSeparator(name);
@override
String get divider => ScriptConstants.getCmdDivider(name);
}
/// Extensions for StatusCmdType
@@ -240,10 +265,10 @@ extension StatusCmdTypeX on StatusCmdType {
};
}
/// Generic extension for Enum types
extension EnumX on Enum {
/// Find out the required segment from [segments]
String find(List<String> segments) {
return segments[index];
/// Extension for CommandType to find content in parsed map
extension CommandTypeX on CommandType {
/// Find the command output from the parsed script output map
String findInMap(Map<String, String> parsedOutput) {
return parsedOutput[name] ?? '';
}
}

View File

@@ -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';
/// Abstract base class for platform-specific script builders
abstract class ScriptBuilder {
sealed class ScriptBuilder {
const ScriptBuilder();
/// 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
String get scriptFileName;
@@ -23,9 +23,6 @@ abstract class ScriptBuilder {
/// Get the script header for this platform
String get scriptHeader;
/// Get the command divider for this platform
String get cmdDivider => ScriptConstants.cmdDivider;
}
/// Windows PowerShell script builder
@@ -53,13 +50,19 @@ class WindowsScriptBuilder extends ScriptBuilder {
@override
String getCustomCmdsString(ShellFunc func, Map<String, String>? customCmds) {
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 '';
}
@override
String buildScript(Map<String, String>? customCmds) {
String buildScript(Map<String, String>? customCmds, [List<String>? disabledCmdTypes]) {
final sb = StringBuffer();
sb.write(scriptHeader);
@@ -69,7 +72,7 @@ class WindowsScriptBuilder extends ScriptBuilder {
sb.write('''
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
String _getWindowsCommand(ShellFunc func) => switch (func) {
ShellFunc.status => WindowsStatusCmdType.values.map((e) => e.cmd).join(cmdDivider),
String _getWindowsCommand(ShellFunc func, [List<String>? disabledCmdTypes]) => switch (func) {
ShellFunc.status => _getWindowsStatusCommand(disabledCmdTypes: disabledCmdTypes ?? []),
ShellFunc.process => 'Get-Process | Select-Object ProcessName, Id, CPU, WorkingSet | ConvertTo-Json',
ShellFunc.shutdown => 'Stop-Computer -Force',
ShellFunc.reboot => 'Restart-Computer -Force',
ShellFunc.suspend =>
'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
@@ -129,13 +138,19 @@ chmod 755 $scriptPath
@override
String getCustomCmdsString(ShellFunc func, Map<String, String>? customCmds) {
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 '';
}
@override
String buildScript(Map<String, String>? customCmds) {
String buildScript(Map<String, String>? customCmds, [List<String>? disabledCmdTypes]) {
final sb = StringBuffer();
sb.write(scriptHeader);
// Write each function
@@ -143,7 +158,7 @@ chmod 755 $scriptPath
final customCmdsStr = getCustomCmdsString(func, customCmds);
sb.write('''
${func.name}() {
${_getUnixCommand(func).split('\n').map((e) => '\t$e').join('\n')}
${_getUnixCommand(func, disabledCmdTypes).split('\n').map((e) => '\t$e').join('\n')}
$customCmdsStr
}
@@ -168,27 +183,24 @@ esac''');
}
/// Get Unix-specific command for a shell function
String _getUnixCommand(ShellFunc func) {
switch (func) {
case ShellFunc.status:
return _getUnixStatusCommand();
case ShellFunc.process:
return _getUnixProcessCommand();
case ShellFunc.shutdown:
return _getUnixShutdownCommand();
case ShellFunc.reboot:
return _getUnixRebootCommand();
case ShellFunc.suspend:
return _getUnixSuspendCommand();
}
String _getUnixCommand(ShellFunc func, [List<String>? disabledCmdTypes]) {
return switch (func) {
ShellFunc.status => _getUnixStatusCommand(disabledCmdTypes: disabledCmdTypes ?? []),
ShellFunc.process => _getUnixProcessCommand(),
ShellFunc.shutdown => _getUnixShutdownCommand(),
ShellFunc.reboot => _getUnixRebootCommand(),
ShellFunc.suspend => _getUnixSuspendCommand(),
};
}
/// Get Unix status command with OS detection
String _getUnixStatusCommand() {
// Generate command lists for better readability
final linuxCommands = StatusCmdType.values.map((e) => e.cmd).join(cmdDivider);
String _getUnixStatusCommand({required List<String> disabledCmdTypes}) {
// Generate command lists with command-specific separators, filtering disabled commands
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 '''
if [ "\$macSign" = "" ] && [ "\$bsdSign" = "" ]; then

View File

@@ -17,8 +17,58 @@ class ScriptConstants {
// Command separators and dividers
static const String separator = 'SrvBoxSep';
/// The suffix `\t` is for formatting
static const String cmdDivider = '\necho $separator\n\t';
/// Custom command separator
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
static const String unixPathSeparator = '/';

View File

@@ -93,10 +93,10 @@ class ShellFuncManager {
}
/// 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 builder = ScriptBuilderFactory.getBuilder(isWindows);
return builder.buildScript(customCmds);
return builder.buildScript(customCmds, disabledCmdTypes);
}
}

View File

@@ -48,6 +48,9 @@ abstract class Spi with _$Spi {
/// Custom system type (unix or windows). If set, skip auto-detection.
@JsonKey(includeIfNull: false) SystemType? customSystemType,
/// Disabled command types for this server
@JsonKey(includeIfNull: false) List<String>? disabledCmdTypes,
}) = _Spi;
factory Spi.fromJson(Map<String, dynamic> json) => _$SpiFromJson(json);

View File

@@ -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
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.
@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
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@@ -33,12 +34,12 @@ $SpiCopyWith<Spi> get copyWith => _$SpiCopyWithImpl<Spi>(this as Spi, _$identity
@override
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)
@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;
@useResult
$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
/// 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(
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
@@ -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 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 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)
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);
@override final String name;
@@ -133,6 +135,17 @@ class _Spi extends Spi {
@override@JsonKey(fromJson: Spi.parseId) final String id;
/// Custom system type (unix or windows). If set, skip auto-detection.
@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
/// with the given fields replaced by the non-null parameter values.
@@ -147,12 +160,12 @@ Map<String, dynamic> toJson() {
@override
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)
@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;
@override @useResult
$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
/// 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(
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
@@ -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 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 SystemType?,
as SystemType?,disabledCmdTypes: freezed == disabledCmdTypes ? _self._disabledCmdTypes : disabledCmdTypes // ignore: cast_nullable_to_non_nullable
as List<String>?,
));
}

View File

@@ -31,6 +31,9 @@ _Spi _$SpiFromJson(Map<String, dynamic> json) => _Spi(
_$SystemTypeEnumMap,
json['customSystemType'],
),
disabledCmdTypes: (json['disabledCmdTypes'] as List<dynamic>?)
?.map((e) => e as String)
.toList(),
);
Map<String, dynamic> _$SpiToJson(_Spi instance) => <String, dynamic>{
@@ -50,6 +53,7 @@ Map<String, dynamic> _$SpiToJson(_Spi instance) => <String, dynamic>{
'id': instance.id,
if (_$SystemTypeEnumMap[instance.customSystemType] case final value?)
'customSystemType': value,
if (instance.disabledCmdTypes case final value?) 'disabledCmdTypes': value,
};
const _$SystemTypeEnumMap = {

View File

@@ -20,14 +20,14 @@ import 'package:server_box/data/model/server/windows_parser.dart';
class ServerStatusUpdateReq {
final ServerStatus ss;
final List<String> segments;
final Map<String, String> parsedOutput;
final SystemType system;
final Map<String, String> customCmds;
const ServerStatusUpdateReq({
required this.system,
required this.ss,
required this.segments,
required this.parsedOutput,
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,
// the following operations can still be executed.
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;
try {
final net = NetSpeed.parse(StatusCmdType.net.find(segments), time);
final net = NetSpeed.parse(StatusCmdType.net.findInMap(parsedOutput), time);
req.ss.netSpeed.update(net);
} catch (e, s) {
Loggers.app.warning(e, s);
}
try {
final sys = _parseSysVer(StatusCmdType.sys.find(segments));
final sys = _parseSysVer(StatusCmdType.sys.findInMap(parsedOutput));
if (sys != null) {
req.ss.more[StatusCmdType.sys] = sys;
}
@@ -65,7 +65,7 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
}
try {
final host = _parseHostName(StatusCmdType.host.find(segments));
final host = _parseHostName(StatusCmdType.host.findInMap(parsedOutput));
if (host != null) {
req.ss.more[StatusCmdType.host] = host;
}
@@ -74,9 +74,9 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
}
try {
final cpus = SingleCpuCore.parse(StatusCmdType.cpu.find(segments));
final cpus = SingleCpuCore.parse(StatusCmdType.cpu.findInMap(parsedOutput));
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.addAll(brand);
} catch (e, s) {
@@ -85,15 +85,15 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
try {
req.ss.temps.parse(
StatusCmdType.tempType.find(segments),
StatusCmdType.tempVal.find(segments),
StatusCmdType.tempType.findInMap(parsedOutput),
StatusCmdType.tempVal.findInMap(parsedOutput),
);
} catch (e, s) {
Loggers.app.warning(e, s);
}
try {
final tcp = Conn.parse(StatusCmdType.conn.find(segments));
final tcp = Conn.parse(StatusCmdType.conn.findInMap(parsedOutput));
if (tcp != null) {
req.ss.tcp = tcp;
}
@@ -102,20 +102,20 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
}
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);
} catch (e, s) {
Loggers.app.warning(e, s);
}
try {
req.ss.mem = Memory.parse(StatusCmdType.mem.find(segments));
req.ss.mem = Memory.parse(StatusCmdType.mem.findInMap(parsedOutput));
} catch (e, s) {
Loggers.app.warning(e, s);
}
try {
final uptime = _parseUpTime(StatusCmdType.uptime.find(segments));
final uptime = _parseUpTime(StatusCmdType.uptime.findInMap(parsedOutput));
if (uptime != null) {
req.ss.more[StatusCmdType.uptime] = uptime;
}
@@ -124,39 +124,39 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
}
try {
req.ss.swap = Swap.parse(StatusCmdType.mem.find(segments));
req.ss.swap = Swap.parse(StatusCmdType.mem.findInMap(parsedOutput));
} catch (e, s) {
Loggers.app.warning(e, s);
}
try {
final diskio = DiskIO.parse(StatusCmdType.diskio.find(segments), time);
final diskio = DiskIO.parse(StatusCmdType.diskio.findInMap(parsedOutput), time);
req.ss.diskIO.update(diskio);
} catch (e, s) {
Loggers.app.warning(e, s);
}
try {
final smarts = DiskSmart.parse(StatusCmdType.diskSmart.find(segments));
final smarts = DiskSmart.parse(StatusCmdType.diskSmart.findInMap(parsedOutput));
req.ss.diskSmart = smarts;
} catch (e, s) {
Loggers.app.warning(e, s);
}
try {
req.ss.nvidia = NvidiaSmi.fromXml(StatusCmdType.nvidia.find(segments));
req.ss.nvidia = NvidiaSmi.fromXml(StatusCmdType.nvidia.findInMap(parsedOutput));
} catch (e, s) {
Loggers.app.warning(e, s);
}
try {
req.ss.amd = AmdSmi.fromJson(StatusCmdType.amd.find(segments));
req.ss.amd = AmdSmi.fromJson(StatusCmdType.amd.findInMap(parsedOutput));
} catch (e, s) {
Loggers.app.warning(e, s);
}
try {
final battery = StatusCmdType.battery.find(segments);
final battery = StatusCmdType.battery.findInMap(parsedOutput);
/// Only collect li-poly batteries
final batteries = Batteries.parse(battery, true);
@@ -169,7 +169,7 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
}
try {
final sensors = SensorItem.parse(StatusCmdType.sensors.find(segments));
final sensors = SensorItem.parse(StatusCmdType.sensors.findInMap(parsedOutput));
if (sensors.isNotEmpty) {
req.ss.sensors.clear();
req.ss.sensors.addAll(sensors);
@@ -179,9 +179,9 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
}
try {
for (int idx = 0; idx < req.customCmds.length; idx++) {
final key = req.customCmds.keys.elementAt(idx);
final value = req.segments[idx + req.system.segmentsLen];
for (final entry in req.customCmds.entries) {
final key = entry.key;
final value = req.parsedOutput[key] ?? '';
req.ss.customCmds[key] = value;
}
} catch (e, s) {
@@ -193,36 +193,36 @@ Future<ServerStatus> _getLinuxStatus(ServerStatusUpdateReq req) async {
// Same as above, wrap with try-catch
Future<ServerStatus> _getBsdStatus(ServerStatusUpdateReq req) async {
final segments = req.segments;
final parsedOutput = req.parsedOutput;
try {
final time = int.parse(BSDStatusCmdType.time.find(segments));
final net = NetSpeed.parseBsd(BSDStatusCmdType.net.find(segments), time);
final time = int.parse(BSDStatusCmdType.time.findInMap(parsedOutput));
final net = NetSpeed.parseBsd(BSDStatusCmdType.net.findInMap(parsedOutput), time);
req.ss.netSpeed.update(net);
} catch (e, s) {
Loggers.app.warning(e, s);
}
try {
req.ss.more[StatusCmdType.sys] = BSDStatusCmdType.sys.find(segments);
req.ss.more[StatusCmdType.sys] = BSDStatusCmdType.sys.findInMap(parsedOutput);
} catch (e, s) {
Loggers.app.warning(e, s);
}
try {
req.ss.cpu = parseBsdCpu(BSDStatusCmdType.cpu.find(segments));
req.ss.cpu = parseBsdCpu(BSDStatusCmdType.cpu.findInMap(parsedOutput));
} catch (e, s) {
Loggers.app.warning(e, s);
}
try {
req.ss.mem = parseBsdMemory(BSDStatusCmdType.mem.find(segments));
req.ss.mem = parseBsdMemory(BSDStatusCmdType.mem.findInMap(parsedOutput));
} catch (e, s) {
Loggers.app.warning(e, s);
}
try {
final uptime = _parseUpTime(BSDStatusCmdType.uptime.find(segments));
final uptime = _parseUpTime(BSDStatusCmdType.uptime.findInMap(parsedOutput));
if (uptime != null) {
req.ss.more[StatusCmdType.uptime] = uptime;
}
@@ -231,7 +231,7 @@ Future<ServerStatus> _getBsdStatus(ServerStatusUpdateReq req) async {
}
try {
req.ss.disk = Disk.parse(BSDStatusCmdType.disk.find(segments));
req.ss.disk = Disk.parse(BSDStatusCmdType.disk.findInMap(parsedOutput));
} catch (e, s) {
Loggers.app.warning(e, s);
}
@@ -302,32 +302,32 @@ String? _parseHostName(String raw) {
// Windows status parsing implementation
Future<ServerStatus> _getWindowsStatus(ServerStatusUpdateReq req) async {
final segments = req.segments;
final time = int.tryParse(WindowsStatusCmdType.time.find(segments)) ??
final parsedOutput = req.parsedOutput;
final time = int.tryParse(WindowsStatusCmdType.time.findInMap(parsedOutput)) ??
DateTime.now().millisecondsSinceEpoch ~/ 1000;
// Parse all different resource types using helper methods
_parseWindowsNetworkData(req, segments, time);
_parseWindowsSystemData(req, segments);
_parseWindowsHostData(req, segments);
_parseWindowsCpuData(req, segments);
_parseWindowsMemoryData(req, segments);
_parseWindowsDiskData(req, segments);
_parseWindowsUptimeData(req, segments);
_parseWindowsDiskIOData(req, segments, time);
_parseWindowsConnectionData(req, segments);
_parseWindowsBatteryData(req, segments);
_parseWindowsTemperatureData(req, segments);
_parseWindowsGpuData(req, segments);
WindowsParser.parseCustomCommands(req.ss, segments, req.customCmds, req.system.segmentsLen);
_parseWindowsNetworkData(req, parsedOutput, time);
_parseWindowsSystemData(req, parsedOutput);
_parseWindowsHostData(req, parsedOutput);
_parseWindowsCpuData(req, parsedOutput);
_parseWindowsMemoryData(req, parsedOutput);
_parseWindowsDiskData(req, parsedOutput);
_parseWindowsUptimeData(req, parsedOutput);
_parseWindowsDiskIOData(req, parsedOutput, time);
_parseWindowsConnectionData(req, parsedOutput);
_parseWindowsBatteryData(req, parsedOutput);
_parseWindowsTemperatureData(req, parsedOutput);
_parseWindowsGpuData(req, parsedOutput);
WindowsParser.parseCustomCommands(req.ss, req.parsedOutput, req.customCmds);
return req.ss;
}
/// Parse Windows network data
void _parseWindowsNetworkData(ServerStatusUpdateReq req, List<String> segments, int time) {
void _parseWindowsNetworkData(ServerStatusUpdateReq req, Map<String, String> parsedOutput, int time) {
try {
final netRaw = WindowsStatusCmdType.net.find(segments);
final netRaw = WindowsStatusCmdType.net.findInMap(parsedOutput);
if (netRaw.isNotEmpty &&
netRaw != 'null' &&
!netRaw.contains('network_error') &&
@@ -344,9 +344,9 @@ void _parseWindowsNetworkData(ServerStatusUpdateReq req, List<String> segments,
}
/// Parse Windows system information
void _parseWindowsSystemData(ServerStatusUpdateReq req, List<String> segments) {
void _parseWindowsSystemData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
try {
final sys = WindowsStatusCmdType.sys.find(segments);
final sys = WindowsStatusCmdType.sys.findInMap(parsedOutput);
if (sys.isNotEmpty) {
req.ss.more[StatusCmdType.sys] = sys;
}
@@ -356,9 +356,9 @@ void _parseWindowsSystemData(ServerStatusUpdateReq req, List<String> segments) {
}
/// Parse Windows host information
void _parseWindowsHostData(ServerStatusUpdateReq req, List<String> segments) {
void _parseWindowsHostData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
try {
final host = _parseHostName(WindowsStatusCmdType.host.find(segments));
final host = _parseHostName(WindowsStatusCmdType.host.findInMap(parsedOutput));
if (host != null) {
req.ss.more[StatusCmdType.host] = host;
}
@@ -368,10 +368,10 @@ void _parseWindowsHostData(ServerStatusUpdateReq req, List<String> segments) {
}
/// Parse Windows CPU data and brand information
void _parseWindowsCpuData(ServerStatusUpdateReq req, List<String> segments) {
void _parseWindowsCpuData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
try {
// Windows CPU parsing - JSON format from PowerShell
final cpuRaw = WindowsStatusCmdType.cpu.find(segments);
final cpuRaw = WindowsStatusCmdType.cpu.findInMap(parsedOutput);
if (cpuRaw.isNotEmpty &&
cpuRaw != 'null' &&
!cpuRaw.contains('error') &&
@@ -383,7 +383,7 @@ void _parseWindowsCpuData(ServerStatusUpdateReq req, List<String> segments) {
}
// Windows CPU brand parsing
final brandRaw = WindowsStatusCmdType.cpuBrand.find(segments);
final brandRaw = WindowsStatusCmdType.cpuBrand.findInMap(parsedOutput);
if (brandRaw.isNotEmpty && brandRaw != 'null') {
req.ss.cpu.brand.clear();
req.ss.cpu.brand[brandRaw.trim()] = 1;
@@ -394,9 +394,9 @@ void _parseWindowsCpuData(ServerStatusUpdateReq req, List<String> segments) {
}
/// Parse Windows memory data
void _parseWindowsMemoryData(ServerStatusUpdateReq req, List<String> segments) {
void _parseWindowsMemoryData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
try {
final memRaw = WindowsStatusCmdType.mem.find(segments);
final memRaw = WindowsStatusCmdType.mem.findInMap(parsedOutput);
if (memRaw.isNotEmpty &&
memRaw != 'null' &&
!memRaw.contains('error') &&
@@ -412,9 +412,9 @@ void _parseWindowsMemoryData(ServerStatusUpdateReq req, List<String> segments) {
}
/// Parse Windows disk data
void _parseWindowsDiskData(ServerStatusUpdateReq req, List<String> segments) {
void _parseWindowsDiskData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
try {
final diskRaw = WindowsStatusCmdType.disk.find(segments);
final diskRaw = WindowsStatusCmdType.disk.findInMap(parsedOutput);
if (diskRaw.isNotEmpty && diskRaw != 'null') {
final disks = WindowsParser.parseDisks(diskRaw);
req.ss.disk = disks;
@@ -426,9 +426,9 @@ void _parseWindowsDiskData(ServerStatusUpdateReq req, List<String> segments) {
}
/// Parse Windows uptime data
void _parseWindowsUptimeData(ServerStatusUpdateReq req, List<String> segments) {
void _parseWindowsUptimeData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
try {
final uptime = WindowsParser.parseUpTime(WindowsStatusCmdType.uptime.find(segments));
final uptime = WindowsParser.parseUpTime(WindowsStatusCmdType.uptime.findInMap(parsedOutput));
if (uptime != null) {
req.ss.more[StatusCmdType.uptime] = uptime;
}
@@ -438,9 +438,9 @@ void _parseWindowsUptimeData(ServerStatusUpdateReq req, List<String> segments) {
}
/// 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 {
final diskIOraw = WindowsStatusCmdType.diskio.find(segments);
final diskIOraw = WindowsStatusCmdType.diskio.findInMap(parsedOutput);
if (diskIOraw.isNotEmpty && diskIOraw != 'null') {
final diskio = _parseWindowsDiskIO(diskIOraw, time);
req.ss.diskIO.update(diskio);
@@ -451,9 +451,9 @@ void _parseWindowsDiskIOData(ServerStatusUpdateReq req, List<String> segments, i
}
/// Parse Windows connection data
void _parseWindowsConnectionData(ServerStatusUpdateReq req, List<String> segments) {
void _parseWindowsConnectionData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
try {
final connStr = WindowsStatusCmdType.conn.find(segments);
final connStr = WindowsStatusCmdType.conn.findInMap(parsedOutput);
final connCount = int.tryParse(connStr.trim());
if (connCount != null) {
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
void _parseWindowsBatteryData(ServerStatusUpdateReq req, List<String> segments) {
void _parseWindowsBatteryData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
try {
final batteryRaw = WindowsStatusCmdType.battery.find(segments);
final batteryRaw = WindowsStatusCmdType.battery.findInMap(parsedOutput);
if (batteryRaw.isNotEmpty && batteryRaw != 'null') {
final batteries = _parseWindowsBatteries(batteryRaw);
req.ss.batteries.clear();
@@ -480,9 +480,9 @@ void _parseWindowsBatteryData(ServerStatusUpdateReq req, List<String> segments)
}
/// Parse Windows temperature data
void _parseWindowsTemperatureData(ServerStatusUpdateReq req, List<String> segments) {
void _parseWindowsTemperatureData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
try {
final tempRaw = WindowsStatusCmdType.temp.find(segments);
final tempRaw = WindowsStatusCmdType.temp.findInMap(parsedOutput);
if (tempRaw.isNotEmpty && tempRaw != 'null') {
_parseWindowsTemperatures(req.ss.temps, tempRaw);
}
@@ -492,15 +492,15 @@ void _parseWindowsTemperatureData(ServerStatusUpdateReq req, List<String> segmen
}
/// Parse Windows GPU data (NVIDIA/AMD)
void _parseWindowsGpuData(ServerStatusUpdateReq req, List<String> segments) {
void _parseWindowsGpuData(ServerStatusUpdateReq req, Map<String, String> parsedOutput) {
try {
req.ss.nvidia = NvidiaSmi.fromXml(WindowsStatusCmdType.nvidia.find(segments));
req.ss.nvidia = NvidiaSmi.fromXml(WindowsStatusCmdType.nvidia.findInMap(parsedOutput));
} catch (e, s) {
Loggers.app.warning('Windows NVIDIA GPU parsing failed: $e', s);
}
try {
req.ss.amd = AmdSmi.fromJson(WindowsStatusCmdType.amd.find(segments));
req.ss.amd = AmdSmi.fromJson(WindowsStatusCmdType.amd.findInMap(parsedOutput));
} catch (e, s) {
Loggers.app.warning('Windows AMD GPU parsing failed: $e', s);
}

View File

@@ -1,5 +1,4 @@
import 'package:fl_lib/fl_lib.dart';
import 'package:server_box/data/model/app/scripts/cmd_types.dart';
enum SystemType {
linux(linuxSign),
@@ -53,16 +52,4 @@ enum SystemType {
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;
}
}
}

View File

@@ -15,27 +15,17 @@ import 'package:server_box/data/model/server/server.dart';
class WindowsParser {
const WindowsParser._();
/// Parse Windows custom commands from segments
/// Parse Windows custom commands from parsed output
static void parseCustomCommands(
ServerStatus serverStatus,
List<String> segments,
Map<String, String> parsedOutput,
Map<String, String> customCmds,
int systemSegmentsLength,
) {
try {
for (int idx = 0; idx < customCmds.length; idx++) {
final key = customCmds.keys.elementAt(idx);
// Ensure we don't go out of bounds when accessing segments
final segmentIndex = idx + systemSegmentsLength;
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)'
);
}
for (final entry in customCmds.entries) {
final key = entry.key;
final value = parsedOutput[key] ?? '';
serverStatus.customCmds[key] = value;
}
} catch (e, s) {
Loggers.app.warning('Windows custom commands parsing failed: $e', s);