GET parameters, rendering HTML with ejs
This commit is contained in:
parent
0b4663195c
commit
94f14d31ab
96
package-lock.json
generated
96
package-lock.json
generated
@ -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"
|
||||
|
@ -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",
|
||||
|
25
src/app.ts
25
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")) }, () => {
|
||||
|
@ -1,18 +1,28 @@
|
||||
import { IHandlerOutput } from "./handler.interface";
|
||||
import { readability } from "./readability";
|
||||
|
||||
export default function handlePage(url: string): Promise<IHandlerOutput> {
|
||||
const host = new URL(url).hostname;
|
||||
return fallback[host]?.(url) || fallback["*"](url);
|
||||
export default function handlePage(url: string, engine: string): Promise<IHandlerOutput> {
|
||||
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<IHandlerOutput>;
|
||||
}
|
||||
|
||||
/*
|
||||
const fallback: Engines = {
|
||||
"*": engines.readability,
|
||||
};
|
||||
interface Engines {
|
||||
[host: string]: (url: string) => Promise<IHandlerOutput>;
|
||||
}
|
||||
*/
|
||||
|
@ -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;
|
||||
});
|
||||
};
|
||||
}
|
47
src/routes/main-route.ts
Normal file
47
src/routes/main-route.ts
Normal file
@ -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 });
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
12
templates/index.ejs
Normal file
12
templates/index.ejs
Normal file
@ -0,0 +1,12 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="<%= parsed.lang %>">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><%= parsed.title %></title>
|
||||
</head>
|
||||
<body>
|
||||
<h1><%= parsed.title %></h1>
|
||||
<main><%- parsed.content %></main>
|
||||
</body>
|
||||
</html>
|
Loading…
x
Reference in New Issue
Block a user