- Any-LLM Gateway setup with Docker Compose - Laravel 11 admin interface with Livewire - Dashboard with usage statistics and charts - Gateway Users management with budget tracking - API Keys management with revocation - Budget templates with assignment - Usage Logs with filtering and CSV export - Model Pricing management with calculator - PostgreSQL database integration - Complete authentication system for admins
32 KiB
32 KiB
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
- 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
- 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
- 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
- budget_id (PK) VARCHAR
- max_budget DOUBLE
- created_at TIMESTAMP
- updated_at TIMESTAMP
- budget_duration_sec INT
model_pricing - Model Costs
- 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
namespace App\Models;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class Admin extends Authenticatable
{
use Notifiable;
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
4.2 GatewayUser Model
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class GatewayUser extends Model
{
protected $table = 'users';
protected $primaryKey = 'user_id';
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'user_id',
'alias',
'spend',
'budget_id',
'blocked',
'metadata',
'budget_started_at',
'next_budget_reset_at',
];
protected $casts = [
'spend' => '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
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ApiKey extends Model
{
protected $primaryKey = 'id';
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'id',
'key_hash',
'key_name',
'user_id',
'last_used_at',
'expires_at',
'is_active',
'metadata',
];
protected $casts = [
'is_active' => '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
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class UsageLog extends Model
{
protected $primaryKey = 'id';
public $incrementing = false;
protected $keyType = 'string';
public $timestamps = false;
protected $fillable = [
'id',
'api_key_id',
'user_id',
'timestamp',
'model',
'provider',
'endpoint',
'prompt_tokens',
'completion_tokens',
'total_tokens',
'cost',
'status',
'error_message',
];
protected $casts = [
'timestamp' => '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
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Budget extends Model
{
protected $primaryKey = 'budget_id';
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'budget_id',
'max_budget',
'budget_duration_sec',
];
protected $casts = [
'max_budget' => '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
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class ModelPricing extends Model
{
protected $table = 'model_pricing';
protected $primaryKey = 'model_key';
public $incrementing = false;
protected $keyType = 'string';
protected $fillable = [
'model_key',
'input_price_per_million',
'output_price_per_million',
];
protected $casts = [
'input_price_per_million' => '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
namespace App\Services;
use App\Models\UsageLog;
use App\Models\GatewayUser;
use Illuminate\Support\Facades\DB;
class StatisticsService
{
public function getDashboardStats()
{
return [
'total_users' => 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
namespace App\Http\Controllers;
use App\Services\StatisticsService;
class DashboardController extends Controller
{
public function __construct(
private StatisticsService $statsService
) {}
public function index()
{
$stats = $this->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
namespace App\Http\Controllers;
use App\Models\GatewayUser;
use App\Services\StatisticsService;
use Illuminate\Http\Request;
class GatewayUserController extends Controller
{
public function __construct(
private StatisticsService $statsService
) {}
public function index()
{
$users = GatewayUser::with('budget')
->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
namespace App\Http\Livewire\Dashboard;
use Livewire\Component;
use App\Services\StatisticsService;
class StatsOverview extends Component
{
public $refreshInterval = 30000; // 30 seconds
public function render()
{
$stats = app(StatisticsService::class)->getDashboardStats();
return view('livewire.dashboard.stats-overview', [
'stats' => $stats
]);
}
public function refresh()
{
// Livewire will automatically re-render
}
}
7.2 Usage Chart Component
<?php
namespace App\Http\Livewire\Dashboard;
use Livewire\Component;
use App\Services\StatisticsService;
class UsageChart extends Component
{
public $days = 30;
public $chartType = 'requests'; // requests, cost, tokens
public function render()
{
$data = app(StatisticsService::class)->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
<!-- resources/views/layouts/app.blade.php -->
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Any-LLM Admin - @yield('title')</title>
@vite(['resources/css/app.css', 'resources/js/app.js'])
@livewireStyles
</head>
<body class="bg-gray-100">
<div class="min-h-screen">
<!-- Navigation -->
@include('layouts.navigation')
<!-- Page Content -->
<main class="py-10">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
@yield('content')
</div>
</main>
</div>
@livewireScripts
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</body>
</html>
8.2 Dashboard View
<!-- resources/views/dashboard.blade.php -->
@extends('layouts.app')
@section('title', 'Dashboard')
@section('content')
<div class="space-y-6">
<!-- Stats Cards -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
<div class="bg-white rounded-lg shadow p-6">
<div class="text-sm text-gray-600">Total Users</div>
<div class="text-3xl font-bold text-gray-900">{{ $stats['total_users'] }}</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="text-sm text-gray-600">Requests Today</div>
<div class="text-3xl font-bold text-blue-600">{{ number_format($stats['total_requests_today']) }}</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="text-sm text-gray-600">Spend Today</div>
<div class="text-3xl font-bold text-green-600">${{ number_format($stats['total_spend_today'], 2) }}</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="text-sm text-gray-600">Tokens Today</div>
<div class="text-3xl font-bold text-purple-600">{{ number_format($stats['total_tokens_today']) }}</div>
</div>
</div>
<!-- Usage Chart -->
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-lg font-semibold mb-4">Usage Trend (Last 30 Days)</h3>
<canvas id="usageChart"></canvas>
</div>
<!-- Provider Stats & Top Users -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-lg font-semibold mb-4">Usage by Provider</h3>
<canvas id="providerChart"></canvas>
</div>
<div class="bg-white rounded-lg shadow p-6">
<h3 class="text-lg font-semibold mb-4">Top Users</h3>
<div class="space-y-3">
@foreach($topUsers as $user)
<div class="flex justify-between items-center">
<div>
<div class="font-medium">{{ $user->alias ?? $user->user_id }}</div>
<div class="text-sm text-gray-500">{{ number_format($user->usage_logs_count) }} requests</div>
</div>
<div class="text-right">
<div class="font-semibold text-green-600">${{ number_format($user->usage_logs_sum_cost ?? 0, 2) }}</div>
</div>
</div>
@endforeach
</div>
</div>
</div>
</div>
@push('scripts')
<script>
// Chart.js initialization
const usageCtx = document.getElementById('usageChart').getContext('2d');
new Chart(usageCtx, {
type: 'line',
data: {
labels: @json($dailyUsage->pluck('date')),
datasets: [{
label: 'Requests',
data: @json($dailyUsage->pluck('requests')),
borderColor: 'rgb(59, 130, 246)',
backgroundColor: 'rgba(59, 130, 246, 0.1)',
}]
},
options: {
responsive: true,
maintainAspectRatio: true
}
});
const providerCtx = document.getElementById('providerChart').getContext('2d');
new Chart(providerCtx, {
type: 'doughnut',
data: {
labels: @json($providerStats->pluck('provider')),
datasets: [{
data: @json($providerStats->pluck('count')),
backgroundColor: [
'rgb(59, 130, 246)',
'rgb(16, 185, 129)',
'rgb(249, 115, 22)',
'rgb(168, 85, 247)',
]
}]
}
});
</script>
@endpush
@endsection
9. Routes
9.1 Web Routes
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\DashboardController;
use App\Http\Controllers\GatewayUserController;
use App\Http\Controllers\ApiKeyController;
use App\Http\Controllers\BudgetController;
use App\Http\Controllers\UsageLogController;
use App\Http\Controllers\ModelPricingController;
// Authentication Routes (Laravel Breeze)
require __DIR__.'/auth.php';
// Protected Admin Routes
Route::middleware(['auth'])->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
# 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
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('admins', function (Blueprint $table) {
$table->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
// 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 Routesthrottle: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
// 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
// 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
# 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
# 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
# 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:
- Repository aufsetzen
- Models & Migrations implementieren
- Dashboard UI aufbauen
- Testing durchführen
- Production Deployment