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 = { '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, }, } })