파일 및 이미지
사용자 파일은 BLOB이 아닌 디스크에 저장됩니다. 원본은 data/files/,
재생성 가능한 썸네일은 data/cache/thumbnails/에 있으며, 모든 쓰기는
원자적입니다.
원본과 캐시
구분은 엄격합니다.
- 원본 —
data/files/{avatars,backgrounds,attachments,audio,generated}/. 원본은 절대 수정되지 않으며 캐시 유지 관리가 절대 삭제하지 않습니다. - 캐시 —
data/cache/thumbnails/. 썸네일은 재생성 가능하고 콘텐츠 주소로 지정됩니다.
캐시를 지워도 원본은 제거되지 않습니다. 누락된 썸네일은 원본에서 자동으로 재생성됩니다.
이미지 가져오기 파이프라인
이미지 가져오기는 고정된 파이프라인을 따릅니다.
- 크기, MIME 유형, 확장자를 검증합니다.
- 콘텐츠 해시(SHA-256)를 계산합니다.
- 원본을 손실 없이 콘텐츠 주소로 저장합니다(
{sha256}{ext}). 콘텐츠로 중복이 제거됩니다. - 갤러리, 목록, 미리보기용 저해상도 썸네일을 생성합니다.
- 썸네일을
data/cache/thumbnails/에 저장합니다. - 각 썸네일을 원본 해시, 대상 크기, 알고리즘 버전으로 키를 지정합니다.
{hash}-{size}-v{algorithmVersion}형식입니다. - 키가 바뀌지 않은 썸네일은 재생성하지 않습니다.
- 썸네일로 충분한 곳에서 원본을 로드하지 않습니다.
- 썸네일이 없으면 캐시를 자동으로 재구축합니다.
- 캐시 정리는 원본을 건드리지 않습니다.
원자적 쓰기
모든 파일 쓰기는 임시 파일 후 이름 변경(rename) 방식으로 이루어집니다. 중간에 크래시가 나도 부분적으로 기록된 파일이 남지 않습니다. 이는 원본, 썸네일, 다운로드된 토크나이저 파일 모두에 적용됩니다.
캐릭터 갤러리
갤러리 이미지는 owner_type = character.gallery로 attachments
테이블을 재사용합니다. 메타데이터 행은 원본과 썸네일의 URL을
보유하고, 바이트는 콘텐츠 주소 지정된 files/avatars/에 남습니다.
갤러리에서 이미지를 제거하면 첨부 레코드만 삭제되고 원본 파일은
삭제되지 않습니다. 작업은 되돌릴 수 있고 중복 제거는 유지됩니다.
채팅 배경
files/backgrounds/가 진실의 원천입니다. 목록은 디렉터리 스캔으로
만들어지므로 SillyTavern에서 가져온 배경도 전송 단계 없이
나타납니다. 업로드된 파일은 콘텐츠 주소로 저장되고 절대 수정되지
않습니다.
배경 썸네일은 콘텐츠가 아닌 파일 이름의 SHA-256으로 키가 지정된
cache/thumbnails/에 있습니다. 덕분에 임의 이름의 SillyTavern 가져오기
파일도 썸네일을 얻고, 업로드·목록·삭제가 하나의 키에서
이루어집니다. 디코딩할 수 없거나 64 MiB를 초과하는 파일은 썸네일
없이 목록에 표시되며 원본은 계속 사용할 수 있습니다. 배경을 삭제하면
원본과 캐시된 썸네일이 모두 제거됩니다.
캐릭터 카드 가져오기
POST /api/v2/characters/import는 JSON Character Card V1/V2와 chara
메타데이터가 있는 PNG를 받습니다. 입력은 25 MiB로 제한되며 콘텐츠로
감지됩니다. 원본 파일 전체의 SHA-256이 ext._st2.importHash에
저장되며, 같은 파일을 다시 가져오면 기존 레코드를 반환합니다. PNG는
이미지 디코더로 검증됩니다. 원본은 files/avatars/에 원자적으로
기록되고 WebP 썸네일이 생성되며, 썸네일이 없으면 다음 읽기에서
원본으로 재구축됩니다.
캐시 유지 관리
진단 화면은 DELETE /api/v2/diagnostics/cache를 호출하며, 이는
cache/thumbnails/의 파일과 해당 cache_metadata 행만 제거합니다.
cache/ 루트는 유지되므로 활성 마이그레이션 스테이징 디렉터리가
중단되지 않습니다. 결과는 제거된 파일의 수와 크기를 보고하며,
재실행은 안전하고 0을 반환합니다.