diff --git a/Makefile b/Makefile index 203d1bf6..6096ee5b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION=0.53 +VERSION=0.54 BINARY_NAME=statup GOPATH:=$(GOPATH) GOCMD=go diff --git a/cmd/cli.go b/cmd/cli.go index 66d020f1..da5ba6c4 100644 --- a/cmd/cli.go +++ b/cmd/cli.go @@ -29,7 +29,6 @@ import ( "io/ioutil" "math/rand" "net/http" - "strings" "time" ) @@ -240,14 +239,6 @@ func FakeSeed(plug types.PluginActions) { if err != nil { utils.Log(3, err) } - up, _ := source.SqlBox.String("sqlite_up.sql") - requests := strings.Split(up, ";") - for _, request := range requests { - db := core.DbSession.Exec(request) - if db.Error != nil { - utils.Log(2, db.Error) - } - } fmt.Println("Finished creating Test SQLite database") fmt.Println("Inserting example services into test database...") diff --git a/cmd/main.go b/cmd/main.go index c980f938..bea47f84 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -101,7 +101,7 @@ func mainProcess() { if err != nil { utils.Log(4, fmt.Sprintf("could not connect to database: %v", err)) } - core.RunDatabaseUpgrades() + core.Configs.MigrateDatabase() core.InitApp() if !core.SetupMode { LoadPlugins(false) diff --git a/cmd/main_test.go b/cmd/main_test.go index 480b8923..b449c9f4 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -101,7 +101,6 @@ func TestRunAll(t *testing.T) { assert.Nil(t, err) }) t.Run(dbt+" Run Database Migrations", func(t *testing.T) { - t.SkipNow() RunDatabaseMigrations(t, dbt) }) t.Run(dbt+" Select Core", func(t *testing.T) { @@ -251,13 +250,8 @@ func RunCreateSchema(t *testing.T, db string) { assert.Nil(t, err) } -func RunConnectDatabase(t *testing.T) { - err := core.Configs.Connect(false, dir) - assert.Nil(t, err) -} - func RunDatabaseMigrations(t *testing.T, db string) { - err := core.RunDatabaseUpgrades() + err := core.Configs.MigrateDatabase() assert.Nil(t, err) } @@ -493,7 +487,7 @@ func RunService_Failures(t *testing.T) { service := core.SelectService(18) assert.NotNil(t, service) assert.Equal(t, "Failing URL", service.Name) - assert.NotEmpty(t, service.Failures) + assert.NotEmpty(t, service.AllFailures()) } func RunService_LimitedHits(t *testing.T) { diff --git a/core/checkin.go b/core/checkin.go index a51d9390..e2dfe1e8 100644 --- a/core/checkin.go +++ b/core/checkin.go @@ -48,15 +48,15 @@ func FindCheckin(api string) *types.Checkin { func (s *Service) AllCheckins() []*types.Checkin { var checkins []*types.Checkin - col := checkinDB().Where("service = ?", s.Id).Order("-id") - col.Scan(&checkins) + col := checkinDB().Where("service = ?", s.Id).Order("id desc") + col.Find(&checkins) s.Checkins = checkins return checkins } func (u *Checkin) Create() (int64, error) { u.CreatedAt = time.Now() - row := checkinDB().Create(&u) + row := checkinDB().Create(u) if row.Error == nil { utils.Log(2, row.Error) return 0, row.Error diff --git a/core/core.go b/core/core.go index e877d423..2d5a50d0 100644 --- a/core/core.go +++ b/core/core.go @@ -22,6 +22,7 @@ import ( "github.com/hunterlong/statup/utils" "github.com/pkg/errors" "os" + "sort" "time" ) @@ -33,10 +34,10 @@ type Core struct { } var ( - Configs *DbConfig - CoreApp *Core - SetupMode bool - VERSION string + Configs *DbConfig // Configs holds all of the config.yml and database info + CoreApp *Core // CoreApp is a global variable that contains many elements + SetupMode bool // SetupMode will be true if Statup does not have a database connection + VERSION string // VERSION is set on build automatically by setting a -ldflag ) func init() { @@ -74,15 +75,18 @@ func InsertNotifierDB() error { return nil } +// UpdateCore will update the CoreApp variable inside of the 'core' table in database func UpdateCore(c *Core) (*Core, error) { - db := coreDB().Update(c) + db := coreDB().Update(&c) return c, db.Error } +// UsingAssets will return true if /assets folder is present func (c Core) UsingAssets() bool { return source.UsingAssets(utils.Directory) } +// SassVars opens the file /assets/scss/variables.scss to be edited in Theme func (c Core) SassVars() string { if !source.UsingAssets(utils.Directory) { return "" @@ -90,6 +94,7 @@ func (c Core) SassVars() string { return source.OpenAsset(utils.Directory, "scss/variables.scss") } +// BaseSASS is the base design , this opens the file /assets/scss/base.scss to be edited in Theme func (c Core) BaseSASS() string { if !source.UsingAssets(utils.Directory) { return "" @@ -97,6 +102,8 @@ func (c Core) BaseSASS() string { return source.OpenAsset(utils.Directory, "scss/base.scss") } +// MobileSASS is the -webkit responsive custom css designs. This opens the +// file /assets/scss/mobile.scss to be edited in Theme func (c Core) MobileSASS() string { if !source.UsingAssets(utils.Directory) { return "" @@ -104,6 +111,7 @@ func (c Core) MobileSASS() string { return source.OpenAsset(utils.Directory, "scss/mobile.scss") } +// AllOnline will be true if all services are online func (c Core) AllOnline() bool { for _, s := range CoreApp.Services() { if !s.Online { @@ -113,14 +121,7 @@ func (c Core) AllOnline() bool { return true } -func SelectLastMigration() (int64, error) { - if DbSession == nil { - return 0, errors.New("Database connection has not been created yet") - } - row := coreDB().Take(&CoreApp) - return CoreApp.MigrationId, row.Error -} - +// SelectCore will return the CoreApp global variable and the settings/configs for Statup func SelectCore() (*Core, error) { if DbSession == nil { return nil, errors.New("database has not been initiated yet.") @@ -129,7 +130,7 @@ func SelectCore() (*Core, error) { if !exists { return nil, errors.New("core database has not been setup yet.") } - db := coreDB().Take(&CoreApp) + db := coreDB().First(&CoreApp) if db.Error != nil { return nil, db.Error } @@ -143,16 +144,19 @@ func SelectCore() (*Core, error) { return CoreApp, db.Error } +// ServiceOrder will reorder the services based on 'order_id' (Order) type ServiceOrder []*types.Service func (c ServiceOrder) Len() int { return len(c) } func (c ServiceOrder) Swap(i, j int) { c[i], c[j] = c[j], c[i] } func (c ServiceOrder) Less(i, j int) bool { return c[i].Order < c[j].Order } +// Services returns each Service that is attached to this instance func (c *Core) Services() []*Service { var services []*Service servs := CoreApp.GetServices() - //sort.Sort(ServiceOrder(servs)) + sort.Sort(ServiceOrder(servs)) + CoreApp.SetServices(servs) for _, ser := range servs { services = append(services, ReturnService(ser)) } diff --git a/core/core_test.go b/core/core_test.go index 9725a84a..66f4df9f 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -70,8 +70,18 @@ func TestDbConnection(t *testing.T) { assert.Nil(t, err) } +func TestDropDatabase(t *testing.T) { + err := Configs.DropDatabase() + assert.Nil(t, err) +} + func TestSeedSchemaDatabase(t *testing.T) { - _, _, err := Configs.SeedSchema() + err := Configs.CreateDatabase() + assert.Nil(t, err) +} + +func TestMigrateDatabase(t *testing.T) { + err := Configs.MigrateDatabase() assert.Nil(t, err) } @@ -92,13 +102,6 @@ func TestSelectCore(t *testing.T) { assert.Equal(t, "Awesome Status", core.Name) } -func TestSelectLastMigration(t *testing.T) { - id, err := SelectLastMigration() - assert.Nil(t, err) - //assert.NotZero(t, id) - t.Log("Last migration id: ", id) -} - func TestInsertNotifierDB(t *testing.T) { err := InsertNotifierDB() assert.Nil(t, err) diff --git a/core/database.go b/core/database.go index 63c26209..89a5ae04 100644 --- a/core/database.go +++ b/core/database.go @@ -17,10 +17,8 @@ package core import ( "fmt" - _ "github.com/go-sql-driver/mysql" "github.com/go-yaml/yaml" "github.com/hunterlong/statup/notifiers" - "github.com/hunterlong/statup/source" "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" "github.com/jinzhu/gorm" @@ -28,23 +26,20 @@ import ( _ "github.com/jinzhu/gorm/dialects/mysql" _ "github.com/jinzhu/gorm/dialects/postgres" _ "github.com/jinzhu/gorm/dialects/sqlite" - _ "github.com/lib/pq" - _ "github.com/mattn/go-sqlite3" "os" - "strings" "time" ) var ( - DbSession *gorm.DB - currentMigration int64 + DbSession *gorm.DB ) func failuresDB() *gorm.DB { - if os.Getenv("GO_ENV") == "TEST" { - return DbSession.Model(&types.Failure{}).Debug() + db := DbSession.Model(&types.Failure{}) + if os.Getenv("GO_ENV") == "test" { + return db.Debug() } - return DbSession.Model(&types.Failure{}) + return db } func (s *Service) allHits() *gorm.DB { @@ -53,42 +48,47 @@ func (s *Service) allHits() *gorm.DB { } func hitsDB() *gorm.DB { - if os.Getenv("GO_ENV") == "TEST" { - return DbSession.Model(&types.Hit{}).Debug() + db := DbSession.Model(&types.Hit{}) + if os.Getenv("GO_ENV") == "test" { + return db.Debug() } - return DbSession.Model(&types.Hit{}) + return db } func servicesDB() *gorm.DB { - if os.Getenv("GO_ENV") == "TEST" { - return DbSession.Model(&types.Service{}).Debug() + db := DbSession.Model(&types.Service{}) + if os.Getenv("GO_ENV") == "test" { + return db.Debug() } - return DbSession.Model(&types.Service{}) + return db } func coreDB() *gorm.DB { - if os.Getenv("GO_ENV") == "TEST" { - return DbSession.Table("core").Debug() + db := DbSession.Table("core").Model(&CoreApp) + if os.Getenv("GO_ENV") == "test" { + return db.Debug() } - return DbSession.Table("core") + return db } func usersDB() *gorm.DB { - if os.Getenv("GO_ENV") == "TEST" { - return DbSession.Model(&types.User{}).Debug() + db := DbSession.Model(&types.User{}) + if os.Getenv("GO_ENV") == "test" { + return db.Debug() } - return DbSession.Model(&types.User{}) + return db } func commDB() *gorm.DB { - if os.Getenv("GO_ENV") == "TEST" { - return DbSession.Table("communication").Model(¬ifiers.Notification{}).Debug() + db := DbSession.Table("communication").Model(¬ifiers.Notification{}) + if os.Getenv("GO_ENV") == "test" { + return db.Debug() } - return DbSession.Table("communication").Model(¬ifiers.Notification{}) + return db } func checkinDB() *gorm.DB { - if os.Getenv("GO_ENV") == "TEST" { + if os.Getenv("GO_ENV") == "test" { return DbSession.Model(&types.Checkin{}).Debug() } return DbSession.Model(&types.Checkin{}) @@ -98,10 +98,12 @@ type DbConfig struct { *types.DbConfig } +// Close shutsdown the database connection func (db *DbConfig) Close() error { - return DbSession.Close() + return DbSession.DB().Close() } +// InsertCore create the single row for the Core settings in Statup func (db *DbConfig) InsertCore() (*Core, error) { CoreApp = &Core{Core: &types.Core{ Name: db.Project, @@ -117,64 +119,44 @@ func (db *DbConfig) InsertCore() (*Core, error) { return CoreApp, query.Error } +// Connect will attempt to connect to the sqlite, postgres, or mysql database func (db *DbConfig) Connect(retry bool, location string) error { var err error if DbSession != nil { DbSession = nil } - switch Configs.DbConn { + var conn, dbType string + dbType = Configs.DbConn + switch dbType { case "sqlite": - DbSession, err = gorm.Open("sqlite3", utils.Directory+"/statup.db") - if err != nil { - return err - } + conn = utils.Directory + "/statup.db" + dbType = "sqlite3" case "mysql": if Configs.DbPort == 0 { Configs.DbPort = 3306 } host := fmt.Sprintf("%v:%v", Configs.DbHost, Configs.DbPort) - conn := fmt.Sprintf("%v:%v@tcp(%v)/%v?charset=utf8&parseTime=True&loc=Local", Configs.DbUser, Configs.DbPass, host, Configs.DbData) - DbSession, err = gorm.Open("mysql", conn) - DbSession.DB().SetConnMaxLifetime(time.Minute * 5) - DbSession.DB().SetMaxIdleConns(0) - DbSession.DB().SetMaxOpenConns(5) - if err != nil { - if retry { - utils.Log(1, fmt.Sprintf("Database connection to '%v' is not available, trying again in 5 seconds...", host)) - return db.waitForDb() - } else { - return err - } - } + conn = fmt.Sprintf("%v:%v@tcp(%v)/%v?charset=utf8&parseTime=True&loc=Local", Configs.DbUser, Configs.DbPass, host, Configs.DbData) case "postgres": if Configs.DbPort == 0 { Configs.DbPort = 5432 } - 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) - DbSession, err = gorm.Open("postgres", conn) - if err != nil { - if retry { - utils.Log(1, fmt.Sprintf("Database connection to '%v' is not available, trying again in 5 seconds...", Configs.DbHost)) - return db.waitForDb() - } else { - fmt.Println("ERROR:", err) - return err - } - } + 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) case "mssql": if Configs.DbPort == 0 { Configs.DbPort = 1433 } 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) - DbSession, err = gorm.Open("mssql", conn) - if err != nil { - if retry { - utils.Log(1, fmt.Sprintf("Database connection to '%v' is not available, trying again in 5 seconds...", host)) - return db.waitForDb() - } else { - return err - } + conn = fmt.Sprintf("sqlserver://%v:%v@%v?database=%v", Configs.DbUser, Configs.DbPass, host, Configs.DbData) + } + DbSession, err = gorm.Open(dbType, conn) + if err != nil { + if retry { + utils.Log(1, fmt.Sprintf("Database connection to '%v' is not available, trying again in 5 seconds...", Configs.DbHost)) + return db.waitForDb() + } else { + fmt.Println("ERROR:", err) + return err } } err = DbSession.DB().Ping() @@ -189,6 +171,8 @@ func (db *DbConfig) waitForDb() error { return db.Connect(true, utils.Directory) } +// DatabaseMaintence will automatically delete old records from 'failures' and 'hits' +// this function is currently set to delete records 7+ days old every 60 minutes func DatabaseMaintence() { for range time.Tick(60 * time.Minute) { utils.Log(1, "Checking for database records older than 7 days...") @@ -198,6 +182,7 @@ func DatabaseMaintence() { } } +// DeleteAllSince will delete a specific table's records based on a time. func DeleteAllSince(table string, date time.Time) { sql := fmt.Sprintf("DELETE FROM %v WHERE created_at < '%v';", table, date.Format("2006-01-02")) db := DbSession.Raw(sql) @@ -207,6 +192,7 @@ func DeleteAllSince(table string, date time.Time) { } } +// Update will save the config.yml file func (c *DbConfig) Update() error { var err error config, err := os.Create(utils.Directory + "/config.yml") @@ -224,6 +210,7 @@ func (c *DbConfig) Update() error { return err } +// Save will initially create the config.yml file func (c *DbConfig) Save() (*DbConfig, error) { var err error config, err := os.Create(utils.Directory + "/config.yml") @@ -243,6 +230,7 @@ func (c *DbConfig) Save() (*DbConfig, error) { return c, err } +// CreateCore will initialize the global variable 'CoreApp". This global variable contains most of Statup app. func (c *DbConfig) CreateCore() *Core { newCore := &types.Core{ Name: c.Project, @@ -264,91 +252,7 @@ func (c *DbConfig) CreateCore() *Core { return CoreApp } -func versionHigher(migrate int64) bool { - if CoreApp.MigrationId < migrate { - return true - } - return false -} - -func reverseSlice(s []string) []string { - for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] - } - return s -} - -func RunDatabaseUpgrades() error { - var err error - currentMigration, err = SelectLastMigration() - if err != nil { - return err - } - utils.Log(1, fmt.Sprintf("Checking for Database Upgrades since #%v", currentMigration)) - upgrade, _ := source.SqlBox.String(CoreApp.DbConnection + "_upgrade.sql") - // parse db version and upgrade file - ups := strings.Split(upgrade, "=========================================== ") - ups = reverseSlice(ups) - var ran int - var lastMigration int64 - for _, v := range ups { - if len(v) == 0 { - continue - } - vers := strings.Split(v, "\n") - lastMigration = utils.StringInt(vers[0]) - data := vers[1:] - - //fmt.Printf("Checking Migration from v%v to v%v - %v\n", CoreApp.Version, version, versionHigher(version)) - if currentMigration >= lastMigration { - continue - } - utils.Log(1, fmt.Sprintf("Migrating Database from #%v to #%v", currentMigration, lastMigration)) - for _, m := range data { - if m == "" { - continue - } - utils.Log(1, fmt.Sprintf("Running Query: %v", m)) - db := DbSession.Raw(m) - ran++ - if db.Error != nil { - utils.Log(2, db.Error) - continue - } - } - currentMigration = lastMigration - } - if ran > 0 { - utils.Log(1, fmt.Sprintf("Database Upgraded %v queries ran, current #%v", ran, currentMigration)) - CoreApp, err = SelectCore() - if err != nil { - return err - } - CoreApp.MigrationId = currentMigration - UpdateCore(CoreApp) - } - return err -} - -func (db *DbConfig) SeedSchema() (string, string, error) { - utils.Log(1, "Seeding Schema Database with Dummy Data...") - dir := utils.Directory - var cmd string - switch db.DbConn { - case "sqlite": - cmd = fmt.Sprintf("cat %v/source/sql/sqlite_up.sql | sqlite3 %v/statup.db", dir, dir) - case "mysql": - cmd = fmt.Sprintf("mysql -h %v -P %v -u %v --password=%v %v < %v/source/sql/mysql_up.sql", Configs.DbHost, Configs.DbPort, Configs.DbUser, Configs.DbPass, Configs.DbData, dir) - case "postgres": - cmd = fmt.Sprintf("PGPASSWORD=%v psql -U %v -h %v -d %v -1 -f %v/source/sql/postgres_up.sql", db.DbPass, db.DbUser, db.DbHost, db.DbData, dir) - } - out, outErr, err := utils.Command(cmd) - if err != nil { - return out, outErr, err - } - return out, outErr, err -} - +// SeedDatabase will insert many elements into the database. This is only ran in Dev/Test move func (db *DbConfig) SeedDatabase() (string, string, error) { utils.Log(1, "Seeding Database with Dummy Data...") dir := utils.Directory @@ -365,6 +269,7 @@ func (db *DbConfig) SeedDatabase() (string, string, error) { return out, outErr, err } +// DropDatabase will DROP each table Statup created func (db *DbConfig) DropDatabase() error { utils.Log(1, "Dropping Database Tables...") err := DbSession.DropTableIfExists("checkins") @@ -377,6 +282,7 @@ func (db *DbConfig) DropDatabase() error { return err.Error } +// CreateDatabase will CREATE TABLES for each of the Statup elements func (db *DbConfig) CreateDatabase() error { utils.Log(1, "Creating Database Tables...") err := DbSession.CreateTable(&types.Checkin{}) @@ -390,17 +296,29 @@ func (db *DbConfig) CreateDatabase() error { return err.Error } +// MigrateDatabase will migrate the database structure to current version. +// This function will NOT remove previous records, tables or columns from the database. +// If this function has an issue, it will ROLLBACK to the previous state. func (db *DbConfig) MigrateDatabase() error { utils.Log(1, "Migrating Database Tables...") - err := DbSession.AutoMigrate(&types.Checkin{}) - err = DbSession.Table("communication").AutoMigrate(¬ifiers.Notification{}) - err = DbSession.Table("core").AutoMigrate(&types.Core{}) - err = DbSession.AutoMigrate(&types.Failure{}) - err = DbSession.AutoMigrate(&types.Hit{}) - err = DbSession.AutoMigrate(&types.Service{}) - err = DbSession.AutoMigrate(&types.User{}) + + tx := DbSession.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() + } + }() + if tx.Error != nil { + return tx.Error + } + tx = tx.AutoMigrate(&types.Service{}, &types.User{}, &types.Hit{}, &types.Failure{}, &types.Checkin{}).Table("core").AutoMigrate(&types.Core{}).Table("communication").AutoMigrate(¬ifiers.Notification{}) + if tx.Error != nil { + tx.Rollback() + utils.Log(3, fmt.Sprintf("Statup Database could not be migrated: %v", tx.Error)) + return tx.Error + } utils.Log(1, "Statup Database Migrated") - return err.Error + return tx.Commit().Error } func (c *DbConfig) Clean() *DbConfig { diff --git a/core/failures.go b/core/failures.go index d9e1b9ed..7c5437ad 100644 --- a/core/failures.go +++ b/core/failures.go @@ -66,13 +66,6 @@ func (s *Service) LimitedFailures() []*Failure { return failArr } -func reverseFailures(input []*Failure) []*Failure { - if len(input) == 0 { - return input - } - return append(reverseFailures(input[1:]), input[0]) -} - func (f *Failure) Ago() string { got, _ := timeago.TimeAgoWithTime(time.Now(), f.CreatedAt) return got @@ -101,7 +94,7 @@ func (s *Service) TotalFailures24() (uint64, error) { func (s *Service) TotalFailures() (uint64, error) { var count uint64 rows := failuresDB().Where("service = ?", s.Id) - err := rows.Count(count) + err := rows.Count(&count) return count, err.Error } diff --git a/core/hits.go b/core/hits.go index 931a0e77..b8b9b9f5 100644 --- a/core/hits.go +++ b/core/hits.go @@ -26,7 +26,7 @@ type Hit struct { } func (s *Service) CreateHit(h *types.Hit) (int64, error) { - db := hitsDB().Create(&h) + db := hitsDB().Create(h) if db.Error != nil { utils.Log(2, db.Error) return 0, db.Error @@ -62,10 +62,6 @@ func (s *Service) SelectHitsGroupBy(group string) ([]*Hit, error) { return hits, err.Error } -func (s *Service) hits() { - -} - func (s *Service) TotalHits() (uint64, error) { var count uint64 col := hitsDB().Where("service = ?", s.Id) diff --git a/core/services.go b/core/services.go index 4ec329c7..124ca014 100644 --- a/core/services.go +++ b/core/services.go @@ -44,7 +44,7 @@ func SelectService(id int64) *Service { func (c *Core) SelectAllServices() ([]*types.Service, error) { var services []*types.Service var servs []*types.Service - db := servicesDB().Find(&services) + db := servicesDB().Find(&services).Order("order_id desc") if db.Error != nil { utils.Log(3, fmt.Sprintf("service error: %v", db.Error)) return nil, db.Error @@ -186,24 +186,37 @@ func (s *Service) AvgUptime24() string { func (s *Service) AvgUptime(ago time.Time) string { failed, _ := s.TotalFailuresSince(ago) if failed == 0 { - s.TotalUptime = "100" - return s.TotalUptime + return "100" } total, _ := s.TotalHitsSince(ago) if total == 0 { - s.TotalUptime = "0" - return s.TotalUptime + return "0" } percent := float64(failed) / float64(total) * 100 percent = 100 - percent if percent < 0 { percent = 0 } - s.TotalUptime = fmt.Sprintf("%0.2f", percent) - if s.TotalUptime == "100.00" { - s.TotalUptime = "100" + amount := fmt.Sprintf("%0.2f", percent) + if amount == "100.00" { + amount = "100" } - return s.TotalUptime + return amount +} + +func (s *Service) TotalUptime() string { + hits, _ := s.TotalHits() + failures, _ := s.TotalFailures() + percent := float64(failures) / float64(hits) * 100 + percent = 100 - percent + if percent < 0 { + percent = 0 + } + amount := fmt.Sprintf("%0.2f", percent) + if amount == "100.00" { + amount = "100" + } + return amount } func (s *Service) index() int { diff --git a/handlers/services.go b/handlers/services.go index f24166c1..06c54bba 100644 --- a/handlers/services.go +++ b/handlers/services.go @@ -59,8 +59,10 @@ func ReorderServiceHandler(w http.ResponseWriter, r *http.Request) { decoder := json.NewDecoder(r.Body) decoder.Decode(&newOrder) for _, s := range newOrder { + fmt.Println("updating: ", s.Id, " to be order_id: ", s.Order) service := core.SelectService(s.Id) - service.UpdateSingle("order_id", s.Order) + service.Order = s.Order + service.Update(false) } w.WriteHeader(http.StatusOK) } diff --git a/notifiers/notifiers.go b/notifiers/notifiers.go index b10ba2f5..f831b3a8 100644 --- a/notifiers/notifiers.go +++ b/notifiers/notifiers.go @@ -33,18 +33,19 @@ var ( type Notification struct { Id int64 `gorm:"primary_key column:id" json:"id"` Method string `gorm:"column:method" json:"method"` - Host string `gorm:"column:host" json:"-"` - Port int `gorm:"column:port" json:"-"` - Username string `gorm:"column:username" json:"-"` - Password string `gorm:"column:password" json:"-"` - Var1 string `gorm:"column:var1" json:"-"` - Var2 string `gorm:"column:var2" json:"-"` - ApiKey string `gorm:"column:api_key" json:"-"` - ApiSecret string `gorm:"column:api_secret" json:"-"` - Enabled bool `gorm:"column:enabled" json:"enabled"` - Limits int `gorm:"column:limits" json:"-"` + Host string `gorm:"not null;column:host" json:"-"` + Port int `gorm:"not null;column:port" json:"-"` + Username string `gorm:"not null;column:username" json:"-"` + Password string `gorm:"not null;column:password" json:"-"` + Var1 string `gorm:"not null;column:var1" json:"-"` + Var2 string `gorm:"not null;column:var2" json:"-"` + ApiKey string `gorm:"not null;column:api_key" json:"-"` + ApiSecret string `gorm:"not null;column:api_secret" json:"-"` + Enabled bool `gorm:"column:enabled;type:boolean;default:false" json:"enabled"` + Limits int `gorm:"not null;column:limits" json:"-"` Removable bool `gorm:"column:removable" json:"-"` CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` + UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"` Form []NotificationForm `gorm:"-" json:"-"` Routine chan struct{} `gorm:"-" json:"-"` } diff --git a/source/source.go b/source/source.go index 205da107..8fb2c049 100644 --- a/source/source.go +++ b/source/source.go @@ -27,7 +27,6 @@ import ( ) var ( - SqlBox *rice.Box CssBox *rice.Box ScssBox *rice.Box JsBox *rice.Box @@ -35,7 +34,6 @@ var ( ) func Assets() { - SqlBox = rice.MustFindBox("sql") CssBox = rice.MustFindBox("css") ScssBox = rice.MustFindBox("scss") JsBox = rice.MustFindBox("js") diff --git a/source/sql/down.sql b/source/sql/down.sql deleted file mode 100644 index eda2d5cc..00000000 --- a/source/sql/down.sql +++ /dev/null @@ -1,7 +0,0 @@ -DROP TABLE IF EXISTS core; -DROP TABLE IF EXISTS hits; -DROP TABLE IF EXISTS failures; -DROP TABLE IF EXISTS users; -DROP TABLE IF EXISTS checkins; -DROP TABLE IF EXISTS services; -DROP TABLE IF EXISTS communication; diff --git a/source/sql/mysql_up.sql b/source/sql/mysql_up.sql deleted file mode 100644 index d40e5cd2..00000000 --- a/source/sql/mysql_up.sql +++ /dev/null @@ -1,78 +0,0 @@ -CREATE TABLE core ( - name VARCHAR(50), - description text, - config VARCHAR(50), - api_key VARCHAR(50), - api_secret VARCHAR(50), - style text, - footer text, - domain text, - version VARCHAR(50), - migration_id INT(6) NOT NULL DEFAULT 0, - use_cdn BOOL NOT NULL DEFAULT '0' -) ENGINE=INNODB; -CREATE TABLE users ( - id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id), - username VARCHAR(50) NOT NULL UNIQUE, - password text, - email VARCHAR (50), - api_key VARCHAR(50), - api_secret VARCHAR(50), - administrator BOOL NOT NULL DEFAULT '0', - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - UNIQUE (username, email) -) ENGINE=INNODB; -CREATE TABLE services ( - id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id), - name VARCHAR(50), - domain text, - check_type text, - method VARCHAR(50), - port INT(6), - expected text, - expected_status INT(6), - check_interval int(11), - post_data text, - order_id integer default 0, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - timeout INT(6) DEFAULT 30 -) ENGINE=INNODB; -CREATE TABLE hits ( - id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id), - service BIGINT(20) UNSIGNED NOT NULL, - latency float, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (service) REFERENCES services(id) ON DELETE CASCADE -) ENGINE=INNODB; -CREATE TABLE failures ( - id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id), - issue text, - method text, - service BIGINT(20) UNSIGNED NOT NULL, - created_at TIMESTAMP, - FOREIGN KEY (service) REFERENCES services(id) ON DELETE CASCADE -) ENGINE=INNODB; -CREATE TABLE checkins ( - id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id), - service BIGINT(20) UNSIGNED NOT NULL, - check_interval integer, - api text, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, - FOREIGN KEY (service) REFERENCES services(id) ON DELETE CASCADE -) ENGINE=INNODB; -CREATE TABLE communication ( - id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT, PRIMARY KEY(id), - method text, - host text, - port integer, - username text, - password text, - var1 text, - var2 text, - api_key text, - api_secret text, - enabled BOOL NOT NULL DEFAULT '0', - removable BOOL NOT NULL DEFAULT '0', - limits integer, - created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP -) ENGINE=INNODB; diff --git a/source/sql/mysql_upgrade.sql b/source/sql/mysql_upgrade.sql deleted file mode 100644 index 19dc913b..00000000 --- a/source/sql/mysql_upgrade.sql +++ /dev/null @@ -1,9 +0,0 @@ -=========================================== 1534178020 -UPDATE services SET order_id=0 WHERE order_id IS NULL; -=========================================== 1532068515 -ALTER TABLE services ALTER COLUMN order_id integer DEFAULT 0; -ALTER TABLE services ADD COLUMN timeout integer DEFAULT 30; -=========================================== 1530841150 -ALTER TABLE core ADD COLUMN use_cdn BOOL NOT NULL DEFAULT '0'; -=========================================== 1 -ALTER TABLE core ADD COLUMN migration_id INT(6) NOT NULL DEFAULT 0; \ No newline at end of file diff --git a/source/sql/postgres_up.sql b/source/sql/postgres_up.sql deleted file mode 100644 index cf2a96e7..00000000 --- a/source/sql/postgres_up.sql +++ /dev/null @@ -1,84 +0,0 @@ -CREATE TABLE core ( - name text, - description text, - config text, - api_key text, - api_secret text, - style text, - footer text, - domain text, - version text, - migration_id integer default 0, - use_cdn bool default false -); - -CREATE TABLE users ( - id SERIAL PRIMARY KEY, - username VARCHAR (50) UNIQUE, - password text, - email VARCHAR (50) UNIQUE, - api_key text, - api_secret text, - administrator bool, - created_at TIMESTAMP -); - -CREATE TABLE services ( - id SERIAL PRIMARY KEY, - name text, - domain text, - check_type text, - method text, - port integer, - expected text, - expected_status integer, - check_interval integer, - post_data text, - order_id integer default 0, - timeout integer default 30, - created_at TIMESTAMP -); - -CREATE TABLE hits ( - id SERIAL PRIMARY KEY, - service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE, - latency float, - created_at TIMESTAMP -); - -CREATE TABLE failures ( - id SERIAL PRIMARY KEY, - issue text, - method text, - service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE, - created_at TIMESTAMP -); - -CREATE TABLE checkins ( - id SERIAL PRIMARY KEY, - service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE, - check_interval integer, - api text, - created_at TIMESTAMP -); - -CREATE TABLE communication ( - id SERIAL PRIMARY KEY, - method text, - host text, - port integer, - username text, - password text, - var1 text, - var2 text, - api_key text, - api_secret text, - enabled boolean, - removable boolean, - limits integer, - created_at TIMESTAMP -); - -CREATE INDEX idx_hits ON hits(service); -CREATE INDEX idx_failures ON failures(service); -CREATE INDEX idx_checkins ON checkins(service); \ No newline at end of file diff --git a/source/sql/postgres_upgrade.sql b/source/sql/postgres_upgrade.sql deleted file mode 100644 index b27749cf..00000000 --- a/source/sql/postgres_upgrade.sql +++ /dev/null @@ -1,9 +0,0 @@ -=========================================== 1534178020 -UPDATE services SET order_id=0 WHERE order_id IS NULL; -=========================================== 1532068515 -ALTER TABLE services ALTER COLUMN order_id integer DEFAULT 0; -ALTER TABLE services ADD COLUMN timeout integer DEFAULT 30; -=========================================== 1530841150 -ALTER TABLE core ADD COLUMN use_cdn bool DEFAULT FALSE; -=========================================== 1 -ALTER TABLE core ADD COLUMN migration_id integer default 0 NOT NULL; \ No newline at end of file diff --git a/source/sql/sqlite_up.sql b/source/sql/sqlite_up.sql deleted file mode 100644 index 872b8f7e..00000000 --- a/source/sql/sqlite_up.sql +++ /dev/null @@ -1,84 +0,0 @@ -CREATE TABLE core ( - name text, - description text, - config text, - api_key text, - api_secret text, - style text, - footer text, - domain text, - version text, - migration_id integer default 0, - use_cdn bool default false -); -CREATE TABLE users ( - id INTEGER PRIMARY KEY, - username text NOT NULL UNIQUE, - password text, - email text, - api_key text, - api_secret text, - administrator bool, - created_at TIMESTAMP, - UNIQUE (username, email) -); -CREATE TABLE services ( - id INTEGER PRIMARY KEY, - name text, - domain text, - check_type text, - method text, - port integer, - expected text, - expected_status integer, - check_interval integer, - post_data text, - order_id integer default 0, - timeout integer default 30, - created_at TIMESTAMP -); - -CREATE TABLE hits ( - id INTEGER PRIMARY KEY, - service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE, - latency float, - created_at TIMESTAMP -); - -CREATE TABLE failures ( - id INTEGER PRIMARY KEY, - issue text, - method text, - service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE, - created_at TIMESTAMP -); - -CREATE TABLE checkins ( - id INTEGER PRIMARY KEY, - service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE, - check_interval integer, - api text, - created_at TIMESTAMP -); - -CREATE TABLE communication ( - id INTEGER PRIMARY KEY, - method text, - host text, - port integer, - username text, - password text, - var1 text, - var2 text, - api_key text, - api_secret text, - enabled boolean, - removable boolean, - limits integer, - created_at TIMESTAMP -); - - -CREATE INDEX idx_hits ON hits(service); -CREATE INDEX idx_failures ON failures(service); -CREATE INDEX idx_checkins ON checkins(service); diff --git a/source/sql/sqlite_upgrade.sql b/source/sql/sqlite_upgrade.sql deleted file mode 100644 index 754213c2..00000000 --- a/source/sql/sqlite_upgrade.sql +++ /dev/null @@ -1,9 +0,0 @@ -=========================================== 1534178020 -UPDATE services SET order_id=0 WHERE order_id IS NULL; -=========================================== 1532068515 -ALTER TABLE services ALTER COLUMN order_id integer DEFAULT 0; -ALTER TABLE services ADD COLUMN timeout integer DEFAULT 30; -=========================================== 1530841150 -ALTER TABLE core ADD COLUMN use_cdn bool DEFAULT FALSE; -=========================================== 1 -ALTER TABLE core ADD COLUMN migration_id integer NOT NULL DEFAULT 0; \ No newline at end of file diff --git a/source/tmpl/footer.html b/source/tmpl/footer.html index 2e4839da..95056d1b 100644 --- a/source/tmpl/footer.html +++ b/source/tmpl/footer.html @@ -1,9 +1,9 @@ {{ define "footer"}}
-{{ end }} \ No newline at end of file +{{ end }} diff --git a/source/tmpl/service.html b/source/tmpl/service.html index 13b95b32..aec043cc 100644 --- a/source/tmpl/service.html +++ b/source/tmpl/service.html @@ -51,7 +51,7 @@