diff --git a/.travis.yml b/.travis.yml index d477e825..6d2f2fa0 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ os: language: go go: - - "1.10.x" + - "1.10.3" go_import_path: github.com/hunterlong/statup diff --git a/Gopkg.lock b/Gopkg.lock index 098faf57..7b6fc3f8 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -1,6 +1,14 @@ # 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 = "c728a003b238b26cef9ab6753a5dc424b331c3ad" + version = "v0.27.0" + [[projects]] branch = "master" digest = "1:65796e5fdb94d94bc0ee6bc422aa3541dbe69ed4da275cb5a27f8fa141f4e35c" @@ -13,12 +21,20 @@ revision = "c02ca9a983da5807ddf7d796784928f5be4afd09" [[projects]] - branch = "master" - digest = "1:4b79025e1eaa5726b92e409a46571f1998b56f0a2d881d6271ca616095eae46e" + digest = "1:e92f5581902c345eb4ceffdcd4a854fb8f73cf436d47d837d1ec98ef1fe0a214" + name = "github.com/StackExchange/wmi" + packages = ["."] + pruneopts = "UT" + revision = "5d049714c4a64225c3c79a7cf7d02f7fb5b96338" + version = "1.0.0" + +[[projects]] + digest = "1:f1ec92a2b8473612547f6e13edbc8c8e6cda6c8be9c54b31958aad4a7ccaaa2b" name = "github.com/ararog/timeago" packages = ["."] pruneopts = "UT" - revision = "e9969cf18b8d5f04cc42f050e8b9968e152cd294" + revision = "518814407569bf983ea81e1bf8b550dd4e7b34f3" + version = "0.0.1" [[projects]] branch = "master" @@ -29,28 +45,50 @@ revision = "a5fe2436ffcb3236e175e5149162b41cd28bd27d" [[projects]] - digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" + digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec" name = "github.com/davecgh/go-spew" packages = ["spew"] pruneopts = "UT" - revision = "346938d642f2ec3594ed81d874461961cd0faa76" - version = "v1.1.0" + revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73" + version = "v1.1.1" [[projects]] branch = "master" - digest = "1:14f498fc55a89776c562fa3510aeb7687356944a560c561c0e617af5f9c5f2c7" + digest = "1:6f120164f62e62991d0f85562abe2002d438abb2ca80b7717a2f4ae2af1c6829" + name = "github.com/denisenkom/go-mssqldb" + packages = [ + ".", + "internal/cp", + ] + pruneopts = "UT" + revision = "1eb28afdf9b6e56cf673badd47545f844fe81103" + +[[projects]] + digest = "1:ca82a3b99694824c627573c2a76d0e49719b4a9c02d1d85a2ac91f1c1f52ab9b" name = "github.com/fatih/structs" packages = ["."] pruneopts = "UT" - revision = "ebf56d35bba727c68ac77f56f2fcf90b181851aa" + revision = "a720dfa8df582c51dee1b36feabb906bde1588bd" + version = "v1.0" [[projects]] - branch = "master" - digest = "1:70a20b8adf085489a342d033b68b7fc27f4017c51e015b857387249493ee0561" + digest = "1:64a5a67c69b70c2420e607a8545d674a23778ed9c3e80607bfd17b77c6c87f6a" + name = "github.com/go-ole/go-ole" + packages = [ + ".", + "oleutil", + ] + pruneopts = "UT" + revision = "a41e3c4b706f6ae8dfbff342b06e40fa4d2d0506" + version = "v1.2.1" + +[[projects]] + digest = "1:adea5a94903eb4384abef30f3d878dc9ff6b6b5b0722da25b82e5169216dfb61" name = "github.com/go-sql-driver/mysql" packages = ["."] pruneopts = "UT" - revision = "99ff426eb706cffe92ff3d058e168b278cabf7c7" + revision = "d523deb1b23d913de5bdada721a6071e71283618" + version = "v1.4.0" [[projects]] digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" @@ -69,35 +107,74 @@ version = "v1.1.1" [[projects]] - digest = "1:195b71563f8432dac9d9692aca2a9ae098bc35763999573eb1c7d52a02a47ce7" + digest = "1:664d37ea261f0fc73dd17f4a1f5f46d01fbb0b0d75f6375af064824424109b7d" + name = "github.com/gorilla/handlers" + packages = ["."] + pruneopts = "UT" + revision = "7e0847f9db758cdebd26c149d0ae9d5d0b9c98ce" + version = "v1.4.0" + +[[projects]] + digest = "1:e73f5b0152105f18bc131fba127d9949305c8693f8a762588a82a48f61756f5f" name = "github.com/gorilla/mux" packages = ["."] pruneopts = "UT" - revision = "cb4698366aa625048f3b815af6a0dea8aef9280a" + revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf" + version = "v1.6.2" [[projects]] - branch = "master" - digest = "1:6e0af49591c81ae6d542fd0ba41ea7206f4242471b384bebd206283b9f38e091" + digest = "1:e72d1ebb8d395cf9f346fd9cbc652e5ae222dd85e0ac842dc57f175abed6d195" name = "github.com/gorilla/securecookie" packages = ["."] pruneopts = "UT" - revision = "78f3d318a8bf316cda921f25e96fd0b441c5173d" - -[[projects]] - digest = "1:0fe783ea0c04c7d13f7c55d8f74b01b17e18a8320e7deecf578b41ef99b27205" - name = "github.com/gorilla/sessions" - packages = ["."] - pruneopts = "UT" - revision = "03b6f63cc43ef9c7240a635a5e22b13180e822b8" + revision = "e59506cc896acb7f7bf732d4fdf5e25f7ccd8983" version = "v1.1.1" +[[projects]] + digest = "1:0aa142ca3543fa3aca344c2fa2d52e01d706a3ce4e1fa893e0ef29b5349ddf7a" + name = "github.com/gorilla/sessions" + packages = ["."] + pruneopts = "UT" + revision = "81547393f870a35be888759a606ba7bf71dbe5c7" + version = "v1.1.2" + +[[projects]] + digest = "1:7b5c6e2eeaa9ae5907c391a91c132abfd5c9e8a784a341b5625e750c67e6825d" + name = "github.com/gorilla/websocket" + packages = ["."] + pruneopts = "UT" + revision = "66b9c49e59c6c48f0ffce28c2d8b8a5678502c6d" + version = "v1.4.0" + +[[projects]] + digest = "1:4d28d632da146ec6e632bdd29676a95e0874f1ad837fff06aef6610b3b6b4728" + name = "github.com/jinzhu/gorm" + packages = [ + ".", + "dialects/mssql", + "dialects/mysql", + "dialects/postgres", + "dialects/sqlite", + ] + pruneopts = "UT" + revision = "6ed508ec6a4ecb3531899a69cbc746ccf65a4166" + version = "v1.9.1" + [[projects]] branch = "master" - digest = "1:d166a4b3543a71d87e34767ace94e8b516e79ba45111730306f07c8d6f797f1f" + digest = "1:fd97437fbb6b7dce04132cf06775bd258cce305c44add58eb55ca86c6c325160" + name = "github.com/jinzhu/inflection" + packages = ["."] + pruneopts = "UT" + revision = "04140366298a54a039076d798123ffa108fff46c" + +[[projects]] + digest = "1:70e697d67ccaec45e16bac3a32380ebcd9e7e071079c60d0171d42cf1cf9748a" name = "github.com/joho/godotenv" packages = ["."] pruneopts = "UT" - revision = "1709ab122c988931ad53508747b3c061400c2984" + revision = "a79fa1e548e2c689c241d10173efd51e5d689d5b" + version = "v1.2.0" [[projects]] branch = "master" @@ -108,31 +185,43 @@ revision = "ae77be60afb1dcacde03767a8c37337fad28ac14" [[projects]] - branch = "master" - digest = "1:37ce7d7d80531b227023331002c0d42b4b4b291a96798c82a049d03a54ba79e4" + digest = "1:b18ffc558326ebaed3b4a175617f1e12ed4e3f53d6ebfe5ba372a3de16d22278" name = "github.com/lib/pq" packages = [ ".", + "hstore", "oid", ] pruneopts = "UT" - revision = "90697d60dd844d5ef6ff15135d0203f65d2f53b8" + revision = "4ded0e9383f75c197b3a2aaa6d590ac52df6fd79" + version = "v1.0.0" [[projects]] - branch = "master" - digest = "1:05b7bf6e275fe5d56f479a944d1fd823437f6a2739610ae8359e137690d0a050" + digest = "1:3cafc6a5a1b8269605d9df4c6956d43d8011fc57f266ca6b9d04da6c09dee548" name = "github.com/mattn/go-sqlite3" packages = ["."] pruneopts = "UT" - revision = "b3511bfdd742af558b54eb6160aca9446d762a19" + revision = "25ecb14adfc7543176f7d85291ec7dba82c6f7e4" + version = "v1.9.0" [[projects]] branch = "master" - digest = "1:3891cc78541df6e4596b3e73978eb062d32967084069270ec881b959be5155ae" + digest = "1:4f4fd75cbdd5ad0696f4d762328f094b9c86061323c9a9f8d0de157f06197e89" + name = "github.com/mkevac/debugcharts" + packages = [ + ".", + "bindata", + ] + pruneopts = "UT" + revision = "d3203a8fa92649b82dc35c214979861de918874a" + +[[projects]] + digest = "1:40e195917a951a8bf867cd05de2a46aaf1806c50cf92eebf4c16f78cd196f747" name = "github.com/pkg/errors" packages = ["."] pruneopts = "UT" - revision = "816c9085562cd7ee03e7f8188a1cfd942858cded" + revision = "645ef00459ed84a119197bfb8d8205042c6df63d" + version = "v0.8.0" [[projects]] digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" @@ -150,6 +239,29 @@ pruneopts = "UT" revision = "6283090d169f51a2410b4e260341a01c9a4c0ca7" +[[projects]] + digest = "1:7395b855a6078ad2e6c40311402a057a91125fb9f32cf228e1b32cdc57c33538" + name = "github.com/shirou/gopsutil" + packages = [ + "cpu", + "host", + "internal/common", + "mem", + "net", + "process", + ] + pruneopts = "UT" + revision = "8048a2e9c5773235122027dd585cf821b2af1249" + version = "v2.18.07" + +[[projects]] + branch = "master" + digest = "1:99c6a6dab47067c9b898e8c8b13d130c6ab4ffbcc4b7cc6236c2cd0b1e344f5b" + name = "github.com/shirou/w32" + packages = ["."] + pruneopts = "UT" + revision = "bb4de0191aa41b5507caa14b0650cdbddcd9280b" + [[projects]] digest = "1:18752d0b95816a1b777505a97f71c7467a8445b8ffb55631a7bf779f6ba4fa83" name = "github.com/stretchr/testify" @@ -159,14 +271,27 @@ version = "v1.2.2" [[projects]] - digest = "1:1ecf2a49df33be51e757d0033d5d51d5f784f35f68e5a38f797b2d3f03357d71" + branch = "master" + digest = "1:68344dbfaa4179bb50a583eb8172ace3f1edaf3aebc24e68c03f549f6e6b60dc" name = "golang.org/x/crypto" packages = [ "bcrypt", "blowfish", + "md4", ] pruneopts = "UT" - revision = "c126467f60eb25f8f27e5a981f32a87e3965053f" + revision = "0709b304e793a5edb4a2c0145f281ecdc20838a4" + +[[projects]] + branch = "master" + digest = "1:7f4a61b989d94774dc61016b660cf8347f59eb0bed91a10b2f23fc72a38d45d4" + name = "golang.org/x/sys" + packages = [ + "unix", + "windows", + ] + pruneopts = "UT" + revision = "ebe1bf3edb3325c393447059974de898d5133eb8" [[projects]] digest = "1:c25289f43ac4a68d88b02245742347c94f1e108c534dda442188015ff80669b3" @@ -185,12 +310,12 @@ revision = "2caba252f4dc53eaf6b553000885530023f54623" [[projects]] - branch = "v2" - digest = "1:e096ada745e034a059752c202717baee0379b69db9473a90f59d103c973384cf" + digest = "1:eebd52aee67d9f1e0af1859a584e14b1817581dd5f90d4585418c27b6a93c966" name = "gopkg.in/gomail.v2" packages = ["."] pruneopts = "UT" - revision = "81ebce5c23dfd25c6c67194b37d3dd3f338c98b1" + revision = "41f3572897373c5538c50a2402db15db079fa4fd" + version = "2.0.0" [[projects]] digest = "1:c805e517269b0ba4c21ded5836019ed7d16953d4026cb7d00041d039c7906be9" @@ -201,25 +326,21 @@ version = "v2.1" [[projects]] - digest = "1:0d1f3aba6c5c26e67b090b4eed519582fba0dff24d3e486fcb011d84ca3e7806" + digest = "1:0a6ab450a46e158a97e3daf7da9df3bfd4f84420047fab6a65fb70b1337ce026" name = "upper.io/db.v3" packages = [ ".", "internal/cache", "internal/cache/hashstructure", "internal/immutable", - "internal/sqladapter", "internal/sqladapter/compat", "internal/sqladapter/exql", "lib/reflectx", "lib/sqlbuilder", - "mysql", - "postgresql", - "sqlite", ] pruneopts = "UT" - revision = "d90922beee6de3f39c93ed677f6da82565d07154" - version = "v3.5.3" + revision = "199d13d76c7cfba05ea0327375056fdabc8bea80" + version = "v3.5.4" [solve-meta] analyzer-name = "dep" @@ -230,20 +351,23 @@ "github.com/ararog/timeago", "github.com/fatih/structs", "github.com/go-yaml/yaml", + "github.com/gorilla/handlers", "github.com/gorilla/mux", "github.com/gorilla/sessions", + "github.com/jinzhu/gorm", + "github.com/jinzhu/gorm/dialects/mssql", + "github.com/jinzhu/gorm/dialects/mysql", + "github.com/jinzhu/gorm/dialects/postgres", + "github.com/jinzhu/gorm/dialects/sqlite", "github.com/joho/godotenv", + "github.com/mkevac/debugcharts", "github.com/pkg/errors", "github.com/rendon/testcli", "github.com/stretchr/testify/assert", "golang.org/x/crypto/bcrypt", "gopkg.in/gomail.v2", "gopkg.in/natefinch/lumberjack.v2", - "upper.io/db.v3", "upper.io/db.v3/lib/sqlbuilder", - "upper.io/db.v3/mysql", - "upper.io/db.v3/postgresql", - "upper.io/db.v3/sqlite", ] solver-name = "gps-cdcl" solver-version = 1 diff --git a/Gopkg.toml b/Gopkg.toml index 86976537..103c2ea8 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -30,28 +30,44 @@ name = "github.com/GeertJohan/go.rice" [[constraint]] - branch = "master" name = "github.com/ararog/timeago" + version = "0.0.1" [[constraint]] - branch = "master" name = "github.com/fatih/structs" + version = "1.0.0" [[constraint]] name = "github.com/go-yaml/yaml" version = "2.2.1" +[[constraint]] + name = "github.com/gorilla/handlers" + version = "1.4.0" + +[[constraint]] + name = "github.com/gorilla/mux" + version = "1.6.2" + [[constraint]] name = "github.com/gorilla/sessions" - version = "1.1.1" + version = "1.1.2" + +[[constraint]] + name = "github.com/jinzhu/gorm" + version = "1.9.1" [[constraint]] - branch = "master" name = "github.com/joho/godotenv" + version = "1.2.0" [[constraint]] branch = "master" + name = "github.com/mkevac/debugcharts" + +[[constraint]] name = "github.com/pkg/errors" + version = "0.8.0" [[constraint]] branch = "master" @@ -62,8 +78,12 @@ version = "1.2.2" [[constraint]] - branch = "v2" + branch = "master" + name = "golang.org/x/crypto" + +[[constraint]] name = "gopkg.in/gomail.v2" + version = "2.0.0" [[constraint]] name = "gopkg.in/natefinch/lumberjack.v2" @@ -71,7 +91,7 @@ [[constraint]] name = "upper.io/db.v3" - version = "3.5.3" + version = "3.5.4" [prune] go-tests = true diff --git a/Makefile b/Makefile index a390d67f..6fced6c6 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -VERSION=0.52 +VERSION=0.54 BINARY_NAME=statup GOPATH:=$(GOPATH) GOCMD=go @@ -26,6 +26,10 @@ docker-build-all: docker-build-base docker-dev docker docker-publish-all: docker-push-base docker-push-dev docker-push-latest +seed: + rm -f statup.db + cat dev/seed.sql | sqlite3 statup.db + build: compile $(GOBUILD) $(BUILDVERSION) -o $(BINARY_NAME) -v ./cmd @@ -127,6 +131,7 @@ dep: dep ensure -vendor-only dev-deps: dep + $(GOGET) -u github.com/jinzhu/gorm/... $(GOGET) github.com/stretchr/testify/assert $(GOGET) golang.org/x/tools/cmd/cover $(GOGET) github.com/mattn/goveralls diff --git a/cmd/cli.go b/cmd/cli.go index 03ccc96b..da5ba6c4 100644 --- a/cmd/cli.go +++ b/cmd/cli.go @@ -24,13 +24,12 @@ import ( "github.com/hunterlong/statup/source" "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" + "github.com/jinzhu/gorm" "github.com/joho/godotenv" "io/ioutil" "math/rand" "net/http" - "strings" "time" - "upper.io/db.v3/sqlite" ) const ( @@ -45,6 +44,8 @@ func CatchCLI(args []string) error { LoadDotEnvs() switch args[0] { + case "seed": + handlers.DesktopInit(ipAddress, port) case "app": handlers.DesktopInit(ipAddress, port) case "version": @@ -94,7 +95,7 @@ func CatchCLI(args []string) error { case "export": var err error fmt.Printf("Statup v%v Exporting Static 'index.html' page...\n", VERSION) - core.Configs, err = core.LoadConfig() + core.Configs, err = core.LoadConfig(dir) if err != nil { utils.Log(4, "config.yml file not found") return err @@ -132,11 +133,11 @@ func CatchCLI(args []string) error { func RunOnce() { var err error - core.Configs, err = core.LoadConfig() + core.Configs, err = core.LoadConfig(utils.Directory) if err != nil { utils.Log(4, "config.yml file not found") } - err = core.DbConnection(core.Configs.Connection, false, utils.Directory) + err = core.Configs.Connect(false, utils.Directory) if err != nil { utils.Log(4, err) } @@ -234,21 +235,10 @@ func FakeSeed(plug types.PluginActions) { fmt.Printf("\n" + BRAKER) fmt.Println("\nCreating a SQLite database for testing, will be deleted automatically...") - sqlFake := sqlite.ConnectionURL{ - Database: "./.plugin_test.db", - } - core.DbSession, err = sqlite.Open(sqlFake) + core.DbSession, err = gorm.Open("sqlite", "./.plugin_test.db") if err != nil { utils.Log(3, err) } - up, _ := source.SqlBox.String("sqlite_up.sql") - requests := strings.Split(up, ";") - for _, request := range requests { - _, err := core.DbSession.Exec(request) - if err != nil { - utils.Log(2, err) - } - } 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 cedceec0..bea47f84 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -55,6 +55,8 @@ func parseFlags() { func main() { var err error parseFlags() + LoadDotEnvs() + source.Assets() utils.InitLogs() args := flag.Args() @@ -68,13 +70,8 @@ func main() { os.Exit(1) } } - - source.Assets() - LoadDotEnvs() - utils.Log(1, fmt.Sprintf("Starting Statup v%v", VERSION)) - - core.Configs, err = core.LoadConfig() + core.Configs, err = core.LoadConfig(utils.Directory) if err != nil { utils.Log(3, err) core.SetupMode = true @@ -94,15 +91,18 @@ func LoadDotEnvs() error { } func mainProcess() { + dir := utils.Directory var err error - err = core.DbConnection(core.Configs.Connection, false, utils.Directory) + core.Configs, err = core.LoadConfig(dir) + if err != nil { + utils.Log(4, fmt.Sprintf("could not load config.yml %v", err)) + } + err = core.Configs.Connect(false, dir) 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) fmt.Println(handlers.RunHTTPServer(ipAddress, port)) @@ -170,7 +170,7 @@ func LoadPlugins(debug bool) { if debug { TestPlugin(plugActions) } else { - plugActions.OnLoad(core.DbSession) + plugActions.OnLoad(*core.DbSession) core.CoreApp.Plugins = append(core.CoreApp.Plugins, plugActions.GetInfo()) core.CoreApp.AllPlugins = append(core.CoreApp.AllPlugins, plugActions) } diff --git a/cmd/main_test.go b/cmd/main_test.go index 4ee82dfa..6b2b0b9c 100644 --- a/cmd/main_test.go +++ b/cmd/main_test.go @@ -31,27 +31,30 @@ import ( "os" "strings" "testing" + "time" ) var ( - route *mux.Router - testSession *sessions.Session - dir string + route *mux.Router + testSession *sessions.Session + dir string + SERVICE_SINCE, _ = time.Parse(time.RFC3339, "2018-08-30T10:42:08-07:00") ) func init() { dir = utils.Directory - os.Remove(dir + "/statup.db") - //os.Remove(gopath+"/cmd/config.yml") - os.RemoveAll(dir + "/cmd/assets") - os.RemoveAll(dir + "/logs") +} + +func Clean() { + utils.DeleteFile(dir + "/config.yml") + utils.DeleteFile(dir + "/statup.db") + utils.DeleteDirectory(dir + "/assets") + utils.DeleteDirectory(dir + "/logs") } func RunInit(t *testing.T) { source.Assets() - os.Remove(dir + "/statup.db") - os.Remove(dir + "/cmd/config.yml") - os.Remove(dir + "/cmd/index.html") + Clean() route = handlers.Router() LoadDotEnvs() core.CoreApp = core.NewCore() @@ -60,35 +63,55 @@ func RunInit(t *testing.T) { func TestRunAll(t *testing.T) { //t.Parallel() - databases := []string{"sqlite", "postgres", "mysql"} + databases := []string{"postgres", "sqlite", "mysql"} if os.Getenv("ONLY_DB") != "" { databases = []string{os.Getenv("ONLY_DB")} } for _, dbt := range databases { - t.Run(dbt+" init", func(t *testing.T) { RunInit(t) }) - t.Run(dbt+" load database config", func(t *testing.T) { - RunMakeDatabaseConfig(t, dbt) - }) - t.Run(dbt+" run database migrations", func(t *testing.T) { - RunDatabaseMigrations(t, dbt) - }) - t.Run(dbt+" Sample Data", func(t *testing.T) { - RunInsertSampleData(t) + t.Run(dbt+" Save Config", func(t *testing.T) { + RunSaveConfig(t, dbt) }) t.Run(dbt+" Load Configs", func(t *testing.T) { RunLoadConfig(t) + t.Log(core.Configs) + }) + t.Run(dbt+" Connect to Database", func(t *testing.T) { + err := core.Configs.Connect(false, dir) + assert.Nil(t, err) + }) + t.Run(dbt+" Drop Database", func(t *testing.T) { + RunDropDatabase(t) + }) + t.Run(dbt+" Connect to Database Again", func(t *testing.T) { + err := core.Configs.Connect(false, dir) + assert.Nil(t, err) + }) + t.Run(dbt+" Inserting Database Structure", func(t *testing.T) { + RunCreateSchema(t, dbt) + }) + t.Run(dbt+" Inserting Seed Data", func(t *testing.T) { + RunInsertSampleData(t) + }) + t.Run(dbt+" Connect to Database Again", func(t *testing.T) { + err := core.Configs.Connect(false, dir) + assert.Nil(t, err) + }) + t.Run(dbt+" Run Database Migrations", func(t *testing.T) { + RunDatabaseMigrations(t, dbt) }) t.Run(dbt+" Select Core", func(t *testing.T) { RunSelectCoreMYQL(t, dbt) + t.Log(core.CoreApp) }) t.Run(dbt+" Select Services", func(t *testing.T) { RunSelectAllMysqlServices(t) }) t.Run(dbt+" Select Comms", func(t *testing.T) { + t.SkipNow() RunSelectAllMysqlCommunications(t) }) t.Run(dbt+" Create Users", func(t *testing.T) { @@ -131,7 +154,7 @@ func TestRunAll(t *testing.T) { t.Run(dbt+" Create Failing Service", func(t *testing.T) { RunBadService_Create(t) }) - t.Run(dbt+" Check Service", func(t *testing.T) { + t.Run(dbt+" Check Bad Service", func(t *testing.T) { RunBadService_Check(t) }) t.Run(dbt+" Select Hits", func(t *testing.T) { @@ -183,35 +206,24 @@ func TestRunAll(t *testing.T) { RunSettingsHandler(t) }) t.Run(dbt+" Cleanup", func(t *testing.T) { - //Cleanup(t) + core.Configs.Close() + core.DbSession = nil + //Clean() }) + //<-done + } } -func Cleanup(t *testing.T) { - core.DbSession.ClearCache() - err := core.DbSession.Close() - assert.Nil(t, err) -} - -func RunMakeDatabaseConfig(t *testing.T, db string) { +func RunSaveConfig(t *testing.T, db string) { + var err error port := 5432 if db == "mysql" { port = 3306 } - - //Project string `yaml:"-"` - //Description string `yaml:"-"` - //Domain string `yaml:"-"` - //Username string `yaml:"-"` - //Password string `yaml:"-"` - //Email string `yaml:"-"` - //Error error `yaml:"-"` - //Location string `yaml:"location"` - - config := &core.DbConfig{&types.DbConfig{ + core.Configs = &core.DbConfig{DbConfig: &types.DbConfig{ DbConn: db, DbHost: os.Getenv("DB_HOST"), DbUser: os.Getenv("DB_USER"), @@ -227,39 +239,49 @@ func RunMakeDatabaseConfig(t *testing.T, db string) { Error: nil, Location: dir, }} - err := config.Save() + core.Configs, err = core.Configs.Save() assert.Nil(t, err) +} - _, err = core.LoadConfig() +func RunCreateSchema(t *testing.T, db string) { + err := core.Configs.Connect(false, dir) assert.Nil(t, err) - assert.Equal(t, db, core.Configs.Connection) - - err = core.DbConnection(core.Configs.Connection, false, dir) + err = core.Configs.CreateDatabase() assert.Nil(t, err) } func RunDatabaseMigrations(t *testing.T, db string) { - err := core.RunDatabaseUpgrades() + err := core.Configs.MigrateDatabase() assert.Nil(t, err) } func RunInsertSampleData(t *testing.T) { - err := core.LoadSampleData() - assert.Nil(t, err) + core.Configs.SeedDatabase() + //assert.Nil(t, err) } func RunLoadConfig(t *testing.T) { var err error - core.Configs, err = core.LoadConfig() + core.Configs, err = core.LoadConfig(dir) + t.Log(core.Configs) assert.Nil(t, err) assert.NotNil(t, core.Configs) } +func RunDropDatabase(t *testing.T) { + err := core.Configs.DropDatabase() + assert.Nil(t, err) +} + func RunSelectCoreMYQL(t *testing.T, db string) { var err error core.CoreApp, err = core.SelectCore() + if err != nil { + t.FailNow() + } assert.Nil(t, err) - assert.Equal(t, "Testing "+db, core.CoreApp.Name) + t.Log("core: ", core.CoreApp.Core) + assert.Equal(t, "Awesome Status", core.CoreApp.Name) assert.Equal(t, db, core.CoreApp.DbConnection) assert.NotEmpty(t, core.CoreApp.ApiKey) assert.NotEmpty(t, core.CoreApp.ApiSecret) @@ -270,12 +292,12 @@ func RunSelectAllMysqlServices(t *testing.T) { var err error services, err := core.CoreApp.SelectAllServices() assert.Nil(t, err) - assert.Equal(t, 5, len(services)) + assert.Equal(t, 18, len(services)) } func RunSelectAllMysqlCommunications(t *testing.T) { var err error - notifiers.Collections = core.DbSession.Collection("communication") + notifiers.Collections = core.DbSession.Table("communication").Model(¬ifiers.Notification{}) comms := notifiers.Load() assert.Nil(t, err) assert.Equal(t, 3, len(comms)) @@ -284,19 +306,19 @@ func RunSelectAllMysqlCommunications(t *testing.T) { func RunUser_SelectAll(t *testing.T) { users, err := core.SelectAllUsers() assert.Nil(t, err) - assert.Equal(t, 2, len(users)) + assert.Equal(t, 3, len(users)) } func RunUser_Create(t *testing.T) { user := core.ReturnUser(&types.User{ - Username: "admin", - Password: "admin", - Email: "info@testuser.com", + Username: "hunterlong", + Password: "password123", + Email: "info@gmail.com", Admin: true, }) id, err := user.Create() assert.Nil(t, err) - assert.Equal(t, int64(1), id) + assert.Equal(t, int64(2), id) user2 := core.ReturnUser(&types.User{ Username: "superadmin", Password: "admin", @@ -305,7 +327,7 @@ func RunUser_Create(t *testing.T) { }) id, err = user2.Create() assert.Nil(t, err) - assert.Equal(t, int64(2), id) + assert.Equal(t, int64(3), id) } func RunUser_Update(t *testing.T) { @@ -342,7 +364,10 @@ func RunSelectAllServices(t *testing.T) { var err error services, err := core.CoreApp.SelectAllServices() assert.Nil(t, err) - assert.Equal(t, 5, len(services)) + assert.Equal(t, 18, len(services)) + for _, s := range services { + assert.NotEmpty(t, s.CreatedAt) + } } func RunOneService_Check(t *testing.T) { @@ -365,8 +390,7 @@ func RunService_Create(t *testing.T) { }) id, err := service.Create() assert.Nil(t, err) - assert.Equal(t, int64(6), id) - t.Log(service) + assert.Equal(t, int64(19), id) } func RunService_ToJSON(t *testing.T) { @@ -379,15 +403,20 @@ func RunService_ToJSON(t *testing.T) { func RunService_AvgTime(t *testing.T) { service := core.SelectService(1) assert.NotNil(t, service) - avg := service.AvgUptime() + avg := service.AvgUptime24() assert.Equal(t, "100", avg) } func RunService_Online24(t *testing.T) { service := core.SelectService(1) assert.NotNil(t, service) - online := service.Online24() - assert.Equal(t, float32(100), online) + online := service.OnlineSince(SERVICE_SINCE) + assert.Equal(t, float32(80), online) + + service = core.SelectService(18) + assert.NotNil(t, service) + online = service.OnlineSince(SERVICE_SINCE) + assert.Equal(t, float32(0), online) } func RunService_GraphData(t *testing.T) { @@ -413,13 +442,13 @@ func RunBadService_Create(t *testing.T) { }) id, err := service.Create() assert.Nil(t, err) - assert.Equal(t, int64(7), id) + assert.Equal(t, int64(20), id) } func RunBadService_Check(t *testing.T) { - service := core.SelectService(7) + service := core.SelectService(18) assert.NotNil(t, service) - assert.Equal(t, "Bad Service", service.Name) + assert.Equal(t, "Failing URL", service.Name) for i := 0; i <= 10; i++ { service.Check(true) } @@ -431,9 +460,7 @@ func RunDeleteService(t *testing.T) { assert.NotNil(t, service) assert.Equal(t, "JSON API Tester", service.Name) assert.True(t, service.IsRunning()) - t.Log(service.Running) err := service.Delete() - t.Log(service.Running) assert.False(t, service.IsRunning()) assert.Nil(t, err) } @@ -441,13 +468,10 @@ func RunDeleteService(t *testing.T) { func RunCreateService_Hits(t *testing.T) { services := core.CoreApp.Services() assert.NotNil(t, services) - assert.Equal(t, 6, len(services)) - for i := 0; i <= 15; i++ { - for _, s := range services { - var service *core.Service - service = s.Check(true) - assert.NotNil(t, service) - } + assert.Equal(t, 19, len(services)) + for _, s := range services { + service := s.Check(true) + assert.NotNil(t, service) } } @@ -460,10 +484,10 @@ func RunService_Hits(t *testing.T) { } func RunService_Failures(t *testing.T) { - service := core.SelectService(7) + service := core.SelectService(18) assert.NotNil(t, service) - assert.Equal(t, "Bad Service", service.Name) - assert.NotEmpty(t, service.Failures) + assert.Equal(t, "Failing URL", service.Name) + assert.NotEmpty(t, service.AllFailures()) } func RunService_LimitedHits(t *testing.T) { @@ -479,7 +503,7 @@ func RunIndexHandler(t *testing.T) { assert.Nil(t, err) rr := httptest.NewRecorder() route.ServeHTTP(rr, req) - assert.True(t, strings.Contains(rr.Body.String(), "This is a test of Statup.io!")) + assert.True(t, strings.Contains(rr.Body.String(), "Awesome")) assert.True(t, strings.Contains(rr.Body.String(), "footer")) } @@ -499,7 +523,7 @@ func RunPrometheusHandler(t *testing.T) { rr := httptest.NewRecorder() route.ServeHTTP(rr, req) t.Log(rr.Body.String()) - assert.True(t, strings.Contains(rr.Body.String(), "statup_total_services 6")) + assert.True(t, strings.Contains(rr.Body.String(), "statup_total_services 19")) assert.True(t, handlers.IsAuthenticated(req)) } @@ -515,7 +539,7 @@ func RunFailingPrometheusHandler(t *testing.T) { func RunLoginHandler(t *testing.T) { form := url.Values{} form.Add("username", "admin") - form.Add("password", "admin") + form.Add("password", "password123") req, err := http.NewRequest("POST", "/dashboard", strings.NewReader(form.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") assert.Nil(t, err) diff --git a/core/checker.go b/core/checker.go index 2a0c5c16..9848c020 100644 --- a/core/checker.go +++ b/core/checker.go @@ -210,7 +210,9 @@ func RecordSuccess(s *Service) { s.Online = true s.LastOnline = time.Now() data := &types.Hit{ - Latency: s.Latency, + Service: s.Id, + Latency: s.Latency, + CreatedAt: time.Now(), } utils.Log(1, fmt.Sprintf("Service %v Successful: %0.2f ms", s.Name, data.Latency*1000)) s.CreateHit(data) diff --git a/core/checkin.go b/core/checkin.go index 8173d31a..e2dfe1e8 100644 --- a/core/checkin.go +++ b/core/checkin.go @@ -48,26 +48,25 @@ func FindCheckin(api string) *types.Checkin { func (s *Service) AllCheckins() []*types.Checkin { var checkins []*types.Checkin - col := DbSession.Collection("checkins").Find("service", s.Id).OrderBy("-id") - col.All(&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() - uuid, err := DbSession.Collection("checkins").Insert(u) - if uuid == nil { - utils.Log(2, err) - return 0, err + row := checkinDB().Create(u) + if row.Error == nil { + utils.Log(2, row.Error) + return 0, row.Error } - fmt.Println("new checkin: ", uuid) - return uuid.(int64), err + return u.Id, row.Error } func SelectCheckinApi(api string) *Checkin { var checkin *Checkin - DbSession.Collection("checkins").Find("api", api).One(&checkin) + checkinDB().Where("api = ?", api).Find(&checkin) return checkin } diff --git a/core/configs.go b/core/configs.go index b04665b6..23068cb5 100644 --- a/core/configs.go +++ b/core/configs.go @@ -23,29 +23,28 @@ import ( "github.com/hunterlong/statup/utils" "io/ioutil" "os" - "time" ) -func LoadConfig() (*types.Config, error) { +func LoadConfig(directory string) (*DbConfig, error) { + var configs *types.DbConfig if os.Getenv("DB_CONN") != "" { utils.Log(1, "DB_CONN environment variable was found, waiting for database...") return LoadUsingEnv() } - Configs = new(types.Config) - file, err := ioutil.ReadFile(utils.Directory + "/config.yml") + file, err := ioutil.ReadFile(directory + "/config.yml") if err != nil { - return nil, errors.New("config.yml file not found - starting in setup mode") + return nil, errors.New("config.yml file not found at " + directory + "/config.yml - starting in setup mode") } - err = yaml.Unmarshal(file, &Configs) + err = yaml.Unmarshal(file, &configs) if err != nil { return nil, err } - CoreApp.DbConnection = Configs.Connection + Configs = &DbConfig{configs} return Configs, err } -func LoadUsingEnv() (*types.Config, error) { - Configs = new(types.Config) +func LoadUsingEnv() (*DbConfig, error) { + Configs = new(DbConfig) if os.Getenv("DB_CONN") == "" { return nil, errors.New("Missing DB_CONN environment variable") } @@ -61,12 +60,12 @@ func LoadUsingEnv() (*types.Config, error) { if os.Getenv("DB_DATABASE") == "" { return nil, errors.New("Missing DB_DATABASE environment variable") } - Configs.Connection = os.Getenv("DB_CONN") - Configs.Host = os.Getenv("DB_HOST") - Configs.Port = os.Getenv("DB_PORT") - Configs.User = os.Getenv("DB_USER") + Configs.DbConn = os.Getenv("DB_CONN") + Configs.DbHost = os.Getenv("DB_HOST") + Configs.DbPort = int(utils.StringInt(os.Getenv("DB_PORT"))) + Configs.DbUser = os.Getenv("DB_USER") Configs.Password = os.Getenv("DB_PASS") - Configs.Database = os.Getenv("DB_DATABASE") + Configs.DbData = os.Getenv("DB_DATABASE") CoreApp.DbConnection = os.Getenv("DB_CONN") CoreApp.Name = os.Getenv("NAME") CoreApp.Domain = os.Getenv("DOMAIN") @@ -89,32 +88,19 @@ func LoadUsingEnv() (*types.Config, error) { Email: "info@localhost.com", }} - err := DbConnection(dbConfig.DbConn, true, utils.Directory) + err := dbConfig.Connect(true, utils.Directory) if err != nil { utils.Log(4, err) return nil, err } - exists, err := DbSession.Collection("core").Find().Exists() + exists := DbSession.HasTable("core") if !exists { - utils.Log(1, fmt.Sprintf("Core database does not exist, creating now!")) - DropDatabase() - CreateDatabase() + dbConfig.DropDatabase() + dbConfig.CreateDatabase() - CoreApp = &Core{Core: &types.Core{ - Name: dbConfig.Project, - Description: dbConfig.Description, - Config: "config.yml", - ApiKey: utils.NewSHA1Hash(9), - ApiSecret: utils.NewSHA1Hash(16), - Domain: dbConfig.Domain, - MigrationId: time.Now().Unix(), - }} - - CoreApp.DbConnection = dbConfig.DbConn - - err := InsertCore(CoreApp) + CoreApp, err = dbConfig.InsertCore() if err != nil { utils.Log(3, err) } diff --git a/core/core.go b/core/core.go index 0de215d5..2d5a50d0 100644 --- a/core/core.go +++ b/core/core.go @@ -34,10 +34,10 @@ type Core struct { } var ( - Configs *types.Config - 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() { @@ -51,12 +51,6 @@ func NewCore() *Core { return CoreApp } -func InsertCore(c *Core) error { - col := DbSession.Collection("core") - _, err := col.Insert(c.Core) - return err -} - func (c *Core) ToCore() *types.Core { return c.Core } @@ -72,25 +66,27 @@ func InitApp() { func InsertNotifierDB() error { if DbSession == nil { - err := DbConnection(CoreApp.DbConnection, false, utils.Directory) + err := Configs.Connect(false, utils.Directory) if err != nil { return errors.New("database connection has not been created") } } - notifiers.Collections = DbSession.Collection("communication") + notifiers.Collections = commDB() return nil } +// UpdateCore will update the CoreApp variable inside of the 'core' table in database func UpdateCore(c *Core) (*Core, error) { - res := DbSession.Collection("core").Find().Limit(1) - err := res.Update(c.Core) - return c, err + 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 "" @@ -98,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 "" @@ -105,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 "" @@ -112,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 { @@ -121,49 +121,42 @@ func (c Core) AllOnline() bool { return true } -func SelectLastMigration() (int64, error) { - var c *types.Core - if DbSession == nil { - return 0, errors.New("Database connection has not been created yet") - } - err := DbSession.Collection("core").Find().One(&c) - if err != nil { - return 0, err - } - return c.MigrationId, err -} - +// SelectCore will return the CoreApp global variable and the settings/configs for Statup func SelectCore() (*Core, error) { - var c *types.Core - exists := DbSession.Collection("core").Exists() + if DbSession == nil { + return nil, errors.New("database has not been initiated yet.") + } + exists := DbSession.HasTable("core") if !exists { return nil, errors.New("core database has not been setup yet.") } - err := DbSession.Collection("core").Find().One(&c) - if err != nil { - return nil, err + db := coreDB().First(&CoreApp) + if db.Error != nil { + return nil, db.Error } - CoreApp.Core = c - CoreApp.DbConnection = Configs.Connection + CoreApp.DbConnection = Configs.DbConn CoreApp.Version = VERSION CoreApp.SelectAllServices() if os.Getenv("USE_CDN") == "true" { CoreApp.UseCdn = true } //store = sessions.NewCookieStore([]byte(core.ApiSecret)) - return CoreApp, err + 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)) + 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 070d893e..66f4df9f 100644 --- a/core/core_test.go +++ b/core/core_test.go @@ -24,9 +24,11 @@ import ( ) var ( - testCore *Core - testConfig *DbConfig - dir string + dir string +) + +const ( + SERVICE_SINCE = "2018-08-30T10:42:08-07:00" // "2006-01-02T15:04:05Z07:00" ) func init() { @@ -36,51 +38,68 @@ func init() { } func TestNewCore(t *testing.T) { - testCore = NewCore() - assert.NotNil(t, testCore) - testCore.Name = "Tester" + utils.DeleteFile(dir + "/config.yml") + utils.DeleteFile(dir + "/statup.db") + CoreApp = NewCore() + assert.NotNil(t, CoreApp) + CoreApp.Name = "Tester" } func TestDbConfig_Save(t *testing.T) { - testConfig = &DbConfig{&types.DbConfig{ + var err error + Configs = &DbConfig{&types.DbConfig{ DbConn: "sqlite", Project: "Tester", Location: dir, }} - err := testConfig.Save() + Configs, err = Configs.Save() assert.Nil(t, err) + assert.Equal(t, "sqlite", Configs.DbConn) + assert.NotEmpty(t, Configs.ApiKey) + assert.NotEmpty(t, Configs.ApiSecret) +} + +func TestLoadDbConfig(t *testing.T) { + Configs, err := LoadConfig(dir) + assert.Nil(t, err) + assert.Equal(t, "sqlite", Configs.DbConn) } func TestDbConnection(t *testing.T) { - err := DbConnection(testConfig.DbConn, false, dir) + err := Configs.Connect(false, dir) assert.Nil(t, err) } -func TestCreateDatabase(t *testing.T) { - err := CreateDatabase() +func TestDropDatabase(t *testing.T) { + err := Configs.DropDatabase() assert.Nil(t, err) } -func TestInsertCore(t *testing.T) { - err := InsertCore(testCore) +func TestSeedSchemaDatabase(t *testing.T) { + err := Configs.CreateDatabase() assert.Nil(t, err) } +func TestMigrateDatabase(t *testing.T) { + err := Configs.MigrateDatabase() + assert.Nil(t, err) +} + +func TestSeedDatabase(t *testing.T) { + _, _, err := Configs.SeedDatabase() + assert.Nil(t, err) +} + +func TestReLoadDbConfig(t *testing.T) { + err := Configs.Connect(false, dir) + assert.Nil(t, err) + assert.Equal(t, "sqlite", Configs.DbConn) +} + func TestSelectCore(t *testing.T) { core, err := SelectCore() assert.Nil(t, err) - assert.Equal(t, "Tester", core.Name) -} - -func TestSampleData(t *testing.T) { - err := LoadSampleData() - assert.Nil(t, err) -} - -func TestSelectLastMigration(t *testing.T) { - id, err := SelectLastMigration() - assert.Nil(t, err) - assert.NotZero(t, id) + assert.Equal(t, "Awesome Status", core.Name) } func TestInsertNotifierDB(t *testing.T) { diff --git a/core/database.go b/core/database.go index 27bab018..eac12e3a 100644 --- a/core/database.go +++ b/core/database.go @@ -18,95 +18,161 @@ package core import ( "fmt" "github.com/go-yaml/yaml" - "github.com/hunterlong/statup/source" + "github.com/hunterlong/statup/notifiers" "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/mssql" + _ "github.com/jinzhu/gorm/dialects/mysql" + _ "github.com/jinzhu/gorm/dialects/postgres" + _ "github.com/jinzhu/gorm/dialects/sqlite" "os" - "strings" "time" - "upper.io/db.v3" - "upper.io/db.v3/lib/sqlbuilder" - "upper.io/db.v3/mysql" - "upper.io/db.v3/postgresql" - "upper.io/db.v3/sqlite" ) var ( - sqliteSettings sqlite.ConnectionURL - postgresSettings postgresql.ConnectionURL - mysqlSettings mysql.ConnectionURL - DbSession sqlbuilder.Database - currentMigration int64 + DbSession *gorm.DB ) +func failuresDB() *gorm.DB { + db := DbSession.Model(&types.Failure{}) + if os.Getenv("GO_ENV") == "test" { + return db.Debug() + } + return db +} + +func (s *Service) allHits() *gorm.DB { + var hits []*Hit + return servicesDB().Find(s).Related(&hits) +} + +func hitsDB() *gorm.DB { + db := DbSession.Model(&types.Hit{}) + if os.Getenv("GO_ENV") == "test" { + return db.Debug() + } + return db +} + +func servicesDB() *gorm.DB { + db := DbSession.Model(&types.Service{}) + if os.Getenv("GO_ENV") == "test" { + return db.Debug() + } + return db +} + +func coreDB() *gorm.DB { + db := DbSession.Table("core").Model(&CoreApp) + if os.Getenv("GO_ENV") == "test" { + return db.Debug() + } + return db +} + +func usersDB() *gorm.DB { + db := DbSession.Model(&types.User{}) + if os.Getenv("GO_ENV") == "test" { + return db.Debug() + } + return db +} + +func commDB() *gorm.DB { + db := DbSession.Table("communication").Model(¬ifiers.Notification{}) + if os.Getenv("GO_ENV") == "test" { + return db.Debug() + } + return db +} + +func checkinDB() *gorm.DB { + if os.Getenv("GO_ENV") == "test" { + return DbSession.Model(&types.Checkin{}).Debug() + } + return DbSession.Model(&types.Checkin{}) +} + type DbConfig struct { *types.DbConfig } -func DbConnection(dbType string, retry bool, location string) error { +// Close shutsdown the database connection +func (db *DbConfig) Close() error { + 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, + Description: db.Description, + Config: "config.yml", + ApiKey: utils.NewSHA1Hash(9), + ApiSecret: utils.NewSHA1Hash(16), + Domain: db.Domain, + MigrationId: time.Now().Unix(), + }} + CoreApp.DbConnection = db.DbConn + query := coreDB().Create(&CoreApp) + 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 dbType == "sqlite" { - sqliteSettings = sqlite.ConnectionURL{ - Database: location + "/statup.db", + if DbSession != nil { + DbSession = nil + } + var conn, dbType string + dbType = Configs.DbConn + switch dbType { + case "sqlite": + conn = utils.Directory + "/statup.db" + dbType = "sqlite3" + case "mysql": + if Configs.DbPort == 0 { + Configs.DbPort = 3306 } - DbSession, err = sqlite.Open(sqliteSettings) - if err != nil { + 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) + 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) + 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)) + return db.waitForDb() + } else { + fmt.Println("ERROR:", err) return err } - } else if dbType == "mysql" { - if Configs.Port == "" { - Configs.Port = "3306" - } - host := fmt.Sprintf("%v:%v", Configs.Host, Configs.Port) - mysqlSettings = mysql.ConnectionURL{ - Database: Configs.Database, - Host: host, - User: Configs.User, - Password: Configs.Password, - Options: map[string]string{"parseTime": "true", "charset": "utf8"}, - } - DbSession, err = mysql.Open(mysqlSettings) - if err != nil { - if retry { - utils.Log(1, fmt.Sprintf("Database connection to '%v' is not available, trying again in 5 seconds...", host)) - return waitForDb(dbType) - } else { - return err - } - } - } else { - if Configs.Port == "" { - Configs.Port = "5432" - } - host := fmt.Sprintf("%v:%v", Configs.Host, Configs.Port) - postgresSettings = postgresql.ConnectionURL{ - Database: Configs.Database, - Host: host, - User: Configs.User, - Password: Configs.Password, - } - DbSession, err = postgresql.Open(postgresSettings) - if err != nil { - if retry { - utils.Log(1, fmt.Sprintf("Database connection to '%v' is not available, trying again in 5 seconds...", host)) - return waitForDb(dbType) - } else { - return err - } - } } - err = DbSession.Ping() + err = DbSession.DB().Ping() if err == nil { - utils.Log(1, fmt.Sprintf("Database connection to '%v' was successful.", DbSession.Name())) + utils.Log(1, fmt.Sprintf("Database connection to '%v' was successful.", Configs.DbData)) } return err } -func waitForDb(dbType string) error { +func (db *DbConfig) waitForDb() error { time.Sleep(5 * time.Second) - return DbConnection(dbType, true, utils.Directory) + 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...") @@ -116,14 +182,17 @@ 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")) - _, err := DbSession.Exec(db.Raw(sql)) - if err != nil { - utils.Log(2, err) + db := DbSession.Raw(sql) + defer db.Close() + if db.Error != nil { + utils.Log(2, db.Error) } } +// Update will save the config.yml file func (c *DbConfig) Update() error { var err error config, err := os.Create(utils.Directory + "/config.yml") @@ -141,162 +210,115 @@ func (c *DbConfig) Update() error { return err } -func (c *DbConfig) Save() error { +// 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") if err != nil { utils.Log(4, err) - return err + return nil, err } + c.ApiKey = utils.NewSHA1Hash(16) + c.ApiSecret = utils.NewSHA1Hash(16) data, err := yaml.Marshal(c.DbConfig) if err != nil { utils.Log(3, err) - return err + return nil, err } config.WriteString(string(data)) - config.Close() - - Configs, err = LoadConfig() - if err != nil { - utils.Log(3, err) - return err - } - err = DbConnection(Configs.Connection, false, c.Location) - if err != nil { - utils.Log(4, err) - return err - } - DropDatabase() - CreateDatabase() + defer config.Close() + 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, Description: c.Description, Config: "config.yml", - ApiKey: utils.NewSHA1Hash(9), - ApiSecret: utils.NewSHA1Hash(16), + ApiKey: c.ApiKey, + ApiSecret: c.ApiSecret, Domain: c.Domain, MigrationId: time.Now().Unix(), } - col := DbSession.Collection("core") - _, err = col.Insert(newCore) - if err == nil { + db := coreDB().Create(&newCore) + if db.Error == nil { CoreApp = &Core{Core: newCore} } - - CoreApp, err = SelectCore() + CoreApp, err := SelectCore() if err != nil { utils.Log(4, err) } - CoreApp.DbConnection = c.DbConn - c.ApiKey = CoreApp.ApiKey - c.ApiSecret = CoreApp.ApiSecret - return err + return CoreApp } -func versionHigher(migrate int64) bool { - if CoreApp.MigrationId < migrate { - return true +// 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 + var cmd string + switch db.DbConn { + case "sqlite": + cmd = fmt.Sprintf("cat %v/dev/sqlite_seed.sql | sqlite3 %v/statup.db", dir, dir) + case "mysql": + cmd = fmt.Sprintf("mysql -h %v -P %v -u %v --password=%v %v < %v/dev/mysql_seed.sql", Configs.DbHost, 3306, Configs.DbUser, Configs.DbPass, Configs.DbData, dir) + case "postgres": + cmd = fmt.Sprintf("PGPASSWORD=%v psql -U %v -h %v -d %v -1 -f %v/dev/postgres_seed.sql", db.DbPass, db.DbUser, db.DbHost, db.DbData, dir) } - return false + out, outErr, err := utils.Command(cmd) + return out, outErr, err } -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)) - _, err := DbSession.Exec(db.Raw(m + ";")) - ran++ - if err != nil { - utils.Log(2, err) - 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 { - panic(err) - } - CoreApp.MigrationId = currentMigration - UpdateCore(CoreApp) - } - return err -} - -func DropDatabase() error { +// DropDatabase will DROP each table Statup created +func (db *DbConfig) DropDatabase() error { utils.Log(1, "Dropping Database Tables...") - down, err := source.SqlBox.String("down.sql") - if err != nil { - return err - } - requests := strings.Split(down, ";") - for _, request := range requests { - _, err := DbSession.Exec(request) - if err != nil { - utils.Log(2, err) - } - } - return err + err := DbSession.DropTableIfExists("checkins") + err = DbSession.DropTableIfExists("communication") + err = DbSession.DropTableIfExists("core") + err = DbSession.DropTableIfExists("failures") + err = DbSession.DropTableIfExists("hits") + err = DbSession.DropTableIfExists("services") + err = DbSession.DropTableIfExists("users") + return err.Error } -func CreateDatabase() error { +// CreateDatabase will CREATE TABLES for each of the Statup elements +func (db *DbConfig) CreateDatabase() error { utils.Log(1, "Creating Database Tables...") - sql := "postgres_up.sql" - if CoreApp.DbConnection == "mysql" { - sql = "mysql_up.sql" - } else if CoreApp.DbConnection == "sqlite" { - sql = "sqlite_up.sql" - } - up, err := source.SqlBox.String(sql) - requests := strings.Split(up, ";") - for _, request := range requests { - _, err := DbSession.Exec(request) - if err != nil { - utils.Log(2, err) + err := DbSession.CreateTable(&types.Checkin{}) + err = DbSession.Table("communication").CreateTable(¬ifiers.Notification{}) + err = DbSession.Table("core").CreateTable(&types.Core{}) + err = DbSession.CreateTable(&types.Failure{}) + err = DbSession.CreateTable(&types.Hit{}) + err = DbSession.CreateTable(&types.Service{}) + err = DbSession.CreateTable(&types.User{}) + utils.Log(1, "Statup Database Created") + 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...") + + tx := DbSession.Begin() + defer func() { + if r := recover(); r != nil { + tx.Rollback() } + }() + if tx.Error != nil { + return tx.Error } - //secret := NewSHA1Hash() - //db.QueryRow("INSERT INTO core (secret, version) VALUES ($1, $2);", secret, VERSION).Scan() - utils.Log(1, "Database Created") - //SampleData() - return err + 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 tx.Commit().Error } func (c *DbConfig) Clean() *DbConfig { diff --git a/core/events.go b/core/events.go index 8037316a..009a615d 100644 --- a/core/events.go +++ b/core/events.go @@ -19,12 +19,12 @@ import ( "github.com/fatih/structs" "github.com/hunterlong/statup/notifiers" "github.com/hunterlong/statup/types" - "upper.io/db.v3/lib/sqlbuilder" + "github.com/jinzhu/gorm" ) -func OnLoad(db sqlbuilder.Database) { +func OnLoad(db *gorm.DB) { for _, p := range CoreApp.AllPlugins { - p.OnLoad(db) + p.OnLoad(*db) } } diff --git a/core/export.go b/core/export.go index 54a0fe50..1513a597 100644 --- a/core/export.go +++ b/core/export.go @@ -25,7 +25,11 @@ import ( ) func injectDatabase() { - DbConnection(Configs.Connection, false, utils.Directory) + Configs.Connect(false, utils.Directory) +} + +func GenerateSeed() { + } func ExportIndexHTML() string { diff --git a/core/failures.go b/core/failures.go index 9d96f561..7c5437ad 100644 --- a/core/failures.go +++ b/core/failures.go @@ -32,23 +32,19 @@ func (s *Service) CreateFailure(f *types.Failure) (int64, error) { f.CreatedAt = time.Now() f.Service = s.Id s.Failures = append(s.Failures, f) - col := DbSession.Collection("failures") - uuid, err := col.Insert(f) - if err != nil { - utils.Log(3, err) - return 0, err + row := failuresDB().Create(f) + if row.Error != nil { + utils.Log(3, row.Error) + return 0, row.Error } - if uuid == nil { - return 0, err - } - return uuid.(int64), err + return f.Id, row.Error } func (s *Service) AllFailures() []*types.Failure { var fails []*types.Failure - col := DbSession.Collection("failures").Find("service", s.Id).OrderBy("-id") - err := col.All(&fails) - if err != nil { + col := failuresDB().Where("service = ?", s.Id).Order("id desc") + err := col.Find(&fails) + if err.Error != nil { utils.Log(3, fmt.Sprintf("Issue getting failures for service %v, %v", s.Name, err)) return nil } @@ -56,8 +52,8 @@ func (s *Service) AllFailures() []*types.Failure { } func (u *Service) DeleteFailures() { - _, err := DbSession.Exec(`DELETE FROM failures WHERE service = ?`, u.Id) - if err != nil { + err := DbSession.Exec(`DELETE FROM failures WHERE service = ?`, u.Id) + if err.Error != nil { utils.Log(3, fmt.Sprintf("failed to delete all failures: %v", err)) } u.Failures = nil @@ -65,48 +61,48 @@ func (u *Service) DeleteFailures() { func (s *Service) LimitedFailures() []*Failure { var failArr []*Failure - col := DbSession.Collection("failures").Find("service", s.Id).OrderBy("-id").Limit(10) - col.All(&failArr) + col := failuresDB().Where("service = ?", s.Id).Order("id desc").Limit(10) + col.Find(&failArr) 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 } func (f *Failure) Delete() error { - col := DbSession.Collection("failures").Find("id", f.Id) - return col.Delete() + db := failuresDB().Delete(f) + return db.Error } func CountFailures() uint64 { - col := DbSession.Collection("failures").Find() - amount, err := col.Count() - if err != nil { - utils.Log(2, err) + var count uint64 + err := failuresDB().Count(&count) + if err.Error != nil { + utils.Log(2, err.Error) return 0 } - return amount + return count +} + +func (s *Service) TotalFailures24() (uint64, error) { + ago := time.Now().Add(-24 * time.Hour) + return s.TotalFailuresSince(ago) } func (s *Service) TotalFailures() (uint64, error) { - col := DbSession.Collection("failures").Find("service", s.Id) - amount, err := col.Count() - return amount, err + var count uint64 + rows := failuresDB().Where("service = ?", s.Id) + err := rows.Count(&count) + return count, err.Error } -func (s *Service) TotalFailures24Hours() (uint64, error) { - col := DbSession.Collection("failures").Find("service", s.Id) - amount, err := col.Count() - return amount, err +func (s *Service) TotalFailuresSince(ago time.Time) (uint64, error) { + var count uint64 + rows := failuresDB().Where("service = ? AND created_at > ?", s.Id, ago.Format("2006-01-02 15:04:05")) + err := rows.Count(&count) + return count, err.Error } func (f *Failure) ParseError() string { diff --git a/core/hits.go b/core/hits.go index 9b2f38a3..b8b9b9f5 100644 --- a/core/hits.go +++ b/core/hits.go @@ -19,40 +19,33 @@ import ( "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" "time" - "upper.io/db.v3" ) type Hit struct { *types.Hit } -func hitCol() db.Collection { - return DbSession.Collection("hits") -} - func (s *Service) CreateHit(h *types.Hit) (int64, error) { - h.CreatedAt = time.Now() - h.Service = s.Id - uuid, err := hitCol().Insert(h) - if uuid == nil { - utils.Log(2, err) - return 0, err + db := hitsDB().Create(h) + if db.Error != nil { + utils.Log(2, db.Error) + return 0, db.Error } - return uuid.(int64), err + return h.Id, db.Error } func (s *Service) Hits() ([]*Hit, error) { var hits []*Hit - col := hitCol().Find("service", s.Id).OrderBy("-id") - err := col.All(&hits) - return hits, err + col := hitsDB().Where("service = ?", s.Id).Order("id desc") + err := col.Find(&hits) + return hits, err.Error } func (s *Service) LimitedHits() ([]*Hit, error) { var hits []*Hit - col := hitCol().Find("service", s.Id).OrderBy("-id").Limit(1024) - err := col.All(&hits) - return reverseHits(hits), err + col := hitsDB().Where("service = ?", s.Id).Order("id desc").Limit(1024) + err := col.Find(&hits) + return reverseHits(hits), err.Error } func reverseHits(input []*Hit) []*Hit { @@ -64,15 +57,23 @@ func reverseHits(input []*Hit) []*Hit { func (s *Service) SelectHitsGroupBy(group string) ([]*Hit, error) { var hits []*Hit - col := hitCol().Find("service", s.Id) - err := col.All(&hits) - return hits, err + col := hitsDB().Where("service = ?", s.Id) + err := col.Find(&hits) + return hits, err.Error } func (s *Service) TotalHits() (uint64, error) { - col := hitCol().Find("service", s.Id) - amount, err := col.Count() - return amount, err + var count uint64 + col := hitsDB().Where("service = ?", s.Id) + err := col.Count(&count) + return count, err.Error +} + +func (s *Service) TotalHitsSince(ago time.Time) (uint64, error) { + var count uint64 + rows := hitsDB().Where("service = ? AND created_at > ?", s.Id, ago.Format("2006-01-02 15:04:05")) + err := rows.Count(&count) + return count, err.Error } func (s *Service) Sum() (float64, error) { diff --git a/core/services.go b/core/services.go index 850a31a2..124ca014 100644 --- a/core/services.go +++ b/core/services.go @@ -22,7 +22,6 @@ import ( "github.com/hunterlong/statup/utils" "strconv" "time" - "upper.io/db.v3" ) type Service struct { @@ -30,16 +29,12 @@ type Service struct { } func ReturnService(s *types.Service) *Service { - return &Service{Service: s} -} - -func serviceCol() db.Collection { - return DbSession.Collection("services") + return &Service{s} } func SelectService(id int64) *Service { for _, s := range CoreApp.Services() { - if s.Id == id { + if s.Service.Id == id { return s } } @@ -49,11 +44,10 @@ func SelectService(id int64) *Service { func (c *Core) SelectAllServices() ([]*types.Service, error) { var services []*types.Service var servs []*types.Service - col := serviceCol().Find().OrderBy("order_id") - err := col.All(&services) - if err != nil { - utils.Log(3, fmt.Sprintf("service error: %v", err)) - return nil, err + 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 } for _, ser := range services { single := ReturnService(ser) @@ -63,7 +57,7 @@ func (c *Core) SelectAllServices() ([]*types.Service, error) { servs = append(servs, single.Service) } CoreApp.SetServices(servs) - return services, err + return services, db.Error } func (s *Service) ToJSON() string { @@ -84,12 +78,17 @@ func (s *Service) AvgTime() float64 { } func (s *Service) Online24() float32 { - total, _ := s.TotalHits() - failed, _ := s.TotalFailures24Hours() + ago := time.Now().Add(-24 * time.Hour) + return s.OnlineSince(ago) +} + +func (s *Service) OnlineSince(ago time.Time) float32 { + failed, _ := s.TotalFailuresSince(ago) if failed == 0 { s.Online24Hours = 100.00 return s.Online24Hours } + total, _ := s.TotalHitsSince(ago) if total == 0 { s.Online24Hours = 0 return s.Online24Hours @@ -150,16 +149,17 @@ func (s *Service) GraphData() string { var d []*DateScan since := time.Now().Add(time.Hour*-24 + time.Minute*0 + time.Second*0) sql := GroupDataBy("hits", s.Id, since, "minute") - dated, err := DbSession.Query(db.Raw(sql)) + rows, err := DbSession.Raw(sql).Rows() + defer rows.Close() if err != nil { utils.Log(2, err) return "" } - for dated.Next() { + for rows.Next() { gd := new(DateScan) var tt string var ff float64 - err := dated.Scan(&tt, &ff) + err := rows.Scan(&tt, &ff) if err != nil { utils.Log(2, fmt.Sprintf("Issue loading chart data for service %v, %v", s.Name, err)) } @@ -178,27 +178,45 @@ func (s *Service) GraphData() string { return string(data) } -func (s *Service) AvgUptime() string { - failed, _ := s.TotalFailures() - total, _ := s.TotalHits() +func (s *Service) AvgUptime24() string { + ago := time.Now().Add(-24 * time.Hour) + return s.AvgUptime(ago) +} + +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 { @@ -217,25 +235,27 @@ func updateService(service *Service) { } func (u *Service) Delete() error { - res := serviceCol().Find("id", u.Id) - err := res.Delete() - if err != nil { - utils.Log(3, fmt.Sprintf("Failed to delete service %v. %v", u.Name, err)) - return err + err := servicesDB().Delete(u) + if err.Error != nil { + utils.Log(3, fmt.Sprintf("Failed to delete service %v. %v", u.Name, err.Error)) + return err.Error } u.Close() CoreApp.RemoveService(u.index()) OnDeletedService(u) - return err + return err.Error +} + +func (u *Service) UpdateSingle(attr ...interface{}) error { + return servicesDB().Model(u).Update(attr).Error } func (u *Service) Update(restart bool) error { u.CreatedAt = time.Now() - res := serviceCol().Find("id", u.Id) - err := res.Update(u) - if err != nil { + err := servicesDB().Update(u) + if err.Error != nil { utils.Log(3, fmt.Sprintf("Failed to update service %v. %v", u.Name, err)) - return err + return err.Error } if restart { u.Close() @@ -245,20 +265,19 @@ func (u *Service) Update(restart bool) error { go u.CheckQueue(true) } OnUpdateService(u) - return err + return err.Error } func (u *Service) Create() (int64, error) { u.CreatedAt = time.Now() - uuid, err := serviceCol().Insert(u) - if uuid == nil { - utils.Log(3, fmt.Sprintf("Failed to create service %v. %v", u.Name, err)) - return 0, err + db := servicesDB().Create(u) + if db.Error != nil { + utils.Log(3, fmt.Sprintf("Failed to create service %v #%v: %v", u.Name, u.Id, db.Error)) + return 0, db.Error } - u.Id = uuid.(int64) u.Start() CoreApp.AddService(u.Service) - return uuid.(int64), err + return u.Id, nil } func CountOnline() int { diff --git a/core/services_test.go b/core/services_test.go index d1ad758a..b0331736 100644 --- a/core/services_test.go +++ b/core/services_test.go @@ -26,23 +26,29 @@ var ( newServiceId int64 ) +func TestSelectHTTPService(t *testing.T) { + services, err := CoreApp.SelectAllServices() + assert.Nil(t, err) + assert.Equal(t, 18, len(services)) + assert.Equal(t, "Google", services[0].Name) + assert.Equal(t, "http", services[0].Type) +} + func TestSelectAllServices(t *testing.T) { services := CoreApp.Services() for _, s := range services { service := s.Check(true) - assert.True(t, service.Online) + assert.True(t, service.IsRunning()) + t.Logf("ID: %v %v\n", s.Id, s.Name) } - assert.Equal(t, 5, len(services)) -} - -func TestSelectHTTPService(t *testing.T) { - service := SelectService(1) - assert.Equal(t, "Google", service.Name) - assert.Equal(t, "http", service.Type) + assert.Equal(t, 18, len(services)) } func TestSelectTCPService(t *testing.T) { + services := CoreApp.Services() + assert.Equal(t, 18, len(services)) service := SelectService(5) + assert.NotNil(t, service) assert.Equal(t, "Google DNS", service.Name) assert.Equal(t, "tcp", service.Type) } @@ -105,9 +111,14 @@ func TestCheckTCPService(t *testing.T) { } func TestServiceOnline24Hours(t *testing.T) { - service := SelectService(5) - amount := service.Online24() - assert.Equal(t, float32(100), amount) + since, err := time.Parse(time.RFC3339, SERVICE_SINCE) + assert.Nil(t, err) + service := SelectService(1) + assert.Equal(t, float32(83.33), service.OnlineSince(since)) + service2 := SelectService(5) + assert.Equal(t, float32(100), service2.OnlineSince(since)) + service3 := SelectService(18) + assert.Equal(t, float32(0), service3.OnlineSince(since)) } func TestServiceSmallText(t *testing.T) { @@ -117,30 +128,35 @@ func TestServiceSmallText(t *testing.T) { } func TestServiceAvgUptime(t *testing.T) { - service := SelectService(5) - uptime := service.AvgUptime() - assert.Equal(t, "100", uptime) + since, err := time.Parse(time.RFC3339, SERVICE_SINCE) + assert.Nil(t, err) + service := SelectService(1) + assert.Equal(t, "83.33", service.AvgUptime(since)) + service2 := SelectService(5) + assert.Equal(t, "100", service2.AvgUptime(since)) + service3 := SelectService(18) + assert.Equal(t, "0.00", service3.AvgUptime(since)) } func TestServiceHits(t *testing.T) { service := SelectService(5) hits, err := service.Hits() assert.Nil(t, err) - assert.Equal(t, int(2), len(hits)) + assert.Equal(t, int(5), len(hits)) } func TestServiceLimitedHits(t *testing.T) { service := SelectService(5) hits, err := service.LimitedHits() assert.Nil(t, err) - assert.Equal(t, int(2), len(hits)) + assert.Equal(t, int(5), len(hits)) } func TestServiceTotalHits(t *testing.T) { service := SelectService(5) hits, err := service.TotalHits() assert.Nil(t, err) - assert.Equal(t, uint64(0x2), hits) + assert.Equal(t, uint64(0x5), hits) } func TestServiceSum(t *testing.T) { @@ -176,7 +192,6 @@ func TestCreateService(t *testing.T) { func TestViewNewService(t *testing.T) { newService := SelectService(newServiceId) assert.Equal(t, "That'll do 🐢", newService.Name) - } func TestCreateFailingHTTPService(t *testing.T) { @@ -195,10 +210,12 @@ func TestCreateFailingHTTPService(t *testing.T) { assert.NotZero(t, newServiceId) newService := SelectService(newServiceId) assert.Equal(t, "Bad URL", newService.Name) + t.Log("new service ID: ", newServiceId) } func TestServiceFailedCheck(t *testing.T) { - service := SelectService(7) + service := SelectService(20) + assert.Equal(t, "Bad URL", service.Name) checked := service.Check(true) assert.Equal(t, "Bad URL", checked.Name) assert.False(t, checked.Online) @@ -219,10 +236,11 @@ func TestCreateFailingTCPService(t *testing.T) { assert.NotZero(t, newServiceId) newService := SelectService(newServiceId) assert.Equal(t, "Bad TCP", newService.Name) + t.Log("new failing tcp service ID: ", newServiceId) } func TestServiceFailedTCPCheck(t *testing.T) { - service := SelectService(8) + service := SelectService(21) checked := service.Check(true) assert.Equal(t, "Bad TCP", checked.Name) assert.False(t, checked.Online) @@ -244,13 +262,13 @@ func TestDeleteService(t *testing.T) { count, err := CoreApp.SelectAllServices() assert.Nil(t, err) - assert.Equal(t, 8, len(count)) + assert.Equal(t, 21, len(count)) err = service.Delete() assert.Nil(t, err) services := CoreApp.Services() - assert.Equal(t, 7, len(services)) + assert.Equal(t, 20, len(services)) } func TestServiceCloseRoutine(t *testing.T) { diff --git a/core/users.go b/core/users.go index b4586314..345e5321 100644 --- a/core/users.go +++ b/core/users.go @@ -28,36 +28,29 @@ type User struct { } func ReturnUser(u *types.User) *User { - return &User{User: u} + return &User{u} } func SelectUser(id int64) (*User, error) { - var user *User - col := DbSession.Collection("users") - res := col.Find("id", id) - err := res.One(&user) - return user, err + var user User + err := usersDB().First(&user, id) + return &user, err.Error } func SelectUsername(username string) (*User, error) { - var user *User - col := DbSession.Collection("users") - res := col.Find("username", username) - err := res.One(&user) - return user, err + var user User + res := usersDB().Where("username = ?", username) + err := res.First(&user) + return &user, err.Error } func (u *User) Delete() error { - col := DbSession.Collection("users") - user := col.Find("id", u.Id) - return user.Delete() + return usersDB().Delete(u).Error } func (u *User) Update() error { u.CreatedAt = time.Now() - col := DbSession.Collection("users") - user := col.Find("id", u.Id) - return user.Update(u) + return usersDB().Update(u).Error } func (u *User) Create() (int64, error) { @@ -65,39 +58,36 @@ func (u *User) Create() (int64, error) { u.Password = utils.HashPassword(u.Password) u.ApiKey = utils.NewSHA1Hash(5) u.ApiSecret = utils.NewSHA1Hash(10) - col := DbSession.Collection("users") - uuid, err := col.Insert(u) - if err != nil { - return 0, err + db := usersDB().Create(u) + if db.Error != nil { + return 0, db.Error } - if uuid == nil { - utils.Log(3, fmt.Sprintf("Failed to create user %v. %v", u.Username, err)) - return 0, err + if u.Id == 0 { + utils.Log(3, fmt.Sprintf("Failed to create user %v. %v", u.Username, db.Error)) + return 0, db.Error } - return uuid.(int64), err + return u.Id, db.Error } func SelectAllUsers() ([]*User, error) { var users []*User - col := DbSession.Collection("users").Find() - err := col.All(&users) - if err != nil { - utils.Log(3, fmt.Sprintf("Failed to load all users. %v", err)) + db := usersDB().Find(&users) + if db.Error != nil { + utils.Log(3, fmt.Sprintf("Failed to load all users. %v", db.Error)) } - return users, err + return users, db.Error } func AuthUser(username, password string) (*User, bool) { - var auth bool user, err := SelectUsername(username) if err != nil { utils.Log(2, err) return nil, false } if CheckHash(password, user.Password) { - auth = true + return user, true } - return user, auth + return nil, false } func CheckHash(password, hash string) bool { diff --git a/core/users_test.go b/core/users_test.go index 6f069193..647677ac 100644 --- a/core/users_test.go +++ b/core/users_test.go @@ -36,13 +36,13 @@ func TestCreateUser(t *testing.T) { func TestSelectAllUsers(t *testing.T) { users, err := SelectAllUsers() assert.Nil(t, err) - assert.Equal(t, 1, len(users)) + assert.Equal(t, 2, len(users)) } func TestSelectUser(t *testing.T) { user, err := SelectUser(1) assert.Nil(t, err) - assert.Equal(t, "test@email.com", user.Email) + assert.Equal(t, "info@statup.io", user.Email) assert.True(t, user.Admin) } @@ -50,7 +50,7 @@ func TestSelectUsername(t *testing.T) { user, err := SelectUsername("hunter") assert.Nil(t, err) assert.Equal(t, "test@email.com", user.Email) - assert.Equal(t, int64(1), user.Id) + assert.Equal(t, int64(2), user.Id) assert.True(t, user.Admin) } @@ -80,7 +80,7 @@ func TestCreateUser2(t *testing.T) { func TestSelectAllUsersAgain(t *testing.T) { users, err := SelectAllUsers() assert.Nil(t, err) - assert.Equal(t, 2, len(users)) + assert.Equal(t, 3, len(users)) } func TestAuthUser(t *testing.T) { @@ -88,12 +88,12 @@ func TestAuthUser(t *testing.T) { assert.True(t, auth) assert.NotNil(t, user) assert.Equal(t, "user@email.com", user.Email) - assert.Equal(t, int64(2), user.Id) + assert.Equal(t, int64(3), user.Id) assert.True(t, user.Admin) } func TestFailedAuthUser(t *testing.T) { - user, auth := AuthUser("hunter", "wrongpassword") + user, auth := AuthUser("hunterlong", "wrongpassword") assert.False(t, auth) assert.Nil(t, user) } @@ -111,3 +111,8 @@ func TestDeleteUser(t *testing.T) { err = user.Delete() assert.Nil(t, err) } + +func TestDbConfig_Close(t *testing.T) { + err := Configs.Close() + assert.Nil(t, err) +} diff --git a/dev/mysql_seed.sql b/dev/mysql_seed.sql new file mode 100644 index 00000000..fde0dcf7 --- /dev/null +++ b/dev/mysql_seed.sql @@ -0,0 +1,142 @@ +INSERT INTO core (name,description,config,api_key,api_secret,style,footer,domain,version,migration_id,use_cdn) VALUES ('Awesome Status','This is from the seed file!','config.yml','d2fead3e459bd14f570cf08527175b88b32d7faa','e351393306ea245de5f9588cbe8627c74db007c6','','Created by Hunter Long','','',0,false); +INSERT INTO services (name,domain,check_type,method,port,expected,expected_status,check_interval,post_data,order_id,timeout,created_at) VALUES + ('Google','https://google.com','http','GET',0,'',200,10,'',0,10,'2018-08-31 10:42:08'), + ('Statup Github','https://github.com/hunterlong/statup','http','GET',0,'',200,30,'',0,20,'2018-08-31 10:42:08'), + ('JSON Users Test','https://jsonplaceholder.typicode.com/users','http','GET',0,'',200,60,'',0,30,'2018-08-31 10:42:08'), + ('JSON API Tester','https://jsonplaceholder.typicode.com/posts','http','POST',0,'(title)": "((\\"|[statup])*)"',201,30,'{ "title": "statup", "body": "bar", "userId": 19999 }',0,30,'2018-08-31 10:42:08'), + ('Google DNS','8.8.8.8','tcp','',53,'',0,20,'',0,120,'2018-08-31 10:42:08'), + ('The Bravery - An Honest Mistake','https://www.youtube.com/watch?v=O8vzbezVru4','http','GET',0,'',0,30,'',0,15,'2018-08-31 10:42:14'), + ('Upper.io','https://upper.io/db.v3/','http','GET',0,'',0,30,'',0,15,'2018-08-31 10:42:14'), + ('CoinApp Status','https://status.coinapp.io','http','GET',0,'',200,1,'',0,30,'2018-08-31 10:42:16'), + ('Demo Page','https://demo.statup.io','http','GET',0,'',200,2,'',0,30,'2018-08-31 10:42:16'), + ('Golang','https://golang.org','http','GET',0,'',200,3,'',0,30,'2018-08-31 10:42:16'), + ('Github','https://github.com/hunterlong','http','GET',0,'',200,4,'',0,30,'2018-08-31 10:42:17'), + ('Santa Monica','https://www.santamonica.com','http','GET',0,'',200,5,'',0,30,'2018-08-31 10:42:17'), + ('Oeschs Die Dritten','https://www.oeschs-die-dritten.ch/en/','http','GET',0,'',200,6,'',0,30,'2018-08-31 10:42:18'), + ('EtherScan.io','https://etherscan.io','http','GET',0,'',200,7,'',0,30,'2018-08-31 10:42:20'), + ('Test Service 7','https://www.youtube.com/watch?v=ipvEIZMMILA','http','GET',0,'',200,8,'',0,30,'2018-08-31 10:42:20'), + ('Test Service 8','https://www.youtube.com/watch?v=UdaYVxYF1Ok','http','GET',0,'',200,9,'',0,30,'2018-08-31 10:42:20'), + ('Test Service 9','https://www.youtube.com/watch?v=yydZbVoCbn0&t=870s','http','GET',0,'',200,10,'',0,30,'2018-08-31 10:42:20'), + ('Failing URL','http://failingdomainsarenofunatall.com','http','GET',0,'',200,11,'',0,30,'2018-08-31 10:42:20'); +INSERT INTO users (username,password,email,api_key,api_secret,administrator,created_at) VALUES + ('admin','$2a$14$Aye3yHae0ml6WRtvdgkRnO19OFze0IKF6IOHrdLpETtwLjnPelMUm','info@statup.io','27aa701119fb561d734eb4469cf13ba2550007e2','29de07014d32fbbbb80053ef3c19b464b2b72f64',1,'2018-08-31 10:42:07'); +INSERT INTO hits (service,latency,created_at) VALUES + (5,0.006504081,'2018-08-29 10:42:08'), + (3,0.202164333,'2018-08-29 10:42:08'), + (4,0.376675172,'2018-08-29 10:42:09'), + (1,0.413912204,'2018-08-29 10:42:09'), + (1,0.473912204,'2018-08-29 10:42:09'), + (2,0.528427935,'2018-08-29 10:42:09'), + (6,0.11930188,'2018-08-29 10:42:09'), + (7,0.001062722,'2018-08-29 10:42:09'), + (7,0.004046882,'2018-08-29 10:42:09'), + (6,0.063360069,'2018-08-29 10:42:09'), + (8,0.168951416,'2018-08-29 10:42:16'), + (8,0.069032763,'2018-08-29 10:42:16'), + (9,0.47712966,'2018-08-29 10:42:16'), + (10,0.104510482,'2018-08-29 10:42:17'), + (10,0.062536146,'2018-08-29 10:42:17'), + (9,0.352823197,'2018-08-29 10:42:09'), + (8,0.38226374,'2018-08-29 10:42:09'), + (11,0.857324393,'2018-08-29 10:42:17'), + (12,0.113320285,'2018-08-29 10:42:18'), + (12,0.038532321,'2018-08-29 10:42:18'), + (8,0.123430059,'2018-08-29 10:42:18'), + (11,0.625290389,'2018-08-29 10:42:18'), + (1,0.091823417,'2018-08-29 10:42:18'), + (9,0.246651097,'2018-08-29 10:42:19'), + (8,0.222901604,'2018-08-29 10:42:19'), + (13,1.600367041,'2018-08-29 10:42:20'), + (10,0.050076397,'2018-08-29 10:42:20'), + (14,0.460363958,'2018-08-29 10:42:20'), + (8,0.252590543,'2018-08-29 10:42:20'), + (15,0.144109113,'2018-08-29 10:42:20'), + (15,0.059993314,'2018-08-29 10:42:20'), + (16,0.058810662,'2018-08-29 10:42:20'), + (17,0.061824594,'2018-08-29 10:42:20'), + (16,0.074584583,'2018-08-29 10:42:20'), + (17,0.057086551,'2018-08-29 10:42:20'), + (18,0.020983572,'2018-08-29 10:42:20'), + (5,0.006504081,'2018-08-30 10:42:08'), + (3,0.202164333,'2018-08-30 10:42:08'), + (4,0.376675172,'2018-08-30 10:42:09'), + (1,0.413912204,'2018-08-30 10:42:09'), + (2,0.528427935,'2018-08-30 10:42:09'), + (6,0.11930188,'2018-08-30 10:42:14'), + (7,0.001062722,'2018-08-30 10:42:14'), + (7,0.004046882,'2018-08-30 10:42:14'), + (6,0.063360069,'2018-08-30 10:42:14'), + (8,0.168951416,'2018-08-30 10:42:16'), + (8,0.069032763,'2018-08-30 10:42:16'), + (9,0.47712966,'2018-08-30 10:42:16'), + (10,0.104510482,'2018-08-30 10:42:17'), + (10,0.062536146,'2018-08-30 10:42:17'), + (9,0.352823197,'2018-08-30 10:42:17'), + (8,0.38226374,'2018-08-30 10:42:17'), + (11,0.857324393,'2018-08-30 10:42:17'), + (12,0.113320285,'2018-08-30 10:42:18'), + (12,0.038532321,'2018-08-30 10:42:18'), + (8,0.123430059,'2018-08-30 10:42:18'), + (11,0.625290389,'2018-08-30 10:42:18'), + (1,0.091823417,'2018-08-30 10:42:18'), + (9,0.246651097,'2018-08-30 10:42:19'), + (8,0.222901604,'2018-08-30 10:42:19'), + (13,1.600367041,'2018-08-30 10:42:20'), + (10,0.050076397,'2018-08-30 10:42:20'), + (14,0.460363958,'2018-08-30 10:42:20'), + (8,0.252590543,'2018-08-30 10:42:20'), + (15,0.144109113,'2018-08-30 10:42:20'), + (15,0.059993314,'2018-08-30 10:42:20'), + (16,0.058810662,'2018-08-30 10:42:20'), + (17,0.061824594,'2018-08-30 10:42:20'), + (16,0.074584583,'2018-08-30 10:42:20'), + (17,0.057086551,'2018-08-30 10:42:20'), + (18,0.020983572,'2018-08-30 10:42:20'), + (5,0.006504081,'2018-08-31 10:42:08'), + (3,0.202164333,'2018-08-31 10:42:08'), + (4,0.376675172,'2018-08-31 10:42:09'), + (1,0.413912204,'2018-08-31 10:42:09'), + (2,0.528427935,'2018-08-31 10:42:09'), + (6,0.11930188,'2018-08-31 10:42:14'), + (7,0.001062722,'2018-08-31 10:42:14'), + (7,0.004046882,'2018-08-31 10:42:14'), + (6,0.063360069,'2018-08-31 10:42:14'), + (8,0.168951416,'2018-08-31 10:42:16'), + (8,0.069032763,'2018-08-31 10:42:16'), + (9,0.47712966,'2018-08-31 10:42:16'), + (10,0.104510482,'2018-08-31 10:42:17'), + (10,0.062536146,'2018-08-31 10:42:17'), + (9,0.352823197,'2018-08-31 10:42:17'), + (8,0.38226374,'2018-08-31 10:42:17'), + (11,0.857324393,'2018-08-31 10:42:17'), + (12,0.113320285,'2018-08-31 10:42:18'), + (12,0.038532321,'2018-08-31 10:42:18'), + (8,0.123430059,'2018-08-31 10:42:18'), + (11,0.625290389,'2018-08-31 10:42:18'), + (1,0.091823417,'2018-08-31 10:42:18'), + (9,0.246651097,'2018-08-31 10:42:19'), + (8,0.222901604,'2018-08-31 10:42:19'), + (13,1.600367041,'2018-08-31 10:42:20'), + (10,0.050076397,'2018-08-31 10:42:20'), + (14,0.460363958,'2018-08-31 10:42:20'), + (8,0.252590543,'2018-08-31 10:42:20'), + (15,0.144109113,'2018-08-31 10:42:20'), + (15,0.059993314,'2018-08-31 10:42:20'), + (16,0.058810662,'2018-08-31 10:42:20'), + (17,0.061824594,'2018-08-31 10:42:20'), + (16,0.074584583,'2018-08-31 10:42:20'), + (17,0.057086551,'2018-08-31 10:42:20'), + (18,0.020983572,'2018-08-31 10:42:20'); +INSERT INTO failures (issue,method,service,created_at) VALUES + ('HTTP Status Code 200 did not match 0','',18,'2018-08-28 10:42:14'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-28 10:42:14'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-29 10:42:14'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-30 10:42:14'), + ('Incorrect Response','',1,'2018-08-31 10:40:14'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-31 10:42:14'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-31 10:42:14'), + ('HTTP Status Code 200 did not match 0','',18,'2018-08-31 10:42:14'); +INSERT INTO communication (id,method,host,port,username,password,var1,var2,api_key,api_secret,enabled,removable,limits,created_at) VALUES + (1,'email','smtp.emailer.com',587,'exampleuser','password123','info@betatude.com','sendto@gmail.com','','',1,0,7,'2018-08-31 10:42:15'), + (2,'slack','https://webhooksurl.slack.com/***',0,'','','','','','',0,0,3,'2018-08-31 10:42:08'), + (3,'twilio','',0,'','','','','','',0,0,3,'2018-08-31 10:42:08'); diff --git a/dev/postgres_seed.sql b/dev/postgres_seed.sql new file mode 100644 index 00000000..529eb42e --- /dev/null +++ b/dev/postgres_seed.sql @@ -0,0 +1,142 @@ +INSERT INTO core (name,description,config,api_key,api_secret,style,footer,domain,version,migration_id,use_cdn) VALUES ('Awesome Status','This is from the seed file!','config.yml','d2fead3e459bd14f570cf08527175b88b32d7faa','e351393306ea245de5f9588cbe8627c74db007c6','','Created by Hunter Long','DEV','',0,false); +INSERT INTO services (name,domain,check_type,method,port,expected,expected_status,check_interval,post_data,order_id,timeout,created_at) VALUES + ('Google','https://google.com','http','GET',0,'',200,10,'',0,10,'2018-08-31 10:42:08.76390584-07:00'), + ('Statup Github','https://github.com/hunterlong/statup','http','GET',0,'',200,30,'',0,20,'2018-08-31 10:42:08.764977938-07:00'), + ('JSON Users Test','https://jsonplaceholder.typicode.com/users','http','GET',0,'',200,60,'',0,30,'2018-08-31 10:42:08.765794226-07:00'), + ('JSON API Tester','https://jsonplaceholder.typicode.com/posts','http','POST',0,'(title)": "((\\"|[statup])*)"',201,30,'{ "title": "statup", "body": "bar", "userId": 19999 }',0,30,'2018-08-31 10:42:08.766542311-07:00'), + ('Google DNS','8.8.8.8','tcp','',53,'',0,20,'',0,120,'2018-08-31 10:42:08.767327346-07:00'), + ('The Bravery - An Honest Mistake','https://www.youtube.com/watch?v=O8vzbezVru4','http','GET',0,'',0,30,'',0,15,'2018-08-31 10:42:14.201305666-07:00'), + ('Upper.io','https://upper.io/db.v3/','http','GET',0,'',0,30,'',0,15,'2018-08-31 10:42:14.201305666-07:00'), + ('CoinApp Status','https://status.coinapp.io','http','GET',0,'',200,1,'',0,30,'2018-08-31 10:42:16.097416218-07:00'), + ('Demo Page','https://demo.statup.io','http','GET',0,'',200,2,'',0,30,'2018-08-31 10:42:16.360051225-07:00'), + ('Golang','https://golang.org','http','GET',0,'',200,3,'',0,30,'2018-08-31 10:42:16.923478722-07:00'), + ('Github','https://github.com/hunterlong','http','GET',0,'',200,4,'',0,30,'2018-08-31 10:42:17.075544885-07:00'), + ('Santa Monica','https://www.santamonica.com','http','GET',0,'',200,5,'',0,30,'2018-08-31 10:42:17.946947674-07:00'), + ('Oeschs Die Dritten','https://www.oeschs-die-dritten.ch/en/','http','GET',0,'',200,6,'',0,30,'2018-08-31 10:42:18.083709297-07:00'), + ('EtherScan.io','https://etherscan.io','http','GET',0,'',200,7,'',0,30,'2018-08-31 10:42:20.020969513-07:00'), + ('Test Service 7','https://www.youtube.com/watch?v=ipvEIZMMILA','http','GET',0,'',200,8,'',0,30,'2018-08-31 10:42:20.50135711-07:00'), + ('Test Service 8','https://www.youtube.com/watch?v=UdaYVxYF1Ok','http','GET',0,'',200,9,'',0,30,'2018-08-31 10:42:20.651218082-07:00'), + ('Test Service 9','https://www.youtube.com/watch?v=yydZbVoCbn0&t=870s','http','GET',0,'',200,10,'',0,30,'2018-08-31 10:42:20.725479695-07:00'), + ('Failing URL','http://failingdomainsarenofunatall.com','http','GET',0,'',200,11,'',0,30,'2018-08-31 10:42:20.799471402-07:00'); +INSERT INTO users (username,password,email,api_key,api_secret,administrator,created_at) VALUES + ('admin','$2a$14$Aye3yHae0ml6WRtvdgkRnO19OFze0IKF6IOHrdLpETtwLjnPelMUm','info@statup.io','27aa701119fb561d734eb4469cf13ba2550007e2','29de07014d32fbbbb80053ef3c19b464b2b72f64',true,'2018-08-31 10:42:07.684406458-07:00'); +INSERT INTO hits (service,latency,created_at) VALUES + (5,0.006504081,'2018-08-29 10:42:08.779875117-07:00'), + (3,0.202164333,'2018-08-29 10:42:08.977187173-07:00'), + (4,0.376675172,'2018-08-29 10:42:09.151858662-07:00'), + (1,0.413912204,'2018-08-29 10:42:09.188850317-07:00'), + (1,0.473912204,'2018-08-29 10:42:10.118850317-07:00'), + (2,0.528427935,'2018-08-29 10:42:09.310642068-07:00'), + (6,0.11930188,'2018-08-29 10:42:14.133392018-07:00'), + (7,0.001062722,'2018-08-29 10:42:14.148258553-07:00'), + (7,0.004046882,'2018-08-29 10:42:14.156087817-07:00'), + (6,0.063360069,'2018-08-29 10:42:14.205383358-07:00'), + (8,0.168951416,'2018-08-29 10:42:16.346340211-07:00'), + (8,0.069032763,'2018-08-29 10:42:16.421634189-07:00'), + (9,0.47712966,'2018-08-29 10:42:16.91309317-07:00'), + (10,0.104510482,'2018-08-29 10:42:17.065673146-07:00'), + (10,0.062536146,'2018-08-29 10:42:17.134754949-07:00'), + (9,0.352823197,'2018-08-29 10:42:17.272174866-07:00'), + (8,0.38226374,'2018-08-29 10:42:17.738731999-07:00'), + (11,0.857324393,'2018-08-29 10:42:17.939738264-07:00'), + (12,0.113320285,'2018-08-29 10:42:18.073586363-07:00'), + (12,0.038532321,'2018-08-29 10:42:18.119730063-07:00'), + (8,0.123430059,'2018-08-29 10:42:18.479407581-07:00'), + (11,0.625290389,'2018-08-29 10:42:18.5715553-07:00'), + (1,0.091823417,'2018-08-29 10:42:18.868788983-07:00'), + (9,0.246651097,'2018-08-29 10:42:19.165697332-07:00'), + (8,0.222901604,'2018-08-29 10:42:19.57929225-07:00'), + (13,1.600367041,'2018-08-29 10:42:20.010203546-07:00'), + (10,0.050076397,'2018-08-29 10:42:20.12391038-07:00'), + (14,0.460363958,'2018-08-29 10:42:20.495937751-07:00'), + (8,0.252590543,'2018-08-29 10:42:20.609139136-07:00'), + (15,0.144109113,'2018-08-29 10:42:20.64756516-07:00'), + (15,0.059993314,'2018-08-29 10:42:20.710322678-07:00'), + (16,0.058810662,'2018-08-29 10:42:20.712087274-07:00'), + (17,0.061824594,'2018-08-29 10:42:20.791266761-07:00'), + (16,0.074584583,'2018-08-29 10:42:20.797581163-07:00'), + (17,0.057086551,'2018-08-29 10:42:20.854020864-07:00'), + (18,0.020983572,'2018-08-29 10:42:20.864610424-07:00'), + (5,0.006504081,'2018-08-30 10:42:08.779875117-07:00'), + (3,0.202164333,'2018-08-30 10:42:08.977187173-07:00'), + (4,0.376675172,'2018-08-30 10:42:09.151858662-07:00'), + (1,0.413912204,'2018-08-30 10:42:09.188850317-07:00'), + (2,0.528427935,'2018-08-30 10:42:09.310642068-07:00'), + (6,0.11930188,'2018-08-30 10:42:14.133392018-07:00'), + (7,0.001062722,'2018-08-30 10:42:14.148258553-07:00'), + (7,0.004046882,'2018-08-30 10:42:14.156087817-07:00'), + (6,0.063360069,'2018-08-30 10:42:14.205383358-07:00'), + (8,0.168951416,'2018-08-30 10:42:16.346340211-07:00'), + (8,0.069032763,'2018-08-30 10:42:16.421634189-07:00'), + (9,0.47712966,'2018-08-30 10:42:16.91309317-07:00'), + (10,0.104510482,'2018-08-30 10:42:17.065673146-07:00'), + (10,0.062536146,'2018-08-30 10:42:17.134754949-07:00'), + (9,0.352823197,'2018-08-30 10:42:17.272174866-07:00'), + (8,0.38226374,'2018-08-30 10:42:17.738731999-07:00'), + (11,0.857324393,'2018-08-30 10:42:17.939738264-07:00'), + (12,0.113320285,'2018-08-30 10:42:18.073586363-07:00'), + (12,0.038532321,'2018-08-30 10:42:18.119730063-07:00'), + (8,0.123430059,'2018-08-30 10:42:18.479407581-07:00'), + (11,0.625290389,'2018-08-30 10:42:18.5715553-07:00'), + (1,0.091823417,'2018-08-30 10:42:18.868788983-07:00'), + (9,0.246651097,'2018-08-30 10:42:19.165697332-07:00'), + (8,0.222901604,'2018-08-30 10:42:19.57929225-07:00'), + (13,1.600367041,'2018-08-30 10:42:20.010203546-07:00'), + (10,0.050076397,'2018-08-30 10:42:20.12391038-07:00'), + (14,0.460363958,'2018-08-30 10:42:20.495937751-07:00'), + (8,0.252590543,'2018-08-30 10:42:20.609139136-07:00'), + (15,0.144109113,'2018-08-30 10:42:20.64756516-07:00'), + (15,0.059993314,'2018-08-30 10:42:20.710322678-07:00'), + (16,0.058810662,'2018-08-30 10:42:20.712087274-07:00'), + (17,0.061824594,'2018-08-30 10:42:20.791266761-07:00'), + (16,0.074584583,'2018-08-30 10:42:20.797581163-07:00'), + (17,0.057086551,'2018-08-30 10:42:20.854020864-07:00'), + (18,0.020983572,'2018-08-30 10:42:20.864610424-07:00'), + (5,0.006504081,'2018-08-31 10:42:08.779875117-07:00'), + (3,0.202164333,'2018-08-31 10:42:08.977187173-07:00'), + (4,0.376675172,'2018-08-31 10:42:09.151858662-07:00'), + (1,0.413912204,'2018-08-31 10:42:09.188850317-07:00'), + (2,0.528427935,'2018-08-31 10:42:09.310642068-07:00'), + (6,0.11930188,'2018-08-31 10:42:14.133392018-07:00'), + (7,0.001062722,'2018-08-31 10:42:14.148258553-07:00'), + (7,0.004046882,'2018-08-31 10:42:14.156087817-07:00'), + (6,0.063360069,'2018-08-31 10:42:14.205383358-07:00'), + (8,0.168951416,'2018-08-31 10:42:16.346340211-07:00'), + (8,0.069032763,'2018-08-31 10:42:16.421634189-07:00'), + (9,0.47712966,'2018-08-31 10:42:16.91309317-07:00'), + (10,0.104510482,'2018-08-31 10:42:17.065673146-07:00'), + (10,0.062536146,'2018-08-31 10:42:17.134754949-07:00'), + (9,0.352823197,'2018-08-31 10:42:17.272174866-07:00'), + (8,0.38226374,'2018-08-31 10:42:17.738731999-07:00'), + (11,0.857324393,'2018-08-31 10:42:17.939738264-07:00'), + (12,0.113320285,'2018-08-31 10:42:18.073586363-07:00'), + (12,0.038532321,'2018-08-31 10:42:18.119730063-07:00'), + (8,0.123430059,'2018-08-31 10:42:18.479407581-07:00'), + (11,0.625290389,'2018-08-31 10:42:18.5715553-07:00'), + (1,0.091823417,'2018-08-31 10:42:18.868788983-07:00'), + (9,0.246651097,'2018-08-31 10:42:19.165697332-07:00'), + (8,0.222901604,'2018-08-31 10:42:19.57929225-07:00'), + (13,1.600367041,'2018-08-31 10:42:20.010203546-07:00'), + (10,0.050076397,'2018-08-31 10:42:20.12391038-07:00'), + (14,0.460363958,'2018-08-31 10:42:20.495937751-07:00'), + (8,0.252590543,'2018-08-31 10:42:20.609139136-07:00'), + (15,0.144109113,'2018-08-31 10:42:20.64756516-07:00'), + (15,0.059993314,'2018-08-31 10:42:20.710322678-07:00'), + (16,0.058810662,'2018-08-31 10:42:20.712087274-07:00'), + (17,0.061824594,'2018-08-31 10:42:20.791266761-07:00'), + (16,0.074584583,'2018-08-31 10:42:20.797581163-07:00'), + (17,0.057086551,'2018-08-31 10:42:20.854020864-07:00'), + (18,0.020983572,'2018-08-31 10:42:20.864610424-07:00'); +INSERT INTO failures (issue,method,service,created_at) VALUES + ('HTTP Status Code 200 did not match 0','',18,'2018-08-28 10:42:14.271162743-07:00'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-28 10:42:14.271162743-07:00'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-29 10:42:14.271162743-07:00'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-30 10:42:14.271162743-07:00'), + ('Incorrect Response','',1,'2018-08-31 10:40:14.272209564-07:00'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-31 10:42:14.271162743-07:00'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-31 10:42:14.272209564-07:00'), + ('HTTP Status Code 200 did not match 0','',18,'2018-08-31 10:42:14.271162743-07:00'); +INSERT INTO communication (id,method,host,port,username,password,var1,var2,api_key,api_secret,enabled,removable,limits,created_at) VALUES + (1,'email','smtp.emailer.com',587,'exampleuser','password123','info@betatude.com','sendto@gmail.com','','',true,false,7,'2018-08-31 10:42:15.000829706-07:00'), + (2,'slack','https://webhooksurl.slack.com/***',0,'','','','','','',false,false,3,'2018-08-31 10:42:08.775366824-07:00'), + (3,'twilio','',0,'','','','','','',false,false,3,'2018-08-31 10:42:08.776944923-07:00'); diff --git a/dev/sqlite_seed.sql b/dev/sqlite_seed.sql new file mode 100644 index 00000000..dfec3b10 --- /dev/null +++ b/dev/sqlite_seed.sql @@ -0,0 +1,142 @@ +INSERT INTO core (name,description,config,api_key,api_secret,style,footer,domain,version,migration_id,use_cdn) VALUES ('Awesome Status','This is from the seed file!','config.yml','d2fead3e459bd14f570cf08527175b88b32d7faa','e351393306ea245de5f9588cbe8627c74db007c6','','Created by Hunter Long','','',0,0); +INSERT INTO services (id,name,domain,check_type,method,port,expected,expected_status,check_interval,post_data,order_id,timeout,created_at) VALUES + (1,'Google','https://google.com','http','GET',0,'',200,10,'',0,10,'2018-08-31 10:42:08.76390584-07:00'), + (2,'Statup Github','https://github.com/hunterlong/statup','http','GET',0,'',200,30,'',0,20,'2018-08-31 10:42:08.764977938-07:00'), + (3,'JSON Users Test','https://jsonplaceholder.typicode.com/users','http','GET',0,'',200,60,'',0,30,'2018-08-31 10:42:08.765794226-07:00'), + (4,'JSON API Tester','https://jsonplaceholder.typicode.com/posts','http','POST',0,'(title)": "((\\"|[statup])*)"',201,30,'{ "title": "statup", "body": "bar", "userId": 19999 }',0,30,'2018-08-31 10:42:08.766542311-07:00'), + (5,'Google DNS','8.8.8.8','tcp','',53,'',0,20,'',0,120,'2018-08-31 10:42:08.767327346-07:00'), + (6,'The Bravery - An Honest Mistake','https://www.youtube.com/watch?v=O8vzbezVru4','http','GET',0,'',0,30,'',0,15,'2018-08-31 10:42:14.201305666-07:00'), + (7,'Upper.io','https://upper.io/db.v3/','http','GET',0,'',0,30,'',0,15,'2018-08-31 10:42:14.201305666-07:00'), + (8,'CoinApp Status','https://status.coinapp.io','http','GET',0,'',200,1,'',0,30,'2018-08-31 10:42:16.097416218-07:00'), + (9,'Demo Page','https://demo.statup.io','http','GET',0,'',200,2,'',0,30,'2018-08-31 10:42:16.360051225-07:00'), + (10,'Golang','https://golang.org','http','GET',0,'',200,3,'',0,30,'2018-08-31 10:42:16.923478722-07:00'), + (11,'Github','https://github.com/hunterlong','http','GET',0,'',200,4,'',0,30,'2018-08-31 10:42:17.075544885-07:00'), + (12,'Santa Monica','https://www.santamonica.com','http','GET',0,'',200,5,'',0,30,'2018-08-31 10:42:17.946947674-07:00'), + (13,'Oeschs Die Dritten','https://www.oeschs-die-dritten.ch/en/','http','GET',0,'',200,6,'',0,30,'2018-08-31 10:42:18.083709297-07:00'), + (14,'EtherScan.io','https://etherscan.io','http','GET',0,'',200,7,'',0,30,'2018-08-31 10:42:20.020969513-07:00'), + (15,'Test Service 7','https://www.youtube.com/watch?v=ipvEIZMMILA','http','GET',0,'',200,8,'',0,30,'2018-08-31 10:42:20.50135711-07:00'), + (16,'Test Service 8','https://www.youtube.com/watch?v=UdaYVxYF1Ok','http','GET',0,'',200,9,'',0,30,'2018-08-31 10:42:20.651218082-07:00'), + (17,'Test Service 9','https://www.youtube.com/watch?v=yydZbVoCbn0&t=870s','http','GET',0,'',200,10,'',0,30,'2018-08-31 10:42:20.725479695-07:00'), + (18,'Failing URL','http://failingdomainsarenofunatall.com','http','GET',0,'',200,11,'',0,30,'2018-08-31 10:42:20.799471402-07:00'); +INSERT INTO users (username,password,email,api_key,api_secret,administrator,created_at) VALUES + ('admin','$2a$14$Aye3yHae0ml6WRtvdgkRnO19OFze0IKF6IOHrdLpETtwLjnPelMUm','info@statup.io','27aa701119fb561d734eb4469cf13ba2550007e2','29de07014d32fbbbb80053ef3c19b464b2b72f64',1,'2018-08-31 10:42:07.684406458-07:00'); +INSERT INTO hits (service,latency,created_at) VALUES + (5,0.006504081,'2018-08-29 10:42:08.779875117-07:00'), + (3,0.202164333,'2018-08-29 10:42:08.977187173-07:00'), + (4,0.376675172,'2018-08-29 10:42:09.151858662-07:00'), + (1,0.413912204,'2018-08-29 10:42:09.188850317-07:00'), + (1,0.473912204,'2018-08-29 10:42:10.118850317-07:00'), + (2,0.528427935,'2018-08-29 10:42:09.310642068-07:00'), + (6,0.11930188,'2018-08-29 10:42:14.133392018-07:00'), + (7,0.001062722,'2018-08-29 10:42:14.148258553-07:00'), + (7,0.004046882,'2018-08-29 10:42:14.156087817-07:00'), + (6,0.063360069,'2018-08-29 10:42:14.205383358-07:00'), + (8,0.168951416,'2018-08-29 10:42:16.346340211-07:00'), + (8,0.069032763,'2018-08-29 10:42:16.421634189-07:00'), + (9,0.47712966,'2018-08-29 10:42:16.91309317-07:00'), + (10,0.104510482,'2018-08-29 10:42:17.065673146-07:00'), + (10,0.062536146,'2018-08-29 10:42:17.134754949-07:00'), + (9,0.352823197,'2018-08-29 10:42:17.272174866-07:00'), + (8,0.38226374,'2018-08-29 10:42:17.738731999-07:00'), + (11,0.857324393,'2018-08-29 10:42:17.939738264-07:00'), + (12,0.113320285,'2018-08-29 10:42:18.073586363-07:00'), + (12,0.038532321,'2018-08-29 10:42:18.119730063-07:00'), + (8,0.123430059,'2018-08-29 10:42:18.479407581-07:00'), + (11,0.625290389,'2018-08-29 10:42:18.5715553-07:00'), + (1,0.091823417,'2018-08-29 10:42:18.868788983-07:00'), + (9,0.246651097,'2018-08-29 10:42:19.165697332-07:00'), + (8,0.222901604,'2018-08-29 10:42:19.57929225-07:00'), + (13,1.600367041,'2018-08-29 10:42:20.010203546-07:00'), + (10,0.050076397,'2018-08-29 10:42:20.12391038-07:00'), + (14,0.460363958,'2018-08-29 10:42:20.495937751-07:00'), + (8,0.252590543,'2018-08-29 10:42:20.609139136-07:00'), + (15,0.144109113,'2018-08-29 10:42:20.64756516-07:00'), + (15,0.059993314,'2018-08-29 10:42:20.710322678-07:00'), + (16,0.058810662,'2018-08-29 10:42:20.712087274-07:00'), + (17,0.061824594,'2018-08-29 10:42:20.791266761-07:00'), + (16,0.074584583,'2018-08-29 10:42:20.797581163-07:00'), + (17,0.057086551,'2018-08-29 10:42:20.854020864-07:00'), + (18,0.020983572,'2018-08-29 10:42:20.864610424-07:00'), + (5,0.006504081,'2018-08-30 10:42:08.779875117-07:00'), + (3,0.202164333,'2018-08-30 10:42:08.977187173-07:00'), + (4,0.376675172,'2018-08-30 10:42:09.151858662-07:00'), + (1,0.413912204,'2018-08-30 10:42:09.188850317-07:00'), + (2,0.528427935,'2018-08-30 10:42:09.310642068-07:00'), + (6,0.11930188,'2018-08-30 10:42:14.133392018-07:00'), + (7,0.001062722,'2018-08-30 10:42:14.148258553-07:00'), + (7,0.004046882,'2018-08-30 10:42:14.156087817-07:00'), + (6,0.063360069,'2018-08-30 10:42:14.205383358-07:00'), + (8,0.168951416,'2018-08-30 10:42:16.346340211-07:00'), + (8,0.069032763,'2018-08-30 10:42:16.421634189-07:00'), + (9,0.47712966,'2018-08-30 10:42:16.91309317-07:00'), + (10,0.104510482,'2018-08-30 10:42:17.065673146-07:00'), + (10,0.062536146,'2018-08-30 10:42:17.134754949-07:00'), + (9,0.352823197,'2018-08-30 10:42:17.272174866-07:00'), + (8,0.38226374,'2018-08-30 10:42:17.738731999-07:00'), + (11,0.857324393,'2018-08-30 10:42:17.939738264-07:00'), + (12,0.113320285,'2018-08-30 10:42:18.073586363-07:00'), + (12,0.038532321,'2018-08-30 10:42:18.119730063-07:00'), + (8,0.123430059,'2018-08-30 10:42:18.479407581-07:00'), + (11,0.625290389,'2018-08-30 10:42:18.5715553-07:00'), + (1,0.091823417,'2018-08-30 10:42:18.868788983-07:00'), + (9,0.246651097,'2018-08-30 10:42:19.165697332-07:00'), + (8,0.222901604,'2018-08-30 10:42:19.57929225-07:00'), + (13,1.600367041,'2018-08-30 10:42:20.010203546-07:00'), + (10,0.050076397,'2018-08-30 10:42:20.12391038-07:00'), + (14,0.460363958,'2018-08-30 10:42:20.495937751-07:00'), + (8,0.252590543,'2018-08-30 10:42:20.609139136-07:00'), + (15,0.144109113,'2018-08-30 10:42:20.64756516-07:00'), + (15,0.059993314,'2018-08-30 10:42:20.710322678-07:00'), + (16,0.058810662,'2018-08-30 10:42:20.712087274-07:00'), + (17,0.061824594,'2018-08-30 10:42:20.791266761-07:00'), + (16,0.074584583,'2018-08-30 10:42:20.797581163-07:00'), + (17,0.057086551,'2018-08-30 10:42:20.854020864-07:00'), + (18,0.020983572,'2018-08-30 10:42:20.864610424-07:00'), + (5,0.006504081,'2018-08-31 10:42:08.779875117-07:00'), + (3,0.202164333,'2018-08-31 10:42:08.977187173-07:00'), + (4,0.376675172,'2018-08-31 10:42:09.151858662-07:00'), + (1,0.413912204,'2018-08-31 10:42:09.188850317-07:00'), + (2,0.528427935,'2018-08-31 10:42:09.310642068-07:00'), + (6,0.11930188,'2018-08-31 10:42:14.133392018-07:00'), + (7,0.001062722,'2018-08-31 10:42:14.148258553-07:00'), + (7,0.004046882,'2018-08-31 10:42:14.156087817-07:00'), + (6,0.063360069,'2018-08-31 10:42:14.205383358-07:00'), + (8,0.168951416,'2018-08-31 10:42:16.346340211-07:00'), + (8,0.069032763,'2018-08-31 10:42:16.421634189-07:00'), + (9,0.47712966,'2018-08-31 10:42:16.91309317-07:00'), + (10,0.104510482,'2018-08-31 10:42:17.065673146-07:00'), + (10,0.062536146,'2018-08-31 10:42:17.134754949-07:00'), + (9,0.352823197,'2018-08-31 10:42:17.272174866-07:00'), + (8,0.38226374,'2018-08-31 10:42:17.738731999-07:00'), + (11,0.857324393,'2018-08-31 10:42:17.939738264-07:00'), + (12,0.113320285,'2018-08-31 10:42:18.073586363-07:00'), + (12,0.038532321,'2018-08-31 10:42:18.119730063-07:00'), + (8,0.123430059,'2018-08-31 10:42:18.479407581-07:00'), + (11,0.625290389,'2018-08-31 10:42:18.5715553-07:00'), + (1,0.091823417,'2018-08-31 10:42:18.868788983-07:00'), + (9,0.246651097,'2018-08-31 10:42:19.165697332-07:00'), + (8,0.222901604,'2018-08-31 10:42:19.57929225-07:00'), + (13,1.600367041,'2018-08-31 10:42:20.010203546-07:00'), + (10,0.050076397,'2018-08-31 10:42:20.12391038-07:00'), + (14,0.460363958,'2018-08-31 10:42:20.495937751-07:00'), + (8,0.252590543,'2018-08-31 10:42:20.609139136-07:00'), + (15,0.144109113,'2018-08-31 10:42:20.64756516-07:00'), + (15,0.059993314,'2018-08-31 10:42:20.710322678-07:00'), + (16,0.058810662,'2018-08-31 10:42:20.712087274-07:00'), + (17,0.061824594,'2018-08-31 10:42:20.791266761-07:00'), + (16,0.074584583,'2018-08-31 10:42:20.797581163-07:00'), + (17,0.057086551,'2018-08-31 10:42:20.854020864-07:00'), + (18,0.020983572,'2018-08-31 10:42:20.864610424-07:00'); +INSERT INTO failures (issue,method,service,created_at) VALUES + ('HTTP Status Code 200 did not match 0','',18,'2018-08-28 10:42:14.271162743-07:00'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-28 10:42:14.271162743-07:00'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-29 10:42:14.271162743-07:00'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-30 10:42:14.271162743-07:00'), + ('Incorrect Response','',1,'2018-08-31 10:40:14.272209564-07:00'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-31 10:42:14.271162743-07:00'), + ('HTTP Status Code 200 did not match 0','',6,'2018-08-31 10:42:14.272209564-07:00'), + ('HTTP Status Code 200 did not match 0','',18,'2018-08-31 10:42:14.271162743-07:00'); +INSERT INTO communication (id,method,host,port,username,password,var1,var2,api_key,api_secret,enabled,removable,limits,created_at) VALUES + (1,'email','smtp.emailer.com',587,'exampleuser','password123','info@betatude.com','sendto@gmail.com','','',1,0,7,'2018-08-31 10:42:15.000829706-07:00'), + (2,'slack','https://webhooksurl.slack.com/***',0,'','','','','','',0,0,3,'2018-08-31 10:42:08.775366824-07:00'), + (3,'twilio','',0,'','','','','','',0,0,3,'2018-08-31 10:42:08.776944923-07:00'); diff --git a/handlers/api_handlers_test.go b/handlers/api_handlers_test.go index 0502d1b8..4bb5464c 100644 --- a/handlers/api_handlers_test.go +++ b/handlers/api_handlers_test.go @@ -36,17 +36,23 @@ const ( func injectDatabase() { core.NewCore() - core.Configs = new(types.Config) - core.Configs.Connection = "sqlite" + core.Configs = new(core.DbConfig) + core.Configs.DbConn = "sqlite" core.CoreApp.DbConnection = "sqlite" core.CoreApp.Version = "DEV" - core.DbConnection("sqlite", false, utils.Directory) + core.Configs.Connect(false, utils.Directory) core.InitApp() } +func Clean() { + utils.DeleteFile(dir + "/config.yml") + utils.DeleteFile(dir + "/statup.db") + utils.DeleteDirectory(dir + "/assets") + utils.DeleteDirectory(dir + "/logs") +} + func TestInit(t *testing.T) { - t.SkipNow() - injectDatabase() + Clean() } func formatJSON(res string, out interface{}) { diff --git a/handlers/handlers_test.go b/handlers/handlers_test.go index e60a645d..0eba5e23 100644 --- a/handlers/handlers_test.go +++ b/handlers/handlers_test.go @@ -97,6 +97,8 @@ func TestProcessSetupHandler(t *testing.T) { rr := httptest.NewRecorder() Router().ServeHTTP(rr, req) assert.Equal(t, 303, rr.Code) + assert.FileExists(t, dir+"/config.yml") + assert.FileExists(t, dir+"/statup.db") } func TestCheckSetupHandler(t *testing.T) { @@ -143,7 +145,9 @@ func TestServiceChartHandler(t *testing.T) { assert.Equal(t, 200, rr.Code) t.Log(body) assert.Contains(t, body, "var ctx_1") + assert.Contains(t, body, "var ctx_2") assert.Contains(t, body, "var ctx_3") + assert.Contains(t, body, "var ctx_4") assert.Contains(t, body, "var ctx_5") } diff --git a/handlers/index.go b/handlers/index.go index 5ae9d4f3..f1f129e9 100644 --- a/handlers/index.go +++ b/handlers/index.go @@ -27,7 +27,7 @@ type index struct { } func IndexHandler(w http.ResponseWriter, r *http.Request) { - if core.CoreApp.DbConnection == "" { + if core.Configs == nil { http.Redirect(w, r, "/setup", http.StatusSeeOther) return } @@ -42,12 +42,12 @@ func DesktopInit(ip string, port int) { var err error exists := utils.FileExists(utils.Directory + "/statup.db") if exists { - core.Configs, err = core.LoadConfig() + core.Configs, err = core.LoadConfig(utils.Directory) if err != nil { utils.Log(3, err) return } - err = core.DbConnection(core.Configs.Connection, false, utils.Directory) + err = core.Configs.Connect(false, utils.Directory) if err != nil { utils.Log(3, err) return @@ -68,24 +68,28 @@ func DesktopInit(ip string, port int) { Location: utils.Directory, }} - err = config.Save() + config, err = config.Save() if err != nil { utils.Log(4, err) } + config.DropDatabase() + config.CreateDatabase() + core.CoreApp = config.CreateCore() + if err != nil { utils.Log(3, err) return } - core.Configs, err = core.LoadConfig() + core.Configs, err = core.LoadConfig(utils.Directory) if err != nil { utils.Log(3, err) config.Error = err return } - err = core.DbConnection(core.Configs.Connection, false, utils.Directory) + err = core.Configs.Connect(false, utils.Directory) if err != nil { utils.Log(3, err) core.DeleteConfig() diff --git a/handlers/plugins.go b/handlers/plugins.go index 12d033ad..7e3b4607 100644 --- a/handlers/plugins.go +++ b/handlers/plugins.go @@ -16,7 +16,6 @@ package handlers import ( - "github.com/hunterlong/statup/core" "net/http" "strings" ) @@ -53,6 +52,6 @@ func PluginsDownloadHandler(w http.ResponseWriter, r *http.Request) { //vars := mux.Vars(router) //name := vars["name"] //DownloadPlugin(name) - core.LoadConfig() + //core.LoadConfig(utils.Directory) http.Redirect(w, r, "/plugins", http.StatusSeeOther) } diff --git a/handlers/prometheus.go b/handlers/prometheus.go index 888df67b..0a9e55f7 100644 --- a/handlers/prometheus.go +++ b/handlers/prometheus.go @@ -61,13 +61,6 @@ func PrometheusHandler(w http.ResponseWriter, r *http.Request) { w.Write([]byte(output)) } -func ResetDbHandler(w http.ResponseWriter, r *http.Request) { - utils.Log(1, fmt.Sprintf("Prometheus /metrics Request From IP: %v\n", r.RemoteAddr)) - core.DropDatabase() - core.CoreApp = nil - w.WriteHeader(http.StatusOK) -} - func isAuthorized(r *http.Request) bool { var token string tokens, ok := r.Header["Authorization"] diff --git a/handlers/routes.go b/handlers/routes.go index 34344554..1ace7c5b 100644 --- a/handlers/routes.go +++ b/handlers/routes.go @@ -51,7 +51,6 @@ func Router() *mux.Router { r.Handle("/setup", http.HandlerFunc(SetupHandler)).Methods("GET") r.Handle("/setup", http.HandlerFunc(ProcessSetupHandler)).Methods("POST") r.Handle("/dashboard", http.HandlerFunc(DashboardHandler)).Methods("GET") - //r.Handle("/backups/create", http.HandlerFunc(BackupCreateHandler)).Methods("GET") r.Handle("/dashboard", http.HandlerFunc(LoginHandler)).Methods("POST") r.Handle("/logout", http.HandlerFunc(LogoutHandler)) r.Handle("/services", http.HandlerFunc(ServicesHandler)).Methods("GET") diff --git a/handlers/services.go b/handlers/services.go index a779b006..06c54bba 100644 --- a/handlers/services.go +++ b/handlers/services.go @@ -59,6 +59,7 @@ 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.Order = s.Order service.Update(false) diff --git a/handlers/setup.go b/handlers/setup.go index b7aaf076..e67ae86b 100644 --- a/handlers/setup.go +++ b/handlers/setup.go @@ -16,7 +16,6 @@ package handlers import ( - "fmt" "github.com/hunterlong/statup/core" "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" @@ -56,6 +55,7 @@ func SetupHandler(w http.ResponseWriter, r *http.Request) { } func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) { + var err error if core.CoreApp.Services() != nil { http.Redirect(w, r, "/", http.StatusSeeOther) return @@ -75,7 +75,9 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) { domain := r.PostForm.Get("domain") email := r.PostForm.Get("email") - config := &core.DbConfig{&types.DbConfig{ + dir := utils.Directory + + config := &core.DbConfig{DbConfig: &types.DbConfig{ DbConn: dbConn, DbHost: dbHost, DbUser: dbUser, @@ -92,13 +94,15 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) { Location: utils.Directory, }} - fmt.Println(config) - - err := config.Save() + core.Configs, err = config.Save() if err != nil { utils.Log(4, err) + config.Error = err + SetupResponseError(w, r, config) + return } + core.Configs, err = core.LoadConfig(dir) if err != nil { utils.Log(3, err) config.Error = err @@ -106,23 +110,26 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) { return } - core.Configs, err = core.LoadConfig() + err = core.Configs.Connect(false, dir) if err != nil { - utils.Log(3, err) - config.Error = err - SetupResponseError(w, r, config) - return - } - - err = core.DbConnection(core.Configs.Connection, false, utils.Directory) - if err != nil { - utils.Log(3, err) + utils.Log(4, err) core.DeleteConfig() config.Error = err SetupResponseError(w, r, config) return } + config.DropDatabase() + config.CreateDatabase() + + core.CoreApp, err = config.InsertCore() + if err != nil { + utils.Log(4, err) + config.Error = err + SetupResponseError(w, r, config) + return + } + admin := core.ReturnUser(&types.User{ Username: config.Username, Password: config.Password, diff --git a/notifiers/email.go b/notifiers/email.go index 15a1da16..e1c15dff 100644 --- a/notifiers/email.go +++ b/notifiers/email.go @@ -112,7 +112,7 @@ func (u *Email) Init() error { if u.Enabled { utils.Log(1, fmt.Sprintf("Loading SMTP Emailer using host: %v:%v", u.Notification.Host, u.Notification.Port)) - mailer = gomail.NewDialer(u.Notification.Host, u.Notification.Port, u.Notification.Username, u.Notification.Password) + mailer = gomail.NewPlainDialer(u.Notification.Host, u.Notification.Port, u.Notification.Username, u.Notification.Password) mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true} go u.Run() @@ -215,7 +215,7 @@ func (u *Email) OnSave() error { // ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS func (u *Email) Install() error { - inDb, err := emailer.Notification.IsInDatabase() + inDb := emailer.Notification.IsInDatabase() if !inDb { newNotifer, err := InsertDatabase(u.Notification) if err != nil { @@ -224,7 +224,7 @@ func (u *Email) Install() error { } utils.Log(1, fmt.Sprintf("new notifier #%v installed: %v", newNotifer, u.Method)) } - return err + return nil } func (u *Email) dialSend(email *EmailOutgoing) error { diff --git a/notifiers/notifiers.go b/notifiers/notifiers.go index 0e750ea7..f831b3a8 100644 --- a/notifiers/notifiers.go +++ b/notifiers/notifiers.go @@ -19,34 +19,35 @@ import ( "fmt" "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" + "github.com/jinzhu/gorm" "strings" "time" - "upper.io/db.v3" ) var ( AllCommunications []types.AllNotifiers - Collections db.Collection + Collections *gorm.DB Logs []*NotificationLog ) type Notification struct { - Id int64 `db:"id,omitempty" json:"id"` - Method string `db:"method" json:"method"` - Host string `db:"host" json:"-"` - Port int `db:"port" json:"-"` - Username string `db:"username" json:"-"` - Password string `db:"password" json:"-"` - Var1 string `db:"var1" json:"-"` - Var2 string `db:"var2" json:"-"` - ApiKey string `db:"api_key" json:"-"` - ApiSecret string `db:"api_secret" json:"-"` - Enabled bool `db:"enabled" json:"enabled"` - Limits int `db:"limits" json:"-"` - Removable bool `db:"removable" json:"-"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - Form []NotificationForm - Routine chan struct{} + Id int64 `gorm:"primary_key column:id" json:"id"` + Method string `gorm:"column:method" json:"method"` + 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:"-"` } type Notifier interface { @@ -115,30 +116,30 @@ func reverseLogs(input []*NotificationLog) []*NotificationLog { return append(reverseLogs(input[1:]), input[0]) } -func (n *Notification) IsInDatabase() (bool, error) { - return Collections.Find("id", n.Id).Exists() +func (n *Notification) IsInDatabase() bool { + return !Collections.Find(n).RecordNotFound() } func SelectNotification(id int64) (*Notification, error) { - var notifier *Notification - err := Collections.Find("id", id).One(¬ifier) - return notifier, err + var notifier Notification + err := Collections.Find(¬ifier, id) + return ¬ifier, err.Error } func (n *Notification) Update() (*Notification, error) { n.CreatedAt = time.Now() - err := Collections.Find("id", n.Id).Update(n) - return n, err + err := Collections.Update(n) + return n, err.Error } func InsertDatabase(n *Notification) (int64, error) { n.CreatedAt = time.Now() n.Limits = 3 - newId, err := Collections.Insert(n) - if err != nil { - return 0, err + db := Collections.Create(n) + if db.Error != nil { + return 0, db.Error } - return newId.(int64), err + return n.Id, db.Error } func SelectNotifier(id int64) Notifier { diff --git a/notifiers/notifiers_test.go b/notifiers/notifiers_test.go index a43922fb..90eefae1 100644 --- a/notifiers/notifiers_test.go +++ b/notifiers/notifiers_test.go @@ -18,10 +18,11 @@ package notifiers import ( "github.com/hunterlong/statup/types" "github.com/hunterlong/statup/utils" + "github.com/jinzhu/gorm" + _ "github.com/jinzhu/gorm/dialects/sqlite" "github.com/pkg/errors" "github.com/stretchr/testify/assert" "testing" - "upper.io/db.v3/sqlite" ) var ( @@ -67,11 +68,8 @@ func init() { } func injectDatabase() { - sqliteDb := sqlite.ConnectionURL{ - Database: dir + "/statup.db", - } - dbSession, _ := sqlite.Open(sqliteDb) - Collections = dbSession.Collection("communication") + dbSession, _ := gorm.Open("sqlite3", dir+"/statup.db") + Collections = dbSession.Table("communication").Model(&Notification{}) } type Tester struct { @@ -100,8 +98,7 @@ func TestAdd(t *testing.T) { } func TestIsInDatabase(t *testing.T) { - in, err := testNotifier.IsInDatabase() - assert.Nil(t, err) + in := testNotifier.IsInDatabase() assert.False(t, in) } @@ -110,8 +107,7 @@ func TestInsertDatabase(t *testing.T) { assert.Nil(t, err) assert.NotZero(t, newId) - in, err := testNotifier.IsInDatabase() - assert.Nil(t, err) + in := testNotifier.IsInDatabase() assert.True(t, in) } diff --git a/notifiers/slack.go b/notifiers/slack.go index a4d941fd..a170aedc 100644 --- a/notifiers/slack.go +++ b/notifiers/slack.go @@ -165,7 +165,7 @@ func (u *Slack) OnSave() error { // ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS func (u *Slack) Install() error { - inDb, err := slacker.Notification.IsInDatabase() + inDb := slacker.Notification.IsInDatabase() if !inDb { newNotifer, err := InsertDatabase(u.Notification) if err != nil { @@ -174,5 +174,5 @@ func (u *Slack) Install() error { } utils.Log(1, fmt.Sprintf("new notifier #%v installed: %v", newNotifer, u.Method)) } - return err + return nil } diff --git a/notifiers/twilio.go b/notifiers/twilio.go index 9db78012..8b0c7995 100644 --- a/notifiers/twilio.go +++ b/notifiers/twilio.go @@ -179,7 +179,7 @@ func (u *Twilio) OnSave() error { // ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS func (u *Twilio) Install() error { - inDb, err := twilio.Notification.IsInDatabase() + inDb := twilio.Notification.IsInDatabase() if !inDb { newNotifer, err := InsertDatabase(u.Notification) if err != nil { @@ -188,5 +188,5 @@ func (u *Twilio) Install() error { } utils.Log(1, fmt.Sprintf("new notifier #%v installed: %v", newNotifer, u.Method)) } - return err + return nil } 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 44611f72..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; \ No newline at end of file diff --git a/source/sql/mysql_up.sql b/source/sql/mysql_up.sql deleted file mode 100644 index 92fa241c..00000000 --- a/source/sql/mysql_up.sql +++ /dev/null @@ -1,83 +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' -); -CREATE TABLE users ( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - 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 DATETIME DEFAULT CURRENT_TIMESTAMP, - INDEX (id), - UNIQUE (username, email) -); -CREATE TABLE services ( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - 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 DATETIME DEFAULT CURRENT_TIMESTAMP, - timeout INT(6) DEFAULT 30, - INDEX (id) -); -CREATE TABLE hits ( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - service INTEGER NOT NULL, - latency float, - created_at DATETIME, - INDEX (id, service), - FOREIGN KEY (service) REFERENCES services(id) ON DELETE CASCADE -); -CREATE TABLE failures ( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - issue text, - method text, - service INTEGER NOT NULL, - created_at DATETIME, - INDEX (id, service), - FOREIGN KEY (service) REFERENCES services(id) ON DELETE CASCADE -); -CREATE TABLE checkins ( - id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, - service INTEGER NOT NULL, - check_interval integer, - api text, - created_at DATETIME, - INDEX (id, service), - FOREIGN KEY (service) REFERENCES services(id) ON DELETE CASCADE -); -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 BOOL NOT NULL DEFAULT '0', - removable BOOL NOT NULL DEFAULT '0', - limits integer, - created_at DATETIME -); \ No newline at end of file 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 7d915fc5..00000000 --- a/source/sql/sqlite_up.sql +++ /dev/null @@ -1,86 +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 NOT NULL PRIMARY KEY AUTOINCREMENT, - username text NOT NULL UNIQUE, - password text, - email text, - api_key text, - api_secret text, - administrator bool, - created_at DATETIME, - UNIQUE (username, email) -); - -CREATE TABLE services ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - 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 DATETIME -); - -CREATE TABLE hits ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE, - latency float, - created_at DATETIME -); - -CREATE TABLE failures ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - issue text, - method text, - service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE, - created_at DATETIME -); - -CREATE TABLE checkins ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE, - check_interval integer, - api text, - created_at DATETIME -); - -CREATE TABLE communication ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - 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 DATETIME -); - - -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/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/index.html b/source/tmpl/index.html index 339dbda7..5b1cc735 100644 --- a/source/tmpl/index.html +++ b/source/tmpl/index.html @@ -73,14 +73,14 @@ Average Response
- {{.AvgUptime}}% - Total Uptime + {{.AvgUptime24}}% + Uptime last 24 Hours
- {{ if .AvgTime }} + {{ if .AvgUptime24 }}
@@ -127,4 +127,4 @@ - \ No newline at end of file + diff --git a/source/tmpl/service.html b/source/tmpl/service.html index 1e403cfc..aec043cc 100644 --- a/source/tmpl/service.html +++ b/source/tmpl/service.html @@ -51,7 +51,7 @@
- {{.AvgUptime}}% + {{.TotalUptime}}% Total Uptime
@@ -277,4 +277,4 @@ {{end}} - \ No newline at end of file + diff --git a/source/tmpl/settings.html b/source/tmpl/settings.html index be663205..601320da 100644 --- a/source/tmpl/settings.html +++ b/source/tmpl/settings.html @@ -237,4 +237,4 @@ - \ No newline at end of file + diff --git a/types/checkin.go b/types/checkin.go index fa68d15d..749c3749 100644 --- a/types/checkin.go +++ b/types/checkin.go @@ -1,13 +1,16 @@ package types -import "time" +import ( + "time" +) type Checkin struct { - Id int `db:"id,omitempty"` - Service int64 `db:"service"` - Interval int64 `db:"check_interval"` - Api string `db:"api"` - CreatedAt time.Time `db:"created_at"` + Id int64 `gorm:"primary_key;column:id"` + Service int64 `gorm:"index;column:service"` + Interval int64 `gorm:"column:check_interval"` + Api string `gorm:"column:api"` + 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:"-"` diff --git a/types/core.go b/types/core.go index b336c1d7..89f1b1ca 100644 --- a/types/core.go +++ b/types/core.go @@ -1,30 +1,45 @@ package types -import "time" +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 `db:"name" json:"name"` - Description string `db:"description" json:"description,omitempty"` - Config string `db:"config" json:"-"` - ApiKey string `db:"api_key" json:"-"` - ApiSecret string `db:"api_secret" json:"-"` - Style string `db:"style" json:"style,omitempty"` - Footer string `db:"footer" json:"footer,omitempty"` - Domain string `db:"domain" json:"domain,omitempty"` - Version string `db:"version" json:"version"` - MigrationId int64 `db:"migration_id" json:"migration_id,omitempty"` - UseCdn bool `db:"use_cdn" json:"using_cdn,omitempty"` - DbConnection string `json:"database"` - Started time.Time `json:"started_on"` - dbServices []*Service `json:"services,omitempty"` - Plugins []Info `json:"-"` - Repos []PluginJSON `json:"-"` - AllPlugins []PluginActions `json:"-"` - Communications []AllNotifiers `json:"-"` - CoreInterface `json:"-"` + 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:"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;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 } diff --git a/types/failure.go b/types/failure.go index aae741d7..1b37c466 100644 --- a/types/failure.go +++ b/types/failure.go @@ -1,14 +1,16 @@ package types -import "time" +import ( + "time" +) type Failure struct { - Id int `db:"id,omitempty" json:"id"` - Issue string `db:"issue" json:"issue"` - Method string `db:"method" json:"method,omitempty"` - Service int64 `db:"service" json:"service_id"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - FailureInterface `json:"-"` + Id int64 `gorm:"primary_key;column:id" json:"id"` + Issue string `gorm:"column:issue" json:"issue"` + Method string `gorm:"column:method" json:"method,omitempty"` + Service int64 `gorm:"index;column:service" json:"service_id"` + CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` + FailureInterface `gorm:"-" json:"-"` } type FailureInterface interface { diff --git a/types/service.go b/types/service.go index 41957ccc..568c5949 100644 --- a/types/service.go +++ b/types/service.go @@ -20,33 +20,33 @@ import ( ) type Service struct { - Id int64 `db:"id,omitempty" json:"id"` - Name string `db:"name" json:"name"` - Domain string `db:"domain" json:"domain"` - Expected string `db:"expected" json:"expected"` - ExpectedStatus int `db:"expected_status" json:"expected_status"` - Interval int `db:"check_interval" json:"check_interval"` - Type string `db:"check_type" json:"type"` - Method string `db:"method" json:"method"` - PostData string `db:"post_data" json:"post_data"` - Port int `db:"port" json:"port"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - Timeout int `db:"timeout" json:"timeout"` - Order int `db:"order_id" json:"order_id"` - Online bool `json:"online"` - Latency float64 `json:"latency"` - Online24Hours float32 `json:"24_hours_online"` - AvgResponse string `json:"avg_response"` - TotalUptime string `json:"uptime"` - Failures []*Failure `json:"failures"` - Checkins []*Checkin `json:"checkins"` - Running chan bool `json:"-"` - Checkpoint time.Time `json:"-"` - LastResponse string `json:"-"` - LastStatusCode int `json:"status_code"` - LastOnline time.Time `json:"last_online"` - DnsLookup float64 `json:"dns_lookup_time"` - ServiceInterface `json:"-"` + 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:"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:"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"` + 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"` + Failures []*Failure `gorm:"-" json:"failures"` + Checkins []*Checkin `gorm:"-" json:"checkins"` + Running chan bool `gorm:"-" json:"-"` + Checkpoint time.Time `gorm:"-" json:"-"` + LastResponse string `gorm:"-" json:"-"` + LastStatusCode int `gorm:"-" json:"status_code"` + LastOnline time.Time `gorm:"-" json:"last_online"` + DnsLookup float64 `gorm:"-" json:"dns_lookup_time"` + ServiceInterface `gorm:"-" json:"-"` } type ServiceInterface interface { @@ -56,6 +56,7 @@ type ServiceInterface interface { Delete() error // Basic Method functions AvgTime() float64 + OnlineSince(time.Time) float32 Online24() float32 SmallText() string GraphData() string @@ -65,13 +66,15 @@ type ServiceInterface interface { CreateFailure(*Failure) (int64, error) LimitedFailures() []*Failure AllFailures() []*Failure + TotalFailuresSince(time.Time) (uint64, error) + TotalFailures24() (uint64, error) TotalFailures() (uint64, error) - TotalFailures24Hours() (uint64, error) DeleteFailures() // Hits functions (successful responses) CreateHit(*Hit) (int64, error) Hits() ([]*Hit, error) TotalHits() (uint64, error) + TotalHitsSince(time.Time) (uint64, error) Sum() (float64, error) LimitedHits() ([]*Hit, error) SelectHitsGroupBy(string) ([]*Hit, error) diff --git a/types/types.go b/types/types.go index 448f5fd2..b8ba0ff0 100644 --- a/types/types.go +++ b/types/types.go @@ -16,9 +16,9 @@ package types import ( + "github.com/jinzhu/gorm" "net/http" "time" - "upper.io/db.v3/lib/sqlbuilder" ) type PluginInfo struct { @@ -41,7 +41,7 @@ type Info struct { type PluginActions interface { GetInfo() Info GetForm() string - OnLoad(sqlbuilder.Database) + OnLoad(db gorm.DB) SetInfo(map[string]interface{}) Info Routes() []Routing OnSave(map[string]interface{}) @@ -61,23 +61,15 @@ type PluginActions interface { type AllNotifiers interface{} +// Hit struct is a 'successful' ping or web response entry for a service. type Hit struct { - Id int `db:"id,omitempty"` - Service int64 `db:"service"` - Latency float64 `db:"latency"` - CreatedAt time.Time `db:"created_at"` -} - -type Config struct { - Connection string `yaml:"connection"` - Host string `yaml:"host"` - Database string `yaml:"database"` - User string `yaml:"user"` - Password string `yaml:"password"` - Port string `yaml:"port"` - Secret string `yaml:"secret"` + Id int64 `gorm:"primary_key;column:id"` + Service int64 `gorm:"index;column:service"` + Latency float64 `gorm:"column:latency"` + 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"` diff --git a/types/user.go b/types/user.go index 64feb20d..c18738ed 100644 --- a/types/user.go +++ b/types/user.go @@ -1,17 +1,20 @@ package types -import "time" +import ( + "time" +) type User struct { - Id int64 `db:"id,omitempty" json:"id"` - Username string `db:"username" json:"username"` - Password string `db:"password" json:"-"` - Email string `db:"email" json:"-"` - ApiKey string `db:"api_key" json:"api_key"` - ApiSecret string `db:"api_secret" json:"-"` - Admin bool `db:"administrator" json:"admin"` - CreatedAt time.Time `db:"created_at" json:"created_at"` - UserInterface `json:"-"` + Id int64 `gorm:"primary_key;column:id" json:"id"` + Username string `gorm:"type:varchar(100);unique;column:username;" json:"username"` + Password string `gorm:"column:password" json:"-"` + Email string `gorm:"type:varchar(100);unique;column:email" json:"-"` + ApiKey string `gorm:"column:api_key" json:"api_key"` + 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:"-"` } type UserInterface interface { diff --git a/utils/log.go b/utils/log.go index d4e3d3b8..d757a891 100644 --- a/utils/log.go +++ b/utils/log.go @@ -34,6 +34,10 @@ var ( LockLines sync.Mutex ) +func Logger() *lumberjack.Logger { + return ljLogger +} + func createLog(dir string) error { var err error _, err = os.Stat(dir + "/logs") diff --git a/utils/utils.go b/utils/utils.go index 909ffec2..e98afb57 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -16,8 +16,11 @@ package utils import ( + "errors" "github.com/ararog/timeago" + "io" "os" + "os/exec" "regexp" "strconv" "strings" @@ -97,6 +100,7 @@ func FileExists(name string) bool { } func DeleteFile(file string) error { + Log(1, "deleting file: "+file) err := os.Remove(file) if err != nil { return err @@ -107,3 +111,56 @@ func DeleteFile(file string) error { func DeleteDirectory(directory string) error { return os.RemoveAll(directory) } + +func Command(cmd string) (string, string, error) { + Log(1, "running command: "+cmd) + testCmd := exec.Command("sh", "-c", cmd) + 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 { + return "", "", err + } + + if errStdout != nil || errStderr != nil { + return "", "", errors.New("failed to capture stdout or stderr") + } + + outStr, errStr := string(stdout), string(stderr) + return outStr, errStr, err +} + +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 + } + } +}