txt.
+<%= description %>
+diff --git a/package-lock.json b/package-lock.json
index 0ea6847..c40fba8 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "txtdot",
- "version": "1.1.1",
+ "version": "1.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "txtdot",
- "version": "1.1.1",
+ "version": "1.2.0",
"license": "MIT",
"dependencies": {
"@fastify/static": "^6.10.2",
diff --git a/package.json b/package.json
index 5f4ba7d..39dd3ea 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "txtdot",
- "version": "1.1.1",
+ "version": "1.2.0",
"private": true,
"description": "",
"main": "dist/app.js",
diff --git a/src/errors/api.ts b/src/errors/api.ts
new file mode 100644
index 0000000..4d0728c
--- /dev/null
+++ b/src/errors/api.ts
@@ -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,
+ },
+};
diff --git a/src/errors/handler.ts b/src/errors/handler.ts
index 58365ae..b23560b 100644
--- a/src/errors/handler.ts
+++ b/src/errors/handler.ts
@@ -1,14 +1,66 @@
import { FastifyReply, FastifyRequest } from "fastify";
-import { NotHtmlMimetypeError } from "./main";
+import { NotHtmlMimetypeError, TxtDotError } from "./main";
+import { getFastifyError } from "./validation";
export default function errorHandler(
error: Error,
- _: FastifyRequest,
+ req: FastifyRequest,
reply: FastifyReply
) {
+ if (req.originalUrl.startsWith("/api/")) {
+ return apiErrorHandler(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) {
if (error instanceof NotHtmlMimetypeError) {
return reply.redirect(301, error.url);
- } else {
- return error;
}
+
+ if (getFastifyError(error)?.statusCode === 400) {
+ return reply.code(400).view("/templates/error.ejs", {
+ code: 400,
+ description: `Invalid parameter specified: ${error.message}`,
+ })
+ }
+
+ if (error instanceof TxtDotError) {
+ return reply.code(error.code).view("/templates/error.ejs", {
+ code: error.code,
+ description: error.description,
+ });
+ }
+
+ return reply.code(500).view("/templates/error.ejs", {
+ code: 500,
+ description: `${error.name}: ${error.message}`,
+ });
}
diff --git a/src/errors/main.ts b/src/errors/main.ts
index bd528bd..29190c9 100644
--- a/src/errors/main.ts
+++ b/src/errors/main.ts
@@ -1,10 +1,34 @@
-export class EngineParseError extends Error {}
-export class InvalidParameterError extends Error {}
-export class LocalResourceError extends Error {}
-export class NotHtmlMimetypeError extends Error {
- url: string;
- constructor(params: { url: string }) {
- super();
- this.url = params?.url;
+export abstract class TxtDotError extends Error {
+ code: number;
+ name: string;
+ description: string;
+
+ constructor(code: number, name: string, description: string) {
+ super(description);
+ this.code = code;
+ this.name = name;
+ this.description = description;
+ }
+}
+
+export class EngineParseError extends TxtDotError {
+ constructor(message: string) {
+ super(422, "EngineParseError", `Parse error: ${message}`);
+ }
+}
+
+export class LocalResourceError extends TxtDotError {
+ constructor() {
+ super(403, "LocalResourceError", "Proxying local resources is forbidden.");
+ }
+}
+
+export class NotHtmlMimetypeError extends Error {
+ name: string = "NotHtmlMimetypeError";
+ url: string;
+
+ constructor(url: string) {
+ super();
+ this.url = url;
}
}
diff --git a/src/errors/validation.ts b/src/errors/validation.ts
new file mode 100644
index 0000000..5304da8
--- /dev/null
+++ b/src/errors/validation.ts
@@ -0,0 +1,9 @@
+export interface IFastifyValidationError {
+ statusCode?: number;
+ code?: string;
+ validation?: any;
+}
+
+export function getFastifyError(error: Error) {
+ return error as unknown as IFastifyValidationError;
+}
diff --git a/src/handlers/main.ts b/src/handlers/main.ts
index 0fbb172..7a37f5b 100644
--- a/src/handlers/main.ts
+++ b/src/handlers/main.ts
@@ -6,16 +6,15 @@ import { DOMWindow } from "jsdom";
import readability from "./readability";
import google from "./google";
+import stackoverflow from "./stackoverflow/main";
import { generateProxyUrl } from "../utils/generate";
import isLocalResource from "../utils/islocal";
import {
- InvalidParameterError,
LocalResourceError,
NotHtmlMimetypeError,
} from "../errors/main";
-import stackoverflow from "./stackoverflow/main";
export default async function handlePage(
url: string, // remote URL
@@ -28,21 +27,21 @@ export default async function handlePage(
throw new LocalResourceError();
}
- if (engine && engineList.indexOf(engine) === -1) {
- throw new InvalidParameterError("Invalid engine");
- }
-
const response = await axios.get(url);
const mime: string | undefined = response.headers["content-type"]?.toString();
if (mime && mime.indexOf("text/html") === -1) {
- throw new NotHtmlMimetypeError({ url });
+ throw new NotHtmlMimetypeError(url);
}
const window = new JSDOM(response.data, { url }).window;
[...window.document.getElementsByTagName("a")].forEach((link) => {
- link.href = generateProxyUrl(requestUrl, link.href, engine);
+ try {
+ link.href = generateProxyUrl(requestUrl, link.href, engine);
+ } catch (_err) {
+ // ignore TypeError: Invalid URL
+ }
});
if (engine) {
diff --git a/src/publicConfig.ts b/src/publicConfig.ts
index bb34e36..847469e 100644
--- a/src/publicConfig.ts
+++ b/src/publicConfig.ts
@@ -1,5 +1,5 @@
export default {
- version: "1.1.0",
+ version: "1.1.1",
description:
"HTTP proxy that parses only text, links and pictures from pages reducing internet traffic, removing ads and heavy scripts",
};
diff --git a/src/routes/get.ts b/src/routes/get.ts
index dbd1e59..795b7e2 100644
--- a/src/routes/get.ts
+++ b/src/routes/get.ts
@@ -1,6 +1,6 @@
import { FastifyInstance } from "fastify";
-import { GetSchema, IGetSchema } from "../types/requests";
+import { GetSchema, IGetSchema } from "../types/requests/browser";
import handlePage from "../handlers/main";
import { generateRequestUrl } from "../utils/generate";
diff --git a/src/routes/index.ts b/src/routes/index.ts
index ea3ff2d..00d6dba 100644
--- a/src/routes/index.ts
+++ b/src/routes/index.ts
@@ -1,6 +1,6 @@
import { FastifyInstance } from "fastify";
import { engineList } from "../handlers/main";
-import { indexSchema } from "../types/requests";
+import { indexSchema } from "../types/requests/browser";
export default async function indexRoute(fastify: FastifyInstance) {
fastify.get("/", { schema: indexSchema }, async (_, reply) => {
diff --git a/src/routes/parse.ts b/src/routes/parse.ts
index 24b5738..bca7d1d 100644
--- a/src/routes/parse.ts
+++ b/src/routes/parse.ts
@@ -1,5 +1,7 @@
-import { EngineRequest, IParseSchema, parseSchema } from "../types/requests";
import { FastifyInstance } from "fastify";
+
+import { EngineRequest, IParseSchema, parseSchema } from "../types/requests/api";
+
import handlePage from "../handlers/main";
import { generateRequestUrl } from "../utils/generate";
@@ -8,15 +10,18 @@ export default async function parseRoute(fastify: FastifyInstance) {
"/api/parse",
{ schema: parseSchema },
async (request: EngineRequest) => {
- return await handlePage(
- request.query.url,
- generateRequestUrl(
- request.protocol,
- request.hostname,
- request.originalUrl
+ return {
+ data: await handlePage(
+ request.query.url,
+ generateRequestUrl(
+ request.protocol,
+ request.hostname,
+ request.originalUrl
+ ),
+ request.query.engine
),
- request.query.engine
- );
+ error: null,
+ };
}
);
}
diff --git a/src/routes/raw-html.ts b/src/routes/raw-html.ts
index 83ac62f..4665fe2 100644
--- a/src/routes/raw-html.ts
+++ b/src/routes/raw-html.ts
@@ -1,6 +1,8 @@
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 { generateRequestUrl } from "../utils/generate";
diff --git a/src/types/requests.ts b/src/types/requests.ts
deleted file mode 100644
index e395149..0000000
--- a/src/types/requests.ts
+++ /dev/null
@@ -1,99 +0,0 @@
-import { FastifyRequest, FastifySchema } from "fastify";
-import { handlerSchema } from "../handlers/handler.interface";
-import { engineList } from "../handlers/main";
-
-export type GetRequest = FastifyRequest<{
- Querystring: {
- url: string;
- format?: string;
- engine?: string;
- };
-}>;
-
-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;
- };
-}>;
diff --git a/src/types/requests/api.ts b/src/types/requests/api.ts
new file mode 100644
index 0000000..9e13803
--- /dev/null
+++ b/src/types/requests/api.ts
@@ -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 <%= description %>
<%= parsed.title %>
- txt.
+
+
<%= desc %>