v1.4.1: 관리자 미디어 업로드 한도·라이브 에디터 UX 개선
종류별 업로드 크기 한도와 413 안내를 추가하고, 임베드·미디어 라이브 프리뷰·제목 Enter 포커스·스크롤 동작을 보정한다. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
113
lib/upload-size-limit.js
Normal file
113
lib/upload-size-limit.js
Normal file
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* 파일명에서 확장자를 추출한다. (브라우저·서버 공용, node:path 미사용)
|
||||
* @param {string} [filename] - 파일명
|
||||
* @returns {string} 소문자 확장자(점 포함, 예: `.mp4`)
|
||||
*/
|
||||
const getFileExtension = (filename = '') => {
|
||||
const normalized = String(filename).trim()
|
||||
const lastDot = normalized.lastIndexOf('.')
|
||||
|
||||
if (lastDot <= 0 || lastDot === normalized.length - 1) {
|
||||
return ''
|
||||
}
|
||||
|
||||
return normalized.slice(lastDot).toLowerCase()
|
||||
}
|
||||
|
||||
/** @type {Record<string, string>} 업로드 종류 */
|
||||
export const UPLOAD_KIND = {
|
||||
image: 'image',
|
||||
video: 'video',
|
||||
audio: 'audio',
|
||||
document: 'document'
|
||||
}
|
||||
|
||||
const VIDEO_EXTENSIONS = new Set(['.mp4', '.webm', '.mov'])
|
||||
const AUDIO_EXTENSIONS = new Set(['.mp3', '.wav', '.ogg', '.m4a'])
|
||||
const IMAGE_EXTENSIONS = new Set(['.jpg', '.jpeg', '.png', '.webp', '.gif'])
|
||||
|
||||
/** @type {Record<string, string>} 업로드 종류 한글 라벨 */
|
||||
const UPLOAD_KIND_LABELS = {
|
||||
[UPLOAD_KIND.image]: '이미지',
|
||||
[UPLOAD_KIND.video]: '비디오',
|
||||
[UPLOAD_KIND.audio]: '오디오',
|
||||
[UPLOAD_KIND.document]: '파일'
|
||||
}
|
||||
|
||||
/**
|
||||
* 기본 업로드 크기 한도(바이트)를 만든다.
|
||||
* @param {Object} [overrides] - 종류별 한도 덮어쓰기
|
||||
* @returns {{ image: number, video: number, audio: number, document: number }} 종류별 한도
|
||||
*/
|
||||
export const buildDefaultUploadSizeLimits = (overrides = {}) => ({
|
||||
image: 10485760,
|
||||
video: 209715200,
|
||||
audio: 52428800,
|
||||
document: 52428800,
|
||||
...overrides
|
||||
})
|
||||
|
||||
/**
|
||||
* MIME·파일명으로 업로드 종류를 판별한다.
|
||||
* @param {string} [mimeType] - MIME 타입
|
||||
* @param {string} [filename] - 파일명
|
||||
* @returns {'image'|'video'|'audio'|'document'} 업로드 종류
|
||||
*/
|
||||
export const getUploadKind = (mimeType = '', filename = '') => {
|
||||
const extension = getFileExtension(filename)
|
||||
|
||||
if (mimeType.startsWith('video/') || VIDEO_EXTENSIONS.has(extension)) {
|
||||
return UPLOAD_KIND.video
|
||||
}
|
||||
|
||||
if (mimeType.startsWith('audio/') || AUDIO_EXTENSIONS.has(extension)) {
|
||||
return UPLOAD_KIND.audio
|
||||
}
|
||||
|
||||
if (mimeType.startsWith('image/') || IMAGE_EXTENSIONS.has(extension)) {
|
||||
return UPLOAD_KIND.image
|
||||
}
|
||||
|
||||
return UPLOAD_KIND.document
|
||||
}
|
||||
|
||||
/**
|
||||
* 업로드 종류별 최대 바이트를 반환한다.
|
||||
* @param {'image'|'video'|'audio'|'document'} kind - 업로드 종류
|
||||
* @param {{ image: number, video: number, audio: number, document: number }} limits - 종류별 한도
|
||||
* @returns {number} 최대 바이트
|
||||
*/
|
||||
export const getMaxUploadBytesForKind = (kind, limits) => limits[kind] ?? limits.image
|
||||
|
||||
/**
|
||||
* 바이트를 사람이 읽기 쉬운 용량 문자열로 변환한다.
|
||||
* @param {number} bytes - 바이트
|
||||
* @returns {string} 용량 문자열
|
||||
*/
|
||||
export const formatUploadSizeLimit = (bytes) => {
|
||||
if (!Number.isFinite(bytes) || bytes <= 0) {
|
||||
return '0B'
|
||||
}
|
||||
|
||||
if (bytes >= 1073741824) {
|
||||
const gigabytes = bytes / 1073741824
|
||||
return `${Number.isInteger(gigabytes) ? gigabytes : gigabytes.toFixed(1)}GB`
|
||||
}
|
||||
|
||||
if (bytes >= 1048576) {
|
||||
return `${Math.round(bytes / 1048576)}MB`
|
||||
}
|
||||
|
||||
if (bytes >= 1024) {
|
||||
return `${Math.round(bytes / 1024)}KB`
|
||||
}
|
||||
|
||||
return `${bytes}B`
|
||||
}
|
||||
|
||||
/**
|
||||
* 업로드 종류 한글 라벨을 반환한다.
|
||||
* @param {'image'|'video'|'audio'|'document'} kind - 업로드 종류
|
||||
* @returns {string} 한글 라벨
|
||||
*/
|
||||
export const getUploadKindLabel = (kind) => UPLOAD_KIND_LABELS[kind] || UPLOAD_KIND_LABELS[UPLOAD_KIND.image]
|
||||
Reference in New Issue
Block a user