fix: refactor OG link builder (#17897)

* fix: manually encode special characters

* fix test

* fix

* fix
This commit is contained in:
Benny Joo
2024-12-02 19:01:09 +00:00
committed by GitHub
parent 262738f288
commit d057d7d37f
5 changed files with 35 additions and 35 deletions
+2 -2
View File
@@ -55,10 +55,10 @@ test("check SSR and OG - User Event Type", async ({ page, users }) => {
expect(canonicalLink).toEqual(`${WEBAPP_URL}/${user.username}/30-min`);
// Verify that there is correct URL that would generate the awesome OG image
expect(ogImage).toContain(
"/_next/image?w=1200&q=100&url=%2Fapi%2Fsocial%2Fog%2Fimage?type=meeting&title=30%20min"
"/_next/image?w=1200&q=100&url=%2Fapi%2Fsocial%2Fog%2Fimage%3Ftype%3Dmeeting%26title%3D30%2Bmin"
);
// Verify Organizer Name in the URL
expect(ogImage).toContain("meetingProfileName=Test%20User");
expect(ogImage).toContain("meetingProfileName%3DTest%2BUser");
});
todo("check SSR and OG - Team Event Type");
@@ -428,10 +428,10 @@ test.describe("Bookings", () => {
expect(canonicalLink).toEqual(`${orgOrigin}${calLink}`);
// Verify that there is correct URL that would generate the awesome OG image
expect(ogImage).toContain(
"/_next/image?w=1200&q=100&url=%2Fapi%2Fsocial%2Fog%2Fimage?type=meeting&title="
"/_next/image?w=1200&q=100&url=%2Fapi%2Fsocial%2Fog%2Fimage%3Ftype%3Dmeeting%26title%3D"
);
// Verify Organizer Name in the URL
expect(ogImage).toContain("meetingProfileName=Test%20User");
expect(ogImage).toContain("meetingProfileName%3DTest%2BUser");
}
);
});
@@ -64,9 +64,10 @@ test.describe("User Avatar", async () => {
);
// verify objectKey is passed to the OG image
// yes, OG image URI encodes at multiple places.. don't want to mess with that.
const searchParam = `meetingImage=${encodeURIComponent(`${CAL_URL}/api/avatar/${objectKey}.png`)}`;
await expect(page.locator('meta[property="og:image"]')).toHaveAttribute(
"content",
new RegExp(`meetingImage=${encodeURIComponent(`${CAL_URL}/api/avatar/${objectKey}.png`)}`)
new RegExp(encodeURIComponent(searchParam))
);
});
});
+28 -27
View File
@@ -57,17 +57,22 @@ const makeAbsoluteUrl = (url: string) => (/^https?:\/\//.test(url) ? url : `${CA
* 5. Dynamic collective (2 persons) http://localhost:3000/api/social/og/image?type=meeting&title=15min&meetingProfileName=Team%20Pro%20Example,%20Pro%20Example&names=Team%20Pro%20Example&names=Pro%20Example&usernames=teampro&usernames=pro
*/
export const constructMeetingImage = ({ title, users = [], profile }: MeetingImageProps): string => {
const url = [
`?type=meeting`,
`&title=${encodeURIComponent(title)}`,
`&meetingProfileName=${encodeURIComponent(profile.name)}`,
profile.image && `&meetingImage=${encodeURIComponent(makeAbsoluteUrl(profile.image))}`,
`${users.map((user) => `&names=${encodeURIComponent(user.name)}`).join("")}`,
`${users.map((user) => `&usernames=${encodeURIComponent(user.username)}`).join("")}`,
// Joining a multiline string for readability.
].join("");
const params = new URLSearchParams({
type: "meeting",
title,
meetingProfileName: profile.name,
});
return url;
if (profile.image) {
params.set("meetingImage", makeAbsoluteUrl(profile.image));
}
users.forEach((user) => {
params.append("names", user.name);
params.append("usernames", user.username);
});
return encodeURIComponent(`/api/social/og/image?${params.toString()}`);
};
/**
@@ -75,26 +80,22 @@ export const constructMeetingImage = ({ title, users = [], profile }: MeetingIma
* http://localhost:3000/api/social/og/image?type=app&name=Huddle01&slug=/api/app-store/huddle01video/icon.svg&description=Huddle01%20is%20a%20new%20video%20conferencing%20software%20native%20to%20Web3%20and%20is%20comparable%20to%20a%20decentralized%20version%20of%20Zoom.%20It%20supports%20conversations%20for...
*/
export const constructAppImage = ({ name, slug, description }: AppImageProps): string => {
const url = [
`?type=app`,
`&name=${encodeURIComponent(name)}`,
`&slug=${encodeURIComponent(slug)}`,
`&description=${encodeURIComponent(description)}`,
// Joining a multiline string for readability.
].join("");
return url;
const params = new URLSearchParams({
type: "app",
name,
slug,
description,
});
return encodeURIComponent(`/api/social/og/image?${params.toString()}`);
};
export const constructGenericImage = ({ title, description }: GenericImageProps) => {
const url = [
`?type=generic`,
`&title=${encodeURIComponent(title)}`,
`&description=${encodeURIComponent(description)}`,
// Joining a multiline string for readability.
].join("");
return url;
const params = new URLSearchParams({
type: "generic",
title,
description,
});
return encodeURIComponent(`/api/social/og/image?${params.toString()}`);
};
const Wrapper = ({ children, variant = "light", rotateBackground }: WrapperProps) => (
+1 -3
View File
@@ -87,9 +87,7 @@ export const SEO_IMG_DEFAULT = `${CAL_URL}/og-image.png`;
// This results in a 80% smaller image 🤯. It is however important that for the query
// parameters you pass to the /api/social/og/image endpoint, you wrap them in encodeURIComponent
// as well, otherwise the URL won't be valid.
export const SEO_IMG_OGIMG = `${CAL_URL}/_next/image?w=1200&q=100&url=${encodeURIComponent(
`/api/social/og/image`
)}`;
export const SEO_IMG_OGIMG = `${CAL_URL}/_next/image?w=1200&q=100&url=`;
export const SEO_IMG_OGIMG_VIDEO = `${CAL_URL}/video-og-image.png`;
export const IS_STRIPE_ENABLED = !!(
process.env.STRIPE_CLIENT_ID &&