import * as Sentry from "@sentry/nextjs" import { TRPCError } from "@trpc/server" import { unstable_noStore as noStore } from "next/cache" import { NextResponse } from "next/server" import { Lang } from "@scandic-hotels/common/constants/language" import { equalsIgnoreCaseAndAccents } from "@scandic-hotels/common/utils/stringEquals" import { gatewayTimeout, httpStatusByErrorCode, notFound, } from "@scandic-hotels/trpc/errors" import { Country } from "@scandic-hotels/trpc/types/country" import { isCityLocation, isHotelLocation, } from "@scandic-hotels/trpc/types/locations" import { serverClient } from "@/lib/trpc/server" import { timeout } from "@/utils/timeout" import { createDataResponse } from "./createDataResponse" export const revalidate = 28_800 // 8 hours export async function GET( _request: Request, { params }: { params: Promise<{ country: string }> } ) { try { const { country: countryParam } = await params const country = Object.values(Country).find((c) => equalsIgnoreCaseAndAccents(c, countryParam) ) if (!country) { throw notFound(`Country "${countryParam.toLowerCase()}" not found`) } const caller = await serverClient() const locations = await Promise.any([ timeout(3_000).then(() => { throw gatewayTimeout("Fetching locations timed out") }), caller.hotel.locations.get({ lang: Lang.en }), ]) const cities = locations.filter(isCityLocation).filter((c) => { return equalsIgnoreCaseAndAccents(c.countryName, countryParam) }) if (cities.length === 0) { throw notFound( `No cities found in country "${countryParam.toLowerCase()}"` ) } const hotels = locations .filter(isHotelLocation) .filter( (x) => x.isActive && x.isPublished && cities.some((c) => equalsIgnoreCaseAndAccents(x.relationships.city.name, c.name) ) ) if (hotels.length === 0) { throw notFound( `No hotels found in country "${countryParam.toLowerCase()}"` ) } return NextResponse.json( createDataResponse({ countryParam, hotels }, { includeCity: true }), { status: 200, headers: { "Cache-Control": `public, max-age=${revalidate}, stale-while-revalidate=86400`, "Netlify-CDN-Cache-Control": `public, max-age=${revalidate}, stale-while-revalidate=86400`, }, } ) } catch (error) { noStore() const noCacheHeader: HeadersInit = { "Cache-Control": `no-store, max-age=0`, } if (error instanceof TRPCError) { switch (error.code) { case "GATEWAY_TIMEOUT": case "INTERNAL_SERVER_ERROR": { return NextResponse.json( { message: error.cause?.toString() || error.message, }, { status: httpStatusByErrorCode(error), headers: { ...noCacheHeader, }, } ) } case "NOT_FOUND": { return NextResponse.json( { message: error.cause?.toString() || error.message, }, { status: 404, headers: { "Cache-Control": `public, max-age=${revalidate}, stale-while-revalidate=86400`, "Netlify-CDN-Cache-Control": `public, max-age=${revalidate}, stale-while-revalidate=86400`, }, } ) } } } Sentry.captureException(error) return NextResponse.json( { message: "Internal Server Error" }, { status: 500, headers: { ...noCacheHeader, }, } ) } }