import { Injectable, NotFoundException, ConflictException, Logger } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { PluginRegistry } from '../../entities/plugin-registry.entity'; import { InstallPluginDto } from './dto/install-plugin.dto'; import { UpdatePluginConfigDto } from './dto/update-plugin-config.dto'; import { NatsService } from '../../services/nats.service'; @Injectable() export class PluginsService { private readonly logger = new Logger(PluginsService.name); constructor( @InjectRepository(PluginRegistry) private readonly pluginRegistryRepo: Repository, private readonly natsService: NatsService, ) {} async getPlugins(licenseId: string): Promise { return this.pluginRegistryRepo.find({ where: { license_id: licenseId }, order: { installed_at: 'DESC' }, }); } async installPlugin(licenseId: string, dto: InstallPluginDto): Promise { // Check if plugin already exists const existing = await this.pluginRegistryRepo.findOne({ where: { license_id: licenseId, plugin_name: dto.plugin_name, }, }); if (existing) { throw new ConflictException(`Plugin ${dto.plugin_name} is already installed`); } const plugin = this.pluginRegistryRepo.create({ license_id: licenseId, plugin_name: dto.plugin_name, umod_slug: dto.umod_slug, source: dto.source || 'manual', is_installed: true, is_loaded: false, }); const saved = await this.pluginRegistryRepo.save(plugin); try { await this.natsService.publish(`corrosion.${licenseId}.cmd.server`, { action: 'plugin_install', plugin_name: dto.plugin_name, umod_slug: dto.umod_slug, timestamp: new Date().toISOString(), }); this.logger.log(`Plugin install dispatched for ${dto.plugin_name} on license ${licenseId}`); } catch (err) { this.logger.error(`Failed to dispatch plugin install for ${dto.plugin_name} on license ${licenseId}: ${(err as Error).message}`); } return saved; } async uninstallPlugin(licenseId: string, pluginId: string): Promise { const plugin = await this.pluginRegistryRepo.findOne({ where: { id: pluginId, license_id: licenseId }, }); if (!plugin) { throw new NotFoundException(`Plugin ${pluginId} not found`); } await this.pluginRegistryRepo.delete({ id: pluginId, license_id: licenseId }); await this.natsService.publish(`corrosion.${licenseId}.cmd.plugin`, { action: 'unload', plugin_name: plugin.plugin_name, timestamp: new Date().toISOString(), }); this.logger.log(`Plugin uninstall dispatched for ${plugin.plugin_name} on license ${licenseId}`); } async reloadPlugin( licenseId: string, pluginId: string, ): Promise<{ reloaded: boolean; plugin_name: string }> { const plugin = await this.pluginRegistryRepo.findOne({ where: { id: pluginId, license_id: licenseId }, }); if (!plugin) { throw new NotFoundException(`Plugin ${pluginId} not found`); } await this.natsService.publish(`corrosion.${licenseId}.cmd.plugin`, { action: 'reload', plugin_name: plugin.plugin_name, timestamp: new Date().toISOString(), }); this.logger.log(`Plugin reload dispatched for ${plugin.plugin_name} on license ${licenseId}`); return { reloaded: true, plugin_name: plugin.plugin_name }; } async updateConfig( licenseId: string, pluginId: string, dto: UpdatePluginConfigDto, ): Promise { const plugin = await this.pluginRegistryRepo.findOne({ where: { id: pluginId, license_id: licenseId }, }); if (!plugin) { throw new NotFoundException(`Plugin ${pluginId} not found`); } Object.assign(plugin, dto); plugin.updated_at = new Date(); return this.pluginRegistryRepo.save(plugin); } async searchUmod(query: string): Promise<{ results: any[]; message: string }> { // uMod API integration pending return { results: [], message: 'uMod search integration not yet configured', }; } }