14 KiB
14 KiB
🔌 Integration Guide: Recipe Import Service
Denna guide visar hur du integrerar recipe-document-converter (import-service) med din befintliga recipe-app.
📋 Innehållsförteckning
- Installation & Setup
- Arkitektur
- Backend Integration
- Docker Deployment
- API Endpoints
- Testing
- Troubleshooting
Installation & Setup
1. Krav
- recipe-app — Din befintliga receptapp (Next.js frontend + NestJS backend)
- recipe-document-converter — Import-service (denna repo)
- Docker 24+ och Docker Compose
- Node.js 22.x
2. Klona och organisera projekten
dev/
├── recipe-app/ # Din befintliga app
│ ├── frontend/
│ ├── backend/
│ ├── Dockerfile
│ └── compose.yml
│
└── recipe-document-converter/ # Import-service
├── recipe-document-converter/
├── Dockerfile
├── docker-compose.yml
├── Caddyfile
└── README.md
3. Uppdatera recipe-document-converter paket
cd recipe-document-converter/recipe-document-converter
# TypeScript är redan uppdaterad till 5.4.5 ✅
# Installera dependencies
npm install
Arkitektur
System Diagram
┌─────────────────────────────────────────────────────────┐
│ Caddy Reverse Proxy │
│ (port 80/443) │
└────────────┬────────────────┬─────────────────┬─────────┘
│ │ │
┌───────▼───┐ ┌──────▼────────┐ ┌──▼─────────┐
│ Frontend │ │ Backend API │ │ Import │
│ :4000 │ │ :3001 │ │ :3000 │
│ │ │ (NestJS) │ │ (NestJS) │
│ Next.js │ │ │ │ │
│ 16.2 │ │ Recipes ┐ │ │ PDF │
│ │ │ Inventory ├─────┼──extract │
└───────────┘ │ Products │ │ │ structure │
│ │ │ │ │
└──────┬────┘ │ └──────┬──────┘
│ │ │
┌──────▼──────────────┐ │
│ MariaDB 11 │ │
│ (recipe_db) │ │
└─────────────────────┘ │
│
┌─────────────▼─┐
│ Uploads/ │
│ (Volym) │
└───────────────┘
Data Flow
1. Användare laddar upp PDF
↓
2. recipe-app frontend → backend POST /recipes/import/pdf
↓
3. Backend → FormData → import-service POST /import/pdf
↓
4. import-service extraherar → JSON (ingredients, instructions, metadata)
↓
5. Backend validerar + sparar i Prisma/MariaDB
↓
6. Frontend visar importerat recept
Backend Integration
Steg 1: Uppdatera recipe-app backend
cd recipe-app/backend
# Installera axios om det saknas
npm install axios
# Om du använder FormData
npm install form-data
Steg 2: Skapa import-modul
Skapa 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';
interface RecipeImportDTO {
name: string;
ingredients: Array<{
name: string;
quantity?: number;
unit?: string;
}>;
instructions: string[];
metadata?: {
prepTime?: string;
servings?: number;
};
}
@Injectable()
export class ImportService {
private readonly IMPORT_SERVICE_URL = process.env.IMPORT_SERVICE_URL || 'http://import-service:3000';
constructor(private prisma: PrismaService) {}
async importRecipeFromPDF(file: Express.Multer.File) {
try {
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, HttpStatus.BAD_REQUEST);
}
const recipeData = this.mapImportDataToRecipe(response.data.structuredData);
return {
success: true,
data: recipeData,
metadata: response.data.pdfMetadata,
};
} catch (error) {
if (file?.path && fs.existsSync(file.path)) {
fs.unlinkSync(file.path);
}
if (error instanceof axios.AxiosError) {
throw new HttpException(
`Import misslyckades: ${error.message}`,
HttpStatus.SERVICE_UNAVAILABLE,
);
}
throw error;
}
}
async saveImportedRecipe(recipeData: RecipeImportDTO) {
try {
const recipe = await this.prisma.recipe.create({
data: {
name: recipeData.name,
instructions: recipeData.instructions.join('\n'),
prepTime: recipeData.metadata?.prepTime,
servings: recipeData.metadata?.servings,
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,
);
}
}
private mapImportDataToRecipe(importedData: any): RecipeImportDTO {
return {
name: importedData.name || 'Importerat recept',
ingredients: importedData.ingredients || [],
instructions: importedData.instructions || [],
metadata: importedData.metadata || {},
};
}
}
Steg 3: Skapa import-modul
Skapa backend/src/modules/import/import.module.ts:
import { Module } from '@nestjs/common';
import { ImportService } from './import.service';
@Module({
providers: [ImportService],
exports: [ImportService],
})
export class ImportModule {}
Steg 4: Lägg till endpoints i recipes controller
Uppdatera backend/src/modules/recipes/recipes.controller.ts:
import { Controller, Post, Get, UseInterceptors, UploadedFile } from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { ImportService } from '../import/import.service';
@Controller('recipes')
export class RecipesController {
constructor(private importService: ImportService) {}
/**
* Import recipe from PDF without saving
* POST /recipes/import/pdf
*/
@Post('import/pdf')
@UseInterceptors(FileInterceptor('file'))
async importRecipeFromPDF(@UploadedFile() file: Express.Multer.File) {
return this.importService.importRecipeFromPDF(file);
}
/**
* Import and save recipe
* POST /recipes/import/pdf/save
*/
@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);
}
/**
* Check import service health
* GET /recipes/import/health
*/
@Get('import/health')
async importServiceHealth() {
return this.importService.checkImportServiceHealth();
}
}
Steg 5: Registrera import-modul
Uppdatera backend/src/app.module.ts:
import { ImportModule } from './modules/import/import.module';
@Module({
imports: [
// ... existing modules
ImportModule,
],
})
export class AppModule {}
Docker Deployment
Option 1: Lokal utveckling (utan Docker)
# Terminal 1: Start import-service
cd recipe-document-converter/recipe-document-converter
npm install
npm run start:dev
# Terminal 2: Start recipe-app backend
cd recipe-app/backend
npm install
IMPORT_SERVICE_URL=http://localhost:3000 npm run start:dev
# Terminal 3: Start recipe-app frontend
cd recipe-app/frontend
npm install
npm run dev
Access:
- Frontend: http://localhost:3000 (Next.js default)
- Backend: http://localhost:3001/api
- Import: http://localhost:3000/import
Option 2: Docker Compose (Full Stack)
Förutsättningar:
recipe-app/ligger bredvidrecipe-document-converter/- recipe-app har
Dockerfilei root
Starta all-in-one:
cd recipe-document-converter/recipe-document-converter
# Bygg och starta alla tjänster
docker-compose up -d
# Eller med bygge
docker-compose up -d --build
Tjänster:
- Frontend: http://localhost (via Caddy)
- Backend API: http://localhost/api
- Import Service: http://localhost/import
- Health: http://localhost/health
Se loggar:
docker-compose logs -f recipe-app-backend
docker-compose logs -f import-service
docker-compose logs -f recipe-db
docker-compose logs -f caddy
Stoppa allt:
docker-compose down
API Endpoints
Import Service Endpoints
| Method | Endpoint | Beskrivning | Auth |
|---|---|---|---|
GET |
/health |
Hälsokontroll | Nej |
POST |
/import/pdf |
Importera PDF | Nej |
Recipe App Backend Integration Endpoints
| Method | Endpoint | Beskrivning |
|---|---|---|
POST |
/api/recipes/import/pdf |
Importera PDF (preview) |
POST |
/api/recipes/import/pdf/save |
Importera och spara |
GET |
/api/recipes/import/health |
Health check |
Testing
1. Test import-service isolerat
curl -X POST \
-F "file=@recipe.pdf" \
http://localhost:3000/import/pdf
Response:
{
"success": true,
"rawText": "...",
"pdfMetadata": {
"fileName": "recipe.pdf",
"pages": 1,
"author": "Unknown"
},
"structuredData": {
"name": "Kycklingcurry",
"ingredients": [...],
"instructions": [...]
}
}
2. Test recipe-app integration
curl -X POST \
-F "file=@recipe.pdf" \
http://localhost:3001/api/recipes/import/pdf
3. Test health checks
# Import service
curl http://localhost:3000/health
# Recipe app backend
curl http://localhost:3001/health
# Import from backend
curl http://localhost:3001/api/recipes/import/health
4. Test full end-to-end (Docker)
# Start services
docker-compose up -d
# Wait for services to be healthy
sleep 10
# Check all services
curl http://localhost/health
curl http://localhost/api/health
curl http://localhost/import/health
# Import recipe
curl -X POST \
-F "file=@recipe.pdf" \
http://localhost/api/recipes/import/pdf/save
Troubleshooting
Import Service är inte tillgänglig
# Kontrollera om den körs
docker ps | grep import-service
# Se loggar
docker logs recipe-import-service
# Kontrollera hälsa
curl http://localhost:3000/health
Docker nätverk-error
# Reinitialize docker-compose
docker-compose down
docker system prune -a
docker-compose up --build
Filuppladdning misslyckas
# Kontrollera filstorlek (default 50MB)
ls -lh recipe.pdf
# Kontrollera uploads-mapp permissions
docker exec recipe-import-service ls -la /app/uploads/
TypeScript fel
# Uppdatera dependencies
npm install
# Clear cache
npm cache clean --force
# Rebuild
npm run build
Databas-anslutning misslyckad
# Kontrollera MariaDB
docker logs recipe-db
# Kontrollera URL
echo $DATABASE_URL
# Test direkten
docker exec recipe-db mysql -urecipe_user -psecure_password -e "USE recipe_db; SHOW TABLES;"
Production Checklist
- TypeScript compilerar utan fel
- Alla miljövariabler är satta
- Database-backup är konfigurerat
- Import-service är tillgänglig från backend
- Caddy certifikater är satta upp
- Log rotation är konfigurerat
- Health endpoints fungerar
- File uploads permissions är rätt
- Rate limiting är konfigurerat (if needed)
- Monitoring setup är på plats
Nästa steg
- LLM Integration — Lägg till Mistral för avancerad strukturering
- Excel/Word support — Expandera till fler filformat
- OCR — Lägg till stöd för skannade dokument
- Caching — Implementera Redis för snabbare import
- Authentication — Lägg till auth om behövs