import Fastify from "fastify"; import { processImageWithSmartCrop, parseImgproxyOptions, mapUrl, } from "@mintel/image-processor"; const fastify = Fastify({ logger: true, }); fastify.get("/unsafe/:options/:urlSafeB64", async (request, reply) => { const { options, urlSafeB64 } = request.params as { options: string; urlSafeB64: string; }; // urlSafeB64 might be "plain/http://..." or a Base64 string let url = ""; if (urlSafeB64.startsWith("plain/")) { url = urlSafeB64.substring(6); } else { try { url = Buffer.from(urlSafeB64, "base64").toString("utf-8"); } catch (e) { return reply.status(400).send({ error: "Invalid Base64 URL" }); } } const parsedOptions = parseImgproxyOptions(options); const mappedUrl = mapUrl(url, process.env.IMGPROXY_URL_MAPPING); return handleProcessing(mappedUrl, parsedOptions, reply); }); // Helper to avoid duplication async function handleProcessing(url: string, options: any, reply: any) { const width = options.width || 800; const height = options.height || 600; const quality = options.quality || 80; const format = options.format || "webp"; try { const response = await fetch(url); if (!response.ok) { return reply.status(response.status).send({ error: `Failed to fetch source image: ${response.statusText}`, }); } const arrayBuffer = await response.arrayBuffer(); const buffer = Buffer.from(arrayBuffer); const processedBuffer = await processImageWithSmartCrop(buffer, { width, height, format, quality, openRouterApiKey: process.env.OPENROUTER_API_KEY, }); reply.header("Content-Type", `image/${format}`); reply.header("Cache-Control", "public, max-age=31536000, immutable"); return reply.send(processedBuffer); } catch (err) { fastify.log.error(err); return reply .status(500) .send({ error: "Internal Server Error processing image" }); } } fastify.get("/process", async (request, reply) => { const query = request.query as { url?: string; w?: string; h?: string; q?: string; format?: string; }; const { url } = query; const width = parseInt(query.w || "800", 10); const height = parseInt(query.h || "600", 10); const quality = parseInt(query.q || "80", 10); const format = (query.format || "webp") as any; if (!url) { return reply.status(400).send({ error: 'Parameter "url" is required' }); } const mappedUrl = mapUrl(url, process.env.IMGPROXY_URL_MAPPING); return handleProcessing(mappedUrl, { width, height, quality, format }, reply); }); fastify.get("/health", async () => { return { status: "ok" }; }); const start = async () => { try { await fastify.listen({ port: 8080, host: "0.0.0.0" }); console.log(`Server listening on 8080`); } catch (err) { fastify.log.error(err); process.exit(1); } }; start();