# Laravel Verwaltungsoberfläche für Any-LLM Gateway ## Implementierungskonzept --- ## 1. Projekt-Übersicht ### 1.1 Ziel Vollständige Laravel-Verwaltungsoberfläche für das Any-LLM Gateway mit: - Benutzer-Login und Authentifizierung - Dashboard mit Statistiken und Analysen - Verwaltung von Users, API Keys, Budgets - Detaillierte Nutzungsberichte - Monitoring und Alerts ### 1.2 Tech Stack - **Backend**: Laravel 11.x - **Frontend**: Livewire 3.x + Alpine.js + Tailwind CSS - **Charts**: Chart.js / ApexCharts - **Datenbank**: PostgreSQL (existing Gateway DB) - **Authentication**: Laravel Breeze mit Livewire --- ## 2. Datenbankstruktur (Existing Gateway DB) ### 2.1 Tabellen-Übersicht ``` ┌─────────────────┐ │ users │ ← Gateway Users (API Consumers) └────────┬────────┘ │ ┌────┴────┬─────────────┬──────────────┐ │ │ │ │ ┌───▼────┐ ┌─▼────────┐ ┌──▼───────┐ ┌──▼──────────┐ │api_keys│ │usage_logs│ │ budgets │ │budget_reset │ └────────┘ └──────────┘ └──────────┘ │ _logs │ └─────────────┘ ``` ### 2.2 Tabellen-Details #### `users` - Gateway API Users ```sql - user_id (PK) VARCHAR - alias VARCHAR - spend DOUBLE - budget_id (FK) VARCHAR - blocked BOOLEAN - created_at TIMESTAMP - updated_at TIMESTAMP - metadata JSON - budget_started_at TIMESTAMP - next_budget_reset_at TIMESTAMP ``` #### `api_keys` - Virtual Keys ```sql - id (PK) VARCHAR - key_hash VARCHAR (UNIQUE) - key_name VARCHAR - user_id (FK) VARCHAR - created_at TIMESTAMP - last_used_at TIMESTAMP - expires_at TIMESTAMP - is_active BOOLEAN - metadata JSON ``` #### `usage_logs` - Request Tracking ```sql - id (PK) VARCHAR - api_key_id (FK) VARCHAR - user_id (FK) VARCHAR - timestamp TIMESTAMP (INDEXED) - model VARCHAR - provider VARCHAR - endpoint VARCHAR - prompt_tokens INT - completion_tokens INT - total_tokens INT - cost DOUBLE - status VARCHAR - error_message VARCHAR ``` #### `budgets` - Budget Definitions ```sql - budget_id (PK) VARCHAR - max_budget DOUBLE - created_at TIMESTAMP - updated_at TIMESTAMP - budget_duration_sec INT ``` #### `model_pricing` - Model Costs ```sql - model_key (PK) VARCHAR - input_price_per_million DOUBLE - output_price_per_million DOUBLE - created_at TIMESTAMP - updated_at TIMESTAMP ``` --- ## 3. Laravel Projektstruktur ### 3.1 Verzeichnisstruktur ``` any-llm-admin/ ├── app/ │ ├── Http/ │ │ ├── Controllers/ │ │ │ ├── DashboardController.php │ │ │ ├── GatewayUserController.php │ │ │ ├── ApiKeyController.php │ │ │ ├── BudgetController.php │ │ │ ├── UsageLogController.php │ │ │ └── ModelPricingController.php │ │ ├── Livewire/ │ │ │ ├── Dashboard/ │ │ │ │ ├── StatsOverview.php │ │ │ │ ├── UsageChart.php │ │ │ │ ├── TopUsers.php │ │ │ │ └── RecentActivity.php │ │ │ ├── GatewayUsers/ │ │ │ │ ├── Index.php │ │ │ │ ├── Create.php │ │ │ │ ├── Edit.php │ │ │ │ └── Show.php │ │ │ ├── ApiKeys/ │ │ │ │ ├── Index.php │ │ │ │ ├── Create.php │ │ │ │ └── Revoke.php │ │ │ └── Budgets/ │ │ │ ├── Index.php │ │ │ ├── Create.php │ │ │ └── Edit.php │ │ └── Middleware/ │ │ └── EnsureAdmin.php │ ├── Models/ │ │ ├── Admin.php (Laravel Auth User) │ │ ├── GatewayUser.php (Gateway users table) │ │ ├── ApiKey.php │ │ ├── UsageLog.php │ │ ├── Budget.php │ │ ├── BudgetResetLog.php │ │ └── ModelPricing.php │ └── Services/ │ ├── StatisticsService.php │ ├── BudgetService.php │ └── GatewayApiService.php ├── database/ │ ├── migrations/ │ │ └── 2025_11_15_000001_create_admins_table.php │ └── seeders/ │ └── AdminSeeder.php ├── resources/ │ ├── views/ │ │ ├── layouts/ │ │ │ ├── app.blade.php │ │ │ ├── navigation.blade.php │ │ │ └── guest.blade.php │ │ ├── dashboard.blade.php │ │ ├── gateway-users/ │ │ ├── api-keys/ │ │ ├── budgets/ │ │ ├── usage-logs/ │ │ └── model-pricing/ │ └── js/ │ └── charts.js └── routes/ ├── web.php └── api.php ``` --- ## 4. Laravel Models ### 4.1 Admin Model (Laravel Auth) ```php 'datetime', 'password' => 'hashed', ]; } ``` ### 4.2 GatewayUser Model ```php 'double', 'blocked' => 'boolean', 'metadata' => 'array', 'created_at' => 'datetime', 'updated_at' => 'datetime', 'budget_started_at' => 'datetime', 'next_budget_reset_at' => 'datetime', ]; // Relationships public function apiKeys() { return $this->hasMany(ApiKey::class, 'user_id', 'user_id'); } public function usageLogs() { return $this->hasMany(UsageLog::class, 'user_id', 'user_id'); } public function budget() { return $this->belongsTo(Budget::class, 'budget_id', 'budget_id'); } public function budgetResetLogs() { return $this->hasMany(BudgetResetLog::class, 'user_id', 'user_id'); } // Scopes public function scopeActive($query) { return $query->where('blocked', false); } public function scopeBlocked($query) { return $query->where('blocked', true); } // Accessors public function getSpendFormattedAttribute() { return '$' . number_format($this->spend, 2); } public function getTotalRequestsAttribute() { return $this->usageLogs()->count(); } public function getTotalTokensAttribute() { return $this->usageLogs()->sum('total_tokens'); } } ``` ### 4.3 ApiKey Model ```php 'boolean', 'metadata' => 'array', 'created_at' => 'datetime', 'last_used_at' => 'datetime', 'expires_at' => 'datetime', ]; // Relationships public function gatewayUser() { return $this->belongsTo(GatewayUser::class, 'user_id', 'user_id'); } public function usageLogs() { return $this->hasMany(UsageLog::class, 'api_key_id', 'id'); } // Scopes public function scopeActive($query) { return $query->where('is_active', true) ->where(function ($q) { $q->whereNull('expires_at') ->orWhere('expires_at', '>', now()); }); } public function scopeExpired($query) { return $query->whereNotNull('expires_at') ->where('expires_at', '<=', now()); } // Accessors public function getMaskedKeyAttribute() { return 'gw-' . substr($this->id, 0, 8) . '...' . substr($this->id, -8); } public function getIsExpiredAttribute() { return $this->expires_at && $this->expires_at->isPast(); } } ``` ### 4.4 UsageLog Model ```php 'datetime', 'prompt_tokens' => 'integer', 'completion_tokens' => 'integer', 'total_tokens' => 'integer', 'cost' => 'double', ]; // Relationships public function gatewayUser() { return $this->belongsTo(GatewayUser::class, 'user_id', 'user_id'); } public function apiKey() { return $this->belongsTo(ApiKey::class, 'api_key_id', 'id'); } // Scopes public function scopeSuccess($query) { return $query->where('status', 'success'); } public function scopeFailed($query) { return $query->where('status', '!=', 'success'); } public function scopeToday($query) { return $query->whereDate('timestamp', today()); } public function scopeDateRange($query, $start, $end) { return $query->whereBetween('timestamp', [$start, $end]); } // Accessors public function getCostFormattedAttribute() { return $this->cost ? '$' . number_format($this->cost, 4) : 'N/A'; } } ``` ### 4.5 Budget Model ```php 'double', 'budget_duration_sec' => 'integer', 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; // Relationships public function gatewayUsers() { return $this->hasMany(GatewayUser::class, 'budget_id', 'budget_id'); } public function resetLogs() { return $this->hasMany(BudgetResetLog::class, 'budget_id', 'budget_id'); } // Accessors public function getMaxBudgetFormattedAttribute() { return '$' . number_format($this->max_budget, 2); } public function getDurationHumanAttribute() { if (!$this->budget_duration_sec) return 'No limit'; $days = floor($this->budget_duration_sec / 86400); $hours = floor(($this->budget_duration_sec % 86400) / 3600); return "{$days}d {$hours}h"; } } ``` ### 4.6 ModelPricing Model ```php 'double', 'output_price_per_million' => 'double', 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; // Accessors public function getInputPriceFormattedAttribute() { return '$' . number_format($this->input_price_per_million, 2) . '/M'; } public function getOutputPriceFormattedAttribute() { return '$' . number_format($this->output_price_per_million, 2) . '/M'; } } ``` --- ## 5. Services ### 5.1 StatisticsService ```php GatewayUser::count(), 'active_users' => GatewayUser::active()->count(), 'total_requests_today' => UsageLog::today()->count(), 'total_spend_today' => UsageLog::today()->sum('cost'), 'total_tokens_today' => UsageLog::today()->sum('total_tokens'), ]; } public function getUsageByProvider($days = 30) { return UsageLog::selectRaw('provider, COUNT(*) as count, SUM(cost) as total_cost') ->where('timestamp', '>=', now()->subDays($days)) ->groupBy('provider') ->get(); } public function getUsageByModel($days = 30) { return UsageLog::selectRaw('model, COUNT(*) as count, SUM(total_tokens) as tokens') ->where('timestamp', '>=', now()->subDays($days)) ->groupBy('model') ->orderByDesc('count') ->limit(10) ->get(); } public function getDailyUsageChart($days = 30) { return UsageLog::selectRaw('DATE(timestamp) as date, COUNT(*) as requests, SUM(cost) as cost') ->where('timestamp', '>=', now()->subDays($days)) ->groupBy('date') ->orderBy('date') ->get(); } public function getTopUsers($limit = 10) { return GatewayUser::withCount('usageLogs') ->withSum('usageLogs', 'cost') ->orderByDesc('usage_logs_sum_cost') ->limit($limit) ->get(); } public function getRecentActivity($limit = 20) { return UsageLog::with(['gatewayUser', 'apiKey']) ->orderByDesc('timestamp') ->limit($limit) ->get(); } public function getUserStatistics($userId, $days = 30) { $stats = UsageLog::where('user_id', $userId) ->where('timestamp', '>=', now()->subDays($days)) ->selectRaw(' COUNT(*) as total_requests, SUM(prompt_tokens) as total_prompt_tokens, SUM(completion_tokens) as total_completion_tokens, SUM(total_tokens) as total_tokens, SUM(cost) as total_cost, AVG(total_tokens) as avg_tokens_per_request ') ->first(); return $stats; } } ``` --- ## 6. Controllers ### 6.1 DashboardController ```php statsService->getDashboardStats(); $dailyUsage = $this->statsService->getDailyUsageChart(30); $topUsers = $this->statsService->getTopUsers(5); $providerStats = $this->statsService->getUsageByProvider(30); return view('dashboard', compact( 'stats', 'dailyUsage', 'topUsers', 'providerStats' )); } } ``` ### 6.2 GatewayUserController ```php withCount('apiKeys') ->withCount('usageLogs') ->paginate(20); return view('gateway-users.index', compact('users')); } public function show($userId) { $user = GatewayUser::with(['apiKeys', 'budget']) ->findOrFail($userId); $stats = $this->statsService->getUserStatistics($userId, 30); $recentLogs = $user->usageLogs() ->orderByDesc('timestamp') ->limit(50) ->get(); return view('gateway-users.show', compact('user', 'stats', 'recentLogs')); } // ... weitere CRUD Methoden } ``` --- ## 7. Livewire Components ### 7.1 Dashboard Stats Overview ```php getDashboardStats(); return view('livewire.dashboard.stats-overview', [ 'stats' => $stats ]); } public function refresh() { // Livewire will automatically re-render } } ``` ### 7.2 Usage Chart Component ```php getDailyUsageChart($this->days); return view('livewire.dashboard.usage-chart', [ 'chartData' => $data ]); } public function updatedDays() { // Chart will automatically update } } ``` --- ## 8. Views Structure ### 8.1 Dashboard Layout ```blade Any-LLM Admin - @yield('title') @vite(['resources/css/app.css', 'resources/js/app.js']) @livewireStyles
@include('layouts.navigation')
@yield('content')
@livewireScripts ``` ### 8.2 Dashboard View ```blade @extends('layouts.app') @section('title', 'Dashboard') @section('content')
Total Users
{{ $stats['total_users'] }}
Requests Today
{{ number_format($stats['total_requests_today']) }}
Spend Today
${{ number_format($stats['total_spend_today'], 2) }}
Tokens Today
{{ number_format($stats['total_tokens_today']) }}

