mirror of https://github.com/statping/statping
core updates - sql migrations
parent
b58326a453
commit
cb46bf6496
|
@ -18,7 +18,7 @@ services:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- VERSION=0.29
|
- VERSION=0.29.1
|
||||||
- DB_HOST=localhost
|
- DB_HOST=localhost
|
||||||
- DB_USER=travis
|
- DB_USER=travis
|
||||||
- DB_PASS=
|
- DB_PASS=
|
||||||
|
|
|
@ -5,7 +5,7 @@ gem install sass
|
||||||
sass source/scss/base.scss source/css/base.css
|
sass source/scss/base.scss source/css/base.css
|
||||||
|
|
||||||
# MIGRATION SQL FILE FOR CURRENT VERSION
|
# MIGRATION SQL FILE FOR CURRENT VERSION
|
||||||
printf "UPDATE core SET version='$VERSION';\n" >> source/sql/upgrade.sql
|
#printf "UPDATE core SET version='$VERSION';\n" >> source/sql/upgrade.sql
|
||||||
|
|
||||||
# COMPILE SRC INTO BIN
|
# COMPILE SRC INTO BIN
|
||||||
rice embed-go
|
rice embed-go
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
|
||||||
ENV VERSION=v0.29
|
ENV VERSION=v0.29.1
|
||||||
|
|
||||||
RUN apk --no-cache add libstdc++ ca-certificates
|
RUN apk --no-cache add libstdc++ ca-certificates
|
||||||
RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \
|
RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \
|
||||||
|
|
|
@ -13,12 +13,13 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func LoadConfig() (*Config, error) {
|
func LoadConfig() (*Config, error) {
|
||||||
var config Config
|
var config *Config
|
||||||
file, err := ioutil.ReadFile("config.yml")
|
file, err := ioutil.ReadFile("config.yml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
err = yaml.Unmarshal(file, &config)
|
err = yaml.Unmarshal(file, &config)
|
||||||
Configs = &config
|
Configs = config
|
||||||
return &config, err
|
CoreApp.DbConnection = config.Connection
|
||||||
|
return config, err
|
||||||
}
|
}
|
||||||
|
|
18
core/core.go
18
core/core.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"github.com/GeertJohan/go.rice"
|
"github.com/GeertJohan/go.rice"
|
||||||
"github.com/hunterlong/statup/plugin"
|
"github.com/hunterlong/statup/plugin"
|
||||||
"github.com/hunterlong/statup/types"
|
"github.com/hunterlong/statup/types"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PluginJSON types.PluginJSON
|
type PluginJSON types.PluginJSON
|
||||||
|
@ -25,6 +26,8 @@ type Core struct {
|
||||||
AllPlugins []plugin.PluginActions
|
AllPlugins []plugin.PluginActions
|
||||||
Communications []*types.Communication
|
Communications []*types.Communication
|
||||||
OfflineAssets bool
|
OfflineAssets bool
|
||||||
|
DbConnection string
|
||||||
|
started time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -41,7 +44,13 @@ var (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
CoreApp = NewCore()
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewCore() *Core {
|
||||||
CoreApp = new(Core)
|
CoreApp = new(Core)
|
||||||
|
CoreApp.started = time.Now()
|
||||||
|
return CoreApp
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitApp() {
|
func InitApp() {
|
||||||
|
@ -56,9 +65,10 @@ func InitApp() {
|
||||||
|
|
||||||
func (c *Core) Update() (*Core, error) {
|
func (c *Core) Update() (*Core, error) {
|
||||||
res := DbSession.Collection("core").Find().Limit(1)
|
res := DbSession.Collection("core").Find().Limit(1)
|
||||||
res.Update(c)
|
err := res.Update(c)
|
||||||
CoreApp = c
|
CoreApp = c
|
||||||
return c, nil
|
CoreApp.Services, err = SelectAllServices()
|
||||||
|
return c, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Core) UsingAssets() bool {
|
func (c Core) UsingAssets() bool {
|
||||||
|
@ -102,6 +112,10 @@ func SelectCore() (*Core, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
CoreApp = c
|
CoreApp = c
|
||||||
|
CoreApp.DbConnection = Configs.Connection
|
||||||
|
CoreApp.Version = VERSION
|
||||||
|
CoreApp.Services, _ = SelectAllServices()
|
||||||
|
CoreApp.Update()
|
||||||
//store = sessions.NewCookieStore([]byte(core.ApiSecret))
|
//store = sessions.NewCookieStore([]byte(core.ApiSecret))
|
||||||
return CoreApp, err
|
return CoreApp, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,6 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dbServer string
|
|
||||||
sqliteSettings sqlite.ConnectionURL
|
sqliteSettings sqlite.ConnectionURL
|
||||||
postgresSettings postgresql.ConnectionURL
|
postgresSettings postgresql.ConnectionURL
|
||||||
mysqlSettings mysql.ConnectionURL
|
mysqlSettings mysql.ConnectionURL
|
||||||
|
@ -68,7 +67,6 @@ func DbConnection(dbType string) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//dbSession.SetLogging(true)
|
//dbSession.SetLogging(true)
|
||||||
dbServer = dbType
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,20 +128,87 @@ func (c *DbConfig) Save() error {
|
||||||
if err == nil {
|
if err == nil {
|
||||||
CoreApp = newCore
|
CoreApp = newCore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CoreApp, err = SelectCore()
|
||||||
|
CoreApp.DbConnection = c.DbConn
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunDatabaseUpgrades() {
|
func versionSplit(v string) (int64, int64, int64) {
|
||||||
utils.Log(1, "Running Database Upgrade from 'upgrade.sql'...")
|
currSplit := strings.Split(v, ".")
|
||||||
upgrade, _ := SqlBox.String("upgrade.sql")
|
if len(currSplit) < 2 {
|
||||||
requests := strings.Split(upgrade, ";")
|
return 9999, 9999, 9999
|
||||||
for _, request := range requests {
|
}
|
||||||
_, err := DbSession.Exec(db.Raw(request + ";"))
|
var major, mid, minor string
|
||||||
|
if len(currSplit) == 3 {
|
||||||
|
major = currSplit[0]
|
||||||
|
mid = currSplit[1]
|
||||||
|
minor = currSplit[2]
|
||||||
|
return utils.StringInt(major), utils.StringInt(mid), utils.StringInt(minor)
|
||||||
|
}
|
||||||
|
major = currSplit[0]
|
||||||
|
mid = currSplit[1]
|
||||||
|
return utils.StringInt(major), utils.StringInt(mid), 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func versionHigher(migrate string) bool {
|
||||||
|
cM, cMi, cMn := versionSplit(CoreApp.Version)
|
||||||
|
mM, mMi, mMn := versionSplit(migrate)
|
||||||
|
if mM > cM {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if mMi > cMi {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if mMn > cMn {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunDatabaseUpgrades() error {
|
||||||
|
var err error
|
||||||
|
utils.Log(1, fmt.Sprintf("Checking Database Upgrades from v%v in '%v_upgrade.sql'...", CoreApp.Version, CoreApp.DbConnection))
|
||||||
|
upgrade, _ := SqlBox.String(CoreApp.DbConnection + "_upgrade.sql")
|
||||||
|
// parse db version and upgrade file
|
||||||
|
ups := strings.Split(upgrade, "=========================================== ")
|
||||||
|
var ran int
|
||||||
|
for _, v := range ups {
|
||||||
|
if len(v) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
vers := strings.Split(v, "\n")
|
||||||
|
version := vers[0]
|
||||||
|
data := vers[1:]
|
||||||
|
//fmt.Printf("Checking Migration from v%v to v%v - %v\n", CoreApp.Version, version, versionHigher(version))
|
||||||
|
if !versionHigher(version) {
|
||||||
|
//fmt.Printf("Already up-to-date with v%v\n", version)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf("Migration Database from v%v to v%v\n", CoreApp.Version, version)
|
||||||
|
for _, m := range data {
|
||||||
|
if m == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fmt.Printf("Running Migration: %v\n", m)
|
||||||
|
_, err := DbSession.Exec(db.Raw(m + ";"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(2, err)
|
utils.Log(2, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ran++
|
||||||
|
CoreApp.Version = m
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
utils.Log(1, "Database Upgraded")
|
CoreApp.Update()
|
||||||
|
CoreApp, err = SelectCore()
|
||||||
|
if ran > 0 {
|
||||||
|
utils.Log(1, fmt.Sprintf("Database Upgraded, %v query ran", ran))
|
||||||
|
} else {
|
||||||
|
utils.Log(1, fmt.Sprintf("Database is already up-to-date, latest v%v", CoreApp.Version))
|
||||||
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func DropDatabase() {
|
func DropDatabase() {
|
||||||
|
@ -161,9 +226,9 @@ func DropDatabase() {
|
||||||
func CreateDatabase() {
|
func CreateDatabase() {
|
||||||
fmt.Println("Creating Tables...")
|
fmt.Println("Creating Tables...")
|
||||||
sql := "postgres_up.sql"
|
sql := "postgres_up.sql"
|
||||||
if dbServer == "mysql" {
|
if CoreApp.DbConnection == "mysql" {
|
||||||
sql = "mysql_up.sql"
|
sql = "mysql_up.sql"
|
||||||
} else if dbServer == "sqlite" {
|
} else if CoreApp.DbConnection == "sqlite" {
|
||||||
sql = "sqlite_up.sql"
|
sql = "sqlite_up.sql"
|
||||||
}
|
}
|
||||||
up, _ := SqlBox.String(sql)
|
up, _ := SqlBox.String(sql)
|
||||||
|
|
|
@ -124,17 +124,27 @@ func (s *Service) SmallText() string {
|
||||||
return fmt.Sprintf("No Failures in the last 24 hours! %v", hits[0])
|
return fmt.Sprintf("No Failures in the last 24 hours! %v", hits[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GroupDataBy(column string, id int64, tm time.Time, increment string) string {
|
||||||
|
var sql string
|
||||||
|
fmt.Println("gropu by", column, CoreApp.DbConnection)
|
||||||
|
switch CoreApp.DbConnection {
|
||||||
|
case "mysql":
|
||||||
|
sql = fmt.Sprintf("SELECT CONCAT(date_format(created_at, '%%Y-%%m-%%dT%%H:%%i:00Z')) AS created_at, AVG(latency)*1000 AS value FROM %v WHERE service=%v AND DATE_FORMAT(created_at, '%%Y-%%m-%%dT%%TZ') BETWEEN DATE_FORMAT('%v', '%%Y-%%m-%%dT%%TZ') AND DATE_FORMAT(NOW(), '%%Y-%%m-%%dT%%TZ') GROUP BY 1 ORDER BY created_at ASC;", column, id, tm.Format(time.RFC3339))
|
||||||
|
case "sqlite":
|
||||||
|
sql = fmt.Sprintf("SELECT strftime('%%Y-%%m-%%dT%%H:%%M:00Z', created_at), AVG(latency)*1000 as value FROM %v WHERE service=%v AND created_at >= '%v' GROUP BY strftime('%%M:00', created_at) ORDER BY created_at ASC;", column, id, tm.Format(time.RFC3339))
|
||||||
|
case "postgres":
|
||||||
|
sql = fmt.Sprintf("SELECT date_trunc('%v', created_at), AVG(latency)*1000 AS value FROM %v WHERE service=%v AND created_at >= '%v' GROUP BY 1 ORDER BY date_trunc ASC;", increment, column, id, tm.Format(time.RFC3339))
|
||||||
|
}
|
||||||
|
fmt.Println(sql)
|
||||||
|
return sql
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) GraphData() string {
|
func (s *Service) GraphData() string {
|
||||||
var d []*DateScan
|
var d []*DateScan
|
||||||
increment := "minute"
|
|
||||||
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)
|
||||||
// group by interval sql query for postgres, mysql and sqlite
|
|
||||||
sql := fmt.Sprintf("SELECT date_trunc('%v', created_at), AVG(latency)*1000 AS value FROM hits WHERE service=%v AND created_at > '%v' GROUP BY 1 ORDER BY date_trunc ASC;", increment, s.Id, since.Format(time.RFC3339))
|
sql := GroupDataBy("hits", s.Id, since, "minute")
|
||||||
if dbServer == "mysql" {
|
|
||||||
sql = fmt.Sprintf("SELECT CONCAT(date_format(created_at, '%%Y-%%m-%%dT%%TZ')) AS created_at, AVG(latency)*1000 AS value FROM hits WHERE service=%v AND DATE_FORMAT(created_at, '%%Y-%%m-%%dT%%TZ') BETWEEN DATE_FORMAT(NOW() - INTERVAL 12 HOUR, '%%Y-%%m-%%dT%%TZ') AND DATE_FORMAT(NOW(), '%%Y-%%m-%%dT%%TZ') GROUP BY created_at ORDER BY created_at ASC;", s.Id)
|
|
||||||
} else if dbServer == "sqlite" {
|
|
||||||
sql = fmt.Sprintf("SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', created_at), AVG(latency)*1000 as value FROM hits WHERE service=%v AND created_at >= '%v' GROUP BY strftime('%%M', created_at) ORDER BY created_at ASC;", s.Id, since.Format(time.RFC3339))
|
|
||||||
}
|
|
||||||
dated, err := DbSession.Query(db.Raw(sql))
|
dated, err := DbSession.Query(db.Raw(sql))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(2, err)
|
utils.Log(2, err)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
"github.com/hunterlong/statup/core"
|
"github.com/hunterlong/statup/core"
|
||||||
"github.com/hunterlong/statup/types"
|
"github.com/hunterlong/statup/types"
|
||||||
|
@ -22,14 +23,14 @@ var (
|
||||||
func RunHTTPServer() {
|
func RunHTTPServer() {
|
||||||
utils.Log(1, "Statup HTTP Server running on http://localhost:8080")
|
utils.Log(1, "Statup HTTP Server running on http://localhost:8080")
|
||||||
r := Router()
|
r := Router()
|
||||||
//for _, p := range allPlugins {
|
for _, p := range core.CoreApp.AllPlugins {
|
||||||
// info := p.GetInfo()
|
info := p.GetInfo()
|
||||||
// for _, route := range p.Routes() {
|
for _, route := range p.Routes() {
|
||||||
// path := fmt.Sprintf("/plugins/%v/%v", info.Name, route.URL)
|
path := fmt.Sprintf("/plugins/%v/%v", info.Name, route.URL)
|
||||||
// r.Handle(path, http.HandlerFunc(route.Handler)).Methods(route.Method)
|
r.Handle(path, http.HandlerFunc(route.Handler)).Methods(route.Method)
|
||||||
// fmt.Printf("Added Route %v for plugin %v\n", path, info.Name)
|
fmt.Printf("Added Route %v for plugin %v\n", path, info.Name)
|
||||||
// }
|
}
|
||||||
//}
|
}
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
Addr: "0.0.0.0:8080",
|
Addr: "0.0.0.0:8080",
|
||||||
WriteTimeout: time.Second * 15,
|
WriteTimeout: time.Second * 15,
|
||||||
|
|
|
@ -1,19 +1,19 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
"github.com/hunterlong/statup/core"
|
"github.com/hunterlong/statup/core"
|
||||||
"github.com/tdewolff/minify"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Router() *mux.Router {
|
func Router() *mux.Router {
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
r.Handle("/", http.HandlerFunc(IndexHandler))
|
r.Handle("/", http.HandlerFunc(IndexHandler))
|
||||||
LocalizedAssets(r)
|
LocalizedAssets(r)
|
||||||
m := minify.New()
|
r.Handle("/charts.js", http.HandlerFunc(RenderServiceChartsHandler))
|
||||||
r.Handle("/charts.js", m.Middleware(http.HandlerFunc(RenderServiceChartsHandler)))
|
|
||||||
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")
|
||||||
|
@ -49,7 +49,12 @@ func Router() *mux.Router {
|
||||||
r.Handle("/api/users/{id}", http.HandlerFunc(ApiUserHandler))
|
r.Handle("/api/users/{id}", http.HandlerFunc(ApiUserHandler))
|
||||||
r.Handle("/metrics", http.HandlerFunc(PrometheusHandler))
|
r.Handle("/metrics", http.HandlerFunc(PrometheusHandler))
|
||||||
r.NotFoundHandler = http.HandlerFunc(Error404Handler)
|
r.NotFoundHandler = http.HandlerFunc(Error404Handler)
|
||||||
|
if core.CoreApp != nil {
|
||||||
|
cookie := fmt.Sprintf("%v_%v", core.CoreApp.ApiSecret, time.Now().Nanosecond())
|
||||||
|
Store = sessions.NewCookieStore([]byte(cookie))
|
||||||
|
} else {
|
||||||
Store = sessions.NewCookieStore([]byte("secretinfo"))
|
Store = sessions.NewCookieStore([]byte("secretinfo"))
|
||||||
|
}
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,8 @@ import (
|
||||||
|
|
||||||
func RenderServiceChartsHandler(w http.ResponseWriter, r *http.Request) {
|
func RenderServiceChartsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
services := core.CoreApp.Services
|
services := core.CoreApp.Services
|
||||||
//w.Header().Set("Content-Type", "text/javascript")
|
w.Header().Set("Content-Type", "text/javascript")
|
||||||
//w.Header().Set("Cache-Control", "no-cache, private, max-age=0")
|
w.Header().Set("Cache-Control", "max-age=60")
|
||||||
ExecuteJSResponse(w, r, "charts.js", services)
|
ExecuteJSResponse(w, r, "charts.js", services)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,6 @@ func CreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Println("service adding")
|
|
||||||
r.ParseForm()
|
r.ParseForm()
|
||||||
name := r.PostForm.Get("name")
|
name := r.PostForm.Get("name")
|
||||||
domain := r.PostForm.Get("domain")
|
domain := r.PostForm.Get("domain")
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/gorilla/sessions"
|
||||||
"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"
|
||||||
|
@ -106,7 +107,7 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
admin := &core.User{
|
admin := &core.User{
|
||||||
Username: config.Username,
|
Username: config.Username,
|
||||||
Password: config.Password,
|
Password: config.Password,
|
||||||
Email: email,
|
Email: config.Email,
|
||||||
Admin: true,
|
Admin: true,
|
||||||
}
|
}
|
||||||
admin.Create()
|
admin.Create()
|
||||||
|
@ -116,6 +117,7 @@ func ProcessSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
core.InitApp()
|
core.InitApp()
|
||||||
|
Store = sessions.NewCookieStore([]byte(core.CoreApp.ApiSecret))
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
}
|
}
|
||||||
|
|
15
main_test.go
15
main_test.go
|
@ -13,7 +13,6 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -28,6 +27,7 @@ func RunInit(t *testing.T) {
|
||||||
os.Remove("./index.html")
|
os.Remove("./index.html")
|
||||||
route = handlers.Router()
|
route = handlers.Router()
|
||||||
LoadDotEnvs()
|
LoadDotEnvs()
|
||||||
|
core.CoreApp = core.NewCore()
|
||||||
}
|
}
|
||||||
|
|
||||||
var forceSequential chan bool = make(chan bool, 1)
|
var forceSequential chan bool = make(chan bool, 1)
|
||||||
|
@ -49,6 +49,9 @@ func TestRunAll(t *testing.T) {
|
||||||
t.Run(dbt+" Sample Data", func(t *testing.T) {
|
t.Run(dbt+" Sample Data", func(t *testing.T) {
|
||||||
RunInsertMysqlSample(t)
|
RunInsertMysqlSample(t)
|
||||||
})
|
})
|
||||||
|
t.Run(dbt+" Load Configs", func(t *testing.T) {
|
||||||
|
RunLoadConfig(t)
|
||||||
|
})
|
||||||
t.Run(dbt+" Select Core", func(t *testing.T) {
|
t.Run(dbt+" Select Core", func(t *testing.T) {
|
||||||
RunSelectCoreMYQL(t, dbt)
|
RunSelectCoreMYQL(t, dbt)
|
||||||
})
|
})
|
||||||
|
@ -213,11 +216,20 @@ func RunInsertMysqlSample(t *testing.T) {
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RunLoadConfig(t *testing.T) {
|
||||||
|
var err error
|
||||||
|
core.Configs, err = core.LoadConfig()
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.NotNil(t, core.Configs)
|
||||||
|
}
|
||||||
|
|
||||||
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()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
t.Log(core.CoreApp)
|
||||||
assert.Equal(t, "Testing "+db, core.CoreApp.Name)
|
assert.Equal(t, "Testing "+db, core.CoreApp.Name)
|
||||||
|
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)
|
||||||
assert.Equal(t, VERSION, core.CoreApp.Version)
|
assert.Equal(t, VERSION, core.CoreApp.Version)
|
||||||
|
@ -363,7 +375,6 @@ func RunCreateService_Hits(t *testing.T) {
|
||||||
service := s.Check()
|
service := s.Check()
|
||||||
assert.NotNil(t, service)
|
assert.NotNil(t, service)
|
||||||
}
|
}
|
||||||
time.Sleep(1 * time.Second)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"net/http"
|
"net/http"
|
||||||
"upper.io/db.v3/lib/sqlbuilder"
|
"upper.io/db.v3/lib/sqlbuilder"
|
||||||
)
|
)
|
||||||
|
@ -26,10 +25,6 @@ func SetDatabase(database sqlbuilder.Database) {
|
||||||
DB = database
|
DB = database
|
||||||
}
|
}
|
||||||
|
|
||||||
func Throw(err error) {
|
|
||||||
fmt.Println(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
type PluginInfo struct {
|
type PluginInfo struct {
|
||||||
Info Info
|
Info Info
|
||||||
PluginActions
|
PluginActions
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
[{
|
[{
|
||||||
"name": "slack",
|
"name": "Example Plugin",
|
||||||
"description": "slack bot that send a message in a channel when server is down.",
|
"description": "An example of a plugin for Statup",
|
||||||
"repo": "https://github.com/hunterlong/statup_slack",
|
"repo": "https://github.com/hunterlong/statup_plugin",
|
||||||
"author": "Hunter Long",
|
"author": "Hunter Long",
|
||||||
"namespace": "slack"
|
"namespace": "example"
|
||||||
}]
|
}]
|
|
@ -1,146 +1,6 @@
|
||||||
{{ range . }}{{ if .AvgTime }}var ctx = document.getElementById("service_{{.Id}}").getContext('2d');
|
{{ range . }}{{ if .AvgTime }}var ctx_{{.Id}}=document.getElementById("service_{{.Id}}").getContext('2d');var chartdata=new Chart(ctx_{{.Id}},{type:'line',data:{datasets:[{label:'Response Time (Milliseconds)',data:{{safe .GraphData}},backgroundColor:['rgba(47, 206, 30, 0.92)'],borderColor:['rgb(47, 171, 34)'],borderWidth:1}]},options:{maintainAspectRatio:!1,scaleShowValues:!0,layout:{padding:{left:0,right:0,top:0,bottom:-10}},hover:{animationDuration:0,},responsiveAnimationDuration:0,animation:{duration:3500,onComplete:function(){var chartInstance=this.chart,ctx=chartInstance.ctx;var controller=this.chart.controller;var xAxis=controller.scales['x-axis-0'];var yAxis=controller.scales['y-axis-0'];ctx.font=Chart.helpers.fontString(Chart.defaults.global.defaultFontSize,Chart.defaults.global.defaultFontStyle,Chart.defaults.global.defaultFontFamily);ctx.textAlign='center';ctx.textBaseline='bottom';var numTicks=xAxis.ticks.length;var yOffsetStart=xAxis.width/numTicks;var halfBarWidth=(xAxis.width/(numTicks*2));xAxis.ticks.forEach(function(value,index){var xOffset=20;var yOffset=(yOffsetStart*index)+halfBarWidth;ctx.fillStyle='#e2e2e2';ctx.fillText(value,yOffset,xOffset)});this.data.datasets.forEach(function(dataset,i){var meta=chartInstance.controller.getDatasetMeta(i);var hxH=0;var hyH=0;var hxL=0;var hyL=0;var highestNum=0;var lowestnum=999999999999;meta.data.forEach(function(bar,index){var data=dataset.data[index];if(lowestnum>data.y){lowestnum=data.y;hxL=bar._model.x;hyL=bar._model.y}
|
||||||
|
if(data.y>highestNum){highestNum=data.y;hxH=bar._model.x;hyH=bar._model.y}});if(hxH>=820){hxH=820}else if(50>=hxH){hxH=50}
|
||||||
var chartdata = new Chart(ctx, {
|
if(hxL>=820){hxL=820}else if(70>=hxL){hxL=70}
|
||||||
type: 'line',
|
ctx.fillStyle='#ffa7a2';ctx.fillText(highestNum+"ms",hxH-40,hyH+15);ctx.fillStyle='#45d642';ctx.fillText(lowestnum+"ms",hxL,hyL+10);console.log("done service_id_{{.Id}}")})}},legend:{display:!1},tooltips:{"enabled":!1},scales:{yAxes:[{display:!1,ticks:{fontSize:20,display:!1,beginAtZero:!1},gridLines:{display:!1}}],xAxes:[{type:'time',distribution:'series',autoSkip:!1,gridLines:{display:!1},ticks:{stepSize:1,min:0,fontColor:"white",fontSize:20,display:!1,}}]},elements:{point:{radius:0}}}})
|
||||||
data: {
|
|
||||||
datasets: [{
|
|
||||||
label: 'Response Time (Milliseconds)',
|
|
||||||
data: {{safe .GraphData}},
|
|
||||||
backgroundColor: [
|
|
||||||
'rgba(47, 206, 30, 0.92)'
|
|
||||||
],
|
|
||||||
borderColor: [
|
|
||||||
'rgb(47, 171, 34)'
|
|
||||||
],
|
|
||||||
borderWidth: 1
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
scaleShowValues: true,
|
|
||||||
layout: {
|
|
||||||
padding: {
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
top: 0,
|
|
||||||
bottom: -10
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
animationDuration: 0,
|
|
||||||
},
|
|
||||||
responsiveAnimationDuration: 0,
|
|
||||||
animation: {
|
|
||||||
duration: 3500,
|
|
||||||
onComplete: function() {
|
|
||||||
var chartInstance = this.chart,
|
|
||||||
ctx = chartInstance.ctx;
|
|
||||||
|
|
||||||
var controller = this.chart.controller;
|
|
||||||
var xAxis = controller.scales['x-axis-0'];
|
|
||||||
var yAxis = controller.scales['y-axis-0'];
|
|
||||||
|
|
||||||
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.textBaseline = 'bottom';
|
|
||||||
|
|
||||||
var numTicks = xAxis.ticks.length;
|
|
||||||
var yOffsetStart = xAxis.width / numTicks;
|
|
||||||
var halfBarWidth = (xAxis.width / (numTicks * 2));
|
|
||||||
|
|
||||||
xAxis.ticks.forEach(function(value, index) {
|
|
||||||
var xOffset = 20;
|
|
||||||
var yOffset = (yOffsetStart * index) + halfBarWidth;
|
|
||||||
ctx.fillStyle = '#e2e2e2';
|
|
||||||
ctx.fillText(value, yOffset, xOffset);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.data.datasets.forEach(function(dataset, i) {
|
|
||||||
var meta = chartInstance.controller.getDatasetMeta(i);
|
|
||||||
var hxH = 0;
|
|
||||||
var hyH = 0;
|
|
||||||
var hxL = 0;
|
|
||||||
var hyL = 0;
|
|
||||||
var highestNum = 0;
|
|
||||||
var lowestnum = 999999999999;
|
|
||||||
meta.data.forEach(function(bar, index) {
|
|
||||||
var data = dataset.data[index];
|
|
||||||
|
|
||||||
if (data.y {{safe "<"}} lowestnum) {
|
|
||||||
lowestnum = data.y;
|
|
||||||
hxL = bar._model.x;
|
|
||||||
hyL = bar._model.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.y > highestNum) {
|
|
||||||
highestNum = data.y;
|
|
||||||
hxH = bar._model.x;
|
|
||||||
hyH = bar._model.y;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (hxH {{safe ">"}}= 820) {
|
|
||||||
hxH = 820;
|
|
||||||
} else if (hxH {{safe "<"}}= 50) {
|
|
||||||
hxH = 50;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hxL {{safe ">"}}= 820) {
|
|
||||||
hxL = 820;
|
|
||||||
} else if (hxL {{safe "<"}}= 70) {
|
|
||||||
hxL = 70;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.fillStyle = '#ffa7a2';
|
|
||||||
ctx.fillText(highestNum+"ms", hxH - 40, hyH + 15);
|
|
||||||
ctx.fillStyle = '#45d642';
|
|
||||||
ctx.fillText(lowestnum+"ms", hxL, hyL + 10);
|
|
||||||
|
|
||||||
console.log("done service_id_{{.Id}}")
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: false
|
|
||||||
},
|
|
||||||
tooltips: {
|
|
||||||
"enabled": false
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
yAxes: [{
|
|
||||||
display: false,
|
|
||||||
ticks: {
|
|
||||||
fontSize: 20,
|
|
||||||
display: false,
|
|
||||||
beginAtZero: false
|
|
||||||
},
|
|
||||||
gridLines: {
|
|
||||||
display:false
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
xAxes: [{
|
|
||||||
type: 'time',
|
|
||||||
distribution: 'series',
|
|
||||||
autoSkip: false,
|
|
||||||
gridLines: {
|
|
||||||
display:false
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
stepSize: 1,
|
|
||||||
min: 0,
|
|
||||||
fontColor: "white",
|
|
||||||
fontSize: 20,
|
|
||||||
display: false,
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
elements: {
|
|
||||||
point: {
|
|
||||||
radius: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
{{ end }}
|
{{ end }}
|
||||||
{{ end }}
|
{{ end }}
|
|
@ -45,9 +45,7 @@
|
||||||
<div class="mt-4" id="service_id_{{.Id}}">
|
<div class="mt-4" id="service_id_{{.Id}}">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
|
||||||
<h4 class="mt-3"><a href="/service/{{.Id}}"{{if not .Online}} class="text-danger"{{end}}>{{ .Name }}</a>
|
<h4 class="mt-3"><a href="/service/{{.Id}}"{{if not .Online}} class="text-danger"{{end}}>{{ .Name }}</a>
|
||||||
{{if .Online}}
|
{{if .Online}}
|
||||||
<span class="badge bg-success float-right">ONLINE</span>
|
<span class="badge bg-success float-right">ONLINE</span>
|
||||||
|
|
Loading…
Reference in New Issue