mirror of https://github.com/statping/statping
changes n upgrades
parent
cb70a0134d
commit
09bc158e5e
29
failures.go
29
failures.go
|
@ -1,31 +1,44 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"github.com/ararog/timeago"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
type Failure struct {
|
type Failure struct {
|
||||||
Id int
|
Id int
|
||||||
Issue string
|
Issue string
|
||||||
Service int
|
Service int
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
|
Ago string
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectAllFailures(id int64) []float64 {
|
func (s *Service) SelectAllFailures() []*Failure {
|
||||||
var tks []float64
|
var tks []*Failure
|
||||||
rows, err := db.Query("SELECT * FROM failures WHERE service=$1 ORDER BY id ASC", id)
|
rows, err := db.Query("SELECT * FROM failures WHERE service=$1 ORDER BY id DESC LIMIT 10", s.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var tk Hit
|
var tk Failure
|
||||||
err = rows.Scan(&tk.Id, &tk.Metric, &tk.Value, &tk.CreatedAt)
|
err = rows.Scan(&tk.Id, &tk.Issue, &tk.Service, &tk.CreatedAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
tks = append(tks, tk.Value)
|
|
||||||
|
tk.Ago, _ = timeago.TimeAgoWithTime(time.Now(), tk.CreatedAt)
|
||||||
|
|
||||||
|
tks = append(tks, &tk)
|
||||||
}
|
}
|
||||||
return tks
|
return tks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CountFailures() int {
|
||||||
|
var amount int
|
||||||
|
db.QueryRow("SELECT COUNT(id) FROM failures;").Scan(&amount)
|
||||||
|
return amount
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) TotalFailures() int {
|
func (s *Service) TotalFailures() int {
|
||||||
var amount int
|
var amount int
|
||||||
db.QueryRow("SELECT COUNT(id) FROM failures WHERE service=$1;", s.Id).Scan(&amount)
|
db.QueryRow("SELECT COUNT(id) FROM failures WHERE service=$1;", s.Id).Scan(&amount)
|
||||||
|
@ -36,6 +49,6 @@ func (s *Service) TotalFailures24Hours() int {
|
||||||
var amount int
|
var amount int
|
||||||
t := time.Now()
|
t := time.Now()
|
||||||
x := t.AddDate(0, 0, -1)
|
x := t.AddDate(0, 0, -1)
|
||||||
db.QueryRow("SELECT COUNT(id) FROM failures WHERE service=$1 AND created_at>=$2 AND created_at<$3;", s.Id, t, x).Scan(&amount)
|
db.QueryRow("SELECT COUNT(id) FROM failures WHERE service=$1 AND created_at>=$2 AND created_at<$3;", s.Id, x, t).Scan(&amount)
|
||||||
return amount
|
return amount
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,14 +21,36 @@ HTML,BODY {
|
||||||
font-size: 26pt;
|
font-size: 26pt;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
display: block;
|
display: block;
|
||||||
|
color: #3e3e3e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_perfect {
|
||||||
|
color: #33b418;
|
||||||
|
text-shadow: 0px 1px 0 #0e6702;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_good {
|
||||||
|
color: #33b418;
|
||||||
|
text-shadow: 0px 1px 0 #0e6702;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_ok {
|
||||||
|
color: #33b418;
|
||||||
|
text-shadow: 0px 1px 0 #0e6702;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text_bad {
|
||||||
|
color: #33b418;
|
||||||
|
text-shadow: 0px 1px 0 #0e6702;
|
||||||
}
|
}
|
||||||
|
|
||||||
.stats_area {
|
.stats_area {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
color: #a5a5a5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.offline_bg {
|
.offline_bg {
|
||||||
background-color: #c5c5c578 !important;
|
background-color: white !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
|
@ -41,6 +63,28 @@ HTML,BODY {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.online_badge {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #35b317;
|
||||||
|
}
|
||||||
|
|
||||||
|
.offline_badge {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #c51919;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress {
|
||||||
|
margin-top: -20px;
|
||||||
|
margin-left: -20px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
width: calc(100% + 40px);
|
||||||
|
height: 3px;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-body {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
|
|
||||||
|
|
|
@ -43,18 +43,18 @@
|
||||||
<div class="row stats_area">
|
<div class="row stats_area">
|
||||||
|
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="lg_number">69</span>
|
<span class="lg_number">{{ .CountServices }}</span>
|
||||||
24 Hour Hits
|
Total Services
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="lg_number">3921</span>
|
<span class="lg_number">{{ .Count24Failures }}</span>
|
||||||
24 Hour Hits
|
Failures last 24 Hours
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="lg_number">453</span>
|
<span class="lg_number">{{ .CountOnline }}</span>
|
||||||
Total Tokens
|
Online Services
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,22 +8,33 @@
|
||||||
<link rel="stylesheet" href="/css/base.css">
|
<link rel="stylesheet" href="/css/base.css">
|
||||||
<script src="/js/Chart.bundle.min.js"></script>
|
<script src="/js/Chart.bundle.min.js"></script>
|
||||||
|
|
||||||
<title>Statup | Dashboard</title>
|
<title>{{.Project}} Status</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<h1 class="text-center">{{.Project}}</h1>
|
<h1 class="text-center mb-4">{{.Project}}</h1>
|
||||||
|
|
||||||
<div class="container">
|
<div class="container">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|
||||||
{{ range .Services }}
|
{{ range .Services }}
|
||||||
<div class="col-12 mb-4">
|
<div class="col-12 mb-4">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body{{if .Online}}{{else}} offline_bg{{end}}">
|
<div class="card-body{{if .Online}}{{else}} offline_bg{{end}}">
|
||||||
<h3>{{ .Name }} <span class="badge badge-secondary float-right">{{if .Online}} ONLINE {{ else }} OFFLINE {{end}}</span></h3>
|
|
||||||
|
|
||||||
<div class="row stats_area mt-3 mb-3">
|
<div class="progress">
|
||||||
|
<div class="progress-bar {{if .Online24Hours}} bg-success {{else}} bg-danger {{end}}" role="progressbar" style="width: {{.Online24Hours}}%" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<h3 class="mt-4">{{ .Name }}
|
||||||
|
{{if .Online}}
|
||||||
|
<span class="badge online_badge float-right">ONLINE</span>
|
||||||
|
{{ else }}
|
||||||
|
<span class="badge offline_badge float-right">OFFLINE</span>
|
||||||
|
{{end}}</h3>
|
||||||
|
|
||||||
|
<div class="row stats_area mt-5 mb-5">
|
||||||
|
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="lg_number">{{.Online24Hours}}%</span>
|
<span class="lg_number">{{.Online24Hours}}%</span>
|
||||||
|
@ -39,10 +50,17 @@
|
||||||
<span class="lg_number">{{.TotalUptime}}%</span>
|
<span class="lg_number">{{.TotalUptime}}%</span>
|
||||||
Total Uptime
|
Total Uptime
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<canvas id="service_{{ .Id }}" width="400" height="120"></canvas>
|
<canvas id="service_{{ .Id }}" width="400" height="120"></canvas>
|
||||||
|
|
||||||
|
{{ range .Failures }}
|
||||||
|
<blockquote class="blockquote text-right mt-3">
|
||||||
|
<p class="mb-0">{{.Issue}}</p>
|
||||||
|
<footer class="blockquote-footer">Reported <cite title="Source Title">{{.Ago}}</cite></footer>
|
||||||
|
</blockquote>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,7 +71,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="footer text-center">
|
<div class="footer text-center">
|
||||||
<a href="https://statup.io" target="_blank">Statup.io Opensource Status Page</a>
|
<a href="https://statup.io" target="_blank">Created with Statup.io</a> | <a href="/dashboard">Dashboard</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
|
@ -52,7 +52,8 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col">#</th>
|
<th scope="col">#</th>
|
||||||
<th scope="col">Name</th>
|
<th scope="col">Name</th>
|
||||||
<th scope="col">Actions</th>
|
<th scope="col">Status</th>
|
||||||
|
<th scope="col"></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -60,11 +61,12 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="row">{{.Id}}</th>
|
<th scope="row">{{.Id}}</th>
|
||||||
<td>{{.Name}}</td>
|
<td>{{.Name}}</td>
|
||||||
<td>
|
<td>{{.Online}} <span class="badge badge-danger">OFFLINE</span> </td>
|
||||||
|
<td class="text-right">
|
||||||
<div class="btn-group" data-toggle="buttons">
|
<div class="btn-group" data-toggle="buttons">
|
||||||
<button class="btn btn-primary" type="submit">View</button>
|
<a href="/services/{{.Id}}" class="btn btn-primary">View</a>
|
||||||
<button class="btn btn-primary" type="submit">Edit</button>
|
<a href="/services/{{.Id}}/edit" class="btn btn-primary">Edit</a>
|
||||||
<button class="btn btn-primary" type="submit">Delete</button>
|
<a href="/services/{{.Id}}/delete" class="btn btn-danger">Delete</a>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -81,8 +83,8 @@
|
||||||
|
|
||||||
<form action="/services/create" method="POST">
|
<form action="/services/create" method="POST">
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="inputEmail3" class="col-sm-2 col-form-label">Name</label>
|
<label for="inputEmail3" class="col-sm-4 col-form-label">Service Name</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-8">
|
||||||
<input type="text" name="name" class="form-control" id="inputEmail3" placeholder="Name">
|
<input type="text" name="name" class="form-control" id="inputEmail3" placeholder="Name">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -118,7 +120,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
<button type="submit" class="btn btn-primary">Create</button>
|
<a class="btn btn-primary">Test</a>
|
||||||
|
<button type="submit" class="btn btn-success">Create Service</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestMakeConfig(t *testing.T) {
|
func TestMakeConfig(t *testing.T) {
|
||||||
|
|
42
services.go
42
services.go
|
@ -5,6 +5,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -25,9 +26,10 @@ type Service struct {
|
||||||
Data string
|
Data string
|
||||||
Online bool
|
Online bool
|
||||||
Latency float64
|
Latency float64
|
||||||
Online24Hours float64
|
Online24Hours float32
|
||||||
AvgResponse string
|
AvgResponse string
|
||||||
TotalUptime float64
|
TotalUptime string
|
||||||
|
Failures []*Failure
|
||||||
}
|
}
|
||||||
|
|
||||||
func SelectService(id string) Service {
|
func SelectService(id string) Service {
|
||||||
|
@ -57,6 +59,7 @@ func SelectAllServices() []*Service {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
tk.Failures = tk.SelectAllFailures()
|
||||||
tk.FormatData()
|
tk.FormatData()
|
||||||
tks = append(tks, &tk)
|
tks = append(tks, &tk)
|
||||||
}
|
}
|
||||||
|
@ -79,7 +82,7 @@ func (s *Service) AvgTime() float64 {
|
||||||
return avg
|
return avg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Online24() float64 {
|
func (s *Service) Online24() float32 {
|
||||||
total := s.TotalHits()
|
total := s.TotalHits()
|
||||||
failed := s.TotalFailures24Hours()
|
failed := s.TotalFailures24Hours()
|
||||||
if failed == 0 {
|
if failed == 0 {
|
||||||
|
@ -91,8 +94,13 @@ func (s *Service) Online24() float64 {
|
||||||
return s.Online24Hours
|
return s.Online24Hours
|
||||||
}
|
}
|
||||||
avg := float64(failed) / float64(total) * 100
|
avg := float64(failed) / float64(total) * 100
|
||||||
s.Online24Hours = avg
|
avg = 100 - avg
|
||||||
return avg
|
if avg < 0 {
|
||||||
|
avg = 0
|
||||||
|
}
|
||||||
|
amount, _ := strconv.ParseFloat(fmt.Sprintf("%0.2f", avg), 10)
|
||||||
|
s.Online24Hours = float32(amount)
|
||||||
|
return s.Online24Hours
|
||||||
}
|
}
|
||||||
|
|
||||||
type GraphJson struct {
|
type GraphJson struct {
|
||||||
|
@ -116,20 +124,24 @@ func (s *Service) GraphData() string {
|
||||||
return s.Data
|
return s.Data
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) AvgUptime() float64 {
|
func (s *Service) AvgUptime() string {
|
||||||
failed := s.TotalFailures()
|
failed := s.TotalFailures()
|
||||||
total := s.TotalHits()
|
total := s.TotalHits()
|
||||||
if failed == 0 {
|
if failed == 0 {
|
||||||
s.TotalUptime = 100.00
|
s.TotalUptime = "100.00"
|
||||||
return s.TotalUptime
|
return s.TotalUptime
|
||||||
}
|
}
|
||||||
if total == 0 {
|
if total == 0 {
|
||||||
s.TotalUptime = 0
|
s.TotalUptime = "0"
|
||||||
return s.TotalUptime
|
return s.TotalUptime
|
||||||
}
|
}
|
||||||
percent := float64(failed) / float64(total) * 100
|
percent := float64(failed) / float64(total) * 100
|
||||||
s.TotalUptime = percent
|
percent = 100 - percent
|
||||||
return percent
|
if percent < 0 {
|
||||||
|
percent = 0
|
||||||
|
}
|
||||||
|
s.TotalUptime = fmt.Sprintf("%0.2f", percent)
|
||||||
|
return s.TotalUptime
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *Service) Create() int {
|
func (u *Service) Create() int {
|
||||||
|
@ -143,6 +155,16 @@ func (u *Service) Create() int {
|
||||||
return lastInsertId
|
return lastInsertId
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CountOnline() int {
|
||||||
|
amount := 0
|
||||||
|
for _, v := range services {
|
||||||
|
if v.Online {
|
||||||
|
amount++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return amount
|
||||||
|
}
|
||||||
|
|
||||||
func NewSHA1Hash(n ...int) string {
|
func NewSHA1Hash(n ...int) string {
|
||||||
noRandomCharacters := 32
|
noRandomCharacters := 32
|
||||||
|
|
||||||
|
|
16
web.go
16
web.go
|
@ -7,12 +7,6 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
type dashboard struct {
|
|
||||||
Services []*Service
|
|
||||||
Users []User
|
|
||||||
Core *Core
|
|
||||||
}
|
|
||||||
|
|
||||||
func RunHTTPServer() {
|
func RunHTTPServer() {
|
||||||
fmt.Println("Fusioner HTTP Server running on http://localhost:8080")
|
fmt.Println("Fusioner HTTP Server running on http://localhost:8080")
|
||||||
css := http.StripPrefix("/css/", http.FileServer(cssBox.HTTPBox()))
|
css := http.StripPrefix("/css/", http.FileServer(cssBox.HTTPBox()))
|
||||||
|
@ -149,6 +143,14 @@ func IndexHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
indexTmpl.Execute(w, out)
|
indexTmpl.Execute(w, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type dashboard struct {
|
||||||
|
Services []*Service
|
||||||
|
Core *Core
|
||||||
|
CountOnline int
|
||||||
|
CountServices int
|
||||||
|
Count24Failures int
|
||||||
|
}
|
||||||
|
|
||||||
func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
session, _ := store.Get(r, "apizer_auth")
|
session, _ := store.Get(r, "apizer_auth")
|
||||||
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth {
|
||||||
|
@ -170,7 +172,7 @@ func DashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
out := dashboard{services, SelectAllUsers(), core}
|
out := dashboard{services, core, CountOnline(), len(services), CountFailures()}
|
||||||
dashboardTmpl.Execute(w, out)
|
dashboardTmpl.Execute(w, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue