Files
buzz-sheet/app/api/admin/media/route.ts
T
gunshiz 87e6bcd96f
CI / Verify and audit (push) Successful in 2m33s
CI / Build, scan and deploy immutable images (push) Failing after 1m29s
feat : 6 astra improve it
2026-09-22 18:28:18 +07:00

37 lines
1.7 KiB
TypeScript

import { requireAdmin } from "@/lib/auth/server";
import { uploadPublicMedia } from "@/lib/media/repository";
import { MAX_MEDIA_BYTES, mediaUploadSchema } from "@/lib/media/validation";
import { boundedBody, errorResponse, HttpError, requireSameOrigin, securityLog, withUploadSlot } from "@/lib/security/http";
import { limitRequest } from "@/lib/security/rate-limit";
export async function POST(request: Request) {
try {
requireSameOrigin(request);
const admin = await requireAdmin();
await limitRequest("media", admin.user.id, 20);
if (!request.headers.get("content-type")?.toLowerCase().startsWith("multipart/form-data;")) {
throw new HttpError(415, "expected-multipart");
}
return await withUploadSlot(async () => {
let form: FormData;
try {
form = await boundedBody(request, MAX_MEDIA_BYTES + 64 * 1024).formData();
} catch (cause) {
if (cause instanceof HttpError) throw cause;
throw new HttpError(400, "invalid-form");
}
const file = form.get("file");
if (!(file instanceof File)) throw new HttpError(400, "missing-image");
if (file.size > MAX_MEDIA_BYTES) throw new HttpError(413, "image-too-large");
if (!mediaUploadSchema.safeParse({ fileName: file.name, mimeType: file.type, byteSize: file.size }).success) {
throw new HttpError(415, "invalid-image");
}
const result = await uploadPublicMedia(file, admin.user.id);
securityLog("media-uploaded", { actorId: admin.user.id, targetId: result.id });
return Response.json(result, { status: 201, headers: { "Cache-Control": "no-store" } });
});
} catch (cause) {
return errorResponse(cause);
}
}