diff --git a/routes/web.php b/routes/web.php index dda3589..2ee330e 100644 --- a/routes/web.php +++ b/routes/web.php @@ -18,16 +18,38 @@ Route::get('/', function () { }); /* -| 后台 Vue SPA(public/admin):子路径需回退到 index.html,否则刷新或直接打开 -| /admin/xxx 会得到 404。若静态资源已由 Nginx 直出,通常不会进入此路由。 +| 后台 Vue SPA(public/admin):子路径需回退到 index.html,否则刷新 /admin/venues 等会得到 404。 +| 静态文件(assets/*)若存在则直出;其余路径统一返回 index.html。 +| index 使用字符串响应而非 BinaryFileResponse,避免部分 Nginx + X-Sendfile 配置下返回 500。 */ Route::get('/admin/{path?}', function (?string $path = null) { + $adminBase = realpath(public_path('admin')); + if ($adminBase === false || ! is_dir($adminBase)) { + abort(503, 'Admin frontend is not deployed (missing public/admin).'); + } + if ($path !== null && $path !== '') { - $candidate = public_path('admin/'.$path); - if (is_file($candidate)) { + $relative = str_replace(['/', '\\'], DIRECTORY_SEPARATOR, $path); + $candidate = realpath($adminBase.DIRECTORY_SEPARATOR.$relative); + if ($candidate !== false + && str_starts_with($candidate, $adminBase) + && is_file($candidate) + && is_readable($candidate)) { return response()->file($candidate); } } - return response()->file(public_path('admin/index.html')); + $index = $adminBase.DIRECTORY_SEPARATOR.'index.html'; + if (! is_file($index) || ! is_readable($index)) { + abort(503, 'Admin index.html is missing or unreadable.'); + } + + $html = @file_get_contents($index); + if ($html === false) { + abort(503, 'Cannot read admin index.html.'); + } + + return response($html, 200, [ + 'Content-Type' => 'text/html; charset=UTF-8', + ]); })->where('path', '.*');