From cd7135518a78b46953caf289a7d4e85237b9a648 Mon Sep 17 00:00:00 2001 From: Scott Gould Date: Wed, 25 Sep 2024 16:11:09 -0400 Subject: [PATCH] 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;