first commit

This commit is contained in:
creations 2025-04-05 01:28:29 -04:00
commit b11c87a506
Signed by: creations
GPG key ID: 8F553AA4320FC711
26 changed files with 1782 additions and 0 deletions

6
src/helpers/char.ts Normal file
View file

@ -0,0 +1,6 @@
export function timestampToReadable(timestamp?: number): string {
const date: Date =
timestamp && !isNaN(timestamp) ? new Date(timestamp) : new Date();
if (isNaN(date.getTime())) return "Invalid Date";
return date.toISOString().replace("T", " ").replace("Z", "");
}

26
src/helpers/ejs.ts Normal file
View file

@ -0,0 +1,26 @@
import { renderFile } from "ejs";
import { resolve } from "path";
export async function renderEjsTemplate(
viewName: string | string[],
data: EjsTemplateData,
headers?: Record<string, string | number | boolean>,
): Promise<Response> {
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 },
});
}

46
src/helpers/lanyard.ts Normal file
View file

@ -0,0 +1,46 @@
import { lanyardConfig } from "@config/environment";
import { fetch } from "bun";
export async function getLanyardData(id?: string): Promise<LanyardResponse> {
let instance: string = lanyardConfig.instance;
if (instance.endsWith("/")) {
instance = instance.slice(0, -1);
}
if (!instance.startsWith("http://") && !instance.startsWith("https://")) {
instance = `https://${instance}`;
}
const url: string = `${instance}/v1/users/${id || lanyardConfig.userId}`;
const res: Response = await fetch(url, {
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
});
if (!res.ok) {
return {
success: false,
error: {
code: "API_ERROR",
message: `Lanyard API responded with status ${res.status}`,
},
};
}
const data: LanyardResponse = (await res.json()) as LanyardResponse;
if (!data.success) {
return {
success: false,
error: {
code: "API_ERROR",
message: "Failed to fetch valid Lanyard data",
},
};
}
return data;
}

205
src/helpers/logger.ts Normal file
View file

