-
+
Approve
-
+
diff --git a/server/website/templates/browsefile.handlebars b/server/website/templates/browsefile.handlebars
index 63bf5e2..678904f 100644
--- a/server/website/templates/browsefile.handlebars
+++ b/server/website/templates/browsefile.handlebars
@@ -6,7 +6,7 @@
diff --git a/server/website/templates/browsehost.handlebars b/server/website/templates/browsehost.handlebars
index ec7231a..d32985b 100644
--- a/server/website/templates/browsehost.handlebars
+++ b/server/website/templates/browsehost.handlebars
@@ -3,7 +3,7 @@
IP address {{ipAddress}}
-Last seen {{lastseen}}
+Last seen {{formatDateTime lastseen}}
OS {{os}}
OS edition {{osEdition}}
Kernel {{kernel}}
diff --git a/server/website/templates/latestnewmachines.handlebars b/server/website/templates/latestnewmachines.handlebars
index 0c4eaaa..8478ac6 100644
--- a/server/website/templates/latestnewmachines.handlebars
+++ b/server/website/templates/latestnewmachines.handlebars
@@ -1,3 +1,3 @@
-{{#each latestnewmachines}}
-{{hostname}} {{firstseen}}
+{{#each .}}
+{{hostname}} {{formatDateTime lastseen}}
{{/each}}
diff --git a/server/website/templates/search.handlebars b/server/website/templates/search.handlebars
index 434b5af..59dbf65 100644
--- a/server/website/templates/search.handlebars
+++ b/server/website/templates/search.handlebars
@@ -1,14 +1,18 @@
-{{numberOfHits}} hits
-{{#if hits}}:{{/if}}
+{{numberOfHits}} hits{{#if hits}}:{{/if}}
{{#each hits}}
-
- {{hostname}} :
-
- {{filename}}
- {{{excerpt}}}
+
+
{{/each}}
diff --git a/server/website/templates/systemstatus.handlebars b/server/website/templates/systemstatus.handlebars
index 8c629a7..1f89df5 100644
--- a/server/website/templates/systemstatus.handlebars
+++ b/server/website/templates/systemstatus.handlebars
@@ -1,10 +1,10 @@
{{filesLastHour}}
files were collected during the last hour.
-{{totalMachines}}
+{{numberOfMachines}}
machines are in the system.
-{{#if reportingPercentage}}
-{{reportingPercentage}}%
+{{#if numberOfMachines}}
+{{reportingPercentageLastHour}}%
of machines sent in files during the last hour.
{{/if}}
From 6584019772c5f0ca6e23a9b20113318729479e98 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=98yvind=20Hagberg?=
Date: Wed, 21 Feb 2018 14:41:06 +0100
Subject: [PATCH 08/16] Renamed "jobrunner" to "service"
---
.gitignore | 2 +-
rpm/nivlheim.spec | 2 +-
server/nivlheim.service | 2 +-
server/{jobrunner => service}/api.go | 0
server/{jobrunner => service}/api.txt | 0
server/{jobrunner => service}/api_awaitingApproval.go | 0
server/{jobrunner => service}/api_file.go | 0
server/{jobrunner => service}/api_host.go | 0
server/{jobrunner => service}/api_hostlist.go | 0
server/{jobrunner => service}/api_hostlist_test.go | 0
server/{jobrunner => service}/api_searchpage.go | 0
server/{jobrunner => service}/api_status.go | 0
server/{jobrunner => service}/scanFilesTable.go | 0
server/{jobrunner => service}/scanQueueDir.go | 0
server/{jobrunner => service}/taskrunner.go | 0
server/setup.sh | 8 ++++----
16 files changed, 7 insertions(+), 7 deletions(-)
rename server/{jobrunner => service}/api.go (100%)
rename server/{jobrunner => service}/api.txt (100%)
rename server/{jobrunner => service}/api_awaitingApproval.go (100%)
rename server/{jobrunner => service}/api_file.go (100%)
rename server/{jobrunner => service}/api_host.go (100%)
rename server/{jobrunner => service}/api_hostlist.go (100%)
rename server/{jobrunner => service}/api_hostlist_test.go (100%)
rename server/{jobrunner => service}/api_searchpage.go (100%)
rename server/{jobrunner => service}/api_status.go (100%)
rename server/{jobrunner => service}/scanFilesTable.go (100%)
rename server/{jobrunner => service}/scanQueueDir.go (100%)
rename server/{jobrunner => service}/taskrunner.go (100%)
diff --git a/.gitignore b/.gitignore
index 4ef0994..0eb8849 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
*.exe
server/web/web
-server/jobrunner/jobrunner
+server/service/service
server/website/libs
diff --git a/rpm/nivlheim.spec b/rpm/nivlheim.spec
index b7b153f..82b4619 100644
--- a/rpm/nivlheim.spec
+++ b/rpm/nivlheim.spec
@@ -134,7 +134,7 @@ install -p -m 0644 server/nivlheim.service %{buildroot}%{_unitdir}/%{name}.servi
install -p -m 0644 server/logrotate.conf %{buildroot}%{_sysconfdir}/logrotate.d/%{name}-server
install -p -m 0755 -D client/cron_hourly %{buildroot}%{_sysconfdir}/cron.hourly/nivlheim_client
cp -r server/web %{buildroot}%{_localstatedir}/nivlheim/go/src/
-cp -r server/jobrunner %{buildroot}%{_localstatedir}/nivlheim/go/src/
+cp -r server/service %{buildroot}%{_localstatedir}/nivlheim/go/src/
cp server/templates/* %{buildroot}/var/www/nivlheim/templates/
cp -r server/static/* %{buildroot}%{_localstatedir}/www/html/static/
echo %{version} > %{buildroot}%{_sysconfdir}/nivlheim/version
diff --git a/server/nivlheim.service b/server/nivlheim.service
index 1ddd554..54bbd29 100644
--- a/server/nivlheim.service
+++ b/server/nivlheim.service
@@ -4,7 +4,7 @@ Wants=network-online.target
After=network.target network-online.target
[Service]
-ExecStart=/usr/sbin/nivlheim_jobs
+ExecStart=/usr/sbin/nivlheim_service
Restart=always
RestartSec=10s
diff --git a/server/jobrunner/api.go b/server/service/api.go
similarity index 100%
rename from server/jobrunner/api.go
rename to server/service/api.go
diff --git a/server/jobrunner/api.txt b/server/service/api.txt
similarity index 100%
rename from server/jobrunner/api.txt
rename to server/service/api.txt
diff --git a/server/jobrunner/api_awaitingApproval.go b/server/service/api_awaitingApproval.go
similarity index 100%
rename from server/jobrunner/api_awaitingApproval.go
rename to server/service/api_awaitingApproval.go
diff --git a/server/jobrunner/api_file.go b/server/service/api_file.go
similarity index 100%
rename from server/jobrunner/api_file.go
rename to server/service/api_file.go
diff --git a/server/jobrunner/api_host.go b/server/service/api_host.go
similarity index 100%
rename from server/jobrunner/api_host.go
rename to server/service/api_host.go
diff --git a/server/jobrunner/api_hostlist.go b/server/service/api_hostlist.go
similarity index 100%
rename from server/jobrunner/api_hostlist.go
rename to server/service/api_hostlist.go
diff --git a/server/jobrunner/api_hostlist_test.go b/server/service/api_hostlist_test.go
similarity index 100%
rename from server/jobrunner/api_hostlist_test.go
rename to server/service/api_hostlist_test.go
diff --git a/server/jobrunner/api_searchpage.go b/server/service/api_searchpage.go
similarity index 100%
rename from server/jobrunner/api_searchpage.go
rename to server/service/api_searchpage.go
diff --git a/server/jobrunner/api_status.go b/server/service/api_status.go
similarity index 100%
rename from server/jobrunner/api_status.go
rename to server/service/api_status.go
diff --git a/server/jobrunner/scanFilesTable.go b/server/service/scanFilesTable.go
similarity index 100%
rename from server/jobrunner/scanFilesTable.go
rename to server/service/scanFilesTable.go
diff --git a/server/jobrunner/scanQueueDir.go b/server/service/scanQueueDir.go
similarity index 100%
rename from server/jobrunner/scanQueueDir.go
rename to server/service/scanQueueDir.go
diff --git a/server/jobrunner/taskrunner.go b/server/service/taskrunner.go
similarity index 100%
rename from server/jobrunner/taskrunner.go
rename to server/service/taskrunner.go
diff --git a/server/setup.sh b/server/setup.sh
index c63cb18..cefd3c0 100644
--- a/server/setup.sh
+++ b/server/setup.sh
@@ -83,16 +83,16 @@ sudo -u postgres bash -c "psql -c \"grant apache to root\""
sudo -u apache bash -c "psql < /var/nivlheim/init.sql"
# compile and install the Go code
-rm -f /usr/sbin/nivlheim_jobs
+rm -f /usr/sbin/nivlheim_service
rm -f /var/www/cgi-bin/frontpage.cgi
export GOPATH=/var/nivlheim/go
export GOBIN=$GOPATH/bin
#
-cd $GOPATH/src/jobrunner
+cd $GOPATH/src/service
go get || exit 1
go install || exit 1
-mv $GOBIN/jobrunner /usr/sbin/nivlheim_jobs
-chcon -t bin_t -u system_u /usr/sbin/nivlheim_jobs
+mv $GOBIN/service /usr/sbin/nivlheim_service
+chcon -t bin_t -u system_u /usr/sbin/nivlheim_service
#
cd $GOPATH/src/web
go get || exit 1
From 45f5b3bd70d7e94692332744f0b5a7d9698ae3cd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=98yvind=20Hagberg?=
Date: Wed, 21 Feb 2018 14:45:50 +0100
Subject: [PATCH 09/16] Removed the old web-, static-, and template-dirs
---
rpm/nivlheim.spec | 13 +-
server/httpd_ssl.conf | 6 +-
server/setup.sh | 7 --
server/static/background.png | Bin 7572 -> 0 bytes
server/static/intercooler-1.1.2.min.js | 2 -
server/static/jquery-3.2.1.min.js | 4 -
server/static/style.css | 77 ------------
server/templates/browsefile.html | 21 ----
server/templates/browsehost.html | 40 ------
server/templates/common_head.html | 6 -
server/templates/frontpage.html | 58 ---------
server/templates/searchpage.html | 36 ------
server/web/browse.go | 142 ----------------------
server/web/frontpage.go | 161 -------------------------
server/web/search.go | 117 ------------------
15 files changed, 9 insertions(+), 681 deletions(-)
delete mode 100644 server/static/background.png
delete mode 100644 server/static/intercooler-1.1.2.min.js
delete mode 100644 server/static/jquery-3.2.1.min.js
delete mode 100644 server/static/style.css
delete mode 100644 server/templates/browsefile.html
delete mode 100644 server/templates/browsehost.html
delete mode 100644 server/templates/common_head.html
delete mode 100644 server/templates/frontpage.html
delete mode 100644 server/templates/searchpage.html
delete mode 100644 server/web/browse.go
delete mode 100644 server/web/frontpage.go
delete mode 100644 server/web/search.go
diff --git a/rpm/nivlheim.spec b/rpm/nivlheim.spec
index 82b4619..894221d 100644
--- a/rpm/nivlheim.spec
+++ b/rpm/nivlheim.spec
@@ -110,9 +110,9 @@ mkdir -p %{buildroot}%{_sbindir}
mkdir -p %{buildroot}%{_sysconfdir}/nivlheim
mkdir -p %{buildroot}%{_sysconfdir}/httpd/conf.d
mkdir -p %{buildroot}%{_localstatedir}/nivlheim/go/{src,pkg,bin}
-mkdir -p %{buildroot}/var/www/nivlheim/templates
+mkdir -p %{buildroot}/var/www/nivlheim
mkdir -p %{buildroot}/var/www/cgi-bin/secure
-mkdir -p %{buildroot}/var/www/html/static
+mkdir -p %{buildroot}/var/www/html
mkdir -p %{buildroot}/var/log/nivlheim
mkdir -p %{buildroot}%{_unitdir}
mkdir -p %{buildroot}%{_sysconfdir}/logrotate.d
@@ -133,10 +133,8 @@ install -p -m 0755 server/cgi/parsefile %{buildroot}/var/www/cgi-bin/
install -p -m 0644 server/nivlheim.service %{buildroot}%{_unitdir}/%{name}.service
install -p -m 0644 server/logrotate.conf %{buildroot}%{_sysconfdir}/logrotate.d/%{name}-server
install -p -m 0755 -D client/cron_hourly %{buildroot}%{_sysconfdir}/cron.hourly/nivlheim_client
-cp -r server/web %{buildroot}%{_localstatedir}/nivlheim/go/src/
cp -r server/service %{buildroot}%{_localstatedir}/nivlheim/go/src/
-cp server/templates/* %{buildroot}/var/www/nivlheim/templates/
-cp -r server/static/* %{buildroot}%{_localstatedir}/www/html/static/
+cp -r server/website/* %{buildroot}%{_localstatedir}/www/html/
echo %{version} > %{buildroot}%{_sysconfdir}/nivlheim/version
%check
@@ -178,7 +176,7 @@ rm -rf %{buildroot}
%dir /var/log/nivlheim
/var/www/nivlheim
/var/www/cgi-bin
-/var/www/html/static
+/var/www/html
%attr(0644, root, apache) /var/www/nivlheim/log4perl.conf
%attr(0755, root, root) %{_localstatedir}/nivlheim/setup.sh
%{_localstatedir}/nivlheim
@@ -194,6 +192,9 @@ rm -rf %{buildroot}
%systemd_postun_with_restart %{name}.service
%changelog
+* Wed Feb 21 2018 Øyvind Hagberg - 0.1.4-20180221
+- New web frontend, installs in /var/www/html. frontpage.cgi is gone.
+
* Fri Jan 05 2018 Øyvind Hagberg - 0.1.1-20180105
- Removed dependencies on the missing parent package "nivlheim",
since it isn't being build anymore.
diff --git a/server/httpd_ssl.conf b/server/httpd_ssl.conf
index 5fe589f..bbf7709 100644
--- a/server/httpd_ssl.conf
+++ b/server/httpd_ssl.conf
@@ -28,11 +28,9 @@ SSLVerifyDepth 10
SSLRequire %{SSL_CLIENT_VERIFY} eq "SUCCESS"
-
- DirectoryIndex cgi-bin/frontpage.cgi
+
+ ProxyPass "http://127.0.0.1:4040/api/"
-ScriptAlias "/search" "/var/www/cgi-bin/frontpage.cgi"
-ScriptAlias "/browse" "/var/www/cgi-bin/frontpage.cgi"
diff --git a/server/setup.sh b/server/setup.sh
index cefd3c0..22a1de7 100644
--- a/server/setup.sh
+++ b/server/setup.sh
@@ -84,7 +84,6 @@ sudo -u apache bash -c "psql < /var/nivlheim/init.sql"
# compile and install the Go code
rm -f /usr/sbin/nivlheim_service
-rm -f /var/www/cgi-bin/frontpage.cgi
export GOPATH=/var/nivlheim/go
export GOBIN=$GOPATH/bin
#
@@ -93,12 +92,6 @@ go get || exit 1
go install || exit 1
mv $GOBIN/service /usr/sbin/nivlheim_service
chcon -t bin_t -u system_u /usr/sbin/nivlheim_service
-#
-cd $GOPATH/src/web
-go get || exit 1
-go install || exit 1
-mv $GOBIN/web /var/www/cgi-bin/frontpage.cgi
-chcon -t httpd_sys_script_exec_t -u system_u /var/www/cgi-bin/frontpage.cgi
# enable the systemd service
if which systemctl > /dev/null 2>&1; then
diff --git a/server/static/background.png b/server/static/background.png
deleted file mode 100644
index 84427a0c1d0dc88693b12af27917ccfb74ee6453..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 7572
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
diff --git a/server/static/intercooler-1.1.2.min.js b/server/static/intercooler-1.1.2.min.js
deleted file mode 100644
index 3c83fcc..0000000
--- a/server/static/intercooler-1.1.2.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! intercooler 1.1.2 2017-05-05 */
-!function(a,b){"function"==typeof define&&define.amd?define(["jquery"],function(c){return a.Intercooler=b(c)}):"object"==typeof exports?module.exports=b(require("jquery")):a.Intercooler=b(a.jQuery)}(this,function($){var Intercooler=Intercooler||function(){"use strict";function remove(a){a.remove()}function showIndicator(a){a.closest(".ic-use-transition").length>0?(a.data("ic-use-transition",!0),a.removeClass("ic-use-transition")):a.show()}function hideIndicator(a){a.data("ic-use-transition")?(a.data("ic-use-transition",null),a.addClass("ic-use-transition")):a.hide()}function fixICAttributeName(a){return USE_DATA?"data-"+a:a}function getICAttribute(a,b){return a.attr(fixICAttributeName(b))}function setICAttribute(a,b,c){a.attr(fixICAttributeName(b),c)}function prepend(a,b){try{a.prepend(b)}catch(b){log(a,formatError(b),"ERROR")}if(getICAttribute(a,"ic-limit-children")){var c=parseInt(getICAttribute(a,"ic-limit-children"));a.children().length>c&&a.children().slice(c,a.children().length).remove()}}function append(a,b){try{a.append(b)}catch(b){log(a,formatError(b),"ERROR")}if(getICAttribute(a,"ic-limit-children")){var c=parseInt(getICAttribute(a,"ic-limit-children"));a.children().length>c&&a.children().slice(0,a.children().length-c).remove()}}function triggerEvent(a,b,c){$.zepto&&(b=b.split(".").reverse().join(":")),a.trigger(b,c)}function log(a,b,c){if(null==a&&(a=$("body")),triggerEvent(a,"log.ic",[b,c,a]),"ERROR"==c){window.console&&window.console.log("Intercooler Error : "+b);var d=closestAttrValue($("body"),"ic-post-errors-to");d&&$.post(d,{error:b})}}function uuid(){return _UUID++}function icSelectorFor(a){return getICAttributeSelector("ic-id='"+getIntercoolerId(a)+"'")}function parseInterval(a){return log(null,"POLL: Parsing interval string "+a,"DEBUG"),"null"==a||"false"==a||""==a?null:a.lastIndexOf("ms")==a.length-2?parseFloat(a.substr(0,a.length-2)):a.lastIndexOf("s")==a.length-1?1e3*parseFloat(a.substr(0,a.length-1)):1e3}function getICAttributeSelector(a){return"["+fixICAttributeName(a)+"]"}function initScrollHandler(){null==_scrollHandler&&(_scrollHandler=function(){$(getICAttributeSelector("ic-trigger-on='scrolled-into-view'")).each(function(){var a=$(this);isScrolledIntoView(getTriggeredElement(a))&&1!=a.data("ic-scrolled-into-view-loaded")&&(a.data("ic-scrolled-into-view-loaded",!0),fireICRequest(a))})},$(window).scroll(_scrollHandler))}function currentUrl(){return window.location.pathname+window.location.search+window.location.hash}function createDocument(a){var b=null;return/<(html|body)/i.test(a)?(b=document.documentElement.cloneNode(),b.innerHTML=a):(b=document.documentElement.cloneNode(!0),b.querySelector("body").innerHTML=a),$(b)}function getTarget(a){return getTargetImpl(a,"ic-target")}function getTargetImpl(a,b){var c=$(a).closest(getICAttributeSelector(b)),d=getICAttribute(c,b);return"this"==d?c:d&&0!=d.indexOf("this.")?0==d.indexOf("closest ")?a.closest(d.substr(8)):0==d.indexOf("find ")?a.find(d.substr(5)):$(d):a}function processHeaders(elt,xhr){elt=$(elt),triggerEvent(elt,"beforeHeaders.ic",[elt,xhr]),log(elt,"response headers: "+xhr.getAllResponseHeaders(),"DEBUG");var target=null;if(xhr.getResponseHeader("X-IC-Title")&&(document.title=xhr.getResponseHeader("X-IC-Title")),xhr.getResponseHeader("X-IC-Refresh")){var pathsToRefresh=xhr.getResponseHeader("X-IC-Refresh").split(",");log(elt,"X-IC-Refresh: refreshing "+pathsToRefresh,"DEBUG"),$.each(pathsToRefresh,function(a,b){refreshDependencies(b.replace(/ /g,""),elt)})}if(xhr.getResponseHeader("X-IC-Script")&&(log(elt,"X-IC-Script: evaling "+xhr.getResponseHeader("X-IC-Script"),"DEBUG"),eval(xhr.getResponseHeader("X-IC-Script"))),xhr.getResponseHeader("X-IC-Redirect")&&(log(elt,"X-IC-Redirect: redirecting to "+xhr.getResponseHeader("X-IC-Redirect"),"DEBUG"),window.location=xhr.getResponseHeader("X-IC-Redirect")),"true"==xhr.getResponseHeader("X-IC-CancelPolling")&&cancelPolling(elt.closest(getICAttributeSelector("ic-poll"))),"true"==xhr.getResponseHeader("X-IC-ResumePolling")){var pollingElt=elt.closest(getICAttributeSelector("ic-poll"));setICAttribute(pollingElt,"ic-pause-polling",null),startPolling(pollingElt)}if(xhr.getResponseHeader("X-IC-SetPollInterval")){var pollingElt=elt.closest(getICAttributeSelector("ic-poll"));cancelPolling(pollingElt),setICAttribute(pollingElt,"ic-poll",xhr.getResponseHeader("X-IC-SetPollInterval")),startPolling(pollingElt)}xhr.getResponseHeader("X-IC-Open")&&(log(elt,"X-IC-Open: opening "+xhr.getResponseHeader("X-IC-Open"),"DEBUG"),window.open(xhr.getResponseHeader("X-IC-Open")));var triggerValue=xhr.getResponseHeader("X-IC-Trigger");if(triggerValue)if(log(elt,"X-IC-Trigger: found trigger "+triggerValue,"DEBUG"),target=getTarget(elt),xhr.getResponseHeader("X-IC-Trigger-Data")){var triggerArgs=$.parseJSON(xhr.getResponseHeader("X-IC-Trigger-Data"));triggerEvent(target,triggerValue,triggerArgs)}else triggerValue.indexOf("{")>=0?$.each($.parseJSON(triggerValue),function(a,b){triggerEvent(target,a,b)}):triggerEvent(target,triggerValue,[]);var localVars=xhr.getResponseHeader("X-IC-Set-Local-Vars");if(localVars&&$.each($.parseJSON(localVars),function(a,b){localStorage.setItem(a,b)}),xhr.getResponseHeader("X-IC-Remove")&&elt){var removeVal=xhr.getResponseHeader("X-IC-Remove");removeVal+="";var removeValAsInterval=parseInterval(removeVal);log(elt,"X-IC-Remove header found.","DEBUG"),target=getTarget(elt),"true"==removeVal||null==removeValAsInterval?remove(target):(target.addClass("ic-removing"),setTimeout(function(){remove(target)},removeValAsInterval))}return triggerEvent(elt,"afterHeaders.ic",[elt,xhr]),!0}function beforeRequest(a){a.addClass("disabled"),a.data("ic-request-in-flight",!0)}function requestCleanup(a,b){a.length>0&&hideIndicator(a),b.removeClass("disabled"),b.data("ic-request-in-flight",!1),b.data("ic-next-request")&&(b.data("ic-next-request").req(),b.data("ic-next-request",null))}function replaceOrAddMethod(a,b){if("string"===$.type(a)){var c=/(&|^)_method=[^&]*/,d="&_method="+b;return c.test(a)?a.replace(c,d):a+d}return a.append("_method",b),a}function globalEval(a){return window.eval.call(window,a)}function closestAttrValue(a,b){var c=$(a).closest(getICAttributeSelector(b));return c.length>0?getICAttribute(c,b):null}function formatError(a){var b=a.toString()+"\n";try{b+=a.stack}catch(a){}return b}function handleRemoteRequest(a,b,c,d,e){beforeRequest(a),d=replaceOrAddMethod(d,b);var f=findIndicator(a);f.length>0&&showIndicator(f);var g,h=uuid(),i=new Date;g=USE_ACTUAL_HTTP_METHOD?b:"GET"==b?"GET":"POST";var j={type:g,url:c,data:d,dataType:"text",headers:{Accept:"text/html-partial, */*; q=0.9","X-IC-Request":!0,"X-HTTP-Method-Override":b},beforeSend:function(e,f){triggerEvent(a,"beforeSend.ic",[a,d,f,e,h]),log(a,"before AJAX request "+h+": "+b+" to "+c,"DEBUG");var g=closestAttrValue(a,"ic-on-beforeSend");g&&globalEval("(function (data, settings, xhr) {"+g+"})")(d,f,e)},success:function(b,c,d){triggerEvent(a,"success.ic",[a,b,c,d,h]),log(a,"AJAX request "+h+" was successful.","DEBUG");var g=closestAttrValue(a,"ic-on-success");if(!g||0!=globalEval("(function (data, textStatus, xhr) {"+g+"})")(b,c,d)){var i=new Date;try{if(processHeaders(a,d)){log(a,"Processed headers for request "+h+" in "+(new Date-i)+"ms","DEBUG");var j=new Date;if(d.getResponseHeader("X-IC-PushURL")||"true"==closestAttrValue(a,"ic-push-url"))try{requestCleanup(f,a);var k=d.getResponseHeader("X-IC-PushURL")||closestAttrValue(a,"ic-src");if(!_history)throw"History support not enabled";_history.snapshotForHistory(k)}catch(b){log(a,"Error during history snapshot for "+h+": "+formatError(b),"ERROR")}e(b,c,a,d),log(a,"Process content for request "+h+" in "+(new Date-j)+"ms","DEBUG")}triggerEvent(a,"after.success.ic",[a,b,c,d,h])}catch(b){log(a,"Error processing successful request "+h+" : "+formatError(b),"ERROR")}}},error:function(b,d,e){triggerEvent(a,"error.ic",[a,d,e,b]);var f=closestAttrValue(a,"ic-on-error");f&&globalEval("(function (status, str, xhr) {"+f+"})")(d,e,b),processHeaders(a,b),log(a,"AJAX request "+h+" to "+c+" experienced an error: "+e,"ERROR")},complete:function(b,c){log(a,"AJAX request "+h+" completed in "+(new Date-i)+"ms","DEBUG"),requestCleanup(f,a);try{$.contains(document,a[0])?triggerEvent(a,"complete.ic",[a,d,c,b,h]):triggerEvent($("body"),"complete.ic",[a,d,c,b,h])}catch(b){log(a,"Error during complete.ic event for "+h+" : "+formatError(b),"ERROR")}var e=closestAttrValue(a,"ic-on-complete");e&&globalEval("(function (xhr, status) {"+e+"})")(b,c)}};"string"!=$.type(d)&&(j.dataType=null,j.processData=!1,j.contentType=!1),triggerEvent($(document),"beforeAjaxSend.ic",[j,a]),j.cancel?requestCleanup(f,a):$.ajax(j)}function findIndicator(a){var b=null;if(a=$(a),getICAttribute(a,"ic-indicator"))b=$(getICAttribute(a,"ic-indicator")).first();else if(b=a.find(".ic-indicator").first(),0==b.length){var c=closestAttrValue(a,"ic-indicator");c?b=$(c).first():a.next().is(".ic-indicator")&&(b=a.next())}return b}function processIncludes(a,b){if(0==$.trim(b).indexOf("{")){var c=$.parseJSON(b);$.each(c,function(b,c){a=appendData(a,b,c)})}else $(b).each(function(){var b=$(this).serializeArray();$.each(b,function(b,c){a=appendData(a,c.name,c.value)})});return a}function processLocalVars(a,b){return $(b.split(",")).each(function(){var b=$.trim(this),c=localStorage.getItem(b);c&&(a=appendData(a,b,c))}),a}function appendData(a,b,c){return"string"===$.type(a)?("string"!==$.type(c)&&(c=JSON.stringify(c)),a+"&"+b+"="+encodeURIComponent(c)):(a.append(b,c),a)}function getParametersForElement(a,b,c){var d=getTarget(b),e=null;if(b.is("form")&&"multipart/form-data"==b.attr("enctype"))e=new FormData(b[0]),e=appendData(e,"ic-request",!0);else{e="ic-request=true";var f=b.closest("form");if(b.is("form")||"GET"!=a&&f.length>0){e+="&"+f.serialize();var g=b.data("ic-last-clicked-button");g&&(e=appendData(e,g.name,g.value))}else e+="&"+b.serialize()}var h=closestAttrValue(b,"ic-prompt");if(h){var i=prompt(h);if(!i)return null;var j=closestAttrValue(b,"ic-prompt-name")||"ic-prompt-value";e=appendData(e,j,i)}b.attr("id")&&(e=appendData(e,"ic-element-id",b.attr("id"))),b.attr("name")&&(e=appendData(e,"ic-element-name",b.attr("name"))),getICAttribute(d,"ic-id")&&(e=appendData(e,"ic-id",getICAttribute(d,"ic-id"))),d.attr("id")&&(e=appendData(e,"ic-target-id",d.attr("id"))),c&&c.attr("id")&&(e=appendData(e,"ic-trigger-id",c.attr("id"))),c&&c.attr("name")&&(e=appendData(e,"ic-trigger-name",c.attr("name")));var k=closestAttrValue(b,"ic-include");k&&(e=processIncludes(e,k));var l=closestAttrValue(b,"ic-local-vars");l&&(e=processLocalVars(e,l)),$(getICAttributeSelector("ic-global-include")).each(function(){e=processIncludes(e,getICAttribute($(this),"ic-global-include"))}),e=appendData(e,"ic-current-url",currentUrl());var m=closestAttrValue(b,"ic-select-from-response");return m&&(e=appendData(e,"ic-select-from-response",m)),log(b,"request parameters "+e,"DEBUG"),e}function maybeSetIntercoolerInfo(a){var b=getTarget(a);getIntercoolerId(b),1!=a.data("elementAdded.ic")&&(a.data("elementAdded.ic",!0),triggerEvent(a,"elementAdded.ic"))}function getIntercoolerId(a){return getICAttribute(a,"ic-id")||setICAttribute(a,"ic-id",uuid()),getICAttribute(a,"ic-id")}function processNodes(a){a=$(a),a.length>1?a.each(function(){processNodes(this)}):(processMacros(a),processSources(a),processPolling(a),processEventSources(a),processTriggerOn(a),processRemoveAfter(a),processAddClasses(a),processRemoveClasses(a))}function fireReadyStuff(a){triggerEvent(a,"nodesProcessed.ic"),$.each(_readyHandlers,function(b,c){try{c(a)}catch(b){log(a,formatError(b),"ERROR")}})}function autoFocus(a){a.find("[autofocus]").last().focus()}function processMacros(a){$.each(_MACROS,function(b,c){0==a.closest(".ic-ignore").length&&(a.is("["+c+"]")&&processMacro(c,a),a.find("["+c+"]").each(function(){var a=$(this);0==a.closest(".ic-ignore").length&&processMacro(c,a)}))})}function processSources(a){0==a.closest(".ic-ignore").length&&(a.is(getICAttributeSelector("ic-src"))&&maybeSetIntercoolerInfo(a),a.find(getICAttributeSelector("ic-src")).each(function(){var a=$(this);0==a.closest(".ic-ignore").length&&maybeSetIntercoolerInfo(a)}))}function processPolling(a){0==a.closest(".ic-ignore").length&&(a.is(getICAttributeSelector("ic-poll"))&&(maybeSetIntercoolerInfo(a),startPolling(a)),a.find(getICAttributeSelector("ic-poll")).each(function(){var a=$(this);0==a.closest(".ic-ignore").length&&(maybeSetIntercoolerInfo(a),startPolling(a))}))}function processTriggerOn(a){0==a.closest(".ic-ignore").length&&(handleTriggerOn(a),a.find(getICAttributeSelector("ic-trigger-on")).each(function(){var a=$(this);0==a.closest(".ic-ignore").length&&handleTriggerOn(a)}))}function processRemoveAfter(a){0==a.closest(".ic-ignore").length&&(handleRemoveAfter(a),a.find(getICAttributeSelector("ic-remove-after")).each(function(){var a=$(this);0==a.closest(".ic-ignore").length&&handleRemoveAfter(a)}))}function processAddClasses(a){0==a.closest(".ic-ignore").length&&(handleAddClasses(a),a.find(getICAttributeSelector("ic-add-class")).each(function(){var a=$(this);0==a.closest(".ic-ignore").length&&handleAddClasses(a)}))}function processRemoveClasses(a){0==a.closest(".ic-ignore").length&&(handleRemoveClasses(a),a.find(getICAttributeSelector("ic-remove-class")).each(function(){var a=$(this);0==a.closest(".ic-ignore").length&&handleRemoveClasses(a)}))}function processEventSources(a){0==a.closest(".ic-ignore").length&&(handleEventSource(a),a.find(getICAttributeSelector("ic-sse-src")).each(function(){var a=$(this);0==a.closest(".ic-ignore").length&&handleEventSource(a)}))}function startPolling(a){if(null==a.data("ic-poll-interval-id")&&"true"!=getICAttribute(a,"ic-pause-polling")){var b=parseInterval(getICAttribute(a,"ic-poll"));if(null!=b){var c=icSelectorFor(a),d=parseInt(getICAttribute(a,"ic-poll-repeats"))||-1,e=0;log(a,"POLL: Starting poll for element "+c,"DEBUG");var f=setInterval(function(){var b=$(c);triggerEvent(a,"onPoll.ic",b),0==b.length||e==d||a.data("ic-poll-interval-id")!=f?(log(a,"POLL: Clearing poll for element "+c,"DEBUG"),clearTimeout(f)):fireICRequest(b),e++},b);a.data("ic-poll-interval-id",f)}}}function cancelPolling(a){null!=a.data("ic-poll-interval-id")&&(clearTimeout(a.data("ic-poll-interval-id")),a.data("ic-poll-interval-id",null))}function refreshDependencies(a,b){log(b,"refreshing dependencies for path "+a,"DEBUG"),$(getICAttributeSelector("ic-src")).each(function(){var c=!1,d=$(this);"GET"==verbFor(d)&&"ignore"!=getICAttribute(d,"ic-deps")&&(isDependent(a,getICAttribute(d,"ic-src"))?null!=b&&$(b)[0]==d[0]||(fireICRequest(d),c=!0):(isICDepsDependent(a,getICAttribute(d,"ic-deps"))||"*"==getICAttribute(d,"ic-deps"))&&(null!=b&&$(b)[0]==d[0]||(fireICRequest(d),c=!0))),c&&log(d,"depends on path "+a+", refreshing...","DEBUG")})}function isICDepsDependent(a,b){if(b)for(var c=b.split(","),d=0;d0){var f=a.split(":");d=f[0],e=parseInterval(f[1])}else d=a;setTimeout(function(){b[c](d)},e)}function handleAddClasses(a){if(a=$(a),getICAttribute(a,"ic-add-class"))for(var b=getICAttribute(a,"ic-add-class").split(","),c=b.length,d=0;d=0?eval(triggerFrom):triggerFrom):elt}function handleTriggerOn(a){if(getICAttribute(a,"ic-trigger-on"))if(a.is("form")&&a.find("input, button").on("click focus",function(b){$(this).is('input[type="submit"], button')&&$(this).is("[name]")?a.data("ic-last-clicked-button",{name:$(this).attr("name"),value:$(this).val()}):a.data("ic-last-clicked-button",null)}),"load"==getICAttribute(a,"ic-trigger-on"))fireICRequest(a);else if("scrolled-into-view"==getICAttribute(a,"ic-trigger-on"))initScrollHandler(),setTimeout(function(){triggerEvent($(window),"scroll")},100);else{var b=getICAttribute(a,"ic-trigger-on").split(" ");if(0==b[0].indexOf("sse:")){var c=a.closest(getICAttributeSelector("ic-sse-src"));c&®isterSSE(c,b[0].substr(4))}else $(getTriggeredElement(a)).on(eventFor(b[0],a),function(c){var d=closestAttrValue(a,"ic-on-beforeTrigger");if(d&&0==globalEval("(function (evt, elt) {"+d+"})")(c,a))return log(a,"ic-trigger cancelled by ic-on-beforeTrigger","DEBUG"),!1;if("changed"==b[1]){var e=a.val(),f=a.data("ic-previous-val");a.data("ic-previous-val",e),e!=f&&fireICRequest(a)}else fireICRequest(a);return!preventDefault(a,c)||(c.preventDefault(),!1)})}}function macroIs(a,b){return a==fixICAttributeName(b)}function processMacro(a,b){macroIs(a,"ic-post-to")&&(setIfAbsent(b,"ic-src",getICAttribute(b,"ic-post-to")),setIfAbsent(b,"ic-verb","POST"),setIfAbsent(b,"ic-trigger-on","default"),setIfAbsent(b,"ic-deps","ignore")),macroIs(a,"ic-put-to")&&(setIfAbsent(b,"ic-src",getICAttribute(b,"ic-put-to")),setIfAbsent(b,"ic-verb","PUT"),setIfAbsent(b,"ic-trigger-on","default"),setIfAbsent(b,"ic-deps","ignore")),macroIs(a,"ic-patch-to")&&(setIfAbsent(b,"ic-src",getICAttribute(b,"ic-patch-to")),setIfAbsent(b,"ic-verb","PATCH"),setIfAbsent(b,"ic-trigger-on","default"),setIfAbsent(b,"ic-deps","ignore")),macroIs(a,"ic-get-from")&&(setIfAbsent(b,"ic-src",getICAttribute(b,"ic-get-from")),setIfAbsent(b,"ic-trigger-on","default"),setIfAbsent(b,"ic-deps","ignore")),macroIs(a,"ic-delete-from")&&(setIfAbsent(b,"ic-src",getICAttribute(b,"ic-delete-from")),setIfAbsent(b,"ic-verb","DELETE"),setIfAbsent(b,"ic-trigger-on","default"),setIfAbsent(b,"ic-deps","ignore")),macroIs(a,"ic-action")&&setIfAbsent(b,"ic-trigger-on","default");var c=null,d=null;if(macroIs(a,"ic-style-src")){c=getICAttribute(b,"ic-style-src").split(":");var e=c[0];d=c[1],setIfAbsent(b,"ic-src",d),setIfAbsent(b,"ic-target","this.style."+e)}if(macroIs(a,"ic-attr-src")){c=getICAttribute(b,"ic-attr-src").split(":");var f=c[0];d=c[1],setIfAbsent(b,"ic-src",d),setIfAbsent(b,"ic-target","this."+f)}macroIs(a,"ic-prepend-from")&&(setIfAbsent(b,"ic-src",getICAttribute(b,"ic-prepend-from")),setIfAbsent(b,"ic-swap-style","prepend")),macroIs(a,"ic-append-from")&&(setIfAbsent(b,"ic-src",getICAttribute(b,"ic-append-from")),setIfAbsent(b,"ic-swap-style","append"))}function setIfAbsent(a,b,c){null==getICAttribute(a,b)&&setICAttribute(a,b,c)}function isScrolledIntoView(a){if(a=$(a),0==a.height()&&0==a.width())return!1;var b=$(window).scrollTop(),c=b+$(window).height(),d=a.offset().top,e=d+a.height();return e>=b&&d<=c&&e<=c&&d>=b}function maybeScrollToTarget(a,b){if("false"!=closestAttrValue(a,"ic-scroll-to-target")&&("true"==closestAttrValue(a,"ic-scroll-to-target")||"true"==closestAttrValue(b,"ic-scroll-to-target"))){var c=-50;closestAttrValue(a,"ic-scroll-offset")?c=parseInt(closestAttrValue(a,"ic-scroll-offset")):closestAttrValue(b,"ic-scroll-offset")&&(c=parseInt(closestAttrValue(b,"ic-scroll-offset")));var d=b.offset().top,e=$(window).scrollTop(),f=e+window.innerHeight;(df)&&(c+=d,$("html,body").animate({scrollTop:c},400))}}function getTransitionDuration(a,b){var c=closestAttrValue(a,"ic-transition-duration");if(c)return parseInterval(c);if(c=closestAttrValue(b,"ic-transition-duration"))return parseInterval(c);b=$(b);var d=0,e=b.css("transition-duration");e&&(d+=parseInterval(e));var f=b.css("transition-delay");return f&&(d+=parseInterval(f)),d}function closeSSESource(a){var b=a.data("ic-event-sse-source");try{b&&b.close()}catch(b){log(a,"Error closing ServerSentEvent source"+b,"ERROR")}}function beforeSwapCleanup(a){a.find(getICAttributeSelector("ic-sse-src")).each(function(){closeSSESource($(this))}),triggerEvent(a,"beforeSwap.ic")}function processICResponse(a,b,c){if(a&&""!=a&&" "!=a){log(b,"response content: \n"+a,"DEBUG");var d=getTarget(b),e=maybeFilter(a,closestAttrValue(b,"ic-select-from-response")),f=function(){if("true"==closestAttrValue(b,"ic-replace-target")){try{beforeSwapCleanup(d),closeSSESource(d),d.replaceWith(e),d=e}catch(a){log(b,formatError(a),"ERROR")}processNodes(e),fireReadyStuff(d),autoFocus(d)}else{if("prepend"==getICAttribute(b,"ic-swap-style"))prepend(d,e),processNodes(e),fireReadyStuff(d),autoFocus(d);else if("append"==getICAttribute(b,"ic-swap-style"))append(d,e),processNodes(e),fireReadyStuff(d),autoFocus(d);else{try{beforeSwapCleanup(d),d.empty().append(e)}catch(a){log(b,formatError(a),"ERROR")}d.children().each(function(){processNodes(this)}),fireReadyStuff(d),autoFocus(d)}1!=c&&maybeScrollToTarget(b,d)}};if(0==d.length)return void log(b,"Invalid target for element: "+getICAttribute(b.closest(getICAttributeSelector("ic-target")),"ic-target"),"ERROR");var g=getTransitionDuration(b,d);d.addClass("ic-transitioning"),setTimeout(function(){try{f()}catch(a){log(b,"Error during content swap : "+formatError(a),"ERROR")}setTimeout(function(){try{d.removeClass("ic-transitioning"),_history&&_history.updateHistory(),triggerEvent(d,"complete_transition.ic",[d])}catch(a){log(b,"Error during transition complete : "+formatError(a),"ERROR")}},20)},g)}else log(b,"Empty response, nothing to do here.","DEBUG")}function maybeFilter(a,b){var c;if($.zepto){var d=createDocument(a);c=$(d).find("body").contents()}else c=$($.parseHTML(a,null,!0));return b?c.filter(b).add(c.find(b)).contents():c}function getStyleTarget(a){var b=closestAttrValue(a,"ic-target");return b&&0==b.indexOf("this.style.")?b.substr(11):null}function getAttrTarget(a){var b=closestAttrValue(a,"ic-target");return b&&0==b.indexOf("this.")?b.substr(5):null}function fireICRequest(a,b){a=$(a);var c=a;a.is(getICAttributeSelector("ic-src"))||void 0!=getICAttribute(a,"ic-action")||(a=a.closest(getICAttributeSelector("ic-src")));var d=closestAttrValue(a,"ic-confirm");if((!d||confirm(d))&&a.length>0){var e=uuid();a.data("ic-event-id",e);var f=function(){if(1==a.data("ic-request-in-flight"))return void a.data("ic-next-request",{req:f});if(a.data("ic-event-id")==e){var d=getStyleTarget(a),g=d?null:getAttrTarget(a),h=verbFor(a),i=getICAttribute(a,"ic-src");if(i){var j=b||function(b){d?a.css(d,b):g?a.attr(g,b):(processICResponse(b,a),"GET"!=h&&refreshDependencies(getICAttribute(a,"ic-src"),a))},k=getParametersForElement(h,a,c);k&&handleRemoteRequest(a,h,i,k,j)}var l=getICAttribute(a,"ic-action");l&&invokeLocalAction(a,l)}},g=closestAttrValue(a,"ic-trigger-delay");g?setTimeout(f,parseInterval(g)):f()}}function invokeLocalAction(a,b){var c=closestAttrValue(a,"ic-action-target"),d=null;d=c?getTargetImpl(a,"ic-action-target"):getTarget(a);var e=b.split(";"),f=[],g=0;$.each(e,function(a,b){var c=$.trim(b),e=c,h=[];c.indexOf(":")>0&&(e=c.substr(0,c.indexOf(":")),h=computeArgs(c.substr(c.indexOf(":")+1,c.length))),""==e||("delay"==e?(null==g&&(g=0),g+=parseInterval(h[0]+"")):(null==g&&(g=420),f.push([g,makeApplyAction(d,e,h)]),g=null))}),g=0,$.each(f,function(a,b){g+=b[0],setTimeout(b[1],g)})}function computeArgs(args){try{return eval("["+args+"]")}catch(a){return[$.trim(args)]}}function makeApplyAction(a,b,c){return function(){var d=a[b]||window[b];d?d.apply(a,c):log(a,"Action "+b+" was not found","ERROR")}}function newIntercoolerHistory(a,b,c,d){function e(a){return null==a||a.slotLimit!=c||a.historyVersion!=d||null==a.lruList}function f(){for(var b=[],e=0;e=0)log(e,"URL found in LRU list, moving to end","INFO"),c.splice(d,1),c.push(b);else if(log(e,"URL not found in LRU list, adding","INFO"),c.push(b),c.length>t.slotLimit){var f=c.shift();log(e,"History overflow, removing local history for "+f,"INFO"),a.removeItem(s+f)}return a.setItem(r,JSON.stringify(t)),c}function h(b){var d=JSON.stringify(b);try{a.setItem(b.id,d)}catch(e){try{f(),a.setItem(b.id,d)}catch(a){log(n($("body")),"Unable to save intercooler history with entire history cleared, is something else eating local storage? History Limit:"+c,"ERROR")}}}function i(a,b,c){var d={url:c,id:s+c,content:a,yOffset:b,timestamp:(new Date).getTime()};return g(c),h(d),d}function j(a){if(null==a.onpopstate||1!=a.onpopstate["ic-on-pop-state-handler"]){var b=a.onpopstate;a.onpopstate=function(a){triggerEvent(n($("body")),"handle.onpopstate.ic"),m(a)||b&&b(a),triggerEvent(n($("body")),"pageLoad.ic")},a.onpopstate["ic-on-pop-state-handler"]=!0}}function k(){u&&(l(u.newUrl,currentUrl(),u.oldHtml,u.yOffset),u=null)}function l(a,c,d,e){var f=i(d,e,c);b.replaceState({"ic-id":f.id},"","");var g=n($("body")),h=i(g.html(),window.pageYOffset,a);b.pushState({"ic-id":h.id},"",a),triggerEvent(g,"pushUrl.ic",[g,h])}function m(b){var c=b.state;if(c&&c["ic-id"]){var d=JSON.parse(a.getItem(c["ic-id"]));if(d)return processICResponse(d.content,n($("body")),!0),d.yOffset&&window.scrollTo(0,d.yOffset),!0;$.get(currentUrl(),{"ic-restore-history":!0},function(a,b){var c=createDocument(a),d=n(c).html();processICResponse(d,n($("body")),!0)})}return!1}function n(a){var b=a.find(getICAttributeSelector("ic-history-elt"));return b.length>0?b:a}function o(a){var b=n($("body"));triggerEvent(b,"beforeHistorySnapshot.ic",[b]),u={newUrl:a,oldHtml:b.html(),yOffset:window.pageYOffset}}function p(){var b="",c=[];for(var d in a)c.push(d);c.sort();var e=0;for(var f in c){var g=2*a[c[f]].length;e+=g,b+=c[f]+"="+(g/1024/1024).toFixed(2)+" MB\n"}return b+"\nTOTAL LOCAL STORAGE: "+(e/1024/1024).toFixed(2)+" MB"}function q(){return t}var r="ic-history-support",s="ic-hist-elt-",t=JSON.parse(a.getItem(r)),u=null;return e(t)&&(log(n($("body")),"Intercooler History configuration changed, clearing history","INFO"),f()),null==t&&(t={slotLimit:c,historyVersion:d,lruList:[]}),{clearHistory:f,updateHistory:k,addPopStateHandler:j,snapshotForHistory:o,_internal:{addPopStateHandler:j,supportData:q,dumpLocalStorage:p,updateLRUList:g}}}function getSlotLimit(){return 20}function refresh(a){return"string"==typeof a||a instanceof String?refreshDependencies(a):fireICRequest(a),Intercooler}function init(){var a=$("body");processNodes(a),fireReadyStuff(a),_history&&_history.addPopStateHandler(window),$.zepto&&($("body").data("zeptoDataTest",{}),"string"==typeof $("body").data("zeptoDataTest")&&console.log("!!!! Please include the data module with Zepto! Intercooler requires full data support to function !!!!")),location.search&&location.search.indexOf("ic-launch-debugger=true")>=0&&Intercooler.debug()}"undefined"!=typeof Zepto&&null==$&&($=Zepto);var USE_DATA="true"==$('meta[name="intercoolerjs:use-data-prefix"]').attr("content"),USE_ACTUAL_HTTP_METHOD="true"==$('meta[name="intercoolerjs:use-actual-http-method"]').attr("content"),_MACROS=$.map(["ic-get-from","ic-post-to","ic-put-to","ic-patch-to","ic-delete-from","ic-style-src","ic-attr-src","ic-prepend-from","ic-append-from","ic-action"],function(a){return fixICAttributeName(a)}),_scrollHandler=null,_UUID=1,_readyHandlers=[],_isDependentFunction=function(a,b){if(!a||!b)return!1;var c=a.split(/[\?#]/,1)[0].split("/").filter(function(a){return""!=a}),d=b.split(/[\?#]/,1)[0].split("/").filter(function(a){return""!=a});return""!=c&&""!=d&&(d.slice(0,c.length).join("/")==c.join("/")||c.slice(0,d.length).join("/")==d.join("/"))},_history=null;try{_history=newIntercoolerHistory(localStorage,window.history,getSlotLimit(),.1)}catch(a){log($("body"),"Could not initialize history","WARN")}return $.ajaxTransport&&$.ajaxTransport("text",function(a,b){if("#"==b.url[0]){var c=fixICAttributeName("ic-local-"),d=$(b.url),e=[],f=200,g="OK";d.each(function(a,b){$.each(b.attributes,function(a,b){if(b.name.substr(0,c.length)==c){var d=b.name.substring(c.length);if("status"==d){var h=b.value.match(/(\d+)\s?(.*)/);null!=h?(f=h[1],g=h[2]):(f="500",g="Attribute Error")}else e.push(d+": "+b.value)}})});var h=d.length>0?d.html():"";return{send:function(a,b){b(f,g,{html:h},e.join("\n"))},abort:function(){}}}return null}),$(function(){init()}),{refresh:refresh,history:_history,triggerRequest:fireICRequest,processNodes:processNodes,closestAttrValue:closestAttrValue,verbFor:verbFor,isDependent:isDependent,getTarget:getTarget,processHeaders:processHeaders,setIsDependentFunction:function(a){_isDependentFunction=a},ready:function(a){_readyHandlers.push(a)},debug:function(){var a=closestAttrValue("body","ic-debugger-url")||"https://intercoolerreleases-leaddynocom.netdna-ssl.com/intercooler-debugger.js";$.getScript(a).fail(function(a,b,c){log($("body"),formatError(c),"ERROR")})},_internal:{init:init,replaceOrAddMethod:replaceOrAddMethod,initEventSource:function(a){return new EventSource(a)}}}}();return Intercooler});
\ No newline at end of file
diff --git a/server/static/jquery-3.2.1.min.js b/server/static/jquery-3.2.1.min.js
deleted file mode 100644
index 644d35e..0000000
--- a/server/static/jquery-3.2.1.min.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */
-!function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML=" ",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML=" ";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML=" ","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML=" ",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S),
-a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,""," "],thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/
-
-
-
-
diff --git a/server/templates/frontpage.html b/server/templates/frontpage.html
deleted file mode 100644
index f94d481..0000000
--- a/server/templates/frontpage.html
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
- Nivlheim
-{{template "common_head.html"}}
-
-
-{{template "logo"}}
-
-
-
System status
-{{.filesLastHour}}
-files were collected during the last hour.
-{{.totalMachines}}
-machines in the system.
-{{if .reportingPercentage}}
-{{.reportingPercentage}}%
-of machines sent in files during the last hour.
-{{end}}
-
-
-{{if .approval}}
-
-
Waiting for approval
-{{range .approval}}
-
- {{.Hostname.Value}} ({{.Ipaddr.Value}})
-
-
-{{end}}
-
-{{end}}
-
-{{if .machines}}
-
-
Machines
-{{template "searchbox" .}}
-{{range .machines}}
-{{.}}
-{{end}}
-
-{{end}}
-
-
-
-
-{{define "logo"}}
-
-{{end}}
diff --git a/server/templates/searchpage.html b/server/templates/searchpage.html
deleted file mode 100644
index 24f4ac5..0000000
--- a/server/templates/searchpage.html
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
-
- Nivlheim
-{{template "common_head.html"}}
-
-
-{{template "logo"}}
-{{template "searchbox" .}}
-
-
-{{.count}} hits
-
-
-{{range .hits}}
-
-
- {{.Hostname}} :
-
- {{.Filename}}
- {{.Excerpt}}
-
-{{end}}
-
-
-
-
-
-{{define "searchbox"}}
-
-
-
-{{end}}
diff --git a/server/web/browse.go b/server/web/browse.go
deleted file mode 100644
index d1b9bad..0000000
--- a/server/web/browse.go
+++ /dev/null
@@ -1,142 +0,0 @@
-package main
-
-import (
- "database/sql"
- "html/template"
- "net/http"
- "strconv"
-
- "github.com/lib/pq"
-)
-
-type Hostinfo struct {
- Hostname sql.NullString
- IPaddr sql.NullString
- Certfp string
- Kernel sql.NullString
- Lastseen pq.NullTime
- OS sql.NullString
- OSEdition sql.NullString
- Vendor sql.NullString
- Model sql.NullString
- Serialno sql.NullString
- Clientversion sql.NullString
-}
-
-type File struct {
- Fileid int
- Filename sql.NullString
- Received pq.NullTime
- Content sql.NullString
-}
-
-func browse(w http.ResponseWriter, req *http.Request) {
- db, err := sql.Open("postgres", dbConnectionString)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- defer db.Close()
-
- // Load templates
- templates, err := template.ParseGlob(templatePath + "/*")
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- tValues := make(map[string]interface{})
- var templatename string
-
- if (req.FormValue("f") != "" && req.FormValue("c") != "") ||
- req.FormValue("fid") != "" {
-
- templatename = "browsefile.html"
- fileid, err := strconv.Atoi(req.FormValue("fid"))
- if err != nil {
- err = db.QueryRow("SELECT fileid FROM files "+
- "WHERE filename=$1 AND certfp=$2 "+
- "ORDER BY received DESC LIMIT 1",
- req.FormValue("f"), req.FormValue("c")).Scan(&fileid)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- }
- if fileid == 0 {
- http.Error(w, "", http.StatusNotFound)
- return
- }
- var f File
- var hostname sql.NullString
- err = db.QueryRow("SELECT fileid,content,filename,received,hostname "+
- "FROM files JOIN hostinfo ON hostinfo.certfp=files.certfp "+
- "WHERE fileid=$1", fileid).
- Scan(&f.Fileid, &f.Content, &f.Filename, &f.Received, &hostname)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- tValues["file"] = f
- if hostname.Valid {
- tValues["hostname"] = hostname.String
- }
- } else if req.FormValue("h") != "" {
- templatename = "browsehost.html"
-
- // Hostinfo
- var hi Hostinfo
- err = db.QueryRow("SELECT hostname, ipaddr, certfp, kernel, "+
- "lastseen, os, os_edition, vendor, model, serialno, clientversion "+
- "FROM hostinfo WHERE hostname=$1",
- req.FormValue("h")).Scan(&hi.Hostname, &hi.IPaddr,
- &hi.Certfp, &hi.Kernel, &hi.Lastseen,
- &hi.OS, &hi.OSEdition, &hi.Vendor, &hi.Model,
- &hi.Serialno, &hi.Clientversion)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- tValues["hostinfo"] = hi
-
- // File and command List
- files := make([]string, 0, 0)
- commands := make([]string, 0, 0)
- rows, err := db.Query("SELECT DISTINCT filename,is_command FROM files "+
- " WHERE certfp=$1 ORDER BY filename", &hi.Certfp)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- } else {
- defer rows.Close()
- for rows.Next() {
- var filename sql.NullString
- var isCommand sql.NullBool
- err = rows.Scan(&filename, &isCommand)
- if err != nil && err != sql.ErrNoRows {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- if filename.Valid {
- if isCommand.Valid && isCommand.Bool {
- commands = append(commands, filename.String)
- } else {
- files = append(files, filename.String)
- }
- }
- }
- }
- tValues["files"] = files
- tValues["commands"] = commands
- } else {
- w.Write([]byte("Missing or wrong parameters."))
- return
- }
-
- // Render template
- err = templates.ExecuteTemplate(w, templatename, tValues)
- if err != nil {
- s := "
\nTemplate: " + templatename + " \n
" + err.Error() + " "
- w.Write([]byte(s))
- }
-}
diff --git a/server/web/frontpage.go b/server/web/frontpage.go
deleted file mode 100644
index 107d69e..0000000
--- a/server/web/frontpage.go
+++ /dev/null
@@ -1,161 +0,0 @@
-package main
-
-import (
- "database/sql"
- "fmt"
- "html/template"
- "net/http"
- "net/http/cgi"
- "os"
-
- "github.com/lib/pq"
-)
-
-var templatePath string
-var templates *template.Template
-var dbConnectionString string
-
-type waitingForApproval struct {
- Ipaddr sql.NullString
- Hostname sql.NullString
- Received pq.NullTime
-}
-
-func init() {
- http.HandleFunc("/", frontpage)
- http.HandleFunc("/search", search)
- http.HandleFunc("/browse", browse)
-}
-
-func main() {
- if len(os.Args) >= 2 && os.Args[1] == "--dev" {
- templatePath = "../templates"
- dbConnectionString = "sslmode=disable host=/var/run/postgresql"
- fmt.Println("Listening on port 8080")
- http.HandleFunc("/static/", staticfiles)
- http.ListenAndServe("127.0.0.1:8080", nil)
- } else {
- templatePath = "/var/www/nivlheim/templates"
- dbConnectionString = "dbname=apache host=/var/run/postgresql"
- cgi.Serve(nil)
- }
-}
-
-func frontpage(w http.ResponseWriter, req *http.Request) {
- db, err := sql.Open("postgres", dbConnectionString)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- defer db.Close()
-
- if req.FormValue("approve") != "" {
- approved := req.FormValue("approve") == "1"
- var res sql.Result
- res, err = db.Exec("UPDATE waiting_for_approval SET approved=$1 "+
- "WHERE hostname=$2 AND ipaddr=$3",
- approved,
- req.FormValue("h"),
- req.FormValue("ip"))
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- var rows int64
- rows, err = res.RowsAffected()
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- if rows == 0 {
- http.Error(w, "Record not found.", http.StatusNotFound)
- return
- }
- w.WriteHeader(http.StatusOK)
- switch approved {
- case true:
- w.Write([]byte("Approved"))
- case false:
- w.Write([]byte("Denied"))
- }
- return
- }
-
- // Load html templates
- templates, err := template.ParseGlob(templatePath + "/*")
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- machines := make([]string, 0, 0)
- rows, err := db.Query("SELECT hostname FROM hostinfo ORDER BY hostname")
- if err != nil {
- http.Error(w, "1: "+err.Error(), http.StatusInternalServerError)
- return
- }
-
- {
- defer rows.Close()
- for rows.Next() {
- var hostname sql.NullString
- err = rows.Scan(&hostname)
- if err != nil && err != sql.ErrNoRows {
- http.Error(w, "2: "+err.Error(), http.StatusInternalServerError)
- return
- }
- if hostname.Valid {
- machines = append(machines, hostname.String)
- }
- }
- }
-
- var filesLastHour int
- db.QueryRow("SELECT count(*) FROM files WHERE " +
- "received > now() - '1 hour'::INTERVAL").Scan(&filesLastHour)
-
- var machinesLastHour int
- db.QueryRow("SELECT count(distinct certfp) FROM files WHERE " +
- "received > now() - '1 hour'::INTERVAL").Scan(&machinesLastHour)
-
- var totalMachines int
- db.QueryRow("SELECT count(*) FROM hostinfo").Scan(&totalMachines)
-
- approval := make([]waitingForApproval, 0, 0)
- rows, err = db.Query("SELECT ipaddr, hostname, received " +
- "FROM waiting_for_approval WHERE approved IS NULL ORDER BY hostname")
- if err != nil {
- http.Error(w, "1: "+err.Error(), http.StatusInternalServerError)
- return
- }
-
- {
- defer rows.Close()
- for rows.Next() {
- var app waitingForApproval
- err = rows.Scan(&app.Ipaddr, &app.Hostname, &app.Received)
- if err != nil && err != sql.ErrNoRows {
- http.Error(w, "4: "+err.Error(), http.StatusInternalServerError)
- return
- }
- approval = append(approval, app)
- }
- }
-
- // Fill template values
- tValues := make(map[string]interface{})
- tValues["machines"] = machines
- tValues["filesLastHour"] = filesLastHour
- tValues["totalMachines"] = totalMachines
- if totalMachines > 0 {
- tValues["reportingPercentage"] = (machinesLastHour * 100) / totalMachines
- }
- tValues["approval"] = approval
-
- // Render template
- templates.ExecuteTemplate(w, "frontpage.html", tValues)
-}
-
-func staticfiles(w http.ResponseWriter, req *http.Request) {
- http.ServeFile(w, req, "../static/"+req.URL.Path[8:])
-}
diff --git a/server/web/search.go b/server/web/search.go
deleted file mode 100644
index e95acf0..0000000
--- a/server/web/search.go
+++ /dev/null
@@ -1,117 +0,0 @@
-package main
-
-import (
- "database/sql"
- "html/template"
- "net/http"
- "strings"
-
- "github.com/lib/pq"
-)
-
-type Hit struct {
- Fileid int
- Filename string
- Excerpt template.HTML
- Hostname string
- Number int
-}
-
-func search(w http.ResponseWriter, req *http.Request) {
- db, err := sql.Open("postgres", dbConnectionString)
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
- defer db.Close()
-
- hits := make([]Hit, 0, 0)
- query := strings.TrimSpace(req.FormValue("q"))
- lowerq := strings.ToLower(query)
- var count int
-
- if query != "" {
- // Number of hits
- err = db.QueryRow("SELECT count(distinct(filename,certcn)) FROM files "+
- "WHERE lower(content) LIKE '%' || $1 || '%' ", lowerq).Scan(&count)
- if err != nil && err != sql.ErrNoRows {
- http.Error(w, "2: "+err.Error(), http.StatusInternalServerError)
- return
- }
-
- // Hit list
- rows, err := db.Query("SELECT filename,certcn,max(received) "+
- "FROM files "+
- "WHERE lower(content) LIKE '%' || $1 || '%' "+
- "GROUP BY filename,certcn "+
- "ORDER BY max(received) DESC", lowerq)
- if err != nil {
- http.Error(w, "1: "+err.Error(), http.StatusInternalServerError)
- return
- } else {
- defer rows.Close()
- for no := 1; rows.Next(); no++ {
- var hit Hit
- var received pq.NullTime
- err = rows.Scan(&hit.Filename, &hit.Hostname, &received)
- if err != nil {
- http.Error(w, "3: "+err.Error(), http.StatusInternalServerError)
- return
- }
- hit.Number = no
- var content string
- err = db.QueryRow("SELECT fileid, content FROM files "+
- "WHERE filename=$1 AND certcn=$2 "+
- "ORDER BY received DESC LIMIT 1",
- hit.Filename, hit.Hostname).
- Scan(&hit.Fileid, &content)
- switch {
- case err == sql.ErrNoRows:
- continue
- case err != nil:
- http.Error(w, "2: "+err.Error(), http.StatusInternalServerError)
- return
- }
- i := strings.Index(strings.ToLower(content), lowerq)
- start := i - 20
- if start < 0 {
- start = 0
- }
- end := i + len(query) + 20
- if end >= len(content) {
- end = len(content) - 1
- }
- ex := content[start:end]
- lowerex := strings.ToLower(ex)
- i = strings.Index(lowerex, lowerq)
- if i > -1 {
- i2 := i + len(query)
- ex = ex[0:i2] + "" + ex[i2:]
- ex = ex[0:i] + "
" + ex[i:]
- }
- hit.Excerpt = template.HTML(ex)
- hits = append(hits, hit)
- }
- }
- }
-
- // Load templates
- templates, err := template.ParseGlob(templatePath + "/*")
- if err != nil {
- http.Error(w, err.Error(), http.StatusInternalServerError)
- return
- }
-
- // Fill template values
- tValues := make(map[string]interface{})
- tValues["q"] = req.FormValue("q")
- tValues["hits"] = hits
- tValues["count"] = count
-
- // Render template
- err = templates.ExecuteTemplate(w, "searchpage.html", tValues)
- if err != nil {
- s := "Error:
" + err.Error() + " "
- w.Write([]byte(s))
- }
-}
From b68b3928a0d684240b2493c29872e7c90837e417 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=98yvind=20Hagberg?=
Date: Fri, 23 Feb 2018 13:34:39 +0100
Subject: [PATCH 10/16] Download & package 3rd party JS and CSS libraries
Also:
Compile Handlebars templates during build process
Updated test_packages.sh to use the API
Fixed API urls for prod/dev in Javascript files
---
.gitignore | 4 +--
rpm/nivlheim.spec | 18 +++++++++++-
rpm/test_packages.sh | 17 +++++++++---
server/service/taskrunner.go | 4 +++
server/website/browse.html | 12 ++------
server/website/index.html | 12 ++------
server/website/js/browse.js | 6 ++--
server/website/js/functions.js | 4 +++
server/website/js/index.js | 14 +++++-----
server/website/js/search.js | 2 +-
server/website/libs/download_libraries.sh | 34 +++++++++++++++++++++++
server/website/search.html | 12 ++------
12 files changed, 91 insertions(+), 48 deletions(-)
create mode 100755 server/website/libs/download_libraries.sh
diff --git a/.gitignore b/.gitignore
index 0eb8849..97e1f2d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,4 @@
*.exe
-server/web/web
server/service/service
-server/website/libs
+server/website/libs/*
+!server/website/libs/download_libraries.sh
diff --git a/rpm/nivlheim.spec b/rpm/nivlheim.spec
index 894221d..018b42d 100644
--- a/rpm/nivlheim.spec
+++ b/rpm/nivlheim.spec
@@ -13,6 +13,7 @@ License: GPLv3+
URL: https://github.com/usit-gd/nivlheim
Source0: https://github.com/usit-gd/nivlheim/archive/%{getenv:GIT_BRANCH}.tar.gz
+BuildRequires: curl, unzip, npm
BuildRequires: perl(Archive::Tar)
BuildRequires: perl(Archive::Zip)
BuildRequires: perl(CGI)
@@ -134,9 +135,24 @@ install -p -m 0644 server/nivlheim.service %{buildroot}%{_unitdir}/%{name}.servi
install -p -m 0644 server/logrotate.conf %{buildroot}%{_sysconfdir}/logrotate.d/%{name}-server
install -p -m 0755 -D client/cron_hourly %{buildroot}%{_sysconfdir}/cron.hourly/nivlheim_client
cp -r server/service %{buildroot}%{_localstatedir}/nivlheim/go/src/
-cp -r server/website/* %{buildroot}%{_localstatedir}/www/html/
echo %{version} > %{buildroot}%{_sysconfdir}/nivlheim/version
+# Static website files
+cp -r server/website/* %{buildroot}%{_localstatedir}/www/html/
+%{buildroot}%{_localstatedir}/www/html/libs/download_libraries.sh
+rm %{buildroot}%{_localstatedir}/www/html/libs/*.sh
+rm -rf %{buildroot}%{_localstatedir}/www/html/mockapi
+
+# Compile web templates
+npm install handlebars -g
+cd %{buildroot}%{_localstatedir}/www/html/templates
+handlebars *.handlebars -min -f templates.js
+mv templates.js ../js/
+cd ../libs/
+mv handlebars.runtime.min.js handlebars.min.js
+cd %{_builddir}
+rm -rf %{buildroot}%{_localstatedir}/www/html/templates
+
%check
perl -c %{buildroot}%{_sbindir}/nivlheim_client
perl -c %{buildroot}/var/www/cgi-bin/secure/renewcert
diff --git a/rpm/test_packages.sh b/rpm/test_packages.sh
index 86aea71..3f63091 100755
--- a/rpm/test_packages.sh
+++ b/rpm/test_packages.sh
@@ -5,6 +5,7 @@ set -x
# It should signal success by outputting "END_TO_END_SUCCESS" if and only if
# the test(s) succeeded.
+# Install the packages. Different methods on Fedora and CentOS.
if [ -f /etc/fedora-release ]; then
sudo dnf copr -y enable oyvindh/Nivlheim-test
sudo dnf install -y nivlheim-client nivlheim-server || touch installerror
@@ -14,20 +15,27 @@ elif [ -f /etc/centos-release ]; then
https://copr.fedorainfracloud.org/coprs/oyvindh/Nivlheim-test/repo/epel-7/oyvindh-Nivlheim-test-epel-7.repo
sudo yum install -y nivlheim-client nivlheim-server || touch installerror
fi
-
if [ -f installerror ]; then
echo "Package installation failed."
exit
fi
-if [ $(curl -s -k https://localhost/ | grep -c "Nivlheim") -eq 0 ]; then
+# Check that the home page is being served
+if [ $(curl -s -k https://localhost/ | grep -c "Nivlheim ") -eq 0 ]; then
echo "The web server isn't properly configured and running."
exit
fi
+# Configure the client to use the server at localhost
echo "server=localhost" | sudo tee -a /etc/nivlheim/client.conf
+# Run the client, it will be put on waiting list for a certificate
sudo /usr/sbin/nivlheim_client
-sudo -u apache psql -c 'update waiting_for_approval set approved=true;'
+# Approve the client, using the API
+ID=`curl -s 'http://localhost:4040/api/v0/awaitingApproval?fields=approvalId'|perl -ne 'print $1 if /"approvalId":\s+(\d+)/'`
+curl -X PUT -s "http://localhost:4040/api/v0/awaitingApproval/$ID?hostname=abcdef"
+
+# Run the client again, this time it will receive a certificate
+# and post data into the system
sudo /usr/sbin/nivlheim_client
if [ ! -f /var/nivlheim/my.crt ]; then
echo "Certificate generation failed."
@@ -38,7 +46,8 @@ fi
OK=0
for try in {1..20}; do
sleep 3
- if [ $(curl -s -k https://localhost/ | grep -c "novalocal") -gt 0 ]; then
+ # Query the API for the new machine
+ if [ $(curl -s 'http://localhost:4040/api/v0/hostlist?fields=hostname' | grep -c "abcdef") -gt 0 ]; then
OK=1
break
fi
diff --git a/server/service/taskrunner.go b/server/service/taskrunner.go
index 567c22a..fde1d14 100644
--- a/server/service/taskrunner.go
+++ b/server/service/taskrunner.go
@@ -69,6 +69,10 @@ func main() {
defer log.Println("Stopped.")
log.Println("Starting up.")
+ if devmode {
+ log.Println("Running in development mode.")
+ }
+
// Connect to database
var dbConnectionString string
if devmode {
diff --git a/server/website/browse.html b/server/website/browse.html
index 51810d9..709356a 100644
--- a/server/website/browse.html
+++ b/server/website/browse.html
@@ -5,23 +5,15 @@
Nivlheim
-
-
+
+
diff --git a/server/website/index.html b/server/website/index.html
index eb828ee..b3fdf44 100644
--- a/server/website/index.html
+++ b/server/website/index.html
@@ -5,23 +5,15 @@
Nivlheim
-
-
+
+
diff --git a/server/website/js/browse.js b/server/website/js/browse.js
index 7eddec2..6dccd88 100644
--- a/server/website/js/browse.js
+++ b/server/website/js/browse.js
@@ -5,7 +5,7 @@ function browseHost(certfp, pushState = true) {
}
APIcall(
//"mockapi/browsehost.json",
- "http://127.0.0.1:4040/api/v0/host?certfp="+encodeURIComponent(certfp)+
+ "/api/v0/host?certfp="+encodeURIComponent(certfp)+
"&fields=ipAddress,hostname,lastseen,os,osEdition,"+
"kernel,vendor,model,serialNo,clientVersion,certfp,files",
"browsehost", "#placeholder_browse");
@@ -18,7 +18,7 @@ function browseFile(fileId, pushState = true) {
}
APIcall(
//"mockapi/browsefile.json",
- "http://127.0.0.1:4040/api/v0/file?fields=lastModified,hostname,filename,"+
+ "/api/v0/file?fields=lastModified,hostname,filename,"+
"content,certfp,versions&fileId="+encodeURIComponent(fileId),
"browsefile", "#placeholder_browse")
.done(function(){
@@ -40,7 +40,7 @@ function browseFile2(hostname, filename, pushState = true) {
}
APIcall(
//"mockapi/browsefile.json",
- "http://127.0.0.1:4040/api/v0/file?fields=fileId,lastModified,"+
+ "/api/v0/file?fields=fileId,lastModified,"+
"hostname,filename,content,certfp,versions"+
"&filename="+encodeURIComponent(filename)+
"&hostname="+encodeURIComponent(hostname),
diff --git a/server/website/js/functions.js b/server/website/js/functions.js
index b41d4ec..6f0c610 100644
--- a/server/website/js/functions.js
+++ b/server/website/js/functions.js
@@ -49,6 +49,10 @@ function renderTemplate(name, templateValues, domElement, deferredObj) {
}
function APIcall(url, templateName, domElement) {
+ if (location.origin.match('http://(127\\.0\\.0\\.1|localhost)')) {
+ // Developer mode. Assumes the API is running locally on port 4040.
+ url = "http://localhost:4040" + url;
+ }
var deferredObj = $.Deferred();
$.getJSON(url, function(data){
try {
diff --git a/server/website/js/index.js b/server/website/js/index.js
index b5b97a8..388b33a 100644
--- a/server/website/js/index.js
+++ b/server/website/js/index.js
@@ -10,28 +10,28 @@ $(document).ready(function(){
APIcall(
//"mockapi/systemstatus_data.json",
- "http://127.0.0.1:4040/api/v0/status",
+ "/api/v0/status",
"systemstatus", $('#placeholder_systemstatus'));
APIcall(
//"mockapi/awaiting_approval.json",
- "http://127.0.0.1:4040/api/v0/awaitingApproval"+
+ "/api/v0/awaitingApproval"+
"?fields=hostname,reversedns,ipaddress,approvalId",
"awaiting_approval", $('#placeholder_approval'));
APIcall(
//"mockapi/latestnewmachines.json",
- "http://127.0.0.1:4040/api/v0/hostlist?fields=hostname,certfp,lastseen"+
+ "/api/v0/hostlist?fields=hostname,certfp,lastseen"+
"&rsort=lastseen&limit=10",
"latestnewmachines", $('#placeholder_latestnewmachines'));
});
function approve(id) {
$.ajax({
- url : 'http://127.0.0.1:4040/api/v0/awaitingApproval/'
+ url : '/api/v0/awaitingApproval/'
+id+'?hostname='+$('input#hostname'+id).val(),
method: "PUT"
})
.always(function(){
- APIcall("http://127.0.0.1:4040/api/v0/awaitingApproval"+
+ APIcall("/api/v0/awaitingApproval"+
"?fields=hostname,reversedns,ipaddress,approvalId",
"awaiting_approval", $('#placeholder_approval'));
});
@@ -39,11 +39,11 @@ function approve(id) {
function deny(id) {
$.ajax({
- url : 'http://127.0.0.1:4040/api/v0/awaitingApproval/'+id,
+ url : '/api/v0/awaitingApproval/'+id,
method: "DELETE"
})
.always(function(){
- APIcall("http://127.0.0.1:4040/api/v0/awaitingApproval"+
+ APIcall("/api/v0/awaitingApproval"+
"?fields=hostname,reversedns,ipaddress,approvalId",
"awaiting_approval", $('#placeholder_approval'));
});
diff --git a/server/website/js/search.js b/server/website/js/search.js
index 3974e11..eb61e93 100644
--- a/server/website/js/search.js
+++ b/server/website/js/search.js
@@ -27,7 +27,7 @@ function newSearch() {
function performSearch(q) {
APIcall(
//"mockapi/searchpage.json",
- "http://127.0.0.1:4040/api/v0/searchpage?q="+encodeURIComponent(q)+
+ "/api/v0/searchpage?q="+encodeURIComponent(q)+
"&page=1&hitsPerPage=10&excerpt=80",
"search", "#placeholder_searchresult");
}
diff --git a/server/website/libs/download_libraries.sh b/server/website/libs/download_libraries.sh
new file mode 100755
index 0000000..2036106
--- /dev/null
+++ b/server/website/libs/download_libraries.sh
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# This script downloads 3rd party Javascript and CSS libraries
+# that the website uses.
+# These libraries do not come with the Git repository,
+# but are downloaded during the build process and added to the packages.
+
+# As a developer, you can run this script to download the libraries
+# into the libs/ folder.
+
+
+#
+#
+#
+#
+#
+
+cd `dirname $0`
+echo "Downloading Javascript and CSS libraries into `pwd`"
+
+curl -s --remote-name https://code.jquery.com/jquery-3.3.1.min.js
+curl -s --remote-name https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js
+curl -s --remote-name https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.runtime.min.js
+curl -s --remote-name https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js
+curl -s --remote-name https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css
+curl -s --remote-name https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css.map
+
+curl -s --remote-name https://use.fontawesome.com/releases/v5.0.6/fontawesome-free-5.0.6.zip
+rm -rf fontawesome-free-5.0.6 fontawesome
+unzip -q fontawesome-free-5.0.6.zip
+mv fontawesome-free-5.0.6/on-server fontawesome
+rm -rf fontawesome-free-5.0.6 fontawesome-free-5.0.6.zip
diff --git a/server/website/search.html b/server/website/search.html
index 711e541..d94a5df 100644
--- a/server/website/search.html
+++ b/server/website/search.html
@@ -5,23 +5,15 @@
Nivlheim
-
-
+
+
From 7e4540d6108576288956ee33dee4a5fd7f4bc11b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=98yvind=20Hagberg?=
Date: Fri, 23 Feb 2018 20:17:38 +0000
Subject: [PATCH 11/16] Fixed lots of errors in the spec file and setup
Fixed npm/handlebars requirement in spec file.
Fetching JS and CSS libraries as part of the setup after package
installation. That way, we don't distribute them, so we don't
have to deal with software licenses.
---
rpm/buildrpm.sh | 2 +-
rpm/nivlheim.spec | 26 ++++++++---------------
server/setup.sh | 4 ++++
server/website/libs/download_libraries.sh | 15 +++++++------
4 files changed, 22 insertions(+), 25 deletions(-)
diff --git a/rpm/buildrpm.sh b/rpm/buildrpm.sh
index a3c7f17..121c5ee 100755
--- a/rpm/buildrpm.sh
+++ b/rpm/buildrpm.sh
@@ -20,7 +20,7 @@ then
exit 1
fi
cp *.spec $BUILDDIR/SPECS/
-[ -f *.patch ] && cp *.patch $BUILDDIR/SOURCES/
+cp *.patch $BUILDDIR/SOURCES/ 2>/dev/null
SPEC=`eval echo $BUILDDIR/SPECS/*.spec`
echo "buildrpm: Spec file = $SPEC"
diff --git a/rpm/nivlheim.spec b/rpm/nivlheim.spec
index 018b42d..5593cf4 100644
--- a/rpm/nivlheim.spec
+++ b/rpm/nivlheim.spec
@@ -13,7 +13,8 @@ License: GPLv3+
URL: https://github.com/usit-gd/nivlheim
Source0: https://github.com/usit-gd/nivlheim/archive/%{getenv:GIT_BRANCH}.tar.gz
-BuildRequires: curl, unzip, npm
+BuildRequires: curl, unzip
+BuildRequires: npm(handlebars)
BuildRequires: perl(Archive::Tar)
BuildRequires: perl(Archive::Zip)
BuildRequires: perl(CGI)
@@ -137,21 +138,12 @@ install -p -m 0755 -D client/cron_hourly %{buildroot}%{_sysconfdir}/cron.hourly/
cp -r server/service %{buildroot}%{_localstatedir}/nivlheim/go/src/
echo %{version} > %{buildroot}%{_sysconfdir}/nivlheim/version
-# Static website files
-cp -r server/website/* %{buildroot}%{_localstatedir}/www/html/
-%{buildroot}%{_localstatedir}/www/html/libs/download_libraries.sh
-rm %{buildroot}%{_localstatedir}/www/html/libs/*.sh
-rm -rf %{buildroot}%{_localstatedir}/www/html/mockapi
-
# Compile web templates
-npm install handlebars -g
-cd %{buildroot}%{_localstatedir}/www/html/templates
-handlebars *.handlebars -min -f templates.js
-mv templates.js ../js/
-cd ../libs/
-mv handlebars.runtime.min.js handlebars.min.js
-cd %{_builddir}
-rm -rf %{buildroot}%{_localstatedir}/www/html/templates
+handlebars server/website/templates --min -f server/website/js/templates.js
+
+# Copy static website files, excluding files that are only for development
+rm -rf server/website/mockapi server/website/templates
+cp -r server/website/* %{buildroot}%{_localstatedir}/www/html/
%check
perl -c %{buildroot}%{_sbindir}/nivlheim_client
@@ -192,7 +184,7 @@ rm -rf %{buildroot}
%dir /var/log/nivlheim
/var/www/nivlheim
/var/www/cgi-bin
-/var/www/html
+/var/www/html/*
%attr(0644, root, apache) /var/www/nivlheim/log4perl.conf
%attr(0755, root, root) %{_localstatedir}/nivlheim/setup.sh
%{_localstatedir}/nivlheim
@@ -208,7 +200,7 @@ rm -rf %{buildroot}
%systemd_postun_with_restart %{name}.service
%changelog
-* Wed Feb 21 2018 Øyvind Hagberg - 0.1.4-20180221
+* Fri Feb 23 2018 Øyvind Hagberg - 0.1.4-20180223
- New web frontend, installs in /var/www/html. frontpage.cgi is gone.
* Fri Jan 05 2018 Øyvind Hagberg - 0.1.1-20180105
diff --git a/server/setup.sh b/server/setup.sh
index 22a1de7..e8d37b1 100644
--- a/server/setup.sh
+++ b/server/setup.sh
@@ -6,6 +6,10 @@ if [ `whoami` != "root" ]; then
exit 1
fi
+# download 3rd party Javascript and CSS libraries
+cd /var/www/html/libs
+./download_libraries.sh && rm ./download_libraries.sh
+
# make dirs
mkdir -p /var/www/nivlheim/{db,certs,CA,queue}
diff --git a/server/website/libs/download_libraries.sh b/server/website/libs/download_libraries.sh
index 2036106..7d4384f 100755
--- a/server/website/libs/download_libraries.sh
+++ b/server/website/libs/download_libraries.sh
@@ -17,17 +17,18 @@
#
#
+set -e
cd `dirname $0`
echo "Downloading Javascript and CSS libraries into `pwd`"
-curl -s --remote-name https://code.jquery.com/jquery-3.3.1.min.js
-curl -s --remote-name https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js
-curl -s --remote-name https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.runtime.min.js
-curl -s --remote-name https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js
-curl -s --remote-name https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css
-curl -s --remote-name https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css.map
+curl -sS --remote-name https://code.jquery.com/jquery-3.3.1.min.js
+curl -sS --remote-name https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js
+curl -sS --remote-name https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.runtime.min.js
+curl -sS --remote-name https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js
+curl -sS --remote-name https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css
+curl -sS --remote-name https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css.map
-curl -s --remote-name https://use.fontawesome.com/releases/v5.0.6/fontawesome-free-5.0.6.zip
+curl -sS --remote-name https://use.fontawesome.com/releases/v5.0.6/fontawesome-free-5.0.6.zip
rm -rf fontawesome-free-5.0.6 fontawesome
unzip -q fontawesome-free-5.0.6.zip
mv fontawesome-free-5.0.6/on-server fontawesome
From 08fbef504ae953ed2141df86abe82091e4d2956a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=98yvind=20Hagberg?=
Date: Fri, 23 Feb 2018 22:24:31 +0100
Subject: [PATCH 12/16] Fixed problem with SELinux and httpd proxy to API
---
rpm/test_packages.sh | 6 ++++++
server/service/api_status.go | 6 +++++-
server/setup.sh | 3 ++-
3 files changed, 13 insertions(+), 2 deletions(-)
diff --git a/rpm/test_packages.sh b/rpm/test_packages.sh
index 3f63091..d32b91c 100755
--- a/rpm/test_packages.sh
+++ b/rpm/test_packages.sh
@@ -26,6 +26,12 @@ if [ $(curl -s -k https://localhost/ | grep -c "Nivlheim ") -eq 0
exit
fi
+# Check that the API is available through the main web server
+if ! curl -so /dev/null https://localhost/api/v0/status; then
+ echo "The API is unavailable."
+ exit
+fi
+
# Configure the client to use the server at localhost
echo "server=localhost" | sudo tee -a /etc/nivlheim/client.conf
# Run the client, it will be put on waiting list for a certificate
diff --git a/server/service/api_status.go b/server/service/api_status.go
index 253e614..beb42a7 100644
--- a/server/service/api_status.go
+++ b/server/service/api_status.go
@@ -33,7 +33,11 @@ func (vars *apiMethodStatus) ServeHTTP(w http.ResponseWriter, req *http.Request)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
- status.ReportingPercentageLastHour = 100 * machinesLastHour / status.NumOfMachines
+ if status.NumOfMachines > 0 {
+ status.ReportingPercentageLastHour = 100 * machinesLastHour / status.NumOfMachines
+ } else {
+ status.ReportingPercentageLastHour = 0
+ }
returnJSON(w, req, status)
}
diff --git a/server/setup.sh b/server/setup.sh
index e8d37b1..b078dd1 100644
--- a/server/setup.sh
+++ b/server/setup.sh
@@ -56,7 +56,8 @@ chmod 0644 /var/www/nivlheim/default_cert.pem /var/www/nivlheim/CA/nivlheimca.cr
chcon -R -t httpd_sys_rw_content_t /var/log/nivlheim /var/www/nivlheim/{db,certs,rand,queue}
chown -R apache:apache /var/www/nivlheim/{db,certs,rand,queue}
chmod -R u+w /var/www/nivlheim/{db,certs,rand,queue}
-setsebool httpd_can_network_connect_db on
+setsebool -P httpd_can_network_connect_db on
+setsebool -P httpd_can_network_connect on # for proxy connections to the API
# initialize postgresql. new/old syntax
if ! (/usr/bin/postgresql-setup --initdb || /usr/bin/postgresql-setup initdb); then
From 50f1ac0fab3db2f4b6c0919c6cc61f10cb2762a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=98yvind=20Hagberg?=
Date: Sat, 24 Feb 2018 13:35:45 +0100
Subject: [PATCH 13/16] Fixed faulty curl options in test_packages.sh
---
rpm/test_packages.sh | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/rpm/test_packages.sh b/rpm/test_packages.sh
index d32b91c..ddbed7e 100755
--- a/rpm/test_packages.sh
+++ b/rpm/test_packages.sh
@@ -21,13 +21,13 @@ if [ -f installerror ]; then
fi
# Check that the home page is being served
-if [ $(curl -s -k https://localhost/ | grep -c "Nivlheim ") -eq 0 ]; then
+if [ $(curl -sSk https://localhost/ | grep -c "Nivlheim ") -eq 0 ]; then
echo "The web server isn't properly configured and running."
exit
fi
# Check that the API is available through the main web server
-if ! curl -so /dev/null https://localhost/api/v0/status; then
+if ! curl -sSko /dev/null https://localhost/api/v0/status; then
echo "The API is unavailable."
exit
fi
@@ -37,8 +37,8 @@ echo "server=localhost" | sudo tee -a /etc/nivlheim/client.conf
# Run the client, it will be put on waiting list for a certificate
sudo /usr/sbin/nivlheim_client
# Approve the client, using the API
-ID=`curl -s 'http://localhost:4040/api/v0/awaitingApproval?fields=approvalId'|perl -ne 'print $1 if /"approvalId":\s+(\d+)/'`
-curl -X PUT -s "http://localhost:4040/api/v0/awaitingApproval/$ID?hostname=abcdef"
+ID=`curl -sS 'http://localhost:4040/api/v0/awaitingApproval?fields=approvalId'|perl -ne 'print $1 if /"approvalId":\s+(\d+)/'`
+curl -X PUT -sS "http://localhost:4040/api/v0/awaitingApproval/$ID?hostname=abcdef"
# Run the client again, this time it will receive a certificate
# and post data into the system
@@ -53,7 +53,7 @@ OK=0
for try in {1..20}; do
sleep 3
# Query the API for the new machine
- if [ $(curl -s 'http://localhost:4040/api/v0/hostlist?fields=hostname' | grep -c "abcdef") -gt 0 ]; then
+ if [ $(curl -sS 'http://localhost:4040/api/v0/hostlist?fields=hostname' | grep -c "abcdef") -gt 0 ]; then
OK=1
break
fi
From 3ce5620feab67cbfeca0fd25f2fa5de1218b54bf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=98yvind=20Hagberg?=
Date: Mon, 26 Feb 2018 08:11:12 +0100
Subject: [PATCH 14/16] test_packages.sh verifies download of js/css libs
download_libraries.sh: --prod option downloads runtime handlebars
---
rpm/test_packages.sh | 12 ++++++++++--
server/setup.sh | 2 +-
server/website/libs/download_libraries.sh | 18 +++++++++++-------
3 files changed, 22 insertions(+), 10 deletions(-)
diff --git a/rpm/test_packages.sh b/rpm/test_packages.sh
index ddbed7e..cd59a92 100755
--- a/rpm/test_packages.sh
+++ b/rpm/test_packages.sh
@@ -21,13 +21,21 @@ if [ -f installerror ]; then
fi
# Check that the home page is being served
-if [ $(curl -sSk https://localhost/ | grep -c "Nivlheim ") -eq 0 ]; then
+if [ $(curl -sSk https://localhost/ | tee /tmp/homepage | grep -c "Nivlheim ") -eq 0 ]; then
echo "The web server isn't properly configured and running."
exit
fi
+# 3rd party libraries
+for URL in $(perl -ne 'm!"(libs/.*?)"!&&print "$1\n"' < /tmp/homepage);
+do
+ if ! curl -sSkfo /dev/null "https://localhost/$URL"; then
+ echo "The web server returns an error code for $URL"
+ exit
+ fi
+done
# Check that the API is available through the main web server
-if ! curl -sSko /dev/null https://localhost/api/v0/status; then
+if ! curl -sSkfo /dev/null https://localhost/api/v0/status; then
echo "The API is unavailable."
exit
fi
diff --git a/server/setup.sh b/server/setup.sh
index b078dd1..696f23d 100644
--- a/server/setup.sh
+++ b/server/setup.sh
@@ -8,7 +8,7 @@ fi
# download 3rd party Javascript and CSS libraries
cd /var/www/html/libs
-./download_libraries.sh && rm ./download_libraries.sh
+./download_libraries.sh --prod && rm ./download_libraries.sh
# make dirs
mkdir -p /var/www/nivlheim/{db,certs,CA,queue}
diff --git a/server/website/libs/download_libraries.sh b/server/website/libs/download_libraries.sh
index 7d4384f..820ea32 100755
--- a/server/website/libs/download_libraries.sh
+++ b/server/website/libs/download_libraries.sh
@@ -21,14 +21,18 @@ set -e
cd `dirname $0`
echo "Downloading Javascript and CSS libraries into `pwd`"
-curl -sS --remote-name https://code.jquery.com/jquery-3.3.1.min.js
-curl -sS --remote-name https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js
-curl -sS --remote-name https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.runtime.min.js
-curl -sS --remote-name https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js
-curl -sS --remote-name https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css
-curl -sS --remote-name https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css.map
+if [[ "$1" == "--prod" ]] {
+ curl -sSf --remote-name https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.runtime.min.js
+ mv handlebars.runtime.min.js handlebars.min.js
+} else {
+ curl -sSf --remote-name https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js
+}
+curl -sSf --remote-name https://code.jquery.com/jquery-3.3.1.min.js
+curl -sSf --remote-name https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js
+curl -sSf --remote-name https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css
+curl -sSf --remote-name https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css.map
-curl -sS --remote-name https://use.fontawesome.com/releases/v5.0.6/fontawesome-free-5.0.6.zip
+curl -sSf --remote-name https://use.fontawesome.com/releases/v5.0.6/fontawesome-free-5.0.6.zip
rm -rf fontawesome-free-5.0.6 fontawesome
unzip -q fontawesome-free-5.0.6.zip
mv fontawesome-free-5.0.6/on-server fontawesome
From 2233b136b12faa780cbb97c2d793cf8adcd0b8be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=98yvind=20Hagberg?=
Date: Mon, 26 Feb 2018 08:34:23 +0100
Subject: [PATCH 15/16] Fixed a stupid typo in download_libraries.sh
---
server/website/libs/download_libraries.sh | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/server/website/libs/download_libraries.sh b/server/website/libs/download_libraries.sh
index 820ea32..7bf89dd 100755
--- a/server/website/libs/download_libraries.sh
+++ b/server/website/libs/download_libraries.sh
@@ -21,12 +21,12 @@ set -e
cd `dirname $0`
echo "Downloading Javascript and CSS libraries into `pwd`"
-if [[ "$1" == "--prod" ]] {
+if [[ "$1" == "--prod" ]]; then
curl -sSf --remote-name https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.runtime.min.js
mv handlebars.runtime.min.js handlebars.min.js
-} else {
+else
curl -sSf --remote-name https://cdnjs.cloudflare.com/ajax/libs/handlebars.js/4.0.11/handlebars.min.js
-}
+fi
curl -sSf --remote-name https://code.jquery.com/jquery-3.3.1.min.js
curl -sSf --remote-name https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js
curl -sSf --remote-name https://cdnjs.cloudflare.com/ajax/libs/bulma/0.6.2/css/bulma.min.css
From 024e48939b49582b4471d2cd182c04b3ae934235 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=98yvind=20Hagberg?=
Date: Mon, 26 Feb 2018 09:13:31 +0100
Subject: [PATCH 16/16] Cosmetic changes after code review
---
rpm/nivlheim.spec | 1 -
server/service/api.go | 3 +--
server/service/api_awaitingApproval.go | 2 +-
3 files changed, 2 insertions(+), 4 deletions(-)
diff --git a/rpm/nivlheim.spec b/rpm/nivlheim.spec
index 5593cf4..cfeb977 100644
--- a/rpm/nivlheim.spec
+++ b/rpm/nivlheim.spec
@@ -13,7 +13,6 @@ License: GPLv3+
URL: https://github.com/usit-gd/nivlheim
Source0: https://github.com/usit-gd/nivlheim/archive/%{getenv:GIT_BRANCH}.tar.gz
-BuildRequires: curl, unzip
BuildRequires: npm(handlebars)
BuildRequires: perl(Archive::Tar)
BuildRequires: perl(Archive::Zip)
diff --git a/server/service/api.go b/server/service/api.go
index 1746476..8b0b4d5 100644
--- a/server/service/api.go
+++ b/server/service/api.go
@@ -1,6 +1,5 @@
package main
-// Create tasks to parse new files that have been read into the database
import (
"database/sql"
"encoding/json"
@@ -67,7 +66,7 @@ func wrapAllowLocalhostCORS(h http.Handler) http.Handler {
if req.Method == "OPTIONS" {
// When cross-domain, browsers sends OPTIONS first, to check for CORS headers
// See: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
- http.Error(w, "", http.StatusNoContent)
+ http.Error(w, "", http.StatusNoContent) // 204 OK
return
}
h.ServeHTTP(w, req)
diff --git a/server/service/api_awaitingApproval.go b/server/service/api_awaitingApproval.go
index c3dfbe1..fde4f2f 100644
--- a/server/service/api_awaitingApproval.go
+++ b/server/service/api_awaitingApproval.go
@@ -124,5 +124,5 @@ func (vars *apiMethodAwaitingApproval) ServeHTTPREST(w http.ResponseWriter,
http.StatusNotFound)
return
}
- http.Error(w, "", http.StatusNoContent)
+ http.Error(w, "", http.StatusNoContent) // 204 OK
}