diff --git a/.travis.yml b/.travis.yml
index ef1eebaf..5b44fc55 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -18,7 +18,7 @@ services:
env:
global:
- - VERSION=0.28.91
+ - VERSION=0.29
- DB_HOST=localhost
- DB_USER=travis
- DB_PASS=
diff --git a/Dockerfile b/Dockerfile
index f7e636df..4136105a 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,6 +1,6 @@
FROM alpine:latest
-ENV VERSION=v0.28.91
+ENV VERSION=v0.29
RUN apk --no-cache add libstdc++ ca-certificates
RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \
diff --git a/core/services.go b/core/services.go
index c98cd87d..edb9d2ee 100644
--- a/core/services.go
+++ b/core/services.go
@@ -131,9 +131,9 @@ func (s *Service) GraphData() string {
// group by interval sql query for postgres, mysql and sqlite
sql := fmt.Sprintf("SELECT date_trunc('%v', created_at), AVG(latency)*1000 AS value FROM hits WHERE service=%v AND created_at > '%v' GROUP BY 1 ORDER BY date_trunc ASC;", increment, s.Id, since.Format(time.RFC3339))
if dbServer == "mysql" {
- sql = fmt.Sprintf("SELECT CONCAT(date_format(created_at, '%%Y-%%m-%%dT%%TZ')) AS created_at, AVG(latency)*1000 AS value FROM hits WHERE service=%v AND DATE_FORMAT(created_at, '%%Y-%%m-%%dT%%TZ') BETWEEN DATE_FORMAT(NOW() - INTERVAL 12 HOUR, '%%Y-%%m-%%dT%%TZ') AND DATE_FORMAT(NOW(), '%%Y-%%m-%%dT%%TZ') GROUP BY created_at", s.Id)
+ sql = fmt.Sprintf("SELECT CONCAT(date_format(created_at, '%%Y-%%m-%%dT%%TZ')) AS created_at, AVG(latency)*1000 AS value FROM hits WHERE service=%v AND DATE_FORMAT(created_at, '%%Y-%%m-%%dT%%TZ') BETWEEN DATE_FORMAT(NOW() - INTERVAL 12 HOUR, '%%Y-%%m-%%dT%%TZ') AND DATE_FORMAT(NOW(), '%%Y-%%m-%%dT%%TZ') GROUP BY created_at ORDER BY created_at ASC;", s.Id)
} else if dbServer == "sqlite" {
- sql = fmt.Sprintf("SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', created_at), AVG(latency)*1000 as value FROM hits WHERE service=%v AND created_at >= '%v' GROUP BY strftime('%%M', created_at)", s.Id, since.Format(time.RFC3339))
+ sql = fmt.Sprintf("SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', created_at), AVG(latency)*1000 as value FROM hits WHERE service=%v AND created_at >= '%v' GROUP BY strftime('%%M', created_at) ORDER BY created_at ASC;", s.Id, since.Format(time.RFC3339))
}
dated, err := DbSession.Query(db.Raw(sql))
if err != nil {
diff --git a/handlers/handlers.go b/handlers/handlers.go
index d96e017e..7ef36d06 100644
--- a/handlers/handlers.go
+++ b/handlers/handlers.go
@@ -98,4 +98,19 @@ func ExecuteResponse(w http.ResponseWriter, r *http.Request, file string, data i
t.Execute(w, data)
}
+func ExecuteJSResponse(w http.ResponseWriter, r *http.Request, file string, data interface{}) {
+ render, err := core.JsBox.String(file)
+ if err != nil {
+ utils.Log(4, err)
+ }
+ t := template.New("charts")
+ t.Funcs(template.FuncMap{
+ "safe": func(html string) template.HTML {
+ return template.HTML(html)
+ },
+ })
+ t.Parse(render)
+ t.Execute(w, data)
+}
+
type DbConfig types.DbConfig
diff --git a/handlers/routes.go b/handlers/routes.go
index 5e83a0c7..ba038a10 100644
--- a/handlers/routes.go
+++ b/handlers/routes.go
@@ -4,6 +4,7 @@ import (
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
"github.com/hunterlong/statup/core"
+ "github.com/tdewolff/minify"
"net/http"
)
@@ -11,6 +12,8 @@ func Router() *mux.Router {
r := mux.NewRouter()
r.Handle("/", http.HandlerFunc(IndexHandler))
LocalizedAssets(r)
+ m := minify.New()
+ r.Handle("/charts.js", m.Middleware(http.HandlerFunc(RenderServiceChartsHandler)))
r.Handle("/setup", http.HandlerFunc(SetupHandler)).Methods("GET")
r.Handle("/setup", http.HandlerFunc(ProcessSetupHandler)).Methods("POST")
r.Handle("/dashboard", http.HandlerFunc(DashboardHandler)).Methods("GET")
diff --git a/handlers/services.go b/handlers/services.go
index 93fe525b..bb33d33c 100644
--- a/handlers/services.go
+++ b/handlers/services.go
@@ -9,6 +9,13 @@ import (
"strconv"
)
+func RenderServiceChartsHandler(w http.ResponseWriter, r *http.Request) {
+ services := core.CoreApp.Services
+ //w.Header().Set("Content-Type", "text/javascript")
+ //w.Header().Set("Cache-Control", "no-cache, private, max-age=0")
+ ExecuteJSResponse(w, r, "charts.js", services)
+}
+
func ServicesHandler(w http.ResponseWriter, r *http.Request) {
if !IsAuthenticated(r) {
http.Redirect(w, r, "/", http.StatusSeeOther)
diff --git a/source/js/charts.js b/source/js/charts.js
new file mode 100644
index 00000000..82a2c570
--- /dev/null
+++ b/source/js/charts.js
@@ -0,0 +1,146 @@
+{{ range . }}{{ if .AvgTime }}var ctx = document.getElementById("service_{{.Id}}").getContext('2d');
+
+var chartdata = new Chart(ctx, {
+ type: 'line',
+ data: {
+ datasets: [{
+ label: 'Response Time (Milliseconds)',
+ data: {{safe .GraphData}},
+ backgroundColor: [
+ 'rgba(47, 206, 30, 0.92)'
+],
+ borderColor: [
+ 'rgb(47, 171, 34)'
+],
+ borderWidth: 1
+}]
+},
+options: {
+ maintainAspectRatio: false,
+ scaleShowValues: true,
+ layout: {
+ padding: {
+ left: 0,
+ right: 0,
+ top: 0,
+ bottom: -10
+ }
+ },
+ hover: {
+ animationDuration: 0,
+ },
+ responsiveAnimationDuration: 0,
+ animation: {
+ duration: 3500,
+ onComplete: function() {
+ var chartInstance = this.chart,
+ ctx = chartInstance.ctx;
+
+ var controller = this.chart.controller;
+ var xAxis = controller.scales['x-axis-0'];
+ var yAxis = controller.scales['y-axis-0'];
+
+ ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'bottom';
+
+ var numTicks = xAxis.ticks.length;
+ var yOffsetStart = xAxis.width / numTicks;
+ var halfBarWidth = (xAxis.width / (numTicks * 2));
+
+ xAxis.ticks.forEach(function(value, index) {
+ var xOffset = 20;
+ var yOffset = (yOffsetStart * index) + halfBarWidth;
+ ctx.fillStyle = '#e2e2e2';
+ ctx.fillText(value, yOffset, xOffset);
+ });
+
+ this.data.datasets.forEach(function(dataset, i) {
+ var meta = chartInstance.controller.getDatasetMeta(i);
+ var hxH = 0;
+ var hyH = 0;
+ var hxL = 0;
+ var hyL = 0;
+ var highestNum = 0;
+ var lowestnum = 999999999999;
+ meta.data.forEach(function(bar, index) {
+ var data = dataset.data[index];
+
+ if (data.y {{safe "<"}} lowestnum) {
+ lowestnum = data.y;
+ hxL = bar._model.x;
+ hyL = bar._model.y;
+ }
+
+ if (data.y > highestNum) {
+ highestNum = data.y;
+ hxH = bar._model.x;
+ hyH = bar._model.y;
+ }
+ });
+
+ if (hxH {{safe ">"}}= 820) {
+ hxH = 820;
+ } else if (hxH {{safe "<"}}= 50) {
+ hxH = 50;
+ }
+
+ if (hxL {{safe ">"}}= 820) {
+ hxL = 820;
+ } else if (hxL {{safe "<"}}= 70) {
+ hxL = 70;
+ }
+
+ ctx.fillStyle = '#ffa7a2';
+ ctx.fillText(highestNum+"ms", hxH - 40, hyH + 15);
+ ctx.fillStyle = '#45d642';
+ ctx.fillText(lowestnum+"ms", hxL, hyL + 10);
+
+ console.log("done service_id_{{.Id}}")
+
+ });
+ }
+ },
+ legend: {
+ display: false
+ },
+ tooltips: {
+ "enabled": false
+ },
+ scales: {
+ yAxes: [{
+ display: false,
+ ticks: {
+ fontSize: 20,
+ display: false,
+ beginAtZero: false
+ },
+ gridLines: {
+ display:false
+ }
+ }],
+ xAxes: [{
+ type: 'time',
+ distribution: 'series',
+ autoSkip: false,
+ gridLines: {
+ display:false
+ },
+ ticks: {
+ stepSize: 1,
+ min: 0,
+ fontColor: "white",
+ fontSize: 20,
+ display: false,
+ }
+ }]
+ },
+ elements: {
+ point: {
+ radius: 0
+ }
+ }
+}
+});
+{{ end }}
+{{ end }}
\ No newline at end of file
diff --git a/source/tmpl/index.html b/source/tmpl/index.html
index f68fdb5b..6999663c 100644
--- a/source/tmpl/index.html
+++ b/source/tmpl/index.html
@@ -104,159 +104,8 @@
{{end}}
-
-
-
+
{{ if .Core.Style }}