mirror of https://github.com/statping/statping
Merge branch 'checkin-updates' into sass-design
commit
6a0f14c4e3
|
@ -1,8 +1,12 @@
|
|||
# 0.90.60 (07-15-2020)
|
||||
- Added LETSENCRYPT_ENABLE (boolean) env to enable/disable letsencrypt SSL
|
||||
|
||||
# 0.90.59 (07-14-2020)
|
||||
- Added LetsEncrypt SSL Generator by using LETSENCRYPT_HOST and LETSENCRYPT_EMAIL envs.
|
||||
- Modified JWT token key to be sha256 of API Secret
|
||||
- Modified github actions to build multi-arch Docker images
|
||||
- Added "update" command to install latest version
|
||||
- Fixed dashboard uptime_data API request to request correct start/time timestamp
|
||||
|
||||
# 0.90.58 (07-09-2020)
|
||||
- Fixed ICMP latency/ping durations
|
||||
|
|
|
@ -155,6 +155,10 @@ class Api {
|
|||
return axios.delete('api/incidents/'+incident.id).then(response => (response.data))
|
||||
}
|
||||
|
||||
async checkin(api) {
|
||||
return axios.get('api/checkins/'+api).then(response => (response.data))
|
||||
}
|
||||
|
||||
async checkin_create(data) {
|
||||
return axios.post('api/checkins', data).then(response => (response.data))
|
||||
}
|
||||
|
|
|
@ -2,40 +2,81 @@
|
|||
<div class="col-12">
|
||||
<h2>{{service.name}} Checkins</h2>
|
||||
<p class="mb-3">Tell your service to send a routine HTTP request to a Statping Checkin.</p>
|
||||
<div v-for="(checkin, i) in checkins" class="col-12 alert alert-light" role="alert">
|
||||
<span class="badge badge-pill badge-info text-uppercase">{{checkin.name}}</span>
|
||||
<span class="float-right font-2">Last checkin {{ago(checkin.last_hit)}}</span>
|
||||
<span class="float-right font-2 mr-3">Check Every {{checkin.interval}} seconds</span>
|
||||
<span class="float-right font-2 mr-3">Grace Period {{checkin.grace}} seconds</span>
|
||||
<span class="d-block mt-2">
|
||||
<input type="text" class="form-control" :value="`${core.domain}/checkin/${checkin.api_key}`" readonly>
|
||||
<span class="small">Send a GET request to this URL every {{checkin.interval}} seconds
|
||||
<button @click="deleteCheckin(checkin)" type="button" class="btn btn-danger btn-xs float-right mt-1">Delete</button>
|
||||
|
||||
<div v-for="(checkin, i) in checkins" class="card text-black-50 bg-white mt-3">
|
||||
<div class="card-header text-capitalize">
|
||||
{{checkin.name}}
|
||||
<button @click="deleteCheckin(checkin)" class="btn btn-sm btn-danger float-right text-uppercase">Delete</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" :value="`${core.domain}/checkin/${checkin.api_key}`" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(`${core.domain}/checkin/${checkin.api_key}`)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="small">Send a GET request to this URL every {{checkin.interval}} minutes</span>
|
||||
<span class="small float-right mt-1">Requested {{ago(checkin.last_hit)}} ago</span>
|
||||
<span class="small float-right mt-1 mr-3">Request expected every {{checkin.interval}} minutes</span>
|
||||
|
||||
<div class="card text-black-50 bg-white mt-3">
|
||||
<div class="card-header text-capitalize">
|
||||
<font-awesome-icon @click="expanded = !expanded" :icon="expanded ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
{{checkin.name}} Records
|
||||
</div>
|
||||
<div class="card-body" :class="{'d-none': !expanded}">
|
||||
<div class="alert alert-primary small" :class="{'alert-success': hit.success, 'alert-danger': !hit.success}" v-for="(hit, i) in records(checkin)">
|
||||
Checkin {{hit.success ? "Request" : "Failure"}} at {{hit.created_at}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card text-black-50 bg-white mt-3">
|
||||
<div class="card-header text-capitalize">
|
||||
<font-awesome-icon @click="curl_expanded = !curl_expanded" :icon="curl_expanded ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Cronjob Task
|
||||
</div>
|
||||
<div class="card-body" :class="{'d-none': !curl_expanded}">
|
||||
This cronjob script will request the checkin endpoint every {{checkin.interval}} minutes. Add this cronjob task to the machine running this service.
|
||||
<div class="input-group mt-2">
|
||||
<input type="text" class="form-control" :value="`${checkin.interval} * * * * /usr/bin/curl ${core.domain}/checkin/${checkin.api_key} >/dev/null 2>&1`" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(`${checkin.interval} * * * * /usr/bin/curl ${core.domain}/checkin/${checkin.api_key} >/dev/null 2>&1`)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
<span class="small d-block">Using CURL</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<span :class="{'text-success': last_record(checkin).success, 'text-danger': !last_record(checkin).success}">
|
||||
{{last_record(checkin).success ? "Checkin is currently working correctly" : "Checkin is currently failing"}}
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 alert alert-light">
|
||||
<div class="card text-black-50 bg-white mt-4">
|
||||
<div class="card-header text-capitalize">Create Checkin</div>
|
||||
<div class="card-body">
|
||||
<form @submit.prevent="saveCheckin">
|
||||
<div class="form-group row">
|
||||
<div class="col-5">
|
||||
<label for="checkin_interval" class="col-form-label">Checkin Name</label>
|
||||
<input v-model="checkin.name" type="text" name="name" class="form-control" id="checkin_name" placeholder="New Checkin">
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<label for="checkin_interval" class="col-form-label">Interval</label>
|
||||
<input v-model="checkin.interval" type="number" name="interval" class="form-control" id="checkin_interval" placeholder="60">
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<label for="grace_period" class="col-form-label">Grace Period</label>
|
||||
<input v-model="checkin.grace" type="number" name="grace" class="form-control" id="grace_period" placeholder="10">
|
||||
<div class="col-3">
|
||||
<label for="checkin_interval" class="col-form-label">Interval (minutes)</label>
|
||||
<input v-model="checkin.interval" type="number" name="interval" class="form-control" id="checkin_interval" placeholder="1" min="1">
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<label class="col-form-label"></label>
|
||||
<button @click.prevent="saveCheckin" type="submit" id="submit" class="btn btn-primary d-block mt-2">Save Checkin</button>
|
||||
<button :disabled="btn_disabled" @click.prevent="saveCheckin" type="submit" id="submit" class="btn btn-primary d-block mt-2">Save Checkin</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -49,10 +90,11 @@ export default {
|
|||
return {
|
||||
service: {},
|
||||
ready: false,
|
||||
expanded: false,
|
||||
curl_expanded: false,
|
||||
checkin: {
|
||||
name: "",
|
||||
interval: 60,
|
||||
grace: 60,
|
||||
interval: 1,
|
||||
service_id: 0,
|
||||
hits: [],
|
||||
failures: []
|
||||
|
@ -66,6 +108,12 @@ export default {
|
|||
core() {
|
||||
return this.$store.getters.core
|
||||
},
|
||||
btn_disabled() {
|
||||
if (this.checkin.name === "" || this.checkin.interval <= 0) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
if (this.$route.params) {
|
||||
|
@ -76,22 +124,37 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
records(checkin) {
|
||||
let hits = []
|
||||
let failures = []
|
||||
checkin.hits.forEach((hit) => {
|
||||
hits.push({success: true, created_at: this.parseISO(hit.created_at), id: hit.id})
|
||||
})
|
||||
checkin.failures.forEach((failure) => {
|
||||
failures.push({success: false, created_at: this.parseISO(failure.created_at), id: failure.id})
|
||||
})
|
||||
return hits.concat(failures).sort((a, b) => {return a.created_at-b.created_at}).reverse().slice(0,32)
|
||||
},
|
||||
last_record(checkin) {
|
||||
const r = this.records(checkin)
|
||||
return r[0]
|
||||
},
|
||||
fixInts() {
|
||||
const c = this.checkin
|
||||
this.checkin.interval = parseInt(c.interval)
|
||||
this.checkin.grace = parseInt(c.grace)
|
||||
return this.checkin
|
||||
},
|
||||
async saveCheckin() {
|
||||
const c = this.fixInts()
|
||||
await Api.checkin_create(c)
|
||||
await this.updateCheckins()
|
||||
this.checkin.name = ""
|
||||
await this.load()
|
||||
},
|
||||
async deleteCheckin(checkin) {
|
||||
await Api.checkin_delete(checkin)
|
||||
await this.updateCheckins()
|
||||
await this.load()
|
||||
},
|
||||
async updateCheckins() {
|
||||
async load() {
|
||||
const checkins = await Api.checkins()
|
||||
this.$store.commit('setCheckins', checkins)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="service-chart-container">
|
||||
<apexchart width="100%" height="420" type="area" :options="main_chart_options" :series="main_chart"></apexchart>
|
||||
<div class="card text-black-50 bg-white mt-3 mb-3">
|
||||
<div class="card-header text-capitalize">Service Latency</div>
|
||||
<div class="card-body">
|
||||
<div class="service-chart-container">
|
||||
<apexchart width="100%" height="420" type="area" :options="main_chart_options" :series="main_chart"></apexchart>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
<input v-model="checkin.name" type="text" name="name" class="form-control" id="checkin_name" placeholder="New Checkin">
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<label for="checkin_interval" class="col-form-label">Interval</label>
|
||||
<input v-model="checkin.interval" type="number" name="interval" class="form-control" id="checkin_interval" placeholder="60">
|
||||
<label for="checkin_interval" class="col-form-label">Interval (minutes)</label>
|
||||
<input v-model="checkin.interval" type="number" name="interval" class="form-control" id="checkin_interval" placeholder="1" min="1">
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<label for="grace_period" class="col-form-label">Grace Period</label>
|
||||
|
|
|
@ -85,7 +85,6 @@ export default Vue.mixin({
|
|||
copy(txt) {
|
||||
this.$copyText(txt).then(function (e) {
|
||||
alert('Copied: \n'+txt)
|
||||
console.log(e)
|
||||
});
|
||||
},
|
||||
serviceLink(service) {
|
||||
|
|
|
@ -18,41 +18,51 @@
|
|||
|
||||
<MessageBlock v-for="message in messagesInRange" v-bind:key="message.id" :message="message"/>
|
||||
|
||||
<div class="row mt-5 mb-4">
|
||||
<div class="col-12 col-md-5 font-2 mb-3 mb-md-0">
|
||||
<flatPickr :disabled="loading" @on-change="onnn" v-model="start_time" :config="{ enableTime: true, altInput: true, altFormat: 'Y-m-d h:i K', maxDate: new Date() }" type="text" class="btn btn-white text-left" required />
|
||||
<small class="d-block">From {{this.format(new Date(start_time))}}</small>
|
||||
</div>
|
||||
<div class="col-12 col-md-5 font-2 mb-3 mb-md-0">
|
||||
<flatPickr :disabled="loading" @on-change="onnn" v-model="end_time" :config="{ enableTime: true, altInput: true, altFormat: 'Y-m-d h:i K', maxDate: new Date()}" type="text" class="btn btn-white text-left" required />
|
||||
<small class="d-block">To {{this.format(new Date(end_time))}}</small>
|
||||
</div>
|
||||
<div class="col-12 col-md-2">
|
||||
<select :disabled="loading" @change="chartHits" v-model="group" class="form-control">
|
||||
<option value="1m">1 Minute</option>
|
||||
<option value="5m">5 Minutes</option>
|
||||
<option value="15m">15 Minute</option>
|
||||
<option value="30m">30 Minutes</option>
|
||||
<option value="1h">1 Hour</option>
|
||||
<option value="3h">3 Hours</option>
|
||||
<option value="6h">6 Hours</option>
|
||||
<option value="12h">12 Hours</option>
|
||||
<option value="24h">1 Day</option>
|
||||
<option value="168h">7 Days</option>
|
||||
<option value="360h">15 Days</option>
|
||||
</select>
|
||||
<small class="d-block d-md-none d-block">Increment Timeframe</small>
|
||||
<div class="card text-black-50 bg-white mt-3">
|
||||
<div class="card-header text-capitalize">Timeframe</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4 font-2">
|
||||
<flatPickr :disabled="loading" @on-change="onnn" v-model="start_time" :config="{ enableTime: true, altInput: true, altFormat: 'Y-m-d h:i K', maxDate: new Date() }" type="text" class="btn btn-white text-left" required />
|
||||
<small class="d-block">From {{this.format(new Date(start_time))}}</small>
|
||||
</div>
|
||||
<div class="col-12 col-md-4 font-2">
|
||||
<flatPickr :disabled="loading" @on-change="onnn" v-model="end_time" :config="{ enableTime: true, altInput: true, altFormat: 'Y-m-d h:i K', maxDate: new Date()}" type="text" class="btn btn-white text-left" required />
|
||||
<small class="d-block">To {{this.format(new Date(end_time))}}</small>
|
||||
</div>
|
||||
<div class="col-12 col-md-4">
|
||||
<select :disabled="loading" @change="chartHits" v-model="group" class="form-control">
|
||||
<option value="1m">1 Minute</option>
|
||||
<option value="5m">5 Minutes</option>
|
||||
<option value="15m">15 Minute</option>
|
||||
<option value="30m">30 Minutes</option>
|
||||
<option value="1h">1 Hour</option>
|
||||
<option value="3h">3 Hours</option>
|
||||
<option value="6h">6 Hours</option>
|
||||
<option value="12h">12 Hours</option>
|
||||
<option value="24h">1 Day</option>
|
||||
<option value="168h">7 Days</option>
|
||||
<option value="360h">15 Days</option>
|
||||
</select>
|
||||
<small class="d-block d-md-none d-block">Increment Timeframe</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<AdvancedChart :group="group" :updated="updated_chart" :start="start_time.toString()" :end="end_time.toString()" :service="service"/>
|
||||
|
||||
<div v-if="!loading" class="col-12">
|
||||
<div v-if="!loading" class="row">
|
||||
<apexchart width="100%" height="120" type="rangeBar" :options="timeRangeOptions" :series="uptime_data"></apexchart>
|
||||
</div>
|
||||
|
||||
<div class="service-chart-heatmap mt-5 mb-4">
|
||||
<ServiceHeatmap :service="service"/>
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-header text-capitalize">Service Failures</div>
|
||||
<div class="card-body">
|
||||
<div class="service-chart-heatmap mt-5 mb-4">
|
||||
<ServiceHeatmap :service="service"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -378,7 +388,7 @@ export default {
|
|||
this.loading = false
|
||||
},
|
||||
async fetchUptime() {
|
||||
const uptime = await Api.service_uptime(this.id, this.params.start, this.params.end)
|
||||
const uptime = await Api.service_uptime(this.service.id, this.params.start, this.params.end)
|
||||
this.uptime_data = this.parse_uptime(uptime)
|
||||
},
|
||||
parse_uptime(timedata) {
|
||||
|
|
|
@ -2,6 +2,7 @@ package handlers
|
|||
|
||||
import (
|
||||
"github.com/statping/statping/utils"
|
||||
"net/url"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
@ -106,6 +107,13 @@ func (s Storage) Delete(key string) {
|
|||
func (s Storage) Set(key string, content []byte, duration time.Duration) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
u, err := url.Parse(key)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if u.Query().Get("v") != "" {
|
||||
return
|
||||
}
|
||||
s.items[key] = Item{
|
||||
Content: content,
|
||||
Expiration: utils.Now().Add(duration).UnixNano(),
|
||||
|
|
|
@ -24,8 +24,7 @@ func findCheckin(r *http.Request) (*checkins.Checkin, string, error) {
|
|||
}
|
||||
|
||||
func apiAllCheckinsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
chks := checkins.All()
|
||||
returnJson(chks, w, r)
|
||||
returnJson(checkins.All(), w, r)
|
||||
}
|
||||
|
||||
func apiCheckinHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -39,8 +38,7 @@ func apiCheckinHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func checkinCreateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var checkin *checkins.Checkin
|
||||
err := DecodeJSON(r, &checkin)
|
||||
if err != nil {
|
||||
if err := DecodeJSON(r, &checkin); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
@ -63,22 +61,27 @@ func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
log.Infof("Checking %s was requested", checkin.Name)
|
||||
|
||||
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||
|
||||
if last := checkin.LastHit(); last == nil {
|
||||
checkin.Start()
|
||||
}
|
||||
|
||||
hit := &checkins.CheckinHit{
|
||||
Checkin: checkin.Id,
|
||||
From: ip,
|
||||
CreatedAt: utils.Now(),
|
||||
}
|
||||
log.Infof("Checking %s was requested", checkin.Name)
|
||||
|
||||
err = hit.Create()
|
||||
if err != nil {
|
||||
if err := hit.Create(); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
checkin.Failing = false
|
||||
checkin.LastHitTime = utils.Now()
|
||||
|
||||
sendJsonAction(hit.Id, "update", w, r)
|
||||
}
|
||||
|
||||
|
|
|
@ -45,8 +45,7 @@ func TestApiCheckinRoutes(t *testing.T) {
|
|||
Body: `{
|
||||
"name": "Example Checkin",
|
||||
"service_id": 1,
|
||||
"checkin_interval": 300,
|
||||
"grace_period": 60,
|
||||
"interval": 300,
|
||||
"api_key": "example"
|
||||
}`,
|
||||
},
|
||||
|
|
|
@ -56,7 +56,7 @@ func RunHTTPServer() error {
|
|||
resetCookies()
|
||||
httpError = make(chan error)
|
||||
|
||||
if utils.Params.GetString("LETSENCRYPT_HOST") != "" {
|
||||
if utils.Params.GetBool("LETSENCRYPT_ENABLE") {
|
||||
go startLetsEncryptServer(ip)
|
||||
} else if usingSSL {
|
||||
go startSSLServer(ip)
|
||||
|
|
|
@ -64,8 +64,6 @@ func letsEncryptCert() (*tls.Config, error) {
|
|||
}
|
||||
|
||||
func startLetsEncryptServer(ip string) {
|
||||
log.Infoln("Starting SSL with LetsEncrypt")
|
||||
|
||||
log.Infoln("Starting LetEncrypt redirect server on port 80")
|
||||
go http.ListenAndServe(":80", http.HandlerFunc(simplecert.Redirect))
|
||||
|
||||
|
|
|
@ -13,8 +13,7 @@ import (
|
|||
var testCheckin = &Checkin{
|
||||
ServiceId: 1,
|
||||
Name: "Test Checkin",
|
||||
Interval: 60,
|
||||
GracePeriod: 10,
|
||||
Interval: 3,
|
||||
ApiKey: "tHiSiSaTeStXXX",
|
||||
CreatedAt: utils.Now(),
|
||||
UpdatedAt: utils.Now(),
|
||||
|
|
|
@ -15,6 +15,11 @@ func SetDB(database database.Database) {
|
|||
}
|
||||
|
||||
func (c *Checkin) AfterFind() {
|
||||
c.AllHits = c.Hits()
|
||||
c.AllFailures = c.Failures().LastAmount(64)
|
||||
if last := c.LastHit(); last != nil {
|
||||
c.LastHitTime = last.CreatedAt
|
||||
}
|
||||
metrics.Query("checkin", "find")
|
||||
}
|
||||
|
||||
|
@ -41,9 +46,6 @@ func (c *Checkin) Create() error {
|
|||
c.ApiKey = utils.RandomString(32)
|
||||
}
|
||||
q := db.Create(c)
|
||||
|
||||
c.Start()
|
||||
go c.checkinRoutine()
|
||||
return q.Error()
|
||||
}
|
||||
|
||||
|
|
|
@ -2,13 +2,13 @@ package checkins
|
|||
|
||||
func (c *Checkin) LastHit() *CheckinHit {
|
||||
var hit CheckinHit
|
||||
dbHits.Where("checkin = ?", c.Id).Limit(1).Find(&hit)
|
||||
dbHits.Where("checkin = ?", c.Id).Last(&hit)
|
||||
return &hit
|
||||
}
|
||||
|
||||
func (c *Checkin) Hits() []*CheckinHit {
|
||||
var hits []*CheckinHit
|
||||
dbHits.Where("checkin = ?", c.Id).Find(&hits)
|
||||
dbHits.Where("checkin = ?", c.Id).Order("DESC").Find(&hits)
|
||||
c.AllHits = hits
|
||||
return hits
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import (
|
|||
|
||||
func (c *Checkin) CreateFailure(f *failures.Failure) error {
|
||||
f.Checkin = c.Id
|
||||
c.Failing = true
|
||||
return failures.DB().Create(f).Error()
|
||||
}
|
||||
|
||||
|
|
|
@ -10,22 +10,11 @@ func (c *Checkin) Expected() time.Duration {
|
|||
last := c.LastHit()
|
||||
now := utils.Now()
|
||||
lastDir := now.Sub(last.CreatedAt)
|
||||
sub := time.Duration(c.Period() - lastDir)
|
||||
return sub
|
||||
return c.Period() - lastDir
|
||||
}
|
||||
|
||||
func (c *Checkin) Period() time.Duration {
|
||||
duration, _ := time.ParseDuration(fmt.Sprintf("%ds", c.Interval))
|
||||
if duration.Seconds() <= 15 {
|
||||
return 15 * time.Second
|
||||
}
|
||||
return duration
|
||||
}
|
||||
|
||||
// Grace will return the duration of the Checkin Grace Period (after service hasn't responded, wait a bit for a response)
|
||||
func (c *Checkin) Grace() time.Duration {
|
||||
duration, _ := time.ParseDuration(fmt.Sprintf("%vs", c.GracePeriod))
|
||||
return duration
|
||||
return time.Duration(c.Interval) * time.Minute
|
||||
}
|
||||
|
||||
// Start will create a channel for the checkin checking go routine
|
||||
|
|
|
@ -25,11 +25,8 @@ func (c *Checkin) RecheckCheckinFailure(guard chan struct{}) {
|
|||
|
||||
// checkinRoutine for checking if the last Checkin was within its interval
|
||||
func (c *Checkin) checkinRoutine() {
|
||||
lastHit := c.LastHit()
|
||||
if lastHit == nil {
|
||||
return
|
||||
}
|
||||
reCheck := c.Period()
|
||||
|
||||
CheckinLoop:
|
||||
for {
|
||||
select {
|
||||
|
@ -38,20 +35,25 @@ CheckinLoop:
|
|||
c.Failing = false
|
||||
break CheckinLoop
|
||||
case <-time.After(reCheck):
|
||||
log.Infoln(fmt.Sprintf("Checkin '%s' expects a request every %v", c.Name, utils.FormatDuration(c.Period())))
|
||||
if c.Expected() <= 0 {
|
||||
issue := fmt.Sprintf("Checkin '%s' is failing, no request since %v", c.Name, lastHit.CreatedAt)
|
||||
//log.Errorln(issue)
|
||||
lastHit := c.LastHit()
|
||||
ago := utils.Now().Sub(lastHit.CreatedAt)
|
||||
|
||||
log.Infoln(fmt.Sprintf("Checkin '%s' expects a request every %s last request was %s ago", c.Name, c.Period(), utils.DurationReadable(ago)))
|
||||
|
||||
if ago.Seconds() > c.Period().Seconds() {
|
||||
issue := fmt.Sprintf("Checkin expects a request every %d seconds", c.Interval)
|
||||
log.Warnln(issue)
|
||||
|
||||
fail := &failures.Failure{
|
||||
Issue: issue,
|
||||
Method: "checkin",
|
||||
Service: c.ServiceId,
|
||||
Checkin: c.Id,
|
||||
PingTime: c.Expected().Milliseconds(),
|
||||
PingTime: ago.Milliseconds(),
|
||||
}
|
||||
|
||||
c.CreateFailure(fail)
|
||||
if err := c.CreateFailure(fail); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
reCheck = c.Period()
|
||||
}
|
||||
|
|
|
@ -8,22 +8,20 @@ import (
|
|||
func Samples() error {
|
||||
log.Infoln("Inserting Sample Checkins...")
|
||||
checkin1 := &Checkin{
|
||||
Name: "Demo Checkin 1",
|
||||
ServiceId: 1,
|
||||
Interval: 300,
|
||||
GracePeriod: 300,
|
||||
ApiKey: "demoCheckin123",
|
||||
Name: "Demo Checkin 1",
|
||||
ServiceId: 1,
|
||||
Interval: 3,
|
||||
ApiKey: "demoCheckin123",
|
||||
}
|
||||
if err := checkin1.Create(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
checkin2 := &Checkin{
|
||||
Name: "Example Checkin 2",
|
||||
ServiceId: 2,
|
||||
Interval: 900,
|
||||
GracePeriod: 300,
|
||||
ApiKey: utils.RandomString(7),
|
||||
Name: "Example Checkin 2",
|
||||
ServiceId: 2,
|
||||
Interval: 1,
|
||||
ApiKey: utils.RandomString(7),
|
||||
}
|
||||
if err := checkin2.Create(); err != nil {
|
||||
return err
|
||||
|
|
|
@ -11,7 +11,6 @@ type Checkin struct {
|
|||
ServiceId int64 `gorm:"index;column:service" json:"service_id"`
|
||||
Name string `gorm:"column:name" json:"name"`
|
||||
Interval int64 `gorm:"column:check_interval" json:"interval"`
|
||||
GracePeriod int64 `gorm:"column:grace_period" json:"grace"`
|
||||
ApiKey string `gorm:"column:api_key" json:"api_key"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
|
|
|
@ -57,29 +57,43 @@ func LoadConfigs(cfgFile string) (*DbConfig, error) {
|
|||
p.Set("API_SECRET", db.ApiSecret)
|
||||
}
|
||||
if db.Language != "" {
|
||||
p.Set("LANGUAGE", "en")
|
||||
p.Set("LANGUAGE", db.Language)
|
||||
}
|
||||
if db.SendReports {
|
||||
p.Set("ALLOW_REPORTS", true)
|
||||
}
|
||||
if db.LetsEncryptEmail != "" {
|
||||
p.Set("LETSENCRYPT_EMAIL", db.LetsEncryptEmail)
|
||||
}
|
||||
if db.LetsEncryptHost != "" {
|
||||
p.Set("LETSENCRYPT_HOST", db.LetsEncryptHost)
|
||||
}
|
||||
if db.LetsEncryptEnable {
|
||||
p.Set("LETSENCRYPT_ENABLE", db.LetsEncryptEnable)
|
||||
}
|
||||
|
||||
configs := &DbConfig{
|
||||
DbConn: p.GetString("DB_CONN"),
|
||||
DbHost: p.GetString("DB_HOST"),
|
||||
DbUser: p.GetString("DB_USER"),
|
||||
DbPass: p.GetString("DB_PASS"),
|
||||
DbData: p.GetString("DB_DATABASE"),
|
||||
DbPort: p.GetInt("DB_PORT"),
|
||||
Project: p.GetString("NAME"),
|
||||
Description: p.GetString("DESCRIPTION"),
|
||||
Domain: p.GetString("DOMAIN"),
|
||||
Email: p.GetString("EMAIL"),
|
||||
Username: p.GetString("ADMIN_USER"),
|
||||
Password: p.GetString("ADMIN_PASSWORD"),
|
||||
Location: utils.Directory,
|
||||
SqlFile: p.GetString("SQL_FILE"),
|
||||
Language: p.GetString("LANGUAGE"),
|
||||
SendReports: p.GetBool("ALLOW_REPORTS"),
|
||||
DbConn: p.GetString("DB_CONN"),
|
||||
DbHost: p.GetString("DB_HOST"),
|
||||
DbUser: p.GetString("DB_USER"),
|
||||
DbPass: p.GetString("DB_PASS"),
|
||||
DbData: p.GetString("DB_DATABASE"),
|
||||
DbPort: p.GetInt("DB_PORT"),
|
||||
Project: p.GetString("NAME"),
|
||||
Description: p.GetString("DESCRIPTION"),
|
||||
Domain: p.GetString("DOMAIN"),
|
||||
Email: p.GetString("EMAIL"),
|
||||
Username: p.GetString("ADMIN_USER"),
|
||||
Password: p.GetString("ADMIN_PASSWORD"),
|
||||
Location: utils.Directory,
|
||||
SqlFile: p.GetString("SQL_FILE"),
|
||||
Language: p.GetString("LANGUAGE"),
|
||||
SendReports: p.GetBool("ALLOW_REPORTS"),
|
||||
LetsEncryptEnable: p.GetBool("LETSENCRYPT_ENABLE"),
|
||||
}
|
||||
if configs.LetsEncryptEnable {
|
||||
configs.LetsEncryptHost = p.GetString("LETSENCRYPT_HOST")
|
||||
configs.LetsEncryptEmail = p.GetString("LETSENCRYPT_EMAIL")
|
||||
}
|
||||
log.WithFields(utils.ToFields(configs)).Debugln("read config file: " + cfgFile)
|
||||
|
||||
|
|
|
@ -6,26 +6,29 @@ const SqliteFilename = "statping.db"
|
|||
|
||||
// DbConfig struct is used for the Db connection and creates the 'config.yml' file
|
||||
type DbConfig struct {
|
||||
DbConn string `yaml:"connection" json:"connection"`
|
||||
DbHost string `yaml:"host" json:"-"`
|
||||
DbUser string `yaml:"user" json:"-"`
|
||||
DbPass string `yaml:"password" json:"-"`
|
||||
DbData string `yaml:"database" json:"-"`
|
||||
DbPort int `yaml:"port" json:"-"`
|
||||
ApiSecret string `yaml:"api_secret" json:"-"`
|
||||
Language string `yaml:"language" json:"language"`
|
||||
SendReports bool `yaml:"send_reports" json:"send_reports"`
|
||||
Project string `yaml:"-" json:"-"`
|
||||
Description string `yaml:"-" json:"-"`
|
||||
Domain string `yaml:"-" json:"-"`
|
||||
Username string `yaml:"-" json:"-"`
|
||||
Password string `yaml:"-" json:"-"`
|
||||
Email string `yaml:"-" json:"-"`
|
||||
Error error `yaml:"-" json:"-"`
|
||||
Location string `yaml:"location" json:"-"`
|
||||
SqlFile string `yaml:"sqlfile,omitempty" json:"-"`
|
||||
LocalIP string `yaml:"-" json:"-"`
|
||||
filename string `yaml:"-" json:"-"`
|
||||
DbConn string `yaml:"connection" json:"connection"`
|
||||
DbHost string `yaml:"host" json:"-"`
|
||||
DbUser string `yaml:"user" json:"-"`
|
||||
DbPass string `yaml:"password" json:"-"`
|
||||
DbData string `yaml:"database" json:"-"`
|
||||
DbPort int `yaml:"port" json:"-"`
|
||||
ApiSecret string `yaml:"api_secret" json:"-"`
|
||||
Language string `yaml:"language" json:"language"`
|
||||
SendReports bool `yaml:"send_reports" json:"send_reports"`
|
||||
Project string `yaml:"-" json:"-"`
|
||||
Description string `yaml:"-" json:"-"`
|
||||
Domain string `yaml:"-" json:"-"`
|
||||
Username string `yaml:"-" json:"-"`
|
||||
Password string `yaml:"-" json:"-"`
|
||||
Email string `yaml:"-" json:"-"`
|
||||
Error error `yaml:"-" json:"-"`
|
||||
Location string `yaml:"location" json:"-"`
|
||||
SqlFile string `yaml:"sqlfile,omitempty" json:"-"`
|
||||
LetsEncryptHost string `yaml:"letsencrypt_host,omitempty" json:"letsencrypt_host"`
|
||||
LetsEncryptEmail string `yaml:"letsencrypt_email,omitempty" json:"letsencrypt_email"`
|
||||
LetsEncryptEnable bool `yaml:"letsencrypt_enable" json:"letsencrypt_enable"`
|
||||
LocalIP string `yaml:"-" json:"-"`
|
||||
filename string `yaml:"-" json:"-"`
|
||||
|
||||
Db database.Database `yaml:"-" json:"-"`
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ func (f Failurer) List() []*Failure {
|
|||
|
||||
func (f Failurer) LastAmount(amount int) []*Failure {
|
||||
var fail []*Failure
|
||||
f.db.Order("id asc").Limit(amount).Find(&fail)
|
||||
f.db.Order("id DESC").Limit(amount).Find(&fail)
|
||||
return fail
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,9 @@ import (
|
|||
// CheckinProcess runs the checkin routine for each checkin attached to service
|
||||
func CheckinProcess(s *Service) {
|
||||
for _, c := range s.Checkins() {
|
||||
c.Start()
|
||||
if last := c.LastHit(); last != nil {
|
||||
c.Start()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -253,10 +253,6 @@ func SelectAllServices(start bool) (map[int64]*Service, error) {
|
|||
return allServices, nil
|
||||
}
|
||||
for _, s := range all() {
|
||||
|
||||
if start {
|
||||
CheckinProcess(s)
|
||||
}
|
||||
s.Failures = s.AllFailures().LastAmount(limitedFailures)
|
||||
for _, c := range s.Checkins() {
|
||||
s.AllCheckins = append(s.AllCheckins, c)
|
||||
|
@ -264,6 +260,9 @@ func SelectAllServices(start bool) (map[int64]*Service, error) {
|
|||
// collect initial service stats
|
||||
s.UpdateStats()
|
||||
allServices[s.Id] = s
|
||||
if start {
|
||||
CheckinProcess(s)
|
||||
}
|
||||
}
|
||||
return allServices, nil
|
||||
}
|
||||
|
|
|
@ -61,11 +61,10 @@ var hit3 = &hits.Hit{
|
|||
}
|
||||
|
||||
var exmapleCheckin = &checkins.Checkin{
|
||||
ServiceId: 1,
|
||||
Name: "Example Checkin",
|
||||
Interval: 60,
|
||||
GracePeriod: 30,
|
||||
ApiKey: "wdededede",
|
||||
ServiceId: 1,
|
||||
Name: "Example Checkin",
|
||||
Interval: 3,
|
||||
ApiKey: "wdededede",
|
||||
}
|
||||
|
||||
var fail1 = &failures.Failure{
|
||||
|
|
|
@ -52,6 +52,7 @@ func InitEnvs() {
|
|||
Params.SetDefault("LETSENCRYPT_HOST", "")
|
||||
Params.SetDefault("LETSENCRYPT_EMAIL", "")
|
||||
Params.SetDefault("LETSENCRYPT_LOCAL", false)
|
||||
Params.SetDefault("LETSENCRYPT_ENABLE", false)
|
||||
Params.SetDefault("LOGS_MAX_COUNT", 5)
|
||||
Params.SetDefault("LOGS_MAX_AGE", 28)
|
||||
Params.SetDefault("LOGS_MAX_SIZE", 16)
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.90.59
|
||||
0.90.60
|
||||
|
|
Loading…
Reference in New Issue