512 lines
12 KiB
Markdown
512 lines
12 KiB
Markdown
|
|
# 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 <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:**
|
||
|
|
```bash
|
||
|
|
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):**
|
||
|
|
```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 <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):
|
||
|
|
|
||
|
|
```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 <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):
|
||
|
|
|
||
|
|
```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`
|