All checks were successful
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧪 Test (push) Successful in 1m1s
Monorepo Pipeline / 🧹 Lint (push) Successful in 2m56s
Monorepo Pipeline / 🏗️ Build (push) Successful in 4m32s
Monorepo Pipeline / 🚀 Release (push) Has been skipped
Monorepo Pipeline / 🐳 Build Image Processor (push) Has been skipped
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Has been skipped
Monorepo Pipeline / 🐳 Build Build-Base (push) Has been skipped
Monorepo Pipeline / 🐳 Build Production Runtime (push) Has been skipped
111 lines
2.9 KiB
TypeScript
111 lines
2.9 KiB
TypeScript
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();
|