Convert submodule to regular directory
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user