Merge pull request #6 from TxtDot/new-routing

GET parameters, rendering HTML with ejs
This commit is contained in:
Andrey 2023-08-14 16:59:10 +04:00 committed by GitHub
commit f965098d37
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 178 additions and 75 deletions

96
package-lock.json generated
View File

@ -10,14 +10,17 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@fastify/middie": "^8.3.0", "@fastify/middie": "^8.3.0",
"@fastify/view": "^8.0.0",
"@mozilla/readability": "^0.4.4", "@mozilla/readability": "^0.4.4",
"axios": "^1.4.0", "axios": "^1.4.0",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"ejs": "^3.1.9",
"fastify": "^4.21.0", "fastify": "^4.21.0",
"jsdom": "^22.1.0", "jsdom": "^22.1.0",
"node-cache": "^5.1.2" "node-cache": "^5.1.2"
}, },
"devDependencies": { "devDependencies": {
"@types/ejs": "^3.1.2",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/jsdom": "^21.1.1", "@types/jsdom": "^21.1.1",
"@types/node": "^20.4.10", "@types/node": "^20.4.10",
@ -158,6 +161,15 @@
"reusify": "^1.0.4" "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": { "node_modules/@humanwhocodes/config-array": {
"version": "0.11.10", "version": "0.11.10",
"dev": true, "dev": true,
@ -297,6 +309,12 @@
"@types/node": "*" "@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": { "node_modules/@types/express": {
"version": "4.17.17", "version": "4.17.17",
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz", "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.17.tgz",
@ -703,7 +721,6 @@
}, },
"node_modules/ansi-styles": { "node_modules/ansi-styles": {
"version": "4.3.0", "version": "4.3.0",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"color-convert": "^2.0.1" "color-convert": "^2.0.1"
@ -738,6 +755,11 @@
"node": ">=8" "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": { "node_modules/asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@ -773,7 +795,6 @@
}, },
"node_modules/balanced-match": { "node_modules/balanced-match": {
"version": "1.0.2", "version": "1.0.2",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/base64-js": { "node_modules/base64-js": {
@ -797,7 +818,6 @@
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
"version": "1.1.11", "version": "1.1.11",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"balanced-match": "^1.0.0", "balanced-match": "^1.0.0",
@ -848,7 +868,6 @@
}, },
"node_modules/chalk": { "node_modules/chalk": {
"version": "4.1.2", "version": "4.1.2",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ansi-styles": "^4.1.0", "ansi-styles": "^4.1.0",
@ -871,7 +890,6 @@
}, },
"node_modules/color-convert": { "node_modules/color-convert": {
"version": "2.0.1", "version": "2.0.1",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"color-name": "~1.1.4" "color-name": "~1.1.4"
@ -882,7 +900,6 @@
}, },
"node_modules/color-name": { "node_modules/color-name": {
"version": "1.1.4", "version": "1.1.4",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/combined-stream": { "node_modules/combined-stream": {
@ -898,7 +915,6 @@
}, },
"node_modules/concat-map": { "node_modules/concat-map": {
"version": "0.0.1", "version": "0.0.1",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/cookie": { "node_modules/cookie": {
@ -1036,6 +1052,20 @@
"url": "https://github.com/motdotla/dotenv?sponsor=1" "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": { "node_modules/entities": {
"version": "4.5.0", "version": "4.5.0",
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
@ -1358,6 +1388,33 @@
"node": "^10.12.0 || >=12.0.0" "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": { "node_modules/fill-range": {
"version": "7.0.1", "version": "7.0.1",
"dev": true, "dev": true,
@ -1529,12 +1586,16 @@
}, },
"node_modules/has-flag": { "node_modules/has-flag": {
"version": "4.0.0", "version": "4.0.0",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=8" "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": { "node_modules/html-encoding-sniffer": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
@ -1688,6 +1749,23 @@
"dev": true, "dev": true,
"license": "ISC" "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": { "node_modules/js-yaml": {
"version": "4.1.0", "version": "4.1.0",
"dev": true, "dev": true,
@ -1847,7 +1925,6 @@
}, },
"node_modules/minimatch": { "node_modules/minimatch": {
"version": "3.1.2", "version": "3.1.2",
"dev": true,
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"brace-expansion": "^1.1.7" "brace-expansion": "^1.1.7"
@ -2382,7 +2459,6 @@
}, },
"node_modules/supports-color": { "node_modules/supports-color": {
"version": "7.2.0", "version": "7.2.0",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"has-flag": "^4.0.0" "has-flag": "^4.0.0"

View File

@ -6,14 +6,17 @@
"main": "dist/app.js", "main": "dist/app.js",
"dependencies": { "dependencies": {
"@fastify/middie": "^8.3.0", "@fastify/middie": "^8.3.0",
"@fastify/view": "^8.0.0",
"@mozilla/readability": "^0.4.4", "@mozilla/readability": "^0.4.4",
"axios": "^1.4.0", "axios": "^1.4.0",
"dotenv": "^16.3.1", "dotenv": "^16.3.1",
"ejs": "^3.1.9",
"fastify": "^4.21.0", "fastify": "^4.21.0",
"jsdom": "^22.1.0", "jsdom": "^22.1.0",
"node-cache": "^5.1.2" "node-cache": "^5.1.2"
}, },
"devDependencies": { "devDependencies": {
"@types/ejs": "^3.1.2",
"@types/express": "^4.17.17", "@types/express": "^4.17.17",
"@types/jsdom": "^21.1.1", "@types/jsdom": "^21.1.1",
"@types/node": "^20.4.10", "@types/node": "^20.4.10",

View File

@ -1,13 +1,14 @@
import { IConfigService } from "./config/config.interface"; import { IConfigService } from "./config/config.interface";
import { ConfigService } from "./config/config.service"; import { ConfigService } from "./config/config.service";
import { Cached } from "./types/requests";
import NodeCache from "node-cache"; import NodeCache from "node-cache";
import Fastify from "fastify"; import Fastify from "fastify";
import middie from "@fastify/middie"; 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"; import parseRoute from "./routes/parseRoute";
class App { class App {
@ -25,25 +26,13 @@ class App {
}); });
await fastify.register(middie); await fastify.register(middie);
await fastify.register(fastifyView, {
fastify.use((req, res, next) => { engine: {
const url = req.originalUrl || req.url || "/"; ejs: ejs,
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();
} }
}); });
fastify.register(getRoute(this.cache)); fastify.register(mainRoute(this.cache));
fastify.register(parseRoute(this.cache)); fastify.register(parseRoute(this.cache));
fastify.listen({ port: Number(this.config.get("PORT")) }, () => { fastify.listen({ port: Number(this.config.get("PORT")) }, () => {

View File

@ -1,25 +1,27 @@
import { IHandlerOutput } from "./handler.interface"; import { IHandlerOutput } from "./handler.interface";
import { readability } from "./readability"; import { readability } from "./readability";
export default function handlePage( type EngineFunction = (url: string) => Promise<IHandlerOutput>
url: string,
engine?: string export default function handlePage(url: string, engine?: string): Promise<IHandlerOutput> {
): Promise<IHandlerOutput> { let func: EngineFunction | undefined = engines[engine || ""];
if (engine) {
return engines[engine](url); if (func === undefined) {
const host = new URL(url).hostname;
func = fallback[host] || fallback["*"];
} }
const host = new URL(url).hostname; return func(url);
return fallback[host]?.(url) || fallback["*"](url); }
interface Engines {
[key: string]: EngineFunction;
} }
export const engines: Engines = { export const engines: Engines = {
readability, "readability": readability,
}; };
const fallback: Engines = { const fallback: Engines = {
"*": engines.readability, "*": engines.readability,
}; };
interface Engines {
[host: string]: (url: string) => Promise<IHandlerOutput>;
}

View File

@ -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
View 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 });
}
});
};
}

View File

@ -2,18 +2,20 @@ import { FastifyInstance, FastifyRequest } from "fastify";
import NodeCache from "node-cache"; import NodeCache from "node-cache";
export type GetRequest = FastifyRequest<{ export type GetRequest = FastifyRequest<{
Querystring: { url: string; type?: string }; Querystring: {
url: string;
format?: string;
engine?: string;
};
}>; }>;
export type EngineRequest = FastifyRequest<{ 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 { export interface IFastifyInstance extends FastifyInstance {
cache: NodeCache; cache: NodeCache;
} }

12
templates/index.ejs Normal file
View 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>