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:
@@ -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),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
},
|
})
|
||||||
alternates
|
break
|
||||||
)
|
|
||||||
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,
|
||||||
},
|
})
|
||||||
alternates
|
break
|
||||||
)
|
|
||||||
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,
|
||||||
},
|
})
|
||||||
alternates
|
break
|
||||||
)
|
|
||||||
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
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user