Merge branch 'master' into line-notify

pull/61/head
Hunter Long 2018-09-06 13:04:08 -07:00 committed by GitHub
commit 82f2c47c8d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 1540 additions and 1097 deletions

View File

@ -4,7 +4,7 @@ os:
language: go language: go
go: go:
- "1.10.x" - "1.10.3"
go_import_path: github.com/hunterlong/statup go_import_path: github.com/hunterlong/statup

222
Gopkg.lock generated
View File

@ -1,6 +1,14 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. # This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
digest = "1:b62a3c5b37db602bf1158e921da1a762315a4c37855fd418a14498aa87a342d5"
name = "cloud.google.com/go"
packages = ["civil"]
pruneopts = "UT"
revision = "c728a003b238b26cef9ab6753a5dc424b331c3ad"
version = "v0.27.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
digest = "1:65796e5fdb94d94bc0ee6bc422aa3541dbe69ed4da275cb5a27f8fa141f4e35c" digest = "1:65796e5fdb94d94bc0ee6bc422aa3541dbe69ed4da275cb5a27f8fa141f4e35c"
@ -13,12 +21,20 @@
revision = "c02ca9a983da5807ddf7d796784928f5be4afd09" revision = "c02ca9a983da5807ddf7d796784928f5be4afd09"
[[projects]] [[projects]]
branch = "master" digest = "1:e92f5581902c345eb4ceffdcd4a854fb8f73cf436d47d837d1ec98ef1fe0a214"
digest = "1:4b79025e1eaa5726b92e409a46571f1998b56f0a2d881d6271ca616095eae46e" name = "github.com/StackExchange/wmi"
packages = ["."]
pruneopts = "UT"
revision = "5d049714c4a64225c3c79a7cf7d02f7fb5b96338"
version = "1.0.0"
[[projects]]
digest = "1:f1ec92a2b8473612547f6e13edbc8c8e6cda6c8be9c54b31958aad4a7ccaaa2b"
name = "github.com/ararog/timeago" name = "github.com/ararog/timeago"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "e9969cf18b8d5f04cc42f050e8b9968e152cd294" revision = "518814407569bf983ea81e1bf8b550dd4e7b34f3"
version = "0.0.1"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -29,28 +45,50 @@
revision = "a5fe2436ffcb3236e175e5149162b41cd28bd27d" revision = "a5fe2436ffcb3236e175e5149162b41cd28bd27d"
[[projects]] [[projects]]
digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" digest = "1:ffe9824d294da03b391f44e1ae8281281b4afc1bdaa9588c9097785e3af10cec"
name = "github.com/davecgh/go-spew" name = "github.com/davecgh/go-spew"
packages = ["spew"] packages = ["spew"]
pruneopts = "UT" pruneopts = "UT"
revision = "346938d642f2ec3594ed81d874461961cd0faa76" revision = "8991bc29aa16c548c550c7ff78260e27b9ab7c73"
version = "v1.1.0" version = "v1.1.1"
[[projects]] [[projects]]
branch = "master" 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" name = "github.com/fatih/structs"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "ebf56d35bba727c68ac77f56f2fcf90b181851aa" revision = "a720dfa8df582c51dee1b36feabb906bde1588bd"
version = "v1.0"
[[projects]] [[projects]]
branch = "master" digest = "1:64a5a67c69b70c2420e607a8545d674a23778ed9c3e80607bfd17b77c6c87f6a"
digest = "1:70a20b8adf085489a342d033b68b7fc27f4017c51e015b857387249493ee0561" 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" name = "github.com/go-sql-driver/mysql"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "99ff426eb706cffe92ff3d058e168b278cabf7c7" revision = "d523deb1b23d913de5bdada721a6071e71283618"
version = "v1.4.0"
[[projects]] [[projects]]
digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202" digest = "1:342378ac4dcb378a5448dd723f0784ae519383532f5e70ade24132c4c8693202"
@ -69,35 +107,74 @@
version = "v1.1.1" version = "v1.1.1"
[[projects]] [[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" name = "github.com/gorilla/mux"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "cb4698366aa625048f3b815af6a0dea8aef9280a" revision = "e3702bed27f0d39777b0b37b664b6280e8ef8fbf"
version = "v1.6.2"
[[projects]] [[projects]]
branch = "master" digest = "1:e72d1ebb8d395cf9f346fd9cbc652e5ae222dd85e0ac842dc57f175abed6d195"
digest = "1:6e0af49591c81ae6d542fd0ba41ea7206f4242471b384bebd206283b9f38e091"
name = "github.com/gorilla/securecookie" name = "github.com/gorilla/securecookie"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "78f3d318a8bf316cda921f25e96fd0b441c5173d" revision = "e59506cc896acb7f7bf732d4fdf5e25f7ccd8983"
[[projects]]
digest = "1:0fe783ea0c04c7d13f7c55d8f74b01b17e18a8320e7deecf578b41ef99b27205"
name = "github.com/gorilla/sessions"
packages = ["."]
pruneopts = "UT"
revision = "03b6f63cc43ef9c7240a635a5e22b13180e822b8"
version = "v1.1.1" 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]] [[projects]]
branch = "master" 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" name = "github.com/joho/godotenv"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "1709ab122c988931ad53508747b3c061400c2984" revision = "a79fa1e548e2c689c241d10173efd51e5d689d5b"
version = "v1.2.0"
[[projects]] [[projects]]
branch = "master" branch = "master"
@ -108,31 +185,43 @@
revision = "ae77be60afb1dcacde03767a8c37337fad28ac14" revision = "ae77be60afb1dcacde03767a8c37337fad28ac14"
[[projects]] [[projects]]
branch = "master" digest = "1:b18ffc558326ebaed3b4a175617f1e12ed4e3f53d6ebfe5ba372a3de16d22278"
digest = "1:37ce7d7d80531b227023331002c0d42b4b4b291a96798c82a049d03a54ba79e4"
name = "github.com/lib/pq" name = "github.com/lib/pq"
packages = [ packages = [
".", ".",
"hstore",
"oid", "oid",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "90697d60dd844d5ef6ff15135d0203f65d2f53b8" revision = "4ded0e9383f75c197b3a2aaa6d590ac52df6fd79"
version = "v1.0.0"
[[projects]] [[projects]]
branch = "master" digest = "1:3cafc6a5a1b8269605d9df4c6956d43d8011fc57f266ca6b9d04da6c09dee548"
digest = "1:05b7bf6e275fe5d56f479a944d1fd823437f6a2739610ae8359e137690d0a050"
name = "github.com/mattn/go-sqlite3" name = "github.com/mattn/go-sqlite3"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "b3511bfdd742af558b54eb6160aca9446d762a19" revision = "25ecb14adfc7543176f7d85291ec7dba82c6f7e4"
version = "v1.9.0"
[[projects]] [[projects]]
branch = "master" 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" name = "github.com/pkg/errors"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "816c9085562cd7ee03e7f8188a1cfd942858cded" revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]] [[projects]]
digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe" digest = "1:0028cb19b2e4c3112225cd871870f2d9cf49b9b4276531f03438a88e94be86fe"
@ -150,6 +239,29 @@
pruneopts = "UT" pruneopts = "UT"
revision = "6283090d169f51a2410b4e260341a01c9a4c0ca7" 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]] [[projects]]
digest = "1:18752d0b95816a1b777505a97f71c7467a8445b8ffb55631a7bf779f6ba4fa83" digest = "1:18752d0b95816a1b777505a97f71c7467a8445b8ffb55631a7bf779f6ba4fa83"
name = "github.com/stretchr/testify" name = "github.com/stretchr/testify"
@ -159,14 +271,27 @@
version = "v1.2.2" version = "v1.2.2"
[[projects]] [[projects]]
digest = "1:1ecf2a49df33be51e757d0033d5d51d5f784f35f68e5a38f797b2d3f03357d71" branch = "master"
digest = "1:68344dbfaa4179bb50a583eb8172ace3f1edaf3aebc24e68c03f549f6e6b60dc"
name = "golang.org/x/crypto" name = "golang.org/x/crypto"
packages = [ packages = [
"bcrypt", "bcrypt",
"blowfish", "blowfish",
"md4",
] ]
pruneopts = "UT" 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]] [[projects]]
digest = "1:c25289f43ac4a68d88b02245742347c94f1e108c534dda442188015ff80669b3" digest = "1:c25289f43ac4a68d88b02245742347c94f1e108c534dda442188015ff80669b3"
@ -185,12 +310,12 @@
revision = "2caba252f4dc53eaf6b553000885530023f54623" revision = "2caba252f4dc53eaf6b553000885530023f54623"
[[projects]] [[projects]]
branch = "v2" digest = "1:eebd52aee67d9f1e0af1859a584e14b1817581dd5f90d4585418c27b6a93c966"
digest = "1:e096ada745e034a059752c202717baee0379b69db9473a90f59d103c973384cf"
name = "gopkg.in/gomail.v2" name = "gopkg.in/gomail.v2"
packages = ["."] packages = ["."]
pruneopts = "UT" pruneopts = "UT"
revision = "81ebce5c23dfd25c6c67194b37d3dd3f338c98b1" revision = "41f3572897373c5538c50a2402db15db079fa4fd"
version = "2.0.0"
[[projects]] [[projects]]
digest = "1:c805e517269b0ba4c21ded5836019ed7d16953d4026cb7d00041d039c7906be9" digest = "1:c805e517269b0ba4c21ded5836019ed7d16953d4026cb7d00041d039c7906be9"
@ -201,25 +326,21 @@
version = "v2.1" version = "v2.1"
[[projects]] [[projects]]
digest = "1:0d1f3aba6c5c26e67b090b4eed519582fba0dff24d3e486fcb011d84ca3e7806" digest = "1:0a6ab450a46e158a97e3daf7da9df3bfd4f84420047fab6a65fb70b1337ce026"
name = "upper.io/db.v3" name = "upper.io/db.v3"
packages = [ packages = [
".", ".",
"internal/cache", "internal/cache",
"internal/cache/hashstructure", "internal/cache/hashstructure",
"internal/immutable", "internal/immutable",
"internal/sqladapter",
"internal/sqladapter/compat", "internal/sqladapter/compat",
"internal/sqladapter/exql", "internal/sqladapter/exql",
"lib/reflectx", "lib/reflectx",
"lib/sqlbuilder", "lib/sqlbuilder",
"mysql",
"postgresql",
"sqlite",
] ]
pruneopts = "UT" pruneopts = "UT"
revision = "d90922beee6de3f39c93ed677f6da82565d07154" revision = "199d13d76c7cfba05ea0327375056fdabc8bea80"
version = "v3.5.3" version = "v3.5.4"
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
@ -230,20 +351,23 @@
"github.com/ararog/timeago", "github.com/ararog/timeago",
"github.com/fatih/structs", "github.com/fatih/structs",
"github.com/go-yaml/yaml", "github.com/go-yaml/yaml",
"github.com/gorilla/handlers",
"github.com/gorilla/mux", "github.com/gorilla/mux",
"github.com/gorilla/sessions", "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/joho/godotenv",
"github.com/mkevac/debugcharts",
"github.com/pkg/errors", "github.com/pkg/errors",
"github.com/rendon/testcli", "github.com/rendon/testcli",
"github.com/stretchr/testify/assert", "github.com/stretchr/testify/assert",
"golang.org/x/crypto/bcrypt", "golang.org/x/crypto/bcrypt",
"gopkg.in/gomail.v2", "gopkg.in/gomail.v2",
"gopkg.in/natefinch/lumberjack.v2", "gopkg.in/natefinch/lumberjack.v2",
"upper.io/db.v3",
"upper.io/db.v3/lib/sqlbuilder", "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-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View File

@ -30,28 +30,44 @@
name = "github.com/GeertJohan/go.rice" name = "github.com/GeertJohan/go.rice"
[[constraint]] [[constraint]]
branch = "master"
name = "github.com/ararog/timeago" name = "github.com/ararog/timeago"
version = "0.0.1"
[[constraint]] [[constraint]]
branch = "master"
name = "github.com/fatih/structs" name = "github.com/fatih/structs"
version = "1.0.0"
[[constraint]] [[constraint]]
name = "github.com/go-yaml/yaml" name = "github.com/go-yaml/yaml"
version = "2.2.1" 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]] [[constraint]]
name = "github.com/gorilla/sessions" name = "github.com/gorilla/sessions"
version = "1.1.1" version = "1.1.2"
[[constraint]]
name = "github.com/jinzhu/gorm"
version = "1.9.1"
[[constraint]] [[constraint]]
branch = "master"
name = "github.com/joho/godotenv" name = "github.com/joho/godotenv"
version = "1.2.0"
[[constraint]] [[constraint]]
branch = "master" branch = "master"
name = "github.com/mkevac/debugcharts"
[[constraint]]
name = "github.com/pkg/errors" name = "github.com/pkg/errors"
version = "0.8.0"
[[constraint]] [[constraint]]
branch = "master" branch = "master"
@ -62,8 +78,12 @@
version = "1.2.2" version = "1.2.2"
[[constraint]] [[constraint]]
branch = "v2" branch = "master"
name = "golang.org/x/crypto"
[[constraint]]
name = "gopkg.in/gomail.v2" name = "gopkg.in/gomail.v2"
version = "2.0.0"
[[constraint]] [[constraint]]
name = "gopkg.in/natefinch/lumberjack.v2" name = "gopkg.in/natefinch/lumberjack.v2"
@ -71,7 +91,7 @@
[[constraint]] [[constraint]]
name = "upper.io/db.v3" name = "upper.io/db.v3"
version = "3.5.3" version = "3.5.4"
[prune] [prune]
go-tests = true go-tests = true

View File

