Initial commit
This commit is contained in:
100
api/core/upload/routes.ts
Normal file
100
api/core/upload/routes.ts
Normal file
@@ -0,0 +1,100 @@
|
||||
import express, { type Request, type Response } from 'express'
|
||||
import fs from 'fs/promises'
|
||||
import path from 'path'
|
||||
import { asyncHandler } from '../../utils/asyncHandler.js'
|
||||
import { successResponse } from '../../utils/response.js'
|
||||
import { resolveNotebookPath } from '../../utils/pathSafety.js'
|
||||
import { getUniqueFilename, mimeToExt, validateImageBuffer, detectImageMimeType } from '../../utils/file.js'
|
||||
import { NOTEBOOK_ROOT } from '../../config/paths.js'
|
||||
import { toPosixPath } from '../../../shared/utils/path.js'
|
||||
import { pad2, formatTimestamp } from '../../../shared/utils/date.js'
|
||||
import { ValidationError, UnsupportedMediaTypeError } from '../../../shared/errors/index.js'
|
||||
|
||||
const router = express.Router()
|
||||
|
||||
const parseImageDataUrl = (dataUrl: string): { mimeType: string; base64Data: string } | null => {
|
||||
const match = dataUrl.match(/^data:(image\/[a-zA-Z0-9.+-]+);base64,([A-Za-z0-9+/=\s]+)$/)
|
||||
if (!match) return null
|
||||
const [, mimeType, base64Data] = match
|
||||
return { mimeType, base64Data: base64Data.replace(/\s/g, '') }
|
||||
}
|
||||
|
||||
router.post(
|
||||
'/image',
|
||||
asyncHandler(async (req: Request, res: Response) => {
|
||||
const { image } = req.body as { image?: string }
|
||||
if (!image) throw new ValidationError('需要图片数据')
|
||||
|
||||
const parsed = parseImageDataUrl(image)
|
||||
if (!parsed) {
|
||||
throw new ValidationError('无效的图片数据URL')
|
||||
}
|
||||
|
||||
const ext = mimeToExt[parsed.mimeType]
|
||||
if (!ext) {
|
||||
throw new UnsupportedMediaTypeError('不支持的图片类型')
|
||||
}
|
||||
|
||||
const buffer = Buffer.from(parsed.base64Data, 'base64')
|
||||
|
||||
validateImageBuffer(buffer, parsed.mimeType)
|
||||
|
||||
const detectedMimeType = detectImageMimeType(buffer)
|
||||
if (!detectedMimeType || detectedMimeType !== parsed.mimeType) {
|
||||
throw new ValidationError('图片内容类型不匹配或图片已损坏')
|
||||
}
|
||||
|
||||
const now = new Date()
|
||||
const year = now.getFullYear()
|
||||
const month = pad2(now.getMonth() + 1)
|
||||
const day = pad2(now.getDate())
|
||||
|
||||
const imagesSubDir = `images/${year}/${month}/${day}`
|
||||
const { fullPath: imagesDirFullPath } = resolveNotebookPath(imagesSubDir)
|
||||
await fs.mkdir(imagesDirFullPath, { recursive: true })
|
||||
|
||||
const baseName = formatTimestamp(now)
|
||||
const filename = await getUniqueFilename(imagesDirFullPath, baseName, ext)
|
||||
const relPath = `${imagesSubDir}/${filename}`
|
||||
const { fullPath } = resolveNotebookPath(relPath)
|
||||
|
||||
await fs.writeFile(fullPath, buffer)
|
||||
successResponse(res, { name: toPosixPath(relPath), path: toPosixPath(relPath) })
|
||||
}),
|
||||
)
|
||||
|
||||
router.post(
|
||||
'/wallpaper',
|
||||
asyncHandler(async (req: Request, res: Response) => {
|
||||
const { image } = req.body as { image?: string }
|
||||
if (!image) throw new ValidationError('需要图片数据')
|
||||
|
||||
const parsed = parseImageDataUrl(image)
|
||||
if (!parsed) {
|
||||
throw new ValidationError('无效的图片数据URL')
|
||||
}
|
||||
|
||||
const allowedWallpaperTypes = ['image/png', 'image/jpeg', 'image/webp']
|
||||
if (!allowedWallpaperTypes.includes(parsed.mimeType)) {
|
||||
throw new UnsupportedMediaTypeError('壁纸只支持PNG、JPEG和WebP格式')
|
||||
}
|
||||
|
||||
const buffer = Buffer.from(parsed.base64Data, 'base64')
|
||||
|
||||
validateImageBuffer(buffer, parsed.mimeType)
|
||||
|
||||
const detectedMimeType = detectImageMimeType(buffer)
|
||||
if (!detectedMimeType || detectedMimeType !== parsed.mimeType) {
|
||||
throw new ValidationError('图片内容类型不匹配或图片已损坏')
|
||||
}
|
||||
|
||||
const configDir = path.join(NOTEBOOK_ROOT, '.config')
|
||||
const backgroundPath = path.join(configDir, 'background.png')
|
||||
|
||||
await fs.mkdir(configDir, { recursive: true })
|
||||
await fs.writeFile(backgroundPath, buffer)
|
||||
successResponse(res, { message: '壁纸已更新' })
|
||||
}),
|
||||
)
|
||||
|
||||
export default router
|
||||
Reference in New Issue
Block a user