Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions frontend/apply/src/app/about/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import { useTranslation } from "react-i18next";
import Image from "next/image";
import { getImageUrl } from "@/utils/imageUrl";

export default function About() {
const { t } = useTranslation();
Expand All @@ -11,7 +10,7 @@ export default function About() {
<h1 style={{ marginTop: 48 }}>{t("aboutPage.title")}</h1>
<p>{t("aboutPage.introText")}</p>
<Image
src={getImageUrl("/städfestival-ht24.jpg")}
src="/städfestival-ht24.jpg"
style={{ margin: "12px 0", width: "100%", height: "auto" }}
alt="UTN members in front of our union house in winter"
width={0}
Expand Down
39 changes: 27 additions & 12 deletions frontend/apply/src/app/utils/imageUrl.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,43 @@
/**
* The backend returns relative paths like "/media/team_logos/dg.png".
* Next.js Image Optimization fetches the source server-side, so we must
* prepend an absolute base URL that the Next.js server can reach.
*
* Production set NEXT_PUBLIC_API_URL to a full absolute URL
* (e.g. "https://apply.utn.se/api"), or the
* current origin is used on the client.
* Docker Compose defaults to "http://backend:8000"
* The URL is used as the `src` of Next.js `<Image>`. The browser never
* loads it directly — it only sees `/_next/image?url=…`. So the URL
* must be reachable from the Next.js **server**.
*
* Docker dev → http://backend:8000 (always correct inside Docker)
* Production → window.location.origin (nginx proxies /media/ to Django)
*
* Set NEXT_PUBLIC_API_URL at **build time** to an absolute URL to override
* the Docker default (e.g. for local dev: http://localhost:8000/api).
*/
export function getImageUrl(url: string): string {
if (url.startsWith("http://") || url.startsWith("https://")) {
return url;
}

const apiUrl = process.env.NEXT_PUBLIC_API_URL;
// Paths that don't start with /media/ are local Next.js files
if (!url.startsWith("/media/")) {
return url;
}

// 1. Absolute env var – derive backend base from it
// Build-time env var override (NEXT_PUBLIC_* are inlined at build time)
const apiUrl = process.env.NEXT_PUBLIC_API_URL;
if (apiUrl && (apiUrl.startsWith("http://") || apiUrl.startsWith("https://"))) {
return `${apiUrl.replace(/\/api\/?$/, "")}${url}`;
}

// 2. Docker Compose dev – always use the internal Docker hostname.
// next/image fetches images on the server, so the browser never
// needs to resolve "backend". Both SSR and client must return the
// same URL to avoid a hydration mismatch.
// Client-side
if (typeof window !== "undefined") {
// Docker dev – don't use localhost:3000, the Next.js server needs
// to reach the backend container at backend:8000
if (window.location.hostname === "localhost") {
return `http://backend:8000${url}`;
}
// Production – nginx proxies /media/ to Django on the same domain
return `${window.location.origin}${url}`;
}

// SSR default: Docker Compose (Next.js server reaches backend:8000)
return `http://backend:8000${url}`;
}
Loading