diff --git a/src/route/wcl/common.ts b/src/route/wcl/common.ts index 5b6554c..2b0720d 100644 --- a/src/route/wcl/common.ts +++ b/src/route/wcl/common.ts @@ -3,6 +3,7 @@ import * as crypto from "node:crypto"; import { FastifyInstance, FastifyRequest } from "fastify"; import * as cache from "../../cache.ts"; import * as Sentry from "@sentry/node"; +import { ApiError, ApiErrorType } from "../../wcl/api.ts"; export type WclProxy = { Params: P; Querystring: T }; export type ReportParams = { code: string }; @@ -95,6 +96,14 @@ export function wrapEndpoint< } } } catch (error) { + if ( + error instanceof ApiError && + error.type === ApiErrorType.NoSuchLog + ) { + return reply.code(404).send({ + message: "No log found with that code.", + }); + } console.error(error); // TODO handle error return reply.code(500).send({ diff --git a/src/route/wcl/fights.ts b/src/route/wcl/fights.ts index 82d771b..c355af8 100644 --- a/src/route/wcl/fights.ts +++ b/src/route/wcl/fights.ts @@ -9,7 +9,6 @@ import { ReportPlayer, WCLFight, WCLReport, - WithFights, } from "./v1-types"; const fightQuery = gql` diff --git a/src/wcl/api.ts b/src/wcl/api.ts index 9b32827..45f6d66 100644 --- a/src/wcl/api.ts +++ b/src/wcl/api.ts @@ -1,4 +1,4 @@ -import { request, Variables } from "graphql-request"; +import { ClientError, request, Variables } from "graphql-request"; import axios from "axios"; async function fetchToken(): Promise { @@ -31,6 +31,22 @@ async function getToken(force: boolean = false): Promise { return token; } +export enum ApiErrorType { + /** The log is private or does not exist. */ + NoSuchLog, + Unknown, +} + +export class ApiError extends Error { + public readonly type: ApiErrorType; + public readonly cause: Error; + constructor(cause: Error, type: ApiErrorType) { + super(cause.message); + this.cause = cause; + this.type = type; + } +} + export async function query( gql: string, variables: V, @@ -46,10 +62,38 @@ export async function query( try { data = await run(); } catch (error) { - // TODO: actually check status code + if (error instanceof ClientError) { + if (isPrivateLogError(error)) { + throw new ApiError(error, ApiErrorType.NoSuchLog); + } + } + + // blindly attempt to reauthenticate and try again token = await getToken(true); - data = await run(); + try { + data = await run(); + } catch (error) { + if (error instanceof ClientError) { + if (isPrivateLogError(error)) { + throw new ApiError(error, ApiErrorType.NoSuchLog); + } + + // we only use Unknown here after attempting to re-auth to make sure that the re-auth happens + throw new ApiError(error, ApiErrorType.Unknown); + } + + throw error; + } } return data; } + +function isPrivateLogError(error: ClientError): boolean { + return ( + error.response.errors?.some( + (err) => + err.message === "You do not have permission to view this report.", + ) === true + ); +}