Files
BRETT/server/api/users/me.post.ts
T
2026-10-03 20:07:49 +02:00

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,
},
}
})