710034c80a
Nachbesserung aus dem Browser-Rundgang zu 260923-dhh (Befund 1): "Verbindung testen" pruefte bislang immer den in der Datenbank gespeicherten Server, nicht das ungespeicherte Formular. Eine im Formular abgeschaltete Zertifikatspruefung oder ein neu eingetipptes Token-Geheimnis wurden dadurch beim Test ignoriert und erst nach "Speichern" wirksam — eine Falle fuer genau den Ablauf, den Nutzer instinktiv waehlen (eintippen, testen, dann erst speichern). Neues `TestProxmoxServerDto` plus Merge-Baustein `resolveEffectiveTestServer` in `ProxmoxService`: normale Felder folgen dem Formular (auch wenn absichtlich geleert), Geheimnisfelder folgen der bestehenden "leer -> gespeicherten Wert behalten"-Regel, weil `ServerForm` sie beim Laden nie aus der Datenbank vorbefuellt. Neue Route `POST servers/test` (ohne `:id`) deckt die Neuanlage ab, wo es noch keinen gespeicherten Server gibt. Der Testen-Knopf steht jetzt immer zur Verfuegung, nicht mehr nur nach dem ersten Speichern. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
124 lines
4.3 KiB
TypeScript
124 lines
4.3 KiB
TypeScript
import {
|
|
Body,
|
|
Controller,
|
|
Delete,
|
|
ForbiddenException,
|
|
Get,
|
|
Param,
|
|
Post,
|
|
Put,
|
|
Req,
|
|
} from '@nestjs/common';
|
|
import { Role } from '@prisma/client';
|
|
import { Roles } from '../auth/decorators/roles.decorator';
|
|
import type { AuthenticatedRequest } from '../auth/types/auth-user';
|
|
import { 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`, `POST servers/test`,
|
|
* `POST servers/:id/poll`, `POST servers/:id/test`) zusaetzlich
|
|
* `@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')
|
|
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
|
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')
|
|
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
|
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')
|
|
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
|
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')
|
|
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
|
async poll(@Req() req: AuthenticatedRequest, @Param('id') id: string) {
|
|
return this.proxmoxService.pollServer(this.requireTenantId(req), id);
|
|
}
|
|
|
|
/**
|
|
* Verbindungstest fuer einen gespeicherten Server (Aufgabe 4, `<behavior>`;
|
|
* 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')
|
|
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
|
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')
|
|
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
|
async testDraft(@Req() req: AuthenticatedRequest, @Body() dto: TestProxmoxServerDto) {
|
|
this.requireTenantId(req);
|
|
return this.proxmoxService.testDraftConnection(dto);
|
|
}
|
|
}
|