미디어 사용 현황 표시 추가
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { readdir, rename, rm, stat } from 'node:fs/promises'
|
||||
import { basename, dirname, extname, join, relative } from 'node:path'
|
||||
import { createError } from 'h3'
|
||||
import { listAdminPosts, listPages } from '../repositories/content-repository'
|
||||
|
||||
const uploadRoot = join(process.cwd(), 'public', 'uploads')
|
||||
|
||||
@@ -92,14 +93,81 @@ const readMediaDirectory = async (directoryPath) => {
|
||||
return items.flat()
|
||||
}
|
||||
|
||||
/**
|
||||
* 콘텐츠에서 미디어 URL 사용 여부 확인
|
||||
* @param {Object} contentItem - 콘텐츠 항목
|
||||
* @param {string} url - 미디어 URL
|
||||
* @returns {Array<Object>} 사용처 목록
|
||||
*/
|
||||
const getContentMediaUsage = (contentItem, url) => {
|
||||
const usages = []
|
||||
|
||||
if (contentItem.featuredImage === url) {
|
||||
usages.push({
|
||||
location: 'featuredImage',
|
||||
label: '대표 이미지'
|
||||
})
|
||||
}
|
||||
|
||||
if (contentItem.content?.includes(url)) {
|
||||
usages.push({
|
||||
location: 'content',
|
||||
label: '본문'
|
||||
})
|
||||
}
|
||||
|
||||
return usages
|
||||
}
|
||||
|
||||
/**
|
||||
* 미디어 URL 사용처 조회
|
||||
* @param {string} url - 미디어 URL
|
||||
* @returns {Promise<Array<Object>>} 사용처 목록
|
||||
*/
|
||||
const getMediaUsage = (url, posts, pages) => {
|
||||
const postUsages = posts.flatMap((post) => getContentMediaUsage(post, url).map((usage) => ({
|
||||
type: 'post',
|
||||
typeLabel: '게시물',
|
||||
id: post.id,
|
||||
title: post.title,
|
||||
slug: post.slug,
|
||||
adminUrl: `/admin/posts/${post.id}`,
|
||||
publicUrl: `/posts/${post.slug}`,
|
||||
status: post.status,
|
||||
...usage
|
||||
})))
|
||||
|
||||
const pageUsages = pages.flatMap((page) => getContentMediaUsage(page, url).map((usage) => ({
|
||||
type: 'page',
|
||||
typeLabel: '페이지',
|
||||
id: page.id,
|
||||
title: page.title,
|
||||
slug: page.slug,
|
||||
adminUrl: '',
|
||||
publicUrl: `/pages/${page.slug}`,
|
||||
status: 'page',
|
||||
...usage
|
||||
})))
|
||||
|
||||
return [...postUsages, ...pageUsages]
|
||||
}
|
||||
|
||||
/**
|
||||
* 미디어 목록 조회
|
||||
* @returns {Promise<Array<Object>>} 미디어 항목 목록
|
||||
*/
|
||||
export const listMediaItems = async () => {
|
||||
const items = await readMediaDirectory(uploadRoot)
|
||||
const [posts, pages] = await Promise.all([
|
||||
listAdminPosts(),
|
||||
listPages()
|
||||
])
|
||||
const itemsWithUsage = items.map((item) => ({
|
||||
...item,
|
||||
usage: getMediaUsage(item.url, posts, pages)
|
||||
}))
|
||||
|
||||
return items.sort((left, right) => new Date(right.updatedAt) - new Date(left.updatedAt))
|
||||
return itemsWithUsage.sort((left, right) => new Date(right.updatedAt) - new Date(left.updatedAt))
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,6 +176,19 @@ export const listMediaItems = async () => {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
export const deleteMediaItem = async (url) => {
|
||||
const [posts, pages] = await Promise.all([
|
||||
listAdminPosts(),
|
||||
listPages()
|
||||
])
|
||||
const usage = getMediaUsage(url, posts, pages)
|
||||
|
||||
if (usage.length) {
|
||||
throw createError({
|
||||
statusCode: 409,
|
||||
message: '사용 중인 미디어는 삭제할 수 없습니다.'
|
||||
})
|
||||
}
|
||||
|
||||
await rm(resolveMediaPath(url))
|
||||
}
|
||||
|
||||
@@ -118,6 +199,19 @@ export const deleteMediaItem = async (url) => {
|
||||
* @returns {Promise<Object>} 변경된 미디어 항목
|
||||
*/
|
||||
export const renameMediaItem = async (url, name) => {
|
||||
const [posts, pages] = await Promise.all([
|
||||
listAdminPosts(),
|
||||
listPages()
|
||||
])
|
||||
const usage = getMediaUsage(url, posts, pages)
|
||||
|
||||
if (usage.length) {
|
||||
throw createError({
|
||||
statusCode: 409,
|
||||
message: '사용 중인 미디어는 파일명을 변경할 수 없습니다.'
|
||||
})
|
||||
}
|
||||
|
||||
const currentPath = resolveMediaPath(url)
|
||||
const currentExtension = extname(currentPath)
|
||||
const cleanName = sanitizeMediaName(name.replace(/\.[^.]+$/g, ''))
|
||||
|
||||
Reference in New Issue
Block a user