Error handler for API, separated schemas
This commit is contained in:
parent
e106509c45
commit
66ce1caab0
34
src/errors/api.ts
Normal file
34
src/errors/api.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
export interface IApiError {
|
||||||
|
code: number;
|
||||||
|
name: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const errorSchema = {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
code: {
|
||||||
|
type: "number",
|
||||||
|
description: "HTTP error code",
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: "string",
|
||||||
|
description: "Exception class name",
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
type: "string",
|
||||||
|
description: "Exception message",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const errorResponseSchema = {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
data: {
|
||||||
|
type: "object",
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
error: errorSchema,
|
||||||
|
},
|
||||||
|
};
|
@ -4,14 +4,42 @@ import { getFastifyError } from "./validation";
|
|||||||
|
|
||||||
export default function errorHandler(
|
export default function errorHandler(
|
||||||
error: Error,
|
error: Error,
|
||||||
_: FastifyRequest,
|
req: FastifyRequest,
|
||||||
reply: FastifyReply
|
reply: FastifyReply
|
||||||
) {
|
) {
|
||||||
// TODO: check if req.url starts with "/api/" and return JSON
|
if (req.originalUrl.startsWith("/api/")) {
|
||||||
|
return apiErrorHandler(error, reply);
|
||||||
|
}
|
||||||
return htmlErrorHandler(error, reply);
|
return htmlErrorHandler(error, reply);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function apiErrorHandler(error: Error, reply: FastifyReply) {
|
||||||
|
function generateResponse(code: number) {
|
||||||
|
return reply.code(code).send({
|
||||||
|
data: null,
|
||||||
|
error: {
|
||||||
|
code: code,
|
||||||
|
name: error.name,
|
||||||
|
message: error.message,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof NotHtmlMimetypeError) {
|
||||||
|
return generateResponse(501);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getFastifyError(error)?.statusCode === 400) {
|
||||||
|
return generateResponse(400);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error instanceof TxtDotError) {
|
||||||
|
return generateResponse(error.code);
|
||||||
|
}
|
||||||
|
|
||||||
|
return generateResponse(500);
|
||||||
|
}
|
||||||
|
|
||||||
function htmlErrorHandler(error: Error, reply: FastifyReply) {
|
function htmlErrorHandler(error: Error, reply: FastifyReply) {
|
||||||
if (error instanceof NotHtmlMimetypeError) {
|
if (error instanceof NotHtmlMimetypeError) {
|
||||||
return reply.redirect(301, error.url);
|
return reply.redirect(301, error.url);
|
||||||
|
@ -1,27 +1,30 @@
|
|||||||
export class TxtDotError extends Error {
|
export abstract class TxtDotError extends Error {
|
||||||
code: number;
|
code: number;
|
||||||
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
|
|
||||||
constructor(code: number, description: string) {
|
constructor(code: number, name: string, description: string) {
|
||||||
super(description);
|
super(description);
|
||||||
this.code = code;
|
this.code = code;
|
||||||
|
this.name = name;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EngineParseError extends TxtDotError {
|
export class EngineParseError extends TxtDotError {
|
||||||
constructor(message: string) {
|
constructor(message: string) {
|
||||||
super(500, `Parse error: ${message}`);
|
super(422, "EngineParseError", `Parse error: ${message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class LocalResourceError extends TxtDotError {
|
export class LocalResourceError extends TxtDotError {
|
||||||
constructor() {
|
constructor() {
|
||||||
super(403, "Proxying local resources is forbidden.");
|
super(403, "LocalResourceError", "Proxying local resources is forbidden.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class NotHtmlMimetypeError extends Error {
|
export class NotHtmlMimetypeError extends Error {
|
||||||
|
name: string = "NotHtmlMimetypeError";
|
||||||
url: string;
|
url: string;
|
||||||
|
|
||||||
constructor(url: string) {
|
constructor(url: string) {
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { FastifyInstance } from "fastify";
|
import { FastifyInstance } from "fastify";
|
||||||
|
|
||||||
import { GetSchema, IGetSchema } from "../types/requests";
|
import { GetSchema, IGetSchema } from "../types/requests/browser";
|
||||||
import handlePage from "../handlers/main";
|
import handlePage from "../handlers/main";
|
||||||
import { generateRequestUrl } from "../utils/generate";
|
import { generateRequestUrl } from "../utils/generate";
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { FastifyInstance } from "fastify";
|
import { FastifyInstance } from "fastify";
|
||||||
import { engineList } from "../handlers/main";
|
import { engineList } from "../handlers/main";
|
||||||
import { indexSchema } from "../types/requests";
|
import { indexSchema } from "../types/requests/browser";
|
||||||
|
|
||||||
export default async function indexRoute(fastify: FastifyInstance) {
|
export default async function indexRoute(fastify: FastifyInstance) {
|
||||||
fastify.get("/", { schema: indexSchema }, async (_, reply) => {
|
fastify.get("/", { schema: indexSchema }, async (_, reply) => {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import { EngineRequest, IParseSchema, parseSchema } from "../types/requests";
|
|
||||||
import { FastifyInstance } from "fastify";
|
import { FastifyInstance } from "fastify";
|
||||||
|
|
||||||
|
import { EngineRequest, IParseSchema, parseSchema } from "../types/requests/api";
|
||||||
|
|
||||||
import handlePage from "../handlers/main";
|
import handlePage from "../handlers/main";
|
||||||
import { generateRequestUrl } from "../utils/generate";
|
import { generateRequestUrl } from "../utils/generate";
|
||||||
|
|
||||||
@ -8,7 +10,8 @@ export default async function parseRoute(fastify: FastifyInstance) {
|
|||||||
"/api/parse",
|
"/api/parse",
|
||||||
{ schema: parseSchema },
|
{ schema: parseSchema },
|
||||||
async (request: EngineRequest) => {
|
async (request: EngineRequest) => {
|
||||||
return await handlePage(
|
return {
|
||||||
|
data: await handlePage(
|
||||||
request.query.url,
|
request.query.url,
|
||||||
generateRequestUrl(
|
generateRequestUrl(
|
||||||
request.protocol,
|
request.protocol,
|
||||||
@ -16,7 +19,9 @@ export default async function parseRoute(fastify: FastifyInstance) {
|
|||||||
request.originalUrl
|
request.originalUrl
|
||||||
),
|
),
|
||||||
request.query.engine
|
request.query.engine
|
||||||
);
|
),
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
import { FastifyInstance } from "fastify";
|
import { FastifyInstance } from "fastify";
|
||||||
|
|
||||||
import { GetRequest, IParseSchema, rawHtmlSchema } from "../types/requests";
|
import { IParseSchema, rawHtmlSchema } from "../types/requests/api";
|
||||||
|
import { GetRequest } from "../types/requests/browser";
|
||||||
|
|
||||||
import handlePage from "../handlers/main";
|
import handlePage from "../handlers/main";
|
||||||
import { generateRequestUrl } from "../utils/generate";
|
import { generateRequestUrl } from "../utils/generate";
|
||||||
|
|
||||||
|
@ -1,95 +0,0 @@
|
|||||||
import { FastifyRequest, FastifySchema } from "fastify";
|
|
||||||
import { handlerSchema } from "../handlers/handler.interface";
|
|
||||||
import { engineList } from "../handlers/main";
|
|
||||||
|
|
||||||
export type GetRequest = FastifyRequest<{
|
|
||||||
Querystring: IGetQuery;
|
|
||||||
}>;
|
|
||||||
|
|
||||||
export interface IGetQuery {
|
|
||||||
url: string;
|
|
||||||
format?: string;
|
|
||||||
engine?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IParseQuery {
|
|
||||||
url: string;
|
|
||||||
engine?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IGetSchema {
|
|
||||||
Querystring: IGetQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IParseSchema {
|
|
||||||
Querystring: IParseQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const indexSchema = {
|
|
||||||
produces: ["text/html"],
|
|
||||||
hide: true
|
|
||||||
};
|
|
||||||
|
|
||||||
export const getQuerySchema = {
|
|
||||||
type: "object",
|
|
||||||
required: ["url"],
|
|
||||||
properties: {
|
|
||||||
url: {
|
|
||||||
type: "string",
|
|
||||||
description: "URL",
|
|
||||||
},
|
|
||||||
format: {
|
|
||||||
type: "string",
|
|
||||||
enum: ["text", "html", ""],
|
|
||||||
default: "html",
|
|
||||||
},
|
|
||||||
engine: {
|
|
||||||
type: "string",
|
|
||||||
enum: [...engineList, ""],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseQuerySchema = {
|
|
||||||
type: "object",
|
|
||||||
required: ["url"],
|
|
||||||
properties: {
|
|
||||||
url: {
|
|
||||||
type: "string",
|
|
||||||
description: "URL",
|
|
||||||
},
|
|
||||||
engine: {
|
|
||||||
type: "string",
|
|
||||||
enum: [...engineList, ""],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const GetSchema: FastifySchema = {
|
|
||||||
description: "Get page",
|
|
||||||
hide: true,
|
|
||||||
querystring: getQuerySchema,
|
|
||||||
produces: ["text/html", "text/plain"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const parseSchema: FastifySchema = {
|
|
||||||
description: "Parse page",
|
|
||||||
querystring: parseQuerySchema,
|
|
||||||
response: {
|
|
||||||
"2xx": handlerSchema,
|
|
||||||
},
|
|
||||||
produces: ["text/json"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export const rawHtmlSchema: FastifySchema = {
|
|
||||||
description: "Get raw HTML",
|
|
||||||
querystring: parseQuerySchema,
|
|
||||||
produces: ["text/html"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export type EngineRequest = FastifyRequest<{
|
|
||||||
Querystring: {
|
|
||||||
url: string;
|
|
||||||
engine?: string;
|
|
||||||
};
|
|
||||||
}>;
|
|
66
src/types/requests/api.ts
Normal file
66
src/types/requests/api.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import { FastifySchema, FastifyRequest } from "fastify";
|
||||||
|
import { IApiError, errorResponseSchema } from "../../errors/api";
|
||||||
|
import { handlerSchema } from "../../handlers/handler.interface";
|
||||||
|
import { engineList } from "../../handlers/main";
|
||||||
|
|
||||||
|
export interface IApiResponse<T> {
|
||||||
|
data?: T;
|
||||||
|
error?: IApiError;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IParseQuery {
|
||||||
|
url: string;
|
||||||
|
engine?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IParseSchema {
|
||||||
|
Querystring: IParseQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parseQuerySchema = {
|
||||||
|
type: "object",
|
||||||
|
required: ["url"],
|
||||||
|
properties: {
|
||||||
|
url: {
|
||||||
|
type: "string",
|
||||||
|
description: "URL",
|
||||||
|
},
|
||||||
|
engine: {
|
||||||
|
type: "string",
|
||||||
|
enum: [...engineList, ""],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const parseSchema: FastifySchema = {
|
||||||
|
description: "Parse the page and get all data from the engine",
|
||||||
|
querystring: parseQuerySchema,
|
||||||
|
response: {
|
||||||
|
"2xx": {
|
||||||
|
type: "object",
|
||||||
|
properties: {
|
||||||
|
data: handlerSchema,
|
||||||
|
error: {
|
||||||
|
type: "object",
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"4xx": errorResponseSchema,
|
||||||
|
"5xx": errorResponseSchema,
|
||||||
|
},
|
||||||
|
produces: ["text/json"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const rawHtmlSchema: FastifySchema = {
|
||||||
|
description: "Parse the page and get raw HTML from the engine",
|
||||||
|
querystring: parseQuerySchema,
|
||||||
|
produces: ["text/html"],
|
||||||
|
};
|
||||||
|
|
||||||
|
export type EngineRequest = FastifyRequest<{
|
||||||
|
Querystring: {
|
||||||
|
url: string;
|
||||||
|
engine?: string;
|
||||||
|
};
|
||||||
|
}>;
|
48
src/types/requests/browser.ts
Normal file
48
src/types/requests/browser.ts
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
import { FastifyRequest, FastifySchema } from "fastify";
|
||||||
|
import { engineList } from "../../handlers/main";
|
||||||
|
|
||||||
|
export type GetRequest = FastifyRequest<{
|
||||||
|
Querystring: IGetQuery;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export interface IGetQuery {
|
||||||
|
url: string;
|
||||||
|
format?: string;
|
||||||
|
engine?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IGetSchema {
|
||||||
|
Querystring: IGetQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const indexSchema = {
|
||||||
|
produces: ["text/html"],
|
||||||
|
hide: true
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getQuerySchema = {
|
||||||
|
type: "object",
|
||||||
|
required: ["url"],
|
||||||
|
properties: {
|
||||||
|
url: {
|
||||||
|
type: "string",
|
||||||
|
description: "URL",
|
||||||
|
},
|
||||||
|
format: {
|
||||||
|
type: "string",
|
||||||
|
enum: ["text", "html", ""],
|
||||||
|
default: "html",
|
||||||
|
},
|
||||||
|
engine: {
|
||||||
|
type: "string",
|
||||||
|
enum: [...engineList, ""],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const GetSchema: FastifySchema = {
|
||||||
|
description: "Get page",
|
||||||
|
hide: true,
|
||||||
|
querystring: getQuerySchema,
|
||||||
|
produces: ["text/html", "text/plain"],
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user