Files
GearBox/src/server/routes/images.ts
Jean-Luc Makiola 17d76761bb fix: address code review issues — MCP auth, error handling, password route
- MCP auth middleware now rejects requests without API key when users exist
- Image /from-url route distinguishes validation errors (400) from server errors (500)
- Password change route returns 401 when no session cookie instead of crashing

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 13:42:34 +02:00

70 lines
1.9 KiB
TypeScript

import { randomUUID } from "node:crypto";
import { mkdir } from "node:fs/promises";
import { join } from "node:path";
import { zValidator } from "@hono/zod-validator";
import { Hono } from "hono";
import { z } from "zod";
import { fetchImageFromUrl } from "../services/image.service";
const ALLOWED_TYPES = ["image/jpeg", "image/png", "image/webp"];
const MAX_SIZE = 5 * 1024 * 1024; // 5MB
const fromUrlSchema = z.object({ url: z.string().url("Invalid URL") });
const app = new Hono();
app.post("/from-url", zValidator("json", fromUrlSchema), async (c) => {
const { url } = c.req.valid("json");
try {
const result = await fetchImageFromUrl(url);
return c.json(result, 201);
} catch (err) {
const message = (err as Error).message;
// Known validation errors from the service
const isValidationError =
message.startsWith("Invalid") ||
message.startsWith("URL must") ||
message.startsWith("File too") ||
message.startsWith("HTTP ");
return c.json({ error: message }, isValidationError ? 400 : 500);
}
});
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 };