add css kv var, move away from ssr ( multiple queries ), remove colors kv var, add option to disable logging per route

This commit is contained in:
creations 2025-04-25 21:20:08 -04:00
parent bd680ab607
commit 3b6c68c25d
Signed by: creations
GPG key ID: 8F553AA4320FC711
18 changed files with 571 additions and 667 deletions

View file

@ -1,7 +1,5 @@
import { getImageColors } from "@/helpers/colors";
import { badgeApi, lanyardConfig } from "@config/environment";
import { renderEjsTemplate } from "@helpers/ejs";
import { getLanyardData, handleReadMe } from "@helpers/lanyard";
const routeDef: RouteDef = {
method: "GET",
@ -11,67 +9,18 @@ const routeDef: RouteDef = {
async function handler(request: ExtendedRequest): Promise<Response> {
const { id } = request.params;
const data: LanyardResponse = await getLanyardData(
id || lanyardConfig.userId,
);
if (!data.success) {
return await renderEjsTemplate("error", {
message: data.error.message,
});
}
let instance: string = lanyardConfig.instance;
if (instance.endsWith("/")) {
instance = instance.slice(0, -1);
}
if (instance.startsWith("http://") || instance.startsWith("https://")) {
instance = instance.slice(instance.indexOf("://") + 3);
}
const presence: LanyardData = data.data;
const readme: string | Promise<string> | null = await handleReadMe(presence);
let status: string;
if (presence.activities.some((activity) => activity.type === 1)) {
status = "streaming";
} else {
status = presence.discord_status;
}
const avatar: string = presence.discord_user.avatar
? `https://cdn.discordapp.com/avatars/${presence.discord_user.id}/${presence.discord_user.avatar}`
: `https://cdn.discordapp.com/embed/avatars/${Math.floor(Math.random() * 5)}.png`;
let colors: ImageColorResult | null = null;
if (presence.kv.colors === "true") {
colors = await getImageColors(avatar, true);
}
const instance = lanyardConfig.instance
.replace(/^https?:\/\//, "")
.replace(/\/$/, "");
const ejsTemplateData: EjsTemplateData = {
title: presence.discord_user.global_name || presence.discord_user.username,
username:
presence.discord_user.global_name || presence.discord_user.username,
status: status,
activities: presence.activities,
user: presence.discord_user,
platform: {
desktop: presence.active_on_discord_desktop,
mobile: presence.active_on_discord_mobile,
web: presence.active_on_discord_web,
},
title: "Discord Profile",
username: "",
user: { id: id || lanyardConfig.userId },
instance: instance,
readme: readme,
badgeApi: presence.kv.badges !== "false" ? badgeApi : null,
avatar: avatar,
colors: colors?.colors ?? {},
extraOptions: {
snow: presence.kv.snow === "true",
rain: presence.kv.rain === "true",
stars: presence.kv.stars === "true",
},
badgeApi: badgeApi,
avatar: `https://cdn.discordapp.com/embed/avatars/${Math.floor(Math.random() * 5)}.png`,
extraOptions: {},
};
return await renderEjsTemplate("index", ejsTemplateData);

View file

@ -1,38 +0,0 @@
import { getImageColors } from "@helpers/colors";
const routeDef: RouteDef = {
method: "GET",
accepts: "*/*",
returns: "application/json",
};
async function handler(request: ExtendedRequest): Promise<Response> {
const { url } = request.query;
const result: ImageColorResult | null = await getImageColors(url, true);
await getImageColors(url);
if (!result) {
return new Response("Invalid URL", {
status: 400,
headers: {
"Content-Type": "application/json",
"Cache-Control": "no-store",
"Access-Control-Allow-Origin": "*",
},
});
}
const compressed: Uint8Array = Bun.gzipSync(JSON.stringify(result));
return new Response(compressed, {
headers: {
"Content-Type": "application/json",
"Content-Encoding": "gzip",
"Cache-Control": "public, max-age=31536000, immutable",
"Access-Control-Allow-Origin": "*",
},
});
}
export { handler, routeDef };

90
src/routes/api/css.ts Normal file
View file

@ -0,0 +1,90 @@
import { fetch } from "bun";
const routeDef: RouteDef = {
method: "GET",
accepts: "*/*",
returns: "*/*",
log: false,
};
async function handler(request: ExtendedRequest): Promise<Response> {
const { url } = request.query;
if (!url || !url.startsWith("http") || !/\.css$/i.test(url)) {
return Response.json(
{
success: false,
error: {
code: "INVALID_URL",
message: "Invalid URL provided",
},
},
{ status: 400 },
);
}
const res = await fetch(url, {
headers: {
Accept: "text/css",
},
});
if (!res.ok) {
return Response.json(
{
success: false,
error: {
code: "FETCH_FAILED",
message: "Failed to fetch CSS file",
},
},
{ status: 400 },
);
}
if (res.headers.has("content-length")) {
const size = Number.parseInt(res.headers.get("content-length") || "0", 10);
if (size > 1024 * 50) {
return Response.json(
{
success: false,
error: {
code: "FILE_TOO_LARGE",
message: "CSS file exceeds 50KB limit",
},
},
{ status: 400 },
);
}
}
const text = await res.text();
if (!text || text.length < 5) {
return Response.json(
{
success: false,
error: {
code: "INVALID_CONTENT",
message: "CSS content is too small or invalid",
},
},
{ status: 400 },
);
}
const sanitized = text
.replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, "")
.replace(/@import\s+url\(['"]?(.*?)['"]?\);?/gi, "");
return new Response(sanitized, {
headers: {
"Content-Type": "text/css",
"Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate",
Pragma: "no-cache",
Expires: "0",
},
status: 200,
});
}
export { handler, routeDef };

105
src/routes/api/readme.ts Normal file
View file

@ -0,0 +1,105 @@
import { fetch } from "bun";
import DOMPurify from "isomorphic-dompurify";
import { marked } from "marked";
const routeDef: RouteDef = {
method: "GET",
accepts: "*/*",
returns: "*/*",
log: false,
};
async function handler(request: ExtendedRequest): Promise<Response> {
const { url } = request.query;
if (
!url ||
!url.startsWith("http") ||
!/\.(md|markdown|txt|html?)$/i.test(url)
) {
return Response.json(
{
success: false,
error: {
code: "INVALID_URL",
message: "Invalid URL provided",
},
},
{ status: 400 },
);
}
const res = await fetch(url, {
headers: {
Accept: "text/markdown",
},
});
if (!res.ok) {
return Response.json(
{
success: false,
error: {
code: "FETCH_FAILED",
message: "Failed to fetch the file",
},
},
{ status: 400 },
);
}
if (res.headers.has("content-length")) {
const size = Number.parseInt(res.headers.get("content-length") || "0", 10);
if (size > 1024 * 100) {
return Response.json(
{
success: false,
error: {
code: "FILE_TOO_LARGE",
message: "File size exceeds 100KB limit",
},
},
{ status: 400 },
);
}
}
const text = await res.text();
if (!text || text.length < 10) {
return Response.json(
{
success: false,
error: {
code: "INVALID_CONTENT",
message: "File is too small or invalid",
},
},
{ status: 400 },
);
}
let html: string;
if (
url.toLowerCase().endsWith(".html") ||
url.toLowerCase().endsWith(".htm")
) {
html = text;
} else {
html = await marked.parse(text);
}
const safe = DOMPurify.sanitize(html) || "";
return new Response(safe, {
headers: {
"Content-Type": "text/html; charset=utf-8",
"Cache-Control": "no-store, no-cache, must-revalidate, proxy-revalidate",
Pragma: "no-cache",
Expires: "0",
},
status: 200,
});
}
export { handler, routeDef };