134 lines
5.3 KiB
JSON
134 lines
5.3 KiB
JSON
{
|
|
"schema": "vignette.avatar-image-resilience-recovery.v1",
|
|
"evidence_id": "AVATAR-IMAGE-RESILIENCE-RECOVERY-2026-08-29",
|
|
"captured_at": "2026-08-29T18:00:42+09:00",
|
|
"privacy": {
|
|
"raw_user_uuid_recorded": false,
|
|
"raw_filename_recorded": false,
|
|
"email_recorded": false,
|
|
"resource_identity": "SHA-256 식별자만 기록"
|
|
},
|
|
"incident": {
|
|
"root_cause": {
|
|
"runtime_mode": "Python uvicorn 직접 실행 프로세스",
|
|
"process_working_directory": "<release-root>/apps/api",
|
|
"user_upload_dir_environment": "미설정",
|
|
"effective_user_upload_dir": "cwd 기준 상대경로 uploads",
|
|
"effective_storage_location": "<release-root>/apps/api/uploads",
|
|
"docker_named_upload_volume_used_by_public_api": false,
|
|
"failure_mechanism": "DB 경로는 승격 뒤에도 남았지만 업로드 파일은 이전 detached release root에 머물렀다. 새로 승격된 Python 직접 실행 런타임이 같은 상대 업로드 경로를 서로 다른 작업 디렉터리에서 해석해 파일을 찾지 못했다."
|
|
},
|
|
"initial_public_failure": {
|
|
"phase": "복구 전",
|
|
"http_status": 404,
|
|
"response_content_type": "application/json",
|
|
"browser_img_complete": true,
|
|
"browser_img_natural_width": 0,
|
|
"browser_img_natural_height": 0
|
|
},
|
|
"problem_object": {
|
|
"path_sha256": "484dcee08b007741872ac48b49421fd45bf5a60b273e7a64c5b9a66dac85138a",
|
|
"bytes": 28208,
|
|
"content_sha256": "c5d06061bfa635d65f8285df242c5c9820dadcffcd785ed3f9b8619be24212c7"
|
|
}
|
|
},
|
|
"recovery": {
|
|
"source_release_commit_prefix": "bf5f7352cee0",
|
|
"current_release_commit_prefix": "dba9b75a3887",
|
|
"objects": [
|
|
{
|
|
"object_key": "recovered_object_1",
|
|
"bytes": 18395,
|
|
"content_sha256": "854403aed367db2d459e482a5e980fd22a846738b6034068cd7b4e80bbb7f34d"
|
|
},
|
|
{
|
|
"object_key": "problem_object",
|
|
"bytes": 28208,
|
|
"content_sha256": "c5d06061bfa635d65f8285df242c5c9820dadcffcd785ed3f9b8619be24212c7"
|
|
}
|
|
],
|
|
"object_count": 2,
|
|
"total_bytes": 46603,
|
|
"source_and_current_content_hashes_match": true,
|
|
"current_release_storage_path_kind": "release root 내부의 일반 디렉터리",
|
|
"current_release_git_dirty_paths": 0,
|
|
"final_public_probe": {
|
|
"http_status": 200,
|
|
"content_type": "image/jpeg",
|
|
"bytes": 28208,
|
|
"content_sha256": "c5d06061bfa635d65f8285df242c5c9820dadcffcd785ed3f9b8619be24212c7",
|
|
"matches_recovered_problem_object": true
|
|
},
|
|
"authenticated_browser_dom": {
|
|
"route": "/admin/users",
|
|
"image_complete": true,
|
|
"natural_width": 512,
|
|
"natural_height": 512,
|
|
"computed_display": "block",
|
|
"visible": true,
|
|
"raw_source_recorded": false
|
|
}
|
|
},
|
|
"ui_resilience": {
|
|
"files": [
|
|
"apps/web/src/components/avatar/ResilientImage.tsx",
|
|
"apps/web/src/components/shell/Topbar.tsx",
|
|
"apps/web/src/pages/Settings.tsx",
|
|
"apps/web/src/pages/Onboarding.tsx",
|
|
"apps/web/src/lib/auth.tsx",
|
|
"apps/web/e2e/image-resilience.spec.ts",
|
|
"apps/web/e2e/uc-settings-consent.spec.ts"
|
|
],
|
|
"covered_states": [
|
|
"유효한 외부 HTTPS 이미지",
|
|
"API 상대경로 이미지",
|
|
"빈 소스",
|
|
"허용하지 않는 비웹 scheme",
|
|
"HTTP 404",
|
|
"디코딩 또는 로드 실패",
|
|
"지연 응답 중 0x0 비노출 로딩 상태",
|
|
"업로드 뒤 소스 교체",
|
|
"접근 가능한 이니셜 fallback"
|
|
],
|
|
"verification": {
|
|
"typecheck": {
|
|
"command": "npm run typecheck",
|
|
"result": "passed"
|
|
},
|
|
"production_build": {
|
|
"command": "npm run build",
|
|
"result": "passed",
|
|
"vite_modules_transformed": 148,
|
|
"deterministic_runs": 2,
|
|
"dist_file_count": 601,
|
|
"dist_manifest_sha256": "35c48fbf844efd0514282b76937bbef3b1f173992313c7d377363d71f0806d89"
|
|
},
|
|
"design_ssot": {
|
|
"command": "npm run check:design-ssot",
|
|
"result": "passed"
|
|
},
|
|
"focused_e2e": {
|
|
"command": "npx playwright test e2e/image-resilience.spec.ts e2e/uc-settings-consent.spec.ts --project=chromium-desktop --project=chromium-mobile",
|
|
"result": "40/40 passed"
|
|
},
|
|
"layout_and_tabs_e2e": "17/17 passed",
|
|
"session_layout_e2e": "8/8 passed",
|
|
"scoped_diff_check": "passed"
|
|
},
|
|
"release_candidate": {
|
|
"base_commit": "5bf89ff4ee971940882695d27f153e1460df295e",
|
|
"commit": "35a62fda90d3ba56707acfeb3fb94090d3949f8e",
|
|
"tree": "d8634141289b6a0007954671ece5fb7dba9c2881",
|
|
"author": "Yun Chan",
|
|
"clean": true,
|
|
"protected_api_or_generated_paths_changed": 0,
|
|
"remote_push_and_pages_deploy": "pending_explicit_external_write_approval"
|
|
}
|
|
},
|
|
"stable_upload_contract": {
|
|
"status": "pending",
|
|
"current_recovery_scope": "현재 release root에 대한 시점 복구",
|
|
"remaining_risk": "USER_UPLOAD_DIR가 cwd 상대경로인 동안 이후 승격이나 다른 release root 재시작에서 업로드가 다시 고립될 수 있다.",
|
|
"required_follow_up": "사용자 업로드를 release root 밖의 절대 영속 상태 디렉터리로 옮기고, 모든 부팅·watchdog 진입점에서 USER_UPLOAD_DIR를 주입·검증한 뒤 inventory, 콘텐츠 해시 검증, 중복 제거, rollback, 공개 재시작 증거를 포함해 마이그레이션한다."
|
|
}
|
|
}
|