Compare commits

...

2 Commits

Author SHA1 Message Date
02e15c3f4a chore: sync versions to v1.8.19
Some checks failed
Monorepo Pipeline / ⚡ Prioritize Release (push) Successful in 2s
Monorepo Pipeline / 🧪 Test (push) Successful in 3m54s
Monorepo Pipeline / 🧹 Lint (push) Successful in 4m12s
Monorepo Pipeline / 🏗️ Build (push) Successful in 2m42s
Monorepo Pipeline / 🐳 Build Directus (Base) (push) Successful in 1m7s
Monorepo Pipeline / 🐳 Build Build-Base (push) Successful in 1m43s
Monorepo Pipeline / 🐳 Build Production Runtime (push) Successful in 1m37s
Monorepo Pipeline / 🐳 Build Image Processor (push) Successful in 2m47s
Monorepo Pipeline / 🐳 Build Gatekeeper (Product) (push) Successful in 6m39s
Monorepo Pipeline / 🚀 Release (push) Successful in 7m18s
🏥 Server Maintenance / 🧹 Prune & Clean (push) Failing after 8s
2026-02-23 00:52:35 +01:00
cd4c2193ce feat: implement legacy imgproxy compatibility and URL mapping
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
2026-02-23 00:14:13 +01:00
34 changed files with 134 additions and 55 deletions

View File

@@ -1,5 +1,5 @@
# Project # Project
IMAGE_TAG=v1.8.16 IMAGE_TAG=v1.8.19
PROJECT_NAME=sample-website PROJECT_NAME=sample-website
PROJECT_COLOR=#82ed20 PROJECT_COLOR=#82ed20

View File

