Files
customer-installer/API_DOCUMENTATION.md
2026-03-01 20:12:52 +01:00

12 KiB

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:

curl -X POST 'http://192.168.45.104:3000/rpc/get_public_config' \
  -H "Content-Type: application/json" \
  -d '{}'

Response (Success):

{
  "registration_webhook_url": "https://api.botkonzept.de/webhook/botkonzept-registration",
  "api_base_url": "https://api.botkonzept.de"
}

Response (Error):

{
  "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:

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):

[
  {
    "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):

[]

Response (Error):

{
  "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:

curl -X POST 'http://192.168.45.104:3000/rpc/get_instance_config_by_ctid' \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <SERVICE_ROLE_KEY>" \
  -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:

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,
      "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):

{
  "success": true,
  "instance_id": "550e8400-e29b-41d4-a716-446655440000",
  "customer_id": "123e4567-e89b-12d3-a456-426614174000",
  "message": "Installer JSON stored successfully"
}

Response (Error):

{
  "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:

curl -X GET 'http://192.168.45.104:3000/api/instance_config' \
  -H "Authorization: Bearer <USER_JWT_TOKEN>"

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 <USER_JWT_TOKEN>

Verwendung:

  • /api/instance_config (direkter View-Zugriff)

CORS-Konfiguration

PostgREST CORS Headers

In der PostgREST-Konfiguration (docker-compose.yml):

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:

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

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

{
  "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

# Auf dem Proxmox Host
pct exec <CTID> -- 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):

# 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

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

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)

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

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:

SELECT * FROM audit_log 
WHERE action = 'api_config_access' 
ORDER BY created_at DESC 
LIMIT 10;

PostgREST Logs

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