# BotKonzept Installer JSON API Documentation ## Übersicht Diese API stellt die Installer-JSON-Daten sicher für Frontend-Clients bereit, **ohne Secrets preiszugeben**. **Basis-URL:** `http://192.168.45.104:3000` (PostgREST auf Kunden-LXC) **Zentrale API:** `https://api.botkonzept.de` (zentrales PostgREST/n8n) --- ## Sicherheitsmodell ### ✅ Erlaubte Daten (Frontend-sicher) - `ctid`, `hostname`, `fqdn`, `ip`, `vlan` - `urls.*` (alle URL-Endpunkte) - `supabase.url_external` - `supabase.anon_key` - `ollama.url`, `ollama.model`, `ollama.embedding_model` ### ❌ Verbotene Daten (Secrets) - `postgres.password` - `supabase.service_role_key` - `supabase.jwt_secret` - `n8n.owner_password` - `n8n.encryption_key` --- ## API-Endpunkte ### 1. Public Config (Keine Authentifizierung) **Zweck:** Liefert öffentliche Konfiguration für Website (Registrierungs-Webhook) **Route:** `POST /rpc/get_public_config` **Request:** ```bash curl -X POST 'http://192.168.45.104:3000/rpc/get_public_config' \ -H "Content-Type: application/json" \ -d '{}' ``` **Response (Success):** ```json { "registration_webhook_url": "https://api.botkonzept.de/webhook/botkonzept-registration", "api_base_url": "https://api.botkonzept.de" } ``` **Response (Error):** ```json { "code": "PGRST204", "message": "No rows returned", "details": null, "hint": null } ``` **CORS:** Erlaubt (öffentlich) --- ### 2. Instance Config by Email (Öffentlich, aber rate-limited) **Zweck:** Liefert Instanz-Konfiguration für einen Kunden (via E-Mail) **Route:** `POST /rpc/get_instance_config_by_email` **Request:** ```bash curl -X POST 'http://192.168.45.104:3000/rpc/get_instance_config_by_email' \ -H "Content-Type: application/json" \ -d '{"customer_email_param": "max@beispiel.de"}' ``` **Response (Success):** ```json [ { "id": "550e8400-e29b-41d4-a716-446655440000", "customer_id": "123e4567-e89b-12d3-a456-426614174000", "ctid": 769697636, "hostname": "sb-1769697636", "fqdn": "sb-1769697636.userman.de", "ip": "192.168.45.104", "vlan": 90, "status": "active", "created_at": "2025-01-15T10:30:00Z", "urls": { "n8n_internal": "http://192.168.45.104:5678/", "n8n_external": "https://sb-1769697636.userman.de", "postgrest": "http://192.168.45.104:3000", "chat_webhook": "https://sb-1769697636.userman.de/webhook/rag-chat-webhook/chat", "chat_internal": "http://192.168.45.104:5678/webhook/rag-chat-webhook/chat", "upload_form": "https://sb-1769697636.userman.de/form/rag-upload-form", "upload_form_internal": "http://192.168.45.104:5678/form/rag-upload-form" }, "supabase": { "url_external": "http://192.168.45.104:3000", "anon_key": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." }, "ollama": { "url": "http://192.168.45.3:11434", "model": "ministral-3:3b", "embedding_model": "nomic-embed-text:latest" }, "customer_email": "max@beispiel.de", "first_name": "Max", "last_name": "Mustermann", "company": "Muster GmbH", "customer_status": "trial" } ] ``` **Response (Not Found):** ```json [] ``` **Response (Error):** ```json { "code": "PGRST301", "message": "Invalid input syntax", "details": "...", "hint": null } ``` **Authentifizierung:** Keine (öffentlich, aber sollte rate-limited sein) **CORS:** Erlaubt --- ### 3. Instance Config by CTID (Service Role Only) **Zweck:** Liefert Instanz-Konfiguration für interne Workflows (via CTID) **Route:** `POST /rpc/get_instance_config_by_ctid` **Request:** ```bash curl -X POST 'http://192.168.45.104:3000/rpc/get_instance_config_by_ctid' \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{"ctid_param": 769697636}' ``` **Response:** Gleiche Struktur wie `/get_instance_config_by_email` **Authentifizierung:** Service Role Key erforderlich **CORS:** Nicht erlaubt (nur Backend-to-Backend) --- ### 4. Store Installer JSON (Service Role Only) **Zweck:** Speichert Installer-JSON nach Instanz-Erstellung (wird von install.sh aufgerufen) **Route:** `POST /rpc/store_installer_json` **Request:** ```bash curl -X POST 'http://192.168.45.104:3000/rpc/store_installer_json' \ -H "Content-Type: application/json" \ -H "Authorization: Bearer " \ -d '{ "customer_email_param": "max@beispiel.de", "lxc_id_param": 769697636, "installer_json_param": { "ctid": 769697636, "hostname": "sb-1769697636", "fqdn": "sb-1769697636.userman.de", "ip": "192.168.45.104", "vlan": 90, "urls": { "n8n_internal": "http://192.168.45.104:5678/", "n8n_external": "https://sb-1769697636.userman.de", "postgrest": "http://192.168.45.104:3000", "chat_webhook": "https://sb-1769697636.userman.de/webhook/rag-chat-webhook/chat", "chat_internal": "http://192.168.45.104:5678/webhook/rag-chat-webhook/chat", "upload_form": "https://sb-1769697636.userman.de/form/rag-upload-form", "upload_form_internal": "http://192.168.45.104:5678/form/rag-upload-form" }, "postgres": { "host": "postgres", "port": 5432, "db": "customer", "user": "customer", "password": "REDACTED" }, "supabase": { "url": "http://postgrest:3000", "url_external": "http://192.168.45.104:3000", "anon_key": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "service_role_key": "REDACTED", "jwt_secret": "REDACTED" }, "ollama": { "url": "http://192.168.45.3:11434", "model": "ministral-3:3b", "embedding_model": "nomic-embed-text:latest" }, "n8n": { "encryption_key": "REDACTED", "owner_email": "admin@userman.de", "owner_password": "REDACTED", "secure_cookie": false } } }' ``` **Response (Success):** ```json { "success": true, "instance_id": "550e8400-e29b-41d4-a716-446655440000", "customer_id": "123e4567-e89b-12d3-a456-426614174000", "message": "Installer JSON stored successfully" } ``` **Response (Error):** ```json { "success": false, "error": "Instance not found for customer email and LXC ID" } ``` **Authentifizierung:** Service Role Key erforderlich **CORS:** Nicht erlaubt (nur Backend-to-Backend) --- ### 5. Direct View Access (Authenticated) **Zweck:** Direkter Zugriff auf View (für authentifizierte Benutzer) **Route:** `GET /api/instance_config` **Request:** ```bash curl -X GET 'http://192.168.45.104:3000/api/instance_config' \ -H "Authorization: Bearer " ``` **Response:** Array von Instanz-Konfigurationen (gefiltert nach RLS) **Authentifizierung:** JWT Token erforderlich (Supabase Auth) **CORS:** Erlaubt --- ## Authentifizierung ### 1. Keine Authentifizierung (Public) - `/rpc/get_public_config` - `/rpc/get_instance_config_by_email` (sollte rate-limited sein) ### 2. Service Role Key **Header:** ``` Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoic2VydmljZV9yb2xlIiwiaXNzIjoic3VwYWJhc2UiLCJpYXQiOjE3MDAwMDAwMDAsImV4cCI6MjAwMDAwMDAwMH0... ``` **Verwendung:** - `/rpc/get_instance_config_by_ctid` - `/rpc/store_installer_json` ### 3. User JWT Token (Supabase Auth) **Header:** ``` Authorization: Bearer ``` **Verwendung:** - `/api/instance_config` (direkter View-Zugriff) --- ## CORS-Konfiguration ### PostgREST CORS Headers In der PostgREST-Konfiguration (docker-compose.yml): ```yaml postgrest: environment: PGRST_SERVER_CORS_ALLOWED_ORIGINS: "*" # Oder spezifisch: # PGRST_SERVER_CORS_ALLOWED_ORIGINS: "https://botkonzept.de,https://www.botkonzept.de" ``` ### Nginx Reverse Proxy CORS Falls über Nginx: ```nginx add_header 'Access-Control-Allow-Origin' '*'; add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; add_header 'Access-Control-Allow-Headers' 'Content-Type, Authorization'; ``` --- ## Rate Limiting **Empfehlung:** Rate Limiting für öffentliche Endpunkte implementieren ### Nginx Rate Limiting ```nginx limit_req_zone $binary_remote_addr zone=api_limit:10m rate=10r/s; location /rpc/get_instance_config_by_email { limit_req zone=api_limit burst=20 nodelay; proxy_pass http://postgrest:3000; } ``` ### PostgREST Rate Limiting Alternativ: Verwende einen API Gateway (Kong, Tyk) vor PostgREST. --- ## Fehlerbehandlung ### HTTP Status Codes - `200 OK` - Erfolgreiche Anfrage - `204 No Content` - Keine Daten gefunden (PostgREST) - `400 Bad Request` - Ungültige Eingabe - `401 Unauthorized` - Fehlende/ungültige Authentifizierung - `403 Forbidden` - Keine Berechtigung - `404 Not Found` - Ressource nicht gefunden - `500 Internal Server Error` - Serverfehler ### PostgREST Error Format ```json { "code": "PGRST301", "message": "Invalid input syntax for type integer", "details": "invalid input syntax for type integer: \"abc\"", "hint": null } ``` --- ## Integration mit install.sh ### Schritt 1: SQL-Schema anwenden ```bash # Auf dem Proxmox Host pct exec -- bash -c " docker exec customer-postgres psql -U customer -d customer < /opt/customer-stack/sql/add_installer_json_api.sql " ``` ### Schritt 2: install.sh erweitern Am Ende von `install.sh` (nach JSON-Generierung): ```bash # Store installer JSON in database via PostgREST info "Storing installer JSON in database..." STORE_RESPONSE=$(curl -sS -X POST "http://${CT_IP}:${POSTGREST_PORT}/rpc/store_installer_json" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${SERVICE_ROLE_KEY}" \ -d "{ \"customer_email_param\": \"${N8N_OWNER_EMAIL}\", \"lxc_id_param\": ${CTID}, \"installer_json_param\": ${JSON_OUTPUT} }" 2>&1) if echo "$STORE_RESPONSE" | grep -q '"success":true'; then info "Installer JSON stored successfully" else warn "Failed to store installer JSON: ${STORE_RESPONSE}" fi ``` --- ## Testing ### Test 1: Public Config ```bash curl -X POST 'http://192.168.45.104:3000/rpc/get_public_config' \ -H "Content-Type: application/json" \ -d '{}' # Erwartete Antwort: # {"registration_webhook_url":"https://api.botkonzept.de/webhook/botkonzept-registration","api_base_url":"https://api.botkonzept.de"} ``` ### Test 2: Instance Config by Email ```bash curl -X POST 'http://192.168.45.104:3000/rpc/get_instance_config_by_email' \ -H "Content-Type: application/json" \ -d '{"customer_email_param": "max@beispiel.de"}' # Erwartete Antwort: Array mit Instanz-Konfiguration (siehe oben) ``` ### Test 3: Store Installer JSON (mit Service Role Key) ```bash SERVICE_ROLE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." curl -X POST 'http://192.168.45.104:3000/rpc/store_installer_json' \ -H "Content-Type: application/json" \ -H "Authorization: Bearer ${SERVICE_ROLE_KEY}" \ -d '{ "customer_email_param": "max@beispiel.de", "lxc_id_param": 769697636, "installer_json_param": {"ctid": 769697636, "urls": {...}} }' # Erwartete Antwort: # {"success":true,"instance_id":"...","customer_id":"...","message":"Installer JSON stored successfully"} ``` ### Test 4: Verify No Secrets Exposed ```bash curl -X POST 'http://192.168.45.104:3000/rpc/get_instance_config_by_email' \ -H "Content-Type: application/json" \ -d '{"customer_email_param": "max@beispiel.de"}' | jq . # Prüfe: Response enthält KEINE der folgenden Felder: # - postgres.password # - supabase.service_role_key # - supabase.jwt_secret # - n8n.owner_password # - n8n.encryption_key ``` --- ## Deployment Checklist - [ ] SQL-Schema auf allen Instanzen anwenden - [ ] PostgREST CORS konfigurieren - [ ] Rate Limiting aktivieren - [ ] install.sh erweitern (Installer JSON speichern) - [ ] Frontend auf neue API umstellen - [ ] Tests durchführen - [ ] Monitoring einrichten (API-Zugriffe loggen) --- ## Monitoring & Logging ### Audit Log Alle API-Zugriffe werden in `audit_log` Tabelle protokolliert: ```sql SELECT * FROM audit_log WHERE action = 'api_config_access' ORDER BY created_at DESC LIMIT 10; ``` ### PostgREST Logs ```bash docker logs customer-postgrest --tail 100 -f ``` --- ## Sicherheitshinweise 1. **Service Role Key schützen:** Niemals im Frontend verwenden! 2. **Rate Limiting:** Öffentliche Endpunkte müssen rate-limited sein 3. **HTTPS:** In Produktion nur über HTTPS (OPNsense Reverse Proxy) 4. **Input Validation:** PostgREST validiert automatisch, aber zusätzliche Checks empfohlen 5. **Audit Logging:** Alle API-Zugriffe werden geloggt --- ## Support Bei Fragen oder Problemen: - Dokumentation: `customer-installer/wiki/` - Troubleshooting: `customer-installer/REGISTRATION_TROUBLESHOOTING.md`