Usage Trend (Last 30 Days)

Usage by Provider

Top Users

@foreach($topUsers as $user)
{{ $user->alias ?? $user->user_id }}
{{ number_format($user->usage_logs_count) }} requests
${{ number_format($user->usage_logs_sum_cost ?? 0, 2) }}
@endforeach
@push('scripts') @endpush @endsection ``` --- ## 9. Routes ### 9.1 Web Routes ```php group(function () { // Dashboard Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard'); // Gateway Users Route::resource('gateway-users', GatewayUserController::class); Route::post('gateway-users/{id}/toggle-block', [GatewayUserController::class, 'toggleBlock']) ->name('gateway-users.toggle-block'); // API Keys Route::resource('api-keys', ApiKeyController::class)->except(['edit', 'update']); Route::post('api-keys/{id}/revoke', [ApiKeyController::class, 'revoke']) ->name('api-keys.revoke'); // Budgets Route::resource('budgets', BudgetController::class'); // Usage Logs Route::get('usage-logs', [UsageLogController::class, 'index'])->name('usage-logs.index'); Route::get('usage-logs/export', [UsageLogController::class, 'export'])->name('usage-logs.export'); // Model Pricing Route::resource('model-pricing', ModelPricingController::class); // API for Charts (AJAX) Route::prefix('api')->group(function () { Route::get('stats/daily-usage', [DashboardController::class, 'dailyUsage']); Route::get('stats/provider-breakdown', [DashboardController::class, 'providerBreakdown']); Route::get('stats/model-usage', [DashboardController::class, 'modelUsage']); }); }); ``` --- ## 10. Installation & Setup ### 10.1 Voraussetzungen - PHP 8.2+ - Composer - Node.js & NPM - PostgreSQL Client ### 10.2 Installation Steps ```bash # 1. Laravel Projekt erstellen composer create-project laravel/laravel any-llm-admin cd any-llm-admin # 2. Zusätzliche Packages installieren composer require livewire/livewire composer require laravel/breeze --dev # 3. Breeze mit Livewire installieren php artisan breeze:install livewire npm install && npm run build # 4. .env konfigurieren cat > .env << 'EOF' APP_NAME="Any-LLM Admin" APP_URL=http://localhost:8001 DB_CONNECTION=pgsql DB_HOST=localhost DB_PORT=5432 DB_DATABASE=gateway DB_USERNAME=gateway DB_PASSWORD=gateway EOF # 5. Admin Migration erstellen php artisan make:migration create_admins_table # 6. Models erstellen php artisan make:model GatewayUser php artisan make:model ApiKey php artisan make:model UsageLog php artisan make:model Budget php artisan make:model BudgetResetLog php artisan make:model ModelPricing # 7. Services erstellen php artisan make:class Services/StatisticsService # 8. Controllers erstellen php artisan make:controller DashboardController php artisan make:controller GatewayUserController --resource php artisan make:controller ApiKeyController --resource php artisan make:controller BudgetController --resource php artisan make:controller UsageLogController php artisan make:controller ModelPricingController --resource # 9. Livewire Components erstellen php artisan make:livewire Dashboard/StatsOverview php artisan make:livewire Dashboard/UsageChart php artisan make:livewire GatewayUsers/Index php artisan make:livewire ApiKeys/Index # 10. Migration für Admins ausführen php artisan migrate # 11. Admin User erstellen php artisan tinker >>> \App\Models\Admin::create([ ... 'name' => 'Admin', ... 'email' => 'admin@example.com', ... 'password' => bcrypt('password123') ... ]); # 12. Development Server starten php artisan serve --port=8001 ``` ### 10.3 Admin Migration ```php id(); $table->string('name'); $table->string('email')->unique(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); $table->timestamps(); }); } public function down(): void { Schema::dropIfExists('admins'); } }; ``` --- ## 11. Features & Funktionalität ### 11.1 Dashboard ✅ **Übersicht-Statistiken** - Total Users / Active Users - Requests Today - Spend Today / This Month - Tokens Today ✅ **Visualisierungen** - Daily Usage Chart (Line Chart) - Provider Breakdown (Doughnut Chart) - Model Usage (Bar Chart) - Cost Trends ✅ **Quick Actions** - Top Users Widget - Recent Activity Feed - Alerts (Budget Warnings) ### 11.2 Gateway Users Management ✅ **Liste aller Users** - Sortierbar nach Spend, Requests, Created Date - Filter: Active / Blocked / All - Suche nach User ID / Alias ✅ **User Detail Page** - Übersicht (Spend, Requests, Tokens) - Associated API Keys - Budget Information - 30-Day Usage Chart - Recent Activity Log ✅ **User Actions** - Create New User - Edit User (Alias, Budget) - Block / Unblock User - Delete User (mit Confirmation) ### 11.3 API Keys Management ✅ **Liste aller Keys** - Masked Keys anzeigen - Status: Active / Expired / Revoked - Last Used Date - Associated User ✅ **Key Actions** - Create Virtual Key - Revoke Key - Set Expiration Date - View Key Details ### 11.4 Budgets Management ✅ **Budget Templates** - Daily / Weekly / Monthly Budgets - Custom Duration - No Limit Option ✅ **Budget Assignment** - Assign to Users - Bulk Assignment - Auto-Reset Configuration ### 11.5 Usage Logs ✅ **Comprehensive Logging** - Filter by Date Range - Filter by User / Provider / Model - Export to CSV / Excel - Real-time Updates (Livewire) ✅ **Log Details** - Request Metadata - Token Counts - Cost Calculation - Error Messages (if failed) ### 11.6 Model Pricing ✅ **Pricing Management** - Add New Model Pricing - Update Existing Prices - Bulk Import from CSV - Cost Calculator Tool --- ## 12. Security & Best Practices ### 12.1 Authentication ```php // auth.php guard configuration 'guards' => [ 'web' => [ 'driver' => 'session', 'provider' => 'admins', ], ], 'providers' => [ 'admins' => [ 'driver' => 'eloquent', 'model' => App\Models\Admin::class, ], ], ``` ### 12.2 Middleware - `auth` - Alle Admin Routes - `throttle:60,1` - Rate Limiting - CSRF Protection (automatisch) ### 12.3 Database Security - Read-only Connection für Statistics - Prepared Statements (Eloquent default) - Input Validation & Sanitization --- ## 13. Testing Strategy ### 13.1 Feature Tests ```php // tests/Feature/DashboardTest.php public function test_admin_can_view_dashboard() { $admin = Admin::factory()->create(); $response = $this->actingAs($admin) ->get('/dashboard'); $response->assertStatus(200); $response->assertSee('Total Users'); } ``` ### 13.2 Unit Tests ```php // tests/Unit/StatisticsServiceTest.php public function test_dashboard_stats_calculation() { $stats = app(StatisticsService::class)->getDashboardStats(); $this->assertIsArray($stats); $this->assertArrayHasKey('total_users', $stats); } ``` --- ## 14. Deployment Checklist ### 14.1 Production Setup ```bash # 1. Umgebung vorbereiten composer install --optimize-autoloader --no-dev npm ci && npm run build # 2. .env für Production APP_ENV=production APP_DEBUG=false APP_KEY=[generieren mit php artisan key:generate] # 3. Optimierungen php artisan config:cache php artisan route:cache php artisan view:cache php artisan event:cache # 4. Queue Worker (optional) php artisan queue:work --tries=3 ``` ### 14.2 Server Requirements - PHP 8.2+ (php-fpm) - Nginx / Apache - PostgreSQL Client - Supervisor (für Queue Workers) - SSL Certificate (Let's Encrypt) --- ## 15. Erweiterungsmöglichkeiten ### 15.1 Geplante Features - [ ] Email Notifications (Budget Alerts) - [ ] API Rate Limiting per User - [ ] Advanced Analytics Dashboard - [ ] Multi-Admin with Roles - [ ] Audit Log - [ ] Webhook Support - [ ] Slack Integration - [ ] Cost Forecasting ### 15.2 Integration Options - Stripe für Billing - Sentry für Error Tracking - DataDog für Monitoring - Grafana für Advanced Charts --- ## 16. Maintenance ### 16.1 Backup Strategy ```bash # Database Backup pg_dump -U gateway gateway > backup_$(date +%Y%m%d).sql # Automated Backup 0 2 * * * /usr/bin/pg_dump -U gateway gateway > /backups/gateway_$(date +\%Y\%m\%d).sql ``` ### 16.2 Log Rotation ```bash # Laravel Log Rotation php artisan log:clear --keep=30 # Usage Logs Archival (älter als 90 Tage) DELETE FROM usage_logs WHERE timestamp < NOW() - INTERVAL '90 days'; ``` --- ## 17. Support & Documentation ### 17.1 Admin Handbuch - Benutzer-Onboarding Guide - Troubleshooting Common Issues - API Key Best Practices - Budget Configuration Examples ### 17.2 Developer Documentation - Code Style Guide (PSR-12) - Component Structure - Adding New Features - Testing Guidelines --- **Ende des Implementierungskonzepts** Nächste Schritte: 1. Repository aufsetzen 2. Models & Migrations implementieren 3. Dashboard UI aufbauen 4. Testing durchführen 5. Production Deployment