관리자 업로드 제한 설정 추가
This commit is contained in:
@@ -1,4 +1,13 @@
|
||||
<script setup>
|
||||
import {
|
||||
DEFAULT_UPLOAD_MAX_FILE_COUNT,
|
||||
DEFAULT_UPLOAD_MAX_TOTAL_SIZE,
|
||||
formatUploadSizeLimit,
|
||||
getUploadFilesTotalSize,
|
||||
normalizeUploadMaxFileCount,
|
||||
normalizeUploadMaxTotalSize
|
||||
} from '~/lib/upload-size-limit.js'
|
||||
|
||||
definePageMeta({
|
||||
layout: 'admin'
|
||||
})
|
||||
@@ -42,6 +51,11 @@ const draggingUrls = ref([])
|
||||
const uploadInputRef = ref(null)
|
||||
const uploadingFiles = ref(false)
|
||||
const deletingSelected = ref(false)
|
||||
const { data: uploadSettings } = await useFetch('/admin/api/settings', {
|
||||
key: 'admin-media-upload-settings'
|
||||
})
|
||||
const uploadMaxFileCount = computed(() => normalizeUploadMaxFileCount(uploadSettings.value?.uploadMaxFileCount || DEFAULT_UPLOAD_MAX_FILE_COUNT))
|
||||
const uploadMaxTotalSize = computed(() => normalizeUploadMaxTotalSize(uploadSettings.value?.uploadMaxTotalSize || DEFAULT_UPLOAD_MAX_TOTAL_SIZE))
|
||||
|
||||
const { toast, showToast } = useAdminToast()
|
||||
|
||||
@@ -617,6 +631,19 @@ const uploadSelectedFiles = async (event) => {
|
||||
}
|
||||
|
||||
const files = Array.from(input.files)
|
||||
|
||||
if (files.length > uploadMaxFileCount.value) {
|
||||
showToast('error', `한 번에 업로드할 수 있는 파일은 최대 ${uploadMaxFileCount.value}개입니다.`)
|
||||
input.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
if (getUploadFilesTotalSize(files) > uploadMaxTotalSize.value) {
|
||||
showToast('error', `한 번에 업로드 가능한 전체 용량을 초과했습니다. (최대 ${formatUploadSizeLimit(uploadMaxTotalSize.value)})`)
|
||||
input.value = ''
|
||||
return
|
||||
}
|
||||
|
||||
const formData = new FormData()
|
||||
|
||||
files.forEach((file) => {
|
||||
|
||||
@@ -33,6 +33,15 @@ import {
|
||||
POST_LIST_COUNT_OPTIONS,
|
||||
normalizeDisplayCount
|
||||
} from '~/lib/display-count-settings.js'
|
||||
import {
|
||||
DEFAULT_UPLOAD_MAX_FILE_COUNT,
|
||||
DEFAULT_UPLOAD_MAX_TOTAL_SIZE,
|
||||
UPLOAD_FILE_COUNT_OPTIONS,
|
||||
UPLOAD_TOTAL_SIZE_OPTIONS,
|
||||
formatUploadSizeLimit,
|
||||
normalizeUploadMaxFileCount,
|
||||
normalizeUploadMaxTotalSize
|
||||
} from '~/lib/upload-size-limit.js'
|
||||
|
||||
definePageMeta({
|
||||
layout: 'admin'
|
||||
@@ -125,7 +134,9 @@ const postSnapshot = reactive({
|
||||
postListPageSize: DEFAULT_POST_LIST_PAGE_SIZE,
|
||||
tagPostListPageSize: DEFAULT_TAG_POST_LIST_PAGE_SIZE,
|
||||
featuredPostLimit: DEFAULT_FEATURED_POST_LIMIT,
|
||||
pageSectionItemLimit: DEFAULT_PAGE_SECTION_ITEM_LIMIT
|
||||
pageSectionItemLimit: DEFAULT_PAGE_SECTION_ITEM_LIMIT,
|
||||
uploadMaxFileCount: DEFAULT_UPLOAD_MAX_FILE_COUNT,
|
||||
uploadMaxTotalSize: DEFAULT_UPLOAD_MAX_TOTAL_SIZE
|
||||
})
|
||||
/** 편집 시작 시점의 메인 화면 커버(취소 시 복원용) */
|
||||
const homeCoverSnapshot = reactive({
|
||||
@@ -194,6 +205,8 @@ const form = reactive({
|
||||
tagPostListPageSize: normalizeDisplayCount(settings.value?.tagPostListPageSize, DEFAULT_TAG_POST_LIST_PAGE_SIZE),
|
||||
featuredPostLimit: normalizeDisplayCount(settings.value?.featuredPostLimit, DEFAULT_FEATURED_POST_LIMIT),
|
||||
pageSectionItemLimit: normalizeDisplayCount(settings.value?.pageSectionItemLimit, DEFAULT_PAGE_SECTION_ITEM_LIMIT),
|
||||
uploadMaxFileCount: normalizeUploadMaxFileCount(settings.value?.uploadMaxFileCount),
|
||||
uploadMaxTotalSize: normalizeUploadMaxTotalSize(settings.value?.uploadMaxTotalSize),
|
||||
homeCoverImageUrl: settings.value?.homeCoverImageUrl || '',
|
||||
homeCoverDarkImageUrl: settings.value?.homeCoverDarkImageUrl || '',
|
||||
homeCoverTitle: settings.value?.homeCoverTitle || '',
|
||||
@@ -258,6 +271,8 @@ const hasPostChanges = computed(() => editPost.value
|
||||
|| normalizeDisplayCount(form.tagPostListPageSize, DEFAULT_TAG_POST_LIST_PAGE_SIZE) !== normalizeDisplayCount(postSnapshot.tagPostListPageSize, DEFAULT_TAG_POST_LIST_PAGE_SIZE)
|
||||
|| normalizeDisplayCount(form.featuredPostLimit, DEFAULT_FEATURED_POST_LIMIT) !== normalizeDisplayCount(postSnapshot.featuredPostLimit, DEFAULT_FEATURED_POST_LIMIT)
|
||||
|| normalizeDisplayCount(form.pageSectionItemLimit, DEFAULT_PAGE_SECTION_ITEM_LIMIT) !== normalizeDisplayCount(postSnapshot.pageSectionItemLimit, DEFAULT_PAGE_SECTION_ITEM_LIMIT)
|
||||
|| normalizeUploadMaxFileCount(form.uploadMaxFileCount) !== normalizeUploadMaxFileCount(postSnapshot.uploadMaxFileCount)
|
||||
|| normalizeUploadMaxTotalSize(form.uploadMaxTotalSize) !== normalizeUploadMaxTotalSize(postSnapshot.uploadMaxTotalSize)
|
||||
))
|
||||
|
||||
/**
|
||||
@@ -1164,6 +1179,8 @@ const buildSiteSettingsPayload = () => ({
|
||||
tagPostListPageSize: normalizeDisplayCount(form.tagPostListPageSize, DEFAULT_TAG_POST_LIST_PAGE_SIZE),
|
||||
featuredPostLimit: normalizeDisplayCount(form.featuredPostLimit, DEFAULT_FEATURED_POST_LIMIT),
|
||||
pageSectionItemLimit: normalizeDisplayCount(form.pageSectionItemLimit, DEFAULT_PAGE_SECTION_ITEM_LIMIT),
|
||||
uploadMaxFileCount: normalizeUploadMaxFileCount(form.uploadMaxFileCount),
|
||||
uploadMaxTotalSize: normalizeUploadMaxTotalSize(form.uploadMaxTotalSize),
|
||||
homeCoverImageUrl: form.homeCoverImageUrl || '',
|
||||
homeCoverDarkImageUrl: form.homeCoverDarkImageUrl || '',
|
||||
homeCoverTitle: form.homeCoverTitle || '',
|
||||
@@ -1399,12 +1416,16 @@ const beginEditPost = () => {
|
||||
postSnapshot.tagPostListPageSize = normalizeDisplayCount(form.tagPostListPageSize, DEFAULT_TAG_POST_LIST_PAGE_SIZE)
|
||||
postSnapshot.featuredPostLimit = normalizeDisplayCount(form.featuredPostLimit, DEFAULT_FEATURED_POST_LIMIT)
|
||||
postSnapshot.pageSectionItemLimit = normalizeDisplayCount(form.pageSectionItemLimit, DEFAULT_PAGE_SECTION_ITEM_LIMIT)
|
||||
postSnapshot.uploadMaxFileCount = normalizeUploadMaxFileCount(form.uploadMaxFileCount)
|
||||
postSnapshot.uploadMaxTotalSize = normalizeUploadMaxTotalSize(form.uploadMaxTotalSize)
|
||||
form.postTagLimit = normalizePostTagLimit(form.postTagLimit)
|
||||
form.homeLatestPostLimit = postSnapshot.homeLatestPostLimit
|
||||
form.postListPageSize = postSnapshot.postListPageSize
|
||||
form.tagPostListPageSize = postSnapshot.tagPostListPageSize
|
||||
form.featuredPostLimit = postSnapshot.featuredPostLimit
|
||||
form.pageSectionItemLimit = postSnapshot.pageSectionItemLimit
|
||||
form.uploadMaxFileCount = postSnapshot.uploadMaxFileCount
|
||||
form.uploadMaxTotalSize = postSnapshot.uploadMaxTotalSize
|
||||
editPost.value = true
|
||||
}
|
||||
|
||||
@@ -1420,6 +1441,8 @@ const cancelEditPost = () => {
|
||||
form.tagPostListPageSize = normalizeDisplayCount(postSnapshot.tagPostListPageSize, DEFAULT_TAG_POST_LIST_PAGE_SIZE)
|
||||
form.featuredPostLimit = normalizeDisplayCount(postSnapshot.featuredPostLimit, DEFAULT_FEATURED_POST_LIMIT)
|
||||
form.pageSectionItemLimit = normalizeDisplayCount(postSnapshot.pageSectionItemLimit, DEFAULT_PAGE_SECTION_ITEM_LIMIT)
|
||||
form.uploadMaxFileCount = normalizeUploadMaxFileCount(postSnapshot.uploadMaxFileCount)
|
||||
form.uploadMaxTotalSize = normalizeUploadMaxTotalSize(postSnapshot.uploadMaxTotalSize)
|
||||
editPost.value = false
|
||||
}
|
||||
|
||||
@@ -1445,12 +1468,16 @@ const savePostSection = async () => {
|
||||
postSnapshot.tagPostListPageSize = normalizeDisplayCount(form.tagPostListPageSize, DEFAULT_TAG_POST_LIST_PAGE_SIZE)
|
||||
postSnapshot.featuredPostLimit = normalizeDisplayCount(form.featuredPostLimit, DEFAULT_FEATURED_POST_LIMIT)
|
||||
postSnapshot.pageSectionItemLimit = normalizeDisplayCount(form.pageSectionItemLimit, DEFAULT_PAGE_SECTION_ITEM_LIMIT)
|
||||
postSnapshot.uploadMaxFileCount = normalizeUploadMaxFileCount(form.uploadMaxFileCount)
|
||||
postSnapshot.uploadMaxTotalSize = normalizeUploadMaxTotalSize(form.uploadMaxTotalSize)
|
||||
form.postTagLimit = postSnapshot.postTagLimit
|
||||
form.homeLatestPostLimit = postSnapshot.homeLatestPostLimit
|
||||
form.postListPageSize = postSnapshot.postListPageSize
|
||||
form.tagPostListPageSize = postSnapshot.tagPostListPageSize
|
||||
form.featuredPostLimit = postSnapshot.featuredPostLimit
|
||||
form.pageSectionItemLimit = postSnapshot.pageSectionItemLimit
|
||||
form.uploadMaxFileCount = postSnapshot.uploadMaxFileCount
|
||||
form.uploadMaxTotalSize = postSnapshot.uploadMaxTotalSize
|
||||
editPost.value = false
|
||||
}
|
||||
}
|
||||
@@ -2104,6 +2131,40 @@ onBeforeUnmount(() => {
|
||||
{{ form.description?.trim() ? form.description : '—' }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="admin-settings-screen__field grid gap-3 border-t border-[#eceff2] pt-5">
|
||||
<div>
|
||||
<span class="font-bold text-[#15171a]">업로드 제한</span>
|
||||
<p class="mt-1 text-xs leading-relaxed text-[#657080]">
|
||||
파일 추가나 글쓰기 미디어 업로드에서 한 번에 처리할 수 있는 최대치를 선택합니다.
|
||||
</p>
|
||||
</div>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<label class="grid gap-1.5">
|
||||
<span class="text-xs font-semibold text-[#657080]">파일 수</span>
|
||||
<span class="relative">
|
||||
<select
|
||||
v-model.number="form.uploadMaxFileCount"
|
||||
class="h-10 w-full appearance-none rounded-md border border-[#dce0e5] bg-white px-3 pr-10 text-[#15171a] outline-none focus:border-[#15171a] focus:ring-1 focus:ring-[#15171a]"
|
||||
>
|
||||
<option v-for="option in UPLOAD_FILE_COUNT_OPTIONS" :key="`upload-count-${option}`" :value="option">{{ option }}개</option>
|
||||
</select>
|
||||
<svg class="pointer-events-none absolute right-3 top-1/2 size-4 -translate-y-1/2 text-[#394047]" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
|
||||
</span>
|
||||
</label>
|
||||
<label class="grid gap-1.5">
|
||||
<span class="text-xs font-semibold text-[#657080]">전체 용량</span>
|
||||
<span class="relative">
|
||||
<select
|
||||
v-model.number="form.uploadMaxTotalSize"
|
||||
class="h-10 w-full appearance-none rounded-md border border-[#dce0e5] bg-white px-3 pr-10 text-[#15171a] outline-none focus:border-[#15171a] focus:ring-1 focus:ring-[#15171a]"
|
||||
>
|
||||
<option v-for="option in UPLOAD_TOTAL_SIZE_OPTIONS" :key="`upload-total-${option}`" :value="option">{{ formatUploadSizeLimit(option) }}</option>
|
||||
</select>
|
||||
<svg class="pointer-events-none absolute right-3 top-1/2 size-4 -translate-y-1/2 text-[#394047]" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m6 9 6 6 6-6" /></svg>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else class="admin-settings-screen__card-body grid gap-5">
|
||||
@@ -2540,6 +2601,22 @@ onBeforeUnmount(() => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mt-5 grid gap-3 border-t border-[#eceff2] pt-5">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<span class="font-bold text-[#15171a]">업로드 제한</span>
|
||||
<span class="text-xs text-[#657080]">1회 업로드 기준</span>
|
||||
</div>
|
||||
<div class="grid gap-2 text-xs text-[#657080] sm:grid-cols-2">
|
||||
<div class="flex items-center justify-between gap-3 rounded-md border border-[#edf0f2] px-3 py-2">
|
||||
<span>파일 수</span>
|
||||
<strong class="font-mono text-[#15171a]">{{ normalizeUploadMaxFileCount(form.uploadMaxFileCount) }}개</strong>
|
||||
</div>
|
||||
<div class="flex items-center justify-between gap-3 rounded-md border border-[#edf0f2] px-3 py-2">
|
||||
<span>전체 용량</span>
|
||||
<strong class="font-mono text-[#15171a]">{{ formatUploadSizeLimit(normalizeUploadMaxTotalSize(form.uploadMaxTotalSize)) }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
|
||||
Reference in New Issue
Block a user