@ -0,0 +1,205 @@
import { environment } from "@config/environment";
import { timestampToReadable } from "@helpers/char";
import type { Stats } from "fs";
import {
createWriteStream,
existsSync,
mkdirSync,
statSync,
WriteStream,
} from "fs";
import { EOL } from "os";
import { basename, join } from "path";
class Logger {
private static instance: Logger;
private static log: string = join(__dirname, "../../logs");
public static getInstance(): Logger {
if (!Logger.instance) {
Logger.instance = new Logger();
}
return Logger.instance;
}
private writeToLog(logMessage: string): void {
if (environment.development) return;
const date: Date = new Date();
const logDir: string = Logger.log;
const logFile: string = join(
logDir,
`${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}.log`,
);
if (!existsSync(logDir)) {
mkdirSync(logDir, { recursive: true });
}
let addSeparator: boolean = false;
if (existsSync(logFile)) {
const fileStats: Stats = statSync(logFile);
if (fileStats.size > 0) {
const lastModified: Date = new Date(fileStats.mtime);
if (
lastModified.getFullYear() === date.getFullYear() &&
lastModified.getMonth() === date.getMonth() &&
lastModified.getDate() === date.getDate() &&
lastModified.getHours() !== date.getHours()
) {
addSeparator = true;
}
}
}
const stream: WriteStream = createWriteStream(logFile, { flags: "a" });
if (addSeparator) {
stream.write(`${EOL}${date.toISOString()}${EOL}`);
}
stream.write(`${logMessage}${EOL}`);
stream.close();
}
private extractFileName(stack: string): string {
const stackLines: string[] = stack.split("\n");
let callerFile: string = "";
for (let i: number = 2; i < stackLines.length; i++) {
const line: string = stackLines[i].trim();
if (line && !line.includes("Logger.") && line.includes("(")) {
callerFile = line.split("(")[1]?.split(")")[0] || "";
break;
}
}
return basename(callerFile);
}
private getCallerInfo(stack: unknown): {
filename: string;
timestamp: string;
} {
const filename: string =
typeof stack === "string" ? this.extractFileName(stack) : "unknown";
const readableTimestamp: string = timestampToReadable();
return { filename, timestamp: readableTimestamp };
}
public info(message: string | string[], breakLine: boolean = false): void {
const stack: string = new Error().stack || "";
const { filename, timestamp } = this.getCallerInfo(stack);
const joinedMessage: string = Array.isArray(message)
? message.join(" ")
: message;
const logMessageParts: ILogMessageParts = {
readableTimestamp: { value: timestamp, color: "90" },
level: { value: "[INFO]", color: "32" },
filename: { value: `(${filename})`, color: "36" },
message: { value: joinedMessage, color: "0" },
};
this.writeToLog(`${timestamp} [INFO] (${filename}) ${joinedMessage}`);
this.writeConsoleMessageColored(logMessageParts, breakLine);
}
public warn(message: string | string[], breakLine: boolean = false): void {
const stack: string = new Error().stack || "";
const { filename, timestamp } = this.getCallerInfo(stack);
const joinedMessage: string = Array.isArray(message)
? message.join(" ")
: message;
const logMessageParts: ILogMessageParts = {
readableTimestamp: { value: timestamp, color: "90" },
level: { value: "[WARN]", color: "33" },
filename: { value: `(${filename})`, color: "36" },
message: { value: joinedMessage, color: "0" },
};
this.writeToLog(`${timestamp} [WARN] (${filename}) ${joinedMessage}`);
this.writeConsoleMessageColored(logMessageParts, breakLine);
}
public error(
message: string | Error | (string | Error)[],
breakLine: boolean = false,
): void {
const stack: string = new Error().stack || "";
const { filename, timestamp } = this.getCallerInfo(stack);
const messages: (string | Error)[] = Array.isArray(message)
? message
: [message];
const joinedMessage: string = messages
.map((msg: string | Error): string =>
typeof msg === "string" ? msg : msg.message,
)
.join(" ");
const logMessageParts: ILogMessageParts = {
readableTimestamp: { value: timestamp, color: "90" },
level: { value: "[ERROR]", color: "31" },
filename: { value: `(${filename})`, color: "36" },
message: { value: joinedMessage, color: "0" },
};
this.writeToLog(`${timestamp} [ERROR] (${filename}) ${joinedMessage}`);
this.writeConsoleMessageColored(logMessageParts, breakLine);
}
public custom(
bracketMessage: string,
bracketMessage2: string,
message: string | string[],
color: string,
breakLine: boolean = false,
): void {
const stack: string = new Error().stack || "";
const { timestamp } = this.getCallerInfo(stack);
const joinedMessage: string = Array.isArray(message)
? message.join(" ")
: message;
const logMessageParts: ILogMessageParts = {
readableTimestamp: { value: timestamp, color: "90" },
level: { value: bracketMessage, color },
filename: { value: `${bracketMessage2}`, color: "36" },
message: { value: joinedMessage, color: "0" },
};
this.writeToLog(
`${timestamp} ${bracketMessage} (${bracketMessage2}) ${joinedMessage}`,
);
this.writeConsoleMessageColored(logMessageParts, breakLine);
}
public space(): void {
console.log();
}
private writeConsoleMessageColored(
logMessageParts: ILogMessageParts,
breakLine: boolean = false,
): void {
const logMessage: string = Object.keys(logMessageParts)
.map((key: string) => {
const part: ILogMessagePart = logMessageParts[key];
return `\x1b[${part.color}m${part.value}\x1b[0m`;
})
.join(" ");
console.log(logMessage + (breakLine ? EOL : ""));
}
}
const logger: Logger = Logger.getInstance();
export { logger };

16
src/index.ts Normal file
View file

@ -0,0 +1,16 @@
import { logger } from "@helpers/logger";
import { serverHandler } from "@/server";
async function main(): Promise<void> {
try {
serverHandler.initialize();
} catch (error) {
throw error;
}
}
main().catch((error: Error) => {
logger.error(["Error initializing the server:", error]);
process.exit(1);
});

51
src/routes/[id].ts Normal file
View file

@ -0,0 +1,51 @@
import { lanyardConfig } from "@config/environment";
import { renderEjsTemplate } from "@helpers/ejs";
import { getLanyardData } from "@helpers/lanyard";
const routeDef: RouteDef = {
method: "GET",
accepts: "*/*",
returns: "text/html",
};
async function handler(request: ExtendedRequest): Promise<Response> {
const { id } = request.params;
const data: LanyardResponse = await getLanyardData(id);
if (!data.success) {
return await renderEjsTemplate("error", {
message: "User not found or Lanyard data unavailable.",
});
}
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 ejsTemplateData: EjsTemplateData = {
title: "User Page",
username: presence.discord_user.username,
status: presence.discord_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,
},
instance: instance,
};
return await renderEjsTemplate("index", ejsTemplateData);
}
export { handler, routeDef };

