Add system control feature for remote shutdown/restart with timer support and UI integration

This commit is contained in:
Din
2025-08-08 11:52:12 +08:00
parent 5ece1fbe27
commit 1129f9a2b1
4 changed files with 302 additions and 1 deletions
+80
View File
@@ -154,5 +154,85 @@ namespace ResourceMonitorService.Controllers
return StatusCode(500, "Internal server error");
}
}
[HttpPost("system-control")]
public ActionResult SystemControl([FromBody] SystemControlRequest request)
{
try
{
if (request == null)
{
return BadRequest("Invalid request");
}
// Validate action
if (request.Action != "shutdown" && request.Action != "restart")
{
return BadRequest("Invalid action. Use 'shutdown' or 'restart'");
}
// Validate timer
if (request.Timer < 0 || request.Timer > 86400)
{
return BadRequest("Timer must be between 0 and 86400 seconds (24 hours)");
}
// Build the shutdown command
var arguments = request.Action == "shutdown" ? "/s" : "/r";
if (request.Force)
{
arguments += " /f";
}
if (request.Timer > 0)
{
arguments += $" /t {request.Timer}";
}
// Execute the shutdown command
var processInfo = new ProcessStartInfo
{
FileName = "shutdown",
Arguments = arguments,
UseShellExecute = false,
CreateNoWindow = true
};
var process = Process.Start(processInfo);
string message;
if (request.Timer > 0)
{
var minutes = request.Timer / 60;
var seconds = request.Timer % 60;
var timeString = minutes > 0
? $"{minutes} minute{(minutes != 1 ? "s" : "")}" + (seconds > 0 ? $" and {seconds} second{(seconds != 1 ? "s" : "")}" : "")
: $"{seconds} second{(seconds != 1 ? "s" : "")}";
message = $"System {request.Action} scheduled in {timeString}";
}
else
{
message = $"System {request.Action} initiated immediately";
}
_logger.LogWarning($"System {request.Action} command executed by user. Timer: {request.Timer}s, Force: {request.Force}");
return Ok(new { message });
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error executing system {request?.Action} command");
return StatusCode(500, "Internal server error");
}
}
}
public class SystemControlRequest
{
public string Action { get; set; } = string.Empty;
public int Timer { get; set; } = 0;
public bool Force { get; set; } = true;
}
}
+24 -1
View File
@@ -47,7 +47,30 @@ Access the interactive web dashboard at `http://localhost:5000` featuring:
- **Process Management**: View top processes with CPU/memory percentages, terminate processes via API
- **Smart Alerting**: Duration-based alerting to prevent false positives
- **Telegram Bot Integration**: Real-time alerts via Telegram bot with customizable notifications
- **System Control**: Remote shutdown/restart capabilities
- **System Control**: Remote shutdown/restart capabilities with timer support (hidden feature)
### 🔐 Hidden System Control Feature
For security reasons, the system control feature is hidden by default. To access it:
**Activation Methods:**
1. **Triple-click** on the "Resource Monitor" title in the navigation bar
2. **Keyboard shortcut**: `Ctrl + Shift + P`
**Features:**
- **Shutdown**: Graceful or forced system shutdown
- **Restart**: System restart with optional force
- **Timer Support**: Delay execution from 0 to 86400 seconds (24 hours)
- **Safety Confirmations**: Multiple confirmation dialogs before execution
**Usage:**
1. Activate the system control button using one of the methods above
2. Click the red "System" button that appears in the navigation
3. Set optional timer (leave empty for immediate action)
4. Choose shutdown or restart
5. Confirm the action
⚠️ **Warning**: Use this feature with extreme caution as it will shut down or restart the entire system.
- **Health Monitoring**: Comprehensive health checks and uptime tracking
- **Real-time Metrics**: CPU usage calculation and memory percentage tracking for processes
+58
View File
@@ -33,6 +33,10 @@
<button id="refreshData" class="bg-green-500 hover:bg-green-700 px-4 py-2 rounded-lg transition-colors">
<i class="fas fa-sync-alt mr-2"></i>Refresh
</button>
<!-- Hidden System Control Button (requires triple-click to show) -->
<button id="systemControl" class="hidden bg-red-600 hover:bg-red-800 px-4 py-2 rounded-lg transition-colors" title="System Control">
<i class="fas fa-power-off mr-2"></i>System
</button>
</div>
</div>
</div>
@@ -191,6 +195,60 @@
</div>
</div>
<!-- System Control Modal -->
<div id="systemControlModal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
<div class="bg-white rounded-lg p-6 max-w-md w-full mx-4">
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-bold text-gray-800">
<i class="fas fa-power-off mr-2 text-red-600"></i>System Control
</h3>
<button id="closeSystemModal" class="text-gray-400 hover:text-gray-600">
<i class="fas fa-times text-xl"></i>
</button>
</div>
<div class="space-y-4">
<!-- Timer Input -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">
Timer (seconds) - Leave empty for immediate action
</label>
<input type="number" id="systemTimer"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder="0" min="0" max="86400">
<p class="text-xs text-gray-500 mt-1">Maximum: 24 hours (86400 seconds)</p>
</div>
<!-- Action Buttons -->
<div class="grid grid-cols-2 gap-3">
<button id="shutdownBtn" class="bg-red-600 hover:bg-red-700 text-white px-4 py-3 rounded-lg transition-colors flex items-center justify-center">
<i class="fas fa-power-off mr-2"></i>Shutdown
</button>
<button id="restartBtn" class="bg-orange-600 hover:bg-orange-700 text-white px-4 py-3 rounded-lg transition-colors flex items-center justify-center">
<i class="fas fa-redo mr-2"></i>Restart
</button>
</div>
<!-- Force Shutdown Option -->
<div class="flex items-center">
<input type="checkbox" id="forceShutdown" class="mr-2" checked>
<label for="forceShutdown" class="text-sm text-gray-700">Force shutdown (close applications without saving)</label>
</div>
<!-- Warning -->
<div class="bg-yellow-50 border border-yellow-200 rounded-lg p-3">
<div class="flex">
<i class="fas fa-exclamation-triangle text-yellow-600 mr-2 mt-0.5"></i>
<div class="text-sm text-yellow-800">
<strong>Warning:</strong> This will shut down or restart the entire system.
Make sure to save any unsaved work first.
</div>
</div>
</div>
</div>
</div>
</div>
<script src="js/dashboard.js"></script>
</body>
</html>
+140
View File
@@ -40,6 +40,54 @@ class ResourceDashboard {
document.getElementById('refreshData').addEventListener('click', () => {
this.refreshData();
});
// System control event listeners
document.getElementById('systemControl').addEventListener('click', () => {
this.showSystemControlModal();
});
document.getElementById('closeSystemModal').addEventListener('click', () => {
this.hideSystemControlModal();
});
document.getElementById('shutdownBtn').addEventListener('click', () => {
this.executeSystemCommand('shutdown');
});
document.getElementById('restartBtn').addEventListener('click', () => {
this.executeSystemCommand('restart');
});
// Hidden system control activation - triple click on title
let clickCount = 0;
let clickTimer = null;
document.querySelector('h1').addEventListener('click', () => {
clickCount++;
if (clickCount === 1) {
clickTimer = setTimeout(() => {
clickCount = 0;
}, 2000); // Reset after 2 seconds
} else if (clickCount === 3) {
clearTimeout(clickTimer);
clickCount = 0;
this.toggleSystemControlButton();
}
});
// Close modal when clicking outside
document.getElementById('systemControlModal').addEventListener('click', (e) => {
if (e.target.id === 'systemControlModal') {
this.hideSystemControlModal();
}
});
// Keyboard shortcut: Ctrl+Shift+P to toggle system control
document.addEventListener('keydown', (e) => {
if (e.ctrlKey && e.shiftKey && e.key === 'P') {
e.preventDefault();
this.toggleSystemControlButton();
}
});
}
toggleDetailsSection() {
@@ -675,6 +723,98 @@ class ResourceDashboard {
}, 3000);
}
// System Control Methods
toggleSystemControlButton() {
const systemButton = document.getElementById('systemControl');
if (systemButton.classList.contains('hidden')) {
systemButton.classList.remove('hidden');
this.showNotification('System control unlocked! Use with caution.', 'info');
} else {
systemButton.classList.add('hidden');
this.hideSystemControlModal();
}
}
showSystemControlModal() {
document.getElementById('systemControlModal').classList.remove('hidden');
document.getElementById('systemTimer').value = '';
document.getElementById('forceShutdown').checked = true;
}
hideSystemControlModal() {
document.getElementById('systemControlModal').classList.add('hidden');
}
async executeSystemCommand(action) {
const timer = document.getElementById('systemTimer').value;
const force = document.getElementById('forceShutdown').checked;
// Validate timer input
const timerSeconds = parseInt(timer) || 0;
if (timerSeconds < 0 || timerSeconds > 86400) {
this.showNotification('Timer must be between 0 and 86400 seconds (24 hours)', 'error');
return;
}
// Build confirmation message
let confirmMessage = `Are you sure you want to ${action} the system`;
if (timerSeconds > 0) {
const minutes = Math.floor(timerSeconds / 60);
const seconds = timerSeconds % 60;
if (minutes > 0) {
confirmMessage += ` in ${minutes} minute${minutes !== 1 ? 's' : ''}`;
if (seconds > 0) {
confirmMessage += ` and ${seconds} second${seconds !== 1 ? 's' : ''}`;
}
} else {
confirmMessage += ` in ${seconds} second${seconds !== 1 ? 's' : ''}`;
}
} else {
confirmMessage += ' immediately';
}
confirmMessage += '?';
if (!confirm(confirmMessage)) {
return;
}
try {
// Prepare the command data
const commandData = {
action: action,
timer: timerSeconds,
force: force
};
const response = await fetch('/api/resource/system-control', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(commandData)
});
if (response.ok) {
const result = await response.json();
this.showNotification(result.message, 'success');
this.hideSystemControlModal();
// If immediate action, warn user
if (timerSeconds === 0) {
setTimeout(() => {
this.showNotification(`System ${action} initiated! Connection will be lost.`, 'info');
}, 1000);
}
} else {
const error = await response.text();
this.showNotification(`Failed to ${action} system: ${error}`, 'error');
}
} catch (error) {
console.error(`Error executing ${action}:`, error);
this.showNotification(`Error executing ${action} command`, 'error');
}
}
hideLoading() {
document.getElementById('loadingOverlay').style.display = 'none';
}