From 958563a3602780b48599c27acf212139c2e6904d Mon Sep 17 00:00:00 2001 From: Lars-Dominik Braun Date: Sun, 14 Oct 2018 12:41:37 +0200 Subject: irc: Add PoC dashboard Using websockets, vue and bulma. --- contrib/dashboard.css | 7 +++ contrib/dashboard.html | 22 +++++++++ contrib/dashboard.js | 127 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+) create mode 100644 contrib/dashboard.css create mode 100644 contrib/dashboard.html create mode 100644 contrib/dashboard.js (limited to 'contrib') diff --git a/contrib/dashboard.css b/contrib/dashboard.css new file mode 100644 index 0000000..469db78 --- /dev/null +++ b/contrib/dashboard.css @@ -0,0 +1,7 @@ +.jid { + font-family: Consolas, mono; +} +.job .urls { + max-height: 10em; + overflow-y: scroll; +} diff --git a/contrib/dashboard.html b/contrib/dashboard.html new file mode 100644 index 0000000..cc09d50 --- /dev/null +++ b/contrib/dashboard.html @@ -0,0 +1,22 @@ + + + + + + chromebot dashboard + + + + + + + +
+

chromebot dashboard

+
+ +
+
+ + + diff --git a/contrib/dashboard.js b/contrib/dashboard.js new file mode 100644 index 0000000..eb34d43 --- /dev/null +++ b/contrib/dashboard.js @@ -0,0 +1,127 @@ +/* configuration */ +let socket = "ws://localhost:6789/", + urllogMax = 100; + +function formatSize (bytes) { + let prefixes = ['B', 'KiB', 'MiB', 'GiB', 'TiB']; + while (bytes >= 1024 && prefixes.length > 1) { + bytes /= 1024; + prefixes.shift (); + } + return bytes.toFixed (1) + ' ' + prefixes[0]; +} + +class Job { + constructor (id, url, user, queued) { + this.id = id; + this.url = url; + this.user = user; + this.status = undefined; + this.stats = {'pending': 0, 'have': 0, 'running': 0, + 'requests': 0, 'finished': 0, 'failed': 0, + 'bytesRcv': 0, 'crashed': 0, 'ignored': 0}; + this.urllog = []; + this.queued = queued; + this.started = undefined; + this.finished = undefined; + this.aborted = undefined; + } + + addUrl (url) { + if (this.urllog.push (url) > urllogMax) { + this.urllog.shift (); + } + } +} + +let jobs = {}; +/* list of ignored job ids, i.e. those the user deleted from the dashboard */ +let ignored = []; +let ws = new WebSocket(socket); +ws.onmessage = function (event) { + var msg = JSON.parse (event.data); + let msgdate = new Date (Date.parse (msg.date)); + var j = undefined; + console.log (msg); + if (msg.job) { + if (ignored.includes (msg.job)) { + console.log ("job ignored", msg.job); + return; + } + j = jobs[msg.job]; + if (j === undefined) { + j = new Job (msg.job, 'unknown', '', new Date ()); + Vue.set (jobs, msg.job, j); + } + } + if (msg.uuid == '36cc34a6-061b-4cc5-84a9-4ab6552c8d75') { + j = new Job (msg.job, msg.url, msg.user, msgdate); + /* jobs[msg.job] = j does not work with vue, see + https://vuejs.org/v2/guide/list.html#Object-Change-Detection-Caveats + */ + Vue.set (jobs, msg.job, j); + j.status = 'pending'; + } else if (msg.uuid == '46e62d60-f498-4ab0-90e1-d08a073b10fb') { + j.status = 'running'; + j.started = msgdate; + } else if (msg.uuid == '7b40ffbb-faab-4224-90ed-cd4febd8f7ec') { + j.status = 'finished'; + j.finished = msgdate; + } else if (msg.uuid == '865b3b3e-a54a-4a56-a545-f38a37bac295') { + j.status = 'aborted'; + j.aborted = msgdate; + } else if (msg.uuid == '5c0f9a11-dcd8-4182-a60f-54f4d3ab3687') { + /* forwarded job message */ + let rmsg = msg.data; + if (rmsg.uuid == '24d92d16-770e-4088-b769-4020e127a7ff') { + /* job status */ + Object.assign (j.stats, rmsg); + } else if (rmsg.uuid == '5b8498e4-868d-413c-a67e-004516b8452c') { + /* recursion status */ + Object.assign (j.stats, rmsg); + } else if (rmsg.uuid == '1680f384-744c-4b8a-815b-7346e632e8db') { + /* fetch */ + j.addUrl (rmsg.url); + } + } +}; +ws.onopen = function (event) { +}; +ws.onerror = function (event) { +}; + +Vue.component('job-item', { + props: ['job', 'jobs', 'ignored'], + template: '', + methods: { + del: function (id) { + Vue.delete(this.jobs, id); + this.ignored.push (id); + } + } +}); +Vue.component('job-status', { + props: ['job'], + template: 'queued on {{ job.queued.toLocaleString() }}aborted on {{ job.aborted.toLocaleString() }}running since {{ job.started.toLocaleString() }}finished since {{ job.finished.toLocaleString() }}' +}); +Vue.component('job-stats', { + props: ['job'], + template: '
  • {{ job.stats.have }} have
  • {{ job.stats.running }} running
  • {{ job.stats.pending }} pending
  • {{ job.stats.requests }} requests
' +}); +Vue.component('job-urls', { + props: ['job'], + template: '' +}); +Vue.component('filesize', { + props: ['value'], + template: '{{ fvalue }}', + computed: { fvalue: function () { return formatSize (this.value); } } +}); + +let app = new Vue({ + el: '#app', + data: { + jobs: jobs, + } +}); + -- cgit v1.2.3