forked from BRETT/BRETT
126 lines
4.6 KiB
TypeScript
126 lines
4.6 KiB
TypeScript
import { randomUUID } from 'node:crypto'
|
|
import { mkdir, unlink, writeFile } from 'node:fs/promises'
|
|
import { join } from 'node:path'
|
|
import { eq } from 'drizzle-orm'
|
|
import { isCardBorderEffect } from '../../../shared/utils/cardBorderEffects'
|
|
import { isCardEffect } from '../../../shared/utils/cardEffects'
|
|
import { users } from '../../db/schema'
|
|
|
|
const AVATAR_MIME_EXT: Record<string, string> = {
|
|
'image/jpeg': '.jpg',
|
|
'image/png': '.png',
|
|
'image/gif': '.gif',
|
|
'image/webp': '.webp',
|
|
'image/avif': '.avif',
|
|
}
|
|
|
|
const MAX_AVATAR_SIZE = 5 * 1024 * 1024 // 5 MB
|
|
|
|
export default defineEventHandler(async (event) => {
|
|
const user = await requireUser(event)
|
|
const parts = await readMultipartFormData(event)
|
|
|
|
const colorField = parts?.find(p => p.name === 'color')?.data.toString('utf8').trim()
|
|
const cardEffectField = parts?.find(p => p.name === 'cardEffect')?.data.toString('utf8').trim()
|
|
const cardEffectColorField = parts?.find(p => p.name === 'cardEffectColor')?.data.toString('utf8').trim()
|
|
const cardVignetteField = parts?.find(p => p.name === 'cardVignette')?.data.toString('utf8').trim()
|
|
const cardBorderEffectField = parts?.find(p => p.name === 'cardBorderEffect')?.data.toString('utf8').trim()
|
|
const cardBorderEffectColorField = parts?.find(p => p.name === 'cardBorderEffectColor')?.data.toString('utf8').trim()
|
|
const avatarFile = parts?.find(p => p.name === 'avatar')
|
|
|
|
let color: string | null | undefined
|
|
if (colorField !== undefined) {
|
|
if (colorField === '') {
|
|
color = null
|
|
}
|
|
else if (/^#[0-9a-fA-F]{6}$/.test(colorField)) {
|
|
color = colorField.toLowerCase()
|
|
}
|
|
else {
|
|
throw createError({ statusCode: 400, statusMessage: 'Ungültige Farbe' })
|
|
}
|
|
}
|
|
|
|
let cardEffect: string | null | undefined
|
|
if (cardEffectField !== undefined) {
|
|
if (cardEffectField === '') {
|
|
cardEffect = null
|
|
}
|
|
else if (isCardEffect(cardEffectField)) {
|
|
cardEffect = cardEffectField
|
|
}
|
|
else {
|
|
throw createError({ statusCode: 400, statusMessage: 'Ungültiger Effekt' })
|
|
}
|
|
}
|
|
|
|
let cardBorderEffect: string | null | undefined
|
|
if (cardBorderEffectField !== undefined) {
|
|
if (cardBorderEffectField === '') {
|
|
cardBorderEffect = null
|
|
}
|
|
else if (isCardBorderEffect(cardBorderEffectField)) {
|
|
cardBorderEffect = cardBorderEffectField
|
|
}
|
|
else {
|
|
throw createError({ statusCode: 400, statusMessage: 'Ungültiger Border-Effekt' })
|
|
}
|
|
}
|
|
|
|
const parseOptionalColor = (field: string | undefined): string | null | undefined => {
|
|
if (field === undefined) return undefined
|
|
if (field === '') return null
|
|
if (/^#[0-9a-fA-F]{6}$/.test(field)) return field.toLowerCase()
|
|
throw createError({ statusCode: 400, statusMessage: 'Ungültige Effekt-Farbe' })
|
|
}
|
|
const cardEffectColor = parseOptionalColor(cardEffectColorField)
|
|
const cardBorderEffectColor = parseOptionalColor(cardBorderEffectColorField)
|
|
const cardVignette = cardVignetteField === undefined ? undefined : cardVignetteField === '1'
|
|
|
|
let avatarName: string | undefined
|
|
if (avatarFile?.data?.length && avatarFile.type) {
|
|
const ext = AVATAR_MIME_EXT[avatarFile.type]
|
|
if (!ext) {
|
|
throw createError({ statusCode: 415, statusMessage: 'Nur Bilder sind als Avatar erlaubt' })
|
|
}
|
|
if (avatarFile.data.length > MAX_AVATAR_SIZE) {
|
|
throw createError({ statusCode: 413, statusMessage: 'Avatar ist zu groß (max. 5 MB)' })
|
|
}
|
|
avatarName = `${randomUUID()}${ext}`
|
|
await mkdir(avatarDir(), { recursive: true })
|
|
await writeFile(join(avatarDir(), avatarName), avatarFile.data)
|
|
}
|
|
|
|
const db = useDb()
|
|
await db.update(users).set({
|
|
...(color !== undefined && { color }),
|
|
...(cardEffect !== undefined && { cardEffect }),
|
|
...(cardEffectColor !== undefined && { cardEffectColor }),
|
|
...(cardVignette !== undefined && { cardVignette }),
|
|
...(cardBorderEffect !== undefined && { cardBorderEffect }),
|
|
...(cardBorderEffectColor !== undefined && { cardBorderEffectColor }),
|
|
...(avatarName && { avatar: avatarName }),
|
|
}).where(eq(users.id, user.id))
|
|
|
|
if (avatarName && user.avatar) {
|
|
await unlink(join(avatarDir(), user.avatar)).catch(() => {})
|
|
}
|
|
|
|
const [updated] = await db.select().from(users).where(eq(users.id, user.id)).limit(1)
|
|
return {
|
|
user: {
|
|
id: updated!.id,
|
|
name: updated!.name,
|
|
email: updated!.email,
|
|
color: updated!.color,
|
|
cardEffect: updated!.cardEffect,
|
|
cardEffectColor: updated!.cardEffectColor,
|
|
cardVignette: updated!.cardVignette,
|
|
cardBorderEffect: updated!.cardBorderEffect,
|
|
cardBorderEffectColor: updated!.cardBorderEffectColor,
|
|
avatarUrl: avatarUrl(updated!.avatar),
|
|
roles: user.roles,
|
|
},
|
|
}
|
|
})
|