/** * INTEGRATION GUIDE: RECIPE IMPORT SERVICE * * This file shows how to integrate recipe-document-converter with recipe-app backend. * Copy and adapt this code to your recipe-app/backend directory. * * Path: backend/src/modules/import/import.service.ts */ import { Injectable, HttpException, HttpStatus } from '@nestjs/common'; import axios from 'axios'; import FormData from 'form-data'; import * as fs from 'fs'; import { PrismaService } from '../../prisma/prisma.service'; // Adjust path based on your project interface RecipeImportDTO { name: string; ingredients: Array<{ name: string; quantity?: number; unit?: string; }>; instructions: string[]; metadata?: { prepTime?: string; servings?: number; author?: string; }; } @Injectable() export class ImportService { private readonly IMPORT_SERVICE_URL = process.env.IMPORT_SERVICE_URL || 'http://import-service:3000'; constructor(private prisma: PrismaService) {} /** * Upload a PDF file to the import service and extract recipe data * * Usage in your recipe controller: * @Post('import/pdf') * @UseInterceptors(FileInterceptor('file')) * async importRecipeFromPDF(@UploadedFile() file: Express.Multer.File) { * return this.importService.importRecipeFromPDF(file); * } */ async importRecipeFromPDF(file: Express.Multer.File) { try { // Validate file if (!file) { throw new HttpException('Ingen fil angiven', HttpStatus.BAD_REQUEST); } if (file.mimetype !== 'application/pdf') { throw new HttpException('Endast PDF-filer tillåtna', HttpStatus.BAD_REQUEST); } // Call import-service const form = new FormData(); form.append('file', fs.createReadStream(file.path)); const response = await axios.post(`${this.IMPORT_SERVICE_URL}/import/pdf`, form, { headers: form.getHeaders(), timeout: 30000, }); if (!response.data.success) { throw new HttpException(response.data.error || 'Receptextrahering misslyckades', HttpStatus.BAD_REQUEST); } // Extract and validate recipe data const recipeData = this.mapImportDataToRecipe(response.data.structuredData); return { success: true, data: recipeData, metadata: response.data.pdfMetadata, }; } catch (error) { // Clean up uploaded file if (file && file.path && fs.existsSync(file.path)) { fs.unlinkSync(file.path); } if (error instanceof axios.AxiosError) { throw new HttpException( `Receptextrahering misslyckades: ${error.message}`, HttpStatus.SERVICE_UNAVAILABLE, ); } throw error; } } /** * Save imported recipe to database * * Usage in your recipe controller: * @Post('import/pdf/save') * @UseInterceptors(FileInterceptor('file')) * async importAndSaveRecipe(@UploadedFile() file: Express.Multer.File) { * const imported = await this.importService.importRecipeFromPDF(file); * return this.importService.saveImportedRecipe(imported.data); * } */ async saveImportedRecipe(recipeData: RecipeImportDTO) { try { // Create recipe with ingredients const recipe = await this.prisma.recipe.create({ data: { name: recipeData.name, instructions: recipeData.instructions.join('\n'), prepTime: recipeData.metadata?.prepTime, servings: recipeData.metadata?.servings, author: recipeData.metadata?.author, ingredients: { create: recipeData.ingredients.map((ing) => ({ name: ing.name, quantity: ing.quantity, unit: ing.unit, })), }, }, include: { ingredients: true, }, }); return { success: true, message: 'Recept sparat', recipe, }; } catch (error) { throw new HttpException( `Kunde inte spara recept: ${error instanceof Error ? error.message : 'Okänt fel'}`, HttpStatus.INTERNAL_SERVER_ERROR, ); } } /** * Map import service data to recipe format */ private mapImportDataToRecipe(importedData: any): RecipeImportDTO { return { name: importedData.name || 'Importerat recept', ingredients: importedData.ingredients || [], instructions: importedData.instructions || [], metadata: importedData.metadata || {}, }; } /** * Health check for import service (useful for monitoring) */ async checkImportServiceHealth() { try { const response = await axios.get(`${this.IMPORT_SERVICE_URL}/health`, { timeout: 5000, }); return response.data; } catch { throw new HttpException('Import-service är ej tillgänglig', HttpStatus.SERVICE_UNAVAILABLE); } } }