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

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`