Convert submodule to regular directory

This commit is contained in:
Nils-Johan Gynther
2026-04-11 16:46:48 +02:00
parent 343416a28d
commit 4189f94e0e
13 changed files with 1781 additions and 1 deletions
@@ -0,0 +1,168 @@
/**
* 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);
}
}
}