50
src/routes/index.ts Normal file
View file

@ -0,0 +1,50 @@
import { lanyardConfig } from "@config/environment";
import { renderEjsTemplate } from "@helpers/ejs";
import { getLanyardData } from "@helpers/lanyard";
const routeDef: RouteDef = {
method: "GET",
accepts: "*/*",
returns: "text/html",
};
async function handler(): Promise<Response> {
const data: LanyardResponse = await getLanyardData();
if (!data.success) {
return Response.json(data.error, {
status: 500,
});
}
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 ejsTemplateData: EjsTemplateData = {
title: "User Page",
username: presence.discord_user.username,
status: presence.discord_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,
},
instance: instance,
};
return await renderEjsTemplate("index", ejsTemplateData);
}
export { handler, routeDef };

267
src/server.ts Normal file
View file

@ -0,0 +1,267 @@
import { environment } from "@config/environment";
import { logger } from "@helpers/logger";
import {
type BunFile,
FileSystemRouter,
type MatchedRoute,
type Serve,
} from "bun";
import { resolve } from "path";
import { webSocketHandler } from "@/websocket";
class ServerHandler {
private router: FileSystemRouter;
constructor(
private port: number,
private host: string,
) {
this.router = new FileSystemRouter({
style: "nextjs",
dir: "./src/routes",
fileExtensions: [".ts"],
origin: `http://${this.host}:${this.port}`,
});
}
public initialize(): void {
const server: Serve = Bun.serve({
port: this.port,
hostname: this.host,
fetch: this.handleRequest.bind(this),
websocket: {
open: webSocketHandler.handleOpen.bind(webSocketHandler),
message: webSocketHandler.handleMessage.bind(webSocketHandler),
close: webSocketHandler.handleClose.bind(webSocketHandler),
},
});
logger.info(
`Server running at http://${server.hostname}:${server.port}`,
true,
);
this.logRoutes();
}
private logRoutes(): void {
logger.info("Available routes:");
const sortedRoutes: [string, string][] = Object.entries(
this.router.routes,
).sort(([pathA]: [string, string], [pathB]: [string, string]) =>
pathA.localeCompare(pathB),
);
for (const [path, filePath] of sortedRoutes) {
logger.info(`Route: ${path}, File: ${filePath}`);
}
}
private async serveStaticFile(pathname: string): Promise<Response> {
try {
let filePath: string;
if (pathname === "/favicon.ico") {
filePath = resolve("public", "assets", "favicon.ico");
} else {
filePath = resolve(`.${pathname}`);
}
const file: BunFile = Bun.file(filePath);
if (await file.exists()) {
const fileContent: ArrayBuffer = await file.arrayBuffer();
const contentType: string =
file.type || "application/octet-stream";
return new Response(fileContent, {
headers: { "Content-Type": contentType },
});
} else {
logger.warn(`File not found: ${filePath}`);
return new Response("Not Found", { status: 404 });
}
} catch (error) {
logger.error([
`Error serving static file: ${pathname}`,
error as Error,
]);
return new Response("Internal Server Error", { status: 500 });
}
}
private async handleRequest(
request: Request,
server: BunServer,
): Promise<Response> {
const extendedRequest: ExtendedRequest = request as ExtendedRequest;
extendedRequest.startPerf = performance.now();
const pathname: string = new URL(request.url).pathname;
if (pathname.startsWith("/public") || pathname === "/favicon.ico") {
return await this.serveStaticFile(pathname);
}
const match: MatchedRoute | null = this.router.match(request);
let requestBody: unknown = {};
let response: Response;
if (match) {
const { filePath, params, query } = match;
try {
const routeModule: RouteModule = await import(filePath);
const contentType: string | null =
request.headers.get("Content-Type");
const actualContentType: string | null = contentType
? contentType.split(";")[0].trim()
: null;
if (
routeModule.routeDef.needsBody === "json" &&
actualContentType === "application/json"
) {
try {
requestBody = await request.json();
} catch {
requestBody = {};
}
} else if (
routeModule.routeDef.needsBody === "multipart" &&
actualContentType === "multipart/form-data"
) {
try {
requestBody = await request.formData();
} catch {
requestBody = {};
}
}
if (
(Array.isArray(routeModule.routeDef.method) &&
!routeModule.routeDef.method.includes(
request.method,
)) ||
(!Array.isArray(routeModule.routeDef.method) &&
routeModule.routeDef.method !== request.method)
) {
response = Response.json(
{
success: false,
code: 405,
error: `Method ${request.method} Not Allowed, expected ${
Array.isArray(routeModule.routeDef.method)
? routeModule.routeDef.method.join(", ")
: routeModule.routeDef.method
}`,
},
{ status: 405 },
);
} else {
const expectedContentType: string | string[] | null =
routeModule.routeDef.accepts;
let matchesAccepts: boolean;
if (Array.isArray(expectedContentType)) {
matchesAccepts =
expectedContentType.includes("*/*") ||
expectedContentType.includes(
actualContentType || "",
);
} else {
matchesAccepts =
expectedContentType === "*/*" ||
actualContentType === expectedContentType;
}
if (!matchesAccepts) {
response = Response.json(
{
success: false,
code: 406,
error: `Content-Type ${actualContentType} Not Acceptable, expected ${
Array.isArray(expectedContentType)
? expectedContentType.join(", ")
: expectedContentType
}`,
},
{ status: 406 },
);
} else {
extendedRequest.params = params;
extendedRequest.query = query;
response = await routeModule.handler(
extendedRequest,
requestBody,
server,
);
if (routeModule.routeDef.returns !== "*/*") {
response.headers.set(
"Content-Type",
routeModule.routeDef.returns,
);
}
}
}
} catch (error: unknown) {
logger.error([
`Error handling route ${request.url}:`,
error as Error,
]);
response = Response.json(
{
success: false,
code: 500,
error: "Internal Server Error",
},
{ status: 500 },
);
}
} else {
response = Response.json(
{
success: false,
code: 404,
error: "Not Found",
},
{ status: 404 },
);
}
const headers: Headers = response.headers;
let ip: string | null = server.requestIP(request)?.address || null;
if (!ip) {
ip =
headers.get("CF-Connecting-IP") ||
headers.get("X-Real-IP") ||
headers.get("X-Forwarded-For") ||
null;
}
logger.custom(
`[${request.method}]`,
`(${response.status})`,
[
request.url,
`${(performance.now() - extendedRequest.startPerf).toFixed(2)}ms`,
ip || "unknown",
],
"90",
);
return response;
}
}
const serverHandler: ServerHandler = new ServerHandler(
environment.port,
environment.host,
);
export { serverHandler };

