Files
paperlessmanager/paperless-backend/src/inbox/inbox.controller.ts
T
bjoernpoettker 66aeab282c
Build and Push Multi-Platform Images / build-and-push (push) Successful in 19s
Revert "fix: resolve all ESLint errors in backend and frontend"
This reverts commit 07dfd7e840.
2026-06-16 16:19:11 +02:00

280 lines
7.8 KiB
TypeScript

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 { UserSettingsService } from '../user-settings/user-settings.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,
private readonly userSettingsService: UserSettingsService,
) {}
@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/pages/:page/split')
@HttpCode(204)
async toggleManualSplit(
@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.toggleManualSplit(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));
}
@Post(':id/pages/:page/scan-region')
async scanRegion(
@Param('id') id: string,
@Param('page', ParseIntPipe) page: number,
@Body() body: { x: number; y: number; w: number; h: number },
@Request() req: any,
): Promise<{ found: string[] }> {
const preferredUsername: string | null =
req.user?.preferredUsername ?? null;
return this.inboxService.scanRegion(
id,
page,
body.x,
body.y,
body.w,
body.h,
preferredUsername,
);
}
@Post(':id/source')
@HttpCode(204)
async updateSource(
@Param('id') id: string,
@Body() body: { source: any },
@Request() req: any,
): Promise<void> {
const preferredUsername: string | null =
req.user?.preferredUsername ?? null;
await this.inboxService.updateSource(id, body.source, preferredUsername);
}
@Post(':id/download-segment')
async downloadSegment(
@Param('id') id: string,
@Body() body: { pages: number[] },
@Request() req: any,
@Res({ passthrough: true }) res: Response,
): Promise<StreamableFile> {
const preferredUsername: string | null =
req.user?.preferredUsername ?? null;
const { buffer, filename } = await this.inboxService.getSegmentPdfBuffer(
id,
preferredUsername,
body.pages ?? [],
);
const { Readable } = await import('stream');
res.setHeader('Content-Type', 'application/pdf');
res.setHeader(
'Content-Disposition',
`attachment; filename="${encodeURIComponent(filename)}"`,
);
return new StreamableFile(Readable.from(buffer));
}
@Post(':id/send-email')
@HttpCode(204)
async sendEmail(
@Param('id') id: string,
@Body()
body: {
to: string;
subject: string;
body: string;
html?: string;
segments: { pages: number[]; filename: string }[];
sender?: string;
},
@Request() req: any,
): Promise<void> {
const preferredUsername: string | null =
req.user?.preferredUsername ?? null;
const smtpOverride =
body.sender === 'user'
? await this.userSettingsService.getSmtpConfig(req.user.userId)
: null;
await this.inboxService.sendAsEmail(id, preferredUsername, {
...body,
smtpOverride: smtpOverride ?? undefined,
});
}
}