From ff108963a4abb089dfbc83d6bf691a0e11a88ed3 Mon Sep 17 00:00:00 2001 From: pmgasset Date: Wed, 15 Apr 2026 11:10:44 -0400 Subject: [PATCH] Use R2 for public image assets --- .env.example | 5 ++++ next.config.ts | 34 +++++++++++++++++++++- src/components/sections/FounderSection.tsx | 3 +- src/components/sections/HeroSection.tsx | 3 +- src/lib/assets.ts | 33 +++++++++++++++++++++ src/lib/constants.ts | 18 ++++++++---- 6 files changed, 87 insertions(+), 9 deletions(-) create mode 100644 src/lib/assets.ts diff --git a/.env.example b/.env.example index edbf78f..6e037e9 100644 --- a/.env.example +++ b/.env.example @@ -27,3 +27,8 @@ AUTH_URL=http://localhost:3000 # https://console.cloud.google.com/apis/credentials AUTH_GOOGLE_ID= AUTH_GOOGLE_SECRET= + +# Cloudflare R2 public image origin +# Use a custom domain or R2 public development URL with no trailing slash. +# Upload site assets under /images, for example /images/hero-rv-sunset.jpg. +NEXT_PUBLIC_R2_PUBLIC_URL= diff --git a/next.config.ts b/next.config.ts index 862135b..6dc0968 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,8 +1,40 @@ import type { NextConfig } from "next"; +function getR2PublicUrl(): URL | null { + const publicUrl = process.env.NEXT_PUBLIC_R2_PUBLIC_URL?.trim(); + + if (!publicUrl) { + return null; + } + + try { + const url = new URL(publicUrl); + + if (url.protocol !== "https:" && url.protocol !== "http:") { + return null; + } + + return url; + } catch { + return null; + } +} + +const r2PublicUrl = getR2PublicUrl(); + const nextConfig: NextConfig = { images: { - remotePatterns: [], + remotePatterns: r2PublicUrl + ? [ + { + protocol: r2PublicUrl.protocol.replace(":", "") as "http" | "https", + hostname: r2PublicUrl.hostname, + port: r2PublicUrl.port, + pathname: "/**", + }, + ] + : [], + unoptimized: Boolean(r2PublicUrl), }, }; diff --git a/src/components/sections/FounderSection.tsx b/src/components/sections/FounderSection.tsx index 1577a3e..91e39dd 100644 --- a/src/components/sections/FounderSection.tsx +++ b/src/components/sections/FounderSection.tsx @@ -1,6 +1,7 @@ import Image from "next/image"; import SectionWrapper from "@/components/ui/SectionWrapper"; import Button from "@/components/ui/Button"; +import { assetUrl } from "@/lib/assets"; export default function FounderSection() { return ( @@ -10,7 +11,7 @@ export default function FounderSection() {
David, Founder of TechSolve Travel, working from his RV with a Starlink and Peplink setup {/* Background image */}