mirror of https://github.com/statping/statping
integrations
parent
83ad6ab6db
commit
4fcd7d8347
|
@ -272,6 +272,8 @@ func HelpEcho() {
|
|||
fmt.Println(" ADMIN_PASS - Password for administrator account (default: admin)")
|
||||
fmt.Println(" SASS - Set the absolute path to the sass binary location")
|
||||
fmt.Println(" HTTP_PROXY - Use a HTTP Proxy for HTTP Requests")
|
||||
fmt.Println(" AUTH_USERNAME - HTTP Basic Authentication username")
|
||||
fmt.Println(" AUTH_PASSWORD - HTTP Basic Authentication password")
|
||||
fmt.Println(" * You can insert environment variables into a '.env' file in root directory.")
|
||||
fmt.Println("Give Statping a Star at https://github.com/hunterlong/statping")
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/core/notifier"
|
||||
"github.com/hunterlong/statping/integrations"
|
||||
"github.com/hunterlong/statping/notifiers"
|
||||
"github.com/hunterlong/statping/source"
|
||||
"github.com/hunterlong/statping/types"
|
||||
|
@ -68,6 +69,7 @@ func InitApp() {
|
|||
checkServices()
|
||||
AttachNotifiers()
|
||||
CoreApp.Notifications = notifier.AllCommunications
|
||||
CoreApp.Integrations = integrations.Integrations
|
||||
go DatabaseMaintence()
|
||||
SetupMode = false
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"projectId": "bi8mhr",
|
||||
"env": {
|
||||
"DB_HOST": "localhost",
|
||||
"DB_USER": "root",
|
||||
"DB_DATABASE": "root",
|
||||
"DB_PORT": "5432",
|
||||
"DB_PASS": "password123",
|
||||
"GO_ENV": "production"
|
||||
},
|
||||
"chromeWebSecurity": false,
|
||||
"defaultCommandTimeout": 5000,
|
||||
"requestTimeout": 5000,
|
||||
"watchForFileChanges": false
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
{
|
||||
"name": "Using fixtures to represent data",
|
||||
"email": "hello@cypress.io",
|
||||
"body": "Fixtures are a great way to mock data for responses to routes"
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
/*
|
||||
* Statping
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statping
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
context('Setup Process', () => {
|
||||
|
||||
// it('should go to setup Statping with Postgres', () => {
|
||||
// cy.visit('http://localhost:8080')
|
||||
// cy.get('select[name=db_connection]').select('postgres')
|
||||
// cy.get('input[name="db_host"]').clear().type(Cypress.env('DB_HOST'))
|
||||
// cy.get('input[name="db_port"]').clear().type('5432')
|
||||
// cy.get('input[name="db_user"]').clear().type(Cypress.env('DB_USER'))
|
||||
// if (Cypress.env('TRAVIS')==="yes") {
|
||||
// cy.get('input[name="db_password"]').clear()
|
||||
// } else {
|
||||
// cy.get('input[name="db_password"]').clear().type(Cypress.env('DB_PASS'))
|
||||
// }
|
||||
// cy.get('input[name="db_database"]').clear().type(Cypress.env('DB_DATABASE'))
|
||||
// cy.get('input[name="project"]').clear().type('Demo Tester')
|
||||
// cy.get('input[name="description"]').clear().type('This is a test from Crypress!')
|
||||
// cy.get('input[name="domain"]').clear().type('http://localhost:8080')
|
||||
// cy.get('input[name="username"]').clear().type('admin')
|
||||
// cy.get('input[name="email"]').clear().type('info@domain.com')
|
||||
// cy.get('input[name="password"]').clear().type('admin')
|
||||
// cy.scrollTo('bottom')
|
||||
// cy.get('#setup_button').click().wait(10000)
|
||||
// cy.get('.header-title').should('contain', 'Demo Tester')
|
||||
// cy.get('.header-desc').should('contain', 'This is a test from Crypress!')
|
||||
// cy.scrollTo('bottom')
|
||||
// cy.get('.service_li').should('have.length', 5)
|
||||
// cy.get('.card').should('have.length', 5)
|
||||
// })
|
||||
|
||||
it('should go to setup Statping with SQLite', () => {
|
||||
cy.visit('http://localhost:8080')
|
||||
cy.get('select[name=db_connection]').select('sqlite')
|
||||
cy.get('input[name="project"]').clear().type('Demo Tester')
|
||||
cy.get('input[name="description"]').clear().type('This is a test from Crypress!')
|
||||
cy.get('input[name="domain"]').clear().type('http://localhost:8080')
|
||||
cy.get('input[name="username"]').clear().type('admin')
|
||||
cy.get('input[name="email"]').clear().type('info@domain.com')
|
||||
cy.get('input[name="password"]').clear().type('admin')
|
||||
cy.scrollTo('bottom')
|
||||
cy.get('#setup_button').click()
|
||||
cy.get('.header-title').should('contain', 'Demo Tester')
|
||||
cy.get('.header-desc').should('contain', 'This is a test from Crypress!')
|
||||
cy.scrollTo('bottom')
|
||||
cy.get('.service_li').should('have.length', 5)
|
||||
cy.get('.card').should('have.length', 5)
|
||||
})
|
||||
|
||||
})
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Statping
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statping
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
context('Asset Tests', () => {
|
||||
|
||||
beforeEach(function () {
|
||||
cy.visit('http://localhost:8080/dashboard')
|
||||
cy.get('input[name="username"]').type('admin')
|
||||
cy.get('input[name="password"]').type('admin')
|
||||
cy.get('form').submit()
|
||||
})
|
||||
|
||||
it('should create local assets', () => {
|
||||
cy.visit('http://localhost:8080/settings/build')
|
||||
cy.get('#v-pills-style-tab').click()
|
||||
cy.wait(500)
|
||||
cy.get(':nth-child(2) > .CodeMirror-line').should('contain', '$background-color')
|
||||
})
|
||||
|
||||
it('should save assets form', () => {
|
||||
cy.request({method: 'POST', url: 'http://localhost:8080/settings/css', form: true, body: {
|
||||
variables: '$tester: #bababa',
|
||||
theme: '@import \'variables\'; .test-var { color: $tester; }'
|
||||
}})
|
||||
})
|
||||
|
||||
it('should confirm sass variable in css', () => {
|
||||
cy.request('http://localhost:8080/css/base.css').its('body').should('contain', '.test-var')
|
||||
})
|
||||
|
||||
it('should delete assets', () => {
|
||||
cy.visit('http://localhost:8080/settings')
|
||||
cy.get('#v-pills-style-tab').click()
|
||||
cy.wait(500)
|
||||
cy.get('.btn-danger').click()
|
||||
})
|
||||
|
||||
it('should check css file after delete', () => {
|
||||
cy.request('http://localhost:8080/css/base.css').its('body').should('contain', 'BODY')
|
||||
})
|
||||
|
||||
});
|
|
@ -1,41 +0,0 @@
|
|||
/*
|
||||
* Statping
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statping
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
context('Dashboard Tests', () => {
|
||||
|
||||
beforeEach(function() {
|
||||
cy.visit('http://localhost:8080/dashboard')
|
||||
cy.get('input[name="username"]').type('admin')
|
||||
cy.get('input[name="password"]').type('admin')
|
||||
cy.get('form').submit()
|
||||
})
|
||||
|
||||
it('should view logs', () => {
|
||||
cy.visit('http://localhost:8080/settings')
|
||||
cy.get(':nth-child(5) > .nav-link').click()
|
||||
cy.wait(10000)
|
||||
cy.get('#live_logs').should('contain', 'Service')
|
||||
})
|
||||
|
||||
it('should view help', () => {
|
||||
cy.visit('http://localhost:8080/settings')
|
||||
cy.get(':nth-child(6) > .nav-link').click()
|
||||
cy.title().should('eq', 'Statping | Help')
|
||||
cy.get('.col-12 > :nth-child(1)').should('contain', 'Statping')
|
||||
})
|
||||
|
||||
});
|
|
@ -1,115 +0,0 @@
|
|||
/*
|
||||
* Statping
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statping
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
context('Service Tests', () => {
|
||||
|
||||
beforeEach(function () {
|
||||
cy.visit('http://localhost:8080/dashboard')
|
||||
cy.get('input[name="username"]').type('admin')
|
||||
cy.get('input[name="password"]').type('admin')
|
||||
cy.get('form').submit()
|
||||
})
|
||||
|
||||
it('should view services', () => {
|
||||
cy.visit('http://localhost:8080/services')
|
||||
cy.get('tr').should('have.length', 6)
|
||||
cy.title().should('eq', 'Statping | Services')
|
||||
})
|
||||
|
||||
it('should create HTTP GET service', () => {
|
||||
cy.visit('http://localhost:8080/services')
|
||||
cy.get('select[name="method"]').select('GET')
|
||||
cy.get('input[name="name"]').clear().type('Google.com')
|
||||
cy.get('select[name="check_type"]').select('http')
|
||||
cy.get('input[name="domain"]').clear().type('https://google.com')
|
||||
cy.get('input[name="expected_status"]').clear().type('200')
|
||||
cy.get('input[name="interval"]').clear().type('25')
|
||||
cy.get('input[name="timeout"]').clear().type('30')
|
||||
cy.get('form').submit()
|
||||
cy.title().should('eq', 'Statping | Services')
|
||||
cy.get('tr').should('have.length', 7)
|
||||
})
|
||||
|
||||
it('should create HTTP POST service', () => {
|
||||
cy.visit('http://localhost:8080/services')
|
||||
cy.get('select[name="method"]').select('POST')
|
||||
cy.get('input[name="name"]').clear().type('JSON Regex Test')
|
||||
cy.get('select[name="check_type"]').select('http')
|
||||
cy.get('input[name="domain"]').clear().type('https://jsonplaceholder.typicode.com/posts')
|
||||
cy.get('textarea[name="post_data"]').clear().type(`(title)": "((\\"|[statping])*)"`)
|
||||
cy.get('input[name="expected_status"]').clear().type('201')
|
||||
cy.get('input[name="interval"]').clear().type('15')
|
||||
cy.get('input[name="timeout"]').clear().type('45')
|
||||
cy.get('form').submit()
|
||||
cy.title().should('eq', 'Statping | Services')
|
||||
cy.get('tr').should('have.length', 8)
|
||||
})
|
||||
|
||||
it('should create TCP service', () => {
|
||||
cy.visit('http://localhost:8080/services')
|
||||
cy.get('select[name="check_type"]').select('tcp')
|
||||
cy.get('input[name="name"]').clear().type('Google DNS')
|
||||
cy.get('input[name="domain"]').clear().type('8.8.8.8')
|
||||
cy.get('input[name="port"]').clear().type('53')
|
||||
cy.get('input[name="interval"]').clear().type('25')
|
||||
cy.get('input[name="timeout"]').clear().type('15')
|
||||
cy.get('form').submit()
|
||||
cy.title().should('eq', 'Statping | Services')
|
||||
cy.get('tr').should('have.length', 9)
|
||||
})
|
||||
|
||||
it('should view HTTP GET service', () => {
|
||||
cy.visit('http://localhost:8080/service/6')
|
||||
cy.title().should('eq', 'Statping | Google.com Service')
|
||||
})
|
||||
|
||||
it('should view HTTP POST service', () => {
|
||||
cy.visit('http://localhost:8080/service/7')
|
||||
cy.title().should('eq', 'Statping | JSON Regex Test Service')
|
||||
})
|
||||
|
||||
it('should view TCP service', () => {
|
||||
cy.visit('http://localhost:8080/service/8')
|
||||
cy.title().should('eq', 'Statping | Google DNS Service')
|
||||
})
|
||||
|
||||
it('should update HTTP service', () => {
|
||||
cy.visit('http://localhost:8080/service/6')
|
||||
cy.title().should('eq', 'Statping | Google.com Service')
|
||||
cy.get('#service_name').clear().type('Google Updated')
|
||||
cy.get('#service_interval').clear().type('60')
|
||||
cy.get(':nth-child(3) > form').submit()
|
||||
cy.title().should('eq', 'Statping | Google Updated Service')
|
||||
cy.get('#service_name').should('have.value', 'Google Updated')
|
||||
});
|
||||
|
||||
it('should check the updated service', () => {
|
||||
cy.visit('http://localhost:8080/service/6')
|
||||
cy.title().should('eq', 'Statping | Google Updated Service')
|
||||
cy.get('#service_name').should('have.value', 'Google Updated')
|
||||
cy.get('#service_interval').should('have.value', '60')
|
||||
})
|
||||
|
||||
it('should delete a service', () => {
|
||||
cy.visit('http://localhost:8080/services')
|
||||
cy.get(':nth-child(5) > .text-right > .btn-group > .btn-danger').click()
|
||||
cy.title().should('eq', 'Statping | Services')
|
||||
cy.get('tr').should('have.length', 8)
|
||||
})
|
||||
|
||||
|
||||
});
|
|
@ -1,48 +0,0 @@
|
|||
/*
|
||||
* Statping
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statping
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
context('Settings Forms', () => {
|
||||
|
||||
beforeEach(function() {
|
||||
cy.visit('http://localhost:8080/dashboard')
|
||||
cy.get('input[name="username"]').type('admin')
|
||||
cy.get('input[name="password"]').type('admin')
|
||||
cy.get('form').submit()
|
||||
})
|
||||
|
||||
it('should edit main settings', () => {
|
||||
cy.visit('http://localhost:8080/settings')
|
||||
cy.get('input[name="project"]').clear().type('Project Updated')
|
||||
cy.get('input[name="description"]').clear().type('This is an awesome page')
|
||||
cy.get('input[name="domain"]').clear().type('http://0.0.0.0:8080')
|
||||
cy.get('textarea[name="footer"]').clear().type('This is a custom footer')
|
||||
cy.get('#v-pills-home > form').submit()
|
||||
cy.title().should('eq', 'Statping | Settings')
|
||||
cy.get('input[name="project"]').should('have.value', 'Project Updated')
|
||||
cy.get('input[name="description"]').should('have.value', 'This is an awesome page')
|
||||
cy.get('input[name="domain"]').should('have.value', 'http://0.0.0.0:8080')
|
||||
cy.get('.footer').should('contain', 'This is a custom footer')
|
||||
})
|
||||
|
||||
// it('should check index page for changes', () => {
|
||||
// cy.visit('http://localhost:8080/')
|
||||
// cy.title().should('eq', 'Project Updated Status')
|
||||
// cy.get('.header-title').should('contain', 'Project Updated')
|
||||
// cy.get('.header-desc').should('contain', 'This is an awesome page')
|
||||
// })
|
||||
|
||||
});
|
|
@ -1,69 +0,0 @@
|
|||
/*
|
||||
* Statping
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statping
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
context('User Testing', () => {
|
||||
|
||||
beforeEach(function () {
|
||||
cy.visit('http://localhost:8080/dashboard')
|
||||
cy.get('input[name="username"]').type('admin')
|
||||
cy.get('input[name="password"]').type('admin')
|
||||
cy.get('form').submit()
|
||||
})
|
||||
|
||||
it('should view users', () => {
|
||||
cy.visit('http://localhost:8080/users')
|
||||
cy.get('tr').should('have.length', 2)
|
||||
cy.title().should('eq', 'Statping | Users')
|
||||
})
|
||||
|
||||
it('should create a new user', () => {
|
||||
cy.visit('http://localhost:8080/users')
|
||||
cy.get('input[name="username"]').type('hunterlong')
|
||||
cy.get('input[name="email"]').type('info@yayaya.com')
|
||||
cy.get('input[name="password"]').type('admin')
|
||||
cy.get('input[name="password_confirm"]').type('admin')
|
||||
cy.get('form').submit()
|
||||
cy.get('tr').should('have.length', 3)
|
||||
})
|
||||
|
||||
it('should create a edit user', () => {
|
||||
cy.visit('http://localhost:8080/user/2')
|
||||
cy.get('input[name="password"]').type('password567')
|
||||
cy.get('input[name="password_confirm"]').type('password567')
|
||||
cy.get('form').submit()
|
||||
cy.get('tr').should('have.length', 3)
|
||||
})
|
||||
|
||||
// it('should logout and login with new password', () => {
|
||||
// cy.visit('http://localhost:8080/logout')
|
||||
// cy.title().should('eq', 'Statping | Users')
|
||||
// cy.get('#user_2 > .btn-group > .btn-danger').click()
|
||||
// cy.get('tr').should('have.length', 2)
|
||||
// cy.visit('http://localhost:8080/login')
|
||||
// cy.get('input[name="username"]').type('hunterlong')
|
||||
// cy.get('input[name="password"]').type('password567')
|
||||
// cy.get('form').submit()
|
||||
// cy.title().should('eq', 'Project Updated Status')
|
||||
// })
|
||||
|
||||
it('should delete a user', () => {
|
||||
cy.visit('http://localhost:8080/users')
|
||||
cy.get('#user_2 > .btn-group > .btn-danger').click()
|
||||
cy.get('tr').should('have.length', 2)
|
||||
})
|
||||
|
||||
});
|
|
@ -1,34 +0,0 @@
|
|||
/*
|
||||
* Statping
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statping
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ***********************************************************
|
||||
// This example plugins/index.js can be used to load plugins
|
||||
//
|
||||
// You can change the location of this file or turn off loading
|
||||
// the plugins file with the 'pluginsFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/plugins-guide
|
||||
// ***********************************************************
|
||||
|
||||
// This function is called when a project is opened or re-opened (e.g. due to
|
||||
// the project's config changing)
|
||||
|
||||
module.exports = (on, config) => {
|
||||
// `on` is used to hook into various events Cypress emits
|
||||
// `config` is the resolved Cypress config
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/*
|
||||
* Statping
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statping
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ***********************************************
|
||||
// This example commands.js shows you how to
|
||||
// create various custom commands and overwrite
|
||||
// existing commands.
|
||||
//
|
||||
// For more comprehensive examples of custom
|
||||
// commands please read more here:
|
||||
// https://on.cypress.io/custom-commands
|
||||
// ***********************************************
|
||||
//
|
||||
//
|
||||
// -- This is a parent command --
|
||||
// Cypress.Commands.add("login", (email, password) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a child command --
|
||||
// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is a dual command --
|
||||
// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... })
|
||||
//
|
||||
//
|
||||
// -- This is will overwrite an existing command --
|
||||
// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... })
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Statping
|
||||
* Copyright (C) 2018. Hunter Long and the project contributors
|
||||
* Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
*
|
||||
* https://github.com/hunterlong/statping
|
||||
*
|
||||
* The licenses for most software and other practical works are designed
|
||||
* to take away your freedom to share and change the works. By contrast,
|
||||
* the GNU General Public License is intended to guarantee your freedom to
|
||||
* share and change all versions of a program--to make sure it remains free
|
||||
* software for all its users.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// ***********************************************************
|
||||
// This example support/index.js is processed and
|
||||
// loaded automatically before your test files.
|
||||
//
|
||||
// This is a great place to put global configuration and
|
||||
// behavior that modifies Cypress.
|
||||
//
|
||||
// You can change the location of this file or turn off
|
||||
// automatically serving support files with the
|
||||
// 'supportFile' configuration option.
|
||||
//
|
||||
// You can read more here:
|
||||
// https://on.cypress.io/configuration
|
||||
// ***********************************************************
|
||||
|
||||
// Import commands.js using ES2015 syntax:
|
||||
import './commands'
|
||||
|
||||
// Alternatively you can use CommonJS syntax:
|
||||
// require('./commands')
|
File diff suppressed because it is too large
Load Diff
|
@ -1,23 +0,0 @@
|
|||
{
|
||||
"name": "statping-testing",
|
||||
"version": "1.0.0",
|
||||
"description": "Statping Application Testing using Cypress!",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"cypress": "^3.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"start-server-and-test": "^1.7.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "curl -I -X GET http://localhost:8080/robots.txt",
|
||||
"cy:run-video": "cypress run --record --key $CYPRESS_KEY",
|
||||
"cy:run": "cypress run",
|
||||
"test": "bash -c \"./test.sh\"",
|
||||
"test-docker": "bash -c \"./test-docker.sh\"",
|
||||
"testnovid": "cypress run",
|
||||
"open": "cypress open"
|
||||
},
|
||||
"author": "Hunter Long",
|
||||
"license": "ISC"
|
||||
}
|
|
@ -1,5 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
RELEASE_MASTER='{ "request": { "branch": "master", "config": { "script": "make docker-run-test", "services": ["docker"], "before_script": [], "after_deploy": [], "after_success": ["make publish-homebrew", "make publish-latest"], "deploy": [], "before_deploy": [], "env": { "VERSION": "$(VERSION)" } } } }'
|
||||
|
||||
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token $(TRAVIS_API)" -d $(RELEASE_MASTER) https://api.travis-ci.com/repo/hunterlong%2Fstatping/requests
|
|
@ -1,5 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
statping > /dev/null &
|
||||
|
||||
./node_modules/.bin/start-server-and-test start http://localhost:8080/robots.txt cy:run
|
|
@ -1,13 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
DIR=`pwd`
|
||||
DOCKER=`which docker`
|
||||
|
||||
$DOCKER build -t hunterlong/statping:dev -f ../Dockerfile ../../
|
||||
$DOCKER run -it -d -p 8080:8080 -v $DIR/app:/app --name statping_dev hunterlong/statping:dev
|
||||
|
||||
./node_modules/.bin/start-server-and-test start http://localhost:8080/robots.txt cy:run
|
||||
|
||||
$DOCKER stop statping_dev || true && $DOCKER rm -f statping_dev || true
|
||||
|
||||
sudo rm -rf $DIR/app
|
5
go.mod
5
go.mod
|
@ -8,6 +8,10 @@ require (
|
|||
github.com/agnivade/levenshtein v1.0.2 // indirect
|
||||
github.com/ararog/timeago v0.0.0-20160328174124-e9969cf18b8d
|
||||
github.com/daaku/go.zipexe v1.0.1 // indirect
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
github.com/docker/docker v1.13.1
|
||||
github.com/docker/go-connections v0.4.0
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/go-mail/mail v2.3.1+incompatible
|
||||
github.com/go-yaml/yaml v2.1.0+incompatible
|
||||
|
@ -18,6 +22,7 @@ require (
|
|||
github.com/jinzhu/gorm v1.9.11
|
||||
github.com/joho/godotenv v1.3.0
|
||||
github.com/lib/pq v1.2.0 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
|
||||
github.com/rendon/testcli v0.0.0-20161027181003-6283090d169f
|
||||
github.com/russross/blackfriday/v2 v2.0.1
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
||||
|
|
10
go.sum
10
go.sum
|
@ -34,6 +34,14 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3 h1:tkum0XDgfR0jcVVXuTsYv/erY2NnEDqwRojbxR1rBYA=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20190515213511-eb9f6a1743f3/go.mod h1:zAg7JM8CkOJ43xKXIj7eRO9kmWm/TW578qo+oDO6tuM=
|
||||
github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
|
||||
github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
|
||||
github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
|
||||
github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
|
@ -119,6 +127,8 @@ github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYX
|
|||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
|
|
|
@ -43,7 +43,7 @@ var (
|
|||
httpServer *http.Server
|
||||
usingSSL bool
|
||||
mainTmpl = `{{define "main" }} {{ template "base" . }} {{ end }}`
|
||||
templates = []string{"base.gohtml", "head.gohtml", "nav.gohtml", "footer.gohtml", "scripts.gohtml", "form_service.gohtml", "form_notifier.gohtml", "form_group.gohtml", "form_user.gohtml", "form_checkin.gohtml", "form_message.gohtml"}
|
||||
templates = []string{"base.gohtml", "head.gohtml", "nav.gohtml", "footer.gohtml", "scripts.gohtml", "form_service.gohtml", "form_notifier.gohtml", "form_integration.gohtml", "form_group.gohtml", "form_user.gohtml", "form_checkin.gohtml", "form_message.gohtml"}
|
||||
javascripts = []string{"charts.js", "chart_index.js"}
|
||||
mainTemplate *template.Template
|
||||
)
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/hunterlong/statping/integrations"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func apiAllIntegrationsHandler(w http.ResponseWriter, r *http.Request) {
|
||||
integrations := integrations.Integrations
|
||||
returnJson(integrations, w, r)
|
||||
}
|
||||
|
||||
func apiIntegrationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
intg := vars["name"]
|
||||
r.ParseForm()
|
||||
for k, v := range r.PostForm {
|
||||
log.Info(k, v)
|
||||
}
|
||||
|
||||
integration, err := integrations.Find(intg)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
list, err := integration.List()
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
returnJson(list, w, r)
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/core"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
|
@ -9,6 +10,28 @@ import (
|
|||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
authUser string
|
||||
authPass string
|
||||
)
|
||||
|
||||
// basicAuthHandler is a middleware to implement HTTP basic authentication using
|
||||
// AUTH_USERNAME and AUTH_PASSWORD environment variables
|
||||
func basicAuthHandler(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
user, pass, ok := r.BasicAuth()
|
||||
if !ok || subtle.ConstantTimeCompare([]byte(user),
|
||||
[]byte(authUser)) != 1 || subtle.ConstantTimeCompare([]byte(pass),
|
||||
[]byte(authPass)) != 1 {
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="statping"`)
|
||||
w.WriteHeader(401)
|
||||
w.Write([]byte("You are unauthorized to access the application.\n"))
|
||||
return
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
// sendLog is a http middleware that will log the duration of request and other useful fields
|
||||
func sendLog(handler func(w http.ResponseWriter, r *http.Request)) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -25,6 +25,7 @@ import (
|
|||
"github.com/hunterlong/statping/source"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"net/http"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -38,6 +39,11 @@ func Router() *mux.Router {
|
|||
dir := utils.Directory
|
||||
CacheStorage = NewStorage()
|
||||
r := mux.NewRouter()
|
||||
if os.Getenv("AUTH_USERNAME") != "" && os.Getenv("AUTH_PASSWORD") != "" {
|
||||
authUser = os.Getenv("AUTH_USERNAME")
|
||||
authPass = os.Getenv("AUTH_PASSWORD")
|
||||
r.Use(basicAuthHandler)
|
||||
}
|
||||
r.Handle("/", sendLog(indexHandler))
|
||||
if source.UsingAssets(dir) {
|
||||
indexHandler := http.FileServer(http.Dir(dir + "/assets/"))
|
||||
|
@ -86,6 +92,7 @@ func Router() *mux.Router {
|
|||
r.Handle("/settings/delete_assets", authenticated(deleteAssetsHandler, true)).Methods("GET")
|
||||
r.Handle("/settings/export", authenticated(exportHandler, true)).Methods("GET")
|
||||
r.Handle("/settings/bulk_import", authenticated(bulkImportHandler, true)).Methods("POST")
|
||||
r.Handle("/settings/integrator/{name}", authenticated(integratorHandler, true)).Methods("POST")
|
||||
|
||||
// SERVICE Routes
|
||||
r.Handle("/services", authenticated(servicesHandler, true)).Methods("GET")
|
||||
|
@ -109,6 +116,10 @@ func Router() *mux.Router {
|
|||
r.Handle("/api/renew", authenticated(apiRenewHandler, false))
|
||||
r.Handle("/api/clear_cache", authenticated(apiClearCacheHandler, false))
|
||||
|
||||
r.Handle("/api/integrations", authenticated(apiAllIntegrationsHandler, false)).Methods("GET")
|
||||
r.Handle("/api/integrations/{name}", authenticated(apiIntegrationHandler, false)).Methods("GET")
|
||||
r.Handle("/api/integrations/{name}", authenticated(apiIntegrationHandler, false)).Methods("POST")
|
||||
|
||||
// API SERVICE Routes
|
||||
r.Handle("/api/services", readOnly(apiAllServicesHandler, false)).Methods("GET")
|
||||
r.Handle("/api/services", authenticated(apiCreateServiceHandler, false)).Methods("POST")
|
||||
|
|
|
@ -18,7 +18,9 @@ package handlers
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/hunterlong/statping/core"
|
||||
"github.com/hunterlong/statping/integrations"
|
||||
"github.com/hunterlong/statping/source"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
|
@ -144,6 +146,54 @@ func bulkImportHandler(w http.ResponseWriter, r *http.Request) {
|
|||
ExecuteResponse(w, r, "settings.gohtml", core.CoreApp, "/settings")
|
||||
}
|
||||
|
||||
type integratorOut struct {
|
||||
Integrator *types.Integration `json:"integrator"`
|
||||
Services []*types.Service `json:"services"`
|
||||
Error error
|
||||
}
|
||||
|
||||
func integratorHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
integratorName := vars["name"]
|
||||
r.ParseForm()
|
||||
|
||||
integrator, err := integrations.Find(integratorName)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
ExecuteResponse(w, r, "integrator.gohtml", integratorOut{
|
||||
Error: err,
|
||||
}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
log.Info(r.PostForm)
|
||||
|
||||
for _, v := range integrator.Get().Fields {
|
||||
log.Info(v.Name, v.Value)
|
||||
}
|
||||
|
||||
integrations.SetFields(integrator, r.PostForm)
|
||||
|
||||
for _, v := range integrator.Get().Fields {
|
||||
log.Info(v.Name, v.Value)
|
||||
}
|
||||
|
||||
services, err := integrator.List()
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
ExecuteResponse(w, r, "integrator.gohtml", integratorOut{
|
||||
Integrator: integrator.Get(),
|
||||
Error: err,
|
||||
}, nil)
|
||||
return
|
||||
}
|
||||
|
||||
ExecuteResponse(w, r, "integrator.gohtml", integratorOut{
|
||||
Integrator: integrator.Get(),
|
||||
Services: services,
|
||||
}, nil)
|
||||
}
|
||||
|
||||
// commaToService will convert a CSV comma delimited string slice to a Service type
|
||||
// this function is used for the bulk import services feature
|
||||
func commaToService(s []string) (*types.Service, error) {
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
// Statping
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statping
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const requiredSize = 17
|
||||
|
||||
type csvIntegration struct {
|
||||
*types.Integration
|
||||
}
|
||||
|
||||
var csvIntegrator = &csvIntegration{&types.Integration{
|
||||
ShortName: "csv",
|
||||
Name: "CSV File",
|
||||
Description: "Import multiple services from a CSV file",
|
||||
Fields: []*types.IntegrationField{
|
||||
{
|
||||
Name: "input",
|
||||
Type: "file",
|
||||
MimeType: "application/csv",
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
var csvData [][]string
|
||||
|
||||
func (t *csvIntegration) Get() *types.Integration {
|
||||
return t.Integration
|
||||
}
|
||||
|
||||
func (t *csvIntegration) List() ([]*types.Service, error) {
|
||||
path := csvIntegrator.Fields[0].Value.(string)
|
||||
data, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, line := range strings.Split(strings.TrimSuffix(string(data), "\n"), "\n") {
|
||||
col := strings.Split(line, ",")
|
||||
csvData = append(csvData, col)
|
||||
}
|
||||
|
||||
var services []*types.Service
|
||||
for _, v := range csvData {
|
||||
s, err := commaToService(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
services = append(services, s)
|
||||
}
|
||||
return services, nil
|
||||
}
|
||||
|
||||
// commaToService will convert a CSV comma delimited string slice to a Service type
|
||||
// this function is used for the bulk import services feature
|
||||
func commaToService(s []string) (*types.Service, error) {
|
||||
if len(s) != requiredSize {
|
||||
err := fmt.Errorf("file has %v columns of data, not the expected amount of %v columns for a service", len(s), requiredSize)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
interval, err := time.ParseDuration(s[4])
|
||||
if err != nil {
|
||||
return nil, errors.New("could not parse internal duration: " + s[4])
|
||||
}
|
||||
|
||||
timeout, err := time.ParseDuration(s[9])
|
||||
if err != nil {
|
||||
return nil, errors.New("could not parse timeout duration: " + s[9])
|
||||
}
|
||||
|
||||
allowNotifications, err := strconv.ParseBool(s[11])
|
||||
if err != nil {
|
||||
return nil, errors.New("could not parse allow notifications boolean: " + s[11])
|
||||
}
|
||||
|
||||
public, err := strconv.ParseBool(s[12])
|
||||
if err != nil {
|
||||
return nil, errors.New("could not parse public boolean: " + s[12])
|
||||
}
|
||||
|
||||
verifySsl, err := strconv.ParseBool(s[16])
|
||||
if err != nil {
|
||||
return nil, errors.New("could not parse verifiy SSL boolean: " + s[16])
|
||||
}
|
||||
|
||||
newService := &types.Service{
|
||||
Name: s[0],
|
||||
Domain: s[1],
|
||||
Expected: types.NewNullString(s[2]),
|
||||
ExpectedStatus: int(utils.ToInt(s[3])),
|
||||
Interval: int(utils.ToInt(interval.Seconds())),
|
||||
Type: s[5],
|
||||
Method: s[6],
|
||||
PostData: types.NewNullString(s[7]),
|
||||
Port: int(utils.ToInt(s[8])),
|
||||
Timeout: int(utils.ToInt(timeout.Seconds())),
|
||||
AllowNotifications: types.NewNullBool(allowNotifications),
|
||||
Public: types.NewNullBool(public),
|
||||
GroupId: int(utils.ToInt(s[13])),
|
||||
Headers: types.NewNullString(s[14]),
|
||||
Permalink: types.NewNullString(s[15]),
|
||||
VerifySSL: types.NewNullBool(verifySsl),
|
||||
}
|
||||
|
||||
return newService, nil
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package integrations
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCsvFileIntegration(t *testing.T) {
|
||||
|
||||
csvIntegrator.Fields[0].Value = "test_files/example_services.csv"
|
||||
|
||||
t.Run("CSV File", func(t *testing.T) {
|
||||
path := csvIntegrator.Fields[0].Value
|
||||
assert.Equal(t, "test_files/example_services.csv", path)
|
||||
})
|
||||
|
||||
t.Run("CSV Open File", func(t *testing.T) {
|
||||
err := csvIntegrator.Open()
|
||||
require.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("List Services from CSV File", func(t *testing.T) {
|
||||
services, err := csvIntegrator.List()
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, len(services), 1)
|
||||
})
|
||||
|
||||
t.Run("Confirm Services from CSV File", func(t *testing.T) {
|
||||
services, err := csvIntegrator.List()
|
||||
require.Nil(t, err)
|
||||
assert.Equal(t, "Bulk Upload", services[0].Name)
|
||||
assert.Equal(t, "http://google.com", services[0].Domain)
|
||||
assert.Equal(t, 60, services[0].Interval)
|
||||
for _, s := range services {
|
||||
t.Log(s)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Close CSV", func(t *testing.T) {
|
||||
err := csvIntegrator.Close()
|
||||
require.Nil(t, err)
|
||||
})
|
||||
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
// Statping
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statping
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"context"
|
||||
dTypes "github.com/docker/docker/api/types"
|
||||
"github.com/docker/docker/client"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"os"
|
||||
)
|
||||
|
||||
type dockerIntegration struct {
|
||||
*types.Integration
|
||||
}
|
||||
|
||||
var dockerIntegrator = &dockerIntegration{&types.Integration{
|
||||
ShortName: "docker",
|
||||
Name: "Docker",
|
||||
Description: `Import multiple services from Docker by attaching the unix socket to Statping.
|
||||
You can also do this in Docker by setting <u>-v /var/run/docker.sock:/var/run/docker.sock</u> in the Statping Docker container.
|
||||
All of the containers with open TCP/UDP ports will be listed for you to choose which services you want to add. If you running Statping inside of a container,
|
||||
this container must be attached to all networks you want to communicate with.`,
|
||||
Fields: []*types.IntegrationField{
|
||||
{
|
||||
Name: "path",
|
||||
Description: "The absolute path to the Docker unix socket",
|
||||
Type: "text",
|
||||
Value: client.DefaultDockerHost,
|
||||
},
|
||||
{
|
||||
Name: "version",
|
||||
Description: "Version number of Docker server",
|
||||
Type: "text",
|
||||
Value: client.DefaultVersion,
|
||||
},
|
||||
},
|
||||
}}
|
||||
|
||||
var cli *client.Client
|
||||
|
||||
func (t *dockerIntegration) Get() *types.Integration {
|
||||
return t.Integration
|
||||
}
|
||||
|
||||
func (t *dockerIntegration) List() ([]*types.Service, error) {
|
||||
var err error
|
||||
path := Value(t, "path").(string)
|
||||
version := Value(t, "version").(string)
|
||||
os.Setenv("DOCKER_HOST", path)
|
||||
os.Setenv("DOCKER_VERSION", version)
|
||||
cli, err = client.NewEnvClient()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer cli.Close()
|
||||
|
||||
var services []*types.Service
|
||||
|
||||
containers, err := cli.ContainerList(context.Background(), dTypes.ContainerListOptions{})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, container := range containers {
|
||||
if container.State != "running" {
|
||||
continue
|
||||
}
|
||||
|
||||
for _, v := range container.Ports {
|
||||
if v.IP == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
service := &types.Service{
|
||||
Name: container.Names[0][1:],
|
||||
Domain: v.IP,
|
||||
Type: v.Type,
|
||||
Port: int(v.PublicPort),
|
||||
Interval: 60,
|
||||
Timeout: 2,
|
||||
}
|
||||
|
||||
services = append(services, service)
|
||||
}
|
||||
|
||||
}
|
||||
return services, nil
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package integrations
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDockerIntegration(t *testing.T) {
|
||||
|
||||
t.Run("Docker Open containers", func(t *testing.T) {
|
||||
err := dockerIntegrator.Open()
|
||||
require.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("List Services from Docker", func(t *testing.T) {
|
||||
services, err := dockerIntegrator.List()
|
||||
require.Nil(t, err)
|
||||
assert.NotEqual(t, 0, len(services))
|
||||
})
|
||||
|
||||
t.Run("Confirm Services from Docker", func(t *testing.T) {
|
||||
services, err := dockerIntegrator.List()
|
||||
require.Nil(t, err)
|
||||
for _, s := range services {
|
||||
t.Log(s)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Close Docker", func(t *testing.T) {
|
||||
err := dockerIntegrator.Close()
|
||||
require.Nil(t, err)
|
||||
})
|
||||
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
// Statping
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statping
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package integrations
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
)
|
||||
|
||||
var (
|
||||
Integrations []types.Integrator
|
||||
log = utils.Log.WithField("type", "integration")
|
||||
)
|
||||
|
||||
func init() {
|
||||
Integrations = append(Integrations,
|
||||
csvIntegrator,
|
||||
dockerIntegrator,
|
||||
)
|
||||
}
|
||||
|
||||
func Value(intg types.Integrator, fieldName string) interface{} {
|
||||
for _, v := range intg.Get().Fields {
|
||||
if fieldName == v.Name {
|
||||
return v.Value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetFields(intg types.Integrator, data map[string][]string) (*types.Integration, error) {
|
||||
i := intg.Get()
|
||||
for _, v := range i.Fields {
|
||||
if data[v.Name] != nil {
|
||||
v.Value = data[v.Name][0]
|
||||
}
|
||||
}
|
||||
return i, nil
|
||||
}
|
||||
|
||||
func Find(name string) (types.Integrator, error) {
|
||||
for _, i := range Integrations {
|
||||
obj := i.Get()
|
||||
if obj.ShortName == name {
|
||||
return i, nil
|
||||
}
|
||||
}
|
||||
return nil, errors.New(name + " not found")
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
package integrations
|
||||
|
||||
import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIntegrations(t *testing.T) {
|
||||
|
||||
t.Run("Collect Integrations", func(t *testing.T) {
|
||||
amount := len(Integrations)
|
||||
assert.Equal(t, 2, amount)
|
||||
})
|
||||
|
||||
t.Run("Close All Integrations", func(t *testing.T) {
|
||||
closedAll := CloseAll()
|
||||
require.Nil(t, closedAll)
|
||||
})
|
||||
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
Bulk Upload,http://google.com,,200,60s,http,get,,,60s,1,TRUE,TRUE,,Authorization=example,bulk_example,false
|
|
|
@ -7,54 +7,66 @@
|
|||
/* Mobile Service Container */
|
||||
HTML, BODY {
|
||||
background-color: #fcfcfc;
|
||||
padding-bottom: 10px; }
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding-top: 20px;
|
||||
padding-bottom: 25px;
|
||||
max-width: 860px;
|
||||
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important; }
|
||||
box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15) !important;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
color: #464646; }
|
||||
color: #464646;
|
||||
}
|
||||
|
||||
.header-desc {
|
||||
color: #939393; }
|
||||
color: #939393;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border-radius: 0.2rem; }
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.online_list .badge {
|
||||
margin-top: 0.2rem; }
|
||||
margin-top: 0.2rem;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-bottom: 30px; }
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
line-height: 1.3;
|
||||
font-size: 0.75rem; }
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.view_service_btn {
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
right: 40px; }
|
||||
right: 40px;
|
||||
}
|
||||
|
||||
.service_lower_info {
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
left: 40px;
|
||||
color: #d1ffca;
|
||||
font-size: 0.85rem; }
|
||||
font-size: 0.85rem;
|
||||
}
|
||||
|
||||
.lg_number {
|
||||
font-size: 2.3rem;
|
||||
font-weight: bold;
|
||||
display: block;
|
||||
color: #4f4f4f; }
|
||||
color: #4f4f4f;
|
||||
}
|
||||
|
||||
.stats_area {
|
||||
text-align: center;
|
||||
color: #a5a5a5; }
|
||||
color: #a5a5a5;
|
||||
}
|
||||
|
||||
.lower_canvas {
|
||||
height: 3.4rem;
|
||||
|
@ -62,82 +74,101 @@ HTML, BODY {
|
|||
background-color: #48d338;
|
||||
padding: 15px 10px;
|
||||
margin-left: 0px !important;
|
||||
margin-right: 0px !important; }
|
||||
margin-right: 0px !important;
|
||||
}
|
||||
|
||||
.lower_canvas SPAN {
|
||||
font-size: 1rem;
|
||||
color: #fff; }
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.footer {
|
||||
text-decoration: none;
|
||||
margin-top: 20px; }
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.footer A {
|
||||
color: #8d8d8d;
|
||||
text-decoration: none; }
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.footer A:HOVER {
|
||||
color: #6d6d6d; }
|
||||
color: #6d6d6d;
|
||||
}
|
||||
|
||||
.badge {
|
||||
color: white;
|
||||
border-radius: 0.2rem; }
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
height: 25px; }
|
||||
.btn-group A {
|
||||
padding: 0.1rem .75rem;
|
||||
font-size: 0.8rem; }
|
||||
height: 25px;
|
||||
}
|
||||
.btn-group A {
|
||||
padding: 0.1rem 0.75rem;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.card-body .badge {
|
||||
color: #fff; }
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link {
|
||||
border-radius: 0.2rem; }
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.form-control {
|
||||
border-radius: 0.2rem; }
|
||||
border-radius: 0.2rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background-color: #ffffff;
|
||||
border: 1px solid rgba(0, 0, 0, 0.125); }
|
||||
border: 1px solid rgba(0, 0, 0, 0.125);
|
||||
}
|
||||
|
||||
.card-body {
|
||||
overflow: hidden; }
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-body H4 A {
|
||||
color: #444444;
|
||||
text-decoration: none; }
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
position: relative;
|
||||
height: 170px;
|
||||
width: 100%;
|
||||
overflow: hidden; }
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.service-chart-container {
|
||||
position: relative;
|
||||
height: 400px;
|
||||
width: 100%; }
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.service-chart-heatmap {
|
||||
position: relative;
|
||||
height: 300px;
|
||||
width: 100%; }
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inputTags-field {
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
padding-top: .13rem; }
|
||||
padding-top: 0.13rem;
|
||||
}
|
||||
|
||||
input.inputTags-field:focus {
|
||||
outline-width: 0; }
|
||||
outline-width: 0;
|
||||
}
|
||||
|
||||
.inputTags-list {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: calc(2.25rem + 2px);
|
||||
padding: .2rem .35rem;
|
||||
padding: 0.2rem 0.35rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
|
@ -145,8 +176,9 @@ input.inputTags-field:focus {
|
|||
background-color: #fff;
|
||||
background-clip: padding-box;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: .25rem;
|
||||
transition: border-color .15s ease-in-out,box-shadow .15s ease-in-out; }
|
||||
border-radius: 0.25rem;
|
||||
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.inputTags-item {
|
||||
background-color: #3aba39;
|
||||
|
@ -154,63 +186,81 @@ input.inputTags-field:focus {
|
|||
padding: 5px 8px;
|
||||
font-size: 10pt;
|
||||
color: white;
|
||||
border-radius: 4px; }
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.inputTags-item .close-item {
|
||||
margin-left: 6px;
|
||||
font-size: 13pt;
|
||||
font-weight: bold;
|
||||
cursor: pointer; }
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #3e9bff;
|
||||
border-color: #006fe6;
|
||||
color: white; }
|
||||
.btn-primary.dyn-dark {
|
||||
background-color: #32a825 !important;
|
||||
border-color: #2c9320 !important; }
|
||||
.btn-primary.dyn-light {
|
||||
background-color: #75de69 !important;
|
||||
border-color: #88e37e !important; }
|
||||
color: white;
|
||||
}
|
||||
.btn-primary.dyn-dark {
|
||||
background-color: #32a825 !important;
|
||||
border-color: #2c9320 !important;
|
||||
}
|
||||
.btn-primary.dyn-light {
|
||||
background-color: #75de69 !important;
|
||||
border-color: #88e37e !important;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background-color: #47d337; }
|
||||
.btn-success.dyn-dark {
|
||||
background-color: #32a825 !important;
|
||||
border-color: #2c9320 !important; }
|
||||
.btn-success.dyn-light {
|
||||
background-color: #75de69 !important;
|
||||
border-color: #88e37e !important; }
|
||||
background-color: #47d337;
|
||||
}
|
||||
.btn-success.dyn-dark {
|
||||
background-color: #32a825 !important;
|
||||
border-color: #2c9320 !important;
|
||||
}
|
||||
.btn-success.dyn-light {
|
||||
background-color: #75de69 !important;
|
||||
border-color: #88e37e !important;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background-color: #dd3545; }
|
||||
.btn-danger.dyn-dark {
|
||||
background-color: #b61f2d !important;
|
||||
border-color: #a01b28 !important; }
|
||||
.btn-danger.dyn-light {
|
||||
background-color: #e66975 !important;
|
||||
border-color: #e97f89 !important; }
|
||||
background-color: #dd3545;
|
||||
}
|
||||
.btn-danger.dyn-dark {
|
||||
background-color: #b61f2d !important;
|
||||
border-color: #a01b28 !important;
|
||||
}
|
||||
.btn-danger.dyn-light {
|
||||
background-color: #e66975 !important;
|
||||
border-color: #e97f89 !important;
|
||||
}
|
||||
|
||||
.bg-success {
|
||||
background-color: #47d337 !important; }
|
||||
background-color: #47d337 !important;
|
||||
}
|
||||
|
||||
.bg-danger {
|
||||
background-color: #dd3545 !important; }
|
||||
background-color: #dd3545 !important;
|
||||
}
|
||||
|
||||
.bg-success .dyn-dark {
|
||||
background-color: #35b027 !important; }
|
||||
background-color: #35b027 !important;
|
||||
}
|
||||
|
||||
.bg-danger .dyn-dark {
|
||||
background-color: #bf202f !important; }
|
||||
background-color: #bf202f !important;
|
||||
}
|
||||
|
||||
.nav-pills .nav-link.active, .nav-pills .show > .nav-link {
|
||||
background-color: #13a00d; }
|
||||
background-color: #13a00d;
|
||||
}
|
||||
|
||||
.nav-pills A {
|
||||
color: #424242; }
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
.nav-pills I {
|
||||
margin-right: 10px; }
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.CodeMirror {
|
||||
/* Bootstrap Settings */
|
||||
|
@ -230,23 +280,26 @@ input.inputTags-field:focus {
|
|||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
|
||||
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||
/* Code Mirror Settings */
|
||||
font-family: monospace;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
height: 80vh; }
|
||||
height: 80vh;
|
||||
}
|
||||
|
||||
.CodeMirror-focused {
|
||||
/* Bootstrap Settings */
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||
transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s; }
|
||||
transition: border-color ease-in-out 0.15s, box-shadow ease-in-out 0.15s;
|
||||
}
|
||||
|
||||
.switch {
|
||||
font-size: 1rem;
|
||||
position: relative; }
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.switch input {
|
||||
position: absolute;
|
||||
|
@ -257,7 +310,8 @@ input.inputTags-field:focus {
|
|||
clip: rect(0 0 0 0);
|
||||
clip-path: inset(50%);
|
||||
overflow: hidden;
|
||||
padding: 0; }
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.switch input + label {
|
||||
position: relative;
|
||||
|
@ -270,23 +324,26 @@ input.inputTags-field:focus {
|
|||
outline: none;
|
||||
user-select: none;
|
||||
vertical-align: middle;
|
||||
text-indent: calc(calc(calc(2.375rem * .8) * 2) + .5rem); }
|
||||
text-indent: calc(calc(calc(2.375rem * .8) * 2) + .5rem);
|
||||
}
|
||||
|
||||
.switch input + label::before,
|
||||
.switch input + label::after {
|
||||
content: '';
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: calc(calc(2.375rem * .8) * 2);
|
||||
bottom: 0;
|
||||
display: block; }
|
||||
display: block;
|
||||
}
|
||||
|
||||
.switch input + label::before {
|
||||
right: 0;
|
||||
background-color: #dee2e6;
|
||||
border-radius: calc(2.375rem * .8);
|
||||
transition: 0.2s all; }
|
||||
transition: 0.2s all;
|
||||
}
|
||||
|
||||
.switch input + label::after {
|
||||
top: 2px;
|
||||
|
@ -295,105 +352,137 @@ input.inputTags-field:focus {
|
|||
height: calc(calc(2.375rem * .8) - calc(2px * 2));
|
||||
border-radius: 50%;
|
||||
background-color: white;
|
||||
transition: 0.2s all; }
|
||||
transition: 0.2s all;
|
||||
}
|
||||
|
||||
.switch input:checked + label::before {
|
||||
background-color: #08d; }
|
||||
background-color: #08d;
|
||||
}
|
||||
|
||||
.switch input:checked + label::after {
|
||||
margin-left: calc(2.375rem * .8); }
|
||||
margin-left: calc(2.375rem * .8);
|
||||
}
|
||||
|
||||
.switch input:focus + label::before {
|
||||
outline: none;
|
||||
box-shadow: 0 0 0 0.2rem rgba(0, 136, 221, 0.25); }
|
||||
box-shadow: 0 0 0 0.2rem rgba(0, 136, 221, 0.25);
|
||||
}
|
||||
|
||||
.switch input:disabled + label {
|
||||
color: #868e96;
|
||||
cursor: not-allowed; }
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.switch input:disabled + label::before {
|
||||
background-color: #e9ecef; }
|
||||
background-color: #e9ecef;
|
||||
}
|
||||
|
||||
.switch.switch-sm {
|
||||
font-size: 0.875rem; }
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.switch.switch-sm input + label {
|
||||
min-width: calc(calc(1.9375rem * .8) * 2);
|
||||
height: calc(1.9375rem * .8);
|
||||
line-height: calc(1.9375rem * .8);
|
||||
text-indent: calc(calc(calc(1.9375rem * .8) * 2) + .5rem); }
|
||||
text-indent: calc(calc(calc(1.9375rem * .8) * 2) + .5rem);
|
||||
}
|
||||
|
||||
.switch.switch-sm input + label::before {
|
||||
width: calc(calc(1.9375rem * .8) * 2); }
|
||||
width: calc(calc(1.9375rem * .8) * 2);
|
||||
}
|
||||
|
||||
.switch.switch-sm input + label::after {
|
||||
width: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(1.9375rem * .8) - calc(2px * 2)); }
|
||||
height: calc(calc(1.9375rem * .8) - calc(2px * 2));
|
||||
}
|
||||
|
||||
.switch.switch-sm input:checked + label::after {
|
||||
margin-left: calc(1.9375rem * .8); }
|
||||
margin-left: calc(1.9375rem * .8);
|
||||
}
|
||||
|
||||
.switch.switch-lg {
|
||||
font-size: 1.25rem; }
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.switch.switch-lg input + label {
|
||||
min-width: calc(calc(3rem * .8) * 2);
|
||||
height: calc(3rem * .8);
|
||||
line-height: calc(3rem * .8);
|
||||
text-indent: calc(calc(calc(3rem * .8) * 2) + .5rem); }
|
||||
text-indent: calc(calc(calc(3rem * .8) * 2) + .5rem);
|
||||
}
|
||||
|
||||
.switch.switch-lg input + label::before {
|
||||
width: calc(calc(3rem * .8) * 2); }
|
||||
width: calc(calc(3rem * .8) * 2);
|
||||
}
|
||||
|
||||
.switch.switch-lg input + label::after {
|
||||
width: calc(calc(3rem * .8) - calc(2px * 2));
|
||||
height: calc(calc(3rem * .8) - calc(2px * 2)); }
|
||||
height: calc(calc(3rem * .8) - calc(2px * 2));
|
||||
}
|
||||
|
||||
.switch.switch-lg input:checked + label::after {
|
||||
margin-left: calc(3rem * .8); }
|
||||
margin-left: calc(3rem * .8);
|
||||
}
|
||||
|
||||
.switch + .switch {
|
||||
margin-left: 1rem; }
|
||||
margin-left: 1rem;
|
||||
}
|
||||
|
||||
@keyframes pulse_animation {
|
||||
0% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
30% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
40% {
|
||||
transform: scale(1.02); }
|
||||
transform: scale(1.02);
|
||||
}
|
||||
50% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
60% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
70% {
|
||||
transform: scale(1.05); }
|
||||
transform: scale(1.05);
|
||||
}
|
||||
80% {
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1); } }
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
.pulse {
|
||||
animation-name: pulse_animation;
|
||||
animation-duration: 1500ms;
|
||||
transform-origin: 70% 70%;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear; }
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
@keyframes glow-grow {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(1); }
|
||||
transform: scale(1);
|
||||
}
|
||||
80% {
|
||||
opacity: 1; }
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(2);
|
||||
opacity: 0; } }
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.pulse-glow {
|
||||
animation-name: glow-grown;
|
||||
animation-duration: 100ms;
|
||||
transform-origin: 70% 30%;
|
||||
animation-iteration-count: infinite;
|
||||
animation-timing-function: linear; }
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
|
||||
.pulse-glow:before,
|
||||
.pulse-glow:after {
|
||||
|
@ -405,10 +494,12 @@ input.inputTags-field:focus {
|
|||
right: 2.15rem;
|
||||
border-radius: 0;
|
||||
box-shadow: 0 0 6px #47d337;
|
||||
animation: glow-grow 2s ease-out infinite; }
|
||||
animation: glow-grow 2s ease-out infinite;
|
||||
}
|
||||
|
||||
.sortable_drag {
|
||||
background-color: #0000000f; }
|
||||
background-color: #0000000f;
|
||||
}
|
||||
|
||||
.drag_icon {
|
||||
cursor: move;
|
||||
|
@ -422,112 +513,139 @@ input.inputTags-field:focus {
|
|||
margin-right: 5px;
|
||||
margin-left: -10px;
|
||||
text-align: center;
|
||||
color: #b1b1b1; }
|
||||
color: #b1b1b1;
|
||||
}
|
||||
|
||||
/* (Optional) Apply a "closed-hand" cursor during drag operation. */
|
||||
.drag_icon:active {
|
||||
cursor: grabbing;
|
||||
cursor: -moz-grabbing;
|
||||
cursor: -webkit-grabbing; }
|
||||
cursor: -webkit-grabbing;
|
||||
}
|
||||
|
||||
.switch_btn {
|
||||
float: right;
|
||||
margin: -1px 0px 0px 0px;
|
||||
display: block; }
|
||||
display: block;
|
||||
}
|
||||
|
||||
#start_container {
|
||||
position: absolute;
|
||||
z-index: 99999;
|
||||
margin-top: 20px; }
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
#end_container {
|
||||
position: absolute;
|
||||
z-index: 99999;
|
||||
margin-top: 20px;
|
||||
right: 0; }
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer; }
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.jumbotron {
|
||||
background-color: white; }
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
.toggle-service {
|
||||
font-size: 18pt;
|
||||
float: left;
|
||||
margin: 2px 3px 0 0;
|
||||
cursor: pointer; }
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@media (max-width: 767px) {
|
||||
HTML, BODY {
|
||||
background-color: #fcfcfc; }
|
||||
background-color: #fcfcfc;
|
||||
}
|
||||
|
||||
.sm-container {
|
||||
margin-top: 0px !important;
|
||||
padding: 0 !important; }
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.list-group-item H5 {
|
||||
font-size: 0.9rem; }
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0px !important;
|
||||
padding-top: 15px !important; }
|
||||
padding-top: 15px !important;
|
||||
}
|
||||
|
||||
.group_header {
|
||||
margin-left: 15px; }
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.navbar {
|
||||
margin-left: 0px;
|
||||
margin-top: 0px;
|
||||
width: 100%;
|
||||
margin-bottom: 0; }
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.btn-sm {
|
||||
line-height: 0.9rem;
|
||||
font-size: 0.65rem; }
|
||||
font-size: 0.65rem;
|
||||
}
|
||||
|
||||
.full-col-12 {
|
||||
padding-left: 0px;
|
||||
padding-right: 0px; }
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
.card {
|
||||
border: 0;
|
||||
border-radius: 0rem;
|
||||
padding: 0;
|
||||
background-color: #ffffff; }
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
font-size: 10pt;
|
||||
padding: 10px 10px; }
|
||||
padding: 10px 10px;
|
||||
}
|
||||
|
||||
.lg_number {
|
||||
font-size: 7.8vw; }
|
||||
font-size: 7.8vw;
|
||||
}
|
||||
|
||||
.stats_area {
|
||||
margin-top: 1.5rem !important;
|
||||
margin-bottom: 1.5rem !important; }
|
||||
margin-bottom: 1.5rem !important;
|
||||
}
|
||||
|
||||
.stats_area .col-4 {
|
||||
padding-left: 0;
|
||||
padding-right: 0;
|
||||
font-size: 0.6rem; }
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
|
||||
.list-group-item {
|
||||
border-top: 1px solid #e4e4e4;
|
||||
border: 0px; }
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
.list-group-item:first-child {
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0; }
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-item:last-child {
|
||||
border-bottom-right-radius: 0;
|
||||
border-bottom-left-radius: 0; }
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.list-group-item P {
|
||||
font-size: 0.7rem; }
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
.service-chart-container {
|
||||
height: 200px; } }
|
||||
height: 200px;
|
||||
}
|
||||
}
|
||||
|
||||
/*# sourceMappingURL=base.css.map */
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
<div class="card">
|
||||
<div class="card-body">
|
||||
{{$message := .}}
|
||||
{{if ne .Id 0}}
|
||||
<form class="ajax_form" action="/api/groups/{{.Id}}" data-redirect="/services" method="POST">
|
||||
{{else}}
|
||||
<form class="ajax_form" action="/api/groups" data-redirect="/services" method="POST">
|
||||
{{end}}
|
||||
<form class="ajax_form" action="/api/groups{{if ne .Id 0}}/{{.Id}}{{end}}" data-redirect="/services" method="POST">
|
||||
<div class="form-group row">
|
||||
<label for="username" class="col-sm-4 col-form-label">Group Name</label>
|
||||
<div class="col-sm-8">
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
<div class="card">
|
||||
<div class="card-body">
|
||||
{{$message := .}}
|
||||
{{if ne .Id 0}}
|
||||
<form class="ajax_form" action="/api/messages/{{.Id}}" data-redirect="/messages" method="POST">
|
||||
{{else}}
|
||||
<form class="ajax_form" action="/api/messages" data-redirect="/messages" method="POST">
|
||||
{{end}}
|
||||
<form class="ajax_form" action="/api/messages{{if ne .Id 0}}/{{.Id}}{{end}}" data-redirect="/messages" method="POST">
|
||||
<div class="form-group row">
|
||||
<label for="username" class="col-sm-4 col-form-label">Title</label>
|
||||
<div class="col-sm-8">
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
{{define "form_integration"}}
|
||||
{{$i := .Get}}
|
||||
<form class="integration_{{underscore $i.ShortName }}" action="/settings/integrator/{{ $i.ShortName }}" method="POST">
|
||||
<input type="hidden" name="integrator" class="form-control" value="{{ $i.ShortName }}">
|
||||
{{if $i.ShortName}}<h4 class="text-capitalize">{{$i.ShortName}}</h4>{{end}}
|
||||
{{if $i.Description}}<p class="small text-muted">{{safe $i.Description}}</p>{{end}}
|
||||
|
||||
{{range $i.Fields}}
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize" for="{{underscore .Name}}">{{.Name}}</label>
|
||||
{{if eq .Type "textarea"}}
|
||||
<textarea rows="3" class="form-control" name="{{underscore .Name}}" id="{{underscore .Name}}">{{ .Value }}</textarea>
|
||||
{{else if eq .Type "text"}}
|
||||
<input type="text" name="{{underscore .Name}}" class="form-control" value="{{ .Value }}" id="{{underscore .Name}}">
|
||||
{{else if eq .Type "integer"}}
|
||||
<input type="number" name="{{underscore .Name}}" class="form-control" value="{{ .Value }}" id="{{underscore .Name}}">
|
||||
{{else if eq .Type "file"}}
|
||||
<input type="file" name="{{underscore .Name}}" class="form-control" value="{{ .Value }}" id="{{underscore .Name}}">
|
||||
{{end}}
|
||||
{{if .Description}}
|
||||
<small class="form-text text-muted">{{safe .Description}}</small>
|
||||
{{end}}
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
<button type="submit" class="btn btn-block btn-info fetch_integrator">Fetch Services</button>
|
||||
|
||||
<div class="alert alert-danger d-none" id="integration_alerter" role="alert"></div>
|
||||
</form>
|
||||
{{end}}
|
|
@ -2,11 +2,7 @@
|
|||
<div class="card">
|
||||
<div class="card-body">
|
||||
{{$message := .}}
|
||||
{{if ne .Id 0}}
|
||||
<form class="ajax_form" action="/api/messages/{{.Id}}" data-redirect="/messages" method="POST">
|
||||
{{else}}
|
||||
<form class="ajax_form" action="/api/messages" data-redirect="/messages" method="POST">
|
||||
{{end}}
|
||||
<form class="ajax_form" action="/api/messages{{if ne .Id 0}}/{{.Id}}{{end}}" data-redirect="/messages" method="POST">
|
||||
<div class="form-group row">
|
||||
<label for="username" class="col-sm-4 col-form-label">Title</label>
|
||||
<div class="col-sm-8">
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
<div class="card">
|
||||
<div class="card-body">
|
||||
{{$s := .}}
|
||||
{{if ne .Id 0}}
|
||||
<form class="ajax_form" action="/api/services/{{.Id}}" data-redirect="/services" method="POST">
|
||||
{{else}}
|
||||
<form class="ajax_form" action="/api/services" data-redirect="/services" method="POST">
|
||||
{{end}}
|
||||
<form class="ajax_form" action="/api/services{{if ne .Id 0}}/{{.Id}}{{end}}" data-redirect="/services" method="POST">
|
||||
<h4 class="mb-5 text-muted">Basic Information</h4>
|
||||
<div class="form-group row">
|
||||
<label for="service_name" class="col-sm-4 col-form-label">Service Name</label>
|
||||
|
|
|
@ -1,11 +1,7 @@
|
|||
{{define "form_user"}}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
{{if ne .Id 0}}
|
||||
<form class="ajax_form" action="/api/users/{{.Id}}" data-redirect="/users" method="POST">
|
||||
{{else}}
|
||||
<form class="ajax_form" action="/api/users" data-redirect="/users" method="POST">
|
||||
{{end}}
|
||||
<form class="ajax_form" action="/api/users{{if ne .Id 0}}/{{.Id}}{{end}}" data-redirect="/users" method="POST">
|
||||
<div class="form-group row">
|
||||
<label for="username" class="col-sm-4 col-form-label">Username</label>
|
||||
<div class="col-6 col-md-4">
|
||||
|
@ -46,4 +42,4 @@
|
|||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{end}}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
{{define "title"}}Statping | {{.Integrator.Name}} Integration{{end}}
|
||||
{{define "content"}}
|
||||
<div class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
||||
{{if Auth}}
|
||||
{{template "nav"}}
|
||||
{{end}}
|
||||
{{$i := .Integrator}}
|
||||
<div class="col-12">
|
||||
<h3 class="mb-2 text-muted">{{$i.Name}} Integration</h3>
|
||||
<p>{{safe $i.Description}}</p>
|
||||
{{if .Error}}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{{.Error}}
|
||||
</div>
|
||||
{{else}}
|
||||
<table id="integrator_table" class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col"><input name="all" type="checkbox" checked></th></th>
|
||||
<th scope="col">Service Name</th>
|
||||
<th scope="col">Endpoint</th>
|
||||
<th scope="col">Port</th>
|
||||
<th scope="col">Type</th>
|
||||
<th scope="col">Interval</th>
|
||||
<th scope="col">Timeout</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="integrator_services">
|
||||
{{range .Services}}
|
||||
<tr id="{{underscore .Name}}">
|
||||
<th scope="row"><input name="add" type="checkbox" checked></th>
|
||||
<th><div class="input-group-sm"><input type="text" class="form-control" name="name" value="{{.Name}}"></div></th>
|
||||
<th><div style="width: 80pt;" class="input-group-sm"><input type="text" class="form-control" name="domain" value="{{.Domain}}"></div></th>
|
||||
<th><div style="width: 55pt;" class="input-group-sm"><input type="number" class="form-control" name="port" value="{{.Port}}"></div></th>
|
||||
<th><div style="width: 32pt;" class="input-group-sm"><input type="text" class="form-control" name="type" value="{{.Type}}"></div></th>
|
||||
<th><div style="width: 40pt;" class="input-group-sm"><input type="text" class="form-control" name="check_interval" value="{{.Interval}}"></div></th>
|
||||
<th><div style="width: 40pt;" class="input-group-sm"><input type="text" class="form-control" name="timeout" value="{{.Timeout}}"></div></th>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<div id="imported_area"></div>
|
||||
|
||||
<button class="btn btn-block btn-primary add_integration_services mb-5" data-id="{{.Integrator.ShortName}}">Add Services</button>
|
||||
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
{{define "extra_scripts"}}
|
||||
<script>
|
||||
$('.add_integration_services').on('click', function(e) {
|
||||
var table = $(`#integrator_services`);
|
||||
|
||||
table.find('tr').each(function() {
|
||||
var t = $(this).find('input');
|
||||
var eachService = t.serializeArray();
|
||||
let newArr = {};
|
||||
|
||||
var add = false;
|
||||
|
||||
eachService.forEach(function(k, v) {
|
||||
if (k.value === "on" && k.name === "add") {
|
||||
add = true
|
||||
}
|
||||
if($.isNumeric(k.value)){
|
||||
k.value = parseInt(k.value)
|
||||
}
|
||||
if (add && k.name !== "add") {
|
||||
newArr[k.name] = k.value;
|
||||
}
|
||||
});
|
||||
|
||||
let sendData = JSON.stringify(newArr);
|
||||
$.ajax({
|
||||
url: "/api/services",
|
||||
type: "POST",
|
||||
data: sendData,
|
||||
success: function (data) {
|
||||
var box = `<div class="alert alert-success" role="alert">Service '${data.output.name}' Added <a href="/service/${data.output.id}" class="badge badge-secondary mt-1 float-right">View</a></div>`;
|
||||
$("#imported_area").append(box);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
</script>
|
||||
{{end}}
|
|
@ -21,6 +21,12 @@
|
|||
{{ range .Plugins }}
|
||||
<a class="nav-link text-capitalize" id="v-pills-{{underscore .Name}}-tab" data-toggle="pill" href="#v-pills-{{underscore .Name}}" role="tab" aria-controls="v-pills-profile" aria-selected="false">{{.Name}}</a>
|
||||
{{end}}
|
||||
|
||||
<h6 class="mt-4 text-muted">Integrations</h6>
|
||||
{{ range .Integrations }}
|
||||
{{$i := .Get}}
|
||||
<a class="nav-link text-capitalize" id="v-pills-integration-{{underscore $i.ShortName}}-tab" data-toggle="pill" href="#v-pills-integration-{{underscore $i.ShortName}}" role="tab" aria-controls="v-pills-integration-{{underscore $i.ShortName}}" aria-selected="false">{{$i.Name}}</a>
|
||||
{{end}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-9 col-sm-12">
|
||||
|
@ -261,6 +267,15 @@
|
|||
</div>
|
||||
{{ end }}
|
||||
|
||||
|
||||
{{ range .Integrations }}
|
||||
{{$i := .Get}}
|
||||
<div class="tab-pane fade" id="v-pills-integration-{{underscore $i.ShortName}}" role="tabpanel" aria-labelledby="v-pills-integration-{{underscore $i.ShortName}}-tab">
|
||||
{{template "form_integration" .}}
|
||||
</div>
|
||||
{{ end }}
|
||||
|
||||
|
||||
<div class="tab-pane fade" id="v-pills-browse" role="tabpanel" aria-labelledby="v-pills-browse-tab">
|
||||
{{ range .Repos }}
|
||||
<div class="card col-6" style="width: 18rem;">
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -51,4 +51,5 @@ type Core struct {
|
|||
AllPlugins []PluginActions `gorm:"-" json:"-"`
|
||||
Notifications []AllNotifiers `gorm:"-" json:"-"`
|
||||
Config *DbConfig `gorm:"-" json:"config"`
|
||||
Integrations []Integrator `gorm:"-" json:"-"`
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
package types
|
||||
|
||||
type Integration struct {
|
||||
ShortName string `json:"name"`
|
||||
Name string `json:"full_name"`
|
||||
Description string `json:"description"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Fields []*IntegrationField `json:"fields"`
|
||||
}
|
||||
|
||||
type IntegrationField struct {
|
||||
Name string `json:"name"`
|
||||
Value interface{} `json:"value"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description,omitempty"`
|
||||
MimeType string `json:"mime_type,omitempty"`
|
||||
}
|
||||
|
||||
type Integrator interface {
|
||||
Get() *Integration
|
||||
List() ([]*Service, error)
|
||||
}
|
Loading…
Reference in New Issue