mirror of https://github.com/statping/statping
merged with release
commit
fa743f2919
|
@ -41,4 +41,5 @@ tmp
|
||||||
/frontend/cypress/videos/
|
/frontend/cypress/videos/
|
||||||
services.yml
|
services.yml
|
||||||
statping.wiki
|
statping.wiki
|
||||||
assets/
|
assets/
|
||||||
|
.vscode/settings.json
|
||||||
|
|
|
@ -19,3 +19,6 @@ yarn-error.log*
|
||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
# Package lock file
|
||||||
|
package-lock.json
|
||||||
|
|
|
@ -282,6 +282,27 @@ class Api {
|
||||||
await axios.all([all])
|
await axios.all([all])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async downtimes ({ serviceId, start, end, skip, count, subStatus }) {
|
||||||
|
return axios.get('api/downtimes', {
|
||||||
|
params: { service_id: serviceId, start, end, skip, count, sub_status: subStatus }
|
||||||
|
}).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async downtime (id) {
|
||||||
|
return axios.get(`api/downtimes/${id}`).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async downtime_create (data) {
|
||||||
|
return axios.post('/api/downtimes', data).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async downtime_update ({ id, data }) {
|
||||||
|
return axios.patch(`/api/downtimes/${id}`, data).then((response) => response.data);
|
||||||
|
}
|
||||||
|
|
||||||
|
async downtime_delete (id) {
|
||||||
|
return axios.delete(`/api/downtimes/${id}`).then((response) => response.data);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const api = new Api()
|
const api = new Api()
|
||||||
export default api
|
export default api
|
||||||
|
|
|
@ -85,6 +85,10 @@ A {
|
||||||
|
|
||||||
.nav-link {
|
.nav-link {
|
||||||
color: $navbar-color;
|
color: $navbar-color;
|
||||||
|
|
||||||
|
&.router-link-exact-active {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-control {
|
.form-control {
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
<template>
|
||||||
|
<div class="col-12">
|
||||||
|
<DowntimesFilterForm
|
||||||
|
:handle-clear-filters="handleClearFilters"
|
||||||
|
:params="params"
|
||||||
|
:handle-filter-search="handleFilterSearch"
|
||||||
|
:filter-errors="filterErrors"
|
||||||
|
:handle-filter-change="handleFilterChange"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div class="card contain-card mb-4">
|
||||||
|
<div class="card-header">
|
||||||
|
{{ $t('downtimes') }}
|
||||||
|
<router-link
|
||||||
|
v-if="$store.state.admin"
|
||||||
|
to="/dashboard/create_downtime"
|
||||||
|
class="btn btn-sm btn-success float-right"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon="plus" /> {{ $t('create') }}
|
||||||
|
</router-link>
|
||||||
|
</div>
|
||||||
|
<div class="card-body pt-0">
|
||||||
|
<div
|
||||||
|
v-if="isLoading"
|
||||||
|
class="mt-5"
|
||||||
|
>
|
||||||
|
<div class="col-12 text-center">
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon="circle-notch"
|
||||||
|
size="3x"
|
||||||
|
spin
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 text-center mt-3 mb-3">
|
||||||
|
<span class="text-muted">
|
||||||
|
Loading Downtimes
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<DowntimesList :get-downtimes="getDowntimes" />
|
||||||
|
<Pagination
|
||||||
|
v-if="downtimes.length !== 0"
|
||||||
|
:get-next-downtimes="getNextDowntimes"
|
||||||
|
:get-prev-downtimes="getPrevDowntimes"
|
||||||
|
:skip="params.skip"
|
||||||
|
:count="params.count"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import DowntimesList from './DowntimesList.vue';
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
import DowntimesFilterForm from '../../forms/DowntimeFilters.vue';
|
||||||
|
import Pagination from '../Elements/Pagination.vue';
|
||||||
|
import { removeEmptyParams } from '../../forms/Downtime.vue';
|
||||||
|
|
||||||
|
export const initialParams = {
|
||||||
|
serviceId: '',
|
||||||
|
start: '',
|
||||||
|
end: '',
|
||||||
|
skip: 0,
|
||||||
|
count: 10,
|
||||||
|
subStatus: ''
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertToSec = (val) => {
|
||||||
|
return (new Date(val).getTime())/1000;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const checkErrors = (params) => {
|
||||||
|
const { start, end } = params;
|
||||||
|
const errors = {};
|
||||||
|
|
||||||
|
// Converting into millisec
|
||||||
|
const startSec = convertToSec(start);
|
||||||
|
const endSec = convertToSec(end) + (60 * 60 * 23 + 59 * 60 + 59);
|
||||||
|
|
||||||
|
if (!start && end) {
|
||||||
|
errors.start = 'Need to enter Start Date';
|
||||||
|
} else if (start && !end) {
|
||||||
|
errors.end = 'Need to enter End Date';
|
||||||
|
} else if ( startSec > endSec ) {
|
||||||
|
errors.end = 'End Date should be greater than Start Date';
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DashboardDowntimes',
|
||||||
|
components: {
|
||||||
|
DowntimesList,
|
||||||
|
Pagination,
|
||||||
|
DowntimesFilterForm
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
isLoading: false,
|
||||||
|
params: { ...initialParams },
|
||||||
|
filterErrors: {}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState([ 'downtimes' ]),
|
||||||
|
},
|
||||||
|
created: function () {
|
||||||
|
// Set start date
|
||||||
|
const startDate = new Date();
|
||||||
|
startDate.setDate(startDate.getDate() - 30);
|
||||||
|
startDate.setHours(0,0,0,0);
|
||||||
|
this.params.start = startDate.toJSON();
|
||||||
|
|
||||||
|
// Set end date
|
||||||
|
const endDate = new Date();
|
||||||
|
endDate.setHours(0,0,0,0);
|
||||||
|
this.params.end = endDate.toJSON();
|
||||||
|
|
||||||
|
this.getDowntimes(this.params);
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getDowntimes: async function (params = this.params) {
|
||||||
|
const { start, end } = params;
|
||||||
|
|
||||||
|
const errors = checkErrors(this.params);
|
||||||
|
|
||||||
|
if (Object.keys(errors).length > 0) {
|
||||||
|
this.filterErrors = Object.assign({}, errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let startSec = ''; let endSec = '';
|
||||||
|
|
||||||
|
if (start) {
|
||||||
|
startSec = convertToSec(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end) {
|
||||||
|
endSec = convertToSec(end) + (60 * 60 * 23 + 59 * 60 + 59); // adding end of time for that particular date.
|
||||||
|
}
|
||||||
|
|
||||||
|
const payload = removeEmptyParams({ ...params, start: startSec, end: endSec });
|
||||||
|
|
||||||
|
this.isLoading = true;
|
||||||
|
await this.$store.dispatch({ type: 'getDowntimes', payload });
|
||||||
|
this.isLoading = false;
|
||||||
|
},
|
||||||
|
getNextDowntimes: function () {
|
||||||
|
this.params = { ...this.params, skip: this.params.skip + this.params.count };
|
||||||
|
this.getDowntimes();
|
||||||
|
},
|
||||||
|
getPrevDowntimes: function () {
|
||||||
|
this.params = { ...this.params, skip: this.params.skip - this.params.count };
|
||||||
|
this.getDowntimes(this.params);
|
||||||
|
},
|
||||||
|
handleClearFilters: function () {
|
||||||
|
this.params = { ...initialParams };
|
||||||
|
},
|
||||||
|
handleFilterSearch: function () {
|
||||||
|
this.params = { ...this.params, skip: 0 };
|
||||||
|
|
||||||
|
this.getDowntimes();
|
||||||
|
},
|
||||||
|
handleFilterChange: function (e) {
|
||||||
|
const { name } = e.target;
|
||||||
|
|
||||||
|
// reset error according to edit input
|
||||||
|
delete this.filterErrors[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,172 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
v-if="downtimes.length === 0"
|
||||||
|
class="alert alert-dark d-block mt-3 mb-0"
|
||||||
|
>
|
||||||
|
You currently don't have any downtimes for this services!
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table
|
||||||
|
v-else
|
||||||
|
class="table"
|
||||||
|
>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th scope="col">
|
||||||
|
{{ $t('name') }}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
class="d-none d-md-table-cell"
|
||||||
|
>
|
||||||
|
{{ $t('start_time') }}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
class="d-none d-md-table-cell"
|
||||||
|
>
|
||||||
|
{{ $t('end_time') }}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
class="d-none d-md-table-cell"
|
||||||
|
>
|
||||||
|
{{ $t('status') }}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
class="d-none d-md-table-cell"
|
||||||
|
>
|
||||||
|
{{ $t('failures') }}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
scope="col"
|
||||||
|
class="d-none d-md-table-cell"
|
||||||
|
>
|
||||||
|
{{ $t('actions') }}
|
||||||
|
</th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr
|
||||||
|
v-for="downtime in downtimes"
|
||||||
|
:key="downtime.id"
|
||||||
|
>
|
||||||
|
<td>
|
||||||
|
<span>
|
||||||
|
{{ downtime.service.name }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="d-none d-md-table-cell">
|
||||||
|
<span
|
||||||
|
class=""
|
||||||
|
>
|
||||||
|
{{ niceDateWithYear(downtime.start) }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="d-none d-md-table-cell">
|
||||||
|
<span
|
||||||
|
class=""
|
||||||
|
>
|
||||||
|
{{ downtime.end ? niceDateWithYear(downtime.end) : 'Ongoing' }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="d-none d-md-table-cell">
|
||||||
|
<span
|
||||||
|
class="badge text-uppercase"
|
||||||
|
:class="[downtime.sub_status === 'down' ? 'badge-danger' : 'badge-warning' ]"
|
||||||
|
>
|
||||||
|
{{ downtime.sub_status }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="d-none d-md-table-cell">
|
||||||
|
<span
|
||||||
|
class=""
|
||||||
|
>
|
||||||
|
{{ downtime.failures }}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button
|
||||||
|
v-if="$store.state.admin"
|
||||||
|
:disabled="isLoading"
|
||||||
|
class="btn btn-sm btn-outline-secondary"
|
||||||
|
@click.prevent="goto(`/dashboard/edit_downtime/${downtime.id}`)"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon="edit" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="$store.state.admin"
|
||||||
|
:disabled="downtimeDeleteId === downtime.id && isLoading"
|
||||||
|
class="btn btn-sm btn-danger"
|
||||||
|
@click.prevent="handleDowntimeDelete(downtime)"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
v-if="!isLoading"
|
||||||
|
icon="trash"
|
||||||
|
/>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
v-if="downtimeDeleteId === downtime.id && isLoading"
|
||||||
|
icon="circle-notch"
|
||||||
|
spin
|
||||||
|
/>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
import Api from '../../API';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DashboardDowntimeList',
|
||||||
|
props: {
|
||||||
|
getDowntimes: {
|
||||||
|
type: Function,
|
||||||
|
default: function () {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
isLoading: false,
|
||||||
|
downtimeDeleteId: null,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState([ 'downtimes' ]),
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
goto: function (to) {
|
||||||
|
this.$router.push(to);
|
||||||
|
},
|
||||||
|
setIsLoading ({ id, isLoading }) {
|
||||||
|
this.downtimeDeleteId = id;
|
||||||
|
this.isLoading = isLoading;
|
||||||
|
},
|
||||||
|
delete: async function (id) {
|
||||||
|
this.setIsLoading({ id, isLoading: true });
|
||||||
|
await Api.downtime_delete(id);
|
||||||
|
this.setIsLoading({ id: null, isLoading: false });
|
||||||
|
|
||||||
|
this.getDowntimes();
|
||||||
|
},
|
||||||
|
handleDowntimeDelete: async function (downtime) {
|
||||||
|
const modal = {
|
||||||
|
visible: true,
|
||||||
|
title: 'Delete Downtime',
|
||||||
|
body: `Are you sure you want to delete the downtime for service ${downtime.service.name}?`,
|
||||||
|
btnColor: 'btn-danger',
|
||||||
|
btnText: 'Delete Downtime',
|
||||||
|
func: () => this.delete(downtime.id),
|
||||||
|
};
|
||||||
|
this.$store.commit('setModal', modal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,60 @@
|
||||||
|
<template>
|
||||||
|
<div class="col-12">
|
||||||
|
<div
|
||||||
|
v-if="isLoading"
|
||||||
|
class="row mt-5"
|
||||||
|
>
|
||||||
|
<div class="col-12 text-center">
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon="circle-notch"
|
||||||
|
size="3x"
|
||||||
|
spin
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 text-center mt-3 mb-3">
|
||||||
|
<span class="text-muted">
|
||||||
|
Loading Downtime
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormDowntime
|
||||||
|
v-else
|
||||||
|
:edit-downtime="editDowntime"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import Api from '../../API';
|
||||||
|
|
||||||
|
const FormDowntime = () =>
|
||||||
|
import(/* webpackChunkName: "dashboard" */ '../../forms/Downtime.vue');
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'EditDowntime',
|
||||||
|
components: {
|
||||||
|
FormDowntime
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return { isLoading: false, editDowntime: null };
|
||||||
|
},
|
||||||
|
mounted () {
|
||||||
|
this.getDowntime();
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
getDowntime: async function () {
|
||||||
|
const id = this.$route.params.id;
|
||||||
|
if (!id) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isLoading = true;
|
||||||
|
const { output } = await Api.downtime(id);
|
||||||
|
this.isLoading = false;
|
||||||
|
|
||||||
|
this.editDowntime = output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -14,6 +14,9 @@
|
||||||
<li @click="navopen = !navopen" class="nav-item navbar-item">
|
<li @click="navopen = !navopen" class="nav-item navbar-item">
|
||||||
<router-link to="/dashboard/services" class="nav-link">{{ $t('services') }}</router-link>
|
<router-link to="/dashboard/services" class="nav-link">{{ $t('services') }}</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
<li @click="navopen = !navopen" class="nav-item navbar-item">
|
||||||
|
<router-link to="/dashboard/downtimes" class="nav-link">{{'Downtimes'}}</router-link>
|
||||||
|
</li>
|
||||||
<li v-if="admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
<li v-if="admin" @click="navopen = !navopen" class="nav-item navbar-item">
|
||||||
<router-link to="/dashboard/users" class="nav-link">{{ $t('users') }}</router-link>
|
<router-link to="/dashboard/users" class="nav-link">{{ $t('users') }}</router-link>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -0,0 +1,59 @@
|
||||||
|
<template>
|
||||||
|
<ul class="pagination d-flex justify-content-center">
|
||||||
|
<li class="page-item">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-secondary page-link"
|
||||||
|
aria-label="Previous"
|
||||||
|
:disabled="skip <= 0"
|
||||||
|
@click="getPrevDowntimes"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">
|
||||||
|
«
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li class="page-item">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-secondary page-link"
|
||||||
|
aria-label="Next"
|
||||||
|
:disabled="downtimes.length < count"
|
||||||
|
@click="getNextDowntimes"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">
|
||||||
|
»
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DasboardDowntimePagination',
|
||||||
|
props: {
|
||||||
|
count:{
|
||||||
|
type: Number,
|
||||||
|
default: 10,
|
||||||
|
},
|
||||||
|
getNextDowntimes: {
|
||||||
|
type: Function,
|
||||||
|
default: function () {}
|
||||||
|
},
|
||||||
|
getPrevDowntimes: {
|
||||||
|
type: Function,
|
||||||
|
default: function () {}
|
||||||
|
},
|
||||||
|
skip: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState([ 'downtimes' ])
|
||||||
|
},
|
||||||
|
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,325 @@
|
||||||
|
<template>
|
||||||
|
<div class="card contain-card mb-4">
|
||||||
|
<div class="card-header d-flex align-items-center">
|
||||||
|
<button
|
||||||
|
class="btn p-0 mr-2"
|
||||||
|
@click="$router.push('/dashboard/downtimes');"
|
||||||
|
>
|
||||||
|
<FontAwesomeIcon icon="arrow-circle-left" />
|
||||||
|
</button>
|
||||||
|
<div>{{ $t("downtime_info") }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form>
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-sm-4 col-form-label">
|
||||||
|
{{
|
||||||
|
$t("service_name")
|
||||||
|
}}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select
|
||||||
|
v-model="downtime.serviceId"
|
||||||
|
name="service"
|
||||||
|
class="form-control"
|
||||||
|
required
|
||||||
|
:disabled="$route.params.id"
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
v-for="(service) in services"
|
||||||
|
:key="service.id"
|
||||||
|
:value="service.id"
|
||||||
|
>
|
||||||
|
{{ service.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<small
|
||||||
|
class="form-text text-muted"
|
||||||
|
>
|
||||||
|
Select Servive you want to have downtime for
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-sm-4 col-form-label">
|
||||||
|
{{
|
||||||
|
$t("downtime_status")
|
||||||
|
}}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<select
|
||||||
|
v-model="downtime.subStatus"
|
||||||
|
name="service"
|
||||||
|
class="form-control"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<option
|
||||||
|
value="degraded"
|
||||||
|
>
|
||||||
|
Degraded
|
||||||
|
</option>
|
||||||
|
<option value="down">
|
||||||
|
Down
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
<small
|
||||||
|
class="form-text text-muted"
|
||||||
|
>
|
||||||
|
Choose status you want to give to the Servive
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-sm-4 col-form-label">
|
||||||
|
{{
|
||||||
|
$t("downtime_date_range")
|
||||||
|
}}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<FlatPickr
|
||||||
|
id="start"
|
||||||
|
v-model="downtime.start"
|
||||||
|
type="text"
|
||||||
|
name="start"
|
||||||
|
class="form-control form-control-plaintext"
|
||||||
|
:config="config"
|
||||||
|
placeholder="Select Start Date"
|
||||||
|
@on-change="() => handleFormChange({target: {name: 'start'}})"
|
||||||
|
/>
|
||||||
|
<small
|
||||||
|
v-if="errors.start"
|
||||||
|
class="form-text text-danger"
|
||||||
|
>
|
||||||
|
{{ errors.start }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<FlatPickr
|
||||||
|
id="end"
|
||||||
|
v-model="downtime.end"
|
||||||
|
type="text"
|
||||||
|
name="end"
|
||||||
|
class="form-control form-control-plaintext"
|
||||||
|
:config="config"
|
||||||
|
placeholder="Select End Date"
|
||||||
|
@on-change="() => handleFormChange({target: {name: 'end'}})"
|
||||||
|
/>
|
||||||
|
<small
|
||||||
|
v-if="errors.end"
|
||||||
|
class="form-text text-danger"
|
||||||
|
>
|
||||||
|
{{ errors.end }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<small
|
||||||
|
class="form-text text-muted"
|
||||||
|
>
|
||||||
|
Enter the Start and End date for which your service will be down/degraded
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<label class="col-sm-4 col-form-label">
|
||||||
|
{{
|
||||||
|
$t("failures")
|
||||||
|
}}
|
||||||
|
</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input
|
||||||
|
v-model.number="downtime.failures"
|
||||||
|
type="number"
|
||||||
|
name="check_interval"
|
||||||
|
class="form-control"
|
||||||
|
min="0"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<small
|
||||||
|
v-if="errors.failures"
|
||||||
|
class="form-text text-danger"
|
||||||
|
>
|
||||||
|
{{ errors.failures }}
|
||||||
|
</small>
|
||||||
|
<small
|
||||||
|
class="form-text text-muted"
|
||||||
|
>
|
||||||
|
Select the number of failures you want for your service
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group row">
|
||||||
|
<div class="col-12">
|
||||||
|
<button
|
||||||
|
:disabled="isLoading || !isCreateDowntimeBtnEnabled()"
|
||||||
|
type="button"
|
||||||
|
class="btn btn-success btn-block"
|
||||||
|
@click.prevent="saveDowntime"
|
||||||
|
>
|
||||||
|
{{ $route.params.id ? $t("downtime_update") : $t("downtime_create") }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
import Api from '../API';
|
||||||
|
import FlatPickr from 'vue-flatpickr-component';
|
||||||
|
import 'flatpickr/dist/flatpickr.css';
|
||||||
|
import { convertToSec } from '../components/Dashboard/DashboardDowntimes.vue';
|
||||||
|
|
||||||
|
const checkFormErrors = (value, id) => {
|
||||||
|
const { failures, start, end } = value;
|
||||||
|
const errors = {};
|
||||||
|
let endSec = ''; let startSec = '';
|
||||||
|
|
||||||
|
// Converting into millisec
|
||||||
|
if (start) {
|
||||||
|
startSec = convertToSec(start);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (end) {
|
||||||
|
endSec = convertToSec(end);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for valid positive numbers
|
||||||
|
if (!(/^\d+$/.test(failures))) {
|
||||||
|
errors.failures = 'Enter Valid Positve Number without decimal point';
|
||||||
|
} else if (!start && end) {
|
||||||
|
errors.start = 'Need to enter Start Date';
|
||||||
|
} else if ( id && startSec && !endSec ) {
|
||||||
|
errors.end = 'Need to enter End Date';
|
||||||
|
} else if ( endSec && startSec > endSec ) {
|
||||||
|
errors.end = 'End Date should be greater than Start Date';
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const removeEmptyParams = (obj) => {
|
||||||
|
const updatedObj = {};
|
||||||
|
|
||||||
|
for (const [ key , value ] of Object.entries(obj)) {
|
||||||
|
if (value) {
|
||||||
|
updatedObj[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return updatedObj;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'FormDowntime',
|
||||||
|
components: {
|
||||||
|
FlatPickr,
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
editDowntime: {
|
||||||
|
type: Object,
|
||||||
|
default: null,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
isLoading: false,
|
||||||
|
errors: {},
|
||||||
|
downtime: {
|
||||||
|
serviceId: '',
|
||||||
|
subStatus: 'degraded',
|
||||||
|
failures: 20,
|
||||||
|
start: new Date().toJSON(),
|
||||||
|
end: null,
|
||||||
|
},
|
||||||
|
config: {
|
||||||
|
altFormat: 'J M, Y, h:iK',
|
||||||
|
altInput: true,
|
||||||
|
enableTime: true,
|
||||||
|
dateFormat: 'Z',
|
||||||
|
maxDate: new Date().toJSON(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
services : function (state) {
|
||||||
|
const { id } = this.$route.params;
|
||||||
|
|
||||||
|
if (!id && state.services.length > 0) {
|
||||||
|
this.downtime.serviceId = state.services[0].id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state.services;
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
mounted: function () {
|
||||||
|
if (this.editDowntime) {
|
||||||
|
const { service_id, sub_status, failures, start, end } = this.editDowntime;
|
||||||
|
|
||||||
|
this.downtime = {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
failures,
|
||||||
|
serviceId: service_id,
|
||||||
|
subStatus: sub_status
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isCreateDowntimeBtnEnabled: function () {
|
||||||
|
const { id } = this.$route.params;
|
||||||
|
const { serviceId, subStatus, failures, start, end } = this.downtime;
|
||||||
|
|
||||||
|
return serviceId && subStatus && failures && start && (id ? end : true);
|
||||||
|
},
|
||||||
|
saveDowntime: async function () {
|
||||||
|
const { id } = this.$route.params;
|
||||||
|
const errors = checkFormErrors(this.downtime, id);
|
||||||
|
|
||||||
|
// Check of invalid input.
|
||||||
|
if (Object.keys(errors).length > 0) {
|
||||||
|
this.errors = Object.assign({}, errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { serviceId, subStatus, ...rest } = removeEmptyParams(this.downtime);
|
||||||
|
|
||||||
|
const downtime = {
|
||||||
|
...rest,
|
||||||
|
...(!id && { 'service_id': serviceId }),
|
||||||
|
'sub_status': subStatus,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.isLoading=true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (id) {
|
||||||
|
await Api.downtime_update({ id, data: downtime });
|
||||||
|
} else {
|
||||||
|
await Api.downtime_create(downtime);
|
||||||
|
}
|
||||||
|
this.isLoading=false;
|
||||||
|
} catch (error) {
|
||||||
|
this.isLoading=false;
|
||||||
|
throw new Error(error.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.$router.push('/dashboard/downtimes');
|
||||||
|
},
|
||||||
|
handleFormChange: function (e) {
|
||||||
|
const { name } = e.target;
|
||||||
|
|
||||||
|
delete this.errors[name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -0,0 +1,173 @@
|
||||||
|
<template>
|
||||||
|
<div class="card contain-card mb-4">
|
||||||
|
<div class="card-header">
|
||||||
|
{{ $t('filters') }}
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group col-md-2">
|
||||||
|
<label class="col-form-label">
|
||||||
|
{{ $t('service') }}
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
v-model="params.serviceId"
|
||||||
|
name="service"
|
||||||
|
class="form-control"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
All
|
||||||
|
</option>
|
||||||
|
<option
|
||||||
|
v-for="service in services"
|
||||||
|
:key="service.id"
|
||||||
|
:value="service.id"
|
||||||
|
>
|
||||||
|
{{ service.name }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="form-group col-md-5">
|
||||||
|
<label class="col-form-label">
|
||||||
|
{{ $t('downtime_date_range') }}
|
||||||
|
</label>
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<FlatPickr
|
||||||
|
id="start"
|
||||||
|
v-model="params.start"
|
||||||
|
type="text"
|
||||||
|
name="start"
|
||||||
|
class="form-control form-control-plaintext"
|
||||||
|
value=""
|
||||||
|
:config="config"
|
||||||
|
placeholder="Select Start Date"
|
||||||
|
@on-change="() => handleFilterChange({target: {name: 'start'}})"
|
||||||
|
/>
|
||||||
|
<small
|
||||||
|
v-if="filterErrors.start"
|
||||||
|
class="form-text text-danger"
|
||||||
|
>
|
||||||
|
{{ filterErrors.start }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
<div class="col-sm-6">
|
||||||
|
<FlatPickr
|
||||||
|
id="end"
|
||||||
|
v-model="params.end"
|
||||||
|
type="text"
|
||||||
|
name="end"
|
||||||
|
class="form-control form-control-plaintext"
|
||||||
|
value=""
|
||||||
|
:config="config"
|
||||||
|
placeholder="Select End Date"
|
||||||
|
@on-change="() => handleFilterChange({target: {name: 'end'}})"
|
||||||
|
/>
|
||||||
|
<small
|
||||||
|
v-if="filterErrors.end"
|
||||||
|
class="form-text text-danger"
|
||||||
|
>
|
||||||
|
{{ filterErrors.end }}
|
||||||
|
</small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group col-md-2">
|
||||||
|
<label class="col-form-label">
|
||||||
|
{{ $t('status') }}
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
v-model="params.subStatus"
|
||||||
|
name="status"
|
||||||
|
class="form-control"
|
||||||
|
>
|
||||||
|
<option value="">
|
||||||
|
All
|
||||||
|
</option>
|
||||||
|
<option value="degraded">
|
||||||
|
Degraded
|
||||||
|
</option>
|
||||||
|
<option value="down">
|
||||||
|
Down
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group col-md-3">
|
||||||
|
<label class="col-form-label invisible">
|
||||||
|
{{ $t('actions') }}
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
class="d-flex justify-content-end"
|
||||||
|
role="group"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-primary mr-1"
|
||||||
|
@click.prevent="handleFilterSearch"
|
||||||
|
>
|
||||||
|
{{ $t('search') }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
@click.prevent="handleClearFilters"
|
||||||
|
>
|
||||||
|
{{ $t('clear') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { mapState } from 'vuex';
|
||||||
|
import FlatPickr from 'vue-flatpickr-component';
|
||||||
|
import 'flatpickr/dist/flatpickr.css';
|
||||||
|
import { initialParams } from '../components/Dashboard/DashboardDowntimes.vue';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'DashboardDowntimeFilters',
|
||||||
|
components: {
|
||||||
|
FlatPickr
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
params: {
|
||||||
|
type: Object,
|
||||||
|
default: initialParams
|
||||||
|
},
|
||||||
|
handleClearFilters: {
|
||||||
|
type: Function,
|
||||||
|
default: function () {}
|
||||||
|
},
|
||||||
|
handleFilterSearch: {
|
||||||
|
type: Function,
|
||||||
|
default: function () {}
|
||||||
|
},
|
||||||
|
filterErrors: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
},
|
||||||
|
handleFilterChange: {
|
||||||
|
type: Function,
|
||||||
|
default: function () {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: function () {
|
||||||
|
return {
|
||||||
|
config: {
|
||||||
|
altFormat: 'J M, Y',
|
||||||
|
altInput: true,
|
||||||
|
dateFormat: 'Z',
|
||||||
|
maxDate: new Date().toJSON(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
...mapState([ 'services' ])
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -6,4 +6,4 @@ import Vue from "vue";
|
||||||
|
|
||||||
library.add(fas, fab)
|
library.add(fas, fab)
|
||||||
|
|
||||||
Vue.component('font-awesome-icon', FontAwesomeIcon)
|
Vue.component('FontAwesomeIcon', FontAwesomeIcon);
|
||||||
|
|
|
@ -140,6 +140,20 @@ const english = {
|
||||||
notify_all: "Notify All Changes",
|
notify_all: "Notify All Changes",
|
||||||
service_update: "Update Service",
|
service_update: "Update Service",
|
||||||
service_create: "Create Service",
|
service_create: "Create Service",
|
||||||
|
start_time: 'Start Time',
|
||||||
|
end_time: "End Time",
|
||||||
|
actions: "Actions",
|
||||||
|
services: "Services",
|
||||||
|
downtimes: "Downtimes",
|
||||||
|
downtime_info: "Downtime Info",
|
||||||
|
downtime_status: "Downtime Status",
|
||||||
|
downtime_date_range: "Downtime Date Range",
|
||||||
|
downtime_create: "Create Downtime",
|
||||||
|
downtime_update: "Update Downtime",
|
||||||
|
filters: "Filters",
|
||||||
|
service: "Service",
|
||||||
|
clear: "Clear",
|
||||||
|
search: "Search",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default english;
|
export default english;
|
||||||
|
|
|
@ -256,6 +256,9 @@ export default Vue.mixin({
|
||||||
},
|
},
|
||||||
addSeconds(date, amount) {
|
addSeconds(date, amount) {
|
||||||
return addSeconds(date, amount)
|
return addSeconds(date, amount)
|
||||||
}
|
},
|
||||||
|
niceDateWithYear (val) {
|
||||||
|
return format(parseISO(val), 'do MMM, yyyy h:mma');
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,6 +16,9 @@ const Checkins = () => import(/* webpackChunkName: "dashboard" */ '@/components/
|
||||||
const Failures = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Failures')
|
const Failures = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Failures')
|
||||||
const NotFound = () => import(/* webpackChunkName: "index" */ '@/pages/NotFound')
|
const NotFound = () => import(/* webpackChunkName: "index" */ '@/pages/NotFound')
|
||||||
const Importer = () => import(/* webpackChunkName: "index" */ '@/components/Dashboard/Importer')
|
const Importer = () => import(/* webpackChunkName: "index" */ '@/components/Dashboard/Importer')
|
||||||
|
const DashboardDowntimes = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardDowntimes')
|
||||||
|
const EditDowntime = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/EditDowntime')
|
||||||
|
|
||||||
|
|
||||||
import VueRouter from "vue-router";
|
import VueRouter from "vue-router";
|
||||||
import Api from "./API";
|
import Api from "./API";
|
||||||
|
@ -137,6 +140,27 @@ const routes = [
|
||||||
requiresAuth: true,
|
requiresAuth: true,
|
||||||
title: 'Statping - Service Failures',
|
title: 'Statping - Service Failures',
|
||||||
}
|
}
|
||||||
|
},{
|
||||||
|
path: 'downtimes',
|
||||||
|
component: DashboardDowntimes,
|
||||||
|
meta: {
|
||||||
|
requiresAuth: true,
|
||||||
|
title: 'Statping - Downtimes',
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
path: 'create_downtime',
|
||||||
|
component: EditDowntime,
|
||||||
|
meta: {
|
||||||
|
requiresAuth: true,
|
||||||
|
title: 'Statping - Create Downtime',
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
path: 'edit_downtime/:id',
|
||||||
|
component: EditDowntime,
|
||||||
|
meta: {
|
||||||
|
requiresAuth: true,
|
||||||
|
title: 'Statping - Update Downtime',
|
||||||
|
}
|
||||||
},{
|
},{
|
||||||
path: 'messages',
|
path: 'messages',
|
||||||
component: DashboardMessages,
|
component: DashboardMessages,
|
||||||
|
@ -228,3 +252,4 @@ router.beforeEach((to, from, next) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
export default router
|
export default router
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ export default new Vuex.Store({
|
||||||
oauth: {},
|
oauth: {},
|
||||||
token: null,
|
token: null,
|
||||||
services: [],
|
services: [],
|
||||||
|
downtimes: [],
|
||||||
service: null,
|
service: null,
|
||||||
groups: [],
|
groups: [],
|
||||||
messages: [],
|
messages: [],
|
||||||
|
@ -152,12 +153,19 @@ export default new Vuex.Store({
|
||||||
setModal(state, modal) {
|
setModal(state, modal) {
|
||||||
state.modal = modal
|
state.modal = modal
|
||||||
},
|
},
|
||||||
|
setDowntimes (state, downtimes) {
|
||||||
|
state.downtimes = downtimes;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
async getAllServices(context) {
|
async getAllServices(context) {
|
||||||
const services = await Api.services()
|
const services = await Api.services()
|
||||||
context.commit("setServices", services);
|
context.commit("setServices", services);
|
||||||
},
|
},
|
||||||
|
async getDowntimes (context, { payload }) {
|
||||||
|
const { output } = await Api.downtimes(payload);
|
||||||
|
context.commit('setDowntimes', output ?? []);
|
||||||
|
},
|
||||||
async loadCore(context) {
|
async loadCore(context) {
|
||||||
const core = await Api.core()
|
const core = await Api.core()
|
||||||
const token = await Api.token()
|
const token = await Api.token()
|
||||||
|
|
|
@ -167,7 +167,9 @@ func sendJsonAction(obj interface{}, method string, w http.ResponseWriter, r *ht
|
||||||
case *downtimes.Downtime:
|
case *downtimes.Downtime:
|
||||||
objName = "downtime"
|
objName = "downtime"
|
||||||
objId = v.Id
|
objId = v.Id
|
||||||
|
case *DowntimeService:
|
||||||
|
objName = "downtime_with_service"
|
||||||
|
objId = v.Id
|
||||||
default:
|
default:
|
||||||
objName = fmt.Sprintf("%T", v)
|
objName = fmt.Sprintf("%T", v)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/statping/statping/types/services"
|
"github.com/statping/statping/types/services"
|
||||||
"github.com/statping/statping/utils"
|
"github.com/statping/statping/utils"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -21,10 +22,75 @@ func findDowntime(r *http.Request) (*downtimes.Downtime, error) {
|
||||||
return downtime, nil
|
return downtime, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func convertToMap(query url.Values) map[string]string {
|
||||||
|
vars := make(map[string]string)
|
||||||
|
if query.Get("start") != "" {
|
||||||
|
vars["start"] = query.Get("start")
|
||||||
|
}
|
||||||
|
if query.Get("end") != "" {
|
||||||
|
vars["end"] = query.Get("end")
|
||||||
|
}
|
||||||
|
if query.Get("sub_status") != "" {
|
||||||
|
vars["sub_status"] = query.Get("sub_status")
|
||||||
|
}
|
||||||
|
if query.Get("service_id") != "" {
|
||||||
|
vars["service_id"] = query.Get("service_id")
|
||||||
|
}
|
||||||
|
if query.Get("type") != "" {
|
||||||
|
vars["type"] = query.Get("type")
|
||||||
|
}
|
||||||
|
if query.Get("skip") != "" {
|
||||||
|
vars["skip"] = query.Get("skip")
|
||||||
|
}
|
||||||
|
if query.Get("count") != "" {
|
||||||
|
vars["count"] = query.Get("count")
|
||||||
|
}
|
||||||
|
return vars
|
||||||
|
}
|
||||||
|
|
||||||
|
type DowntimeService struct {
|
||||||
|
Id int64 `gorm:"primary_key;column:id" json:"id"`
|
||||||
|
Service *services.Service `gorm:"foreignKey:service" json:"service"`
|
||||||
|
ServiceId int64 `gorm:"index;column:service" json:"service_id"`
|
||||||
|
SubStatus string `gorm:"column:sub_status" json:"sub_status"`
|
||||||
|
Failures int `gorm:"column:failures" json:"failures"`
|
||||||
|
Start *time.Time `gorm:"index;column:start" json:"start"`
|
||||||
|
End *time.Time `gorm:"column:end" json:"end"`
|
||||||
|
Type string `gorm:"default:'auto';column:type" json:"type"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiAllDowntimes(w http.ResponseWriter, r *http.Request) {
|
||||||
|
query := r.URL.Query()
|
||||||
|
vars := convertToMap(query)
|
||||||
|
downtime, err := downtimes.FindAll(vars)
|
||||||
|
var downtimeWithService []DowntimeService
|
||||||
|
servicesMap := services.All()
|
||||||
|
if downtime == nil {
|
||||||
|
sendJsonAction(downtimeWithService, "fetch", w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, dtime := range *downtime {
|
||||||
|
var downtimeWithServiceVar DowntimeService
|
||||||
|
downtimeWithServiceVar.Id = dtime.Id
|
||||||
|
downtimeWithServiceVar.ServiceId = dtime.ServiceId
|
||||||
|
downtimeWithServiceVar.SubStatus = dtime.SubStatus
|
||||||
|
downtimeWithServiceVar.Failures = dtime.Failures
|
||||||
|
downtimeWithServiceVar.Start = dtime.Start
|
||||||
|
downtimeWithServiceVar.End = dtime.End
|
||||||
|
downtimeWithServiceVar.Type = dtime.Type
|
||||||
|
downtimeWithServiceVar.Service = servicesMap[dtime.ServiceId]
|
||||||
|
downtimeWithService = append(downtimeWithService, downtimeWithServiceVar)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
sendErrorJson(err, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
sendJsonAction(downtimeWithService, "fetch", w, r)
|
||||||
|
}
|
||||||
|
|
||||||
func apiAllDowntimesForServiceHandler(w http.ResponseWriter, r *http.Request) {
|
func apiAllDowntimesForServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
serviceId := utils.ToInt(vars["service_id"])
|
serviceId := utils.ToInt(vars["service_id"])
|
||||||
|
|
||||||
ninetyDaysAgo := time.Now().Add(time.Duration(-90*24) * time.Hour)
|
ninetyDaysAgo := time.Now().Add(time.Duration(-90*24) * time.Hour)
|
||||||
|
|
||||||
downtime, err := downtimes.FindByService(serviceId, ninetyDaysAgo, time.Now())
|
downtime, err := downtimes.FindByService(serviceId, ninetyDaysAgo, time.Now())
|
||||||
|
|
|
@ -194,6 +194,7 @@ func Router() *mux.Router {
|
||||||
//r.Handle("/checkin/{api}", http.HandlerFunc(checkinHitHandler))
|
//r.Handle("/checkin/{api}", http.HandlerFunc(checkinHitHandler))
|
||||||
|
|
||||||
// API DOWNTIME Routes
|
// API DOWNTIME Routes
|
||||||
|
api.Handle("/api/downtimes", authenticated(apiAllDowntimes, false)).Methods("GET")
|
||||||
api.Handle("/api/service/{service_id}/downtimes", authenticated(apiAllDowntimesForServiceHandler, false)).Methods("GET")
|
api.Handle("/api/service/{service_id}/downtimes", authenticated(apiAllDowntimesForServiceHandler, false)).Methods("GET")
|
||||||
api.Handle("/api/downtimes", authenticated(apiCreateDowntimeHandler, false)).Methods("POST")
|
api.Handle("/api/downtimes", authenticated(apiCreateDowntimeHandler, false)).Methods("POST")
|
||||||
api.Handle("/api/downtimes/{id}", authenticated(apiDowntimeHandler, false)).Methods("GET")
|
api.Handle("/api/downtimes/{id}", authenticated(apiDowntimeHandler, false)).Methods("GET")
|
||||||
|
|
|
@ -3,6 +3,7 @@ package downtimes
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/statping/statping/database"
|
"github.com/statping/statping/database"
|
||||||
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -66,6 +67,68 @@ func FindDowntime(timeVar time.Time) []Downtime {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ConvertToUnixTime(str string) (time.Time, error) {
|
||||||
|
i, err := strconv.ParseInt(str, 10, 64)
|
||||||
|
var t time.Time
|
||||||
|
if err != nil {
|
||||||
|
return t, err
|
||||||
|
}
|
||||||
|
tm := time.Unix(i, 0)
|
||||||
|
return tm, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindAll(vars map[string]string) (*[]Downtime, error) {
|
||||||
|
var downtime []Downtime
|
||||||
|
var start time.Time
|
||||||
|
var end time.Time
|
||||||
|
st, err1 := vars["start"]
|
||||||
|
en, err2 := vars["end"]
|
||||||
|
startInt, err := strconv.ParseInt(st, 10, 64)
|
||||||
|
endInt, err := strconv.ParseInt(en, 10, 64)
|
||||||
|
if err1 && err2 && (endInt > startInt) {
|
||||||
|
start, err = ConvertToUnixTime(vars["start"])
|
||||||
|
if err != nil {
|
||||||
|
return &downtime, err
|
||||||
|
}
|
||||||
|
end, err = ConvertToUnixTime(vars["end"])
|
||||||
|
if err != nil {
|
||||||
|
return &downtime, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ninetyDaysAgo := time.Now().Add(time.Duration(-90*24) * time.Hour)
|
||||||
|
start = ninetyDaysAgo
|
||||||
|
end = time.Now()
|
||||||
|
}
|
||||||
|
q := db.Where("start BETWEEN ? AND ?", start, end)
|
||||||
|
if subStatusVar, subStatusErr := vars["sub_status"]; subStatusErr {
|
||||||
|
q = q.Where("sub_status = ?", subStatusVar)
|
||||||
|
}
|
||||||
|
if serviceIdVar, serviceIdErr := vars["service_id"]; serviceIdErr {
|
||||||
|
q = q.Where("service = ?", serviceIdVar)
|
||||||
|
}
|
||||||
|
if typeVar, typeErr := vars["type"]; typeErr {
|
||||||
|
q = q.Where("type = ?", typeVar)
|
||||||
|
}
|
||||||
|
var count int64
|
||||||
|
if countVar, countErr := vars["count"]; countErr {
|
||||||
|
count, err = strconv.ParseInt(countVar, 10, 64)
|
||||||
|
if count > 100 {
|
||||||
|
count = 100
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
count = 20
|
||||||
|
}
|
||||||
|
var skip int64
|
||||||
|
if skipVar, err6 := vars["skip"]; err6 {
|
||||||
|
skip, err = strconv.ParseInt(skipVar, 10, 64)
|
||||||
|
} else {
|
||||||
|
skip = 0
|
||||||
|
}
|
||||||
|
q = q.Order("start DESC")
|
||||||
|
q = q.Limit((int)(count)).Offset((int)(skip)).Find(&downtime)
|
||||||
|
return &downtime, q.Error()
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Downtime) Create() error {
|
func (c *Downtime) Create() error {
|
||||||
q := db.Create(c)
|
q := db.Create(c)
|
||||||
return q.Error()
|
return q.Error()
|
||||||
|
|
Loading…
Reference in New Issue