oggui/ogWebconsole/src/app/components/plugins/plugin-settings/plugin-settings.component.ts

182 lines
6.0 KiB
TypeScript

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<string, any> = {};
editingScripts: Record<string, boolean> = {};
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<any>(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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}
private highlightLine(rawLine: string): SafeHtml {
const trimmed = rawLine.trim();
if (trimmed.length === 0) {
return this.sanitizer.bypassSecurityTrustHtml('&nbsp;');
}
// Comentario completo
if (trimmed.startsWith('#')) {
const escaped = this.escapeHtml(rawLine);
return this.sanitizer.bypassSecurityTrustHtml(`<span class="syntax-comment">${escaped}</span>`);
}
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<span class="syntax-flag">$2</span>');
// 3. Comandos ejecutables
escaped = escaped.replace(/\b(uwfmgr\.exe|shutdown|powershell\.exe|cmd\.exe)\b/gi, '<span class="syntax-cmd">$1</span>');
// 4. Subcomandos y palabras clave
escaped = escaped.replace(/\b(overlay|filter|volume|file|enable|disable|protect|set-type|set-size|add-exclusion)\b/gi, '<span class="syntax-keyword">$1</span>');
// 5. Valores y tipos
escaped = escaped.replace(/\b(RAM|DISK|true|false)\b/gi, '<span class="syntax-val">$1</span>');
// 6. Números
escaped = escaped.replace(/\b(\d+)\b/g, '<span class="syntax-num">$1</span>');
// Restaurar cadenas
escaped = escaped.replace(/___STR_(\d+)___/g, (_, index) => {
const original = strings[Number(index)];
return `<span class="syntax-string">${original}</span>`;
});
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');
}
}