17
src/views/error.ejs Normal file
View file

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Error</title>
<link rel="stylesheet" href="/public/css/error.css">
<meta name="color-scheme" content="dark">
</head>
<body>
<div class="error-container">
<div class="error-title">Something went wrong</div>
<div class="error-message">
<%= message || "An unexpected error occurred." %>
</div>
</div>
</body>
</html>

118
src/views/index.ejs Normal file
View file

@ -0,0 +1,118 @@
<!DOCTYPE html>
<html lang="en">
<head data-user-id="<%= user.id %>" data-instance-uri="<%= instance %>">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><%= title %></title>
<link rel="stylesheet" href="/public/css/index.css">
<script src="/public/js/index.js" defer></script>
<meta name="color-scheme" content="dark">
</head>
<body>
<div class="user-card">
<div class="avatar-status-wrapper">
<div class="avatar-wrapper">
<img class="avatar" src="https://cdn.discordapp.com/avatars/<%= user.id %>/<%= user.avatar %>" alt="Avatar">
<% if (user.avatar_decoration_data) { %>
<img class="decoration" src="https://cdn.discordapp.com/avatar-decoration-presets/<%= user.avatar_decoration_data.asset %>" alt="Decoration">
<% } %>
<% if (platform.mobile) { %>
<svg class="platform-icon mobile-only" viewBox="0 0 1000 1500" fill="#43a25a" aria-label="Mobile" width="17" height="17">
<path d="M 187 0 L 813 0 C 916.277 0 1000 83.723 1000 187 L 1000 1313 C 1000 1416.277 916.277 1500 813 1500 L 187 1500 C 83.723 1500 0 1416.277 0 1313 L 0 187 C 0 83.723 83.723 0 187 0 Z M 125 1000 L 875 1000 L 875 250 L 125 250 Z M 500 1125 C 430.964 1125 375 1180.964 375 1250 C 375 1319.036 430.964 1375 500 1375 C 569.036 1375 625 1319.036 625 1250 C 625 1180.964 569.036 1125 500 1125 Z"/>
</svg>
<% } else { %>
<div class="status-indicator <%= status %>"></div>
<% } %>
</div>
<div class="user-info">
<h1><%= username %></h1>
<% if (activities.length && activities[0].type === 4) {
const emoji = activities[0].emoji;
const isCustom = emoji?.id;
const emojiUrl = isCustom
? `https://cdn.discordapp.com/emojis/${emoji.id}.${emoji.animated ? "gif" : "png"}`
: null;
%>
<p class="custom-status">
<% if (isCustom && emojiUrl) { %>
<img src="<%= emojiUrl %>" alt="<%= emoji.name %>" class="custom-emoji">
<% } else if (emoji?.name) { %>
<%= emoji.name %>
<% } %>
<%= activities[0].state %>
</p>
<% } %>
</div>
</div>
</div>
<% const filtered = activities.filter(a => a.type !== 4); %>
<% if (filtered.length > 0) { %>
<h2>Activities</h2>
<ul class="activities">
<% filtered.forEach(activity => {
const start = activity.timestamps?.start;
const end = activity.timestamps?.end;
const now = Date.now();
const elapsed = start ? now - start : 0;
const total = (start && end) ? end - start : null;
const progress = (total && elapsed > 0) ? Math.min(100, Math.floor((elapsed / total) * 100)) : null;
const img = activity.assets?.large_image;
let art = null;
if (img?.includes("https")) {
const clean = img.split("/https/")[1];
if (clean) art = `https://${clean}`;
} else if (img?.startsWith("spotify:")) {
art = `https://i.scdn.co/image/${img.split(":")[1]}`;
} else if (img) {
art = `https://cdn.discordapp.com/app-assets/${activity.application_id}/${img}.png`;
}
%>
<li class="activity">
<% if (art) { %>
<img class="activity-art" src="<%= art %>" alt="Art">
<% } %>
<div class="activity-content">
<div class="activity-header <%= progress !== null ? 'no-timestamp' : '' %>">
<span class="activity-name"><%= activity.name %></span>
<% if (start && progress === null) { %>
<div class="activity-timestamp" data-start="<%= start %>">
<% const started = new Date(start); %>
<span>
Since: <%= started.toLocaleTimeString('en-GB', { hour: '2-digit', minute: '2-digit', second: '2-digit' }) %> <span class="elapsed"></span>
</span>
</div>
<% } %>
</div>
<% if (activity.details) { %>
<div class="activity-detail"><%= activity.details %></div>
<% } %>
<% if (activity.state) { %>
<div class="activity-detail"><%= activity.state %></div>
<% } %>
<% if (progress !== null) { %>
<div class="progress-bar" data-start="<%= start %>" data-end="<%= end %>">
<div class="progress-fill" style="width: <%= progress %>%"></div>
</div>
<% if (start && end) { %>
<div class="progress-time-labels" data-start="<%= start %>" data-end="<%= end %>">
<span class="progress-current">--:--</span>
<span class="progress-total"><%= Math.floor((end - start) / 60000) %>:<%= String(Math.floor(((end - start) % 60000) / 1000)).padStart(2, "0") %></span>
</div>
<% } %>
<% } %>
</div>
</li>
<% }) %>
</ul>
<% } %>
</body>
</html>

34
src/websocket.ts Normal file
View file

@ -0,0 +1,34 @@
import { logger } from "@helpers/logger";
import { type ServerWebSocket } from "bun";
class WebSocketHandler {
public handleMessage(ws: ServerWebSocket, message: string): void {
logger.info(`WebSocket received: ${message}`);
try {
ws.send(`You said: ${message}`);
} catch (error) {
logger.error(["WebSocket send error", error as Error]);
}
}
public handleOpen(ws: ServerWebSocket): void {
logger.info("WebSocket connection opened.");
try {
ws.send("Welcome to the WebSocket server!");
} catch (error) {
logger.error(["WebSocket send error", error as Error]);
}
}
public handleClose(
ws: ServerWebSocket,
code: number,
reason: string,
): void {
logger.warn(`WebSocket closed with code ${code}, reason: ${reason}`);
}
}
const webSocketHandler: WebSocketHandler = new WebSocketHandler();
export { webSocketHandler };