import { metrics } from "@opentelemetry/api" import { revalidateTag, unstable_cache } from "next/cache" import { env } from "@/env/server" import { generateServiceTokenTag } from "@/utils/generateTag" import { ServiceTokenResponse } from "@/types/tokens" // OpenTelemetry metrics: Service token const meter = metrics.getMeter("trpc.context.serviceToken") const fetchServiceTokenCounter = meter.createCounter( "trpc.context.serviceToken.fetch-new-token" ) const fetchTempServiceTokenCounter = meter.createCounter( "trpc.context.serviceToken.fetch-temporary" ) const fetchServiceTokenFailCounter = meter.createCounter( "trpc.context.serviceToken.fetch-fail" ) async function fetchServiceToken(scopes: string[]) { fetchServiceTokenCounter.add(1) 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, scope: scopes.join(" "), }), }) if (!response.ok) { fetchServiceTokenFailCounter.add(1, { error_type: "http_error", error: JSON.stringify({ status: response.status, statusText: response.statusText, }), }) throw new Error("Failed to obtain service token") } return response.json() } export async function getServiceToken(): Promise { try { const scopes = ["profile", "hotel", "booking"] const tag = generateServiceTokenTag(scopes) const getCachedJwt = unstable_cache( async (scopes) => { const jwt = await fetchServiceToken(scopes) const expiresAt = Date.now() + jwt.expires_in * 1000 return { expiresAt, jwt } }, [tag], { tags: [tag] } ) const cachedJwt = await getCachedJwt(scopes) if (cachedJwt.expiresAt < Date.now()) { console.log( "trpc.context.serviceToken: Service token expired, revalidating tag" ) revalidateTag(tag) console.log( "trpc.context.serviceToken: Fetching new temporary service token." ) fetchTempServiceTokenCounter.add(1) const newToken = await fetchServiceToken(scopes) return newToken } return cachedJwt.jwt } catch (error) { console.error("Error fetching service token:", error) throw error } }