mirror of https://github.com/statping/statping
Merge branch 'dev' into contributors
commit
bc2228e526
|
@ -2,6 +2,7 @@
|
|||
.github
|
||||
.idea
|
||||
logs
|
||||
certs
|
||||
*.log
|
||||
build
|
||||
assets
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
github: hunterlong
|
||||
patreon: statping
|
||||
custom: ['https://www.nfoservers.com/donate.pl?force_recipient=1&recipient=info%40socialeck.com', 'https://opencollective.com/statping', 'https://www.buymeacoffee.com/hunterlong']
|
||||
open_collective: statping
|
||||
custom: ['https://www.nfoservers.com/donate.pl?force_recipient=1&recipient=info%40socialeck.com', 'https://www.buymeacoffee.com/hunterlong']
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
name: Dev Release
|
||||
name: Development Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
|
@ -7,40 +7,46 @@ on:
|
|||
- '**.md'
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
|
||||
frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
go-version: 1.15.x
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
- uses: actions/checkout@v2
|
||||
node-version: 12.18.2
|
||||
|
||||
- name: Add GOBIN to PATH
|
||||
- name: Set Version
|
||||
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
|
||||
run: npm install -g yarn sass cross-env mjml
|
||||
|
||||
- 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
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
MJML_APP: ${{ secrets.MJML_APP }}
|
||||
MJML_PRIVATE: ${{ secrets.MJML_PRIVATE }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
run: make clean generate compile
|
||||
|
||||
- name: Upload Compiled Frontend (rice-box.go)
|
||||
uses: actions/upload-artifact@v1
|
||||
|
@ -48,10 +54,145 @@ jobs:
|
|||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
test:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
- name: Configure AWS credentials for Asset uploads
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-west-2
|
||||
|
||||
- name: Upload Static Assets to S3
|
||||
run: |
|
||||
tar -czvf source.tar.gz source/
|
||||
aws s3 cp source.tar.gz s3://assets.statping.com/commit/${{ github.sha }}/
|
||||
rm -rf source.tar.gz
|
||||
|
||||
build:
|
||||
needs: frontend
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [linux]
|
||||
arch: [386, amd64, arm-7, arm-6, arm64]
|
||||
include:
|
||||
- platform: darwin
|
||||
arch: amd64
|
||||
- platform: windows
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Download Compiled Frontend (rice-box.go)
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
- name: Configure AWS credentials for Asset uploads
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-west-2
|
||||
|
||||
- name: Add GOBIN to PATH
|
||||
run: |
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Set Linux Build Flags
|
||||
if: matrix.platform == 'linux'
|
||||
run: |
|
||||
echo ::set-env name=BUILD_FLAGS::'-extldflags -static'
|
||||
echo ::set-env name=XGO_TAGS::'netgo,osusergo,linux,sqlite_omit_load_extension'
|
||||
shell: bash
|
||||
|
||||
- name: Set Darwin Build Flags
|
||||
if: matrix.platform == 'darwin'
|
||||
run: echo ::set-env name=XGO_TAGS::'netgo,osusergo,darwin,sqlite_omit_load_extension'
|
||||
shell: bash
|
||||
|
||||
- name: Set Windows Build Flags
|
||||
if: matrix.platform == 'windows'
|
||||
run: |
|
||||
echo ::set-env name=BUILD_FLAGS::'-extldflags -static'
|
||||
echo ::set-env name=XGO_TAGS::'netgo,osusergo,sqlite_omit_load_extension'
|
||||
shell: bash
|
||||
|
||||
- name: Build ${{ matrix.platform }}/${{ matrix.arch }}
|
||||
uses: crazy-max/ghaction-xgo@v1
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
with:
|
||||
xgo_version: latest
|
||||
go_version: 1.15.x
|
||||
dest: build
|
||||
prefix: statping
|
||||
targets: ${{ matrix.platform }}/${{ matrix.arch }}
|
||||
v: false
|
||||
x: false
|
||||
pkg: cmd
|
||||
buildmode: pie
|
||||
tags: ${{ env.XGO_TAGS }}
|
||||
ldflags: -s -w -X main.VERSION=${{ env.VERSION }} -X main.COMMIT=${{ env.COMMIT }} ${{ env.BUILD_FLAGS }}
|
||||
|
||||
- name: Compress Linux Builds
|
||||
if: matrix.platform == 'linux'
|
||||
run: |
|
||||
cd build
|
||||
mv statping-linux-${{ matrix.arch }} statping
|
||||
chmod +x statping
|
||||
tar -czvf statping-linux-${{ matrix.arch }}.tar.gz statping
|
||||
rm -rf statping
|
||||
echo ::set-env name=compressed::statping-linux-${{ matrix.arch }}.tar.gz
|
||||
|
||||
- name: Compress Windows Builds
|
||||
if: matrix.platform == 'windows'
|
||||
run: |
|
||||
cd build
|
||||
mv statping-windows-4.0-${{ matrix.arch }}.exe statping.exe
|
||||
chmod +x statping.exe
|
||||
zip statping-windows-${{ matrix.arch }}.zip statping.exe
|
||||
rm -rf statping.exe
|
||||
echo ::set-env name=compressed::statping-windows-${{ matrix.arch }}.zip
|
||||
|
||||
- name: Compress Darwin Builds
|
||||
if: matrix.platform == 'darwin'
|
||||
run: |
|
||||
cd build
|
||||
mv statping-darwin-10.6-${{ matrix.arch }} statping
|
||||
chmod +x statping
|
||||
tar -czvf statping-darwin-${{ matrix.arch }}.tar.gz statping
|
||||
rm -rf statping
|
||||
echo ::set-env name=compressed::statping-darwin-${{ matrix.arch }}.tar.gz
|
||||
|
||||
- name: Upload Compiled Statping Binary
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: statping-${{ matrix.platform }}-${{ matrix.arch }}
|
||||
path: ./build
|
||||
|
||||
- name: Upload Releases
|
||||
id: upload-assets
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ env.VERSION }}
|
||||
with:
|
||||
tag_name: dev-v${{ env.VERSION }}
|
||||
draft: true
|
||||
prerelease: true
|
||||
files: build/${{ env.compressed }}
|
||||
|
||||
- name: Upload Compiled Binaries to S3
|
||||
run: |
|
||||
aws s3 cp build/${{ env.compressed }} s3://assets.statping.com/commit/${{ github.sha }}/
|
||||
|
||||
test:
|
||||
needs: frontend
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10.8
|
||||
|
@ -73,13 +214,13 @@ jobs:
|
|||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
go-version: 1.15.x
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
- uses: actions/checkout@v2
|
||||
node-version: 12.18.2
|
||||
|
||||
- name: Install Global Dependencies
|
||||
run: npm install -g yarn sass newman cross-env wait-on @sentry/cli
|
||||
|
@ -87,7 +228,7 @@ jobs:
|
|||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/12.18.2/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
|
@ -100,6 +241,7 @@ jobs:
|
|||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
run: |
|
||||
make build certs
|
||||
chmod +x statping
|
||||
|
@ -111,11 +253,13 @@ jobs:
|
|||
gotestsum --no-summary=skipped --format dots -- -covermode=count -coverprofile=coverage.out -p=1 ./...
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
DB_CONN: sqlite3
|
||||
STATPING_DIR: ${{ github.workspace }}
|
||||
API_SECRET: demopassword123
|
||||
DISABLE_LOGS: false
|
||||
ALLOW_REPORTS: true
|
||||
SAMPLE_DATA: true
|
||||
COVERALLS: ${{ secrets.COVERALLS }}
|
||||
DISCORD_URL: ${{ secrets.DISCORD_URL }}
|
||||
EMAIL_HOST: ${{ secrets.EMAIL_HOST }}
|
||||
|
@ -136,6 +280,12 @@ jobs:
|
|||
TWILIO_FROM: ${{ secrets.TWILIO_FROM }}
|
||||
TWILIO_TO: ${{ secrets.TWILIO_TO }}
|
||||
TEST_EMAIL: ${{ secrets.TEST_EMAIL }}
|
||||
GOTIFY_URL: ${{ secrets.GOTIFY_URL }}
|
||||
GOTIFY_TOKEN: ${{ secrets.GOTIFY_TOKEN }}
|
||||
SNS_TOKEN: ${{ secrets.SNS_TOKEN }}
|
||||
SNS_SECRET: ${{ secrets.SNS_SECRET }}
|
||||
SNS_REGION: ${{ secrets.SNS_REGION }}
|
||||
SNS_TOPIC: ${{ secrets.SNS_TOPIC }}
|
||||
|
||||
- name: Coveralls Testing Coverage
|
||||
run: |
|
||||
|
@ -145,18 +295,18 @@ jobs:
|
|||
COVERALLS: ${{ secrets.COVERALLS }}
|
||||
|
||||
test-postman-sqlite:
|
||||
needs: compile
|
||||
needs: frontend
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
go-version: 1.15.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 "::add-path::/opt/hostedtoolcache/node/12.18.2/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
|
@ -169,16 +319,15 @@ jobs:
|
|||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
run: |
|
||||
make build
|
||||
chmod +x statping
|
||||
mv statping $(go env GOPATH)/bin/
|
||||
|
||||
- name: Run Statping
|
||||
run: |
|
||||
API_SECRET=demosecret123 statping --port=8585 > /dev/null &
|
||||
sleep 5
|
||||
|
||||
- name: Postman SQLite Tests
|
||||
uses: matt-ball/newman-action@master
|
||||
with:
|
||||
|
@ -189,7 +338,7 @@ jobs:
|
|||
delayRequest: 600
|
||||
|
||||
test-postman-mysql:
|
||||
needs: compile
|
||||
needs: frontend
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
|
@ -206,12 +355,12 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
go-version: 1.15.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 "::add-path::/opt/hostedtoolcache/node/12.18.2/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
|
@ -224,16 +373,17 @@ jobs:
|
|||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
MJML_APP: ${{ secrets.MJML_APP }}
|
||||
MJML_PRIVATE: ${{ secrets.MJML_PRIVATE }}
|
||||
run: |
|
||||
make build
|
||||
chmod +x statping
|
||||
mv statping $(go env GOPATH)/bin/
|
||||
|
||||
- name: Run Statping
|
||||
run: |
|
||||
API_SECRET=demosecret123 statping --port=8585 > /dev/null &
|
||||
sleep 5
|
||||
|
||||
- name: Postman MySQL Tests
|
||||
uses: matt-ball/newman-action@master
|
||||
with:
|
||||
|
@ -244,7 +394,7 @@ jobs:
|
|||
delayRequest: 600
|
||||
|
||||
test-postman-postgres:
|
||||
needs: compile
|
||||
needs: frontend
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
|
@ -262,12 +412,12 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
go-version: 1.15.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 "::add-path::/opt/hostedtoolcache/node/12.18.2/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
|
@ -280,16 +430,15 @@ jobs:
|
|||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
run: |
|
||||
make build
|
||||
chmod +x statping
|
||||
mv statping $(go env GOPATH)/bin/
|
||||
|
||||
- name: Run Statping
|
||||
run: |
|
||||
API_SECRET=demosecret123 statping --port=8585 > /dev/null &
|
||||
sleep 5
|
||||
|
||||
- name: Postman Postgres Tests
|
||||
uses: matt-ball/newman-action@master
|
||||
with:
|
||||
|
@ -300,7 +449,7 @@ jobs:
|
|||
delayRequest: 600
|
||||
|
||||
docker-release:
|
||||
needs: [test, test-postman-sqlite, test-postman-mysql, test-postman-postgres]
|
||||
needs: [test, build, test-postman-sqlite, test-postman-mysql, test-postman-postgres]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Statping Repo
|
||||
|
@ -310,25 +459,47 @@ jobs:
|
|||
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: Set up Docker Buildx
|
||||
uses: crazy-max/ghaction-docker-buildx@v3
|
||||
|
||||
- name: Dev Docker Image
|
||||
uses: elgohr/Publish-Docker-Github-Action@master
|
||||
- name: Docker Login
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
ARCH: amd64
|
||||
DOCKER_CLI_EXPERIMENTAL: enabled
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
id: buildx-docker-master
|
||||
with:
|
||||
name: statping/statping
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
dockerfile: Dockerfile
|
||||
tags: "dev"
|
||||
buildargs: VERSION,ARCH
|
||||
path: /tmp/.buildx-cache
|
||||
key: buildx-docker-master
|
||||
restore-keys: |
|
||||
buildx-docker-master
|
||||
- name: Docker Build :base
|
||||
run: make buildx-base
|
||||
|
||||
- name: Docker Build :dev
|
||||
run: make buildx-dev
|
||||
|
||||
sentry-release:
|
||||
needs: [test, test-postman-sqlite, test-postman-postgres, test-postman-mysql]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Statping Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Sentry CLI
|
||||
uses: mathrix-education/setup-sentry-cli@master
|
||||
with:
|
||||
version: latest
|
||||
url: ${{ secrets.SENTRY_URL }}
|
||||
token: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
organization: statping
|
||||
|
||||
- name: Setting ENV's
|
||||
run: echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Sentry Release
|
||||
run: make sentry-release
|
|
@ -1,4 +1,4 @@
|
|||
name: Master Release
|
||||
name: Master Build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
|
@ -7,40 +7,46 @@ on:
|
|||
- '**.md'
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
|
||||
frontend:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
go-version: 1.15.x
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
- uses: actions/checkout@v2
|
||||
node-version: 12.18.2
|
||||
|
||||
- name: Add GOBIN to PATH
|
||||
- name: Set Version
|
||||
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
|
||||
run: npm install -g yarn sass cross-env mjml
|
||||
|
||||
- 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
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
MJML_APP: ${{ secrets.MJML_APP }}
|
||||
MJML_PRIVATE: ${{ secrets.MJML_PRIVATE }}
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
run: make clean generate compile
|
||||
|
||||
- name: Upload Compiled Frontend (rice-box.go)
|
||||
uses: actions/upload-artifact@v1
|
||||
|
@ -48,10 +54,145 @@ jobs:
|
|||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
test:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
- name: Configure AWS credentials for Asset uploads
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-west-2
|
||||
|
||||
- name: Upload Static Assets to S3
|
||||
run: |
|
||||
tar -czvf source.tar.gz source/
|
||||
aws s3 cp source.tar.gz s3://assets.statping.com/commit/${{ github.sha }}/
|
||||
rm -rf source.tar.gz
|
||||
|
||||
build:
|
||||
needs: frontend
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
platform: [linux]
|
||||
arch: [386, amd64, arm-7, arm-6, arm64]
|
||||
include:
|
||||
- platform: darwin
|
||||
arch: amd64
|
||||
- platform: windows
|
||||
arch: amd64
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Download Compiled Frontend (rice-box.go)
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
- name: Configure AWS credentials for Asset uploads
|
||||
uses: aws-actions/configure-aws-credentials@v1
|
||||
with:
|
||||
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||
aws-region: us-west-2
|
||||
|
||||
- name: Add GOBIN to PATH
|
||||
run: |
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Set Linux Build Flags
|
||||
if: matrix.platform == 'linux'
|
||||
run: |
|
||||
echo ::set-env name=BUILD_FLAGS::'-extldflags -static'
|
||||
echo ::set-env name=XGO_TAGS::'netgo,osusergo,linux,sqlite_omit_load_extension'
|
||||
shell: bash
|
||||
|
||||
- name: Set Darwin Build Flags
|
||||
if: matrix.platform == 'darwin'
|
||||
run: echo ::set-env name=XGO_TAGS::'netgo,osusergo,darwin,sqlite_omit_load_extension'
|
||||
shell: bash
|
||||
|
||||
- name: Set Windows Build Flags
|
||||
if: matrix.platform == 'windows'
|
||||
run: |
|
||||
echo ::set-env name=BUILD_FLAGS::'-extldflags -static'
|
||||
echo ::set-env name=XGO_TAGS::'netgo,osusergo,sqlite_omit_load_extension'
|
||||
shell: bash
|
||||
|
||||
- name: Build ${{ matrix.platform }}/${{ matrix.arch }}
|
||||
uses: crazy-max/ghaction-xgo@v1
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
with:
|
||||
xgo_version: latest
|
||||
go_version: 1.15.x
|
||||
dest: build
|
||||
prefix: statping
|
||||
targets: ${{ matrix.platform }}/${{ matrix.arch }}
|
||||
v: false
|
||||
x: false
|
||||
pkg: cmd
|
||||
buildmode: pie
|
||||
tags: ${{ env.XGO_TAGS }}
|
||||
ldflags: -s -w -X main.VERSION=${{ env.VERSION }} -X main.COMMIT=${{ env.COMMIT }} ${{ env.BUILD_FLAGS }}
|
||||
|
||||
- name: Compress Linux Builds
|
||||
if: matrix.platform == 'linux'
|
||||
run: |
|
||||
cd build
|
||||
mv statping-linux-${{ matrix.arch }} statping
|
||||
chmod +x statping
|
||||
tar -czvf statping-linux-${{ matrix.arch }}.tar.gz statping
|
||||
rm -rf statping
|
||||
echo ::set-env name=compressed::statping-linux-${{ matrix.arch }}.tar.gz
|
||||
|
||||
- name: Compress Windows Builds
|
||||
if: matrix.platform == 'windows'
|
||||
run: |
|
||||
cd build
|
||||
mv statping-windows-4.0-${{ matrix.arch }}.exe statping.exe
|
||||
chmod +x statping.exe
|
||||
zip statping-windows-${{ matrix.arch }}.zip statping.exe
|
||||
rm -rf statping.exe
|
||||
echo ::set-env name=compressed::statping-windows-${{ matrix.arch }}.zip
|
||||
|
||||
- name: Compress Darwin Builds
|
||||
if: matrix.platform == 'darwin'
|
||||
run: |
|
||||
cd build
|
||||
mv statping-darwin-10.6-${{ matrix.arch }} statping
|
||||
chmod +x statping
|
||||
tar -czvf statping-darwin-${{ matrix.arch }}.tar.gz statping
|
||||
rm -rf statping
|
||||
echo ::set-env name=compressed::statping-darwin-${{ matrix.arch }}.tar.gz
|
||||
|
||||
- name: Upload Compiled Statping Binary
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: statping-${{ matrix.platform }}-${{ matrix.arch }}
|
||||
path: ./build
|
||||
|
||||
- name: Upload Releases
|
||||
id: upload-assets
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ env.VERSION }}
|
||||
with:
|
||||
tag_name: v${{ env.VERSION }}
|
||||
draft: true
|
||||
prerelease: true
|
||||
files: build/${{ env.compressed }}
|
||||
|
||||
- name: Upload Compiled Binaries to S3
|
||||
run: |
|
||||
aws s3 cp build/${{ env.compressed }} s3://assets.statping.com/commit/${{ github.sha }}/
|
||||
|
||||
test:
|
||||
needs: frontend
|
||||
runs-on: ubuntu-latest
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10.8
|
||||
|
@ -73,13 +214,13 @@ jobs:
|
|||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
go-version: 1.15.x
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
- uses: actions/checkout@v2
|
||||
node-version: 12.18.2
|
||||
|
||||
- name: Install Global Dependencies
|
||||
run: npm install -g yarn sass newman cross-env wait-on @sentry/cli
|
||||
|
@ -87,7 +228,7 @@ jobs:
|
|||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/12.18.2/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
|
@ -100,6 +241,7 @@ jobs:
|
|||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
run: |
|
||||
make build certs
|
||||
chmod +x statping
|
||||
|
@ -111,11 +253,13 @@ jobs:
|
|||
gotestsum --no-summary=skipped --format dots -- -covermode=count -coverprofile=coverage.out -p=1 ./...
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
DB_CONN: sqlite3
|
||||
STATPING_DIR: ${{ github.workspace }}
|
||||
API_SECRET: demopassword123
|
||||
DISABLE_LOGS: false
|
||||
ALLOW_REPORTS: true
|
||||
SAMPLE_DATA: true
|
||||
COVERALLS: ${{ secrets.COVERALLS }}
|
||||
DISCORD_URL: ${{ secrets.DISCORD_URL }}
|
||||
EMAIL_HOST: ${{ secrets.EMAIL_HOST }}
|
||||
|
@ -136,6 +280,12 @@ jobs:
|
|||
TWILIO_FROM: ${{ secrets.TWILIO_FROM }}
|
||||
TWILIO_TO: ${{ secrets.TWILIO_TO }}
|
||||
TEST_EMAIL: ${{ secrets.TEST_EMAIL }}
|
||||
GOTIFY_URL: ${{ secrets.GOTIFY_URL }}
|
||||
GOTIFY_TOKEN: ${{ secrets.GOTIFY_TOKEN }}
|
||||
SNS_TOKEN: ${{ secrets.SNS_TOKEN }}
|
||||
SNS_SECRET: ${{ secrets.SNS_SECRET }}
|
||||
SNS_REGION: ${{ secrets.SNS_REGION }}
|
||||
SNS_TOPIC: ${{ secrets.SNS_TOPIC }}
|
||||
|
||||
- name: Coveralls Testing Coverage
|
||||
run: |
|
||||
|
@ -145,18 +295,18 @@ jobs:
|
|||
COVERALLS: ${{ secrets.COVERALLS }}
|
||||
|
||||
test-postman-sqlite:
|
||||
needs: compile
|
||||
needs: frontend
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
go-version: 1.15.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 "::add-path::/opt/hostedtoolcache/node/12.18.2/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
|
@ -169,16 +319,15 @@ jobs:
|
|||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
run: |
|
||||
make build
|
||||
chmod +x statping
|
||||
mv statping $(go env GOPATH)/bin/
|
||||
|
||||
- name: Run Statping
|
||||
run: |
|
||||
API_SECRET=demosecret123 statping --port=8585 > /dev/null &
|
||||
sleep 5
|
||||
|
||||
- name: Postman SQLite Tests
|
||||
uses: matt-ball/newman-action@master
|
||||
with:
|
||||
|
@ -189,7 +338,7 @@ jobs:
|
|||
delayRequest: 600
|
||||
|
||||
test-postman-mysql:
|
||||
needs: compile
|
||||
needs: frontend
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
|
@ -206,12 +355,12 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
go-version: 1.15.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 "::add-path::/opt/hostedtoolcache/node/12.18.2/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
|
@ -224,16 +373,17 @@ jobs:
|
|||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
MJML_APP: ${{ secrets.MJML_APP }}
|
||||
MJML_PRIVATE: ${{ secrets.MJML_PRIVATE }}
|
||||
run: |
|
||||
make build
|
||||
chmod +x statping
|
||||
mv statping $(go env GOPATH)/bin/
|
||||
|
||||
- name: Run Statping
|
||||
run: |
|
||||
API_SECRET=demosecret123 statping --port=8585 > /dev/null &
|
||||
sleep 5
|
||||
|
||||
- name: Postman MySQL Tests
|
||||
uses: matt-ball/newman-action@master
|
||||
with:
|
||||
|
@ -244,7 +394,7 @@ jobs:
|
|||
delayRequest: 600
|
||||
|
||||
test-postman-postgres:
|
||||
needs: compile
|
||||
needs: frontend
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
|
@ -262,12 +412,12 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
go-version: 1.15.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 "::add-path::/opt/hostedtoolcache/node/12.18.2/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
|
@ -280,16 +430,15 @@ jobs:
|
|||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
run: |
|
||||
make build
|
||||
chmod +x statping
|
||||
mv statping $(go env GOPATH)/bin/
|
||||
|
||||
- name: Run Statping
|
||||
run: |
|
||||
API_SECRET=demosecret123 statping --port=8585 > /dev/null &
|
||||
sleep 5
|
||||
|
||||
- name: Postman Postgres Tests
|
||||
uses: matt-ball/newman-action@master
|
||||
with:
|
||||
|
@ -299,110 +448,8 @@ jobs:
|
|||
timeoutRequest: 30000
|
||||
delayRequest: 600
|
||||
|
||||
build-binaries:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install cross compiling libraries
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y automake autogen build-essential ca-certificates libsqlite3-dev \
|
||||
gcc-5-arm-linux-gnueabi g++-5-arm-linux-gnueabi libc6-dev-armel-cross linux-headers-generic \
|
||||
gcc-5-arm-linux-gnueabihf g++-5-arm-linux-gnueabihf libc6-dev-armhf-cross \
|
||||
gcc-5-aarch64-linux-gnu g++-5-aarch64-linux-gnu libc6-dev-arm64-cross \
|
||||
gcc-5-mips-linux-gnu g++-5-mips-linux-gnu libc6-dev-mips-cross \
|
||||
gcc-5-mipsel-linux-gnu g++-5-mipsel-linux-gnu libc6-dev-mipsel-cross \
|
||||
gcc-5-mips64-linux-gnuabi64 g++-5-mips64-linux-gnuabi64 libc6-dev-mips64-cross \
|
||||
gcc-5-mips64el-linux-gnuabi64 g++-5-mips64el-linux-gnuabi64 libc6-dev-mips64el-cross \
|
||||
gcc-5-multilib g++-5-multilib gcc-mingw-w64 g++-mingw-w64 clang llvm-dev \
|
||||
gcc-6-arm-linux-gnueabi g++-6-arm-linux-gnueabi libc6-dev-armel-cross \
|
||||
gcc-6-arm-linux-gnueabihf g++-6-arm-linux-gnueabihf libc6-dev-armhf-cross \
|
||||
gcc-6-aarch64-linux-gnu g++-6-aarch64-linux-gnu libc6-dev-arm64-cross \
|
||||
gcc-6-mips-linux-gnu g++-6-mips-linux-gnu libc6-dev-mips-cross \
|
||||
gcc-6-mipsel-linux-gnu g++-6-mipsel-linux-gnu libc6-dev-mipsel-cross \
|
||||
gcc-6-mips64-linux-gnuabi64 g++-6-mips64-linux-gnuabi64 libc6-dev-mips64-cross \
|
||||
gcc-6-mips64el-linux-gnuabi64 g++-6-mips64el-linux-gnuabi64 libc6-dev-mips64el-cross \
|
||||
gcc-6-multilib gcc-7-multilib g++-6-multilib gcc-mingw-w64 g++-mingw-w64 clang llvm-dev \
|
||||
libtool libxml2-dev uuid-dev libssl-dev swig openjdk-8-jdk pkg-config patch \
|
||||
make xz-utils cpio wget zip unzip p7zip git mercurial bzr texinfo help2man cmake --no-install-recommends
|
||||
sudo ln -s /usr/include/asm-generic/ /usr/include/asm
|
||||
|
||||
- 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
|
||||
run: |
|
||||
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-folders build-linux build-linux-arm build-darwin build-win compress-folders
|
||||
|
||||
- name: Upload Builds
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: builds
|
||||
path: ./build
|
||||
|
||||
upload-release:
|
||||
needs: [test, test-postman-sqlite, test-postman-mysql, test-postman-postgres, build-binaries]
|
||||
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 Builds
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: builds
|
||||
path: ./builds
|
||||
|
||||
- name: Upload Releases
|
||||
id: upload-assets
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
VERSION: ${{ env.VERSION }}
|
||||
with:
|
||||
tag_name: v${{ env.VERSION }}
|
||||
draft: false
|
||||
prerelease: true
|
||||
files: |
|
||||
builds/statping-linux-386.tar.gz
|
||||
builds/statping-linux-amd64.tar.gz
|
||||
builds/statping-linux-arm.tar.gz
|
||||
builds/statping-linux-arm64.tar.gz
|
||||
builds/statping-darwin-amd64.tar.gz
|
||||
builds/statping-darwin-386.tar.gz
|
||||
builds/statping-windows-386.zip
|
||||
builds/statping-windows-amd64.zip
|
||||
builds/statping-windows-arm.zip
|
||||
|
||||
docker-release:
|
||||
needs: upload-release
|
||||
needs: [build, test, test-postman-sqlite, test-postman-postgres, test-postman-mysql]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Statping Repo
|
||||
|
@ -412,64 +459,53 @@ jobs:
|
|||
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: Set up Docker Buildx
|
||||
uses: crazy-max/ghaction-docker-buildx@v3
|
||||
|
||||
- name: Latest/Version Docker Image
|
||||
uses: elgohr/Publish-Docker-Github-Action@master
|
||||
- name: Docker Login
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
ARCH: amd64
|
||||
DOCKER_CLI_EXPERIMENTAL: enabled
|
||||
DOCKER_USERNAME: ${{ secrets.DOCKER_USERNAME }}
|
||||
DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }}
|
||||
run: echo "${DOCKER_PASSWORD}" | docker login --username "${DOCKER_USERNAME}" --password-stdin
|
||||
|
||||
- name: Cache Docker layers
|
||||
uses: actions/cache@v2
|
||||
id: buildx-docker
|
||||
with:
|
||||
name: statping/statping
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
dockerfile: Dockerfile
|
||||
tags: "latest,v${{ env.VERSION }}"
|
||||
buildargs: VERSION,ARCH
|
||||
path: /tmp/.buildx-cache
|
||||
key: buildx-docker
|
||||
restore-keys: |
|
||||
buildx-docker
|
||||
- name: Docker Build :base
|
||||
run: make buildx-base
|
||||
|
||||
- name: Docker Build :lastest
|
||||
run: make buildx-latest
|
||||
|
||||
sentry-release:
|
||||
needs: upload-release
|
||||
needs: docker-release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Statping Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setup Sentry CLI
|
||||
uses: mathrix-education/setup-sentry-cli@master
|
||||
with:
|
||||
version: latest
|
||||
url: ${{ secrets.SENTRY_URL }}
|
||||
token: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
organization: statping
|
||||
|
||||
- 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
|
||||
- name: Sentry Release
|
||||
run: make sentry-release
|
||||
|
||||
homebrew-release:
|
||||
needs: upload-release
|
||||
needs: docker-release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Statping Repo
|
||||
|
@ -486,7 +522,7 @@ jobs:
|
|||
run: make publish-homebrew
|
||||
|
||||
slack-update:
|
||||
needs: upload-release
|
||||
needs: docker-release
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Statping Repo
|
||||
|
@ -497,7 +533,7 @@ jobs:
|
|||
shell: bash
|
||||
|
||||
- name: Slack Notification
|
||||
uses: rtCamp/action-slack-notify@v2.0.0
|
||||
uses: rtCamp/action-slack-notify@master
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_URL }}
|
||||
SLACK_CHANNEL: dev
|
||||
|
|
|
@ -5,9 +5,14 @@ on:
|
|||
- '*' # matches every branch
|
||||
- '*/*' # matches every branch containing a single '/'
|
||||
- '!master' # excludes master
|
||||
- '!dev' # excludes dev
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
- '*' # matches every branch
|
||||
- '*/*' # matches every branch containing a single '/'
|
||||
- '!master' # excludes master
|
||||
- '!dev' # excludes dev
|
||||
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
|
@ -18,7 +23,7 @@ jobs:
|
|||
go-version: '1.14.2'
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
node-version: '12.18.2'
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Add GOBIN to PATH
|
||||
|
@ -78,10 +83,10 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
go-version: 1.15.x
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
node-version: 12.18.2
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Global Dependencies
|
||||
|
@ -92,7 +97,7 @@ jobs:
|
|||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/12.18.2/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
|
@ -115,6 +120,7 @@ jobs:
|
|||
SASS=`which sass` gotestsum --no-summary=skipped --format dots -- -covermode=count -coverprofile=coverage.out -p=1 ./...
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
DB_CONN: sqlite3
|
||||
STATPING_DIR: ${{ github.workspace }}
|
||||
API_SECRET: demopassword123
|
||||
|
@ -134,7 +140,7 @@ jobs:
|
|||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/12.18.2/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
|
@ -147,6 +153,7 @@ jobs:
|
|||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
COMMIT: ${{ github.sha }}
|
||||
run: |
|
||||
make build
|
||||
chmod +x statping
|
||||
|
|
|
@ -3,6 +3,8 @@ snap
|
|||
prime
|
||||
stage
|
||||
parts
|
||||
assets_backup
|
||||
certs
|
||||
releases
|
||||
core/rice-box.go
|
||||
config.yml
|
||||
|
@ -37,3 +39,4 @@ tmp
|
|||
/frontend/cypress/screenshots/
|
||||
/frontend/cypress/videos/
|
||||
services.yml
|
||||
statping.wiki
|
||||
|
|
94
CHANGELOG.md
94
CHANGELOG.md
|
@ -1,3 +1,97 @@
|
|||
# 0.90.69 (09-18-2020)
|
||||
- Fixed issue with service view not loading. #808 #811 #800
|
||||
|
||||
# 0.90.68 (09-17-2020)
|
||||
- Added DB_DSN env for mysql, postgres or sqlite DSN database connection string
|
||||
- Added READ_ONLY env for a read only connection to the database
|
||||
- Added Custom OAuth OpenID toggle switch in settings (appends 'openid' in scope)
|
||||
- Fixed Custom OAuth response_type issue
|
||||
- Added Configs tab in Settings to edit the config.yml from frontend
|
||||
|
||||
# 0.90.67 (09-14-2020)
|
||||
- Modified core settings to update config.yml on save
|
||||
- Modified Theme Editor to restart the HTTP router on create/delete (fixing 404's)
|
||||
|
||||
# 0.90.66 (09-08-2020)
|
||||
- Added Import and Export views in Dashboard
|
||||
- Modified services list sparkline to use start/end of day timestamp
|
||||
- Modified i18n language files, added go generate script to automatically translate
|
||||
|
||||
# 0.90.65 (09-01-2020)
|
||||
- Fixed issue with dashboard not logging in (notifier panic)
|
||||
- Modified static email templates to github.com/statping/emails
|
||||
- Modified Regenerate API function to keep API_SECRET env
|
||||
- Added DEMO_MODE env variable, if true, 'admin' cannot be deleted
|
||||
- Modified Service sparklines on Dashboard
|
||||
- Added modal popup for UI deletes/edits
|
||||
|
||||
# 0.90.64 (08-18-2020)
|
||||
- Modified max-width for container to 1012px, larger UI
|
||||
- Added failure sparklines in the Services list view
|
||||
- Added "Update Available" alert on the top of Settings if new version is available
|
||||
- Added Version and Github Commit hash to left navigation on Settings page
|
||||
- Added "reason" for failures (will be used for more custom notification messages) [regex, lookup, timeout, connection, close, status_code]
|
||||
- Added Help page that is generated from Statping's Wiki repo on build
|
||||
- Modified Service Group failures on index page to show 90 days of failures
|
||||
- Modified Service view page, updated Latency and Ping charts, added failures below
|
||||
- Modified Service chart on index page to show ping data along with latency
|
||||
- Added AWS SNS Notifier
|
||||
- Modified dashboard services UI
|
||||
- Modified service.Failures API to include 32 failures (max)
|
||||
|
||||
# 0.90.63 (08-17-2020)
|
||||
- Modified build process to use xgo for all arch builds
|
||||
- Modified Statping's Push Notifications server notifier to match with Firebase/gorush params
|
||||
|
||||
# 0.90.62 (08-07-2020)
|
||||
- Added Notification logs
|
||||
- Fixed issues with Notifer After (x) failures for notifications
|
||||
- Modified notifications to not send on initial startup
|
||||
- Updated Incident UI
|
||||
- Added additional testing for notifications
|
||||
- Modified SCSS/SASS files to be generated from 1, main.scss to main.css
|
||||
- Modified index page to use /assets directory for assets, (main.css, style.css)
|
||||
- Modified index page to use CDN asset paths
|
||||
- Fixed New Checkin form
|
||||
- Modified email notifier template to be rendered from MJML (using go generate)
|
||||
- Modified database relationships with services using gorm
|
||||
- Modified "statping env" command to show user/group ID
|
||||
- Removed "js" folder when exporting assets, js files are always version of release, not static JS files
|
||||
|
||||
# 0.90.61 (07-22-2020)
|
||||
- Modified sass layouts, organized and split up sections
|
||||
- Modified Checkins to seconds rather than milliseconds (for cronjob)
|
||||
- Modified Service View page to show data inside cards
|
||||
- Fixed issue with uptime_data sending incorrect start/end timestamps
|
||||
- Modified http cache to bypass if url has a "v" query param
|
||||
- Added "Static Services" (a fake service that requires you to update the online/offline status)
|
||||
- Added Update Static Service PATCH route (/api/services/{id})
|
||||
- Modified SASS api endpoints (base, layout, forms, mixins, mobile, variables)
|
||||
- Added additional testing
|
||||
- Modified node version from 10.x to 12.18.2
|
||||
- Modified Notifier's struct values to be NullString and NullInt to allow empty values
|
||||
- Added Search ability to Logs in UI
|
||||
- Fixed issue with Incidents and Checkins not being deleted once service is deleted
|
||||
|
||||
# 0.90.60 (07-15-2020)
|
||||
- Added LETSENCRYPT_ENABLE (boolean) env to enable/disable letsencrypt SSL
|
||||
|
||||
# 0.90.59 (07-14-2020)
|
||||
- Added LetsEncrypt SSL Generator by using LETSENCRYPT_HOST and LETSENCRYPT_EMAIL envs.
|
||||
- Modified JWT token key to be sha256 of API Secret
|
||||
- Modified github actions to build multi-arch Docker images
|
||||
- Added "update" command to install latest version
|
||||
- Fixed dashboard uptime_data API request to request correct start/time timestamp
|
||||
|
||||
# 0.90.58 (07-09-2020)
|
||||
- Fixed ICMP latency/ping durations
|
||||
- Fixed webhook notifier
|
||||
- Modified file structure for Vue admin dashboard components.
|
||||
- Added Gotify notifier
|
||||
|
||||
# 0.90.57 (07-04-2020)
|
||||
- Fixed login issue
|
||||
|
||||
# 0.90.56 (06-25-2020)
|
||||
- Modified metrics now include service name for each service metric
|
||||
- Added switch for true/false notifier values
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
{
|
||||
"id": "com.statping",
|
||||
"title": "Statping",
|
||||
"author": "Hunter Long <info@statping.com>",
|
||||
"description": "Monitor your web services and remote servers",
|
||||
"tagline": "Server Monitoring",
|
||||
"version": "0.90.61",
|
||||
"healthCheckPath": "/health",
|
||||
"httpPort": 8080,
|
||||
"addons": {
|
||||
"localstorage": {}
|
||||
},
|
||||
"manifestVersion": 2,
|
||||
"website": "https://github.com/statping/statping",
|
||||
"contactEmail": "info@statping.com",
|
||||
"icon": "https://assets.statping.com/icon.png",
|
||||
"tags": [ "monitoring", "uptime" ],
|
||||
"mediaLinks": [ "https://assets.statping.com/cloudron.png" ]
|
||||
}s
|
|
@ -1,18 +1,18 @@
|
|||
FROM statping/statping:base AS base
|
||||
|
||||
ARG BUILDPLATFORM
|
||||
# Statping main Docker image that contains all required libraries
|
||||
FROM alpine:latest
|
||||
RUN apk --no-cache add libgcc libstdc++ ca-certificates curl jq && update-ca-certificates
|
||||
|
||||
COPY --from=base /go/bin/statping /usr/local/bin/
|
||||
COPY --from=base /usr/local/bin/sass /usr/local/bin/
|
||||
COPY --from=base /root/sassc/bin/sassc /usr/local/bin/
|
||||
COPY --from=base /usr/local/share/ca-certificates /usr/local/share/
|
||||
|
||||
WORKDIR /app
|
||||
VOLUME /app
|
||||
|
||||
ENV IS_DOCKER=true
|
||||
ENV SASS=/usr/local/bin/sass
|
||||
ENV SASS=/usr/local/bin/sassc
|
||||
ENV STATPING_DIR=/app
|
||||
ENV PORT=8080
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
FROM node:10.17.0 AS frontend
|
||||
RUN npm install yarn -g
|
||||
FROM node:12.18.2-alpine AS frontend
|
||||
LABEL maintainer="Hunter Long (https://github.com/hunterlong)"
|
||||
ARG BUILDPLATFORM
|
||||
WORKDIR /statping
|
||||
COPY ./frontend/package.json .
|
||||
COPY ./frontend/yarn.lock .
|
||||
|
@ -7,20 +8,28 @@ RUN yarn install --pure-lockfile --network-timeout 1000000
|
|||
COPY ./frontend .
|
||||
RUN yarn build && yarn cache clean
|
||||
|
||||
|
||||
# Statping Golang BACKEND building from source
|
||||
# Creates "/go/bin/statping" and "/usr/local/bin/sass" for copying
|
||||
FROM golang:1.14-alpine AS backend
|
||||
LABEL maintainer="Hunter Long (https://github.com/hunterlong)"
|
||||
ARG VERSION
|
||||
RUN apk add --update --no-cache libstdc++ gcc g++ make git ca-certificates linux-headers wget curl jq && \
|
||||
ARG COMMIT
|
||||
ARG BUILDPLATFORM
|
||||
ARG TARGETARCH
|
||||
RUN apk add --update --no-cache libstdc++ gcc g++ make git autoconf \
|
||||
libtool ca-certificates linux-headers wget curl jq && \
|
||||
update-ca-certificates
|
||||
RUN curl -L -s https://assets.statping.com/sass -o /usr/local/bin/sass && \
|
||||
chmod +x /usr/local/bin/sass
|
||||
|
||||
WORKDIR /root
|
||||
RUN git clone https://github.com/sass/sassc.git
|
||||
RUN . sassc/script/bootstrap && make -C sassc -j4
|
||||
# sassc binary: /root/sassc/bin/sassc
|
||||
|
||||
WORKDIR /go/src/github.com/statping/statping
|
||||
ADD go.mod go.sum ./
|
||||
RUN go mod download
|
||||
ENV GO111MODULE on
|
||||
ENV CGO_ENABLED 1
|
||||
RUN go get github.com/stretchr/testify/assert && \
|
||||
go get github.com/stretchr/testify/require && \
|
||||
go get github.com/GeertJohan/go.rice/rice && \
|
||||
|
@ -28,8 +37,9 @@ RUN go get github.com/stretchr/testify/assert && \
|
|||
go get github.com/crazy-max/xgo
|
||||
COPY . .
|
||||
COPY --from=frontend /statping/dist/ ./source/dist/
|
||||
RUN make clean frontend-copy generate embed build
|
||||
RUN make clean generate embed
|
||||
RUN go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION} -X main.COMMIT=${COMMIT}" -o statping --tags "netgo linux" ./cmd
|
||||
RUN chmod a+x statping && mv statping /go/bin/statping
|
||||
# /go/bin/statping - statping binary
|
||||
# /usr/local/bin/sass - sass binary
|
||||
# /root/sassc/bin/sassc - sass binary
|
||||
# /statping - Vue frontend (from frontend)
|
||||
|
|
214
Makefile
214
Makefile
|
@ -1,19 +1,40 @@
|
|||
VERSION=$(shell cat version.txt)
|
||||
COMMIT=$(shell git rev-parse HEAD)
|
||||
SIGN_KEY=B76D61FAA6DB759466E83D9964B9C6AAE2D55278
|
||||
BINARY_NAME=statping
|
||||
GOBUILD=go build -a
|
||||
GOVERSION=1.14.0
|
||||
NODE_VERSION=12.18.2
|
||||
XGO=xgo -go $(GOVERSION) --dest=build
|
||||
BUILDVERSION=-ldflags "-X main.VERSION=${VERSION} -X main.COMMIT=$(TRAVIS_COMMIT)"
|
||||
BUILDVERSION=-ldflags "-X main.VERSION=${VERSION} -X main.COMMIT=${COMMIT}"
|
||||
TRVIS_SECRET=O3/2KTOV8krv+yZ1EB/7D1RQRe6NdpFUEJNJkMS/ollYqmz3x2mCO7yIgIJKCKguLXZxjM6CxJcjlCrvUwibL+8BBp7xJe4XFIOrjkPvbbVPry4HkFZCf2GfcUK6o4AByQ+RYqsW2F17Fp9KLQ1rL3OT3eLTwCAGKx3tlY8y+an43zkmo5dN64V6sawx26fh6XTfww590ey+ltgQTjf8UPNup2wZmGvMo9Hwvh/bYR/47bR6PlBh6vhlKWyotKf2Fz1Bevbu0zc35pee5YlsrHR+oSF+/nNd/dOij34BhtqQikUR+zQVy9yty8SlmneVwD3yOENvlF+8roeKIXb6P6eZnSMHvelhWpAFTwDXq2N3d/FIgrQtLxsAFTI3nTHvZgs6OoTd6dA0wkhuIGLxaL3FOeztCdxP5J/CQ9GUcTvifh5ArGGwYxRxQU6rTgtebJcNtXFISP9CEUR6rwRtb6ax7h6f1SbjUGAdxt+r2LbEVEk4ZlwHvdJ2DtzJHT5DQtLrqq/CTUgJ8SJFMkrJMp/pPznKhzN4qvd8oQJXygSXX/gz92MvoX0xgpNeLsUdAn+PL9KketfR+QYosBz04d8k05E+aTqGaU7FUCHPTLwlOFvLD8Gbv0zsC/PWgSLXTBlcqLEz5PHwPVHTcVzspKj/IyYimXpCSbvu1YOIjyc=
|
||||
PUBLISH_BODY='{ "request": { "branch": "master", "message": "Homebrew update version v${VERSION}", "config": { "env": { "VERSION": "${VERSION}", "COMMIT": "$(TRAVIS_COMMIT)" } } } }'
|
||||
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)
|
||||
PATH:=$(GOPATH)/bin:$(PATH)
|
||||
OS = freebsd linux openbsd
|
||||
ARCHS = 386 arm amd64 arm64
|
||||
|
||||
all: clean yarn-install compile docker-base docker-vue build-all
|
||||
all: build-deps compile install test build
|
||||
|
||||
test: clean compile
|
||||
go test -v -p=1 -ldflags="-X main.VERSION=${VERSION} -X main.COMMIT=${COMMIT}" -coverprofile=coverage.out ./...
|
||||
|
||||
build: clean
|
||||
CGO_ENABLED=1 go build -a -ldflags "-s -w -X main.VERSION=${VERSION} -X main.COMMIT=${COMMIT}" -o statping --tags "netgo osusergo" ./cmd
|
||||
|
||||
go-build: clean
|
||||
rm -rf source/dist
|
||||
rm -rf source/rice-box.go
|
||||
wget https://assets.statping.com/source.tar.gz
|
||||
tar -xvf source.tar.gz
|
||||
go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION} -X main.COMMIT=${COMMIT}" -o statping --tags "netgo osusergo" ./cmd
|
||||
|
||||
lint:
|
||||
go fmt ./...
|
||||
golint ./...
|
||||
impi --local github.com/statping/statping/ --scheme stdLocalThirdParty ./...
|
||||
goimports ./...
|
||||
|
||||
up:
|
||||
docker-compose -f docker-compose.yml -f dev/docker-compose.full.yml up -d --remove-orphans
|
||||
|
@ -29,9 +50,6 @@ lite: clean
|
|||
|
||||
reup: down clean compose-build-full up
|
||||
|
||||
test: clean compile
|
||||
go test -v -p=1 -ldflags="-X main.VERSION=testing" -coverprofile=coverage.out ./...
|
||||
|
||||
# build all arch's and release Statping
|
||||
release: test-deps
|
||||
wget -O statping.gpg $(SIGN_URL)
|
||||
|
@ -50,7 +68,7 @@ cypress: clean
|
|||
|
||||
test-api:
|
||||
DB_CONN=sqlite DB_HOST=localhost DB_DATABASE=sqlite DB_PASS=none DB_USER=none statping &
|
||||
sleep 5000 && newman run source/tmpl/postman.json -e dev/postman_environment.json --delay-request 500
|
||||
sleep 5000 && newman run dev/postman.json -e dev/postman_environment.json --delay-request 500
|
||||
|
||||
test-deps:
|
||||
go get golang.org/x/tools/cmd/cover
|
||||
|
@ -58,6 +76,7 @@ test-deps:
|
|||
go get github.com/GeertJohan/go.rice/rice
|
||||
go get github.com/mattn/go-sqlite3
|
||||
go install github.com/mattn/go-sqlite3
|
||||
go install github.com/wellington/go-libsass
|
||||
|
||||
deps:
|
||||
go get -d -v -t ./...
|
||||
|
@ -121,14 +140,17 @@ top:
|
|||
docker-compose -f docker-compose.yml -f dev/docker-compose.full.yml top
|
||||
|
||||
frontend-build:
|
||||
rm -rf source/dist && rm -rf frontend/dist
|
||||
@echo "Removing old frontend distributions..."
|
||||
@rm -rf source/dist && rm -rf frontend/dist
|
||||
@echo "yarn install and build static frontend"
|
||||
cd frontend && yarn && yarn build
|
||||
cp -r frontend/dist source/ && cp -r frontend/src/assets/scss source/dist/
|
||||
cp -r source/tmpl/*.* source/dist/
|
||||
cp -r frontend/public/favicon source/dist/
|
||||
|
||||
frontend-copy:
|
||||
cp -r source/tmpl/*.* source/dist/
|
||||
@cp -r frontend/dist source/
|
||||
@cp -r frontend/src/assets/scss source/dist/
|
||||
@cp frontend/public/favicon.ico source/dist/
|
||||
@cp frontend/public/robots.txt source/dist/
|
||||
@cp frontend/public/banner.png source/dist/
|
||||
@cp -r frontend/public/favicon source/dist/
|
||||
@echo "Frontend build complete at ./source/dist"
|
||||
|
||||
yarn:
|
||||
rm -rf source/dist && rm -rf frontend/dist
|
||||
|
@ -138,32 +160,40 @@ yarn:
|
|||
compile: frontend-build
|
||||
rm -f source/rice-box.go
|
||||
cd source && rice embed-go
|
||||
make generate
|
||||
|
||||
embed:
|
||||
cd source && rice embed-go
|
||||
|
||||
build:
|
||||
$(GOBUILD) $(BUILDVERSION) -o $(BINARY_NAME) ./cmd
|
||||
|
||||
install: build
|
||||
mv $(BINARY_NAME) $(GOPATH)/bin/$(BINARY_NAME)
|
||||
|
||||
install-local: build
|
||||
mv $(BINARY_NAME) /usr/local/bin/$(BINARY_NAME)
|
||||
|
||||
install-darwin:
|
||||
go build -a -ldflags "-X main.VERSION=${VERSION}" -o statping --tags "netgo darwin" ./cmd
|
||||
mv $(BINARY_NAME) /usr/local/bin/$(BINARY_NAME)
|
||||
|
||||
generate:
|
||||
cd source && go generate
|
||||
go generate ./...
|
||||
|
||||
build-all: clean compile build-folders build-linux build-linux-arm build-darwin build-win compress-folders
|
||||
|
||||
build-win:
|
||||
CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc-posix CXX=x86_64-w64-mingw32-g++-posix GO111MODULE="on" GOOS=windows GOARCH=amd64 \
|
||||
go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION}" -o releases/statping-windows-amd64/statping.exe ./cmd
|
||||
CGO_ENABLED=1 CC=i686-w64-mingw32-gcc-posix CXX=i686-w64-mingw32-g++-posix GO111MODULE="on" GOOS=windows GOARCH=386 \
|
||||
go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION}" -o releases/statping-windows-386/statping.exe ./cmd
|
||||
build-deps:
|
||||
apt install -y libc6-armel-cross libc6-dev-armel-cross binutils-arm-linux-gnueabi \
|
||||
libncurses5-dev build-essential bison flex libssl-dev bc gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf \
|
||||
gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libsqlite3-dev gcc-mingw-w64 gcc-mingw-w64-x86-64
|
||||
|
||||
build-darwin:
|
||||
GO111MODULE="on" GOOS=darwin GOARCH=amd64 go build -a -ldflags "-s -w -X main.VERSION=${VERSION}" -o releases/statping-darwin-amd64/statping --tags "netgo darwin" ./cmd
|
||||
GO111MODULE="on" GOOS=darwin GOARCH=amd64 \
|
||||
go build -a -ldflags "-s -w -X main.VERSION=${VERSION}" -o releases/statping-darwin-amd64/statping --tags "netgo darwin" ./cmd
|
||||
|
||||
build-win:
|
||||
CGO_ENABLED=1 CC=x86_64-w64-mingw32-gcc CXX=x86_64-w64-mingw32-g++ GO111MODULE="on" GOOS=windows GOARCH=amd64 \
|
||||
go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION}" -o releases/statping-windows-amd64/statping.exe ./cmd
|
||||
CGO_ENABLED=1 CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++ GO111MODULE="on" GOOS=windows GOARCH=386 \
|
||||
go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION}" -o releases/statping-windows-386/statping.exe ./cmd
|
||||
|
||||
build-linux:
|
||||
CGO_ENABLED=1 GO111MODULE="on" GOOS=linux GOARCH=amd64 \
|
||||
|
@ -172,16 +202,18 @@ build-linux:
|
|||
go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION}" -o releases/statping-linux-386/statping --tags "netgo linux" ./cmd
|
||||
|
||||
build-linux-arm:
|
||||
CGO_ENABLED=1 CC=arm-linux-gnueabihf-gcc-6 CXX=arm-linux-gnueabihf-g++-6 GO111MODULE="on" GOOS=linux GOARCH=arm GOARM=6 \
|
||||
go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION}" -o releases/statping-linux-arm6/statping --tags "netgo" ./cmd
|
||||
CGO_ENABLED=1 CC=arm-linux-gnueabihf-gcc-6 CXX=arm-linux-gnueabihf-g++-6 GO111MODULE="on" GOOS=linux GOARCH=arm GOARM=7 \
|
||||
go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION}" -o releases/statping-linux-arm/statping ./cmd
|
||||
go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION}" -o releases/statping-linux-arm7/statping --tags "netgo" ./cmd
|
||||
CGO_ENABLED=1 CC=aarch64-linux-gnu-gcc-6 CXX=aarch64-linux-gnu-g++-6 GO111MODULE="on" GOOS=linux GOARCH=arm64 \
|
||||
go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION}" -o releases/statping-linux-arm64/statping ./cmd
|
||||
go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION}" -o releases/statping-linux-arm64/statping --tags "netgo" ./cmd
|
||||
|
||||
build-folders:
|
||||
mkdir build || true
|
||||
for os in windows darwin linux;\
|
||||
do \
|
||||
for arch in 386 amd64 arm arm64;\
|
||||
for arch in 386 amd64 arm6 arm7 arm64;\
|
||||
do \
|
||||
mkdir -p releases/statping-$$os-$$arch/; \
|
||||
done \
|
||||
|
@ -191,7 +223,7 @@ compress-folders:
|
|||
mkdir build || true
|
||||
for os in darwin linux;\
|
||||
do \
|
||||
for arch in 386 amd64 arm arm64;\
|
||||
for arch in 386 amd64 arm6 arm7 arm64;\
|
||||
do \
|
||||
chmod +x releases/statping-$$os-$$arch/statping || true; \
|
||||
tar -czf releases/statping-$$os-$$arch.tar.gz -C releases/statping-$$os-$$arch statping || true; \
|
||||
|
@ -208,26 +240,26 @@ compress-folders:
|
|||
|
||||
# remove files for a clean compile/build
|
||||
clean:
|
||||
rm -rf ./{logs,assets,plugins,*.db,config.yml,.sass-cache,config.yml,statping,build,.sass-cache,index.html,vendor}
|
||||
rm -rf cmd/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log,*.html,*.json}
|
||||
rm -rf core/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
rm -rf types/notifications/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
rm -rf handlers/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
rm -rf notifiers/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
rm -rf source/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
rm -rf types/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
rm -rf utils/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
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 frontend/cypress/videos
|
||||
rm -f coverage.* sass
|
||||
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,source/build,snap}
|
||||
@echo "Cleaning temporary and build folders..."
|
||||
@rm -rf ./{logs,assets,plugins,*.db,config.yml,.sass-cache,config.yml,statping,build,.sass-cache,index.html,vendor}
|
||||
@rm -rf cmd/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log,*.html,*.json}
|
||||
@rm -rf core/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
@rm -rf types/notifications/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
@rm -rf handlers/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
@rm -rf notifiers/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
@rm -rf source/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
@rm -rf types/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
@rm -rf utils/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
|
||||
@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 frontend/cypress/videos
|
||||
@rm -f coverage.* sass
|
||||
@rm -rf **/*.db-journal
|
||||
@find . -name "*.out" -type f -delete
|
||||
@find . -name "*.cpu" -type f -delete
|
||||
@find . -name "*.mem" -type f -delete
|
||||
@rm -rf {build,releases,tmp,source/build,snap,parts,prime,snap,stage}
|
||||
@echo "Finished removing temporary and build folders"
|
||||
|
||||
print_details:
|
||||
@echo \==== Statping Development Instance ====
|
||||
|
@ -279,11 +311,14 @@ post-release: frontend-build upload_to_s3 publish-homebrew dockerhub
|
|||
publish-homebrew:
|
||||
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token $(TRAVIS_API)" -d $(PUBLISH_BODY) https://api.travis-ci.com/repo/statping%2Fhomebrew-statping/requests
|
||||
|
||||
upload_to_s3: travis_s3_creds
|
||||
aws s3 cp ./source/dist/css $(ASSETS_BKT) --recursive --exclude "*" --include "*.css"
|
||||
aws s3 cp ./source/dist/js $(ASSETS_BKT) --recursive --exclude "*" --include "*.js"
|
||||
aws s3 cp ./source/dist/scss $(ASSETS_BKT) --recursive --exclude "*" --include "*.scss"
|
||||
aws s3 cp ./install.sh $(ASSETS_BKT)
|
||||
upload_to_s3:
|
||||
tar -czvf source.tar.gz source/
|
||||
aws s3 cp source.tar.gz s3://assets.statping.com/
|
||||
rm -rf source.tar.gz
|
||||
aws s3 cp source/dist/css/ s3://assets.statping.com/css/ --recursive --exclude "*" --include "*.css"
|
||||
aws s3 cp source/dist/js/ s3://assets.statping.com/js/ --recursive --exclude "*" --include "*.js"
|
||||
aws s3 cp source/dist/scss/ s3://assets.statping.com/scss/ --recursive --exclude "*" --include "*.scss"
|
||||
aws s3 cp install.sh s3://assets.statping.com/
|
||||
|
||||
travis_s3_creds:
|
||||
mkdir -p ~/.aws
|
||||
|
@ -296,11 +331,25 @@ valid-sign:
|
|||
gpg --verify statping.asc
|
||||
|
||||
sentry-release:
|
||||
sentry-cli releases new -p backend -p frontend v${VERSION}
|
||||
sentry-cli releases set-commits --auto v${VERSION}
|
||||
sentry-cli releases finalize v${VERSION}
|
||||
sentry-cli releases --org statping --project backend new v${VERSION}
|
||||
sentry-cli releases --org statping --project backend set-commits v${VERSION} --auto
|
||||
sentry-cli releases --org statping --project backend finalize v${VERSION}
|
||||
sentry-cli releases --org statping --project frontend new v${VERSION}
|
||||
sentry-cli releases --org statping --project frontend set-commits v${VERSION} --auto
|
||||
sentry-cli releases --org statping --project frontend finalize v${VERSION}
|
||||
|
||||
snapcraft: clean compile build-linux
|
||||
download-bins: clean
|
||||
mkdir build || true
|
||||
wget "https://github.com/statping/statping/releases/download/v${VERSION}/statping-linux-386.tar.gz"
|
||||
wget "https://github.com/statping/statping/releases/download/v${VERSION}/statping-linux-amd64.tar.gz"
|
||||
wget "https://github.com/statping/statping/releases/download/v${VERSION}/statping-linux-arm.tar.gz"
|
||||
wget "https://github.com/statping/statping/releases/download/v${VERSION}/statping-linux-arm64.tar.gz"
|
||||
mv statping-linux-386.tar.gz build/
|
||||
mv statping-linux-amd64.tar.gz build/
|
||||
mv statping-linux-arm.tar.gz build/
|
||||
mv statping-linux-arm64.tar.gz build/
|
||||
|
||||
snapcraft: download-bins
|
||||
mkdir snap
|
||||
mv snapcraft.yaml snap/
|
||||
PWD=$(shell pwd)
|
||||
|
@ -331,5 +380,54 @@ certs:
|
|||
-keyout key.pem \
|
||||
-subj "/C=US/ST=California/L=Santa Monica/O=Statping/OU=Development/CN=localhost"
|
||||
|
||||
.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
|
||||
xgo-latest:
|
||||
xgo --go $(GOVERSION) --targets=linux/amd64,linux/386,linux/arm-7,linux/arm-6,linux/arm64,windows/386,windows/amd64,darwin/386,darwin/amd64 --out='statping' --pkg='cmd' --dest=build --tags 'netgo' --ldflags='-X main.VERSION=${VERSION} -X main.COMMIT=$(COMMIT) -linkmode external -extldflags "-static"' .
|
||||
|
||||
buildx-latest: multiarch
|
||||
docker buildx create --name statping-latest
|
||||
docker buildx inspect --builder statping-latest --bootstrap
|
||||
docker buildx build --builder statping-latest --cache-from "type=local,src=/tmp/.buildx-cache" --cache-to "type=local,dest=/tmp/.buildx-cache" --pull --push --platform linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 -f Dockerfile -t statping/statping:latest -t statping/statping:v${VERSION} --build-arg=VERSION=${VERSION} --build-arg=COMMIT=${COMMIT} .
|
||||
docker buildx rm statping-latest
|
||||
|
||||
buildx-dev: multiarch
|
||||
docker buildx create --name statping-dev
|
||||
docker buildx inspect --builder statping-dev --bootstrap
|
||||
docker buildx build --builder statping-dev --cache-from "type=local,src=/tmp/.buildx-cache" --cache-to "type=local,dest=/tmp/.buildx-cache" --pull --push --platform linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 -f Dockerfile -t statping/statping:dev --build-arg=VERSION=${VERSION} --build-arg=COMMIT=${COMMIT} .
|
||||
docker buildx rm statping-dev
|
||||
|
||||
buildx-base: multiarch
|
||||
docker buildx create --name statping-base
|
||||
docker buildx inspect --builder statping-base --bootstrap
|
||||
docker buildx build --builder statping-base --cache-from "type=local,src=/tmp/.buildx-cache" --cache-to "type=local,dest=/tmp/.buildx-cache" --pull --push --platform linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6 -f Dockerfile.base -t statping/statping:base --build-arg=VERSION=${VERSION} --build-arg=COMMIT=${COMMIT} .
|
||||
docker buildx rm statping-base
|
||||
|
||||
multiarch:
|
||||
mkdir /tmp/.buildx-cache || true
|
||||
docker run --rm --privileged multiarch/qemu-user-static --reset -p yes
|
||||
|
||||
delve:
|
||||
go build -gcflags "all=-N -l" -o statping ./cmd
|
||||
dlv --listen=:2345 --headless=true --api-version=2 --accept-multiclient exec ./statping
|
||||
|
||||
check:
|
||||
@echo "Checking the programs required for the build are installed..."
|
||||
@echo "go: $(shell go version) - $(shell which go)" && go version >/dev/null 2>&1 || (echo "ERROR: go 1.14 is required."; exit 1)
|
||||
@echo "node: $(shell node --version) - $(shell which node)" && node --version >/dev/null 2>&1 || (echo "ERROR: node 12.x is required."; exit 1)
|
||||
@echo "yarn: $(shell yarn --version) - $(shell which yarn)" && yarn --version >/dev/null 2>&1 || (echo "ERROR: yarn is required."; exit 1)
|
||||
@echo "All required programs are installed!"
|
||||
|
||||
#sentry-release:
|
||||
# sentry-cli releases new -p $SENTRY_PROJECT $VERSION
|
||||
# sentry-cli releases set-commits --auto $VERSION
|
||||
# sentry-cli releases files $VERSION upload-sourcemaps dist
|
||||
|
||||
gen_help:
|
||||
for file in ./statping.wiki/*.md
|
||||
do
|
||||
# convert each file to html and place it in the html directory
|
||||
# --gfm == use github flavoured markdown
|
||||
marked -o html/$file.html $file --gfm
|
||||
done
|
||||
|
||||
.PHONY: all check build certs multiarch install-darwin go-build build-all buildx-base buildx-dev buildx-latest 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
|
||||
|
|
2
app.json
2
app.json
|
@ -2,6 +2,6 @@
|
|||
"name": "Statping",
|
||||
"description": "Statping Server Monitoring with Status Page",
|
||||
"repository": "https://github.com/statping/statping",
|
||||
"logo": "https://raw.githubusercontent.com/statping/statping/master/source/tmpl/banner.png",
|
||||
"logo": "https://assets.statping.com/banner.png",
|
||||
"keywords": ["statping", "server", "monitoring", "status page","golang", "go"]
|
||||
}
|
||||
|
|
223
cmd/cli.go
223
cmd/cli.go
|
@ -5,21 +5,23 @@ import (
|
|||
"encoding/json"
|
||||
"fmt"
|
||||
"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"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
importAll *bool
|
||||
)
|
||||
|
||||
func assetsCli() error {
|
||||
dir := utils.Directory
|
||||
if err := utils.InitLogs(); err != nil {
|
||||
|
@ -34,12 +36,81 @@ func assetsCli() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func systemctlCli(dir string, uninstall bool, port int64) error {
|
||||
location := "/etc/systemd/system/statping.service"
|
||||
|
||||
if uninstall {
|
||||
fmt.Println("systemctl stop statping")
|
||||
if _, _, err := utils.Command("systemctl", "stop", "statping"); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
fmt.Println("systemctl disable statping")
|
||||
if _, _, err := utils.Command("systemctl", "disable", "statping"); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
fmt.Println("Deleting systemctl: ", location)
|
||||
if err := utils.DeleteFile(location); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if ok := utils.FolderExists(dir); !ok {
|
||||
return errors.New("directory does not exist: " + dir)
|
||||
}
|
||||
|
||||
binPath, err := os.Executable()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
config := []byte(`[Unit]
|
||||
Description=Statping Server
|
||||
After=network.target
|
||||
After=systemd-user-sessions.service
|
||||
After=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Restart=always
|
||||
Environment="STATPING_DIR=` + dir + `"
|
||||
Environment="ALLOW_REPORTS=true"
|
||||
ExecStart=` + binPath + ` --port=` + utils.ToString(port) + `
|
||||
WorkingDirectory=` + dir + `
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target"
|
||||
`)
|
||||
fmt.Println("Saving systemctl service to: ", location)
|
||||
fmt.Printf("Using directory %s for Statping data\n", dir)
|
||||
fmt.Printf("Running on port %d\n", port)
|
||||
fmt.Printf("\n\n%s\n\n", string(config))
|
||||
if err := utils.SaveFile(location, config); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("systemctl daemon-reload")
|
||||
if _, _, err := utils.Command("systemctl", "daemon-reload"); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("systemctl enable statping")
|
||||
if _, _, err := utils.Command("systemctl", "enable", "statping.service"); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("systemctl start statping")
|
||||
if _, _, err := utils.Command("systemctl", "start", "statping"); err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Println("Statping was will auto start on reboots")
|
||||
fmt.Println("systemctl service: ", location)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func exportCli(args []string) error {
|
||||
filename := fmt.Sprintf("%s/statping-%s.json", utils.Directory, time.Now().Format("01-02-2006-1504"))
|
||||
filename := filepath.Join(utils.Directory, time.Now().Format("01-02-2006-1504")+".json")
|
||||
if len(args) == 1 {
|
||||
filename = fmt.Sprintf("%s/%s", utils.Directory, args)
|
||||
}
|
||||
var data []byte
|
||||
var data *handlers.ExportData
|
||||
if err := utils.InitLogs(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -56,10 +127,10 @@ func exportCli(args []string) error {
|
|||
if _, err := services.SelectAllServices(false); err != nil {
|
||||
return err
|
||||
}
|
||||
if data, err = ExportSettings(); err != nil {
|
||||
if data, err = handlers.ExportSettings(); err != nil {
|
||||
return fmt.Errorf("could not export settings: %v", err.Error())
|
||||
}
|
||||
if err = utils.SaveFile(filename, data); err != nil {
|
||||
if err = utils.SaveFile(filename, data.JSON()); err != nil {
|
||||
return fmt.Errorf("could not write file statping-export.json: %v", err.Error())
|
||||
}
|
||||
log.Infoln("Statping export file saved to ", filename)
|
||||
|
@ -73,7 +144,7 @@ func sassCli() error {
|
|||
if err := source.Assets(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := source.CompileSASS(source.DefaultScss...); err != nil {
|
||||
if err := source.CompileSASS(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -128,6 +199,10 @@ func resetCli() error {
|
|||
|
||||
func envCli() error {
|
||||
fmt.Println("Statping Configuration")
|
||||
fmt.Printf("Process ID: %d\n", os.Getpid())
|
||||
fmt.Printf("Running as user id: %d\n", os.Getuid())
|
||||
fmt.Printf("Running as group id: %d\n", os.Getgid())
|
||||
fmt.Printf("Statping Directory: %s\n", utils.Directory)
|
||||
for k, v := range utils.Params.AllSettings() {
|
||||
fmt.Printf("%s=%v\n", strings.ToUpper(k), v)
|
||||
}
|
||||
|
@ -153,20 +228,54 @@ func onceCli() error {
|
|||
func importCli(args []string) error {
|
||||
var err error
|
||||
var data []byte
|
||||
filename := args[1]
|
||||
if data, err = ioutil.ReadFile(filename); err != nil {
|
||||
if len(args) < 1 {
|
||||
return errors.New("invalid command arguments")
|
||||
}
|
||||
if data, err = ioutil.ReadFile(args[0]); err != nil {
|
||||
return err
|
||||
}
|
||||
var exportData ExportData
|
||||
var exportData handlers.ExportData
|
||||
if err = json.Unmarshal(data, &exportData); err != nil {
|
||||
return err
|
||||
}
|
||||
log.Printf("=== %s ===\n", exportData.Core.Name)
|
||||
if exportData.Config != nil {
|
||||
log.Printf("Configs: %s\n", exportData.Config.DbConn)
|
||||
if exportData.Config.DbUser != "" {
|
||||
log.Printf(" - Host: %s\n", exportData.Config.DbHost)
|
||||
log.Printf(" - User: %s\n", exportData.Config.DbUser)
|
||||
}
|
||||
}
|
||||
if len(exportData.Services) > 0 {
|
||||
log.Printf("Services: %d\n", len(exportData.Services))
|
||||
}
|
||||
if len(exportData.Checkins) > 0 {
|
||||
log.Printf("Checkins: %d\n", len(exportData.Checkins))
|
||||
}
|
||||
if len(exportData.Groups) > 0 {
|
||||
log.Printf("Groups: %d\n", len(exportData.Groups))
|
||||
}
|
||||
if len(exportData.Messages) > 0 {
|
||||
log.Printf("Messages: %d\n", len(exportData.Messages))
|
||||
}
|
||||
if len(exportData.Incidents) > 0 {
|
||||
log.Printf("Incidents: %d\n", len(exportData.Incidents))
|
||||
}
|
||||
if len(exportData.Users) > 0 {
|
||||
log.Printf("Users: %d\n", len(exportData.Users))
|
||||
}
|
||||
|
||||
if exportData.Config != nil {
|
||||
if ask("Create config.yml file from Configs?") {
|
||||
log.Printf("Database Host: %s\n", exportData.Config.DbHost)
|
||||
log.Printf("Database Port: %d\n", exportData.Config.DbPort)
|
||||
log.Printf("Database User: %s\n", exportData.Config.DbUser)
|
||||
log.Printf("Database Password: %s\n", exportData.Config.DbPass)
|
||||
if err := exportData.Config.Save(utils.Directory); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
config, err := configs.LoadConfigs(configFile)
|
||||
if err != nil {
|
||||
|
@ -175,21 +284,22 @@ func importCli(args []string) error {
|
|||
if err = configs.ConnectConfigs(config, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if data, err = ExportSettings(); err != nil {
|
||||
return fmt.Errorf("could not export settings: %v", err.Error())
|
||||
if ask("Create database rows and sample data?") {
|
||||
if err := config.ResetCore(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if ask("Import Core settings?") {
|
||||
c := exportData.Core
|
||||
if err := c.Update(); err != nil {
|
||||
return err
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
for _, s := range exportData.Groups {
|
||||
if ask(fmt.Sprintf("Import Group '%s'?", s.Name)) {
|
||||
s.Id = 0
|
||||
if err := s.Create(); err != nil {
|
||||
return err
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -197,7 +307,7 @@ func importCli(args []string) error {
|
|||
if ask(fmt.Sprintf("Import Service '%s'?", s.Name)) {
|
||||
s.Id = 0
|
||||
if err := s.Create(); err != nil {
|
||||
return err
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -205,7 +315,7 @@ func importCli(args []string) error {
|
|||
if ask(fmt.Sprintf("Import Checkin '%s'?", s.Name)) {
|
||||
s.Id = 0
|
||||
if err := s.Create(); err != nil {
|
||||
return err
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -213,7 +323,7 @@ func importCli(args []string) error {
|
|||
if ask(fmt.Sprintf("Import Message '%s'?", s.Title)) {
|
||||
s.Id = 0
|
||||
if err := s.Create(); err != nil {
|
||||
return err
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -221,7 +331,7 @@ func importCli(args []string) error {
|
|||
if ask(fmt.Sprintf("Import User '%s'?", s.Username)) {
|
||||
s.Id = 0
|
||||
if err := s.Create(); err != nil {
|
||||
return err
|
||||
log.Errorln(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -230,6 +340,7 @@ func importCli(args []string) error {
|
|||
}
|
||||
|
||||
func ask(format string) bool {
|
||||
|
||||
fmt.Printf(fmt.Sprintf(format + " [y/N]: "))
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
text, _ := reader.ReadString('\n')
|
||||
|
@ -375,15 +486,16 @@ type gitUploader struct {
|
|||
|
||||
// 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"`
|
||||
}
|
||||
//type ExportData struct {
|
||||
// Config *configs.DbConfig `json:"config"`
|
||||
// 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
|
||||
|
@ -393,28 +505,35 @@ type ExportData struct {
|
|||
// - Services
|
||||
// - Groups
|
||||
// - Messages
|
||||
func ExportSettings() ([]byte, error) {
|
||||
c, err := core.Select()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var srvs []services.Service
|
||||
for _, s := range services.AllInOrder() {
|
||||
s.Failures = nil
|
||||
srvs = append(srvs, s)
|
||||
}
|
||||
data := ExportData{
|
||||
Core: c,
|
||||
Notifiers: core.App.Notifications,
|
||||
Checkins: checkins.All(),
|
||||
Users: users.All(),
|
||||
Services: srvs,
|
||||
Groups: groups.All(),
|
||||
Messages: messages.All(),
|
||||
}
|
||||
export, err := json.Marshal(data)
|
||||
return export, err
|
||||
}
|
||||
//func ExportSettings() ([]byte, error) {
|
||||
// c, err := core.Select()
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
// var srvs []services.Service
|
||||
// for _, s := range services.AllInOrder() {
|
||||
// s.Failures = nil
|
||||
// srvs = append(srvs, s)
|
||||
// }
|
||||
//
|
||||
// cfg, err := configs.LoadConfigs(configFile)
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// data := ExportData{
|
||||
// Config: cfg,
|
||||
// Core: c,
|
||||
// Notifiers: core.App.Notifications,
|
||||
// Checkins: checkins.All(),
|
||||
// Users: users.All(),
|
||||
// Services: srvs,
|
||||
// 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 {
|
||||
|
|
|
@ -2,10 +2,12 @@ package main
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"github.com/statping/statping/source"
|
||||
"github.com/statping/statping/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -18,16 +20,15 @@ func init() {
|
|||
}
|
||||
|
||||
func TestStatpingDirectory(t *testing.T) {
|
||||
dir := utils.Directory
|
||||
require.NotContains(t, dir, "/cmd")
|
||||
require.NotEmpty(t, dir)
|
||||
|
||||
dir = utils.Params.GetString("STATPING_DIR")
|
||||
require.NotContains(t, dir, "/cmd")
|
||||
require.NotEmpty(t, dir)
|
||||
}
|
||||
|
||||
func TestEnvCLI(t *testing.T) {
|
||||
os.Setenv("API_SECRET", "demoapisecret123")
|
||||
os.Setenv("SASS", "/usr/local/bin/sass")
|
||||
|
||||
cmd := rootCmd
|
||||
b := bytes.NewBufferString("")
|
||||
cmd.SetOut(b)
|
||||
|
@ -39,6 +40,12 @@ func TestEnvCLI(t *testing.T) {
|
|||
assert.Contains(t, string(out), VERSION)
|
||||
assert.Contains(t, utils.Directory, string(out))
|
||||
assert.Contains(t, "SAMPLE_DATA=true", string(out))
|
||||
assert.Contains(t, "API_SECRET=demoapisecret123", string(out))
|
||||
assert.Contains(t, "STATPING_DIR="+dir, string(out))
|
||||
assert.Contains(t, "SASS=/usr/local/bin/sass", string(out))
|
||||
|
||||
os.Unsetenv("API_SECRET")
|
||||
os.Unsetenv("SASS")
|
||||
}
|
||||
|
||||
func TestVersionCLI(t *testing.T) {
|
||||
|
@ -58,16 +65,26 @@ func TestAssetsCLI(t *testing.T) {
|
|||
b := bytes.NewBufferString("")
|
||||
cmd.SetOut(b)
|
||||
cmd.SetArgs([]string{"assets"})
|
||||
cmd.Execute()
|
||||
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")
|
||||
for _, f := range source.RequiredFiles {
|
||||
assert.FileExists(t, utils.Directory+"/assets/"+f)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdateCLI(t *testing.T) {
|
||||
cmd := rootCmd
|
||||
b := bytes.NewBufferString("")
|
||||
cmd.SetOut(b)
|
||||
cmd.SetArgs([]string{"update"})
|
||||
err := cmd.Execute()
|
||||
require.Nil(t, err)
|
||||
out, err := ioutil.ReadAll(b)
|
||||
require.Nil(t, err)
|
||||
assert.Contains(t, string(out), VERSION)
|
||||
}
|
||||
|
||||
func TestHelpCLI(t *testing.T) {
|
||||
|
|
|
@ -1,22 +1,69 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/statping/statping/utils"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "statping",
|
||||
Version: VERSION,
|
||||
Short: "A simple Application Status Monitor that is opensource and lightweight.",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
start()
|
||||
},
|
||||
}
|
||||
|
||||
var updateCmd = &cobra.Command{
|
||||
Use: "update",
|
||||
Example: "statping update",
|
||||
Short: "Update to the latest version",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
log.Infoln("Updating Statping to the latest version...")
|
||||
log.Infoln("curl -o- -L https://statping.com/install.sh | bash")
|
||||
curl, err := exec.LookPath("curl")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bash, err := exec.LookPath("bash")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ree := bytes.NewBuffer(nil)
|
||||
|
||||
c1 := exec.Command(curl, "-o-", "-L", "https://statping.com/install.sh")
|
||||
c2 := exec.Command(bash)
|
||||
|
||||
r, w := io.Pipe()
|
||||
c1.Stdout = w
|
||||
c2.Stdin = r
|
||||
|
||||
var b2 bytes.Buffer
|
||||
c2.Stdout = &b2
|
||||
|
||||
c1.Start()
|
||||
c2.Start()
|
||||
c1.Wait()
|
||||
w.Close()
|
||||
c2.Wait()
|
||||
io.Copy(ree, &b2)
|
||||
|
||||
log.Infoln(ree.String())
|
||||
os.Exit(0)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var versionCmd = &cobra.Command{
|
||||
Use: "version",
|
||||
Example: "statping version",
|
||||
Short: "Print the version number of Statping",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
if COMMIT != "" {
|
||||
|
@ -28,8 +75,37 @@ var versionCmd = &cobra.Command{
|
|||
},
|
||||
}
|
||||
|
||||
var systemctlCmd = &cobra.Command{
|
||||
Use: "systemctl [install/uninstall]",
|
||||
Example: "statping systemctl install",
|
||||
Short: "Install or Uninstall systemctl services",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if args[1] == "install" {
|
||||
if len(args) < 3 {
|
||||
return errors.New("requires 'install <working_path> <port>'")
|
||||
}
|
||||
}
|
||||
port := utils.ToInt(args[2])
|
||||
if port == 0 {
|
||||
port = 80
|
||||
}
|
||||
if err := systemctlCli(args[1], args[0] == "uninstall", port); err != nil {
|
||||
return err
|
||||
}
|
||||
os.Exit(0)
|
||||
return nil
|
||||
},
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 2 {
|
||||
return errors.New("requires 'install <working_path>' or 'uninstall' as arguments")
|
||||
}
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
var assetsCmd = &cobra.Command{
|
||||
Use: "assets",
|
||||
Example: "statping assets",
|
||||
Short: "Dump all assets used locally to be edited",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := assetsCli(); err != nil {
|
||||
|
@ -42,6 +118,7 @@ var assetsCmd = &cobra.Command{
|
|||
|
||||
var exportCmd = &cobra.Command{
|
||||
Use: "export",
|
||||
Example: "statping export",
|
||||
Short: "Exports your Statping settings to a 'statping-export.json' file.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := exportCli(args); err != nil {
|
||||
|
@ -54,6 +131,7 @@ var exportCmd = &cobra.Command{
|
|||
|
||||
var sassCmd = &cobra.Command{
|
||||
Use: "sass",
|
||||
Example: "statping sass",
|
||||
Short: "Compile .scss files into the css directory",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := sassCli(); err != nil {
|
||||
|
@ -66,6 +144,7 @@ var sassCmd = &cobra.Command{
|
|||
|
||||
var envCmd = &cobra.Command{
|
||||
Use: "env",
|
||||
Example: "statping env",
|
||||
Short: "Return the configs that will be ran",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := envCli(); err != nil {
|
||||
|
@ -78,6 +157,7 @@ var envCmd = &cobra.Command{
|
|||
|
||||
var resetCmd = &cobra.Command{
|
||||
Use: "reset",
|
||||
Example: "statping reset",
|
||||
Short: "Start a fresh copy of Statping",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := resetCli(); err != nil {
|
||||
|
@ -90,6 +170,7 @@ var resetCmd = &cobra.Command{
|
|||
|
||||
var onceCmd = &cobra.Command{
|
||||
Use: "once",
|
||||
Example: "statping once",
|
||||
Short: "Check all services 1 time and then quit",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := onceCli(); err != nil {
|
||||
|
@ -102,6 +183,7 @@ var onceCmd = &cobra.Command{
|
|||
|
||||
var importCmd = &cobra.Command{
|
||||
Use: "import [.json file]",
|
||||
Example: "statping import backup.json",
|
||||
Short: "Imports settings from a previously saved JSON file.",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
if err := importCli(args); err != nil {
|
||||
|
|
49
cmd/main.go
49
cmd/main.go
|
@ -29,16 +29,20 @@ var (
|
|||
|
||||
func init() {
|
||||
stopped = make(chan bool, 1)
|
||||
core.New(VERSION)
|
||||
core.New(VERSION, COMMIT)
|
||||
utils.InitEnvs()
|
||||
utils.Params.Set("VERSION", VERSION)
|
||||
utils.Params.Set("COMMIT", COMMIT)
|
||||
|
||||
rootCmd.AddCommand(versionCmd)
|
||||
rootCmd.AddCommand(updateCmd)
|
||||
rootCmd.AddCommand(assetsCmd)
|
||||
rootCmd.AddCommand(exportCmd)
|
||||
rootCmd.AddCommand(importCmd)
|
||||
rootCmd.AddCommand(sassCmd)
|
||||
rootCmd.AddCommand(onceCmd)
|
||||
rootCmd.AddCommand(envCmd)
|
||||
rootCmd.AddCommand(systemctlCmd)
|
||||
rootCmd.AddCommand(resetCmd)
|
||||
|
||||
parseFlags(rootCmd)
|
||||
|
@ -96,39 +100,8 @@ func start() {
|
|||
exit(err)
|
||||
}
|
||||
|
||||
if !confgs.Db.HasTable("core") {
|
||||
var srvs int64
|
||||
if confgs.Db.HasTable(&services.Service{}) {
|
||||
confgs.Db.Model(&services.Service{}).Count(&srvs)
|
||||
if srvs > 0 {
|
||||
exit(errors.Wrap(err, "there are already services setup."))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if err := confgs.DropDatabase(); err != nil {
|
||||
exit(errors.Wrap(err, "error dropping database"))
|
||||
}
|
||||
|
||||
if err := confgs.CreateDatabase(); err != nil {
|
||||
exit(errors.Wrap(err, "error creating database"))
|
||||
}
|
||||
|
||||
if err := configs.CreateAdminUser(confgs); err != nil {
|
||||
exit(errors.Wrap(err, "error creating default admin user"))
|
||||
}
|
||||
|
||||
if utils.Params.GetBool("SAMPLE_DATA") {
|
||||
log.Infoln("Adding Sample Data")
|
||||
if err := configs.TriggerSamples(); err != nil {
|
||||
exit(errors.Wrap(err, "error adding sample data"))
|
||||
}
|
||||
} else {
|
||||
if err := core.Samples(); err != nil {
|
||||
exit(errors.Wrap(err, "error added core details"))
|
||||
}
|
||||
}
|
||||
|
||||
if err = confgs.ResetCore(); err != nil {
|
||||
exit(err)
|
||||
}
|
||||
|
||||
if err = confgs.DatabaseChanges(); err != nil {
|
||||
|
@ -175,20 +148,20 @@ func InitApp() error {
|
|||
if _, err := core.Select(); err != nil {
|
||||
return err
|
||||
}
|
||||
// init Sentry error monitoring (its useful)
|
||||
utils.SentryInit(core.App.AllowReports.Bool)
|
||||
// init prometheus metrics
|
||||
metrics.InitMetrics()
|
||||
// connect each notifier, added them into database if needed
|
||||
notifiers.InitNotifiers()
|
||||
// select all services in database and store services in a mapping of Service pointers
|
||||
if _, err := services.SelectAllServices(true); err != nil {
|
||||
return err
|
||||
}
|
||||
// start routines for each service checking process
|
||||
services.CheckServices()
|
||||
// connect each notifier, added them into database if needed
|
||||
notifiers.InitNotifiers()
|
||||
// start routine to delete old records (failures, hits)
|
||||
go database.Maintenance()
|
||||
// init Sentry error monitoring (its useful)
|
||||
utils.SentryInit(&VERSION, core.App.AllowReports.Bool)
|
||||
core.App.Setup = true
|
||||
core.App.Started = utils.Now()
|
||||
return nil
|
||||
|
|
|
@ -169,10 +169,6 @@ func Available(db Database) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func AmountGreaterThan1000(db *gorm.DB) *gorm.DB {
|
||||
return db.Where("service = ?", 1000)
|
||||
}
|
||||
|
||||
func (it *Db) MultipleSelects(args ...string) Database {
|
||||
joined := strings.Join(args, ", ")
|
||||
return it.Select(joined)
|
||||
|
@ -181,6 +177,7 @@ func (it *Db) MultipleSelects(args ...string) Database {
|
|||
type Db struct {
|
||||
Database *gorm.DB
|
||||
Type string
|
||||
ReadOnly bool
|
||||
}
|
||||
|
||||
// Openw is a drop-in replacement for Open()
|
||||
|
@ -223,6 +220,9 @@ func OpenTester() (Database, error) {
|
|||
default:
|
||||
dbString = fmt.Sprintf("file:%s?mode=memory&cache=shared", utils.RandomString(12))
|
||||
}
|
||||
if utils.Params.IsSet("DB_DSN") {
|
||||
dbString = utils.Params.GetString("DB_DSN")
|
||||
}
|
||||
newDb, err := Openw(testDB, dbString)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -239,6 +239,7 @@ func Wrap(db *gorm.DB) Database {
|
|||
return &Db{
|
||||
Database: db,
|
||||
Type: db.Dialect().GetName(),
|
||||
ReadOnly: utils.Params.GetBool("READ_ONLY"),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -379,14 +380,26 @@ func (it *Db) Related(value interface{}, foreignKeys ...string) Database {
|
|||
}
|
||||
|
||||
func (it *Db) FirstOrInit(out interface{}, where ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.FirstOrInit(out, where...))
|
||||
}
|
||||
|
||||
func (it *Db) FirstOrCreate(out interface{}, where ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.FirstOrCreate(out, where...))
|
||||
}
|
||||
|
||||
func (it *Db) Update(attrs ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.Update(attrs...))
|
||||
}
|
||||
|
||||
|
@ -395,22 +408,42 @@ func (it *Db) Updates(values interface{}, ignoreProtectedAttrs ...bool) Database
|
|||
}
|
||||
|
||||
func (it *Db) UpdateColumn(attrs ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.UpdateColumn(attrs...))
|
||||
}
|
||||
|
||||
func (it *Db) UpdateColumns(values interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.UpdateColumns(values))
|
||||
}
|
||||
|
||||
func (it *Db) Save(value interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.Save(value))
|
||||
}
|
||||
|
||||
func (it *Db) Create(value interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.Create(value))
|
||||
}
|
||||
|
||||
func (it *Db) Delete(value interface{}, where ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.Delete(value, where...))
|
||||
}
|
||||
|
||||
|
@ -435,14 +468,26 @@ func (it *Db) Debug() Database {
|
|||
}
|
||||
|
||||
func (it *Db) Begin() Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.Begin())
|
||||
}
|
||||
|
||||
func (it *Db) Commit() Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.Commit())
|
||||
}
|
||||
|
||||
func (it *Db) Rollback() Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.Rollback())
|
||||
}
|
||||
|
||||
|
@ -455,14 +500,26 @@ func (it *Db) RecordNotFound() bool {
|
|||
}
|
||||
|
||||
func (it *Db) CreateTable(values ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.CreateTable(values...))
|
||||
}
|
||||
|
||||
func (it *Db) DropTable(values ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.DropTable(values...))
|
||||
}
|
||||
|
||||
func (it *Db) DropTableIfExists(values ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.DropTableIfExists(values...))
|
||||
}
|
||||
|
||||
|
@ -471,26 +528,50 @@ func (it *Db) HasTable(value interface{}) bool {
|
|||
}
|
||||
|
||||
func (it *Db) AutoMigrate(values ...interface{}) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.AutoMigrate(values...))
|
||||
}
|
||||
|
||||
func (it *Db) ModifyColumn(column string, typ string) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.ModifyColumn(column, typ))
|
||||
}
|
||||
|
||||
func (it *Db) DropColumn(column string) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.DropColumn(column))
|
||||
}
|
||||
|
||||
func (it *Db) AddIndex(indexName string, columns ...string) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.AddIndex(indexName, columns...))
|
||||
}
|
||||
|
||||
func (it *Db) AddUniqueIndex(indexName string, columns ...string) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.AddUniqueIndex(indexName, columns...))
|
||||
}
|
||||
|
||||
func (it *Db) RemoveIndex(indexName string) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.RemoveIndex(indexName))
|
||||
}
|
||||
|
||||
|
@ -519,6 +600,10 @@ func (it *Db) SetJoinTableHandler(source interface{}, column string, handler gor
|
|||
}
|
||||
|
||||
func (it *Db) AddForeignKey(field string, dest string, onDelete string, onUpdate string) Database {
|
||||
if it.ReadOnly {
|
||||
it.Database.Error = nil
|
||||
return Wrap(it.Database)
|
||||
}
|
||||
return Wrap(it.Database.AddForeignKey(field, dest, onDelete, onUpdate))
|
||||
}
|
||||
|
||||
|
|
|
@ -39,7 +39,7 @@ type GroupQuery struct {
|
|||
}
|
||||
|
||||
func (b GroupQuery) Find(data interface{}) error {
|
||||
return b.db.Find(data).Error()
|
||||
return b.db.Order("id DESC").Find(data).Error()
|
||||
}
|
||||
|
||||
func (b GroupQuery) Database() Database {
|
||||
|
@ -70,27 +70,28 @@ func (t *TimeVar) ToValues() ([]*TimeValue, error) {
|
|||
}
|
||||
|
||||
// GraphData will return all hits or failures
|
||||
func (g *GroupQuery) GraphData(by By) ([]*TimeValue, error) {
|
||||
dbQuery := g.db.MultipleSelects(
|
||||
g.db.SelectByTime(g.Group),
|
||||
func (b *GroupQuery) GraphData(by By) ([]*TimeValue, error) {
|
||||
b.db = b.db.MultipleSelects(
|
||||
b.db.SelectByTime(b.Group),
|
||||
by.String(),
|
||||
).Group("timeframe").Order("timeframe", true)
|
||||
|
||||
g.db = dbQuery
|
||||
|
||||
caller, err := g.ToTimeValue()
|
||||
caller, err := b.ToTimeValue()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if g.FillEmpty {
|
||||
return caller.FillMissing(g.Start, g.End)
|
||||
if b.FillEmpty {
|
||||
return caller.FillMissing(b.Start, b.End)
|
||||
}
|
||||
return caller.ToValues()
|
||||
}
|
||||
|
||||
func (g *GroupQuery) ToTimeValue() (*TimeVar, error) {
|
||||
rows, err := g.db.Rows()
|
||||
// ToTimeValue will format the SQL rows into a JSON format for the API.
|
||||
// [{"timestamp": "2006-01-02T15:04:05Z", "amount": 468293}]
|
||||
// TODO redo this entire function, use better SQL query to group by time
|
||||
func (b *GroupQuery) ToTimeValue() (*TimeVar, error) {
|
||||
rows, err := b.db.Rows()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -101,8 +102,8 @@ func (g *GroupQuery) ToTimeValue() (*TimeVar, error) {
|
|||
if err := rows.Scan(&timeframe, &amount); err != nil {
|
||||
log.Error(err, timeframe)
|
||||
}
|
||||
trueTime, _ := g.db.ParseTime(timeframe)
|
||||
newTs := types.FixedTime(trueTime, g.Group)
|
||||
trueTime, _ := b.db.ParseTime(timeframe)
|
||||
newTs := types.FixedTime(trueTime, b.Group)
|
||||
|
||||
tv := &TimeValue{
|
||||
Timeframe: newTs,
|
||||
|
@ -110,33 +111,32 @@ func (g *GroupQuery) ToTimeValue() (*TimeVar, error) {
|
|||
}
|
||||
data = append(data, tv)
|
||||
}
|
||||
return &TimeVar{g, data}, nil
|
||||
return &TimeVar{b, data}, nil
|
||||
}
|
||||
|
||||
func (t *TimeVar) FillMissing(current, end time.Time) ([]*TimeValue, error) {
|
||||
timeMap := make(map[string]int64)
|
||||
var validSet []*TimeValue
|
||||
dur := t.g.Group
|
||||
for _, v := range t.data {
|
||||
timeMap[v.Timeframe] = v.Amount
|
||||
}
|
||||
|
||||
for {
|
||||
currentStr := types.FixedTime(current, t.g.Group)
|
||||
|
||||
for {
|
||||
var amount int64
|
||||
if timeMap[currentStr] != 0 {
|
||||
amount = timeMap[currentStr]
|
||||
}
|
||||
|
||||
validSet = append(validSet, &TimeValue{
|
||||
Timeframe: currentStr,
|
||||
Amount: amount,
|
||||
})
|
||||
current = current.Add(t.g.Group)
|
||||
if current.After(end) {
|
||||
break
|
||||
}
|
||||
current = current.Add(dur)
|
||||
currentStr = types.FixedTime(current, t.g.Group)
|
||||
}
|
||||
|
||||
return validSet, nil
|
||||
|
@ -233,10 +233,6 @@ func ParseQueries(r *http.Request, o isObject) (*GroupQuery, error) {
|
|||
if endField == 0 {
|
||||
query.End = utils.Now()
|
||||
}
|
||||
if query.End.After(utils.Now()) {
|
||||
query.End = utils.Now()
|
||||
}
|
||||
|
||||
if query.Limit != 0 {
|
||||
q = q.Limit(query.Limit)
|
||||
}
|
||||
|
|
|
@ -16,6 +16,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
|
||||
// env: REMOVE_AFTER - golang duration parsed time for deleting records older than REMOVE_AFTER duration from now
|
||||
// env: CLEANUP_INTERVAL - golang duration parsed time for checking old records routine
|
||||
func Maintenance() {
|
||||
dur := utils.Params.GetDuration("REMOVE_AFTER")
|
||||
interval := utils.Params.GetDuration("CLEANUP_INTERVAL")
|
||||
|
|
|
@ -19,10 +19,9 @@ func (it *Db) ParseTime(t string) (time.Time, error) {
|
|||
}
|
||||
}
|
||||
|
||||
// FormatTime returns the timestamp in the same format as the DATETIME column in database
|
||||
func (it *Db) FormatTime(t time.Time) string {
|
||||
switch it.Type {
|
||||
case "mysql":
|
||||
return t.Format("2006-01-02 15:04:05")
|
||||
case "postgres":
|
||||
return t.Format("2006-01-02 15:04:05.999999999")
|
||||
default:
|
||||
|
@ -30,6 +29,7 @@ func (it *Db) FormatTime(t time.Time) string {
|
|||
}
|
||||
}
|
||||
|
||||
// SelectByTime returns an SQL query that will group "created_at" column by x seconds and returns as "timeframe"
|
||||
func (it *Db) SelectByTime(increment time.Duration) string {
|
||||
seconds := int64(increment.Seconds())
|
||||
switch it.Type {
|
||||
|
@ -41,33 +41,3 @@ func (it *Db) SelectByTime(increment time.Duration) string {
|
|||
return fmt.Sprintf("datetime((strftime('%%s', created_at) / %d) * %d, 'unixepoch') as timeframe", seconds, seconds)
|
||||
}
|
||||
}
|
||||
|
||||
func (it *Db) correctTimestamp(increment string) string {
|
||||
var timestamper string
|
||||
switch increment {
|
||||
case "second":
|
||||
timestamper = "%Y-%m-%d %H:%M:%S"
|
||||
case "minute":
|
||||
timestamper = "%Y-%m-%d %H:%M:00"
|
||||
case "hour":
|
||||
timestamper = "%Y-%m-%d %H:00:00"
|
||||
case "day":
|
||||
timestamper = "%Y-%m-%d 00:00:00"
|
||||
case "month":
|
||||
timestamper = "%Y-%m-01 00:00:00"
|
||||
case "year":
|
||||
timestamper = "%Y-01-01 00:00:00"
|
||||
default:
|
||||
timestamper = "%Y-%m-%d 00:00:00"
|
||||
}
|
||||
|
||||
switch it.Type {
|
||||
case "mysql":
|
||||
case "second":
|
||||
timestamper = "%Y-%m-%d %H:%i:%S"
|
||||
case "minute":
|
||||
timestamper = "%Y-%m-%d %H:%i:00"
|
||||
}
|
||||
|
||||
return timestamper
|
||||
}
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: statping
|
||||
spec:
|
||||
ports:
|
||||
- port: 8080
|
||||
selector:
|
||||
app: statping
|
||||
clusterIP: None
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: statping
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: statping
|
||||
strategy:
|
||||
type: Recreate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: statping
|
||||
spec:
|
||||
containers:
|
||||
- image: statping/statping
|
||||
name: statping
|
||||
env:
|
||||
- name: ALLOW_REPORTS
|
||||
value: "true"
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
name: statping
|
||||
volumeMounts:
|
||||
- name: statping-storage
|
||||
mountPath: /app
|
||||
volumes:
|
||||
- name: statping-storage
|
||||
persistentVolumeClaim:
|
||||
claimName: statping-claim
|
|
@ -0,0 +1,21 @@
|
|||
[
|
||||
{
|
||||
"type": 1,
|
||||
"title": "Statping",
|
||||
"restart_policy": "unless-stopped",
|
||||
"description": "Service monitoring with an easy to use status page and mobile app",
|
||||
"logo": "https://assets.statping.com/icon.png",
|
||||
"image": "statping/statping:latest",
|
||||
"platform": "linux",
|
||||
"categories": ["monitoring"],
|
||||
"administrator_only": false,
|
||||
"ports": [
|
||||
"8080:8080/tcp"
|
||||
],
|
||||
"volumes": [
|
||||
{
|
||||
"container": "/app"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -2,7 +2,7 @@
|
|||
"info": {
|
||||
"_postman_id": "3c6a7841-0b39-4878-a3a6-1c76832b7679",
|
||||
"name": "Statping",
|
||||
"description": "The Statping API allows you to programmatically access data on your Statping server.\n\nThe easiest way to get started with the API is by running your own Docker or local instance of the Statping server.\n\n<img width=\"100%\" src=\"https://img.cjx.io/statuptokenbalance.gif\">\n\n# Authentication\n\nAn API Key is required to be sent as part of every request to the Statping API, by using the `Authorization` and the API Secret Key as a header.\n\n> If you do not have an API Secret Key, you can find it on the Settings page.\n\nYou can Authenticate by implementing the following...\n- Using the `Authorization` header with API Secret Key\n- Setting `GO_ENV` to `test` to bypass all authentication\n- Adding `?api=<keyhere>` URL Query along with the API Secret Key\n- Being logged into Statping as an admin (using JWT sessions/cookies)\n\n> Authentication will create a cookie named `statping_auth`.\n\n# Environment Variables\nStatping includes many environment variables that can give you more control over your instance. Please view the [Environment Variables Wiki](https://github.com/statping/statping/wiki/Environment-Variables) page to view a complete list. Below are a couple important ones...\n- `STATPING_DIR` - Statping's working directory. By default, this will be set to the current working directory. This path will contain the `config.yml` file, `logs`, and `assets` folder. \n- `SASS` - Absolute path to the `sass` executable. By default it will attempt to find `sass` in your `$PATH`. \n\n# Demo\nYou can checkout the Statping Demo instance at [https://demo.statping.com](https://demo.statping.com). All features are available for you to experiment with. The API Secret Key is `demoapisecret123`. Since this instance is public, it will be reset with sample data **every 90 minutes**.\n",
|
||||
"description": "The Statping API allows you to programmatically access data on your Statping server.\n\nThe easiest way to get started with the API is by running your own Docker or local instance of the Statping server.\n\n<img width=\"100%\" src=\"https://img.cjx.io/statuptokenbalance.gif\">\n\n# Authentication\n\nAn API Key is required to be sent as part of every request to the Statping API, by using the `Authorization` and the API Secret Key as a header.\n\n> If you do not have an API Secret Key, you can find it on the Settings page.\n\nYou can Authenticate by implementing the following...\n- Using the `Authorization` header with API Secret Key\n- Setting `GO_ENV` to `test` to bypass all authentication\n- Adding `?api=<keyhere>` URL Query along with the API Secret Key\n- Adding `?api=<userkey>` URL Query along with the API Key for a user\n- Being logged into Statping as an admin (using JWT sessions/cookies)\n\n> Authentication will create a cookie named `statping_auth`.\n\n# Environment Variables\nStatping includes many environment variables that can give you more control over your instance. Please view the [Environment Variables Wiki](https://github.com/statping/statping/wiki/Environment-Variables) page to view a complete list. Below are a couple important ones...\n- `STATPING_DIR` - Statping's working directory. By default, this will be set to the current working directory. This path will contain the `config.yml` file, `logs`, and `assets` folder. \n- `SASS` - Absolute path to the `sass` executable. By default it will attempt to find `sass` in your `$PATH`. \n\n# Demo\nYou can checkout the Statping Demo instance at [https://demo.statping.com](https://demo.statping.com). All features are available for you to experiment with. The API Secret Key is `demoapisecret123`. Since this instance is public, it will be reset with sample data **every 90 minutes**.\n",
|
||||
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
|
||||
},
|
||||
"item": [
|
||||
|
@ -786,6 +786,105 @@
|
|||
"description": "You can create custom badges with dynamic information by using [Shields.io](https://shields.io/) and parsing JSON fields with [JSONPath](http://jsonpath.com/). \n\n## Examples\n\n#### Service Uptime Percent\n<img src=\"https://img.shields.io/badge/dynamic/json?color=blue&label=%20Statping%20Uptime&query=%24.online_24_hours&url=https%3A%2F%2Fdemo.statping.com%2Fapi%2Fservices%2F2&suffix=%\">\n\n- URL: [https://demo.statping.com/api/services/2](https://demo.statping.com/api/services/2)\n- JSON Path: `$.online_24_hours`\n- Suffix: `%`\n\n```\nhttps://img.shields.io/badge/dynamic/json?color=blue&label=%20Statping%20Uptime&query=%24.online_24_hours&url=https%3A%2F%2Fdemo.statping.com%2Fapi%2Fservices%2F2&suffix=%\n```\n\n#### Count Services\n<img src=\"https://img.shields.io/badge/dynamic/json?color=purple&label=Demo%20Site&query=%24.services&url=https://demo.statping.com/health&suffix=%20services\">\n\n- URL: [https://demo.statping.com/health](https://demo.statping.com/health)\n- JSON Path: `$.services`\n- Suffix: ` services`\n\n```\nhttps://img.shields.io/badge/dynamic/json?color=purple&label=Demo%20Site&query=%24.services&url=https://demo.statping.com/health&suffix=%20services\n```"
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Send Push Notification",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "11fe392f-3636-4d2d-84e9-1119b351d8ee",
|
||||
"exec": [
|
||||
""
|
||||
],
|
||||
"type": "text/javascript"
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "noauth"
|
||||
},
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"notifications\": [\n {\n \"tokens\": [\"dBLB1WvTJkiWl3ZPjP0-BS:APA91bGXUbKy65CaN1XqExHXZ892jik2k9XORXSiqdUyXhcQ5RDiJ6LfXrckuH3StYJFcma4UCDr_N038YUtxYsRIHYx_8vWZ6D2uq3199LegWXGl5tz-9zk3M4WZGX8WGxIRUJ31QtW\"],\n \"platform\": 2,\n \"message\": \"This notification will go to iOS and Android platform via Firebase!\"\n }\n ]\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "https://push.statping.com/api/push",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"push",
|
||||
"statping",
|
||||
"com"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"push"
|
||||
]
|
||||
},
|
||||
"description": "Send a push notification to the Statping mobile app using your Firebase device identifier."
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"name": "Send Push Notification",
|
||||
"originalRequest": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"notifications\": [\n {\n \"tokens\": [\"dBLB1WvTJkiWl3ZPjP0-BS:APA91bGXUbKy65CaN1XqExHXZ892jik2k9XORXSiqdUyXhcQ5RDiJ6LfXrckuH3StYJFcma4UCDr_N038YUtxYsRIHYx_8vWZ6D2uq3199LegWXGl5tz-9zk3M4WZGX8WGxIRUJ31QtW\"],\n \"platform\": 2,\n \"message\": \"This notification will go to iOS and Android Statping App\"\n }\n ]\n}",
|
||||
"options": {
|
||||
"raw": {
|
||||
"language": "json"
|
||||
}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "https://push.statping.com/api/push",
|
||||
"protocol": "https",
|
||||
"host": [
|
||||
"push",
|
||||
"statping",
|
||||
"com"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"push"
|
||||
]
|
||||
}
|
||||
},
|
||||
"status": "OK",
|
||||
"code": 200,
|
||||
"_postman_previewlanguage": "json",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Length",
|
||||
"value": "37"
|
||||
},
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json; charset=utf-8"
|
||||
},
|
||||
{
|
||||
"key": "Date",
|
||||
"value": "Thu, 13 Aug 2020 02:24:17 GMT"
|
||||
},
|
||||
{
|
||||
"key": "X-Gorush-Version",
|
||||
"value": "No Version Provided"
|
||||
}
|
||||
],
|
||||
"cookie": [],
|
||||
"body": "{\n \"counts\": 1,\n \"logs\": [],\n \"success\": \"ok\"\n}"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"description": "This is for Statping's miscellaneous API endpoints that aren't a part of another category.",
|
||||
|
@ -840,7 +939,7 @@
|
|||
"",
|
||||
"pm.test(\"View All Services\", function () {",
|
||||
" var jsonData = pm.response.json();",
|
||||
" pm.expect(jsonData.length).to.eql(5);",
|
||||
" pm.expect(jsonData.length).to.eql(6);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
|
@ -1714,7 +1813,7 @@
|
|||
"services"
|
||||
]
|
||||
},
|
||||
"description": "Create a new service and begin monitoring."
|
||||
"description": "View a specific service, this will include the service's failures and checkins.\n\n#### Service Type Field\n- `http` - HTTP Service\n- `tcp` - TCP Service\n- `udp` - UDP Service\n- `icmp` - ICMP Service\n- `grpc` - gRPC Service\n- `static` - Static Service"
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
|
@ -1876,6 +1975,69 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Update Static Service",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "18cfae1e-4025-4338-a734-a552c8ac85ca",
|
||||
"exec": [
|
||||
"pm.test(\"Response is ok\", function () {",
|
||||
" pm.response.to.have.status(200);",
|
||||
"});",
|
||||
"",
|
||||
"pm.test(\"Update Service\", function () {",
|
||||
" var jsonData = pm.response.json();",
|
||||
" pm.expect(jsonData.status).to.eql(\"success\");",
|
||||
" pm.expect(jsonData.output.type).to.eql(\"static\");",
|
||||
" pm.expect(jsonData.output.online).to.eql(false);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "bearer",
|
||||
"bearer": [
|
||||
{
|
||||
"key": "token",
|
||||
"value": "{{api_key}}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "PATCH",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"online\": false,\n \"latency\": 30500,\n \"issue\": \"This is a failure string you can create\"\n}",
|
||||
"options": {
|
||||
"raw": {}
|
||||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{endpoint}}/api/services/7",
|
||||
"host": [
|
||||
"{{endpoint}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"services",
|
||||
"7"
|
||||
]
|
||||
},
|
||||
"description": "Update a Static Service by setting it's online status to true or false. If false, you can include a issue string in the `issue` JSON field."
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Delete Service Failures",
|
||||
"event": [
|
||||
|
@ -2026,7 +2188,7 @@
|
|||
]
|
||||
}
|
||||
],
|
||||
"description": "With the Statping API, you can add, remove, edit all your services fields from the API directly. This includes viewing Service chart data for latency/up-time, and even viewing a log of failures. ",
|
||||
"description": "With the Statping API, you can add, remove, edit all your services fields from the API directly. This includes viewing Service chart data for latency/up-time, and even viewing a log of failures. \n\n### HTTP Services\nA HTTP service is a basic service that sends a HTTP request (GET, POST, PATCH, DELETE, etc) to check if that web service is online or not. You can expect a specific status code, and response body (including regex). \n\n### TCP and UDP Services\nTCP and UDP Services will send a request to the hostname and port of your choice.\n\n### ICMP Ping Services\nICMP Services will send a ICMP (ping) packet to your server to test if it's online.\n\n### gRPC Services\ngRPC Services will request your gRPC server and check the response\n\n### Static Services\nA Static Service is a \"fake\" service that is set online/offline by you.\n",
|
||||
"auth": {
|
||||
"type": "bearer",
|
||||
"bearer": [
|
||||
|
@ -2218,14 +2380,14 @@
|
|||
}
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{endpoint}}/api/services/1/incidents",
|
||||
"raw": "{{endpoint}}/api/services/3/incidents",
|
||||
"host": [
|
||||
"{{endpoint}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"services",
|
||||
"1",
|
||||
"3",
|
||||
"incidents"
|
||||
]
|
||||
},
|
||||
|
@ -2292,213 +2454,6 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Update Service Incident",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "42cd68de-5bd4-4d4e-b687-fa0bafc1d61a",
|
||||
"exec": [
|
||||
"pm.test(\"Response is ok\", function () {",
|
||||
" pm.response.to.have.status(200);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "bearer",
|
||||
"bearer": [
|
||||
{
|
||||
"key": "token",
|
||||
"value": "{{api_key}}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"name": "Content-Type",
|
||||
"type": "text",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n\t\"title\": \"Updated Downtime\",\n\t\"description\": \"This is an update for an incident\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{endpoint}}/api/incidents/1",
|
||||
"host": [
|
||||
"{{endpoint}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"incidents",
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"description": "View all incidents for a single service."
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Delete Incident",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "c6735dbe-86e8-4b42-9b04-6fc1fea949df",
|
||||
"exec": [
|
||||
"pm.test(\"Response is ok\", function () {",
|
||||
" pm.response.to.have.status(200);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "bearer",
|
||||
"bearer": [
|
||||
{
|
||||
"key": "token",
|
||||
"value": "{{api_key}}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "DELETE",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"name": "Content-Type",
|
||||
"type": "text",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"url": {
|
||||
"raw": "{{endpoint}}/api/incidents/1",
|
||||
"host": [
|
||||
"{{endpoint}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"incidents",
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"description": "View all incidents for a single service."
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"name": "Delete Incident",
|
||||
"originalRequest": {
|
||||
"method": "DELETE",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"name": "Content-Type",
|
||||
"type": "text",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"url": {
|
||||
"raw": "{{endpoint}}/api/incidents/2",
|
||||
"host": [
|
||||
"{{endpoint}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"incidents",
|
||||
"2"
|
||||
]
|
||||
}
|
||||
},
|
||||
"status": "OK",
|
||||
"code": 200,
|
||||
"_postman_previewlanguage": "json",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"key": "Date",
|
||||
"value": "Mon, 04 May 2020 03:31:48 GMT"
|
||||
},
|
||||
{
|
||||
"key": "Content-Length",
|
||||
"value": "207"
|
||||
},
|
||||
{
|
||||
"key": "Connection",
|
||||
"value": "close"
|
||||
}
|
||||
],
|
||||
"cookie": [],
|
||||
"body": "{\n \"status\": \"success\",\n \"type\": \"incident\",\n \"method\": \"delete\",\n \"id\": 2,\n \"output\": {\n \"id\": 2,\n \"title\": \"Service Downtime\",\n \"service\": 1,\n \"created_at\": \"2020-05-04T03:18:24.818629Z\",\n \"updated_at\": \"2020-05-04T03:18:24.818629Z\"\n }\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Incident Updates",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "e2e7d38f-efae-44d1-9361-cd301547feb3",
|
||||
"exec": [
|
||||
"pm.test(\"Response is ok\", function () {",
|
||||
" pm.response.to.have.status(200);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "bearer",
|
||||
"bearer": [
|
||||
{
|
||||
"key": "token",
|
||||
"value": "{{api_key}}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "GET",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"name": "Content-Type",
|
||||
"type": "text",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"url": {
|
||||
"raw": "{{endpoint}}/api/incidents/2/updates",
|
||||
"host": [
|
||||
"{{endpoint}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"incidents",
|
||||
"2",
|
||||
"updates"
|
||||
]
|
||||
},
|
||||
"description": "View all incidents for a single service."
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Create Incident Update",
|
||||
"event": [
|
||||
|
@ -2609,6 +2564,113 @@
|
|||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Incident Updates",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "e2e7d38f-efae-44d1-9361-cd301547feb3",
|
||||
"exec": [
|
||||
"pm.test(\"Response is ok\", function () {",
|
||||
" pm.response.to.have.status(200);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "bearer",
|
||||
"bearer": [
|
||||
{
|
||||
"key": "token",
|
||||
"value": "{{api_key}}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "GET",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"name": "Content-Type",
|
||||
"type": "text",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"url": {
|
||||
"raw": "{{endpoint}}/api/incidents/2/updates",
|
||||
"host": [
|
||||
"{{endpoint}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"incidents",
|
||||
"2",
|
||||
"updates"
|
||||
]
|
||||
},
|
||||
"description": "View all incidents for a single service."
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Update Service Incident",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "42cd68de-5bd4-4d4e-b687-fa0bafc1d61a",
|
||||
"exec": [
|
||||
"pm.test(\"Response is ok\", function () {",
|
||||
" pm.response.to.have.status(200);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "bearer",
|
||||
"bearer": [
|
||||
{
|
||||
"key": "token",
|
||||
"value": "{{api_key}}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "POST",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"name": "Content-Type",
|
||||
"type": "text",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n\t\"title\": \"Updated Downtime\",\n\t\"description\": \"This is an update for an incident\"\n}"
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{endpoint}}/api/incidents/1",
|
||||
"host": [
|
||||
"{{endpoint}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"incidents",
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"description": "View all incidents for a single service."
|
||||
},
|
||||
"response": []
|
||||
},
|
||||
{
|
||||
"name": "Delete Incident Update",
|
||||
"event": [
|
||||
|
@ -2712,6 +2774,106 @@
|
|||
"body": "{\n \"status\": \"success\",\n \"type\": \"incident_update\",\n \"method\": \"delete\",\n \"id\": 1,\n \"output\": {\n \"id\": 1,\n \"message\": \"Website is loading very slowly, looking into this.\",\n \"type\": \"Investigating\",\n \"created_at\": \"2020-05-04T03:20:05.102435Z\",\n \"updated_at\": \"2020-05-04T03:20:05.102435Z\"\n }\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Delete Incident",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "c6735dbe-86e8-4b42-9b04-6fc1fea949df",
|
||||
"exec": [
|
||||
"pm.test(\"Response is ok\", function () {",
|
||||
" pm.response.to.have.status(200);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "bearer",
|
||||
"bearer": [
|
||||
{
|
||||
"key": "token",
|
||||
"value": "{{api_key}}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "DELETE",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"name": "Content-Type",
|
||||
"type": "text",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"url": {
|
||||
"raw": "{{endpoint}}/api/incidents/1",
|
||||
"host": [
|
||||
"{{endpoint}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"incidents",
|
||||
"1"
|
||||
]
|
||||
},
|
||||
"description": "View all incidents for a single service."
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"name": "Delete Incident",
|
||||
"originalRequest": {
|
||||
"method": "DELETE",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"name": "Content-Type",
|
||||
"type": "text",
|
||||
"value": "application/json"
|
||||
}
|
||||
],
|
||||
"url": {
|
||||
"raw": "{{endpoint}}/api/incidents/2",
|
||||
"host": [
|
||||
"{{endpoint}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"incidents",
|
||||
"2"
|
||||
]
|
||||
}
|
||||
},
|
||||
"status": "OK",
|
||||
"code": 200,
|
||||
"_postman_previewlanguage": "json",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"key": "Date",
|
||||
"value": "Mon, 04 May 2020 03:31:48 GMT"
|
||||
},
|
||||
{
|
||||
"key": "Content-Length",
|
||||
"value": "207"
|
||||
},
|
||||
{
|
||||
"key": "Connection",
|
||||
"value": "close"
|
||||
}
|
||||
],
|
||||
"cookie": [],
|
||||
"body": "{\n \"status\": \"success\",\n \"type\": \"incident\",\n \"method\": \"delete\",\n \"id\": 2,\n \"output\": {\n \"id\": 2,\n \"title\": \"Service Downtime\",\n \"service\": 1,\n \"created_at\": \"2020-05-04T03:18:24.818629Z\",\n \"updated_at\": \"2020-05-04T03:18:24.818629Z\"\n }\n}"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"protocolProfileBehavior": {}
|
||||
|
@ -3212,6 +3374,8 @@
|
|||
"pm.test(\"Check Login JWT Token\", function () {",
|
||||
" var jsonData = pm.response.json();",
|
||||
" pm.expect(jsonData).to.have.property('token');",
|
||||
" pm.expect(jsonData).to.have.property('admin');",
|
||||
" pm.globals.set(\"token\", jsonData.token);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
|
@ -3308,24 +3472,141 @@
|
|||
"_postman_previewlanguage": "json",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Length",
|
||||
"value": "174"
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"key": "Set-Cookie",
|
||||
"value": "statping_auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWUsInNjb3BlcyI6ImFkbWluIiwiZXhwIjoxNTk2NzQzMDUzfQ.dQQGgUDhFEjCL2Gi-Seg0hBp_sqVsDn3cXB0GpSorJI; Path=/; Expires=Thu, 06 Aug 2020 19:44:13 GMT; Max-Age=259200"
|
||||
},
|
||||
{
|
||||
"key": "Date",
|
||||
"value": "Mon, 03 Aug 2020 19:44:13 GMT"
|
||||
},
|
||||
{
|
||||
"key": "Content-Length",
|
||||
"value": "197"
|
||||
},
|
||||
{
|
||||
"key": "Connection",
|
||||
"value": "close"
|
||||
}
|
||||
],
|
||||
"cookie": [],
|
||||
"body": "{\n \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWUsInNjb3BlcyI6ImFkbWluIiwiZXhwIjoxNTk2NzQzMDUzfQ.dQQGgUDhFEjCL2Gi-Seg0hBp_sqVsDn3cXB0GpSorJI\",\n \"admin\": true\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Check User Token",
|
||||
"event": [
|
||||
{
|
||||
"listen": "test",
|
||||
"script": {
|
||||
"id": "560e439b-d588-4a2f-a8a6-a0607531d74c",
|
||||
"exec": [
|
||||
"pm.test(\"Response is ok\", function () {",
|
||||
" pm.response.to.have.status(200);",
|
||||
"});",
|
||||
"",
|
||||
"pm.test(\"View Token Response\", function () {",
|
||||
" var jsonData = pm.response.json();",
|
||||
" pm.expect(jsonData.username).to.eql(\"admin\");",
|
||||
" pm.expect(jsonData.admin).to.eql(true);",
|
||||
" pm.expect(jsonData.scopes).to.eql(\"admin\");",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
}
|
||||
}
|
||||
],
|
||||
"request": {
|
||||
"auth": {
|
||||
"type": "bearer",
|
||||
"bearer": [
|
||||
{
|
||||
"key": "token",
|
||||
"value": "{{api_key}}",
|
||||
"type": "string"
|
||||
}
|
||||
]
|
||||
},
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "urlencoded",
|
||||
"urlencoded": [
|
||||
{
|
||||
"key": "token",
|
||||
"value": "{{token}}",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{endpoint}}/api/users/token",
|
||||
"host": [
|
||||
"{{endpoint}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"users",
|
||||
"token"
|
||||
]
|
||||
},
|
||||
"description": "Send your JWT token from login to this endpoint to return the JSON values."
|
||||
},
|
||||
"response": [
|
||||
{
|
||||
"name": "Check User Token",
|
||||
"originalRequest": {
|
||||
"method": "POST",
|
||||
"header": [],
|
||||
"body": {
|
||||
"mode": "urlencoded",
|
||||
"urlencoded": [
|
||||
{
|
||||
"key": "token",
|
||||
"value": "{{token}}",
|
||||
"type": "text"
|
||||
}
|
||||
]
|
||||
},
|
||||
"url": {
|
||||
"raw": "{{endpoint}}/api/users/token",
|
||||
"host": [
|
||||
"{{endpoint}}"
|
||||
],
|
||||
"path": [
|
||||
"api",
|
||||
"users",
|
||||
"token"
|
||||
]
|
||||
}
|
||||
},
|
||||
"status": "OK",
|
||||
"code": 200,
|
||||
"_postman_previewlanguage": "json",
|
||||
"header": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"key": "Date",
|
||||
"value": "Sat, 02 May 2020 00:56:17 GMT"
|
||||
"value": "Mon, 03 Aug 2020 19:47:23 GMT"
|
||||
},
|
||||
{
|
||||
"key": "Set-Cookie",
|
||||
"value": "statping_auth=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWUsImV4cCI6MTU4ODY0MDE3N30.tf399_LfAphSGlKMtgphg6qpPrn-_w92XfCrK5FwbZY; Expires=Tue, 05 May 2020 00:56:17 GMT"
|
||||
"key": "Content-Length",
|
||||
"value": "68"
|
||||
},
|
||||
{
|
||||
"key": "Connection",
|
||||
"value": "close"
|
||||
}
|
||||
],
|
||||
"cookie": [],
|
||||
"body": "{\n \"token\": \"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOnRydWUsImV4cCI6MTU4ODY0MDE3N30.tf399_LfAphSGlKMtgphg6qpPrn-_w92XfCrK5FwbZY\",\n \"admin\": true\n}"
|
||||
"body": "{\n \"username\": \"admin\",\n \"admin\": true,\n \"scopes\": \"admin\",\n \"exp\": 1596743053\n}"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -3917,7 +4198,7 @@
|
|||
"",
|
||||
"pm.test(\"View All Notifiers\", function () {",
|
||||
" var jsonData = pm.response.json();",
|
||||
" pm.expect(jsonData.length).to.eql(11);",
|
||||
" pm.expect(jsonData.length).to.eql(13);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
|
@ -4121,7 +4402,7 @@
|
|||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"method\": \"slack\",\n \"host\": \"https://hooks.slack.com/services/EXAMPLEIDHERE/BV33WKP0C/MtKw3Kc8BFylTv4pohKqHtXX\",\n \"enabled\": true,\n \"limits\": 55\n}",
|
||||
"raw": "{\n \"method\": \"slack\",\n \"host\": \"https://hooks.slack.com/services/TTJ1B90DP/RENU20O9M/9uI823SUnYBuGcxYlpSimD6H\",\n \"enabled\": true,\n \"limits\": 55\n}",
|
||||
"options": {
|
||||
"raw": {}
|
||||
}
|
||||
|
@ -4233,7 +4514,7 @@
|
|||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"method\": \"success\",\n \"notifier\": {\n \"enabled\": false,\n \"limits\": 60,\n \"method\": \"slack\",\n \"host\": \"https://webhooksurl.slack.com/***\",\n \"success_data\": \"{\\n \\\"blocks\\\": [{\\n \\\"type\\\": \\\"section\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"The service {{.Service.Name}} is back online.\\\"\\n }\\n }, {\\n \\\"type\\\": \\\"actions\\\",\\n \\\"elements\\\": [{\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"View Service\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"style\\\": \\\"primary\\\",\\n \\\"url\\\": \\\"{{.Core.Domain}}/service/{{.Service.Id}}\\\"\\n }, {\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"Go to Statping\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"url\\\": \\\"{{.Core.Domain}}\\\"\\n }]\\n }]\\n}\",\n \"failure_data\": \"{\\n \\\"blocks\\\": [{\\n \\\"type\\\": \\\"section\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\":warning: The service {{.Service.Name}} is currently offline! :warning:\\\"\\n }\\n }, {\\n \\\"type\\\": \\\"divider\\\"\\n }, {\\n \\\"type\\\": \\\"section\\\",\\n \\\"fields\\\": [{\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*Service:*\\\\n{{.Service.Name}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*URL:*\\\\n{{.Service.Domain}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*Status Code:*\\\\n{{.Service.LastStatusCode}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*When:*\\\\n{{.Failure.CreatedAt}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*Downtime:*\\\\n{{.Service.DowntimeAgo}}\\\"\\n }, {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"*Error:*\\\\n{{.Failure.Issue}}\\\"\\n }]\\n }, {\\n \\\"type\\\": \\\"divider\\\"\\n }, {\\n \\\"type\\\": \\\"actions\\\",\\n \\\"elements\\\": [{\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"View Offline Service\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"style\\\": \\\"danger\\\",\\n \\\"url\\\": \\\"{{.Core.Domain}}/service/{{.Service.Id}}\\\"\\n }, {\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"Go to Statping\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"url\\\": \\\"{{.Core.Domain}}\\\"\\n }]\\n }]\\n}\"\n }\n}",
|
||||
"raw": "{\n \"method\": \"success\",\n \"notifier\": {\n \"enabled\": false,\n \"limits\": 60,\n \"method\": \"slack\",\n \"host\": \"https://webhooksurl.slack.com/***\",\n \"success_data\": \"{\\n \\\"blocks\\\": [{\\n \\\"type\\\": \\\"section\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"The service {{.Service.Name}} is back online.\\\"\\n }\\n }, {\\n \\\"type\\\": \\\"actions\\\",\\n \\\"elements\\\": [{\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"View Service\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"style\\\": \\\"primary\\\",\\n \\\"url\\\": \\\"{{.Core.Domain}}/service/{{.Service.Id}}\\\"\\n }, {\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"Go to Statping\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"url\\\": \\\"{{.Core.Domain}}\\\"\\n }]\\n }]\\n}\",\n \"failure_data\": \"{\\n \\\"blocks\\\": [{\\n \\\"type\\\": \\\"section\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\":warning: The service {{.Service.Name}} is currently offline! :warning:\\\"\\n }\\n }, {\\n \\\"type\\\": \\\"divider\\\"\\n }, {\\n \\\"type\\\": \\\"section\\\",\\n \\\"fields\\\": [{\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*Service:*\\\\n{{.Service.Name}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*URL:*\\\\n{{.Service.Domain}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*Status Code:*\\\\n{{.Service.LastStatusCode}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*When:*\\\\n{{.Failure.CreatedAt}}\\\"\\n }, {\\n \\\"type\\\": \\\"mrkdwn\\\",\\n \\\"text\\\": \\\"*Downtime:*\\\\n{{.Service.Downtime.Human}}\\\"\\n }, {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"*Error:*\\\\n{{.Failure.Issue}}\\\"\\n }]\\n }, {\\n \\\"type\\\": \\\"divider\\\"\\n }, {\\n \\\"type\\\": \\\"actions\\\",\\n \\\"elements\\\": [{\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"View Offline Service\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"style\\\": \\\"danger\\\",\\n \\\"url\\\": \\\"{{.Core.Domain}}/service/{{.Service.Id}}\\\"\\n }, {\\n \\\"type\\\": \\\"button\\\",\\n \\\"text\\\": {\\n \\\"type\\\": \\\"plain_text\\\",\\n \\\"text\\\": \\\"Go to Statping\\\",\\n \\\"emoji\\\": true\\n },\\n \\\"url\\\": \\\"{{.Core.Domain}}\\\"\\n }]\\n }]\\n}\"\n }\n}",
|
||||
"options": {
|
||||
"raw": {}
|
||||
}
|
||||
|
@ -4911,8 +5192,7 @@
|
|||
" var first = jsonData[0];",
|
||||
" var id = pm.globals.get(\"checkin_id\");",
|
||||
" pm.expect(first.name).to.eql(\"Demo Checkin 1\");",
|
||||
" pm.expect(first.grace).to.eql(300);",
|
||||
" pm.expect(first.interval).to.eql(300);",
|
||||
" pm.expect(first.interval).to.eql(3);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
|
@ -4990,8 +5270,7 @@
|
|||
" pm.expect(jsonData.status).to.eql(\"success\");",
|
||||
" pm.expect(jsonData.type).to.eql(\"checkin\");",
|
||||
" pm.expect(jsonData.output.name).to.eql(\"Server Checkin\");",
|
||||
" pm.expect(jsonData.output.grace).to.eql(60);",
|
||||
" pm.expect(jsonData.output.interval).to.eql(900);",
|
||||
" pm.expect(jsonData.output.interval).to.eql(3);",
|
||||
" var id = jsonData.output.api_key;",
|
||||
" pm.globals.set(\"checkin_id\", id);",
|
||||
"});"
|
||||
|
@ -5022,7 +5301,7 @@
|
|||
],
|
||||
"body": {
|
||||
"mode": "raw",
|
||||
"raw": "{\n \"service_id\": 2,\n \"name\": \"Server Checkin\",\n \"interval\": 900,\n \"grace\": 60\n}",
|
||||
"raw": "{\n \"service_id\": 2,\n \"name\": \"Server Checkin\",\n \"interval\": 3\n}",
|
||||
"options": {
|
||||
"raw": {}
|
||||
}
|
||||
|
@ -5182,8 +5461,7 @@
|
|||
" var id = pm.globals.get(\"checkin_id\");",
|
||||
" pm.expect(jsonData.name).to.eql(\"Server Checkin\");",
|
||||
" pm.expect(jsonData.api_key).to.eql(id);",
|
||||
" pm.expect(jsonData.grace).to.eql(60);",
|
||||
" pm.expect(jsonData.interval).to.eql(900);",
|
||||
" pm.expect(jsonData.interval).to.eql(3);",
|
||||
"});"
|
||||
],
|
||||
"type": "text/javascript"
|
||||
|
|
|
@ -60,6 +60,9 @@ const webpackConfig = merge(commonConfig, {
|
|||
plugins: [
|
||||
new webpack.EnvironmentPlugin(environment),
|
||||
new CleanWebpackPlugin(),
|
||||
// new webpack.optimize.LimitChunkCountPlugin({
|
||||
// maxChunks: 1
|
||||
// }),
|
||||
new MiniCSSExtractPlugin({
|
||||
filename: 'css/[name].css',
|
||||
chunkFilename: 'css/[name].css'
|
||||
|
@ -71,7 +74,7 @@ const webpackConfig = merge(commonConfig, {
|
|||
threshold: 10240,
|
||||
minRatio: 0.8
|
||||
}),
|
||||
new webpack.HashedModuleIdsPlugin(),
|
||||
// new webpack.HashedModuleIdsPlugin(),
|
||||
new HtmlPlugin({
|
||||
template: 'public/base.gohtml',
|
||||
filename: 'base.gohtml',
|
||||
|
|
|
@ -19,26 +19,25 @@
|
|||
"@fortawesome/free-brands-svg-icons": "^5.12.1",
|
||||
"@fortawesome/free-solid-svg-icons": "^5.12.0",
|
||||
"@fortawesome/vue-fontawesome": "^0.1.9",
|
||||
"@sentry/browser": "^5.13.2",
|
||||
"@sentry/integrations": "^5.13.2",
|
||||
"apexcharts": "^3.15.0",
|
||||
"@sentry/browser": "^5.20.1",
|
||||
"@sentry/integrations": "^5.20.1",
|
||||
"apexcharts": "^3.6.6",
|
||||
"axios": "^0.19.1",
|
||||
"bootstrap": "^4.4.1",
|
||||
"bootstrap-vue": "^2.6.1",
|
||||
"codemirror-colorpicker": "^1.9.66",
|
||||
"core-js": "^3.4.4",
|
||||
"core-js": "^3.6.5",
|
||||
"date-fns": "^2.9.0",
|
||||
"js-beautify": "^1.11.0",
|
||||
"querystring": "^0.2.0",
|
||||
"sass": "^1.26.10",
|
||||
"semver": "^7.3.2",
|
||||
"vue": "^2.6.11",
|
||||
"vue-apexcharts": "^1.5.2",
|
||||
"vue-apexcharts": "^1.6.0",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-codemirror": "^4.0.6",
|
||||
"vue-cookies": "^1.7.0",
|
||||
"vue-flatpickr-component": "^8.1.5",
|
||||
"vue-github-button": "^1.1.2",
|
||||
"vue-i18n": "^8.18.1",
|
||||
"vue-moment": "^4.1.0",
|
||||
"vue-observe-visibility": "^0.4.6",
|
||||
"vue-router": "~3.0",
|
||||
"vuedraggable": "^2.23.2",
|
||||
|
@ -64,7 +63,6 @@
|
|||
"compression-webpack-plugin": "~2.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"css-loader": "~2.1",
|
||||
"cypress": "^4.3.0",
|
||||
"eslint": "~5.16",
|
||||
"eslint-config-standard": "~10.2",
|
||||
"eslint-friendly-formatter": "~3.0",
|
||||
|
@ -78,13 +76,13 @@
|
|||
"expect": "^25.1.0",
|
||||
"file-loader": "^5.0.2",
|
||||
"friendly-errors-webpack-plugin": "~1.7",
|
||||
"github-wikito-converter": "^1.5.2",
|
||||
"html-webpack-plugin": "^4.0.0-beta.11",
|
||||
"jsdom": "^16.2.0",
|
||||
"jsdom-global": "^3.0.2",
|
||||
"mini-css-extract-plugin": "~0.5",
|
||||
"mocha": "^7.0.1",
|
||||
"mochapack": "^1.1.13",
|
||||
"node-sass": "^4.13.1",
|
||||
"optimize-css-assets-webpack-plugin": "~5.0",
|
||||
"sass-loader": "^8.0.2",
|
||||
"start-server-and-test": "^1.10.11",
|
||||
|
|
|
@ -43,19 +43,11 @@
|
|||
<meta property="twitter:image" content="favicon/social.png">
|
||||
|
||||
{{if USE_CDN}}
|
||||
<link rel="stylesheet" href="https://assets.statping.com/vendor.css">
|
||||
<link rel="stylesheet" href="https://assets.statping.com/style.css">
|
||||
<link rel="stylesheet" href="https://assets.statping.com/main.css">
|
||||
{{else}}
|
||||
{{if USING_ASSETS}}
|
||||
<link href="css/vendor.css" rel="stylesheet">
|
||||
<link href="css/style.css" rel="stylesheet">
|
||||
<link href="css/main.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://assets.statping.com/css/vendor.css">
|
||||
{{else}}
|
||||
<% _.each(htmlWebpackPlugin.tags.headTags, function(headTag) { %>
|
||||
<%= headTag %> <% }) %>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</head>
|
||||
<body>
|
||||
<noscript>
|
||||
|
@ -65,11 +57,10 @@
|
|||
<div id="app" class="statping_container"></div>
|
||||
|
||||
{{if USE_CDN}}
|
||||
<script src="https://assets.statping.com/bundle.js"></script>
|
||||
<script src="https://assets.statping.com/vendor.chunk.js"></script>
|
||||
<script src="https://assets.statping.com/polyfill.chunk.js"></script>
|
||||
<script src="https://assets.statping.com/style.chunk.js"></script>
|
||||
<script src="https://assets.statping.com/main.chunk.js"></script>
|
||||
<script src="https://assets.statping.com/js/bundle.js"></script>
|
||||
<script src="https://assets.statping.com/js/vendor.chunk.js"></script>
|
||||
<script src="https://assets.statping.com/js/polyfill.chunk.js"></script>
|
||||
<script src="https://assets.statping.com/js/main.chunk.js"></script>
|
||||
{{else}}
|
||||
<% _.each(htmlWebpackPlugin.tags.bodyTags, function(bodyTag) { %>
|
||||
<%= bodyTag %> <% }) %>
|
||||
|
|
|
@ -113,10 +113,6 @@ HTML, BODY {
|
|||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link {
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 17 KiB |
|
@ -1,16 +1,14 @@
|
|||
import Vue from "vue";
|
||||
import axios from 'axios'
|
||||
import * as Sentry from "@sentry/browser";
|
||||
import * as Integrations from "@sentry/integrations";
|
||||
|
||||
const qs = require('querystring');
|
||||
axios.defaults.withCredentials = true
|
||||
|
||||
const tokenKey = "statping_auth";
|
||||
const errorReporter = "https://bed4d75404924cb3a799e370733a1b64@sentry.statping.com/3"
|
||||
|
||||
class Api {
|
||||
constructor() {
|
||||
|
||||
this.version = "0.90.67";
|
||||
this.commit = "7e121335791d2143a2eefd404dbcce83b8f46f61";
|
||||
}
|
||||
|
||||
async oauth() {
|
||||
|
@ -54,15 +52,15 @@ class Api {
|
|||
return axios.post('api/services/' + data.id, data).then(response => (response.data))
|
||||
}
|
||||
|
||||
async service_hits(id, start, end, group, fill=true) {
|
||||
async service_hits(id, start, end, group, fill = true) {
|
||||
return axios.get('api/services/' + id + '/hits_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=' + fill).then(response => (response.data))
|
||||
}
|
||||
|
||||
async service_ping(id, start, end, group, fill=true) {
|
||||
async service_ping(id, start, end, group, fill = true) {
|
||||
return axios.get('api/services/' + id + '/ping_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=' + fill).then(response => (response.data))
|
||||
}
|
||||
|
||||
async service_failures_data(id, start, end, group, fill=true) {
|
||||
async service_failures_data(id, start, end, group, fill = true) {
|
||||
return axios.get('api/services/' + id + '/failure_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=' + fill).then(response => (response.data))
|
||||
}
|
||||
|
||||
|
@ -75,7 +73,7 @@ class Api {
|
|||
}
|
||||
|
||||
async service_failures(id, start, end, limit = 999, offset = 0) {
|
||||
return axios.get('api/services/' + id + '/failures?start=' + start + '&end=' + end + '&limit=' + limit+ '&offset=' + offset).then(response => (response.data))
|
||||
return axios.get('api/services/' + id + '/failures?start=' + start + '&end=' + end + '&limit=' + limit + '&offset=' + offset).then(response => (response.data))
|
||||
}
|
||||
|
||||
async service_failures_delete(service) {
|
||||
|
@ -99,7 +97,6 @@ class Api {
|
|||
}
|
||||
|
||||
async groups_reorder(data) {
|
||||
window.console.log('api/reorder/groups', data)
|
||||
return axios.post('api/reorder/groups', data).then(response => (response.data))
|
||||
}
|
||||
|
||||
|
@ -132,27 +129,31 @@ class Api {
|
|||
}
|
||||
|
||||
async incident_updates(incident) {
|
||||
return axios.get('api/incidents/'+incident.id+'/updates').then(response => (response.data))
|
||||
return axios.get('api/incidents/' + incident.id + '/updates').then(response => (response.data))
|
||||
}
|
||||
|
||||
async incident_update_create(update) {
|
||||
return axios.post('api/incidents/'+update.incident+'/updates', update).then(response => (response.data))
|
||||
return axios.post('api/incidents/' + update.incident + '/updates', update).then(response => (response.data))
|
||||
}
|
||||
|
||||
async incident_update_delete(update) {
|
||||
return axios.delete('api/incidents/'+update.incident+'/updates/'+update.id).then(response => (response.data))
|
||||
return axios.delete('api/incidents/' + update.incident + '/updates/' + update.id).then(response => (response.data))
|
||||
}
|
||||
|
||||
async incidents_service(id) {
|
||||
return axios.get('api/services/'+id+'/incidents').then(response => (response.data))
|
||||
return axios.get('api/services/' + id + '/incidents').then(response => (response.data))
|
||||
}
|
||||
|
||||
async incident_create(service_id, data) {
|
||||
return axios.post('api/services/'+service_id+'/incidents', data).then(response => (response.data))
|
||||
return axios.post('api/services/' + service_id + '/incidents', data).then(response => (response.data))
|
||||
}
|
||||
|
||||
async incident_delete(incident) {
|
||||
return axios.delete('api/incidents/'+incident.id).then(response => (response.data))
|
||||
return axios.delete('api/incidents/' + incident.id).then(response => (response.data))
|
||||
}
|
||||
|
||||
async checkin(api) {
|
||||
return axios.get('api/checkins/' + api).then(response => (response.data))
|
||||
}
|
||||
|
||||
async checkin_create(data) {
|
||||
|
@ -160,7 +161,7 @@ class Api {
|
|||
}
|
||||
|
||||
async checkin_delete(checkin) {
|
||||
return axios.delete('api/checkins/'+checkin.api_key).then(response => (response.data))
|
||||
return axios.delete('api/checkins/' + checkin.api_key).then(response => (response.data))
|
||||
}
|
||||
|
||||
async messages() {
|
||||
|
@ -231,6 +232,15 @@ class Api {
|
|||
return axios.post('api/theme', data).then(response => (response.data))
|
||||
}
|
||||
|
||||
async import(data) {
|
||||
return axios.post('api/settings/import', data).then(response => (response.data))
|
||||
}
|
||||
|
||||
async check_token(token) {
|
||||
const f = {token: token}
|
||||
return axios.post('api/users/token', qs.stringify(f)).then(response => (response.data))
|
||||
}
|
||||
|
||||
async login(username, password) {
|
||||
const f = {username: username, password: password}
|
||||
return axios.post('api/login', qs.stringify(f)).then(response => (response.data))
|
||||
|
@ -251,6 +261,14 @@ class Api {
|
|||
})
|
||||
}
|
||||
|
||||
async configs() {
|
||||
return axios.get('api/settings/configs').then(response => (response.data)) || []
|
||||
}
|
||||
|
||||
async configs_save(data) {
|
||||
return axios.post('api/settings/configs', data).then(response => (response.data)) || []
|
||||
}
|
||||
|
||||
token() {
|
||||
return $cookies.get(tokenKey);
|
||||
}
|
||||
|
@ -264,15 +282,12 @@ class Api {
|
|||
}
|
||||
}
|
||||
|
||||
async allActions(...all) {
|
||||
await axios.all([all])
|
||||
async github_release() {
|
||||
return fetch('https://api.github.com/repos/statping/statping/releases/latest').then(response => response.json())
|
||||
}
|
||||
|
||||
async sentry_init() {
|
||||
Sentry.init({
|
||||
dsn: errorReporter,
|
||||
integrations: [new Integrations.Vue({Vue, attachProps: true})],
|
||||
});
|
||||
async allActions(...all) {
|
||||
await axios.all([all])
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,19 +1,19 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<router-view :loaded="loaded"/>
|
||||
<router-view/>
|
||||
<Footer v-if="$route.path !== '/setup'"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Footer from "./components/Index/Footer";
|
||||
const Footer = () => import(/* webpackChunkName: "index" */ "./components/Index/Footer");
|
||||
|
||||
export default {
|
||||
name: 'app',
|
||||
components: {
|
||||
Footer
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
loaded: false,
|
||||
version: "",
|
||||
|
@ -50,10 +50,10 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "./assets/css/bootstrap.min.css";
|
||||
@import "./assets/scss/main";
|
||||
@import "./assets/scss/index";
|
||||
</style>
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
@import 'variables';
|
||||
|
||||
HTML,BODY {
|
||||
background-color: $background-color;
|
||||
}
|
||||
@import 'mixin';
|
||||
|
||||
.index-chart {
|
||||
height: $service-card-height;
|
||||
|
@ -26,26 +23,11 @@ HTML,BODY {
|
|||
box-shadow: 0px 3px 6px 1px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.copy-btn {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.btn-xs {
|
||||
font-size: 8pt;
|
||||
padding: 2px 6px;
|
||||
}
|
||||
|
||||
.copy-btn BUTTON {
|
||||
background-color: white;
|
||||
margin: 6px;
|
||||
height: 26px;
|
||||
font-size: 8pt;
|
||||
padding: 5px 7px;
|
||||
border: 1px solid #a7a7a7;
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
.dim {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
@ -55,41 +37,6 @@ HTML,BODY {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* The slider itself */
|
||||
.slider {
|
||||
-webkit-appearance: none; /* Override default CSS styles */
|
||||
appearance: none;
|
||||
width: 100%; /* Full-width */
|
||||
height: 5px; /* Specified height */
|
||||
background: #d3d3d3; /* Grey background */
|
||||
outline: none; /* Remove outline */
|
||||
-webkit-transition: .2s; /* 0.2 seconds transition on hover */
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
/* Mouse-over effects */
|
||||
.slider:hover {
|
||||
opacity: 1; /* Fully shown on mouse-over */
|
||||
}
|
||||
|
||||
/* The slider handle (use -webkit- (Chrome, Opera, Safari, Edge) and -moz- (Firefox) to override default look) */
|
||||
.slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none; /* Override default look */
|
||||
appearance: none;
|
||||
border-radius: 50%;
|
||||
width: 20px; /* Set a specific slider handle width */
|
||||
height: 20px; /* Slider handle height */
|
||||
background: #4CAF50; /* Green background */
|
||||
cursor: pointer; /* Cursor on hover */
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb {
|
||||
width: 15px; /* Set a specific slider handle width */
|
||||
height: 15px; /* Slider handle height */
|
||||
background: #4CAF50; /* Green background */
|
||||
cursor: pointer; /* Cursor on hover */
|
||||
}
|
||||
|
||||
@-o-keyframes fadeIt {
|
||||
0% { background-color: #f5f5f5; }
|
||||
50% { background-color: #f2f2f2; }
|
||||
|
@ -136,43 +83,53 @@ HTML,BODY {
|
|||
}
|
||||
|
||||
.chartmarker {
|
||||
padding: 5px;
|
||||
width: 240px;
|
||||
text-align: right;
|
||||
padding: 0px;
|
||||
width: 200px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.chartmarker SPAN {
|
||||
font-size: 9pt;
|
||||
font-size: 4pt;
|
||||
display: block;
|
||||
color: #8b8b8b;
|
||||
color: #b1b1b1;
|
||||
}
|
||||
|
||||
.apexcharts-tooltip {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.contain-card {
|
||||
|
||||
.card-header {
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
background-color: rgba(239, 239, 239, 0.65);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.dropup .dropdown-menu {
|
||||
border-radius: 8px 8px 8px 0;
|
||||
background-color: #efefef;
|
||||
}
|
||||
|
||||
.divided {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.divider {
|
||||
flex-grow: 1;
|
||||
border-bottom: 1px solid rgba(0,0,0,0.1);
|
||||
margin: 0 20px 0 20px;
|
||||
}
|
||||
|
||||
.daily-failures {
|
||||
position: absolute;
|
||||
padding-top: 3px;
|
||||
top: 10px;
|
||||
right: 100px;
|
||||
width: 300px;
|
||||
height: 25px;
|
||||
}
|
||||
|
||||
.service_day {
|
||||
height: 20px;
|
||||
margin-right: 2px;
|
||||
border-radius: 4px;
|
||||
max-width: 25px;
|
||||
border-radius: 2px;
|
||||
max-width: 30px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.service_day SPAN {
|
||||
|
@ -202,13 +159,6 @@ HTML,BODY {
|
|||
opacity: 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 25px;
|
||||
max-width: $max-width;
|
||||
box-shadow: 0 .5rem 1rem rgba(0,0,0,.15)!important;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
color: $title-color;
|
||||
}
|
||||
|
@ -226,10 +176,6 @@ HTML,BODY {
|
|||
padding: 5px 7px;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
line-height: 1.3;
|
||||
font-size: 0.75rem;
|
||||
|
@ -274,20 +220,6 @@ HTML,BODY {
|
|||
color: $service-description-color
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-decoration: none;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.footer A {
|
||||
color: $footer-text-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer A:HOVER {
|
||||
color: #6d6d6d;
|
||||
}
|
||||
|
||||
.font-0 {
|
||||
font-size: 0.35rem;
|
||||
}
|
||||
|
@ -342,22 +274,6 @@ HTML,BODY {
|
|||
}
|
||||
}
|
||||
|
||||
.card-body .badge {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link {
|
||||
border-radius: $global-border-radius;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border-radius: $global-border-radius;
|
||||
}
|
||||
|
||||
.mini_success {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.no-decoration {
|
||||
color: black;
|
||||
text-decoration: none;
|
||||
|
@ -368,10 +284,6 @@ HTML,BODY {
|
|||
text-decoration: none;
|
||||
}
|
||||
|
||||
.mini_error {
|
||||
background-color: #ffbbbb;
|
||||
}
|
||||
|
||||
.btn-white {
|
||||
background-color: white;
|
||||
border: 1px solid #d8d8d8;
|
||||
|
@ -395,10 +307,6 @@ HTML,BODY {
|
|||
color: #a0a0a0;
|
||||
}
|
||||
|
||||
.service_block {
|
||||
min-height: 340px;
|
||||
}
|
||||
|
||||
.json-field {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
@ -418,27 +326,6 @@ HTML,BODY {
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
.card {
|
||||
background-color: $service-background;
|
||||
border: $service-border;
|
||||
box-shadow: 0px 3px 6px 1px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-body H4 A {
|
||||
color: $service-title;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.card-title A {
|
||||
color: $service-title;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 240px;
|
||||
|
@ -458,79 +345,6 @@ HTML,BODY {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
.inputTags-field {
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
padding-top: .13rem;
|
||||
}
|
||||
|
||||
input.inputTags-field:focus {
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
.inputTags-list {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: calc(2.25rem + 2px);
|
||||
padding: .2rem .35rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: .25rem;
|
||||
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
||||
}
|
||||
|
||||
.inputTags-item {
|
||||
background-color: #3aba39;
|
||||
margin-right: 5px;
|
||||
padding: 5px 8px;
|
||||
font-size: 10pt;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.inputTags-item .close-item {
|
||||
margin-left: 6px;
|
||||
font-size: 13pt;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@mixin dynamic-color-hov($color) {
|
||||
&.dyn-dark {
|
||||
background-color: darken($color, 12%) !important;
|
||||
border-color: darken($color, 17%) !important;
|
||||
}
|
||||
&.dyn-dark:HOVER {
|
||||
background-color: darken($color, 17%) !important;
|
||||
border-color: darken($color, 20%) !important;
|
||||
}
|
||||
&.dyn-light {
|
||||
background-color: lighten($color, 12%) !important;
|
||||
border-color: lighten($color, 17%) !important;
|
||||
}
|
||||
&.dyn-light:HOVER {
|
||||
background-color: lighten($color, 17%) !important;
|
||||
border-color: lighten($color, 20%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin dynamic-color($color) {
|
||||
&.dyn-dark {
|
||||
background-color: darken($color, 12%) !important;
|
||||
border-color: darken($color, 17%) !important;
|
||||
}
|
||||
&.dyn-light {
|
||||
background-color: lighten($color, 12%) !important;
|
||||
border-color: lighten($color, 17%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.btn-primary {
|
||||
background-color: $primary-color;
|
||||
border-color: darken($primary-color, 17%);
|
||||
|
@ -564,18 +378,6 @@ HTML,BODY {
|
|||
background-color: darken($danger-color, 10%) !important;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active, .nav-pills .show>.nav-link {
|
||||
background-color: $nav-tab-color;
|
||||
}
|
||||
|
||||
.nav-pills A {
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
.nav-pills I {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
@keyframes fadeInOut {
|
||||
0% { opacity:1; }
|
||||
50% { opacity:0.3; }
|
||||
|
@ -600,181 +402,13 @@ HTML,BODY {
|
|||
-webkit-animation: fadeInOut 1s infinite;
|
||||
-moz-animation: fadeInOut 1s infinite;
|
||||
-o-animation: fadeInOut 1s infinite;
|
||||
animation: fadeInOut 21 infinite;
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
/* Bootstrap Settings */
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font: inherit;
|
||||
overflow: auto;
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
/* Code Mirror Settings */
|
||||
font-family: monospace;
|
||||
position: relative;
|
||||
height:80vh;
|
||||
}
|
||||
|
||||
.CodeMirror-focused {
|
||||
/* Bootstrap Settings */
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
}
|
||||
|
||||
.switch {
|
||||
font-size: 1rem;
|
||||
position: relative;
|
||||
}
|
||||
.switch input {
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
background: none;
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
.switch input + label {
|
||||
position: relative;
|
||||
min-width: calc(calc(2.375rem * .8) * 2);
|
||||
border-radius: calc(2.375rem * .8);
|
||||
height: calc(2.375rem * .8);
|
||||
line-height: calc(2.375rem * .8);
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
vertical-align: middle;
|
||||
text-indent: calc(calc(calc(2.375rem * .8) * 2) + .5rem);
|
||||
}
|
||||
.switch input + label::before,
|
||||
.switch input + label::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: calc(calc(2.375rem * .8) * 2);
|
||||
bottom: 0;
|
||||
display: block;
|
||||
}
|
||||
.switch input + label::before {
|
||||
right: 0;
|
||||
background-color: #dee2e6;
|
||||
border-radius: calc(2.375rem * .8);
|
||||
transition: 0.2s all;
|
||||
}
|
||||
.switch input + label::after {
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: calc(calc(2.375rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(2.375rem * .8) - calc(2px * 2));
|
||||
border-radius: 50%;
|
||||
background-color: white;
|
||||
transition: 0.2s all;
|
||||
}
|
||||
.switch-rd-gr input:checked + label::before {
|
||||
background-color: #29b10c !important;
|
||||
}
|
||||
.switch input:checked + label::before {
|
||||
background-color: #08d;
|
||||
}
|
||||
.switch input:checked + label::after {
|
||||
margin-left: calc(2.375rem * .8);
|
||||
}
|
||||
.switch input:focus + label::before {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 0.2rem rgba(0, 136, 221, 0.25);
|
||||
}
|
||||
.switch input:disabled + label {
|
||||
color: #868e96;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.switch input:disabled + label::before {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
.switch.switch-sm {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.switch.switch-sm input + label {
|
||||
min-width: calc(calc(1.9375rem * .8) * 2);
|
||||
height: calc(1.9375rem * .8);
|
||||
line-height: calc(1.9375rem * .8);
|
||||
text-indent: calc(calc(calc(1.9375rem * .8) * 2) + .5rem);
|
||||
}
|
||||
.switch.switch-sm input + label::before {
|
||||
width: calc(calc(1.9375rem * .8) * 2);
|
||||
}
|
||||
.switch.switch-sm input + label::after {
|
||||
width: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
||||
}
|
||||
.switch.switch-sm input:checked + label::after {
|
||||
margin-left: calc(1.9375rem * .8);
|
||||
}
|
||||
.switch.switch-lg {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
.switch.switch-lg input + label {
|
||||
min-width: calc(calc(3rem * .8) * 2);
|
||||
height: calc(3rem * .8);
|
||||
line-height: calc(3rem * .8);
|
||||
text-indent: calc(calc(calc(3rem * .8) * 2) + .5rem);
|
||||
}
|
||||
.switch.switch-lg input + label::before {
|
||||
width: calc(calc(3rem * .8) * 2);
|
||||
}
|
||||
.switch.switch-lg input + label::after {
|
||||
width: calc(calc(3rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(3rem * .8) - calc(2px * 2));
|
||||
}
|
||||
.switch.switch-lg input:checked + label::after {
|
||||
margin-left: calc(3rem * .8);
|
||||
}
|
||||
.switch + .switch {
|
||||
margin-left: 1rem;
|
||||
animation: fadeInOut 1s infinite;
|
||||
}
|
||||
|
||||
.sortable_drag {
|
||||
background-color: #0000000f;
|
||||
}
|
||||
|
||||
.drag_icon {
|
||||
cursor: move; /* fallback if grab cursor is unsupported */
|
||||
cursor: grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
margin-left: -10px;
|
||||
text-align: center;
|
||||
color: #b1b1b1;
|
||||
}
|
||||
|
||||
/* (Optional) Apply a "closed-hand" cursor during drag operation. */
|
||||
.drag_icon:active {
|
||||
cursor: grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
|
||||
.switch_btn {
|
||||
float: right;
|
||||
margin: -1px 0px 0px 0px;
|
||||
|
@ -799,7 +433,7 @@ HTML,BODY {
|
|||
}
|
||||
|
||||
.jumbotron {
|
||||
background-color: white;
|
||||
background-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
.toggle-service {
|
||||
|
@ -809,19 +443,6 @@ HTML,BODY {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
min-height: 85pt;
|
||||
}
|
||||
|
||||
.list-group-item:HOVER {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.index_container {
|
||||
min-height: 980pt;
|
||||
background-color: $container-color;
|
||||
}
|
||||
|
||||
/* Enter and leave animations can use different */
|
||||
/* durations and timing functions. */
|
||||
.slide-fade-enter-active {
|
||||
|
|
|
@ -0,0 +1,299 @@
|
|||
@import 'variables';
|
||||
@import 'mixin';
|
||||
|
||||
.copy-btn {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
z-index: 995;
|
||||
}
|
||||
|
||||
.copy-btn BUTTON {
|
||||
background-color: white;
|
||||
margin: 6px;
|
||||
height: 26px;
|
||||
font-size: 8pt;
|
||||
padding: 5px 7px;
|
||||
border: 1px solid #a7a7a7;
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
background-color: $input-background;
|
||||
border: $input-border;
|
||||
color: $input-color;
|
||||
}
|
||||
|
||||
.form-control:FOCUS {
|
||||
background-color: lighten($input-background, 4%) !important;
|
||||
border: $input-border;
|
||||
color: $input-color;
|
||||
}
|
||||
|
||||
.form-control[readonly] {
|
||||
background-color: lighten($background-color, 12%) !important;
|
||||
color: lighten($input-color, 30%) !important;
|
||||
}
|
||||
|
||||
/* The slider itself */
|
||||
.slider {
|
||||
-webkit-appearance: none; /* Override default CSS styles */
|
||||
appearance: none;
|
||||
width: 100%; /* Full-width */
|
||||
height: 5px; /* Specified height */
|
||||
background: #d3d3d3; /* Grey background */
|
||||
outline: none; /* Remove outline */
|
||||
-webkit-transition: .2s; /* 0.2 seconds transition on hover */
|
||||
transition: opacity .2s;
|
||||
}
|
||||
|
||||
/* Mouse-over effects */
|
||||
.slider:hover {
|
||||
opacity: 1; /* Fully shown on mouse-over */
|
||||
}
|
||||
|
||||
/* The slider handle (use -webkit- (Chrome, Opera, Safari, Edge) and -moz- (Firefox) to override default look) */
|
||||
.slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none; /* Override default look */
|
||||
appearance: none;
|
||||
border-radius: 50%;
|
||||
width: 20px; /* Set a specific slider handle width */
|
||||
height: 20px; /* Slider handle height */
|
||||
background: #4CAF50; /* Green background */
|
||||
cursor: pointer; /* Cursor on hover */
|
||||
}
|
||||
|
||||
.slider::-moz-range-thumb {
|
||||
width: 15px; /* Set a specific slider handle width */
|
||||
height: 15px; /* Slider handle height */
|
||||
background: #4CAF50; /* Green background */
|
||||
cursor: pointer; /* Cursor on hover */
|
||||
}
|
||||
|
||||
|
||||
.inputTags-field {
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
padding-top: .13rem;
|
||||
}
|
||||
|
||||
input.inputTags-field:focus {
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
.inputTags-list {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: calc(2.25rem + 2px);
|
||||
padding: .2rem .35rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #495057;
|
||||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: .25rem;
|
||||
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out;
|
||||
}
|
||||
|
||||
.inputTags-item {
|
||||
background-color: #3aba39;
|
||||
margin-right: 5px;
|
||||
padding: 5px 8px;
|
||||
font-size: 10pt;
|
||||
color: white;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.inputTags-item .close-item {
|
||||
margin-left: 6px;
|
||||
font-size: 13pt;
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.switch {
|
||||
font-size: 1rem;
|
||||
position: relative;
|
||||
}
|
||||
.switch input {
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
width: 1px;
|
||||
background: none;
|
||||
border: 0;
|
||||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
overflow: hidden;
|
||||
padding: 0;
|
||||
}
|
||||
.switch input + label {
|
||||
position: relative;
|
||||
min-width: calc(calc(2.375rem * .8) * 2);
|
||||
border-radius: calc(2.375rem * .8);
|
||||
height: calc(2.375rem * .8);
|
||||
line-height: calc(2.375rem * .8);
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
outline: none;
|
||||
user-select: none;
|
||||
vertical-align: middle;
|
||||
text-indent: calc(calc(calc(2.375rem * .8) * 2) + .5rem);
|
||||
}
|
||||
.switch input + label::before,
|
||||
.switch input + label::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: calc(calc(2.375rem * .8) * 2);
|
||||
bottom: 0;
|
||||
display: block;
|
||||
}
|
||||
.switch input + label::before {
|
||||
right: 0;
|
||||
background-color: #dee2e6;
|
||||
border-radius: calc(2.375rem * .8);
|
||||
transition: 0.2s all;
|
||||
}
|
||||
.switch input + label::after {
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
width: calc(calc(2.375rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(2.375rem * .8) - calc(2px * 2));
|
||||
border-radius: 50%;
|
||||
background-color: white;
|
||||
transition: 0.2s all;
|
||||
}
|
||||
.switch-rd-gr input:checked + label::before {
|
||||
background-color: #29b10c !important;
|
||||
}
|
||||
.switch input:checked + label::before {
|
||||
background-color: #08d;
|
||||
}
|
||||
.switch input:checked + label::after {
|
||||
margin-left: calc(2.375rem * .8);
|
||||
}
|
||||
.switch input:focus + label::before {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 0.2rem rgba(0, 136, 221, 0.25);
|
||||
}
|
||||
.switch input:disabled + label {
|
||||
color: #868e96;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.switch input:disabled + label::before {
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
.switch.switch-sm {
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
.switch.switch-sm input + label {
|
||||
min-width: calc(calc(1.9375rem * .8) * 2);
|
||||
height: calc(1.9375rem * .8);
|
||||
line-height: calc(1.9375rem * .8);
|
||||
text-indent: calc(calc(calc(1.9375rem * .8) * 2) + .5rem);
|
||||
}
|
||||
.switch.switch-sm input + label::before {
|
||||
width: calc(calc(1.9375rem * .8) * 2);
|
||||
}
|
||||
.switch.switch-sm input + label::after {
|
||||
width: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
||||
}
|
||||
.switch.switch-sm input:checked + label::after {
|
||||
margin-left: calc(1.9375rem * .8);
|
||||
}
|
||||
.switch.switch-lg {
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
.switch.switch-lg input + label {
|
||||
min-width: calc(calc(3rem * .8) * 2);
|
||||
height: calc(3rem * .8);
|
||||
line-height: calc(3rem * .8);
|
||||
text-indent: calc(calc(calc(3rem * .8) * 2) + .5rem);
|
||||
}
|
||||
.switch.switch-lg input + label::before {
|
||||
width: calc(calc(3rem * .8) * 2);
|
||||
}
|
||||
.switch.switch-lg input + label::after {
|
||||
width: calc(calc(3rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(3rem * .8) - calc(2px * 2));
|
||||
}
|
||||
.switch.switch-lg input:checked + label::after {
|
||||
margin-left: calc(3rem * .8);
|
||||
}
|
||||
.switch + .switch {
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
|
||||
.CodeMirror {
|
||||
/* Bootstrap Settings */
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
font: inherit;
|
||||
overflow: auto;
|
||||
display: block;
|
||||
width: 100%;
|
||||
padding: 0px;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
color: #555;
|
||||
background-color: #fff;
|
||||
background-image: none;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
/* Code Mirror Settings */
|
||||
font-family: monospace;
|
||||
position: relative;
|
||||
height:80vh;
|
||||
}
|
||||
|
||||
.CodeMirror-focused {
|
||||
/* Bootstrap Settings */
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, .6);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active, .nav-pills .show>.nav-link {
|
||||
background-color: $nav-tab-color;
|
||||
}
|
||||
|
||||
.nav-pills A {
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.nav-link.active A:HOVER {
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.nav-pills I {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
|
||||
.drag_icon {
|
||||
cursor: move; /* fallback if grab cursor is unsupported */
|
||||
cursor: grab;
|
||||
cursor: -moz-grab;
|
||||
cursor: -webkit-grab;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
display: inline-block;
|
||||
margin-right: 5px;
|
||||
margin-left: -10px;
|
||||
text-align: center;
|
||||
color: #b1b1b1;
|
||||
}
|
||||
|
||||
/* (Optional) Apply a "closed-hand" cursor during drag operation. */
|
||||
.drag_icon:active {
|
||||
cursor: grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
@import 'variables';
|
||||
@import 'mixin';
|
||||
@import 'layout';
|
||||
@import 'base';
|
||||
@import 'forms';
|
||||
@import 'mobile';
|
|
@ -0,0 +1,230 @@
|
|||
@import 'variables';
|
||||
@import 'mixin';
|
||||
|
||||
HTML,BODY {
|
||||
background-color: $background-color;
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
A {
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.modal-backdrop {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1040;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.modal {
|
||||
z-index: 999999 !important;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.modal-dialog {
|
||||
top: 20%;
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
|
||||
.text-muted {
|
||||
color: lighten($text-color, 30%) !important;
|
||||
}
|
||||
|
||||
.day-success {
|
||||
background-color: $day-success-background;
|
||||
}
|
||||
|
||||
.day-success:HOVER {
|
||||
background-color: lighten($day-success-background, 2%) !important;
|
||||
}
|
||||
|
||||
.day-error {
|
||||
background-color: $day-error-background;
|
||||
}
|
||||
|
||||
.day-error:HOVER {
|
||||
background-color: lighten($day-error-background, 2%) !important;
|
||||
}
|
||||
|
||||
.contain-card {
|
||||
|
||||
.card-header {
|
||||
font-size: 1.15rem;
|
||||
}
|
||||
|
||||
.dropdown-menu {
|
||||
background-color: rgba(239, 239, 239, 0.65);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-bottom: 30px;
|
||||
color: $navbar-color;
|
||||
background-color: $navbar-background;
|
||||
}
|
||||
|
||||
.table {
|
||||
color: $text-color;
|
||||
}
|
||||
|
||||
.nav-pills {
|
||||
border-radius: $global-border-radius;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: $navbar-color;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border-radius: $global-border-radius;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: $card-background;
|
||||
border: $card-border;
|
||||
box-shadow: $card-shadow;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-body H4 A {
|
||||
color: $service-title;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.card-title A {
|
||||
color: $service-title;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.card-body .badge {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.dashboard_card {
|
||||
background-color: $group-list-background;
|
||||
box-shadow: rgba(0,0,0,.05) 0px 2px 3px 1px;
|
||||
}
|
||||
|
||||
.dashboard_card:HOVER {
|
||||
background-color: lighten($group-list-background, 2%) !important;
|
||||
box-shadow: rgba(0,0,0,.05) 0px 1px 5px 3px;
|
||||
-webkit-transition-duration: 300ms;
|
||||
-moz-transition-duration: 300ms;
|
||||
-o-transition-duration: 300ms;
|
||||
transition-duration: 300ms;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
min-height: 85pt;
|
||||
background-color: $group-list-background;
|
||||
}
|
||||
|
||||
.list-group-item:HOVER {
|
||||
background-color: lighten($group-list-background, 5%) !important;
|
||||
box-shadow: rgba(0, 0, 0, 0.1) 0px 4px 6px 1px;
|
||||
-webkit-transition-duration: 300ms;
|
||||
-moz-transition-duration: 300ms;
|
||||
-o-transition-duration: 300ms;
|
||||
transition-duration: 300ms;
|
||||
margin-top: -1px;
|
||||
margin-bottom: 1px;
|
||||
}
|
||||
|
||||
.list-group-item A {
|
||||
color: $group-list-title;
|
||||
}
|
||||
|
||||
.chart_list_tooltip {
|
||||
height: 30px;
|
||||
font-size: 6pt;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 25px;
|
||||
max-width: $max-width;
|
||||
box-shadow: 0 .5rem 1rem rgba(0, 0, 0, 0.15) !important;
|
||||
background-color: $container-color;
|
||||
}
|
||||
|
||||
.login_container {
|
||||
border-radius: 5px;
|
||||
box-shadow: 0 .5rem 1rem rgba(0, 0, 0, 0.15) !important;
|
||||
background-color: $container-color;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-decoration: none;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.footer .links {
|
||||
color: $footer-text-color;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer .links:HOVER {
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.footer .statping {
|
||||
color: lighten($footer-text-color, 10%);
|
||||
text-decoration: none;
|
||||
}
|
||||
.footer .statping:HOVER {
|
||||
color: lighten($footer-text-color, 0%);
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.no-select {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.dates{
|
||||
padding:10px 0px;
|
||||
margin:10px 20px;
|
||||
font-weight:600;
|
||||
overflow:auto;
|
||||
color: darken($card-background, 45%);
|
||||
font-size: 22px;
|
||||
}
|
||||
.dates div{
|
||||
float:left;
|
||||
width:50%;
|
||||
text-align:center;
|
||||
position:relative;
|
||||
}
|
||||
.dates strong {
|
||||
display:block;
|
||||
color: darken($card-background, 20%);
|
||||
font-size:20px;
|
||||
font-weight:700;
|
||||
}
|
||||
.dates span{
|
||||
width:1px;
|
||||
height:60px;
|
||||
position:absolute;
|
||||
right:0;
|
||||
top:0;
|
||||
background: darken($card-background, 10%);
|
||||
}
|
|
@ -1,2 +0,0 @@
|
|||
@import 'base';
|
||||
@import 'mobile';
|
|
@ -0,0 +1,29 @@
|
|||
@mixin dynamic-color-hov($color) {
|
||||
&.dyn-dark {
|
||||
background-color: darken($color, 12%) !important;
|
||||
border-color: darken($color, 17%) !important;
|
||||
}
|
||||
&.dyn-dark:HOVER {
|
||||
background-color: darken($color, 17%) !important;
|
||||
border-color: darken($color, 20%) !important;
|
||||
}
|
||||
&.dyn-light {
|
||||
background-color: lighten($color, 12%) !important;
|
||||
border-color: lighten($color, 17%) !important;
|
||||
}
|
||||
&.dyn-light:HOVER {
|
||||
background-color: lighten($color, 17%) !important;
|
||||
border-color: lighten($color, 20%) !important;
|
||||
}
|
||||
}
|
||||
|
||||
@mixin dynamic-color($color) {
|
||||
&.dyn-dark {
|
||||
background-color: darken($color, 12%) !important;
|
||||
border-color: darken($color, 17%) !important;
|
||||
}
|
||||
&.dyn-light {
|
||||
background-color: lighten($color, 12%) !important;
|
||||
border-color: lighten($color, 17%) !important;
|
||||
}
|
||||
}
|
|
@ -40,10 +40,6 @@
|
|||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.service_li {
|
||||
border: 1px solid #f3f3f3 !important;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0px !important;
|
||||
padding-top: 0vh !important;
|
||||
|
@ -75,8 +71,17 @@
|
|||
}
|
||||
|
||||
.btn-sm {
|
||||
line-height: 1.4rem;
|
||||
font-size: 0.83rem;
|
||||
line-height: 1.3;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.badge {
|
||||
padding-top: 2px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.switch LABEL {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
.full-col-12 {
|
||||
|
|
|
@ -1,16 +1,28 @@
|
|||
/* Index Page */
|
||||
$background-color: #fcfcfc;
|
||||
$container-color: #fcfcfc;
|
||||
$max-width: 860px;
|
||||
$title-color: #464646;
|
||||
$description-color: #939393;
|
||||
$background-color: #EAEAEA;
|
||||
$container-color: #ffffff;
|
||||
$text-color: #2a2a2a;
|
||||
$max-width: 1012px;
|
||||
$title-color: #4e4e4e;
|
||||
$description-color: #828282;
|
||||
$subtitle-color: #747474;
|
||||
$mobile-card-shadow: 2px 3px 10px #b7b7b7;
|
||||
|
||||
$group-list-background: #fcfcfc;
|
||||
$group-list-title: #474747;
|
||||
$navbar-color: #1c1c1c;
|
||||
$navbar-background: #ffffff;
|
||||
$input-background: #fdfdfd;
|
||||
$input-color: #4e4e4e;
|
||||
$input-border: 1px solid #c9c9c9;
|
||||
$day-success-background: #e9e9e9;
|
||||
$day-error-background: #d50a0a;
|
||||
|
||||
/* Status Container */
|
||||
$service-background: #ffffff;
|
||||
$service-border: 1px solid rgba(0,0,0,.125);
|
||||
$service-title: #444444;
|
||||
$card-background: #fcfcfc;
|
||||
$card-border: 1px solid rgba(76, 76, 76, 0.12);
|
||||
$card-shadow: 0px 3px 6px 1px rgba(0, 0, 0, 0.08);
|
||||
$service-title: #3e3e3e;
|
||||
$service-title-size: 1.8rem;
|
||||
$service-stats-color: #4f4f4f;
|
||||
$service-description-color: #fff;
|
||||
|
@ -23,7 +35,7 @@ $danger-color: #dd3545;
|
|||
$primary-color: #3e9bff;
|
||||
|
||||
/* Footer Settings */
|
||||
$footer-text-color: #8d8d8d;
|
||||
$footer-text-color: #b0b0b0;
|
||||
$nav-tab-color: #13a00d;
|
||||
$footer-display: block;
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ CodeMirror.defineMode('mymode', () => {
|
|||
if (stream.match(".Service") || (stream.match(".Core")) || (stream.match(".Failure"))) {
|
||||
return "var-highlight"
|
||||
} else if (stream.match(".Id") || stream.match(".Domain") || stream.match(".CreatedAt") ||
|
||||
stream.match(".Name") || stream.match(".DowntimeAgo") || stream.match(".Issue") || stream.match(".LastStatusCode") ||
|
||||
stream.match(".Name") || stream.match(".Downtime.Human") || stream.match(".Issue") || stream.match(".LastStatusCode") ||
|
||||
stream.match(".Port") || stream.match(".FailuresLast24Hours") || stream.match(".PingTime")) {
|
||||
return "var-sub-highlight"
|
||||
} else if (stream.match("{{") || stream.match("}}")) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="card text-black-50 bg-white mb-5">
|
||||
<div class="card mb-5">
|
||||
<div class="card-header">Cache</div>
|
||||
<div class="card-body">
|
||||
<span v-if="!cache" class="text-muted">There are no cached pages yet!</span>
|
||||
|
|
|
@ -2,42 +2,82 @@
|
|||
<div class="col-12">
|
||||
<h2>{{service.name}} Checkins</h2>
|
||||
<p class="mb-3">Tell your service to send a routine HTTP request to a Statping Checkin.</p>
|
||||
<div v-for="(checkin, i) in checkins" class="col-12 alert alert-light" role="alert">
|
||||
<span class="badge badge-pill badge-info text-uppercase">{{checkin.name}}</span>
|
||||
<span class="float-right font-2">Last checkin {{ago(checkin.last_hit)}}</span>
|
||||
<span class="float-right font-2 mr-3">Check Every {{checkin.interval}} seconds</span>
|
||||
<span class="float-right font-2 mr-3">Grace Period {{checkin.grace}} seconds</span>
|
||||
<span class="d-block mt-2">
|
||||
|
||||
<div v-for="(checkin, i) in checkins" class="card text-black-50 bg-white mt-3">
|
||||
<div class="card-header text-capitalize">
|
||||
{{checkin.name}}
|
||||
<button @click="deleteCheckin(checkin)" class="btn btn-sm small btn-danger float-right text-uppercase">Delete</button>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="input-group">
|
||||
<input type="text" class="form-control" :value="`${core.domain}/checkin/${checkin.api_key}`" readonly>
|
||||
<span class="small">Send a GET request to this URL every {{checkin.interval}} seconds
|
||||
<button @click="deleteCheckin(checkin)" type="button" class="btn btn-danger btn-xs float-right mt-1">Delete</button>
|
||||
</span>
|
||||
</span>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(`${core.domain}/checkin/${checkin.api_key}`)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-12 alert alert-light">
|
||||
<span class="small">Send a GET request to this URL every {{checkin.interval}} minutes</span>
|
||||
<span class="small float-right mt-1 mr-3 d-none d-md-block">Requested {{ago(checkin.last_hit)}} ago</span>
|
||||
<span class="small float-right mt-1 mr-3 d-none d-md-block">Request expected every {{checkin.interval}} minutes</span>
|
||||
|
||||
<div class="card text-black-50 bg-white mt-3">
|
||||
<div class="card-header text-capitalize">
|
||||
<font-awesome-icon @click="expanded = !expanded" :icon="expanded ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
{{checkin.name}} Records
|
||||
</div>
|
||||
<div class="card-body" :class="{'d-none': !expanded}">
|
||||
<div class="alert alert-primary small" :class="{'alert-success': hit.success, 'alert-danger': !hit.success}" v-for="(hit, i) in records(checkin)">
|
||||
Checkin {{hit.success ? "Request" : "Failure"}} at {{hit.created_at}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card text-black-50 bg-white mt-3">
|
||||
<div class="card-header text-capitalize">
|
||||
<font-awesome-icon @click="curl_expanded = !curl_expanded" :icon="curl_expanded ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Cronjob Task
|
||||
</div>
|
||||
<div class="card-body" :class="{'d-none': !curl_expanded}">
|
||||
This cronjob script will request the checkin endpoint every {{checkin.interval}} minutes. Add this cronjob task to the machine running this service.
|
||||
<div class="input-group mt-2">
|
||||
<input type="text" class="form-control" :value="`${checkin.interval} * * * * /usr/bin/curl ${core.domain}/checkin/${checkin.api_key} >/dev/null 2>&1`" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(`${checkin.interval} * * * * /usr/bin/curl ${core.domain}/checkin/${checkin.api_key} >/dev/null 2>&1`)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
<span class="small d-block">Using CURL</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<span :class="{'text-success': last_record(checkin).success, 'text-danger': !last_record(checkin).success}">
|
||||
{{last_record(checkin).success ? "Checkin is currently working correctly" : "Checkin is currently failing"}}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card text-black-50 bg-white mt-4">
|
||||
<div class="card-header text-capitalize">Create Checkin</div>
|
||||
<div class="card-body">
|
||||
<form @submit.prevent="saveCheckin">
|
||||
<div class="form-group row">
|
||||
<div class="col-5">
|
||||
<div class="col-7 col-md-5">
|
||||
<label for="checkin_interval" class="col-form-label">Checkin Name</label>
|
||||
<input v-model="checkin.name" type="text" name="name" class="form-control" id="checkin_name" placeholder="New Checkin">
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<label for="checkin_interval" class="col-form-label">Interval</label>
|
||||
<input v-model="checkin.interval" type="number" name="interval" class="form-control" id="checkin_interval" placeholder="60">
|
||||
<div class="col-5 col-md-3">
|
||||
<label for="checkin_interval" class="col-form-label">Interval (minutes)</label>
|
||||
<input v-model="checkin.interval" type="number" name="interval" class="form-control" id="checkin_interval" placeholder="1" min="1">
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<label for="grace_period" class="col-form-label">Grace Period</label>
|
||||
<input v-model="checkin.grace" type="number" name="grace" class="form-control" id="grace_period" placeholder="10">
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="col-12 col-md-4">
|
||||
<label class="col-form-label"></label>
|
||||
<button @click.prevent="saveCheckin" type="submit" id="submit" class="btn btn-primary d-block mt-2">Save Checkin</button>
|
||||
<button :disabled="btn_disabled" @click.prevent="saveCheckin" type="submit" id="submit" class="btn btn-primary d-block mt-2">Save Checkin</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -49,11 +89,14 @@ export default {
|
|||
return {
|
||||
service: {},
|
||||
ready: false,
|
||||
expanded: false,
|
||||
curl_expanded: false,
|
||||
checkin: {
|
||||
name: "",
|
||||
interval: 60,
|
||||
grace: 60,
|
||||
service_id: 0
|
||||
interval: 1,
|
||||
service_id: 0,
|
||||
hits: [],
|
||||
failures: []
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -64,6 +107,12 @@ export default {
|
|||
core() {
|
||||
return this.$store.getters.core
|
||||
},
|
||||
btn_disabled() {
|
||||
if (this.checkin.name === "" || this.checkin.interval <= 0) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
},
|
||||
},
|
||||
async created() {
|
||||
if (this.$route.params) {
|
||||
|
@ -74,32 +123,43 @@ export default {
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
records(checkin) {
|
||||
let hits = []
|
||||
let failures = []
|
||||
checkin.hits.forEach((hit) => {
|
||||
hits.push({success: true, created_at: this.parseISO(hit.created_at), id: hit.id})
|
||||
})
|
||||
checkin.failures.forEach((failure) => {
|
||||
failures.push({success: false, created_at: this.parseISO(failure.created_at), id: failure.id})
|
||||
})
|
||||
return hits.concat(failures).sort((a, b) => {return a.created_at-b.created_at}).reverse().slice(0,32)
|
||||
},
|
||||
last_record(checkin) {
|
||||
const r = this.records(checkin)
|
||||
if (r.length === 0) {
|
||||
return {success: false}
|
||||
}
|
||||
return r[0]
|
||||
},
|
||||
fixInts() {
|
||||
const c = this.checkin
|
||||
this.checkin.interval = parseInt(c.interval)
|
||||
this.checkin.grace = parseInt(c.grace)
|
||||
return this.checkin
|
||||
},
|
||||
async saveCheckin() {
|
||||
const c = this.fixInts()
|
||||
await Api.checkin_create(c)
|
||||
await this.updateCheckins()
|
||||
this.checkin.name = ""
|
||||
await this.load()
|
||||
},
|
||||
async deleteCheckin(checkin) {
|
||||
await Api.checkin_delete(checkin)
|
||||
await this.updateCheckins()
|
||||
await this.load()
|
||||
},
|
||||
async updateCheckins() {
|
||||
async load() {
|
||||
const checkins = await Api.checkins()
|
||||
this.$store.commit('setCheckins', checkins)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
.sm {
|
||||
font-size: 8pt;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,67 @@
|
|||
<template>
|
||||
<div>
|
||||
<h3>Configuration</h3>
|
||||
For security reasons, all database credentials cannot be editted from this page.
|
||||
|
||||
<codemirror v-show="loaded" v-model="configs" ref="configs" :options="cmOptions" class="mt-4 codemirrorInput"/>
|
||||
|
||||
<button @click.prevent="save" class="btn col-12 btn-primary mt-3">Save</button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../../API";
|
||||
|
||||
import {codemirror} from 'vue-codemirror'
|
||||
import('codemirror/lib/codemirror.css')
|
||||
import('codemirror/mode/yaml/yaml.js')
|
||||
|
||||
export default {
|
||||
name: "Configs",
|
||||
components: {
|
||||
codemirror
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loaded: false,
|
||||
configs: null,
|
||||
cmOptions: {
|
||||
height: 700,
|
||||
tabSize: 4,
|
||||
lineNumbers: true,
|
||||
matchBrackets: true,
|
||||
mode: "text/x-yaml",
|
||||
line: true
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loaded = false
|
||||
this.update()
|
||||
this.loaded = true
|
||||
},
|
||||
watch: {
|
||||
"configs" () {
|
||||
this.$refs.configs.codemirror.refresh()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async update() {
|
||||
this.configs = await Api.configs()
|
||||
this.$refs.configs.codemirror.value = this.configs
|
||||
this.$refs.configs.codemirror.refresh()
|
||||
},
|
||||
async save() {
|
||||
try {
|
||||
await Api.configs_save(this.configs)
|
||||
} catch(e) {
|
||||
window.console.error(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,33 +1,58 @@
|
|||
<template>
|
||||
<div class="col-12 mt-4 mt-md-3">
|
||||
|
||||
<div class="row stats_area mb-5">
|
||||
<div class="col-4">
|
||||
<span class="font-6 font-weight-bold d-block">{{$store.getters.services.length}}</span>
|
||||
<span class="font-2">{{ $t('dashboard.total_services') }}</span>
|
||||
<span class="font-2">{{ $t('total_services') }}</span>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<span class="font-6 font-weight-bold d-block">{{failuresLast24Hours()}}</span>
|
||||
<span class="font-2">{{ $t('dashboard.failures_24_hours') }}</span>
|
||||
<span class="font-2">{{ $t('failures_24_hours') }}</span>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<span class="font-6 font-weight-bold d-block">{{$store.getters.onlineServices(true).length}}</span>
|
||||
<span class="font-2">{{ $t('dashboard.online_services') }}</span>
|
||||
<span class="font-2">{{ $t('online_services') }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="(service, index) in services" class="service_block" v-bind:key="index">
|
||||
<ServiceInfo :service=service />
|
||||
<div class="col-12" v-if="services.length === 0">
|
||||
<div class="alert alert-dark d-block">
|
||||
{{$t('no_services')}}
|
||||
<router-link v-if="$store.state.admin" to="/dashboard/create_service" class="btn btn-sm btn-success float-right">
|
||||
<font-awesome-icon icon="plus"/> {{ $t('create') }}
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="message in messagesInRange" class="bg-light shadow-sm p-3 pr-4 pl-4 col-12 mb-4">
|
||||
<font-awesome-icon icon="calendar" class="mr-3" size="1x"/> {{message.description}}
|
||||
<span class="d-block small text-muted mt-3">
|
||||
Starts at <strong>{{niceDate(message.start_on)}}</strong> till <strong>{{niceDate(message.end_on)}}</strong>
|
||||
({{dur(parseISO(message.start_on), parseISO(message.end_on))}})
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div v-for="(service, index) in services_no_group" class="col-12 col-md-4">
|
||||
<ServiceInfo :service="service" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="group in groups">
|
||||
<GroupedServices :group="group"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const ServiceInfo = () => import('@/components/Service/ServiceInfo')
|
||||
import GroupedServices from "@/components/Dashboard/GroupedServices";
|
||||
const ServiceInfo = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/ServiceInfo')
|
||||
|
||||
export default {
|
||||
name: 'DashboardIndex',
|
||||
components: {
|
||||
GroupedServices,
|
||||
ServiceInfo
|
||||
},
|
||||
data() {
|
||||
|
@ -36,12 +61,20 @@
|
|||
}
|
||||
},
|
||||
computed: {
|
||||
messagesInRange() {
|
||||
return this.$store.getters.globalMessages.filter(m => this.isAfter(this.now(), m.start_on) && this.isBefore(this.now(), m.end_on))
|
||||
},
|
||||
services() {
|
||||
return this.$store.getters.services
|
||||
}
|
||||
},
|
||||
services_no_group() {
|
||||
return this.$store.getters.servicesNoGroup
|
||||
},
|
||||
groups() {
|
||||
return this.$store.getters.groupsInOrder
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
|
||||
failuresLast24Hours() {
|
||||
let total = 0;
|
||||
this.services.map((s) => {
|
||||
|
|
|
@ -1,19 +1,26 @@
|
|||
<template>
|
||||
<div class="col-12">
|
||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div class="card-header">{{ $t('top_nav.announcements') }}</div>
|
||||
<div class="card contain-card mb-4">
|
||||
<div class="card-header">{{ $t('announcements') }}</div>
|
||||
<div class="card-body pt-0">
|
||||
<table class="table table-striped">
|
||||
|
||||
<div v-if="messages.length === 0">
|
||||
<div class="alert alert-dark d-block mt-3 mb-0">
|
||||
You currently don't have any Announcements! Create one using the form below.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table v-else class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{ $t('dashboard.title') }}</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">{{ $tc('dashboard.service', 1) }}</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">{{ $t('dashboard.begins') }}</th>
|
||||
<th scope="col">{{ $t('title') }}</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">{{ $tc('service', 1) }}</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">{{ $t('begins') }}</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="message in $store.getters.messages" v-bind:key="message.id">
|
||||
<tr v-for="message in messages" v-bind:key="message.id">
|
||||
<td>{{message.title}}</td>
|
||||
<td class="d-none d-md-table-cell">
|
||||
<router-link :to="serviceLink(service(message.service))">{{serviceName(service(message.service))}}</router-link>
|
||||
|
@ -40,7 +47,7 @@
|
|||
|
||||
<script>
|
||||
import Api from "../../API"
|
||||
import FormMessage from "../../forms/Message";
|
||||
const FormMessage = () => import(/* webpackChunkName: "dashboard" */ "../../forms/Message");
|
||||
|
||||
export default {
|
||||
name: 'DashboardMessages',
|
||||
|
@ -51,6 +58,11 @@
|
|||
message: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
messages() {
|
||||
return this.$store.getters.messages
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
goto(to) {
|
||||
this.$router.push(to)
|
||||
|
@ -69,13 +81,21 @@
|
|||
serviceName (service) {
|
||||
return service.name || "Global Message"
|
||||
},
|
||||
async deleteMessage(m) {
|
||||
let c = confirm(`Are you sure you want to delete message '${m.title}'?`)
|
||||
if (c) {
|
||||
async delete(m) {
|
||||
await Api.message_delete(m.id)
|
||||
const messages = await Api.messages()
|
||||
this.$store.commit('setMessages', messages)
|
||||
},
|
||||
async deleteMessage(m) {
|
||||
const modal = {
|
||||
visible: true,
|
||||
title: "Delete Announcement",
|
||||
body: `Are you sure you want to delete Announcement ${m.title}?`,
|
||||
btnColor: "btn-danger",
|
||||
btnText: "Delete Announcement",
|
||||
func: () => this.delete(m),
|
||||
}
|
||||
this.$store.commit("setModal", modal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<template>
|
||||
<div class="col-12">
|
||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div class="card-header">{{ $t('top_nav.services') }}
|
||||
<router-link v-if="$store.state.admin" to="/dashboard/create_service" class="btn btn-sm btn-outline-success float-right">
|
||||
<font-awesome-icon icon="plus"/> Create
|
||||
|
||||
<div class="card contain-card mb-4">
|
||||
<div class="card-header">{{ $t('services') }}
|
||||
<router-link v-if="$store.state.admin" to="/dashboard/create_service" class="btn btn-sm btn-success float-right">
|
||||
<font-awesome-icon icon="plus"/> {{$t('create')}}
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="card-body pt-0">
|
||||
|
@ -11,15 +12,22 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div class="card-header">{{ $t('top_nav.groups') }}</div>
|
||||
<div class="card contain-card mb-4">
|
||||
<div class="card-header">{{ $t('groups') }}</div>
|
||||
<div class="card-body pt-0">
|
||||
<table class="table">
|
||||
|
||||
<div v-if="groupsList.length === 0">
|
||||
<div class="alert alert-dark d-block mt-3 mb-0">
|
||||
You currently don't have any groups! Create one using the form below.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table v-else class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">{{ $t('dashboard.name') }}</th>
|
||||
<th scope="col">{{ $tc('dashboard.service', 2) }}</th>
|
||||
<th scope="col">{{ $t('dashboard.visibility') }}</th>
|
||||
<th scope="col">{{ $t('name') }}</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">{{ $tc('service', 2) }}</th>
|
||||
<th scope="col">{{ $t('visibility') }}</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -29,7 +37,7 @@
|
|||
<td><span class="drag_icon d-none d-md-inline">
|
||||
<font-awesome-icon icon="bars" class="mr-3" /></span> {{group.name}}
|
||||
</td>
|
||||
<td>{{$store.getters.servicesInGroup(group.id).length}}</td>
|
||||
<td class="d-none d-md-table-cell">{{$store.getters.servicesInGroup(group.id).length}}</td>
|
||||
<td>
|
||||
<span class="badge text-uppercase" :class="{'badge-primary': group.public, 'badge-secondary': !group.public}">
|
||||
{{group.public ? $t('public') : $t('private')}}
|
||||
|
@ -60,15 +68,17 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
const FormGroup = () => import('@/forms/Group')
|
||||
const ToggleSwitch = () => import('@/forms/ToggleSwitch')
|
||||
const ServicesList = () => import('@/components/Dashboard/ServicesList')
|
||||
const Modal = () => import(/* webpackChunkName: "dashboard" */ "@/components/Elements/Modal")
|
||||
const FormGroup = () => import(/* webpackChunkName: "dashboard" */ '@/forms/Group')
|
||||
const ToggleSwitch = () => import(/* webpackChunkName: "dashboard" */ '@/forms/ToggleSwitch')
|
||||
const ServicesList = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/ServicesList')
|
||||
import Api from "../../API";
|
||||
import draggable from 'vuedraggable'
|
||||
const draggable = () => import(/* webpackChunkName: "dashboard" */ 'vuedraggable')
|
||||
|
||||
export default {
|
||||
name: 'DashboardServices',
|
||||
components: {
|
||||
Modal,
|
||||
ServicesList,
|
||||
ToggleSwitch,
|
||||
FormGroup,
|
||||
|
@ -105,13 +115,24 @@
|
|||
this.group = g
|
||||
this.edit = !mode
|
||||
},
|
||||
async deleteGroup(g) {
|
||||
let c = confirm(`Are you sure you want to delete '${g.name}'?`)
|
||||
if (c) {
|
||||
confirm_delete(service) {
|
||||
|
||||
},
|
||||
async delete(g) {
|
||||
await Api.group_delete(g.id)
|
||||
const groups = await Api.groups()
|
||||
this.$store.commit('setGroups', groups)
|
||||
},
|
||||
async deleteGroup(g) {
|
||||
const modal = {
|
||||
visible: true,
|
||||
title: "Delete Group",
|
||||
body: `Are you sure you want to delete group ${g.name}? All services attached will be removed from this group.`,
|
||||
btnColor: "btn-danger",
|
||||
btnText: "Delete Group",
|
||||
func: () => this.delete(g),
|
||||
}
|
||||
this.$store.commit("setModal", modal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="col-12">
|
||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div class="card-header">{{ $t('top_nav.users') }}</div>
|
||||
<div class="card contain-card mb-4">
|
||||
<div class="card-header">{{ $t('users') }}</div>
|
||||
<div class="card-body pt-0">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
|
@ -9,6 +9,7 @@
|
|||
<th scope="col">{{$t('username')}}</th>
|
||||
<th scope="col">{{$t('type')}}</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">{{ $t('last_login') }}</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">Scopes</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -22,6 +23,7 @@
|
|||
</span>
|
||||
</td>
|
||||
<td class="d-none d-md-table-cell">{{niceDate(user.updated_at)}}</td>
|
||||
<td class="d-none d-md-table-cell">{{user.scopes}}</td>
|
||||
<td class="text-right">
|
||||
<div class="btn-group">
|
||||
<a @click.prevent="editUser(user, edit)" href="#" class="btn btn-outline-secondary edit-user">
|
||||
|
@ -45,7 +47,7 @@
|
|||
|
||||
<script>
|
||||
import Api from "../../API"
|
||||
const FormUser = () => import('@/forms/User')
|
||||
const FormUser = () => import(/* webpackChunkName: "dashboard" */ '@/forms/User')
|
||||
|
||||
export default {
|
||||
name: 'DashboardUsers',
|
||||
|
@ -72,13 +74,21 @@
|
|||
this.user = u
|
||||
this.edit = !mode
|
||||
},
|
||||
async deleteUser(u) {
|
||||
let c = confirm(`Are you sure you want to delete user '${u.username}'?`)
|
||||
if (c) {
|
||||
async delete(u) {
|
||||
await Api.user_delete(u.id)
|
||||
const users = await Api.users()
|
||||
this.$store.commit('setUsers', users)
|
||||
},
|
||||
async deleteUser(u) {
|
||||
const modal = {
|
||||
visible: true,
|
||||
title: "Delete User",
|
||||
body: `Are you sure you want to delete user ${u.username}?`,
|
||||
btnColor: "btn-danger",
|
||||
btnText: "Delete User",
|
||||
func: () => this.delete(u),
|
||||
}
|
||||
this.$store.commit("setModal", modal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,11 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import FormGroup from "../../forms/Group";
|
||||
const FormGroup = () => import(/* webpackChunkName: "dashboard" */ "../../forms/Group");
|
||||
import Api from "../../API";
|
||||
import ToggleSwitch from "../../forms/ToggleSwitch";
|
||||
import draggable from 'vuedraggable'
|
||||
import FormService from "../../forms/Service";
|
||||
const ToggleSwitch = () => import(/* webpackChunkName: "dashboard" */ "../../forms/ToggleSwitch");
|
||||
const draggable = () => import(/* webpackChunkName: "dashboard" */ 'vuedraggable')
|
||||
const FormService = () => import(/* webpackChunkName: "dashboard" */ "../../forms/Service");
|
||||
|
||||
export default {
|
||||
name: 'EditService',
|
||||
|
|
|
@ -1,22 +1,77 @@
|
|||
<template>
|
||||
<div class="col-12">
|
||||
<h2>{{service.name}} Failures
|
||||
<button v-if="failures.length>0" @click="deleteFailures" class="btn btn-outline-danger float-right">Delete All</button></h2>
|
||||
<div class="list-group mt-3 mb-4">
|
||||
<div v-if="service" class="col-12">
|
||||
<h3>{{service.name}} Failures
|
||||
<button v-if="failures.length>0" @click="deleteFailures" class="btn btn-danger float-right">Delete All</button>
|
||||
</h3>
|
||||
|
||||
<div class="alert alert-info" v-if="failures.length===0">
|
||||
<div class="card mt-4 mb-4">
|
||||
<div class="card-header">
|
||||
Search and Filter
|
||||
<span class="float-right">
|
||||
<font-awesome-icon v-if="loading" icon="circle-notch" spin/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="form-row">
|
||||
<div class="col">
|
||||
<label for="fromdate">From Date</label>
|
||||
<flatPickr id="fromdate" :disabled="loading" @on-change="load" v-model="start_time" :config="{ wrap: true, allowInput: true, enableTime: true, dateFormat: 'Z', altInput: true, altFormat: 'Y-m-d h:i K', maxDate: new Date() }" type="text" class="form-control text-left d-block" required />
|
||||
</div>
|
||||
<div class="col">
|
||||
<label for="todate">To Date</label>
|
||||
<flatPickr id="todate" :disabled="loading" @on-change="load" v-model="end_time" :config="{ wrap: true, allowInput: true, enableTime: true, dateFormat: 'Z', altInput: true, altFormat: 'Y-m-d h:i K', maxDate: new Date() }" type="text" class="form-control text-left d-block" required />
|
||||
</div>
|
||||
<div class="col">
|
||||
<label for="search">Search Terms</label>
|
||||
<input id="search" type="text" v-model="search" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row mt-3">
|
||||
<div class="col">
|
||||
<span @click="show_checkins = !!show_checkins" class="switch float-left">
|
||||
<input v-model="show_checkins" type="checkbox" class="switch" id="showcheckins" v-bind:checked="show_checkins">
|
||||
<label v-if="show_checkins" for="showcheckins">Showing Checkin Failures</label>
|
||||
<label v-else for="showcheckins">View Checkin Failures</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="failures.length === 0" class="alert alert-info">
|
||||
<span v-if="search">
|
||||
Could not find any failures with issue: "{{search}}"
|
||||
</span>
|
||||
<span v-else>
|
||||
You don't have any failures for {{service.name}}. Way to go!
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div v-for="(failure, index) in failures" :key="index" class="mb-2 list-group-item list-group-item-action flex-column align-items-start">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">{{failure.issue}}</h5>
|
||||
<small>{{niceDate(failure.created_at)}}</small>
|
||||
</div>
|
||||
<p class="mb-1">{{failure.issue}}</p>
|
||||
</div>
|
||||
<table v-else class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">#</th>
|
||||
<th scope="col">Issue</th>
|
||||
<th scope="col">Status Code</th>
|
||||
<th scope="col">Ping</th>
|
||||
<th scope="col">Created</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(failure, index) in failures" :key="index">
|
||||
<th class="font-1" scope="row">{{failure.id}}</th>
|
||||
<td class="font-1">{{failure.issue}}</td>
|
||||
<td class="font-1">{{failure.error_code}}</td>
|
||||
<td class="font-1">{{humanTime(failure.ping)}}</td>
|
||||
<td class="font-1">{{ago(failure.created_at)}}</td>
|
||||
</tr>
|
||||
|
||||
<nav v-if="total > 4" class="mt-3">
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<nav v-if="total > 4 && failures.length !== 0" class="mt-3">
|
||||
<ul class="pagination justify-content-center">
|
||||
<li class="page-item" :class="{'disabled': page===1}">
|
||||
<a @click.prevent="gotoPage(page-1)" :disabled="page===1" class="page-link" href="#" aria-label="Previous">
|
||||
|
@ -35,39 +90,59 @@
|
|||
</li>
|
||||
</ul>
|
||||
<div class="text-center">
|
||||
<span class="text-black-50">{{total}} Total</span>
|
||||
<span>{{total}} Failures</span>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../../API";
|
||||
import flatPickr from 'vue-flatpickr-component';
|
||||
import 'flatpickr/dist/flatpickr.css';
|
||||
|
||||
export default {
|
||||
name: 'Failures',
|
||||
components: {
|
||||
flatPickr
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
service: {},
|
||||
failures: [],
|
||||
limit: 10,
|
||||
loading: true,
|
||||
search: "",
|
||||
show_checkins: false,
|
||||
service: null,
|
||||
fails: [],
|
||||
limit: 64,
|
||||
offset: 0,
|
||||
total: 0,
|
||||
page: 1
|
||||
page: 1,
|
||||
start_time: this.nowSubtract(216000).toISOString(),
|
||||
end_time: this.nowSubtract(0).toISOString(),
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$route': 'reloadTimes',
|
||||
},
|
||||
computed: {
|
||||
failures() {
|
||||
let sorted = this.fails
|
||||
if (this.show_checkins) {
|
||||
sorted = sorted.filter(f => f.method === "checkin");
|
||||
} else {
|
||||
sorted = sorted.filter(f => f.method !== "checkin");
|
||||
}
|
||||
if (this.search !== "") {
|
||||
sorted = sorted.filter(f => f.issue.toLowerCase().includes(this.search));
|
||||
}
|
||||
return sorted
|
||||
},
|
||||
pages() {
|
||||
return Math.floor(this.total / this.limit)
|
||||
},
|
||||
maxPages() {
|
||||
const p = Math.floor(this.total / this.limit)
|
||||
if (p > 16) {
|
||||
return 16
|
||||
} else {
|
||||
return p
|
||||
}
|
||||
return Math.floor(this.total / this.limit)
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
|
@ -76,14 +151,22 @@ export default {
|
|||
await this.gotoPage(1)
|
||||
},
|
||||
methods: {
|
||||
async deleteFailures() {
|
||||
const c = confirm('Are you sure you want to delete all failures?')
|
||||
if (c) {
|
||||
async delete() {
|
||||
await Api.service_failures_delete(this.service)
|
||||
this.service = await Api.service(this.service.id)
|
||||
this.total = 0
|
||||
await this.load()
|
||||
},
|
||||
async deleteFailures() {
|
||||
const modal = {
|
||||
visible: true,
|
||||
title: "Delete All Failures",
|
||||
body: `Are you sure you want to delete all Failures for service ${this.service.title}?`,
|
||||
btnColor: "btn-danger",
|
||||
btnText: "Delete Failures",
|
||||
func: () => this.delete(),
|
||||
}
|
||||
this.$store.commit("setModal", modal)
|
||||
},
|
||||
async gotoPage(page) {
|
||||
this.page = page;
|
||||
|
@ -91,15 +174,10 @@ export default {
|
|||
await this.load()
|
||||
},
|
||||
async load() {
|
||||
this.failures = await Api.service_failures(this.service.id, 0, 9999999999, this.limit, this.offset)
|
||||
this.loading = true
|
||||
this.fails = await Api.service_failures(this.service.id, this.toUnix(this.parseISO(this.start_time)), this.toUnix(this.parseISO(this.end_time)), this.limit, this.offset)
|
||||
this.loading = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
.sm {
|
||||
font-size: 8pt;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
<template>
|
||||
<div class="row">
|
||||
<h5 v-if="group.name && group_services" class="h5 col-12 mb-3 mt-2 text-dim">
|
||||
<font-awesome-icon @click="toggle" :icon="expanded ? 'minus' : 'plus'" class="pointer mr-3"/> {{group.name}}
|
||||
<span class="badge badge-success text-uppercase float-right ml-2">{{services_online.length}} {{$t('online')}}</span>
|
||||
<span v-if="services_online.services_offline > 0" class="badge badge-danger text-uppercase float-right">
|
||||
{{services_offline.length}} {{$t('offline')}}
|
||||
</span>
|
||||
</h5>
|
||||
<div class="col-12 col-md-4" v-if="expanded" v-for="service in group_services">
|
||||
<ServiceInfo :service="service" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const ServiceInfo = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/ServiceInfo')
|
||||
|
||||
|
||||
export default {
|
||||
name: "GroupedServices",
|
||||
components: {
|
||||
ServiceInfo
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
expanded: true
|
||||
}
|
||||
},
|
||||
props: {
|
||||
group: {
|
||||
required: true,
|
||||
type: Object,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
services_online() {
|
||||
return this.$store.getters.servicesInGroup(this.group.id).filter((s) => s.online)
|
||||
},
|
||||
services_offline() {
|
||||
return this.$store.getters.servicesInGroup(this.group.id).filter((s) => !s.online)
|
||||
},
|
||||
group_services() {
|
||||
return this.$store.getters.servicesInGroup(this.group.id)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
toggle() {
|
||||
this.expanded = !this.expanded
|
||||
},
|
||||
dashboard_cookies() {
|
||||
const data = [{group: 5, show: false}]
|
||||
if (!this.$cookies.isKey("statping_layout")) {
|
||||
this.$cookies.set("statping_layout", JSON.stringify(data))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,250 @@
|
|||
<template>
|
||||
<div class="mb-5">
|
||||
<h3 v-if="!loaded" >Import and Export</h3>
|
||||
<p v-if="!loaded">
|
||||
You can export your current Statping services, groups, notifiers, and other settings to a JSON file.
|
||||
</p>
|
||||
|
||||
<div v-if="!loaded" class="mt-4 row">
|
||||
<div class="col-8 custom-file">
|
||||
<input @change="onFileChange" type="file" class="custom-file-input pointer" id="customFile" accept=".json,application/json">
|
||||
<label class="custom-file-label" for="customFile">Choose exported Statping JSON file</label>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<a class="btn btn-block btn-light btn-outline-secondary" href="/api/settings/export">Export</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loaded" class="col-12 mb-4">
|
||||
<h3>Core Settings
|
||||
<span @click="file.core.enabled = !!file.core.enabled" class="switch switch-sm float-right">
|
||||
<input @change="update" v-model="file.core.enabled" type="checkbox" class="switch" :id="`switch-core`">
|
||||
<label :for="`switch-core`"></label>
|
||||
</span>
|
||||
</h3>
|
||||
|
||||
<div class="row mb-2"><span class="col-4">Name</span><span class="col-8 text-right font-weight-bold">{{file.core.name}}</span></div>
|
||||
<div class="row mb-2"><span class="col-4">Description</span><span class="col-8 text-right font-weight-bold">{{file.core.description}}</span></div>
|
||||
<div class="row mb-2"><span class="col-4">Domain</span><span class="col-8 text-right font-weight-bold">{{file.core.domain}}</span></div>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-if="loaded" class="col-12 mb-4">
|
||||
<h3>Users
|
||||
<button @click.prevent="toggle_all(file.users)" class="btn btn-sm btn-outline-dark float-right mt-1">Select All</button>
|
||||
</h3>
|
||||
<div v-if="!file.users" class="alert alert-link">
|
||||
No Users in file
|
||||
</div>
|
||||
<div v-for="user in file.users" v-bind:key="user.id" class="row">
|
||||
<div class="col-4 font-weight-bold">
|
||||
{{user.username}}
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{user.email}}
|
||||
</div>
|
||||
<div class="col-2 text-right">
|
||||
<span @click="user.enabled = !!user.enabled" class="switch switch-sm">
|
||||
<input @change="update" v-model="user.enabled" type="checkbox" class="switch" :id="`switch-user-${user.id}`">
|
||||
<label :for="`switch-user-${user.id}`"></label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div v-if="loaded" class="col-12 mb-4">
|
||||
<h3>Checkins
|
||||
<button @click.prevent="toggle_all(file.checkins)" class="btn btn-sm btn-outline-dark float-right mt-1">Select All</button>
|
||||
</h3>
|
||||
<div v-if="!file.checkins" class="alert alert-link">
|
||||
No Checkins in file
|
||||
</div>
|
||||
<div v-for="checkin in file.checkins" v-bind:key="checkin.id" class="row">
|
||||
<div class="col-4 font-weight-bold">
|
||||
{{checkin.name}}
|
||||
</div>
|
||||
<div class="col-6">
|
||||
Service #{{checkin.service_id}}
|
||||
</div>
|
||||
<div class="col-2 text-right">
|
||||
<span @click="checkin.enabled = !!checkin.enabled" class="switch switch-sm">
|
||||
<input @change="update" v-model="checkin.enabled" type="checkbox" class="switch" :id="`switch-checkin-${checkin.id}`">
|
||||
<label :for="`switch-checkin-${checkin.id}`"></label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loaded" class="col-12 mb-4">
|
||||
<h3>Services
|
||||
<button @click.prevent="toggle_all(file.services)" class="btn btn-sm btn-outline-dark float-right mt-1">Select All</button>
|
||||
</h3>
|
||||
<div v-if="!file.services" class="alert alert-link">
|
||||
No Services in file
|
||||
</div>
|
||||
<div v-for="service in file.services" v-bind:key="service.id" class="row">
|
||||
<div class="col-4 font-weight-bold">
|
||||
{{service.name}}
|
||||
</div>
|
||||
<div class="col-6">
|
||||
{{service.domain}}
|
||||
</div>
|
||||
<div class="col-2 text-right">
|
||||
<span @click="service.enabled = !!service.enabled" class="switch switch-sm">
|
||||
<input @change="update" v-model="service.enabled" type="checkbox" class="switch" :id="`switch-service-${service.id}`">
|
||||
<label :for="`switch-service-${service.id}`"></label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loaded" class="col-12 mb-4">
|
||||
<h3>Groups
|
||||
<button @click.prevent="toggle_all(file.groups)" class="btn btn-sm btn-outline-dark float-right mt-1">Select All</button>
|
||||
</h3>
|
||||
<div v-if="!file.groups" class="alert alert-link">
|
||||
No Groups in file
|
||||
</div>
|
||||
<div v-for="group in file.groups" v-bind:key="group.id" class="row">
|
||||
<div class="col-4 font-weight-bold">
|
||||
{{group.name}}
|
||||
</div>
|
||||
<div class="col-8 text-right">
|
||||
<span @click="group.enabled = !!group.enabled" class="switch switch-sm">
|
||||
<input @change="update" v-model="group.enabled" type="checkbox" class="switch" :id="`switch-group-${group.id}`">
|
||||
<label :for="`switch-group-${group.id}`"></label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loaded" class="col-12 mb-4">
|
||||
<h3>Incidents
|
||||
<button @click.prevent="toggle_all(file.incidents)" class="btn btn-sm btn-outline-dark float-right mt-1">Select All</button>
|
||||
</h3>
|
||||
<div v-if="!file.incidents" class="alert alert-link">
|
||||
No Incidents in file
|
||||
</div>
|
||||
<div v-for="incident in file.incidents" v-bind:key="incident.id" class="row">
|
||||
<div class="col-4 font-weight-bold">
|
||||
{{incident.name}}
|
||||
</div>
|
||||
<div class="col-8 text-right">
|
||||
<span @click="incident.enabled = !!incident.enabled" class="switch switch-sm">
|
||||
<input @change="update" v-model="incident.enabled" type="checkbox" class="switch" :id="`switch-incident-${incident.id}`">
|
||||
<label :for="`switch-incident-${incident.id}`"></label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loaded" class="col-12 mb-3">
|
||||
<h3>Notifiers
|
||||
<button @click.prevent="toggle_all(file.notifiers)" class="btn btn-sm btn-outline-dark float-right mt-1">Select All</button>
|
||||
</h3>
|
||||
<div v-if="!file.notifiers" class="alert alert-link">
|
||||
No Notifiers in file
|
||||
</div>
|
||||
<div v-for="notifier in file.notifiers" v-bind:key="notifier.id" class="row">
|
||||
<div class="col-4">
|
||||
{{notifier.title}}
|
||||
</div>
|
||||
<div class="col-8 text-right">
|
||||
<span @click="notifier.enabled = !!notifier.enabled" class="switch">
|
||||
<input @change="update" v-model="notifier.enabled" type="checkbox" class="switch" :id="`switch-notifier-${notifier.id}`">
|
||||
<label :for="`switch-notifier-${notifier.id}`"></label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="error" class="alert alert-danger">
|
||||
{{error}}
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<button v-if="loaded" @click.prevent="import_all" class="btn btn-block btn-success">Import</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from '../../API';
|
||||
|
||||
export default {
|
||||
name: "Importer",
|
||||
data () {
|
||||
return {
|
||||
error: null,
|
||||
file: null,
|
||||
loaded: false,
|
||||
output: null,
|
||||
all: {
|
||||
notifiers: false,
|
||||
services: false,
|
||||
groups: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
clean_elem(elem) {
|
||||
if (!elem) {
|
||||
return null
|
||||
}
|
||||
elem.map(e => delete(e.enabled) && delete(e.id))
|
||||
return elem
|
||||
},
|
||||
async import_all() {
|
||||
this.error = null
|
||||
const outgoing = {
|
||||
core: this.output.core,
|
||||
users: this.clean_elem(this.output.users),
|
||||
services: this.clean_elem(this.output.services),
|
||||
groups: this.clean_elem(this.output.groups),
|
||||
notifiers: this.clean_elem(this.output.notifiers),
|
||||
checkins: this.clean_elem(this.output.checkins),
|
||||
}
|
||||
try {
|
||||
await Api.import(outgoing)
|
||||
} catch(e) {
|
||||
this.error = e
|
||||
}
|
||||
},
|
||||
toggle_all(elem) {
|
||||
elem.map(s => s.enabled = true)
|
||||
this.update()
|
||||
},
|
||||
update() {
|
||||
this.output = {
|
||||
core: this.file.core.enabled ? this.file.core : null,
|
||||
users: this.file.users.filter(s => s.enabled),
|
||||
services: this.file.services.filter(s => s.enabled),
|
||||
groups: this.file.groups.filter(s => s.enabled),
|
||||
notifiers: this.file.notifiers.filter(s => s.enabled),
|
||||
checkins: this.file.checkins.filter(s => s.enabled),
|
||||
}
|
||||
},
|
||||
onFileChange(e) {
|
||||
let files = e.target.files || e.dataTransfer.files;
|
||||
if (!files.length)
|
||||
return;
|
||||
this.processJSON(files[0]);
|
||||
},
|
||||
processJSON(file) {
|
||||
let reader = new FileReader();
|
||||
reader.onload = (e) => {
|
||||
this.file = JSON.parse(e.target.result);
|
||||
this.file.core.enabled = false
|
||||
};
|
||||
reader.readAsText(file);
|
||||
this.loaded = true
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -1,10 +1,10 @@
|
|||
<template>
|
||||
<div class="col-12">
|
||||
|
||||
<div v-for="incident in incidents" :key="incident.id" class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div v-for="incident in incidents" :key="incident.id" class="card contain-card mb-4">
|
||||
<div class="card-header">Incident: {{incident.title}}
|
||||
<button @click="deleteIncident(incident)" class="btn btn-sm btn-danger float-right">
|
||||
<font-awesome-icon icon="times" /> Delete
|
||||
<font-awesome-icon icon="times" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
@ -14,7 +14,7 @@
|
|||
</div>
|
||||
|
||||
|
||||
<div class="card contain-card text-black-50 bg-white">
|
||||
<div class="card contain-card">
|
||||
<div class="card-header">Create Incident</div>
|
||||
<div class="card-body">
|
||||
<form @submit.prevent="createIncident">
|
||||
|
@ -50,8 +50,9 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../../API";
|
||||
const FormIncidentUpdates = () => import('@/forms/IncidentUpdates')
|
||||
import Api from "../../API";
|
||||
|
||||
const FormIncidentUpdates = () => import(/* webpackChunkName: "dashboard" */ '@/forms/IncidentUpdates')
|
||||
|
||||
export default {
|
||||
name: 'Incidents',
|
||||
|
@ -79,16 +80,24 @@
|
|||
|
||||
methods: {
|
||||
|
||||
async deleteIncident(incident) {
|
||||
let c = confirm(`Are you sure you want to delete '${incident.title}'?`)
|
||||
if (c) {
|
||||
this.res = await Api.incident_delete(incident)
|
||||
async delete(i) {
|
||||
this.res = await Api.incident_delete(i)
|
||||
if (this.res.status === "success") {
|
||||
this.incidents = this.incidents.filter(obj => obj.id !== incident.id); // this is better in terms of not having to querry the db to get a fresh copy of all updates
|
||||
this.incidents = this.incidents.filter(obj => obj.id !== i.id);
|
||||
//await this.loadIncidents()
|
||||
} // TODO: further error checking here... maybe alert user it failed with modal or so
|
||||
}
|
||||
},
|
||||
async deleteIncident(incident) {
|
||||
const modal = {
|
||||
visible: true,
|
||||
title: "Delete Incident",
|
||||
body: `Are you sure you want to delete Incident ${incident.title}?`,
|
||||
btnColor: "btn-danger",
|
||||
btnText: "Delete Incident",
|
||||
func: () => this.delete(incident),
|
||||
}
|
||||
this.$store.commit("setModal", modal)
|
||||
},
|
||||
|
||||
async createIncident() {
|
||||
this.res = await Api.incident_create(this.serviceID, this.incident)
|
||||
|
@ -112,10 +121,3 @@
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
.sm {
|
||||
font-size: 8pt;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
<template>
|
||||
<div class="row p-2">
|
||||
|
||||
<div v-if="loaded && last_failure && failureBefore" class="col-12 text-danger font-2 m-0 mb-2">
|
||||
<font-awesome-icon icon="exclamation" class="mr-1 text-danger font-weight-bold" size="1x"/> Recent Failure<br>
|
||||
<span class="font-italic font-weight-light text-dim mt-1" style="max-width: 270px">
|
||||
Last failure was {{ago(last_failure.created_at)}} ago. {{last_failure.issue}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div v-if="loaded" v-for="message in messages" class="col-12 font-2 m-0 mb-2">
|
||||
<font-awesome-icon icon="calendar" class="mr-1" size="1x"/> Upcoming Announcement<br>
|
||||
<span class="font-italic font-weight-light text-dim mt-1">{{message.description}}</span>
|
||||
<span class="font-0 text-dim float-right font-weight-light mt-1">@ <strong>{{niceDate(message.start_on)}}</strong>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div v-if="loaded" v-for="incident in incidents" class="col-12 font-2 m-0 mb-2">
|
||||
<font-awesome-icon icon="bullhorn" class="mr-1" size="1x"/>Recent Incident<br>
|
||||
<span class="font-italic font-weight-light text-dim mt-1" style="max-width: 270px">{{incident.title}} - {{incident.description}}</span>
|
||||
<span class="font-0 text-dim float-right font-weight-light mt-1">@ <strong>{{niceDate(incident.created_at)}}</strong></span>
|
||||
</div>
|
||||
|
||||
<div v-if="success_event && !failureBefore" class="col-12 font-2 m-0 mb-2">
|
||||
<span class="text-success"><font-awesome-icon icon="check" class="mr-1" size="1x"/>No New Events</span>
|
||||
<span class="font-italic d-inline-block text-truncate text-dim mt-1" style="max-width: 270px">
|
||||
Last failure was {{ago(service.last_error)}} ago.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../../API";
|
||||
const Loading = () => import(/* webpackChunkName: "index" */ "@/components/Elements/Loading");
|
||||
|
||||
export default {
|
||||
name: "ServiceEvents",
|
||||
components: {
|
||||
Loading
|
||||
},
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
incidents: null,
|
||||
loaded: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.load()
|
||||
},
|
||||
computed: {
|
||||
last_failure() {
|
||||
if (!this.service.failures) {
|
||||
return null
|
||||
}
|
||||
return this.service.failures[0]
|
||||
},
|
||||
failureBefore() {
|
||||
return this.isAfter(this.parseISO(this.service.last_error), this.nowSubtract(43200).toISOString())
|
||||
},
|
||||
messages() {
|
||||
return this.$store.getters.serviceMessages(this.service.id)
|
||||
},
|
||||
success_event() {
|
||||
if (this.service.online && this.service.messages.length === 0 && this.service.incidents.length === 0) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async load() {
|
||||
this.loaded = false
|
||||
await this.getMessages()
|
||||
await this.getIncidents()
|
||||
this.loaded = true
|
||||
},
|
||||
async getMessages() {
|
||||
// this.messages = this.$store.getters.serviceMessages(this.service.id)
|
||||
},
|
||||
async getIncidents() {
|
||||
this.incidents = await Api.incidents_service(this.service.id)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -0,0 +1,173 @@
|
|||
<template>
|
||||
<div class="dashboard_card card mb-4" :class="{'offline-card': !service.online}">
|
||||
<div class="card-header pb-1">
|
||||
<h6 v-observe-visibility="setVisible">
|
||||
<router-link :to="serviceLink(service)" class="no-decoration">{{service.name}}</router-link>
|
||||
<span class="badge float-right text-uppercase" :class="{'badge-success': service.online, 'badge-danger': !service.online}">
|
||||
{{service.online ? $t('online') : $t('offline')}}
|
||||
</span>
|
||||
</h6>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div v-if="loaded" class="row pl-2">
|
||||
<div class="col-md-6 col-sm-12 pl-2 mt-2 mt-md-0 mb-3">
|
||||
<ServiceSparkLine :title="set2_name" subtitle="Latency Last 24 Hours" :series="set2"/>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12 pl-0 mt-4 mt-md-0 mb-3">
|
||||
<ServiceSparkLine :title="set1_name" subtitle="Latency Last 7 Days" :series="set1"/>
|
||||
</div>
|
||||
<ServiceEvents :service="service"/>
|
||||
</div>
|
||||
<div v-else class="row mb-5">
|
||||
<div class="col-12 col-md-6 text-center">
|
||||
<font-awesome-icon icon="circle-notch" class="text-dim" size="2x" spin/>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 text-center text-dim">
|
||||
<font-awesome-icon icon="circle-notch" class="text-dim" size="2x" spin/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-5 pr-0">
|
||||
<span class="small text-dim">{{ hoverbtn }}</span>
|
||||
</div>
|
||||
|
||||
<div class="col-7 pr-2 pl-0">
|
||||
<div class="btn-group float-right">
|
||||
<button @click="$router.push({path: `/dashboard/service/${service.id}/incidents`, params: {id: service.id}})" @mouseleave="unsetHover" @mouseover="setHover($t('incidents'))" class="btn btn-sm btn-white incident">
|
||||
<font-awesome-icon icon="bullhorn"/>
|
||||
</button>
|
||||
<button @click="$router.push({path: `/dashboard/service/${service.id}/checkins`, params: {id: service.id}})" @mouseleave="unsetHover" @mouseover="setHover($t('checkins'))" class="btn btn-sm btn-white checkins">
|
||||
<font-awesome-icon icon="calendar-check"/>
|
||||
</button>
|
||||
<button @click="$router.push({path: `/dashboard/service/${service.id}/failures`, params: {id: service.id}})" @mouseleave="unsetHover" @mouseover="setHover($t('failures'))" class="btn btn-sm btn-white failures">
|
||||
<font-awesome-icon icon="exclamation-triangle"/> <span v-if="service.stats.failures !== 0" class="badge badge-danger ml-1">{{service.stats.failures}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<span v-for="(failure, index) in failures" v-bind:key="index" class="alert alert-light">
|
||||
{{ $t('failed') }} {{failure.created_at}}<br>
|
||||
{{failure.issue}}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const Checkin = () => import(/* webpackChunkName: "dashboard" */ '../../forms/Checkin');
|
||||
const FormMessage = () => import(/* webpackChunkName: "dashboard" */ '../../forms/Message');
|
||||
const ServiceFailures = () => import(/* webpackChunkName: "dashboard" */ '../Service/ServiceFailures');
|
||||
const ServiceSparkLine = () => import(/* webpackChunkName: "dashboard" */ "./ServiceSparkLine");
|
||||
import Api from "../../API";
|
||||
|
||||
const ServiceEvents = () => import(/* webpackChunkName: "dashboard" */ "@/components/Dashboard/ServiceEvents");
|
||||
|
||||
export default {
|
||||
name: 'ServiceInfo',
|
||||
components: {
|
||||
ServiceEvents,
|
||||
Checkin,
|
||||
ServiceFailures,
|
||||
FormMessage,
|
||||
ServiceSparkLine
|
||||
},
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
uptime: null,
|
||||
hovered: false,
|
||||
hoverbtn: "",
|
||||
openTab: "",
|
||||
set1: [],
|
||||
set2: [],
|
||||
loaded: false,
|
||||
set1_name: "",
|
||||
set2_name: "",
|
||||
failures: null,
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
mounted() {
|
||||
this.unsetHover()
|
||||
},
|
||||
methods: {
|
||||
setHover(name) {
|
||||
this.hoverbtn = name
|
||||
},
|
||||
unsetHover() {
|
||||
this.hoverbtn = this.$t('uptime', [this.service.online_7_days])
|
||||
},
|
||||
async setVisible(isVisible, entry) {
|
||||
if (isVisible && !this.visible) {
|
||||
await this.loadInfo()
|
||||
await this.getUptime()
|
||||
this.visible = true
|
||||
}
|
||||
},
|
||||
async getUptime() {
|
||||
const end = this.endOf("day", this.now())
|
||||
const start = this.beginningOf("day", this.nowSubtract(3 * 86400))
|
||||
this.uptime = await Api.service_uptime(this.service.id, this.toUnix(start), this.toUnix(end))
|
||||
},
|
||||
async loadInfo() {
|
||||
this.set1 = await this.getHits(86400 * 7, "12h")
|
||||
this.set1_name = this.calc(this.set1)
|
||||
this.set2 = await this.getHits(86400, "60m")
|
||||
this.set2_name = this.calc(this.set2)
|
||||
this.loaded = true
|
||||
},
|
||||
Tab(name) {
|
||||
if (this.openTab === name) {
|
||||
this.openTab = ''
|
||||
return
|
||||
}
|
||||
this.openTab=name;
|
||||
},
|
||||
sinceYesterday(data) {
|
||||
let total = 0
|
||||
data.forEach((f) => {
|
||||
total += parseInt(f.y)
|
||||
});
|
||||
total = total / data.length
|
||||
},
|
||||
async getHits(seconds, group) {
|
||||
let start = this.nowSubtract(seconds)
|
||||
let end = this.endOf("today")
|
||||
const startEnd = this.startEndParams(start, end, group)
|
||||
const fetched = await Api.service_hits(this.service.id, startEnd.start, startEnd.end, group, true)
|
||||
const data = this.convertToChartData(fetched, 0.001, true)
|
||||
return [{name: "Latency", ...data}]
|
||||
},
|
||||
calc(s) {
|
||||
let data = s[0].data
|
||||
|
||||
if (data) {
|
||||
let total = 0
|
||||
data.forEach((f) => {
|
||||
total += parseInt(f.y)
|
||||
});
|
||||
total = total / data.length
|
||||
return Math.round(total) + " ms"
|
||||
} else {
|
||||
return "Offline"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
|
@ -1,11 +1,10 @@
|
|||
<template v-if="series.length">
|
||||
<apexchart width="100%" height="180" type="bar" :options="chartOpts" :series="series"></apexchart>
|
||||
<apexchart width="100%" height="100" type="bar" :options="chartOpts" :series="series"></apexchart>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const timeoptions = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' };
|
||||
|
||||
|
||||
export default {
|
||||
name: 'ServiceSparkLine',
|
||||
props: {
|
||||
|
@ -38,6 +37,9 @@
|
|||
enabled: true
|
||||
},
|
||||
},
|
||||
showPoint: false,
|
||||
fullWidth:true,
|
||||
chartPadding: {top: 0,right: 0,bottom: 0,left: 0},
|
||||
stroke: {
|
||||
curve: 'straight'
|
||||
},
|
||||
|
@ -51,21 +53,20 @@
|
|||
tooltip: {
|
||||
theme: false,
|
||||
enabled: true,
|
||||
custom: function({series, seriesIndex, dataPointIndex, w}) {
|
||||
custom: ({series, seriesIndex, dataPointIndex, w}) => {
|
||||
let ts = w.globals.seriesX[seriesIndex][dataPointIndex];
|
||||
const dt = new Date(ts).toLocaleDateString("en-us", timeoptions)
|
||||
let val = series[seriesIndex][dataPointIndex];
|
||||
val = val + " ms"
|
||||
return `<div class="chartmarker"><span>Average Response Time: </span><span class="font-3">${val}</span><span>${dt}</span></div>`
|
||||
return `<div class="chartmarker"><span class="font-3">Average Response Time: ${this.humanTime(val)}</span><span>${dt}</span></div>`
|
||||
},
|
||||
fixed: {
|
||||
enabled: true,
|
||||
position: 'topRight',
|
||||
position: 'bottomLeft',
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
offsetY: -30,
|
||||
},
|
||||
x: {
|
||||
show: false,
|
||||
show: true,
|
||||
},
|
||||
y: {
|
||||
formatter: (value) => { return value + " %" },
|
||||
|
@ -75,15 +76,16 @@
|
|||
text: this.title,
|
||||
offsetX: 0,
|
||||
style: {
|
||||
fontSize: '28px',
|
||||
fontSize: '18px',
|
||||
cssClass: 'apexcharts-yaxis-title'
|
||||
}
|
||||
},
|
||||
subtitle: {
|
||||
text: this.subtitle,
|
||||
offsetX: 0,
|
||||
offsetY: 20,
|
||||
style: {
|
||||
fontSize: '14px',
|
||||
fontSize: '9px',
|
||||
cssClass: 'apexcharts-yaxis-title'
|
||||
}
|
||||
}
|
||||
|
@ -92,7 +94,3 @@
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -1,10 +1,26 @@
|
|||
<template>
|
||||
<table class="table">
|
||||
<div>
|
||||
<div v-if="servicesList.length === 0">
|
||||
<div class="alert alert-dark d-block mt-3 mb-0">
|
||||
You currently don't have any services!
|
||||
</div>
|
||||
</div>
|
||||
<table v-else class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">Visibility</th>
|
||||
<th scope="col">{{$t('name')}}</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">{{$t('status')}}</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">{{$t('visibility')}}</th>
|
||||
<th scope="col" class="d-none d-md-table-cell">{{ $t('group') }}</th>
|
||||
<th scope="col" class="d-none d-md-table-cell" style="width: 130px">
|
||||
{{$t('failures')}}
|
||||
<div class="btn-group float-right" role="group">
|
||||
<a @click="list_timeframe='3h'" type="button" class="small" :class="{'text-success': list_timeframe==='3h', 'text-muted': list_timeframe!=='3h'}">3h</a>
|
||||
<a @click="list_timeframe='12h'" type="button" class="small" :class="{'text-success': list_timeframe==='12h', 'text-muted': list_timeframe!=='12h'}">12h</a>
|
||||
<a @click="list_timeframe='24h'" type="button" class="small" :class="{'text-success': list_timeframe==='24h', 'text-muted': list_timeframe!=='24h'}">24h</a>
|
||||
<a @click="list_timeframe='7d'" type="button" class="small" :class="{'text-success': list_timeframe==='7d', 'text-muted': list_timeframe!=='7d'}">7d</a>
|
||||
</div>
|
||||
</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -15,6 +31,11 @@
|
|||
<font-awesome-icon icon="bars" class="mr-3"/>
|
||||
</span> {{service.name}}
|
||||
</td>
|
||||
<td class="d-none d-md-table-cell">
|
||||
<span class="badge text-uppercase" :class="{'badge-success': service.online, 'badge-danger': !service.online}">
|
||||
{{service.online ? $t('online') : $t('offline')}}
|
||||
</span>
|
||||
</td>
|
||||
<td class="d-none d-md-table-cell">
|
||||
<span class="badge text-uppercase" :class="{'badge-primary': service.public, 'badge-secondary': !service.public}">
|
||||
{{service.public ? $t('public') : $t('private')}}
|
||||
|
@ -25,15 +46,18 @@
|
|||
<span class="badge badge-secondary">{{serviceGroup(service)}}</span>
|
||||
</div>
|
||||
</td>
|
||||
<td class="d-none d-md-table-cell">
|
||||
<ServiceSparkList :service="service" :timeframe="list_timeframe"/>
|
||||
</td>
|
||||
<td class="text-right">
|
||||
<div class="btn-group">
|
||||
<button v-if="$store.state.admin" @click.prevent="goto({path: `/dashboard/edit_service/${service.id}`, params: {service: service} })" class="btn btn-sm btn-outline-secondary">
|
||||
<button :disabled="loading" v-if="$store.state.admin" @click.prevent="goto({path: `/dashboard/edit_service/${service.id}`, params: {service: service} })" class="btn btn-sm btn-outline-secondary">
|
||||
<font-awesome-icon icon="edit" />
|
||||
</button>
|
||||
<button @click.prevent="goto({path: serviceLink(service), params: {service: service} })" class="btn btn-sm btn-outline-secondary">
|
||||
<button :disabled="loading" @click.prevent="goto({path: serviceLink(service), params: {service: service} })" class="btn btn-sm btn-outline-secondary">
|
||||
<font-awesome-icon icon="chart-area" />
|
||||
</button>
|
||||
<button v-if="$store.state.admin" @click.prevent="deleteService(service)" href="#" class="btn btn-sm btn-danger">
|
||||
<button :disabled="loading" v-if="$store.state.admin" @click.prevent="deleteService(service)" class="btn btn-sm btn-danger">
|
||||
<font-awesome-icon v-if="!loading" icon="times" />
|
||||
<font-awesome-icon v-if="loading" icon="circle-notch" spin/>
|
||||
</button>
|
||||
|
@ -42,22 +66,77 @@
|
|||
</tr>
|
||||
</draggable>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../../API";
|
||||
import draggable from 'vuedraggable'
|
||||
import ToggleSwitch from '../../forms/ToggleSwitch';
|
||||
import ServiceSparkList from "@/components/Service/ServiceSparkList";
|
||||
import Modal from "@/components/Elements/Modal";
|
||||
const draggable = () => import(/* webpackChunkName: "dashboard" */ 'vuedraggable')
|
||||
const ToggleSwitch = () => import(/* webpackChunkName: "dashboard" */ '../../forms/ToggleSwitch');
|
||||
|
||||
export default {
|
||||
name: 'ServicesList',
|
||||
components: {
|
||||
Modal,
|
||||
ServiceSparkList,
|
||||
ToggleSwitch,
|
||||
draggable
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
list_timeframe: "12h",
|
||||
chartOpts: {
|
||||
chart: {
|
||||
type: 'bar',
|
||||
height: 50,
|
||||
sparkline: {
|
||||
enabled: true
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
type: 'numeric',
|
||||
},
|
||||
showPoint: false,
|
||||
fullWidth:true,
|
||||
chartPadding: {top: 0,right: 0,bottom: 0,left: 0},
|
||||
stroke: {
|
||||
curve: 'straight'
|
||||
},
|
||||
fill: {
|
||||
opacity: 0.8,
|
||||
},
|
||||
yaxis: {
|
||||
min: 0
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
colors: {
|
||||
ranges: [{
|
||||
from: 0,
|
||||
to: 1,
|
||||
color: '#39c10a'
|
||||
}, {
|
||||
from: 2,
|
||||
to: 90,
|
||||
color: '#e01a1a'
|
||||
}]
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
theme: false,
|
||||
enabled: false,
|
||||
},
|
||||
title: {
|
||||
enabled: false,
|
||||
},
|
||||
subtitle: {
|
||||
enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -82,14 +161,25 @@ export default {
|
|||
await Api.services_reorder(data)
|
||||
await this.update()
|
||||
},
|
||||
async deleteService(s) {
|
||||
let c = confirm(`Are you sure you want to delete '${s.name}'?`)
|
||||
if (c) {
|
||||
tester(s) {
|
||||
console.log(s)
|
||||
},
|
||||
async delete(s) {
|
||||
this.loading = true
|
||||
await Api.service_delete(s.id)
|
||||
await this.update()
|
||||
this.loading = false
|
||||
},
|
||||
async deleteService(s) {
|
||||
const modal = {
|
||||
visible: true,
|
||||
title: "Delete Service",
|
||||
body: `Are you sure you want to delete service ${s.name}? This will also delete all failures, checkins, and incidents for this service.`,
|
||||
btnColor: "btn-danger",
|
||||
btnText: "Delete Service",
|
||||
func: () => this.delete(s),
|
||||
}
|
||||
this.$store.commit("setModal", modal)
|
||||
},
|
||||
serviceGroup(s) {
|
||||
let group = this.$store.getters.groupById(s.group_id)
|
||||
|
|
|
@ -1,17 +1,21 @@
|
|||
<template>
|
||||
<div class="card text-black-50 bg-white mb-5">
|
||||
<div class="card-header">Theme Editor</div>
|
||||
<div class="card mb-5">
|
||||
<div class="card-header">{{$t('theme_editor')}}</div>
|
||||
<div class="card-body">
|
||||
<div v-if="error" class="alert alert-danger mt-3" style="white-space: pre-line;">
|
||||
{{error}}
|
||||
</div>
|
||||
|
||||
<h6 v-if="directory" id="assets_dir" class="text-muted text-monospace text-sm-center font-1 mb-4">
|
||||
{{$t('assets_dir')}}: {{directory}}
|
||||
</h6>
|
||||
|
||||
<div v-if="loaded && !directory" class="jumbotron jumbotron-fluid">
|
||||
<div class="text-center col-12">
|
||||
<h1 class="display-5">Enable Local Assets</h1>
|
||||
<span class="lead">Customize your status page design by enabling local assets. This will create a 'assets' directory containing all CSS.<p>
|
||||
<h1 class="display-5">{{$t('enable_assets')}}</h1>
|
||||
<span class="lead">{{$t('assets_desc')}}<p>
|
||||
<button id="enable_assets" @click.prevent="createAssets" :disabled="pending" href="#" class="btn btn-primary mt-3">
|
||||
<font-awesome-icon v-if="pending" icon="circle-notch" class="mr-2" spin/>{{pending ? "Creating Assets" : "Enable Local Assets"}}
|
||||
<font-awesome-icon v-if="pending" icon="circle-notch" class="mr-2" spin/>{{pending ? $t('assets_loading') : $t('assets_btn')}}
|
||||
</button>
|
||||
</p></span>
|
||||
</div>
|
||||
|
@ -21,33 +25,48 @@
|
|||
<h3>Variables</h3>
|
||||
<codemirror v-show="loaded" v-model="vars" ref="vars" :options="cmOptions" class="codemirrorInput"/>
|
||||
|
||||
<h3 class="mt-3">Base Theme</h3>
|
||||
<h3 class="mt-3">Base {{$t('theme')}}</h3>
|
||||
<codemirror v-show="loaded" v-model="base" ref="base" :options="cmOptions" class="codemirrorInput"/>
|
||||
|
||||
<h3 class="mt-3">Layout {{$t('theme')}}</h3>
|
||||
<codemirror v-show="loaded" v-model="layout" ref="layout" :options="cmOptions" class="codemirrorInput"/>
|
||||
|
||||
<h3 class="mt-3">Forms {{$t('theme')}}</h3>
|
||||
<codemirror v-show="loaded" v-model="forms" ref="forms" :options="cmOptions" class="codemirrorInput"/>
|
||||
|
||||
<h3 class="mt-3">Mixins</h3>
|
||||
<codemirror v-show="loaded" v-model="mixins" ref="mixins" :options="cmOptions" class="codemirrorInput"/>
|
||||
|
||||
<h3 class="mt-3">Mobile Overwrites</h3>
|
||||
<codemirror v-show="loaded" v-model="mobile" ref="mobile" :options="cmOptions" class="codemirrorInput"/>
|
||||
|
||||
<button id="save_assets" @submit.prevent="saveAssets" type="submit" class="btn btn-primary btn-block mt-2" :disabled="pending">{{pending ? "Saving..." : "Save Style"}}</button>
|
||||
<button id="delete_assets" v-if="directory" @click.prevent="deleteAssets" href="#" class="btn btn-danger btn-block confirm-btn" :disabled="pending">Delete Local Assets</button>
|
||||
|
||||
<h6 id="assets_dir" class="text-muted text-monospace text-sm-center font-1 mt-3">
|
||||
Asset Directory: {{directory}}
|
||||
</h6>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div v-if="directory" class="card-footer">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<button id="save_assets" @click.prevent="saveAssets" type="submit" class="btn btn-primary btn-block" :disabled="pending">{{pending ? "Saving..." : "Save Styles"}}</button>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<button id="delete_assets" @click.prevent="deleteAssets" class="btn btn-danger btn-block confirm-btn" :disabled="pending">Delete Local Assets</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../../API";
|
||||
import Api from "../../API";
|
||||
|
||||
// require component
|
||||
import { codemirror } from 'vue-codemirror'
|
||||
import 'codemirror/mode/css/css.js'
|
||||
// require component
|
||||
import {codemirror} from 'vue-codemirror'
|
||||
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
import 'codemirror-colorpicker/dist/codemirror-colorpicker.css'
|
||||
import 'codemirror-colorpicker'
|
||||
import('codemirror/mode/css/css.js')
|
||||
|
||||
import('codemirror/lib/codemirror.css')
|
||||
import('codemirror-colorpicker/dist/codemirror-colorpicker.css')
|
||||
import('codemirror-colorpicker')
|
||||
|
||||
export default {
|
||||
name: 'ThemeEditor',
|
||||
|
@ -62,6 +81,9 @@
|
|||
data () {
|
||||
return {
|
||||
base: null,
|
||||
layout: null,
|
||||
forms: null,
|
||||
mixins: null,
|
||||
vars: null,
|
||||
mobile: null,
|
||||
error: null,
|
||||
|
@ -90,6 +112,9 @@
|
|||
this.$refs.vars.codemirror.refresh()
|
||||
this.$refs.base.codemirror.refresh()
|
||||
this.$refs.mobile.codemirror.refresh()
|
||||
this.$refs.layout.codemirror.refresh()
|
||||
this.$refs.forms.codemirror.refresh()
|
||||
this.$refs.mixins.codemirror.refresh()
|
||||
}
|
||||
},
|
||||
async fetchTheme() {
|
||||
|
@ -101,6 +126,9 @@
|
|||
this.base = theme.base
|
||||
this.vars = theme.variables
|
||||
this.mobile = theme.mobile
|
||||
this.layout = theme.layout
|
||||
this.forms = theme.forms
|
||||
this.mixins = theme.mixins
|
||||
}
|
||||
this.pending = false
|
||||
this.loaded = true
|
||||
|
@ -116,18 +144,33 @@
|
|||
this.pending = false
|
||||
await this.fetchTheme()
|
||||
},
|
||||
async deleteAssets() {
|
||||
async delete() {
|
||||
this.pending = true
|
||||
let c = confirm('Are you sure you want to delete all local assets?')
|
||||
if (c) {
|
||||
const resp = await Api.theme_generate(false)
|
||||
await this.fetchTheme()
|
||||
}
|
||||
this.pending = false
|
||||
},
|
||||
async deleteAssets() {
|
||||
const modal = {
|
||||
visible: true,
|
||||
title: "Delete Local Assets",
|
||||
body: `Are you sure you want to delete all local assets?`,
|
||||
btnColor: "btn-danger",
|
||||
btnText: "Delete",
|
||||
func: () => this.delete(),
|
||||
}
|
||||
this.$store.commit("setModal", modal)
|
||||
},
|
||||
async saveAssets() {
|
||||
this.pending = true
|
||||
const data = {base: this.base, variables: this.vars, mobile: this.mobile}
|
||||
const data = {
|
||||
base: this.base,
|
||||
layout: this.layout,
|
||||
forms: this.forms,
|
||||
mixins: this.mixins,
|
||||
variables: this.vars,
|
||||
mobile: this.mobile
|
||||
}
|
||||
let resp
|
||||
try {
|
||||
resp = await Api.theme_save(data)
|
||||
|
@ -157,9 +200,3 @@
|
|||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.CodeMirror {
|
||||
border: 1px solid #eee;
|
||||
height: 550px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<router-link to="/" class="navbar-brand">Statping</router-link>
|
||||
<button @click="navopen = !navopen" class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<font-awesome-icon v-if="!navopen" icon="bars"/>
|
||||
|
@ -9,26 +9,29 @@
|
|||
<div class="navbar-collapse" :class="{collapse: !navopen}" id="navbarText">
|
||||
<ul class="navbar-nav mr-auto">
|
||||
<li @click="navopen = !navopen" class="nav-item navbar-item">
|
||||
<router-link to="/dashboard" class="nav-link">{{ $t('top_nav.dashboard') }}</router-link>
|
||||
<router-link to="/dashboard" class="nav-link">{{ $t('dashboard') }}</router-link>
|
||||
</li>
|
||||
<li @click="navopen = !navopen" class="nav-item navbar-item">
|
||||
<router-link to="/dashboard/services" class="nav-link">{{ $t('top_nav.services') }}</router-link>
|
||||
<router-link to="/dashboard/services" class="nav-link">{{ $t('services') }}</router-link>
|
||||
</li>
|
||||
<li v-if="$store.state.admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
||||
<router-link to="/dashboard/users" class="nav-link">{{ $t('top_nav.users') }}</router-link>
|
||||
<li v-if="admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
||||
<router-link to="/dashboard/users" class="nav-link">{{ $t('users') }}</router-link>
|
||||
</li>
|
||||
<li @click="navopen = !navopen" class="nav-item navbar-item">
|
||||
<router-link to="/dashboard/messages" class="nav-link">{{ $t('top_nav.announcements') }}</router-link>
|
||||
<router-link to="/dashboard/messages" class="nav-link">{{ $t('announcements') }}</router-link>
|
||||
</li>
|
||||
<li v-if="$store.state.admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
||||
<router-link to="/dashboard/settings" class="nav-link">{{ $t('top_nav.settings') }}</router-link>
|
||||
<li v-if="admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
||||
<router-link to="/dashboard/settings" class="nav-link">{{ $t('settings') }}</router-link>
|
||||
</li>
|
||||
<li v-if="$store.state.admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
||||
<router-link to="/dashboard/logs" class="nav-link">{{ $t('top_nav.logs') }}</router-link>
|
||||
<li v-if="admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
||||
<router-link to="/dashboard/logs" class="nav-link">{{ $t('logs') }}</router-link>
|
||||
</li>
|
||||
<li v-if="admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
||||
<router-link to="/dashboard/help" class="nav-link">{{ $t('help') }}</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
<span class="navbar-text">
|
||||
<a href="#" class="nav-link" @click.prevent="logout">{{ $t('top_nav.logout') }}</a>
|
||||
<a href="#" class="nav-link" @click.prevent="logout">{{ $t('logout') }}</a>
|
||||
</span>
|
||||
</div>
|
||||
</nav>
|
||||
|
@ -37,7 +40,6 @@
|
|||
|
||||
<script>
|
||||
import Api from "../../API"
|
||||
import Vue from "vue";
|
||||
|
||||
export default {
|
||||
name: 'TopNav',
|
||||
|
@ -46,6 +48,11 @@
|
|||
navopen: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
admin() {
|
||||
return this.$store.state.admin
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async logout () {
|
||||
await Api.logout()
|
||||
|
@ -58,7 +65,3 @@
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
<template>
|
||||
<div class="col-12 mb-3 pb-2 border-bottom" role="alert">
|
||||
<span class="font-weight-bold text-capitalize" :class="{'text-success': update.type.toLowerCase()==='resolved', 'text-danger': update.type.toLowerCase()==='investigating', 'text-warning': update.type.toLowerCase()==='update'}">{{update.type}}</span>
|
||||
<span class="text-muted">- {{update.message}}
|
||||
<button v-if="admin" @click="delete_update(update)" type="button" class="close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</span>
|
||||
<span class="d-block small">{{ago(update.created_at)}} ago</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "@/API";
|
||||
|
||||
export default {
|
||||
name: "IncidentUpdate",
|
||||
props: {
|
||||
update: {
|
||||
required: true
|
||||
},
|
||||
admin: {
|
||||
required: true
|
||||
},
|
||||
onUpdate: {
|
||||
required: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async delete_update(update) {
|
||||
this.res = await Api.incident_update_delete(update)
|
||||
if (this.res.status === "success") {
|
||||
this.onUpdate()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,23 @@
|
|||
<template>
|
||||
<div v-if="loading" class="row mt-3 mb-3">
|
||||
<div class="col-12 text-center text-muted">
|
||||
<font-awesome-icon icon="circle-notch" size="3x" spin/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Loading",
|
||||
props: {
|
||||
loading: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,50 @@
|
|||
<template>
|
||||
<div v-if="modal.visible" class="modal d-block mt-5 pt-5" tabindex="-1">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">{{modal.title}}</h5>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>{{modal.body}}</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button @click.prevent="close" type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||
<button @click.prevent="runFunc" type="button" :class="`btn ${modal.btnColor}`">{{modal.btnText}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "Modal",
|
||||
data () {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
modal() {
|
||||
return this.$store.getters.modal
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
runFunc() {
|
||||
this.$store.getters.modal.func()
|
||||
this.close()
|
||||
},
|
||||
close() {
|
||||
this.$store.commit("setModal", {visible: false})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
|
@ -0,0 +1,46 @@
|
|||
|
||||
|
||||
const serviceSparkLine = {
|
||||
chart: {
|
||||
type: 'bar',
|
||||
height: 50,
|
||||
sparkline: {
|
||||
enabled: true
|
||||
},
|
||||
},
|
||||
stroke: {
|
||||
curve: 'straight'
|
||||
},
|
||||
fill: {
|
||||
opacity: 0.3,
|
||||
},
|
||||
yaxis: {
|
||||
min: 0
|
||||
},
|
||||
colors: ['#b3bdc3'],
|
||||
tooltip: {
|
||||
theme: false,
|
||||
enabled: false,
|
||||
},
|
||||
title: {
|
||||
text: this.title,
|
||||
offsetX: 0,
|
||||
style: {
|
||||
fontSize: '28px',
|
||||
cssClass: 'apexcharts-yaxis-title'
|
||||
}
|
||||
},
|
||||
subtitle: {
|
||||
text: this.subtitle,
|
||||
offsetX: 0,
|
||||
style: {
|
||||
fontSize: '14px',
|
||||
cssClass: 'apexcharts-yaxis-title'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export default {
|
||||
ServiceList: serviceSparkLine
|
||||
}
|
|
@ -1,10 +1,15 @@
|
|||
<template>
|
||||
<footer>
|
||||
<div v-if="!core.footer" class="footer text-center mb-4 p-2">
|
||||
<a href="https://github.com/statping/statping" target="_blank">
|
||||
Statping {{core.version}} made with <font-awesome-icon icon="heart" class="text-danger"/>
|
||||
</a> |
|
||||
<router-link :to="$store.state.admin ? '/dashboard' : '/login'">{{$t('top_nav.dashboard')}}</router-link>
|
||||
<div class="d-block text-dim">
|
||||
<div class="mb-3">
|
||||
<router-link class="links" :to="admin ? '/dashboard' : '/login'">{{$t('dashboard')}}</router-link>
|
||||
</div>
|
||||
<span class="font-1 mt-3">
|
||||
<a href="https://github.com/statping/statping" class="statping" target="_blank">
|
||||
Statping v{{core.version}} made with <font-awesome-icon icon="heart" class="hlight font-1"/></a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-else class="footer text-center mb-4 p-2" v-html="core.footer"></div>
|
||||
</footer>
|
||||
|
@ -21,11 +26,19 @@
|
|||
computed: {
|
||||
core() {
|
||||
return this.$store.getters.core
|
||||
}
|
||||
},
|
||||
commit() {
|
||||
return this.$store.getters.core.commit.slice(0,8)
|
||||
},
|
||||
admin() {
|
||||
return this.$store.getters.admin
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
.hlight {
|
||||
color: #f6cbcb;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<template>
|
||||
<div class="col-12 full-col-12">
|
||||
<div v-if="services.length > 0" class="col-12 full-col-12">
|
||||
<h4 v-if="group.name !== 'Empty Group'" class="group_header mb-3 mt-4">{{group.name}}</h4>
|
||||
<div class="list-group online_list mb-4">
|
||||
|
||||
<a v-for="(service, index) in $store.getters.servicesInGroup(group.id)" v-bind:key="index" class="service_li list-group-item list-group-item-action">
|
||||
<div v-for="(service, index) in services" v-bind:key="index" class="list-group-item list-group-item-action">
|
||||
<router-link class="no-decoration font-3" :to="serviceLink(service)">{{service.name}}</router-link>
|
||||
<span class="badge text-uppercase float-right" :class="{'bg-success': service.online, 'bg-danger': !service.online }">
|
||||
{{service.online ? $t('online') : $t('offline')}}
|
||||
|
@ -12,16 +12,16 @@
|
|||
<GroupServiceFailures :service="service"/>
|
||||
|
||||
<IncidentsBlock :service="service"/>
|
||||
</a>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from '../../API';
|
||||
import GroupServiceFailures from './GroupServiceFailures';
|
||||
import IncidentsBlock from './IncidentsBlock';
|
||||
const GroupServiceFailures = () => import(/* webpackChunkName: "index" */ './GroupServiceFailures');
|
||||
const IncidentsBlock = () => import(/* webpackChunkName: "index" */ './IncidentsBlock');
|
||||
|
||||
export default {
|
||||
name: 'Group',
|
||||
|
@ -30,11 +30,15 @@ export default {
|
|||
GroupServiceFailures
|
||||
},
|
||||
props: {
|
||||
group: Object
|
||||
group: {
|
||||
type: Object,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
services() {
|
||||
return this.$store.getters.servicesInGroup(this.group.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
||||
|
|
|
@ -1,17 +1,31 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="d-flex mt-3 mb-2">
|
||||
<div class="flex-fill service_day" v-for="(d, index) in failureData" :class="{'mini_error': d.amount > 0, 'mini_success': d.amount === 0}">
|
||||
<span v-if="d.amount != 0" class="small">{{d.amount}}</span>
|
||||
<div v-observe-visibility="{callback: visibleChart, once: true}" v-if="!loaded" class="row">
|
||||
<div class="col-12 text-center mt-3">
|
||||
<font-awesome-icon icon="circle-notch" class="text-dim" size="2x" spin/>
|
||||
</div>
|
||||
</div>
|
||||
<transition name="fade">
|
||||
<div v-if="loaded">
|
||||
<div class="d-flex mt-3">
|
||||
<div class="flex-fill service_day" v-for="(d, index) in failureData" @mouseover="mouseover(d)" @mouseout="mouseout" :class="{'day-error': d.amount > 0, 'day-success': d.amount === 0}">
|
||||
<span v-if="d.amount !== 0" class="d-none d-md-block text-center small"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-2">
|
||||
<div class="col-3 text-left font-2 text-muted">30 Days Ago</div>
|
||||
<div class="col-6 text-center font-2" :class="{'text-muted': service.online, 'text-danger': !service.online}">
|
||||
{{service_txt}}
|
||||
<div class="col-12 no-select">
|
||||
<p class="divided">
|
||||
<span class="font-2 text-muted">90 {{$t('days_ago')}}</span>
|
||||
<span class="divider"></span>
|
||||
<span class="text-center font-2" :class="{'text-muted': service.online, 'text-danger': !service.online}">{{service_txt}}</span>
|
||||
<span class="divider"></span>
|
||||
<span class="font-2 text-muted">{{$t('today')}}</span>
|
||||
</p>
|
||||
</div>
|
||||
<div class="col-3 text-right font-2 text-muted">Today</div>
|
||||
</div>
|
||||
<div class="daily-failures small text-right text-dim">{{hover_text}}</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -26,6 +40,9 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
failureData: [],
|
||||
hover_text: "",
|
||||
loaded: false,
|
||||
visible: false,
|
||||
}
|
||||
},
|
||||
props: {
|
||||
|
@ -40,21 +57,34 @@ export default {
|
|||
}
|
||||
},
|
||||
mounted () {
|
||||
this.lastDaysFailures()
|
||||
|
||||
},
|
||||
methods: {
|
||||
visibleChart(isVisible, entry) {
|
||||
if (isVisible && !this.visible) {
|
||||
this.visible = true
|
||||
this.lastDaysFailures().then(() => this.loaded = true)
|
||||
}
|
||||
},
|
||||
mouseout() {
|
||||
this.hover_text = ""
|
||||
},
|
||||
mouseover(e) {
|
||||
let txt = `${e.amount} Failures`
|
||||
if (e.amount === 0) {
|
||||
txt = `No Issues`
|
||||
}
|
||||
this.hover_text = `${e.date.toLocaleDateString()} - ${txt}`
|
||||
},
|
||||
async lastDaysFailures() {
|
||||
const start = this.nowSubtract(86400 * 30)
|
||||
const data = await Api.service_failures_data(this.service.id, this.toUnix(start), this.toUnix(this.startToday()), "24h")
|
||||
const start = this.beginningOf('day', this.nowSubtract(86400 * 90))
|
||||
const end = this.endOf('tomorrow')
|
||||
const data = await Api.service_failures_data(this.service.id, this.toUnix(start), this.toUnix(end), "24h", true)
|
||||
data.forEach((d) => {
|
||||
let date = this.parseISO(d.timeframe)
|
||||
this.failureData.push({month: 1, day: date.getDate(), amount: d.amount})
|
||||
this.failureData.push({month: date.getMonth(), day: date.getDate(), date: date, amount: d.amount})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
||||
|
|
|
@ -15,7 +15,3 @@ export default {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
<template>
|
||||
<div class="row">
|
||||
<div v-for="(incident, i) in incidents" class="col-12 mt-4 mb-3">
|
||||
<div v-for="(incident, i) in incidents" class="col-12 mt-2">
|
||||
<span class="braker mt-1 mb-3"></span>
|
||||
<h6>Incident: {{incident.title}}
|
||||
<h6>{{incident.title}}
|
||||
<span class="font-2 float-right">{{niceDate(incident.created_at)}}</span>
|
||||
</h6>
|
||||
<span class="font-2" v-html="incident.description"></span>
|
||||
|
||||
<UpdatesBlock :incident="incident"/>
|
||||
|
||||
<div class="font-2 mb-3" v-html="incident.description"></div>
|
||||
<IncidentUpdate v-for="(update, i) in incident.updates" v-bind:key="i" :update="update" :admin="false"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from '../../API';
|
||||
import UpdatesBlock from "@/components/Index/UpdatesBlock";
|
||||
import IncidentUpdate from "@/components/Elements/IncidentUpdate";
|
||||
|
||||
export default {
|
||||
name: 'IncidentsBlock',
|
||||
components: {UpdatesBlock},
|
||||
components: {
|
||||
IncidentUpdate
|
||||
},
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
|
@ -49,8 +49,7 @@ export default {
|
|||
this.incidents = await Api.incidents_service(this.service.id)
|
||||
},
|
||||
async incident_updates(incident) {
|
||||
await Api.incident_updates(incident).then((d) => {return d})
|
||||
return o
|
||||
return await Api.incident_updates(incident)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
<template>
|
||||
<div class="alert alert-warning pb-4 pt-3 mt-5 mb-5" role="alert">
|
||||
<h3 class="mb-3">{{message.title}}</h3>
|
||||
<span class="mb-3">{{message.description}}</span>
|
||||
<div class="row d-block mt-3">
|
||||
<span class="col-12 col-md-6 text-left small">
|
||||
Started {{niceDate(message.start_on)}} ({{ago(message.start_on)}} ago)
|
||||
</span>
|
||||
<span class="col-12 col-md-6 text-right float-right small">
|
||||
Ends on {{niceDate(message.end_on)}} (in {{ago(message.end_on)}})</span>
|
||||
<div class="card shadow mb-4" role="alert">
|
||||
<div class="card-body pb-2">
|
||||
<h3 class="mb-3 font-weight-bold">{{message.title}}</h3>
|
||||
<span class="mb-2">{{message.description}}</span>
|
||||
<div class="col-12 mb-0">
|
||||
<div class="dates">
|
||||
<div class="start">
|
||||
<strong>STARTS</strong> {{niceDate(message.start_on)}}
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="ends">
|
||||
<strong>ENDS</strong> {{niceDate(message.end_on)}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -17,7 +24,8 @@ export default {
|
|||
name: 'MessageBlock',
|
||||
props: {
|
||||
message: {
|
||||
type: Object
|
||||
type: Object,
|
||||
required: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,4 +33,5 @@ export default {
|
|||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
|
|
|
@ -1,52 +0,0 @@
|
|||
<template>
|
||||
<div class="row">
|
||||
<div v-for="(update, i) in updates" v-bind:key="i" class="col-12 mt-3">
|
||||
<div class="col-md-2 col-12">
|
||||
<span class="badge text-uppercase" :class="badgeClass(update.type)">{{update.type}}</span>
|
||||
</div>
|
||||
<div class="col-md-12 col-12 mt-2 font-3">{{update.message}}</div>
|
||||
<div class="col-12 font-1 float-right text-black-50 mt-2">{{ago(update.created_at)}} ago</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from '../../API';
|
||||
|
||||
export default {
|
||||
name: 'UpdatesBlock',
|
||||
props: {
|
||||
incident: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
updates: null,
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.getIncidentUpdates()
|
||||
},
|
||||
methods: {
|
||||
badgeClass(val) {
|
||||
switch (val.toLowerCase()) {
|
||||
case "resolved":
|
||||
return "badge-success"
|
||||
case "update":
|
||||
return "badge-info"
|
||||
case "investigating":
|
||||
return "badge-danger"
|
||||
}
|
||||
},
|
||||
async getIncidentUpdates() {
|
||||
this.updates = await Api.incident_updates(this.incident)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -1,8 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="service-chart-container">
|
||||
<apexchart width="100%" height="420" type="area" :options="main_chart_options" :series="main_chart"></apexchart>
|
||||
</div>
|
||||
<apexchart width="100%" height="350" type="area" :options="main_chart_options" :series="main_chart"></apexchart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -38,6 +36,7 @@
|
|||
return {
|
||||
loading: true,
|
||||
main_data: null,
|
||||
ping_data: null,
|
||||
expanded_data: null,
|
||||
main_chart_options: {
|
||||
noData: {
|
||||
|
@ -53,6 +52,7 @@
|
|||
},
|
||||
chart: {
|
||||
id: 'mainchart',
|
||||
stacked: true,
|
||||
events: {
|
||||
dataPointSelection: (event, chartContext, config) => {
|
||||
window.console.log('slect')
|
||||
|
@ -112,7 +112,9 @@
|
|||
},
|
||||
yaxis: {
|
||||
labels: {
|
||||
show: true
|
||||
formatter: (value) => {
|
||||
return this.humanTime(value)
|
||||
}
|
||||
},
|
||||
},
|
||||
markers: {
|
||||
|
@ -167,15 +169,15 @@
|
|||
show: false
|
||||
},
|
||||
fill: {
|
||||
colors: ["#48d338"],
|
||||
colors: ["#f1771f", "#48d338"],
|
||||
opacity: 1,
|
||||
type: 'solid'
|
||||
},
|
||||
stroke: {
|
||||
show: true,
|
||||
curve: 'smooth',
|
||||
curve: 'stepline',
|
||||
lineCap: 'butt',
|
||||
colors: ["#3aa82d"],
|
||||
colors: ["#f1771f", "#48d338"],
|
||||
width: 2,
|
||||
}
|
||||
},
|
||||
|
@ -229,8 +231,11 @@
|
|||
computed: {
|
||||
main_chart () {
|
||||
return [{
|
||||
name: this.service.name,
|
||||
name: "latency",
|
||||
...this.convertToChartData(this.main_data)
|
||||
},{
|
||||
name: "ping",
|
||||
...this.convertToChartData(this.ping_data)
|
||||
}]
|
||||
},
|
||||
expanded_chart () {
|
||||
|
@ -263,9 +268,13 @@
|
|||
},
|
||||
async chartHits() {
|
||||
this.main_data = await this.load_hits()
|
||||
this.ping_data = await this.load_ping()
|
||||
},
|
||||
async load_hits(start=this.params.start, end=this.params.end, group=this.group) {
|
||||
return await Api.service_hits(this.service.id, start, end, group, false)
|
||||
},
|
||||
async load_ping(start=this.params.start, end=this.params.end, group=this.group) {
|
||||
return await Api.service_ping(this.service.id, start, end, group, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
|
||||
<script>
|
||||
import MiniSparkLine from './MiniSparkLine';
|
||||
import ServiceSparkLine from './ServiceSparkLine';
|
||||
import ServiceSparkLine from '../Dashboard/ServiceSparkLine';
|
||||
|
||||
export default {
|
||||
name: 'Analytics',
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
<template>
|
||||
<div class="col-12">
|
||||
<div class="text-center" style="width:210px" v-if="!loaded">
|
||||
<font-awesome-icon icon="circle-notch" class="h-25 text-dim" spin/>
|
||||
</div>
|
||||
<apexchart v-else width="100%" height="50" type="bar" :options="chartOpts" :series="data"></apexchart>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "@/API";
|
||||
const timeoptions = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' };
|
||||
|
||||
|
||||
export default {
|
||||
name: "FailuresBarChart",
|
||||
props: {
|
||||
service: {
|
||||
required: true,
|
||||
type: Object,
|
||||
},
|
||||
group: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
start: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
end: {
|
||||
required: true,
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: null,
|
||||
loaded: false,
|
||||
chartOpts: {
|
||||
chart: {
|
||||
type: 'bar',
|
||||
height: 150,
|
||||
sparkline: {
|
||||
enabled: true
|
||||
},
|
||||
animations: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
},
|
||||
showPoint: false,
|
||||
fullWidth:true,
|
||||
chartPadding: {top: 0,right: 0,bottom: 0,left: 80},
|
||||
stroke: {
|
||||
curve: 'straight'
|
||||
},
|
||||
fill: {
|
||||
opacity: 0.4,
|
||||
},
|
||||
yaxis: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
colors: {
|
||||
ranges: [{
|
||||
from: 0,
|
||||
to: 1,
|
||||
color: '#cfcfcf'
|
||||
}, {
|
||||
from: 2,
|
||||
to: 3,
|
||||
color: '#f58e49'
|
||||
}, {
|
||||
from: 3,
|
||||
to: 20,
|
||||
color: '#e01a1a'
|
||||
}, {
|
||||
from: 21,
|
||||
to: Infinity,
|
||||
color: '#9b0909'
|
||||
}]
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
theme: false,
|
||||
enabled: true,
|
||||
custom: ({series, seriesIndex, dataPointIndex, w}) => {
|
||||
let val = series[seriesIndex][dataPointIndex];
|
||||
let ts = w.globals.seriesX[seriesIndex][dataPointIndex];
|
||||
const dt = new Date(ts).toLocaleDateString("en-us", timeoptions)
|
||||
let ago = `${(dataPointIndex-12) * -1} hours ago`
|
||||
if ((dataPointIndex-12) * -1 === 0) {
|
||||
ago = `Current hour`
|
||||
}
|
||||
return `<div class="chart_list_tooltip font-2 mb-4">${val-1} Failures<br>${dt}</div>`
|
||||
},
|
||||
fixed: {
|
||||
enabled: true,
|
||||
position: 'topLeft',
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
},
|
||||
x: {
|
||||
formatter: (value) => { return value },
|
||||
},
|
||||
y: {
|
||||
show: false
|
||||
},
|
||||
},
|
||||
title: {
|
||||
enabled: false,
|
||||
},
|
||||
subtitle: {
|
||||
enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.loadFailures()
|
||||
},
|
||||
watch: {
|
||||
group(o, n) {
|
||||
this.loaded = false
|
||||
this.loadFailures()
|
||||
this.loaded = true
|
||||
},
|
||||
start(o, n) {
|
||||
this.loaded = false
|
||||
this.loadFailures()
|
||||
this.loaded = true
|
||||
},
|
||||
end(o, n) {
|
||||
this.loaded = false
|
||||
this.loadFailures()
|
||||
this.loaded = true
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
convertChartData(data) {
|
||||
if (!data) {
|
||||
return []
|
||||
}
|
||||
let arr = []
|
||||
data.forEach((d, k) => {
|
||||
arr.push({
|
||||
x: d.timeframe,
|
||||
y: d.amount+1,
|
||||
})
|
||||
})
|
||||
return arr
|
||||
},
|
||||
async loadFailures() {
|
||||
this.loaded = false
|
||||
const startEnd = this.startEndParams(this.parseISO(this.start), this.parseISO(this.end), this.group)
|
||||
const data = await Api.service_failures_data(this.service.id, startEnd.start, startEnd.end, this.group, true)
|
||||
this.loaded = true
|
||||
this.data = [{data: this.convertChartData(data)}]
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -1,21 +0,0 @@
|
|||
<template>
|
||||
<div class="row stats_area mt-5 mb-4">
|
||||
okok
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Incidents',
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -10,24 +10,10 @@
|
|||
|
||||
<ServiceTopStats :service="service"/>
|
||||
|
||||
<div v-if="expanded" class="row">
|
||||
<Analytics title="Last Failure" :func="stats.total_failures"/>
|
||||
<Analytics title="Total Failures" :func="stats.total_failures"/>
|
||||
<Analytics title="Highest Latency" :func="stats.high_latency"/>
|
||||
<Analytics title="Lowest Latency" :func="stats.lowest_latency"/>
|
||||
<Analytics title="Total Uptime" :func="stats.high_ping"/>
|
||||
<Analytics title="Total Downtime" :func="stats.low_ping"/>
|
||||
|
||||
<div class="col-12">
|
||||
<router-link :to="serviceLink(service)" class="btn btn-block btn-outline-success mt-4" :class="{'btn-outline-success': service.online, 'btn-outline-danger': !service.online}">
|
||||
View More Details
|
||||
</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="!expanded" v-observe-visibility="visibleChart" class="chart-container">
|
||||
<div v-show="!expanded" v-observe-visibility="{callback: visibleChart, throttle: 200}" class="chart-container">
|
||||
<ServiceChart :service="service" :visible="visible" :chart_timeframe="chartTimeframe"/>
|
||||
</div>
|
||||
|
||||
|
@ -62,7 +48,7 @@
|
|||
|
||||
<div class="col-md-2 col-6 float-right">
|
||||
<button v-if="!expanded" @click="setService" class="btn btn-sm float-right dyn-dark text-white" :class="{'bg-success': service.online, 'bg-danger': !service.online}">
|
||||
{{$t('service.view')}}
|
||||
{{$t('view')}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -72,28 +58,20 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from '../../API';
|
||||
import Analytics from './Analytics';
|
||||
import ServiceChart from "./ServiceChart";
|
||||
import ServiceTopStats from "@/components/Service/ServiceTopStats";
|
||||
import Graphing from '../../graphing'
|
||||
const Analytics = () => import(/* webpackChunkName: "service" */ './Analytics');
|
||||
const ServiceChart = () => import(/* webpackChunkName: "service" */ "./ServiceChart");
|
||||
const ServiceTopStats = () => import(/* webpackChunkName: "service" */ "@/components/Service/ServiceTopStats");
|
||||
|
||||
export default {
|
||||
name: 'ServiceBlock',
|
||||
components: { Analytics, ServiceTopStats, ServiceChart},
|
||||
props: {
|
||||
in_service: {
|
||||
service: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
computed: {
|
||||
service() {
|
||||
return this.track_service
|
||||
},
|
||||
timeframepick() {
|
||||
return this.timeframes.find(s => s.value === this.timeframe_val)
|
||||
},
|
||||
|
@ -166,14 +144,13 @@ export default {
|
|||
value: 0,
|
||||
}
|
||||
},
|
||||
track_service: null,
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
// clearInterval(this.timer_func)
|
||||
},
|
||||
async created() {
|
||||
this.track_service = this.in_service
|
||||
created() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
disabled_interval(interval) {
|
||||
|
@ -204,29 +181,7 @@ export default {
|
|||
},
|
||||
async setService() {
|
||||
await this.$store.commit('setService', this.service)
|
||||
this.$router.push('/service/'+this.service.id, {props: {in_service: this.service}})
|
||||
},
|
||||
async showMoreStats() {
|
||||
this.expanded = !this.expanded;
|
||||
|
||||
const failData = await Graphing.failures(this.service, 7)
|
||||
this.stats.total_failures.chart = failData.data;
|
||||
this.stats.total_failures.value = failData.total;
|
||||
|
||||
const hitsData = await Graphing.hits(this.service, 7)
|
||||
|
||||
this.stats.high_latency.chart = hitsData.chart;
|
||||
this.stats.high_latency.value = this.humanTime(hitsData.high);
|
||||
|
||||
this.stats.lowest_latency.chart = hitsData.chart;
|
||||
this.stats.lowest_latency.value = this.humanTime(hitsData.low);
|
||||
|
||||
const pingData = await Graphing.pings(this.service, 7)
|
||||
this.stats.high_ping.chart = pingData.chart;
|
||||
this.stats.high_ping.value = this.humanTime(pingData.high);
|
||||
|
||||
this.stats.low_ping.chart = pingData.chart;
|
||||
this.stats.low_ping.value = this.humanTime(pingData.low);
|
||||
this.$router.push('/service/'+this.service.id, {props: {service: this.service}})
|
||||
},
|
||||
visibleChart(isVisible, entry) {
|
||||
if (isVisible && !this.visible) {
|
||||
|
@ -242,7 +197,3 @@ export default {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
||||
|
|
|
@ -47,8 +47,14 @@
|
|||
return {
|
||||
ready: false,
|
||||
showing: false,
|
||||
data: [],
|
||||
chartOptions: {
|
||||
data: null,
|
||||
ping_data: null,
|
||||
series: null,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
chartOptions() {
|
||||
return {
|
||||
noData: {
|
||||
text: 'Loading...'
|
||||
},
|
||||
|
@ -58,9 +64,20 @@
|
|||
type: "area",
|
||||
animations: {
|
||||
enabled: true,
|
||||
initialAnimation: {
|
||||
enabled: true
|
||||
}
|
||||
easing: 'easeinout',
|
||||
speed: 800,
|
||||
animateGradually: {
|
||||
enabled: false,
|
||||
delay: 400,
|
||||
},
|
||||
dynamicAnimation: {
|
||||
enabled: true,
|
||||
speed: 500
|
||||
},
|
||||
hover: {
|
||||
animationDuration: 0, // duration of animations when hovering an item
|
||||
},
|
||||
responsiveAnimationDuration: 0,
|
||||
},
|
||||
selection: {
|
||||
enabled: false
|
||||
|
@ -112,13 +129,13 @@
|
|||
custom: ({series, seriesIndex, dataPointIndex, w}) => {
|
||||
let ts = w.globals.seriesX[seriesIndex][dataPointIndex];
|
||||
const dt = new Date(ts).toLocaleDateString("en-us", timeoptions)
|
||||
let val = series[seriesIndex][dataPointIndex];
|
||||
if (val >= 10000) {
|
||||
val = Math.round(val / 1000) + " ms"
|
||||
} else {
|
||||
val = val + " μs"
|
||||
}
|
||||
return `<div class="chartmarker"><span>Average Response Time: </span><span class="font-3">${val}</span><span>${dt}</span></div>`
|
||||
let val = series[0][dataPointIndex];
|
||||
let pingVal = series[1][dataPointIndex];
|
||||
return `<div class="chartmarker">
|
||||
<span>Average Response Time: ${this.humanTime(val)}/${this.chart_timeframe.interval}</span>
|
||||
<span>Average Ping: ${this.humanTime(pingVal)}/${this.chart_timeframe.interval}</span>
|
||||
<span>${dt}</span>
|
||||
</div>`
|
||||
},
|
||||
fixed: {
|
||||
enabled: true,
|
||||
|
@ -130,7 +147,9 @@
|
|||
show: false,
|
||||
},
|
||||
y: {
|
||||
formatter: (value) => { return value + " %" },
|
||||
formatter: (value) => {
|
||||
return value + " %"
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
|
@ -147,20 +166,17 @@
|
|||
show: false
|
||||
},
|
||||
fill: {
|
||||
colors: [this.service.online ? "#48d338" : "#dd3545"],
|
||||
colors: this.service.online ? ["#3dc82f", "#48d338"] : ["#c60f20", "#dd3545"],
|
||||
opacity: 1,
|
||||
type: 'solid'
|
||||
type: 'solid',
|
||||
},
|
||||
stroke: {
|
||||
show: false,
|
||||
curve: 'smooth',
|
||||
lineCap: 'butt',
|
||||
colors: [this.service.online ? "#3aa82d" : "#dd3545"],
|
||||
colors: this.service.online ? ["#38bc2a", "#48d338"] : ["#c60f20", "#dd3545"],
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
data: []
|
||||
}]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
@ -178,16 +194,16 @@
|
|||
},
|
||||
methods: {
|
||||
async chartHits(val) {
|
||||
const start = val.start_time
|
||||
const end = this.toUnix(new Date())
|
||||
this.data = await Api.service_hits(this.service.id, start, end, val.interval, false)
|
||||
if (this.data === null && val.interval !== "5m") {
|
||||
await this.chartHits({start_time: val.start_time, interval: "5m"})
|
||||
}
|
||||
this.series = [{
|
||||
name: this.service.name,
|
||||
...this.convertToChartData(this.data)
|
||||
}]
|
||||
this.ready = false
|
||||
const end = this.endOf("hour", this.now())
|
||||
const start = this.beginningOf("hour", this.fromUnix(val.start_time))
|
||||
this.data = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(end), val.interval, false)
|
||||
this.ping_data = await Api.service_ping(this.service.id, this.toUnix(start), this.toUnix(end), val.interval, false)
|
||||
|
||||
this.series = [
|
||||
{name: "Latency", ...this.convertToChartData(this.data)},
|
||||
{name: "Ping", ...this.convertToChartData(this.ping_data)},
|
||||
]
|
||||
this.ready = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import ServiceChart from "./ServiceChart";
|
||||
const ServiceChart = () => import(/* webpackChunkName: "service" */ "./ServiceChart");
|
||||
import Api from "../../API";
|
||||
|
||||
export default {
|
||||
|
|
|
@ -1,211 +0,0 @@
|
|||
<template>
|
||||
<div class="card mb-4" :class="{'offline-card': !service.online}">
|
||||
<div class="card-title px-4 pt-3">
|
||||
<h4 v-observe-visibility="setVisible">
|
||||
<router-link :to="serviceLink(service)">{{service.name}}</router-link>
|
||||
<span class="badge float-right text-uppercase" :class="{'badge-success': service.online, 'badge-danger': !service.online}">
|
||||
{{service.online ? $t('online') : $t('offline')}}
|
||||
</span>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="card-body p-3 p-md-1 pt-md-1 pb-md-1">
|
||||
|
||||
<transition name="fade">
|
||||
<div v-if="loaded" class="col-12 pb-2">
|
||||
|
||||
<div v-if="false" class="row mb-4 align-content-center">
|
||||
|
||||
<div v-if="!service.online" class="col-3 text-left">
|
||||
<span class="text-danger font-5 font-weight-bold"></span>
|
||||
<span class="font-2 d-block">Current Downtime</span>
|
||||
</div>
|
||||
|
||||
<div v-if="service.online" class="col-3 text-left">
|
||||
<span class="text-success font-5 font-weight-bold">
|
||||
{{service.online_24_hours.toString()}} %
|
||||
</span>
|
||||
<span class="font-2 d-block">Total Uptime</span>
|
||||
</div>
|
||||
|
||||
<div v-if="service.online" class="col-3 text-left">
|
||||
<span class="text-success font-5 font-weight-bold">
|
||||
0
|
||||
</span>
|
||||
<span class="font-2 d-block">Downtime Today</span>
|
||||
</div>
|
||||
|
||||
<div v-if="service.online" class="col-3 text-left">
|
||||
<span class="text-success font-5 font-weight-bold">
|
||||
{{(uptime.uptime / 10000).toFixed(0).toString()}}
|
||||
</span>
|
||||
<span class="font-2 d-block">Uptime Duration</span>
|
||||
</div>
|
||||
|
||||
<div class="col-3 text-left">
|
||||
<span class="text-danger font-5 font-weight-bold">
|
||||
{{service.failures_24_hours}}
|
||||
</span>
|
||||
<span class="font-2 d-block">Failures last 24 hours</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-sm-12 mt-2 mt-md-0 mb-3">
|
||||
<ServiceSparkLine :title="set2_name" subtitle="Latency Last 24 Hours" :series="set2"/>
|
||||
</div>
|
||||
<div class="col-md-6 col-sm-12 mt-4 mt-md-0 mb-3">
|
||||
<ServiceSparkLine :title="set1_name" subtitle="Latency Last 7 Days" :series="set1"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-12 col-md-3 mb-2 mb-md-0">
|
||||
<router-link :to="{path: `/dashboard/service/${service.id}/incidents`, params: {id: service.id} }" class="btn btn-block btn-white text-capitalize incident">
|
||||
{{$tc('incident', 2)}}
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="col-12 col-md-3 mb-2 mb-md-0">
|
||||
<router-link :to="{path: `/dashboard/service/${service.id}/checkins`, params: {id: service.id} }" class="btn btn-block btn-white text-capitalize checkins">
|
||||
{{$tc('checkin', 2)}}
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="col-12 col-md-3 mb-2 mb-md-0">
|
||||
<router-link :to="{path: `/dashboard/service/${service.id}/failures`, params: {id: service.id} }" class="btn btn-block btn-white text-capitalize failures">
|
||||
{{$tc('failure', 2)}} <span class="badge badge-danger float-right mt-1">{{service.stats.failures}}</span>
|
||||
</router-link>
|
||||
</div>
|
||||
<div class="col-12 col-md-3 mb-2 mb-md-0 mt-0 mt-md-1">
|
||||
<span class="text-black-50 float-md-right">
|
||||
{{$t('uptime', [service.online_7_days])}}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span v-for="(failure, index) in failures" v-bind:key="index" class="alert alert-light">
|
||||
Failed {{failure.created_at}}<br>
|
||||
{{failure.issue}}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Checkin from '../../forms/Checkin';
|
||||
import FormIncident from '../../forms/Incident';
|
||||
import FormMessage from '../../forms/Message';
|
||||
import ServiceFailures from './ServiceFailures';
|
||||
import ServiceSparkLine from "./ServiceSparkLine";
|
||||
import Api from "../../API";
|
||||
|
||||
export default {
|
||||
name: 'ServiceInfo',
|
||||
components: {
|
||||
Checkin,
|
||||
ServiceFailures,
|
||||
FormIncident,
|
||||
FormMessage,
|
||||
ServiceSparkLine
|
||||
},
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
uptime: null,
|
||||
openTab: "",
|
||||
set1: [],
|
||||
set2: [],
|
||||
loaded: false,
|
||||
set1_name: "",
|
||||
set2_name: "",
|
||||
failures: null,
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
methods: {
|
||||
async setVisible(isVisible, entry) {
|
||||
if (isVisible && !this.visible) {
|
||||
await this.loadInfo()
|
||||
await this.getUptime()
|
||||
this.visible = true
|
||||
}
|
||||
},
|
||||
async getUptime() {
|
||||
this.uptime = await Api.service_uptime(this.service.id)
|
||||
},
|
||||
async loadInfo() {
|
||||
this.set1 = await this.getHits(24 * 7, "6h")
|
||||
this.set1_name = this.calc(this.set1)
|
||||
this.set2 = await this.getHits(24, "1h")
|
||||
this.set2_name = this.calc(this.set2)
|
||||
this.loaded = true
|
||||
},
|
||||
Tab(name) {
|
||||
if (this.openTab === name) {
|
||||
this.openTab = ''
|
||||
return
|
||||
}
|
||||
this.openTab=name;
|
||||
},
|
||||
sinceYesterday(data) {
|
||||
let total = 0
|
||||
data.forEach((f) => {
|
||||
total += parseInt(f.y)
|
||||
});
|
||||
total = total / data.length
|
||||
},
|
||||
async getHits(hours, group) {
|
||||
const start = this.nowSubtract(3600 * hours)
|
||||
const fetched = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(this.now()), group, false)
|
||||
|
||||
const data = this.convertToChartData(fetched, 0.001, true)
|
||||
|
||||
return [{name: "Latency", ...data}]
|
||||
|
||||
},
|
||||
calc(s) {
|
||||
let data = s[0].data
|
||||
|
||||
if (data) {
|
||||
let total = 0
|
||||
data.forEach((f) => {
|
||||
total += parseInt(f.y)
|
||||
});
|
||||
total = total / data.length
|
||||
return Math.round(total) + " ms"
|
||||
} else {
|
||||
return "Offline"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
.offline-card {
|
||||
background-color: #fff5f5;
|
||||
}
|
||||
.fade-enter-active, .fade-leave-active {
|
||||
transition: opacity .75s;
|
||||
}
|
||||
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,165 @@
|
|||
<template>
|
||||
<div class="text-center" style="width:210px" v-if="!loaded">
|
||||
<font-awesome-icon icon="circle-notch" class="h-25 text-dim" spin/>
|
||||
</div>
|
||||
<apexchart v-else width="240" height="30" type="bar" :options="chartOpts" :series="data"></apexchart>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "@/API";
|
||||
const timeoptions = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' };
|
||||
|
||||
|
||||
export default {
|
||||
name: "ServiceSparkList",
|
||||
props: {
|
||||
service: {
|
||||
required: true,
|
||||
type: Object,
|
||||
},
|
||||
timeframe: {
|
||||
required: true,
|
||||
type: String,
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
data: null,
|
||||
loaded: false,
|
||||
chartOpts: {
|
||||
chart: {
|
||||
type: 'bar',
|
||||
height: 50,
|
||||
sparkline: {
|
||||
enabled: true
|
||||
},
|
||||
animations: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
xaxis: {
|
||||
type: 'datetime',
|
||||
},
|
||||
showPoint: false,
|
||||
fullWidth:true,
|
||||
chartPadding: {top: 0,right: 0,bottom: 0,left: 0},
|
||||
stroke: {
|
||||
curve: 'straight'
|
||||
},
|
||||
fill: {
|
||||
opacity: 0.4,
|
||||
},
|
||||
yaxis: {
|
||||
min: 0,
|
||||
max: 5,
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
colors: {
|
||||
ranges: [{
|
||||
from: 0,
|
||||
to: 1,
|
||||
color: '#cfcfcf'
|
||||
}, {
|
||||
from: 2,
|
||||
to: 3,
|
||||
color: '#f58e49'
|
||||
}, {
|
||||
from: 3,
|
||||
to: 20,
|
||||
color: '#e01a1a'
|
||||
}, {
|
||||
from: 21,
|
||||
to: Infinity,
|
||||
color: '#9b0909'
|
||||
}]
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
theme: false,
|
||||
enabled: true,
|
||||
custom: ({series, seriesIndex, dataPointIndex, w}) => {
|
||||
let val = series[seriesIndex][dataPointIndex];
|
||||
let ts = w.globals.seriesX[seriesIndex][dataPointIndex];
|
||||
const dt = new Date(ts).toLocaleDateString("en-us", timeoptions)
|
||||
let ago = `${(dataPointIndex-12) * -1} hours ago`
|
||||
if ((dataPointIndex-12) * -1 === 0) {
|
||||
ago = `Current hour`
|
||||
}
|
||||
return `<div class="chart_list_tooltip">${val-1} Failures<br>${dt}</div>`
|
||||
},
|
||||
fixed: {
|
||||
enabled: true,
|
||||
position: 'topLeft',
|
||||
offsetX: 0,
|
||||
offsetY: 0,
|
||||
},
|
||||
x: {
|
||||
formatter: (value) => { return value },
|
||||
},
|
||||
y: {
|
||||
show: false
|
||||
},
|
||||
},
|
||||
title: {
|
||||
enabled: false,
|
||||
},
|
||||
subtitle: {
|
||||
enabled: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.loadFailures()
|
||||
},
|
||||
watch: {
|
||||
timeframe(o, n) {
|
||||
this.loaded = false
|
||||
this.loadFailures()
|
||||
this.loaded = true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
convertChartData(data) {
|
||||
if (!data) {
|
||||
return []
|
||||
}
|
||||
let arr = []
|
||||
data.forEach((d, k) => {
|
||||
arr.push({
|
||||
x: d.timeframe,
|
||||
y: d.amount+1,
|
||||
})
|
||||
})
|
||||
return arr
|
||||
},
|
||||
async loadFailures() {
|
||||
this.loaded = false
|
||||
let start = 43200
|
||||
let group = "12h"
|
||||
if (this.timeframe === "3h") {
|
||||
start = 10800
|
||||
group = "5m"
|
||||
} else if (this.timeframe === "12h") {
|
||||
start = 43200
|
||||
group = "1h"
|
||||
} else if (this.timeframe === "24h") {
|
||||
start = 86400
|
||||
group = "2h"
|
||||
} else if (this.timeframe === "7d") {
|
||||
start = 86400 * 7
|
||||
group = "24h"
|
||||
}
|
||||
|
||||
const startTime = this.beginningOf("day", this.nowSubtract(start))
|
||||
const endTime = this.endOf("day", this.now())
|
||||
|
||||
const data = await Api.service_failures_data(this.service.id, this.toUnix(startTime), this.toUnix(endTime), group, true)
|
||||
this.loaded = true
|
||||
this.data = [{data: this.convertChartData(data)}]
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -2,15 +2,15 @@
|
|||
<div class="row stats_area mt-5 mb-4">
|
||||
<div class="col-4">
|
||||
<span class="font-5 d-block font-weight-bold">{{humanTime(service.avg_response)}}</span>
|
||||
<span class="font-1 subtitle">{{$t('service.average')}}</span>
|
||||
<span class="font-1 subtitle">{{$t('average_response')}}</span>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<span class="font-5 d-block font-weight-bold">{{service.online_24_hours}} %</span>
|
||||
<span class="font-1 subtitle">{{$t('service.last_uptime', [24, $tc('hour', 24)])}}</span>
|
||||
<span class="font-1 subtitle">{{$t('last_uptime', [24, $tc('hour', 24)])}}</span>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<span class="font-5 d-block font-weight-bold">{{service.online_7_days}} %</span>
|
||||
<span class="font-1 subtitle">{{$t('service.last_uptime', [7, $tc('day', 7)])}}</span>
|
||||
<span class="font-1 subtitle">{{$t('last_uptime', [7, $tc('day', 7)])}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -16,19 +16,15 @@
|
|||
<div class="col-12 alert alert-light">
|
||||
<form @submit.prevent="saveCheckin">
|
||||
<div class="form-group row">
|
||||
<div class="col-5">
|
||||
<div class="col-12 col-md-5">
|
||||
<label for="checkin_interval" class="col-form-label">Checkin Name</label>
|
||||
<input v-model="checkin.name" type="text" name="name" class="form-control" id="checkin_name" placeholder="New Checkin">
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<label for="checkin_interval" class="col-form-label">Interval</label>
|
||||
<input v-model="checkin.interval" type="number" name="interval" class="form-control" id="checkin_interval" placeholder="60">
|
||||
<div class="col-12 col-md-5">
|
||||
<label for="checkin_interval" class="col-form-label">Interval (minutes)</label>
|
||||
<input v-model="checkin.interval" type="number" name="interval" class="form-control" id="checkin_interval" placeholder="1" min="1">
|
||||
</div>
|
||||
<div class="col-2">
|
||||
<label for="grace_period" class="col-form-label">Grace Period</label>
|
||||
<input v-model="checkin.grace" type="number" name="grace" class="form-control" id="grace_period" placeholder="10">
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<div class="col-12 col-md-5">
|
||||
<label class="col-form-label"></label>
|
||||
<button @click.prevent="saveCheckin" type="submit" id="submit" class="btn btn-success d-block mt-2">Save Checkin</button>
|
||||
</div>
|
||||
|
@ -54,7 +50,6 @@
|
|||
checkin: {
|
||||
name: "",
|
||||
interval: 60,
|
||||
grace: 60,
|
||||
service_id: this.service.id
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,15 @@
|
|||
<template>
|
||||
<form @submit.prevent="saveSettings">
|
||||
<div class="card">
|
||||
<div class="card-header">Statping Settings</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group">
|
||||
<label>{{ $t('settings.name') }}</label>
|
||||
<label>{{ $t('project_name') }}</label>
|
||||
<input v-model="core.name" type="text" class="form-control" placeholder="Great Uptime" id="project">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ $t('settings.description') }}</label>
|
||||
<label>{{ $t('description') }}</label>
|
||||
<input v-model="core.description" type="text" class="form-control" placeholder="Great Uptime" id="description">
|
||||
</div>
|
||||
|
||||
|
@ -16,8 +19,8 @@
|
|||
<input v-model="core.domain" type="url" class="form-control" id="domain">
|
||||
</div>
|
||||
<div class="col-4 col-sm-3 mt-sm-1 mt-0">
|
||||
<label class="d-inline d-sm-none">Enable CDN</label>
|
||||
<label class="d-none d-sm-block">Enable CDN</label>
|
||||
<label class="d-inline d-sm-none">{{$t('enable_cdn')}}</label>
|
||||
<label class="d-none d-sm-block">{{$t('enable_cdn')}}</label>
|
||||
<span @click="core.using_cdn = !!core.using_cdn" class="switch" id="using_cdn">
|
||||
<input v-model="core.using_cdn" type="checkbox" name="using_cdn" class="switch" id="switch-normal" :checked="core.using_cdn">
|
||||
<label for="switch-normal"></label>
|
||||
|
@ -26,24 +29,28 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ $t('settings.footer') }}</label>
|
||||
<label>{{ $t('footer') }}</label>
|
||||
<textarea v-model="core.footer" rows="4" class="form-control" id="footer">{{core.footer}}</textarea>
|
||||
<small class="form-text text-muted">{{ $t('settings.footer_notes') }}</small>
|
||||
<small class="form-text text-muted">{{ $t('footer_notes') }}</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>{{ $t('setup.language') }}</label>
|
||||
<label>{{ $t('language') }}</label>
|
||||
<select v-model="core.language" class="form-control">
|
||||
<option value="en">English</option>
|
||||
<option value="es">Spanish</option>
|
||||
<option value="fr">French</option>
|
||||
<option value="ru">Russian</option>
|
||||
<option value="de">German</option>
|
||||
<option value="ja">Japanese</option>
|
||||
<option value="it">Italian</option>
|
||||
<option value="ko">Korean</option>
|
||||
<option value="zh">Chinese</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group row mt-3">
|
||||
<label class="col-sm-10 col-form-label">{{ $t('settings.error_reporting') }}</label>
|
||||
<label class="col-sm-10 col-form-label">{{ $t('send_reports') }}</label>
|
||||
<div class="col-sm-2 float-right">
|
||||
<span @click="core.allow_reports = !!core.allow_reports" class="switch" id="allow_report">
|
||||
<input v-model="core.allow_reports" type="checkbox" name="allow_report" class="switch" id="switch_allow_report" :checked="core.allow_reports">
|
||||
|
@ -51,14 +58,17 @@
|
|||
</span>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<small>{{ $t('settings.error_reporting_notes') }}</small>
|
||||
<small>{{ $t('send_reports_desc') }}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button @click.prevent="saveSettings" id="save_core" type="submit" class="btn btn-primary btn-block mt-3" v-bind:disabled="loading">
|
||||
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/>{{ $t('settings.save') }}
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<button @click.prevent="saveSettings" id="save_core" type="submit" class="btn btn-primary btn-block" v-bind:disabled="loading">
|
||||
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/>{{ $t('save_settings') }}
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
|
@ -82,8 +92,8 @@
|
|||
this.loading = true
|
||||
const c = this.core
|
||||
await Api.core_save(c)
|
||||
const core = await Api.core()
|
||||
this.$store.commit('setCore', core)
|
||||
this.$store.commit('setCore', c)
|
||||
this.$i18n.locale = c.language || "en";
|
||||
this.loading = false
|
||||
},
|
||||
selectAll() {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="card contain-card text-black-50 bg-white mb-3">
|
||||
<div class="card-header">{{group.id ? `Update ${group.name}` : "Create Group"}}
|
||||
<div class="card contain-card mb-3">
|
||||
<div class="card-header">{{group.id ? `${$t('update')} ${group.name}` : $t('group_create')}}
|
||||
<transition name="slide-fade">
|
||||
<button @click="removeEdit" v-if="group.id" class="btn float-right btn-danger btn-sm">
|
||||
{{ $t('close') }}
|
||||
|
@ -10,24 +10,24 @@
|
|||
|
||||
<form @submit.prevent="saveGroup">
|
||||
<div class="form-group row">
|
||||
<label for="title" class="col-sm-4 col-form-label">{{ $t('dashboard.group') }} {{ $t('dashboard.name') }}</label>
|
||||
<label for="title" class="col-sm-4 col-form-label">{{ $t('group') }} {{ $t('name') }}</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="group.name" type="text" class="form-control" id="title" placeholder="Group Name" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="switch-group-public" class="col-sm-4 col-form-label text-capitalize">{{ $t('public') }} {{ $t('dashboard.group') }}</label>
|
||||
<label for="switch-group-public" class="col-sm-4 col-form-label text-capitalize">{{ $t('public') }} {{ $t('group') }}</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
<span @click="group.public = !!group.public" class="switch float-left">
|
||||
<input v-model="group.public" type="checkbox" class="switch" id="switch-group-public" :checked="group.public">
|
||||
<label for="switch-group-public">Show group services to the public</label>
|
||||
<label for="switch-group-public">{{$t('group_public_desc')}}</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button @click.prevent="saveGroup" type="submit" :disabled="loading || group.name === ''" class="btn btn-block" :class="{'btn-primary': !group.id, 'btn-secondary': group.id}">
|
||||
{{loading ? "Loading..." : group.id ? "Update Group" : "Create Group"}}
|
||||
{{loading ? "Loading..." : group.id ? $t('group_update') : $t('group_create')}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,44 +0,0 @@
|
|||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../API";
|
||||
import flatPickr from 'vue-flatpickr-component';
|
||||
import 'flatpickr/dist/flatpickr.css';
|
||||
import FormIncidentUpdates from './IncidentUpdates';
|
||||
|
||||
export default {
|
||||
name: 'FormIncident',
|
||||
components: {
|
||||
FormIncidentUpdates
|
||||
},
|
||||
props: {
|
||||
service: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
incident: {
|
||||
title: "",
|
||||
description: "",
|
||||
service: this.service.id,
|
||||
},
|
||||
incidents: [],
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
await this.loadIncidents()
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -1,24 +1,16 @@
|
|||
<template>
|
||||
<div class="card-body bg-light pt-3">
|
||||
<div class="card-body pt-3">
|
||||
|
||||
<div v-if="updates.length===0" class="alert alert-link text-danger">
|
||||
No updates found, create a new Incident Update below.
|
||||
</div>
|
||||
|
||||
<div v-for="update in updates" :key="update.id">
|
||||
<div class="alert alert-light" role="alert">
|
||||
<span class="badge badge-pill badge-info text-uppercase">{{update.type}}</span>
|
||||
<span class="float-right font-2">{{ago(update.created_at)}} ago</span>
|
||||
<span class="d-block mt-2">{{update.message}}
|
||||
<button @click="delete_update(update)" type="button" class="close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</span>
|
||||
</div>
|
||||
<div v-for="update in updates.reverse()" :key="update.id">
|
||||
<IncidentUpdate :update="update" :onUpdate="loadUpdates" :admin="true"/>
|
||||
</div>
|
||||
|
||||
<form class="row" @submit.prevent="createIncidentUpdate">
|
||||
<div class="col-3">
|
||||
<div class="col-12 col-md-3 mb-3 mb-md-0">
|
||||
<select v-model="incident_update.type" class="form-control">
|
||||
<option value="Investigating">Investigating</option>
|
||||
<option value="Update">Update</option>
|
||||
|
@ -26,11 +18,11 @@
|
|||
<option value="Resolved">Resolved</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-7">
|
||||
<input v-model="incident_update.message" rows="5" name="description" class="form-control" id="message" required>
|
||||
<div class="col-12 col-md-7 mb-3 mb-md-0">
|
||||
<input v-model="incident_update.message" name="description" class="form-control" id="message" required>
|
||||
</div>
|
||||
|
||||
<div class="col-2">
|
||||
<div class="col-12 col-md-2">
|
||||
<button @click.prevent="createIncidentUpdate"
|
||||
:disabled="!incident_update.message"
|
||||
type="submit" class="btn btn-block btn-primary">
|
||||
|
@ -44,12 +36,11 @@
|
|||
|
||||
<script>
|
||||
import Api from "../API";
|
||||
import flatPickr from 'vue-flatpickr-component';
|
||||
import 'flatpickr/dist/flatpickr.css';
|
||||
const IncidentUpdate = () => import(/* webpackChunkName: "index" */ "@/components/Elements/IncidentUpdate");
|
||||
|
||||
export default {
|
||||
name: 'FormIncidentUpdates',
|
||||
components: {},
|
||||
components: {IncidentUpdate},
|
||||
props: {
|
||||
incident: {
|
||||
type: Object,
|
||||
|
@ -58,7 +49,7 @@
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
updates: [],
|
||||
updates: null,
|
||||
incident_update: {
|
||||
incident: this.incident.id,
|
||||
message: "",
|
||||
|
@ -72,15 +63,6 @@
|
|||
},
|
||||
|
||||
methods: {
|
||||
|
||||
async delete_update(update) {
|
||||
this.res = await Api.incident_update_delete(update)
|
||||
if (this.res.status === "success") {
|
||||
this.updates = this.updates.filter(obj => obj.id !== update.id); // this is better in terms of not having to querry the db to get a fresh copy of all updates
|
||||
//await this.loadUpdates()
|
||||
}
|
||||
},
|
||||
|
||||
async createIncidentUpdate() {
|
||||
this.res = await Api.incident_update_create(this.incident_update)
|
||||
if (this.res.status === "success") {
|
||||
|
|
|
@ -2,30 +2,30 @@
|
|||
<div>
|
||||
<form @submit.prevent="login" autocomplete="on">
|
||||
<div class="form-group row">
|
||||
<label for="username" class="col-sm-2 col-form-label">{{$t('username')}}</label>
|
||||
<div class="col-sm-10">
|
||||
<input @keyup="checkForm" type="text" v-model="username" name="username" class="form-control" id="username" placeholder="Username" autocorrect="off" autocapitalize="none">
|
||||
<label for="username" class="col-4 col-form-label">{{$t('username')}}</label>
|
||||
<div class="col-8">
|
||||
<input @keyup="checkForm" type="text" v-model="username" autocomplete="username" name="username" class="form-control" id="username" placeholder="admin" autocorrect="off" autocapitalize="none">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="password" class="col-sm-2 col-form-label">{{$t('password')}}</label>
|
||||
<div class="col-sm-10">
|
||||
<input @keyup="checkForm" type="password" v-model="password" name="password" class="form-control" id="password" placeholder="Password">
|
||||
<label for="password" class="col-4 col-form-label">{{$t('password')}}</label>
|
||||
<div class="col-8">
|
||||
<input @keyup="checkForm" type="password" v-model="password" autocomplete="current-password" name="password" class="form-control" id="password" placeholder="************">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<div v-if="error" class="alert alert-danger" role="alert">
|
||||
{{$t('dashboard.wrong_login')}}
|
||||
{{$t('wrong_login')}}
|
||||
</div>
|
||||
<button @click.prevent="login" type="submit" class="btn btn-block mb-3 btn-primary" :disabled="disabled || loading">
|
||||
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/>{{loading ? $t('dashboard.loading') : $t('dashboard.sign_in')}}
|
||||
<button @click.prevent="login" type="submit" class="btn btn-block btn-primary" :disabled="disabled || loading">
|
||||
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/>{{loading ? $t('loading') : $t('sign_in')}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<a v-if="oauth && oauth.gh_client_id" @click.prevent="GHlogin" href="#" class="btn btn-block btn-outline-dark">
|
||||
<a v-if="oauth && oauth.gh_client_id" @click.prevent="GHlogin" href="#" class="mt-4 btn btn-block btn-outline-dark">
|
||||
<font-awesome-icon :icon="['fab', 'github']" /> Login with Github
|
||||
</a>
|
||||
|
||||
|
@ -69,6 +69,9 @@
|
|||
slack_scope: "identity.email,identity.basic"
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.$cookies.remove("statping_auth")
|
||||
},
|
||||
methods: {
|
||||
checkForm() {
|
||||
if (!this.username || !this.password) {
|
||||
|
@ -84,24 +87,39 @@
|
|||
if (auth.error) {
|
||||
this.error = true
|
||||
} else if (auth.token) {
|
||||
// this.$cookies.set("statping_auth", auth.token)
|
||||
this.$cookies.set("statping_auth", auth.token)
|
||||
await this.$store.dispatch('loadAdmin')
|
||||
this.$store.commit('setAdmin', auth.admin)
|
||||
this.$store.commit('setLoggedIn', true)
|
||||
this.$router.push('/dashboard')
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
encode(val) {
|
||||
return encodeURI(val)
|
||||
},
|
||||
custom_scopes() {
|
||||
let scopes = []
|
||||
if (this.oauth.custom_open_id) {
|
||||
scopes.push("openid")
|
||||
}
|
||||
scopes.push(this.oauth.custom_scopes.split(","))
|
||||
if (scopes.length !== 0) {
|
||||
return "&scopes="+scopes.join(",")
|
||||
}
|
||||
return ""
|
||||
},
|
||||
GHlogin() {
|
||||
window.location = `https://github.com/login/oauth/authorize?client_id=${this.oauth.gh_client_id}&redirect_uri=${this.core.domain}/oauth/github&scope=user,repo`
|
||||
window.location = `https://github.com/login/oauth/authorize?client_id=${this.oauth.gh_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/github")}&scope=user,repo`
|
||||
},
|
||||
Slacklogin() {
|
||||
window.location = `https://slack.com/oauth/authorize?client_id=${this.oauth.slack_client_id}&redirect_uri=${this.core.domain}/oauth/slack&scope=identity.basic`
|
||||
window.location = `https://slack.com/oauth/authorize?client_id=${this.oauth.slack_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/slack")}&scope=identity.basic`
|
||||
},
|
||||
Googlelogin() {
|
||||
window.location = `https://accounts.google.com/signin/oauth?client_id=${this.oauth.google_client_id}&redirect_uri=${this.core.domain}/oauth/google&response_type=code&scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email`
|
||||
window.location = `https://accounts.google.com/signin/oauth?client_id=${this.oauth.google_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/google")}&response_type=code&scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email`
|
||||
},
|
||||
Customlogin() {
|
||||
window.location = `${this.oauth.custom_endpoint_auth}?client_id=${this.oauth.custom_client_id}&redirect_uri=${this.core.domain}/oauth/custom${this.oauth.custom_scopes !== "" ? "&scope="+this.oauth.custom_scopes : "" }`
|
||||
window.location = `${this.oauth.custom_endpoint_auth}?client_id=${this.oauth.custom_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/custom")}&response_type=code${this.custom_scopes()}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<div class="card contain-card text-black-50 bg-white mb-5">
|
||||
<div class="card-header">{{message.id ? `Update ${message.title}` : "Create Announcement"}}
|
||||
<div class="card contain-card mb-5">
|
||||
<div class="card-header">{{message.id ? `${$t('update')} ${message.title}` : $t('message_create')}}
|
||||
<transition name="slide-fade">
|
||||
<button @click="removeEdit" v-if="message.id" class="btn btn-sm float-right btn-danger btn-sm">
|
||||
{{ $t('close') }}
|
||||
|
@ -11,75 +11,75 @@
|
|||
<div class="card-body">
|
||||
<form @submit="saveMessage">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">{{ $t('dashboard.title') }}</label>
|
||||
<label class="col-sm-4 col-form-label">{{ $t('title') }}</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="message.title" type="text" name="title" class="form-control" id="title" placeholder="Announcement Title" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Description</label>
|
||||
<label class="col-sm-4 col-form-label">{{ $t('description') }}</label>
|
||||
<div class="col-sm-8">
|
||||
<textarea v-model="message.description" rows="5" name="description" class="form-control" id="description" required></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Service</label>
|
||||
<label class="col-sm-4 col-form-label">{{ $t('service') }}</label>
|
||||
<div class="col-sm-8">
|
||||
<select v-model="message.service" name="service_id" class="form-control">
|
||||
<option :value="0">Global Announcement</option>
|
||||
<option v-bind:value="0">{{ $t('global_announcement') }}</option>
|
||||
<option v-for="service in $store.getters.services" :value="service.id" v-bind:key="service.id" >{{service.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Announcement Date Range</label>
|
||||
<label class="col-sm-4 col-form-label">{{ $t('announcement_date') }}</label>
|
||||
<div class="col-sm-4">
|
||||
<flatPickr v-model="message.start_on" @on-change="startChange" :config="config" type="text" name="start_on" class="form-control form-control-plaintext" id="start_on" value="0001-01-01T00:00:00Z" required />
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="col-sm-4 mt-3 mt-md-0">
|
||||
<flatPickr v-model="message.end_on" @on-change="endChange" :config="config" type="text" name="end_on" class="form-control form-control-plaintext" id="end_on" value="0001-01-01T00:00:00Z" required />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-show="this.service === null" class="form-group row">
|
||||
<label for="service_id" class="col-sm-4 col-form-label">Service</label>
|
||||
<label for="service_id" class="col-sm-4 col-form-label">{{ $t('service') }}</label>
|
||||
<div class="col-sm-8">
|
||||
<select v-model="message.service" class="form-control" name="service" id="service_id">
|
||||
<option :value="0">Global Message</option>
|
||||
<option v-bind:value="0">{{ $t('global_announcement') }}</option>
|
||||
<option v-for="service in $store.getters.services" :value="service.id" v-bind:key="service.id">{{service.name}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="notify_method" class="col-sm-4 col-form-label">Notify Users</label>
|
||||
<label for="notify_method" class="col-sm-4 col-form-label">{{ $t('notify_users') }}</label>
|
||||
<div class="col-sm-8">
|
||||
<span @click="message.notify = !!message.notify" class="switch">
|
||||
<input v-model="message.notify" type="checkbox" class="switch" id="switch-normal">
|
||||
<label for="switch-normal">Notify Users Before Scheduled Time</label>
|
||||
<label for="switch-normal">{{ $t('notify_desc') }}</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="message.notify" class="form-group row">
|
||||
<label for="notify_method" class="col-sm-4 col-form-label">Notification Method</label>
|
||||
<label for="notify_method" class="col-sm-4 col-form-label">{{ $t('notify_method') }}</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="message.notify_method" type="text" name="notify_method" class="form-control" id="notify_method" value="" placeholder="email">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="message.notify" class="form-group row">
|
||||
<label for="notify_before" class="col-sm-4 col-form-label">Notify Before</label>
|
||||
<label for="notify_before" class="col-sm-4 col-form-label">{{ $t('notify_before') }}</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="form-inline">
|
||||
<input v-model="message.notify_before" type="number" name="notify_before" class="col-4 form-control" id="notify_before">
|
||||
<input v-model.number="message.notify_before" type="number" name="notify_before" class="col-4 form-control" id="notify_before">
|
||||
<select v-model="message.notify_before_scale" class="ml-2 col-7 form-control" name="notify_before_scale" id="notify_before_scale">
|
||||
<option value="minute">Minutes</option>
|
||||
<option value="hour">Hours</option>
|
||||
<option value="day">Days</option>
|
||||
<option value="minute">{{ $t('minutes') }}</option>
|
||||
<option value="hour">{{ $t('hours') }}</option>
|
||||
<option value="day">{{ $t('days') }}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -90,7 +90,7 @@
|
|||
<button @click="saveMessage"
|
||||
:disabled="!message.title || !message.description"
|
||||
type="submit" class="btn btn-block" :class="{'btn-primary': !message.id, 'btn-secondary': message.id}">
|
||||
{{message.id ? "Edit Message" : "Create Message"}}
|
||||
{{message.id ? $t('message_edit') : $t('message_create')}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -130,6 +130,7 @@
|
|||
start_on: new Date(),
|
||||
end_on: new Date(),
|
||||
service_id: 0,
|
||||
service: 0,
|
||||
notify_method: "",
|
||||
notify: false,
|
||||
notify_before: 0,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div>
|
||||
<form @submit.prevent="saveNotifier">
|
||||
<div class="card contain-card text-black-50 bg-white mb-3">
|
||||
<div class="card contain-card mb-3">
|
||||
<div class="card-header text-capitalize">
|
||||
{{notifier.title}}
|
||||
<span @click="enableToggle" class="switch switch-sm switch-rd-gr float-right">
|
||||
|
@ -14,10 +14,10 @@
|
|||
|
||||
<div v-if="notifier.method==='mobile'">
|
||||
<div class="form-group row mt-3">
|
||||
<label for="domain" class="col-sm-4 col-form-label">Statping Domain</label>
|
||||
<label for="statping_domain" class="col-sm-4 col-form-label">Statping Domain</label>
|
||||
<div class="col-sm-8">
|
||||
<div class="input-group">
|
||||
<input v-bind:value="$store.getters.core.domain" type="text" class="form-control" id="domain" readonly>
|
||||
<input v-bind:value="$store.getters.core.domain" type="text" class="form-control" id="statping_domain" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy($store.getters.core.domain)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
|
@ -69,7 +69,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="notifier.data_type" class="card text-black-50 bg-white mb-3">
|
||||
<div v-if="notifier.data_type" class="card mb-3">
|
||||
<div class="card-header text-capitalize">
|
||||
<font-awesome-icon @click="expanded = !expanded" :icon="expanded ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
{{notifier.title}} Outgoing Request
|
||||
|
@ -103,7 +103,7 @@
|
|||
|
||||
</form>
|
||||
|
||||
<div v-if="error || success" class="card text-black-50 bg-white mb-3">
|
||||
<div v-if="error || success" class="card mb-3">
|
||||
<div class="card-body">
|
||||
|
||||
<div v-if="error && !success" class="alert alert-danger col-12" role="alert">
|
||||
|
@ -119,7 +119,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-12 col-sm-4 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||
|
@ -128,14 +128,39 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="col-12 col-md-4 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||
<button @click.prevent="testNotifier('success')" :disabled="loadingTest" class="btn btn-outline-dark btn-block text-capitalize test-notifier">
|
||||
<button @click.prevent="testNotifier('success')" :disabled="loadingTest" class="btn btn-secondary btn-block text-capitalize test-notifier">
|
||||
<font-awesome-icon v-if="loadingTest" icon="circle-notch" class="mr-2" spin/>{{loadingTest ? "Loading..." : "Test Success"}}</button>
|
||||
</div>
|
||||
<div class="col-12 col-md-4 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||
<button @click.prevent="testNotifier('failure')" :disabled="loadingTest" class="btn btn-outline-dark btn-block text-capitalize test-notifier">
|
||||
<button @click.prevent="testNotifier('failure')" :disabled="loadingTest" class="btn btn-secondary btn-block text-capitalize test-notifier">
|
||||
<font-awesome-icon v-if="loadingTest" icon="circle-notch" class="mr-2" spin/>{{loadingTest ? "Loading..." : "Test Failure"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="notifier.logs" class="card mb-3">
|
||||
<div class="card-header text-capitalize">
|
||||
<font-awesome-icon @click="expanded_logs = !expanded_logs" :icon="expanded_logs ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
{{notifier.title}} Logs
|
||||
<span class="badge badge-info float-right text-uppercase mt-1">{{notifier.logs.length}}</span>
|
||||
</div>
|
||||
<div class="card-body" :class="{'d-none': !expanded_logs}">
|
||||
|
||||
<div v-for="(log, i) in notifier.logs.reverse()" class="alert" :class="{'alert-danger': log.error, 'alert-dark': !log.success && !log.error, 'alert-success': log.success && !log.error}">
|
||||
<span class="d-block">
|
||||
Service {{log.service}}
|
||||
{{log.success ? "Success Triggered" : "Failure Triggered"}}
|
||||
</span>
|
||||
|
||||
<div v-if="log.message !== ''" class="bg-white p-3 small mt-2">
|
||||
<code>{{log.message}}</code>
|
||||
</div>
|
||||
|
||||
<div class="row mt-2">
|
||||
<span class="col-6 small">{{niceDate(log.created_at)}}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -148,16 +173,19 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../API";
|
||||
import Api from "../API";
|
||||
/* webpackChunkName: "codemirror" */
|
||||
import {codemirror} from 'vue-codemirror'
|
||||
/* webpackChunkName: "codemirror" */
|
||||
import 'codemirror/mode/javascript/javascript.js'
|
||||
/* webpackChunkName: "codemirror" */
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
/* webpackChunkName: "codemirror" */
|
||||
import 'codemirror/theme/neat.css'
|
||||
/* webpackChunkName: "codemirror" */
|
||||
import '../codemirror_json'
|
||||
|
||||
const beautify = require('js-beautify').js
|
||||
|
||||
// require component
|
||||
import { codemirror } from 'vue-codemirror'
|
||||
import 'codemirror/mode/javascript/javascript.js'
|
||||
import 'codemirror/lib/codemirror.css'
|
||||
import 'codemirror/theme/neat.css'
|
||||
import '../codemirror_json'
|
||||
const beautify = require('js-beautify').js
|
||||
|
||||
export default {
|
||||
name: 'Notifier',
|
||||
|
@ -183,6 +211,7 @@ export default {
|
|||
success: false,
|
||||
saved: false,
|
||||
expanded: false,
|
||||
expanded_logs: false,
|
||||
success_data: null,
|
||||
failure_data: null,
|
||||
form: {},
|
||||
|
@ -300,10 +329,3 @@ export default {
|
|||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped>
|
||||
.CodeMirror {
|
||||
border: 1px solid #eee;
|
||||
height: 550px;
|
||||
font-size: 9pt;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<form @submit.prevent="saveOAuth">
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
Internal Login
|
||||
<span @click="local_enabled = !!local_enabled" class="switch switch-sm switch-rd-gr float-right">
|
||||
|
@ -12,7 +12,7 @@
|
|||
Use Statping's default authentication to allow users you've created to login.
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header text-capitalize">
|
||||
<font-awesome-icon @click="expanded.github = !expanded.github" :icon="expanded.github ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Github Settings
|
||||
|
@ -50,12 +50,6 @@
|
|||
<small>Optional comma delimited list of Github Organizations</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Enable Github Login</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="gh_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<div class="col-sm-8">
|
||||
|
@ -69,7 +63,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<font-awesome-icon @click="expanded.google = !expanded.google" :icon="expanded.google ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Google Settings
|
||||
|
@ -113,7 +107,7 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<font-awesome-icon @click="expanded.slack = !expanded.slack" :icon="expanded.slack ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Slack Settings
|
||||
|
@ -165,7 +159,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header">
|
||||
<font-awesome-icon @click="expanded.custom = !expanded.custom" :icon="expanded.custom ? 'minus' : 'plus'" class="mr-2 pointer"/>
|
||||
Custom oAuth Settings
|
||||
|
@ -174,8 +168,8 @@
|
|||
<label for="switch-custom-oauth" class="mb-0"> </label>
|
||||
</span>
|
||||
</div>
|
||||
<div class="card-body" :class="{'d-none': !expanded.custom}">
|
||||
<div class="form-group row mt-3">
|
||||
<div class="card-body" :class="{'d-none': !expanded.custom || !custom_enabled}">
|
||||
<div class="form-group row">
|
||||
<label for="custom_name" class="col-sm-4 col-form-label">Custom Name</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.custom_name" type="text" class="form-control" id="custom_name" required>
|
||||
|
@ -212,6 +206,17 @@
|
|||
<small>Optional comma delimited list of oauth scopes</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="custom_scopes" class="col-sm-4 col-form-label">Open ID</label>
|
||||
<div class="col-sm-8">
|
||||
<span @click="oauth.custom_open_id = !!oauth.custom_open_id" class="switch switch-rd-gr float-right">
|
||||
<input v-model="oauth.custom_open_id" type="checkbox" id="switch-custom-openid" :checked="oauth.custom_open_id">
|
||||
<label for="switch-custom-openid" class="mb-0"> </label>
|
||||
</span>
|
||||
<small>Enable if provider is OpenID</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="slack_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<div class="col-sm-8">
|
||||
|
@ -277,6 +282,7 @@
|
|||
custom_endpoint_auth: "",
|
||||
custom_endpoint_token: "",
|
||||
custom_scopes: "",
|
||||
custom_open_id: false,
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,24 +1,25 @@
|
|||
<template>
|
||||
<form v-if="service.type" @submit.prevent="saveService">
|
||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div class="card-header">{{ $t('service.info') }}</div>
|
||||
<div class="card contain-card mb-4">
|
||||
<div class="card-header">{{ $t('service_info') }}</div>
|
||||
<div class="card-body">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">{{ $t('service.name') }}</label>
|
||||
<label class="col-sm-4 col-form-label">{{ $t('service_name') }}</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="service.name" @input="updatePermalink" id="name" type="text" name="name" class="form-control" placeholder="Server Name" required spellcheck="false" autocorrect="off">
|
||||
<small class="form-text text-muted">Give your service a name you can recognize</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="service_type" class="col-sm-4 col-form-label">{{ $t('service.type') }}</label>
|
||||
<label for="service_type" class="col-sm-4 col-form-label">{{ $t('service_type') }}</label>
|
||||
<div class="col-sm-8">
|
||||
<select v-model="service.type" class="form-control" id="service_type">
|
||||
<option value="http">HTTP Service</option>
|
||||
<option value="tcp">TCP Service</option>
|
||||
<option value="udp">UDP Service</option>
|
||||
<option value="http">HTTP {{ $t('service') }}</option>
|
||||
<option value="tcp">TCP {{ $t('service') }}</option>
|
||||
<option value="udp">UDP {{ $t('service') }}</option>
|
||||
<option value="icmp">ICMP Ping</option>
|
||||
<option value="grpc">gRPC Service</option>
|
||||
<option value="grpc">gRPC {{ $t('service') }}</option>
|
||||
<option value="static">Static {{ $t('service') }}</option>
|
||||
</select>
|
||||
<small class="form-text text-muted">Use HTTP if you are checking a website or use TCP if you are checking a server</small>
|
||||
</div>
|
||||
|
@ -34,7 +35,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Permalink URL</label>
|
||||
<label class="col-sm-4 col-form-label">{{ $t('permalink') }}</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="service.permalink" type="text" name="permalink" class="form-control" id="permalink" autocapitalize="none" spellcheck="true" placeholder='awesome_service'>
|
||||
<small class="form-text text-muted">Use text for the service URL rather than the service number.</small>
|
||||
|
@ -42,8 +43,8 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Public Service</label>
|
||||
<div class="col-8 mt-1">
|
||||
<label class="col-sm-4 col-form-label">{{ $t('service_public') }}</label>
|
||||
<div class="col-12 col-md-8 mt-1 mb-2">
|
||||
<span @click="service.public = !!service.public" class="switch float-left">
|
||||
<input v-model="service.public" type="checkbox" name="public-option" class="switch" id="switch-public" v-bind:checked="service.public">
|
||||
<label v-if="service.public" for="switch-public">This service will be visible for everyone</label>
|
||||
|
@ -52,29 +53,31 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="service_interval" class="col-sm-4 col-form-label">Check Interval</label>
|
||||
<div v-if="service.type !== 'static'" class="form-group row">
|
||||
<label for="service_interval" class="col-sm-4 col-form-label">{{ $t('check_interval') }}</label>
|
||||
<div class="col-sm-6">
|
||||
<span class="slider-info">{{secondsHumanize(service.check_interval)}}</span>
|
||||
<input v-model="service.check_interval" type="range" class="slider" id="service_interval" min="1" max="1800" :step="1">
|
||||
<small id="interval" class="form-text text-muted">Interval to check your service state</small>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<input v-model="service.check_interval" type="text" name="check_interval" class="form-control">
|
||||
<input v-model="service.check_interval" type="number" name="check_interval" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div v-if="service.type !== 'static'" class="card contain-card mb-4">
|
||||
<div class="card-header">Request Details</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="form-group row">
|
||||
<label for="service_url" class="col-sm-4 col-form-label">Service Endpoint {{service.type === 'http' ? "(URL)" : "(Domain)"}}</label>
|
||||
<label for="service_url" class="col-sm-4 col-form-label">
|
||||
{{ $t('service_endpoint') }} {{service.type === 'http' ? "(URL)" : "(Domain)"}}
|
||||
</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="service.domain" type="text" class="form-control" id="service_url" :placeholder="service.type === 'http' ? 'https://google.com' : '192.168.1.1'" required autocapitalize="none" spellcheck="false">
|
||||
<input v-model="service.domain" type="url" class="form-control" id="service_url" :placeholder="service.type === 'http' ? 'https://google.com' : '192.168.1.1'" required autocapitalize="none" spellcheck="false">
|
||||
<small class="form-text text-muted">Statping will attempt to connect to this address</small>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -87,7 +90,7 @@
|
|||
</div>
|
||||
|
||||
<div v-if="service.type.match(/^(http)$/)" class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Service Check Type</label>
|
||||
<label class="col-sm-4 col-form-label">{{ $t('service_check') }}</label>
|
||||
<div class="col-sm-8">
|
||||
<select v-model="service.method" name="method" class="form-control">
|
||||
<option value="GET" >GET</option>
|
||||
|
@ -101,7 +104,7 @@
|
|||
</div>
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Request Timeout</label>
|
||||
<label class="col-sm-4 col-form-label">{{ $t('service_timeout') }}</label>
|
||||
<div class="col-sm-6">
|
||||
<span v-if="service.timeout >= 0" class="slider-info">{{secondsHumanize(service.timeout)}}</span>
|
||||
<input v-model="service.timeout" type="range" id="timeout" name="timeout" class="slider" min="1" max="180">
|
||||
|
@ -109,7 +112,7 @@
|
|||
</div>
|
||||
|
||||
<div class="col-sm-2">
|
||||
<input v-model="service.timeout" type="text" name="service_timeout" class="form-control">
|
||||
<input v-model="service.timeout" type="number" name="service_timeout" class="form-control">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
@ -129,14 +132,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<div v-if="service.type.match(/^(http)$/)" class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Expected Response (Regex)</label>
|
||||
<label class="col-sm-4 col-form-label">{{ $t('expected_resp') }} (Regex)</label>
|
||||
<div class="col-sm-8">
|
||||
<textarea v-model="service.expected" class="form-control" rows="3" autocapitalize="none" spellcheck="false" placeholder='(method)": "((\\"|[success])*)"'></textarea>
|
||||
<small class="form-text text-muted">You can use plain text or insert <a target="_blank" href="https://regex101.com/r/I5bbj9/1">Regex</a> to validate the response</small>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="service.type.match(/^(http)$/)" class="form-group row">
|
||||
<label for="service_response_code" class="col-sm-4 col-form-label">Expected Status Code</label>
|
||||
<label for="service_response_code" class="col-sm-4 col-form-label">{{ $t('expected_code') }}</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="service.expected_status" type="number" name="expected_status" class="form-control" placeholder="200" id="service_response_code">
|
||||
<small class="form-text text-muted">A status code of 200 is success, or view all the <a target="_blank" href="https://www.restapitutorial.com/httpstatuscodes.html">HTTP Status Codes</a></small>
|
||||
|
@ -144,8 +147,8 @@
|
|||
</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">
|
||||
<label class="col-12 col-md-4 col-form-label">{{ $t('follow_redir') }}</label>
|
||||
<div class="col-12 col-md-8 mt-1 mb-2 mb-md-0">
|
||||
<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>
|
||||
|
@ -154,8 +157,8 @@
|
|||
</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">
|
||||
<label class="col-12 col-md-4 col-form-label">{{ $t('verify_ssl') }}</label>
|
||||
<div class="col-12 col-md-8 mt-1 mb-2 mb-md-0">
|
||||
<span @click="service.verify_ssl = !!service.verify_ssl" class="switch float-left">
|
||||
<input v-model="service.verify_ssl" type="checkbox" name="verify_ssl-option" class="switch" id="switch-verify-ssl" v-bind:checked="service.verify_ssl">
|
||||
<label for="switch-verify-ssl" v-if="service.verify_ssl">Verify SSL Certificate for this service</label>
|
||||
|
@ -165,8 +168,8 @@
|
|||
</div>
|
||||
|
||||
<div v-if="service.type.match(/^(tcp|http)$/)" class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Use TLS Certificate</label>
|
||||
<div class="col-8 mt-1">
|
||||
<label class="col-12 col-md-4 col-form-label">{{ $t('tls_cert') }}</label>
|
||||
<div class="col-12 col-md-8 mt-1 mb-2 mb-md-0">
|
||||
<span @click="use_tls = !!use_tls" class="switch float-left">
|
||||
<input v-model="use_tls" type="checkbox" name="verify_ssl-option" class="switch" id="switch-use-tls" v-bind:checked="use_tls">
|
||||
<label for="switch-use-tls" v-if="use_tls">Custom TLS Certificates for mTLS services</label>
|
||||
|
@ -202,13 +205,13 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card contain-card text-black-50 bg-white mb-4">
|
||||
<div class="card-header">Notification Options</div>
|
||||
<div class="card contain-card mb-4">
|
||||
<div class="card-header">{{ $t('notification_opts') }}</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Enable Notifications</label>
|
||||
<div class="col-8 mt-1">
|
||||
<label class="col-sm-4 col-form-label">{{ $t('notifications_enable') }}</label>
|
||||
<div class="col-12 col-md-8 mt-1 mb-2 mb-md-0">
|
||||
<span @click="service.allow_notifications = !!service.allow_notifications" class="switch float-left">
|
||||
<input v-model="service.allow_notifications" type="checkbox" name="allow_notifications-option" class="switch" id="switch-notifications" v-bind:checked="service.allow_notifications">
|
||||
<label for="switch-notifications">Allow notifications to be sent for this service</label>
|
||||
|
@ -216,7 +219,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<div v-if="service.allow_notifications" class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Notify After Failures</label>
|
||||
<label class="col-sm-4 col-form-label">{{ $t('notify_after') }}</label>
|
||||
<div class="col-sm-8">
|
||||
<span class="slider-info">{{service.notify_after === 0 ? "First Failure" : service.notify_after+' Failures'}}</span>
|
||||
<input v-model="service.notify_after" type="range" name="notify_after" class="slider" id="notify_after" min="0" max="20">
|
||||
|
@ -224,8 +227,8 @@
|
|||
</div>
|
||||
</div>
|
||||
<div v-if="service.allow_notifications" class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Notify All Changes</label>
|
||||
<div class="col-8 mt-1">
|
||||
<label class="col-sm-4 col-form-label">{{ $t('notify_all') }}</label>
|
||||
<div class="col-12 col-md-8 mt-1">
|
||||
<span @click="service.notify_all_changes = !!service.notify_all_changes" class="switch float-left">
|
||||
<input v-model="service.notify_all_changes" type="checkbox" name="notify_all-option" class="switch" id="notify_all" v-bind:checked="service.notify_all_changes">
|
||||
<label v-if="service.notify_all_changes" for="notify_all">Continuously send notifications when service is failing.</label>
|
||||
|
@ -240,7 +243,7 @@
|
|||
<div class="form-group row">
|
||||
<div class="col-12">
|
||||
<button :disabled="loading" @click.prevent="saveService" type="submit" class="btn btn-success btn-block">
|
||||
{{service.id ? "Update Service" : "Create Service"}}
|
||||
{{service.id ? $t('service_update') : $t('service_create')}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,25 +1,29 @@
|
|||
<template>
|
||||
<div class="container col-md-7 col-sm-12 mt-2 sm-container">
|
||||
<div class="col-12 col-md-8 offset-md-2 mb-4">
|
||||
<img alt="Statping Setup" class="col-12 mt-5 mt-md-0" style="max-width:680px" src="banner.png">
|
||||
<div class="col-12 col-md-6 offset-md-3 mb-4">
|
||||
<img alt="Statping Setup" class="img-fluid mt-5 mt-md-0" src="banner.png">
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<form @submit.prevent="saveSetup">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="col-12 col-md-6">
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize">{{ $t('setup.language') }}</label>
|
||||
<label class="text-capitalize">{{ $t('language') }}</label>
|
||||
<select @change="changeLanguages" v-model="setup.language" id="language" class="form-control">
|
||||
<option value="en">English</option>
|
||||
<option value="es">Spanish</option>
|
||||
<option value="fr">French</option>
|
||||
<option value="ru">Russian</option>
|
||||
<option value="de">German</option>
|
||||
<option value="ja">Japanese</option>
|
||||
<option value="ko">Korean</option>
|
||||
<option value="it">Italian</option>
|
||||
<option value="zh">Chinese</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize">{{ $t('setup.connection') }}</label>
|
||||
<label class="text-capitalize">{{ $t('db_connection') }}</label>
|
||||
<select @change="canSubmit" v-model="setup.db_connection" id="db_connection" class="form-control">
|
||||
<option value="sqlite">SQLite</option>
|
||||
<option value="postgres">Postgres</option>
|
||||
|
@ -27,36 +31,36 @@
|
|||
</select>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="col-7 col-md-6">
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<label class="text-capitalize">{{ $t('setup.host') }}</label>
|
||||
<label class="text-capitalize">{{ $t('db_host') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.db_host" id="db_host" type="text" class="form-control" placeholder="localhost">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<div class="col-5 col-md-6">
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<label class="text-capitalize">{{ $t('port') }}</label>
|
||||
<label class="text-capitalize">{{ $t('db_port') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.db_port" id="db_port" type="number" class="form-control" placeholder="5432">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<label class="text-capitalize">{{ $t('username') }}</label>
|
||||
<label class="text-capitalize">{{ $t('db_username') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.db_user" id="db_user" type="text" class="form-control" placeholder="root">
|
||||
</div>
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<label for="db_password" class="text-capitalize">{{ $t('password') }}</label>
|
||||
<label for="db_password" class="text-capitalize">{{ $t('db_password') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.db_password" id="db_password" type="password" class="form-control" placeholder="password123">
|
||||
</div>
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<label for="db_database" class="text-capitalize">{{ $t('setup.database') }}</label>
|
||||
<label for="db_database" class="text-capitalize">{{ $t('db_database') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.db_database" id="db_database" type="text" class="form-control" placeholder="Database name">
|
||||
</div>
|
||||
|
||||
<div class="form-group mt-3">
|
||||
<div class="row">
|
||||
<div class="col-9">
|
||||
<span class="text-left text-capitalize">{{ $t('setup.send_reports') }}</span>
|
||||
<span class="text-left text-capitalize">{{ $t('send_reports') }}</span>
|
||||
</div>
|
||||
<div class="col-3 text-right">
|
||||
<span @click="setup.send_reports = !!setup.send_reports" class="switch">
|
||||
|
@ -65,40 +69,39 @@
|
|||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-6">
|
||||
<div class="col-12 col-md-6">
|
||||
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize">{{ $t('setup.project_name') }}</label>
|
||||
<label class="text-capitalize">{{ $t('project_name') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.project" id="project" type="text" class="form-control" placeholder="Work Servers" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize">{{ $t('setup.project_description') }}</label>
|
||||
<label class="text-capitalize">{{ $t('description') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.description" id="description" type="text" class="form-control" placeholder="Monitors all of my work services">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize" for="domain">{{ $t('setup.domain') }}</label>
|
||||
<label class="text-capitalize" for="domain">{{ $t('domain') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.domain" type="text" class="form-control" id="domain" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize">{{ $t('setup.username') }}</label>
|
||||
<label class="text-capitalize">{{ $t('username') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.username" id="username" type="text" class="form-control" placeholder="admin" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize">{{ $t('setup.password') }}</label>
|
||||
<label class="text-capitalize">{{ $t('password') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.password" id="password" type="password" class="form-control" placeholder="password" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize">{{ $t('setup.password_confirm') }}</label>
|
||||
<label class="text-capitalize">{{ $t('confirm_password') }}</label>
|
||||
<input @keyup="canSubmit" v-model="setup.confirm_password" id="password_confirm" type="password" class="form-control" placeholder="password" required>
|
||||
<span v-if="passnomatch" class="small text-danger">Both passwords should match</span>
|
||||
</div>
|
||||
|
@ -110,14 +113,14 @@
|
|||
<input @keyup="canSubmit" v-model="setup.email" id="email" type="text" class="form-control" placeholder="myemail@domain.com">
|
||||
</div>
|
||||
<div class="col-4 text-right">
|
||||
<label class="d-none d-sm-block text-capitalize text-capitalize">{{ $t('setup.newsletter') }}</label>
|
||||
<label class="d-none d-sm-block text-capitalize text-capitalize">{{ $t('newsletter') }}</label>
|
||||
<span @click="setup.newsletter = !!setup.newsletter" class="switch">
|
||||
<input v-model="setup.newsletter" type="checkbox" name="send_newsletter" class="switch" id="send_newsletter" :checked="setup.newsletter">
|
||||
<label for="send_newsletter"></label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<small>{{ $t('setup.newsletter_note') }}</small>
|
||||
<small>{{ $t('newsletter_note') }}</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -125,10 +128,12 @@
|
|||
{{error}}
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<button @click.prevent="saveSetup" v-bind:disabled="disabled || loading" type="submit" class="btn btn-primary btn-block" :class="{'btn-primary': !loading, 'btn-default': loading}">
|
||||
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/>{{loading ? "Loading..." : "Save Settings"}}
|
||||
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/>{{loading ? $t('loading') : $t('save_settings')}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
@ -137,7 +142,6 @@
|
|||
|
||||
<script>
|
||||
import Api from "../API";
|
||||
import Index from "../pages/Index";
|
||||
|
||||
export default {
|
||||
name: 'Setup',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="card contain-card text-black-50 bg-white mb-3">
|
||||
<div class="card-header"> {{user.id ? `Update ${user.username}` : "Create User"}}
|
||||
<div class="card contain-card mb-3">
|
||||
<div class="card-header"> {{user.id ? `${$t('update')} ${user.username}` : $t('user_create')}}
|
||||
<transition name="slide-fade">
|
||||
<button @click.prevent="removeEdit" v-if="user.id" class="btn btn-sm float-right btn-danger btn-sm">Close</button>
|
||||
</transition>
|
||||
|
@ -8,31 +8,31 @@
|
|||
<div class="card-body">
|
||||
<form @submit="saveUser">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Username</label>
|
||||
<label class="col-sm-4 col-form-label">{{$t('username')}}</label>
|
||||
<div class="col-6 col-md-4">
|
||||
<input v-model="user.username" type="text" class="form-control" id="username" placeholder="Username" required autocorrect="off" autocapitalize="none" v-bind:readonly="user.id">
|
||||
</div>
|
||||
<div class="col-6 col-md-4">
|
||||
<span id="admin_switch" @click="user.admin = !!user.admin" class="switch">
|
||||
<input v-model="user.admin" type="checkbox" class="switch" id="user_admin_switch" v-bind:checked="user.admin">
|
||||
<label for="user_admin_switch">Administrator</label>
|
||||
<label for="user_admin_switch">{{$t('administrator')}}</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="email" class="col-sm-4 col-form-label">Email Address</label>
|
||||
<label for="email" class="col-sm-4 col-form-label">{{$t('email')}}</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="user.email" type="email" class="form-control" id="email" placeholder="user@domain.com" required autocapitalize="none" spellcheck="false">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Password</label>
|
||||
<label class="col-sm-4 col-form-label">{{$t('password')}}</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="user.password" type="password" id="password" class="form-control" placeholder="Password" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Confirm Password</label>
|
||||
<label class="col-sm-4 col-form-label">{{$t('confirm_password')}}</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="user.confirm_password" type="password" id="password_confirm" class="form-control" placeholder="Confirm Password" required>
|
||||
</div>
|
||||
|
@ -54,7 +54,7 @@
|
|||
class="btn-primary"
|
||||
:disabled="loading || !user.username || !user.email || !user.password || !user.confirm_password || (user.password !== user.confirm_password)"
|
||||
:action="saveUser"
|
||||
:label="user.id ? 'Update User' : 'Create User'"
|
||||
:label="user.id ? $t('user_update'): $t('user_create')"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -66,7 +66,7 @@
|
|||
|
||||
<script>
|
||||
import Api from "../API";
|
||||
import LoadButton from "@/components/Elements/LoadButton";
|
||||
const LoadButton = () => import(/* webpackChunkName: "index" */ "@/components/Elements/LoadButton");
|
||||
|
||||
export default {
|
||||
name: 'FormUser',
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import {library} from '@fortawesome/fontawesome-svg-core'
|
||||
import {fas} from '@fortawesome/fontawesome-free-solid';
|
||||
import {fab} from '@fortawesome/free-brands-svg-icons';
|
||||
import {far} from '@fortawesome/fontawesome-svg-core';
|
||||
import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome'
|
||||
import Vue from "vue";
|
||||
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
const chinese = {
|
||||
settings: "设置",
|
||||
dashboard: "仪表板",
|
||||
services: "服务",
|
||||
service: "服务",
|
||||
failures: "失败",
|
||||
users: "用户",
|
||||
login: "登录",
|
||||
logout: "注销",
|
||||
online: "在线",
|
||||
offline: "离线",
|
||||
configs: "配置",
|
||||
username: "用户名",
|
||||
password: "密码",
|
||||
email: "电子邮件",
|
||||
confirm_password: "确认密码",
|
||||
uptime: "正常运行时间",
|
||||
name: "名称",
|
||||
copy: "复制",
|
||||
close: "关",
|
||||
secret: "秘密",
|
||||
regen_api: "重新生成 API 密钥",
|
||||
regen_desc: "API 密码用于读取创建更新和删除路由。如果需要,您可以重新生成 API 密钥。",
|
||||
visibility: "可见性",
|
||||
group: "小组",
|
||||
group_create: "创建组",
|
||||
group_update: "更新组",
|
||||
group_public_desc: "向公众显示群组服务",
|
||||
groups: "组",
|
||||
no_group: "无组",
|
||||
public: "公共",
|
||||
private: "私人",
|
||||
announcements: "公告",
|
||||
notifiers: "通告程序",
|
||||
logs: "日志",
|
||||
help: "帮助",
|
||||
type: "类型",
|
||||
edit: "编辑",
|
||||
update: "更新",
|
||||
create: "创建",
|
||||
view: "查看",
|
||||
save: "保存",
|
||||
title: "标题",
|
||||
status: "状态",
|
||||
begins: "开始",
|
||||
total_services: "服务总数",
|
||||
online_services: "在线服务",
|
||||
request_timeout: "请求超时",
|
||||
service_never_online: "服务从未在线",
|
||||
service_online_check: "在线检查",
|
||||
service_offline_time: "服务已脱机",
|
||||
days_ago: "天前",
|
||||
today: "今天",
|
||||
week: "周",
|
||||
month: "月份",
|
||||
day: "日",
|
||||
hour: "小时",
|
||||
minute: "分钟",
|
||||
failures_24_hours: "过去 24 小时失败",
|
||||
no_services: "您目前没有任何服务!",
|
||||
theme: "主题",
|
||||
cache: "高速缓存",
|
||||
authentication: "身份验证",
|
||||
import: "导入",
|
||||
main_settings: "主要设置",
|
||||
variables: "变量",
|
||||
docs: "文档",
|
||||
links: "链接",
|
||||
changelog: "更改日志",
|
||||
repo: "存储库",
|
||||
language: "语言",
|
||||
db_connection: "数据库连接",
|
||||
db_host: "数据库主机",
|
||||
db_port: "数据库端口",
|
||||
db_username: "数据库用户名",
|
||||
db_password: "数据库口令",
|
||||
db_database: "数据库名称",
|
||||
send_reports: "发送错误报告",
|
||||
send_reports_desc: "将错误发送到 “状态” 以进行调试",
|
||||
project_name: "状态页面名称",
|
||||
description: "说明",
|
||||
domain: "域",
|
||||
enable_cdn: "启用 CDN",
|
||||
newsletter: "新闻通讯",
|
||||
newsletter_note: "我们只会向您发送重大变更的电子邮件",
|
||||
loading: "正在加载",
|
||||
save_settings: "保存设置",
|
||||
average_response: "平均响应",
|
||||
last_uptime: "上次正常运行时间",
|
||||
sign_in: "登录",
|
||||
last_login: "上次登录",
|
||||
admin: "管理员",
|
||||
user: "用户",
|
||||
failed: "失败",
|
||||
wrong_login: "用户名或密码不正确",
|
||||
theme_editor: "主题编辑器",
|
||||
enable_assets: "启用本地资产",
|
||||
assets_desc: "通过启用本地资产自定义状态页面设计。这将创建一个包含所有 CSS 的 “资产” 目录。",
|
||||
assets_btn: "启用本地资产",
|
||||
assets_loading: "创建资产",
|
||||
assets_dir: "资产目录",
|
||||
footer: "页脚",
|
||||
footer_notes: "您可以在页脚中使用 HTML 标签",
|
||||
global_announcement: "全球公告",
|
||||
announcement_date: "公告日期范围",
|
||||
notify_users: "通知用户",
|
||||
notify_desc: "在计划时间之前通知用户",
|
||||
notify_method: "通知方法",
|
||||
notify_before: "之前通知",
|
||||
message_create: "创建公告",
|
||||
message_edit: "编辑公告",
|
||||
minutes: "分钟",
|
||||
hours: "小时数",
|
||||
days: "天数",
|
||||
user_create: "创建用户",
|
||||
user_update: "更新用户",
|
||||
administrator: "管理员",
|
||||
checkins: "车臣金斯",
|
||||
incidents: "事件",
|
||||
service_info: "服务信息",
|
||||
service_name: "服务名称",
|
||||
service_type: "服务类型",
|
||||
permalink: "永久链接 URL",
|
||||
service_public: "公共服务",
|
||||
check_interval: "检查间隔",
|
||||
service_endpoint: "服务终端节点",
|
||||
service_check: "服务检查类型",
|
||||
service_timeout: "请求超时",
|
||||
expected_resp: "预期响应",
|
||||
expected_code: "预期状态代码",
|
||||
follow_redir: "跟随重定向",
|
||||
verify_ssl: "验证 SSL",
|
||||
tls_cert: "使用 TLS 证书",
|
||||
notification_opts: "通知选项",
|
||||
notifications_enable: "启用通知",
|
||||
notify_after: "故障后通知",
|
||||
notify_all: "通知所有更改",
|
||||
service_update: "更新服务",
|
||||
service_create: "创建服务"
|
||||
}
|
||||
|
||||
export default chinese
|
|
@ -0,0 +1,139 @@
|
|||
key,en
|
||||
settings,Settings
|
||||
dashboard,Dashboard
|
||||
services,Services
|
||||
service,Service
|
||||
failures,Failures
|
||||
users,Users
|
||||
login,Login
|
||||
logout,Logout
|
||||
online,Online
|
||||
offline,Offline
|
||||
configs,Configuration
|
||||
username,Username
|
||||
password,Password
|
||||
email,Email
|
||||
confirm_password,Confirm Password
|
||||
uptime,Uptime
|
||||
name,Name
|
||||
copy,Copy
|
||||
close,Close
|
||||
secret,Secret
|
||||
regen_api,Regenerate API Keys
|
||||
regen_desc,API Secret is used for read create update and delete routes. You can Regenerate API Keys if you need to.
|
||||
visibility,Visibility
|
||||
group,Group
|
||||
group_create,Create Group
|
||||
group_update,Update Group
|
||||
group_public_desc,Show group services to the public
|
||||
groups,Groups
|
||||
no_group,No Group
|
||||
public,Public
|
||||
private,Private
|
||||
announcements,Announcements
|
||||
notifiers,Notifiers
|
||||
logs,Logs
|
||||
help,Help
|
||||
type,Type
|
||||
edit,Edit
|
||||
update,Update
|
||||
create,Create
|
||||
view,View
|
||||
save,Save
|
||||
title,Title
|
||||
status,Status
|
||||
begins,Begins
|
||||
total_services,Total Services
|
||||
online_services,Online Services
|
||||
request_timeout,Request Timeout
|
||||
service_never_online,Service has never been online
|
||||
service_online_check,Online checked
|
||||
service_offline_time,Service has been offline for
|
||||
days_ago,Days Ago
|
||||
today,Today
|
||||
week,Week
|
||||
month,Month
|
||||
day,Day
|
||||
hour,Hour
|
||||
minute,Minute
|
||||
failures_24_hours,Failures last 24 hours
|
||||
no_services,You currently don't have any services!
|
||||
theme,Theme
|
||||
cache,Cache
|
||||
authentication,Authentication
|
||||
import,Import
|
||||
main_settings,Main Settings
|
||||
variables,Variables
|
||||
docs,Documentation
|
||||
links,Links
|
||||
changelog,Change Log
|
||||
repo,Repository
|
||||
language,Language
|
||||
db_connection,Database Connection
|
||||
db_host,Database Host
|
||||
db_port,Database Port
|
||||
db_username,Database Username
|
||||
db_password,Database Password
|
||||
db_database,Database Name
|
||||
send_reports,Send Error Reports
|
||||
send_reports_desc,Send errors to Statping for debugging
|
||||
project_name,Status Page Name
|
||||
description,Description
|
||||
domain,Domain
|
||||
enable_cdn,Enable CDN
|
||||
newsletter,Newsletter
|
||||
newsletter_note,We will only send you an email for major changes
|
||||
loading,Loading
|
||||
save_settings,Save Settings
|
||||
average_response,Average Response
|
||||
last_uptime,Uptime last
|
||||
sign_in,Sign In
|
||||
last_login,Last Login
|
||||
admin,Admin
|
||||
user,User
|
||||
failed,Failed
|
||||
wrong_login,Incorrect username or password
|
||||
theme_editor,Theme Editor
|
||||
enable_assets,Enable Local Assets
|
||||
assets_desc,Customize your status page design by enabling local assets. This will create a 'assets' directory containing all CSS.
|
||||
assets_btn,Enable Local Assets
|
||||
assets_loading,Creating Assets
|
||||
assets_dir,Assets Directory
|
||||
footer,Footer
|
||||
footer_notes,You can use HTML tags in footer
|
||||
global_announcement,Global Announcement
|
||||
announcement_date,Announcement Date Range
|
||||
notify_users,Notify Users
|
||||
notify_desc,Notify Users Before Scheduled Time
|
||||
notify_method,Notification Method
|
||||
notify_before,Notify Before
|
||||
message_create,Create Announcement
|
||||
message_edit,Edit Announcement
|
||||
minutes,Minutes
|
||||
hours,Hours
|
||||
days,Days
|
||||
user_create,Create User
|
||||
user_update,Update User
|
||||
administrator,Administrator
|
||||
checkins,Checkins
|
||||
incidents,Incidents
|
||||
service_info,Service Info
|
||||
service_name,Service Name
|
||||
service_type,Service Type
|
||||
permalink,Permalink URL
|
||||
service_public,Public Service
|
||||
check_interval,Check Interval
|
||||
service_endpoint,Service Endpoint
|
||||
service_check,Service Check Type
|
||||
service_timeout,Request Timeout
|
||||
expected_resp,Expected Response
|
||||
expected_code,Expected Status Code
|
||||
follow_redir,Follow Redirects
|
||||
verify_ssl,Verify SSL
|
||||
tls_cert,Use TLS Cert
|
||||
notification_opts,Notification Options
|
||||
notifications_enable,Enable Notifications
|
||||
notify_after,Notify After Failures
|
||||
notify_all,Notify All Changes
|
||||
service_update,Update Service
|
||||
service_create,Create Service
|
|
|
@ -1,100 +1,142 @@
|
|||
const english = {
|
||||
top_nav: {
|
||||
settings: "Settings",
|
||||
dashboard: "Dashboard",
|
||||
services: "Services",
|
||||
service: "Service",
|
||||
failures: "Failures",
|
||||
users: "Users",
|
||||
groups: "Groups",
|
||||
announcements: "Announcements",
|
||||
settings: "Settings",
|
||||
logs: "Logs",
|
||||
logout: 'Logout',
|
||||
},
|
||||
setup: {
|
||||
language: "Language",
|
||||
connection: "Database Connection",
|
||||
host: "Host",
|
||||
database: "Database",
|
||||
project_name: "Project Name",
|
||||
project_description: "Project Description",
|
||||
domain: "Domain URL",
|
||||
username: "Admin Username",
|
||||
password: "Admin Password",
|
||||
password_confirm: "Confirm Admin Password",
|
||||
newsletter: "Newsletter",
|
||||
newsletter_note: "We will not share your email, emails are only for major updates.",
|
||||
send_reports: "Send Error Reports to Statping"
|
||||
},
|
||||
dashboard: {
|
||||
total_services: "Total Services",
|
||||
failures_24_hours: "Failures last 24 Hours",
|
||||
online_services: "Online Services",
|
||||
service: 'Service | Services',
|
||||
group: 'Group',
|
||||
title: 'Title',
|
||||
begins: 'Begins',
|
||||
name: 'Name',
|
||||
loading: 'Loading',
|
||||
login: 'Login',
|
||||
sign_in: "Sign In",
|
||||
visibility: 'Visibility',
|
||||
wrong_login: 'Incorrect username or password'
|
||||
},
|
||||
settings: {
|
||||
name: "Project Name",
|
||||
description: "Project Name",
|
||||
footer: "Custom Footer",
|
||||
footer_notes: "HTML is allowed inside the footer",
|
||||
error_reporting: "Enable Error Reporting",
|
||||
error_reporting_notes: "Help the Statping project out by sending anonymous error logs back to our server.",
|
||||
save: "Save Settings",
|
||||
main: "Main Settings",
|
||||
theme: "Theme Editor",
|
||||
cache: "Cache",
|
||||
oauth: "OAuth",
|
||||
beta: "BETA",
|
||||
changelog: "Changelog",
|
||||
repo: "Statping Github Repo",
|
||||
docs: "Documentation",
|
||||
},
|
||||
service: {
|
||||
name: "Service Name",
|
||||
type: "Service Type",
|
||||
info: "Service Information",
|
||||
view: "View Service",
|
||||
average: "Average Response",
|
||||
last_uptime: "Uptime last {0} {1}",
|
||||
},
|
||||
email: "Email Address",
|
||||
port: "Database Port",
|
||||
setting: "Settings",
|
||||
login: "Login",
|
||||
logout: "Logout",
|
||||
online: "Online",
|
||||
offline: "Offline",
|
||||
configs: "Configuration",
|
||||
username: "Username",
|
||||
password: 'Password',
|
||||
services: 'Services',
|
||||
domain: 'Domain',
|
||||
online: 'online',
|
||||
public: 'Public',
|
||||
private: 'Private',
|
||||
admin: 'Admin',
|
||||
offline: 'offline',
|
||||
failure: 'failure | failures',
|
||||
incident: 'incident | incidents',
|
||||
checkin: 'checkin | checkins',
|
||||
user: 'User | Users',
|
||||
group: 'Group',
|
||||
message: 'message',
|
||||
edit: 'Edit',
|
||||
type: 'Type',
|
||||
sample_data: 'Sample Data',
|
||||
today: 'Today',
|
||||
last_login: 'Last Login',
|
||||
uptime: '{0}% Uptime',
|
||||
close: 'Close',
|
||||
second: 'second | seconds',
|
||||
minute: 'minute | minutes',
|
||||
hour: 'hour | hours',
|
||||
day: 'day | days',
|
||||
week: 'week | weeks',
|
||||
month: 'month | months',
|
||||
password: "Password",
|
||||
email: "Email",
|
||||
confirm_password: "Confirm Password",
|
||||
uptime: "Uptime",
|
||||
name: "Name",
|
||||
copy: "Copy",
|
||||
close: "Close",
|
||||
secret: "Secret",
|
||||
regen_api: "Regenerate API Keys",
|
||||
regen_desc: "API Secret is used for read create update and delete routes. You can Regenerate API Keys if you need to.",
|
||||
visibility: "Visibility",
|
||||
group: "Group",
|
||||
group_create: "Create Group",
|
||||
group_update: "Update Group",
|
||||
group_public_desc: "Show group services to the public",
|
||||
groups: "Groups",
|
||||
no_group: "No Group",
|
||||
public: "Public",
|
||||
private: "Private",
|
||||
announcements: "Announcements",
|
||||
notifiers: "Notifiers",
|
||||
logs: "Logs",
|
||||
help: "Help",
|
||||
type: "Type",
|
||||
edit: "Edit",
|
||||
update: "Update",
|
||||
create: "Create",
|
||||
view: "View",
|
||||
save: "Save",
|
||||
title: "Title",
|
||||
status: "Status",
|
||||
begins: "Begins",
|
||||
total_services: "Total Services",
|
||||
online_services: "Online Services",
|
||||
request_timeout: "Request Timeout",
|
||||
service_never_online: "Service has never been online",
|
||||
service_online_check: "Online checked",
|
||||
service_offline_time: "Service has been offline for",
|
||||
days_ago: "Days Ago",
|
||||
today: "Today",
|
||||
week: "Week",
|
||||
month: "Month",
|
||||
day: "Day",
|
||||
hour: "Hour",
|
||||
minute: "Minute",
|
||||
failures_24_hours: "Failures last 24 hours",
|
||||
no_services: "You currently don't have any services!",
|
||||
theme: "Theme",
|
||||
cache: "Cache",
|
||||
authentication: "Authentication",
|
||||
import: "Import",
|
||||
main_settings: "Main Settings",
|
||||
variables: "Variables",
|
||||
docs: "Documentation",
|
||||
links: "Links",
|
||||
changelog: "Change Log",
|
||||
repo: "Repository",
|
||||
language: "Language",
|
||||
db_connection: "Database Connection",
|
||||
db_host: "Database Host",
|
||||
db_port: "Database Port",
|
||||
db_username: "Database Username",
|
||||
db_password: "Database Password",
|
||||
db_database: "Database Name",
|
||||
send_reports: "Send Error Reports",
|
||||
send_reports_desc: "Send errors to Statping for debugging",
|
||||
project_name: "Status Page Name",
|
||||
description: "Description",
|
||||
domain: "Domain",
|
||||
enable_cdn: "Enable CDN",
|
||||
newsletter: "Newsletter",
|
||||
newsletter_note: "We will only send you an email for major changes",
|
||||
loading: "Loading",
|
||||
save_settings: "Save Settings",
|
||||
average_response: "Average Response",
|
||||
last_uptime: "Uptime last",
|
||||
sign_in: "Sign In",
|
||||
last_login: "Last Login",
|
||||
admin: "Admin",
|
||||
user: "User",
|
||||
failed: "Failed",
|
||||
wrong_login: "Incorrect username or password",
|
||||
theme_editor: "Theme Editor",
|
||||
enable_assets: "Enable Local Assets",
|
||||
assets_desc: "Customize your status page design by enabling local assets. This will create a 'assets' directory containing all CSS.",
|
||||
assets_btn: "Enable Local Assets",
|
||||
assets_loading: "Creating Assets",
|
||||
assets_dir: "Assets Directory",
|
||||
footer: "Footer",
|
||||
footer_notes: "You can use HTML tags in footer",
|
||||
global_announcement: "Global Announcement",
|
||||
announcement_date: "Announcement Date Range",
|
||||
notify_users: "Notify Users",
|
||||
notify_desc: "Notify Users Before Scheduled Time",
|
||||
notify_method: "Notification Method",
|
||||
notify_before: "Notify Before",
|
||||
message_create: "Create Announcement",
|
||||
message_edit: "Edit Announcement",
|
||||
minutes: "Minutes",
|
||||
hours: "Hours",
|
||||
days: "Days",
|
||||
user_create: "Create User",
|
||||
user_update: "Update User",
|
||||
administrator: "Administrator",
|
||||
checkins: "Checkins",
|
||||
incidents: "Incidents",
|
||||
service_info: "Service Info",
|
||||
service_name: "Service Name",
|
||||
service_type: "Service Type",
|
||||
permalink: "Permalink URL",
|
||||
service_public: "Public Service",
|
||||
check_interval: "Check Interval",
|
||||
service_endpoint: "Service Endpoint",
|
||||
service_check: "Service Check Type",
|
||||
service_timeout: "Request Timeout",
|
||||
expected_resp: "Expected Response",
|
||||
expected_code: "Expected Status Code",
|
||||
follow_redir: "Follow Redirects",
|
||||
verify_ssl: "Verify SSL",
|
||||
tls_cert: "Use TLS Cert",
|
||||
notification_opts: "Notification Options",
|
||||
notifications_enable: "Enable Notifications",
|
||||
notify_after: "Notify After Failures",
|
||||
notify_all: "Notify All Changes",
|
||||
service_update: "Update Service",
|
||||
service_create: "Create Service"
|
||||
}
|
||||
|
||||
export default english
|
|
@ -1,100 +1,142 @@
|
|||
const french = {
|
||||
top_nav: {
|
||||
dashboard: "Dashboard",
|
||||
settings: "Paramètres",
|
||||
dashboard: "Tableau de bord",
|
||||
services: "Services",
|
||||
users: "Users",
|
||||
groups: "Groups",
|
||||
announcements: "Announcements",
|
||||
settings: "Settings",
|
||||
logs: "Logs",
|
||||
logout: 'Logout',
|
||||
},
|
||||
setup: {
|
||||
language: "Language",
|
||||
connection: "Database Connection",
|
||||
host: "Host",
|
||||
database: "Database",
|
||||
project_name: "Project Name",
|
||||
project_description: "Project Description",
|
||||
domain: "Domain URL",
|
||||
username: "Admin Username",
|
||||
password: "Admin Password",
|
||||
password_confirm: "Confirm Admin Password",
|
||||
newsletter: "Newsletter",
|
||||
newsletter_note: "We will not share your email, emails are only for major updates.",
|
||||
send_reports: "Send Error Reports to Statping"
|
||||
},
|
||||
dashboard: {
|
||||
total_services: "Total Services",
|
||||
failures_24_hours: "Failures last 24 Hours",
|
||||
online_services: "Online Services",
|
||||
service: 'Service | Services',
|
||||
group: 'Group',
|
||||
title: 'Title',
|
||||
begins: 'Begins',
|
||||
name: 'Name',
|
||||
loading: 'Loading',
|
||||
login: 'Login',
|
||||
sign_in: "Sign In",
|
||||
visibility: 'Visibility',
|
||||
wrong_login: 'Incorrect username or password'
|
||||
},
|
||||
settings: {
|
||||
name: "Project Name",
|
||||
description: "Project Name",
|
||||
footer: "Custom Footer",
|
||||
footer_notes: "HTML is allowed inside the footer",
|
||||
error_reporting: "Enable Error Reporting",
|
||||
error_reporting_notes: "Help the Statping project out by sending anonymous error logs back to our server.",
|
||||
save: "Save Settings",
|
||||
main: "Main Settings",
|
||||
theme: "Theme Editor",
|
||||
service: "Service",
|
||||
failures: "Échecs",
|
||||
users: "Utilisateurs",
|
||||
login: "Identifiant",
|
||||
logout: "Déconnexion",
|
||||
online: "En ligne",
|
||||
offline: "Offline",
|
||||
configs: "Configuration",
|
||||
username: "Nom d'utilisateur",
|
||||
password: "mot de passe",
|
||||
email: "Email",
|
||||
confirm_password: "Confirmer le",
|
||||
uptime: "Temps de disponibilité",
|
||||
name: "Nom",
|
||||
copy: "Copie",
|
||||
close: "Fermer",
|
||||
secret: "Secret",
|
||||
regen_api: "Régénérer les clés d'API",
|
||||
regen_desc: "API Secret est utilisé pour lire créer des routes de mise à jour et de suppression. Vous pouvez Régénérer les clés d'API si vous en avez besoin.",
|
||||
visibility: "Visibilité",
|
||||
group: "Groupe",
|
||||
group_create: "Créer un groupe",
|
||||
group_update: "Groupe Mettre à jour",
|
||||
group_public_desc: "Afficher les services de groupe au public",
|
||||
groups: "Groupes",
|
||||
no_group: "Pas de groupe",
|
||||
public: "Publique",
|
||||
private: "Privé",
|
||||
announcements: "Annonces",
|
||||
notifiers: "Notifiants",
|
||||
logs: "Journaux",
|
||||
help: "Aider",
|
||||
type: "Type",
|
||||
edit: "Éditer",
|
||||
update: "Mise à jour",
|
||||
create: "Créer",
|
||||
view: "Voir",
|
||||
save: "Sauvegarder",
|
||||
title: "Titre",
|
||||
status: "Statut",
|
||||
begins: "Commence",
|
||||
total_services: "Total des services",
|
||||
online_services: "Services en ligne",
|
||||
request_timeout: "Délai d'attente de",
|
||||
service_never_online: "Le service n'a jamais été en ligne",
|
||||
service_online_check: "Vérifié en ligne",
|
||||
service_offline_time: "Le service a été hors ligne pour",
|
||||
days_ago: "Il y a jours",
|
||||
today: "Aujourd'hui",
|
||||
week: "Semaine",
|
||||
month: "Mois",
|
||||
day: "Jour",
|
||||
hour: "Heure",
|
||||
minute: "Minute",
|
||||
failures_24_hours: "Les échecs durent 24 heures",
|
||||
no_services: "Vous n'avez actuellement aucun service !",
|
||||
theme: "Thème",
|
||||
cache: "Cache",
|
||||
oauth: "OAuth",
|
||||
beta: "BETA",
|
||||
changelog: "Changelog",
|
||||
repo: "Statping Github Repo",
|
||||
authentication: "Authentification",
|
||||
import: "Importer",
|
||||
main_settings: "Paramètres principaux",
|
||||
variables: "Variables",
|
||||
docs: "Documentation",
|
||||
},
|
||||
service: {
|
||||
name: "Service Name",
|
||||
type: "Service Type",
|
||||
info: "Service Information",
|
||||
view: "View Service",
|
||||
average: "Average Response",
|
||||
last_uptime: "Uptime last {0} {1}",
|
||||
},
|
||||
email: "Email Address",
|
||||
port: "Database Port",
|
||||
setting: "Settings",
|
||||
username: "Username",
|
||||
password: 'Password',
|
||||
services: 'Services',
|
||||
domain: 'Domain',
|
||||
online: 'online',
|
||||
public: 'Public',
|
||||
private: 'Private',
|
||||
admin: 'Admin',
|
||||
offline: 'offline',
|
||||
failure: 'failure | failures',
|
||||
incident: 'incident | incidents',
|
||||
checkin: 'checkin | checkins',
|
||||
user: 'User | Users',
|
||||
group: 'Group',
|
||||
message: 'message',
|
||||
edit: 'Edit',
|
||||
type: 'Type',
|
||||
sample_data: 'Sample Data',
|
||||
today: 'Today',
|
||||
last_login: 'Last Login',
|
||||
uptime: '{0}% Uptime',
|
||||
close: 'Close',
|
||||
second: 'second | seconds',
|
||||
minute: 'minute | minutes',
|
||||
hour: 'hour | hours',
|
||||
day: 'day | days',
|
||||
week: 'week | weeks',
|
||||
month: 'month | months',
|
||||
links: "Links",
|
||||
changelog: "Journal des modifications",
|
||||
repo: "Référentiel",
|
||||
language: "Langue",
|
||||
db_connection: "Connexion à la base",
|
||||
db_host: "Hôte de base",
|
||||
db_port: "Port base de données",
|
||||
db_username: "Nom utilisateur de base",
|
||||
db_password: "Mot de passe",
|
||||
db_database: "Nom de la base",
|
||||
send_reports: "Envoyer des rapports d'erreurs",
|
||||
send_reports_desc: "Envoyer des erreurs à Statping pour le débogage",
|
||||
project_name: "Nom de la page d'état",
|
||||
description: "Description",
|
||||
domain: "Domaine",
|
||||
enable_cdn: "Activer le CDN",
|
||||
newsletter: "Bulletin",
|
||||
newsletter_note: "Nous ne vous enverrons qu'un e-mail pour les modifications majeures",
|
||||
loading: "Chargement",
|
||||
save_settings: "Enregistrer les paramètres",
|
||||
average_response: "Réponse moyenne",
|
||||
last_uptime: "Temps de disponibilité dernier",
|
||||
sign_in: "Se connecter",
|
||||
last_login: "Dernière connexion",
|
||||
admin: "Admin",
|
||||
user: "Utilisateur",
|
||||
failed: "Échec",
|
||||
wrong_login: "Nom d'utilisateur ou mot de passe",
|
||||
theme_editor: "Editeur de thème",
|
||||
enable_assets: "Activer les actifs locaux",
|
||||
assets_desc: "Personnalisez la conception de votre page d'état en activant les ressources locales. Cela va créer un répertoire 'assets' contenant tous les CSS.",
|
||||
assets_btn: "Activer les actifs locaux",
|
||||
assets_loading: "Création d'actifs",
|
||||
assets_dir: "Répertoire des actifs",
|
||||
footer: "Pied de page",
|
||||
footer_notes: "Vous pouvez utiliser des balises HTML dans le pied de page",
|
||||
global_announcement: "Annonce mondiale",
|
||||
announcement_date: "Fourchette de dates d'annonce",
|
||||
notify_users: "Aviser les utilisateurs",
|
||||
notify_desc: "Avertir les utilisateurs avant l'heure prévue",
|
||||
notify_method: "Méthode de notification",
|
||||
notify_before: "Avertir avant",
|
||||
message_create: "Créer une annonce",
|
||||
message_edit: "Modifier l'annonce",
|
||||
minutes: "Procès-Verbal",
|
||||
hours: "Heures",
|
||||
days: "Jours",
|
||||
user_create: "Créer un utilisateur",
|
||||
user_update: "Mise à jour utilisateur",
|
||||
administrator: "Administrateur",
|
||||
checkins: "Checkins",
|
||||
incidents: "Incidents",
|
||||
service_info: "Informations sur le service",
|
||||
service_name: "Nom du service",
|
||||
service_type: "Type de service",
|
||||
permalink: "URL Permalien",
|
||||
service_public: "Fonction publique",
|
||||
check_interval: "Période de vérification",
|
||||
service_endpoint: "Point de terminaison de service",
|
||||
service_check: "Type de vérification de service",
|
||||
service_timeout: "Délai d'attente de",
|
||||
expected_resp: "Réponse attendue",
|
||||
expected_code: "Code d'état attendu",
|
||||
follow_redir: "Suivre les redirections",
|
||||
verify_ssl: "Vérifier SSL",
|
||||
tls_cert: "Utiliser le certificat TLS",
|
||||
notification_opts: "Options de notification",
|
||||
notifications_enable: "Activer les notifications",
|
||||
notify_after: "Notification après les échecs",
|
||||
notify_all: "Notifier toutes les modifications",
|
||||
service_update: "Mise à jour Service",
|
||||
service_create: "Créer un service"
|
||||
}
|
||||
|
||||
export default french
|
|
@ -1,100 +1,142 @@
|
|||
const german = {
|
||||
top_nav: {
|
||||
dashboard: "Dashboard",
|
||||
services: "Services",
|
||||
users: "Users",
|
||||
groups: "Groups",
|
||||
announcements: "Announcements",
|
||||
settings: "Settings",
|
||||
logs: "Logs",
|
||||
logout: 'Logout',
|
||||
},
|
||||
setup: {
|
||||
language: "Language",
|
||||
connection: "Database Connection",
|
||||
host: "Host",
|
||||
database: "Database",
|
||||
project_name: "Project Name",
|
||||
project_description: "Project Description",
|
||||
domain: "Domain URL",
|
||||
username: "Admin Username",
|
||||
password: "Admin Password",
|
||||
password_confirm: "Confirm Admin Password",
|
||||
newsletter: "Newsletter",
|
||||
newsletter_note: "We will not share your email, emails are only for major updates.",
|
||||
send_reports: "Send Error Reports to Statping"
|
||||
},
|
||||
dashboard: {
|
||||
total_services: "Total Services",
|
||||
failures_24_hours: "Failures last 24 Hours",
|
||||
online_services: "Online Services",
|
||||
service: 'Service | Services',
|
||||
group: 'Group',
|
||||
title: 'Title',
|
||||
begins: 'Begins',
|
||||
name: 'Name',
|
||||
loading: 'Loading',
|
||||
login: 'Login',
|
||||
sign_in: "Sign In",
|
||||
visibility: 'Visibility',
|
||||
wrong_login: 'Incorrect username or password'
|
||||
},
|
||||
settings: {
|
||||
name: "Project Name",
|
||||
description: "Project Name",
|
||||
footer: "Custom Footer",
|
||||
footer_notes: "HTML is allowed inside the footer",
|
||||
error_reporting: "Enable Error Reporting",
|
||||
error_reporting_notes: "Help the Statping project out by sending anonymous error logs back to our server.",
|
||||
save: "Save Settings",
|
||||
main: "Main Settings",
|
||||
theme: "Theme Editor",
|
||||
settings: "Einstellungen",
|
||||
dashboard: "Armaturenbrett",
|
||||
services: "Dienstleistungen",
|
||||
service: "Service",
|
||||
failures: "Ausfälle",
|
||||
users: "Benutzer",
|
||||
login: "Login",
|
||||
logout: "Abmelden",
|
||||
online: "Online",
|
||||
offline: "Offline",
|
||||
configs: "Konfiguration",
|
||||
username: "Benutzername",
|
||||
password: "Kennwort",
|
||||
email: "Mail",
|
||||
confirm_password: "Passwort bestätigen",
|
||||
uptime: "Betriebszeit",
|
||||
name: "Name",
|
||||
copy: "Kopie",
|
||||
close: "Schließen",
|
||||
secret: "Geheimnis",
|
||||
regen_api: "API-Schlüssel neu generieren",
|
||||
regen_desc: "API Secret wird für Lesen erstellen Update und Löschen Routen verwendet. Sie können API-Schlüssel bei Bedarf neu generieren.",
|
||||
visibility: "Sichtbarkeit",
|
||||
group: "Gruppe",
|
||||
group_create: "Gruppe erstellen",
|
||||
group_update: "Gruppe aktualisieren",
|
||||
group_public_desc: "Gruppendienste für die Öffentlichkeit anzeigen",
|
||||
groups: "Gruppen",
|
||||
no_group: "Keine Gruppe",
|
||||
public: "Öffentlichkeit",
|
||||
private: "Privat",
|
||||
announcements: "Ankündigungen",
|
||||
notifiers: "Notifizierer",
|
||||
logs: "Protokolle",
|
||||
help: "Helfen",
|
||||
type: "Typ",
|
||||
edit: "Bearbeiten",
|
||||
update: "Update",
|
||||
create: "Schaffen",
|
||||
view: "Ansicht",
|
||||
save: "sparen",
|
||||
title: "Titel",
|
||||
status: "Status",
|
||||
begins: "Beginnt",
|
||||
total_services: "Dienstleistungen insgesamt",
|
||||
online_services: "Online-Dienste",
|
||||
request_timeout: "Zeitüberschreitung anfordern",
|
||||
service_never_online: "Service war noch nie online",
|
||||
service_online_check: "Online geprüft",
|
||||
service_offline_time: "Dienst war offline für",
|
||||
days_ago: "Vor Tagen",
|
||||
today: "Heute",
|
||||
week: "Woche",
|
||||
month: "Monat",
|
||||
day: "Tag",
|
||||
hour: "Stunde",
|
||||
minute: "Minute",
|
||||
failures_24_hours: "Ausfälle dauern 24 Stunden",
|
||||
no_services: "Sie haben derzeit keine Dienste!",
|
||||
theme: "Thema",
|
||||
cache: "Cache",
|
||||
oauth: "OAuth",
|
||||
beta: "BETA",
|
||||
changelog: "Changelog",
|
||||
repo: "Statping Github Repo",
|
||||
docs: "Documentation",
|
||||
},
|
||||
service: {
|
||||
name: "Service Name",
|
||||
type: "Service Type",
|
||||
info: "Service Information",
|
||||
view: "View Service",
|
||||
average: "Average Response",
|
||||
last_uptime: "Uptime last {0} {1}",
|
||||
},
|
||||
email: "Email Address",
|
||||
port: "Database Port",
|
||||
setting: "Settings",
|
||||
username: "Username",
|
||||
password: 'Password',
|
||||
services: 'Services',
|
||||
domain: 'Domain',
|
||||
online: 'online',
|
||||
public: 'Public',
|
||||
private: 'Private',
|
||||
admin: 'Admin',
|
||||
offline: 'offline',
|
||||
failure: 'failure | failures',
|
||||
incident: 'incident | incidents',
|
||||
checkin: 'checkin | checkins',
|
||||
user: 'User | Users',
|
||||
group: 'Group',
|
||||
message: 'message',
|
||||
edit: 'Edit',
|
||||
type: 'Type',
|
||||
sample_data: 'Sample Data',
|
||||
today: 'Today',
|
||||
last_login: 'Last Login',
|
||||
uptime: '{0}% Uptime',
|
||||
close: 'Close',
|
||||
second: 'second | seconds',
|
||||
minute: 'minute | minutes',
|
||||
hour: 'hour | hours',
|
||||
day: 'day | days',
|
||||
week: 'week | weeks',
|
||||
month: 'month | months',
|
||||
authentication: "Authentifikation",
|
||||
import: "Importieren",
|
||||
main_settings: "Haupteinstellungen",
|
||||
variables: "Variablen",
|
||||
docs: "Dokumentation",
|
||||
links: "Links",
|
||||
changelog: "Protokoll ändern",
|
||||
repo: "Depot",
|
||||
language: "Sprache",
|
||||
db_connection: "Datenbankverbindung",
|
||||
db_host: "Datenbank-Host",
|
||||
db_port: "Datenbank-Port",
|
||||
db_username: "Datenbank-Benutzername",
|
||||
db_password: "Datenbank-Kennwort",
|
||||
db_database: "Name der Datenbank",
|
||||
send_reports: "Fehlerberichte senden",
|
||||
send_reports_desc: "Fehler zum Debuggen an Statping senden",
|
||||
project_name: "Name der Seite „Status“",
|
||||
description: "Beschreibung",
|
||||
domain: "Domäne",
|
||||
enable_cdn: "CDN aktivieren",
|
||||
newsletter: "Newsletter",
|
||||
newsletter_note: "Wir senden Ihnen nur eine E-Mail für größere Änderungen",
|
||||
loading: "Laden",
|
||||
save_settings: "Einstellungen speichern",
|
||||
average_response: "Durchschnittliche Antwort",
|
||||
last_uptime: "Betriebszeit zuletzt",
|
||||
sign_in: "Anmelden",
|
||||
last_login: "Letzte Anmeldung",
|
||||
admin: "Admin",
|
||||
user: "Benutzer",
|
||||
failed: "Fehlgeschlagen",
|
||||
wrong_login: "Falscher Benutzername oder Passwort",
|
||||
theme_editor: "Theme-Editor",
|
||||
enable_assets: "Lokale Assets aktivieren",
|
||||
assets_desc: "Passen Sie das Design Ihrer Statusseite an, indem Sie lokale Elemente aktivieren. Dadurch wird ein Verzeichnis „Assets“ erstellt, das alle CSS enthält.",
|
||||
assets_btn: "Lokale Assets aktivieren",
|
||||
assets_loading: "Erstellen von Assets",
|
||||
assets_dir: "Assets Verzeichnis",
|
||||
footer: "Fußzeile",
|
||||
footer_notes: "Sie können HTML-Tags in der Fußzeile verwenden",
|
||||
global_announcement: "Globale Ankündigung",
|
||||
announcement_date: "Datumsbereich für Ankündigung",
|
||||
notify_users: "Benutzer benachrichtigen",
|
||||
notify_desc: "Benutzer vor geplanter Zeit benachrichtigen",
|
||||
notify_method: "Benachrichtigungsmethode",
|
||||
notify_before: "Vor benachrichtigen",
|
||||
message_create: "Ankündigung erstellen",
|
||||
message_edit: "Ankündigung bearbeiten",
|
||||
minutes: "Protokoll",
|
||||
hours: "Stunden",
|
||||
days: "Tage",
|
||||
user_create: "Benutzer erstellen",
|
||||
user_update: "Benutzer aktualisieren",
|
||||
administrator: "Administrator",
|
||||
checkins: "Checkins",
|
||||
incidents: "Vorfälle",
|
||||
service_info: "Service-Info",
|
||||
service_name: "Dienstname",
|
||||
service_type: "Service-Art",
|
||||
permalink: "Permalink-URL",
|
||||
service_public: "Öffentlicher Dienst",
|
||||
check_interval: "Intervall prüfen",
|
||||
service_endpoint: "Service-Endpunkt",
|
||||
service_check: "Service-Prüfungstyp",
|
||||
service_timeout: "Zeitüberschreitung anfordern",
|
||||
expected_resp: "Erwartete Antwort",
|
||||
expected_code: "Erwarteter Statuscode",
|
||||
follow_redir: "Weiterleitungen folgen",
|
||||
verify_ssl: "SSL verifizieren",
|
||||
tls_cert: "TLS-Zertifikat verwenden",
|
||||
notification_opts: "Benachrichtigungsoptionen",
|
||||
notifications_enable: "Benachrichtigungen aktivieren",
|
||||
notify_after: "Benachrichtigung nach Fehlern",
|
||||
notify_all: "Alle Änderungen benachrichtigen",
|
||||
service_update: "Dienst aktualisieren",
|
||||
service_create: "Dienst erstellen"
|
||||
}
|
||||
|
||||
export default german
|
|
@ -3,6 +3,10 @@ import spanish from "./spanish"
|
|||
import german from "./german"
|
||||
import russian from "./russian";
|
||||
import french from "./french";
|
||||
import japanese from "./japanese";
|
||||
import chinese from "./chinese";
|
||||
import korean from "./korean";
|
||||
import italian from "./italian";
|
||||
|
||||
const language = {
|
||||
en: english,
|
||||
|
@ -10,6 +14,10 @@ const language = {
|
|||
de: german,
|
||||
ru: russian,
|
||||
fr: french,
|
||||
ja: japanese,
|
||||
zh: chinese,
|
||||
ko: korean,
|
||||
it: italian,
|
||||
}
|
||||
|
||||
export default language
|
||||
|
|
|
@ -0,0 +1,142 @@
|
|||
const italian = {
|
||||
settings: "Impostazioni",
|
||||
dashboard: "cruscotto",
|
||||
services: "Servizi",
|
||||
service: "Servizio",
|
||||
failures: "Fallimenti",
|
||||
users: "Utenti",
|
||||
login: "Login",
|
||||
logout: "Disconnetti",
|
||||
online: "Online",
|
||||
offline: "Offline",
|
||||
configs: "Configurazione",
|
||||
username: "Nome utente",
|
||||
password: "Password",
|
||||
email: "E-mail",
|
||||
confirm_password: "Conferma password",
|
||||
uptime: "Tempi di attività",
|
||||
name: "Nome",
|
||||
copy: "Copia",
|
||||
close: "Vicino",
|
||||
secret: "Segreto",
|
||||
regen_api: "Rigenera chiavi API",
|
||||
regen_desc: "API Secret viene utilizzato per leggere creare aggiornamento ed eliminare route. Se necessario, è possibile rigenerare le chiavi API.",
|
||||
visibility: "Visibilità",
|
||||
group: "Gruppo",
|
||||
group_create: "Crea gruppo",
|
||||
group_update: "Aggiorna gruppo",
|
||||
group_public_desc: "Mostra i servizi di gruppo al pubblico",
|
||||
groups: "Gruppi",
|
||||
no_group: "Nessun gruppo",
|
||||
public: "Pubblico",
|
||||
private: "Privato",
|
||||
announcements: "Comunicazioni",
|
||||
notifiers: "Notificatori",
|
||||
logs: "Log",
|
||||
help: "Guida",
|
||||
type: "Tipo",
|
||||
edit: "Modifica",
|
||||
update: "Aggiorna",
|
||||
create: "Crea",
|
||||
view: "Visualizza",
|
||||
save: "Salvare",
|
||||
title: "Titolo",
|
||||
status: "Stato",
|
||||
begins: "Inizia",
|
||||
total_services: "Totale servizi",
|
||||
online_services: "Servizi online",
|
||||
request_timeout: "Timeout richiesta",
|
||||
service_never_online: "Il servizio non è mai stato online",
|
||||
service_online_check: "Controllato online",
|
||||
service_offline_time: "Il servizio è stato offline per",
|
||||
days_ago: "Giorni fa",
|
||||
today: "Oggi",
|
||||
week: "Settimana",
|
||||
month: "Mese",
|
||||
day: "Giorno",
|
||||
hour: "Ora",
|
||||
minute: "Minuti",
|
||||
failures_24_hours: "Errori nelle ultime 24 ore",
|
||||
no_services: "Al momento non hai alcun servizio!",
|
||||
theme: "Tema",
|
||||
cache: "Cache",
|
||||
authentication: "autenticazione",
|
||||
import: "Importa",
|
||||
main_settings: "Impostazioni principali",
|
||||
variables: "Variabili",
|
||||
docs: "Documentazione",
|
||||
links: "Collegamenti",
|
||||
changelog: "Registro delle modifiche",
|
||||
repo: "repository",
|
||||
language: "Lingua",
|
||||
db_connection: "Connessione al database",
|
||||
db_host: "Host del database",
|
||||
db_port: "Porta del database",
|
||||
db_username: "Nome utente del database",
|
||||
db_password: "Password del database",
|
||||
db_database: "Nome database",
|
||||
send_reports: "Invia segnalazioni errori",
|
||||
send_reports_desc: "Invia errori a Statping per il debug",
|
||||
project_name: "Nome pagina di stato",
|
||||
description: "Descrizione",
|
||||
domain: "Dominio",
|
||||
enable_cdn: "Abilita CDN",
|
||||
newsletter: "Bollettino",
|
||||
newsletter_note: "Ti invieremo solo una e-mail per le modifiche più importanti",
|
||||
loading: "Caricamento",
|
||||
save_settings: "Salva impostazioni",
|
||||
average_response: "Risposta media",
|
||||
last_uptime: "Ultimo tempo di attività",
|
||||
sign_in: "Accedi",
|
||||
last_login: "Ultimo Login",
|
||||
admin: "Amministratore",
|
||||
user: "Utente",
|
||||
failed: "Non riuscito",
|
||||
wrong_login: "Nome utente o password non corretti",
|
||||
theme_editor: "Editor tema",
|
||||
enable_assets: "Abilita risorse locali",
|
||||
assets_desc: "Personalizza la progettazione della pagina di stato abilitando le risorse locali. Questo creerà una directory 'asset' contenente tutti i CSS.",
|
||||
assets_btn: "Abilita risorse locali",
|
||||
assets_loading: "Creazione di risorse",
|
||||
assets_dir: "Directory delle risorse",
|
||||
footer: "Piè di pagina",
|
||||
footer_notes: "È possibile utilizzare i tag HTML nel piè di pagina",
|
||||
global_announcement: "Annuncio globale",
|
||||
announcement_date: "Intervallo di date di annuncio",
|
||||
notify_users: "Notify agli utenti",
|
||||
notify_desc: "Notifichi agli utenti prima dell'ora pianific",
|
||||
notify_method: "Metodo di notifica",
|
||||
notify_before: "Notifichi prima",
|
||||
message_create: "Crea annuncio",
|
||||
message_edit: "Modifica annuncio",
|
||||
minutes: "Minuti",
|
||||
hours: "Ore",
|
||||
days: "Giorni",
|
||||
user_create: "Crea utente",
|
||||
user_update: "Aggiorna utente",
|
||||
administrator: "Amministratore",
|
||||
checkins: "Check-ins",
|
||||
incidents: "Incidenti",
|
||||
service_info: "Info servizio",
|
||||
service_name: "Nome servizio",
|
||||
service_type: "Tipo di servizio",
|
||||
permalink: "URL Permalink",
|
||||
service_public: "Servizio pubblico",
|
||||
check_interval: "Intervallo controllo",
|
||||
service_endpoint: "Endpoint servizio",
|
||||
service_check: "Tipo di controllo del servizio",
|
||||
service_timeout: "Timeout richiesta",
|
||||
expected_resp: "Risposta prevista",
|
||||
expected_code: "Codice di stato previsto",
|
||||
follow_redir: "Segui i reindirizzamenti",
|
||||
verify_ssl: "Verifica SSL",
|
||||
tls_cert: "Usa certificato TLS",
|
||||
notification_opts: "Opzioni di notifica",
|
||||
notifications_enable: "Abilita notifiche",
|
||||
notify_after: "Notify dopo gli errori",
|
||||
notify_all: "Notifichi tutte le modifiche",
|
||||
service_update: "Servizio di aggiornamento",
|
||||
service_create: "Crea servizio"
|
||||
}
|
||||
|
||||
export default italian
|
|
@ -0,0 +1,142 @@
|
|||
const japanese = {
|
||||
settings: "設定",
|
||||
dashboard: "ダッシュボード",
|
||||
services: "サービス",
|
||||
service: "サービス",
|
||||
failures: "障害",
|
||||
users: "ユーザー",
|
||||
login: "ログイン",
|
||||
logout: "ログアウト",
|
||||
online: "オンライン",
|
||||
offline: "オフライン",
|
||||
configs: "構成",
|
||||
username: "ユーザ名",
|
||||
password: "パスワード",
|
||||
email: "Eメール",
|
||||
confirm_password: "パスワードの確認",
|
||||
uptime: "稼働時間",
|
||||
name: "氏名",
|
||||
copy: "コピー",
|
||||
close: "閉じる",
|
||||
secret: "秘密",
|
||||
regen_api: "API キーの再生成",
|
||||
regen_desc: "APIシークレットは、ルート作成の更新と削除の読み込みに使用されます。必要に応じて、API キーを再生成できます。",
|
||||
visibility: "可視性",
|
||||
group: "グループ",
|
||||
group_create: "[グループを作成]",
|
||||
group_update: "[グループを更新]",
|
||||
group_public_desc: "グループサービスを一般公開する",
|
||||
groups: "グループ",
|
||||
no_group: "グループなし",
|
||||
public: "パブリック",
|
||||
private: "私立",
|
||||
announcements: "アナウンス",
|
||||
notifiers: "通知者",
|
||||
logs: "ログ",
|
||||
help: "ヘルプ",
|
||||
type: "タイプ",
|
||||
edit: "編集",
|
||||
update: "更新",
|
||||
create: "作成",
|
||||
view: "ビュー",
|
||||
save: "保存する",
|
||||
title: "タイトル",
|
||||
status: "ステータス",
|
||||
begins: "開始する",
|
||||
total_services: "トータルサービス",
|
||||
online_services: "オンラインサービス",
|
||||
request_timeout: "リクエストのタイムアウト",
|
||||
service_never_online: "サービスがオンラインになったことがない",
|
||||
service_online_check: "オンラインチェック済み",
|
||||
service_offline_time: "のサービスがオフラインになりました",
|
||||
days_ago: "日前",
|
||||
today: "今日",
|
||||
week: "週数",
|
||||
month: "月",
|
||||
day: "日",
|
||||
hour: "アワー",
|
||||
minute: "分",
|
||||
failures_24_hours: "過去 24 時間の障害",
|
||||
no_services: "現在、サービスをお持ちになりません。",
|
||||
theme: "テーマ",
|
||||
cache: "キャッシュ",
|
||||
authentication: "認証",
|
||||
import: "インポート",
|
||||
main_settings: "メイン設定",
|
||||
variables: "変数",
|
||||
docs: "ドキュメント",
|
||||
links: "リンク",
|
||||
changelog: "変更ログ",
|
||||
repo: "リポジトリ",
|
||||
language: "言語",
|
||||
db_connection: "データベース接続",
|
||||
db_host: "データベース・ホスト",
|
||||
db_port: "データベースポート",
|
||||
db_username: "データベースのユーザー名",
|
||||
db_password: "データベースパスワード",
|
||||
db_database: "データベース名",
|
||||
send_reports: "エラーレポートを送信",
|
||||
send_reports_desc: "デバッグのためにエラーを Statping に送信する",
|
||||
project_name: "ステータスページ名",
|
||||
description: "説明",
|
||||
domain: "ドメイン",
|
||||
enable_cdn: "CDN を有効にする",
|
||||
newsletter: "ニュースレター",
|
||||
newsletter_note: "大きな変更についてのみメールをお送りします",
|
||||
loading: "ロード中",
|
||||
save_settings: "設定を保存",
|
||||
average_response: "平均応答",
|
||||
last_uptime: "稼働時間最後",
|
||||
sign_in: "サインイン",
|
||||
last_login: "最終ログイン",
|
||||
admin: "管理者",
|
||||
user: "ユーザー",
|
||||
failed: "失敗しました",
|
||||
wrong_login: "ユーザー名またはパスワードが正しくありません",
|
||||
theme_editor: "テーマ・エディター",
|
||||
enable_assets: "ローカルアセットを有効にする",
|
||||
assets_desc: "ローカルアセットを有効にして、ステータスページのデザインをカスタマイズします。これにより、すべてのCSSを含む「assets」ディレクトリが作成されます。",
|
||||
assets_btn: "ローカルアセットを有効にする",
|
||||
assets_loading: "アセットの作成",
|
||||
assets_dir: "アセットディレクトリ",
|
||||
footer: "フッター",
|
||||
footer_notes: "あなたはフッターにHTMLタグを使用することができます",
|
||||
global_announcement: "グローバル発表",
|
||||
announcement_date: "発表日の範囲",
|
||||
notify_users: "ユーザーに通知",
|
||||
notify_desc: "スケジュールされた時間前にユーザーに通知する",
|
||||
notify_method: "通知方法",
|
||||
notify_before: "前に通知する",
|
||||
message_create: "アナウンスの作成",
|
||||
message_edit: "アナウンスの編集",
|
||||
minutes: "分単位",
|
||||
hours: "時間",
|
||||
days: "日数",
|
||||
user_create: "ユーザーの作成",
|
||||
user_update: "ユーザーの更新",
|
||||
administrator: "管理者",
|
||||
checkins: "チェックイン",
|
||||
incidents: "事変",
|
||||
service_info: "サービス情報",
|
||||
service_name: "サービス名",
|
||||
service_type: "サービスタイプ",
|
||||
permalink: "パーマリンクURL",
|
||||
service_public: "公共サービス",
|
||||
check_interval: "チェック間隔",
|
||||
service_endpoint: "サービスエンドポイント",
|
||||
service_check: "サービス・チェック・タイプ",
|
||||
service_timeout: "リクエストのタイムアウト",
|
||||
expected_resp: "期待される応答",
|
||||
expected_code: "予想されるステータスコード",
|
||||
follow_redir: "リダイレクトに従う",
|
||||
verify_ssl: "SSL の確認",
|
||||
tls_cert: "TLS 証明書を使用",
|
||||
notification_opts: "通知オプション",
|
||||
notifications_enable: "通知を有効にする",
|
||||
notify_after: "障害発生後に通知する",
|
||||
notify_all: "すべての変更を通知",
|
||||
service_update: "サービスの更新",
|
||||
service_create: "サービスの作成"
|
||||
}
|
||||
|
||||
export default japanese
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue