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)=>{
|
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);
|
res.send(instanceUptime);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
41
src/stats.ts
41
src/stats.ts
|
@ -2,6 +2,7 @@ import fs from"node:fs";
|
||||||
import path from"node:path";
|
import path from"node:path";
|
||||||
import{ getApiUrls }from"./utils.js";
|
import{ getApiUrls }from"./utils.js";
|
||||||
import{ fileURLToPath }from"node:url";
|
import{ fileURLToPath }from"node:url";
|
||||||
|
import{ setTimeout, clearTimeout }from"node:timers";
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
const __filename = fileURLToPath(import.meta.url);
|
||||||
const __dirname = path.dirname(__filename);
|
const __dirname = path.dirname(__filename);
|
||||||
|
@ -11,10 +12,6 @@ interface UptimeEntry {
|
||||||
online: boolean;
|
online: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface UptimeObject {
|
|
||||||
[key: string]: UptimeEntry[];
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Instance {
|
interface Instance {
|
||||||
name: string;
|
name: string;
|
||||||
urls?: { api: string };
|
urls?: { api: string };
|
||||||
|
@ -27,37 +24,46 @@ interface Instance {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const uptimeObject: UptimeObject = loadUptimeObject();
|
const uptimeObject: Map<string, UptimeEntry[]> = loadUptimeObject();
|
||||||
export{ uptimeObject as uptime };
|
export{ uptimeObject as uptime };
|
||||||
|
|
||||||
function loadUptimeObject(): UptimeObject{
|
function loadUptimeObject(): Map<string, UptimeEntry[]>{
|
||||||
const filePath = path.join(__dirname, "..", "uptime.json");
|
const filePath = path.join(__dirname, "..", "uptime.json");
|
||||||
if(fs.existsSync(filePath)){
|
if(fs.existsSync(filePath)){
|
||||||
try{
|
try{
|
||||||
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
const data = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
||||||
|
return new Map(Object.entries(data));
|
||||||
}catch(error){
|
}catch(error){
|
||||||
console.error("Error reading uptime.json:", 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{
|
function saveUptimeObject(): void{
|
||||||
|
if(saveTimeout){
|
||||||
|
clearTimeout(saveTimeout);
|
||||||
|
}
|
||||||
|
saveTimeout = setTimeout(()=>{
|
||||||
|
const data = Object.fromEntries(uptimeObject);
|
||||||
fs.writeFile(
|
fs.writeFile(
|
||||||
path.join(__dirname, "..", "uptime.json"),
|
path.join(__dirname, "..", "uptime.json"),
|
||||||
JSON.stringify(uptimeObject),
|
JSON.stringify(data),
|
||||||
error=>{
|
error=>{
|
||||||
if(error){
|
if(error){
|
||||||
console.error("Error saving uptime.json:", error);
|
console.error("Error saving uptime.json:", error);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
}, 5000); // Batch updates every 5 seconds
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeUndefinedKey(): void{
|
function removeUndefinedKey(): void{
|
||||||
if(uptimeObject.undefined){
|
if(uptimeObject.has("undefined")){
|
||||||
delete uptimeObject.undefined;
|
uptimeObject.delete("undefined");
|
||||||
saveUptimeObject();
|
saveUptimeObject();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,7 +90,7 @@ async function resolveInstance(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
activeInstances.add(instance.name);
|
activeInstances.add(instance.name);
|
||||||
await checkHealth(instance, api); // Ensure health is checked immediately
|
await checkHealth(instance, api);
|
||||||
scheduleHealthCheck(instance, api);
|
scheduleHealthCheck(instance, api);
|
||||||
}catch(error){
|
}catch(error){
|
||||||
console.error("Error resolving instance:", error);
|
console.error("Error resolving instance:", error);
|
||||||
|
@ -126,7 +132,6 @@ async function checkHealth(
|
||||||
const response = await fetch(`${api}/ping`, { method: "HEAD" });
|
const response = await fetch(`${api}/ping`, { method: "HEAD" });
|
||||||
console.log(`Checking health for ${instance.name}: ${response.status}`);
|
console.log(`Checking health for ${instance.name}: ${response.status}`);
|
||||||
if(response.ok || tries > 3){
|
if(response.ok || tries > 3){
|
||||||
console.log(`Setting status for ${instance.name} to ${response.ok}`);
|
|
||||||
setStatus(instance, response.ok);
|
setStatus(instance, response.ok);
|
||||||
}else{
|
}else{
|
||||||
retryHealthCheck(instance, api, tries);
|
retryHealthCheck(instance, api, tries);
|
||||||
|
@ -150,7 +155,7 @@ function retryHealthCheck(
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateInactiveInstances(activeInstances: Set<string>): void{
|
function updateInactiveInstances(activeInstances: Set<string>): void{
|
||||||
for(const key of Object.keys(uptimeObject)){
|
for(const key of uptimeObject.keys()){
|
||||||
if(!activeInstances.has(key)){
|
if(!activeInstances.has(key)){
|
||||||
setStatus(key, false);
|
setStatus(key, false);
|
||||||
}
|
}
|
||||||
|
@ -158,7 +163,7 @@ function updateInactiveInstances(activeInstances: Set<string>): void{
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcStats(instance: Instance): void{
|
function calcStats(instance: Instance): void{
|
||||||
const obj = uptimeObject[instance.name];
|
const obj = uptimeObject.get(instance.name);
|
||||||
if(!obj)return;
|
if(!obj)return;
|
||||||
|
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
|
@ -234,11 +239,11 @@ function calculateUptimeStats(
|
||||||
|
|
||||||
function setStatus(instance: string | Instance, status: boolean): void{
|
function setStatus(instance: string | Instance, status: boolean): void{
|
||||||
const name = typeof instance === "string" ? instance : instance.name;
|
const name = typeof instance === "string" ? instance : instance.name;
|
||||||
let obj = uptimeObject[name];
|
let obj = uptimeObject.get(name);
|
||||||
|
|
||||||
if(!obj){
|
if(!obj){
|
||||||
obj = [];
|
obj = [];
|
||||||
uptimeObject[name] = obj;
|
uptimeObject.set(name, obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastEntry = obj.at(-1);
|
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 += "/";
|
url += "/";
|
||||||
}
|
}
|
||||||
try{
|
try{
|
||||||
const info: ApiUrls = await fetch(`${url}.well-known/spacebar`).then(
|
const info: ApiUrls = await fetch(`${url}.well-known/spacebar`).then(res=>res.json());
|
||||||
res=>res.json() as Promise<ApiUrls>
|
|
||||||
);
|
|
||||||
const api = info.api;
|
const api = info.api;
|
||||||
const apiUrl = new URL(api);
|
const apiUrl = new URL(api);
|
||||||
const policies: any = await fetch(
|
const policies: any = await fetch(
|
||||||
|
@ -44,14 +42,11 @@ export async function getApiUrls(url: string): Promise<ApiUrls | null>{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function inviteResponse(
|
export async function inviteResponse(req: Request, res: Response): Promise<void>{
|
||||||
req: Request,
|
|
||||||
res: Response
|
|
||||||
): Promise<void>{
|
|
||||||
let url: URL;
|
let url: URL;
|
||||||
if(URL.canParse(req.query.url as string)){
|
try{
|
||||||
url = new URL(req.query.url as string);
|
url = new URL(req.query.url as string);
|
||||||
}else{
|
}catch{
|
||||||
const scheme = req.secure ? "https" : "http";
|
const scheme = req.secure ? "https" : "http";
|
||||||
const host = `${scheme}://${req.get("Host")}`;
|
const host = `${scheme}://${req.get("Host")}`;
|
||||||
url = new URL(host);
|
url = new URL(host);
|
||||||
|
@ -67,45 +62,37 @@ export async function inviteResponse(
|
||||||
if(!instance){
|
if(!instance){
|
||||||
throw new Error("Instance not specified");
|
throw new Error("Instance not specified");
|
||||||
}
|
}
|
||||||
|
|
||||||
const urls = await getApiUrls(instance);
|
const urls = await getApiUrls(instance);
|
||||||
if(!urls){
|
if(!urls){
|
||||||
throw new Error("Failed to get API URLs");
|
throw new Error("Failed to get API URLs");
|
||||||
}
|
}
|
||||||
|
|
||||||
const invite = await fetch(`${urls.api}/invites/${code}`).then(
|
const invite = await fetch(`${urls.api}/invites/${code}`).then(json=>json.json() as Promise<Invite>);
|
||||||
res=>res.json() as Promise<Invite>
|
|
||||||
);
|
|
||||||
const title = invite.guild.name;
|
const title = invite.guild.name;
|
||||||
const description = invite.inviter
|
const description = invite.inviter
|
||||||
? `${invite.inviter.username} has invited you to ${invite.guild.name}${
|
? `${invite.inviter.username} has invited you to ${invite.guild.name}${invite.guild.description ? `\n${invite.guild.description}` : ""}`
|
||||||
invite.guild.description ? `\n${invite.guild.description}` : ""
|
: `You've been invited 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
|
const thumbnail = invite.guild.icon
|
||||||
? `${urls.cdn}/icons/${invite.guild.id}/${invite.guild.icon}.png`
|
? `${urls.cdn}/icons/${invite.guild.id}/${invite.guild.icon}.png`
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
const jsonResponse = {
|
res.json({
|
||||||
type: "link",
|
type: "link",
|
||||||
version: "1.0",
|
version: "1.0",
|
||||||
title,
|
title,
|
||||||
thumbnail,
|
thumbnail,
|
||||||
description,
|
description,
|
||||||
};
|
});
|
||||||
|
|
||||||
res.json(jsonResponse);
|
|
||||||
}catch(error){
|
}catch(error){
|
||||||
console.error("Error processing invite response:", error);
|
console.error("Error processing invite response:", error);
|
||||||
const jsonResponse = {
|
res.json({
|
||||||
type: "link",
|
type: "link",
|
||||||
version: "1.0",
|
version: "1.0",
|
||||||
title: "Jank Client",
|
title: "Jank Client",
|
||||||
thumbnail: "/logo.webp",
|
thumbnail: "/logo.webp",
|
||||||
description: "A spacebar client that has DMs, replying and more",
|
description: "A spacebar client that has DMs, replying and more",
|
||||||
url: url.toString(),
|
url: url.toString(),
|
||||||
};
|
});
|
||||||
res.json(jsonResponse);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,9 +17,8 @@ class Emoji{
|
||||||
get guild(){
|
get guild(){
|
||||||
if(this.owner instanceof Guild){
|
if(this.owner instanceof Guild){
|
||||||
return this.owner;
|
return this.owner;
|
||||||
}else{
|
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
get localuser(){
|
get localuser(){
|
||||||
if(this.owner instanceof Guild){
|
if(this.owner instanceof Guild){
|
||||||
|
@ -83,7 +82,7 @@ class Emoji{
|
||||||
array[i] = read8();
|
array[i] = read8();
|
||||||
}
|
}
|
||||||
//console.log(array);
|
//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 }[] }[] =
|
const build: { name: string; emojis: { name: string; emoji: string }[] }[] =
|
||||||
[];
|
[];
|
||||||
|
|
|
@ -11,7 +11,7 @@ offset: number
|
||||||
private readonly maxDist = 6000;
|
private readonly maxDist = 6000;
|
||||||
HTMLElements: [HTMLElement, string][] = [];
|
HTMLElements: [HTMLElement, string][] = [];
|
||||||
div: HTMLDivElement | null = null;
|
div: HTMLDivElement | null = null;
|
||||||
timeout: NodeJS.Timeout | null = null;
|
timeout: ReturnType<typeof setTimeout> | null = null;
|
||||||
beenloaded = false;
|
beenloaded = false;
|
||||||
scrollBottom = 0;
|
scrollBottom = 0;
|
||||||
scrollTop = 0;
|
scrollTop = 0;
|
||||||
|
@ -239,7 +239,6 @@ offset: number
|
||||||
try{
|
try{
|
||||||
if(!this.div){
|
if(!this.div){
|
||||||
res(false);
|
res(false);
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
const out = (await Promise.allSettled([
|
const out = (await Promise.allSettled([
|
||||||
this.watchForTop(),
|
this.watchForTop(),
|
||||||
|
@ -250,11 +249,9 @@ offset: number
|
||||||
this.timeout = setTimeout(this.updatestuff.bind(this), 300);
|
this.timeout = setTimeout(this.updatestuff.bind(this), 300);
|
||||||
}
|
}
|
||||||
res(Boolean(changed));
|
res(Boolean(changed));
|
||||||
return Boolean(changed);
|
|
||||||
}catch(e){
|
}catch(e){
|
||||||
console.error(e);
|
console.error(e);
|
||||||
res(false);
|
res(false);
|
||||||
return false;
|
|
||||||
}finally{
|
}finally{
|
||||||
setTimeout(()=>{
|
setTimeout(()=>{
|
||||||
this.changePromise = undefined;
|
this.changePromise = undefined;
|
||||||
|
@ -281,9 +278,13 @@ offset: number
|
||||||
behavior: "smooth",
|
behavior: "smooth",
|
||||||
block: "center",
|
block: "center",
|
||||||
});
|
});
|
||||||
await new Promise(resolve=>setTimeout(resolve, 1000));
|
await new Promise(resolve=>{
|
||||||
|
setTimeout(resolve, 1000);
|
||||||
|
});
|
||||||
element.classList.remove("jumped");
|
element.classList.remove("jumped");
|
||||||
await new Promise(resolve=>setTimeout(resolve, 100));
|
await new Promise(resolve=>{
|
||||||
|
setTimeout(resolve, 100);
|
||||||
|
});
|
||||||
element.classList.add("jumped");
|
element.classList.add("jumped");
|
||||||
}else{
|
}else{
|
||||||
element.scrollIntoView();
|
element.scrollIntoView();
|
||||||
|
@ -296,7 +297,9 @@ offset: number
|
||||||
await this.firstElement(id);
|
await this.firstElement(id);
|
||||||
this.updatestuff();
|
this.updatestuff();
|
||||||
await this.watchForChange();
|
await this.watchForChange();
|
||||||
await new Promise(resolve=>setTimeout(resolve, 100));
|
await new Promise(resolve=>{
|
||||||
|
setTimeout(resolve, 100);
|
||||||
|
});
|
||||||
await this.focus(id, true);
|
await this.focus(id, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -199,7 +199,7 @@ function adduser(user: typeof Specialuser.prototype.json){
|
||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
const instancein = document.getElementById("instancein") as HTMLInputElement;
|
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;
|
// let instanceinfo;
|
||||||
const stringURLMap = new Map<string, string>();
|
const stringURLMap = new Map<string, string>();
|
||||||
|
|
||||||
|
@ -231,8 +231,8 @@ async function getapiurls(str: string): Promise<
|
||||||
if(stringURLMap.size!==0){
|
if(stringURLMap.size!==0){
|
||||||
res();
|
res();
|
||||||
}
|
}
|
||||||
},100)
|
},100);
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
if(val){
|
if(val){
|
||||||
str = val;
|
str = val;
|
||||||
|
@ -335,7 +335,7 @@ async function checkInstance(instance?: string){
|
||||||
gateway: string;
|
gateway: string;
|
||||||
login: string;
|
login: string;
|
||||||
value: string;
|
value: string;
|
||||||
}
|
};
|
||||||
if(instanceinfo){
|
if(instanceinfo){
|
||||||
instanceinfo.value = instanceValue;
|
instanceinfo.value = instanceValue;
|
||||||
localStorage.setItem("instanceinfo", JSON.stringify(instanceinfo));
|
localStorage.setItem("instanceinfo", JSON.stringify(instanceinfo));
|
||||||
|
@ -360,10 +360,12 @@ async function checkInstance(instance?: string){
|
||||||
|
|
||||||
if(instancein){
|
if(instancein){
|
||||||
console.log(instancein);
|
console.log(instancein);
|
||||||
instancein.addEventListener("keydown", _=>{
|
instancein.addEventListener("keydown", ()=>{
|
||||||
const verify = document.getElementById("verify");
|
const verify = document.getElementById("verify");
|
||||||
verify!.textContent = "Waiting to check Instance";
|
verify!.textContent = "Waiting to check Instance";
|
||||||
|
if(timeout !== null && typeof timeout !== "string"){
|
||||||
clearTimeout(timeout);
|
clearTimeout(timeout);
|
||||||
|
}
|
||||||
timeout = setTimeout(()=>checkInstance(), 1000);
|
timeout = setTimeout(()=>checkInstance(), 1000);
|
||||||
});
|
});
|
||||||
if(localStorage.getItem("instanceinfo")){
|
if(localStorage.getItem("instanceinfo")){
|
||||||
|
@ -410,7 +412,9 @@ async function login(username: string, password: string, captcha: string){
|
||||||
|
|
||||||
if(response.captcha_sitekey){
|
if(response.captcha_sitekey){
|
||||||
const capt = document.getElementById("h-captcha");
|
const capt = document.getElementById("h-captcha");
|
||||||
if(!capt!.children.length){
|
if(capt!.children.length){
|
||||||
|
eval("hcaptcha.reset()");
|
||||||
|
}else{
|
||||||
const capty = document.createElement("div");
|
const capty = document.createElement("div");
|
||||||
capty.classList.add("h-captcha");
|
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";
|
script.src = "https://js.hcaptcha.com/1/api.js";
|
||||||
capt!.append(script);
|
capt!.append(script);
|
||||||
capt!.append(capty);
|
capt!.append(capty);
|
||||||
}else{
|
|
||||||
eval("hcaptcha.reset()");
|
|
||||||
}
|
}
|
||||||
}else{
|
}else{
|
||||||
console.log(response);
|
console.log(response);
|
||||||
|
@ -434,6 +436,7 @@ async function login(username: string, password: string, captcha: string){
|
||||||
"",
|
"",
|
||||||
"",
|
"",
|
||||||
function(this: HTMLInputElement){
|
function(this: HTMLInputElement){
|
||||||
|
// eslint-disable-next-line no-invalid-this
|
||||||
onetimecode = this.value;
|
onetimecode = this.value;
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@ -453,18 +456,18 @@ async function login(username: string, password: string, captcha: string){
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.then(r=>r.json())
|
.then(r=>r.json())
|
||||||
.then(response=>{
|
.then(res=>{
|
||||||
if(response.message){
|
if(res.message){
|
||||||
alert(response.message);
|
alert(res.message);
|
||||||
}else{
|
}else{
|
||||||
console.warn(response);
|
console.warn(res);
|
||||||
if(!response.token)return;
|
if(!res.token)return;
|
||||||
adduser({
|
adduser({
|
||||||
serverurls: JSON.parse(
|
serverurls: JSON.parse(
|
||||||
localStorage.getItem("instanceinfo")!
|
localStorage.getItem("instanceinfo")!
|
||||||
),
|
),
|
||||||
email: username,
|
email: username,
|
||||||
token: response.token,
|
token: res.token,
|
||||||
}).username = username;
|
}).username = username;
|
||||||
const redir = new URLSearchParams(
|
const redir = new URLSearchParams(
|
||||||
window.location.search
|
window.location.search
|
||||||
|
@ -579,7 +582,7 @@ export function getInstances(){
|
||||||
}
|
}
|
||||||
|
|
||||||
fetch("/instances.json")
|
fetch("/instances.json")
|
||||||
.then(_=>_.json())
|
.then(res=>res.json())
|
||||||
.then(
|
.then(
|
||||||
(json: {
|
(json: {
|
||||||
name: string;
|
name: string;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue