llmbackend/app/Http/Controllers/Api/Admin/ClientController.php
2024-12-04 13:53:58 +08:00

327 lines
9.5 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Http\Controllers\Api\Admin;
use App\Constants\ErrorCode;
use App\Http\Controllers\Controller;
use App\Models\Admin;
use App\Models\Client;
use App\Services\Auth\TokenService;
use App\Services\LogService;
use App\Traits\ApiResponse;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Redis;
use Illuminate\Validation\ValidationException;
class ClientController extends Controller
{
use ApiResponse;
private Admin $admin;
public function __construct(
private readonly TokenService $tokenService,
private readonly LogService $logService,
Request $request
) {
$this->admin = $request->admin;
}
/**
* 獲取客戶列表
*/
public function index(): JsonResponse
{
try {
$clients = Client::select([
'id',
'name',
'llm_provider_id',
'created_at',
])->get();
return $this->success([
'items' => $clients->map(fn($client) => [
'id' => $client->id,
'name' => $client->name,
'llm_provider_id' => $client->llm_provider_id,
'created_at' => $client->created_at->toIso8601String(),
])
]);
} catch (\Exception $e) {
Log::error('Error fetching clients', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
]);
return $this->error(
ErrorCode::SERVER_ERROR,
ErrorCode::getMessage(ErrorCode::SERVER_ERROR)
);
}
}
/**
* 新增客戶
*/
public function store(Request $request): JsonResponse
{
try {
$validated = $request->validate([
'name' => [
'required',
'string',
'max:100',
'unique:clients',
],
'llm_provider_id' => [
'required',
'integer',
'exists:llm_providers,id',
],
]);
$validated['status'] = Client::STATUS_ACTIVE;
$validated['rate_limit'] = config('llm.default_rate_limit', 60);
$validated['timeout'] = config('llm.default_timeout', 30);
$client = Client::create($validated);
$this->logService->logOperation(
'admin',
$this->admin->id,
"Created client: {$client->name}"
);
return $this->created([
'id' => $client->id,
'name' => $client->name,
'llm_provider_id' => $client->llm_provider_id,
'created_at' => $client->created_at->toIso8601String(),
]);
} catch (ValidationException $e) {
return $this->error(
ErrorCode::VALIDATION_ERROR,
ErrorCode::getMessage(ErrorCode::VALIDATION_ERROR),
$e->errors()
);
} catch (\Exception $e) {
Log::error('Error creating client', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'request_data' => $request->all(),
]);
return $this->error(
ErrorCode::SERVER_ERROR,
ErrorCode::getMessage(ErrorCode::SERVER_ERROR)
);
}
}
/**
* 修改客戶
*/
public function update(Request $request, int $id): JsonResponse
{
try {
$client = Client::findOrFail($id);
if (!$this->admin->canManageClient($client->id)) {
return $this->error(
ErrorCode::FORBIDDEN,
'您無權管理該客戶。'
);
}
$validated = $request->validate([
'name' => [
'required',
'string',
'max:100',
"unique:clients,name,{$id}",
],
'llm_provider_id' => [
'required',
'integer',
'exists:llm_providers,id',
],
]);
$client->update($validated);
$this->logService->logOperation(
'admin',
$this->admin->id,
"Updated client: {$client->name}"
);
return $this->success([
'id' => $client->id,
'name' => $client->name,
'llm_provider_id' => $client->llm_provider_id,
'updated_at' => $client->updated_at->toIso8601String(),
]);
} catch (ValidationException $e) {
return $this->error(
ErrorCode::VALIDATION_ERROR,
ErrorCode::getMessage(ErrorCode::VALIDATION_ERROR),
$e->errors()
);
} catch (ModelNotFoundException $e) {
return $this->error(
ErrorCode::CLIENT_NOT_FOUND,
ErrorCode::getMessage(ErrorCode::CLIENT_NOT_FOUND)
);
} catch (\Exception $e) {
Log::error('Error updating client', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'client_id' => $id,
'request_data' => $request->all(),
]);
return $this->error(
ErrorCode::SERVER_ERROR,
ErrorCode::getMessage(ErrorCode::SERVER_ERROR)
);
}
}
/**
* 刪除客戶
*/
public function destroy(int $id): JsonResponse
{
try {
$client = Client::findOrFail($id);
if (!$this->admin->canManageClient($client->id)) {
return $this->error(
ErrorCode::FORBIDDEN,
'您無權管理該客戶。'
);
}
if ($this->hasActiveTokens($client)) {
return $this->error(
ErrorCode::CLIENT_HAS_ACTIVE_TOKENS,
'該客戶有活躍的認證令牌,無法刪除。'
);
}
$client->delete();
$this->logService->logOperation(
'admin',
$this->admin->id,
"Deleted client: {$client->name}"
);
return $this->success(null, '客戶已刪除。');
} catch (ModelNotFoundException $e) {
return $this->error(
ErrorCode::CLIENT_NOT_FOUND,
ErrorCode::getMessage(ErrorCode::CLIENT_NOT_FOUND)
);
} catch (\Exception $e) {
Log::error('Error deleting client', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'client_id' => $id,
]);
return $this->error(
ErrorCode::SERVER_ERROR,
ErrorCode::getMessage(ErrorCode::SERVER_ERROR)
);
}
}
/**
* 為客戶生成認證令牌
*/
public function generateAuthToken(int $id): JsonResponse
{
try {
$client = Client::findOrFail($id);
if (!$this->admin->canManageClient($client->id)) {
return $this->error(
ErrorCode::FORBIDDEN,
'您無權管理該客戶。'
);
}
if (!$client->isActive()) {
return $this->error(
ErrorCode::CLIENT_INACTIVE,
'客戶未啟用,無法生成令牌。'
);
}
$result = $this->tokenService->generateAuthToken($client);
$this->logService->logOperation(
'admin',
$this->admin->id,
"Generated auth token for client: {$client->name}"
);
return $this->success([
'client_id' => $client->id,
'auth_token' => $result['token'],
'created_at' => now()->toIso8601String(),
]);
} catch (ModelNotFoundException $e) {
return $this->error(
ErrorCode::CLIENT_NOT_FOUND,
ErrorCode::getMessage(ErrorCode::CLIENT_NOT_FOUND)
);
} catch (\Exception $e) {
Log::error('Error generating auth token', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'client_id' => $id,
]);
return $this->error(
ErrorCode::SERVER_ERROR,
ErrorCode::getMessage(ErrorCode::SERVER_ERROR)
);
}
}
/**
* 檢查客戶是否有活躍的令牌
*/
private function hasActiveTokens(Client $client): bool
{
$pattern = 'auth_token:*';
$keys = Redis::keys($pattern);
foreach ($keys as $key) {
$data = Redis::get($key);
if ($data) {
$tokenData = json_decode($data, true);
if ($tokenData &&
$tokenData['client_id'] === $client->id &&
now()->isBefore($tokenData['expires_at'])) {
return true;
}
}
}
return false;
}
}