54 lines
1.6 KiB
TypeScript
54 lines
1.6 KiB
TypeScript
import { type NextRequest } from "next/server";
|
|
import { prisma } from "@/lib/prisma";
|
|
import { ok, errorResponse, requireRole, ApiError } from "@/lib/api";
|
|
import { saveUploadedFile } from "@/lib/files";
|
|
import { audit } from "@/lib/audit";
|
|
import { clientIp } from "@/lib/request";
|
|
|
|
export async function GET() {
|
|
try {
|
|
await requireRole("admin");
|
|
const files = await prisma.fileAsset.findMany({
|
|
orderBy: { uploadedAt: "desc" },
|
|
take: 200,
|
|
});
|
|
return ok({ files });
|
|
} catch (err) {
|
|
return errorResponse(err);
|
|
}
|
|
}
|
|
|
|
export async function POST(req: NextRequest) {
|
|
try {
|
|
const user = await requireRole("admin");
|
|
|
|
const form = await req.formData().catch(() => null);
|
|
if (!form) throw new ApiError(400, "invalid_form", "Expected multipart/form-data");
|
|
|
|
const file = form.get("file");
|
|
if (!(file instanceof File)) {
|
|
throw new ApiError(400, "missing_file", "Missing 'file' field");
|
|
}
|
|
|
|
const saved = await saveUploadedFile(file, user.id);
|
|
|
|
if (!saved.deduped) {
|
|
await audit({
|
|
actorId: user.id,
|
|
action: "create",
|
|
entity: "FileAsset",
|
|
entityId: saved.id,
|
|
after: { sha256: saved.sha256, kind: saved.kind, originalName: saved.originalName },
|
|
ipAddress: clientIp(req),
|
|
});
|
|
}
|
|
|
|
return ok({ file: saved }, { status: saved.deduped ? 200 : 201 });
|
|
} catch (err) {
|
|
if (err instanceof Error && /Empty file|File too large/.test(err.message)) {
|
|
return errorResponse(new ApiError(413, "file_rejected", err.message));
|
|
}
|
|
return errorResponse(err);
|
|
}
|
|
}
|