feat: Complete NestJS backend scaffold — 22 modules, 39 entities, WebSocket gateway
All checks were successful
Test Asgard Runner / test (push) Successful in 3s

Full backend rewrite from Rust/Axum to NestJS/TypeScript.
- 22 feature modules (auth, servers, wipes, maps, plugins, players, console,
  chat, team, notifications, settings, schedules, analytics, alerts, status,
  store, webstore, admin, setup, migration, users, licenses)
- 39 TypeORM entities matching PostgreSQL schema (12 migrations)
- Common infrastructure: JWT/RBAC guards, decorators, exception filter
- NATS service with pub/sub/request-reply
- Socket.IO WebSocket gateway with NATS bridge
- Docker: NestJS Dockerfile + updated docker-compose.yml
- Zero compile errors (npx tsc --noEmit clean)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Vantz Stockwell
2026-02-15 21:29:25 -05:00
parent 0f8d0dd14f
commit d20493d533
141 changed files with 13552 additions and 4 deletions

View File

@@ -0,0 +1,65 @@
import { Controller, Get, Post, Put, Delete, Body, Param, Query, UseGuards } from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation, ApiQuery } from '@nestjs/swagger';
import { PluginsService } from './plugins.service';
import { InstallPluginDto } from './dto/install-plugin.dto';
import { UpdatePluginConfigDto } from './dto/update-plugin-config.dto';
import { CurrentTenant } from '../../common/decorators/current-tenant.decorator';
import { RequirePermission } from '../../common/decorators/require-permission.decorator';
import { JwtAuthGuard } from '../../common/guards/jwt-auth.guard';
import { PermissionsGuard } from '../../common/guards/permissions.guard';
@ApiTags('plugins')
@ApiBearerAuth()
@Controller('plugins')
@UseGuards(JwtAuthGuard, PermissionsGuard)
export class PluginsController {
constructor(private readonly pluginsService: PluginsService) {}
@Get()
@RequirePermission('plugin.view')
@ApiOperation({ summary: 'Get all installed plugins for tenant' })
getPlugins(@CurrentTenant() licenseId: string) {
return this.pluginsService.getPlugins(licenseId);
}
@Post('install')
@RequirePermission('plugin.manage')
@ApiOperation({ summary: 'Install new plugin' })
installPlugin(@CurrentTenant() licenseId: string, @Body() dto: InstallPluginDto) {
return this.pluginsService.installPlugin(licenseId, dto);
}
@Delete(':id')
@RequirePermission('plugin.manage')
@ApiOperation({ summary: 'Uninstall plugin' })
async uninstallPlugin(@CurrentTenant() licenseId: string, @Param('id') pluginId: string) {
await this.pluginsService.uninstallPlugin(licenseId, pluginId);
return { deleted: true };
}
@Post(':id/reload')
@RequirePermission('plugin.manage')
@ApiOperation({ summary: 'Reload plugin on game server' })
reloadPlugin(@CurrentTenant() licenseId: string, @Param('id') pluginId: string) {
return this.pluginsService.reloadPlugin(licenseId, pluginId);
}
@Put(':id/config')
@RequirePermission('plugin.manage')
@ApiOperation({ summary: 'Update plugin configuration' })
updateConfig(
@CurrentTenant() licenseId: string,
@Param('id') pluginId: string,
@Body() dto: UpdatePluginConfigDto,
) {
return this.pluginsService.updateConfig(licenseId, pluginId, dto);
}
@Get('search')
@RequirePermission('plugin.view')
@ApiOperation({ summary: 'Search uMod plugin directory' })
@ApiQuery({ name: 'q', required: true, example: 'kits' })
searchUmod(@Query('q') query: string) {
return this.pluginsService.searchUmod(query);
}
}