import { Body, Controller, Delete, ForbiddenException, Get, Param, Post, Put, Req, } from '@nestjs/common'; import type { AuthenticatedRequest } from '../auth/types/auth-user'; import { ModuleManage, UseModule } from '../module-registry/module.guard'; import { CreateProxmoxServerDto, TestProxmoxServerDto, UpdateProxmoxServerDto, } from './dto/proxmox-server.dto'; import { ProxmoxSchedulerService } from './proxmox-scheduler.service'; import { ProxmoxService } from './proxmox.service'; /** * `@UseModule('proxmox')` auf Klassenebene (D-09, Vorbild * `domaincheck.controller.ts`) — Aktivierung UND Freigabe. `tenantId` kommt * ausschliesslich aus `req.tenantId` (gesetzt vom `TenantGuard`), nie aus * Body oder Query. Lesen (`GET servers`) steht jedem Benutzer mit * Modulzugriff offen; Schreiben (`POST servers`, `PUT`/`DELETE servers/:id`, * `POST servers/test`, `POST servers/:id/poll`, `POST servers/:id/test`) * zusaetzlich `@ModuleManage('proxmox')` — Administratoren und Benutzer mit * der Freigabestufe Verwalten (261002-icv, vorher `@Roles(ADMIN, * SUPER_ADMIN)`; T-DHH-05). `servers/test` (statisch, zwei * Segmente) und `servers/:id/test` (drei Segmente) ueberschneiden sich * nicht — beide POST, aber unterschiedliche Segmentzahl, deshalb keine * Reihenfolge-Abhaengigkeit (anders als `GET :id` vs. statische Routen). */ @Controller('modules/proxmox') @UseModule('proxmox') export class ProxmoxController { constructor( private readonly proxmoxService: ProxmoxService, private readonly scheduler: ProxmoxSchedulerService, ) {} private requireTenantId(req: AuthenticatedRequest): string { const tenantId = req.tenantId; if (!tenantId) { throw new ForbiddenException('Kein Mandantenkontext'); } return tenantId; } @Get('servers') async list(@Req() req: AuthenticatedRequest) { return this.proxmoxService.listWithStatus(this.requireTenantId(req)); } @Post('servers') @ModuleManage('proxmox') async create(@Req() req: AuthenticatedRequest, @Body() dto: CreateProxmoxServerDto) { const tenantId = this.requireTenantId(req); const created = await this.proxmoxService.createServer(tenantId, dto); // Planer sofort nachziehen — ohne Neustart (Aufgabe 4, Vorbild DkvController). await this.scheduler.refreshTenant(tenantId); return created; } @Put('servers/:id') @ModuleManage('proxmox') async update( @Req() req: AuthenticatedRequest, @Param('id') id: string, @Body() dto: UpdateProxmoxServerDto, ) { const tenantId = this.requireTenantId(req); const updated = await this.proxmoxService.updateServer(tenantId, id, dto); await this.scheduler.refreshTenant(tenantId); return updated; } @Delete('servers/:id') @ModuleManage('proxmox') async remove(@Req() req: AuthenticatedRequest, @Param('id') id: string) { const tenantId = this.requireTenantId(req); const deleted = await this.proxmoxService.deleteServer(tenantId, id); await this.scheduler.refreshTenant(tenantId); return { deleted }; } @Post('servers/:id/poll') @ModuleManage('proxmox') async poll(@Req() req: AuthenticatedRequest, @Param('id') id: string) { return this.proxmoxService.pollServer(this.requireTenantId(req), id); } /** * Verbindungstest fuer einen gespeicherten Server (Aufgabe 4, ``; * Nachbesserung Befund 1: `dto` traegt den aktuellen Formularstand, * `ProxmoxService.testConnection` prueft diesen statt blind des * gespeicherten Stands). Liefert bei Erfolg eine Erfolgsmeldung und bei * Misserfolg einen der sieben Fehlerschluessel samt kurzer Ergaenzung, * OHNE den Zwischenlagerstand zu ueberschreiben. */ @Post('servers/:id/test') @ModuleManage('proxmox') async test( @Req() req: AuthenticatedRequest, @Param('id') id: string, @Body() dto: TestProxmoxServerDto, ) { return this.proxmoxService.testConnection(this.requireTenantId(req), id, dto); } /** * Verbindungstest waehrend der Neuanlage (Nachbesserung Befund 1): es gibt * noch keinen gespeicherten Server, `dto` ist deshalb die einzige Quelle. */ @Post('servers/test') @ModuleManage('proxmox') async testDraft(@Req() req: AuthenticatedRequest, @Body() dto: TestProxmoxServerDto) { this.requireTenantId(req); return this.proxmoxService.testDraftConnection(dto); } }