mirror of https://github.com/statping/statping
pull/429/head
parent
05c77a5e98
commit
871424f9c3
|
@ -19,6 +19,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/go-yaml/yaml"
|
||||
"github.com/hunterlong/statping/core/notifier"
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"github.com/jinzhu/gorm"
|
||||
|
@ -32,7 +33,7 @@ import (
|
|||
|
||||
var (
|
||||
// DbSession stores the Statping database session
|
||||
DbSession types.Database
|
||||
DbSession database.Database
|
||||
DbModels []interface{}
|
||||
)
|
||||
|
||||
|
@ -47,7 +48,7 @@ func init() {
|
|||
// DbConfig stores the config.yml file for the statup configuration
|
||||
type DbConfig types.DbConfig
|
||||
|
||||
func Database(obj interface{}) types.Database {
|
||||
func Database(obj interface{}) database.Database {
|
||||
switch obj.(type) {
|
||||
case *types.Service, *Service, []*Service:
|
||||
return DbSession.Model(&types.Service{})
|
||||
|
@ -77,7 +78,7 @@ func Database(obj interface{}) types.Database {
|
|||
}
|
||||
|
||||
// HitsBetween returns the gorm database query for a collection of service hits between a time range
|
||||
func (s *Service) HitsBetween(t1, t2 time.Time, group string, column string) types.Database {
|
||||
func (s *Service) HitsBetween(t1, t2 time.Time, group string, column string) database.Database {
|
||||
selector := Dbtimestamp(group, column)
|
||||
if CoreApp.Config.DbConn == "postgres" {
|
||||
return Database(&Hit{}).Select(selector).Where("service = ? AND created_at BETWEEN ? AND ?", s.Id, t1.UTC().Format(types.TIME), t2.UTC().Format(types.TIME))
|
||||
|
@ -87,7 +88,7 @@ func (s *Service) HitsBetween(t1, t2 time.Time, group string, column string) typ
|
|||
}
|
||||
|
||||
// FailuresBetween returns the gorm database query for a collection of service hits between a time range
|
||||
func (s *Service) FailuresBetween(t1, t2 time.Time, group string, column string) types.Database {
|
||||
func (s *Service) FailuresBetween(t1, t2 time.Time, group string, column string) database.Database {
|
||||
selector := Dbtimestamp(group, column)
|
||||
if CoreApp.Config.DbConn == "postgres" {
|
||||
return Database(&Failure{}).Select(selector).Where("service = ? AND created_at BETWEEN ? AND ?", s.Id, t1.UTC().Format(types.TIME), t2.UTC().Format(types.TIME))
|
||||
|
@ -226,7 +227,7 @@ func (c *Core) Connect(retry bool, location string) error {
|
|||
conn = fmt.Sprintf("sqlserver://%v:%v@%v?database=%v", CoreApp.Config.DbUser, CoreApp.Config.DbPass, host, CoreApp.Config.DbData)
|
||||
}
|
||||
log.WithFields(utils.ToFields(c, conn)).Debugln("attempting to connect to database")
|
||||
dbSession, err := types.Openw(dbType, conn)
|
||||
dbSession, err := database.Openw(dbType, conn)
|
||||
if err != nil {
|
||||
log.Debugln(fmt.Sprintf("Database connection error %v", err))
|
||||
if retry {
|
||||
|
|
|
@ -18,6 +18,7 @@ package core
|
|||
import (
|
||||
"fmt"
|
||||
"github.com/ararog/timeago"
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
@ -53,7 +54,7 @@ func (s *Service) CreateFailure(f *types.Failure) (int64, error) {
|
|||
// AllFailures will return all failures attached to a service
|
||||
func (s *Service) AllFailures() []types.Failure {
|
||||
var fails []types.Failure
|
||||
err := DbSession.Failures(s.Id).Find(&fails)
|
||||
err := Database(&types.Failure{}).Find(&fails)
|
||||
if err.Error() != nil {
|
||||
log.Errorln(fmt.Sprintf("Issue getting failures for service %v, %v", s.Name, err))
|
||||
return nil
|
||||
|
@ -61,7 +62,7 @@ func (s *Service) AllFailures() []types.Failure {
|
|||
return fails
|
||||
}
|
||||
|
||||
func (s *Service) FailuresDb(r *http.Request) types.Database {
|
||||
func (s *Service) FailuresDb(r *http.Request) database.Database {
|
||||
return Database(&types.Failure{}).Where("service = ?", s.Id).QuerySearch(r).Order("id desc")
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"net/http"
|
||||
"time"
|
||||
|
@ -51,7 +52,7 @@ func (s *Service) HitsQuery(r *http.Request) ([]*types.Hit, error) {
|
|||
return hits, err.Error()
|
||||
}
|
||||
|
||||
func (s *Service) HitsDb(r *http.Request) types.Database {
|
||||
func (s *Service) HitsDb(r *http.Request) database.Database {
|
||||
return Database(&types.Hit{}).Where("service = ?", s.Id).QuerySearch(r).Order("id desc")
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
)
|
||||
|
@ -26,7 +27,7 @@ import (
|
|||
var (
|
||||
Integrations []types.Integrator
|
||||
log = utils.Log.WithField("type", "integration")
|
||||
db types.Database
|
||||
db database.Database
|
||||
)
|
||||
|
||||
//func init() {
|
||||
|
@ -38,12 +39,12 @@ var (
|
|||
//}
|
||||
|
||||
// integrationsDb returns the 'integrations' database column
|
||||
func integrationsDb() types.Database {
|
||||
func integrationsDb() database.Database {
|
||||
return db.Model(&types.Integration{})
|
||||
}
|
||||
|
||||
// SetDB is called by core to inject the database for a integrator to use
|
||||
func SetDB(d types.Database) {
|
||||
func SetDB(d database.Database) {
|
||||
db = d
|
||||
}
|
||||
|
||||
|
@ -106,7 +107,7 @@ func Find(name string) (types.Integrator, error) {
|
|||
}
|
||||
|
||||
// db will return the notifier database column/record
|
||||
func integratorDb(n *types.Integration) types.Database {
|
||||
func integratorDb(n *types.Integration) database.Database {
|
||||
return db.Model(&types.Integration{}).Where("name = ?", n.Name).Find(n)
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"reflect"
|
||||
|
@ -30,7 +31,7 @@ var (
|
|||
// AllCommunications holds all the loaded notifiers
|
||||
AllCommunications []types.AllNotifiers
|
||||
// db holds the Statping database connection
|
||||
db types.Database
|
||||
db database.Database
|
||||
timezone float32
|
||||
log = utils.Log.WithField("type", "notifier")
|
||||
)
|
||||
|
@ -111,12 +112,12 @@ func (n *Notification) CanTest() bool {
|
|||
}
|
||||
|
||||
// db will return the notifier database column/record
|
||||
func modelDb(n *Notification) types.Database {
|
||||
func modelDb(n *Notification) database.Database {
|
||||
return db.Model(&Notification{}).Where("method = ?", n.Method).Find(n)
|
||||
}
|
||||
|
||||
// SetDB is called by core to inject the database for a notifier to use
|
||||
func SetDB(d types.Database) {
|
||||
func SetDB(d database.Database) {
|
||||
db = d
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/ararog/timeago"
|
||||
"github.com/hunterlong/statping/core/notifier"
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"sort"
|
||||
|
@ -268,41 +269,37 @@ func (s *Service) Downtime() time.Duration {
|
|||
|
||||
// DateScanObj struct is for creating the charts.js graph JSON array
|
||||
type DateScanObj struct {
|
||||
Array []*types.DateScan `json:"data"`
|
||||
Array []*database.DateScan `json:"data"`
|
||||
}
|
||||
|
||||
// GraphDataRaw will return all the hits between 2 times for a Service
|
||||
func GraphHitsDataRaw(service types.ServiceInterface, query types.GroupQuery, column string) *DateScanObj {
|
||||
func GraphHitsDataRaw(service types.ServiceInterface, query *types.GroupQuery, column string) []*database.TimeValue {
|
||||
srv := service.(*Service)
|
||||
|
||||
dbQuery := Database(&types.Hit{}).
|
||||
dbQuery, err := Database(&types.Hit{}).
|
||||
Where("service = ?", srv.Id).
|
||||
Between(query.Start, query.End).
|
||||
MultipleSelects(Database(&types.Hit{}).SelectByTime(query.Group), types.CountAmount()).
|
||||
GroupByTimeframe()
|
||||
GroupQuery(query).ToTimeValue()
|
||||
|
||||
outgoing, err := dbQuery.ToChart()
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil
|
||||
}
|
||||
return &DateScanObj{outgoing}
|
||||
return dbQuery.FillMissing()
|
||||
}
|
||||
|
||||
// GraphDataRaw will return all the hits between 2 times for a Service
|
||||
func GraphFailuresDataRaw(service types.ServiceInterface, query types.GroupQuery) []*types.TimeValue {
|
||||
func GraphFailuresDataRaw(service types.ServiceInterface, query *types.GroupQuery) []*database.TimeValue {
|
||||
srv := service.(*Service)
|
||||
|
||||
dbQuery := Database(&types.Failure{}).
|
||||
dbQuery, err := Database(&types.Failure{}).
|
||||
Where("service = ?", srv.Id).
|
||||
Between(query.Start, query.End).
|
||||
MultipleSelects(Database(&types.Failure{}).SelectByTime(query.Group), types.CountAmount()).
|
||||
GroupByTimeframe()
|
||||
GroupQuery(query).ToTimeValue()
|
||||
|
||||
outgoing, err := dbQuery.ToTimeValue(query.Start, query.End)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return nil
|
||||
}
|
||||
return outgoing
|
||||
return dbQuery.FillMissing()
|
||||
}
|
||||
|
||||
// ToString will convert the DateScanObj into a JSON string for the charts to render
|
||||
|
|
|
@ -1,23 +1,9 @@
|
|||
// Statup
|
||||
// Copyright (C) 2020. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statup
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
package database
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/jinzhu/gorm"
|
||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||
|
@ -28,6 +14,13 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
TIME_NANO = "2006-01-02T15:04:05Z"
|
||||
TIME = "2006-01-02 15:04:05"
|
||||
CHART_TIME = "2006-01-02T15:04:05.999999-07:00"
|
||||
TIME_DAY = "2006-01-02"
|
||||
)
|
||||
|
||||
// Database is an interface which DB implements
|
||||
type Database interface {
|
||||
Close() error
|
||||
|
@ -110,58 +103,16 @@ type Database interface {
|
|||
|
||||
Since(time.Time) Database
|
||||
Between(time.Time, time.Time) Database
|
||||
Hits() ([]*Hit, error)
|
||||
Hits() ([]*types.Hit, error)
|
||||
ToChart() ([]*DateScan, error)
|
||||
|
||||
GroupByTimeframe() Database
|
||||
ToTimeValue(time.Time, time.Time) ([]*TimeValue, error)
|
||||
SelectByTime(string) string
|
||||
MultipleSelects(args ...string) Database
|
||||
|
||||
Failurer
|
||||
}
|
||||
FormatTime(t time.Time) string
|
||||
ParseTime(t string) (time.Time, error)
|
||||
|
||||
type Failurer interface {
|
||||
Failures(id int64) Database
|
||||
Fails() ([]*Failure, error)
|
||||
}
|
||||
|
||||
func mysqlTimestamps(increment string) string {
|
||||
switch increment {
|
||||
case "second":
|
||||
return "%Y-%m-%d %H:%i:%S"
|
||||
case "minute":
|
||||
return "%Y-%m-%d %H:%i:00"
|
||||
case "hour":
|
||||
return "%Y-%m-%d %H:00:00"
|
||||
case "day":
|
||||
return "%Y-%m-%d 00:00:00"
|
||||
case "month":
|
||||
return "%Y-%m 00:00:00"
|
||||
case "year":
|
||||
return "%Y"
|
||||
default:
|
||||
return "%Y-%m-%d 00:00:00"
|
||||
}
|
||||
}
|
||||
|
||||
func sqliteTimestamps(increment string) string {
|
||||
switch increment {
|
||||
case "second":
|
||||
return "%Y-%m-%d %H:%M:%S"
|
||||
case "minute":
|
||||
return "%Y-%m-%d %H:%M:00"
|
||||
case "hour":
|
||||
return "%Y-%m-%d %H:00:00"
|
||||
case "day":
|
||||
return "%Y-%m-%d 00:00:00"
|
||||
case "month":
|
||||
return "%Y-%m 00:00:00"
|
||||
case "year":
|
||||
return "%Y"
|
||||
default:
|
||||
return "%Y-%m-%d 00:00:00"
|
||||
}
|
||||
GroupQuery(query *types.GroupQuery) GroupByer
|
||||
}
|
||||
|
||||
func (it *Db) MultipleSelects(args ...string) Database {
|
||||
|
@ -173,31 +124,6 @@ func CountAmount() string {
|
|||
return fmt.Sprintf("COUNT(id) as amount")
|
||||
}
|
||||
|
||||
func (it *Db) SelectByTime(increment string) string {
|
||||
switch it.Type {
|
||||
case "mysql":
|
||||
return fmt.Sprintf("CONCAT(date_format(created_at, '%s')) AS timeframe", mysqlTimestamps(increment))
|
||||
case "postgres":
|
||||
return fmt.Sprintf("date_trunc('%s', created_at) AS timeframe", increment)
|
||||
default:
|
||||
return fmt.Sprintf("strftime('%s', created_at, 'utc') as timeframe", sqliteTimestamps(increment))
|
||||
}
|
||||
}
|
||||
|
||||
func (it *Db) GroupByTimeframe() Database {
|
||||
return it.Group("timeframe")
|
||||
}
|
||||
|
||||
func (it *Db) Failures(id int64) Database {
|
||||
return it.Model(&Failure{}).Where("service = ?", id).Not("method = 'checkin'").Order("id desc")
|
||||
}
|
||||
|
||||
func (it *Db) Fails() ([]*Failure, error) {
|
||||
var fails []*Failure
|
||||
err := it.Find(&fails)
|
||||
return fails, err.Error()
|
||||
}
|
||||
|
||||
type Db struct {
|
||||
Database *gorm.DB
|
||||
Type string
|
||||
|
@ -513,18 +439,18 @@ func (it *Db) Error() error {
|
|||
return it.Database.Error
|
||||
}
|
||||
|
||||
func (it *Db) Hits() ([]*Hit, error) {
|
||||
var hits []*Hit
|
||||
func (it *Db) Hits() ([]*types.Hit, error) {
|
||||
var hits []*types.Hit
|
||||
err := it.Find(&hits)
|
||||
return hits, err.Error()
|
||||
}
|
||||
|
||||
func (it *Db) Since(ago time.Time) Database {
|
||||
return it.Where("created_at > ?", ago.UTC().Format(TIME))
|
||||
return it.Where("created_at > ?", it.FormatTime(ago))
|
||||
}
|
||||
|
||||
func (it *Db) Between(t1 time.Time, t2 time.Time) Database {
|
||||
return it.Where("created_at BETWEEN ? AND ?", t1.UTC().Format(TIME), t2.UTC().Format(TIME))
|
||||
return it.Where("created_at BETWEEN ? AND ?", it.FormatTime(t1), it.FormatTime(t2))
|
||||
}
|
||||
|
||||
// DateScan struct is for creating the charts.js graph JSON array
|
||||
|
@ -538,75 +464,6 @@ type TimeValue struct {
|
|||
Amount int64 `json:"amount"`
|
||||
}
|
||||
|
||||
func (it *Db) ToTimeValue(start, end time.Time) ([]*TimeValue, error) {
|
||||
rows, err := it.Database.Rows()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data []*TimeValue
|
||||
for rows.Next() {
|
||||
var timeframe string
|
||||
var amount int64
|
||||
if err := rows.Scan(&timeframe, &amount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
createdTime, _ := time.Parse(TIME, timeframe)
|
||||
fmt.Println("got: ", createdTime.UTC(), amount)
|
||||
data = append(data, &TimeValue{
|
||||
Timeframe: createdTime.UTC(),
|
||||
Amount: amount,
|
||||
})
|
||||
}
|
||||
return it.fillMissing(data, start, end), nil
|
||||
}
|
||||
|
||||
func (it *Db) FormatTime(t time.Time) string {
|
||||
switch it.Type {
|
||||
case "mysql":
|
||||
return t.UTC().Format("2006-01-02T00:00:00Z")
|
||||
case "postgres":
|
||||
return t.UTC().Format("2006-01-02T00:00:00Z")
|
||||
default:
|
||||
return t.UTC().Format("2006-01-02T00:00:00Z")
|
||||
}
|
||||
}
|
||||
|
||||
func reparseTime(t string) time.Time {
|
||||
re, _ := time.Parse("2006-01-02T00:00:00Z", t)
|
||||
return re.UTC()
|
||||
}
|
||||
|
||||
func (it *Db) fillMissing(vals []*TimeValue, start, end time.Time) []*TimeValue {
|
||||
timeMap := make(map[string]*TimeValue)
|
||||
var validSet []*TimeValue
|
||||
|
||||
for _, v := range vals {
|
||||
timeMap[it.FormatTime(v.Timeframe)] = v
|
||||
}
|
||||
|
||||
current := start.UTC()
|
||||
maxTime := end
|
||||
for {
|
||||
amount := int64(0)
|
||||
currentStr := it.FormatTime(current)
|
||||
if timeMap[currentStr] != nil {
|
||||
amount = timeMap[currentStr].Amount
|
||||
}
|
||||
|
||||
validSet = append(validSet, &TimeValue{
|
||||
Timeframe: reparseTime(currentStr),
|
||||
Amount: amount,
|
||||
})
|
||||
|
||||
if current.After(maxTime) {
|
||||
break
|
||||
}
|
||||
current = current.Add(24 * time.Hour)
|
||||
}
|
||||
|
||||
return validSet
|
||||
}
|
||||
|
||||
func (it *Db) ToChart() ([]*DateScan, error) {
|
||||
rows, err := it.Database.Rows()
|
||||
if err != nil {
|
|
@ -0,0 +1,129 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"time"
|
||||
)
|
||||
|
||||
type GroupBy struct {
|
||||
db Database
|
||||
query *types.GroupQuery
|
||||
}
|
||||
|
||||
type GroupByer interface {
|
||||
ToTimeValue() (*TimeVar, error)
|
||||
}
|
||||
|
||||
type GroupMethod interface {
|
||||
}
|
||||
|
||||
var (
|
||||
ByCount = func() GroupMethod {
|
||||
return fmt.Sprintf("COUNT(id) as amount")
|
||||
}
|
||||
BySum = func(column string) GroupMethod {
|
||||
return fmt.Sprintf("SUM(%s) as amount", column)
|
||||
}
|
||||
ByAverage = func(column string) GroupMethod {
|
||||
return fmt.Sprintf("SUM(%s) as amount", column)
|
||||
}
|
||||
)
|
||||
|
||||
func execute(db Database, query *types.GroupQuery) Database {
|
||||
return db.MultipleSelects(
|
||||
db.SelectByTime(query.Group),
|
||||
CountAmount(),
|
||||
).Between(query.Start, query.End).Group("timeframe").Debug()
|
||||
}
|
||||
|
||||
func (db *Db) GroupQuery(query *types.GroupQuery) GroupByer {
|
||||
return &GroupBy{execute(db, query), query}
|
||||
}
|
||||
|
||||
type TimeVar struct {
|
||||
g *GroupBy
|
||||
data []*TimeValue
|
||||
}
|
||||
|
||||
func (g *GroupBy) ToTimeValue() (*TimeVar, error) {
|
||||
rows, err := g.db.Rows()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var data []*TimeValue
|
||||
for rows.Next() {
|
||||
var timeframe string
|
||||
var amount int64
|
||||
if err := rows.Scan(&timeframe, &amount); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
createdTime, _ := g.db.ParseTime(timeframe)
|
||||
data = append(data, &TimeValue{
|
||||
Timeframe: createdTime,
|
||||
Amount: amount,
|
||||
})
|
||||
|
||||
}
|
||||
return &TimeVar{g, data}, nil
|
||||
}
|
||||
|
||||
func (t *TimeVar) Values() []*TimeValue {
|
||||
var validSet []*TimeValue
|
||||
for _, v := range t.data {
|
||||
validSet = append(validSet, &TimeValue{
|
||||
Timeframe: v.Timeframe,
|
||||
Amount: v.Amount,
|
||||
})
|
||||
}
|
||||
|
||||
return validSet
|
||||
}
|
||||
|
||||
func (t *TimeVar) FillMissing() []*TimeValue {
|
||||
timeMap := make(map[time.Time]*TimeValue)
|
||||
var validSet []*TimeValue
|
||||
if len(t.data) == 0 {
|
||||
return nil
|
||||
}
|
||||
current := t.data[0].Timeframe
|
||||
for _, v := range t.data {
|
||||
timeMap[v.Timeframe] = v
|
||||
}
|
||||
maxTime := t.g.query.End
|
||||
for {
|
||||
amount := int64(0)
|
||||
if timeMap[current] != nil {
|
||||
amount = timeMap[current].Amount
|
||||
}
|
||||
validSet = append(validSet, &TimeValue{
|
||||
Timeframe: current,
|
||||
Amount: amount,
|
||||
})
|
||||
if current.After(maxTime) {
|
||||
break
|
||||
}
|
||||
current = current.Add(t.g.duration())
|
||||
}
|
||||
|
||||
return validSet
|
||||
}
|
||||
|
||||
func (g *GroupBy) duration() time.Duration {
|
||||
switch g.query.Group {
|
||||
case "second":
|
||||
return time.Second
|
||||
case "minute":
|
||||
return time.Minute
|
||||
case "hour":
|
||||
return time.Hour
|
||||
case "day":
|
||||
return time.Hour * 24
|
||||
case "month":
|
||||
return time.Hour * 730
|
||||
case "year":
|
||||
return time.Hour * 8760
|
||||
default:
|
||||
return time.Hour
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
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,27 @@
|
|||
package database
|
||||
|
||||
import "github.com/hunterlong/statping/types"
|
||||
|
||||
type Service struct {
|
||||
db Database
|
||||
service *types.Service
|
||||
}
|
||||
|
||||
type Servicer interface {
|
||||
Failures() Database
|
||||
Hits() Database
|
||||
}
|
||||
|
||||
func (it *Db) GetService(id int64) (Servicer, error) {
|
||||
var service types.Service
|
||||
query := it.Model(&types.Service{}).Where("id = ?", id).Find(&service)
|
||||
return &Service{it, &service}, query.Error()
|
||||
}
|
||||
|
||||
func (s *Service) Failures() Database {
|
||||
return s.db.Model(&types.Failure{}).Where("service = ?", s.service.Id)
|
||||
}
|
||||
|
||||
func (s *Service) Hits() Database {
|
||||
return s.db.Model(&types.Hit{}).Where("service = ?", s.service.Id)
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
type TimeGroup interface {
|
||||
}
|
||||
|
||||
func (it *Db) ParseTime(t string) (time.Time, error) {
|
||||
switch it.Type {
|
||||
case "mysql":
|
||||
return time.Parse("2006-01-02 15:04:05", t)
|
||||
case "postgres":
|
||||
return time.Parse("2006-01-02T15:04:05Z", t)
|
||||
default:
|
||||
return time.Parse("2006-01-02 15:04:05", t)
|
||||
}
|
||||
}
|
||||
|
||||
func (it *Db) FormatTime(t time.Time) string {
|
||||
switch it.Type {
|
||||
case "mysql":
|
||||
return t.UTC().Format("2006-01-02 15:04:05")
|
||||
case "postgres":
|
||||
return t.UTC().Format("2006-01-02 15:04:05.999999999")
|
||||
default:
|
||||
return t.UTC().Format("2006-01-02 15:04:05")
|
||||
}
|
||||
}
|
||||
|
||||
func (it *Db) SelectByTime(increment string) string {
|
||||
switch it.Type {
|
||||
case "mysql":
|
||||
return fmt.Sprintf("CONCAT(date_format(created_at, '%s')) AS timeframe", it.correctTimestamp(increment))
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
func (it *Db) correctTimestamp(increment string) string {
|
||||
var timestamper string
|
||||
switch increment {
|
||||
case "second":
|
||||
timestamper = "%Y-%m-%d %H:%M:%S"
|
||||
case "minute":
|
||||
timestamper = "%Y-%m-%d %H:%M:00"
|
||||
case "hour":
|
||||
timestamper = "%Y-%m-%d %H:00:00"
|
||||
case "day":
|
||||
timestamper = "%Y-%m-%d 00:00:00"
|
||||
case "month":
|
||||
timestamper = "%Y-%m-01 00:00:00"
|
||||
case "year":
|
||||
timestamper = "%Y-01-01 00:00:00"
|
||||
default:
|
||||
timestamper = "%Y-%m-%d 00:00:00"
|
||||
}
|
||||
|
||||
switch it.Type {
|
||||
case "mysql":
|
||||
case "second":
|
||||
timestamper = "%Y-%m-%d %H:%i:%S"
|
||||
case "minute":
|
||||
timestamper = "%Y-%m-%d %H:%i:00"
|
||||
}
|
||||
|
||||
return timestamper
|
||||
}
|
|
@ -126,14 +126,18 @@
|
|||
visible: function(newVal, oldVal) {
|
||||
if (newVal && !this.showing) {
|
||||
this.showing = true
|
||||
this.chartHits()
|
||||
this.chartHits("hour")
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async chartHits() {
|
||||
async chartHits(group) {
|
||||
const start = this.nowSubtract((3600 * 24) * 7)
|
||||
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()), group)
|
||||
|
||||
if (this.data.length === 0 && group !== "minute") {
|
||||
await this.chartHits("minute")
|
||||
}
|
||||
this.series = [{
|
||||
name: this.service.name,
|
||||
...this.data
|
||||
|
|
|
@ -175,7 +175,7 @@ func apiServiceFailureDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
returnJson(obj, w, r)
|
||||
}
|
||||
|
||||
func parseGroupQuery(r *http.Request) types.GroupQuery {
|
||||
func parseGroupQuery(r *http.Request) *types.GroupQuery {
|
||||
fields := parseGet(r)
|
||||
grouping := fields.Get("group")
|
||||
if grouping == "" {
|
||||
|
@ -184,7 +184,7 @@ func parseGroupQuery(r *http.Request) types.GroupQuery {
|
|||
startField := utils.ToInt(fields.Get("start"))
|
||||
endField := utils.ToInt(fields.Get("end"))
|
||||
|
||||
return types.GroupQuery{
|
||||
return &types.GroupQuery{
|
||||
Start: time.Unix(startField, 0).UTC(),
|
||||
End: time.Unix(endField, 0).UTC(),
|
||||
Group: grouping,
|
||||
|
@ -305,10 +305,7 @@ func apiServiceFailuresHandler(r *http.Request) interface{} {
|
|||
if servicer == nil {
|
||||
return errors.New("service not found")
|
||||
}
|
||||
fails, err := servicer.FailuresDb(r).Fails()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fails := servicer.LimitedFailures(100)
|
||||
return fails
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue