From cbe00f12842c500285ce449b004ef2215afbe819 Mon Sep 17 00:00:00 2001 From: kuaifan Date: Fri, 12 Jun 2026 19:51:19 +0000 Subject: [PATCH] =?UTF-8?q?refactor(skeleton):=20=E5=B9=B3=E7=A7=BB=20Lara?= =?UTF-8?q?vel=2013=20=E6=96=B0=E7=9B=AE=E5=BD=95=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - bootstrap/app.php 改为 Application::configure() 链式配置: withRouting(web/api/console) + withMiddleware + withExceptions - 删除 app/Http/Kernel.php、app/Console/Kernel.php:全局/分组中间件 归并到 13 默认栈,定制项经 trustProxies/trimStrings/ validateCsrfTokens/throttleApi/alias(webapi) 配置 API 表达 - 删除 app/Exceptions/Handler.php:ApiException/ModelNotFound 渲染、 ApiException 条件日志(report->stop)迁入 withExceptions; 图片动态裁剪逻辑抽为 App\Exceptions\ImagePathHandler - 删除 RouteServiceProvider/EventServiceProvider/AuthServiceProvider/ BroadcastServiceProvider:限流、14 个模型观察者、Registered 监听 迁入 AppServiceProvider::boot;新增 bootstrap/providers.php - 删除 7 个框架默认中间件子类(TrustProxies/TrimStrings/VerifyCsrfToken/ EncryptCookies/Authenticate/RedirectIfAuthenticated/ PreventRequestsDuringMaintenance)与未启用的 TrustHosts, 保留自定义 WebApi - config/app.php 移除 providers/aliases 数组(改用框架默认集 + bootstrap/providers.php,补齐 9~13 新增的框架 provider) - artisan、public/index.php 换 13 骨架版(handleCommand/handleRequest) 验证:LaravelS 正常拉起,/health、登录、token 认证、WebSocket 握手、 头像、裁剪(经 withExceptions)、404 兜底全过;php artisan test 145 passed/1 skipped;migrate:fresh 213 全过 Co-Authored-By: Claude Fable 5 --- app/Console/Kernel.php | 41 ------- .../{Handler.php => ImagePathHandler.php} | 92 ++------------ app/Http/Kernel.php | 68 ----------- app/Http/Middleware/Authenticate.php | 21 ---- app/Http/Middleware/EncryptCookies.php | 17 --- .../PreventRequestsDuringMaintenance.php | 17 --- .../Middleware/RedirectIfAuthenticated.php | 32 ----- app/Http/Middleware/TrimStrings.php | 19 --- app/Http/Middleware/TrustHosts.php | 20 ---- app/Http/Middleware/TrustProxies.php | 28 ----- app/Http/Middleware/VerifyCsrfToken.php | 21 ---- app/Providers/AppServiceProvider.php | 77 ++++++++++++ app/Providers/AuthServiceProvider.php | 30 ----- app/Providers/BroadcastServiceProvider.php | 21 ---- app/Providers/EventServiceProvider.php | 72 ----------- app/Providers/RouteServiceProvider.php | 63 ---------- artisan | 50 +------- bootstrap/app.php | 112 +++++++++++------- bootstrap/providers.php | 5 + config/app.php | 107 ----------------- public/index.php | 51 ++------ 21 files changed, 170 insertions(+), 794 deletions(-) delete mode 100644 app/Console/Kernel.php rename app/Exceptions/{Handler.php => ImagePathHandler.php} (70%) delete mode 100644 app/Http/Kernel.php delete mode 100644 app/Http/Middleware/Authenticate.php delete mode 100644 app/Http/Middleware/EncryptCookies.php delete mode 100644 app/Http/Middleware/PreventRequestsDuringMaintenance.php delete mode 100644 app/Http/Middleware/RedirectIfAuthenticated.php delete mode 100644 app/Http/Middleware/TrimStrings.php delete mode 100644 app/Http/Middleware/TrustHosts.php delete mode 100644 app/Http/Middleware/TrustProxies.php delete mode 100644 app/Http/Middleware/VerifyCsrfToken.php delete mode 100644 app/Providers/AuthServiceProvider.php delete mode 100644 app/Providers/BroadcastServiceProvider.php delete mode 100644 app/Providers/EventServiceProvider.php delete mode 100644 app/Providers/RouteServiceProvider.php create mode 100644 bootstrap/providers.php diff --git a/app/Console/Kernel.php b/app/Console/Kernel.php deleted file mode 100644 index 69914e993..000000000 --- a/app/Console/Kernel.php +++ /dev/null @@ -1,41 +0,0 @@ -command('inspire')->hourly(); - } - - /** - * Register the commands for the application. - * - * @return void - */ - protected function commands() - { - $this->load(__DIR__.'/Commands'); - - require base_path('routes/console.php'); - } -} diff --git a/app/Exceptions/Handler.php b/app/Exceptions/ImagePathHandler.php similarity index 70% rename from app/Exceptions/Handler.php rename to app/Exceptions/ImagePathHandler.php index b4cf3f267..90a171332 100644 --- a/app/Exceptions/Handler.php +++ b/app/Exceptions/ImagePathHandler.php @@ -4,94 +4,18 @@ namespace App\Exceptions; use App\Module\Base; use App\Module\Image; -use Illuminate\Database\Eloquent\ModelNotFoundException; -use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler; -use Illuminate\Support\Facades\Log; -use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use Throwable; -class Handler extends ExceptionHandler +/** + * 图片路径处理(原 Exceptions\Handler::ImagePathHandler,新结构下由 bootstrap/app.php + * 的 withExceptions 在 NotFoundHttpException 时调用) + */ +class ImagePathHandler { /** - * A list of the exception types that are not reported. - * - * @var array + * @param \Illuminate\Http\Request $request + * @return \Symfony\Component\HttpFoundation\BinaryFileResponse|null 命中返回图片响应,未命中返回 null(继续默认 404) */ - protected $dontReport = [ - // - ]; - - /** - * A list of the inputs that are never flashed for validation exceptions. - * - * @var array - */ - protected $dontFlash = [ - 'current_password', - 'password', - 'password_confirmation', - ]; - - /** - * Register the exception handling callbacks for the application. - * - * @return void - */ - public function register() - { - $this->reportable(function (Throwable $e) { - // - }); - } - - /** - * 将异常转换为 HTTP 响应。 - * @param $request - * @param Throwable $e - * @return array|\Illuminate\Http\JsonResponse|\Illuminate\Http\Response|\Symfony\Component\HttpFoundation\Response - * @throws Throwable - */ - public function render($request, Throwable $e) - { - if ($e instanceof NotFoundHttpException) { - if ($result = $this->ImagePathHandler($request)) { - return $result; - } - } - if ($e instanceof ApiException) { - return response()->json(Base::retError($e->getMessage(), $e->getData(), $e->getCode())); - } elseif ($e instanceof ModelNotFoundException) { - return response()->json(Base::retError('Interface error')); - } - return parent::render($request, $e); - } - - /** - * 重写report优雅记录 - * @param Throwable $e - * @throws Throwable - */ - public function report(Throwable $e) - { - if ($e instanceof ApiException) { - if ($e->isWriteLog()) { - Log::error($e->getMessage(), [ - 'code' => $e->getCode(), - 'data' => $e->getData(), - 'exception' => ' at ' . $e->getFile() . ':' . $e->getLine() - ]); - } - } else { - parent::report($e); - } - } - - /** - * 图片路径处理 - * @param $request - * @return \Illuminate\Http\JsonResponse|\Symfony\Component\HttpFoundation\BinaryFileResponse|null - */ - private function ImagePathHandler($request) + public static function render($request) { $path = $request->path(); diff --git a/app/Http/Kernel.php b/app/Http/Kernel.php deleted file mode 100644 index dd9df18f2..000000000 --- a/app/Http/Kernel.php +++ /dev/null @@ -1,68 +0,0 @@ - [ - \App\Http\Middleware\EncryptCookies::class, - \Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class, - \Illuminate\Session\Middleware\StartSession::class, - // \Illuminate\Session\Middleware\AuthenticateSession::class, - \Illuminate\View\Middleware\ShareErrorsFromSession::class, - \App\Http\Middleware\VerifyCsrfToken::class, - \Illuminate\Routing\Middleware\SubstituteBindings::class, - ], - - 'api' => [ - 'throttle:api', - \Illuminate\Routing\Middleware\SubstituteBindings::class, - ], - ]; - - /** - * The application's route middleware. - * - * These middleware may be assigned to groups or used individually. - * - * @var array - */ - protected $routeMiddleware = [ - 'auth' => \App\Http\Middleware\Authenticate::class, - 'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class, - 'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class, - 'can' => \Illuminate\Auth\Middleware\Authorize::class, - 'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class, - 'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class, - 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class, - 'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class, - 'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class, - - 'webapi' => \App\Http\Middleware\WebApi::class, - ]; -} diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php deleted file mode 100644 index 704089a7f..000000000 --- a/app/Http/Middleware/Authenticate.php +++ /dev/null @@ -1,21 +0,0 @@ -expectsJson()) { - return route('login'); - } - } -} diff --git a/app/Http/Middleware/EncryptCookies.php b/app/Http/Middleware/EncryptCookies.php deleted file mode 100644 index 033136ad1..000000000 --- a/app/Http/Middleware/EncryptCookies.php +++ /dev/null @@ -1,17 +0,0 @@ -check()) { - return redirect(RouteServiceProvider::HOME); - } - } - - return $next($request); - } -} diff --git a/app/Http/Middleware/TrimStrings.php b/app/Http/Middleware/TrimStrings.php deleted file mode 100644 index a8a252df4..000000000 --- a/app/Http/Middleware/TrimStrings.php +++ /dev/null @@ -1,19 +0,0 @@ -allSubdomainsOfApplicationUrl(), - ]; - } -} diff --git a/app/Http/Middleware/TrustProxies.php b/app/Http/Middleware/TrustProxies.php deleted file mode 100644 index e555b9d26..000000000 --- a/app/Http/Middleware/TrustProxies.php +++ /dev/null @@ -1,28 +0,0 @@ -getQuery()->rawSql()); }); + + $this->configureRateLimiting(); + $this->registerEvents(); + $this->registerObservers(); + } + + /** + * api 组限流(原 RouteServiceProvider::configureRateLimiting) + */ + protected function configureRateLimiting() + { + RateLimiter::for('api', function (Request $request) { + return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()); + }); + } + + /** + * 事件监听(原 EventServiceProvider::$listen) + */ + protected function registerEvents() + { + Event::listen(Registered::class, SendEmailVerificationNotification::class); + } + + /** + * 模型观察者(原 EventServiceProvider::boot) + */ + protected function registerObservers() + { + File::observe(FileObserver::class); + FileUser::observe(FileUserObserver::class); + Project::observe(ProjectObserver::class); + ProjectTask::observe(ProjectTaskObserver::class); + ProjectTaskContent::observe(ProjectTaskContentObserver::class); + ProjectTaskUser::observe(ProjectTaskUserObserver::class); + ProjectTaskVisibilityUser::observe(ProjectTaskVisibilityUserObserver::class); + ProjectUser::observe(ProjectUserObserver::class); + User::observe(UserObserver::class); + UserTag::observe(UserTagObserver::class); + UserTagRecognition::observe(UserTagRecognitionObserver::class); + WebSocketDialog::observe(WebSocketDialogObserver::class); + WebSocketDialogMsg::observe(WebSocketDialogMsgObserver::class); + WebSocketDialogUser::observe(WebSocketDialogUserObserver::class); } } diff --git a/app/Providers/AuthServiceProvider.php b/app/Providers/AuthServiceProvider.php deleted file mode 100644 index ce7449164..000000000 --- a/app/Providers/AuthServiceProvider.php +++ /dev/null @@ -1,30 +0,0 @@ - 'App\Policies\ModelPolicy', - ]; - - /** - * Register any authentication / authorization services. - * - * @return void - */ - public function boot() - { - $this->registerPolicies(); - - // - } -} diff --git a/app/Providers/BroadcastServiceProvider.php b/app/Providers/BroadcastServiceProvider.php deleted file mode 100644 index 395c518bc..000000000 --- a/app/Providers/BroadcastServiceProvider.php +++ /dev/null @@ -1,21 +0,0 @@ - [ - SendEmailVerificationNotification::class, - ], - ]; - - /** - * Register any events for your application. - * - * @return void - */ - public function boot() - { - File::observe(FileObserver::class); - FileUser::observe(FileUserObserver::class); - Project::observe(ProjectObserver::class); - ProjectTask::observe(ProjectTaskObserver::class); - ProjectTaskContent::observe(ProjectTaskContentObserver::class); - ProjectTaskUser::observe(ProjectTaskUserObserver::class); - ProjectTaskVisibilityUser::observe(ProjectTaskVisibilityUserObserver::class); - ProjectUser::observe(ProjectUserObserver::class); - User::observe(UserObserver::class); - UserTag::observe(UserTagObserver::class); - UserTagRecognition::observe(UserTagRecognitionObserver::class); - WebSocketDialog::observe(WebSocketDialogObserver::class); - WebSocketDialogMsg::observe(WebSocketDialogMsgObserver::class); - WebSocketDialogUser::observe(WebSocketDialogUserObserver::class); - } -} diff --git a/app/Providers/RouteServiceProvider.php b/app/Providers/RouteServiceProvider.php deleted file mode 100644 index 3bd3c81eb..000000000 --- a/app/Providers/RouteServiceProvider.php +++ /dev/null @@ -1,63 +0,0 @@ -configureRateLimiting(); - - $this->routes(function () { - Route::prefix('api') - ->middleware('api') - ->namespace($this->namespace) - ->group(base_path('routes/api.php')); - - Route::middleware('web') - ->namespace($this->namespace) - ->group(base_path('routes/web.php')); - }); - } - - /** - * Configure the rate limiters for the application. - * - * @return void - */ - protected function configureRateLimiting() - { - RateLimiter::for('api', function (Request $request) { - return Limit::perMinute(60)->by(optional($request->user())->id ?: $request->ip()); - }); - } -} diff --git a/artisan b/artisan index 67a3329b1..8e04b4224 100755 --- a/artisan +++ b/artisan @@ -1,53 +1,15 @@ #!/usr/bin/env php make(Illuminate\Contracts\Console\Kernel::class); - -$status = $kernel->handle( - $input = new Symfony\Component\Console\Input\ArgvInput, - new Symfony\Component\Console\Output\ConsoleOutput -); - -/* -|-------------------------------------------------------------------------- -| Shutdown The Application -|-------------------------------------------------------------------------- -| -| Once Artisan has finished running, we will fire off the shutdown events -| so that any final work may be done by the application before we shut -| down the process. This is the last thing to happen to the request. -| -*/ - -$kernel->terminate($input, $status); +// Bootstrap Laravel and handle the command... +$status = (require_once __DIR__.'/bootstrap/app.php') + ->handleCommand(new ArgvInput); exit($status); diff --git a/bootstrap/app.php b/bootstrap/app.php index 037e17df0..c542f5c6c 100644 --- a/bootstrap/app.php +++ b/bootstrap/app.php @@ -1,55 +1,75 @@ withRouting( + web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', + apiPrefix: 'api', + commands: __DIR__.'/../routes/console.php', + ) + ->withMiddleware(function (Middleware $middleware): void { + // PHP(Swoole)只在内网被 nginx 访问,外部无法直连,故信任内网代理。 + // 只采信 X-Forwarded-Proto:nginx 已用 $the_scheme 覆盖该头(值由 nginx 控制), + // 据此让 url() 实时跟随 https;host/for 一律不信,避免 Host 注入与 IP 伪造。 + $middleware->trustProxies(at: '*', headers: Request::HEADER_X_FORWARDED_PROTO); -/* -|-------------------------------------------------------------------------- -| Bind Important Interfaces -|-------------------------------------------------------------------------- -| -| Next, we need to bind some important interfaces into the container so -| we will be able to resolve them when needed. The kernels serve the -| incoming requests to this application from both the web and CLI. -| -*/ + $middleware->trimStrings(except: [ + 'current_password', + 'password', + 'password_confirmation', + ]); -$app->singleton( - Illuminate\Contracts\Http\Kernel::class, - App\Http\Kernel::class -); + $middleware->validateCsrfTokens(except: [ + // 接口部分 + 'api/*', -$app->singleton( - Illuminate\Contracts\Console\Kernel::class, - App\Console\Kernel::class -); + // 发布桌面端 + 'desktop/publish/', + ]); -$app->singleton( - Illuminate\Contracts\Debug\ExceptionHandler::class, - App\Exceptions\Handler::class -); + // api 组限流(限流规则定义在 AppServiceProvider::boot) + $middleware->throttleApi(); -/* -|-------------------------------------------------------------------------- -| Return The Application -|-------------------------------------------------------------------------- -| -| This script returns the application instance. The instance is given to -| the calling script so we can separate the building of the instances -| from the actual running of the application and sending responses. -| -*/ + $middleware->alias([ + 'webapi' => \App\Http\Middleware\WebApi::class, + ]); -return $app; + $middleware->redirectGuestsTo('/login'); + $middleware->redirectUsersTo('/home'); + }) + ->withExceptions(function (Exceptions $exceptions): void { + // /uploads/**.png/crop/... 动态裁剪与缩略图(命中则返回图片,否则走默认 404) + $exceptions->render(function (NotFoundHttpException $e, Request $request) { + return ImagePathHandler::render($request); + }); + + $exceptions->render(function (ApiException $e) { + return response()->json(Base::retError($e->getMessage(), $e->getData(), $e->getCode())); + }); + + $exceptions->render(function (ModelNotFoundException $e) { + return response()->json(Base::retError('Interface error')); + }); + + // ApiException 按 isWriteLog 决定是否记录,且不走默认 report + $exceptions->report(function (ApiException $e) { + if ($e->isWriteLog()) { + Log::error($e->getMessage(), [ + 'code' => $e->getCode(), + 'data' => $e->getData(), + 'exception' => ' at ' . $e->getFile() . ':' . $e->getLine() + ]); + } + })->stop(); + })->create(); diff --git a/bootstrap/providers.php b/bootstrap/providers.php new file mode 100644 index 000000000..38b258d18 --- /dev/null +++ b/bootstrap/providers.php @@ -0,0 +1,5 @@ + 'AES-256-CBC', - /* - |-------------------------------------------------------------------------- - | Autoloaded Service Providers - |-------------------------------------------------------------------------- - | - | The service providers listed here will be automatically loaded on the - | request to your application. Feel free to add your own services to - | this array to grant expanded functionality to your applications. - | - */ - - 'providers' => [ - - /* - * Laravel Framework Service Providers... - */ - Illuminate\Auth\AuthServiceProvider::class, - Illuminate\Broadcasting\BroadcastServiceProvider::class, - Illuminate\Bus\BusServiceProvider::class, - Illuminate\Cache\CacheServiceProvider::class, - Illuminate\Foundation\Providers\ConsoleSupportServiceProvider::class, - Illuminate\Cookie\CookieServiceProvider::class, - Illuminate\Database\DatabaseServiceProvider::class, - Illuminate\Encryption\EncryptionServiceProvider::class, - Illuminate\Filesystem\FilesystemServiceProvider::class, - Illuminate\Foundation\Providers\FoundationServiceProvider::class, - Illuminate\Hashing\HashServiceProvider::class, - Illuminate\Mail\MailServiceProvider::class, - Illuminate\Notifications\NotificationServiceProvider::class, - Illuminate\Pagination\PaginationServiceProvider::class, - Illuminate\Pipeline\PipelineServiceProvider::class, - Illuminate\Queue\QueueServiceProvider::class, - Illuminate\Redis\RedisServiceProvider::class, - Illuminate\Auth\Passwords\PasswordResetServiceProvider::class, - Illuminate\Session\SessionServiceProvider::class, - Illuminate\Translation\TranslationServiceProvider::class, - Illuminate\Validation\ValidationServiceProvider::class, - Illuminate\View\ViewServiceProvider::class, - - /* - * Package Service Providers... - */ - - /* - * Application Service Providers... - */ - App\Providers\AppServiceProvider::class, - App\Providers\AuthServiceProvider::class, - // App\Providers\BroadcastServiceProvider::class, - App\Providers\EventServiceProvider::class, - App\Providers\RouteServiceProvider::class, - - ], - - /* - |-------------------------------------------------------------------------- - | Class Aliases - |-------------------------------------------------------------------------- - | - | This array of class aliases will be registered when this application - | is started. However, feel free to register as many as you wish as - | the aliases are "lazy" loaded so they don't hinder performance. - | - */ - - 'aliases' => [ - - 'App' => Illuminate\Support\Facades\App::class, - 'Arr' => Illuminate\Support\Arr::class, - 'Artisan' => Illuminate\Support\Facades\Artisan::class, - 'Auth' => Illuminate\Support\Facades\Auth::class, - 'Blade' => Illuminate\Support\Facades\Blade::class, - 'Broadcast' => Illuminate\Support\Facades\Broadcast::class, - 'Bus' => Illuminate\Support\Facades\Bus::class, - 'Cache' => Illuminate\Support\Facades\Cache::class, - 'Config' => Illuminate\Support\Facades\Config::class, - 'Cookie' => Illuminate\Support\Facades\Cookie::class, - 'Crypt' => Illuminate\Support\Facades\Crypt::class, - 'Date' => Illuminate\Support\Facades\Date::class, - 'DB' => Illuminate\Support\Facades\DB::class, - 'Eloquent' => Illuminate\Database\Eloquent\Model::class, - 'Event' => Illuminate\Support\Facades\Event::class, - 'File' => Illuminate\Support\Facades\File::class, - 'Gate' => Illuminate\Support\Facades\Gate::class, - 'Hash' => Illuminate\Support\Facades\Hash::class, - 'Http' => Illuminate\Support\Facades\Http::class, - 'Lang' => Illuminate\Support\Facades\Lang::class, - 'Log' => Illuminate\Support\Facades\Log::class, - 'Mail' => Illuminate\Support\Facades\Mail::class, - 'Notification' => Illuminate\Support\Facades\Notification::class, - 'Password' => Illuminate\Support\Facades\Password::class, - 'Queue' => Illuminate\Support\Facades\Queue::class, - 'Redirect' => Illuminate\Support\Facades\Redirect::class, - // 'Redis' => Illuminate\Support\Facades\Redis::class, - 'Request' => Illuminate\Support\Facades\Request::class, - 'Response' => Illuminate\Support\Facades\Response::class, - 'Route' => Illuminate\Support\Facades\Route::class, - 'Schema' => Illuminate\Support\Facades\Schema::class, - 'Session' => Illuminate\Support\Facades\Session::class, - 'Storage' => Illuminate\Support\Facades\Storage::class, - 'Str' => Illuminate\Support\Str::class, - 'URL' => Illuminate\Support\Facades\URL::class, - 'Validator' => Illuminate\Support\Facades\Validator::class, - 'View' => Illuminate\Support\Facades\View::class, - - ], - ]; diff --git a/public/index.php b/public/index.php index 66ea93cd0..ee8f07e99 100644 --- a/public/index.php +++ b/public/index.php @@ -1,55 +1,20 @@ make(Kernel::class); - -$response = tap($kernel->handle( - $request = Request::capture() -))->send(); - -$kernel->terminate($request, $response); +$app->handleRequest(Request::capture());