import { Component, Input, OnInit, OnChanges, OnDestroy, SimpleChanges, inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { PluginManifest, PluginService, SchemaField } from '@services/plugin.service'; import { ConfigService } from '@services/config.service'; import { ToastrService } from 'ngx-toastr'; import { DomSanitizer, SafeHtml } from '@angular/platform-browser'; import { Subscription } from 'rxjs'; @Component({ selector: 'app-plugin-settings', templateUrl: './plugin-settings.component.html', styleUrls: ['./plugin-settings.component.css'] }) export class PluginSettingsComponent implements OnInit, OnChanges, OnDestroy { @Input() pluginName: string = ''; plugin?: PluginManifest; schema: SchemaField[] = []; configData: Record = {}; editingScripts: Record = {}; private pluginSub?: Subscription; private sanitizer = inject(DomSanitizer); private http = inject(HttpClient); private configService = inject(ConfigService); constructor( private pluginService: PluginService, private toast: ToastrService ) {} ngOnInit(): void { this.pluginSub = this.pluginService.plugins$.subscribe(() => { this.loadPluginData(); }); this.loadPluginData(); } ngOnDestroy(): void { this.pluginSub?.unsubscribe(); } ngOnChanges(changes: SimpleChanges): void { if (changes['pluginName']) { this.loadPluginData(); } } private loadPluginData(): void { if (!this.pluginName) return; this.plugin = this.pluginService.getPlugin(this.pluginName); if (this.plugin) { this.schema = this.plugin.schema; this.configData = this.pluginService.getPluginConfig(this.pluginName); } } toggleEnabled(checked: boolean): void { if (!this.pluginName) return; this.pluginService.togglePlugin(this.pluginName, checked).subscribe({ next: () => { const statusText = checked ? 'activado' : 'desactivado'; this.toast.success(`Plugin ${statusText} correctamente`, this.plugin?.title || 'Plugin'); }, error: () => { this.toast.error('Error al cambiar el estado del plugin', this.plugin?.title || 'Plugin'); this.loadPluginData(); } }); } runFieldAction(field: SchemaField): void { if (!field.action || !field.action.endpoint) return; const apiUrl = this.configService.apiUrl || ''; const cleanEndpoint = field.action.endpoint.replace(/^\/api/, ''); const url = `${apiUrl}${cleanEndpoint}`; this.http.post(url, { config: this.configData }).subscribe({ next: (response) => { if (response && response.data) { this.configData = { ...this.configData, ...response.data }; } const message = response?.message || `${field.action?.label} completado correctamente`; this.toast.info(message, this.plugin?.title || 'Plugin'); }, error: (err) => { const errorMsg = err?.error?.message || 'Error al ejecutar la acción del plugin'; this.toast.error(errorMsg, field.action?.label || 'Plugin'); } }); } isEditingScript(key: string): boolean { return !!this.editingScripts[key]; } toggleScriptEdit(key: string): void { this.editingScripts[key] = !this.editingScripts[key]; } copyToClipboard(text: string): void { if (!text) return; navigator.clipboard.writeText(text).then(() => { this.toast.success('Script copiado al portapapeles', 'Portapapeles'); }).catch(() => { this.toast.error('No se pudo copiar el script al portapapeles', 'Error'); }); } getScriptLines(script: string): SafeHtml[] { if (!script) return []; const lines = script.split('\n'); return lines.map(line => this.highlightLine(line)); } private escapeHtml(str: string): string { return str .replace(/&/g, '&') .replace(//g, '>'); } private highlightLine(rawLine: string): SafeHtml { const trimmed = rawLine.trim(); if (trimmed.length === 0) { return this.sanitizer.bypassSecurityTrustHtml(' '); } // Comentario completo if (trimmed.startsWith('#')) { const escaped = this.escapeHtml(rawLine); return this.sanitizer.bypassSecurityTrustHtml(`${escaped}`); } let escaped = this.escapeHtml(rawLine); // 1. Cadenas entre comillas dobles o simples const strings: string[] = []; escaped = escaped.replace(/(["'])(.*?)\1/g, (match) => { strings.push(match); return `___STR_${strings.length - 1}___`; }); // 2. Parámetros / flags (/r, /t, -c, etc.) escaped = escaped.replace(/(\s)([\/-][a-zA-Z0-9?]+)/g, '$1$2'); // 3. Comandos ejecutables escaped = escaped.replace(/\b(uwfmgr\.exe|shutdown|powershell\.exe|cmd\.exe)\b/gi, '$1'); // 4. Subcomandos y palabras clave escaped = escaped.replace(/\b(overlay|filter|volume|file|enable|disable|protect|set-type|set-size|add-exclusion)\b/gi, '$1'); // 5. Valores y tipos escaped = escaped.replace(/\b(RAM|DISK|true|false)\b/gi, '$1'); // 6. Números escaped = escaped.replace(/\b(\d+)\b/g, '$1'); // Restaurar cadenas escaped = escaped.replace(/___STR_(\d+)___/g, (_, index) => { const original = strings[Number(index)]; return `${original}`; }); return this.sanitizer.bypassSecurityTrustHtml(escaped); } save(): void { if (!this.pluginName) return; this.pluginService.savePluginConfig(this.pluginName, this.configData); this.toast.success('Configuración guardada correctamente', this.plugin?.title || 'Plugin'); } reset(): void { if (!this.pluginName) return; this.configData = this.pluginService.resetPluginConfig(this.pluginName); this.toast.info('Configuración restaurada a valores por defecto', this.plugin?.title || 'Plugin'); } }