All checks were successful
CI / ci (push) Successful in 15s
Run biome check --write --unsafe to fix tabs, import ordering, and non-null assertions across entire codebase. Disable a11y rules not applicable to this single-user app. Exclude auto-generated routeTree. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
48 lines
1.2 KiB
TypeScript
48 lines
1.2 KiB
TypeScript
import { randomUUID } from "node:crypto";
|
|
import { mkdir } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { Hono } from "hono";
|
|
|
|
const ALLOWED_TYPES = ["image/jpeg", "image/png", "image/webp"];
|
|
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
|
|
|
|
const app = new Hono();
|
|
|
|
app.post("/", async (c) => {
|
|
const body = await c.req.parseBody();
|
|
const file = body.image;
|
|
|
|
if (!file || typeof file === "string") {
|
|
return c.json({ error: "No image file provided" }, 400);
|
|
}
|
|
|
|
// Validate file type
|
|
if (!ALLOWED_TYPES.includes(file.type)) {
|
|
return c.json(
|
|
{ error: "Invalid file type. Accepted: jpeg, png, webp" },
|
|
400,
|
|
);
|
|
}
|
|
|
|
// Validate file size
|
|
if (file.size > MAX_SIZE) {
|
|
return c.json({ error: "File too large. Maximum size is 5MB" }, 400);
|
|
}
|
|
|
|
// Generate unique filename
|
|
const ext =
|
|
file.type.split("/")[1] === "jpeg" ? "jpg" : file.type.split("/")[1];
|
|
const filename = `${Date.now()}-${randomUUID()}.${ext}`;
|
|
|
|
// Ensure uploads directory exists
|
|
await mkdir("uploads", { recursive: true });
|
|
|
|
// Write file
|
|
const buffer = await file.arrayBuffer();
|
|
await Bun.write(join("uploads", filename), buffer);
|
|
|
|
return c.json({ filename }, 201);
|
|
});
|
|
|
|
export { app as imageRoutes };
|