import { Body, Controller, HttpCode, Post, Request, UploadedFile, UseGuards, UseInterceptors, BadRequestException, UnauthorizedException, } from '@nestjs/common'; import { Throttle } from '@nestjs/throttler'; import { FileInterceptor } from '@nestjs/platform-express'; import { memoryStorage } from 'multer'; import { ReceiptImportService } from './receipt-import.service'; import { ParsedReceiptItem } from './dto/parsed-receipt-item.dto'; import { SaveReceiptDto } from './dto/save-receipt.dto'; import { SaveReceiptResponse } from './dto/save-receipt.response'; import { CreateUnitMappingDto } from './dto/create-unit-mapping.dto'; import { AuthGuard } from '@nestjs/passport'; const ALLOWED_MIMES = [ 'image/jpeg', 'image/png', 'image/webp', 'image/heic', 'image/heif', 'application/pdf', 'application/octet-stream', // Flutter Web skickar detta för PDF-filer ]; @Controller('receipt-import') export class ReceiptImportController { constructor(private readonly receiptImportService: ReceiptImportService) {} @HttpCode(200) @Post() @Throttle({ default: { ttl: 60_000, limit: 20 } }) @UseInterceptors( FileInterceptor('file', { storage: memoryStorage(), limits: { fileSize: 15 * 1024 * 1024 }, // 15 MB }), ) async parseReceipt( @UploadedFile() file?: Express.Multer.File, @Request() req?: any, ): Promise { if (!file?.buffer) { throw new BadRequestException('Ingen fil skickades med.'); } if (!ALLOWED_MIMES.includes(file.mimetype)) { throw new BadRequestException( 'Otillåten filtyp. Använd JPEG, PNG, WebP eller PDF.', ); } const isPremium = req?.user?.isPremium === true || req?.user?.role === 'admin'; const userId = typeof req?.user?.id === 'number' ? req.user.id : undefined; return this.receiptImportService.parseReceipt(file, isPremium, userId); } @Post('unit-mappings') @UseGuards(AuthGuard('jwt')) async upsertUnitMapping( @Body() dto: CreateUnitMappingDto, @Request() req?: any, ) { const userId = typeof req?.user?.id === 'number' ? req.user.id : typeof req?.user?.userId === 'number' ? req.user.userId : undefined; if (!userId) { throw new UnauthorizedException('Kunde inte identifiera användaren.'); } return this.receiptImportService.upsertUnitMapping( userId, dto.productId, dto.originalUnit, dto.preferredUnit, ); } @HttpCode(200) @Post('save') @UseGuards(AuthGuard('jwt')) @Throttle({ default: { ttl: 60_000, limit: 10 } }) async saveReceipt( @Body() dto: SaveReceiptDto, @Request() req?: any, ): Promise { const userId = typeof req?.user?.id === 'number' ? req.user.id : typeof req?.user?.userId === 'number' ? req.user.userId : undefined; if (!userId) { throw new UnauthorizedException('Kunde inte identifiera användaren.'); } const isAdmin = req?.user?.role === 'admin'; if (dto.items.some((item) => item.learnAliasGlobally === true) && !isAdmin) { throw new BadRequestException('Endast administratörer kan spara globala aliaser.'); } return this.receiptImportService.saveReceipt(userId, dto); } }