removed sql files - db updates - migrations - beginning to comment on funcs

pull/61/head^2
Hunter Long 2018-09-05 22:28:35 -07:00
parent 1eb55a0c71
commit 3f056125ae
29 changed files with 196 additions and 546 deletions

View File

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

View File

@ -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...")

View File

@ -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)

View File

@ -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) {

View File

@ -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

View File

@ -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))
}

View File

@ -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)

View File

@ -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
)
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(&notifiers.Notification{}).Debug()
db := DbSession.Table("communication").Model(&notifiers.Notification{})
if os.Getenv("GO_ENV") == "test" {
return db.Debug()
}
return DbSession.Table("communication").Model(&notifiers.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,41 +119,37 @@ 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)
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(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))
@ -161,22 +159,6 @@ func (db *DbConfig) Connect(retry bool, location string) error {
return err
}
}
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
}
}
}
err = DbSession.DB().Ping()
if err == nil {
utils.Log(1, fmt.Sprintf("Database connection to '%v' was successful.", Configs.DbData))
@ -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(&notifiers.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(&notifiers.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 {

View File

@ -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
}

View File

@ -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)

View File

@ -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 {

View File

@ -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)
}

View File

@ -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:"-"`
}

View File

@ -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")

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -1,6 +1,6 @@
{{ define "footer"}}
<div class="footer text-center mb-4">
{{ if CoreApp.Footer }}
{{ if ne CoreApp.Footer "" }}
{{ safe CoreApp.Footer }}
{{ else }}
<a href="https://github.com/hunterlong/statup" target="_blank">Statup {{VERSION}} made with ❤️</a> | <a href="/dashboard">Dashboard</a>

View File

@ -51,7 +51,7 @@
</div>
<div class="col-4">
<span class="lg_number">{{.AvgUptime24}}%</span>
<span class="lg_number">{{.TotalUptime}}%</span>
Total Uptime
</div>
</div>

View File

@ -9,7 +9,8 @@ type Checkin struct {
Service int64 `gorm:"index;column:service"`
Interval int64 `gorm:"column:check_interval"`
Api string `gorm:"column:api"`
CreatedAt time.Time `gorm:"column:created_at"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
Hits int64 `json:"hits"`
Last time.Time `json:"last"`
CheckinInterface `json:"-"`

View File

@ -1,32 +1,45 @@
package types
import (
"sort"
"time"
)
// Core struct contains all the required fields for Statup. All application settings
// will be saved into 1 row in the 'core' table. You can use the core.CoreApp
// global variable to interact with the attributes to the application, such as services.
type Core struct {
Name string `gorm:"column:name" json:"name"`
Description string `gorm:"column:description" json:"description,omitempty"`
Name string `gorm:"not null;column:name" json:"name"`
Description string `gorm:"not null;column:description" json:"description,omitempty"`
Config string `gorm:"column:config" json:"-"`
ApiKey string `gorm:"column:api_key" json:"-"`
ApiSecret string `gorm:"column:api_secret" json:"-"`
Style string `gorm:"column:style" json:"style,omitempty"`
Footer string `gorm:"column:footer" json:"footer,omitempty"`
Domain string `gorm:"column:domain" json:"domain,omitempty"`
Style string `gorm:"not null;column:style" json:"style,omitempty"`
Footer string `gorm:"not null;column:footer" json:"footer,omitempty"`
Domain string `gorm:"not null;column:domain" json:"domain,omitempty"`
Version string `gorm:"column:version" json:"version"`
MigrationId int64 `gorm:"column:migration_id" json:"migration_id,omitempty"`
UseCdn bool `gorm:"column:use_cdn" json:"using_cdn,omitempty"`
DbConnection string `gorm:"-" json:database"`
Started time.Time `gorm:"-" json:started_on"`
dbServices []*Service `gorm:"-" json:services,omitempty"`
Plugins []Info `gorm:"-" json:-"`
Repos []PluginJSON `gorm:"-" json:-"`
AllPlugins []PluginActions `gorm:"-" json:-"`
Communications []AllNotifiers `gorm:"-" json:-"`
CoreInterface `gorm:"-" json:-"`
UseCdn bool `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
DbConnection string `gorm:"-" json:"database"`
Started time.Time `gorm:"-" json:"started_on"`
dbServices []*Service `gorm:"-" json:"services,omitempty"`
Plugins []Info `gorm:"-" json:"-"`
Repos []PluginJSON `gorm:"-" json:"-"`
AllPlugins []PluginActions `gorm:"-" json:"-"`
Communications []AllNotifiers `gorm:"-" json:"-"`
CoreInterface `gorm:"-" json:"-"`
}
type ServiceOrder []*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 }
func (c *Core) SetServices(s []*Service) {
sort.Sort(ServiceOrder(c.dbServices))
c.dbServices = s
}

View File

@ -23,21 +23,21 @@ type Service struct {
Id int64 `gorm:"primary_key;column:id" json:"id"`
Name string `gorm:"column:name" json:"name"`
Domain string `gorm:"column:domain" json:"domain"`
Expected string `gorm:"column:expected" json:"expected"`
ExpectedStatus int `gorm:"column:expected_status" json:"expected_status"`
Interval int `gorm:"column:check_interval" json:"check_interval"`
Expected string `gorm:"not null;column:expected" json:"expected"`
ExpectedStatus int `gorm:"default:200;column:expected_status" json:"expected_status"`
Interval int `gorm:"default:30;column:check_interval" json:"check_interval"`
Type string `gorm:"column:check_type" json:"type"`
Method string `gorm:"column:method" json:"method"`
PostData string `gorm:"column:post_data" json:"post_data"`
Port int `gorm:"column:port" json:"port"`
PostData string `gorm:"not null;column:post_data" json:"post_data"`
Port int `gorm:"not null;column:port" json:"port"`
Timeout int `gorm:"default:30;column:timeout" json:"timeout"`
Order int `gorm:"default:0;column:order_id" json:"order_id"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
Timeout int `gorm:"column:timeout" json:"timeout"`
Order int `gorm:"column:order_id" json:"order_id"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
Online bool `gorm:"-" json:"online"`
Latency float64 `gorm:"-" json:"latency"`
Online24Hours float32 `gorm:"-" json:"24_hours_online"`
AvgResponse string `gorm:"-" json:"avg_response"`
TotalUptime string `gorm:"-" json:"uptime"`
Failures []*Failure `gorm:"-" json:"failures"`
Checkins []*Checkin `gorm:"-" json:"checkins"`
Running chan bool `gorm:"-" json:"-"`

View File

@ -61,6 +61,7 @@ type PluginActions interface {
type AllNotifiers interface{}
// Hit struct is a 'successful' ping or web response entry for a service.
type Hit struct {
Id int64 `gorm:"primary_key;column:id"`
Service int64 `gorm:"index;column:service"`
@ -68,6 +69,7 @@ type Hit struct {
CreatedAt time.Time `gorm:"column:created_at"`
}
// DbConfig struct is used for the database connection and creates the 'config.yml' file
type DbConfig struct {
DbConn string `yaml:"connection"`
DbHost string `yaml:"host"`

View File

@ -13,6 +13,7 @@ type User struct {
ApiSecret string `gorm:"column:api_secret" json:"-"`
Admin bool `gorm:"column:administrator" json:"admin"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
UserInterface `gorm:"-" json:"-"`
}