diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a5a451..8833c20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ ### Fixed - **Recarga de página en Plugins (`plugins.component`, `plugin-settings.component`)**: Corregido el problema de sincronización reactiva al refrescar directamente (F5) la vista de `/plugins`, añadiendo un estado de carga visual y suscripción a `plugins$` para evitar mostrar prematuramente la pantalla de "No hay plugins instalados". - **Rutas excluidas vacías en ogfreeze (`plugin-settings.component`)**: Corregida la regeneración de scripts cuando el campo de rutas excluidas se deja intencionadamente en blanco; ahora omite de forma limpia el bloque de exclusiones en lugar de forzar los valores predeterminados. +- **Ejecución y Validación de Comandos de Plugins (`execute-command.component`)**: Implementado el envío HTTP real de las órdenes de plugins (`POST /api/plugins/...`), validación de estado del SO activo (aviso si están ejecutando Linux u og-live cuando se requiere Windows) y diálogo de confirmación de encolado (`QueueConfirmationModalComponent`) cuando los equipos seleccionados se encuentran apagados o desconectados. ## [1.15.0] - 2026-10-20 diff --git a/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts index 768155b..e528fbe 100644 --- a/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts +++ b/ogWebconsole/src/app/components/commands/main-commands/execute-command/execute-command.component.ts @@ -12,7 +12,7 @@ import { AuthService } from '@services/auth.service'; import {SoftwareProfilePartitionComponent} from "./software-profile-partition/software-profile-partition.component"; import { QueueConfirmationModalComponent } from '../../../../shared/queue-confirmation-modal/queue-confirmation-modal.component'; import { ExecuteTaskNowComponent } from './execute-task-now/execute-task-now.component'; -import { PluginService } from '@services/plugin.service'; +import { PluginService, PluginCommand } from '@services/plugin.service'; @Component({ selector: 'app-execute-command', @@ -219,15 +219,7 @@ export class ExecuteCommandComponent implements OnInit { // Punto de anclaje para ejecución de comandos aportados por plugins const pluginCmd = this.pluginService.findCommand(action); if (pluginCmd) { - if (action === 'ogfreeze-freeze') { - this.pluginService.toggleFreezeStatus(this.clientData, true); - this.toastService.success('Comando de congelación (UWF) enviado correctamente a los equipos seleccionados', 'Plugin Freeze'); - } else if (action === 'ogfreeze-unfreeze') { - this.pluginService.toggleFreezeStatus(this.clientData, false); - this.toastService.info('Comando de descongelación (UWF) enviado correctamente a los equipos seleccionados', 'Plugin Freeze'); - } else { - this.toastService.success(`Acción ${pluginCmd.label} ejecutada en los equipos seleccionados`, 'Plugin'); - } + this.executePluginCommand(pluginCmd); return; } @@ -539,4 +531,83 @@ export class ExecuteCommandComponent implements OnInit { } }); } + + private executePluginCommand(pluginCmd: PluginCommand): void { + if (!this.clientData || this.clientData.length === 0) { + this.toastService.warning('No hay ningún equipo seleccionado para ejecutar esta acción.', pluginCmd.label); + return; + } + + const requiredOs = (pluginCmd.os || []).map(os => os.toLowerCase()); + + // 1. Verificar si hay equipos encendidos en un sistema operativo incompatible (ej. Linux u og-live cuando se requiere Windows) + const incompatibleRunningClients = this.clientData.filter(c => { + const status = (c.status || '').toLowerCase(); + const isOnline = status !== 'off' && status !== 'disconnected' && status !== ''; + if (!isOnline || requiredOs.length === 0) return false; + return !requiredOs.some(req => status.includes(req)); + }); + + if (incompatibleRunningClients.length > 0) { + const names = incompatibleRunningClients.map(c => c.name || c.ip).join(', '); + this.toastService.warning( + `Los siguientes equipos no están ejecutando ${requiredOs.join('/')} actualmente: ${names}. La acción requiere que el equipo esté iniciado en el sistema compatible.`, + pluginCmd.label + ); + return; + } + + // 2. Verificar si hay equipos apagados o desconectados + const offlineClients = this.clientData.filter(c => { + const status = (c.status || '').toLowerCase(); + return !status || status === 'off' || status === 'disconnected'; + }); + + if (offlineClients.length > 0) { + const dialogRef = this.dialog.open(QueueConfirmationModalComponent, { + width: '400px', + data: { clients: this.clientData } + }); + + dialogRef.afterClosed().subscribe((queue: boolean | undefined) => { + if (queue === undefined) { + // El usuario canceló o cerró el modal + return; + } + + this.sendPluginCommandRequest(pluginCmd, queue); + }); + return; + } + + // 3. Todos los equipos están en línea en el sistema operativo correcto + this.sendPluginCommandRequest(pluginCmd, false); + } + + private sendPluginCommandRequest(pluginCmd: PluginCommand, queue: boolean): void { + const defaultSlug = pluginCmd.slug.includes('unfreeze') ? 'unfreeze' : 'freeze'; + const endpoint = pluginCmd.endpoint || `/api/plugins/ogfreeze/${defaultSlug}`; + const cleanEndpoint = endpoint.replace(/^\/api/, ''); + const url = `${this.baseUrl}${cleanEndpoint}`; + + const clientIds = this.clientData.map(c => c.id || c['@id']); + const isFreeze = pluginCmd.slug.includes('freeze') && !pluginCmd.slug.includes('unfreeze'); + + this.http.post(url, { + clients: clientIds, + queue: queue + }).subscribe({ + next: (response) => { + this.pluginService.toggleFreezeStatus(this.clientData, isFreeze); + const msg = response?.message || (isFreeze + ? 'Comando de congelación (UWF) procesado correctamente' + : 'Comando de descongelación (UWF) procesado correctamente'); + this.toastService.success(msg, pluginCmd.label); + }, + error: (error) => { + const errorMsg = error?.error?.message || error?.error?.['hydra:description'] || 'Error al comunicar con el servidor'; + this.toastService.error(errorMsg, pluginCmd.label); + } + }); + } }