45 lines
1.5 KiB
TypeScript
45 lines
1.5 KiB
TypeScript
import { auth } from "~/lib/auth.server";
|
|
import { buildWebhookUrl, uploadAvatarImage } from "~/lib/cloudinary.server";
|
|
import { logger } from "~/lib/logger";
|
|
import type { Route } from "./+types/api.upload-avatar";
|
|
|
|
const MAX_UPLOAD_BYTES = 5 * 1024 * 1024;
|
|
const ACCEPTED_IMAGE_TYPES = new Set(["image/jpeg", "image/png", "image/webp", "image/gif"]);
|
|
|
|
export async function action({ request }: Route.ActionArgs) {
|
|
const session = await auth.api.getSession({ headers: request.headers });
|
|
if (!session) {
|
|
return Response.json({ error: "Unauthorized" }, { status: 401 });
|
|
}
|
|
|
|
const formData = await request.formData();
|
|
const file = formData.get("file");
|
|
|
|
if (!(file instanceof File)) {
|
|
return Response.json({ error: "Image file is required" }, { status: 400 });
|
|
}
|
|
|
|
if (!ACCEPTED_IMAGE_TYPES.has(file.type)) {
|
|
return Response.json({ error: "Unsupported image type" }, { status: 400 });
|
|
}
|
|
|
|
if (file.size > MAX_UPLOAD_BYTES) {
|
|
return Response.json({ error: "Image must be under 5 MB" }, { status: 400 });
|
|
}
|
|
|
|
try {
|
|
const buffer = Buffer.from(await file.arrayBuffer());
|
|
await uploadAvatarImage({
|
|
buffer,
|
|
contentType: file.type,
|
|
target: "user",
|
|
entityId: session.user.id,
|
|
notificationUrl: buildWebhookUrl(request),
|
|
});
|
|
|
|
return Response.json({ status: "pending" });
|
|
} catch (error) {
|
|
logger.error("Avatar upload failed:", error);
|
|
return Response.json({ error: "Upload failed" }, { status: 500 });
|
|
}
|
|
}
|