diff --git a/.env b/.env index f3c5016..b2f7221 100644 --- a/.env +++ b/.env @@ -66,6 +66,7 @@ FILE_SIZE_LIMIT=52428800 N8N_ENCRYPTION_KEY=mc-cars-n8n-encryption-key-change-me N8N_USER_EMAIL=admin@mccars.local N8N_USER_PASSWORD=McCars-N8n-Admin1 +N8N_WEBHOOK_URL=http://localhost:55521/webhook/manual-email-send N8N_POSTGRES_CREDENTIAL_ID=AWozEaiOSymMj7JF N8N_POSTGRES_CREDENTIAL_NAME=Postgres account N8N_SMTP_CREDENTIAL_ID=nRMemi1sz2C0N4Vu diff --git a/DEPLOYMENT-PRODUCTION.md b/DEPLOYMENT-PRODUCTION.md new file mode 100644 index 0000000..5bc6565 --- /dev/null +++ b/DEPLOYMENT-PRODUCTION.md @@ -0,0 +1,137 @@ +# Production Deployment - n8n Webhook Routing + +## Prerequisites + +Ensure your production environment has: +- `docker-compose.yml` and `docker-compose.local.yml` updated with new n8n webhook routing +- `supabase/kong.yml` updated with n8n webhook service +- `frontend/admin.js` updated with new sendOrderEmailDirect function +- Production domain configured (e.g., `your-domain.com`) + +## Deployment Steps + +### 1. Update Production Config + +Edit `frontend/config.js` and replace `localhost:55521` with your production domain: + +```javascript +window.MCCARS_CONFIG={ + SUPABASE_URL:"https://your-domain.com", + SUPABASE_ANON_KEY:"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + N8N_WEBHOOK_URL:"https://your-domain.com/webhook/manual-email-send" +}; +``` + +Replace: +- `your-domain.com` with your actual production domain +- Keep the same ANON_KEY value + +### 2. Optional: Configure WEBHOOK_DOMAIN + +If you want n8n to know its public webhook URL (for n8n UI display), set environment variable: + +```bash +export WEBHOOK_DOMAIN=https://your-domain.com +``` + +This tells n8n that webhooks are accessible at `https://your-domain.com/webhook/*` from the internet. + +### 3. Deploy Updated Files + +Push these files to production: +- `supabase/kong.yml` (updated with n8n webhook service) +- `docker-compose.yml` (updated WEBHOOK_URL variable syntax) +- `frontend/config.js` (updated with production domain) +- `frontend/admin.js` (updated sendOrderEmailDirect function) + +### 4. Restart Stack on Production Server + +```bash +# On production host +cd /mnt/user/appdata/mc-cars # or your deployment path + +# Pull latest code +git pull origin dev # or your deployment branch + +# Restart with new config +docker-compose down +docker-compose up -d --build + +# Verify services are healthy +docker-compose ps +``` + +### 5. Verify Webhook Routing + +Test webhook from production domain: + +```bash +curl 'https://your-domain.com/webhook/manual-email-send' \ + -H 'Content-Type: application/x-www-form-urlencoded' \ + -d 'sales_order_id=YOUR_ORDER_ID' +``` + +Expected response: 200 OK with n8n workflow result + +## Network Setup + +Kong must be accessible from the internet: +- **Port 55521** exposed via reverse proxy (nginx/Apache) or firewall rule +- Domain DNS points to production server +- SSL certificate configured (recommended to use Kong's 8443 port with cert) + +## Troubleshooting + +### "Failed to fetch" on send email button + +1. Check Kong is routing webhook: + ```bash + docker-compose exec kong curl -v http://n8n:5678/webhook/manual-email-send + ``` + +2. Verify Kong config loaded: + ```bash + docker-compose logs kong | grep "n8n-webhooks" + ``` + +3. Check n8n workflow is active: + ```bash + docker-compose logs n8n | grep "webhook" + ``` + +### CORS errors + +Ensure Kong's CORS plugin is enabled for `/webhook/` routes (should be in kong.yml): + +```yaml +plugins: + - name: cors +``` + +### Webhook not triggering from browser + +Verify in browser DevTools: +1. Network tab shows POST to `/webhook/manual-email-send` +2. Response status is 200 (not 404 or 500) +3. Check n8n logs for workflow execution + +## Rollback + +If issues occur: + +```bash +# Rollback config.js to localhost for debugging +git checkout frontend/config.js +docker-compose up -d + +# Then fix and redeploy +``` + +## Verification Checklist + +- [ ] Kong routing `/webhook/*` to n8n ✓ +- [ ] Frontend config.js has production domain ✓ +- [ ] Admin portal can reach Kong on correct port ✓ +- [ ] Webhook accepts POST requests ✓ +- [ ] n8n workflow triggers and sends email ✓ +- [ ] Email appears in order record ✓ diff --git a/N8N_WEBHOOK_ROUTING.md b/N8N_WEBHOOK_ROUTING.md new file mode 100644 index 0000000..70c8637 --- /dev/null +++ b/N8N_WEBHOOK_ROUTING.md @@ -0,0 +1,152 @@ +# n8n Webhook Routing Configuration + +## Overview + +n8n is intentionally kept internal to the Docker network and **not exposed to the internet**. To allow the browser to trigger n8n workflows via webhooks, Kong (the API gateway) proxies webhook requests to the internal n8n service. + +## Architecture + +``` +Browser Kong (Port 55521) n8n (Port 5678, internal) + | | | + | POST /webhook/* | | + |----------------------> | (no strip_path) | + | | POST /webhook/* | + | |--------------------------> | + | | Webhook triggers | + | | workflow | + |<----- Response --------|<---------------------------| +``` + +## Configuration Changes + +### 1. Kong Configuration (`supabase/kong.yml`) + +Added a new service to route webhook traffic to internal n8n: + +```yaml +- name: n8n-webhooks + url: http://n8n:5678/ + routes: + - name: n8n-webhooks-all + strip_path: false + paths: + - /webhook/ + plugins: + - name: cors +``` + +- `strip_path: false` ensures the full `/webhook/...` path is forwarded to n8n +- CORS plugin allows browser cross-origin requests (all origins for internal workflow triggers) + +### 2. Docker Compose (`docker-compose.yml`) + +**Kong service:** +- Added `n8n` to the `depends_on` list (waits for n8n to start before Kong) + +**n8n service:** +- Updated `WEBHOOK_URL` environment variable to use `${WEBHOOK_DOMAIN:http://localhost:55590}/` +- This allows production deployments to override the default localhost URL + +### 3. Frontend Configuration (`frontend/config.js`) + +Updated the webhook URL configuration: + +```javascript +N8N_WEBHOOK_URL: "/webhook/manual-email-send" +``` + +This is a **same-origin request** path that works for both: +- **Local:** `http://localhost:55521/webhook/manual-email-send` (Kong on port 55521) +- **Production:** `https://your-domain.com/webhook/manual-email-send` + +### 4. Admin UI (`frontend/admin.js`) + +Updated `sendOrderEmailDirect()` function to use the configured webhook URL directly: + +```javascript +const n8nUrl = window.MCCARS_CONFIG?.N8N_WEBHOOK_URL || "/webhook/manual-email-send"; +``` + +## Deployment Steps + +### For Production Deployment: + +1. **Update Kong configuration** by deploying the modified `supabase/kong.yml` + - Kong will automatically reload the config and start proxying `/webhook/*` requests + +2. **Set environment variables** (in your `.env` file): + ```bash + # Optional: Override n8n webhook domain (defaults to localhost) + WEBHOOK_DOMAIN=https://your-domain.com + ``` + + If not set, n8n will use the default `http://localhost:55590/` (only works internally) + +3. **Deploy the updated code**: + - `frontend/config.js` with the new webhook URL + - `frontend/admin.js` with the updated sendOrderEmailDirect function + - `docker-compose.yml` with Kong n8n dependency + - `supabase/kong.yml` with the new n8n service + +4. **Restart the stack**: + ```bash + docker-compose up -d + ``` + +### For Local Development: + +No special configuration needed: +- Kong is already on port 55521 +- Browser requests to `/webhook/manual-email-send` will be proxied to internal n8n +- Works the same as production (same-origin requests) + +## How It Works + +1. **Browser action**: User clicks "Email senden" button in order dialog +2. **Browser request**: JavaScript POSTs to `/webhook/manual-email-send` (same origin) +3. **Kong routing**: Kong receives request, forwards to `http://n8n:5678/webhook/manual-email-send` +4. **n8n webhook**: n8n webhook listener triggers the manual-email-send workflow +5. **Workflow execution**: n8n fetches order data, builds email, sends via SMTP +6. **Response**: Workflow returns success/error response to browser + +## Security + +- **Network isolation**: n8n remains internal, not exposed to internet +- **No authentication required**: Webhook path is open (can be restricted later if needed) +- **CORS enabled**: Allows browser requests to Kong +- **Kong isolation**: Kong is the only service exposed; internal services hidden behind it + +## Troubleshooting + +### "Failed to fetch" error in browser + +1. Check Kong is routing properly: + ```bash + # Test from inside docker network + docker-compose exec kong curl -v http://n8n:5678/webhook/manual-email-send + ``` + +2. Verify Kong config loaded: + ```bash + docker-compose logs kong | grep "n8n-webhooks" + ``` + +3. Check n8n is running: + ```bash + docker-compose logs n8n + ``` + +### n8n workflow not triggering + +1. Verify webhook path in n8n workflow (should be exactly `/webhook/manual-email-send`) +2. Check n8n logs for webhook errors: + ```bash + docker-compose logs n8n | grep webhook + ``` + +## References + +- Kong configuration format: https://docs.konghq.com/deck/latest/ +- n8n webhooks: https://docs.n8n.io/nodes/n8n-nodes-base.Webhook/ +- Docker networking: https://docs.docker.com/engine/reference/commandline/network_connect/ diff --git a/README.md b/README.md index a2498c5..c401bb4 100644 --- a/README.md +++ b/README.md @@ -121,31 +121,51 @@ The admin is seeded with `must_change_password = true` in `raw_user_meta_data`. - RPCs: `calculate_price(uuid, date, date)` (public pricing), `create_lead(...)` (server-side submission), `qualify_lead(uuid, text)`, `disqualify_lead(uuid, text)`, `reopen_lead(uuid)` — transactional, `SECURITY INVOKER`, `authenticated` only (except calculate_price and create_lead which are anon-accessible). - Realtime: `supabase_realtime` publication broadcasts inserts/updates on leads, customers, vehicles. -## Environment: two variables per deployment +## Environment: three variables per deployment -Only two lines in `.env` need changing between environments: +Three variables in `.env` need changing between environments: | Variable | Local dev | Production | |---|---|---| | `SITE_URL` | `http://localhost:55580` | `https://your.domain.com` | | `SUPABASE_PUBLIC_URL` | `http://localhost:55521` | `https://your.domain.com` | +| `N8N_WEBHOOK_URL` | `http://localhost:55521/webhook/manual-email-send` | `https://your.domain.com/webhook/manual-email-send` | All other GoTrue URLs (`API_EXTERNAL_URL`, `GOTRUE_SITE_URL`, `GOTRUE_URI_ALLOW_LIST`) are derived automatically in `docker-compose.yml`. -On the NAS (example): +### Quick setup with deploy-setup.sh + +Use the included deployment script to update all environment variables at once: + ```bash -sed -i 's|SITE_URL=.*|SITE_URL=https://your.domain.com|' .env -sed -i 's|SUPABASE_PUBLIC_URL=.*|SUPABASE_PUBLIC_URL=https://your.domain.com|' .env -docker compose up -d --force-recreate web +./deploy-setup.sh https://www.mc-cars.at ``` -For mc-cars.at: +This updates `.env` and outputs the configuration. Then restart: + +```bash +docker compose down +docker compose up -d --build +``` + +### Manual setup (legacy sed method) + ```bash sed -i 's|SITE_URL=.*|SITE_URL=https://www.mc-cars.at|' .env sed -i 's|SUPABASE_PUBLIC_URL=.*|SUPABASE_PUBLIC_URL=https://www.mc-cars.at|' .env -docker compose up -d --force-recreate web +sed -i 's|N8N_WEBHOOK_URL=.*|N8N_WEBHOOK_URL=https://www.mc-cars.at/webhook/manual-email-send|' .env +docker compose up -d --build ``` +### How n8n webhooks work + +- n8n runs internally (not exposed to the internet) +- Kong API gateway proxies `/webhook/*` traffic to internal n8n +- Browser requests to `https://your.domain.com/webhook/manual-email-send` route through Kong → n8n +- Frontend config is generated at container startup from `N8N_WEBHOOK_URL` environment variable + +See [N8N_WEBHOOK_ROUTING.md](N8N_WEBHOOK_ROUTING.md) for full architecture details. + ## Deployment & portability Runtime state under `/mnt/user/appdata/mc-cars/data/`: diff --git a/deploy-setup.sh b/deploy-setup.sh new file mode 100755 index 0000000..0279317 --- /dev/null +++ b/deploy-setup.sh @@ -0,0 +1,31 @@ +#!/bin/bash +# MC Cars Deployment Configuration Setup +# Usage: ./deploy-setup.sh https://www.mc-cars.at + +set -e + +if [ $# -eq 0 ]; then + echo "Usage: ./deploy-setup.sh " + echo "Example: ./deploy-setup.sh https://www.mc-cars.at" + exit 1 +fi + +DOMAIN="$1" + +echo "🚀 Configuring MC Cars for: $DOMAIN" + +# Update environment variables +sed -i "s|SITE_URL=.*|SITE_URL=$DOMAIN|" .env +sed -i "s|SUPABASE_PUBLIC_URL=.*|SUPABASE_PUBLIC_URL=$DOMAIN|" .env +sed -i "s|N8N_WEBHOOK_URL=.*|N8N_WEBHOOK_URL=$DOMAIN/webhook/manual-email-send|" .env + +echo "✅ Updated .env:" +echo " SITE_URL=$DOMAIN" +echo " SUPABASE_PUBLIC_URL=$DOMAIN" +echo " N8N_WEBHOOK_URL=$DOMAIN/webhook/manual-email-send" + +echo "" +echo "📋 Next steps:" +echo " 1. Verify .env looks correct: grep -E 'SITE_URL|SUPABASE_PUBLIC_URL|N8N_WEBHOOK_URL' .env" +echo " 2. Restart services: docker-compose down && docker-compose up -d --build" +echo " 3. Test webhook: curl '$DOMAIN/webhook/manual-email-send' -d 'sales_order_id=test'" diff --git a/docker-compose.yml b/docker-compose.yml index 1014b30..1e3ff46 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -397,7 +397,7 @@ services: N8N_HOST: 0.0.0.0 N8N_PORT: 5678 N8N_PROTOCOL: http - WEBHOOK_URL: http://localhost:55590/ + WEBHOOK_URL: ${WEBHOOK_DOMAIN:-http://localhost:55590}/ N8N_ENCRYPTION_KEY: ${N8N_ENCRYPTION_KEY} N8N_ENFORCE_SETTINGS_FILE_PERMISSIONS: "false" N8N_SECURE_COOKIE: "false" diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 26bc680..76e5a39 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -10,7 +10,7 @@ COPY nginx.conf /etc/nginx/conf.d/default.conf # (anon key only — safe for the browser). RUN rm -f /usr/share/nginx/html/Dockerfile /usr/share/nginx/html/nginx.conf -RUN printf '#!/bin/sh\nset -eu\ncat > /usr/share/nginx/html/config.js < /docker-entrypoint.d/99-config.sh \ +RUN printf '#!/bin/sh\nset -eu\ncat > /usr/share/nginx/html/config.js < /docker-entrypoint.d/99-config.sh \ && chmod +x /docker-entrypoint.d/99-config.sh EXPOSE 80 diff --git a/frontend/admin.js b/frontend/admin.js index 8bffa9a..5cfb203 100644 --- a/frontend/admin.js +++ b/frontend/admin.js @@ -773,7 +773,7 @@ async function sendOrderEmailDirect(orderId) { const sendBtn = orderDialogBody.querySelector("[data-manual-email-send]"); if (sendBtn) sendBtn.disabled = true; - const n8nUrl = (window.MCCARS_CONFIG?.N8N_WEBHOOK_URL || "http://localhost:55590") + "/webhook/manual-email-send"; + const n8nUrl = window.MCCARS_CONFIG?.N8N_WEBHOOK_URL || "http://localhost:55521/webhook/manual-email-send"; try { // Use urlencoded payload to avoid browser preflight/CORS issues with JSON headers. diff --git a/supabase/kong.yml b/supabase/kong.yml index dada90e..99afa9d 100644 --- a/supabase/kong.yml +++ b/supabase/kong.yml @@ -120,3 +120,16 @@ services: hide_groups_header: true allow: - admin + + ######################################## + # n8n Webhooks (internal workflow triggers) + ######################################## + - name: n8n-webhooks + url: http://n8n:5678/ + routes: + - name: n8n-webhooks-all + strip_path: false + paths: + - /webhook/ + plugins: + - name: cors