mirror of https://github.com/statping/statping
Merge branch 'master' into feature/tls-renegotiation
commit
274b82c3b6
|
@ -0,0 +1,19 @@
|
|||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 60
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- bug
|
||||
- urgent
|
||||
- pinned
|
||||
- security
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions. If this is still an problem, please create a new issue.
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: true
|
|
@ -10,13 +10,13 @@ jobs:
|
|||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@master
|
||||
- name: Create a Sentry.io release
|
||||
- name: Create a Sentry release
|
||||
uses: tclindner/sentry-releases-action@v1.0.0
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||
SENTRY_ORG: Statping
|
||||
SENTRY_PROJECT: golang
|
||||
SENTRY_ORG: statping
|
||||
SENTRY_PROJECT: backend
|
||||
with:
|
||||
tagName: ${{ github.ref }}
|
||||
environment: qa
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
name: ReleaseFrontendWorkflow
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published, prereleased]
|
||||
|
||||
jobs:
|
||||
createSentryRelease:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@master
|
||||
- name: Create a Sentry Frontend release
|
||||
uses: tclindner/sentry-releases-action@v1.0.0
|
||||
env:
|
||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_FRONTEND_AUTH_TOKEN }}
|
||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||
SENTRY_ORG: statping
|
||||
SENTRY_PROJECT: frontend
|
||||
with:
|
||||
tagName: ${{ github.ref }}
|
||||
environment: qa
|
|
@ -10,12 +10,6 @@ before_script:
|
|||
branches:
|
||||
only:
|
||||
- master
|
||||
cache:
|
||||
directories:
|
||||
- ~/.npm
|
||||
- ~/.cache
|
||||
- $GOPATH/pkg/mod
|
||||
- $GOPATH/src/github.com/statping/statping/frontend/node_modules
|
||||
env:
|
||||
global:
|
||||
- "PATH=$HOME/.local/bin:$PATH"
|
||||
|
@ -23,7 +17,6 @@ env:
|
|||
- DB_USER=travis
|
||||
- DB_PASS=
|
||||
- DB_DATABASE=test
|
||||
- GO_ENV=test
|
||||
- STATPING_DIR=$GOPATH/src/github.com/statping/statping
|
||||
go: 1.14
|
||||
go_import_path: github.com/statping/statping
|
||||
|
@ -36,6 +29,7 @@ install:
|
|||
- "make test-deps yarn clean compile install"
|
||||
language: go
|
||||
addons:
|
||||
chrome: stable
|
||||
apt:
|
||||
packages:
|
||||
- libgconf-2-4
|
||||
|
|
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,7 +1,19 @@
|
|||
# 0.90.25
|
||||
- Added string response on OnTest for Notifiers
|
||||
- Modified UI to show user the response for a Notifier.
|
||||
- Modified some Notifiers title's
|
||||
- Added more Cypress e2e testing
|
||||
|
||||
# 0.90.24
|
||||
- Fixed login form from not showing
|
||||
|
||||
# 0.90.23
|
||||
- Added Incident Reporting
|
||||
- Added Cypress tests
|
||||
- Added Github and Google OAuth login (beta)
|
||||
- Added Delete All Failures
|
||||
- Added Checkin form
|
||||
- Added Pushover notifier
|
||||
|
||||
# 0.90.22
|
||||
- Added range input types for integer form fields
|
||||
|
|
11
Makefile
11
Makefile
|
@ -34,7 +34,7 @@ test: clean
|
|||
release: test-deps
|
||||
wget -O statping.gpg $(SIGN_URL)
|
||||
gpg --import statping.gpg
|
||||
make build-all
|
||||
make build-all upload_to_s3
|
||||
|
||||
test-ci: clean compile test-deps
|
||||
SASS=`which sass` go test -v -covermode=count -coverprofile=coverage.out -p=1 ./...
|
||||
|
@ -260,11 +260,10 @@ publish-dev:
|
|||
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:
|
||||
aws s3 cp ./source/css $(ASSETS_BKT) --recursive --exclude "*" --include "*.css"
|
||||
aws s3 cp ./source/js $(ASSETS_BKT) --recursive --exclude "*" --include "*.js"
|
||||
aws s3 cp ./source/font $(ASSETS_BKT) --recursive --exclude "*" --include "*.eot" --include "*.svg" --include "*.woff" --include "*.woff2" --include "*.ttf" --include "*.css"
|
||||
aws s3 cp ./source/scss $(ASSETS_BKT) --recursive --exclude "*" --include "*.scss"
|
||||
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)
|
||||
|
||||
travis_s3_creds:
|
||||
|
|
|
@ -132,6 +132,7 @@ func TestAssetsCLI(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestSassCLI(t *testing.T) {
|
||||
t.SkipNow()
|
||||
catchCLI([]string{"sass"})
|
||||
assert.FileExists(t, dir+"/assets/css/main.css")
|
||||
assert.FileExists(t, dir+"/assets/css/style.css")
|
||||
|
|
13
cmd/main.go
13
cmd/main.go
|
@ -28,8 +28,7 @@ var (
|
|||
verboseMode int
|
||||
port int
|
||||
log = utils.Log.WithField("type", "cmd")
|
||||
|
||||
confgs *configs.DbConfig
|
||||
confgs *configs.DbConfig
|
||||
)
|
||||
|
||||
// parseFlags will parse the application flags
|
||||
|
@ -75,8 +74,6 @@ func main() {
|
|||
|
||||
parseFlags()
|
||||
|
||||
utils.SentryInit(VERSION)
|
||||
|
||||
if err := source.Assets(); err != nil {
|
||||
exit(err)
|
||||
}
|
||||
|
@ -202,18 +199,14 @@ func InitApp() error {
|
|||
if _, err := core.Select(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if _, err := services.SelectAllServices(true); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
go services.CheckServices()
|
||||
|
||||
notifiers.InitNotifiers()
|
||||
|
||||
go database.Maintenance()
|
||||
utils.SentryInit(&VERSION, core.App.AllowReports.Bool)
|
||||
core.App.Setup = true
|
||||
core.App.Started = utils.Now()
|
||||
|
||||
go database.Maintenance()
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -8,10 +8,9 @@
|
|||
"DB_PASS": "password123",
|
||||
"GO_ENV": "production"
|
||||
},
|
||||
"baseUrl": "http://localhost:8888",
|
||||
"chromeWebSecurity": false,
|
||||
"defaultCommandTimeout": 15000,
|
||||
"requestTimeout": 15000,
|
||||
"defaultCommandTimeout": 30000,
|
||||
"requestTimeout": 30000,
|
||||
"watchForFileChanges": false,
|
||||
"failOnStatusCode": false
|
||||
}
|
||||
|
|
|
@ -2,6 +2,12 @@
|
|||
|
||||
context('Setup Process', () => {
|
||||
|
||||
it('should be not be setup yet', () => {
|
||||
cy.request(`/api`).then((response) => {
|
||||
expect(response.body).to.have.property('setup', false)
|
||||
})
|
||||
})
|
||||
|
||||
it('should setup Statping with SQLite', () => {
|
||||
cy.visit('/setup', {failOnStatusCode: false})
|
||||
cy.get('#db_connection').select('sqlite')
|
||||
|
@ -19,8 +25,34 @@ context('Setup Process', () => {
|
|||
|
||||
it('should have sample data', () => {
|
||||
cy.visit('/')
|
||||
cy.get('#title').should('contain', 'Demo Tester')
|
||||
cy.get('#description').should('contain', 'This is a test from Crypress!')
|
||||
cy.get('.card').should('have.length', 5)
|
||||
cy.get('.group_header').should('have.length', 2)
|
||||
})
|
||||
|
||||
it('should be completely setup', () => {
|
||||
cy.request(`/api`).then((response) => {
|
||||
expect(response.body).to.have.property('setup', true)
|
||||
expect(response.body).to.have.property('domain', 'http://localhost:8888')
|
||||
})
|
||||
})
|
||||
|
||||
it('should be able to Login', () => {
|
||||
cy.visit('/login')
|
||||
cy.get('#username').clear().type('admin')
|
||||
cy.get('#password').clear().type('admin')
|
||||
cy.get('button[type="submit"]').click()
|
||||
|
||||
cy.get('.navbar-brand').should('contain', 'Statping')
|
||||
cy.getCookies()
|
||||
|
||||
cy.getCookies().should('have.length', 1)
|
||||
|
||||
cy.request(`/api`).then((response) => {
|
||||
expect(response.body).to.have.property('admin', true)
|
||||
expect(response.body).to.have.property('logged_in', true)
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
@ -29,7 +29,7 @@ context('Groups Tests', () => {
|
|||
cy.visit('/dashboard/services')
|
||||
cy.get('.sortable_groups > tr').should('have.length', 3)
|
||||
|
||||
cy.get('.sortable_groups > tr').eq(0).contains('PRIVATE')
|
||||
cy.get('.sortable_groups > tr').eq(0).contains('PUBLIC')
|
||||
cy.get('.sortable_groups > tr').eq(1).contains('PUBLIC')
|
||||
cy.get('.sortable_groups > tr').eq(2).contains('PRIVATE')
|
||||
})
|
||||
|
@ -48,17 +48,28 @@ context('Groups Tests', () => {
|
|||
cy.get('button[type="submit"]').click()
|
||||
})
|
||||
|
||||
it('should edit Group', () => {
|
||||
cy.visit('/dashboard/services')
|
||||
cy.get('.sortable_groups > tr').eq(0).find('.btn-outline-secondary').click()
|
||||
cy.get('#title').should('have.value', 'Test Group')
|
||||
cy.get('#title').clear().type('Updated Group')
|
||||
cy.get('button[type="submit"]').click()
|
||||
|
||||
cy.get('.sortable_groups > tr').eq(0).contains('Updated Group')
|
||||
})
|
||||
|
||||
it('should confirm new groups', () => {
|
||||
cy.visit('/dashboard/services')
|
||||
cy.get('.sortable_groups > tr').should('have.length', 5)
|
||||
|
||||
cy.get('.sortable_groups > tr').eq(0).contains('PUBLIC')
|
||||
cy.get('.sortable_groups > tr').eq(0).contains('Test Group')
|
||||
cy.get('.sortable_groups > tr').eq(0).contains('Updated Group')
|
||||
cy.get('.sortable_groups > tr').eq(1).contains('PRIVATE')
|
||||
cy.get('.sortable_groups > tr').eq(1).contains('Test Private Group')
|
||||
})
|
||||
|
||||
it('should delete new groups', () => {
|
||||
cy.visit('/dashboard/services')
|
||||
cy.get('.sortable_groups > tr').eq(0).find('.btn-danger').click()
|
||||
cy.get('.sortable_groups > tr').eq(1).find('.btn-danger').click()
|
||||
cy.get('.sortable_groups > tr').should('have.length', 3)
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import "../support/commands"
|
||||
|
||||
context('Messages Tests', () => {
|
||||
context('Annoucements Tests', () => {
|
||||
|
||||
|
||||
beforeEach(() => {
|
||||
|
@ -43,4 +43,10 @@ context('Messages Tests', () => {
|
|||
cy.get('tbody > tr').should('have.length', 3)
|
||||
})
|
||||
|
||||
it('should confirm delete Message', () => {
|
||||
cy.visit('/dashboard/messages')
|
||||
cy.get('tbody > tr').eq(0).find('.btn-danger').click()
|
||||
cy.get('tbody > tr').should('have.length', 2)
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
@ -25,12 +25,26 @@ context('Notifier Tests', () => {
|
|||
cy.getCookies().should('have.length', 1)
|
||||
})
|
||||
|
||||
// uzrwstmtd69hi4wgzsj27q2v29mtpu
|
||||
it('should confirm notifiers are installed', () => {
|
||||
cy.visit('/dashboard/settings')
|
||||
cy.get('#notifiers_tabs > a').should('have.length', 9)
|
||||
cy.get('#notifiers_tabs > a').should('have.length', 10)
|
||||
|
||||
cy.get('#api_key').should('not.have.value', '')
|
||||
cy.get('#api_secret').should('not.have.value', '')
|
||||
})
|
||||
|
||||
// it('should test and save notifier', () => {
|
||||
// cy.visit('/dashboard/settings')
|
||||
// cy.get('#notifiers_tabs > a').should('have.length', 10)
|
||||
// cy.get('#notifiers_tabs > #v-pills-command-tab').click()
|
||||
//
|
||||
// cy.get('#v-pills-command-tab > .form-control').eq(0).clear().type('/bin/echo')
|
||||
// cy.get('#v-pills-command-tab > .form-control').eq(1).clear().type('"success"')
|
||||
// cy.get('#v-pills-command-tab > .form-control').eq(2).clear().type('"failure"')
|
||||
//
|
||||
// cy.get('#v-pills-command-tab').find(".save-notifier").click()
|
||||
// cy.get('#v-pills-command-tab').find(".test-notifier").click()
|
||||
// })
|
||||
|
||||
})
|
||||
|
|
|
@ -26,7 +26,7 @@ context('Services Tests', () => {
|
|||
|
||||
it('should goto services', () => {
|
||||
cy.visit('/dashboard/services')
|
||||
cy.get('#services_list > tr').should('have.length', 5)
|
||||
cy.get('#services_list > tr').should('have.length', 6)
|
||||
cy.get('.sortable_groups > tr').should('have.length', 3)
|
||||
})
|
||||
|
||||
|
@ -79,7 +79,7 @@ context('Services Tests', () => {
|
|||
|
||||
it('should create new ICMP service', () => {
|
||||
cy.visit('/dashboard/create_service')
|
||||
cy.get('#name').clear().type('ICMP Service')
|
||||
cy.get('#name').clear().type('ICMP Service')
|
||||
cy.get('#service_type').select('icmp')
|
||||
cy.get('#service_url').clear().type('8.8.8.8')
|
||||
|
||||
|
@ -93,16 +93,20 @@ context('Services Tests', () => {
|
|||
|
||||
it('should confirm new services', () => {
|
||||
cy.visit('/dashboard/services')
|
||||
cy.get('#services_list > tr').should('have.length', 9)
|
||||
cy.get('#services_list > tr').should('have.length', 10)
|
||||
})
|
||||
|
||||
it('should delete new services', () => {
|
||||
cy.visit('/dashboard/services')
|
||||
cy.get('#services_list > tr').eq(0).find('.btn-danger').click()
|
||||
cy.get('#services_list > tr').eq(0).find('.btn-danger').click()
|
||||
cy.get('#services_list > tr').eq(0).find('.btn-danger').click()
|
||||
cy.get('#services_list > tr').eq(0).find('.btn-danger').click()
|
||||
cy.get('#services_list > tr').should('have.length', 4)
|
||||
cy.get('#services_list > tr').should('have.length', 10)
|
||||
cy.get('#services_list > tr').eq(0).find('a.btn-danger').click()
|
||||
cy.get('#services_list > tr').should('have.length', 9)
|
||||
cy.get('#services_list > tr').eq(1).find('a.btn-danger').click()
|
||||
cy.get('#services_list > tr').should('have.length', 8)
|
||||
cy.get('#services_list > tr').eq(2).find('a.btn-danger').click()
|
||||
cy.get('#services_list > tr').should('have.length', 7)
|
||||
cy.get('#services_list > tr').eq(3).find('a.btn-danger').click()
|
||||
cy.get('#services_list > tr').should('have.length', 6)
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
@ -27,7 +27,7 @@ context('Settings Tests', () => {
|
|||
|
||||
it('should confirm notifiers are installed', () => {
|
||||
cy.visit('/dashboard/settings')
|
||||
cy.get('#notifiers_tabs > a').should('have.length', 9)
|
||||
cy.get('#notifiers_tabs > a').should('have.length', 10)
|
||||
|
||||
cy.get('#api_key').should('not.have.value', '')
|
||||
cy.get('#api_secret').should('not.have.value', '')
|
||||
|
@ -36,7 +36,7 @@ context('Settings Tests', () => {
|
|||
it('should update Statping settings', () => {
|
||||
cy.visit('/dashboard/settings')
|
||||
|
||||
cy.get('#project').clear().type('Statping Cypress Tests')
|
||||
cy.get('#project').clear().type('Statping Updated')
|
||||
cy.get('#description').clear().type('Statping can use Cypress e2e testing to make it more stable!')
|
||||
cy.get('#domain').clear().type('http://localhost:8888')
|
||||
cy.get('#footer').clear().type('Statping Custom Footer')
|
||||
|
@ -46,7 +46,7 @@ context('Settings Tests', () => {
|
|||
it('should confirm Statping settings', () => {
|
||||
cy.visit('/dashboard/settings')
|
||||
|
||||
cy.get('#project').should('have.value', 'Statping Cypress Tests')
|
||||
cy.get('#project').should('have.value', 'Statping Updated')
|
||||
cy.get('#description').should('have.value', 'Statping can use Cypress e2e testing to make it more stable!')
|
||||
cy.get('#domain').should('have.value', 'http://localhost:8888')
|
||||
cy.get('#footer').should('have.value', 'Statping Custom Footer')
|
||||
|
@ -59,6 +59,13 @@ context('Settings Tests', () => {
|
|||
cy.get('.footer').should('contain', 'Statping Custom Footer')
|
||||
})
|
||||
|
||||
it('should regenerate API Keys', () => {
|
||||
cy.visit('/dashboard/settings')
|
||||
cy.get('#regenkeys').click()
|
||||
cy.get('#api_key').should('not.have.value', '')
|
||||
cy.get('#api_secret').should('not.have.value', '')
|
||||
})
|
||||
|
||||
it('should create Local Assets', () => {
|
||||
cy.visit('/dashboard/settings')
|
||||
cy.get('#v-pills-style-tab').click()
|
||||
|
|
|
@ -39,13 +39,39 @@ context('Users Tests', () => {
|
|||
cy.get('#password_confirm').clear().type('password123')
|
||||
|
||||
cy.get('button[type="submit"]').click()
|
||||
cy.get('#users_table > tr').should('have.length', 2)
|
||||
})
|
||||
|
||||
// it('should confirm new user', () => {
|
||||
// cy.visit('/dashboard/users')
|
||||
// cy.get('#users_table > tr').should('have.length', 2)
|
||||
// cy.get('#users_table > tr').eq(0).contains('admin')
|
||||
// cy.get('#users_table > tr').eq(1).contains('admin2')
|
||||
// })
|
||||
it('should create new Admin User', () => {
|
||||
cy.visit('/dashboard/users')
|
||||
cy.get('#username').clear().type('admin3')
|
||||
cy.get('#admin_switch').click()
|
||||
cy.get('#email').clear().type('info@admin3.com')
|
||||
cy.get('#password').clear().type('password123')
|
||||
cy.get('#password_confirm').clear().type('password123')
|
||||
|
||||
cy.get('button[type="submit"]').click()
|
||||
cy.get('#users_table > tr').should('have.length', 3)
|
||||
})
|
||||
|
||||
it('should confirm new user', () => {
|
||||
cy.visit('/dashboard/users')
|
||||
cy.get('#users_table > tr').should('have.length', 3)
|
||||
cy.get('#users_table > tr').eq(0).contains('admin')
|
||||
cy.get('#users_table > tr').eq(1).contains('admin2')
|
||||
cy.get('#users_table > tr').eq(2).contains('admin3')
|
||||
|
||||
cy.get('#users_table > tr').eq(0).contains('ADMIN')
|
||||
cy.get('#users_table > tr').eq(1).contains('USER')
|
||||
cy.get('#users_table > tr').eq(2).contains('ADMIN')
|
||||
})
|
||||
|
||||
it('should delete new users', () => {
|
||||
cy.visit('/dashboard/users')
|
||||
cy.get('#users_table > tr').should('have.length', 3)
|
||||
cy.get('#users_table > tr').eq(2).find('a.btn-danger').click()
|
||||
cy.get('#users_table > tr').eq(1).find('a.btn-danger').click()
|
||||
cy.get('#users_table > tr').should('have.length', 1)
|
||||
})
|
||||
|
||||
})
|
||||
|
|
|
@ -18,4 +18,12 @@
|
|||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
on("before:browser:launch", (browser = {}, args) => {
|
||||
if (browser.name === "chrome") {
|
||||
// ^ make sure this is your browser name, you may
|
||||
// be using 'canary' or 'chromium' for example, so change it to match!
|
||||
args.push("--proxy-bypass-list=<-loopback>");
|
||||
return args;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@
|
|||
"lint": "vue-cli-service lint",
|
||||
"backend-test": "newman run -e ../dev/postman_environment.json --delay-request 500 ../dev/postman.json",
|
||||
"cypress:open": "cypress open",
|
||||
"cypress:test": "cypress run --record --key 49d99e5e-04c6-46df-beef-54b68e152a4d",
|
||||
"test": "start-server-and-test start http://0.0.0.0:8888/api cypress:test",
|
||||
"start": "statping -port 8888 > /dev/null 2>&1"
|
||||
"cypress:test": "cypress run --browser chrome --record false --key $CYPRESS_KEY",
|
||||
"test": "start-server-and-test start http://localhost:8080/api cypress:test",
|
||||
"start": "statping -port 8080"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free-solid": "^5.1.0-3",
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
import Vue from "vue";
|
||||
import axios from 'axios'
|
||||
import * as Sentry from "@sentry/browser";
|
||||
import * as Integrations from "@sentry/integrations";
|
||||
const qs = require('querystring');
|
||||
|
||||
const qs = require('querystring')
|
||||
const tokenKey = "statping_user";
|
||||
const errorReporter = "https://bed4d75404924cb3a799e370733a1b64@sentry.statping.com/3"
|
||||
|
||||
class Api {
|
||||
constructor() {
|
||||
|
@ -9,7 +13,11 @@ class Api {
|
|||
}
|
||||
|
||||
async core() {
|
||||
return axios.get('api').then(response => (response.data))
|
||||
const core = axios.get('api').then(response => (response.data))
|
||||
if (core.allow_reports) {
|
||||
await this.sentry_init()
|
||||
}
|
||||
return core
|
||||
}
|
||||
|
||||
async core_save(obj) {
|
||||
|
@ -68,6 +76,10 @@ class Api {
|
|||
return axios.post('api/reorder/services', data).then(response => (response.data))
|
||||
}
|
||||
|
||||
async checkins() {
|
||||
return axios.get('api/checkins').then(response => (response.data))
|
||||
}
|
||||
|
||||
async groups() {
|
||||
return axios.get('api/groups').then(response => (response.data))
|
||||
}
|
||||
|
@ -114,7 +126,7 @@ class Api {
|
|||
}
|
||||
|
||||
async incident_update_delete(update) {
|
||||
return axios.post('api/incidents/'+incident.id+'/updates', data).then(response => (response.data))
|
||||
return axios.delete('api/incidents/'+update.incident+'/updates/'+update.id).then(response => (response.data))
|
||||
}
|
||||
|
||||
async incidents_service(service) {
|
||||
|
@ -129,6 +141,14 @@ class Api {
|
|||
return axios.delete('api/incidents/'+incident.id).then(response => (response.data))
|
||||
}
|
||||
|
||||
async checkin_create(data) {
|
||||
return axios.post('api/checkins', data).then(response => (response.data))
|
||||
}
|
||||
|
||||
async checkin_delete(checkin) {
|
||||
return axios.delete('api/checkins/'+checkin.api_key).then(response => (response.data))
|
||||
}
|
||||
|
||||
async messages() {
|
||||
return axios.get('api/messages').then(response => (response.data))
|
||||
}
|
||||
|
@ -246,6 +266,13 @@ class Api {
|
|||
await axios.all([all])
|
||||
}
|
||||
|
||||
async sentry_init() {
|
||||
Sentry.init({
|
||||
dsn: errorReporter,
|
||||
integrations: [new Integrations.Vue({Vue, attachProps: true})],
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
const api = new Api()
|
||||
export default api
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<router-view :app="app" :loaded="loaded"/>
|
||||
<Footer :logged_in="logged_in" :version="version" v-if="$route.path !== '/setup'"/>
|
||||
<router-view :loaded="loaded"/>
|
||||
<Footer v-if="$route.path !== '/setup'"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -18,35 +18,38 @@
|
|||
return {
|
||||
loaded: false,
|
||||
version: "",
|
||||
logged_in: false,
|
||||
app: null
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.app = await this.$store.dispatch('loadRequired')
|
||||
computed: {
|
||||
core() {
|
||||
return this.$store.getters.core
|
||||
}
|
||||
},
|
||||
async beforeMount() {
|
||||
await this.$store.dispatch('loadCore')
|
||||
|
||||
this.app = {...this.$store.state}
|
||||
|
||||
if (this.$store.getters.core.logged_in) {
|
||||
await this.$store.dispatch('loadAdmin')
|
||||
}
|
||||
this.loaded = true
|
||||
if (!this.$store.getters.core.setup) {
|
||||
if (!this.core.setup) {
|
||||
this.$router.push('/setup')
|
||||
}
|
||||
window.console.log('finished loadRequired')
|
||||
if (this.$route.path !== '/setup') {
|
||||
if (this.core.logged_in) {
|
||||
await this.$store.dispatch('loadAdmin')
|
||||
} else {
|
||||
await this.$store.dispatch('loadRequired')
|
||||
}
|
||||
this.loaded = true
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
async mounted() {
|
||||
if (this.$route.path !== '/setup') {
|
||||
const tk = localStorage.getItem("statping_user")
|
||||
if (this.$store.getters.core.logged_in) {
|
||||
if (this.core.logged_in) {
|
||||
this.logged_in = true
|
||||
await this.$store.dispatch('loadAdmin')
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -15,13 +15,27 @@ HTML,BODY {
|
|||
}
|
||||
|
||||
.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: 10pt;
|
||||
padding: 3px 7px;
|
||||
border: 1px solid #a7a7a7;
|
||||
border-radius: 4px;
|
||||
border-radius: 4px !important;
|
||||
}
|
||||
|
||||
.dim {
|
||||
background-color: #f3f3f3;
|
||||
}
|
||||
|
||||
.slider-info {
|
||||
|
@ -333,6 +347,7 @@ HTML,BODY {
|
|||
.card {
|
||||
background-color: $service-background;
|
||||
border: $service-border;
|
||||
//box-shadow: 0px 2px 11px 1px rgba(0, 0, 0, 0.13);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="(service, index) in $store.getters.services" class="service_block" v-bind:key="index">
|
||||
<div v-for="(service, index) in services" class="service_block" v-bind:key="index">
|
||||
<ServiceInfo :service=service />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -30,10 +30,21 @@
|
|||
components: {
|
||||
ServiceInfo
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
services() {
|
||||
return this.$store.getters.services
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
|
||||
failuresLast24Hours() {
|
||||
let total = 0;
|
||||
this.$store.getters.services.map((s) => {
|
||||
this.services.map((s) => {
|
||||
total += s.failures_24_hours
|
||||
})
|
||||
return total
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
</thead>
|
||||
|
||||
<draggable tag="tbody" v-model="groupsList" class="sortable_groups" handle=".drag_icon">
|
||||
<tr v-for="(group, index) in $store.getters.groupsCleanInOrder" v-bind:key="group.id">
|
||||
<tr v-for="(group, index) in groupsList" v-bind:key="group.id">
|
||||
<td><span class="drag_icon d-none d-md-inline">
|
||||
<font-awesome-icon icon="bars" class="mr-3" /></span> {{group.name}}
|
||||
</td>
|
||||
|
@ -49,7 +49,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<FormGroup v-if="$store.state.admin" :edit="editChange" :in_group="group"/>
|
||||
|
||||
</div>
|
||||
|
@ -92,9 +91,6 @@
|
|||
this.$store.commit('setGroups', groups)
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
editChange(v) {
|
||||
|
@ -105,13 +101,6 @@
|
|||
this.group = g
|
||||
this.edit = !mode
|
||||
},
|
||||
reordered_services() {
|
||||
|
||||
},
|
||||
saveUpdatedOrder: function (e) {
|
||||
window.console.log("saving...");
|
||||
window.console.log(this.myViews.array()); // this.myViews.array is not a function
|
||||
},
|
||||
async deleteGroup(g) {
|
||||
let c = confirm(`Are you sure you want to delete '${g.name}'?`)
|
||||
if (c) {
|
||||
|
|
|
@ -14,15 +14,22 @@
|
|||
</thead>
|
||||
<tbody id="users_table">
|
||||
|
||||
<tr v-for="(user, index) in $store.getters.users" v-bind:key="index" >
|
||||
<tr v-for="(user, index) in users" v-bind:key="user.id" >
|
||||
<td>{{user.username}}</td>
|
||||
<td v-if="user.admin"><span class="badge badge-danger">ADMIN</span></td>
|
||||
<td v-if="!user.admin"><span class="badge badge-primary">USER</span></td>
|
||||
<td>
|
||||
<span class="badge" :class="{'badge-danger': user.admin, 'badge-primary': !user.admin}">
|
||||
{{user.admin ? 'ADMIN' : 'USER'}}
|
||||
</span>
|
||||
</td>
|
||||
<td class="d-none d-md-table-cell">{{niceDate(user.updated_at)}}</td>
|
||||
<td class="text-right">
|
||||
<div class="btn-group">
|
||||
<a @click.prevent="editUser(user, edit)" href="" class="btn btn-outline-secondary"><font-awesome-icon icon="user" /> Edit</a>
|
||||
<a @click.prevent="deleteUser(user)" v-if="index !== 0" href="" class="btn btn-danger"><font-awesome-icon icon="times" /></a>
|
||||
<a @click.prevent="editUser(user, edit)" href="#" class="btn btn-outline-secondary edit-user">
|
||||
<font-awesome-icon icon="user" /> Edit
|
||||
</a>
|
||||
<a @click.prevent="deleteUser(user)" v-if="index !== 0" href="#" class="btn btn-danger delete-user">
|
||||
<font-awesome-icon icon="times" />
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
@ -49,6 +56,11 @@
|
|||
user: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
users() {
|
||||
return this.$store.getters.users
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
editChange(v) {
|
||||
this.user = {}
|
||||
|
|
|
@ -64,28 +64,20 @@ export default {
|
|||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async updateOrder(value) {
|
||||
let data = [];
|
||||
value.forEach((s, k) => {
|
||||
data.push({ service: s.id, order: k + 1 })
|
||||
});
|
||||
const reorder = await Api.services_reorder(data)
|
||||
window.console.log('reorder', reorder)
|
||||
const services = await Api.services()
|
||||
this.$store.commit('setServices', services)
|
||||
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) {
|
||||
await Api.service_delete(s.id)
|
||||
const services = await Api.services()
|
||||
this.$store.commit('setServices', services)
|
||||
await this.update()
|
||||
}
|
||||
},
|
||||
serviceGroup(s) {
|
||||
|
@ -95,6 +87,10 @@ export default {
|
|||
}
|
||||
return ""
|
||||
},
|
||||
async update() {
|
||||
const services = await Api.services()
|
||||
this.$store.commit('setServices', services)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -59,10 +59,9 @@
|
|||
components: {
|
||||
codemirror
|
||||
},
|
||||
props: {
|
||||
core: {
|
||||
type: Object,
|
||||
required: true
|
||||
computed: {
|
||||
core() {
|
||||
return this.$store.getters.core
|
||||
}
|
||||
},
|
||||
data () {
|
||||
|
@ -86,11 +85,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
codemirror () {
|
||||
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
await this.fetchTheme()
|
||||
this.changeTab('vars')
|
||||
|
|
|
@ -26,10 +26,6 @@
|
|||
<li v-if="$store.state.admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
||||
<router-link to="/dashboard/logs" class="nav-link">Logs</router-link>
|
||||
</li>
|
||||
|
||||
<li @click="navopen = !navopen" class="nav-item navbar-item">
|
||||
<router-link to="/dashboard/help" class="nav-link">Help</router-link>
|
||||
</li>
|
||||
</ul>
|
||||
<span class="navbar-text">
|
||||
<a href="#" class="nav-link" v-on:click="logout">Logout</a>
|
||||
|
@ -55,7 +51,7 @@
|
|||
this.$store.commit('setHasAllData', false)
|
||||
this.$store.commit('setToken', null)
|
||||
this.$store.commit('setAdmin', false)
|
||||
await this.$router.push('/')
|
||||
await this.$router.push('/logout')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
<template>
|
||||
<button v-html="loading ? loadLabel : label" @click.prevent="runAction" type="submit" :disabled="loading || disabled" class="btn btn-block" :class="{class: !loading, 'btn-outline-light': loading}">
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'LoadButton',
|
||||
props: {
|
||||
action: {
|
||||
type: Function,
|
||||
required: true
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
class: {
|
||||
type: String,
|
||||
default: "btn-primary"
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
loadLabel: "<div class=\"spinner-border text-dark\"><span class=\"sr-only\">Loading</span></div>"
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async runAction() {
|
||||
this.loading = true;
|
||||
await this.action();
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -1,12 +1,12 @@
|
|||
<template>
|
||||
<footer>
|
||||
<div v-if="!$store.getters.core.footer" class="footer text-center mb-4 p-2">
|
||||
<div v-if="!core.footer" class="footer text-center mb-4 p-2">
|
||||
<a href="https://github.com/statping/statping" target="_blank">
|
||||
Statping {{$store.getters.core.version}} made with <font-awesome-icon style="color: #d40d0d" icon="heart"/>
|
||||
Statping {{core.version}} made with <font-awesome-icon icon="heart"/>
|
||||
</a> |
|
||||
<router-link :to="$store.getters.core.logged_in ? '/dashboard' : '/login'">Dashboard</router-link>
|
||||
<router-link :to="core.logged_in ? '/dashboard' : '/login'">Dashboard</router-link>
|
||||
</div>
|
||||
<div v-else class="footer text-center mb-4 p-2" v-html="$store.getters.core.footer"></div>
|
||||
<div v-else class="footer text-center mb-4 p-2" v-html="core.footer"></div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
|
@ -18,15 +18,11 @@
|
|||
components: {
|
||||
Dashboard
|
||||
},
|
||||
props: {
|
||||
version: String,
|
||||
logged_in: Boolean
|
||||
},
|
||||
watch: {
|
||||
logged_in() {
|
||||
|
||||
}
|
||||
computed: {
|
||||
core() {
|
||||
return this.$store.getters.core
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@ export default {
|
|||
components: {
|
||||
IncidentsBlock,
|
||||
GroupServiceFailures
|
||||
|
||||
},
|
||||
props: {
|
||||
group: Object
|
||||
|
|
|
@ -34,7 +34,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
async lastDaysFailures() {
|
||||
const start = this.nowSubtract(86400 * 30)
|
||||
const start = this.nowSubtract(86400 * 30)
|
||||
this.failureData = await Api.service_failures_data(this.service.id, this.toUnix(start), this.toUnix(this.now()), "24h")
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,18 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 id="title" class="col-12 text-center pt-4 mt-4 mb-3 header-title font-6">{{$store.getters.core.name}}</h1>
|
||||
<h5 id="description" class="col-12 text-center mb-5 header-desc font-3">{{$store.getters.core.description}}</h5>
|
||||
<h1 id="title" class="col-12 text-center pt-4 mt-4 mb-3 header-title font-6">{{core.name}}</h1>
|
||||
<h5 id="description" class="col-12 text-center mb-5 header-desc font-3">{{core.description}}</h5>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Header',
|
||||
computed: {
|
||||
core() {
|
||||
return this.$store.getters.core
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -35,10 +35,12 @@
|
|||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.value = this.func.value;
|
||||
this.title = this.func.title;
|
||||
this.subtitle = this.func.subtitle;
|
||||
this.chart = this.convertToChartData(this.func.chart);
|
||||
if (this.func) {
|
||||
this.value = this.func.value;
|
||||
this.title = this.func.title;
|
||||
this.subtitle = this.func.subtitle;
|
||||
this.chart = this.convertToChartData(this.func.chart);
|
||||
}
|
||||
},
|
||||
async latencyYesterday() {
|
||||
const todayTime = await Api.service_hits(this.service.id, this.toUnix(this.nowSubtract(86400)), this.toUnix(new Date()), this.group, false)
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
<p class="mb-1">{{failure.issue}}</p>
|
||||
</div>
|
||||
|
||||
<nav v-if="total > 4" aria-label="page navigation example">
|
||||
<nav v-if="total > 4" 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">
|
||||
|
@ -17,7 +17,7 @@
|
|||
<span class="sr-only">Previous</span>
|
||||
</a>
|
||||
</li>
|
||||
<li v-for="n in Math.floor(total / limit)" class="page-item" :class="{'active': page === n}">
|
||||
<li v-for="n in maxPages" class="page-item" :class="{'active': page === n}">
|
||||
<a @click.prevent="gotoPage(n)" class="page-link" href="#">{{n}}</a>
|
||||
</li>
|
||||
<li class="page-item" :class="{'disabled': page===Math.floor(total / limit)}">
|
||||
|
@ -53,6 +53,19 @@ export default {
|
|||
page: 1
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
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
|
||||
}
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
await this.gotoPage(1)
|
||||
},
|
||||
|
|
|
@ -1,23 +1,28 @@
|
|||
<template v-if="service">
|
||||
<div class="col-12 card mb-4" style="min-height: 280px;" :class="{'offline-card': !service.online}">
|
||||
<div class="card-body p-3 p-md-1 pt-md-3 pb-md-1">
|
||||
<h4 class="card-title mb-4">
|
||||
<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" :class="{'badge-success': service.online, 'badge-danger': !service.online}">
|
||||
{{service.online ? "ONLINE" : "OFFLINE"}}
|
||||
</span>
|
||||
{{service.online ? "ONLINE" : "OFFLINE"}}
|
||||
</span>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="card-body p-3 p-md-1 pt-md-3 pb-md-1">
|
||||
|
||||
<transition name="fade">
|
||||
<div v-if="loaded && service.online" class="row pb-3">
|
||||
<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 v-if="loaded && service.online" class="col-12 pb-2">
|
||||
<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 class="d-none row col-12 mt-4 pt-1 mb-3 align-content-center">
|
||||
<div v-if="false" class="row mt-4 pt-1 mb-3 align-content-center">
|
||||
|
||||
<StatsGen :service="service"
|
||||
title="Since Yesterday"
|
||||
|
@ -44,21 +49,35 @@
|
|||
group="24h" expression="latencyPercent"/>
|
||||
</div>
|
||||
|
||||
<div class="col-4">
|
||||
<button @click.prevent="Tab('incident')" class="btn btn-block btn-outline-secondary incident" :class="{'text-white btn-secondary': openTab==='incident'}" >Incidents</button>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
<div class="card-footer">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-3">
|
||||
<button @click.prevent="Tab('incident')" class="btn btn-block btn-outline-secondary incident" :class="{'text-white btn-secondary': openTab==='incident'}" >Incidents</button>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<button @click.prevent="Tab('checkin')" class="btn btn-block btn-outline-secondary checkin" :class="{'text-white btn-secondary': openTab==='checkin'}" >Checkins</button>
|
||||
</div>
|
||||
<div class="col-4">
|
||||
<div class="col-3">
|
||||
<button @click.prevent="Tab('failures')" class="btn btn-block btn-outline-secondary failures" :disabled="service.stats.failures === 0" :class="{'text-white btn-secondary': openTab==='failures'}">
|
||||
Failures <span class="badge badge-danger float-right mt-1">{{service.stats.failures}}</span></button>
|
||||
</div>
|
||||
<div class="col-3 pt-2">
|
||||
<span class="text-black-50 float-right">{{service.online_7_days}}% Uptime</span>
|
||||
</div>
|
||||
|
||||
<div v-if="openTab === 'incident'" class="col-12 mt-4">
|
||||
<FormIncident :service="service" />
|
||||
</div>
|
||||
|
||||
<div v-if="openTab === 'checkin'" class="col-12 mt-4">
|
||||
<Checkin :service="service" />
|
||||
</div>
|
||||
|
||||
<div v-if="openTab === 'failures'" class="col-12 mt-4">
|
||||
<button @click.prevent="deleteFailures" class="btn btn-block btn-outline-secondary delete_failures" :disabled="service.stats.failures === 0">Delete Failures</button>
|
||||
|
||||
|
@ -66,8 +85,6 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
</div>
|
||||
|
||||
<span v-for="(failure, index) in failures" v-bind:key="index" class="alert alert-light">
|
||||
|
@ -79,6 +96,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Checkin from '../../forms/Checkin';
|
||||
import FormIncident from '../../forms/Incident';
|
||||
import FormMessage from '../../forms/Message';
|
||||
import ServiceFailures from './ServiceFailures';
|
||||
|
@ -89,6 +107,7 @@
|
|||
export default {
|
||||
name: 'ServiceInfo',
|
||||
components: {
|
||||
Checkin,
|
||||
ServiceFailures,
|
||||
FormIncident,
|
||||
FormMessage,
|
||||
|
@ -109,17 +128,27 @@
|
|||
loaded: false,
|
||||
set1_name: "",
|
||||
set2_name: "",
|
||||
failures: null
|
||||
failures: null,
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
watch: {
|
||||
|
||||
},
|
||||
methods: {
|
||||
async setVisible(isVisible, entry) {
|
||||
if (isVisible && !this.visible) {
|
||||
await this.loadInfo()
|
||||
this.visible = true
|
||||
}
|
||||
},
|
||||
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
|
||||
},
|
||||
methods: {
|
||||
},
|
||||
async deleteFailures() {
|
||||
const c = confirm('Are you sure you want to delete all failures?')
|
||||
if (c) {
|
||||
|
|
|
@ -1,23 +1,41 @@
|
|||
<template>
|
||||
<form @submit.prevent="saveCheckin">
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<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-3">
|
||||
<label for="checkin_interval" class="col-form-label">Interval (seconds)</label>
|
||||
<input v-model="checkin.interval" type="number" name="interval" class="form-control" id="checkin_interval" placeholder="60">
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<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">
|
||||
<button @click.prevent="saveCheckin" type="submit" id="submit" class="btn btn-success d-block" style="margin-top: 14px;">Save Checkin</button>
|
||||
</div>
|
||||
<div>
|
||||
<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">
|
||||
<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>
|
||||
|
||||
<div class="col-12 alert alert-light">
|
||||
<form @submit.prevent="saveCheckin">
|
||||
<div class="form-group row">
|
||||
<div class="col-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>
|
||||
<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">
|
||||
<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>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -37,20 +55,41 @@
|
|||
name: "",
|
||||
interval: 60,
|
||||
grace: 60,
|
||||
service: this.service.id
|
||||
service_id: this.service.id
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
async saveCheckin() {
|
||||
const data = {name: this.group.name, public: this.group.public}
|
||||
await Api.group_create(data)
|
||||
const groups = await Api.groups()
|
||||
this.$store.commit('setGroups', groups)
|
||||
computed: {
|
||||
checkins() {
|
||||
return this.$store.getters.serviceCheckins(this.service.id)
|
||||
},
|
||||
core() {
|
||||
return this.$store.getters.core
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
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()
|
||||
},
|
||||
async deleteCheckin(checkin) {
|
||||
await Api.checkin_delete(checkin)
|
||||
await this.updateCheckins()
|
||||
},
|
||||
async updateCheckins() {
|
||||
const checkins = await Api.checkins()
|
||||
this.$store.commit('setCheckins', checkins)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -31,7 +31,20 @@
|
|||
<small class="form-text text-muted">HTML is allowed inside the footer</small>
|
||||
</div>
|
||||
|
||||
<button @click.prevent="saveSettings" id="save_core" type="submit" class="btn btn-primary btn-block">Save Settings</button>
|
||||
<div class="form-group row mt-3">
|
||||
<label class="col-sm-10 col-form-label">Enable Error Reporting</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">
|
||||
<label for="switch_allow_report"></label>
|
||||
</span>
|
||||
</div>
|
||||
<div class="col-12">
|
||||
<small>Help the Statping project out by sending anonymous error logs back to our server.</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button @click.prevent="saveSettings" id="save_core" type="submit" class="btn btn-primary btn-block mt-3">Save Settings</button>
|
||||
|
||||
</form>
|
||||
</template>
|
||||
|
@ -41,24 +54,17 @@
|
|||
|
||||
export default {
|
||||
name: 'CoreSettings',
|
||||
props: {
|
||||
in_core: {
|
||||
type: Object,
|
||||
required: true,
|
||||
computed: {
|
||||
core() {
|
||||
return this.$store.getters.core
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
core: this.in_core
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async saveSettings() {
|
||||
const c = this.core
|
||||
await Api.core_save(c)
|
||||
const core = await Api.core()
|
||||
this.$store.commit('setCore', core)
|
||||
this.core = core
|
||||
},
|
||||
selectAll() {
|
||||
this.$refs.input.select();
|
||||
|
|
|
@ -1,80 +0,0 @@
|
|||
<template>
|
||||
<form @submit="updateCore">
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Github Client ID</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="clientId" type="text" class="form-control" placeholder="" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Github Client Secret</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="clientSecret" type="text" class="form-control" placeholder="" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="switch-group-public" class="col-sm-4 col-form-label">Enabled</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
<span @click="enabled = !!enabled" class="switch float-left">
|
||||
<input v-model="enabled" type="checkbox" class="switch" id="switch-group-public" :checked="enabled">
|
||||
<label for="switch-group-public">Enabled Github Auth</label>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button @click="updateCore" 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"}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-danger d-none" id="alerter" role="alert"></div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../API";
|
||||
|
||||
export default {
|
||||
name: 'FormGroup',
|
||||
props: {
|
||||
in_group: {
|
||||
type: Object
|
||||
},
|
||||
edit: {
|
||||
type: Function
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
clientId: "",
|
||||
clientSecret: "",
|
||||
enabled: true,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
in_group() {
|
||||
this.group = this.in_group
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
removeEdit() {
|
||||
this.group = {}
|
||||
this.edit(false)
|
||||
},
|
||||
async updateCore() {
|
||||
const g = this.group
|
||||
const data = {id: g.id, name: g.name, public: g.public}
|
||||
await Api.core_save(data)
|
||||
const groups = await Api.groups()
|
||||
this.$store.commit('setGroups', groups)
|
||||
this.edit(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -80,17 +80,19 @@
|
|||
const g = this.group
|
||||
const data = {name: g.name, public: g.public}
|
||||
await Api.group_create(data)
|
||||
const groups = await Api.groups()
|
||||
this.$store.commit('setGroups', groups)
|
||||
await this.update()
|
||||
this.group = {}
|
||||
},
|
||||
async updateGroup() {
|
||||
const g = this.group
|
||||
const data = {id: g.id, name: g.name, public: g.public}
|
||||
await Api.group_update(data)
|
||||
await this.update()
|
||||
this.edit(false)
|
||||
},
|
||||
async update() {
|
||||
const groups = await Api.groups()
|
||||
this.$store.commit('setGroups', groups)
|
||||
this.edit(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,11 +8,9 @@
|
|||
</button>
|
||||
</div>
|
||||
<div class="card-body bg-light pt-3">
|
||||
|
||||
<div v-for="(update, i) in incident.updates" 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" data-dismiss="alert" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
|
@ -95,9 +93,9 @@
|
|||
await Api.incident_update_delete(update)
|
||||
this.incidents = await Api.incidents_service(this.service)
|
||||
},
|
||||
async loadIncidents() {
|
||||
this.incidents = await Api.incidents_service(this.service)
|
||||
},
|
||||
async loadIncidents() {
|
||||
this.incidents = await Api.incidents_service(this.service)
|
||||
},
|
||||
async createIncident() {
|
||||
await Api.incident_create(this.service, this.incident)
|
||||
await this.loadIncidents()
|
||||
|
@ -107,13 +105,13 @@
|
|||
service: this.service.id,
|
||||
}
|
||||
},
|
||||
async deleteIncident(incident) {
|
||||
let c = confirm(`Are you sure you want to delete '${incident.title}'?`)
|
||||
if (c) {
|
||||
await Api.incident_delete(incident)
|
||||
await this.loadIncidents()
|
||||
}
|
||||
async deleteIncident(incident) {
|
||||
let c = confirm(`Are you sure you want to delete '${incident.title}'?`)
|
||||
if (c) {
|
||||
await Api.incident_delete(incident)
|
||||
await this.loadIncidents()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -23,10 +23,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<a v-if="oauth.oauth_providers.split(',').includes('github')" class="btn btn-block btn-outline-dark" :href="`https://github.com/login/oauth/authorize?scope=user:email&client_id=${oauth.gh_client_id}`">Login with Github</a>
|
||||
<a v-if="oauth.oauth_providers.split(',').includes('google')" class="btn btn-block btn-outline-secondary" :href="`https://accounts.google.com/signin/oauth?client_id=${oauth.google_client_id}&response_type=code&scope=${google_scope}&redirect_uri=${$store.getters.core.domain}/oauth/google`">Login with Google</a>
|
||||
<a v-if="oauth.oauth_providers.split(',').includes('slack')" class="btn btn-block btn-outline-secondary" :href="`https://slack.com/oauth/v2/authorize?client_id=${oauth.slack_client_id}&team=${oauth.slack_team}&user_scope=${slack_scope}&redirect_uri=${$store.getters.core.domain}/oauth/slack`">Login with Slack</a>
|
||||
|
||||
</form>
|
||||
</template>
|
||||
|
||||
|
@ -35,11 +31,14 @@
|
|||
|
||||
export default {
|
||||
name: 'FormLogin',
|
||||
props: {
|
||||
oauth: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
core() {
|
||||
return this.$store.getters.core
|
||||
},
|
||||
oauth() {
|
||||
return this.$store.getters.core.oauth
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
username: "",
|
||||
|
|
|
@ -35,20 +35,25 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="error" class="alert alert-danger col-12" role="alert">{{error}}</div>
|
||||
<div v-if="success" class="alert alert-success col-12" role="alert">{{notifier.title}} appears to be working!</div>
|
||||
<div v-if="error && !success" class="alert alert-danger col-12" role="alert">
|
||||
{{error}}<p v-if="response">Response:<br>{{response}}</p>
|
||||
</div>
|
||||
<div v-if="success" class="alert alert-success col-12" role="alert">
|
||||
{{notifier.title}} appears to be working!
|
||||
<p v-if="response">Response:<br>{{response}}</p>
|
||||
</div>
|
||||
|
||||
<div class="card text-black-50 bg-white mb-3">
|
||||
<div class="card-body">
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6 col-sm-6 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||
<button @click.prevent="saveNotifier" type="submit" class="btn btn-block text-capitalize btn-primary">
|
||||
<button @click.prevent="saveNotifier" type="submit" class="btn btn-block text-capitalize btn-primary save-notifier">
|
||||
<i class="fa fa-check-circle"></i> {{loading ? "Loading..." : saved ? "Saved" : "Save Settings"}}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-6 col-sm-6 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||
<button @click.prevent="testNotifier" class="btn btn-outline-dark btn-block text-capitalize"><i class="fa fa-vial"></i>
|
||||
<button @click.prevent="testNotifier" class="btn btn-outline-dark btn-block text-capitalize test-notifier"><i class="fa fa-vial"></i>
|
||||
{{loadingTest ? "Loading..." : "Test Notifier"}}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -79,6 +84,7 @@ export default {
|
|||
loading: false,
|
||||
loadingTest: false,
|
||||
error: null,
|
||||
response: null,
|
||||
success: false,
|
||||
saved: false,
|
||||
form: {},
|
||||
|
@ -130,6 +136,7 @@ export default {
|
|||
} else {
|
||||
this.error = tested.error
|
||||
}
|
||||
this.response = tested.response
|
||||
this.loadingTest = false
|
||||
},
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
<div class="form-group row">
|
||||
<label for="switch-gh-oauth" class="col-sm-4 col-form-label">OAuth Login Settings</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
<span @click="internal_enabled = !!internal_enabled" class="switch float-left">
|
||||
<input v-model="internal_enabled" type="checkbox" class="switch" id="switch-local-oauth" :checked="internal_enabled">
|
||||
<span @click="oauth.internal_enabled = !!core.oauth.internal_enabled" class="switch float-left">
|
||||
<input v-model="oauth.internal_enabled" type="checkbox" class="switch" id="switch-local-oauth" :checked="oauth.internal_enabled">
|
||||
<label for="switch-local-oauth">Use email/password Authentication</label>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -15,7 +15,7 @@
|
|||
<div class="form-group row">
|
||||
<label for="whitelist_domains" class="col-sm-4 col-form-label">Whitelist Domains</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.oauth_domains" type="text" class="form-control" placeholder="domain.com" id="whitelist_domains">
|
||||
<input v-model="oauth.oauth.oauth_domains" type="text" class="form-control" placeholder="domain.com" id="whitelist_domains">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -28,20 +28,20 @@
|
|||
<div class="form-group row mt-3">
|
||||
<label for="github_client" class="col-sm-4 col-form-label">Github Client ID</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.gh_client_id" type="text" class="form-control" id="github_client" required>
|
||||
<input v-model="oauth.oauth.gh_client_id" type="text" class="form-control" id="github_client" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="github_secret" class="col-sm-4 col-form-label">Github Client Secret</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-model="oauth.gh_client_secret" type="text" class="form-control" id="github_secret" required>
|
||||
<input v-model="oauth.oauth.gh_client_secret" type="text" class="form-control" id="github_secret" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="switch-gh-oauth" class="col-sm-4 col-form-label">Enable Github Login</label>
|
||||
<div class="col-md-8 col-xs-12 mt-1">
|
||||
<span @click="github_enabled = !!github_enabled" class="switch float-left">
|
||||
<input v-model="github_enabled" type="checkbox" class="switch" id="switch-gh-oauth" :checked="github_enabled">
|
||||
<span @click="oauth.github_enabled = !!oauth.github_enabled" class="switch float-left">
|
||||
<input v-model="oauth.github_enabled" type="checkbox" class="switch" id="switch-gh-oauth" :checked="oauth.github_enabled">
|
||||
<label for="switch-gh-oauth"> </label>
|
||||
</span>
|
||||
</div>
|
||||
|
@ -49,7 +49,12 @@
|
|||
<div class="form-group row">
|
||||
<label for="gh_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-bind:value="`${$store.getters.core.domain}/oauth/github`" type="text" class="form-control" id="gh_callback" readonly>
|
||||
<div class="input-group">
|
||||
<input v-bind:value="`${core.domain}/oauth/github`" type="text" class="form-control" id="gh_callback" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(`${core.domain}/oauth/github`)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -81,9 +86,14 @@
|
|||
</div>
|
||||
</div>
|
||||
<div class="form-group row">
|
||||
<label for="callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<label for="google_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-bind:value="`${$store.getters.core.domain}/oauth/google`" type="text" class="form-control" id="callback" readonly>
|
||||
<div class="input-group">
|
||||
<input v-bind:value="`${core.domain}/oauth/google`" type="text" class="form-control" id="google_callback" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(`${core.domain}/oauth/google`)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -124,14 +134,17 @@
|
|||
<div class="form-group row">
|
||||
<label for="slack_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||
<div class="col-sm-8">
|
||||
<input v-bind:value="`${$store.getters.core.domain}/oauth/slack`" type="text" class="form-control" id="slack_callback" readonly>
|
||||
<div class="input-group">
|
||||
<input v-bind:value="`${core.domain}/oauth/slack`" type="text" class="form-control" id="slack_callback" readonly>
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(`${core.domain}/oauth/slack`)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{providers()}}
|
||||
|
||||
<button class="btn btn-primary btn-block" @click.prevent="saveOAuth" type="submit">
|
||||
Save OAuth Settings
|
||||
</button>
|
||||
|
@ -144,25 +157,34 @@
|
|||
|
||||
export default {
|
||||
name: 'OAuth',
|
||||
props: {
|
||||
oauth: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
internal_enabled: this.$store.getters.core.oauth.oauth_providers.split(",").includes('local'),
|
||||
google_enabled: this.$store.getters.core.oauth.oauth_providers.split(",").includes('google'),
|
||||
github_enabled: this.$store.getters.core.oauth.oauth_providers.split(",").includes('github'),
|
||||
slack_enabled: this.$store.getters.core.oauth.oauth_providers.split(",").includes('slack')
|
||||
computed: {
|
||||
oauth() {
|
||||
return this.$store.getters.core.oauth
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
internal_enabled: this.has('local'),
|
||||
google_enabled: this.has('google'),
|
||||
github_enabled: this.has('github'),
|
||||
slack_enabled: this.has('slack')
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
window.console.log(this.core.oauth)
|
||||
},
|
||||
beforeCreate() {
|
||||
// this.github_enabled = this.$store.getters.core.oauth.oauth_providers.split(",").includes('github')
|
||||
// const c = await Api.core()
|
||||
// this.auth = c.auth
|
||||
},
|
||||
methods: {
|
||||
has(val) {
|
||||
if (!this.core.oauth.oauth_providers) {
|
||||
return false
|
||||
}
|
||||
return this.core.oauth.oauth_providers.split(",").includes(val)
|
||||
},
|
||||
providers() {
|
||||
let providers = [];
|
||||
if (this.github_enabled) {
|
||||
|
@ -186,7 +208,6 @@
|
|||
await Api.core_save(c)
|
||||
const core = await Api.core()
|
||||
this.$store.commit('setCore', core)
|
||||
this.core = core
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,84 +5,83 @@
|
|||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<form @submit.prevent="saveSetup">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="form-group">
|
||||
<label>Database 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>
|
||||
<option value="mysql">MySQL</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<label>Host</label>
|
||||
<input @keyup="canSubmit" v-model="setup.db_host" id="db_host" type="text" class="form-control" placeholder="localhost">
|
||||
</div>
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<label>Database Port</label>
|
||||
<input @keyup="canSubmit" v-model="setup.db_port" id="db_port" type="text" class="form-control" placeholder="localhost">
|
||||
</div>
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<label>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">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">Database</label>
|
||||
<input @keyup="canSubmit" v-model="setup.db_database" id="db_database" type="text" class="form-control" placeholder="Database name">
|
||||
</div>
|
||||
|
||||
<form @submit.prevent="saveSetup">
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="form-group">
|
||||
<label>Database 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>
|
||||
<option value="mysql">MySQL</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<label>Host</label>
|
||||
<input @keyup="canSubmit" v-model="setup.db_host" id="db_host" type="text" class="form-control" placeholder="localhost">
|
||||
</div>
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<label>Database Port</label>
|
||||
<input @keyup="canSubmit" v-model="setup.db_port" id="db_port" type="text" class="form-control" placeholder="localhost">
|
||||
</div>
|
||||
<div v-if="setup.db_connection !== 'sqlite'" class="form-group">
|
||||
<label>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">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">Database</label>
|
||||
<input @keyup="canSubmit" v-model="setup.db_database" id="db_database" type="text" class="form-control" placeholder="Database name">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-6">
|
||||
|
||||
<div class="col-6">
|
||||
<div class="form-group">
|
||||
<label>Project Name</label>
|
||||
<input @keyup="canSubmit" v-model="setup.project" id="project" type="text" class="form-control" placeholder="Great Uptime" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Project Name</label>
|
||||
<input @keyup="canSubmit" v-model="setup.project" id="project" type="text" class="form-control" placeholder="Great Uptime" required>
|
||||
<div class="form-group">
|
||||
<label>Project Description</label>
|
||||
<input @keyup="canSubmit" v-model="setup.description" id="description" type="text" class="form-control" placeholder="Great Uptime">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="domain">Domain URL</label>
|
||||
<input @keyup="canSubmit" v-model="setup.domain" type="text" class="form-control" id="domain" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Admin 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>Admin 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>Confirm Admin Password</label>
|
||||
<input @keyup="canSubmit" v-model="setup.confirm_password" id="password_confirm" type="password" class="form-control" placeholder="password" required>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-if="error" class="col-12 alert alert-danger">
|
||||
{{error}}
|
||||
</div>
|
||||
|
||||
<button @click.prevent="saveSetup" v-bind:disabled="disabled || loading" type="submit" class="btn btn-primary btn-block" :class="{'btn-primary': !loading, 'btn-default': loading}">
|
||||
{{loading ? "Loading..." : "Save Settings"}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Project Description</label>
|
||||
<input @keyup="canSubmit" v-model="setup.description" id="description" type="text" class="form-control" placeholder="Great Uptime">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="domain">Domain URL</label>
|
||||
<input @keyup="canSubmit" v-model="setup.domain" type="text" class="form-control" id="domain" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>Admin 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>Admin 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>Confirm Admin Password</label>
|
||||
<input @keyup="canSubmit" v-model="setup.confirm_password" id="password_confirm" type="password" class="form-control" placeholder="password" required>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div v-if="error" class="col-12 alert alert-danger">
|
||||
{{error}}
|
||||
</div>
|
||||
|
||||
<button @click.prevent="saveSetup" v-bind:disabled="disabled || loading" type="submit" class="btn btn-primary btn-block" :class="{'btn-primary': !loading, 'btn-default': loading}">
|
||||
{{loading ? "Loading..." : "Save Settings"}}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -163,6 +162,7 @@
|
|||
return
|
||||
}
|
||||
|
||||
await this.$store.dispatch('loadCore')
|
||||
await this.$store.dispatch('loadRequired')
|
||||
|
||||
this.loading = false
|
||||
|
|
|
@ -13,9 +13,9 @@
|
|||
<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 @click="user.admin = !!user.admin" class="switch">
|
||||
<input v-model="user.admin" type="checkbox" class="switch" id="switch-normal" v-bind:checked="user.admin">
|
||||
<label for="switch-normal">Administrator</label>
|
||||
<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>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -39,11 +39,12 @@
|
|||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button type="submit" @click="saveUser"
|
||||
<LoadButton
|
||||
class="btn-primary"
|
||||
:disabled="loading || !user.username || !user.email || !user.password || !user.confirm_password || (user.password !== user.confirm_password)"
|
||||
class="btn btn-block" :class="{'btn-primary': !user.id, 'btn-secondary': user.id}">
|
||||
{{loading ? "Loading..." : user.id ? "Update User" : "Create User"}}
|
||||
</button>
|
||||
:action="saveUser"
|
||||
:label="user.id ? 'Update User' : 'Create User'"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="alert alert-danger d-none" id="alerter" role="alert"></div>
|
||||
|
@ -54,10 +55,12 @@
|
|||
|
||||
<script>
|
||||
import Api from "../API";
|
||||
import LoadButton from "@/components/Elements/LoadButton";
|
||||
|
||||
export default {
|
||||
name: 'FormUser',
|
||||
props: {
|
||||
components: {LoadButton},
|
||||
props: {
|
||||
in_user: {
|
||||
type: Object
|
||||
},
|
||||
|
@ -81,6 +84,7 @@
|
|||
in_user() {
|
||||
let u = this.in_user
|
||||
u.password = null
|
||||
u.password_confirm = null
|
||||
this.user = u
|
||||
}
|
||||
},
|
||||
|
@ -89,8 +93,7 @@
|
|||
this.user = {}
|
||||
this.edit(false)
|
||||
},
|
||||
async saveUser(e) {
|
||||
e.preventDefault();
|
||||
async saveUser() {
|
||||
this.loading = true
|
||||
if (this.user.id) {
|
||||
await this.updateUser()
|
||||
|
@ -103,8 +106,7 @@
|
|||
let user = this.user
|
||||
delete user.confirm_password
|
||||
await Api.user_create(user)
|
||||
const users = await Api.users()
|
||||
this.$store.commit('setUsers', users)
|
||||
await this.update()
|
||||
this.user = {}
|
||||
},
|
||||
async updateUser() {
|
||||
|
@ -114,9 +116,12 @@
|
|||
}
|
||||
delete user.confirm_password
|
||||
await Api.user_update(user)
|
||||
await this.update()
|
||||
this.edit(false)
|
||||
},
|
||||
async update() {
|
||||
const users = await Api.users()
|
||||
this.$store.commit('setUsers', users)
|
||||
this.edit(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
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";
|
||||
|
||||
library.add(fas, fab)
|
||||
|
||||
Vue.component('font-awesome-icon', FontAwesomeIcon)
|
||||
Vue.component('font-awesome-icon', FontAwesomeIcon)
|
||||
|
|
|
@ -6,9 +6,7 @@ import VueClipboard from 'vue-clipboard2'
|
|||
|
||||
import App from '@/App.vue'
|
||||
import store from './store'
|
||||
import * as Sentry from '@sentry/browser';
|
||||
import * as Integrations from '@sentry/integrations';
|
||||
const errorReporter = "https://bed4d75404924cb3a799e370733a1b64@sentry.statping.com/3"
|
||||
|
||||
import router from './routes'
|
||||
import "./mixin"
|
||||
import "./icons"
|
||||
|
@ -19,12 +17,6 @@ Vue.use(VueClipboard);
|
|||
Vue.use(VueRouter);
|
||||
Vue.use(VueObserveVisibility);
|
||||
|
||||
Sentry.init({
|
||||
dsn: errorReporter,
|
||||
integrations: [new Integrations.Vue({Vue, attachProps: true})],
|
||||
});
|
||||
|
||||
|
||||
Vue.config.productionTip = false
|
||||
new Vue({
|
||||
router,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<div class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
||||
<TopNav :admin="$store.state.admin"/>
|
||||
<router-view :admin="$store.state.admin"/>
|
||||
<TopNav :admin="admin"/>
|
||||
<router-view :admin="admin"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -17,18 +17,23 @@
|
|||
data () {
|
||||
return {
|
||||
authenticated: false,
|
||||
loaded: false,
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
const core = await Api.core()
|
||||
this.$store.commit('setAdmin', core.admin)
|
||||
this.$store.commit('setCore', core)
|
||||
},
|
||||
async created() {
|
||||
const core = await Api.core()
|
||||
this.$store.commit('setCore', core)
|
||||
computed: {
|
||||
admin() {
|
||||
return this.$store.getters.admin
|
||||
},
|
||||
user() {
|
||||
return this.$store.getters.user
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// if (!this.user || !this.admin) {
|
||||
// this.$router.push('/login')
|
||||
// }
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
<Header/>
|
||||
|
||||
<div v-for="(service, i) in $store.getters.servicesNoGroup" class="col-12 full-col-12">
|
||||
<div v-for="(service, i) in services_no_group" class="col-12 full-col-12">
|
||||
<div class="list-group online_list mb-4">
|
||||
<a class="service_li list-group-item list-group-item-action">
|
||||
<router-link class="no-decoration font-3" :to="serviceLink(service)">{{service.name}}</router-link>
|
||||
|
@ -16,16 +16,16 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="(group, index) in $store.getters.groupsInOrder" v-bind:key="index">
|
||||
<div v-for="(group, index) in groups" v-bind:key="index">
|
||||
<Group :group=group />
|
||||
</div>
|
||||
|
||||
<div v-for="(message, index) in $store.getters.messages" v-bind:key="index" v-if="inRange(message) && message.service === 0">
|
||||
<div v-for="(message, index) in messages" v-bind:key="index" v-if="inRange(message) && message.service === 0">
|
||||
<MessageBlock :message="message"/>
|
||||
</div>
|
||||
|
||||
<div class="col-12 full-col-12">
|
||||
<div v-for="(service, index) in $store.getters.servicesInOrder" :ref="service.id" v-bind:key="index">
|
||||
<div v-for="(service, index) in services" :ref="service.id" v-bind:key="index">
|
||||
<ServiceBlock :service=service />
|
||||
</div>
|
||||
</div>
|
||||
|
@ -58,6 +58,20 @@ export default {
|
|||
logged_in: false
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
messages() {
|
||||
return this.$store.getters.messages
|
||||
},
|
||||
groups() {
|
||||
return this.$store.getters.groupsInOrder
|
||||
},
|
||||
services() {
|
||||
return this.$store.getters.servicesInOrder
|
||||
},
|
||||
services_no_group() {
|
||||
return this.$store.getters.servicesNoGroup
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.logged_in = this.loggedIn()
|
||||
},
|
||||
|
@ -69,11 +83,6 @@ export default {
|
|||
const start = this.isBetween(new Date(), message.start_on)
|
||||
const end = this.isBetween(message.end_on, new Date())
|
||||
return start && end
|
||||
},
|
||||
clickService(s) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.s.scrollTop = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,9 @@
|
|||
<div class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
||||
<div class="col-10 offset-1 col-md-8 offset-md-2 mt-md-2">
|
||||
<div class="col-12 col-md-8 offset-md-2 mb-4">
|
||||
<img alt="Statping Login" class="col-12 mt-5 mt-md-0" style="max-width:680px" src="banner.png">
|
||||
<img alt="Statping Login" class="col-12 mt-5 mt-md-0" style="max-width:650px" src="banner.png">
|
||||
</div>
|
||||
|
||||
<FormLogin :oauth="$store.getters.core.oauth"/>
|
||||
|
||||
<FormLogin/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -12,7 +12,6 @@ import Api from "../API";
|
|||
|
||||
export default {
|
||||
name: 'Logs',
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
logs: [],
|
||||
|
|
|
@ -21,8 +21,8 @@
|
|||
<h6 class="mt-4 text-muted">Notifiers</h6>
|
||||
|
||||
<div id="notifiers_tabs">
|
||||
<a v-for="(notifier, index) in $store.getters.notifiers" v-bind:key="`${notifier.method}_${index}`" @click.prevent="changeTab" class="nav-link text-capitalize" v-bind:class="{active: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}" v-bind:id="`v-pills-${notifier.method.toLowerCase()}-tab`" data-toggle="pill" v-bind:href="`#v-pills-${notifier.method.toLowerCase()}`" role="tab" v-bind:aria-controls="`v-pills-${notifier.method.toLowerCase()}`" aria-selected="false">
|
||||
<font-awesome-icon :icon="iconName(notifier.icon)" class="mr-2"/> {{notifier.method}}
|
||||
<a v-for="(notifier, index) in notifiers" v-bind:key="`${notifier.method}_${index}`" @click.prevent="changeTab" class="nav-link text-capitalize" v-bind:class="{active: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}" v-bind:id="`v-pills-${notifier.method.toLowerCase()}-tab`" data-toggle="pill" v-bind:href="`#v-pills-${notifier.method.toLowerCase()}`" role="tab" v-bind:aria-controls="`v-pills-${notifier.method.toLowerCase()}`" aria-selected="false">
|
||||
<font-awesome-icon :icon="iconName(notifier.icon)" class="mr-2"/> {{notifier.title}}
|
||||
<span v-if="notifier.enabled" class="badge badge-pill float-right mt-1" :class="{'badge-success': !liClass(`v-pills-${notifier.method.toLowerCase()}-tab`), 'badge-light': liClass(`v-pills-${notifier.method.toLowerCase()}-tab`), 'text-dark': liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}">ON</span>
|
||||
</a>
|
||||
</div>
|
||||
|
@ -60,11 +60,10 @@
|
|||
<div class="card text-black-50 bg-white">
|
||||
<div class="card-header">Statping Settings</div>
|
||||
<div class="card-body">
|
||||
<CoreSettings :in_core="core"/>
|
||||
<CoreSettings/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="card text-black-50 bg-white mt-3">
|
||||
<div class="card-header">API Settings</div>
|
||||
<div class="card-body">
|
||||
|
@ -73,7 +72,7 @@
|
|||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input v-model="core.api_key" type="text" class="form-control" id="api_key" readonly>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click.prevent="copy(core.api_key)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -86,12 +85,12 @@
|
|||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<input v-model="core.api_secret" @focus="$event.target.select()" type="text" class="form-control select-input" id="api_secret" readonly>
|
||||
<div class="input-group-append">
|
||||
<div class="input-group-append copy-btn">
|
||||
<button @click="copy(core.api_secret)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||
</div>
|
||||
</div>
|
||||
<small class="form-text text-muted">API Secret is used for read, create, update and delete routes</small>
|
||||
<small class="form-text text-muted">You can <a href="#" @click="renewApiKeys">Regenerate API Keys</a> if you need to.</small>
|
||||
<small class="form-text text-muted">You can <a href="#" id="regenkeys" @click="renewApiKeys">Regenerate API Keys</a> if you need to.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -112,7 +111,7 @@
|
|||
<div class="card text-black-50 bg-white mb-5">
|
||||
<div class="card-header">Theme Editor</div>
|
||||
<div class="card-body">
|
||||
<ThemeEditor :core="core"/>
|
||||
<ThemeEditor/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -127,10 +126,10 @@
|
|||
</div>
|
||||
|
||||
<div class="tab-pane fade" v-bind:class="{active: liClass('v-pills-oauth-tab'), show: liClass('v-pills-oauth-tab')}" id="v-pills-oauth" role="tabpanel" aria-labelledby="v-pills-oauth-tab">
|
||||
<OAuth :oauth="core.oauth"/>
|
||||
<OAuth/>
|
||||
</div>
|
||||
|
||||
<div v-for="(notifier, index) in $store.getters.notifiers" v-bind:key="`${notifier.title}_${index}`" class="tab-pane fade" v-bind:class="{active: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`), show: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}" v-bind:id="`v-pills-${notifier.method.toLowerCase()}-tab`" role="tabpanel" v-bind:aria-labelledby="`v-pills-${notifier.method.toLowerCase()}-tab`">
|
||||
<div v-for="(notifier, index) in notifiers" v-bind:key="`${notifier.method}_${index}`" class="tab-pane fade" v-bind:class="{active: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`), show: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}" v-bind:id="`v-pills-${notifier.method.toLowerCase()}-tab`" role="tabpanel" v-bind:aria-labelledby="`v-pills-${notifier.method.toLowerCase()}-tab`">
|
||||
<Notifier :notifier="notifier"/>
|
||||
</div>
|
||||
|
||||
|
@ -167,20 +166,24 @@
|
|||
tab: "v-pills-home-tab",
|
||||
qrcode: "",
|
||||
qrurl: "",
|
||||
core: this.$store.getters.core
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
core() {
|
||||
return this.$store.getters.core
|
||||
},
|
||||
notifiers() {
|
||||
return this.$store.getters.notifiers
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.cache = await Api.cache()
|
||||
},
|
||||
async created() {
|
||||
const c = this.$store.state.core
|
||||
const c = this.core
|
||||
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)
|
||||
},
|
||||
async beforeMount() {
|
||||
this.core = await Api.core()
|
||||
},
|
||||
methods: {
|
||||
changeTab(e) {
|
||||
this.tab = e.target.id
|
||||
|
|
|
@ -26,7 +26,9 @@ export default new Vuex.Store({
|
|||
messages: [],
|
||||
users: [],
|
||||
notifiers: [],
|
||||
admin: false
|
||||
checkins: [],
|
||||
admin: false,
|
||||
user: false
|
||||
},
|
||||
getters: {
|
||||
hasAllData: state => state.hasAllData,
|
||||
|
@ -39,8 +41,10 @@ export default new Vuex.Store({
|
|||
incidents: state => state.incidents,
|
||||
users: state => state.users,
|
||||
notifiers: state => state.notifiers,
|
||||
checkins: state => state.checkins,
|
||||
|
||||
isAdmin: state => state.admin,
|
||||
isUser: state => state.user,
|
||||
|
||||
servicesInOrder: state => state.services.sort((a, b) => a.order_id - b.order_id),
|
||||
servicesNoGroup: state => state.services.filter(g => g.group_id === 0).sort((a, b) => a.order_id - b.order_id),
|
||||
|
@ -48,6 +52,9 @@ export default new Vuex.Store({
|
|||
groupsClean: state => state.groups.filter(g => g.name !== '').sort((a, b) => a.order_id - b.order_id),
|
||||
groupsCleanInOrder: state => state.groups.filter(g => g.name !== '').sort((a, b) => a.order_id - b.order_id).sort((a, b) => a.order_id - b.order_id),
|
||||
|
||||
serviceCheckins: (state) => (id) => {
|
||||
return state.checkins.filter(c => c.service_id === id)
|
||||
},
|
||||
serviceByAll: (state) => (element) => {
|
||||
if (element % 1 === 0) {
|
||||
return state.services.find(s => s.id == element)
|
||||
|
@ -91,6 +98,7 @@ export default new Vuex.Store({
|
|||
state.hasPublicData = bool
|
||||
},
|
||||
setCore (state, core) {
|
||||
window.console.log('GETTING CORE')
|
||||
state.core = core
|
||||
},
|
||||
setToken (state, token) {
|
||||
|
@ -99,6 +107,9 @@ export default new Vuex.Store({
|
|||
setServices (state, services) {
|
||||
state.services = services
|
||||
},
|
||||
setCheckins (state, checkins) {
|
||||
state.checkins = checkins
|
||||
},
|
||||
setGroups (state, groups) {
|
||||
state.groups = groups
|
||||
},
|
||||
|
@ -114,15 +125,23 @@ export default new Vuex.Store({
|
|||
setAdmin (state, admin) {
|
||||
state.admin = admin
|
||||
},
|
||||
setUser (state, user) {
|
||||
state.user = user
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
async getAllServices(context) {
|
||||
const services = await Api.services()
|
||||
context.commit("setServices", services);
|
||||
},
|
||||
async loadCore(context) {
|
||||
const core = await Api.core()
|
||||
context.commit("setCore", core);
|
||||
context.commit('setAdmin', core.admin)
|
||||
context.commit('setCore', core)
|
||||
context.commit('setUser', core.logged_in)
|
||||
},
|
||||
async loadRequired(context) {
|
||||
const core = await Api.core()
|
||||
context.commit("setCore", core);
|
||||
const groups = await Api.groups()
|
||||
context.commit("setGroups", groups);
|
||||
const services = await Api.services()
|
||||
|
@ -130,23 +149,15 @@ export default new Vuex.Store({
|
|||
const messages = await Api.messages()
|
||||
context.commit("setMessages", messages)
|
||||
context.commit("setHasPublicData", true)
|
||||
// if (core.logged_in) {
|
||||
// const notifiers = await Api.notifiers()
|
||||
// context.commit("setNotifiers", notifiers);
|
||||
// const users = await Api.users()
|
||||
// context.commit("setUsers", users);
|
||||
// const integrations = await Api.integrations()
|
||||
// context.commit("setIntegrations", integrations);
|
||||
// }
|
||||
window.console.log('finished loading required data')
|
||||
},
|
||||
async loadAdmin(context) {
|
||||
const core = await Api.core()
|
||||
context.commit("setCore", core);
|
||||
const groups = await Api.groups()
|
||||
context.commit("setGroups", groups);
|
||||
const services = await Api.services()
|
||||
context.commit("setServices", services);
|
||||
const checkins = await Api.checkins()
|
||||
context.commit("setCheckins", checkins);
|
||||
const messages = await Api.messages()
|
||||
context.commit("setMessages", messages)
|
||||
context.commit("setHasPublicData", true)
|
||||
|
|
|
@ -81,6 +81,8 @@ func apiCoreHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
app.OAuth = c.OAuth
|
||||
app.UseCdn = null.NewNullBool(c.UseCdn.Bool)
|
||||
app.AllowReports = null.NewNullBool(c.AllowReports.Bool)
|
||||
utils.SentryInit(nil, app.AllowReports.Bool)
|
||||
err = app.Update()
|
||||
returnJson(core.App, w, r)
|
||||
}
|
||||
|
|
|
@ -195,26 +195,6 @@ func TestMainApiRoutes(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
//func TestExportSettings(t *testing.T) {
|
||||
// data, err := ExportSettings()
|
||||
// require.Nil(t, err)
|
||||
// assert.Len(t, data, 50)
|
||||
//
|
||||
// var exportData ExportData
|
||||
// err = json.Unmarshal(data, &exportData)
|
||||
// require.Nil(t, err)
|
||||
//
|
||||
// assert.Len(t, exportData.Services, 4)
|
||||
// assert.Len(t, exportData.Messages, 4)
|
||||
// assert.Len(t, exportData.Checkins, 2)
|
||||
// assert.Len(t, exportData.Groups, 1)
|
||||
//
|
||||
// assert.Equal(t, "Updated Core", exportData.Core.Name)
|
||||
// assert.True(t, exportData.Core.Setup)
|
||||
// assert.NotEmpty(t, exportData.Core.ApiKey)
|
||||
// assert.NotEmpty(t, exportData.Core.ApiSecret)
|
||||
//}
|
||||
|
||||
type HttpFuncTest func(*testing.T) error
|
||||
|
||||
// HTTPTest contains all the parameters for a HTTP Unit Test
|
||||
|
|
|
@ -88,6 +88,8 @@ func (s Storage) List() map[string]Item {
|
|||
|
||||
//Get a cached content by key
|
||||
func (s Storage) Get(key string) []byte {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
item := s.items[key]
|
||||
if item.Expired() {
|
||||
CacheStorage.Delete(key)
|
||||
|
|
|
@ -40,8 +40,7 @@ func checkinCreateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
checkin.ServiceId = service.Id
|
||||
err = checkin.Create()
|
||||
if err != nil {
|
||||
if err := checkin.Create(); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
@ -52,7 +51,7 @@ func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
|
|||
vars := mux.Vars(r)
|
||||
checkin, err := checkins.FindByAPI(vars["api"])
|
||||
if err != nil {
|
||||
sendErrorJson(fmt.Errorf("checkin %v was not found", vars["api"]), w, r)
|
||||
sendErrorJson(fmt.Errorf("checkin %s was not found", vars["api"]), w, r)
|
||||
return
|
||||
}
|
||||
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||
|
@ -60,15 +59,17 @@ func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
|
|||
hit := &checkins.CheckinHit{
|
||||
Checkin: checkin.Id,
|
||||
From: ip,
|
||||
CreatedAt: utils.Now().UTC(),
|
||||
CreatedAt: utils.Now(),
|
||||
}
|
||||
log.Infof("Checking %s was requested", checkin.Name)
|
||||
|
||||
err = hit.Create()
|
||||
if err != nil {
|
||||
sendErrorJson(fmt.Errorf("checkin %v was not found", vars["api"]), w, r)
|
||||
return
|
||||
}
|
||||
checkin.Failing = false
|
||||
checkin.LastHitTime = utils.Now().UTC()
|
||||
checkin.LastHitTime = utils.Now()
|
||||
sendJsonAction(hit.Id, "update", w, r)
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@ func TestApiCheckinRoutes(t *testing.T) {
|
|||
SecureRoute: true,
|
||||
}, {
|
||||
Name: "Statping Create Checkin",
|
||||
URL: "/api/checkin",
|
||||
URL: "/api/checkins",
|
||||
Method: "POST",
|
||||
Body: `{
|
||||
"service_id": 2,
|
||||
|
|
|
@ -52,7 +52,7 @@ func TestGroupAPIRoutes(t *testing.T) {
|
|||
URL: "/api/groups",
|
||||
Method: "GET",
|
||||
ExpectedStatus: 200,
|
||||
ResponseLen: 2,
|
||||
ResponseLen: 3,
|
||||
BeforeTest: UnsetTestENV,
|
||||
},
|
||||
{
|
||||
|
|
|
@ -8,11 +8,6 @@ import (
|
|||
"net/http"
|
||||
)
|
||||
|
||||
func apiAllIncidentsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
inc := incidents.All()
|
||||
returnJson(inc, w, r)
|
||||
}
|
||||
|
||||
func apiServiceIncidentsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
incids := incidents.FindByService(utils.ToInt(vars["id"]))
|
||||
|
|
|
@ -72,16 +72,18 @@ func testNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
notif := services.ReturnNotifier(notifer.Method)
|
||||
err = notif.OnTest()
|
||||
out, err := notif.OnTest()
|
||||
|
||||
resp := ¬ifierTestResp{
|
||||
Success: err == nil,
|
||||
Error: err,
|
||||
Success: err == nil,
|
||||
Response: out,
|
||||
Error: err,
|
||||
}
|
||||
returnJson(resp, w, r)
|
||||
}
|
||||
|
||||
type notifierTestResp struct {
|
||||
Success bool `json:"success"`
|
||||
Error error `json:"error,omitempty"`
|
||||
Success bool `json:"success"`
|
||||
Response string `json:"response,omitempty"`
|
||||
Error error `json:"error,omitempty"`
|
||||
}
|
||||
|
|
|
@ -146,9 +146,9 @@ func Router() *mux.Router {
|
|||
|
||||
// API CHECKIN Routes
|
||||
api.Handle("/api/checkins", authenticated(apiAllCheckinsHandler, false)).Methods("GET")
|
||||
api.Handle("/api/checkin/{api}", authenticated(apiCheckinHandler, false)).Methods("GET")
|
||||
api.Handle("/api/checkin", authenticated(checkinCreateHandler, false)).Methods("POST")
|
||||
api.Handle("/api/checkin/{api}", authenticated(checkinDeleteHandler, false)).Methods("DELETE")
|
||||
api.Handle("/api/checkins", authenticated(checkinCreateHandler, false)).Methods("POST")
|
||||
api.Handle("/api/checkins/{api}", authenticated(apiCheckinHandler, false)).Methods("GET")
|
||||
api.Handle("/api/checkins/{api}", authenticated(checkinDeleteHandler, false)).Methods("DELETE")
|
||||
r.Handle("/checkin/{api}", http.HandlerFunc(checkinHitHandler))
|
||||
|
||||
// Static Files Routes
|
||||
|
|
|
@ -23,11 +23,11 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
Method: "GET",
|
||||
ExpectedContains: []string{`"name":"Google"`},
|
||||
ExpectedStatus: 200,
|
||||
ResponseLen: 5,
|
||||
ResponseLen: 6,
|
||||
BeforeTest: SetTestENV,
|
||||
FuncTest: func(t *testing.T) error {
|
||||
count := len(services.Services())
|
||||
if count != 5 {
|
||||
if count != 6 {
|
||||
return errors.Errorf("incorrect services count: %d", count)
|
||||
}
|
||||
return nil
|
||||
|
@ -39,11 +39,11 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
Method: "GET",
|
||||
ExpectedContains: []string{`"name":"Google"`},
|
||||
ExpectedStatus: 200,
|
||||
ResponseLen: 4,
|
||||
ResponseLen: 5,
|
||||
BeforeTest: UnsetTestENV,
|
||||
FuncTest: func(t *testing.T) error {
|
||||
count := len(services.Services())
|
||||
if count != 5 {
|
||||
if count != 6 {
|
||||
return errors.Errorf("incorrect services count: %d", count)
|
||||
}
|
||||
return nil
|
||||
|
@ -59,7 +59,7 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
},
|
||||
{
|
||||
Name: "Statping Private Service 1",
|
||||
URL: "/api/services/2",
|
||||
URL: "/api/services/6",
|
||||
Method: "GET",
|
||||
ExpectedContains: []string{`"error":"not authenticated"`},
|
||||
ExpectedStatus: 200,
|
||||
|
@ -105,21 +105,18 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
Name: "Statping Service 1 Failure Data - 24 Hour",
|
||||
URL: "/api/services/1/failure_data" + startEndQuery + "&group=24h",
|
||||
Method: "GET",
|
||||
ResponseLen: 4,
|
||||
ExpectedStatus: 200,
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 1 Failure Data - 12 Hour",
|
||||
URL: "/api/services/1/failure_data" + startEndQuery + "&group=12h",
|
||||
Method: "GET",
|
||||
ResponseLen: 7,
|
||||
ExpectedStatus: 200,
|
||||
},
|
||||
{
|
||||
Name: "Statping Service 1 Failure Data - 1 Hour",
|
||||
URL: "/api/services/1/failure_data" + startEndQuery + "&group=1h",
|
||||
Method: "GET",
|
||||
ResponseLen: 73,
|
||||
ExpectedStatus: 200,
|
||||
},
|
||||
{
|
||||
|
@ -140,7 +137,6 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
Name: "Statping Service 1 Failure Data",
|
||||
URL: "/api/services/1/failure_data" + startEndQuery,
|
||||
Method: "GET",
|
||||
ResponseLen: 73,
|
||||
ExpectedStatus: 200,
|
||||
},
|
||||
{
|
||||
|
@ -176,7 +172,7 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
ExpectedContains: []string{`"status":"success","type":"service","method":"create"`, `"public":false`, `"group_id":1`},
|
||||
FuncTest: func(t *testing.T) error {
|
||||
count := len(services.Services())
|
||||
if count != 6 {
|
||||
if count != 7 {
|
||||
return errors.Errorf("incorrect services count: %d", count)
|
||||
}
|
||||
return nil
|
||||
|
@ -238,7 +234,7 @@ func TestApiServiceRoutes(t *testing.T) {
|
|||
ExpectedContains: []string{`"status":"success"`, `"method":"delete"`},
|
||||
FuncTest: func(t *testing.T) error {
|
||||
count := len(services.Services())
|
||||
if count != 5 {
|
||||
if count != 6 {
|
||||
return errors.Errorf("incorrect services count: %d", count)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -22,7 +22,7 @@ func (c *commandLine) Select() *notifications.Notification {
|
|||
|
||||
var Command = &commandLine{¬ifications.Notification{
|
||||
Method: "command",
|
||||
Title: "Shell Command",
|
||||
Title: "Command",
|
||||
Description: "Shell Command allows you to run a customized shell/bash Command on the local machine it's running on.",
|
||||
Author: "Hunter Long",
|
||||
AuthorUrl: "https://github.com/hunterlong",
|
||||
|
@ -33,37 +33,30 @@ var Command = &commandLine{¬ifications.Notification{
|
|||
Form: []notifications.NotificationForm{{
|
||||
Type: "text",
|
||||
Title: "Shell or Bash",
|
||||
Placeholder: "/bin/bash",
|
||||
Placeholder: "/usr/bin/curl",
|
||||
DbField: "host",
|
||||
SmallText: "You can use '/bin/sh', '/bin/bash' or even an absolute path for an application.",
|
||||
SmallText: "You can use '/bin/sh', '/bin/bash', '/usr/bin/curl' or an absolute path for an application.",
|
||||
}, {
|
||||
Type: "text",
|
||||
Title: "Command to Run on OnSuccess",
|
||||
Placeholder: "curl google.com",
|
||||
Placeholder: "http://localhost:8080/health",
|
||||
DbField: "var1",
|
||||
SmallText: "This Command will run every time a service is receiving a Successful event.",
|
||||
SmallText: "This Command will run when a service is receiving a Successful event.",
|
||||
}, {
|
||||
Type: "text",
|
||||
Title: "Command to Run on OnFailure",
|
||||
Placeholder: "curl offline.com",
|
||||
Placeholder: "http://localhost:8080/health",
|
||||
DbField: "var2",
|
||||
SmallText: "This Command will run every time a service is receiving a Failing event.",
|
||||
SmallText: "This Command will run when a service is receiving a Failing event.",
|
||||
}}},
|
||||
}
|
||||
|
||||
func runCommand(app string, cmd ...string) (string, string, error) {
|
||||
utils.Log.Infof("Command notifier sending: %s %s", app, strings.Join(cmd, " "))
|
||||
outStr, errStr, err := utils.Command(app, cmd...)
|
||||
return outStr, errStr, err
|
||||
}
|
||||
|
||||
// OnFailure for commandLine will trigger failing service
|
||||
func (c *commandLine) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||
msg := c.GetValue("var2")
|
||||
tmpl := ReplaceVars(msg, s, f)
|
||||
_, _, err := runCommand(c.Host, tmpl)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnSuccess for commandLine will trigger successful service
|
||||
func (c *commandLine) OnSuccess(s *services.Service) error {
|
||||
msg := c.GetValue("var1")
|
||||
|
@ -72,11 +65,19 @@ func (c *commandLine) OnSuccess(s *services.Service) error {
|
|||
return err
|
||||
}
|
||||
|
||||
// OnTest for commandLine triggers when this notifier has been saved
|
||||
func (c *commandLine) OnTest() error {
|
||||
cmds := strings.Split(c.Var1, " ")
|
||||
in, out, err := runCommand(c.Host, cmds...)
|
||||
utils.Log.Infoln(in)
|
||||
utils.Log.Infoln(out)
|
||||
// OnFailure for commandLine will trigger failing service
|
||||
func (c *commandLine) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||
msg := c.GetValue("var2")
|
||||
tmpl := ReplaceVars(msg, s, f)
|
||||
_, _, err := runCommand(c.Host, tmpl)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnTest for commandLine triggers when this notifier has been saved
|
||||
func (c *commandLine) OnTest() (string, error) {
|
||||
tmpl := ReplaceVars(c.Var1, exampleService, exampleFailure)
|
||||
in, out, err := runCommand(c.Host, tmpl)
|
||||
utils.Log.Infoln(in)
|
||||
utils.Log.Infoln(out)
|
||||
return out, err
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import (
|
|||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/statping/statping/types/failures"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/notifier"
|
||||
|
@ -61,23 +60,22 @@ func (d *discord) OnSuccess(s *services.Service) error {
|
|||
}
|
||||
|
||||
// OnSave triggers when this notifier has been saved
|
||||
func (d *discord) OnTest() error {
|
||||
func (d *discord) OnTest() (string, error) {
|
||||
outError := errors.New("Incorrect discord URL, please confirm URL is correct")
|
||||
message := `{"content": "Testing the discord notifier"}`
|
||||
contents, _, err := utils.HttpRequest(Discorder.Host, "POST", "application/json", nil, bytes.NewBuffer([]byte(message)), time.Duration(10*time.Second), true)
|
||||
if string(contents) == "" {
|
||||
return nil
|
||||
return "", nil
|
||||
}
|
||||
var dtt discordTestJson
|
||||
err = json.Unmarshal(contents, &dtt)
|
||||
if err != nil {
|
||||
return outError
|
||||
return string(contents), outError
|
||||
}
|
||||
if dtt.Code == 0 {
|
||||
return outError
|
||||
return string(contents), outError
|
||||
}
|
||||
fmt.Println("discord: ", string(contents))
|
||||
return nil
|
||||
return string(contents), nil
|
||||
}
|
||||
|
||||
type discordTestJson struct {
|
||||
|
|
|
@ -186,7 +186,7 @@ func (e *emailer) OnSuccess(s *services.Service) error {
|
|||
}
|
||||
|
||||
// OnTest triggers when this notifier has been saved
|
||||
func (e *emailer) OnTest() error {
|
||||
func (e *emailer) OnTest() (string, error) {
|
||||
testService := &services.Service{
|
||||
Id: 1,
|
||||
Name: "Example Service",
|
||||
|
@ -201,14 +201,16 @@ func (e *emailer) OnTest() error {
|
|||
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)
|
||||
email := &emailOutgoing{
|
||||
To: e.Var2,
|
||||
Subject: fmt.Sprintf("Service %v is Back Online", testService.Name),
|
||||
Subject: subject,
|
||||
Template: mainEmailTemplate,
|
||||
Data: testService,
|
||||
From: e.Var1,
|
||||
}
|
||||
return e.dialSend(email)
|
||||
err := e.dialSend(email)
|
||||
return subject, err
|
||||
}
|
||||
|
||||
func (e *emailer) dialSend(email *emailOutgoing) error {
|
||||
|
|
|
@ -43,28 +43,31 @@ var LineNotify = &lineNotifier{¬ifications.Notification{
|
|||
}
|
||||
|
||||
// Send will send a HTTP Post with the Authorization to the notify-api.line.me server. It accepts type: string
|
||||
func (l *lineNotifier) sendMessage(message string) error {
|
||||
func (l *lineNotifier) sendMessage(message string) (string, error) {
|
||||
v := url.Values{}
|
||||
v.Set("message", message)
|
||||
headers := []string{fmt.Sprintf("Authorization=Bearer %v", l.ApiSecret)}
|
||||
_, _, err := utils.HttpRequest("https://notify-api.line.me/api/notify", "POST", "application/x-www-form-urlencoded", headers, strings.NewReader(v.Encode()), time.Duration(10*time.Second), true)
|
||||
return err
|
||||
content, _, err := utils.HttpRequest("https://notify-api.line.me/api/notify", "POST", "application/x-www-form-urlencoded", headers, strings.NewReader(v.Encode()), time.Duration(10*time.Second), true)
|
||||
return string(content), err
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (l *lineNotifier) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
||||
return l.sendMessage(msg)
|
||||
_, err := l.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (l *lineNotifier) OnSuccess(s *services.Service) error {
|
||||
msg := fmt.Sprintf("Service %s is online!", s.Name)
|
||||
return l.sendMessage(msg)
|
||||
_, err := l.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnTest triggers when this notifier has been saved
|
||||
func (l *lineNotifier) OnTest() error {
|
||||
func (l *lineNotifier) OnTest() (string, error) {
|
||||
msg := fmt.Sprintf("Testing if Line Notifier is working!")
|
||||
return l.sendMessage(msg)
|
||||
_, err := l.sendMessage(msg)
|
||||
return msg, err
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ func (m *mobilePush) Select() *notifications.Notification {
|
|||
|
||||
var Mobile = &mobilePush{¬ifications.Notification{
|
||||
Method: "mobile",
|
||||
Title: "Mobile Notifications",
|
||||
Title: "Mobile",
|
||||
Description: `Receive push notifications on your Mobile device using the Statping App. You can scan the Authentication QR Code found in Settings to get the Mobile app setup in seconds.
|
||||
<p align="center"><a href="https://play.google.com/store/apps/details?id=com.statping"><img src="https://img.cjx.io/google-play.svg"></a><a href="https://itunes.apple.com/us/app/apple-store/id1445513219"><img src="https://img.cjx.io/app-store-badge.svg"></a></p>`,
|
||||
Author: "Hunter Long",
|
||||
|
@ -97,7 +97,7 @@ func (m *mobilePush) OnSuccess(s *services.Service) error {
|
|||
}
|
||||
|
||||
// OnTest triggers when this notifier has been saved
|
||||
func (m *mobilePush) OnTest() error {
|
||||
func (m *mobilePush) OnTest() (string, error) {
|
||||
msg := &pushArray{
|
||||
Message: "Testing the Mobile Notifier",
|
||||
Title: "Testing Notifications",
|
||||
|
@ -107,18 +107,18 @@ func (m *mobilePush) OnTest() error {
|
|||
}
|
||||
body, err := pushRequest(msg)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
var output mobileResponse
|
||||
err = json.Unmarshal(body, &output)
|
||||
if err != nil {
|
||||
return err
|
||||
return string(body), err
|
||||
}
|
||||
if len(output.Logs) == 0 {
|
||||
return nil
|
||||
return string(body), err
|
||||
} else {
|
||||
firstLog := output.Logs[0].Error
|
||||
return fmt.Errorf("Mobile Notification error: %v", firstLog)
|
||||
return string(body), fmt.Errorf("Mobile Notification error: %v", firstLog)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ func InitNotifiers() {
|
|||
Twilio,
|
||||
Webhook,
|
||||
Mobile,
|
||||
Pushover,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,85 @@
|
|||
package notifiers
|
||||
|
||||
import (
|
||||
"github.com/statping/statping/types/services"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAllNotifiers(t *testing.T) {
|
||||
|
||||
notifiers := []notifierTest{
|
||||
{
|
||||
Notifier: Command,
|
||||
RequiredENV: nil,
|
||||
},
|
||||
{
|
||||
Notifier: Discorder,
|
||||
RequiredENV: []string{"DISCORD_URL"},
|
||||
},
|
||||
{
|
||||
Notifier: email,
|
||||
RequiredENV: []string{"EMAIL_HOST", "EMAIL_USER", "EMAIL_PASS", "EMAIL_OUTGOING", "EMAIL_SEND_TO", "EMAIL_PORT"},
|
||||
},
|
||||
{
|
||||
Notifier: Mobile,
|
||||
RequiredENV: []string{"MOBILE_ID", "MOBILE_NUMBER"},
|
||||
},
|
||||
{
|
||||
Notifier: Pushover,
|
||||
RequiredENV: []string{"PUSHOVER_TOKEN", "PUSHOVER_API"},
|
||||
},
|
||||
{
|
||||
Notifier: slacker,
|
||||
RequiredENV: []string{"SLACK_URL"},
|
||||
},
|
||||
{
|
||||
Notifier: Telegram,
|
||||
RequiredENV: []string{"TELEGRAM_TOKEN", "TELEGRAM_CHANNEL"},
|
||||
},
|
||||
{
|
||||
Notifier: Twilio,
|
||||
RequiredENV: []string{"TWILIO_SID", "TWILIO_SECRET", "TWILIO_FROM", "TWILIO_TO"},
|
||||
},
|
||||
{
|
||||
Notifier: Webhook,
|
||||
RequiredENV: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, n := range notifiers {
|
||||
|
||||
if !getEnvs(n.RequiredENV) {
|
||||
t.Skip()
|
||||
continue
|
||||
}
|
||||
|
||||
Add(n.Notifier)
|
||||
|
||||
err := n.Notifier.OnSuccess(exampleService)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = n.Notifier.OnFailure(exampleService, exampleFailure)
|
||||
assert.Nil(t, err)
|
||||
|
||||
err = n.Notifier.OnTest()
|
||||
assert.Nil(t, err)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getEnvs(env []string) bool {
|
||||
for _, v := range env {
|
||||
if os.Getenv(v) == "" {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type notifierTest struct {
|
||||
Notifier services.ServiceNotifier
|
||||
RequiredENV []string
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package notifiers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
pushoverUrl = "https://api.pushover.net/1/messages.json"
|
||||
)
|
||||
|
||||
var _ notifier.Notifier = (*pushover)(nil)
|
||||
|
||||
type pushover struct {
|
||||
*notifications.Notification
|
||||
}
|
||||
|
||||
func (t *pushover) Select() *notifications.Notification {
|
||||
return t.Notification
|
||||
}
|
||||
|
||||
var Pushover = &pushover{¬ifications.Notification{
|
||||
Method: "pushover",
|
||||
Title: "Pushover",
|
||||
Description: "Use Pushover to receive push notifications. You will need to create a <a href=\"https://pushover.net/apps/build\">New Application</a> on Pushover before using this notifier.",
|
||||
Author: "Hunter Long",
|
||||
AuthorUrl: "https://github.com/hunterlong",
|
||||
Icon: "fa dot-circle",
|
||||
Delay: time.Duration(10 * time.Second),
|
||||
Limits: 60,
|
||||
Form: []notifications.NotificationForm{{
|
||||
Type: "text",
|
||||
Title: "User Token",
|
||||
Placeholder: "Insert your device's Pushover Token",
|
||||
DbField: "api_key",
|
||||
Required: true,
|
||||
}, {
|
||||
Type: "text",
|
||||
Title: "Application API Key",
|
||||
Placeholder: "Create an Application and insert the API Key here",
|
||||
DbField: "api_secret",
|
||||
Required: true,
|
||||
},
|
||||
}},
|
||||
}
|
||||
|
||||
// Send will send a HTTP Post to the Pushover API. It accepts type: string
|
||||
func (t *pushover) sendMessage(message string) (string, error) {
|
||||
v := url.Values{}
|
||||
v.Set("token", t.ApiSecret)
|
||||
v.Set("user", t.ApiKey)
|
||||
v.Set("message", message)
|
||||
rb := strings.NewReader(v.Encode())
|
||||
|
||||
content, _, err := utils.HttpRequest(pushoverUrl, "POST", "application/x-www-form-urlencoded", nil, rb, time.Duration(10*time.Second), true)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(content), err
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (t *pushover) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
||||
_, err := t.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (t *pushover) OnSuccess(s *services.Service) error {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
|
||||
_, err := t.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnTest will test the Pushover SMS messaging
|
||||
func (t *pushover) OnTest() (string, error) {
|
||||
msg := fmt.Sprintf("Testing the Pushover Notifier")
|
||||
content, err := t.sendMessage(msg)
|
||||
return content, err
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
package notifiers
|
||||
|
||||
import (
|
||||
"github.com/statping/statping/database"
|
||||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/null"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
PUSHOVER_TOKEN = os.Getenv("PUSHOVER_TOKEN")
|
||||
PUSHOVER_API = os.Getenv("PUSHOVER_API")
|
||||
)
|
||||
|
||||
func TestPushoverNotifier(t *testing.T) {
|
||||
t.SkipNow()
|
||||
db, err := database.OpenTester()
|
||||
require.Nil(t, err)
|
||||
db.AutoMigrate(¬ifications.Notification{})
|
||||
notifications.SetDB(db)
|
||||
|
||||
if PUSHOVER_TOKEN == "" || PUSHOVER_API == "" {
|
||||
t.Log("Pushover notifier testing skipped, missing PUSHOVER_TOKEN and PUSHOVER_API environment variable")
|
||||
t.SkipNow()
|
||||
}
|
||||
|
||||
t.Run("Load Pushover", func(t *testing.T) {
|
||||
Pushover.ApiKey = PUSHOVER_TOKEN
|
||||
Pushover.ApiSecret = PUSHOVER_API
|
||||
Pushover.Enabled = null.NewNullBool(true)
|
||||
|
||||
Add(Pushover)
|
||||
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "Hunter Long", Pushover.Author)
|
||||
assert.Equal(t, PUSHOVER_TOKEN, Pushover.ApiKey)
|
||||
})
|
||||
|
||||
t.Run("Pushover Within Limits", func(t *testing.T) {
|
||||
assert.True(t, Pushover.CanSend())
|
||||
})
|
||||
|
||||
t.Run("Pushover OnFailure", func(t *testing.T) {
|
||||
err := Pushover.OnFailure(exampleService, exampleFailure)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Pushover OnSuccess", func(t *testing.T) {
|
||||
err := Pushover.OnSuccess(exampleService)
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Pushover Test", func(t *testing.T) {
|
||||
err := Pushover.OnTest()
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
}
|
|
@ -58,16 +58,16 @@ func (s *slack) sendSlack(msg string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *slack) OnTest() error {
|
||||
func (s *slack) OnTest() (string, error) {
|
||||
contents, resp, err := utils.HttpRequest(s.Host, "POST", "application/json", nil, bytes.NewBuffer([]byte(`{"text":"testing message"}`)), time.Duration(10*time.Second), true)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if string(contents) != "ok" {
|
||||
return errors.New("the slack response was incorrect, check the URL")
|
||||
return string(contents), errors.New("the slack response was incorrect, check the URL")
|
||||
}
|
||||
return nil
|
||||
return string(contents), nil
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
|
|
|
@ -51,7 +51,7 @@ var Telegram = &telegram{¬ifications.Notification{
|
|||
}
|
||||
|
||||
// Send will send a HTTP Post to the Telegram API. It accepts type: string
|
||||
func (t *telegram) sendMessage(message string) error {
|
||||
func (t *telegram) sendMessage(message string) (string, error) {
|
||||
apiEndpoint := fmt.Sprintf("https://api.telegram.org/bot%v/sendMessage", t.ApiSecret)
|
||||
|
||||
v := url.Values{}
|
||||
|
@ -65,27 +65,30 @@ func (t *telegram) sendMessage(message string) error {
|
|||
if !success {
|
||||
errorOut := telegramError(contents)
|
||||
out := fmt.Sprintf("Error code %v - %v", errorOut.ErrorCode, errorOut.Description)
|
||||
return errors.New(out)
|
||||
return string(contents), errors.New(out)
|
||||
}
|
||||
return err
|
||||
return string(contents), err
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (t *telegram) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
||||
return t.sendMessage(msg)
|
||||
_, err := t.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (t *telegram) OnSuccess(s *services.Service) error {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
|
||||
return t.sendMessage(msg)
|
||||
_, err := t.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnTest will test the Twilio SMS messaging
|
||||
func (t *telegram) OnTest() error {
|
||||
func (t *telegram) OnTest() (string, error) {
|
||||
msg := fmt.Sprintf("Testing the Twilio SMS Notifier on your Statping server")
|
||||
return t.sendMessage(msg)
|
||||
content, err := t.sendMessage(msg)
|
||||
return content, err
|
||||
}
|
||||
|
||||
func telegramSuccess(res []byte) (bool, telegramResponse) {
|
||||
|
|
|
@ -25,6 +25,7 @@ func init() {
|
|||
}
|
||||
|
||||
func TestTelegramNotifier(t *testing.T) {
|
||||
t.SkipNow()
|
||||
db, err := database.OpenTester()
|
||||
require.Nil(t, err)
|
||||
db.AutoMigrate(¬ifications.Notification{})
|
||||
|
|
|
@ -61,7 +61,7 @@ var Twilio = &twilio{¬ifications.Notification{
|
|||
}
|
||||
|
||||
// Send will send a HTTP Post to the Twilio SMS API. It accepts type: string
|
||||
func (t *twilio) sendMessage(message string) error {
|
||||
func (t *twilio) sendMessage(message string) (string, error) {
|
||||
twilioUrl := fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%v/Messages.json", t.GetValue("api_key"))
|
||||
|
||||
v := url.Values{}
|
||||
|
@ -75,25 +75,27 @@ func (t *twilio) sendMessage(message string) error {
|
|||
if !success {
|
||||
errorOut := twilioError(contents)
|
||||
out := fmt.Sprintf("Error code %v - %v", errorOut.Code, errorOut.Message)
|
||||
return errors.New(out)
|
||||
return string(contents), errors.New(out)
|
||||
}
|
||||
return err
|
||||
return string(contents), err
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
func (t *twilio) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
||||
return t.sendMessage(msg)
|
||||
_, err := t.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnSuccess will trigger successful service
|
||||
func (t *twilio) OnSuccess(s *services.Service) error {
|
||||
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
|
||||
return t.sendMessage(msg)
|
||||
_, err := t.sendMessage(msg)
|
||||
return err
|
||||
}
|
||||
|
||||
// OnTest will test the Twilio SMS messaging
|
||||
func (t *twilio) OnTest() error {
|
||||
func (t *twilio) OnTest() (string, error) {
|
||||
msg := fmt.Sprintf("Testing the Twilio SMS Notifier")
|
||||
return t.sendMessage(msg)
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ type webhooker struct {
|
|||
|
||||
var Webhook = &webhooker{¬ifications.Notification{
|
||||
Method: webhookMethod,
|
||||
Title: "HTTP webhooker",
|
||||
Title: "Webhook",
|
||||
Description: "Send a custom HTTP request to a specific URL with your own body, headers, and parameters.",
|
||||
Author: "Hunter Long",
|
||||
AuthorUrl: "https://github.com/hunterlong",
|
||||
|
@ -113,16 +113,17 @@ func (w *webhooker) sendHttpWebhook(body string) (*http.Response, error) {
|
|||
return resp, err
|
||||
}
|
||||
|
||||
func (w *webhooker) OnTest() error {
|
||||
func (w *webhooker) OnTest() (string, error) {
|
||||
body := ReplaceVars(w.Var2, exampleService, exampleFailure)
|
||||
resp, err := w.sendHttpWebhook(body)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
content, err := ioutil.ReadAll(resp.Body)
|
||||
utils.Log.Infoln(fmt.Sprintf("Webhook notifier received: '%v'", string(content)))
|
||||
return err
|
||||
out := fmt.Sprintf("Webhook notifier received: '%v'", string(content))
|
||||
utils.Log.Infoln(out)
|
||||
return out, err
|
||||
}
|
||||
|
||||
// OnFailure will trigger failing service
|
||||
|
|
|
@ -32,7 +32,7 @@ func All() []*Checkin {
|
|||
}
|
||||
|
||||
func (c *Checkin) Create() error {
|
||||
c.ApiKey = utils.RandomString(7)
|
||||
c.ApiKey = utils.RandomString(32)
|
||||
q := db.Create(c)
|
||||
|
||||
c.Start()
|
||||
|
|
|
@ -57,7 +57,7 @@ func Connect(configs *DbConfig, retry bool) error {
|
|||
if err != nil {
|
||||
log.Debugln(fmt.Sprintf("Database connection error %s", err))
|
||||
if retry {
|
||||
log.Errorln(fmt.Sprintf("Database %s connection to '%s' is not available, trying again in 5 seconds...", configs.DbConn, configs.DbHost))
|
||||
log.Warnln(fmt.Sprintf("Database %s connection to '%s' is not available, trying again in 5 seconds...", configs.DbConn, configs.DbHost))
|
||||
time.Sleep(5 * time.Second)
|
||||
return Connect(configs, retry)
|
||||
} else {
|
||||
|
|
|
@ -30,7 +30,13 @@ func Select() (*Core, error) {
|
|||
return nil, db.Error()
|
||||
}
|
||||
App = &c
|
||||
App.UseCdn = null.NewNullBool(os.Getenv("USE_CDN") == "true")
|
||||
|
||||
if os.Getenv("USE_CDN") == "true" {
|
||||
App.UseCdn = null.NewNullBool(true)
|
||||
}
|
||||
if os.Getenv("ALLOW_REPORTS") == "true" {
|
||||
App.AllowReports = null.NewNullBool(true)
|
||||
}
|
||||
return App, q.Error()
|
||||
}
|
||||
|
||||
|
|
|
@ -35,6 +35,7 @@ type Core struct {
|
|||
Timezone float32 `gorm:"column:timezone;default:-8.0" json:"timezone,omitempty"`
|
||||
LoggedIn bool `gorm:"-" json:"logged_in"`
|
||||
IsAdmin bool `gorm:"-" json:"admin"`
|
||||
AllowReports null.NullBool `gorm:"column:allow_reports;default:false" json:"allow_reports"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
Started time.Time `gorm:"-" json:"started_on"`
|
||||
|
|
|
@ -9,7 +9,7 @@ func Samples() error {
|
|||
incident1 := &Incident{
|
||||
Title: "Github Issues",
|
||||
Description: "There are new features for Statping, if you have any issues please visit the Github Repo.",
|
||||
ServiceId: 2,
|
||||
ServiceId: 4,
|
||||
}
|
||||
if err := incident1.Create(); err != nil {
|
||||
return err
|
||||
|
@ -18,7 +18,7 @@ func Samples() error {
|
|||
incident2 := &Incident{
|
||||
Title: "Recent Downtime",
|
||||
Description: "We've noticed an issue with authentications and we're looking into it now.",
|
||||
ServiceId: 4,
|
||||
ServiceId: 5,
|
||||
}
|
||||
if err := incident2.Create(); err != nil {
|
||||
return err
|
||||
|
|
|
@ -9,5 +9,5 @@ import (
|
|||
type Notifier interface {
|
||||
OnSuccess(*services.Service) error // OnSuccess is triggered when a service is successful
|
||||
OnFailure(*services.Service, *failures.Failure) error // OnFailure is triggered when a service is failing
|
||||
OnTest() error // OnTest is triggered for testing
|
||||
OnTest() (string, error) // OnTest is triggered for testing
|
||||
}
|
||||
|
|
|
@ -28,6 +28,6 @@ func FindNotifier(method string) *notifications.Notification {
|
|||
type ServiceNotifier interface {
|
||||
OnSuccess(*Service) error // OnSuccess is triggered when a service is successful
|
||||
OnFailure(*Service, *failures.Failure) error // OnFailure is triggered when a service is failing
|
||||
OnTest() error // OnTest is triggered for testing
|
||||
OnTest() (string, error) // OnTest is triggered for testing
|
||||
Select() *notifications.Notification // OnTest is triggered for testing
|
||||
}
|
||||
|
|
48
utils/log.go
48
utils/log.go
|
@ -4,6 +4,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/fatih/structs"
|
||||
"github.com/getsentry/sentry-go"
|
||||
"github.com/prometheus/common/log"
|
||||
Logger "github.com/sirupsen/logrus"
|
||||
"github.com/statping/statping/types/null"
|
||||
"gopkg.in/natefinch/lumberjack.v2"
|
||||
|
@ -16,12 +17,13 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
Log = Logger.StandardLogger()
|
||||
ljLogger *lumberjack.Logger
|
||||
LastLines []*logRow
|
||||
LockLines sync.Mutex
|
||||
VerboseMode int
|
||||
version string
|
||||
Log = Logger.StandardLogger()
|
||||
ljLogger *lumberjack.Logger
|
||||
LastLines []*logRow
|
||||
LockLines sync.Mutex
|
||||
VerboseMode int
|
||||
version string
|
||||
allowReports bool
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -29,22 +31,32 @@ const (
|
|||
errorReporter = "https://ddf2784201134d51a20c3440e222cebe@sentry.statping.com/4"
|
||||
)
|
||||
|
||||
func SentryInit(v string) {
|
||||
if v == "" {
|
||||
v = "development"
|
||||
func SentryInit(v *string, allow bool) {
|
||||
allowReports = allow
|
||||
if v != nil {
|
||||
if *v == "" {
|
||||
*v = "development"
|
||||
}
|
||||
version = *v
|
||||
}
|
||||
version = v
|
||||
errorEnv := Getenv("GO_ENV", "production").(string)
|
||||
if err := sentry.Init(sentry.ClientOptions{
|
||||
Dsn: errorReporter,
|
||||
Environment: errorEnv,
|
||||
Release: v,
|
||||
}); err != nil {
|
||||
Log.Errorln(err)
|
||||
goEnv := Getenv("GO_ENV", "production").(string)
|
||||
allowReports := Getenv("ALLOW_REPORTS", false).(bool)
|
||||
if allowReports || allow {
|
||||
if err := sentry.Init(sentry.ClientOptions{
|
||||
Dsn: errorReporter,
|
||||
Environment: goEnv,
|
||||
Release: version,
|
||||
}); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
Log.Infoln("Error Reporting initiated, thank you!")
|
||||
}
|
||||
}
|
||||
|
||||
func SentryErr(err error) {
|
||||
if !allowReports {
|
||||
return
|
||||
}
|
||||
sentry.CaptureException(err)
|
||||
}
|
||||
|
||||
|
@ -63,7 +75,7 @@ type hook struct {
|
|||
|
||||
func (t *hook) Fire(e *Logger.Entry) error {
|
||||
pushLastLine(e.Message)
|
||||
if e.Level == Logger.ErrorLevel {
|
||||
if e.Level == Logger.ErrorLevel && allowReports {
|
||||
SentryLogEntry(e)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.90.23
|
||||
0.90.25
|
||||
|
|
Loading…
Reference in New Issue