mirror of
https://github.com/samuelthomas2774/nxapi.git
synced 2026-03-21 18:04:10 -05:00
API error classes
This commit is contained in:
parent
5bd62cd576
commit
3c6025790e
|
|
@ -5,7 +5,7 @@ export interface CoralSuccessResponse<T = unknown> {
|
|||
correlationId: string;
|
||||
}
|
||||
|
||||
export interface CoralErrorResponse {
|
||||
export interface CoralError {
|
||||
status: CoralStatus | number;
|
||||
errorMessage: string;
|
||||
correlationId: string;
|
||||
|
|
@ -53,7 +53,7 @@ export enum CoralStatus {
|
|||
// UNKNOWN = -1,
|
||||
}
|
||||
|
||||
export type CoralResponse<T = unknown> = CoralSuccessResponse<T> | CoralErrorResponse;
|
||||
export type CoralResponse<T = unknown> = CoralSuccessResponse<T> | CoralError;
|
||||
|
||||
export interface AccountLoginParameter {
|
||||
naIdToken: string;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { JwtPayload } from '../util/jwt.js';
|
|||
import { timeoutSignal } from '../util/misc.js';
|
||||
import { getAdditionalUserAgents } from '../util/useragent.js';
|
||||
import type { CoralRemoteConfig } from '../common/remote-config.js';
|
||||
import { AccountLogin, AccountLoginParameter, AccountToken, AccountTokenParameter, Announcements, CoralErrorResponse, CoralResponse, CoralStatus, CoralSuccessResponse, CurrentUser, CurrentUserPermissions, Event, FriendCodeUrl, FriendCodeUser, Friends, GetActiveEventResult, PresencePermissions, User, WebServices, WebServiceToken, WebServiceTokenParameter } from './coral-types.js';
|
||||
import { AccountLogin, AccountLoginParameter, AccountToken, AccountTokenParameter, Announcements, CoralError, CoralResponse, CoralStatus, CoralSuccessResponse, CurrentUser, CurrentUserPermissions, Event, FriendCodeUrl, FriendCodeUser, Friends, GetActiveEventResult, PresencePermissions, User, WebServices, WebServiceToken, WebServiceTokenParameter } from './coral-types.js';
|
||||
import { f, FResult, HashMethod } from './f.js';
|
||||
import { generateAuthData, getNintendoAccountToken, getNintendoAccountUser, NintendoAccountSessionAuthorisation, NintendoAccountToken, NintendoAccountUser } from './na.js';
|
||||
import { ErrorResponse, ResponseSymbol } from './util.js';
|
||||
|
|
@ -74,7 +74,7 @@ export default class CoralApi implements CoralApiInterface {
|
|||
[CoralUserIdSymbol]: string;
|
||||
[NintendoAccountIdSymbol]: string;
|
||||
|
||||
onTokenExpired: ((data?: CoralErrorResponse, res?: Response) => Promise<CoralAuthData | void>) | null = null;
|
||||
onTokenExpired: ((data?: CoralError, res?: Response) => Promise<CoralAuthData | void>) | null = null;
|
||||
/** @internal */
|
||||
_renewToken: Promise<void> | null = null;
|
||||
/** @internal */
|
||||
|
|
@ -144,7 +144,7 @@ export default class CoralApi implements CoralApiInterface {
|
|||
debug('fetch %s %s, response %s', method, url, response.status);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse('[znc] Non-200 status code', response, await response.text());
|
||||
throw new CoralErrorResponse('[znc] Non-200 status code', response, await response.text());
|
||||
}
|
||||
|
||||
const data = await response.json() as CoralResponse<T>;
|
||||
|
|
@ -161,10 +161,10 @@ export default class CoralApi implements CoralApiInterface {
|
|||
}
|
||||
|
||||
if ('errorMessage' in data) {
|
||||
throw new ErrorResponse('[znc] ' + data.errorMessage, response, data);
|
||||
throw new CoralErrorResponse('[znc] ' + data.errorMessage, response, data);
|
||||
}
|
||||
if (data.status !== CoralStatus.OK) {
|
||||
throw new ErrorResponse('[znc] Unknown error', response, data);
|
||||
throw new CoralErrorResponse('[znc] Unknown error', response, data);
|
||||
}
|
||||
|
||||
const result = data.result;
|
||||
|
|
@ -295,7 +295,7 @@ export default class CoralApi implements CoralApiInterface {
|
|||
try {
|
||||
return await this.call<WebServiceToken>('/v2/Game/GetWebServiceToken', req, false);
|
||||
} catch (err) {
|
||||
if (err instanceof ErrorResponse && err.data.status === CoralStatus.TOKEN_EXPIRED && !_attempt && this.onTokenExpired) {
|
||||
if (err instanceof CoralErrorResponse && err.status === CoralStatus.TOKEN_EXPIRED && !_attempt && this.onTokenExpired) {
|
||||
debug('Error getting web service token, renewing token before retrying', err);
|
||||
// _renewToken will be awaited when calling getWebServiceToken
|
||||
this._renewToken = this._renewToken ?? this.onTokenExpired.call(null, err.data, err.response as Response).then(data => {
|
||||
|
|
@ -448,16 +448,16 @@ export default class CoralApi implements CoralApiInterface {
|
|||
debug('fetch %s %s, response %s', 'POST', '/v3/Account/Login', response.status);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse('[znc] Non-200 status code', response, await response.text());
|
||||
throw new CoralErrorResponse('[znc] Non-200 status code', response, await response.text());
|
||||
}
|
||||
|
||||
const data = await response.json() as CoralResponse<AccountLogin>;
|
||||
|
||||
if ('errorMessage' in data) {
|
||||
throw new ErrorResponse('[znc] ' + data.errorMessage, response, data);
|
||||
throw new CoralErrorResponse('[znc] ' + data.errorMessage, response, data);
|
||||
}
|
||||
if (data.status !== CoralStatus.OK) {
|
||||
throw new ErrorResponse('[znc] Unknown error', response, data);
|
||||
throw new CoralErrorResponse('[znc] Unknown error', response, data);
|
||||
}
|
||||
|
||||
debug('Got Nintendo Switch Online app token', data);
|
||||
|
|
@ -474,6 +474,12 @@ export default class CoralApi implements CoralApiInterface {
|
|||
}
|
||||
}
|
||||
|
||||
export class CoralErrorResponse extends ErrorResponse<CoralError> {
|
||||
get status(): CoralStatus | null {
|
||||
return this.data?.status ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
const na_client_settings = {
|
||||
client_id: ZNCA_CLIENT_ID,
|
||||
scope: 'openid user user.birthday user.mii user.screenName',
|
||||
|
|
|
|||
|
|
@ -86,13 +86,13 @@ export default class MoonApi {
|
|||
}
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse('[moon] Non-200 status code', response, await response.text());
|
||||
throw new MoonErrorResponse('[moon] Non-200 status code', response, await response.text());
|
||||
}
|
||||
|
||||
const data = await response.json() as T | MoonError;
|
||||
|
||||
if ('errorCode' in data) {
|
||||
throw new ErrorResponse('[moon] ' + data.title, response, data);
|
||||
throw new MoonErrorResponse('[moon] ' + data.title, response, data);
|
||||
}
|
||||
|
||||
return defineResponse(data, response);
|
||||
|
|
@ -177,6 +177,8 @@ export default class MoonApi {
|
|||
}
|
||||
}
|
||||
|
||||
export class MoonErrorResponse extends ErrorResponse<MoonError> {}
|
||||
|
||||
const na_client_settings = {
|
||||
client_id: ZNMA_CLIENT_ID,
|
||||
scope: [
|
||||
|
|
|
|||
|
|
@ -126,16 +126,16 @@ export async function getNintendoAccountSessionToken(code: string, verifier: str
|
|||
}).finally(cancel);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse('[na] Non-200 status code', response, await response.text());
|
||||
throw new NintendoAccountAuthErrorResponse('[na] Non-200 status code', response, await response.text());
|
||||
}
|
||||
|
||||
const token = await response.json() as NintendoAccountSessionToken | NintendoAccountAuthError | NintendoAccountError;
|
||||
|
||||
if ('errorCode' in token) {
|
||||
throw new ErrorResponse<NintendoAccountError>('[na] ' + token.detail, response, token);
|
||||
}
|
||||
if ('error' in token) {
|
||||
throw new ErrorResponse<NintendoAccountAuthError>('[na] ' + token.error_description ?? token.error, response, token);
|
||||
throw new NintendoAccountAuthErrorResponse('[na] ' + token.error_description ?? token.error, response, token);
|
||||
}
|
||||
if ('errorCode' in token) {
|
||||
throw new NintendoAccountErrorResponse('[na] ' + token.detail, response, token);
|
||||
}
|
||||
|
||||
debug('Got Nintendo Account session token', token);
|
||||
|
|
@ -163,16 +163,17 @@ export async function getNintendoAccountToken(token: string, client_id: string)
|
|||
}).finally(cancel);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse('[na] Non-200 status code', response, await response.text());
|
||||
throw new NintendoAccountAuthErrorResponse('[na] Non-200 status code', response, await response.text());
|
||||
}
|
||||
|
||||
const nintendoAccountToken = await response.json() as NintendoAccountToken | NintendoAccountAuthError | NintendoAccountError;
|
||||
|
||||
if ('errorCode' in nintendoAccountToken) {
|
||||
throw new ErrorResponse<NintendoAccountError>('[na] ' + nintendoAccountToken.detail, response, nintendoAccountToken);
|
||||
}
|
||||
if ('error' in nintendoAccountToken) {
|
||||
throw new ErrorResponse<NintendoAccountAuthError>('[na] ' + nintendoAccountToken.error_description ?? nintendoAccountToken.error, response, nintendoAccountToken);
|
||||
throw new NintendoAccountAuthErrorResponse('[na] ' + nintendoAccountToken.error_description ??
|
||||
nintendoAccountToken.error, response, nintendoAccountToken);
|
||||
}
|
||||
if ('errorCode' in nintendoAccountToken) {
|
||||
throw new NintendoAccountErrorResponse('[na] ' + nintendoAccountToken.detail, response, nintendoAccountToken);
|
||||
}
|
||||
|
||||
debug('Got Nintendo Account token', nintendoAccountToken);
|
||||
|
|
@ -196,13 +197,13 @@ export async function getNintendoAccountUser(token: NintendoAccountToken) {
|
|||
}).finally(cancel);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse('[na] Non-200 status code', response, await response.text());
|
||||
throw new NintendoAccountErrorResponse('[na] Non-200 status code', response, await response.text());
|
||||
}
|
||||
|
||||
const user = await response.json() as NintendoAccountUser | NintendoAccountError;
|
||||
|
||||
if ('errorCode' in user) {
|
||||
throw new ErrorResponse<NintendoAccountError>('[na] ' + user.detail, response, user);
|
||||
throw new NintendoAccountErrorResponse('[na] ' + user.detail, response, user);
|
||||
}
|
||||
|
||||
debug('Got Nintendo Account user info', user);
|
||||
|
|
@ -406,3 +407,7 @@ export interface NintendoAccountError {
|
|||
status: number;
|
||||
type: string;
|
||||
}
|
||||
|
||||
export class NintendoAccountAuthErrorResponse extends ErrorResponse<NintendoAccountAuthError> {}
|
||||
|
||||
export class NintendoAccountErrorResponse extends ErrorResponse<NintendoAccountError> {}
|
||||
|
|
|
|||
|
|
@ -80,14 +80,14 @@ export default class NooklinkApi {
|
|||
return this.fetch(url, method, body, headers, _autoRenewToken, _attempt + 1);
|
||||
}
|
||||
|
||||
if (response.status !== 200 && response.status !== 201) {
|
||||
throw new ErrorResponse('[nooklink] Non-200/201 status code', response, await response.text());
|
||||
if (!response.ok) {
|
||||
throw new NooklinkErrorResponse('[nooklink] Non-2xx status code', response, await response.text());
|
||||
}
|
||||
|
||||
const data = await response.json() as T | WebServiceError;
|
||||
|
||||
if ('code' in data) {
|
||||
throw new ErrorResponse<WebServiceError>('[nooklink] Error ' + data.code, response, data);
|
||||
throw new NooklinkErrorResponse('[nooklink] Error ' + data.code, response, data);
|
||||
}
|
||||
|
||||
return defineResponse(data, response);
|
||||
|
|
@ -175,14 +175,14 @@ export default class NooklinkApi {
|
|||
const body = await response.text();
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse('[nooklink] Non-200 status code', response, body);
|
||||
throw new NooklinkErrorResponse('[nooklink] Non-200 status code', response, body);
|
||||
}
|
||||
|
||||
const cookies = response.headers.get('Set-Cookie');
|
||||
const match = cookies?.match(/\b_gtoken=([^;]*)(;(\s*((?!expires)[a-z]+=([^;]*));?)*(\s*(expires=([^;]*));?)?|$)/i);
|
||||
|
||||
if (!match) {
|
||||
throw new ErrorResponse('[nooklink] Response didn\'t include _gtoken cookie', response, body);
|
||||
throw new NooklinkErrorResponse('[nooklink] Response didn\'t include _gtoken cookie', response, body);
|
||||
}
|
||||
|
||||
const gtoken = decodeURIComponent(match[1]);
|
||||
|
|
@ -275,14 +275,14 @@ export class NooklinkUserApi {
|
|||
return this.fetch(url, method, body, headers, _autoRenewToken, _attempt + 1);
|
||||
}
|
||||
|
||||
if (response.status !== 200 && response.status !== 201) {
|
||||
throw new ErrorResponse('[nooklink] Non-200/201 status code', response, await response.text());
|
||||
if (!response.ok) {
|
||||
throw new NooklinkErrorResponse('[nooklink] Non-2xx status code', response, await response.text());
|
||||
}
|
||||
|
||||
const data = await response.json() as T | WebServiceError;
|
||||
|
||||
if ('code' in data) {
|
||||
throw new ErrorResponse<WebServiceError>('[nooklink] Error ' + data.code, response, data);
|
||||
throw new NooklinkErrorResponse('[nooklink] Error ' + data.code, response, data);
|
||||
}
|
||||
|
||||
return defineResponse(data, response);
|
||||
|
|
@ -391,6 +391,8 @@ export class NooklinkUserApi {
|
|||
}
|
||||
}
|
||||
|
||||
export class NooklinkErrorResponse extends ErrorResponse<WebServiceError> {}
|
||||
|
||||
export interface NooklinkAuthData {
|
||||
webserviceToken: WebServiceToken;
|
||||
url: string;
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ export default class SplatNet2Api {
|
|||
}
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse('[splatnet2] Non-200 status code', response, await response.text());
|
||||
throw new SplatNet2ErrorResponse('[splatnet2] Non-200 status code', response, await response.text());
|
||||
}
|
||||
|
||||
updateIksmSessionLastUsed.handler?.call(null, this.iksm_session);
|
||||
|
|
@ -71,7 +71,7 @@ export default class SplatNet2Api {
|
|||
const data = await response.json() as T | WebServiceError;
|
||||
|
||||
if ('code' in data) {
|
||||
throw new ErrorResponse<WebServiceError>('[splatnet2] ' + data.message, response, data);
|
||||
throw new SplatNet2ErrorResponse('[splatnet2] ' + data.message, response, data);
|
||||
}
|
||||
|
||||
return defineResponse(data, response);
|
||||
|
|
@ -305,14 +305,14 @@ ${colour}
|
|||
const body = await response.text();
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse('[splatnet2] Non-200 status code', response, body);
|
||||
throw new SplatNet2ErrorResponse('[splatnet2] Non-200 status code', response, body);
|
||||
}
|
||||
|
||||
const cookies = response.headers.get('Set-Cookie');
|
||||
const match = cookies?.match(/\biksm_session=([^;]*)(;(\s*((?!expires)[a-z]+=([^;]*));?)*(\s*(expires=([^;]*));?)?|$)/i);
|
||||
|
||||
if (!match) {
|
||||
throw new ErrorResponse('[splatnet2] Response didn\'t include iksm_session cookie', response, body);
|
||||
throw new SplatNet2ErrorResponse('[splatnet2] Response didn\'t include iksm_session cookie', response, body);
|
||||
}
|
||||
|
||||
const iksm_session = decodeURIComponent(match[1]);
|
||||
|
|
@ -330,10 +330,10 @@ ${colour}
|
|||
const mn = body.match(/<html(?:\s+[a-z0-9-]+(?:=(?:"[^"]*"|[^\s>]*))?)*\s+data-nsa-id=(?:"([^"]*)"|([^\s>]*))/i);
|
||||
const [language, region, user_id, nsa_id] = [ml, mr, mu, mn].map(m => m?.[1] || m?.[2] || null);
|
||||
|
||||
if (!language) throw new Error('[splatnet2] Invalid language in response');
|
||||
if (!region) throw new Error('[splatnet2] Invalid region in response');
|
||||
if (!user_id) throw new Error('[splatnet2] Invalid unique player ID in response');
|
||||
if (!nsa_id) throw new Error('[splatnet2] Invalid NSA ID in response');
|
||||
if (!language) throw new ErrorResponse('[splatnet2] Invalid language in response', response, body);
|
||||
if (!region) throw new ErrorResponse('[splatnet2] Invalid region in response', response, body);
|
||||
if (!user_id) throw new ErrorResponse('[splatnet2] Invalid unique player ID in response', response, body);
|
||||
if (!nsa_id) throw new ErrorResponse('[splatnet2] Invalid NSA ID in response', response, body);
|
||||
|
||||
debug('SplatNet 2 user', {
|
||||
language,
|
||||
|
|
@ -359,6 +359,8 @@ ${colour}
|
|||
}
|
||||
}
|
||||
|
||||
export class SplatNet2ErrorResponse extends ErrorResponse<WebServiceError> {}
|
||||
|
||||
export interface SplatNet2AuthData {
|
||||
webserviceToken: WebServiceToken;
|
||||
url: string;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import fetch, { Response } from 'node-fetch';
|
||||
import { BankaraBattleHistoriesRefetchResult, BankaraBattleHistoriesRefetchVariables, GraphQLRequest, GraphQLResponse, GraphQLSuccessResponse, KnownRequestId, LatestBattleHistoriesRefetchResult, LatestBattleHistoriesRefetchVariables, MyOutfitInput, PagerUpdateBattleHistoriesByVsModeResult, PagerUpdateBattleHistoriesByVsModeVariables, PrivateBattleHistoriesRefetchResult, PrivateBattleHistoriesRefetchVariables, RegularBattleHistoriesRefetchResult, RegularBattleHistoriesRefetchVariables, RequestId, ResultTypes, VariablesTypes, XBattleHistoriesRefetchResult, XBattleHistoriesRefetchVariables } from 'splatnet3-types/splatnet3';
|
||||
import { BankaraBattleHistoriesRefetchResult, BankaraBattleHistoriesRefetchVariables, GraphQLError, GraphQLErrorResponse, GraphQLRequest, GraphQLResponse, GraphQLSuccessResponse, KnownRequestId, LatestBattleHistoriesRefetchResult, LatestBattleHistoriesRefetchVariables, MyOutfitInput, PagerUpdateBattleHistoriesByVsModeResult, PagerUpdateBattleHistoriesByVsModeVariables, PrivateBattleHistoriesRefetchResult, PrivateBattleHistoriesRefetchVariables, RegularBattleHistoriesRefetchResult, RegularBattleHistoriesRefetchVariables, RequestId, ResultTypes, VariablesTypes, XBattleHistoriesRefetchResult, XBattleHistoriesRefetchVariables } from 'splatnet3-types/splatnet3';
|
||||
import { WebServiceToken } from './coral-types.js';
|
||||
import { CoralApiInterface } from './coral.js';
|
||||
import { NintendoAccountUser } from './na.js';
|
||||
|
|
@ -27,15 +27,25 @@ const SPLATNET3_URL = SPLATNET3_WEBSERVICE_URL + '/api';
|
|||
const SHOULD_RENEW_TOKEN_AT = 300; // 5 minutes in seconds
|
||||
const TOKEN_EXPIRES_IN = 2 * 60 * 60 * 1000; // 2 hours in milliseconds
|
||||
|
||||
export enum SplatNet3AuthErrorCode {
|
||||
USER_NOT_REGISTERED = 'USER_NOT_REGISTERED',
|
||||
ERROR_INVALID_PARAMETERS = 'ERROR_INVALID_PARAMETERS',
|
||||
ERROR_INVALID_GAME_WEB_TOKEN = 'ERROR_INVALID_GAME_WEB_TOKEN',
|
||||
ERROR_OBSOLETE_VERSION = 'ERROR_OBSOLETE_VERSION',
|
||||
ERROR_RATE_LIMIT = 'ERROR_RATE_LIMIT',
|
||||
ERROR_SERVER = 'ERROR_SERVER',
|
||||
ERROR_SERVER_MAINTENANCE = 'ERROR_SERVER_MAINTENANCE',
|
||||
}
|
||||
|
||||
const AUTH_ERROR_CODES = {
|
||||
204: 'USER_NOT_REGISTERED',
|
||||
400: 'ERROR_INVALID_PARAMETERS',
|
||||
401: 'ERROR_INVALID_GAME_WEB_TOKEN',
|
||||
403: 'ERROR_OBSOLETE_VERSION',
|
||||
429: 'ERROR_RATE_LIMIT',
|
||||
500: 'ERROR_SERVER',
|
||||
503: 'ERROR_SERVER_MAINTENANCE',
|
||||
599: 'ERROR_SERVER',
|
||||
204: SplatNet3AuthErrorCode.USER_NOT_REGISTERED,
|
||||
400: SplatNet3AuthErrorCode.ERROR_INVALID_PARAMETERS,
|
||||
401: SplatNet3AuthErrorCode.ERROR_INVALID_GAME_WEB_TOKEN,
|
||||
403: SplatNet3AuthErrorCode.ERROR_OBSOLETE_VERSION,
|
||||
429: SplatNet3AuthErrorCode.ERROR_RATE_LIMIT,
|
||||
500: SplatNet3AuthErrorCode.ERROR_SERVER,
|
||||
503: SplatNet3AuthErrorCode.ERROR_SERVER_MAINTENANCE,
|
||||
599: SplatNet3AuthErrorCode.ERROR_SERVER,
|
||||
} as const;
|
||||
|
||||
const REPLAY_CODE_REGEX = /^[A-Z0-9]{16}$/;
|
||||
|
|
@ -143,7 +153,7 @@ export default class SplatNet3Api {
|
|||
}
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse('[splatnet3] Non-200 status code', response, await response.text());
|
||||
throw new SplatNet3ErrorResponse('[splatnet3] Non-200 status code', response, await response.text());
|
||||
}
|
||||
|
||||
const remaining = parseInt(response.headers.get('x-bullettoken-remaining') ?? '0');
|
||||
|
|
@ -189,9 +199,9 @@ export default class SplatNet3Api {
|
|||
const data = await this.fetch<GraphQLResponse<_Result>>('/graphql', 'POST', JSON.stringify(req), undefined,
|
||||
'graphql query ' + id);
|
||||
|
||||
if (!('data' in data) || (this.graphql_strict && data.errors?.length)) {
|
||||
throw new ErrorResponse('[splatnet3] GraphQL error: ' + data.errors!.map(e => e.message).join(', '),
|
||||
data[ResponseSymbol], data);
|
||||
if (data.errors && (!('data' in data) || this.graphql_strict)) {
|
||||
throw SplatNet3GraphQLErrorResponse.from(data[ResponseSymbol], data as GraphQLResponseWithErrors,
|
||||
id, variables);
|
||||
}
|
||||
|
||||
for (const error of data.errors ?? []) {
|
||||
|
|
@ -377,7 +387,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.journey) {
|
||||
throw new ErrorResponse('[splatnet3] Journey not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Journey not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'journey'>;
|
||||
|
|
@ -390,7 +400,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.journey) {
|
||||
throw new ErrorResponse('[splatnet3] Journey not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Journey not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'journey'>;
|
||||
|
|
@ -403,7 +413,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.journey) {
|
||||
throw new ErrorResponse('[splatnet3] Journey not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Journey not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'journey'>;
|
||||
|
|
@ -416,7 +426,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.journey) {
|
||||
throw new ErrorResponse('[splatnet3] Journey not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Journey not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'journey'>;
|
||||
|
|
@ -450,7 +460,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.fest) {
|
||||
throw new ErrorResponse('[splatnet3] Fest not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Fest not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'fest'>;
|
||||
|
|
@ -463,7 +473,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.fest) {
|
||||
throw new ErrorResponse('[splatnet3] Fest not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Fest not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'fest'>;
|
||||
|
|
@ -476,7 +486,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.fest) {
|
||||
throw new ErrorResponse('[splatnet3] Fest not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Fest not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'fest'>;
|
||||
|
|
@ -489,7 +499,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.fest) {
|
||||
throw new ErrorResponse('[splatnet3] Fest not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Fest not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'fest'>;
|
||||
|
|
@ -509,7 +519,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.fest) {
|
||||
throw new ErrorResponse('[splatnet3] Fest not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Fest not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'fest'>;
|
||||
|
|
@ -528,7 +538,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.node) {
|
||||
throw new ErrorResponse('[splatnet3] FestTeam not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] FestTeam not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'node'>;
|
||||
|
|
@ -589,7 +599,7 @@ export default class SplatNet3Api {
|
|||
null :
|
||||
null;
|
||||
|
||||
if (!query) throw new Error('Invalid leaderboard');
|
||||
if (!query) throw new TypeError('Invalid leaderboard');
|
||||
|
||||
return this.persistedQuery<{
|
||||
[XRankingLeaderboardType.X_RANKING]: {
|
||||
|
|
@ -633,7 +643,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.saleGear) {
|
||||
throw new ErrorResponse('[splatnet3] Sale gear not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Sale gear not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'saleGear'>;
|
||||
|
|
@ -670,7 +680,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.myOutfit) {
|
||||
throw new ErrorResponse('[splatnet3] My outfit not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] My outfit not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'myOutfit'>;
|
||||
|
|
@ -741,7 +751,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.replay) {
|
||||
throw new ErrorResponse('[splatnet3] Replay not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Replay not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'replay'>;
|
||||
|
|
@ -842,7 +852,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.vsHistoryDetail) {
|
||||
throw new ErrorResponse('[splatnet3] Battle history not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Battle history not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'vsHistoryDetail'>;
|
||||
|
|
@ -855,7 +865,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.vsHistoryDetail) {
|
||||
throw new ErrorResponse('[splatnet3] Battle history not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Battle history not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'vsHistoryDetail'>;
|
||||
|
|
@ -901,7 +911,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.coopHistoryDetail) {
|
||||
throw new ErrorResponse('[splatnet3] Co-op history not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Co-op history not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'coopHistoryDetail'>;
|
||||
|
|
@ -914,7 +924,7 @@ export default class SplatNet3Api {
|
|||
});
|
||||
|
||||
if (!result.data.node) {
|
||||
throw new ErrorResponse('[splatnet3] Co-op history not found', result[ResponseSymbol], result);
|
||||
throw SplatNet3GraphQLResourceNotFoundResponse.from('[splatnet3] Co-op history not found', result);
|
||||
}
|
||||
|
||||
return result as NotNullPersistedQueryResult<typeof result, 'node'>;
|
||||
|
|
@ -1022,7 +1032,7 @@ export default class SplatNet3Api {
|
|||
const body = await response.text();
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse('[splatnet3] Non-200 status code', response, body);
|
||||
throw new SplatNet3ErrorResponse('[splatnet3] Non-200 status code', response, body);
|
||||
}
|
||||
|
||||
const cookies = response.headers.get('Set-Cookie');
|
||||
|
|
@ -1047,9 +1057,9 @@ export default class SplatNet3Api {
|
|||
|
||||
debug('fetch %s %s, response %s', 'POST', '/bullet_tokens', response.status);
|
||||
|
||||
const error: string | undefined = AUTH_ERROR_CODES[tr.status as keyof typeof AUTH_ERROR_CODES];
|
||||
if (error) throw new ErrorResponse('[splatnet3] ' + error, tr, await tr.text());
|
||||
if (tr.status !== 201) throw new ErrorResponse('[splatnet3] Non-201 status code', tr, await tr.text());
|
||||
const error: SplatNet3AuthErrorCode | undefined = AUTH_ERROR_CODES[tr.status as keyof typeof AUTH_ERROR_CODES];
|
||||
if (error) throw new SplatNet3AuthErrorResponse('[splatnet3] ' + error, tr, await tr.text(), error);
|
||||
if (tr.status !== 201) throw new SplatNet3ErrorResponse('[splatnet3] Non-201 status code', tr, await tr.text());
|
||||
|
||||
const bullet_token = await tr.json() as BulletToken;
|
||||
const created_at = Date.now();
|
||||
|
|
@ -1083,6 +1093,65 @@ function getMapPersistedQueriesModeFromEnvironment(): MapQueriesMode {
|
|||
return MapQueriesMode.ALL;
|
||||
}
|
||||
|
||||
export class SplatNet3ErrorResponse<T = unknown> extends ErrorResponse<T> {}
|
||||
|
||||
export class SplatNet3AuthErrorResponse extends SplatNet3ErrorResponse {
|
||||
constructor(
|
||||
message: string, response: Response | globalThis.Response,
|
||||
body?: string | unknown | undefined,
|
||||
readonly code = SplatNet3AuthErrorCode.ERROR_SERVER,
|
||||
) {
|
||||
super(message, response, body);
|
||||
}
|
||||
}
|
||||
|
||||
type GraphQLResponseWithErrors = (GraphQLSuccessResponse & {errors: GraphQLError[]}) | GraphQLErrorResponse;
|
||||
|
||||
export class SplatNet3GraphQLErrorResponse<
|
||||
Id extends string = string,
|
||||
/** @private */
|
||||
_Variables extends Id extends KnownRequestId ? VariablesTypes[Id] : unknown =
|
||||
Id extends KnownRequestId ? VariablesTypes[Id] : unknown,
|
||||
> extends SplatNet3ErrorResponse<GraphQLResponseWithErrors> {
|
||||
constructor(
|
||||
message: string, response: Response | globalThis.Response,
|
||||
body?: string | GraphQLResponseWithErrors | undefined,
|
||||
readonly request_id?: Id | string,
|
||||
readonly variables?: _Variables,
|
||||
) {
|
||||
super(message, response, body);
|
||||
}
|
||||
|
||||
static from(response: Response, data: GraphQLResponseWithErrors, id: string, variables: unknown) {
|
||||
return new SplatNet3GraphQLErrorResponse('[splatnet3] GraphQL error: ' +
|
||||
data.errors.map(e => e.message).join(', '), response, data, id, variables);
|
||||
}
|
||||
}
|
||||
|
||||
export class SplatNet3GraphQLResourceNotFoundResponse<
|
||||
Id extends string = string,
|
||||
/** @private */
|
||||
_Result extends Id extends KnownRequestId ? ResultTypes[Id] : unknown =
|
||||
Id extends KnownRequestId ? ResultTypes[Id] : unknown,
|
||||
/** @private */
|
||||
_Variables extends Id extends KnownRequestId ? VariablesTypes[Id] : unknown =
|
||||
Id extends KnownRequestId ? VariablesTypes[Id] : unknown,
|
||||
> extends SplatNet3ErrorResponse<PersistedQueryResult<_Result>> {
|
||||
constructor(
|
||||
message: string, response: Response | globalThis.Response,
|
||||
body?: string | PersistedQueryResult<_Result> | undefined,
|
||||
readonly request_id?: Id | string,
|
||||
readonly variables?: _Variables,
|
||||
) {
|
||||
super(message, response, body);
|
||||
}
|
||||
|
||||
static from(message: string, data: PersistedQueryResult<any>) {
|
||||
return new SplatNet3GraphQLResourceNotFoundResponse<any, any>(
|
||||
message, data[ResponseSymbol], data, data[RequestIdSymbol], data[VariablesSymbol]);
|
||||
}
|
||||
}
|
||||
|
||||
export interface SplatNet3AuthData {
|
||||
webserviceToken: WebServiceToken;
|
||||
url: string;
|
||||
|
|
|
|||
|
|
@ -33,8 +33,8 @@ export default class ZncProxyApi implements CoralApiInterface {
|
|||
|
||||
debug('fetch %s %s, response %s', method, url, response.status);
|
||||
|
||||
if (response.status !== 200 && response.status !== 204) {
|
||||
throw new ErrorResponse('[zncproxy] Non-200/204 status code', response, await response.text());
|
||||
if (!response.ok) {
|
||||
throw new ZncProxyErrorResponse('[zncproxy] Non-2xx status code', response, await response.text());
|
||||
}
|
||||
|
||||
const data = (response.status === 204 ? {} : await response.json()) as T;
|
||||
|
|
@ -182,6 +182,8 @@ function createResult<T extends {}, R>(data: R & {[ResponseSymbol]: Response}, r
|
|||
return result as Result<T>;
|
||||
}
|
||||
|
||||
export class ZncProxyErrorResponse extends ErrorResponse {}
|
||||
|
||||
export interface AuthToken {
|
||||
user: string;
|
||||
policy?: AuthPolicy;
|
||||
|
|
@ -227,12 +229,12 @@ export async function getPresenceFromUrl(presence_url: string, useragent?: strin
|
|||
debug('fetch %s %s, response %s', 'GET', presence_url, response.status);
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new ErrorResponse('[zncproxy] Unknown error', response, await response.text());
|
||||
throw new ZncProxyErrorResponse('[zncproxy] Unknown error', response, await response.text());
|
||||
}
|
||||
|
||||
if (!response.headers.get('Content-Type')?.match(/^application\/json(;|$)$/)) {
|
||||
controller.abort();
|
||||
throw new ErrorResponse('[zncproxy] Unacceptable content type', response);
|
||||
throw new ZncProxyErrorResponse('[zncproxy] Unacceptable content type', response);
|
||||
}
|
||||
|
||||
const data = await response.json() as PresenceUrlResponse;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { dialog, Notification } from './electron.js';
|
|||
import { App } from './index.js';
|
||||
import { tryGetNativeImageFromUrl } from './util.js';
|
||||
import { DiscordPresenceConfiguration, DiscordPresenceExternalMonitorsConfiguration, DiscordPresenceSource } from '../common/types.js';
|
||||
import { CurrentUser, Friend, Game, CoralErrorResponse } from '../../api/coral-types.js';
|
||||
import { CurrentUser, Friend, Game, CoralError } from '../../api/coral-types.js';
|
||||
import { ErrorResponse } from '../../api/util.js';
|
||||
import { ZncDiscordPresence, ZncProxyDiscordPresence } from '../../common/presence.js';
|
||||
import { NotificationManager } from '../../common/notify.js';
|
||||
|
|
@ -346,7 +346,7 @@ export class PresenceMonitorManager {
|
|||
|
||||
async handleError(
|
||||
monitor: EmbeddedPresenceMonitor | EmbeddedProxyPresenceMonitor,
|
||||
err: ErrorResponse<CoralErrorResponse> | NodeJS.ErrnoException
|
||||
err: ErrorResponse<CoralError> | NodeJS.ErrnoException
|
||||
): Promise<LoopResult> {
|
||||
const {response} = await dialog.showMessageBox({
|
||||
message: err.name + ' updating presence monitor',
|
||||
|
|
@ -366,7 +366,7 @@ export class PresenceMonitorManager {
|
|||
|
||||
export class EmbeddedPresenceMonitor extends ZncDiscordPresence {
|
||||
notifications = new ElectronNotificationManager();
|
||||
onError?: (error: ErrorResponse<CoralErrorResponse> | NodeJS.ErrnoException) =>
|
||||
onError?: (error: ErrorResponse<CoralError> | NodeJS.ErrnoException) =>
|
||||
Promise<LoopResult | void> | LoopResult | void = undefined;
|
||||
|
||||
enable() {
|
||||
|
|
@ -409,7 +409,7 @@ export class EmbeddedPresenceMonitor extends ZncDiscordPresence {
|
|||
}
|
||||
}
|
||||
|
||||
async handleError(err: ErrorResponse<CoralErrorResponse> | NodeJS.ErrnoException): Promise<LoopResult> {
|
||||
async handleError(err: ErrorResponse<CoralError> | NodeJS.ErrnoException): Promise<LoopResult> {
|
||||
try {
|
||||
return await super.handleError(err);
|
||||
} catch (err: any) {
|
||||
|
|
@ -425,7 +425,7 @@ export class EmbeddedPresenceMonitor extends ZncDiscordPresence {
|
|||
|
||||
export class EmbeddedProxyPresenceMonitor extends ZncProxyDiscordPresence {
|
||||
notifications = new ElectronNotificationManager();
|
||||
onError?: (error: ErrorResponse<CoralErrorResponse> | NodeJS.ErrnoException) =>
|
||||
onError?: (error: ErrorResponse<CoralError> | NodeJS.ErrnoException) =>
|
||||
Promise<LoopResult | void> | LoopResult | void = undefined;
|
||||
|
||||
enable() {
|
||||
|
|
@ -468,7 +468,7 @@ export class EmbeddedProxyPresenceMonitor extends ZncProxyDiscordPresence {
|
|||
}
|
||||
}
|
||||
|
||||
async handleError(err: ErrorResponse<CoralErrorResponse> | NodeJS.ErrnoException): Promise<LoopResult> {
|
||||
async handleError(err: ErrorResponse<CoralError> | NodeJS.ErrnoException): Promise<LoopResult> {
|
||||
try {
|
||||
return await super.handleError(err);
|
||||
} catch (err: any) {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import { protocol_registration_options } from './index.js';
|
|||
import { createModalWindow } from './windows.js';
|
||||
import { tryGetNativeImageFromUrl } from './util.js';
|
||||
import { WindowType } from '../common/types.js';
|
||||
import { NintendoAccountAuthError, NintendoAccountSessionAuthorisation, NintendoAccountSessionAuthorisationError, NintendoAccountSessionToken } from '../../api/na.js';
|
||||
import { NintendoAccountAuthError, NintendoAccountAuthErrorResponse, NintendoAccountSessionAuthorisation, NintendoAccountSessionAuthorisationError, NintendoAccountSessionToken } from '../../api/na.js';
|
||||
import { NintendoAccountSessionAuthorisationCoral } from '../../api/coral.js';
|
||||
import { NintendoAccountSessionAuthorisationMoon } from '../../api/moon.js';
|
||||
import { ErrorResponse } from '../../api/util.js';
|
||||
|
|
@ -329,13 +329,9 @@ export async function addNsoAccount(storage: persist.LocalStorage, use_in_app_br
|
|||
|
||||
return {nso, data};
|
||||
} catch (err) {
|
||||
if (err instanceof ErrorResponse && err.response.url.startsWith('https://accounts.nintendo.com/')) {
|
||||
const data: NintendoAccountAuthError = err.data;
|
||||
|
||||
if (data.error === 'invalid_grant') {
|
||||
// The session token has expired/was revoked
|
||||
return authenticateCoralSessionToken(storage, authenticator, code, true);
|
||||
}
|
||||
if (err instanceof NintendoAccountAuthErrorResponse && err.data?.error === 'invalid_grant') {
|
||||
// The session token has expired/was revoked
|
||||
return authenticateCoralSessionToken(storage, authenticator, code, true);
|
||||
}
|
||||
|
||||
throw err;
|
||||
|
|
@ -494,13 +490,9 @@ export async function addPctlAccount(storage: persist.LocalStorage, use_in_app_b
|
|||
|
||||
return {moon, data};
|
||||
} catch (err) {
|
||||
if (err instanceof ErrorResponse && err.response.url.startsWith('https://accounts.nintendo.com/')) {
|
||||
const data: NintendoAccountAuthError = err.data;
|
||||
|
||||
if (data.error === 'invalid_grant') {
|
||||
// The session token has expired/was revoked
|
||||
return authenticateMoonSessionToken(storage, authenticator, code, true);
|
||||
}
|
||||
if (err instanceof NintendoAccountAuthErrorResponse && err.data?.error === 'invalid_grant') {
|
||||
// The session token has expired/was revoked
|
||||
return authenticateMoonSessionToken(storage, authenticator, code, true);
|
||||
}
|
||||
|
||||
throw err;
|
||||
|
|
|
|||
|
|
@ -5,10 +5,9 @@ import express, { Request, RequestHandler, Response } from 'express';
|
|||
import bodyParser from 'body-parser';
|
||||
import { v4 as uuidgen } from 'uuid';
|
||||
import type { Arguments as ParentArguments } from '../nso.js';
|
||||
import CoralApi, { CoralApiInterface } from '../../api/coral.js';
|
||||
import CoralApi, { CoralApiInterface, CoralErrorResponse } from '../../api/coral.js';
|
||||
import { Announcement, CoralStatus, CurrentUser, Friend, FriendCodeUrl, FriendCodeUser, Presence } from '../../api/coral-types.js';
|
||||
import { AuthPolicy, AuthToken, ZncPresenceEventStreamEvent } from '../../api/znc-proxy.js';
|
||||
import { ErrorResponse } from '../../api/util.js';
|
||||
import createDebug from '../../util/debug.js';
|
||||
import { ArgumentsCamelCase, Argv, YargsArguments } from '../../util/yargs.js';
|
||||
import { initStorage } from '../../util/storage.js';
|
||||
|
|
@ -685,7 +684,7 @@ class Server extends HttpServer {
|
|||
const user = await coral.getUserByFriendCode(friendcode);
|
||||
return [user, id];
|
||||
} catch (err) {
|
||||
if (err instanceof ErrorResponse && err.data?.status === CoralStatus.RESOURCE_NOT_FOUND) {
|
||||
if (err instanceof CoralErrorResponse && err.status === CoralStatus.RESOURCE_NOT_FOUND) {
|
||||
// A user with this friend code doesn't exist
|
||||
// This should be cached
|
||||
return [null, id];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Response } from 'node-fetch';
|
||||
import CoralApi, { CoralApiInterface, CoralAuthData, Result, ZNCA_CLIENT_ID } from '../api/coral.js';
|
||||
import { Announcements, Friends, Friend, GetActiveEventResult, WebServices, CoralErrorResponse } from '../api/coral-types.js';
|
||||
import { Announcements, Friends, Friend, GetActiveEventResult, WebServices, CoralError } from '../api/coral-types.js';
|
||||
import ZncProxyApi from '../api/znc-proxy.js';
|
||||
import { NintendoAccountSession, Storage } from './storage/index.js';
|
||||
import { checkUseLimit } from './util.js';
|
||||
|
|
@ -262,7 +262,7 @@ function createTokenExpiredHandler(
|
|||
session: NintendoAccountSession<SavedToken>, coral: CoralApi,
|
||||
renew_token_data: {auth_data: SavedToken}, ratelimit = true
|
||||
) {
|
||||
return (data: CoralErrorResponse, response: Response) => {
|
||||
return (data: CoralError, response: Response) => {
|
||||
debug('Token expired', renew_token_data.auth_data.user.id, data);
|
||||
return renewToken(session, coral, renew_token_data, ratelimit);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@ import { ConfigureAnalyticsResult, CurrentFestResult, DetailVotingStatusResult,
|
|||
import createDebug from '../util/debug.js';
|
||||
import { ZNCA_CLIENT_ID } from '../api/coral.js';
|
||||
import { NintendoAccountSession, Storage } from './storage/index.js';
|
||||
import SplatNet3Api, { PersistedQueryResult, SplatNet3AuthData } from '../api/splatnet3.js';
|
||||
import SplatNet3Api, { PersistedQueryResult, SplatNet3AuthData, SplatNet3AuthErrorCode, SplatNet3AuthErrorResponse } from '../api/splatnet3.js';
|
||||
import Coral, { SavedToken as SavedCoralToken } from './coral.js';
|
||||
import { ErrorResponse } from '../api/util.js';
|
||||
import Users from './users.js';
|
||||
import { checkUseLimit } from './util.js';
|
||||
|
||||
|
|
@ -191,7 +190,7 @@ async function renewToken(
|
|||
debug('Unable to renew bullet token with saved web services token - cached data for this session token doesn\'t exist??');
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof ErrorResponse && err.response.status === 401) {
|
||||
if (err instanceof SplatNet3AuthErrorResponse && err.code === SplatNet3AuthErrorCode.ERROR_INVALID_GAME_WEB_TOKEN) {
|
||||
// Web service token invalid/expired...
|
||||
debug('Web service token expired, authenticating with new token', err);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import * as persist from 'node-persist';
|
||||
import { Response } from 'node-fetch';
|
||||
import CoralApi, { CoralAuthData, ZNCA_CLIENT_ID } from '../../api/coral.js';
|
||||
import { CoralErrorResponse } from '../../api/coral-types.js';
|
||||
import { CoralError } from '../../api/coral-types.js';
|
||||
import ZncProxyApi from '../../api/znc-proxy.js';
|
||||
import { getNintendoAccountUser, NintendoAccountSessionTokenJwtPayload } from '../../api/na.js';
|
||||
import createDebug from '../../util/debug.js';
|
||||
|
|
@ -120,7 +120,7 @@ function createTokenExpiredHandler(
|
|||
storage: persist.LocalStorage, token: string, nso: CoralApi,
|
||||
renew_token_data: {existingToken: SavedToken}, ratelimit = true
|
||||
) {
|
||||
return (data?: CoralErrorResponse, response?: Response) => {
|
||||
return (data?: CoralError, response?: Response) => {
|
||||
debug('Token expired', renew_token_data.existingToken.user.id, data);
|
||||
return renewToken(storage, token, nso, renew_token_data, ratelimit);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import persist from 'node-persist';
|
||||
import { Response } from 'node-fetch';
|
||||
import { getToken, Login, SavedToken } from './coral.js';
|
||||
import SplatNet3Api, { SplatNet3AuthData } from '../../api/splatnet3.js';
|
||||
import SplatNet3Api, { SplatNet3AuthData, SplatNet3AuthErrorCode, SplatNet3AuthErrorResponse } from '../../api/splatnet3.js';
|
||||
import { checkUseLimit, SHOULD_LIMIT_USE } from './util.js';
|
||||
import createDebug from '../../util/debug.js';
|
||||
import { Jwt } from '../../util/jwt.js';
|
||||
import { NintendoAccountSessionTokenJwtPayload } from '../../api/na.js';
|
||||
import { ErrorResponse } from '../../api/util.js';
|
||||
|
||||
const debug = createDebug('nxapi:auth:splatnet3');
|
||||
|
||||
|
|
@ -121,7 +120,7 @@ async function renewToken(
|
|||
debug('Unable to renew bullet token with saved web services token - cached data for this session token doesn\'t exist??');
|
||||
}
|
||||
} catch (err) {
|
||||
if (err instanceof ErrorResponse && err.response.status === 401) {
|
||||
if (err instanceof SplatNet3AuthErrorResponse && err.code === SplatNet3AuthErrorCode.ERROR_INVALID_GAME_WEB_TOKEN) {
|
||||
// Web service token invalid/expired...
|
||||
debug('Web service token expired, authenticating with new token', err);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import persist from 'node-persist';
|
||||
import { CoralApiInterface } from '../api/coral.js';
|
||||
import { ActiveEvent, Announcements, CurrentUser, Friend, Game, Presence, PresenceState, WebServices, CoralErrorResponse, GetActiveEventResult } from '../api/coral-types.js';
|
||||
import { ActiveEvent, Announcements, CurrentUser, Friend, Game, Presence, PresenceState, WebServices, CoralError, GetActiveEventResult } from '../api/coral-types.js';
|
||||
import ZncProxyApi from '../api/znc-proxy.js';
|
||||
import { ErrorResponse } from '../api/util.js';
|
||||
import { SavedToken } from './auth/coral.js';
|
||||
|
|
@ -179,7 +179,7 @@ export class ZncNotifications extends Loop {
|
|||
if (user) await this.updatePresenceForSplatNet2Monitors([user]);
|
||||
}
|
||||
|
||||
async handleError(err: ErrorResponse<CoralErrorResponse> | NodeJS.ErrnoException): Promise<LoopResult> {
|
||||
async handleError(err: ErrorResponse<CoralError> | NodeJS.ErrnoException): Promise<LoopResult> {
|
||||
return handleError(err, this);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { DiscordRpcClient, findDiscordRpcClient } from '../discord/rpc.js';
|
|||
import { getDiscordPresence, getInactiveDiscordPresence } from '../discord/util.js';
|
||||
import { DiscordPresencePlayTime, DiscordPresenceContext, DiscordPresence, ExternalMonitorConstructor, ExternalMonitor, ErrorResult } from '../discord/types.js';
|
||||
import { EmbeddedSplatNet2Monitor, ZncNotifications } from './notify.js';
|
||||
import { ActiveEvent, CurrentUser, Friend, Game, Presence, PresenceState, CoralErrorResponse } from '../api/coral-types.js';
|
||||
import { ActiveEvent, CurrentUser, Friend, Game, Presence, PresenceState, CoralError } from '../api/coral-types.js';
|
||||
import { getPresenceFromUrl } from '../api/znc-proxy.js';
|
||||
import createDebug from '../util/debug.js';
|
||||
import { ErrorResponse, ResponseSymbol } from '../api/util.js';
|
||||
|
|
@ -499,7 +499,7 @@ export class ZncDiscordPresence extends ZncNotifications {
|
|||
this.discord.title = {id: title_id, since: saved_presence.title_since};
|
||||
}
|
||||
|
||||
async handleError(err: ErrorResponse<CoralErrorResponse> | NodeJS.ErrnoException): Promise<LoopResult> {
|
||||
async handleError(err: ErrorResponse<CoralError> | NodeJS.ErrnoException): Promise<LoopResult> {
|
||||
this.discord.onError(err);
|
||||
|
||||
return super.handleError(err);
|
||||
|
|
@ -759,7 +759,7 @@ export class ZncProxyDiscordPresence extends Loop {
|
|||
}
|
||||
}
|
||||
|
||||
async handleError(err: ErrorResponse<CoralErrorResponse> | NodeJS.ErrnoException): Promise<LoopResult> {
|
||||
async handleError(err: ErrorResponse<CoralError> | NodeJS.ErrnoException): Promise<LoopResult> {
|
||||
this.discord.onError(err);
|
||||
|
||||
return handleError(err, this);
|
||||
|
|
|
|||
|
|
@ -2,12 +2,11 @@ import * as path from 'node:path';
|
|||
import * as fs from 'node:fs/promises';
|
||||
import persist from 'node-persist';
|
||||
import mkdirp from 'mkdirp';
|
||||
import SplatNet2Api from '../../api/splatnet2.js';
|
||||
import SplatNet2Api, { SplatNet2ErrorResponse } from '../../api/splatnet2.js';
|
||||
import { renewIksmToken } from '../auth/splatnet2.js';
|
||||
import { Records, Stages, WebServiceError } from '../../api/splatnet2-types.js';
|
||||
import { Records, Stages } from '../../api/splatnet2-types.js';
|
||||
import { dumpCoopResults, dumpResults } from './dump-results.js';
|
||||
import { dumpProfileImage, dumpRecords } from './dump-records.js';
|
||||
import { ErrorResponse } from '../../api/util.js';
|
||||
import createDebug from '../../util/debug.js';
|
||||
import Loop, { LoopResult } from '../../util/loop.js';
|
||||
|
||||
|
|
@ -90,8 +89,8 @@ export class SplatNet2RecordsMonitor extends Loop {
|
|||
}
|
||||
}
|
||||
|
||||
async handleError(err: Error | ErrorResponse<WebServiceError>): Promise<LoopResult> {
|
||||
if ('response' in err && err.data?.code === 'AUTHENTICATION_ERROR') {
|
||||
async handleError(err: Error): Promise<LoopResult> {
|
||||
if (err instanceof SplatNet2ErrorResponse && err.data?.code === 'AUTHENTICATION_ERROR') {
|
||||
// Token expired
|
||||
debug('Renewing iksm_session cookie');
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ export {
|
|||
ResponseDataSymbol,
|
||||
CorrelationIdSymbol,
|
||||
|
||||
CoralErrorResponse,
|
||||
|
||||
NintendoAccountSessionAuthorisationCoral,
|
||||
} from '../api/coral.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ export {
|
|||
MoonAuthData,
|
||||
PartialMoonAuthData,
|
||||
|
||||
MoonErrorResponse,
|
||||
|
||||
NintendoAccountSessionAuthorisationMoon,
|
||||
} from '../api/moon.js';
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,9 @@ export {
|
|||
NintendoAccountSessionAuthorisation,
|
||||
NintendoAccountSessionAuthorisationError,
|
||||
|
||||
NintendoAccountAuthErrorResponse,
|
||||
NintendoAccountErrorResponse,
|
||||
|
||||
NintendoAccountSessionToken,
|
||||
NintendoAccountToken,
|
||||
NintendoAccountAuthError,
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@ export {
|
|||
default as NooklinkApi,
|
||||
NooklinkAuthData,
|
||||
|
||||
NooklinkErrorResponse,
|
||||
|
||||
NooklinkUserApi,
|
||||
NooklinkUserAuthData,
|
||||
PartialNooklinkUserAuthData,
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@ export {
|
|||
SplatNet2AuthData,
|
||||
SplatNet2CliTokenData,
|
||||
|
||||
SplatNet2ErrorResponse,
|
||||
|
||||
LeagueType,
|
||||
LeagueRegion,
|
||||
ShareColour as ShareProfileColour,
|
||||
|
|
|
|||
|
|
@ -6,6 +6,11 @@ export {
|
|||
RequestIdSymbol,
|
||||
VariablesSymbol,
|
||||
|
||||
SplatNet3ErrorResponse,
|
||||
SplatNet3AuthErrorResponse,
|
||||
SplatNet3GraphQLErrorResponse,
|
||||
SplatNet3GraphQLResourceNotFoundResponse,
|
||||
|
||||
XRankingRegion,
|
||||
XRankingLeaderboardType,
|
||||
XRankingLeaderboardRule,
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import { AbortError } from 'node-fetch';
|
|||
import createDebug from './debug.js';
|
||||
import Loop, { LoopResult } from './loop.js';
|
||||
import { TemporaryErrorSymbol } from './misc.js';
|
||||
import { CoralErrorResponse } from '../api/coral-types.js';
|
||||
import { ErrorResponse } from '../api/util.js';
|
||||
|
||||
const debug = createDebug('nxapi:util:errors');
|
||||
|
|
@ -28,7 +27,7 @@ export const temporary_http_errors = [
|
|||
];
|
||||
|
||||
export async function handleError(
|
||||
err: ErrorResponse<CoralErrorResponse> | NodeJS.ErrnoException,
|
||||
err: ErrorResponse | NodeJS.ErrnoException,
|
||||
loop: Loop,
|
||||
): Promise<LoopResult> {
|
||||
if (TemporaryErrorSymbol in err && err[TemporaryErrorSymbol]) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user