2018-06-30 00:57:05 +00:00
package core
2018-06-10 01:31:13 +00:00
import (
"encoding/json"
"fmt"
2018-06-30 00:57:05 +00:00
"github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils"
2018-06-11 00:20:42 +00:00
"strconv"
2018-06-10 01:31:13 +00:00
"time"
2018-06-22 04:02:57 +00:00
"upper.io/db.v3"
2018-06-10 01:31:13 +00:00
)
type Service struct {
2018-07-14 02:37:39 +00:00
S interface { }
}
type Failure struct {
F interface { }
2018-06-10 01:31:13 +00:00
}
2018-06-22 04:02:57 +00:00
func serviceCol ( ) db . Collection {
2018-06-30 00:57:05 +00:00
return DbSession . Collection ( "services" )
2018-06-22 04:02:57 +00:00
}
2018-06-23 00:10:37 +00:00
func SelectService ( id int64 ) * Service {
2018-07-14 02:37:39 +00:00
for _ , ser := range CoreApp . Services {
s := ser . ToService ( )
2018-06-23 00:10:37 +00:00
if s . Id == id {
2018-07-14 02:37:39 +00:00
return ser
2018-06-23 00:10:37 +00:00
}
}
return nil
2018-06-10 01:31:13 +00:00
}
2018-06-15 04:30:10 +00:00
func SelectAllServices ( ) ( [ ] * Service , error ) {
2018-07-14 02:37:39 +00:00
var services [ ] * types . Service
var sers [ ] * Service
2018-06-22 04:02:57 +00:00
col := serviceCol ( ) . Find ( )
2018-07-14 02:37:39 +00:00
err := col . All ( & services )
2018-06-30 00:57:05 +00:00
if err != nil {
utils . Log ( 3 , err )
2018-07-12 04:49:18 +00:00
return nil , err
2018-06-30 00:57:05 +00:00
}
2018-07-14 02:37:39 +00:00
for _ , s := range services {
ser := NewService ( s )
sers = append ( sers , ser )
s . Checkins = SelectAllCheckins ( s )
s . Failures = SelectAllFailures ( s )
2018-06-22 06:56:44 +00:00
}
2018-07-14 02:37:39 +00:00
CoreApp . Services = sers
return sers , err
2018-06-10 01:31:13 +00:00
}
func ( s * Service ) AvgTime ( ) float64 {
2018-06-15 04:30:10 +00:00
total , _ := s . TotalHits ( )
if total == 0 {
return float64 ( 0 )
}
sum , _ := s . Sum ( )
2018-06-10 01:31:13 +00:00
avg := sum / float64 ( total ) * 100
2018-06-15 04:30:10 +00:00
amount := fmt . Sprintf ( "%0.0f" , avg * 10 )
val , _ := strconv . ParseFloat ( amount , 10 )
return val
2018-06-10 01:31:13 +00:00
}
2018-07-14 02:37:39 +00:00
func ( ser * Service ) Online24 ( ) float32 {
s := ser . S . ( * types . Service )
total , _ := ser . TotalHits ( )
failed , _ := ser . TotalFailures24Hours ( )
2018-06-10 03:44:47 +00:00
if failed == 0 {
s . Online24Hours = 100.00
return s . Online24Hours
}
2018-06-10 04:21:12 +00:00
if total == 0 {
s . Online24Hours = 0
return s . Online24Hours
}
2018-06-10 03:44:47 +00:00
avg := float64 ( failed ) / float64 ( total ) * 100
2018-06-11 00:20:42 +00:00
avg = 100 - avg
if avg < 0 {
avg = 0
}
amount , _ := strconv . ParseFloat ( fmt . Sprintf ( "%0.2f" , avg ) , 10 )
s . Online24Hours = float32 ( amount )
return s . Online24Hours
2018-06-10 03:44:47 +00:00
}
2018-06-24 11:51:07 +00:00
type DateScan struct {
CreatedAt time . Time ` json:"x" `
2018-06-24 20:56:58 +00:00
Value int64 ` json:"y" `
2018-06-24 11:51:07 +00:00
}
2018-07-14 02:37:39 +00:00
func ( s * Service ) ToService ( ) * types . Service {
return s . S . ( * types . Service )
}
func NewService ( s * types . Service ) * Service {
ser := & Service { s }
return ser
}
func ( ser * Service ) SmallText ( ) string {
s := ser . ToService ( )
last := ser . LimitedFailures ( )
hits , _ := ser . LimitedHits ( )
2018-06-30 03:40:00 +00:00
if ! s . Online {
2018-06-30 03:48:55 +00:00
if len ( last ) > 0 {
2018-07-14 02:37:39 +00:00
lastFailure := MakeFailure ( last [ 0 ] )
return fmt . Sprintf ( "%v on %v" , lastFailure . ParseError ( ) , last [ 0 ] . CreatedAt . Format ( "Monday 3:04PM, Jan _2 2006" ) )
2018-06-30 03:48:55 +00:00
} else {
return fmt . Sprintf ( "%v is currently offline" , s . Name )
}
2018-06-30 03:40:00 +00:00
} else {
if len ( last ) == 0 {
return fmt . Sprintf ( "Online since %v" , s . CreatedAt . Format ( "Monday 3:04PM, Jan _2 2006" ) )
} else {
return fmt . Sprintf ( "Online, last failure was %v" , hits [ 0 ] . CreatedAt . Format ( "Monday 3:04PM, Jan _2 2006" ) )
}
}
return fmt . Sprintf ( "No Failures in the last 24 hours! %v" , hits [ 0 ] )
}
2018-07-04 09:00:16 +00:00
func GroupDataBy ( column string , id int64 , tm time . Time , increment string ) string {
var sql string
switch CoreApp . DbConnection {
case "mysql" :
sql = fmt . Sprintf ( "SELECT CONCAT(date_format(created_at, '%%Y-%%m-%%dT%%H:%%i:00Z')) AS created_at, AVG(latency)*1000 AS value FROM %v WHERE service=%v AND DATE_FORMAT(created_at, '%%Y-%%m-%%dT%%TZ') BETWEEN DATE_FORMAT('%v', '%%Y-%%m-%%dT%%TZ') AND DATE_FORMAT(NOW(), '%%Y-%%m-%%dT%%TZ') GROUP BY 1 ORDER BY created_at ASC;" , column , id , tm . Format ( time . RFC3339 ) )
case "sqlite" :
sql = fmt . Sprintf ( "SELECT strftime('%%Y-%%m-%%dT%%H:%%M:00Z', created_at), AVG(latency)*1000 as value FROM %v WHERE service=%v AND created_at >= '%v' GROUP BY strftime('%%M:00', created_at) ORDER BY created_at ASC;" , column , id , tm . Format ( time . RFC3339 ) )
case "postgres" :
sql = fmt . Sprintf ( "SELECT date_trunc('%v', created_at), AVG(latency)*1000 AS value FROM %v WHERE service=%v AND created_at >= '%v' GROUP BY 1 ORDER BY date_trunc ASC;" , increment , column , id , tm . Format ( time . RFC3339 ) )
}
return sql
}
2018-07-14 02:37:39 +00:00
func ( ser * Service ) GraphData ( ) string {
s := ser . ToService ( )
2018-07-03 21:39:56 +00:00
var d [ ] * DateScan
since := time . Now ( ) . Add ( time . Hour * - 24 + time . Minute * 0 + time . Second * 0 )
2018-07-04 09:00:16 +00:00
sql := GroupDataBy ( "hits" , s . Id , since , "minute" )
2018-06-30 00:57:05 +00:00
dated , err := DbSession . Query ( db . Raw ( sql ) )
2018-06-24 11:51:07 +00:00
if err != nil {
2018-06-30 00:57:05 +00:00
utils . Log ( 2 , err )
2018-06-25 06:21:18 +00:00
return ""
2018-06-10 01:31:13 +00:00
}
2018-06-24 11:51:07 +00:00
for dated . Next ( ) {
2018-07-03 21:39:56 +00:00
gd := new ( DateScan )
var tt string
2018-06-24 11:51:07 +00:00
var ff float64
2018-07-03 21:39:56 +00:00
err := dated . Scan ( & tt , & ff )
if err != nil {
utils . Log ( 2 , fmt . Sprintf ( "Issue loading chart data for service %v, %v" , s . Name , err ) )
}
gd . CreatedAt , err = time . Parse ( time . RFC3339 , tt )
if err != nil {
utils . Log ( 2 , fmt . Sprintf ( "Issue parsing time %v" , err ) )
}
2018-06-24 11:51:07 +00:00
gd . Value = int64 ( ff )
d = append ( d , gd )
}
2018-06-25 06:21:18 +00:00
data , err := json . Marshal ( d )
2018-06-24 11:51:07 +00:00
if err != nil {
2018-06-30 00:57:05 +00:00
utils . Log ( 2 , err )
2018-06-25 06:21:18 +00:00
return ""
2018-06-24 11:51:07 +00:00
}
2018-06-19 04:48:25 +00:00
return string ( data )
2018-06-10 01:31:13 +00:00
}
2018-07-14 02:37:39 +00:00
func ( ser * Service ) AvgUptime ( ) string {
s := ser . ToService ( )
failed , _ := ser . TotalFailures ( )
total , _ := ser . TotalHits ( )
2018-06-10 01:31:13 +00:00
if failed == 0 {
2018-06-22 04:02:57 +00:00
s . TotalUptime = "100"
2018-06-10 01:31:13 +00:00
return s . TotalUptime
}
2018-06-10 04:21:12 +00:00
if total == 0 {
2018-06-11 00:20:42 +00:00
s . TotalUptime = "0"
2018-06-10 04:21:12 +00:00
return s . TotalUptime
}
2018-06-10 01:31:13 +00:00
percent := float64 ( failed ) / float64 ( total ) * 100
2018-06-11 00:20:42 +00:00
percent = 100 - percent
if percent < 0 {
percent = 0
}
s . TotalUptime = fmt . Sprintf ( "%0.2f" , percent )
2018-06-22 04:02:57 +00:00
if s . TotalUptime == "100.00" {
s . TotalUptime = "100"
}
2018-06-11 00:20:42 +00:00
return s . TotalUptime
2018-06-10 01:31:13 +00:00
}
2018-07-14 02:37:39 +00:00
func RemoveArray ( u * types . Service ) [ ] * Service {
2018-06-23 00:10:37 +00:00
var srvcs [ ] * Service
2018-07-14 02:37:39 +00:00
for _ , ser := range CoreApp . Services {
s := ser . ToService ( )
2018-06-23 00:10:37 +00:00
if s . Id != u . Id {
2018-07-14 02:37:39 +00:00
srvcs = append ( srvcs , ser )
2018-06-23 00:10:37 +00:00
}
}
2018-06-30 00:57:05 +00:00
CoreApp . Services = srvcs
2018-06-23 00:10:37 +00:00
return srvcs
}
2018-07-14 02:37:39 +00:00
func DeleteService ( u * types . Service ) error {
2018-06-22 04:02:57 +00:00
res := serviceCol ( ) . Find ( "id" , u . Id )
2018-06-15 04:30:10 +00:00
err := res . Delete ( )
2018-06-30 00:57:05 +00:00
if err != nil {
utils . Log ( 3 , fmt . Sprintf ( "Failed to delete service %v. %v" , u . Name , err ) )
return err
}
2018-07-04 19:14:28 +00:00
utils . Log ( 1 , fmt . Sprintf ( "Stopping %v Monitoring..." , u . Name ) )
2018-07-14 02:37:39 +00:00
if u . StopRoutine != nil {
close ( u . StopRoutine )
2018-07-04 19:14:28 +00:00
}
utils . Log ( 1 , fmt . Sprintf ( "Stopped %v Monitoring Service" , u . Name ) )
2018-07-14 02:37:39 +00:00
RemoveArray ( u )
2018-06-19 04:48:25 +00:00
OnDeletedService ( u )
2018-06-15 04:30:10 +00:00
return err
2018-06-11 03:41:02 +00:00
}
2018-07-14 02:37:39 +00:00
func UpdateService ( u * types . Service ) * types . Service {
u . CreatedAt = time . Now ( )
2018-07-01 03:54:28 +00:00
res := serviceCol ( ) . Find ( "id" , u . Id )
2018-07-14 02:37:39 +00:00
err := res . Update ( u )
2018-07-01 03:54:28 +00:00
if err != nil {
utils . Log ( 3 , fmt . Sprintf ( "Failed to update service %v. %v" , u . Name , err ) )
}
2018-06-19 04:48:25 +00:00
OnUpdateService ( u )
2018-07-01 10:24:35 +00:00
return u
2018-06-11 03:41:02 +00:00
}
2018-07-14 02:37:39 +00:00
func CreateService ( u * types . Service ) ( int64 , error ) {
2018-06-15 04:30:10 +00:00
u . CreatedAt = time . Now ( )
2018-06-22 04:02:57 +00:00
uuid , err := serviceCol ( ) . Insert ( u )
2018-06-15 04:30:10 +00:00
if uuid == nil {
2018-06-30 00:57:05 +00:00
utils . Log ( 3 , fmt . Sprintf ( "Failed to create service %v. %v" , u . Name , err ) )
2018-06-15 04:30:10 +00:00
return 0 , err
2018-06-10 03:44:47 +00:00
}
2018-06-22 08:48:47 +00:00
u . Id = uuid . ( int64 )
2018-07-14 02:37:39 +00:00
u . StopRoutine = make ( chan struct { } )
nn := & Service { u }
CoreApp . Services = append ( CoreApp . Services , nn )
2018-06-15 04:30:10 +00:00
return uuid . ( int64 ) , err
2018-06-10 01:31:13 +00:00
}
2018-06-11 00:20:42 +00:00
func CountOnline ( ) int {
amount := 0
2018-07-14 02:37:39 +00:00
for _ , ser := range CoreApp . Services {
s := ser . ToService ( )
if s . Online {
2018-06-11 00:20:42 +00:00
amount ++
}
}
return amount
2018-06-25 07:09:31 +00:00
}