미디어 폴더 트리 관리 추가
This commit is contained in:
13
server/routes/admin/api/media-folders.get.js
Normal file
13
server/routes/admin/api/media-folders.get.js
Normal file
@@ -0,0 +1,13 @@
|
||||
import { requireAdminSession } from '../../../utils/admin-auth'
|
||||
import { listMediaFolders } from '../../../utils/media-library'
|
||||
|
||||
/**
|
||||
* 관리자 미디어 폴더 목록 API
|
||||
* @param {import('h3').H3Event} event - 요청 이벤트
|
||||
* @returns {Promise<Array<string>>} 미디어 폴더 경로 목록
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
requireAdminSession(event)
|
||||
|
||||
return listMediaFolders()
|
||||
})
|
||||
16
server/routes/admin/api/media-folders.post.js
Normal file
16
server/routes/admin/api/media-folders.post.js
Normal file
@@ -0,0 +1,16 @@
|
||||
import { readBody } from 'h3'
|
||||
import { requireAdminSession } from '../../../utils/admin-auth'
|
||||
import { createMediaFolder } from '../../../utils/media-library'
|
||||
|
||||
/**
|
||||
* 관리자 미디어 폴더 생성 API
|
||||
* @param {import('h3').H3Event} event - 요청 이벤트
|
||||
* @returns {Promise<{ path: string }>} 생성된 미디어 폴더
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
requireAdminSession(event)
|
||||
|
||||
const body = await readBody(event)
|
||||
|
||||
return createMediaFolder(body?.path || '')
|
||||
})
|
||||
@@ -1,17 +1,21 @@
|
||||
import { readBody } from 'h3'
|
||||
import { requireAdminSession } from '../../../utils/admin-auth'
|
||||
import { renameMediaItem, updateMediaCategory } from '../../../utils/media-library'
|
||||
import { renameMediaItem, updateMediaCategories, updateMediaCategory } from '../../../utils/media-library'
|
||||
|
||||
/**
|
||||
* 관리자 미디어 파일명 변경 API
|
||||
* 관리자 미디어 파일명 및 폴더 변경 API
|
||||
* @param {import('h3').H3Event} event - 요청 이벤트
|
||||
* @returns {Promise<Object>} 변경된 미디어 항목
|
||||
* @returns {Promise<Object|Array<Object>>} 변경된 미디어 항목
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
requireAdminSession(event)
|
||||
|
||||
const body = await readBody(event)
|
||||
|
||||
if (body?.category !== undefined && Array.isArray(body?.urls)) {
|
||||
return updateMediaCategories(body.urls, body.category)
|
||||
}
|
||||
|
||||
if (body?.category !== undefined) {
|
||||
return updateMediaCategory(body?.url, body?.category)
|
||||
}
|
||||
|
||||
@@ -43,8 +43,62 @@ const getMediaMetadataMap = async () => {
|
||||
const normalizeMediaCategory = (category) => String(category || '')
|
||||
.trim()
|
||||
.replace(/\s+/g, ' ')
|
||||
.replace(/\/+/g, '/')
|
||||
.replace(/^\/|\/$/g, '')
|
||||
|| '미분류'
|
||||
|
||||
/**
|
||||
* 미디어 폴더 목록 조회
|
||||
* @returns {Promise<Array<string>>} 미디어 폴더 경로 목록
|
||||
*/
|
||||
export const listMediaFolders = async () => {
|
||||
const sql = getPostgresClient()
|
||||
const items = await readMediaDirectory(uploadRoot)
|
||||
const metadataMap = await getMediaMetadataMap()
|
||||
const defaultCategories = items.map((item) => metadataMap[item.url]?.category || item.category)
|
||||
|
||||
if (!sql) {
|
||||
return [...new Set(['미분류', ...defaultCategories])].sort((left, right) => left.localeCompare(right))
|
||||
}
|
||||
|
||||
const rows = await sql`
|
||||
SELECT path
|
||||
FROM media_folders
|
||||
ORDER BY path ASC
|
||||
`
|
||||
|
||||
return [...new Set([
|
||||
'미분류',
|
||||
...rows.map((row) => row.path),
|
||||
...defaultCategories
|
||||
])].sort((left, right) => left.localeCompare(right))
|
||||
}
|
||||
|
||||
/**
|
||||
* 미디어 폴더 생성
|
||||
* @param {string} path - 폴더 경로
|
||||
* @returns {Promise<{ path: string }>} 생성된 폴더
|
||||
*/
|
||||
export const createMediaFolder = async (path) => {
|
||||
const sql = getPostgresClient()
|
||||
const normalizedPath = normalizeMediaCategory(path)
|
||||
|
||||
if (!sql) {
|
||||
throw new Error('DATABASE_REQUIRED')
|
||||
}
|
||||
|
||||
await sql`
|
||||
INSERT INTO media_folders (path)
|
||||
VALUES (${normalizedPath})
|
||||
ON CONFLICT (path) DO UPDATE
|
||||
SET updated_at = now()
|
||||
`
|
||||
|
||||
return {
|
||||
path: normalizedPath
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 미디어 파일명 조각을 안전하게 정리
|
||||
* @param {string} value - 원본 파일명
|
||||
@@ -259,35 +313,56 @@ const moveMediaMetadata = async (currentUrl, nextUrl) => {
|
||||
* @returns {Promise<Object>} 수정된 미디어 항목
|
||||
*/
|
||||
export const updateMediaCategory = async (url, category) => {
|
||||
const [item] = await updateMediaCategories([url], category)
|
||||
|
||||
return item
|
||||
}
|
||||
|
||||
/**
|
||||
* 여러 미디어 카테고리 저장
|
||||
* @param {Array<string>} urls - 미디어 URL 목록
|
||||
* @param {string} category - 미디어 카테고리
|
||||
* @returns {Promise<Array<Object>>} 수정된 미디어 항목 목록
|
||||
*/
|
||||
export const updateMediaCategories = async (urls, category) => {
|
||||
const sql = getPostgresClient()
|
||||
const mediaPath = resolveMediaPath(url)
|
||||
const normalizedCategory = normalizeMediaCategory(category)
|
||||
|
||||
if (!sql) {
|
||||
throw new Error('DATABASE_REQUIRED')
|
||||
}
|
||||
|
||||
await sql`
|
||||
INSERT INTO media_metadata (
|
||||
url,
|
||||
category
|
||||
)
|
||||
VALUES (
|
||||
${url},
|
||||
${normalizeMediaCategory(category)}
|
||||
)
|
||||
ON CONFLICT (url) DO UPDATE
|
||||
SET
|
||||
category = EXCLUDED.category,
|
||||
updated_at = now()
|
||||
`
|
||||
await createMediaFolder(normalizedCategory)
|
||||
|
||||
const item = await createMediaItem(mediaPath)
|
||||
const items = []
|
||||
|
||||
return {
|
||||
...item,
|
||||
category: normalizeMediaCategory(category),
|
||||
usage: []
|
||||
for (const url of [...new Set(urls.filter(Boolean))]) {
|
||||
const mediaPath = resolveMediaPath(url)
|
||||
|
||||
await sql`
|
||||
INSERT INTO media_metadata (
|
||||
url,
|
||||
category
|
||||
)
|
||||
VALUES (
|
||||
${url},
|
||||
${normalizedCategory}
|
||||
)
|
||||
ON CONFLICT (url) DO UPDATE
|
||||
SET
|
||||
category = EXCLUDED.category,
|
||||
updated_at = now()
|
||||
`
|
||||
|
||||
const item = await createMediaItem(mediaPath)
|
||||
items.push({
|
||||
...item,
|
||||
category: normalizedCategory,
|
||||
usage: []
|
||||
})
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user