Files
corrosion-admin-panel/backend-nest/src/modules/plugins/plugins.service.ts
Vantz Stockwell 7bf3e5639e
All checks were successful
Test Asgard Runner / test (push) Successful in 3s
feat: Wire NATS command publishing for server commands and plugin installs
- servers.service: sendCommand() now throws InternalServerErrorException on
  NATS failure instead of silently succeeding; returns { success, message }
  instead of the legacy { output } shape; adds NestJS Logger
- plugins.service: installPlugin() dispatches plugin_install to
  corrosion.{license_id}.cmd.server after DB save; NATS failure is logged
  but non-fatal so the DB record is preserved regardless

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-21 15:59:59 -05:00

132 lines
4.1 KiB
TypeScript

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<PluginRegistry>,
private readonly natsService: NatsService,
) {}
async getPlugins(licenseId: string): Promise<PluginRegistry[]> {
return this.pluginRegistryRepo.find({
where: { license_id: licenseId },
order: { installed_at: 'DESC' },
});
}
async installPlugin(licenseId: string, dto: InstallPluginDto): Promise<PluginRegistry> {
// 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<void> {
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<PluginRegistry> {
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',
};
}
}