This commit is contained in:
Jethro Lin 2024-12-04 12:43:04 +08:00
parent 9df4178800
commit 0af7e10998
10 changed files with 290 additions and 0 deletions

View file

@ -0,0 +1,21 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Auth\Middleware\Authenticate as Middleware;
class Authenticate extends Middleware
{
/**
* Get the path the user should be redirected to when they are not authenticated.
*
* @param \Illuminate\Http\Request $request
* @return string|null
*/
protected function redirectTo($request)
{
if (! $request->expectsJson()) {
return route('login');
}
}
}

View file

@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
class EncryptCookies extends Middleware
{
/**
* The names of the cookies that should not be encrypted.
*
* @var array
*/
protected $except = [
//
];
}

View file

@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\PreventRequestsDuringMaintenance as Middleware;
class PreventRequestsDuringMaintenance extends Middleware
{
/**
* The URIs that should be reachable while maintenance mode is enabled.
*
* @var array
*/
protected $except = [
//
];
}

View file

@ -0,0 +1,32 @@
<?php
namespace App\Http\Middleware;
use App\Providers\RouteServiceProvider;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class RedirectIfAuthenticated
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @param string|null ...$guards
* @return mixed
*/
public function handle(Request $request, Closure $next, ...$guards)
{
$guards = empty($guards) ? [null] : $guards;
foreach ($guards as $guard) {
if (Auth::guard($guard)->check()) {
return redirect(RouteServiceProvider::HOME);
}
}
return $next($request);
}
}

View file

@ -0,0 +1,19 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
class TrimStrings extends Middleware
{
/**
* The names of the attributes that should not be trimmed.
*
* @var array
*/
protected $except = [
'current_password',
'password',
'password_confirmation',
];
}

View file

@ -0,0 +1,20 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustHosts as Middleware;
class TrustHosts extends Middleware
{
/**
* Get the host patterns that should be trusted.
*
* @return array
*/
public function hosts()
{
return [
$this->allSubdomainsOfApplicationUrl(),
];
}
}

View file

@ -0,0 +1,23 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Http\Middleware\TrustProxies as Middleware;
use Illuminate\Http\Request;
class TrustProxies extends Middleware
{
/**
* The trusted proxies for this application.
*
* @var array|string|null
*/
protected $proxies;
/**
* The headers that should be used to detect proxies.
*
* @var int
*/
protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB;
}

View file

@ -0,0 +1,17 @@
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* The URIs that should be excluded from CSRF verification.
*
* @var array
*/
protected $except = [
'api/*',
];
}

View file

@ -0,0 +1,40 @@
declare(strict_types=1);
namespace App\Providers;
use Illuminate\Cache\RateLimiting\Limit;
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\RateLimiter;
use Illuminate\Support\Facades\Route;
class RouteServiceProvider extends ServiceProvider
{
/**
* The path to your application's "home" route.
*
* Typically, users are redirected here after authentication.
*
* @var string
*/
public const HOME = '/';
/**
* Define your route model bindings, pattern filters, and other route configuration.
*/
public function boot(): void
{
RateLimiter::for('api', function (Request $request) {
return Limit::perMinute(60)->by($request->user()?->id ?: $request->ip());
});
$this->routes(function () {
Route::middleware('api')
->prefix('api')
->group(base_path('routes/api.php'));
Route::middleware('web')
->group(base_path('routes/web.php'));
});
}
}

View file

@ -0,0 +1,84 @@
<?php
declare(strict_types=1);
namespace App\Services;
use App\Models\Client;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\RateLimiter;
class LlmService
{
private const REQUEST_TIMEOUT = 30; // 请求超时时间(秒)
/**
* 发送 LLM 请求
*
* @param Client $client 客户
* @param string $prompt 提示词
* @param array $options 可选参数
* @return array{response: string, usage: array}
* @throws \RuntimeException
*/
public function sendRequest(Client $client, string $prompt, array $options = []): array
{
// 检查请求频率限制
$key = 'llm_request_' . $client->id;
if (RateLimiter::tooManyAttempts($key, $client->rate_limit)) {
$seconds = RateLimiter::availableIn($key);
throw new \RuntimeException("请求过于频繁,请在 {$seconds} 秒后重试。");
}
try {
$provider = $client->llmProvider;
if ($provider->status !== 'active') {
throw new \RuntimeException('LLM 提供商当前不可用。');
}
// 发送请求到 LLM 提供商
$response = Http::timeout($client->timeout)
->withToken($provider->api_token)
->withHeaders([
'Content-Type' => 'application/json',
'Accept' => 'application/json',
])
->post($provider->api_url, array_merge([
'prompt' => $prompt,
], $options));
if (!$response->successful()) {
Log::error('LLM provider request failed', [
'status' => $response->status(),
'body' => $response->body(),
'provider' => $provider->name,
'client_id' => $client->id,
]);
throw new \RuntimeException('LLM 提供商服务异常,请稍后重试。');
}
// 记录成功的请求
RateLimiter::hit($key, 60);
$result = $response->json();
return [
'response' => $result['choices'][0]['text'] ?? $result['response'] ?? '',
'usage' => $result['usage'] ?? [],
];
} catch (\Illuminate\Http\Client\ConnectionException $e) {
Log::error('LLM provider connection timeout', [
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString(),
'provider' => $provider->name ?? null,
'client_id' => $client->id,
]);
throw new \RuntimeException('LLM 提供商响应超时,请稍后重试。');
}
}
}