diff --git a/apps/web/modules/users/views/users-public-view.getServerSideProps.tsx b/apps/web/modules/users/views/users-public-view.getServerSideProps.tsx index 46b64c6ef6..1ee4fc242b 100644 --- a/apps/web/modules/users/views/users-public-view.getServerSideProps.tsx +++ b/apps/web/modules/users/views/users-public-view.getServerSideProps.tsx @@ -7,6 +7,7 @@ import { handleUserRedirection } from "@calcom/features/booking-redirect/handle- import { orgDomainConfig } from "@calcom/features/ee/organizations/lib/orgDomains"; import { DEFAULT_DARK_BRAND_COLOR, DEFAULT_LIGHT_BRAND_COLOR } from "@calcom/lib/constants"; import { getUsernameList } from "@calcom/lib/defaultEvents"; +import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl"; import logger from "@calcom/lib/logger"; import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; import { safeStringify } from "@calcom/lib/safeStringify"; @@ -206,7 +207,10 @@ export const getServerSideProps: GetServerSideProps = async (cont const profile = { name: user.name || user.username || "", - image: user.avatar, + image: getUserAvatarUrl({ + ...user, + profile: user.profile, + }), theme: user.theme, brandColor: user.brandColor ?? DEFAULT_LIGHT_BRAND_COLOR, avatarUrl: user.avatarUrl, diff --git a/packages/features/auth/lib/getServerSession.ts b/packages/features/auth/lib/getServerSession.ts index 18924bc16f..e0b2141733 100644 --- a/packages/features/auth/lib/getServerSession.ts +++ b/packages/features/auth/lib/getServerSession.ts @@ -4,7 +4,7 @@ import type { AuthOptions, Session } from "next-auth"; import { getToken } from "next-auth/jwt"; import checkLicense from "@calcom/features/ee/common/server/checkLicense"; -import { WEBAPP_URL } from "@calcom/lib/constants"; +import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl"; import logger from "@calcom/lib/logger"; import { safeStringify } from "@calcom/lib/safeStringify"; import { UserRepository } from "@calcom/lib/server/repository/user"; @@ -94,7 +94,10 @@ export async function getServerSession(options: { emailVerified: user.emailVerified, email_verified: user.emailVerified !== null, role: user.role, - image: `${WEBAPP_URL}/${user.username}/avatar.png`, + image: getUserAvatarUrl({ + ...user, + profile: user.profile, + }), belongsToActiveTeam: token.belongsToActiveTeam, org: token.org, locale: user.locale ?? undefined, diff --git a/packages/features/eventtypes/lib/getPublicEvent.ts b/packages/features/eventtypes/lib/getPublicEvent.ts index 3d3b835749..4a4fbda0c2 100644 --- a/packages/features/eventtypes/lib/getPublicEvent.ts +++ b/packages/features/eventtypes/lib/getPublicEvent.ts @@ -8,19 +8,20 @@ import { getBookingFieldsWithSystemFields } from "@calcom/features/bookings/lib/ import { getSlugOrRequestedSlug } from "@calcom/features/ee/organizations/lib/orgDomains"; import { isRecurringEvent, parseRecurringEvent } from "@calcom/lib"; import { getDefaultEvent, getUsernameList } from "@calcom/lib/defaultEvents"; +import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl"; import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client"; import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML"; import { UserRepository } from "@calcom/lib/server/repository/user"; import type { PrismaClient } from "@calcom/prisma"; import type { BookerLayoutSettings } from "@calcom/prisma/zod-utils"; import { - bookerLayoutOptions, - EventTypeMetaDataSchema, - customInputSchema, - userMetadata as userMetadataSchema, - bookerLayouts as bookerLayoutsSchema, BookerLayouts, + EventTypeMetaDataSchema, + bookerLayoutOptions, + bookerLayouts as bookerLayoutsSchema, + customInputSchema, teamMetadataSchema, + userMetadata as userMetadataSchema, } from "@calcom/prisma/zod-utils"; import type { UserProfile } from "@calcom/types/UserProfile"; @@ -163,7 +164,10 @@ export const getPublicEvent = async ( username: users[0].username, name: users[0].name, weekStart: users[0].weekStart, - image: `/${users[0].username}/avatar.png`, + image: getUserAvatarUrl({ + ...users[0], + profile: users[0].profile, + }), brandColor: users[0].brandColor, darkBrandColor: users[0].darkBrandColor, theme: null, diff --git a/packages/lib/OgImages.tsx b/packages/lib/OgImages.tsx index 1bef494f38..88f562be4b 100644 --- a/packages/lib/OgImages.tsx +++ b/packages/lib/OgImages.tsx @@ -1,6 +1,6 @@ import React from "react"; -import { LOGO, WEBAPP_URL } from "./constants"; +import { CAL_URL, LOGO, WEBAPP_URL } from "./constants"; // Ensures tw prop is typed. declare module "react" { @@ -62,7 +62,7 @@ export const constructMeetingImage = ( `?type=meeting`, `&title=${encodeURIComponent(title)}`, `&meetingProfileName=${encodeURIComponent(profile.name)}`, - profile.image && `&meetingImage=${encodeURIComponent(WEBAPP_URL + profile.image)}`, + profile.image && `&meetingImage=${encodeURIComponent(profile.image)}`, `${users.map((user) => `&names=${encodeURIComponent(user.name)}`).join("")}`, `${users.map((user) => `&usernames=${encodeURIComponent(user.username)}`).join("")}`, // Joining a multiline string for readability. @@ -125,7 +125,7 @@ export const Meeting = ({ title, users = [], profile }: MeetingImageProps) => { const avatars = attendees .map((user) => { if ("image" in user && user?.image) return user.image; - if ("username" in user && user?.username) return `${WEBAPP_URL}/${user.username}/avatar.png`; + if ("username" in user && user?.username) return `${CAL_URL}/${user.username}/avatar.png`; return null; }) .filter(Boolean) as string[]; diff --git a/packages/lib/constants.ts b/packages/lib/constants.ts index 59960c41ee..4b90e3dc68 100644 --- a/packages/lib/constants.ts +++ b/packages/lib/constants.ts @@ -32,7 +32,11 @@ export const SENDER_NAME = process.env.NEXT_PUBLIC_SENDGRID_SENDER_NAME || "Cal. // This is the URL from which all Cal Links and their assets are served. // Use website URL to make links shorter(cal.com and not app.cal.com) // As website isn't setup for preview environments, use the webapp url instead -export const CAL_URL = new URL(WEBAPP_URL).hostname.endsWith(".vercel.app") ? WEBAPP_URL : WEBSITE_URL; +// If it's a .vercel.app domain, keep it. +// Else use the website url if defined and finally fallback to the webapp url +export const CAL_URL = new URL(WEBAPP_URL).hostname.endsWith(".vercel.app") + ? WEBAPP_URL + : process.env.NEXT_PUBLIC_WEBSITE_URL || WEBAPP_URL; export const IS_CALCOM = WEBAPP_URL && @@ -73,15 +77,15 @@ export const JOIN_DISCORD = "https://go.cal.com/discord"; export const POWERED_BY_URL = `${WEBSITE_URL}/?utm_source=embed&utm_medium=powered-by-button`; export const DOCS_URL = "https://cal.com/docs"; export const DEVELOPER_DOCS = "https://developer.cal.com"; -export const SEO_IMG_DEFAULT = `${WEBSITE_URL}/og-image.png`; +export const SEO_IMG_DEFAULT = `${CAL_URL}/og-image.png`; // The Dynamic OG Image is passed through Next's Image API to further optimize it. // 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 = `${WEBAPP_URL}/_next/image?w=1200&q=100&url=${encodeURIComponent( +export const SEO_IMG_OGIMG = `${CAL_URL}/_next/image?w=1200&q=100&url=${encodeURIComponent( "/api/social/og/image" )}`; -export const SEO_IMG_OGIMG_VIDEO = `${WEBSITE_URL}/video-og-image.png`; +export const SEO_IMG_OGIMG_VIDEO = `${CAL_URL}/video-og-image.png`; export const IS_STRIPE_ENABLED = !!( process.env.STRIPE_CLIENT_ID && process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY && diff --git a/packages/lib/getAvatarUrl.ts b/packages/lib/getAvatarUrl.ts index 76b6156984..b0d7d62ebc 100644 --- a/packages/lib/getAvatarUrl.ts +++ b/packages/lib/getAvatarUrl.ts @@ -1,11 +1,11 @@ -import { WEBAPP_URL } from "@calcom/lib/constants"; -import { AVATAR_FALLBACK } from "@calcom/lib/constants"; -import type { User, Team } from "@calcom/prisma/client"; +import { AVATAR_FALLBACK, CAL_URL, WEBAPP_URL } from "@calcom/lib/constants"; +import type { Team, User } from "@calcom/prisma/client"; import type { UserProfile } from "@calcom/types/UserProfile"; /** * Gives an organization aware avatar url for a user * It ensures that the wrong avatar isn't fetched by ensuring that organizationId is always passed + * It should always return a fully formed url */ export const getUserAvatarUrl = ( user: @@ -16,11 +16,11 @@ export const getUserAvatarUrl = ( | undefined ) => { if (user?.avatarUrl) { - return user.avatarUrl; + return CAL_URL + user.avatarUrl; } - if (!user?.username) return AVATAR_FALLBACK; + if (!user?.username) return CAL_URL + AVATAR_FALLBACK; // avatar.png automatically redirects to fallback avatar if user doesn't have one - return `${WEBAPP_URL}/${user.profile?.username}/avatar.png${ + return `${CAL_URL}/${user.profile?.username}/avatar.png${ user.profile?.organizationId ? `?orgId=${user.profile.organizationId}` : "" }`; }; diff --git a/packages/ui/components/head-seo/HeadSeo.tsx b/packages/ui/components/head-seo/HeadSeo.tsx index 63a5cca048..bf91882f70 100644 --- a/packages/ui/components/head-seo/HeadSeo.tsx +++ b/packages/ui/components/head-seo/HeadSeo.tsx @@ -4,8 +4,8 @@ import { usePathname } from "next/navigation"; import type { AppImageProps, MeetingImageProps } from "@calcom/lib/OgImages"; import { constructAppImage, constructGenericImage, constructMeetingImage } from "@calcom/lib/OgImages"; -import { APP_NAME, WEBSITE_URL, IS_CALCOM } from "@calcom/lib/constants"; -import { seoConfig, getSeoImage, buildCanonical } from "@calcom/lib/next-seo.config"; +import { APP_NAME, CAL_URL } from "@calcom/lib/constants"; +import { buildCanonical, getSeoImage, seoConfig } from "@calcom/lib/next-seo.config"; import { truncateOnWord } from "@calcom/lib/text"; export type HeadSeoProps = { @@ -71,11 +71,8 @@ export const HeadSeo = (props: HeadSeoProps): JSX.Element => { const path = usePathname(); // The below code sets the defaultUrl for our canonical tags // Get the router's path - const selfHostedOrigin = WEBSITE_URL || "https://cal.com"; // Set the default URL to either the current URL (if self-hosted) or https://cal.com canonical URL - const defaultUrl = IS_CALCOM - ? buildCanonical({ path, origin: "https://cal.com" }) - : buildCanonical({ path, origin: selfHostedOrigin }); + const defaultUrl = buildCanonical({ path, origin: CAL_URL }); const { title,