# 🚀 Deployment Guide: Import Service Integration Din nuvarande setup: ``` recept.gynther.se → Caddy Proxy ├─ recipe-frontend:3000 (Next.js) ├─ recipe-api:8080 (NestJS Backend) └─ MariaDB ``` Ny setup (med import-service): ``` recept.gynther.se → Caddy Proxy ├─ recipe-frontend:3000 (Next.js) ├─ recipe-api:8080 (NestJS Backend) *← kan anropa import-service* ├─ recipe-import-service:3000 (NestJS Import) *← NYT* └─ MariaDB ``` --- ## 📋 Steg-för-steg deployment ### 1. Uppdatera Caddy configuration **Option A: Manuell uppdatering** ```bash # SSH till server ssh user@server.se # Backup nuvarande Caddyfile cp /opt/containers/caddy/conf/Caddyfile /opt/containers/caddy/conf/Caddyfile.backup # Uppdatera med nya import-service reglerna # Se Caddyfile.production för den kompletta konfigurationen ``` **Option B: AnvĂ€nda versionerad fil** ```bash # Kopiera Caddyfile.production till servern scp Caddyfile.production user@server.se:/opt/containers/caddy/conf/Caddyfile # Reload Caddy docker exec caddy-proxy caddy reload ``` --- ### 2. Starta import-service container **Om du anvĂ€nder din nuvarande docker-compose:** ```bash # SSH till server ssh user@server.se # Navigera till import-service mapp cd /path/to/recipe-document-converter/recipe-document-converter # Build och starta docker-compose up -d recipe-import-service # Eller anvĂ€nd production docker-compose: docker-compose -f docker-compose.production.yml up -d recipe-import-service ``` **Kontrollera att den körs:** ```bash docker ps | grep import-service docker logs recipe-import-service # Test health endpoint curl http://localhost:3000/health ``` --- ### 3. Integrera import-service i recipe-app backend LĂ€gg till i ditt `recipe-app/backend` projekt: **Skapa `src/modules/import/import.service.ts`:** ```typescript 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://recipe-import-service:3000'; constructor(private prisma: PrismaService) {} async importRecipeFromPDF(file: Express.Multer.File) { try { if (!file || file.mimetype !== 'application/pdf') { throw new HttpException('Endast PDF-filer tillĂ„tna', HttpStatus.BAD_REQUEST); } 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); } return { success: true, data: response.data.structuredData, 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, recipe }; } catch (error) { throw new HttpException('Kunde inte spara recept', HttpStatus.INTERNAL_SERVER_ERROR); } } } ``` **Skapa `src/modules/import/import.module.ts`:** ```typescript import { Module } from '@nestjs/common'; import { ImportService } from './import.service'; @Module({ providers: [ImportService], exports: [ImportService], }) export class ImportModule {} ``` **Uppdatera `src/modules/recipes/recipes.controller.ts`:** ```typescript import { Controller, Post, 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) {} @Post('import/pdf') @UseInterceptors(FileInterceptor('file')) async importRecipeFromPDF(@UploadedFile() file: Express.Multer.File) { return this.importService.importRecipeFromPDF(file); } @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); } } ``` **Uppdatera `src/app.module.ts`:** ```typescript import { ImportModule } from './modules/import/import.module'; @Module({ imports: [ // ... existing modules ImportModule, ], }) export class AppModule {} ``` --- ### 4. Uppdatera miljövariabler **`recipe-app/backend/.env` eller docker environment:** ```env DATABASE_URL=mysql://recipe_user:secure_password@recipe-db:3306/recipe_db IMPORT_SERVICE_URL=http://recipe-import-service:3000 ``` --- ### 5. Deploy **Lokalt (development):** ```bash # Terminal 1: import-service cd recipe-document-converter/recipe-document-converter npm install npm run start:dev # Terminal 2: recipe-app backend cd recipe-app/backend npm install IMPORT_SERVICE_URL=http://localhost:3000 npm run start:dev # Terminal 3: recipe-app frontend cd recipe-app/frontend npm run dev ``` **Production (Docker):** ```bash # Option A: Starta import-service separat ssh user@server.se cd /path/to/recipe-document-converter docker-compose up -d recipe-import-service # Option B: AnvĂ€nd production docker-compose (all-in-one) docker-compose -f docker-compose.production.yml up -d # Reload Caddy docker exec caddy-proxy caddy reload ``` --- ## đŸ§Ș Testing ### Test 1: Import-service health ```bash curl https://recept.gynther.se/api/recipes/import/health ``` **Expected Response:** ```json { "status": "ok", "timestamp": "...", "service": "recipe-import-service", "version": "1.0.0" } ``` ### Test 2: Import PDF (preview) ```bash curl -X POST \ -F "file=@recipe.pdf" \ https://recept.gynther.se/api/recipes/import/pdf ``` ### Test 3: Import & Save ```bash curl -X POST \ -F "file=@recipe.pdf" \ https://recept.gynther.se/api/recipes/import/pdf/save ``` --- ## 📊 Routing Overview ``` https://recept.gynther.se ↓ Caddy Proxy (:443) ├─ / → recipe-frontend:3000 ├─ /api/recipes/import/* → recipe-import-service:3000 ├─ /api/products* → recipe-api:8080 ├─ /api/inventory* → recipe-api:8080 ├─ /api/recipes* → recipe-api:8080 (BUT /api/recipes/import/* intercepts here) └─ /api/* → recipe-frontend:3000 ``` **Wichtigt:** Import-endpoints mĂ„ste komma FÖRE andra /api/recipes* regler för att inte fastna! --- ## 🔍 Troubleshooting ### Import-service inte tillgĂ€nglig ```bash # Kontrollera container docker ps | grep import-service # Se loggar docker logs recipe-import-service # Kontrollera Caddy routing docker exec caddy-proxy curl http://recipe-import-service:3000/health # Reload Caddy docker exec caddy-proxy caddy reload ``` ### 502 Bad Gateway frĂ„n Caddy ```bash # Kontrollera att import-service körs docker logs recipe-import-service # Kontrollera nĂ€tverksanslutning docker network inspect recipe-network # Verify environment variable docker inspect recipe-api | grep IMPORT_SERVICE_URL ``` ### Filuppladdning misslyckas ```bash # Kontrollera volym permissions docker exec recipe-import-service ls -la /app/uploads/ # Kontrollera filstorlek ls -lh recipe.pdf # Öka MAX_FILE_SIZE om behövs (standard: 50MB) docker exec recipe-import-service env | grep MAX_FILE_SIZE ``` --- ## ✅ Deployment Checklist - [ ] Caddy.production Ă€r uppdaterad med import-service reglerna - [ ] Import-service container Ă€r byggd och klar - [ ] ImportModule Ă€r tillagd i recipe-app backend - [ ] IMPORT_SERVICE_URL Ă€r satt i miljövariabler - [ ] axios Ă€r installerat i backend - [ ] Caddy reloadad - [ ] Health endpoints testad - [ ] PDF import testad - [ ] Loggar monitorade --- ## 🎯 NĂ€sta Steg 1. **LLM Integration (Mistral)** - För avancerad recepttolkning 2. **Excel/Word support** - Expandera filformat 3. **Caching** - Redis för snabbare import 4. **Error Monitoring** - LĂ€gg till Sentry eller liknande 5. **Rate Limiting** - Skydda import-endpoint