feat(SW-1885): Added noindex/nofollow to pages with searchparams when generating metadata to avoid duplicate content issues

Approved-by: Linus Flood
This commit is contained in:
Erik Tiekstra
2025-03-17 13:15:13 +00:00
parent e530905d8d
commit 05addfa0bb
4 changed files with 56 additions and 50 deletions

View File

@@ -3,4 +3,5 @@ import { z } from "zod"
export const getMetadataInput = z.object({
subpage: z.string().optional(),
filterFromUrl: z.string().optional(),
noIndex: z.boolean().default(false),
})

View File

@@ -120,7 +120,7 @@ export const metadataSchema = rawMetadataSchema.transform(async (data) => {
if (noIndex) {
metadata.robots = {
index: false,
follow: true,
follow: false,
}
}
return metadata

View File

@@ -95,10 +95,7 @@ const fetchMetadata = cache(async function fetchMemoizedMetadata<T>(
return response.data
})
async function getTransformedMetadata(
data: unknown,
alternates: Metadata["alternates"]
) {
async function getTransformedMetadata(data: unknown) {
transformMetadataCounter.add(1)
console.info("contentstack.metadata transform start")
const validatedMetadata = await metadataSchema.safeParseAsync(data)
@@ -120,9 +117,6 @@ async function getTransformedMetadata(
transformMetadataSuccessCounter.add(1)
console.info("contentstack.metadata transform success")
if (alternates) {
validatedMetadata.data.alternates = alternates
}
return validatedMetadata.data
}
@@ -163,39 +157,41 @@ export const metadataQueryRouter = router({
}
}
let metadata: Metadata | null = null
switch (ctx.contentType) {
case PageContentTypeEnum.accountPage:
const accountPageResponse = await fetchMetadata<{
account_page: RawMetadataSchema
}>(GetAccountPageMetadata, variables)
return getTransformedMetadata(
accountPageResponse.account_page,
alternates
metadata = await getTransformedMetadata(
accountPageResponse.account_page
)
break
case PageContentTypeEnum.collectionPage:
const collectionPageResponse = await fetchMetadata<{
collection_page: RawMetadataSchema
}>(GetCollectionPageMetadata, variables)
return getTransformedMetadata(
collectionPageResponse.collection_page,
alternates
metadata = await getTransformedMetadata(
collectionPageResponse.collection_page
)
break
case PageContentTypeEnum.contentPage:
const contentPageResponse = await fetchMetadata<{
content_page: RawMetadataSchema
}>(GetContentPageMetadata, variables)
return getTransformedMetadata(
contentPageResponse.content_page,
alternates
metadata = await getTransformedMetadata(
contentPageResponse.content_page
)
break
case PageContentTypeEnum.destinationOverviewPage:
const destinationOverviewPageResponse = await fetchMetadata<{
destination_overview_page: RawMetadataSchema
}>(GetDestinationOverviewPageMetadata, variables)
return getTransformedMetadata(
destinationOverviewPageResponse.destination_overview_page,
alternates
metadata = await getTransformedMetadata(
destinationOverviewPageResponse.destination_overview_page
)
break
case PageContentTypeEnum.destinationCountryPage:
const destinationCountryPageResponse = await fetchMetadata<{
destination_country_page: RawMetadataSchema
@@ -206,13 +202,11 @@ export const metadataQueryRouter = router({
ctx.serviceToken,
ctx.lang
)
return getTransformedMetadata(
{
...destinationCountryPageResponse.destination_country_page,
...countryData,
},
alternates
)
metadata = await getTransformedMetadata({
...destinationCountryPageResponse.destination_country_page,
...countryData,
})
break
case PageContentTypeEnum.destinationCityPage:
const destinationCityPageResponse = await fetchMetadata<{
destination_city_page: RawMetadataSchema
@@ -223,21 +217,19 @@ export const metadataQueryRouter = router({
ctx.serviceToken,
ctx.lang
)
return getTransformedMetadata(
{
...destinationCityPageResponse.destination_city_page,
...cityData,
},
alternates
)
metadata = await getTransformedMetadata({
...destinationCityPageResponse.destination_city_page,
...cityData,
})
break
case PageContentTypeEnum.loyaltyPage:
const loyaltyPageResponse = await fetchMetadata<{
loyalty_page: RawMetadataSchema
}>(GetLoyaltyPageMetadata, variables)
return getTransformedMetadata(
loyaltyPageResponse.loyalty_page,
alternates
metadata = await getTransformedMetadata(
loyaltyPageResponse.loyalty_page
)
break
case PageContentTypeEnum.hotelPage:
const hotelPageResponse = await fetchMetadata<{
hotel_page: RawMetadataSchema
@@ -254,23 +246,32 @@ export const metadataQueryRouter = router({
)
: null
return getTransformedMetadata(
{
...hotelPageData,
hotelData: hotelData?.hotel,
},
alternates
)
metadata = await getTransformedMetadata({
...hotelPageData,
hotelData: hotelData?.hotel,
})
break
case PageContentTypeEnum.startPage:
const startPageResponse = await fetchMetadata<{
start_page: RawMetadataSchema
}>(GetStartPageMetadata, variables)
return getTransformedMetadata(
startPageResponse.start_page,
alternates
)
metadata = await getTransformedMetadata(startPageResponse.start_page)
default:
return null
break
}
if (metadata) {
if (alternates) {
metadata.alternates = alternates
}
if (input.noIndex) {
metadata.robots = {
index: false,
follow: false,
}
}
}
return metadata
}),
})

View File

@@ -13,10 +13,14 @@ export async function generateMetadata({
LangParams & ContentTypeParams & UIDParams,
{ subpage?: string; filterFromUrl?: string }
>) {
const { subpage, filterFromUrl } = searchParams
const { subpage, filterFromUrl, ...otherSearchParams } = searchParams
// If there are other (real) search params, we don't want to index the page as this will
// cause duplicate content issues.
const noIndexOnSearchParams = !!Object.keys(otherSearchParams).length
const metadata = await serverClient().contentstack.metadata.get({
subpage,
filterFromUrl,
noIndex: noIndexOnSearchParams,
})
return metadata
}