fix: disable command menu doesnt work (#852)

This commit is contained in:
lollipopkit🏳️‍⚧️
2025-08-13 23:32:22 +08:00
committed by GitHub
parent ed8a1d18b9
commit 68734a9e52
4 changed files with 208 additions and 35 deletions

View File

@@ -1,9 +1,29 @@
import 'package:flutter/material.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/data/model/app/scripts/script_consts.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';
/// Enum representing different command types for various systems
enum CmdTypeSys {
linux('Linux'),
bsd('BSD'),
windows('Windows');
final String sign;
const CmdTypeSys(this.sign);
IconData get icon {
return switch (this) {
CmdTypeSys.linux => MingCute.linux_line,
CmdTypeSys.bsd => LineAwesome.freebsd,
CmdTypeSys.windows => MingCute.windows_line,
};
}
}
/// Base class for all command type enums /// Base class for all command type enums
abstract class CommandType implements Enum { sealed class ShellCmdType implements Enum {
String get cmd; String get cmd;
/// Get command-specific separator /// Get command-specific separator
@@ -11,10 +31,22 @@ abstract class CommandType implements Enum {
/// Get command-specific divider (separator with echo and formatting) /// Get command-specific divider (separator with echo and formatting)
String get divider; String get divider;
/// Get corresponding system type
CmdTypeSys get sysType;
static Set<ShellCmdType> get all {
return {...StatusCmdType.values, ...BSDStatusCmdType.values, ...WindowsStatusCmdType.values};
}
}
extension ShellCmdTypeX on ShellCmdType {
/// Display name of the command type
String get displayName => '${sysType.sign}.$name';
} }
/// Linux/Unix status commands /// Linux/Unix status commands
enum StatusCmdType implements CommandType { enum StatusCmdType implements ShellCmdType {
echo('echo ${SystemType.linuxSign}'), echo('echo ${SystemType.linuxSign}'),
time('date +%s'), time('date +%s'),
net('cat /proc/net/dev'), net('cat /proc/net/dev'),
@@ -96,10 +128,13 @@ enum StatusCmdType implements CommandType {
@override @override
String get divider => ScriptConstants.getCmdDivider(name); String get divider => ScriptConstants.getCmdDivider(name);
@override
CmdTypeSys get sysType => CmdTypeSys.linux;
} }
/// BSD/macOS status commands /// BSD/macOS status commands
enum BSDStatusCmdType implements CommandType { enum BSDStatusCmdType implements ShellCmdType {
echo('echo ${SystemType.bsdSign}'), echo('echo ${SystemType.bsdSign}'),
time('date +%s'), time('date +%s'),
net('netstat -ibn'), net('netstat -ibn'),
@@ -121,10 +156,13 @@ enum BSDStatusCmdType implements CommandType {
@override @override
String get divider => ScriptConstants.getCmdDivider(name); String get divider => ScriptConstants.getCmdDivider(name);
@override
CmdTypeSys get sysType => CmdTypeSys.bsd;
} }
/// Windows PowerShell status commands /// Windows PowerShell status commands
enum WindowsStatusCmdType implements CommandType { enum WindowsStatusCmdType implements ShellCmdType {
echo('echo ${SystemType.windowsSign}'), echo('echo ${SystemType.windowsSign}'),
time('[DateTimeOffset]::UtcNow.ToUnixTimeSeconds()'), time('[DateTimeOffset]::UtcNow.ToUnixTimeSeconds()'),
@@ -250,6 +288,9 @@ enum WindowsStatusCmdType implements CommandType {
@override @override
String get divider => ScriptConstants.getCmdDivider(name); String get divider => ScriptConstants.getCmdDivider(name);
@override
CmdTypeSys get sysType => CmdTypeSys.windows;
} }
/// Extensions for StatusCmdType /// Extensions for StatusCmdType
@@ -266,7 +307,7 @@ extension StatusCmdTypeX on StatusCmdType {
} }
/// Extension for CommandType to find content in parsed map /// Extension for CommandType to find content in parsed map
extension CommandTypeX on CommandType { extension CommandTypeX on ShellCmdType {
/// Find the command output from the parsed script output map /// Find the command output from the parsed script output map
String findInMap(Map<String, String> parsedOutput) { String findInMap(Map<String, String> parsedOutput) {
return parsedOutput[name] ?? ''; return parsedOutput[name] ?? '';

View File

@@ -106,7 +106,7 @@ switch (\$args[0]) {
/// Get Windows status command with command-specific separators /// Get Windows status command with command-specific separators
String _getWindowsStatusCommand({required List<String> disabledCmdTypes}) { String _getWindowsStatusCommand({required List<String> disabledCmdTypes}) {
final cmdTypes = WindowsStatusCmdType.values.where((e) => !disabledCmdTypes.contains(e.name)); final cmdTypes = WindowsStatusCmdType.values.where((e) => !disabledCmdTypes.contains(e.displayName));
return cmdTypes.map((e) => '${e.divider}${e.cmd}').join('').trimRight(); // Remove trailing divider return cmdTypes.map((e) => '${e.divider}${e.cmd}').join('').trimRight(); // Remove trailing divider
} }
} }
@@ -196,10 +196,10 @@ esac''');
/// Get Unix status command with OS detection /// Get Unix status command with OS detection
String _getUnixStatusCommand({required List<String> disabledCmdTypes}) { String _getUnixStatusCommand({required List<String> disabledCmdTypes}) {
// Generate command lists with command-specific separators, filtering disabled commands // Generate command lists with command-specific separators, filtering disabled commands
final filteredLinuxCmdTypes = StatusCmdType.values.where((e) => !disabledCmdTypes.contains(e.name)); final filteredLinuxCmdTypes = StatusCmdType.values.where((e) => !disabledCmdTypes.contains(e.displayName));
final linuxCommands = filteredLinuxCmdTypes.map((e) => '${e.divider}${e.cmd}').join('').trimRight(); final linuxCommands = filteredLinuxCmdTypes.map((e) => '${e.divider}${e.cmd}').join('').trimRight();
final filteredBsdCmdTypes = BSDStatusCmdType.values.where((e) => !disabledCmdTypes.contains(e.name)); final filteredBsdCmdTypes = BSDStatusCmdType.values.where((e) => !disabledCmdTypes.contains(e.displayName));
final bsdCommands = filteredBsdCmdTypes.map((e) => '${e.divider}${e.cmd}').join('').trimRight(); final bsdCommands = filteredBsdCmdTypes.map((e) => '${e.divider}${e.cmd}').join('').trimRight();
return ''' return '''

View File

@@ -597,21 +597,16 @@ extension on _ServerEditPageState {
} }
void _onTapDisabledCmdTypes() async { void _onTapDisabledCmdTypes() async {
final allCmdTypes = <String>{}; final allCmdTypes = ShellCmdType.all;
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 // [TimeSeq] depends on the `time` cmd type, so it should be removed from the list
allCmdTypes.remove(StatusCmdType.time.name); allCmdTypes.remove(StatusCmdType.time);
final selected = await _showCmdTypesDialog(allCmdTypes); await _showCmdTypesDialog(allCmdTypes);
if (selected == null) return;
_disabledCmdTypes.value = selected;
} }
Future<Set<String>?> _showCmdTypesDialog(Set<String> allCmdTypes) { Future<void> _showCmdTypesDialog(Set<ShellCmdType> allCmdTypes) {
return context.showRoundDialog<Set<String>>( return context.showRoundDialog(
title: '${libL10n.disabled} ${l10n.cmd}', title: '${libL10n.disabled} ${l10n.cmd}',
child: SizedBox( child: SizedBox(
width: 270, width: 270,
@@ -622,16 +617,30 @@ extension on _ServerEditPageState {
itemBuilder: (context, index) { itemBuilder: (context, index) {
final cmdType = allCmdTypes.elementAtOrNull(index); final cmdType = allCmdTypes.elementAtOrNull(index);
if (cmdType == null) return UIs.placeholder; if (cmdType == null) return UIs.placeholder;
return CheckboxListTile( final display = cmdType.displayName;
title: Text(cmdType), return ListTile(
value: disabled.contains(cmdType), leading: Icon(cmdType.sysType.icon, size: 20),
title: Text(cmdType.name, style: const TextStyle(fontSize: 16)),
trailing: Checkbox(
value: disabled.contains(display),
onChanged: (value) { onChanged: (value) {
if (value == null) return; if (value == null) return;
if (value) { if (value) {
_disabledCmdTypes.value.add(cmdType); _disabledCmdTypes.value.add(display);
} else { } else {
_disabledCmdTypes.value.remove(cmdType); _disabledCmdTypes.value.remove(display);
} }
_disabledCmdTypes.notify();
},
),
onTap: () {
final isDisabled = disabled.contains(display);
if (isDisabled) {
_disabledCmdTypes.value.remove(display);
} else {
_disabledCmdTypes.value.add(display);
}
_disabledCmdTypes.notify();
}, },
); );
}, },
@@ -764,6 +773,10 @@ 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() ?? {};
final disabledCmdTypes = spi.disabledCmdTypes?.toSet() ?? {};
final allAvailableCmdTypes = ShellCmdType.all.map((e) => e.displayName);
disabledCmdTypes.removeWhere((e) => !allAvailableCmdTypes.contains(e));
_disabledCmdTypes.value = disabledCmdTypes;
} }
} }

View File

@@ -0,0 +1,119 @@
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/shell_func.dart';
import 'package:server_box/data/model/server/system.dart';
void main() {
group('disabledCmdTypes filtering', () {
test('filters Linux status commands when disabled', () {
final disabled = <String>{
StatusCmdType.net.displayName,
StatusCmdType.sys.displayName,
}.toList();
final script = ShellFuncManager.allScript(
null,
systemType: SystemType.linux,
disabledCmdTypes: disabled,
);
// Linux-specific commands should be removed
expect(script, isNot(contains('cat /proc/net/dev'))); // net
expect(script, isNot(contains('cat /etc/*-release | grep ^PRETTY_NAME'))); // sys
// Other commands should remain
expect(script, contains('uptime'));
expect(script, contains('date +%s'));
});
test('filters BSD status commands when disabled', () {
final disabled = <String>{
BSDStatusCmdType.sys.displayName,
BSDStatusCmdType.mem.displayName,
}.toList();
final script = ShellFuncManager.allScript(
null,
systemType: SystemType.linux, // Unix builder is used for Linux/BSD
disabledCmdTypes: disabled,
);
// BSD-specific commands should be removed
expect(script, isNot(contains('uname -or'))); // sys
expect(script, isNot(contains('top -l 1 | grep PhysMem'))); // mem
// Linux equivalents should remain
expect(script, contains('cat /etc/*-release | grep ^PRETTY_NAME'));
expect(script, contains("cat /proc/meminfo | grep -E 'Mem|Swap'"));
});
test('filters Windows status commands when disabled', () {
final disabled = <String>{
WindowsStatusCmdType.net.displayName,
WindowsStatusCmdType.uptime.displayName,
WindowsStatusCmdType.temp.displayName,
}.toList();
final script = ShellFuncManager.allScript(
null,
systemType: SystemType.windows,
disabledCmdTypes: disabled,
);
// Windows-specific commands should be removed
expect(script, isNot(contains('LastBootUpTime'))); // uptime
expect(script, isNot(contains('MSAcpi_ThermalZoneTemperature'))); // temp
// Other Windows commands should remain
expect(script, contains('Get-Process'));
expect(script, contains('Get-WmiObject -Class Win32_OperatingSystem'));
});
test('ignores disabled names for other platforms', () {
final disabled = <String>{
WindowsStatusCmdType.sys.displayName,
WindowsStatusCmdType.net.displayName,
}.toList();
final script = ShellFuncManager.allScript(
null,
systemType: SystemType.linux,
disabledCmdTypes: disabled,
);
// Linux commands should not be affected by Windows-only disables
expect(script, contains('cat /etc/*-release | grep ^PRETTY_NAME'));
expect(script, contains('cat /proc/net/dev'));
});
test('disabling all status commands removes separators', () {
final allUnixDisabled = <String>{
...StatusCmdType.values.map((e) => e.displayName),
...BSDStatusCmdType.values.map((e) => e.displayName),
}.toList();
final unixScript = ShellFuncManager.allScript(
null,
systemType: SystemType.linux,
disabledCmdTypes: allUnixDisabled,
);
// No status separators for Unix script
expect(unixScript, isNot(contains('SrvBoxSep.')));
final allWinDisabled = <String>{
...WindowsStatusCmdType.values.map((e) => e.displayName),
}.toList();
final windowsScript = ShellFuncManager.allScript(
null,
systemType: SystemType.windows,
disabledCmdTypes: allWinDisabled,
);
// No status separators for Windows script
expect(windowsScript, isNot(contains('SrvBoxSep.')));
});
});
}