mirror of https://github.com/statping/statping
updates
parent
b8dbad85fe
commit
7762ca40d7
|
@ -280,7 +280,6 @@ func recordFailure(s *Service, issue string) {
|
||||||
s.CreateFailure(fail)
|
s.CreateFailure(fail)
|
||||||
s.Online = false
|
s.Online = false
|
||||||
s.SuccessNotified = false
|
s.SuccessNotified = false
|
||||||
s.UpdateNotify = CoreApp.UpdateNotify.Bool
|
|
||||||
s.DownText = s.DowntimeText()
|
s.DownText = s.DowntimeText()
|
||||||
notifier.OnFailure(s.Service, fail)
|
notifier.OnFailure(s.Service, fail)
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@ func OnFailure(s *types.Service, f *types.Failure) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if User wants to receive every Status Change
|
// check if User wants to receive every Status Change
|
||||||
if s.UpdateNotify {
|
if s.UpdateNotify.Bool {
|
||||||
// send only if User hasn't been already notified about the Downtime
|
// send only if User hasn't been already notified about the Downtime
|
||||||
if !s.UserNotified {
|
if !s.UserNotified {
|
||||||
s.UserNotified = true
|
s.UserNotified = true
|
||||||
|
@ -69,7 +69,7 @@ func OnSuccess(s *types.Service) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if User wants to receive every Status Change
|
// check if User wants to receive every Status Change
|
||||||
if s.UpdateNotify && s.UserNotified {
|
if s.UpdateNotify.Bool && s.UserNotified {
|
||||||
s.UserNotified = false
|
s.UserNotified = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,12 +10,14 @@ const environment = require('./dev.env');
|
||||||
|
|
||||||
const webpackConfig = merge(commonConfig, {
|
const webpackConfig = merge(commonConfig, {
|
||||||
mode: 'development',
|
mode: 'development',
|
||||||
devtool: 'cheap-module-eval-source-map',
|
devtool: 'inline-cheap-module-source-map',
|
||||||
output: {
|
output: {
|
||||||
path: helpers.root('dist'),
|
path: helpers.root('dist'),
|
||||||
publicPath: '/',
|
publicPath: '/',
|
||||||
filename: 'js/[name].bundle.js',
|
filename: 'js/[name].bundle.js',
|
||||||
chunkFilename: 'js/[name].chunk.js'
|
chunkFilename: 'js/[name].chunk.js',
|
||||||
|
devtoolModuleFilenameTemplate: '[absolute-resource-path]',
|
||||||
|
devtoolFallbackModuleFilenameTemplate: '[absolute-resource-path]?[hash]'
|
||||||
},
|
},
|
||||||
optimization: {
|
optimization: {
|
||||||
runtimeChunk: 'single',
|
runtimeChunk: 'single',
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
"serve": "vue-cli-service serve",
|
"serve": "vue-cli-service serve",
|
||||||
"build": "rm -rf dist && cross-env NODE_ENV=production webpack --mode production",
|
"build": "rm -rf dist && cross-env NODE_ENV=production webpack --mode production",
|
||||||
"dev": "cross-env NODE_ENV=development webpack-dev-server --host 0.0.0.0 --port 8888 --progress",
|
"dev": "cross-env NODE_ENV=development webpack-dev-server --host 0.0.0.0 --port 8888 --progress",
|
||||||
"lint": "vue-cli-service lint"
|
"lint": "vue-cli-service lint",
|
||||||
|
"test": "cross-env NODE_ENV=development mochapack --webpack-config webpack.config.js --require test/setup.js test/**/*.spec.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-free-solid": "^5.1.0-3",
|
"@fortawesome/fontawesome-free-solid": "^5.1.0-3",
|
||||||
|
@ -18,7 +19,7 @@
|
||||||
"axios": "^0.19.1",
|
"axios": "^0.19.1",
|
||||||
"codemirror-colorpicker": "^1.9.66",
|
"codemirror-colorpicker": "^1.9.66",
|
||||||
"core-js": "^3.4.4",
|
"core-js": "^3.4.4",
|
||||||
"moment": "^2.24.0",
|
"date-fns": "^2.9.0",
|
||||||
"querystring": "^0.2.0",
|
"querystring": "^0.2.0",
|
||||||
"vue": "^2.6.10",
|
"vue": "^2.6.10",
|
||||||
"vue-apexcharts": "^1.5.2",
|
"vue-apexcharts": "^1.5.2",
|
||||||
|
@ -40,6 +41,7 @@
|
||||||
"@babel/preset-env": "~7.8.3",
|
"@babel/preset-env": "~7.8.3",
|
||||||
"@vue/babel-preset-app": "^4.1.2",
|
"@vue/babel-preset-app": "^4.1.2",
|
||||||
"@vue/cli-plugin-babel": "^4.1.0",
|
"@vue/cli-plugin-babel": "^4.1.0",
|
||||||
|
"@vue/test-utils": "^1.0.0-beta.31",
|
||||||
"babel-eslint": "~10.0",
|
"babel-eslint": "~10.0",
|
||||||
"babel-loader": "~8.0",
|
"babel-loader": "~8.0",
|
||||||
"compression-webpack-plugin": "~2.0",
|
"compression-webpack-plugin": "~2.0",
|
||||||
|
@ -55,10 +57,15 @@
|
||||||
"eslint-plugin-promise": "~3.5",
|
"eslint-plugin-promise": "~3.5",
|
||||||
"eslint-plugin-standard": "~3.0",
|
"eslint-plugin-standard": "~3.0",
|
||||||
"eslint-plugin-vue": "~5.1",
|
"eslint-plugin-vue": "~5.1",
|
||||||
|
"expect": "^25.1.0",
|
||||||
"file-loader": "^5.0.2",
|
"file-loader": "^5.0.2",
|
||||||
"friendly-errors-webpack-plugin": "~1.7",
|
"friendly-errors-webpack-plugin": "~1.7",
|
||||||
"html-webpack-plugin": "^4.0.0-beta.11",
|
"html-webpack-plugin": "^4.0.0-beta.11",
|
||||||
|
"jsdom": "^16.2.0",
|
||||||
|
"jsdom-global": "^3.0.2",
|
||||||
"mini-css-extract-plugin": "~0.5",
|
"mini-css-extract-plugin": "~0.5",
|
||||||
|
"mocha": "^7.0.1",
|
||||||
|
"mochapack": "^1.1.13",
|
||||||
"node-sass": "^4.13.1",
|
"node-sass": "^4.13.1",
|
||||||
"optimize-css-assets-webpack-plugin": "~5.0",
|
"optimize-css-assets-webpack-plugin": "~5.0",
|
||||||
"sass-loader": "^8.0.2",
|
"sass-loader": "^8.0.2",
|
||||||
|
|
|
@ -10,11 +10,20 @@
|
||||||
<base href="{{BasePath}}">
|
<base href="{{BasePath}}">
|
||||||
{{if USE_CDN}}
|
{{if USE_CDN}}
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="https://assets.statping.com/favicon.ico">
|
<link rel="shortcut icon" type="image/x-icon" href="https://assets.statping.com/favicon.ico">
|
||||||
|
<link rel="stylesheet" href="https://assets.statping.com/css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="https://assets.statping.com/css/base.css">
|
||||||
|
<link rel="stylesheet" href="https://assets.statping.com/font/all.css">
|
||||||
{{else}}
|
{{else}}
|
||||||
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
|
<link rel="shortcut icon" type="image/x-icon" href="favicon.ico">
|
||||||
|
{{if USING_ASSETS}}
|
||||||
|
<link rel="stylesheet" href="css/bootstrap.min.css">
|
||||||
|
<link rel="stylesheet" href="css/base.css">
|
||||||
|
<link rel="stylesheet" href="font/all.css">
|
||||||
|
{{else}}
|
||||||
<% _.each(htmlWebpackPlugin.tags.headTags, function(headTag) { %>
|
<% _.each(htmlWebpackPlugin.tags.headTags, function(headTag) { %>
|
||||||
<%= headTag %> <% }) %>
|
<%= headTag %> <% }) %>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{end}}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<noscript>
|
<noscript>
|
||||||
|
|
|
@ -0,0 +1,226 @@
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
const qs = require('querystring')
|
||||||
|
const tokenKey = "statping_user";
|
||||||
|
|
||||||
|
class Api {
|
||||||
|
constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async core() {
|
||||||
|
return axios.get('/api').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async core_save(obj) {
|
||||||
|
return axios.post('/api/core', obj).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async setup_save(data) {
|
||||||
|
return axios.post('/api/setup', qs.stringify(data)).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async services() {
|
||||||
|
return axios.get('/api/services').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async service(id) {
|
||||||
|
return axios.get('/api/services/' + id).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_create(data) {
|
||||||
|
return axios.post('/api/services', data).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_update(data) {
|
||||||
|
return axios.post('/api/services/' + data.id, data).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_hits(id, start, end, group) {
|
||||||
|
return axios.get('/api/services/' + id + '/data?start=' + start + '&end=' + end + '&group=' + group).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_heatmap(id, start, end, group) {
|
||||||
|
return axios.get('/api/services/' + id + '/heatmap').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_failures(id, start, end, limit = 999, offset = 0) {
|
||||||
|
return axios.get('/api/services/' + id + '/failures?start=' + start + '&end=' + end + '&limit=' + limit).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async service_delete(id) {
|
||||||
|
return axios.delete('/api/services/' + id).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async services_reorder(data) {
|
||||||
|
return axios.post('/api/reorder/services', data).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async groups() {
|
||||||
|
return axios.get('/api/groups').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async groups_reorder(data) {
|
||||||
|
return axios.post('/api/reorder/groups', data).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async group_delete(id) {
|
||||||
|
return axios.delete('/api/groups/' + id).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async group_create(data) {
|
||||||
|
return axios.post('/api/groups', data).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async group_update(data) {
|
||||||
|
return axios.post('/api/groups/' + data.id, data).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async users() {
|
||||||
|
return axios.get('/api/users').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async user_create(data) {
|
||||||
|
return axios.post('/api/users', data).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async user_update(data) {
|
||||||
|
return axios.post('/api/users/' + data.id, data).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async user_delete(id) {
|
||||||
|
return axios.delete('/api/users/' + id).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async messages() {
|
||||||
|
return axios.get('/api/messages').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async message_create(data) {
|
||||||
|
return axios.post('/api/messages', data).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async message_update(data) {
|
||||||
|
return axios.post('/api/messages/' + data.id, data).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async message_delete(id) {
|
||||||
|
return axios.delete('/api/messages/' + id).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async group(id) {
|
||||||
|
return axios.get('/api/groups/' + id).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async notifiers() {
|
||||||
|
return axios.get('/api/notifiers').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async notifier_save(data) {
|
||||||
|
return axios.post('/api/notifier/' + data.method, data).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async notifier_test(data) {
|
||||||
|
return axios.post('/api/notifier/' + data.method + '/test', data).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async integrations() {
|
||||||
|
return axios.get('/api/integrations').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async integration(name) {
|
||||||
|
return axios.get('/api/integrations/' + name).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async integration_save(data) {
|
||||||
|
return axios.post('/api/integrations/' + data.name, data).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async renewApiKeys() {
|
||||||
|
return axios.get('/api/renew').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async cache() {
|
||||||
|
return axios.get('/api/cache').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async clearCache() {
|
||||||
|
return axios.get('/api/clear_cache').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async logs() {
|
||||||
|
return axios.get('/api/logs').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async logs_last() {
|
||||||
|
return axios.get('/api/logs/last').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async theme() {
|
||||||
|
return axios.get('/api/theme').then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async theme_generate(create = true) {
|
||||||
|
if (create) {
|
||||||
|
return axios.get('/api/theme/create').then(response => (response.data))
|
||||||
|
} else {
|
||||||
|
return axios.delete('/api/theme').then(response => (response.data))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async theme_save(data) {
|
||||||
|
return axios.post('/api/theme', data).then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async login(username, password) {
|
||||||
|
const f = {username: username, password: password}
|
||||||
|
return axios.post('/api/login', qs.stringify(f))
|
||||||
|
.then(response => (response.data))
|
||||||
|
}
|
||||||
|
|
||||||
|
async logout() {
|
||||||
|
await axios.get('/api/logout').then(response => (response.data))
|
||||||
|
return localStorage.removeItem(tokenKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
saveToken(username, token) {
|
||||||
|
const user = {username: username, token: token}
|
||||||
|
localStorage.setItem(tokenKey, JSON.stringify(user));
|
||||||
|
return user
|
||||||
|
}
|
||||||
|
|
||||||
|
async scss_base() {
|
||||||
|
return await axios({
|
||||||
|
url: '/scss/base.scss',
|
||||||
|
method: 'GET',
|
||||||
|
responseType: 'blob'
|
||||||
|
}).then((response) => {
|
||||||
|
const reader = new window.FileReader();
|
||||||
|
return reader.readAsText(response.data)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
token() {
|
||||||
|
const tk = localStorage.getItem(tokenKey)
|
||||||
|
if (!tk) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return JSON.parse(tk);
|
||||||
|
}
|
||||||
|
|
||||||
|
authToken() {
|
||||||
|
let user = JSON.parse(localStorage.getItem(tokenKey));
|
||||||
|
if (user && user.token) {
|
||||||
|
return {'Authorization': 'Bearer ' + user.token};
|
||||||
|
} else {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async allActions(...all) {
|
||||||
|
await axios.all([all])
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
const api = new Api()
|
||||||
|
export default api
|
|
@ -6,7 +6,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from './components/API';
|
import Api from './API';
|
||||||
import Footer from "./components/Index/Footer";
|
import Footer from "./components/Index/Footer";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -1,226 +0,0 @@
|
||||||
import axios from 'axios'
|
|
||||||
|
|
||||||
const qs = require('querystring')
|
|
||||||
const tokenKey = "statping_user";
|
|
||||||
|
|
||||||
class Api {
|
|
||||||
constructor() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
async core () {
|
|
||||||
return axios.get('/api').then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async core_save (obj) {
|
|
||||||
return axios.post('/api/core', obj).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async setup_save (data) {
|
|
||||||
return axios.post('/api/setup', qs.stringify(data)).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async services () {
|
|
||||||
return axios.get('/api/services').then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async service (id) {
|
|
||||||
return axios.get('/api/services/'+id).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async service_create (data) {
|
|
||||||
return axios.post('/api/services', data).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async service_update (data) {
|
|
||||||
return axios.post('/api/services/'+data.id, data).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async service_hits (id, start, end, group) {
|
|
||||||
return axios.get('/api/services/'+id+'/data?start=' + start + '&end=' + end + '&group=' + group).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async service_heatmap (id, start, end, group) {
|
|
||||||
return axios.get('/api/services/'+id+'/heatmap?start=' + start + '&end=' + end + '&group=' + group).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async service_failures (id, start, end, limit=999, offset=0) {
|
|
||||||
return axios.get('/api/services/'+id+'/failures?start=' + start + '&end=' + end + '&limit=' + limit).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async service_delete (id) {
|
|
||||||
return axios.delete('/api/services/'+id).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async services_reorder (data) {
|
|
||||||
return axios.post('/api/reorder/services', data).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async groups () {
|
|
||||||
return axios.get('/api/groups').then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async groups_reorder (data) {
|
|
||||||
return axios.post('/api/reorder/groups', data).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async group_delete (id) {
|
|
||||||
return axios.delete('/api/groups/'+id).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async group_create (data) {
|
|
||||||
return axios.post('/api/groups', data).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async group_update (data) {
|
|
||||||
return axios.post('/api/groups/'+data.id, data).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async users () {
|
|
||||||
return axios.get('/api/users').then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async user_create (data) {
|
|
||||||
return axios.post('/api/users', data).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async user_update (data) {
|
|
||||||
return axios.post('/api/users/'+data.id, data).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async user_delete (id) {
|
|
||||||
return axios.delete('/api/users/'+id).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async messages () {
|
|
||||||
return axios.get('/api/messages').then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async message_create (data) {
|
|
||||||
return axios.post('/api/messages', data).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async message_update (data) {
|
|
||||||
return axios.post('/api/messages/'+data.id, data).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async message_delete (id) {
|
|
||||||
return axios.delete('/api/messages/'+id).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async group (id) {
|
|
||||||
return axios.get('/api/groups/'+id).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async notifiers () {
|
|
||||||
return axios.get('/api/notifiers').then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async notifier_save (data) {
|
|
||||||
return axios.post('/api/notifier/'+data.method, data).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async notifier_test (data) {
|
|
||||||
return axios.post('/api/notifier/'+data.method+'/test', data).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async integrations () {
|
|
||||||
return axios.get('/api/integrations').then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async integration (name) {
|
|
||||||
return axios.get('/api/integrations/'+name).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async integration_save (data) {
|
|
||||||
return axios.post('/api/integrations/'+data.name, data).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async renewApiKeys () {
|
|
||||||
return axios.get('/api/renew').then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async cache () {
|
|
||||||
return axios.get('/api/cache').then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async clearCache () {
|
|
||||||
return axios.get('/api/clear_cache').then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async logs () {
|
|
||||||
return axios.get('/api/logs').then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async logs_last () {
|
|
||||||
return axios.get('/api/logs/last').then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async theme () {
|
|
||||||
return axios.get('/api/theme').then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async theme_generate (create=true) {
|
|
||||||
if (create) {
|
|
||||||
return axios.get('/api/theme/create').then(response => (response.data))
|
|
||||||
} else {
|
|
||||||
return axios.delete('/api/theme').then(response => (response.data))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async theme_save (data) {
|
|
||||||
return axios.post('/api/theme', data).then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async login (username, password) {
|
|
||||||
const f = {username: username, password: password}
|
|
||||||
return axios.post('/api/login', qs.stringify(f))
|
|
||||||
.then(response => (response.data))
|
|
||||||
}
|
|
||||||
|
|
||||||
async logout () {
|
|
||||||
await axios.get('/api/logout').then(response => (response.data))
|
|
||||||
return localStorage.removeItem(tokenKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
saveToken (username, token) {
|
|
||||||
const user = {username: username, token: token}
|
|
||||||
localStorage.setItem(tokenKey, JSON.stringify(user));
|
|
||||||
return user
|
|
||||||
}
|
|
||||||
|
|
||||||
async scss_base () {
|
|
||||||
return await axios({
|
|
||||||
url: '/scss/base.scss',
|
|
||||||
method: 'GET',
|
|
||||||
responseType: 'blob'
|
|
||||||
}).then((response) => {
|
|
||||||
const reader = new window.FileReader();
|
|
||||||
return reader.readAsText(response.data)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
token () {
|
|
||||||
const tk = localStorage.getItem(tokenKey)
|
|
||||||
if (!tk) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
return JSON.parse(tk);
|
|
||||||
}
|
|
||||||
|
|
||||||
authToken () {
|
|
||||||
let user = JSON.parse(localStorage.getItem(tokenKey));
|
|
||||||
if (user && user.token) {
|
|
||||||
return { 'Authorization': 'Bearer ' + user.token };
|
|
||||||
} else {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async allActions (...all) {
|
|
||||||
await axios.all([all])
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
const api = new Api()
|
|
||||||
export default api
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<h3>Cache</h3>
|
||||||
|
<div v-if="!cache && cache.length !== 0" class="alert alert-danger">
|
||||||
|
There are no cached files
|
||||||
|
</div>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">URL</th>
|
||||||
|
<th scope="col">Size</th>
|
||||||
|
<th scope="col">Expiration</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
|
||||||
|
<tr v-for="(cache, index) in cache">
|
||||||
|
<td>{{cache.url}}</td>
|
||||||
|
<td>{{cache.size}}</td>
|
||||||
|
<td>{{expireTime(cache.expiration)}}</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
<button @click.prevent="clearCache" class="btn btn-danger btn-block">Clear Cache</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Api from "../../API";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'Cache',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
cache: [],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async mounted() {
|
||||||
|
this.cache = await Api.cache()
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
expireTime(ex) {
|
||||||
|
return this.toLocal(ex)
|
||||||
|
},
|
||||||
|
async clearCache() {
|
||||||
|
await Api.clearCache()
|
||||||
|
this.cache = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
|
@ -23,33 +23,24 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ServiceInfo from "./ServiceInfo";
|
import ServiceInfo from "../Service/ServiceInfo";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DashboardIndex',
|
name: 'DashboardIndex',
|
||||||
components: {
|
components: {
|
||||||
ServiceInfo
|
ServiceInfo
|
||||||
},
|
},
|
||||||
data () {
|
methods: {
|
||||||
return {
|
failuresLast24Hours() {
|
||||||
|
let total = 0;
|
||||||
|
this.$store.getters.services.map((s) => {
|
||||||
|
total += s.failures_24_hours
|
||||||
|
})
|
||||||
|
return total
|
||||||
|
},
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
|
|
||||||
},
|
|
||||||
async created() {
|
|
||||||
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
failuresLast24Hours() {
|
|
||||||
let total = 0;
|
|
||||||
this.$store.getters.services.map((s) => { total += s.failures_24_hours })
|
|
||||||
return total
|
|
||||||
},
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../API"
|
import Api from "../../API"
|
||||||
import FormMessage from "../../forms/Message";
|
import FormMessage from "../../forms/Message";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -99,97 +99,97 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import FormGroup from "../../forms/Group";
|
import FormGroup from "../../forms/Group";
|
||||||
import Api from "../../components/API";
|
import Api from "../../API";
|
||||||
import ToggleSwitch from "../../forms/ToggleSwitch";
|
import ToggleSwitch from "../../forms/ToggleSwitch";
|
||||||
import draggable from 'vuedraggable'
|
import draggable from 'vuedraggable'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'DashboardServices',
|
name: 'DashboardServices',
|
||||||
components: {
|
components: {
|
||||||
ToggleSwitch,
|
ToggleSwitch,
|
||||||
FormGroup,
|
FormGroup,
|
||||||
draggable
|
draggable
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
edit: false,
|
|
||||||
group: {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
servicesList: {
|
|
||||||
get() {
|
|
||||||
return this.$store.state.servicesInOrder
|
|
||||||
},
|
},
|
||||||
async set(value) {
|
data() {
|
||||||
let data = [];
|
return {
|
||||||
value.forEach((s, k) => {
|
edit: false,
|
||||||
data.push({service: s.id, order: k+1})
|
group: {}
|
||||||
});
|
}
|
||||||
await Api.services_reorder(data)
|
|
||||||
const services = await Api.services()
|
|
||||||
this.$store.commit('setServices', services)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
groupsList: {
|
|
||||||
get() {
|
|
||||||
return this.$store.state.groupsInOrder
|
|
||||||
},
|
},
|
||||||
async set(value) {
|
computed: {
|
||||||
let data = [];
|
servicesList: {
|
||||||
value.forEach((s, k) => {
|
get() {
|
||||||
data.push({group: s.id, order: k+1})
|
return this.$store.state.servicesInOrder
|
||||||
});
|
},
|
||||||
await Api.groups_reorder(data)
|
async set(value) {
|
||||||
const groups = await Api.groups()
|
let data = [];
|
||||||
this.$store.commit('setGroups', groups)
|
value.forEach((s, k) => {
|
||||||
}
|
data.push({service: s.id, order: k + 1})
|
||||||
}
|
});
|
||||||
},
|
await Api.services_reorder(data)
|
||||||
beforeMount() {
|
const services = await Api.services()
|
||||||
|
this.$store.commit('setServices', services)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
groupsList: {
|
||||||
|
get() {
|
||||||
|
return this.$store.state.groupsInOrder
|
||||||
|
},
|
||||||
|
async set(value) {
|
||||||
|
let data = [];
|
||||||
|
value.forEach((s, k) => {
|
||||||
|
data.push({group: s.id, order: k + 1})
|
||||||
|
});
|
||||||
|
await Api.groups_reorder(data)
|
||||||
|
const groups = await Api.groups()
|
||||||
|
this.$store.commit('setGroups', groups)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeMount() {
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
editChange(v) {
|
editChange(v) {
|
||||||
this.group = {}
|
this.group = {}
|
||||||
this.edit = v
|
this.edit = v
|
||||||
},
|
},
|
||||||
editGroup(g, mode) {
|
editGroup(g, mode) {
|
||||||
this.group = g
|
this.group = g
|
||||||
this.edit = !mode
|
this.edit = !mode
|
||||||
},
|
},
|
||||||
reordered_services() {
|
reordered_services() {
|
||||||
|
|
||||||
},
|
},
|
||||||
saveUpdatedOrder: function (e) {
|
saveUpdatedOrder: function (e) {
|
||||||
window.console.log("saving...");
|
window.console.log("saving...");
|
||||||
window.console.log(this.myViews.array()); // this.myViews.array is not a function
|
window.console.log(this.myViews.array()); // this.myViews.array is not a function
|
||||||
},
|
},
|
||||||
serviceGroup(s) {
|
serviceGroup(s) {
|
||||||
let group = this.$store.getters.groupById(s.group_id)
|
let group = this.$store.getters.groupById(s.group_id)
|
||||||
if (group) {
|
if (group) {
|
||||||
return group.name
|
return group.name
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
},
|
||||||
|
async deleteGroup(g) {
|
||||||
|
let c = confirm(`Are you sure you want to delete '${g.name}'?`)
|
||||||
|
if (c) {
|
||||||
|
await Api.group_delete(g.id)
|
||||||
|
const groups = await Api.groups()
|
||||||
|
this.$store.commit('setGroups', groups)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async deleteService(s) {
|
||||||
|
let c = confirm(`Are you sure you want to delete '${s.name}'?`)
|
||||||
|
if (c) {
|
||||||
|
await Api.service_delete(s.id)
|
||||||
|
const services = await Api.services()
|
||||||
|
this.$store.commit('setServices', services)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return ""
|
|
||||||
},
|
|
||||||
async deleteGroup(g) {
|
|
||||||
let c = confirm(`Are you sure you want to delete '${g.name}'?`)
|
|
||||||
if (c) {
|
|
||||||
await Api.group_delete(g.id)
|
|
||||||
const groups = await Api.groups()
|
|
||||||
this.$store.commit('setGroups', groups)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async deleteService(s) {
|
|
||||||
let c = confirm(`Are you sure you want to delete '${s.name}'?`)
|
|
||||||
if (c) {
|
|
||||||
await Api.service_delete(s.id)
|
|
||||||
const services = await Api.services()
|
|
||||||
this.$store.commit('setServices', services)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
|
|
@ -32,7 +32,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../API"
|
import Api from "../../API"
|
||||||
import FormUser from "../../forms/User";
|
import FormUser from "../../forms/User";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<FormService :in_service="service"/>
|
<FormService :in_service="service"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import FormGroup from "../../forms/Group";
|
import FormGroup from "../../forms/Group";
|
||||||
import Api from "../../components/API";
|
import Api from "../../API";
|
||||||
import ToggleSwitch from "../../forms/ToggleSwitch";
|
import ToggleSwitch from "../../forms/ToggleSwitch";
|
||||||
import draggable from 'vuedraggable'
|
import draggable from 'vuedraggable'
|
||||||
import FormService from "../../forms/Service";
|
import FormService from "../../forms/Service";
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../API";
|
import Api from "../../API";
|
||||||
|
|
||||||
// require component
|
// require component
|
||||||
import { codemirror } from 'vue-codemirror'
|
import { codemirror } from 'vue-codemirror'
|
||||||
|
@ -136,7 +136,6 @@
|
||||||
this.error = null
|
this.error = null
|
||||||
}
|
}
|
||||||
this.pending = false
|
this.pending = false
|
||||||
window.console.log(resp)
|
|
||||||
await this.fetchTheme()
|
await this.fetchTheme()
|
||||||
},
|
},
|
||||||
changeTab (v) {
|
changeTab (v) {
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../API"
|
import Api from "../../API"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'TopNav',
|
name: 'TopNav',
|
||||||
|
|
|
@ -8,20 +8,7 @@
|
||||||
<span class="badge float-right" :class="{'bg-success': service.online, 'bg-danger': !service.online}">{{service.online ? "ONLINE" : "OFFLINE"}}</span>
|
<span class="badge float-right" :class="{'bg-success': service.online, 'bg-danger': !service.online}">{{service.online ? "ONLINE" : "OFFLINE"}}</span>
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<div class="row stats_area mt-5">
|
<ServiceTopStats :service="service"/>
|
||||||
<div class="col-4">
|
|
||||||
<span class="lg_number">{{service.avg_response}}ms</span>
|
|
||||||
Average Response
|
|
||||||
</div>
|
|
||||||
<div class="col-4">
|
|
||||||
<span class="lg_number">{{service.online_24_hours}}%</span>
|
|
||||||
Uptime last 24 Hours
|
|
||||||
</div>
|
|
||||||
<div class="col-4">
|
|
||||||
<span class="lg_number">{{service.online_7_days}}%</span>
|
|
||||||
Uptime last 7 Days
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -48,29 +35,26 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ServiceChart from "./ServiceChart";
|
import ServiceChart from "./ServiceChart";
|
||||||
|
import ServiceTopStats from "@/components/Service/ServiceTopStats";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ServiceBlock',
|
name: 'ServiceBlock',
|
||||||
components: {ServiceChart},
|
components: {ServiceTopStats, ServiceChart},
|
||||||
props: {
|
props: {
|
||||||
service: {
|
service: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
methods: {
|
||||||
methods: {
|
|
||||||
smallText(s) {
|
smallText(s) {
|
||||||
if (s.online) {
|
if (s.online) {
|
||||||
return `Online, last checked ${this.ago(s.last_success)}`
|
return `Online, last checked ${this.ago(this.parseTime(s.last_success))}`
|
||||||
} else {
|
} else {
|
||||||
return `Offline, last error: ${s.last_failure.issue} ${this.ago(s.last_failure.created_at)}`
|
return `Offline, last error: ${s.last_failure.issue} ${this.ago(this.parseTime(s.last_failure.created_at))}`
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
ago(t1) {
|
}
|
||||||
const tm = this.parseTime(t1)
|
|
||||||
return this.duration(this.$moment().utc(), tm)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../../components/API"
|
import Api from "../../API"
|
||||||
|
|
||||||
const axisOptions = {
|
const axisOptions = {
|
||||||
labels: {
|
labels: {
|
||||||
|
@ -30,108 +30,108 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ServiceChart',
|
name: 'ServiceChart',
|
||||||
props: {
|
props: {
|
||||||
service: {
|
service: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
|
||||||
},
|
|
||||||
async created() {
|
|
||||||
await this.chartHits()
|
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
ready: false,
|
|
||||||
data: [],
|
|
||||||
chartOptions: {
|
|
||||||
chart: {
|
|
||||||
height: 210,
|
|
||||||
width: "100%",
|
|
||||||
type: "area",
|
|
||||||
animations: {
|
|
||||||
enabled: true,
|
|
||||||
initialAnimation: {
|
|
||||||
enabled: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
selection: {
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
zoom: {
|
|
||||||
enabled: false
|
|
||||||
},
|
|
||||||
toolbar: {
|
|
||||||
show: false
|
|
||||||
},
|
|
||||||
},
|
|
||||||
grid: {
|
|
||||||
show: false,
|
|
||||||
padding: {
|
|
||||||
top: 0,
|
|
||||||
right: 0,
|
|
||||||
bottom: 0,
|
|
||||||
left: -10,
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
xaxis: {
|
async created() {
|
||||||
type: "datetime",
|
await this.chartHits()
|
||||||
...axisOptions
|
},
|
||||||
},
|
data() {
|
||||||
yaxis: {
|
return {
|
||||||
...axisOptions
|
ready: false,
|
||||||
},
|
data: [],
|
||||||
tooltip: {
|
chartOptions: {
|
||||||
enabled: false,
|
chart: {
|
||||||
marker: {
|
height: 210,
|
||||||
show: false,
|
width: "100%",
|
||||||
},
|
type: "area",
|
||||||
x: {
|
animations: {
|
||||||
show: false,
|
enabled: true,
|
||||||
}
|
initialAnimation: {
|
||||||
},
|
enabled: true
|
||||||
legend: {
|
}
|
||||||
show: false,
|
},
|
||||||
},
|
selection: {
|
||||||
dataLabels: {
|
enabled: false
|
||||||
enabled: false
|
},
|
||||||
},
|
zoom: {
|
||||||
floating: true,
|
enabled: false
|
||||||
axisTicks: {
|
},
|
||||||
show: false
|
toolbar: {
|
||||||
},
|
show: false
|
||||||
axisBorder: {
|
},
|
||||||
show: false
|
},
|
||||||
},
|
grid: {
|
||||||
fill: {
|
show: false,
|
||||||
colors: [this.service.online ? "#48d338" : "#dd3545"],
|
padding: {
|
||||||
opacity: 1,
|
top: 0,
|
||||||
type: 'solid'
|
right: 0,
|
||||||
},
|
bottom: 0,
|
||||||
stroke: {
|
left: -10,
|
||||||
show: false,
|
}
|
||||||
curve: 'smooth',
|
},
|
||||||
lineCap: 'butt',
|
xaxis: {
|
||||||
colors: [this.service.online ? "#3aa82d" : "#dd3545"],
|
type: "datetime",
|
||||||
|
...axisOptions
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
...axisOptions
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: false,
|
||||||
|
marker: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
show: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
floating: true,
|
||||||
|
axisTicks: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisBorder: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
colors: [this.service.online ? "#48d338" : "#dd3545"],
|
||||||
|
opacity: 1,
|
||||||
|
type: 'solid'
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
show: false,
|
||||||
|
curve: 'smooth',
|
||||||
|
lineCap: 'butt',
|
||||||
|
colors: [this.service.online ? "#3aa82d" : "#dd3545"],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
data: []
|
||||||
|
}]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
series: [{
|
|
||||||
data: []
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
async chartHits() {
|
async chartHits() {
|
||||||
const start = this.ago((3600 * 24) * 7)
|
const start = this.nowSubtract((3600 * 24) * 7)
|
||||||
this.data = await Api.service_hits(this.service.id, start, this.now(), "hour")
|
this.data = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(new Date()), "hour")
|
||||||
this.series = [{
|
this.series = [{
|
||||||
name: this.service.name,
|
name: this.service.name,
|
||||||
...this.data
|
...this.data
|
||||||
}]
|
}]
|
||||||
this.ready = true
|
this.ready = true
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ServiceChart from "./ServiceChart";
|
import ServiceChart from "./ServiceChart";
|
||||||
import Api from "../API";
|
import Api from "../../API";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ServiceFailures',
|
name: 'ServiceFailures',
|
||||||
|
|
|
@ -0,0 +1,84 @@
|
||||||
|
<template>
|
||||||
|
<apexchart v-if="ready" width="100%" height="300" type="heatmap" :options="chartOptions" :series="series"></apexchart>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Api from "../../API"
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ServiceHeatmap',
|
||||||
|
props: {
|
||||||
|
service: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
await this.chartHeatmap()
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
ready: false,
|
||||||
|
data: [],
|
||||||
|
chartOptions: {
|
||||||
|
chart: {
|
||||||
|
height: "100%",
|
||||||
|
width: "100%",
|
||||||
|
type: 'heatmap',
|
||||||
|
toolbar: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
enableShades: true,
|
||||||
|
shadeIntensity: 0.5,
|
||||||
|
colors: ["#d53a3b"],
|
||||||
|
series: [{data: [{}]}],
|
||||||
|
yaxis: {
|
||||||
|
labels: {
|
||||||
|
formatter: (value) => {
|
||||||
|
return value
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: true,
|
||||||
|
x: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
formatter: function(val, opts) { return val+" Failures" },
|
||||||
|
title: {
|
||||||
|
formatter: (seriesName) => seriesName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
data: []
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async chartHeatmap() {
|
||||||
|
const start = this.nowSubtract((3600 * 24) * 7)
|
||||||
|
const data = await Api.service_heatmap(this.service.id, this.toUnix(start), this.toUnix(new Date()), "hour")
|
||||||
|
|
||||||
|
let dataArr = []
|
||||||
|
data.forEach(function(d) {
|
||||||
|
let date = new Date(d.date);
|
||||||
|
dataArr.push({name: date.toLocaleString('en-us', { month: 'long' }), data: d.data});
|
||||||
|
});
|
||||||
|
|
||||||
|
this.series = dataArr
|
||||||
|
this.ready = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
|
@ -16,15 +16,16 @@
|
||||||
</div>
|
</div>
|
||||||
</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">
|
||||||
Failed {{duration(current(), failure.created_at)}}<br>
|
Failed {{ago(parseTime(failure.created_at))}}<br>
|
||||||
{{failure.issue}}
|
{{failure.issue}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ServiceSparkLine from "./ServiceSparkLine";
|
import ServiceSparkLine from "./ServiceSparkLine";
|
||||||
import Api from "../API";
|
import Api from "../../API";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ServiceInfo',
|
name: 'ServiceInfo',
|
||||||
|
@ -37,7 +38,7 @@
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
set1: [],
|
set1: [],
|
||||||
set2: [],
|
set2: [],
|
||||||
|
@ -47,7 +48,7 @@
|
||||||
failures: null
|
failures: null
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted () {
|
async mounted() {
|
||||||
this.set1 = await this.getHits(24 * 2, "hour")
|
this.set1 = await this.getHits(24 * 2, "hour")
|
||||||
this.set1_name = this.calc(this.set1)
|
this.set1_name = this.calc(this.set1)
|
||||||
this.set2 = await this.getHits(24 * 7, "hour")
|
this.set2 = await this.getHits(24 * 7, "hour")
|
||||||
|
@ -55,19 +56,19 @@
|
||||||
this.loaded = true
|
this.loaded = true
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async getHits (hours, group) {
|
async getHits(hours, group) {
|
||||||
const start = this.ago(3600 * hours)
|
const start = this.nowSubtract(3600 * hours)
|
||||||
if (!this.service.online) {
|
if (!this.service.online) {
|
||||||
this.failures = await Api.service_failures(this.service.id, this.now()-360, this.now(), 5)
|
this.failures = await Api.service_failures(this.service.id, this.toUnix(start), this.toUnix(this.now()), 5)
|
||||||
return [ { name: "None", data: [] } ]
|
return [{name: "None", data: []}]
|
||||||
}
|
}
|
||||||
const data = await Api.service_hits(this.service.id, start, this.now(), group)
|
const data = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(this.now()), group)
|
||||||
if (!data) {
|
if (!data) {
|
||||||
return [ { name: "None", data: [] } ]
|
return [{name: "None", data: []}]
|
||||||
}
|
}
|
||||||
return [ { name: "Latency", data: data.data } ]
|
return [{name: "Latency", data: data.data}]
|
||||||
},
|
},
|
||||||
calc (s) {
|
calc(s) {
|
||||||
let data = s[0].data
|
let data = s[0].data
|
||||||
if (data.length > 1) {
|
if (data.length > 1) {
|
||||||
let total = 0
|
let total = 0
|
||||||
|
@ -75,7 +76,7 @@
|
||||||
total += f.y
|
total += f.y
|
||||||
});
|
});
|
||||||
total = total / data.length
|
total = total / data.length
|
||||||
return total.toFixed(0) + "ms Average"
|
return total.toFixed(0) + "ms"
|
||||||
} else {
|
} else {
|
||||||
return "Offline"
|
return "Offline"
|
||||||
}
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
<template>
|
||||||
|
<div class="row stats_area mt-5 mb-4">
|
||||||
|
<div class="col-4">
|
||||||
|
<span class="lg_number">{{service.avg_response}}ms</span>
|
||||||
|
Average Response
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<span class="lg_number">{{service.online_24_hours}}%</span>
|
||||||
|
Uptime last 24 Hours
|
||||||
|
</div>
|
||||||
|
<div class="col-4">
|
||||||
|
<span class="lg_number">{{service.online_7_days}}%</span>
|
||||||
|
Uptime last 7 Days
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'ServiceTopStats',
|
||||||
|
props: {
|
||||||
|
service: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
|
@ -1,14 +0,0 @@
|
||||||
class Time {
|
|
||||||
now () {
|
|
||||||
return new Date();
|
|
||||||
}
|
|
||||||
utc () {
|
|
||||||
return new Date().getUTCDate();
|
|
||||||
}
|
|
||||||
utcToLocal (utc) {
|
|
||||||
let u = new Date().setUTCDate(utc)
|
|
||||||
return u.toLocaleString()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const time = new Time()
|
|
||||||
export default time
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<form @submit="saveCheckin">
|
<form @submit.prevent="saveCheckin">
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label for="checkin_interval" class="col-form-label">Checkin Name</label>
|
<label for="checkin_interval" class="col-form-label">Checkin Name</label>
|
||||||
|
@ -14,46 +14,45 @@
|
||||||
<input v-model="checkin.grace" type="number" name="grace" class="form-control" id="grace_period" placeholder="10">
|
<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-3">
|
||||||
<button @click="saveCheckin" type="submit" id="submit" class="btn btn-success d-block" style="margin-top: 14px;">Save Checkin</button>
|
<button @click.prevent="saveCheckin" type="submit" id="submit" class="btn btn-success d-block" style="margin-top: 14px;">Save Checkin</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../components/API";
|
import Api from "../API";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Checkin',
|
name: 'Checkin',
|
||||||
props: {
|
props: {
|
||||||
service: {
|
service: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
checkin: {
|
checkin: {
|
||||||
name: "",
|
name: "",
|
||||||
interval: 60,
|
interval: 60,
|
||||||
grace: 60,
|
grace: 60,
|
||||||
service: this.service.id
|
service: this.service.id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async saveCheckin(e) {
|
async saveCheckin() {
|
||||||
e.preventDefault();
|
const data = {name: this.group.name, public: this.group.public}
|
||||||
const data = {name: this.group.name, public: this.group.public}
|
await Api.group_create(data)
|
||||||
await Api.group_create(data)
|
const groups = await Api.groups()
|
||||||
const groups = await Api.groups()
|
this.$store.commit('setGroups', groups)
|
||||||
this.$store.commit('setGroups', groups)
|
},
|
||||||
},
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<form @submit="saveSettings">
|
<form @submit.prevent="saveSettings">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Project Name</label>
|
<label>Project Name</label>
|
||||||
<input v-model="core.name" type="text" class="form-control" placeholder="Great Uptime">
|
<input v-model="core.name" type="text" class="form-control" placeholder="Great Uptime">
|
||||||
|
@ -68,20 +68,9 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<button @click.prevent="saveSettings" type="submit" class="btn btn-primary btn-block">Save Settings</button>
|
||||||
<div class="col-12">
|
|
||||||
<label class="d-none d-sm-block">Send Updates only</label>
|
|
||||||
<span class="switch">
|
|
||||||
<input v-model="core.update_notify" @change="core.update_notify = !!core.update_notify" type="checkbox" class="switch" id="switch-update_notify" v-bind:checked="core.update_notify">
|
|
||||||
<label for="switch-update_notify" class="mt-2 mt-sm-0"></label>
|
|
||||||
<small class="form-text text-muted">Enabling this will send only notifications when the status of a services changes.</small>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button @click="saveSettings" type="submit" class="btn btn-primary btn-block">Save Settings</button>
|
<div class="form-group row mt-5">
|
||||||
|
|
||||||
<div class="form-group row mt-3">
|
|
||||||
<label class="col-sm-3 col-form-label">API Key</label>
|
<label class="col-sm-3 col-form-label">API Key</label>
|
||||||
<div class="col-sm-9">
|
<div class="col-sm-9">
|
||||||
<input v-model="core.api_key" @focus="$event.target.select()" type="text" class="form-control select-input" readonly>
|
<input v-model="core.api_key" @focus="$event.target.select()" type="text" class="form-control select-input" readonly>
|
||||||
|
@ -102,43 +91,44 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from '../components/API'
|
import Api from '../API'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'CoreSettings',
|
name: 'CoreSettings',
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
core: this.$store.getters.core,
|
core: this.$store.getters.core,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted () {
|
async mounted() {
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async saveSettings (e) {
|
async saveSettings() {
|
||||||
e.preventDefault()
|
const c = this.core
|
||||||
const c = this.core
|
const coreForm = {
|
||||||
const coreForm = {name: c.name, description: c.description, domain: c.domain,
|
name: c.name, description: c.description, domain: c.domain,
|
||||||
timezone: c.timezone, using_cdn: c.using_cdn, footer: c.footer, update_notify: c.update_notify}
|
timezone: c.timezone, using_cdn: c.using_cdn, footer: c.footer, update_notify: c.update_notify
|
||||||
await Api.core_save(coreForm)
|
}
|
||||||
const core = await Api.core()
|
await Api.core_save(coreForm)
|
||||||
this.$store.commit('setCore', core)
|
const core = await Api.core()
|
||||||
this.core = core
|
this.$store.commit('setCore', core)
|
||||||
},
|
this.core = core
|
||||||
async renewApiKeys () {
|
},
|
||||||
let r = confirm("Are you sure you want to reset the API keys?");
|
async renewApiKeys() {
|
||||||
if (r === true) {
|
let r = confirm("Are you sure you want to reset the API keys?");
|
||||||
await Api.renewApiKeys()
|
if (r === true) {
|
||||||
const core = await Api.core()
|
await Api.renewApiKeys()
|
||||||
this.$store.commit('setCore', core)
|
const core = await Api.core()
|
||||||
this.core = core
|
this.$store.commit('setCore', core)
|
||||||
}
|
this.core = core
|
||||||
},
|
}
|
||||||
selectAll() {
|
},
|
||||||
this.$refs.input.select();
|
selectAll() {
|
||||||
|
this.$refs.input.select();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
|
|
@ -38,65 +38,65 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../components/API";
|
import Api from "../API";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FormGroup',
|
name: 'FormGroup',
|
||||||
props: {
|
props: {
|
||||||
in_group: {
|
in_group: {
|
||||||
type: Object
|
type: Object
|
||||||
},
|
},
|
||||||
edit: {
|
edit: {
|
||||||
type: Function
|
type: Function
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
group: {
|
group: {
|
||||||
name: "",
|
name: "",
|
||||||
public: true
|
public: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
in_group() {
|
||||||
|
this.group = this.in_group
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
removeEdit() {
|
||||||
|
this.group = {}
|
||||||
|
this.edit(false)
|
||||||
|
},
|
||||||
|
async saveGroup(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
this.loading = true
|
||||||
|
if (this.in_group) {
|
||||||
|
await this.updateGroup()
|
||||||
|
} else {
|
||||||
|
await this.createGroup()
|
||||||
|
}
|
||||||
|
this.loading = false
|
||||||
|
},
|
||||||
|
async createGroup() {
|
||||||
|
const g = this.group
|
||||||
|
const data = {name: g.name, public: g.public}
|
||||||
|
await Api.group_create(data)
|
||||||
|
const groups = await Api.groups()
|
||||||
|
this.$store.commit('setGroups', groups)
|
||||||
|
this.group = {}
|
||||||
|
},
|
||||||
|
async updateGroup() {
|
||||||
|
const g = this.group
|
||||||
|
const data = {id: g.id, name: g.name, public: g.public}
|
||||||
|
await Api.group_update(data)
|
||||||
|
const groups = await Api.groups()
|
||||||
|
this.$store.commit('setGroups', groups)
|
||||||
|
this.edit(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
in_group() {
|
|
||||||
this.group = this.in_group
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
removeEdit() {
|
|
||||||
this.group = {}
|
|
||||||
this.edit(false)
|
|
||||||
},
|
|
||||||
async saveGroup(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.loading = true
|
|
||||||
if (this.in_group) {
|
|
||||||
await this.updateGroup()
|
|
||||||
} else {
|
|
||||||
await this.createGroup()
|
|
||||||
}
|
|
||||||
this.loading = false
|
|
||||||
},
|
|
||||||
async createGroup() {
|
|
||||||
const g = this.group
|
|
||||||
const data = {name: g.name, public: g.public}
|
|
||||||
await Api.group_create(data)
|
|
||||||
const groups = await Api.groups()
|
|
||||||
this.$store.commit('setGroups', groups)
|
|
||||||
this.group = {}
|
|
||||||
},
|
|
||||||
async updateGroup() {
|
|
||||||
const g = this.group
|
|
||||||
const data = {id: g.id, name: g.name, public: g.public}
|
|
||||||
await Api.group_update(data)
|
|
||||||
const groups = await Api.groups()
|
|
||||||
this.$store.commit('setGroups', groups)
|
|
||||||
this.edit(false)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
|
|
@ -59,45 +59,50 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../components/API";
|
import Api from "../API";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FormIntegration',
|
name: 'FormIntegration',
|
||||||
props: {
|
props: {
|
||||||
integration: {
|
integration: {
|
||||||
type: Object
|
type: Object
|
||||||
}
|
}
|
||||||
},
|
|
||||||
data () {
|
|
||||||
return {
|
|
||||||
out: {},
|
|
||||||
services: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async addService(s) {
|
|
||||||
const data = {name: s.name, type: s.type, domain: s.domain, port: s.port, check_interval: s.check_interval, timeout: s.timeout}
|
|
||||||
const out = await Api.service_create(data)
|
|
||||||
const services = await Api.services()
|
|
||||||
this.$store.commit('setServices', services)
|
|
||||||
s.added = true
|
|
||||||
},
|
},
|
||||||
async updateIntegration() {
|
data() {
|
||||||
const i = this.integration
|
return {
|
||||||
const data = {name: i.name, enabled: i.enabled, fields: i.fields}
|
out: {},
|
||||||
this.out = data
|
services: []
|
||||||
const out = await Api.integration_save(data)
|
}
|
||||||
if (out != null) {
|
},
|
||||||
this.services = out
|
watch: {},
|
||||||
}
|
methods: {
|
||||||
const integrations = await Api.integrations()
|
async addService(s) {
|
||||||
this.$store.commit('setIntegrations', integrations)
|
const data = {
|
||||||
}
|
name: s.name,
|
||||||
|
type: s.type,
|
||||||
|
domain: s.domain,
|
||||||
|
port: s.port,
|
||||||
|
check_interval: s.check_interval,
|
||||||
|
timeout: s.timeout
|
||||||
|
}
|
||||||
|
const out = await Api.service_create(data)
|
||||||
|
const services = await Api.services()
|
||||||
|
this.$store.commit('setServices', services)
|
||||||
|
s.added = true
|
||||||
|
},
|
||||||
|
async updateIntegration() {
|
||||||
|
const i = this.integration
|
||||||
|
const data = {name: i.name, enabled: i.enabled, fields: i.fields}
|
||||||
|
this.out = data
|
||||||
|
const out = await Api.integration_save(data)
|
||||||
|
if (out != null) {
|
||||||
|
this.services = out
|
||||||
|
}
|
||||||
|
const integrations = await Api.integrations()
|
||||||
|
this.$store.commit('setIntegrations', integrations)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
|
|
@ -26,43 +26,43 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../components/API";
|
import Api from "../API";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FormLogin',
|
name: 'FormLogin',
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
username: "",
|
username: "",
|
||||||
password: "",
|
password: "",
|
||||||
auth: {},
|
auth: {},
|
||||||
loading: false,
|
loading: false,
|
||||||
error: false,
|
error: false,
|
||||||
disabled: true
|
disabled: true
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
checkForm() {
|
|
||||||
if (!this.username || !this.password) {
|
|
||||||
this.disabled = true
|
|
||||||
} else {
|
|
||||||
this.disabled = false
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async login () {
|
methods: {
|
||||||
this.loading = true
|
checkForm() {
|
||||||
this.error = false
|
if (!this.username || !this.password) {
|
||||||
const auth = await Api.login(this.username, this.password)
|
this.disabled = true
|
||||||
if (auth.error) {
|
} else {
|
||||||
this.error = true
|
this.disabled = false
|
||||||
} else if (auth.token) {
|
}
|
||||||
this.auth = Api.saveToken(this.username, auth.token)
|
},
|
||||||
await this.$store.dispatch('loadAdmin')
|
async login() {
|
||||||
this.$router.push('/dashboard')
|
this.loading = true
|
||||||
|
this.error = false
|
||||||
|
const auth = await Api.login(this.username, this.password)
|
||||||
|
if (auth.error) {
|
||||||
|
this.error = true
|
||||||
|
} else if (auth.token) {
|
||||||
|
this.auth = Api.saveToken(this.username, auth.token)
|
||||||
|
await this.$store.dispatch('loadAdmin')
|
||||||
|
this.$router.push('/dashboard')
|
||||||
|
}
|
||||||
|
this.loading = false
|
||||||
}
|
}
|
||||||
this.loading = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
|
|
@ -24,10 +24,10 @@
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-4 col-form-label">Message Date Range</label>
|
<label class="col-sm-4 col-form-label">Message Date Range</label>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<flatPickr v-model="message.start_on" :config="config" type="text" name="start_on" class="form-control form-control-plaintext" id="start_on" value="0001-01-01T00:00:00Z" required />
|
<flatPickr v-model="message.start_on" @on-change="startChange" :config="config" type="text" name="start_on" class="form-control form-control-plaintext" id="start_on" value="0001-01-01T00:00:00Z" required />
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-4">
|
<div class="col-sm-4">
|
||||||
<flatPickr v-model="message.end_on" :config="config" type="text" name="end_on" class="form-control form-control-plaintext" id="end_on" value="0001-01-01T00:00:00Z" required />
|
<flatPickr v-model="message.end_on" @on-change="endChange" :config="config" type="text" name="end_on" class="form-control form-control-plaintext" id="end_on" value="0001-01-01T00:00:00Z" required />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -90,7 +90,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../components/API";
|
import Api from "../API";
|
||||||
import flatPickr from 'vue-flatpickr-component';
|
import flatPickr from 'vue-flatpickr-component';
|
||||||
import 'flatpickr/dist/flatpickr.css';
|
import 'flatpickr/dist/flatpickr.css';
|
||||||
|
|
||||||
|
@ -135,6 +135,12 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
startChange(e) {
|
||||||
|
window.console.log(e)
|
||||||
|
},
|
||||||
|
endChange(e) {
|
||||||
|
window.console.log(e)
|
||||||
|
},
|
||||||
removeEdit() {
|
removeEdit() {
|
||||||
this.message = {}
|
this.message = {}
|
||||||
this.edit(false)
|
this.edit(false)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<form @submit="saveNotifier">
|
<form @submit.prevent="saveNotifier">
|
||||||
|
|
||||||
<div v-if="error" class="alert alert-danger col-12" role="alert">{{error}}</div>
|
<div v-if="error" class="alert alert-danger col-12" role="alert">{{error}}</div>
|
||||||
|
|
||||||
|
@ -38,13 +38,13 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 col-sm-4 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
<div class="col-12 col-sm-4 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||||
<button @click="saveNotifier" type="submit" class="btn btn-block text-capitalize" :class="{'btn-primary': !saved, 'btn-success': saved}">
|
<button @click.prevent="saveNotifier" type="submit" class="btn btn-block text-capitalize" :class="{'btn-primary': !saved, 'btn-success': saved}">
|
||||||
<i class="fa fa-check-circle"></i> {{loading ? "Loading..." : saved ? "Saved" : "Save"}}
|
<i class="fa fa-check-circle"></i> {{loading ? "Loading..." : saved ? "Saved" : "Save"}}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 col-sm-12 mt-3">
|
<div class="col-12 col-sm-12 mt-3">
|
||||||
<button @click="testNotifier" class="btn btn-secondary btn-block text-capitalize col-12 float-right"><i class="fa fa-vial"></i>
|
<button @click.prevent="testNotifier" class="btn btn-secondary btn-block text-capitalize col-12 float-right"><i class="fa fa-vial"></i>
|
||||||
{{loading ? "Loading..." : "Test Notifier"}}</button>
|
{{loading ? "Loading..." : "Test Notifier"}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -57,67 +57,65 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../components/API";
|
import Api from "../API";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Notifier',
|
name: 'Notifier',
|
||||||
props: {
|
props: {
|
||||||
notifier: {
|
notifier: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
error: null,
|
error: null,
|
||||||
saved: false,
|
saved: false,
|
||||||
ok: false,
|
ok: false,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async saveNotifier(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
this.loading = true
|
|
||||||
let form = {}
|
|
||||||
this.notifier.form.forEach((f) => {
|
|
||||||
form[f.field] = this.notifier[f.field]
|
|
||||||
});
|
|
||||||
form.enabled = this.notifier.enabled
|
|
||||||
form.limits = parseInt(this.notifier.limits)
|
|
||||||
form.method = this.notifier.method
|
|
||||||
await Api.notifier_save(form)
|
|
||||||
const notifiers = await Api.notifiers()
|
|
||||||
this.$store.commit('setNotifiers', notifiers)
|
|
||||||
this.saved = true
|
|
||||||
this.loading = false
|
|
||||||
setTimeout(() => {
|
|
||||||
this.saved = false
|
|
||||||
}, 2000)
|
|
||||||
},
|
},
|
||||||
async testNotifier(e) {
|
methods: {
|
||||||
e.preventDefault();
|
async saveNotifier() {
|
||||||
this.ok = false
|
this.loading = true
|
||||||
this.loading = true
|
let form = {}
|
||||||
let form = {}
|
this.notifier.form.forEach((f) => {
|
||||||
this.notifier.form.forEach((f) => {
|
form[f.field] = this.notifier[f.field]
|
||||||
form[f.field] = this.notifier[f.field]
|
});
|
||||||
});
|
form.enabled = this.notifier.enabled
|
||||||
form.enabled = this.notifier.enabled
|
form.limits = parseInt(this.notifier.limits)
|
||||||
form.limits = parseInt(this.notifier.limits)
|
form.method = this.notifier.method
|
||||||
form.method = this.notifier.method
|
await Api.notifier_save(form)
|
||||||
const tested = await Api.notifier_test(form)
|
const notifiers = await Api.notifiers()
|
||||||
if (tested === 'ok') {
|
await this.$store.commit('setNotifiers', notifiers)
|
||||||
this.ok = true
|
this.saved = true
|
||||||
} else {
|
this.loading = false
|
||||||
this.error = tested
|
setTimeout(() => {
|
||||||
}
|
this.saved = false
|
||||||
this.loading = false
|
}, 2000)
|
||||||
},
|
},
|
||||||
}
|
async testNotifier() {
|
||||||
|
this.ok = false
|
||||||
|
this.loading = true
|
||||||
|
let form = {}
|
||||||
|
this.notifier.form.forEach((f) => {
|
||||||
|
form[f.field] = this.notifier[f.field]
|
||||||
|
});
|
||||||
|
form.enabled = this.notifier.enabled
|
||||||
|
form.limits = parseInt(this.notifier.limits)
|
||||||
|
form.method = this.notifier.method
|
||||||
|
const tested = await Api.notifier_test(form)
|
||||||
|
if (tested === 'ok') {
|
||||||
|
this.ok = true
|
||||||
|
} else {
|
||||||
|
this.error = tested
|
||||||
|
}
|
||||||
|
this.loading = false
|
||||||
|
},
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<form @submit="saveService">
|
<form @submit.prevent="saveService">
|
||||||
<h4 class="mb-5 text-muted">Basic Information</h4>
|
<h4 class="mb-5 text-muted">Basic Information</h4>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label class="col-sm-4 col-form-label">Service Name</label>
|
<label class="col-sm-4 col-form-label">Service Name</label>
|
||||||
|
@ -12,10 +12,10 @@
|
||||||
<label for="service_type" class="col-sm-4 col-form-label">Service Type</label>
|
<label for="service_type" class="col-sm-4 col-form-label">Service Type</label>
|
||||||
<div class="col-sm-8">
|
<div class="col-sm-8">
|
||||||
<select v-model="service.type" class="form-control" id="service_type" >
|
<select v-model="service.type" class="form-control" id="service_type" >
|
||||||
<option value="http" >HTTP Service</option>
|
<option value="http">HTTP Service</option>
|
||||||
<option value="tcp" >TCP Service</option>
|
<option value="tcp">TCP Service</option>
|
||||||
<option value="udp" >UDP Service</option>
|
<option value="udp">UDP Service</option>
|
||||||
<option value="icmp" >ICMP Ping</option>
|
<option value="icmp">ICMP Ping</option>
|
||||||
</select>
|
</select>
|
||||||
<small class="form-text text-muted">Use HTTP if you are checking a website or use TCP if you are checking a server</small>
|
<small class="form-text text-muted">Use HTTP if you are checking a website or use TCP if you are checking a server</small>
|
||||||
</div>
|
</div>
|
||||||
|
@ -111,15 +111,8 @@
|
||||||
<small class="form-text text-muted">Use text for the service URL rather than the service number.</small>
|
<small class="form-text text-muted">Use text for the service URL rather than the service number.</small>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
|
||||||
<label for="order" class="col-sm-4 col-form-label">List Order</label>
|
|
||||||
<div class="col-sm-8">
|
|
||||||
<input v-model="service.order" type="number" name="order" class="form-control" min="0" id="order">
|
|
||||||
<small class="form-text text-muted">You can also drag and drop services to reorder on the Services tab.</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div v-if="service.type.match(/^(http)$/)" class="form-group row">
|
<div v-if="service.type.match(/^(http)$/)" class="form-group row">
|
||||||
<label for="order" class="col-sm-4 col-form-label">Verify SSL</label>
|
<label class="col-sm-4 col-form-label">Verify SSL</label>
|
||||||
<div class="col-8 mt-1">
|
<div class="col-8 mt-1">
|
||||||
<span @click="service.verify_ssl = !!service.verify_ssl" class="switch float-left">
|
<span @click="service.verify_ssl = !!service.verify_ssl" class="switch float-left">
|
||||||
<input v-model="service.verify_ssl" type="checkbox" name="verify_ssl-option" class="switch" id="switch-verify-ssl" v-bind:checked="service.verify_ssl">
|
<input v-model="service.verify_ssl" type="checkbox" name="verify_ssl-option" class="switch" id="switch-verify-ssl" v-bind:checked="service.verify_ssl">
|
||||||
|
@ -128,7 +121,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="order" class="col-sm-4 col-form-label">Notifications</label>
|
<label class="col-sm-4 col-form-label">Notifications</label>
|
||||||
<div class="col-8 mt-1">
|
<div class="col-8 mt-1">
|
||||||
<span @click="service.allow_notifications = !!service.allow_notifications" class="switch float-left">
|
<span @click="service.allow_notifications = !!service.allow_notifications" class="switch float-left">
|
||||||
<input v-model="service.allow_notifications" type="checkbox" name="allow_notifications-option" class="switch" id="switch-notifications" v-bind:checked="service.allow_notifications">
|
<input v-model="service.allow_notifications" type="checkbox" name="allow_notifications-option" class="switch" id="switch-notifications" v-bind:checked="service.allow_notifications">
|
||||||
|
@ -136,8 +129,17 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="service.allow_notifications" class="form-group row">
|
||||||
|
<label class="col-sm-4 col-form-label">Notify All Changes</label>
|
||||||
|
<div class="col-8 mt-1">
|
||||||
|
<span @click="service.notify_all_changes = !!service.notify_all_changes" class="switch float-left">
|
||||||
|
<input v-model="service.notify_all_changes" type="checkbox" name="notify_all-option" class="switch" id="notify_all" v-bind:checked="service.notify_all_changes">
|
||||||
|
<label for="notify_all">Continuously notify when service is failing.</label>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="order" class="col-sm-4 col-form-label">Visible</label>
|
<label class="col-sm-4 col-form-label">Visible</label>
|
||||||
<div class="col-8 mt-1">
|
<div class="col-8 mt-1">
|
||||||
<span @click="service.public = !!service.public" class="switch float-left">
|
<span @click="service.public = !!service.public" class="switch float-left">
|
||||||
<input v-model="service.public" type="checkbox" name="public-option" class="switch" id="switch-public" v-bind:checked="service.public">
|
<input v-model="service.public" type="checkbox" name="public-option" class="switch" id="switch-public" v-bind:checked="service.public">
|
||||||
|
@ -157,7 +159,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../components/API";
|
import Api from "../API";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FormService',
|
name: 'FormService',
|
||||||
|
@ -181,6 +183,7 @@
|
||||||
order: 1,
|
order: 1,
|
||||||
verify_ssl: true,
|
verify_ssl: true,
|
||||||
allow_notifications: true,
|
allow_notifications: true,
|
||||||
|
notify_all_changes: true,
|
||||||
public: true,
|
public: true,
|
||||||
},
|
},
|
||||||
groups: [],
|
groups: [],
|
||||||
|
@ -203,8 +206,7 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
async saveService(e) {
|
async saveService() {
|
||||||
e.preventDefault()
|
|
||||||
let s = this.service
|
let s = this.service
|
||||||
delete s.failures
|
delete s.failures
|
||||||
delete s.created_at
|
delete s.created_at
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="container col-md-7 col-sm-12 mt-2 sm-container">
|
<div class="container col-md-7 col-sm-12 mt-2 sm-container">
|
||||||
<div class="col-12 col-md-8 offset-md-2 mb-4">
|
<div class="col-12 col-md-8 offset-md-2 mb-4">
|
||||||
<img class="col-12 mt-5 mt-md-0" src="/public/banner.png">
|
<img class="col-12 mt-5 mt-md-0" src="/banner.png">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
@ -96,7 +96,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../components/API";
|
import Api from "../API";
|
||||||
import Index from "../pages/Index";
|
import Index from "../pages/Index";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -5,39 +5,39 @@
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'ToggleSwitch',
|
name: 'ToggleSwitch',
|
||||||
props: {
|
props: {
|
||||||
service: {
|
service: {
|
||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
icon: "toggle-on",
|
icon: "toggle-on",
|
||||||
running: true
|
running: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
if (this.service.online) {
|
if (this.service.online) {
|
||||||
this.running = true
|
this.running = true
|
||||||
this.icon = "toggle-on"
|
this.icon = "toggle-on"
|
||||||
} else {
|
} else {
|
||||||
this.running = false
|
this.running = false
|
||||||
this.icon = "toggle-off"
|
this.icon = "toggle-off"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleChecking() {
|
toggleChecking() {
|
||||||
if (this.running) {
|
if (this.running) {
|
||||||
this.icon = "toggle-off"
|
this.icon = "toggle-off"
|
||||||
} else {
|
} else {
|
||||||
this.icon = "toggle-on"
|
this.icon = "toggle-on"
|
||||||
|
}
|
||||||
|
this.running = !this.running
|
||||||
|
},
|
||||||
}
|
}
|
||||||
this.running = !this.running
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
|
|
@ -55,7 +55,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../components/API";
|
import Api from "../API";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'FormUser',
|
name: 'FormUser',
|
||||||
|
|
|
@ -12,7 +12,6 @@ import "./icons"
|
||||||
Vue.component('apexchart', VueApexCharts)
|
Vue.component('apexchart', VueApexCharts)
|
||||||
|
|
||||||
Vue.use(VueRouter);
|
Vue.use(VueRouter);
|
||||||
Vue.use(require('vue-moment'));
|
|
||||||
|
|
||||||
Vue.config.productionTip = false
|
Vue.config.productionTip = false
|
||||||
new Vue({
|
new Vue({
|
||||||
|
|
|
@ -1,45 +1,51 @@
|
||||||
import Vue from "vue";
|
import Vue from "vue";
|
||||||
|
const { zonedTimeToUtc, utcToZonedTime, subSeconds, parse, parseISO, getUnixTime, fromUnixTime, format, differenceInSeconds, formatDistanceToNow, formatDistance } = require('date-fns')
|
||||||
|
|
||||||
export default Vue.mixin({
|
export default Vue.mixin({
|
||||||
methods: {
|
methods: {
|
||||||
now() {
|
now() {
|
||||||
return Math.round(new Date().getTime() / 1000)
|
return new Date()
|
||||||
},
|
},
|
||||||
current() {
|
current() {
|
||||||
return new Date()
|
return parse(new Date())
|
||||||
},
|
},
|
||||||
ago(seconds) {
|
utc(val) {
|
||||||
return this.now() - seconds
|
return fromUnixTime(this.toUnix(val) + val.getTimezoneOffset() * 60 * 1000)
|
||||||
|
},
|
||||||
|
ago(t1) {
|
||||||
|
return formatDistanceToNow(t1)
|
||||||
|
},
|
||||||
|
nowSubtract(seconds) {
|
||||||
|
return subSeconds(new Date(), seconds)
|
||||||
},
|
},
|
||||||
duration(t1, t2) {
|
duration(t1, t2) {
|
||||||
const val = (this.toUnix(t1) - this.toUnix(t2))
|
return formatDistance(t1, t2)
|
||||||
if (val <= 59) {
|
|
||||||
return this.$moment.duration(val, 'seconds').get('seconds') + " seconds ago"
|
|
||||||
}
|
|
||||||
return this.$moment.duration(val, 'seconds').humanize();
|
|
||||||
},
|
},
|
||||||
niceDate(val) {
|
niceDate(val) {
|
||||||
return this.parseTime(val).format('LLLL')
|
return this.parseTime(val).format('LLLL')
|
||||||
},
|
},
|
||||||
parseTime(val) {
|
parseTime(val) {
|
||||||
return this.$moment(val, this.$moment.ISO_8601, true)
|
return parseISO(val)
|
||||||
},
|
},
|
||||||
toLocal(val, suf='at') {
|
toLocal(val, suf = 'at') {
|
||||||
return this.parseTime(val).local().format(`dddd, MMM Do \\${suf} h:mma`)
|
const t = this.parseTime(val)
|
||||||
|
return format(t, `EEEE, MMM do h:mma`)
|
||||||
|
},
|
||||||
|
toUnix(val) {
|
||||||
|
return getUnixTime(val)
|
||||||
},
|
},
|
||||||
toUnix(val) {
|
|
||||||
return this.$moment(val).utc().unix().valueOf()
|
|
||||||
},
|
|
||||||
fromUnix(val) {
|
fromUnix(val) {
|
||||||
return this.$moment.unix(val).utc()
|
return fromUnixTime(val)
|
||||||
|
},
|
||||||
|
isBetween(t1, t2) {
|
||||||
|
return differenceInSeconds(parseISO(t1), parseISO(t2)) > 0
|
||||||
|
},
|
||||||
|
hour() {
|
||||||
|
return 3600
|
||||||
|
},
|
||||||
|
day() {
|
||||||
|
return 3600 * 24
|
||||||
},
|
},
|
||||||
isBetween(t1, t2) {
|
|
||||||
const now = this.$moment(t1).utc().valueOf()
|
|
||||||
const sub = this.$moment(t2).utc().valueOf()
|
|
||||||
return (now - sub) > 0
|
|
||||||
},
|
|
||||||
hour(){ return 3600 },
|
|
||||||
day() { return 3600 * 24 },
|
|
||||||
serviceLink(service) {
|
serviceLink(service) {
|
||||||
if (!service) {
|
if (!service) {
|
||||||
return ""
|
return ""
|
||||||
|
@ -53,10 +59,10 @@ export default Vue.mixin({
|
||||||
isInt(n) {
|
isInt(n) {
|
||||||
return n % 1 === 0;
|
return n % 1 === 0;
|
||||||
},
|
},
|
||||||
loggedIn() {
|
loggedIn() {
|
||||||
const core = this.$store.getters.core
|
const core = this.$store.getters.core
|
||||||
return core.logged_in === true
|
return core.logged_in === true
|
||||||
},
|
},
|
||||||
iconName(name) {
|
iconName(name) {
|
||||||
switch (name) {
|
switch (name) {
|
||||||
case "fas fa-terminal":
|
case "fas fa-terminal":
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../components/API"
|
import Api from "../API"
|
||||||
import TopNav from "../components/Dashboard/TopNav";
|
import TopNav from "../components/Dashboard/TopNav";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from '../components/API';
|
import Api from '../API';
|
||||||
import Group from '../components/Index/Group';
|
import Group from '../components/Index/Group';
|
||||||
import Header from '../components/Index/Header';
|
import Header from '../components/Index/Header';
|
||||||
import MessageBlock from '../components/Index/MessageBlock';
|
import MessageBlock from '../components/Index/MessageBlock';
|
||||||
|
@ -30,36 +30,36 @@ import ServiceBlock from '../components/Service/ServiceBlock';
|
||||||
|
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Index',
|
name: 'Index',
|
||||||
components: {
|
components: {
|
||||||
ServiceBlock,
|
ServiceBlock,
|
||||||
MessageBlock,
|
MessageBlock,
|
||||||
Group,
|
Group,
|
||||||
Header
|
Header
|
||||||
},
|
},
|
||||||
data () {
|
data() {
|
||||||
return {
|
return {
|
||||||
logged_in: false
|
logged_in: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
this.logged_in = this.loggedIn()
|
this.logged_in = this.loggedIn()
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
inRange(message) {
|
inRange(message) {
|
||||||
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) {
|
clickService(s) {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.$refs.s.scrollTop = 0;
|
this.$refs.s.scrollTop = 0;
|
||||||
});
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../components/API";
|
import Api from "../API";
|
||||||
import FormLogin from '../forms/Login';
|
import FormLogin from '../forms/Login';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -3,61 +3,55 @@
|
||||||
<p v-if="logs.length === 0" class="text-monospace sm">
|
<p v-if="logs.length === 0" class="text-monospace sm">
|
||||||
Loading Logs...
|
Loading Logs...
|
||||||
</p>
|
</p>
|
||||||
<p v-for="(log, index) in logs.reverse()" class="text-monospace sm">{{log}}</p>
|
<p v-for="(log, index) in logs" class="text-monospace sm">{{log}}</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../components/API";
|
import Api from "../API";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Logs',
|
name: 'Logs',
|
||||||
components: {
|
components: {},
|
||||||
|
data() {
|
||||||
},
|
return {
|
||||||
data () {
|
logs: [],
|
||||||
return {
|
last: "",
|
||||||
logs: [],
|
t: null
|
||||||
last: "",
|
}
|
||||||
t: null
|
},
|
||||||
}
|
async created() {
|
||||||
},
|
await this.getLogs()
|
||||||
created() {
|
if (!this.t) {
|
||||||
if (!this.t) {
|
this.t = setInterval(async () => {
|
||||||
this.t = setInterval(() => {
|
await this.lastLog()
|
||||||
this.lastLog()
|
}, 650)
|
||||||
}, 650)
|
}
|
||||||
}
|
},
|
||||||
},
|
|
||||||
async mounted() {
|
|
||||||
await this.getLogs()
|
|
||||||
},
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
clearInterval(this.t)
|
clearInterval(this.t)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
cleanLog(l) {
|
cleanLog(l) {
|
||||||
const splitLog = l.split(": ")
|
const splitLog = l.split(": ")
|
||||||
const last = splitLog.slice(1);
|
const last = splitLog.slice(1);
|
||||||
return last.join(": ")
|
return last.join(": ")
|
||||||
},
|
},
|
||||||
async getLogs() {
|
async getLogs() {
|
||||||
const logs = await Api.logs()
|
const logs = await Api.logs()
|
||||||
this.logs = logs.reverse()
|
const l = logs.reverse()
|
||||||
this.last = this.cleanLog(this.logs[this.logs.length-1])
|
this.last = this.cleanLog(l[l.length - 1])
|
||||||
},
|
this.logs = l
|
||||||
async lastLog() {
|
},
|
||||||
const log = await Api.logs_last()
|
async lastLog() {
|
||||||
const cleanLast = this.cleanLog(log)
|
const log = await Api.logs_last()
|
||||||
|
const cleanLast = this.cleanLog(log)
|
||||||
if (this.last !== cleanLast) {
|
if (this.last !== cleanLast) {
|
||||||
this.last = cleanLast
|
this.last = cleanLast
|
||||||
this.logs.reverse().push(log)
|
this.logs.reverse().push(log)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -7,50 +7,37 @@
|
||||||
{{service.online ? "ONLINE" : "OFFLINE"}}
|
{{service.online ? "ONLINE" : "OFFLINE"}}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
<h4 class="mt-2"><router-link to="/">{{$store.getters.core.name}}</router-link> - {{service.name}}
|
<h4 class="mt-2">
|
||||||
|
<router-link to="/">{{$store.getters.core.name}}</router-link> - {{service.name}}
|
||||||
<span class="badge float-right d-none d-md-block" :class="{'bg-success': service.online, 'bg-danger': !service.online}">
|
<span class="badge float-right d-none d-md-block" :class="{'bg-success': service.online, 'bg-danger': !service.online}">
|
||||||
{{service.online ? "ONLINE" : "OFFLINE"}}
|
{{service.online ? "ONLINE" : "OFFLINE"}}
|
||||||
</span>
|
</span>
|
||||||
</h4>
|
</h4>
|
||||||
|
|
||||||
<div class="row stats_area mt-5 mb-5">
|
<ServiceTopStats :service="service"/>
|
||||||
<div class="col-4">
|
|
||||||
<span class="lg_number">{{service.online_24_hours}}%</span>
|
|
||||||
Online last 24 Hours
|
|
||||||
</div>
|
|
||||||
<div class="col-4">
|
|
||||||
<span class="lg_number">31ms</span>
|
|
||||||
Average Response
|
|
||||||
</div>
|
|
||||||
<div class="col-4">
|
|
||||||
<span class="lg_number">85.70%</span>
|
|
||||||
Total Uptime
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-for="(message, index) in messages" v-if="messageInRange(message)">
|
<div v-for="(message, index) in messages" v-if="messageInRange(message)">
|
||||||
<MessageBlock :message="message"/>
|
<MessageBlock :message="message"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="row mt-5 mb-4">
|
||||||
|
<span class="col-6 font-2">
|
||||||
|
<flatPickr v-model="start_time" type="text" name="start_time" class="form-control form-control-plaintext" id="start_time" value="0001-01-01T00:00:00Z" required />
|
||||||
|
</span>
|
||||||
|
<span class="col-6 font-2">
|
||||||
|
<flatPickr v-model="end_time" type="text" name="end_time" class="form-control form-control-plaintext" id="end_time" value="0001-01-01T00:00:00Z" required />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div v-if="series" class="service-chart-container">
|
<div v-if="series" class="service-chart-container">
|
||||||
<apexchart width="100%" height="420" type="area" :options="chartOptions" :series="series"></apexchart>
|
<apexchart width="100%" height="420" type="area" :options="chartOptions" :series="series"></apexchart>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="series" class="service-chart-heatmap">
|
<div class="service-chart-heatmap mt-3 mb-4">
|
||||||
<apexchart width="100%" height="215" type="heatmap" :options="chartOptions" :series="series"></apexchart>
|
<ServiceHeatmap :service="service"/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form id="service_date_form" class="col-12 mt-2 mb-3">
|
<nav v-if="service.failures" class="nav nav-pills flex-column flex-sm-row mt-3" id="service_tabs">
|
||||||
<input type="text" class="d-none" name="start" id="service_start" data-input>
|
|
||||||
<span data-toggle title="toggle" id="start_date" class="text-muted small float-left pointer mt-2">Thu, 09 Jan 2020 to Thu, 16 Jan 2020</span>
|
|
||||||
<button type="submit" class="btn btn-light btn-sm mt-2">Set Timeframe</button>
|
|
||||||
<input type="text" class="d-none" name="end" id="service_end" data-input>
|
|
||||||
|
|
||||||
<div id="start_container"></div>
|
|
||||||
<div id="end_container"></div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<nav class="nav nav-pills flex-column flex-sm-row mt-3" id="service_tabs">
|
|
||||||
<a @click="tab='failures'" class="flex-sm-fill text-sm-center nav-link active">Failures</a>
|
<a @click="tab='failures'" class="flex-sm-fill text-sm-center nav-link active">Failures</a>
|
||||||
<a @click="tab='incidents'" class="flex-sm-fill text-sm-center nav-link">Incidents</a>
|
<a @click="tab='incidents'" class="flex-sm-fill text-sm-center nav-link">Incidents</a>
|
||||||
<a @click="tab='checkins'" v-if="$store.getters.token" class="flex-sm-fill text-sm-center nav-link">Checkins</a>
|
<a @click="tab='checkins'" v-if="$store.getters.token" class="flex-sm-fill text-sm-center nav-link">Checkins</a>
|
||||||
|
@ -58,7 +45,7 @@
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
<div class="tab-content">
|
<div v-if="service.failures" class="tab-content">
|
||||||
<div class="tab-pane fade active show">
|
<div class="tab-pane fade active show">
|
||||||
<ServiceFailures :service="service"/>
|
<ServiceFailures :service="service"/>
|
||||||
</div>
|
</div>
|
||||||
|
@ -98,10 +85,14 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from "../components/API"
|
import Api from "../API"
|
||||||
import MessageBlock from '../components/Index/MessageBlock';
|
import MessageBlock from '../components/Index/MessageBlock';
|
||||||
import ServiceFailures from '../components/Service/ServiceFailures';
|
import ServiceFailures from '../components/Service/ServiceFailures';
|
||||||
import Checkin from "../forms/Checkin";
|
import Checkin from "../forms/Checkin";
|
||||||
|
import ServiceHeatmap from "@/components/Service/ServiceHeatmap";
|
||||||
|
import ServiceTopStats from "@/components/Service/ServiceTopStats";
|
||||||
|
import flatPickr from 'vue-flatpickr-component';
|
||||||
|
import 'flatpickr/dist/flatpickr.css';
|
||||||
|
|
||||||
const axisOptions = {
|
const axisOptions = {
|
||||||
labels: {
|
labels: {
|
||||||
|
@ -128,143 +119,145 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Service',
|
name: 'Service',
|
||||||
components: {
|
components: {
|
||||||
ServiceFailures,
|
ServiceTopStats,
|
||||||
MessageBlock,
|
ServiceHeatmap,
|
||||||
Checkin
|
ServiceFailures,
|
||||||
},
|
MessageBlock,
|
||||||
data () {
|
Checkin,
|
||||||
return {
|
flatPickr
|
||||||
id: null,
|
},
|
||||||
tab: "failures",
|
data() {
|
||||||
service: {},
|
return {
|
||||||
authenticated: false,
|
id: null,
|
||||||
ready: false,
|
tab: "failures",
|
||||||
data: null,
|
service: {},
|
||||||
messages: [],
|
authenticated: false,
|
||||||
failures: [],
|
ready: false,
|
||||||
chartOptions: {
|
data: null,
|
||||||
chart: {
|
messages: [],
|
||||||
height: 500,
|
failures: [],
|
||||||
width: "100%",
|
start_time: "",
|
||||||
type: "area",
|
end_time: "",
|
||||||
animations: {
|
chartOptions: {
|
||||||
enabled: true,
|
chart: {
|
||||||
initialAnimation: {
|
height: 500,
|
||||||
enabled: true
|
width: "100%",
|
||||||
}
|
type: "area",
|
||||||
},
|
animations: {
|
||||||
selection: {
|
enabled: true,
|
||||||
enabled: false
|
initialAnimation: {
|
||||||
},
|
enabled: true
|
||||||
zoom: {
|
}
|
||||||
enabled: false
|
},
|
||||||
},
|
selection: {
|
||||||
toolbar: {
|
enabled: false
|
||||||
show: false
|
},
|
||||||
},
|
zoom: {
|
||||||
},
|
enabled: false
|
||||||
grid: {
|
},
|
||||||
show: true,
|
toolbar: {
|
||||||
padding: {
|
show: false
|
||||||
top: 0,
|
},
|
||||||
right: 0,
|
},
|
||||||
bottom: 0,
|
grid: {
|
||||||
left: 0,
|
show: true,
|
||||||
}
|
padding: {
|
||||||
},
|
top: 0,
|
||||||
xaxis: {
|
right: 0,
|
||||||
type: "datetime",
|
bottom: 0,
|
||||||
...axisOptions
|
left: 0,
|
||||||
},
|
}
|
||||||
yaxis: {
|
},
|
||||||
...axisOptions
|
xaxis: {
|
||||||
},
|
type: "datetime",
|
||||||
tooltip: {
|
...axisOptions
|
||||||
enabled: false,
|
},
|
||||||
marker: {
|
yaxis: {
|
||||||
show: false,
|
...axisOptions
|
||||||
},
|
},
|
||||||
x: {
|
tooltip: {
|
||||||
show: false,
|
enabled: false,
|
||||||
}
|
marker: {
|
||||||
},
|
show: false,
|
||||||
legend: {
|
},
|
||||||
show: false,
|
x: {
|
||||||
},
|
show: false,
|
||||||
dataLabels: {
|
}
|
||||||
enabled: false
|
},
|
||||||
},
|
legend: {
|
||||||
floating: true,
|
show: false,
|
||||||
axisTicks: {
|
},
|
||||||
show: false
|
dataLabels: {
|
||||||
},
|
enabled: false
|
||||||
axisBorder: {
|
},
|
||||||
show: false
|
floating: true,
|
||||||
},
|
axisTicks: {
|
||||||
fill: {
|
show: false
|
||||||
colors: ["#48d338"],
|
},
|
||||||
opacity: 1,
|
axisBorder: {
|
||||||
type: 'solid'
|
show: false
|
||||||
},
|
},
|
||||||
stroke: {
|
fill: {
|
||||||
show: true,
|
colors: ["#48d338"],
|
||||||
curve: 'smooth',
|
opacity: 1,
|
||||||
lineCap: 'butt',
|
type: 'solid'
|
||||||
colors: ["#3aa82d"],
|
},
|
||||||
|
stroke: {
|
||||||
|
show: true,
|
||||||
|
curve: 'smooth',
|
||||||
|
lineCap: 'butt',
|
||||||
|
colors: ["#3aa82d"],
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [{
|
||||||
|
data: []
|
||||||
|
}],
|
||||||
|
heatmap_data: [],
|
||||||
|
config: {
|
||||||
|
enableTime: true
|
||||||
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
series: [{
|
|
||||||
data: []
|
|
||||||
}],
|
|
||||||
heatmap_data: []
|
|
||||||
}
|
|
||||||
},
|
|
||||||
async mounted() {
|
async mounted() {
|
||||||
const id = this.$attrs.id
|
const id = this.$attrs.id
|
||||||
this.id = id
|
this.id = id
|
||||||
let service;
|
let service;
|
||||||
if (this.isInt(id)) {
|
if (this.isInt(id)) {
|
||||||
service = this.$store.getters.serviceById(id)
|
service = this.$store.getters.serviceById(id)
|
||||||
} else {
|
} else {
|
||||||
service = this.$store.getters.serviceByPermalink(id)
|
service = this.$store.getters.serviceByPermalink(id)
|
||||||
}
|
}
|
||||||
this.service = service
|
this.service = service
|
||||||
this.getService(service)
|
this.getService(service)
|
||||||
this.messages = this.$store.getters.serviceMessages(service.id)
|
this.messages = this.$store.getters.serviceMessages(service.id)
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
messageInRange(message) {
|
messageInRange(message) {
|
||||||
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
|
||||||
},
|
},
|
||||||
async getService(s) {
|
async getService(s) {
|
||||||
await this.chartHits()
|
await this.chartHits()
|
||||||
await this.heatmapData()
|
await this.serviceFailures()
|
||||||
await this.serviceFailures()
|
},
|
||||||
},
|
async serviceFailures() {
|
||||||
async serviceFailures() {
|
this.failures = await Api.service_failures(this.service.id, this.now() - 3600, this.now(), 15)
|
||||||
this.failures = await Api.service_failures(this.service.id, this.now() - 3600, this.now(), 15)
|
},
|
||||||
},
|
async chartHits() {
|
||||||
async chartHits() {
|
const start = this.nowSubtract((3600 * 24) * 7)
|
||||||
this.data = await Api.service_hits(this.service.id, 0, 99999999999, "hour")
|
this.start_time = start
|
||||||
this.series = [{
|
this.end_time = new Date()
|
||||||
name: this.service.name,
|
this.data = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(new Date()), "hour")
|
||||||
...this.data
|
this.series = [{
|
||||||
}]
|
name: this.service.name,
|
||||||
this.ready = true
|
...this.data
|
||||||
},
|
}]
|
||||||
async heatmapData() {
|
this.ready = true
|
||||||
this.data = await Api.service_heatmap(this.service.id, 0, 99999999999, "hour")
|
}
|
||||||
this.series = [{
|
}
|
||||||
name: this.service.name,
|
|
||||||
...this.data
|
|
||||||
}]
|
|
||||||
this.ready = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
<div class="row align-content-center">
|
<div class="row align-content-center">
|
||||||
<img class="rounded text-center" width="300" height="300" :src="qrcode">
|
<img class="rounded text-center" width="300" height="300" :src="qrcode">
|
||||||
</div>
|
</div>
|
||||||
<a class="btn btn-sm btn-primary" href=statping://setup?domain=https://demo.statping.com&api=6b05b48f4b3a1460f3864c31b26cab6a27dbaff9>Open in Statping App</a>
|
<a class="btn btn-sm btn-primary" :href="qrurl">Open in Statping App</a>
|
||||||
<a href="settings/export" class="btn btn-sm btn-secondary">Export Settings</a>
|
<a href="settings/export" class="btn btn-sm btn-secondary">Export Settings</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,32 +53,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane fade" v-bind:class="{active: liClass('v-pills-style-tab'), show: liClass('v-pills-style-tab')}" id="v-pills-style" role="tabpanel" aria-labelledby="v-pills-style-tab">
|
<div class="tab-pane fade" v-bind:class="{active: liClass('v-pills-style-tab'), show: liClass('v-pills-style-tab')}" id="v-pills-style" role="tabpanel" aria-labelledby="v-pills-style-tab">
|
||||||
|
|
||||||
<ThemeEditor :core="core"/>
|
<ThemeEditor :core="core"/>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="tab-pane fade" v-bind:class="{active: liClass('v-pills-cache-tab'), show: liClass('v-pills-cache-tab')}" id="v-pills-cache" role="tabpanel" aria-labelledby="v-pills-cache-tab">
|
<div class="tab-pane fade" v-bind:class="{active: liClass('v-pills-cache-tab'), show: liClass('v-pills-cache-tab')}" id="v-pills-cache" role="tabpanel" aria-labelledby="v-pills-cache-tab">
|
||||||
<h3>Cache</h3>
|
<Cache/>
|
||||||
<table class="table">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col">URL</th>
|
|
||||||
<th scope="col">Size</th>
|
|
||||||
<th scope="col">Expiration</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
|
|
||||||
<tr v-for="(cache, index) in cache">
|
|
||||||
<td>{{cache.url}}</td>
|
|
||||||
<td>{{cache.size}}</td>
|
|
||||||
<td>{{expireTime(cache.expiration)}}</td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<a @click.prevent="clearCache" href="#" class="btn btn-danger btn-block">Clear Cache</a>
|
|
||||||
</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 $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`">
|
||||||
|
@ -97,54 +76,50 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import Api from '../components/API';
|
import Api from '../API';
|
||||||
import CoreSettings from '../forms/CoreSettings';
|
import CoreSettings from '../forms/CoreSettings';
|
||||||
import FormIntegration from '../forms/Integration';
|
import FormIntegration from '../forms/Integration';
|
||||||
import Notifier from "../forms/Notifier";
|
import Notifier from "../forms/Notifier";
|
||||||
import ThemeEditor from "../components/Dashboard/ThemeEditor";
|
import ThemeEditor from "../components/Dashboard/ThemeEditor";
|
||||||
|
import Cache from "@/components/Dashboard/Cache";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Settings',
|
name: 'Settings',
|
||||||
components: {
|
components: {
|
||||||
ThemeEditor,
|
Cache,
|
||||||
FormIntegration,
|
ThemeEditor,
|
||||||
Notifier,
|
FormIntegration,
|
||||||
CoreSettings
|
Notifier,
|
||||||
},
|
CoreSettings
|
||||||
data () {
|
},
|
||||||
return {
|
data() {
|
||||||
tab: "v-pills-home-tab",
|
return {
|
||||||
qrcode: "",
|
tab: "v-pills-home-tab",
|
||||||
core: this.$store.getters.core,
|
qrcode: "",
|
||||||
cache: [],
|
qrurl: "",
|
||||||
}
|
core: this.$store.getters.core
|
||||||
},
|
}
|
||||||
async mounted () {
|
},
|
||||||
|
async mounted() {
|
||||||
this.cache = await Api.cache()
|
this.cache = await Api.cache()
|
||||||
},
|
},
|
||||||
async created() {
|
async created() {
|
||||||
const qrurl = `statping://setup?domain=${core.domain}&api=${core.api_secret}`
|
const c = this.$store.getters.core
|
||||||
this.qrcode = "https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=" + encodeURI(qrurl)
|
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)
|
||||||
beforeMount() {
|
},
|
||||||
|
beforeMount() {
|
||||||
|
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
changeTab (e) {
|
changeTab(e) {
|
||||||
this.tab = e.target.id
|
this.tab = e.target.id
|
||||||
},
|
},
|
||||||
liClass (id) {
|
liClass(id) {
|
||||||
return this.tab === id
|
return this.tab === id
|
||||||
},
|
}
|
||||||
expireTime(ex) {
|
}
|
||||||
return this.toLocal(ex)
|
|
||||||
},
|
|
||||||
async clearCache () {
|
|
||||||
await Api.clearCache()
|
|
||||||
this.cache = await Api.cache()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||||
|
|
|
@ -13,7 +13,7 @@ import Service from "./pages/Service";
|
||||||
import VueRouter from "vue-router";
|
import VueRouter from "vue-router";
|
||||||
import Setup from "./forms/Setup";
|
import Setup from "./forms/Setup";
|
||||||
|
|
||||||
import Api from "./components/API";
|
import Api from "./API";
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
{
|
{
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Vuex from 'vuex'
|
import Vuex from 'vuex'
|
||||||
import Vue from 'vue'
|
import Vue from 'vue'
|
||||||
import Api from "./components/API"
|
import Api from "./API"
|
||||||
|
|
||||||
Vue.use(Vuex)
|
Vue.use(Vuex)
|
||||||
|
|
||||||
|
@ -105,7 +105,7 @@ export default new Vuex.Store({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async loadRequired (context) {
|
async loadRequired(context) {
|
||||||
const core = await Api.core()
|
const core = await Api.core()
|
||||||
context.commit("setCore", core);
|
context.commit("setCore", core);
|
||||||
const groups = await Api.groups()
|
const groups = await Api.groups()
|
||||||
|
@ -125,7 +125,7 @@ export default new Vuex.Store({
|
||||||
// }
|
// }
|
||||||
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()
|
const core = await Api.core()
|
||||||
context.commit("setCore", core);
|
context.commit("setCore", core);
|
||||||
const groups = await Api.groups()
|
const groups = await Api.groups()
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
import API from "@/API"
|
||||||
|
import { shallowMount, mount } from '@vue/test-utils'
|
||||||
|
|
||||||
|
describe('API Tests', async () => {
|
||||||
|
|
||||||
|
await it('should get core info', async () => {
|
||||||
|
|
||||||
|
const wrapper = mount(API)
|
||||||
|
|
||||||
|
const core = await wrapper.core()
|
||||||
|
expect(core).toBe(9)
|
||||||
|
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,24 @@
|
||||||
|
// import Vuex from 'vuex'
|
||||||
|
// import Api from '../src/components/API'
|
||||||
|
// import thisStore from '../src/store'
|
||||||
|
//
|
||||||
|
// import { createLocalVue } from '@vue/test-utils'
|
||||||
|
|
||||||
|
// const localVue = createLocalVue()
|
||||||
|
// localVue.use(Vuex)
|
||||||
|
//
|
||||||
|
// describe('MyName test', async () => {
|
||||||
|
// const services = [
|
||||||
|
// { id: 1, title: 'Apple', order_id: 3 },
|
||||||
|
// { id: 2, title: 'Orange', order_id: 2},
|
||||||
|
// { id: 3, title: 'Carrot', order_id: 1}
|
||||||
|
// ]
|
||||||
|
//
|
||||||
|
// const store = new Vuex.Store(thisStore)
|
||||||
|
//
|
||||||
|
// await store.dispatch('loadRequired')
|
||||||
|
//
|
||||||
|
// console.log(store.getters.services)
|
||||||
|
//
|
||||||
|
// expect(store.getters.services).toEqual(services.slice(0, 20))
|
||||||
|
// })
|
|
@ -0,0 +1,32 @@
|
||||||
|
import { shallowMount, mount } from '@vue/test-utils'
|
||||||
|
|
||||||
|
import FormLogin from "../../src/forms/Login.vue"
|
||||||
|
|
||||||
|
const wrapper = shallowMount(FormLogin)
|
||||||
|
|
||||||
|
describe('Login Form', () => {
|
||||||
|
|
||||||
|
it('has a created hook', () => {
|
||||||
|
expect(typeof FormLogin.methods.checkForm).toBe('function')
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should login', async () => {
|
||||||
|
|
||||||
|
expect(wrapper.vm.$data.loading).toBe(false)
|
||||||
|
|
||||||
|
expect(wrapper.vm.$data.username).toBe('')
|
||||||
|
expect(wrapper.vm.$data.password).toBe('')
|
||||||
|
|
||||||
|
wrapper.setData({ username: 'admin' })
|
||||||
|
wrapper.setData({ password: 'admin' })
|
||||||
|
|
||||||
|
wrapper.find('button').trigger('click')
|
||||||
|
|
||||||
|
await wrapper.vm.$nextTick()
|
||||||
|
|
||||||
|
expect(wrapper.vm.$data.loading).toBe(true)
|
||||||
|
done()
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
|
@ -0,0 +1,17 @@
|
||||||
|
// import { mount } from '@vue/test-utils';
|
||||||
|
// import Index from '../../src/pages/Index';
|
||||||
|
//
|
||||||
|
// const wrapper = mount(Index);
|
||||||
|
|
||||||
|
// describe('MyName test', () => {
|
||||||
|
// it('Displays my name when I write it', () => {
|
||||||
|
//
|
||||||
|
// expect(wrapper.vm.$data.logged_in).toBe('My name');
|
||||||
|
//
|
||||||
|
// const input = wrapper.find('input');
|
||||||
|
// input.element.value = 'Stefan';
|
||||||
|
// input.trigger('input');
|
||||||
|
//
|
||||||
|
// expect(wrapper.vm.$data.name).toBe('Stefan');
|
||||||
|
// })
|
||||||
|
// });
|
|
@ -0,0 +1,5 @@
|
||||||
|
require('jsdom-global')()
|
||||||
|
|
||||||
|
global.expect = require('expect')
|
||||||
|
|
||||||
|
|
1295
frontend/yarn.lock
1295
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
|
@ -89,7 +89,6 @@ func apiCoreHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if c.Timezone != app.Timezone {
|
if c.Timezone != app.Timezone {
|
||||||
app.Timezone = c.Timezone
|
app.Timezone = c.Timezone
|
||||||
}
|
}
|
||||||
app.UpdateNotify = c.UpdateNotify
|
|
||||||
app.UseCdn = types.NewNullBool(c.UseCdn.Bool)
|
app.UseCdn = types.NewNullBool(c.UseCdn.Bool)
|
||||||
core.CoreApp, err = core.UpdateCore(app)
|
core.CoreApp, err = core.UpdateCore(app)
|
||||||
returnJson(core.CoreApp, w, r)
|
returnJson(core.CoreApp, w, r)
|
||||||
|
|
|
@ -25,6 +25,9 @@ var handlerFuncs = func(w http.ResponseWriter, r *http.Request) template.FuncMap
|
||||||
"USE_CDN": func() bool {
|
"USE_CDN": func() bool {
|
||||||
return core.CoreApp.UseCdn.Bool
|
return core.CoreApp.UseCdn.Bool
|
||||||
},
|
},
|
||||||
|
"USING_ASSETS": func() bool {
|
||||||
|
return core.CoreApp.UsingAssets()
|
||||||
|
},
|
||||||
"BasePath": func() string {
|
"BasePath": func() string {
|
||||||
return basePath
|
return basePath
|
||||||
},
|
},
|
||||||
|
|
|
@ -70,9 +70,6 @@ func (u *discord) OnSuccess(s *types.Service) {
|
||||||
if !s.Online || !s.SuccessNotified {
|
if !s.Online || !s.SuccessNotified {
|
||||||
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
||||||
var msg interface{}
|
var msg interface{}
|
||||||
if s.UpdateNotify {
|
|
||||||
s.UpdateNotify = false
|
|
||||||
}
|
|
||||||
msg = s.DownText
|
msg = s.DownText
|
||||||
|
|
||||||
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
|
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
|
||||||
|
|
|
@ -193,9 +193,6 @@ func (u *email) OnFailure(s *types.Service, f *types.Failure) {
|
||||||
func (u *email) OnSuccess(s *types.Service) {
|
func (u *email) OnSuccess(s *types.Service) {
|
||||||
if !s.Online || !s.SuccessNotified {
|
if !s.Online || !s.SuccessNotified {
|
||||||
var msg string
|
var msg string
|
||||||
if s.UpdateNotify {
|
|
||||||
s.UpdateNotify = false
|
|
||||||
}
|
|
||||||
msg = s.DownText
|
msg = s.DownText
|
||||||
|
|
||||||
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
||||||
|
|
|
@ -72,9 +72,6 @@ func (u *lineNotifier) OnFailure(s *types.Service, f *types.Failure) {
|
||||||
func (u *lineNotifier) OnSuccess(s *types.Service) {
|
func (u *lineNotifier) OnSuccess(s *types.Service) {
|
||||||
if !s.Online || !s.SuccessNotified {
|
if !s.Online || !s.SuccessNotified {
|
||||||
var msg string
|
var msg string
|
||||||
if s.UpdateNotify {
|
|
||||||
s.UpdateNotify = false
|
|
||||||
}
|
|
||||||
msg = s.DownText
|
msg = s.DownText
|
||||||
|
|
||||||
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
||||||
|
|
|
@ -99,9 +99,6 @@ func (u *mobilePush) OnSuccess(s *types.Service) {
|
||||||
data := dataJson(s, nil)
|
data := dataJson(s, nil)
|
||||||
if !s.Online || !s.SuccessNotified {
|
if !s.Online || !s.SuccessNotified {
|
||||||
var msgStr string
|
var msgStr string
|
||||||
if s.UpdateNotify {
|
|
||||||
s.UpdateNotify = false
|
|
||||||
}
|
|
||||||
msgStr = s.DownText
|
msgStr = s.DownText
|
||||||
|
|
||||||
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
||||||
|
|
|
@ -92,9 +92,6 @@ func (u *telegram) OnSuccess(s *types.Service) {
|
||||||
if !s.Online || !s.SuccessNotified {
|
if !s.Online || !s.SuccessNotified {
|
||||||
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
||||||
var msg interface{}
|
var msg interface{}
|
||||||
if s.UpdateNotify {
|
|
||||||
s.UpdateNotify = false
|
|
||||||
}
|
|
||||||
msg = s.DownText
|
msg = s.DownText
|
||||||
|
|
||||||
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
|
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
|
||||||
|
|
|
@ -102,9 +102,6 @@ func (u *twilio) OnSuccess(s *types.Service) {
|
||||||
if !s.Online || !s.SuccessNotified {
|
if !s.Online || !s.SuccessNotified {
|
||||||
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
||||||
var msg string
|
var msg string
|
||||||
if s.UpdateNotify {
|
|
||||||
s.UpdateNotify = false
|
|
||||||
}
|
|
||||||
msg = s.DownText
|
msg = s.DownText
|
||||||
|
|
||||||
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
|
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
|
||||||
|
|
|
@ -41,7 +41,6 @@ type Core struct {
|
||||||
Setup bool `gorm:"-" json:"setup"`
|
Setup bool `gorm:"-" json:"setup"`
|
||||||
MigrationId int64 `gorm:"column:migration_id" json:"migration_id,omitempty"`
|
MigrationId int64 `gorm:"column:migration_id" json:"migration_id,omitempty"`
|
||||||
UseCdn NullBool `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"`
|
UseCdn NullBool `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"`
|
||||||
UpdateNotify NullBool `gorm:"column:update_notify;default:true" json:"update_notify,omitempty"`
|
|
||||||
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"`
|
||||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||||
|
|
|
@ -33,7 +33,6 @@ type Service struct {
|
||||||
Port int `gorm:"not null;column:port" json:"port" scope:"user,admin"`
|
Port int `gorm:"not null;column:port" json:"port" scope:"user,admin"`
|
||||||
Timeout int `gorm:"default:30;column:timeout" json:"timeout" scope:"user,admin"`
|
Timeout int `gorm:"default:30;column:timeout" json:"timeout" scope:"user,admin"`
|
||||||
Order int `gorm:"default:0;column:order_id" json:"order_id"`
|
Order int `gorm:"default:0;column:order_id" json:"order_id"`
|
||||||
AllowNotifications NullBool `gorm:"default:true;column:allow_notifications" json:"allow_notifications" scope:"user,admin"`
|
|
||||||
VerifySSL NullBool `gorm:"default:false;column:verify_ssl" json:"verify_ssl" scope:"user,admin"`
|
VerifySSL NullBool `gorm:"default:false;column:verify_ssl" json:"verify_ssl" scope:"user,admin"`
|
||||||
Public NullBool `gorm:"default:true;column:public" json:"public"`
|
Public NullBool `gorm:"default:true;column:public" json:"public"`
|
||||||
GroupId int `gorm:"default:0;column:group_id" json:"group_id"`
|
GroupId int `gorm:"default:0;column:group_id" json:"group_id"`
|
||||||
|
@ -53,10 +52,11 @@ type Service struct {
|
||||||
Checkpoint time.Time `gorm:"-" json:"-"`
|
Checkpoint time.Time `gorm:"-" json:"-"`
|
||||||
SleepDuration time.Duration `gorm:"-" json:"-"`
|
SleepDuration time.Duration `gorm:"-" json:"-"`
|
||||||
LastResponse string `gorm:"-" json:"-"`
|
LastResponse string `gorm:"-" json:"-"`
|
||||||
UserNotified bool `gorm:"-" json:"-"` // True if the User was already notified about a Downtime
|
AllowNotifications NullBool `gorm:"default:true;column:allow_notifications" json:"allow_notifications" scope:"user,admin"`
|
||||||
UpdateNotify bool `gorm:"-" json:"-"` // This Variable is a simple copy of `core.CoreApp.UpdateNotify.Bool`
|
UserNotified bool `gorm:"-" json:"-"` // True if the User was already notified about a Downtime
|
||||||
DownText string `gorm:"-" json:"-"` // Contains the current generated Downtime Text
|
UpdateNotify NullBool `gorm:"default:true;column:notify_all_changes" json:"notify_all_changes" scope:"user,admin"` // This Variable is a simple copy of `core.CoreApp.UpdateNotify.Bool`
|
||||||
SuccessNotified bool `gorm:"-" json:"-"` // Is 'true' if the user has already be informed that the Services now again available
|
DownText string `gorm:"-" json:"-"` // Contains the current generated Downtime Text
|
||||||
|
SuccessNotified bool `gorm:"-" json:"-"` // Is 'true' if the user has already be informed that the Services now again available
|
||||||
LastStatusCode int `gorm:"-" json:"status_code"`
|
LastStatusCode int `gorm:"-" json:"status_code"`
|
||||||
LastOnline time.Time `gorm:"-" json:"last_success"`
|
LastOnline time.Time `gorm:"-" json:"last_success"`
|
||||||
Failures []FailureInterface `gorm:"-" json:"failures,omitempty" scope:"user,admin"`
|
Failures []FailureInterface `gorm:"-" json:"failures,omitempty" scope:"user,admin"`
|
||||||
|
|
Loading…
Reference in New Issue