diff options
Diffstat (limited to 'static/serverping.js')
-rw-r--r-- | static/serverping.js | 495 |
1 files changed, 495 insertions, 0 deletions
diff --git a/static/serverping.js b/static/serverping.js new file mode 100644 index 0000000..5fe285d --- /dev/null +++ b/static/serverping.js @@ -0,0 +1,495 @@ + +function htmlspecialchars(str){ + + if(str === null){ + + return "<i><Empty></i>"; + } + + var map = { + '&': '&', + '<': '<', + '>': '>', + '"': '"', + "'": ''' + } + + return str.replace(/[&<>"']/g, function(m){return map[m];}); +} + +// initialize garbage +var list = []; +var pinged_list = []; +var reqs = 0; +var errors = 0; +var sort = 0; // lower ping first + +// check for instance redirect stuff +var redir = ""; +var target = "/web?"; +new URL(window.location.href) + .searchParams + .forEach( + function(value, key){ + + if(key == "target"){ + + target = "/" + encodeURIComponent(value) + "?"; + return; + } + + if(key == "npt"){ return; } + redir += encodeURIComponent(key) + "=" + encodeURIComponent(value) + } + ); + +if(redir != ""){ + redir = target + redir; +} + +var quote = document.createElement("div"); +quote.className = "quote"; +quote.innerHTML = 'Pinged <b>0</b> servers (<b>0</b> failed requests)'; +var [div_servercount, div_failedreqs] = + quote.getElementsByTagName("b"); + +var noscript = document.getElementsByTagName("noscript")[0]; +document.body.insertBefore(quote, noscript.nextSibling); + +// create table +var table = document.createElement("table"); +table.innerHTML = + '<thead>' + + '<tr>' + + '<th><div class="arrow up"></div>Ping</th>' + + '<th class="extend">Server</th>' + + '<th>Address</th>' + + '<th>Bot protection</th>' + + '<th title="Amount of legit requests processed since the last APCU cache clear (usually happens at midnight)">Real reqs (?)</th>' + + '<th title="Amount of filtered requests processed since the last APCU cache clear (usually happens at midnight)">Bot reqs (?)</th>' + + '<th>API</th>' + + '<th>Version</th>' + + '</tr>' + + '</thead>' + + '<tbody></tbody>'; + +document.body.insertBefore(table, quote.nextSibling); + +// handle sorting clicks +var tbody = table.getElementsByTagName("tbody")[0]; +var th = table.getElementsByTagName("th"); + +for(var i=0; i<th.length; i++){ + + th[i].addEventListener("click", function(event){ + + if(event.target.className.includes("arrow")){ + + var div = event.target.parentElement; + }else{ + + var div = event.target; + } + + var arrow = div.getElementsByClassName("arrow"); + var orientation = 0; // up + + if(arrow.length === 0){ + + // delete arrow and add new one + arrow = document.getElementsByClassName("arrow"); + arrow[0].remove(); + + arrow = document.createElement("div"); + arrow.className = "arrow up"; + div.insertBefore(arrow, event.target.firstChild); + }else{ + + // switch arrow position + if(arrow[0].className == "arrow down"){ + + arrow[0].className = "arrow up"; + }else{ + + arrow[0].className = "arrow down"; + orientation = 1; + } + } + + switch(div.textContent.toLowerCase()){ + + case "ping": sort = orientation; break; + case "server": sort = 2 + orientation; break; + case "address": sort = 4 + orientation; break; + case "bot protection": sort = 6 + orientation; break; + case "real reqs (?)": sort = 8 + orientation; break; + case "bot reqs (?)": sort = 10 + orientation; break; + case "api": sort = 12 + orientation; break; + case "version": sort = 14 + orientation; break; + } + + render_list(); + }); +} + +function validate_url(url, allow_http = false){ + + try{ + + url = new URL(url); + if( + url.protocol == "https:" || + ( + ( + allow_http === true || + window.location.protocol == "http:" + ) && + url.protocol == "http:" + ) + ){ + + return true; + } + }catch(error){} // do nothing + + return false; +} + +function number_format(int){ + + return new Intl.NumberFormat().format(int); +} + +// parse initial server list +fetch_server(window.location.origin); + +async function fetch_server(server){ + + if(!validate_url(server)){ + console.warn("Invalid server URL: " + server); + return; + } + + // make sure baseURL is origin + server = new URL(server).origin; + // prevent multiple fetches + for(var i=0; i<list.length; i++){ + + if(list[i] == server){ + + // serber was already fetched + console.info("Already checked server: " + server); + return; + } + } + + // prevent future fetches + list.push(server); + + var data = null; + var ping = new Date().getTime(); + + try{ + + data = await fetch( + server + "/ami4get" + ); + + if(data.status !== 200){ + + // endpoint is not available + errors++; + div_failedreqs.textContent = number_format(errors); + console.warn(server + ": Invalid HTTP code " + data.status); + return; + } + + data = await data.json(); + data.server.ping = new Date().getTime() - ping; + + }catch(error){ + + errors++; + div_failedreqs.textContent = number_format(errors); + console.warn(server + ": Could not fetch or decode JSON"); + return; + } + + // sanitize data + if( + typeof data.status != "string" || + data.status != "ok" || + typeof data.server != "object" || + !( + typeof data.server.name == "string" || + ( + typeof data.server.name == "object" && + data.server.name === null + ) + ) || + typeof data.service != "string" || + data.service != "4get" || + ( + typeof data.server.description != "string" && + data.server.description !== null + ) || + typeof data.server.bot_protection != "number" || + typeof data.server.real_requests != "number" || + typeof data.server.bot_requests != "number" || + typeof data.server.api_enabled != "boolean" || + typeof data.server.alt_addresses != "object" || + typeof data.server.version != "number" || + typeof data.instances != "object" + ){ + + errors++; + div_failedreqs.textContent = number_format(errors); + console.warn(server + ": Malformed JSON"); + return; + } + + data.server.ip = server; + + reqs++; + div_servercount.textContent = number_format(reqs); + + var total = pinged_list.push(data) - 1; + pinged_list[total].index = total; + + render_list(); + + // get more serbers + for(var i=0; i<data.instances.length; i++){ + + fetch_server(data.instances[i]); + } +} + +function sorta(object, element, order){ + + return object.slice().sort( + function(a, b){ + + if(order){ + + return a.server[element] - b.server[element]; + } + + return b.server[element] - a.server[element]; + } + ); +} + +function textsort(object, element, order){ + + var sort = object.slice().sort( + function(a, b){ + + return a.server[element].localeCompare(b.server[element]); + } + ); + + if(!order){ + return sort.reverse(); + } + + return sort; +} + +function render_list(){ + + var sorted_list = []; + + // sort + var filter = Boolean(sort % 2); + + switch(sort){ + + case 0: + case 1: + sorted_list = sorta(pinged_list, "ping", filter === true ? false : true); + break; + + case 2: + case 3: + sorted_list = textsort(pinged_list, "name", filter === true ? false : true); + break; + + case 4: + case 5: + sorted_list = textsort(pinged_list, "ip", filter === true ? false : true); + break; + + case 6: + case 7: + sorted_list = sorta(pinged_list, "bot_protection", filter === true ? false : true); + break; + + case 8: + case 9: + sorted_list = sorta(pinged_list, "real_requests", filter); + break; + + case 10: + case 11: + sorted_list = sorta(pinged_list, "bot_requests", filter); + break; + + case 12: + case 13: + sorted_list = sorta(pinged_list, "api_enabled", filter); + break; + + case 14: + case 15: + sorted_list = sorta(pinged_list, "version", filter); + break; + } + + // render tabloid + var html = ""; + + for(var k=0; k<sorted_list.length; k++){ + + html += '<tr onclick="show_server(' + sorted_list[k].index + ');">'; + + for(var i=0; i<8; i++){ + + html += '<td'; + + switch(i){ + + case 0: // server ping + if(sorted_list[k].server.ping <= 100){ + + html += '><span style="color:var(--green);">' + sorted_list[k].server.ping + '</span>'; + break; + } + + if(sorted_list[k].server.ping <= 200){ + + html += '><span style="color:var(--yellow);">' + sorted_list[k].server.ping + '</span>'; + break; + } + + html += '><span style="color:var(--red);">' + number_format(sorted_list[k].server.ping) + '</span>'; + break; + + // server name + case 1: html += ' class="extend">' + htmlspecialchars(sorted_list[k].server.name); break; + case 2: html += '>' + htmlspecialchars(new URL(sorted_list[k].server.ip).host); break; + case 3: // bot protection + switch(sorted_list[k].server.bot_protection){ + + case 0: + html += '><span style="color:var(--green);">Disabled</span>'; + break; + + case 1: + html += '><span style="color:var(--yellow);">Image captcha</span>'; + break; + + case 2: + html += '><span style="color:var(--red);">Invite only</span>'; + break; + + default: + html += '>Unknown'; + } + break; + + case 4: // real reqs + html += '>' + number_format(sorted_list[k].server.real_requests); + break; + + case 5: // bot reqs + html += '>' + number_format(sorted_list[k].server.bot_requests); + break; + + case 6: // api enabled + + if(sorted_list[k].server.api_enabled){ + + html += '><span style="color:var(--green);">Yes</span>'; + }else{ + + html += '><span style="color:var(--red);">No</span>'; + } + break; + + // version + case 7: html += ">v" + sorted_list[k].server.version; break; + } + + html += '</td>'; + } + + html += '</tr>'; + } + + tbody.innerHTML = html; +} + +var popup_bg = document.getElementById("popup-bg"); +var popup_wrapper = document.getElementsByClassName("popup-wrapper")[0]; +var popup = popup_wrapper.getElementsByClassName("popup")[0]; +var popup_shown = false; + +popup_bg.addEventListener("click", function(){ + + popup_wrapper.style.display = "none"; + popup_bg.style.display = "none"; +}); + +function show_server(serverid){ + + var html = + '<h2>' + htmlspecialchars(pinged_list[serverid].server.name) + '</h2>' + + 'Description' + + '<div class="code">' + htmlspecialchars(pinged_list[serverid].server.description) + '</div>'; + + var url_obj = new URL(pinged_list[serverid].server.ip); + var url = htmlspecialchars(url_obj.origin); + var domain = url_obj.hostname; + + html += + 'URL: <a rel="noreferer" target="_BLANK" href="' + url + redir + '">' + url + '</a> <a rel="noreferer" target="_BLANK" href="https://browserleaks.com/ip/' + encodeURIComponent(domain) + '">(IP lookup)</a>' + + '<br><br>Alt addresses:'; + + var len = pinged_list[serverid].server.alt_addresses.length; + + if(len === 0){ + + html += ' <i><Empty></i>'; + }else{ + + html += '<ul>'; + + for(var i=0; i<len; i++){ + + var url_obj = new URL(pinged_list[serverid].server.alt_addresses[i]); + var url = htmlspecialchars(url_obj.origin); + var domain = url_obj.hostname; + + if(validate_url(pinged_list[serverid].server.alt_addresses[i], true)){ + + html += '<li><a rel="noreferer" href="' + url + redir + '" target="_BLANK">' + url + '</a> <a rel="noreferer" target="_BLANK" href="https://browserleaks.com/ip/' + encodeURIComponent(domain) + '">(IP lookup)</a></li>'; + }else{ + + console.warn(pinged_list[serverid].server.ip + ": Invalid peer URL => " + pinged_list[serverid].server.alt_addresses[i]); + } + } + + html += '</ul>'; + } + popup.innerHTML = html; + + popup_wrapper.style.display = "block"; + popup_bg.style.display = "block"; +} + +function hide_server(){ + + popup_wrapper.style.display = "none"; + popup_bg.style.display = "none"; +} |