feat: update getHotel to use real hotel api endpoint, support for service tokens, type modifications
This commit is contained in:
@@ -11,6 +11,7 @@ CURITY_CLIENT_SECRET_SERVICE=""
|
|||||||
CURITY_CLIENT_ID_USER=""
|
CURITY_CLIENT_ID_USER=""
|
||||||
CURITY_CLIENT_SECRET_USER=""
|
CURITY_CLIENT_SECRET_USER=""
|
||||||
CURITY_ISSUER_USER="https://testlogin.scandichotels.com"
|
CURITY_ISSUER_USER="https://testlogin.scandichotels.com"
|
||||||
|
CURITY_ISSUER_SERVICE="https://testlogin.scandichotels.com"
|
||||||
CYPRESS_BASE_URL="http://localhost:3000"
|
CYPRESS_BASE_URL="http://localhost:3000"
|
||||||
# See next.config.js for info
|
# See next.config.js for info
|
||||||
DEPLOY_PRIME_URL="http://localhost:3000"
|
DEPLOY_PRIME_URL="http://localhost:3000"
|
||||||
|
|||||||
@@ -18,14 +18,20 @@ export default async function SelectHotelPage({
|
|||||||
const intl = await getIntl()
|
const intl = await getIntl()
|
||||||
setLang(params.lang)
|
setLang(params.lang)
|
||||||
|
|
||||||
const { attributes } = await serverClient().hotel.getHotel({
|
const hotelData = await serverClient().hotel.getHotel({
|
||||||
hotelId: "d98c7ab1-ebaa-4102-b351-758daf1ddf55",
|
hotelId: "879",
|
||||||
language: getLang(),
|
language: getLang(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!hotelData) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const { attributes } = hotelData
|
||||||
const hotels = [attributes]
|
const hotels = [attributes]
|
||||||
|
|
||||||
const hotelFilters = await serverClient().hotel.getFilters({
|
const hotelFilters = await serverClient().hotel.getFilters({
|
||||||
hotelId: "d98c7ab1-ebaa-4102-b351-758daf1ddf55",
|
hotelId: "879",
|
||||||
})
|
})
|
||||||
|
|
||||||
const tempSearchTerm = "Stockholm"
|
const tempSearchTerm = "Stockholm"
|
||||||
|
|||||||
@@ -15,10 +15,15 @@ export default async function SelectRate({ params }: PageArgs<LangParams>) {
|
|||||||
setLang(params.lang)
|
setLang(params.lang)
|
||||||
|
|
||||||
// TODO: pass the correct hotel ID
|
// TODO: pass the correct hotel ID
|
||||||
const { attributes: hotel } = await serverClient().hotel.getHotel({
|
const hotel = await serverClient().hotel.getHotel({
|
||||||
hotelId: "d98c7ab1-ebaa-4102-b351-758daf1ddf55",
|
hotelId: "879",
|
||||||
language: getLang(),
|
language: getLang(),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!hotel) return null
|
||||||
|
|
||||||
|
const { attributes } = hotel
|
||||||
|
|
||||||
const rooms = await serverClient().hotel.getRates({
|
const rooms = await serverClient().hotel.getRates({
|
||||||
// TODO: pass the correct hotel ID and all other parameters that should be included in the search
|
// TODO: pass the correct hotel ID and all other parameters that should be included in the search
|
||||||
hotelId: "1",
|
hotelId: "1",
|
||||||
@@ -28,7 +33,7 @@ export default async function SelectRate({ params }: PageArgs<LangParams>) {
|
|||||||
<div className={styles.page}>
|
<div className={styles.page}>
|
||||||
<main className={styles.content}>
|
<main className={styles.content}>
|
||||||
<div className={styles.hotelInfo}>
|
<div className={styles.hotelInfo}>
|
||||||
<HotelCard hotel={hotel} />
|
<HotelCard hotel={attributes} />
|
||||||
</div>
|
</div>
|
||||||
<RoomSelection rooms={rooms} />
|
<RoomSelection rooms={rooms} />
|
||||||
<FlexibilitySelection />
|
<FlexibilitySelection />
|
||||||
|
|||||||
@@ -18,12 +18,18 @@ export default async function HotelPage() {
|
|||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
const lang = getLang()
|
const lang = getLang()
|
||||||
const { attributes, roomCategories } = await serverClient().hotel.getHotel({
|
|
||||||
|
const hotelData = await serverClient().hotel.getHotel({
|
||||||
hotelId: hotelPageIdentifierData.hotel_page_id,
|
hotelId: hotelPageIdentifierData.hotel_page_id,
|
||||||
language: lang,
|
language: lang,
|
||||||
include: ["RoomCategories"],
|
include: ["RoomCategories"],
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!hotelData) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const { attributes, roomCategories } = hotelData
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.pageContainer}>
|
<div className={styles.pageContainer}>
|
||||||
<TabNavigation />
|
<TabNavigation />
|
||||||
@@ -34,7 +40,7 @@ export default async function HotelPage() {
|
|||||||
hotelDescription={attributes.hotelContent.texts.descriptions.short}
|
hotelDescription={attributes.hotelContent.texts.descriptions.short}
|
||||||
location={attributes.location}
|
location={attributes.location}
|
||||||
address={attributes.address}
|
address={attributes.address}
|
||||||
tripAdvisor={attributes.ratings.tripAdvisor}
|
tripAdvisor={attributes.ratings?.tripAdvisor}
|
||||||
/>
|
/>
|
||||||
<SidePeeks />
|
<SidePeeks />
|
||||||
<AmenitiesList detailedFacilities={attributes.detailedFacilities} />
|
<AmenitiesList detailedFacilities={attributes.detailedFacilities} />
|
||||||
|
|||||||
@@ -30,10 +30,14 @@ export default async function IntroSection({
|
|||||||
)
|
)
|
||||||
const lang = getLang()
|
const lang = getLang()
|
||||||
const formattedLocationText = `${streetAddress}, ${city} (${formattedDistanceText})`
|
const formattedLocationText = `${streetAddress}, ${city} (${formattedDistanceText})`
|
||||||
const formattedTripAdvisorText = intl.formatMessage(
|
const hasTripAdvisorData =
|
||||||
{ id: "Tripadvisor reviews" },
|
tripAdvisor?.rating && tripAdvisor?.numberOfReviews && tripAdvisor?.webUrl
|
||||||
{ rating: tripAdvisor.rating, count: tripAdvisor.numberOfReviews }
|
const formattedTripAdvisorText = hasTripAdvisorData
|
||||||
)
|
? intl.formatMessage(
|
||||||
|
{ id: "Tripadvisor reviews" },
|
||||||
|
{ rating: tripAdvisor.rating, count: tripAdvisor.numberOfReviews }
|
||||||
|
)
|
||||||
|
: ""
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className={styles.introSection}>
|
<section className={styles.introSection}>
|
||||||
@@ -45,17 +49,19 @@ export default async function IntroSection({
|
|||||||
<Title level="h2">{hotelName}</Title>
|
<Title level="h2">{hotelName}</Title>
|
||||||
</div>
|
</div>
|
||||||
<Body color="textMediumContrast">{formattedLocationText}</Body>
|
<Body color="textMediumContrast">{formattedLocationText}</Body>
|
||||||
<Link
|
{hasTripAdvisorData && (
|
||||||
className={styles.introLink}
|
<Link
|
||||||
target="_blank"
|
className={styles.introLink}
|
||||||
variant="icon"
|
target="_blank"
|
||||||
textDecoration="underline"
|
variant="icon"
|
||||||
color="peach80"
|
textDecoration="underline"
|
||||||
href={tripAdvisor.webUrl}
|
color="peach80"
|
||||||
>
|
href={tripAdvisor.webUrl}
|
||||||
<TripAdvisorIcon color="peach80" />
|
>
|
||||||
{formattedTripAdvisorText}
|
<TripAdvisorIcon color="peach80" />
|
||||||
</Link>
|
{formattedTripAdvisorText}
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.subtitleContent}>
|
<div className={styles.subtitleContent}>
|
||||||
<Preamble>{hotelDescription}</Preamble>
|
<Preamble>{hotelDescription}</Preamble>
|
||||||
|
|||||||
6
env/server.ts
vendored
6
env/server.ts
vendored
@@ -21,8 +21,11 @@ export const env = createEnv({
|
|||||||
CMS_PREVIEW_URL: z.string(),
|
CMS_PREVIEW_URL: z.string(),
|
||||||
CMS_URL: z.string(),
|
CMS_URL: z.string(),
|
||||||
CURITY_CLIENT_ID_USER: z.string(),
|
CURITY_CLIENT_ID_USER: z.string(),
|
||||||
|
CURITY_CLIENT_ID_SERVICE: z.string(),
|
||||||
|
CURITY_CLIENT_SECRET_SERVICE: z.string(),
|
||||||
CURITY_CLIENT_SECRET_USER: z.string(),
|
CURITY_CLIENT_SECRET_USER: z.string(),
|
||||||
CURITY_ISSUER_USER: z.string(),
|
CURITY_ISSUER_USER: z.string(),
|
||||||
|
CURITY_ISSUER_SERVICE: z.string(),
|
||||||
CYPRESS_BASE_URL: z.string().default("http://127.0.0.1:3000"),
|
CYPRESS_BASE_URL: z.string().default("http://127.0.0.1:3000"),
|
||||||
DESIGN_SYSTEM_ACCESS_TOKEN: z.string(),
|
DESIGN_SYSTEM_ACCESS_TOKEN: z.string(),
|
||||||
ENVTEST: z.string().optional(),
|
ENVTEST: z.string().optional(),
|
||||||
@@ -76,8 +79,11 @@ export const env = createEnv({
|
|||||||
CMS_PREVIEW_URL: process.env.CMS_PREVIEW_URL,
|
CMS_PREVIEW_URL: process.env.CMS_PREVIEW_URL,
|
||||||
CMS_URL: process.env.CMS_URL,
|
CMS_URL: process.env.CMS_URL,
|
||||||
CURITY_CLIENT_ID_USER: process.env.CURITY_CLIENT_ID_USER,
|
CURITY_CLIENT_ID_USER: process.env.CURITY_CLIENT_ID_USER,
|
||||||
|
CURITY_CLIENT_ID_SERVICE: process.env.CURITY_CLIENT_ID_SERVICE,
|
||||||
|
CURITY_CLIENT_SECRET_SERVICE: process.env.CURITY_CLIENT_SECRET_SERVICE,
|
||||||
CURITY_CLIENT_SECRET_USER: process.env.CURITY_CLIENT_SECRET_USER,
|
CURITY_CLIENT_SECRET_USER: process.env.CURITY_CLIENT_SECRET_USER,
|
||||||
CURITY_ISSUER_USER: process.env.CURITY_ISSUER_USER,
|
CURITY_ISSUER_USER: process.env.CURITY_ISSUER_USER,
|
||||||
|
CURITY_ISSUER_SERVICE: process.env.CURITY_ISSUER_SERVICE,
|
||||||
CYPRESS_BASE_URL: process.env.CYPRESS_TEST_URL,
|
CYPRESS_BASE_URL: process.env.CYPRESS_TEST_URL,
|
||||||
DESIGN_SYSTEM_ACCESS_TOKEN: process.env.DESIGN_SYSTEM_ACCESS_TOKEN,
|
DESIGN_SYSTEM_ACCESS_TOKEN: process.env.DESIGN_SYSTEM_ACCESS_TOKEN,
|
||||||
ENVTEST: process.env.ENVTEST,
|
ENVTEST: process.env.ENVTEST,
|
||||||
|
|||||||
@@ -12,8 +12,14 @@ export namespace endpoints {
|
|||||||
friendTransactions = "profile/v1/Transaction/friendTransactions",
|
friendTransactions = "profile/v1/Transaction/friendTransactions",
|
||||||
upcomingStays = "booking/v1/Stays/future",
|
upcomingStays = "booking/v1/Stays/future",
|
||||||
previousStays = "booking/v1/Stays/past",
|
previousStays = "booking/v1/Stays/past",
|
||||||
hotel = "hotel/v1/Hotels",
|
hotels = "hotel/v1/Hotels",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export type Endpoint = endpoints.v0 | endpoints.v1
|
export const getHotelEndpoint = (hotelId: string | number) =>
|
||||||
|
`${endpoints.v1.hotels}/${hotelId}` as const
|
||||||
|
|
||||||
|
export type Endpoint =
|
||||||
|
| endpoints.v0
|
||||||
|
| endpoints.v1
|
||||||
|
| ReturnType<typeof getHotelEndpoint>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import type {
|
|||||||
} from "@/types/fetch"
|
} from "@/types/fetch"
|
||||||
import type { Endpoint } from "./endpoints"
|
import type { Endpoint } from "./endpoints"
|
||||||
|
|
||||||
export { endpoints } from "./endpoints"
|
export { endpoints, getHotelEndpoint } from "./endpoints"
|
||||||
|
|
||||||
const defaultOptions: RequestInit = {
|
const defaultOptions: RequestInit = {
|
||||||
cache: "no-store",
|
cache: "no-store",
|
||||||
|
|||||||
@@ -2,29 +2,31 @@ import { z } from "zod"
|
|||||||
|
|
||||||
import { fromUppercaseToLangEnum } from "@/utils/languages"
|
import { fromUppercaseToLangEnum } from "@/utils/languages"
|
||||||
|
|
||||||
const RatingsSchema = z.object({
|
const RatingsSchema = z
|
||||||
tripAdvisor: z.object({
|
.object({
|
||||||
numberOfReviews: z.number(),
|
tripAdvisor: z.object({
|
||||||
rating: z.number(),
|
numberOfReviews: z.number(),
|
||||||
ratingImageUrl: z.string(),
|
rating: z.number(),
|
||||||
webUrl: z.string(),
|
ratingImageUrl: z.string(),
|
||||||
awards: z.array(
|
webUrl: z.string(),
|
||||||
z.object({
|
awards: z.array(
|
||||||
displayName: z.string(),
|
z.object({
|
||||||
images: z.object({
|
displayName: z.string(),
|
||||||
small: z.string(),
|
images: z.object({
|
||||||
medium: z.string(),
|
small: z.string(),
|
||||||
large: z.string(),
|
medium: z.string(),
|
||||||
}),
|
large: z.string(),
|
||||||
})
|
}),
|
||||||
),
|
})
|
||||||
reviews: z.object({
|
),
|
||||||
widgetHtmlTagId: z.string(),
|
reviews: z.object({
|
||||||
widgetScriptEmbedUrlIframe: z.string(),
|
widgetHtmlTagId: z.string(),
|
||||||
widgetScriptEmbedUrlJavaScript: z.string(),
|
widgetScriptEmbedUrlIframe: z.string(),
|
||||||
|
widgetScriptEmbedUrlJavaScript: z.string(),
|
||||||
|
}),
|
||||||
}),
|
}),
|
||||||
}),
|
})
|
||||||
})
|
.optional()
|
||||||
|
|
||||||
const AddressSchema = z.object({
|
const AddressSchema = z.object({
|
||||||
streetAddress: z.string(),
|
streetAddress: z.string(),
|
||||||
@@ -88,7 +90,7 @@ const InteriorSchema = z.object({
|
|||||||
numberOfFloors: z.number(),
|
numberOfFloors: z.number(),
|
||||||
numberOfRooms: z.object({
|
numberOfRooms: z.object({
|
||||||
connected: z.number(),
|
connected: z.number(),
|
||||||
forEllergics: z.number(),
|
forEllergics: z.number().optional(),
|
||||||
forDisabled: z.number(),
|
forDisabled: z.number(),
|
||||||
nonSmoking: z.number(),
|
nonSmoking: z.number(),
|
||||||
pet: z.number(),
|
pet: z.number(),
|
||||||
@@ -151,7 +153,7 @@ const DetailedFacilitySchema = z.object({
|
|||||||
code: z.string().optional(),
|
code: z.string().optional(),
|
||||||
applyToAllHotels: z.boolean(),
|
applyToAllHotels: z.boolean(),
|
||||||
public: z.boolean(),
|
public: z.boolean(),
|
||||||
icon: z.number(),
|
icon: z.string(), //Check output.
|
||||||
iconName: z.string().optional(),
|
iconName: z.string().optional(),
|
||||||
sortOrder: z.number(),
|
sortOrder: z.number(),
|
||||||
})
|
})
|
||||||
@@ -302,7 +304,7 @@ const SocialMediaSchema = z.object({
|
|||||||
|
|
||||||
const MetaSpecialAlertSchema = z.object({
|
const MetaSpecialAlertSchema = z.object({
|
||||||
type: z.string(),
|
type: z.string(),
|
||||||
description: z.string(),
|
description: z.string().optional(),
|
||||||
displayInBookingFlow: z.boolean(),
|
displayInBookingFlow: z.boolean(),
|
||||||
startDate: z.string(),
|
startDate: z.string(),
|
||||||
endDate: z.string(),
|
endDate: z.string(),
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
import * as api from "@/lib/api"
|
import * as api from "@/lib/api"
|
||||||
|
import { getHotelEndpoint } from "@/lib/api/endpoints"
|
||||||
import { badRequestError } from "@/server/errors/trpc"
|
import { badRequestError } from "@/server/errors/trpc"
|
||||||
import { publicProcedure, router } from "@/server/trpc"
|
import {
|
||||||
|
anonymousOrAuthProcedure,
|
||||||
|
publicProcedure,
|
||||||
|
router,
|
||||||
|
} from "@/server/trpc"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getFiltersInputSchema,
|
getFiltersInputSchema,
|
||||||
@@ -14,65 +19,61 @@ import {
|
|||||||
RoomSchema,
|
RoomSchema,
|
||||||
} from "./output"
|
} from "./output"
|
||||||
import tempFilterData from "./tempFilterData.json"
|
import tempFilterData from "./tempFilterData.json"
|
||||||
import tempHotelData from "./tempHotelData.json"
|
// import tempHotelData from "./tempHotelData.json"
|
||||||
import tempRatesData from "./tempRatesData.json"
|
import tempRatesData from "./tempRatesData.json"
|
||||||
import { toApiLang } from "./utils"
|
import { toApiLang } from "./utils"
|
||||||
|
|
||||||
export const hotelQueryRouter = router({
|
export const hotelQueryRouter = router({
|
||||||
getHotel: publicProcedure
|
getHotel: anonymousOrAuthProcedure
|
||||||
.input(getHotelInputSchema)
|
.input(getHotelInputSchema)
|
||||||
.query(async ({ input, ctx }) => {
|
.query(async ({ input, ctx }) => {
|
||||||
const { hotelId, language, include } = input
|
const { hotelId, language, include } = input
|
||||||
|
|
||||||
const params = new URLSearchParams()
|
const params = new URLSearchParams()
|
||||||
const apiLang = toApiLang(language)
|
const apiLang = toApiLang(language)
|
||||||
params.set("hotelId", hotelId.toString())
|
|
||||||
params.set("language", apiLang)
|
params.set("language", apiLang)
|
||||||
if (include) {
|
if (include) {
|
||||||
params.set("include", include.join(","))
|
params.set("include", include.join(","))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Enable once we have authorized API access.
|
const authToken = await ctx.getToken()
|
||||||
// const apiResponse = await api.get(
|
const apiResponse = await api.get(
|
||||||
// api.endpoints.v1.hotel,
|
getHotelEndpoint(hotelId),
|
||||||
// {}, // Include token.
|
{
|
||||||
// params
|
cache: "no-store",
|
||||||
// )
|
headers: {
|
||||||
//
|
Authorization: `Bearer ${authToken}`,
|
||||||
// if (!apiResponse.ok) {
|
},
|
||||||
// console.info(`API Response Failed - Getting Hotel`)
|
},
|
||||||
// console.error(apiResponse)
|
params
|
||||||
// return null
|
|
||||||
// }
|
|
||||||
// const apiJson = await apiResponse.json()
|
|
||||||
|
|
||||||
// NOTE: We can pass an "include" param to the hotel API to retrieve
|
|
||||||
// additional data for an individual hotel.
|
|
||||||
// Example "included" data can be found in our tempHotelData file.
|
|
||||||
const { included, ...apiJsonWithoutIncluded } = tempHotelData
|
|
||||||
const validatedHotelData = getHotelDataSchema.safeParse(
|
|
||||||
apiJsonWithoutIncluded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (!apiResponse.ok) {
|
||||||
|
console.info(`API Response Failed - Getting Hotel`)
|
||||||
|
console.error(apiResponse)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
const apiJson = await apiResponse.json()
|
||||||
|
const validatedHotelData = getHotelDataSchema.safeParse(apiJson)
|
||||||
if (!validatedHotelData.success) {
|
if (!validatedHotelData.success) {
|
||||||
console.error(`Get Individual Hotel Data - Verified Data Error`)
|
console.error(`Get Individual Hotel Data - Verified Data Error`)
|
||||||
console.error(validatedHotelData.error)
|
console.error(validatedHotelData.error)
|
||||||
throw badRequestError()
|
throw badRequestError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const included = validatedHotelData.data.included || []
|
||||||
|
|
||||||
const roomCategories = included
|
const roomCategories = included
|
||||||
? included
|
.filter((item) => item.type === "roomcategories")
|
||||||
.filter((item) => item.type === "roomcategories")
|
.map((roomCategory) => {
|
||||||
.map((roomCategory) => {
|
const validatedRoom = RoomSchema.safeParse(roomCategory)
|
||||||
const validatedRoom = RoomSchema.safeParse(roomCategory)
|
if (!validatedRoom.success) {
|
||||||
if (!validatedRoom.success) {
|
console.error(`Get Room Category Data - Verified Data Error`)
|
||||||
console.error(`Get Room Category Data - Verified Data Error`)
|
console.error(validatedRoom.error)
|
||||||
console.error(validatedRoom.error)
|
throw badRequestError()
|
||||||
throw badRequestError()
|
}
|
||||||
}
|
return validatedRoom.data
|
||||||
return validatedRoom.data
|
})
|
||||||
})
|
|
||||||
: []
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
attributes: validatedHotelData.data.data.attributes,
|
attributes: validatedHotelData.data.data.attributes,
|
||||||
|
|||||||
43
server/tokenManager.ts
Normal file
43
server/tokenManager.ts
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
import { env } from "@/env/server"
|
||||||
|
|
||||||
|
import { ServiceTokenResponse } from "@/types/tokens"
|
||||||
|
|
||||||
|
const SERVICE_TOKEN_REVALIDATE_SECONDS = 3599 // 59 minutes and 59 seconds.
|
||||||
|
|
||||||
|
async function fetchServiceToken(): Promise<ServiceTokenResponse> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${env.CURITY_ISSUER_USER}/oauth/v2/token`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
Accept: "application/json",
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({
|
||||||
|
grant_type: "client_credentials",
|
||||||
|
client_id: env.CURITY_CLIENT_ID_SERVICE,
|
||||||
|
client_secret: env.CURITY_CLIENT_SECRET_SERVICE,
|
||||||
|
}),
|
||||||
|
next: {
|
||||||
|
revalidate: SERVICE_TOKEN_REVALIDATE_SECONDS,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error("Failed to obtain service token")
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.json()
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching service token:", error)
|
||||||
|
throw error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getAuthToken(userToken?: string | null): Promise<string> {
|
||||||
|
if (userToken) {
|
||||||
|
return userToken
|
||||||
|
}
|
||||||
|
|
||||||
|
const { access_token } = await fetchServiceToken()
|
||||||
|
return access_token
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
sessionExpiredError,
|
sessionExpiredError,
|
||||||
unauthorizedError,
|
unauthorizedError,
|
||||||
} from "./errors/trpc"
|
} from "./errors/trpc"
|
||||||
|
import { getAuthToken } from "./tokenManager"
|
||||||
import { transformer } from "./transformer"
|
import { transformer } from "./transformer"
|
||||||
import { langInput } from "./utils"
|
import { langInput } from "./utils"
|
||||||
|
|
||||||
@@ -99,3 +100,17 @@ export const safeProtectedProcedure = t.procedure.use(async function (opts) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
export const anonymousOrAuthProcedure = t.procedure.use(async function (opts) {
|
||||||
|
const session: Session | null = await opts.ctx.auth()
|
||||||
|
const userToken = session?.token?.access_token || null
|
||||||
|
|
||||||
|
const getToken = async () => await getAuthToken(userToken)
|
||||||
|
|
||||||
|
return opts.next({
|
||||||
|
ctx: {
|
||||||
|
session,
|
||||||
|
getToken,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,13 +1,16 @@
|
|||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import { getHotelDataSchema,RoomSchema } from "@/server/routers/hotels/output"
|
import { getHotelDataSchema, RoomSchema } from "@/server/routers/hotels/output"
|
||||||
|
|
||||||
export type HotelData = z.infer<typeof getHotelDataSchema>
|
export type HotelData = z.infer<typeof getHotelDataSchema>
|
||||||
|
|
||||||
export type Hotel = HotelData["data"]["attributes"]
|
export type Hotel = HotelData["data"]["attributes"]
|
||||||
export type HotelAddress = HotelData["data"]["attributes"]["address"]
|
export type HotelAddress = HotelData["data"]["attributes"]["address"]
|
||||||
export type HotelLocation = HotelData["data"]["attributes"]["location"]
|
export type HotelLocation = HotelData["data"]["attributes"]["location"]
|
||||||
|
|
||||||
|
type HotelRatings = HotelData["data"]["attributes"]["ratings"]
|
||||||
export type HotelTripAdvisor =
|
export type HotelTripAdvisor =
|
||||||
HotelData["data"]["attributes"]["ratings"]["tripAdvisor"]
|
| NonNullable<HotelRatings>["tripAdvisor"]
|
||||||
|
| undefined
|
||||||
|
|
||||||
export type RoomData = z.infer<typeof RoomSchema>
|
export type RoomData = z.infer<typeof RoomSchema>
|
||||||
|
|||||||
6
types/tokens.ts
Normal file
6
types/tokens.ts
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
export interface ServiceTokenResponse {
|
||||||
|
access_token: string
|
||||||
|
scope?: string
|
||||||
|
token_type: string
|
||||||
|
expires_in: number
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user