diff --git a/.dist/home.js b/.dist/home.js new file mode 100644 index 0000000..ffb50ee --- /dev/null +++ b/.dist/home.js @@ -0,0 +1,62 @@ +import { mobile } from "./login.js"; +console.log(mobile); +const serverbox = document.getElementById("instancebox"); +fetch("/instances.json").then(_ => _.json()).then((json) => { + console.warn(json); + for (const instance of json) { + if (instance.display === false) { + continue; + } + const div = document.createElement("div"); + div.classList.add("flexltr", "instance"); + if (instance.image) { + const img = document.createElement("img"); + img.src = instance.image; + div.append(img); + } + const statbox = document.createElement("div"); + statbox.classList.add("flexttb"); + { + const textbox = document.createElement("div"); + textbox.classList.add("flexttb", "instatancetextbox"); + const title = document.createElement("h2"); + title.innerText = instance.name; + if (instance.online !== undefined) { + const status = document.createElement("span"); + status.innerText = instance.online ? "Online" : "Offline"; + status.classList.add("instanceStatus"); + title.append(status); + } + textbox.append(title); + if (instance.description || instance.descriptionLong) { + const p = document.createElement("p"); + if (instance.descriptionLong) { + p.innerText = instance.descriptionLong; + } + else { + p.innerText = instance.description; + } + textbox.append(p); + } + statbox.append(textbox); + } + { + const stats = document.createElement("div"); + stats.classList.add("flexltr"); + const span = document.createElement("span"); + span.innerText = `Uptime: All time: ${Math.floor(instance.uptime.alltime * 100)}% This week: ${Math.floor(instance.uptime.weektime * 100)}% Today: ${Math.floor(instance.uptime.daytime * 100)}%`; + stats.append(span); + statbox.append(stats); + } + div.append(statbox); + div.onclick = _ => { + if (instance.online) { + window.location.href = "/register.html?instance=" + encodeURI(instance.name); + } + else { + alert("Instance is offline, can't connect"); + } + }; + serverbox.append(div); + } +}); diff --git a/.dist/login.js b/.dist/login.js index b4f0817..46aa5b3 100644 --- a/.dist/login.js +++ b/.dist/login.js @@ -419,7 +419,6 @@ if (datalist) { console.warn(json); if (instancein && instancein.value === "") { instancein.value = json[0].name; - setTimeout(checkInstance, 10); } for (const instance of json) { if (instance.display === false) { @@ -447,5 +446,6 @@ if (datalist) { } datalist.append(option); } + checkInstance(""); }); } diff --git a/index.js b/index.js index a27f368..9ac4858 100755 --- a/index.js +++ b/index.js @@ -4,7 +4,8 @@ const compression = require('compression') const express = require('express'); const fs = require('fs'); const app = express(); -const instances=require("./webpage/instances.json") +const instances=require("./webpage/instances.json"); +const stats=require("./stats.js"); const instancenames=new Map(); for(const instance of instances){ instancenames.set(instance.name,instance); @@ -12,7 +13,6 @@ for(const instance of instances){ app.use(compression()) fetch("https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/instances/instances.json").then(_=>_.json()).then(json=>{ for(const instance of json){ - console.log(instance); if(!instancenames.has(instance.name)){ instances.push(instance); }else{ @@ -24,6 +24,7 @@ fetch("https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/instan } } } + stats.observe(instances) }) app.use("/getupdates",(req, res) => { @@ -145,6 +146,10 @@ async function inviteres(req,res){ app.use('/services/oembed', (req, res) => { inviteres(req, res); }) +app.use("/uptime",(req,res)=>{ + const uptime=stats.uptime[req.query.name]; + res.send(uptime); +}) app.use('/', async (req, res) => { const scheme = req.secure ? 'https' : 'http'; const host=`${scheme}://${req.get("Host")}`; @@ -156,13 +161,14 @@ app.use('/', async (req, res) => { }else{ console.log(req); } - + if(req.path==="/"){ + res.sendFile(`./webpage/home.html`, {root: __dirname}); + } if(debugging&&req.path.startsWith("/service.js")){ res.send("dud"); return; } if(req.path.startsWith("/instances.json")){ - console.log("grabbed") res.send(JSON.stringify(instances)); return; } @@ -189,3 +195,5 @@ app.use('/', async (req, res) => { const PORT = process.env.PORT || +process.argv[1] || 8080; app.listen(PORT, () => {}); console.log("this ran :P"); + +exports.getapiurls=getapiurls; diff --git a/stats.js b/stats.js new file mode 100644 index 0000000..385ca5e --- /dev/null +++ b/stats.js @@ -0,0 +1,142 @@ +const index = require('./index.js'); +const fs=require("fs"); +let uptimeObject={}; +if(fs.existsSync("./uptime.json")){ + try{ + uptimeObject=JSON.parse(fs.readFileSync('./uptime.json', 'utf8')); + }catch{ + uptimeObject={}; + } +} + +async function observe(instances){ + const active=new Set(); + async function resolveinstance(instance){ + calcStats(instance); + let api; + if(instance.urls){ + api=instance.urls.api; + }else if(instance.url){ + const urls=await index.getapiurls(instance.url); + if(urls){ + api=urls.api; + } + } + if(!api||api===""){ + + setSatus(instance,false); + console.warn(instance.name+" does not resolve api URL"); + setTimeout(_=>{resolveinstance(instance)},1000*60*30,); + return + } + active.add(instance.name); + api+=api.endsWith("/")?"":"/" + function check(){ + fetch(api+"ping").then(_=>{ + setSatus(instance,_.ok); + }) + } + setTimeout( + _=>{ + check(); + setInterval(_=>{ + check(); + },1000*60*30) + },Math.random()*1000*60*10 + ) + } + const promlist=[]; + for(const instance of instances){ + promlist.push(resolveinstance(instance)); + } + await Promise.allSettled(promlist); + for(const key of Object.keys(uptimeObject)){ + if(!active.has(key)){ + setSatus(key,false); + } + } +} +function calcStats(instance){ + let obj=uptimeObject[instance.name]; + if(!obj) return; + const day=Date.now()-1000*60*60*24; + const week=Date.now()-1000*60*60*24*7; + let alltime=-1; + let totalTimePassed=0; + let laststamp=0; + let daytime=-1; + let weektime=-1; + let online=false; + for(const thing of obj){ + const stamp=thing.time; + if(alltime===-1){ + laststamp=stamp; + alltime=0; + } + const timepassed=stamp-laststamp; + totalTimePassed+=timepassed; + alltime+=online*timepassed; + if(stamp>week){ + if(weektime===-1){ + weektime=online*(stamp-week); + }else{ + weektime+=online*timepassed; + } + if(stamp>day){ + if(daytime===-1){ + daytime=online*(stamp-day); + }else{ + daytime+=online*timepassed; + } + } + } + online=thing.online; + } + instance.online=online; + const timepassed=Date.now()-laststamp; + totalTimePassed+=timepassed; + daytime+=online*timepassed; + weektime+=online*timepassed; + alltime+=online*timepassed; + alltime/=totalTimePassed; + if(timepassed>1000*60*60*24){ + daytime/=1000*60*60*24; + if(timepassed>1000*60*60*24*7){ + weektime/=1000*60*60*24*7; + }else{ + weektime=alltime; + } + }else{ + weektime=alltime + daytime=alltime; + } + instance.uptime={daytime,weektime,alltime} +} +/** + * @param {string} name + * @param {boolean} status + */ +function setSatus(instance,status){ + const name=instance.name; + let obj=uptimeObject[name]; + let needSetting=false; + if(!obj){ + obj=[]; + uptimeObject[name]=obj; + needSetting=true; + }else{ + if(obj[obj.length-1].online!==status){ + needSetting=true; + } + } + if(needSetting){ + obj.push({time:Date.now(),online:status}); + updatejson(); + } + calcStats(instance); +} +function updatejson(){ + fs.writeFile('./uptime.json',JSON.stringify(uptimeObject),_=>{}); +} +exports.observe=observe; +exports.uptime=uptimeObject; diff --git a/webpage/home.html b/webpage/home.html new file mode 100644 index 0000000..a6ffaab --- /dev/null +++ b/webpage/home.html @@ -0,0 +1,48 @@ + + +
+ + +Jank Client is a spacebar compatible client seeking to be as good as it can be with many features including:
+We always appreciate some help, wether that be in the form of bug reports, or code, or even just pointing out some typos.