- Umfassende Analyse der aktuellen Implementierung durchgeführt - Identifiziert: Zwei getrennte User-Systeme (users vs gateway_users) - Problem: API verwendet falsche Tabelle (users statt gateway_users) - Lösung: Kompletter Implementierungsplan für korrekte Architektur - Dokument: ARCHITEKTUR.md mit 6-Tage-Umsetzungsplan erstellt - Enthält: Custom API-Key Guard, Gateway-User-Credentials, Budget-System - Swagger/Scramble Paket hinzugefügt (für spätere API-Dokumentation) Status: Bereit für Implementierung (Start: Tag 1 - Datenbank & Models)
32 KiB
Laravel LLM Gateway - Korrekte Architektur-Umsetzung
Datum: 2025-11-18
Status: 🎯 Klarstellung der Architektur-Vision
🏗️ Architektur-Konzept (KORREKT)
Zwei getrennte User-Typen:
1. Admins (users Tabelle)
- Zweck: Verwaltung des Gateway-Systems
- Zugriff: Laravel Web-Interface (Session-basiert)
- Funktionen:
- Gateway-Users verwalten
- Budgets konfigurieren
- Usage-Logs einsehen
- Model-Pricing pflegen
- Provider-Credentials für Gateway-Users konfigurieren
2. Gateway-Users (gateway_users Tabelle)
- Zweck: API-Clients (externe Anwendungen)
- Zugriff: REST API via API-Keys
- Funktionen:
- Chat-Completions anfragen
- Antworten von LLM-Providern erhalten
- Innerhalb ihres Budgets arbeiten
Das ist eine saubere Trennung - so sollte es sein! ✅
🔴 Aktuelles Problem
Die API-Implementation verwendet die falschen Tabellen:
Was der Code aktuell macht:
// ChatCompletionController.php
Route::middleware('auth:sanctum')->group(function () {
Route::post('/chat/completions', ...);
});
// Verwendet:
$user = $request->user(); // ← Aus 'users' Tabelle (Admins!)
$credential = UserProviderCredential::where('user_id', $user->id)->first();
Was der Code machen sollte:
// API-Key Authentication für gateway_users
Route::middleware('auth:api')->group(function () {
Route::post('/chat/completions', ...);
});
// Sollte verwenden:
$gatewayUser = $request->user(); // ← Aus 'gateway_users' Tabelle
$credential = GatewayUserCredential::where('gateway_user_id', $gatewayUser->id)->first();
📋 Umsetzungsplan
Phase 1: Datenbank-Struktur anpassen
1.1 Neue Migration: Provider-Credentials für Gateway-Users
Aktuell: user_provider_credentials hat Foreign Key zu users
Neu: Credential-Tabelle für gateway_users erstellen
// Migration: create_gateway_user_credentials_table.php
Schema::create('gateway_user_credentials', function (Blueprint $table) {
$table->id();
$table->string('gateway_user_id'); // ← Foreign Key zu gateway_users
$table->string('provider'); // openai, anthropic, google, deepseek, mistral
$table->text('api_key'); // Verschlüsselt
$table->string('organization_id')->nullable();
$table->boolean('is_active')->default(true);
$table->timestamp('last_used_at')->nullable();
$table->timestamp('last_tested_at')->nullable();
$table->string('test_status')->nullable(); // success, failed
$table->text('test_error')->nullable();
$table->timestamps();
$table->foreign('gateway_user_id')
->references('user_id')
->on('gateway_users')
->onDelete('cascade');
$table->unique(['gateway_user_id', 'provider']);
$table->index('is_active');
});
1.2 Budget-Tabelle vereinheitlichen
Entscheidung: Welche Tabelle behalten?
Option A: budgets Tabelle behalten (aktuell für gateway_users)
-- budgets Tabelle hat bereits:
- id, name, monthly_limit, current_spending, alert_threshold
- Keine direkte User-Zuordnung (wird über gateway_users.budget_id verknüpft)
Option B: Direkte Budget-Felder in gateway_users
ALTER TABLE gateway_users ADD COLUMN monthly_budget_limit DECIMAL(10,2);
ALTER TABLE gateway_users ADD COLUMN current_month_spending DECIMAL(10,2);
ALTER TABLE gateway_users ADD COLUMN budget_alert_threshold INT;
Empfehlung: Option B - Vereinfachung
- Direkter Zugriff auf Budget pro User
- Weniger Joins
- Einfacheres Datenmodell
1.3 Usage-Logging konsolidieren
Aktuell: Zwei Tabellen:
llm_requests(von API-Code genutzt)usage_logs(im Admin-Interface angezeigt)
Neu: Nur usage_logs verwenden, aber anpassen:
Schema::table('usage_logs', function (Blueprint $table) {
// Stelle sicher dass alle Felder vorhanden sind:
// user_id → gateway_user_id umbenennen
$table->renameColumn('user_id', 'gateway_user_id');
// Falls fehlt:
if (!Schema::hasColumn('usage_logs', 'request_payload')) {
$table->json('request_payload')->nullable();
}
if (!Schema::hasColumn('usage_logs', 'response_payload')) {
$table->json('response_payload')->nullable();
}
if (!Schema::hasColumn('usage_logs', 'ip_address')) {
$table->string('ip_address')->nullable();
}
if (!Schema::hasColumn('usage_logs', 'user_agent')) {
$table->string('user_agent')->nullable();
}
});
Phase 2: API-Key Authentication System
2.1 Custom Guard für API-Keys
Laravel kann mit Custom Guards arbeiten. Wir erstellen einen api-key Guard:
config/auth.php:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users', // ← Für Admins
],
'api' => [
'driver' => 'api-key', // ← Custom Guard
'provider' => 'gateway_users',
],
],
'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\User::class,
],
'gateway_users' => [
'driver' => 'eloquent',
'model' => App\Models\GatewayUser::class,
],
],
2.2 API-Key Guard Implementation
Neue Datei: app/Auth/ApiKeyGuard.php
<?php
namespace App\Auth;
use App\Models\ApiKey;
use App\Models\GatewayUser;
use Illuminate\Auth\GuardHelpers;
use Illuminate\Contracts\Auth\Guard;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Hash;
class ApiKeyGuard implements Guard
{
use GuardHelpers;
protected $request;
protected $provider;
public function __construct($provider, Request $request)
{
$this->provider = $provider;
$this->request = $request;
}
public function user()
{
if ($this->user !== null) {
return $this->user;
}
// Get API key from header: Authorization: Bearer sk-xxx
$apiKey = $this->request->bearerToken();
if (!$apiKey) {
return null;
}
// Find API key in database
$keyRecord = ApiKey::where('key_prefix', substr($apiKey, 0, 10))
->where('is_active', true)
->first();
if (!$keyRecord) {
return null;
}
// Verify full key hash
if (!Hash::check($apiKey, $keyRecord->key_hash)) {
return null;
}
// Update last used timestamp
$keyRecord->update(['last_used_at' => now()]);
// Return the gateway user
$this->user = GatewayUser::find($keyRecord->gateway_user_id);
return $this->user;
}
public function validate(array $credentials = [])
{
return $this->user() !== null;
}
}
2.3 Service Provider registrieren
app/Providers/AuthServiceProvider.php:
use App\Auth\ApiKeyGuard;
use Illuminate\Support\Facades\Auth;
public function boot(): void
{
Auth::extend('api-key', function ($app, $name, array $config) {
return new ApiKeyGuard(
Auth::createUserProvider($config['provider']),
$app['request']
);
});
}
Phase 3: API-Code anpassen
3.1 Routes aktualisieren
routes/api.php:
use App\Http\Controllers\Api\ChatCompletionController;
// Gateway API - verwendet 'api' guard (API-Keys für gateway_users)
Route::middleware('auth:api')->group(function () {
Route::post('/chat/completions', [ChatCompletionController::class, 'create'])
->middleware(['checkbudget', 'checkratelimit']);
Route::get('/user', function (Request $request) {
return $request->user(); // Gibt GatewayUser zurück
});
});
3.2 GatewayUser Model erweitern
app/Models/GatewayUser.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Auth\Authenticatable as AuthenticatableTrait;
class GatewayUser extends Model implements Authenticatable
{
use AuthenticatableTrait;
protected $primaryKey = 'user_id';
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'user_id',
'alias',
'monthly_budget_limit',
'current_month_spending',
'budget_alert_threshold',
'rate_limit_per_hour',
'blocked',
'metadata',
];
protected $casts = [
'blocked' => 'boolean',
'metadata' => 'json',
'monthly_budget_limit' => 'decimal:2',
'current_month_spending' => 'decimal:2',
];
// Relations
public function apiKeys()
{
return $this->hasMany(ApiKey::class, 'gateway_user_id', 'user_id');
}
public function credentials()
{
return $this->hasMany(GatewayUserCredential::class, 'gateway_user_id', 'user_id');
}
public function usageLogs()
{
return $this->hasMany(UsageLog::class, 'gateway_user_id', 'user_id');
}
// Helper methods
public function isBlocked(): bool
{
return $this->blocked;
}
public function hasExceededBudget(): bool
{
if (!$this->monthly_budget_limit) {
return false;
}
return $this->current_month_spending >= $this->monthly_budget_limit;
}
public function incrementSpending(float $amount): void
{
$this->increment('current_month_spending', $amount);
}
public function resetMonthlySpending(): void
{
$this->update(['current_month_spending' => 0]);
}
}
3.3 Neues Model: GatewayUserCredential
app/Models/GatewayUserCredential.php:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Crypt;
class GatewayUserCredential extends Model
{
protected $fillable = [
'gateway_user_id',
'provider',
'api_key',
'organization_id',
'is_active',
'last_used_at',
'last_tested_at',
'test_status',
'test_error',
];
protected $hidden = ['api_key'];
protected $casts = [
'is_active' => 'boolean',
'last_used_at' => 'datetime',
'last_tested_at' => 'datetime',
];
// Automatic encryption
public function setApiKeyAttribute($value): void
{
$this->attributes['api_key'] = Crypt::encryptString($value);
}
public function getApiKeyAttribute($value): string
{
return Crypt::decryptString($value);
}
public function gatewayUser()
{
return $this->belongsTo(GatewayUser::class, 'gateway_user_id', 'user_id');
}
}
3.4 GatewayService anpassen
app/Services/LLM/GatewayService.php - Änderungen:
<?php
namespace App\Services\LLM;
use App\Models\GatewayUser; // ← Statt User
use App\Models\GatewayUserCredential; // ← Neu
use App\Exceptions\{ProviderException, InsufficientBudgetException};
class GatewayService
{
public function chatCompletion(
GatewayUser $user, // ← Typ geändert
string $provider,
string $model,
array $messages,
array $options = [],
?string $ipAddress = null,
?string $userAgent = null
): array {
// ... (Rest bleibt gleich, nur getUserCredential anpassen)
}
private function getUserCredential(GatewayUser $user, string $provider): GatewayUserCredential
{
$credential = GatewayUserCredential::where('gateway_user_id', $user->user_id)
->where('provider', $provider)
->where('is_active', true)
->first();
if (!$credential) {
throw new ProviderException(
"No active API credentials found for provider: {$provider}",
400
);
}
$credential->update(['last_used_at' => now()]);
return $credential;
}
private function updateUserBudget(GatewayUser $user, float $cost): void
{
// Budget direkt in gateway_users Tabelle
$user->incrementSpending($cost);
// Check if budget exceeded
if ($user->hasExceededBudget()) {
// TODO: Dispatch alert notification
}
}
}
3.5 RequestLogger anpassen
app/Services/LLM/RequestLogger.php:
use App\Models\UsageLog; // ← Statt LlmRequest
public function logSuccess(
string $gatewayUserId, // ← Statt int $userId
string $provider,
string $model,
array $requestPayload,
array $response,
array $costs,
int $responseTimeMs,
?string $ipAddress = null,
?string $userAgent = null
): int {
$log = UsageLog::create([
'gateway_user_id' => $gatewayUserId, // ← Geändert
'provider' => $provider,
'model' => $model,
'prompt_tokens' => $response['usage']['prompt_tokens'],
'completion_tokens' => $response['usage']['completion_tokens'],
'total_tokens' => $response['usage']['total_tokens'],
'cost' => $costs['total_cost'],
'request_payload' => $requestPayload,
'response_payload' => $response,
'response_time_ms' => $responseTimeMs,
'ip_address' => $ipAddress,
'user_agent' => $userAgent,
'status' => 'success',
]);
return $log->id;
}
3.6 Middleware anpassen
app/Http/Middleware/CheckBudget.php:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class CheckBudget
{
public function handle(Request $request, Closure $next): Response
{
$user = $request->user(); // GatewayUser
if ($user && $user->hasExceededBudget()) {
return response()->json([
'error' => [
'message' => 'Budget exceeded. Please contact your administrator.',
'type' => 'budget_exceeded',
'code' => 429,
'budget_limit' => $user->monthly_budget_limit,
'current_spending' => $user->current_month_spending,
]
], 429);
}
if ($user && $user->isBlocked()) {
return response()->json([
'error' => [
'message' => 'User is blocked. Please contact your administrator.',
'type' => 'user_blocked',
'code' => 403,
]
], 403);
}
return $next($request);
}
}
app/Http/Middleware/CheckRateLimit.php:
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Cache;
use Symfony\Component\HttpFoundation\Response;
class CheckRateLimit
{
public function handle(Request $request, Closure $next): Response
{
$user = $request->user(); // GatewayUser
if (!$user || !$user->rate_limit_per_hour) {
return $next($request);
}
$key = 'rate_limit:' . $user->user_id;
$requests = Cache::get($key, 0);
if ($requests >= $user->rate_limit_per_hour) {
$ttl = Cache::get($key . ':ttl', 3600);
return response()->json([
'error' => [
'message' => 'Rate limit exceeded. Please try again later.',
'type' => 'rate_limit_exceeded',
'code' => 429,
'limit' => $user->rate_limit_per_hour,
'current' => $requests,
'retry_after' => $ttl,
]
], 429);
}
// Increment counter
Cache::put($key, $requests + 1, 3600);
if ($requests == 0) {
Cache::put($key . ':ttl', 3600, 3600);
}
return $next($request);
}
}
Phase 4: Admin-Interface für Gateway-User Credentials
4.1 Neuer Controller: GatewayUserCredentialController
app/Http/Controllers/Admin/GatewayUserCredentialController.php:
<?php
namespace App\Http\Controllers\Admin;
use App\Http\Controllers\Controller;
use App\Models\GatewayUser;
use App\Models\GatewayUserCredential;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class GatewayUserCredentialController extends Controller
{
public function index(Request $request, $gatewayUserId)
{
$user = GatewayUser::findOrFail($gatewayUserId);
$credentials = $user->credentials()->get();
return view('admin.gateway-user-credentials.index', compact('user', 'credentials'));
}
public function create($gatewayUserId)
{
$user = GatewayUser::findOrFail($gatewayUserId);
$providers = ['openai', 'anthropic', 'google', 'deepseek', 'mistral'];
return view('admin.gateway-user-credentials.create', compact('user', 'providers'));
}
public function store(Request $request, $gatewayUserId)
{
$validated = $request->validate([
'provider' => 'required|in:openai,anthropic,google,deepseek,mistral',
'api_key' => 'required|string',
'organization_id' => 'nullable|string',
]);
GatewayUserCredential::create([
'gateway_user_id' => $gatewayUserId,
'provider' => $validated['provider'],
'api_key' => $validated['api_key'],
'organization_id' => $validated['organization_id'] ?? null,
'is_active' => true,
]);
return redirect()
->route('admin.gateway-users.credentials.index', $gatewayUserId)
->with('success', 'Credential added successfully');
}
// Test, Toggle, Delete methods...
}
4.2 Routes erweitern
routes/web.php - Hinzufügen:
Route::middleware(['auth', 'verified'])->group(function () {
// ... existierende Routes ...
// Gateway User Credentials Management
Route::prefix('admin/gateway-users/{gatewayUser}')->name('admin.gateway-users.')->group(function () {
Route::get('credentials', [GatewayUserCredentialController::class, 'index'])
->name('credentials.index');
Route::get('credentials/create', [GatewayUserCredentialController::class, 'create'])
->name('credentials.create');
Route::post('credentials', [GatewayUserCredentialController::class, 'store'])
->name('credentials.store');
Route::post('credentials/{credential}/test', [GatewayUserCredentialController::class, 'test'])
->name('credentials.test');
Route::post('credentials/{credential}/toggle', [GatewayUserCredentialController::class, 'toggle'])
->name('credentials.toggle');
Route::delete('credentials/{credential}', [GatewayUserCredentialController::class, 'destroy'])
->name('credentials.destroy');
});
});
🎯 Migrations-Reihenfolge
Migration 1: Budget-Felder zu gateway_users hinzufügen
php artisan make:migration add_budget_fields_to_gateway_users
Schema::table('gateway_users', function (Blueprint $table) {
$table->decimal('monthly_budget_limit', 10, 2)->nullable()->after('alias');
$table->decimal('current_month_spending', 10, 2)->default(0)->after('monthly_budget_limit');
$table->integer('budget_alert_threshold')->nullable()->after('current_month_spending');
$table->integer('rate_limit_per_hour')->default(60)->after('budget_alert_threshold');
$table->dropColumn('spend'); // Alte Spalte entfernen
$table->dropColumn('budget_id'); // Nicht mehr gebraucht
});
Migration 2: Gateway User Credentials erstellen
php artisan make:migration create_gateway_user_credentials_table
(Wie oben bereits dokumentiert)
Migration 3: Usage Logs anpassen
php artisan make:migration update_usage_logs_for_gateway_users
Schema::table('usage_logs', function (Blueprint $table) {
// Spalte umbenennen
$table->renameColumn('user_id', 'gateway_user_id');
// Neue Spalten hinzufügen falls nicht vorhanden
if (!Schema::hasColumn('usage_logs', 'request_payload')) {
$table->json('request_payload')->nullable();
}
if (!Schema::hasColumn('usage_logs', 'response_payload')) {
$table->json('response_payload')->nullable();
}
if (!Schema::hasColumn('usage_logs', 'response_time_ms')) {
$table->integer('response_time_ms')->nullable();
}
if (!Schema::hasColumn('usage_logs', 'ip_address')) {
$table->string('ip_address', 45)->nullable();
}
if (!Schema::hasColumn('usage_logs', 'user_agent')) {
$table->string('user_agent')->nullable();
}
});
Migration 4: Alte Tabellen optional behalten/entfernen
php artisan make:migration cleanup_old_tables
// Optional: Diese Tabellen könnten gelöscht werden wenn nicht mehr gebraucht:
// - user_provider_credentials (alte Struktur)
// - user_budgets (für users, nicht gateway_users)
// - llm_requests (wenn usage_logs konsolidiert ist)
// - budgets (wenn Budget jetzt in gateway_users ist)
// Oder umbenennen für Archiv:
Schema::rename('user_provider_credentials', 'old_user_provider_credentials');
Schema::rename('user_budgets', 'old_user_budgets');
Schema::rename('llm_requests', 'old_llm_requests');
Schema::rename('budgets', 'old_budgets');
⏱️ Implementierungs-Zeitplan
Tag 1-2: Datenbank & Models
- ✅ Migration 1: Budget-Felder zu gateway_users
- ✅ Migration 2: Gateway User Credentials Tabelle
- ✅ Migration 3: Usage Logs anpassen
- ✅ GatewayUser Model erweitern
- ✅ GatewayUserCredential Model erstellen
- ✅ ApiKey Model anpassen (falls nötig)
Tag 3: Authentication System
- ✅ ApiKeyGuard implementieren
- ✅ AuthServiceProvider erweitern
- ✅ config/auth.php anpassen
- ✅ Middleware anpassen (CheckBudget, CheckRateLimit)
- ✅ Testing: API-Key Authentication
Tag 4: API Services anpassen
- ✅ GatewayService umbauen
- ✅ RequestLogger anpassen
- ✅ routes/api.php anpassen
- ✅ ChatCompletionController anpassen
- ✅ Testing: Chat Completions Endpoint
Tag 5: Admin-Interface
- ✅ GatewayUserCredentialController erstellen
- ✅ Views für Credential-Management
- ✅ Routes hinzufügen
- ✅ GatewayUserController anpassen (Budget-Felder)
- ✅ Testing: Admin-Interface
Tag 6: Testing & Cleanup
- ✅ Integration Tests schreiben
- ✅ Ende-zu-Ende Testing
- ✅ Alte Tabellen archivieren/entfernen
- ✅ Dokumentation finalisieren
Geschätzter Aufwand: 6 Arbeitstage
📝 Checkliste
Datenbank
- Migration: Budget-Felder zu gateway_users
- Migration: Gateway User Credentials Tabelle erstellen
- Migration: Usage Logs anpassen
- Migration: Alte Tabellen archivieren
- php artisan migrate
Models
- GatewayUser Model erweitern (Authenticatable)
- GatewayUserCredential Model erstellen
- UsageLog Model anpassen (gateway_user_id)
Authentication
- ApiKeyGuard implementieren
- AuthServiceProvider registrieren
- config/auth.php anpassen
API
- routes/api.php: auth:api statt auth:sanctum
- GatewayService: GatewayUser statt User
- GatewayService: GatewayUserCredential verwenden
- RequestLogger: gateway_user_id verwenden
- Middleware: CheckBudget anpassen
- Middleware: CheckRateLimit anpassen
Admin-Interface
- GatewayUserCredentialController erstellen
- Routes für Credential-Management hinzufügen
- Views erstellen für Credential-Management
- GatewayUserController: Budget-Felder anpassen
Testing
- API-Key Authentication testen
- Chat Completions Endpoint testen
- Budget-System testen
- Rate-Limiting testen
- Admin-Interface testen
📊 Finale Architektur (Übersicht)
User-Typen & Zugriff
┌─────────────────────────────────────────────────────────────┐
│ Laravel LLM Gateway │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Admin Users │ │ Gateway Users │ │
│ │ (users table) │ │ (gateway_users) │ │
│ ├──────────────────┤ ├──────────────────┤ │
│ │ • Web-Interface │ │ • API-Zugriff │ │
│ │ • Session Auth │ │ • API-Key Auth │ │
│ │ • Management │ │ • Chat Requests │ │
│ └──────────────────┘ └──────────────────┘ │
│ │ │ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────┐ ┌──────────────────┐ │
│ │ Web-Interface │ │ REST API │ │
│ │ /dashboard │ │ /api/chat/... │ │
│ │ /gateway-users │ │ │ │
│ │ /budgets │ │ Auth: API-Key │ │
│ │ /usage-logs │ │ Guard: 'api' │ │
│ └──────────────────┘ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Datenfluss: API-Request
1. Externe App sendet Request:
POST /api/chat/completions
Authorization: Bearer sk-gw-abc123xyz...
2. ApiKeyGuard validiert API-Key
→ Findet GatewayUser in gateway_users
3. Middleware: CheckBudget
→ Prüft gateway_user.monthly_budget_limit
→ Prüft gateway_user.current_month_spending
→ Prüft gateway_user.blocked
4. Middleware: CheckRateLimit
→ Prüft gateway_user.rate_limit_per_hour
5. ChatCompletionController
→ Empfängt GatewayUser
6. GatewayService
→ Holt GatewayUserCredential (Provider API-Key)
→ Ruft LLM-Provider auf
→ Berechnet Kosten
7. RequestLogger
→ Speichert in usage_logs (gateway_user_id)
8. Budget Update
→ Erhöht gateway_user.current_month_spending
9. Response zurück an externe App
🔧 Verwendungs-Beispiele
Admin erstellt Gateway-User
Im Admin-Interface:
1. Login als Admin (users Tabelle)
2. Navigate zu: /gateway-users/create
3. Erstelle Gateway-User:
- User ID: "app-customer-123"
- Alias: "Customer App"
- Budget: 100.00 EUR
- Rate Limit: 60 requests/hour
4. Gateway-User wird in gateway_users gespeichert
Admin konfiguriert Provider-Credentials für Gateway-User
Im Admin-Interface:
1. Navigate zu: /admin/gateway-users/app-customer-123/credentials
2. Klick auf "Add Credential"
3. Wähle Provider: "OpenAI"
4. API-Key: "sk-proj-..."
5. Speichern
6. GatewayUserCredential wird erstellt (verschlüsselt)
Admin generiert API-Key für Gateway-User
Im Admin-Interface:
1. Navigate zu: /api-keys
2. Klick auf "Create API Key"
3. Wähle Gateway-User: "app-customer-123"
4. Name: "Production Key"
5. System generiert:
- Full Key: "sk-gw-1234567890abcdef..."
- Prefix: "sk-gw-1234"
- Hash wird gespeichert
6. WICHTIG: Zeige Full Key nur EINMAL an!
Externe App verwendet die API
Request:
curl -X POST http://localhost/api/chat/completions \
-H "Authorization: Bearer sk-gw-1234567890abcdef..." \
-H "Content-Type: application/json" \
-d '{
"provider": "openai",
"model": "gpt-4",
"messages": [
{"role": "user", "content": "Hello, how are you?"}
]
}'
Response:
{
"success": true,
"request_id": 12345,
"provider": "openai",
"model": "gpt-4",
"content": "I'm doing well, thank you for asking! ...",
"role": "assistant",
"finish_reason": "stop",
"usage": {
"prompt_tokens": 15,
"completion_tokens": 20,
"total_tokens": 35
},
"cost": {
"prompt_cost": 0.00045,
"completion_cost": 0.0012,
"total_cost": 0.00165
},
"response_time_ms": 1234
}
✅ Vorteile dieser Architektur
-
Klare Trennung
- Admins (users) vs. API-Clients (gateway_users)
- Verschiedene Auth-Mechanismen (Session vs. API-Key)
- Verschiedene Use-Cases
-
Sicherheit
- API-Keys sind verschlüsselt und gehashed
- Provider-Credentials sind verschlüsselt
- Budget-Limits pro Gateway-User
- Rate-Limiting pro Gateway-User
-
Skalierbarkeit
- Jeder Gateway-User kann eigene Provider-Credentials haben
- Oder zentrale Admin-Credentials nutzen
- Flexible Budget-Verwaltung
-
Management
- Admins haben volle Kontrolle
- Können Gateway-Users blockieren
- Können Budgets anpassen
- Sehen alle Usage-Logs
-
Standard-Laravel
- Nutzt Laravel Guards (Standard-Mechanismus)
- Nutzt Eloquent Models
- Nutzt Laravel Migrations
- Gut dokumentiert und wartbar
⚠️ Potenzielle Herausforderungen
-
Custom Guard
- Etwas mehr Code als Sanctum
- Braucht sorgfältiges Testing
Mitigation: Guard ist einfach und klar strukturiert
-
API-Key Management
- API-Keys müssen sicher gespeichert werden
- Nur einmal anzeigbar
Mitigation: Standard-Pattern (wie GitHub, AWS, etc.)
-
Migration von existierendem Code
- Mehrere Dateien müssen angepasst werden
- Testing ist wichtig
Mitigation: Systematischer Plan (6 Tage)
🎯 Zusammenfassung
Problem erkannt:
- ❌ API-Code verwendet
users(für Admins gedacht) - ❌ Admin-Interface verwaltet
gateway_users(für API-Clients) - ❌ Keine Integration zwischen beiden
Lösung:
- ✅ API-Code auf
gateway_usersumstellen - ✅ Custom Guard für API-Key Authentication
- ✅ Admin-Interface für Gateway-User Credentials
- ✅ Klare Architektur: Admins vs. API-Clients
Architektur:
users (Admins)
└── Web-Interface (Session Auth)
└── Verwalten gateway_users
gateway_users (API-Clients)
├── API-Key Authentication
├── gateway_user_credentials (Provider API-Keys)
├── Budget & Rate Limits
└── usage_logs
Aufwand:
- 6 Arbeitstage für vollständige Umsetzung
- Systematischer Plan vorhanden
- Klare Checkliste
Ergebnis:
- Funktionierende API
- Saubere Architektur
- Standard Laravel-Patterns
- Wartbar und erweiterbar
🚀 Nächste Schritte
Option A: Sofort starten (Empfohlen)
- Migrations erstellen und ausführen
- Models anpassen
- ApiKeyGuard implementieren
- API-Code anpassen
- Admin-Interface erweitern
- Testing
Option B: Erst Prototyp
- Nur Migrations und Models
- Minimal-Implementierung für Testing
- Dann entscheiden ob weitermachen
Option C: Review und Anpassungen
- Plan reviewen
- Anpassungen vornehmen
- Dann starten
📎 Wichtige Dateien (Neu/Geändert)
Neu erstellen:
app/Auth/ApiKeyGuard.phpapp/Models/GatewayUserCredential.phpapp/Http/Controllers/Admin/GatewayUserCredentialController.phpdatabase/migrations/*_add_budget_fields_to_gateway_users.phpdatabase/migrations/*_create_gateway_user_credentials_table.phpdatabase/migrations/*_update_usage_logs_for_gateway_users.php
Anpassen:
app/Models/GatewayUser.php(Authenticatable implementieren)app/Services/LLM/GatewayService.php(GatewayUser statt User)app/Services/LLM/RequestLogger.php(gateway_user_id)app/Http/Middleware/CheckBudget.phpapp/Http/Middleware/CheckRateLimit.phpapp/Providers/AuthServiceProvider.phpconfig/auth.phproutes/api.phproutes/web.php
Dokument-Ende
Erstellt am: 2025-11-18
Status: BEREIT FÜR IMPLEMENTIERUNG
Geschätzter Aufwand: 6 Arbeitstage