mirror of https://github.com/statping/statping
db changes
parent
66ec613953
commit
7dc285295b
6
Makefile
6
Makefile
|
@ -45,6 +45,12 @@ stop:
|
|||
logs:
|
||||
docker logs statping --follow
|
||||
|
||||
db-up:
|
||||
docker-compose -f dev/docker-compose.db.yml up -d --remove-orphans
|
||||
|
||||
db-down:
|
||||
docker-compose -f dev/docker-compose.full.yml down --remove-orphans
|
||||
|
||||
console:
|
||||
docker exec -t -i statping /bin/sh
|
||||
|
||||
|
|
|
@ -104,17 +104,22 @@ type Database interface {
|
|||
Since(time.Time) Database
|
||||
Between(time.Time, time.Time) Database
|
||||
|
||||
SelectByTime(string) string
|
||||
SelectByTime(time.Duration) string
|
||||
MultipleSelects(args ...string) Database
|
||||
|
||||
FormatTime(t time.Time) string
|
||||
ParseTime(t string) (time.Time, error)
|
||||
DbType() string
|
||||
}
|
||||
|
||||
func DB() Database {
|
||||
return database
|
||||
}
|
||||
|
||||
func (it *Db) DbType() string {
|
||||
return it.Database.Dialect().GetName()
|
||||
}
|
||||
|
||||
func Close() error {
|
||||
if database == nil {
|
||||
return nil
|
||||
|
@ -145,11 +150,21 @@ func Available() bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("service = ?", 1000)
|
||||
}
|
||||
|
||||
func (it *Db) MultipleSelects(args ...string) Database {
|
||||
joined := strings.Join(args, ", ")
|
||||
return it.Select(joined)
|
||||
}
|
||||
|
||||
func OrderStatus(status []string) func(db *gorm.DB) *gorm.DB {
|
||||
return func(db *gorm.DB) *gorm.DB {
|
||||
return db.Scopes(AmountGreaterThan1000).Where("status IN (?)", status)
|
||||
}
|
||||
}
|
||||
|
||||
type Db struct {
|
||||
Database *gorm.DB
|
||||
Type string
|
||||
|
|
|
@ -28,7 +28,7 @@ func (b By) String() string {
|
|||
type GroupQuery struct {
|
||||
Start time.Time
|
||||
End time.Time
|
||||
Group string
|
||||
Group time.Duration
|
||||
Order string
|
||||
Limit int
|
||||
Offset int
|
||||
|
@ -47,8 +47,15 @@ func (b GroupQuery) Database() Database {
|
|||
|
||||
var (
|
||||
ByCount = By("COUNT(id) as amount")
|
||||
ByAverage = func(column string) By {
|
||||
return By(fmt.Sprintf("AVG(%s) as amount", column))
|
||||
ByAverage = func(column string, multiplier int) By {
|
||||
switch database.DbType() {
|
||||
case "mysql":
|
||||
return By(fmt.Sprintf("CAST(AVG(%s)*%d as UNSIGNED) as amount", column, multiplier))
|
||||
case "postgres":
|
||||
return By(fmt.Sprintf("cast(AVG(%s)*%d as int) as amount", column, multiplier))
|
||||
default:
|
||||
return By(fmt.Sprintf("cast(AVG(%s)*%d as int) as amount", column, multiplier))
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -76,7 +83,7 @@ func (g *GroupQuery) toFloatRows() []*TimeValue {
|
|||
|
||||
fmt.Println("float rows: ", timeframe, amount)
|
||||
|
||||
newTs := types.FixedTime(timeframe, g.duration())
|
||||
newTs := types.FixedTime(timeframe, g.Group)
|
||||
data = append(data, &TimeValue{
|
||||
Timeframe: newTs,
|
||||
Amount: amount,
|
||||
|
@ -91,7 +98,7 @@ func (g *GroupQuery) GraphData(by By) ([]*TimeValue, error) {
|
|||
dbQuery := g.db.MultipleSelects(
|
||||
g.db.SelectByTime(g.Group),
|
||||
by.String(),
|
||||
).Group("timeframe")
|
||||
).Group("timeframe").Debug()
|
||||
|
||||
g.db = dbQuery
|
||||
|
||||
|
@ -119,7 +126,7 @@ func (g *GroupQuery) ToTimeValue() (*TimeVar, error) {
|
|||
log.Error(err, timeframe)
|
||||
}
|
||||
trueTime, _ := g.db.ParseTime(timeframe)
|
||||
newTs := types.FixedTime(trueTime, g.duration())
|
||||
newTs := types.FixedTime(trueTime, g.Group)
|
||||
data = append(data, &TimeValue{
|
||||
Timeframe: newTs,
|
||||
Amount: amount,
|
||||
|
@ -131,12 +138,12 @@ func (g *GroupQuery) ToTimeValue() (*TimeVar, error) {
|
|||
func (t *TimeVar) FillMissing(current, end time.Time) ([]*TimeValue, error) {
|
||||
timeMap := make(map[string]float64)
|
||||
var validSet []*TimeValue
|
||||
dur := t.g.duration()
|
||||
dur := t.g.Group
|
||||
for _, v := range t.data {
|
||||
timeMap[v.Timeframe] = v.Amount
|
||||
}
|
||||
|
||||
currentStr := types.FixedTime(current, t.g.duration())
|
||||
currentStr := types.FixedTime(current, t.g.Group)
|
||||
|
||||
for {
|
||||
var amount float64
|
||||
|
@ -151,31 +158,12 @@ func (t *TimeVar) FillMissing(current, end time.Time) ([]*TimeValue, error) {
|
|||
break
|
||||
}
|
||||
current = current.Add(dur)
|
||||
currentStr = types.FixedTime(current, t.g.duration())
|
||||
currentStr = types.FixedTime(current, t.g.Group)
|
||||
}
|
||||
|
||||
return validSet, nil
|
||||
}
|
||||
|
||||
func (g *GroupQuery) duration() time.Duration {
|
||||
switch g.Group {
|
||||
case "second":
|
||||
return types.Second
|
||||
case "minute":
|
||||
return types.Minute
|
||||
case "hour":
|
||||
return types.Hour
|
||||
case "day":
|
||||
return types.Day
|
||||
case "month":
|
||||
return types.Month
|
||||
case "year":
|
||||
return types.Year
|
||||
default:
|
||||
return types.Hour
|
||||
}
|
||||
}
|
||||
|
||||
type isObject interface {
|
||||
Db() Database
|
||||
}
|
||||
|
@ -183,9 +171,6 @@ type isObject interface {
|
|||
func ParseQueries(r *http.Request, o isObject) *GroupQuery {
|
||||
fields := parseGet(r)
|
||||
grouping := fields.Get("group")
|
||||
if grouping == "" {
|
||||
grouping = "hour"
|
||||
}
|
||||
startField := utils.ToInt(fields.Get("start"))
|
||||
endField := utils.ToInt(fields.Get("end"))
|
||||
limit := utils.ToInt(fields.Get("limit"))
|
||||
|
@ -198,10 +183,19 @@ func ParseQueries(r *http.Request, o isObject) *GroupQuery {
|
|||
|
||||
db := o.Db()
|
||||
|
||||
if grouping == "" {
|
||||
grouping = "1h"
|
||||
}
|
||||
groupDur, err := time.ParseDuration(grouping)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
groupDur = 1 * time.Hour
|
||||
}
|
||||
|
||||
query := &GroupQuery{
|
||||
Start: time.Unix(startField, 0).UTC(),
|
||||
End: time.Unix(endField, 0).UTC(),
|
||||
Group: grouping,
|
||||
Group: groupDur,
|
||||
Order: orderBy,
|
||||
Limit: int(limit),
|
||||
Offset: int(offset),
|
||||
|
@ -210,11 +204,14 @@ func ParseQueries(r *http.Request, o isObject) *GroupQuery {
|
|||
}
|
||||
|
||||
if startField == 0 {
|
||||
query.Start = time.Now().Add(-3 * types.Month).UTC()
|
||||
query.Start = time.Now().Add(-7 * types.Day).UTC()
|
||||
}
|
||||
if endField == 0 {
|
||||
query.End = time.Now().UTC()
|
||||
}
|
||||
if query.End.After(utils.Now()) {
|
||||
query.End = utils.Now()
|
||||
}
|
||||
|
||||
if query.Limit != 0 {
|
||||
db = db.Limit(query.Limit)
|
||||
|
|
|
@ -30,14 +30,15 @@ func (it *Db) FormatTime(t time.Time) string {
|
|||
}
|
||||
}
|
||||
|
||||
func (it *Db) SelectByTime(increment string) string {
|
||||
func (it *Db) SelectByTime(increment time.Duration) string {
|
||||
seconds := int(increment.Seconds())
|
||||
switch it.Type {
|
||||
case "mysql":
|
||||
return fmt.Sprintf("CONCAT(date_format(created_at, '%s')) AS timeframe", it.correctTimestamp(increment))
|
||||
return fmt.Sprintf("FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(created_at) / %d) * %d) AS timeframe", seconds, seconds)
|
||||
case "postgres":
|
||||
return fmt.Sprintf("date_trunc('%s', created_at) AS timeframe", increment)
|
||||
default:
|
||||
return fmt.Sprintf("strftime('%s', created_at, 'utc') as timeframe", it.correctTimestamp(increment))
|
||||
return fmt.Sprintf("datetime((strftime('%%s', created_at) / %d) * %d, 'unixepoch') as timeframe", seconds, seconds)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ services:
|
|||
MYSQL_ROOT_PASSWORD: password123
|
||||
MYSQL_DATABASE: statping
|
||||
MYSQL_USER: root
|
||||
MYSQL_PASSWORD: password
|
||||
MYSQL_PASSWORD: password123
|
||||
ports:
|
||||
- 3306:3306
|
||||
healthcheck:
|
||||
|
|
|
@ -3,6 +3,12 @@ global:
|
|||
evaluation_interval: 15s
|
||||
|
||||
scrape_configs:
|
||||
- job_name: 'statping_local'
|
||||
scrape_interval: 15s
|
||||
bearer_token: 'samplesecret'
|
||||
static_configs:
|
||||
- targets: ['docker0:8585']
|
||||
|
||||
- job_name: 'statping'
|
||||
scrape_interval: 15s
|
||||
bearer_token: 'exampleapisecret'
|
||||
|
|
|
@ -36,12 +36,12 @@ class Api {
|
|||
return axios.post('/api/services/' + data.id, data).then(response => (response.data))
|
||||
}
|
||||
|
||||
async service_hits(id, start, end, group) {
|
||||
return axios.get('/api/services/' + id + '/hits_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=true').then(response => (response.data))
|
||||
async service_hits(id, start, end, group, fill=true) {
|
||||
return axios.get('/api/services/' + id + '/hits_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=' + fill).then(response => (response.data))
|
||||
}
|
||||
|
||||
async service_failures_data(id, start, end, group) {
|
||||
return axios.get('/api/services/' + id + '/failure_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=true').then(response => (response.data))
|
||||
async service_failures_data(id, start, end, group, fill=true) {
|
||||
return axios.get('/api/services/' + id + '/failure_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=' + fill).then(response => (response.data))
|
||||
}
|
||||
|
||||
async service_heatmap(id, start, end, group) {
|
||||
|
|
|
@ -539,4 +539,26 @@ input.inputTags-field:focus {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
min-height: 85pt;
|
||||
}
|
||||
|
||||
.index_container {
|
||||
min-height: 980pt;
|
||||
}
|
||||
|
||||
/* Enter and leave animations can use different */
|
||||
/* durations and timing functions. */
|
||||
.slide-fade-enter-active {
|
||||
transition: all .3s ease;
|
||||
}
|
||||
.slide-fade-leave-active {
|
||||
transition: all .8s cubic-bezier(1.0, 0.5, 0.8, 1.0);
|
||||
}
|
||||
.slide-fade-enter, .slide-fade-leave-to
|
||||
/* .slide-fade-leave-active below version 2.1.8 */ {
|
||||
transform: translateX(10px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
@import 'mobile';
|
||||
|
|
|
@ -17,7 +17,8 @@
|
|||
|
||||
.container {
|
||||
padding: 0px !important;
|
||||
padding-top: 15px !important;
|
||||
padding-top: 4vh !important;
|
||||
min-height: 960pt;
|
||||
}
|
||||
|
||||
.group_header {
|
||||
|
@ -29,6 +30,9 @@
|
|||
margin-top: 0px;
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
box-shadow: 1px 10px 20px 0px;
|
||||
height: 55pt;
|
||||
color: rgba(0, 0, 0, 0.20);
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="col-12">
|
||||
<h1 class="text-black-50">Messages</h1>
|
||||
<h3 class="text-black-50">Messages</h3>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="col-12">
|
||||
<h1 class="text-black-50">Services
|
||||
<h3 class="text-black-50">Services
|
||||
<router-link to="/dashboard/create_service" class="btn btn-outline-success mt-1 float-right">
|
||||
<font-awesome-icon icon="plus"/> Create
|
||||
</router-link>
|
||||
</h1>
|
||||
</h3>
|
||||
|
||||
<ServicesList/>
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<div class="col-12">
|
||||
<h1 class="text-black-50">Users</h1>
|
||||
<h3 class="text-black-50">Users</h3>
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Username</th>
|
||||
<th scope="col">Type</th>
|
||||
<th scope="col">Last Login</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">Last Login</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -16,7 +16,7 @@
|
|||
<td>{{user.username}}</td>
|
||||
<td v-if="user.admin"><span class="badge badge-danger">ADMIN</span></td>
|
||||
<td v-if="!user.admin"><span class="badge badge-primary">USER</span></td>
|
||||
<td>{{toLocal(user.updated_at)}}</td>
|
||||
<td class="d-none d-md-table-cell">{{toLocal(user.updated_at)}}</td>
|
||||
<td class="text-right">
|
||||
<div class="btn-group">
|
||||
<a @click.prevent="editUser(user, edit)" href="" class="btn btn-outline-secondary"><font-awesome-icon icon="user" /> Edit</a>
|
||||
|
|
|
@ -35,7 +35,7 @@ export default {
|
|||
methods: {
|
||||
async lastDaysFailures() {
|
||||
const start = this.nowSubtract(86400 * 30)
|
||||
this.failureData = await Api.service_failures_data(this.service.id, this.toUnix(start), this.toUnix(this.now()), "day")
|
||||
this.failureData = await Api.service_failures_data(this.service.id, this.toUnix(start), this.toUnix(this.now()), "24h")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -126,7 +126,7 @@
|
|||
visible: function(newVal, oldVal) {
|
||||
if (newVal && !this.showing) {
|
||||
this.showing = true
|
||||
this.chartHits("hour")
|
||||
this.chartHits("1h")
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -135,8 +135,8 @@
|
|||
const start = this.nowSubtract((3600 * 24) * 30)
|
||||
this.data = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(new Date()), group)
|
||||
|
||||
if (this.data.length === 0 && group !== "hour") {
|
||||
await this.chartHits("hour")
|
||||
if (this.data.length === 0 && group !== "1h") {
|
||||
await this.chartHits("30m")
|
||||
}
|
||||
this.series = [{
|
||||
name: this.service.name,
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
methods: {
|
||||
async chartHeatmap() {
|
||||
const start = this.nowSubtract((3600 * 24) * 7)
|
||||
const data = await Api.service_heatmap(this.service.id, this.toUnix(start), this.toUnix(new Date()), "hour")
|
||||
const data = await Api.service_heatmap(this.service.id, this.toUnix(start), this.toUnix(new Date()), "1h")
|
||||
|
||||
let dataArr = []
|
||||
data.forEach(function(d) {
|
||||
|
|
|
@ -49,9 +49,9 @@
|
|||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.set1 = await this.getHits(24, "hour")
|
||||
this.set1 = await this.getHits(24, "1h")
|
||||
this.set1_name = this.calc(this.set1)
|
||||
this.set2 = await this.getHits(24 * 7, "day")
|
||||
this.set2 = await this.getHits(24 * 7, "24h")
|
||||
this.set2_name = this.calc(this.set2)
|
||||
this.loaded = true
|
||||
},
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<template>
|
||||
<div class="col-12">
|
||||
<h1 class="text-black-50 mt-5">
|
||||
<h3 class="text-black-50 mt-5">
|
||||
{{message.id ? `Update ${message.title}` : "Create Message"}}
|
||||
<button @click="removeEdit" v-if="message.id" class="mt-3 btn float-right btn-danger btn-sm">Close</button>
|
||||
</h1>
|
||||
</h3>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<form @submit="saveMessage">
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="text-black-50 mt-5">
|
||||
<h3 class="text-black-50 mt-5">
|
||||
{{user.id ? `Update ${user.username}` : "Create User"}}
|
||||
|
||||
<button @click.prevent="removeEdit" v-if="user.id" class="mt-3 btn float-right btn-danger btn-sm">Close</button></h1>
|
||||
<button @click.prevent="removeEdit" v-if="user.id" class="mt-3 btn float-right btn-danger btn-sm">Close</button></h3>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="container col-md-7 col-sm-12 mt-4 sm-container">
|
||||
<div class="container col-md-7 col-sm-12 mt-4 sm-container index_container">
|
||||
|
||||
<Header/>
|
||||
|
||||
|
|
|
@ -250,7 +250,7 @@ export default {
|
|||
const start = this.nowSubtract((3600 * 24) * 7)
|
||||
this.start_time = start
|
||||
this.end_time = new Date()
|
||||
this.data = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(new Date()), "hour")
|
||||
this.data = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(new Date()), "1h")
|
||||
this.series = [{
|
||||
name: this.service.name,
|
||||
...this.data
|
||||
|
|
|
@ -187,7 +187,7 @@ func IsAdmin(r *http.Request) bool {
|
|||
if !core.App.Setup {
|
||||
return false
|
||||
}
|
||||
if os.Getenv("GO_ENV") == "test" {
|
||||
if utils.Getenv("GO_ENV", false).(bool) {
|
||||
return true
|
||||
}
|
||||
claim, err := getJwtToken(r)
|
||||
|
|
|
@ -128,7 +128,7 @@ func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
groupQuery := database.ParseQueries(r, service.AllHits())
|
||||
|
||||
objs, err := groupQuery.GraphData(database.ByAverage("latency"))
|
||||
objs, err := groupQuery.GraphData(database.ByAverage("latency", 1000))
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
|
@ -164,7 +164,7 @@ func apiServicePingDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
groupQuery := database.ParseQueries(r, service.AllHits())
|
||||
|
||||
objs, err := groupQuery.GraphData(database.ByAverage("ping_time"))
|
||||
objs, err := groupQuery.GraphData(database.ByAverage("ping_time", 1000))
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
|
|
|
@ -90,6 +90,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -104,7 +104,7 @@ func InitialSetup(configs *DbConfig) error {
|
|||
|
||||
admin := &users.User{
|
||||
Username: username,
|
||||
Password: utils.HashPassword(password),
|
||||
Password: password,
|
||||
Email: "info@admin.com",
|
||||
Admin: null.NewNullBool(true),
|
||||
}
|
||||
|
|
|
@ -14,6 +14,10 @@ import (
|
|||
"github.com/hunterlong/statping/types/notifications"
|
||||
"github.com/hunterlong/statping/types/services"
|
||||
"github.com/hunterlong/statping/types/users"
|
||||
|
||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
)
|
||||
|
||||
// InsertNotifierDB inject the Statping database instance to the Notifier package
|
||||
|
|
|
@ -6,6 +6,10 @@ import (
|
|||
"github.com/prometheus/common/log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
)
|
||||
|
||||
func Samples() {
|
||||
|
|
|
@ -18,6 +18,18 @@ func (h Hitters) Db() database.Database {
|
|||
return h.db
|
||||
}
|
||||
|
||||
func (h Hitters) First() *Hit {
|
||||
var hit Hit
|
||||
h.db.Order("id ASC").Limit(1).Find(&hit)
|
||||
return &hit
|
||||
}
|
||||
|
||||
func (h Hitters) Last() *Hit {
|
||||
var hit Hit
|
||||
h.db.Order("id DESC").Limit(1).Find(&hit)
|
||||
return &hit
|
||||
}
|
||||
|
||||
func (h Hitters) Since(t time.Time) []*Hit {
|
||||
var hits []*Hit
|
||||
h.db.Since(t).Find(&hits)
|
||||
|
@ -30,9 +42,9 @@ func (h Hitters) List() []*Hit {
|
|||
return hits
|
||||
}
|
||||
|
||||
func (h Hitters) Last(amount int) []*Hit {
|
||||
func (h Hitters) LastCount(amounts int) []*Hit {
|
||||
var hits []*Hit
|
||||
h.db.Limit(amount).Find(&hits)
|
||||
h.db.Order("id asc").Limit(amounts).Find(&hits)
|
||||
return hits
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
package hits
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
)
|
||||
|
||||
var SampleHits = 9900.
|
||||
|
@ -13,29 +19,38 @@ func Samples() {
|
|||
tx := DB().Begin()
|
||||
sg := new(sync.WaitGroup)
|
||||
|
||||
createdAt := time.Now().Add(-1 * types.Month)
|
||||
for i := int64(1); i <= 4; i++ {
|
||||
sg.Add(1)
|
||||
|
||||
p := utils.NewPerlin(2, 2, 5, time.Now().UnixNano())
|
||||
|
||||
go func() {
|
||||
defer sg.Done()
|
||||
for hi := 0.; hi <= SampleHits; hi++ {
|
||||
latency := p.Noise1D(hi / 500)
|
||||
createdAt = createdAt.Add(1 * time.Minute)
|
||||
hit := &Hit{
|
||||
Service: i,
|
||||
CreatedAt: createdAt.UTC(),
|
||||
Latency: latency,
|
||||
}
|
||||
tx = tx.Create(&hit)
|
||||
}
|
||||
}()
|
||||
}
|
||||
sg.Wait()
|
||||
|
||||
if err := tx.Commit().Error(); err != nil {
|
||||
err := createHitsAt(tx, i, sg)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
}
|
||||
tx = DB().Begin()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func createHitsAt(db database.Database, serviceID int64, sg *sync.WaitGroup) error {
|
||||
createdAt := utils.Now().Add(-30 * types.Day)
|
||||
p := utils.NewPerlin(2, 2, 5, utils.Now().UnixNano())
|
||||
|
||||
i := 0
|
||||
for hi := 0.; hi <= SampleHits; hi++ {
|
||||
latency := p.Noise1D(hi / 500)
|
||||
|
||||
createdAt = createdAt.Add(10 * time.Minute)
|
||||
|
||||
hit := &Hit{
|
||||
Service: serviceID,
|
||||
Latency: latency,
|
||||
PingTime: latency * 0.15,
|
||||
CreatedAt: createdAt,
|
||||
}
|
||||
|
||||
db = db.Create(&hit)
|
||||
fmt.Printf("Creating hit %d hit %d: %.2f %v\n", serviceID, hit.Id, latency, createdAt.String())
|
||||
i++
|
||||
}
|
||||
|
||||
return db.Commit().Error()
|
||||
|
||||
}
|
||||
|
|
|
@ -10,16 +10,16 @@ func (i *Incident) Updates() []*IncidentUpdate {
|
|||
}
|
||||
|
||||
func (i *IncidentUpdate) Create() error {
|
||||
db := database.DB().Create(&i)
|
||||
db := database.DB().Create(i)
|
||||
return db.Error()
|
||||
}
|
||||
|
||||
func (i *IncidentUpdate) Update() error {
|
||||
db := database.DB().Update(&i)
|
||||
db := database.DB().Update(i)
|
||||
return db.Error()
|
||||
}
|
||||
|
||||
func (i *IncidentUpdate) Delete() error {
|
||||
db := database.DB().Delete(&i)
|
||||
db := database.DB().Delete(i)
|
||||
return db.Error()
|
||||
}
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
package types
|
|
@ -19,16 +19,16 @@ func All() []*Message {
|
|||
}
|
||||
|
||||
func (m *Message) Create() error {
|
||||
db := DB().Create(&m)
|
||||
db := DB().Create(m)
|
||||
return db.Error()
|
||||
}
|
||||
|
||||
func (m *Message) Update() error {
|
||||
db := DB().Update(&m)
|
||||
db := DB().Update(m)
|
||||
return db.Error()
|
||||
}
|
||||
|
||||
func (m *Message) Delete() error {
|
||||
db := DB().Delete(&m)
|
||||
db := DB().Delete(m)
|
||||
return db.Error()
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package services
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/types/failures"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -16,10 +15,10 @@ func (s *Service) AllFailures() failures.Failurer {
|
|||
return failures.AllFailures(s)
|
||||
}
|
||||
|
||||
func (s *Service) LastFailures(amount int) []*failures.Failure {
|
||||
var fail []*failures.Failure
|
||||
database.DB().Limit(amount).Find(&fail)
|
||||
return fail
|
||||
func (s *Service) LastFailure() *failures.Failure {
|
||||
var fail failures.Failure
|
||||
failures.DB().Where("service = ?", s.Id).Order("id desc").Limit(1).Find(&fail)
|
||||
return &fail
|
||||
}
|
||||
|
||||
func (s *Service) FailuresCount() int {
|
||||
|
@ -35,11 +34,11 @@ func (s *Service) FailuresSince(t time.Time) []*failures.Failure {
|
|||
}
|
||||
|
||||
func (s *Service) DowntimeText() string {
|
||||
last := s.LastFailures(1)
|
||||
if len(last) == 0 {
|
||||
last := s.LastFailure()
|
||||
if last == nil {
|
||||
return ""
|
||||
}
|
||||
return parseError(last[0])
|
||||
return parseError(last)
|
||||
}
|
||||
|
||||
// ParseError returns a human readable error for a Failure
|
||||
|
|
|
@ -9,6 +9,14 @@ func (s *Service) HitsColumnID() (string, int64) {
|
|||
return "service", s.Id
|
||||
}
|
||||
|
||||
func (s *Service) FirstHit() *hits.Hit {
|
||||
return hits.AllHits(s).First()
|
||||
}
|
||||
|
||||
func (s *Service) LastHit() *hits.Hit {
|
||||
return hits.AllHits(s).Last()
|
||||
}
|
||||
|
||||
func (s *Service) AllHits() hits.Hitters {
|
||||
return hits.AllHits(s)
|
||||
}
|
||||
|
|
|
@ -186,14 +186,14 @@ func (s *Service) OnlineSince(ago time.Time) float32 {
|
|||
|
||||
// Downtime returns the amount of time of a offline service
|
||||
func (s *Service) Downtime() time.Duration {
|
||||
hits := s.AllHits().Last(1)
|
||||
fail := s.AllFailures().Last(1)
|
||||
if len(fail) == 0 {
|
||||
hit := s.LastHit()
|
||||
fail := s.LastFailure()
|
||||
if hit == nil {
|
||||
return time.Duration(0)
|
||||
}
|
||||
if len(fail) == 0 {
|
||||
return time.Now().UTC().Sub(fail[0].CreatedAt.UTC())
|
||||
if fail == nil {
|
||||
return utils.Now().Sub(fail.CreatedAt)
|
||||
}
|
||||
since := fail[0].CreatedAt.UTC().Sub(hits[0].CreatedAt.UTC())
|
||||
return since
|
||||
|
||||
return fail.CreatedAt.Sub(hit.CreatedAt)
|
||||
}
|
||||
|
|
|
@ -15,11 +15,6 @@ func AuthUser(username, password string) (*User, bool) {
|
|||
log.Warnln(fmt.Errorf("user %v not found", username))
|
||||
return nil, false
|
||||
}
|
||||
|
||||
fmt.Println(username, password)
|
||||
|
||||
fmt.Println(username, user.Password)
|
||||
|
||||
if CheckHash(password, user.Password) {
|
||||
user.UpdatedAt = time.Now().UTC()
|
||||
user.Update()
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"errors"
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"github.com/prometheus/common/log"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -13,14 +14,14 @@ func DB() database.Database {
|
|||
|
||||
func Find(id int64) (*User, error) {
|
||||
var user User
|
||||
db := DB().Where("id = ?", id).Find(user)
|
||||
db := DB().Where("id = ?", id).Find(&user)
|
||||
return &user, db.Error()
|
||||
}
|
||||
|
||||
func FindByUsername(username string) (*User, error) {
|
||||
var user *User
|
||||
db := DB().Where("username = ?", username).Find(user)
|
||||
return user, db.Error()
|
||||
var user User
|
||||
db := DB().Where("username = ?", username).Find(&user)
|
||||
return &user, db.Error()
|
||||
}
|
||||
|
||||
func All() []*User {
|
||||
|
@ -35,21 +36,27 @@ func (u *User) Create() error {
|
|||
return errors.New("did not supply user password")
|
||||
}
|
||||
u.Password = utils.HashPassword(u.Password)
|
||||
u.ApiKey = utils.NewSHA1Hash(5)
|
||||
u.ApiSecret = utils.NewSHA1Hash(10)
|
||||
u.ApiKey = utils.NewSHA1Hash(16)
|
||||
u.ApiSecret = utils.NewSHA1Hash(16)
|
||||
|
||||
db := DB().Create(u)
|
||||
if db.Error() == nil {
|
||||
log.Warnf("User #%d (%s) has been created", u.Id, u.Username)
|
||||
}
|
||||
return db.Error()
|
||||
}
|
||||
|
||||
func (u *User) Update() error {
|
||||
u.ApiKey = utils.NewSHA1Hash(5)
|
||||
u.ApiSecret = utils.NewSHA1Hash(10)
|
||||
db := DB().Update(&u)
|
||||
//u.ApiKey = utils.NewSHA1Hash(5)
|
||||
//u.ApiSecret = utils.NewSHA1Hash(10)
|
||||
db := DB().Update(u)
|
||||
return db.Error()
|
||||
}
|
||||
|
||||
func (u *User) Delete() error {
|
||||
db := DB().Delete(u)
|
||||
if db.Error() == nil {
|
||||
log.Warnf("User #%d (%s) has been deleted")
|
||||
}
|
||||
return db.Error()
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue