From 8b716a173cf9b962574de4c62dd276fded72d6ee Mon Sep 17 00:00:00 2001 From: "soroush.asadi" Date: Sat, 6 Jun 2026 23:19:39 +0330 Subject: [PATCH] fix(images): allow MinIO host in next/image remotePatterns (broken uploads) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit next.config had no images.remotePatterns, so the optimizer rejected every remote URL with HTTP 400 → all MinIO-hosted images (avatars, template art) showed broken. Add remotePatterns derived from NEXT_PUBLIC_MINIO_URL + dev hosts (172.28.144.1/ localhost/minio :9000). Verified /_next/image → 200 image/jpeg. Co-Authored-By: Claude Opus 4.8 --- next.config.mjs | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/next.config.mjs b/next.config.mjs index cf19c4e..4154aed 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -2,6 +2,37 @@ import createNextIntlPlugin from "next-intl/plugin"; const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts"); +/** + * Hosts the Next.js image optimizer is allowed to fetch from. Without these, every + * remote (MinIO uploads/avatars/template art) returns HTTP 400. Derived from + * NEXT_PUBLIC_MINIO_URL (baked at build) plus the dev MinIO hosts. + */ +function imageRemotePatterns() { + const patterns = []; + const seen = new Set(); + const add = (protocol, hostname, port) => { + if (!hostname) return; + const key = `${protocol}//${hostname}:${port}`; + if (seen.has(key)) return; + seen.add(key); + patterns.push({ protocol, hostname, port: port || "", pathname: "/**" }); + }; + const url = process.env.NEXT_PUBLIC_MINIO_URL; + if (url) { + try { + const u = new URL(url); + add(u.protocol.replace(":", ""), u.hostname, u.port); + } catch { + /* ignore malformed env */ + } + } + // dev / docker fallbacks + add("http", "172.28.144.1", "9000"); + add("http", "localhost", "9000"); + add("http", "minio", "9000"); + return patterns; +} + /** @type {import('next').NextConfig} */ const nextConfig = { output: "standalone", @@ -25,6 +56,8 @@ const nextConfig = { return config; }, images: { + // Allow the image optimizer to fetch MinIO uploads (avatars, template art, …). + remotePatterns: imageRemotePatterns(), // Placeholder art is now a same-origin SVG from /api/placeholder (offline-safe). // dangerouslyAllowSVG only ever serves our own generated gradients — never user // uploads — and the CSP + attachment disposition neutralise any script content.