@ -1,4 +1,4 @@
VERSION=0.52 VERSION=0.54
BINARY_NAME=statup BINARY_NAME=statup
GOPATH:=$(GOPATH) GOPATH:=$(GOPATH)
GOCMD=go 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 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 build: compile
$(GOBUILD) $(BUILDVERSION) -o $(BINARY_NAME) -v ./cmd $(GOBUILD) $(BUILDVERSION) -o $(BINARY_NAME) -v ./cmd
@ -127,6 +131,7 @@ dep:
dep ensure -vendor-only dep ensure -vendor-only
dev-deps: dep dev-deps: dep
$(GOGET) -u github.com/jinzhu/gorm/...
$(GOGET) github.com/stretchr/testify/assert $(GOGET) github.com/stretchr/testify/assert
$(GOGET) golang.org/x/tools/cmd/cover $(GOGET) golang.org/x/tools/cmd/cover
$(GOGET) github.com/mattn/goveralls $(GOGET) github.com/mattn/goveralls

View File

@ -24,13 +24,12 @@ import (
"github.com/hunterlong/statup/source" "github.com/hunterlong/statup/source"
"github.com/hunterlong/statup/types" "github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils" "github.com/hunterlong/statup/utils"
"github.com/jinzhu/gorm"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"io/ioutil" "io/ioutil"
"math/rand" "math/rand"
"net/http" "net/http"
"strings"
"time" "time"
"upper.io/db.v3/sqlite"
) )
const ( const (
@ -45,6 +44,8 @@ func CatchCLI(args []string) error {
LoadDotEnvs() LoadDotEnvs()
switch args[0] { switch args[0] {
case "seed":
handlers.DesktopInit(ipAddress, port)
case "app": case "app":
handlers.DesktopInit(ipAddress, port) handlers.DesktopInit(ipAddress, port)
case "version": case "version":
@ -94,7 +95,7 @@ func CatchCLI(args []string) error {
case "export": case "export":
var err error var err error
fmt.Printf("Statup v%v Exporting Static 'index.html' page...\n", VERSION) 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 { if err != nil {
utils.Log(4, "config.yml file not found") utils.Log(4, "config.yml file not found")
return err return err
@ -132,11 +133,11 @@ func CatchCLI(args []string) error {
func RunOnce() { func RunOnce() {
var err error var err error
core.Configs, err = core.LoadConfig() core.Configs, err = core.LoadConfig(utils.Directory)
if err != nil { if err != nil {
utils.Log(4, "config.yml file not found") 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 { if err != nil {
utils.Log(4, err) utils.Log(4, err)
} }
@ -234,21 +235,10 @@ func FakeSeed(plug types.PluginActions) {
fmt.Printf("\n" + BRAKER) fmt.Printf("\n" + BRAKER)
fmt.Println("\nCreating a SQLite database for testing, will be deleted automatically...") fmt.Println("\nCreating a SQLite database for testing, will be deleted automatically...")
sqlFake := sqlite.ConnectionURL{ core.DbSession, err = gorm.Open("sqlite", "./.plugin_test.db")
Database: "./.plugin_test.db",
}
core.DbSession, err = sqlite.Open(sqlFake)
if err != nil { if err != nil {
utils.Log(3, err) 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("Finished creating Test SQLite database")
fmt.Println("Inserting example services into test database...") fmt.Println("Inserting example services into test database...")

View File

@ -55,6 +55,8 @@ func parseFlags() {
func main() { func main() {
var err error var err error
parseFlags() parseFlags()
LoadDotEnvs()
source.Assets()
utils.InitLogs() utils.InitLogs()
args := flag.Args() args := flag.Args()
@ -68,13 +70,8 @@ func main() {
os.Exit(1) os.Exit(1)
} }
} }
source.Assets()
LoadDotEnvs()
utils.Log(1, fmt.Sprintf("Starting Statup v%v", VERSION)) utils.Log(1, fmt.Sprintf("Starting Statup v%v", VERSION))
core.Configs, err = core.LoadConfig(utils.Directory)
core.Configs, err = core.LoadConfig()
if err != nil { if err != nil {
utils.Log(3, err) utils.Log(3, err)
core.SetupMode = true core.SetupMode = true
@ -94,15 +91,18 @@ func LoadDotEnvs() error {
} }
func mainProcess() { func mainProcess() {
dir := utils.Directory
var err error 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 { if err != nil {
utils.Log(4, fmt.Sprintf("could not connect to database: %v", err)) utils.Log(4, fmt.Sprintf("could not connect to database: %v", err))
} }
core.Configs.MigrateDatabase()
core.RunDatabaseUpgrades()
core.InitApp() core.InitApp()
if !core.SetupMode { if !core.SetupMode {
LoadPlugins(false) LoadPlugins(false)
fmt.Println(handlers.RunHTTPServer(ipAddress, port)) fmt.Println(handlers.RunHTTPServer(ipAddress, port))
@ -170,7 +170,7 @@ func LoadPlugins(debug bool) {
if debug { if debug {
TestPlugin(plugActions) TestPlugin(plugActions)
} else { } else {
plugActions.OnLoad(core.DbSession) plugActions.OnLoad(*core.DbSession)
core.CoreApp.Plugins = append(core.CoreApp.Plugins, plugActions.GetInfo()) core.CoreApp.Plugins = append(core.CoreApp.Plugins, plugActions.GetInfo())
core.CoreApp.AllPlugins = append(core.CoreApp.AllPlugins, plugActions) core.CoreApp.AllPlugins = append(core.CoreApp.AllPlugins, plugActions)
} }

View File

@ -31,27 +31,30 @@ import (
"os" "os"
"strings" "strings"
"testing" "testing"
"time"
) )
var ( var (
route *mux.Router route *mux.Router
testSession *sessions.Session testSession *sessions.Session
dir string dir string
SERVICE_SINCE, _ = time.Parse(time.RFC3339, "2018-08-30T10:42:08-07:00")
) )
func init() { func init() {
dir = utils.Directory dir = utils.Directory
os.Remove(dir + "/statup.db") }
//os.Remove(gopath+"/cmd/config.yml")
os.RemoveAll(dir + "/cmd/assets") func Clean() {
os.RemoveAll(dir + "/logs") utils.DeleteFile(dir + "/config.yml")
utils.DeleteFile(dir + "/statup.db")
utils.DeleteDirectory(dir + "/assets")
utils.DeleteDirectory(dir + "/logs")
} }
func RunInit(t *testing.T) { func RunInit(t *testing.T) {
source.Assets() source.Assets()
os.Remove(dir + "/statup.db") Clean()
os.Remove(dir + "/cmd/config.yml")
os.Remove(dir + "/cmd/index.html")
route = handlers.Router() route = handlers.Router()
LoadDotEnvs() LoadDotEnvs()
core.CoreApp = core.NewCore() core.CoreApp = core.NewCore()
@ -60,35 +63,55 @@ func RunInit(t *testing.T) {
func TestRunAll(t *testing.T) { func TestRunAll(t *testing.T) {
//t.Parallel() //t.Parallel()
databases := []string{"sqlite", "postgres", "mysql"} databases := []string{"postgres", "sqlite", "mysql"}
if os.Getenv("ONLY_DB") != "" { if os.Getenv("ONLY_DB") != "" {
databases = []string{os.Getenv("ONLY_DB")} databases = []string{os.Getenv("ONLY_DB")}
} }
for _, dbt := range databases { for _, dbt := range databases {
t.Run(dbt+" init", func(t *testing.T) { t.Run(dbt+" init", func(t *testing.T) {
RunInit(t) RunInit(t)
}) })
t.Run(dbt+" load database config", func(t *testing.T) { t.Run(dbt+" Save Config", func(t *testing.T) {
RunMakeDatabaseConfig(t, dbt) RunSaveConfig(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+" Load Configs", func(t *testing.T) { t.Run(dbt+" Load Configs", func(t *testing.T) {
RunLoadConfig(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) { t.Run(dbt+" Select Core", func(t *testing.T) {
RunSelectCoreMYQL(t, dbt) RunSelectCoreMYQL(t, dbt)
t.Log(core.CoreApp)
}) })
t.Run(dbt+" Select Services", func(t *testing.T) { t.Run(dbt+" Select Services", func(t *testing.T) {
RunSelectAllMysqlServices(t) RunSelectAllMysqlServices(t)
}) })
t.Run(dbt+" Select Comms", func(t *testing.T) { t.Run(dbt+" Select Comms", func(t *testing.T) {
t.SkipNow()
RunSelectAllMysqlCommunications(t) RunSelectAllMysqlCommunications(t)
}) })
t.Run(dbt+" Create Users", func(t *testing.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) { t.Run(dbt+" Create Failing Service", func(t *testing.T) {
RunBadService_Create(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) RunBadService_Check(t)
}) })
t.Run(dbt+" Select Hits", func(t *testing.T) { t.Run(dbt+" Select Hits", func(t *testing.T) {
@ -183,35 +206,24 @@ func TestRunAll(t *testing.T) {
RunSettingsHandler(t) RunSettingsHandler(t)
}) })
t.Run(dbt+" Cleanup", func(t *testing.T) { t.Run(dbt+" Cleanup", func(t *testing.T) {
//Cleanup(t) core.Configs.Close()
core.DbSession = nil
//Clean()
}) })
//<-done
} }
} }
func Cleanup(t *testing.T) { func RunSaveConfig(t *testing.T, db string) {
core.DbSession.ClearCache() var err error
err := core.DbSession.Close()
assert.Nil(t, err)
}
func RunMakeDatabaseConfig(t *testing.T, db string) {
port := 5432 port := 5432
if db == "mysql" { if db == "mysql" {
port = 3306 port = 3306
} }
core.Configs = &core.DbConfig{DbConfig: &types.DbConfig{
//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{
DbConn: db, DbConn: db,
DbHost: os.Getenv("DB_HOST"), DbHost: os.Getenv("DB_HOST"),
DbUser: os.Getenv("DB_USER"), DbUser: os.Getenv("DB_USER"),
@ -227,39 +239,49 @@ func RunMakeDatabaseConfig(t *testing.T, db string) {
Error: nil, Error: nil,
Location: dir, Location: dir,
}} }}
err := config.Save() core.Configs, err = core.Configs.Save()
assert.Nil(t, err) 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.Nil(t, err)
assert.Equal(t, db, core.Configs.Connection) err = core.Configs.CreateDatabase()
err = core.DbConnection(core.Configs.Connection, false, dir)
assert.Nil(t, err) assert.Nil(t, err)
} }
func RunDatabaseMigrations(t *testing.T, db string) { func RunDatabaseMigrations(t *testing.T, db string) {
err := core.RunDatabaseUpgrades() err := core.Configs.MigrateDatabase()
assert.Nil(t, err) assert.Nil(t, err)
} }
func RunInsertSampleData(t *testing.T) { func RunInsertSampleData(t *testing.T) {
err := core.LoadSampleData() core.Configs.SeedDatabase()
assert.Nil(t, err) //assert.Nil(t, err)
} }
func RunLoadConfig(t *testing.T) { func RunLoadConfig(t *testing.T) {
var err error var err error
core.Configs, err = core.LoadConfig() core.Configs, err = core.LoadConfig(dir)
t.Log(core.Configs)
assert.Nil(t, err) assert.Nil(t, err)
assert.NotNil(t, core.Configs) 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) { func RunSelectCoreMYQL(t *testing.T, db string) {
var err error var err error
core.CoreApp, err = core.SelectCore() core.CoreApp, err = core.SelectCore()
if err != nil {
t.FailNow()
}
assert.Nil(t, err) 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.Equal(t, db, core.CoreApp.DbConnection)
assert.NotEmpty(t, core.CoreApp.ApiKey) assert.NotEmpty(t, core.CoreApp.ApiKey)
assert.NotEmpty(t, core.CoreApp.ApiSecret) assert.NotEmpty(t, core.CoreApp.ApiSecret)
@ -270,12 +292,12 @@ func RunSelectAllMysqlServices(t *testing.T) {
var err error var err error
services, err := core.CoreApp.SelectAllServices() services, err := core.CoreApp.SelectAllServices()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 5, len(services)) assert.Equal(t, 18, len(services))
} }
func RunSelectAllMysqlCommunications(t *testing.T) { func RunSelectAllMysqlCommunications(t *testing.T) {
var err error var err error
notifiers.Collections = core.DbSession.Collection("communication") notifiers.Collections = core.DbSession.Table("communication").Model(&notifiers.Notification{})
comms := notifiers.Load() comms := notifiers.Load()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 3, len(comms)) assert.Equal(t, 3, len(comms))
@ -284,19 +306,19 @@ func RunSelectAllMysqlCommunications(t *testing.T) {
func RunUser_SelectAll(t *testing.T) { func RunUser_SelectAll(t *testing.T) {
users, err := core.SelectAllUsers() users, err := core.SelectAllUsers()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 2, len(users)) assert.Equal(t, 3, len(users))
} }
func RunUser_Create(t *testing.T) { func RunUser_Create(t *testing.T) {
user := core.ReturnUser(&types.User{ user := core.ReturnUser(&types.User{
Username: "admin", Username: "hunterlong",
Password: "admin", Password: "password123",
Email: "info@testuser.com", Email: "info@gmail.com",
Admin: true, Admin: true,
}) })
id, err := user.Create() id, err := user.Create()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(1), id) assert.Equal(t, int64(2), id)
user2 := core.ReturnUser(&types.User{ user2 := core.ReturnUser(&types.User{
Username: "superadmin", Username: "superadmin",
Password: "admin", Password: "admin",
@ -305,7 +327,7 @@ func RunUser_Create(t *testing.T) {
}) })
id, err = user2.Create() id, err = user2.Create()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(2), id) assert.Equal(t, int64(3), id)
} }
func RunUser_Update(t *testing.T) { func RunUser_Update(t *testing.T) {
@ -342,7 +364,10 @@ func RunSelectAllServices(t *testing.T) {
var err error var err error
services, err := core.CoreApp.SelectAllServices() services, err := core.CoreApp.SelectAllServices()
assert.Nil(t, err) 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) { func RunOneService_Check(t *testing.T) {
@ -365,8 +390,7 @@ func RunService_Create(t *testing.T) {
}) })
id, err := service.Create() id, err := service.Create()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(6), id) assert.Equal(t, int64(19), id)
t.Log(service)
} }
func RunService_ToJSON(t *testing.T) { func RunService_ToJSON(t *testing.T) {
@ -379,15 +403,20 @@ func RunService_ToJSON(t *testing.T) {
func RunService_AvgTime(t *testing.T) { func RunService_AvgTime(t *testing.T) {
service := core.SelectService(1) service := core.SelectService(1)
assert.NotNil(t, service) assert.NotNil(t, service)
avg := service.AvgUptime() avg := service.AvgUptime24()
assert.Equal(t, "100", avg) assert.Equal(t, "100", avg)
} }
func RunService_Online24(t *testing.T) { func RunService_Online24(t *testing.T) {
service := core.SelectService(1) service := core.SelectService(1)
assert.NotNil(t, service) assert.NotNil(t, service)
online := service.Online24() online := service.OnlineSince(SERVICE_SINCE)
assert.Equal(t, float32(100), online) 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) { func RunService_GraphData(t *testing.T) {
@ -413,13 +442,13 @@ func RunBadService_Create(t *testing.T) {
}) })
id, err := service.Create() id, err := service.Create()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int64(7), id) assert.Equal(t, int64(20), id)
} }
func RunBadService_Check(t *testing.T) { func RunBadService_Check(t *testing.T) {
service := core.SelectService(7) service := core.SelectService(18)
assert.NotNil(t, service) assert.NotNil(t, service)
assert.Equal(t, "Bad Service", service.Name) assert.Equal(t, "Failing URL", service.Name)
for i := 0; i <= 10; i++ { for i := 0; i <= 10; i++ {
service.Check(true) service.Check(true)
} }
@ -431,9 +460,7 @@ func RunDeleteService(t *testing.T) {
assert.NotNil(t, service) assert.NotNil(t, service)
assert.Equal(t, "JSON API Tester", service.Name) assert.Equal(t, "JSON API Tester", service.Name)
assert.True(t, service.IsRunning()) assert.True(t, service.IsRunning())
t.Log(service.Running)
err := service.Delete() err := service.Delete()
t.Log(service.Running)
assert.False(t, service.IsRunning()) assert.False(t, service.IsRunning())
assert.Nil(t, err) assert.Nil(t, err)
} }
@ -441,15 +468,12 @@ func RunDeleteService(t *testing.T) {
func RunCreateService_Hits(t *testing.T) { func RunCreateService_Hits(t *testing.T) {
services := core.CoreApp.Services() services := core.CoreApp.Services()
assert.NotNil(t, services) assert.NotNil(t, services)
assert.Equal(t, 6, len(services)) assert.Equal(t, 19, len(services))
for i := 0; i <= 15; i++ {
for _, s := range services { for _, s := range services {
var service *core.Service service := s.Check(true)
service = s.Check(true)
assert.NotNil(t, service) assert.NotNil(t, service)
} }
} }
}
func RunService_Hits(t *testing.T) { func RunService_Hits(t *testing.T) {
service := core.SelectService(1) service := core.SelectService(1)
@ -460,10 +484,10 @@ func RunService_Hits(t *testing.T) {
} }
func RunService_Failures(t *testing.T) { func RunService_Failures(t *testing.T) {
service := core.SelectService(7) service := core.SelectService(18)
assert.NotNil(t, service) assert.NotNil(t, service)
assert.Equal(t, "Bad Service", service.Name) assert.Equal(t, "Failing URL", service.Name)
assert.NotEmpty(t, service.Failures) assert.NotEmpty(t, service.AllFailures())
} }
func RunService_LimitedHits(t *testing.T) { func RunService_LimitedHits(t *testing.T) {
@ -479,7 +503,7 @@ func RunIndexHandler(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
route.ServeHTTP(rr, req) 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")) assert.True(t, strings.Contains(rr.Body.String(), "footer"))
} }
@ -499,7 +523,7 @@ func RunPrometheusHandler(t *testing.T) {
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
route.ServeHTTP(rr, req) route.ServeHTTP(rr, req)
t.Log(rr.Body.String()) 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)) assert.True(t, handlers.IsAuthenticated(req))
} }
@ -515,7 +539,7 @@ func RunFailingPrometheusHandler(t *testing.T) {
func RunLoginHandler(t *testing.T) { func RunLoginHandler(t *testing.T) {
form := url.Values{} form := url.Values{}
form.Add("username", "admin") form.Add("username", "admin")
form.Add("password", "admin") form.Add("password", "password123")
req, err := http.NewRequest("POST", "/dashboard", strings.NewReader(form.Encode())) req, err := http.NewRequest("POST", "/dashboard", strings.NewReader(form.Encode()))
req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
assert.Nil(t, err) assert.Nil(t, err)

View File

@ -210,7 +210,9 @@ func RecordSuccess(s *Service) {
s.Online = true s.Online = true
s.LastOnline = time.Now() s.LastOnline = time.Now()
data := &types.Hit{ data := &types.Hit{
Service: s.Id,
Latency: s.Latency, Latency: s.Latency,
CreatedAt: time.Now(),
} }
utils.Log(1, fmt.Sprintf("Service %v Successful: %0.2f ms", s.Name, data.Latency*1000)) utils.Log(1, fmt.Sprintf("Service %v Successful: %0.2f ms", s.Name, data.Latency*1000))
s.CreateHit(data) s.CreateHit(data)

View File

@ -48,26 +48,25 @@ func FindCheckin(api string) *types.Checkin {
func (s *Service) AllCheckins() []*types.Checkin { func (s *Service) AllCheckins() []*types.Checkin {
var checkins []*types.Checkin var checkins []*types.Checkin
col := DbSession.Collection("checkins").Find("service", s.Id).OrderBy("-id") col := checkinDB().Where("service = ?", s.Id).Order("id desc")
col.All(&checkins) col.Find(&checkins)
s.Checkins = checkins s.Checkins = checkins
return checkins return checkins
} }
func (u *Checkin) Create() (int64, error) { func (u *Checkin) Create() (int64, error) {
u.CreatedAt = time.Now() u.CreatedAt = time.Now()
uuid, err := DbSession.Collection("checkins").Insert(u) row := checkinDB().Create(u)
if uuid == nil { if row.Error == nil {
utils.Log(2, err) utils.Log(2, row.Error)
return 0, err return 0, row.Error
} }
fmt.Println("new checkin: ", uuid) return u.Id, row.Error
return uuid.(int64), err
} }
func SelectCheckinApi(api string) *Checkin { func SelectCheckinApi(api string) *Checkin {
var checkin *Checkin var checkin *Checkin
DbSession.Collection("checkins").Find("api", api).One(&checkin) checkinDB().Where("api = ?", api).Find(&checkin)
return checkin return checkin
} }

View File

@ -23,29 +23,28 @@ import (
"github.com/hunterlong/statup/utils" "github.com/hunterlong/statup/utils"
"io/ioutil" "io/ioutil"
"os" "os"
"time"
) )
func LoadConfig() (*types.Config, error) { func LoadConfig(directory string) (*DbConfig, error) {
var configs *types.DbConfig
if os.Getenv("DB_CONN") != "" { if os.Getenv("DB_CONN") != "" {
utils.Log(1, "DB_CONN environment variable was found, waiting for database...") utils.Log(1, "DB_CONN environment variable was found, waiting for database...")
return LoadUsingEnv() return LoadUsingEnv()
} }
Configs = new(types.Config) file, err := ioutil.ReadFile(directory + "/config.yml")
file, err := ioutil.ReadFile(utils.Directory + "/config.yml")
if err != nil { 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 { if err != nil {
return nil, err return nil, err
} }
CoreApp.DbConnection = Configs.Connection Configs = &DbConfig{configs}
return Configs, err return Configs, err
} }
func LoadUsingEnv() (*types.Config, error) { func LoadUsingEnv() (*DbConfig, error) {
Configs = new(types.Config) Configs = new(DbConfig)
if os.Getenv("DB_CONN") == "" { if os.Getenv("DB_CONN") == "" {
return nil, errors.New("Missing DB_CONN environment variable") return nil, errors.New("Missing DB_CONN environment variable")
} }
@ -61,12 +60,12 @@ func LoadUsingEnv() (*types.Config, error) {
if os.Getenv("DB_DATABASE") == "" { if os.Getenv("DB_DATABASE") == "" {
return nil, errors.New("Missing DB_DATABASE environment variable") return nil, errors.New("Missing DB_DATABASE environment variable")
} }
Configs.Connection = os.Getenv("DB_CONN") Configs.DbConn = os.Getenv("DB_CONN")
Configs.Host = os.Getenv("DB_HOST") Configs.DbHost = os.Getenv("DB_HOST")
Configs.Port = os.Getenv("DB_PORT") Configs.DbPort = int(utils.StringInt(os.Getenv("DB_PORT")))
Configs.User = os.Getenv("DB_USER") Configs.DbUser = os.Getenv("DB_USER")
Configs.Password = os.Getenv("DB_PASS") 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.DbConnection = os.Getenv("DB_CONN")
CoreApp.Name = os.Getenv("NAME") CoreApp.Name = os.Getenv("NAME")
CoreApp.Domain = os.Getenv("DOMAIN") CoreApp.Domain = os.Getenv("DOMAIN")
@ -89,32 +88,19 @@ func LoadUsingEnv() (*types.Config, error) {
Email: "info@localhost.com", Email: "info@localhost.com",
}} }}
err := DbConnection(dbConfig.DbConn, true, utils.Directory) err := dbConfig.Connect(true, utils.Directory)
if err != nil { if err != nil {
utils.Log(4, err) utils.Log(4, err)
return nil, err return nil, err
} }
exists, err := DbSession.Collection("core").Find().Exists() exists := DbSession.HasTable("core")
if !exists { if !exists {
utils.Log(1, fmt.Sprintf("Core database does not exist, creating now!")) utils.Log(1, fmt.Sprintf("Core database does not exist, creating now!"))
DropDatabase() dbConfig.DropDatabase()
CreateDatabase() dbConfig.CreateDatabase()
CoreApp = &Core{Core: &types.Core{ CoreApp, err = dbConfig.InsertCore()
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)
if err != nil { if err != nil {
utils.Log(3, err) utils.Log(3, err)
} }

View File

@ -34,10 +34,10 @@ type Core struct {
} }
var ( var (
Configs *types.Config Configs *DbConfig // Configs holds all of the config.yml and database info
CoreApp *Core CoreApp *Core // CoreApp is a global variable that contains many elements
SetupMode bool SetupMode bool // SetupMode will be true if Statup does not have a database connection
VERSION string VERSION string // VERSION is set on build automatically by setting a -ldflag
) )
func init() { func init() {
@ -51,12 +51,6 @@ func NewCore() *Core {
return CoreApp return CoreApp
} }
func InsertCore(c *Core) error {
col := DbSession.Collection("core")
_, err := col.Insert(c.Core)
return err
}
func (c *Core) ToCore() *types.Core { func (c *Core) ToCore() *types.Core {
return c.Core return c.Core
} }
@ -72,25 +66,27 @@ func InitApp() {
func InsertNotifierDB() error { func InsertNotifierDB() error {
if DbSession == nil { if DbSession == nil {
err := DbConnection(CoreApp.DbConnection, false, utils.Directory) err := Configs.Connect(false, utils.Directory)
if err != nil { if err != nil {
return errors.New("database connection has not been created") return errors.New("database connection has not been created")
} }
} }
notifiers.Collections = DbSession.Collection("communication") notifiers.Collections = commDB()
return nil return nil
} }
// UpdateCore will update the CoreApp variable inside of the 'core' table in database
func UpdateCore(c *Core) (*Core, error) { func UpdateCore(c *Core) (*Core, error) {
res := DbSession.Collection("core").Find().Limit(1) db := coreDB().Update(&c)
err := res.Update(c.Core) return c, db.Error
return c, err
} }
// UsingAssets will return true if /assets folder is present
func (c Core) UsingAssets() bool { func (c Core) UsingAssets() bool {
return source.UsingAssets(utils.Directory) return source.UsingAssets(utils.Directory)
} }
// SassVars opens the file /assets/scss/variables.scss to be edited in Theme
func (c Core) SassVars() string { func (c Core) SassVars() string {
if !source.UsingAssets(utils.Directory) { if !source.UsingAssets(utils.Directory) {
return "" return ""
@ -98,6 +94,7 @@ func (c Core) SassVars() string {
return source.OpenAsset(utils.Directory, "scss/variables.scss") 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 { func (c Core) BaseSASS() string {
if !source.UsingAssets(utils.Directory) { if !source.UsingAssets(utils.Directory) {
return "" return ""
@ -105,6 +102,8 @@ func (c Core) BaseSASS() string {
return source.OpenAsset(utils.Directory, "scss/base.scss") 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 { func (c Core) MobileSASS() string {
if !source.UsingAssets(utils.Directory) { if !source.UsingAssets(utils.Directory) {
return "" return ""
@ -112,6 +111,7 @@ func (c Core) MobileSASS() string {
return source.OpenAsset(utils.Directory, "scss/mobile.scss") return source.OpenAsset(utils.Directory, "scss/mobile.scss")
} }
// AllOnline will be true if all services are online
func (c Core) AllOnline() bool { func (c Core) AllOnline() bool {
for _, s := range CoreApp.Services() { for _, s := range CoreApp.Services() {
if !s.Online { if !s.Online {
@ -121,49 +121,42 @@ func (c Core) AllOnline() bool {
return true return true
} }
func SelectLastMigration() (int64, error) { // SelectCore will return the CoreApp global variable and the settings/configs for Statup
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
}
func SelectCore() (*Core, error) { func SelectCore() (*Core, error) {
var c *types.Core if DbSession == nil {
exists := DbSession.Collection("core").Exists() return nil, errors.New("database has not been initiated yet.")
}
exists := DbSession.HasTable("core")
if !exists { if !exists {
return nil, errors.New("core database has not been setup yet.") return nil, errors.New("core database has not been setup yet.")
} }
err := DbSession.Collection("core").Find().One(&c) db := coreDB().First(&CoreApp)
if err != nil { if db.Error != nil {
return nil, err return nil, db.Error
} }
CoreApp.Core = c CoreApp.DbConnection = Configs.DbConn
CoreApp.DbConnection = Configs.Connection
CoreApp.Version = VERSION CoreApp.Version = VERSION
CoreApp.SelectAllServices() CoreApp.SelectAllServices()
if os.Getenv("USE_CDN") == "true" { if os.Getenv("USE_CDN") == "true" {
CoreApp.UseCdn = true CoreApp.UseCdn = true
} }
//store = sessions.NewCookieStore([]byte(core.ApiSecret)) //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 type ServiceOrder []*types.Service
func (c ServiceOrder) Len() int { return len(c) } 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) 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 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 { func (c *Core) Services() []*Service {
var services []*Service var services []*Service
servs := CoreApp.GetServices() servs := CoreApp.GetServices()
sort.Sort(ServiceOrder(servs)) sort.Sort(ServiceOrder(servs))
CoreApp.SetServices(servs)
for _, ser := range servs { for _, ser := range servs {
services = append(services, ReturnService(ser)) services = append(services, ReturnService(ser))
} }

View File

@ -24,11 +24,13 @@ import (
) )
var ( 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() { func init() {
dir = utils.Directory dir = utils.Directory
utils.InitLogs() utils.InitLogs()
@ -36,51 +38,68 @@ func init() {
} }
func TestNewCore(t *testing.T) { func TestNewCore(t *testing.T) {
testCore = NewCore() utils.DeleteFile(dir + "/config.yml")
assert.NotNil(t, testCore) utils.DeleteFile(dir + "/statup.db")
testCore.Name = "Tester" CoreApp = NewCore()
assert.NotNil(t, CoreApp)
CoreApp.Name = "Tester"
} }
func TestDbConfig_Save(t *testing.T) { func TestDbConfig_Save(t *testing.T) {
testConfig = &DbConfig{&types.DbConfig{ var err error
Configs = &DbConfig{&types.DbConfig{
DbConn: "sqlite", DbConn: "sqlite",
Project: "Tester", Project: "Tester",
Location: dir, Location: dir,
}} }}
err := testConfig.Save() Configs, err = Configs.Save()
assert.Nil(t, err) 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) { func TestDbConnection(t *testing.T) {
err := DbConnection(testConfig.DbConn, false, dir) err := Configs.Connect(false, dir)
assert.Nil(t, err) assert.Nil(t, err)
} }
func TestCreateDatabase(t *testing.T) { func TestDropDatabase(t *testing.T) {
err := CreateDatabase() err := Configs.DropDatabase()
assert.Nil(t, err) assert.Nil(t, err)
} }
func TestInsertCore(t *testing.T) { func TestSeedSchemaDatabase(t *testing.T) {
err := InsertCore(testCore) err := Configs.CreateDatabase()
assert.Nil(t, err) 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) { func TestSelectCore(t *testing.T) {
core, err := SelectCore() core, err := SelectCore()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "Tester", core.Name) assert.Equal(t, "Awesome Status", 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)
} }
func TestInsertNotifierDB(t *testing.T) { func TestInsertNotifierDB(t *testing.T) {

View File

@ -18,95 +18,161 @@ package core
import ( import (
"fmt" "fmt"
"github.com/go-yaml/yaml" "github.com/go-yaml/yaml"
"github.com/hunterlong/statup/source" "github.com/hunterlong/statup/notifiers"
"github.com/hunterlong/statup/types" "github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils" "github.com/hunterlong/statup/utils"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mssql"
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/postgres"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"os" "os"
"strings"
"time" "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 ( var (
sqliteSettings sqlite.ConnectionURL DbSession *gorm.DB
postgresSettings postgresql.ConnectionURL
mysqlSettings mysql.ConnectionURL
DbSession sqlbuilder.Database
currentMigration int64
) )
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(&notifiers.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 { type DbConfig struct {
*types.DbConfig *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 var err error
if dbType == "sqlite" { if DbSession != nil {
sqliteSettings = sqlite.ConnectionURL{ DbSession = nil
Database: location + "/statup.db",
} }
DbSession, err = sqlite.Open(sqliteSettings) var conn, dbType string
if err != nil { dbType = Configs.DbConn
return err switch dbType {
case "sqlite":
conn = utils.Directory + "/statup.db"
dbType = "sqlite3"
case "mysql":
if Configs.DbPort == 0 {
Configs.DbPort = 3306
} }
} else if dbType == "mysql" { host := fmt.Sprintf("%v:%v", Configs.DbHost, Configs.DbPort)
if Configs.Port == "" { conn = fmt.Sprintf("%v:%v@tcp(%v)/%v?charset=utf8&parseTime=True&loc=Local", Configs.DbUser, Configs.DbPass, host, Configs.DbData)
Configs.Port = "3306" case "postgres":
if Configs.DbPort == 0 {
Configs.DbPort = 5432
} }
host := fmt.Sprintf("%v:%v", Configs.Host, Configs.Port) 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)
mysqlSettings = mysql.ConnectionURL{ case "mssql":
Database: Configs.Database, if Configs.DbPort == 0 {
Host: host, Configs.DbPort = 1433
User: Configs.User,
Password: Configs.Password,
Options: map[string]string{"parseTime": "true", "charset": "utf8"},
} }
DbSession, err = mysql.Open(mysqlSettings) 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 err != nil {
if retry { if retry {
utils.Log(1, fmt.Sprintf("Database connection to '%v' is not available, trying again in 5 seconds...", host)) utils.Log(1, fmt.Sprintf("Database connection to '%v' is not available, trying again in 5 seconds...", Configs.DbHost))
return waitForDb(dbType) return db.waitForDb()
} else { } else {
fmt.Println("ERROR:", err)
return err return err
} }
} }
} else { err = DbSession.DB().Ping()
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()
if err == nil { 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 return err
} }
func waitForDb(dbType string) error { func (db *DbConfig) waitForDb() error {
time.Sleep(5 * time.Second) 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() { func DatabaseMaintence() {
for range time.Tick(60 * time.Minute) { for range time.Tick(60 * time.Minute) {
utils.Log(1, "Checking for database records older than 7 days...") 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) { func DeleteAllSince(table string, date time.Time) {
sql := fmt.Sprintf("DELETE FROM %v WHERE created_at < '%v';", table, date.Format("2006-01-02")) sql := fmt.Sprintf("DELETE FROM %v WHERE created_at < '%v';", table, date.Format("2006-01-02"))
_, err := DbSession.Exec(db.Raw(sql)) db := DbSession.Raw(sql)
if err != nil { defer db.Close()
utils.Log(2, err) if db.Error != nil {
utils.Log(2, db.Error)
} }
} }
// Update will save the config.yml file
func (c *DbConfig) Update() error { func (c *DbConfig) Update() error {
var err error var err error
config, err := os.Create(utils.Directory + "/config.yml") config, err := os.Create(utils.Directory + "/config.yml")
@ -141,162 +210,115 @@ func (c *DbConfig) Update() error {
return err return err
} }
func (c *DbConfig) Save() error { // Save will initially create the config.yml file
func (c *DbConfig) Save() (*DbConfig, error) {
var err error var err error
config, err := os.Create(utils.Directory + "/config.yml") config, err := os.Create(utils.Directory + "/config.yml")
if err != nil { if err != nil {
utils.Log(4, err) 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) data, err := yaml.Marshal(c.DbConfig)
if err != nil { if err != nil {
utils.Log(3, err) utils.Log(3, err)
return err return nil, err
} }
config.WriteString(string(data)) config.WriteString(string(data))
config.Close() defer config.Close()
return c, err
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()
// CreateCore will initialize the global variable 'CoreApp". This global variable contains most of Statup app.
func (c *DbConfig) CreateCore() *Core {
newCore := &types.Core{ newCore := &types.Core{
Name: c.Project, Name: c.Project,
Description: c.Description, Description: c.Description,
Config: "config.yml", Config: "config.yml",
ApiKey: utils.NewSHA1Hash(9), ApiKey: c.ApiKey,
ApiSecret: utils.NewSHA1Hash(16), ApiSecret: c.ApiSecret,
Domain: c.Domain, Domain: c.Domain,
MigrationId: time.Now().Unix(), MigrationId: time.Now().Unix(),
} }
col := DbSession.Collection("core") db := coreDB().Create(&newCore)
_, err = col.Insert(newCore) if db.Error == nil {
if err == nil {
CoreApp = &Core{Core: newCore} CoreApp = &Core{Core: newCore}
} }
CoreApp, err := SelectCore()
CoreApp, err = SelectCore()
if err != nil { if err != nil {
utils.Log(4, err) utils.Log(4, err)
} }
CoreApp.DbConnection = c.DbConn return CoreApp
c.ApiKey = CoreApp.ApiKey
c.ApiSecret = CoreApp.ApiSecret
return err
} }
func versionHigher(migrate int64) bool { // SeedDatabase will insert many elements into the database. This is only ran in Dev/Test move
if CoreApp.MigrationId < migrate { func (db *DbConfig) SeedDatabase() (string, string, error) {
return true 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 { // DropDatabase will DROP each table Statup created
for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { func (db *DbConfig) DropDatabase() error {
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 {
utils.Log(1, "Dropping Database Tables...") utils.Log(1, "Dropping Database Tables...")
down, err := source.SqlBox.String("down.sql") err := DbSession.DropTableIfExists("checkins")
if err != nil { err = DbSession.DropTableIfExists("communication")
return err err = DbSession.DropTableIfExists("core")
} err = DbSession.DropTableIfExists("failures")
requests := strings.Split(down, ";") err = DbSession.DropTableIfExists("hits")
for _, request := range requests { err = DbSession.DropTableIfExists("services")
_, err := DbSession.Exec(request) err = DbSession.DropTableIfExists("users")
if err != nil { return err.Error
utils.Log(2, err)
}
}
return err
} }
func CreateDatabase() error { // CreateDatabase will CREATE TABLES for each of the Statup elements
func (db *DbConfig) CreateDatabase() error {
utils.Log(1, "Creating Database Tables...") utils.Log(1, "Creating Database Tables...")
sql := "postgres_up.sql" err := DbSession.CreateTable(&types.Checkin{})
if CoreApp.DbConnection == "mysql" { err = DbSession.Table("communication").CreateTable(&notifiers.Notification{})
sql = "mysql_up.sql" err = DbSession.Table("core").CreateTable(&types.Core{})
} else if CoreApp.DbConnection == "sqlite" { err = DbSession.CreateTable(&types.Failure{})
sql = "sqlite_up.sql" err = DbSession.CreateTable(&types.Hit{})
err = DbSession.CreateTable(&types.Service{})
err = DbSession.CreateTable(&types.User{})
utils.Log(1, "Statup Database Created")
return err.Error
} }
up, err := source.SqlBox.String(sql)
requests := strings.Split(up, ";") // MigrateDatabase will migrate the database structure to current version.
for _, request := range requests { // This function will NOT remove previous records, tables or columns from the database.
_, err := DbSession.Exec(request) // If this function has an issue, it will ROLLBACK to the previous state.
if err != nil { func (db *DbConfig) MigrateDatabase() error {
utils.Log(2, err) 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() tx = tx.AutoMigrate(&types.Service{}, &types.User{}, &types.Hit{}, &types.Failure{}, &types.Checkin{}).Table("core").AutoMigrate(&types.Core{}).Table("communication").AutoMigrate(&notifiers.Notification{})
//db.QueryRow("INSERT INTO core (secret, version) VALUES ($1, $2);", secret, VERSION).Scan() if tx.Error != nil {
utils.Log(1, "Database Created") tx.Rollback()
//SampleData() utils.Log(3, fmt.Sprintf("Statup Database could not be migrated: %v", tx.Error))
return err return tx.Error
}
utils.Log(1, "Statup Database Migrated")
return tx.Commit().Error
} }
func (c *DbConfig) Clean() *DbConfig { func (c *DbConfig) Clean() *DbConfig {

View File

@ -19,12 +19,12 @@ import (
"github.com/fatih/structs" "github.com/fatih/structs"
"github.com/hunterlong/statup/notifiers" "github.com/hunterlong/statup/notifiers"
"github.com/hunterlong/statup/types" "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 { for _, p := range CoreApp.AllPlugins {
p.OnLoad(db) p.OnLoad(*db)
} }
} }

View File

@ -25,7 +25,11 @@ import (
) )
func injectDatabase() { func injectDatabase() {
DbConnection(Configs.Connection, false, utils.Directory) Configs.Connect(false, utils.Directory)
}
func GenerateSeed() {
} }
func ExportIndexHTML() string { func ExportIndexHTML() string {

View File

@ -32,23 +32,19 @@ func (s *Service) CreateFailure(f *types.Failure) (int64, error) {
f.CreatedAt = time.Now() f.CreatedAt = time.Now()
f.Service = s.Id f.Service = s.Id
s.Failures = append(s.Failures, f) s.Failures = append(s.Failures, f)
col := DbSession.Collection("failures") row := failuresDB().Create(f)
uuid, err := col.Insert(f) if row.Error != nil {
if err != nil { utils.Log(3, row.Error)
utils.Log(3, err) return 0, row.Error
return 0, err
} }
if uuid == nil { return f.Id, row.Error
return 0, err
}
return uuid.(int64), err
} }
func (s *Service) AllFailures() []*types.Failure { func (s *Service) AllFailures() []*types.Failure {
var fails []*types.Failure var fails []*types.Failure
col := DbSession.Collection("failures").Find("service", s.Id).OrderBy("-id") col := failuresDB().Where("service = ?", s.Id).Order("id desc")
err := col.All(&fails) err := col.Find(&fails)
if err != nil { if err.Error != nil {
utils.Log(3, fmt.Sprintf("Issue getting failures for service %v, %v", s.Name, err)) utils.Log(3, fmt.Sprintf("Issue getting failures for service %v, %v", s.Name, err))
return nil return nil
} }
@ -56,8 +52,8 @@ func (s *Service) AllFailures() []*types.Failure {
} }
func (u *Service) DeleteFailures() { func (u *Service) DeleteFailures() {
_, err := DbSession.Exec(`DELETE FROM failures WHERE service = ?`, u.Id) err := DbSession.Exec(`DELETE FROM failures WHERE service = ?`, u.Id)
if err != nil { if err.Error != nil {
utils.Log(3, fmt.Sprintf("failed to delete all failures: %v", err)) utils.Log(3, fmt.Sprintf("failed to delete all failures: %v", err))
} }
u.Failures = nil u.Failures = nil
@ -65,48 +61,48 @@ func (u *Service) DeleteFailures() {
func (s *Service) LimitedFailures() []*Failure { func (s *Service) LimitedFailures() []*Failure {
var failArr []*Failure var failArr []*Failure
col := DbSession.Collection("failures").Find("service", s.Id).OrderBy("-id").Limit(10) col := failuresDB().Where("service = ?", s.Id).Order("id desc").Limit(10)
col.All(&failArr) col.Find(&failArr)
return 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 { func (f *Failure) Ago() string {
got, _ := timeago.TimeAgoWithTime(time.Now(), f.CreatedAt) got, _ := timeago.TimeAgoWithTime(time.Now(), f.CreatedAt)
return got return got
} }
func (f *Failure) Delete() error { func (f *Failure) Delete() error {
col := DbSession.Collection("failures").Find("id", f.Id) db := failuresDB().Delete(f)
return col.Delete() return db.Error
} }
func CountFailures() uint64 { func CountFailures() uint64 {
col := DbSession.Collection("failures").Find() var count uint64
amount, err := col.Count() err := failuresDB().Count(&count)
if err != nil { if err.Error != nil {
utils.Log(2, err) utils.Log(2, err.Error)
return 0 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) { func (s *Service) TotalFailures() (uint64, error) {
col := DbSession.Collection("failures").Find("service", s.Id) var count uint64
amount, err := col.Count() rows := failuresDB().Where("service = ?", s.Id)
return amount, err err := rows.Count(&count)
return count, err.Error
} }
func (s *Service) TotalFailures24Hours() (uint64, error) { func (s *Service) TotalFailuresSince(ago time.Time) (uint64, error) {
col := DbSession.Collection("failures").Find("service", s.Id) var count uint64
amount, err := col.Count() rows := failuresDB().Where("service = ? AND created_at > ?", s.Id, ago.Format("2006-01-02 15:04:05"))
return amount, err err := rows.Count(&count)
return count, err.Error
} }
func (f *Failure) ParseError() string { func (f *Failure) ParseError() string {

View File

@ -19,40 +19,33 @@ import (
"github.com/hunterlong/statup/types" "github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils" "github.com/hunterlong/statup/utils"
"time" "time"
"upper.io/db.v3"
) )
type Hit struct { type Hit struct {
*types.Hit *types.Hit
} }
func hitCol() db.Collection {
return DbSession.Collection("hits")
}
func (s *Service) CreateHit(h *types.Hit) (int64, error) { func (s *Service) CreateHit(h *types.Hit) (int64, error) {
h.CreatedAt = time.Now() db := hitsDB().Create(h)
h.Service = s.Id if db.Error != nil {
uuid, err := hitCol().Insert(h) utils.Log(2, db.Error)
if uuid == nil { return 0, db.Error
utils.Log(2, err)
return 0, err
} }
return uuid.(int64), err return h.Id, db.Error
} }
func (s *Service) Hits() ([]*Hit, error) { func (s *Service) Hits() ([]*Hit, error) {
var hits []*Hit var hits []*Hit
col := hitCol().Find("service", s.Id).OrderBy("-id") col := hitsDB().Where("service = ?", s.Id).Order("id desc")
err := col.All(&hits) err := col.Find(&hits)
return hits, err return hits, err.Error
} }
func (s *Service) LimitedHits() ([]*Hit, error) { func (s *Service) LimitedHits() ([]*Hit, error) {
var hits []*Hit var hits []*Hit
col := hitCol().Find("service", s.Id).OrderBy("-id").Limit(1024) col := hitsDB().Where("service = ?", s.Id).Order("id desc").Limit(1024)
err := col.All(&hits) err := col.Find(&hits)
return reverseHits(hits), err return reverseHits(hits), err.Error
} }
func reverseHits(input []*Hit) []*Hit { func reverseHits(input []*Hit) []*Hit {
@ -64,15 +57,23 @@ func reverseHits(input []*Hit) []*Hit {
func (s *Service) SelectHitsGroupBy(group string) ([]*Hit, error) { func (s *Service) SelectHitsGroupBy(group string) ([]*Hit, error) {
var hits []*Hit var hits []*Hit
col := hitCol().Find("service", s.Id) col := hitsDB().Where("service = ?", s.Id)
err := col.All(&hits) err := col.Find(&hits)
return hits, err return hits, err.Error
} }
func (s *Service) TotalHits() (uint64, error) { func (s *Service) TotalHits() (uint64, error) {
col := hitCol().Find("service", s.Id) var count uint64
amount, err := col.Count() col := hitsDB().Where("service = ?", s.Id)
return amount, err 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) { func (s *Service) Sum() (float64, error) {

View File

@ -22,7 +22,6 @@ import (
"github.com/hunterlong/statup/utils" "github.com/hunterlong/statup/utils"
"strconv" "strconv"
"time" "time"
"upper.io/db.v3"
) )
type Service struct { type Service struct {
@ -30,16 +29,12 @@ type Service struct {
} }
func ReturnService(s *types.Service) *Service { func ReturnService(s *types.Service) *Service {
return &Service{Service: s} return &Service{s}
}
func serviceCol() db.Collection {
return DbSession.Collection("services")
} }
func SelectService(id int64) *Service { func SelectService(id int64) *Service {
for _, s := range CoreApp.Services() { for _, s := range CoreApp.Services() {
if s.Id == id { if s.Service.Id == id {
return s return s
} }
} }
@ -49,11 +44,10 @@ func SelectService(id int64) *Service {
func (c *Core) SelectAllServices() ([]*types.Service, error) { func (c *Core) SelectAllServices() ([]*types.Service, error) {
var services []*types.Service var services []*types.Service
var servs []*types.Service var servs []*types.Service
col := serviceCol().Find().OrderBy("order_id") db := servicesDB().Find(&services).Order("order_id desc")
err := col.All(&services) if db.Error != nil {
if err != nil { utils.Log(3, fmt.Sprintf("service error: %v", db.Error))
utils.Log(3, fmt.Sprintf("service error: %v", err)) return nil, db.Error
return nil, err
} }
for _, ser := range services { for _, ser := range services {
single := ReturnService(ser) single := ReturnService(ser)
@ -63,7 +57,7 @@ func (c *Core) SelectAllServices() ([]*types.Service, error) {
servs = append(servs, single.Service) servs = append(servs, single.Service)
} }
CoreApp.SetServices(servs) CoreApp.SetServices(servs)
return services, err return services, db.Error
} }
func (s *Service) ToJSON() string { func (s *Service) ToJSON() string {
@ -84,12 +78,17 @@ func (s *Service) AvgTime() float64 {
} }
func (s *Service) Online24() float32 { func (s *Service) Online24() float32 {
total, _ := s.TotalHits() ago := time.Now().Add(-24 * time.Hour)
failed, _ := s.TotalFailures24Hours() return s.OnlineSince(ago)
}
func (s *Service) OnlineSince(ago time.Time) float32 {
failed, _ := s.TotalFailuresSince(ago)
if failed == 0 { if failed == 0 {
s.Online24Hours = 100.00 s.Online24Hours = 100.00
return s.Online24Hours return s.Online24Hours
} }
total, _ := s.TotalHitsSince(ago)
if total == 0 { if total == 0 {
s.Online24Hours = 0 s.Online24Hours = 0
return s.Online24Hours return s.Online24Hours
@ -150,16 +149,17 @@ func (s *Service) GraphData() string {
var d []*DateScan var d []*DateScan
since := time.Now().Add(time.Hour*-24 + time.Minute*0 + time.Second*0) since := time.Now().Add(time.Hour*-24 + time.Minute*0 + time.Second*0)
sql := GroupDataBy("hits", s.Id, since, "minute") 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 { if err != nil {
utils.Log(2, err) utils.Log(2, err)
return "" return ""
} }
for dated.Next() { for rows.Next() {
gd := new(DateScan) gd := new(DateScan)
var tt string var tt string
var ff float64 var ff float64
err := dated.Scan(&tt, &ff) err := rows.Scan(&tt, &ff)
if err != nil { if err != nil {
utils.Log(2, fmt.Sprintf("Issue loading chart data for service %v, %v", s.Name, err)) 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) return string(data)
} }
func (s *Service) AvgUptime() string { func (s *Service) AvgUptime24() string {
failed, _ := s.TotalFailures() ago := time.Now().Add(-24 * time.Hour)
total, _ := s.TotalHits() return s.AvgUptime(ago)
if failed == 0 {
s.TotalUptime = "100"
return s.TotalUptime
} }
func (s *Service) AvgUptime(ago time.Time) string {
failed, _ := s.TotalFailuresSince(ago)
if failed == 0 {
return "100"
}
total, _ := s.TotalHitsSince(ago)
if total == 0 { if total == 0 {
s.TotalUptime = "0" return "0"
return s.TotalUptime
} }
percent := float64(failed) / float64(total) * 100 percent := float64(failed) / float64(total) * 100
percent = 100 - percent percent = 100 - percent
if percent < 0 { if percent < 0 {
percent = 0 percent = 0
} }
s.TotalUptime = fmt.Sprintf("%0.2f", percent) amount := fmt.Sprintf("%0.2f", percent)
if s.TotalUptime == "100.00" { if amount == "100.00" {
s.TotalUptime = "100" 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 { func (s *Service) index() int {
@ -217,25 +235,27 @@ func updateService(service *Service) {
} }
func (u *Service) Delete() error { func (u *Service) Delete() error {
res := serviceCol().Find("id", u.Id) err := servicesDB().Delete(u)
err := res.Delete() if err.Error != nil {
if err != nil { utils.Log(3, fmt.Sprintf("Failed to delete service %v. %v", u.Name, err.Error))
utils.Log(3, fmt.Sprintf("Failed to delete service %v. %v", u.Name, err)) return err.Error
return err
} }
u.Close() u.Close()
CoreApp.RemoveService(u.index()) CoreApp.RemoveService(u.index())
OnDeletedService(u) 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 { func (u *Service) Update(restart bool) error {
u.CreatedAt = time.Now() u.CreatedAt = time.Now()
res := serviceCol().Find("id", u.Id) err := servicesDB().Update(u)
err := res.Update(u) if err.Error != nil {
if err != nil {
utils.Log(3, fmt.Sprintf("Failed to update service %v. %v", u.Name, err)) utils.Log(3, fmt.Sprintf("Failed to update service %v. %v", u.Name, err))
return err return err.Error
} }
if restart { if restart {
u.Close() u.Close()
@ -245,20 +265,19 @@ func (u *Service) Update(restart bool) error {
go u.CheckQueue(true) go u.CheckQueue(true)
} }
OnUpdateService(u) OnUpdateService(u)
return err return err.Error
} }
func (u *Service) Create() (int64, error) { func (u *Service) Create() (int64, error) {
u.CreatedAt = time.Now() u.CreatedAt = time.Now()
uuid, err := serviceCol().Insert(u) db := servicesDB().Create(u)
if uuid == nil { if db.Error != nil {
utils.Log(3, fmt.Sprintf("Failed to create service %v. %v", u.Name, err)) utils.Log(3, fmt.Sprintf("Failed to create service %v #%v: %v", u.Name, u.Id, db.Error))
return 0, err return 0, db.Error
} }
u.Id = uuid.(int64)
u.Start() u.Start()
CoreApp.AddService(u.Service) CoreApp.AddService(u.Service)
return uuid.(int64), err return u.Id, nil
} }
func CountOnline() int { func CountOnline() int {

View File

@ -26,23 +26,29 @@ var (
newServiceId int64 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) { func TestSelectAllServices(t *testing.T) {
services := CoreApp.Services() services := CoreApp.Services()
for _, s := range services { for _, s := range services {
service := s.Check(true) 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)) assert.Equal(t, 18, len(services))
}
func TestSelectHTTPService(t *testing.T) {
service := SelectService(1)
assert.Equal(t, "Google", service.Name)
assert.Equal(t, "http", service.Type)
} }
func TestSelectTCPService(t *testing.T) { func TestSelectTCPService(t *testing.T) {
services := CoreApp.Services()
assert.Equal(t, 18, len(services))
service := SelectService(5) service := SelectService(5)
assert.NotNil(t, service)
assert.Equal(t, "Google DNS", service.Name) assert.Equal(t, "Google DNS", service.Name)
assert.Equal(t, "tcp", service.Type) assert.Equal(t, "tcp", service.Type)
} }
@ -105,9 +111,14 @@ func TestCheckTCPService(t *testing.T) {
} }
func TestServiceOnline24Hours(t *testing.T) { func TestServiceOnline24Hours(t *testing.T) {
service := SelectService(5) since, err := time.Parse(time.RFC3339, SERVICE_SINCE)
amount := service.Online24() assert.Nil(t, err)
assert.Equal(t, float32(100), amount) 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) { func TestServiceSmallText(t *testing.T) {
@ -117,30 +128,35 @@ func TestServiceSmallText(t *testing.T) {
} }
func TestServiceAvgUptime(t *testing.T) { func TestServiceAvgUptime(t *testing.T) {
service := SelectService(5) since, err := time.Parse(time.RFC3339, SERVICE_SINCE)
uptime := service.AvgUptime() assert.Nil(t, err)
assert.Equal(t, "100", uptime) 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) { func TestServiceHits(t *testing.T) {
service := SelectService(5) service := SelectService(5)
hits, err := service.Hits() hits, err := service.Hits()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int(2), len(hits)) assert.Equal(t, int(5), len(hits))
} }
func TestServiceLimitedHits(t *testing.T) { func TestServiceLimitedHits(t *testing.T) {
service := SelectService(5) service := SelectService(5)
hits, err := service.LimitedHits() hits, err := service.LimitedHits()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, int(2), len(hits)) assert.Equal(t, int(5), len(hits))
} }
func TestServiceTotalHits(t *testing.T) { func TestServiceTotalHits(t *testing.T) {
service := SelectService(5) service := SelectService(5)
hits, err := service.TotalHits() hits, err := service.TotalHits()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, uint64(0x2), hits) assert.Equal(t, uint64(0x5), hits)
} }
func TestServiceSum(t *testing.T) { func TestServiceSum(t *testing.T) {
@ -176,7 +192,6 @@ func TestCreateService(t *testing.T) {
func TestViewNewService(t *testing.T) { func TestViewNewService(t *testing.T) {
newService := SelectService(newServiceId) newService := SelectService(newServiceId)
assert.Equal(t, "That'll do 🐢", newService.Name) assert.Equal(t, "That'll do 🐢", newService.Name)
} }
func TestCreateFailingHTTPService(t *testing.T) { func TestCreateFailingHTTPService(t *testing.T) {
@ -195,10 +210,12 @@ func TestCreateFailingHTTPService(t *testing.T) {
assert.NotZero(t, newServiceId) assert.NotZero(t, newServiceId)
newService := SelectService(newServiceId) newService := SelectService(newServiceId)
assert.Equal(t, "Bad URL", newService.Name) assert.Equal(t, "Bad URL", newService.Name)
t.Log("new service ID: ", newServiceId)
} }
func TestServiceFailedCheck(t *testing.T) { func TestServiceFailedCheck(t *testing.T) {
service := SelectService(7) service := SelectService(20)
assert.Equal(t, "Bad URL", service.Name)
checked := service.Check(true) checked := service.Check(true)
assert.Equal(t, "Bad URL", checked.Name) assert.Equal(t, "Bad URL", checked.Name)
assert.False(t, checked.Online) assert.False(t, checked.Online)
@ -219,10 +236,11 @@ func TestCreateFailingTCPService(t *testing.T) {
assert.NotZero(t, newServiceId) assert.NotZero(t, newServiceId)
newService := SelectService(newServiceId) newService := SelectService(newServiceId)
assert.Equal(t, "Bad TCP", newService.Name) assert.Equal(t, "Bad TCP", newService.Name)
t.Log("new failing tcp service ID: ", newServiceId)
} }
func TestServiceFailedTCPCheck(t *testing.T) { func TestServiceFailedTCPCheck(t *testing.T) {
service := SelectService(8) service := SelectService(21)
checked := service.Check(true) checked := service.Check(true)
assert.Equal(t, "Bad TCP", checked.Name) assert.Equal(t, "Bad TCP", checked.Name)
assert.False(t, checked.Online) assert.False(t, checked.Online)
@ -244,13 +262,13 @@ func TestDeleteService(t *testing.T) {
count, err := CoreApp.SelectAllServices() count, err := CoreApp.SelectAllServices()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 8, len(count)) assert.Equal(t, 21, len(count))
err = service.Delete() err = service.Delete()
assert.Nil(t, err) assert.Nil(t, err)
services := CoreApp.Services() services := CoreApp.Services()
assert.Equal(t, 7, len(services)) assert.Equal(t, 20, len(services))
} }
func TestServiceCloseRoutine(t *testing.T) { func TestServiceCloseRoutine(t *testing.T) {

View File

@ -28,36 +28,29 @@ type User struct {
} }
func ReturnUser(u *types.User) *User { func ReturnUser(u *types.User) *User {
return &User{User: u} return &User{u}
} }
func SelectUser(id int64) (*User, error) { func SelectUser(id int64) (*User, error) {
var user *User var user User
col := DbSession.Collection("users") err := usersDB().First(&user, id)
res := col.Find("id", id) return &user, err.Error
err := res.One(&user)
return user, err
} }
func SelectUsername(username string) (*User, error) { func SelectUsername(username string) (*User, error) {
var user *User var user User
col := DbSession.Collection("users") res := usersDB().Where("username = ?", username)
res := col.Find("username", username) err := res.First(&user)
err := res.One(&user) return &user, err.Error
return user, err
} }
func (u *User) Delete() error { func (u *User) Delete() error {
col := DbSession.Collection("users") return usersDB().Delete(u).Error
user := col.Find("id", u.Id)
return user.Delete()
} }
func (u *User) Update() error { func (u *User) Update() error {
u.CreatedAt = time.Now() u.CreatedAt = time.Now()
col := DbSession.Collection("users") return usersDB().Update(u).Error
user := col.Find("id", u.Id)
return user.Update(u)
} }
func (u *User) Create() (int64, error) { func (u *User) Create() (int64, error) {
@ -65,39 +58,36 @@ func (u *User) Create() (int64, error) {
u.Password = utils.HashPassword(u.Password) u.Password = utils.HashPassword(u.Password)
u.ApiKey = utils.NewSHA1Hash(5) u.ApiKey = utils.NewSHA1Hash(5)
u.ApiSecret = utils.NewSHA1Hash(10) u.ApiSecret = utils.NewSHA1Hash(10)
col := DbSession.Collection("users") db := usersDB().Create(u)
uuid, err := col.Insert(u) if db.Error != nil {
if err != nil { return 0, db.Error
return 0, err
} }
if uuid == nil { if u.Id == 0 {
utils.Log(3, fmt.Sprintf("Failed to create user %v. %v", u.Username, err)) utils.Log(3, fmt.Sprintf("Failed to create user %v. %v", u.Username, db.Error))
return 0, err return 0, db.Error
} }
return uuid.(int64), err return u.Id, db.Error
} }
func SelectAllUsers() ([]*User, error) { func SelectAllUsers() ([]*User, error) {
var users []*User var users []*User
col := DbSession.Collection("users").Find() db := usersDB().Find(&users)
err := col.All(&users) if db.Error != nil {
if err != nil { utils.Log(3, fmt.Sprintf("Failed to load all users. %v", db.Error))
utils.Log(3, fmt.Sprintf("Failed to load all users. %v", err))
} }
return users, err return users, db.Error
} }
func AuthUser(username, password string) (*User, bool) { func AuthUser(username, password string) (*User, bool) {
var auth bool
user, err := SelectUsername(username) user, err := SelectUsername(username)
if err != nil { if err != nil {
utils.Log(2, err) utils.Log(2, err)
return nil, false return nil, false
} }
if CheckHash(password, user.Password) { if CheckHash(password, user.Password) {
auth = true return user, true
} }
return user, auth return nil, false
} }
func CheckHash(password, hash string) bool { func CheckHash(password, hash string) bool {

View File

@ -36,13 +36,13 @@ func TestCreateUser(t *testing.T) {
func TestSelectAllUsers(t *testing.T) { func TestSelectAllUsers(t *testing.T) {
users, err := SelectAllUsers() users, err := SelectAllUsers()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 1, len(users)) assert.Equal(t, 2, len(users))
} }
func TestSelectUser(t *testing.T) { func TestSelectUser(t *testing.T) {
user, err := SelectUser(1) user, err := SelectUser(1)
assert.Nil(t, err) 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) assert.True(t, user.Admin)
} }
@ -50,7 +50,7 @@ func TestSelectUsername(t *testing.T) {
user, err := SelectUsername("hunter") user, err := SelectUsername("hunter")
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, "test@email.com", user.Email) 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) assert.True(t, user.Admin)
} }
@ -80,7 +80,7 @@ func TestCreateUser2(t *testing.T) {
func TestSelectAllUsersAgain(t *testing.T) { func TestSelectAllUsersAgain(t *testing.T) {
users, err := SelectAllUsers() users, err := SelectAllUsers()
assert.Nil(t, err) assert.Nil(t, err)
assert.Equal(t, 2, len(users)) assert.Equal(t, 3, len(users))
} }
func TestAuthUser(t *testing.T) { func TestAuthUser(t *testing.T) {
@ -88,12 +88,12 @@ func TestAuthUser(t *testing.T) {
assert.True(t, auth) assert.True(t, auth)
assert.NotNil(t, user) assert.NotNil(t, user)
assert.Equal(t, "user@email.com", user.Email) 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) assert.True(t, user.Admin)
} }
func TestFailedAuthUser(t *testing.T) { func TestFailedAuthUser(t *testing.T) {
user, auth := AuthUser("hunter", "wrongpassword") user, auth := AuthUser("hunterlong", "wrongpassword")
assert.False(t, auth) assert.False(t, auth)
assert.Nil(t, user) assert.Nil(t, user)
} }
@ -111,3 +111,8 @@ func TestDeleteUser(t *testing.T) {
err = user.Delete() err = user.Delete()
assert.Nil(t, err) assert.Nil(t, err)
} }
func TestDbConfig_Close(t *testing.T) {
err := Configs.Close()
assert.Nil(t, err)
}

142
dev/mysql_seed.sql Normal file
View File

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

142
dev/postgres_seed.sql Normal file
View File

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

142
dev/sqlite_seed.sql Normal file
View File

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

View File

@ -36,17 +36,23 @@ const (
func injectDatabase() { func injectDatabase() {
core.NewCore() core.NewCore()
core.Configs = new(types.Config) core.Configs = new(core.DbConfig)
core.Configs.Connection = "sqlite" core.Configs.DbConn = "sqlite"
core.CoreApp.DbConnection = "sqlite" core.CoreApp.DbConnection = "sqlite"
core.CoreApp.Version = "DEV" core.CoreApp.Version = "DEV"
core.DbConnection("sqlite", false, utils.Directory) core.Configs.Connect(false, utils.Directory)
core.InitApp() 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) { func TestInit(t *testing.T) {
t.SkipNow() Clean()
injectDatabase()
} }
func formatJSON(res string, out interface{}) { func formatJSON(res string, out interface{}) {

View File

@ -97,6 +97,8 @@ func TestProcessSetupHandler(t *testing.T) {
rr := httptest.NewRecorder() rr := httptest.NewRecorder()
Router().ServeHTTP(rr, req) Router().ServeHTTP(rr, req)
assert.Equal(t, 303, rr.Code) assert.Equal(t, 303, rr.Code)
assert.FileExists(t, dir+"/config.yml")
assert.FileExists(t, dir+"/statup.db")
} }
func TestCheckSetupHandler(t *testing.T) { func TestCheckSetupHandler(t *testing.T) {
@ -143,7 +145,9 @@ func TestServiceChartHandler(t *testing.T) {
assert.Equal(t, 200, rr.Code) assert.Equal(t, 200, rr.Code)
t.Log(body) t.Log(body)
assert.Contains(t, body, "var ctx_1") 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_3")
assert.Contains(t, body, "var ctx_4")
assert.Contains(t, body, "var ctx_5") assert.Contains(t, body, "var ctx_5")
} }

View File

@ -27,7 +27,7 @@ type index struct {
} }
func IndexHandler(w http.ResponseWriter, r *http.Request) { func IndexHandler(w http.ResponseWriter, r *http.Request) {
if core.CoreApp.DbConnection == "" { if core.Configs == nil {
http.Redirect(w, r, "/setup", http.StatusSeeOther) http.Redirect(w, r, "/setup", http.StatusSeeOther)
return return
} }
@ -42,12 +42,12 @@ func DesktopInit(ip string, port int) {
var err error var err error
exists := utils.FileExists(utils.Directory + "/statup.db") exists := utils.FileExists(utils.Directory + "/statup.db")
if exists { if exists {
core.Configs, err = core.LoadConfig() core.Configs, err = core.LoadConfig(utils.Directory)
if err != nil { if err != nil {
utils.Log(3, err) utils.Log(3, err)
return return
} }
err = core.DbConnection(core.Configs.Connection, false, utils.Directory) err = core.Configs.Connect(false, utils.Directory)
if err != nil { if err != nil {
utils.Log(3, err) utils.Log(3, err)
return return
@ -68,24 +68,28 @@ func DesktopInit(ip string, port int) {
Location: utils.Directory, Location: utils.Directory,
}} }}
err = config.Save() config, err = config.Save()
if err != nil { if err != nil {
utils.Log(4, err) utils.Log(4, err)
} }
config.DropDatabase()
config.CreateDatabase()
core.CoreApp = config.CreateCore()
if err != nil { if err != nil {
utils.Log(3, err) utils.Log(3, err)
return return
} }
core.Configs, err = core.LoadConfig() core.Configs, err = core.LoadConfig(utils.Directory)
if err != nil { if err != nil {
utils.Log(3, err) utils.Log(3, err)
config.Error = err config.Error = err
return return
} }
err = core.DbConnection(core.Configs.Connection, false, utils.Directory) err = core.Configs.Connect(false, utils.Directory)
if err != nil { if err != nil {
utils.Log(3, err) utils.Log(3, err)
core.DeleteConfig() core.DeleteConfig()

View File

@ -16,7 +16,6 @@
package handlers package handlers
import ( import (
"github.com/hunterlong/statup/core"
"net/http" "net/http"
"strings" "strings"
) )
@ -53,6 +52,6 @@ func PluginsDownloadHandler(w http.ResponseWriter, r *http.Request) {
//vars := mux.Vars(router) //vars := mux.Vars(router)
//name := vars["name"] //name := vars["name"]
//DownloadPlugin(name) //DownloadPlugin(name)
core.LoadConfig() //core.LoadConfig(utils.Directory)
http.Redirect(w, r, "/plugins", http.StatusSeeOther) http.Redirect(w, r, "/plugins", http.StatusSeeOther)
} }

View File

@ -61,13 +61,6 @@ func PrometheusHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte(output)) 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 { func isAuthorized(r *http.Request) bool {
var token string var token string
tokens, ok := r.Header["Authorization"] tokens, ok := r.Header["Authorization"]

View File

@ -51,7 +51,6 @@ func Router() *mux.Router {
r.Handle("/setup", http.HandlerFunc(SetupHandler)).Methods("GET") r.Handle("/setup", http.HandlerFunc(SetupHandler)).Methods("GET")
r.Handle("/setup", http.HandlerFunc(ProcessSetupHandler)).Methods("POST") r.Handle("/setup", http.HandlerFunc(ProcessSetupHandler)).Methods("POST")
r.Handle("/dashboard", http.HandlerFunc(DashboardHandler)).Methods("GET") 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("/dashboard", http.HandlerFunc(LoginHandler)).Methods("POST")
r.Handle("/logout", http.HandlerFunc(LogoutHandler)) r.Handle("/logout", http.HandlerFunc(LogoutHandler))
r.Handle("/services", http.HandlerFunc(ServicesHandler)).Methods("GET") r.Handle("/services", http.HandlerFunc(ServicesHandler)).Methods("GET")

View File

@ -59,6 +59,7 @@ func ReorderServiceHandler(w http.ResponseWriter, r *http.Request) {
decoder := json.NewDecoder(r.Body) decoder := json.NewDecoder(r.Body)
decoder.Decode(&newOrder) decoder.Decode(&newOrder)
for _, s := range newOrder { for _, s := range newOrder {
fmt.Println("updating: ", s.Id, " to be order_id: ", s.Order)
service := core.SelectService(s.Id) service := core.SelectService(s.Id)
service.Order = s.Order service.Order = s.Order
service.Update(false) service.Update(false)

View File

@ -16,7 +16,6 @@
package handlers package handlers
import ( import (
"fmt"
"github.com/hunterlong/statup/core" "github.com/hunterlong/statup/core"
"github.com/hunterlong/statup/types" "github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils" "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) { func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
var err error
if core.CoreApp.Services() != nil { if core.CoreApp.Services() != nil {
http.Redirect(w, r, "/", http.StatusSeeOther) http.Redirect(w, r, "/", http.StatusSeeOther)
return return
@ -75,7 +75,9 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
domain := r.PostForm.Get("domain") domain := r.PostForm.Get("domain")
email := r.PostForm.Get("email") email := r.PostForm.Get("email")
config := &core.DbConfig{&types.DbConfig{ dir := utils.Directory
config := &core.DbConfig{DbConfig: &types.DbConfig{
DbConn: dbConn, DbConn: dbConn,
DbHost: dbHost, DbHost: dbHost,
DbUser: dbUser, DbUser: dbUser,
@ -92,13 +94,15 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
Location: utils.Directory, Location: utils.Directory,
}} }}
fmt.Println(config) core.Configs, err = config.Save()
err := config.Save()
if err != nil { if err != nil {
utils.Log(4, err) utils.Log(4, err)
config.Error = err
SetupResponseError(w, r, config)
return
} }
core.Configs, err = core.LoadConfig(dir)
if err != nil { if err != nil {
utils.Log(3, err) utils.Log(3, err)
config.Error = err config.Error = err
@ -106,23 +110,26 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
core.Configs, err = core.LoadConfig() err = core.Configs.Connect(false, dir)
if err != nil { if err != nil {
utils.Log(3, err) utils.Log(4, 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)
core.DeleteConfig() core.DeleteConfig()
config.Error = err config.Error = err
SetupResponseError(w, r, config) SetupResponseError(w, r, config)
return 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{ admin := core.ReturnUser(&types.User{
Username: config.Username, Username: config.Username,
Password: config.Password, Password: config.Password,

View File

@ -112,7 +112,7 @@ func (u *Email) Init() error {
if u.Enabled { if u.Enabled {
utils.Log(1, fmt.Sprintf("Loading SMTP Emailer using host: %v:%v", u.Notification.Host, u.Notification.Port)) 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} mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
go u.Run() go u.Run()
@ -215,7 +215,7 @@ func (u *Email) OnSave() error {
// ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS // ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS
func (u *Email) Install() error { func (u *Email) Install() error {
inDb, err := emailer.Notification.IsInDatabase() inDb := emailer.Notification.IsInDatabase()
if !inDb { if !inDb {
newNotifer, err := InsertDatabase(u.Notification) newNotifer, err := InsertDatabase(u.Notification)
if err != nil { 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)) utils.Log(1, fmt.Sprintf("new notifier #%v installed: %v", newNotifer, u.Method))
} }
return err return nil
} }
func (u *Email) dialSend(email *EmailOutgoing) error { func (u *Email) dialSend(email *EmailOutgoing) error {

View File

@ -19,34 +19,35 @@ import (
"fmt" "fmt"
"github.com/hunterlong/statup/types" "github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils" "github.com/hunterlong/statup/utils"
"github.com/jinzhu/gorm"
"strings" "strings"
"time" "time"
"upper.io/db.v3"
) )
var ( var (
AllCommunications []types.AllNotifiers AllCommunications []types.AllNotifiers
Collections db.Collection Collections *gorm.DB
Logs []*NotificationLog Logs []*NotificationLog
) )
type Notification struct { type Notification struct {
Id int64 `db:"id,omitempty" json:"id"` Id int64 `gorm:"primary_key column:id" json:"id"`
Method string `db:"method" json:"method"` Method string `gorm:"column:method" json:"method"`
Host string `db:"host" json:"-"` Host string `gorm:"not null;column:host" json:"-"`
Port int `db:"port" json:"-"` Port int `gorm:"not null;column:port" json:"-"`
Username string `db:"username" json:"-"` Username string `gorm:"not null;column:username" json:"-"`
Password string `db:"password" json:"-"` Password string `gorm:"not null;column:password" json:"-"`
Var1 string `db:"var1" json:"-"` Var1 string `gorm:"not null;column:var1" json:"-"`
Var2 string `db:"var2" json:"-"` Var2 string `gorm:"not null;column:var2" json:"-"`
ApiKey string `db:"api_key" json:"-"` ApiKey string `gorm:"not null;column:api_key" json:"-"`
ApiSecret string `db:"api_secret" json:"-"` ApiSecret string `gorm:"not null;column:api_secret" json:"-"`
Enabled bool `db:"enabled" json:"enabled"` Enabled bool `gorm:"column:enabled;type:boolean;default:false" json:"enabled"`
Limits int `db:"limits" json:"-"` Limits int `gorm:"not null;column:limits" json:"-"`
Removable bool `db:"removable" json:"-"` Removable bool `gorm:"column:removable" json:"-"`
CreatedAt time.Time `db:"created_at" json:"created_at"` CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
Form []NotificationForm UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
Routine chan struct{} Form []NotificationForm `gorm:"-" json:"-"`
Routine chan struct{} `gorm:"-" json:"-"`
} }
type Notifier interface { type Notifier interface {
@ -115,30 +116,30 @@ func reverseLogs(input []*NotificationLog) []*NotificationLog {
return append(reverseLogs(input[1:]), input[0]) return append(reverseLogs(input[1:]), input[0])
} }
func (n *Notification) IsInDatabase() (bool, error) { func (n *Notification) IsInDatabase() bool {
return Collections.Find("id", n.Id).Exists() return !Collections.Find(n).RecordNotFound()
} }
func SelectNotification(id int64) (*Notification, error) { func SelectNotification(id int64) (*Notification, error) {
var notifier *Notification var notifier Notification
err := Collections.Find("id", id).One(&notifier) err := Collections.Find(&notifier, id)
return notifier, err return &notifier, err.Error
} }
func (n *Notification) Update() (*Notification, error) { func (n *Notification) Update() (*Notification, error) {
n.CreatedAt = time.Now() n.CreatedAt = time.Now()
err := Collections.Find("id", n.Id).Update(n) err := Collections.Update(n)
return n, err return n, err.Error
} }
func InsertDatabase(n *Notification) (int64, error) { func InsertDatabase(n *Notification) (int64, error) {
n.CreatedAt = time.Now() n.CreatedAt = time.Now()
n.Limits = 3 n.Limits = 3
newId, err := Collections.Insert(n) db := Collections.Create(n)
if err != nil { if db.Error != nil {
return 0, err return 0, db.Error
} }
return newId.(int64), err return n.Id, db.Error
} }
func SelectNotifier(id int64) Notifier { func SelectNotifier(id int64) Notifier {

View File

@ -18,10 +18,11 @@ package notifiers
import ( import (
"github.com/hunterlong/statup/types" "github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils" "github.com/hunterlong/statup/utils"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"testing" "testing"
"upper.io/db.v3/sqlite"
) )
var ( var (
@ -67,11 +68,8 @@ func init() {
} }
func injectDatabase() { func injectDatabase() {
sqliteDb := sqlite.ConnectionURL{ dbSession, _ := gorm.Open("sqlite3", dir+"/statup.db")
Database: dir + "/statup.db", Collections = dbSession.Table("communication").Model(&Notification{})
}
dbSession, _ := sqlite.Open(sqliteDb)
Collections = dbSession.Collection("communication")
} }
type Tester struct { type Tester struct {
@ -100,8 +98,7 @@ func TestAdd(t *testing.T) {
} }
func TestIsInDatabase(t *testing.T) { func TestIsInDatabase(t *testing.T) {
in, err := testNotifier.IsInDatabase() in := testNotifier.IsInDatabase()
assert.Nil(t, err)
assert.False(t, in) assert.False(t, in)
} }
@ -110,8 +107,7 @@ func TestInsertDatabase(t *testing.T) {
assert.Nil(t, err) assert.Nil(t, err)
assert.NotZero(t, newId) assert.NotZero(t, newId)
in, err := testNotifier.IsInDatabase() in := testNotifier.IsInDatabase()
assert.Nil(t, err)
assert.True(t, in) assert.True(t, in)
} }

View File

@ -165,7 +165,7 @@ func (u *Slack) OnSave() error {
// ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS // ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS
func (u *Slack) Install() error { func (u *Slack) Install() error {
inDb, err := slacker.Notification.IsInDatabase() inDb := slacker.Notification.IsInDatabase()
if !inDb { if !inDb {
newNotifer, err := InsertDatabase(u.Notification) newNotifer, err := InsertDatabase(u.Notification)
if err != nil { 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)) utils.Log(1, fmt.Sprintf("new notifier #%v installed: %v", newNotifer, u.Method))
} }
return err return nil
} }

View File

@ -179,7 +179,7 @@ func (u *Twilio) OnSave() error {
// ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS // ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS
func (u *Twilio) Install() error { func (u *Twilio) Install() error {
inDb, err := twilio.Notification.IsInDatabase() inDb := twilio.Notification.IsInDatabase()
if !inDb { if !inDb {
newNotifer, err := InsertDatabase(u.Notification) newNotifer, err := InsertDatabase(u.Notification)
if err != nil { 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)) utils.Log(1, fmt.Sprintf("new notifier #%v installed: %v", newNotifer, u.Method))
} }
return err return nil
} }

View File

@ -27,7 +27,6 @@ import (
) )
var ( var (
SqlBox *rice.Box
CssBox *rice.Box CssBox *rice.Box
ScssBox *rice.Box ScssBox *rice.Box
JsBox *rice.Box JsBox *rice.Box
@ -35,7 +34,6 @@ var (
) )
func Assets() { func Assets() {
SqlBox = rice.MustFindBox("sql")
CssBox = rice.MustFindBox("css") CssBox = rice.MustFindBox("css")
ScssBox = rice.MustFindBox("scss") ScssBox = rice.MustFindBox("scss")
JsBox = rice.MustFindBox("js") JsBox = rice.MustFindBox("js")

View File

@ -1,7 +0,0 @@
DROP TABLE IF EXISTS core;
DROP TABLE IF EXISTS hits;
DROP TABLE IF EXISTS failures;
DROP TABLE IF EXISTS users;
DROP TABLE IF EXISTS checkins;
DROP TABLE IF EXISTS services;
DROP TABLE IF EXISTS communication;

View File

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

View File

@ -1,9 +0,0 @@
=========================================== 1534178020
UPDATE services SET order_id=0 WHERE order_id IS NULL;
=========================================== 1532068515
ALTER TABLE services ALTER COLUMN order_id integer DEFAULT 0;
ALTER TABLE services ADD COLUMN timeout integer DEFAULT 30;
=========================================== 1530841150
ALTER TABLE core ADD COLUMN use_cdn BOOL NOT NULL DEFAULT '0';
=========================================== 1
ALTER TABLE core ADD COLUMN migration_id INT(6) NOT NULL DEFAULT 0;

View File

@ -1,84 +0,0 @@
CREATE TABLE core (
name text,
description text,
config text,
api_key text,
api_secret text,
style text,
footer text,
domain text,
version text,
migration_id integer default 0,
use_cdn bool default false
);
CREATE TABLE users (
id SERIAL PRIMARY KEY,
username VARCHAR (50) UNIQUE,
password text,
email VARCHAR (50) UNIQUE,
api_key text,
api_secret text,
administrator bool,
created_at TIMESTAMP
);
CREATE TABLE services (
id SERIAL PRIMARY KEY,
name text,
domain text,
check_type text,
method text,
port integer,
expected text,
expected_status integer,
check_interval integer,
post_data text,
order_id integer default 0,
timeout integer default 30,
created_at TIMESTAMP
);
CREATE TABLE hits (
id SERIAL PRIMARY KEY,
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
latency float,
created_at TIMESTAMP
);
CREATE TABLE failures (
id SERIAL PRIMARY KEY,
issue text,
method text,
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
created_at TIMESTAMP
);
CREATE TABLE checkins (
id SERIAL PRIMARY KEY,
service INTEGER NOT NULL REFERENCES services(id) ON DELETE CASCADE ON UPDATE CASCADE,
check_interval integer,
api text,
created_at TIMESTAMP
);
CREATE TABLE communication (
id SERIAL PRIMARY KEY,
method text,
host text,
port integer,
username text,
password text,
var1 text,
var2 text,
api_key text,
api_secret text,
enabled boolean,
removable boolean,
limits integer,
created_at TIMESTAMP
);
CREATE INDEX idx_hits ON hits(service);
CREATE INDEX idx_failures ON failures(service);
CREATE INDEX idx_checkins ON checkins(service);

View File

@ -1,9 +0,0 @@
=========================================== 1534178020
UPDATE services SET order_id=0 WHERE order_id IS NULL;
=========================================== 1532068515
ALTER TABLE services ALTER COLUMN order_id integer DEFAULT 0;
ALTER TABLE services ADD COLUMN timeout integer DEFAULT 30;
=========================================== 1530841150
ALTER TABLE core ADD COLUMN use_cdn bool DEFAULT FALSE;
=========================================== 1
ALTER TABLE core ADD COLUMN migration_id integer default 0 NOT NULL;

View File

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

View File

@ -1,9 +0,0 @@
=========================================== 1534178020
UPDATE services SET order_id=0 WHERE order_id IS NULL;
=========================================== 1532068515
ALTER TABLE services ALTER COLUMN order_id integer DEFAULT 0;
ALTER TABLE services ADD COLUMN timeout integer DEFAULT 30;
=========================================== 1530841150
ALTER TABLE core ADD COLUMN use_cdn bool DEFAULT FALSE;
=========================================== 1
ALTER TABLE core ADD COLUMN migration_id integer NOT NULL DEFAULT 0;

View File

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

View File

@ -73,14 +73,14 @@
Average Response Average Response
</div> </div>
<div class="col-4"> <div class="col-4">
<span class="lg_number">{{.AvgUptime}}%</span> <span class="lg_number">{{.AvgUptime24}}%</span>
Total Uptime Uptime last 24 Hours
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{{ if .AvgTime }} {{ if .AvgUptime24 }}
<div class="chart-container"> <div class="chart-container">
<canvas id="service_{{ .Id }}"></canvas> <canvas id="service_{{ .Id }}"></canvas>
</div> </div>

View File

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

View File

@ -1,13 +1,16 @@
package types package types
import "time" import (
"time"
)
type Checkin struct { type Checkin struct {
Id int `db:"id,omitempty"` Id int64 `gorm:"primary_key;column:id"`
Service int64 `db:"service"` Service int64 `gorm:"index;column:service"`
Interval int64 `db:"check_interval"` Interval int64 `gorm:"column:check_interval"`
Api string `db:"api"` Api string `gorm:"column:api"`
CreatedAt time.Time `db:"created_at"` CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
Hits int64 `json:"hits"` Hits int64 `json:"hits"`
Last time.Time `json:"last"` Last time.Time `json:"last"`
CheckinInterface `json:"-"` CheckinInterface `json:"-"`

View File

@ -1,30 +1,45 @@
package types 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 { type Core struct {
Name string `db:"name" json:"name"` Name string `gorm:"not null;column:name" json:"name"`
Description string `db:"description" json:"description,omitempty"` Description string `gorm:"not null;column:description" json:"description,omitempty"`
Config string `db:"config" json:"-"` Config string `gorm:"column:config" json:"-"`
ApiKey string `db:"api_key" json:"-"` ApiKey string `gorm:"column:api_key" json:"-"`
ApiSecret string `db:"api_secret" json:"-"` ApiSecret string `gorm:"column:api_secret" json:"-"`
Style string `db:"style" json:"style,omitempty"` Style string `gorm:"not null;column:style" json:"style,omitempty"`
Footer string `db:"footer" json:"footer,omitempty"` Footer string `gorm:"not null;column:footer" json:"footer,omitempty"`
Domain string `db:"domain" json:"domain,omitempty"` Domain string `gorm:"not null;column:domain" json:"domain,omitempty"`
Version string `db:"version" json:"version"` Version string `gorm:"column:version" json:"version"`
MigrationId int64 `db:"migration_id" json:"migration_id,omitempty"` MigrationId int64 `gorm:"column:migration_id" json:"migration_id,omitempty"`
UseCdn bool `db:"use_cdn" json:"using_cdn,omitempty"` UseCdn bool `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"`
DbConnection string `json:"database"` CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
Started time.Time `json:"started_on"` UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
dbServices []*Service `json:"services,omitempty"` DbConnection string `gorm:"-" json:"database"`
Plugins []Info `json:"-"` Started time.Time `gorm:"-" json:"started_on"`
Repos []PluginJSON `json:"-"` dbServices []*Service `gorm:"-" json:"services,omitempty"`
AllPlugins []PluginActions `json:"-"` Plugins []Info `gorm:"-" json:"-"`
Communications []AllNotifiers `json:"-"` Repos []PluginJSON `gorm:"-" json:"-"`
CoreInterface `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) { func (c *Core) SetServices(s []*Service) {
sort.Sort(ServiceOrder(c.dbServices))
c.dbServices = s c.dbServices = s
} }

View File

@ -1,14 +1,16 @@
package types package types
import "time" import (
"time"
)
type Failure struct { type Failure struct {
Id int `db:"id,omitempty" json:"id"` Id int64 `gorm:"primary_key;column:id" json:"id"`
Issue string `db:"issue" json:"issue"` Issue string `gorm:"column:issue" json:"issue"`
Method string `db:"method" json:"method,omitempty"` Method string `gorm:"column:method" json:"method,omitempty"`
Service int64 `db:"service" json:"service_id"` Service int64 `gorm:"index;column:service" json:"service_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"` CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
FailureInterface `json:"-"` FailureInterface `gorm:"-" json:"-"`
} }
type FailureInterface interface { type FailureInterface interface {

View File

@ -20,33 +20,33 @@ import (
) )
type Service struct { type Service struct {
Id int64 `db:"id,omitempty" json:"id"` Id int64 `gorm:"primary_key;column:id" json:"id"`
Name string `db:"name" json:"name"` Name string `gorm:"column:name" json:"name"`
Domain string `db:"domain" json:"domain"` Domain string `gorm:"column:domain" json:"domain"`
Expected string `db:"expected" json:"expected"` Expected string `gorm:"not null;column:expected" json:"expected"`
ExpectedStatus int `db:"expected_status" json:"expected_status"` ExpectedStatus int `gorm:"default:200;column:expected_status" json:"expected_status"`
Interval int `db:"check_interval" json:"check_interval"` Interval int `gorm:"default:30;column:check_interval" json:"check_interval"`
Type string `db:"check_type" json:"type"` Type string `gorm:"column:check_type" json:"type"`
Method string `db:"method" json:"method"` Method string `gorm:"column:method" json:"method"`
PostData string `db:"post_data" json:"post_data"` PostData string `gorm:"not null;column:post_data" json:"post_data"`
Port int `db:"port" json:"port"` Port int `gorm:"not null;column:port" json:"port"`
CreatedAt time.Time `db:"created_at" json:"created_at"` Timeout int `gorm:"default:30;column:timeout" json:"timeout"`
Timeout int `db:"timeout" json:"timeout"` Order int `gorm:"default:0;column:order_id" json:"order_id"`
Order int `db:"order_id" json:"order_id"` CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
Online bool `json:"online"` UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
Latency float64 `json:"latency"` Online bool `gorm:"-" json:"online"`
Online24Hours float32 `json:"24_hours_online"` Latency float64 `gorm:"-" json:"latency"`
AvgResponse string `json:"avg_response"` Online24Hours float32 `gorm:"-" json:"24_hours_online"`
TotalUptime string `json:"uptime"` AvgResponse string `gorm:"-" json:"avg_response"`
Failures []*Failure `json:"failures"` Failures []*Failure `gorm:"-" json:"failures"`
Checkins []*Checkin `json:"checkins"` Checkins []*Checkin `gorm:"-" json:"checkins"`
Running chan bool `json:"-"` Running chan bool `gorm:"-" json:"-"`
Checkpoint time.Time `json:"-"` Checkpoint time.Time `gorm:"-" json:"-"`
LastResponse string `json:"-"` LastResponse string `gorm:"-" json:"-"`
LastStatusCode int `json:"status_code"` LastStatusCode int `gorm:"-" json:"status_code"`
LastOnline time.Time `json:"last_online"` LastOnline time.Time `gorm:"-" json:"last_online"`
DnsLookup float64 `json:"dns_lookup_time"` DnsLookup float64 `gorm:"-" json:"dns_lookup_time"`
ServiceInterface `json:"-"` ServiceInterface `gorm:"-" json:"-"`
} }
type ServiceInterface interface { type ServiceInterface interface {
@ -56,6 +56,7 @@ type ServiceInterface interface {
Delete() error Delete() error
// Basic Method functions // Basic Method functions
AvgTime() float64 AvgTime() float64
OnlineSince(time.Time) float32
Online24() float32 Online24() float32
SmallText() string SmallText() string
GraphData() string GraphData() string
@ -65,13 +66,15 @@ type ServiceInterface interface {
CreateFailure(*Failure) (int64, error) CreateFailure(*Failure) (int64, error)
LimitedFailures() []*Failure LimitedFailures() []*Failure
AllFailures() []*Failure AllFailures() []*Failure
TotalFailuresSince(time.Time) (uint64, error)
TotalFailures24() (uint64, error)
TotalFailures() (uint64, error) TotalFailures() (uint64, error)
TotalFailures24Hours() (uint64, error)
DeleteFailures() DeleteFailures()
// Hits functions (successful responses) // Hits functions (successful responses)
CreateHit(*Hit) (int64, error) CreateHit(*Hit) (int64, error)
Hits() ([]*Hit, error) Hits() ([]*Hit, error)
TotalHits() (uint64, error) TotalHits() (uint64, error)
TotalHitsSince(time.Time) (uint64, error)
Sum() (float64, error) Sum() (float64, error)
LimitedHits() ([]*Hit, error) LimitedHits() ([]*Hit, error)
SelectHitsGroupBy(string) ([]*Hit, error) SelectHitsGroupBy(string) ([]*Hit, error)

View File

@ -16,9 +16,9 @@
package types package types
import ( import (
"github.com/jinzhu/gorm"
"net/http" "net/http"
"time" "time"
"upper.io/db.v3/lib/sqlbuilder"
) )
type PluginInfo struct { type PluginInfo struct {
@ -41,7 +41,7 @@ type Info struct {
type PluginActions interface { type PluginActions interface {
GetInfo() Info GetInfo() Info
GetForm() string GetForm() string
OnLoad(sqlbuilder.Database) OnLoad(db gorm.DB)
SetInfo(map[string]interface{}) Info SetInfo(map[string]interface{}) Info
Routes() []Routing Routes() []Routing
OnSave(map[string]interface{}) OnSave(map[string]interface{})
@ -61,23 +61,15 @@ type PluginActions interface {
type AllNotifiers interface{} type AllNotifiers interface{}
// Hit struct is a 'successful' ping or web response entry for a service.
type Hit struct { type Hit struct {
Id int `db:"id,omitempty"` Id int64 `gorm:"primary_key;column:id"`
Service int64 `db:"service"` Service int64 `gorm:"index;column:service"`
Latency float64 `db:"latency"` Latency float64 `gorm:"column:latency"`
CreatedAt time.Time `db:"created_at"` CreatedAt time.Time `gorm:"column: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"`
} }
// DbConfig struct is used for the database connection and creates the 'config.yml' file
type DbConfig struct { type DbConfig struct {
DbConn string `yaml:"connection"` DbConn string `yaml:"connection"`
DbHost string `yaml:"host"` DbHost string `yaml:"host"`

View File

@ -1,17 +1,20 @@
package types package types
import "time" import (
"time"
)
type User struct { type User struct {
Id int64 `db:"id,omitempty" json:"id"` Id int64 `gorm:"primary_key;column:id" json:"id"`
Username string `db:"username" json:"username"` Username string `gorm:"type:varchar(100);unique;column:username;" json:"username"`
Password string `db:"password" json:"-"` Password string `gorm:"column:password" json:"-"`
Email string `db:"email" json:"-"` Email string `gorm:"type:varchar(100);unique;column:email" json:"-"`
ApiKey string `db:"api_key" json:"api_key"` ApiKey string `gorm:"column:api_key" json:"api_key"`
ApiSecret string `db:"api_secret" json:"-"` ApiSecret string `gorm:"column:api_secret" json:"-"`
Admin bool `db:"administrator" json:"admin"` Admin bool `gorm:"column:administrator" json:"admin"`
CreatedAt time.Time `db:"created_at" json:"created_at"` CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UserInterface `json:"-"` UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
UserInterface `gorm:"-" json:"-"`
} }
type UserInterface interface { type UserInterface interface {

View File

@ -34,6 +34,10 @@ var (
LockLines sync.Mutex LockLines sync.Mutex
) )
func Logger() *lumberjack.Logger {
return ljLogger
}
func createLog(dir string) error { func createLog(dir string) error {
var err error var err error
_, err = os.Stat(dir + "/logs") _, err = os.Stat(dir + "/logs")

View File

@ -16,8 +16,11 @@
package utils package utils
import ( import (
"errors"
"github.com/ararog/timeago" "github.com/ararog/timeago"
"io"
"os" "os"
"os/exec"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -97,6 +100,7 @@ func FileExists(name string) bool {
} }
func DeleteFile(file string) error { func DeleteFile(file string) error {
Log(1, "deleting file: "+file)
err := os.Remove(file) err := os.Remove(file)
if err != nil { if err != nil {
return err return err
@ -107,3 +111,56 @@ func DeleteFile(file string) error {
func DeleteDirectory(directory string) error { func DeleteDirectory(directory string) error {
return os.RemoveAll(directory) 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
}
}
}