mirror of https://github.com/statping/statping
comments - core updates - db connection close fix
parent
e8b745a46c
commit
0e364b0aac
|
@ -1,4 +1,4 @@
|
||||||
FROM hunterlong/statup:base-v0.56
|
FROM hunterlong/statup:base-v0.57
|
||||||
MAINTAINER "Hunter Long (https://github.com/hunterlong)"
|
MAINTAINER "Hunter Long (https://github.com/hunterlong)"
|
||||||
|
|
||||||
# Locked version of Statup for 'latest' Docker tag
|
# Locked version of Statup for 'latest' Docker tag
|
||||||
|
|
|
@ -284,14 +284,14 @@
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
branch = "master"
|
branch = "master"
|
||||||
digest = "1:7f4a61b989d94774dc61016b660cf8347f59eb0bed91a10b2f23fc72a38d45d4"
|
digest = "1:374fc90fcb026e9a367e3fad29e988e5dd944b68ca3f24a184d77abc5307dda4"
|
||||||
name = "golang.org/x/sys"
|
name = "golang.org/x/sys"
|
||||||
packages = [
|
packages = [
|
||||||
"unix",
|
"unix",
|
||||||
"windows",
|
"windows",
|
||||||
]
|
]
|
||||||
pruneopts = "UT"
|
pruneopts = "UT"
|
||||||
revision = "ebe1bf3edb3325c393447059974de898d5133eb8"
|
revision = "d0be0721c37eeb5299f245a996a483160fc36940"
|
||||||
|
|
||||||
[[projects]]
|
[[projects]]
|
||||||
digest = "1:c25289f43ac4a68d88b02245742347c94f1e108c534dda442188015ff80669b3"
|
digest = "1:c25289f43ac4a68d88b02245742347c94f1e108c534dda442188015ff80669b3"
|
||||||
|
|
157
Makefile
157
Makefile
|
@ -1,4 +1,4 @@
|
||||||
VERSION=0.56
|
VERSION=0.57
|
||||||
BINARY_NAME=statup
|
BINARY_NAME=statup
|
||||||
GOPATH:=$(GOPATH)
|
GOPATH:=$(GOPATH)
|
||||||
GOCMD=go
|
GOCMD=go
|
||||||
|
@ -14,127 +14,180 @@ PUBLISH_BODY='{ "request": { "branch": "master", "config": { "env": { "VERSION":
|
||||||
TRAVIS_BUILD_CMD='{ "request": { "branch": "master", "message": "Compile master for Statup v$(VERSION)", "config": { "os": [ "linux" ], "language": "go", "go": [ "1.10.x" ], "go_import_path": "github.com/hunterlong/statup", "install": true, "sudo": "required", "services": [ "docker" ], "env": { "VERSION": "$(VERSION)" }, "matrix": { "allow_failures": [ { "go": "master" } ], "fast_finish": true }, "before_deploy": [ "git config --local user.name \"hunterlong\"", "git config --local user.email \"info@socialeck.com\"", "make tag" ], "deploy": [ { "provider": "releases", "api_key": "$(GH_TOKEN)", "file": [ "build/statup-osx-x64.tar.gz", "build/statup-osx-x32.tar.gz", "build/statup-linux-x64.tar.gz", "build/statup-linux-x32.tar.gz", "build/statup-linux-arm64.tar.gz", "build/statup-linux-arm7.tar.gz", "build/statup-linux-alpine.tar.gz", "build/statup-windows-x64.zip" ], "skip_cleanup": true } ], "notifications": { "email": false }, "before_script": ["gem install sass"], "script": [ "travis_wait 30 docker pull karalabe/xgo-latest", "make release" ], "after_success": [], "after_deploy": [ "make publish-dev" ] } } }'
|
TRAVIS_BUILD_CMD='{ "request": { "branch": "master", "message": "Compile master for Statup v$(VERSION)", "config": { "os": [ "linux" ], "language": "go", "go": [ "1.10.x" ], "go_import_path": "github.com/hunterlong/statup", "install": true, "sudo": "required", "services": [ "docker" ], "env": { "VERSION": "$(VERSION)" }, "matrix": { "allow_failures": [ { "go": "master" } ], "fast_finish": true }, "before_deploy": [ "git config --local user.name \"hunterlong\"", "git config --local user.email \"info@socialeck.com\"", "make tag" ], "deploy": [ { "provider": "releases", "api_key": "$(GH_TOKEN)", "file": [ "build/statup-osx-x64.tar.gz", "build/statup-osx-x32.tar.gz", "build/statup-linux-x64.tar.gz", "build/statup-linux-x32.tar.gz", "build/statup-linux-arm64.tar.gz", "build/statup-linux-arm7.tar.gz", "build/statup-linux-alpine.tar.gz", "build/statup-windows-x64.zip" ], "skip_cleanup": true } ], "notifications": { "email": false }, "before_script": ["gem install sass"], "script": [ "travis_wait 30 docker pull karalabe/xgo-latest", "make release" ], "after_success": [], "after_deploy": [ "make publish-dev" ] } } }'
|
||||||
TEST_DIR=$(GOPATH)/src/github.com/hunterlong/statup
|
TEST_DIR=$(GOPATH)/src/github.com/hunterlong/statup
|
||||||
|
|
||||||
all: dev-deps compile install test-all
|
# build and compile Statup and then test
|
||||||
|
all: dev-deps compile install test-all docker-build-all
|
||||||
|
|
||||||
|
# build all arch's and release Statup
|
||||||
release: dev-deps build-all compress
|
release: dev-deps build-all compress
|
||||||
|
|
||||||
test-all: dev-deps test cypress-test
|
# test all versions of Statup, golang testing and then cypress UI testing
|
||||||
|
test-all: dev-deps test
|
||||||
|
|
||||||
travis-test: dev-deps cypress-install test docker-test cypress-test coverage
|
# test all versions of Statup, golang testing and then cypress UI testing
|
||||||
|
test-ui: dev-deps docker-build-dev cypress-test
|
||||||
|
|
||||||
docker-build-all: docker-build-base docker-dev docker
|
# testing to be ran on travis ci
|
||||||
|
travis-test: dev-deps cypress-install test coverage
|
||||||
|
|
||||||
docker-publish-all: docker-push-base docker-push-dev docker-push-latest
|
# build and compile all arch's for Statup
|
||||||
|
build-all: build-mac build-linux build-windows build-alpine compress
|
||||||
|
|
||||||
seed:
|
# build all docker tags
|
||||||
rm -f statup.db
|
docker-build-all: docker-build-base docker-build-dev docker-build-latest
|
||||||
cat dev/seed.sql | sqlite3 statup.db
|
|
||||||
|
|
||||||
|
# push all docker tags built
|
||||||
|
docker-publish-all: docker-push-base docker-push-dev docker-push-latest docker-push-cypress
|
||||||
|
|
||||||
|
# build Statup for local arch
|
||||||
build: compile
|
build: compile
|
||||||
$(GOBUILD) $(BUILDVERSION) -o $(BINARY_NAME) -v ./cmd
|
$(GOBUILD) $(BUILDVERSION) -o $(BINARY_NAME) -v ./cmd
|
||||||
|
|
||||||
|
# build Statup plugins
|
||||||
build-plugin:
|
build-plugin:
|
||||||
$(GOBUILD) $(BUILDVERSION) -buildmode=plugin -o $(BINARY_NAME) -v ./dev/plugin
|
$(GOBUILD) $(BUILDVERSION) -buildmode=plugin -o $(BINARY_NAME) -v ./dev/plugin
|
||||||
|
|
||||||
|
# build Statup debug app
|
||||||
build-debug: compile
|
build-debug: compile
|
||||||
$(GOBUILD) $(BUILDVERSION) -tags debug -o $(BINARY_NAME) -v ./cmd
|
$(GOBUILD) $(BUILDVERSION) -tags debug -o $(BINARY_NAME) -v ./cmd
|
||||||
|
|
||||||
|
# install Statup for local arch and move binary to gopath/src/bin/statup
|
||||||
install: build
|
install: build
|
||||||
mv $(BINARY_NAME) $(GOPATH)/bin/$(BINARY_NAME)
|
mv $(BINARY_NAME) $(GOPATH)/bin/$(BINARY_NAME)
|
||||||
$(GOPATH)/bin/$(BINARY_NAME) version
|
$(GOPATH)/bin/$(BINARY_NAME) version
|
||||||
|
|
||||||
|
# run Statup from local arch
|
||||||
run: build
|
run: build
|
||||||
./$(BINARY_NAME) --ip 0.0.0.0 --port 8080
|
./$(BINARY_NAME) --ip 0.0.0.0 --port 8080
|
||||||
|
|
||||||
|
# compile assets using SASS and Rice. compiles scss -> css, and run rice embed-go
|
||||||
compile:
|
compile:
|
||||||
cd source && $(GOPATH)/bin/rice embed-go
|
cd source && $(GOPATH)/bin/rice embed-go
|
||||||
sass source/scss/base.scss source/css/base.css
|
sass source/scss/base.scss source/css/base.css
|
||||||
rm -rf .sass-cache
|
rm -rf .sass-cache
|
||||||
|
|
||||||
|
# benchmark testing
|
||||||
benchmark:
|
benchmark:
|
||||||
cd handlers && go test -v -run=^$ -bench=. -benchtime=5s -memprofile=prof.mem -cpuprofile=prof.cpu
|
cd handlers && go test -v -run=^$ -bench=. -benchtime=5s -memprofile=prof.mem -cpuprofile=prof.cpu
|
||||||
|
|
||||||
|
# view benchmark testing using pprof
|
||||||
benchmark-view:
|
benchmark-view:
|
||||||
go tool pprof handlers/handlers.test handlers/prof.cpu > top20
|
go tool pprof handlers/handlers.test handlers/prof.cpu > top20
|
||||||
|
|
||||||
|
# test Statup golang tetsing files
|
||||||
test: clean compile install
|
test: clean compile install
|
||||||
STATUP_DIR=$(TEST_DIR) go test -v -p=1 $(BUILDVERSION) -coverprofile=coverage.out ./...
|
STATUP_DIR=$(TEST_DIR) go test -v -p=1 $(BUILDVERSION) -coverprofile=coverage.out ./...
|
||||||
gocov convert coverage.out > coverage.json
|
gocov convert coverage.out > coverage.json
|
||||||
|
|
||||||
|
# report coverage to Coveralls
|
||||||
coverage:
|
coverage:
|
||||||
$(GOPATH)/bin/goveralls -coverprofile=coverage.out -service=travis -repotoken $(COVERALLS)
|
$(GOPATH)/bin/goveralls -coverprofile=coverage.out -service=travis -repotoken $(COVERALLS)
|
||||||
|
|
||||||
|
# generate documentation for Statup functions
|
||||||
docs:
|
docs:
|
||||||
godoc2md github.com/hunterlong/statup > servers/docs/README.md
|
godoc2md github.com/hunterlong/statup > servers/docs/README.md
|
||||||
gocov-html coverage.json > servers/docs/COVERAGE.html
|
gocov-html coverage.json > servers/docs/COVERAGE.html
|
||||||
revive -formatter stylish > servers/docs/LINT.md
|
revive -formatter stylish > servers/docs/LINT.md
|
||||||
|
|
||||||
build-all: clean compile
|
#
|
||||||
|
# Build binary for Statup
|
||||||
|
#
|
||||||
|
|
||||||
|
# build Statup for Mac, 64 and 32 bit
|
||||||
|
build-mac: compile
|
||||||
mkdir build
|
mkdir build
|
||||||
$(XGO) $(BUILDVERSION) --targets=darwin/amd64 ./cmd
|
$(XGO) $(BUILDVERSION) --targets=darwin/amd64 ./cmd
|
||||||
$(XGO) $(BUILDVERSION) --targets=darwin/386 ./cmd
|
$(XGO) $(BUILDVERSION) --targets=darwin/386 ./cmd
|
||||||
|
|
||||||
|
# build Statup for Linux 64, 32 bit, arm6/arm7
|
||||||
|
build-linux: compile
|
||||||
|
mkdir build
|
||||||
$(XGO) $(BUILDVERSION) --targets=linux/amd64 ./cmd
|
$(XGO) $(BUILDVERSION) --targets=linux/amd64 ./cmd
|
||||||
$(XGO) $(BUILDVERSION) --targets=linux/386 ./cmd
|
$(XGO) $(BUILDVERSION) --targets=linux/386 ./cmd
|
||||||
$(XGO) $(BUILDVERSION) --targets=windows-6.0/amd64 ./cmd
|
|
||||||
$(XGO) $(BUILDVERSION) --targets=linux/arm-7 ./cmd
|
$(XGO) $(BUILDVERSION) --targets=linux/arm-7 ./cmd
|
||||||
$(XGO) $(BUILDVERSION) --targets=linux/arm64 ./cmd
|
$(XGO) $(BUILDVERSION) --targets=linux/arm64 ./cmd
|
||||||
$(XGO) --targets=linux/amd64 -ldflags="-X main.VERSION=$(VERSION) -X main.COMMIT=$(TRAVIS_COMMIT) -linkmode external -extldflags -static" -out alpine ./cmd
|
|
||||||
|
|
||||||
build-alpine: clean compile
|
# build for windows 64 bit only
|
||||||
|
build-windows: compile
|
||||||
|
mkdir build
|
||||||
|
$(XGO) $(BUILDVERSION) --targets=windows-6.0/amd64 ./cmd
|
||||||
|
|
||||||
|
# build Alpine linux binary (used in docker images)
|
||||||
|
build-alpine: compile
|
||||||
mkdir build
|
mkdir build
|
||||||
$(XGO) --targets=linux/amd64 -ldflags="-X main.VERSION=$(VERSION) -X main.COMMIT=$(TRAVIS_COMMIT) -linkmode external -extldflags -static" -out alpine ./cmd
|
$(XGO) --targets=linux/amd64 -ldflags="-X main.VERSION=$(VERSION) -X main.COMMIT=$(TRAVIS_COMMIT) -linkmode external -extldflags -static" -out alpine ./cmd
|
||||||
|
|
||||||
docker:
|
|
||||||
docker build --no-cache -t hunterlong/statup:latest .
|
|
||||||
|
|
||||||
docker-run: docker
|
#
|
||||||
docker run -it -p 8080:8080 hunterlong/statup:latest
|
# Docker Makefile commands
|
||||||
|
#
|
||||||
|
|
||||||
docker-dev: clean docker-build-base
|
# build :latest docker tag
|
||||||
|
docker-build-latest: docker-build-base
|
||||||
|
docker build -t hunterlong/statup:latest --no-cache -f Dockerfile .
|
||||||
|
|
||||||
|
# build :dev docker tag
|
||||||
|
docker-build-dev: docker-build-base
|
||||||
docker build -t hunterlong/statup:dev --no-cache -f dev/Dockerfile-dev .
|
docker build -t hunterlong/statup:dev --no-cache -f dev/Dockerfile-dev .
|
||||||
|
|
||||||
docker-push-dev:
|
# build :base and base-v{VERSION} docker tag
|
||||||
docker push hunterlong/statup:dev
|
docker-build-base: clean
|
||||||
|
|
||||||
docker-push-cypress:
|
|
||||||
docker push hunterlong/statup:cypress
|
|
||||||
|
|
||||||
docker-push-latest: docker
|
|
||||||
docker push hunterlong/statup:latest
|
|
||||||
|
|
||||||
docker-run-dev: docker-dev
|
|
||||||
docker run -t -p 8080:8080 hunterlong/statup:dev
|
|
||||||
|
|
||||||
docker-cypress: clean
|
|
||||||
GOPATH=$(GOPATH) xgo -out statup -go 1.10.x -ldflags "-X main.VERSION=$(VERSION) -X main.COMMIT=$(TRAVIS_COMMIT)" --targets=linux/amd64 ./cmd
|
|
||||||
docker build -t hunterlong/statup:cypress -f dev/Dockerfile-cypress .
|
|
||||||
rm -f statup
|
|
||||||
|
|
||||||
docker-run-cypress: docker-cypress
|
|
||||||
docker run -t hunterlong/statup:cypress
|
|
||||||
|
|
||||||
docker-push-base:
|
|
||||||
docker tag hunterlong/statup:base hunterlong/statup:base-v$(VERSION)
|
|
||||||
docker push hunterlong/statup:base
|
|
||||||
docker push hunterlong/statup:base-v$(VERSION)
|
|
||||||
|
|
||||||
docker-build-base:
|
|
||||||
wget -q https://assets.statup.io/sass && chmod +x sass
|
wget -q https://assets.statup.io/sass && chmod +x sass
|
||||||
$(XGO) --targets=linux/amd64 -ldflags="-X main.VERSION=$(VERSION) -linkmode external -extldflags -static" -out alpine ./cmd
|
$(XGO) --targets=linux/amd64 -ldflags="-X main.VERSION=$(VERSION) -linkmode external -extldflags -static" -out alpine ./cmd
|
||||||
docker build -t hunterlong/statup:base --no-cache -f dev/Dockerfile-base .
|
docker build -t hunterlong/statup:base --no-cache -f dev/Dockerfile-base .
|
||||||
docker tag hunterlong/statup:base hunterlong/statup:base-v$(VERSION)
|
docker tag hunterlong/statup:base hunterlong/statup:base-v$(VERSION)
|
||||||
|
|
||||||
docker-build-latest:
|
# build Cypress UI testing :cypress docker tag
|
||||||
docker build -t hunterlong/statup:latest --no-cache -f Dockerfile .
|
docker-build-cypress: clean
|
||||||
|
GOPATH=$(GOPATH) xgo -out statup -go 1.10.x -ldflags "-X main.VERSION=$(VERSION) -X main.COMMIT=$(TRAVIS_COMMIT)" --targets=linux/amd64 ./cmd
|
||||||
|
docker build -t hunterlong/statup:cypress -f dev/Dockerfile-cypress .
|
||||||
|
rm -f statup
|
||||||
|
|
||||||
|
# run hunterlong/statup:latest docker image
|
||||||
|
docker-run: docker-build-latest
|
||||||
|
docker run -it -p 8080:8080 hunterlong/statup:latest
|
||||||
|
|
||||||
|
# run hunterlong/statup:dev docker image
|
||||||
|
docker-run-dev: docker-build-dev
|
||||||
|
docker run -t -p 8080:8080 hunterlong/statup:dev
|
||||||
|
|
||||||
|
# run Cypress UI testing, hunterlong/statup:cypress docker image
|
||||||
|
docker-run-cypress: docker-build-cypress
|
||||||
|
docker run -t hunterlong/statup:cypress
|
||||||
|
|
||||||
|
# push the :dev tag to Docker hub
|
||||||
|
docker-push-dev:
|
||||||
|
docker push hunterlong/statup:dev
|
||||||
|
|
||||||
|
# push the :cypress tag to Docker hub
|
||||||
|
docker-push-cypress:
|
||||||
|
docker push hunterlong/statup:cypress
|
||||||
|
|
||||||
|
# push the :latest tag to Docker hub
|
||||||
|
docker-push-latest: docker
|
||||||
|
docker push hunterlong/statup:latest
|
||||||
|
|
||||||
|
# push the :base and :base-v{VERSION} tag to Docker hub
|
||||||
|
docker-push-base: docker-build-base
|
||||||
|
docker tag hunterlong/statup:base hunterlong/statup:base-v$(VERSION)
|
||||||
|
docker push hunterlong/statup:base
|
||||||
|
docker push hunterlong/statup:base-v$(VERSION)
|
||||||
|
|
||||||
|
# create Postgres, and MySQL instance using Docker (used for testing)
|
||||||
databases:
|
databases:
|
||||||
docker run --name statup_postgres -p 5432:5432 -e POSTGRES_PASSWORD=password123 -e POSTGRES_USER=root -e POSTGRES_DB=root -d postgres
|
docker run --name statup_postgres -p 5432:5432 -e POSTGRES_PASSWORD=password123 -e POSTGRES_USER=root -e POSTGRES_DB=root -d postgres
|
||||||
docker run --name statup_mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password123 -e MYSQL_DATABASE=root -d mysql
|
docker run --name statup_mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=password123 -e MYSQL_DATABASE=root -d mysql
|
||||||
sleep 30
|
sleep 30
|
||||||
|
|
||||||
|
|
||||||
|
#
|
||||||
|
# Download and Install dependencies
|
||||||
|
#
|
||||||
|
|
||||||
|
# run dep to install all required golang dependecies
|
||||||
dep:
|
dep:
|
||||||
dep ensure -vendor-only
|
dep ensure -vendor-only
|
||||||
|
|
||||||
|
# install all required golang dependecies
|
||||||
dev-deps: dep
|
dev-deps: dep
|
||||||
$(GOGET) -u github.com/jinzhu/gorm/...
|
$(GOGET) -u github.com/jinzhu/gorm/...
|
||||||
$(GOGET) github.com/stretchr/testify/assert
|
$(GOGET) github.com/stretchr/testify/assert
|
||||||
|
@ -153,6 +206,7 @@ dev-deps: dep
|
||||||
$(GOCMD) install gopkg.in/matm/v1/gocov-html
|
$(GOCMD) install gopkg.in/matm/v1/gocov-html
|
||||||
$(GOCMD) get github.com/mgechev/revive
|
$(GOCMD) get github.com/mgechev/revive
|
||||||
|
|
||||||
|
# remove files for a clean compile/build
|
||||||
clean:
|
clean:
|
||||||
rm -rf ./{logs,assets,plugins,statup.db,config.yml,.sass-cache,config.yml,statup,build,.sass-cache,statup.db,index.html,vendor}
|
rm -rf ./{logs,assets,plugins,statup.db,config.yml,.sass-cache,config.yml,statup,build,.sass-cache,statup.db,index.html,vendor}
|
||||||
rm -rf cmd/{logs,assets,plugins,statup.db,config.yml,.sass-cache,*.log}
|
rm -rf cmd/{logs,assets,plugins,statup.db,config.yml,.sass-cache,*.log}
|
||||||
|
@ -169,9 +223,11 @@ clean:
|
||||||
find . -name "*.mem" -type f -delete
|
find . -name "*.mem" -type f -delete
|
||||||
find . -name "*.test" -type f -delete
|
find . -name "*.test" -type f -delete
|
||||||
|
|
||||||
|
# tag version using git
|
||||||
tag:
|
tag:
|
||||||
git tag "v$(VERSION)" --force
|
git tag "v$(VERSION)" --force
|
||||||
|
|
||||||
|
# compress built binaries into tar.gz and zip formats
|
||||||
compress:
|
compress:
|
||||||
cd build && mv alpine-linux-amd64 $(BINARY_NAME)
|
cd build && mv alpine-linux-amd64 $(BINARY_NAME)
|
||||||
cd build && tar -czvf $(BINARY_NAME)-linux-alpine.tar.gz $(BINARY_NAME) && rm -f $(BINARY_NAME)
|
cd build && tar -czvf $(BINARY_NAME)-linux-alpine.tar.gz $(BINARY_NAME) && rm -f $(BINARY_NAME)
|
||||||
|
@ -190,26 +246,33 @@ compress:
|
||||||
cd build && mv cmd-linux-arm64 $(BINARY_NAME)
|
cd build && mv cmd-linux-arm64 $(BINARY_NAME)
|
||||||
cd build && tar -czvf $(BINARY_NAME)-linux-arm64.tar.gz $(BINARY_NAME) && rm -f $(BINARY_NAME)
|
cd build && tar -czvf $(BINARY_NAME)-linux-arm64.tar.gz $(BINARY_NAME) && rm -f $(BINARY_NAME)
|
||||||
|
|
||||||
|
# push the :dev docker tag using curl
|
||||||
publish-dev:
|
publish-dev:
|
||||||
curl -H "Content-Type: application/json" --data '{"docker_tag": "dev"}' -X POST $(DOCKER)
|
curl -H "Content-Type: application/json" --data '{"docker_tag": "dev"}' -X POST $(DOCKER)
|
||||||
|
|
||||||
|
# push the :latest docker tag using curl
|
||||||
publish-latest:
|
publish-latest:
|
||||||
curl -H "Content-Type: application/json" --data '{"docker_tag": "latest"}' -X POST $(DOCKER)
|
curl -H "Content-Type: application/json" --data '{"docker_tag": "latest"}' -X POST $(DOCKER)
|
||||||
|
|
||||||
|
# update the homebrew application to latest for mac
|
||||||
publish-homebrew:
|
publish-homebrew:
|
||||||
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token $(TRAVIS_API)" -d $(PUBLISH_BODY) https://api.travis-ci.com/repo/hunterlong%2Fhomebrew-statup/requests
|
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token $(TRAVIS_API)" -d $(PUBLISH_BODY) https://api.travis-ci.com/repo/hunterlong%2Fhomebrew-statup/requests
|
||||||
|
|
||||||
|
# install NPM reuqirements for cypress testing
|
||||||
cypress-install:
|
cypress-install:
|
||||||
cd dev/test && npm install
|
cd dev/test && npm install
|
||||||
|
|
||||||
|
# run Cypress UI testing
|
||||||
cypress-test: clean cypress-install
|
cypress-test: clean cypress-install
|
||||||
cd dev/test && npm test
|
cd dev/test && npm test
|
||||||
|
|
||||||
|
# build Statup using a travis ci trigger
|
||||||
travis-build:
|
travis-build:
|
||||||
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token $(TRAVIS_API)" -d $(TRAVIS_BUILD_CMD) https://api.travis-ci.com/repo/hunterlong%2Fstatup/requests
|
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token $(TRAVIS_API)" -d $(TRAVIS_BUILD_CMD) https://api.travis-ci.com/repo/hunterlong%2Fstatup/requests
|
||||||
|
|
||||||
|
# install xgo and pull the xgo docker image
|
||||||
xgo-install: clean
|
xgo-install: clean
|
||||||
go get github.com/karalabe/xgo
|
go get github.com/karalabe/xgo
|
||||||
docker pull karalabe/xgo-latest
|
docker pull karalabe/xgo-latest
|
||||||
|
|
||||||
.PHONY: build build-all build-alpine test-all test
|
.PHONY: all build build-all build-alpine test-all test
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
# Statup - Status Page & Monitoring Server
|
# Statup - Status Page & Monitoring Server
|
||||||
An easy to use Status Page for your websites and applications. Statup will automatically fetch the application and render a beautiful status page with tons of features for you to build an even better status page. This Status Page generator allows you to use MySQL, Postgres, or SQLite on multiple operating systems.
|
An easy to use Status Page for your websites and applications. Statup will automatically fetch the application and render a beautiful status page with tons of features for you to build an even better status page. This Status Page generator allows you to use MySQL, Postgres, or SQLite on multiple operating systems.
|
||||||
|
|
||||||
|
[](https://gitter.im/statup-app/general)
|
||||||
|
|
||||||
## A Future-Proof Status Page
|
## A Future-Proof Status Page
|
||||||
Statup strives to remain future-proof and remain intact if a failure is created. Your Statup service should not be running on the same instance you're trying to monitor. If your server crashes your Status Page should still remaining online to notify your users of downtime.
|
Statup strives to remain future-proof and remain intact if a failure is created. Your Statup service should not be running on the same instance you're trying to monitor. If your server crashes your Status Page should still remaining online to notify your users of downtime.
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ const (
|
||||||
POINT = " "
|
POINT = " "
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CatchCLI will run functions based on the commands sent to Statup
|
||||||
func CatchCLI(args []string) error {
|
func CatchCLI(args []string) error {
|
||||||
dir := utils.Directory
|
dir := utils.Directory
|
||||||
utils.InitLogs()
|
utils.InitLogs()
|
||||||
|
@ -129,6 +130,7 @@ func CatchCLI(args []string) error {
|
||||||
return errors.New("end")
|
return errors.New("end")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RunOnce will initialize the Statup application and check each service 1 time, will not run HTTP server
|
||||||
func RunOnce() {
|
func RunOnce() {
|
||||||
var err error
|
var err error
|
||||||
core.Configs, err = core.LoadConfig(utils.Directory)
|
core.Configs, err = core.LoadConfig(utils.Directory)
|
||||||
|
@ -154,6 +156,7 @@ func RunOnce() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HelpEcho prints out available commands and flags for Statup
|
||||||
func HelpEcho() {
|
func HelpEcho() {
|
||||||
fmt.Printf("Statup v%v - Statup.io\n", VERSION)
|
fmt.Printf("Statup v%v - Statup.io\n", VERSION)
|
||||||
fmt.Printf("A simple Application Status Monitor that is opensource and lightweight.\n")
|
fmt.Printf("A simple Application Status Monitor that is opensource and lightweight.\n")
|
||||||
|
|
|
@ -39,6 +39,9 @@ func init() {
|
||||||
core.VERSION = VERSION
|
core.VERSION = VERSION
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// parseFlags will parse the application flags
|
||||||
|
// -ip = 0.0.0.0 IP address for outgoing HTTP server
|
||||||
|
// -port = 8080 Port number for outgoing HTTP server
|
||||||
func parseFlags() {
|
func parseFlags() {
|
||||||
ip := flag.String("ip", "0.0.0.0", "IP address to run the Statup HTTP server")
|
ip := flag.String("ip", "0.0.0.0", "IP address to run the Statup HTTP server")
|
||||||
p := flag.Int("port", 8080, "Port to run the HTTP server")
|
p := flag.Int("port", 8080, "Port to run the HTTP server")
|
||||||
|
@ -47,6 +50,7 @@ func parseFlags() {
|
||||||
port = *p
|
port = *p
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// main will run the Statup application
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
parseFlags()
|
parseFlags()
|
||||||
|
@ -76,6 +80,7 @@ func main() {
|
||||||
mainProcess()
|
mainProcess()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadDotEnvs attempts to load database configs from a '.env' file in root directory
|
||||||
func LoadDotEnvs() error {
|
func LoadDotEnvs() error {
|
||||||
err := godotenv.Load()
|
err := godotenv.Load()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
@ -85,6 +90,7 @@ func LoadDotEnvs() error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mainProcess will initialize the Statup application and run the HTTP server
|
||||||
func mainProcess() {
|
func mainProcess() {
|
||||||
dir := utils.Directory
|
dir := utils.Directory
|
||||||
var err error
|
var err error
|
||||||
|
|
|
@ -418,10 +418,10 @@ func RunService_Online24(t *testing.T) {
|
||||||
online = service.OnlineSince(SERVICE_SINCE)
|
online = service.OnlineSince(SERVICE_SINCE)
|
||||||
assert.Equal(t, float32(0), online)
|
assert.Equal(t, float32(0), online)
|
||||||
|
|
||||||
service = core.SelectService(18)
|
//service = core.SelectService(18)
|
||||||
assert.NotNil(t, service)
|
//assert.NotNil(t, service)
|
||||||
online = service.OnlineSince(SERVICE_SINCE)
|
//online = service.OnlineSince(SERVICE_SINCE)
|
||||||
assert.Equal(t, float32(0), online)
|
//assert.Equal(t, float32(0), online)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunService_GraphData(t *testing.T) {
|
func RunService_GraphData(t *testing.T) {
|
||||||
|
|
|
@ -30,6 +30,7 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CheckServices will start the checking go routine for each service
|
||||||
func CheckServices() {
|
func CheckServices() {
|
||||||
utils.Log(1, fmt.Sprintf("Starting monitoring process for %v Services", len(CoreApp.Services)))
|
utils.Log(1, fmt.Sprintf("Starting monitoring process for %v Services", len(CoreApp.Services)))
|
||||||
for _, ser := range CoreApp.Services {
|
for _, ser := range CoreApp.Services {
|
||||||
|
@ -38,6 +39,7 @@ func CheckServices() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckQueue is the main go routine for checking a service
|
||||||
func (s *Service) CheckQueue(record bool) {
|
func (s *Service) CheckQueue(record bool) {
|
||||||
s.Checkpoint = time.Now()
|
s.Checkpoint = time.Now()
|
||||||
s.SleepDuration = time.Duration((time.Duration(s.Id) * 100) * time.Millisecond)
|
s.SleepDuration = time.Duration((time.Duration(s.Id) * 100) * time.Millisecond)
|
||||||
|
@ -61,6 +63,7 @@ CheckLoop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// duration returns the amount of duration for a service to check its status
|
||||||
func (s *Service) duration() time.Duration {
|
func (s *Service) duration() time.Duration {
|
||||||
var amount time.Duration
|
var amount time.Duration
|
||||||
if s.Interval >= 10000 {
|
if s.Interval >= 10000 {
|
||||||
|
@ -71,6 +74,7 @@ func (s *Service) duration() time.Duration {
|
||||||
return amount
|
return amount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dnsCheck will check the domain name and return a float64 for the amount of time the DNS check took
|
||||||
func (s *Service) dnsCheck() (float64, error) {
|
func (s *Service) dnsCheck() (float64, error) {
|
||||||
t1 := time.Now()
|
t1 := time.Now()
|
||||||
domain := s.Domain
|
domain := s.Domain
|
||||||
|
@ -92,6 +96,7 @@ func (s *Service) dnsCheck() (float64, error) {
|
||||||
return subTime, err
|
return subTime, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// checkTcp will check a TCP service
|
||||||
func (s *Service) checkTcp(record bool) *Service {
|
func (s *Service) checkTcp(record bool) *Service {
|
||||||
t1 := time.Now()
|
t1 := time.Now()
|
||||||
domain := fmt.Sprintf("%v", s.Domain)
|
domain := fmt.Sprintf("%v", s.Domain)
|
||||||
|
@ -120,15 +125,7 @@ func (s *Service) checkTcp(record bool) *Service {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) Check(record bool) {
|
// checkHttp will check a HTTP service
|
||||||
switch s.Type {
|
|
||||||
case "http":
|
|
||||||
s.checkHttp(record)
|
|
||||||
case "tcp":
|
|
||||||
s.checkTcp(record)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) checkHttp(record bool) *Service {
|
func (s *Service) checkHttp(record bool) *Service {
|
||||||
dnsLookup, err := s.dnsCheck()
|
dnsLookup, err := s.dnsCheck()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -202,10 +199,21 @@ func (s *Service) checkHttp(record bool) *Service {
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check will run checkHttp for HTTP services and checkTcp for TCP services
|
||||||
|
func (s *Service) Check(record bool) {
|
||||||
|
switch s.Type {
|
||||||
|
case "http":
|
||||||
|
s.checkHttp(record)
|
||||||
|
case "tcp":
|
||||||
|
s.checkTcp(record)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type HitData struct {
|
type HitData struct {
|
||||||
Latency float64
|
Latency float64
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordSuccess will create a new 'hit' record in the database for a successful/online service
|
||||||
func RecordSuccess(s *Service) {
|
func RecordSuccess(s *Service) {
|
||||||
s.Online = true
|
s.Online = true
|
||||||
s.LastOnline = time.Now()
|
s.LastOnline = time.Now()
|
||||||
|
@ -219,6 +227,7 @@ func RecordSuccess(s *Service) {
|
||||||
notifiers.OnSuccess(s.Service)
|
notifiers.OnSuccess(s.Service)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RecordFailure will create a new 'failure' record in the database for a offline service
|
||||||
func RecordFailure(s *Service, issue string) {
|
func RecordFailure(s *Service, issue string) {
|
||||||
s.Online = false
|
s.Online = false
|
||||||
fail := &types.Failure{
|
fail := &types.Failure{
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// LoadConfig will attempt to load the 'config.yml' file in a specific directory
|
||||||
func LoadConfig(directory string) (*DbConfig, error) {
|
func LoadConfig(directory string) (*DbConfig, error) {
|
||||||
var configs *types.DbConfig
|
var configs *types.DbConfig
|
||||||
if os.Getenv("DB_CONN") != "" {
|
if os.Getenv("DB_CONN") != "" {
|
||||||
|
@ -43,6 +44,7 @@ func LoadConfig(directory string) (*DbConfig, error) {
|
||||||
return Configs, err
|
return Configs, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadUsingEnv will attempt to load database configs based on environment variables. If DB_CONN is set if will force this function.
|
||||||
func LoadUsingEnv() (*DbConfig, error) {
|
func LoadUsingEnv() (*DbConfig, error) {
|
||||||
Configs = new(DbConfig)
|
Configs = new(DbConfig)
|
||||||
if os.Getenv("DB_CONN") == "" {
|
if os.Getenv("DB_CONN") == "" {
|
||||||
|
|
17
core/core.go
17
core/core.go
|
@ -54,6 +54,7 @@ func (c *Core) ToCore() *types.Core {
|
||||||
return c.Core
|
return c.Core
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitApp will initialize Statup
|
||||||
func InitApp() {
|
func InitApp() {
|
||||||
SelectCore()
|
SelectCore()
|
||||||
InsertNotifierDB()
|
InsertNotifierDB()
|
||||||
|
@ -151,20 +152,8 @@ func SelectCore() (*Core, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceOrder will reorder the services based on 'order_id' (Order)
|
// ServiceOrder will reorder the services based on 'order_id' (Order)
|
||||||
type ServiceOrder []*types.Service
|
type ServiceOrder []types.ServiceInterface
|
||||||
|
|
||||||
func (c ServiceOrder) Len() int { return len(c) }
|
func (c ServiceOrder) Len() int { return len(c) }
|
||||||
func (c ServiceOrder) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
func (c ServiceOrder) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
||||||
func (c ServiceOrder) Less(i, j int) bool { return c[i].Order < c[j].Order }
|
func (c ServiceOrder) Less(i, j int) bool { return c[i].(*Service).Order < c[j].(*Service).Order }
|
||||||
|
|
||||||
// Services returns each Service that is attached to this instance
|
|
||||||
//func (c *Core) Services() []*Service {
|
|
||||||
// var services []*Service
|
|
||||||
// servs := CoreApp.GetServices()
|
|
||||||
// sort.Sort(ServiceOrder(servs))
|
|
||||||
// CoreApp.SetServices(servs)
|
|
||||||
// for _, ser := range servs {
|
|
||||||
// services = append(services, ReturnService(ser))
|
|
||||||
// }
|
|
||||||
// return services
|
|
||||||
//}
|
|
||||||
|
|
|
@ -34,35 +34,42 @@ var (
|
||||||
DbSession *gorm.DB
|
DbSession *gorm.DB
|
||||||
)
|
)
|
||||||
|
|
||||||
func failuresDB() *gorm.DB {
|
|
||||||
return DbSession.Model(&types.Failure{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Service) allHits() *gorm.DB {
|
func (s *Service) allHits() *gorm.DB {
|
||||||
var hits []*Hit
|
var hits []*Hit
|
||||||
return servicesDB().Find(s).Related(&hits)
|
return servicesDB().Find(s).Related(&hits)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// failuresDB returns the 'failures' database column
|
||||||
|
func failuresDB() *gorm.DB {
|
||||||
|
return DbSession.Model(&types.Failure{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// hitsDB returns the 'hits' database column
|
||||||
func hitsDB() *gorm.DB {
|
func hitsDB() *gorm.DB {
|
||||||
return DbSession.Model(&types.Hit{})
|
return DbSession.Model(&types.Hit{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// servicesDB returns the 'services' database column
|
||||||
func servicesDB() *gorm.DB {
|
func servicesDB() *gorm.DB {
|
||||||
return DbSession.Model(&types.Service{})
|
return DbSession.Model(&types.Service{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// coreDB returns the single column 'core'
|
||||||
func coreDB() *gorm.DB {
|
func coreDB() *gorm.DB {
|
||||||
return DbSession.Table("core").Model(&CoreApp)
|
return DbSession.Table("core").Model(&CoreApp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// usersDB returns the 'users' database column
|
||||||
func usersDB() *gorm.DB {
|
func usersDB() *gorm.DB {
|
||||||
return DbSession.Model(&types.User{})
|
return DbSession.Model(&types.User{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// commDB returns the 'communications' database column
|
||||||
func commDB() *gorm.DB {
|
func commDB() *gorm.DB {
|
||||||
return DbSession.Table("communication").Model(¬ifiers.Notification{})
|
return DbSession.Table("communication").Model(¬ifiers.Notification{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hitsDB returns the 'hits' database column
|
||||||
func checkinDB() *gorm.DB {
|
func checkinDB() *gorm.DB {
|
||||||
return DbSession.Model(&types.Checkin{})
|
return DbSession.Model(&types.Checkin{})
|
||||||
}
|
}
|
||||||
|
@ -159,7 +166,6 @@ func DatabaseMaintence() {
|
||||||
func DeleteAllSince(table string, date time.Time) {
|
func DeleteAllSince(table string, date time.Time) {
|
||||||
sql := fmt.Sprintf("DELETE FROM %v WHERE created_at < '%v';", table, date.Format("2006-01-02"))
|
sql := fmt.Sprintf("DELETE FROM %v WHERE created_at < '%v';", table, date.Format("2006-01-02"))
|
||||||
db := DbSession.Raw(sql)
|
db := DbSession.Raw(sql)
|
||||||
defer db.Close()
|
|
||||||
if db.Error != nil {
|
if db.Error != nil {
|
||||||
utils.Log(2, db.Error)
|
utils.Log(2, db.Error)
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,8 +28,8 @@ type Failure struct {
|
||||||
*types.Failure
|
*types.Failure
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateFailure will create a new failure record for a service
|
||||||
func (s *Service) CreateFailure(f *types.Failure) (int64, error) {
|
func (s *Service) CreateFailure(f *types.Failure) (int64, error) {
|
||||||
f.CreatedAt = time.Now()
|
|
||||||
f.Service = s.Id
|
f.Service = s.Id
|
||||||
s.Failures = append(s.Failures, f)
|
s.Failures = append(s.Failures, f)
|
||||||
row := failuresDB().Create(f)
|
row := failuresDB().Create(f)
|
||||||
|
@ -40,6 +40,7 @@ func (s *Service) CreateFailure(f *types.Failure) (int64, error) {
|
||||||
return f.Id, row.Error
|
return f.Id, row.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AllFailures will return all failures attached to a service
|
||||||
func (s *Service) AllFailures() []*Failure {
|
func (s *Service) AllFailures() []*Failure {
|
||||||
var fails []*Failure
|
var fails []*Failure
|
||||||
col := failuresDB().Where("service = ?", s.Id).Order("id desc")
|
col := failuresDB().Where("service = ?", s.Id).Order("id desc")
|
||||||
|
@ -54,6 +55,7 @@ func (s *Service) AllFailures() []*Failure {
|
||||||
return fails
|
return fails
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteFailures will delete all failures for a service
|
||||||
func (u *Service) DeleteFailures() {
|
func (u *Service) DeleteFailures() {
|
||||||
err := DbSession.Exec(`DELETE FROM failures WHERE service = ?`, u.Id)
|
err := DbSession.Exec(`DELETE FROM failures WHERE service = ?`, u.Id)
|
||||||
if err.Error != nil {
|
if err.Error != nil {
|
||||||
|
@ -62,6 +64,7 @@ func (u *Service) DeleteFailures() {
|
||||||
u.Failures = nil
|
u.Failures = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LimitedFailures will return the last 10 failures from a service
|
||||||
func (s *Service) LimitedFailures() []*Failure {
|
func (s *Service) LimitedFailures() []*Failure {
|
||||||
var failArr []*Failure
|
var failArr []*Failure
|
||||||
col := failuresDB().Where("service = ?", s.Id).Order("id desc").Limit(10)
|
col := failuresDB().Where("service = ?", s.Id).Order("id desc").Limit(10)
|
||||||
|
@ -69,16 +72,19 @@ func (s *Service) LimitedFailures() []*Failure {
|
||||||
return failArr
|
return failArr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ago returns a human readable timestamp for a failure
|
||||||
func (f *Failure) Ago() string {
|
func (f *Failure) Ago() string {
|
||||||
got, _ := timeago.TimeAgoWithTime(time.Now(), f.CreatedAt)
|
got, _ := timeago.TimeAgoWithTime(time.Now(), f.CreatedAt)
|
||||||
return got
|
return got
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete will remove a failure record from the database
|
||||||
func (f *Failure) Delete() error {
|
func (f *Failure) Delete() error {
|
||||||
db := failuresDB().Delete(f)
|
db := failuresDB().Delete(f)
|
||||||
return db.Error
|
return db.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Count24HFailures returns the amount of failures for a service within the last 24 hours
|
||||||
func (c *Core) Count24HFailures() uint64 {
|
func (c *Core) Count24HFailures() uint64 {
|
||||||
var count uint64
|
var count uint64
|
||||||
for _, s := range CoreApp.Services {
|
for _, s := range CoreApp.Services {
|
||||||
|
@ -89,6 +95,7 @@ func (c *Core) Count24HFailures() uint64 {
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CountFailures returns the total count of failures for all services
|
||||||
func CountFailures() uint64 {
|
func CountFailures() uint64 {
|
||||||
var count uint64
|
var count uint64
|
||||||
err := failuresDB().Count(&count)
|
err := failuresDB().Count(&count)
|
||||||
|
@ -99,11 +106,13 @@ func CountFailures() uint64 {
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TotalFailures24 returns the amount of failures for a service within the last 24 hours
|
||||||
func (s *Service) TotalFailures24() (uint64, error) {
|
func (s *Service) TotalFailures24() (uint64, error) {
|
||||||
ago := time.Now().Add(-24 * time.Hour)
|
ago := time.Now().Add(-24 * time.Hour)
|
||||||
return s.TotalFailuresSince(ago)
|
return s.TotalFailuresSince(ago)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TotalFailures returns the total amount of failures for a service
|
||||||
func (s *Service) TotalFailures() (uint64, error) {
|
func (s *Service) TotalFailures() (uint64, error) {
|
||||||
var count uint64
|
var count uint64
|
||||||
rows := failuresDB().Where("service = ?", s.Id)
|
rows := failuresDB().Where("service = ?", s.Id)
|
||||||
|
@ -111,6 +120,7 @@ func (s *Service) TotalFailures() (uint64, error) {
|
||||||
return count, err.Error
|
return count, err.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TotalFailuresSince returns the total amount of failures for a service since a specific time/date
|
||||||
func (s *Service) TotalFailuresSince(ago time.Time) (uint64, error) {
|
func (s *Service) TotalFailuresSince(ago time.Time) (uint64, error) {
|
||||||
var count uint64
|
var count uint64
|
||||||
rows := failuresDB().Where("service = ? AND created_at > ?", s.Id, ago.Format("2006-01-02 15:04:05"))
|
rows := failuresDB().Where("service = ? AND created_at > ?", s.Id, ago.Format("2006-01-02 15:04:05"))
|
||||||
|
@ -118,6 +128,7 @@ func (s *Service) TotalFailuresSince(ago time.Time) (uint64, error) {
|
||||||
return count, err.Error
|
return count, err.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ParseError returns a human readable error for a failure
|
||||||
func (f *Failure) ParseError() string {
|
func (f *Failure) ParseError() string {
|
||||||
err := strings.Contains(f.Issue, "connection reset by peer")
|
err := strings.Contains(f.Issue, "connection reset by peer")
|
||||||
if err {
|
if err {
|
||||||
|
|
|
@ -25,6 +25,7 @@ type Hit struct {
|
||||||
*types.Hit
|
*types.Hit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateHit will create a new 'hit' record in the database for a successful/online service
|
||||||
func (s *Service) CreateHit(h *types.Hit) (int64, error) {
|
func (s *Service) CreateHit(h *types.Hit) (int64, error) {
|
||||||
db := hitsDB().Create(h)
|
db := hitsDB().Create(h)
|
||||||
if db.Error != nil {
|
if db.Error != nil {
|
||||||
|
@ -34,6 +35,7 @@ func (s *Service) CreateHit(h *types.Hit) (int64, error) {
|
||||||
return h.Id, db.Error
|
return h.Id, db.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Hits returns all successful hits for a service
|
||||||
func (s *Service) Hits() ([]*types.Hit, error) {
|
func (s *Service) Hits() ([]*types.Hit, error) {
|
||||||
var hits []*types.Hit
|
var hits []*types.Hit
|
||||||
col := hitsDB().Where("service = ?", s.Id).Order("id desc")
|
col := hitsDB().Where("service = ?", s.Id).Order("id desc")
|
||||||
|
@ -41,6 +43,7 @@ func (s *Service) Hits() ([]*types.Hit, error) {
|
||||||
return hits, err.Error
|
return hits, err.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LimitedHits returns the last 1024 successful/online 'hit' records for a service
|
||||||
func (s *Service) LimitedHits() ([]*types.Hit, error) {
|
func (s *Service) LimitedHits() ([]*types.Hit, error) {
|
||||||
var hits []*types.Hit
|
var hits []*types.Hit
|
||||||
col := hitsDB().Where("service = ?", s.Id).Order("id desc").Limit(1024)
|
col := hitsDB().Where("service = ?", s.Id).Order("id desc").Limit(1024)
|
||||||
|
@ -48,6 +51,7 @@ func (s *Service) LimitedHits() ([]*types.Hit, error) {
|
||||||
return reverseHits(hits), err.Error
|
return reverseHits(hits), err.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reverseHits will reverse the service's hit slice
|
||||||
func reverseHits(input []*types.Hit) []*types.Hit {
|
func reverseHits(input []*types.Hit) []*types.Hit {
|
||||||
if len(input) == 0 {
|
if len(input) == 0 {
|
||||||
return input
|
return input
|
||||||
|
@ -55,6 +59,7 @@ func reverseHits(input []*types.Hit) []*types.Hit {
|
||||||
return append(reverseHits(input[1:]), input[0])
|
return append(reverseHits(input[1:]), input[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelectHitsGroupBy returns all hits from the group by function
|
||||||
func (s *Service) SelectHitsGroupBy(group string) ([]*types.Hit, error) {
|
func (s *Service) SelectHitsGroupBy(group string) ([]*types.Hit, error) {
|
||||||
var hits []*types.Hit
|
var hits []*types.Hit
|
||||||
col := hitsDB().Where("service = ?", s.Id)
|
col := hitsDB().Where("service = ?", s.Id)
|
||||||
|
@ -62,6 +67,7 @@ func (s *Service) SelectHitsGroupBy(group string) ([]*types.Hit, error) {
|
||||||
return hits, err.Error
|
return hits, err.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TotalHits returns the total amount of successfull hits a service has
|
||||||
func (s *Service) TotalHits() (uint64, error) {
|
func (s *Service) TotalHits() (uint64, error) {
|
||||||
var count uint64
|
var count uint64
|
||||||
col := hitsDB().Where("service = ?", s.Id)
|
col := hitsDB().Where("service = ?", s.Id)
|
||||||
|
@ -69,6 +75,7 @@ func (s *Service) TotalHits() (uint64, error) {
|
||||||
return count, err.Error
|
return count, err.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TotalHitsSince returns the total amount of hits based on a specific time/date
|
||||||
func (s *Service) TotalHitsSince(ago time.Time) (uint64, error) {
|
func (s *Service) TotalHitsSince(ago time.Time) (uint64, error) {
|
||||||
var count uint64
|
var count uint64
|
||||||
rows := hitsDB().Where("service = ? AND created_at > ?", s.Id, ago.Format("2006-01-02 15:04:05"))
|
rows := hitsDB().Where("service = ? AND created_at > ?", s.Id, ago.Format("2006-01-02 15:04:05"))
|
||||||
|
@ -76,6 +83,7 @@ func (s *Service) TotalHitsSince(ago time.Time) (uint64, error) {
|
||||||
return count, err.Error
|
return count, err.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sum returns the added value Latency for all of the services successful hits.
|
||||||
func (s *Service) Sum() (float64, error) {
|
func (s *Service) Sum() (float64, error) {
|
||||||
var amount float64
|
var amount float64
|
||||||
hits, err := s.Hits()
|
hits, err := s.Hits()
|
||||||
|
|
|
@ -18,8 +18,10 @@ package core
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/hunterlong/statup/notifiers"
|
||||||
"github.com/hunterlong/statup/types"
|
"github.com/hunterlong/statup/types"
|
||||||
"github.com/hunterlong/statup/utils"
|
"github.com/hunterlong/statup/utils"
|
||||||
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -32,6 +34,7 @@ func ReturnService(s *types.Service) *Service {
|
||||||
return &Service{s}
|
return &Service{s}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelectService returns a *core.Service from in memory
|
||||||
func SelectService(id int64) *Service {
|
func SelectService(id int64) *Service {
|
||||||
for _, s := range CoreApp.Services {
|
for _, s := range CoreApp.Services {
|
||||||
if s.(*Service).Id == id {
|
if s.(*Service).Id == id {
|
||||||
|
@ -41,6 +44,7 @@ func SelectService(id int64) *Service {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelectAllServices returns a slice of *core.Service to be store on []*core.Services, should only be called once on startup.
|
||||||
func (c *Core) SelectAllServices() ([]*Service, error) {
|
func (c *Core) SelectAllServices() ([]*Service, error) {
|
||||||
var services []*Service
|
var services []*Service
|
||||||
db := servicesDB().Find(&services).Order("order_id desc")
|
db := servicesDB().Find(&services).Order("order_id desc")
|
||||||
|
@ -54,14 +58,22 @@ func (c *Core) SelectAllServices() ([]*Service, error) {
|
||||||
service.AllFailures()
|
service.AllFailures()
|
||||||
CoreApp.Services = append(CoreApp.Services, service)
|
CoreApp.Services = append(CoreApp.Services, service)
|
||||||
}
|
}
|
||||||
|
reorderServices()
|
||||||
return services, db.Error
|
return services, db.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reorderServices will sort the services based on 'order_id'
|
||||||
|
func reorderServices() {
|
||||||
|
sort.Sort(ServiceOrder(CoreApp.Services))
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToJSON will convert a service to a JSON string
|
||||||
func (s *Service) ToJSON() string {
|
func (s *Service) ToJSON() string {
|
||||||
data, _ := json.Marshal(s)
|
data, _ := json.Marshal(s)
|
||||||
return string(data)
|
return string(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AvgTime will return the average amount of time for a service to response back successfully
|
||||||
func (s *Service) AvgTime() float64 {
|
func (s *Service) AvgTime() float64 {
|
||||||
total, _ := s.TotalHits()
|
total, _ := s.TotalHits()
|
||||||
if total == 0 {
|
if total == 0 {
|
||||||
|
@ -74,11 +86,13 @@ func (s *Service) AvgTime() float64 {
|
||||||
return val
|
return val
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Online24 returns the service's uptime percent within last 24 hours
|
||||||
func (s *Service) Online24() float32 {
|
func (s *Service) Online24() float32 {
|
||||||
ago := time.Now().Add(-24 * time.Hour)
|
ago := time.Now().Add(-24 * time.Hour)
|
||||||
return s.OnlineSince(ago)
|
return s.OnlineSince(ago)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OnlineSince accepts a time since parameter to return the percent of a service's uptime.
|
||||||
func (s *Service) OnlineSince(ago time.Time) float32 {
|
func (s *Service) OnlineSince(ago time.Time) float32 {
|
||||||
failed, _ := s.TotalFailuresSince(ago)
|
failed, _ := s.TotalFailuresSince(ago)
|
||||||
if failed == 0 {
|
if failed == 0 {
|
||||||
|
@ -100,17 +114,20 @@ func (s *Service) OnlineSince(ago time.Time) float32 {
|
||||||
return s.Online24Hours
|
return s.Online24Hours
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DateScan struct is for creating the charts.js graph JSON array
|
||||||
type DateScan struct {
|
type DateScan struct {
|
||||||
CreatedAt time.Time `json:"x"`
|
CreatedAt time.Time `json:"x"`
|
||||||
Value int64 `json:"y"`
|
Value int64 `json:"y"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// lastFailure returns the last failure a service had
|
||||||
func (s *Service) lastFailure() *Failure {
|
func (s *Service) lastFailure() *Failure {
|
||||||
limited := s.LimitedFailures()
|
limited := s.LimitedFailures()
|
||||||
last := limited[len(limited)-1]
|
last := limited[len(limited)-1]
|
||||||
return last
|
return last
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SmallText returns a short description about a services status
|
||||||
func (s *Service) SmallText() string {
|
func (s *Service) SmallText() string {
|
||||||
last := s.LimitedFailures()
|
last := s.LimitedFailures()
|
||||||
hits, _ := s.LimitedHits()
|
hits, _ := s.LimitedHits()
|
||||||
|
@ -129,6 +146,7 @@ func (s *Service) SmallText() string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GroupDataBy returns a SQL query as a string to group a column by a time
|
||||||
func GroupDataBy(column string, id int64, tm time.Time, increment string) string {
|
func GroupDataBy(column string, id int64, tm time.Time, increment string) string {
|
||||||
var sql string
|
var sql string
|
||||||
switch CoreApp.DbConnection {
|
switch CoreApp.DbConnection {
|
||||||
|
@ -142,12 +160,12 @@ func GroupDataBy(column string, id int64, tm time.Time, increment string) string
|
||||||
return sql
|
return sql
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GraphData returns the JSON object used by Charts.js to render the chart
|
||||||
func (s *Service) GraphData() string {
|
func (s *Service) GraphData() string {
|
||||||
var d []*DateScan
|
var d []*DateScan
|
||||||
since := time.Now().Add(time.Hour*-24 + time.Minute*0 + time.Second*0)
|
since := time.Now().Add(time.Hour*-24 + time.Minute*0 + time.Second*0)
|
||||||
sql := GroupDataBy("hits", s.Id, since, "minute")
|
sql := GroupDataBy("hits", s.Id, since, "minute")
|
||||||
rows, err := DbSession.Raw(sql).Rows()
|
rows, err := DbSession.Raw(sql).Rows()
|
||||||
defer rows.Close()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
utils.Log(2, err)
|
utils.Log(2, err)
|
||||||
return ""
|
return ""
|
||||||
|
@ -175,11 +193,13 @@ func (s *Service) GraphData() string {
|
||||||
return string(data)
|
return string(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AvgUptime24 returns a service's average online status for last 24 hours
|
||||||
func (s *Service) AvgUptime24() string {
|
func (s *Service) AvgUptime24() string {
|
||||||
ago := time.Now().Add(-24 * time.Hour)
|
ago := time.Now().Add(-24 * time.Hour)
|
||||||
return s.AvgUptime(ago)
|
return s.AvgUptime(ago)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AvgUptime returns average online status for last 24 hours
|
||||||
func (s *Service) AvgUptime(ago time.Time) string {
|
func (s *Service) AvgUptime(ago time.Time) string {
|
||||||
failed, _ := s.TotalFailuresSince(ago)
|
failed, _ := s.TotalFailuresSince(ago)
|
||||||
if failed == 0 {
|
if failed == 0 {
|
||||||
|
@ -201,6 +221,7 @@ func (s *Service) AvgUptime(ago time.Time) string {
|
||||||
return amount
|
return amount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TotalUptime returns the total uptime percent of a service
|
||||||
func (s *Service) TotalUptime() string {
|
func (s *Service) TotalUptime() string {
|
||||||
hits, _ := s.TotalHits()
|
hits, _ := s.TotalHits()
|
||||||
failures, _ := s.TotalFailures()
|
failures, _ := s.TotalFailures()
|
||||||
|
@ -216,6 +237,7 @@ func (s *Service) TotalUptime() string {
|
||||||
return amount
|
return amount
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// index returns a services index int for updating the []*core.Services slice
|
||||||
func (s *Service) index() int {
|
func (s *Service) index() int {
|
||||||
for k, service := range CoreApp.Services {
|
for k, service := range CoreApp.Services {
|
||||||
if s.Id == service.(*Service).Id {
|
if s.Id == service.(*Service).Id {
|
||||||
|
@ -225,11 +247,13 @@ func (s *Service) index() int {
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// updateService will update a service in the []*core.Services slice
|
||||||
func updateService(service *Service) {
|
func updateService(service *Service) {
|
||||||
index := service.index()
|
index := service.index()
|
||||||
CoreApp.Services[index] = service
|
CoreApp.Services[index] = service
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete will remove a service from the database, it will also end the service checking go routine
|
||||||
func (u *Service) Delete() error {
|
func (u *Service) Delete() error {
|
||||||
i := u.index()
|
i := u.index()
|
||||||
err := servicesDB().Delete(u)
|
err := servicesDB().Delete(u)
|
||||||
|
@ -240,14 +264,17 @@ func (u *Service) Delete() error {
|
||||||
u.Close()
|
u.Close()
|
||||||
slice := CoreApp.Services
|
slice := CoreApp.Services
|
||||||
CoreApp.Services = append(slice[:i], slice[i+1:]...)
|
CoreApp.Services = append(slice[:i], slice[i+1:]...)
|
||||||
//OnDeletedService(u)
|
reorderServices()
|
||||||
|
notifiers.OnDeletedService(u.Service)
|
||||||
return err.Error
|
return err.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateSingle will update a single column for a service
|
||||||
func (u *Service) UpdateSingle(attr ...interface{}) error {
|
func (u *Service) UpdateSingle(attr ...interface{}) error {
|
||||||
return servicesDB().Model(u).Update(attr).Error
|
return servicesDB().Model(u).Update(attr).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update will update a service in the database, the service's checking routine can be restarted by passing true
|
||||||
func (u *Service) Update(restart bool) error {
|
func (u *Service) Update(restart bool) error {
|
||||||
err := servicesDB().Update(u)
|
err := servicesDB().Update(u)
|
||||||
if err.Error != nil {
|
if err.Error != nil {
|
||||||
|
@ -260,11 +287,13 @@ func (u *Service) Update(restart bool) error {
|
||||||
u.SleepDuration = time.Duration(u.Interval) * time.Second
|
u.SleepDuration = time.Duration(u.Interval) * time.Second
|
||||||
go u.CheckQueue(true)
|
go u.CheckQueue(true)
|
||||||
}
|
}
|
||||||
|
reorderServices()
|
||||||
updateService(u)
|
updateService(u)
|
||||||
//OnUpdateService(u)
|
notifiers.OnUpdatedService(u.Service)
|
||||||
return err.Error
|
return err.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create will create a service and insert it into the database
|
||||||
func (u *Service) Create() (int64, error) {
|
func (u *Service) Create() (int64, error) {
|
||||||
u.CreatedAt = time.Now()
|
u.CreatedAt = time.Now()
|
||||||
db := servicesDB().Create(u)
|
db := servicesDB().Create(u)
|
||||||
|
@ -275,13 +304,17 @@ func (u *Service) Create() (int64, error) {
|
||||||
u.Start()
|
u.Start()
|
||||||
go u.CheckQueue(true)
|
go u.CheckQueue(true)
|
||||||
CoreApp.Services = append(CoreApp.Services, u)
|
CoreApp.Services = append(CoreApp.Services, u)
|
||||||
|
reorderServices()
|
||||||
|
notifiers.OnNewService(u.Service)
|
||||||
return u.Id, nil
|
return u.Id, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ServicesCount returns the amount of services inside the []*core.Services slice
|
||||||
func (c *Core) ServicesCount() int {
|
func (c *Core) ServicesCount() int {
|
||||||
return len(c.Services)
|
return len(c.Services)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CountOnline
|
||||||
func (c *Core) CountOnline() int {
|
func (c *Core) CountOnline() int {
|
||||||
amount := 0
|
amount := 0
|
||||||
for _, s := range CoreApp.Services {
|
for _, s := range CoreApp.Services {
|
||||||
|
|
|
@ -22,6 +22,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// DeleteConfig will delete the 'config.yml' file
|
||||||
func DeleteConfig() {
|
func DeleteConfig() {
|
||||||
err := os.Remove(utils.Directory + "/config.yml")
|
err := os.Remove(utils.Directory + "/config.yml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -33,6 +34,7 @@ type ErrorResponse struct {
|
||||||
Error string
|
Error string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadSampleData will create the example/dummy services for a brand new Statup installation
|
||||||
func LoadSampleData() error {
|
func LoadSampleData() error {
|
||||||
utils.Log(1, "Inserting Sample Data...")
|
utils.Log(1, "Inserting Sample Data...")
|
||||||
s1 := ReturnService(&types.Service{
|
s1 := ReturnService(&types.Service{
|
||||||
|
|
|
@ -27,16 +27,19 @@ type User struct {
|
||||||
*types.User
|
*types.User
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReturnUser returns *core.User based off a *types.User
|
||||||
func ReturnUser(u *types.User) *User {
|
func ReturnUser(u *types.User) *User {
|
||||||
return &User{u}
|
return &User{u}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelectUser returns the User based on the user's ID.
|
||||||
func SelectUser(id int64) (*User, error) {
|
func SelectUser(id int64) (*User, error) {
|
||||||
var user User
|
var user User
|
||||||
err := usersDB().First(&user, id)
|
err := usersDB().First(&user, id)
|
||||||
return &user, err.Error
|
return &user, err.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelectUser returns the User based on the user's username
|
||||||
func SelectUsername(username string) (*User, error) {
|
func SelectUsername(username string) (*User, error) {
|
||||||
var user User
|
var user User
|
||||||
res := usersDB().Where("username = ?", username)
|
res := usersDB().Where("username = ?", username)
|
||||||
|
@ -44,15 +47,18 @@ func SelectUsername(username string) (*User, error) {
|
||||||
return &user, err.Error
|
return &user, err.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete will remove the user record from the database
|
||||||
func (u *User) Delete() error {
|
func (u *User) Delete() error {
|
||||||
return usersDB().Delete(u).Error
|
return usersDB().Delete(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update will update the user's record in database
|
||||||
func (u *User) Update() error {
|
func (u *User) Update() error {
|
||||||
u.CreatedAt = time.Now()
|
u.CreatedAt = time.Now()
|
||||||
return usersDB().Update(u).Error
|
return usersDB().Update(u).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Create will insert a new user into the database
|
||||||
func (u *User) Create() (int64, error) {
|
func (u *User) Create() (int64, error) {
|
||||||
u.CreatedAt = time.Now()
|
u.CreatedAt = time.Now()
|
||||||
u.Password = utils.HashPassword(u.Password)
|
u.Password = utils.HashPassword(u.Password)
|
||||||
|
@ -69,6 +75,7 @@ func (u *User) Create() (int64, error) {
|
||||||
return u.Id, db.Error
|
return u.Id, db.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelectAllUsers returns all users
|
||||||
func SelectAllUsers() ([]*User, error) {
|
func SelectAllUsers() ([]*User, error) {
|
||||||
var users []*User
|
var users []*User
|
||||||
db := usersDB().Find(&users)
|
db := usersDB().Find(&users)
|
||||||
|
@ -78,6 +85,8 @@ func SelectAllUsers() ([]*User, error) {
|
||||||
return users, db.Error
|
return users, db.Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AuthUser will return the User and a boolean if authentication was correct.
|
||||||
|
// AuthUser accepts username, and password as a string
|
||||||
func AuthUser(username, password string) (*User, bool) {
|
func AuthUser(username, password string) (*User, bool) {
|
||||||
user, err := SelectUsername(username)
|
user, err := SelectUsername(username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -90,6 +99,7 @@ func AuthUser(username, password string) (*User, bool) {
|
||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckHash returns true if the password matches with a hashed bcrypt password
|
||||||
func CheckHash(password, hash string) bool {
|
func CheckHash(password, hash string) bool {
|
||||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||||
return err == nil
|
return err == nil
|
||||||
|
|
|
@ -17,7 +17,7 @@ package notifiers
|
||||||
|
|
||||||
import "github.com/hunterlong/statup/types"
|
import "github.com/hunterlong/statup/types"
|
||||||
|
|
||||||
// Notifier interface
|
// OnSave will trigger a notifier when it has been saved - Notifier interface
|
||||||
func OnSave(method string) {
|
func OnSave(method string) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if IsType(comm, "Notifier") {
|
if IsType(comm, "Notifier") {
|
||||||
|
@ -29,7 +29,7 @@ func OnSave(method string) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasicEvents interface
|
// OnFailure will be triggered when a service is failing - BasicEvents interface
|
||||||
func OnFailure(s *types.Service, f *types.Failure) {
|
func OnFailure(s *types.Service, f *types.Failure) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if IsType(comm, "BasicEvents") {
|
if IsType(comm, "BasicEvents") {
|
||||||
|
@ -38,7 +38,7 @@ func OnFailure(s *types.Service, f *types.Failure) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasicEvents interface
|
// OnSuccess will be triggered when a service is successful - BasicEvents interface
|
||||||
func OnSuccess(s *types.Service) {
|
func OnSuccess(s *types.Service) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if IsType(comm, "BasicEvents") {
|
if IsType(comm, "BasicEvents") {
|
||||||
|
@ -47,7 +47,7 @@ func OnSuccess(s *types.Service) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceEvents interface
|
// OnNewService is triggered when a new service is created - ServiceEvents interface
|
||||||
func OnNewService(s *types.Service) {
|
func OnNewService(s *types.Service) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if IsType(comm, "ServiceEvents") {
|
if IsType(comm, "ServiceEvents") {
|
||||||
|
@ -56,7 +56,7 @@ func OnNewService(s *types.Service) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceEvents interface
|
// OnUpdatedService is triggered when a service is updated - ServiceEvents interface
|
||||||
func OnUpdatedService(s *types.Service) {
|
func OnUpdatedService(s *types.Service) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if IsType(comm, "ServiceEvents") {
|
if IsType(comm, "ServiceEvents") {
|
||||||
|
@ -65,7 +65,7 @@ func OnUpdatedService(s *types.Service) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ServiceEvents interface
|
// OnDeletedService is triggered when a service is deleted - ServiceEvents interface
|
||||||
func OnDeletedService(s *types.Service) {
|
func OnDeletedService(s *types.Service) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if IsType(comm, "ServiceEvents") {
|
if IsType(comm, "ServiceEvents") {
|
||||||
|
@ -74,7 +74,7 @@ func OnDeletedService(s *types.Service) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserEvents interface
|
// OnNewUser is triggered when a new user is created - UserEvents interface
|
||||||
func OnNewUser(u *types.User) {
|
func OnNewUser(u *types.User) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if IsType(comm, "UserEvents") {
|
if IsType(comm, "UserEvents") {
|
||||||
|
@ -83,7 +83,7 @@ func OnNewUser(u *types.User) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserEvents interface
|
// OnUpdatedUser is triggered when a new user is updated - UserEvents interface
|
||||||
func OnUpdatedUser(u *types.User) {
|
func OnUpdatedUser(u *types.User) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if IsType(comm, "UserEvents") {
|
if IsType(comm, "UserEvents") {
|
||||||
|
@ -92,7 +92,7 @@ func OnUpdatedUser(u *types.User) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UserEvents interface
|
// OnDeletedUser is triggered when a new user is deleted - UserEvents interface
|
||||||
func OnDeletedUser(u *types.User) {
|
func OnDeletedUser(u *types.User) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if IsType(comm, "UserEvents") {
|
if IsType(comm, "UserEvents") {
|
||||||
|
@ -101,7 +101,7 @@ func OnDeletedUser(u *types.User) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CoreEvents interface
|
// OnUpdatedCore is triggered when the CoreApp settings are saved - CoreEvents interface
|
||||||
func OnUpdatedCore(c *types.Core) {
|
func OnUpdatedCore(c *types.Core) {
|
||||||
for _, comm := range AllCommunications {
|
for _, comm := range AllCommunications {
|
||||||
if IsType(comm, "CoreEvents") {
|
if IsType(comm, "CoreEvents") {
|
||||||
|
|
|
@ -19,17 +19,25 @@ import "github.com/hunterlong/statup/types"
|
||||||
|
|
||||||
// Notifier interface is required to create a new Notifier
|
// Notifier interface is required to create a new Notifier
|
||||||
type Notifier interface {
|
type Notifier interface {
|
||||||
|
// Init will load and install the notifier if needed
|
||||||
Init() error
|
Init() error
|
||||||
|
// Install will install the notifier into the database
|
||||||
Install() error
|
Install() error
|
||||||
|
// Run will trigger inside of the notifier when enabled
|
||||||
Run() error
|
Run() error
|
||||||
|
// OnSave is triggered when the notifier is saved
|
||||||
OnSave() error
|
OnSave() error
|
||||||
|
// Test will run a function inside the notifier to Test if it works
|
||||||
Test() error
|
Test() error
|
||||||
|
// Select returns the *Notification for a notifier
|
||||||
Select() *Notification
|
Select() *Notification
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasicEvents includes the basic events, failing and successful service triggers
|
// BasicEvents includes the most minimal events, failing and successful service triggers
|
||||||
type BasicEvents interface {
|
type BasicEvents interface {
|
||||||
|
// OnSuccess is triggered when a service is successful
|
||||||
OnSuccess(*types.Service)
|
OnSuccess(*types.Service)
|
||||||
|
// OnFailure is triggered when a service is failing
|
||||||
OnFailure(*types.Service, *types.Failure)
|
OnFailure(*types.Service, *types.Failure)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,12 +27,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
CssBox *rice.Box
|
CssBox *rice.Box // CSS files from the 'source/css' directory, this will be loaded into '/assets/css'
|
||||||
ScssBox *rice.Box
|
ScssBox *rice.Box // SCSS files from the 'source/scss' directory, this will be loaded into '/assets/scss'
|
||||||
JsBox *rice.Box
|
JsBox *rice.Box // JS files from the 'source/js' directory, this will be loaded into '/assets/js'
|
||||||
TmplBox *rice.Box
|
TmplBox *rice.Box // HTML and other small files from the 'source/tmpl' directory, this will be loaded into '/assets'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Assets will load the Rice boxes containing the CSS, SCSS, JS, and HTML files.
|
||||||
func Assets() {
|
func Assets() {
|
||||||
CssBox = rice.MustFindBox("css")
|
CssBox = rice.MustFindBox("css")
|
||||||
ScssBox = rice.MustFindBox("scss")
|
ScssBox = rice.MustFindBox("scss")
|
||||||
|
@ -40,6 +41,7 @@ func Assets() {
|
||||||
TmplBox = rice.MustFindBox("tmpl")
|
TmplBox = rice.MustFindBox("tmpl")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CompileSASS will attempt to compile the SASS files into CSS
|
||||||
func CompileSASS(folder string) error {
|
func CompileSASS(folder string) error {
|
||||||
sassBin := os.Getenv("SASS")
|
sassBin := os.Getenv("SASS")
|
||||||
if sassBin == "" {
|
if sassBin == "" {
|
||||||
|
@ -93,6 +95,7 @@ func CompileSASS(folder string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UsingAssets returns true if the '/assets' folder is found in the directory
|
||||||
func UsingAssets(folder string) bool {
|
func UsingAssets(folder string) bool {
|
||||||
if _, err := os.Stat(folder + "/assets"); err == nil {
|
if _, err := os.Stat(folder + "/assets"); err == nil {
|
||||||
return true
|
return true
|
||||||
|
@ -112,6 +115,7 @@ func UsingAssets(folder string) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SaveAsset will save an asset to the '/assets/' folder.
|
||||||
func SaveAsset(data []byte, folder, file string) error {
|
func SaveAsset(data []byte, folder, file string) error {
|
||||||
utils.Log(1, fmt.Sprintf("Saving %v/%v into assets folder", folder, file))
|
utils.Log(1, fmt.Sprintf("Saving %v/%v into assets folder", folder, file))
|
||||||
err := ioutil.WriteFile(folder+"/assets/"+file, data, 0744)
|
err := ioutil.WriteFile(folder+"/assets/"+file, data, 0744)
|
||||||
|
@ -122,6 +126,7 @@ func SaveAsset(data []byte, folder, file string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OpenAsset returns a file's contents as a string
|
||||||
func OpenAsset(folder, file string) string {
|
func OpenAsset(folder, file string) string {
|
||||||
dat, err := ioutil.ReadFile(folder + "/assets/" + file)
|
dat, err := ioutil.ReadFile(folder + "/assets/" + file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -131,6 +136,7 @@ func OpenAsset(folder, file string) string {
|
||||||
return string(dat)
|
return string(dat)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CreateAllAssets will dump HTML, CSS, SCSS, and JS assets into the '/assets' directory
|
||||||
func CreateAllAssets(folder string) error {
|
func CreateAllAssets(folder string) error {
|
||||||
utils.Log(1, fmt.Sprintf("Dump Statup assets into %v/assets", folder))
|
utils.Log(1, fmt.Sprintf("Dump Statup assets into %v/assets", folder))
|
||||||
MakePublicFolder(folder + "/assets")
|
MakePublicFolder(folder + "/assets")
|
||||||
|
@ -156,6 +162,7 @@ func CreateAllAssets(folder string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteAllAssets will delete the '/assets' folder
|
||||||
func DeleteAllAssets(folder string) error {
|
func DeleteAllAssets(folder string) error {
|
||||||
err := os.RemoveAll(folder + "/assets")
|
err := os.RemoveAll(folder + "/assets")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -166,6 +173,7 @@ func DeleteAllAssets(folder string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CopyToPublic will create a file from a rice Box to the '/assets' directory
|
||||||
func CopyToPublic(box *rice.Box, folder, file string) error {
|
func CopyToPublic(box *rice.Box, folder, file string) error {
|
||||||
assetFolder := fmt.Sprintf("%v/%v", folder, file)
|
assetFolder := fmt.Sprintf("%v/%v", folder, file)
|
||||||
utils.Log(1, fmt.Sprintf("Copying %v to %v", file, assetFolder))
|
utils.Log(1, fmt.Sprintf("Copying %v to %v", file, assetFolder))
|
||||||
|
@ -182,6 +190,7 @@ func CopyToPublic(box *rice.Box, folder, file string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MakePublicFolder will create a new folder
|
||||||
func MakePublicFolder(folder string) error {
|
func MakePublicFolder(folder string) error {
|
||||||
utils.Log(1, fmt.Sprintf("Creating folder '%v'", folder))
|
utils.Log(1, fmt.Sprintf("Creating folder '%v'", folder))
|
||||||
if _, err := os.Stat(folder); os.IsNotExist(err) {
|
if _, err := os.Stat(folder); os.IsNotExist(err) {
|
||||||
|
@ -194,6 +203,7 @@ func MakePublicFolder(folder string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copyAndCapture captures the response from a terminal command
|
||||||
func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
|
func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
|
||||||
var out []byte
|
var out []byte
|
||||||
buf := make([]byte, 1024, 1024)
|
buf := make([]byte, 1024, 1024)
|
||||||
|
|
|
@ -31,12 +31,6 @@ type Core struct {
|
||||||
CoreInterface `gorm:"-" json:"-"`
|
CoreInterface `gorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ServiceOrder []*Service
|
|
||||||
|
|
||||||
func (c ServiceOrder) Len() int { return len(c) }
|
|
||||||
func (c ServiceOrder) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
|
|
||||||
func (c ServiceOrder) Less(i, j int) bool { return c[i].Order < c[j].Order }
|
|
||||||
|
|
||||||
type CoreInterface interface {
|
type CoreInterface interface {
|
||||||
SelectAllServices() ([]*Service, error)
|
SelectAllServices() ([]*Service, error)
|
||||||
Count24HFailures() uint64
|
Count24HFailures() uint64
|
||||||
|
|
|
@ -14,6 +14,6 @@ type Failure struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type FailureInterface interface {
|
type FailureInterface interface {
|
||||||
Ago() string
|
Ago() string // Ago returns a human readble timestamp
|
||||||
ParseError() string
|
ParseError() string // ParseError returns a human readable error for a service failure
|
||||||
}
|
}
|
||||||
|
|
|
@ -89,16 +89,19 @@ type ServiceInterface interface {
|
||||||
AllCheckins() []*Checkin
|
AllCheckins() []*Checkin
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start will create a channel for the service checking go routine
|
||||||
func (s *Service) Start() {
|
func (s *Service) Start() {
|
||||||
s.Running = make(chan bool)
|
s.Running = make(chan bool)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close will stop the go routine that is checking if service is online or not
|
||||||
func (s *Service) Close() {
|
func (s *Service) Close() {
|
||||||
if s.IsRunning() {
|
if s.IsRunning() {
|
||||||
close(s.Running)
|
close(s.Running)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IsRunning returns true if the service go routine is running
|
||||||
func (s *Service) IsRunning() bool {
|
func (s *Service) IsRunning() bool {
|
||||||
if s.Running == nil {
|
if s.Running == nil {
|
||||||
return false
|
return false
|
||||||
|
|
|
@ -23,11 +23,13 @@ import (
|
||||||
"math/rand"
|
"math/rand"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// HashPassword returns the bcrypt hash of a password string
|
||||||
func HashPassword(password string) string {
|
func HashPassword(password string) string {
|
||||||
bytes, _ := bcrypt.GenerateFromPassword([]byte(password), 14)
|
bytes, _ := bcrypt.GenerateFromPassword([]byte(password), 14)
|
||||||
return string(bytes)
|
return string(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewSHA1Hash returns a random SHA1 hash based on a specific length
|
||||||
func NewSHA1Hash(n ...int) string {
|
func NewSHA1Hash(n ...int) string {
|
||||||
noRandomCharacters := 32
|
noRandomCharacters := 32
|
||||||
if len(n) > 0 {
|
if len(n) > 0 {
|
||||||
|
@ -51,6 +53,7 @@ func RandomString(n int) string {
|
||||||
return string(b)
|
return string(b)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sha256 returns a SHA256 hash as string from []byte
|
||||||
func Sha256(data []byte) string {
|
func Sha256(data []byte) string {
|
||||||
h := sha1.New()
|
h := sha1.New()
|
||||||
h.Write(data)
|
h.Write(data)
|
||||||
|
|
|
@ -38,6 +38,7 @@ func Logger() *lumberjack.Logger {
|
||||||
return ljLogger
|
return ljLogger
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// createLog will create the '/logs' directory based on a directory
|
||||||
func createLog(dir string) error {
|
func createLog(dir string) error {
|
||||||
var err error
|
var err error
|
||||||
_, err = os.Stat(dir + "/logs")
|
_, err = os.Stat(dir + "/logs")
|
||||||
|
@ -56,6 +57,7 @@ func createLog(dir string) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InitLogs will create the '/logs' directory and creates a file '/logs/statup.log' for application logging
|
||||||
func InitLogs() error {
|
func InitLogs() error {
|
||||||
err := createLog(Directory)
|
err := createLog(Directory)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -90,6 +92,7 @@ func 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 {
|
func Log(level int, err interface{}) error {
|
||||||
pushLastLine(err)
|
pushLastLine(err)
|
||||||
var outErr error
|
var outErr error
|
||||||
|
@ -122,6 +125,7 @@ func Log(level int, err interface{}) error {
|
||||||
return outErr
|
return outErr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Http returns a log for a HTTP request
|
||||||
func Http(r *http.Request) string {
|
func Http(r *http.Request) string {
|
||||||
msg := fmt.Sprintf("%v (%v) | IP: %v", r.RequestURI, r.Method, r.Host)
|
msg := fmt.Sprintf("%v (%v) | IP: %v", r.RequestURI, r.Method, r.Host)
|
||||||
fmtLogs.Printf("WEB: %v\n", msg)
|
fmtLogs.Printf("WEB: %v\n", msg)
|
||||||
|
@ -140,6 +144,7 @@ func pushLastLine(line interface{}) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetLastLine returns 1 line for a recent log entry
|
||||||
func GetLastLine() *LogRow {
|
func GetLastLine() *LogRow {
|
||||||
LockLines.Lock()
|
LockLines.Lock()
|
||||||
defer LockLines.Unlock()
|
defer LockLines.Unlock()
|
||||||
|
|
|
@ -31,6 +31,7 @@ var (
|
||||||
Directory string
|
Directory string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// init will set the utils.Directory to the current running directory, or STATUP_DIR if it is set
|
||||||
func init() {
|
func init() {
|
||||||
if os.Getenv("STATUP_DIR") != "" {
|
if os.Getenv("STATUP_DIR") != "" {
|
||||||
Directory = os.Getenv("STATUP_DIR")
|
Directory = os.Getenv("STATUP_DIR")
|
||||||
|
@ -39,15 +40,18 @@ func init() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// StringInt converts a string to an int64
|
||||||
func StringInt(s string) int64 {
|
func StringInt(s string) int64 {
|
||||||
num, _ := strconv.Atoi(s)
|
num, _ := strconv.Atoi(s)
|
||||||
return int64(num)
|
return int64(num)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// IntString converts a int to a string
|
||||||
func IntString(s int) string {
|
func IntString(s int) string {
|
||||||
return strconv.Itoa(s)
|
return strconv.Itoa(s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dir returns the current working directory
|
||||||
func dir() string {
|
func dir() string {
|
||||||
dir, err := os.Getwd()
|
dir, err := os.Getwd()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -57,16 +61,17 @@ func dir() string {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Timestamp time.Time
|
type Timestamp time.Time
|
||||||
|
|
||||||
type Timestamper interface {
|
type Timestamper interface {
|
||||||
Ago() string
|
Ago() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Ago returns a human readable timestamp based on the Timestamp (time.Time) interface
|
||||||
func (t Timestamp) Ago() string {
|
func (t Timestamp) Ago() string {
|
||||||
got, _ := timeago.TimeAgoWithTime(time.Now(), time.Time(t))
|
got, _ := timeago.TimeAgoWithTime(time.Now(), time.Time(t))
|
||||||
return got
|
return got
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnderScoreString will return a string that replaces spaces and other characters to underscores
|
||||||
func UnderScoreString(str string) string {
|
func UnderScoreString(str string) string {
|
||||||
|
|
||||||
// convert every letter to lower case
|
// convert every letter to lower case
|
||||||
|
@ -90,6 +95,7 @@ func UnderScoreString(str string) string {
|
||||||
return newStr
|
return newStr
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileExists returns true if a file exists
|
||||||
func FileExists(name string) bool {
|
func FileExists(name string) bool {
|
||||||
if _, err := os.Stat(name); err != nil {
|
if _, err := os.Stat(name); err != nil {
|
||||||
if os.IsNotExist(err) {
|
if os.IsNotExist(err) {
|
||||||
|
@ -99,6 +105,7 @@ func FileExists(name string) bool {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteFile will attempt to delete a file
|
||||||
func DeleteFile(file string) error {
|
func DeleteFile(file string) error {
|
||||||
Log(1, "deleting file: "+file)
|
Log(1, "deleting file: "+file)
|
||||||
err := os.Remove(file)
|
err := os.Remove(file)
|
||||||
|
@ -108,10 +115,12 @@ func DeleteFile(file string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteDirectory will attempt to delete a directory and all contents inside
|
||||||
func DeleteDirectory(directory string) error {
|
func DeleteDirectory(directory string) error {
|
||||||
return os.RemoveAll(directory)
|
return os.RemoveAll(directory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Command will run a terminal command with 'sh -c COMMAND' and return stdout and errOut as strings
|
||||||
func Command(cmd string) (string, string, error) {
|
func Command(cmd string) (string, string, error) {
|
||||||
Log(1, "running command: "+cmd)
|
Log(1, "running command: "+cmd)
|
||||||
testCmd := exec.Command("sh", "-c", cmd)
|
testCmd := exec.Command("sh", "-c", cmd)
|
||||||
|
@ -142,6 +151,7 @@ func Command(cmd string) (string, string, error) {
|
||||||
return outStr, errStr, err
|
return outStr, errStr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// copyAndCapture will read a terminal command into bytes
|
||||||
func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
|
func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
|
||||||
var out []byte
|
var out []byte
|
||||||
buf := make([]byte, 1024, 1024)
|
buf := make([]byte, 1024, 1024)
|
||||||
|
|
Loading…
Reference in New Issue