mirror of https://github.com/statping/statping
Merge branch 'master' into feature/tls-renegotiation
commit
23e8b9f8db
|
@ -1,19 +0,0 @@
|
|||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 60
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- bug
|
||||
- urgent
|
||||
- pinned
|
||||
- security
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions. If this is still an problem, please create a new issue.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: true
|
|
@ -0,0 +1,162 @@
|
|||
name: Dev Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.x'
|
||||
|
||||
- name: Add GOBIN to PATH
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
|
||||
- name: Install Global Dependencies
|
||||
run: npm install -g yarn sass cross-env
|
||||
|
||||
- name: Checkout Statping Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/cache@v1
|
||||
id: nodecache
|
||||
with:
|
||||
path: ./frontend/node_modules
|
||||
key: ${{ runner.os }}-node-${{ hashFiles('**/package.json') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-node-
|
||||
|
||||
- name: Download Frontend Dependencies
|
||||
if: steps.nodecache.outputs.cache-hit != 'true'
|
||||
working-directory: ./frontend
|
||||
run: yarn
|
||||
|
||||
- uses: actions/cache@v1
|
||||
id: golangcache
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/go/bin
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Download Go mods
|
||||
if: steps.golangcache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
go mod download
|
||||
go mod verify
|
||||
make test-deps
|
||||
|
||||
- name: Build Frontend Statping
|
||||
run: make clean frontend-build
|
||||
|
||||
- name: Upload Compiled Frontend
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: static-frontend
|
||||
path: /home/runner/work/statping/statping/source/dist
|
||||
|
||||
test:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Statping Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Go
|
||||
uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: '1.14.x'
|
||||
|
||||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
|
||||
- name: Install Global Dependencies
|
||||
run: npm install -g yarn sass newman cross-env wait-on @sentry/cli
|
||||
|
||||
- uses: actions/cache@v1
|
||||
id: golangcache
|
||||
with:
|
||||
path: |
|
||||
~/go/pkg/mod
|
||||
~/go/bin
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-go-
|
||||
|
||||
- name: Download Go mods
|
||||
if: steps.golangcache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
go mod download
|
||||
go mod verify
|
||||
make test-deps
|
||||
|
||||
- name: Download Compiled Frontend
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: static-frontend
|
||||
path: /home/runner/work/statping/statping/source/dist
|
||||
|
||||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
run: |
|
||||
make build
|
||||
chmod +x statping
|
||||
mv statping $(go env GOPATH)/bin/
|
||||
|
||||
- name: Go Tests
|
||||
run: SASS=`which sass` go test -v -covermode=count -coverprofile=coverage.out -p=1 ./...
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
DB_CONN: sqlite3
|
||||
STATPING_DIR: /home/runner/work/statping/statping
|
||||
API_KEY: demopassword123
|
||||
DISABLE_LOGS: true
|
||||
ALLOW_REPORTS: true
|
||||
COVERALLS: ${{ secrets.COVERALLS }}
|
||||
DISCORD_URL: ${{ secrets.DISCORD_URL }}
|
||||
EMAIL_HOST: ${{ secrets.EMAIL_HOST }}
|
||||
EMAIL_USER: ${{ secrets.EMAIL_USER }}
|
||||
EMAIL_PASS: ${{ secrets.EMAIL_PASS }}
|
||||
EMAIL_OUTGOING: ${{ secrets.EMAIL_OUTGOING }}
|
||||
EMAIL_SEND_TO: ${{ secrets.EMAIL_SEND_TO }}
|
||||
EMAIL_PORT: ${{ secrets.EMAIL_PORT }}
|
||||
MOBILE_ID: ${{ secrets.MOBILE_ID }}
|
||||
MOBILE_NUMBER: ${{ secrets.MOBILE_NUMBER }}
|
||||
PUSHOVER_TOKEN: ${{ secrets.PUSHOVER_TOKEN }}
|
||||
PUSHOVER_API: ${{ secrets.PUSHOVER_API }}
|
||||
SLACK_URL: ${{ secrets.SLACK_URL }}
|
||||
TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }}
|
||||
TELEGRAM_CHANNEL: ${{ secrets.TELEGRAM_CHANNEL }}
|
||||
TWILIO_SID: ${{ secrets.TWILIO_SID }}
|
||||
TWILIO_SECRET: ${{ secrets.TWILIO_SECRET }}
|
||||
TWILIO_FROM: ${{ secrets.TWILIO_FROM }}
|
||||
TWILIO_TO: ${{ secrets.TWILIO_TO }}
|
||||
|
||||
- name: Coveralls Testing Coverage
|
||||
run: goveralls -coverprofile=coverage.out -repotoken $COVERALLS
|
||||
env:
|
||||
COVERALLS: ${{ secrets.COVERALLS }}
|
|
@ -0,0 +1,503 @@
|
|||
name: Master Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Add GOBIN to PATH
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Install Global Dependencies
|
||||
run: npm install -g yarn sass cross-env
|
||||
|
||||
- name: Download Frontend Dependencies
|
||||
if: steps.nodecache.outputs.cache-hit != 'true'
|
||||
working-directory: ./frontend
|
||||
run: yarn
|
||||
|
||||
- name: Download Go mods
|
||||
if: steps.golangcache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
go mod download
|
||||
go mod verify
|
||||
make test-deps
|
||||
|
||||
- name: Build Frontend Statping
|
||||
run: make clean compile
|
||||
|
||||
- name: Upload Compiled Frontend (rice-box.go)
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
test:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10.8
|
||||
env:
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_PASSWORD: password123
|
||||
POSTGRES_DB: statping
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
||||
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: password123
|
||||
MYSQL_DATABASE: statping
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Global Dependencies
|
||||
run: npm install -g yarn sass newman cross-env wait-on @sentry/cli
|
||||
|
||||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Download Compiled Frontend (rice-box.go)
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
run: |
|
||||
make build
|
||||
chmod +x statping
|
||||
mv statping $(go env GOPATH)/bin/
|
||||
|
||||
- name: Go Tests
|
||||
run: |
|
||||
go get gotest.tools/gotestsum
|
||||
gotestsum --no-summary=skipped --format dots -- -covermode=count -coverprofile=coverage.out -p=1 ./...
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
DB_CONN: sqlite3
|
||||
STATPING_DIR: ${{ github.workspace }}
|
||||
API_KEY: demopassword123
|
||||
DISABLE_LOGS: true
|
||||
ALLOW_REPORTS: true
|
||||
COVERALLS: ${{ secrets.COVERALLS }}
|
||||
DISCORD_URL: ${{ secrets.DISCORD_URL }}
|
||||
EMAIL_HOST: ${{ secrets.EMAIL_HOST }}
|
||||
EMAIL_USER: ${{ secrets.EMAIL_USER }}
|
||||
EMAIL_PASS: ${{ secrets.EMAIL_PASS }}
|
||||
EMAIL_OUTGOING: ${{ secrets.EMAIL_OUTGOING }}
|
||||
EMAIL_SEND_TO: ${{ secrets.EMAIL_SEND_TO }}
|
||||
EMAIL_PORT: ${{ secrets.EMAIL_PORT }}
|
||||
MOBILE_ID: ${{ secrets.MOBILE_ID }}
|
||||
MOBILE_NUMBER: ${{ secrets.MOBILE_NUMBER }}
|
||||
PUSHOVER_TOKEN: ${{ secrets.PUSHOVER_TOKEN }}
|
||||
PUSHOVER_API: ${{ secrets.PUSHOVER_API }}
|
||||
SLACK_URL: ${{ secrets.SLACK_URL }}
|
||||
TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }}
|
||||
TELEGRAM_CHANNEL: ${{ secrets.TELEGRAM_CHANNEL }}
|
||||
TWILIO_SID: ${{ secrets.TWILIO_SID }}
|
||||
TWILIO_SECRET: ${{ secrets.TWILIO_SECRET }}
|
||||
TWILIO_FROM: ${{ secrets.TWILIO_FROM }}
|
||||
TWILIO_TO: ${{ secrets.TWILIO_TO }}
|
||||
|
||||
- name: Coveralls Testing Coverage
|
||||
run: |
|
||||
go get github.com/mattn/goveralls
|
||||
goveralls -coverprofile=coverage.out -repotoken $COVERALLS
|
||||
env:
|
||||
COVERALLS: ${{ secrets.COVERALLS }}
|
||||
|
||||
test-postman:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
|
||||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Download Compiled Frontend (rice-box.go)
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
run: |
|
||||
make build
|
||||
chmod +x statping
|
||||
mv statping $(go env GOPATH)/bin/
|
||||
|
||||
- name: Run Statping
|
||||
run: |
|
||||
API_SECRET=demosecret123 statping --port=8080 > /dev/null &
|
||||
sleep 3
|
||||
|
||||
- name: Postman Tests
|
||||
uses: matt-ball/newman-action@master
|
||||
with:
|
||||
postmanApiKey: ${{ secrets.POSTMAN_API }}
|
||||
collection: ./dev/postman.json
|
||||
environment: ./dev/postman_environment.json
|
||||
timeoutRequest: 15000
|
||||
delayRequest: 1000
|
||||
|
||||
build-mac:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Download Go mods
|
||||
if: steps.golangcache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
go mod download
|
||||
go mod verify
|
||||
make test-deps
|
||||
|
||||
- name: Download Compiled Frontend (rice-box.go)
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
- name: Build Binaries
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: $GITHUB_SHA
|
||||
run: make build-mac
|
||||
|
||||
- name: Upload MacOSX Builds
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: darwin-builds
|
||||
path: ./build
|
||||
|
||||
build-linux:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Libraries
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install libc-dev gcc-multilib build-essential musl-dev gcc g++ -y
|
||||
|
||||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Download Go mods
|
||||
if: steps.golangcache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
go mod download
|
||||
go mod verify
|
||||
make test-deps
|
||||
|
||||
- name: Download Compiled Frontend (rice-box.go)
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
- name: Build Binaries
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: $GITHUB_SHA
|
||||
run: |
|
||||
go env
|
||||
make build-linux
|
||||
|
||||
- name: Upload Linux Builds
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: linux-builds
|
||||
path: ./build
|
||||
|
||||
build-windows:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Download Go mods
|
||||
if: steps.golangcache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
go mod download
|
||||
go mod verify
|
||||
make test-deps
|
||||
|
||||
- name: Download Compiled Frontend (rice-box.go)
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
- name: Build Binaries
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: $GITHUB_SHA
|
||||
run: make build-win
|
||||
|
||||
- name: Upload Windows Builds
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: windows-builds
|
||||
path: ./build
|
||||
|
||||
upload-release:
|
||||
needs: [test, test-postman, build-linux, build-mac, build-windows]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Download Linux Builds
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: linux-builds
|
||||
path: ./linux
|
||||
|
||||
- name: Download MacOSX Builds
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: darwin-builds
|
||||
path: ./darwin
|
||||
|
||||
- name: Download Windows Builds
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: windows-builds
|
||||
path: ./windows
|
||||
|
||||
- name: Upload Linux Release
|
||||
id: upload-linux-asset
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ env.VERSION }}
|
||||
with:
|
||||
tag_name: v${{ env.VERSION }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
files: |
|
||||
linux/statping-freebsd-386.tar.gz
|
||||
linux/statping-freebsd-amd64.tar.gz
|
||||
linux/statping-freebsd-arm.tar.gz
|
||||
linux/statping-linux-386.tar.gz
|
||||
linux/statping-linux-amd64.tar.gz
|
||||
linux/statping-linux-arm.tar.gz
|
||||
linux/statping-linux-arm64.tar.gz
|
||||
linux/statping-openbsd-386.tar.gz
|
||||
linux/statping-openbsd-amd64.tar.gz
|
||||
linux/statping-openbsd-arm.tar.gz
|
||||
linux/statping-openbsd-arm64.tar.gz
|
||||
|
||||
- name: Upload MaxOSX Release
|
||||
id: upload-darwin-asset
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ env.VERSION }}
|
||||
with:
|
||||
tag_name: v${{ env.VERSION }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
files: |
|
||||
darwin/statping-darwin-386.tar.gz
|
||||
darwin/statping-darwin-amd64.tar.gz
|
||||
|
||||
- name: Upload Windows Release
|
||||
id: upload-windows-asset
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ env.VERSION }}
|
||||
with:
|
||||
tag_name: v${{ env.VERSION }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
files: |
|
||||
windows/statping-windows-386.zip
|
||||
windows/statping-windows-amd64.zip
|
||||
windows/statping-windows-arm.zip
|
||||
|
||||
docker-release:
|
||||
needs: upload-release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Statping Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setting ENV's
|
||||
run: echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Base Docker Image
|
||||
uses: elgohr/Publish-Docker-Github-Action@master
|
||||
with:
|
||||
name: statping/statping
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
dockerfile: Dockerfile.base
|
||||
tags: "base"
|
||||
|
||||
- name: Latest/Version Docker Image
|
||||
uses: elgohr/Publish-Docker-Github-Action@master
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
ARCH: amd64
|
||||
DOCKER_CLI_EXPERIMENTAL: enabled
|
||||
with:
|
||||
name: statping/statping
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
dockerfile: Dockerfile
|
||||
tags: "latest,v${{ env.VERSION }}"
|
||||
buildargs: VERSION,ARCH
|
||||
|
||||
sentry-release:
|
||||
needs: upload-release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Statping Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setting ENV's
|
||||
run: echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Sentry Backend Release
|
||||
uses: tclindner/sentry-releases-action@v1.0.0
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||
SENTRY_ORG: statping
|
||||
SENTRY_PROJECT: backend
|
||||
with:
|
||||
tagName: v${{ env.VERSION }}
|
||||
environment: production
|
||||
|
||||
- name: Sentry Frontend Release
|
||||
uses: tclindner/sentry-releases-action@v1.0.0
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||
SENTRY_ORG: statping
|
||||
SENTRY_PROJECT: frontend
|
||||
with:
|
||||
tagName: v${{ env.VERSION }}
|
||||
environment: production
|
||||
|
||||
homebrew-release:
|
||||
needs: upload-release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Statping Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setting ENV's
|
||||
run: echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Update Homebrew Package
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
TRAVIS_API: ${{ secrets.TRAVIS_API }}
|
||||
run: make publish-homebrew
|
||||
|
||||
slack-update:
|
||||
needs: upload-release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Statping Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setting ENV's
|
||||
run: echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Slack Notification
|
||||
uses: rtCamp/action-slack-notify@v2.0.0
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_URL }}
|
||||
SLACK_CHANNEL: dev
|
||||
SLACK_USERNAME: StatpingDev
|
|
@ -1,22 +0,0 @@
|
|||
name: ReleaseWorkflow
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published, prereleased]
|
||||
|
||||
jobs:
|
||||
createSentryRelease:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@master
|
||||
- name: Create a Sentry release
|
||||
uses: tclindner/sentry-releases-action@v1.0.0
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||
SENTRY_ORG: statping
|
||||
SENTRY_PROJECT: backend
|
||||
with:
|
||||
tagName: ${{ github.ref }}
|
||||
environment: qa
|
|
@ -1,22 +0,0 @@
|
|||
name: ReleaseFrontendWorkflow
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published, prereleased]
|
||||
|
||||
jobs:
|
||||
createSentryRelease:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@master
|
||||
- name: Create a Sentry Frontend release
|
||||
uses: tclindner/sentry-releases-action@v1.0.0
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_FRONTEND_AUTH_TOKEN }}
|
||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||
SENTRY_ORG: statping
|
||||
SENTRY_PROJECT: frontend
|
||||
with:
|
||||
tagName: ${{ github.ref }}
|
||||
environment: qa
|
|
@ -1,14 +0,0 @@
|
|||
on: push
|
||||
name: Slack Notification
|
||||
jobs:
|
||||
slackNotification:
|
||||
name: Slack Notification
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Slack Notification
|
||||
uses: rtCamp/action-slack-notify@v2.0.0
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_URL }}
|
||||
SLACK_CHANNEL: dev
|
||||
SLACK_USERNAME: Github
|
|
@ -0,0 +1,17 @@
|
|||
name: Issues
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 0 * * *"
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Stale Issues
|
||||
uses: actions/stale@v1.1.0
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: "This issue hasn't had any updates in a while. If this is still a problem, please create a new issue."
|
||||
days-before-stale: 30
|
||||
days-before-close: 5
|
||||
exempt-issue-labels: "bug,urgent,feature,pinned,locked"
|
52
.travis.yml
52
.travis.yml
|
@ -1,52 +0,0 @@
|
|||
after_success:
|
||||
- "if [[ \"$TRAVIS_BRANCH\" == \"master\" && \"$TRAVIS_PULL_REQUEST\" = \"false\" ]]; then make travis-build; fi"
|
||||
before_install:
|
||||
- "rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install stable"
|
||||
- "nvm install 10.17.0"
|
||||
- "nvm use 10.17.0 --default"
|
||||
before_script:
|
||||
- "mysql -e 'CREATE DATABASE IF NOT EXISTS test;'"
|
||||
- "psql -c 'create database test;' -U postgres"
|
||||
branches:
|
||||
only:
|
||||
- master
|
||||
- dev
|
||||
env:
|
||||
global:
|
||||
- "PATH=$HOME/.local/bin:$PATH"
|
||||
- DB_HOST=localhost
|
||||
- DB_USER=travis
|
||||
- DB_PASS=
|
||||
- DB_DATABASE=test
|
||||
- GO_ENV=test
|
||||
- STATPING_DIR=$GOPATH/src/github.com/statping/statping
|
||||
go: 1.14
|
||||
go_import_path: github.com/statping/statping
|
||||
install:
|
||||
- "npm install -g sass newman cross-env wait-on @sentry/cli"
|
||||
- "pip install --user awscli"
|
||||
- "go get github.com/mattn/goveralls"
|
||||
- "go mod download"
|
||||
- "go mod verify"
|
||||
- "make test-deps yarn clean compile install"
|
||||
language: go
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- libgconf-2-4
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: master
|
||||
fast_finish: true
|
||||
notifications:
|
||||
email: true
|
||||
os:
|
||||
- linux
|
||||
script:
|
||||
- "travis_retry make clean test-ci"
|
||||
- "if [[ \"$TRAVIS_BRANCH\" == \"master\" && \"$TRAVIS_PULL_REQUEST\" = \"false\" ]]; then make coverage; fi"
|
||||
services:
|
||||
- docker
|
||||
- postgresql
|
||||
- mysql
|
||||
- mongodb
|
37
CHANGELOG.md
37
CHANGELOG.md
|
@ -1,3 +1,40 @@
|
|||
# 0.90.33 (04-24-2020)
|
||||
- Fixed config loading method
|
||||
|
||||
# 0.90.32 (04-23-2020)
|
||||
- Modified the saving and loading process config.yml
|
||||
|
||||
# 0.90.31 (04-21-2020)
|
||||
- Version bump for github actions
|
||||
|
||||
# 0.90.30 (04-19-2020)
|
||||
- Attempt to fix Github Actions build process
|
||||
- Fix for empty database connection string, and not starting in setup mode
|
||||
|
||||
# 0.90.29 (04-19-2020)
|
||||
- Added HTTP Redirects for services
|
||||
- Removed use of SASS environment variable, now finds path or sends error
|
||||
- Modified Makefile to create new snapcraft versions
|
||||
- Fixed issue when logs are not initiated yet. Issue #502
|
||||
- Fixed issue when SQLite (statping.db) is not found Issue #499
|
||||
- Modified port flag in Docker image
|
||||
- Fixed issue on startup without config.yml file not starting in setup mode
|
||||
|
||||
# 0.90.28 (04-16-2020)
|
||||
- Fixed postgres timestamp grouping
|
||||
- Added postman (newman) API testing
|
||||
- Added Viper and Cobra config/env parsing package
|
||||
- Added more golang tests
|
||||
- Modified handlers to use a more generic find method
|
||||
- Added 'env' command to show variables used in config
|
||||
- Added 'reset' command that will delete files and backup .db file for a fresh install
|
||||
- Added error type that has common errors with http status code based on error
|
||||
|
||||
# 0.90.27 (04-15-2020)
|
||||
- Fixed postgres database table creation process
|
||||
- Modified go build process, additional ARCHs
|
||||
- Added 'SAMPLE_DATA' environment variable to disable example data on startup. (default: true)
|
||||
|
||||
# 0.90.26 (04-13-2020)
|
||||
- Fixed Delete Failures button/function
|
||||
- Removed timezone field from Settings (core)
|
||||
|
|
|
@ -18,5 +18,4 @@ EXPOSE $PORT
|
|||
|
||||
HEALTHCHECK --interval=60s --timeout=10s --retries=3 CMD curl -s "http://localhost:$PORT/health" | jq -r -e ".online==true"
|
||||
|
||||
CMD statping -port $PORT
|
||||
|
||||
CMD statping --port $PORT
|
||||
|
|
97
Makefile
97
Makefile
|
@ -10,7 +10,7 @@ PUBLISH_BODY='{ "request": { "branch": "master", "message": "Homebrew update ver
|
|||
TRAVIS_BUILD_CMD='{ "request": { "branch": "master", "message": "Compile master for Statping v${VERSION}", "config": { "merge_mode": "replace", "language": "go", "go": 1.14, "install": true, "sudo": "required", "services": ["docker"], "env": { "secure": "${TRVIS_SECRET}" }, "before_deploy": ["git config --local user.name \"hunterlong\"", "git config --local user.email \"info@socialeck.com\"", "git tag v$(VERSION) --force"], "deploy": [{ "provider": "releases", "api_key": "$$GITHUB_TOKEN", "file_glob": true, "file": "build/*", "skip_cleanup": true, "on": { "branch": "master" } }], "before_script": ["rm -rf ~/.nvm && git clone https://github.com/creationix/nvm.git ~/.nvm && (cd ~/.nvm && git checkout `git describe --abbrev=0 --tags`) && source ~/.nvm/nvm.sh && nvm install stable", "nvm install 10.17.0", "nvm use 10.17.0 --default", "npm install -g sass yarn cross-env", "pip install --user awscli"], "script": ["make release"], "after_success": [], "after_deploy": ["make post-release"] } } }'
|
||||
TEST_DIR=$(GOPATH)/src/github.com/statping/statping
|
||||
PATH:=/usr/local/bin:$(GOPATH)/bin:$(PATH)
|
||||
OS = darwin freebsd linux openbsd
|
||||
OS = freebsd linux openbsd
|
||||
ARCHS = 386 arm amd64 arm64
|
||||
|
||||
all: clean yarn-install compile docker-base docker-vue build-all
|
||||
|
@ -39,7 +39,7 @@ release: test-deps
|
|||
make build-all
|
||||
|
||||
test-ci: clean compile test-deps
|
||||
SASS=`which sass` go test -v -covermode=count -coverprofile=coverage.out -p=1 ./...
|
||||
DB_CONN=sqlite go test -v -covermode=count -coverprofile=coverage.out -p=1 ./...
|
||||
goveralls -coverprofile=coverage.out -service=travis-ci -repotoken ${COVERALLS}
|
||||
|
||||
cypress: clean
|
||||
|
@ -85,7 +85,7 @@ db-up:
|
|||
docker-compose -f dev/docker-compose.db.yml up -d --remove-orphans
|
||||
|
||||
db-down:
|
||||
docker-compose -f dev/docker-compose.full.yml down --remove-orphans
|
||||
docker-compose -f dev/docker-compose.db.yml down --volumes --remove-orphans
|
||||
|
||||
console:
|
||||
docker exec -t -i statping /bin/sh
|
||||
|
@ -151,30 +151,44 @@ install-local: build
|
|||
generate:
|
||||
cd source && go generate
|
||||
|
||||
build-bin:
|
||||
mkdir build
|
||||
build-linux:
|
||||
mkdir build || true
|
||||
export PWD=`pwd`
|
||||
@for arch in $(ARCHS);\
|
||||
do \
|
||||
for os in $(OS);\
|
||||
do \
|
||||
echo "Building $$os-$$arch"; \
|
||||
echo "Building v$$VERSION for $$os-$$arch"; \
|
||||
mkdir -p releases/statping-$$os-$$arch/; \
|
||||
GO111MODULE="on" GOOS=$$os GOARCH=$$arch go build -a -ldflags "-X main.VERSION=${VERSION} -X main.COMMIT=$(TRAVIS_COMMIT)" -o releases/statping-$$os-$$arch/statping ${PWD}/cmd; \
|
||||
chmod +x releases/statping-$$os-$$arch/statping; \
|
||||
tar -czf releases/statping-$$os-$$arch.tar.gz -C releases/statping-$$os-$$arch statping; \
|
||||
GO111MODULE="on" GOOS=$$os GOARCH=$$arch go build -a -ldflags "-X main.VERSION=${VERSION} -X main.COMMIT=$(TRAVIS_COMMIT)" -o releases/statping-$$os-$$arch/statping ${PWD}/cmd || true; \
|
||||
chmod +x releases/statping-$$os-$$arch/statping || true; \
|
||||
tar -czf releases/statping-$$os-$$arch.tar.gz -C releases/statping-$$os-$$arch statping || true; \
|
||||
done \
|
||||
done
|
||||
find ./releases/ -name "*.tar.gz" -type f -size +1M -exec mv "{}" build/ \;
|
||||
|
||||
build-win:
|
||||
build-mac:
|
||||
mkdir build || true
|
||||
export PWD=`pwd`
|
||||
@for arch in $(ARCHS);\
|
||||
do \
|
||||
echo "Building windows-$$arch"; \
|
||||
echo "Building v$$VERSION for darwin-$$arch"; \
|
||||
mkdir -p releases/statping-darwin-$$arch/; \
|
||||
GO111MODULE="on" GOOS=darwin GOARCH=$$arch go build -a -ldflags "-X main.VERSION=${VERSION} -X main.COMMIT=$(TRAVIS_COMMIT)" -o releases/statping-darwin-$$arch/statping ${PWD}/cmd || true; \
|
||||
chmod +x releases/statping-darwin-$$arch/statping || true; \
|
||||
tar -czf releases/statping-darwin-$$arch.tar.gz -C releases/statping-darwin-$$arch statping || true; \
|
||||
done
|
||||
find ./releases/ -name "*.tar.gz" -type f -size +1M -exec mv "{}" build/ \;
|
||||
|
||||
build-win:
|
||||
mkdir build || true
|
||||
export PWD=`pwd`
|
||||
@for arch in $(ARCHS);\
|
||||
do \
|
||||
echo "Building v$$VERSION for windows-$$arch"; \
|
||||
mkdir -p releases/statping-windows-$$arch/; \
|
||||
GO111MODULE="on" GOOS=windows GOARCH=$$arch go build -a -ldflags "-X main.VERSION=${VERSION} -X main.COMMIT=$(TRAVIS_COMMIT)" -o releases/statping-windows-$$arch/statping.exe ${PWD}/cmd; \
|
||||
chmod +x releases/statping-windows-$$arch/statping.exe; \
|
||||
GO111MODULE="on" GOOS=windows GOARCH=$$arch go build -a -ldflags "-X main.VERSION=${VERSION} -X main.COMMIT=$(TRAVIS_COMMIT)" -o releases/statping-windows-$$arch/statping.exe ${PWD}/cmd || true; \
|
||||
chmod +x releases/statping-windows-$$arch/statping.exe || true; \
|
||||
zip -j releases/statping-windows-$$arch.zip releases/statping-windows-$$arch/statping.exe || true; \
|
||||
done
|
||||
find ./releases/ -name "*.zip" -type f -size +1M -exec mv "{}" build/ \;
|
||||
|
@ -193,15 +207,14 @@ clean:
|
|||
rm -rf frontend/{logs,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
rm -rf dev/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log,test/app,plugin/*.so}
|
||||
rm -rf {parts,prime,snap,stage}
|
||||
rm -rf dev/test/cypress/videos
|
||||
rm -rf frontend/cypress/videos
|
||||
rm -f coverage.* sass
|
||||
rm -f source/rice-box.go
|
||||
rm -rf **/*.db-journal
|
||||
rm -rf *.snap
|
||||
find . -name "*.out" -type f -delete
|
||||
find . -name "*.cpu" -type f -delete
|
||||
find . -name "*.mem" -type f -delete
|
||||
rm -rf {build,releases,tmp}
|
||||
rm -rf {build,releases,tmp,source/build,snap}
|
||||
|
||||
print_details:
|
||||
@echo \==== Statping Development Instance ====
|
||||
|
@ -219,7 +232,7 @@ print_details:
|
|||
@echo \==== Monitoring and IDE ====
|
||||
@echo \Grafana: http://localhost:3000 \(username: admin, password: admin\)
|
||||
|
||||
build-all: clean compile build-bin build-win
|
||||
build-all: clean compile build-linux build-mac build-win
|
||||
|
||||
coverage: test-deps
|
||||
$(GOPATH)/bin/goveralls -coverprofile=coverage.out -service=travis -repotoken $(COVERALLS)
|
||||
|
@ -232,22 +245,24 @@ download-key:
|
|||
wget -O statping.gpg $(SIGN_URL)
|
||||
gpg --import statping.gpg
|
||||
|
||||
# build :latest docker tag
|
||||
docker-build-latest:
|
||||
docker build --build-arg VERSION=${VERSION} -t statping/statping:latest --no-cache -f Dockerfile .
|
||||
docker tag statping/statping:latest statping/statping:v${VERSION}
|
||||
|
||||
# push the :dev docker tag using curl
|
||||
publish-dev:
|
||||
curl -H "Content-Type: application/json" --data '{"docker_tag": "dev"}' -X POST $(DOCKER)
|
||||
dockerhub-dev:
|
||||
docker build --build-arg VERSION=${VERSION} -t statping/statping:dev --no-cache -f Dockerfile.base .
|
||||
docker push statping/statping:dev
|
||||
|
||||
publish-latest: publish-base
|
||||
curl -H "Content-Type: application/json" --data '{"docker_tag": "latest"}' -X POST $(DOCKER)
|
||||
dockerhub:
|
||||
docker build --build-arg VERSION=${VERSION} -t statping/statping:base --no-cache -f Dockerfile.base .
|
||||
docker build --build-arg VERSION=${VERSION} -t statping/statping:latest --no-cache -f Dockerfile .
|
||||
docker tag statping/statping statping/statping:v${VERSION}
|
||||
docker push statping/statping:base
|
||||
docker push statping/statping:v${VERSION}
|
||||
docker push statping/statping
|
||||
|
||||
publish-base:
|
||||
curl -H "Content-Type: application/json" --data '{"docker_tag": "base"}' -X POST $(DOCKER)
|
||||
docker-build-dev:
|
||||
docker build --build-arg VERSION=${VERSION} -t hunterlong/statping:latest --no-cache -f Dockerfile .
|
||||
docker tag hunterlong/statping:dev hunterlong/statping:dev-v${VERSION}
|
||||
|
||||
post-release: frontend-build upload_to_s3 publish-homebrew publish-latest
|
||||
post-release: frontend-build upload_to_s3 publish-homebrew dockerhub
|
||||
|
||||
# update the homebrew application to latest for mac
|
||||
publish-homebrew:
|
||||
|
@ -274,20 +289,28 @@ sentry-release:
|
|||
sentry-cli releases set-commits --auto v${VERSION}
|
||||
sentry-cli releases finalize v${VERSION}
|
||||
|
||||
snapcraft: clean compile build-bin
|
||||
snapcraft: clean compile build-linux
|
||||
mkdir snap
|
||||
mv snapcraft.yaml snap/
|
||||
PWD=$(shell pwd)
|
||||
snapcraft clean statping -s pull
|
||||
snapcraft clean statping
|
||||
docker run --rm -v ${PWD}/build/statping-linux-amd64.tar.gz:/build/statping-linux.tar.gz -w /build --env VERSION=${VERSION} snapcore/snapcraft bash -c "apt update && snapcraft --target-arch=amd64"
|
||||
snapcraft clean statping -s pull
|
||||
snapcraft clean statping
|
||||
docker run --rm -v ${PWD}/build/statping-linux-386.tar.gz:/build/statping-linux.tar.gz -w /build --env VERSION=${VERSION} snapcore/snapcraft bash -c "apt update && snapcraft --target-arch=i386"
|
||||
snapcraft clean statping -s pull
|
||||
snapcraft clean statping
|
||||
docker run --rm -v ${PWD}/build/statping-linux-arm64.tar.gz:/build/statping-linux.tar.gz -w /build --env VERSION=${VERSION} snapcore/snapcraft bash -c "apt update && snapcraft --target-arch=arm64"
|
||||
snapcraft clean statping -s pull
|
||||
docker run --rm -v ${PWD}/build/statping-linux-arm.tar.gz:/build/statping-linux.tar.gz -w /build --env VERSION=${VERSION} snapcore/snapcraft bash -c "apt update && snapcraft --target-arch=armhf"
|
||||
snapcraft clean statping
|
||||
docker run --rm -v ${PWD}/build/statping-linux-arm.tar.gz:/build/statping-linux.tar.gz -w /build --env VERSION=${VERSION} snapcore/snapcraft bash -c "apt update && snapcraft --target-arch=arm"
|
||||
snapcraft push statping_${VERSION}_amd64.snap --release stable
|
||||
snapcraft push statping_${VERSION}_arm64.snap --release stable
|
||||
snapcraft push statping_${VERSION}_i386.snap --release stable
|
||||
snapcraft push statping_${VERSION}_armhf.snap --release stable
|
||||
snapcraft push statping_${VERSION}_arm.snap --release stable
|
||||
|
||||
.PHONY: all build build-all build-alpine test-all test test-api docker frontend up down print_details lite sentry-release snapcraft build-bin build-win build-all
|
||||
postman: clean compile
|
||||
API_SECRET=demosecret123 statping --port=8080 > /dev/null &
|
||||
sleep 3
|
||||
newman run -e dev/postman_environment.json dev/postman.json
|
||||
killall statping
|
||||
|
||||
.PHONY: all build build-all build-alpine test-all test test-api docker frontend up down print_details lite sentry-release snapcraft build-linux build-mac build-win build-all postman
|
||||
.SILENT: travis_s3_creds
|
||||
|
|
|
@ -9,13 +9,15 @@
|
|||
# Statping - Status Page & Monitoring Server
|
||||
An easy to use Status Page for your websites and applications. Statping 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://godoc.org/github.com/statping/statping) [](https://slack.statping.com) [](https://microbadger.com/images/statping/statping) [](https://hub.docker.com/r/statping/statping/builds/)
|
||||
[](https://github.com/statping/statping/actions) [](https://godoc.org/github.com/statping/statping) [](https://slack.statping.com) [](https://microbadger.com/images/statping/statping) [](https://hub.docker.com/r/statping/statping/builds/)
|
||||
|
||||
<br><br>
|
||||
<img align="left" width="320" height="235" src="https://img.cjx.io/statupsiterun.gif">
|
||||
<h2>A Future-Proof Status Page</h2>
|
||||
Statping strives to remain future-proof and remain intact if a failure is created. Your Statping 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.
|
||||
<br><br><br><br><br>
|
||||
|
||||
<br><a href="https://labs.play-with-docker.com/?stack=https://raw.githubusercontent.com/statping/statping/master/dev/pwd-stack.yml"><img height=25 src="https://assets.statping.com/docker-pwd.png"></a> (dashboard login is `admin`, password `admin`)
|
||||
<br><br><br>
|
||||
|
||||
<h2>No Requirements</h2>
|
||||
Statping is built in Go Language so all you need is the precompile binary based on your operating system. You won't need to install anything extra once you have the Statping binary installed. You can even run Statping on a Raspberry Pi.
|
||||
|
|
|
@ -1,30 +0,0 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/statping/statping/source"
|
||||
"github.com/statping/statping/utils"
|
||||
"net"
|
||||
)
|
||||
|
||||
// UsingAssets will return true if /assets folder is present
|
||||
func UsingAssets() bool {
|
||||
return source.UsingAssets(utils.Directory)
|
||||
}
|
||||
|
||||
// GetLocalIP returns the non loopback local IP of the host
|
||||
func GetLocalIP() string {
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
return "http://localhost"
|
||||
}
|
||||
for _, address := range addrs {
|
||||
// check the address type and if it is not a loopback the display it
|
||||
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
if ipnet.IP.To4() != nil {
|
||||
return fmt.Sprintf("http://%v", ipnet.IP.String())
|
||||
}
|
||||
}
|
||||
}
|
||||
return "http://localhost"
|
||||
}
|
350
cmd/cli.go
350
cmd/cli.go
|
@ -4,13 +4,15 @@ import (
|
|||
"bufio"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/statping/statping/handlers"
|
||||
"github.com/statping/statping/source"
|
||||
"github.com/statping/statping/types/checkins"
|
||||
"github.com/statping/statping/types/configs"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/groups"
|
||||
"github.com/statping/statping/types/messages"
|
||||
"github.com/statping/statping/types/services"
|
||||
"github.com/statping/statping/types/users"
|
||||
"github.com/statping/statping/utils"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
@ -18,108 +20,144 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
// catchCLI will run functions based on the commands sent to Statping
|
||||
func catchCLI(args []string) error {
|
||||
func assetsCli() error {
|
||||
dir := utils.Directory
|
||||
runLogs := utils.InitLogs
|
||||
runAssets := source.Assets
|
||||
if err := utils.InitLogs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := source.Assets(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := source.CreateAllAssets(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
case "version":
|
||||
if COMMIT != "" {
|
||||
fmt.Printf("%s (%s)\n", VERSION, COMMIT)
|
||||
} else {
|
||||
fmt.Printf("%s\n", VERSION)
|
||||
func exportCli(args []string) error {
|
||||
filename := fmt.Sprintf("%s/statping-%s.json", utils.Directory, time.Now().Format("01-02-2006-1504"))
|
||||
if len(args) == 1 {
|
||||
filename = fmt.Sprintf("%s/%s", utils.Directory, args)
|
||||
}
|
||||
return errors.New("end")
|
||||
case "assets":
|
||||
var err error
|
||||
if err = runLogs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = runAssets(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = source.CreateAllAssets(dir); err != nil {
|
||||
return err
|
||||
}
|
||||
return errors.New("end")
|
||||
case "sass":
|
||||
if err := runLogs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runAssets(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := source.CompileSASS(source.DefaultScss...); err != nil {
|
||||
return err
|
||||
}
|
||||
return errors.New("end")
|
||||
case "update":
|
||||
updateDisplay()
|
||||
return errors.New("end")
|
||||
case "static":
|
||||
//var err error
|
||||
//if err = runLogs(); err != nil {
|
||||
// return err
|
||||
//}
|
||||
//if err = runAssets(); err != nil {
|
||||
// return err
|
||||
//}
|
||||
//fmt.Printf("Statping v%v Exporting Static 'index.html' page...\n", VERSION)
|
||||
//if _, err = core.LoadConfigFile(dir); err != nil {
|
||||
// log.Errorln("config.yml file not found")
|
||||
// return err
|
||||
//}
|
||||
//indexSource := ExportIndexHTML()
|
||||
////core.CloseDB()
|
||||
//if err = utils.SaveFile(dir+"/index.html", indexSource); err != nil {
|
||||
// log.Errorln(err)
|
||||
// return err
|
||||
//}
|
||||
//log.Infoln("Exported Statping index page: 'index.html'")
|
||||
case "help":
|
||||
HelpEcho()
|
||||
return errors.New("end")
|
||||
case "export":
|
||||
var err error
|
||||
var data []byte
|
||||
if err = runLogs(); err != nil {
|
||||
if err := utils.InitLogs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err = runAssets(); err != nil {
|
||||
if err := source.Assets(); err != nil {
|
||||
return err
|
||||
}
|
||||
config, err := configs.LoadConfigs()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = configs.ConnectConfigs(config); err != nil {
|
||||
if err = configs.ConnectConfigs(config, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := services.SelectAllServices(false); err != nil {
|
||||
return err
|
||||
}
|
||||
if data, err = handlers.ExportSettings(); err != nil {
|
||||
if data, err = ExportSettings(); err != nil {
|
||||
return fmt.Errorf("could not export settings: %v", err.Error())
|
||||
}
|
||||
filename := fmt.Sprintf("%s/statping-%s.json", dir, time.Now().Format("01-02-2006-1504"))
|
||||
if err = utils.SaveFile(filename, data); err != nil {
|
||||
return fmt.Errorf("could not write file statping-export.json: %v", err.Error())
|
||||
}
|
||||
log.Infoln("Statping export file saved to ", filename)
|
||||
return errors.New("end")
|
||||
case "import":
|
||||
return nil
|
||||
}
|
||||
|
||||
func sassCli() error {
|
||||
if err := utils.InitLogs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := source.Assets(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := source.CompileSASS(source.DefaultScss...); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resetCli() error {
|
||||
d := utils.Directory
|
||||
fmt.Println("Statping directory: ", d)
|
||||
assets := d + "/assets"
|
||||
if utils.FolderExists(assets) {
|
||||
fmt.Printf("Deleting %s folder.\n", assets)
|
||||
if err := utils.DeleteDirectory(assets); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Assets folder does not exist %s\n", assets)
|
||||
}
|
||||
|
||||
logDir := d + "/logs"
|
||||
if utils.FolderExists(logDir) {
|
||||
fmt.Printf("Deleting %s directory.\n", logDir)
|
||||
if err := utils.DeleteDirectory(logDir); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Logs folder does not exist %s\n", logDir)
|
||||
}
|
||||
|
||||
c := d + "/config.yml"
|
||||
if utils.FileExists(c) {
|
||||
fmt.Printf("Deleting %s file.\n", c)
|
||||
if err := utils.DeleteFile(c); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Config file does not exist %s\n", c)
|
||||
}
|
||||
|
||||
dbFile := d + "/statping.db"
|
||||
if utils.FileExists(dbFile) {
|
||||
fmt.Printf("Backuping up %s file.\n", dbFile)
|
||||
if err := utils.RenameDirectory(dbFile, d+"/statping.db.backup"); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Statping SQL Database file does not exist %s\n", dbFile)
|
||||
}
|
||||
|
||||
fmt.Println("Statping has been reset")
|
||||
return nil
|
||||
}
|
||||
|
||||
func envCli() error {
|
||||
fmt.Println("Statping Configuration")
|
||||
for k, v := range utils.Params.AllSettings() {
|
||||
fmt.Printf("%s=%v\n", strings.ToUpper(k), v)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func onceCli() error {
|
||||
if err := utils.InitLogs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := source.Assets(); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infoln("Running 1 time and saving to database...")
|
||||
if err := runOnce(); err != nil {
|
||||
return err
|
||||
}
|
||||
//core.CloseDB()
|
||||
fmt.Println("Check is complete.")
|
||||
return nil
|
||||
}
|
||||
|
||||
func importCli(args []string) error {
|
||||
var err error
|
||||
var data []byte
|
||||
if len(args) != 2 {
|
||||
return fmt.Errorf("did not include a JSON file to import\nstatping import filename.json")
|
||||
}
|
||||
filename := args[1]
|
||||
if data, err = ioutil.ReadFile(filename); err != nil {
|
||||
return err
|
||||
}
|
||||
var exportData handlers.ExportData
|
||||
var exportData ExportData
|
||||
if err = json.Unmarshal(data, &exportData); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -134,10 +172,10 @@ func catchCLI(args []string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = configs.ConnectConfigs(config); err != nil {
|
||||
if err = configs.ConnectConfigs(config, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if data, err = handlers.ExportSettings(); err != nil {
|
||||
if data, err = ExportSettings(); err != nil {
|
||||
return fmt.Errorf("could not export settings: %v", err.Error())
|
||||
}
|
||||
|
||||
|
@ -188,40 +226,8 @@ func catchCLI(args []string) error {
|
|||
}
|
||||
}
|
||||
log.Infof("Import complete")
|
||||
return errors.New("end")
|
||||
case "run":
|
||||
if err := runLogs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runAssets(); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Infoln("Running 1 time and saving to database...")
|
||||
runOnce()
|
||||
//core.CloseDB()
|
||||
fmt.Println("Check is complete.")
|
||||
return errors.New("end")
|
||||
case "env":
|
||||
fmt.Println("Statping Environment Variable")
|
||||
if err := runLogs(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runAssets(); err != nil {
|
||||
return err
|
||||
}
|
||||
envs, err := godotenv.Read(".env")
|
||||
if err != nil {
|
||||
log.Errorln("No .env file found in current directory.")
|
||||
return err
|
||||
}
|
||||
for k, e := range envs {
|
||||
fmt.Printf("%v=%v\n", k, e)
|
||||
}
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
return errors.New("end")
|
||||
}
|
||||
|
||||
func ask(format string) bool {
|
||||
fmt.Printf(fmt.Sprintf(format + " [y/N]: "))
|
||||
|
@ -231,21 +237,6 @@ func ask(format string) bool {
|
|||
return strings.ToLower(text) == "y"
|
||||
}
|
||||
|
||||
// ExportIndexHTML returns the HTML of the index page as a string
|
||||
//func ExportIndexHTML() []byte {
|
||||
// source.Assets()
|
||||
// core.CoreApp.Connect(core.CoreApp., utils.Directory)
|
||||
// core.SelectAllServices(false)
|
||||
// core.CoreApp.UseCdn = types.NewNullBool(true)
|
||||
// for _, srv := range core.Services() {
|
||||
// core.CheckService(srv, true)
|
||||
// }
|
||||
// w := httptest.NewRecorder()
|
||||
// r := httptest.NewRequest("GET", "/", nil)
|
||||
// handlers.ExecuteResponse(w, r, "index.gohtml", nil, nil)
|
||||
// return w.Body.Bytes()
|
||||
//}
|
||||
|
||||
func updateDisplay() error {
|
||||
gitCurrent, err := checkGithubUpdates()
|
||||
if err != nil {
|
||||
|
@ -271,7 +262,7 @@ func runOnce() error {
|
|||
if err != nil {
|
||||
return errors.Wrap(err, "config.yml file not found")
|
||||
}
|
||||
err = configs.ConnectConfigs(config)
|
||||
err = configs.ConnectConfigs(config, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "issue connecting to database")
|
||||
}
|
||||
|
@ -292,62 +283,6 @@ func runOnce() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// HelpEcho prints out available commands and flags for Statping
|
||||
func HelpEcho() {
|
||||
fmt.Printf("Statping v%v - Statping.com\n", VERSION)
|
||||
fmt.Printf("A simple Application Status Monitor that is opensource and lightweight.\n")
|
||||
fmt.Printf("Commands:\n")
|
||||
fmt.Println(" statping - Main command to run Statping server")
|
||||
fmt.Println(" statping version - Returns the current version of Statping")
|
||||
fmt.Println(" statping run - Check all services 1 time and then quit")
|
||||
fmt.Println(" statping assets - Dump all assets used locally to be edited.")
|
||||
//fmt.Println(" statping static - Creates a static HTML file of the index page")
|
||||
fmt.Println(" statping sass - Compile .scss files into the css directory")
|
||||
fmt.Println(" statping env - Show all environment variables being used for Statping")
|
||||
fmt.Println(" statping update - Attempts to update to the latest version")
|
||||
fmt.Println(" statping export - Exports your Statping settings to a 'statping-export.json' file.")
|
||||
fmt.Println(" statping import <file> - Imports settings from a previously saved JSON file.")
|
||||
fmt.Println(" statping help - Shows the user basic information about Statping")
|
||||
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)")
|
||||
fmt.Println(" VERBOSE - Display more logs in verbose mode. (1 - 4)")
|
||||
fmt.Println(" STATPING_DIR - Set a absolute path for the root path of Statping server (logs, assets, SQL db)")
|
||||
fmt.Println(" DB_CONN - Automatic Database connection (sqlite, postgres, mysql)")
|
||||
fmt.Println(" DB_HOST - Database hostname or IP address")
|
||||
fmt.Println(" DB_USER - Database username")
|
||||
fmt.Println(" DB_PASS - Database password")
|
||||
fmt.Println(" DB_PORT - Database port (5432, 3306, ...)")
|
||||
fmt.Println(" DB_DATABASE - Database connection's database name")
|
||||
fmt.Println(" POSTGRES_SSLMODE - Enable Postgres SSL Mode 'ssl_mode=VALUE' (enable/disable/verify-full/verify-ca)")
|
||||
fmt.Println(" DISABLE_LOGS - Disable viewing and writing to the log file (default is false)")
|
||||
fmt.Println(" GO_ENV - Run Statping in testmode, will bypass HTTP authentication (if set as 'test')")
|
||||
fmt.Println(" NAME - Set a name for the Statping status page")
|
||||
fmt.Println(" DESCRIPTION - Set a description for the Statping status page")
|
||||
fmt.Println(" DOMAIN - Set a URL for the Statping status page")
|
||||
fmt.Println(" ADMIN_USER - Username for administrator account (default: admin)")
|
||||
fmt.Println(" ADMIN_PASS - Password for administrator account (default: admin)")
|
||||
fmt.Println(" SASS - Set the absolute path to the sass binary location")
|
||||
fmt.Println(" USE_ASSETS - Automatically use assets from 'assets folder' (true/false)")
|
||||
fmt.Println(" HTTP_PROXY - Use a HTTP Proxy for HTTP Requests")
|
||||
fmt.Println(" AUTH_USERNAME - HTTP Basic Authentication username")
|
||||
fmt.Println(" AUTH_PASSWORD - HTTP Basic Authentication password")
|
||||
fmt.Println(" BASE_PATH - Set the base URL prefix (set to 'monitor' if URL is domain.com/monitor)")
|
||||
fmt.Println(" PREFIX - A Prefix for each value in Prometheus /metric exporter")
|
||||
fmt.Println(" API_KEY - Set a custom API Key for Statping")
|
||||
fmt.Println(" API_SECRET - Set a custom API Secret for API Authentication")
|
||||
fmt.Println(" MAX_OPEN_CONN - Set Maximum Open Connections for database server (default: 5)")
|
||||
fmt.Println(" MAX_IDLE_CONN - Set Maximum Idle Connections for database server")
|
||||
fmt.Println(" MAX_LIFE_CONN - Set Maximum Life Connections for database server")
|
||||
fmt.Println(" * You can insert environment variables into a '.env' file in root directory.")
|
||||
fmt.Println("Give Statping a Star at https://github.com/statping/statping")
|
||||
}
|
||||
|
||||
func checkGithubUpdates() (githubResponse, error) {
|
||||
url := "https://api.github.com/repos/statping/statping/releases/latest"
|
||||
contents, _, err := utils.HttpRequest(url, "GET", nil, nil, nil, time.Duration(2*time.Second), true)
|
||||
|
@ -437,3 +372,56 @@ type gitUploader struct {
|
|||
Type string `json:"type"`
|
||||
SiteAdmin bool `json:"site_admin"`
|
||||
}
|
||||
|
||||
// ExportChartsJs renders the charts for the index page
|
||||
|
||||
type ExportData struct {
|
||||
Core *core.Core `json:"core"`
|
||||
Services []services.Service `json:"services"`
|
||||
Messages []*messages.Message `json:"messages"`
|
||||
Checkins []*checkins.Checkin `json:"checkins"`
|
||||
Users []*users.User `json:"users"`
|
||||
Groups []*groups.Group `json:"groups"`
|
||||
Notifiers []core.AllNotifiers `json:"notifiers"`
|
||||
}
|
||||
|
||||
// ExportSettings will export a JSON file containing all of the settings below:
|
||||
// - Core
|
||||
// - Notifiers
|
||||
// - Checkins
|
||||
// - Users
|
||||
// - Services
|
||||
// - Groups
|
||||
// - Messages
|
||||
func ExportSettings() ([]byte, error) {
|
||||
c, err := core.Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := ExportData{
|
||||
Core: c,
|
||||
//Notifiers: notifications.All(),
|
||||
Checkins: checkins.All(),
|
||||
Users: users.All(),
|
||||
Services: services.AllInOrder(),
|
||||
Groups: groups.All(),
|
||||
Messages: messages.All(),
|
||||
}
|
||||
export, err := json.Marshal(data)
|
||||
return export, err
|
||||
}
|
||||
|
||||
// ExportIndexHTML returns the HTML of the index page as a string
|
||||
//func ExportIndexHTML() []byte {
|
||||
// source.Assets()
|
||||
// core.CoreApp.Connect(core.CoreApp., utils.Directory)
|
||||
// core.SelectAllServices(false)
|
||||
// core.CoreApp.UseCdn = types.NewNullBool(true)
|
||||
// for _, srv := range core.Services() {
|
||||
// core.CheckService(srv, true)
|
||||
// }
|
||||
// w := httptest.NewRecorder()
|
||||
// r := httptest.NewRequest("GET", "/", nil)
|
||||
// handlers.ExecuteResponse(w, r, "index.gohtml", nil, nil)
|
||||
// return w.Body.Bytes()
|
||||
//}
|
||||
|
|
238
cmd/cli_test.go
238
cmd/cli_test.go
|
@ -1,14 +1,12 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/rendon/testcli"
|
||||
"bytes"
|
||||
"github.com/statping/statping/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"os"
|
||||
"os/exec"
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -16,169 +14,95 @@ var (
|
|||
)
|
||||
|
||||
func init() {
|
||||
dir = utils.Directory
|
||||
//core.SampleHits = 480
|
||||
utils.InitCLI()
|
||||
}
|
||||
|
||||
func TestStartServerCommand(t *testing.T) {
|
||||
t.SkipNow()
|
||||
os.Setenv("DB_CONN", "sqlite")
|
||||
cmd := helperCommand(nil, "")
|
||||
var got = make(chan string)
|
||||
commandAndSleep(cmd, time.Duration(60*time.Second), got)
|
||||
os.Unsetenv("DB_CONN")
|
||||
gg, _ := <-got
|
||||
assert.Contains(t, gg, "DB_CONN environment variable was found")
|
||||
assert.Contains(t, gg, "Core database does not exist, creating now!")
|
||||
assert.Contains(t, gg, "Starting monitoring process for 5 Services")
|
||||
}
|
||||
func TestStatpingDirectory(t *testing.T) {
|
||||
dir := utils.Directory
|
||||
require.NotContains(t, dir, "/cmd")
|
||||
require.NotEmpty(t, dir)
|
||||
|
||||
func TestVersionCommand(t *testing.T) {
|
||||
c := testcli.Command("statping", "version")
|
||||
c.Run()
|
||||
assert.True(t, c.StdoutContains(VERSION))
|
||||
}
|
||||
|
||||
func TestHelpCommand(t *testing.T) {
|
||||
c := testcli.Command("statping", "help")
|
||||
c.Run()
|
||||
t.Log(c.Stdout())
|
||||
assert.True(t, c.StdoutContains("statping help - Shows the user basic information about Statping"))
|
||||
}
|
||||
|
||||
func TestStaticCommand(t *testing.T) {
|
||||
t.SkipNow()
|
||||
cmd := helperCommand(nil, "static")
|
||||
var got = make(chan string)
|
||||
commandAndSleep(cmd, time.Duration(10*time.Second), got)
|
||||
gg, _ := <-got
|
||||
t.Log(gg)
|
||||
assert.Contains(t, gg, "Exporting Static 'index.html' page...")
|
||||
assert.Contains(t, gg, "Exported Statping index page: 'index.html'")
|
||||
assert.FileExists(t, dir+"/index.html")
|
||||
}
|
||||
|
||||
func TestExportCommand(t *testing.T) {
|
||||
t.SkipNow()
|
||||
cmd := helperCommand(nil, "export")
|
||||
var got = make(chan string)
|
||||
commandAndSleep(cmd, time.Duration(10*time.Second), got)
|
||||
gg, _ := <-got
|
||||
t.Log(gg)
|
||||
assert.FileExists(t, dir+"/statping-export.json")
|
||||
}
|
||||
|
||||
func TestUpdateCommand(t *testing.T) {
|
||||
t.SkipNow()
|
||||
cmd := helperCommand(nil, "version")
|
||||
var got = make(chan string)
|
||||
commandAndSleep(cmd, time.Duration(15*time.Second), got)
|
||||
gg, _ := <-got
|
||||
t.Log(gg)
|
||||
assert.Contains(t, gg, VERSION)
|
||||
}
|
||||
|
||||
func TestAssetsCommand(t *testing.T) {
|
||||
t.SkipNow()
|
||||
c := testcli.Command("statping", "assets")
|
||||
c.Run()
|
||||
t.Log(c.Stdout())
|
||||
t.Log("Directory for Assets: ", dir)
|
||||
time.Sleep(1 * time.Second)
|
||||
err := utils.DeleteDirectory(dir + "/assets")
|
||||
require.Nil(t, err)
|
||||
assert.FileExists(t, dir+"/assets/robots.txt")
|
||||
assert.FileExists(t, dir+"/assets/scss/base.scss")
|
||||
assert.FileExists(t, dir+"/assets/scss/main.scss")
|
||||
assert.FileExists(t, dir+"/assets/scss/variables.scss")
|
||||
assert.FileExists(t, dir+"/assets/css/main.css")
|
||||
assert.FileExists(t, dir+"/assets/css/vendor.css")
|
||||
assert.FileExists(t, dir+"/assets/css/style.css")
|
||||
err = utils.DeleteDirectory(dir + "/assets")
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestRunCommand(t *testing.T) {
|
||||
t.SkipNow()
|
||||
cmd := helperCommand(nil, "run")
|
||||
var got = make(chan string)
|
||||
commandAndSleep(cmd, time.Duration(15*time.Second), got)
|
||||
gg, _ := <-got
|
||||
t.Log(gg)
|
||||
assert.Contains(t, gg, "Running 1 time and saving to database...")
|
||||
assert.Contains(t, gg, "Check is complete.")
|
||||
}
|
||||
|
||||
func TestEnvironmentVarsCommand(t *testing.T) {
|
||||
c := testcli.Command("statping", "env")
|
||||
c.Run()
|
||||
assert.True(t, c.StdoutContains("Statping Environment Variable"))
|
||||
}
|
||||
|
||||
func TestVersionCLI(t *testing.T) {
|
||||
run := catchCLI([]string{"version"})
|
||||
assert.EqualError(t, run, "end")
|
||||
}
|
||||
|
||||
func TestAssetsCLI(t *testing.T) {
|
||||
catchCLI([]string{"assets"})
|
||||
//assert.EqualError(t, run, "end")
|
||||
assert.FileExists(t, dir+"/assets/css/main.css")
|
||||
assert.FileExists(t, dir+"/assets/css/style.css")
|
||||
assert.FileExists(t, dir+"/assets/css/vendor.css")
|
||||
assert.FileExists(t, dir+"/assets/scss/base.scss")
|
||||
assert.FileExists(t, dir+"/assets/scss/mobile.scss")
|
||||
assert.FileExists(t, dir+"/assets/scss/variables.scss")
|
||||
}
|
||||
|
||||
func TestSassCLI(t *testing.T) {
|
||||
t.SkipNow()
|
||||
catchCLI([]string{"sass"})
|
||||
assert.FileExists(t, dir+"/assets/css/main.css")
|
||||
assert.FileExists(t, dir+"/assets/css/style.css")
|
||||
assert.FileExists(t, dir+"/assets/css/vendor.css")
|
||||
}
|
||||
|
||||
func TestUpdateCLI(t *testing.T) {
|
||||
t.SkipNow()
|
||||
cmd := helperCommand(nil, "update")
|
||||
var got = make(chan string)
|
||||
commandAndSleep(cmd, time.Duration(15*time.Second), got)
|
||||
gg, _ := <-got
|
||||
t.Log(gg)
|
||||
assert.Contains(t, gg, "version")
|
||||
}
|
||||
|
||||
func TestHelpCLI(t *testing.T) {
|
||||
run := catchCLI([]string{"help"})
|
||||
assert.EqualError(t, run, "end")
|
||||
}
|
||||
|
||||
func TestRunOnceCLI(t *testing.T) {
|
||||
t.SkipNow()
|
||||
run := catchCLI([]string{"run"})
|
||||
assert.EqualError(t, run, "end")
|
||||
dir = utils.Params.GetString("STATPING_DIR")
|
||||
require.NotContains(t, dir, "/cmd")
|
||||
require.NotEmpty(t, dir)
|
||||
}
|
||||
|
||||
func TestEnvCLI(t *testing.T) {
|
||||
run := catchCLI([]string{"env"})
|
||||
assert.Error(t, run)
|
||||
cmd := rootCmd
|
||||
b := bytes.NewBufferString("")
|
||||
cmd.SetOut(b)
|
||||
cmd.SetArgs([]string{"env"})
|
||||
err := cmd.Execute()
|
||||
require.Nil(t, err)
|
||||
out, err := ioutil.ReadAll(b)
|
||||
require.Nil(t, err)
|
||||
assert.Contains(t, string(out), VERSION)
|
||||
assert.Contains(t, utils.Directory, string(out))
|
||||
assert.Contains(t, "SAMPLE_DATA=true", string(out))
|
||||
}
|
||||
|
||||
func commandAndSleep(cmd *exec.Cmd, duration time.Duration, out chan<- string) {
|
||||
go func(out chan<- string) {
|
||||
runCommand(cmd, out)
|
||||
}(out)
|
||||
time.Sleep(duration)
|
||||
cmd.Process.Kill()
|
||||
func TestVersionCLI(t *testing.T) {
|
||||
cmd := rootCmd
|
||||
b := bytes.NewBufferString("")
|
||||
cmd.SetOut(b)
|
||||
cmd.SetArgs([]string{"version"})
|
||||
err := cmd.Execute()
|
||||
require.Nil(t, err)
|
||||
out, err := ioutil.ReadAll(b)
|
||||
require.Nil(t, err)
|
||||
assert.Contains(t, VERSION, string(out))
|
||||
}
|
||||
|
||||
func helperCommand(envs []string, s ...string) *exec.Cmd {
|
||||
cmd := exec.Command("statping", s...)
|
||||
return cmd
|
||||
func TestAssetsCLI(t *testing.T) {
|
||||
cmd := rootCmd
|
||||
b := bytes.NewBufferString("")
|
||||
cmd.SetOut(b)
|
||||
cmd.SetArgs([]string{"assets"})
|
||||
err := cmd.Execute()
|
||||
require.Nil(t, err)
|
||||
out, err := ioutil.ReadAll(b)
|
||||
assert.Nil(t, err)
|
||||
assert.Contains(t, string(out), VERSION)
|
||||
assert.FileExists(t, utils.Directory+"/assets/css/main.css")
|
||||
assert.FileExists(t, utils.Directory+"/assets/css/style.css")
|
||||
assert.FileExists(t, utils.Directory+"/assets/css/vendor.css")
|
||||
assert.FileExists(t, utils.Directory+"/assets/scss/base.scss")
|
||||
assert.FileExists(t, utils.Directory+"/assets/scss/mobile.scss")
|
||||
assert.FileExists(t, utils.Directory+"/assets/scss/variables.scss")
|
||||
}
|
||||
|
||||
func runCommand(c *exec.Cmd, out chan<- string) {
|
||||
bout, _ := c.CombinedOutput()
|
||||
out <- string(bout)
|
||||
func TestHelpCLI(t *testing.T) {
|
||||
cmd := rootCmd
|
||||
b := bytes.NewBufferString("")
|
||||
cmd.SetOut(b)
|
||||
cmd.SetArgs([]string{"help"})
|
||||
err := cmd.Execute()
|
||||
require.Nil(t, err)
|
||||
out, err := ioutil.ReadAll(b)
|
||||
require.Nil(t, err)
|
||||
assert.Contains(t, string(out), VERSION)
|
||||
}
|
||||
|
||||
func TestResetCLI(t *testing.T) {
|
||||
err := utils.SaveFile(utils.Directory+"/statping.db", []byte("test data"))
|
||||
require.Nil(t, err)
|
||||
|
||||
cmd := rootCmd
|
||||
b := bytes.NewBufferString("")
|
||||
cmd.SetOut(b)
|
||||
cmd.SetArgs([]string{"reset"})
|
||||
err = cmd.Execute()
|
||||
require.Nil(t, err)
|
||||
out, err := ioutil.ReadAll(b)
|
||||
require.Nil(t, err)
|
||||
assert.Contains(t, string(out), VERSION)
|
||||
|
||||
assert.NoDirExists(t, utils.Directory+"/assets")
|
||||
assert.NoDirExists(t, utils.Directory+"/logs")
|
||||
assert.NoFileExists(t, utils.Directory+"/config.yml")
|
||||
assert.NoFileExists(t, utils.Directory+"/statping.db")
|
||||
assert.FileExists(t, utils.Directory+"/statping.db.backup")
|
||||
|
||||
err = utils.DeleteFile(utils.Directory + "/statping.db.backup")
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "statping",
|
||||
Short: "A simple Application Status Monitor that is opensource and lightweight.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
start()
|
||||
},
|
||||
}
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Short: "Print the version number of Statping",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if COMMIT != "" {
|
||||
fmt.Printf("%s (%s)\n", VERSION, COMMIT)
|
||||
} else {
|
||||
fmt.Printf("%s\n", VERSION)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var assetsCmd = &cobra.Command{
|
||||
Use: "assets",
|
||||
Short: "Dump all assets used locally to be edited",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return assetsCli()
|
||||
},
|
||||
}
|
||||
|
||||
var exportCmd = &cobra.Command{
|
||||
Use: "export",
|
||||
Short: "Exports your Statping settings to a 'statping-export.json' file.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return exportCli(args)
|
||||
},
|
||||
}
|
||||
|
||||
var sassCmd = &cobra.Command{
|
||||
Use: "sass",
|
||||
Short: "Compile .scss files into the css directory",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return sassCli()
|
||||
},
|
||||
}
|
||||
|
||||
var envCmd = &cobra.Command{
|
||||
Use: "env",
|
||||
Short: "Return the configs that will be ran",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return envCli()
|
||||
},
|
||||
}
|
||||
|
||||
var resetCmd = &cobra.Command{
|
||||
Use: "reset",
|
||||
Short: "Start a fresh copy of Statping",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return resetCli()
|
||||
},
|
||||
}
|
||||
|
||||
var onceCmd = &cobra.Command{
|
||||
Use: "once",
|
||||
Short: "Check all services 1 time and then quit",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return onceCli()
|
||||
},
|
||||
}
|
||||
|
||||
var importCmd = &cobra.Command{
|
||||
Use: "import [.json file]",
|
||||
Short: "Imports settings from a previously saved JSON file.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return importCli(args)
|
||||
},
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return errors.New("requires input file (.json)")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func Execute() {
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
exit(err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
func parseFlags(cmd *cobra.Command) {
|
||||
cmd.PersistentFlags().StringVarP(&ipAddress, "ip", "s", "0.0.0.0", "server port")
|
||||
viper.BindPFlag("ip", cmd.PersistentFlags().Lookup("ip"))
|
||||
|
||||
cmd.PersistentFlags().IntVarP(&port, "port", "p", 8080, "server port")
|
||||
viper.BindPFlag("port", cmd.PersistentFlags().Lookup("port"))
|
||||
|
||||
cmd.PersistentFlags().IntVarP(&verboseMode, "verbose", "v", 2, "server port")
|
||||
viper.BindPFlag("verbose", cmd.PersistentFlags().Lookup("verbose"))
|
||||
}
|
65
cmd/main.go
65
cmd/main.go
|
@ -1,7 +1,6 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/statping/statping/database"
|
||||
|
@ -31,26 +30,18 @@ var (
|
|||
confgs *configs.DbConfig
|
||||
)
|
||||
|
||||
// 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
|
||||
// environment variables WILL overwrite flags
|
||||
func parseFlags() {
|
||||
envPort := utils.Getenv("PORT", 8080).(int)
|
||||
envIpAddress := utils.Getenv("IP", "0.0.0.0").(string)
|
||||
envVerbose := utils.Getenv("VERBOSE", 2).(int)
|
||||
//envGrpcPort := utils.Getenv("GRPC_PORT", 0).(int)
|
||||
|
||||
flag.StringVar(&ipAddress, "ip", envIpAddress, "IP address to run the Statping HTTP server")
|
||||
flag.StringVar(&envFile, "env", "", "IP address to run the Statping HTTP server")
|
||||
flag.IntVar(&port, "port", envPort, "Port to run the HTTP server")
|
||||
//flag.IntVar(&grpcPort, "grpc", envGrpcPort, "Port to run the gRPC server")
|
||||
flag.IntVar(&verboseMode, "verbose", envVerbose, "Run in verbose mode to see detailed logs (1 - 4)")
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func init() {
|
||||
core.New(VERSION)
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
rootCmd.AddCommand(assetsCmd)
|
||||
rootCmd.AddCommand(exportCmd)
|
||||
rootCmd.AddCommand(importCmd)
|
||||
rootCmd.AddCommand(sassCmd)
|
||||
rootCmd.AddCommand(onceCmd)
|
||||
rootCmd.AddCommand(envCmd)
|
||||
rootCmd.AddCommand(resetCmd)
|
||||
utils.InitCLI()
|
||||
parseFlags(rootCmd)
|
||||
}
|
||||
|
||||
// exit will return an error and return an exit code 1 due to this error
|
||||
|
@ -69,11 +60,14 @@ func Close() {
|
|||
|
||||
// main will run the Statping application
|
||||
func main() {
|
||||
Execute()
|
||||
}
|
||||
|
||||
// main will run the Statping application
|
||||
func start() {
|
||||
var err error
|
||||
go sigterm()
|
||||
|
||||
parseFlags()
|
||||
|
||||
if err := source.Assets(); err != nil {
|
||||
exit(err)
|
||||
}
|
||||
|
@ -84,32 +78,21 @@ func main() {
|
|||
log.Errorf("Statping Log Error: %v\n", err)
|
||||
}
|
||||
|
||||
args := flag.Args()
|
||||
|
||||
if len(args) >= 1 {
|
||||
err := catchCLI(args)
|
||||
if err != nil {
|
||||
if err.Error() == "end" {
|
||||
os.Exit(0)
|
||||
return
|
||||
}
|
||||
exit(err)
|
||||
}
|
||||
}
|
||||
log.Info(fmt.Sprintf("Starting Statping v%s", VERSION))
|
||||
|
||||
if err := updateDisplay(); err != nil {
|
||||
log.Warnln(err)
|
||||
}
|
||||
//if err := updateDisplay(); err != nil {
|
||||
// log.Warnln(err)
|
||||
//}
|
||||
|
||||
confgs, err = configs.LoadConfigs()
|
||||
if err != nil {
|
||||
log.Infoln("Starting in Setup Mode")
|
||||
if err := SetupMode(); err != nil {
|
||||
exit(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err = configs.ConnectConfigs(confgs); err != nil {
|
||||
if err = configs.ConnectConfigs(confgs, true); err != nil {
|
||||
exit(err)
|
||||
}
|
||||
|
||||
|
@ -135,9 +118,11 @@ func main() {
|
|||
exit(errors.Wrap(err, "error creating default admin user"))
|
||||
}
|
||||
|
||||
if utils.Params.GetBool("SAMPLE_DATA") {
|
||||
if err := configs.TriggerSamples(); err != nil {
|
||||
exit(errors.Wrap(err, "error creating database"))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -149,12 +134,6 @@ func main() {
|
|||
exit(err)
|
||||
}
|
||||
|
||||
//log.Infoln("Migrating Notifiers...")
|
||||
//if err := notifier.Migrate(); err != nil {
|
||||
// exit(errors.Wrap(err, "error migrating notifiers"))
|
||||
//}
|
||||
//log.Infoln("Notifiers Migrated")
|
||||
|
||||
if err := mainProcess(); err != nil {
|
||||
exit(err)
|
||||
}
|
||||
|
|
|
@ -92,6 +92,7 @@ type Database interface {
|
|||
|
||||
// extra
|
||||
Error() error
|
||||
Status() int
|
||||
RowsAffected() int64
|
||||
|
||||
Since(time.Time) Database
|
||||
|
@ -156,7 +157,10 @@ type Db struct {
|
|||
// Openw is a drop-in replacement for Open()
|
||||
func Openw(dialect string, args ...interface{}) (db Database, err error) {
|
||||
gorm.NowFunc = func() time.Time {
|
||||
return time.Now().UTC()
|
||||
return utils.Now()
|
||||
}
|
||||
if dialect == "sqlite" {
|
||||
dialect = "sqlite3"
|
||||
}
|
||||
gormdb, err := gorm.Open(dialect, args...)
|
||||
if err != nil {
|
||||
|
@ -167,22 +171,36 @@ func Openw(dialect string, args ...interface{}) (db Database, err error) {
|
|||
}
|
||||
|
||||
func OpenTester() (Database, error) {
|
||||
testDB := utils.Getenv("TEST_DB", "sqlite3").(string)
|
||||
var dbParamsstring string
|
||||
testDB := utils.Params.GetString("DB_CONN")
|
||||
var dbString string
|
||||
|
||||
switch testDB {
|
||||
case "mysql":
|
||||
dbParamsstring = fmt.Sprintf("root:password123@tcp(localhost:3306)/statping?charset=utf8&parseTime=True&loc=UTC&time_zone=%%27UTC%%27")
|
||||
dbString = fmt.Sprintf("%s:%s@tcp(%s:%v)/%s?charset=utf8&parseTime=True&loc=UTC&time_zone=%%27UTC%%27",
|
||||
utils.Params.GetString("DB_HOST"),
|
||||
utils.Params.GetString("DB_PASS"),
|
||||
utils.Params.GetString("DB_HOST"),
|
||||
utils.Params.GetInt("DB_PORT"),
|
||||
utils.Params.GetString("DB_DATABASE"),
|
||||
)
|
||||
case "postgres":
|
||||
dbParamsstring = fmt.Sprintf("host=localhost port=5432 user=root dbname=statping password=password123 timezone=UTC")
|
||||
dbString = fmt.Sprintf("host=%s port=%v user=%s dbname=%s password=%s sslmode=disable timezone=UTC",
|
||||
utils.Params.GetString("DB_HOST"),
|
||||
utils.Params.GetInt("DB_PORT"),
|
||||
utils.Params.GetString("DB_USER"),
|
||||
utils.Params.GetString("DB_DATABASE"),
|
||||
utils.Params.GetString("DB_PASS"))
|
||||
default:
|
||||
dbParamsstring = fmt.Sprintf("file:%s?mode=memory&cache=shared", utils.RandomString(12))
|
||||
dbString = fmt.Sprintf("file:%s?mode=memory&cache=shared", utils.RandomString(12))
|
||||
}
|
||||
fmt.Println(testDB, dbParamsstring)
|
||||
newDb, err := Openw(testDB, dbParamsstring)
|
||||
newDb, err := Openw(testDB, dbString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newDb.DB().SetMaxOpenConns(1)
|
||||
if testDB != "sqlite3" {
|
||||
newDb.DB().SetMaxOpenConns(25)
|
||||
}
|
||||
return newDb, err
|
||||
}
|
||||
|
||||
|
@ -490,6 +508,34 @@ func (it *Db) Error() error {
|
|||
return it.Database.Error
|
||||
}
|
||||
|
||||
func (it *Db) Status() int {
|
||||
switch it.Database.Error {
|
||||
case gorm.ErrRecordNotFound:
|
||||
return 404
|
||||
case gorm.ErrCantStartTransaction:
|
||||
return 422
|
||||
case gorm.ErrInvalidSQL:
|
||||
return 500
|
||||
case gorm.ErrUnaddressable:
|
||||
return 500
|
||||
default:
|
||||
return 500
|
||||
}
|
||||
}
|
||||
|
||||
func (it *Db) Loggable() bool {
|
||||
switch it.Database.Error {
|
||||
case gorm.ErrCantStartTransaction:
|
||||
return true
|
||||
case gorm.ErrInvalidSQL:
|
||||
return true
|
||||
case gorm.ErrUnaddressable:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (it *Db) Since(ago time.Time) Database {
|
||||
return it.Where("created_at > ?", it.FormatTime(ago))
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ func (g *GroupQuery) GraphData(by By) ([]*TimeValue, error) {
|
|||
dbQuery := g.db.MultipleSelects(
|
||||
g.db.SelectByTime(g.Group),
|
||||
by.String(),
|
||||
).Group("timeframe")
|
||||
).Group("timeframe").Order("timeframe", true)
|
||||
|
||||
g.db = dbQuery
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@ var (
|
|||
// Maintenance will automatically delete old records from 'failures' and 'hits'
|
||||
// this function is currently set to delete records 7+ days old every 60 minutes
|
||||
func Maintenance() {
|
||||
dur := utils.GetenvAs("REMOVE_AFTER", "2160h").Duration()
|
||||
interval := utils.GetenvAs("CLEANUP_INTERVAL", "1h").Duration()
|
||||
dur := utils.Params.GetDuration("REMOVE_AFTER")
|
||||
interval := utils.Params.GetDuration("CLEANUP_INTERVAL")
|
||||
|
||||
log.Infof("Database Cleanup runs every %s and will remove records older than %s", interval.String(), dur.String())
|
||||
ticker := interval
|
||||
|
|
|
@ -36,7 +36,7 @@ func (it *Db) SelectByTime(increment time.Duration) string {
|
|||
case "mysql":
|
||||
return fmt.Sprintf("FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(created_at) / %d) * %d) AS timeframe", seconds, seconds)
|
||||
case "postgres":
|
||||
return fmt.Sprintf("date_trunc('%s', created_at) AS timeframe", increment)
|
||||
return fmt.Sprintf("date_trunc('minute', created_at) - (CAST(EXTRACT(MINUTE FROM created_at) AS integer) %% %d) * interval '1 minute' AS timeframe", seconds)
|
||||
default:
|
||||
return fmt.Sprintf("datetime((strftime('%%s', created_at) / %d) * %d, 'unixepoch') as timeframe", seconds, seconds)
|
||||
}
|
||||
|
|
|
@ -5,14 +5,12 @@ services:
|
|||
postgres:
|
||||
container_name: postgres
|
||||
image: postgres
|
||||
volumes:
|
||||
- ../docker/databases/postgres:/var/lib/postgresql/data
|
||||
environment:
|
||||
POSTGRES_PASSWORD: password123
|
||||
POSTGRES_DB: statping
|
||||
POSTGRES_USER: root
|
||||
ports:
|
||||
- 5432:5432
|
||||
- "127.0.0.1:5432:5432"
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U root"]
|
||||
interval: 15s
|
||||
|
@ -22,8 +20,6 @@ services:
|
|||
mysql:
|
||||
container_name: mysql
|
||||
image: mysql:5.7
|
||||
volumes:
|
||||
- ../docker/databases/mysql:/var/lib/mysql
|
||||
restart: always
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: password123
|
||||
|
@ -31,7 +27,7 @@ services:
|
|||
MYSQL_USER: root
|
||||
MYSQL_PASSWORD: password123
|
||||
ports:
|
||||
- 3306:3306
|
||||
- "127.0.0.1:3306:3306"
|
||||
healthcheck:
|
||||
test: [ "CMD", "mysqladmin" ,"ping", "-h", "localhost" ]
|
||||
timeout: 20s
|
||||
|
@ -46,7 +42,7 @@ services:
|
|||
mysql:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- 5050:80
|
||||
- "127.0.0.1:5050:80"
|
||||
links:
|
||||
- mysql:db
|
||||
environment:
|
||||
|
@ -62,7 +58,7 @@ services:
|
|||
restart: on-failure
|
||||
command: sqlite_web -H 0.0.0.0 -r -x /data/statping.db
|
||||
ports:
|
||||
- 6050:8080
|
||||
- "127.0.0.1:6050:8080"
|
||||
volumes:
|
||||
- ../docker/statping/sqlite/statping.db:/data/statping.db:ro
|
||||
environment:
|
||||
|
@ -75,11 +71,8 @@ services:
|
|||
environment:
|
||||
DEFAULT_USER: admin@admin.com
|
||||
DEFAULT_PASSWORD: admin
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
ports:
|
||||
- 7000:5050
|
||||
- "127.0.0.1:7000:5050"
|
||||
links:
|
||||
- postgres:postgres
|
||||
|
||||
|
@ -91,7 +84,7 @@ services:
|
|||
- ./prometheus.yml:/etc/prometheus/prometheus.yml
|
||||
- ../docker/databases/prometheus:/prometheus
|
||||
ports:
|
||||
- 7050:9090
|
||||
- "127.0.0.1:7050:9090"
|
||||
healthcheck:
|
||||
test: "/bin/wget -q -Y off http://localhost:9090/status -O /dev/null > /dev/null 2>&1"
|
||||
interval: 10s
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -7,6 +7,12 @@
|
|||
"value": "http://127.0.0.1:8080",
|
||||
"description": "",
|
||||
"enabled": true
|
||||
},
|
||||
{
|
||||
"key": "api_key",
|
||||
"value": "demosecret123",
|
||||
"description": "",
|
||||
"enabled": true
|
||||
}
|
||||
],
|
||||
"_postman_variable_scope": "environment",
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
version: '3.1'
|
||||
|
||||
services:
|
||||
|
||||
statping:
|
||||
hostname: statping
|
||||
image: statping/statping
|
||||
ports:
|
||||
- 8080:8080
|
||||
environment:
|
||||
PORT: 8080
|
||||
SERVICES: '[{"name": "Local Statping", "type": "http", "domain": "http://localhost:8585", "interval": 30}]'
|
||||
DB_CONN: sqlite
|
||||
API_KEY: exampleapikey
|
||||
API_SECRET: exampleapisecret
|
||||
NAME: Statping on SQLite
|
||||
DOMAIN: http://localhost:8080
|
||||
DESCRIPTION: This is a dev environment on SQLite!
|
||||
ADMIN_USER: admin
|
||||
ADMIN_PASS: admin
|
|
@ -11,7 +11,7 @@
|
|||
"cypress:open": "cypress open",
|
||||
"cypress:test": "cypress run --browser chrome --record false --key $CYPRESS_KEY",
|
||||
"test": "start-server-and-test start http://localhost:8080/api cypress:test",
|
||||
"start": "statping -port 8080"
|
||||
"start": "statping --port 8080 > /dev/null"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free-solid": "^5.1.0-3",
|
||||
|
|
|
@ -12,6 +12,11 @@ class Api {
|
|||
|
||||
}
|
||||
|
||||
async oauth() {
|
||||
const oauth = axios.get('api/oauth').then(response => (response.data))
|
||||
return oauth
|
||||
}
|
||||
|
||||
async core() {
|
||||
const core = axios.get('api').then(response => (response.data))
|
||||
if (core.allow_reports) {
|
||||
|
@ -198,7 +203,7 @@ class Api {
|
|||
}
|
||||
|
||||
async logs() {
|
||||
return axios.get('api/logs').then(response => (response.data))
|
||||
return axios.get('api/logs').then(response => (response.data)) || []
|
||||
}
|
||||
|
||||
async logs_last() {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<button v-html="loading ? loadLabel : label" @click.prevent="runAction" type="submit" :disabled="loading || disabled" class="btn btn-block" :class="{class: !loading, 'btn-outline-light': loading}">
|
||||
<button v-html="loading ? loadLabel : label" @click.prevent="runAction" type="submit" :disabled="loading || disabled" class="btn btn-block" :class="{'btn-outline-light': loading}">
|
||||
</button>
|
||||
</template>
|
||||
|
||||
|
@ -15,10 +15,6 @@
|
|||
type: String,
|
||||
required: true
|
||||
},
|
||||
class: {
|
||||
type: String,
|
||||
default: "btn-primary"
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../../API";
|
||||
import MiniSparkLine from './MiniSparkLine';
|
||||
import ServiceSparkLine from './ServiceSparkLine';
|
||||
|
||||
|
@ -42,21 +41,6 @@
|
|||
this.chart = this.convertToChartData(this.func.chart);
|
||||
}
|
||||
},
|
||||
async latencyYesterday() {
|
||||
const todayTime = await Api.service_hits(this.service.id, this.toUnix(this.nowSubtract(86400)), this.toUnix(new Date()), this.group, false)
|
||||
const fetched = await Api.service_hits(this.service.id, this.start, this.end, this.group, false)
|
||||
|
||||
let todayAmount = this.addAmounts(todayTime)
|
||||
let yesterday = this.addAmounts(fetched)
|
||||
|
||||
},
|
||||
addAmounts(data) {
|
||||
let total = 0
|
||||
data.forEach((f) => {
|
||||
total += parseInt(f.amount)
|
||||
});
|
||||
return total
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -104,7 +104,6 @@
|
|||
import ServiceFailures from './ServiceFailures';
|
||||
import ServiceSparkLine from "./ServiceSparkLine";
|
||||
import Api from "../../API";
|
||||
import StatsGen from "./StatsGen";
|
||||
|
||||
export default {
|
||||
name: 'ServiceInfo',
|
||||
|
@ -113,7 +112,6 @@
|
|||
ServiceFailures,
|
||||
FormIncident,
|
||||
FormMessage,
|
||||
StatsGen,
|
||||
ServiceSparkLine
|
||||
},
|
||||
props: {
|
||||
|
|
|
@ -1,75 +0,0 @@
|
|||
<template>
|
||||
<div class="col-3 text-left">
|
||||
<span class="text-success font-5 font-weight-bold">{{value}}</span>
|
||||
<span class="font-2 d-block">{{title}}</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../../API";
|
||||
|
||||
export default {
|
||||
name: 'StatsGen',
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
start: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
end: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
group: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
expression: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
in_value: {
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
value: "+17%"
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
if (this.in_value) {
|
||||
this.value = this.in_value
|
||||
}
|
||||
await this.latencyYesterday();
|
||||
},
|
||||
methods: {
|
||||
async latencyYesterday() {
|
||||
const todayTime = await Api.service_hits(this.service.id, this.toUnix(this.nowSubtract(86400)), this.toUnix(new Date()), this.group, false)
|
||||
const fetched = await Api.service_hits(this.service.id, this.start, this.end, this.group, false)
|
||||
|
||||
let todayAmount = this.addAmounts(todayTime)
|
||||
let yesterday = this.addAmounts(fetched)
|
||||
|
||||
},
|
||||
addAmounts(data) {
|
||||
let total = 0
|
||||
data.forEach((f) => {
|
||||
total += parseInt(f.amount)
|
||||
});
|
||||
return total
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -1,4 +1,5 @@
|
|||
<template>
|
||||
<div>
|
||||
<form @submit.prevent="login" autocomplete="on">
|
||||
<div class="form-group row">
|
||||
<label for="username" class="col-sm-2 col-form-label">Username</label>
|
||||
|
@ -22,8 +23,21 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
<a v-if="oauth.gh_client_id" :href="GHlogin()" class="btn btn-block">
|
||||
Github Login
|
||||
</a>
|
||||
|
||||
<a v-if="oauth.slack_client_id" :href="Slacklogin()" class="btn btn-block">
|
||||
Slack Login
|
||||
</a>
|
||||
|
||||
<a v-if="oauth.google_client_id" :href="Googlelogin()" class="btn btn-block">
|
||||
Google Login
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -36,7 +50,7 @@
|
|||
return this.$store.getters.core
|
||||
},
|
||||
oauth() {
|
||||
return this.$store.getters.core.oauth
|
||||
return this.$store.getters.oauth
|
||||
}
|
||||
},
|
||||
data() {
|
||||
|
@ -51,9 +65,6 @@
|
|||
slack_scope: "identity.email,identity.basic"
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.GHlogin()
|
||||
},
|
||||
methods: {
|
||||
checkForm() {
|
||||
if (!this.username || !this.password) {
|
||||
|
@ -76,9 +87,14 @@
|
|||
}
|
||||
this.loading = false
|
||||
},
|
||||
async GHlogin() {
|
||||
const core = this.$store.getters.core;
|
||||
this.ghLoginURL = `https://github.com/login/oauth/authorize?client_id=${core.gh_client_id}&redirect_uri=${core.domain}/oauth/callback&scope=user,repo`
|
||||
GHlogin() {
|
||||
return `https://github.com/login/oauth/authorize?client_id=${this.oauth.gh_client_id}&redirect_uri=${this.core.domain}/api/oauth/github&scope=user,repo`
|
||||
},
|
||||
Slacklogin() {
|
||||
return `https://slack.com/oauth/authorize?client_id=${this.oauth.slack_client_id}&redirect_uri=${this.core.domain}/api/oauth/slack&scope=users.profile:read,users:read.email`
|
||||
},
|
||||
Googlelogin() {
|
||||
return `https://accounts.google.com/signin/oauth?client_id=${this.oauth.google_client_id}&redirect_uri=${this.core.domain}/api/oauth/google&response_type=code&scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
<template>
|
||||
<form @submit.prevent="saveOAuth">
|
||||
{{core.oauth}}
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-header">Internal Login</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label for="switch-gh-oauth" class="col-sm-4 col-form-label">OAuth Login Settings</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
<span @click="oauth.internal_enabled = !!core.oauth.internal_enabled" class="switch float-left">
|
||||
<input v-model="oauth.internal_enabled" type="checkbox" class="switch" id="switch-local-oauth" :checked="oauth.internal_enabled">
|
||||
<span @click="local_enabled = !!local_enabled" class="switch float-left">
|
||||
<input v-model="local_enabled" type="checkbox" class="switch" id="switch-local-oauth" :checked="local_enabled">
|
||||
<label for="switch-local-oauth">Use email/password Authentication</label>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -15,7 +16,7 @@
|
|||
<div class="form-group row">
|
||||
<label for="whitelist_domains" class="col-sm-4 col-form-label">Whitelist Domains</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.oauth.oauth_domains" type="text" class="form-control" placeholder="domain.com" id="whitelist_domains">
|
||||
<input v-model="oauth.oauth_domains" type="text" class="form-control" placeholder="domain.com" id="whitelist_domains">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -28,20 +29,20 @@
|
|||
<div class="form-group row mt-3">
|
||||
<label for="github_client" class="col-sm-4 col-form-label">Github Client ID</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.oauth.gh_client_id" type="text" class="form-control" id="github_client" required>
|
||||
<input v-model="oauth.gh_client_id" type="text" class="form-control" id="github_client" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="github_secret" class="col-sm-4 col-form-label">Github Client Secret</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.oauth.gh_client_secret" type="text" class="form-control" id="github_secret" required>
|
||||
<input v-model="oauth.gh_client_secret" type="text" class="form-control" id="github_secret" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="switch-gh-oauth" class="col-sm-4 col-form-label">Enable Github Login</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
<span @click="oauth.github_enabled = !!oauth.github_enabled" class="switch float-left">
|
||||
<input v-model="oauth.github_enabled" type="checkbox" class="switch" id="switch-gh-oauth" :checked="oauth.github_enabled">
|
||||
<span @click="github_enabled = !!github_enabled" class="switch float-left">
|
||||
<input v-model="github_enabled" type="checkbox" class="switch" id="switch-gh-oauth" :checked="github_enabled">
|
||||
<label for="switch-gh-oauth"> </label>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -50,7 +51,7 @@
|
|||
<label for="gh_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<input v-bind:value="`${core.domain}/oauth/github`" type="text" class="form-control" id="gh_callback" readonly>
|
||||
<input v-bind:value="`${core.domain}/api/oauth/github`" type="text" class="form-control" id="gh_callback" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(`${core.domain}/oauth/github`)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
|
@ -89,7 +90,7 @@
|
|||
<label for="google_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<input v-bind:value="`${core.domain}/oauth/google`" type="text" class="form-control" id="google_callback" readonly>
|
||||
<input v-bind:value="`${core.domain}/api/oauth/google`" type="text" class="form-control" id="google_callback" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(`${core.domain}/oauth/google`)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
|
@ -126,7 +127,7 @@
|
|||
<label for="switch-slack-oauth" class="col-sm-4 col-form-label">Enable Slack Login</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
<span @click="slack_enabled = !!slack_enabled" class="switch float-left">
|
||||
<input v-model="slack_enabled" type="checkbox" class="switch" id="switch-slack-oauth" :checked="google_enabled">
|
||||
<input v-model="slack_enabled" type="checkbox" class="switch" id="switch-slack-oauth" :checked="slack_enabled">
|
||||
<label for="switch-slack-oauth"> </label>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -135,7 +136,7 @@
|
|||
<label for="slack_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<input v-bind:value="`${core.domain}/oauth/slack`" type="text" class="form-control" id="slack_callback" readonly>
|
||||
<input v-bind:value="`${core.domain}/api/oauth/slack`" type="text" class="form-control" id="slack_callback" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(`${core.domain}/oauth/slack`)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
|
@ -158,39 +159,34 @@
|
|||
export default {
|
||||
name: 'OAuth',
|
||||
computed: {
|
||||
core() {
|
||||
return this.$store.getters.core
|
||||
},
|
||||
oauth() {
|
||||
return this.$store.getters.core.oauth
|
||||
return this.$store.getters.oauth
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
internal_enabled: this.has('local'),
|
||||
google_enabled: this.has('google'),
|
||||
github_enabled: this.has('github'),
|
||||
slack_enabled: this.has('slack')
|
||||
google_enabled: false,
|
||||
slack_enabled: false,
|
||||
github_enabled: false,
|
||||
local_enabled: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
window.console.log(this.core.oauth)
|
||||
},
|
||||
beforeCreate() {
|
||||
// this.github_enabled = this.$store.getters.core.oauth.oauth_providers.split(",").includes('github')
|
||||
// const c = await Api.core()
|
||||
// this.auth = c.auth
|
||||
this.local_enabled = this.has('local')
|
||||
this.github_enabled = this.has('github')
|
||||
this.google_enabled = this.has('google')
|
||||
this.slack_enabled = this.has('slack')
|
||||
},
|
||||
methods: {
|
||||
has(val) {
|
||||
if (!this.core.oauth.oauth_providers) {
|
||||
return false
|
||||
}
|
||||
return this.core.oauth.oauth_providers.split(",").includes(val)
|
||||
},
|
||||
providers() {
|
||||
let providers = [];
|
||||
if (this.github_enabled) {
|
||||
providers.push("github")
|
||||
}
|
||||
if (this.internal_enabled) {
|
||||
if (this.local_enabled) {
|
||||
providers.push("local")
|
||||
}
|
||||
if (this.google_enabled) {
|
||||
|
@ -200,14 +196,21 @@
|
|||
providers.push("slack")
|
||||
}
|
||||
return providers.join(",")
|
||||
},
|
||||
has(val) {
|
||||
if (!this.oauth.oauth_providers) {
|
||||
return false
|
||||
}
|
||||
return this.oauth.oauth_providers.split(",").includes(val)
|
||||
},
|
||||
async saveOAuth() {
|
||||
let c = this.$store.getters.core
|
||||
let c = this.core
|
||||
c.oauth = this.oauth
|
||||
c.oauth.oauth_providers = this.providers()
|
||||
await Api.core_save(c)
|
||||
const core = await Api.core()
|
||||
this.$store.commit('setCore', core)
|
||||
this.$store.commit('setOAuth', c.oauth)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -135,6 +135,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="service.type.match(/^(http)$/)" class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Follow HTTP Redirects</label>
|
||||
<div class="col-8 mt-1">
|
||||
<span @click="service.redirect = !!service.redirect" class="switch float-left">
|
||||
<input v-model="service.redirect" type="checkbox" name="redirect-option" class="switch" id="switch-redirect" v-bind:checked="service.redirect">
|
||||
<label for="switch-redirect">Follow HTTP Redirects if server attempts</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="service.type.match(/^(http)$/)" class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Verify SSL</label>
|
||||
<div class="col-8 mt-1">
|
||||
|
@ -220,6 +230,7 @@
|
|||
permalink: "",
|
||||
order: 1,
|
||||
verify_ssl: true,
|
||||
redirect: true,
|
||||
allow_notifications: true,
|
||||
notify_all_changes: true,
|
||||
notify_after: 2,
|
||||
|
|
|
@ -20,6 +20,7 @@ export default new Vuex.Store({
|
|||
hasAllData: false,
|
||||
hasPublicData: false,
|
||||
core: {},
|
||||
oauth: {},
|
||||
token: null,
|
||||
services: [],
|
||||
service: null,
|
||||
|
@ -35,6 +36,7 @@ export default new Vuex.Store({
|
|||
hasAllData: state => state.hasAllData,
|
||||
hasPublicData: state => state.hasPublicData,
|
||||
core: state => state.core,
|
||||
oauth: state => state.oauth,
|
||||
token: state => state.token,
|
||||
services: state => state.services,
|
||||
service: state => state.service,
|
||||
|
@ -133,6 +135,9 @@ export default new Vuex.Store({
|
|||
setUser (state, user) {
|
||||
state.user = user
|
||||
},
|
||||
setOAuth (state, oauth) {
|
||||
state.oauth = oauth
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async getAllServices(context) {
|
||||
|
@ -153,8 +158,9 @@ export default new Vuex.Store({
|
|||
context.commit("setServices", services);
|
||||
const messages = await Api.messages()
|
||||
context.commit("setMessages", messages)
|
||||
const oauth = await Api.oauth()
|
||||
context.commit("setOAuth", oauth);
|
||||
context.commit("setHasPublicData", true)
|
||||
window.console.log('finished loading required data')
|
||||
},
|
||||
async loadAdmin(context) {
|
||||
const groups = await Api.groups()
|
||||
|
@ -170,6 +176,8 @@ export default new Vuex.Store({
|
|||
context.commit("setNotifiers", notifiers);
|
||||
const users = await Api.users()
|
||||
context.commit("setUsers", users);
|
||||
const oauth = await Api.oauth()
|
||||
context.commit("setOAuth", oauth);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
15
go.mod
15
go.mod
|
@ -8,6 +8,7 @@ require (
|
|||
github.com/ararog/timeago v0.0.0-20160328174124-e9969cf18b8d
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/fsnotify/fsnotify v1.4.9 // indirect
|
||||
github.com/getsentry/sentry-go v0.5.1
|
||||
github.com/go-mail/mail v2.3.1+incompatible
|
||||
github.com/gogo/protobuf v1.3.1 // indirect
|
||||
|
@ -17,19 +18,31 @@ require (
|
|||
github.com/jinzhu/gorm v1.9.12
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/kataras/iris/v12 v12.0.1
|
||||
github.com/mattn/go-sqlite3 v2.0.1+incompatible
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||
github.com/mitchellh/mapstructure v1.2.2 // indirect
|
||||
github.com/pelletier/go-toml v1.7.0 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/common v0.9.1
|
||||
github.com/rendon/testcli v0.0.0-20161027181003-6283090d169f
|
||||
github.com/russross/blackfriday/v2 v2.0.1
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect
|
||||
github.com/spf13/afero v1.2.2 // indirect
|
||||
github.com/spf13/cast v1.3.1 // indirect
|
||||
github.com/spf13/cobra v1.0.0
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.6.3
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
|
||||
golang.org/x/crypto v0.0.0-20200320181102-891825fb96df
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be
|
||||
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
golang.org/x/tools v0.0.0-20200321014904-268ba720d32c // indirect
|
||||
google.golang.org/grpc v1.28.0
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/ini.v1 v1.55.0 // indirect
|
||||
gopkg.in/mail.v2 v2.3.1 // indirect
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
|
|
92
go.sum
92
go.sum
|
@ -11,6 +11,7 @@ github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voi
|
|||
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
|
||||
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
|
||||
github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 h1:WDC6ySpJzbxGWFh4aMxFFC28wwGp5pEuoTtvA4q/qQ4=
|
||||
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
|
||||
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
|
||||
|
@ -29,13 +30,19 @@ github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go
|
|||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY=
|
||||
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
@ -47,6 +54,7 @@ github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6ps
|
|||
github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM=
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
|
@ -59,10 +67,14 @@ github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod
|
|||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
|
||||
github.com/getsentry/sentry-go v0.5.1 h1:MIPe7ScHADsrK2vznqmhksIUFxq7m0JfTh+ZIMkI+VQ=
|
||||
github.com/getsentry/sentry-go v0.5.1/go.mod h1:B8H7x8TYDPkeWPRzGpIiFO97LZP6rL8A3hEt8lUItMw=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
|
||||
github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
|
||||
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
|
||||
|
@ -83,11 +95,13 @@ github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm
|
|||
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
|
||||
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls=
|
||||
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
@ -97,10 +111,12 @@ github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaW
|
|||
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
|
@ -109,10 +125,15 @@ github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+
|
|||
github.com/gorilla/sessions v1.2.0 h1:S7P+1Hm5V/AT9cjEcUD5uDaQSX0OE577aCXgoaKpYbQ=
|
||||
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/iris-contrib/blackfriday v2.0.0+incompatible h1:o5sHQHHm0ToHUlAJSTjW9UWicjJSDDauOOQ2AHuIVp4=
|
||||
github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
|
||||
|
@ -131,8 +152,10 @@ github.com/jinzhu/now v1.0.1 h1:HjfetcXq097iXP0uoPCdnM4Efp5/9MsM0/M+XOTeR3M=
|
|||
github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
|
||||
github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
|
||||
|
@ -146,6 +169,7 @@ github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/l
|
|||
github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
|
||||
github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d h1:V5Rs9ztEWdp58oayPq/ulmlqJJZeJP6pP79uP3qjcao=
|
||||
github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
|
@ -163,22 +187,32 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL
|
|||
github.com/lib/pq v1.1.1 h1:sJZmqHoEaY7f+NPP8pgLB/WxulyR3fewgCM2qaSlBb4=
|
||||
github.com/lib/pq v1.1.1/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
|
||||
github.com/mattn/go-sqlite3 v2.0.1+incompatible h1:xQ15muvnzGBHpIpdrNi1DA5x0+TcBZzsIDwmw9uTHzw=
|
||||
github.com/mattn/go-sqlite3 v2.0.1+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
|
||||
github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
|
||||
github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s=
|
||||
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.2.2 h1:dxe5oCinTXiTIcfgmZecdCzPmAJKd46KsCWc35r0TV4=
|
||||
github.com/mitchellh/mapstructure v1.2.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
|
||||
|
@ -188,10 +222,14 @@ github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7
|
|||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 h1:E2B8qYyeSgv5MXpmzZXRNp8IAQ4vjxIjhpAf5hv/tAg=
|
||||
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.7.0 h1:7utD74fnzVc/cpcyy8sjrlFr5vYpypUixARcHIMIGuI=
|
||||
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
@ -201,18 +239,24 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.9.1 h1:KOMtN28tlbam3/7ZKEYKHhKoJZYYj3gMH4uc62x7X7U=
|
||||
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rendon/testcli v0.0.0-20161027181003-6283090d169f h1:onGP+qmYmjKs7pkmi9j0mwyr97/D5wki80e74aKIOxg=
|
||||
github.com/rendon/testcli v0.0.0-20161027181003-6283090d169f/go.mod h1:cq57a4l475CeMvE7RRpSui1MEqCmhirIt1E7kl8BC2Q=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/russross/blackfriday v1.5.2 h1:HyvC0ARfnZBqnXwABFeSZHpKvJHJJfPz81GNueLj0oo=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
|
||||
|
@ -224,14 +268,36 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV
|
|||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA=
|
||||
github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.5 h1:f0B+LkLX6DtmRH1isoNA9VTtNUK9K8xYd28JNNfOv/s=
|
||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||
github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8=
|
||||
github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||
github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
|
||||
github.com/spf13/viper v1.6.3 h1:pDDu1OyEDTKzpJwdq4TiuLyMsUgRa/BT5cn5O62NoHs=
|
||||
github.com/spf13/viper v1.6.3/go.mod h1:jUMtyi0/lB5yZH/FjyGAoH7IMNrIhlBf6pXZmbMDvzw=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
|
@ -239,8 +305,11 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV
|
|||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e h1:nt2877sKfojlHCTOBXbpWjBkuWKritFaGIfgQwbQUls=
|
||||
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e/go.mod h1:B4+Kq1u5FlULTjFSM707Q6e/cOHFv0z/6QRoxubDIQ8=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
|
@ -255,12 +324,17 @@ github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV
|
|||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
|
||||
github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
|
||||
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
|
||||
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
|
@ -285,6 +359,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
|
@ -301,6 +376,7 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -310,8 +386,16 @@ golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4 h1:opSr2sbRXk5X5/givKrrKj9HXxFpW2sdCiP8MJSKLQY=
|
||||
golang.org/x/sys v0.0.0-20200413165638-669c56c373c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
@ -334,6 +418,7 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
|
|||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.28.0 h1:bO/TA4OxCOummhSf10siHuG7vJOiwh7SpRpFZDkOgl4=
|
||||
|
@ -344,15 +429,22 @@ gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc h1:2gG
|
|||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc/go.mod h1:m7x9LTH6d71AHyAX77c9yqWCCa3UKHcVEj9y7hAtKDk=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
||||
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.55.0 h1:E8yzL5unfpW3M6fz/eB7Cb5MQAYSZ7GKo4Qth+N2sgQ=
|
||||
gopkg.in/ini.v1 v1.55.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/mail.v2 v2.3.1 h1:WYFn/oANrAGP2C0dcV6/pbkPzv8yGzqTjPmTeO7qoXk=
|
||||
gopkg.in/mail.v2 v2.3.1/go.mod h1:htwXN1Qh09vZJ1NVKxQqHPBaCBbzKhp5GzuJEA4VJWw=
|
||||
gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
@ -1,11 +1,10 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/statping/statping/types/checkins"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/types/groups"
|
||||
"github.com/statping/statping/types/incidents"
|
||||
"github.com/statping/statping/types/messages"
|
||||
|
@ -22,7 +21,7 @@ type apiResponse struct {
|
|||
Status string `json:"status"`
|
||||
Object string `json:"type,omitempty"`
|
||||
Method string `json:"method,omitempty"`
|
||||
Error string `json:"error,omitempty"`
|
||||
Error error `json:"error,omitempty"`
|
||||
Id int64 `json:"id,omitempty"`
|
||||
Output interface{} `json:"output,omitempty"`
|
||||
}
|
||||
|
@ -52,10 +51,14 @@ func apiRenewHandler(w http.ResponseWriter, r *http.Request) {
|
|||
returnJson(output, w, r)
|
||||
}
|
||||
|
||||
func apiOAuthHandler(r *http.Request) interface{} {
|
||||
app := core.App
|
||||
return app.OAuth
|
||||
}
|
||||
|
||||
func apiCoreHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var c *core.Core
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&c)
|
||||
err := DecodeJSON(r, &c)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
|
@ -111,17 +114,18 @@ func apiClearCacheHandler(w http.ResponseWriter, r *http.Request) {
|
|||
returnJson(output, w, r)
|
||||
}
|
||||
|
||||
func sendErrorJson(err error, w http.ResponseWriter, r *http.Request, statusCode ...int) {
|
||||
func sendErrorJson(err error, w http.ResponseWriter, r *http.Request) {
|
||||
errCode := 0
|
||||
e, ok := err.(errors.Error)
|
||||
if ok {
|
||||
errCode = e.Status()
|
||||
}
|
||||
log.WithField("url", r.URL.String()).
|
||||
WithField("method", r.Method).
|
||||
WithField("code", statusCode).
|
||||
WithField("code", errCode).
|
||||
Errorln(fmt.Errorf("sending error response for %s: %s", r.URL.String(), err.Error()))
|
||||
|
||||
output := apiResponse{
|
||||
Status: "error",
|
||||
Error: err.Error(),
|
||||
}
|
||||
returnJson(output, w, r, statusCode...)
|
||||
returnJson(err, w, r)
|
||||
}
|
||||
|
||||
func sendJsonAction(obj interface{}, method string, w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -173,11 +177,6 @@ func sendJsonAction(obj interface{}, method string, w http.ResponseWriter, r *ht
|
|||
}
|
||||
|
||||
func sendUnauthorizedJson(w http.ResponseWriter, r *http.Request) {
|
||||
output := apiResponse{
|
||||
Status: "error",
|
||||
Error: errors.New("not authorized").Error(),
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
returnJson(output, w, r)
|
||||
returnJson(errors.NotAuthenticated, w, r)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/getsentry/sentry-go"
|
||||
|
@ -18,7 +19,6 @@ import (
|
|||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
@ -34,13 +34,6 @@ func init() {
|
|||
core.New("test")
|
||||
}
|
||||
|
||||
//func TestResetDatabase(t *testing.T) {
|
||||
// err := core.TmpRecords("handlers.db")
|
||||
// t.Log(err)
|
||||
// require.Nil(t, err)
|
||||
// require.NotNil(t, core.CoreApp)
|
||||
//}
|
||||
|
||||
func TestFailedHTTPServer(t *testing.T) {
|
||||
err := RunHTTPServer("missinghost", 0)
|
||||
assert.Error(t, err)
|
||||
|
@ -48,12 +41,12 @@ func TestFailedHTTPServer(t *testing.T) {
|
|||
|
||||
func TestSetupRoutes(t *testing.T) {
|
||||
form := url.Values{}
|
||||
form.Add("db_host", "")
|
||||
form.Add("db_user", "")
|
||||
form.Add("db_password", "")
|
||||
form.Add("db_database", "")
|
||||
form.Add("db_connection", "sqlite")
|
||||
form.Add("db_port", "")
|
||||
form.Add("db_host", utils.Params.GetString("DB_HOST"))
|
||||
form.Add("db_user", utils.Params.GetString("DB_USER"))
|
||||
form.Add("db_password", utils.Params.GetString("DB_PASS"))
|
||||
form.Add("db_database", utils.Params.GetString("DB_DATABASE"))
|
||||
form.Add("db_connection", utils.Params.GetString("DB_CONN"))
|
||||
form.Add("db_port", utils.Params.GetString("DB_PORT"))
|
||||
form.Add("project", "Tester")
|
||||
form.Add("username", "admin")
|
||||
form.Add("password", "password123")
|
||||
|
@ -62,6 +55,21 @@ func TestSetupRoutes(t *testing.T) {
|
|||
form.Add("domain", "http://localhost:8080")
|
||||
form.Add("email", "info@statping.com")
|
||||
|
||||
badForm := url.Values{}
|
||||
badForm.Add("db_host", "badconnection")
|
||||
badForm.Add("db_user", utils.Params.GetString("DB_USER"))
|
||||
badForm.Add("db_password", utils.Params.GetString("DB_PASS"))
|
||||
badForm.Add("db_database", utils.Params.GetString("DB_DATABASE"))
|
||||
badForm.Add("db_connection", "mysql")
|
||||
badForm.Add("db_port", utils.Params.GetString("DB_PORT"))
|
||||
badForm.Add("project", "Tester")
|
||||
badForm.Add("username", "admin")
|
||||
badForm.Add("password", "password123")
|
||||
badForm.Add("sample_data", "on")
|
||||
badForm.Add("description", "This is an awesome test")
|
||||
badForm.Add("domain", "http://localhost:8080")
|
||||
badForm.Add("email", "info@statping.com")
|
||||
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "Statping Check",
|
||||
|
@ -75,14 +83,23 @@ func TestSetupRoutes(t *testing.T) {
|
|||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Statping Error Setup",
|
||||
URL: "/api/setup",
|
||||
Method: "POST",
|
||||
Body: badForm.Encode(),
|
||||
ExpectedStatus: 500,
|
||||
ExpectedContains: []string{BadJSONDatabase},
|
||||
HttpHeaders: []string{"Content-Type=application/x-www-form-urlencoded"},
|
||||
},
|
||||
{
|
||||
Name: "Statping Run Setup",
|
||||
URL: "/api/setup",
|
||||
Method: "POST",
|
||||
Body: form.Encode(),
|
||||
ExpectedStatus: 200,
|
||||
//ExpectedStatus: 200,
|
||||
HttpHeaders: []string{"Content-Type=application/x-www-form-urlencoded"},
|
||||
ExpectedFiles: []string{dir + "/config.yml", dir + "/" + "statping.db"},
|
||||
ExpectedFiles: []string{utils.Directory + "/config.yml"},
|
||||
FuncTest: func(t *testing.T) error {
|
||||
if !core.App.Setup {
|
||||
return errors.New("core has not been setup")
|
||||
|
@ -99,7 +116,8 @@ func TestSetupRoutes(t *testing.T) {
|
|||
return nil
|
||||
},
|
||||
AfterTest: StopServices,
|
||||
}}
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
|
@ -113,6 +131,7 @@ func TestSetupRoutes(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestMainApiRoutes(t *testing.T) {
|
||||
date := utils.Now().Format("2006-01")
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "Statping Details",
|
||||
|
@ -135,6 +154,15 @@ func TestMainApiRoutes(t *testing.T) {
|
|||
BeforeTest: SetTestENV,
|
||||
SecureRoute: true,
|
||||
},
|
||||
{
|
||||
Name: "Statping View Cache",
|
||||
URL: "/api/cache",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
BeforeTest: SetTestENV,
|
||||
SecureRoute: true,
|
||||
ResponseLen: 0,
|
||||
},
|
||||
{
|
||||
Name: "Statping Clear Cache",
|
||||
URL: "/api/clear_cache",
|
||||
|
@ -172,18 +200,48 @@ func TestMainApiRoutes(t *testing.T) {
|
|||
Method: "GET",
|
||||
ExpectedStatus: 404,
|
||||
},
|
||||
//{
|
||||
// Name: "Prometheus Export Metrics",
|
||||
// URL: "/metrics",
|
||||
// Method: "GET",
|
||||
// BeforeTest: SetTestENV,
|
||||
// ExpectedStatus: 200,
|
||||
// ExpectedContains: []string{
|
||||
// `Statping Totals`,
|
||||
// `total_failures`,
|
||||
// `Golang Metrics`,
|
||||
// },
|
||||
//},
|
||||
{
|
||||
Name: "Health Check endpoint",
|
||||
URL: "/health",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{`"online":true`, `"setup":true`},
|
||||
},
|
||||
{
|
||||
Name: "Logs endpoint",
|
||||
URL: "/api/logs",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
GreaterThan: 20,
|
||||
ExpectedContains: []string{date},
|
||||
},
|
||||
{
|
||||
Name: "Logs endpoint",
|
||||
URL: "/api/logs",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
GreaterThan: 20,
|
||||
ExpectedContains: []string{date},
|
||||
},
|
||||
{
|
||||
Name: "Logs Last Line endpoint",
|
||||
URL: "/api/logs/last",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{date},
|
||||
},
|
||||
{
|
||||
Name: "Prometheus Export Metrics",
|
||||
URL: "/metrics",
|
||||
Method: "GET",
|
||||
BeforeTest: SetTestENV,
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{
|
||||
`Statping Totals`,
|
||||
`total_failures`,
|
||||
`Golang Metrics`,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
|
@ -197,6 +255,8 @@ func TestMainApiRoutes(t *testing.T) {
|
|||
|
||||
type HttpFuncTest func(*testing.T) error
|
||||
|
||||
type ResponseFunc func(*testing.T, []byte) error
|
||||
|
||||
// HTTPTest contains all the parameters for a HTTP Unit Test
|
||||
type HTTPTest struct {
|
||||
Name string
|
||||
|
@ -211,8 +271,11 @@ type HTTPTest struct {
|
|||
FuncTest HttpFuncTest
|
||||
BeforeTest HttpFuncTest
|
||||
AfterTest HttpFuncTest
|
||||
ResponseFunc ResponseFunc
|
||||
ResponseLen int
|
||||
GreaterThan int
|
||||
SecureRoute bool
|
||||
Skip bool
|
||||
}
|
||||
|
||||
func logTest(t *testing.T, err error) error {
|
||||
|
@ -228,6 +291,9 @@ func logTest(t *testing.T, err error) error {
|
|||
|
||||
// RunHTTPTest accepts a HTTPTest type to execute the HTTP request
|
||||
func RunHTTPTest(test HTTPTest, t *testing.T) (string, *testing.T, error) {
|
||||
if test.Skip {
|
||||
t.SkipNow()
|
||||
}
|
||||
if test.BeforeTest != nil {
|
||||
if err := test.BeforeTest(t); err != nil {
|
||||
return "", t, logTest(t, err)
|
||||
|
@ -240,6 +306,13 @@ func RunHTTPTest(test HTTPTest, t *testing.T) (string, *testing.T, error) {
|
|||
}
|
||||
defer rr.Result().Body.Close()
|
||||
|
||||
if test.ExpectedStatus != 0 {
|
||||
if test.ExpectedStatus != rr.Result().StatusCode {
|
||||
assert.Equal(t, test.ExpectedStatus, rr.Result().StatusCode)
|
||||
return "", t, fmt.Errorf("status code %v does not match %v", rr.Result().StatusCode, test.ExpectedStatus)
|
||||
}
|
||||
}
|
||||
|
||||
body, err := ioutil.ReadAll(rr.Result().Body)
|
||||
if err != nil {
|
||||
assert.Nil(t, err)
|
||||
|
@ -248,10 +321,6 @@ func RunHTTPTest(test HTTPTest, t *testing.T) (string, *testing.T, error) {
|
|||
|
||||
stringBody := string(body)
|
||||
|
||||
if test.ExpectedStatus != rr.Result().StatusCode {
|
||||
assert.Equal(t, test.ExpectedStatus, rr.Result().StatusCode)
|
||||
return stringBody, t, fmt.Errorf("status code %v does not match %v", rr.Result().StatusCode, test.ExpectedStatus)
|
||||
}
|
||||
if len(test.ExpectedContains) != 0 {
|
||||
for _, v := range test.ExpectedContains {
|
||||
assert.Contains(t, stringBody, v)
|
||||
|
@ -271,21 +340,24 @@ func RunHTTPTest(test HTTPTest, t *testing.T) (string, *testing.T, error) {
|
|||
err := test.FuncTest(t)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
if test.ResponseFunc != nil {
|
||||
err := test.ResponseFunc(t, body)
|
||||
assert.Nil(t, err)
|
||||
}
|
||||
|
||||
if test.ResponseLen != 0 {
|
||||
var respArray []interface{}
|
||||
err := json.Unmarshal(body, &respArray)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, test.ResponseLen, len(respArray))
|
||||
}
|
||||
//if test.SecureRoute {
|
||||
// UnsetTestENV()
|
||||
// rec, err := Request(test)
|
||||
// if err != nil {
|
||||
// return "", t, logTest(t, err)
|
||||
// }
|
||||
// defer rec.Result().Body.Close()
|
||||
// assert.Equal(t, http.StatusUnauthorized, rec.Result().StatusCode)
|
||||
//}
|
||||
|
||||
if test.GreaterThan != 0 {
|
||||
var respArray []interface{}
|
||||
err := json.Unmarshal(body, &respArray)
|
||||
assert.Nil(t, err)
|
||||
assert.GreaterOrEqual(t, len(respArray), test.GreaterThan)
|
||||
}
|
||||
|
||||
if test.AfterTest != nil {
|
||||
if err := test.AfterTest(t); err != nil {
|
||||
|
@ -312,11 +384,13 @@ func Request(test HTTPTest) (*httptest.ResponseRecorder, error) {
|
|||
}
|
||||
|
||||
func SetTestENV(t *testing.T) error {
|
||||
return os.Setenv("GO_ENV", "test")
|
||||
utils.Params.Set("GO_ENV", "test")
|
||||
return nil
|
||||
}
|
||||
|
||||
func UnsetTestENV(t *testing.T) error {
|
||||
return os.Setenv("GO_ENV", "production")
|
||||
utils.Params.Set("GO_ENV", "production")
|
||||
return nil
|
||||
}
|
||||
|
||||
func StopServices(t *testing.T) error {
|
||||
|
@ -325,3 +399,20 @@ func StopServices(t *testing.T) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func basicAuth(username, password string) string {
|
||||
auth := username + ":" + password
|
||||
return base64.StdEncoding.EncodeToString([]byte(auth))
|
||||
}
|
||||
|
||||
var (
|
||||
Success = `"status":"success"`
|
||||
|
||||
MethodCreate = `"method":"create"`
|
||||
MethodUpdate = `"method":"update"`
|
||||
MethodDelete = `"method":"delete"`
|
||||
|
||||
BadJSON = `{incorrect: JSON %%% formatting, [&]}`
|
||||
BadJSONResponse = `{"error":"could not decode incoming JSON"}`
|
||||
BadJSONDatabase = `{"error":"error connecting to database`
|
||||
)
|
||||
|
|
|
@ -1,26 +1,37 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/statping/statping/types/checkins"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/types/services"
|
||||
"github.com/statping/statping/utils"
|
||||
"net"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func findCheckin(r *http.Request) (*checkins.Checkin, string, error) {
|
||||
vars := mux.Vars(r)
|
||||
id := vars["api"]
|
||||
if id == "" {
|
||||
return nil, "", errors.IDMissing
|
||||
}
|
||||
checkin, err := checkins.FindByAPI(id)
|
||||
if err != nil {
|
||||
return nil, id, errors.Missing(checkins.Checkin{}, id)
|
||||
}
|
||||
return checkin, id, nil
|
||||
}
|
||||
|
||||
func apiAllCheckinsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
chks := checkins.All()
|
||||
returnJson(chks, w, r)
|
||||
}
|
||||
|
||||
func apiCheckinHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
checkin, err := checkins.FindByAPI(vars["api"])
|
||||
checkin, _, err := findCheckin(r)
|
||||
if err != nil {
|
||||
sendErrorJson(fmt.Errorf("checkin %v was not found", vars["api"]), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
returnJson(checkin, w, r)
|
||||
|
@ -28,15 +39,14 @@ func apiCheckinHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
func checkinCreateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var checkin *checkins.Checkin
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&checkin)
|
||||
err := DecodeJSON(r, &checkin)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
service, err := services.Find(checkin.ServiceId)
|
||||
if err != nil {
|
||||
sendErrorJson(fmt.Errorf("missing service_id field"), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
checkin.ServiceId = service.Id
|
||||
|
@ -48,10 +58,9 @@ func checkinCreateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
checkin, err := checkins.FindByAPI(vars["api"])
|
||||
checkin, _, err := findCheckin(r)
|
||||
if err != nil {
|
||||
sendErrorJson(fmt.Errorf("checkin %s was not found", vars["api"]), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||
|
@ -65,7 +74,7 @@ func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
err = hit.Create()
|
||||
if err != nil {
|
||||
sendErrorJson(fmt.Errorf("checkin %v was not found", vars["api"]), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
checkin.Failing = false
|
||||
|
@ -74,10 +83,9 @@ func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func checkinDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
checkin, err := checkins.FindByAPI(vars["api"])
|
||||
checkin, _, err := findCheckin(r)
|
||||
if err != nil {
|
||||
sendErrorJson(fmt.Errorf("checkin %v was not found", vars["api"]), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -5,30 +5,106 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestApiCheckinRoutes(t *testing.T) {
|
||||
func TestUnAuthenticatedCheckinRoutes(t *testing.T) {
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "Statping Checkins",
|
||||
URL: "/api/checkins",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
BeforeTest: SetTestENV,
|
||||
SecureRoute: true,
|
||||
}, {
|
||||
Name: "Statping Create Checkin",
|
||||
Name: "No Authentication - New Checkin",
|
||||
URL: "/api/checkins",
|
||||
Method: "POST",
|
||||
Body: `{
|
||||
"service_id": 2,
|
||||
"name": "Server Checkin",
|
||||
"interval": 900,
|
||||
"grace": 60
|
||||
}`,
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{`"status":"success","type":"checkin","method":"create"`},
|
||||
BeforeTest: SetTestENV,
|
||||
SecureRoute: true,
|
||||
}}
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Delete Checkin",
|
||||
URL: "/api/checkins/1",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
str, t, err := RunHTTPTest(v, t)
|
||||
t.Logf("Test %s: \n %v\n", v.Name, str)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApiCheckinRoutes(t *testing.T) {
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "Statping Create Checkins",
|
||||
URL: "/api/checkins",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 200,
|
||||
BeforeTest: SetTestENV,
|
||||
SecureRoute: true,
|
||||
ExpectedContains: []string{Success},
|
||||
Body: `{
|
||||
"name": "Example Checkin",
|
||||
"service_id": 1,
|
||||
"checkin_interval": 300,
|
||||
"grace_period": 60,
|
||||
"api_key": "example"
|
||||
}`,
|
||||
},
|
||||
{
|
||||
Name: "Statping Checkins",
|
||||
URL: "/api/checkins",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
ResponseLen: 3,
|
||||
BeforeTest: SetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "Statping View Checkin",
|
||||
URL: "/api/checkins/example",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
BeforeTest: SetTestENV,
|
||||
SecureRoute: true,
|
||||
},
|
||||
{
|
||||
Name: "Statping Trigger Checkin",
|
||||
URL: "/checkin/example",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
SecureRoute: true,
|
||||
BeforeTest: SetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "Statping Missing Trigger Checkin",
|
||||
URL: "/checkin/missing123",
|
||||
Method: "GET",
|
||||
BeforeTest: SetTestENV,
|
||||
ExpectedStatus: 404,
|
||||
},
|
||||
{
|
||||
Name: "Statping Missing Checkin",
|
||||
URL: "/api/checkins/missing123",
|
||||
Method: "GET",
|
||||
BeforeTest: SetTestENV,
|
||||
ExpectedStatus: 404,
|
||||
},
|
||||
{
|
||||
Name: "Statping Delete Checkin",
|
||||
URL: "/api/checkins/example",
|
||||
Method: "DELETE",
|
||||
BeforeTest: SetTestENV,
|
||||
ExpectedContains: []string{Success},
|
||||
ExpectedStatus: 200,
|
||||
},
|
||||
{
|
||||
Name: "Incorrect JSON POST",
|
||||
URL: "/api/checkins",
|
||||
Body: BadJSON,
|
||||
ExpectedContains: []string{BadJSONResponse},
|
||||
Method: "POST",
|
||||
ExpectedStatus: 422,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
|
|
|
@ -17,15 +17,6 @@ func logoutHandler(w http.ResponseWriter, r *http.Request) {
|
|||
http.Redirect(w, r, basePath, http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func helpHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !IsUser(r) {
|
||||
http.Redirect(w, r, basePath, http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
help := source.HelpMarkdown()
|
||||
ExecuteResponse(w, r, "help.gohtml", help, nil)
|
||||
}
|
||||
|
||||
func logsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
utils.LockLines.Lock()
|
||||
logs := make([]string, 0)
|
||||
|
@ -101,7 +92,7 @@ func apiThemeSaveHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiThemeCreateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
dir := utils.Directory
|
||||
dir := utils.Params.GetString("STATPING_DIR")
|
||||
utils.Log.Infof("creating assets in folder: %s/%s", dir, "assets")
|
||||
if err := source.CreateAllAssets(dir); err != nil {
|
||||
log.Errorln(err)
|
||||
|
@ -194,6 +185,7 @@ func apiLoginHandler(w http.ResponseWriter, r *http.Request) {
|
|||
form := parseForm(r)
|
||||
username := form.Get("username")
|
||||
password := form.Get("password")
|
||||
|
||||
user, auth := users.AuthUser(username, password)
|
||||
if auth {
|
||||
utils.Log.Infoln(fmt.Sprintf("User %v logged in from IP %v", user.Username, r.RemoteAddr))
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/statping/statping/types/checkins"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/groups"
|
||||
"github.com/statping/statping/types/messages"
|
||||
"github.com/statping/statping/types/services"
|
||||
"github.com/statping/statping/types/users"
|
||||
)
|
||||
|
||||
// ExportChartsJs renders the charts for the index page
|
||||
|
||||
type ExportData struct {
|
||||
Core *core.Core `json:"core"`
|
||||
Services []services.Service `json:"services"`
|
||||
Messages []*messages.Message `json:"messages"`
|
||||
Checkins []*checkins.Checkin `json:"checkins"`
|
||||
Users []*users.User `json:"users"`
|
||||
Groups []*groups.Group `json:"groups"`
|
||||
Notifiers []core.AllNotifiers `json:"notifiers"`
|
||||
}
|
||||
|
||||
// ExportSettings will export a JSON file containing all of the settings below:
|
||||
// - Core
|
||||
// - Notifiers
|
||||
// - Checkins
|
||||
// - Users
|
||||
// - Services
|
||||
// - Groups
|
||||
// - Messages
|
||||
func ExportSettings() ([]byte, error) {
|
||||
c, err := core.Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := ExportData{
|
||||
Core: c,
|
||||
//Notifiers: notifications.All(),
|
||||
Checkins: checkins.All(),
|
||||
Users: users.All(),
|
||||
Services: services.AllInOrder(),
|
||||
Groups: groups.All(),
|
||||
Messages: messages.All(),
|
||||
}
|
||||
export, err := json.Marshal(data)
|
||||
return export, err
|
||||
}
|
|
@ -2,19 +2,30 @@ package handlers
|
|||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/types/groups"
|
||||
"github.com/statping/statping/utils"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func selectGroup(r *http.Request) (*groups.Group, error) {
|
||||
func findGroup(r *http.Request) (*groups.Group, error) {
|
||||
vars := mux.Vars(r)
|
||||
if utils.NotNumber(vars["id"]) {
|
||||
return nil, errors.NotNumber
|
||||
}
|
||||
id := utils.ToInt(vars["id"])
|
||||
if id == 0 {
|
||||
return nil, errors.IDMissing
|
||||
}
|
||||
g, err := groups.Find(id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !g.Public.Bool {
|
||||
if !IsReadAuthenticated(r) {
|
||||
return nil, errors.NotAuthenticated
|
||||
}
|
||||
}
|
||||
return g, nil
|
||||
}
|
||||
|
||||
|
@ -26,9 +37,9 @@ func apiAllGroupHandler(r *http.Request) interface{} {
|
|||
|
||||
// apiGroupHandler will show a single group
|
||||
func apiGroupHandler(w http.ResponseWriter, r *http.Request) {
|
||||
group, err := selectGroup(r)
|
||||
group, err := findGroup(r)
|
||||
if err != nil {
|
||||
sendErrorJson(errors.Wrap(err, "group not found"), w, r, http.StatusNotFound)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
returnJson(group, w, r)
|
||||
|
@ -36,10 +47,9 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// apiGroupUpdateHandler will update a group
|
||||
func apiGroupUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
group, err := selectGroup(r)
|
||||
group, err := findGroup(r)
|
||||
if err != nil {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
sendErrorJson(errors.Wrap(err, "group not found"), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -74,9 +84,9 @@ func apiCreateGroupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
// apiGroupDeleteHandler accepts a DELETE method to delete groups
|
||||
func apiGroupDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
group, err := selectGroup(r)
|
||||
group, err := findGroup(r)
|
||||
if err != nil {
|
||||
sendErrorJson(errors.Wrap(err, "group not found"), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,46 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/statping/statping/types/groups"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUnAuthenticatedGroupRoutes(t *testing.T) {
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "No Authentication - New Group",
|
||||
URL: "/api/groups",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Update Group",
|
||||
URL: "/api/groups/1",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Delete Group",
|
||||
URL: "/api/groups/1",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
str, t, err := RunHTTPTest(v, t)
|
||||
t.Logf("Test %s: \n %v\n", v.Name, str)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGroupAPIRoutes(t *testing.T) {
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
|
@ -47,6 +83,14 @@ func TestGroupAPIRoutes(t *testing.T) {
|
|||
Method: "POST",
|
||||
ExpectedStatus: 200,
|
||||
},
|
||||
{
|
||||
Name: "Incorrect JSON POST",
|
||||
URL: "/api/groups",
|
||||
Body: BadJSON,
|
||||
ExpectedContains: []string{BadJSONResponse},
|
||||
Method: "POST",
|
||||
ExpectedStatus: 422,
|
||||
},
|
||||
{
|
||||
Name: "Statping Public and Private Groups",
|
||||
URL: "/api/groups",
|
||||
|
@ -86,14 +130,35 @@ func TestGroupAPIRoutes(t *testing.T) {
|
|||
BeforeTest: SetTestENV,
|
||||
ExpectedStatus: 404,
|
||||
},
|
||||
{
|
||||
Name: "Statping Update Group",
|
||||
URL: "/api/groups/1",
|
||||
Method: "POST",
|
||||
Body: `{
|
||||
"name": "Updated Group",
|
||||
"public": false
|
||||
}`,
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{Success, MethodUpdate},
|
||||
BeforeTest: SetTestENV,
|
||||
SecureRoute: true,
|
||||
AfterTest: func(t *testing.T) error {
|
||||
g, err := groups.Find(1)
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "Updated Group", g.Name)
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Statping Delete Group",
|
||||
URL: "/api/groups/1",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{Success, MethodDelete},
|
||||
AfterTest: UnsetTestENV,
|
||||
SecureRoute: true,
|
||||
}}
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
|
|
|
@ -4,13 +4,12 @@ import (
|
|||
"crypto/subtle"
|
||||
"crypto/tls"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"html/template"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -44,7 +43,7 @@ func RunHTTPServer(ip string, port int) error {
|
|||
log.Infoln(fmt.Sprintf("Statping Secure HTTPS Server running on https://%v:%v", ip, 443))
|
||||
usingSSL = true
|
||||
} else {
|
||||
log.Infoln("Statping HTTP Server running on http://" + host)
|
||||
log.Infoln("Statping HTTP Server running on http://" + host + basePath)
|
||||
}
|
||||
|
||||
router = Router()
|
||||
|
@ -110,7 +109,7 @@ func IsReadAuthenticated(r *http.Request) bool {
|
|||
// IsFullAuthenticated returns true if the HTTP request is authenticated. You can set the environment variable GO_ENV=test
|
||||
// to bypass the admin authenticate to the dashboard features.
|
||||
func IsFullAuthenticated(r *http.Request) bool {
|
||||
if os.Getenv("GO_ENV") == "test" {
|
||||
if utils.Params.Get("GO_ENV") == "test" {
|
||||
return true
|
||||
}
|
||||
if core.App == nil {
|
||||
|
@ -172,7 +171,7 @@ func IsAdmin(r *http.Request) bool {
|
|||
if !core.App.Setup {
|
||||
return false
|
||||
}
|
||||
if utils.Getenv("GO_ENV", false).(bool) {
|
||||
if utils.Params.GetString("GO_ENV") == "test" {
|
||||
return true
|
||||
}
|
||||
claim, err := getJwtToken(r)
|
||||
|
@ -187,7 +186,7 @@ func IsUser(r *http.Request) bool {
|
|||
if !core.App.Setup {
|
||||
return false
|
||||
}
|
||||
if os.Getenv("GO_ENV") == "test" {
|
||||
if utils.Params.Get("GO_ENV") == "test" {
|
||||
return true
|
||||
}
|
||||
tk, err := getJwtToken(r)
|
||||
|
@ -248,12 +247,19 @@ func ExecuteResponse(w http.ResponseWriter, r *http.Request, file string, data i
|
|||
}
|
||||
}
|
||||
|
||||
func returnJson(d interface{}, w http.ResponseWriter, r *http.Request, statusCode ...int) {
|
||||
func returnJson(d interface{}, w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
if len(statusCode) != 0 {
|
||||
code := statusCode[0]
|
||||
w.WriteHeader(code)
|
||||
if e, ok := d.(errors.Error); ok {
|
||||
w.WriteHeader(e.Status())
|
||||
json.NewEncoder(w).Encode(e)
|
||||
return
|
||||
}
|
||||
if e, ok := d.(error); ok {
|
||||
w.WriteHeader(500)
|
||||
json.NewEncoder(w).Encode(errors.New(e.Error()))
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(d)
|
||||
}
|
||||
|
||||
|
@ -263,5 +269,5 @@ func error404Handler(w http.ResponseWriter, r *http.Request) {
|
|||
w.Header().Add("Strict-Transport-Security", "max-age=63072000; includeSubDomains")
|
||||
}
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
ExecuteResponse(w, r, "index.html", nil, nil)
|
||||
ExecuteResponse(w, r, "base.gohtml", nil, nil)
|
||||
}
|
||||
|
|
|
@ -1,13 +1,29 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/types/incidents"
|
||||
"github.com/statping/statping/utils"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func findIncident(r *http.Request) (*incidents.Incident, int64, error) {
|
||||
vars := mux.Vars(r)
|
||||
if utils.NotNumber(vars["id"]) {
|
||||
return nil, 0, errors.NotNumber
|
||||
}
|
||||
id := utils.ToInt(vars["id"])
|
||||
if id == 0 {
|
||||
return nil, id, errors.IDMissing
|
||||
}
|
||||
checkin, err := incidents.Find(id)
|
||||
if err != nil {
|
||||
return nil, id, errors.Missing(&incidents.Incident{}, id)
|
||||
}
|
||||
return checkin, id, nil
|
||||
}
|
||||
|
||||
func apiServiceIncidentsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
incids := incidents.FindByService(utils.ToInt(vars["id"]))
|
||||
|
@ -15,8 +31,7 @@ func apiServiceIncidentsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiIncidentUpdatesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
incid, err := incidents.Find(utils.ToInt(vars["id"]))
|
||||
incid, _, err := findIncident(r)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
|
@ -25,16 +40,19 @@ func apiIncidentUpdatesHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiCreateIncidentUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
var update *incidents.IncidentUpdate
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&update)
|
||||
incid, _, err := findIncident(r)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
update.IncidentId = utils.ToInt(vars["id"])
|
||||
var update *incidents.IncidentUpdate
|
||||
if err := DecodeJSON(r, &update); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
update.IncidentId = incid.Id
|
||||
|
||||
err = update.Create()
|
||||
if err != nil {
|
||||
|
@ -45,13 +63,17 @@ func apiCreateIncidentUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiCreateIncidentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var incident *incidents.Incident
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&incident)
|
||||
service, err := findService(r)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
var incident *incidents.Incident
|
||||
if err := DecodeJSON(r, &incident); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
incident.ServiceId = service.Id
|
||||
err = incident.Create()
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
|
@ -61,16 +83,12 @@ func apiCreateIncidentHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiIncidentUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
incident, err := incidents.Find(utils.ToInt(vars["id"]))
|
||||
incident, _, err := findIncident(r)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err = decoder.Decode(&incident)
|
||||
if err != nil {
|
||||
if err := DecodeJSON(r, &incident); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
@ -80,8 +98,7 @@ func apiIncidentUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiDeleteIncidentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
incident, err := incidents.Find(utils.ToInt(vars["id"]))
|
||||
incident, _, err := findIncident(r)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
|
|
|
@ -0,0 +1,155 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUnAuthenticatedIncidentRoutes(t *testing.T) {
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "No Authentication - New Incident",
|
||||
URL: "/api/services/1/incidents",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - New Incident Update",
|
||||
URL: "/api/incidents/updates",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Update Incident",
|
||||
URL: "/api/incidents/1",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Delete Incident",
|
||||
URL: "/api/incidents/1",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Delete Incident Update",
|
||||
URL: "/api/incidents/1/updates/1",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
str, t, err := RunHTTPTest(v, t)
|
||||
t.Logf("Test %s: \n %v\n", v.Name, str)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIncidentsAPIRoutes(t *testing.T) {
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "Statping Create Incident",
|
||||
URL: "/api/services/1/incidents",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 200,
|
||||
BeforeTest: SetTestENV,
|
||||
AfterTest: UnsetTestENV,
|
||||
Body: `{
|
||||
"title": "New Incident",
|
||||
"description": "This is a test for incidents"
|
||||
}`,
|
||||
ExpectedContains: []string{Success},
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 1 Incidents",
|
||||
URL: "/api/services/1/incidents",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
ResponseLen: 1,
|
||||
BeforeTest: SetTestENV,
|
||||
AfterTest: UnsetTestENV,
|
||||
ExpectedContains: []string{`"title":"New Incident"`},
|
||||
},
|
||||
{
|
||||
Name: "Statping Update Incident",
|
||||
URL: "/api/incidents/1",
|
||||
Body: `{
|
||||
"title": "Updated Incident",
|
||||
"description": "This is an updated incidents"
|
||||
}`,
|
||||
Method: "POST",
|
||||
ExpectedStatus: 200,
|
||||
BeforeTest: SetTestENV,
|
||||
ExpectedContains: []string{Success},
|
||||
},
|
||||
{
|
||||
Name: "Statping View Incident Updates",
|
||||
URL: "/api/incidents/1/updates",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
ResponseLen: 3,
|
||||
BeforeTest: SetTestENV,
|
||||
ExpectedContains: []string{`"type":"investigating"`},
|
||||
},
|
||||
{
|
||||
Name: "Statping Create Incident Update",
|
||||
URL: "/api/incidents/1/updates",
|
||||
Method: "POST",
|
||||
Body: `{
|
||||
"message": "Test message here",
|
||||
"type": "Update"
|
||||
}`,
|
||||
ExpectedStatus: 200,
|
||||
BeforeTest: SetTestENV,
|
||||
ExpectedContains: []string{Success},
|
||||
},
|
||||
{
|
||||
Name: "Incorrect Checkin JSON POST",
|
||||
URL: "/api/incidents/1/updates",
|
||||
Body: BadJSON,
|
||||
ExpectedContains: []string{BadJSONResponse},
|
||||
BeforeTest: SetTestENV,
|
||||
Method: "POST",
|
||||
ExpectedStatus: 422,
|
||||
},
|
||||
{
|
||||
Name: "Statping Delete Incident Update",
|
||||
URL: "/api/incidents/1/updates/1",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 200,
|
||||
BeforeTest: SetTestENV,
|
||||
ExpectedContains: []string{Success},
|
||||
},
|
||||
{
|
||||
Name: "Statping Delete Incident",
|
||||
URL: "/api/incidents/1",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 200,
|
||||
BeforeTest: SetTestENV,
|
||||
ExpectedContains: []string{Success},
|
||||
},
|
||||
{
|
||||
Name: "Incorrect JSON POST",
|
||||
URL: "/api/services/1/incidents",
|
||||
Body: BadJSON,
|
||||
ExpectedContains: []string{BadJSONResponse},
|
||||
Method: "POST",
|
||||
ExpectedStatus: 422,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
_, t, err := RunHTTPTest(v, t)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
func indexHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !core.App.Setup {
|
||||
http.Redirect(w, r, "/setup", http.StatusSeeOther)
|
||||
ExecuteResponse(w, r, "base.gohtml", core.App, "setup")
|
||||
return
|
||||
}
|
||||
ExecuteResponse(w, r, "base.gohtml", core.App, nil)
|
||||
|
|
|
@ -1,21 +1,24 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/types/messages"
|
||||
"github.com/statping/statping/utils"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func getMessageByID(r *http.Request) (*messages.Message, int64, error) {
|
||||
func findMessage(r *http.Request) (*messages.Message, int64, error) {
|
||||
vars := mux.Vars(r)
|
||||
num := utils.ToInt(vars["id"])
|
||||
message, err := messages.Find(num)
|
||||
if err != nil {
|
||||
return nil, num, err
|
||||
if utils.NotNumber(vars["id"]) {
|
||||
return nil, 0, errors.NotNumber
|
||||
}
|
||||
return message, num, nil
|
||||
id := utils.ToInt(vars["id"])
|
||||
message, err := messages.Find(id)
|
||||
if err != nil {
|
||||
return nil, id, err
|
||||
}
|
||||
return message, id, nil
|
||||
}
|
||||
|
||||
func apiAllMessagesHandler(r *http.Request) interface{} {
|
||||
|
@ -37,17 +40,17 @@ func apiMessageCreateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiMessageGetHandler(r *http.Request) interface{} {
|
||||
message, id, err := getMessageByID(r)
|
||||
message, _, err := findMessage(r)
|
||||
if err != nil {
|
||||
return fmt.Errorf("message #%d was not found", id)
|
||||
return err
|
||||
}
|
||||
return message
|
||||
}
|
||||
|
||||
func apiMessageDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
message, id, err := getMessageByID(r)
|
||||
message, _, err := findMessage(r)
|
||||
if err != nil {
|
||||
sendErrorJson(fmt.Errorf("message #%d was not found", id), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
err = message.Delete()
|
||||
|
@ -59,9 +62,9 @@ func apiMessageDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiMessageUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
message, id, err := getMessageByID(r)
|
||||
message, _, err := findMessage(r)
|
||||
if err != nil {
|
||||
sendErrorJson(fmt.Errorf("message #%d was not found", id), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
if err := DecodeJSON(r, &message); err != nil {
|
||||
|
|
|
@ -5,6 +5,40 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestUnAuthenticatedMessageRoutes(t *testing.T) {
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "No Authentication - New Message",
|
||||
URL: "/api/messages",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Update Message",
|
||||
URL: "/api/messages/1",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Delete Message",
|
||||
URL: "/api/messages/1",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
str, t, err := RunHTTPTest(v, t)
|
||||
t.Logf("Test %s: \n %v\n", v.Name, str)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMessagesApiRoutes(t *testing.T) {
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
|
@ -29,7 +63,7 @@ func TestMessagesApiRoutes(t *testing.T) {
|
|||
"notify_before_scale": "hour"
|
||||
}`,
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{`"status":"success"`, `"type":"message"`, `"method":"create"`, `"title":"API Message"`},
|
||||
ExpectedContains: []string{Success, `"type":"message"`, `"method":"create"`, `"title":"API Message"`},
|
||||
BeforeTest: SetTestENV,
|
||||
AfterTest: UnsetTestENV,
|
||||
SecureRoute: true,
|
||||
|
@ -40,7 +74,8 @@ func TestMessagesApiRoutes(t *testing.T) {
|
|||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{`"title":"Routine Downtime"`},
|
||||
}, {
|
||||
},
|
||||
{
|
||||
Name: "Statping Update Message",
|
||||
URL: "/api/messages/1",
|
||||
Method: "POST",
|
||||
|
@ -56,7 +91,7 @@ func TestMessagesApiRoutes(t *testing.T) {
|
|||
"notify_before_scale": "hour"
|
||||
}`,
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{`"status":"success"`, `"type":"message"`, `"method":"update"`},
|
||||
ExpectedContains: []string{Success, `"type":"message"`, MethodUpdate},
|
||||
BeforeTest: SetTestENV,
|
||||
SecureRoute: true,
|
||||
},
|
||||
|
@ -65,10 +100,26 @@ func TestMessagesApiRoutes(t *testing.T) {
|
|||
URL: "/api/messages/1",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{`"status":"success"`, `"method":"delete"`},
|
||||
ExpectedContains: []string{Success, MethodDelete},
|
||||
BeforeTest: SetTestENV,
|
||||
SecureRoute: true,
|
||||
}}
|
||||
},
|
||||
{
|
||||
Name: "Statping Missing Message",
|
||||
URL: "/api/messages/999999",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 404,
|
||||
},
|
||||
{
|
||||
Name: "Incorrect JSON POST",
|
||||
URL: "/api/messages",
|
||||
Body: BadJSON,
|
||||
ExpectedContains: []string{BadJSONResponse},
|
||||
BeforeTest: SetTestENV,
|
||||
Method: "POST",
|
||||
ExpectedStatus: 422,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
|
|
|
@ -4,9 +4,9 @@ import (
|
|||
"compress/gzip"
|
||||
"crypto/subtle"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/utils"
|
||||
"io"
|
||||
"net/http"
|
||||
|
@ -162,3 +162,13 @@ func cached(duration, contentType string, handler func(w http.ResponseWriter, r
|
|||
}
|
||||
})
|
||||
}
|
||||
|
||||
func DecodeJSON(r *http.Request, obj interface{}) error {
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&obj)
|
||||
if err != nil {
|
||||
return errors.DecodeJSON
|
||||
}
|
||||
defer r.Body.Close()
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/services"
|
||||
|
@ -41,12 +40,11 @@ func apiNotifierUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err = decoder.Decode(¬ifer)
|
||||
if err != nil {
|
||||
if err := DecodeJSON(r, ¬ifer); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
err = notifer.Update()
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
|
@ -64,9 +62,7 @@ func testNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err = decoder.Decode(¬ifer)
|
||||
if err != nil {
|
||||
if err := DecodeJSON(r, ¬ifer); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
|
|
@ -11,6 +11,40 @@ func TestAttachment(t *testing.T) {
|
|||
notifiers.InitNotifiers()
|
||||
}
|
||||
|
||||
func TestUnAuthenticatedNotifierRoutes(t *testing.T) {
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "No Authentication - View All Notifiers",
|
||||
URL: "/api/notifiers",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - View Notifier",
|
||||
URL: "/api/notifier/slack",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Update Notifier",
|
||||
URL: "/api/notifier/slack",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
str, t, err := RunHTTPTest(v, t)
|
||||
t.Logf("Test %s: \n %v\n", v.Name, str)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApiNotifiersRoutes(t *testing.T) {
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
|
@ -49,7 +83,17 @@ func TestApiNotifiersRoutes(t *testing.T) {
|
|||
ExpectedContains: []string{`"method":"slack"`},
|
||||
BeforeTest: SetTestENV,
|
||||
SecureRoute: true,
|
||||
}}
|
||||
},
|
||||
{
|
||||
Name: "Incorrect JSON POST",
|
||||
URL: "/api/notifier/slack",
|
||||
Body: BadJSON,
|
||||
ExpectedContains: []string{BadJSONResponse},
|
||||
BeforeTest: SetTestENV,
|
||||
Method: "POST",
|
||||
ExpectedStatus: 422,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
|
|
|
@ -34,10 +34,13 @@ func oauthHandler(w http.ResponseWriter, r *http.Request) {
|
|||
err, oauth = googleOAuth(r)
|
||||
case "github":
|
||||
err, oauth = githubOAuth(r)
|
||||
case "slack":
|
||||
err, oauth = slackOAuth(r)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -45,6 +48,7 @@ func oauthHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func oauthLogin(oauth *oAuth, w http.ResponseWriter, r *http.Request) {
|
||||
log.Infoln(oauth)
|
||||
user := &users.User{
|
||||
Id: 0,
|
||||
Username: oauth.Email,
|
||||
|
@ -80,7 +84,7 @@ func githubOAuth(r *http.Request) (error, *oAuth) {
|
|||
}
|
||||
|
||||
func googleOAuth(r *http.Request) (error, *oAuth) {
|
||||
c := *core.App
|
||||
c := core.App
|
||||
code := r.URL.Query().Get("code")
|
||||
|
||||
config := &oauth2.Config{
|
||||
|
|
|
@ -40,7 +40,7 @@ func hex2int(hexStr string) uint64 {
|
|||
|
||||
func prometheusHandler(w http.ResponseWriter, r *http.Request) {
|
||||
promValues = []string{}
|
||||
prefix = utils.Getenv("PREFIX", "").(string)
|
||||
prefix = utils.Params.GetString("PREFIX")
|
||||
if prefix != "" {
|
||||
prefix = prefix + "_"
|
||||
}
|
||||
|
@ -60,10 +60,6 @@ func prometheusHandler(w http.ResponseWriter, r *http.Request) {
|
|||
PrometheusKeyValue("total_services", len(services.Services()))
|
||||
PrometheusKeyValue("seconds_online", secondsOnline)
|
||||
|
||||
if secondsOnline < 5 {
|
||||
return
|
||||
}
|
||||
|
||||
for _, ser := range services.AllInOrder() {
|
||||
online := 1
|
||||
if !ser.Online {
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/statping/statping/utils"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func DecodeJSON(r *http.Request, obj interface{}) error {
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetID(r *http.Request) (int64, error) {
|
||||
vars := mux.Vars(r)
|
||||
if vars["id"] == "" {
|
||||
return 0, errors.New("no id specified in request")
|
||||
}
|
||||
return utils.ToInt(vars["id"]), nil
|
||||
}
|
|
@ -26,14 +26,14 @@ func Router() *mux.Router {
|
|||
CacheStorage = NewStorage()
|
||||
r := mux.NewRouter().StrictSlash(true)
|
||||
|
||||
authUser := utils.Getenv("AUTH_USERNAME", "").(string)
|
||||
authPass := utils.Getenv("AUTH_PASSWORD", "").(string)
|
||||
authUser := utils.Params.GetString("AUTH_USERNAME")
|
||||
authPass := utils.Params.GetString("AUTH_PASSWORD")
|
||||
|
||||
if authUser != "" && authPass != "" {
|
||||
r.Use(basicAuthHandler)
|
||||
}
|
||||
|
||||
bPath := utils.Getenv("BASE_PATH", "").(string)
|
||||
bPath := utils.Params.GetString("BASE_PATH")
|
||||
sentryHandler := sentryhttp.New(sentryhttp.Options{})
|
||||
|
||||
if bPath != "" {
|
||||
|
@ -78,6 +78,7 @@ func Router() *mux.Router {
|
|||
api.Handle("/api/cache", authenticated(apiCacheHandler, false)).Methods("GET")
|
||||
api.Handle("/api/clear_cache", authenticated(apiClearCacheHandler, false))
|
||||
api.Handle("/api/core", authenticated(apiCoreHandler, false)).Methods("POST")
|
||||
api.Handle("/api/oauth", scoped(apiOAuthHandler)).Methods("GET")
|
||||
api.Handle("/api/logs", authenticated(logsHandler, false)).Methods("GET")
|
||||
api.Handle("/api/logs/last", authenticated(logsLineHandler, false)).Methods("GET")
|
||||
|
||||
|
@ -100,7 +101,6 @@ func Router() *mux.Router {
|
|||
api.Handle("/api/services", authenticated(apiCreateServiceHandler, false)).Methods("POST")
|
||||
api.Handle("/api/services/{id}", scoped(apiServiceHandler)).Methods("GET")
|
||||
api.Handle("/api/reorder/services", authenticated(reorderServiceHandler, false)).Methods("POST")
|
||||
api.Handle("/api/services/{id}/running", authenticated(apiServiceRunningHandler, false)).Methods("POST")
|
||||
api.Handle("/api/services/{id}", authenticated(apiServiceUpdateHandler, false)).Methods("POST")
|
||||
api.Handle("/api/services/{id}", authenticated(apiServiceDeleteHandler, false)).Methods("DELETE")
|
||||
api.Handle("/api/services/{id}/failures", scoped(apiServiceFailuresHandler)).Methods("GET")
|
||||
|
@ -112,7 +112,6 @@ func Router() *mux.Router {
|
|||
api.Handle("/api/services/{id}/failure_data", cached("30s", "application/json", apiServiceFailureDataHandler)).Methods("GET")
|
||||
api.Handle("/api/services/{id}/ping_data", cached("30s", "application/json", apiServicePingDataHandler)).Methods("GET")
|
||||
api.Handle("/api/services/{id}/uptime_data", http.HandlerFunc(apiServiceTimeDataHandler)).Methods("GET")
|
||||
//api.Handle("/api/services/{id}/heatmap", cached("30s", "application/json", apiServiceHeatmapHandler)).Methods("GET")
|
||||
|
||||
// API INCIDENTS Routes
|
||||
api.Handle("/api/services/{id}/incidents", http.HandlerFunc(apiServiceIncidentsHandler)).Methods("GET")
|
||||
|
@ -152,17 +151,11 @@ func Router() *mux.Router {
|
|||
api.Handle("/api/checkins/{api}", authenticated(checkinDeleteHandler, false)).Methods("DELETE")
|
||||
r.Handle("/checkin/{api}", http.HandlerFunc(checkinHitHandler))
|
||||
|
||||
// Static Files Routes
|
||||
r.PathPrefix("/files/postman.json").Handler(http.StripPrefix("/files/", http.FileServer(source.TmplBox.HTTPBox())))
|
||||
r.PathPrefix("/files/swagger.json").Handler(http.StripPrefix("/files/", http.FileServer(source.TmplBox.HTTPBox())))
|
||||
r.PathPrefix("/files/grafana.json").Handler(http.StripPrefix("/files/", http.FileServer(source.TmplBox.HTTPBox())))
|
||||
|
||||
// API Generic Routes
|
||||
r.Handle("/metrics", readOnly(prometheusHandler, false))
|
||||
r.Handle("/health", http.HandlerFunc(healthCheckHandler))
|
||||
r.Handle("/oauth/{provider}", http.HandlerFunc(oauthHandler))
|
||||
api.Handle("/api/oauth/{provider}", http.HandlerFunc(oauthHandler))
|
||||
r.Handle("/.well-known/", http.StripPrefix("/.well-known/", http.FileServer(http.Dir(dir+"/.well-known"))))
|
||||
|
||||
r.NotFoundHandler = http.HandlerFunc(error404Handler)
|
||||
return r
|
||||
}
|
||||
|
|
|
@ -2,8 +2,8 @@ package handlers
|
|||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/hits"
|
||||
"github.com/statping/statping/types/services"
|
||||
|
@ -16,12 +16,16 @@ type serviceOrder struct {
|
|||
Order int `json:"order"`
|
||||
}
|
||||
|
||||
func serviceByID(r *http.Request) (*services.Service, error) {
|
||||
func findService(r *http.Request) (*services.Service, error) {
|
||||
vars := mux.Vars(r)
|
||||
id := utils.ToInt(vars["id"])
|
||||
servicer, err := services.Find(id)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("service %d not found", id)
|
||||
return nil, err
|
||||
}
|
||||
user := IsUser(r)
|
||||
if !servicer.Public.Bool && !user {
|
||||
return nil, errors.NotAuthenticated
|
||||
}
|
||||
return servicer, nil
|
||||
}
|
||||
|
@ -37,7 +41,7 @@ func reorderServiceHandler(w http.ResponseWriter, r *http.Request) {
|
|||
for _, s := range newOrder {
|
||||
service, err := services.Find(s.Id)
|
||||
if err != nil {
|
||||
sendErrorJson(errors.Errorf("service %d not found", s.Id), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
service.Order = s.Order
|
||||
|
@ -47,14 +51,10 @@ func reorderServiceHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiServiceHandler(r *http.Request) interface{} {
|
||||
srv, err := serviceByID(r)
|
||||
srv, err := findService(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
user := IsUser(r)
|
||||
if !srv.Public.Bool && !user {
|
||||
return errors.New("not authenticated")
|
||||
}
|
||||
srv = srv.UpdateStats()
|
||||
return *srv
|
||||
}
|
||||
|
@ -76,13 +76,13 @@ func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiServiceUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
service, err := serviceByID(r)
|
||||
service, err := findService(r)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r, http.StatusNotFound)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
if err := DecodeJSON(r, &service); err != nil {
|
||||
sendErrorJson(err, w, r, http.StatusBadRequest)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -95,27 +95,12 @@ func apiServiceUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sendJsonAction(service, "update", w, r)
|
||||
}
|
||||
|
||||
func apiServiceRunningHandler(w http.ResponseWriter, r *http.Request) {
|
||||
service, err := serviceByID(r)
|
||||
func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
service, err := findService(r)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
if service.IsRunning() {
|
||||
service.Close()
|
||||
} else {
|
||||
service.Start()
|
||||
}
|
||||
sendJsonAction(service, "running", w, r)
|
||||
}
|
||||
|
||||
func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
service, err := services.Find(utils.ToInt(vars["id"]))
|
||||
if err != nil {
|
||||
sendErrorJson(errors.New("service data not found"), w, r)
|
||||
return
|
||||
}
|
||||
|
||||
groupQuery, err := database.ParseQueries(r, service.AllHits())
|
||||
if err != nil {
|
||||
|
@ -132,10 +117,9 @@ func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiServiceFailureDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
service, err := services.Find(utils.ToInt(vars["id"]))
|
||||
service, err := findService(r)
|
||||
if err != nil {
|
||||
sendErrorJson(errors.New("service data not found"), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -155,9 +139,9 @@ func apiServiceFailureDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiServicePingDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
service, err := serviceByID(r)
|
||||
service, err := findService(r)
|
||||
if err != nil {
|
||||
sendErrorJson(errors.New("service data not found"), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -177,9 +161,9 @@ func apiServicePingDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiServiceTimeDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
service, err := serviceByID(r)
|
||||
service, err := findService(r)
|
||||
if err != nil {
|
||||
sendErrorJson(errors.New("service data not found"), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -217,7 +201,7 @@ func apiServiceTimeDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiServiceDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
service, err := serviceByID(r)
|
||||
service, err := findService(r)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
|
@ -244,7 +228,7 @@ func apiAllServicesHandler(r *http.Request) interface{} {
|
|||
}
|
||||
|
||||
func servicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) {
|
||||
service, err := serviceByID(r)
|
||||
service, err := findService(r)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
|
@ -258,12 +242,10 @@ func servicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiServiceFailuresHandler(r *http.Request) interface{} {
|
||||
vars := mux.Vars(r)
|
||||
service, err := services.Find(utils.ToInt(vars["id"]))
|
||||
service, err := findService(r)
|
||||
if err != nil {
|
||||
return errors.New("service not found")
|
||||
return err
|
||||
}
|
||||
|
||||
var fails []*failures.Failure
|
||||
query, err := database.ParseQueries(r, service.AllFailures())
|
||||
if err != nil {
|
||||
|
@ -274,12 +256,10 @@ func apiServiceFailuresHandler(r *http.Request) interface{} {
|
|||
}
|
||||
|
||||
func apiServiceHitsHandler(r *http.Request) interface{} {
|
||||
vars := mux.Vars(r)
|
||||
service, err := services.Find(utils.ToInt(vars["id"]))
|
||||
service, err := findService(r)
|
||||
if err != nil {
|
||||
return errors.New("service not found")
|
||||
return err
|
||||
}
|
||||
|
||||
var hts []*hits.Hit
|
||||
query, err := database.ParseQueries(r, service.AllHits())
|
||||
if err != nil {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/statping/statping/types"
|
||||
|
@ -9,12 +10,47 @@ import (
|
|||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func TestApiServiceRoutes(t *testing.T) {
|
||||
func TestUnAuthenticatedServicesRoutes(t *testing.T) {
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "No Authentication - New Service",
|
||||
URL: "/api/services",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Update Service",
|
||||
URL: "/api/services/1",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Delete Service",
|
||||
URL: "/api/services/1",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
str, t, err := RunHTTPTest(v, t)
|
||||
t.Logf("Test %s: \n %v\n", v.Name, str)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApiServiceRoutes(t *testing.T) {
|
||||
since := utils.Now().Add(-30 * types.Day)
|
||||
startEndQuery := fmt.Sprintf("?start=%d&end=%d", since.Unix(), utils.Now().Unix())
|
||||
end := utils.Now().Add(-30 * time.Minute)
|
||||
startEndQuery := fmt.Sprintf("?start=%d&end=%d", since.Unix(), end.Unix()+15)
|
||||
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
|
@ -58,13 +94,20 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "Statping Private Service 1",
|
||||
Name: "Statping Private Service 6",
|
||||
URL: "/api/services/6",
|
||||
Method: "GET",
|
||||
ExpectedContains: []string{`"error":"not authenticated"`},
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{`"error":"user not authenticated"`},
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "Statping Authenticated Private Service 6",
|
||||
URL: "/api/services/6",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
BeforeTest: SetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 1 with Private responses",
|
||||
URL: "/api/services/1",
|
||||
|
@ -75,9 +118,23 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Name: "Statping Service Failures",
|
||||
URL: "/api/services/1/failures",
|
||||
URL: "/api/services/1/failures" + startEndQuery,
|
||||
Method: "GET",
|
||||
ResponseLen: 125,
|
||||
GreaterThan: 120,
|
||||
ExpectedStatus: 200,
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 1 Hits",
|
||||
URL: "/api/services/1/hits" + startEndQuery,
|
||||
Method: "GET",
|
||||
GreaterThan: 8580,
|
||||
ExpectedStatus: 200,
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 2 Hits",
|
||||
URL: "/api/services/2/hits" + startEndQuery,
|
||||
Method: "GET",
|
||||
GreaterThan: 8580,
|
||||
ExpectedStatus: 200,
|
||||
},
|
||||
{
|
||||
|
@ -88,55 +145,73 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
ExpectedStatus: 200,
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 1 Data",
|
||||
Name: "Statping Service 1 Hits Data",
|
||||
URL: "/api/services/1/hits_data" + startEndQuery,
|
||||
Method: "GET",
|
||||
ResponseLen: 73,
|
||||
GreaterThan: 70,
|
||||
ExpectedStatus: 200,
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 1 Ping Data",
|
||||
URL: "/api/services/1/ping_data" + startEndQuery,
|
||||
Method: "GET",
|
||||
ResponseLen: 73,
|
||||
ExpectedStatus: 200,
|
||||
GreaterThan: 70,
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 1 Failure Data - 24 Hour",
|
||||
URL: "/api/services/1/failure_data" + startEndQuery + "&group=24h",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
GreaterThan: 3,
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 1 Failure Data - 12 Hour",
|
||||
URL: "/api/services/1/failure_data" + startEndQuery + "&group=12h",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
GreaterThan: 6,
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 1 Failure Data - 1 Hour",
|
||||
URL: "/api/services/1/failure_data" + startEndQuery + "&group=1h",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
GreaterThan: 70,
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 1 Failure Data - 15 Minute",
|
||||
URL: "/api/services/1/failure_data" + startEndQuery + "&group=15m",
|
||||
Method: "GET",
|
||||
ResponseLen: 124,
|
||||
GreaterThan: 120,
|
||||
ExpectedStatus: 200,
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 1 Hits",
|
||||
URL: "/api/services/1/hits_data" + startEndQuery,
|
||||
Method: "GET",
|
||||
ResponseLen: 73,
|
||||
GreaterThan: 70,
|
||||
ExpectedStatus: 200,
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 1 Uptime",
|
||||
URL: "/api/services/1/uptime_data" + startEndQuery,
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
ResponseFunc: func(t *testing.T, resp []byte) error {
|
||||
var uptime *services.UptimeSeries
|
||||
if err := json.Unmarshal(resp, &uptime); err != nil {
|
||||
return err
|
||||
}
|
||||
assert.GreaterOrEqual(t, uptime.Uptime, int64(200000000))
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 1 Failure Data",
|
||||
URL: "/api/services/1/failure_data" + startEndQuery,
|
||||
Method: "GET",
|
||||
GreaterThan: 70,
|
||||
ExpectedStatus: 200,
|
||||
},
|
||||
{
|
||||
|
@ -169,7 +244,7 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
"order_id": 0
|
||||
}`,
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{`"status":"success","type":"service","method":"create"`, `"public":false`, `"group_id":1`},
|
||||
ExpectedContains: []string{Success, `"type":"service","method":"create"`, `"public":false`, `"group_id":1`},
|
||||
FuncTest: func(t *testing.T) error {
|
||||
count := len(services.Services())
|
||||
if count != 7 {
|
||||
|
@ -198,7 +273,7 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
"order_id": 0
|
||||
}`,
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{`"status":"success"`, `"name":"Updated New Service"`, `"method":"update"`},
|
||||
ExpectedContains: []string{Success, `"name":"Updated New Service"`, MethodUpdate},
|
||||
FuncTest: func(t *testing.T) error {
|
||||
item, err := services.Find(1)
|
||||
require.Nil(t, err)
|
||||
|
@ -214,7 +289,7 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
URL: "/api/services/1/failures",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{`"status":"success"`, `"method":"delete_failures"`},
|
||||
ExpectedContains: []string{Success, `"method":"delete_failures"`},
|
||||
FuncTest: func(t *testing.T) error {
|
||||
item, err := services.Find(1)
|
||||
require.Nil(t, err)
|
||||
|
@ -231,7 +306,7 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
URL: "/api/services/1",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{`"status":"success"`, `"method":"delete"`},
|
||||
ExpectedContains: []string{Success, MethodDelete},
|
||||
FuncTest: func(t *testing.T) error {
|
||||
count := len(services.Services())
|
||||
if count != 6 {
|
||||
|
@ -240,7 +315,17 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
return nil
|
||||
},
|
||||
SecureRoute: true,
|
||||
}}
|
||||
},
|
||||
{
|
||||
Name: "Incorrect JSON POST",
|
||||
URL: "/api/services",
|
||||
Body: BadJSON,
|
||||
ExpectedContains: []string{BadJSONResponse},
|
||||
BeforeTest: SetTestENV,
|
||||
Method: "POST",
|
||||
ExpectedStatus: 422,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
|
|
|
@ -32,13 +32,8 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
log.WithFields(utils.ToFields(core.App, confgs)).Debugln("new configs posted")
|
||||
|
||||
if err = configs.ConnectConfigs(confgs); err != nil {
|
||||
if err = configs.ConnectConfigs(confgs, false); err != nil {
|
||||
log.Errorln(err)
|
||||
if err := confgs.Delete(); err != nil {
|
||||
log.Errorln(err)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
@ -83,8 +78,8 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
c := &core.Core{
|
||||
Name: project,
|
||||
Description: description,
|
||||
//ApiKey: apiKey.(string),
|
||||
//ApiSecret: apiSecret.(string),
|
||||
ApiKey: utils.Params.GetString("API_KEY"),
|
||||
ApiSecret: utils.Params.GetString("API_SECRET"),
|
||||
Domain: domain,
|
||||
Version: core.App.Version,
|
||||
Started: utils.Now(),
|
||||
|
|
|
@ -7,7 +7,50 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestUnAuthenticatedThemeRoutes(t *testing.T) {
|
||||
t.SkipNow()
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "No Authentication - Create Themes",
|
||||
URL: "/api/theme/create",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - View Themes",
|
||||
URL: "/api/theme",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Update Themes",
|
||||
URL: "/api/theme",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Delete Themes",
|
||||
URL: "/api/theme",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
str, t, err := RunHTTPTest(v, t)
|
||||
t.Logf("Test %s: \n %v\n", v.Name, str)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestThemeRoutes(t *testing.T) {
|
||||
t.SkipNow()
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "Create Theme Assets",
|
||||
|
@ -17,7 +60,7 @@ func TestThemeRoutes(t *testing.T) {
|
|||
ExpectedContains: []string{`"status":"success"`},
|
||||
BeforeTest: SetTestENV,
|
||||
AfterTest: func(t *testing.T) error {
|
||||
assert.True(t, source.UsingAssets(utils.Directory))
|
||||
assert.True(t, source.UsingAssets(utils.Params.GetString("STATPING_DIR")))
|
||||
return nil
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,28 +1,31 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/types/users"
|
||||
"github.com/statping/statping/utils"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func getUser(r *http.Request) (*users.User, int64, error) {
|
||||
func findUser(r *http.Request) (*users.User, int64, error) {
|
||||
vars := mux.Vars(r)
|
||||
if utils.NotNumber(vars["id"]) {
|
||||
return nil, 0, errors.NotNumber
|
||||
}
|
||||
num := utils.ToInt(vars["id"])
|
||||
user, err := users.Find(num)
|
||||
if err != nil {
|
||||
return nil, num, err
|
||||
return nil, num, errors.Missing(&users.User{}, num)
|
||||
}
|
||||
return user, num, nil
|
||||
}
|
||||
|
||||
func apiUserHandler(w http.ResponseWriter, r *http.Request) {
|
||||
user, _, err := getUser(r)
|
||||
user, _, err := findUser(r)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r, http.StatusNotFound)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
user.Password = ""
|
||||
|
@ -30,15 +33,15 @@ func apiUserHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiUserUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
user, id, err := getUser(r)
|
||||
user, _, err := findUser(r)
|
||||
if err != nil {
|
||||
sendErrorJson(fmt.Errorf("user #%d was not found", id), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
err = DecodeJSON(r, &user)
|
||||
if err != nil {
|
||||
sendErrorJson(fmt.Errorf("user #%d was not found", id), w, r)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -60,7 +63,7 @@ func apiUserDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sendErrorJson(errors.New("cannot delete the last user"), w, r)
|
||||
return
|
||||
}
|
||||
user, _, err := getUser(r)
|
||||
user, _, err := findUser(r)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
|
|
|
@ -1,11 +1,54 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/statping/statping/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestUnAuthenticatedUserRoutes(t *testing.T) {
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "No Authentication - New User",
|
||||
URL: "/api/users",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Update User",
|
||||
URL: "/api/users/1",
|
||||
Method: "POST",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - View User",
|
||||
URL: "/api/users/1",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
Name: "No Authentication - Delete User",
|
||||
URL: "/api/users/1",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
str, t, err := RunHTTPTest(v, t)
|
||||
t.Logf("Test %s: \n %v\n", v.Name, str)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApiUsersRoutes(t *testing.T) {
|
||||
form := url.Values{}
|
||||
form.Add("username", "adminupdated")
|
||||
|
@ -16,12 +59,29 @@ func TestApiUsersRoutes(t *testing.T) {
|
|||
badForm.Add("password", "wrongpassword")
|
||||
|
||||
tests := []HTTPTest{
|
||||
{
|
||||
Name: "Check Basic Authentication",
|
||||
URL: "/api",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 401,
|
||||
BeforeTest: func(t *testing.T) error {
|
||||
utils.Params.Set("AUTH_USERNAME", "admin")
|
||||
utils.Params.Set("AUTH_PASSWORD", "admin")
|
||||
return nil
|
||||
},
|
||||
AfterTest: func(t *testing.T) error {
|
||||
utils.Params.Set("AUTH_USERNAME", "")
|
||||
utils.Params.Set("AUTH_PASSWORD", "")
|
||||
return nil
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Statping All Users",
|
||||
URL: "/api/users",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
ResponseLen: 1,
|
||||
BeforeTest: SetTestENV,
|
||||
}, {
|
||||
Name: "Statping Create User",
|
||||
URL: "/api/users",
|
||||
|
@ -34,11 +94,17 @@ func TestApiUsersRoutes(t *testing.T) {
|
|||
"admin": true
|
||||
}`,
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{Success, MethodCreate},
|
||||
}, {
|
||||
Name: "Statping View User",
|
||||
URL: "/api/users/1",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
}, {
|
||||
Name: "Statping Incorrect User ID",
|
||||
URL: "/api/users/NOinteger",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 422,
|
||||
}, {
|
||||
Name: "Statping Missing User",
|
||||
URL: "/api/users/9393939393",
|
||||
|
@ -55,11 +121,13 @@ func TestApiUsersRoutes(t *testing.T) {
|
|||
"admin": true
|
||||
}`,
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{Success, MethodUpdate},
|
||||
}, {
|
||||
Name: "Statping Delete User",
|
||||
URL: "/api/users/2",
|
||||
Method: "DELETE",
|
||||
ExpectedStatus: 200,
|
||||
ExpectedContains: []string{Success, MethodDelete},
|
||||
}, {
|
||||
Name: "Statping Login User",
|
||||
URL: "/api/login",
|
||||
|
@ -81,7 +149,17 @@ func TestApiUsersRoutes(t *testing.T) {
|
|||
URL: "/api/logout",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 303,
|
||||
}}
|
||||
},
|
||||
{
|
||||
Name: "Incorrect JSON POST",
|
||||
URL: "/api/users",
|
||||
Body: BadJSON,
|
||||
ExpectedContains: []string{BadJSONResponse},
|
||||
BeforeTest: SetTestENV,
|
||||
Method: "POST",
|
||||
ExpectedStatus: 422,
|
||||
},
|
||||
}
|
||||
|
||||
for _, v := range tests {
|
||||
t.Run(v.Name, func(t *testing.T) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/null"
|
||||
"github.com/statping/statping/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"os"
|
||||
|
@ -12,7 +13,7 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
DISCORD_URL = os.Getenv("DISCORD_URL")
|
||||
DISCORD_URL = utils.Params.GetString("DISCORD_URL")
|
||||
discordMessage = `{"content": "The discord notifier on Statping has been tested!"}`
|
||||
)
|
||||
|
||||
|
|
|
@ -8,7 +8,6 @@ import (
|
|||
"github.com/statping/statping/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
@ -25,12 +24,12 @@ var (
|
|||
var testEmail *emailOutgoing
|
||||
|
||||
func init() {
|
||||
EMAIL_HOST = os.Getenv("EMAIL_HOST")
|
||||
EMAIL_USER = os.Getenv("EMAIL_USER")
|
||||
EMAIL_PASS = os.Getenv("EMAIL_PASS")
|
||||
EMAIL_OUTGOING = os.Getenv("EMAIL_OUTGOING")
|
||||
EMAIL_SEND_TO = os.Getenv("EMAIL_SEND_TO")
|
||||
EMAIL_PORT = utils.ToInt(os.Getenv("EMAIL_PORT"))
|
||||
EMAIL_HOST = utils.Params.GetString("EMAIL_HOST")
|
||||
EMAIL_USER = utils.Params.GetString("EMAIL_USER")
|
||||
EMAIL_PASS = utils.Params.GetString("EMAIL_PASS")
|
||||
EMAIL_OUTGOING = utils.Params.GetString("EMAIL_OUTGOING")
|
||||
EMAIL_SEND_TO = utils.Params.GetString("EMAIL_SEND_TO")
|
||||
EMAIL_PORT = utils.ToInt(utils.Params.GetString("EMAIL_PORT"))
|
||||
}
|
||||
|
||||
func TestEmailNotifier(t *testing.T) {
|
||||
|
|
|
@ -4,9 +4,9 @@ import (
|
|||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/null"
|
||||
"github.com/statping/statping/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
@ -21,7 +21,7 @@ func TestSlackNotifier(t *testing.T) {
|
|||
db.AutoMigrate(¬ifications.Notification{})
|
||||
notifications.SetDB(db)
|
||||
|
||||
SLACK_URL = os.Getenv("SLACK_URL")
|
||||
SLACK_URL = utils.Params.GetString("SLACK_URL")
|
||||
slacker.Host = SLACK_URL
|
||||
slacker.Enabled = null.NewNullBool(true)
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/russross/blackfriday/v2"
|
||||
"github.com/statping/statping/utils"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
@ -42,10 +43,13 @@ func scssRendered(name string) string {
|
|||
|
||||
// CompileSASS will attempt to compile the SASS files into CSS
|
||||
func CompileSASS(files ...string) error {
|
||||
sassBin := utils.Getenv("SASS", "sass").(string)
|
||||
sassBin, err := exec.LookPath("sass")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, file := range files {
|
||||
scssFile := fmt.Sprintf("%v/assets/%v", utils.Directory, file)
|
||||
scssFile := fmt.Sprintf("%v/assets/%v", utils.Params.GetString("STATPING_DIR"), file)
|
||||
|
||||
log.Infoln(fmt.Sprintf("Compiling SASS %v into %v", scssFile, scssRendered(scssFile)))
|
||||
|
||||
|
@ -57,10 +61,10 @@ func CompileSASS(files ...string) error {
|
|||
return errors.Wrapf(err, "failed to compile assets, %s %s %s", err, stdout, stderr)
|
||||
}
|
||||
|
||||
if stdout != "" || stderr != "" {
|
||||
log.Errorln(fmt.Sprintf("Failed to compile assets with SASS %v %v %v", err, stdout, stderr))
|
||||
return errors.Wrap(err, "failed to capture stdout or stderr")
|
||||
}
|
||||
//if stdout != "" || stderr != "" {
|
||||
// log.Errorln(fmt.Sprintf("Failed to compile assets with SASS %v %v %v", err, stdout, stderr))
|
||||
// return errors.Wrap(err, "failed to capture stdout or stderr")
|
||||
//}
|
||||
|
||||
if stdout != "" || stderr != "" {
|
||||
log.Infoln(fmt.Sprintf("out: %v | error: %v", stdout, stderr))
|
||||
|
@ -75,7 +79,7 @@ func UsingAssets(folder string) bool {
|
|||
if _, err := os.Stat(folder + "/assets"); err == nil {
|
||||
return true
|
||||
} else {
|
||||
useAssets := utils.Getenv("USE_ASSETS", false).(bool)
|
||||
useAssets := utils.Params.GetBool("USE_ASSETS")
|
||||
|
||||
if useAssets {
|
||||
log.Infoln("Environment variable USE_ASSETS was found.")
|
||||
|
@ -137,9 +141,6 @@ func CreateAllAssets(folder string) error {
|
|||
CopyToPublic(TmplBox, "", "robots.txt")
|
||||
CopyToPublic(TmplBox, "", "banner.png")
|
||||
CopyToPublic(TmplBox, "", "favicon.ico")
|
||||
CopyToPublic(TmplBox, "files", "swagger.json")
|
||||
CopyToPublic(TmplBox, "files", "postman.json")
|
||||
CopyToPublic(TmplBox, "files", "grafana.json")
|
||||
log.Infoln("Compiling CSS from SCSS style...")
|
||||
err := CompileSASS(DefaultScss...)
|
||||
log.Infoln("Statping assets have been inserted")
|
||||
|
@ -163,9 +164,6 @@ func CopyAllToPublic(box *rice.Box) error {
|
|||
exclude := map[string]bool{
|
||||
"base.gohtml": true,
|
||||
"index.html": true,
|
||||
"swagger.json": true,
|
||||
"postman.json": true,
|
||||
"grafana.json": true,
|
||||
}
|
||||
|
||||
err := box.Walk("/", func(path string, info os.FileInfo, err error) error {
|
||||
|
|
|
@ -16,6 +16,7 @@ func init() {
|
|||
utils.InitLogs()
|
||||
Assets()
|
||||
utils.DeleteDirectory(dir + "/assets")
|
||||
dir = utils.Params.GetString("STATPING_DIR")
|
||||
}
|
||||
|
||||
func TestCore_UsingAssets(t *testing.T) {
|
||||
|
@ -23,7 +24,7 @@ func TestCore_UsingAssets(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestCreateAssets(t *testing.T) {
|
||||
assert.Nil(t, CreateAllAssets(dir))
|
||||
CreateAllAssets(dir)
|
||||
assert.True(t, UsingAssets(dir))
|
||||
assert.Nil(t, CompileSASS(DefaultScss...))
|
||||
assert.FileExists(t, dir+"/assets/css/main.css")
|
||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,611 +0,0 @@
|
|||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"version": "1.0",
|
||||
"title": "Statping",
|
||||
"description": "Statping API Requests"
|
||||
},
|
||||
"host": "example.com",
|
||||
"basePath": "/",
|
||||
"securityDefinitions": {
|
||||
"auth": {
|
||||
"type": "oauth2",
|
||||
"flow": "implicit",
|
||||
"authorizationUrl": "http://example.com",
|
||||
"scopes": {}
|
||||
}
|
||||
},
|
||||
"schemes": [
|
||||
"http"
|
||||
],
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"paths": {
|
||||
"/api": {
|
||||
"get": {
|
||||
"description": "TODO: Add Description",
|
||||
"summary": "Statping Details",
|
||||
"tags": [
|
||||
"Main"
|
||||
],
|
||||
"operationId": "ApiGet",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"auth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/services": {
|
||||
"get": {
|
||||
"description": "TODO: Add Description",
|
||||
"summary": "View All Services",
|
||||
"tags": [
|
||||
"Services"
|
||||
],
|
||||
"operationId": "ApiServicesGet",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"auth": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"description": "TODO: Add Description",
|
||||
"summary": "Create Service",
|
||||
"tags": [
|
||||
"Services"
|
||||
],
|
||||
"operationId": "ApiServicesPost",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "Body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"description": "",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/CreateServicerequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"auth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/services/1": {
|
||||
"get": {
|
||||
"description": "TODO: Add Description",
|
||||
"summary": "View Service",
|
||||
"tags": [
|
||||
"Services"
|
||||
],
|
||||
"operationId": "ApiServices1Get",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"auth": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"description": "TODO: Add Description",
|
||||
"summary": "Delete Service",
|
||||
"tags": [
|
||||
"Services"
|
||||
],
|
||||
"operationId": "ApiServices1Delete",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"auth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/services/19": {
|
||||
"post": {
|
||||
"description": "TODO: Add Description",
|
||||
"summary": "Update Service",
|
||||
"tags": [
|
||||
"Services"
|
||||
],
|
||||
"operationId": "ApiServices19Post",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "Body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"description": "",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/UpdateServicerequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"auth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/users": {
|
||||
"get": {
|
||||
"description": "TODO: Add Description",
|
||||
"summary": "View All Users",
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"operationId": "ApiUsersGet",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"auth": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"post": {
|
||||
"description": "TODO: Add Description",
|
||||
"summary": "Create User",
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"operationId": "ApiUsersPost",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "Body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"description": "",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/CreateUserrequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"auth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/users/1": {
|
||||
"get": {
|
||||
"description": "TODO: Add Description",
|
||||
"summary": "View User",
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"operationId": "ApiUsers1Get",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"auth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"/api/users/4": {
|
||||
"post": {
|
||||
"description": "TODO: Add Description",
|
||||
"summary": "Update User",
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"operationId": "ApiUsers4Post",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [
|
||||
{
|
||||
"name": "Content-Type",
|
||||
"in": "header",
|
||||
"required": true,
|
||||
"type": "string",
|
||||
"description": ""
|
||||
},
|
||||
{
|
||||
"name": "Body",
|
||||
"in": "body",
|
||||
"required": true,
|
||||
"description": "",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/UpdateUserrequest"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"auth": []
|
||||
}
|
||||
]
|
||||
},
|
||||
"delete": {
|
||||
"description": "TODO: Add Description",
|
||||
"summary": "Delete User",
|
||||
"tags": [
|
||||
"Users"
|
||||
],
|
||||
"operationId": "ApiUsers4Delete",
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"parameters": [],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": ""
|
||||
}
|
||||
},
|
||||
"security": [
|
||||
{
|
||||
"auth": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"definitions": {
|
||||
"CreateServicerequest": {
|
||||
"title": "Create ServiceRequest",
|
||||
"example": {
|
||||
"name": "New Service",
|
||||
"domain": "https://google.com",
|
||||
"expected": "",
|
||||
"expected_status": 200,
|
||||
"check_interval": 15,
|
||||
"type": "http",
|
||||
"method": "GET",
|
||||
"post_data": "",
|
||||
"port": 0,
|
||||
"timeout": 10,
|
||||
"order_id": 0
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "",
|
||||
"example": "New Service",
|
||||
"type": "string"
|
||||
},
|
||||
"domain": {
|
||||
"description": "",
|
||||
"example": "https://google.com",
|
||||
"type": "string"
|
||||
},
|
||||
"expected": {
|
||||
"description": "",
|
||||
"type": "string"
|
||||
},
|
||||
"expected_status": {
|
||||
"description": "",
|
||||
"example": 200,
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"check_interval": {
|
||||
"description": "",
|
||||
"example": 15,
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"type": {
|
||||
"description": "",
|
||||
"example": "http",
|
||||
"type": "string"
|
||||
},
|
||||
"method": {
|
||||
"description": "",
|
||||
"example": "GET",
|
||||
"type": "string"
|
||||
},
|
||||
"post_data": {
|
||||
"description": "",
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"description": "",
|
||||
"example": 0,
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"timeout": {
|
||||
"description": "",
|
||||
"example": 10,
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"order_id": {
|
||||
"description": "",
|
||||
"example": 0,
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"domain",
|
||||
"expected",
|
||||
"expected_status",
|
||||
"check_interval",
|
||||
"type",
|
||||
"method",
|
||||
"post_data",
|
||||
"port",
|
||||
"timeout",
|
||||
"order_id"
|
||||
]
|
||||
},
|
||||
"UpdateServicerequest": {
|
||||
"title": "Update ServiceRequest",
|
||||
"example": {
|
||||
"name": "Updated Service",
|
||||
"domain": "https://google.com",
|
||||
"expected": "",
|
||||
"expected_status": 200,
|
||||
"check_interval": 60,
|
||||
"type": "http",
|
||||
"method": "GET",
|
||||
"post_data": "",
|
||||
"port": 0,
|
||||
"timeout": 10,
|
||||
"order_id": 0
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "",
|
||||
"example": "Updated Service",
|
||||
"type": "string"
|
||||
},
|
||||
"domain": {
|
||||
"description": "",
|
||||
"example": "https://google.com",
|
||||
"type": "string"
|
||||
},
|
||||
"expected": {
|
||||
"description": "",
|
||||
"type": "string"
|
||||
},
|
||||
"expected_status": {
|
||||
"description": "",
|
||||
"example": 200,
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"check_interval": {
|
||||
"description": "",
|
||||
"example": 60,
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"type": {
|
||||
"description": "",
|
||||
"example": "http",
|
||||
"type": "string"
|
||||
},
|
||||
"method": {
|
||||
"description": "",
|
||||
"example": "GET",
|
||||
"type": "string"
|
||||
},
|
||||
"post_data": {
|
||||
"description": "",
|
||||
"type": "string"
|
||||
},
|
||||
"port": {
|
||||
"description": "",
|
||||
"example": 0,
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"timeout": {
|
||||
"description": "",
|
||||
"example": 10,
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
},
|
||||
"order_id": {
|
||||
"description": "",
|
||||
"example": 0,
|
||||
"type": "integer",
|
||||
"format": "int32"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"name",
|
||||
"domain",
|
||||
"expected",
|
||||
"expected_status",
|
||||
"check_interval",
|
||||
"type",
|
||||
"method",
|
||||
"post_data",
|
||||
"port",
|
||||
"timeout",
|
||||
"order_id"
|
||||
]
|
||||
},
|
||||
"CreateUserrequest": {
|
||||
"title": "Create UserRequest",
|
||||
"example": {
|
||||
"username": "admin",
|
||||
"email": "info@email.com",
|
||||
"password": "password123",
|
||||
"admin": true
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"username": {
|
||||
"description": "",
|
||||
"example": "admin",
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"description": "",
|
||||
"example": "info@email.com",
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"description": "",
|
||||
"example": "password123",
|
||||
"type": "string"
|
||||
},
|
||||
"admin": {
|
||||
"description": "",
|
||||
"example": true,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"username",
|
||||
"email",
|
||||
"password",
|
||||
"admin"
|
||||
]
|
||||
},
|
||||
"UpdateUserrequest": {
|
||||
"title": "Update UserRequest",
|
||||
"example": {
|
||||
"username": "adminupdated",
|
||||
"email": "info@email.com",
|
||||
"password": "password123",
|
||||
"admin": true
|
||||
},
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"username": {
|
||||
"description": "",
|
||||
"example": "adminupdated",
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"description": "",
|
||||
"example": "info@email.com",
|
||||
"type": "string"
|
||||
},
|
||||
"password": {
|
||||
"description": "",
|
||||
"example": "password123",
|
||||
"type": "string"
|
||||
},
|
||||
"admin": {
|
||||
"description": "",
|
||||
"example": true,
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"username",
|
||||
"email",
|
||||
"password",
|
||||
"admin"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,10 @@ func (c *Checkin) Expected() time.Duration {
|
|||
}
|
||||
|
||||
func (c *Checkin) Period() time.Duration {
|
||||
duration, _ := time.ParseDuration(fmt.Sprintf("%vs", c.Interval))
|
||||
duration, _ := time.ParseDuration(fmt.Sprintf("%ds", c.Interval))
|
||||
if duration.Seconds() <= 15 {
|
||||
return 15 * time.Second
|
||||
}
|
||||
return duration
|
||||
}
|
||||
|
||||
|
|
|
@ -38,10 +38,10 @@ CheckinLoop:
|
|||
c.Failing = false
|
||||
break CheckinLoop
|
||||
case <-time.After(reCheck):
|
||||
log.Infoln(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 '%s' expects a request every %v", c.Name, utils.FormatDuration(c.Period())))
|
||||
if c.Expected() <= 0 {
|
||||
issue := fmt.Sprintf("Checkin %v is failing, no request since %v", c.Name, lastHit.CreatedAt)
|
||||
log.Errorln(issue)
|
||||
issue := fmt.Sprintf("Checkin '%s' is failing, no request since %v", c.Name, lastHit.CreatedAt)
|
||||
//log.Errorln(issue)
|
||||
|
||||
fail := &failures.Failure{
|
||||
Issue: issue,
|
||||
|
@ -49,7 +49,6 @@ CheckinLoop:
|
|||
Service: c.ServiceId,
|
||||
Checkin: c.Id,
|
||||
PingTime: c.Expected().Milliseconds(),
|
||||
CreatedAt: time.Time{},
|
||||
}
|
||||
|
||||
c.CreateFailure(fail)
|
||||
|
|
|
@ -31,7 +31,7 @@ func Samples() error {
|
|||
}
|
||||
|
||||
func SamplesChkHits() error {
|
||||
checkTime := time.Now().UTC().Add(-24 * time.Hour)
|
||||
checkTime := utils.Now().Add(-3 * time.Minute)
|
||||
|
||||
for i := int64(1); i <= 2; i++ {
|
||||
checkHit := &CheckinHit{
|
||||
|
@ -44,7 +44,7 @@ func SamplesChkHits() error {
|
|||
return err
|
||||
}
|
||||
|
||||
checkTime = checkTime.Add(10 * time.Minute)
|
||||
checkTime = checkTime.Add(1 * time.Minute)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
|
|
@ -32,6 +32,8 @@ type CheckinHit struct {
|
|||
}
|
||||
|
||||
func (c *Checkin) BeforeCreate() (err error) {
|
||||
if c.ApiKey == "" {
|
||||
c.ApiKey = utils.RandomString(7)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
package configs
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSQLiteConfig(t *testing.T) {
|
||||
sqlite := &DbConfig{
|
||||
DbConn: "sqlite",
|
||||
DbHost: "localhost",
|
||||
DbUser: "",
|
||||
DbPass: "",
|
||||
DbData: "",
|
||||
DbPort: 0,
|
||||
}
|
||||
|
||||
err := Connect(sqlite, false)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestMySQLConfig(t *testing.T) {
|
||||
mysql := &DbConfig{
|
||||
DbConn: "mysql",
|
||||
DbHost: "localhost",
|
||||
DbUser: "root",
|
||||
DbPass: "password123",
|
||||
DbData: "statping",
|
||||
DbPort: 3306,
|
||||
}
|
||||
|
||||
err := Connect(mysql, false)
|
||||
require.Nil(t, err)
|
||||
}
|
||||
|
||||
func TestPostgresConfig(t *testing.T) {
|
||||
postgres := &DbConfig{
|
||||
DbConn: "postgres",
|
||||
DbHost: "localhost",
|
||||
DbUser: "root",
|
||||
DbPass: "password123",
|
||||
DbData: "statping",
|
||||
DbPort: 5432,
|
||||
}
|
||||
|
||||
err := Connect(postgres, false)
|
||||
require.Nil(t, err)
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
package configs
|
||||
|
||||
import (
|
||||
"github.com/joho/godotenv"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/statping/statping/utils"
|
||||
)
|
||||
|
||||
func loadConfigEnvs() (*DbConfig, error) {
|
||||
var err error
|
||||
|
||||
log.Infof("Loading configs from environment variables")
|
||||
|
||||
loadDotEnvs()
|
||||
|
||||
dbConn := utils.Getenv("DB_CONN", "").(string)
|
||||
dbHost := utils.Getenv("DB_HOST", "").(string)
|
||||
dbUser := utils.Getenv("DB_USER", "").(string)
|
||||
dbPass := utils.Getenv("DB_PASS", "").(string)
|
||||
dbData := utils.Getenv("DB_DATABASE", "").(string)
|
||||
dbPort := utils.Getenv("DB_PORT", defaultPort(dbConn)).(int)
|
||||
name := utils.Getenv("NAME", "Statping").(string)
|
||||
desc := utils.Getenv("DESCRIPTION", "Statping Monitoring Sample Data").(string)
|
||||
user := utils.Getenv("ADMIN_USER", "admin").(string)
|
||||
password := utils.Getenv("ADMIN_PASS", "admin").(string)
|
||||
domain := utils.Getenv("DOMAIN", "").(string)
|
||||
sqlFile := utils.Getenv("SQL_FILE", "").(string)
|
||||
|
||||
if dbConn != "" && dbConn != "sqlite" {
|
||||
if dbHost == "" {
|
||||
return nil, errors.New("Missing DB_HOST environment variable")
|
||||
}
|
||||
if dbPort == 0 {
|
||||
return nil, errors.New("Missing DB_PORT environment variable")
|
||||
}
|
||||
if dbUser == "" {
|
||||
return nil, errors.New("Missing DB_USER environment variable")
|
||||
}
|
||||
if dbPass == "" {
|
||||
return nil, errors.New("Missing DB_PASS environment variable")
|
||||
}
|
||||
if dbData == "" {
|
||||
return nil, errors.New("Missing DB_DATABASE environment variable")
|
||||
}
|
||||
}
|
||||
|
||||
config := &DbConfig{
|
||||
DbConn: dbConn,
|
||||
DbHost: dbHost,
|
||||
DbUser: dbUser,
|
||||
DbPass: dbPass,
|
||||
DbData: dbData,
|
||||
DbPort: dbPort,
|
||||
Project: name,
|
||||
Description: desc,
|
||||
Domain: domain,
|
||||
Email: "",
|
||||
Username: user,
|
||||
Password: password,
|
||||
Error: nil,
|
||||
Location: utils.Directory,
|
||||
SqlFile: sqlFile,
|
||||
}
|
||||
return config, err
|
||||
}
|
||||
|
||||
// loadDotEnvs attempts to load database configs from a '.env' file in root directory
|
||||
func loadDotEnvs() {
|
||||
err := godotenv.Overload(utils.Directory + "/" + ".env")
|
||||
if err == nil {
|
||||
log.Warnln("Environment file '.env' found")
|
||||
envs, _ := godotenv.Read(utils.Directory + "/" + ".env")
|
||||
for k, e := range envs {
|
||||
log.Infof("Overwriting %s=%s\n", k, e)
|
||||
}
|
||||
log.Warnln("These environment variables will overwrite any existing")
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
package configs
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/utils"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func LoadConfigFile(directory string) (*DbConfig, error) {
|
||||
var configs *DbConfig
|
||||
|
||||
log.Infof("Attempting to read config file at: %s/config.yml ", directory)
|
||||
file, err := utils.OpenFile(directory + "/config.yml")
|
||||
if err != nil {
|
||||
core.App.Setup = false
|
||||
return nil, errors.Wrapf(err, "config.yml file not found at %s/config.yml - starting in setup mode", directory)
|
||||
}
|
||||
err = yaml.Unmarshal([]byte(file), &configs)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "yaml file not formatted correctly")
|
||||
}
|
||||
log.WithFields(utils.ToFields(configs)).Debugln("read config file: " + directory + "/config.yml")
|
||||
|
||||
return configs, nil
|
||||
}
|
|
@ -10,24 +10,35 @@ func LoadConfigForm(r *http.Request) (*DbConfig, error) {
|
|||
if err := r.ParseForm(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbHost := r.PostForm.Get("db_host")
|
||||
dbUser := r.PostForm.Get("db_user")
|
||||
dbPass := r.PostForm.Get("db_password")
|
||||
dbDatabase := r.PostForm.Get("db_database")
|
||||
dbConn := r.PostForm.Get("db_connection")
|
||||
dbPort := utils.ToInt(r.PostForm.Get("db_port"))
|
||||
project := r.PostForm.Get("project")
|
||||
username := r.PostForm.Get("username")
|
||||
password := r.PostForm.Get("password")
|
||||
description := r.PostForm.Get("description")
|
||||
domain := r.PostForm.Get("domain")
|
||||
email := r.PostForm.Get("email")
|
||||
g := r.PostForm.Get
|
||||
dbHost := g("db_host")
|
||||
dbUser := g("db_user")
|
||||
dbPass := g("db_password")
|
||||
dbDatabase := g("db_database")
|
||||
dbConn := g("db_connection")
|
||||
dbPort := utils.ToInt(g("db_port"))
|
||||
project := g("project")
|
||||
username := g("username")
|
||||
password := g("password")
|
||||
description := g("description")
|
||||
domain := g("domain")
|
||||
email := g("email")
|
||||
|
||||
if project == "" || username == "" || password == "" {
|
||||
err := errors.New("Missing required elements on setup form")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p := utils.Params
|
||||
p.Set("DB_CONN", dbConn)
|
||||
p.Set("DB_HOST", dbHost)
|
||||
p.Set("DB_USER", dbUser)
|
||||
p.Set("DB_PORT", dbPort)
|
||||
p.Set("DB_PASS", dbPass)
|
||||
p.Set("DB_DATABASE", dbDatabase)
|
||||
p.Set("NAME", project)
|
||||
p.Set("DESCRIPTION", description)
|
||||
|
||||
confg := &DbConfig{
|
||||
DbConn: dbConn,
|
||||
DbHost: dbHost,
|
||||
|
@ -41,7 +52,6 @@ func LoadConfigForm(r *http.Request) (*DbConfig, error) {
|
|||
Username: username,
|
||||
Password: password,
|
||||
Email: email,
|
||||
Error: nil,
|
||||
Location: utils.Directory,
|
||||
}
|
||||
|
||||
|
|
|
@ -17,45 +17,20 @@ import (
|
|||
"github.com/statping/statping/types/services"
|
||||
"github.com/statping/statping/types/users"
|
||||
"github.com/statping/statping/utils"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Connect will attempt to connect to the sqlite, postgres, or mysql database
|
||||
func Connect(configs *DbConfig, retry bool) error {
|
||||
postgresSSL := os.Getenv("POSTGRES_SSLMODE")
|
||||
var conn string
|
||||
conn := configs.ConnectionString()
|
||||
p := utils.Params
|
||||
var err error
|
||||
|
||||
switch configs.DbConn {
|
||||
case "sqlite", "sqlite3", "memory":
|
||||
if configs.DbConn == "memory" {
|
||||
conn = "sqlite3"
|
||||
configs.DbConn = ":memory:"
|
||||
} else {
|
||||
conn = findDbFile(configs)
|
||||
configs.SqlFile = conn
|
||||
log.Infof("SQL database file at: %s", configs.SqlFile)
|
||||
configs.DbConn = "sqlite3"
|
||||
}
|
||||
case "mysql":
|
||||
host := fmt.Sprintf("%v:%v", configs.DbHost, configs.DbPort)
|
||||
conn = fmt.Sprintf("%v:%v@tcp(%v)/%v?charset=utf8&parseTime=True&loc=UTC&time_zone=%%27UTC%%27", configs.DbUser, configs.DbPass, host, configs.DbData)
|
||||
case "postgres":
|
||||
sslMode := "disable"
|
||||
if postgresSSL != "" {
|
||||
sslMode = postgresSSL
|
||||
}
|
||||
conn = fmt.Sprintf("host=%v port=%v user=%v dbname=%v password=%v timezone=UTC sslmode=%v", configs.DbHost, configs.DbPort, configs.DbUser, configs.DbData, configs.DbPass, sslMode)
|
||||
case "mssql":
|
||||
host := fmt.Sprintf("%v:%v", configs.DbHost, configs.DbPort)
|
||||
conn = fmt.Sprintf("sqlserver://%v:%v@%v?database=%v", configs.DbUser, configs.DbPass, host, configs.DbData)
|
||||
}
|
||||
log.WithFields(utils.ToFields(configs, conn)).Debugln("attempting to connect to database")
|
||||
|
||||
dbSession, err := database.Openw(configs.DbConn, conn)
|
||||
if err != nil {
|
||||
log.Debugln(fmt.Sprintf("Database connection error %s", err))
|
||||
log.Errorf(fmt.Sprintf("Database connection error %s", err))
|
||||
if retry {
|
||||
log.Warnln(fmt.Sprintf("Database %s connection to '%s' is not available, trying again in 5 seconds...", configs.DbConn, configs.DbHost))
|
||||
time.Sleep(5 * time.Second)
|
||||
|
@ -65,20 +40,20 @@ func Connect(configs *DbConfig, retry bool) error {
|
|||
}
|
||||
}
|
||||
|
||||
apiKey := utils.Getenv("API_KEY", utils.RandomString(16)).(string)
|
||||
apiSecret := utils.Getenv("API_SECRET", utils.RandomString(16)).(string)
|
||||
apiKey := p.GetString("API_KEY")
|
||||
apiSecret := p.GetString("API_SECRET")
|
||||
configs.ApiKey = apiKey
|
||||
configs.ApiSecret = apiSecret
|
||||
|
||||
log.WithFields(utils.ToFields(dbSession)).Debugln("connected to database")
|
||||
|
||||
maxOpenConn := utils.Getenv("MAX_OPEN_CONN", 25)
|
||||
maxIdleConn := utils.Getenv("MAX_IDLE_CONN", 25)
|
||||
maxLifeConn := utils.Getenv("MAX_LIFE_CONN", 5*time.Minute)
|
||||
maxOpenConn := p.GetInt("MAX_OPEN_CONN")
|
||||
maxIdleConn := p.GetInt("MAX_IDLE_CONN")
|
||||
maxLifeConn := p.GetDuration("MAX_LIFE_CONN")
|
||||
|
||||
dbSession.DB().SetMaxOpenConns(maxOpenConn.(int))
|
||||
dbSession.DB().SetMaxIdleConns(maxIdleConn.(int))
|
||||
dbSession.DB().SetConnMaxLifetime(maxLifeConn.(time.Duration))
|
||||
dbSession.DB().SetMaxOpenConns(maxOpenConn)
|
||||
dbSession.DB().SetMaxIdleConns(maxIdleConn)
|
||||
dbSession.DB().SetConnMaxLifetime(maxLifeConn)
|
||||
|
||||
if dbSession.DB().Ping() == nil {
|
||||
if utils.VerboseMode >= 4 {
|
||||
|
@ -107,17 +82,20 @@ func initModels(db database.Database) {
|
|||
groups.SetDB(db)
|
||||
}
|
||||
|
||||
func CreateAdminUser(configs *DbConfig) error {
|
||||
log.Infoln(fmt.Sprintf("Core database does not exist, creating now!"))
|
||||
func CreateAdminUser(c *DbConfig) error {
|
||||
log.Infoln(fmt.Sprintf("Default Admininstrator user does not exist, creating now! (admin/admin)"))
|
||||
|
||||
if configs.Username == "" && configs.Password == "" {
|
||||
configs.Username = utils.Getenv("ADMIN_USER", "admin").(string)
|
||||
configs.Password = utils.Getenv("ADMIN_PASSWORD", "admin").(string)
|
||||
adminUser := utils.Params.GetString("ADMIN_USER")
|
||||
adminPass := utils.Params.GetString("ADMIN_PASSWORD")
|
||||
|
||||
if adminUser == "" || adminPass == "" {
|
||||
adminUser = "admin"
|
||||
adminPass = "admin"
|
||||
}
|
||||
|
||||
admin := &users.User{
|
||||
Username: configs.Username,
|
||||
Password: configs.Password,
|
||||
Username: adminUser,
|
||||
Password: adminPass,
|
||||
Email: "info@admin.com",
|
||||
Admin: null.NewNullBool(true),
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package configs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/checkins"
|
||||
"github.com/statping/statping/types/core"
|
||||
|
@ -101,11 +103,11 @@ func (d *DbConfig) CreateDatabase() error {
|
|||
log.Infoln("Creating Database Tables...")
|
||||
for _, table := range DbModels {
|
||||
if err := d.Db.CreateTable(table); err.Error() != nil {
|
||||
return err.Error()
|
||||
return errors.Wrap(err.Error(), fmt.Sprintf("error creating '%T' table", table))
|
||||
}
|
||||
}
|
||||
if err := d.Db.Table("core").CreateTable(&core.Core{}); err.Error() != nil {
|
||||
return err.Error()
|
||||
return errors.Wrap(err.Error(), fmt.Sprintf("error creating 'core' table"))
|
||||
}
|
||||
log.Infoln("Statping Database Created")
|
||||
|
||||
|
|
|
@ -1,24 +1,23 @@
|
|||
package configs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/statping/statping/utils"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var log = utils.Log
|
||||
|
||||
func ConnectConfigs(configs *DbConfig) error {
|
||||
err := Connect(configs, true)
|
||||
func ConnectConfigs(configs *DbConfig, retry bool) error {
|
||||
err := Connect(configs, retry)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error connecting to database")
|
||||
}
|
||||
if err := configs.Save(utils.Directory); err != nil {
|
||||
return errors.Wrap(err, "error saving configuration")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -31,42 +30,43 @@ func LoadConfigs() (*DbConfig, error) {
|
|||
return nil, errors.Errorf("Directory %s is not writable!", utils.Directory)
|
||||
}
|
||||
|
||||
dbConn := utils.Getenv("DB_CONN", "").(string)
|
||||
if dbConn != "" {
|
||||
configs, err := loadConfigEnvs()
|
||||
if err != nil {
|
||||
return LoadConfigFile(utils.Directory)
|
||||
}
|
||||
return configs, nil
|
||||
}
|
||||
|
||||
return LoadConfigFile(utils.Directory)
|
||||
}
|
||||
|
||||
func findDbFile(configs *DbConfig) string {
|
||||
func findDbFile(configs *DbConfig) (string, error) {
|
||||
location := utils.Directory + "/" + SqliteFilename
|
||||
if configs == nil {
|
||||
return findSQLin(utils.Directory)
|
||||
file, err := findSQLin(utils.Directory)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return location, nil
|
||||
}
|
||||
if configs.SqlFile != "" {
|
||||
return configs.SqlFile
|
||||
location = file
|
||||
}
|
||||
return utils.Directory + "/" + SqliteFilename
|
||||
if configs != nil && configs.SqlFile != "" {
|
||||
return configs.SqlFile, nil
|
||||
}
|
||||
return location, nil
|
||||
}
|
||||
|
||||
func findSQLin(path string) string {
|
||||
func findSQLin(path string) (string, error) {
|
||||
filename := SqliteFilename
|
||||
var found []string
|
||||
err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
if filepath.Ext(path) == ".db" {
|
||||
fmt.Println("DB file is now: ", info.Name())
|
||||
filename = info.Name()
|
||||
found = append(found, filename)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return filename, err
|
||||
}
|
||||
return filename
|
||||
if len(found) > 1 {
|
||||
return filename, errors.Errorf("found multiple database files: %s", strings.Join(found, ", "))
|
||||
}
|
||||
return filename, nil
|
||||
}
|
||||
|
|
|
@ -12,58 +12,58 @@ func init() {
|
|||
os.Setenv("MIGRATION_ID", utils.ToString(latestMigration))
|
||||
}
|
||||
|
||||
func (c *DbConfig) genericMigration(alterStr string, isPostgres bool) error {
|
||||
func (d *DbConfig) genericMigration(alterStr string, isPostgres bool) error {
|
||||
var extra string
|
||||
extraType := "UNSIGNED INTEGER"
|
||||
if isPostgres {
|
||||
extra = " TYPE"
|
||||
extraType = "bigint"
|
||||
}
|
||||
if err := c.Db.Exec(fmt.Sprintf("ALTER TABLE hits %s COLUMN latency%s BIGINT;", alterStr, extra)).Error(); err != nil {
|
||||
if err := d.Db.Exec(fmt.Sprintf("ALTER TABLE hits %s COLUMN latency%s BIGINT;", alterStr, extra)).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(fmt.Sprintf("ALTER TABLE hits %s COLUMN ping_time%s BIGINT;", alterStr, extra)).Error(); err != nil {
|
||||
if err := d.Db.Exec(fmt.Sprintf("ALTER TABLE hits %s COLUMN ping_time%s BIGINT;", alterStr, extra)).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(fmt.Sprintf("ALTER TABLE failures %s COLUMN ping_time%s BIGINT;", alterStr, extra)).Error(); err != nil {
|
||||
if err := d.Db.Exec(fmt.Sprintf("ALTER TABLE failures %s COLUMN ping_time%s BIGINT;", alterStr, extra)).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(fmt.Sprintf("UPDATE hits SET latency = CAST(latency * 1000000 AS %s);", extraType)).Error(); err != nil {
|
||||
if err := d.Db.Exec(fmt.Sprintf("UPDATE hits SET latency = CAST(latency * 1000000 AS %s);", extraType)).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(fmt.Sprintf("UPDATE hits SET ping_time = CAST(ping_time * 1000000 AS %s);", extraType)).Error(); err != nil {
|
||||
if err := d.Db.Exec(fmt.Sprintf("UPDATE hits SET ping_time = CAST(ping_time * 1000000 AS %s);", extraType)).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(fmt.Sprintf("UPDATE failures SET ping_time = CAST(ping_time * 1000000 AS %s);", extraType)).Error(); err != nil {
|
||||
if err := d.Db.Exec(fmt.Sprintf("UPDATE failures SET ping_time = CAST(ping_time * 1000000 AS %s);", extraType)).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *DbConfig) sqliteMigration() error {
|
||||
if err := c.Db.Exec(`ALTER TABLE hits RENAME TO hits_backup;`).Error(); err != nil {
|
||||
func (d *DbConfig) sqliteMigration() error {
|
||||
if err := d.Db.Exec(`ALTER TABLE hits RENAME TO hits_backup;`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(`CREATE TABLE hits (id INTEGER PRIMARY KEY AUTOINCREMENT, service bigint, latency bigint, ping_time bigint, created_at datetime);`).Error(); err != nil {
|
||||
if err := d.Db.Exec(`CREATE TABLE hits (id INTEGER PRIMARY KEY AUTOINCREMENT, service bigint, latency bigint, ping_time bigint, created_at datetime);`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(`INSERT INTO hits (id, service, latency, ping_time, created_at) SELECT id, service, CAST(latency * 1000000 AS bigint), CAST(ping_time * 1000000 AS bigint), created_at FROM hits_backup;`).Error(); err != nil {
|
||||
if err := d.Db.Exec(`INSERT INTO hits (id, service, latency, ping_time, created_at) SELECT id, service, CAST(latency * 1000000 AS bigint), CAST(ping_time * 1000000 AS bigint), created_at FROM hits_backup;`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
// failures table
|
||||
if err := c.Db.Exec(`ALTER TABLE failures RENAME TO failures_backup;`).Error(); err != nil {
|
||||
if err := d.Db.Exec(`ALTER TABLE failures RENAME TO failures_backup;`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(`CREATE TABLE failures (id INTEGER PRIMARY KEY AUTOINCREMENT, issue varchar(255), method varchar(255), method_id bigint, service bigint, ping_time bigint, checkin bigint, error_code bigint, created_at datetime);`).Error(); err != nil {
|
||||
if err := d.Db.Exec(`CREATE TABLE failures (id INTEGER PRIMARY KEY AUTOINCREMENT, issue varchar(255), method varchar(255), method_id bigint, service bigint, ping_time bigint, checkin bigint, error_code bigint, created_at datetime);`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(`INSERT INTO failures (id, issue, method, method_id, service, ping_time, checkin, created_at) SELECT id, issue, method, method_id, service, CAST(ping_time * 1000000 AS bigint), checkin, created_at FROM failures_backup;`).Error(); err != nil {
|
||||
if err := d.Db.Exec(`INSERT INTO failures (id, issue, method, method_id, service, ping_time, checkin, created_at) SELECT id, issue, method, method_id, service, CAST(ping_time * 1000000 AS bigint), checkin, created_at FROM failures_backup;`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(`DROP TABLE hits_backup;`).Error(); err != nil {
|
||||
if err := d.Db.Exec(`DROP TABLE hits_backup;`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(`DROP TABLE failures_backup;`).Error(); err != nil {
|
||||
if err := d.Db.Exec(`DROP TABLE failures_backup;`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package configs
|
||||
|
||||
import (
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/utils"
|
||||
"gopkg.in/yaml.v2"
|
||||
"os"
|
||||
)
|
||||
|
||||
func LoadConfigFile(directory string) (*DbConfig, error) {
|
||||
p := utils.Params
|
||||
log.Infof("Attempting to read config file at: %s/config.yml ", directory)
|
||||
p.SetConfigFile(directory + "/config.yml")
|
||||
p.SetConfigType("yaml")
|
||||
p.ReadInConfig()
|
||||
|
||||
db := new(DbConfig)
|
||||
content, err := utils.OpenFile(directory + "/config.yml")
|
||||
if err == nil {
|
||||
if err := yaml.Unmarshal([]byte(content), &db); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if os.Getenv("DB_CONN") == "sqlite" || os.Getenv("DB_CONN") == "sqlite3" {
|
||||
db.DbConn = "sqlite3"
|
||||
}
|
||||
if db.DbConn != "" {
|
||||
p.Set("DB_CONN", db.DbConn)
|
||||
}
|
||||
if db.DbHost != "" {
|
||||
p.Set("DB_HOST", db.DbHost)
|
||||
}
|
||||
if db.DbPort != 0 {
|
||||
p.Set("DB_PORT", db.DbPort)
|
||||
}
|
||||
if db.DbPass != "" {
|
||||
p.Set("DB_PASS", db.DbPass)
|
||||
}
|
||||
if db.DbUser != "" {
|
||||
p.Set("DB_USER", db.DbUser)
|
||||
}
|
||||
if db.DbData != "" {
|
||||
p.Set("DB_DATABASE", db.DbData)
|
||||
}
|
||||
if db.Location != "" {
|
||||
p.Set("LOCATION", db.Location)
|
||||
}
|
||||
if db.ApiKey != "" {
|
||||
p.Set("API_KEY", db.ApiKey)
|
||||
}
|
||||
if db.ApiSecret != "" {
|
||||
p.Set("API_SECRET", db.ApiSecret)
|
||||
}
|
||||
|
||||
configs := &DbConfig{
|
||||
DbConn: p.GetString("DB_CONN"),
|
||||
DbHost: p.GetString("DB_HOST"),
|
||||
DbUser: p.GetString("DB_USER"),
|
||||
DbPass: p.GetString("DB_PASS"),
|
||||
DbData: p.GetString("DB_DATABASE"),
|
||||
DbPort: p.GetInt("DB_PORT"),
|
||||
Project: p.GetString("NAME"),
|
||||
Description: p.GetString("DESCRIPTION"),
|
||||
Domain: p.GetString("DOMAIN"),
|
||||
Email: p.GetString("EMAIL"),
|
||||
Username: p.GetString("ADMIN_USER"),
|
||||
Password: p.GetString("ADMIN_PASS"),
|
||||
Location: utils.Directory,
|
||||
SqlFile: p.GetString("SQL_FILE"),
|
||||
}
|
||||
log.WithFields(utils.ToFields(configs)).Debugln("read config file: " + directory + "/config.yml")
|
||||
|
||||
if configs.DbConn == "" {
|
||||
return configs, errors.New("Starting in setup mode")
|
||||
}
|
||||
return configs, nil
|
||||
}
|
|
@ -1,34 +1,48 @@
|
|||
package configs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/statping/statping/utils"
|
||||
"gopkg.in/yaml.v2"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Save will initially create the config.yml file
|
||||
func (d *DbConfig) Save(directory string) error {
|
||||
data, err := yaml.Marshal(d)
|
||||
c, err := yaml.Marshal(d)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(directory+"/config.yml", data, os.ModePerm); err != nil {
|
||||
return err
|
||||
if err := utils.SaveFile(directory+"/config.yml", c); err != nil {
|
||||
return nil
|
||||
}
|
||||
d.filename = directory + "/config.yml"
|
||||
return nil
|
||||
}
|
||||
|
||||
// defaultPort accepts a database type and returns its default port
|
||||
func defaultPort(db string) int {
|
||||
switch db {
|
||||
func (d *DbConfig) ConnectionString() string {
|
||||
var conn string
|
||||
postgresSSL := utils.Params.GetString("POSTGRES_SSLMODE")
|
||||
|
||||
switch d.DbConn {
|
||||
case "memory", ":memory:":
|
||||
conn = "sqlite3"
|
||||
d.DbConn = ":memory:"
|
||||
return d.DbConn
|
||||
case "sqlite", "sqlite3":
|
||||
conn, err := findDbFile(d)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
d.SqlFile = conn
|
||||
log.Infof("SQL database file at: %s", d.SqlFile)
|
||||
d.DbConn = "sqlite3"
|
||||
return d.SqlFile
|
||||
case "mysql":
|
||||
return 3306
|
||||
host := fmt.Sprintf("%v:%v", d.DbHost, d.DbPort)
|
||||
conn = fmt.Sprintf("%v:%v@tcp(%v)/%v?charset=utf8&parseTime=True&loc=UTC&time_zone=%%27UTC%%27", d.DbUser, d.DbPass, host, d.DbData)
|
||||
return conn
|
||||
case "postgres":
|
||||
return 5432
|
||||
case "mssql":
|
||||
return 1433
|
||||
default:
|
||||
return 0
|
||||
conn = fmt.Sprintf("host=%v port=%v user=%v dbname=%v password=%v timezone=UTC sslmode=%v", d.DbHost, d.DbPort, d.DbUser, d.DbData, d.DbPass, postgresSSL)
|
||||
return conn
|
||||
}
|
||||
return conn
|
||||
}
|
||||
|
|
|
@ -20,33 +20,33 @@ import (
|
|||
"github.com/statping/statping/types/users"
|
||||
)
|
||||
|
||||
func (c *DbConfig) DatabaseChanges() error {
|
||||
func (d *DbConfig) DatabaseChanges() error {
|
||||
var cr core.Core
|
||||
c.Db.Model(&core.Core{}).Find(&cr)
|
||||
d.Db.Model(&core.Core{}).Find(&cr)
|
||||
|
||||
if latestMigration > cr.MigrationId {
|
||||
log.Infof("Statping database is out of date, migrating to: %d", latestMigration)
|
||||
|
||||
switch c.Db.DbType() {
|
||||
switch d.Db.DbType() {
|
||||
case "mysql":
|
||||
if err := c.genericMigration("MODIFY", false); err != nil {
|
||||
if err := d.genericMigration("MODIFY", false); err != nil {
|
||||
return err
|
||||
}
|
||||
case "postgres":
|
||||
if err := c.genericMigration("ALTER", true); err != nil {
|
||||
if err := d.genericMigration("ALTER", true); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if err := c.sqliteMigration(); err != nil {
|
||||
if err := d.sqliteMigration(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := c.Db.Exec(fmt.Sprintf("UPDATE core SET migration_id = %d", latestMigration)).Error(); err != nil {
|
||||
if err := d.Db.Exec(fmt.Sprintf("UPDATE core SET migration_id = %d", latestMigration)).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := c.BackupAssets(); err != nil {
|
||||
if err := d.BackupAssets(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ func (c *DbConfig) DatabaseChanges() error {
|
|||
|
||||
// BackupAssets is a temporary function (to version 0.90.*) to backup your customized theme
|
||||
// to a new folder called 'assets_backup'.
|
||||
func (c *DbConfig) BackupAssets() error {
|
||||
func (d *DbConfig) BackupAssets() error {
|
||||
if source.UsingAssets(utils.Directory) {
|
||||
log.Infof("Backing up 'assets' folder to 'assets_backup'")
|
||||
if err := utils.RenameDirectory(utils.Directory+"/assets", utils.Directory+"/assets_backup"); err != nil {
|
||||
|
@ -69,12 +69,12 @@ func (c *DbConfig) BackupAssets() error {
|
|||
//MigrateDatabase will migrate the database structure to current version.
|
||||
//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 (c *DbConfig) MigrateDatabase() error {
|
||||
func (d *DbConfig) MigrateDatabase() error {
|
||||
|
||||
var DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, ¬ifications.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}}
|
||||
|
||||
log.Infoln("Migrating Database Tables...")
|
||||
tx := c.Db.Begin()
|
||||
tx := d.Db.Begin()
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
tx.Rollback()
|
||||
|
@ -99,27 +99,27 @@ func (c *DbConfig) MigrateDatabase() error {
|
|||
return err
|
||||
}
|
||||
|
||||
c.Db.Table("core").Model(&core.Core{}).Update("version", core.App.Version)
|
||||
d.Db.Table("core").Model(&core.Core{}).Update("version", core.App.Version)
|
||||
|
||||
log.Infoln("Statping Database Tables Migrated")
|
||||
|
||||
if err := c.Db.Model(&hits.Hit{}).AddIndex("idx_service_hit", "service").Error(); err != nil {
|
||||
if err := d.Db.Model(&hits.Hit{}).AddIndex("idx_service_hit", "service").Error(); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
|
||||
if err := c.Db.Model(&hits.Hit{}).AddIndex("hit_created_at", "created_at").Error(); err != nil {
|
||||
if err := d.Db.Model(&hits.Hit{}).AddIndex("hit_created_at", "created_at").Error(); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
|
||||
if err := c.Db.Model(&failures.Failure{}).AddIndex("fail_created_at", "created_at").Error(); err != nil {
|
||||
if err := d.Db.Model(&failures.Failure{}).AddIndex("fail_created_at", "created_at").Error(); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
|
||||
if err := c.Db.Model(&failures.Failure{}).AddIndex("idx_service_fail", "service").Error(); err != nil {
|
||||
if err := d.Db.Model(&failures.Failure{}).AddIndex("idx_service_fail", "service").Error(); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
|
||||
if err := c.Db.Model(&failures.Failure{}).AddIndex("idx_checkin_fail", "checkin").Error(); err != nil {
|
||||
if err := d.Db.Model(&failures.Failure{}).AddIndex("idx_checkin_fail", "checkin").Error(); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
log.Infoln("Database Indexes Created")
|
||||
|
|
|
@ -5,8 +5,6 @@ import (
|
|||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/null"
|
||||
"github.com/statping/statping/utils"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
var db database.Database
|
||||
|
@ -31,32 +29,29 @@ func Select() (*Core, error) {
|
|||
}
|
||||
App = &c
|
||||
|
||||
if os.Getenv("USE_CDN") == "true" {
|
||||
if utils.Params.GetBool("USE_CDN") {
|
||||
App.UseCdn = null.NewNullBool(true)
|
||||
}
|
||||
if os.Getenv("ALLOW_REPORTS") == "true" {
|
||||
if utils.Params.GetBool("ALLOW_REPORTS") {
|
||||
App.AllowReports = null.NewNullBool(true)
|
||||
}
|
||||
return App, q.Error()
|
||||
}
|
||||
|
||||
func (c *Core) Create() error {
|
||||
apiKey := utils.Getenv("API_KEY", utils.NewSHA256Hash()).(string)
|
||||
apiSecret := utils.Getenv("API_SECRET", utils.NewSHA256Hash()).(string)
|
||||
|
||||
if c.ApiKey == "" || c.ApiSecret == "" {
|
||||
c.ApiSecret = apiSecret
|
||||
c.ApiKey = apiKey
|
||||
secret := utils.Params.GetString("API_SECRET")
|
||||
if secret == "" {
|
||||
secret = utils.RandomString(32)
|
||||
}
|
||||
newCore := &Core{
|
||||
Name: c.Name,
|
||||
Description: c.Description,
|
||||
ConfigFile: utils.Directory + "/config.yml",
|
||||
ApiKey: c.ApiKey,
|
||||
ApiSecret: c.ApiSecret,
|
||||
ApiKey: utils.RandomString(32),
|
||||
ApiSecret: secret,
|
||||
Version: App.Version,
|
||||
Domain: c.Domain,
|
||||
MigrationId: time.Now().Unix(),
|
||||
MigrationId: utils.Now().Unix(),
|
||||
}
|
||||
q := db.Create(&newCore)
|
||||
return q.Error()
|
||||
|
|
|
@ -6,14 +6,19 @@ import (
|
|||
)
|
||||
|
||||
func Samples() error {
|
||||
apiKey := utils.Getenv("API_KEY", utils.RandomString(16))
|
||||
apiSecret := utils.Getenv("API_SECRET", utils.RandomString(16))
|
||||
apiKey := utils.Params.GetString("API_KEY")
|
||||
apiSecret := utils.Params.GetString("API_SECRET")
|
||||
|
||||
if apiKey == "" || apiSecret == "" {
|
||||
apiKey = utils.RandomString(32)
|
||||
apiSecret = utils.RandomString(32)
|
||||
}
|
||||
|
||||
core := &Core{
|
||||
Name: "Statping Sample Data",
|
||||
Description: "This data is only used to testing",
|
||||
ApiKey: apiKey.(string),
|
||||
ApiSecret: apiSecret.(string),
|
||||
ApiKey: apiKey,
|
||||
ApiSecret: apiSecret,
|
||||
Domain: "http://localhost:8080",
|
||||
CreatedAt: utils.Now(),
|
||||
UseCdn: null.NewNullBool(false),
|
||||
|
|
|
@ -34,26 +34,26 @@ type Core struct {
|
|||
UseCdn null.NullBool `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"`
|
||||
LoggedIn bool `gorm:"-" json:"logged_in"`
|
||||
IsAdmin bool `gorm:"-" json:"admin"`
|
||||
AllowReports null.NullBool `gorm:"column:allow_reports;default:false" json:"allow_reports"`
|
||||
AllowReports null.NullBool `gorm:"column:allow_reports;default:false" json:"allow_reports,omitempty"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
Started time.Time `gorm:"-" json:"started_on"`
|
||||
Notifications []AllNotifiers `gorm:"-" json:"-"`
|
||||
Integrations []Integrator `gorm:"-" json:"-"`
|
||||
|
||||
OAuth `json:"oauth"`
|
||||
OAuth `json:"-"`
|
||||
}
|
||||
|
||||
type OAuth struct {
|
||||
Domains string `gorm:"column:oauth_domains" json:"oauth_domains,omitempty" scope:"admin"`
|
||||
Providers string `gorm:"column:oauth_providers;default:local" json:"oauth_providers,omitempty"`
|
||||
GithubClientID string `gorm:"column:gh_client_id" json:"gh_client_id,omitempty" scope:"admin"`
|
||||
GithubClientSecret string `gorm:"column:gh_client_secret" json:"gh_client_secret,omitempty" scope:"admin"`
|
||||
GoogleClientID string `gorm:"column:google_client_id" json:"google_client_id,omitempty" scope:"admin"`
|
||||
GoogleClientSecret string `gorm:"column:google_client_secret" json:"google_client_secret,omitempty" scope:"admin"`
|
||||
SlackClientID string `gorm:"column:slack_client_id" json:"slack_client_id,omitempty" scope:"admin"`
|
||||
SlackClientSecret string `gorm:"column:slack_client_secret" json:"slack_client_secret,omitempty" scope:"admin"`
|
||||
SlackTeam string `gorm:"column:slack_team" json:"slack_team,omitempty" scope:"admin"`
|
||||
Domains string `gorm:"column:oauth_domains" json:"oauth_domains" scope:"admin"`
|
||||
Providers string `gorm:"column:oauth_providers;" json:"oauth_providers"`
|
||||
GithubClientID string `gorm:"column:gh_client_id" json:"gh_client_id"`
|
||||
GithubClientSecret string `gorm:"column:gh_client_secret" json:"gh_client_secret" scope:"admin"`
|
||||
GoogleClientID string `gorm:"column:google_client_id" json:"google_client_id"`
|
||||
GoogleClientSecret string `gorm:"column:google_client_secret" json:"google_client_secret" scope:"admin"`
|
||||
SlackClientID string `gorm:"column:slack_client_id" json:"slack_client_id"`
|
||||
SlackClientSecret string `gorm:"column:slack_client_secret" json:"slack_client_secret" scope:"admin"`
|
||||
SlackTeam string `gorm:"column:slack_team" json:"slack_team"`
|
||||
}
|
||||
|
||||
// AllNotifiers contains all the Notifiers loaded
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
NotAuthenticated = &appError{
|
||||
Err: "user not authenticated",
|
||||
Code: 401,
|
||||
}
|
||||
DecodeJSON = &appError{
|
||||
Err: "could not decode incoming JSON",
|
||||
Code: 422,
|
||||
}
|
||||
IDMissing = &appError{
|
||||
Err: "ID missing in URL",
|
||||
Code: 422,
|
||||
}
|
||||
NotNumber = &appError{
|
||||
Err: "ID needs to be an integer",
|
||||
Code: 422,
|
||||
}
|
||||
)
|
||||
|
||||
func Missing(object interface{}, id interface{}) error {
|
||||
outErr := fmt.Errorf("%s with id %v was not found", splitVar(object), id)
|
||||
return &appError{
|
||||
Err: outErr.Error(),
|
||||
Code: 404,
|
||||
}
|
||||
}
|
||||
|
||||
func splitVar(val interface{}) string {
|
||||
s := strings.Split(fmt.Sprintf("%T", val), ".")
|
||||
return strings.ToLower(s[len(s)-1])
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type appError struct {
|
||||
Err string `json:"error"`
|
||||
Code int `json:"-"`
|
||||
DbCode int `json:"code,omitempty"`
|
||||
Id int64 `json:"id,omitempty"`
|
||||
loggable bool `json:"-"`
|
||||
}
|
||||
|
||||
type Error interface {
|
||||
Error() string
|
||||
Status() int
|
||||
}
|
||||
|
||||
func New(err string) Error {
|
||||
return &appError{
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
|
||||
func Err(err Error) Error {
|
||||
return &appError{
|
||||
Err: err.Error(),
|
||||
Code: err.Status(),
|
||||
}
|
||||
}
|
||||
|
||||
func Wrap(err error, message string) Error {
|
||||
return &appError{
|
||||
Err: errors.Wrap(err, message).Error(),
|
||||
}
|
||||
}
|
||||
|
||||
func (e *appError) Error() string {
|
||||
return e.Err
|
||||
}
|
||||
|
||||
func (e appError) Status() int {
|
||||
if e.Code == 0 {
|
||||
return 200
|
||||
}
|
||||
return e.Code
|
||||
}
|
|
@ -5,10 +5,6 @@ import (
|
|||
"github.com/statping/statping/types"
|
||||
"github.com/statping/statping/utils"
|
||||
"time"
|
||||
|
||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -38,7 +34,6 @@ func Samples() error {
|
|||
log.Infoln(fmt.Sprintf("Adding %v Failure records to service", 400))
|
||||
|
||||
for fi := 0.; fi <= float64(400); fi++ {
|
||||
createdAt = createdAt.Add(35 * time.Minute)
|
||||
failure := &Failure{
|
||||
Service: i,
|
||||
Issue: "testing right here",
|
||||
|
@ -46,6 +41,7 @@ func Samples() error {
|
|||
}
|
||||
|
||||
tx = tx.Create(&failure)
|
||||
createdAt = createdAt.Add(35 * time.Minute)
|
||||
}
|
||||
if err := tx.Commit().Error(); err != nil {
|
||||
log.Error(err)
|
||||
|
|
|
@ -2,6 +2,7 @@ package groups
|
|||
|
||||
import (
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"sort"
|
||||
)
|
||||
|
||||
|
@ -14,6 +15,9 @@ func SetDB(database database.Database) {
|
|||
func Find(id int64) (*Group, error) {
|
||||
var group Group
|
||||
q := db.Where("id = ?", id).Find(&group)
|
||||
if q.Error() != nil {
|
||||
return nil, errors.Missing(group, id)
|
||||
}
|
||||
return &group, q.Error()
|
||||
}
|
||||
|
||||
|
|
|
@ -2,46 +2,40 @@ package hits
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types"
|
||||
"github.com/statping/statping/utils"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types"
|
||||
"github.com/statping/statping/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
var SampleHits = 99900.
|
||||
|
||||
func Samples() error {
|
||||
tx := db.Begin()
|
||||
sg := new(sync.WaitGroup)
|
||||
|
||||
for i := int64(1); i <= 5; i++ {
|
||||
err := createHitsAt(tx, i, sg)
|
||||
if err != nil {
|
||||
tx := db.Begin()
|
||||
tx = createHitsAt(tx, i)
|
||||
|
||||
if err := tx.Commit().Error(); err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
tx = db.Begin()
|
||||
}
|
||||
|
||||
return tx.Error()
|
||||
return nil
|
||||
}
|
||||
|
||||
func createHitsAt(db database.Database, serviceID int64, sg *sync.WaitGroup) error {
|
||||
log.Infoln(fmt.Sprintf("Adding sample hit records to service #%d", serviceID))
|
||||
func createHitsAt(db database.Database, serviceID int64) database.Database {
|
||||
log.Infoln(fmt.Sprintf("Adding Sample records to service #%d...", serviceID))
|
||||
|
||||
createdAt := utils.Now().Add(-3 * types.Day)
|
||||
p := utils.NewPerlin(2, 2, 5, utils.Now().UnixNano())
|
||||
|
||||
i := 0
|
||||
for hi := 0.; hi <= SampleHits; hi++ {
|
||||
latency := p.Noise1D(hi / 500)
|
||||
|
||||
createdAt = createdAt.Add(30 * time.Second)
|
||||
|
||||
hit := &Hit{
|
||||
Service: serviceID,
|
||||
Latency: int64(latency * 10000000),
|
||||
|
@ -50,15 +44,12 @@ func createHitsAt(db database.Database, serviceID int64, sg *sync.WaitGroup) err
|
|||
}
|
||||
|
||||
db = db.Create(&hit)
|
||||
if err := db.Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
i++
|
||||
if createdAt.After(utils.Now()) {
|
||||
break
|
||||
}
|
||||
createdAt = createdAt.Add(30 * time.Second)
|
||||
}
|
||||
|
||||
return db.Commit().Error()
|
||||
return db
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
package messages
|
||||
|
||||
import "github.com/statping/statping/database"
|
||||
import (
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/errors"
|
||||
)
|
||||
|
||||
var db database.Database
|
||||
|
||||
|
@ -11,6 +14,9 @@ func SetDB(database database.Database) {
|
|||
func Find(id int64) (*Message, error) {
|
||||
var message Message
|
||||
q := db.Where("id = ?", id).Find(&message)
|
||||
if q.Error() != nil {
|
||||
return nil, errors.Missing(message, id)
|
||||
}
|
||||
return &message, q.Error()
|
||||
}
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ type Notification struct {
|
|||
Var2 string `gorm:"not null;column:var2" json:"var2,omitempty"`
|
||||
ApiKey string `gorm:"not null;column:api_key" json:"api_key,omitempty"`
|
||||
ApiSecret string `gorm:"not null;column:api_secret" json:"api_secret,omitempty"`
|
||||
Enabled null.NullBool `gorm:"column:enabled;type:boolean;default:false" json:"enabled"`
|
||||
Enabled null.NullBool `gorm:"column:enabled;type:boolean;default:false" json:"enabled,omitempty"`
|
||||
Limits int `gorm:"not null;column:limits" json:"limits"`
|
||||
Removable bool `gorm:"column:removable" json:"removable"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/errors"
|
||||
"github.com/statping/statping/utils"
|
||||
"sort"
|
||||
)
|
||||
|
@ -20,7 +20,7 @@ func SetDB(database database.Database) {
|
|||
func Find(id int64) (*Service, error) {
|
||||
srv := allServices[id]
|
||||
if srv == nil {
|
||||
return nil, errors.New("service not found")
|
||||
return nil, errors.Missing(&Service{}, id)
|
||||
}
|
||||
return srv, nil
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ func findServiceByHash(hash string) *Service {
|
|||
}
|
||||
|
||||
func ServicesFromEnvFile() error {
|
||||
servicesEnv := utils.Getenv("SERVICES_FILE", "").(string)
|
||||
servicesEnv := utils.Params.GetString("SERVICES_FILE")
|
||||
if servicesEnv == "" {
|
||||
return nil
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue