62 lines
1.7 KiB
TypeScript
62 lines
1.7 KiB
TypeScript
import { Elysia, t } from "elysia";
|
|
import { join } from "path";
|
|
import { mkdir } from "fs/promises";
|
|
|
|
export const uploadRoute = new Elysia({ prefix: "/upload" })
|
|
.post("/", async ({ body, set }) => {
|
|
const { file } = body as { file: File };
|
|
|
|
if (!file) {
|
|
set.status = 400;
|
|
return { error: "No file uploaded" };
|
|
}
|
|
|
|
// Validate file type
|
|
if (!file.type.startsWith("image/")) {
|
|
set.status = 400;
|
|
return { error: "Only image files are allowed" };
|
|
}
|
|
|
|
console.log(`[Upload] Processing: ${file.name} (${(file.size / 1024).toFixed(2)} KB, ${file.type})`);
|
|
|
|
// Generate unique filename
|
|
const ext = file.name.split(".").pop();
|
|
const filename = `${crypto.randomUUID()}.${ext}`;
|
|
const uploadDir = join(process.cwd(), "public", "uploads");
|
|
const filePath = join(uploadDir, filename);
|
|
|
|
try {
|
|
// Ensure directory exists
|
|
await mkdir(uploadDir, { recursive: true });
|
|
|
|
// Write file using Bun.write
|
|
await Bun.write(filePath, await file.arrayBuffer());
|
|
|
|
const APP_URL = process.env.APP_URL || "http://localhost:3000";
|
|
const publicUrl = `${APP_URL}/uploads/${filename}`;
|
|
|
|
console.log(`[Upload] Success: ${filename} -> ${publicUrl}`);
|
|
|
|
return {
|
|
success: true,
|
|
url: publicUrl,
|
|
filename
|
|
};
|
|
} catch (err) {
|
|
console.error("[Upload] Error:", err);
|
|
set.status = 500;
|
|
return { error: "Failed to save file" };
|
|
}
|
|
}, {
|
|
body: t.Object({
|
|
file: t.File({
|
|
maxSize: '5m',
|
|
type: ['image/jpeg', 'image/png', 'image/webp', 'image/gif']
|
|
})
|
|
}),
|
|
detail: {
|
|
summary: "Upload product image",
|
|
tags: ["upload"]
|
|
}
|
|
});
|