diff --git a/.dist/home.js b/.dist/home.js new file mode 100644 index 0000000..b54844f --- /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); + } + if (instance.uptime) { + 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..5ff4e0b 100644 --- a/.dist/login.js +++ b/.dist/login.js @@ -419,13 +419,13 @@ 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) { continue; } const option = document.createElement("option"); + option.disabled = !instance.online; option.value = instance.name; if (instance.url) { stringURLMap.set(option.value, instance.url); @@ -447,5 +447,6 @@ if (datalist) { } datalist.append(option); } + checkInstance(""); }); } diff --git a/.gitignore b/.gitignore index 11de622..48a8706 100644 --- a/.gitignore +++ b/.gitignore @@ -132,3 +132,5 @@ dist #Data file for tomoato testAccount.json CC +uptime.json +.directory diff --git a/InstanceInfo.md b/InstanceInfo.md index 3e9b4b1..45e16f4 100644 --- a/InstanceInfo.md +++ b/InstanceInfo.md @@ -31,6 +31,7 @@ anything with a `?` in-front of its `:` are optional, though you must either inc Some of these values may not be used right now, though they will likely be used in the future, so feel free to fill out what you like, though the more you fill out the more information we can give the users about your instance in the future. language should be [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1_codes). Country should be [ISO 8166-2 alpha-2](https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2). +You can also add yourself to [this](https://github.com/spacebarchat/spacebarchat/tree/master/instances) list, and you should, though there are some disadvantages to only being in that list # Questions ## Do I have to do this to let Jank Client connect to my server? No, you may choose to not do this, this just makes it easier for people using Jank Client to find and use your instance as it's in the dropdown menu for instances, though the user may enter any instance they please. @@ -38,3 +39,5 @@ No, you may choose to not do this, this just makes it easier for people using Ja If it's spacebar compatable, yes it may be entered, though if there are too many incompatablities, it may not be included, or may need a warning of sorts. ## I'm hosting my own instance of spacebar and would like to change the defualt instance on my instance of Jank Client to my own instance. Just change the first entry in the list to your own, and it should connect without issue. +## Why would I put my instance in this list over the official spacebar list? +While putting your instance in the other list will get it to show up on jank client, this list does have more settings, and will show up earlier in the results, though either list will work to get in the dropdown menu diff --git a/index.js b/index.js index 1ab22b7..b5ddf81 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,11 +13,8 @@ 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)){ - console.log("pushed"); instances.push(instance); - console.log(instances) }else{ const ofinst=instancenames.get(instance.name) for(const key of Object.keys(instance)){ @@ -26,6 +24,7 @@ fetch("https://raw.githubusercontent.com/spacebarchat/spacebarchat/master/instan } } } + stats.observe(instances) }) app.use("/getupdates",(req, res) => { @@ -147,6 +146,12 @@ async function inviteres(req,res){ app.use('/services/oembed', (req, res) => { inviteres(req, res); }) +app.use("/uptime",(req,res)=>{ + console.log(req.query.name) + const uptime=stats.uptime[req.query.name]; + console.log(req.query.name,uptime,stats.uptime) + res.send(uptime); +}) app.use('/', async (req, res) => { const scheme = req.secure ? 'https' : 'http'; const host=`${scheme}://${req.get("Host")}`; @@ -158,13 +163,15 @@ app.use('/', async (req, res) => { }else{ console.log(req); } - + if(req.path==="/"){ + res.sendFile(`./webpage/home.html`, {root: __dirname}); + return; + } 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; } @@ -191,3 +198,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..7c82cc9 --- /dev/null +++ b/stats.js @@ -0,0 +1,152 @@ +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={}; + } +} +if(uptimeObject["undefined"]){ + delete uptimeObject["undefined"]; + updatejson(); +} +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===""){ + + setStatus(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(_=>{ + setStatus(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)){ + setStatus(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*Math.min(timepassed,1000*60*60*24); + weektime+=online*Math.min(timepassed,1000*60*60*24*7); + 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|Object} instance + * @param {boolean} status + */ +function setStatus(instance,status){ + let name=instance.name; + if(typeof instance==="string" ){ + name=instance; + } + + 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(); + } + if(typeof instance!=="string" ){ + 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..968b747 --- /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.