fix: improve error handling
- Do not throw in onError callback for server client. This lets the originating error bubble to Next.js. - Do not log errors inside the service token procedure, server client onError callback will log all procedure errors. - Log errors for fetching service token failure.
This commit is contained in:
@@ -50,8 +50,6 @@ export function serverClient() {
|
|||||||
redirect(redirectUrl)
|
redirect(redirectUrl)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw internalServerError()
|
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ const fetchServiceTokenFailCounter = meter.createCounter(
|
|||||||
|
|
||||||
async function fetchServiceToken(scopes: string[]) {
|
async function fetchServiceToken(scopes: string[]) {
|
||||||
fetchServiceTokenCounter.add(1)
|
fetchServiceTokenCounter.add(1)
|
||||||
|
|
||||||
const response = await fetch(`${env.CURITY_ISSUER_USER}/oauth/v2/token`, {
|
const response = await fetch(`${env.CURITY_ISSUER_USER}/oauth/v2/token`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
@@ -36,52 +37,67 @@ async function fetchServiceToken(scopes: string[]) {
|
|||||||
})
|
})
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
fetchServiceTokenFailCounter.add(1, {
|
const text = await response.text()
|
||||||
error_type: "http_error",
|
const error = {
|
||||||
error: JSON.stringify({
|
status: response.status,
|
||||||
status: response.status,
|
statusText: response.statusText,
|
||||||
statusText: response.statusText,
|
text,
|
||||||
}),
|
|
||||||
})
|
|
||||||
throw new Error("Failed to obtain service token")
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.json()
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function getServiceToken(): Promise<ServiceTokenResponse> {
|
|
||||||
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
|
fetchServiceTokenFailCounter.add(1, {
|
||||||
} catch (error) {
|
error_type: "http_error",
|
||||||
console.error("Error fetching service token:", error)
|
error: JSON.stringify(error),
|
||||||
throw error
|
})
|
||||||
|
|
||||||
|
console.error(
|
||||||
|
"fetchServiceToken error",
|
||||||
|
JSON.stringify({
|
||||||
|
query: {
|
||||||
|
grant_type: "client_credentials",
|
||||||
|
client_id: env.CURITY_CLIENT_ID_SERVICE,
|
||||||
|
scope: scopes.join(" "),
|
||||||
|
},
|
||||||
|
error,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
throw new Error(
|
||||||
|
`[fetchServiceToken] Failed to obtain service token: ${JSON.stringify(error)}`
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return response.json() as Promise<ServiceTokenResponse>
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getServiceToken() {
|
||||||
|
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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ export const safeProtectedProcedure = t.procedure.use(async function (opts) {
|
|||||||
export const serviceProcedure = t.procedure.use(async (opts) => {
|
export const serviceProcedure = t.procedure.use(async (opts) => {
|
||||||
const { access_token } = await getServiceToken()
|
const { access_token } = await getServiceToken()
|
||||||
if (!access_token) {
|
if (!access_token) {
|
||||||
throw internalServerError(`Failed to obtain service token`)
|
throw internalServerError(`[serviceProcedure] No service token`)
|
||||||
}
|
}
|
||||||
return opts.next({
|
return opts.next({
|
||||||
ctx: {
|
ctx: {
|
||||||
@@ -144,7 +144,9 @@ export const serviceServerActionProcedure = serverActionProcedure.use(
|
|||||||
async (opts) => {
|
async (opts) => {
|
||||||
const { access_token } = await getServiceToken()
|
const { access_token } = await getServiceToken()
|
||||||
if (!access_token) {
|
if (!access_token) {
|
||||||
throw internalServerError("Failed to obtain service token")
|
throw internalServerError(
|
||||||
|
"[serviceServerActionProcedure]: No service token"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
return opts.next({
|
return opts.next({
|
||||||
ctx: {
|
ctx: {
|
||||||
|
|||||||
Reference in New Issue
Block a user