| // Async METHOD to API |
| async function METHOD(method = 'POST', url = '', data) { |
| let payload = { |
| method: method, |
| mode: 'cors', |
| cache: 'no-cache', |
| credentials: 'same-origin', |
| headers: data ? { |
| 'Content-Type': 'application/json' |
| } : {}, |
| redirect: 'follow', |
| referrerPolicy: 'no-referrer' |
| } |
| if (data) { |
| payload.body = JSON.stringify(data); |
| } |
| try { |
| const response = await fetch(url, payload).catch( (e) => {throw e}); |
| if (response.ok !== true) throw `HTTP Error ${response.status}: ${response.statusText}` |
| let js = response.json(); |
| return js |
| } catch (e) { |
| alert(e); |
| } |
| } |
| |
| // HTTP methods |
| let DELETE = (url, data) => METHOD('DELETE', url, data); |
| let GET = (url, data) => METHOD('GET', url, data); |
| let PATCH = (url, data) => METHOD('PATCH', url, data); |
| let POST = (url, data) => METHOD('POST', url, data); |
| let PUT = (url, data) => METHOD('PUT', url, data); |
| |
| |
| // Prettifier for large numbers (adds commas) |
| Number.prototype.pretty = function(fix) { |
| if (fix) { |
| return String(this.toFixed(fix)).replace(/(\d)(?=(\d{3})+\.)/g, '$1,'); |
| } |
| return String(this.toFixed(0)).replace(/(\d)(?=(\d{3})+$)/g, '$1,'); |
| }; |
| |
| // HTML shortcuts |
| let htmlobj = (type, text) => { let obj = document.createElement(type); obj.innerText = text ? text : ""; return obj } |
| let _h1 = (title) => htmlobj('h1', title); |
| let _h2 = (title) => htmlobj('h2', title); |
| let _span = (title) => htmlobj('span', title); |
| let _p = (title) => htmlobj('p', title); |
| let _br = (title) => htmlobj('br'); |
| let _hr = (title) => htmlobj('hr'); |
| let _tr = () => htmlobj('tr'); |
| let _td = (title) => htmlobj('td', title); |
| let _th = (title, width) => { let obj = htmlobj('th', title); if (width) obj.style.width = width + "px"; return obj } |
| let _table = () => htmlobj('table'); |
| let _a = (txt) => htmlobj('a', txt); |
| |
| |
| async function unblock_ip(_entry) { |
| if (confirm(`This will unblock ${_entry.ip} and add it to the allow-list for 10 minutes. Continue?`)) { |
| result = await POST('allow', { |
| ip: _entry.ip, |
| reason: "Temporarily allow-listed for 10 minutes to unblock IP", |
| expires: parseInt(new Date().getTime()/1000) + 600, |
| force: true |
| }) |
| if (result.success === true) { |
| alert("IP Unblocked. Please allow for a few moments for it to apply across the servers."); |
| location.reload(); |
| } else { |
| alert(result.message); |
| } |
| } |
| return false; |
| } |
| |
| |
| async function remove_allow(_entry) { |
| if (confirm(`This will remove ${_entry.ip} from the allow list. Continue?`)) { |
| result = await DELETE('allow', { |
| ip: _entry.ip, |
| force: true |
| }) |
| if (result.success === true) { |
| alert("IP Removed from allow-list."); |
| location.reload(); |
| } else { |
| alert(result.message); |
| } |
| } |
| return false; |
| } |
| |
| |
| function unblock_link(entry, allowlist=false) { |
| let link = _a(allowlist ? 'Remove' : 'Unblock'); |
| let _entry = entry; |
| link.setAttribute("href", 'javascript:void(0);'); |
| if (allowlist === true) { link.addEventListener('click', () => remove_allow(_entry)); } |
| else {link.addEventListener('click', () => unblock_ip(_entry));} |
| return link; |
| } |
| |
| async function prime_frontpage() { |
| let all = await GET("all"); |
| let main = document.getElementById('main'); |
| main.innerHTML = ""; |
| let block_count = all.block.length.pretty(); |
| let h1 = _h1(`Recent activity (${block_count} blocks in total)`); |
| main.appendChild(h1); |
| all.block.sort((a,b) => b.timestamp - a.timestamp); // sort desc by timestamp |
| |
| |
| // Recent blocks |
| let activity_table = _table(); |
| activity_table.style.tableLayout = 'fixed'; |
| main.appendChild(activity_table); |
| |
| let theader = _tr(); |
| theader.appendChild(_th('Source IP', 300)); |
| theader.appendChild(_th('Added', 120)); |
| theader.appendChild(_th('Expires', 120)); |
| theader.appendChild(_th('Reason', 500)); |
| theader.appendChild(_th('Actions', 100)); |
| activity_table.appendChild(theader); |
| |
| let results_shown = 0; |
| for (const entry of all.block) { |
| let tr = _tr(); |
| let td_ip = _td(entry.ip); |
| td_ip.style.fontFamily = "monospace"; |
| if (entry.ip.length > 16) td_ip.style.fontSize = "0.8rem"; |
| let td_added = _td(moment(entry.timestamp*1000.0).fromNow()); |
| let td_expires = _td(entry.expires > 0 ? moment(entry.expires*1000.0).fromNow() : 'Never'); |
| let td_reason = _td(entry.reason); |
| let td_action = _td(); |
| td_action.appendChild(unblock_link(entry)); |
| tr.appendChild(td_ip); |
| tr.appendChild(td_added); |
| tr.appendChild(td_expires); |
| tr.appendChild(td_reason); |
| tr.appendChild(td_action); |
| activity_table.appendChild(tr); |
| results_shown++; |
| if (results_shown > 25 && all.block.length > 25) { |
| break |
| } |
| } |
| if (results_shown === 0) { |
| let tr = _tr(); |
| tr.innerText = "No activity found..."; |
| activity_table.appendChild(_tr); |
| } |
| |
| |
| } |
| |
| |
| async function prime_search(target, state) { |
| if (!state) { |
| window.history.pushState({}, '', `?search:${target}`); |
| } |
| let main = document.getElementById('main'); |
| main.innerHTML = ''; |
| let title = _h1("Search results for " + target + ":"); |
| main.appendChild(title); |
| let p = _p("Searching, please wait..."); |
| main.appendChild(p); |
| |
| let results = {}; |
| if (target && target.length > 0) { |
| results = await POST('search', {source: target}); |
| main.removeChild(p); |
| |
| |
| // Allow list results |
| let h2 = _h2(`Allow list results (${results.allow.length})`); |
| main.appendChild(h2); |
| let allow_table = _table(); |
| allow_table.style.tableLayout = 'fixed'; |
| main.appendChild(allow_table); |
| |
| let theader = _tr(); |
| theader.appendChild(_th('Source IP', 300)); |
| theader.appendChild(_th('Added', 120)); |
| theader.appendChild(_th('Expires', 120)); |
| theader.appendChild(_th('Reason', 500)); |
| theader.appendChild(_th('Actions', 100)); |
| allow_table.appendChild(theader); |
| |
| let results_shown = 0; |
| results.allow.sort((a,b) => b.timestamp - a.timestamp); // sort desc by timestamp |
| for (const entry of results.allow) { |
| let tr = _tr(); |
| let td_ip = _td(entry.ip); |
| let td_added = _td(moment(entry.timestamp * 1000.0).fromNow()); |
| let td_expires = _td(entry.expires > 0 ? moment(entry.expires * 1000.0).fromNow() : 'Never'); |
| let td_reason = _td(entry.reason); |
| let td_action = _td(); |
| td_action.appendChild(unblock_link(entry, true)); |
| td_ip.style.fontFamily = "monospace"; |
| if (entry.ip.length > 16) td_ip.style.fontSize = "0.8rem"; |
| tr.appendChild(td_ip); |
| tr.appendChild(td_added); |
| tr.appendChild(td_expires); |
| tr.appendChild(td_reason); |
| tr.appendChild(td_action); |
| allow_table.appendChild(tr); |
| results_shown++; |
| if (results_shown > 25 && results.block.length > 25) { |
| break |
| } |
| } |
| if (results_shown === 0) { |
| let tr = _tr(); |
| tr.innerText = "No results found..."; |
| allow_table.appendChild(tr); |
| } |
| |
| // Block list results |
| let bh2 = _h2(`Block list results (${results.block.length})`); |
| main.appendChild(bh2); |
| let block_table = _table(); |
| block_table.style.tableLayout = 'fixed'; |
| main.appendChild(block_table); |
| |
| let btheader = _tr(); |
| btheader.appendChild(_th('Source IP', 300)); |
| btheader.appendChild(_th('Added', 120)); |
| btheader.appendChild(_th('Expires', 120)); |
| btheader.appendChild(_th('Reason', 500)); |
| btheader.appendChild(_th('Actions', 100)); |
| block_table.appendChild(btheader); |
| |
| results_shown = 0; |
| results.block.sort((a,b) => b.timestamp - a.timestamp); // sort desc by timestamp |
| for (const entry of results.block) { |
| let tr = _tr(); |
| let td_ip = _td(entry.ip); |
| let td_added = _td(moment(entry.timestamp * 1000.0).fromNow()); |
| let td_expires = _td(entry.expires > 0 ? moment(entry.expires * 1000.0).fromNow() : 'Never'); |
| let td_reason = _td(entry.reason); |
| let td_actions = _td(''); |
| td_actions.appendChild(unblock_link(entry)); |
| td_ip.style.fontFamily = "monospace"; |
| if (entry.ip.length > 16) td_ip.style.fontSize = "0.8rem"; |
| tr.appendChild(td_ip); |
| tr.appendChild(td_added); |
| tr.appendChild(td_expires); |
| tr.appendChild(td_reason); |
| tr.appendChild(td_actions); |
| |
| block_table.appendChild(tr); |
| results_shown++; |
| if (results_shown > 25 && results.block.length > 25) { |
| break |
| } |
| } |
| if (results_shown === 0) { |
| let tr = _tr(); |
| tr.innerText = "No results found..."; |
| block_table.appendChild(tr); |
| } |
| |
| // iptables results |
| let ih2 = _h2(`Local iptables results (${results.iptables.length})`); |
| main.appendChild(ih2); |
| let iptables_table = _table(); |
| iptables_table.style.tableLayout = 'fixed'; |
| main.appendChild(iptables_table); |
| |
| let itheader = _tr(); |
| itheader.appendChild(_th('Source IP', 300)); |
| itheader.appendChild(_th('Host', 200)); |
| itheader.appendChild(_th('Chain', 100)); |
| itheader.appendChild(_th('Reason', 450)); |
| itheader.appendChild(_th('Actions', 100)); |
| iptables_table.appendChild(itheader); |
| |
| results_shown = 0; |
| for (const entry of results.iptables) { |
| let tr = _tr(); |
| let td_ip = _td(entry.source); |
| let td_host = _td(entry.hostname); |
| let td_chain = _td(entry.chain); |
| let td_reason = _td(entry.extensions.replace(/\/\*\s*(.+)\s*\*\//, (b,a) => a)); |
| let td_actions = _td(''); |
| td_ip.style.fontFamily = "monospace"; |
| if (entry.source.length > 16) td_ip.style.fontSize = "0.8rem"; |
| tr.appendChild(td_ip); |
| tr.appendChild(td_host); |
| tr.appendChild(td_chain); |
| tr.appendChild(td_reason); |
| tr.appendChild(td_actions); |
| |
| iptables_table.appendChild(tr); |
| results_shown++; |
| if (results_shown > 25 && results.block.length > 25) { |
| break |
| } |
| } |
| if (results_shown === 0) { |
| let tr = _tr(); |
| tr.innerText = "No results found..."; |
| iptables_table.appendChild(tr); |
| } |
| |
| } else { |
| main.innerText = "Use the search bar in the top left corner for now..."; |
| } |
| |
| } |
| |
| |
| let actions = { |
| frontpage: prime_frontpage, |
| search: prime_search |
| }; |
| |
| |
| async function prime(args) { |
| console.log(args); |
| let segs = location.search.substr(1).match(/^([a-z]*):?(.*)$/); |
| let action = segs[1]; |
| let params = segs[2]; |
| action_call = actions[action] ? actions[action] : actions['frontpage']; |
| await action_call(params, args ? args.state : null); |
| } |
| |
| window.onpopstate = prime; |