mirror of https://github.com/statping/statping
better logging (logrus), verbose (-v) mode, index page show create service btn, install.sh script, Dockerfile go version to 1.13, UI fixes
parent
b534652064
commit
15063734a5
|
@ -1,12 +1,11 @@
|
||||||
FROM golang:1.12-alpine as base
|
FROM golang:1.13-alpine as base
|
||||||
LABEL maintainer="Hunter Long (https://github.com/hunterlong)"
|
LABEL maintainer="Hunter Long (https://github.com/hunterlong)"
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
ENV DEP_VERSION v0.5.0
|
|
||||||
RUN apk add --no-cache libstdc++ gcc g++ make git ca-certificates linux-headers wget curl jq libsass
|
RUN apk add --no-cache libstdc++ gcc g++ make git ca-certificates linux-headers wget curl jq libsass
|
||||||
RUN curl -L -s https://assets.statping.com/sass -o /usr/local/bin/sass && \
|
RUN curl -L -s https://assets.statping.com/sass -o /usr/local/bin/sass && \
|
||||||
chmod +x /usr/local/bin/sass
|
chmod +x /usr/local/bin/sass
|
||||||
WORKDIR /go/src/github.com/hunterlong/statping
|
WORKDIR /go/src/github.com/hunterlong/statping
|
||||||
ADD Makefile Gopkg.* /go/src/github.com/hunterlong/statping/
|
ADD Makefile go.mod /go/src/github.com/hunterlong/statping/
|
||||||
RUN go mod vendor && \
|
RUN go mod vendor && \
|
||||||
make dev-deps
|
make dev-deps
|
||||||
ADD . /go/src/github.com/hunterlong/statping
|
ADD . /go/src/github.com/hunterlong/statping
|
||||||
|
|
4
Makefile
4
Makefile
|
@ -1,14 +1,14 @@
|
||||||
VERSION=$(shell cat version.txt)
|
VERSION=$(shell cat version.txt)
|
||||||
SIGN_KEY=B76D61FAA6DB759466E83D9964B9C6AAE2D55278
|
SIGN_KEY=B76D61FAA6DB759466E83D9964B9C6AAE2D55278
|
||||||
BINARY_NAME=statping
|
BINARY_NAME=statping
|
||||||
GOPATH:=$(GOPATH)
|
|
||||||
GOCMD=go
|
GOCMD=go
|
||||||
GOBUILD=$(GOCMD) build -a
|
GOBUILD=$(GOCMD) build -a
|
||||||
GOTEST=$(GOCMD) test
|
GOTEST=$(GOCMD) test
|
||||||
GOGET=$(GOCMD) get
|
GOGET=$(GOCMD) get
|
||||||
GOVERSION=1.13.1
|
GOVERSION=1.13.1
|
||||||
GOINSTALL=$(GOCMD) install
|
GOINSTALL=$(GOCMD) install
|
||||||
XGO=GOPATH=$(GOPATH) xgo -go $(GOVERSION) --dest=build
|
GOPATH:=$(GOPATH)
|
||||||
|
XGO=$(GOPATH) xgo -go $(GOVERSION) --dest=build
|
||||||
BUILDVERSION=-ldflags "-X main.VERSION=${VERSION} -X main.COMMIT=$(TRAVIS_COMMIT)"
|
BUILDVERSION=-ldflags "-X main.VERSION=${VERSION} -X main.COMMIT=$(TRAVIS_COMMIT)"
|
||||||
RICE=$(GOPATH)/bin/rice
|
RICE=$(GOPATH)/bin/rice
|
||||||
PATH:=/usr/local/bin:$(GOPATH)/bin:$(PATH)
|
PATH:=/usr/local/bin:$(GOPATH)/bin:$(PATH)
|
||||||
|
|
60
cmd/cli.go
60
cmd/cli.go
|
@ -43,9 +43,9 @@ func catchCLI(args []string) error {
|
||||||
switch args[0] {
|
switch args[0] {
|
||||||
case "version":
|
case "version":
|
||||||
if COMMIT != "" {
|
if COMMIT != "" {
|
||||||
fmt.Printf("Statping v%v (%v)\n", VERSION, COMMIT)
|
fmt.Printf("%v (%v)\n", VERSION, COMMIT)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("Statping v%v\n", VERSION)
|
fmt.Printf("%v\n", VERSION)
|
||||||
}
|
}
|
||||||
return errors.New("end")
|
return errors.New("end")
|
||||||
case "assets":
|
case "assets":
|
||||||
|
@ -60,18 +60,7 @@ func catchCLI(args []string) error {
|
||||||
}
|
}
|
||||||
return errors.New("end")
|
return errors.New("end")
|
||||||
case "update":
|
case "update":
|
||||||
var err error
|
return updateDisplay()
|
||||||
var gitCurrent githubResponse
|
|
||||||
if gitCurrent, err = checkGithubUpdates(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("Statping Version: v%v\nLatest Version: %v\n", VERSION, gitCurrent.TagName)
|
|
||||||
if VERSION != gitCurrent.TagName[1:] {
|
|
||||||
fmt.Printf("You don't have the latest version v%v!\nDownload the latest release at: https://github.com/hunterlong/statping\n", gitCurrent.TagName[1:])
|
|
||||||
} else {
|
|
||||||
fmt.Printf("You have the latest version of Statping!\n")
|
|
||||||
}
|
|
||||||
return errors.New("end")
|
|
||||||
case "test":
|
case "test":
|
||||||
cmd := args[1]
|
cmd := args[1]
|
||||||
switch cmd {
|
switch cmd {
|
||||||
|
@ -84,16 +73,16 @@ func catchCLI(args []string) error {
|
||||||
fmt.Printf("Statping v%v Exporting Static 'index.html' page...\n", VERSION)
|
fmt.Printf("Statping v%v Exporting Static 'index.html' page...\n", VERSION)
|
||||||
utils.InitLogs()
|
utils.InitLogs()
|
||||||
if core.Configs, err = core.LoadConfigFile(dir); err != nil {
|
if core.Configs, err = core.LoadConfigFile(dir); err != nil {
|
||||||
utils.Log(4, "config.yml file not found")
|
utils.Log.Errorln("config.yml file not found")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
indexSource := ExportIndexHTML()
|
indexSource := ExportIndexHTML()
|
||||||
//core.CloseDB()
|
//core.CloseDB()
|
||||||
if err = utils.SaveFile(dir+"/index.html", indexSource); err != nil {
|
if err = utils.SaveFile(dir+"/index.html", indexSource); err != nil {
|
||||||
utils.Log(4, err)
|
utils.Log.Errorln(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
utils.Log(1, "Exported Statping index page: 'index.html'")
|
utils.Log.Infoln("Exported Statping index page: 'index.html'")
|
||||||
case "help":
|
case "help":
|
||||||
HelpEcho()
|
HelpEcho()
|
||||||
return errors.New("end")
|
return errors.New("end")
|
||||||
|
@ -133,8 +122,8 @@ func catchCLI(args []string) error {
|
||||||
}
|
}
|
||||||
return errors.New("end")
|
return errors.New("end")
|
||||||
case "run":
|
case "run":
|
||||||
utils.Log(1, "Running 1 time and saving to database...")
|
utils.Log.Infoln("Running 1 time and saving to database...")
|
||||||
RunOnce()
|
runOnce()
|
||||||
//core.CloseDB()
|
//core.CloseDB()
|
||||||
fmt.Println("Check is complete.")
|
fmt.Println("Check is complete.")
|
||||||
return errors.New("end")
|
return errors.New("end")
|
||||||
|
@ -142,7 +131,7 @@ func catchCLI(args []string) error {
|
||||||
fmt.Println("Statping Environment Variable")
|
fmt.Println("Statping Environment Variable")
|
||||||
envs, err := godotenv.Read(".env")
|
envs, err := godotenv.Read(".env")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, "No .env file found in current directory.")
|
utils.Log.Errorln("No .env file found in current directory.")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for k, e := range envs {
|
for k, e := range envs {
|
||||||
|
@ -170,16 +159,33 @@ func ExportIndexHTML() []byte {
|
||||||
return w.Body.Bytes()
|
return w.Body.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RunOnce will initialize the Statping application and check each service 1 time, will not run HTTP server
|
func updateDisplay() error {
|
||||||
func RunOnce() {
|
var err error
|
||||||
|
var gitCurrent githubResponse
|
||||||
|
if gitCurrent, err = checkGithubUpdates(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Statping Version: v%v\nLatest Version: %v\n", VERSION, gitCurrent.TagName)
|
||||||
|
if VERSION != gitCurrent.TagName[1:] {
|
||||||
|
fmt.Printf("\n New Update %v Available\n", gitCurrent.TagName[1:])
|
||||||
|
fmt.Printf("Update Command:\n")
|
||||||
|
fmt.Printf("curl -o- -L https://statping.com/install.sh | bash\n\n")
|
||||||
|
} else {
|
||||||
|
fmt.Printf("You have the latest version of Statping!\n")
|
||||||
|
}
|
||||||
|
return errors.New("end")
|
||||||
|
}
|
||||||
|
|
||||||
|
// runOnce will initialize the Statping application and check each service 1 time, will not run HTTP server
|
||||||
|
func runOnce() {
|
||||||
var err error
|
var err error
|
||||||
core.Configs, err = core.LoadConfigFile(utils.Directory)
|
core.Configs, err = core.LoadConfigFile(utils.Directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, "config.yml file not found")
|
utils.Log.Errorln("config.yml file not found")
|
||||||
}
|
}
|
||||||
err = core.Configs.Connect(false, utils.Directory)
|
err = core.Configs.Connect(false, utils.Directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, err)
|
utils.Log.Errorln(err)
|
||||||
}
|
}
|
||||||
core.CoreApp, err = core.SelectCore()
|
core.CoreApp, err = core.SelectCore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -187,7 +193,7 @@ func RunOnce() {
|
||||||
}
|
}
|
||||||
_, err = core.CoreApp.SelectAllServices(true)
|
_, err = core.CoreApp.SelectAllServices(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, err)
|
utils.Log.Errorln(err)
|
||||||
}
|
}
|
||||||
for _, out := range core.CoreApp.Services {
|
for _, out := range core.CoreApp.Services {
|
||||||
out.Check(true)
|
out.Check(true)
|
||||||
|
@ -214,6 +220,8 @@ func HelpEcho() {
|
||||||
fmt.Printf("Flags:\n")
|
fmt.Printf("Flags:\n")
|
||||||
fmt.Println(" -ip 127.0.0.1 - Run HTTP server on specific IP address (default: localhost)")
|
fmt.Println(" -ip 127.0.0.1 - Run HTTP server on specific IP address (default: localhost)")
|
||||||
fmt.Println(" -port 8080 - Run HTTP server on Port (default: 8080)")
|
fmt.Println(" -port 8080 - Run HTTP server on Port (default: 8080)")
|
||||||
|
fmt.Println(" -verbose 1 - Verbose mode levels 1 - 4 (default: 1)")
|
||||||
|
fmt.Println(" -env path/debug.env - Optional .env file to set as environment variables while running server")
|
||||||
fmt.Printf("Environment Variables:\n")
|
fmt.Printf("Environment Variables:\n")
|
||||||
fmt.Println(" PORT - Set the outgoing port for the HTTP server (or use -port)")
|
fmt.Println(" PORT - Set the outgoing port for the HTTP server (or use -port)")
|
||||||
fmt.Println(" IP - Bind a specific IP address to the HTTP server (or use -ip)")
|
fmt.Println(" IP - Bind a specific IP address to the HTTP server (or use -ip)")
|
||||||
|
@ -241,7 +249,7 @@ func HelpEcho() {
|
||||||
func checkGithubUpdates() (githubResponse, error) {
|
func checkGithubUpdates() (githubResponse, error) {
|
||||||
var gitResp githubResponse
|
var gitResp githubResponse
|
||||||
url := "https://api.github.com/repos/hunterlong/statping/releases/latest"
|
url := "https://api.github.com/repos/hunterlong/statping/releases/latest"
|
||||||
contents, _, err := utils.HttpRequest(url, "GET", nil, nil, nil, time.Duration(10*time.Second), true)
|
contents, _, err := utils.HttpRequest(url, "GET", nil, nil, nil, time.Duration(2*time.Second), true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return githubResponse{}, err
|
return githubResponse{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ func TestStartServerCommand(t *testing.T) {
|
||||||
func TestVersionCommand(t *testing.T) {
|
func TestVersionCommand(t *testing.T) {
|
||||||
c := testcli.Command("statping", "version")
|
c := testcli.Command("statping", "version")
|
||||||
c.Run()
|
c.Run()
|
||||||
assert.True(t, c.StdoutContains("Statping v"+VERSION))
|
assert.True(t, c.StdoutContains(VERSION))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHelpCommand(t *testing.T) {
|
func TestHelpCommand(t *testing.T) {
|
||||||
|
@ -90,7 +90,7 @@ func TestUpdateCommand(t *testing.T) {
|
||||||
commandAndSleep(cmd, time.Duration(15*time.Second), got)
|
commandAndSleep(cmd, time.Duration(15*time.Second), got)
|
||||||
gg, _ := <-got
|
gg, _ := <-got
|
||||||
t.Log(gg)
|
t.Log(gg)
|
||||||
assert.Contains(t, gg, "Statping")
|
assert.Contains(t, gg, VERSION)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAssetsCommand(t *testing.T) {
|
func TestAssetsCommand(t *testing.T) {
|
||||||
|
|
41
cmd/main.go
41
cmd/main.go
|
@ -16,13 +16,14 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hunterlong/statping/utils"
|
||||||
|
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hunterlong/statping/core"
|
"github.com/hunterlong/statping/core"
|
||||||
"github.com/hunterlong/statping/handlers"
|
"github.com/hunterlong/statping/handlers"
|
||||||
"github.com/hunterlong/statping/plugin"
|
"github.com/hunterlong/statping/plugin"
|
||||||
"github.com/hunterlong/statping/source"
|
"github.com/hunterlong/statping/source"
|
||||||
"github.com/hunterlong/statping/utils"
|
|
||||||
"github.com/joho/godotenv"
|
"github.com/joho/godotenv"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
@ -35,7 +36,8 @@ var (
|
||||||
// COMMIT stores the git commit hash for this version of Statping
|
// COMMIT stores the git commit hash for this version of Statping
|
||||||
COMMIT string
|
COMMIT string
|
||||||
ipAddress string
|
ipAddress string
|
||||||
UsingDotEnv bool
|
envFile string
|
||||||
|
verboseMode int
|
||||||
port int
|
port int
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -47,17 +49,21 @@ func init() {
|
||||||
// -ip = 0.0.0.0 IP address for outgoing HTTP server
|
// -ip = 0.0.0.0 IP address for outgoing HTTP server
|
||||||
// -port = 8080 Port number for outgoing HTTP server
|
// -port = 8080 Port number for outgoing HTTP server
|
||||||
func parseFlags() {
|
func parseFlags() {
|
||||||
ip := flag.String("ip", "0.0.0.0", "IP address to run the Statping HTTP server")
|
flag.StringVar(&ipAddress, "ip", "0.0.0.0", "IP address to run the Statping HTTP server")
|
||||||
p := flag.Int("port", 8080, "Port to run the HTTP server")
|
flag.StringVar(&envFile, "env", "", "IP address to run the Statping HTTP server")
|
||||||
|
flag.IntVar(&port, "port", 8080, "Port to run the HTTP server")
|
||||||
|
flag.IntVar(&verboseMode, "verbose", 1, "Run in verbose mode to see detailed logs (1 - 4)")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
ipAddress = *ip
|
|
||||||
port = *p
|
|
||||||
if os.Getenv("PORT") != "" {
|
if os.Getenv("PORT") != "" {
|
||||||
port = int(utils.ToInt(os.Getenv("PORT")))
|
port = int(utils.ToInt(os.Getenv("PORT")))
|
||||||
}
|
}
|
||||||
if os.Getenv("IP") != "" {
|
if os.Getenv("IP") != "" {
|
||||||
ipAddress = os.Getenv("IP")
|
ipAddress = os.Getenv("IP")
|
||||||
}
|
}
|
||||||
|
if os.Getenv("VERBOSE") != "" {
|
||||||
|
verboseMode = int(utils.ToInt(os.Getenv("VERBOSE")))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// main will run the Statping application
|
// main will run the Statping application
|
||||||
|
@ -67,6 +73,7 @@ func main() {
|
||||||
parseFlags()
|
parseFlags()
|
||||||
loadDotEnvs()
|
loadDotEnvs()
|
||||||
source.Assets()
|
source.Assets()
|
||||||
|
utils.VerboseMode = verboseMode
|
||||||
if err := utils.InitLogs(); err != nil {
|
if err := utils.InitLogs(); err != nil {
|
||||||
fmt.Printf("Statping Log Error: \n %v\n", err)
|
fmt.Printf("Statping Log Error: \n %v\n", err)
|
||||||
os.Exit(2)
|
os.Exit(2)
|
||||||
|
@ -83,33 +90,39 @@ func main() {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
utils.Log(1, fmt.Sprintf("Starting Statping v%v", VERSION))
|
utils.Log.Info(fmt.Sprintf("Starting Statping v%v", VERSION))
|
||||||
|
updateDisplay()
|
||||||
|
|
||||||
core.Configs, err = core.LoadConfigFile(utils.Directory)
|
core.Configs, err = core.LoadConfigFile(utils.Directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, err)
|
utils.Log.Errorln(err)
|
||||||
core.SetupMode = true
|
core.SetupMode = true
|
||||||
utils.Log(1, handlers.RunHTTPServer(ipAddress, port))
|
utils.Log.Infoln(handlers.RunHTTPServer(ipAddress, port))
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
mainProcess()
|
mainProcess()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close will gracefully stop the database connection, and log file
|
||||||
|
func Close() {
|
||||||
|
core.CloseDB()
|
||||||
|
utils.CloseLogs()
|
||||||
|
}
|
||||||
|
|
||||||
// sigterm will attempt to close the database connections gracefully
|
// sigterm will attempt to close the database connections gracefully
|
||||||
func sigterm() {
|
func sigterm() {
|
||||||
sigs := make(chan os.Signal, 1)
|
sigs := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
|
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
|
||||||
<-sigs
|
<-sigs
|
||||||
core.CloseDB()
|
Close()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// loadDotEnvs attempts to load database configs from a '.env' file in root directory
|
// loadDotEnvs attempts to load database configs from a '.env' file in root directory
|
||||||
func loadDotEnvs() error {
|
func loadDotEnvs() error {
|
||||||
err := godotenv.Load()
|
err := godotenv.Load(envFile)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
utils.Log(1, "Environment file '.env' Loaded")
|
utils.Log.Infoln("Environment file '.env' Loaded")
|
||||||
UsingDotEnv = true
|
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -120,7 +133,7 @@ func mainProcess() {
|
||||||
var err error
|
var err error
|
||||||
err = core.Configs.Connect(false, dir)
|
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.Errorln(fmt.Sprintf("could not connect to database: %v", err))
|
||||||
}
|
}
|
||||||
core.Configs.MigrateDatabase()
|
core.Configs.MigrateDatabase()
|
||||||
core.InitApp()
|
core.InitApp()
|
||||||
|
|
|
@ -32,7 +32,7 @@ import (
|
||||||
|
|
||||||
// checkServices will start the checking go routine for each service
|
// checkServices will start the checking go routine for each service
|
||||||
func checkServices() {
|
func checkServices() {
|
||||||
utils.Log(1, fmt.Sprintf("Starting monitoring process for %v Services", len(CoreApp.Services)))
|
log.Infoln(fmt.Sprintf("Starting monitoring process for %v Services", len(CoreApp.Services)))
|
||||||
for _, ser := range CoreApp.Services {
|
for _, ser := range CoreApp.Services {
|
||||||
//go obj.StartCheckins()
|
//go obj.StartCheckins()
|
||||||
go ser.CheckQueue(true)
|
go ser.CheckQueue(true)
|
||||||
|
@ -60,7 +60,7 @@ CheckLoop:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-s.Running:
|
case <-s.Running:
|
||||||
utils.Log(1, fmt.Sprintf("Stopping service: %v", s.Name))
|
log.Infoln(fmt.Sprintf("Stopping service: %v", s.Name))
|
||||||
break CheckLoop
|
break CheckLoop
|
||||||
case <-time.After(s.SleepDuration):
|
case <-time.After(s.SleepDuration):
|
||||||
s.Check(record)
|
s.Check(record)
|
||||||
|
@ -229,7 +229,7 @@ func (s *Service) checkHttp(record bool) *Service {
|
||||||
if s.Expected.String != "" {
|
if s.Expected.String != "" {
|
||||||
match, err := regexp.MatchString(s.Expected.String, string(content))
|
match, err := regexp.MatchString(s.Expected.String, string(content))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(2, fmt.Sprintf("Service %v expected: %v to match %v", s.Name, string(content), s.Expected.String))
|
log.Warnln(fmt.Sprintf("Service %v expected: %v to match %v", s.Name, string(content), s.Expected.String))
|
||||||
}
|
}
|
||||||
if !match {
|
if !match {
|
||||||
if record {
|
if record {
|
||||||
|
@ -259,8 +259,8 @@ func recordSuccess(s *Service) {
|
||||||
PingTime: s.PingTime,
|
PingTime: s.PingTime,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
utils.Log(1, fmt.Sprintf("Service %v Successful Response: %0.2f ms | Lookup in: %0.2f ms", s.Name, hit.Latency*1000, hit.PingTime*1000))
|
|
||||||
s.CreateHit(hit)
|
s.CreateHit(hit)
|
||||||
|
log.WithFields(utils.ToFields(hit, s.Select())).Infoln(fmt.Sprintf("Service %v Successful Response: %0.2f ms | Lookup in: %0.2f ms", s.Name, hit.Latency*1000, hit.PingTime*1000))
|
||||||
notifier.OnSuccess(s.Service)
|
notifier.OnSuccess(s.Service)
|
||||||
s.Online = true
|
s.Online = true
|
||||||
s.SuccessNotified = true
|
s.SuccessNotified = true
|
||||||
|
@ -268,18 +268,18 @@ func recordSuccess(s *Service) {
|
||||||
|
|
||||||
// recordFailure will create a new 'Failure' record in the database for a offline service
|
// recordFailure will create a new 'Failure' record in the database for a offline service
|
||||||
func recordFailure(s *Service, issue string) {
|
func recordFailure(s *Service, issue string) {
|
||||||
fail := &Failure{&types.Failure{
|
fail := &types.Failure{
|
||||||
Service: s.Id,
|
Service: s.Id,
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
PingTime: s.PingTime,
|
PingTime: s.PingTime,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
ErrorCode: s.LastStatusCode,
|
ErrorCode: s.LastStatusCode,
|
||||||
}}
|
}
|
||||||
utils.Log(2, fmt.Sprintf("Service %v Failing: %v | Lookup in: %0.2f ms", s.Name, issue, fail.PingTime*1000))
|
log.WithFields(utils.ToFields(fail, s.Select())).Warnln(fmt.Sprintf("Service %v Failing: %v | Lookup in: %0.2f ms", s.Name, issue, fail.PingTime*1000))
|
||||||
s.CreateFailure(fail)
|
s.CreateFailure(fail)
|
||||||
s.Online = false
|
s.Online = false
|
||||||
s.SuccessNotified = false
|
s.SuccessNotified = false
|
||||||
s.UpdateNotify = CoreApp.UpdateNotify.Bool
|
s.UpdateNotify = CoreApp.UpdateNotify.Bool
|
||||||
s.DownText = s.DowntimeText()
|
s.DownText = s.DowntimeText()
|
||||||
notifier.OnFailure(s.Service, fail.Failure)
|
notifier.OnFailure(s.Service, fail)
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,14 +47,14 @@ CheckinLoop:
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-c.Running:
|
case <-c.Running:
|
||||||
utils.Log(1, fmt.Sprintf("Stopping checkin routine: %v", c.Name))
|
log.Infoln(fmt.Sprintf("Stopping checkin routine: %v", c.Name))
|
||||||
c.Failing = false
|
c.Failing = false
|
||||||
break CheckinLoop
|
break CheckinLoop
|
||||||
case <-time.After(reCheck):
|
case <-time.After(reCheck):
|
||||||
utils.Log(1, fmt.Sprintf("Checkin %v is expected at %v, checking every %v", c.Name, utils.FormatDuration(c.Expected()), utils.FormatDuration(c.Period())))
|
log.Infoln(fmt.Sprintf("Checkin %v is expected at %v, checking every %v", c.Name, utils.FormatDuration(c.Expected()), utils.FormatDuration(c.Period())))
|
||||||
if c.Expected().Seconds() <= 0 {
|
if c.Expected().Seconds() <= 0 {
|
||||||
issue := fmt.Sprintf("Checkin %v is failing, no request since %v", c.Name, c.Last().CreatedAt)
|
issue := fmt.Sprintf("Checkin %v is failing, no request since %v", c.Name, c.Last().CreatedAt)
|
||||||
utils.Log(3, issue)
|
log.Errorln(issue)
|
||||||
c.CreateFailure()
|
c.CreateFailure()
|
||||||
}
|
}
|
||||||
reCheck = c.Period()
|
reCheck = c.Period()
|
||||||
|
@ -143,7 +143,7 @@ func (c *Checkin) Grace() time.Duration {
|
||||||
// Expected returns the duration of when the serviec should receive a Checkin
|
// Expected returns the duration of when the serviec should receive a Checkin
|
||||||
func (c *Checkin) Expected() time.Duration {
|
func (c *Checkin) Expected() time.Duration {
|
||||||
last := c.Last().CreatedAt
|
last := c.Last().CreatedAt
|
||||||
now := time.Now()
|
now := utils.Now()
|
||||||
lastDir := now.Sub(last)
|
lastDir := now.Sub(last)
|
||||||
sub := time.Duration(c.Period() - lastDir)
|
sub := time.Duration(c.Period() - lastDir)
|
||||||
return sub
|
return sub
|
||||||
|
@ -213,7 +213,7 @@ func (c *Checkin) Create() (int64, error) {
|
||||||
c.ApiKey = utils.RandomString(7)
|
c.ApiKey = utils.RandomString(7)
|
||||||
row := checkinDB().Create(&c)
|
row := checkinDB().Create(&c)
|
||||||
if row.Error != nil {
|
if row.Error != nil {
|
||||||
utils.Log(2, row.Error)
|
log.Warnln(row.Error)
|
||||||
return 0, row.Error
|
return 0, row.Error
|
||||||
}
|
}
|
||||||
service := SelectService(c.ServiceId)
|
service := SelectService(c.ServiceId)
|
||||||
|
@ -227,7 +227,7 @@ func (c *Checkin) Create() (int64, error) {
|
||||||
func (c *Checkin) Update() (int64, error) {
|
func (c *Checkin) Update() (int64, error) {
|
||||||
row := checkinDB().Update(&c)
|
row := checkinDB().Update(&c)
|
||||||
if row.Error != nil {
|
if row.Error != nil {
|
||||||
utils.Log(2, row.Error)
|
log.Warnln(row.Error)
|
||||||
return 0, row.Error
|
return 0, row.Error
|
||||||
}
|
}
|
||||||
return c.Id, row.Error
|
return c.Id, row.Error
|
||||||
|
@ -236,11 +236,11 @@ func (c *Checkin) Update() (int64, error) {
|
||||||
// Create will create a new successful checkinHit
|
// Create will create a new successful checkinHit
|
||||||
func (c *CheckinHit) Create() (int64, error) {
|
func (c *CheckinHit) Create() (int64, error) {
|
||||||
if c.CreatedAt.IsZero() {
|
if c.CreatedAt.IsZero() {
|
||||||
c.CreatedAt = time.Now()
|
c.CreatedAt = utils.Now()
|
||||||
}
|
}
|
||||||
row := checkinHitsDB().Create(&c)
|
row := checkinHitsDB().Create(&c)
|
||||||
if row.Error != nil {
|
if row.Error != nil {
|
||||||
utils.Log(2, row.Error)
|
log.Warnln(row.Error)
|
||||||
return 0, row.Error
|
return 0, row.Error
|
||||||
}
|
}
|
||||||
return c.Id, row.Error
|
return c.Id, row.Error
|
||||||
|
@ -248,13 +248,13 @@ func (c *CheckinHit) Create() (int64, error) {
|
||||||
|
|
||||||
// Ago returns the duration of time between now and the last successful checkinHit
|
// Ago returns the duration of time between now and the last successful checkinHit
|
||||||
func (c *CheckinHit) Ago() string {
|
func (c *CheckinHit) Ago() string {
|
||||||
got, _ := timeago.TimeAgoWithTime(time.Now(), c.CreatedAt)
|
got, _ := timeago.TimeAgoWithTime(utils.Now(), c.CreatedAt)
|
||||||
return got
|
return got
|
||||||
}
|
}
|
||||||
|
|
||||||
// RecheckCheckinFailure will check if a Service Checkin has been reported yet
|
// RecheckCheckinFailure will check if a Service Checkin has been reported yet
|
||||||
func (c *Checkin) RecheckCheckinFailure(guard chan struct{}) {
|
func (c *Checkin) RecheckCheckinFailure(guard chan struct{}) {
|
||||||
between := time.Now().Sub(time.Now()).Seconds()
|
between := utils.Now().Sub(utils.Now()).Seconds()
|
||||||
if between > float64(c.Interval) {
|
if between > float64(c.Interval) {
|
||||||
fmt.Println("rechecking every 15 seconds!")
|
fmt.Println("rechecking every 15 seconds!")
|
||||||
time.Sleep(15 * time.Second)
|
time.Sleep(15 * time.Second)
|
||||||
|
|
|
@ -34,7 +34,7 @@ type ErrorResponse struct {
|
||||||
func LoadConfigFile(directory string) (*DbConfig, error) {
|
func LoadConfigFile(directory string) (*DbConfig, error) {
|
||||||
var configs *DbConfig
|
var configs *DbConfig
|
||||||
if os.Getenv("DB_CONN") != "" {
|
if os.Getenv("DB_CONN") != "" {
|
||||||
utils.Log(1, "DB_CONN environment variable was found, waiting for database...")
|
log.Infoln("DB_CONN environment variable was found, waiting for database...")
|
||||||
return LoadUsingEnv()
|
return LoadUsingEnv()
|
||||||
}
|
}
|
||||||
file, err := ioutil.ReadFile(directory + "/config.yml")
|
file, err := ioutil.ReadFile(directory + "/config.yml")
|
||||||
|
@ -66,18 +66,18 @@ func LoadUsingEnv() (*DbConfig, error) {
|
||||||
|
|
||||||
err = Configs.Connect(true, utils.Directory)
|
err = Configs.Connect(true, utils.Directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
Configs.Save()
|
Configs.Save()
|
||||||
exists := DbSession.HasTable("core")
|
exists := DbSession.HasTable("core")
|
||||||
if !exists {
|
if !exists {
|
||||||
utils.Log(1, fmt.Sprintf("Core database does not exist, creating now!"))
|
log.Infoln(fmt.Sprintf("Core database does not exist, creating now!"))
|
||||||
Configs.DropDatabase()
|
Configs.DropDatabase()
|
||||||
Configs.CreateDatabase()
|
Configs.CreateDatabase()
|
||||||
CoreApp, err = Configs.InsertCore()
|
CoreApp, err = Configs.InsertCore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, err)
|
log.Errorln(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
username := os.Getenv("ADMIN_USER")
|
username := os.Getenv("ADMIN_USER")
|
||||||
|
@ -195,7 +195,7 @@ func SampleData() error {
|
||||||
func DeleteConfig() error {
|
func DeleteConfig() error {
|
||||||
err := os.Remove(utils.Directory + "/config.yml")
|
err := os.Remove(utils.Directory + "/config.yml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, err)
|
log.Errorln(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -40,6 +40,7 @@ var (
|
||||||
CoreApp *Core // CoreApp is a global variable that contains many elements
|
CoreApp *Core // CoreApp is a global variable that contains many elements
|
||||||
SetupMode bool // SetupMode will be true if Statping does not have a database connection
|
SetupMode bool // SetupMode will be true if Statping does not have a database connection
|
||||||
VERSION string // VERSION is set on build automatically by setting a -ldflag
|
VERSION string // VERSION is set on build automatically by setting a -ldflag
|
||||||
|
log = utils.Log.WithField("type", "core")
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
|
|
@ -134,6 +134,7 @@ func TestEnvToConfig(t *testing.T) {
|
||||||
os.Setenv("DESCRIPTION", "Testing Statping")
|
os.Setenv("DESCRIPTION", "Testing Statping")
|
||||||
os.Setenv("ADMIN_USER", "admin")
|
os.Setenv("ADMIN_USER", "admin")
|
||||||
os.Setenv("ADMIN_PASS", "admin123")
|
os.Setenv("ADMIN_PASS", "admin123")
|
||||||
|
os.Setenv("VERBOSE", "true")
|
||||||
config, err := EnvToConfig()
|
config, err := EnvToConfig()
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.Equal(t, config.DbConn, "sqlite")
|
assert.Equal(t, config.DbConn, "sqlite")
|
||||||
|
|
|
@ -217,7 +217,7 @@ func (db *DbConfig) Connect(retry bool, location string) error {
|
||||||
dbSession, err := gorm.Open(dbType, conn)
|
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...", Configs.DbHost))
|
log.Infoln(fmt.Sprintf("Database connection to '%v' is not available, trying again in 5 seconds...", Configs.DbHost))
|
||||||
return db.waitForDb()
|
return db.waitForDb()
|
||||||
} else {
|
} else {
|
||||||
return err
|
return err
|
||||||
|
@ -229,7 +229,10 @@ func (db *DbConfig) Connect(retry bool, location string) error {
|
||||||
err = dbSession.DB().Ping()
|
err = dbSession.DB().Ping()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
DbSession = dbSession
|
DbSession = dbSession
|
||||||
utils.Log(1, fmt.Sprintf("Database %v connection was successful.", dbType))
|
if utils.VerboseMode >= 4 {
|
||||||
|
DbSession.LogMode(true).Debug().SetLogger(log)
|
||||||
|
}
|
||||||
|
log.Infoln(fmt.Sprintf("Database %v connection was successful.", dbType))
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -244,7 +247,7 @@ func (db *DbConfig) waitForDb() error {
|
||||||
// this function is currently set to delete records 7+ days old every 60 minutes
|
// 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 3 months...")
|
log.Infoln("Checking for database records older than 3 months...")
|
||||||
since := time.Now().AddDate(0, -3, 0).UTC()
|
since := time.Now().AddDate(0, -3, 0).UTC()
|
||||||
DeleteAllSince("failures", since)
|
DeleteAllSince("failures", since)
|
||||||
DeleteAllSince("hits", since)
|
DeleteAllSince("hits", since)
|
||||||
|
@ -256,7 +259,7 @@ 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"))
|
||||||
db := DbSession.Exec(sql)
|
db := DbSession.Exec(sql)
|
||||||
if db.Error != nil {
|
if db.Error != nil {
|
||||||
utils.Log(2, db.Error)
|
log.Warnln(db.Error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -265,12 +268,12 @@ func (db *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")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
data, err := yaml.Marshal(db)
|
data, err := yaml.Marshal(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, err)
|
log.Errorln(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
config.WriteString(string(data))
|
config.WriteString(string(data))
|
||||||
|
@ -283,14 +286,14 @@ func (db *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)
|
log.Errorln(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
db.ApiKey = utils.NewSHA1Hash(16)
|
db.ApiKey = utils.NewSHA1Hash(16)
|
||||||
db.ApiSecret = utils.NewSHA1Hash(16)
|
db.ApiSecret = utils.NewSHA1Hash(16)
|
||||||
data, err := yaml.Marshal(db)
|
data, err := yaml.Marshal(db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, err)
|
log.Errorln(err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
config.WriteString(string(data))
|
config.WriteString(string(data))
|
||||||
|
@ -315,14 +318,14 @@ func (c *DbConfig) CreateCore() *Core {
|
||||||
}
|
}
|
||||||
CoreApp, err := SelectCore()
|
CoreApp, err := SelectCore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
}
|
}
|
||||||
return CoreApp
|
return CoreApp
|
||||||
}
|
}
|
||||||
|
|
||||||
// DropDatabase will DROP each table Statping created
|
// DropDatabase will DROP each table Statping created
|
||||||
func (db *DbConfig) DropDatabase() error {
|
func (db *DbConfig) DropDatabase() error {
|
||||||
utils.Log(1, "Dropping Database Tables...")
|
log.Infoln("Dropping Database Tables...")
|
||||||
err := DbSession.DropTableIfExists("checkins")
|
err := DbSession.DropTableIfExists("checkins")
|
||||||
err = DbSession.DropTableIfExists("checkin_hits")
|
err = DbSession.DropTableIfExists("checkin_hits")
|
||||||
err = DbSession.DropTableIfExists("notifications")
|
err = DbSession.DropTableIfExists("notifications")
|
||||||
|
@ -340,7 +343,7 @@ func (db *DbConfig) DropDatabase() error {
|
||||||
// CreateDatabase will CREATE TABLES for each of the Statping elements
|
// CreateDatabase will CREATE TABLES for each of the Statping elements
|
||||||
func (db *DbConfig) CreateDatabase() error {
|
func (db *DbConfig) CreateDatabase() error {
|
||||||
var err error
|
var err error
|
||||||
utils.Log(1, "Creating Database Tables...")
|
log.Infoln("Creating Database Tables...")
|
||||||
for _, table := range DbModels {
|
for _, table := range DbModels {
|
||||||
if err := DbSession.CreateTable(table); err.Error != nil {
|
if err := DbSession.CreateTable(table); err.Error != nil {
|
||||||
return err.Error
|
return err.Error
|
||||||
|
@ -349,7 +352,7 @@ func (db *DbConfig) CreateDatabase() error {
|
||||||
if err := DbSession.Table("core").CreateTable(&types.Core{}); err.Error != nil {
|
if err := DbSession.Table("core").CreateTable(&types.Core{}); err.Error != nil {
|
||||||
return err.Error
|
return err.Error
|
||||||
}
|
}
|
||||||
utils.Log(1, "Statping Database Created")
|
log.Infoln("Statping Database Created")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,7 +360,7 @@ func (db *DbConfig) CreateDatabase() error {
|
||||||
// This function will NOT remove previous records, tables or columns from the database.
|
// This function will NOT remove previous records, tables or columns from the database.
|
||||||
// If this function has an issue, it will ROLLBACK to the previous state.
|
// If this function has an issue, it will ROLLBACK to the previous state.
|
||||||
func (db *DbConfig) MigrateDatabase() error {
|
func (db *DbConfig) MigrateDatabase() error {
|
||||||
utils.Log(1, "Migrating Database Tables...")
|
log.Infoln("Migrating Database Tables...")
|
||||||
tx := DbSession.Begin()
|
tx := DbSession.Begin()
|
||||||
defer func() {
|
defer func() {
|
||||||
if r := recover(); r != nil {
|
if r := recover(); r != nil {
|
||||||
|
@ -372,9 +375,9 @@ func (db *DbConfig) MigrateDatabase() error {
|
||||||
}
|
}
|
||||||
if err := tx.Table("core").AutoMigrate(&types.Core{}); err.Error != nil {
|
if err := tx.Table("core").AutoMigrate(&types.Core{}); err.Error != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
utils.Log(3, fmt.Sprintf("Statping Database could not be migrated: %v", tx.Error))
|
log.Errorln(fmt.Sprintf("Statping Database could not be migrated: %v", tx.Error))
|
||||||
return tx.Error
|
return tx.Error
|
||||||
}
|
}
|
||||||
utils.Log(1, "Statping Database Migrated")
|
log.Infoln("Statping Database Migrated")
|
||||||
return tx.Commit().Error
|
return tx.Commit().Error
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"github.com/hunterlong/statping/source"
|
"github.com/hunterlong/statping/source"
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/hunterlong/statping/utils"
|
|
||||||
"html/template"
|
"html/template"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -28,7 +27,7 @@ import (
|
||||||
func ExportChartsJs() string {
|
func ExportChartsJs() string {
|
||||||
render, err := source.JsBox.String("charts.js")
|
render, err := source.JsBox.String("charts.js")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
}
|
}
|
||||||
t := template.New("charts")
|
t := template.New("charts")
|
||||||
t.Funcs(template.FuncMap{
|
t.Funcs(template.FuncMap{
|
||||||
|
@ -39,7 +38,7 @@ func ExportChartsJs() string {
|
||||||
t.Parse(render)
|
t.Parse(render)
|
||||||
var tpl bytes.Buffer
|
var tpl bytes.Buffer
|
||||||
if err := t.Execute(&tpl, CoreApp.Services); err != nil {
|
if err := t.Execute(&tpl, CoreApp.Services); err != nil {
|
||||||
utils.Log(3, err)
|
log.Errorln(err)
|
||||||
}
|
}
|
||||||
result := tpl.String()
|
result := tpl.String()
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -19,7 +19,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/ararog/timeago"
|
"github.com/ararog/timeago"
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/hunterlong/statping/utils"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -35,16 +34,15 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
// CreateFailure will create a new Failure record for a service
|
// CreateFailure will create a new Failure record for a service
|
||||||
func (s *Service) CreateFailure(fail types.FailureInterface) (int64, error) {
|
func (s *Service) CreateFailure(f *types.Failure) (int64, error) {
|
||||||
f := fail.(*Failure)
|
|
||||||
f.Service = s.Id
|
f.Service = s.Id
|
||||||
row := failuresDB().Create(f)
|
row := failuresDB().Create(f)
|
||||||
if row.Error != nil {
|
if row.Error != nil {
|
||||||
utils.Log(3, row.Error)
|
log.Errorln(row.Error)
|
||||||
return 0, row.Error
|
return 0, row.Error
|
||||||
}
|
}
|
||||||
sort.Sort(types.FailSort(s.Failures))
|
sort.Sort(types.FailSort(s.Failures))
|
||||||
s.Failures = append(s.Failures, f)
|
//s.Failures = append(s.Failures, f)
|
||||||
if len(s.Failures) > limitedFailures {
|
if len(s.Failures) > limitedFailures {
|
||||||
s.Failures = s.Failures[1:]
|
s.Failures = s.Failures[1:]
|
||||||
}
|
}
|
||||||
|
@ -57,7 +55,7 @@ func (s *Service) AllFailures() []*Failure {
|
||||||
col := failuresDB().Where("service = ?", s.Id).Not("method = 'checkin'").Order("id desc")
|
col := failuresDB().Where("service = ?", s.Id).Not("method = 'checkin'").Order("id desc")
|
||||||
err := col.Find(&fails)
|
err := col.Find(&fails)
|
||||||
if err.Error != nil {
|
if err.Error != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Issue getting failures for service %v, %v", s.Name, err))
|
log.Errorln(fmt.Sprintf("Issue getting failures for service %v, %v", s.Name, err))
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fails
|
return fails
|
||||||
|
@ -67,7 +65,7 @@ func (s *Service) AllFailures() []*Failure {
|
||||||
func (s *Service) DeleteFailures() {
|
func (s *Service) DeleteFailures() {
|
||||||
err := DbSession.Exec(`DELETE FROM failures WHERE service = ?`, s.Id)
|
err := DbSession.Exec(`DELETE FROM failures WHERE service = ?`, s.Id)
|
||||||
if err.Error != nil {
|
if err.Error != nil {
|
||||||
utils.Log(3, fmt.Sprintf("failed to delete all failures: %v", err))
|
log.Errorln(fmt.Sprintf("failed to delete all failures: %v", err))
|
||||||
}
|
}
|
||||||
s.Failures = nil
|
s.Failures = nil
|
||||||
}
|
}
|
||||||
|
@ -119,7 +117,7 @@ func CountFailures() uint64 {
|
||||||
var count uint64
|
var count uint64
|
||||||
err := failuresDB().Count(&count)
|
err := failuresDB().Count(&count)
|
||||||
if err.Error != nil {
|
if err.Error != nil {
|
||||||
utils.Log(2, err.Error)
|
log.Warnln(err.Error)
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
return count
|
return count
|
||||||
|
|
|
@ -17,7 +17,6 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/hunterlong/statping/utils"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -29,7 +28,7 @@ type Hit struct {
|
||||||
func (s *Service) CreateHit(h *types.Hit) (int64, error) {
|
func (s *Service) CreateHit(h *types.Hit) (int64, error) {
|
||||||
db := hitsDB().Create(&h)
|
db := hitsDB().Create(&h)
|
||||||
if db.Error != nil {
|
if db.Error != nil {
|
||||||
utils.Log(2, db.Error)
|
log.Warnln(db.Error)
|
||||||
return 0, db.Error
|
return 0, db.Error
|
||||||
}
|
}
|
||||||
return h.Id, db.Error
|
return h.Id, db.Error
|
||||||
|
|
|
@ -18,7 +18,6 @@ package core
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
"github.com/hunterlong/statping/utils"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -64,7 +63,7 @@ func (m *Message) Create() (int64, error) {
|
||||||
m.CreatedAt = time.Now().UTC()
|
m.CreatedAt = time.Now().UTC()
|
||||||
db := messagesDb().Create(m)
|
db := messagesDb().Create(m)
|
||||||
if db.Error != nil {
|
if db.Error != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Failed to create message %v #%v: %v", m.Title, m.Id, db.Error))
|
log.Errorln(fmt.Sprintf("Failed to create message %v #%v: %v", m.Title, m.Id, db.Error))
|
||||||
return 0, db.Error
|
return 0, db.Error
|
||||||
}
|
}
|
||||||
return m.Id, nil
|
return m.Id, nil
|
||||||
|
@ -80,7 +79,7 @@ func (m *Message) Delete() error {
|
||||||
func (m *Message) Update() (*Message, error) {
|
func (m *Message) Update() (*Message, error) {
|
||||||
db := messagesDb().Update(m)
|
db := messagesDb().Update(m)
|
||||||
if db.Error != nil {
|
if db.Error != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Failed to update message %v #%v: %v", m.Title, m.Id, db.Error))
|
log.Errorln(fmt.Sprintf("Failed to update message %v #%v: %v", m.Title, m.Id, db.Error))
|
||||||
return nil, db.Error
|
return nil, db.Error
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
|
|
|
@ -54,7 +54,9 @@ sendMessages:
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if isType(comm, new(BasicEvents)) && isEnabled(comm) && (s.Online || inLimits(comm)) {
|
if isType(comm, new(BasicEvents)) && isEnabled(comm) && (s.Online || inLimits(comm)) {
|
||||||
notifier := comm.(Notifier).Select()
|
notifier := comm.(Notifier).Select()
|
||||||
utils.Log(1, fmt.Sprintf("Sending [OnFailure] '%v' notification for service %v", notifier.Method, s.Name))
|
log.
|
||||||
|
WithField("trigger", "OnFailure").
|
||||||
|
WithFields(utils.ToFields(notifier, s)).Infoln(fmt.Sprintf("Sending [OnFailure] '%v' notification for service %v", notifier.Method, s.Name))
|
||||||
comm.(BasicEvents).OnFailure(s, f)
|
comm.(BasicEvents).OnFailure(s, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -74,7 +76,9 @@ func OnSuccess(s *types.Service) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if isType(comm, new(BasicEvents)) && isEnabled(comm) && (!s.Online || inLimits(comm)) {
|
if isType(comm, new(BasicEvents)) && isEnabled(comm) && (!s.Online || inLimits(comm)) {
|
||||||
notifier := comm.(Notifier).Select()
|
notifier := comm.(Notifier).Select()
|
||||||
utils.Log(1, fmt.Sprintf("Sending [OnSuccess] '%v' notification for service %v", notifier.Method, s.Name))
|
log.
|
||||||
|
WithField("trigger", "OnSuccess").
|
||||||
|
WithFields(utils.ToFields(notifier, s)).Infoln(fmt.Sprintf("Sending [OnSuccess] '%v' notification for service %v", notifier.Method, s.Name))
|
||||||
comm.(BasicEvents).OnSuccess(s)
|
comm.(BasicEvents).OnSuccess(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,7 +88,9 @@ func OnSuccess(s *types.Service) {
|
||||||
func OnNewService(s *types.Service) {
|
func OnNewService(s *types.Service) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if isType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
|
if isType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||||
utils.Log(1, fmt.Sprintf("Sending new service notification for service %v", s.Name))
|
log.
|
||||||
|
WithField("trigger", "OnNewService").
|
||||||
|
Infoln(fmt.Sprintf("Sending new service notification for service %v", s.Name))
|
||||||
comm.(ServiceEvents).OnNewService(s)
|
comm.(ServiceEvents).OnNewService(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +103,7 @@ func OnUpdatedService(s *types.Service) {
|
||||||
}
|
}
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if isType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
|
if isType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||||
utils.Log(1, fmt.Sprintf("Sending updated service notification for service %v", s.Name))
|
log.Infoln(fmt.Sprintf("Sending updated service notification for service %v", s.Name))
|
||||||
comm.(ServiceEvents).OnUpdatedService(s)
|
comm.(ServiceEvents).OnUpdatedService(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -110,7 +116,7 @@ func OnDeletedService(s *types.Service) {
|
||||||
}
|
}
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if isType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
|
if isType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||||
utils.Log(1, fmt.Sprintf("Sending deleted service notification for service %v", s.Name))
|
log.Infoln(fmt.Sprintf("Sending deleted service notification for service %v", s.Name))
|
||||||
comm.(ServiceEvents).OnDeletedService(s)
|
comm.(ServiceEvents).OnDeletedService(s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,7 +126,7 @@ func OnDeletedService(s *types.Service) {
|
||||||
func OnNewUser(u *types.User) {
|
func OnNewUser(u *types.User) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if isType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
|
if isType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||||
utils.Log(1, fmt.Sprintf("Sending new user notification for user %v", u.Username))
|
log.Infoln(fmt.Sprintf("Sending new user notification for user %v", u.Username))
|
||||||
comm.(UserEvents).OnNewUser(u)
|
comm.(UserEvents).OnNewUser(u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -130,7 +136,7 @@ func OnNewUser(u *types.User) {
|
||||||
func OnUpdatedUser(u *types.User) {
|
func OnUpdatedUser(u *types.User) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if isType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
|
if isType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||||
utils.Log(1, fmt.Sprintf("Sending updated user notification for user %v", u.Username))
|
log.Infoln(fmt.Sprintf("Sending updated user notification for user %v", u.Username))
|
||||||
comm.(UserEvents).OnUpdatedUser(u)
|
comm.(UserEvents).OnUpdatedUser(u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +146,7 @@ func OnUpdatedUser(u *types.User) {
|
||||||
func OnDeletedUser(u *types.User) {
|
func OnDeletedUser(u *types.User) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if isType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
|
if isType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||||
utils.Log(1, fmt.Sprintf("Sending deleted user notification for user %v", u.Username))
|
log.Infoln(fmt.Sprintf("Sending deleted user notification for user %v", u.Username))
|
||||||
comm.(UserEvents).OnDeletedUser(u)
|
comm.(UserEvents).OnDeletedUser(u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -150,7 +156,7 @@ func OnDeletedUser(u *types.User) {
|
||||||
func OnUpdatedCore(c *types.Core) {
|
func OnUpdatedCore(c *types.Core) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if isType(comm, new(CoreEvents)) && isEnabled(comm) && inLimits(comm) {
|
if isType(comm, new(CoreEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||||
utils.Log(1, fmt.Sprintf("Sending updated core notification"))
|
log.Infoln(fmt.Sprintf("Sending updated core notification"))
|
||||||
comm.(CoreEvents).OnUpdatedCore(c)
|
comm.(CoreEvents).OnUpdatedCore(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,7 +184,7 @@ func OnNewNotifier(n *Notification) {
|
||||||
func OnUpdatedNotifier(n *Notification) {
|
func OnUpdatedNotifier(n *Notification) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if isType(comm, new(NotifierEvents)) && isEnabled(comm) && inLimits(comm) {
|
if isType(comm, new(NotifierEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||||
utils.Log(1, fmt.Sprintf("Sending updated notifier for %v", n.Id))
|
log.Infoln(fmt.Sprintf("Sending updated notifier for %v", n.Id))
|
||||||
comm.(NotifierEvents).OnUpdatedNotifier(n)
|
comm.(NotifierEvents).OnUpdatedNotifier(n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -233,7 +233,6 @@ func ExampleNotification_OnSuccess() {
|
||||||
example.AddQueue("example", msg)
|
example.AddQueue("example", msg)
|
||||||
fmt.Println(len(example.Queue))
|
fmt.Println(len(example.Queue))
|
||||||
// Output:
|
// Output:
|
||||||
// INFO: Notifier 'Example' added new item (example) to the queue. (1 queued)
|
|
||||||
// 1
|
// 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,7 +269,6 @@ func ExampleNotification_AddQueue() {
|
||||||
queue := example.Queue
|
queue := example.Queue
|
||||||
fmt.Printf("Example has %v items in the queue", len(queue))
|
fmt.Printf("Example has %v items in the queue", len(queue))
|
||||||
// Output:
|
// Output:
|
||||||
// INFO: Notifier 'Example' added new item (example) to the queue. (2 queued)
|
|
||||||
// Example has 2 items in the queue
|
// Example has 2 items in the queue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ var (
|
||||||
// db holds the Statping database connection
|
// db holds the Statping database connection
|
||||||
db *gorm.DB
|
db *gorm.DB
|
||||||
timezone float32
|
timezone float32
|
||||||
|
log = utils.Log.WithField("type", "notifier")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Notification contains all the fields for a Statping Notifier.
|
// Notification contains all the fields for a Statping Notifier.
|
||||||
|
@ -102,7 +103,7 @@ func (n *Notification) AfterFind() (err error) {
|
||||||
func (n *Notification) AddQueue(uid string, msg interface{}) {
|
func (n *Notification) AddQueue(uid string, msg interface{}) {
|
||||||
data := &QueueData{uid, msg}
|
data := &QueueData{uid, msg}
|
||||||
n.Queue = append(n.Queue, data)
|
n.Queue = append(n.Queue, data)
|
||||||
utils.Log(1, fmt.Sprintf("Notifier '%v' added new item (%v) to the queue. (%v queued)", n.Method, uid, len(n.Queue)))
|
log.WithFields(utils.ToFields(data, n)).Infoln(fmt.Sprintf("Notifier '%v' added new item (%v) to the queue. (%v queued)", n.Method, uid, len(n.Queue)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// CanTest returns true if the notifier implements the OnTest interface
|
// CanTest returns true if the notifier implements the OnTest interface
|
||||||
|
@ -169,8 +170,8 @@ func normalizeType(ty interface{}) string {
|
||||||
func (n *Notification) makeLog(msg interface{}) {
|
func (n *Notification) makeLog(msg interface{}) {
|
||||||
log := &NotificationLog{
|
log := &NotificationLog{
|
||||||
Message: normalizeType(msg),
|
Message: normalizeType(msg),
|
||||||
Time: utils.Timestamp(time.Now()),
|
Time: utils.Timestamp(utils.Now()),
|
||||||
Timestamp: time.Now(),
|
Timestamp: utils.Now(),
|
||||||
}
|
}
|
||||||
n.logs = append(n.logs, log)
|
n.logs = append(n.logs, log)
|
||||||
}
|
}
|
||||||
|
@ -290,9 +291,9 @@ CheckNotifier:
|
||||||
msg := notification.Queue[0]
|
msg := notification.Queue[0]
|
||||||
err := n.Send(msg.Data)
|
err := n.Send(msg.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(2, fmt.Sprintf("Notifier '%v' had an error: %v", notification.Method, err))
|
log.WithFields(utils.ToFields(notification, msg)).Warnln(fmt.Sprintf("Notifier '%v' had an error: %v", notification.Method, err))
|
||||||
} else {
|
} else {
|
||||||
utils.Log(1, fmt.Sprintf("Notifier '%v' sent outgoing message (%v) %v left in queue.", notification.Method, msg.Id, len(notification.Queue)))
|
log.WithFields(utils.ToFields(notification, msg)).Infoln(fmt.Sprintf("Notifier '%v' sent outgoing message (%v) %v left in queue.", notification.Method, msg.Id, len(notification.Queue)))
|
||||||
}
|
}
|
||||||
notification.makeLog(msg.Data)
|
notification.makeLog(msg.Data)
|
||||||
if len(notification.Queue) > 1 {
|
if len(notification.Queue) > 1 {
|
||||||
|
@ -311,10 +312,13 @@ CheckNotifier:
|
||||||
// install will check the database for the notification, if its not inserted it will insert a new record for it
|
// install will check the database for the notification, if its not inserted it will insert a new record for it
|
||||||
func install(n Notifier) error {
|
func install(n Notifier) error {
|
||||||
inDb := isInDatabase(n)
|
inDb := isInDatabase(n)
|
||||||
|
log.WithField("installed", inDb).
|
||||||
|
WithFields(utils.ToFields(n)).
|
||||||
|
Debugln(fmt.Sprintf("Checking if notifier '%v' is installed: %v", n.Select().Method, inDb))
|
||||||
if !inDb {
|
if !inDb {
|
||||||
_, err := insertDatabase(n)
|
_, err := insertDatabase(n)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, err)
|
log.Errorln(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -333,13 +337,13 @@ func (n *Notification) LastSent() time.Duration {
|
||||||
|
|
||||||
// SentLastHour returns the total amount of notifications sent in last 1 hour
|
// SentLastHour returns the total amount of notifications sent in last 1 hour
|
||||||
func (n *Notification) SentLastHour() int {
|
func (n *Notification) SentLastHour() int {
|
||||||
since := time.Now().Add(-1 * time.Hour)
|
since := utils.Now().Add(-1 * time.Hour)
|
||||||
return n.SentLast(since)
|
return n.SentLast(since)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SentLastMinute returns the total amount of notifications sent in last 1 minute
|
// SentLastMinute returns the total amount of notifications sent in last 1 minute
|
||||||
func (n *Notification) SentLastMinute() int {
|
func (n *Notification) SentLastMinute() int {
|
||||||
since := time.Now().Add(-1 * time.Minute)
|
since := utils.Now().Add(-1 * time.Minute)
|
||||||
return n.SentLast(since)
|
return n.SentLast(since)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -475,5 +479,5 @@ var ExampleService = &types.Service{
|
||||||
LastStatusCode: 404,
|
LastStatusCode: 404,
|
||||||
Expected: types.NewNullString("test example"),
|
Expected: types.NewNullString("test example"),
|
||||||
LastResponse: "<html>this is an example response</html>",
|
LastResponse: "<html>this is an example response</html>",
|
||||||
CreatedAt: time.Now().Add(-24 * time.Hour),
|
CreatedAt: utils.Now().Add(-24 * time.Hour),
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,7 +29,7 @@ var (
|
||||||
|
|
||||||
// InsertSampleData will create the example/dummy services for a brand new Statping installation
|
// InsertSampleData will create the example/dummy services for a brand new Statping installation
|
||||||
func InsertSampleData() error {
|
func InsertSampleData() error {
|
||||||
utils.Log(1, "Inserting Sample Data...")
|
log.Infoln("Inserting Sample Data...")
|
||||||
|
|
||||||
insertSampleGroups()
|
insertSampleGroups()
|
||||||
createdOn := time.Now().Add(((-24 * 30) * 3) * time.Hour).UTC()
|
createdOn := time.Now().Add(((-24 * 30) * 3) * time.Hour).UTC()
|
||||||
|
@ -113,7 +113,7 @@ func InsertSampleData() error {
|
||||||
|
|
||||||
insertSampleIncidents()
|
insertSampleIncidents()
|
||||||
|
|
||||||
utils.Log(1, "Sample data has finished importing")
|
log.Infoln("Sample data has finished importing")
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -211,7 +211,7 @@ func InsertSampleHits() error {
|
||||||
service := SelectService(i)
|
service := SelectService(i)
|
||||||
seed := time.Now().UnixNano()
|
seed := time.Now().UnixNano()
|
||||||
|
|
||||||
utils.Log(1, fmt.Sprintf("Adding %v sample hit records to service %v", SampleHits, service.Name))
|
log.Infoln(fmt.Sprintf("Adding %v sample hit records to service %v", SampleHits, service.Name))
|
||||||
createdAt := sampleStart
|
createdAt := sampleStart
|
||||||
|
|
||||||
p := utils.NewPerlin(2., 2., 10, seed)
|
p := utils.NewPerlin(2., 2., 10, seed)
|
||||||
|
@ -455,17 +455,17 @@ func InsertLargeSampleData() error {
|
||||||
func insertFailureRecords(since time.Time, amount int64) {
|
func insertFailureRecords(since time.Time, amount int64) {
|
||||||
for i := int64(14); i <= 15; i++ {
|
for i := int64(14); i <= 15; i++ {
|
||||||
service := SelectService(i)
|
service := SelectService(i)
|
||||||
utils.Log(1, fmt.Sprintf("Adding %v Failure records to service %v", amount, service.Name))
|
log.Infoln(fmt.Sprintf("Adding %v Failure records to service %v", amount, service.Name))
|
||||||
createdAt := since
|
createdAt := since
|
||||||
|
|
||||||
for fi := int64(1); fi <= amount; fi++ {
|
for fi := int64(1); fi <= amount; fi++ {
|
||||||
createdAt = createdAt.Add(2 * time.Minute)
|
createdAt = createdAt.Add(2 * time.Minute)
|
||||||
|
|
||||||
failure := &Failure{&types.Failure{
|
failure := &types.Failure{
|
||||||
Service: service.Id,
|
Service: service.Id,
|
||||||
Issue: "testing right here",
|
Issue: "testing right here",
|
||||||
CreatedAt: createdAt,
|
CreatedAt: createdAt,
|
||||||
}}
|
}
|
||||||
|
|
||||||
service.CreateFailure(failure)
|
service.CreateFailure(failure)
|
||||||
}
|
}
|
||||||
|
@ -476,7 +476,7 @@ func insertFailureRecords(since time.Time, amount int64) {
|
||||||
func insertHitRecords(since time.Time, amount int64) {
|
func insertHitRecords(since time.Time, amount int64) {
|
||||||
for i := int64(1); i <= 15; i++ {
|
for i := int64(1); i <= 15; i++ {
|
||||||
service := SelectService(i)
|
service := SelectService(i)
|
||||||
utils.Log(1, fmt.Sprintf("Adding %v hit records to service %v", amount, service.Name))
|
log.Infoln(fmt.Sprintf("Adding %v hit records to service %v", amount, service.Name))
|
||||||
createdAt := since
|
createdAt := since
|
||||||
p := utils.NewPerlin(2, 2, 5, time.Now().UnixNano())
|
p := utils.NewPerlin(2, 2, 5, time.Now().UnixNano())
|
||||||
for hi := int64(1); hi <= amount; hi++ {
|
for hi := int64(1); hi <= amount; hi++ {
|
||||||
|
|
|
@ -101,7 +101,7 @@ func (c *Core) SelectAllServices(start bool) ([]*Service, error) {
|
||||||
var services []*Service
|
var services []*Service
|
||||||
db := servicesDB().Find(&services).Order("order_id desc")
|
db := servicesDB().Find(&services).Order("order_id desc")
|
||||||
if db.Error != nil {
|
if db.Error != nil {
|
||||||
utils.Log(3, fmt.Sprintf("service error: %v", db.Error))
|
log.Errorln(fmt.Sprintf("service error: %v", db.Error))
|
||||||
return nil, db.Error
|
return nil, db.Error
|
||||||
}
|
}
|
||||||
CoreApp.Services = nil
|
CoreApp.Services = nil
|
||||||
|
@ -272,7 +272,7 @@ func GraphDataRaw(service types.ServiceInterface, start, end time.Time, group st
|
||||||
model = model.Order("timeframe asc", false).Group("timeframe")
|
model = model.Order("timeframe asc", false).Group("timeframe")
|
||||||
rows, err := model.Rows()
|
rows, err := model.Rows()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Errorf("issue fetching service chart data: %v", err))
|
log.Errorln(fmt.Errorf("issue fetching service chart data: %v", err))
|
||||||
}
|
}
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var gd DateScan
|
var gd DateScan
|
||||||
|
@ -284,7 +284,7 @@ func GraphDataRaw(service types.ServiceInterface, start, end time.Time, group st
|
||||||
if CoreApp.DbConnection == "postgres" {
|
if CoreApp.DbConnection == "postgres" {
|
||||||
createdTime, err = time.Parse(types.TIME_NANO, createdAt)
|
createdTime, err = time.Parse(types.TIME_NANO, createdAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, fmt.Errorf("issue parsing time from database: %v to %v", createdAt, types.TIME_NANO))
|
log.Errorln(fmt.Errorf("issue parsing time from database: %v to %v", createdAt, types.TIME_NANO))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
createdTime, err = time.Parse(types.TIME, createdAt)
|
createdTime, err = time.Parse(types.TIME, createdAt)
|
||||||
|
@ -301,7 +301,7 @@ func GraphDataRaw(service types.ServiceInterface, start, end time.Time, group st
|
||||||
func (d *DateScanObj) ToString() string {
|
func (d *DateScanObj) ToString() string {
|
||||||
data, err := json.Marshal(d.Array)
|
data, err := json.Marshal(d.Array)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(2, err)
|
log.Warnln(err)
|
||||||
return "{}"
|
return "{}"
|
||||||
}
|
}
|
||||||
return string(data)
|
return string(data)
|
||||||
|
@ -371,7 +371,7 @@ func (s *Service) Delete() error {
|
||||||
i := s.index()
|
i := s.index()
|
||||||
err := servicesDB().Delete(s)
|
err := servicesDB().Delete(s)
|
||||||
if err.Error != nil {
|
if err.Error != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Failed to delete service %v. %v", s.Name, err.Error))
|
log.Errorln(fmt.Sprintf("Failed to delete service %v. %v", s.Name, err.Error))
|
||||||
return err.Error
|
return err.Error
|
||||||
}
|
}
|
||||||
s.Close()
|
s.Close()
|
||||||
|
@ -386,7 +386,7 @@ func (s *Service) Delete() error {
|
||||||
func (s *Service) Update(restart bool) error {
|
func (s *Service) Update(restart bool) error {
|
||||||
err := servicesDB().Update(&s)
|
err := servicesDB().Update(&s)
|
||||||
if err.Error != nil {
|
if err.Error != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Failed to update service %v. %v", s.Name, err))
|
log.Errorln(fmt.Sprintf("Failed to update service %v. %v", s.Name, err))
|
||||||
return err.Error
|
return err.Error
|
||||||
}
|
}
|
||||||
// clear the notification queue for a service
|
// clear the notification queue for a service
|
||||||
|
@ -413,7 +413,7 @@ func (s *Service) Create(check bool) (int64, error) {
|
||||||
s.CreatedAt = time.Now()
|
s.CreatedAt = time.Now()
|
||||||
db := servicesDB().Create(s)
|
db := servicesDB().Create(s)
|
||||||
if db.Error != nil {
|
if db.Error != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Failed to create service %v #%v: %v", s.Name, s.Id, db.Error))
|
log.Errorln(fmt.Sprintf("Failed to create service %v #%v: %v", s.Name, s.Id, db.Error))
|
||||||
return 0, db.Error
|
return 0, db.Error
|
||||||
}
|
}
|
||||||
s.Start()
|
s.Start()
|
||||||
|
|
|
@ -17,6 +17,7 @@ package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/hunterlong/statping/types"
|
"github.com/hunterlong/statping/types"
|
||||||
|
"github.com/hunterlong/statping/utils"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -24,7 +25,6 @@ import (
|
||||||
|
|
||||||
var (
|
var (
|
||||||
newServiceId int64
|
newServiceId int64
|
||||||
newGroupId int64
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSelectHTTPService(t *testing.T) {
|
func TestSelectHTTPService(t *testing.T) {
|
||||||
|
@ -118,7 +118,7 @@ func TestCheckTCPService(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceOnline24Hours(t *testing.T) {
|
func TestServiceOnline24Hours(t *testing.T) {
|
||||||
since := time.Now().Add(-24 * time.Hour).Add(-10 * time.Minute)
|
since := utils.Now().Add(-24 * time.Hour).Add(-10 * time.Minute)
|
||||||
service := SelectService(1)
|
service := SelectService(1)
|
||||||
assert.Equal(t, float32(100), service.OnlineSince(since))
|
assert.Equal(t, float32(100), service.OnlineSince(since))
|
||||||
service2 := SelectService(5)
|
service2 := SelectService(5)
|
||||||
|
@ -134,7 +134,7 @@ func TestServiceSmallText(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServiceAvgUptime(t *testing.T) {
|
func TestServiceAvgUptime(t *testing.T) {
|
||||||
since := time.Now().Add(-24 * time.Hour).Add(-10 * time.Minute)
|
since := utils.Now().Add(-24 * time.Hour).Add(-10 * time.Minute)
|
||||||
service := SelectService(1)
|
service := SelectService(1)
|
||||||
assert.NotEqual(t, "0.00", service.AvgUptime(since))
|
assert.NotEqual(t, "0.00", service.AvgUptime(since))
|
||||||
service2 := SelectService(5)
|
service2 := SelectService(5)
|
||||||
|
@ -256,10 +256,10 @@ func TestServiceFailedTCPCheck(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateServiceFailure(t *testing.T) {
|
func TestCreateServiceFailure(t *testing.T) {
|
||||||
fail := &Failure{&types.Failure{
|
fail := &types.Failure{
|
||||||
Issue: "This is not an issue, but it would container HTTP response errors.",
|
Issue: "This is not an issue, but it would container HTTP response errors.",
|
||||||
Method: "http",
|
Method: "http",
|
||||||
}}
|
}
|
||||||
service := SelectService(8)
|
service := SelectService(8)
|
||||||
id, err := service.CreateFailure(fail)
|
id, err := service.CreateFailure(fail)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
@ -398,7 +398,7 @@ func TestService_TotalFailures24(t *testing.T) {
|
||||||
|
|
||||||
func TestService_TotalFailuresOnDate(t *testing.T) {
|
func TestService_TotalFailuresOnDate(t *testing.T) {
|
||||||
t.SkipNow()
|
t.SkipNow()
|
||||||
ago := time.Now().UTC()
|
ago := utils.Now().UTC()
|
||||||
service := SelectService(8)
|
service := SelectService(8)
|
||||||
failures, err := service.TotalFailuresOnDate(ago)
|
failures, err := service.TotalFailuresOnDate(ago)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
|
|
@ -77,7 +77,7 @@ func (u *User) Create() (int64, error) {
|
||||||
return 0, db.Error
|
return 0, db.Error
|
||||||
}
|
}
|
||||||
if u.Id == 0 {
|
if u.Id == 0 {
|
||||||
utils.Log(3, fmt.Sprintf("Failed to create User %v. %v", u.Username, db.Error))
|
log.Errorln(fmt.Sprintf("Failed to create User %v. %v", u.Username, db.Error))
|
||||||
return 0, db.Error
|
return 0, db.Error
|
||||||
}
|
}
|
||||||
return u.Id, db.Error
|
return u.Id, db.Error
|
||||||
|
@ -88,7 +88,7 @@ func SelectAllUsers() ([]*User, error) {
|
||||||
var users []*User
|
var users []*User
|
||||||
db := usersDB().Find(&users)
|
db := usersDB().Find(&users)
|
||||||
if db.Error != nil {
|
if db.Error != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Failed to load all users. %v", db.Error))
|
log.Errorln(fmt.Sprintf("Failed to load all users. %v", db.Error))
|
||||||
return nil, db.Error
|
return nil, db.Error
|
||||||
}
|
}
|
||||||
return users, db.Error
|
return users, db.Error
|
||||||
|
@ -99,7 +99,7 @@ func SelectAllUsers() ([]*User, error) {
|
||||||
func AuthUser(username, password string) (*User, bool) {
|
func AuthUser(username, password string) (*User, bool) {
|
||||||
user, err := SelectUsername(username)
|
user, err := SelectUsername(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(2, fmt.Errorf("user %v not found", username))
|
log.Warnln(fmt.Errorf("user %v not found", username))
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
if CheckHash(password, user.Password) {
|
if CheckHash(password, user.Password) {
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -1,5 +1,5 @@
|
||||||
FROM golang:1.11-alpine as base
|
FROM golang:1.13-alpine as base
|
||||||
MAINTAINER "Hunter Long (https://github.com/hunterlong)"
|
LABEL maintainer="Hunter Long (https://github.com/hunterlong)"
|
||||||
ARG VERSION
|
ARG VERSION
|
||||||
ENV DEP_VERSION v0.5.0
|
ENV DEP_VERSION v0.5.0
|
||||||
RUN apk add --no-cache libstdc++ gcc g++ make git ca-certificates linux-headers wget curl jq
|
RUN apk add --no-cache libstdc++ gcc g++ make git ca-certificates linux-headers wget curl jq
|
||||||
|
|
|
@ -2496,7 +2496,7 @@ for application logging
|
||||||
```go
|
```go
|
||||||
func Log(level int, err interface{}) error
|
func Log(level int, err interface{}) error
|
||||||
```
|
```
|
||||||
Log creates a new entry in the Logger. Log has 1-5 levels depending on how
|
Log creates a new entry in the utils.Log. Log has 1-5 levels depending on how
|
||||||
critical the log/error is
|
critical the log/error is
|
||||||
|
|
||||||
#### func NewSHA1Hash
|
#### func NewSHA1Hash
|
||||||
|
|
4
doc.go
4
doc.go
|
@ -1,4 +1,4 @@
|
||||||
// Package statping is a server monitoring application that includs a status page server. Visit the Statping repo at
|
// Package statping is a server monitoring application that includes a status page server. Visit the Statping repo at
|
||||||
// https://github.com/hunterlong/statping to get a full understanding of what this application can do.
|
// https://github.com/hunterlong/statping to get a full understanding of what this application can do.
|
||||||
//
|
//
|
||||||
// Install Statping
|
// Install Statping
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
// brew install statping
|
// brew install statping
|
||||||
//
|
//
|
||||||
// // Linux installation
|
// // Linux installation
|
||||||
// bash <(curl -s https://assets.statping.com/install.sh)
|
// curl -o- -L https://statping.com/install.sh | bash
|
||||||
// statping version
|
// statping version
|
||||||
//
|
//
|
||||||
// Docker
|
// Docker
|
||||||
|
|
2
go.mod
2
go.mod
|
@ -8,6 +8,7 @@ require (
|
||||||
github.com/agnivade/levenshtein v1.0.2 // indirect
|
github.com/agnivade/levenshtein v1.0.2 // indirect
|
||||||
github.com/ararog/timeago v0.0.0-20160328174124-e9969cf18b8d
|
github.com/ararog/timeago v0.0.0-20160328174124-e9969cf18b8d
|
||||||
github.com/daaku/go.zipexe v1.0.1 // indirect
|
github.com/daaku/go.zipexe v1.0.1 // indirect
|
||||||
|
github.com/fatih/structs v1.1.0
|
||||||
github.com/go-mail/mail v2.3.1+incompatible
|
github.com/go-mail/mail v2.3.1+incompatible
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible
|
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||||
github.com/gorilla/mux v1.7.3
|
github.com/gorilla/mux v1.7.3
|
||||||
|
@ -20,6 +21,7 @@ require (
|
||||||
github.com/rendon/testcli v0.0.0-20161027181003-6283090d169f
|
github.com/rendon/testcli v0.0.0-20161027181003-6283090d169f
|
||||||
github.com/russross/blackfriday/v2 v2.0.1
|
github.com/russross/blackfriday/v2 v2.0.1
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||||
|
github.com/sirupsen/logrus v1.2.0
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.4.0
|
||||||
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
|
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
|
||||||
github.com/vektah/gqlparser v1.1.2
|
github.com/vektah/gqlparser v1.1.2
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -39,6 +39,8 @@ github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1
|
||||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5 h1:Yzb9+7DPaBjB8zlTR87/ElzFsnQfuHnVUVqpZZIcV5Y=
|
||||||
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0=
|
||||||
|
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||||
|
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||||
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
|
@ -95,6 +97,7 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||||
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||||
|
@ -144,6 +147,7 @@ github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJ
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw=
|
||||||
|
github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
|
|
@ -58,7 +58,7 @@ func apiClearCacheHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendErrorJson(err error, w http.ResponseWriter, r *http.Request) {
|
func sendErrorJson(err error, w http.ResponseWriter, r *http.Request) {
|
||||||
utils.Log(2, fmt.Errorf("sending error response for %v: %v", r.URL.String(), err.Error()))
|
log.Warnln(fmt.Errorf("sending error response for %v: %v", r.URL.String(), err.Error()))
|
||||||
output := apiResponse{
|
output := apiResponse{
|
||||||
Status: "error",
|
Status: "error",
|
||||||
Error: err.Error(),
|
Error: err.Error(),
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hunterlong/statping/utils"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -25,7 +26,7 @@ func (item Item) Expired() bool {
|
||||||
if item.Expiration == 0 {
|
if item.Expiration == 0 {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return time.Now().UnixNano() > item.Expiration
|
return utils.Now().UnixNano() > item.Expiration
|
||||||
}
|
}
|
||||||
|
|
||||||
//Storage mecanism for caching strings in memory
|
//Storage mecanism for caching strings in memory
|
||||||
|
@ -68,6 +69,6 @@ func (s Storage) Set(key string, content []byte, duration time.Duration) {
|
||||||
defer s.mu.Unlock()
|
defer s.mu.Unlock()
|
||||||
s.items[key] = Item{
|
s.items[key] = Item{
|
||||||
Content: content,
|
Content: content,
|
||||||
Expiration: time.Now().Add(duration).UnixNano(),
|
Expiration: utils.Now().Add(duration).UnixNano(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"github.com/hunterlong/statping/utils"
|
"github.com/hunterlong/statping/utils"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func apiAllCheckinsHandler(w http.ResponseWriter, r *http.Request) {
|
func apiAllCheckinsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -81,7 +80,7 @@ func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
hit := &types.CheckinHit{
|
hit := &types.CheckinHit{
|
||||||
Checkin: checkin.Id,
|
Checkin: checkin.Id,
|
||||||
From: ip,
|
From: ip,
|
||||||
CreatedAt: time.Now().UTC(),
|
CreatedAt: utils.Now().UTC(),
|
||||||
}
|
}
|
||||||
checkinHit := core.ReturnCheckinHit(hit)
|
checkinHit := core.ReturnCheckinHit(hit)
|
||||||
if checkin.Last() == nil {
|
if checkin.Last() == nil {
|
||||||
|
@ -95,7 +94,7 @@ func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
checkin.Failing = false
|
checkin.Failing = false
|
||||||
checkin.LastHit = utils.Timezoner(time.Now().UTC(), core.CoreApp.Timezone)
|
checkin.LastHit = utils.Timezoner(utils.Now().UTC(), core.CoreApp.Timezone)
|
||||||
sendJsonAction(checkinHit, "update", w, r)
|
sendJsonAction(checkinHit, "update", w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ import (
|
||||||
"github.com/hunterlong/statping/utils"
|
"github.com/hunterlong/statping/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func dashboardHandler(w http.ResponseWriter, r *http.Request) {
|
func dashboardHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -50,7 +49,7 @@ func loginHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
session.Values["user_id"] = user.Id
|
session.Values["user_id"] = user.Id
|
||||||
session.Values["admin"] = user.Admin.Bool
|
session.Values["admin"] = user.Admin.Bool
|
||||||
session.Save(r, w)
|
session.Save(r, w)
|
||||||
utils.Log(1, fmt.Sprintf("User %v logged in from IP %v", user.Username, r.RemoteAddr))
|
log.Infoln(fmt.Sprintf("User %v logged in from IP %v", user.Username, r.RemoteAddr))
|
||||||
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
http.Redirect(w, r, "/dashboard", http.StatusSeeOther)
|
||||||
} else {
|
} else {
|
||||||
err := core.ErrorResponse{Error: "Incorrect login information submitted, try again."}
|
err := core.ErrorResponse{Error: "Incorrect login information submitted, try again."}
|
||||||
|
@ -115,6 +114,6 @@ func exportHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Length", strconv.Itoa(fileSize))
|
w.Header().Set("Content-Length", strconv.Itoa(fileSize))
|
||||||
w.Header().Set("Content-Control", "private, no-transform, no-store, must-revalidate")
|
w.Header().Set("Content-Control", "private, no-transform, no-store, must-revalidate")
|
||||||
|
|
||||||
http.ServeContent(w, r, "export.json", time.Now(), bytes.NewReader(export))
|
http.ServeContent(w, r, "export.json", utils.Now(), bytes.NewReader(export))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,11 +56,11 @@ func RunHTTPServer(ip string, port int) error {
|
||||||
cert := utils.FileExists(utils.Directory + "/server.crt")
|
cert := utils.FileExists(utils.Directory + "/server.crt")
|
||||||
|
|
||||||
if key && cert {
|
if key && cert {
|
||||||
utils.Log(1, "server.cert and server.key was found in root directory! Starting in SSL mode.")
|
log.Infoln("server.cert and server.key was found in root directory! Starting in SSL mode.")
|
||||||
utils.Log(1, fmt.Sprintf("Statping Secure HTTPS Server running on https://%v:%v", ip, 443))
|
log.Infoln(fmt.Sprintf("Statping Secure HTTPS Server running on https://%v:%v", ip, 443))
|
||||||
usingSSL = true
|
usingSSL = true
|
||||||
} else {
|
} else {
|
||||||
utils.Log(1, "Statping HTTP Server running on http://"+host)
|
log.Infoln("Statping HTTP Server running on http://" + host)
|
||||||
}
|
}
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
|
@ -178,7 +178,7 @@ func loadTemplate(w http.ResponseWriter, r *http.Request) error {
|
||||||
mainTemplate.Funcs(handlerFuncs(w, r))
|
mainTemplate.Funcs(handlerFuncs(w, r))
|
||||||
mainTemplate, err = mainTemplate.Parse(mainTmpl)
|
mainTemplate, err = mainTemplate.Parse(mainTmpl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// render all templates
|
// render all templates
|
||||||
|
@ -187,7 +187,7 @@ func loadTemplate(w http.ResponseWriter, r *http.Request) error {
|
||||||
tmp, _ := source.TmplBox.String(temp)
|
tmp, _ := source.TmplBox.String(temp)
|
||||||
mainTemplate, err = mainTemplate.Parse(tmp)
|
mainTemplate, err = mainTemplate.Parse(tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,7 +196,7 @@ func loadTemplate(w http.ResponseWriter, r *http.Request) error {
|
||||||
tmp, _ := source.JsBox.String(temp)
|
tmp, _ := source.JsBox.String(temp)
|
||||||
mainTemplate, err = mainTemplate.Parse(tmp)
|
mainTemplate, err = mainTemplate.Parse(tmp)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -205,7 +205,6 @@ func loadTemplate(w http.ResponseWriter, r *http.Request) error {
|
||||||
|
|
||||||
// ExecuteResponse will render a HTTP response for the front end user
|
// ExecuteResponse will render a HTTP response for the front end user
|
||||||
func ExecuteResponse(w http.ResponseWriter, r *http.Request, file string, data interface{}, redirect interface{}) {
|
func ExecuteResponse(w http.ResponseWriter, r *http.Request, file string, data interface{}, redirect interface{}) {
|
||||||
utils.Http(r)
|
|
||||||
if url, ok := redirect.(string); ok {
|
if url, ok := redirect.(string); ok {
|
||||||
http.Redirect(w, r, url, http.StatusSeeOther)
|
http.Redirect(w, r, url, http.StatusSeeOther)
|
||||||
return
|
return
|
||||||
|
@ -216,15 +215,15 @@ func ExecuteResponse(w http.ResponseWriter, r *http.Request, file string, data i
|
||||||
loadTemplate(w, r)
|
loadTemplate(w, r)
|
||||||
render, err := source.TmplBox.String(file)
|
render, err := source.TmplBox.String(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
}
|
}
|
||||||
// render the page requested
|
// render the page requested
|
||||||
if _, err := mainTemplate.Parse(render); err != nil {
|
if _, err := mainTemplate.Parse(render); err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
}
|
}
|
||||||
// execute the template
|
// execute the template
|
||||||
if err := mainTemplate.Execute(w, data); err != nil {
|
if err := mainTemplate.Execute(w, data); err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,7 +231,7 @@ func ExecuteResponse(w http.ResponseWriter, r *http.Request, file string, data i
|
||||||
func executeJSResponse(w http.ResponseWriter, r *http.Request, file string, data interface{}) {
|
func executeJSResponse(w http.ResponseWriter, r *http.Request, file string, data interface{}) {
|
||||||
render, err := source.JsBox.String(file)
|
render, err := source.JsBox.String(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
}
|
}
|
||||||
if usingSSL {
|
if usingSSL {
|
||||||
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
|
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
|
||||||
|
@ -247,10 +246,10 @@ func executeJSResponse(w http.ResponseWriter, r *http.Request, file string, data
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if _, err := t.Parse(render); err != nil {
|
if _, err := t.Parse(render); err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
}
|
}
|
||||||
if err := t.Execute(w, data); err != nil {
|
if err := t.Execute(w, data); err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,31 @@
|
||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"github.com/hunterlong/statping/core"
|
"github.com/hunterlong/statping/core"
|
||||||
|
"github.com/hunterlong/statping/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// sendLog is a http middleware that will log the duration of request and other useful fields
|
||||||
|
func sendLog(handler func(w http.ResponseWriter, r *http.Request)) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
t1 := utils.Now()
|
||||||
|
handler(w, r)
|
||||||
|
t2 := utils.Now().Sub(t1)
|
||||||
|
log.WithFields(utils.ToFields(w, r)).
|
||||||
|
WithField("url", r.RequestURI).
|
||||||
|
WithField("method", r.Method).
|
||||||
|
WithField("load_micro_seconds", t2.Microseconds()).
|
||||||
|
Infoln(fmt.Sprintf("%v (%v) | IP: %v", r.RequestURI, r.Method, r.Host))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// authenticated is a middleware function to check if user is an Admin before running original request
|
// authenticated is a middleware function to check if user is an Admin before running original request
|
||||||
func authenticated(handler func(w http.ResponseWriter, r *http.Request), redirect bool) http.Handler {
|
func authenticated(handler func(w http.ResponseWriter, r *http.Request), redirect bool) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return sendLog(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if !IsFullAuthenticated(r) {
|
if !IsFullAuthenticated(r) {
|
||||||
if redirect {
|
if redirect {
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
@ -24,7 +40,7 @@ func authenticated(handler func(w http.ResponseWriter, r *http.Request), redirec
|
||||||
|
|
||||||
// readOnly is a middleware function to check if user is a User before running original request
|
// readOnly is a middleware function to check if user is a User before running original request
|
||||||
func readOnly(handler func(w http.ResponseWriter, r *http.Request), redirect bool) http.Handler {
|
func readOnly(handler func(w http.ResponseWriter, r *http.Request), redirect bool) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return sendLog(func(w http.ResponseWriter, r *http.Request) {
|
||||||
if !IsReadAuthenticated(r) {
|
if !IsReadAuthenticated(r) {
|
||||||
if redirect {
|
if redirect {
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
@ -39,7 +55,7 @@ func readOnly(handler func(w http.ResponseWriter, r *http.Request), redirect boo
|
||||||
|
|
||||||
// cached is a middleware function that accepts a duration and content type and will cache the response of the original request
|
// cached is a middleware function that accepts a duration and content type and will cache the response of the original request
|
||||||
func cached(duration, contentType string, handler func(w http.ResponseWriter, r *http.Request)) http.Handler {
|
func cached(duration, contentType string, handler func(w http.ResponseWriter, r *http.Request)) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return sendLog(func(w http.ResponseWriter, r *http.Request) {
|
||||||
content := CacheStorage.Get(r.RequestURI)
|
content := CacheStorage.Get(r.RequestURI)
|
||||||
w.Header().Set("Content-Type", contentType)
|
w.Header().Set("Content-Type", contentType)
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
|
|
|
@ -85,7 +85,7 @@ func testNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
fakeNotifer, notif, err := notifier.SelectNotifier(method)
|
fakeNotifer, notif, err := notifier.SelectNotifier(method)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Sprintf("issue saving notifier %v: %v", method, err))
|
log.Errorln(fmt.Sprintf("issue saving notifier %v: %v", method, err))
|
||||||
ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
|
ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,11 +25,11 @@ import (
|
||||||
"github.com/hunterlong/statping/source"
|
"github.com/hunterlong/statping/source"
|
||||||
"github.com/hunterlong/statping/utils"
|
"github.com/hunterlong/statping/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
router *mux.Router
|
router *mux.Router
|
||||||
|
log = utils.Log.WithField("type", "handlers")
|
||||||
)
|
)
|
||||||
|
|
||||||
// Router returns all of the routes used in Statping.
|
// Router returns all of the routes used in Statping.
|
||||||
|
@ -58,9 +58,9 @@ func Router() *mux.Router {
|
||||||
r.Handle("/charts.js", http.HandlerFunc(renderServiceChartsHandler))
|
r.Handle("/charts.js", 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", sendLog(dashboardHandler)).Methods("GET")
|
||||||
r.Handle("/dashboard", http.HandlerFunc(loginHandler)).Methods("POST")
|
r.Handle("/dashboard", sendLog(loginHandler)).Methods("POST")
|
||||||
r.Handle("/logout", http.HandlerFunc(logoutHandler))
|
r.Handle("/logout", sendLog(logoutHandler))
|
||||||
r.Handle("/plugins/download/{name}", authenticated(pluginsDownloadHandler, true))
|
r.Handle("/plugins/download/{name}", authenticated(pluginsDownloadHandler, true))
|
||||||
r.Handle("/plugins/{name}/save", authenticated(pluginSavedHandler, true)).Methods("POST")
|
r.Handle("/plugins/{name}/save", authenticated(pluginSavedHandler, true)).Methods("POST")
|
||||||
r.Handle("/help", authenticated(helpHandler, true))
|
r.Handle("/help", authenticated(helpHandler, true))
|
||||||
|
@ -90,11 +90,11 @@ func Router() *mux.Router {
|
||||||
// SERVICE Routes
|
// SERVICE Routes
|
||||||
r.Handle("/services", authenticated(servicesHandler, true)).Methods("GET")
|
r.Handle("/services", authenticated(servicesHandler, true)).Methods("GET")
|
||||||
r.Handle("/service/create", authenticated(createServiceHandler, true)).Methods("GET")
|
r.Handle("/service/create", authenticated(createServiceHandler, true)).Methods("GET")
|
||||||
r.Handle("/service/{id}", http.HandlerFunc(servicesViewHandler)).Methods("GET")
|
r.Handle("/service/{id}", sendLog(servicesViewHandler)).Methods("GET")
|
||||||
r.Handle("/service/{id}/edit", authenticated(servicesViewHandler, true)).Methods("GET")
|
r.Handle("/service/{id}/edit", authenticated(servicesViewHandler, true)).Methods("GET")
|
||||||
r.Handle("/service/{id}/delete_failures", authenticated(servicesDeleteFailuresHandler, true)).Methods("GET")
|
r.Handle("/service/{id}/delete_failures", authenticated(servicesDeleteFailuresHandler, true)).Methods("GET")
|
||||||
|
|
||||||
r.Handle("/group/{id}", http.HandlerFunc(groupViewHandler)).Methods("GET")
|
r.Handle("/group/{id}", sendLog(groupViewHandler)).Methods("GET")
|
||||||
|
|
||||||
// API GROUPS Routes
|
// API GROUPS Routes
|
||||||
r.Handle("/api/groups", readOnly(apiAllGroupHandler, false)).Methods("GET")
|
r.Handle("/api/groups", readOnly(apiAllGroupHandler, false)).Methods("GET")
|
||||||
|
@ -115,9 +115,9 @@ func Router() *mux.Router {
|
||||||
r.Handle("/api/services/{id}", readOnly(apiServiceHandler, false)).Methods("GET")
|
r.Handle("/api/services/{id}", readOnly(apiServiceHandler, false)).Methods("GET")
|
||||||
r.Handle("/api/reorder/services", authenticated(reorderServiceHandler, false)).Methods("POST")
|
r.Handle("/api/reorder/services", authenticated(reorderServiceHandler, false)).Methods("POST")
|
||||||
r.Handle("/api/services/{id}/running", authenticated(apiServiceRunningHandler, false)).Methods("POST")
|
r.Handle("/api/services/{id}/running", authenticated(apiServiceRunningHandler, false)).Methods("POST")
|
||||||
r.Handle("/api/services/{id}/data", cached("30s", "application/json", http.HandlerFunc(apiServiceDataHandler))).Methods("GET")
|
r.Handle("/api/services/{id}/data", cached("30s", "application/json", apiServiceDataHandler)).Methods("GET")
|
||||||
r.Handle("/api/services/{id}/ping", cached("30s", "application/json", http.HandlerFunc(apiServicePingDataHandler))).Methods("GET")
|
r.Handle("/api/services/{id}/ping", cached("30s", "application/json", apiServicePingDataHandler)).Methods("GET")
|
||||||
r.Handle("/api/services/{id}/heatmap", cached("30s", "application/json", http.HandlerFunc(apiServiceHeatmapHandler))).Methods("GET")
|
r.Handle("/api/services/{id}/heatmap", cached("30s", "application/json", apiServiceHeatmapHandler)).Methods("GET")
|
||||||
r.Handle("/api/services/{id}", authenticated(apiServiceUpdateHandler, false)).Methods("POST")
|
r.Handle("/api/services/{id}", authenticated(apiServiceUpdateHandler, false)).Methods("POST")
|
||||||
r.Handle("/api/services/{id}", authenticated(apiServiceDeleteHandler, false)).Methods("DELETE")
|
r.Handle("/api/services/{id}", authenticated(apiServiceDeleteHandler, false)).Methods("DELETE")
|
||||||
r.Handle("/api/services/{id}/failures", authenticated(apiServiceFailuresHandler, false)).Methods("GET")
|
r.Handle("/api/services/{id}/failures", authenticated(apiServiceFailuresHandler, false)).Methods("GET")
|
||||||
|
@ -152,7 +152,7 @@ func Router() *mux.Router {
|
||||||
r.Handle("/api/checkin/{api}", authenticated(apiCheckinHandler, false)).Methods("GET")
|
r.Handle("/api/checkin/{api}", authenticated(apiCheckinHandler, false)).Methods("GET")
|
||||||
r.Handle("/api/checkin", authenticated(checkinCreateHandler, false)).Methods("POST")
|
r.Handle("/api/checkin", authenticated(checkinCreateHandler, false)).Methods("POST")
|
||||||
r.Handle("/api/checkin/{api}", authenticated(checkinDeleteHandler, false)).Methods("DELETE")
|
r.Handle("/api/checkin/{api}", authenticated(checkinDeleteHandler, false)).Methods("DELETE")
|
||||||
r.Handle("/checkin/{api}", http.HandlerFunc(checkinHitHandler))
|
r.Handle("/checkin/{api}", sendLog(checkinHitHandler))
|
||||||
|
|
||||||
// Static Files Routes
|
// Static Files Routes
|
||||||
r.PathPrefix("/files/postman.json").Handler(http.StripPrefix("/files/", http.FileServer(source.TmplBox.HTTPBox())))
|
r.PathPrefix("/files/postman.json").Handler(http.StripPrefix("/files/", http.FileServer(source.TmplBox.HTTPBox())))
|
||||||
|
@ -161,10 +161,10 @@ func Router() *mux.Router {
|
||||||
|
|
||||||
// API Generic Routes
|
// API Generic Routes
|
||||||
r.Handle("/metrics", readOnly(prometheusHandler, false))
|
r.Handle("/metrics", readOnly(prometheusHandler, false))
|
||||||
r.Handle("/health", http.HandlerFunc(healthCheckHandler))
|
r.Handle("/health", sendLog(healthCheckHandler))
|
||||||
r.Handle("/.well-known/", http.StripPrefix("/.well-known/", http.FileServer(http.Dir(dir+"/.well-known"))))
|
r.Handle("/.well-known/", http.StripPrefix("/.well-known/", http.FileServer(http.Dir(dir+"/.well-known"))))
|
||||||
|
|
||||||
r.NotFoundHandler = http.HandlerFunc(error404Handler)
|
r.NotFoundHandler = sendLog(error404Handler)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,7 +175,7 @@ func resetRouter() {
|
||||||
|
|
||||||
func resetCookies() {
|
func resetCookies() {
|
||||||
if core.CoreApp != nil {
|
if core.CoreApp != nil {
|
||||||
cookie := fmt.Sprintf("%v_%v", core.CoreApp.ApiSecret, time.Now().Nanosecond())
|
cookie := fmt.Sprintf("%v_%v", core.CoreApp.ApiSecret, utils.Now().Nanosecond())
|
||||||
sessionStore = sessions.NewCookieStore([]byte(cookie))
|
sessionStore = sessions.NewCookieStore([]byte(cookie))
|
||||||
} else {
|
} else {
|
||||||
sessionStore = sessions.NewCookieStore([]byte("secretinfo"))
|
sessionStore = sessions.NewCookieStore([]byte("secretinfo"))
|
||||||
|
|
|
@ -33,8 +33,8 @@ func renderServiceChartsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
w.Header().Set("Content-Type", "text/javascript")
|
w.Header().Set("Content-Type", "text/javascript")
|
||||||
w.Header().Set("Cache-Control", "max-age=60")
|
w.Header().Set("Cache-Control", "max-age=60")
|
||||||
|
|
||||||
end := time.Now().UTC()
|
end := utils.Now().UTC()
|
||||||
start := time.Now().Add((-24 * 7) * time.Hour).UTC()
|
start := utils.Now().Add((-24 * 7) * time.Hour).UTC()
|
||||||
var srvs []*core.Service
|
var srvs []*core.Service
|
||||||
for _, s := range services {
|
for _, s := range services {
|
||||||
srvs = append(srvs, s.(*core.Service))
|
srvs = append(srvs, s.(*core.Service))
|
||||||
|
@ -94,7 +94,7 @@ func servicesViewHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
endField := utils.ToInt(fields.Get("end"))
|
endField := utils.ToInt(fields.Get("end"))
|
||||||
group := r.Form.Get("group")
|
group := r.Form.Get("group")
|
||||||
|
|
||||||
end := time.Now().UTC()
|
end := utils.Now().UTC()
|
||||||
start := end.Add((-24 * 7) * time.Hour).UTC()
|
start := end.Add((-24 * 7) * time.Hour).UTC()
|
||||||
|
|
||||||
if startField != 0 {
|
if startField != 0 {
|
||||||
|
@ -243,7 +243,7 @@ func apiServiceHeatmapHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var monthOutput []*dataXyMonth
|
var monthOutput []*dataXyMonth
|
||||||
|
|
||||||
start := service.CreatedAt
|
start := service.CreatedAt
|
||||||
//now := time.Now()
|
//now := utils.Now()
|
||||||
|
|
||||||
sY, sM, _ := start.Date()
|
sY, sM, _ := start.Date()
|
||||||
|
|
||||||
|
@ -252,10 +252,10 @@ func apiServiceHeatmapHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
month := int(sM)
|
month := int(sM)
|
||||||
maxMonth := 12
|
maxMonth := 12
|
||||||
|
|
||||||
for year := int(sY); year <= time.Now().Year(); year++ {
|
for year := int(sY); year <= utils.Now().Year(); year++ {
|
||||||
|
|
||||||
if year == time.Now().Year() {
|
if year == utils.Now().Year() {
|
||||||
maxMonth = int(time.Now().Month())
|
maxMonth = int(utils.Now().Month())
|
||||||
}
|
}
|
||||||
|
|
||||||
for m := month; m <= maxMonth; m++ {
|
for m := month; m <= maxMonth; m++ {
|
||||||
|
|
|
@ -67,10 +67,9 @@ func saveSettingsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
app.UseCdn = types.NewNullBool(form.Get("enable_cdn") == "on")
|
app.UseCdn = types.NewNullBool(form.Get("enable_cdn") == "on")
|
||||||
core.CoreApp, err = core.UpdateCore(app)
|
core.CoreApp, err = core.UpdateCore(app)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Sprintf("issue updating Core: %v", err.Error()))
|
log.Errorln(fmt.Sprintf("issue updating Core: %v", err.Error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//notifiers.OnSettingsSaved(core.CoreApp.ToCore())
|
//notifiers.OnSettingsSaved(core.CoreApp.ToCore())
|
||||||
ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
|
ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
|
||||||
}
|
}
|
||||||
|
@ -91,13 +90,13 @@ func saveSASSHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
func saveAssetsHandler(w http.ResponseWriter, r *http.Request) {
|
func saveAssetsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
dir := utils.Directory
|
dir := utils.Directory
|
||||||
if err := source.CreateAllAssets(dir); err != nil {
|
if err := source.CreateAllAssets(dir); err != nil {
|
||||||
utils.Log(3, err)
|
log.Errorln(err)
|
||||||
sendErrorJson(err, w, r)
|
sendErrorJson(err, w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if err := source.CompileSASS(dir); err != nil {
|
if err := source.CompileSASS(dir); err != nil {
|
||||||
source.CopyToPublic(source.CssBox, dir+"/assets/css", "base.css")
|
source.CopyToPublic(source.CssBox, dir+"/assets/css", "base.css")
|
||||||
utils.Log(3, "Default 'base.css' was inserted because SASS did not work.")
|
log.Errorln("Default 'base.css' was inserted because SASS did not work.")
|
||||||
}
|
}
|
||||||
resetRouter()
|
resetRouter()
|
||||||
ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
|
ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
|
||||||
|
@ -105,7 +104,7 @@ func saveAssetsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
func deleteAssetsHandler(w http.ResponseWriter, r *http.Request) {
|
func deleteAssetsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if err := source.DeleteAllAssets(utils.Directory); err != nil {
|
if err := source.DeleteAllAssets(utils.Directory); err != nil {
|
||||||
utils.Log(3, fmt.Errorf("error deleting all assets %v", err))
|
log.Errorln(fmt.Errorf("error deleting all assets %v", err))
|
||||||
}
|
}
|
||||||
resetRouter()
|
resetRouter()
|
||||||
ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
|
ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
|
||||||
|
@ -115,7 +114,7 @@ func bulkImportHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
var fileData bytes.Buffer
|
var fileData bytes.Buffer
|
||||||
file, _, err := r.FormFile("file")
|
file, _, err := r.FormFile("file")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Errorf("error bulk import services: %v", err))
|
log.Errorln(fmt.Errorf("error bulk import services: %v", err))
|
||||||
w.Write([]byte(err.Error()))
|
w.Write([]byte(err.Error()))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -129,17 +128,17 @@ func bulkImportHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
newService, err := commaToService(col)
|
newService, err := commaToService(col)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Errorf("issue with row %v: %v", i, err))
|
log.Errorln(fmt.Errorf("issue with row %v: %v", i, err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
service := core.ReturnService(newService)
|
service := core.ReturnService(newService)
|
||||||
_, err = service.Create(true)
|
_, err = service.Create(true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Errorf("cannot create service %v: %v", col[0], err))
|
log.Errorln(fmt.Errorf("cannot create service %v: %v", col[0], err))
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
utils.Log(1, fmt.Sprintf("Created new service %v", service.Name))
|
log.Infoln(fmt.Sprintf("Created new service %v", service.Name))
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
|
ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
|
||||||
|
|
|
@ -57,7 +57,7 @@ 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")
|
||||||
sample := r.PostForm.Get("sample_data") == "on"
|
sample := r.PostForm.Get("sample_data") == "on"
|
||||||
utils.Log(2, sample)
|
log.Warnln(sample)
|
||||||
dir := utils.Directory
|
dir := utils.Directory
|
||||||
|
|
||||||
config := &core.DbConfig{
|
config := &core.DbConfig{
|
||||||
|
@ -78,21 +78,21 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if core.Configs, err = config.Save(); err != nil {
|
if core.Configs, err = config.Save(); err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
config.Error = err
|
config.Error = err
|
||||||
setupResponseError(w, r, config)
|
setupResponseError(w, r, config)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if core.Configs, err = core.LoadConfigFile(dir); err != nil {
|
if core.Configs, err = core.LoadConfigFile(dir); err != nil {
|
||||||
utils.Log(3, err)
|
log.Errorln(err)
|
||||||
config.Error = err
|
config.Error = err
|
||||||
setupResponseError(w, r, config)
|
setupResponseError(w, r, config)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = core.Configs.Connect(false, dir); err != nil {
|
if err = core.Configs.Connect(false, dir); err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
core.DeleteConfig()
|
core.DeleteConfig()
|
||||||
config.Error = err
|
config.Error = err
|
||||||
setupResponseError(w, r, config)
|
setupResponseError(w, r, config)
|
||||||
|
@ -104,7 +104,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
||||||
core.CoreApp, err = config.InsertCore()
|
core.CoreApp, err = config.InsertCore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(4, err)
|
log.Errorln(err)
|
||||||
config.Error = err
|
config.Error = err
|
||||||
setupResponseError(w, r, config)
|
setupResponseError(w, r, config)
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,5 +0,0 @@
|
||||||
build:
|
|
||||||
docker:
|
|
||||||
web: Dockerfile
|
|
||||||
run:
|
|
||||||
web: statping -port $PORT
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
#!/bin/sh
|
||||||
|
# Statping installation script for Linux, Mac, and maybe Windows.
|
||||||
|
#
|
||||||
|
# This installation script is a modification of Yarn's installation
|
||||||
|
set -e
|
||||||
|
|
||||||
|
reset="\033[0m"
|
||||||
|
red="\033[31m"
|
||||||
|
green="\033[32m"
|
||||||
|
yellow="\033[33m"
|
||||||
|
cyan="\033[36m"
|
||||||
|
white="\033[37m"
|
||||||
|
gpg_key=64B9C6AAE2D55278
|
||||||
|
gpgurl=https://statping.com/statping.gpg
|
||||||
|
repo=https://github.com/hunterlong/statping
|
||||||
|
|
||||||
|
statping_get_tarball() {
|
||||||
|
url="https://github.com/hunterlong/statping/releases/latest/download/statping-$1-$2.tar.gz"
|
||||||
|
printf "$cyan> Downloading latest version for $OS $ARCH...\n$url $reset\n"
|
||||||
|
# Get both the tarball and its GPG signature
|
||||||
|
tarball_tmp=`mktemp -t statping.tar.gz.XXXXXXXXXX`
|
||||||
|
if curl --fail -L -o "$tarball_tmp" "$url"; then
|
||||||
|
# All this dance is because `tar --strip=1` does not work everywhere
|
||||||
|
temp=$(mktemp -d statping.XXXXXXXXXX)
|
||||||
|
tar xzf $tarball_tmp -C "$temp"
|
||||||
|
statping_verify_integrity "$temp"/statping
|
||||||
|
printf "$green> Installing to $DEST/statping\n"
|
||||||
|
mv "$temp"/statping "$DEST"
|
||||||
|
newversion=`$DEST/statping version`
|
||||||
|
rm -rf "$temp"
|
||||||
|
rm $tarball_tmp*
|
||||||
|
printf "$cyan> Statping is now installed! $reset\n"
|
||||||
|
printf "$white> Version: $newversion $reset\n"
|
||||||
|
printf "$white> Repo: $repo $reset\n"
|
||||||
|
printf "$white> Wiki: $repo/wiki $reset\n"
|
||||||
|
printf "$white> Issues: $repo/issues $reset\n"
|
||||||
|
printf "$cyan> Try to run \"statping help\" $reset\n"
|
||||||
|
else
|
||||||
|
printf "$red> Failed to download $url.$reset\n"
|
||||||
|
exit 1;
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Verifies the GPG signature of the tarball
|
||||||
|
statping_verify_integrity() {
|
||||||
|
# Check if GPG is installed
|
||||||
|
if [[ -z "$(command -v gpg)" ]]; then
|
||||||
|
printf "$yellow> WARNING: GPG is not installed, integrity can not be verified!$reset\n"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$statping_GPG" == "no" ]; then
|
||||||
|
printf "$cyan> WARNING: Skipping GPG integrity check!$reset\n"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "$cyan> Verifying integrity with gpg key from $gpgurl...$reset\n"
|
||||||
|
# Grab the public key if it doesn't already exist
|
||||||
|
gpg --list-keys $gpg_key >/dev/null 2>&1 || (curl -sS $gpgurl | gpg --import)
|
||||||
|
|
||||||
|
if [ ! -f "$1.asc" ]; then
|
||||||
|
printf "$red> Could not download GPG signature for this Statping release. This means the release can not be verified!$reset\n"
|
||||||
|
statping_verify_or_quit "> Do you really want to continue?"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Actually perform the verification
|
||||||
|
if gpg --verify "$1.asc" $1; then
|
||||||
|
printf "$green> GPG signature looks good$reset\n"
|
||||||
|
else
|
||||||
|
printf "$red> GPG signature for this Statping release is invalid! This is BAD and may mean the release has been tampered with. It is strongly recommended that you report this to the Statping developers.$reset\n"
|
||||||
|
statping_verify_or_quit "> Do you really want to continue?"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
statping_reset() {
|
||||||
|
unset -f statping_install statping_reset statping_get_tarball statping_verify_integrity statping_verify_or_quit statping_brew_install getOS getArch
|
||||||
|
}
|
||||||
|
|
||||||
|
statping_brew_install() {
|
||||||
|
if [[ -z "$(command -v brew --version)" ]]; then
|
||||||
|
printf "${white}Using Brew to install!$reset\n"
|
||||||
|
printf "${yellow}brew tap hunterlong/statping$reset\n"
|
||||||
|
brew tap hunterlong/statping
|
||||||
|
brew install statping
|
||||||
|
printf "${green}Brew installation is complete!$reset\n"
|
||||||
|
printf "${yellow}You can use 'brew upgrade' to upgrade Statping next time.$reset\n"
|
||||||
|
else
|
||||||
|
statping_get_tarball $OS $ARCH
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
statping_install() {
|
||||||
|
printf "${white}Installing Statping!$reset\n"
|
||||||
|
getOS
|
||||||
|
getArch
|
||||||
|
if [ -d "$DEST" ]; then
|
||||||
|
if ! loc="$(type -p "statping version")" || [[ -z $loc ]]; then
|
||||||
|
specified_version=`curl -sS https://raw.githubusercontent.com/hunterlong/statping/master/version.txt`
|
||||||
|
statping_version=`statping version`
|
||||||
|
if [ "$specified_version" = "$statping_version" ]; then
|
||||||
|
printf "$green> Statping is already at the $specified_version version.$reset\n"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
if [ "$OS" == "osx" ]; then
|
||||||
|
statping_brew_install
|
||||||
|
else
|
||||||
|
statping_get_tarball $OS $ARCH
|
||||||
|
fi
|
||||||
|
statping_reset
|
||||||
|
}
|
||||||
|
|
||||||
|
statping_verify_or_quit() {
|
||||||
|
read -p "$1 [y/N] " -n 1 -r
|
||||||
|
echo
|
||||||
|
if [[ ! $REPLY =~ ^[Yy]$ ]]
|
||||||
|
then
|
||||||
|
printf "$red> Aborting$reset\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# get the users operating system
|
||||||
|
getOS() {
|
||||||
|
OS="`uname`"
|
||||||
|
case $OS in
|
||||||
|
'Linux')
|
||||||
|
OS='linux'
|
||||||
|
DEST=/usr/local/bin
|
||||||
|
alias ls='ls --color=auto'
|
||||||
|
;;
|
||||||
|
'FreeBSD')
|
||||||
|
OS='freebsd'
|
||||||
|
DEST=/usr/local/bin
|
||||||
|
alias ls='ls -G'
|
||||||
|
;;
|
||||||
|
'WindowsNT')
|
||||||
|
OS='windows'
|
||||||
|
DEST=/usr/local/bin
|
||||||
|
;;
|
||||||
|
'Darwin')
|
||||||
|
OS='osx'
|
||||||
|
DEST=/usr/local/bin
|
||||||
|
;;
|
||||||
|
'SunOS')
|
||||||
|
OS='solaris'
|
||||||
|
DEST=/usr/local/bin
|
||||||
|
;;
|
||||||
|
'AIX') ;;
|
||||||
|
*) ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# get 64x or 32 machine arch
|
||||||
|
getArch() {
|
||||||
|
MACHINE_TYPE=`uname -m`
|
||||||
|
if [ ${MACHINE_TYPE} == 'x86_64' ]; then
|
||||||
|
ARCH="x64"
|
||||||
|
else
|
||||||
|
ARCH="x32"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cd ~
|
||||||
|
statping_install $1 $2
|
|
@ -89,8 +89,8 @@ func (u *commandLine) OnSave() error {
|
||||||
// OnTest for commandLine triggers when this notifier has been saved
|
// OnTest for commandLine triggers when this notifier has been saved
|
||||||
func (u *commandLine) OnTest() error {
|
func (u *commandLine) OnTest() error {
|
||||||
in, out, err := runCommand(u.Host, u.Var1)
|
in, out, err := runCommand(u.Host, u.Var1)
|
||||||
utils.Log(1, in)
|
utils.Log.Infoln(in)
|
||||||
utils.Log(1, out)
|
utils.Log.Infoln(out)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -216,7 +216,7 @@ func (u *email) Select() *notifier.Notification {
|
||||||
|
|
||||||
// OnSave triggers when this notifier has been saved
|
// OnSave triggers when this notifier has been saved
|
||||||
func (u *email) OnSave() error {
|
func (u *email) OnSave() error {
|
||||||
utils.Log(1, fmt.Sprintf("Notification %v is receiving updated information.", u.Method))
|
utils.Log.Infoln(fmt.Sprintf("Notification %v is receiving updated information.", u.Method))
|
||||||
// Do updating stuff here
|
// Do updating stuff here
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -235,7 +235,7 @@ func (u *email) OnTest() error {
|
||||||
LastStatusCode: 200,
|
LastStatusCode: 200,
|
||||||
Expected: types.NewNullString("test example"),
|
Expected: types.NewNullString("test example"),
|
||||||
LastResponse: "<html>this is an example response</html>",
|
LastResponse: "<html>this is an example response</html>",
|
||||||
CreatedAt: time.Now().Add(-24 * time.Hour),
|
CreatedAt: utils.Now().Add(-24 * time.Hour),
|
||||||
}
|
}
|
||||||
email := &emailOutgoing{
|
email := &emailOutgoing{
|
||||||
To: u.Var2,
|
To: u.Var2,
|
||||||
|
@ -262,7 +262,7 @@ func (u *email) dialSend(email *emailOutgoing) error {
|
||||||
m.SetHeader("Subject", email.Subject)
|
m.SetHeader("Subject", email.Subject)
|
||||||
m.SetBody("text/html", email.Source)
|
m.SetBody("text/html", email.Source)
|
||||||
if err := mailer.DialAndSend(m); err != nil {
|
if err := mailer.DialAndSend(m); err != nil {
|
||||||
utils.Log(3, fmt.Sprintf("email '%v' sent to: %v (size: %v) %v", email.Subject, email.To, len([]byte(email.Source)), err))
|
utils.Log.Errorln(fmt.Sprintf("email '%v' sent to: %v (size: %v) %v", email.Subject, email.To, len([]byte(email.Source)), err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -277,11 +277,11 @@ func emailTemplate(contents string, data interface{}) string {
|
||||||
t := template.New("email")
|
t := template.New("email")
|
||||||
t, err := t.Parse(contents)
|
t, err := t.Parse(contents)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, err)
|
utils.Log.Errorln(err)
|
||||||
}
|
}
|
||||||
var tpl bytes.Buffer
|
var tpl bytes.Buffer
|
||||||
if err := t.Execute(&tpl, data); err != nil {
|
if err := t.Execute(&tpl, data); err != nil {
|
||||||
utils.Log(2, err)
|
utils.Log.Warnln(err)
|
||||||
}
|
}
|
||||||
result := tpl.String()
|
result := tpl.String()
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -85,7 +85,7 @@ func (u *lineNotifier) OnSuccess(s *types.Service) {
|
||||||
// OnSave triggers when this notifier has been saved
|
// OnSave triggers when this notifier has been saved
|
||||||
func (u *lineNotifier) OnSave() error {
|
func (u *lineNotifier) OnSave() error {
|
||||||
msg := fmt.Sprintf("Notification %v is receiving updated information.", u.Method)
|
msg := fmt.Sprintf("Notification %v is receiving updated information.", u.Method)
|
||||||
utils.Log(1, msg)
|
utils.Log.Infoln(msg)
|
||||||
u.AddQueue("saved", msg)
|
u.AddQueue("saved", msg)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,13 +44,13 @@ var TestService = &types.Service{
|
||||||
LastStatusCode: 404,
|
LastStatusCode: 404,
|
||||||
Online: true,
|
Online: true,
|
||||||
LastResponse: "<html>this is an example response</html>",
|
LastResponse: "<html>this is an example response</html>",
|
||||||
CreatedAt: time.Now().Add(-24 * time.Hour),
|
CreatedAt: utils.Now().Add(-24 * time.Hour),
|
||||||
}
|
}
|
||||||
|
|
||||||
var TestFailure = &types.Failure{
|
var TestFailure = &types.Failure{
|
||||||
Issue: "testing",
|
Issue: "testing",
|
||||||
Service: 1,
|
Service: 1,
|
||||||
CreatedAt: time.Now().Add(-12 * time.Hour),
|
CreatedAt: utils.Now().Add(-12 * time.Hour),
|
||||||
}
|
}
|
||||||
|
|
||||||
var TestUser = &types.User{
|
var TestUser = &types.User{
|
||||||
|
|
|
@ -99,7 +99,7 @@ func (u *slack) OnFailure(s *types.Service, f *types.Failure) {
|
||||||
message := slackMessage{
|
message := slackMessage{
|
||||||
Service: s,
|
Service: s,
|
||||||
Template: failingTemplate,
|
Template: failingTemplate,
|
||||||
Time: time.Now().Unix(),
|
Time: utils.Now().Unix(),
|
||||||
Issue: f.Issue,
|
Issue: f.Issue,
|
||||||
}
|
}
|
||||||
parseSlackMessage(s.Id, failingTemplate, message)
|
parseSlackMessage(s.Id, failingTemplate, message)
|
||||||
|
@ -112,7 +112,7 @@ func (u *slack) OnSuccess(s *types.Service) {
|
||||||
message := slackMessage{
|
message := slackMessage{
|
||||||
Service: s,
|
Service: s,
|
||||||
Template: successTemplate,
|
Template: successTemplate,
|
||||||
Time: time.Now().Unix(),
|
Time: utils.Now().Unix(),
|
||||||
}
|
}
|
||||||
parseSlackMessage(s.Id, successTemplate, message)
|
parseSlackMessage(s.Id, successTemplate, message)
|
||||||
}
|
}
|
||||||
|
|
|
@ -103,7 +103,7 @@ func (u *telegram) OnSuccess(s *types.Service) {
|
||||||
|
|
||||||
// OnSave triggers when this notifier has been saved
|
// OnSave triggers when this notifier has been saved
|
||||||
func (u *telegram) OnSave() error {
|
func (u *telegram) OnSave() error {
|
||||||
utils.Log(1, fmt.Sprintf("Notification %v is receiving updated information.", u.Method))
|
utils.Log.Infoln(fmt.Sprintf("Notification %v is receiving updated information.", u.Method))
|
||||||
|
|
||||||
// Do updating stuff here
|
// Do updating stuff here
|
||||||
|
|
||||||
|
|
|
@ -113,7 +113,7 @@ func (u *twilio) OnSuccess(s *types.Service) {
|
||||||
|
|
||||||
// OnSave triggers when this notifier has been saved
|
// OnSave triggers when this notifier has been saved
|
||||||
func (u *twilio) OnSave() error {
|
func (u *twilio) OnSave() error {
|
||||||
utils.Log(1, fmt.Sprintf("Notification %v is receiving updated information.", u.Method))
|
utils.Log.Infoln(fmt.Sprintf("Notification %v is receiving updated information.", u.Method))
|
||||||
|
|
||||||
// Do updating stuff here
|
// Do updating stuff here
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,7 @@ func replaceBodyText(body string, s *types.Service, f *types.Failure) string {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *webhooker) sendHttpWebhook(body string) (*http.Response, error) {
|
func (w *webhooker) sendHttpWebhook(body string) (*http.Response, error) {
|
||||||
utils.Log(1, fmt.Sprintf("sending body: '%v' to %v as a %v request", body, w.Host, w.Var1))
|
utils.Log.Infoln(fmt.Sprintf("sending body: '%v' to %v as a %v request", body, w.Host, w.Var1))
|
||||||
client := new(http.Client)
|
client := new(http.Client)
|
||||||
client.Timeout = time.Duration(10 * time.Second)
|
client.Timeout = time.Duration(10 * time.Second)
|
||||||
var buf *bytes.Buffer
|
var buf *bytes.Buffer
|
||||||
|
@ -136,7 +136,7 @@ func (w *webhooker) OnTest() error {
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
content, err := ioutil.ReadAll(resp.Body)
|
content, err := ioutil.ReadAll(resp.Body)
|
||||||
utils.Log(1, fmt.Sprintf("Webhook notifier received: '%v'", string(content)))
|
utils.Log.Infoln(fmt.Sprintf("Webhook notifier received: '%v'", string(content)))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,7 @@ import (
|
||||||
var (
|
var (
|
||||||
AllPlugins []*types.PluginObject
|
AllPlugins []*types.PluginObject
|
||||||
dir string
|
dir string
|
||||||
|
log = utils.Log.WithField("type", "plugin")
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -49,7 +50,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadPlugin(file string) error {
|
func LoadPlugin(file string) error {
|
||||||
utils.Log(1, fmt.Sprintf("opening file %v", file))
|
log.Infoln(fmt.Sprintf("opening file %v", file))
|
||||||
f, err := os.Open(file)
|
f, err := os.Open(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -58,29 +59,29 @@ func LoadPlugin(file string) error {
|
||||||
fSplit := strings.Split(f.Name(), "/")
|
fSplit := strings.Split(f.Name(), "/")
|
||||||
fileBin := fSplit[len(fSplit)-1]
|
fileBin := fSplit[len(fSplit)-1]
|
||||||
|
|
||||||
utils.Log(1, fmt.Sprintf("Attempting to load plugin '%v'", fileBin))
|
log.Infoln(fmt.Sprintf("Attempting to load plugin '%v'", fileBin))
|
||||||
ext := strings.Split(fileBin, ".")
|
ext := strings.Split(fileBin, ".")
|
||||||
if len(ext) != 2 {
|
if len(ext) != 2 {
|
||||||
utils.Log(3, fmt.Sprintf("Plugin '%v' must end in .so extension", fileBin))
|
log.Errorln(fmt.Sprintf("Plugin '%v' must end in .so extension", fileBin))
|
||||||
return fmt.Errorf("Plugin '%v' must end in .so extension %v", fileBin, len(ext))
|
return fmt.Errorf("Plugin '%v' must end in .so extension %v", fileBin, len(ext))
|
||||||
}
|
}
|
||||||
if ext[1] != "so" {
|
if ext[1] != "so" {
|
||||||
utils.Log(3, fmt.Sprintf("Plugin '%v' must end in .so extension", fileBin))
|
log.Errorln(fmt.Sprintf("Plugin '%v' must end in .so extension", fileBin))
|
||||||
return fmt.Errorf("Plugin '%v' must end in .so extension", fileBin)
|
return fmt.Errorf("Plugin '%v' must end in .so extension", fileBin)
|
||||||
}
|
}
|
||||||
plug, err := plugin.Open(file)
|
plug, err := plugin.Open(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Plugin '%v' could not load correctly. %v", fileBin, err))
|
log.Errorln(fmt.Sprintf("Plugin '%v' could not load correctly. %v", fileBin, err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
symPlugin, err := plug.Lookup("Plugin")
|
symPlugin, err := plug.Lookup("Plugin")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Plugin '%v' could not locate Plugin variable. %v", fileBin, err))
|
log.Errorln(fmt.Sprintf("Plugin '%v' could not locate Plugin variable. %v", fileBin, err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
plugActions, ok := symPlugin.(types.PluginActions)
|
plugActions, ok := symPlugin.(types.PluginActions)
|
||||||
if !ok {
|
if !ok {
|
||||||
utils.Log(3, fmt.Sprintf("Plugin %v was not type PluginObject", f.Name()))
|
log.Errorln(fmt.Sprintf("Plugin %v was not type PluginObject", f.Name()))
|
||||||
return fmt.Errorf("Plugin %v was not type PluginActions %v", f.Name(), plugActions.GetInfo())
|
return fmt.Errorf("Plugin %v was not type PluginActions %v", f.Name(), plugActions.GetInfo())
|
||||||
}
|
}
|
||||||
info := plugActions.GetInfo()
|
info := plugActions.GetInfo()
|
||||||
|
@ -88,28 +89,28 @@ func LoadPlugin(file string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
utils.Log(1, fmt.Sprintf("Plugin %v loaded from %v", info.Name, f.Name()))
|
log.Infoln(fmt.Sprintf("Plugin %v loaded from %v", info.Name, f.Name()))
|
||||||
core.CoreApp.AllPlugins = append(core.CoreApp.AllPlugins, plugActions)
|
core.CoreApp.AllPlugins = append(core.CoreApp.AllPlugins, plugActions)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func LoadPlugins() {
|
func LoadPlugins() {
|
||||||
pluginDir := dir + "/plugins"
|
pluginDir := dir + "/plugins"
|
||||||
utils.Log(1, fmt.Sprintf("Loading any available Plugins from /plugins directory"))
|
log.Infoln(fmt.Sprintf("Loading any available Plugins from /plugins directory"))
|
||||||
if _, err := os.Stat(pluginDir); os.IsNotExist(err) {
|
if _, err := os.Stat(pluginDir); os.IsNotExist(err) {
|
||||||
os.Mkdir(pluginDir, os.ModePerm)
|
os.Mkdir(pluginDir, os.ModePerm)
|
||||||
}
|
}
|
||||||
files, err := ioutil.ReadDir(pluginDir)
|
files, err := ioutil.ReadDir(pluginDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(2, fmt.Sprintf("Plugins directory was not found. Error: %v", err))
|
log.Warnln(fmt.Sprintf("Plugins directory was not found. Error: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for _, f := range files {
|
for _, f := range files {
|
||||||
err := LoadPlugin(f.Name())
|
err := LoadPlugin(f.Name())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, err)
|
log.Errorln(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
utils.Log(1, fmt.Sprintf("Loaded %v Plugins", len(core.CoreApp.Plugins)))
|
log.Infoln(fmt.Sprintf("Loaded %v Plugins", len(core.CoreApp.Plugins)))
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,54 +7,66 @@
|
||||||
/* Mobile Service Container */
|
/* Mobile Service Container */
|
||||||
HTML, BODY {
|
HTML, BODY {
|
||||||
background-color: #fcfcfc;
|
background-color: #fcfcfc;
|
||||||
padding-bottom: 10px; }
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding-top: 20px;
|
padding-top: 20px;
|
||||||
padding-bottom: 25px;
|
padding-bottom: 25px;
|
||||||
max-width: 860px;
|
max-width: 860px;
|
||||||
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; }
|
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
|
||||||
|
}
|
||||||
|
|
||||||
.header-title {
|
.header-title {
|
||||||
color: #464646; }
|
color: #464646;
|
||||||
|
}
|
||||||
|
|
||||||
.header-desc {
|
.header-desc {
|
||||||
color: #939393; }
|
color: #939393;
|
||||||
|
}
|
||||||
|
|
||||||
.btn {
|
.btn {
|
||||||
border-radius: 0.2rem; }
|
border-radius: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.online_list .badge {
|
.online_list .badge {
|
||||||
margin-top: 0.2rem; }
|
margin-top: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
margin-bottom: 30px; }
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-sm {
|
.btn-sm {
|
||||||
line-height: 1.3;
|
line-height: 1.3;
|
||||||
font-size: 0.75rem; }
|
font-size: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
.view_service_btn {
|
.view_service_btn {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -40px;
|
bottom: -40px;
|
||||||
right: 40px; }
|
right: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
.service_lower_info {
|
.service_lower_info {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: -40px;
|
bottom: -40px;
|
||||||
left: 40px;
|
left: 40px;
|
||||||
color: #d1ffca;
|
color: #d1ffca;
|
||||||
font-size: 0.85rem; }
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
.lg_number {
|
.lg_number {
|
||||||
font-size: 2.3rem;
|
font-size: 2.3rem;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
display: block;
|
display: block;
|
||||||
color: #4f4f4f; }
|
color: #4f4f4f;
|
||||||
|
}
|
||||||
|
|
||||||
.stats_area {
|
.stats_area {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #a5a5a5; }
|
color: #a5a5a5;
|
||||||
|
}
|
||||||
|
|
||||||
.lower_canvas {
|
.lower_canvas {
|
||||||
height: 3.4rem;
|
height: 3.4rem;
|
||||||
|
@ -62,82 +74,101 @@ HTML, BODY {
|
||||||
background-color: #48d338;
|
background-color: #48d338;
|
||||||
padding: 15px 10px;
|
padding: 15px 10px;
|
||||||
margin-left: 0px !important;
|
margin-left: 0px !important;
|
||||||
margin-right: 0px !important; }
|
margin-right: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.lower_canvas SPAN {
|
.lower_canvas SPAN {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
color: #fff; }
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
margin-top: 20px; }
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
.footer A {
|
.footer A {
|
||||||
color: #8d8d8d;
|
color: #8d8d8d;
|
||||||
text-decoration: none; }
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
.footer A:HOVER {
|
.footer A:HOVER {
|
||||||
color: #6d6d6d; }
|
color: #6d6d6d;
|
||||||
|
}
|
||||||
|
|
||||||
.badge {
|
.badge {
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 0.2rem; }
|
border-radius: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-group {
|
.btn-group {
|
||||||
height: 25px; }
|
height: 25px;
|
||||||
.btn-group A {
|
}
|
||||||
padding: 0.1rem .75rem;
|
.btn-group A {
|
||||||
font-size: 0.8rem; }
|
padding: 0.1rem 0.75rem;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
.card-body .badge {
|
.card-body .badge {
|
||||||
color: #fff; }
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-pills .nav-link {
|
.nav-pills .nav-link {
|
||||||
border-radius: 0.2rem; }
|
border-radius: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.form-control {
|
.form-control {
|
||||||
border-radius: 0.2rem; }
|
border-radius: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border: 1px solid rgba(0, 0, 0, 0.125); }
|
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||||
|
}
|
||||||
|
|
||||||
.card-body {
|
.card-body {
|
||||||
overflow: hidden; }
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.card-body H4 A {
|
.card-body H4 A {
|
||||||
color: #444444;
|
color: #444444;
|
||||||
text-decoration: none; }
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
.chart-container {
|
.chart-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 170px;
|
height: 170px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
overflow: hidden; }
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.service-chart-container {
|
.service-chart-container {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 400px;
|
height: 400px;
|
||||||
width: 100%; }
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.service-chart-heatmap {
|
.service-chart-heatmap {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 300px;
|
height: 300px;
|
||||||
width: 100%; }
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.inputTags-field {
|
.inputTags-field {
|
||||||
border: 0;
|
border: 0;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
padding-top: .13rem; }
|
padding-top: 0.13rem;
|
||||||
|
}
|
||||||
|
|
||||||
input.inputTags-field:focus {
|
input.inputTags-field:focus {
|
||||||
outline-width: 0; }
|
outline-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.inputTags-list {
|
.inputTags-list {
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-height: calc(2.25rem + 2px);
|
min-height: calc(2.25rem + 2px);
|
||||||
padding: .2rem .35rem;
|
padding: 0.2rem 0.35rem;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
|
@ -145,8 +176,9 @@ input.inputTags-field:focus {
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
border: 1px solid #ced4da;
|
border: 1px solid #ced4da;
|
||||||
border-radius: .25rem;
|
border-radius: 0.25rem;
|
||||||
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; }
|
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||||
|
}
|
||||||
|
|
||||||
.inputTags-item {
|
.inputTags-item {
|
||||||
background-color: #3aba39;
|
background-color: #3aba39;
|
||||||
|
@ -154,63 +186,81 @@ input.inputTags-field:focus {
|
||||||
padding: 5px 8px;
|
padding: 5px 8px;
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
color: white;
|
color: white;
|
||||||
border-radius: 4px; }
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
.inputTags-item .close-item {
|
.inputTags-item .close-item {
|
||||||
margin-left: 6px;
|
margin-left: 6px;
|
||||||
font-size: 13pt;
|
font-size: 13pt;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
cursor: pointer; }
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-primary {
|
.btn-primary {
|
||||||
background-color: #3e9bff;
|
background-color: #3e9bff;
|
||||||
border-color: #006fe6;
|
border-color: #006fe6;
|
||||||
color: white; }
|
color: white;
|
||||||
.btn-primary.dyn-dark {
|
}
|
||||||
background-color: #32a825 !important;
|
.btn-primary.dyn-dark {
|
||||||
border-color: #2c9320 !important; }
|
background-color: #32a825 !important;
|
||||||
.btn-primary.dyn-light {
|
border-color: #2c9320 !important;
|
||||||
background-color: #75de69 !important;
|
}
|
||||||
border-color: #88e37e !important; }
|
.btn-primary.dyn-light {
|
||||||
|
background-color: #75de69 !important;
|
||||||
|
border-color: #88e37e !important;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-success {
|
.btn-success {
|
||||||
background-color: #47d337; }
|
background-color: #47d337;
|
||||||
.btn-success.dyn-dark {
|
}
|
||||||
background-color: #32a825 !important;
|
.btn-success.dyn-dark {
|
||||||
border-color: #2c9320 !important; }
|
background-color: #32a825 !important;
|
||||||
.btn-success.dyn-light {
|
border-color: #2c9320 !important;
|
||||||
background-color: #75de69 !important;
|
}
|
||||||
border-color: #88e37e !important; }
|
.btn-success.dyn-light {
|
||||||
|
background-color: #75de69 !important;
|
||||||
|
border-color: #88e37e !important;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-danger {
|
.btn-danger {
|
||||||
background-color: #dd3545; }
|
background-color: #dd3545;
|
||||||
.btn-danger.dyn-dark {
|
}
|
||||||
background-color: #b61f2d !important;
|
.btn-danger.dyn-dark {
|
||||||
border-color: #a01b28 !important; }
|
background-color: #b61f2d !important;
|
||||||
.btn-danger.dyn-light {
|
border-color: #a01b28 !important;
|
||||||
background-color: #e66975 !important;
|
}
|
||||||
border-color: #e97f89 !important; }
|
.btn-danger.dyn-light {
|
||||||
|
background-color: #e66975 !important;
|
||||||
|
border-color: #e97f89 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.bg-success {
|
.bg-success {
|
||||||
background-color: #47d337 !important; }
|
background-color: #47d337 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.bg-danger {
|
.bg-danger {
|
||||||
background-color: #dd3545 !important; }
|
background-color: #dd3545 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.bg-success .dyn-dark {
|
.bg-success .dyn-dark {
|
||||||
background-color: #35b027 !important; }
|
background-color: #35b027 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.bg-danger .dyn-dark {
|
.bg-danger .dyn-dark {
|
||||||
background-color: #bf202f !important; }
|
background-color: #bf202f !important;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||||
background-color: #13a00d; }
|
background-color: #13a00d;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-pills A {
|
.nav-pills A {
|
||||||
color: #424242; }
|
color: #424242;
|
||||||
|
}
|
||||||
|
|
||||||
.nav-pills I {
|
.nav-pills I {
|
||||||
margin-right: 10px; }
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.CodeMirror {
|
.CodeMirror {
|
||||||
/* Bootstrap Settings */
|
/* Bootstrap Settings */
|
||||||
|
@ -230,23 +280,26 @@ input.inputTags-field:focus {
|
||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||||
/* Code Mirror Settings */
|
/* Code Mirror Settings */
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
height: 80vh; }
|
height: 80vh;
|
||||||
|
}
|
||||||
|
|
||||||
.CodeMirror-focused {
|
.CodeMirror-focused {
|
||||||
/* Bootstrap Settings */
|
/* Bootstrap Settings */
|
||||||
border-color: #66afe9;
|
border-color: #66afe9;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; }
|
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
.switch {
|
.switch {
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
position: relative; }
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.switch input {
|
.switch input {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -257,7 +310,8 @@ input.inputTags-field:focus {
|
||||||
clip: rect(0 0 0 0);
|
clip: rect(0 0 0 0);
|
||||||
clip-path: inset(50%);
|
clip-path: inset(50%);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
padding: 0; }
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.switch input + label {
|
.switch input + label {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -270,23 +324,26 @@ input.inputTags-field:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
text-indent: calc(calc(calc(2.375rem * .8) * 2) + .5rem); }
|
text-indent: calc(calc(calc(2.375rem * .8) * 2) + .5rem);
|
||||||
|
}
|
||||||
|
|
||||||
.switch input + label::before,
|
.switch input + label::before,
|
||||||
.switch input + label::after {
|
.switch input + label::after {
|
||||||
content: '';
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: calc(calc(2.375rem * .8) * 2);
|
width: calc(calc(2.375rem * .8) * 2);
|
||||||
bottom: 0;
|
bottom: 0;
|
||||||
display: block; }
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
.switch input + label::before {
|
.switch input + label::before {
|
||||||
right: 0;
|
right: 0;
|
||||||
background-color: #dee2e6;
|
background-color: #dee2e6;
|
||||||
border-radius: calc(2.375rem * .8);
|
border-radius: calc(2.375rem * .8);
|
||||||
transition: 0.2s all; }
|
transition: 0.2s all;
|
||||||
|
}
|
||||||
|
|
||||||
.switch input + label::after {
|
.switch input + label::after {
|
||||||
top: 2px;
|
top: 2px;
|
||||||
|
@ -295,105 +352,137 @@ input.inputTags-field:focus {
|
||||||
height: calc(calc(2.375rem * .8) - calc(2px * 2));
|
height: calc(calc(2.375rem * .8) - calc(2px * 2));
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
transition: 0.2s all; }
|
transition: 0.2s all;
|
||||||
|
}
|
||||||
|
|
||||||
.switch input:checked + label::before {
|
.switch input:checked + label::before {
|
||||||
background-color: #08d; }
|
background-color: #08d;
|
||||||
|
}
|
||||||
|
|
||||||
.switch input:checked + label::after {
|
.switch input:checked + label::after {
|
||||||
margin-left: calc(2.375rem * .8); }
|
margin-left: calc(2.375rem * .8);
|
||||||
|
}
|
||||||
|
|
||||||
.switch input:focus + label::before {
|
.switch input:focus + label::before {
|
||||||
outline: none;
|
outline: none;
|
||||||
box-shadow: 0 0 0 0.2rem rgba(0, 136, 221, 0.25); }
|
box-shadow: 0 0 0 0.2rem rgba(0, 136, 221, 0.25);
|
||||||
|
}
|
||||||
|
|
||||||
.switch input:disabled + label {
|
.switch input:disabled + label {
|
||||||
color: #868e96;
|
color: #868e96;
|
||||||
cursor: not-allowed; }
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
.switch input:disabled + label::before {
|
.switch input:disabled + label::before {
|
||||||
background-color: #e9ecef; }
|
background-color: #e9ecef;
|
||||||
|
}
|
||||||
|
|
||||||
.switch.switch-sm {
|
.switch.switch-sm {
|
||||||
font-size: 0.875rem; }
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
|
||||||
.switch.switch-sm input + label {
|
.switch.switch-sm input + label {
|
||||||
min-width: calc(calc(1.9375rem * .8) * 2);
|
min-width: calc(calc(1.9375rem * .8) * 2);
|
||||||
height: calc(1.9375rem * .8);
|
height: calc(1.9375rem * .8);
|
||||||
line-height: calc(1.9375rem * .8);
|
line-height: calc(1.9375rem * .8);
|
||||||
text-indent: calc(calc(calc(1.9375rem * .8) * 2) + .5rem); }
|
text-indent: calc(calc(calc(1.9375rem * .8) * 2) + .5rem);
|
||||||
|
}
|
||||||
|
|
||||||
.switch.switch-sm input + label::before {
|
.switch.switch-sm input + label::before {
|
||||||
width: calc(calc(1.9375rem * .8) * 2); }
|
width: calc(calc(1.9375rem * .8) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
.switch.switch-sm input + label::after {
|
.switch.switch-sm input + label::after {
|
||||||
width: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
width: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
||||||
height: calc(calc(1.9375rem * .8) - calc(2px * 2)); }
|
height: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
||||||
|
}
|
||||||
|
|
||||||
.switch.switch-sm input:checked + label::after {
|
.switch.switch-sm input:checked + label::after {
|
||||||
margin-left: calc(1.9375rem * .8); }
|
margin-left: calc(1.9375rem * .8);
|
||||||
|
}
|
||||||
|
|
||||||
.switch.switch-lg {
|
.switch.switch-lg {
|
||||||
font-size: 1.25rem; }
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
.switch.switch-lg input + label {
|
.switch.switch-lg input + label {
|
||||||
min-width: calc(calc(3rem * .8) * 2);
|
min-width: calc(calc(3rem * .8) * 2);
|
||||||
height: calc(3rem * .8);
|
height: calc(3rem * .8);
|
||||||
line-height: calc(3rem * .8);
|
line-height: calc(3rem * .8);
|
||||||
text-indent: calc(calc(calc(3rem * .8) * 2) + .5rem); }
|
text-indent: calc(calc(calc(3rem * .8) * 2) + .5rem);
|
||||||
|
}
|
||||||
|
|
||||||
.switch.switch-lg input + label::before {
|
.switch.switch-lg input + label::before {
|
||||||
width: calc(calc(3rem * .8) * 2); }
|
width: calc(calc(3rem * .8) * 2);
|
||||||
|
}
|
||||||
|
|
||||||
.switch.switch-lg input + label::after {
|
.switch.switch-lg input + label::after {
|
||||||
width: calc(calc(3rem * .8) - calc(2px * 2));
|
width: calc(calc(3rem * .8) - calc(2px * 2));
|
||||||
height: calc(calc(3rem * .8) - calc(2px * 2)); }
|
height: calc(calc(3rem * .8) - calc(2px * 2));
|
||||||
|
}
|
||||||
|
|
||||||
.switch.switch-lg input:checked + label::after {
|
.switch.switch-lg input:checked + label::after {
|
||||||
margin-left: calc(3rem * .8); }
|
margin-left: calc(3rem * .8);
|
||||||
|
}
|
||||||
|
|
||||||
.switch + .switch {
|
.switch + .switch {
|
||||||
margin-left: 1rem; }
|
margin-left: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes pulse_animation {
|
@keyframes pulse_animation {
|
||||||
0% {
|
0% {
|
||||||
transform: scale(1); }
|
transform: scale(1);
|
||||||
|
}
|
||||||
30% {
|
30% {
|
||||||
transform: scale(1); }
|
transform: scale(1);
|
||||||
|
}
|
||||||
40% {
|
40% {
|
||||||
transform: scale(1.02); }
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
50% {
|
50% {
|
||||||
transform: scale(1); }
|
transform: scale(1);
|
||||||
|
}
|
||||||
60% {
|
60% {
|
||||||
transform: scale(1); }
|
transform: scale(1);
|
||||||
|
}
|
||||||
70% {
|
70% {
|
||||||
transform: scale(1.05); }
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
80% {
|
80% {
|
||||||
transform: scale(1); }
|
transform: scale(1);
|
||||||
|
}
|
||||||
100% {
|
100% {
|
||||||
transform: scale(1); } }
|
transform: scale(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
.pulse {
|
.pulse {
|
||||||
animation-name: pulse_animation;
|
animation-name: pulse_animation;
|
||||||
animation-duration: 1500ms;
|
animation-duration: 1500ms;
|
||||||
transform-origin: 70% 70%;
|
transform-origin: 70% 70%;
|
||||||
animation-iteration-count: infinite;
|
animation-iteration-count: infinite;
|
||||||
animation-timing-function: linear; }
|
animation-timing-function: linear;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes glow-grow {
|
@keyframes glow-grow {
|
||||||
0% {
|
0% {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: scale(1); }
|
transform: scale(1);
|
||||||
|
}
|
||||||
80% {
|
80% {
|
||||||
opacity: 1; }
|
opacity: 1;
|
||||||
|
}
|
||||||
100% {
|
100% {
|
||||||
transform: scale(2);
|
transform: scale(2);
|
||||||
opacity: 0; } }
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
.pulse-glow {
|
.pulse-glow {
|
||||||
animation-name: glow-grown;
|
animation-name: glow-grown;
|
||||||
animation-duration: 100ms;
|
animation-duration: 100ms;
|
||||||
transform-origin: 70% 30%;
|
transform-origin: 70% 30%;
|
||||||
animation-iteration-count: infinite;
|
animation-iteration-count: infinite;
|
||||||
animation-timing-function: linear; }
|
animation-timing-function: linear;
|
||||||
|
}
|
||||||
|
|
||||||
.pulse-glow:before,
|
.pulse-glow:before,
|
||||||
.pulse-glow:after {
|
.pulse-glow:after {
|
||||||
|
@ -405,10 +494,12 @@ input.inputTags-field:focus {
|
||||||
right: 2.15rem;
|
right: 2.15rem;
|
||||||
border-radius: 0;
|
border-radius: 0;
|
||||||
box-shadow: 0 0 6px #47d337;
|
box-shadow: 0 0 6px #47d337;
|
||||||
animation: glow-grow 2s ease-out infinite; }
|
animation: glow-grow 2s ease-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
.sortable_drag {
|
.sortable_drag {
|
||||||
background-color: #0000000f; }
|
background-color: #0000000f;
|
||||||
|
}
|
||||||
|
|
||||||
.drag_icon {
|
.drag_icon {
|
||||||
cursor: move;
|
cursor: move;
|
||||||
|
@ -422,112 +513,139 @@ input.inputTags-field:focus {
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
margin-left: -10px;
|
margin-left: -10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
color: #b1b1b1; }
|
color: #b1b1b1;
|
||||||
|
}
|
||||||
|
|
||||||
/* (Optional) Apply a "closed-hand" cursor during drag operation. */
|
/* (Optional) Apply a "closed-hand" cursor during drag operation. */
|
||||||
.drag_icon:active {
|
.drag_icon:active {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
cursor: -moz-grabbing;
|
cursor: -moz-grabbing;
|
||||||
cursor: -webkit-grabbing; }
|
cursor: -webkit-grabbing;
|
||||||
|
}
|
||||||
|
|
||||||
.switch_btn {
|
.switch_btn {
|
||||||
float: right;
|
float: right;
|
||||||
margin: -1px 0px 0px 0px;
|
margin: -1px 0px 0px 0px;
|
||||||
display: block; }
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
#start_container {
|
#start_container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 99999;
|
z-index: 99999;
|
||||||
margin-top: 20px; }
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
#end_container {
|
#end_container {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
z-index: 99999;
|
z-index: 99999;
|
||||||
margin-top: 20px;
|
margin-top: 20px;
|
||||||
right: 0; }
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.pointer {
|
.pointer {
|
||||||
cursor: pointer; }
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
.jumbotron {
|
.jumbotron {
|
||||||
background-color: white; }
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
.toggle-service {
|
.toggle-service {
|
||||||
font-size: 18pt;
|
font-size: 18pt;
|
||||||
float: left;
|
float: left;
|
||||||
margin: 2px 3px 0 0;
|
margin: 2px 3px 0 0;
|
||||||
cursor: pointer; }
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 767px) {
|
@media (max-width: 767px) {
|
||||||
HTML, BODY {
|
HTML, BODY {
|
||||||
background-color: #fcfcfc; }
|
background-color: #fcfcfc;
|
||||||
|
}
|
||||||
|
|
||||||
.sm-container {
|
.sm-container {
|
||||||
margin-top: 0px !important;
|
margin-top: 0px !important;
|
||||||
padding: 0 !important; }
|
padding: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.list-group-item H5 {
|
.list-group-item H5 {
|
||||||
font-size: 0.9rem; }
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
.container {
|
.container {
|
||||||
padding: 0px !important;
|
padding: 0px !important;
|
||||||
padding-top: 15px !important; }
|
padding-top: 15px !important;
|
||||||
|
}
|
||||||
|
|
||||||
.group_header {
|
.group_header {
|
||||||
margin-left: 15px; }
|
margin-left: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
.navbar {
|
.navbar {
|
||||||
margin-left: 0px;
|
margin-left: 0px;
|
||||||
margin-top: 0px;
|
margin-top: 0px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 0; }
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.btn-sm {
|
.btn-sm {
|
||||||
line-height: 0.9rem;
|
line-height: 0.9rem;
|
||||||
font-size: 0.65rem; }
|
font-size: 0.65rem;
|
||||||
|
}
|
||||||
|
|
||||||
.full-col-12 {
|
.full-col-12 {
|
||||||
padding-left: 0px;
|
padding-left: 0px;
|
||||||
padding-right: 0px; }
|
padding-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.card {
|
.card {
|
||||||
border: 0;
|
border: 0;
|
||||||
border-radius: 0rem;
|
border-radius: 0rem;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background-color: #ffffff; }
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
.card-body {
|
.card-body {
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
padding: 0px 10px; }
|
padding: 10px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.lg_number {
|
.lg_number {
|
||||||
font-size: 7.8vw; }
|
font-size: 7.8vw;
|
||||||
|
}
|
||||||
|
|
||||||
.stats_area {
|
.stats_area {
|
||||||
margin-top: 1.5rem !important;
|
margin-top: 1.5rem !important;
|
||||||
margin-bottom: 1.5rem !important; }
|
margin-bottom: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
.stats_area .col-4 {
|
.stats_area .col-4 {
|
||||||
padding-left: 0;
|
padding-left: 0;
|
||||||
padding-right: 0;
|
padding-right: 0;
|
||||||
font-size: 0.6rem; }
|
font-size: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
.list-group-item {
|
.list-group-item {
|
||||||
border-top: 1px solid #e4e4e4;
|
border-top: 1px solid #e4e4e4;
|
||||||
border: 0px; }
|
border: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
.list-group-item:first-child {
|
.list-group-item:first-child {
|
||||||
border-top-left-radius: 0;
|
border-top-left-radius: 0;
|
||||||
border-top-right-radius: 0; }
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.list-group-item:last-child {
|
.list-group-item:last-child {
|
||||||
border-bottom-right-radius: 0;
|
border-bottom-right-radius: 0;
|
||||||
border-bottom-left-radius: 0; }
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.list-group-item P {
|
.list-group-item P {
|
||||||
font-size: 0.7rem; }
|
font-size: 0.7rem;
|
||||||
|
}
|
||||||
|
|
||||||
.service-chart-container {
|
.service-chart-container {
|
||||||
height: 200px; } }
|
height: 200px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*# sourceMappingURL=base.css.map */
|
/*# sourceMappingURL=base.css.map */
|
||||||
|
|
|
@ -67,7 +67,7 @@ func main() {
|
||||||
Compiled string
|
Compiled string
|
||||||
Pages map[string]string
|
Pages map[string]string
|
||||||
}{
|
}{
|
||||||
Timestamp: time.Now(),
|
Timestamp: utils.Now(),
|
||||||
URL: "ok",
|
URL: "ok",
|
||||||
Compiled: compiled,
|
Compiled: compiled,
|
||||||
Pages: newPages,
|
Pages: newPages,
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
log = utils.Log.WithField("type", "source")
|
||||||
CssBox *rice.Box // CSS files from the 'source/css' directory, this will be loaded into '/assets/css'
|
CssBox *rice.Box // CSS files from the 'source/css' directory, this will be loaded into '/assets/css'
|
||||||
ScssBox *rice.Box // SCSS files from the 'source/scss' directory, this will be loaded into '/assets/scss'
|
ScssBox *rice.Box // SCSS files from the 'source/scss' directory, this will be loaded into '/assets/scss'
|
||||||
JsBox *rice.Box // JS files from the 'source/js' directory, this will be loaded into '/assets/js'
|
JsBox *rice.Box // JS files from the 'source/js' directory, this will be loaded into '/assets/js'
|
||||||
|
@ -60,24 +61,24 @@ func CompileSASS(folder string) error {
|
||||||
scssFile := fmt.Sprintf("%v/%v", folder, "assets/scss/base.scss")
|
scssFile := fmt.Sprintf("%v/%v", folder, "assets/scss/base.scss")
|
||||||
baseFile := fmt.Sprintf("%v/%v", folder, "assets/css/base.css")
|
baseFile := fmt.Sprintf("%v/%v", folder, "assets/css/base.css")
|
||||||
|
|
||||||
utils.Log(1, fmt.Sprintf("Compiling SASS %v into %v", scssFile, baseFile))
|
log.Infoln(fmt.Sprintf("Compiling SASS %v into %v", scssFile, baseFile))
|
||||||
command := fmt.Sprintf("%v %v %v", sassBin, scssFile, baseFile)
|
command := fmt.Sprintf("%v %v %v", sassBin, scssFile, baseFile)
|
||||||
|
|
||||||
stdout, stderr, err := utils.Command(command)
|
stdout, stderr, err := utils.Command(command)
|
||||||
|
|
||||||
if stdout != "" || stderr != "" {
|
if stdout != "" || stderr != "" {
|
||||||
utils.Log(3, fmt.Sprintf("Failed to compile assets with SASS %v", err))
|
log.Errorln(fmt.Sprintf("Failed to compile assets with SASS %v", err))
|
||||||
return errors.New("failed to capture stdout or stderr")
|
return errors.New("failed to capture stdout or stderr")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Failed to compile assets with SASS %v", err))
|
log.Errorln(fmt.Sprintf("Failed to compile assets with SASS %v", err))
|
||||||
utils.Log(3, fmt.Sprintf("sh -c %v", command))
|
log.Errorln(fmt.Sprintf("sh -c %v", command))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
utils.Log(1, fmt.Sprintf("out: %v | error: %v", stdout, stderr))
|
log.Infoln(fmt.Sprintf("out: %v | error: %v", stdout, stderr))
|
||||||
utils.Log(1, "SASS Compiling is complete!")
|
log.Infoln("SASS Compiling is complete!")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,12 +88,12 @@ func UsingAssets(folder string) bool {
|
||||||
return true
|
return true
|
||||||
} else {
|
} else {
|
||||||
if os.Getenv("USE_ASSETS") == "true" {
|
if os.Getenv("USE_ASSETS") == "true" {
|
||||||
utils.Log(1, "Environment variable USE_ASSETS was found.")
|
log.Infoln("Environment variable USE_ASSETS was found.")
|
||||||
CreateAllAssets(folder)
|
CreateAllAssets(folder)
|
||||||
err := CompileSASS(folder)
|
err := CompileSASS(folder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
CopyToPublic(CssBox, folder+"/css", "base.css")
|
CopyToPublic(CssBox, folder+"/css", "base.css")
|
||||||
utils.Log(2, "Default 'base.css' was insert because SASS did not work.")
|
log.Warnln("Default 'base.css' was insert because SASS did not work.")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
@ -104,10 +105,10 @@ func UsingAssets(folder string) bool {
|
||||||
// SaveAsset will save an asset to the '/assets/' folder.
|
// SaveAsset will save an asset to the '/assets/' folder.
|
||||||
func SaveAsset(data []byte, folder, file string) error {
|
func SaveAsset(data []byte, folder, file string) error {
|
||||||
location := folder + "/assets/" + file
|
location := folder + "/assets/" + file
|
||||||
utils.Log(1, fmt.Sprintf("Saving %v", location))
|
log.Infoln(fmt.Sprintf("Saving %v", location))
|
||||||
err := utils.SaveFile(location, data)
|
err := utils.SaveFile(location, data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Failed to save %v, %v", location, err))
|
log.Errorln(fmt.Sprintf("Failed to save %v, %v", location, err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -117,7 +118,7 @@ func SaveAsset(data []byte, folder, file string) error {
|
||||||
func OpenAsset(folder, file string) string {
|
func OpenAsset(folder, file string) string {
|
||||||
dat, err := ioutil.ReadFile(folder + "/assets/" + file)
|
dat, err := ioutil.ReadFile(folder + "/assets/" + file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Failed to open %v, %v", file, err))
|
log.Errorln(fmt.Sprintf("Failed to open %v, %v", file, err))
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
return string(dat)
|
return string(dat)
|
||||||
|
@ -125,14 +126,14 @@ func OpenAsset(folder, file string) string {
|
||||||
|
|
||||||
// CreateAllAssets will dump HTML, CSS, SCSS, and JS assets into the '/assets' directory
|
// CreateAllAssets will dump HTML, CSS, SCSS, and JS assets into the '/assets' directory
|
||||||
func CreateAllAssets(folder string) error {
|
func CreateAllAssets(folder string) error {
|
||||||
utils.Log(1, fmt.Sprintf("Dump Statping assets into %v/assets", folder))
|
log.Infoln(fmt.Sprintf("Dump Statping assets into %v/assets", folder))
|
||||||
MakePublicFolder(folder + "/assets")
|
MakePublicFolder(folder + "/assets")
|
||||||
MakePublicFolder(folder + "/assets/js")
|
MakePublicFolder(folder + "/assets/js")
|
||||||
MakePublicFolder(folder + "/assets/css")
|
MakePublicFolder(folder + "/assets/css")
|
||||||
MakePublicFolder(folder + "/assets/scss")
|
MakePublicFolder(folder + "/assets/scss")
|
||||||
MakePublicFolder(folder + "/assets/font")
|
MakePublicFolder(folder + "/assets/font")
|
||||||
MakePublicFolder(folder + "/assets/files")
|
MakePublicFolder(folder + "/assets/files")
|
||||||
utils.Log(1, "Inserting scss, css, and javascript files into assets folder")
|
log.Infoln("Inserting scss, css, and javascript files into assets folder")
|
||||||
CopyAllToPublic(ScssBox, "scss")
|
CopyAllToPublic(ScssBox, "scss")
|
||||||
CopyAllToPublic(FontBox, "font")
|
CopyAllToPublic(FontBox, "font")
|
||||||
CopyAllToPublic(CssBox, "css")
|
CopyAllToPublic(CssBox, "css")
|
||||||
|
@ -144,9 +145,9 @@ func CreateAllAssets(folder string) error {
|
||||||
CopyToPublic(TmplBox, folder+"/assets/files", "swagger.json")
|
CopyToPublic(TmplBox, folder+"/assets/files", "swagger.json")
|
||||||
CopyToPublic(TmplBox, folder+"/assets/files", "postman.json")
|
CopyToPublic(TmplBox, folder+"/assets/files", "postman.json")
|
||||||
CopyToPublic(TmplBox, folder+"/assets/files", "grafana.json")
|
CopyToPublic(TmplBox, folder+"/assets/files", "grafana.json")
|
||||||
utils.Log(1, "Compiling CSS from SCSS style...")
|
log.Infoln("Compiling CSS from SCSS style...")
|
||||||
err := CompileSASS(utils.Directory)
|
err := CompileSASS(utils.Directory)
|
||||||
utils.Log(1, "Statping assets have been inserted")
|
log.Infoln("Statping assets have been inserted")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -154,10 +155,10 @@ func CreateAllAssets(folder string) error {
|
||||||
func DeleteAllAssets(folder string) error {
|
func DeleteAllAssets(folder string) error {
|
||||||
err := os.RemoveAll(folder + "/assets")
|
err := os.RemoveAll(folder + "/assets")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(1, fmt.Sprintf("There was an issue deleting Statping Assets, %v", err))
|
log.Infoln(fmt.Sprintf("There was an issue deleting Statping Assets, %v", err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
utils.Log(1, "Statping assets have been deleted")
|
log.Infoln("Statping assets have been deleted")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -184,15 +185,15 @@ func CopyAllToPublic(box *rice.Box, folder string) error {
|
||||||
// CopyToPublic will create a file from a rice Box to the '/assets' directory
|
// CopyToPublic will create a file from a rice Box to the '/assets' directory
|
||||||
func CopyToPublic(box *rice.Box, folder, file string) error {
|
func CopyToPublic(box *rice.Box, folder, file string) error {
|
||||||
assetFolder := fmt.Sprintf("%v/%v", folder, file)
|
assetFolder := fmt.Sprintf("%v/%v", folder, file)
|
||||||
utils.Log(1, fmt.Sprintf("Copying %v to %v", file, assetFolder))
|
log.Infoln(fmt.Sprintf("Copying %v to %v", file, assetFolder))
|
||||||
base, err := box.String(file)
|
base, err := box.String(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Failed to copy %v to %v, %v.", file, assetFolder, err))
|
log.Errorln(fmt.Sprintf("Failed to copy %v to %v, %v.", file, assetFolder, err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = ioutil.WriteFile(assetFolder, []byte(base), 0744)
|
err = ioutil.WriteFile(assetFolder, []byte(base), 0744)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Failed to write file %v to %v, %v.", file, assetFolder, err))
|
log.Errorln(fmt.Sprintf("Failed to write file %v to %v, %v.", file, assetFolder, err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -200,11 +201,11 @@ func CopyToPublic(box *rice.Box, folder, file string) error {
|
||||||
|
|
||||||
// MakePublicFolder will create a new folder
|
// MakePublicFolder will create a new folder
|
||||||
func MakePublicFolder(folder string) error {
|
func MakePublicFolder(folder string) error {
|
||||||
utils.Log(1, fmt.Sprintf("Creating folder '%v'", folder))
|
log.Infoln(fmt.Sprintf("Creating folder '%v'", folder))
|
||||||
if _, err := os.Stat(folder); os.IsNotExist(err) {
|
if _, err := os.Stat(folder); os.IsNotExist(err) {
|
||||||
err = os.MkdirAll(folder, 0777)
|
err = os.MkdirAll(folder, 0777)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(3, fmt.Sprintf("Failed to created %v directory, %v", folder, err))
|
log.Errorln(fmt.Sprintf("Failed to created %v directory, %v", folder, err))
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
<div class="jumbotron jumbotron-fluid">
|
<div class="jumbotron jumbotron-fluid">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h1 class="display-4">No Services!</h1>
|
<h1 class="display-4">No Services!</h1>
|
||||||
<a class="lead">You don't have any websites or applications being monitored by your Statping server. <p><a href="/services" class="btn btn-secondary mt-3">Add Service</a></p></p>
|
<a class="lead">You don't have any websites or applications being monitored by your Statping server. <p><a href="/service/create" class="btn btn-secondary mt-3">Add Service</a></p></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
{{ if not .Services }}
|
{{ if not .Services }}
|
||||||
<div class="alert alert-danger" role="alert">
|
<div class="alert alert-danger" role="alert">
|
||||||
<h4 class="alert-heading">No Services to Monitor!</h4>
|
<h4 class="alert-heading">No Services to Monitor!</h4>
|
||||||
<p>Your Statping Status Page is working correctly, but you don't have any services to monitor. Go to the <b>Dashboard</b> and add a website to begin really using your status page!</p>
|
<p>Your Statping Status Page is working correctly, but you don't have any services to monitor. Go to the <a href="/dashboard">Dashboard</a> and add a website to begin really using your status page!</p>
|
||||||
<hr>
|
<hr>
|
||||||
<p class="mb-0">If this is a bug, please make an issue in the Statping Github Repo. <a href="https://github.com/hunterlong/statping" class="btn btn-sm btn-outline-danger float-right">Statping Github Repo</a></p>
|
<p class="mb-0">If this is a bug, please make an issue in the Statping Github Repo. <a href="https://github.com/hunterlong/statping" class="btn btn-sm btn-outline-danger float-right">Statping Github Repo</a></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 mt-5">
|
<div class="col-12 mt-5">
|
||||||
|
{{if ne (len (Groups false)) 0}}
|
||||||
<h1 class="text-muted">Groups</h1>
|
<h1 class="text-muted">Groups</h1>
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -67,6 +68,7 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
{{end}}
|
||||||
{{if Auth}}
|
{{if Auth}}
|
||||||
<h1 class="text-muted mt-5">Create Group</h1>
|
<h1 class="text-muted mt-5">Create Group</h1>
|
||||||
{{template "form_group" NewGroup}}
|
{{template "form_group" NewGroup}}
|
||||||
|
|
File diff suppressed because one or more lines are too long
227
utils/log.go
227
utils/log.go
|
@ -17,24 +17,137 @@ package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/fatih/structs"
|
||||||
|
"github.com/hunterlong/statping/types"
|
||||||
|
Logger "github.com/sirupsen/logrus"
|
||||||
"gopkg.in/natefinch/lumberjack.v2"
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
"log"
|
"io"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
logFile *os.File
|
Log = Logger.StandardLogger()
|
||||||
fmtLogs *log.Logger
|
ljLogger *lumberjack.Logger
|
||||||
ljLogger *lumberjack.Logger
|
LastLines []*LogRow
|
||||||
LastLines []*LogRow
|
LockLines sync.Mutex
|
||||||
LockLines sync.Mutex
|
VerboseMode int
|
||||||
|
callerInitOnce sync.Once
|
||||||
|
logrusPackage string
|
||||||
|
minimumCallerDepth = 1
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const logFilePath = "/logs/statping.log"
|
||||||
|
|
||||||
|
type hook struct {
|
||||||
|
Entries []Logger.Entry
|
||||||
|
mu sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *hook) Fire(e *Logger.Entry) error {
|
||||||
|
pushLastLine(e.Message)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *hook) Levels() []Logger.Level {
|
||||||
|
return Logger.AllLevels
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogCaller retrieves the name of the first non-logrus calling function
|
||||||
|
func LogCaller(min int) *runtime.Frame {
|
||||||
|
const maximumCallerDepth = 25
|
||||||
|
var minimumCallerDepth = min
|
||||||
|
// Restrict the lookback frames to avoid runaway lookups
|
||||||
|
pcs := make([]uintptr, maximumCallerDepth)
|
||||||
|
depth := runtime.Callers(minimumCallerDepth, pcs)
|
||||||
|
frames := runtime.CallersFrames(pcs[:depth])
|
||||||
|
|
||||||
|
// cache this package's fully-qualified name
|
||||||
|
callerInitOnce.Do(func() {
|
||||||
|
logrusPackage = getPackageName(runtime.FuncForPC(pcs[0]).Name())
|
||||||
|
|
||||||
|
fmt.Println("caller once", logrusPackage, minimumCallerDepth, frames)
|
||||||
|
})
|
||||||
|
|
||||||
|
for f, again := frames.Next(); again; f, again = frames.Next() {
|
||||||
|
fmt.Println(f.Func, f.File, f.Line)
|
||||||
|
return &f
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we got here, we failed to find the caller's context
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPackageName(f string) string {
|
||||||
|
for {
|
||||||
|
lastPeriod := strings.LastIndex(f, ".")
|
||||||
|
lastSlash := strings.LastIndex(f, "/")
|
||||||
|
if lastPeriod > lastSlash {
|
||||||
|
f = f[:lastPeriod]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToFields accepts any amount of interfaces to create a new mapping for log.Fields. You will need to
|
||||||
|
// turn on verbose mode by starting Statping with "-v". This function will convert a struct of to the
|
||||||
|
// base struct name, and each field into it's own mapping, for example:
|
||||||
|
// type "*types.Service", on string field "Name" converts to "service_name=value". There is also an
|
||||||
|
// additional field called "_pointer" that will return the pointer hex value.
|
||||||
|
func ToFields(d ...interface{}) map[string]interface{} {
|
||||||
|
if VerboseMode == 1 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
fieldKey := make(map[string]interface{})
|
||||||
|
for _, v := range d {
|
||||||
|
spl := strings.Split(fmt.Sprintf("%T", v), ".")
|
||||||
|
trueType := spl[len(spl)-1]
|
||||||
|
if !structs.IsStruct(v) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, f := range structs.Fields(v) {
|
||||||
|
if f.IsExported() && !f.IsZero() && f.Kind() != reflect.Ptr && f.Kind() != reflect.Slice && f.Kind() != reflect.Chan {
|
||||||
|
field := strings.ToLower(trueType + "_" + f.Name())
|
||||||
|
fieldKey[field] = replaceVal(f.Value())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fieldKey[strings.ToLower(trueType+"_pointer")] = fmt.Sprintf("%p", v)
|
||||||
|
}
|
||||||
|
return fieldKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// replaceVal accepts an interface to be converted into human readable type
|
||||||
|
func replaceVal(d interface{}) interface{} {
|
||||||
|
switch v := d.(type) {
|
||||||
|
case types.NullBool:
|
||||||
|
return v.Bool
|
||||||
|
case types.NullString:
|
||||||
|
return v.String
|
||||||
|
case types.NullFloat64:
|
||||||
|
return v.Float64
|
||||||
|
case types.NullInt64:
|
||||||
|
return v.Int64
|
||||||
|
case string:
|
||||||
|
if len(v) > 500 {
|
||||||
|
return v[:500] + "... (truncated in logs)"
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
case time.Time:
|
||||||
|
return v.String()
|
||||||
|
case time.Duration:
|
||||||
|
return v.String()
|
||||||
|
default:
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// createLog will create the '/logs' directory based on a directory
|
// createLog will create the '/logs' directory based on a directory
|
||||||
func createLog(dir string) error {
|
func createLog(dir string) error {
|
||||||
var err error
|
var err error
|
||||||
|
@ -46,11 +159,6 @@ func createLog(dir string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
file, err := os.Create(dir + "/logs/statup.log")
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -60,77 +168,42 @@ func InitLogs() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
logFile, err = os.OpenFile(Directory+"/logs/statup.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("ERROR opening file: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
ljLogger = &lumberjack.Logger{
|
ljLogger = &lumberjack.Logger{
|
||||||
Filename: Directory + "/logs/statup.log",
|
Filename: Directory + logFilePath,
|
||||||
MaxSize: 16,
|
MaxSize: 16,
|
||||||
MaxBackups: 3,
|
MaxBackups: 5,
|
||||||
MaxAge: 28,
|
MaxAge: 28,
|
||||||
}
|
}
|
||||||
fmtLogs = log.New(logFile, "", log.Ldate|log.Ltime)
|
mw := io.MultiWriter(os.Stdout, ljLogger)
|
||||||
log.SetOutput(ljLogger)
|
Log.SetOutput(mw)
|
||||||
rotate()
|
|
||||||
|
Log.SetFormatter(&Logger.TextFormatter{
|
||||||
|
ForceColors: true,
|
||||||
|
DisableColors: false,
|
||||||
|
})
|
||||||
|
Log.AddHook(new(hook))
|
||||||
|
Log.SetNoLock()
|
||||||
|
|
||||||
|
if VerboseMode == 1 {
|
||||||
|
Log.SetLevel(Logger.WarnLevel)
|
||||||
|
} else if VerboseMode == 2 {
|
||||||
|
Log.SetLevel(Logger.InfoLevel)
|
||||||
|
} else if VerboseMode == 3 {
|
||||||
|
Log.SetLevel(Logger.DebugLevel)
|
||||||
|
} else {
|
||||||
|
Log.SetReportCaller(true)
|
||||||
|
Log.SetLevel(Logger.TraceLevel)
|
||||||
|
}
|
||||||
|
|
||||||
LastLines = make([]*LogRow, 0)
|
LastLines = make([]*LogRow, 0)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func rotate() {
|
// CloseLogs will close the log file correctly on shutdown
|
||||||
c := make(chan os.Signal, 1)
|
func CloseLogs() {
|
||||||
signal.Notify(c, syscall.SIGHUP)
|
ljLogger.Rotate()
|
||||||
go func() {
|
Log.Writer().Close()
|
||||||
for {
|
ljLogger.Close()
|
||||||
<-c
|
|
||||||
ljLogger.Rotate()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log creates a new entry in the Logger. Log has 1-5 levels depending on how critical the log/error is
|
|
||||||
func Log(level int, err interface{}) error {
|
|
||||||
if disableLogs {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
pushLastLine(err)
|
|
||||||
var outErr error
|
|
||||||
switch level {
|
|
||||||
case 5:
|
|
||||||
_, outErr = fmt.Printf("PANIC: %v\n", err)
|
|
||||||
fmtLogs.Printf("PANIC: %v\n", err)
|
|
||||||
case 4:
|
|
||||||
_, outErr = fmt.Printf("FATAL: %v\n", err)
|
|
||||||
fmtLogs.Printf("FATAL: %v\n", err)
|
|
||||||
//color.Red("ERROR: %v\n", err)
|
|
||||||
//os.Exit(2)
|
|
||||||
case 3:
|
|
||||||
_, outErr = fmt.Printf("ERROR: %v\n", err)
|
|
||||||
fmtLogs.Printf("ERROR: %v\n", err)
|
|
||||||
//color.Red("ERROR: %v\n", err)
|
|
||||||
case 2:
|
|
||||||
_, outErr = fmt.Printf("WARNING: %v\n", err)
|
|
||||||
fmtLogs.Printf("WARNING: %v\n", err)
|
|
||||||
//color.Yellow("WARNING: %v\n", err)
|
|
||||||
case 1:
|
|
||||||
_, outErr = fmt.Printf("INFO: %v\n", err)
|
|
||||||
fmtLogs.Printf("INFO: %v\n", err)
|
|
||||||
//color.Blue("INFO: %v\n", err)
|
|
||||||
case 0:
|
|
||||||
_, outErr = fmt.Printf("%v\n", err)
|
|
||||||
fmtLogs.Printf("%v\n", err)
|
|
||||||
//color.White("%v\n", err)
|
|
||||||
}
|
|
||||||
return outErr
|
|
||||||
}
|
|
||||||
|
|
||||||
// Http returns a log for a HTTP request
|
|
||||||
func Http(r *http.Request) string {
|
|
||||||
msg := fmt.Sprintf("%v (%v) | IP: %v", r.RequestURI, r.Method, r.Host)
|
|
||||||
fmt.Printf("WEB: %v\n", msg)
|
|
||||||
pushLastLine(msg)
|
|
||||||
return msg
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func pushLastLine(line interface{}) {
|
func pushLastLine(line interface{}) {
|
||||||
|
|
|
@ -34,6 +34,11 @@ func Timezoner(t time.Time, zone float32) time.Time {
|
||||||
return timez
|
return timez
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Now returns the UTC timestamp
|
||||||
|
func Now() time.Time {
|
||||||
|
return time.Now().UTC()
|
||||||
|
}
|
||||||
|
|
||||||
// FormatDuration converts a time.Duration into a string
|
// FormatDuration converts a time.Duration into a string
|
||||||
func FormatDuration(d time.Duration) string {
|
func FormatDuration(d time.Duration) string {
|
||||||
var out string
|
var out string
|
||||||
|
|
|
@ -176,7 +176,7 @@ func FileExists(name string) bool {
|
||||||
// DeleteFile will attempt to delete a file
|
// DeleteFile will attempt to delete a file
|
||||||
// DeleteFile("newfile.json")
|
// DeleteFile("newfile.json")
|
||||||
func DeleteFile(file string) error {
|
func DeleteFile(file string) error {
|
||||||
Log(1, "deleting file: "+file)
|
Log.Infoln("deleting file: " + file)
|
||||||
err := os.Remove(file)
|
err := os.Remove(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -193,7 +193,7 @@ func DeleteDirectory(directory string) error {
|
||||||
// Command will run a terminal command with 'sh -c COMMAND' and return stdout and errOut as strings
|
// Command will run a terminal command with 'sh -c COMMAND' and return stdout and errOut as strings
|
||||||
// in, out, err := Command("sass assets/scss assets/css/base.css")
|
// in, out, err := Command("sass assets/scss assets/css/base.css")
|
||||||
func Command(cmd string) (string, string, error) {
|
func Command(cmd string) (string, string, error) {
|
||||||
Log(1, "running command: "+cmd)
|
Log.Infoln("running command: " + cmd)
|
||||||
testCmd := exec.Command("sh", "-c", cmd)
|
testCmd := exec.Command("sh", "-c", cmd)
|
||||||
var stdout, stderr []byte
|
var stdout, stderr []byte
|
||||||
var errStdout, errStderr error
|
var errStdout, errStderr error
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -42,7 +41,8 @@ func TestCreateLog(t *testing.T) {
|
||||||
|
|
||||||
func TestInitLogs(t *testing.T) {
|
func TestInitLogs(t *testing.T) {
|
||||||
assert.Nil(t, InitLogs())
|
assert.Nil(t, InitLogs())
|
||||||
assert.FileExists(t, Directory+"/logs/statup.log")
|
Log.Infoln("this is a test")
|
||||||
|
assert.FileExists(t, Directory+"/logs/statping.log")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestDir(t *testing.T) {
|
func TestDir(t *testing.T) {
|
||||||
|
@ -57,15 +57,6 @@ func TestCommand(t *testing.T) {
|
||||||
assert.Empty(t, out)
|
assert.Empty(t, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLog(t *testing.T) {
|
|
||||||
assert.Nil(t, Log(0, errors.New("this is a 0 level error")))
|
|
||||||
assert.Nil(t, Log(1, errors.New("this is a 1 level error")))
|
|
||||||
assert.Nil(t, Log(2, errors.New("this is a 2 level error")))
|
|
||||||
assert.Nil(t, Log(3, errors.New("this is a 3 level error")))
|
|
||||||
assert.Nil(t, Log(4, errors.New("this is a 4 level error")))
|
|
||||||
assert.Nil(t, Log(5, errors.New("this is a 5 level error")))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestToInt(t *testing.T) {
|
func TestToInt(t *testing.T) {
|
||||||
assert.Equal(t, int64(55), ToInt("55"))
|
assert.Equal(t, int64(55), ToInt("55"))
|
||||||
assert.Equal(t, int64(55), ToInt(55))
|
assert.Equal(t, int64(55), ToInt(55))
|
||||||
|
@ -126,7 +117,7 @@ func ExampleDurationReadable() {
|
||||||
func TestLogHTTP(t *testing.T) {
|
func TestLogHTTP(t *testing.T) {
|
||||||
req, err := http.NewRequest("GET", "/", nil)
|
req, err := http.NewRequest("GET", "/", nil)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
assert.NotEmpty(t, Http(req))
|
assert.NotNil(t, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestStringInt(t *testing.T) {
|
func TestStringInt(t *testing.T) {
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0.80.66
|
0.80.67
|
||||||
|
|
Loading…
Reference in New Issue