게시물 가져오기 작업 센터 추가
This commit is contained in:
19
server/routes/admin/api/posts/import-jobs.get.js
Normal file
19
server/routes/admin/api/posts/import-jobs.get.js
Normal file
@@ -0,0 +1,19 @@
|
||||
import { requireAdminSession } from '../../../../utils/admin-auth'
|
||||
import {
|
||||
listPostImportJobs,
|
||||
queuePendingPostImportJobs
|
||||
} from '../../../../repositories/post-import-repository'
|
||||
|
||||
/**
|
||||
* 관리자 게시물 Import 작업 목록 API
|
||||
* @param {import('h3').H3Event} event - 요청 이벤트
|
||||
* @returns {Promise<Array>} Import 작업 목록
|
||||
*/
|
||||
export default defineEventHandler((event) => {
|
||||
requireAdminSession(event)
|
||||
queuePendingPostImportJobs().catch((error) => {
|
||||
console.error('대기 중인 게시물 Import 작업 실행 실패', error)
|
||||
})
|
||||
|
||||
return listPostImportJobs()
|
||||
})
|
||||
32
server/routes/admin/api/posts/import-jobs/[jobId].delete.js
Normal file
32
server/routes/admin/api/posts/import-jobs/[jobId].delete.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import { createError, getRouterParam } from 'h3'
|
||||
import { requireAdminSession } from '../../../../../utils/admin-auth'
|
||||
import { deletePostImportJob } from '../../../../../repositories/post-import-repository'
|
||||
|
||||
/**
|
||||
* 관리자 게시물 Import 작업 삭제 API
|
||||
* @param {import('h3').H3Event} event - 요청 이벤트
|
||||
* @returns {Promise<{ ok: boolean }>} 삭제 결과
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
requireAdminSession(event)
|
||||
|
||||
const jobId = getRouterParam(event, 'jobId')
|
||||
|
||||
if (!jobId) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: 'Import 작업 ID가 필요합니다.'
|
||||
})
|
||||
}
|
||||
|
||||
const deleted = await deletePostImportJob(jobId)
|
||||
|
||||
if (!deleted) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
statusMessage: 'Import 작업을 찾을 수 없습니다.'
|
||||
})
|
||||
}
|
||||
|
||||
return { ok: true }
|
||||
})
|
||||
@@ -1,9 +1,11 @@
|
||||
import { mkdtemp, rm } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { rm } from 'node:fs/promises'
|
||||
import { createError } from 'h3'
|
||||
import { requireAdminSession } from '../../../../utils/admin-auth'
|
||||
import { importPostsFromExportZip } from '../../../../repositories/post-import-repository'
|
||||
import {
|
||||
createPostImportJob,
|
||||
createPostImportJobZipPath,
|
||||
queuePostImportJobRun
|
||||
} from '../../../../repositories/post-import-repository'
|
||||
import { saveMultipartFileToDisk } from '../../../../utils/multipart-file-upload'
|
||||
|
||||
const MAX_IMPORT_ZIP_BYTES = 300 * 1024 * 1024
|
||||
@@ -11,12 +13,12 @@ const MAX_IMPORT_ZIP_BYTES = 300 * 1024 * 1024
|
||||
/**
|
||||
* 관리자 게시물 Import API
|
||||
* @param {import('h3').H3Event} event - 요청 이벤트
|
||||
* @returns {Promise<{ importedCount: number, skippedCount: number, assetCount: number, reusedAssetCount: number, warningCount: number, warnings: Array<string>, posts: Array<Object>, skippedPosts: Array<Object> }>} Import 결과
|
||||
* @returns {Promise<Object>} 생성된 Import 작업
|
||||
*/
|
||||
export default defineEventHandler(async (event) => {
|
||||
const adminSession = requireAdminSession(event)
|
||||
const tempDirectory = await mkdtemp(join(tmpdir(), 'sori-studio-import-'))
|
||||
const zipFilePath = join(tempDirectory, 'upload.zip')
|
||||
const zipFilePath = await createPostImportJobZipPath('upload.zip')
|
||||
let shouldRemoveZip = true
|
||||
|
||||
try {
|
||||
const uploaded = await saveMultipartFileToDisk(event, {
|
||||
@@ -39,10 +41,16 @@ export default defineEventHandler(async (event) => {
|
||||
})
|
||||
}
|
||||
|
||||
return await importPostsFromExportZip({
|
||||
const job = await createPostImportJob({
|
||||
zipFilePath,
|
||||
authorId: adminSession.userId
|
||||
requestedBy: adminSession.userId,
|
||||
requestedEmail: adminSession.email,
|
||||
originalFileName: uploaded.filename || 'upload.zip'
|
||||
})
|
||||
shouldRemoveZip = false
|
||||
queuePostImportJobRun(job.id)
|
||||
|
||||
return job
|
||||
} catch (error) {
|
||||
if (error?.statusCode) {
|
||||
throw error
|
||||
@@ -109,12 +117,11 @@ export default defineEventHandler(async (event) => {
|
||||
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
message: '게시물 Import를 완료하지 못했습니다.'
|
||||
message: '게시물 Import 작업을 시작하지 못했습니다.'
|
||||
})
|
||||
} finally {
|
||||
await rm(tempDirectory, {
|
||||
recursive: true,
|
||||
force: true
|
||||
})
|
||||
if (shouldRemoveZip) {
|
||||
await rm(zipFilePath, { force: true })
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
@@ -82,7 +82,7 @@ const resolveUploadFilePath = (pathname) => {
|
||||
|
||||
const relativeUrlPath = decodedPath.replace(/^\/uploads\/?/g, '')
|
||||
|
||||
if (!relativeUrlPath || relativeUrlPath.includes('\0')) {
|
||||
if (!relativeUrlPath || relativeUrlPath.includes('\0') || relativeUrlPath === '_import-jobs' || relativeUrlPath.startsWith('_import-jobs/')) {
|
||||
throw createError({
|
||||
statusCode: 404,
|
||||
message: '업로드 파일을 찾을 수 없습니다.'
|
||||
|
||||
Reference in New Issue
Block a user