From 367b0b8fe6d7fc9e06f4b4a4441cdf14af0b44b5 Mon Sep 17 00:00:00 2001 From: Scott Gould Date: Wed, 25 Sep 2024 14:05:17 -0400 Subject: [PATCH 1/3] optomize index.ts --- src/index.ts | 58 ++++++++++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/src/index.ts b/src/index.ts index e1a30c3..3d59b05 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,19 +2,20 @@ import compression from"compression"; import express, { Request, Response }from"express"; -import fs from"node:fs"; +import fs from"node:fs/promises"; import fetch from"node-fetch"; import path from"node:path"; import{ observe, uptime }from"./stats.js"; import{ getApiUrls, inviteResponse }from"./utils.js"; import{ fileURLToPath }from"node:url"; -const devmode = (process.env.NODE_ENV || 'development')==='development'; +import process from"node:process"; +const devmode = (process.env.NODE_ENV || "development") === "development"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); interface Instance { -name: string; -[key: string]: any; + name: string; + [key: string]: any; } const app = express(); @@ -55,9 +56,9 @@ async function updateInstances(): Promise{ updateInstances(); -app.use("/getupdates", (_req: Request, res: Response)=>{ +app.use("/getupdates", async (_req: Request, res: Response)=>{ try{ - const stats = fs.statSync(path.join(__dirname, "webpage")); + const stats = await fs.stat(path.join(__dirname, "webpage")); res.send(stats.mtimeMs.toString()); }catch(error){ console.error("Error getting updates:", error); @@ -103,33 +104,40 @@ app.use("/", async (req: Request, res: Response)=>{ } const filePath = path.join(__dirname, "webpage", req.path); - if(fs.existsSync(filePath)){ + try{ + await fs.access(filePath); if(devmode){ const filePath2 = path.join(__dirname, "../src/webpage", req.path); - if(fs.existsSync(filePath2)){ + try{ + await fs.access(filePath2); res.sendFile(filePath2); return; - } + }catch{} } res.sendFile(filePath); - }else if(fs.existsSync(`${filePath}.html`)){ - if(devmode){ - const filePath2 = path.join(__dirname, "../src/webpage", req.path); - if(fs.existsSync(filePath2+".html")){ - res.sendFile(filePath2+".html"); - return; + }catch{ + try{ + await fs.access(`${filePath}.html`); + if(devmode){ + const filePath2 = path.join(__dirname, "../src/webpage", req.path); + try{ + await fs.access(filePath2 + ".html"); + res.sendFile(filePath2 + ".html"); + return; + }catch{} } - } - res.sendFile(`${filePath}.html`); - }else{ - if(req.path.startsWith("/src/webpage")){ - const filePath2 = path.join(__dirname, "..", req.path); - if(fs.existsSync(`${filePath2}`)){ - res.sendFile(filePath2); - return; + res.sendFile(`${filePath}.html`); + }catch{ + if(req.path.startsWith("/src/webpage")){ + const filePath2 = path.join(__dirname, "..", req.path); + try{ + await fs.access(filePath2); + res.sendFile(filePath2); + return; + }catch{} } + res.sendFile(path.join(__dirname, "webpage", "index.html")); } - res.sendFile(path.join(__dirname, "webpage", "index.html")); } }); @@ -138,4 +146,4 @@ app.listen(PORT, ()=>{ console.log(`Server running on port ${PORT}`); }); -export{ getApiUrls }; +export{ getApiUrls }; \ No newline at end of file From 2b1644cc038d1955ee895f0d0ac47cbc1b03b4f9 Mon Sep 17 00:00:00 2001 From: Scott Gould Date: Wed, 25 Sep 2024 14:10:43 -0400 Subject: [PATCH 2/3] Remove Node-Fetch --- package.json | 5 ++--- src/index.ts | 2 +- src/stats.ts | 3 +-- src/utils.ts | 33 +++++++++++++++------------------ 4 files changed, 19 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index f4f57f8..41b7566 100644 --- a/package.json +++ b/package.json @@ -20,11 +20,10 @@ "compression": "^1.7.4", "eslint-plugin-html": "^8.1.1", "express": "^4.19.2", + "gulp-sourcemaps": "^3.0.0", "gulp-swc": "^2.2.0", - "node-fetch": "^3.3.2", "rimraf": "^6.0.1", - "ts-to-jsdoc": "^2.2.0", - "gulp-sourcemaps":"^3.0.0" + "ts-to-jsdoc": "^2.2.0" }, "devDependencies": { "@eslint/js": "^9.10.0", diff --git a/src/index.ts b/src/index.ts index 3d59b05..e16cef6 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,12 +3,12 @@ import compression from"compression"; import express, { Request, Response }from"express"; import fs from"node:fs/promises"; -import fetch from"node-fetch"; import path from"node:path"; import{ observe, uptime }from"./stats.js"; import{ getApiUrls, inviteResponse }from"./utils.js"; import{ fileURLToPath }from"node:url"; import process from"node:process"; + const devmode = (process.env.NODE_ENV || "development") === "development"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); diff --git a/src/stats.ts b/src/stats.ts index 42fd207..6443bff 100644 --- a/src/stats.ts +++ b/src/stats.ts @@ -1,6 +1,5 @@ import fs from"node:fs"; import path from"node:path"; -import fetch from"node-fetch"; import{ getApiUrls }from"./utils.js"; import{ fileURLToPath }from"node:url"; @@ -251,4 +250,4 @@ function setStatus(instance: string | Instance, status: boolean): void{ calcStats(instance); } } -} +} \ No newline at end of file diff --git a/src/utils.ts b/src/utils.ts index 05cc353..28dec6b 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,23 +1,22 @@ -import fetch from"node-fetch"; import{ Request, Response }from"express"; interface ApiUrls { -api: string; -gateway: string; -cdn: string; -wellknown: string; + api: string; + gateway: string; + cdn: string; + wellknown: string; } interface Invite { -guild: { -name: string; -description?: string; -icon?: string; -id: string; -}; -inviter?: { -username: string; -}; + guild: { + name: string; + description?: string; + icon?: string; + id: string; + }; + inviter?: { + username: string; + }; } export async function getApiUrls(url: string): Promise{ @@ -31,9 +30,7 @@ export async function getApiUrls(url: string): Promise{ const api = info.api; const apiUrl = new URL(api); const policies: any = await fetch( - `${api}${ - apiUrl.pathname.includes("api") ? "" : "api" - }/policies/instance/domains` + `${api}${apiUrl.pathname.includes("api") ? "" : "api"}/policies/instance/domains` ).then(res=>res.json()); return{ api: policies.apiEndpoint, @@ -111,4 +108,4 @@ export async function inviteResponse( }; res.json(jsonResponse); } -} +} \ No newline at end of file From cd7135518a78b46953caf289a7d4e85237b9a648 Mon Sep 17 00:00:00 2001 From: Scott Gould Date: Wed, 25 Sep 2024 16:11:09 -0400 Subject: [PATCH 3/3] Refactor uptimeObject to use a Map instead of an object Signed-off-by: Scott Gould --- src/index.ts | 2 +- src/stats.ts | 55 ++++++++++++++++++--------------- src/utils.ts | 37 +++++++--------------- src/webpage/emoji.ts | 5 ++- src/webpage/infiniteScroller.ts | 17 +++++----- src/webpage/login.ts | 35 +++++++++++---------- 6 files changed, 74 insertions(+), 77 deletions(-) diff --git a/src/index.ts b/src/index.ts index d970d1f..3bbe747 100644 --- a/src/index.ts +++ b/src/index.ts @@ -71,7 +71,7 @@ app.use("/services/oembed", (req: Request, res: Response)=>{ }); app.use("/uptime", (req: Request, res: Response)=>{ - const instanceUptime = uptime[req.query.name as string]; + const instanceUptime = uptime.get(req.query.name as string); res.send(instanceUptime); }); diff --git a/src/stats.ts b/src/stats.ts index 6443bff..d1e66ac 100644 --- a/src/stats.ts +++ b/src/stats.ts @@ -2,6 +2,7 @@ import fs from"node:fs"; import path from"node:path"; import{ getApiUrls }from"./utils.js"; import{ fileURLToPath }from"node:url"; +import{ setTimeout, clearTimeout }from"node:timers"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -11,10 +12,6 @@ interface UptimeEntry { online: boolean; } -interface UptimeObject { - [key: string]: UptimeEntry[]; -} - interface Instance { name: string; urls?: { api: string }; @@ -27,37 +24,46 @@ interface Instance { }; } -const uptimeObject: UptimeObject = loadUptimeObject(); +const uptimeObject: Map = loadUptimeObject(); export{ uptimeObject as uptime }; -function loadUptimeObject(): UptimeObject{ +function loadUptimeObject(): Map{ const filePath = path.join(__dirname, "..", "uptime.json"); if(fs.existsSync(filePath)){ try{ - return JSON.parse(fs.readFileSync(filePath, "utf8")); + const data = JSON.parse(fs.readFileSync(filePath, "utf8")); + return new Map(Object.entries(data)); }catch(error){ console.error("Error reading uptime.json:", error); - return{}; + return new Map(); } } - return{}; + return new Map(); } +let saveTimeout: ReturnType | null = null; + function saveUptimeObject(): void{ - fs.writeFile( - path.join(__dirname, "..", "uptime.json"), - JSON.stringify(uptimeObject), - error=>{ - if(error){ - console.error("Error saving uptime.json:", error); + if(saveTimeout){ + clearTimeout(saveTimeout); + } + saveTimeout = setTimeout(()=>{ + const data = Object.fromEntries(uptimeObject); + fs.writeFile( + path.join(__dirname, "..", "uptime.json"), + JSON.stringify(data), + error=>{ + if(error){ + console.error("Error saving uptime.json:", error); + } } - } - ); + ); + }, 5000); // Batch updates every 5 seconds } function removeUndefinedKey(): void{ - if(uptimeObject.undefined){ - delete uptimeObject.undefined; + if(uptimeObject.has("undefined")){ + uptimeObject.delete("undefined"); saveUptimeObject(); } } @@ -84,7 +90,7 @@ async function resolveInstance( return; } activeInstances.add(instance.name); - await checkHealth(instance, api); // Ensure health is checked immediately + await checkHealth(instance, api); scheduleHealthCheck(instance, api); }catch(error){ console.error("Error resolving instance:", error); @@ -126,7 +132,6 @@ async function checkHealth( const response = await fetch(`${api}/ping`, { method: "HEAD" }); console.log(`Checking health for ${instance.name}: ${response.status}`); if(response.ok || tries > 3){ - console.log(`Setting status for ${instance.name} to ${response.ok}`); setStatus(instance, response.ok); }else{ retryHealthCheck(instance, api, tries); @@ -150,7 +155,7 @@ function retryHealthCheck( } function updateInactiveInstances(activeInstances: Set): void{ - for(const key of Object.keys(uptimeObject)){ + for(const key of uptimeObject.keys()){ if(!activeInstances.has(key)){ setStatus(key, false); } @@ -158,7 +163,7 @@ function updateInactiveInstances(activeInstances: Set): void{ } function calcStats(instance: Instance): void{ - const obj = uptimeObject[instance.name]; + const obj = uptimeObject.get(instance.name); if(!obj)return; const now = Date.now(); @@ -234,11 +239,11 @@ function calculateUptimeStats( function setStatus(instance: string | Instance, status: boolean): void{ const name = typeof instance === "string" ? instance : instance.name; - let obj = uptimeObject[name]; + let obj = uptimeObject.get(name); if(!obj){ obj = []; - uptimeObject[name] = obj; + uptimeObject.set(name, obj); } const lastEntry = obj.at(-1); diff --git a/src/utils.ts b/src/utils.ts index 28dec6b..029a13c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -24,9 +24,7 @@ export async function getApiUrls(url: string): Promise{ url += "/"; } try{ - const info: ApiUrls = await fetch(`${url}.well-known/spacebar`).then( - res=>res.json() as Promise - ); + const info: ApiUrls = await fetch(`${url}.well-known/spacebar`).then(res=>res.json()); const api = info.api; const apiUrl = new URL(api); const policies: any = await fetch( @@ -44,14 +42,11 @@ export async function getApiUrls(url: string): Promise{ } } -export async function inviteResponse( - req: Request, - res: Response -): Promise{ +export async function inviteResponse(req: Request, res: Response): Promise{ let url: URL; - if(URL.canParse(req.query.url as string)){ + try{ url = new URL(req.query.url as string); - }else{ + }catch{ const scheme = req.secure ? "https" : "http"; const host = `${scheme}://${req.get("Host")}`; url = new URL(host); @@ -67,45 +62,37 @@ export async function inviteResponse( if(!instance){ throw new Error("Instance not specified"); } + const urls = await getApiUrls(instance); if(!urls){ throw new Error("Failed to get API URLs"); } - const invite = await fetch(`${urls.api}/invites/${code}`).then( - res=>res.json() as Promise - ); + const invite = await fetch(`${urls.api}/invites/${code}`).then(json=>json.json() as Promise); const title = invite.guild.name; const description = invite.inviter - ? `${invite.inviter.username} has invited you to ${invite.guild.name}${ - invite.guild.description ? `\n${invite.guild.description}` : "" - }` - : `You've been invited to ${invite.guild.name}${ - invite.guild.description ? `\n${invite.guild.description}` : "" - }`; + ? `${invite.inviter.username} has invited you to ${invite.guild.name}${invite.guild.description ? `\n${invite.guild.description}` : ""}` + : `You've been invited to ${invite.guild.name}${invite.guild.description ? `\n${invite.guild.description}` : ""}`; const thumbnail = invite.guild.icon ? `${urls.cdn}/icons/${invite.guild.id}/${invite.guild.icon}.png` : ""; - const jsonResponse = { + res.json({ type: "link", version: "1.0", title, thumbnail, description, - }; - - res.json(jsonResponse); + }); }catch(error){ console.error("Error processing invite response:", error); - const jsonResponse = { + res.json({ type: "link", version: "1.0", title: "Jank Client", thumbnail: "/logo.webp", description: "A spacebar client that has DMs, replying and more", url: url.toString(), - }; - res.json(jsonResponse); + }); } } \ No newline at end of file diff --git a/src/webpage/emoji.ts b/src/webpage/emoji.ts index acb696c..7756967 100644 --- a/src/webpage/emoji.ts +++ b/src/webpage/emoji.ts @@ -17,9 +17,8 @@ class Emoji{ get guild(){ if(this.owner instanceof Guild){ return this.owner; - }else{ - return undefined; } + return null; } get localuser(){ if(this.owner instanceof Guild){ @@ -83,7 +82,7 @@ class Emoji{ array[i] = read8(); } //console.log(array); - return new TextDecoder("utf-8").decode(array.buffer); + return new TextDecoder("utf8").decode(array.buffer as ArrayBuffer); } const build: { name: string; emojis: { name: string; emoji: string }[] }[] = []; diff --git a/src/webpage/infiniteScroller.ts b/src/webpage/infiniteScroller.ts index 6a5962e..dc6a79b 100644 --- a/src/webpage/infiniteScroller.ts +++ b/src/webpage/infiniteScroller.ts @@ -11,7 +11,7 @@ offset: number private readonly maxDist = 6000; HTMLElements: [HTMLElement, string][] = []; div: HTMLDivElement | null = null; - timeout: NodeJS.Timeout | null = null; + timeout: ReturnType | null = null; beenloaded = false; scrollBottom = 0; scrollTop = 0; @@ -239,7 +239,6 @@ offset: number try{ if(!this.div){ res(false); - return false; } const out = (await Promise.allSettled([ this.watchForTop(), @@ -250,11 +249,9 @@ offset: number this.timeout = setTimeout(this.updatestuff.bind(this), 300); } res(Boolean(changed)); - return Boolean(changed); }catch(e){ console.error(e); res(false); - return false; }finally{ setTimeout(()=>{ this.changePromise = undefined; @@ -281,9 +278,13 @@ offset: number behavior: "smooth", block: "center", }); - await new Promise(resolve=>setTimeout(resolve, 1000)); + await new Promise(resolve=>{ + setTimeout(resolve, 1000); + }); element.classList.remove("jumped"); - await new Promise(resolve=>setTimeout(resolve, 100)); + await new Promise(resolve=>{ + setTimeout(resolve, 100); + }); element.classList.add("jumped"); }else{ element.scrollIntoView(); @@ -296,7 +297,9 @@ offset: number await this.firstElement(id); this.updatestuff(); await this.watchForChange(); - await new Promise(resolve=>setTimeout(resolve, 100)); + await new Promise(resolve=>{ + setTimeout(resolve, 100); + }); await this.focus(id, true); } } diff --git a/src/webpage/login.ts b/src/webpage/login.ts index 8dbefc7..931883a 100644 --- a/src/webpage/login.ts +++ b/src/webpage/login.ts @@ -199,7 +199,7 @@ function adduser(user: typeof Specialuser.prototype.json){ return user; } const instancein = document.getElementById("instancein") as HTMLInputElement; -let timeout: string | number | NodeJS.Timeout | undefined; +let timeout: ReturnType | string | number | undefined | null = null; // let instanceinfo; const stringURLMap = new Map(); @@ -231,8 +231,8 @@ async function getapiurls(str: string): Promise< if(stringURLMap.size!==0){ res(); } - },100) - }) + },100); + }); } if(val){ str = val; @@ -335,7 +335,7 @@ async function checkInstance(instance?: string){ gateway: string; login: string; value: string; - } + }; if(instanceinfo){ instanceinfo.value = instanceValue; localStorage.setItem("instanceinfo", JSON.stringify(instanceinfo)); @@ -360,10 +360,12 @@ async function checkInstance(instance?: string){ if(instancein){ console.log(instancein); - instancein.addEventListener("keydown", _=>{ + instancein.addEventListener("keydown", ()=>{ const verify = document.getElementById("verify"); verify!.textContent = "Waiting to check Instance"; - clearTimeout(timeout); + if(timeout !== null && typeof timeout !== "string"){ + clearTimeout(timeout); + } timeout = setTimeout(()=>checkInstance(), 1000); }); if(localStorage.getItem("instanceinfo")){ @@ -410,7 +412,9 @@ async function login(username: string, password: string, captcha: string){ if(response.captcha_sitekey){ const capt = document.getElementById("h-captcha"); - if(!capt!.children.length){ + if(capt!.children.length){ + eval("hcaptcha.reset()"); + }else{ const capty = document.createElement("div"); capty.classList.add("h-captcha"); @@ -419,8 +423,6 @@ async function login(username: string, password: string, captcha: string){ script.src = "https://js.hcaptcha.com/1/api.js"; capt!.append(script); capt!.append(capty); - }else{ - eval("hcaptcha.reset()"); } }else{ console.log(response); @@ -434,6 +436,7 @@ async function login(username: string, password: string, captcha: string){ "", "", function(this: HTMLInputElement){ + // eslint-disable-next-line no-invalid-this onetimecode = this.value; }, ], @@ -453,18 +456,18 @@ async function login(username: string, password: string, captcha: string){ }), }) .then(r=>r.json()) - .then(response=>{ - if(response.message){ - alert(response.message); + .then(res=>{ + if(res.message){ + alert(res.message); }else{ - console.warn(response); - if(!response.token)return; + console.warn(res); + if(!res.token)return; adduser({ serverurls: JSON.parse( localStorage.getItem("instanceinfo")! ), email: username, - token: response.token, + token: res.token, }).username = username; const redir = new URLSearchParams( window.location.search @@ -579,7 +582,7 @@ export function getInstances(){ } fetch("/instances.json") - .then(_=>_.json()) + .then(res=>res.json()) .then( (json: { name: string;