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({ export const getMetadataInput = z.object({
subpage: z.string().optional(), subpage: z.string().optional(),
filterFromUrl: 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) { if (noIndex) {
metadata.robots = { metadata.robots = {
index: false, index: false,
follow: true, follow: false,
} }
} }
return metadata return metadata

View File

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