fix(core,admin-template): GC 스케줄 허용목록 동기 + 스토리지 잔존물 GC 신설 + 스케줄 모달 저장 회귀 수정

공개 후속 전수 조사의 실작업:
- attachments:prune-orphans 관리자 예약 허용목록 등재 + 가드 테스트 도출 규칙을
 런타임 validator 와 일치 (옵션 포함 키는 영원히 매칭되지 않는 죽은 항목)
- layout-previews:cleanup 을 gc-schedules 가드 구간으로 편입 (+onOneServer)
- storage:prune-leftovers 신설: 중단된 업데이트/설치의 _pending 스테이징·코어 임시·
 vendor 스테이징(3일)과 오래된 백업(30일, 최신 1개 상시 보존), 레거시 browser.log 회수
 — 산출물 누적 + 회수 경로 부재( 동형) 7건 해소
- 언어팩 설치 임시 디렉토리 정리를 finally 로 이동 (설치 성공 시 100% 누수)
- 브라우저 콘솔 로그를 daily(7일) 로테이션으로 전환 (단일 파일 989MB 실측)
- 검수 중 발견한 스케줄 등록/수정 모달 저장 불능 수정 (Form id 부재 + 커스텀
 Select/Toggle 값 미전송) + 동형 재발 차단 audit 룰 2종 신설
This commit is contained in:
HeuJung committed 2026-08-17 15:17:20 +09:00
1 parent db49e8b9b8
commit febffb173e
21 files changed
+1185 -29

No files matched your search

@@ -13,6 +13,7 @@ use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Http;
use Tests\TestCase;
/**
@@ -768,6 +769,51 @@ class LanguagePackServiceTest extends TestCase
}
}
/**
* URL 설치가 성공해도 다운로드 임시 디렉토리(storage/app/temp/{uuid})가 남지 않는다.
*
* 임시 디렉토리 삭제가 catch 경로에만 있으면 실패 시에는 정리되고 성공 시에는
* 100% 잔존한다 — 예외도 로그도 없이 설치할 때마다 ZIP 사본이 쌓이는 누수다.
* 성공 경로를 실제로 통과시켜 잔존물이 0건임을 고정한다.
*
* @scenario vector=install_from_url, actor_permission=cli_system
*
* @effects successful_install_leaves_no_temp_directory
*/
public function test_install_from_url_success_leaves_no_temp_directory(): void
{
$identifier = 'tmpleak-core-ja';
$zipPath = tempnam(sys_get_temp_dir(), 'lp_zip');
$zip = new \ZipArchive;
$zip->open($zipPath, \ZipArchive::OVERWRITE);
$zip->addFromString(
'language-pack.json',
(string) json_encode($this->coreJaManifest($identifier, 'tmpleak', '1.0.0'), JSON_UNESCAPED_UNICODE)
);
$zip->close();
Http::fake(['https://packs.example.com/*' => Http::response((string) file_get_contents($zipPath), 200)]);
$tempRoot = storage_path('app/temp');
$before = File::isDirectory($tempRoot) ? File::directories($tempRoot) : [];
try {
$pack = $this->service->installFromUrl('https://packs.example.com/pack.zip', null);
$this->assertSame($identifier, $pack->identifier);
$after = File::isDirectory($tempRoot) ? File::directories($tempRoot) : [];
$this->assertSame(
[],
array_values(array_diff($after, $before)),
'설치가 성공했는데 storage/app/temp 에 다운로드 임시 디렉토리가 남았다'
);
} finally {
@unlink($zipPath);
File::deleteDirectory(base_path('lang-packs/'.$identifier));
}
}
/**
* 코어 ja 번들 픽스처용 manifest 를 만듭니다.
*