@@ -1,6 +1,6 @@
{ {
"name": "image-service", "name": "image-service",
"version": "1.8.16", "version": "1.8.19",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@@ -1,37 +1,45 @@
import Fastify from "fastify"; import Fastify from "fastify";
import { processImageWithSmartCrop } from "@mintel/image-processor"; import {
processImageWithSmartCrop,
parseImgproxyOptions,
mapUrl,
} from "@mintel/image-processor";
const fastify = Fastify({ const fastify = Fastify({
logger: true, logger: true,
}); });
fastify.get("/unsafe/:options/:urlSafeB64", async (request, reply) => { fastify.get("/unsafe/:options/:urlSafeB64", async (request, reply) => {
// Compatibility endpoint for old imgproxy calls (optional, but requested by some systems sometimes) const { options, urlSafeB64 } = request.params as {
// For now, replacing logic in clients is preferred. So we just redirect or error. options: string;
return reply urlSafeB64: string;
.status(400)
.send({ error: "Legacy imgproxy API not supported. Use /process" });
});
fastify.get("/process", async (request, reply) => {
const query = request.query as {
url?: string;
w?: string;
h?: string;
q?: string;
format?: string;
}; };
const { url } = query; // urlSafeB64 might be "plain/http://..." or a Base64 string
const width = parseInt(query.w || "800", 10); let url = "";
const height = parseInt(query.h || "600", 10); if (urlSafeB64.startsWith("plain/")) {
const quality = parseInt(query.q || "80", 10); url = urlSafeB64.substring(6);
const format = (query.format || "webp") as "webp" | "jpeg" | "png" | "avif"; } else {
try {
if (!url) { url = Buffer.from(urlSafeB64, "base64").toString("utf-8");
return reply.status(400).send({ error: 'Parameter "url" is required' }); } 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 { try {
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) { if (!response.ok) {
@@ -60,6 +68,29 @@ fastify.get("/process", async (request, reply) => {
.status(500) .status(500)
.send({ error: "Internal Server Error processing image" }); .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 () => { fastify.get("/health", async () => {

View File

@@ -1,6 +1,6 @@
{ {
"name": "sample-website", "name": "sample-website",
"version": "1.8.16", "version": "1.8.19",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@@ -57,7 +57,7 @@
"pino-pretty": "^13.1.3", "pino-pretty": "^13.1.3",
"require-in-the-middle": "^8.0.1" "require-in-the-middle": "^8.0.1"
}, },
"version": "1.8.16", "version": "1.8.19",
"pnpm": { "pnpm": {
"onlyBuiltDependencies": [ "onlyBuiltDependencies": [
"@parcel/watcher", "@parcel/watcher",

View File

@@ -2,7 +2,7 @@
"name": "acquisition-manager", "name": "acquisition-manager",
"description": "Custom High-Fidelity Management for Directus", "description": "Custom High-Fidelity Management for Directus",
"icon": "extension", "icon": "extension",
"version": "1.8.16", "version": "1.8.19",
"type": "module", "type": "module",
"keywords": [ "keywords": [
"directus", "directus",

View File

@@ -1,6 +1,6 @@
{ {
"name": "acquisition", "name": "acquisition",
"version": "1.8.16", "version": "1.8.19",
"type": "module", "type": "module",
"directus:extension": { "directus:extension": {
"type": "endpoint", "type": "endpoint",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/cli", "name": "@mintel/cli",
"version": "1.8.16", "version": "1.8.19",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/cloner", "name": "@mintel/cloner",
"version": "1.8.16", "version": "1.8.19",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",
"module": "dist/index.js", "module": "dist/index.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/cms-infra", "name": "@mintel/cms-infra",
"version": "1.8.16", "version": "1.8.19",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@@ -2,7 +2,7 @@
"name": "company-manager", "name": "company-manager",
"description": "Custom High-Fidelity Management for Directus", "description": "Custom High-Fidelity Management for Directus",
"icon": "extension", "icon": "extension",
"version": "1.8.16", "version": "1.8.19",
"type": "module", "type": "module",
"keywords": [ "keywords": [
"directus", "directus",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/content-engine", "name": "@mintel/content-engine",
"version": "1.8.16", "version": "1.8.19",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -2,7 +2,7 @@
"name": "customer-manager", "name": "customer-manager",
"description": "Custom High-Fidelity Management for Directus", "description": "Custom High-Fidelity Management for Directus",
"icon": "extension", "icon": "extension",
"version": "1.8.16", "version": "1.8.19",
"type": "module", "type": "module",
"keywords": [ "keywords": [
"directus", "directus",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/directus-extension-toolkit", "name": "@mintel/directus-extension-toolkit",
"version": "1.8.16", "version": "1.8.19",
"description": "Shared toolkit for Directus extensions in the Mintel ecosystem", "description": "Shared toolkit for Directus extensions in the Mintel ecosystem",
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/eslint-config", "name": "@mintel/eslint-config",
"version": "1.8.16", "version": "1.8.19",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"

View File

@@ -2,7 +2,7 @@
"name": "feedback-commander", "name": "feedback-commander",
"description": "Custom High-Fidelity Management for Directus", "description": "Custom High-Fidelity Management for Directus",
"icon": "extension", "icon": "extension",
"version": "1.8.16", "version": "1.8.19",
"type": "module", "type": "module",
"keywords": [ "keywords": [
"directus", "directus",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/gatekeeper", "name": "@mintel/gatekeeper",
"version": "1.8.16", "version": "1.8.19",
"private": true, "private": true,
"type": "module", "type": "module",
"scripts": { "scripts": {

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/husky-config", "name": "@mintel/husky-config",
"version": "1.8.16", "version": "1.8.19",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/image-processor", "name": "@mintel/image-processor",
"version": "1.8.16", "version": "1.8.19",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -8,6 +8,54 @@ export interface ProcessImageOptions {
openRouterApiKey?: string; openRouterApiKey?: string;
} }
/**
* Maps a URL based on the IMGPROXY_URL_MAPPING environment variable.
* Format: "match1:replace1,match2:replace2"
*/
export function mapUrl(url: string, mappingString?: string): string {
if (!mappingString) return url;
const mappings = mappingString.split(",").map((m) => m.split(":"));
let mappedUrl = url;
for (const [match, replace] of mappings) {
if (match && replace && url.includes(match)) {
mappedUrl = url.replace(match, replace);
}
}
return mappedUrl;
}
/**
* Parses legacy imgproxy options string.
* Example: rs:fill:300:400/q:80
*/
export function parseImgproxyOptions(
optionsStr: string,
): Partial<ProcessImageOptions> {
const parts = optionsStr.split("/");
const options: Partial<ProcessImageOptions> = {};
for (const part of parts) {
if (part.startsWith("rs:")) {
const [, , w, h] = part.split(":");
if (w) options.width = parseInt(w, 10);
if (h) options.height = parseInt(h, 10);
} else if (part.startsWith("q:")) {
const q = part.split(":")[1];
if (q) options.quality = parseInt(q, 10);
} else if (part.startsWith("ext:")) {
const ext = part.split(":")[1] as any;
if (["webp", "jpeg", "png", "avif"].includes(ext)) {
options.format = ext;
}
}
}
return options;
}
interface FaceDetection { interface FaceDetection {
x: number; x: number;
y: number; y: number;

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/infra", "name": "@mintel/infra",
"version": "1.8.16", "version": "1.8.19",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/journaling", "name": "@mintel/journaling",
"version": "1.8.16", "version": "1.8.19",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/mail", "name": "@mintel/mail",
"version": "1.8.16", "version": "1.8.19",
"private": false, "private": false,
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/meme-generator", "name": "@mintel/meme-generator",
"version": "1.8.16", "version": "1.8.19",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/next-config", "name": "@mintel/next-config",
"version": "1.8.16", "version": "1.8.19",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/next-feedback", "name": "@mintel/next-feedback",
"version": "1.8.16", "version": "1.8.19",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/next-observability", "name": "@mintel/next-observability",
"version": "1.8.16", "version": "1.8.19",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/next-utils", "name": "@mintel/next-utils",
"version": "1.8.16", "version": "1.8.19",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/observability", "name": "@mintel/observability",
"version": "1.8.16", "version": "1.8.19",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/pdf", "name": "@mintel/pdf",
"version": "1.8.16", "version": "1.8.19",
"type": "module", "type": "module",
"main": "dist/index.js", "main": "dist/index.js",
"module": "dist/index.js", "module": "dist/index.js",

View File

@@ -2,7 +2,7 @@
"name": "people-manager", "name": "people-manager",
"description": "Custom High-Fidelity Management for Directus", "description": "Custom High-Fidelity Management for Directus",
"icon": "extension", "icon": "extension",
"version": "1.8.16", "version": "1.8.19",
"type": "module", "type": "module",
"keywords": [ "keywords": [
"directus", "directus",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/thumbnail-generator", "name": "@mintel/thumbnail-generator",
"version": "1.8.16", "version": "1.8.19",
"private": true, "private": true,
"type": "module", "type": "module",
"main": "./dist/index.js", "main": "./dist/index.js",

View File

@@ -1,6 +1,6 @@
{ {
"name": "@mintel/tsconfig", "name": "@mintel/tsconfig",
"version": "1.8.16", "version": "1.8.19",
"publishConfig": { "publishConfig": {
"access": "public", "access": "public",
"registry": "https://npm.infra.mintel.me" "registry": "https://npm.infra.mintel.me"

View File

@@ -2,7 +2,7 @@
"name": "unified-dashboard", "name": "unified-dashboard",
"description": "Custom High-Fidelity Management for Directus", "description": "Custom High-Fidelity Management for Directus",
"icon": "extension", "icon": "extension",
"version": "1.8.16", "version": "1.8.19",
"type": "module", "type": "module",
"keywords": [ "keywords": [
"directus", "directus",