130 lines
4.3 KiB
JavaScript
130 lines
4.3 KiB
JavaScript
import { existsSync } from 'node:fs'
|
|
import { dirname, extname, join, posix } from 'node:path'
|
|
|
|
export const POST_THUMBNAIL_WIDTH = 640
|
|
export const POST_THUMBNAIL_HEIGHT = 360
|
|
export const POST_THUMBNAIL_QUALITY = 82
|
|
|
|
const postUploadUrlPattern = /^\/uploads\/posts\/\d{4}\/\d{2}\/[^/?#]+$/i
|
|
const postThumbnailUrlPattern = /^\/uploads\/posts\/\d{4}\/\d{2}\/thumbs\/[^/?#]+-card\.webp$/i
|
|
const postThumbnailDirectoryName = 'thumbs'
|
|
const postThumbnailSuffix = '-card'
|
|
const postThumbnailExtension = '.webp'
|
|
const supportedPostThumbnailExtensions = new Set(['.jpg', '.jpeg', '.png', '.webp'])
|
|
const supportedPostThumbnailTypes = new Set(['image/jpeg', 'image/png', 'image/webp'])
|
|
|
|
/**
|
|
* URL 경로 조각을 파일 시스템 경로용 문자열로 디코딩한다.
|
|
* @param {string} value - URL 경로 조각
|
|
* @returns {string} 디코딩된 값
|
|
*/
|
|
const decodeUrlPathPart = (value) => {
|
|
try {
|
|
return decodeURIComponent(value)
|
|
} catch {
|
|
return value
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 게시물 이미지에서 카드 썸네일을 생성할 수 있는지 확인한다.
|
|
* @param {string} mimeType - 파일 MIME 타입
|
|
* @param {string} fileName - 파일명
|
|
* @returns {boolean} 썸네일 생성 가능 여부
|
|
*/
|
|
export const isPostThumbnailSource = (mimeType, fileName = '') => {
|
|
if (supportedPostThumbnailTypes.has(mimeType)) {
|
|
return true
|
|
}
|
|
|
|
return supportedPostThumbnailExtensions.has(extname(fileName).toLowerCase())
|
|
}
|
|
|
|
/**
|
|
* 원본 파일명에서 게시물 카드 썸네일 파일명을 만든다.
|
|
* @param {string} fileName - 원본 파일명
|
|
* @returns {string} 썸네일 파일명
|
|
*/
|
|
export const getPostThumbnailFileName = (fileName) => {
|
|
const extension = extname(fileName)
|
|
const stem = extension ? fileName.slice(0, -extension.length) : fileName
|
|
|
|
return `${stem}${postThumbnailSuffix}${postThumbnailExtension}`
|
|
}
|
|
|
|
/**
|
|
* 게시물 원본 이미지 URL에 대응하는 카드 썸네일 URL을 만든다.
|
|
* @param {string|null|undefined} imageUrl - 원본 이미지 URL
|
|
* @returns {string} 카드 썸네일 URL
|
|
*/
|
|
export const getPostThumbnailUrl = (imageUrl) => {
|
|
if (!imageUrl || !postUploadUrlPattern.test(imageUrl)) {
|
|
return ''
|
|
}
|
|
|
|
const directory = posix.dirname(imageUrl)
|
|
const fileName = posix.basename(imageUrl)
|
|
const extension = extname(fileName).toLowerCase()
|
|
|
|
if (!supportedPostThumbnailExtensions.has(extension)) {
|
|
return ''
|
|
}
|
|
|
|
return `${directory}/${postThumbnailDirectoryName}/${getPostThumbnailFileName(fileName)}`
|
|
}
|
|
|
|
/**
|
|
* 게시물 카드 썸네일 URL인지 확인한다.
|
|
* @param {string|null|undefined} imageUrl - 검사할 이미지 URL
|
|
* @returns {boolean} 카드 썸네일 여부
|
|
*/
|
|
export const isPostCardThumbnailUrl = (imageUrl) => Boolean(imageUrl && postThumbnailUrlPattern.test(imageUrl))
|
|
|
|
/**
|
|
* 게시물 카드 썸네일 URL의 디스크 경로를 조회한다.
|
|
* @param {string} imageUrl - 원본 이미지 URL
|
|
* @returns {string} 썸네일 디스크 경로
|
|
*/
|
|
export const getPostThumbnailDiskPath = (imageUrl) => {
|
|
const thumbnailUrl = getPostThumbnailUrl(imageUrl)
|
|
|
|
if (!thumbnailUrl) {
|
|
return ''
|
|
}
|
|
|
|
return join(process.cwd(), 'public', decodeUrlPathPart(thumbnailUrl.replace(/^\/+/, '')))
|
|
}
|
|
|
|
/**
|
|
* 게시물 원본 이미지 파일의 썸네일 저장 디렉터리 경로를 조회한다.
|
|
* @param {string} imageFilePath - 원본 이미지 디스크 경로
|
|
* @returns {string} 썸네일 저장 디렉터리 경로
|
|
*/
|
|
export const getPostThumbnailDirectoryPath = (imageFilePath) => join(dirname(imageFilePath), postThumbnailDirectoryName)
|
|
|
|
/**
|
|
* 게시물 원본 이미지 파일의 썸네일 저장 경로를 조회한다.
|
|
* @param {string} imageFilePath - 원본 이미지 디스크 경로
|
|
* @returns {string} 썸네일 저장 경로
|
|
*/
|
|
export const getPostThumbnailPathForFile = (imageFilePath) => {
|
|
const fileName = imageFilePath.split(/[\\/]/).pop() || ''
|
|
|
|
return join(getPostThumbnailDirectoryPath(imageFilePath), getPostThumbnailFileName(fileName))
|
|
}
|
|
|
|
/**
|
|
* 이미 생성된 게시물 카드 썸네일 URL을 조회한다.
|
|
* @param {string|null|undefined} imageUrl - 원본 이미지 URL
|
|
* @returns {string} 존재하는 썸네일 URL
|
|
*/
|
|
export const getExistingPostThumbnailUrl = (imageUrl) => {
|
|
const thumbnailUrl = getPostThumbnailUrl(imageUrl)
|
|
|
|
if (!thumbnailUrl) {
|
|
return ''
|
|
}
|
|
|
|
return existsSync(getPostThumbnailDiskPath(imageUrl)) ? thumbnailUrl : ''
|
|
}
|