chahinebrini 6d59bfd62b feat(community): Bild-Upload-Limit 5MB → 10MB
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 01:09:47 +02:00

56 lines
1.8 KiB
TypeScript

import { serverSupabaseServiceRole } from "../../utils/useSupabase";
/**
* POST /api/community/upload-image
* Body: { dataUrl: string } (base64 JPEG/PNG)
* Returns: { url: string }
*/
export default defineEventHandler(async (event) => {
const user = await requireUser(event);
const body = (await readBody(event)) as { image?: string; dataUrl?: string };
const dataUrl = body.image ?? body.dataUrl;
if (!dataUrl?.startsWith("data:image/")) {
throw createError({ statusCode: 400, message: "Ungültige Bilddaten" });
}
const match = dataUrl.match(/^data:(image\/\w+);base64,(.+)$/);
if (!match)
throw createError({ statusCode: 400, message: "Ungültiges Bildformat" });
const contentType = match[1];
const ext = contentType === "image/png" ? "png" : "jpg";
const base64 = match[2];
// Max 10MB check
const sizeBytes = Math.ceil((base64.length * 3) / 4);
if (sizeBytes > 10 * 1024 * 1024) {
throw createError({ statusCode: 400, message: "Bild zu groß (max 10MB)" });
}
const binaryStr = atob(base64);
const bytes = new Uint8Array(binaryStr.length);
for (let i = 0; i < binaryStr.length; i++) {
bytes[i] = binaryStr.charCodeAt(i);
}
const blob = new Blob([bytes], { type: contentType });
const supabase = serverSupabaseServiceRole(event);
const fileName = `posts/${user.id}/${Date.now()}.${ext}`;
const { error: uploadError } = await supabase.storage
.from("rebreak-avatars")
.upload(fileName, blob, { contentType, upsert: false });
if (uploadError) {
console.error("[community/upload-image] Storage error:", uploadError);
throw createError({ statusCode: 500, message: uploadError.message });
}
const { data: urlData } = supabase.storage
.from("rebreak-avatars")
.getPublicUrl(fileName);
return { url: urlData.publicUrl };
});