import express from "express"; import { Readability, isProbablyReaderable } from "@mozilla/readability"; import got from "got"; import path from "path"; import { fileURLToPath } from "url"; import "dotenv/config"; import { parseHTML, parseJSON } from "linkedom"; // @ts-ignore import XHR2 from "xhr2"; const XMLHttpRequest = XHR2.XMLHttpRequest; import { minify } from "html-minifier"; import { blazeUrl, injectBlazeToPageLinks } from "./utils.js"; const app = express(); const port = 8888; const minifierOptions = { collapseWhitespace: true, removeComments: true, removeOptionalTags: true, removeRedundantAttributes: true, removeScriptTypeAttributes: true, removeTagWhitespace: true, useShortDoctype: true, minifyCSS: true, }; // @ts-ignore const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); app.get("/", async (req, res) => { const searchEngine = "https://api.search.brave.com/res/v1/web/search"; const query = req.query.q as string; if (!query) { return res.sendFile(path.join(__dirname, "/dist/index.html")); } const key = process.env.CYCLIC_BRAVE_KEY; if (!key) { throw new Error("No brave key found"); } try { const xhr = new XMLHttpRequest(); xhr.open("GET", `${searchEngine}?q=${query}&safesearch=moderate`, true); xhr.setRequestHeader("Accept", "*/*"); xhr.setRequestHeader("X-Subscription-Token", key); xhr.onreadystatechange = () => { if (xhr.readyState !== 4) { return; } if (xhr.status !== 200) { console.error("XHR request failed:", xhr.status, xhr.statusText); return; } const data = JSON.parse(xhr.responseText); // @ts-ignore const results = data.web.results.map( (result: any) => `

${result.title}

${result.meta_url.hostname}

${result.description}


` ); const html = ` Blaze - ${query} ${results.join("")} `; const minifiedSerp = minify(html, minifierOptions); res.send(minifiedSerp); }; xhr.send(); } catch (err) { console.error(err); } }); app.get("/blazed", async (req, res) => { const pageToBlaze = req.query.url as string; try { const response = await got(pageToBlaze, { headers: { Accept: "text/html" }, }); const { document } = parseHTML(response.body); // TODO: missing handling of 404. The idea is to send the blaze 404 page, otherwise will show error page on client if (!isProbablyReaderable(document)) { // TODO: send minimalized version of the page instead the read mode // implementation draft: // document.querySelectorAll("link").forEach((l) => { // l.remove(); // }); // document.querySelectorAll("style").forEach((s) => { // s.remove; // }); // document.querySelectorAll("script").forEach((s) => { // s.remove(); // }); // // @ts-ignore // const jsonDocument = document.toJSON(); // const cleanDocument = parseJSON(jsonDocument); // return res.send(document.toString()); return res.sendFile(path.join(__dirname, "/dist/not_blazed.html")); } //TODO: find if there are more performant ways to remove images or evaluate if is the case to remove images document.querySelectorAll("img").forEach((img) => img.remove()); const reader = new Readability(document); const article = reader.parse(); if (!article) { return res.send("Something went wrong"); } const blazedPage = ` ${article.content} `; const minifiedBlazedPage = minify(blazedPage, minifierOptions); res.send(minifiedBlazedPage); } catch (err) { console.log(err); } }); app.get("/info", (_, res) => { res.sendFile(path.join(__dirname + "/dist/info.html")); }); app.get("/ooops", (_, res) => { res.sendFile(path.join(__dirname + "/dist/info_not_blazed.html")); }); app.get("/favicon.svg", (_, res) => { res.sendFile(path.join(__dirname + "/favicon.svg")); }); app.listen(port, () => { console.log(`Got request`); });