58 lines
1.8 KiB
TypeScript
58 lines
1.8 KiB
TypeScript
import { serverSupabaseServiceRole } from "../../utils/useSupabase";
|
|
import { updateProfile } from "../../db/profile";
|
|
|
|
/**
|
|
* POST /api/avatar/upload
|
|
* Body: { dataUrl: string } (base64 JPEG/PNG data URL)
|
|
*/
|
|
export default defineEventHandler(async (event) => {
|
|
const user = await requireUser(event);
|
|
const { dataUrl } = (await readBody(event)) as { dataUrl: string };
|
|
|
|
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];
|
|
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 path = `avatars/${user.id}.${ext}`;
|
|
|
|
const { error: uploadError } = await supabase.storage
|
|
.from("rebreak-avatars")
|
|
.upload(path, blob, { contentType, upsert: true });
|
|
|
|
if (uploadError) {
|
|
console.error("[avatar/upload] Storage error:", uploadError);
|
|
throw createError({ statusCode: 500, message: uploadError.message });
|
|
}
|
|
|
|
const { data: urlData } = supabase.storage
|
|
.from("rebreak-avatars")
|
|
.getPublicUrl(path);
|
|
|
|
const publicUrl = urlData.publicUrl + `?t=${Date.now()}`;
|
|
|
|
// In beide schreiben: profiles (für Prisma include) + user_metadata (für me.get)
|
|
await Promise.all([
|
|
updateProfile(user.id, { avatar: publicUrl }),
|
|
supabase.auth.admin.updateUserById(user.id, {
|
|
user_metadata: { avatar: publicUrl },
|
|
}),
|
|
]);
|
|
|
|
return { url: publicUrl };
|
|
});
|