feat(260923-dhh): Proxmox-Modul Aufgabe 4 - Hintergrundabfrage je Mandant, Verbindungstest

- proxmox-scheduler.service.ts: ein Cron-Auftrag je aktivem Mandant
  (proxmox-poll:<tenantId>), onApplicationBootstrap (nicht onModuleInit,
  Tender-Muster), Abfrageintervall = kleinstes pollIntervalMin der
  aktiven Server, ein fehlgeschlagener Server bricht die Tick-Schleife
  nicht ab, refreshTenant() zieht nach jedem Speichern sofort nach
- proxmox.service.ts: loadActiveServersForScheduler() als einziger
  forSystem()-Aufruf des Moduls (Erlaubnisliste in
  rls-access-inventory.spec.ts), testConnection() schreibt nicht ins
  Zwischenlager, pollServer() bekommt eine Zehn-Sekunden-Sperre (T-DHH-06)
- proxmox.controller.ts: POST servers/:id/test, create() zieht den
  Planer nach dem Anlegen sofort nach
- Zugriffsklassifikation: proxmoxServer wechselt auf system-gebunden
  (Startpfad des Planers), proxmoxServerStatus bleibt gebunden

Tore: api 1306/1306 (>=1240), type-check 4/4, rls-access-inventory
und rls-coverage gruen.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-23 14:34:11 +02:00
parent 998aba9ef3
commit fccaf8db0f
8 changed files with 621 additions and 15 deletions
+23 -4
View File
@@ -4,6 +4,7 @@ import { Roles } from '../auth/decorators/roles.decorator';
import type { AuthenticatedRequest } from '../auth/types/auth-user';
import { UseModule } from '../module-registry/module.guard';
import { CreateProxmoxServerDto } from './dto/proxmox-server.dto';
import { ProxmoxSchedulerService } from './proxmox-scheduler.service';
import { ProxmoxService } from './proxmox.service';
/**
@@ -11,13 +12,16 @@ import { ProxmoxService } from './proxmox.service';
* `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`, `POST servers/:id/poll`)
* zusaetzlich `@Roles(ADMIN, SUPER_ADMIN)` (T-DHH-05).
* Modulzugriff offen; Schreiben (`POST servers`, `POST servers/:id/poll`,
* `POST servers/:id/test`) zusaetzlich `@Roles(ADMIN, SUPER_ADMIN)` (T-DHH-05).
*/
@Controller('modules/proxmox')
@UseModule('proxmox')
export class ProxmoxController {
constructor(private readonly proxmoxService: ProxmoxService) {}
constructor(
private readonly proxmoxService: ProxmoxService,
private readonly scheduler: ProxmoxSchedulerService,
) {}
private requireTenantId(req: AuthenticatedRequest): string {
const tenantId = req.tenantId;
@@ -35,7 +39,11 @@ export class ProxmoxController {
@Post('servers')
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
async create(@Req() req: AuthenticatedRequest, @Body() dto: CreateProxmoxServerDto) {
return this.proxmoxService.createServer(this.requireTenantId(req), dto);
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;
}
@Post('servers/:id/poll')
@@ -43,4 +51,15 @@ export class ProxmoxController {
async poll(@Req() req: AuthenticatedRequest, @Param('id') id: string) {
return this.proxmoxService.pollServer(this.requireTenantId(req), id);
}
/**
* Verbindungstest (Aufgabe 4, `<behavior>`): liefert bei Erfolg eine
* Erfolgsmeldung und bei Misserfolg einen der sieben Fehlerschluessel
* samt kurzer Ergaenzung, OHNE den Zwischenlagerstand zu ueberschreiben.
*/
@Post('servers/:id/test')
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
async test(@Req() req: AuthenticatedRequest, @Param('id') id: string) {
return this.proxmoxService.testConnection(this.requireTenantId(req), id);
}
}