Files
laravel-llm-gateway/LARAVEL_IMPLEMENTATION.md
wtrinkl b1363aeab9 Initial commit: Any-LLM Gateway with Laravel Admin Interface
- 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
2025-11-16 12:38:05 +01:00

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 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

// 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:

  1. Repository aufsetzen
  2. Models & Migrations implementieren
  3. Dashboard UI aufbauen
  4. Testing durchführen
  5. Production Deployment