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:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@master
|
uses: actions/checkout@master
|
||||||
- name: Create a Sentry.io release
|
- name: Create a Sentry release
|
||||||
uses: tclindner/sentry-releases-action@v1.0.0
|
uses: tclindner/sentry-releases-action@v1.0.0
|
||||||
env:
|
env:
|
||||||
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
||||||
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
SENTRY_URL: ${{ secrets.SENTRY_URL }}
|
||||||
SENTRY_ORG: Statping
|
SENTRY_ORG: statping
|
||||||
SENTRY_PROJECT: golang
|
SENTRY_PROJECT: backend
|
||||||
with:
|
with:
|
||||||
tagName: ${{ github.ref }}
|
tagName: ${{ github.ref }}
|
||||||
environment: qa
|
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:
|
branches:
|
||||||
only:
|
only:
|
||||||
- master
|
- master
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- ~/.npm
|
|
||||||
- ~/.cache
|
|
||||||
- $GOPATH/pkg/mod
|
|
||||||
- $GOPATH/src/github.com/statping/statping/frontend/node_modules
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- "PATH=$HOME/.local/bin:$PATH"
|
- "PATH=$HOME/.local/bin:$PATH"
|
||||||
|
@ -23,7 +17,6 @@ env:
|
||||||
- DB_USER=travis
|
- DB_USER=travis
|
||||||
- DB_PASS=
|
- DB_PASS=
|
||||||
- DB_DATABASE=test
|
- DB_DATABASE=test
|
||||||
- GO_ENV=test
|
|
||||||
- STATPING_DIR=$GOPATH/src/github.com/statping/statping
|
- STATPING_DIR=$GOPATH/src/github.com/statping/statping
|
||||||
go: 1.14
|
go: 1.14
|
||||||
go_import_path: github.com/statping/statping
|
go_import_path: github.com/statping/statping
|
||||||
|
@ -36,6 +29,7 @@ install:
|
||||||
- "make test-deps yarn clean compile install"
|
- "make test-deps yarn clean compile install"
|
||||||
language: go
|
language: go
|
||||||
addons:
|
addons:
|
||||||
|
chrome: stable
|
||||||
apt:
|
apt:
|
||||||
packages:
|
packages:
|
||||||
- libgconf-2-4
|
- 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
|
# 0.90.23
|
||||||
- Added Incident Reporting
|
- Added Incident Reporting
|
||||||
- Added Cypress tests
|
- Added Cypress tests
|
||||||
- Added Github and Google OAuth login (beta)
|
- Added Github and Google OAuth login (beta)
|
||||||
|
- Added Delete All Failures
|
||||||
|
- Added Checkin form
|
||||||
|
- Added Pushover notifier
|
||||||
|
|
||||||
# 0.90.22
|
# 0.90.22
|
||||||
- Added range input types for integer form fields
|
- Added range input types for integer form fields
|
||||||
|
|
11
Makefile
11
Makefile
|
@ -34,7 +34,7 @@ test: clean
|
||||||
release: test-deps
|
release: test-deps
|
||||||
wget -O statping.gpg $(SIGN_URL)
|
wget -O statping.gpg $(SIGN_URL)
|
||||||
gpg --import statping.gpg
|
gpg --import statping.gpg
|
||||||
make build-all
|
make build-all upload_to_s3
|
||||||
|
|
||||||
test-ci: clean compile test-deps
|
test-ci: clean compile test-deps
|
||||||
SASS=`which sass` go test -v -covermode=count -coverprofile=coverage.out -p=1 ./...
|
SASS=`which sass` go test -v -covermode=count -coverprofile=coverage.out -p=1 ./...
|
||||||
|
@ -260,11 +260,10 @@ publish-dev:
|
||||||
publish-homebrew:
|
publish-homebrew:
|
||||||
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token $(TRAVIS_API)" -d $(PUBLISH_BODY) https://api.travis-ci.com/repo/statping%2Fhomebrew-statping/requests
|
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token $(TRAVIS_API)" -d $(PUBLISH_BODY) https://api.travis-ci.com/repo/statping%2Fhomebrew-statping/requests
|
||||||
|
|
||||||
upload_to_s3:
|
upload_to_s3: travis_s3_creds
|
||||||
aws s3 cp ./source/css $(ASSETS_BKT) --recursive --exclude "*" --include "*.css"
|
aws s3 cp ./source/dist/css $(ASSETS_BKT) --recursive --exclude "*" --include "*.css"
|
||||||
aws s3 cp ./source/js $(ASSETS_BKT) --recursive --exclude "*" --include "*.js"
|
aws s3 cp ./source/dist/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/dist/scss $(ASSETS_BKT) --recursive --exclude "*" --include "*.scss"
|
||||||
aws s3 cp ./source/scss $(ASSETS_BKT) --recursive --exclude "*" --include "*.scss"
|
|
||||||
aws s3 cp ./install.sh $(ASSETS_BKT)
|
aws s3 cp ./install.sh $(ASSETS_BKT)
|
||||||
|
|
||||||
travis_s3_creds:
|
travis_s3_creds:
|
||||||
|
|
|
@ -132,6 +132,7 @@ func TestAssetsCLI(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSassCLI(t *testing.T) {
|
func TestSassCLI(t *testing.T) {
|
||||||
|
t.SkipNow()
|
||||||
catchCLI([]string{"sass"})
|
catchCLI([]string{"sass"})
|
||||||
assert.FileExists(t, dir+"/assets/css/main.css")
|
assert.FileExists(t, dir+"/assets/css/main.css")
|
||||||
assert.FileExists(t, dir+"/assets/css/style.css")
|
assert.FileExists(t, dir+"/assets/css/style.css")
|
||||||
|
|
13
cmd/main.go
13
cmd/main.go
|
@ -28,8 +28,7 @@ var (
|
||||||
verboseMode int
|
verboseMode int
|
||||||
port int
|
port int
|
||||||
log = utils.Log.WithField("type", "cmd")
|
log = utils.Log.WithField("type", "cmd")
|
||||||
|
confgs *configs.DbConfig
|
||||||
confgs *configs.DbConfig
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// parseFlags will parse the application flags
|
// parseFlags will parse the application flags
|
||||||
|
@ -75,8 +74,6 @@ func main() {
|
||||||
|
|
||||||
parseFlags()
|
parseFlags()
|
||||||
|
|
||||||
utils.SentryInit(VERSION)
|
|
||||||
|
|
||||||
if err := source.Assets(); err != nil {
|
if err := source.Assets(); err != nil {
|
||||||
exit(err)
|
exit(err)
|
||||||
}
|
}
|
||||||
|
@ -202,18 +199,14 @@ func InitApp() error {
|
||||||
if _, err := core.Select(); err != nil {
|
if _, err := core.Select(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := services.SelectAllServices(true); err != nil {
|
if _, err := services.SelectAllServices(true); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
go services.CheckServices()
|
go services.CheckServices()
|
||||||
|
|
||||||
notifiers.InitNotifiers()
|
notifiers.InitNotifiers()
|
||||||
|
go database.Maintenance()
|
||||||
|
utils.SentryInit(&VERSION, core.App.AllowReports.Bool)
|
||||||
core.App.Setup = true
|
core.App.Setup = true
|
||||||
core.App.Started = utils.Now()
|
core.App.Started = utils.Now()
|
||||||
|
|
||||||
go database.Maintenance()
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,10 +8,9 @@
|
||||||
"DB_PASS": "password123",
|
"DB_PASS": "password123",
|
||||||
"GO_ENV": "production"
|
"GO_ENV": "production"
|
||||||
},
|
},
|
||||||
"baseUrl": "http://localhost:8888",
|
|
||||||
"chromeWebSecurity": false,
|
"chromeWebSecurity": false,
|
||||||
"defaultCommandTimeout": 15000,
|
"defaultCommandTimeout": 30000,
|
||||||
"requestTimeout": 15000,
|
"requestTimeout": 30000,
|
||||||
"watchForFileChanges": false,
|
"watchForFileChanges": false,
|
||||||
"failOnStatusCode": false
|
"failOnStatusCode": false
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,12 @@
|
||||||
|
|
||||||
context('Setup Process', () => {
|
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', () => {
|
it('should setup Statping with SQLite', () => {
|
||||||
cy.visit('/setup', {failOnStatusCode: false})
|
cy.visit('/setup', {failOnStatusCode: false})
|
||||||
cy.get('#db_connection').select('sqlite')
|
cy.get('#db_connection').select('sqlite')
|
||||||
|
@ -19,8 +25,34 @@ context('Setup Process', () => {
|
||||||
|
|
||||||
it('should have sample data', () => {
|
it('should have sample data', () => {
|
||||||
cy.visit('/')
|
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('.card').should('have.length', 5)
|
||||||
cy.get('.group_header').should('have.length', 2)
|
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.visit('/dashboard/services')
|
||||||
cy.get('.sortable_groups > tr').should('have.length', 3)
|
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(1).contains('PUBLIC')
|
||||||
cy.get('.sortable_groups > tr').eq(2).contains('PRIVATE')
|
cy.get('.sortable_groups > tr').eq(2).contains('PRIVATE')
|
||||||
})
|
})
|
||||||
|
@ -48,17 +48,28 @@ context('Groups Tests', () => {
|
||||||
cy.get('button[type="submit"]').click()
|
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', () => {
|
it('should confirm new groups', () => {
|
||||||
cy.visit('/dashboard/services')
|
cy.visit('/dashboard/services')
|
||||||
cy.get('.sortable_groups > tr').should('have.length', 5)
|
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('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('PRIVATE')
|
||||||
cy.get('.sortable_groups > tr').eq(1).contains('Test Private Group')
|
cy.get('.sortable_groups > tr').eq(1).contains('Test Private Group')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should delete new groups', () => {
|
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(0).find('.btn-danger').click()
|
||||||
cy.get('.sortable_groups > tr').eq(1).find('.btn-danger').click()
|
cy.get('.sortable_groups > tr').eq(1).find('.btn-danger').click()
|
||||||
cy.get('.sortable_groups > tr').should('have.length', 3)
|
cy.get('.sortable_groups > tr').should('have.length', 3)
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import "../support/commands"
|
import "../support/commands"
|
||||||
|
|
||||||
context('Messages Tests', () => {
|
context('Annoucements Tests', () => {
|
||||||
|
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
@ -43,4 +43,10 @@ context('Messages Tests', () => {
|
||||||
cy.get('tbody > tr').should('have.length', 3)
|
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)
|
cy.getCookies().should('have.length', 1)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// uzrwstmtd69hi4wgzsj27q2v29mtpu
|
||||||
it('should confirm notifiers are installed', () => {
|
it('should confirm notifiers are installed', () => {
|
||||||
cy.visit('/dashboard/settings')
|
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_key').should('not.have.value', '')
|
||||||
cy.get('#api_secret').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', () => {
|
it('should goto services', () => {
|
||||||
cy.visit('/dashboard/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)
|
cy.get('.sortable_groups > tr').should('have.length', 3)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -79,7 +79,7 @@ context('Services Tests', () => {
|
||||||
|
|
||||||
it('should create new ICMP service', () => {
|
it('should create new ICMP service', () => {
|
||||||
cy.visit('/dashboard/create_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_type').select('icmp')
|
||||||
cy.get('#service_url').clear().type('8.8.8.8')
|
cy.get('#service_url').clear().type('8.8.8.8')
|
||||||
|
|
||||||
|
@ -93,16 +93,20 @@ context('Services Tests', () => {
|
||||||
|
|
||||||
it('should confirm new services', () => {
|
it('should confirm new services', () => {
|
||||||
cy.visit('/dashboard/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', () => {
|
it('should delete new services', () => {
|
||||||
cy.visit('/dashboard/services')
|
cy.visit('/dashboard/services')
|
||||||
cy.get('#services_list > tr').eq(0).find('.btn-danger').click()
|
cy.get('#services_list > tr').should('have.length', 10)
|
||||||
cy.get('#services_list > tr').eq(0).find('.btn-danger').click()
|
cy.get('#services_list > tr').eq(0).find('a.btn-danger').click()
|
||||||
cy.get('#services_list > tr').eq(0).find('.btn-danger').click()
|
cy.get('#services_list > tr').should('have.length', 9)
|
||||||
cy.get('#services_list > tr').eq(0).find('.btn-danger').click()
|
cy.get('#services_list > tr').eq(1).find('a.btn-danger').click()
|
||||||
cy.get('#services_list > tr').should('have.length', 4)
|
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', () => {
|
it('should confirm notifiers are installed', () => {
|
||||||
cy.visit('/dashboard/settings')
|
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_key').should('not.have.value', '')
|
||||||
cy.get('#api_secret').should('not.have.value', '')
|
cy.get('#api_secret').should('not.have.value', '')
|
||||||
|
@ -36,7 +36,7 @@ context('Settings Tests', () => {
|
||||||
it('should update Statping settings', () => {
|
it('should update Statping settings', () => {
|
||||||
cy.visit('/dashboard/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('#description').clear().type('Statping can use Cypress e2e testing to make it more stable!')
|
||||||
cy.get('#domain').clear().type('http://localhost:8888')
|
cy.get('#domain').clear().type('http://localhost:8888')
|
||||||
cy.get('#footer').clear().type('Statping Custom Footer')
|
cy.get('#footer').clear().type('Statping Custom Footer')
|
||||||
|
@ -46,7 +46,7 @@ context('Settings Tests', () => {
|
||||||
it('should confirm Statping settings', () => {
|
it('should confirm Statping settings', () => {
|
||||||
cy.visit('/dashboard/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('#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('#domain').should('have.value', 'http://localhost:8888')
|
||||||
cy.get('#footer').should('have.value', 'Statping Custom Footer')
|
cy.get('#footer').should('have.value', 'Statping Custom Footer')
|
||||||
|
@ -59,6 +59,13 @@ context('Settings Tests', () => {
|
||||||
cy.get('.footer').should('contain', 'Statping Custom Footer')
|
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', () => {
|
it('should create Local Assets', () => {
|
||||||
cy.visit('/dashboard/settings')
|
cy.visit('/dashboard/settings')
|
||||||
cy.get('#v-pills-style-tab').click()
|
cy.get('#v-pills-style-tab').click()
|
||||||
|
|
|
@ -39,13 +39,39 @@ context('Users Tests', () => {
|
||||||
cy.get('#password_confirm').clear().type('password123')
|
cy.get('#password_confirm').clear().type('password123')
|
||||||
|
|
||||||
cy.get('button[type="submit"]').click()
|
cy.get('button[type="submit"]').click()
|
||||||
|
cy.get('#users_table > tr').should('have.length', 2)
|
||||||
})
|
})
|
||||||
|
|
||||||
// it('should confirm new user', () => {
|
it('should create new Admin User', () => {
|
||||||
// cy.visit('/dashboard/users')
|
cy.visit('/dashboard/users')
|
||||||
// cy.get('#users_table > tr').should('have.length', 2)
|
cy.get('#username').clear().type('admin3')
|
||||||
// cy.get('#users_table > tr').eq(0).contains('admin')
|
cy.get('#admin_switch').click()
|
||||||
// cy.get('#users_table > tr').eq(1).contains('admin2')
|
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) => {
|
module.exports = (on, config) => {
|
||||||
// `on` is used to hook into various events Cypress emits
|
// `on` is used to hook into various events Cypress emits
|
||||||
// `config` is the resolved Cypress config
|
// `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",
|
"lint": "vue-cli-service lint",
|
||||||
"backend-test": "newman run -e ../dev/postman_environment.json --delay-request 500 ../dev/postman.json",
|
"backend-test": "newman run -e ../dev/postman_environment.json --delay-request 500 ../dev/postman.json",
|
||||||
"cypress:open": "cypress open",
|
"cypress:open": "cypress open",
|
||||||
"cypress:test": "cypress run --record --key 49d99e5e-04c6-46df-beef-54b68e152a4d",
|
"cypress:test": "cypress run --browser chrome --record false --key $CYPRESS_KEY",
|
||||||
"test": "start-server-and-test start http://0.0.0.0:8888/api cypress:test",
|
"test": "start-server-and-test start http://localhost:8080/api cypress:test",
|
||||||
"start": "statping -port 8888 > /dev/null 2>&1"
|
"start": "statping -port 8080"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free-solid": "^5.1.0-3",
|
"@fortawesome/fontawesome-free-solid": "^5.1.0-3",
|
||||||
|
|
|
@ -1,7 +1,11 @@
|
||||||
|
import Vue from "vue";
|
||||||
import axios from 'axios'
|
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 tokenKey = "statping_user";
|
||||||
|
const errorReporter = "https://bed4d75404924cb3a799e370733a1b64@sentry.statping.com/3"
|
||||||
|
|
||||||
class Api {
|
class Api {
|
||||||
constructor() {
|
constructor() {
|
||||||
|
@ -9,7 +13,11 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
async core() {
|
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) {
|
async core_save(obj) {
|
||||||
|
@ -68,6 +76,10 @@ class Api {
|
||||||
return axios.post('api/reorder/services', data).then(response => (response.data))
|
return axios.post('api/reorder/services', data).then(response => (response.data))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async checkins() {
|
||||||
|
return axios.get('api/checkins').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
async groups() {
|
async groups() {
|
||||||
return axios.get('api/groups').then(response => (response.data))
|
return axios.get('api/groups').then(response => (response.data))
|
||||||
}
|
}
|
||||||
|
@ -114,7 +126,7 @@ class Api {
|
||||||
}
|
}
|
||||||
|
|
||||||
async incident_update_delete(update) {
|
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) {
|
async incidents_service(service) {
|
||||||
|
@ -129,6 +141,14 @@ class Api {
|
||||||
return axios.delete('api/incidents/'+incident.id).then(response => (response.data))
|
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() {
|
async messages() {
|
||||||
return axios.get('api/messages').then(response => (response.data))
|
return axios.get('api/messages').then(response => (response.data))
|
||||||
}
|
}
|
||||||
|
@ -246,6 +266,13 @@ class Api {
|
||||||
await axios.all([all])
|
await axios.all([all])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async sentry_init() {
|
||||||
|
Sentry.init({
|
||||||
|
dsn: errorReporter,
|
||||||
|
integrations: [new Integrations.Vue({Vue, attachProps: true})],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
const api = new Api()
|
const api = new Api()
|
||||||
export default api
|
export default api
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div id="app">
|
<div id="app">
|
||||||
<router-view :app="app" :loaded="loaded"/>
|
<router-view :loaded="loaded"/>
|
||||||
<Footer :logged_in="logged_in" :version="version" v-if="$route.path !== '/setup'"/>
|
<Footer v-if="$route.path !== '/setup'"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -18,35 +18,38 @@
|
||||||
return {
|
return {
|
||||||
loaded: false,
|
loaded: false,
|
||||||
version: "",
|
version: "",
|
||||||
logged_in: false,
|
|
||||||
app: null
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created() {
|
computed: {
|
||||||
this.app = await this.$store.dispatch('loadRequired')
|
core() {
|
||||||
|
return this.$store.getters.core
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async beforeMount() {
|
||||||
|
await this.$store.dispatch('loadCore')
|
||||||
|
|
||||||
this.app = {...this.$store.state}
|
if (!this.core.setup) {
|
||||||
|
|
||||||
if (this.$store.getters.core.logged_in) {
|
|
||||||
await this.$store.dispatch('loadAdmin')
|
|
||||||
}
|
|
||||||
this.loaded = true
|
|
||||||
if (!this.$store.getters.core.setup) {
|
|
||||||
this.$router.push('/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() {
|
async mounted() {
|
||||||
if (this.$route.path !== '/setup') {
|
if (this.$route.path !== '/setup') {
|
||||||
const tk = localStorage.getItem("statping_user")
|
const tk = localStorage.getItem("statping_user")
|
||||||
if (this.$store.getters.core.logged_in) {
|
if (this.core.logged_in) {
|
||||||
this.logged_in = true
|
this.logged_in = true
|
||||||
await this.$store.dispatch('loadAdmin')
|
await this.$store.dispatch('loadAdmin')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -15,13 +15,27 @@ HTML,BODY {
|
||||||
}
|
}
|
||||||
|
|
||||||
.copy-btn {
|
.copy-btn {
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-xs {
|
||||||
|
font-size: 8pt;
|
||||||
|
padding: 2px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.copy-btn BUTTON {
|
||||||
background-color: white;
|
background-color: white;
|
||||||
margin: 6px;
|
margin: 6px;
|
||||||
height: 26px;
|
height: 26px;
|
||||||
font-size: 10pt;
|
font-size: 10pt;
|
||||||
padding: 3px 7px;
|
padding: 3px 7px;
|
||||||
border: 1px solid #a7a7a7;
|
border: 1px solid #a7a7a7;
|
||||||
border-radius: 4px;
|
border-radius: 4px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dim {
|
||||||
|
background-color: #f3f3f3;
|
||||||
}
|
}
|
||||||
|
|
||||||
.slider-info {
|
.slider-info {
|
||||||
|
@ -333,6 +347,7 @@ HTML,BODY {
|
||||||
.card {
|
.card {
|
||||||
background-color: $service-background;
|
background-color: $service-background;
|
||||||
border: $service-border;
|
border: $service-border;
|
||||||
|
//box-shadow: 0px 2px 11px 1px rgba(0, 0, 0, 0.13);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-body {
|
.card-body {
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
</div>
|
</div>
|
||||||
</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 />
|
<ServiceInfo :service=service />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -30,10 +30,21 @@
|
||||||
components: {
|
components: {
|
||||||
ServiceInfo
|
ServiceInfo
|
||||||
},
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
visible: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
services() {
|
||||||
|
return this.$store.getters.services
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
|
||||||
failuresLast24Hours() {
|
failuresLast24Hours() {
|
||||||
let total = 0;
|
let total = 0;
|
||||||
this.$store.getters.services.map((s) => {
|
this.services.map((s) => {
|
||||||
total += s.failures_24_hours
|
total += s.failures_24_hours
|
||||||
})
|
})
|
||||||
return total
|
return total
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
</thead>
|
</thead>
|
||||||
|
|
||||||
<draggable tag="tbody" v-model="groupsList" class="sortable_groups" handle=".drag_icon">
|
<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">
|
<td><span class="drag_icon d-none d-md-inline">
|
||||||
<font-awesome-icon icon="bars" class="mr-3" /></span> {{group.name}}
|
<font-awesome-icon icon="bars" class="mr-3" /></span> {{group.name}}
|
||||||
</td>
|
</td>
|
||||||
|
@ -49,7 +49,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<FormGroup v-if="$store.state.admin" :edit="editChange" :in_group="group"/>
|
<FormGroup v-if="$store.state.admin" :edit="editChange" :in_group="group"/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -92,9 +91,6 @@
|
||||||
this.$store.commit('setGroups', groups)
|
this.$store.commit('setGroups', groups)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
|
||||||
beforeMount() {
|
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
editChange(v) {
|
editChange(v) {
|
||||||
|
@ -105,13 +101,6 @@
|
||||||
this.group = g
|
this.group = g
|
||||||
this.edit = !mode
|
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) {
|
async deleteGroup(g) {
|
||||||
let c = confirm(`Are you sure you want to delete '${g.name}'?`)
|
let c = confirm(`Are you sure you want to delete '${g.name}'?`)
|
||||||
if (c) {
|
if (c) {
|
||||||
|
|
|
@ -14,15 +14,22 @@
|
||||||
</thead>
|
</thead>
|
||||||
<tbody id="users_table">
|
<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>{{user.username}}</td>
|
||||||
<td v-if="user.admin"><span class="badge badge-danger">ADMIN</span></td>
|
<td>
|
||||||
<td v-if="!user.admin"><span class="badge badge-primary">USER</span></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="d-none d-md-table-cell">{{niceDate(user.updated_at)}}</td>
|
||||||
<td class="text-right">
|
<td class="text-right">
|
||||||
<div class="btn-group">
|
<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="editUser(user, edit)" href="#" class="btn btn-outline-secondary edit-user">
|
||||||
<a @click.prevent="deleteUser(user)" v-if="index !== 0" href="" class="btn btn-danger"><font-awesome-icon icon="times" /></a>
|
<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>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
@ -49,6 +56,11 @@
|
||||||
user: {}
|
user: {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
users() {
|
||||||
|
return this.$store.getters.users
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
editChange(v) {
|
editChange(v) {
|
||||||
this.user = {}
|
this.user = {}
|
||||||
|
|
|
@ -64,28 +64,20 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
async updateOrder(value) {
|
async updateOrder(value) {
|
||||||
let data = [];
|
let data = [];
|
||||||
value.forEach((s, k) => {
|
value.forEach((s, k) => {
|
||||||
data.push({ service: s.id, order: k + 1 })
|
data.push({ service: s.id, order: k + 1 })
|
||||||
});
|
});
|
||||||
const reorder = await Api.services_reorder(data)
|
await Api.services_reorder(data)
|
||||||
window.console.log('reorder', reorder)
|
await this.update()
|
||||||
const services = await Api.services()
|
|
||||||
this.$store.commit('setServices', services)
|
|
||||||
},
|
},
|
||||||
async deleteService(s) {
|
async deleteService(s) {
|
||||||
let c = confirm(`Are you sure you want to delete '${s.name}'?`)
|
let c = confirm(`Are you sure you want to delete '${s.name}'?`)
|
||||||
if (c) {
|
if (c) {
|
||||||
await Api.service_delete(s.id)
|
await Api.service_delete(s.id)
|
||||||
const services = await Api.services()
|
await this.update()
|
||||||
this.$store.commit('setServices', services)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
serviceGroup(s) {
|
serviceGroup(s) {
|
||||||
|
@ -95,6 +87,10 @@ export default {
|
||||||
}
|
}
|
||||||
return ""
|
return ""
|
||||||
},
|
},
|
||||||
|
async update() {
|
||||||
|
const services = await Api.services()
|
||||||
|
this.$store.commit('setServices', services)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -59,10 +59,9 @@
|
||||||
components: {
|
components: {
|
||||||
codemirror
|
codemirror
|
||||||
},
|
},
|
||||||
props: {
|
computed: {
|
||||||
core: {
|
core() {
|
||||||
type: Object,
|
return this.$store.getters.core
|
||||||
required: true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
|
@ -86,11 +85,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
|
||||||
codemirror () {
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async mounted () {
|
async mounted () {
|
||||||
await this.fetchTheme()
|
await this.fetchTheme()
|
||||||
this.changeTab('vars')
|
this.changeTab('vars')
|
||||||
|
|
|
@ -26,10 +26,6 @@
|
||||||
<li v-if="$store.state.admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
<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>
|
<router-link to="/dashboard/logs" class="nav-link">Logs</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li @click="navopen = !navopen" class="nav-item navbar-item">
|
|
||||||
<router-link to="/dashboard/help" class="nav-link">Help</router-link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
<span class="navbar-text">
|
<span class="navbar-text">
|
||||||
<a href="#" class="nav-link" v-on:click="logout">Logout</a>
|
<a href="#" class="nav-link" v-on:click="logout">Logout</a>
|
||||||
|
@ -55,7 +51,7 @@
|
||||||
this.$store.commit('setHasAllData', false)
|
this.$store.commit('setHasAllData', false)
|
||||||
this.$store.commit('setToken', null)
|
this.$store.commit('setToken', null)
|
||||||
this.$store.commit('setAdmin', false)
|
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>
|
<template>
|
||||||
<footer>
|
<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">
|
<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> |
|
</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>
|
||||||
<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>
|
</footer>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -18,15 +18,11 @@
|
||||||
components: {
|
components: {
|
||||||
Dashboard
|
Dashboard
|
||||||
},
|
},
|
||||||
props: {
|
computed: {
|
||||||
version: String,
|
core() {
|
||||||
logged_in: Boolean
|
return this.$store.getters.core
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
logged_in() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ export default {
|
||||||
components: {
|
components: {
|
||||||
IncidentsBlock,
|
IncidentsBlock,
|
||||||
GroupServiceFailures
|
GroupServiceFailures
|
||||||
|
|
||||||
},
|
},
|
||||||
props: {
|
props: {
|
||||||
group: Object
|
group: Object
|
||||||
|
|
|
@ -34,7 +34,7 @@ export default {
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async lastDaysFailures() {
|
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")
|
this.failureData = await Api.service_failures_data(this.service.id, this.toUnix(start), this.toUnix(this.now()), "24h")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<h1 id="title" class="col-12 text-center pt-4 mt-4 mb-3 header-title font-6">{{$store.getters.core.name}}</h1>
|
<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">{{$store.getters.core.description}}</h5>
|
<h5 id="description" class="col-12 text-center mb-5 header-desc font-3">{{core.description}}</h5>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export default {
|
export default {
|
||||||
name: 'Header',
|
name: 'Header',
|
||||||
|
computed: {
|
||||||
|
core() {
|
||||||
|
return this.$store.getters.core
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.value = this.func.value;
|
if (this.func) {
|
||||||
this.title = this.func.title;
|
this.value = this.func.value;
|
||||||
this.subtitle = this.func.subtitle;
|
this.title = this.func.title;
|
||||||
this.chart = this.convertToChartData(this.func.chart);
|
this.subtitle = this.func.subtitle;
|
||||||
|
this.chart = this.convertToChartData(this.func.chart);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async latencyYesterday() {
|
async latencyYesterday() {
|
||||||
const todayTime = await Api.service_hits(this.service.id, this.toUnix(this.nowSubtract(86400)), this.toUnix(new Date()), this.group, false)
|
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>
|
<p class="mb-1">{{failure.issue}}</p>
|
||||||
</div>
|
</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">
|
<ul class="pagination justify-content-center">
|
||||||
<li class="page-item" :class="{'disabled': page===1}">
|
<li class="page-item" :class="{'disabled': page===1}">
|
||||||
<a @click.prevent="gotoPage(page-1)" :disabled="page===1" class="page-link" href="#" aria-label="Previous">
|
<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>
|
<span class="sr-only">Previous</span>
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</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>
|
<a @click.prevent="gotoPage(n)" class="page-link" href="#">{{n}}</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="page-item" :class="{'disabled': page===Math.floor(total / limit)}">
|
<li class="page-item" :class="{'disabled': page===Math.floor(total / limit)}">
|
||||||
|
@ -53,6 +53,19 @@ export default {
|
||||||
page: 1
|
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 () {
|
async mounted () {
|
||||||
await this.gotoPage(1)
|
await this.gotoPage(1)
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,23 +1,28 @@
|
||||||
<template v-if="service">
|
<template>
|
||||||
<div class="col-12 card mb-4" style="min-height: 280px;" :class="{'offline-card': !service.online}">
|
<div class="card mb-4" :class="{'offline-card': !service.online}">
|
||||||
<div class="card-body p-3 p-md-1 pt-md-3 pb-md-1">
|
<div class="card-title px-4 pt-3">
|
||||||
<h4 class="card-title mb-4">
|
<h4 v-observe-visibility="setVisible">
|
||||||
<router-link :to="serviceLink(service)">{{service.name}}</router-link>
|
<router-link :to="serviceLink(service)">{{service.name}}</router-link>
|
||||||
<span class="badge float-right" :class="{'badge-success': service.online, 'badge-danger': !service.online}">
|
<span class="badge float-right" :class="{'badge-success': service.online, 'badge-danger': !service.online}">
|
||||||
{{service.online ? "ONLINE" : "OFFLINE"}}
|
{{service.online ? "ONLINE" : "OFFLINE"}}
|
||||||
</span>
|
</span>
|
||||||
</h4>
|
</h4>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="card-body p-3 p-md-1 pt-md-3 pb-md-1">
|
||||||
|
|
||||||
<transition name="fade">
|
<transition name="fade">
|
||||||
<div v-if="loaded && service.online" class="row pb-3">
|
<div v-if="loaded && service.online" class="col-12 pb-2">
|
||||||
<div class="col-md-6 col-sm-12 mt-2 mt-md-0 mb-3">
|
<div class="row">
|
||||||
<ServiceSparkLine :title="set2_name" subtitle="Latency Last 24 Hours" :series="set2"/>
|
<div class="col-md-6 col-sm-12 mt-2 mt-md-0 mb-3">
|
||||||
</div>
|
<ServiceSparkLine :title="set2_name" subtitle="Latency Last 24 Hours" :series="set2"/>
|
||||||
<div class="col-md-6 col-sm-12 mt-4 mt-md-0 mb-3">
|
</div>
|
||||||
<ServiceSparkLine :title="set1_name" subtitle="Latency Last 7 Days" :series="set1"/>
|
<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>
|
||||||
|
|
||||||
<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"
|
<StatsGen :service="service"
|
||||||
title="Since Yesterday"
|
title="Since Yesterday"
|
||||||
|
@ -44,21 +49,35 @@
|
||||||
group="24h" expression="latencyPercent"/>
|
group="24h" expression="latencyPercent"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-4">
|
</div>
|
||||||
<button @click.prevent="Tab('incident')" class="btn btn-block btn-outline-secondary incident" :class="{'text-white btn-secondary': openTab==='incident'}" >Incidents</button>
|
</transition>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-4">
|
|
||||||
|
<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>
|
<button @click.prevent="Tab('checkin')" class="btn btn-block btn-outline-secondary checkin" :class="{'text-white btn-secondary': openTab==='checkin'}" >Checkins</button>
|
||||||
</div>
|
</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'}">
|
<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>
|
Failures <span class="badge badge-danger float-right mt-1">{{service.stats.failures}}</span></button>
|
||||||
</div>
|
</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">
|
<div v-if="openTab === 'incident'" class="col-12 mt-4">
|
||||||
<FormIncident :service="service" />
|
<FormIncident :service="service" />
|
||||||
</div>
|
</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">
|
<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>
|
<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>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</transition>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<span v-for="(failure, index) in failures" v-bind:key="index" class="alert alert-light">
|
<span v-for="(failure, index) in failures" v-bind:key="index" class="alert alert-light">
|
||||||
|
@ -79,6 +96,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Checkin from '../../forms/Checkin';
|
||||||
import FormIncident from '../../forms/Incident';
|
import FormIncident from '../../forms/Incident';
|
||||||
import FormMessage from '../../forms/Message';
|
import FormMessage from '../../forms/Message';
|
||||||
import ServiceFailures from './ServiceFailures';
|
import ServiceFailures from './ServiceFailures';
|
||||||
|
@ -89,6 +107,7 @@
|
||||||
export default {
|
export default {
|
||||||
name: 'ServiceInfo',
|
name: 'ServiceInfo',
|
||||||
components: {
|
components: {
|
||||||
|
Checkin,
|
||||||
ServiceFailures,
|
ServiceFailures,
|
||||||
FormIncident,
|
FormIncident,
|
||||||
FormMessage,
|
FormMessage,
|
||||||
|
@ -109,17 +128,27 @@
|
||||||
loaded: false,
|
loaded: false,
|
||||||
set1_name: "",
|
set1_name: "",
|
||||||
set2_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 = await this.getHits(24 * 7, "6h")
|
||||||
this.set1_name = this.calc(this.set1)
|
this.set1_name = this.calc(this.set1)
|
||||||
this.set2 = await this.getHits(24, "1h")
|
this.set2 = await this.getHits(24, "1h")
|
||||||
this.set2_name = this.calc(this.set2)
|
this.set2_name = this.calc(this.set2)
|
||||||
this.loaded = true
|
this.loaded = true
|
||||||
},
|
},
|
||||||
methods: {
|
|
||||||
async deleteFailures() {
|
async deleteFailures() {
|
||||||
const c = confirm('Are you sure you want to delete all failures?')
|
const c = confirm('Are you sure you want to delete all failures?')
|
||||||
if (c) {
|
if (c) {
|
||||||
|
|
|
@ -1,23 +1,41 @@
|
||||||
<template>
|
<template>
|
||||||
<form @submit.prevent="saveCheckin">
|
<div>
|
||||||
<div class="form-group row">
|
<div v-for="(checkin, i) in checkins" class="col-12 alert alert-light" role="alert">
|
||||||
<div class="col-md-3">
|
<span class="badge badge-pill badge-info text-uppercase">{{checkin.name}}</span>
|
||||||
<label for="checkin_interval" class="col-form-label">Checkin Name</label>
|
<span class="float-right font-2">Last checkin {{ago(checkin.last_hit)}}</span>
|
||||||
<input v-model="checkin.name" type="text" name="name" class="form-control" id="checkin_name" placeholder="New Checkin">
|
<span class="float-right font-2 mr-3">Check Every {{checkin.interval}} seconds</span>
|
||||||
</div>
|
<span class="float-right font-2 mr-3">Grace Period {{checkin.grace}} seconds</span>
|
||||||
<div class="col-3">
|
<span class="d-block mt-2">
|
||||||
<label for="checkin_interval" class="col-form-label">Interval (seconds)</label>
|
<input type="text" class="form-control" :value="`${core.domain}/checkin/${checkin.api_key}`" readonly>
|
||||||
<input v-model="checkin.interval" type="number" name="interval" class="form-control" id="checkin_interval" placeholder="60">
|
<span class="small">Send a GET request to this URL every {{checkin.interval}} seconds
|
||||||
</div>
|
<button @click="deleteCheckin(checkin)" type="button" class="btn btn-danger btn-xs float-right mt-1">Delete</button>
|
||||||
<div class="col-3">
|
</span>
|
||||||
<label for="grace_period" class="col-form-label">Grace Period</label>
|
</span>
|
||||||
<input v-model="checkin.grace" type="number" name="grace" class="form-control" id="grace_period" placeholder="10">
|
</div>
|
||||||
</div>
|
|
||||||
<div class="col-3">
|
<div class="col-12 alert alert-light">
|
||||||
<button @click.prevent="saveCheckin" type="submit" id="submit" class="btn btn-success d-block" style="margin-top: 14px;">Save Checkin</button>
|
<form @submit.prevent="saveCheckin">
|
||||||
</div>
|
<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>
|
</div>
|
||||||
</form>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
@ -37,20 +55,41 @@
|
||||||
name: "",
|
name: "",
|
||||||
interval: 60,
|
interval: 60,
|
||||||
grace: 60,
|
grace: 60,
|
||||||
service: this.service.id
|
service_id: this.service.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
computed: {
|
||||||
async saveCheckin() {
|
checkins() {
|
||||||
const data = {name: this.group.name, public: this.group.public}
|
return this.$store.getters.serviceCheckins(this.service.id)
|
||||||
await Api.group_create(data)
|
|
||||||
const groups = await Api.groups()
|
|
||||||
this.$store.commit('setGroups', groups)
|
|
||||||
},
|
},
|
||||||
|
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>
|
</script>
|
||||||
|
|
|
@ -31,7 +31,20 @@
|
||||||
<small class="form-text text-muted">HTML is allowed inside the footer</small>
|
<small class="form-text text-muted">HTML is allowed inside the footer</small>
|
||||||
</div>
|
</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>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
@ -41,24 +54,17 @@
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CoreSettings',
|
name: 'CoreSettings',
|
||||||
props: {
|
computed: {
|
||||||
in_core: {
|
core() {
|
||||||
type: Object,
|
return this.$store.getters.core
|
||||||
required: true,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
core: this.in_core
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
async saveSettings() {
|
async saveSettings() {
|
||||||
const c = this.core
|
const c = this.core
|
||||||
await Api.core_save(c)
|
await Api.core_save(c)
|
||||||
const core = await Api.core()
|
const core = await Api.core()
|
||||||
this.$store.commit('setCore', core)
|
this.$store.commit('setCore', core)
|
||||||
this.core = core
|
|
||||||
},
|
},
|
||||||
selectAll() {
|
selectAll() {
|
||||||
this.$refs.input.select();
|
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 g = this.group
|
||||||
const data = {name: g.name, public: g.public}
|
const data = {name: g.name, public: g.public}
|
||||||
await Api.group_create(data)
|
await Api.group_create(data)
|
||||||
const groups = await Api.groups()
|
await this.update()
|
||||||
this.$store.commit('setGroups', groups)
|
|
||||||
this.group = {}
|
this.group = {}
|
||||||
},
|
},
|
||||||
async updateGroup() {
|
async updateGroup() {
|
||||||
const g = this.group
|
const g = this.group
|
||||||
const data = {id: g.id, name: g.name, public: g.public}
|
const data = {id: g.id, name: g.name, public: g.public}
|
||||||
await Api.group_update(data)
|
await Api.group_update(data)
|
||||||
|
await this.update()
|
||||||
|
this.edit(false)
|
||||||
|
},
|
||||||
|
async update() {
|
||||||
const groups = await Api.groups()
|
const groups = await Api.groups()
|
||||||
this.$store.commit('setGroups', groups)
|
this.$store.commit('setGroups', groups)
|
||||||
this.edit(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,11 +8,9 @@
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-body bg-light pt-3">
|
<div class="card-body bg-light pt-3">
|
||||||
|
|
||||||
<div v-for="(update, i) in incident.updates" class="alert alert-light" role="alert">
|
<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="badge badge-pill badge-info text-uppercase">{{update.type}}</span>
|
||||||
<span class="float-right font-2">{{ago(update.created_at)}} ago</span>
|
<span class="float-right font-2">{{ago(update.created_at)}} ago</span>
|
||||||
|
|
||||||
<span class="d-block mt-2">{{update.message}}
|
<span class="d-block mt-2">{{update.message}}
|
||||||
<button @click="delete_update(update)" type="button" class="close" data-dismiss="alert" aria-label="Close">
|
<button @click="delete_update(update)" type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||||
<span aria-hidden="true">×</span>
|
<span aria-hidden="true">×</span>
|
||||||
|
@ -95,9 +93,9 @@
|
||||||
await Api.incident_update_delete(update)
|
await Api.incident_update_delete(update)
|
||||||
this.incidents = await Api.incidents_service(this.service)
|
this.incidents = await Api.incidents_service(this.service)
|
||||||
},
|
},
|
||||||
async loadIncidents() {
|
async loadIncidents() {
|
||||||
this.incidents = await Api.incidents_service(this.service)
|
this.incidents = await Api.incidents_service(this.service)
|
||||||
},
|
},
|
||||||
async createIncident() {
|
async createIncident() {
|
||||||
await Api.incident_create(this.service, this.incident)
|
await Api.incident_create(this.service, this.incident)
|
||||||
await this.loadIncidents()
|
await this.loadIncidents()
|
||||||
|
@ -107,13 +105,13 @@
|
||||||
service: this.service.id,
|
service: this.service.id,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async deleteIncident(incident) {
|
async deleteIncident(incident) {
|
||||||
let c = confirm(`Are you sure you want to delete '${incident.title}'?`)
|
let c = confirm(`Are you sure you want to delete '${incident.title}'?`)
|
||||||
if (c) {
|
if (c) {
|
||||||
await Api.incident_delete(incident)
|
await Api.incident_delete(incident)
|
||||||
await this.loadIncidents()
|
await this.loadIncidents()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -23,10 +23,6 @@
|
||||||
</div>
|
</div>
|
||||||
</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>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -35,11 +31,14 @@
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FormLogin',
|
name: 'FormLogin',
|
||||||
props: {
|
computed: {
|
||||||
oauth: {
|
core() {
|
||||||
type: Object
|
return this.$store.getters.core
|
||||||
}
|
},
|
||||||
},
|
oauth() {
|
||||||
|
return this.$store.getters.core.oauth
|
||||||
|
}
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
username: "",
|
username: "",
|
||||||
|
|
|
@ -35,20 +35,25 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="error" class="alert alert-danger col-12" role="alert">{{error}}</div>
|
<div v-if="error && !success" class="alert alert-danger col-12" role="alert">
|
||||||
<div v-if="success" class="alert alert-success col-12" role="alert">{{notifier.title}} appears to be working!</div>
|
{{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 text-black-50 bg-white mb-3">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-6 col-sm-6 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
<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"}}
|
<i class="fa fa-check-circle"></i> {{loading ? "Loading..." : saved ? "Saved" : "Save Settings"}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-6 col-sm-6 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
<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>
|
{{loadingTest ? "Loading..." : "Test Notifier"}}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -79,6 +84,7 @@ export default {
|
||||||
loading: false,
|
loading: false,
|
||||||
loadingTest: false,
|
loadingTest: false,
|
||||||
error: null,
|
error: null,
|
||||||
|
response: null,
|
||||||
success: false,
|
success: false,
|
||||||
saved: false,
|
saved: false,
|
||||||
form: {},
|
form: {},
|
||||||
|
@ -130,6 +136,7 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.error = tested.error
|
this.error = tested.error
|
||||||
}
|
}
|
||||||
|
this.response = tested.response
|
||||||
this.loadingTest = false
|
this.loadingTest = false
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="switch-gh-oauth" class="col-sm-4 col-form-label">OAuth Login Settings</label>
|
<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">
|
<div class="col-md-8 col-xs-12 mt-1">
|
||||||
<span @click="internal_enabled = !!internal_enabled" class="switch float-left">
|
<span @click="oauth.internal_enabled = !!core.oauth.internal_enabled" class="switch float-left">
|
||||||
<input v-model="internal_enabled" type="checkbox" class="switch" id="switch-local-oauth" :checked="internal_enabled">
|
<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>
|
<label for="switch-local-oauth">Use email/password Authentication</label>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -15,7 +15,7 @@
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="whitelist_domains" class="col-sm-4 col-form-label">Whitelist Domains</label>
|
<label for="whitelist_domains" class="col-sm-4 col-form-label">Whitelist Domains</label>
|
||||||
<div class="col-sm-8">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -28,20 +28,20 @@
|
||||||
<div class="form-group row mt-3">
|
<div class="form-group row mt-3">
|
||||||
<label for="github_client" class="col-sm-4 col-form-label">Github Client ID</label>
|
<label for="github_client" class="col-sm-4 col-form-label">Github Client ID</label>
|
||||||
<div class="col-sm-8">
|
<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>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="github_secret" class="col-sm-4 col-form-label">Github Client Secret</label>
|
<label for="github_secret" class="col-sm-4 col-form-label">Github Client Secret</label>
|
||||||
<div class="col-sm-8">
|
<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>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="switch-gh-oauth" class="col-sm-4 col-form-label">Enable Github Login</label>
|
<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">
|
<div class="col-md-8 col-xs-12 mt-1">
|
||||||
<span @click="github_enabled = !!github_enabled" class="switch float-left">
|
<span @click="oauth.github_enabled = !!oauth.github_enabled" class="switch float-left">
|
||||||
<input v-model="github_enabled" type="checkbox" class="switch" id="switch-gh-oauth" :checked="github_enabled">
|
<input v-model="oauth.github_enabled" type="checkbox" class="switch" id="switch-gh-oauth" :checked="oauth.github_enabled">
|
||||||
<label for="switch-gh-oauth"> </label>
|
<label for="switch-gh-oauth"> </label>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -49,7 +49,12 @@
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="gh_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
<label for="gh_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||||
<div class="col-sm-8">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -81,9 +86,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<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">
|
<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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -124,14 +134,17 @@
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="slack_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
<label for="slack_callback" class="col-sm-4 col-form-label">Callback URL</label>
|
||||||
<div class="col-sm-8">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{providers()}}
|
|
||||||
|
|
||||||
<button class="btn btn-primary btn-block" @click.prevent="saveOAuth" type="submit">
|
<button class="btn btn-primary btn-block" @click.prevent="saveOAuth" type="submit">
|
||||||
Save OAuth Settings
|
Save OAuth Settings
|
||||||
</button>
|
</button>
|
||||||
|
@ -144,25 +157,34 @@
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'OAuth',
|
name: 'OAuth',
|
||||||
props: {
|
computed: {
|
||||||
oauth: {
|
oauth() {
|
||||||
type: Object
|
return this.$store.getters.core.oauth
|
||||||
}
|
|
||||||
},
|
|
||||||
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')
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
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() {
|
beforeCreate() {
|
||||||
// this.github_enabled = this.$store.getters.core.oauth.oauth_providers.split(",").includes('github')
|
// this.github_enabled = this.$store.getters.core.oauth.oauth_providers.split(",").includes('github')
|
||||||
// const c = await Api.core()
|
// const c = await Api.core()
|
||||||
// this.auth = c.auth
|
// this.auth = c.auth
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
has(val) {
|
||||||
|
if (!this.core.oauth.oauth_providers) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return this.core.oauth.oauth_providers.split(",").includes(val)
|
||||||
|
},
|
||||||
providers() {
|
providers() {
|
||||||
let providers = [];
|
let providers = [];
|
||||||
if (this.github_enabled) {
|
if (this.github_enabled) {
|
||||||
|
@ -186,7 +208,6 @@
|
||||||
await Api.core_save(c)
|
await Api.core_save(c)
|
||||||
const core = await Api.core()
|
const core = await Api.core()
|
||||||
this.$store.commit('setCore', core)
|
this.$store.commit('setCore', core)
|
||||||
this.core = core
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,84 +5,83 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12">
|
<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>
|
||||||
<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 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">
|
<div class="form-group">
|
||||||
<label>Project Name</label>
|
<label>Project Description</label>
|
||||||
<input @keyup="canSubmit" v-model="setup.project" id="project" type="text" class="form-control" placeholder="Great Uptime" required>
|
<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>
|
||||||
|
</form>
|
||||||
<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>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -163,6 +162,7 @@
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.$store.dispatch('loadCore')
|
||||||
await this.$store.dispatch('loadRequired')
|
await this.$store.dispatch('loadRequired')
|
||||||
|
|
||||||
this.loading = false
|
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">
|
<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>
|
||||||
<div class="col-6 col-md-4">
|
<div class="col-6 col-md-4">
|
||||||
<span @click="user.admin = !!user.admin" class="switch">
|
<span id="admin_switch" @click="user.admin = !!user.admin" class="switch">
|
||||||
<input v-model="user.admin" type="checkbox" class="switch" id="switch-normal" v-bind:checked="user.admin">
|
<input v-model="user.admin" type="checkbox" class="switch" id="user_admin_switch" v-bind:checked="user.admin">
|
||||||
<label for="switch-normal">Administrator</label>
|
<label for="user_admin_switch">Administrator</label>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,11 +39,12 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-sm-12">
|
<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)"
|
: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}">
|
:action="saveUser"
|
||||||
{{loading ? "Loading..." : user.id ? "Update User" : "Create User"}}
|
:label="user.id ? 'Update User' : 'Create User'"
|
||||||
</button>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="alert alert-danger d-none" id="alerter" role="alert"></div>
|
<div class="alert alert-danger d-none" id="alerter" role="alert"></div>
|
||||||
|
@ -54,10 +55,12 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../API";
|
import Api from "../API";
|
||||||
|
import LoadButton from "@/components/Elements/LoadButton";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FormUser',
|
name: 'FormUser',
|
||||||
props: {
|
components: {LoadButton},
|
||||||
|
props: {
|
||||||
in_user: {
|
in_user: {
|
||||||
type: Object
|
type: Object
|
||||||
},
|
},
|
||||||
|
@ -81,6 +84,7 @@
|
||||||
in_user() {
|
in_user() {
|
||||||
let u = this.in_user
|
let u = this.in_user
|
||||||
u.password = null
|
u.password = null
|
||||||
|
u.password_confirm = null
|
||||||
this.user = u
|
this.user = u
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -89,8 +93,7 @@
|
||||||
this.user = {}
|
this.user = {}
|
||||||
this.edit(false)
|
this.edit(false)
|
||||||
},
|
},
|
||||||
async saveUser(e) {
|
async saveUser() {
|
||||||
e.preventDefault();
|
|
||||||
this.loading = true
|
this.loading = true
|
||||||
if (this.user.id) {
|
if (this.user.id) {
|
||||||
await this.updateUser()
|
await this.updateUser()
|
||||||
|
@ -103,8 +106,7 @@
|
||||||
let user = this.user
|
let user = this.user
|
||||||
delete user.confirm_password
|
delete user.confirm_password
|
||||||
await Api.user_create(user)
|
await Api.user_create(user)
|
||||||
const users = await Api.users()
|
await this.update()
|
||||||
this.$store.commit('setUsers', users)
|
|
||||||
this.user = {}
|
this.user = {}
|
||||||
},
|
},
|
||||||
async updateUser() {
|
async updateUser() {
|
||||||
|
@ -114,9 +116,12 @@
|
||||||
}
|
}
|
||||||
delete user.confirm_password
|
delete user.confirm_password
|
||||||
await Api.user_update(user)
|
await Api.user_update(user)
|
||||||
|
await this.update()
|
||||||
|
this.edit(false)
|
||||||
|
},
|
||||||
|
async update() {
|
||||||
const users = await Api.users()
|
const users = await Api.users()
|
||||||
this.$store.commit('setUsers', users)
|
this.$store.commit('setUsers', users)
|
||||||
this.edit(false)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import {library} from '@fortawesome/fontawesome-svg-core'
|
import {library} from '@fortawesome/fontawesome-svg-core'
|
||||||
import {fas} from '@fortawesome/fontawesome-free-solid';
|
import {fas} from '@fortawesome/fontawesome-free-solid';
|
||||||
import {fab} from '@fortawesome/free-brands-svg-icons';
|
import {fab} from '@fortawesome/free-brands-svg-icons';
|
||||||
|
import {far} from '@fortawesome/fontawesome-svg-core';
|
||||||
import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome'
|
import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome'
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
|
|
||||||
library.add(fas, fab)
|
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 App from '@/App.vue'
|
||||||
import store from './store'
|
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 router from './routes'
|
||||||
import "./mixin"
|
import "./mixin"
|
||||||
import "./icons"
|
import "./icons"
|
||||||
|
@ -19,12 +17,6 @@ Vue.use(VueClipboard);
|
||||||
Vue.use(VueRouter);
|
Vue.use(VueRouter);
|
||||||
Vue.use(VueObserveVisibility);
|
Vue.use(VueObserveVisibility);
|
||||||
|
|
||||||
Sentry.init({
|
|
||||||
dsn: errorReporter,
|
|
||||||
integrations: [new Integrations.Vue({Vue, attachProps: true})],
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
new Vue({
|
new Vue({
|
||||||
router,
|
router,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
<div class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
||||||
<TopNav :admin="$store.state.admin"/>
|
<TopNav :admin="admin"/>
|
||||||
<router-view :admin="$store.state.admin"/>
|
<router-view :admin="admin"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -17,18 +17,23 @@
|
||||||
data () {
|
data () {
|
||||||
return {
|
return {
|
||||||
authenticated: false,
|
authenticated: false,
|
||||||
|
loaded: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
computed: {
|
||||||
const core = await Api.core()
|
admin() {
|
||||||
this.$store.commit('setAdmin', core.admin)
|
return this.$store.getters.admin
|
||||||
this.$store.commit('setCore', core)
|
},
|
||||||
},
|
user() {
|
||||||
async created() {
|
return this.$store.getters.user
|
||||||
const core = await Api.core()
|
}
|
||||||
this.$store.commit('setCore', core)
|
},
|
||||||
|
mounted() {
|
||||||
|
// if (!this.user || !this.admin) {
|
||||||
|
// this.$router.push('/login')
|
||||||
|
// }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
<Header/>
|
<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">
|
<div class="list-group online_list mb-4">
|
||||||
<a class="service_li list-group-item list-group-item-action">
|
<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>
|
<router-link class="no-decoration font-3" :to="serviceLink(service)">{{service.name}}</router-link>
|
||||||
|
@ -16,16 +16,16 @@
|
||||||
</div>
|
</div>
|
||||||
</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 />
|
<Group :group=group />
|
||||||
</div>
|
</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"/>
|
<MessageBlock :message="message"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 full-col-12">
|
<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 />
|
<ServiceBlock :service=service />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -58,6 +58,20 @@ export default {
|
||||||
logged_in: false
|
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() {
|
async created() {
|
||||||
this.logged_in = this.loggedIn()
|
this.logged_in = this.loggedIn()
|
||||||
},
|
},
|
||||||
|
@ -69,11 +83,6 @@ export default {
|
||||||
const start = this.isBetween(new Date(), message.start_on)
|
const start = this.isBetween(new Date(), message.start_on)
|
||||||
const end = this.isBetween(message.end_on, new Date())
|
const end = this.isBetween(message.end_on, new Date())
|
||||||
return start && end
|
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="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-10 offset-1 col-md-8 offset-md-2 mt-md-2">
|
||||||
<div class="col-12 col-md-8 offset-md-2 mb-4">
|
<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>
|
</div>
|
||||||
|
<FormLogin/>
|
||||||
<FormLogin :oauth="$store.getters.core.oauth"/>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -12,7 +12,6 @@ import Api from "../API";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Logs',
|
name: 'Logs',
|
||||||
components: {},
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
logs: [],
|
logs: [],
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
<h6 class="mt-4 text-muted">Notifiers</h6>
|
<h6 class="mt-4 text-muted">Notifiers</h6>
|
||||||
|
|
||||||
<div id="notifiers_tabs">
|
<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">
|
<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.method}}
|
<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>
|
<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>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -60,11 +60,10 @@
|
||||||
<div class="card text-black-50 bg-white">
|
<div class="card text-black-50 bg-white">
|
||||||
<div class="card-header">Statping Settings</div>
|
<div class="card-header">Statping Settings</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<CoreSettings :in_core="core"/>
|
<CoreSettings/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="card text-black-50 bg-white mt-3">
|
<div class="card text-black-50 bg-white mt-3">
|
||||||
<div class="card-header">API Settings</div>
|
<div class="card-header">API Settings</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
@ -73,7 +72,7 @@
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<input v-model="core.api_key" type="text" class="form-control" id="api_key" readonly>
|
<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>
|
<button @click.prevent="copy(core.api_key)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -86,12 +85,12 @@
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<div class="input-group">
|
<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>
|
<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>
|
<button @click="copy(core.api_secret)" class="btn btn-outline-secondary" type="button">Copy</button>
|
||||||
</div>
|
</div>
|
||||||
</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">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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -112,7 +111,7 @@
|
||||||
<div class="card text-black-50 bg-white mb-5">
|
<div class="card text-black-50 bg-white mb-5">
|
||||||
<div class="card-header">Theme Editor</div>
|
<div class="card-header">Theme Editor</div>
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<ThemeEditor :core="core"/>
|
<ThemeEditor/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -127,10 +126,10 @@
|
||||||
</div>
|
</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">
|
<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>
|
||||||
|
|
||||||
<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"/>
|
<Notifier :notifier="notifier"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -167,20 +166,24 @@
|
||||||
tab: "v-pills-home-tab",
|
tab: "v-pills-home-tab",
|
||||||
qrcode: "",
|
qrcode: "",
|
||||||
qrurl: "",
|
qrurl: "",
|
||||||
core: this.$store.getters.core
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
core() {
|
||||||
|
return this.$store.getters.core
|
||||||
|
},
|
||||||
|
notifiers() {
|
||||||
|
return this.$store.getters.notifiers
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
this.cache = await Api.cache()
|
this.cache = await Api.cache()
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
const c = this.$store.state.core
|
const c = this.core
|
||||||
this.qrurl = `statping://setup?domain=${c.domain}&api=${c.api_secret}`
|
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)
|
this.qrcode = "https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=" + encodeURI(this.qrurl)
|
||||||
},
|
},
|
||||||
async beforeMount() {
|
|
||||||
this.core = await Api.core()
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
changeTab(e) {
|
changeTab(e) {
|
||||||
this.tab = e.target.id
|
this.tab = e.target.id
|
||||||
|
|
|
@ -26,7 +26,9 @@ export default new Vuex.Store({
|
||||||
messages: [],
|
messages: [],
|
||||||
users: [],
|
users: [],
|
||||||
notifiers: [],
|
notifiers: [],
|
||||||
admin: false
|
checkins: [],
|
||||||
|
admin: false,
|
||||||
|
user: false
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
hasAllData: state => state.hasAllData,
|
hasAllData: state => state.hasAllData,
|
||||||
|
@ -39,8 +41,10 @@ export default new Vuex.Store({
|
||||||
incidents: state => state.incidents,
|
incidents: state => state.incidents,
|
||||||
users: state => state.users,
|
users: state => state.users,
|
||||||
notifiers: state => state.notifiers,
|
notifiers: state => state.notifiers,
|
||||||
|
checkins: state => state.checkins,
|
||||||
|
|
||||||
isAdmin: state => state.admin,
|
isAdmin: state => state.admin,
|
||||||
|
isUser: state => state.user,
|
||||||
|
|
||||||
servicesInOrder: state => state.services.sort((a, b) => a.order_id - b.order_id),
|
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),
|
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),
|
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),
|
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) => {
|
serviceByAll: (state) => (element) => {
|
||||||
if (element % 1 === 0) {
|
if (element % 1 === 0) {
|
||||||
return state.services.find(s => s.id == element)
|
return state.services.find(s => s.id == element)
|
||||||
|
@ -91,6 +98,7 @@ export default new Vuex.Store({
|
||||||
state.hasPublicData = bool
|
state.hasPublicData = bool
|
||||||
},
|
},
|
||||||
setCore (state, core) {
|
setCore (state, core) {
|
||||||
|
window.console.log('GETTING CORE')
|
||||||
state.core = core
|
state.core = core
|
||||||
},
|
},
|
||||||
setToken (state, token) {
|
setToken (state, token) {
|
||||||
|
@ -99,6 +107,9 @@ export default new Vuex.Store({
|
||||||
setServices (state, services) {
|
setServices (state, services) {
|
||||||
state.services = services
|
state.services = services
|
||||||
},
|
},
|
||||||
|
setCheckins (state, checkins) {
|
||||||
|
state.checkins = checkins
|
||||||
|
},
|
||||||
setGroups (state, groups) {
|
setGroups (state, groups) {
|
||||||
state.groups = groups
|
state.groups = groups
|
||||||
},
|
},
|
||||||
|
@ -114,15 +125,23 @@ export default new Vuex.Store({
|
||||||
setAdmin (state, admin) {
|
setAdmin (state, admin) {
|
||||||
state.admin = admin
|
state.admin = admin
|
||||||
},
|
},
|
||||||
|
setUser (state, user) {
|
||||||
|
state.user = user
|
||||||
|
},
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async getAllServices(context) {
|
async getAllServices(context) {
|
||||||
const services = await Api.services()
|
const services = await Api.services()
|
||||||
context.commit("setServices", 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) {
|
async loadRequired(context) {
|
||||||
const core = await Api.core()
|
|
||||||
context.commit("setCore", core);
|
|
||||||
const groups = await Api.groups()
|
const groups = await Api.groups()
|
||||||
context.commit("setGroups", groups);
|
context.commit("setGroups", groups);
|
||||||
const services = await Api.services()
|
const services = await Api.services()
|
||||||
|
@ -130,23 +149,15 @@ export default new Vuex.Store({
|
||||||
const messages = await Api.messages()
|
const messages = await Api.messages()
|
||||||
context.commit("setMessages", messages)
|
context.commit("setMessages", messages)
|
||||||
context.commit("setHasPublicData", true)
|
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')
|
window.console.log('finished loading required data')
|
||||||
},
|
},
|
||||||
async loadAdmin(context) {
|
async loadAdmin(context) {
|
||||||
const core = await Api.core()
|
|
||||||
context.commit("setCore", core);
|
|
||||||
const groups = await Api.groups()
|
const groups = await Api.groups()
|
||||||
context.commit("setGroups", groups);
|
context.commit("setGroups", groups);
|
||||||
const services = await Api.services()
|
const services = await Api.services()
|
||||||
context.commit("setServices", services);
|
context.commit("setServices", services);
|
||||||
|
const checkins = await Api.checkins()
|
||||||
|
context.commit("setCheckins", checkins);
|
||||||
const messages = await Api.messages()
|
const messages = await Api.messages()
|
||||||
context.commit("setMessages", messages)
|
context.commit("setMessages", messages)
|
||||||
context.commit("setHasPublicData", true)
|
context.commit("setHasPublicData", true)
|
||||||
|
|
|
@ -81,6 +81,8 @@ func apiCoreHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
app.OAuth = c.OAuth
|
app.OAuth = c.OAuth
|
||||||
app.UseCdn = null.NewNullBool(c.UseCdn.Bool)
|
app.UseCdn = null.NewNullBool(c.UseCdn.Bool)
|
||||||
|
app.AllowReports = null.NewNullBool(c.AllowReports.Bool)
|
||||||
|
utils.SentryInit(nil, app.AllowReports.Bool)
|
||||||
err = app.Update()
|
err = app.Update()
|
||||||
returnJson(core.App, w, r)
|
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
|
type HttpFuncTest func(*testing.T) error
|
||||||
|
|
||||||
// HTTPTest contains all the parameters for a HTTP Unit Test
|
// 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
|
//Get a cached content by key
|
||||||
func (s Storage) Get(key string) []byte {
|
func (s Storage) Get(key string) []byte {
|
||||||
|
s.mu.Lock()
|
||||||
|
defer s.mu.Unlock()
|
||||||
item := s.items[key]
|
item := s.items[key]
|
||||||
if item.Expired() {
|
if item.Expired() {
|
||||||
CacheStorage.Delete(key)
|
CacheStorage.Delete(key)
|
||||||
|
|
|
@ -40,8 +40,7 @@ func checkinCreateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
checkin.ServiceId = service.Id
|
checkin.ServiceId = service.Id
|
||||||
err = checkin.Create()
|
if err := checkin.Create(); err != nil {
|
||||||
if err != nil {
|
|
||||||
sendErrorJson(err, w, r)
|
sendErrorJson(err, w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -52,7 +51,7 @@ func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
checkin, err := checkins.FindByAPI(vars["api"])
|
checkin, err := checkins.FindByAPI(vars["api"])
|
||||||
if err != nil {
|
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
|
return
|
||||||
}
|
}
|
||||||
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
|
ip, _, _ := net.SplitHostPort(r.RemoteAddr)
|
||||||
|
@ -60,15 +59,17 @@ func checkinHitHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
hit := &checkins.CheckinHit{
|
hit := &checkins.CheckinHit{
|
||||||
Checkin: checkin.Id,
|
Checkin: checkin.Id,
|
||||||
From: ip,
|
From: ip,
|
||||||
CreatedAt: utils.Now().UTC(),
|
CreatedAt: utils.Now(),
|
||||||
}
|
}
|
||||||
|
log.Infof("Checking %s was requested", checkin.Name)
|
||||||
|
|
||||||
err = hit.Create()
|
err = hit.Create()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sendErrorJson(fmt.Errorf("checkin %v was not found", vars["api"]), w, r)
|
sendErrorJson(fmt.Errorf("checkin %v was not found", vars["api"]), w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
checkin.Failing = false
|
checkin.Failing = false
|
||||||
checkin.LastHitTime = utils.Now().UTC()
|
checkin.LastHitTime = utils.Now()
|
||||||
sendJsonAction(hit.Id, "update", w, r)
|
sendJsonAction(hit.Id, "update", w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ func TestApiCheckinRoutes(t *testing.T) {
|
||||||
SecureRoute: true,
|
SecureRoute: true,
|
||||||
}, {
|
}, {
|
||||||
Name: "Statping Create Checkin",
|
Name: "Statping Create Checkin",
|
||||||
URL: "/api/checkin",
|
URL: "/api/checkins",
|
||||||
Method: "POST",
|
Method: "POST",
|
||||||
Body: `{
|
Body: `{
|
||||||
"service_id": 2,
|
"service_id": 2,
|
||||||
|
|
|
@ -52,7 +52,7 @@ func TestGroupAPIRoutes(t *testing.T) {
|
||||||
URL: "/api/groups",
|
URL: "/api/groups",
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
ResponseLen: 2,
|
ResponseLen: 3,
|
||||||
BeforeTest: UnsetTestENV,
|
BeforeTest: UnsetTestENV,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,11 +8,6 @@ import (
|
||||||
"net/http"
|
"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) {
|
func apiServiceIncidentsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
incids := incidents.FindByService(utils.ToInt(vars["id"]))
|
incids := incidents.FindByService(utils.ToInt(vars["id"]))
|
||||||
|
|
|
@ -72,16 +72,18 @@ func testNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
notif := services.ReturnNotifier(notifer.Method)
|
notif := services.ReturnNotifier(notifer.Method)
|
||||||
err = notif.OnTest()
|
out, err := notif.OnTest()
|
||||||
|
|
||||||
resp := ¬ifierTestResp{
|
resp := ¬ifierTestResp{
|
||||||
Success: err == nil,
|
Success: err == nil,
|
||||||
Error: err,
|
Response: out,
|
||||||
|
Error: err,
|
||||||
}
|
}
|
||||||
returnJson(resp, w, r)
|
returnJson(resp, w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
type notifierTestResp struct {
|
type notifierTestResp struct {
|
||||||
Success bool `json:"success"`
|
Success bool `json:"success"`
|
||||||
Error error `json:"error,omitempty"`
|
Response string `json:"response,omitempty"`
|
||||||
|
Error error `json:"error,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -146,9 +146,9 @@ func Router() *mux.Router {
|
||||||
|
|
||||||
// API CHECKIN Routes
|
// API CHECKIN Routes
|
||||||
api.Handle("/api/checkins", authenticated(apiAllCheckinsHandler, false)).Methods("GET")
|
api.Handle("/api/checkins", authenticated(apiAllCheckinsHandler, false)).Methods("GET")
|
||||||
api.Handle("/api/checkin/{api}", authenticated(apiCheckinHandler, false)).Methods("GET")
|
api.Handle("/api/checkins", authenticated(checkinCreateHandler, false)).Methods("POST")
|
||||||
api.Handle("/api/checkin", authenticated(checkinCreateHandler, false)).Methods("POST")
|
api.Handle("/api/checkins/{api}", authenticated(apiCheckinHandler, false)).Methods("GET")
|
||||||
api.Handle("/api/checkin/{api}", authenticated(checkinDeleteHandler, false)).Methods("DELETE")
|
api.Handle("/api/checkins/{api}", authenticated(checkinDeleteHandler, false)).Methods("DELETE")
|
||||||
r.Handle("/checkin/{api}", http.HandlerFunc(checkinHitHandler))
|
r.Handle("/checkin/{api}", http.HandlerFunc(checkinHitHandler))
|
||||||
|
|
||||||
// Static Files Routes
|
// Static Files Routes
|
||||||
|
|
|
@ -23,11 +23,11 @@ func TestApiServiceRoutes(t *testing.T) {
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
ExpectedContains: []string{`"name":"Google"`},
|
ExpectedContains: []string{`"name":"Google"`},
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
ResponseLen: 5,
|
ResponseLen: 6,
|
||||||
BeforeTest: SetTestENV,
|
BeforeTest: SetTestENV,
|
||||||
FuncTest: func(t *testing.T) error {
|
FuncTest: func(t *testing.T) error {
|
||||||
count := len(services.Services())
|
count := len(services.Services())
|
||||||
if count != 5 {
|
if count != 6 {
|
||||||
return errors.Errorf("incorrect services count: %d", count)
|
return errors.Errorf("incorrect services count: %d", count)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -39,11 +39,11 @@ func TestApiServiceRoutes(t *testing.T) {
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
ExpectedContains: []string{`"name":"Google"`},
|
ExpectedContains: []string{`"name":"Google"`},
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
ResponseLen: 4,
|
ResponseLen: 5,
|
||||||
BeforeTest: UnsetTestENV,
|
BeforeTest: UnsetTestENV,
|
||||||
FuncTest: func(t *testing.T) error {
|
FuncTest: func(t *testing.T) error {
|
||||||
count := len(services.Services())
|
count := len(services.Services())
|
||||||
if count != 5 {
|
if count != 6 {
|
||||||
return errors.Errorf("incorrect services count: %d", count)
|
return errors.Errorf("incorrect services count: %d", count)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -59,7 +59,7 @@ func TestApiServiceRoutes(t *testing.T) {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Statping Private Service 1",
|
Name: "Statping Private Service 1",
|
||||||
URL: "/api/services/2",
|
URL: "/api/services/6",
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
ExpectedContains: []string{`"error":"not authenticated"`},
|
ExpectedContains: []string{`"error":"not authenticated"`},
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
|
@ -105,21 +105,18 @@ func TestApiServiceRoutes(t *testing.T) {
|
||||||
Name: "Statping Service 1 Failure Data - 24 Hour",
|
Name: "Statping Service 1 Failure Data - 24 Hour",
|
||||||
URL: "/api/services/1/failure_data" + startEndQuery + "&group=24h",
|
URL: "/api/services/1/failure_data" + startEndQuery + "&group=24h",
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
ResponseLen: 4,
|
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Statping Service 1 Failure Data - 12 Hour",
|
Name: "Statping Service 1 Failure Data - 12 Hour",
|
||||||
URL: "/api/services/1/failure_data" + startEndQuery + "&group=12h",
|
URL: "/api/services/1/failure_data" + startEndQuery + "&group=12h",
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
ResponseLen: 7,
|
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "Statping Service 1 Failure Data - 1 Hour",
|
Name: "Statping Service 1 Failure Data - 1 Hour",
|
||||||
URL: "/api/services/1/failure_data" + startEndQuery + "&group=1h",
|
URL: "/api/services/1/failure_data" + startEndQuery + "&group=1h",
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
ResponseLen: 73,
|
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -140,7 +137,6 @@ func TestApiServiceRoutes(t *testing.T) {
|
||||||
Name: "Statping Service 1 Failure Data",
|
Name: "Statping Service 1 Failure Data",
|
||||||
URL: "/api/services/1/failure_data" + startEndQuery,
|
URL: "/api/services/1/failure_data" + startEndQuery,
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
ResponseLen: 73,
|
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -176,7 +172,7 @@ func TestApiServiceRoutes(t *testing.T) {
|
||||||
ExpectedContains: []string{`"status":"success","type":"service","method":"create"`, `"public":false`, `"group_id":1`},
|
ExpectedContains: []string{`"status":"success","type":"service","method":"create"`, `"public":false`, `"group_id":1`},
|
||||||
FuncTest: func(t *testing.T) error {
|
FuncTest: func(t *testing.T) error {
|
||||||
count := len(services.Services())
|
count := len(services.Services())
|
||||||
if count != 6 {
|
if count != 7 {
|
||||||
return errors.Errorf("incorrect services count: %d", count)
|
return errors.Errorf("incorrect services count: %d", count)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -238,7 +234,7 @@ func TestApiServiceRoutes(t *testing.T) {
|
||||||
ExpectedContains: []string{`"status":"success"`, `"method":"delete"`},
|
ExpectedContains: []string{`"status":"success"`, `"method":"delete"`},
|
||||||
FuncTest: func(t *testing.T) error {
|
FuncTest: func(t *testing.T) error {
|
||||||
count := len(services.Services())
|
count := len(services.Services())
|
||||||
if count != 5 {
|
if count != 6 {
|
||||||
return errors.Errorf("incorrect services count: %d", count)
|
return errors.Errorf("incorrect services count: %d", count)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -22,7 +22,7 @@ func (c *commandLine) Select() *notifications.Notification {
|
||||||
|
|
||||||
var Command = &commandLine{¬ifications.Notification{
|
var Command = &commandLine{¬ifications.Notification{
|
||||||
Method: "command",
|
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.",
|
Description: "Shell Command allows you to run a customized shell/bash Command on the local machine it's running on.",
|
||||||
Author: "Hunter Long",
|
Author: "Hunter Long",
|
||||||
AuthorUrl: "https://github.com/hunterlong",
|
AuthorUrl: "https://github.com/hunterlong",
|
||||||
|
@ -33,37 +33,30 @@ var Command = &commandLine{¬ifications.Notification{
|
||||||
Form: []notifications.NotificationForm{{
|
Form: []notifications.NotificationForm{{
|
||||||
Type: "text",
|
Type: "text",
|
||||||
Title: "Shell or Bash",
|
Title: "Shell or Bash",
|
||||||
Placeholder: "/bin/bash",
|
Placeholder: "/usr/bin/curl",
|
||||||
DbField: "host",
|
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",
|
Type: "text",
|
||||||
Title: "Command to Run on OnSuccess",
|
Title: "Command to Run on OnSuccess",
|
||||||
Placeholder: "curl google.com",
|
Placeholder: "http://localhost:8080/health",
|
||||||
DbField: "var1",
|
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",
|
Type: "text",
|
||||||
Title: "Command to Run on OnFailure",
|
Title: "Command to Run on OnFailure",
|
||||||
Placeholder: "curl offline.com",
|
Placeholder: "http://localhost:8080/health",
|
||||||
DbField: "var2",
|
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) {
|
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...)
|
outStr, errStr, err := utils.Command(app, cmd...)
|
||||||
return outStr, errStr, err
|
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
|
// OnSuccess for commandLine will trigger successful service
|
||||||
func (c *commandLine) OnSuccess(s *services.Service) error {
|
func (c *commandLine) OnSuccess(s *services.Service) error {
|
||||||
msg := c.GetValue("var1")
|
msg := c.GetValue("var1")
|
||||||
|
@ -72,11 +65,19 @@ func (c *commandLine) OnSuccess(s *services.Service) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnTest for commandLine triggers when this notifier has been saved
|
// OnFailure for commandLine will trigger failing service
|
||||||
func (c *commandLine) OnTest() error {
|
func (c *commandLine) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
cmds := strings.Split(c.Var1, " ")
|
msg := c.GetValue("var2")
|
||||||
in, out, err := runCommand(c.Host, cmds...)
|
tmpl := ReplaceVars(msg, s, f)
|
||||||
utils.Log.Infoln(in)
|
_, _, err := runCommand(c.Host, tmpl)
|
||||||
utils.Log.Infoln(out)
|
|
||||||
return err
|
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"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"github.com/statping/statping/types/failures"
|
"github.com/statping/statping/types/failures"
|
||||||
"github.com/statping/statping/types/notifications"
|
"github.com/statping/statping/types/notifications"
|
||||||
"github.com/statping/statping/types/notifier"
|
"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
|
// 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")
|
outError := errors.New("Incorrect discord URL, please confirm URL is correct")
|
||||||
message := `{"content": "Testing the discord notifier"}`
|
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)
|
contents, _, err := utils.HttpRequest(Discorder.Host, "POST", "application/json", nil, bytes.NewBuffer([]byte(message)), time.Duration(10*time.Second), true)
|
||||||
if string(contents) == "" {
|
if string(contents) == "" {
|
||||||
return nil
|
return "", nil
|
||||||
}
|
}
|
||||||
var dtt discordTestJson
|
var dtt discordTestJson
|
||||||
err = json.Unmarshal(contents, &dtt)
|
err = json.Unmarshal(contents, &dtt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return outError
|
return string(contents), outError
|
||||||
}
|
}
|
||||||
if dtt.Code == 0 {
|
if dtt.Code == 0 {
|
||||||
return outError
|
return string(contents), outError
|
||||||
}
|
}
|
||||||
fmt.Println("discord: ", string(contents))
|
return string(contents), nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type discordTestJson struct {
|
type discordTestJson struct {
|
||||||
|
|
|
@ -186,7 +186,7 @@ func (e *emailer) OnSuccess(s *services.Service) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnTest triggers when this notifier has been saved
|
// OnTest triggers when this notifier has been saved
|
||||||
func (e *emailer) OnTest() error {
|
func (e *emailer) OnTest() (string, error) {
|
||||||
testService := &services.Service{
|
testService := &services.Service{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Name: "Example Service",
|
Name: "Example Service",
|
||||||
|
@ -201,14 +201,16 @@ func (e *emailer) OnTest() error {
|
||||||
LastResponse: "<html>this is an example response</html>",
|
LastResponse: "<html>this is an example response</html>",
|
||||||
CreatedAt: utils.Now().Add(-24 * time.Hour),
|
CreatedAt: utils.Now().Add(-24 * time.Hour),
|
||||||
}
|
}
|
||||||
|
subject := fmt.Sprintf("Service %v is Back Online", testService.Name)
|
||||||
email := &emailOutgoing{
|
email := &emailOutgoing{
|
||||||
To: e.Var2,
|
To: e.Var2,
|
||||||
Subject: fmt.Sprintf("Service %v is Back Online", testService.Name),
|
Subject: subject,
|
||||||
Template: mainEmailTemplate,
|
Template: mainEmailTemplate,
|
||||||
Data: testService,
|
Data: testService,
|
||||||
From: e.Var1,
|
From: e.Var1,
|
||||||
}
|
}
|
||||||
return e.dialSend(email)
|
err := e.dialSend(email)
|
||||||
|
return subject, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (e *emailer) dialSend(email *emailOutgoing) error {
|
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
|
// 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 := url.Values{}
|
||||||
v.Set("message", message)
|
v.Set("message", message)
|
||||||
headers := []string{fmt.Sprintf("Authorization=Bearer %v", l.ApiSecret)}
|
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)
|
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 err
|
return string(content), err
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
func (l *lineNotifier) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (l *lineNotifier) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
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
|
// OnSuccess will trigger successful service
|
||||||
func (l *lineNotifier) OnSuccess(s *services.Service) error {
|
func (l *lineNotifier) OnSuccess(s *services.Service) error {
|
||||||
msg := fmt.Sprintf("Service %s is online!", s.Name)
|
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
|
// 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!")
|
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{
|
var Mobile = &mobilePush{¬ifications.Notification{
|
||||||
Method: "mobile",
|
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.
|
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>`,
|
<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",
|
Author: "Hunter Long",
|
||||||
|
@ -97,7 +97,7 @@ func (m *mobilePush) OnSuccess(s *services.Service) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnTest triggers when this notifier has been saved
|
// OnTest triggers when this notifier has been saved
|
||||||
func (m *mobilePush) OnTest() error {
|
func (m *mobilePush) OnTest() (string, error) {
|
||||||
msg := &pushArray{
|
msg := &pushArray{
|
||||||
Message: "Testing the Mobile Notifier",
|
Message: "Testing the Mobile Notifier",
|
||||||
Title: "Testing Notifications",
|
Title: "Testing Notifications",
|
||||||
|
@ -107,18 +107,18 @@ func (m *mobilePush) OnTest() error {
|
||||||
}
|
}
|
||||||
body, err := pushRequest(msg)
|
body, err := pushRequest(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
var output mobileResponse
|
var output mobileResponse
|
||||||
err = json.Unmarshal(body, &output)
|
err = json.Unmarshal(body, &output)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return string(body), err
|
||||||
}
|
}
|
||||||
if len(output.Logs) == 0 {
|
if len(output.Logs) == 0 {
|
||||||
return nil
|
return string(body), err
|
||||||
} else {
|
} else {
|
||||||
firstLog := output.Logs[0].Error
|
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,
|
Twilio,
|
||||||
Webhook,
|
Webhook,
|
||||||
Mobile,
|
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
|
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)
|
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 {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if string(contents) != "ok" {
|
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
|
// 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
|
// 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)
|
apiEndpoint := fmt.Sprintf("https://api.telegram.org/bot%v/sendMessage", t.ApiSecret)
|
||||||
|
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
|
@ -65,27 +65,30 @@ func (t *telegram) sendMessage(message string) error {
|
||||||
if !success {
|
if !success {
|
||||||
errorOut := telegramError(contents)
|
errorOut := telegramError(contents)
|
||||||
out := fmt.Sprintf("Error code %v - %v", errorOut.ErrorCode, errorOut.Description)
|
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
|
// OnFailure will trigger failing service
|
||||||
func (t *telegram) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (t *telegram) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
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
|
// OnSuccess will trigger successful service
|
||||||
func (t *telegram) OnSuccess(s *services.Service) error {
|
func (t *telegram) OnSuccess(s *services.Service) error {
|
||||||
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
|
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
|
// 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")
|
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) {
|
func telegramSuccess(res []byte) (bool, telegramResponse) {
|
||||||
|
|
|
@ -25,6 +25,7 @@ func init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestTelegramNotifier(t *testing.T) {
|
func TestTelegramNotifier(t *testing.T) {
|
||||||
|
t.SkipNow()
|
||||||
db, err := database.OpenTester()
|
db, err := database.OpenTester()
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
db.AutoMigrate(¬ifications.Notification{})
|
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
|
// 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"))
|
twilioUrl := fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%v/Messages.json", t.GetValue("api_key"))
|
||||||
|
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
|
@ -75,25 +75,27 @@ func (t *twilio) sendMessage(message string) error {
|
||||||
if !success {
|
if !success {
|
||||||
errorOut := twilioError(contents)
|
errorOut := twilioError(contents)
|
||||||
out := fmt.Sprintf("Error code %v - %v", errorOut.Code, errorOut.Message)
|
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
|
// OnFailure will trigger failing service
|
||||||
func (t *twilio) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (t *twilio) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
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
|
// OnSuccess will trigger successful service
|
||||||
func (t *twilio) OnSuccess(s *services.Service) error {
|
func (t *twilio) OnSuccess(s *services.Service) error {
|
||||||
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
|
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
|
// 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")
|
msg := fmt.Sprintf("Testing the Twilio SMS Notifier")
|
||||||
return t.sendMessage(msg)
|
return t.sendMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ type webhooker struct {
|
||||||
|
|
||||||
var Webhook = &webhooker{¬ifications.Notification{
|
var Webhook = &webhooker{¬ifications.Notification{
|
||||||
Method: webhookMethod,
|
Method: webhookMethod,
|
||||||
Title: "HTTP webhooker",
|
Title: "Webhook",
|
||||||
Description: "Send a custom HTTP request to a specific URL with your own body, headers, and parameters.",
|
Description: "Send a custom HTTP request to a specific URL with your own body, headers, and parameters.",
|
||||||
Author: "Hunter Long",
|
Author: "Hunter Long",
|
||||||
AuthorUrl: "https://github.com/hunterlong",
|
AuthorUrl: "https://github.com/hunterlong",
|
||||||
|
@ -113,16 +113,17 @@ func (w *webhooker) sendHttpWebhook(body string) (*http.Response, error) {
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *webhooker) OnTest() error {
|
func (w *webhooker) OnTest() (string, error) {
|
||||||
body := ReplaceVars(w.Var2, exampleService, exampleFailure)
|
body := ReplaceVars(w.Var2, exampleService, exampleFailure)
|
||||||
resp, err := w.sendHttpWebhook(body)
|
resp, err := w.sendHttpWebhook(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
content, err := ioutil.ReadAll(resp.Body)
|
content, err := ioutil.ReadAll(resp.Body)
|
||||||
utils.Log.Infoln(fmt.Sprintf("Webhook notifier received: '%v'", string(content)))
|
out := fmt.Sprintf("Webhook notifier received: '%v'", string(content))
|
||||||
return err
|
utils.Log.Infoln(out)
|
||||||
|
return out, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
|
|
|
@ -32,7 +32,7 @@ func All() []*Checkin {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Checkin) Create() error {
|
func (c *Checkin) Create() error {
|
||||||
c.ApiKey = utils.RandomString(7)
|
c.ApiKey = utils.RandomString(32)
|
||||||
q := db.Create(c)
|
q := db.Create(c)
|
||||||
|
|
||||||
c.Start()
|
c.Start()
|
||||||
|
|
|
@ -57,7 +57,7 @@ func Connect(configs *DbConfig, retry bool) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Debugln(fmt.Sprintf("Database connection error %s", err))
|
log.Debugln(fmt.Sprintf("Database connection error %s", err))
|
||||||
if retry {
|
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)
|
time.Sleep(5 * time.Second)
|
||||||
return Connect(configs, retry)
|
return Connect(configs, retry)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -30,7 +30,13 @@ func Select() (*Core, error) {
|
||||||
return nil, db.Error()
|
return nil, db.Error()
|
||||||
}
|
}
|
||||||
App = &c
|
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()
|
return App, q.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,6 +35,7 @@ type Core struct {
|
||||||
Timezone float32 `gorm:"column:timezone;default:-8.0" json:"timezone,omitempty"`
|
Timezone float32 `gorm:"column:timezone;default:-8.0" json:"timezone,omitempty"`
|
||||||
LoggedIn bool `gorm:"-" json:"logged_in"`
|
LoggedIn bool `gorm:"-" json:"logged_in"`
|
||||||
IsAdmin bool `gorm:"-" json:"admin"`
|
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"`
|
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||||
Started time.Time `gorm:"-" json:"started_on"`
|
Started time.Time `gorm:"-" json:"started_on"`
|
||||||
|
|
|
@ -9,7 +9,7 @@ func Samples() error {
|
||||||
incident1 := &Incident{
|
incident1 := &Incident{
|
||||||
Title: "Github Issues",
|
Title: "Github Issues",
|
||||||
Description: "There are new features for Statping, if you have any issues please visit the Github Repo.",
|
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 {
|
if err := incident1.Create(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -18,7 +18,7 @@ func Samples() error {
|
||||||
incident2 := &Incident{
|
incident2 := &Incident{
|
||||||
Title: "Recent Downtime",
|
Title: "Recent Downtime",
|
||||||
Description: "We've noticed an issue with authentications and we're looking into it now.",
|
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 {
|
if err := incident2.Create(); err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -9,5 +9,5 @@ import (
|
||||||
type Notifier interface {
|
type Notifier interface {
|
||||||
OnSuccess(*services.Service) error // OnSuccess is triggered when a service is successful
|
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
|
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 {
|
type ServiceNotifier interface {
|
||||||
OnSuccess(*Service) error // OnSuccess is triggered when a service is successful
|
OnSuccess(*Service) error // OnSuccess is triggered when a service is successful
|
||||||
OnFailure(*Service, *failures.Failure) error // OnFailure is triggered when a service is failing
|
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
|
Select() *notifications.Notification // OnTest is triggered for testing
|
||||||
}
|
}
|
||||||
|
|
48
utils/log.go
48
utils/log.go
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/fatih/structs"
|
"github.com/fatih/structs"
|
||||||
"github.com/getsentry/sentry-go"
|
"github.com/getsentry/sentry-go"
|
||||||
|
"github.com/prometheus/common/log"
|
||||||
Logger "github.com/sirupsen/logrus"
|
Logger "github.com/sirupsen/logrus"
|
||||||
"github.com/statping/statping/types/null"
|
"github.com/statping/statping/types/null"
|
||||||
"gopkg.in/natefinch/lumberjack.v2"
|
"gopkg.in/natefinch/lumberjack.v2"
|
||||||
|
@ -16,12 +17,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Log = Logger.StandardLogger()
|
Log = Logger.StandardLogger()
|
||||||
ljLogger *lumberjack.Logger
|
ljLogger *lumberjack.Logger
|
||||||
LastLines []*logRow
|
LastLines []*logRow
|
||||||
LockLines sync.Mutex
|
LockLines sync.Mutex
|
||||||
VerboseMode int
|
VerboseMode int
|
||||||
version string
|
version string
|
||||||
|
allowReports bool
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -29,22 +31,32 @@ const (
|
||||||
errorReporter = "https://ddf2784201134d51a20c3440e222cebe@sentry.statping.com/4"
|
errorReporter = "https://ddf2784201134d51a20c3440e222cebe@sentry.statping.com/4"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SentryInit(v string) {
|
func SentryInit(v *string, allow bool) {
|
||||||
if v == "" {
|
allowReports = allow
|
||||||
v = "development"
|
if v != nil {
|
||||||
|
if *v == "" {
|
||||||
|
*v = "development"
|
||||||
|
}
|
||||||
|
version = *v
|
||||||
}
|
}
|
||||||
version = v
|
goEnv := Getenv("GO_ENV", "production").(string)
|
||||||
errorEnv := Getenv("GO_ENV", "production").(string)
|
allowReports := Getenv("ALLOW_REPORTS", false).(bool)
|
||||||
if err := sentry.Init(sentry.ClientOptions{
|
if allowReports || allow {
|
||||||
Dsn: errorReporter,
|
if err := sentry.Init(sentry.ClientOptions{
|
||||||
Environment: errorEnv,
|
Dsn: errorReporter,
|
||||||
Release: v,
|
Environment: goEnv,
|
||||||
}); err != nil {
|
Release: version,
|
||||||
Log.Errorln(err)
|
}); err != nil {
|
||||||
|
log.Errorln(err)
|
||||||
|
}
|
||||||
|
Log.Infoln("Error Reporting initiated, thank you!")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func SentryErr(err error) {
|
func SentryErr(err error) {
|
||||||
|
if !allowReports {
|
||||||
|
return
|
||||||
|
}
|
||||||
sentry.CaptureException(err)
|
sentry.CaptureException(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +75,7 @@ type hook struct {
|
||||||
|
|
||||||
func (t *hook) Fire(e *Logger.Entry) error {
|
func (t *hook) Fire(e *Logger.Entry) error {
|
||||||
pushLastLine(e.Message)
|
pushLastLine(e.Message)
|
||||||
if e.Level == Logger.ErrorLevel {
|
if e.Level == Logger.ErrorLevel && allowReports {
|
||||||
SentryLogEntry(e)
|
SentryLogEntry(e)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0.90.23
|
0.90.25
|
||||||
|
|
Loading…
Reference in New Issue