diff --git a/app/Http/Middleware/AdminAuthenticate.php b/app/Http/Middleware/AdminAuthenticate.php index 2618749..d99d06f 100644 --- a/app/Http/Middleware/AdminAuthenticate.php +++ b/app/Http/Middleware/AdminAuthenticate.php @@ -5,6 +5,7 @@ namespace App\Http\Middleware; use App\Constants\ErrorCode; +use App\Models\Admin; use Closure; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; @@ -22,19 +23,56 @@ public function handle(Request $request, Closure $next): Response ], Response::HTTP_UNAUTHORIZED); } + /** @var Admin $admin */ $admin = Auth::guard('admin')->user(); - // 检查是否是管理员 - if (!$admin || !in_array($admin->role, ['super', 'admin'])) { + // 检查是否是有效的管理员 + if (!$admin->isValidAdmin()) { return response()->json([ 'error' => ErrorCode::FORBIDDEN, 'message' => '无权访问管理员资源。', ], Response::HTTP_FORBIDDEN); } + // 检查资源访问权限 + if (!$this->checkResourcePermission($request, $admin)) { + return response()->json([ + 'error' => ErrorCode::FORBIDDEN, + 'message' => '无权访问该资源。', + ], Response::HTTP_FORBIDDEN); + } + // Add admin information to the request $request->merge(['admin' => $admin]); return $next($request); } + + /** + * 检查管理员是否有权限访问请求的资源 + */ + private function checkResourcePermission(Request $request, Admin $admin): bool + { + // 超级管理员可以访问所有资源 + if ($admin->isSuperAdmin()) { + return true; + } + + // 获取路由参数 + $clientId = $request->route('client') ?? $request->route('id'); + $providerId = $request->route('llm_provider') ?? $request->route('id'); + + // 检查客户管理权限 + if ($clientId && str_contains($request->path(), 'clients')) { + return $admin->canManageClient((int)$clientId); + } + + // 检查LLM提供商管理权限 + if ($providerId && str_contains($request->path(), 'llm-providers')) { + return $admin->canManageLlmProvider((int)$providerId); + } + + // 默认允许访问其他资源 + return true; + } } diff --git a/app/Models/Admin.php b/app/Models/Admin.php index d234f47..9ce1915 100644 --- a/app/Models/Admin.php +++ b/app/Models/Admin.php @@ -9,6 +9,20 @@ class Admin extends Authenticatable { + /** + * 管理员角色常量 + */ + public const ROLE_SUPER = 'super'; + public const ROLE_ADMIN = 'admin'; + + /** + * 有效的角色列表 + */ + public const VALID_ROLES = [ + self::ROLE_SUPER, + self::ROLE_ADMIN, + ]; + protected $table = 'admins'; protected $fillable = [ @@ -32,8 +46,56 @@ public function clients(): BelongsToMany ->withTimestamp('assigned_at'); } + /** + * 检查是否是超级管理员 + */ public function isSuperAdmin(): bool { - return $this->role === 'super'; + return $this->role === self::ROLE_SUPER; + } + + /** + * 检查是否是普通管理员 + */ + public function isAdmin(): bool + { + return $this->role === self::ROLE_ADMIN; + } + + /** + * 检查是否有效的管理员(包括超级管理员和普通管理员) + */ + public function isValidAdmin(): bool + { + return in_array($this->role, self::VALID_ROLES, true); + } + + /** + * 检查是否可以管理指定的客户 + */ + public function canManageClient(int $clientId): bool + { + if ($this->isSuperAdmin()) { + return true; + } + + return $this->clients()->where('client_id', $clientId)->exists(); + } + + /** + * 检查是否可以管理指定的LLM提供商 + */ + public function canManageLlmProvider(int $providerId): bool + { + if ($this->isSuperAdmin()) { + return true; + } + + // 普通管理员只能管理与其关联客户绑定的LLM提供商 + return $this->clients() + ->whereHas('llmProvider', function ($query) use ($providerId) { + $query->where('id', $providerId); + }) + ->exists(); } } diff --git a/app/Models/Client.php b/app/Models/Client.php index 89eece3..c820a32 100644 --- a/app/Models/Client.php +++ b/app/Models/Client.php @@ -11,11 +11,34 @@ class Client extends Model { + /** + * 客户状态常量 + */ + public const STATUS_ACTIVE = 'active'; + public const STATUS_INACTIVE = 'inactive'; + + /** + * 有效的状态列表 + */ + public const VALID_STATUSES = [ + self::STATUS_ACTIVE, + self::STATUS_INACTIVE, + ]; + protected $table = 'clients'; protected $fillable = [ 'name', 'llm_provider_id', + 'rate_limit', + 'timeout', + 'status', + ]; + + protected $casts = [ + 'rate_limit' => 'integer', + 'timeout' => 'integer', + 'status' => 'string', ]; public function llmProvider(): BelongsTo @@ -26,11 +49,27 @@ public function llmProvider(): BelongsTo public function admins(): BelongsToMany { return $this->belongsToMany(Admin::class, 'admin_client') - ->withTimestamp('assigned_at'); + ->withTimestamps(); } public function authTokens(): HasMany { return $this->hasMany(AuthToken::class); } + + /** + * 检查客户是否处于活跃状态 + */ + public function isActive(): bool + { + return $this->status === self::STATUS_ACTIVE; + } + + /** + * 检查是否可以发送LLM请求 + */ + public function canSendLlmRequest(): bool + { + return $this->isActive() && $this->llmProvider->isActive(); + } } diff --git a/app/Models/LlmProvider.php b/app/Models/LlmProvider.php index 1a21f8c..d463a02 100644 --- a/app/Models/LlmProvider.php +++ b/app/Models/LlmProvider.php @@ -9,6 +9,20 @@ class LlmProvider extends Model { + /** + * 提供商状态常量 + */ + public const STATUS_ACTIVE = 'active'; + public const STATUS_INACTIVE = 'inactive'; + + /** + * 有效的状态列表 + */ + public const VALID_STATUSES = [ + self::STATUS_ACTIVE, + self::STATUS_INACTIVE, + ]; + protected $table = 'llm_providers'; protected $fillable = [ @@ -16,14 +30,27 @@ class LlmProvider extends Model 'service_name', 'api_url', 'api_token', + 'status', ]; protected $hidden = [ 'api_token', ]; + protected $casts = [ + 'status' => 'string', + ]; + public function clients(): HasMany { return $this->hasMany(Client::class); } + + /** + * 检查提供商是否处于活跃状态 + */ + public function isActive(): bool + { + return $this->status === self::STATUS_ACTIVE; + } } diff --git a/database/sql/schema.sql b/database/sql/schema.sql index 75b139b..8db508d 100644 --- a/database/sql/schema.sql +++ b/database/sql/schema.sql @@ -19,11 +19,13 @@ CREATE TABLE `llm_providers` ( `service_name` VARCHAR(100) NOT NULL, `api_url` VARCHAR(255) NOT NULL, `api_token` VARCHAR(255) NOT NULL, + `status` ENUM('active', 'inactive') NOT NULL DEFAULT 'active', `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `llm_providers_name_unique` (`name`), - KEY `llm_providers_service_name_index` (`service_name`) + KEY `llm_providers_service_name_index` (`service_name`), + KEY `llm_providers_status_index` (`status`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; -- Create clients table @@ -31,10 +33,14 @@ CREATE TABLE `clients` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(100) NOT NULL, `llm_provider_id` BIGINT UNSIGNED NOT NULL, + `rate_limit` INT NOT NULL DEFAULT 60, + `timeout` INT NOT NULL DEFAULT 30, + `status` ENUM('active', 'inactive') NOT NULL DEFAULT 'active', `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`id`), KEY `clients_llm_provider_id_foreign` (`llm_provider_id`), + KEY `clients_status_index` (`status`), CONSTRAINT `clients_llm_provider_id_foreign` FOREIGN KEY (`llm_provider_id`) REFERENCES `llm_providers` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci; @@ -58,7 +64,8 @@ CREATE TABLE `auth_tokens` ( CREATE TABLE `admin_client` ( `admin_id` BIGINT UNSIGNED NOT NULL, `client_id` BIGINT UNSIGNED NOT NULL, - `assigned_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `created_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + `updated_at` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (`admin_id`, `client_id`), KEY `admin_client_client_id_foreign` (`client_id`), CONSTRAINT `admin_client_admin_id_foreign` FOREIGN KEY (`admin_id`)