Skip to content
Snippets Groups Projects
Commit f33a69d4 authored by Hahn Axel (hahn)'s avatar Hahn Axel (hahn)
Browse files

Merge branch 'abstract_ubd_class' into 'master'

update js and css

See merge request !1
parents c48f3d98 5e0693ff
No related branches found
No related tags found
1 merge request!1update js and css
docs/images/screenshot_main.png

67.6 KiB | W: | H:

docs/images/screenshot_main.png

90.8 KiB | W: | H:

docs/images/screenshot_main.png
docs/images/screenshot_main.png
docs/images/screenshot_main.png
docs/images/screenshot_main.png
  • 2-up
  • Swipe
  • Onion skin
......@@ -13,10 +13,12 @@
<header>
<h1><span>📋</span> Appmonitor Dashboard</h1>
<p id="header-section"></p>
</header>
<section id="tag-section"></section>
<section id="app-section"></setion>
<h2><span>📢</span> App status</h2>
<section id="tag-section"><h2><span></span> Please wait ...</h2></section>
<section id="app-section"></section>
</div>
......@@ -26,6 +28,7 @@
<a href="https://github.com/iml-it/appmonitor" target="_blank">Appmonitor</a>
</footer>
<script src="javascript/ubd.class.js" type="text/javascript"></script>
<script src="javascript/inc_config.js" type="text/javascript"></script>
<script src="javascript/functions.js" type="text/javascript"></script>
......
/*
IML APPMONITOR DASHBOARD
*/
......@@ -16,32 +16,34 @@ const AM_RESULTS={
};
const AM_ICONS={
'wait': '',
'connect': '🔌',
'tag': '🏷️',
'app': '🪧',
'delete': '',
'result0': '',
'result1': '',
'result2': '',
'result3': '🔴',
};
const OUT_ID_MAIN='header-section';
const OUT_ID_APPS='app-section';
const OUT_ID_TAGS='tag-section';
const ID_TAGINPUT='E_TAGS';
// callback object after changing a tag filter
const FILTER_CALLBACK="oUbdApps.update()";
// ----------------------------------------------------------------------
// VARS
// ----------------------------------------------------------------------
var AM_TAGURL=false;
var AM_PRETTYURL=false;
var AM_TIMER=false;
// ----------------------------------------------------------------------
// FUNCTIONS
// ----------------------------------------------------------------------
/**
* generate a api url to the appmonitor
* @param {string} sPath
* @returns
*/
function _getAMApiUrl(sPath){
return AM_PRETTYURL
? AM_SERVER_URL+sPath
......@@ -62,7 +64,15 @@ function _getUrlForTags(){
* @param {string} tags list of tags - separated by comma
* @returns
*/
function _getUrlWithTags(tags){
function _getUrlWithTags(){
var o=document.getElementById(ID_TAGINPUT);
var tags="";
if(o) {
tags+=o.value
} else {
tags+=AM_TAGS
};
return _getAMApiUrl('/v1/apps/tags/'+tags+'/all');
}
......@@ -89,10 +99,15 @@ function _2digits(i){
// FUNCTIONS :: TAGS
// ----------------------------------------------------------------------
/**
* toggle a tag
* callback function of a button with a tagname
* @param {string} tagname
*/
function tagToggle(tagname){
var o=document.getElementById(ID_TAGINPUT);
var s=o.value;
var sLast=o.value;
var s=sLast;
var re = new RegExp(",*"+tagname);
var s2=s.replace(re, "");
if(s2!==s) {
......@@ -101,78 +116,53 @@ function tagToggle(tagname){
} else {
s+=(s ? "," : "" ) + tagname;
}
if(s!==sLast){
o.value=s;
getAppstatus();
// execute update of the app list
eval(FILTER_CALLBACK);
}
}
/**
* clear all tags
* callback function of the [x] button
*/
function tagClear(){
var o=document.getElementById(ID_TAGINPUT);
o.value="";
getAppstatus();
// execute update of the app list
eval(FILTER_CALLBACK);
}
/**
* called from getTags
* @param {string} sData JSOM Response
* @param {object} aData JSON Response
* @returns
*/
function _getTaglist(sData){
function _getTaglist(aData){
var sReturn='';
var sTags='';
let aData=JSON.parse(sData);
if(!aData['tags']){
aData=JSON.parse(aData);
}
// sReturn+='<code>'+sData+'</code><br>';
sReturn+='<input id="'+ID_TAGINPUT+'" type="text" size="20" value="'+AM_TAGS+'"'
+' onkeypress="getAppstatus()"'
+' onkeydown="getAppstatus()"'
+' onkeyup="getAppstatus()"'
+' onkeyup="eval(FILTER_CALLBACK)"'
+' onchange="eval(FILTER_CALLBACK)"'
+'>'
+'<button onclick="tagClear();return false;"> ❌ </button><br>'
;
for (var s in aData['tags']){
sTags+=(sTags ? ",": "") + aData['tags'][s];
sReturn+='<button onclick="tagToggle(\''+aData['tags'][s]+'\'); return false;">'+aData['tags'][s]+'</button>'
sReturn+='<button onclick="tagToggle(\''+aData['tags'][s]+'\'); return false;">'+AM_ICONS['tag'] + ' ' + aData['tags'][s]+'</button>'
}
return sReturn;
}
/**
* fetch appmonitor api - taglist
* called from getAppstatus
*/
async function getTags(){
AM_TAGURL=_getUrlForTags();
let out = '<h2><span>'+AM_ICONS['tag']+'</span> Tags</h2>';
try{
let response = await fetch(AM_TAGURL, { "headers":
AM_AUTH
} );
// let response = await fetch(AM_TAGURL);
if (response.ok) {
out+=_getTaglist(await response.text());
} else {
out+='<div class="app result1">'
+'ERROR '+response.status+': '+response.statusText + ' - '
+AM_TAGURL
+'</div>';
}
} catch {
out+='<div class="app result1">'
+'UNKNOWN: no response from '
+AM_TAGURL
+'</div>';
}
document.getElementById(OUT_ID_TAGS).innerHTML=out;
}
// ----------------------------------------------------------------------
// FUNCTIONS :: APPS
// ----------------------------------------------------------------------
......@@ -182,19 +172,19 @@ function _getTaglist(sData){
* @param {string} sData response body from api request (JSON string)
* @returns string
*/
function _getAllAppsStatus(sData){
function _getAllAppsStatus(aAllData){
var sReturn="";
var oDate=new Date;
// sReturn+='<code>'+sData+'</code><br>';
sReturn+=''
+'<h3>Tags: ' + tags + '</h3>'
+ '<p>'
// +'<h3>Tags: ' + tags + '</h3>'
// + '<p>'
+_2digits(oDate.getHours())
+':'+_2digits(oDate.getMinutes())
+':'+_2digits(oDate.getSeconds())
+' (update every '+REFRESHTIME+' sec)'
+'</p>';
let aAllData=JSON.parse(sData);
// +'</p>';
// let aAllData=JSON.parse(sData);
for (var key in aAllData){
sReturn+=_getSingleAppStatus(aAllData[key]);
}
......@@ -216,13 +206,17 @@ function toggleAppDetails(oLink){
* @returns string
*/
function _getSingleAppStatus(aData){
// let aData=JSON.parse(sData);
if(!aData.result){
return '';
}
// DEBUG
// console.log("----- _getSingleAppStatus")
// console.log(aData);
// ------ checks
let sChecks='';
if(aData.checks) {
for (var j=0; j<aData.checks.length; j++){
let tmpCheck=aData.checks[j];
sChecks+='<tr class="result'+tmpCheck.result+'">'
......@@ -232,14 +226,15 @@ function _getSingleAppStatus(aData){
+'</tr>'
;
}
sChecks=sChecks ? '<table>'+sChecks+'</table>' : ' (No checks were found)';
}
sChecks=sChecks ? '<table class="checks">'+sChecks+'</table>' : ' (No checks were found)';
// ----- generate output
let sReturn='<div class="app result'+ aData.meta.result +'">'
let sReturn='<div class="app result'+ aData.result.result +'">'
+'<div class="title" onclick="toggleAppDetails(this);">'
+'<span class="float-right">'+AM_RESULTS[aData.meta.result]+'</span>'
+'<span class="float-right">'+AM_RESULTS[aData.result.result]+'</span>'
+'<span class="float-right url">'+aData.result.url+'</span>'
+aData.meta.website
+aData.result.website
+'</div>'
;
......@@ -247,15 +242,16 @@ function _getSingleAppStatus(aData){
let iAge=Math.round((new Date()).getTime() / 1000)- aData.result.ts;
sReturn+=''
+'<table class="details" style="display: none;">'
+_appItem('Summary', 'Application status: '+ AM_RESULTS[aData.meta.result]
+ ' | Checks: ' + aData.checks.length
+'<div class="details" style="display: none;">'
+'<br><table>'
+_appItem('Summary', 'Application status: '+ AM_RESULTS[aData.result.result]
// + ' | Checks: ' + aData.checks.length
+ ' | Age: ' + iAge + ' sec'
+ ' | TTL: ' + aData.result.ttl
)
+_appItem('Checks', sChecks)
+'</table>'
+'</table></div>'
;
sReturn+='</div>';
......@@ -263,62 +259,48 @@ function _getSingleAppStatus(aData){
return sReturn;
}
/**
* fetch appmonitor api - status of apps
*/
async function getAppstatus(){
var o=document.getElementById(ID_TAGINPUT);
tags=o ? o.value : AM_TAGS;
let out = '<h2><span>'+AM_ICONS['app']+'</span> Applications</h2>';
let apiurl=_getUrlWithTags(tags)
// let myfunction="_getAllAppsStatus";
try{
let response = await fetch(apiurl, { "headers":
AM_AUTH
} );
if (response.ok) {
out+=_getAllAppsStatus(await response.text());
// out+=eval(myfunction+'(await '+response.text()+')');
} else {
out+='<div class="app result1">'
+'ERROR '+response.status+': '+response.statusText + ' - '
+apiurl
+'</div>';
}
}
catch {
out+='<div class="app result1">'
+'UNKNOWN: no response from '
+apiurl
+'</div>';
}
// }
document.getElementById(OUT_ID_APPS).innerHTML=out;
clearTimeout(AM_TIMER);
AM_TIMER=window.setTimeout("getAppstatus()", REFRESHTIME*1000);
}
// ----------------------------------------------------------------------
// MAIN
// ----------------------------------------------------------------------
// for auth header with basic auth
let AM_AUTH=(AM_USER)
let oHeaders=(AM_USER)
? { "Authorization": "Basic " + btoa(AM_USER + ":" + AM_PASSWORD) }
: {}
;
document.getElementById(OUT_ID_TAGS).innerHTML='<h2><span>'+AM_ICONS['wait']+'</span> wait ...</h2>';
document.getElementById(OUT_ID_APPS).innerHTML='<h2><span>'+AM_ICONS['wait']+'</span> wait ...</h2>';
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': _getTaglist,
'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': 0,
}
);
// fill in initial values
oUbdTag.update();
oUbdApps.update();
getTags();
AM_TIMER=window.setTimeout("getAppstatus()", 500);
// cyclic updates of the app status view
window.setInterval("oUbdApps.update();", REFRESHTIME*1000);
// ----------------------------------------------------------------------
\ No newline at end of file
......@@ -2,7 +2,7 @@ const AM_SERVER_URL='https://appmonitor.example.com/api';
const AM_TAGS='live,myapp';
// optional: BASIC AUTH
// const AM_USER='api';
// const AM_PASSWORD='password-of-api-user';
const AM_USER='';
const AM_PASSWORD='';
const REFRESHTIME=30; // in sec
/**
* ======================================================================
*
* U B D
*
* Url binded to a dom id
*
* ----------------------------------------------------------------------
*
* This is a helper class to update the content of a dom object by a
* given Url after a ttl.
*
* ----------------------------------------------------------------------
* 2022-06-26 www.axel-hahn.de first lines...
* ======================================================================
*/
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='';
/**
* initialize data for a dom object
* @parm {object} oConfig optional config object with those subkeys:
* domid - id of a dom object
* url - url to an api
* header - http request header data
* renderer - renderer function to visualize data
* ttl - ttl in sec (TODO)
* @returns {undefined}
*/
this.init = function(oConfig){
if(oConfig['domid']){
this.setDomid(oConfig['domid']);
}
if(oConfig['url']){
this.setUrl(oConfig['url']);
}
if(oConfig['header']){
this.setHeaders(oConfig['header']);
}
if(oConfig['renderer']){
this.setRenderfunction(oConfig['renderer']);
}
if(oConfig['ttl']){
this.setTtl(oConfig['ttl']);
}
},
// ----------------------------------------------------------------------
// public SETTER for properties
// ----------------------------------------------------------------------
/**
* 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.');
}
},
/**
* set a rendering function that visualized data after a http request
* @param {string|function} oFunction
*/
this.setRenderfunction = function(oFunction){
this._sRenderfunction=oFunction;
},
/**
* Set time to live in seconds
* @param {int} iTTL
*/
this.setTtl = function(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;
},
/**
* set header obejct for 2nd param in javascript fetch() function
* @param {object} oHeader
*/
this.setHeaders = function(oHeader){
this._oHeader=oHeader;
},
/**
* helper: dump current object instance to console
*/
this.dumpme = function(){
console.log('---------- DUMP ubd');
console.log(this);
console.log('---------- /DUMP ubd');
},
// ----------------------------------------------------------------------
// public ACTIONS
// ----------------------------------------------------------------------
/**
* show rendered html content into set domid using the render function
* Optionally you can set a string to display an error message.
*
* @param {string} sHtml optional: htmlcode of an error message
*/
this.render = function(sHtml) {
let out = sHtml ? sHtml : this._sRenderfunction(this._body);
this._oDomObject.innerHTML=out;
},
/**
* reset timer to update the content in dom id after reaching TTL
* used in setTtl
*
* WIP: repeating the update braks out from current instance.
* But what works is
* var oUbd=new ubd(...)
* by setting ttl = 0 and
* window.setInterval("oUbd.update();", 3000);
*/
this.resetTimer = function(){
clearTimeout(this._oTimer);
// clearInterval(this._oTimer);
if(this._iTTL) {
let self = this;
self._oTimer=window.setTimeout(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(){
let self = this;
let url=( typeof this._sUrl2Fetch == "function" ) ? this._sUrl2Fetch() : this._sUrl2Fetch;
console.log("update from url [" + url + "]");
if (url == undefined){
console.error("SKIP update - there is no url in this object instance (anymore) :-/");
this.dumpme();
return 0;
}
try{
let response = await fetch(url, self._oHeader);
if (response.ok) {
self._body=await response.json();
this.render();
} else {
this.render('<div class="app result1">'
+'ERROR '+response.status+': '+response.statusText + ' - '
+url
+'</div>');
}
} catch(e) {
this.render('<div class="app result1">'
+'UNKNOWN: no response from '
+this._sUrl2Fetch
+'</div>');
console.error(e);
}
}
// ----------------------------------------------------------------------
// MAIN
// ----------------------------------------------------------------------
if (arguments) {
this.init(arguments[0]);
}
};
\ No newline at end of file
:root{
--color-0: #345;
--color-h1: #c54;
......@@ -21,20 +19,21 @@ body{
font-family: verdana,arial;
}
button{ border: 1px solid rgba(0,0,0,0.05); background: linear-gradient(#f8f8f8, #eee); border-radius: 0.3em; margin: 0 0.5em 0.5em 0; padding: 0.5em;}
button:hover{ background: linear-gradient(#eee, #ddd); 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.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:active{ border:1px solid #fc2;}
input{ border:1px solid #ccc; padding: 0.4em;}
footer{ position: fixed; bottom: 0; left: 0; width: 100%; padding: 1em; background: var(--bg-footer); text-align: center;}
h1{color: var(--color-h1)}
h2{color: var(--color-h2); margin-left: -1em;}
footer{ position: fixed; bottom: 0; left: 0; width: 100%; padding: 1em; background: var(--bg-footer); border-top: 2px solid rgba(255,255,255,0.5); text-align: center;}
h1{color: var(--color-h1); border-bottom: 1px solid; background: linear-gradient(#fff, #f0f4f8);}
h2{color: var(--color-h2); margin-left: 0em;}
h1>span, h2>span{font-size: 200%;}
section{
margin: 0 0 2em ;
padding: 0.2em 2em;
border-top: 0px solid #abc;
padding: 1em 2em;
border-top: 0px dashed #e0e4f0;
background: linear-gradient(10deg, #fff, #f0f4f8, #fff);
border-radius: 1em;
}
......@@ -45,9 +44,10 @@ td{vertical-align: top;}
border: 3px solid #bbb;
border-radius: 1em;
box-shadow: 0 0 3em rgba(0,0,0,0.2);
margin: 1em 5% 6em;
margin: 1em auto 6em;
padding: 1em;
min-height: 35em;
max-width: 80em;
}
.app{
......@@ -62,9 +62,10 @@ td{vertical-align: top;}
.app .title{font-weight: bold; font-size: 130%; cursor: pointer;}
.app .url{font-size: 80%; font-weight: normal; margin-right: 2em;}
.app .details{}
.result0{background:#dfd !important; background: linear-gradient(#dfd,#beb)!important; color:#080}
.result0{background:#dfd !important; background: linear-gradient(#ded,#beb)!important; color:#383}
.result1{background:#eee !important; background: linear-gradient(#eee,#ddd)!important; color:#666;}
.result2{background:#fff8d0 !important; background: linear-gradient(#fff0d0,#ffe0a0)!important; color:#870;}
.result3{background:#fcd !important; background: linear-gradient(#fcd,#faa)!important; color:#800;}
table.checks{border: 2px solid rgba(0,0,0,0.1);}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment