설치 단계와 코어 부팅의 정책이 엇갈려 있었다. 인스톨러는 root 를 자유롭게 허용하면서, 설치 후 부팅은 같은 값을 보고 확장 로딩 전체를 조용히 스킵했다. 설치는 성공하지만 이후 깨진 화면만 뜨고 원인은 로그 한 줄에만 남았다. 판정을 프레임워크 의존성 0 인 공용 SSoT 로 모아 인스톨러와 코어가 같은 답을 내게 하고, 설치 단계에서 서버 2경로로 차단하며, 설치 후 변경은 전용 에러 페이지로 드러낸다. 가드를 boot 가 아닌 미들웨어에 두어 artisan 이 죽지 않게 해 복구 수단을 남겼다. admin 은 일반 권한 운영 계정 오차단을 막기 위해 차단 목록에서 제외했다. OPcache 는 상태 조회 경로 자체가 없어 성능 저하를 인지할 방법이 없었다. 설치 단계 경고(비차단)와 관리자 정보 탭 표시를 같은 판정 SSoT 로 추가했다. 확장 로드 여부만으로는 부족해 opcache.enable 지시자까지 확인하며, ini_get 이 차단된 환경은 "확인 불가"로 폴백해 경고도 차단도 하지 않는다.
83 lines
2.5 KiB
PHP
83 lines
2.5 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Middleware;
|
|
|
|
use App\Http\Middleware\Concerns\DetectsRequestLocale;
|
|
use Closure;
|
|
use Illuminate\Foundation\Http\MaintenanceModeBypassCookie;
|
|
use Illuminate\Http\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class MaintenanceModePage
|
|
{
|
|
use DetectsRequestLocale;
|
|
|
|
/**
|
|
* Maintenance 모드일 때 전용 정적 페이지를 렌더링합니다.
|
|
*
|
|
* DB, API, JS 에셋에 의존하지 않는 자체 완결 Blade 페이지를 반환합니다.
|
|
* 메인터넌스 모드는 시스템 불안정 시(DB 마이그레이션, 코어 업데이트 등) 사용되므로
|
|
* 외부 의존성 없이 동작해야 합니다.
|
|
*
|
|
* @param Request $request HTTP 요청
|
|
* @param Closure $next 다음 미들웨어
|
|
* @return Response HTTP 응답
|
|
*/
|
|
public function handle(Request $request, Closure $next): Response
|
|
{
|
|
if (! app()->isDownForMaintenance()) {
|
|
return $next($request);
|
|
}
|
|
|
|
// secret 쿠키가 있으면 통과 (관리자 접근)
|
|
if ($this->hasValidBypassCookie($request)) {
|
|
return $next($request);
|
|
}
|
|
|
|
// 로케일 감지 (SetLocale 미들웨어가 실행되지 않으므로 자체 감지)
|
|
// API/HTML 분기보다 먼저 적용해야 JSON 응답 메시지도 ja 등 활성 언어팩 로케일을 반영한다
|
|
$locale = $this->detectLocale($request);
|
|
app()->setLocale($locale);
|
|
|
|
// API 요청은 JSON 응답
|
|
if ($request->expectsJson() || $request->is('api/*')) {
|
|
return response()->json([
|
|
'success' => false,
|
|
'message' => __('maintenance.message'),
|
|
], 503);
|
|
}
|
|
|
|
// 정적 메인터넌스 페이지 렌더링 (DB/API/JS 무의존)
|
|
return response()->view('maintenance', [], 503);
|
|
}
|
|
|
|
/**
|
|
* 유효한 bypass 쿠키가 있는지 확인합니다.
|
|
*
|
|
* @param Request $request HTTP 요청
|
|
* @return bool
|
|
*/
|
|
protected function hasValidBypassCookie(Request $request): bool
|
|
{
|
|
$downFilePath = storage_path('framework/down');
|
|
|
|
if (! file_exists($downFilePath)) {
|
|
return false;
|
|
}
|
|
|
|
$data = json_decode(file_get_contents($downFilePath), true);
|
|
|
|
if (! isset($data['secret'])) {
|
|
return false;
|
|
}
|
|
|
|
$cookie = $request->cookie('laravel_maintenance');
|
|
|
|
if (! $cookie) {
|
|
return false;
|
|
}
|
|
|
|
return MaintenanceModeBypassCookie::isValid($cookie, $data['secret']);
|
|
}
|
|
}
|