diff --git a/.gitea/workflows/production-deploy.yml b/.gitea/workflows/production-deploy.yml index 9c12d9c..02321f6 100644 --- a/.gitea/workflows/production-deploy.yml +++ b/.gitea/workflows/production-deploy.yml @@ -58,8 +58,21 @@ jobs: # Backup current container ID if running OLD_CONTAINER=$(docker ps -q -f name=$CONTAINER_NAME || echo "") + # Export environment variables for docker-compose + export N8N_WEBHOOK_URL="${{ vars.N8N_WEBHOOK_URL || '' }}" + export N8N_SECRET_TOKEN="${{ secrets.N8N_SECRET_TOKEN || '' }}" + export N8N_API_KEY="${{ vars.N8N_API_KEY || '' }}" + + # Also export other variables that docker-compose needs + export MY_EMAIL="${{ vars.MY_EMAIL }}" + export MY_INFO_EMAIL="${{ vars.MY_INFO_EMAIL }}" + export MY_PASSWORD="${{ secrets.MY_PASSWORD }}" + export MY_INFO_PASSWORD="${{ secrets.MY_INFO_PASSWORD }}" + export ADMIN_BASIC_AUTH="${{ secrets.ADMIN_BASIC_AUTH }}" + # Start new container with updated image (docker-compose will handle this) echo "🆕 Starting new production container..." + echo "📝 Environment check: N8N_WEBHOOK_URL=${N8N_WEBHOOK_URL:-(not set)}" docker compose -f $COMPOSE_FILE up -d --no-deps --build portfolio # Wait for new container to be healthy @@ -146,6 +159,7 @@ jobs: ADMIN_BASIC_AUTH: ${{ secrets.ADMIN_BASIC_AUTH }} N8N_WEBHOOK_URL: ${{ vars.N8N_WEBHOOK_URL || '' }} N8N_SECRET_TOKEN: ${{ secrets.N8N_SECRET_TOKEN || '' }} + N8N_API_KEY: ${{ vars.N8N_API_KEY || '' }} - name: Production Health Check run: | diff --git a/app/api/n8n/chat/route.ts b/app/api/n8n/chat/route.ts index 0ebb56e..ec856c0 100644 --- a/app/api/n8n/chat/route.ts +++ b/app/api/n8n/chat/route.ts @@ -30,15 +30,24 @@ export async function POST(request: NextRequest) { // Call your n8n chat webhook const n8nWebhookUrl = process.env.N8N_WEBHOOK_URL; - if (!n8nWebhookUrl) { - console.error("N8N_WEBHOOK_URL not configured"); + if (!n8nWebhookUrl || n8nWebhookUrl.trim() === '') { + console.error("N8N_WEBHOOK_URL not configured. Environment check:", { + hasUrl: !!process.env.N8N_WEBHOOK_URL, + urlValue: process.env.N8N_WEBHOOK_URL || '(empty)', + nodeEnv: process.env.NODE_ENV, + }); return NextResponse.json({ reply: getFallbackResponse(userMessage), }); } - const webhookUrl = `${n8nWebhookUrl}/webhook/chat`; - console.log(`Sending to n8n: ${webhookUrl}`); + // Ensure URL doesn't have trailing slash before adding /webhook/chat + const baseUrl = n8nWebhookUrl.replace(/\/$/, ''); + const webhookUrl = `${baseUrl}/webhook/chat`; + console.log(`Sending to n8n: ${webhookUrl}`, { + hasSecretToken: !!process.env.N8N_SECRET_TOKEN, + hasApiKey: !!process.env.N8N_API_KEY, + }); // Add timeout to prevent hanging requests const controller = new AbortController(); @@ -67,8 +76,13 @@ export async function POST(request: NextRequest) { if (!response.ok) { const errorText = await response.text().catch(() => 'Unknown error'); - console.error(`n8n webhook failed with status: ${response.status}`, errorText); - throw new Error(`n8n webhook failed: ${response.status} - ${errorText}`); + console.error(`n8n webhook failed with status: ${response.status}`, { + status: response.status, + statusText: response.statusText, + error: errorText, + webhookUrl: webhookUrl.replace(/\/\/[^:]+:[^@]+@/, '//***:***@'), // Hide credentials in logs + }); + throw new Error(`n8n webhook failed: ${response.status} - ${errorText.substring(0, 200)}`); } const data = await response.json(); @@ -198,7 +212,10 @@ export async function POST(request: NextRequest) { console.error("Error details:", { message: error instanceof Error ? error.message : String(error), stack: error instanceof Error ? error.stack : undefined, - n8nUrl: process.env.N8N_WEBHOOK_URL ? 'configured' : 'missing', + n8nUrl: process.env.N8N_WEBHOOK_URL ? `configured (${process.env.N8N_WEBHOOK_URL})` : 'missing', + hasSecretToken: !!process.env.N8N_SECRET_TOKEN, + hasApiKey: !!process.env.N8N_API_KEY, + nodeEnv: process.env.NODE_ENV, }); // Fallback to mock responses diff --git a/app/components/ChatWidget.tsx b/app/components/ChatWidget.tsx index 8de68a1..79cb8ee 100644 --- a/app/components/ChatWidget.tsx +++ b/app/components/ChatWidget.tsx @@ -129,10 +129,21 @@ export default function ChatWidget() { }); if (!response.ok) { - throw new Error("Failed to get response"); + const errorText = await response.text().catch(() => 'Unknown error'); + console.error("Chat API error:", { + status: response.status, + statusText: response.statusText, + error: errorText, + }); + throw new Error(`Failed to get response: ${response.status} - ${errorText.substring(0, 100)}`); } const data = await response.json(); + + // Log response for debugging (only in development) + if (process.env.NODE_ENV === 'development') { + console.log("Chat API response:", data); + } // Decode HTML entities in the reply let replyText = data.reply || "Sorry, I couldn't process that. Please try again."; diff --git a/docker-compose.production.yml b/docker-compose.production.yml index ab5854a..63165fb 100644 --- a/docker-compose.production.yml +++ b/docker-compose.production.yml @@ -19,6 +19,9 @@ services: - MY_INFO_PASSWORD=${MY_INFO_PASSWORD} - ADMIN_BASIC_AUTH=${ADMIN_BASIC_AUTH:-admin:your_secure_password_here} - LOG_LEVEL=info + - N8N_WEBHOOK_URL=${N8N_WEBHOOK_URL:-} + - N8N_SECRET_TOKEN=${N8N_SECRET_TOKEN:-} + - N8N_API_KEY=${N8N_API_KEY:-} volumes: - portfolio_data:/app/.next/cache networks: diff --git a/docs/N8N_CHAT_PRODUCTION_SETUP.md b/docs/N8N_CHAT_PRODUCTION_SETUP.md new file mode 100644 index 0000000..9804394 --- /dev/null +++ b/docs/N8N_CHAT_PRODUCTION_SETUP.md @@ -0,0 +1,146 @@ +# 🔧 n8n Chat Setup für Production + +## Problem: AI Chat funktioniert nicht auf Production + +Wenn der AI Chat auf Production nicht funktioniert, liegt es meist an fehlenden Environment-Variablen. + +## ✅ Lösung: Environment-Variablen in Gitea setzen + +### Schritt 1: Gehe zu Gitea Repository Settings + +1. Öffne: `https://git.dk0.dev/denshooter/portfolio/settings` +2. Klicke auf **"Variables"** im linken Menü + +### Schritt 2: Setze die n8n Variables + +#### Variables (öffentlich): +- **Name:** `N8N_WEBHOOK_URL` +- **Value:** `https://n8n.dk0.dev` +- **Protect:** ✅ (optional) + +- **Name:** `N8N_API_KEY` (optional, falls dein n8n eine API-Key benötigt) +- **Value:** Dein n8n API Key +- **Protect:** ✅ + +#### Secrets (verschlüsselt): +- **Name:** `N8N_SECRET_TOKEN` +- **Value:** Dein n8n Secret Token (falls du einen verwendest) +- **Protect:** ✅ + +### Schritt 3: Prüfe die n8n Webhook URL + +Stelle sicher, dass dein n8n Workflow: +1. **Aktiv** ist (Toggle oben rechts) +2. Den Webhook-Pfad `/webhook/chat` hat +3. Die vollständige URL ist: `https://n8n.dk0.dev/webhook/chat` + +### Schritt 4: Teste die Webhook-URL direkt + +```bash +curl -X POST https://n8n.dk0.dev/webhook/chat \ + -H "Content-Type: application/json" \ + -d '{"message": "Hello"}' +``` + +Wenn du einen `N8N_SECRET_TOKEN` verwendest: + +```bash +curl -X POST https://n8n.dk0.dev/webhook/chat \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer YOUR_SECRET_TOKEN" \ + -d '{"message": "Hello"}' +``` + +### Schritt 5: Deploy neu starten + +Nach dem Setzen der Variablen: +1. Push einen Commit zum `production` Branch +2. Oder manuell den Workflow in Gitea starten +3. Die Variablen werden automatisch an den Container übergeben + +## 🔍 Debugging + +### Prüfe Container-Logs + +```bash +docker logs portfolio-app | grep -i n8n +``` + +### Prüfe Environment-Variablen im Container + +```bash +docker exec portfolio-app env | grep N8N +``` + +Sollte zeigen: +``` +N8N_WEBHOOK_URL=https://n8n.dk0.dev +N8N_SECRET_TOKEN=*** (wenn gesetzt) +N8N_API_KEY=*** (wenn gesetzt) +``` + +### Prüfe Browser-Konsole + +Öffne die Browser-Konsole (F12) und schaue nach Fehlern beim Senden einer Chat-Nachricht. + +### Prüfe Server-Logs + +Die Chat-API loggt jetzt detaillierter: +- Ob `N8N_WEBHOOK_URL` gesetzt ist +- Die vollständige Webhook-URL (ohne Credentials) +- HTTP-Fehler mit Status-Codes + +## 🐛 Häufige Probleme + +### Problem 1: "N8N_WEBHOOK_URL not configured" + +**Lösung:** Variable in Gitea setzen (siehe Schritt 2) + +### Problem 2: "n8n webhook failed: 404" + +**Lösung:** +- Prüfe, ob der n8n Workflow aktiv ist +- Prüfe, ob der Webhook-Pfad `/webhook/chat` ist +- Teste die URL direkt mit curl + +### Problem 3: "n8n webhook failed: 401/403" + +**Lösung:** +- Prüfe, ob `N8N_SECRET_TOKEN` in Gitea Secrets gesetzt ist +- Prüfe, ob der Token im n8n Workflow korrekt konfiguriert ist + +### Problem 4: "Connection timeout" + +**Lösung:** +- Prüfe, ob n8n erreichbar ist: `curl https://n8n.dk0.dev` +- Prüfe Firewall-Regeln +- Prüfe, ob n8n im gleichen Netzwerk ist (Docker Network) + +## 📝 Aktuelle Konfiguration + +Die Chat-API verwendet: +- **Webhook URL:** `${N8N_WEBHOOK_URL}/webhook/chat` +- **Authentication:** + - `Authorization: Bearer ${N8N_SECRET_TOKEN}` (wenn gesetzt) + - `X-API-Key: ${N8N_API_KEY}` (wenn gesetzt) +- **Timeout:** 30 Sekunden +- **Fallback:** Wenn n8n nicht erreichbar ist, werden intelligente Fallback-Antworten verwendet + +## ✅ Checkliste + +- [ ] `N8N_WEBHOOK_URL` in Gitea Variables gesetzt +- [ ] `N8N_SECRET_TOKEN` in Gitea Secrets gesetzt (falls benötigt) +- [ ] `N8N_API_KEY` in Gitea Variables gesetzt (falls benötigt) +- [ ] n8n Workflow ist aktiv +- [ ] Webhook-Pfad ist `/webhook/chat` +- [ ] Container wurde nach dem Setzen der Variablen neu deployed +- [ ] Container-Logs zeigen keine n8n-Fehler + +## 🚀 Nach dem Setup + +Nach dem Setzen der Variablen und einem neuen Deployment sollte der Chat funktionieren. Falls nicht: + +1. Prüfe die Container-Logs: `docker logs portfolio-app` +2. Prüfe die Browser-Konsole für Client-seitige Fehler +3. Teste die n8n Webhook-URL direkt mit curl +4. Prüfe, ob die Environment-Variablen im Container gesetzt sind