Initial commit with Email Import Wizard and Task Processor updates

This commit is contained in:
2026-05-04 08:02:11 +02:00
commit effdc5d59f
170 changed files with 67739 additions and 0 deletions
@@ -0,0 +1,145 @@
import {
BadRequestException,
Body,
Controller,
Delete,
Get,
HttpCode,
Param,
ParseIntPipe,
Post,
Put,
Request,
Res,
StreamableFile,
} from '@nestjs/common';
import type { Response } from 'express';
import { createReadStream } from 'fs';
import { InboxService } from './inbox.service';
import { InboxPostprocessorService } from '../inbox-postprocessor/inbox-postprocessor.service';
import { BarcodeScannerService } from '../barcode/barcode-scanner.service';
import { RequirePermissions } from '../auth/permissions.decorator';
import { Permission } from '../auth/permissions.enum';
@Controller('api/inbox')
@RequirePermissions(Permission.VIEW_SCANNER)
export class InboxController {
constructor(
private readonly inboxService: InboxService,
private readonly postprocessor: InboxPostprocessorService,
private readonly barcodeScanner: BarcodeScannerService,
) {}
@Get()
async list(@Request() req: any) {
const preferredUsername: string | null = req.user?.preferredUsername ?? null;
return this.inboxService.listFiles(preferredUsername);
}
@Post('rescan')
async rescan() {
return this.barcodeScanner.rescanAll();
}
@Get(':id/preview')
async preview(
@Param('id') id: string,
@Request() req: any,
@Res({ passthrough: true }) res: Response,
): Promise<StreamableFile> {
const preferredUsername: string | null = req.user?.preferredUsername ?? null;
const { doc, pdfPath } = await this.inboxService.resolveDocument(id, preferredUsername);
res.setHeader('Content-Type', 'application/pdf');
res.setHeader('Content-Disposition', `inline; filename="${doc.OriginalName}"`);
return new StreamableFile(createReadStream(pdfPath));
}
@Get(':id/pages/:page/thumbnail')
async pageThumbnail(
@Param('id') id: string,
@Param('page', ParseIntPipe) page: number,
@Request() req: any,
@Res({ passthrough: true }) res: Response,
): Promise<StreamableFile> {
const preferredUsername: string | null = req.user?.preferredUsername ?? null;
const filePath = await this.inboxService.resolvePageImage(id, page, 'thumbnail', preferredUsername);
res.setHeader('Content-Type', 'image/png');
res.setHeader('Cache-Control', 'private, max-age=3600');
return new StreamableFile(createReadStream(filePath));
}
@Delete(':id')
@HttpCode(204)
async remove(@Param('id') id: string, @Request() req: any): Promise<void> {
const preferredUsername: string | null = req.user?.preferredUsername ?? null;
await this.inboxService.deleteDocument(id, preferredUsername);
}
@Delete(':id/pages/:page')
@HttpCode(204)
async removePage(
@Param('id') id: string,
@Param('page', ParseIntPipe) page: number,
@Request() req: any,
): Promise<void> {
const preferredUsername: string | null = req.user?.preferredUsername ?? null;
await this.inboxService.deletePage(id, page, preferredUsername);
}
@Post(':id/reset-edits')
@HttpCode(204)
async resetEdits(@Param('id') id: string, @Request() req: any): Promise<void> {
const preferredUsername: string | null = req.user?.preferredUsername ?? null;
await this.inboxService.resetEdits(id, preferredUsername);
}
@Post(':id/postprocess')
async postprocess(
@Param('id') id: string,
@Request() req: any,
@Body() body: { sectionOffset?: number; processOnlyOne?: boolean; replaceDuplicate?: boolean },
) {
const preferredUsername: string | null = req.user?.preferredUsername ?? null;
const { results, totalSections } = await this.postprocessor.runForDocument(
id,
preferredUsername,
body?.sectionOffset ?? 0,
body?.processOnlyOne ?? false,
body?.replaceDuplicate ?? false,
);
return { results, totalSections };
}
@Put(':id/pages/:page/rotation')
@HttpCode(204)
async setPageRotation(
@Param('id') id: string,
@Param('page', ParseIntPipe) page: number,
@Body() body: { rotation?: number },
@Request() req: any,
): Promise<void> {
const rotation = Number(body?.rotation);
if (!Number.isFinite(rotation)) {
throw new BadRequestException('rotation muss eine Zahl sein');
}
const preferredUsername: string | null = req.user?.preferredUsername ?? null;
await this.inboxService.setPageRotation(id, page, rotation, preferredUsername);
}
@Get(':id/pages/:page/preview')
async pagePreview(
@Param('id') id: string,
@Param('page', ParseIntPipe) page: number,
@Request() req: any,
@Res({ passthrough: true }) res: Response,
): Promise<StreamableFile> {
const preferredUsername: string | null = req.user?.preferredUsername ?? null;
const filePath = await this.inboxService.resolvePageImage(id, page, 'preview', preferredUsername);
res.setHeader('Content-Type', 'image/png');
res.setHeader('Cache-Control', 'private, max-age=3600');
return new StreamableFile(createReadStream(filePath));
}
}