1/* DokuWiki Monitor Plugin Script file */ 2/* 29.08.2025 - 1.0.5 - initial release */ 3/* Authors: Sascha Leib <ad@hominem.info> */ 4 5const Monitor = { 6 7 init: function() { 8 //console.info('Monitor.init()'); 9 10 // find the plugin basedir: 11 this._baseDir = document.currentScript.src.substring(0, document.currentScript.src.indexOf('/exe/')) 12 + '/plugins/monitor/'; 13 14 // read the page language from the DOM: 15 this._lang = document.getRootNode().documentElement.lang || this._lang; 16 17 // get the time offset: 18 this._timeDiff = Monitor.t._getTimeOffset(); 19 20 // init the sub-objects: 21 Monitor.t._callInit(this); 22 }, 23 24 _baseDir: null, 25 _lang: 'en', 26 _today: (new Date()).toISOString().slice(0, 10), 27 _timeDiff: '', 28 29 /* internal tools */ 30 t: { 31 32 /* helper function to call inits of sub-objects */ 33 _callInit: function(obj) { 34 //console.info('Monitor.t._callInit(obj=',obj,')'); 35 36 /* call init / _init on each sub-object: */ 37 Object.keys(obj).forEach( (key,i) => { 38 const sub = obj[key]; 39 let init = null; 40 if (typeof sub === 'object' && sub.init) { 41 init = sub.init; 42 } 43 44 // bind to object 45 if (typeof init == 'function') { 46 const init2 = init.bind(sub); 47 init2(obj); 48 } 49 }); 50 }, 51 52 /* helper function to calculate the time difference to UTC: */ 53 _getTimeOffset: function() { 54 const now = new Date(); 55 let offset = now.getTimezoneOffset(); // in minutes 56 const sign = Math.sign(offset); // +1 or -1 57 offset = Math.abs(offset); // always positive 58 59 let hours = 0; 60 while (offset >= 60) { 61 hours += 1; 62 offset -= 60; 63 } 64 return ( hours > 0 ? sign * hours + ' h' : '') + (offset > 0 ? ` ${offset} min` : ''); 65 } 66 } 67}; 68 69/* everything specific to the "Today" tab is self-contained here: */ 70Monitor.today = { 71 init: function() { 72 //console.info('Monitor.today.init()'); 73 74 // set the title: 75 const tDiff = (Monitor._timeDiff != '' ? ` (${Monitor._timeDiff})` : ' (<abbr>UTC</abbr>)' ); 76 Monitor.today.status.setTitle(`Showing visits for <time datetime=${Monitor._today}>${Monitor._today}</time>${tDiff}`); 77 78 // init sub-objects: 79 Monitor.t._callInit(this); 80 }, 81 82 data: { 83 init: function() { 84 //console.info('Monitor.today.data.init()'); 85 86 // call sub-inits: 87 Monitor.t._callInit(this); 88 89 // load the first log file: 90 Monitor.today.data.loadLogFile('srv'); 91 }, 92 93 bots: { 94 // loads the list of known bots from a JSON file: 95 init: async function() { 96 //console.info('Monitor.today.data.bots.init()'); 97 98 // Load the list of known bots: 99 Monitor.today.status.showBusy("Loading known bots …"); 100 const url = Monitor._baseDir + 'data/known-bots.json'; 101 102 console.log(url); 103 try { 104 const response = await fetch(url); 105 if (!response.ok) { 106 throw new Error(`${response.status} ${response.statusText}`); 107 } 108 109 Monitor.today.data.bots._list = await response.json(); 110 Monitor.today.data.bots._ready = true; 111 112 // TODO: allow using the bots list... 113 } catch (error) { 114 Monitor.today.status.setError("Error while loading the ’known bots’ file: " + error.message); 115 } finally { 116 Monitor.today.status.hideBusy("Done."); 117 } 118 }, 119 120 // returns bot info if the clientId matches a known bot, null otherwise: 121 match: function(clientId) { 122 123 // TODO! 124 }, 125 126 // indicates if the list is loaded and ready to use: 127 _ready: false, 128 129 // the actual bot list is stored here: 130 _list: [] 131 }, 132 133 loadLogFile: async function(type) { 134 console.info('Monitor.today.data.loadLogFile(',type,')'); 135 136 let typeName = ''; 137 let columns = []; 138 139 switch (type) { 140 case "srv": 141 typeName = "Server"; 142 columns = ['ts','ip','pg','id','usr','client']; 143 break; 144 break; 145 case "log": 146 typeName = "Page load"; 147 columns = ['ts','ip','pg','id','usr']; 148 break; 149 case "tck": 150 typeName = "Ticker"; 151 columns = ['ts','ip','pg','id']; 152 break; 153 default: 154 console.warn(`Unknown log type ${type}.`); 155 return; 156 } 157 158 // Load the list of known bots: 159 Monitor.today.status.showBusy(`Loading ${typeName} log file …`); 160 161 const url = Monitor._baseDir + `logs/${Monitor._today}.${type}`; 162 console.log("Loading:",url); 163 164 try { 165 const response = await fetch(url); 166 if (!response.ok) { 167 throw new Error(`${response.status} ${response.statusText}`); 168 } 169 170 const events = await response.text(); 171 console.log(events); 172 //Monitor.today.data.serverEvents._ready = true; 173 174 // TODO: parse the file... 175 } catch (error) { 176 Monitor.today.status.setError(`Error while loading the ${typeName} log file: ${error.message}.`); 177 } finally { 178 Monitor.today.status.hideBusy("Done."); 179 } 180 } 181 }, 182 183 status: { 184 setText: function(txt) { 185 const el = document.getElementById('monitor__today__status'); 186 if (el && Monitor.today.status._errorCount <= 0) { 187 el.innerText = txt; 188 } 189 }, 190 191 setTitle: function(html) { 192 const el = document.getElementById('monitor__today__title'); 193 if (el) { 194 el.innerHTML = html; 195 } 196 }, 197 198 setError: function(txt) { 199 console.error(txt); 200 Monitor.today.status._errorCount += 1; 201 const el = document.getElementById('monitor__today__status'); 202 if (el) { 203 el.innerText = "An error occured. See the browser log for details!"; 204 el.classList.add('error'); 205 } 206 }, 207 _errorCount: 0, 208 209 showBusy: function(txt = null) { 210 Monitor.today.status._busyCount += 1; 211 const el = document.getElementById('monitor__today__busy'); 212 if (el) { 213 el.style.display = 'inline-block'; 214 } 215 if (txt) Monitor.today.status.setText(txt); 216 }, 217 _busyCount: 0, 218 219 hideBusy: function(txt = null) { 220 const el = document.getElementById('monitor__today__busy'); 221 Monitor.today.status._busyCount -= 1; 222 if (Monitor.today.status._busyCount <= 0) { 223 if (el) el.style.display = 'none'; 224 if (txt) Monitor.today.status.setText(txt); 225 } 226 } 227 } 228}; 229 230/* check if the nustat admin panel is open: */ 231if (document.getElementById('monitor__admin')) { 232 Monitor.init(); 233}