diff --git a/README.md b/README.md index b10c6f643c4bd6b6a1e4fed62f2b2e2af278dd4c..60e18e8fca46637199807dfc0e01e7f241d881eb 100644 --- a/README.md +++ b/README.md @@ -21,14 +21,27 @@ Extract the archive (or use git pull). We need to setup the url for the api of the IML Appmonitor instance. In `public_html/javascript` copy the dist file inc_config.js.dist to inc_config.js -Replace values in AM_SERVER_URL and AM_TAGS with your real data: +Replace values in the first object in the AM_INSTANCES array with your real data: ```js -const AM_SERVER_URL='https://appmonitor.example.com/api'; -const AM_TAGS='live,myapp'; +/** + * you can add multiple instances of appmonitor servers + */ +const AM_INSTANCES=[ + { + 'label': 'local Docker', + 'url': 'http://localhost:8001', + 'tags': 'monitoring', + 'user': 'api', + 'password': 'hello' + } +]; + const REFRESHTIME=30; // in sec ``` +You can multiple instances of appmonitor servers with creating an additional object here. You get a select box to switch between them. + Open the file `public_html/index.html` in your webbrowser. ## Troubleshooting ## diff --git a/public_html/javascript/functions.js b/public_html/javascript/functions.js index 666f1db97c33108336c7ce5c5c3052f6067662c0..a6e97b8ee78913adb6fdef0017f1745e4afa44c7 100644 --- a/public_html/javascript/functions.js +++ b/public_html/javascript/functions.js @@ -24,7 +24,7 @@ const OUT_ID_MAIN='header-section'; const OUT_ID_APPS='app-section'; const OUT_ID_TAGS='tag-section'; const ID_TAGINPUT='E_TAGS'; - +const ID_DELTAGS='button-disable-all-tags'; // callback object after changing a tag filter const FILTER_CALLBACK="oUbdApps.update()"; @@ -48,8 +48,8 @@ var AM_TAGLIST={}; */ function _getAMApiUrl(sPath){ return AM_PRETTYURL - ? AM_SERVER_URL+sPath - : AM_SERVER_URL+'/index.php?request='+sPath + ? AM_SERVER_URL+'/api'+sPath + : AM_SERVER_URL+'/api/index.php?request='+sPath ; } @@ -119,6 +119,7 @@ function _2digits(i){ } if(sLast!==sReturn){ document.getElementById(ID_TAGINPUT).value=sReturn; + document.getElementById(ID_DELTAGS).style.display=sReturn ? 'inline' : 'none'; eval(FILTER_CALLBACK); } } @@ -156,7 +157,7 @@ function _renderTaglist(aData){ : '' ); } - sReturn+='<button onclick="cbTagToggle(true);return false;"> ❌ </button>' + sReturn+='<button id="'+ID_DELTAGS+'" onclick="cbTagToggle(true);return false;"> '+AM_ICONS['delete']+' </button>' // sReturn+='<input id="'+ID_TAGINPUT+'" type="text" size="20" value="'+AM_TAGS+'"'+'>' sReturn+='<input id="'+ID_TAGINPUT+'" type="hidden" size="20" value="'+AM_TAGS+'">' AM_TAGLIST=_TAGLIST; @@ -187,9 +188,11 @@ function _getAllAppsStatus(aAllData){ +' (update every '+REFRESHTIME+' sec)' // +'</p>'; // let aAllData=JSON.parse(sData); + var appOut=''; for (var key in aAllData){ - sReturn+=_getSingleAppStatus(aAllData[key]); + appOut+=_getSingleAppStatus(aAllData[key]); } + sReturn+=appOut ? appOut : '<div class="app result2">NO DATA.<br>No application matches this combination of tags.</div>'; return sReturn; } @@ -262,44 +265,76 @@ function _getSingleAppStatus(aData){ } // ---------------------------------------------------------------------- -// MAIN +// init instance // ---------------------------------------------------------------------- -// for auth header with basic auth -let oHeaders=(AM_USER) - ? { "Authorization": "Basic " + btoa(AM_USER + ":" + AM_PASSWORD) } - : {} -; - -document.getElementById(OUT_ID_MAIN).innerHTML=''+AM_ICONS['connect']+' Connected instance <a href="'+AM_SERVER_URL+'">'+AM_SERVER_URL+'</a>'; - -// initialize tags -var oUbdTag=new ubd( - { - 'domid': OUT_ID_TAGS, - 'url': _getUrlForTags(), - 'header': { "headers": oHeaders }, - 'renderer': _renderTaglist, - 'ttl': 0, - } -); - -// initialize visible apps -var oUbdApps=new ubd( - { - 'domid': OUT_ID_APPS, - 'url': _getUrlWithTags, // remark: this is a function - 'header': { "headers": oHeaders }, - 'renderer': _getAllAppsStatus, - 'ttl': REFRESHTIME, - } -); +/** + * set a appmonitor instance that are defined in AM_INSTANCES + * see inc_config.js + * + * @param {integer} i number of instance to activate + */ +function initInstance(i){ + AM_SERVER_URL=AM_INSTANCES[i]['url']; + AM_TAGS=AM_INSTANCES[i]['tags']; + AM_USER=AM_INSTANCES[i]['user']; + AM_PASSWORD=AM_INSTANCES[i]['password']; + // for auth header with basic auth + let oHeaders=(AM_USER) + ? { "Authorization": "Basic " + btoa(AM_USER + ":" + AM_PASSWORD) } + : {} + ; -// fill in initial values ... and update based on given ttl -oUbdTag.update(); -oUbdApps.update(); + // initialize tags + oUbdTag=new ubd( + { + 'domid': OUT_ID_TAGS, + 'url': _getUrlForTags(), + 'header': { "headers": oHeaders }, + 'renderer': _renderTaglist, + 'ttl': 0, + } + ); + + // initialize visible apps + oUbdApps=new ubd( + { + 'domid': OUT_ID_APPS, + 'url': _getUrlWithTags, // remark: this is a function + 'header': { "headers": oHeaders }, + 'renderer': _getAllAppsStatus, + 'ttl': REFRESHTIME, + } + ); + + + let sInstances=''; + for (var j=0; j<AM_INSTANCES.length; j++){ + sInstances+='<option value="'+j+'" ' + +(i==j ? 'selected ' : '') + + '>'+AM_INSTANCES[j]['label']+'</option>'; + } + sInstances=sInstances + ? AM_ICONS['connect']+' Connected instance ' + + '<select onchange="initInstance(this.value); return false;">'+sInstances+'</select> ' + + ' <a href="'+AM_SERVER_URL+'">'+AM_SERVER_URL+'</a>' + : ''; + + document.getElementById(OUT_ID_MAIN).innerHTML= sInstances; + + // fill in initial values ... and update based on given ttl + window.setTimeout("oUbdTag.update();", 50); + window.setTimeout("oUbdApps.update();", 400); + + +} + +// ---------------------------------------------------------------------- +// MAIN +// ---------------------------------------------------------------------- +initInstance(0); // ---------------------------------------------------------------------- \ No newline at end of file diff --git a/public_html/javascript/inc_config.js.dist b/public_html/javascript/inc_config.js.dist index fb490522c81e494072c063dc6741e2e062ed113b..333c3ff0270a7d80f9acd1969f75eb9ba92cdcc7 100644 --- a/public_html/javascript/inc_config.js.dist +++ b/public_html/javascript/inc_config.js.dist @@ -1,8 +1,14 @@ -const AM_SERVER_URL='https://appmonitor.example.com/api'; -const AM_TAGS='live,myapp'; +/** + * you can add multiple instances of appmonitor servers + */ +const AM_INSTANCES=[ + { + 'label': 'local Docker', + 'url': 'http://localhost:8001', + 'tags': 'monitoring', + 'user': 'api', + 'password': 'hello' + } +]; -// optional: BASIC AUTH -const AM_USER=''; -const AM_PASSWORD=''; - -const REFRESHTIME=30; // in sec +const REFRESHTIME=30; // in sec \ No newline at end of file diff --git a/public_html/javascript/ubd.class.js b/public_html/javascript/ubd.class.js index 69ea642bfac355f2ebfc664243ab31c7c82e9f65..1ebb58290f98823e205a4d77a43022080f653fc5 100644 --- a/public_html/javascript/ubd.class.js +++ b/public_html/javascript/ubd.class.js @@ -15,22 +15,14 @@ * ====================================================================== */ -var ubd = function(){ - - this._sDomId=''; - this._oDomObject=false; - this._sUrl2Fetch=false; // static value or reference of a function - this._oHeader={}; - this._sRenderfunction=false; - this._iTTL=false; - - this._oTimer=false; - - this._body=''; - +/** + * Url binded to a dom id + * @return class + */ +class ubd { /** * initialize data for a dom object - * @parm {object} oConfig optional config object with those subkeys: + * @param {object} oConfig optional config object with those subkeys: * domid - id of a dom object * url - url to an api * header - http request header data @@ -38,25 +30,40 @@ var ubd = function(){ * ttl - ttl in sec (TODO) * @returns {undefined} */ - this.init = function(oConfig){ - if (oConfig){ - if(oConfig['domid']){ + constructor(oConfig) { + + this._sDomId = ''; + this._oDomObject = false; + this._sUrl2Fetch = false; // static value or reference of a function + this._oHeader = {}; + this._sRenderfunction = false; + this._iTTL = false; + + this._oTimer = false; + + this._body = ''; + + oConfig = arguments ? arguments[0] : false; + + + if (oConfig) { + if (oConfig['domid']) { this.setDomid(oConfig['domid']); } - if(oConfig['url']){ + if (oConfig['url']) { this.setUrl(oConfig['url']); } - if(oConfig['header']){ + if (oConfig['header']) { this.setHeaders(oConfig['header']); } - if(oConfig['renderer']){ + if (oConfig['renderer']) { this.setRenderfunction(oConfig['renderer']); } - if(oConfig['ttl']){ + if (oConfig['ttl']) { this.setTtl(oConfig['ttl']); } } - }, + } // ---------------------------------------------------------------------- // public SETTER for properties @@ -66,141 +73,130 @@ var ubd = function(){ * set domid that will by updated * @param {string} sDomid if of a domobject */ - this.setDomid = function(sDomid){ - if (document.getElementById(sDomid)){ - this._sDomId=sDomid; - this._oDomObject=document.getElementById(sDomid); - } else { - this._sDomId=false; - this._oDomObject=false; - console.error('ERROR: setDomid("'+sDomid+'") got an invalid string - this domid does not exist.'); - } - }, - + setDomid(sDomid) { + if (document.getElementById(sDomid)) { + this._sDomId = sDomid; + this._oDomObject = document.getElementById(sDomid); + } else { + this._sDomId = false; + this._oDomObject = false; + console.error('ERROR: setDomid("' + sDomid + '") got an invalid string - this domid does not exist.'); + } + } /** * set a rendering function that visualized data after a http request * @param {string|function} oFunction reference to a function ... or false to disable rendering */ - this.setRenderfunction = function(oFunction){ - this._sRenderfunction=oFunction; - }, + setRenderfunction (oFunction) { + this._sRenderfunction = oFunction; + } /** * Set time to live in seconds - * @param {int} iTTL + * @param {int} iTTL */ - this.setTtl = function(iTTL){ - this._iTTL=iTTL/1; + setTtl(iTTL) { + this._iTTL = iTTL / 1; this.resetTimer(); - }, + } /** * set an url to be requested * @param {string|function} sUrl static value or reference of a function */ - this.setUrl = function(sUrl){ - this._sUrl2Fetch=sUrl; - }, + setUrl(sUrl) { + this._sUrl2Fetch = sUrl; + } /** * set header obejct for 2nd param in javascript fetch() function - * @param {object} oHeader + * @param {object} oHeader */ - this.setHeaders = function(oHeader){ - this._oHeader=oHeader; - }, + setHeaders(oHeader) { + this._oHeader = oHeader; + } /** * helper: dump current object instance to console */ - this.dumpme = function(){ + dumpme() { console.log('---------- DUMP ubd'); console.log(this); console.log('---------- /DUMP ubd'); - }, + } // ---------------------------------------------------------------------- // public ACTIONS // ---------------------------------------------------------------------- - /** * show rendered html content into set domid using the render function. * If no rendering function was set then the response will be written * directly. * You can override both by giving a parameter (a string with html) * to write that one directly. It can be used to show an error message. - * + * * TODO: - * other output places than innerHTML by detecting the tag e.g. + * other output places than innerHTML by detecting the tag e.g. * to use input or textarea. - * + * * @param {string} sHtml optional: htmlcode of an error message */ - this.render = function(sHtml) { - let out = sHtml ? sHtml : - (this._sRenderfunction + render(sHtml) { + let out = sHtml ? sHtml : + (this._sRenderfunction ? this._sRenderfunction(this._body) : this._body ); - this._oDomObject.innerHTML=out; - }, + this._oDomObject.innerHTML = out; + } /** * reset timer to update the content in dom id after reaching TTL * used in setTtl */ - this.resetTimer = function(){ + resetTimer() { clearTimeout(this._oTimer); // clearInterval(this._oTimer); - if(this._iTTL) { + if (this._iTTL) { let self = this; - self._oTimer=window.setTimeout( function(){ self.update() }, this._iTTL*1000); + self._oTimer = window.setTimeout(function () { self.update(); }, this._iTTL * 1000); // self._oTimer=window.setInterval(self.update, this._iTTL*1000); } - }, + } /** * make http request and call the renderer */ - this.update = async function(){ + async update() { let self = this; - let url=( typeof this._sUrl2Fetch == "function" ) ? this._sUrl2Fetch() : this._sUrl2Fetch; + let url = (typeof this._sUrl2Fetch == "function") ? this._sUrl2Fetch() : this._sUrl2Fetch; console.log("update from url [" + url + "]"); - if (url == undefined){ + if (url == undefined) { console.error("SKIP update - there is no url in this object instance (anymore) :-/"); this.dumpme(); return 0; } - try{ + try { let response = await fetch(url, this._oHeader); if (response.ok) { - this._body=await response.json(); - + this._body = await response.json(); + this.render(); } else { this.render('<div class="app result1">' - +'ERROR '+response.status+': '+response.statusText + ' - ' - +url - +'</div>'); - } - } catch(e) { + + 'ERROR ' + response.status + ': ' + response.statusText + ' - ' + + url + + '</div>'); + } + } catch (e) { this.render('<div class="app result1">' - +'UNKNOWN: no response from ' - +this._sUrl2Fetch - +'</div>'); + + 'UNKNOWN: no response from ' + + this._sUrl2Fetch + + '</div>'); console.error(e); } this.resetTimer(); } - // ---------------------------------------------------------------------- - // MAIN - // ---------------------------------------------------------------------- - - if (arguments) { - this.init(arguments[0]); - } else { - this.init(); - } - -}; \ No newline at end of file + + } \ No newline at end of file diff --git a/public_html/main.css b/public_html/main.css index 16e92b27873b8e7c7d3f6da624802b316e4e9a95..f7617f9768137e36e09e9cfc1ee176ffecc1f2d4 100644 --- a/public_html/main.css +++ b/public_html/main.css @@ -1,6 +1,6 @@ :root{ --color-0: #345; - --color-h1: #abc; + --color-h1: #789; --color-h2: #579; --color-links: #349; @@ -19,8 +19,8 @@ body{ font-family: verdana,arial; } -button{ border: 1px solid rgba(0,0,0,0.1); background: linear-gradient(#f8f8f8, #ddd); border-radius: 0.3em; margin: 0 0.5em 0.5em 0; padding: 0.5em;} -button:hover{ background: linear-gradient(#eee, #ccc); border-radius: 0.3em; margin: 0 0.5em 0.5em 0; padding: 0.5em;} +button{ border: 1px solid rgba(0,0,0,0.1); background: linear-gradient(#f8f8f8, #ddd); border-radius: 0.3em; margin: 0 0.5em 0; padding: 0.5em;} +button:hover{ background: linear-gradient(#eee, #ccc);} button:active{ border:1px solid #fc2;} input{ border:1px solid #ccc; padding: 0.4em;} @@ -52,7 +52,15 @@ section input[type=checkbox]:checked + label { section input[type=checkbox]+ label:hover { border-color: #89a; } - +select{ + background: #fff; + border: 2px solid #f8f8f8; + color: var(--color-0); + font-size: 110%; +} +select:hover{ + background: #fec; +} td{vertical-align: top;} #main{