FoundKey/packages/backend/src/server/api/error.ts
2023-03-28 22:49:58 +02:00

396 lines
9.1 KiB
TypeScript

import Koa from 'koa';
export class ApiError extends Error {
public message: string;
public code: string;
public httpStatusCode: number;
public info?: any;
constructor(
code: keyof errors = 'INTERNAL_ERROR',
info?: any | null,
) {
let _info = info, _code = code;
if (!(code in errors)) {
_code = 'INTERNAL_ERROR';
_info = `Unknown error "${code}" occurred.`;
}
const { message, httpStatusCode } = errors[_code];
super(message);
this.code = _code;
this.info = _info;
this.message = message;
this.httpStatusCode = httpStatusCode;
}
/**
* Makes the response of ctx the current error, given the respective endpoint name.
*/
public apply(ctx: Koa.Context, endpoint: string): void {
ctx.status = this.httpStatusCode;
// set additional headers
switch (ctx.status) {
case 401:
ctx.response.set('WWW-Authenticate', 'Bearer');
break;
case 429:
if (typeof this.info === 'object' && typeof this.info.reset === 'number') {
ctx.response.set('Retry-After', Math.floor(this.info.reset - (Date.now() / 1000)));
}
break;
}
ctx.body = {
error: {
message: this.message,
code: this.code,
info: this.info ?? undefined,
endpoint,
},
};
}
}
export const errors: Record<string, { message: string, httpStatusCode: number }> = {
ACCESS_DENIED: {
message: 'Access denied.',
httpStatusCode: 403,
},
ALREADY_ADDED: {
message: 'That user has already been added to that list or group.',
httpStatusCode: 409,
},
ALREADY_BLOCKING: {
message: 'You are already blocking that user.',
httpStatusCode: 409,
},
ALREADY_CLIPPED: {
message: 'That note is already added to that clip.',
httpStatusCode: 409,
},
ALREADY_FAVORITED: {
message: 'That note is already favorited.',
httpStatusCode: 409,
},
ALREADY_FOLLOWING: {
message: 'You are already following that user.',
httpStatusCode: 409,
},
ALREADY_INVITED: {
message: 'That user has already been invited to that group.',
httpStatusCode: 409,
},
ALREADY_LIKED: {
message: 'You already liked that page.',
httpStatusCode: 409,
},
ALREADY_MUTING: {
message: 'You are already muting that user.',
httpStatusCode: 409,
},
ALREADY_PINNED: {
message: 'You already pinned that note.',
httpStatusCode: 409,
},
ALREADY_REACTED: {
message: 'You already reacted to that note.',
httpStatusCode: 409,
},
ALREADY_VOTED: {
message: 'You have already voted in that poll.',
httpStatusCode: 409,
},
AUTHENTICATION_FAILED: {
message: 'Authentication failed.',
httpStatusCode: 401,
},
AUTHENTICATION_REQUIRED: {
message: 'Authentication is required, but authenticating information was not or not appropriately provided.',
httpStatusCode: 401,
},
BLOCKED: {
message: 'You are blocked by that user.',
httpStatusCode: 400,
},
BLOCKEE_IS_YOURSELF: {
message: 'You cannot block yourself.',
httpStatusCode: 400,
},
BLOCKING: {
message: 'You are blocking that user.',
httpStatusCode: 400,
},
CANNOT_REPORT_ADMIN: {
message: 'You cannot report an administrator.',
httpStatusCode: 400,
},
CANNOT_REPORT_YOURSELF: {
message: 'You cannot report yourself.',
httpStatusCode: 400,
},
EMPTY_FILE: {
message: 'The provided file is empty.',
httpStatusCode: 400,
},
EXPIRED_POLL: {
message: 'Poll is already expired.',
httpStatusCode: 400,
},
FAILED_TO_RESOLVE_REMOTE_USER: {
message: 'Failed to resolve remote user.',
httpStatusCode: 502,
},
FILE_TOO_BIG: {
message: 'The provided file is too big.',
httpStatusCode: 400,
},
FILE_REQUIRED: {
message: 'This operation requires a file to be provided.',
httpStatusCode: 400,
},
FOLLOWEE_IS_YOURSELF: {
message: 'You cannot follow yourself.',
httpStatusCode: 400,
},
FOLLOWER_IS_YOURSELF: {
message: 'You cannot unfollow yourself.',
httpStatusCode: 400,
},
GROUP_OWNER: {
message: 'The owner of a group may not leave. Instead, ownership can be transferred or the group deleted.',
httpStatusCode: 400,
},
HAS_CHILD_FILES_OR_FOLDERS: {
message: 'That folder is not empty.',
httpStatusCode: 400,
},
INTERNAL_ERROR: {
message: 'Internal error occurred. Please contact us if the error persists.',
httpStatusCode: 500,
},
INVALID_CHOICE: {
message: 'Choice index is invalid.',
httpStatusCode: 400,
},
INVALID_FILE_NAME: {
message: 'Invalid file name.',
httpStatusCode: 400,
},
INVALID_PARAM: {
message: 'One or more parameters do not match the API definition.',
httpStatusCode: 400,
},
INVALID_PASSWORD: {
message: 'The provided password is not suitable.',
httpStatusCode: 400,
},
INVALID_REGEXP: {
message: 'Invalid Regular Expression',
httpStatusCode: 400,
},
INVALID_URL: {
message: 'Invalid URL.',
httpStatusCode: 400,
},
INVALID_USERNAME: {
message: 'Invalid username.',
httpStatusCode: 400,
},
IS_ADMIN: {
message: 'This action cannot be done to an administrator account.',
httpStatusCode: 400,
},
IS_MODERATOR: {
message: 'This action cannot be done to a moderator account.',
httpStatusCode: 400,
},
LESS_RESTRICTIVE_VISIBILITY: {
message: 'The visibility cannot be less restrictive than the parent note.',
httpStatusCode: 400,
},
MUTEE_IS_YOURSELF: {
message: 'You cannot mute yourself.',
httpStatusCode: 400,
},
NAME_ALREADY_EXISTS: {
message: 'The specified name already exists.',
httpStatusCode: 409,
},
NO_POLL: {
message: 'The note does not have an attached poll.',
httpStatusCode: 404,
},
NO_SUCH_ANNOUNCEMENT: {
message: 'No such announcement.',
httpStatusCode: 404,
},
NO_SUCH_ANTENNA: {
message: 'No such antenna.',
httpStatusCode: 404,
},
NO_SUCH_APP: {
message: 'No such app.',
httpStatusCode: 404,
},
NO_SUCH_CLIP: {
message: 'No such clip.',
httpStatusCode: 404,
},
NO_SUCH_CHANNEL: {
message: 'No such channel.',
httpStatusCode: 404,
},
NO_SUCH_EMOJI: {
message: 'No such emoji.',
httpStatusCode: 404,
},
NO_SUCH_ENDPOINT: {
message: 'No such endpoint.',
httpStatusCode: 404,
},
NO_SUCH_FILE: {
message: 'No such file.',
httpStatusCode: 404,
},
NO_SUCH_FOLDER: {
message: 'No such folder.',
httpStatusCode: 404,
},
NO_SUCH_FOLLOW_REQUEST: {
message: 'No such follow request.',
httpStatusCode: 404,
},
NO_SUCH_GROUP: {
message: 'No such user group.',
httpStatusCode: 404,
},
NO_SUCH_HASHTAG: {
message: 'No such hashtag.',
httpStatusCode: 404,
},
NO_SUCH_INVITATION: {
message: 'No such group invitation.',
httpStatusCode: 404,
},
NO_SUCH_KEY: {
message: 'No such key.',
httpStatusCode: 404,
},
NO_SUCH_NOTE: {
message: 'No such note.',
httpStatusCode: 404,
},
NO_SUCH_NOTIFICATION: {
message: 'No such notification.',
httpStatusCode: 404,
},
NO_SUCH_MESSAGE: {
message: 'No such message.',
httpStatusCode: 404,
},
NO_SUCH_OBJECT: {
message: 'No such object.',
httpStatusCode: 404,
},
NO_SUCH_PAGE: {
message: 'No such page.',
httpStatusCode: 404,
},
NO_SUCH_PARENT_FOLDER: {
message: 'No such parent folder.',
httpStatusCode: 404,
},
NO_SUCH_RESET_REQUEST: {
message: 'No such password reset request.',
httpStatusCode: 404,
},
NO_SUCH_SESSION: {
message: 'No such session',
httpStatusCode: 404,
},
NO_SUCH_USER: {
message: 'No such user.',
httpStatusCode: 404,
},
NO_SUCH_USER_LIST: {
message: 'No such user list.',
httpStatusCode: 404,
},
NO_SUCH_WEBHOOK: {
message: 'No such webhook.',
httpStatusCode: 404,
},
NOT_AN_IMAGE: {
message: 'The file specified was expected to be an image, but it is not.',
httpStatusCode: 400,
},
NOT_BLOCKING: {
message: 'You are not blocking that user.',
httpStatusCode: 409,
},
NOT_CLIPPED: {
message: 'That note is not added to that clip.',
httpStatusCode: 409,
},
NOT_FAVORITED: {
message: 'You have not favorited that note.',
httpStatusCode: 409,
},
NOT_FOLLOWING: {
message: 'You are not following that user.',
httpStatusCode: 409,
},
NOT_FOLLOWED: {
message: 'You are not followed by that user.',
httpStatusCode: 409,
},
NOT_LIKED: {
message: 'You have not liked that page.',
httpStatusCode: 409,
},
NOT_MUTING: {
message: 'You are not muting that user.',
httpStatusCode: 409,
},
NOT_REACTED: {
message: 'You have not reacted to that note.',
httpStatusCode: 409,
},
PENDING_SESSION: {
message: 'That authorization process has not been completed yet.',
httpStatusCode: 400,
},
PIN_LIMIT_EXCEEDED: {
message: 'You can not pin any more notes.',
httpStatusCode: 400,
},
PURE_RENOTE: {
message: 'You cannot renote or reply to a pure renote.',
httpStatusCode: 400,
},
RATE_LIMIT_EXCEEDED: {
message: 'Rate limit exceeded. Please try again later.',
httpStatusCode: 429,
},
RECIPIENT_IS_YOURSELF: {
message: 'You cannot send a message to yourself.',
httpStatusCode: 400,
},
RECURSIVE_FOLDER: {
message: 'Folder cannot be its own parent.',
httpStatusCode: 400,
},
SUSPENDED: {
message: 'Your account has been suspended.',
httpStatusCode: 403,
},
TIMELINE_DISABLED: {
message: 'This timeline is disabled by an administrator.',
httpStatusCode: 503,
},
USED_USERNAME: {
message: 'That username is not available because it is being used or has been used before. Usernames cannot be reassigned.',
httpStatusCode: 409,
},
};