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.Online = false
|
||||
s.SuccessNotified = false
|
||||
s.UpdateNotify = CoreApp.UpdateNotify.Bool
|
||||
s.DownText = s.DowntimeText()
|
||||
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
|
||||
if s.UpdateNotify {
|
||||
if s.UpdateNotify.Bool {
|
||||
// send only if User hasn't been already notified about the Downtime
|
||||
if !s.UserNotified {
|
||||
s.UserNotified = true
|
||||
|
@ -69,7 +69,7 @@ func OnSuccess(s *types.Service) {
|
|||
}
|
||||
|
||||
// check if User wants to receive every Status Change
|
||||
if s.UpdateNotify && s.UserNotified {
|
||||
if s.UpdateNotify.Bool && s.UserNotified {
|
||||
s.UserNotified = false
|
||||
}
|
||||
|
||||
|
|
|
@ -10,12 +10,14 @@ const environment = require('./dev.env');
|
|||
|
||||
const webpackConfig = merge(commonConfig, {
|
||||
mode: 'development',
|
||||
devtool: 'cheap-module-eval-source-map',
|
||||
devtool: 'inline-cheap-module-source-map',
|
||||
output: {
|
||||
path: helpers.root('dist'),
|
||||
publicPath: '/',
|
||||
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: {
|
||||
runtimeChunk: 'single',
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
"serve": "vue-cli-service serve",
|
||||
"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",
|
||||
"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": {
|
||||
"@fortawesome/fontawesome-free-solid": "^5.1.0-3",
|
||||
|
@ -18,7 +19,7 @@
|
|||
"axios": "^0.19.1",
|
||||
"codemirror-colorpicker": "^1.9.66",
|
||||
"core-js": "^3.4.4",
|
||||
"moment": "^2.24.0",
|
||||
"date-fns": "^2.9.0",
|
||||
"querystring": "^0.2.0",
|
||||
"vue": "^2.6.10",
|
||||
"vue-apexcharts": "^1.5.2",
|
||||
|
@ -40,6 +41,7 @@
|
|||
"@babel/preset-env": "~7.8.3",
|
||||
"@vue/babel-preset-app": "^4.1.2",
|
||||
"@vue/cli-plugin-babel": "^4.1.0",
|
||||
"@vue/test-utils": "^1.0.0-beta.31",
|
||||
"babel-eslint": "~10.0",
|
||||
"babel-loader": "~8.0",
|
||||
"compression-webpack-plugin": "~2.0",
|
||||
|
@ -55,10 +57,15 @@
|
|||
"eslint-plugin-promise": "~3.5",
|
||||
"eslint-plugin-standard": "~3.0",
|
||||
"eslint-plugin-vue": "~5.1",
|
||||
"expect": "^25.1.0",
|
||||
"file-loader": "^5.0.2",
|
||||
"friendly-errors-webpack-plugin": "~1.7",
|
||||
"html-webpack-plugin": "^4.0.0-beta.11",
|
||||
"jsdom": "^16.2.0",
|
||||
"jsdom-global": "^3.0.2",
|
||||
"mini-css-extract-plugin": "~0.5",
|
||||
"mocha": "^7.0.1",
|
||||
"mochapack": "^1.1.13",
|
||||
"node-sass": "^4.13.1",
|
||||
"optimize-css-assets-webpack-plugin": "~5.0",
|
||||
"sass-loader": "^8.0.2",
|
||||
|
|
|
@ -10,11 +10,20 @@
|
|||
<base href="{{BasePath}}">
|
||||
{{if USE_CDN}}
|
||||
<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}}
|
||||
<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) { %>
|
||||
<%= headTag %> <% }) %>
|
||||
{{end}}
|
||||
{{end}}
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
|
||||
<script>
|
||||
import Api from './components/API';
|
||||
import Api from './API';
|
||||
import Footer from "./components/Index/Footer";
|
||||
|
||||
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>
|
||||
|
||||
<script>
|
||||
import ServiceInfo from "./ServiceInfo";
|
||||
import ServiceInfo from "../Service/ServiceInfo";
|
||||
|
||||
export default {
|
||||
name: 'DashboardIndex',
|
||||
components: {
|
||||
ServiceInfo
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
name: 'DashboardIndex',
|
||||
components: {
|
||||
ServiceInfo
|
||||
},
|
||||
methods: {
|
||||
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>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
|
|
|
@ -37,7 +37,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../API"
|
||||
import Api from "../../API"
|
||||
import FormMessage from "../../forms/Message";
|
||||
|
||||
export default {
|
||||
|
|
|
@ -99,97 +99,97 @@
|
|||
|
||||
<script>
|
||||
import FormGroup from "../../forms/Group";
|
||||
import Api from "../../components/API";
|
||||
import Api from "../../API";
|
||||
import ToggleSwitch from "../../forms/ToggleSwitch";
|
||||
import draggable from 'vuedraggable'
|
||||
|
||||
export default {
|
||||
name: 'DashboardServices',
|
||||
components: {
|
||||
ToggleSwitch,
|
||||
FormGroup,
|
||||
draggable
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
edit: false,
|
||||
group: {}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
servicesList: {
|
||||
get() {
|
||||
return this.$store.state.servicesInOrder
|
||||
name: 'DashboardServices',
|
||||
components: {
|
||||
ToggleSwitch,
|
||||
FormGroup,
|
||||
draggable
|
||||
},
|
||||
async set(value) {
|
||||
let data = [];
|
||||
value.forEach((s, k) => {
|
||||
data.push({service: s.id, order: k+1})
|
||||
});
|
||||
await Api.services_reorder(data)
|
||||
const services = await Api.services()
|
||||
this.$store.commit('setServices', services)
|
||||
}
|
||||
},
|
||||
groupsList: {
|
||||
get() {
|
||||
return this.$store.state.groupsInOrder
|
||||
data() {
|
||||
return {
|
||||
edit: false,
|
||||
group: {}
|
||||
}
|
||||
},
|
||||
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() {
|
||||
computed: {
|
||||
servicesList: {
|
||||
get() {
|
||||
return this.$store.state.servicesInOrder
|
||||
},
|
||||
async set(value) {
|
||||
let data = [];
|
||||
value.forEach((s, k) => {
|
||||
data.push({service: s.id, order: k + 1})
|
||||
});
|
||||
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) {
|
||||
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: {
|
||||
editChange(v) {
|
||||
this.group = {}
|
||||
this.edit = v
|
||||
},
|
||||
editGroup(g, mode) {
|
||||
this.group = g
|
||||
this.edit = !mode
|
||||
},
|
||||
reordered_services() {
|
||||
},
|
||||
methods: {
|
||||
editChange(v) {
|
||||
this.group = {}
|
||||
this.edit = v
|
||||
},
|
||||
editGroup(g, mode) {
|
||||
this.group = g
|
||||
this.edit = !mode
|
||||
},
|
||||
reordered_services() {
|
||||
|
||||
},
|
||||
saveUpdatedOrder: function (e) {
|
||||
window.console.log("saving...");
|
||||
window.console.log(this.myViews.array()); // this.myViews.array is not a function
|
||||
},
|
||||
serviceGroup(s) {
|
||||
let group = this.$store.getters.groupById(s.group_id)
|
||||
if (group) {
|
||||
return group.name
|
||||
},
|
||||
saveUpdatedOrder: function (e) {
|
||||
window.console.log("saving...");
|
||||
window.console.log(this.myViews.array()); // this.myViews.array is not a function
|
||||
},
|
||||
serviceGroup(s) {
|
||||
let group = this.$store.getters.groupById(s.group_id)
|
||||
if (group) {
|
||||
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>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../API"
|
||||
import Api from "../../API"
|
||||
import FormUser from "../../forms/User";
|
||||
|
||||
export default {
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
<template>
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<FormService :in_service="service"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<FormService :in_service="service"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import FormGroup from "../../forms/Group";
|
||||
import Api from "../../components/API";
|
||||
import Api from "../../API";
|
||||
import ToggleSwitch from "../../forms/ToggleSwitch";
|
||||
import draggable from 'vuedraggable'
|
||||
import FormService from "../../forms/Service";
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../API";
|
||||
import Api from "../../API";
|
||||
|
||||
// require component
|
||||
import { codemirror } from 'vue-codemirror'
|
||||
|
@ -136,7 +136,6 @@
|
|||
this.error = null
|
||||
}
|
||||
this.pending = false
|
||||
window.console.log(resp)
|
||||
await this.fetchTheme()
|
||||
},
|
||||
changeTab (v) {
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../API"
|
||||
import Api from "../../API"
|
||||
|
||||
export default {
|
||||
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>
|
||||
</h4>
|
||||
|
||||
<div class="row stats_area mt-5">
|
||||
<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>
|
||||
<ServiceTopStats :service="service"/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -48,29 +35,26 @@
|
|||
|
||||
<script>
|
||||
import ServiceChart from "./ServiceChart";
|
||||
import ServiceTopStats from "@/components/Service/ServiceTopStats";
|
||||
|
||||
export default {
|
||||
name: 'ServiceBlock',
|
||||
components: {ServiceChart},
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
required: true
|
||||
name: 'ServiceBlock',
|
||||
components: {ServiceTopStats, ServiceChart},
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
methods: {
|
||||
smallText(s) {
|
||||
if (s.online) {
|
||||
return `Online, last checked ${this.ago(s.last_success)}`
|
||||
return `Online, last checked ${this.ago(this.parseTime(s.last_success))}`
|
||||
} 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>
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../../components/API"
|
||||
import Api from "../../API"
|
||||
|
||||
const axisOptions = {
|
||||
labels: {
|
||||
|
@ -30,108 +30,108 @@
|
|||
};
|
||||
|
||||
export default {
|
||||
name: 'ServiceChart',
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
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,
|
||||
name: 'ServiceChart',
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
xaxis: {
|
||||
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"],
|
||||
},
|
||||
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: {
|
||||
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: {
|
||||
async chartHits() {
|
||||
const start = this.ago((3600 * 24) * 7)
|
||||
this.data = await Api.service_hits(this.service.id, start, this.now(), "hour")
|
||||
const start = this.nowSubtract((3600 * 24) * 7)
|
||||
this.data = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(new Date()), "hour")
|
||||
this.series = [{
|
||||
name: this.service.name,
|
||||
...this.data
|
||||
}]
|
||||
this.ready = true
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
|
|
|
@ -33,7 +33,7 @@
|
|||
|
||||
<script>
|
||||
import ServiceChart from "./ServiceChart";
|
||||
import Api from "../API";
|
||||
import Api from "../../API";
|
||||
|
||||
export default {
|
||||
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>
|
||||
<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}}
|
||||
</span>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ServiceSparkLine from "./ServiceSparkLine";
|
||||
import Api from "../API";
|
||||
import Api from "../../API";
|
||||
|
||||
export default {
|
||||
name: 'ServiceInfo',
|
||||
|
@ -37,7 +38,7 @@
|
|||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
data() {
|
||||
return {
|
||||
set1: [],
|
||||
set2: [],
|
||||
|
@ -47,7 +48,7 @@
|
|||
failures: null
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
async mounted() {
|
||||
this.set1 = await this.getHits(24 * 2, "hour")
|
||||
this.set1_name = this.calc(this.set1)
|
||||
this.set2 = await this.getHits(24 * 7, "hour")
|
||||
|
@ -55,19 +56,19 @@
|
|||
this.loaded = true
|
||||
},
|
||||
methods: {
|
||||
async getHits (hours, group) {
|
||||
const start = this.ago(3600 * hours)
|
||||
async getHits(hours, group) {
|
||||
const start = this.nowSubtract(3600 * hours)
|
||||
if (!this.service.online) {
|
||||
this.failures = await Api.service_failures(this.service.id, this.now()-360, this.now(), 5)
|
||||
return [ { name: "None", data: [] } ]
|
||||
this.failures = await Api.service_failures(this.service.id, this.toUnix(start), this.toUnix(this.now()), 5)
|
||||
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) {
|
||||
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
|
||||
if (data.length > 1) {
|
||||
let total = 0
|
||||
|
@ -75,7 +76,7 @@
|
|||
total += f.y
|
||||
});
|
||||
total = total / data.length
|
||||
return total.toFixed(0) + "ms Average"
|
||||
return total.toFixed(0) + "ms"
|
||||
} else {
|
||||
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>
|
||||
<form @submit="saveCheckin">
|
||||
<form @submit.prevent="saveCheckin">
|
||||
<div class="form-group row">
|
||||
<div class="col-md-3">
|
||||
<label for="checkin_interval" class="col-form-label">Checkin Name</label>
|
||||
|
@ -14,46 +14,45 @@
|
|||
<input v-model="checkin.grace" type="number" name="grace" class="form-control" id="grace_period" placeholder="10">
|
||||
</div>
|
||||
<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>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API";
|
||||
import Api from "../API";
|
||||
|
||||
export default {
|
||||
name: 'Checkin',
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
checkin: {
|
||||
name: "",
|
||||
interval: 60,
|
||||
grace: 60,
|
||||
service: this.service.id
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
name: 'Checkin',
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
checkin: {
|
||||
name: "",
|
||||
interval: 60,
|
||||
grace: 60,
|
||||
service: this.service.id
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
async saveCheckin(e) {
|
||||
e.preventDefault();
|
||||
const data = {name: this.group.name, public: this.group.public}
|
||||
await Api.group_create(data)
|
||||
const groups = await Api.groups()
|
||||
this.$store.commit('setGroups', groups)
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async saveCheckin() {
|
||||
const data = {name: this.group.name, public: this.group.public}
|
||||
await Api.group_create(data)
|
||||
const groups = await Api.groups()
|
||||
this.$store.commit('setGroups', groups)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<form @submit="saveSettings">
|
||||
<form @submit.prevent="saveSettings">
|
||||
<div class="form-group">
|
||||
<label>Project Name</label>
|
||||
<input v-model="core.name" type="text" class="form-control" placeholder="Great Uptime">
|
||||
|
@ -68,20 +68,9 @@
|
|||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<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.prevent="saveSettings" type="submit" class="btn btn-primary btn-block">Save Settings</button>
|
||||
|
||||
<button @click="saveSettings" type="submit" class="btn btn-primary btn-block">Save Settings</button>
|
||||
|
||||
<div class="form-group row mt-3">
|
||||
<div class="form-group row mt-5">
|
||||
<label class="col-sm-3 col-form-label">API Key</label>
|
||||
<div class="col-sm-9">
|
||||
<input v-model="core.api_key" @focus="$event.target.select()" type="text" class="form-control select-input" readonly>
|
||||
|
@ -102,43 +91,44 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from '../components/API'
|
||||
import Api from '../API'
|
||||
|
||||
export default {
|
||||
name: 'CoreSettings',
|
||||
data () {
|
||||
return {
|
||||
core: this.$store.getters.core,
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
name: 'CoreSettings',
|
||||
data() {
|
||||
return {
|
||||
core: this.$store.getters.core,
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
async saveSettings (e) {
|
||||
e.preventDefault()
|
||||
const c = this.core
|
||||
const coreForm = {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}
|
||||
await Api.core_save(coreForm)
|
||||
const core = await Api.core()
|
||||
this.$store.commit('setCore', core)
|
||||
this.core = core
|
||||
},
|
||||
async renewApiKeys () {
|
||||
let r = confirm("Are you sure you want to reset the API keys?");
|
||||
if (r === true) {
|
||||
await Api.renewApiKeys()
|
||||
const core = await Api.core()
|
||||
this.$store.commit('setCore', core)
|
||||
this.core = core
|
||||
}
|
||||
},
|
||||
selectAll() {
|
||||
this.$refs.input.select();
|
||||
},
|
||||
methods: {
|
||||
async saveSettings() {
|
||||
const c = this.core
|
||||
const coreForm = {
|
||||
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
|
||||
}
|
||||
await Api.core_save(coreForm)
|
||||
const core = await Api.core()
|
||||
this.$store.commit('setCore', core)
|
||||
this.core = core
|
||||
},
|
||||
async renewApiKeys() {
|
||||
let r = confirm("Are you sure you want to reset the API keys?");
|
||||
if (r === true) {
|
||||
await Api.renewApiKeys()
|
||||
const core = await Api.core()
|
||||
this.$store.commit('setCore', core)
|
||||
this.core = core
|
||||
}
|
||||
},
|
||||
selectAll() {
|
||||
this.$refs.input.select();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
|
|
|
@ -38,65 +38,65 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API";
|
||||
import Api from "../API";
|
||||
|
||||
export default {
|
||||
name: 'FormGroup',
|
||||
props: {
|
||||
in_group: {
|
||||
type: Object
|
||||
},
|
||||
edit: {
|
||||
type: Function
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
group: {
|
||||
name: "",
|
||||
public: true
|
||||
name: 'FormGroup',
|
||||
props: {
|
||||
in_group: {
|
||||
type: Object
|
||||
},
|
||||
edit: {
|
||||
type: Function
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
group: {
|
||||
name: "",
|
||||
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>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
|
|
|
@ -59,45 +59,50 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API";
|
||||
import Api from "../API";
|
||||
|
||||
export default {
|
||||
name: 'FormIntegration',
|
||||
props: {
|
||||
integration: {
|
||||
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
|
||||
name: 'FormIntegration',
|
||||
props: {
|
||||
integration: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
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)
|
||||
}
|
||||
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() {
|
||||
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>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
|
|
|
@ -26,43 +26,43 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API";
|
||||
import Api from "../API";
|
||||
|
||||
export default {
|
||||
name: 'FormLogin',
|
||||
data () {
|
||||
return {
|
||||
username: "",
|
||||
password: "",
|
||||
auth: {},
|
||||
loading: false,
|
||||
error: false,
|
||||
disabled: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
checkForm() {
|
||||
if (!this.username || !this.password) {
|
||||
this.disabled = true
|
||||
} else {
|
||||
this.disabled = false
|
||||
name: 'FormLogin',
|
||||
data() {
|
||||
return {
|
||||
username: "",
|
||||
password: "",
|
||||
auth: {},
|
||||
loading: false,
|
||||
error: false,
|
||||
disabled: true
|
||||
}
|
||||
},
|
||||
async login () {
|
||||
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')
|
||||
methods: {
|
||||
checkForm() {
|
||||
if (!this.username || !this.password) {
|
||||
this.disabled = true
|
||||
} else {
|
||||
this.disabled = false
|
||||
}
|
||||
},
|
||||
async login() {
|
||||
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>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
|
|
|
@ -24,10 +24,10 @@
|
|||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Message Date Range</label>
|
||||
<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 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>
|
||||
|
||||
|
@ -90,7 +90,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API";
|
||||
import Api from "../API";
|
||||
import flatPickr from 'vue-flatpickr-component';
|
||||
import 'flatpickr/dist/flatpickr.css';
|
||||
|
||||
|
@ -135,6 +135,12 @@
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
startChange(e) {
|
||||
window.console.log(e)
|
||||
},
|
||||
endChange(e) {
|
||||
window.console.log(e)
|
||||
},
|
||||
removeEdit() {
|
||||
this.message = {}
|
||||
this.edit(false)
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<form @submit="saveNotifier">
|
||||
<form @submit.prevent="saveNotifier">
|
||||
|
||||
<div v-if="error" class="alert alert-danger col-12" role="alert">{{error}}</div>
|
||||
|
||||
|
@ -38,13 +38,13 @@
|
|||
</div>
|
||||
|
||||
<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"}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
|
||||
|
@ -57,67 +57,65 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API";
|
||||
import Api from "../API";
|
||||
|
||||
export default {
|
||||
name: 'Notifier',
|
||||
props: {
|
||||
notifier: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
error: null,
|
||||
saved: false,
|
||||
ok: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
name: 'Notifier',
|
||||
props: {
|
||||
notifier: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
error: null,
|
||||
saved: false,
|
||||
ok: false,
|
||||
}
|
||||
},
|
||||
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) {
|
||||
e.preventDefault();
|
||||
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
|
||||
},
|
||||
}
|
||||
methods: {
|
||||
async saveNotifier() {
|
||||
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()
|
||||
await this.$store.commit('setNotifiers', notifiers)
|
||||
this.saved = true
|
||||
this.loading = false
|
||||
setTimeout(() => {
|
||||
this.saved = 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>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<form @submit="saveService">
|
||||
<form @submit.prevent="saveService">
|
||||
<h4 class="mb-5 text-muted">Basic Information</h4>
|
||||
<div class="form-group row">
|
||||
<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>
|
||||
<div class="col-sm-8">
|
||||
<select v-model="service.type" class="form-control" id="service_type" >
|
||||
<option value="http" >HTTP Service</option>
|
||||
<option value="tcp" >TCP Service</option>
|
||||
<option value="udp" >UDP Service</option>
|
||||
<option value="icmp" >ICMP Ping</option>
|
||||
<option value="http">HTTP Service</option>
|
||||
<option value="tcp">TCP Service</option>
|
||||
<option value="udp">UDP Service</option>
|
||||
<option value="icmp">ICMP Ping</option>
|
||||
</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>
|
||||
</div>
|
||||
|
@ -111,15 +111,8 @@
|
|||
<small class="form-text text-muted">Use text for the service URL rather than the service number.</small>
|
||||
</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">
|
||||
<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">
|
||||
<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">
|
||||
|
@ -128,7 +121,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<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">
|
||||
|
@ -136,8 +129,17 @@
|
|||
</span>
|
||||
</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">
|
||||
<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">
|
||||
<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">
|
||||
|
@ -157,7 +159,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API";
|
||||
import Api from "../API";
|
||||
|
||||
export default {
|
||||
name: 'FormService',
|
||||
|
@ -181,6 +183,7 @@
|
|||
order: 1,
|
||||
verify_ssl: true,
|
||||
allow_notifications: true,
|
||||
notify_all_changes: true,
|
||||
public: true,
|
||||
},
|
||||
groups: [],
|
||||
|
@ -203,8 +206,7 @@
|
|||
}
|
||||
},
|
||||
methods: {
|
||||
async saveService(e) {
|
||||
e.preventDefault()
|
||||
async saveService() {
|
||||
let s = this.service
|
||||
delete s.failures
|
||||
delete s.created_at
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<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">
|
||||
<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 class="col-12">
|
||||
|
@ -96,7 +96,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API";
|
||||
import Api from "../API";
|
||||
import Index from "../pages/Index";
|
||||
|
||||
export default {
|
||||
|
|
|
@ -5,39 +5,39 @@
|
|||
<script>
|
||||
|
||||
export default {
|
||||
name: 'ToggleSwitch',
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
icon: "toggle-on",
|
||||
running: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.service.online) {
|
||||
this.running = true
|
||||
this.icon = "toggle-on"
|
||||
} else {
|
||||
this.running = false
|
||||
this.icon = "toggle-off"
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleChecking() {
|
||||
if (this.running) {
|
||||
this.icon = "toggle-off"
|
||||
} else {
|
||||
this.icon = "toggle-on"
|
||||
name: 'ToggleSwitch',
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
icon: "toggle-on",
|
||||
running: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.service.online) {
|
||||
this.running = true
|
||||
this.icon = "toggle-on"
|
||||
} else {
|
||||
this.running = false
|
||||
this.icon = "toggle-off"
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleChecking() {
|
||||
if (this.running) {
|
||||
this.icon = "toggle-off"
|
||||
} else {
|
||||
this.icon = "toggle-on"
|
||||
}
|
||||
this.running = !this.running
|
||||
},
|
||||
}
|
||||
this.running = !this.running
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
|
|
|
@ -55,7 +55,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API";
|
||||
import Api from "../API";
|
||||
|
||||
export default {
|
||||
name: 'FormUser',
|
||||
|
|
|
@ -12,7 +12,6 @@ import "./icons"
|
|||
Vue.component('apexchart', VueApexCharts)
|
||||
|
||||
Vue.use(VueRouter);
|
||||
Vue.use(require('vue-moment'));
|
||||
|
||||
Vue.config.productionTip = false
|
||||
new Vue({
|
||||
|
|
|
@ -1,45 +1,51 @@
|
|||
import Vue from "vue";
|
||||
const { zonedTimeToUtc, utcToZonedTime, subSeconds, parse, parseISO, getUnixTime, fromUnixTime, format, differenceInSeconds, formatDistanceToNow, formatDistance } = require('date-fns')
|
||||
|
||||
export default Vue.mixin({
|
||||
methods: {
|
||||
now() {
|
||||
return Math.round(new Date().getTime() / 1000)
|
||||
return new Date()
|
||||
},
|
||||
current() {
|
||||
return new Date()
|
||||
},
|
||||
ago(seconds) {
|
||||
return this.now() - seconds
|
||||
current() {
|
||||
return parse(new Date())
|
||||
},
|
||||
utc(val) {
|
||||
return fromUnixTime(this.toUnix(val) + val.getTimezoneOffset() * 60 * 1000)
|
||||
},
|
||||
ago(t1) {
|
||||
return formatDistanceToNow(t1)
|
||||
},
|
||||
nowSubtract(seconds) {
|
||||
return subSeconds(new Date(), seconds)
|
||||
},
|
||||
duration(t1, t2) {
|
||||
const val = (this.toUnix(t1) - this.toUnix(t2))
|
||||
if (val <= 59) {
|
||||
return this.$moment.duration(val, 'seconds').get('seconds') + " seconds ago"
|
||||
}
|
||||
return this.$moment.duration(val, 'seconds').humanize();
|
||||
return formatDistance(t1, t2)
|
||||
},
|
||||
niceDate(val) {
|
||||
return this.parseTime(val).format('LLLL')
|
||||
},
|
||||
parseTime(val) {
|
||||
return this.$moment(val, this.$moment.ISO_8601, true)
|
||||
},
|
||||
toLocal(val, suf='at') {
|
||||
return this.parseTime(val).local().format(`dddd, MMM Do \\${suf} h:mma`)
|
||||
niceDate(val) {
|
||||
return this.parseTime(val).format('LLLL')
|
||||
},
|
||||
parseTime(val) {
|
||||
return parseISO(val)
|
||||
},
|
||||
toLocal(val, suf = 'at') {
|
||||
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) {
|
||||
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) {
|
||||
if (!service) {
|
||||
return ""
|
||||
|
@ -53,10 +59,10 @@ export default Vue.mixin({
|
|||
isInt(n) {
|
||||
return n % 1 === 0;
|
||||
},
|
||||
loggedIn() {
|
||||
const core = this.$store.getters.core
|
||||
return core.logged_in === true
|
||||
},
|
||||
loggedIn() {
|
||||
const core = this.$store.getters.core
|
||||
return core.logged_in === true
|
||||
},
|
||||
iconName(name) {
|
||||
switch (name) {
|
||||
case "fas fa-terminal":
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API"
|
||||
import Api from "../API"
|
||||
import TopNav from "../components/Dashboard/TopNav";
|
||||
|
||||
export default {
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from '../components/API';
|
||||
import Api from '../API';
|
||||
import Group from '../components/Index/Group';
|
||||
import Header from '../components/Index/Header';
|
||||
import MessageBlock from '../components/Index/MessageBlock';
|
||||
|
@ -30,36 +30,36 @@ import ServiceBlock from '../components/Service/ServiceBlock';
|
|||
|
||||
|
||||
export default {
|
||||
name: 'Index',
|
||||
components: {
|
||||
ServiceBlock,
|
||||
MessageBlock,
|
||||
Group,
|
||||
Header
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
logged_in: false
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.logged_in = this.loggedIn()
|
||||
},
|
||||
async mounted() {
|
||||
name: 'Index',
|
||||
components: {
|
||||
ServiceBlock,
|
||||
MessageBlock,
|
||||
Group,
|
||||
Header
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
logged_in: false
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.logged_in = this.loggedIn()
|
||||
},
|
||||
async mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
inRange(message) {
|
||||
const start = this.isBetween(new Date(), message.start_on)
|
||||
const end = this.isBetween(message.end_on, new Date())
|
||||
return start && end
|
||||
},
|
||||
clickService(s) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.s.scrollTop = 0;
|
||||
});
|
||||
},
|
||||
methods: {
|
||||
inRange(message) {
|
||||
const start = this.isBetween(new Date(), message.start_on)
|
||||
const end = this.isBetween(message.end_on, new Date())
|
||||
return start && end
|
||||
},
|
||||
clickService(s) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.s.scrollTop = 0;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API";
|
||||
import Api from "../API";
|
||||
import FormLogin from '../forms/Login';
|
||||
|
||||
export default {
|
||||
|
|
|
@ -3,61 +3,55 @@
|
|||
<p v-if="logs.length === 0" class="text-monospace sm">
|
||||
Loading Logs...
|
||||
</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>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API";
|
||||
import Api from "../API";
|
||||
|
||||
export default {
|
||||
name: 'Logs',
|
||||
components: {
|
||||
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
logs: [],
|
||||
last: "",
|
||||
t: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (!this.t) {
|
||||
this.t = setInterval(() => {
|
||||
this.lastLog()
|
||||
}, 650)
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.getLogs()
|
||||
},
|
||||
name: 'Logs',
|
||||
components: {},
|
||||
data() {
|
||||
return {
|
||||
logs: [],
|
||||
last: "",
|
||||
t: null
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
await this.getLogs()
|
||||
if (!this.t) {
|
||||
this.t = setInterval(async () => {
|
||||
await this.lastLog()
|
||||
}, 650)
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.t)
|
||||
},
|
||||
methods: {
|
||||
cleanLog(l) {
|
||||
const splitLog = l.split(": ")
|
||||
const last = splitLog.slice(1);
|
||||
return last.join(": ")
|
||||
},
|
||||
async getLogs() {
|
||||
const logs = await Api.logs()
|
||||
this.logs = logs.reverse()
|
||||
this.last = this.cleanLog(this.logs[this.logs.length-1])
|
||||
},
|
||||
async lastLog() {
|
||||
const log = await Api.logs_last()
|
||||
const cleanLast = this.cleanLog(log)
|
||||
|
||||
if (this.last !== cleanLast) {
|
||||
this.last = cleanLast
|
||||
this.logs.reverse().push(log)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
cleanLog(l) {
|
||||
const splitLog = l.split(": ")
|
||||
const last = splitLog.slice(1);
|
||||
return last.join(": ")
|
||||
},
|
||||
async getLogs() {
|
||||
const logs = await Api.logs()
|
||||
const l = logs.reverse()
|
||||
this.last = this.cleanLog(l[l.length - 1])
|
||||
this.logs = l
|
||||
},
|
||||
async lastLog() {
|
||||
const log = await Api.logs_last()
|
||||
const cleanLast = this.cleanLog(log)
|
||||
if (this.last !== cleanLast) {
|
||||
this.last = cleanLast
|
||||
this.logs.reverse().push(log)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -7,50 +7,37 @@
|
|||
{{service.online ? "ONLINE" : "OFFLINE"}}
|
||||
</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}">
|
||||
{{service.online ? "ONLINE" : "OFFLINE"}}
|
||||
</span>
|
||||
</h4>
|
||||
|
||||
<div class="row stats_area mt-5 mb-5">
|
||||
<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>
|
||||
<ServiceTopStats :service="service"/>
|
||||
|
||||
<div v-for="(message, index) in messages" v-if="messageInRange(message)">
|
||||
<MessageBlock :message="message"/>
|
||||
</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">
|
||||
<apexchart width="100%" height="420" type="area" :options="chartOptions" :series="series"></apexchart>
|
||||
</div>
|
||||
|
||||
<div v-if="series" class="service-chart-heatmap">
|
||||
<apexchart width="100%" height="215" type="heatmap" :options="chartOptions" :series="series"></apexchart>
|
||||
<div class="service-chart-heatmap mt-3 mb-4">
|
||||
<ServiceHeatmap :service="service"/>
|
||||
</div>
|
||||
|
||||
<form id="service_date_form" class="col-12 mt-2 mb-3">
|
||||
<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">
|
||||
<nav v-if="service.failures" 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='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>
|
||||
|
@ -58,7 +45,7 @@
|
|||
</nav>
|
||||
|
||||
|
||||
<div class="tab-content">
|
||||
<div v-if="service.failures" class="tab-content">
|
||||
<div class="tab-pane fade active show">
|
||||
<ServiceFailures :service="service"/>
|
||||
</div>
|
||||
|
@ -98,10 +85,14 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API"
|
||||
import Api from "../API"
|
||||
import MessageBlock from '../components/Index/MessageBlock';
|
||||
import ServiceFailures from '../components/Service/ServiceFailures';
|
||||
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 = {
|
||||
labels: {
|
||||
|
@ -128,143 +119,145 @@
|
|||
};
|
||||
|
||||
export default {
|
||||
name: 'Service',
|
||||
components: {
|
||||
ServiceFailures,
|
||||
MessageBlock,
|
||||
Checkin
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
id: null,
|
||||
tab: "failures",
|
||||
service: {},
|
||||
authenticated: false,
|
||||
ready: false,
|
||||
data: null,
|
||||
messages: [],
|
||||
failures: [],
|
||||
chartOptions: {
|
||||
chart: {
|
||||
height: 500,
|
||||
width: "100%",
|
||||
type: "area",
|
||||
animations: {
|
||||
enabled: true,
|
||||
initialAnimation: {
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
selection: {
|
||||
enabled: false
|
||||
},
|
||||
zoom: {
|
||||
enabled: false
|
||||
},
|
||||
toolbar: {
|
||||
show: false
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
show: true,
|
||||
padding: {
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
}
|
||||
},
|
||||
xaxis: {
|
||||
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: ["#48d338"],
|
||||
opacity: 1,
|
||||
type: 'solid'
|
||||
},
|
||||
stroke: {
|
||||
show: true,
|
||||
curve: 'smooth',
|
||||
lineCap: 'butt',
|
||||
colors: ["#3aa82d"],
|
||||
name: 'Service',
|
||||
components: {
|
||||
ServiceTopStats,
|
||||
ServiceHeatmap,
|
||||
ServiceFailures,
|
||||
MessageBlock,
|
||||
Checkin,
|
||||
flatPickr
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
id: null,
|
||||
tab: "failures",
|
||||
service: {},
|
||||
authenticated: false,
|
||||
ready: false,
|
||||
data: null,
|
||||
messages: [],
|
||||
failures: [],
|
||||
start_time: "",
|
||||
end_time: "",
|
||||
chartOptions: {
|
||||
chart: {
|
||||
height: 500,
|
||||
width: "100%",
|
||||
type: "area",
|
||||
animations: {
|
||||
enabled: true,
|
||||
initialAnimation: {
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
selection: {
|
||||
enabled: false
|
||||
},
|
||||
zoom: {
|
||||
enabled: false
|
||||
},
|
||||
toolbar: {
|
||||
show: false
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
show: true,
|
||||
padding: {
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
}
|
||||
},
|
||||
xaxis: {
|
||||
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: ["#48d338"],
|
||||
opacity: 1,
|
||||
type: 'solid'
|
||||
},
|
||||
stroke: {
|
||||
show: true,
|
||||
curve: 'smooth',
|
||||
lineCap: 'butt',
|
||||
colors: ["#3aa82d"],
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
data: []
|
||||
}],
|
||||
heatmap_data: [],
|
||||
config: {
|
||||
enableTime: true
|
||||
},
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
data: []
|
||||
}],
|
||||
heatmap_data: []
|
||||
}
|
||||
},
|
||||
},
|
||||
async mounted() {
|
||||
const id = this.$attrs.id
|
||||
const id = this.$attrs.id
|
||||
this.id = id
|
||||
let service;
|
||||
if (this.isInt(id)) {
|
||||
service = this.$store.getters.serviceById(id)
|
||||
} else {
|
||||
service = this.$store.getters.serviceByPermalink(id)
|
||||
}
|
||||
let service;
|
||||
if (this.isInt(id)) {
|
||||
service = this.$store.getters.serviceById(id)
|
||||
} else {
|
||||
service = this.$store.getters.serviceByPermalink(id)
|
||||
}
|
||||
this.service = service
|
||||
this.getService(service)
|
||||
this.messages = this.$store.getters.serviceMessages(service.id)
|
||||
},
|
||||
methods: {
|
||||
messageInRange(message) {
|
||||
const start = this.isBetween(new Date(), message.start_on)
|
||||
const end = this.isBetween(message.end_on, new Date())
|
||||
return start && end
|
||||
},
|
||||
async getService(s) {
|
||||
await this.chartHits()
|
||||
await this.heatmapData()
|
||||
await this.serviceFailures()
|
||||
},
|
||||
async serviceFailures() {
|
||||
this.failures = await Api.service_failures(this.service.id, this.now() - 3600, this.now(), 15)
|
||||
},
|
||||
async chartHits() {
|
||||
this.data = await Api.service_hits(this.service.id, 0, 99999999999, "hour")
|
||||
this.series = [{
|
||||
name: this.service.name,
|
||||
...this.data
|
||||
}]
|
||||
this.ready = true
|
||||
},
|
||||
async heatmapData() {
|
||||
this.data = await Api.service_heatmap(this.service.id, 0, 99999999999, "hour")
|
||||
this.series = [{
|
||||
name: this.service.name,
|
||||
...this.data
|
||||
}]
|
||||
this.ready = true
|
||||
}
|
||||
}
|
||||
methods: {
|
||||
messageInRange(message) {
|
||||
const start = this.isBetween(new Date(), message.start_on)
|
||||
const end = this.isBetween(message.end_on, new Date())
|
||||
return start && end
|
||||
},
|
||||
async getService(s) {
|
||||
await this.chartHits()
|
||||
await this.serviceFailures()
|
||||
},
|
||||
async serviceFailures() {
|
||||
this.failures = await Api.service_failures(this.service.id, this.now() - 3600, this.now(), 15)
|
||||
},
|
||||
async chartHits() {
|
||||
const start = this.nowSubtract((3600 * 24) * 7)
|
||||
this.start_time = start
|
||||
this.end_time = new Date()
|
||||
this.data = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(new Date()), "hour")
|
||||
this.series = [{
|
||||
name: this.service.name,
|
||||
...this.data
|
||||
}]
|
||||
this.ready = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
<div class="row align-content-center">
|
||||
<img class="rounded text-center" width="300" height="300" :src="qrcode">
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -53,32 +53,11 @@
|
|||
</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">
|
||||
|
||||
<ThemeEditor :core="core"/>
|
||||
|
||||
</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">
|
||||
<h3>Cache</h3>
|
||||
<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>
|
||||
<Cache/>
|
||||
</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`">
|
||||
|
@ -97,54 +76,50 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Api from '../components/API';
|
||||
import Api from '../API';
|
||||
import CoreSettings from '../forms/CoreSettings';
|
||||
import FormIntegration from '../forms/Integration';
|
||||
import Notifier from "../forms/Notifier";
|
||||
import ThemeEditor from "../components/Dashboard/ThemeEditor";
|
||||
import Cache from "@/components/Dashboard/Cache";
|
||||
|
||||
export default {
|
||||
name: 'Settings',
|
||||
components: {
|
||||
ThemeEditor,
|
||||
FormIntegration,
|
||||
Notifier,
|
||||
CoreSettings
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
tab: "v-pills-home-tab",
|
||||
qrcode: "",
|
||||
core: this.$store.getters.core,
|
||||
cache: [],
|
||||
}
|
||||
},
|
||||
async mounted () {
|
||||
name: 'Settings',
|
||||
components: {
|
||||
Cache,
|
||||
ThemeEditor,
|
||||
FormIntegration,
|
||||
Notifier,
|
||||
CoreSettings
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tab: "v-pills-home-tab",
|
||||
qrcode: "",
|
||||
qrurl: "",
|
||||
core: this.$store.getters.core
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
this.cache = await Api.cache()
|
||||
},
|
||||
async created() {
|
||||
const qrurl = `statping://setup?domain=${core.domain}&api=${core.api_secret}`
|
||||
this.qrcode = "https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=" + encodeURI(qrurl)
|
||||
},
|
||||
beforeMount() {
|
||||
const c = this.$store.getters.core
|
||||
this.qrurl = `statping://setup?domain=${c.domain}&api=${c.api_secret}`
|
||||
this.qrcode = "https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=" + encodeURI(this.qrurl)
|
||||
},
|
||||
beforeMount() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
changeTab (e) {
|
||||
this.tab = e.target.id
|
||||
},
|
||||
liClass (id) {
|
||||
return this.tab === id
|
||||
},
|
||||
expireTime(ex) {
|
||||
return this.toLocal(ex)
|
||||
},
|
||||
async clearCache () {
|
||||
await Api.clearCache()
|
||||
this.cache = await Api.cache()
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
changeTab(e) {
|
||||
this.tab = e.target.id
|
||||
},
|
||||
liClass(id) {
|
||||
return this.tab === id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- 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 Setup from "./forms/Setup";
|
||||
|
||||
import Api from "./components/API";
|
||||
import Api from "./API";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import Vuex from 'vuex'
|
||||
import Vue from 'vue'
|
||||
import Api from "./components/API"
|
||||
import Api from "./API"
|
||||
|
||||
Vue.use(Vuex)
|
||||
|
||||
|
@ -105,7 +105,7 @@ export default new Vuex.Store({
|
|||
}
|
||||
},
|
||||
actions: {
|
||||
async loadRequired (context) {
|
||||
async loadRequired(context) {
|
||||
const core = await Api.core()
|
||||
context.commit("setCore", core);
|
||||
const groups = await Api.groups()
|
||||
|
@ -125,7 +125,7 @@ export default new Vuex.Store({
|
|||
// }
|
||||
window.console.log('finished loading required data')
|
||||
},
|
||||
async loadAdmin (context) {
|
||||
async loadAdmin(context) {
|
||||
const core = await Api.core()
|
||||
context.commit("setCore", core);
|
||||
const groups = await Api.groups()
|
||||
|
|
|
@ -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 {
|
||||
app.Timezone = c.Timezone
|
||||
}
|
||||
app.UpdateNotify = c.UpdateNotify
|
||||
app.UseCdn = types.NewNullBool(c.UseCdn.Bool)
|
||||
core.CoreApp, err = core.UpdateCore(app)
|
||||
returnJson(core.CoreApp, w, r)
|
||||
|
|
|
@ -25,6 +25,9 @@ var handlerFuncs = func(w http.ResponseWriter, r *http.Request) template.FuncMap
|
|||
"USE_CDN": func() bool {
|
||||
return core.CoreApp.UseCdn.Bool
|
||||
},
|
||||
"USING_ASSETS": func() bool {
|
||||
return core.CoreApp.UsingAssets()
|
||||
},
|
||||
"BasePath": func() string {
|
||||
return basePath
|
||||
},
|
||||
|
|
|
@ -70,9 +70,6 @@ func (u *discord) OnSuccess(s *types.Service) {
|
|||
if !s.Online || !s.SuccessNotified {
|
||||
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
||||
var msg interface{}
|
||||
if s.UpdateNotify {
|
||||
s.UpdateNotify = false
|
||||
}
|
||||
msg = s.DownText
|
||||
|
||||
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) {
|
||||
if !s.Online || !s.SuccessNotified {
|
||||
var msg string
|
||||
if s.UpdateNotify {
|
||||
s.UpdateNotify = false
|
||||
}
|
||||
msg = s.DownText
|
||||
|
||||
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) {
|
||||
if !s.Online || !s.SuccessNotified {
|
||||
var msg string
|
||||
if s.UpdateNotify {
|
||||
s.UpdateNotify = false
|
||||
}
|
||||
msg = s.DownText
|
||||
|
||||
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
||||
|
|
|
@ -99,9 +99,6 @@ func (u *mobilePush) OnSuccess(s *types.Service) {
|
|||
data := dataJson(s, nil)
|
||||
if !s.Online || !s.SuccessNotified {
|
||||
var msgStr string
|
||||
if s.UpdateNotify {
|
||||
s.UpdateNotify = false
|
||||
}
|
||||
msgStr = s.DownText
|
||||
|
||||
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
||||
|
|
|
@ -92,9 +92,6 @@ func (u *telegram) OnSuccess(s *types.Service) {
|
|||
if !s.Online || !s.SuccessNotified {
|
||||
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
||||
var msg interface{}
|
||||
if s.UpdateNotify {
|
||||
s.UpdateNotify = false
|
||||
}
|
||||
msg = s.DownText
|
||||
|
||||
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 {
|
||||
u.ResetUniqueQueue(fmt.Sprintf("service_%v", s.Id))
|
||||
var msg string
|
||||
if s.UpdateNotify {
|
||||
s.UpdateNotify = false
|
||||
}
|
||||
msg = s.DownText
|
||||
|
||||
u.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
|
||||
|
|
|
@ -41,7 +41,6 @@ type Core struct {
|
|||
Setup bool `gorm:"-" json:"setup"`
|
||||
MigrationId int64 `gorm:"column:migration_id" json:"migration_id,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"`
|
||||
LoggedIn bool `gorm:"-" json:"logged_in"`
|
||||
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"`
|
||||
Timeout int `gorm:"default:30;column:timeout" json:"timeout" scope:"user,admin"`
|
||||
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"`
|
||||
Public NullBool `gorm:"default:true;column:public" json:"public"`
|
||||
GroupId int `gorm:"default:0;column:group_id" json:"group_id"`
|
||||
|
@ -53,10 +52,11 @@ type Service struct {
|
|||
Checkpoint time.Time `gorm:"-" json:"-"`
|
||||
SleepDuration time.Duration `gorm:"-" json:"-"`
|
||||
LastResponse string `gorm:"-" json:"-"`
|
||||
UserNotified bool `gorm:"-" json:"-"` // True if the User was already notified about a Downtime
|
||||
UpdateNotify bool `gorm:"-" json:"-"` // This Variable is a simple copy of `core.CoreApp.UpdateNotify.Bool`
|
||||
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
|
||||
AllowNotifications NullBool `gorm:"default:true;column:allow_notifications" json:"allow_notifications" scope:"user,admin"`
|
||||
UserNotified bool `gorm:"-" json:"-"` // True if the User was already notified about a Downtime
|
||||
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`
|
||||
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"`
|
||||
LastOnline time.Time `gorm:"-" json:"last_success"`
|
||||
Failures []FailureInterface `gorm:"-" json:"failures,omitempty" scope:"user,admin"`
|
||||
|
|
Loading…
Reference in New Issue