Files
Gnuboard7/routes/web.php
T
HeuJung b4ed33aa44 feat(core): 정적 게시 수명주기 보완 — 빌드 파괴·권한·무결성
https://github.com/gnuboard/g7/issues/122 제보자의 추가 지적 2건(빌드가 게시본을
지움 / CLI 최초 게시 후 웹 재생성 불가)에 대응하고, 같은 근본 원인을 공유하는
결함을 전수조사로 함께 고친다.

- 빌드가 자기 산출물만 교체한다: 루트 vite `emptyOutDir: false`
 기본값 true 는 폴백 없는 코어 3번들과 배달된 immutable URL 의 게시본을 함께 지웠다
- 게시 트리 권한을 웹이 이어받는다: 병합 전 프리플라이트 + 부모 그룹 상속·g+w
 종전 소유권 정상화는 root 축만 덮어 비-root CLI 계정은 무방비였다
- 갱신 → 생성 → 완전성 확인을 한 묶음으로: 기록 바이트 대조, .old 원자 스왑,
 rename 일시 거부 재시도, 프론트 JSON 파싱 검증
- 실패를 억제하고 기록한다: 버전 한정 실패 마커 + 사유별 대시보드 알림
 + ext-static:status 점검 커맨드
- 파생: catch-all 제외 목록을 에셋 화이트리스트 합집합에서 파생(mjs·webp·otf 누락),
 번들 디스크 쓰기 fail-soft·빈 번들 503, 활성 디렉토리 prune 회피
2026-08-28 08:02:22 +09:00

62 lines
2.7 KiB
PHP

<?php
use App\Http\Controllers\Api\Public\SitemapController;
use App\Seo\TemplateRouteResolver;
use App\Support\StaticExtensionPattern;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Route;
// 개발용 라우트 - 디버그 모드 + 관리자 인증 필수
Route::get('/dev', function () {
// 1. 디버그 모드 확인
if (! config('app.debug')) {
abort(404);
}
// 2. 관리자 인증 확인 (세션 기반 - stateful 미들웨어로 로그인 시 세션 생성)
$user = Auth::guard('web')->user();
if (! $user || ! $user->is_super) {
abort(403, '관리자 권한이 필요합니다.');
}
return view('dev-dashboard');
})->name('web.dev');
// Admin 라우트 - admin 템플릿 의존성 검증
Route::prefix('admin')
->middleware('template.dependencies:admin')
->group(function () {
Route::get('/{any?}', function (Request $request) {
// 미등록 경로는 SPA 셸 본문 + HTTP 404 (soft 404 방지 — 공개#47).
// 본문은 그대로라 클라이언트가 404 레이아웃을 렌더하고, 봇은 404 색인 제외.
$path = '/'.ltrim($request->getPathInfo(), '/');
if (! app(TemplateRouteResolver::class)->routeExists($path, 'admin')) {
return response(view('admin'), 404);
}
return view('admin');
})->where('any', StaticExtensionPattern::catchAllExclusion());
});
// Sitemap XML 라우트
// 자식 경로는 SitemapFileStore::childUrl() 이 sitemapindex 의 <loc> 에 기록하는 값과 일치해야 한다.
// gzip 여부는 manifest 가 결정하므로 .xml / .xml.gz 를 같은 액션에서 처리한다.
Route::get('/sitemap.xml', [SitemapController::class, 'index'])->name('web.sitemap');
Route::get('/sitemap-{n}.xml', [SitemapController::class, 'child'])->whereNumber('n')->name('web.sitemap.child');
Route::get('/sitemap-{n}.xml.gz', [SitemapController::class, 'child'])->whereNumber('n')->name('web.sitemap.child.gz');
// User 라우트 - user 템플릿 의존성 검증 + SEO 봇 감지
Route::middleware(['template.dependencies:user', 'seo'])
->group(function () {
Route::get('/{any?}', function (Request $request) {
// 미등록 경로는 SPA 셸 본문 + HTTP 404 (soft 404 방지 — 공개#47).
$path = '/'.ltrim($request->getPathInfo(), '/');
if (! app(TemplateRouteResolver::class)->routeExists($path, 'user')) {
return response(view('app'), 404);
}
return view('app');
})->where('any', '(?!admin)(?!api)(?!plugins)'.StaticExtensionPattern::catchAllExclusion());
});