Skip to content

Commit

Permalink
improve errors and add time of error to log
Browse files Browse the repository at this point in the history
  • Loading branch information
3vorp committed Aug 15, 2024
1 parent 277c9c1 commit 5e6f4ee
Show file tree
Hide file tree
Showing 23 changed files with 82 additions and 89 deletions.
113 changes: 54 additions & 59 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import responseTime from "response-time";
import cors from "cors";
import apiErrorHandler from "api-error-handler";
import { RegisterRoutes } from "../build/routes";
import { ApiError } from "./v2/tools/ApiError";
import { APIError } from "./v2/tools/errors";
import formatSwaggerDoc from "./v2/tools/swagger";

const NO_CACHE = process.env.NO_CACHE === "true";
Expand Down Expand Up @@ -78,81 +78,76 @@ app.use(
);

// handle errors
app.use((err: any, req: Request, res: Response, next: NextFunction): Promise<void> => {
let code: number;
app.use((err: any, req: Request, res: Response, next: NextFunction) => {
if (err instanceof ValidateError) {
console.error("ValidateError", err);
console.warn(
console.error(
`Caught Validation Error for ${req.path}: ${JSON.stringify(err.fields)}`,
err.fields,
);

code = 422;

res.status(422).json({
return res.status(422).json({
message: "Validation Failed",
details: err?.fields,
});
return;
}
if (err) {
if (err.isAxiosError)
console.error("axios error: body, headers, err", req.body, req.headers, err);
code =
Number(
(typeof err.status === "number" ? err.status : err.statusCode) ||
(err.response ? err.response.status : err.code),
) || 400;
const message =
(err.response && err.response.data
? err.response.data.error || err.response.data.message
: err.message) || err;
const stack = process.env.VERBOSE && err.stack ? err.stack : "";

if (process.env.VERBOSE === "true") {
console.error("code, message, stack", code, message, stack);
}

let name = err?.response?.data?.name || err.name;

if (!name) {
try {
name = status(code).replace(/ /g, "");
} catch {
// you tried your best, we don't blame you
}
}

const finalError = new ApiError(name, code, message);
if (!err) return next();

if (err.isAxiosError)
console.error("axios error: body, headers, err", req.body, req.headers, err);
const code =
Number(
(typeof err.status === "number" ? err.status : err.statusCode) ||
(err.response ? err.response.status : err.code),
) || 400;
const message =
(err.response && err.response.data
? err.response.data.error || err.response.data.message
: err.message) || err;
const stack = process.env.VERBOSE && err.stack ? err.stack : "";

if (process.env.VERBOSE === "true")
console.error(
`[${new Date().toUTCString()}] Code, Message, Stack ${code}`, message, "\n", stack,
);

// modify error to give more context and details with data
let modified: {
name: string;
message: string;
};
let name = err?.response?.data?.name || err.name;

if (err?.response?.data !== undefined) {
modified = {
name: finalError.name,
message: finalError.message,
};
finalError.name += `: ${finalError.message}`;
finalError.message = err.response.data;
if (!name) {
try {
name = status(code).replace(/ /g, "");
} catch {
// you tried your best, we don't blame you
}
}

// unmodify error to hide details returned as api response
if (modified !== undefined) {
finalError.name = modified.name;
finalError.message = modified.message;
}
const finalError = new APIError(name, code, message);

// modify error to give more context and details with data
let modified: {
name: string;
message: string;
};

// i hate the stack in api response
delete finalError.stack;
if (err?.response?.data !== undefined) {
modified = {
name: finalError.name,
message: finalError.message,
};
finalError.name += `: ${finalError.message}`;
finalError.message = err.response.data;
}

apiErrorHandler()(finalError, req, res, next);
res.end();
return;
// unmodify error to hide details returned as api response
if (modified !== undefined) {
finalError.name = modified.name;
finalError.message = modified.message;
}

next();
// i hate the stack in api response
delete finalError.stack;

apiErrorHandler()(finalError, req, res, next);
res.end();
});
2 changes: 1 addition & 1 deletion src/v2/controller/addon.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
} from "../interfaces";

import AddonService from "../service/addon.service";
import { NotAvailableError, NotFoundError, PermissionError } from "../tools/ApiError";
import { NotAvailableError, NotFoundError, PermissionError } from "../tools/errors";
import * as cache from "../tools/cache";

@Route("addons")
Expand Down
2 changes: 1 addition & 1 deletion src/v2/controller/addonChange.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import {
AddonReviewBody,
AddonUpdateParam,
} from "../interfaces/addons";
import { PermissionError, BadRequestError } from "../tools/ApiError";
import { PermissionError, BadRequestError } from "../tools/errors";
import UserService from "../service/user.service";
import AddonService from "../service/addon.service";
import * as cache from "../tools/cache";
Expand Down
2 changes: 1 addition & 1 deletion src/v2/controller/contribution.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {
PackID,
} from "../interfaces";
import ContributionService from "../service/contribution.service";
import { NotAvailableError } from "../tools/ApiError";
import { NotAvailableError } from "../tools/errors";
import * as cache from "../tools/cache";

@Route("contributions")
Expand Down
4 changes: 1 addition & 3 deletions src/v2/controller/gallery.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,7 @@ export class GalleryController extends Controller {
public async modal(@Path() id: number, @Path() version: string): Promise<GalleryModalResult> {
const packIDs = Object.keys(await this.packService.getRaw());
const urls: Record<PackID, string> = (
await Promise.allSettled(
packIDs.map((p) => this.textureService.getURLById(id, p, version)),
)
await Promise.allSettled(packIDs.map((p) => this.textureService.getURLById(id, p, version)))
)
.map((e, i) => [packIDs[i], e])
.filter((p: [PackID, PromiseFulfilledResult<string>]) => p[1].status === "fulfilled")
Expand Down
2 changes: 1 addition & 1 deletion src/v2/controller/mods.controller.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Controller, Get, Path, Request, Route, SuccessResponse, Tags, Response } from "tsoa";
import { Request as ExRequest } from "express";
import { Mod } from "../interfaces";
import { NotFoundError } from "../tools/ApiError";
import { NotFoundError } from "../tools/errors";
import ModsService from "../service/mods.service";
import * as cache from "../tools/cache";

Expand Down
2 changes: 1 addition & 1 deletion src/v2/controller/post.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
CreateWebsitePost,
} from "../interfaces";

import { BadRequestError, NotFoundError, PermissionError } from "../tools/ApiError";
import { BadRequestError, NotFoundError, PermissionError } from "../tools/errors";
import * as cache from "../tools/cache";
import PostService from "../service/post.service";

Expand Down
2 changes: 1 addition & 1 deletion src/v2/controller/settings.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import {
Tags,
} from "tsoa";
import { WriteConfirmation } from "firestorm-db";
import { NotFoundError, PermissionError } from "../tools/ApiError";
import { NotFoundError, PermissionError } from "../tools/errors";
import SettingsService from "../service/settings.service";

@Route("settings")
Expand Down
2 changes: 1 addition & 1 deletion src/v2/controller/texture.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
EntireTextureToCreate,
} from "../interfaces";
import TextureService from "../service/texture.service";
import { NotFoundError } from "../tools/ApiError";
import { NotFoundError } from "../tools/errors";

@Route("textures")
@Tags("Textures")
Expand Down
2 changes: 1 addition & 1 deletion src/v2/controller/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
Tags,
} from "tsoa";
import { WriteConfirmation } from "firestorm-db";
import { BadRequestError, ForbiddenError, NotAvailableError } from "../tools/ApiError";
import { BadRequestError, ForbiddenError, NotAvailableError } from "../tools/errors";
import {
Addons,
Contributions,
Expand Down
2 changes: 1 addition & 1 deletion src/v2/firestorm/textures/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import "../config";
import { uses } from "./uses";
import { contributions, packs } from "..";
import { MinecraftSorter } from "../../tools/sorter";
import { NotFoundError } from "../../tools/ApiError";
import { NotFoundError } from "../../tools/errors";

export const textures = firestorm.collection<FirestormTexture>("textures", (el) => {
el.uses = (): Promise<Uses> =>
Expand Down
2 changes: 1 addition & 1 deletion src/v2/repository/mods.repository.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import axios from "axios";
import { NotFoundError } from "../tools/ApiError";
import { NotFoundError } from "../tools/errors";
import { mods } from "../firestorm";
import { Mod, ModsRepository } from "../interfaces";

Expand Down
2 changes: 1 addition & 1 deletion src/v2/repository/texture.repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
TextureRepository,
PropertyToOutput,
} from "../interfaces";
import { NotFoundError } from "../tools/ApiError";
import { NotFoundError } from "../tools/errors";
import { textures, paths, uses, contributions, settings, packs } from "../firestorm";
import { MinecraftSorter } from "../tools/sorter";

Expand Down
2 changes: 1 addition & 1 deletion src/v2/service/addon.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { APIEmbedField } from "discord-api-types/v10";
import { WriteConfirmation } from "firestorm-db";
import { User, UserProfile } from "../interfaces/users";
import { Addons, Addon, AddonStatus, AddonAll, Files, File, FileParent } from "../interfaces";
import { BadRequestError, NotFoundError } from "../tools/ApiError";
import { BadRequestError, NotFoundError } from "../tools/errors";
import UserService from "./user.service";
import FileService from "./file.service";
import {
Expand Down
2 changes: 1 addition & 1 deletion src/v2/service/auth.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Request as ExRequest } from "express";
import { NotFoundError } from "../tools/ApiError";
import { NotFoundError } from "../tools/errors";

// REDIRECT URL IS '<origin>/auth/<provider>/callback/<target>'
const REDIRECT_URI_PATHNAME = "/callback/";
Expand Down
2 changes: 1 addition & 1 deletion src/v2/service/path.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { WriteConfirmation } from "firestorm-db";
import { BadRequestError } from "../tools/ApiError";
import { BadRequestError } from "../tools/errors";
import UseService from "./use.service";
import { InputPath, Path, PathNewVersionParam, Paths } from "../interfaces";
import PathFirestormRepository from "../repository/path.repository";
Expand Down
2 changes: 1 addition & 1 deletion src/v2/service/post.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
WebsitePost,
CreateWebsitePost,
} from "../interfaces";
import { NotFoundError } from "../tools/ApiError";
import { NotFoundError } from "../tools/errors";
import PostFirestormRepository from "../repository/posts.repository";

export default class PostService {
Expand Down
2 changes: 1 addition & 1 deletion src/v2/service/submission.service.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { WriteConfirmation } from "firestorm-db";
import { Submission, CreationSubmission, PackID, PackAll } from "../interfaces";
import SubmissionFirestormRepository from "../repository/submission.repository";
import { BadRequestError } from "../tools/ApiError";
import { BadRequestError } from "../tools/errors";
import PackService from "./pack.service";

export default class SubmissionService {
Expand Down
2 changes: 1 addition & 1 deletion src/v2/service/use.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {
GalleryEdition,
} from "../interfaces";
import UseFirestormRepository from "../repository/use.repository";
import { BadRequestError, NotFoundError } from "../tools/ApiError";
import { BadRequestError, NotFoundError } from "../tools/errors";
import PathService from "./path.service";

export default class UseService {
Expand Down
2 changes: 1 addition & 1 deletion src/v2/service/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
Username,
} from "../interfaces";
import UserFirestormRepository from "../repository/user.repository";
import { BadRequestError } from "../tools/ApiError";
import { BadRequestError } from "../tools/errors";

export default class UserService {
private readonly repo = new UserFirestormRepository();
Expand Down
6 changes: 3 additions & 3 deletions src/v2/tools/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import "dotenv/config";
import { Request as ExRequest } from "express";
import axios from "axios";
import { APIUser } from "discord-api-types/v10";
import { PermissionError, NotFoundError, ApiError, ForbiddenError } from "./ApiError";
import { PermissionError, NotFoundError, APIError, ForbiddenError } from "./errors";
import UserService from "../service/user.service";
import AddonService from "../service/addon.service";
import { Addon } from "../interfaces";
Expand Down Expand Up @@ -80,13 +80,13 @@ export async function expressAuthentication(
.then((response) => response.data)
.catch(
(err) =>
new ApiError(
new APIError(
"Discord Error",
err?.response?.status,
err?.response?.data?.message || err.message,
),
);
if (discordUser instanceof ApiError) return Promise.reject(discordUser);
if (discordUser instanceof APIError) return Promise.reject(discordUser);

// proven (authentified)

Expand Down
10 changes: 5 additions & 5 deletions src/v2/tools/ApiError.ts → src/v2/tools/errors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable max-classes-per-file */

export class ApiError extends Error {
export class APIError extends Error {
readonly statusCode: number;

constructor(name: string, statusCode: number, message?: string) {
Expand All @@ -10,25 +10,25 @@ export class ApiError extends Error {
}
}

export class BadRequestError extends ApiError {
export class BadRequestError extends APIError {
constructor(message?: string) {
super("BadRequest", 400, message);
}
}

export class NotFoundError extends ApiError {
export class NotFoundError extends APIError {
constructor(message?: string) {
super("NotFound", 404, message);
}
}

export class NotAvailableError extends ApiError {
export class NotAvailableError extends APIError {
constructor(message?: string) {
super("ResourceNotAvailableYet", 408, message);
}
}

export class ForbiddenError extends ApiError {
export class ForbiddenError extends APIError {
constructor(message?: string) {
super("Forbidden", 403, message);
}
Expand Down
2 changes: 1 addition & 1 deletion src/v2/tools/swagger.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Application, NextFunction, Response as ExResponse, Request as ExRequest
import { readFileSync } from "fs";
import { AddonChangeController } from "../controller/addonChange.controller";
import { expressAuthentication } from "./authentication";
import { BadRequestError } from "./ApiError";
import { BadRequestError } from "./errors";

const MIME_TYPES_ACCEPTED = ["image/gif", "image/png", "image/jpeg"];

Expand Down

0 comments on commit 5e6f4ee

Please sign in to comment.