diff --git a/apps/web/src/app/[locale]/invite/[urlId]/page.tsx b/apps/web/src/app/[locale]/invite/[urlId]/page.tsx index 05ebab740..5799090e3 100644 --- a/apps/web/src/app/[locale]/invite/[urlId]/page.tsx +++ b/apps/web/src/app/[locale]/invite/[urlId]/page.tsx @@ -1,5 +1,6 @@ import { prisma } from "@rallly/database"; import { Metadata } from "next"; +import { notFound } from "next/navigation"; import { InvitePage } from "@/app/[locale]/invite/[urlId]/invite-page"; import { getTranslation } from "@/app/i18n"; @@ -35,12 +36,22 @@ export async function generateMetadata({ const { t } = await getTranslation(locale); if (!poll) { - return null; + notFound(); } const { title, id, user } = poll; - const author = user?.name || t("guest"); + const author = + user?.name || + t("guest", { + ns: "app", + defaultValue: "Guest", + }); + + const ogImageUrl = absoluteUrl("/api/og-image-poll", { + title, + author, + }); return { title, @@ -51,10 +62,7 @@ export async function generateMetadata({ url: `/invite/${id}`, images: [ { - url: `${absoluteUrl("/api/og-image-poll", { - title, - author, - })}`, + url: ogImageUrl, width: 1200, height: 630, alt: title, diff --git a/apps/web/src/utils/absolute-url.test.ts b/apps/web/src/utils/absolute-url.test.ts new file mode 100644 index 000000000..119afffac --- /dev/null +++ b/apps/web/src/utils/absolute-url.test.ts @@ -0,0 +1,53 @@ +import { afterAll, beforeAll, describe, expect, it } from "vitest"; + +import { absoluteUrl } from "./absolute-url"; + +describe("absoluteUrl", () => { + describe("when NEXT_PUBLIC_BASE_URL is set", () => { + beforeAll(() => { + process.env.NEXT_PUBLIC_BASE_URL = "https://example.com"; + }); + + afterAll(() => { + delete process.env.NEXT_PUBLIC_BASE_URL; + }); + + it("should return the value of NEXT_PUBLIC_BASE_URL", () => { + expect(absoluteUrl()).toBe("https://example.com"); + }); + it("should return the correct absolute URL with query params", () => { + expect(absoluteUrl("/", { test: "test" })).toBe( + "https://example.com/?test=test", + ); + }); + it("should return the correct absolute URL with a subpath and query params", () => { + expect(absoluteUrl("/test", { test: "test" })).toBe( + "https://example.com/test?test=test", + ); + }); + }); + + describe("when NEXT_PUBLIC_BASE_URL is not set", () => { + it("should return the correct absolute URL with a subpath and query params", () => { + expect(absoluteUrl("/test", { test: "test" })).toBe( + "http://localhost:3000/test?test=test", + ); + }); + + describe("when NEXT_PUBLIC_VERCEL_URL is set", () => { + beforeAll(() => { + process.env.NEXT_PUBLIC_VERCEL_URL = "example.vercel.com"; + }); + + afterAll(() => { + delete process.env.NEXT_PUBLIC_VERCEL_URL; + }); + + it("should return the correct absolute URL with a subpath and query params", () => { + expect(absoluteUrl("/test", { test: "test" })).toBe( + "https://example.vercel.com/test?test=test", + ); + }); + }); + }); +}); diff --git a/apps/web/src/utils/absolute-url.ts b/apps/web/src/utils/absolute-url.ts index 734e2b928..0d632f79d 100644 --- a/apps/web/src/utils/absolute-url.ts +++ b/apps/web/src/utils/absolute-url.ts @@ -15,27 +15,21 @@ function joinPath(baseUrl: string, subpath = "") { return baseUrl; } -export function objectToQueryString(obj: Record) { - const parts = []; - for (const key in obj) { - if (obj.hasOwnProperty(key)) { - const value = obj[key]; - if (value !== undefined) { - parts.push(encodeURIComponent(key) + "=" + encodeURIComponent(value)); - } - } - } - return parts.join("&"); -} -export function absoluteUrl(subpath = "", query?: Record) { - const queryString = query ? `?${objectToQueryString(query)}` : ""; - +export function absoluteUrl(subpath = "", query: Record = {}) { const baseUrl = process.env.NEXT_PUBLIC_BASE_URL ?? getVercelUrl() ?? `http://localhost:${port}`; - return joinPath(baseUrl, subpath) + queryString; + const url = new URL(subpath, baseUrl); + + Object.entries(query).forEach(([key, value]) => { + url.searchParams.set(key, value); + }); + + const urlString = url.href; + + return urlString.endsWith("/") ? urlString.slice(0, -1) : urlString; } export function shortUrl(subpath = "") {