Files
caddy-proxy-manager/app/api/user/update-avatar/route.ts
akanealw 99819b70ff
Some checks failed
Build and Push Docker Images (Trusted) / build-and-push (., docker/caddy/Dockerfile, caddy) (push) Has been cancelled
Build and Push Docker Images (Trusted) / build-and-push (., docker/l4-port-manager/Dockerfile, l4-port-manager) (push) Has been cancelled
Build and Push Docker Images (Trusted) / build-and-push (., docker/web/Dockerfile, web) (push) Has been cancelled
Tests / test (push) Has been cancelled
added caddy-proxy-manager for testing
2026-04-21 22:49:08 +00:00

82 lines
2.4 KiB
TypeScript
Executable File

import { NextRequest, NextResponse } from "next/server";
import { auth, checkSameOrigin } from "@/src/lib/auth";
import { updateUserProfile } from "@/src/lib/models/user";
import { createAuditEvent } from "@/src/lib/models/audit";
export async function POST(request: NextRequest) {
const originCheck = checkSameOrigin(request);
if (originCheck) return originCheck;
try {
const session = await auth();
if (!session?.user?.id) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
const userId = Number(session.user.id);
const body = await request.json();
const { avatarUrl } = body;
// Validate avatarUrl is either null or a base64 image string
if (avatarUrl !== null && typeof avatarUrl !== "string") {
return NextResponse.json(
{ error: "Invalid avatar data" },
{ status: 400 }
);
}
// If avatarUrl is provided, validate it's a base64 image (png/jpeg/webp only)
if (avatarUrl !== null) {
const match = avatarUrl.match(/^data:(image\/(png|jpeg|jpg|webp));base64,/i);
if (!match) {
return NextResponse.json(
{ error: "Avatar must be a base64-encoded PNG, JPEG, or WebP image" },
{ status: 400 }
);
}
// Check base64 size (rough estimate: base64 is ~33% larger than binary)
// 2MB binary = ~2.7MB base64, so limit to 3MB base64 string
if (avatarUrl.length > 3 * 1024 * 1024) {
return NextResponse.json(
{ error: "Avatar image is too large" },
{ status: 400 }
);
}
}
// Update user avatar
const updatedUser = await updateUserProfile(userId, {
avatarUrl: avatarUrl
});
if (!updatedUser) {
return NextResponse.json(
{ error: "User not found" },
{ status: 404 }
);
}
// Audit log
await createAuditEvent({
userId,
action: avatarUrl ? "avatar_updated" : "avatar_deleted",
entityType: "user",
entityId: userId,
summary: avatarUrl ? "User updated profile picture" : "User removed profile picture",
data: JSON.stringify({ hasAvatar: !!avatarUrl })
});
return NextResponse.json({
success: true,
avatarUrl: updatedUser.avatarUrl
});
} catch (error) {
console.error("Avatar update error:", error);
return NextResponse.json(
{ error: "Failed to update avatar" },
{ status: 500 }
);
}
}