feat: implement backend label print agent system for remote label rendering and job management
Build and Push Multi-Platform Images / build-and-push (push) Successful in 33s

This commit is contained in:
2026-05-07 22:46:29 +02:00
parent 0c94e7b999
commit 80f862a0c0
15 changed files with 995 additions and 15 deletions
@@ -0,0 +1,89 @@
import {
Body,
Controller,
Get,
HttpCode,
HttpStatus,
NotFoundException,
Param,
ParseIntPipe,
Post,
Query,
Res,
StreamableFile,
UseGuards,
} from '@nestjs/common';
import type { Response } from 'express';
import { JwtOrApiKeyGuard } from '../auth/jwt-or-apikey.guard';
import { RequirePermissions } from '../auth/permissions.decorator';
import { Permission } from '../auth/permissions.enum';
import { LabelPrintAgentService } from './label-print-agent.service';
@Controller('api/label-print-agent')
@UseGuards(JwtOrApiKeyGuard)
export class LabelPrintAgentController {
constructor(private readonly service: LabelPrintAgentService) {}
// Manuell einen Job anlegen (Frontend → Backend)
@Post('jobs')
@HttpCode(HttpStatus.CREATED)
@RequirePermissions(Permission.VIEW_SCANNER)
async createJob(
@Body() body: { templateId: number; fieldValues?: Record<string, string> },
) {
const job = await this.service.createJob(body.templateId, body.fieldValues ?? {});
return { jobId: String(job.Id) };
}
// Agent: nächsten Job abholen (Polling)
@Get('jobs/next')
async getNextJob(@Query('agentId') agentId: string, @Res({ passthrough: true }) res: Response) {
const job = await this.service.claimNextJob(agentId ?? 'unknown');
if (!job) {
res.status(HttpStatus.NO_CONTENT).send();
return;
}
return {
jobId: String(job.Id),
labelImageBase64: job.LabelImageData ? job.LabelImageData.toString('base64') : null,
labelImageContentType: 'image/png',
labelWidthMm: job.LabelWidthMm,
labelHeightMm: job.LabelHeightMm,
};
}
// Agent: Bild separat abrufen
@Get('jobs/:id/image')
async getImage(
@Param('id', ParseIntPipe) id: number,
@Res({ passthrough: true }) res: Response,
): Promise<StreamableFile> {
const buf = await this.service.getJobImage(id);
if (!buf) throw new NotFoundException('Bild nicht gefunden');
const { Readable } = await import('stream');
res.setHeader('Content-Type', 'image/png');
return new StreamableFile(Readable.from(buf));
}
// Agent: Druck erfolgreich
@Post('jobs/:id/printed')
@HttpCode(HttpStatus.OK)
async markPrinted(
@Param('id', ParseIntPipe) id: number,
@Body() body: { agentId?: string; printerName?: string },
) {
await this.service.markPrinted(id, body.agentId ?? 'unknown', body.printerName ?? '');
return { ok: true };
}
// Agent: Druckfehler
@Post('jobs/:id/error')
@HttpCode(HttpStatus.OK)
async markError(
@Param('id', ParseIntPipe) id: number,
@Body() body: { agentId?: string; printerName?: string; errorMessage?: string },
) {
await this.service.markError(id, body.agentId ?? 'unknown', body.printerName ?? '', body.errorMessage ?? '');
return { ok: true };
}
}