mirror of https://github.com/statping/statping
service date range
parent
baa4ac49ce
commit
4598d0688e
4
Makefile
4
Makefile
|
@ -1,4 +1,4 @@
|
|||
VERSION=0.63
|
||||
VERSION=0.64
|
||||
BINARY_NAME=statup
|
||||
GOPATH:=$(GOPATH)
|
||||
GOCMD=go
|
||||
|
@ -61,8 +61,8 @@ run: build
|
|||
|
||||
# compile assets using SASS and Rice. compiles scss -> css, and run rice embed-go
|
||||
compile:
|
||||
cd source && $(GOPATH)/bin/rice embed-go
|
||||
sass source/scss/base.scss source/css/base.css
|
||||
cd source && $(GOPATH)/bin/rice embed-go
|
||||
rm -rf .sass-cache
|
||||
|
||||
# benchmark testing
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
// Statup
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statup
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// +build debug
|
||||
|
||||
// Statup
|
||||
|
|
|
@ -123,7 +123,7 @@ func (s *Service) TotalFailures() (uint64, error) {
|
|||
// TotalFailuresSince returns the total amount of failures for a service since a specific time/date
|
||||
func (s *Service) TotalFailuresSince(ago time.Time) (uint64, error) {
|
||||
var count uint64
|
||||
rows := failuresDB().Where("service = ? AND created_at > ?", s.Id, ago.Format("2006-01-02 15:04:05"))
|
||||
rows := failuresDB().Where("service = ? AND created_at > ?", s.Id, ago.UTC().Format("2006-01-02 15:04:05"))
|
||||
err := rows.Count(&count)
|
||||
return count, err.Error
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@ func (s *Service) TotalHits() (uint64, error) {
|
|||
// TotalHitsSince returns the total amount of hits based on a specific time/date
|
||||
func (s *Service) TotalHitsSince(ago time.Time) (uint64, error) {
|
||||
var count uint64
|
||||
rows := hitsDB().Where("service = ? AND created_at > ?", s.Id, ago.Format("2006-01-02 15:04:05"))
|
||||
rows := hitsDB().Where("service = ? AND created_at > ?", s.Id, ago.UTC().Format("2006-01-02 15:04:05"))
|
||||
err := rows.Count(&count)
|
||||
return count, err.Error
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ package core
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/ararog/timeago"
|
||||
"github.com/hunterlong/statup/core/notifier"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
|
@ -125,9 +126,17 @@ type DateScan struct {
|
|||
Value int64 `json:"y"`
|
||||
}
|
||||
|
||||
// DateScanObj struct is for creating the charts.js graph JSON array
|
||||
type DateScanObj struct {
|
||||
Array []DateScan
|
||||
}
|
||||
|
||||
// lastFailure returns the last failure a service had
|
||||
func (s *Service) lastFailure() *Failure {
|
||||
limited := s.LimitedFailures()
|
||||
if len(limited) == 0 {
|
||||
return nil
|
||||
}
|
||||
last := limited[len(limited)-1]
|
||||
return last
|
||||
}
|
||||
|
@ -146,47 +155,61 @@ func (s *Service) SmallText() string {
|
|||
}
|
||||
if len(last) > 0 {
|
||||
lastFailure := s.lastFailure()
|
||||
return fmt.Sprintf("%v on %v", lastFailure.ParseError(), utils.Timezoner(last[0].CreatedAt, zone).Format("Monday 3:04:05PM, Jan _2 2006"))
|
||||
got, _ := timeago.TimeAgoWithTime(time.Now().Add(s.Downtime()), time.Now())
|
||||
return fmt.Sprintf("Reported offline %v, %v", got, lastFailure.ParseError())
|
||||
} else {
|
||||
return fmt.Sprintf("%v is currently offline", s.Name)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Service) DowntimeText() string {
|
||||
lastFailure := s.lastFailure()
|
||||
if lastFailure == nil {
|
||||
return ""
|
||||
}
|
||||
got, _ := timeago.TimeAgoWithTime(time.Now().UTC().Add(s.Downtime()), time.Now().UTC())
|
||||
return fmt.Sprintf("Reported offline %v, %v", got, lastFailure.ParseError())
|
||||
}
|
||||
|
||||
// GroupDataBy returns a SQL query as a string to group a column by a time
|
||||
func GroupDataBy(column string, id int64, tm time.Time, increment string) string {
|
||||
func GroupDataBy(column string, id int64, start, end time.Time, increment string) string {
|
||||
var sql string
|
||||
switch CoreApp.DbConnection {
|
||||
case "mysql":
|
||||
sql = fmt.Sprintf("SELECT CONCAT(date_format(created_at, '%%Y-%%m-%%dT%%H:%%i:00Z')) AS created_at, AVG(latency)*1000 AS value FROM %v WHERE service=%v AND DATE_FORMAT(created_at, '%%Y-%%m-%%dT%%TZ') BETWEEN DATE_FORMAT('%v', '%%Y-%%m-%%dT%%TZ') AND DATE_FORMAT(NOW(), '%%Y-%%m-%%dT%%TZ') GROUP BY 1 ORDER BY created_at ASC;", column, id, tm.Format(time.RFC3339))
|
||||
sql = fmt.Sprintf("SELECT CONCAT(date_format(created_at, '%%Y-%%m-%%dT%%H:%%i:00Z')) AS created_at, AVG(latency)*1000 AS value FROM %v WHERE service=%v AND DATE_FORMAT(created_at, '%%Y-%%m-%%dT%%TZ') BETWEEN DATE_FORMAT('%v', '%%Y-%%m-%%dT%%TZ') AND DATE_FORMAT('%v', '%%Y-%%m-%%dT%%TZ') GROUP BY 1 ORDER BY created_at ASC;", column, id, start.UTC().Format(time.RFC3339), end.UTC().Format(time.RFC3339))
|
||||
case "sqlite":
|
||||
sql = fmt.Sprintf("SELECT strftime('%%Y-%%m-%%dT%%H:%%M:00Z', created_at), AVG(latency)*1000 as value FROM %v WHERE service=%v AND created_at >= '%v' GROUP BY strftime('%%M:00', created_at) ORDER BY created_at ASC;", column, id, tm.Format(time.RFC3339))
|
||||
sql = fmt.Sprintf("SELECT strftime('%%Y-%%m-%%dT%%H:%%M:00Z', created_at), AVG(latency)*1000 as value FROM %v WHERE service=%v AND created_at >= '%v' AND created_at <= '%v' GROUP BY strftime('%%M:00', created_at) ORDER BY created_at ASC;", column, id, start.UTC().Format(time.RFC3339), end.UTC().Format(time.RFC3339))
|
||||
case "postgres":
|
||||
sql = fmt.Sprintf("SELECT date_trunc('%v', created_at), AVG(latency)*1000 AS value FROM %v WHERE service=%v AND created_at >= '%v' GROUP BY 1 ORDER BY date_trunc ASC;", increment, column, id, tm.Format(time.RFC3339))
|
||||
sql = fmt.Sprintf("SELECT date_trunc('%v', created_at), AVG(latency)*1000 AS value FROM %v WHERE service=%v AND created_at >= '%v' AND created_at <= '%v' GROUP BY 1 ORDER BY date_trunc ASC;", increment, column, id, start.UTC().Format(time.RFC3339), end.UTC().Format(time.RFC3339))
|
||||
}
|
||||
return sql
|
||||
}
|
||||
|
||||
// Downtime returns the amount of time of a offline service
|
||||
func (s *Service) Downtime() time.Duration {
|
||||
hits, _ := s.Hits()
|
||||
if len(hits) == 0 {
|
||||
return time.Duration(0)
|
||||
}
|
||||
fails := s.LimitedFailures()
|
||||
if len(fails) == 0 {
|
||||
return time.Duration(0)
|
||||
}
|
||||
since := fails[0].CreatedAt.Sub(hits[0].CreatedAt)
|
||||
since := fails[0].CreatedAt.UTC().Sub(hits[0].CreatedAt.UTC())
|
||||
return since
|
||||
}
|
||||
|
||||
func (s *Service) GraphDataRaw() []*DateScan {
|
||||
var d []*DateScan
|
||||
since := time.Now().Add(time.Hour*-24 + time.Minute*0 + time.Second*0)
|
||||
sql := GroupDataBy("hits", s.Id, since, "minute")
|
||||
func GraphDataRaw(service types.ServiceInterface, start, end time.Time) *DateScanObj {
|
||||
var d []DateScan
|
||||
s := service.Select()
|
||||
sql := GroupDataBy("hits", s.Id, start, end, "minute")
|
||||
rows, err := DbSession.Raw(sql).Rows()
|
||||
if err != nil {
|
||||
utils.Log(2, err)
|
||||
return nil
|
||||
}
|
||||
for rows.Next() {
|
||||
gd := new(DateScan)
|
||||
var gd DateScan
|
||||
var tt string
|
||||
var ff float64
|
||||
err := rows.Scan(&tt, &ff)
|
||||
|
@ -201,12 +224,23 @@ func (s *Service) GraphDataRaw() []*DateScan {
|
|||
gd.Value = int64(ff)
|
||||
d = append(d, gd)
|
||||
}
|
||||
return d
|
||||
return &DateScanObj{d}
|
||||
}
|
||||
|
||||
func (d *DateScanObj) ToString() string {
|
||||
data, err := json.Marshal(d.Array)
|
||||
if err != nil {
|
||||
utils.Log(2, err)
|
||||
return "{}"
|
||||
}
|
||||
return string(data)
|
||||
}
|
||||
|
||||
// GraphData returns the JSON object used by Charts.js to render the chart
|
||||
func (s *Service) GraphData() string {
|
||||
obj := s.GraphDataRaw()
|
||||
start := time.Now().Add(time.Hour*-24 + time.Minute*0 + time.Second*0)
|
||||
end := time.Now()
|
||||
obj := GraphDataRaw(s, start, end)
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
utils.Log(2, err)
|
||||
|
|
|
@ -345,19 +345,19 @@ func TestGroupGraphData(t *testing.T) {
|
|||
service := SelectService(1)
|
||||
CoreApp.DbConnection = "mysql"
|
||||
lastWeek := time.Now().Add(time.Hour*-(24*7) + time.Minute*0 + time.Second*0)
|
||||
out := GroupDataBy("services", service.Id, lastWeek, "hour")
|
||||
out := GroupDataBy("services", service.Id, lastWeek, time.Now(), "hour")
|
||||
t.Log(out)
|
||||
assert.Contains(t, out, "SELECT CONCAT(date_format(created_at, '%Y-%m-%dT%H:%i:00Z'))")
|
||||
|
||||
CoreApp.DbConnection = "postgres"
|
||||
lastWeek = time.Now().Add(time.Hour*-(24*7) + time.Minute*0 + time.Second*0)
|
||||
out = GroupDataBy("services", service.Id, lastWeek, "hour")
|
||||
out = GroupDataBy("services", service.Id, lastWeek, time.Now(), "hour")
|
||||
t.Log(out)
|
||||
assert.Contains(t, out, "SELECT date_trunc('hour', created_at)")
|
||||
|
||||
CoreApp.DbConnection = "sqlite"
|
||||
lastWeek = time.Now().Add(time.Hour*-(24*7) + time.Minute*0 + time.Second*0)
|
||||
out = GroupDataBy("services", service.Id, lastWeek, "hour")
|
||||
out = GroupDataBy("services", service.Id, lastWeek, time.Now(), "hour")
|
||||
t.Log(out)
|
||||
assert.Contains(t, out, "SELECT strftime('%Y-%m-%dT%H:%M:00Z'")
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/hunterlong/statup/utils"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ApiResponse struct {
|
||||
|
@ -99,6 +100,22 @@ func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
fields := parseGet(r)
|
||||
|
||||
startField := utils.StringInt(fields.Get("start"))
|
||||
endField := utils.StringInt(fields.Get("end"))
|
||||
var start time.Time
|
||||
var end time.Time
|
||||
if startField == 0 {
|
||||
start = time.Now().Add(-24 * time.Hour).UTC()
|
||||
} else {
|
||||
start = time.Unix(startField, 0)
|
||||
}
|
||||
if endField == 0 {
|
||||
end = time.Now().UTC()
|
||||
} else {
|
||||
end = time.Unix(endField, 0)
|
||||
}
|
||||
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||
if service == nil {
|
||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||
|
@ -106,7 +123,7 @@ func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(service.GraphDataRaw())
|
||||
json.NewEncoder(w).Encode(core.GraphDataRaw(service, start, end).Array)
|
||||
}
|
||||
|
||||
func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -147,6 +147,12 @@ func executeResponse(w http.ResponseWriter, r *http.Request, file string, data i
|
|||
"ToString": func(v interface{}) string {
|
||||
return utils.ToString(v)
|
||||
},
|
||||
"ToUnix": func(t time.Time) int64 {
|
||||
return t.UTC().Unix()
|
||||
},
|
||||
"FromUnix": func(t int64) string {
|
||||
return utils.Timezoner(time.Unix(t, 0), core.CoreApp.Timezone).Format("Monday, January 02")
|
||||
},
|
||||
})
|
||||
t, err = t.Parse(nav)
|
||||
if err != nil {
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"github.com/hunterlong/statup/utils"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Service struct {
|
||||
|
@ -36,17 +37,56 @@ func renderServiceChartHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
fields := parseGet(r)
|
||||
|
||||
startField := fields.Get("start")
|
||||
endField := fields.Get("end")
|
||||
var start time.Time
|
||||
var end time.Time
|
||||
if startField == "" {
|
||||
start = time.Now().Add(-24 * time.Hour)
|
||||
} else {
|
||||
start = time.Unix(utils.StringInt(startField), 0)
|
||||
}
|
||||
if endField == "" {
|
||||
end = time.Now()
|
||||
} else {
|
||||
end = time.Unix(utils.StringInt(endField), 0)
|
||||
}
|
||||
|
||||
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||
w.Header().Set("Content-Type", "text/javascript")
|
||||
w.Header().Set("Cache-Control", "max-age=60")
|
||||
executeJSResponse(w, r, "charts.js", []*core.Service{service})
|
||||
|
||||
data := core.GraphDataRaw(service, start, end).ToString()
|
||||
|
||||
out := struct {
|
||||
Services []*core.Service
|
||||
Data []string
|
||||
}{[]*core.Service{service}, []string{data}}
|
||||
|
||||
executeJSResponse(w, r, "charts.js", out)
|
||||
}
|
||||
|
||||
func renderServiceChartsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
services := core.CoreApp.Services
|
||||
w.Header().Set("Content-Type", "text/javascript")
|
||||
w.Header().Set("Cache-Control", "max-age=60")
|
||||
executeJSResponse(w, r, "charts.js", services)
|
||||
|
||||
var data []string
|
||||
end := time.Now()
|
||||
start := end.Add(-24 * time.Hour)
|
||||
for _, s := range services {
|
||||
d := core.GraphDataRaw(s, start, end).ToString()
|
||||
data = append(data, d)
|
||||
}
|
||||
|
||||
out := struct {
|
||||
Services []types.ServiceInterface
|
||||
Data []string
|
||||
}{services, data}
|
||||
|
||||
executeJSResponse(w, r, "charts.js", out)
|
||||
}
|
||||
|
||||
func servicesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -139,12 +179,27 @@ func servicesDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func servicesViewHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
fields := parseGet(r)
|
||||
startField := utils.StringInt(fields.Get("start"))
|
||||
endField := utils.StringInt(fields.Get("end"))
|
||||
serv := core.SelectService(utils.StringInt(vars["id"]))
|
||||
if serv == nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
executeResponse(w, r, "service.html", serv, nil)
|
||||
|
||||
if startField == 0 || endField == 0 {
|
||||
startField = time.Now().Add(-24 * time.Hour).UTC().Unix()
|
||||
endField = time.Now().UTC().Unix()
|
||||
}
|
||||
|
||||
out := struct {
|
||||
Service *core.Service
|
||||
Start int64
|
||||
End int64
|
||||
}{serv, startField, endField}
|
||||
|
||||
executeResponse(w, r, "service.html", out, nil)
|
||||
}
|
||||
|
||||
func servicesUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -129,6 +129,11 @@ func parseForm(r *http.Request) url.Values {
|
|||
return r.PostForm
|
||||
}
|
||||
|
||||
func parseGet(r *http.Request) url.Values {
|
||||
r.ParseForm()
|
||||
return r.Form
|
||||
}
|
||||
|
||||
func saveNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var err error
|
||||
if !IsAuthenticated(r) {
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
// Statup
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statup
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
// Statup
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statup
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
// Statup
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statup
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
// Statup
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statup
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
// Statup
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statup
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,36 @@
|
|||
@charset "UTF-8";
|
||||
/*!
|
||||
* Statup
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statup
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/*!
|
||||
* Statup
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statup
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/* Index Page */
|
||||
/* Status Container */
|
||||
/* Button Colors */
|
||||
|
@ -6,53 +39,65 @@
|
|||
/* Mobile Settings */
|
||||
/* Mobile Service Container */
|
||||
HTML, BODY {
|
||||
background-color: #fcfcfc; }
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
max-width: 860px; }
|
||||
max-width: 860px;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
color: #464646; }
|
||||
color: #464646;
|
||||
}
|
||||
|
||||
.header-desc {
|
||||
color: #939393; }
|
||||
color: #939393;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border-radius: 0.2rem; }
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.online_list .badge {
|
||||
margin-top: 0.2rem; }
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-bottom: 30px; }
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
line-height: 1.3;
|
||||
font-size: 0.75rem; }
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.view_service_btn {
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
right: 40px; }
|
||||
right: 40px;
|
||||
}
|
||||
|
||||
.service_lower_info {
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
left: 40px;
|
||||
color: #d1ffca;
|
||||
font-size: 0.85rem; }
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.lg_number {
|
||||
font-size: 2.3rem;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
color: #4f4f4f; }
|
||||
color: #4f4f4f;
|
||||
}
|
||||
|
||||
.stats_area {
|
||||
text-align: center;
|
||||
color: #a5a5a5; }
|
||||
color: #a5a5a5;
|
||||
}
|
||||
|
||||
.lower_canvas {
|
||||
height: 3.4rem;
|
||||
|
@ -60,104 +105,134 @@ HTML, BODY {
|
|||
background-color: #48d338;
|
||||
padding: 15px 10px;
|
||||
margin-left: 0px !important;
|
||||
margin-right: 0px !important; }
|
||||
margin-right: 0px !important;
|
||||
}
|
||||
|
||||
.lower_canvas SPAN {
|
||||
font-size: 1rem;
|
||||
color: #fff; }
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-decoration: none;
|
||||
margin-top: 20px; }
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.footer A {
|
||||
color: #8d8d8d;
|
||||
text-decoration: none; }
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer A:HOVER {
|
||||
color: #6d6d6d; }
|
||||
color: #6d6d6d;
|
||||
}
|
||||
|
||||
.badge {
|
||||
color: white;
|
||||
border-radius: 0.2rem; }
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
height: 25px; }
|
||||
height: 25px;
|
||||
}
|
||||
.btn-group A {
|
||||
padding: 0.1rem .75rem;
|
||||
font-size: 0.8rem; }
|
||||
padding: 0.1rem 0.75rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.card-body .badge {
|
||||
color: #fff; }
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link {
|
||||
border-radius: 0.2rem; }
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border-radius: 0.2rem; }
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.125); }
|
||||
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
overflow: hidden; }
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-body H4 A {
|
||||
color: #444444;
|
||||
text-decoration: none; }
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 170px;
|
||||
width: 100%; }
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #3e9bff;
|
||||
border-color: #006fe6;
|
||||
color: white; }
|
||||
color: white;
|
||||
}
|
||||
.btn-primary.dyn-dark {
|
||||
background-color: #32a825 !important;
|
||||
border-color: #2c9320 !important; }
|
||||
border-color: #2c9320 !important;
|
||||
}
|
||||
.btn-primary.dyn-light {
|
||||
background-color: #75de69 !important;
|
||||
border-color: #88e37e !important; }
|
||||
border-color: #88e37e !important;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background-color: #47d337; }
|
||||
background-color: #47d337;
|
||||
}
|
||||
.btn-success.dyn-dark {
|
||||
background-color: #32a825 !important;
|
||||
border-color: #2c9320 !important; }
|
||||
border-color: #2c9320 !important;
|
||||
}
|
||||
.btn-success.dyn-light {
|
||||
background-color: #75de69 !important;
|
||||
border-color: #88e37e !important; }
|
||||
border-color: #88e37e !important;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: #dd3545; }
|
||||
background-color: #dd3545;
|
||||
}
|
||||
.btn-danger.dyn-dark {
|
||||
background-color: #b61f2d !important;
|
||||
border-color: #a01b28 !important; }
|
||||
border-color: #a01b28 !important;
|
||||
}
|
||||
.btn-danger.dyn-light {
|
||||
background-color: #e66975 !important;
|
||||
border-color: #e97f89 !important; }
|
||||
border-color: #e97f89 !important;
|
||||
}
|
||||
|
||||
.bg-success {
|
||||
background-color: #47d337 !important; }
|
||||
background-color: #47d337 !important;
|
||||
}
|
||||
|
||||
.bg-danger {
|
||||
background-color: #dd3545 !important; }
|
||||
background-color: #dd3545 !important;
|
||||
}
|
||||
|
||||
.bg-success .dyn-dark {
|
||||
background-color: #35b027 !important; }
|
||||
background-color: #35b027 !important;
|
||||
}
|
||||
|
||||
.bg-danger .dyn-dark {
|
||||
background-color: #bf202f !important; }
|
||||
background-color: #bf202f !important;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||
background-color: #13a00d; }
|
||||
background-color: #13a00d;
|
||||
}
|
||||
|
||||
.nav-pills A {
|
||||
color: #424242; }
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
/* Bootstrap Settings */
|
||||
|
@ -177,23 +252,26 @@ HTML, BODY {
|
|||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||
/* Code Mirror Settings */
|
||||
font-family: monospace;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 80vh; }
|
||||
height: 80vh;
|
||||
}
|
||||
|
||||
.CodeMirror-focused {
|
||||
/* Bootstrap Settings */
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; }
|
||||
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||
}
|
||||
|
||||
.switch {
|
||||
font-size: 1rem;
|
||||
position: relative; }
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
position: absolute;
|
||||
|
@ -204,7 +282,8 @@ HTML, BODY {
|
|||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
overflow: hidden;
|
||||
padding: 0; }
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.switch input + label {
|
||||
position: relative;
|
||||
|
@ -217,23 +296,26 @@ HTML, BODY {
|
|||
outline: none;
|
||||
user-select: none;
|
||||
vertical-align: middle;
|
||||
text-indent: calc(calc(calc(2.375rem * .8) * 2) + .5rem); }
|
||||
text-indent: calc(calc(calc(2.375rem * .8) * 2) + .5rem);
|
||||
}
|
||||
|
||||
.switch input + label::before,
|
||||
.switch input + label::after {
|
||||
content: '';
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: calc(calc(2.375rem * .8) * 2);
|
||||
bottom: 0;
|
||||
display: block; }
|
||||
display: block;
|
||||
}
|
||||
|
||||
.switch input + label::before {
|
||||
right: 0;
|
||||
background-color: #dee2e6;
|
||||
border-radius: calc(2.375rem * .8);
|
||||
transition: 0.2s all; }
|
||||
transition: 0.2s all;
|
||||
}
|
||||
|
||||
.switch input + label::after {
|
||||
top: 2px;
|
||||
|
@ -242,120 +324,154 @@ HTML, BODY {
|
|||
height: calc(calc(2.375rem * .8) - calc(2px * 2));
|
||||
border-radius: 50%;
|
||||
background-color: white;
|
||||
transition: 0.2s all; }
|
||||
transition: 0.2s all;
|
||||
}
|
||||
|
||||
.switch input:checked + label::before {
|
||||
background-color: #08d; }
|
||||
background-color: #08d;
|
||||
}
|
||||
|
||||
.switch input:checked + label::after {
|
||||
margin-left: calc(2.375rem * .8); }
|
||||
margin-left: calc(2.375rem * .8);
|
||||
}
|
||||
|
||||
.switch input:focus + label::before {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 0.2rem rgba(0, 136, 221, 0.25); }
|
||||
box-shadow: 0 0 0 0.2rem rgba(0, 136, 221, 0.25);
|
||||
}
|
||||
|
||||
.switch input:disabled + label {
|
||||
color: #868e96;
|
||||
cursor: not-allowed; }
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.switch input:disabled + label::before {
|
||||
background-color: #e9ecef; }
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
.switch.switch-sm {
|
||||
font-size: 0.875rem; }
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.switch.switch-sm input + label {
|
||||
min-width: calc(calc(1.9375rem * .8) * 2);
|
||||
height: calc(1.9375rem * .8);
|
||||
line-height: calc(1.9375rem * .8);
|
||||
text-indent: calc(calc(calc(1.9375rem * .8) * 2) + .5rem); }
|
||||
text-indent: calc(calc(calc(1.9375rem * .8) * 2) + .5rem);
|
||||
}
|
||||
|
||||
.switch.switch-sm input + label::before {
|
||||
width: calc(calc(1.9375rem * .8) * 2); }
|
||||
width: calc(calc(1.9375rem * .8) * 2);
|
||||
}
|
||||
|
||||
.switch.switch-sm input + label::after {
|
||||
width: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(1.9375rem * .8) - calc(2px * 2)); }
|
||||
height: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
||||
}
|
||||
|
||||
.switch.switch-sm input:checked + label::after {
|
||||
margin-left: calc(1.9375rem * .8); }
|
||||
margin-left: calc(1.9375rem * .8);
|
||||
}
|
||||
|
||||
.switch.switch-lg {
|
||||
font-size: 1.25rem; }
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.switch.switch-lg input + label {
|
||||
min-width: calc(calc(3rem * .8) * 2);
|
||||
height: calc(3rem * .8);
|
||||
line-height: calc(3rem * .8);
|
||||
text-indent: calc(calc(calc(3rem * .8) * 2) + .5rem); }
|
||||
text-indent: calc(calc(calc(3rem * .8) * 2) + .5rem);
|
||||
}
|
||||
|
||||
.switch.switch-lg input + label::before {
|
||||
width: calc(calc(3rem * .8) * 2); }
|
||||
width: calc(calc(3rem * .8) * 2);
|
||||
}
|
||||
|
||||
.switch.switch-lg input + label::after {
|
||||
width: calc(calc(3rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(3rem * .8) - calc(2px * 2)); }
|
||||
height: calc(calc(3rem * .8) - calc(2px * 2));
|
||||
}
|
||||
|
||||
.switch.switch-lg input:checked + label::after {
|
||||
margin-left: calc(3rem * .8); }
|
||||
margin-left: calc(3rem * .8);
|
||||
}
|
||||
|
||||
.switch + .switch {
|
||||
margin-left: 1rem; }
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
@keyframes pulse_animation {
|
||||
0% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
30% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
40% {
|
||||
transform: scale(1.02); }
|
||||
transform: scale(1.02);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
60% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
70% {
|
||||
transform: scale(1.05); }
|
||||
transform: scale(1.05);
|
||||
}
|
||||
80% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1); } }
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
.pulse {
|
||||
animation-name: pulse_animation;
|
||||
animation-duration: 1500ms;
|
||||
transform-origin: 70% 70%;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear; }
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
@keyframes glow-grow {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
80% {
|
||||
opacity: 1; }
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(2);
|
||||
opacity: 0; } }
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.pulse-glow {
|
||||
animation-name: glow-grown;
|
||||
animation-duration: 100ms;
|
||||
transform-origin: 70% 30%;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear; }
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
.pulse-glow:before,
|
||||
.pulse-glow:after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
content: "";
|
||||
height: 0.5rem;
|
||||
width: 1.75rem;
|
||||
top: 1.2rem;
|
||||
right: 2.15rem;
|
||||
border-radius: 0;
|
||||
box-shadow: 0 0 7px #47d337;
|
||||
animation: glow-grow 2s ease-out infinite; }
|
||||
animation: glow-grow 2s ease-out infinite;
|
||||
}
|
||||
|
||||
.sortable_drag {
|
||||
background-color: #0000000f; }
|
||||
background-color: #0000000f;
|
||||
}
|
||||
|
||||
.drag_icon {
|
||||
cursor: move;
|
||||
|
@ -369,82 +485,346 @@ HTML, BODY {
|
|||
margin-right: 5px;
|
||||
margin-left: -10px;
|
||||
text-align: center;
|
||||
color: #b1b1b1; }
|
||||
color: #b1b1b1;
|
||||
}
|
||||
|
||||
/* (Optional) Apply a "closed-hand" cursor during drag operation. */
|
||||
.drag_icon:active {
|
||||
cursor: grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing; }
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
|
||||
.switch_btn {
|
||||
float: right;
|
||||
margin: -1px 0px 0px 0px;
|
||||
display: block; }
|
||||
display: block;
|
||||
}
|
||||
|
||||
#start_container {
|
||||
position: absolute;
|
||||
z-index: 99999;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#end_container {
|
||||
position: absolute;
|
||||
z-index: 99999;
|
||||
margin-top: 20px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Pikaday
|
||||
* Copyright © 2014 David Bushell | BSD & MIT license | http://dbushell.com/
|
||||
*/
|
||||
.pika-single {
|
||||
z-index: 9999;
|
||||
display: block;
|
||||
position: relative;
|
||||
color: #333;
|
||||
background: #fff;
|
||||
border: 1px solid #ccc;
|
||||
border-bottom-color: #bbb;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
.pika-single.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
.pika-single.is-bound {
|
||||
position: absolute;
|
||||
box-shadow: 0 5px 15px -5px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.pika-single {
|
||||
*zoom: 1;
|
||||
}
|
||||
.pika-single:before, .pika-single:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.pika-single:after {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.pika-lendar {
|
||||
float: left;
|
||||
width: 240px;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.pika-title {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
}
|
||||
.pika-title select {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
z-index: 9998;
|
||||
margin: 0;
|
||||
left: 0;
|
||||
top: 5px;
|
||||
filter: alpha(opacity=0);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.pika-label {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
position: relative;
|
||||
z-index: 9999;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 5px 3px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.pika-prev,
|
||||
.pika-next {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
outline: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
width: 20px;
|
||||
height: 30px;
|
||||
text-indent: 20px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
background-color: transparent;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 75% 75%;
|
||||
opacity: 0.5;
|
||||
*position: absolute;
|
||||
*top: 0;
|
||||
}
|
||||
.pika-prev:hover,
|
||||
.pika-next:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
.pika-prev.is-disabled,
|
||||
.pika-next.is-disabled {
|
||||
cursor: default;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.pika-prev,
|
||||
.is-rtl .pika-next {
|
||||
float: left;
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==");
|
||||
*left: 0;
|
||||
}
|
||||
|
||||
.pika-next,
|
||||
.is-rtl .pika-prev {
|
||||
float: right;
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=");
|
||||
*right: 0;
|
||||
}
|
||||
|
||||
.pika-select {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
}
|
||||
|
||||
.pika-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border: 0;
|
||||
}
|
||||
.pika-table th,
|
||||
.pika-table td {
|
||||
width: 14.2857142857%;
|
||||
padding: 0;
|
||||
}
|
||||
.pika-table th {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
line-height: 25px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
.pika-table abbr {
|
||||
border-bottom: none;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.pika-button {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: right;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
.is-today .pika-button {
|
||||
color: #33aaff;
|
||||
font-weight: bold;
|
||||
}
|
||||
.is-selected .pika-button {
|
||||
color: #fff;
|
||||
font-weight: bold;
|
||||
background: #33aaff;
|
||||
box-shadow: inset 0 1px 3px #178fe5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.is-disabled .pika-button, .is-outside-current-month .pika-button {
|
||||
color: #999;
|
||||
opacity: 0.3;
|
||||
}
|
||||
.is-disabled .pika-button {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
.pika-button:hover {
|
||||
color: #fff;
|
||||
background: #ff8000;
|
||||
box-shadow: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.pika-button .is-selection-disabled {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.pika-week {
|
||||
font-size: 11px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.is-inrange .pika-button {
|
||||
background: #D5E9F7;
|
||||
}
|
||||
|
||||
.is-startrange .pika-button {
|
||||
color: #fff;
|
||||
background: #6CB31D;
|
||||
box-shadow: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.is-endrange .pika-button {
|
||||
color: #fff;
|
||||
background: #33aaff;
|
||||
box-shadow: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
/*!
|
||||
* Statup
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statup
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
@media (max-width: 767px) {
|
||||
HTML, BODY {
|
||||
background-color: #fcfcfc; }
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
|
||||
.sm-container {
|
||||
margin-top: 40px !important;
|
||||
padding: 0 !important; }
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.list-group-item H5 {
|
||||
font-size: 0.9rem; }
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0 !important; }
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-left: 0px;
|
||||
margin-top: 0px;
|
||||
width: 100%;
|
||||
margin-bottom: 0; }
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
line-height: 0.9rem;
|
||||
font-size: 0.65rem; }
|
||||
font-size: 0.65rem;
|
||||
}
|
||||
|
||||
.full-col-12 {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px; }
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 0;
|
||||
border-radius: 0rem;
|
||||
padding: 0;
|
||||
background-color: #ffffff; }
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
font-size: 6pt;
|
||||
padding: 5px 5px; }
|
||||
padding: 5px 5px;
|
||||
}
|
||||
|
||||
.lg_number {
|
||||
font-size: 7.8vw; }
|
||||
font-size: 7.8vw;
|
||||
}
|
||||
|
||||
.stats_area {
|
||||
margin-top: 1.5rem !important;
|
||||
margin-bottom: 1.5rem !important; }
|
||||
margin-bottom: 1.5rem !important;
|
||||
}
|
||||
|
||||
.stats_area .col-4 {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
font-size: 0.6rem; }
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
border-top: 1px solid #e4e4e4;
|
||||
border: 0px; }
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.list-group-item:first-child {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0; }
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-item:last-child {
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0; }
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-item P {
|
||||
font-size: 0.7rem; } }
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=base.css.map */
|
||||
|
|
|
@ -1,4 +1,21 @@
|
|||
{{ range . }}{{ if .AvgTime }}var ctx_{{.Id}}=document.getElementById("service_{{.Id}}").getContext('2d');var chartdata=new Chart(ctx_{{.Id}},{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:!1,scaleShowValues:!0,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(lowestnum>data.y){lowestnum=data.y;hxL=bar._model.x;hyL=bar._model.y}
|
||||
/*
|
||||
* Statup
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statup
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
{{$d := .Data}}{{ range $i, $s := .Services }}{{ if $s.AvgTime }}var ctx_{{$s.Id}}=document.getElementById("service_{{$s.Id}}").getContext('2d');var chartdata=new Chart(ctx_{{$s.Id}},{type:'line',data:{datasets:[{label:'Response Time (Milliseconds)',data:{{safe (index $d $i)}},backgroundColor:['rgba(47, 206, 30, 0.92)'],borderColor:['rgb(47, 171, 34)'],borderWidth:1}]},options:{maintainAspectRatio:!1,scaleShowValues:!0,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(lowestnum>data.y){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>=820){hxH=820}else if(50>=hxH){hxH=50}
|
||||
if(hxL>=820){hxL=820}else if(70>=hxL){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:!1},tooltips:{"enabled":!1},scales:{yAxes:[{display:!1,ticks:{fontSize:20,display:!1,beginAtZero:!1},gridLines:{display:!1}}],xAxes:[{type:'time',distribution:'series',autoSkip:!1,gridLines:{display:!1},ticks:{stepSize:1,min:0,fontColor:"white",fontSize:20,display:!1,}}]},elements:{point:{radius:0}}}})
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,3 +1,20 @@
|
|||
/*!
|
||||
* Statup
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statup
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@import 'variables';
|
||||
|
||||
|
||||
|
@ -441,4 +458,23 @@ HTML,BODY {
|
|||
display: block;
|
||||
}
|
||||
|
||||
@import 'mobile';
|
||||
#start_container {
|
||||
position: absolute;
|
||||
z-index: 99999;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#end_container {
|
||||
position: absolute;
|
||||
z-index: 99999;
|
||||
margin-top: 20px;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@import './pikaday';
|
||||
|
||||
@import './mobile';
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*!
|
||||
* Statup
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statup
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
@media (max-width: 767px) {
|
||||
|
||||
|
|
|
@ -0,0 +1,255 @@
|
|||
/*!
|
||||
* Pikaday
|
||||
* Copyright © 2014 David Bushell | BSD & MIT license | http://dbushell.com/
|
||||
*/
|
||||
|
||||
// Variables
|
||||
// Declare any of these variables before importing this SCSS file to easily override defaults
|
||||
// Variables are namespaced with the pd (pikaday) prefix
|
||||
|
||||
// Colours
|
||||
$pd-text-color: #333 !default;
|
||||
$pd-title-color: #333 !default;
|
||||
$pd-title-bg: #fff !default;
|
||||
$pd-picker-bg: #fff !default;
|
||||
$pd-picker-border: #ccc !default;
|
||||
$pd-picker-border-bottom: #bbb !default;
|
||||
$pd-picker-shadow: rgba(0,0,0,.5) !default;
|
||||
$pd-th-color: #999 !default;
|
||||
$pd-day-color: #666 !default;
|
||||
$pd-day-bg: #f5f5f5 !default;
|
||||
$pd-day-hover-color: #fff !default;
|
||||
$pd-day-hover-bg: #ff8000 !default;
|
||||
$pd-day-today-color: #33aaff !default;
|
||||
$pd-day-selected-color: #fff !default;
|
||||
$pd-day-selected-bg: #33aaff !default;
|
||||
$pd-day-selected-shadow: #178fe5 !default;
|
||||
$pd-day-disabled-color: #999 !default;
|
||||
$pd-week-color: #999 !default;
|
||||
|
||||
// Font
|
||||
$pd-font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !default;
|
||||
|
||||
|
||||
.pika-single {
|
||||
z-index: 9999;
|
||||
display: block;
|
||||
position: relative;
|
||||
color: $pd-text-color;
|
||||
background: $pd-picker-bg;
|
||||
border: 1px solid $pd-picker-border;
|
||||
border-bottom-color: $pd-picker-border-bottom;
|
||||
font-family: $pd-font-family;
|
||||
|
||||
&.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.is-bound {
|
||||
position: absolute;
|
||||
box-shadow: 0 5px 15px -5px $pd-picker-shadow;
|
||||
}
|
||||
}
|
||||
|
||||
// clear child float (pika-lendar), using the famous micro clearfix hack
|
||||
// http://nicolasgallagher.com/micro-clearfix-hack/
|
||||
.pika-single {
|
||||
*zoom: 1;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
&:after { clear: both }
|
||||
}
|
||||
|
||||
.pika-lendar {
|
||||
float: left;
|
||||
width: 240px;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.pika-title {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
select {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
z-index: 9998;
|
||||
margin: 0;
|
||||
left: 0;
|
||||
top: 5px;
|
||||
filter: alpha(opacity=0);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.pika-label {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
position: relative;
|
||||
z-index: 9999;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 5px 3px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
font-weight: bold;
|
||||
color: $pd-title-color;
|
||||
background-color: $pd-title-bg;
|
||||
}
|
||||
|
||||
.pika-prev,
|
||||
.pika-next {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
outline: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
width: 20px;
|
||||
height: 30px;
|
||||
text-indent: 20px; // hide text using text-indent trick, using width value (it's enough)
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
background-color: transparent;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 75% 75%;
|
||||
opacity: .5;
|
||||
*position: absolute;
|
||||
*top: 0;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
cursor: default;
|
||||
opacity: .2;
|
||||
}
|
||||
}
|
||||
|
||||
.pika-prev,
|
||||
.is-rtl .pika-next {
|
||||
float: left;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==');
|
||||
*left: 0;
|
||||
}
|
||||
|
||||
.pika-next,
|
||||
.is-rtl .pika-prev {
|
||||
float: right;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=');
|
||||
*right: 0;
|
||||
}
|
||||
|
||||
.pika-select {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
}
|
||||
|
||||
.pika-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border: 0;
|
||||
|
||||
th,
|
||||
td {
|
||||
width: 14.285714285714286%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
th {
|
||||
color: $pd-th-color;
|
||||
font-size: 12px;
|
||||
line-height: 25px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
abbr {
|
||||
border-bottom: none;
|
||||
cursor: help;
|
||||
}
|
||||
}
|
||||
|
||||
.pika-button {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
color: $pd-day-color;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: right;
|
||||
background: $pd-day-bg;
|
||||
|
||||
.is-today & {
|
||||
color: $pd-day-today-color;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.is-selected & {
|
||||
color: $pd-day-selected-color;
|
||||
font-weight: bold;
|
||||
background: $pd-day-selected-bg;
|
||||
box-shadow: inset 0 1px 3px $pd-day-selected-shadow;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.is-disabled &,
|
||||
.is-outside-current-month & {
|
||||
color: $pd-day-disabled-color;
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
.is-disabled & {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $pd-day-hover-color;
|
||||
background: $pd-day-hover-bg;
|
||||
box-shadow: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.is-selection-disabled {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.pika-week {
|
||||
font-size: 11px;
|
||||
color: $pd-week-color;
|
||||
}
|
||||
|
||||
.is-inrange .pika-button {
|
||||
background: #D5E9F7;
|
||||
}
|
||||
|
||||
.is-startrange .pika-button {
|
||||
color: #fff;
|
||||
background: #6CB31D;
|
||||
box-shadow: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.is-endrange .pika-button {
|
||||
color: #fff;
|
||||
background: #33aaff;
|
||||
box-shadow: none;
|
||||
border-radius: 3px;
|
||||
}
|
|
@ -1,3 +1,20 @@
|
|||
/*!
|
||||
* Statup
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statup
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/* Index Page */
|
||||
$background-color: #fcfcfc;
|
||||
$max-width: 860px;
|
||||
|
|
|
@ -158,6 +158,7 @@ func CreateAllAssets(folder string) error {
|
|||
CopyToPublic(ScssBox, folder+"/assets/scss", "base.scss")
|
||||
CopyToPublic(ScssBox, folder+"/assets/scss", "variables.scss")
|
||||
CopyToPublic(ScssBox, folder+"/assets/scss", "mobile.scss")
|
||||
CopyToPublic(ScssBox, folder+"/assets/scss", "pikaday.scss")
|
||||
CopyToPublic(CssBox, folder+"/assets/css", "bootstrap.min.css")
|
||||
CopyToPublic(CssBox, folder+"/assets/css", "base.css")
|
||||
//CopyToPublic(JsBox, folder+"/assets/js", "bootstrap.min.js")
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
{{$s := .Service}}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
@ -13,7 +14,7 @@
|
|||
<script src="/js/Chart.bundle.min.js"></script>
|
||||
{{end}}
|
||||
|
||||
<title>Statup | {{.Name}} Service</title>
|
||||
<title>Statup | {{$s.Name}} Service</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
@ -25,14 +26,14 @@
|
|||
|
||||
<div class="col-12 mb-4">
|
||||
|
||||
{{if .Online }}
|
||||
{{if $s.Online }}
|
||||
<span class="mt-3 mb-3 text-white d-md-none btn bg-success d-block d-md-none">ONLINE</span>
|
||||
{{ else }}
|
||||
<span class="mt-3 mb-3 text-white d-md-none btn bg-danger d-block d-md-none">OFFLINE</span>
|
||||
{{end}}
|
||||
|
||||
<h4 class="mt-2">{{ .Name }}
|
||||
{{if .Online }}
|
||||
<h4 class="mt-2">{{ $s.Name }}
|
||||
{{if $s.Online }}
|
||||
<span class="badge bg-success float-right d-none d-md-block">ONLINE</span>
|
||||
{{ else }}
|
||||
<span class="badge bg-danger float-right d-none d-md-block">OFFLINE</span>
|
||||
|
@ -41,28 +42,42 @@
|
|||
<div class="row stats_area mt-5 mb-5">
|
||||
|
||||
<div class="col-4">
|
||||
<span class="lg_number">{{.Online24}}%</span>
|
||||
<span class="lg_number">{{$s.Online24}}%</span>
|
||||
Online last 24 Hours
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<span class="lg_number">{{.AvgTime}}ms</span>
|
||||
<span class="lg_number">{{$s.AvgTime}}ms</span>
|
||||
Average Response
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<span class="lg_number">{{.TotalUptime}}%</span>
|
||||
<span class="lg_number">{{$s.TotalUptime}}%</span>
|
||||
Total Uptime
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="chart-container">
|
||||
<canvas id="service_{{ .Id }}"></canvas>
|
||||
<div class="chart-container" style="height: 250px">
|
||||
<canvas id="service_{{ $s.Id }}"></canvas>
|
||||
</div>
|
||||
|
||||
{{ if .LimitedFailures }}
|
||||
<form id="service_date_form" class="row mt-2 mb-3">
|
||||
<span id="start_date" class="text-muted small float-left pointer">{{FromUnix .Start}}</span>
|
||||
<span id="end_date" class="text-muted small float-right pointer" style="position: absolute;right: 0;">{{FromUnix .End}}</span>
|
||||
<input type="hidden" name="start" class="form-control" id="service_start" spellcheck="false">
|
||||
<input type="hidden" name="end" class="form-control" id="service_end" spellcheck="false">
|
||||
<button type="submit" class="btn btn-light btn-block btn-sm mt-2">Set Timeframe</button>
|
||||
<div id="start_container"></div>
|
||||
<div id="end_container"></div>
|
||||
</form>
|
||||
|
||||
{{if not $s.Online}}
|
||||
<div class="col-12 small text-center mt-3 text-muted">{{$s.DowntimeText}}</div>
|
||||
{{end}}
|
||||
|
||||
{{ if $s.LimitedFailures }}
|
||||
<div class="list-group mt-3 mb-4">
|
||||
{{ range .LimitedFailures }}
|
||||
{{ range $s.LimitedFailures }}
|
||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">{{.ParseError}}</h5>
|
||||
|
@ -83,82 +98,82 @@
|
|||
|
||||
<h3>Edit Service</h3>
|
||||
|
||||
<form action="/service/{{.Id}}" method="POST">
|
||||
<form action="/service/{{$s.Id}}" method="POST">
|
||||
<div class="form-group row">
|
||||
<label for="service_name" class="col-sm-4 col-form-label">Service Name</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="name" class="form-control" id="service_name" value="{{.Name}}" placeholder="Name" required spellcheck="false">
|
||||
<input type="text" name="name" class="form-control" id="service_name" value="{{$s.Name}}" placeholder="Name" required spellcheck="false">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="service_type" class="col-sm-4 col-form-label">Service Check Type</label>
|
||||
<div class="col-sm-8">
|
||||
<select name="check_type" class="form-control" id="service_type" value="{{.Type}}">
|
||||
<option value="http" {{if eq .Type "http"}}selected{{end}}>HTTP Service</option>
|
||||
<option value="tcp" {{if eq .Type "tcp"}}selected{{end}}>TCP Service</option>
|
||||
<select name="check_type" class="form-control" id="service_type" value="{{$s.Type}}">
|
||||
<option value="http" {{if eq $s.Type "http"}}selected{{end}}>HTTP Service</option>
|
||||
<option value="tcp" {{if eq $s.Type "tcp"}}selected{{end}}>TCP Service</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="service_url" class="col-sm-4 col-form-label">Application Endpoint (URL)</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="text" name="domain" class="form-control" id="service_url" value="{{.Domain}}" placeholder="https://google.com" required autocapitalize="false" spellcheck="false">
|
||||
<input type="text" name="domain" class="form-control" id="service_url" value="{{$s.Domain}}" placeholder="https://google.com" required autocapitalize="false" spellcheck="false">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row{{if eq .Type "tcp"}} d-none{{end}}">
|
||||
<div class="form-group row{{if eq $s.Type "tcp"}} d-none{{end}}">
|
||||
<label for="service_check_type" class="col-sm-4 col-form-label">Service Check Type</label>
|
||||
<div class="col-sm-8">
|
||||
<select name="method" class="form-control" id="service_check_type" value="{{.Method}}">
|
||||
<option value="GET" {{if eq .Method "GET"}}selected{{end}}>GET</option>
|
||||
<option value="POST" {{if eq .Method "POST"}}selected{{end}}>POST</option>
|
||||
<option value="DELETE" {{if eq .Method "DELETE"}}selected{{end}}>DELETE</option>
|
||||
<option value="PATCH" {{if eq .Method "PATCH"}}selected{{end}}>PATCH</option>
|
||||
<option value="PUT" {{if eq .Method "PUT"}}selected{{end}}>PUT</option>
|
||||
<select name="method" class="form-control" id="service_check_type" value="{{$s.Method}}">
|
||||
<option value="GET" {{if eq $s.Method "GET"}}selected{{end}}>GET</option>
|
||||
<option value="POST" {{if eq $s.Method "POST"}}selected{{end}}>POST</option>
|
||||
<option value="DELETE" {{if eq $s.Method "DELETE"}}selected{{end}}>DELETE</option>
|
||||
<option value="PATCH" {{if eq $s.Method "PATCH"}}selected{{end}}>PATCH</option>
|
||||
<option value="PUT" {{if eq $s.Method "PUT"}}selected{{end}}>PUT</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row{{if ne .Method "POST"}} d-none{{end}}">
|
||||
<div class="form-group row{{if ne $s.Method "POST"}} d-none{{end}}">
|
||||
<label for="post_data" class="col-sm-4 col-form-label">Optional Post Data (JSON)</label>
|
||||
<div class="col-sm-8">
|
||||
<textarea name="post_data" class="form-control" id="post_data" rows="3" autocapitalize="false" spellcheck="false">{{.PostData}}</textarea>
|
||||
<textarea name="post_data" class="form-control" id="post_data" rows="3" autocapitalize="false" spellcheck="false">{{$s.PostData}}</textarea>
|
||||
<small id="emailHelp" class="form-text text-muted">You can insert <a target="_blank" href="https://regex101.com/r/I5bbj9/1">Regex</a> to validate the response</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row{{if eq .Type "tcp"}} d-none{{end}}">
|
||||
<div class="form-group row{{if eq $s.Type "tcp"}} d-none{{end}}">
|
||||
<label for="service_response" class="col-sm-4 col-form-label">Expected Response (Regex)</label>
|
||||
<div class="col-sm-8">
|
||||
<textarea name="expected" class="form-control" id="service_response" rows="3" autocapitalize="false" spellcheck="false">{{.Expected}}</textarea>
|
||||
<textarea name="expected" class="form-control" id="service_response" rows="3" autocapitalize="false" spellcheck="false">{{$s.Expected}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row{{if eq .Type "tcp"}} d-none{{end}}">
|
||||
<div class="form-group row{{if eq $s.Type "tcp"}} d-none{{end}}">
|
||||
<label for="service_response_code" class="col-sm-4 col-form-label">Expected Status Code</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" name="expected_status" class="form-control" value="{{.ExpectedStatus}}" id="service_response_code">
|
||||
<input type="number" name="expected_status" class="form-control" value="{{$s.ExpectedStatus}}" id="service_response_code">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row{{if eq .Type "http"}} d-none{{end}}">
|
||||
<div class="form-group row{{if eq $s.Type "http"}} d-none{{end}}">
|
||||
<label for="service_port" class="col-sm-4 col-form-label">TCP Port</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" name="port" class="form-control" value="{{.Port}}" id="service_port" placeholder="8080">
|
||||
<input type="number" name="port" class="form-control" value="{{$s.Port}}" id="service_port" placeholder="8080">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="service_interval" class="col-sm-4 col-form-label">Check Interval (Seconds)</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" name="interval" class="form-control" value="{{.Interval}}" min="1" id="service_interval" required>
|
||||
<input type="number" name="interval" class="form-control" value="{{$s.Interval}}" min="1" id="service_interval" required>
|
||||
<small id="emailHelp" class="form-text text-muted">10,000+ will be checked in Microseconds (1 millisecond = 1000 microseconds).</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="service_timeout" class="col-sm-4 col-form-label">Timeout in Seconds</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" name="timeout" class="form-control" value="{{.Timeout}}" id="service_timeout" min="1">
|
||||
<input type="number" name="timeout" class="form-control" value="{{$s.Timeout}}" id="service_timeout" min="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="order" class="col-sm-4 col-form-label">List Order</label>
|
||||
<div class="col-sm-8">
|
||||
<input type="number" name="order" class="form-control" min="0" value="{{.Order}}" id="order">
|
||||
<input type="number" name="order" class="form-control" min="0" value="{{$s.Order}}" id="order">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
|
@ -166,34 +181,33 @@
|
|||
<button type="submit" class="btn btn-success btn-block">Update Service</button>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<a href="/service/{{ .Id }}/delete_failures" class="btn btn-danger btn-block confirm-btn">Delete All Failures</a>
|
||||
<a href="/service/{{ $s.Id }}/delete_failures" class="btn btn-danger btn-block confirm-btn">Delete All Failures</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-12 mt-4{{if eq .Type "tcp"}} d-none{{end}}">
|
||||
<div class="col-12 mt-4{{if eq $s.Type "tcp"}} d-none{{end}}">
|
||||
<h3>Last Response</h3>
|
||||
<textarea rows="8" class="form-control" readonly>{{ .LastResponse }}</textarea>
|
||||
<textarea rows="8" class="form-control" readonly>{{ $s.LastResponse }}</textarea>
|
||||
<div class="form-group row mt-2">
|
||||
<label for="last_status_code" class="col-sm-3 col-form-label">HTTP Status Code</label>
|
||||
<div class="col-sm-2">
|
||||
<input type="text" id="last_status_code" class="form-control" value="{{ .LastStatusCode }}" readonly>
|
||||
<input type="text" id="last_status_code" class="form-control" value="{{ $s.LastStatusCode }}" readonly>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="col-12 mt-4{{if eq .Type "tcp"}} d-none{{end}}">
|
||||
<div class="col-12 mt-4{{if eq $s.Type "tcp"}} d-none{{end}}">
|
||||
<h3>Service Checkins</h3>
|
||||
{{ range .Checkins }}
|
||||
{{ range $s.Checkins }}
|
||||
<h5>Check #{{.Id}} <span class="badge online_badge float-right">Checked in {{.Ago}}</span></h5>
|
||||
<input type="text" class="form-control" value="https://domainhere.com/api/checkin/{{.Api}}">
|
||||
{{ end }}
|
||||
|
||||
<form action="/service/{{.Id}}/checkin" method="POST">
|
||||
<form action="/service/{{$s.Id}}/checkin" method="POST">
|
||||
<div class="form-group row">
|
||||
<label for="service_name" class="col-sm-4 col-form-label">Check Interval (in seconds)</label>
|
||||
<div class="col-md-6 col-sm-12">
|
||||
|
@ -222,15 +236,58 @@
|
|||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js"></script>
|
||||
<script src="https://assets.statup.io/pikaday.js"></script>
|
||||
<script src="https://assets.statup.io/main.js"></script>
|
||||
{{ else }}
|
||||
<script src="/js/jquery-3.3.1.min.js"></script>
|
||||
<script src="/js/bootstrap.min.js"></script>
|
||||
<script src="/js/Chart.bundle.min.js"></script>
|
||||
<script src="/js/pikaday.js"></script>
|
||||
<script src="/js/main.js"></script>
|
||||
{{end}}
|
||||
|
||||
<script src="/charts/{{.Id}}.js"></script>
|
||||
<script>
|
||||
var startPick = new Pikaday({
|
||||
field: $('#service_start')[0],
|
||||
bound: false,
|
||||
trigger: $("#start_date"),
|
||||
container: $("#start_container")[0],
|
||||
maxDate: new Date(),
|
||||
onSelect: function(date) {
|
||||
$('#service_start')[0].value = Math.round(date.getTime() / 1000);
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
|
||||
var endPick = new Pikaday({
|
||||
field: $('#service_end')[0],
|
||||
bound: false,
|
||||
trigger: $("#end_date"),
|
||||
container: $("#end_container")[0],
|
||||
maxDate: new Date(),
|
||||
onSelect: function(date) {
|
||||
$('#service_end')[0].value = Math.round(date.getTime() / 1000);
|
||||
this.hide();
|
||||
}
|
||||
});
|
||||
|
||||
startPick.setDate(new Date({{.Start}}*1000));
|
||||
endPick.setDate(new Date({{.End}}*1000));
|
||||
|
||||
startPick.hide()
|
||||
endPick.hide()
|
||||
|
||||
$("#start_date").click(function(e) {
|
||||
startPick.show()
|
||||
})
|
||||
|
||||
$("#end_date").click(function(e) {
|
||||
endPick.show()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<script src="/charts/{{$s.Id}}.js?start={{.Start}}&end={{.End}}"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
// Statup
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statup
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
// Statup
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statup
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
// Statup
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statup
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
|
|
|
@ -47,6 +47,7 @@ type Service struct {
|
|||
DnsLookup float64 `gorm:"-" json:"dns_lookup_time"`
|
||||
Failures []interface{} `gorm:"-" json:"failures,omitempty"`
|
||||
Checkins []*Checkin `gorm:"-" json:"checkins,omitempty"`
|
||||
Range [2]time.Time `gorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
type ServiceInterface interface {
|
||||
|
|
|
@ -1,3 +1,18 @@
|
|||
// Statup
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statup
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
|
|
Loading…
Reference in New Issue