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)"
|
||||
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 curl -L -s https://assets.statping.com/sass -o /usr/local/bin/sass && \
|
||||
chmod +x /usr/local/bin/sass
|
||||
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 && \
|
||||
make dev-deps
|
||||
ADD . /go/src/github.com/hunterlong/statping
|
||||
|
|
4
Makefile
4
Makefile
|
@ -1,14 +1,14 @@
|
|||
VERSION=$(shell cat version.txt)
|
||||
SIGN_KEY=B76D61FAA6DB759466E83D9964B9C6AAE2D55278
|
||||
BINARY_NAME=statping
|
||||
GOPATH:=$(GOPATH)
|
||||
GOCMD=go
|
||||
GOBUILD=$(GOCMD) build -a
|
||||
GOTEST=$(GOCMD) test
|
||||
GOGET=$(GOCMD) get
|
||||
GOVERSION=1.13.1
|
||||
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)"
|
||||
RICE=$(GOPATH)/bin/rice
|
||||
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] {
|
||||
case "version":
|
||||
if COMMIT != "" {
|
||||
fmt.Printf("Statping v%v (%v)\n", VERSION, COMMIT)
|
||||
fmt.Printf("%v (%v)\n", VERSION, COMMIT)
|
||||
} else {
|
||||
fmt.Printf("Statping v%v\n", VERSION)
|
||||
fmt.Printf("%v\n", VERSION)
|
||||
}
|
||||
return errors.New("end")
|
||||
case "assets":
|
||||
|
@ -60,18 +60,7 @@ func catchCLI(args []string) error {
|
|||
}
|
||||
return errors.New("end")
|
||||
case "update":
|
||||
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("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")
|
||||
return updateDisplay()
|
||||
case "test":
|
||||
cmd := args[1]
|
||||
switch cmd {
|
||||
|
@ -84,16 +73,16 @@ func catchCLI(args []string) error {
|
|||
fmt.Printf("Statping v%v Exporting Static 'index.html' page...\n", VERSION)
|
||||
utils.InitLogs()
|
||||
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
|
||||
}
|
||||
indexSource := ExportIndexHTML()
|
||||
//core.CloseDB()
|
||||
if err = utils.SaveFile(dir+"/index.html", indexSource); err != nil {
|
||||
utils.Log(4, err)
|
||||
utils.Log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
utils.Log(1, "Exported Statping index page: 'index.html'")
|
||||
utils.Log.Infoln("Exported Statping index page: 'index.html'")
|
||||
case "help":
|
||||
HelpEcho()
|
||||
return errors.New("end")
|
||||
|
@ -133,8 +122,8 @@ func catchCLI(args []string) error {
|
|||
}
|
||||
return errors.New("end")
|
||||
case "run":
|
||||
utils.Log(1, "Running 1 time and saving to database...")
|
||||
RunOnce()
|
||||
utils.Log.Infoln("Running 1 time and saving to database...")
|
||||
runOnce()
|
||||
//core.CloseDB()
|
||||
fmt.Println("Check is complete.")
|
||||
return errors.New("end")
|
||||
|
@ -142,7 +131,7 @@ func catchCLI(args []string) error {
|
|||
fmt.Println("Statping Environment Variable")
|
||||
envs, err := godotenv.Read(".env")
|
||||
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
|
||||
}
|
||||
for k, e := range envs {
|
||||
|
@ -170,16 +159,33 @@ func ExportIndexHTML() []byte {
|
|||
return w.Body.Bytes()
|
||||
}
|
||||
|
||||
// RunOnce will initialize the Statping application and check each service 1 time, will not run HTTP server
|
||||
func RunOnce() {
|
||||
func updateDisplay() error {
|
||||
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
|
||||
core.Configs, err = core.LoadConfigFile(utils.Directory)
|
||||
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)
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
utils.Log.Errorln(err)
|
||||
}
|
||||
core.CoreApp, err = core.SelectCore()
|
||||
if err != nil {
|
||||
|
@ -187,7 +193,7 @@ func RunOnce() {
|
|||
}
|
||||
_, err = core.CoreApp.SelectAllServices(true)
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
utils.Log.Errorln(err)
|
||||
}
|
||||
for _, out := range core.CoreApp.Services {
|
||||
out.Check(true)
|
||||
|
@ -214,6 +220,8 @@ func HelpEcho() {
|
|||
fmt.Printf("Flags:\n")
|
||||
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(" -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.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)")
|
||||
|
@ -241,7 +249,7 @@ func HelpEcho() {
|
|||
func checkGithubUpdates() (githubResponse, error) {
|
||||
var gitResp githubResponse
|
||||
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 {
|
||||
return githubResponse{}, err
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ func TestStartServerCommand(t *testing.T) {
|
|||
func TestVersionCommand(t *testing.T) {
|
||||
c := testcli.Command("statping", "version")
|
||||
c.Run()
|
||||
assert.True(t, c.StdoutContains("Statping v"+VERSION))
|
||||
assert.True(t, c.StdoutContains(VERSION))
|
||||
}
|
||||
|
||||
func TestHelpCommand(t *testing.T) {
|
||||
|
@ -90,7 +90,7 @@ func TestUpdateCommand(t *testing.T) {
|
|||
commandAndSleep(cmd, time.Duration(15*time.Second), got)
|
||||
gg, _ := <-got
|
||||
t.Log(gg)
|
||||
assert.Contains(t, gg, "Statping")
|
||||
assert.Contains(t, gg, VERSION)
|
||||
}
|
||||
|
||||
func TestAssetsCommand(t *testing.T) {
|
||||
|
|
41
cmd/main.go
41
cmd/main.go
|
@ -16,13 +16,14 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/hunterlong/statping/utils"
|
||||
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/core"
|
||||
"github.com/hunterlong/statping/handlers"
|
||||
"github.com/hunterlong/statping/plugin"
|
||||
"github.com/hunterlong/statping/source"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"github.com/joho/godotenv"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
@ -35,7 +36,8 @@ var (
|
|||
// COMMIT stores the git commit hash for this version of Statping
|
||||
COMMIT string
|
||||
ipAddress string
|
||||
UsingDotEnv bool
|
||||
envFile string
|
||||
verboseMode int
|
||||
port int
|
||||
)
|
||||
|
||||
|
@ -47,17 +49,21 @@ func init() {
|
|||
// -ip = 0.0.0.0 IP address for outgoing HTTP server
|
||||
// -port = 8080 Port number for outgoing HTTP server
|
||||
func parseFlags() {
|
||||
ip := flag.String("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(&ipAddress, "ip", "0.0.0.0", "IP address to run the Statping 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()
|
||||
ipAddress = *ip
|
||||
port = *p
|
||||
|
||||
if os.Getenv("PORT") != "" {
|
||||
port = int(utils.ToInt(os.Getenv("PORT")))
|
||||
}
|
||||
if os.Getenv("IP") != "" {
|
||||
ipAddress = os.Getenv("IP")
|
||||
}
|
||||
if os.Getenv("VERBOSE") != "" {
|
||||
verboseMode = int(utils.ToInt(os.Getenv("VERBOSE")))
|
||||
}
|
||||
}
|
||||
|
||||
// main will run the Statping application
|
||||
|
@ -67,6 +73,7 @@ func main() {
|
|||
parseFlags()
|
||||
loadDotEnvs()
|
||||
source.Assets()
|
||||
utils.VerboseMode = verboseMode
|
||||
if err := utils.InitLogs(); err != nil {
|
||||
fmt.Printf("Statping Log Error: \n %v\n", err)
|
||||
os.Exit(2)
|
||||
|
@ -83,33 +90,39 @@ func main() {
|
|||
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)
|
||||
if err != nil {
|
||||
utils.Log(3, err)
|
||||
utils.Log.Errorln(err)
|
||||
core.SetupMode = true
|
||||
utils.Log(1, handlers.RunHTTPServer(ipAddress, port))
|
||||
utils.Log.Infoln(handlers.RunHTTPServer(ipAddress, port))
|
||||
os.Exit(1)
|
||||
}
|
||||
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
|
||||
func sigterm() {
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
|
||||
<-sigs
|
||||
core.CloseDB()
|
||||
Close()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// loadDotEnvs attempts to load database configs from a '.env' file in root directory
|
||||
func loadDotEnvs() error {
|
||||
err := godotenv.Load()
|
||||
err := godotenv.Load(envFile)
|
||||
if err == nil {
|
||||
utils.Log(1, "Environment file '.env' Loaded")
|
||||
UsingDotEnv = true
|
||||
utils.Log.Infoln("Environment file '.env' Loaded")
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
@ -120,7 +133,7 @@ func mainProcess() {
|
|||
var err error
|
||||
err = core.Configs.Connect(false, dir)
|
||||
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.InitApp()
|
||||
|
|
|
@ -32,7 +32,7 @@ import (
|
|||
|
||||
// checkServices will start the checking go routine for each service
|
||||
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 {
|
||||
//go obj.StartCheckins()
|
||||
go ser.CheckQueue(true)
|
||||
|
@ -60,7 +60,7 @@ CheckLoop:
|
|||
for {
|
||||
select {
|
||||
case <-s.Running:
|
||||
utils.Log(1, fmt.Sprintf("Stopping service: %v", s.Name))
|
||||
log.Infoln(fmt.Sprintf("Stopping service: %v", s.Name))
|
||||
break CheckLoop
|
||||
case <-time.After(s.SleepDuration):
|
||||
s.Check(record)
|
||||
|
@ -229,7 +229,7 @@ func (s *Service) checkHttp(record bool) *Service {
|
|||
if s.Expected.String != "" {
|
||||
match, err := regexp.MatchString(s.Expected.String, string(content))
|
||||
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 record {
|
||||
|
@ -259,8 +259,8 @@ func recordSuccess(s *Service) {
|
|||
PingTime: s.PingTime,
|
||||
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)
|
||||
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)
|
||||
s.Online = 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
|
||||
func recordFailure(s *Service, issue string) {
|
||||
fail := &Failure{&types.Failure{
|
||||
fail := &types.Failure{
|
||||
Service: s.Id,
|
||||
Issue: issue,
|
||||
PingTime: s.PingTime,
|
||||
CreatedAt: time.Now(),
|
||||
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.Online = false
|
||||
s.SuccessNotified = false
|
||||
s.UpdateNotify = CoreApp.UpdateNotify.Bool
|
||||
s.DownText = s.DowntimeText()
|
||||
notifier.OnFailure(s.Service, fail.Failure)
|
||||
notifier.OnFailure(s.Service, fail)
|
||||
}
|
||||
|
|
|
@ -47,14 +47,14 @@ CheckinLoop:
|
|||
for {
|
||||
select {
|
||||
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
|
||||
break CheckinLoop
|
||||
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 {
|
||||
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()
|
||||
}
|
||||
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
|
||||
func (c *Checkin) Expected() time.Duration {
|
||||
last := c.Last().CreatedAt
|
||||
now := time.Now()
|
||||
now := utils.Now()
|
||||
lastDir := now.Sub(last)
|
||||
sub := time.Duration(c.Period() - lastDir)
|
||||
return sub
|
||||
|
@ -213,7 +213,7 @@ func (c *Checkin) Create() (int64, error) {
|
|||
c.ApiKey = utils.RandomString(7)
|
||||
row := checkinDB().Create(&c)
|
||||
if row.Error != nil {
|
||||
utils.Log(2, row.Error)
|
||||
log.Warnln(row.Error)
|
||||
return 0, row.Error
|
||||
}
|
||||
service := SelectService(c.ServiceId)
|
||||
|
@ -227,7 +227,7 @@ func (c *Checkin) Create() (int64, error) {
|
|||
func (c *Checkin) Update() (int64, error) {
|
||||
row := checkinDB().Update(&c)
|
||||
if row.Error != nil {
|
||||
utils.Log(2, row.Error)
|
||||
log.Warnln(row.Error)
|
||||
return 0, row.Error
|
||||
}
|
||||
return c.Id, row.Error
|
||||
|
@ -236,11 +236,11 @@ func (c *Checkin) Update() (int64, error) {
|
|||
// Create will create a new successful checkinHit
|
||||
func (c *CheckinHit) Create() (int64, error) {
|
||||
if c.CreatedAt.IsZero() {
|
||||
c.CreatedAt = time.Now()
|
||||
c.CreatedAt = utils.Now()
|
||||
}
|
||||
row := checkinHitsDB().Create(&c)
|
||||
if row.Error != nil {
|
||||
utils.Log(2, row.Error)
|
||||
log.Warnln(row.Error)
|
||||
return 0, 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
|
||||
func (c *CheckinHit) Ago() string {
|
||||
got, _ := timeago.TimeAgoWithTime(time.Now(), c.CreatedAt)
|
||||
got, _ := timeago.TimeAgoWithTime(utils.Now(), c.CreatedAt)
|
||||
return got
|
||||
}
|
||||
|
||||
// RecheckCheckinFailure will check if a Service Checkin has been reported yet
|
||||
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) {
|
||||
fmt.Println("rechecking every 15 seconds!")
|
||||
time.Sleep(15 * time.Second)
|
||||
|
|
|
@ -34,7 +34,7 @@ type ErrorResponse struct {
|
|||
func LoadConfigFile(directory string) (*DbConfig, error) {
|
||||
var configs *DbConfig
|
||||
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()
|
||||
}
|
||||
file, err := ioutil.ReadFile(directory + "/config.yml")
|
||||
|
@ -66,18 +66,18 @@ func LoadUsingEnv() (*DbConfig, error) {
|
|||
|
||||
err = Configs.Connect(true, utils.Directory)
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
Configs.Save()
|
||||
exists := DbSession.HasTable("core")
|
||||
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.CreateDatabase()
|
||||
CoreApp, err = Configs.InsertCore()
|
||||
if err != nil {
|
||||
utils.Log(3, err)
|
||||
log.Errorln(err)
|
||||
}
|
||||
|
||||
username := os.Getenv("ADMIN_USER")
|
||||
|
@ -195,7 +195,7 @@ func SampleData() error {
|
|||
func DeleteConfig() error {
|
||||
err := os.Remove(utils.Directory + "/config.yml")
|
||||
if err != nil {
|
||||
utils.Log(3, err)
|
||||
log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -40,6 +40,7 @@ var (
|
|||
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
|
||||
VERSION string // VERSION is set on build automatically by setting a -ldflag
|
||||
log = utils.Log.WithField("type", "core")
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
|
|
@ -134,6 +134,7 @@ func TestEnvToConfig(t *testing.T) {
|
|||
os.Setenv("DESCRIPTION", "Testing Statping")
|
||||
os.Setenv("ADMIN_USER", "admin")
|
||||
os.Setenv("ADMIN_PASS", "admin123")
|
||||
os.Setenv("VERBOSE", "true")
|
||||
config, err := EnvToConfig()
|
||||
assert.Nil(t, err)
|
||||
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)
|
||||
if err != nil {
|
||||
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()
|
||||
} else {
|
||||
return err
|
||||
|
@ -229,7 +229,10 @@ func (db *DbConfig) Connect(retry bool, location string) error {
|
|||
err = dbSession.DB().Ping()
|
||||
if err == nil {
|
||||
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
|
||||
}
|
||||
|
@ -244,7 +247,7 @@ func (db *DbConfig) waitForDb() error {
|
|||
// this function is currently set to delete records 7+ days old every 60 minutes
|
||||
func DatabaseMaintence() {
|
||||
for range time.Tick(60 * time.Minute) {
|
||||
utils.Log(1, "Checking for database records older than 3 months...")
|
||||
log.Infoln("Checking for database records older than 3 months...")
|
||||
since := time.Now().AddDate(0, -3, 0).UTC()
|
||||
DeleteAllSince("failures", 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"))
|
||||
db := DbSession.Exec(sql)
|
||||
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
|
||||
config, err := os.Create(utils.Directory + "/config.yml")
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
data, err := yaml.Marshal(db)
|
||||
if err != nil {
|
||||
utils.Log(3, err)
|
||||
log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
config.WriteString(string(data))
|
||||
|
@ -283,14 +286,14 @@ func (db *DbConfig) Save() (*DbConfig, error) {
|
|||
var err error
|
||||
config, err := os.Create(utils.Directory + "/config.yml")
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
db.ApiKey = utils.NewSHA1Hash(16)
|
||||
db.ApiSecret = utils.NewSHA1Hash(16)
|
||||
data, err := yaml.Marshal(db)
|
||||
if err != nil {
|
||||
utils.Log(3, err)
|
||||
log.Errorln(err)
|
||||
return nil, err
|
||||
}
|
||||
config.WriteString(string(data))
|
||||
|
@ -315,14 +318,14 @@ func (c *DbConfig) CreateCore() *Core {
|
|||
}
|
||||
CoreApp, err := SelectCore()
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
}
|
||||
return CoreApp
|
||||
}
|
||||
|
||||
// DropDatabase will DROP each table Statping created
|
||||
func (db *DbConfig) DropDatabase() error {
|
||||
utils.Log(1, "Dropping Database Tables...")
|
||||
log.Infoln("Dropping Database Tables...")
|
||||
err := DbSession.DropTableIfExists("checkins")
|
||||
err = DbSession.DropTableIfExists("checkin_hits")
|
||||
err = DbSession.DropTableIfExists("notifications")
|
||||
|
@ -340,7 +343,7 @@ func (db *DbConfig) DropDatabase() error {
|
|||
// CreateDatabase will CREATE TABLES for each of the Statping elements
|
||||
func (db *DbConfig) CreateDatabase() error {
|
||||
var err error
|
||||
utils.Log(1, "Creating Database Tables...")
|
||||
log.Infoln("Creating Database Tables...")
|
||||
for _, table := range DbModels {
|
||||
if err := DbSession.CreateTable(table); err.Error != nil {
|
||||
return err.Error
|
||||
|
@ -349,7 +352,7 @@ func (db *DbConfig) CreateDatabase() error {
|
|||
if err := DbSession.Table("core").CreateTable(&types.Core{}); err.Error != nil {
|
||||
return err.Error
|
||||
}
|
||||
utils.Log(1, "Statping Database Created")
|
||||
log.Infoln("Statping Database Created")
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -357,7 +360,7 @@ func (db *DbConfig) CreateDatabase() error {
|
|||
// This function will NOT remove previous records, tables or columns from the database.
|
||||
// If this function has an issue, it will ROLLBACK to the previous state.
|
||||
func (db *DbConfig) MigrateDatabase() error {
|
||||
utils.Log(1, "Migrating Database Tables...")
|
||||
log.Infoln("Migrating Database Tables...")
|
||||
tx := DbSession.Begin()
|
||||
defer func() {
|
||||
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 {
|
||||
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
|
||||
}
|
||||
utils.Log(1, "Statping Database Migrated")
|
||||
log.Infoln("Statping Database Migrated")
|
||||
return tx.Commit().Error
|
||||
}
|
||||
|
|
|
@ -20,7 +20,6 @@ import (
|
|||
"encoding/json"
|
||||
"github.com/hunterlong/statping/source"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"html/template"
|
||||
)
|
||||
|
||||
|
@ -28,7 +27,7 @@ import (
|
|||
func ExportChartsJs() string {
|
||||
render, err := source.JsBox.String("charts.js")
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
}
|
||||
t := template.New("charts")
|
||||
t.Funcs(template.FuncMap{
|
||||
|
@ -39,7 +38,7 @@ func ExportChartsJs() string {
|
|||
t.Parse(render)
|
||||
var tpl bytes.Buffer
|
||||
if err := t.Execute(&tpl, CoreApp.Services); err != nil {
|
||||
utils.Log(3, err)
|
||||
log.Errorln(err)
|
||||
}
|
||||
result := tpl.String()
|
||||
return result
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"fmt"
|
||||
"github.com/ararog/timeago"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -35,16 +34,15 @@ const (
|
|||
)
|
||||
|
||||
// CreateFailure will create a new Failure record for a service
|
||||
func (s *Service) CreateFailure(fail types.FailureInterface) (int64, error) {
|
||||
f := fail.(*Failure)
|
||||
func (s *Service) CreateFailure(f *types.Failure) (int64, error) {
|
||||
f.Service = s.Id
|
||||
row := failuresDB().Create(f)
|
||||
if row.Error != nil {
|
||||
utils.Log(3, row.Error)
|
||||
log.Errorln(row.Error)
|
||||
return 0, row.Error
|
||||
}
|
||||
sort.Sort(types.FailSort(s.Failures))
|
||||
s.Failures = append(s.Failures, f)
|
||||
//s.Failures = append(s.Failures, f)
|
||||
if len(s.Failures) > limitedFailures {
|
||||
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")
|
||||
err := col.Find(&fails)
|
||||
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 fails
|
||||
|
@ -67,7 +65,7 @@ func (s *Service) AllFailures() []*Failure {
|
|||
func (s *Service) DeleteFailures() {
|
||||
err := DbSession.Exec(`DELETE FROM failures WHERE service = ?`, s.Id)
|
||||
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
|
||||
}
|
||||
|
@ -119,7 +117,7 @@ func CountFailures() uint64 {
|
|||
var count uint64
|
||||
err := failuresDB().Count(&count)
|
||||
if err.Error != nil {
|
||||
utils.Log(2, err.Error)
|
||||
log.Warnln(err.Error)
|
||||
return 0
|
||||
}
|
||||
return count
|
||||
|
|
|
@ -17,7 +17,6 @@ package core
|
|||
|
||||
import (
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -29,7 +28,7 @@ type Hit struct {
|
|||
func (s *Service) CreateHit(h *types.Hit) (int64, error) {
|
||||
db := hitsDB().Create(&h)
|
||||
if db.Error != nil {
|
||||
utils.Log(2, db.Error)
|
||||
log.Warnln(db.Error)
|
||||
return 0, db.Error
|
||||
}
|
||||
return h.Id, db.Error
|
||||
|
|
|
@ -18,7 +18,6 @@ package core
|
|||
import (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -64,7 +63,7 @@ func (m *Message) Create() (int64, error) {
|
|||
m.CreatedAt = time.Now().UTC()
|
||||
db := messagesDb().Create(m)
|
||||
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 m.Id, nil
|
||||
|
@ -80,7 +79,7 @@ func (m *Message) Delete() error {
|
|||
func (m *Message) Update() (*Message, error) {
|
||||
db := messagesDb().Update(m)
|
||||
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 m, nil
|
||||
|
|
|
@ -54,7 +54,9 @@ sendMessages:
|
|||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(BasicEvents)) && isEnabled(comm) && (s.Online || inLimits(comm)) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -74,7 +76,9 @@ func OnSuccess(s *types.Service) {
|
|||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(BasicEvents)) && isEnabled(comm) && (!s.Online || inLimits(comm)) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -84,7 +88,9 @@ func OnSuccess(s *types.Service) {
|
|||
func OnNewService(s *types.Service) {
|
||||
for _, comm := range AllCommunications {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -97,7 +103,7 @@ func OnUpdatedService(s *types.Service) {
|
|||
}
|
||||
for _, comm := range AllCommunications {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +116,7 @@ func OnDeletedService(s *types.Service) {
|
|||
}
|
||||
for _, comm := range AllCommunications {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -120,7 +126,7 @@ func OnDeletedService(s *types.Service) {
|
|||
func OnNewUser(u *types.User) {
|
||||
for _, comm := range AllCommunications {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -130,7 +136,7 @@ func OnNewUser(u *types.User) {
|
|||
func OnUpdatedUser(u *types.User) {
|
||||
for _, comm := range AllCommunications {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -140,7 +146,7 @@ func OnUpdatedUser(u *types.User) {
|
|||
func OnDeletedUser(u *types.User) {
|
||||
for _, comm := range AllCommunications {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -150,7 +156,7 @@ func OnDeletedUser(u *types.User) {
|
|||
func OnUpdatedCore(c *types.Core) {
|
||||
for _, comm := range AllCommunications {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -178,7 +184,7 @@ func OnNewNotifier(n *Notification) {
|
|||
func OnUpdatedNotifier(n *Notification) {
|
||||
for _, comm := range AllCommunications {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -233,7 +233,6 @@ func ExampleNotification_OnSuccess() {
|
|||
example.AddQueue("example", msg)
|
||||
fmt.Println(len(example.Queue))
|
||||
// Output:
|
||||
// INFO: Notifier 'Example' added new item (example) to the queue. (1 queued)
|
||||
// 1
|
||||
}
|
||||
|
||||
|
@ -270,7 +269,6 @@ func ExampleNotification_AddQueue() {
|
|||
queue := example.Queue
|
||||
fmt.Printf("Example has %v items in the queue", len(queue))
|
||||
// Output:
|
||||
// INFO: Notifier 'Example' added new item (example) to the queue. (2 queued)
|
||||
// Example has 2 items in the queue
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ var (
|
|||
// db holds the Statping database connection
|
||||
db *gorm.DB
|
||||
timezone float32
|
||||
log = utils.Log.WithField("type", "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{}) {
|
||||
data := &QueueData{uid, msg}
|
||||
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
|
||||
|
@ -169,8 +170,8 @@ func normalizeType(ty interface{}) string {
|
|||
func (n *Notification) makeLog(msg interface{}) {
|
||||
log := &NotificationLog{
|
||||
Message: normalizeType(msg),
|
||||
Time: utils.Timestamp(time.Now()),
|
||||
Timestamp: time.Now(),
|
||||
Time: utils.Timestamp(utils.Now()),
|
||||
Timestamp: utils.Now(),
|
||||
}
|
||||
n.logs = append(n.logs, log)
|
||||
}
|
||||
|
@ -290,9 +291,9 @@ CheckNotifier:
|
|||
msg := notification.Queue[0]
|
||||
err := n.Send(msg.Data)
|
||||
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 {
|
||||
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)
|
||||
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
|
||||
func install(n Notifier) error {
|
||||
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 {
|
||||
_, err := insertDatabase(n)
|
||||
if err != nil {
|
||||
utils.Log(3, err)
|
||||
log.Errorln(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
|
||||
func (n *Notification) SentLastHour() int {
|
||||
since := time.Now().Add(-1 * time.Hour)
|
||||
since := utils.Now().Add(-1 * time.Hour)
|
||||
return n.SentLast(since)
|
||||
}
|
||||
|
||||
// SentLastMinute returns the total amount of notifications sent in last 1 minute
|
||||
func (n *Notification) SentLastMinute() int {
|
||||
since := time.Now().Add(-1 * time.Minute)
|
||||
since := utils.Now().Add(-1 * time.Minute)
|
||||
return n.SentLast(since)
|
||||
}
|
||||
|
||||
|
@ -475,5 +479,5 @@ var ExampleService = &types.Service{
|
|||
LastStatusCode: 404,
|
||||
Expected: types.NewNullString("test example"),
|
||||
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
|
||||
func InsertSampleData() error {
|
||||
utils.Log(1, "Inserting Sample Data...")
|
||||
log.Infoln("Inserting Sample Data...")
|
||||
|
||||
insertSampleGroups()
|
||||
createdOn := time.Now().Add(((-24 * 30) * 3) * time.Hour).UTC()
|
||||
|
@ -113,7 +113,7 @@ func InsertSampleData() error {
|
|||
|
||||
insertSampleIncidents()
|
||||
|
||||
utils.Log(1, "Sample data has finished importing")
|
||||
log.Infoln("Sample data has finished importing")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -211,7 +211,7 @@ func InsertSampleHits() error {
|
|||
service := SelectService(i)
|
||||
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
|
||||
|
||||
p := utils.NewPerlin(2., 2., 10, seed)
|
||||
|
@ -455,17 +455,17 @@ func InsertLargeSampleData() error {
|
|||
func insertFailureRecords(since time.Time, amount int64) {
|
||||
for i := int64(14); i <= 15; 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
|
||||
|
||||
for fi := int64(1); fi <= amount; fi++ {
|
||||
createdAt = createdAt.Add(2 * time.Minute)
|
||||
|
||||
failure := &Failure{&types.Failure{
|
||||
failure := &types.Failure{
|
||||
Service: service.Id,
|
||||
Issue: "testing right here",
|
||||
CreatedAt: createdAt,
|
||||
}}
|
||||
}
|
||||
|
||||
service.CreateFailure(failure)
|
||||
}
|
||||
|
@ -476,7 +476,7 @@ func insertFailureRecords(since time.Time, amount int64) {
|
|||
func insertHitRecords(since time.Time, amount int64) {
|
||||
for i := int64(1); i <= 15; 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
|
||||
p := utils.NewPerlin(2, 2, 5, time.Now().UnixNano())
|
||||
for hi := int64(1); hi <= amount; hi++ {
|
||||
|
|
|
@ -101,7 +101,7 @@ func (c *Core) SelectAllServices(start bool) ([]*Service, error) {
|
|||
var services []*Service
|
||||
db := servicesDB().Find(&services).Order("order_id desc")
|
||||
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
|
||||
}
|
||||
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")
|
||||
rows, err := model.Rows()
|
||||
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() {
|
||||
var gd DateScan
|
||||
|
@ -284,7 +284,7 @@ func GraphDataRaw(service types.ServiceInterface, start, end time.Time, group st
|
|||
if CoreApp.DbConnection == "postgres" {
|
||||
createdTime, err = time.Parse(types.TIME_NANO, createdAt)
|
||||
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 {
|
||||
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 {
|
||||
data, err := json.Marshal(d.Array)
|
||||
if err != nil {
|
||||
utils.Log(2, err)
|
||||
log.Warnln(err)
|
||||
return "{}"
|
||||
}
|
||||
return string(data)
|
||||
|
@ -371,7 +371,7 @@ func (s *Service) Delete() error {
|
|||
i := s.index()
|
||||
err := servicesDB().Delete(s)
|
||||
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
|
||||
}
|
||||
s.Close()
|
||||
|
@ -386,7 +386,7 @@ func (s *Service) Delete() error {
|
|||
func (s *Service) Update(restart bool) error {
|
||||
err := servicesDB().Update(&s)
|
||||
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
|
||||
}
|
||||
// clear the notification queue for a service
|
||||
|
@ -413,7 +413,7 @@ func (s *Service) Create(check bool) (int64, error) {
|
|||
s.CreatedAt = time.Now()
|
||||
db := servicesDB().Create(s)
|
||||
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
|
||||
}
|
||||
s.Start()
|
||||
|
|
|
@ -17,6 +17,7 @@ package core
|
|||
|
||||
import (
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -24,7 +25,6 @@ import (
|
|||
|
||||
var (
|
||||
newServiceId int64
|
||||
newGroupId int64
|
||||
)
|
||||
|
||||
func TestSelectHTTPService(t *testing.T) {
|
||||
|
@ -118,7 +118,7 @@ func TestCheckTCPService(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)
|
||||
assert.Equal(t, float32(100), service.OnlineSince(since))
|
||||
service2 := SelectService(5)
|
||||
|
@ -134,7 +134,7 @@ func TestServiceSmallText(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)
|
||||
assert.NotEqual(t, "0.00", service.AvgUptime(since))
|
||||
service2 := SelectService(5)
|
||||
|
@ -256,10 +256,10 @@ func TestServiceFailedTCPCheck(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.",
|
||||
Method: "http",
|
||||
}}
|
||||
}
|
||||
service := SelectService(8)
|
||||
id, err := service.CreateFailure(fail)
|
||||
assert.Nil(t, err)
|
||||
|
@ -398,7 +398,7 @@ func TestService_TotalFailures24(t *testing.T) {
|
|||
|
||||
func TestService_TotalFailuresOnDate(t *testing.T) {
|
||||
t.SkipNow()
|
||||
ago := time.Now().UTC()
|
||||
ago := utils.Now().UTC()
|
||||
service := SelectService(8)
|
||||
failures, err := service.TotalFailuresOnDate(ago)
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -77,7 +77,7 @@ func (u *User) Create() (int64, error) {
|
|||
return 0, db.Error
|
||||
}
|
||||
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 u.Id, db.Error
|
||||
|
@ -88,7 +88,7 @@ func SelectAllUsers() ([]*User, error) {
|
|||
var users []*User
|
||||
db := usersDB().Find(&users)
|
||||
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 users, db.Error
|
||||
|
@ -99,7 +99,7 @@ func SelectAllUsers() ([]*User, error) {
|
|||
func AuthUser(username, password string) (*User, bool) {
|
||||
user, err := SelectUsername(username)
|
||||
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
|
||||
}
|
||||
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
|
||||
MAINTAINER "Hunter Long (https://github.com/hunterlong)"
|
||||
FROM golang:1.13-alpine as base
|
||||
LABEL maintainer="Hunter Long (https://github.com/hunterlong)"
|
||||
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
|
||||
|
|
|
@ -2496,7 +2496,7 @@ for application logging
|
|||
```go
|
||||
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
|
||||
|
||||
#### 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.
|
||||
//
|
||||
// Install Statping
|
||||
|
@ -12,7 +12,7 @@
|
|||
// brew install statping
|
||||
//
|
||||
// // Linux installation
|
||||
// bash <(curl -s https://assets.statping.com/install.sh)
|
||||
// curl -o- -L https://statping.com/install.sh | bash
|
||||
// statping version
|
||||
//
|
||||
// Docker
|
||||
|
|
2
go.mod
2
go.mod
|
@ -8,6 +8,7 @@ require (
|
|||
github.com/agnivade/levenshtein v1.0.2 // indirect
|
||||
github.com/ararog/timeago v0.0.0-20160328174124-e9969cf18b8d
|
||||
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-yaml/yaml v2.1.0+incompatible
|
||||
github.com/gorilla/mux v1.7.3
|
||||
|
@ -20,6 +21,7 @@ require (
|
|||
github.com/rendon/testcli v0.0.0-20161027181003-6283090d169f
|
||||
github.com/russross/blackfriday/v2 v2.0.1
|
||||
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/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
|
||||
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/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/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/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=
|
||||
|
@ -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/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/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/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=
|
||||
|
@ -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/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
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/stretchr/objx v0.1.0/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) {
|
||||
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{
|
||||
Status: "error",
|
||||
Error: err.Error(),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
@ -25,7 +26,7 @@ func (item Item) Expired() bool {
|
|||
if item.Expiration == 0 {
|
||||
return false
|
||||
}
|
||||
return time.Now().UnixNano() > item.Expiration
|
||||
return utils.Now().UnixNano() > item.Expiration
|
||||
}
|
||||
|
||||
//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()
|
||||
s.items[key] = Item{
|
||||
Content: content,
|
||||
Expiration: time.Now().Add(duration).UnixNano(),
|
||||
Expiration: utils.Now().Add(duration).UnixNano(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import (
|
|||
"github.com/hunterlong/statping/utils"
|
||||
"net"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func apiAllCheckinsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -81,7 +80,7 @@ func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
|
|||
hit := &types.CheckinHit{
|
||||
Checkin: checkin.Id,
|
||||
From: ip,
|
||||
CreatedAt: time.Now().UTC(),
|
||||
CreatedAt: utils.Now().UTC(),
|
||||
}
|
||||
checkinHit := core.ReturnCheckinHit(hit)
|
||||
if checkin.Last() == nil {
|
||||
|
@ -95,7 +94,7 @@ func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,6 @@ import (
|
|||
"github.com/hunterlong/statping/utils"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
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["admin"] = user.Admin.Bool
|
||||
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)
|
||||
} else {
|
||||
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-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")
|
||||
|
||||
if key && cert {
|
||||
utils.Log(1, "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("server.cert and server.key was found in root directory! Starting in SSL mode.")
|
||||
log.Infoln(fmt.Sprintf("Statping Secure HTTPS Server running on https://%v:%v", ip, 443))
|
||||
usingSSL = true
|
||||
} else {
|
||||
utils.Log(1, "Statping HTTP Server running on http://"+host)
|
||||
log.Infoln("Statping HTTP Server running on http://" + host)
|
||||
}
|
||||
|
||||
router = Router()
|
||||
|
@ -178,7 +178,7 @@ func loadTemplate(w http.ResponseWriter, r *http.Request) error {
|
|||
mainTemplate.Funcs(handlerFuncs(w, r))
|
||||
mainTemplate, err = mainTemplate.Parse(mainTmpl)
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
// render all templates
|
||||
|
@ -187,7 +187,7 @@ func loadTemplate(w http.ResponseWriter, r *http.Request) error {
|
|||
tmp, _ := source.TmplBox.String(temp)
|
||||
mainTemplate, err = mainTemplate.Parse(tmp)
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ func loadTemplate(w http.ResponseWriter, r *http.Request) error {
|
|||
tmp, _ := source.JsBox.String(temp)
|
||||
mainTemplate, err = mainTemplate.Parse(tmp)
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(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
|
||||
func ExecuteResponse(w http.ResponseWriter, r *http.Request, file string, data interface{}, redirect interface{}) {
|
||||
utils.Http(r)
|
||||
if url, ok := redirect.(string); ok {
|
||||
http.Redirect(w, r, url, http.StatusSeeOther)
|
||||
return
|
||||
|
@ -216,15 +215,15 @@ func ExecuteResponse(w http.ResponseWriter, r *http.Request, file string, data i
|
|||
loadTemplate(w, r)
|
||||
render, err := source.TmplBox.String(file)
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
}
|
||||
// render the page requested
|
||||
if _, err := mainTemplate.Parse(render); err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
}
|
||||
// execute the template
|
||||
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{}) {
|
||||
render, err := source.JsBox.String(file)
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
}
|
||||
if usingSSL {
|
||||
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 {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
}
|
||||
if err := t.Execute(w, data); err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,31 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/core"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"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
|
||||
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 redirect {
|
||||
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
|
||||
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 redirect {
|
||||
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
|
||||
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)
|
||||
w.Header().Set("Content-Type", contentType)
|
||||
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)
|
||||
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")
|
||||
return
|
||||
}
|
||||
|
|
|
@ -25,11 +25,11 @@ import (
|
|||
"github.com/hunterlong/statping/source"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
router *mux.Router
|
||||
log = utils.Log.WithField("type", "handlers")
|
||||
)
|
||||
|
||||
// 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("/setup", http.HandlerFunc(setupHandler)).Methods("GET")
|
||||
r.Handle("/setup", http.HandlerFunc(processSetupHandler)).Methods("POST")
|
||||
r.Handle("/dashboard", http.HandlerFunc(dashboardHandler)).Methods("GET")
|
||||
r.Handle("/dashboard", http.HandlerFunc(loginHandler)).Methods("POST")
|
||||
r.Handle("/logout", http.HandlerFunc(logoutHandler))
|
||||
r.Handle("/dashboard", sendLog(dashboardHandler)).Methods("GET")
|
||||
r.Handle("/dashboard", sendLog(loginHandler)).Methods("POST")
|
||||
r.Handle("/logout", sendLog(logoutHandler))
|
||||
r.Handle("/plugins/download/{name}", authenticated(pluginsDownloadHandler, true))
|
||||
r.Handle("/plugins/{name}/save", authenticated(pluginSavedHandler, true)).Methods("POST")
|
||||
r.Handle("/help", authenticated(helpHandler, true))
|
||||
|
@ -90,11 +90,11 @@ func Router() *mux.Router {
|
|||
// SERVICE Routes
|
||||
r.Handle("/services", authenticated(servicesHandler, 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}/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
|
||||
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/reorder/services", authenticated(reorderServiceHandler, 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}/ping", cached("30s", "application/json", http.HandlerFunc(apiServicePingDataHandler))).Methods("GET")
|
||||
r.Handle("/api/services/{id}/heatmap", cached("30s", "application/json", http.HandlerFunc(apiServiceHeatmapHandler))).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", apiServicePingDataHandler)).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(apiServiceDeleteHandler, false)).Methods("DELETE")
|
||||
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", authenticated(checkinCreateHandler, false)).Methods("POST")
|
||||
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
|
||||
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
|
||||
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.NotFoundHandler = http.HandlerFunc(error404Handler)
|
||||
r.NotFoundHandler = sendLog(error404Handler)
|
||||
return r
|
||||
}
|
||||
|
||||
|
@ -175,7 +175,7 @@ func resetRouter() {
|
|||
|
||||
func resetCookies() {
|
||||
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))
|
||||
} else {
|
||||
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("Cache-Control", "max-age=60")
|
||||
|
||||
end := time.Now().UTC()
|
||||
start := time.Now().Add((-24 * 7) * time.Hour).UTC()
|
||||
end := utils.Now().UTC()
|
||||
start := utils.Now().Add((-24 * 7) * time.Hour).UTC()
|
||||
var srvs []*core.Service
|
||||
for _, s := range services {
|
||||
srvs = append(srvs, s.(*core.Service))
|
||||
|
@ -94,7 +94,7 @@ func servicesViewHandler(w http.ResponseWriter, r *http.Request) {
|
|||
endField := utils.ToInt(fields.Get("end"))
|
||||
group := r.Form.Get("group")
|
||||
|
||||
end := time.Now().UTC()
|
||||
end := utils.Now().UTC()
|
||||
start := end.Add((-24 * 7) * time.Hour).UTC()
|
||||
|
||||
if startField != 0 {
|
||||
|
@ -243,7 +243,7 @@ func apiServiceHeatmapHandler(w http.ResponseWriter, r *http.Request) {
|
|||
var monthOutput []*dataXyMonth
|
||||
|
||||
start := service.CreatedAt
|
||||
//now := time.Now()
|
||||
//now := utils.Now()
|
||||
|
||||
sY, sM, _ := start.Date()
|
||||
|
||||
|
@ -252,10 +252,10 @@ func apiServiceHeatmapHandler(w http.ResponseWriter, r *http.Request) {
|
|||
month := int(sM)
|
||||
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() {
|
||||
maxMonth = int(time.Now().Month())
|
||||
if year == utils.Now().Year() {
|
||||
maxMonth = int(utils.Now().Month())
|
||||
}
|
||||
|
||||
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")
|
||||
core.CoreApp, err = core.UpdateCore(app)
|
||||
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())
|
||||
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) {
|
||||
dir := utils.Directory
|
||||
if err := source.CreateAllAssets(dir); err != nil {
|
||||
utils.Log(3, err)
|
||||
log.Errorln(err)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
if err := source.CompileSASS(dir); err != nil {
|
||||
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()
|
||||
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) {
|
||||
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()
|
||||
ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
|
||||
|
@ -115,7 +114,7 @@ func bulkImportHandler(w http.ResponseWriter, r *http.Request) {
|
|||
var fileData bytes.Buffer
|
||||
file, _, err := r.FormFile("file")
|
||||
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()))
|
||||
return
|
||||
}
|
||||
|
@ -129,17 +128,17 @@ func bulkImportHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
newService, err := commaToService(col)
|
||||
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
|
||||
}
|
||||
|
||||
service := core.ReturnService(newService)
|
||||
_, err = service.Create(true)
|
||||
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
|
||||
}
|
||||
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")
|
||||
|
|
|
@ -57,7 +57,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
domain := r.PostForm.Get("domain")
|
||||
email := r.PostForm.Get("email")
|
||||
sample := r.PostForm.Get("sample_data") == "on"
|
||||
utils.Log(2, sample)
|
||||
log.Warnln(sample)
|
||||
dir := utils.Directory
|
||||
|
||||
config := &core.DbConfig{
|
||||
|
@ -78,21 +78,21 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
if core.Configs, err = config.Save(); err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
config.Error = err
|
||||
setupResponseError(w, r, config)
|
||||
return
|
||||
}
|
||||
|
||||
if core.Configs, err = core.LoadConfigFile(dir); err != nil {
|
||||
utils.Log(3, err)
|
||||
log.Errorln(err)
|
||||
config.Error = err
|
||||
setupResponseError(w, r, config)
|
||||
return
|
||||
}
|
||||
|
||||
if err = core.Configs.Connect(false, dir); err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
core.DeleteConfig()
|
||||
config.Error = err
|
||||
setupResponseError(w, r, config)
|
||||
|
@ -104,7 +104,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
core.CoreApp, err = config.InsertCore()
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
log.Errorln(err)
|
||||
config.Error = err
|
||||
setupResponseError(w, r, config)
|
||||
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
|
||||
func (u *commandLine) OnTest() error {
|
||||
in, out, err := runCommand(u.Host, u.Var1)
|
||||
utils.Log(1, in)
|
||||
utils.Log(1, out)
|
||||
utils.Log.Infoln(in)
|
||||
utils.Log.Infoln(out)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
@ -216,7 +216,7 @@ func (u *email) Select() *notifier.Notification {
|
|||
|
||||
// OnSave triggers when this notifier has been saved
|
||||
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
|
||||
return nil
|
||||
}
|
||||
|
@ -235,7 +235,7 @@ func (u *email) OnTest() error {
|
|||
LastStatusCode: 200,
|
||||
Expected: types.NewNullString("test example"),
|
||||
LastResponse: "<html>this is an example response</html>",
|
||||
CreatedAt: time.Now().Add(-24 * time.Hour),
|
||||
CreatedAt: utils.Now().Add(-24 * time.Hour),
|
||||
}
|
||||
email := &emailOutgoing{
|
||||
To: u.Var2,
|
||||
|
@ -262,7 +262,7 @@ func (u *email) dialSend(email *emailOutgoing) error {
|
|||
m.SetHeader("Subject", email.Subject)
|
||||
m.SetBody("text/html", email.Source)
|
||||
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 nil
|
||||
|
@ -277,11 +277,11 @@ func emailTemplate(contents string, data interface{}) string {
|
|||
t := template.New("email")
|
||||
t, err := t.Parse(contents)
|
||||
if err != nil {
|
||||
utils.Log(3, err)
|
||||
utils.Log.Errorln(err)
|
||||
}
|
||||
var tpl bytes.Buffer
|
||||
if err := t.Execute(&tpl, data); err != nil {
|
||||
utils.Log(2, err)
|
||||
utils.Log.Warnln(err)
|
||||
}
|
||||
result := tpl.String()
|
||||
return result
|
||||
|
|
|
@ -85,7 +85,7 @@ func (u *lineNotifier) OnSuccess(s *types.Service) {
|
|||
// OnSave triggers when this notifier has been saved
|
||||
func (u *lineNotifier) OnSave() error {
|
||||
msg := fmt.Sprintf("Notification %v is receiving updated information.", u.Method)
|
||||
utils.Log(1, msg)
|
||||
utils.Log.Infoln(msg)
|
||||
u.AddQueue("saved", msg)
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -44,13 +44,13 @@ var TestService = &types.Service{
|
|||
LastStatusCode: 404,
|
||||
Online: true,
|
||||
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{
|
||||
Issue: "testing",
|
||||
Service: 1,
|
||||
CreatedAt: time.Now().Add(-12 * time.Hour),
|
||||
CreatedAt: utils.Now().Add(-12 * time.Hour),
|
||||
}
|
||||
|
||||
var TestUser = &types.User{
|
||||
|
|
|
@ -99,7 +99,7 @@ func (u *slack) OnFailure(s *types.Service, f *types.Failure) {
|
|||
message := slackMessage{
|
||||
Service: s,
|
||||
Template: failingTemplate,
|
||||
Time: time.Now().Unix(),
|
||||
Time: utils.Now().Unix(),
|
||||
Issue: f.Issue,
|
||||
}
|
||||
parseSlackMessage(s.Id, failingTemplate, message)
|
||||
|
@ -112,7 +112,7 @@ func (u *slack) OnSuccess(s *types.Service) {
|
|||
message := slackMessage{
|
||||
Service: s,
|
||||
Template: successTemplate,
|
||||
Time: time.Now().Unix(),
|
||||
Time: utils.Now().Unix(),
|
||||
}
|
||||
parseSlackMessage(s.Id, successTemplate, message)
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ func (u *telegram) OnSuccess(s *types.Service) {
|
|||
|
||||
// OnSave triggers when this notifier has been saved
|
||||
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
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ func (u *twilio) OnSuccess(s *types.Service) {
|
|||
|
||||
// OnSave triggers when this notifier has been saved
|
||||
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
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@ func replaceBodyText(body string, s *types.Service, f *types.Failure) string {
|
|||
}
|
||||
|
||||
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.Timeout = time.Duration(10 * time.Second)
|
||||
var buf *bytes.Buffer
|
||||
|
@ -136,7 +136,7 @@ func (w *webhooker) OnTest() error {
|
|||
}
|
||||
defer resp.Body.Close()
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ import (
|
|||
var (
|
||||
AllPlugins []*types.PluginObject
|
||||
dir string
|
||||
log = utils.Log.WithField("type", "plugin")
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -49,7 +50,7 @@ func init() {
|
|||
}
|
||||
|
||||
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)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -58,29 +59,29 @@ func LoadPlugin(file string) error {
|
|||
fSplit := strings.Split(f.Name(), "/")
|
||||
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, ".")
|
||||
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))
|
||||
}
|
||||
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)
|
||||
}
|
||||
plug, err := plugin.Open(file)
|
||||
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
|
||||
}
|
||||
symPlugin, err := plug.Lookup("Plugin")
|
||||
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
|
||||
}
|
||||
plugActions, ok := symPlugin.(types.PluginActions)
|
||||
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())
|
||||
}
|
||||
info := plugActions.GetInfo()
|
||||
|
@ -88,28 +89,28 @@ func LoadPlugin(file string) error {
|
|||
if err != nil {
|
||||
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)
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadPlugins() {
|
||||
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) {
|
||||
os.Mkdir(pluginDir, os.ModePerm)
|
||||
}
|
||||
files, err := ioutil.ReadDir(pluginDir)
|
||||
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
|
||||
}
|
||||
for _, f := range files {
|
||||
err := LoadPlugin(f.Name())
|
||||
if err != nil {
|
||||
utils.Log(3, err)
|
||||
log.Errorln(err)
|
||||
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 */
|
||||
HTML, BODY {
|
||||
background-color: #fcfcfc;
|
||||
padding-bottom: 10px; }
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 25px;
|
||||
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 {
|
||||
color: #464646; }
|
||||
color: #464646;
|
||||
}
|
||||
|
||||
.header-desc {
|
||||
color: #939393; }
|
||||
color: #939393;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border-radius: 0.2rem; }
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.online_list .badge {
|
||||
margin-top: 0.2rem; }
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-bottom: 30px; }
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
line-height: 1.3;
|
||||
font-size: 0.75rem; }
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.view_service_btn {
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
right: 40px; }
|
||||
right: 40px;
|
||||
}
|
||||
|
||||
.service_lower_info {
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
left: 40px;
|
||||
color: #d1ffca;
|
||||
font-size: 0.85rem; }
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.lg_number {
|
||||
font-size: 2.3rem;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
color: #4f4f4f; }
|
||||
color: #4f4f4f;
|
||||
}
|
||||
|
||||
.stats_area {
|
||||
text-align: center;
|
||||
color: #a5a5a5; }
|
||||
color: #a5a5a5;
|
||||
}
|
||||
|
||||
.lower_canvas {
|
||||
height: 3.4rem;
|
||||
|
@ -62,82 +74,101 @@ HTML, BODY {
|
|||
background-color: #48d338;
|
||||
padding: 15px 10px;
|
||||
margin-left: 0px !important;
|
||||
margin-right: 0px !important; }
|
||||
margin-right: 0px !important;
|
||||
}
|
||||
|
||||
.lower_canvas SPAN {
|
||||
font-size: 1rem;
|
||||
color: #fff; }
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-decoration: none;
|
||||
margin-top: 20px; }
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.footer A {
|
||||
color: #8d8d8d;
|
||||
text-decoration: none; }
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer A:HOVER {
|
||||
color: #6d6d6d; }
|
||||
color: #6d6d6d;
|
||||
}
|
||||
|
||||
.badge {
|
||||
color: white;
|
||||
border-radius: 0.2rem; }
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
height: 25px; }
|
||||
.btn-group A {
|
||||
padding: 0.1rem .75rem;
|
||||
font-size: 0.8rem; }
|
||||
height: 25px;
|
||||
}
|
||||
.btn-group A {
|
||||
padding: 0.1rem 0.75rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.card-body .badge {
|
||||
color: #fff; }
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link {
|
||||
border-radius: 0.2rem; }
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border-radius: 0.2rem; }
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.125); }
|
||||
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
overflow: hidden; }
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-body H4 A {
|
||||
color: #444444;
|
||||
text-decoration: none; }
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 170px;
|
||||
width: 100%;
|
||||
overflow: hidden; }
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.service-chart-container {
|
||||
position: relative;
|
||||
height: 400px;
|
||||
width: 100%; }
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.service-chart-heatmap {
|
||||
position: relative;
|
||||
height: 300px;
|
||||
width: 100%; }
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inputTags-field {
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
padding-top: .13rem; }
|
||||
padding-top: 0.13rem;
|
||||
}
|
||||
|
||||
input.inputTags-field:focus {
|
||||
outline-width: 0; }
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
.inputTags-list {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: calc(2.25rem + 2px);
|
||||
padding: .2rem .35rem;
|
||||
padding: 0.2rem 0.35rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
|
@ -145,8 +176,9 @@ input.inputTags-field:focus {
|
|||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: .25rem;
|
||||
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; }
|
||||
border-radius: 0.25rem;
|
||||
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.inputTags-item {
|
||||
background-color: #3aba39;
|
||||
|
@ -154,63 +186,81 @@ input.inputTags-field:focus {
|
|||
padding: 5px 8px;
|
||||
font-size: 10pt;
|
||||
color: white;
|
||||
border-radius: 4px; }
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.inputTags-item .close-item {
|
||||
margin-left: 6px;
|
||||
font-size: 13pt;
|
||||
font-weight: bold;
|
||||
cursor: pointer; }
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #3e9bff;
|
||||
border-color: #006fe6;
|
||||
color: white; }
|
||||
.btn-primary.dyn-dark {
|
||||
background-color: #32a825 !important;
|
||||
border-color: #2c9320 !important; }
|
||||
.btn-primary.dyn-light {
|
||||
background-color: #75de69 !important;
|
||||
border-color: #88e37e !important; }
|
||||
color: white;
|
||||
}
|
||||
.btn-primary.dyn-dark {
|
||||
background-color: #32a825 !important;
|
||||
border-color: #2c9320 !important;
|
||||
}
|
||||
.btn-primary.dyn-light {
|
||||
background-color: #75de69 !important;
|
||||
border-color: #88e37e !important;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background-color: #47d337; }
|
||||
.btn-success.dyn-dark {
|
||||
background-color: #32a825 !important;
|
||||
border-color: #2c9320 !important; }
|
||||
.btn-success.dyn-light {
|
||||
background-color: #75de69 !important;
|
||||
border-color: #88e37e !important; }
|
||||
background-color: #47d337;
|
||||
}
|
||||
.btn-success.dyn-dark {
|
||||
background-color: #32a825 !important;
|
||||
border-color: #2c9320 !important;
|
||||
}
|
||||
.btn-success.dyn-light {
|
||||
background-color: #75de69 !important;
|
||||
border-color: #88e37e !important;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: #dd3545; }
|
||||
.btn-danger.dyn-dark {
|
||||
background-color: #b61f2d !important;
|
||||
border-color: #a01b28 !important; }
|
||||
.btn-danger.dyn-light {
|
||||
background-color: #e66975 !important;
|
||||
border-color: #e97f89 !important; }
|
||||
background-color: #dd3545;
|
||||
}
|
||||
.btn-danger.dyn-dark {
|
||||
background-color: #b61f2d !important;
|
||||
border-color: #a01b28 !important;
|
||||
}
|
||||
.btn-danger.dyn-light {
|
||||
background-color: #e66975 !important;
|
||||
border-color: #e97f89 !important;
|
||||
}
|
||||
|
||||
.bg-success {
|
||||
background-color: #47d337 !important; }
|
||||
background-color: #47d337 !important;
|
||||
}
|
||||
|
||||
.bg-danger {
|
||||
background-color: #dd3545 !important; }
|
||||
background-color: #dd3545 !important;
|
||||
}
|
||||
|
||||
.bg-success .dyn-dark {
|
||||
background-color: #35b027 !important; }
|
||||
background-color: #35b027 !important;
|
||||
}
|
||||
|
||||
.bg-danger .dyn-dark {
|
||||
background-color: #bf202f !important; }
|
||||
background-color: #bf202f !important;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||
background-color: #13a00d; }
|
||||
background-color: #13a00d;
|
||||
}
|
||||
|
||||
.nav-pills A {
|
||||
color: #424242; }
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
.nav-pills I {
|
||||
margin-right: 10px; }
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
/* Bootstrap Settings */
|
||||
|
@ -230,23 +280,26 @@ input.inputTags-field:focus {
|
|||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
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 */
|
||||
font-family: monospace;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 80vh; }
|
||||
height: 80vh;
|
||||
}
|
||||
|
||||
.CodeMirror-focused {
|
||||
/* Bootstrap Settings */
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
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 {
|
||||
font-size: 1rem;
|
||||
position: relative; }
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
position: absolute;
|
||||
|
@ -257,7 +310,8 @@ input.inputTags-field:focus {
|
|||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
overflow: hidden;
|
||||
padding: 0; }
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.switch input + label {
|
||||
position: relative;
|
||||
|
@ -270,23 +324,26 @@ input.inputTags-field:focus {
|
|||
outline: none;
|
||||
user-select: none;
|
||||
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::after {
|
||||
content: '';
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: calc(calc(2.375rem * .8) * 2);
|
||||
bottom: 0;
|
||||
display: block; }
|
||||
display: block;
|
||||
}
|
||||
|
||||
.switch input + label::before {
|
||||
right: 0;
|
||||
background-color: #dee2e6;
|
||||
border-radius: calc(2.375rem * .8);
|
||||
transition: 0.2s all; }
|
||||
transition: 0.2s all;
|
||||
}
|
||||
|
||||
.switch input + label::after {
|
||||
top: 2px;
|
||||
|
@ -295,105 +352,137 @@ input.inputTags-field:focus {
|
|||
height: calc(calc(2.375rem * .8) - calc(2px * 2));
|
||||
border-radius: 50%;
|
||||
background-color: white;
|
||||
transition: 0.2s all; }
|
||||
transition: 0.2s all;
|
||||
}
|
||||
|
||||
.switch input:checked + label::before {
|
||||
background-color: #08d; }
|
||||
background-color: #08d;
|
||||
}
|
||||
|
||||
.switch input:checked + label::after {
|
||||
margin-left: calc(2.375rem * .8); }
|
||||
margin-left: calc(2.375rem * .8);
|
||||
}
|
||||
|
||||
.switch input:focus + label::before {
|
||||
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 {
|
||||
color: #868e96;
|
||||
cursor: not-allowed; }
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.switch input:disabled + label::before {
|
||||
background-color: #e9ecef; }
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
.switch.switch-sm {
|
||||
font-size: 0.875rem; }
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.switch.switch-sm input + label {
|
||||
min-width: calc(calc(1.9375rem * .8) * 2);
|
||||
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 {
|
||||
width: calc(calc(1.9375rem * .8) * 2); }
|
||||
width: calc(calc(1.9375rem * .8) * 2);
|
||||
}
|
||||
|
||||
.switch.switch-sm input + label::after {
|
||||
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 {
|
||||
margin-left: calc(1.9375rem * .8); }
|
||||
margin-left: calc(1.9375rem * .8);
|
||||
}
|
||||
|
||||
.switch.switch-lg {
|
||||
font-size: 1.25rem; }
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.switch.switch-lg input + label {
|
||||
min-width: calc(calc(3rem * .8) * 2);
|
||||
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 {
|
||||
width: calc(calc(3rem * .8) * 2); }
|
||||
width: calc(calc(3rem * .8) * 2);
|
||||
}
|
||||
|
||||
.switch.switch-lg input + label::after {
|
||||
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 {
|
||||
margin-left: calc(3rem * .8); }
|
||||
margin-left: calc(3rem * .8);
|
||||
}
|
||||
|
||||
.switch + .switch {
|
||||
margin-left: 1rem; }
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
@keyframes pulse_animation {
|
||||
0% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
30% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
40% {
|
||||
transform: scale(1.02); }
|
||||
transform: scale(1.02);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
60% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
70% {
|
||||
transform: scale(1.05); }
|
||||
transform: scale(1.05);
|
||||
}
|
||||
80% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1); } }
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
.pulse {
|
||||
animation-name: pulse_animation;
|
||||
animation-duration: 1500ms;
|
||||
transform-origin: 70% 70%;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear; }
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
@keyframes glow-grow {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
80% {
|
||||
opacity: 1; }
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(2);
|
||||
opacity: 0; } }
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.pulse-glow {
|
||||
animation-name: glow-grown;
|
||||
animation-duration: 100ms;
|
||||
transform-origin: 70% 30%;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear; }
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
.pulse-glow:before,
|
||||
.pulse-glow:after {
|
||||
|
@ -405,10 +494,12 @@ input.inputTags-field:focus {
|
|||
right: 2.15rem;
|
||||
border-radius: 0;
|
||||
box-shadow: 0 0 6px #47d337;
|
||||
animation: glow-grow 2s ease-out infinite; }
|
||||
animation: glow-grow 2s ease-out infinite;
|
||||
}
|
||||
|
||||
.sortable_drag {
|
||||
background-color: #0000000f; }
|
||||
background-color: #0000000f;
|
||||
}
|
||||
|
||||
.drag_icon {
|
||||
cursor: move;
|
||||
|
@ -422,112 +513,139 @@ input.inputTags-field:focus {
|
|||
margin-right: 5px;
|
||||
margin-left: -10px;
|
||||
text-align: center;
|
||||
color: #b1b1b1; }
|
||||
color: #b1b1b1;
|
||||
}
|
||||
|
||||
/* (Optional) Apply a "closed-hand" cursor during drag operation. */
|
||||
.drag_icon:active {
|
||||
cursor: grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing; }
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
|
||||
.switch_btn {
|
||||
float: right;
|
||||
margin: -1px 0px 0px 0px;
|
||||
display: block; }
|
||||
display: block;
|
||||
}
|
||||
|
||||
#start_container {
|
||||
position: absolute;
|
||||
z-index: 99999;
|
||||
margin-top: 20px; }
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#end_container {
|
||||
position: absolute;
|
||||
z-index: 99999;
|
||||
margin-top: 20px;
|
||||
right: 0; }
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer; }
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.jumbotron {
|
||||
background-color: white; }
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.toggle-service {
|
||||
font-size: 18pt;
|
||||
float: left;
|
||||
margin: 2px 3px 0 0;
|
||||
cursor: pointer; }
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
HTML, BODY {
|
||||
background-color: #fcfcfc; }
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
|
||||
.sm-container {
|
||||
margin-top: 0px !important;
|
||||
padding: 0 !important; }
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.list-group-item H5 {
|
||||
font-size: 0.9rem; }
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0px !important;
|
||||
padding-top: 15px !important; }
|
||||
padding-top: 15px !important;
|
||||
}
|
||||
|
||||
.group_header {
|
||||
margin-left: 15px; }
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-left: 0px;
|
||||
margin-top: 0px;
|
||||
width: 100%;
|
||||
margin-bottom: 0; }
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
line-height: 0.9rem;
|
||||
font-size: 0.65rem; }
|
||||
font-size: 0.65rem;
|
||||
}
|
||||
|
||||
.full-col-12 {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px; }
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 0;
|
||||
border-radius: 0rem;
|
||||
padding: 0;
|
||||
background-color: #ffffff; }
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
font-size: 10pt;
|
||||
padding: 0px 10px; }
|
||||
padding: 10px 10px;
|
||||
}
|
||||
|
||||
.lg_number {
|
||||
font-size: 7.8vw; }
|
||||
font-size: 7.8vw;
|
||||
}
|
||||
|
||||
.stats_area {
|
||||
margin-top: 1.5rem !important;
|
||||
margin-bottom: 1.5rem !important; }
|
||||
margin-bottom: 1.5rem !important;
|
||||
}
|
||||
|
||||
.stats_area .col-4 {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
font-size: 0.6rem; }
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
border-top: 1px solid #e4e4e4;
|
||||
border: 0px; }
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.list-group-item:first-child {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0; }
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-item:last-child {
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0; }
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-item P {
|
||||
font-size: 0.7rem; }
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.service-chart-container {
|
||||
height: 200px; } }
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=base.css.map */
|
||||
|
|
|
@ -67,7 +67,7 @@ func main() {
|
|||
Compiled string
|
||||
Pages map[string]string
|
||||
}{
|
||||
Timestamp: time.Now(),
|
||||
Timestamp: utils.Now(),
|
||||
URL: "ok",
|
||||
Compiled: compiled,
|
||||
Pages: newPages,
|
||||
|
|
|
@ -28,6 +28,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
log = utils.Log.WithField("type", "source")
|
||||
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'
|
||||
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")
|
||||
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)
|
||||
|
||||
stdout, stderr, err := utils.Command(command)
|
||||
|
||||
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")
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
utils.Log(3, fmt.Sprintf("Failed to compile assets with SASS %v", err))
|
||||
utils.Log(3, fmt.Sprintf("sh -c %v", command))
|
||||
log.Errorln(fmt.Sprintf("Failed to compile assets with SASS %v", err))
|
||||
log.Errorln(fmt.Sprintf("sh -c %v", command))
|
||||
return err
|
||||
}
|
||||
|
||||
utils.Log(1, fmt.Sprintf("out: %v | error: %v", stdout, stderr))
|
||||
utils.Log(1, "SASS Compiling is complete!")
|
||||
log.Infoln(fmt.Sprintf("out: %v | error: %v", stdout, stderr))
|
||||
log.Infoln("SASS Compiling is complete!")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -87,12 +88,12 @@ func UsingAssets(folder string) bool {
|
|||
return true
|
||||
} else {
|
||||
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)
|
||||
err := CompileSASS(folder)
|
||||
if err != nil {
|
||||
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
|
||||
|
@ -104,10 +105,10 @@ func UsingAssets(folder string) bool {
|
|||
// SaveAsset will save an asset to the '/assets/' folder.
|
||||
func SaveAsset(data []byte, folder, file string) error {
|
||||
location := folder + "/assets/" + file
|
||||
utils.Log(1, fmt.Sprintf("Saving %v", location))
|
||||
log.Infoln(fmt.Sprintf("Saving %v", location))
|
||||
err := utils.SaveFile(location, data)
|
||||
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 nil
|
||||
|
@ -117,7 +118,7 @@ func SaveAsset(data []byte, folder, file string) error {
|
|||
func OpenAsset(folder, file string) string {
|
||||
dat, err := ioutil.ReadFile(folder + "/assets/" + file)
|
||||
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 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
|
||||
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/js")
|
||||
MakePublicFolder(folder + "/assets/css")
|
||||
MakePublicFolder(folder + "/assets/scss")
|
||||
MakePublicFolder(folder + "/assets/font")
|
||||
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(FontBox, "font")
|
||||
CopyAllToPublic(CssBox, "css")
|
||||
|
@ -144,9 +145,9 @@ func CreateAllAssets(folder string) error {
|
|||
CopyToPublic(TmplBox, folder+"/assets/files", "swagger.json")
|
||||
CopyToPublic(TmplBox, folder+"/assets/files", "postman.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)
|
||||
utils.Log(1, "Statping assets have been inserted")
|
||||
log.Infoln("Statping assets have been inserted")
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -154,10 +155,10 @@ func CreateAllAssets(folder string) error {
|
|||
func DeleteAllAssets(folder string) error {
|
||||
err := os.RemoveAll(folder + "/assets")
|
||||
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
|
||||
}
|
||||
utils.Log(1, "Statping assets have been deleted")
|
||||
log.Infoln("Statping assets have been deleted")
|
||||
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
|
||||
func CopyToPublic(box *rice.Box, folder, file string) error {
|
||||
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)
|
||||
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
|
||||
}
|
||||
err = ioutil.WriteFile(assetFolder, []byte(base), 0744)
|
||||
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 nil
|
||||
|
@ -200,11 +201,11 @@ func CopyToPublic(box *rice.Box, folder, file string) error {
|
|||
|
||||
// MakePublicFolder will create a new folder
|
||||
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) {
|
||||
err = os.MkdirAll(folder, 0777)
|
||||
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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
<div class="jumbotron jumbotron-fluid">
|
||||
<div class="text-center">
|
||||
<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>
|
||||
{{end}}
|
||||
|
|
|
@ -47,7 +47,7 @@
|
|||
{{ if not .Services }}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
<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>
|
||||
<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>
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
</div>
|
||||
|
||||
<div class="col-12 mt-5">
|
||||
{{if ne (len (Groups false)) 0}}
|
||||
<h1 class="text-muted">Groups</h1>
|
||||
<table class="table">
|
||||
<thead>
|
||||
|
@ -67,6 +68,7 @@
|
|||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{end}}
|
||||
{{if Auth}}
|
||||
<h1 class="text-muted mt-5">Create Group</h1>
|
||||
{{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 (
|
||||
"fmt"
|
||||
"github.com/fatih/structs"
|
||||
"github.com/hunterlong/statping/types"
|
||||
Logger "github.com/sirupsen/logrus"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
"log"
|
||||
"net/http"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
logFile *os.File
|
||||
fmtLogs *log.Logger
|
||||
ljLogger *lumberjack.Logger
|
||||
LastLines []*LogRow
|
||||
LockLines sync.Mutex
|
||||
Log = Logger.StandardLogger()
|
||||
ljLogger *lumberjack.Logger
|
||||
LastLines []*LogRow
|
||||
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
|
||||
func createLog(dir string) error {
|
||||
var err error
|
||||
|
@ -46,11 +159,6 @@ func createLog(dir string) error {
|
|||
return err
|
||||
}
|
||||
}
|
||||
file, err := os.Create(dir + "/logs/statup.log")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -60,77 +168,42 @@ func InitLogs() error {
|
|||
if err != nil {
|
||||
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{
|
||||
Filename: Directory + "/logs/statup.log",
|
||||
Filename: Directory + logFilePath,
|
||||
MaxSize: 16,
|
||||
MaxBackups: 3,
|
||||
MaxBackups: 5,
|
||||
MaxAge: 28,
|
||||
}
|
||||
fmtLogs = log.New(logFile, "", log.Ldate|log.Ltime)
|
||||
log.SetOutput(ljLogger)
|
||||
rotate()
|
||||
mw := io.MultiWriter(os.Stdout, ljLogger)
|
||||
Log.SetOutput(mw)
|
||||
|
||||
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)
|
||||
return err
|
||||
}
|
||||
|
||||
func rotate() {
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, syscall.SIGHUP)
|
||||
go func() {
|
||||
for {
|
||||
<-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
|
||||
// CloseLogs will close the log file correctly on shutdown
|
||||
func CloseLogs() {
|
||||
ljLogger.Rotate()
|
||||
Log.Writer().Close()
|
||||
ljLogger.Close()
|
||||
}
|
||||
|
||||
func pushLastLine(line interface{}) {
|
||||
|
|
|
@ -34,6 +34,11 @@ func Timezoner(t time.Time, zone float32) time.Time {
|
|||
return timez
|
||||
}
|
||||
|
||||
// Now returns the UTC timestamp
|
||||
func Now() time.Time {
|
||||
return time.Now().UTC()
|
||||
}
|
||||
|
||||
// FormatDuration converts a time.Duration into a string
|
||||
func FormatDuration(d time.Duration) string {
|
||||
var out string
|
||||
|
|
|
@ -176,7 +176,7 @@ func FileExists(name string) bool {
|
|||
// DeleteFile will attempt to delete a file
|
||||
// DeleteFile("newfile.json")
|
||||
func DeleteFile(file string) error {
|
||||
Log(1, "deleting file: "+file)
|
||||
Log.Infoln("deleting file: " + file)
|
||||
err := os.Remove(file)
|
||||
if err != nil {
|
||||
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
|
||||
// in, out, err := Command("sass assets/scss assets/css/base.css")
|
||||
func Command(cmd string) (string, string, error) {
|
||||
Log(1, "running command: "+cmd)
|
||||
Log.Infoln("running command: " + cmd)
|
||||
testCmd := exec.Command("sh", "-c", cmd)
|
||||
var stdout, stderr []byte
|
||||
var errStdout, errStderr error
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"net/http"
|
||||
|
@ -42,7 +41,8 @@ func TestCreateLog(t *testing.T) {
|
|||
|
||||
func TestInitLogs(t *testing.T) {
|
||||
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) {
|
||||
|
@ -57,15 +57,6 @@ func TestCommand(t *testing.T) {
|
|||
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) {
|
||||
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) {
|
||||
req, err := http.NewRequest("GET", "/", nil)
|
||||
assert.Nil(t, err)
|
||||
assert.NotEmpty(t, Http(req))
|
||||
assert.NotNil(t, req)
|
||||
}
|
||||
|
||||
func TestStringInt(t *testing.T) {
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.80.66
|
||||
0.80.67
|
||||
|
|
Loading…
Reference in New Issue