83 lines
2.6 KiB
TypeScript
83 lines
2.6 KiB
TypeScript
import { NextResponse } from "next/server";
|
|||
|
|
import { createSupabaseRouteClient } from "@/lib/supabase/server";
|
||
|
|
import type { MediaAsset } from "@/types/media";
|
||
|
|
import { extname } from "path";
|
||
|
|
|
||
|
|
export const dynamic = "force-dynamic";
|
||
|
|
|
||
|
|
const MEDIA_BUCKET = process.env.NEXT_PUBLIC_SUPABASE_MEDIA_BUCKET ?? "media";
|
||
|
|
|
||
|
|
const resolveAssetType = (mime: string): "image" | "video" | "audio" | "file" => {
|
||
|
|
if (mime.startsWith("image/")) return "image";
|
||
|
|
if (mime.startsWith("video/")) return "video";
|
||
|
|
if (mime.startsWith("audio/")) return "audio";
|
||
|
|
return "file";
|
||
|
|
};
|
||
|
|
|
||
|
|
export async function POST(request: Request) {
|
||
|
|
const supabase = await createSupabaseRouteClient();
|
||
|
|
const {
|
||
|
|
data: { session },
|
||
|
|
} = await supabase.auth.getSession();
|
||
|
|
|
||
|
|
if (!session) {
|
||
|
|
return NextResponse.json({ error: "未登录" }, { status: 401 });
|
||
|
|
}
|
||
|
|
|
||
|
|
const formData = await request.formData();
|
||
|
|
const file = formData.get("file");
|
||
|
|
const workspaceId = String(formData.get("workspaceId") ?? "");
|
||
|
|
const documentId = String(formData.get("documentId") ?? "");
|
||
|
|
|
||
|
|
if (!(file instanceof File) || !workspaceId || !documentId) {
|
||
|
|
return NextResponse.json({ error: "缺少必要参数" }, { status: 400 });
|
||
|
|
}
|
||
|
|
|
||
|
|
try {
|
||
|
|
const arrayBuffer = await file.arrayBuffer();
|
||
|
|
const buffer = Buffer.from(arrayBuffer);
|
||
|
|
const extension = extname(file.name || "").replace(/\s+/g, "");
|
||
|
|
const uniqueId = typeof crypto.randomUUID === "function" ? crypto.randomUUID() : Math.random().toString(36).slice(2);
|
||
|
|
const path = `${workspaceId}/${Date.now()}-${uniqueId}${extension}`;
|
||
|
|
const assetType = resolveAssetType(file.type || "");
|
||
|
|
|
||
|
|
const { error: uploadError } = await supabase.storage.from(MEDIA_BUCKET).upload(path, buffer, {
|
||
|
|
contentType: file.type,
|
||
|
|
upsert: false,
|
||
|
|
});
|
||
|
|
|
||
|
|
if (uploadError) {
|
||
|
|
return NextResponse.json({ error: uploadError.message }, { status: 500 });
|
||
|
|
}
|
||
|
|
|
||
|
|
const {
|
||
|
|
data: { publicUrl },
|
||
|
|
} = supabase.storage.from(MEDIA_BUCKET).getPublicUrl(path);
|
||
|
|
|
||
|
|
const { data: asset, error } = await supabase
|
||
|
|
.from("media_assets")
|
||
|
|
.insert({
|
||
|
|
workspace_id: workspaceId,
|
||
|
|
document_id: documentId,
|
||
|
|
file_url: publicUrl,
|
||
|
|
thumbnail_url: publicUrl,
|
||
|
|
asset_type: assetType,
|
||
|
|
file_name: file.name,
|
||
|
|
file_size: file.size,
|
||
|
|
mime_type: file.type,
|
||
|
|
created_by: session.user.id,
|
||
|
|
})
|
||
|
|
.select("*")
|
||
|
|
.single();
|
||
|
|
|
||
|
|
if (error) {
|
||
|
|
return NextResponse.json({ error: error.message }, { status: 500 });
|
||
|
|
}
|
||
|
|
|
||
|
|
return NextResponse.json({ asset: asset as MediaAsset });
|
||
|
|
} catch (error) {
|
||
|
|
console.error(error);
|
||
|
|
return NextResponse.json({ error: "上传失败" }, { status: 500 });
|
||
|
|
}
|
||
|
|
}
|