fixed postgres time issue - code cleanup - API timezone updates

pull/109/head v0.79.85
Hunter Long 2018-11-18 23:13:53 -08:00
parent aca967caab
commit f231d39369
24 changed files with 151 additions and 144 deletions

41
Gopkg.lock generated
View File

@ -1,14 +1,6 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
digest = "1:b62a3c5b37db602bf1158e921da1a762315a4c37855fd418a14498aa87a342d5"
name = "cloud.google.com/go"
packages = ["civil"]
pruneopts = "UT"
revision = "debcad1964693daf8ef4bc06292d7e828e075130"
version = "v0.31.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:65796e5fdb94d94bc0ee6bc422aa3541dbe69ed4da275cb5a27f8fa141f4e35c" digest = "1:65796e5fdb94d94bc0ee6bc422aa3541dbe69ed4da275cb5a27f8fa141f4e35c"
@ -45,31 +37,20 @@
version = "v1.1.1" version = "v1.1.1"
[[projects]] [[projects]]
branch = "master" digest = "1:3806f369b846160fcbde19bdcf93790868defe7c58d1bb6bc8d974c5b8f8dc1e"
digest = "1:0fd9da444782c2defb1352dc098f55b8b42c538787e29e45677a1dc40ff0ab11"
name = "github.com/denisenkom/go-mssqldb"
packages = [
".",
"internal/cp",
]
pruneopts = "UT"
revision = "4e0d7dc8888fbb59764060e99b7b68e77a6f9698"
[[projects]]
digest = "1:e37eb23cfd852df9c65b5dee28456595d7b12479421221a088b6ea7ad95a3570"
name = "github.com/go-mail/mail" name = "github.com/go-mail/mail"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "63235f23494bf20d713a585bce40b2a0675c2f77" revision = "f59b9b83a4e522098e3d3eb94e6f81850ad6e973"
version = "2.2.0" version = "v2.3.1"
[[projects]] [[projects]]
digest = "1:adea5a94903eb4384abef30f3d878dc9ff6b6b5b0722da25b82e5169216dfb61" digest = "1:ec6f9bf5e274c833c911923c9193867f3f18788c461f76f05f62bb1510e0ae65"
name = "github.com/go-sql-driver/mysql" name = "github.com/go-sql-driver/mysql"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "d523deb1b23d913de5bdada721a6071e71283618" revision = "72cd26f257d44c1114970e19afddcd812016007e"
version = "v1.4.0" version = "v1.4.1"
[[projects]] [[projects]]
digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202"
@ -112,11 +93,10 @@
version = "v1.1.3" version = "v1.1.3"
[[projects]] [[projects]]
digest = "1:4d28d632da146ec6e632bdd29676a95e0874f1ad837fff06aef6610b3b6b4728" digest = "1:8fe19266ce82209076d4a81007ff93f40dd349faca4a917aea59d33956bbd4fd"
name = "github.com/jinzhu/gorm" name = "github.com/jinzhu/gorm"
packages = [ packages = [
".", ".",
"dialects/mssql",
"dialects/mysql", "dialects/mysql",
"dialects/postgres", "dialects/postgres",
"dialects/sqlite", "dialects/sqlite",
@ -211,15 +191,14 @@
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:68344dbfaa4179bb50a583eb8172ace3f1edaf3aebc24e68c03f549f6e6b60dc" digest = "1:1ecf2a49df33be51e757d0033d5d51d5f784f35f68e5a38f797b2d3f03357d71"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
packages = [ packages = [
"bcrypt", "bcrypt",
"blowfish", "blowfish",
"md4",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "4d3f4d9ffa16a13f451c3b2999e9c49e9750bf06" revision = "3d3f9f413869b949e48070b5bc593aa22cc2b8f2"
[[projects]] [[projects]]
digest = "1:c25289f43ac4a68d88b02245742347c94f1e108c534dda442188015ff80669b3" digest = "1:c25289f43ac4a68d88b02245742347c94f1e108c534dda442188015ff80669b3"
@ -258,13 +237,13 @@
analyzer-version = 1 analyzer-version = 1
input-imports = [ input-imports = [
"github.com/GeertJohan/go.rice", "github.com/GeertJohan/go.rice",
"github.com/GeertJohan/go.rice/embedded",
"github.com/ararog/timeago", "github.com/ararog/timeago",
"github.com/go-mail/mail", "github.com/go-mail/mail",
"github.com/go-yaml/yaml", "github.com/go-yaml/yaml",
"github.com/gorilla/mux", "github.com/gorilla/mux",
"github.com/gorilla/sessions", "github.com/gorilla/sessions",
"github.com/jinzhu/gorm", "github.com/jinzhu/gorm",
"github.com/jinzhu/gorm/dialects/mssql",
"github.com/jinzhu/gorm/dialects/mysql", "github.com/jinzhu/gorm/dialects/mysql",
"github.com/jinzhu/gorm/dialects/postgres", "github.com/jinzhu/gorm/dialects/postgres",
"github.com/jinzhu/gorm/dialects/sqlite", "github.com/jinzhu/gorm/dialects/sqlite",

View File

@ -35,7 +35,7 @@
[[constraint]] [[constraint]]
name = "github.com/go-mail/mail" name = "github.com/go-mail/mail"
version = "2.2.0" version = "2.3.1"
[[constraint]] [[constraint]]
name = "github.com/go-yaml/yaml" name = "github.com/go-yaml/yaml"

View File

@ -1,4 +1,4 @@
VERSION=0.79.84 VERSION=0.79.85
BINARY_NAME=statup BINARY_NAME=statup
GOPATH:=$(GOPATH) GOPATH:=$(GOPATH)
GOCMD=go GOCMD=go

View File

@ -288,7 +288,7 @@ func RunSelectAllMysqlServices(t *testing.T) {
func RunSelectAllNotifiers(t *testing.T) { func RunSelectAllNotifiers(t *testing.T) {
var err error var err error
notifier.SetDB(core.DbSession) notifier.SetDB(core.DbSession, float32(-8))
core.CoreApp.Notifications = notifier.Load() core.CoreApp.Notifications = notifier.Load()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 8, len(core.CoreApp.Notifications)) assert.Equal(t, 8, len(core.CoreApp.Notifications))

View File

@ -238,7 +238,7 @@ func (s *Service) Check(record bool) {
// recordSuccess will create a new 'hit' record in the database for a successful/online service // recordSuccess will create a new 'hit' record in the database for a successful/online service
func recordSuccess(s *Service) { func recordSuccess(s *Service) {
s.Online = true s.Online = true
s.LastOnline = time.Now() s.LastOnline = utils.Timezoner(time.Now().UTC(), CoreApp.Timezone)
hit := &types.Hit{ hit := &types.Hit{
Service: s.Id, Service: s.Id,
Latency: s.Latency, Latency: s.Latency,

View File

@ -52,26 +52,32 @@ func LoadConfigFile(directory string) (*DbConfig, error) {
// LoadUsingEnv will attempt to load database configs based on environment variables. If DB_CONN is set if will force this function. // LoadUsingEnv will attempt to load database configs based on environment variables. If DB_CONN is set if will force this function.
func LoadUsingEnv() (*DbConfig, error) { func LoadUsingEnv() (*DbConfig, error) {
Configs = new(DbConfig) Configs = new(DbConfig)
Configs.LocalIP = GetLocalIP()
if os.Getenv("DB_CONN") == "" { if os.Getenv("DB_CONN") == "" {
return nil, errors.New("Missing DB_CONN environment variable") return Configs, errors.New("Missing DB_CONN environment variable")
} }
if os.Getenv("DB_CONN") != "sqlite" { if os.Getenv("DB_CONN") != "sqlite" {
if os.Getenv("DB_HOST") == "" { if os.Getenv("DB_HOST") == "" {
return nil, errors.New("Missing DB_HOST environment variable") return Configs, errors.New("Missing DB_HOST environment variable")
} }
if os.Getenv("DB_USER") == "" { if os.Getenv("DB_USER") == "" {
return nil, errors.New("Missing DB_USER environment variable") return Configs, errors.New("Missing DB_USER environment variable")
} }
if os.Getenv("DB_PASS") == "" { if os.Getenv("DB_PASS") == "" {
return nil, errors.New("Missing DB_PASS environment variable") return Configs, errors.New("Missing DB_PASS environment variable")
} }
if os.Getenv("DB_DATABASE") == "" { if os.Getenv("DB_DATABASE") == "" {
return nil, errors.New("Missing DB_DATABASE environment variable") return Configs, errors.New("Missing DB_DATABASE environment variable")
} }
} }
Configs = EnvToConfig() Configs = EnvToConfig()
CoreApp.Name = os.Getenv("NAME") CoreApp.Name = os.Getenv("NAME")
CoreApp.Domain = os.Getenv("DOMAIN") domain := os.Getenv("DOMAIN")
if domain == "" {
CoreApp.Domain = Configs.LocalIP
} else {
CoreApp.Domain = os.Getenv("DOMAIN")
}
CoreApp.DbConnection = Configs.DbConn CoreApp.DbConnection = Configs.DbConn
CoreApp.UseCdn = types.NewNullBool(os.Getenv("USE_CDN") == "true") CoreApp.UseCdn = types.NewNullBool(os.Getenv("USE_CDN") == "true")
@ -121,7 +127,10 @@ func DefaultPort(db string) int64 {
// EnvToConfig converts environment variables to a DbConfig variable // EnvToConfig converts environment variables to a DbConfig variable
func EnvToConfig() *DbConfig { func EnvToConfig() *DbConfig {
port := DefaultPort(os.Getenv("DB_PORT")) port := utils.StringInt(os.Getenv("DB_PORT"))
if port == 0 {
port = DefaultPort(os.Getenv("DB_PORT"))
}
name := os.Getenv("NAME") name := os.Getenv("NAME")
if name == "" { if name == "" {
name = "Statup" name = "Statup"

View File

@ -17,10 +17,12 @@ package core
import ( import (
"errors" "errors"
"fmt"
"github.com/hunterlong/statup/core/notifier" "github.com/hunterlong/statup/core/notifier"
"github.com/hunterlong/statup/source" "github.com/hunterlong/statup/source"
"github.com/hunterlong/statup/types" "github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils" "github.com/hunterlong/statup/utils"
"net"
"os" "os"
"time" "time"
) )
@ -74,7 +76,7 @@ func InsertNotifierDB() error {
return errors.New("database connection has not been created") return errors.New("database connection has not been created")
} }
} }
notifier.SetDB(DbSession) notifier.SetDB(DbSession, CoreApp.Timezone)
return nil return nil
} }
@ -151,6 +153,23 @@ func SelectCore() (*Core, error) {
return CoreApp, db.Error return CoreApp, db.Error
} }
// GetLocalIP returns the non loopback local IP of the host
func GetLocalIP() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return "http://localhost"
}
for _, address := range addrs {
// check the address type and if it is not a loopback the display it
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return fmt.Sprintf("http://%v", ipnet.IP.String())
}
}
}
return "http://localhost"
}
// ServiceOrder will reorder the services based on 'order_id' (Order) // ServiceOrder will reorder the services based on 'order_id' (Order)
type ServiceOrder []types.ServiceInterface type ServiceOrder []types.ServiceInterface

View File

@ -22,7 +22,6 @@ import (
"github.com/hunterlong/statup/types" "github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils" "github.com/hunterlong/statup/utils"
"github.com/jinzhu/gorm" "github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mssql"
_ "github.com/jinzhu/gorm/dialects/mysql" _ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/postgres" _ "github.com/jinzhu/gorm/dialects/postgres"
_ "github.com/jinzhu/gorm/dialects/sqlite" _ "github.com/jinzhu/gorm/dialects/sqlite"
@ -33,8 +32,13 @@ import (
var ( var (
// DbSession stores the Statup database session // DbSession stores the Statup database session
DbSession *gorm.DB DbSession *gorm.DB
DbModels []interface{}
) )
func init() {
DbModels = []interface{}{&types.Service{}, &types.User{}, &types.Hit{}, &types.Failure{}, &types.Message{}, &types.Checkin{}, &types.CheckinHit{}, &notifier.Notification{}}
}
// DbConfig stores the config.yml file for the statup configuration // DbConfig stores the config.yml file for the statup configuration
type DbConfig types.DbConfig type DbConfig types.DbConfig
@ -81,7 +85,12 @@ func checkinHitsDB() *gorm.DB {
// HitsBetween returns the gorm database query for a collection of service hits between a time range // 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) *gorm.DB { func (s *Service) HitsBetween(t1, t2 time.Time, group string, column string) *gorm.DB {
selector := Dbtimestamp(group, column) selector := Dbtimestamp(group, column)
return DbSession.Model(&types.Hit{}).Select(selector).Where("service = ? AND created_at BETWEEN ? AND ?", s.Id, t1.UTC().Format(types.TIME_DAY), t2.UTC().Format(types.TIME_DAY)) if Configs.DbConn == "postgres" {
timeQuery := fmt.Sprintf("service = %v AND created_at BETWEEN '%v.000000' AND '%v.000000'", s.Id, t1.UTC().Format(types.POSTGRES_TIME), t2.UTC().Format(types.POSTGRES_TIME))
return DbSession.Model(&types.Hit{}).Select(selector).Where(timeQuery)
} else {
return DbSession.Model(&types.Hit{}).Select(selector).Where("service = ? AND created_at BETWEEN ? AND ?", s.Id, t1.UTC().Format(types.TIME_DAY), t2.UTC().Format(types.TIME_DAY))
}
} }
// CloseDB will close the database connection if available // CloseDB will close the database connection if available
@ -96,6 +105,13 @@ func (db *DbConfig) Close() error {
return DbSession.DB().Close() return DbSession.DB().Close()
} }
// AfterFind for Core will set the timezone
func (c *Core) AfterFind() (err error) {
c.CreatedAt = utils.Timezoner(c.CreatedAt, CoreApp.Timezone)
c.UpdatedAt = utils.Timezoner(c.UpdatedAt, CoreApp.Timezone)
return
}
// AfterFind for Service will set the timezone // AfterFind for Service will set the timezone
func (s *Service) AfterFind() (err error) { func (s *Service) AfterFind() (err error) {
s.CreatedAt = utils.Timezoner(s.CreatedAt, CoreApp.Timezone) s.CreatedAt = utils.Timezoner(s.CreatedAt, CoreApp.Timezone)
@ -118,12 +134,14 @@ func (f *failure) AfterFind() (err error) {
// AfterFind for USer will set the timezone // AfterFind for USer will set the timezone
func (u *User) AfterFind() (err error) { func (u *User) AfterFind() (err error) {
u.CreatedAt = utils.Timezoner(u.CreatedAt, CoreApp.Timezone) u.CreatedAt = utils.Timezoner(u.CreatedAt, CoreApp.Timezone)
u.UpdatedAt = utils.Timezoner(u.UpdatedAt, CoreApp.Timezone)
return return
} }
// AfterFind for Checkin will set the timezone // AfterFind for Checkin will set the timezone
func (c *Checkin) AfterFind() (err error) { func (c *Checkin) AfterFind() (err error) {
c.CreatedAt = utils.Timezoner(c.CreatedAt, CoreApp.Timezone) c.CreatedAt = utils.Timezoner(c.CreatedAt, CoreApp.Timezone)
c.UpdatedAt = utils.Timezoner(c.UpdatedAt, CoreApp.Timezone)
return return
} }
@ -136,6 +154,9 @@ func (c *checkinHit) AfterFind() (err error) {
// AfterFind for Message will set the timezone // AfterFind for Message will set the timezone
func (u *Message) AfterFind() (err error) { func (u *Message) AfterFind() (err error) {
u.CreatedAt = utils.Timezoner(u.CreatedAt, CoreApp.Timezone) u.CreatedAt = utils.Timezoner(u.CreatedAt, CoreApp.Timezone)
u.UpdatedAt = utils.Timezoner(u.UpdatedAt, CoreApp.Timezone)
u.StartOn = utils.Timezoner(u.StartOn, CoreApp.Timezone)
u.EndOn = utils.Timezoner(u.EndOn, CoreApp.Timezone)
return return
} }
@ -159,6 +180,7 @@ func (f *failure) BeforeCreate() (err error) {
func (u *User) BeforeCreate() (err error) { func (u *User) BeforeCreate() (err error) {
if u.CreatedAt.IsZero() { if u.CreatedAt.IsZero() {
u.CreatedAt = time.Now().UTC() u.CreatedAt = time.Now().UTC()
u.UpdatedAt = time.Now().UTC()
} }
return return
} }
@ -167,6 +189,7 @@ func (u *User) BeforeCreate() (err error) {
func (u *Message) BeforeCreate() (err error) { func (u *Message) BeforeCreate() (err error) {
if u.CreatedAt.IsZero() { if u.CreatedAt.IsZero() {
u.CreatedAt = time.Now().UTC() u.CreatedAt = time.Now().UTC()
u.UpdatedAt = time.Now().UTC()
} }
return return
} }
@ -232,7 +255,7 @@ func (db *DbConfig) Connect(retry bool, location string) error {
host := fmt.Sprintf("%v:%v", Configs.DbHost, Configs.DbPort) host := fmt.Sprintf("%v:%v", Configs.DbHost, Configs.DbPort)
conn = fmt.Sprintf("%v:%v@tcp(%v)/%v?charset=utf8&parseTime=True&loc=UTC", Configs.DbUser, Configs.DbPass, host, Configs.DbData) conn = fmt.Sprintf("%v:%v@tcp(%v)/%v?charset=utf8&parseTime=True&loc=UTC", Configs.DbUser, Configs.DbPass, host, Configs.DbData)
case "postgres": case "postgres":
conn = fmt.Sprintf("host=%v port=%v user=%v dbname=%v password=%v sslmode=disable", Configs.DbHost, Configs.DbPort, Configs.DbUser, Configs.DbData, Configs.DbPass) conn = fmt.Sprintf("host=%v port=%v user=%v dbname=%v password=%v timezone=UTC sslmode=disable", Configs.DbHost, Configs.DbPort, Configs.DbUser, Configs.DbData, Configs.DbPass)
case "mssql": case "mssql":
host := fmt.Sprintf("%v:%v", Configs.DbHost, Configs.DbPort) host := fmt.Sprintf("%v:%v", Configs.DbHost, Configs.DbPort)
conn = fmt.Sprintf("sqlserver://%v:%v@%v?database=%v", Configs.DbUser, Configs.DbPass, host, Configs.DbData) conn = fmt.Sprintf("sqlserver://%v:%v@%v?database=%v", Configs.DbUser, Configs.DbPass, host, Configs.DbData)
@ -343,8 +366,8 @@ func (c *DbConfig) CreateCore() *Core {
// DropDatabase will DROP each table Statup created // DropDatabase will DROP each table Statup created
func (db *DbConfig) DropDatabase() error { func (db *DbConfig) DropDatabase() error {
utils.Log(1, "Dropping Database Tables...") utils.Log(1, "Dropping Database Tables...")
//err := DbSession.DropTableIfExists("checkins") err := DbSession.DropTableIfExists("checkins")
err := DbSession.DropTableIfExists("checkin_hits") err = DbSession.DropTableIfExists("checkin_hits")
err = DbSession.DropTableIfExists("notifications") err = DbSession.DropTableIfExists("notifications")
err = DbSession.DropTableIfExists("core") err = DbSession.DropTableIfExists("core")
err = DbSession.DropTableIfExists("failures") err = DbSession.DropTableIfExists("failures")
@ -357,18 +380,18 @@ func (db *DbConfig) DropDatabase() error {
// CreateDatabase will CREATE TABLES for each of the Statup elements // CreateDatabase will CREATE TABLES for each of the Statup elements
func (db *DbConfig) CreateDatabase() error { func (db *DbConfig) CreateDatabase() error {
var err error
utils.Log(1, "Creating Database Tables...") utils.Log(1, "Creating Database Tables...")
err := DbSession.CreateTable(&types.Checkin{}) for _, table := range DbModels {
err = DbSession.CreateTable(&types.CheckinHit{}) if err := DbSession.CreateTable(table); err.Error != nil {
err = DbSession.CreateTable(&notifier.Notification{}) return err.Error
err = DbSession.Table("core").CreateTable(&types.Core{}) }
err = DbSession.CreateTable(&types.Failure{}) }
err = DbSession.CreateTable(&types.Hit{}) if err := DbSession.Table("core").CreateTable(&types.Core{}); err.Error != nil {
err = DbSession.CreateTable(&types.Service{}) return err.Error
err = DbSession.CreateTable(&types.User{}) }
err = DbSession.CreateTable(&types.Message{})
utils.Log(1, "Statup Database Created") utils.Log(1, "Statup Database Created")
return err.Error return err
} }
// MigrateDatabase will migrate the database structure to current version. // MigrateDatabase will migrate the database structure to current version.
@ -385,8 +408,10 @@ func (db *DbConfig) MigrateDatabase() error {
if tx.Error != nil { if tx.Error != nil {
return tx.Error return tx.Error
} }
tx = tx.AutoMigrate(&types.Service{}, &types.User{}, &types.Hit{}, &types.Failure{}, &types.Message{}, &types.Checkin{}, &types.CheckinHit{}, &notifier.Notification{}).Table("core").AutoMigrate(&types.Core{}) for _, table := range DbModels {
if tx.Error != nil { tx = tx.AutoMigrate(table)
}
if err := tx.Table("core").AutoMigrate(&types.Core{}); err.Error != nil {
tx.Rollback() tx.Rollback()
utils.Log(3, fmt.Sprintf("Statup Database could not be migrated: %v", tx.Error)) utils.Log(3, fmt.Sprintf("Statup Database could not be migrated: %v", tx.Error))
return tx.Error return tx.Error

View File

@ -27,9 +27,6 @@ type Hit struct {
// CreateHit will create a new 'hit' record in the database for a successful/online service // CreateHit will create a new 'hit' record in the database for a successful/online service
func (s *Service) CreateHit(h *types.Hit) (int64, error) { func (s *Service) CreateHit(h *types.Hit) (int64, error) {
if h.CreatedAt.IsZero() {
h.CreatedAt = time.Now().UTC()
}
db := hitsDB().Create(&h) db := hitsDB().Create(&h)
if db.Error != nil { if db.Error != nil {
utils.Log(2, db.Error) utils.Log(2, db.Error)

View File

@ -31,7 +31,8 @@ var (
// AllCommunications holds all the loaded notifiers // AllCommunications holds all the loaded notifiers
AllCommunications []types.AllNotifiers AllCommunications []types.AllNotifiers
// db holds the Statup database connection // db holds the Statup database connection
db *gorm.DB db *gorm.DB
timezone float32
) )
// Notification contains all the fields for a Statup Notifier. // Notification contains all the fields for a Statup Notifier.
@ -89,6 +90,13 @@ type NotificationLog struct {
Timestamp time.Time `json:"timestamp"` Timestamp time.Time `json:"timestamp"`
} }
// AfterFind for Notification will set the timezone
func (n *Notification) AfterFind() (err error) {
n.CreatedAt = utils.Timezoner(n.CreatedAt, timezone)
n.UpdatedAt = utils.Timezoner(n.UpdatedAt, timezone)
return
}
// AddQueue will add any type of interface (json, string, struct, etc) into the Notifiers queue // AddQueue will add any type of interface (json, string, struct, etc) into the Notifiers queue
func (n *Notification) AddQueue(uid int64, msg interface{}) { func (n *Notification) AddQueue(uid int64, msg interface{}) {
data := &QueueData{uid, msg} data := &QueueData{uid, msg}
@ -106,8 +114,9 @@ func modelDb(n *Notification) *gorm.DB {
} }
// SetDB is called by core to inject the database for a notifier to use // SetDB is called by core to inject the database for a notifier to use
func SetDB(d *gorm.DB) { func SetDB(d *gorm.DB, zone float32) {
db = d db = d
timezone = zone
} }
// asNotification accepts a Notifier and returns a Notification struct // asNotification accepts a Notifier and returns a Notification struct
@ -243,6 +252,8 @@ func Init(n Notifier) (*Notification, error) {
var notify *Notification var notify *Notification
if err == nil { if err == nil {
notify, _ = SelectNotification(n) notify, _ = SelectNotification(n)
notify.CreatedAt = utils.Timezoner(notify.CreatedAt, timezone)
notify.UpdatedAt = utils.Timezoner(notify.UpdatedAt, timezone)
if notify.Delay.Seconds() == 0 { if notify.Delay.Seconds() == 0 {
notify.Delay = time.Duration(1 * time.Second) notify.Delay = time.Duration(1 * time.Second)
} }

View File

@ -219,12 +219,18 @@ func (s *Service) DowntimeText() string {
func Dbtimestamp(group string, column string) string { func Dbtimestamp(group string, column string) string {
var seconds int64 var seconds int64
switch group { switch group {
case "minute":
seconds = 60
case "hour": case "hour":
seconds = 3600 seconds = 3600
case "day": case "day":
seconds = 86400 seconds = 86400
case "week": case "week":
seconds = 604800 seconds = 604800
case "month":
seconds = 2592000
case "year":
seconds = 31557600
default: default:
seconds = 60 seconds = 60
} }
@ -271,14 +277,15 @@ func GraphDataRaw(service types.ServiceInterface, start, end time.Time, group st
var createdTime time.Time var createdTime time.Time
var err error var err error
rows.Scan(&createdAt, &value) rows.Scan(&createdAt, &value)
createdTime, _ = time.Parse(types.TIME, createdAt)
if CoreApp.DbConnection == "postgres" { if CoreApp.DbConnection == "postgres" {
createdTime, err = time.Parse(types.TIME_NANO, createdAt) createdTime, err = time.Parse(types.TIME_NANO, createdAt)
if err != nil { if err != nil {
utils.Log(4, fmt.Errorf("issue parsing time from database: %v to %v", createdAt, types.TIME_NANO)) utils.Log(4, fmt.Errorf("issue parsing time from database: %v to %v", createdAt, types.TIME_NANO))
} }
} else {
createdTime, err = time.Parse(types.TIME, createdAt)
} }
gd.CreatedAt = utils.Timezoner(createdTime, CoreApp.Timezone).Format(types.TIME) gd.CreatedAt = utils.Timezoner(createdTime, CoreApp.Timezone).Format(types.CHART_TIME)
gd.Value = int64(value * 1000) gd.Value = int64(value * 1000)
d = append(d, gd) d = append(d, gd)
} }

View File

@ -83,6 +83,9 @@ func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
} }
fields := parseGet(r) fields := parseGet(r)
grouping := fields.Get("group") grouping := fields.Get("group")
if grouping == "" {
grouping = "hour"
}
startField := utils.StringInt(fields.Get("start")) startField := utils.StringInt(fields.Get("start"))
endField := utils.StringInt(fields.Get("end")) endField := utils.StringInt(fields.Get("end"))

View File

@ -18,7 +18,6 @@ package handlers
import ( import (
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt"
"github.com/hunterlong/statup/core" "github.com/hunterlong/statup/core"
"github.com/hunterlong/statup/core/notifier" "github.com/hunterlong/statup/core/notifier"
"github.com/hunterlong/statup/source" "github.com/hunterlong/statup/source"
@ -30,7 +29,6 @@ import (
) )
func dashboardHandler(w http.ResponseWriter, r *http.Request) { func dashboardHandler(w http.ResponseWriter, r *http.Request) {
fmt.Println()
if !IsAuthenticated(r) { if !IsAuthenticated(r) {
err := core.ErrorResponse{} err := core.ErrorResponse{}
executeResponse(w, r, "login.html", err, nil) executeResponse(w, r, "login.html", err, nil)

View File

@ -94,10 +94,10 @@ func servicesViewHandler(w http.ResponseWriter, r *http.Request) {
start := end.Add((-24 * 7) * time.Hour).UTC() start := end.Add((-24 * 7) * time.Hour).UTC()
if startField != 0 { if startField != 0 {
start = time.Unix(startField, 0) start = time.Unix(startField, 0).UTC()
} }
if endField != 0 { if endField != 0 {
end = time.Unix(endField, 0) end = time.Unix(endField, 0).UTC()
} }
if group == "" { if group == "" {
group = "hour" group = "hour"
@ -106,11 +106,13 @@ func servicesViewHandler(w http.ResponseWriter, r *http.Request) {
data := core.GraphDataRaw(serv, start, end, group, "latency") data := core.GraphDataRaw(serv, start, end, group, "latency")
out := struct { out := struct {
Service *core.Service Service *core.Service
Start string Start string
End string End string
Data string StartUnix int64
}{serv, start.Format(utils.FlatpickrReadable), end.Format(utils.FlatpickrReadable), data.ToString()} EndUnix int64
Data string
}{serv, start.Format(utils.FlatpickrReadable), end.Format(utils.FlatpickrReadable), start.Unix(), end.Unix(), data.ToString()}
executeResponse(w, r, "service.html", out, nil) executeResponse(w, r, "service.html", out, nil)
} }

View File

@ -29,11 +29,11 @@ func setupHandler(w http.ResponseWriter, r *http.Request) {
http.Redirect(w, r, "/", http.StatusSeeOther) http.Redirect(w, r, "/", http.StatusSeeOther)
return return
} }
w.WriteHeader(http.StatusOK)
var data interface{} var data interface{}
if os.Getenv("DB_CONN") != "" { if os.Getenv("DB_CONN") != "" {
data, _ = core.LoadUsingEnv() data, _ = core.LoadUsingEnv()
} }
w.WriteHeader(http.StatusOK)
executeResponse(w, r, "setup.html", data, nil) executeResponse(w, r, "setup.html", data, nil)
} }

View File

@ -79,5 +79,5 @@ func injectDatabase() {
panic(err) panic(err)
} }
db.CreateTable(&notifier.Notification{}) db.CreateTable(&notifier.Notification{})
notifier.SetDB(db) notifier.SetDB(db, float32(-8))
} }

View File

@ -16,7 +16,10 @@
*/ */
var currentLocation = window.location; var currentLocation = window.location;
$("#domain_input").val(currentLocation.origin); var domain = $("#domain_input");
if (domain.val() === "") {
domain.val(currentLocation.origin);
}
$('select#database_type').on('change', function(){ $('select#database_type').on('change', function(){
var selected = $('#database_type option:selected').val(); var selected = $('#database_type option:selected').val();

View File

@ -21,10 +21,8 @@ import (
"github.com/GeertJohan/go.rice" "github.com/GeertJohan/go.rice"
"github.com/hunterlong/statup/utils" "github.com/hunterlong/statup/utils"
"gopkg.in/russross/blackfriday.v2" "gopkg.in/russross/blackfriday.v2"
"io"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec"
) )
var ( var (
@ -68,31 +66,9 @@ func CompileSASS(folder string) error {
utils.Log(1, fmt.Sprintf("Compiling SASS %v into %v", scssFile, baseFile)) utils.Log(1, fmt.Sprintf("Compiling SASS %v into %v", scssFile, baseFile))
command := fmt.Sprintf("%v %v %v", sassBin, scssFile, baseFile) command := fmt.Sprintf("%v %v %v", sassBin, scssFile, baseFile)
utils.Log(1, fmt.Sprintf("Command: sh -c %v", command)) stdout, stderr, err := utils.Command(command)
testCmd := exec.Command("sh", "-c", command) if stdout != "" || stderr != "" {
var stdout, stderr []byte
var errStdout, errStderr error
stdoutIn, _ := testCmd.StdoutPipe()
stderrIn, _ := testCmd.StderrPipe()
testCmd.Start()
go func() {
stdout, errStdout = copyAndCapture(os.Stdout, stdoutIn)
}()
go func() {
stderr, errStderr = copyAndCapture(os.Stderr, stderrIn)
}()
err := testCmd.Wait()
if err != nil {
utils.Log(3, err)
return err
}
if errStdout != nil || errStderr != nil {
utils.Log(3, fmt.Sprintf("Failed to compile assets with SASS %v", err)) utils.Log(3, fmt.Sprintf("Failed to compile assets with SASS %v", err))
return errors.New("failed to capture stdout or stderr") return errors.New("failed to capture stdout or stderr")
} }
@ -103,8 +79,7 @@ func CompileSASS(folder string) error {
return err return err
} }
outStr, errStr := string(stdout), string(stderr) utils.Log(1, fmt.Sprintf("out: %v | error: %v", stdout, stderr))
utils.Log(1, fmt.Sprintf("out: %v | error: %v", outStr, errStr))
utils.Log(1, "SASS Compiling is complete!") utils.Log(1, "SASS Compiling is complete!")
return nil return nil
} }
@ -239,27 +214,3 @@ func MakePublicFolder(folder string) error {
} }
return nil return nil
} }
// copyAndCapture captures the response from a terminal command
func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
var out []byte
buf := make([]byte, 1024, 1024)
for {
n, err := r.Read(buf[:])
if n > 0 {
d := buf[:n]
out = append(out, d...)
_, err := w.Write(d)
if err != nil {
return out, err
}
}
if err != nil {
// Read returns io.EOF at the end of file, which is not an error for us
if err == io.EOF {
err = nil
}
return out, err
}
}
}

View File

@ -225,7 +225,7 @@ $(document).ready(function() {
} }
}); });
AjaxChart(chartdata,{{$s.Id}},{{.Start}},{{.End}},"hour"); AjaxChart(chartdata,{{$s.Id}},{{.StartUnix}},{{.EndUnix}},"hour");
let startDate = $("#service_start").flatpickr({ let startDate = $("#service_start").flatpickr({
enableTime: false, enableTime: false,

View File

@ -20,9 +20,11 @@ import (
) )
const ( const (
TIME_NANO = "2006-01-02T15:04:05Z" TIME_NANO = "2006-01-02T15:04:05Z"
TIME = "2006-01-02 15:04:05" TIME = "2006-01-02 15:04:05"
TIME_DAY = "2006-01-02" POSTGRES_TIME = "2006-01-02 15:04"
CHART_TIME = "2006-01-02T15:04:05.999999-07:00"
TIME_DAY = "2006-01-02"
) )
var ( var (

View File

@ -46,4 +46,5 @@ type DbConfig struct {
Email string `yaml:"-"` Email string `yaml:"-"`
Error error `yaml:"-"` Error error `yaml:"-"`
Location string `yaml:"location"` Location string `yaml:"location"`
LocalIP string `yaml:"-"`
} }

View File

@ -26,6 +26,14 @@ const (
FlatpickrReadable = "Mon, 02 Jan 2006" FlatpickrReadable = "Mon, 02 Jan 2006"
) )
// Timezoner returns the time.Time with the user set timezone
func Timezoner(t time.Time, zone float32) time.Time {
zoneInt := float32(3600) * zone
loc := time.FixedZone("", int(zoneInt))
timez := t.In(loc)
return timez
}
// FormatDuration converts a time.Duration into a string // FormatDuration converts a time.Duration into a string
func FormatDuration(d time.Duration) string { func FormatDuration(d time.Duration) string {
var out string var out string

View File

@ -68,14 +68,6 @@ func ToString(s interface{}) string {
} }
} }
// Timezoner returns the time.Time with the user set timezone
func Timezoner(t time.Time, zone float32) time.Time {
zoneInt := float32(3600) * (zone + 1)
loc := time.FixedZone("", int(zoneInt))
timez := t.In(loc)
return timez
}
// dir returns the current working directory // dir returns the current working directory
func dir() string { func dir() string {
dir, err := os.Getwd() dir, err := os.Getwd()

View File

@ -125,7 +125,7 @@ func TestTimezone(t *testing.T) {
timestamp := time.Date(2018, 1, 1, 10, 0, 0, 0, loc) timestamp := time.Date(2018, 1, 1, 10, 0, 0, 0, loc)
timezone := Timezoner(timestamp, zone) timezone := Timezoner(timestamp, zone)
assert.Equal(t, "2018-01-01 10:00:00 -0800 PST", timestamp.String()) assert.Equal(t, "2018-01-01 10:00:00 -0800 PST", timestamp.String())
assert.Equal(t, "2018-01-01 15:00:00 -0300 -0300", timezone.String()) assert.Equal(t, "2018-01-01 18:00:00 +0000 UTC", timezone.UTC().String())
} }
func TestTimestamp_Ago(t *testing.T) { func TestTimestamp_Ago(t *testing.T) {