mirror of https://github.com/statping/statping
commit
3fcf865269
|
@ -7,7 +7,7 @@ about: If you're having an issue or see an error
|
|||
### Describe the bug
|
||||
Try to explain what issue your'e having in detail. You can copy and paste the issue from the log file in your root directory `/logs/statping.log`.
|
||||
|
||||
You can set the environment variable `ALLOW_REPORTS` to `true` to allow errors to be sent to our error reporting server.
|
||||
You can set the environment variable `ALLOW_REPORTS` to `true` to allow errors to be sent to our error reporting server. It's super helpful.
|
||||
|
||||
### To Reproduce
|
||||
Steps to reproduce the behavior:
|
||||
|
|
|
@ -7,7 +7,7 @@ about: If you're having an issue or see an error
|
|||
### Describe the bug
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
You can set the environment variable `ALLOW_REPORTS` to `true` to allow errors to be sent to our error reporting server.
|
||||
You can set the environment variable `ALLOW_REPORTS` to `true` to allow errors to be sent to our error reporting server. It's super helpful.
|
||||
|
||||
### To Reproduce
|
||||
Steps to reproduce the behavior:
|
||||
|
@ -20,6 +20,6 @@ Steps to reproduce the behavior:
|
|||
A clear and concise description of what you expected to happen.
|
||||
|
||||
### Screenshots or Logs
|
||||
If applicable, add screenshots to help explain your problem. If you can, provide any logs from the latest `logs/statping.log` file.
|
||||
If applicable, add screenshots to help explain your problem. If you can, provide any logs from the latest `logs/statping.log` file.
|
||||
|
||||
[](https://slack.statping.com/) [](https://github.com/statping/statping/releases/latest) [](https://travis-ci.com/hunterlong/statup)
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
name: Push Request Testing
|
||||
name: Dev Release
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*' # matches every branch
|
||||
- '*/*' # matches every branch containing a single '/'
|
||||
- '!master' # excludes master
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
- dev
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
|
@ -51,7 +48,7 @@ jobs:
|
|||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
pr-test:
|
||||
test:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
|
@ -85,9 +82,7 @@ jobs:
|
|||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Global Dependencies
|
||||
run: |
|
||||
go get gotest.tools/gotestsum
|
||||
npm install -g yarn sass newman cross-env wait-on @sentry/cli
|
||||
run: npm install -g yarn sass newman cross-env wait-on @sentry/cli
|
||||
|
||||
- name: Setting ENV's
|
||||
run: |
|
||||
|
@ -112,7 +107,8 @@ jobs:
|
|||
|
||||
- name: Go Tests
|
||||
run: |
|
||||
SASS=`which sass` gotestsum --no-summary=skipped --format dots -- -covermode=count -coverprofile=coverage.out -p=1 ./...
|
||||
go get gotest.tools/gotestsum
|
||||
gotestsum --no-summary=skipped --format dots -- -covermode=count -coverprofile=coverage.out -p=1 ./...
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
DB_CONN: sqlite3
|
||||
|
@ -120,9 +116,35 @@ jobs:
|
|||
API_SECRET: demopassword123
|
||||
DISABLE_LOGS: false
|
||||
ALLOW_REPORTS: true
|
||||
PUSH_REQUEST: true
|
||||
COVERALLS: ${{ secrets.COVERALLS }}
|
||||
DISCORD_URL: ${{ secrets.DISCORD_URL }}
|
||||
EMAIL_HOST: ${{ secrets.EMAIL_HOST }}
|
||||
EMAIL_USER: ${{ secrets.EMAIL_USER }}
|
||||
EMAIL_PASS: ${{ secrets.EMAIL_PASS }}
|
||||
EMAIL_OUTGOING: ${{ secrets.EMAIL_OUTGOING }}
|
||||
EMAIL_SEND_TO: ${{ secrets.EMAIL_SEND_TO }}
|
||||
EMAIL_PORT: ${{ secrets.EMAIL_PORT }}
|
||||
MOBILE_ID: ${{ secrets.MOBILE_ID }}
|
||||
MOBILE_NUMBER: ${{ secrets.MOBILE_NUMBER }}
|
||||
PUSHOVER_TOKEN: ${{ secrets.PUSHOVER_TOKEN }}
|
||||
PUSHOVER_API: ${{ secrets.PUSHOVER_API }}
|
||||
SLACK_URL: ${{ secrets.SLACK_URL }}
|
||||
TELEGRAM_TOKEN: ${{ secrets.TELEGRAM_TOKEN }}
|
||||
TELEGRAM_CHANNEL: ${{ secrets.TELEGRAM_CHANNEL }}
|
||||
TWILIO_SID: ${{ secrets.TWILIO_SID }}
|
||||
TWILIO_SECRET: ${{ secrets.TWILIO_SECRET }}
|
||||
TWILIO_FROM: ${{ secrets.TWILIO_FROM }}
|
||||
TWILIO_TO: ${{ secrets.TWILIO_TO }}
|
||||
TEST_EMAIL: ${{ secrets.TEST_EMAIL }}
|
||||
|
||||
pr-test-postman:
|
||||
- name: Coveralls Testing Coverage
|
||||
run: |
|
||||
go get github.com/mattn/goveralls
|
||||
goveralls -coverprofile=coverage.out -repotoken $COVERALLS
|
||||
env:
|
||||
COVERALLS: ${{ secrets.COVERALLS }}
|
||||
|
||||
test-postman-sqlite:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
|
@ -154,13 +176,159 @@ jobs:
|
|||
|
||||
- name: Run Statping
|
||||
run: |
|
||||
API_SECRET=demosecret123 statping --port=8080 > /dev/null &
|
||||
sleep 3
|
||||
API_SECRET=demosecret123 statping --port=8585 > /dev/null &
|
||||
sleep 5
|
||||
|
||||
- name: Postman Tests
|
||||
- name: Postman SQLite Tests
|
||||
uses: matt-ball/newman-action@master
|
||||
with:
|
||||
apiKey: ${{ secrets.POSTMAN_API }}
|
||||
collection: ./dev/postman.json
|
||||
environment: ./dev/postman_environment_sqlite.json
|
||||
timeoutRequest: 15000
|
||||
delayRequest: 500
|
||||
environment: ./dev/postman_env_sqlite.json
|
||||
timeoutRequest: 30000
|
||||
delayRequest: 600
|
||||
|
||||
test-postman-mysql:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: password123
|
||||
MYSQL_DATABASE: statping
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
|
||||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Download Compiled Frontend (rice-box.go)
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
run: |
|
||||
make build
|
||||
chmod +x statping
|
||||
mv statping $(go env GOPATH)/bin/
|
||||
|
||||
- name: Run Statping
|
||||
run: |
|
||||
API_SECRET=demosecret123 statping --port=8585 > /dev/null &
|
||||
sleep 5
|
||||
|
||||
- name: Postman MySQL Tests
|
||||
uses: matt-ball/newman-action@master
|
||||
with:
|
||||
apiKey: ${{ secrets.POSTMAN_API }}
|
||||
collection: ./dev/postman.json
|
||||
environment: ./dev/postman_env_mysql.json
|
||||
timeoutRequest: 30000
|
||||
delayRequest: 600
|
||||
|
||||
test-postman-postgres:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10.8
|
||||
env:
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_PASSWORD: password123
|
||||
POSTGRES_DB: statping
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
|
||||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Download Compiled Frontend (rice-box.go)
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
run: |
|
||||
make build
|
||||
chmod +x statping
|
||||
mv statping $(go env GOPATH)/bin/
|
||||
|
||||
- name: Run Statping
|
||||
run: |
|
||||
API_SECRET=demosecret123 statping --port=8585 > /dev/null &
|
||||
sleep 5
|
||||
|
||||
- name: Postman Postgres Tests
|
||||
uses: matt-ball/newman-action@master
|
||||
with:
|
||||
apiKey: ${{ secrets.POSTMAN_API }}
|
||||
collection: ./dev/postman.json
|
||||
environment: ./dev/postman_env_postgres.json
|
||||
timeoutRequest: 30000
|
||||
delayRequest: 600
|
||||
|
||||
docker-release:
|
||||
needs: [test, test-postman-sqlite, test-postman-mysql, test-postman-postgres]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout Statping Repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Setting ENV's
|
||||
run: echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Base Docker Image
|
||||
uses: elgohr/Publish-Docker-Github-Action@master
|
||||
with:
|
||||
name: statping/statping
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
dockerfile: Dockerfile.base
|
||||
tags: "base"
|
||||
|
||||
- name: Latest/Version Docker Image
|
||||
uses: elgohr/Publish-Docker-Github-Action@master
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
ARCH: amd64
|
||||
DOCKER_CLI_EXPERIMENTAL: enabled
|
||||
with:
|
||||
name: statping/statping
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
dockerfile: Dockerfile
|
||||
tags: "dev"
|
||||
buildargs: VERSION,ARCH
|
||||
|
|
|
@ -135,6 +135,7 @@ jobs:
|
|||
TWILIO_SECRET: ${{ secrets.TWILIO_SECRET }}
|
||||
TWILIO_FROM: ${{ secrets.TWILIO_FROM }}
|
||||
TWILIO_TO: ${{ secrets.TWILIO_TO }}
|
||||
TEST_EMAIL: ${{ secrets.TEST_EMAIL }}
|
||||
|
||||
- name: Coveralls Testing Coverage
|
||||
run: |
|
||||
|
@ -388,7 +389,7 @@ jobs:
|
|||
with:
|
||||
tag_name: v${{ env.VERSION }}
|
||||
draft: false
|
||||
prerelease: false
|
||||
prerelease: true
|
||||
files: |
|
||||
builds/statping-linux-386.tar.gz
|
||||
builds/statping-linux-amd64.tar.gz
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
name: Push Request Testing
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- '*' # matches every branch
|
||||
- '*/*' # matches every branch containing a single '/'
|
||||
- '!master' # excludes master
|
||||
- '!dev' # excludes dev
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
compile:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Add GOBIN to PATH
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Install Global Dependencies
|
||||
run: npm install -g yarn sass cross-env
|
||||
|
||||
- name: Download Frontend Dependencies
|
||||
if: steps.nodecache.outputs.cache-hit != 'true'
|
||||
working-directory: ./frontend
|
||||
run: yarn
|
||||
|
||||
- name: Download Go mods
|
||||
if: steps.golangcache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
go mod download
|
||||
go mod verify
|
||||
make test-deps
|
||||
|
||||
- name: Build Frontend Statping
|
||||
run: make clean compile
|
||||
|
||||
- name: Upload Compiled Frontend (rice-box.go)
|
||||
uses: actions/upload-artifact@v1
|
||||
with:
|
||||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
pr-test:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:10.8
|
||||
env:
|
||||
POSTGRES_USER: root
|
||||
POSTGRES_PASSWORD: password123
|
||||
POSTGRES_DB: statping
|
||||
ports:
|
||||
- 5432:5432
|
||||
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
|
||||
|
||||
mysql:
|
||||
image: mysql:5.7
|
||||
env:
|
||||
MYSQL_ROOT_PASSWORD: password123
|
||||
MYSQL_DATABASE: statping
|
||||
ports:
|
||||
- 3306:3306
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
|
||||
|
||||
steps:
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: '10.x'
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- name: Install Global Dependencies
|
||||
run: |
|
||||
go get gotest.tools/gotestsum
|
||||
npm install -g yarn sass newman cross-env wait-on @sentry/cli
|
||||
|
||||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Download Compiled Frontend (rice-box.go)
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
run: |
|
||||
make build certs
|
||||
chmod +x statping
|
||||
mv statping $(go env GOPATH)/bin/
|
||||
|
||||
- name: Go Tests
|
||||
run: |
|
||||
SASS=`which sass` gotestsum --no-summary=skipped --format dots -- -covermode=count -coverprofile=coverage.out -p=1 ./...
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
DB_CONN: sqlite3
|
||||
STATPING_DIR: ${{ github.workspace }}
|
||||
API_SECRET: demopassword123
|
||||
DISABLE_LOGS: false
|
||||
ALLOW_REPORTS: true
|
||||
PUSH_REQUEST: true
|
||||
|
||||
pr-test-postman:
|
||||
needs: compile
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.14.2'
|
||||
|
||||
- name: Setting ENV's
|
||||
run: |
|
||||
echo "::add-path::$(go env GOPATH)/bin"
|
||||
echo "::add-path::/opt/hostedtoolcache/node/10.20.1/x64/bin"
|
||||
echo ::set-env name=VERSION::$(cat version.txt)
|
||||
shell: bash
|
||||
|
||||
- name: Download Compiled Frontend (rice-box.go)
|
||||
uses: actions/download-artifact@v1
|
||||
with:
|
||||
name: static-rice-box
|
||||
path: ./source
|
||||
|
||||
- name: Install Statping
|
||||
env:
|
||||
VERSION: ${{ env.VERSION }}
|
||||
run: |
|
||||
make build
|
||||
chmod +x statping
|
||||
mv statping $(go env GOPATH)/bin/
|
||||
|
||||
- name: Run Statping
|
||||
run: |
|
||||
API_SECRET=demosecret123 statping --port=8080 > /dev/null &
|
||||
sleep 3
|
||||
|
||||
- name: Postman Tests
|
||||
uses: matt-ball/newman-action@master
|
||||
with:
|
||||
collection: ./dev/postman.json
|
||||
environment: ./dev/postman_environment_sqlite.json
|
||||
timeoutRequest: 15000
|
||||
delayRequest: 500
|
|
@ -1,3 +1,10 @@
|
|||
# 0.90.55 (06-18-2020)
|
||||
- Added 404 page
|
||||
- Modified Statping's PR process, dev -> master
|
||||
- Fixed Discord notifier
|
||||
- Modified email template for SMTP emails
|
||||
- Added OnSave() method for all notifiers
|
||||
|
||||
# 0.90.54 (06-17-2020)
|
||||
- Fixed Slack Notifier's failure/success data saving issue
|
||||
- Added additional i18n Languages (help needed!)
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
# Statping - Status Page & Monitoring Server
|
||||
An easy to use Status Page for your websites and applications. Statping will automatically fetch the application and render a beautiful status page with tons of features for you to build an even better status page. This Status Page generator allows you to use MySQL, Postgres, or SQLite on multiple operating systems.
|
||||
|
||||
[](https://github.com/statping/statping/actions) [](https://godoc.org/github.com/statping/statping) [](https://slack.statping.com) [](https://microbadger.com/images/statping/statping) [](https://hub.docker.com/r/statping/statping/builds/)
|
||||
 [](https://godoc.org/github.com/statping/statping) [](https://slack.statping.com) [](https://microbadger.com/images/statping/statping) [](https://hub.docker.com/r/statping/statping/builds/)
|
||||
|
||||
<br><br>
|
||||
<img align="left" width="320" height="235" src="https://img.cjx.io/statupsiterun.gif">
|
||||
|
@ -156,8 +156,9 @@ aws ec2 run-instances \
|
|||
```
|
||||
|
||||
## Contributing
|
||||
Statping accepts Push Requests! Feel free to add your own features and notifiers. You probably want to checkout the [Notifier Wiki](https://github.com/statping/statping/wiki/Notifiers) to get a better understanding on how to create your own notification methods for failing/successful services. Testing on Statping will test each function on MySQL, Postgres, and SQLite. I recommend you run a MySQL and a Postgres Docker image for testing.
|
||||
Statping accepts Push Requests to the `dev` branch! Feel free to add your own features and notifiers. You probably want to checkout the [Notifier Wiki](https://github.com/statping/statping/wiki/Notifiers) to get a better understanding on how to create your own notification methods for failing/successful services. Testing on Statping will test each function on MySQL, Postgres, and SQLite. I recommend running MySQL and Postgres Docker containers for testing. You can find multiple docker-compose files in the dev directory.
|
||||
|
||||

|
||||
[](https://goreportcard.com/report/github.com/statping/statping)
|
||||
[](https://travis-ci.com/statping/statping) [](https://dashboard.cypress.io/#/projects/bi8mhr/runs)
|
||||
[](https://hub.docker.com/r/statping/statping/builds/) [](https://godoc.org/github.com/statping/statping)[](https://coveralls.io/github/statping/statping?branch=master)
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -19,7 +19,7 @@
|
|||
{{$t('dashboard.wrong_login')}}
|
||||
</div>
|
||||
<button @click.prevent="login" type="submit" class="btn btn-block mb-3 btn-primary" :disabled="disabled || loading">
|
||||
{{loading ? $t('dashboard.loading') : $t('dashboard.sign_in')}}
|
||||
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/>{{loading ? $t('dashboard.loading') : $t('dashboard.sign_in')}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -89,12 +89,12 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="col-4 col-md-4">
|
||||
<button @click.prevent="testNotifier('success')" class="btn btn-outline-dark btn-block text-capitalize test-notifier">
|
||||
<i class="fa fa-vial"></i>{{loadingTest ? "Loading..." : "Test Success"}}</button>
|
||||
<button @click.prevent="testNotifier('success')" :disabled="loadingTest" class="btn btn-outline-dark 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-4 col-md-4">
|
||||
<button @click.prevent="testNotifier('failure')" class="btn btn-outline-dark btn-block text-capitalize test-notifier">
|
||||
<i class="fa fa-vial"></i>{{loadingTest ? "Loading..." : "Test Failure"}}</button>
|
||||
<button @click.prevent="testNotifier('failure')" :disabled="loadingTest" class="btn btn-outline-dark 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>
|
||||
|
||||
|
|
|
@ -123,6 +123,8 @@ export default Vue.mixin({
|
|||
return "bell"
|
||||
case "fas fa-mobile-alt":
|
||||
return "mobile"
|
||||
case "fas envelope-square":
|
||||
return ["fas", "envelope-square"]
|
||||
case "fab fa-slack":
|
||||
return ["fab", "slack-hash"]
|
||||
case "fab fa-telegram-plane":
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<template>
|
||||
<div class="row mt-4">
|
||||
<div class="mx-auto">
|
||||
<img alt="Statping 404" class="" style="max-width:480px" src="banner.png">
|
||||
</div>
|
||||
|
||||
<div class="col-12 mt-5 mb-5">
|
||||
<div class="text-center">
|
||||
<h2>Page Not Found</h2>
|
||||
<h5 class="text-muted">This URL doesn't seem to be available</h5>
|
||||
|
||||
<router-link class="btn btn-outline-success mt-4" to="/">Back To Homepage</router-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'NotFound',
|
||||
}
|
||||
</script>
|
|
@ -150,7 +150,6 @@
|
|||
return {
|
||||
tab: "v-pills-home-tab",
|
||||
qrcode: "",
|
||||
qrurl: "",
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
|
@ -174,8 +173,8 @@
|
|||
const n = await Api.notifiers()
|
||||
this.$store.commit('setNotifiers', n)
|
||||
|
||||
this.qrurl = `statping://setup?domain=${c.domain}&api=${c.api_secret}`
|
||||
this.qrcode = "https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=" + encodeURI(this.qrurl)
|
||||
const u = `statping://setup?domain=${c.domain}&api=${c.api_secret}`
|
||||
this.qrcode = "https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=" + encodeURIComponent(u)
|
||||
this.cache = await Api.cache()
|
||||
},
|
||||
changeTab(e) {
|
||||
|
|
|
@ -13,6 +13,7 @@ const Setup = () => import('@/forms/Setup')
|
|||
const Incidents = () => import('@/components/Dashboard/Incidents')
|
||||
const Checkins = () => import('@/components/Dashboard/Checkins')
|
||||
const Failures = () => import('@/components/Dashboard/Failures')
|
||||
const NotFound = () => import('@/pages/NotFound')
|
||||
|
||||
import VueRouter from "vue-router";
|
||||
import Api from "./API";
|
||||
|
@ -131,6 +132,11 @@ const routes = [
|
|||
name: 'Service',
|
||||
component: Service,
|
||||
props: true
|
||||
},
|
||||
{
|
||||
path: '*',
|
||||
component: NotFound,
|
||||
name: 'NotFound',
|
||||
}
|
||||
];
|
||||
|
||||
|
|
1
go.mod
1
go.mod
|
@ -15,6 +15,7 @@ require (
|
|||
github.com/golang/protobuf v1.3.5 // indirect
|
||||
github.com/gorilla/mux v1.7.4
|
||||
github.com/gorilla/securecookie v1.1.1
|
||||
github.com/hako/durafmt v0.0.0-20200605151348-3a43fc422dd9
|
||||
github.com/jinzhu/gorm v1.9.12
|
||||
github.com/magiconair/properties v1.8.1
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible
|
||||
|
|
2
go.sum
2
go.sum
|
@ -129,6 +129,8 @@ github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoA
|
|||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/hako/durafmt v0.0.0-20200605151348-3a43fc422dd9 h1:IEhIezS5kcD4ZzOwVl8dAyJ9JCi4Xo6tg44Vj/z7UsI=
|
||||
github.com/hako/durafmt v0.0.0-20200605151348-3a43fc422dd9/go.mod h1:5Scbynm8dF1XAPwIwkGPqzkM/shndPm79Jd1003hTjE=
|
||||
github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
|
|
|
@ -248,8 +248,6 @@ func TestMainApiRoutes(t *testing.T) {
|
|||
ExpectedContains: []string{
|
||||
`go_goroutines`,
|
||||
`go_memstats_alloc_bytes`,
|
||||
`process_cpu_seconds_total`,
|
||||
`promhttp_metric_handler_requests_total`,
|
||||
`go_threads`,
|
||||
},
|
||||
},
|
||||
|
|
|
@ -53,7 +53,13 @@ func apiNotifierUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
//notifications.OnSave(notifer.Method)
|
||||
|
||||
notif := services.ReturnNotifier(notifer.Method)
|
||||
if _, err := notif.OnSave(); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
sendJsonAction(vars["notifier"], "update", w, r)
|
||||
}
|
||||
|
||||
|
|
|
@ -70,3 +70,8 @@ func (c *commandLine) OnTest() (string, error) {
|
|||
utils.Log.Infoln(out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OnSave will trigger when this notifier is saved
|
||||
func (c *commandLine) OnSave() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
|
|
@ -52,15 +52,13 @@ func (d *discord) Select() *notifications.Notification {
|
|||
|
||||
// OnFailure will trigger failing service
|
||||
func (d *discord) OnFailure(s *services.Service, f *failures.Failure) (string, error) {
|
||||
msg := `{"content": "Your service '{{.Service.Name}}' is currently failing! Reason: {{.Failure.Issue}}"}`
|
||||
out, err := d.sendRequest(ReplaceVars(msg, s, f))
|
||||
out, err := d.sendRequest(ReplaceVars(d.FailureData, s, f))
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (d *discord) OnSuccess(s *services.Service) (string, error) {
|
||||
msg := `{"content": "Your service '{{.Service.Name}}' is currently online!"}`
|
||||
out, err := d.sendRequest(ReplaceVars(msg, s, nil))
|
||||
out, err := d.sendRequest(ReplaceVars(d.SuccessData, s, nil))
|
||||
return out, err
|
||||
}
|
||||
|
||||
|
@ -83,6 +81,11 @@ func (d *discord) OnTest() (string, error) {
|
|||
return string(contents), nil
|
||||
}
|
||||
|
||||
// OnSave will trigger when this notifier is saved
|
||||
func (d *discord) OnSave() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
type discordTestJson struct {
|
||||
Code int `json:"code"`
|
||||
Message string `json:"message"`
|
||||
|
|
|
@ -1,96 +1,21 @@
|
|||
package notifiers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"github.com/go-mail/mail"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/notifier"
|
||||
"github.com/statping/statping/types/null"
|
||||
"github.com/statping/statping/types/services"
|
||||
"github.com/statping/statping/utils"
|
||||
"time"
|
||||
"html/template"
|
||||
)
|
||||
|
||||
var _ notifier.Notifier = (*emailer)(nil)
|
||||
|
||||
const (
|
||||
mainEmailTemplate = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Statping email</title>
|
||||
</head>
|
||||
<body style="-webkit-text-size-adjust: none; box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; height: 100%; line-height: 1.4; margin: 0; width: 100% !important;" bgcolor="#F2F4F6">
|
||||
<style type="text/css">
|
||||
body {
|
||||
width: 100% !important;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
background-color: #F2F4F6;
|
||||
color: #74787E;
|
||||
-webkit-text-size-adjust: none;
|
||||
}
|
||||
@media only screen and (max-width: 600px) {
|
||||
.email-body_inner {
|
||||
width: 100% !important;
|
||||
}
|
||||
.email-footer {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media only screen and (max-width: 500px) {
|
||||
.button {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<table class="email-wrapper" width="100%" cellpadding="0" cellspacing="0" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0; padding: 0; width: 100%;" bgcolor="#F2F4F6">
|
||||
<tr>
|
||||
<td align="center" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; word-break: break-word;">
|
||||
<table class="email-content" width="100%" cellpadding="0" cellspacing="0" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0; padding: 0; width: 100%;">
|
||||
<tr>
|
||||
<td class="email-body" width="100%" cellpadding="0" cellspacing="0" style="-premailer-cellpadding: 0; -premailer-cellspacing: 0; border-bottom-color: #EDEFF2; border-bottom-style: solid; border-bottom-width: 1px; border-top-color: #EDEFF2; border-top-style: solid; border-top-width: 1px; box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0; padding: 0; width: 100%; word-break: break-word;" bgcolor="#FFFFFF">
|
||||
<table class="email-body_inner" align="center" width="570" cellpadding="0" cellspacing="0" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin: 0 auto; padding: 0; width: 570px;" bgcolor="#FFFFFF">
|
||||
<tr>
|
||||
<td class="content-cell" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; padding: 35px; word-break: break-word;">
|
||||
<h1 style="box-sizing: border-box; color: #2F3133; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 19px; font-weight: bold; margin-top: 0;" align="left">
|
||||
{{ .Service.Name }} is {{ if .Service.Online }}Online{{else}}Offline{{end}}!
|
||||
</h1>
|
||||
<p style="box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 16px; line-height: 1.5em; margin-top: 0;" align="left">
|
||||
|
||||
{{ if .Service.Online }}
|
||||
Your Statping service <a target="_blank" href="{{.Service.Domain}}">{{.Service.Name}}</a> is back online. This service has been triggered with a HTTP status code of '{{.Service.LastStatusCode}}' and is currently online based on your requirements. Your service was reported online at {{.Service.CreatedAt}}. </p>
|
||||
{{ else }}
|
||||
Your Statping service <a target="_blank" href="{{.Service.Domain}}">{{.Service.Name}}</a> has been triggered with a HTTP status code of '{{.Service.LastStatusCode}}' and is currently offline based on your requirements. This failure was created on {{.Service.CreatedAt}}. </p>
|
||||
{{ end }}
|
||||
|
||||
{{if .Service.LastResponse }}
|
||||
<h1 style="box-sizing: border-box; color: #2F3133; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 19px; font-weight: bold; margin-top: 0;" align="left">
|
||||
Last Response</h1>
|
||||
<p style="box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 16px; line-height: 1.5em; margin-top: 0;" align="left">
|
||||
{{ .Service.LastResponse }} </p> {{end}}
|
||||
<table class="body-sub" style="border-top-color: #EDEFF2; border-top-style: solid; border-top-width: 1px; box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin-top: 25px; padding-top: 25px;">
|
||||
<td style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; word-break: break-word;"> <a href="/service/{{.Service.Id}}" class="button button--blue" target="_blank" style="-webkit-text-size-adjust: none; background: #3869D4; border-color: #3869d4; border-radius: 3px; border-style: solid; border-width: 10px 18px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); box-sizing: border-box; color: #FFF; display: inline-block; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; text-decoration: none;">View Service</a> </td>
|
||||
<td style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; word-break: break-word;"> <a href="/dashboard" class="button button--blue" target="_blank" style="-webkit-text-size-adjust: none; background: #3869D4; border-color: #3869d4; border-radius: 3px; border-style: solid; border-width: 10px 18px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); box-sizing: border-box; color: #FFF; display: inline-block; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; text-decoration: none;">Statping Dashboard</a> </td>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>`
|
||||
)
|
||||
|
||||
var (
|
||||
mailer *mail.Dialer
|
||||
)
|
||||
|
@ -105,14 +30,11 @@ func (e *emailer) Select() *notifications.Notification {
|
|||
|
||||
var email = &emailer{¬ifications.Notification{
|
||||
Method: "email",
|
||||
Title: "email",
|
||||
Title: "SMTP Mail",
|
||||
Description: "Send emails via SMTP when services are online or offline.",
|
||||
Author: "Hunter Long",
|
||||
AuthorUrl: "https://github.com/hunterlong",
|
||||
Icon: "far fa-envelope",
|
||||
SuccessData: "Service {{.Service.Name}} is Back Online",
|
||||
FailureData: "Service {{.Service.Name}} is Offline",
|
||||
DataType: "text",
|
||||
Limits: 30,
|
||||
Form: []notifications.NotificationForm{{
|
||||
Type: "text",
|
||||
|
@ -145,7 +67,7 @@ var email = &emailer{¬ifications.Notification{
|
|||
Placeholder: "sendto@email.com",
|
||||
DbField: "Var2",
|
||||
}, {
|
||||
Type: "text",
|
||||
Type: "switch",
|
||||
Title: "Disable TLS/SSL",
|
||||
Placeholder: "",
|
||||
SmallText: "To Disable TLS/SSL insert 'true'",
|
||||
|
@ -166,64 +88,69 @@ type emailOutgoing struct {
|
|||
// OnFailure will trigger failing service
|
||||
func (e *emailer) OnFailure(s *services.Service, f *failures.Failure) (string, error) {
|
||||
subject := fmt.Sprintf("Service %s is Offline", s.Name)
|
||||
tmpl := renderEmail(s, f)
|
||||
email := &emailOutgoing{
|
||||
To: e.Var2,
|
||||
Subject: subject,
|
||||
Template: mainEmailTemplate,
|
||||
Data: replacer{
|
||||
Service: s,
|
||||
Failure: f,
|
||||
},
|
||||
From: e.Var1,
|
||||
Template: tmpl,
|
||||
From: e.Var1,
|
||||
}
|
||||
return "email failed", e.dialSend(email)
|
||||
return tmpl, e.dialSend(email)
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (e *emailer) OnSuccess(s *services.Service) (string, error) {
|
||||
subject := fmt.Sprintf("Service %s is Back Online", s.Name)
|
||||
tmpl := renderEmail(s, nil)
|
||||
email := &emailOutgoing{
|
||||
To: e.Var2,
|
||||
Subject: subject,
|
||||
Template: mainEmailTemplate,
|
||||
Data: replacer{
|
||||
Service: s,
|
||||
Failure: &failures.Failure{},
|
||||
},
|
||||
From: e.Var1,
|
||||
Template: tmpl,
|
||||
From: e.Var1,
|
||||
}
|
||||
return "email sent", e.dialSend(email)
|
||||
return tmpl, e.dialSend(email)
|
||||
}
|
||||
|
||||
func renderEmail(s *services.Service, f *failures.Failure) string {
|
||||
wr := bytes.NewBuffer(nil)
|
||||
tmpl := template.New("email")
|
||||
tmpl, err := tmpl.Parse(emailBase)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return emailBase
|
||||
}
|
||||
|
||||
data := replacer{
|
||||
Core: core.App,
|
||||
Service: s,
|
||||
Failure: f,
|
||||
Custom: nil,
|
||||
}
|
||||
|
||||
if err = tmpl.ExecuteTemplate(wr, "email", data); err != nil {
|
||||
log.Errorln(err)
|
||||
return emailBase
|
||||
}
|
||||
|
||||
return wr.String()
|
||||
}
|
||||
|
||||
// OnTest triggers when this notifier has been saved
|
||||
func (e *emailer) OnTest() (string, error) {
|
||||
testService := services.Service{
|
||||
Id: 1,
|
||||
Name: "Example Service",
|
||||
Domain: "https://www.youtube.com/watch?v=-u6DvRyyKGU",
|
||||
ExpectedStatus: 200,
|
||||
Interval: 30,
|
||||
Type: "http",
|
||||
Method: "GET",
|
||||
Timeout: 20,
|
||||
LastStatusCode: 200,
|
||||
Expected: null.NewNullString("test example"),
|
||||
LastResponse: "<html>this is an example response</html>",
|
||||
CreatedAt: utils.Now().Add(-24 * time.Hour),
|
||||
}
|
||||
subject := fmt.Sprintf("Service %v is Back Online", testService.Name)
|
||||
service := services.Example(true)
|
||||
subject := fmt.Sprintf("Service %v is Back Online", service.Name)
|
||||
email := &emailOutgoing{
|
||||
To: e.Var2,
|
||||
Subject: subject,
|
||||
Template: mainEmailTemplate,
|
||||
Data: replacer{
|
||||
Service: &testService,
|
||||
Failure: &failures.Failure{},
|
||||
},
|
||||
From: e.Var1,
|
||||
Template: renderEmail(service, failures.Example()),
|
||||
From: e.Var1,
|
||||
}
|
||||
err := e.dialSend(email)
|
||||
return subject, err
|
||||
return subject, e.dialSend(email)
|
||||
}
|
||||
|
||||
// OnSave will trigger when this notifier is saved
|
||||
func (e *emailer) OnSave() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (e *emailer) dialSend(email *emailOutgoing) error {
|
||||
|
@ -236,14 +163,15 @@ func (e *emailer) dialSend(email *emailOutgoing) error {
|
|||
mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
|
||||
m.SetHeader("From", email.From)
|
||||
m.SetAddressHeader("From", email.From, "Statping")
|
||||
m.SetHeader("To", email.To)
|
||||
m.SetHeader("Subject", email.Subject)
|
||||
m.SetBody("text/html", ReplaceTemplate(email.Template, email.Data))
|
||||
m.SetBody("text/html", email.Template)
|
||||
|
||||
if err := mailer.DialAndSend(m); err != nil {
|
||||
utils.Log.Errorln(fmt.Sprintf("email '%v' sent to: %v (size: %v) %v", email.Subject, email.To, len([]byte(email.Source)), err))
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,628 @@
|
|||
package notifiers
|
||||
|
||||
const emailBase = `
|
||||
{{$banner := "https://assets.statping.com/greenbackground.png"}}
|
||||
{{$color := "#4caf50"}}
|
||||
{{if not .Service.Online}}
|
||||
{{$banner = "https://assets.statping.com/offlinebanner.png"}}
|
||||
{{$color = "#c30c0c"}}
|
||||
{{end}}
|
||||
|
||||
<!doctype html>
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:o="urn:schemas-microsoft-com:office:office">
|
||||
|
||||
<head>
|
||||
<title> Statping Service Notification </title>
|
||||
<!--[if !mso]><!-- -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!--<![endif]-->
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style type="text/css">
|
||||
#outlook a {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
table,
|
||||
td {
|
||||
border-collapse: collapse;
|
||||
mso-table-lspace: 0pt;
|
||||
mso-table-rspace: 0pt;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
height: auto;
|
||||
line-height: 100%;
|
||||
outline: none;
|
||||
text-decoration: none;
|
||||
-ms-interpolation-mode: bicubic;
|
||||
}
|
||||
|
||||
p {
|
||||
display: block;
|
||||
margin: 13px 0;
|
||||
}
|
||||
</style>
|
||||
<!--[if mso]>
|
||||
<xml>
|
||||
<o:OfficeDocumentSettings>
|
||||
<o:AllowPNG/>
|
||||
<o:PixelsPerInch>96</o:PixelsPerInch>
|
||||
</o:OfficeDocumentSettings>
|
||||
</xml>
|
||||
<![endif]-->
|
||||
<!--[if lte mso 11]>
|
||||
<style type="text/css">
|
||||
.mj-outlook-group-fix { width:100% !important; }
|
||||
</style>
|
||||
<![endif]-->
|
||||
<!--[if !mso]><!-->
|
||||
<link href="https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700" rel="stylesheet" type="text/css">
|
||||
<style type="text/css">
|
||||
@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700);
|
||||
</style>
|
||||
<!--<![endif]-->
|
||||
<style type="text/css">
|
||||
@media only screen and (min-width:480px) {
|
||||
.mj-column-per-100 {
|
||||
width: 100% !important;
|
||||
max-width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style type="text/css">
|
||||
@media only screen and (max-width:480px) {
|
||||
table.mj-full-width-mobile {
|
||||
width: 100% !important;
|
||||
}
|
||||
td.mj-full-width-mobile {
|
||||
width: auto !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body style="background-color:#E7E7E7;">
|
||||
<div style="background-color:#E7E7E7;">
|
||||
<!-- Top Bar -->
|
||||
<!--[if mso | IE]>
|
||||
<table
|
||||
align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
|
||||
>
|
||||
<tr>
|
||||
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
|
||||
|
||||
<v:rect style="width:600px;" xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false">
|
||||
<v:fill origin="0.5, 0" position="0.5, 0" src="{{$banner}}" color="#FF3FB4" type="tile" />
|
||||
<v:textbox style="mso-fit-shape-to-text:true" inset="0,0,0,0">
|
||||
<![endif]-->
|
||||
<div style="background:#FF3FB4 url({{$banner}}) top center / auto repeat;margin:0px auto;max-width:600px;">
|
||||
<div style="line-height:0;font-size:0;">
|
||||
<table align="center" background="{{$banner}}" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#FF3FB4 url({{$banner}}) top center / auto repeat;width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:0px;text-align:center;">
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
|
||||
<tr>
|
||||
|
||||
<td
|
||||
class="" style="vertical-align:top;width:600px;"
|
||||
>
|
||||
<![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tr>
|
||||
<td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width:45px;"> <a href="https://statping.com" target="_blank">
|
||||
|
||||
<img
|
||||
alt="Statping" height="auto" src="https://assets.statping.com/iconlight.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="45"
|
||||
/>
|
||||
|
||||
</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</v:textbox>
|
||||
</v:rect>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<table
|
||||
align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
|
||||
>
|
||||
<tr>
|
||||
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
|
||||
<![endif]-->
|
||||
<div style="background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
|
||||
<tr>
|
||||
|
||||
<td
|
||||
class="" style="vertical-align:top;width:600px;"
|
||||
>
|
||||
<![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tr>
|
||||
<td align="left" style="font-size:0px;padding:15px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:22px;line-height:30px;text-align:left;color:#000000;">
|
||||
|
||||
{{if .Service.Online}}
|
||||
{{.Service.Name}} is back online.
|
||||
{{else}}
|
||||
{{.Service.Name}} is currently offline, you might want to check it.
|
||||
{{end}}
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-size:0px;padding:20px 0;padding-top:10px;padding-right:0px;padding-bottom:10px;padding-left:0px;word-break:break-word;">
|
||||
<!--[if mso | IE]>
|
||||
<table
|
||||
align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
|
||||
>
|
||||
<tr>
|
||||
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
|
||||
<![endif]-->
|
||||
<div style="margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;padding-bottom:10px;padding-left:0px;padding-right:0px;padding-top:10px;text-align:center;">
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
|
||||
<tr>
|
||||
|
||||
<td
|
||||
class="" style="vertical-align:top;width:600px;"
|
||||
>
|
||||
<![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tr>
|
||||
<td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:20px;line-height:1;text-align:center;color:#626262;">
|
||||
{{if .Service.Online}}
|
||||
Online for {{.Service.Uptime.Human}}
|
||||
{{else}}
|
||||
Offline for {{.Service.Downtime.Human}}
|
||||
{{end}}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" vertical-align="middle" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:separate;line-height:100%;">
|
||||
<tr>
|
||||
<td align="center" bgcolor="{{$color}}" role="presentation" style="border:none;border-radius:4px;cursor:auto;mso-padding-alt:10px 25px;background:{{$color}};" valign="middle">
|
||||
<a href="{{.Core.Domain}}/service/{{.Service.Id}}" style="display:inline-block;background:{{$color}};color:#ffffff;font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:13px;font-weight:normal;line-height:120%;margin:0;text-decoration:none;text-transform:none;padding:10px 25px;mso-padding-alt:0px;border-radius:4px;"
|
||||
target="_blank">
|
||||
View Dashboard
|
||||
</a> </td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
<!-- Bottom Graphic -->
|
||||
<tr>
|
||||
<td style="font-size:0px;padding:0px;word-break:break-word;">
|
||||
<!--[if mso | IE]>
|
||||
<table
|
||||
align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
|
||||
>
|
||||
<tr>
|
||||
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
|
||||
<![endif]-->
|
||||
<div style="background:#fafafa;background-color:#fafafa;margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#fafafa;background-color:#fafafa;width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:0px;text-align:center;">
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
|
||||
<tr>
|
||||
|
||||
<td
|
||||
class="" style="vertical-align:top;width:600px;"
|
||||
>
|
||||
<![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tr>
|
||||
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:20px;line-height:1;text-align:left;color:#626262;">Service Domain</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left" style="font-size:0px;padding:10px 25px;padding-top:0px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:14px;line-height:1;text-align:left;color:#626262;">{{.Service.Domain}}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-size:0px;padding:0px;word-break:break-word;">
|
||||
<!--[if mso | IE]>
|
||||
<table
|
||||
align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
|
||||
>
|
||||
<tr>
|
||||
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
|
||||
<![endif]-->
|
||||
<div style="background:#ffffff;background-color:#ffffff;margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#ffffff;background-color:#ffffff;width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:0px;text-align:center;">
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
|
||||
<tr>
|
||||
|
||||
<td
|
||||
class="" style="vertical-align:top;width:600px;"
|
||||
>
|
||||
<![endif]-->
|
||||
{{if .Failure}}
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tr>
|
||||
<td align="left" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:20px;line-height:1;text-align:left;color:#626262;">Current Issue</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="left" style="font-size:0px;padding:10px 25px;padding-top:0px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:14px;line-height:1;text-align:left;color:#626262;">{{.Failure.Issue}}</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
{{end}}
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-size:0px;word-break:break-word;">
|
||||
<!--[if mso | IE]>
|
||||
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td height="30" style="vertical-align:top;height:30px;">
|
||||
|
||||
<![endif]-->
|
||||
<div style="height:30px;"> </div>
|
||||
<!--[if mso | IE]>
|
||||
|
||||
</td></tr></table>
|
||||
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-size:0px;padding:0;word-break:break-word;">
|
||||
<!--[if mso | IE]>
|
||||
<table
|
||||
align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
|
||||
>
|
||||
<tr>
|
||||
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
|
||||
|
||||
<v:rect style="width:600px;" xmlns:v="urn:schemas-microsoft-com:vml" fill="true" stroke="false">
|
||||
<v:fill origin="0.5, 0" position="0.5, 0" src="{{$banner}}" color="#F15822" type="tile" />
|
||||
<v:textbox style="mso-fit-shape-to-text:true" inset="0,0,0,0">
|
||||
<![endif]-->
|
||||
<div style="background:#F15822 url({{$banner}}) top center / auto repeat;margin:0px auto;max-width:600px;">
|
||||
<div style="line-height:0;font-size:0;">
|
||||
<table align="center" background="{{$banner}}" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#F15822 url({{$banner}}) top center / auto repeat;width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:0;text-align:center;">
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
|
||||
<tr>
|
||||
|
||||
<td
|
||||
class="" style="vertical-align:top;width:600px;"
|
||||
>
|
||||
<![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tr>
|
||||
<td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="border-collapse:collapse;border-spacing:0px;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="width:250px;"> <a href="https://www.sphero.com" target="_blank">
|
||||
|
||||
<img
|
||||
height="auto" src="https://assets.statping.com/statpingcom.png" style="border:0;display:block;outline:none;text-decoration:none;height:auto;width:100%;font-size:13px;" width="250"
|
||||
/>
|
||||
|
||||
</a> </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</v:textbox>
|
||||
</v:rect>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-size:0px;padding:20px 0;padding-top:10px;padding-bottom:0;word-break:break-word;">
|
||||
<!--[if mso | IE]>
|
||||
<table
|
||||
align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
|
||||
>
|
||||
<tr>
|
||||
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
|
||||
<![endif]-->
|
||||
<div style="margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;padding-bottom:0;padding-top:10px;text-align:center;">
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
|
||||
<tr>
|
||||
|
||||
<td
|
||||
class="" style="vertical-align:top;width:600px;"
|
||||
>
|
||||
<![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
|
||||
<tr>
|
||||
<td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:11px;line-height:16px;text-align:center;color:#445566;">You are receiving this email because one of your services has changed on your Statping instance. You can modify this email on the Email Notifier page in Settings.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:11px;line-height:16px;text-align:center;color:#445566;">© Statping</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="font-size:0px;padding:20px 0;padding-top:0;padding-bottom:0;word-break:break-word;">
|
||||
<!--[if mso | IE]>
|
||||
<table
|
||||
align="center" border="0" cellpadding="0" cellspacing="0" class="" style="width:600px;" width="600"
|
||||
>
|
||||
<tr>
|
||||
<td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;">
|
||||
<![endif]-->
|
||||
<div style="margin:0px auto;max-width:600px;">
|
||||
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="direction:ltr;font-size:0px;padding:20px 0;padding-bottom:0;padding-top:0;text-align:center;">
|
||||
<!--[if mso | IE]>
|
||||
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
|
||||
|
||||
<tr>
|
||||
|
||||
<td
|
||||
class="" style="width:600px;"
|
||||
>
|
||||
<![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0;line-height:0;text-align:left;display:inline-block;width:100%;direction:ltr;">
|
||||
<!--[if mso | IE]>
|
||||
<table
|
||||
border="0" cellpadding="0" cellspacing="0" role="presentation"
|
||||
>
|
||||
<tr>
|
||||
|
||||
<td
|
||||
style="vertical-align:top;width:600px;"
|
||||
>
|
||||
<![endif]-->
|
||||
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style="vertical-align:top;padding-right:0;">
|
||||
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="" width="100%">
|
||||
<tr>
|
||||
<td align="center" style="font-size:0px;padding:10px 25px;word-break:break-word;">
|
||||
<div style="font-family:Ubuntu, Helvetica, Arial, sans-serif;font-size:11px;font-weight:bold;line-height:16px;text-align:center;color:#445566;"><a class="footer-link" href="https://statping.com">Statping.com</a>         <a class="footer-link" href="https://github.com/statping/statping">Github</a>        
|
||||
<a class="footer-link" href="https://statping.com/privacy">Privacy</a>         <a class="footer-link" href="https://www.google.com">Unsubscribe</a></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
<![endif]-->
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!--[if mso | IE]>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<![endif]-->
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
`
|
|
@ -71,3 +71,8 @@ func (l *lineNotifier) OnTest() (string, error) {
|
|||
_, err := l.sendMessage(msg)
|
||||
return msg, err
|
||||
}
|
||||
|
||||
// OnSave will trigger when this notifier is saved
|
||||
func (l *lineNotifier) OnSave() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
|
|
@ -129,6 +129,11 @@ func (m *mobilePush) Send(pushMessage *pushArray) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// OnSave will trigger when this notifier is saved
|
||||
func (m *mobilePush) OnSave() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func pushRequest(msg *pushArray) ([]byte, error) {
|
||||
body, err := json.Marshal(&PushNotification{[]*pushArray{msg}})
|
||||
if err != nil {
|
||||
|
|
|
@ -16,6 +16,7 @@ type replacer struct {
|
|||
Core *core.Core
|
||||
Service *services.Service
|
||||
Failure *failures.Failure
|
||||
Custom map[string]string
|
||||
}
|
||||
|
||||
func InitNotifiers() {
|
||||
|
@ -30,6 +31,7 @@ func InitNotifiers() {
|
|||
Webhook,
|
||||
Mobile,
|
||||
Pushover,
|
||||
statpingMailer,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -90,3 +90,8 @@ func (t *pushover) OnTest() (string, error) {
|
|||
content, err := t.sendMessage(msg)
|
||||
return content, err
|
||||
}
|
||||
|
||||
// OnSave will trigger when this notifier is saved
|
||||
func (t *pushover) OnSave() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
|
|
@ -86,3 +86,8 @@ func (s *slack) OnSuccess(srv *services.Service) (string, error) {
|
|||
out, err := s.sendSlack(msg)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OnSave will trigger when this notifier is saved
|
||||
func (s *slack) OnSave() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
package notifiers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/notifier"
|
||||
"github.com/statping/statping/types/services"
|
||||
"github.com/statping/statping/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
var _ notifier.Notifier = (*statpingEmailer)(nil)
|
||||
|
||||
const (
|
||||
statpingEmailerName = "statping_emailer"
|
||||
statpingEmailerHost = "https://news.statping.com"
|
||||
)
|
||||
|
||||
type statpingEmailer struct {
|
||||
*notifications.Notification
|
||||
}
|
||||
|
||||
func (s *statpingEmailer) Select() *notifications.Notification {
|
||||
return s.Notification
|
||||
}
|
||||
|
||||
var statpingMailer = &statpingEmailer{¬ifications.Notification{
|
||||
Method: statpingEmailerName,
|
||||
Title: "Email",
|
||||
Description: "Send an email when a service becomes offline or back online using Statping's email service. You will need to verify your email address.",
|
||||
Author: "Hunter Long",
|
||||
AuthorUrl: "https://github.com/hunterlong",
|
||||
Delay: time.Duration(10 * time.Second),
|
||||
Icon: "fas envelope-square",
|
||||
Limits: 60,
|
||||
Form: []notifications.NotificationForm{{
|
||||
Type: "email",
|
||||
Title: "Send to Email Address",
|
||||
Placeholder: "Insert your email address",
|
||||
DbField: "Host",
|
||||
Required: true,
|
||||
}}},
|
||||
}
|
||||
|
||||
// Send will send a HTTP Post to the slack webhooker API. It accepts type: string
|
||||
func (s *statpingEmailer) sendStatpingEmail(msg statpingMail) (string, error) {
|
||||
data, _ := json.Marshal(msg)
|
||||
resp, _, err := utils.HttpRequest(statpingEmailerHost+"/notifier", "POST", "application/json", nil, bytes.NewBuffer(data), time.Duration(10*time.Second), true, nil)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(resp), nil
|
||||
}
|
||||
|
||||
func (s *statpingEmailer) OnTest() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
type statpingMail struct {
|
||||
Email string `json:"email"`
|
||||
Core *core.Core `json:"core,omitempty"`
|
||||
Service *services.Service `json:"service,omitempty"`
|
||||
Failure *failures.Failure `json:"failure,omitempty"`
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (s *statpingEmailer) OnFailure(srv *services.Service, f *failures.Failure) (string, error) {
|
||||
ee := statpingMail{
|
||||
Email: s.Host,
|
||||
Core: core.App,
|
||||
Service: srv,
|
||||
Failure: f,
|
||||
}
|
||||
return s.sendStatpingEmail(ee)
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (s *statpingEmailer) OnSuccess(srv *services.Service) (string, error) {
|
||||
ee := statpingMail{
|
||||
Email: s.Host,
|
||||
Core: core.App,
|
||||
Service: srv,
|
||||
Failure: nil,
|
||||
}
|
||||
return s.sendStatpingEmail(ee)
|
||||
}
|
||||
|
||||
// OnSave will trigger when this notifier is saved
|
||||
func (s *statpingEmailer) OnSave() (string, error) {
|
||||
ee := statpingMail{
|
||||
Email: s.Host,
|
||||
Core: core.App,
|
||||
Service: nil,
|
||||
Failure: nil,
|
||||
}
|
||||
out, err := s.sendStatpingEmail(ee)
|
||||
log.Println("statping emailer response", out)
|
||||
return out, err
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package notifiers
|
||||
|
||||
import (
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/null"
|
||||
"github.com/statping/statping/types/services"
|
||||
"github.com/statping/statping/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
testEmail string
|
||||
)
|
||||
|
||||
func TestStatpingEmailerNotifier(t *testing.T) {
|
||||
err := utils.InitLogs()
|
||||
require.Nil(t, err)
|
||||
db, err := database.OpenTester()
|
||||
require.Nil(t, err)
|
||||
db.AutoMigrate(¬ifications.Notification{})
|
||||
notifications.SetDB(db)
|
||||
|
||||
testEmail = utils.Params.GetString("TEST_EMAIL")
|
||||
statpingMailer.Host = testEmail
|
||||
statpingMailer.Enabled = null.NewNullBool(true)
|
||||
|
||||
if testEmail == "" {
|
||||
t.Log("statping email notifier testing skipped, missing TEST_EMAIL environment variable")
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
t.Run("Load statping emailer", func(t *testing.T) {
|
||||
statpingMailer.Host = testEmail
|
||||
statpingMailer.Delay = time.Duration(100 * time.Millisecond)
|
||||
statpingMailer.Limits = 3
|
||||
Add(statpingMailer)
|
||||
assert.Equal(t, "Hunter Long", statpingMailer.Author)
|
||||
assert.Equal(t, testEmail, statpingMailer.Host)
|
||||
})
|
||||
|
||||
t.Run("statping emailer Within Limits", func(t *testing.T) {
|
||||
ok := statpingMailer.CanSend()
|
||||
assert.True(t, ok)
|
||||
})
|
||||
|
||||
t.Run("statping emailer OnFailure", func(t *testing.T) {
|
||||
_, err := statpingMailer.OnFailure(services.Example(false), failures.Example())
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("statping emailer OnSuccess", func(t *testing.T) {
|
||||
_, err := statpingMailer.OnSuccess(services.Example(true))
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
}
|
|
@ -94,6 +94,11 @@ func (t *telegram) OnTest() (string, error) {
|
|||
return content, err
|
||||
}
|
||||
|
||||
// OnSave will trigger when this notifier is saved
|
||||
func (t *telegram) OnSave() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func telegramSuccess(res []byte) (bool, telegramResponse) {
|
||||
var obj telegramResponse
|
||||
json.Unmarshal(res, &obj)
|
||||
|
|
|
@ -107,6 +107,11 @@ func (t *twilio) OnTest() (string, error) {
|
|||
return t.sendMessage(msg)
|
||||
}
|
||||
|
||||
// OnSave will trigger when this notifier is saved
|
||||
func (t *twilio) OnSave() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func twilioSuccess(res []byte) (bool, twilioResponse) {
|
||||
var obj twilioResponse
|
||||
json.Unmarshal(res, &obj)
|
||||
|
|
|
@ -148,3 +148,8 @@ func (w *webhooker) OnSuccess(s *services.Service) (string, error) {
|
|||
content, err := ioutil.ReadAll(resp.Body)
|
||||
return string(content), err
|
||||
}
|
||||
|
||||
// OnSave will trigger when this notifier is saved
|
||||
func (w *webhooker) OnSave() (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
|
|
@ -10,4 +10,5 @@ type Notifier interface {
|
|||
OnSuccess(*services.Service) (string, error) // OnSuccess is triggered when a service is successful
|
||||
OnFailure(*services.Service, *failures.Failure) (string, error) // OnFailure is triggered when a service is failing
|
||||
OnTest() (string, error) // OnTest is triggered for testing
|
||||
OnSave() (string, error) // OnSave is triggered for when saved
|
||||
}
|
||||
|
|
|
@ -326,18 +326,13 @@ func (s *Service) OnlineSince(ago time.Time) float32 {
|
|||
return s.Online24Hours
|
||||
}
|
||||
|
||||
// Downtime returns the amount of time of a offline service
|
||||
func (s *Service) Downtime() time.Duration {
|
||||
hit := s.LastHit()
|
||||
fail := s.AllFailures().Last()
|
||||
if hit == nil {
|
||||
return time.Duration(0)
|
||||
}
|
||||
if fail == nil {
|
||||
return utils.Now().Sub(hit.CreatedAt)
|
||||
}
|
||||
func (s *Service) Uptime() utils.Duration {
|
||||
return utils.Duration{Duration: utils.Now().Sub(s.LastOffline)}
|
||||
}
|
||||
|
||||
return fail.CreatedAt.Sub(hit.CreatedAt)
|
||||
// Downtime returns the amount of time of a offline service
|
||||
func (s *Service) Downtime() utils.Duration {
|
||||
return utils.Duration{Duration: utils.Now().Sub(s.LastOnline)}
|
||||
}
|
||||
|
||||
// ServiceOrder will reorder the services based on 'order_id' (Order)
|
||||
|
|
|
@ -29,5 +29,6 @@ type ServiceNotifier interface {
|
|||
OnSuccess(*Service) (string, error) // OnSuccess is triggered when a service is successful
|
||||
OnFailure(*Service, *failures.Failure) (string, error) // OnFailure is triggered when a service is failing
|
||||
OnTest() (string, error) // OnTest is triggered for testing
|
||||
OnSave() (string, error) // OnSave is triggered for testing
|
||||
Select() *notifications.Notification // OnTest is triggered for testing
|
||||
}
|
||||
|
|
|
@ -10,14 +10,14 @@ func Example(online bool) *Service {
|
|||
return &Service{
|
||||
Id: 6283,
|
||||
Name: "Statping Example",
|
||||
Domain: "https://localhost:8080",
|
||||
Domain: "https://statping.com",
|
||||
Expected: null.NewNullString(""),
|
||||
ExpectedStatus: 200,
|
||||
Interval: int(time.Duration(15 * time.Second).Seconds()),
|
||||
Type: "http",
|
||||
Method: "get",
|
||||
PostData: null.NullString{},
|
||||
Port: 0,
|
||||
Port: 443,
|
||||
Timeout: int(time.Duration(2 * time.Second).Seconds()),
|
||||
Order: 0,
|
||||
VerifySSL: null.NewNullBool(true),
|
||||
|
@ -46,7 +46,7 @@ func Example(online bool) *Service {
|
|||
AllowNotifications: null.NewNullBool(true),
|
||||
UserNotified: false,
|
||||
UpdateNotify: null.NewNullBool(true),
|
||||
DownText: "The service ws responding with 500 status code",
|
||||
DownText: "The service was responding with 500 status code",
|
||||
SuccessNotified: false,
|
||||
LastStatusCode: 200,
|
||||
Failures: nil,
|
||||
|
@ -55,9 +55,7 @@ func Example(online bool) *Service {
|
|||
LastLatency: 124399,
|
||||
LastCheck: utils.Now().Add(-37 * time.Second),
|
||||
LastOnline: utils.Now().Add(-37 * time.Second),
|
||||
LastOffline: utils.Now().Add((-14 * 24) * time.Hour),
|
||||
SecondsOnline: int64(utils.Now().Add(-24 * time.Hour).Second()),
|
||||
SecondsOffline: int64(utils.Now().Add(-150 * time.Second).Second()),
|
||||
LastOffline: utils.Now().Add(-75 * time.Second),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -432,7 +432,7 @@ func TestServices(t *testing.T) {
|
|||
item, err := Find(1)
|
||||
require.Nil(t, err)
|
||||
amount := item.Downtime().Seconds()
|
||||
assert.Equal(t, "25", fmt.Sprintf("%0.f", amount))
|
||||
assert.Equal(t, "75", fmt.Sprintf("%0.f", amount))
|
||||
})
|
||||
|
||||
t.Run("Test Failures Since", func(t *testing.T) {
|
||||
|
|
|
@ -59,9 +59,6 @@ type Service struct {
|
|||
LastOnline time.Time `gorm:"-" json:"last_success" yaml:"-"`
|
||||
LastOffline time.Time `gorm:"-" json:"last_error" yaml:"-"`
|
||||
Stats *Stats `gorm:"-" json:"stats,omitempty" yaml:"-"`
|
||||
|
||||
SecondsOnline int64 `gorm:"-" json:"-" yaml:"-"`
|
||||
SecondsOffline int64 `gorm:"-" json:"-" yaml:"-"`
|
||||
}
|
||||
|
||||
type Stats struct {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hako/durafmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -10,58 +10,17 @@ func Now() time.Time {
|
|||
return time.Now().UTC()
|
||||
}
|
||||
|
||||
type Duration struct {
|
||||
time.Duration
|
||||
}
|
||||
|
||||
func (d Duration) Human() string {
|
||||
return durafmt.Parse(d.Duration).LimitFirstN(2).String()
|
||||
}
|
||||
|
||||
// FormatDuration converts a time.Duration into a string
|
||||
func FormatDuration(d time.Duration) string {
|
||||
var out string
|
||||
if d.Hours() >= 24 {
|
||||
out = fmt.Sprintf("%0.0f day", d.Hours()/24)
|
||||
if (d.Hours() / 24) >= 2 {
|
||||
out += "s"
|
||||
}
|
||||
return out
|
||||
} else if d.Hours() >= 1 {
|
||||
out = fmt.Sprintf("%0.0f hour", d.Hours())
|
||||
if d.Hours() >= 2 {
|
||||
out += "s"
|
||||
}
|
||||
return out
|
||||
} else if d.Minutes() >= 1 {
|
||||
out = fmt.Sprintf("%0.0f minute", d.Minutes())
|
||||
if d.Minutes() >= 2 {
|
||||
out += "s"
|
||||
}
|
||||
return out
|
||||
} else if d.Seconds() >= 1 {
|
||||
out = fmt.Sprintf("%0.0f second", d.Seconds())
|
||||
if d.Seconds() >= 2 {
|
||||
out += "s"
|
||||
}
|
||||
return out
|
||||
} else if rev(d.Hours()) >= 24 {
|
||||
out = fmt.Sprintf("%0.0f day", rev(d.Hours()/24))
|
||||
if rev(d.Hours()/24) >= 2 {
|
||||
out += "s"
|
||||
}
|
||||
return out
|
||||
} else if rev(d.Hours()) >= 1 {
|
||||
out = fmt.Sprintf("%0.0f hour", rev(d.Hours()))
|
||||
if rev(d.Hours()) >= 2 {
|
||||
out += "s"
|
||||
}
|
||||
return out
|
||||
} else if rev(d.Minutes()) >= 1 {
|
||||
out = fmt.Sprintf("%0.0f minute", rev(d.Minutes()))
|
||||
if rev(d.Minutes()) >= 2 {
|
||||
out += "s"
|
||||
}
|
||||
return out
|
||||
} else {
|
||||
out = fmt.Sprintf("%0.0f second", rev(d.Seconds()))
|
||||
if rev(d.Seconds()) >= 2 {
|
||||
out += "s"
|
||||
}
|
||||
}
|
||||
return out
|
||||
return durafmt.ParseShort(d).LimitFirstN(3).String()
|
||||
}
|
||||
|
||||
func rev(f float64) float64 {
|
||||
|
|
|
@ -89,9 +89,9 @@ func TestDeleteFile(t *testing.T) {
|
|||
|
||||
func TestFormatDuration(t *testing.T) {
|
||||
dur, _ := time.ParseDuration("158s")
|
||||
assert.Equal(t, "3 minutes", FormatDuration(dur))
|
||||
assert.Equal(t, "2 minutes 38 seconds", FormatDuration(dur))
|
||||
dur, _ = time.ParseDuration("-65s")
|
||||
assert.Equal(t, "1 minute", FormatDuration(dur))
|
||||
assert.Equal(t, "-1 minute 5 seconds", FormatDuration(dur))
|
||||
dur, _ = time.ParseDuration("3s")
|
||||
assert.Equal(t, "3 seconds", FormatDuration(dur))
|
||||
dur, _ = time.ParseDuration("48h")
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.90.54
|
||||
0.90.55
|
||||
|
|
Loading…
Reference in New Issue