diff --git a/apps/scandic-web/server/routers/contentstack/languageSwitcher/query.ts b/apps/scandic-web/server/routers/contentstack/languageSwitcher/query.ts index 9ea090333..fbd0010b8 100644 --- a/apps/scandic-web/server/routers/contentstack/languageSwitcher/query.ts +++ b/apps/scandic-web/server/routers/contentstack/languageSwitcher/query.ts @@ -1,15 +1,12 @@ -import { baseUrls } from "@/constants/routes/baseUrls" -import { findMyBooking } from "@/constants/routes/findMyBooking" -import { hotelreservation } from "@/constants/routes/hotelReservation" import { publicProcedure, router } from "@/server/trpc" import { getUidAndContentTypeByPath } from "@/services/cms/getUidAndContentTypeByPath" +import { getNonContentstackUrls } from "../metadata/output" import { getLanguageSwitcherInput } from "./input" import { getUrlsOfAllLanguages } from "./utils" import type { LanguageSwitcherData } from "@/types/requests/languageSwitcher" -import type { Lang } from "@/constants/languages" export const languageSwitcherQueryRouter = router({ get: publicProcedure @@ -17,29 +14,14 @@ export const languageSwitcherQueryRouter = router({ .query(async ({ input }) => { const { pathName, lang } = input const { uid, contentType } = await getUidAndContentTypeByPath(pathName) + let urls: LanguageSwitcherData | null = null if (!uid || !contentType) { - // we have pages that are not currently routed within contentstack context, - // therefor this fix is needed for some of these pages - if (Object.values(findMyBooking).includes(pathName)) { - const urls: LanguageSwitcherData = {} - return { - lang, - urls: Object.entries(findMyBooking).reduce((acc, [lang, url]) => { - acc[lang as Lang] = { url } - return urls - }, urls), - } - } - if (pathName.startsWith(hotelreservation(lang))) { - return { lang, urls: baseUrls } - } - - return { lang, urls: { [lang]: { url: pathName } } } + urls = getNonContentstackUrls(lang, pathName) + } else { + urls = await getUrlsOfAllLanguages(lang, uid, contentType) } - const urls = await getUrlsOfAllLanguages(lang, uid, contentType) - return { lang, urls, diff --git a/apps/scandic-web/server/routers/contentstack/metadata/output.ts b/apps/scandic-web/server/routers/contentstack/metadata/output.ts index 22cc1174b..7166ff24d 100644 --- a/apps/scandic-web/server/routers/contentstack/metadata/output.ts +++ b/apps/scandic-web/server/routers/contentstack/metadata/output.ts @@ -1,5 +1,10 @@ import { z } from "zod" +import { baseUrls } from "@/constants/routes/baseUrls" +import { findMyBooking } from "@/constants/routes/findMyBooking" +import { hotelreservation } from "@/constants/routes/hotelReservation" +import { env } from "@/env/server" + import { attributesSchema as hotelAttributesSchema } from "../../hotels/schemas/hotel" import { tempImageVaultAssetSchema } from "../schemas/imageVault" import { systemSchema } from "../schemas/system" @@ -8,7 +13,9 @@ import { getDescription, getImage, getTitle } from "./utils" import type { Metadata } from "next" import { Country } from "@/types/enums/country" +import type { LanguageSwitcherData } from "@/types/requests/languageSwitcher" import { RTETypeEnum } from "@/types/rte/enums" +import type { Lang } from "@/constants/languages" const metaDataJsonSchema = z.object({ children: z.array( @@ -101,6 +108,7 @@ export const metadataSchema = rawMetadataSchema.transform(async (data) => { const noIndex = !!data.web?.seo_metadata?.noindex const metadata: Metadata = { + metadataBase: new URL(env.PUBLIC_URL), title: await getTitle(data), description: getDescription(data), openGraph: { @@ -116,3 +124,21 @@ export const metadataSchema = rawMetadataSchema.transform(async (data) => { } return metadata }) + +// Several pages are not currently routed within contentstack context. +// This function is used to generate the urls for these pages. +export function getNonContentstackUrls(lang: Lang, pathName: string) { + if (Object.values(findMyBooking).includes(pathName)) { + const urls: LanguageSwitcherData = {} + return Object.entries(findMyBooking).reduce((acc, [lang, url]) => { + acc[lang as Lang] = { url } + return urls + }, urls) + } + + if (pathName.startsWith(hotelreservation(lang))) { + return baseUrls + } + + return { [lang]: { url: pathName } } +} diff --git a/apps/scandic-web/server/routers/contentstack/metadata/query.ts b/apps/scandic-web/server/routers/contentstack/metadata/query.ts index 45c621a85..c3a39f7db 100644 --- a/apps/scandic-web/server/routers/contentstack/metadata/query.ts +++ b/apps/scandic-web/server/routers/contentstack/metadata/query.ts @@ -16,11 +16,15 @@ import { contentStackUidWithServiceProcedure, router } from "@/server/trpc" import { generateTag } from "@/utils/generateTag" import { getHotel } from "../../hotels/query" +import { getUrlsOfAllLanguages } from "../languageSwitcher/utils" import { getMetadataInput } from "./input" -import { metadataSchema } from "./output" +import { getNonContentstackUrls, metadataSchema } from "./output" import { affix, getCityData, getCountryData } from "./utils" +import type { Metadata } from "next" + import { PageContentTypeEnum } from "@/types/requests/contentType" +import type { LanguageSwitcherData } from "@/types/requests/languageSwitcher" import type { RawMetadataSchema } from "@/types/trpc/routers/contentstack/metadata" import type { Lang } from "@/constants/languages" @@ -92,7 +96,10 @@ const fetchMetadata = cache(async function fetchMemoizedMetadata( return response.data }) -async function getTransformedMetadata(data: unknown) { +async function getTransformedMetadata( + data: unknown, + alternates: Metadata["alternates"] +) { transformMetadataCounter.add(1) console.info("contentstack.metadata transform start") const validatedMetadata = await metadataSchema.safeParseAsync(data) @@ -114,6 +121,9 @@ async function getTransformedMetadata(data: unknown) { transformMetadataSuccessCounter.add(1) console.info("contentstack.metadata transform success") + if (alternates) { + validatedMetadata.data.alternates = alternates + } return validatedMetadata.data } @@ -126,28 +136,66 @@ export const metadataQueryRouter = router({ uid: ctx.uid, } + let urls: LanguageSwitcherData | null = null + if ( + input.subpage || + input.filterFromUrl || + !ctx.uid || + !ctx.contentType + ) { + urls = getNonContentstackUrls(ctx.lang, `${ctx.lang}/${ctx.pathname}`) + } else { + urls = await getUrlsOfAllLanguages(ctx.lang, ctx.uid, ctx.contentType) + } + + let alternates: Metadata["alternates"] = null + + if (urls) { + const languages: Record = {} + Object.entries(urls) + .filter(([lang]) => lang !== ctx.lang) + .forEach(([lang, { url }]) => { + languages[lang] = url + }) + const canonical = urls[ctx.lang]?.url + alternates = { + canonical, + languages, + } + } + switch (ctx.contentType) { case PageContentTypeEnum.accountPage: const accountPageResponse = await fetchMetadata<{ account_page: RawMetadataSchema }>(GetAccountPageMetadata, variables) - return getTransformedMetadata(accountPageResponse.account_page) + return getTransformedMetadata( + accountPageResponse.account_page, + alternates + ) case PageContentTypeEnum.collectionPage: const collectionPageResponse = await fetchMetadata<{ collection_page: RawMetadataSchema }>(GetCollectionPageMetadata, variables) - return getTransformedMetadata(collectionPageResponse.collection_page) + return getTransformedMetadata( + collectionPageResponse.collection_page, + alternates + ) case PageContentTypeEnum.contentPage: const contentPageResponse = await fetchMetadata<{ content_page: RawMetadataSchema }>(GetContentPageMetadata, variables) - return getTransformedMetadata(contentPageResponse.content_page) + return getTransformedMetadata( + contentPageResponse.content_page, + alternates + ) case PageContentTypeEnum.destinationOverviewPage: const destinationOverviewPageResponse = await fetchMetadata<{ destination_overview_page: RawMetadataSchema }>(GetDestinationOverviewPageMetadata, variables) return getTransformedMetadata( - destinationOverviewPageResponse.destination_overview_page + destinationOverviewPageResponse.destination_overview_page, + alternates ) case PageContentTypeEnum.destinationCountryPage: const destinationCountryPageResponse = await fetchMetadata<{ @@ -159,10 +207,13 @@ export const metadataQueryRouter = router({ ctx.serviceToken, ctx.lang ) - return getTransformedMetadata({ - ...destinationCountryPageResponse.destination_country_page, - ...countryData, - }) + return getTransformedMetadata( + { + ...destinationCountryPageResponse.destination_country_page, + ...countryData, + }, + alternates + ) case PageContentTypeEnum.destinationCityPage: const destinationCityPageResponse = await fetchMetadata<{ destination_city_page: RawMetadataSchema @@ -173,15 +224,21 @@ export const metadataQueryRouter = router({ ctx.serviceToken, ctx.lang ) - return getTransformedMetadata({ - ...destinationCityPageResponse.destination_city_page, - ...cityData, - }) + return getTransformedMetadata( + { + ...destinationCityPageResponse.destination_city_page, + ...cityData, + }, + alternates + ) case PageContentTypeEnum.loyaltyPage: const loyaltyPageResponse = await fetchMetadata<{ loyalty_page: RawMetadataSchema }>(GetLoyaltyPageMetadata, variables) - return getTransformedMetadata(loyaltyPageResponse.loyalty_page) + return getTransformedMetadata( + loyaltyPageResponse.loyalty_page, + alternates + ) case PageContentTypeEnum.hotelPage: const hotelPageResponse = await fetchMetadata<{ hotel_page: RawMetadataSchema @@ -198,10 +255,13 @@ export const metadataQueryRouter = router({ ) : null - return getTransformedMetadata({ - ...hotelPageData, - hotelData: hotelData?.hotel, - }) + return getTransformedMetadata( + { + ...hotelPageData, + hotelData: hotelData?.hotel, + }, + alternates + ) default: return null }