Refactor uptimeObject to use a Map instead of an object
Signed-off-by: Scott Gould <greysilly7@gmail.com>
This commit is contained in:
parent
be40162fc5
commit
cd7135518a
6 changed files with 74 additions and 77 deletions
|
@ -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);
|
||||
});
|
||||
|
||||
|
|
55
src/stats.ts
55
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<string, UptimeEntry[]> = loadUptimeObject();
|
||||
export{ uptimeObject as uptime };
|
||||
|
||||
function loadUptimeObject(): UptimeObject{
|
||||
function loadUptimeObject(): Map<string, UptimeEntry[]>{
|
||||
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<typeof setTimeout> | 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<string>): 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<string>): 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);
|
||||
|
|
37
src/utils.ts
37
src/utils.ts
|
@ -24,9 +24,7 @@ export async function getApiUrls(url: string): Promise<ApiUrls | null>{
|
|||
url += "/";
|
||||
}
|
||||
try{
|
||||
const info: ApiUrls = await fetch(`${url}.well-known/spacebar`).then(
|
||||
res=>res.json() as Promise<ApiUrls>
|
||||
);
|
||||
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<ApiUrls | null>{
|
|||
}
|
||||
}
|
||||
|
||||
export async function inviteResponse(
|
||||
req: Request,
|
||||
res: Response
|
||||
): Promise<void>{
|
||||
export async function inviteResponse(req: Request, res: Response): Promise<void>{
|
||||
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<Invite>
|
||||
);
|
||||
const invite = await fetch(`${urls.api}/invites/${code}`).then(json=>json.json() as Promise<Invite>);
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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 }[] }[] =
|
||||
[];
|
||||
|
|
|
@ -11,7 +11,7 @@ offset: number
|
|||
private readonly maxDist = 6000;
|
||||
HTMLElements: [HTMLElement, string][] = [];
|
||||
div: HTMLDivElement | null = null;
|
||||
timeout: NodeJS.Timeout | null = null;
|
||||
timeout: ReturnType<typeof setTimeout> | 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<typeof setTimeout> | string | number | undefined | null = null;
|
||||
// let instanceinfo;
|
||||
const stringURLMap = new Map<string, string>();
|
||||
|
||||
|
@ -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;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue