diff --git a/package-lock.json b/package-lock.json index a5787c8..d2485dc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,14 +10,17 @@ "license": "ISC", "dependencies": { "@fastify/middie": "^8.3.0", + "@fastify/view": "^8.0.0", "@mozilla/readability": "^0.4.4", "axios": "^1.4.0", "dotenv": "^16.3.1", + "ejs": "^3.1.9", "fastify": "^4.21.0", "jsdom": "^22.1.0", "node-cache": "^5.1.2" }, "devDependencies": { + "@types/ejs": "^3.1.2", "@types/express": "^4.17.17", "@types/jsdom": "^21.1.1", "@types/node": "^20.4.10", @@ -158,6 +161,15 @@ "reusify": "^1.0.4" } }, + "node_modules/@fastify/view": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@fastify/view/-/view-8.0.0.tgz", + "integrity": "sha512-XfAffgqRj+AtEtkZeAAkMwTtu32Ve6xWkhxWQ9JOwXm2qQM6Fj+xphxnLvqpvQ0hJAYFYGiTOpB5ZS2VI5u00Q==", + "dependencies": { + "fastify-plugin": "^4.0.0", + "hashlru": "^2.3.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.10", "dev": true, @@ -297,6 +309,12 @@ "@types/node": "*" } }, + "node_modules/@types/ejs": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/ejs/-/ejs-3.1.2.tgz", + "integrity": "sha512-ZmiaE3wglXVWBM9fyVC17aGPkLo/UgaOjEiI2FXQfyczrCefORPxIe+2dVmnmk3zkVIbizjrlQzmPGhSYGXG5g==", + "dev": true + }, "node_modules/@types/express": { "version": "4.17.17", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", @@ -703,7 +721,6 @@ }, "node_modules/ansi-styles": { "version": "4.3.0", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -738,6 +755,11 @@ "node": ">=8" } }, + "node_modules/async": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz", + "integrity": "sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -773,7 +795,6 @@ }, "node_modules/balanced-match": { "version": "1.0.2", - "dev": true, "license": "MIT" }, "node_modules/base64-js": { @@ -797,7 +818,6 @@ }, "node_modules/brace-expansion": { "version": "1.1.11", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", @@ -848,7 +868,6 @@ }, "node_modules/chalk": { "version": "4.1.2", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -871,7 +890,6 @@ }, "node_modules/color-convert": { "version": "2.0.1", - "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -882,7 +900,6 @@ }, "node_modules/color-name": { "version": "1.1.4", - "dev": true, "license": "MIT" }, "node_modules/combined-stream": { @@ -898,7 +915,6 @@ }, "node_modules/concat-map": { "version": "0.0.1", - "dev": true, "license": "MIT" }, "node_modules/cookie": { @@ -1036,6 +1052,20 @@ "url": "https://github.com/motdotla/dotenv?sponsor=1" } }, + "node_modules/ejs": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.9.tgz", + "integrity": "sha512-rC+QVNMJWv+MtPgkt0y+0rVEIdbtxVADApW9JXrUVlzHetgcyczP/E7DJmWJ4fJCZF2cPcBk0laWO9ZHMG3DmQ==", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", @@ -1358,6 +1388,33 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.0.1", "dev": true, @@ -1529,12 +1586,16 @@ }, "node_modules/has-flag": { "version": "4.0.0", - "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, + "node_modules/hashlru": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/hashlru/-/hashlru-2.3.0.tgz", + "integrity": "sha512-0cMsjjIC8I+D3M44pOQdsy0OHXGLVz6Z0beRuufhKa0KfaD2wGwAev6jILzXsd3/vpnNQJmWyZtIILqM1N+n5A==" + }, "node_modules/html-encoding-sniffer": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", @@ -1688,6 +1749,23 @@ "dev": true, "license": "ISC" }, + "node_modules/jake": { + "version": "10.8.7", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.8.7.tgz", + "integrity": "sha512-ZDi3aP+fG/LchyBzUM804VjddnwfSfsdeYkwt8NcbKRvo4rFkjhs456iLFn3k2ZUWvNe4i48WACDbza8fhq2+w==", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "dev": true, @@ -1847,7 +1925,6 @@ }, "node_modules/minimatch": { "version": "3.1.2", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -2382,7 +2459,6 @@ }, "node_modules/supports-color": { "version": "7.2.0", - "dev": true, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" diff --git a/package.json b/package.json index e4722ea..929d41d 100644 --- a/package.json +++ b/package.json @@ -6,14 +6,17 @@ "main": "dist/app.js", "dependencies": { "@fastify/middie": "^8.3.0", + "@fastify/view": "^8.0.0", "@mozilla/readability": "^0.4.4", "axios": "^1.4.0", "dotenv": "^16.3.1", + "ejs": "^3.1.9", "fastify": "^4.21.0", "jsdom": "^22.1.0", "node-cache": "^5.1.2" }, "devDependencies": { + "@types/ejs": "^3.1.2", "@types/express": "^4.17.17", "@types/jsdom": "^21.1.1", "@types/node": "^20.4.10", diff --git a/src/app.ts b/src/app.ts index 49bbae8..0e3efa7 100644 --- a/src/app.ts +++ b/src/app.ts @@ -1,13 +1,14 @@ import { IConfigService } from "./config/config.interface"; import { ConfigService } from "./config/config.service"; -import { Cached } from "./types/requests"; import NodeCache from "node-cache"; import Fastify from "fastify"; import middie from "@fastify/middie"; +import fastifyView from "@fastify/view"; +import ejs from "ejs"; -import getRoute from "./routes/getRoute"; +import mainRoute from "./routes/main-route"; import parseRoute from "./routes/parseRoute"; class App { @@ -25,25 +26,13 @@ class App { }); await fastify.register(middie); - - fastify.use((req, res, next) => { - const url = req.originalUrl || req.url || "/"; - - if (req.query.purge) { - this.cache.del(url); - next(); - } - - const cached: Cached | undefined = this.cache.get(url); - if (cached) { - res.setHeader("content-type", `${cached.contentType}; charset=utf-8`); - res.end(cached.content); - } else { - next(); + await fastify.register(fastifyView, { + engine: { + ejs: ejs, } }); - fastify.register(getRoute(this.cache)); + fastify.register(mainRoute(this.cache)); fastify.register(parseRoute(this.cache)); fastify.listen({ port: Number(this.config.get("PORT")) }, () => { diff --git a/src/handlers/main.ts b/src/handlers/main.ts index d0923ee..21bec92 100644 --- a/src/handlers/main.ts +++ b/src/handlers/main.ts @@ -1,18 +1,28 @@ import { IHandlerOutput } from "./handler.interface"; import { readability } from "./readability"; -export default function handlePage(url: string): Promise { - const host = new URL(url).hostname; - return fallback[host]?.(url) || fallback["*"](url); +export default function handlePage(url: string, engine: string): Promise { + const func = engines[engine]; + if (!func) { + throw new Error('No such engine') + } + + return func(url) } export const engines: Engines = { - readability, + "readability": readability, }; +interface Engines { + [name: string]: (url: string) => Promise; +} + +/* const fallback: Engines = { "*": engines.readability, }; interface Engines { [host: string]: (url: string) => Promise; } +*/ diff --git a/src/routes/getRoute.ts b/src/routes/getRoute.ts deleted file mode 100644 index 832865c..0000000 --- a/src/routes/getRoute.ts +++ /dev/null @@ -1,28 +0,0 @@ -import NodeCache from "node-cache"; -import handlePage from "../handlers/main"; -import { GetRequest } from "../types/requests"; -import { FastifyInstance } from "fastify"; - -export default function getRoute(cache: NodeCache) { - return async (fastify: FastifyInstance) => { - fastify.get("/get", async (req: GetRequest, res) => { - const url = req.query.url; - const type = req.query.type || "html"; - const contentType = - type === "html" - ? "text/html; charset=utf-8" - : "text/plain; charset=utf-8"; - - const parsed = await handlePage(url); - const content = type === "html" ? parsed?.content : parsed?.textContent; - - cache.set(req.originalUrl || req.url, { - content, - contentType: contentType, - }); - - res.type(contentType); - return content; - }); - }; -} diff --git a/src/routes/main-route.ts b/src/routes/main-route.ts new file mode 100644 index 0000000..b02e732 --- /dev/null +++ b/src/routes/main-route.ts @@ -0,0 +1,47 @@ +import { FastifyInstance } from "fastify"; +import NodeCache from "node-cache"; + +import { GetRequest } from "../types/requests"; +import { IHandlerOutput } from "../handlers/handler.interface"; +import handlePage from "../handlers/main"; + +export default function mainRoute(cache: NodeCache) { + return async (fastify: FastifyInstance) => { + fastify.get("/", async (req: GetRequest, res) => { + const remoteUrl = req.query.url; + const engine = req.query.engine || "readability"; + + let format: string; + + if (req.query.format === "text") { + res.type("text/plain; charset=utf-8"); + format = "text"; + } + else { + res.type("text/html; charset=utf-8"); + format = "html"; + } + + const cacheKey = req.originalUrl || req.url; + const cached: string | undefined = cache.get(cacheKey); + + if (cached) { + res.send(cached); + return; + } + + const parsed = await handlePage(remoteUrl, engine); + + if (format === "text") { + const content = parsed.textContent; + cache.set(cacheKey, content); + res.send(content); + } + else { + const content = parsed.content; + cache.set(cacheKey, content); + res.view("/template/index.ejs", { parsed: parsed }); + } + }); + }; +} diff --git a/src/types/requests.ts b/src/types/requests.ts index b775236..f921f8d 100644 --- a/src/types/requests.ts +++ b/src/types/requests.ts @@ -2,18 +2,20 @@ import { FastifyInstance, FastifyRequest } from "fastify"; import NodeCache from "node-cache"; export type GetRequest = FastifyRequest<{ - Querystring: { url: string; type?: string }; + Querystring: { + url: string; + format?: string; + engine?: string; + }; }>; export type EngineRequest = FastifyRequest<{ - Querystring: { url: string; engine?: string }; + Querystring: { + url: string; + engine?: string; + }; }>; -export type Cached = { - content: string; - contentType: string; -}; - export interface IFastifyInstance extends FastifyInstance { cache: NodeCache; } diff --git a/templates/index.ejs b/templates/index.ejs new file mode 100644 index 0000000..9128ce0 --- /dev/null +++ b/templates/index.ejs @@ -0,0 +1,12 @@ + + + + + + <%= parsed.title %> + + +

<%= parsed.title %>

+
<%- parsed.content %>
+ +