Convert submodule to regular directory
This commit is contained in:
@@ -0,0 +1,368 @@
|
||||
# 🚀 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
|
||||
Reference in New Issue
Block a user