mirror of https://github.com/statping/statping
new
parent
6dc81e6849
commit
3866da7aad
|
@ -18,7 +18,7 @@ services:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- VERSION=0.27.8
|
- VERSION=0.27.81
|
||||||
- DB_HOST=localhost
|
- DB_HOST=localhost
|
||||||
- DB_USER=travis
|
- DB_USER=travis
|
||||||
- DB_PASS=
|
- DB_PASS=
|
||||||
|
|
|
@ -20,9 +20,9 @@ curl -s -X POST \
|
||||||
-d "$body" \
|
-d "$body" \
|
||||||
https://api.travis-ci.com/repo/hunterlong%2Fhomebrew-statup/requests
|
https://api.travis-ci.com/repo/hunterlong%2Fhomebrew-statup/requests
|
||||||
|
|
||||||
if [ "$TRAVIS_BRANCH" == "master" ]
|
#if [ "$TRAVIS_BRANCH" == "master" ]
|
||||||
then
|
#then
|
||||||
curl -X POST $DOCKER > /dev/null
|
# curl -X POST $DOCKER > /dev/null
|
||||||
else
|
#else
|
||||||
curl -H "Content-Type: application/json" --data '{"source_type": "Tag", "source_name": "v'"$VERSION"'"}' -X POST $DOCKER > /dev/null
|
# curl -H "Content-Type: application/json" --data '{"source_type": "Tag", "source_name": "v'"$VERSION"'"}' -X POST $DOCKER > /dev/null
|
||||||
fi
|
#fi
|
|
@ -1,6 +1,6 @@
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
|
||||||
ENV VERSION=v0.27.8
|
ENV VERSION=v0.27.81
|
||||||
|
|
||||||
RUN apk --no-cache add libstdc++ ca-certificates
|
RUN apk --no-cache add libstdc++ ca-certificates
|
||||||
RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \
|
RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \
|
||||||
|
|
|
@ -96,10 +96,11 @@ aws ec2 run-instances \
|
||||||
```
|
```
|
||||||
|
|
||||||
## Prometheus Exporter
|
## Prometheus Exporter
|
||||||
Statup includes a prometheus exporter so you can have even more monitoring power with your services. The prometheus exporter can be seen on `/metrics`, simply create another exporter in your prometheus config.
|
Statup includes a prometheus exporter so you can have even more monitoring power with your services. The prometheus exporter can be seen on `/metrics`, simply create another exporter in your prometheus config. Use your Statup API Secret for the Authorization Bearer header, the `/metrics` URL is dedicated for Prometheus and requires the correct API Secret has `Authorization` header.
|
||||||
```yaml
|
```yaml
|
||||||
scrape_configs:
|
scrape_configs:
|
||||||
- job_name: 'statup'
|
- job_name: 'statup'
|
||||||
|
bearer_token: MY API SECRET HERE
|
||||||
static_configs:
|
static_configs:
|
||||||
- targets: ['statup:8080']
|
- targets: ['statup:8080']
|
||||||
```
|
```
|
||||||
|
|
|
@ -116,18 +116,17 @@ func (c *DbConfig) Save() error {
|
||||||
DropDatabase()
|
DropDatabase()
|
||||||
CreateDatabase()
|
CreateDatabase()
|
||||||
|
|
||||||
newCore := Core{
|
newCore := &Core{
|
||||||
Name: c.Project,
|
Name: c.Project,
|
||||||
Description: c.Description,
|
Description: c.Description,
|
||||||
Config: "config.yml",
|
Config: "config.yml",
|
||||||
ApiKey: utils.NewSHA1Hash(5),
|
ApiKey: utils.NewSHA1Hash(9),
|
||||||
ApiSecret: utils.NewSHA1Hash(10),
|
ApiSecret: utils.NewSHA1Hash(16),
|
||||||
Domain: c.Domain,
|
Domain: c.Domain,
|
||||||
}
|
}
|
||||||
|
|
||||||
col := DbSession.Collection("core")
|
col := DbSession.Collection("core")
|
||||||
_, err = col.Insert(newCore)
|
_, err = col.Insert(newCore)
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,21 @@ type DateScan struct {
|
||||||
Value int64 `json:"y"`
|
Value int64 `json:"y"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) SmallText() string {
|
||||||
|
last := s.LimitedFailures()
|
||||||
|
hits, _ := s.LimitedHits()
|
||||||
|
if !s.Online {
|
||||||
|
return fmt.Sprintf("%v at %v", last[0].ParseError(), last[0].CreatedAt.Format("Monday 3:04PM, Jan _2 2006"))
|
||||||
|
} else {
|
||||||
|
if len(last) == 0 {
|
||||||
|
return fmt.Sprintf("Online since %v", s.CreatedAt.Format("Monday 3:04PM, Jan _2 2006"))
|
||||||
|
} else {
|
||||||
|
return fmt.Sprintf("Online, last failure was %v", hits[0].CreatedAt.Format("Monday 3:04PM, Jan _2 2006"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("No Failures in the last 24 hours! %v", hits[0])
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) GraphData() string {
|
func (s *Service) GraphData() string {
|
||||||
var d []DateScan
|
var d []DateScan
|
||||||
increment := "minute"
|
increment := "minute"
|
||||||
|
|
|
@ -3,12 +3,24 @@ package handlers
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hunterlong/statup/core"
|
"github.com/hunterlong/statup/core"
|
||||||
|
"github.com/hunterlong/statup/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func PrometheusHandler(w http.ResponseWriter, r *http.Request) {
|
func PrometheusHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Printf("Prometheus /metrics Request From IP: %v\n", r.RemoteAddr)
|
utils.Log(1, fmt.Sprintf("Prometheus /metrics Request From IP: %v\n", r.RemoteAddr))
|
||||||
|
var token string
|
||||||
|
tokens, ok := r.Header["Authorization"]
|
||||||
|
if ok && len(tokens) >= 1 {
|
||||||
|
token = tokens[0]
|
||||||
|
token = strings.TrimPrefix(token, "Bearer ")
|
||||||
|
}
|
||||||
|
if token != core.CoreApp.ApiSecret {
|
||||||
|
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
metrics := []string{}
|
metrics := []string{}
|
||||||
system := fmt.Sprintf("statup_total_failures %v\n", core.CountFailures())
|
system := fmt.Sprintf("statup_total_failures %v\n", core.CountFailures())
|
||||||
system += fmt.Sprintf("statup_total_services %v", len(core.CoreApp.Services))
|
system += fmt.Sprintf("statup_total_services %v", len(core.CoreApp.Services))
|
||||||
|
|
|
@ -52,7 +52,7 @@ func Router() *mux.Router {
|
||||||
r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceUpdateHandler)).Methods("POST")
|
r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceUpdateHandler)).Methods("POST")
|
||||||
r.Handle("/api/users", http.HandlerFunc(ApiAllUsersHandler))
|
r.Handle("/api/users", http.HandlerFunc(ApiAllUsersHandler))
|
||||||
r.Handle("/api/users/{id}", http.HandlerFunc(ApiUserHandler))
|
r.Handle("/api/users/{id}", http.HandlerFunc(ApiUserHandler))
|
||||||
r.Handle("/metrics", http.HandlerFunc(PrometheusHandler)).Methods("GET")
|
r.Handle("/metrics", http.HandlerFunc(PrometheusHandler))
|
||||||
Store = sessions.NewCookieStore([]byte("secretinfo"))
|
Store = sessions.NewCookieStore([]byte("secretinfo"))
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="lower_canvas full-col-12 text-white{{if not .Online}} bg-danger{{end}}">
|
<div class="lower_canvas full-col-12 text-white{{if not .Online}} bg-danger{{end}}">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<span>No failures within the last 25 days</span>
|
<span>{{.SmallText}}</span>
|
||||||
<a href="/service/{{ .Id }}" class="btn {{if .Online}}btn-success{{else}}btn-danger{{end}} btn-sm float-right">View Service</a>
|
<a href="/service/{{ .Id }}" class="btn {{if .Online}}btn-success{{else}}btn-danger{{end}} btn-sm float-right">View Service</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -64,6 +64,17 @@
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary btn-block">Save Settings</button>
|
<button type="submit" class="btn btn-primary btn-block">Save Settings</button>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="formGroupExampleInput">API Key</label>
|
||||||
|
<input type="text" name="description" class="form-control" value="{{ .ApiKey }}" id="formGroupExampleInput" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="formGroupExampleInput">API Secret</label>
|
||||||
|
<input type="text" name="description" class="form-control" value="{{ .ApiSecret }}" id="formGroupExampleInput" readonly>
|
||||||
|
</div>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -52,53 +52,25 @@
|
||||||
|
|
||||||
<canvas id="service" width="400" height="120"></canvas>
|
<canvas id="service" width="400" height="120"></canvas>
|
||||||
|
|
||||||
{{ range .Failures }}
|
{{ if .LimitedFailures }}
|
||||||
<blockquote class="blockquote text-right mt-3">
|
<div class="list-group mt-5">
|
||||||
<p class="mb-0">{{.ParseError}}</p>
|
{{ range .LimitedFailures }}
|
||||||
<footer class="blockquote-footer">Reported <cite title="Source Title">{{.Ago}}</cite></footer>
|
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
||||||
</blockquote>
|
<div class="d-flex w-100 justify-content-between">
|
||||||
{{ end }}
|
<h5 class="mb-1">{{.ParseError}}</h5>
|
||||||
|
<small>Reported {{.Ago}}</small>
|
||||||
|
</div>
|
||||||
|
<p class="mb-1">{{.Issue}}</p>
|
||||||
|
</a>
|
||||||
|
{{ end }}
|
||||||
|
</div>
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{{if Auth}}
|
{{if Auth}}
|
||||||
|
|
||||||
<div class="col-12 mt-4">
|
|
||||||
|
|
||||||
<h3>Service Checkins</h3>
|
|
||||||
|
|
||||||
{{ range .Checkins }}
|
|
||||||
|
|
||||||
<div class="col-12 mt-3">
|
|
||||||
|
|
||||||
<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}}">
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{ end }}
|
|
||||||
|
|
||||||
<form action="/service/{{.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-sm-8">
|
|
||||||
<input type="number" name="name" class="form-control" id="checkin_interval" value="30" placeholder="Name">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="form-group row">
|
|
||||||
<div class="col-sm-10">
|
|
||||||
<button type="submit" class="btn btn-success">Save Checkin</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<div class="col-12 mt-4">
|
<div class="col-12 mt-4">
|
||||||
|
|
||||||
<h3>Edit Service</h3>
|
<h3>Edit Service</h3>
|
||||||
|
@ -186,19 +158,37 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
|
|
||||||
{{ if .LimitedFailures }}
|
<div class="col-12 mt-4">
|
||||||
<div class="list-group mt-5">
|
<h3>Service Checkins</h3>
|
||||||
{{ range .LimitedFailures }}
|
|
||||||
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
|
{{ range .Checkins }}
|
||||||
<div class="d-flex w-100 justify-content-between">
|
|
||||||
<h5 class="mb-1">{{.ParseError}}</h5>
|
<h5>Check #{{.Id}} <span class="badge online_badge float-right">Checked in {{.Ago}}</span></h5>
|
||||||
<small>Reported {{.Ago}}</small>
|
|
||||||
</div>
|
<input type="text" class="form-control" value="https://domainhere.com/api/checkin/{{.Api}}">
|
||||||
<p class="mb-1">{{.Issue}}</p>
|
|
||||||
</a>
|
|
||||||
{{ end }}
|
</div>
|
||||||
|
|
||||||
|
{{ end }}
|
||||||
|
|
||||||
|
|
||||||
|
<form action="/service/{{.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-sm-8">
|
||||||
|
<input type="number" name="name" class="form-control" id="checkin_interval" value="30" placeholder="Name">
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
<div class="form-group row">
|
||||||
|
<div class="col-sm-10">
|
||||||
|
<button type="submit" class="btn btn-success">Save Checkin</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue