From 2ee5f0512e3a6d52c0a60ed60d79e48ca665bb1d Mon Sep 17 00:00:00 2001 From: creations Date: Sat, 26 Apr 2025 11:10:31 -0400 Subject: [PATCH] move to raw html, make readme use buns html rewrite and always set to lazy image load --- README.md | 2 +- package.json | 3 --- src/helpers/ejs.ts | 26 ------------------------- src/routes/[id].ts | 30 ++++++++++++++++++----------- src/routes/api/readme.ts | 29 ++++++++++++---------------- src/views/{index.ejs => index.html} | 2 +- types/ejs.d.ts | 3 --- 7 files changed, 33 insertions(+), 62 deletions(-) delete mode 100644 src/helpers/ejs.ts rename src/views/{index.ejs => index.html} (92%) delete mode 100644 types/ejs.d.ts diff --git a/README.md b/README.md index 3ae1241..6aefc7b 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Discord Profile Page -A cool little web app that shows your Discord profile, current activity, and more. Built with Bun and EJS. +A cool little web app that shows your Discord profile, current activity, and more. Built with Bun. --- diff --git a/package.json b/package.json index 7395618..4445ea8 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,6 @@ "devDependencies": { "@biomejs/biome": "^1.9.4", "@types/bun": "^1.2.8", - "@types/ejs": "^3.1.5", "globals": "^16.0.0" }, "peerDependencies": { @@ -20,9 +19,7 @@ }, "dependencies": { "@creations.works/logger": "^1.0.3", - "ejs": "^3.1.10", "isomorphic-dompurify": "^2.23.0", - "linkedom": "^0.18.9", "marked": "^15.0.7" } } diff --git a/src/helpers/ejs.ts b/src/helpers/ejs.ts deleted file mode 100644 index 6544009..0000000 --- a/src/helpers/ejs.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { resolve } from "node:path"; -import { renderFile } from "ejs"; - -export async function renderEjsTemplate( - viewName: string | string[], - data: EjsTemplateData, - headers?: Record, -): Promise { - let templatePath: string; - - if (Array.isArray(viewName)) { - templatePath = resolve("src", "views", ...viewName); - } else { - templatePath = resolve("src", "views", viewName); - } - - if (!templatePath.endsWith(".ejs")) { - templatePath += ".ejs"; - } - - const html: string = await renderFile(templatePath, data); - - return new Response(html, { - headers: { "Content-Type": "text/html", ...headers }, - }); -} diff --git a/src/routes/[id].ts b/src/routes/[id].ts index 201d0a4..13b4920 100644 --- a/src/routes/[id].ts +++ b/src/routes/[id].ts @@ -1,5 +1,6 @@ +import { resolve } from "node:path"; import { badgeApi, lanyardConfig } from "@config/environment"; -import { renderEjsTemplate } from "@helpers/ejs"; +import { file } from "bun"; const routeDef: RouteDef = { method: "GET", @@ -13,17 +14,24 @@ async function handler(request: ExtendedRequest): Promise { .replace(/^https?:\/\//, "") .replace(/\/$/, ""); - const ejsTemplateData: EjsTemplateData = { - title: "Discord Profile", - username: "", - user: { id: id || lanyardConfig.userId }, - instance: instance, - badgeApi: badgeApi, - avatar: `https://cdn.discordapp.com/embed/avatars/${Math.floor(Math.random() * 5)}.png`, - extraOptions: {}, - }; + const path = resolve("src", "views", "index.html"); + const bunFile = file(path); - return await renderEjsTemplate("index", ejsTemplateData); + const html = new HTMLRewriter() + .on("head", { + element(head) { + head.setAttribute("data-user-id", id || lanyardConfig.userId); + head.setAttribute("data-instance-uri", instance); + head.setAttribute("data-badge-url", badgeApi || ""); + }, + }) + .transform(await bunFile.text()); + + return new Response(html, { + headers: { + "Content-Type": "text/html", + }, + }); } export { handler, routeDef }; diff --git a/src/routes/api/readme.ts b/src/routes/api/readme.ts index a5a58dd..8c2de06 100644 --- a/src/routes/api/readme.ts +++ b/src/routes/api/readme.ts @@ -1,8 +1,6 @@ import { redisTtl } from "@config/environment"; import { fetch } from "bun"; import { redis } from "bun"; -import DOMPurify from "isomorphic-dompurify"; -import { parseHTML } from "linkedom"; import { marked } from "marked"; const routeDef: RouteDef = { @@ -12,6 +10,16 @@ const routeDef: RouteDef = { log: false, }; +async function addLazyLoading(html: string): Promise { + return new HTMLRewriter() + .on("img", { + element(el) { + el.setAttribute("loading", "lazy"); + }, + }) + .transform(html); +} + async function fetchAndCacheReadme(url: string): Promise { const cacheKey = `readme:${url}`; const cached = await redis.get(cacheKey); @@ -33,22 +41,9 @@ async function fetchAndCacheReadme(url: string): Promise { const text = await res.text(); if (!text || text.length < 10) return null; - let html: string; - if (/\.(html?|htm)$/i.test(url)) { - html = text; - } else { - html = await marked.parse(text); - } + const html = /\.(html?|htm)$/i.test(url) ? text : await marked.parse(text); - const { document } = parseHTML(html); - for (const img of Array.from(document.querySelectorAll("img"))) { - if (!img.hasAttribute("loading")) { - img.setAttribute("loading", "lazy"); - } - } - - const dirtyHtml = document.toString(); - const safe = DOMPurify.sanitize(dirtyHtml) || ""; + const safe = await addLazyLoading(html); await redis.set(cacheKey, safe); await redis.expire(cacheKey, redisTtl); diff --git a/src/views/index.ejs b/src/views/index.html similarity index 92% rename from src/views/index.ejs rename to src/views/index.html index cb59ff0..3a3ac84 100644 --- a/src/views/index.ejs +++ b/src/views/index.html @@ -1,7 +1,7 @@ - + diff --git a/types/ejs.d.ts b/types/ejs.d.ts deleted file mode 100644 index 486a4a4..0000000 --- a/types/ejs.d.ts +++ /dev/null @@ -1,3 +0,0 @@ -interface EjsTemplateData { - [key: string]: string | number | boolean | object | undefined | null; -}