mirror of https://github.com/statping/statping
pull/429/head
parent
f64fc9e682
commit
7cf239125f
|
@ -17,6 +17,7 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/hunterlong/statping/database"
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/hunterlong/statping/utils"
|
"github.com/hunterlong/statping/utils"
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
@ -56,14 +57,14 @@ func SelectUsername(username string) (*User, error) {
|
||||||
|
|
||||||
// Delete will remove the User record from the database
|
// Delete will remove the User record from the database
|
||||||
func (u *User) Delete() error {
|
func (u *User) Delete() error {
|
||||||
return Database(&User{}).Delete(u).Error()
|
return database.Delete(&u)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update will update the User's record in database
|
// Update will update the User's record in database
|
||||||
func (u *User) Update() error {
|
func (u *User) Update() error {
|
||||||
u.ApiKey = utils.NewSHA1Hash(5)
|
u.ApiKey = utils.NewSHA1Hash(5)
|
||||||
u.ApiSecret = utils.NewSHA1Hash(10)
|
u.ApiSecret = utils.NewSHA1Hash(10)
|
||||||
return Database(&User{}).Update(u).Error()
|
return database.Update(&u)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create will insert a new User into the database
|
// Create will insert a new User into the database
|
||||||
|
@ -72,15 +73,16 @@ func (u *User) Create() (int64, error) {
|
||||||
u.Password = utils.HashPassword(u.Password)
|
u.Password = utils.HashPassword(u.Password)
|
||||||
u.ApiKey = utils.NewSHA1Hash(5)
|
u.ApiKey = utils.NewSHA1Hash(5)
|
||||||
u.ApiSecret = utils.NewSHA1Hash(10)
|
u.ApiSecret = utils.NewSHA1Hash(10)
|
||||||
db := Database(&User{}).Create(u)
|
|
||||||
if db.Error() != nil {
|
user, err := database.Create(&u)
|
||||||
return 0, db.Error()
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
}
|
}
|
||||||
if u.Id == 0 {
|
if user.Id == 0 {
|
||||||
log.Errorln(fmt.Sprintf("Failed to create User %v. %v", u.Username, db.Error()))
|
log.Errorln(fmt.Sprintf("Failed to create User %v. %v", u.Username, err))
|
||||||
return 0, db.Error()
|
return 0, err
|
||||||
}
|
}
|
||||||
return u.Id, db.Error()
|
return u.Id, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// SelectAllUsers returns all users
|
// SelectAllUsers returns all users
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import "github.com/hunterlong/statping/types"
|
||||||
|
|
||||||
|
type CheckinObj struct {
|
||||||
|
*types.Checkin
|
||||||
|
failures
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Object) AsCheckin() HitsFailures {
|
||||||
|
return &CheckinObj{
|
||||||
|
Checkin: o.model.(*types.Checkin),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Checkin(id int64) (HitsFailures, error) {
|
||||||
|
var checkin types.Checkin
|
||||||
|
query := database.Model(&types.Checkin{}).Where("id = ?", id).Find(&checkin)
|
||||||
|
return &CheckinObj{Checkin: &checkin}, query.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CheckinObj) Hits() *hits {
|
||||||
|
return &hits{
|
||||||
|
database.Model(&types.Checkin{}).Where("checkin = ?", c.Id),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *CheckinObj) Failures() *failures {
|
||||||
|
return &failures{
|
||||||
|
database.Model(&types.Failure{}).
|
||||||
|
Where("method = 'checkin' AND service = ?", c.Id).Order("id desc"),
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Object struct {
|
||||||
|
Id int64
|
||||||
|
model interface{}
|
||||||
|
db Database
|
||||||
|
}
|
||||||
|
|
||||||
|
type HitsFailures interface {
|
||||||
|
Hits() *hits
|
||||||
|
Failures() *failures
|
||||||
|
}
|
||||||
|
|
||||||
|
func modelId(model interface{}) int64 {
|
||||||
|
iface := reflect.ValueOf(model)
|
||||||
|
field := iface.Elem().FieldByName("Id")
|
||||||
|
return field.Int()
|
||||||
|
}
|
||||||
|
|
||||||
|
func toModel(model interface{}) Database {
|
||||||
|
return database.Model(&model)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Create(data interface{}) (*Object, error) {
|
||||||
|
model := toModel(data)
|
||||||
|
query := model.Create(data)
|
||||||
|
if query.Error() != nil {
|
||||||
|
return nil, query.Error()
|
||||||
|
}
|
||||||
|
obj := &Object{
|
||||||
|
Id: modelId(data),
|
||||||
|
model: data,
|
||||||
|
db: model,
|
||||||
|
}
|
||||||
|
return obj, query.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Update(data interface{}) error {
|
||||||
|
model := toModel(data)
|
||||||
|
return model.Update(&data).Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
func Delete(data interface{}) error {
|
||||||
|
model := toModel(data)
|
||||||
|
return model.Delete(data).Error()
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hunterlong/statping/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
type failures struct {
|
||||||
|
DB Database
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *failures) All() []*types.Failure {
|
||||||
|
var fails []*types.Failure
|
||||||
|
f.DB = f.DB.Find(&fails)
|
||||||
|
return fails
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *failures) Last(amount int) *types.Failure {
|
||||||
|
var fail types.Failure
|
||||||
|
f.DB = f.DB.Limit(amount).Find(&fail)
|
||||||
|
return &fail
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *failures) Count() int {
|
||||||
|
var amount int
|
||||||
|
f.DB = f.DB.Count(&amount)
|
||||||
|
return amount
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *failures) Find(data interface{}) error {
|
||||||
|
q := f.Find(&data)
|
||||||
|
return q
|
||||||
|
}
|
|
@ -1,215 +1,26 @@
|
||||||
package database
|
package database
|
||||||
|
|
||||||
import (
|
import "github.com/hunterlong/statping/types"
|
||||||
"fmt"
|
|
||||||
"github.com/hunterlong/statping/types"
|
|
||||||
"github.com/hunterlong/statping/utils"
|
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type GroupBy struct {
|
type GroupObj struct {
|
||||||
db Database
|
*types.Group
|
||||||
query *GroupQuery
|
db Database
|
||||||
}
|
}
|
||||||
|
|
||||||
type GroupByer interface {
|
type Grouper interface {
|
||||||
ToTimeValue(interface{}) (*TimeVar, error)
|
Services() Database
|
||||||
}
|
}
|
||||||
|
|
||||||
type By string
|
func (o *Object) AsGroup() *types.Group {
|
||||||
|
return o.model.(*types.Group)
|
||||||
func (b By) String() string {
|
|
||||||
return string(b)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type GroupQuery struct {
|
func (it *Db) GetGroup(id int64) (*GroupObj, error) {
|
||||||
db Database
|
var group types.Group
|
||||||
Start time.Time
|
query := it.Model(&types.Group{}).Where("id = ?", id).Find(&group)
|
||||||
End time.Time
|
return &GroupObj{&group, it}, query.Error()
|
||||||
Group string
|
|
||||||
Order string
|
|
||||||
Limit int
|
|
||||||
Offset int
|
|
||||||
FillEmpty bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b GroupQuery) Database() Database {
|
func (it *GroupObj) Services() Database {
|
||||||
return b.db
|
return it.db.Model(&types.Service{}).Where("service = ?", it.Id)
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
ByCount = By("COUNT(id) as amount")
|
|
||||||
BySum = func(column string) By {
|
|
||||||
return By(fmt.Sprintf("SUM(%s) as amount", column))
|
|
||||||
}
|
|
||||||
ByAverage = func(column string) By {
|
|
||||||
return By(fmt.Sprintf("AVG(%s) as amount", column))
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func (db *Db) GroupQuery(q *GroupQuery, by By) GroupByer {
|
|
||||||
dbQuery := db.MultipleSelects(
|
|
||||||
db.SelectByTime(q.Group),
|
|
||||||
by.String(),
|
|
||||||
).Group("timeframe")
|
|
||||||
|
|
||||||
return &GroupBy{dbQuery, q}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TimeVar struct {
|
|
||||||
g *GroupBy
|
|
||||||
data []*TimeValue
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TimeVar) ToValues() []*TimeValue {
|
|
||||||
return t.data
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GroupBy) toFloatRows() []*TimeValue {
|
|
||||||
rows, err := g.db.Rows()
|
|
||||||
if err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
var data []*TimeValue
|
|
||||||
for rows.Next() {
|
|
||||||
var timeframe time.Time
|
|
||||||
amount := float64(0)
|
|
||||||
rows.Scan(&timeframe, &amount)
|
|
||||||
newTs := types.FixedTime(timeframe, g.duration())
|
|
||||||
data = append(data, &TimeValue{
|
|
||||||
Timeframe: newTs,
|
|
||||||
Amount: amount,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GroupBy) ToTimeValue(dbType interface{}) (*TimeVar, error) {
|
|
||||||
rows, err := g.db.Rows()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
var data []*TimeValue
|
|
||||||
for rows.Next() {
|
|
||||||
var timeframe time.Time
|
|
||||||
amount := float64(0)
|
|
||||||
rows.Scan(&timeframe, &amount)
|
|
||||||
newTs := types.FixedTime(timeframe, g.duration())
|
|
||||||
data = append(data, &TimeValue{
|
|
||||||
Timeframe: newTs,
|
|
||||||
Amount: amount,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return &TimeVar{g, data}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *TimeVar) FillMissing(current, end time.Time) []*TimeValue {
|
|
||||||
timeMap := make(map[string]float64)
|
|
||||||
var validSet []*TimeValue
|
|
||||||
dur := t.g.duration()
|
|
||||||
for _, v := range t.data {
|
|
||||||
timeMap[v.Timeframe] = v.Amount
|
|
||||||
}
|
|
||||||
|
|
||||||
currentStr := types.FixedTime(current, t.g.duration())
|
|
||||||
|
|
||||||
for {
|
|
||||||
var amount float64
|
|
||||||
if timeMap[currentStr] != 0 {
|
|
||||||
amount = timeMap[currentStr]
|
|
||||||
}
|
|
||||||
validSet = append(validSet, &TimeValue{
|
|
||||||
Timeframe: currentStr,
|
|
||||||
Amount: amount,
|
|
||||||
})
|
|
||||||
if current.After(end) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
current = current.Add(dur)
|
|
||||||
currentStr = types.FixedTime(current, t.g.duration())
|
|
||||||
}
|
|
||||||
|
|
||||||
return validSet
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GroupBy) duration() time.Duration {
|
|
||||||
switch g.query.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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseQueries(r *http.Request, db Database) *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"))
|
|
||||||
offset := utils.ToInt(fields.Get("offset"))
|
|
||||||
fill, _ := strconv.ParseBool(fields.Get("fill"))
|
|
||||||
orderBy := fields.Get("order")
|
|
||||||
if limit == 0 {
|
|
||||||
limit = 10000
|
|
||||||
}
|
|
||||||
|
|
||||||
query := &GroupQuery{
|
|
||||||
Start: time.Unix(startField, 0).UTC(),
|
|
||||||
End: time.Unix(endField, 0).UTC(),
|
|
||||||
Group: grouping,
|
|
||||||
Order: orderBy,
|
|
||||||
Limit: int(limit),
|
|
||||||
Offset: int(offset),
|
|
||||||
FillEmpty: fill,
|
|
||||||
}
|
|
||||||
|
|
||||||
if query.Limit != 0 {
|
|
||||||
db = db.Limit(query.Limit)
|
|
||||||
}
|
|
||||||
if query.Offset > 0 {
|
|
||||||
db = db.Offset(query.Offset)
|
|
||||||
}
|
|
||||||
if !query.Start.IsZero() && !query.End.IsZero() {
|
|
||||||
db = db.Where("created_at BETWEEN ? AND ?", db.FormatTime(query.Start), db.FormatTime(query.End))
|
|
||||||
} else {
|
|
||||||
if !query.Start.IsZero() {
|
|
||||||
db = db.Where("created_at > ?", db.FormatTime(query.Start))
|
|
||||||
}
|
|
||||||
if !query.End.IsZero() {
|
|
||||||
db = db.Where("created_at < ?", db.FormatTime(query.End))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if query.Order != "" {
|
|
||||||
db = db.Order(query.Order)
|
|
||||||
}
|
|
||||||
query.db = db.Debug()
|
|
||||||
|
|
||||||
return query
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseForm(r *http.Request) url.Values {
|
|
||||||
r.ParseForm()
|
|
||||||
return r.PostForm
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseGet(r *http.Request) url.Values {
|
|
||||||
r.ParseForm()
|
|
||||||
return r.Form
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,215 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/hunterlong/statping/types"
|
||||||
|
"github.com/hunterlong/statping/utils"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type GroupBy struct {
|
||||||
|
db Database
|
||||||
|
query *GroupQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
type GroupByer interface {
|
||||||
|
ToTimeValue(interface{}) (*TimeVar, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type By string
|
||||||
|
|
||||||
|
func (b By) String() string {
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
type GroupQuery struct {
|
||||||
|
db Database
|
||||||
|
Start time.Time
|
||||||
|
End time.Time
|
||||||
|
Group string
|
||||||
|
Order string
|
||||||
|
Limit int
|
||||||
|
Offset int
|
||||||
|
FillEmpty bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b GroupQuery) Database() Database {
|
||||||
|
return b.db
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ByCount = By("COUNT(id) as amount")
|
||||||
|
BySum = func(column string) By {
|
||||||
|
return By(fmt.Sprintf("SUM(%s) as amount", column))
|
||||||
|
}
|
||||||
|
ByAverage = func(column string) By {
|
||||||
|
return By(fmt.Sprintf("AVG(%s) as amount", column))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (db *Db) GroupQuery(q *GroupQuery, by By) GroupByer {
|
||||||
|
dbQuery := db.MultipleSelects(
|
||||||
|
db.SelectByTime(q.Group),
|
||||||
|
by.String(),
|
||||||
|
).Group("timeframe")
|
||||||
|
|
||||||
|
return &GroupBy{dbQuery, q}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TimeVar struct {
|
||||||
|
g *GroupBy
|
||||||
|
data []*TimeValue
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TimeVar) ToValues() []*TimeValue {
|
||||||
|
return t.data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GroupBy) toFloatRows() []*TimeValue {
|
||||||
|
rows, err := g.db.Rows()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var data []*TimeValue
|
||||||
|
for rows.Next() {
|
||||||
|
var timeframe time.Time
|
||||||
|
amount := float64(0)
|
||||||
|
rows.Scan(&timeframe, &amount)
|
||||||
|
newTs := types.FixedTime(timeframe, g.duration())
|
||||||
|
data = append(data, &TimeValue{
|
||||||
|
Timeframe: newTs,
|
||||||
|
Amount: amount,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GroupBy) ToTimeValue(dbType interface{}) (*TimeVar, error) {
|
||||||
|
rows, err := g.db.Rows()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var data []*TimeValue
|
||||||
|
for rows.Next() {
|
||||||
|
var timeframe time.Time
|
||||||
|
amount := float64(0)
|
||||||
|
rows.Scan(&timeframe, &amount)
|
||||||
|
newTs := types.FixedTime(timeframe, g.duration())
|
||||||
|
data = append(data, &TimeValue{
|
||||||
|
Timeframe: newTs,
|
||||||
|
Amount: amount,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return &TimeVar{g, data}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *TimeVar) FillMissing(current, end time.Time) []*TimeValue {
|
||||||
|
timeMap := make(map[string]float64)
|
||||||
|
var validSet []*TimeValue
|
||||||
|
dur := t.g.duration()
|
||||||
|
for _, v := range t.data {
|
||||||
|
timeMap[v.Timeframe] = v.Amount
|
||||||
|
}
|
||||||
|
|
||||||
|
currentStr := types.FixedTime(current, t.g.duration())
|
||||||
|
|
||||||
|
for {
|
||||||
|
var amount float64
|
||||||
|
if timeMap[currentStr] != 0 {
|
||||||
|
amount = timeMap[currentStr]
|
||||||
|
}
|
||||||
|
validSet = append(validSet, &TimeValue{
|
||||||
|
Timeframe: currentStr,
|
||||||
|
Amount: amount,
|
||||||
|
})
|
||||||
|
if current.After(end) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
current = current.Add(dur)
|
||||||
|
currentStr = types.FixedTime(current, t.g.duration())
|
||||||
|
}
|
||||||
|
|
||||||
|
return validSet
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GroupBy) duration() time.Duration {
|
||||||
|
switch g.query.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseQueries(r *http.Request, db Database) *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"))
|
||||||
|
offset := utils.ToInt(fields.Get("offset"))
|
||||||
|
fill, _ := strconv.ParseBool(fields.Get("fill"))
|
||||||
|
orderBy := fields.Get("order")
|
||||||
|
if limit == 0 {
|
||||||
|
limit = 10000
|
||||||
|
}
|
||||||
|
|
||||||
|
query := &GroupQuery{
|
||||||
|
Start: time.Unix(startField, 0).UTC(),
|
||||||
|
End: time.Unix(endField, 0).UTC(),
|
||||||
|
Group: grouping,
|
||||||
|
Order: orderBy,
|
||||||
|
Limit: int(limit),
|
||||||
|
Offset: int(offset),
|
||||||
|
FillEmpty: fill,
|
||||||
|
}
|
||||||
|
|
||||||
|
if query.Limit != 0 {
|
||||||
|
db = db.Limit(query.Limit)
|
||||||
|
}
|
||||||
|
if query.Offset > 0 {
|
||||||
|
db = db.Offset(query.Offset)
|
||||||
|
}
|
||||||
|
if !query.Start.IsZero() && !query.End.IsZero() {
|
||||||
|
db = db.Where("created_at BETWEEN ? AND ?", db.FormatTime(query.Start), db.FormatTime(query.End))
|
||||||
|
} else {
|
||||||
|
if !query.Start.IsZero() {
|
||||||
|
db = db.Where("created_at > ?", db.FormatTime(query.Start))
|
||||||
|
}
|
||||||
|
if !query.End.IsZero() {
|
||||||
|
db = db.Where("created_at < ?", db.FormatTime(query.End))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if query.Order != "" {
|
||||||
|
db = db.Order(query.Order)
|
||||||
|
}
|
||||||
|
query.db = db.Debug()
|
||||||
|
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseForm(r *http.Request) url.Values {
|
||||||
|
r.ParseForm()
|
||||||
|
return r.PostForm
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseGet(r *http.Request) url.Values {
|
||||||
|
r.ParseForm()
|
||||||
|
return r.Form
|
||||||
|
}
|
|
@ -1,22 +0,0 @@
|
||||||
package database
|
|
||||||
|
|
||||||
import "github.com/hunterlong/statping/types"
|
|
||||||
|
|
||||||
type Group struct {
|
|
||||||
db Database
|
|
||||||
group *types.Group
|
|
||||||
}
|
|
||||||
|
|
||||||
type Groupser interface {
|
|
||||||
Services() Database
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *Db) GetGroup(id int64) (Groupser, error) {
|
|
||||||
var group types.Group
|
|
||||||
query := it.Model(&types.Group{}).Where("id = ?", id).Find(&group)
|
|
||||||
return &Group{it, &group}, query.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (it *Group) Services() Database {
|
|
||||||
return it.db.Model(&types.Service{}).Where("group = ?", it.group.Id)
|
|
||||||
}
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import "github.com/hunterlong/statping/types"
|
||||||
|
|
||||||
|
type hits struct {
|
||||||
|
DB Database
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hits) All() []*types.Hit {
|
||||||
|
var fails []*types.Hit
|
||||||
|
h.DB = h.DB.Find(&fails)
|
||||||
|
return fails
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hits) Last(amount int) *types.Hit {
|
||||||
|
var hits types.Hit
|
||||||
|
h.DB = h.DB.Limit(amount).Find(&hits)
|
||||||
|
return &hits
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hits) Count() int {
|
||||||
|
var amount int
|
||||||
|
h.DB = h.DB.Count(&amount)
|
||||||
|
return amount
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h *hits) Find(data interface{}) error {
|
||||||
|
q := h.Find(&data)
|
||||||
|
return q
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import "github.com/hunterlong/statping/types"
|
||||||
|
|
||||||
|
type IncidentObj struct {
|
||||||
|
*types.Incident
|
||||||
|
db Database
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *IncidentObj) AsIncident() *types.Incident {
|
||||||
|
return o.Incident
|
||||||
|
}
|
||||||
|
|
||||||
|
func Incident(id int64) (*IncidentObj, error) {
|
||||||
|
var incident types.Incident
|
||||||
|
query := database.Model(&types.Incident{}).Where("id = ?", id).Find(&incident)
|
||||||
|
return &IncidentObj{Incident: &incident, db: query}, query.Error()
|
||||||
|
}
|
|
@ -3,25 +3,29 @@ package database
|
||||||
import "github.com/hunterlong/statping/types"
|
import "github.com/hunterlong/statping/types"
|
||||||
|
|
||||||
type ServiceObj struct {
|
type ServiceObj struct {
|
||||||
db Database
|
*types.Service
|
||||||
service *types.Service
|
failures
|
||||||
}
|
}
|
||||||
|
|
||||||
type Servicer interface {
|
func (o *Object) AsService() *types.Service {
|
||||||
Failures() Database
|
return o.model.(*types.Service)
|
||||||
Hits() Database
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Service(id int64) (Servicer, error) {
|
func Service(id int64) (HitsFailures, error) {
|
||||||
var service types.Service
|
var service types.Service
|
||||||
query := database.Model(&types.Service{}).Where("id = ?", id).Find(&service)
|
query := database.Model(&types.Service{}).Where("id = ?", id).Find(&service)
|
||||||
return &ServiceObj{query, &service}, query.Error()
|
return &ServiceObj{Service: &service}, query.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServiceObj) Failures() Database {
|
func (s *ServiceObj) Hits() *hits {
|
||||||
return database.Model(&types.Failure{}).Where("service = ?", s.service.Id)
|
return &hits{
|
||||||
|
database.Model(&types.Hit{}).Where("service = ?", s.Id),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ServiceObj) Hits() Database {
|
func (s *ServiceObj) Failures() *failures {
|
||||||
return database.Model(&types.Hit{}).Where("service = ?", s.service.Id)
|
return &failures{
|
||||||
|
database.Model(&types.Failure{}).
|
||||||
|
Where("method != 'checkin' AND service = ?", s.Id).Order("id desc"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package database
|
||||||
|
|
||||||
|
import "github.com/hunterlong/statping/types"
|
||||||
|
|
||||||
|
type UserObj struct {
|
||||||
|
*types.User
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *Object) AsUser() *UserObj {
|
||||||
|
return &UserObj{
|
||||||
|
User: o.model.(*types.User),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func User(id int64) (*UserObj, error) {
|
||||||
|
var user types.User
|
||||||
|
query := database.Model(&types.User{}).Where("id = ?", id).Find(&user)
|
||||||
|
return &UserObj{User: &user}, query.Error()
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"github.com/hunterlong/statping/core"
|
"github.com/hunterlong/statping/core"
|
||||||
"html/template"
|
"html/template"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -11,6 +12,39 @@ var (
|
||||||
basePath = "/"
|
basePath = "/"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type HandlerFunc func(Responder, *Request)
|
||||||
|
|
||||||
|
func (f HandlerFunc) ServeHTTP(w Responder, r *Request) {
|
||||||
|
f(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handler interface {
|
||||||
|
ServeHTTP(Responder, *Request)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Responder struct {
|
||||||
|
Code int // the HTTP response code from WriteHeader
|
||||||
|
HeaderMap http.Header // the HTTP response headers
|
||||||
|
Body *bytes.Buffer // if non-nil, the bytes.Buffer to append written data to
|
||||||
|
Flushed bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type Request struct {
|
||||||
|
*http.Request
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Responder) Header() http.Header {
|
||||||
|
return r.HeaderMap
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Responder) Write(p []byte) (int, error) {
|
||||||
|
return r.Body.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r Responder) WriteHeader(statusCode int) {
|
||||||
|
r.Code = statusCode
|
||||||
|
}
|
||||||
|
|
||||||
func parseForm(r *http.Request) url.Values {
|
func parseForm(r *http.Request) url.Values {
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
return r.PostForm
|
return r.PostForm
|
||||||
|
|
|
@ -130,8 +130,8 @@ func readOnly(handler func(w http.ResponseWriter, r *http.Request), redirect boo
|
||||||
}
|
}
|
||||||
|
|
||||||
// cached is a middleware function that accepts a duration and content type and will cache the response of the original request
|
// cached is a middleware function that accepts a duration and content type and will cache the response of the original request
|
||||||
func cached(duration, contentType string, handler func(w http.ResponseWriter, r *http.Request)) http.Handler {
|
func cached(duration, contentType string, handler func(w Responder, r *Request)) Responder {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return HandlerFunc(func(w Responder, r *Request) {
|
||||||
content := CacheStorage.Get(r.RequestURI)
|
content := CacheStorage.Get(r.RequestURI)
|
||||||
w.Header().Set("Content-Type", contentType)
|
w.Header().Set("Content-Type", contentType)
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
|
|
@ -150,7 +150,7 @@ func apiServiceRunningHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
sendJsonAction(service, "running", w, r)
|
sendJsonAction(service, "running", w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
|
func apiServiceDataHandler(w Responder, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
service, err := database.Service(utils.ToInt(vars["id"]))
|
service, err := database.Service(utils.ToInt(vars["id"]))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -158,7 +158,7 @@ func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
groupQuery := database.ParseQueries(r, service.Hits())
|
groupQuery := database.ParseQueries(r, service.Hits().DB)
|
||||||
|
|
||||||
obj := core.GraphData(groupQuery, &types.Hit{}, database.ByAverage("latency"))
|
obj := core.GraphData(groupQuery, &types.Hit{}, database.ByAverage("latency"))
|
||||||
returnJson(obj, w, r)
|
returnJson(obj, w, r)
|
||||||
|
@ -171,7 +171,7 @@ func apiServiceFailureDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
sendErrorJson(errors.New("service data not found"), w, r)
|
sendErrorJson(errors.New("service data not found"), w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
groupQuery := database.ParseQueries(r, service.Failures())
|
groupQuery := database.ParseQueries(r, service.Hits().DB)
|
||||||
|
|
||||||
obj := core.GraphData(groupQuery, &types.Failure{}, database.ByCount)
|
obj := core.GraphData(groupQuery, &types.Failure{}, database.ByCount)
|
||||||
returnJson(obj, w, r)
|
returnJson(obj, w, r)
|
||||||
|
@ -184,7 +184,7 @@ func apiServicePingDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
sendErrorJson(errors.New("service data not found"), w, r)
|
sendErrorJson(errors.New("service data not found"), w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
groupQuery := database.ParseQueries(r, service.Hits())
|
groupQuery := database.ParseQueries(r, service.Hits().DB)
|
||||||
|
|
||||||
obj := core.GraphData(groupQuery, &types.Hit{}, database.ByAverage("ping_time"))
|
obj := core.GraphData(groupQuery, &types.Hit{}, database.ByAverage("ping_time"))
|
||||||
returnJson(obj, w, r)
|
returnJson(obj, w, r)
|
||||||
|
@ -274,7 +274,7 @@ func joinServices(srvs []types.ServiceInterface) []*types.Service {
|
||||||
return services
|
return services
|
||||||
}
|
}
|
||||||
|
|
||||||
func servicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) {
|
func servicesDeleteFailuresHandler(w Responder, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
service := core.SelectService(utils.ToInt(vars["id"]))
|
service := core.SelectService(utils.ToInt(vars["id"]))
|
||||||
if service == nil {
|
if service == nil {
|
||||||
|
@ -293,8 +293,12 @@ func apiServiceFailuresHandler(r *http.Request) interface{} {
|
||||||
return errors.New("service not found")
|
return errors.New("service not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
service.Hits()
|
||||||
|
|
||||||
|
service.Failures()
|
||||||
|
|
||||||
var fails []types.Failure
|
var fails []types.Failure
|
||||||
service.Failures().Requests(r).Find(&fails)
|
service.Failures().DB.Requests(r).Find(&fails)
|
||||||
return fails
|
return fails
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,11 +30,11 @@ type User struct {
|
||||||
Admin NullBool `gorm:"column:administrator" json:"admin,omitempty"`
|
Admin NullBool `gorm:"column:administrator" json:"admin,omitempty"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||||
UserInterface `gorm:"-" json:"-"`
|
DatabaseInter `gorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserInterface interfaces the Db functions
|
// UserInterface interfaces the Db functions
|
||||||
type UserInterface interface {
|
type DatabaseInter interface {
|
||||||
Create() (int64, error)
|
Create() (int64, error)
|
||||||
Update() error
|
Update() error
|
||||||
Delete() error
|
Delete() error
|
||||||
|
|
Loading…
Reference in New Issue