diff --git a/docs/history.md b/docs/history.md index 449021e..1dfc597 100644 --- a/docs/history.md +++ b/docs/history.md @@ -1,5 +1,11 @@ # 의사결정 이력 +## 2026-05-12 v0.0.92 + +### 프로필 썸네일 해제와 다운로드 + +서버는 이미 디스크를 지우지 않지만, 설정 화면이 `PUT /api/auth/profile`로만 `avatarUrl`을 비울 때는 메타 분리가 빠져 관리자 목록과 체감이 어긋날 수 있어 `DELETE /api/auth/avatar`와 같은 `removeManagedAvatarAsset` 호출을 맞췄다. 관리자 미디어 모달에 다운로드를 넣어 원본 확인을 쉽게 했다. + ## 2026-05-12 v0.0.91 ### 썸네일 미사용 자산과 업로드 파일명 diff --git a/docs/map.md b/docs/map.md index 98f8c3f..590b5b4 100644 --- a/docs/map.md +++ b/docs/map.md @@ -81,7 +81,7 @@ | pages/admin/pages/index.vue | 페이지 목록 | | pages/admin/pages/new.vue | 페이지 작성, 저장 토스트 | | pages/admin/pages/[id].vue | 페이지 수정, 저장/삭제 토스트 | -| pages/admin/media/index.vue | 미디어 관리, **미디어 라이브러리/썸네일** 탭, 검색은 파일명·게시물 사용처 제목만, 라이브러리: 폴더 트리·드래그 이동 등, 썸네일: 미참조 파일 삭제·이름 변경 가능, 상세 모달(연결 회원·폴더 편집) | +| pages/admin/media/index.vue | 미디어 관리, **미디어 라이브러리/썸네일** 탭, 검색은 파일명·게시물 사용처 제목만, 라이브러리: 폴더 트리·드래그 이동 등, 썸네일: 미참조 파일 삭제·이름 변경 가능, 상세 모달(연결 회원·폴더 편집·**다운로드**) | | pages/admin/navigation/index.vue | 메뉴/네비게이션 관리(변경 시에만 메뉴 저장 활성) | | pages/admin/tags/index.vue | 태그 관리(메인 태그 드래그 정렬·일반 강등, 일반 태그 검색/메인 전환/삭제), 액션 피드백 토스트, 순서 변경 시에만 정렬 저장 활성 | | pages/admin/tags/new.vue | 태그 생성 | @@ -123,7 +123,7 @@ | server/api/auth/me.get.js | 회원 세션 조회 API | | server/api/auth/logout.post.js | 회원 로그아웃 API | | server/api/auth/profile.get.js | 회원 프로필 조회 API | -| server/api/auth/profile.put.js | 회원 프로필 수정 API | +| server/api/auth/profile.put.js | 회원 프로필 수정 API(닉네임·`avatarUrl`; 관리 썸네일 URL 교체 시 메타만 분리) | | server/api/auth/avatar.post.js | 회원 썸네일 업로드 API(WebP 변환, 최소 해상도 검증, 중앙 1:1 강제 크롭, 품질 보정, `media_metadata` 논리 폴더 `썸네일`) | | server/api/auth/avatar.delete.js | 회원 썸네일 삭제 API | | server/api/auth/check-username.get.js | 닉네임 중복 확인 API | diff --git a/docs/spec.md b/docs/spec.md index 207d0bf..5eb69f5 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -355,7 +355,7 @@ components/content/ - `GET /api/auth/me` - 현재 회원 세션 조회 - `POST /api/auth/logout` - 회원 로그아웃 - `GET /api/auth/profile` - 회원 설정 조회 -- `PUT /api/auth/profile` - 회원 프로필 수정(닉네임, 썸네일) +- `PUT /api/auth/profile` - 회원 프로필 수정(닉네임, `avatarUrl`). 이전 값이 `/uploads/members/avatars/` URL이고 새 값과 달라지면 `removeManagedAvatarAsset`으로 **메타만** 끊고 디스크 파일은 유지한다(`DELETE /api/auth/avatar`와 동일한 자산 정리 규칙). - `POST /api/auth/avatar` - 회원 썸네일 이미지 업로드 - `DELETE /api/auth/avatar` - 회원 썸네일 제거 - `GET /api/auth/check-username?username=` - 닉네임 중복 확인 @@ -574,6 +574,7 @@ components/content/ - 미디어 라이브러리 탭에서만 선택한 미디어를 폴더로 드래그하면 해당 미디어들의 폴더 경로가 일괄 변경된다. - 관리자 미디어 화면 검색은 **저장 파일명**과 게시물·페이지 **사용처 제목**만 대상으로 한다(URL 경로·논리 폴더명 문자열은 검색에 쓰지 않는다). - 미디어 파일 경로, 사용 현황(라이브러리), 연결 회원(썸네일 탭), 용량 등 세부 정보는 상세 모달에서 표시한다. +- 상세 모달의 **다운로드**는 공개 `/uploads/...` URL을 `download` 속성으로 브라우저에 내려받는다(썸네일·게시물 이미지 공통). - 미디어 폴더는 실제 파일 경로를 옮기지 않고 `media_metadata` 테이블에 URL별 경로 메타데이터로 저장한다. - 글쓰기 미디어 선택 창은 업로드 미디어 목록에서 이미지를 선택해 단일 이미지 또는 갤러리에 삽입한다. - 미디어 사용 현황은 게시물/페이지의 대표 이미지와 본문 내 URL을 기준으로 표시한다. diff --git a/docs/update.md b/docs/update.md index 77691ca..9702e0c 100644 --- a/docs/update.md +++ b/docs/update.md @@ -1,5 +1,11 @@ # 업데이트 이력 +## v0.0.92 + +- 회원 `PUT /api/auth/profile`에서 관리 썸네일 URL이 바뀌거나 비워질 때도 `removeManagedAvatarAsset`으로 메타만 분리해, 해제 후에도 디스크·썸네일 탭 목록과 일치하도록 정리. +- 관리자 미디어 상세 모달에 **다운로드** 버튼 추가. +- 썸네일 탭 안내: 프로필 해제 시에도 파일이 삭제되지 않음·목록 갱신은 새로고침을 명시. + ## v0.0.91 - 회원 썸네일 교체·삭제·탈퇴 시 이전 파일은 디스크에 남기고 `media_metadata`만 제거해, 관리자 썸네일 탭에서 미사용 자산을 구분·삭제할 수 있게 함. diff --git a/package.json b/package.json index 4383793..4d19466 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sori.studio", - "version": "0.0.91", + "version": "0.0.92", "private": true, "type": "module", "imports": { diff --git a/pages/admin/media/index.vue b/pages/admin/media/index.vue index a23095d..6a3b83e 100644 --- a/pages/admin/media/index.vue +++ b/pages/admin/media/index.vue @@ -90,6 +90,27 @@ const formatDateTime = (iso) => { } } +/** + * 상세 모달에서 선택된 미디어를 브라우저로 내려받는다. + * @returns {void} + */ +const downloadSelectedMedia = () => { + const item = selectedMedia.value + + if (!item?.url || !import.meta.client) { + return + } + + const anchor = document.createElement('a') + anchor.href = item.url + anchor.download = item.name || 'image' + anchor.rel = 'noopener noreferrer' + anchor.target = '_blank' + document.body.appendChild(anchor) + anchor.click() + document.body.removeChild(anchor) +} + const { data: mediaFolders, refresh: refreshMediaFolders } = await useFetch('/admin/api/media-folders', { default: () => ['미분류'] }) @@ -635,7 +656,7 @@ const deleteMedia = async (item) => { {{ thumbnailMediaItems.length }}

- 회원 프로필에서 쓰는 이미지는 연결 회원이 있을 때만 삭제·이름 변경이 막힙니다. 프로필에서 바꾸거나 해제된 파일은 이 목록에 남으며, 관리자가 직접 정리할 수 있습니다. + 회원 프로필에서 쓰는 이미지는 연결 회원이 있을 때만 삭제·이름 변경이 막힙니다. 프로필에서 바꾸거나 해제해도 디스크 파일은 삭제되지 않으며 이 목록에 남습니다. 목록이 바로 안 바뀌면 페이지를 새로고침하세요. 관리자는 필요 시 삭제·다운로드로 정리할 수 있습니다.

@@ -775,8 +796,8 @@ const deleteMedia = async (item) => {