done with listing and pagination

pull/1097/head
smit95tpatel 2021-12-13 23:50:56 +05:30
parent ee5a4b8f37
commit 1dd73ca743
19 changed files with 50999 additions and 10230 deletions

6
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,6 @@
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"editor.defaultFormatter": null
}

39735
frontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,264 +1,367 @@
import axios from 'axios' import axios from 'axios';
const qs = require('querystring'); const qs = require('querystring');
axios.defaults.withCredentials = true // axios.defaults.withCredentials = true;
axios.defaults.withCredentials = false;
const tokenKey = "statping_auth"; const tokenKey = 'statping_auth';
class Api { class Api {
constructor () { constructor () {
this.version = "0.90.74"; this.version = '0.90.74';
this.commit = "2612402a7782f28ca0b7fc10c941d8c4a1a5acc6"; this.commit = '2612402a7782f28ca0b7fc10c941d8c4a1a5acc6';
} }
async oauth () { async oauth () {
const oauth = axios.get('api/oauth').then(response => (response.data)) const oauth = axios.get('api/oauth').then((response) => response.data);
return oauth return oauth;
} }
async core () { async core () {
const core = axios.get('api').then(response => (response.data)) const core = axios.get('api').then((response) => response.data);
if (core.allow_reports) { if (core.allow_reports) {
await this.sentry_init() await this.sentry_init();
} }
return core return core;
} }
async core_save (obj) { async core_save (obj) {
return axios.post('api/core', obj).then(response => (response.data)) return axios.post('api/core', obj).then((response) => response.data);
} }
async oauth_save (obj) { async oauth_save (obj) {
return axios.post('api/oauth', obj).then(response => (response.data)) return axios.post('api/oauth', obj).then((response) => response.data);
} }
async setup_save (data) { async setup_save (data) {
return axios.post('api/setup', qs.stringify(data)).then(response => (response.data)) return axios
.post('api/setup', qs.stringify(data))
.then((response) => response.data);
} }
async services () { async services () {
return axios.get('api/services').then(response => (response.data)) return axios.get('api/services').then((response) => response.data);
} }
async service (id) { async service (id) {
return axios.get('api/services/' + id).then(response => (response.data)) return axios.get('api/services/' + id).then((response) => response.data);
} }
async service_create (data) { async service_create (data) {
return axios.post('api/services', data).then(response => (response.data)) return axios.post('api/services', data).then((response) => response.data);
} }
async service_update (data) { async service_update (data) {
return axios.post('api/services/' + data.id, data).then(response => (response.data)) return axios
.post('api/services/' + data.id, data)
.then((response) => response.data);
} }
async service_hits (id, start, end, group, fill = true) { async service_hits (id, start, end, group, fill = true) {
return axios.get('api/services/' + id + '/hits_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=' + fill).then(response => (response.data)) return axios
.get(
'api/services/' +
id +
'/hits_data?start=' +
start +
'&end=' +
end +
'&group=' +
group +
'&fill=' +
fill
)
.then((response) => response.data);
} }
async service_ping (id, start, end, group, fill = true) { async service_ping (id, start, end, group, fill = true) {
return axios.get('api/services/' + id + '/ping_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=' + fill).then(response => (response.data)) return axios
.get(
'api/services/' +
id +
'/ping_data?start=' +
start +
'&end=' +
end +
'&group=' +
group +
'&fill=' +
fill
)
.then((response) => response.data);
} }
async service_failures_data (id, start, end, group, fill = true) { async service_failures_data (id, start, end, group, fill = true) {
return axios.get('api/services/' + id + '/failure_data?start=' + start + '&end=' + end + '&group=' + group + '&fill=' + fill).then(response => (response.data)) return axios
.get(
'api/services/' +
id +
'/failure_data?start=' +
start +
'&end=' +
end +
'&group=' +
group +
'&fill=' +
fill
)
.then((response) => response.data);
} }
async service_uptime (id, start, end) { async service_uptime (id, start, end) {
return axios.get('api/services/' + id + '/uptime_data?start=' + start + '&end=' + end).then(response => (response.data)) return axios
.get('api/services/' + id + '/uptime_data?start=' + start + '&end=' + end)
.then((response) => response.data);
} }
async service_heatmap (id, start, end, group) { async service_heatmap (id, start, end, group) {
return axios.get('api/services/' + id + '/heatmap').then(response => (response.data)) return axios
.get('api/services/' + id + '/heatmap')
.then((response) => response.data);
} }
async service_failures (id, start, end, limit = 999, offset = 0) { async service_failures (id, start, end, limit = 999, offset = 0) {
return axios.get('api/services/' + id + '/failures?start=' + start + '&end=' + end + '&limit=' + limit + '&offset=' + offset).then(response => (response.data)) return axios
.get(
'api/services/' +
id +
'/failures?start=' +
start +
'&end=' +
end +
'&limit=' +
limit +
'&offset=' +
offset
)
.then((response) => response.data);
} }
async service_failures_delete (service) { async service_failures_delete (service) {
return axios.delete('api/services/' + service.id + '/failures').then(response => (response.data)) return axios
.delete('api/services/' + service.id + '/failures')
.then((response) => response.data);
} }
async service_delete (id) { async service_delete (id) {
return axios.delete('api/services/' + id).then(response => (response.data)) return axios.delete('api/services/' + id).then((response) => response.data);
} }
async services_reorder (data) { async services_reorder (data) {
return axios.post('api/reorder/services', data).then(response => (response.data)) return axios
.post('api/reorder/services', data)
.then((response) => response.data);
} }
async checkins () { async checkins () {
return axios.get('api/checkins').then(response => (response.data)) return axios.get('api/checkins').then((response) => response.data);
} }
async groups () { async groups () {
return axios.get('api/groups').then(response => (response.data)) return axios.get('api/groups').then((response) => response.data);
} }
async groups_reorder (data) { async groups_reorder (data) {
return axios.post('api/reorder/groups', data).then(response => (response.data)) return axios
.post('api/reorder/groups', data)
.then((response) => response.data);
} }
async group_delete (id) { async group_delete (id) {
return axios.delete('api/groups/' + id).then(response => (response.data)) return axios.delete('api/groups/' + id).then((response) => response.data);
} }
async group_create (data) { async group_create (data) {
return axios.post('api/groups', data).then(response => (response.data)) return axios.post('api/groups', data).then((response) => response.data);
} }
async group_update (data) { async group_update (data) {
return axios.post('api/groups/' + data.id, data).then(response => (response.data)) return axios
.post('api/groups/' + data.id, data)
.then((response) => response.data);
} }
async users () { async users () {
return axios.get('api/users').then(response => (response.data)) return axios.get('api/users').then((response) => response.data);
} }
async user_create (data) { async user_create (data) {
return axios.post('api/users', data).then(response => (response.data)) return axios.post('api/users', data).then((response) => response.data);
} }
async user_update (data) { async user_update (data) {
return axios.post('api/users/' + data.id, data).then(response => (response.data)) return axios
.post('api/users/' + data.id, data)
.then((response) => response.data);
} }
async user_delete (id) { async user_delete (id) {
return axios.delete('api/users/' + id).then(response => (response.data)) return axios.delete('api/users/' + id).then((response) => response.data);
} }
async incident_updates (incident) { async incident_updates (incident) {
return axios.get('api/incidents/' + incident.id + '/updates').then(response => (response.data)) return axios
.get('api/incidents/' + incident.id + '/updates')
.then((response) => response.data);
} }
async incident_update_create (update) { async incident_update_create (update) {
return axios.post('api/incidents/' + update.incident + '/updates', update).then(response => (response.data)) return axios
.post('api/incidents/' + update.incident + '/updates', update)
.then((response) => response.data);
} }
async incident_update_delete (update) { async incident_update_delete (update) {
return axios.delete('api/incidents/' + update.incident + '/updates/' + update.id).then(response => (response.data)) return axios
.delete('api/incidents/' + update.incident + '/updates/' + update.id)
.then((response) => response.data);
} }
async incidents_service (id) { async incidents_service (id) {
return axios.get('api/services/' + id + '/incidents').then(response => (response.data)) return axios
.get('api/services/' + id + '/incidents')
.then((response) => response.data);
} }
async incident_create (service_id, data) { async incident_create (service_id, data) {
return axios.post('api/services/' + service_id + '/incidents', data).then(response => (response.data)) return axios
.post('api/services/' + service_id + '/incidents', data)
.then((response) => response.data);
} }
async incident_delete (incident) { async incident_delete (incident) {
return axios.delete('api/incidents/' + incident.id).then(response => (response.data)) return axios
.delete('api/incidents/' + incident.id)
.then((response) => response.data);
} }
async checkin (api) { async checkin (api) {
return axios.get('api/checkins/' + api).then(response => (response.data)) return axios.get('api/checkins/' + api).then((response) => response.data);
} }
async checkin_create (data) { async checkin_create (data) {
return axios.post('api/checkins', data).then(response => (response.data)) return axios.post('api/checkins', data).then((response) => response.data);
} }
async checkin_delete (checkin) { async checkin_delete (checkin) {
return axios.delete('api/checkins/' + checkin.api_key).then(response => (response.data)) return axios
.delete('api/checkins/' + checkin.api_key)
.then((response) => response.data);
} }
async messages () { async messages () {
return axios.get('api/messages').then(response => (response.data)) return axios.get('api/messages').then((response) => response.data);
} }
async message_create (data) { async message_create (data) {
return axios.post('api/messages', data).then(response => (response.data)) return axios.post('api/messages', data).then((response) => response.data);
} }
async message_update (data) { async message_update (data) {
return axios.post('api/messages/' + data.id, data).then(response => (response.data)) return axios
.post('api/messages/' + data.id, data)
.then((response) => response.data);
} }
async message_delete (id) { async message_delete (id) {
return axios.delete('api/messages/' + id).then(response => (response.data)) return axios.delete('api/messages/' + id).then((response) => response.data);
} }
async group (id) { async group (id) {
return axios.get('api/groups/' + id).then(response => (response.data)) return axios.get('api/groups/' + id).then((response) => response.data);
} }
async notifiers () { async notifiers () {
return axios.get('api/notifiers').then(response => (response.data)) return axios.get('api/notifiers').then((response) => response.data);
} }
async notifier_save (data) { async notifier_save (data) {
return axios.post('api/notifier/' + data.method, data).then(response => (response.data)) return axios
.post('api/notifier/' + data.method, data)
.then((response) => response.data);
} }
async notifier_test (data, notifier) { async notifier_test (data, notifier) {
return axios.post('api/notifier/' + notifier + '/test', data).then(response => (response.data)) return axios
.post('api/notifier/' + notifier + '/test', data)
.then((response) => response.data);
} }
async renewApiKeys () { async renewApiKeys () {
return axios.get('api/renew').then(response => (response.data)) return axios.get('api/renew').then((response) => response.data);
} }
async logs () { async logs () {
return axios.get('api/logs').then(response => (response.data)) || [] return axios.get('api/logs').then((response) => response.data) || [];
} }
async logs_last () { async logs_last () {
return axios.get('api/logs/last').then(response => (response.data)) return axios.get('api/logs/last').then((response) => response.data);
} }
async theme () { async theme () {
return axios.get('api/theme').then(response => (response.data)) return axios.get('api/theme').then((response) => response.data);
} }
async theme_generate (create = true) { async theme_generate (create = true) {
if (create) { if (create) {
return axios.get('api/theme/create').then(response => (response.data)) return axios.get('api/theme/create').then((response) => response.data);
} else { } else {
return axios.delete('api/theme').then(response => (response.data)) return axios.delete('api/theme').then((response) => response.data);
} }
} }
async theme_save (data) { async theme_save (data) {
return axios.post('api/theme', data).then(response => (response.data)) return axios.post('api/theme', data).then((response) => response.data);
} }
async import (data) { async import (data) {
return axios.post('api/settings/import', data).then(response => (response.data)) return axios
.post('api/settings/import', data)
.then((response) => response.data);
} }
async check_token (token) { async check_token (token) {
const f = {token: token} const f = { token: token };
return axios.post('api/users/token', qs.stringify(f)).then(response => (response.data)) return axios
.post('api/users/token', qs.stringify(f))
.then((response) => response.data);
} }
async login (username, password) { async login (username, password) {
const f = {username: username, password: password} const f = { username: username, password: password };
return axios.post('api/login', qs.stringify(f)).then(response => (response.data)) return axios
.post('api/login', qs.stringify(f))
.then((response) => response.data);
} }
async logout () { async logout () {
return axios.get('api/logout').then(response => (response.data)) return axios.get('api/logout').then((response) => response.data);
} }
async scss_base () { async scss_base () {
return await axios({ return await axios({
url: '/scss/base.scss', url: '/scss/base.scss',
method: 'GET', method: 'GET',
responseType: 'blob' responseType: 'blob',
}).then((response) => { }).then((response) => {
const reader = new window.FileReader(); const reader = new window.FileReader();
return reader.readAsText(response.data) return reader.readAsText(response.data);
}) });
} }
async configs () { async configs () {
return axios.get('api/settings/configs').then(response => (response.data)) || [] return (
axios.get('api/settings/configs').then((response) => response.data) || []
);
} }
async configs_save (data) { async configs_save (data) {
return axios.post('api/settings/configs', data).then(response => (response.data)) || [] return (
axios
.post('api/settings/configs', data)
.then((response) => response.data) || []
);
} }
token () { token () {
@ -266,22 +369,29 @@ class Api {
} }
authToken () { authToken () {
const tk = $cookies.get(tokenKey) const tk = $cookies.get(tokenKey);
if (tk) { if (tk) {
return {'Authorization': 'Bearer ' + tk}; return { Authorization: 'Bearer ' + tk };
} else { } else {
return {}; return {};
} }
} }
async github_release () { async github_release () {
return fetch('https://api.github.com/repos/statping/statping/releases/latest').then(response => response.json()) return fetch(
'https://api.github.com/repos/statping/statping/releases/latest'
).then((response) => response.json());
} }
async allActions (...all) { async allActions (...all) {
await axios.all([all]) await axios.all([ all ]);
} }
async getDowntimes ({ 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);
} }
const api = new Api() }
export default api const api = new Api();
export default api;

View File

@ -6,51 +6,51 @@
</template> </template>
<script> <script>
const Footer = () => import(/* webpackChunkName: "index" */ "./components/Index/Footer"); const Footer = () => import(/* webpackChunkName: "index" */ './components/Index/Footer');
export default { export default {
name: 'app', name: 'App',
components: { components: {
Footer Footer
}, },
data () { data () {
return { return {
loaded: false, loaded: false,
version: "", version: '',
} };
}, },
computed: { computed: {
core () { core () {
return this.$store.getters.core return this.$store.getters.core;
} }
}, },
async beforeMount () { async beforeMount () {
await this.$store.dispatch('loadCore') await this.$store.dispatch('loadCore');
this.$i18n.locale = this.core.language || "en"; this.$i18n.locale = this.core.language || 'en';
// this.$i18n.locale = "ru"; // this.$i18n.locale = "ru";
if (!this.core.setup) { if (!this.core.setup) {
this.$router.push('/setup') this.$router.push('/setup');
} }
if (this.$route.path !== '/setup') { if (this.$route.path !== '/setup') {
if (this.$store.state.admin) { if (this.$store.state.admin) {
await this.$store.dispatch('loadAdmin') await this.$store.dispatch('loadAdmin');
} else { } else {
await this.$store.dispatch('loadRequired') await this.$store.dispatch('loadRequired');
} }
this.loaded = true this.loaded = true;
} }
}, },
async mounted () { async mounted () {
if (this.$route.path !== '/setup') { if (this.$route.path !== '/setup') {
if (this.$store.state.admin) { if (this.$store.state.admin) {
this.logged_in = true this.logged_in = true;
// await this.$store.dispatch('loadAdmin') // await this.$store.dispatch('loadAdmin')
} }
} }
} }
} };
</script> </script>
<style lang="scss"> <style lang="scss">

View File

@ -0,0 +1,89 @@
<template>
<div class="col-12">
<div class="card contain-card mb-4">
<div class="card-header">
{{ $t('downtimes') }}
<!-- <router-link
v-if="$store.state.admin"
to="/dashboard/create_service"
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="loader d-flex align-items-center justify-content-center"
>
<div
class="spinner-border"
role="status"
>
<span class="sr-only">
Loading...
</span>
</div>
</div>
<div v-else>
<DowntimesList />
<Pagination
:get-next-downtimes="getNextDowntimes"
:get-prev-downtimes="getPrevDowntimes"
:skip="params.skip"
/>
</div>
</div>
</div>
</div>
</template>
<script>
import DowntimesList from './DowntimesList.vue';
import Pagination from '../Elements/Pagination.vue';
export default {
name: 'DashboardDowntimes',
components: {
DowntimesList,
Pagination
},
data: function () {
return {
isLoading: false,
params: {
serviceId: null,
start: null,
end: null,
skip: 0,
count: 10,
subStatus: ''
}
};
},
async mounted () {
this.getDowntimes(this.params);
},
methods: {
getDowntimes: async function (params) {
this.isLoading = true;
await this.$store.dispatch({ type: 'getDowntimes', payload: params });
this.isLoading = false;
},
getNextDowntimes: function () {
this.params = { ...this.params, skip: this.params.skip + 1 };
this.getDowntimes(this.params);
},
getPrevDowntimes: function () {
this.params = { ...this.params, skip: this.params.skip + 1 };
this.getDowntimes(this.params);
}
}
};
</script>
<style scoped>
.loader {
min-height: 100px;
}
</style>

View File

@ -0,0 +1,144 @@
<template>
<div>
<div
v-if="downtimes.length === 0"
class="alert alert-dark d-block mt-3 mb-0"
>
You currently don't have any 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
v-if="$store.state.admin"
class="drag_icon d-none d-md-inline"
>
<FontAwesomeIcon
icon="bars"
class="mr-3"
/>
</span>
<span>
{{ downtime.service_id }}
</span>
</td>
<td class="d-none d-md-table-cell">
<span
class=""
>
{{ niceDate(downtime.start) }}
</span>
</td>
<td class="d-none d-md-table-cell">
<span
class=""
>
{{ niceDate(downtime.end) }}
</span>
</td>
<td class="d-none d-md-table-cell">
<span
class=""
>
{{ 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({path: `/dashboard/edit_service/${service.id}`, params: {service: service} })"
>
<FontAwesomeIcon icon="edit" />
</button>
<button
v-if="$store.state.admin"
:disabled="isLoading"
class="btn btn-sm btn-danger"
@click.prevent="deleteService(service)"
>
<FontAwesomeIcon
v-if="!isLoading"
icon="times"
/>
<FontAwesomeIcon
v-if="isLoading"
icon="circle-notch"
spin
/>
</button>
</div>
</td>
</tr>
</thead>
</table>
</div>
</template>
<script>
import { mapState } from 'vuex';
export default {
data: function () {
return {
isLoading: false,
};
},
computed: {
...mapState([ 'downtimes' ])
}
};
</script>

View File

@ -5,75 +5,176 @@
You currently don't have any services! You currently don't have any services!
</div> </div>
</div> </div>
<table v-else class="table"> <table
v-else
class="table"
>
<thead> <thead>
<tr> <tr>
<th scope="col">{{$t('name')}}</th> <th scope="col">
<th scope="col" class="d-none d-md-table-cell">{{$t('status')}}</th> {{ $t('name') }}
<th scope="col" class="d-none d-md-table-cell">{{$t('visibility')}}</th> </th>
<th scope="col" class="d-none d-md-table-cell">{{ $t('group') }}</th> <th
<th scope="col" class="d-none d-md-table-cell" style="width: 130px"> scope="col"
class="d-none d-md-table-cell"
>
{{ $t('status') }}
</th>
<th
scope="col"
class="d-none d-md-table-cell"
>
{{ $t('visibility') }}
</th>
<th
scope="col"
class="d-none d-md-table-cell"
>
{{ $t('group') }}
</th>
<th
scope="col"
class="d-none d-md-table-cell"
style="width: 130px"
>
{{ $t('failures') }} {{ $t('failures') }}
<div class="btn-group float-right" role="group"> <div
<a @click="list_timeframe='3h'" type="button" class="small" :class="{'text-success': list_timeframe==='3h', 'text-muted': list_timeframe!=='3h'}">3h</a> class="btn-group float-right"
<a @click="list_timeframe='12h'" type="button" class="small" :class="{'text-success': list_timeframe==='12h', 'text-muted': list_timeframe!=='12h'}">12h</a> role="group"
<a @click="list_timeframe='24h'" type="button" class="small" :class="{'text-success': list_timeframe==='24h', 'text-muted': list_timeframe!=='24h'}">24h</a> >
<a @click="list_timeframe='7d'" type="button" class="small" :class="{'text-success': list_timeframe==='7d', 'text-muted': list_timeframe!=='7d'}">7d</a> <a
type="button"
class="small"
:class="{'text-success': list_timeframe==='3h', 'text-muted': list_timeframe!=='3h'}"
@click="list_timeframe='3h'"
>
3h
</a>
<a
type="button"
class="small"
:class="{'text-success': list_timeframe==='12h', 'text-muted': list_timeframe!=='12h'}"
@click="list_timeframe='12h'"
>
12h
</a>
<a
type="button"
class="small"
:class="{'text-success': list_timeframe==='24h', 'text-muted': list_timeframe!=='24h'}"
@click="list_timeframe='24h'"
>
24h
</a>
<a
type="button"
class="small"
:class="{'text-success': list_timeframe==='7d', 'text-muted': list_timeframe!=='7d'}"
@click="list_timeframe='7d'"
>
7d
</a>
</div> </div>
</th> </th>
<th scope="col"></th> <th scope="col" />
</tr> </tr>
</thead> </thead>
<draggable id="services_list" tag="tbody" v-model="servicesList" handle=".drag_icon"> <Draggable
<tr v-for="(service, index) in servicesList" :key="service.id"> id="services_list"
v-model="servicesList"
tag="tbody"
handle=".drag_icon"
>
<tr
v-for="(service, index) in servicesList"
:key="service.id"
>
<td> <td>
<span v-if="$store.state.admin" class="drag_icon d-none d-md-inline"> <span
<font-awesome-icon icon="bars" class="mr-3"/> v-if="$store.state.admin"
class="drag_icon d-none d-md-inline"
>
<FontAwesomeIcon
icon="bars"
class="mr-3"
/>
</span> {{ service.name }} </span> {{ service.name }}
</td> </td>
<td class="d-none d-md-table-cell"> <td class="d-none d-md-table-cell">
<span class="badge text-uppercase" :class="{'badge-success': service.online, 'badge-danger': !service.online}"> <span
class="badge text-uppercase"
:class="{'badge-success': service.online, 'badge-danger': !service.online}"
>
{{ service.online ? $t('online') : $t('offline') }} {{ service.online ? $t('online') : $t('offline') }}
</span> </span>
</td> </td>
<td class="d-none d-md-table-cell"> <td class="d-none d-md-table-cell">
<span class="badge text-uppercase" :class="{'badge-primary': service.public, 'badge-secondary': !service.public}"> <span
class="badge text-uppercase"
:class="{'badge-primary': service.public, 'badge-secondary': !service.public}"
>
{{ service.public ? $t('public') : $t('private') }} {{ service.public ? $t('public') : $t('private') }}
</span> </span>
</td> </td>
<td class="d-none d-md-table-cell"> <td class="d-none d-md-table-cell">
<div v-if="service.group_id !== 0"> <div v-if="service.group_id !== 0">
<span class="badge badge-secondary">{{serviceGroup(service)}}</span> <span class="badge badge-secondary">
{{ serviceGroup(service) }}
</span>
</div> </div>
</td> </td>
<td class="d-none d-md-table-cell"> <td class="d-none d-md-table-cell">
<ServiceSparkList :service="service" :timeframe="list_timeframe"/> <ServiceSparkList
:service="service"
:timeframe="list_timeframe"
/>
</td> </td>
<td class="text-right"> <td class="text-right">
<div class="btn-group"> <div class="btn-group">
<button :disabled="loading" v-if="$store.state.admin" @click.prevent="goto({path: `/dashboard/edit_service/${service.id}`, params: {service: service} })" class="btn btn-sm btn-outline-secondary"> <button
<font-awesome-icon icon="edit" /> v-if="$store.state.admin"
:disabled="loading"
class="btn btn-sm btn-outline-secondary"
@click.prevent="goto({path: `/dashboard/edit_service/${service.id}`, params: {service: service} })"
>
<FontAwesomeIcon icon="edit" />
</button> </button>
<button :disabled="loading" @click.prevent="goto({path: serviceLink(service), params: {service: service} })" class="btn btn-sm btn-outline-secondary"> <button
<font-awesome-icon icon="chart-area" /> :disabled="loading"
class="btn btn-sm btn-outline-secondary"
@click.prevent="goto({path: serviceLink(service), params: {service: service} })"
>
<FontAwesomeIcon icon="chart-area" />
</button> </button>
<button :disabled="loading" v-if="$store.state.admin" @click.prevent="deleteService(service)" class="btn btn-sm btn-danger"> <button
<font-awesome-icon v-if="!loading" icon="times" /> v-if="$store.state.admin"
<font-awesome-icon v-if="loading" icon="circle-notch" spin/> :disabled="loading"
class="btn btn-sm btn-danger"
@click.prevent="deleteService(service)"
>
<FontAwesomeIcon
v-if="!loading"
icon="times"
/>
<FontAwesomeIcon
v-if="loading"
icon="circle-notch"
spin
/>
</button> </button>
</div> </div>
</td> </td>
</tr> </tr>
</draggable> </Draggable>
</table> </table>
</div> </div>
</template> </template>
<script> <script>
import Api from "../../API"; import Api from '../../API';
import ServiceSparkList from "@/components/Service/ServiceSparkList"; import ServiceSparkList from '@/components/Service/ServiceSparkList';
import Modal from "@/components/Elements/Modal"; import Modal from '@/components/Elements/Modal';
const draggable = () => import(/* webpackChunkName: "dashboard" */ 'vuedraggable') const Draggable = () => import(/* webpackChunkName: "dashboard" */ 'vuedraggable');
const ToggleSwitch = () => import(/* webpackChunkName: "dashboard" */ '../../forms/ToggleSwitch'); const ToggleSwitch = () => import(/* webpackChunkName: "dashboard" */ '../../forms/ToggleSwitch');
export default { export default {
@ -82,12 +183,12 @@ export default {
Modal, Modal,
ServiceSparkList, ServiceSparkList,
ToggleSwitch, ToggleSwitch,
draggable Draggable
}, },
data () { data () {
return { return {
loading: false, loading: false,
list_timeframe: "12h", list_timeframe: '12h',
chartOpts: { chartOpts: {
chart: { chart: {
type: 'bar', type: 'bar',
@ -137,63 +238,63 @@ export default {
enabled: false, enabled: false,
} }
} }
} };
}, },
computed: { computed: {
servicesList: { servicesList: {
get () { get () {
return this.$store.getters.servicesInOrder return this.$store.getters.servicesInOrder;
}, },
set (value) { set (value) {
this.updateOrder(value) this.updateOrder(value);
} }
} }
}, },
methods: { methods: {
goto (to) { goto (to) {
this.$router.push(to) this.$router.push(to);
}, },
async updateOrder (value) { async updateOrder (value) {
let data = []; const data = [];
value.forEach((s, k) => { value.forEach((s, k) => {
data.push({ service: s.id, order: k + 1 }) data.push({ service: s.id, order: k + 1 });
}); });
await Api.services_reorder(data) await Api.services_reorder(data);
await this.update() await this.update();
}, },
tester (s) { tester (s) {
console.log(s) console.log(s);
}, },
async delete (s) { async delete (s) {
this.loading = true this.loading = true;
await Api.service_delete(s.id) await Api.service_delete(s.id);
await this.update() await this.update();
this.loading = false this.loading = false;
}, },
async deleteService (s) { async deleteService (s) {
const modal = { const modal = {
visible: true, visible: true,
title: "Delete Service", title: 'Delete Service',
body: `Are you sure you want to delete service ${s.name}? This will also delete all failures, checkins, and incidents for this service.`, body: `Are you sure you want to delete service ${s.name}? This will also delete all failures, checkins, and incidents for this service.`,
btnColor: "btn-danger", btnColor: 'btn-danger',
btnText: "Delete Service", btnText: 'Delete Service',
func: () => this.delete(s), func: () => this.delete(s),
} };
this.$store.commit("setModal", modal) this.$store.commit('setModal', modal);
}, },
serviceGroup (s) { serviceGroup (s) {
let group = this.$store.getters.groupById(s.group_id) const group = this.$store.getters.groupById(s.group_id);
if (group) { if (group) {
return group.name return group.name;
} }
return "" return '';
}, },
async update () { async update () {
const services = await Api.services() const services = await Api.services();
this.$store.commit('setServices', services) this.$store.commit('setServices', services);
}
} }
} }
};
</script> </script>
<!-- Add "scoped" attribute to limit CSS to this component only --> <!-- Add "scoped" attribute to limit CSS to this component only -->

View File

@ -1,67 +1,183 @@
<template> <template>
<nav class="navbar navbar-expand-lg"> <nav class="navbar navbar-expand-lg">
<router-link to="/" class="navbar-brand">Statping</router-link> <router-link
<button @click="navopen = !navopen" class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarText" aria-controls="navbarText" aria-expanded="false" aria-label="Toggle navigation"> to="/"
<font-awesome-icon v-if="!navopen" icon="bars"/> class="navbar-brand"
<font-awesome-icon v-if="navopen" icon="times"/> >
Statping
</router-link>
<button
class="navbar-toggler"
type="button"
data-toggle="collapse"
data-target="#navbarText"
aria-controls="navbarText"
aria-expanded="false"
aria-label="Toggle navigation"
@click="navopen = !navopen"
>
<FontAwesomeIcon
v-if="!navopen"
icon="bars"
/>
<FontAwesomeIcon
v-if="navopen"
icon="times"
/>
</button> </button>
<div class="navbar-collapse" :class="{collapse: !navopen}" id="navbarText"> <div
id="navbarText"
class="navbar-collapse"
:class="{ collapse: !navopen }"
>
<ul class="navbar-nav mr-auto"> <ul class="navbar-nav mr-auto">
<li @click="navopen = !navopen" class="nav-item navbar-item"> <li
<router-link to="/dashboard" class="nav-link">{{ $t('dashboard') }}</router-link> class="nav-item navbar-item"
@click="navopen = !navopen"
>
<router-link
to="/dashboard"
class="nav-link"
>
{{ $t('dashboard') }}
</router-link>
</li> </li>
<li @click="navopen = !navopen" class="nav-item navbar-item"> <li
<router-link to="/dashboard/services" class="nav-link">{{ $t('services') }}</router-link> class="nav-item navbar-item"
@click="navopen = !navopen"
>
<router-link
to="/dashboard/services"
class="nav-link"
>
{{
$t('services')
}}
</router-link>
</li> </li>
<li v-if="admin" @click="navopen = !navopen" class="nav-item navbar-item"> <li
<router-link to="/dashboard/users" class="nav-link">{{ $t('users') }}</router-link> class="nav-item navbar-item"
@click="navopen = !navopen"
>
<router-link
to="/dashboard/downtimes"
class="nav-link"
>
{{
'Downtimes'
}}
</router-link>
</li> </li>
<li @click="navopen = !navopen" class="nav-item navbar-item"> <li
<router-link to="/dashboard/messages" class="nav-link">{{ $t('announcements') }}</router-link> v-if="admin"
class="nav-item navbar-item"
@click="navopen = !navopen"
>
<router-link
to="/dashboard/users"
class="nav-link"
>
{{
$t('users')
}}
</router-link>
</li> </li>
<li v-if="admin" @click="navopen = !navopen" class="nav-item navbar-item"> <li
<router-link to="/dashboard/settings" class="nav-link">{{ $t('settings') }}</router-link> class="nav-item navbar-item"
@click="navopen = !navopen"
>
<router-link
to="/dashboard/messages"
class="nav-link"
>
{{
$t('announcements')
}}
</router-link>
</li> </li>
<li v-if="admin" @click="navopen = !navopen" class="nav-item navbar-item"> <li
<router-link to="/dashboard/logs" class="nav-link">{{ $t('logs') }}</router-link> v-if="admin"
class="nav-item navbar-item"
@click="navopen = !navopen"
>
<router-link
to="/dashboard/settings"
class="nav-link"
>
{{
$t('settings')
}}
</router-link>
</li> </li>
<li v-if="admin" @click="navopen = !navopen" class="nav-item navbar-item"> <li
<router-link to="/dashboard/help" class="nav-link">{{ $t('help') }}</router-link> v-if="admin"
class="nav-item navbar-item"
@click="navopen = !navopen"
>
<router-link
to="/dashboard/logs"
class="nav-link"
>
{{
$t('logs')
}}
</router-link>
</li>
<li
v-if="admin"
class="nav-item navbar-item"
@click="navopen = !navopen"
>
<router-link
to="/dashboard/help"
class="nav-link"
>
{{
$t('help')
}}
</router-link>
</li> </li>
</ul> </ul>
<span class="navbar-text"> <span class="navbar-text">
<a href="#" class="nav-link" @click.prevent="logout">{{ $t('logout') }}</a> <a
href="#"
class="nav-link"
@click.prevent="logout"
>
{{
$t('logout')
}}
</a>
</span> </span>
</div> </div>
</nav> </nav>
</template> </template>
<script> <script>
import Api from "../../API" import Api from '../../API';
export default { export default {
name: 'TopNav', name: 'TopNav',
data () { data () {
return { return {
navopen: false navopen: false,
} };
}, },
computed: { computed: {
admin () { admin () {
return this.$store.state.admin return this.$store.state.admin;
} },
}, },
methods: { methods: {
async logout () { async logout () {
await Api.logout() await Api.logout();
this.$store.commit('setHasAllData', false) this.$store.commit('setHasAllData', false);
this.$store.commit('setToken', null) this.$store.commit('setToken', null);
this.$store.commit('setAdmin', false) this.$store.commit('setAdmin', false);
// this.$cookies.remove("statping_auth") // this.$cookies.remove("statping_auth")
await this.$router.push('/logout') await this.$router.push('/logout');
} },
} },
} };
</script> </script>

View File

@ -0,0 +1,47 @@
<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">
&laquo;
</span>
</button>
</li>
<li class="page-item">
<button
class="btn btn-outline-secondary page-link"
aria-label="Next"
@click="getNextDowntimes"
>
<span aria-hidden="true">
&raquo;
</span>
</button>
</li>
</ul>
</template>
<script>
export default {
props: {
getNextDowntimes: {
type: Function,
default: function () {}
},
getPrevDowntimes: {
type: Function,
default: function () {}
},
skip: {
type: Number,
default: 0,
}
}
};
</script>

View File

@ -1,128 +1,196 @@
<template> <template>
<div> <div>
<form @submit.prevent="login" autocomplete="on"> <form
autocomplete="on"
@submit.prevent="login"
>
<div class="form-group row"> <div class="form-group row">
<label for="username" class="col-4 col-form-label">{{$t('username')}}</label> <label
for="username"
class="col-4 col-form-label"
>
{{ $t('username') }}
</label>
<div class="col-8"> <div class="col-8">
<input @keyup="checkForm" @change="checkForm" type="text" v-model="username" autocomplete="username" name="username" class="form-control" id="username" placeholder="admin" autocorrect="off" autocapitalize="none"> <input
id="username"
v-model="username"
type="text"
autocomplete="username"
name="username"
class="form-control"
placeholder="admin"
autocorrect="off"
autocapitalize="none"
@keyup="checkForm"
@change="checkForm"
>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<label for="password" class="col-4 col-form-label">{{$t('password')}}</label> <label
for="password"
class="col-4 col-form-label"
>
{{ $t('password') }}
</label>
<div class="col-8"> <div class="col-8">
<input @keyup="checkForm" @change="checkForm" type="password" v-model="password" autocomplete="current-password" name="password" class="form-control" id="password" placeholder="************"> <input
id="password"
v-model="password"
type="password"
autocomplete="current-password"
name="password"
class="form-control"
placeholder="************"
@keyup="checkForm"
@change="checkForm"
>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">
<div class="col-sm-12"> <div class="col-sm-12">
<div v-if="error" class="alert alert-danger" role="alert"> <div
v-if="error"
class="alert alert-danger"
role="alert"
>
{{ $t('wrong_login') }} {{ $t('wrong_login') }}
</div> </div>
<button @click.prevent="login" type="submit" class="btn btn-block btn-primary" :disabled="disabled || loading"> <button
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/>{{loading ? $t('loading') : $t('sign_in')}} type="submit"
class="btn btn-block btn-primary"
:disabled="disabled || loading"
@click.prevent="login"
>
<FontAwesomeIcon
v-if="loading"
icon="circle-notch"
class="mr-2"
spin
/>{{ loading ? $t('loading') : $t('sign_in') }}
</button> </button>
</div> </div>
</div> </div>
</form> </form>
<a v-if="oauth && oauth.gh_client_id" @click.prevent="GHlogin" href="#" class="mt-4 btn btn-block btn-outline-dark"> <a
<font-awesome-icon :icon="['fab', 'github']" /> Login with Github v-if="oauth && oauth.gh_client_id"
href="#"
class="mt-4 btn btn-block btn-outline-dark"
@click.prevent="GHlogin"
>
<FontAwesomeIcon :icon="['fab', 'github']" /> Login with Github
</a> </a>
<a v-if="oauth && oauth.slack_client_id" @click.prevent="Slacklogin" href="#" class="btn btn-block btn-outline-dark"> <a
<font-awesome-icon :icon="['fab', 'slack']" /> Login with Slack v-if="oauth && oauth.slack_client_id"
href="#"
class="btn btn-block btn-outline-dark"
@click.prevent="Slacklogin"
>
<FontAwesomeIcon :icon="['fab', 'slack']" /> Login with Slack
</a> </a>
<a v-if="oauth && oauth.google_client_id" @click.prevent="Googlelogin" href="#" class="btn btn-block btn-outline-dark"> <a
<font-awesome-icon :icon="['fab', 'google']" /> Login with Google v-if="oauth && oauth.google_client_id"
href="#"
class="btn btn-block btn-outline-dark"
@click.prevent="Googlelogin"
>
<FontAwesomeIcon :icon="['fab', 'google']" /> Login with Google
</a> </a>
<a v-if="oauth && oauth.custom_client_id" @click.prevent="Customlogin" href="#" class="btn btn-block btn-outline-dark"> <a
<font-awesome-icon :icon="['fas', 'address-card']" /> Login with {{oauth.custom_name}} v-if="oauth && oauth.custom_client_id"
href="#"
class="btn btn-block btn-outline-dark"
@click.prevent="Customlogin"
>
<FontAwesomeIcon :icon="['fas', 'address-card']" /> Login with {{ oauth.custom_name }}
</a> </a>
</div> </div>
</template> </template>
<script> <script>
import Api from "../API"; import Api from '../API';
export default { export default {
name: 'FormLogin', name: 'FormLogin',
computed: {
core() {
return this.$store.getters.core
},
oauth() {
return this.$store.getters.oauth
}
},
data () { data () {
return { return {
username: "", username: '',
password: "", password: '',
auth: {}, auth: {},
loading: false, loading: false,
error: false, error: false,
disabled: true, disabled: true,
google_scope: "https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email", google_scope: 'https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email',
slack_scope: "identity.email,identity.basic" slack_scope: 'identity.email,identity.basic'
};
},
computed: {
core () {
return this.$store.getters.core;
},
oauth () {
return this.$store.getters.oauth;
} }
}, },
mounted () { mounted () {
this.$cookies.remove("statping_auth") this.$cookies.remove('statping_auth');
}, },
methods: { methods: {
checkForm () { checkForm () {
if (!this.username || !this.password) { if (!this.username || !this.password) {
this.disabled = true this.disabled = true;
} else { } else {
this.disabled = false this.disabled = false;
} }
}, },
async login () { async login () {
this.loading = true this.loading = true;
this.error = false this.error = false;
const auth = await Api.login(this.username, this.password) const auth = await Api.login(this.username, this.password);
if (auth.error) { if (auth.error) {
this.error = true this.error = true;
} else if (auth.token) { } else if (auth.token) {
this.$cookies.set("statping_auth", auth.token) this.$cookies.set('statping_auth', auth.token);
await this.$store.dispatch('loadAdmin') await this.$store.dispatch('loadAdmin');
this.$store.commit('setAdmin', auth.admin) this.$store.commit('setAdmin', auth.admin);
this.$store.commit('setLoggedIn', true) this.$store.commit('setLoggedIn', true);
this.$router.push('/dashboard') this.$router.push('/dashboard');
} }
this.loading = false this.loading = false;
}, },
encode (val) { encode (val) {
return encodeURI(val) return encodeURI(val);
}, },
custom_scopes () { custom_scopes () {
let scopes = [] const scopes = [];
if (this.oauth.custom_open_id) { if (this.oauth.custom_open_id) {
scopes.push("openid") scopes.push('openid');
} }
scopes.push(this.oauth.custom_scopes.split(",")) scopes.push(this.oauth.custom_scopes.split(','));
if (scopes.length !== 0) { if (scopes.length !== 0) {
return "&scopes="+scopes.join(",") return '&scopes='+scopes.join(',');
} }
return "" return '';
}, },
GHlogin () { GHlogin () {
window.location = `https://github.com/login/oauth/authorize?client_id=${this.oauth.gh_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/github")}&scope=read:user,read:org` window.location = `https://github.com/login/oauth/authorize?client_id=${this.oauth.gh_client_id}&redirect_uri=${this.encode(this.core.domain+'/oauth/github')}&scope=read:user,read:org`;
}, },
Slacklogin () { Slacklogin () {
window.location = `https://slack.com/oauth/authorize?client_id=${this.oauth.slack_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/slack")}&scope=identity.basic` window.location = `https://slack.com/oauth/authorize?client_id=${this.oauth.slack_client_id}&redirect_uri=${this.encode(this.core.domain+'/oauth/slack')}&scope=identity.basic`;
}, },
Googlelogin () { Googlelogin () {
window.location = `https://accounts.google.com/signin/oauth?client_id=${this.oauth.google_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/google")}&response_type=code&scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email` window.location = `https://accounts.google.com/signin/oauth?client_id=${this.oauth.google_client_id}&redirect_uri=${this.encode(this.core.domain+'/oauth/google')}&response_type=code&scope=https://www.googleapis.com/auth/userinfo.profile+https://www.googleapis.com/auth/userinfo.email`;
}, },
Customlogin () { Customlogin () {
window.location = `${this.oauth.custom_endpoint_auth}?client_id=${this.oauth.custom_client_id}&redirect_uri=${this.encode(this.core.domain+"/oauth/custom")}&response_type=code${this.custom_scopes()}` window.location = `${this.oauth.custom_endpoint_auth}?client_id=${this.oauth.custom_client_id}&redirect_uri=${this.encode(this.core.domain+'/oauth/custom')}&response_type=code${this.custom_scopes()}`;
}
} }
} }
};
</script> </script>
<!-- Add "scoped" attribute to limit CSS to this component only --> <!-- Add "scoped" attribute to limit CSS to this component only -->

View File

@ -1,9 +1,9 @@
import {library} from '@fortawesome/fontawesome-svg-core' import { library } from '@fortawesome/fontawesome-svg-core';
import { fas } from '@fortawesome/fontawesome-free-solid'; import { fas } from '@fortawesome/fontawesome-free-solid';
import { fab } from '@fortawesome/free-brands-svg-icons'; import { fab } from '@fortawesome/free-brands-svg-icons';
import {FontAwesomeIcon} from '@fortawesome/vue-fontawesome' import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
import Vue from "vue"; import Vue from 'vue';
library.add(fas, fab) library.add(fas, fab);
Vue.component('font-awesome-icon', FontAwesomeIcon) Vue.component('FontAwesomeIcon', FontAwesomeIcon);

View File

@ -1,145 +1,149 @@
const english = { const english = {
settings: "Settings", settings: 'Settings',
dashboard: "Dashboard", dashboard: 'Dashboard',
services: "Services", start_time: 'Start Time',
service: "Service", end_time: 'End Time',
failures: "Failures", actions: 'Actions',
users: "Users", services: 'Services',
login: "Login", downtimes: 'Downtimes',
logout: "Logout", service: 'Service',
online: "Online", failures: 'Failures',
offline: "Offline", users: 'Users',
configs: "Configuration", login: 'Login',
username: "Username", logout: 'Logout',
password: "Password", online: 'Online',
email: "Email", offline: 'Offline',
confirm_password: "Confirm Password", configs: 'Configuration',
uptime: "Uptime", username: 'Username',
name: "Name", password: 'Password',
copy: "Copy", email: 'Email',
close: "Close", confirm_password: 'Confirm Password',
secret: "Secret", uptime: 'Uptime',
regen_api: "Regenerate API Keys", name: 'Name',
copy: 'Copy',
close: 'Close',
secret: 'Secret',
regen_api: 'Regenerate API Keys',
regen_desc: regen_desc:
"API Secret is used for read create update and delete routes. You can Regenerate API Keys if you need to.", 'API Secret is used for read create update and delete routes. You can Regenerate API Keys if you need to.',
visibility: "Visibility", visibility: 'Visibility',
group: "Group", group: 'Group',
group_create: "Create Group", group_create: 'Create Group',
group_update: "Update Group", group_update: 'Update Group',
group_public_desc: "Show group services to the public", group_public_desc: 'Show group services to the public',
groups: "Groups", groups: 'Groups',
no_group: "No Group", no_group: 'No Group',
public: "Public", public: 'Public',
private: "Private", private: 'Private',
announcements: "Announcements", announcements: 'Announcements',
notifiers: "Notifiers", notifiers: 'Notifiers',
logs: "Logs", logs: 'Logs',
help: "Help", help: 'Help',
type: "Type", type: 'Type',
edit: "Edit", edit: 'Edit',
update: "Update", update: 'Update',
create: "Create", create: 'Create',
view: "View", view: 'View',
save: "Save", save: 'Save',
title: "Title", title: 'Title',
status: "Status", status: 'Status',
begins: "Begins", begins: 'Begins',
total_services: "Total Services", total_services: 'Total Services',
online_services: "Online Services", online_services: 'Online Services',
request_timeout: "Request Timeout", request_timeout: 'Request Timeout',
service_never_online: "Service has never been online", service_never_online: 'Service has never been online',
service_online_check: "Online checked", service_online_check: 'Online checked',
service_offline_time: "Service has been offline for", service_offline_time: 'Service has been offline for',
days_ago: "Days Ago", days_ago: 'Days Ago',
today: "Today", today: 'Today',
week: "Week", week: 'Week',
month: "Month", month: 'Month',
day: "Day", day: 'Day',
hour: "Hour", hour: 'Hour',
minute: "Minute", minute: 'Minute',
failures_24_hours: "Failures last 24 hours", failures_24_hours: 'Failures last 24 hours',
no_services: "You currently don't have any services!", no_services: 'You currently don\'t have any services!',
theme: "Theme", theme: 'Theme',
cache: "Cache", cache: 'Cache',
authentication: "Authentication", authentication: 'Authentication',
import: "Import", import: 'Import',
main_settings: "Main Settings", main_settings: 'Main Settings',
variables: "Variables", variables: 'Variables',
docs: "Documentation", docs: 'Documentation',
links: "Links", links: 'Links',
changelog: "Change Log", changelog: 'Change Log',
repo: "Repository", repo: 'Repository',
language: "Language", language: 'Language',
db_connection: "Database Connection", db_connection: 'Database Connection',
db_host: "Database Host", db_host: 'Database Host',
db_port: "Database Port", db_port: 'Database Port',
db_username: "Database Username", db_username: 'Database Username',
db_password: "Database Password", db_password: 'Database Password',
db_database: "Database Name", db_database: 'Database Name',
send_reports: "Send Error Reports", send_reports: 'Send Error Reports',
send_reports_desc: "Send errors to Statping for debugging", send_reports_desc: 'Send errors to Statping for debugging',
project_name: "Status Page Name", project_name: 'Status Page Name',
description: "Description", description: 'Description',
domain: "Domain", domain: 'Domain',
enable_cdn: "Enable CDN", enable_cdn: 'Enable CDN',
newsletter: "Newsletter", newsletter: 'Newsletter',
newsletter_note: "We will only send you an email for major changes", newsletter_note: 'We will only send you an email for major changes',
loading: "Loading", loading: 'Loading',
save_settings: "Save Settings", save_settings: 'Save Settings',
average_response: "Average Response", average_response: 'Average Response',
last_uptime: "Uptime last", last_uptime: 'Uptime last',
sign_in: "Sign In", sign_in: 'Sign In',
last_login: "Last Login", last_login: 'Last Login',
admin: "Admin", admin: 'Admin',
user: "User", user: 'User',
failed: "Failed", failed: 'Failed',
wrong_login: "Incorrect username or password", wrong_login: 'Incorrect username or password',
theme_editor: "Theme Editor", theme_editor: 'Theme Editor',
enable_assets: "Enable Local Assets", enable_assets: 'Enable Local Assets',
assets_desc: assets_desc:
"Customize your status page design by enabling local assets. This will create a 'assets' directory containing all CSS.", 'Customize your status page design by enabling local assets. This will create a \'assets\' directory containing all CSS.',
assets_btn: "Enable Local Assets", assets_btn: 'Enable Local Assets',
assets_loading: "Creating Assets", assets_loading: 'Creating Assets',
assets_dir: "Assets Directory", assets_dir: 'Assets Directory',
footer: "Footer", footer: 'Footer',
footer_notes: "You can use HTML tags in footer", footer_notes: 'You can use HTML tags in footer',
global_announcement: "Global Announcement", global_announcement: 'Global Announcement',
announcement_date: "Announcement Date Range", announcement_date: 'Announcement Date Range',
notify_users: "Notify Users", notify_users: 'Notify Users',
notify_desc: "Notify Users Before Scheduled Time", notify_desc: 'Notify Users Before Scheduled Time',
notify_method: "Notification Method", notify_method: 'Notification Method',
notify_before: "Notify Before", notify_before: 'Notify Before',
message_create: "Create Announcement", message_create: 'Create Announcement',
message_edit: "Edit Announcement", message_edit: 'Edit Announcement',
minutes: "Minutes", minutes: 'Minutes',
hours: "Hours", hours: 'Hours',
days: "Days", days: 'Days',
user_create: "Create User", user_create: 'Create User',
user_update: "Update User", user_update: 'Update User',
administrator: "Administrator", administrator: 'Administrator',
checkins: "Checkins", checkins: 'Checkins',
incidents: "Incidents", incidents: 'Incidents',
service_info: "Service Info", service_info: 'Service Info',
service_name: "Service Name", service_name: 'Service Name',
service_description: "Service Description", service_description: 'Service Description',
service_type: "Service Type", service_type: 'Service Type',
permalink: "Permalink URL", permalink: 'Permalink URL',
service_public: "Public Service", service_public: 'Public Service',
check_interval: "Check Interval", check_interval: 'Check Interval',
service_endpoint: "Service Endpoint", service_endpoint: 'Service Endpoint',
service_check: "Service Check Type", service_check: 'Service Check Type',
service_timeout: "Request Timeout", service_timeout: 'Request Timeout',
expected_resp: "Expected Response", expected_resp: 'Expected Response',
expected_code: "Expected Status Code", expected_code: 'Expected Status Code',
follow_redir: "Follow Redirects", follow_redir: 'Follow Redirects',
verify_ssl: "Verify SSL", verify_ssl: 'Verify SSL',
tls_cert: "Use TLS Cert", tls_cert: 'Use TLS Cert',
notification_opts: "Notification Options", notification_opts: 'Notification Options',
notifications_enable: "Enable Notifications", notifications_enable: 'Enable Notifications',
notify_after: "Notify After Failures", notify_after: 'Notify After Failures',
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',
}; };
export default english; export default english;

View File

@ -1,19 +1,19 @@
import Vue from 'vue' import Vue from 'vue';
import VueRouter from 'vue-router' import VueRouter from 'vue-router';
import VueApexCharts from 'vue-apexcharts' import VueApexCharts from 'vue-apexcharts';
import VueObserveVisibility from 'vue-observe-visibility' import VueObserveVisibility from 'vue-observe-visibility';
import VueClipboard from 'vue-clipboard2' import VueClipboard from 'vue-clipboard2';
import VueCookies from 'vue-cookies' import VueCookies from 'vue-cookies';
import VueI18n from 'vue-i18n' import VueI18n from 'vue-i18n';
import router from './routes' import router from './routes';
import "./mixin" import './mixin';
import "./icons" import './icons';
import store from './store' import store from './store';
import language from './languages' import language from './languages';
const App = () => import(/* webpackChunkName: "index" */ '@/App.vue') const App = () => import(/* webpackChunkName: "index" */ '@/App.vue');
Vue.component('apexchart', VueApexCharts) Vue.component('apexchart', VueApexCharts);
Vue.use(VueClipboard); Vue.use(VueClipboard);
Vue.use(VueRouter); Vue.use(VueRouter);
@ -22,19 +22,19 @@ Vue.use(VueCookies);
Vue.use(VueI18n); Vue.use(VueI18n);
const i18n = new VueI18n({ const i18n = new VueI18n({
fallbackLocale: "en", fallbackLocale: 'en',
messages: language messages: language
}); });
Vue.$cookies.config('3d') Vue.$cookies.config('3d');
Vue.config.productionTip = process.env.NODE_ENV !== 'production' Vue.config.productionTip = process.env.NODE_ENV !== 'production';
Vue.config.devtools = process.env.NODE_ENV !== 'production' Vue.config.devtools = process.env.NODE_ENV !== 'production';
Vue.config.performance = process.env.NODE_ENV !== 'production' Vue.config.performance = process.env.NODE_ENV !== 'production';
new Vue({ new Vue({
router, router,
store, store,
i18n, i18n,
render: h => h(App), render: h => h(App),
}).$mount('#app') }).$mount('#app');

View File

@ -1,261 +1,261 @@
import Vue from "vue"; import Vue from 'vue';
const { startOfDay, startOfHour, startOfWeek, endOfMonth, endOfHour, startOfToday, startOfTomorrow, startOfYesterday, endOfYesterday, endOfTomorrow, endOfToday, endOfDay, startOfMonth, lastDayOfMonth, subSeconds, getUnixTime, fromUnixTime, differenceInSeconds, formatDistance, addMonths, addSeconds, isWithinInterval } = require('date-fns') const { startOfDay, startOfHour, startOfWeek, endOfMonth, endOfHour, startOfToday, startOfTomorrow, startOfYesterday, endOfYesterday, endOfTomorrow, endOfToday, endOfDay, startOfMonth, lastDayOfMonth, subSeconds, getUnixTime, fromUnixTime, differenceInSeconds, formatDistance, addMonths, addSeconds, isWithinInterval } = require('date-fns');
import formatDistanceToNow from 'date-fns/formatDistanceToNow' import formatDistanceToNow from 'date-fns/formatDistanceToNow';
import format from 'date-fns/format' import format from 'date-fns/format';
import parseISO from 'date-fns/parseISO' import parseISO from 'date-fns/parseISO';
import isBefore from 'date-fns/isBefore' import isBefore from 'date-fns/isBefore';
import isAfter from 'date-fns/isAfter' import isAfter from 'date-fns/isAfter';
import { roundToNearestMinutes } from 'date-fns' import { roundToNearestMinutes } from 'date-fns';
export default Vue.mixin({ export default Vue.mixin({
methods: { methods: {
now () { now () {
return new Date() return new Date();
}, },
isNumeric: function (n) { isNumeric: function (n) {
return !isNaN(parseFloat(n)) && isFinite(n); return !isNaN(parseFloat(n)) && isFinite(n);
}, },
current () { current () {
return parseISO(new Date()) return parseISO(new Date());
}, },
startToday () { startToday () {
return startOfToday() return startOfToday();
}, },
secondsHumanize (val) { secondsHumanize (val) {
return `${val} ${this.$t('second', val)}` return `${val} ${this.$t('second', val)}`;
}, },
utc (val) { utc (val) {
return new Date.UTC(val) return new Date.UTC(val);
}, },
ago (t1) { ago (t1) {
return formatDistanceToNow(parseISO(t1)) return formatDistanceToNow(parseISO(t1));
}, },
daysInMonth (t1) { daysInMonth (t1) {
return lastDayOfMonth(t1) return lastDayOfMonth(t1);
}, },
nowSubtract (seconds) { nowSubtract (seconds) {
return subSeconds(this.now(), seconds) return subSeconds(this.now(), seconds);
}, },
isAfter (date, compare) { isAfter (date, compare) {
return isAfter(date, parseISO(compare)) return isAfter(date, parseISO(compare));
}, },
isBefore (date, compare) { isBefore (date, compare) {
return isBefore(date, parseISO(compare)) return isBefore(date, parseISO(compare));
}, },
dur (t1, t2) { dur (t1, t2) {
return formatDistance(t1, t2) return formatDistance(t1, t2);
}, },
format(val, type = "EEEE, MMM do h:mma") { format (val, type = 'EEE, MMM do h:mma') {
return format(val, type) return format(val, type);
}, },
niceDate (val) { niceDate (val) {
return format(parseISO(val), "EEEE, MMM do h:mma") return format(parseISO(val), 'EEE, MMM do h:mma');
}, },
parseISO (v) { parseISO (v) {
return parseISO(v) return parseISO(v);
}, },
round (minutes) { round (minutes) {
return roundToNearestMinutes(minutes) return roundToNearestMinutes(minutes);
}, },
endOf (method, val) { endOf (method, val) {
switch (method) { switch (method) {
case "hour": case 'hour':
return endOfHour(val) return endOfHour(val);
case "day": case 'day':
return endOfDay(val) return endOfDay(val);
case "today": case 'today':
return endOfToday() return endOfToday();
case "tomorrow": case 'tomorrow':
return endOfTomorrow() return endOfTomorrow();
case "yesterday": case 'yesterday':
return endOfYesterday() return endOfYesterday();
case "month": case 'month':
return endOfMonth(val) return endOfMonth(val);
} }
return val return val;
}, },
startEndParams (start, end, group) { startEndParams (start, end, group) {
start = this.beginningOf("hour", start) start = this.beginningOf('hour', start);
end = this.endOf("hour", end) end = this.endOf('hour', end);
return {start: this.toUnix(start), end: this.toUnix(end), group: group} return { start: this.toUnix(start), end: this.toUnix(end), group: group };
}, },
beginningOf (method, val) { beginningOf (method, val) {
switch (method) { switch (method) {
case "hour": case 'hour':
return startOfHour(val) return startOfHour(val);
case "day": case 'day':
return startOfDay(val) return startOfDay(val);
case "today": case 'today':
return startOfToday() return startOfToday();
case "tomorrow": case 'tomorrow':
return startOfTomorrow() return startOfTomorrow();
case "yesterday": case 'yesterday':
return startOfYesterday() return startOfYesterday();
case "week": case 'week':
return startOfWeek(val) return startOfWeek(val);
case "month": case 'month':
return startOfMonth(val) return startOfMonth(val);
} }
return val return val;
}, },
isZero (val) { isZero (val) {
return getUnixTime(parseISO(val)) <= 0 return getUnixTime(parseISO(val)) <= 0;
}, },
smallText (s) { smallText (s) {
if (s.online) { if (s.online) {
return `${this.$t('service_online_check')} ${this.ago(s.last_success)} ago` return `${this.$t('service_online_check')} ${this.ago(s.last_success)} ago`;
} else { } else {
const last = s.last_failure const last = s.last_failure;
if (last) { if (last) {
return `Offline, last error: ${last} ${this.ago(last.created_at)}` return `Offline, last error: ${last} ${this.ago(last.created_at)}`;
} }
if (this.isZero(s.last_success)) { if (this.isZero(s.last_success)) {
return this.$t('service_never_online') return this.$t('service_never_online');
} }
return `${this.$t('service_offline_time')} ${this.ago(s.last_success)}` return `${this.$t('service_offline_time')} ${this.ago(s.last_success)}`;
} }
}, },
round_time (frame, val) { round_time (frame, val) {
switch (frame) { switch (frame) {
case "15m": case '15m':
return roundToNearestMinutes(val, {nearestTo: 60 * 15}) return roundToNearestMinutes(val, { nearestTo: 60 * 15 });
case "30m": case '30m':
return roundToNearestMinutes(val, {nearestTo: 60 * 30}) return roundToNearestMinutes(val, { nearestTo: 60 * 30 });
case "1h": case '1h':
return roundToNearestMinutes(val, {nearestTo: 3600}) return roundToNearestMinutes(val, { nearestTo: 3600 });
case "3h": case '3h':
return roundToNearestMinutes(val, {nearestTo: 3600 * 3}) return roundToNearestMinutes(val, { nearestTo: 3600 * 3 });
case "6h": case '6h':
return roundToNearestMinutes(val, {nearestTo: 3600 * 6}) return roundToNearestMinutes(val, { nearestTo: 3600 * 6 });
case "12h": case '12h':
return roundToNearestMinutes(val, {nearestTo: 3600 * 12}) return roundToNearestMinutes(val, { nearestTo: 3600 * 12 });
case "24h": case '24h':
return roundToNearestMinutes(val, {nearestTo: 3600 * 24}) return roundToNearestMinutes(val, { nearestTo: 3600 * 24 });
} }
}, },
toUnix (val) { toUnix (val) {
return getUnixTime(val) return getUnixTime(val);
}, },
fromUnix (val) { fromUnix (val) {
return fromUnixTime(val) return fromUnixTime(val);
}, },
isBetween (t, start, end) { isBetween (t, start, end) {
return isWithinInterval(t, {start: parseISO(start), end: parseISO(end)}) return isWithinInterval(t, { start: parseISO(start), end: parseISO(end) });
}, },
hour () { hour () {
return 3600 return 3600;
}, },
day () { day () {
return 3600 * 24 return 3600 * 24;
}, },
maxDate () { maxDate () {
return new Date(8640000000000000) return new Date(8640000000000000);
}, },
copy (txt) { copy (txt) {
this.$copyText(txt).then(function (e) { this.$copyText(txt).then((e) => {
alert('Copied: \n' + txt) alert('Copied: \n' + txt);
}); });
}, },
serviceLink (service) { serviceLink (service) {
if (service.permalink) { if (service.permalink) {
service = this.$store.getters.serviceById(service.permalink) service = this.$store.getters.serviceById(service.permalink);
} }
if (service === undefined || this.isEmptyObject(service)) { if (service === undefined || this.isEmptyObject(service)) {
return `/service/0` return `/service/0`;
} }
let link = service.permalink ? service.permalink : service.id const link = service.permalink ? service.permalink : service.id;
return `/service/${link}` return `/service/${link}`;
}, },
isEmptyObject (obj) { isEmptyObject (obj) {
return Object.keys(obj).length === 0 && obj.constructor === Object return Object.keys(obj).length === 0 && obj.constructor === Object;
}, },
isInt (n) { isInt (n) {
return n % 1 === 0; return n % 1 === 0;
}, },
isAdmin () { isAdmin () {
return this.$store.state.admin return this.$store.state.admin;
}, },
iconName (name) { iconName (name) {
switch (name) { switch (name) {
case "fas fa-terminal": case 'fas fa-terminal':
return "terminal" return 'terminal';
case "fab fa-discord": case 'fab fa-discord':
return ["fab", "discord"] return [ 'fab', 'discord' ];
case "far fa-envelope": case 'far fa-envelope':
return "envelope" return 'envelope';
case "far fa-bell": case 'far fa-bell':
return "bell" return 'bell';
case "fas fa-mobile-alt": case 'fas fa-mobile-alt':
return "mobile" return 'mobile';
case "fa dot-circle": case 'fa dot-circle':
return ["fa", "dot-circle"] return [ 'fa', 'dot-circle' ];
case "fas envelope-square": case 'fas envelope-square':
return ["fas", "envelope-square"] return [ 'fas', 'envelope-square' ];
case "fab fa-slack": case 'fab fa-slack':
return ["fab", "slack-hash"] return [ 'fab', 'slack-hash' ];
case "fab fa-telegram-plane": case 'fab fa-telegram-plane':
return ["fab", "telegram-plane"] return [ 'fab', 'telegram-plane' ];
case "far fa-comment-alt": case 'far fa-comment-alt':
return "comment" return 'comment';
case "fas fa-code-branch": case 'fas fa-code-branch':
return "code-branch" return 'code-branch';
case "csv": case 'csv':
return "file" return 'file';
case "docker": case 'docker':
return ["fab", "docker"] return [ 'fab', 'docker' ];
case "traefik": case 'traefik':
return "server" return 'server';
default: default:
return "bars" return 'bars';
} }
}, },
toBarData (data = []) { toBarData (data = []) {
let newSet = []; const newSet = [];
data.forEach((f) => { data.forEach((f) => {
newSet.push([this.toUnix(this.parseISO(f.timeframe)), f.amount]) newSet.push([ this.toUnix(this.parseISO(f.timeframe)), f.amount ]);
}) });
return newSet return newSet;
}, },
convertToChartData (data = [], multiplier = 1, asInt = false) { convertToChartData (data = [], multiplier = 1, asInt = false) {
if (!data) { if (!data) {
return {data: []} return { data: [] };
} }
let newSet = []; const newSet = [];
data.forEach((f) => { data.forEach((f) => {
let amount = f.amount * multiplier; let amount = f.amount * multiplier;
if (asInt) { if (asInt) {
amount = amount.toFixed(0) amount = amount.toFixed(0);
} }
newSet.push({ newSet.push({
x: f.timeframe, x: f.timeframe,
y: amount y: amount
}) });
}) });
return {data: newSet} return { data: newSet };
}, },
humanTime (val) { humanTime (val) {
if (val >= 1000) { if (val >= 1000) {
return Math.round(val / 1000) + " ms" return Math.round(val / 1000) + ' ms';
} }
return val + " μs" return val + ' μs';
}, },
humanTimeNum (val) { humanTimeNum (val) {
if (val >= 1000) { if (val >= 1000) {
return Math.round(val / 1000) return Math.round(val / 1000);
} }
return val return val;
}, },
firstDayOfMonth (date) { firstDayOfMonth (date) {
return startOfMonth(date) return startOfMonth(date);
}, },
lastDayOfMonth (month) { lastDayOfMonth (month) {
return lastDayOfMonth(month) return lastDayOfMonth(month);
}, },
addMonths (date, amount) { addMonths (date, amount) {
return addMonths(date, amount) return addMonths(date, amount);
}, },
addSeconds (date, amount) { addSeconds (date, amount) {
return addSeconds(date, amount) return addSeconds(date, amount);
} }
} }
}); });

View File

@ -1,8 +1,11 @@
<template> <template>
<div class="offset-md-3 offset-lg-4 offset-0 col-lg-4 col-md-6 mt-5"> <div class="offset-md-3 offset-lg-4 offset-0 col-lg-4 col-md-6 mt-5">
<div class="offset-1 offset-lg-2 col-lg-8 col-10 mb-4 mb-md-3"> <div class="offset-1 offset-lg-2 col-lg-8 col-10 mb-4 mb-md-3">
<img alt="Statping Login" class="embed-responsive" src="banner.png"> <img
alt="Statping Login"
class="embed-responsive"
src="banner.png"
>
</div> </div>
<div class="login_container col-12 p-4"> <div class="login_container col-12 p-4">
@ -12,7 +15,7 @@
</template> </template>
<script> <script>
const FormLogin = () => import(/* webpackChunkName: "index" */ '@/forms/Login') const FormLogin = () => import(/* webpackChunkName: "index" */ '@/forms/Login');
export default { export default {
name: 'Login', name: 'Login',
@ -22,7 +25,7 @@
data () { data () {
return { return {
} };
}, },
mounted () { mounted () {
@ -30,7 +33,7 @@
methods: { methods: {
} }
} };
</script> </script>
<!-- Add "scoped" attribute to limit CSS to this component only --> <!-- Add "scoped" attribute to limit CSS to this component only -->

View File

@ -1,29 +1,30 @@
const Index = () => import(/* webpackChunkName: "index" */ '@/pages/Index') const Index = () => import(/* webpackChunkName: "index" */ '@/pages/Index');
const Dashboard = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Dashboard') const Dashboard = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Dashboard');
const DashboardIndex = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardIndex') const DashboardIndex = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardIndex');
const DashboardUsers = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardUsers') const DashboardUsers = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardUsers');
const DashboardServices = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardServices') const DashboardServices = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardServices');
const DashboardMessages = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardMessages') const DashboardMessages = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardMessages');
const EditService = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/EditService') const EditService = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/EditService');
const Logs = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Logs') const Logs = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Logs');
const Help = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Help') const Help = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Help');
const Settings = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Settings') const Settings = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Settings');
const Login = () => import(/* webpackChunkName: "index" */ '@/pages/Login') const Login = () => import(/* webpackChunkName: "index" */ '@/pages/Login');
const Service = () => import(/* webpackChunkName: "index" */ '@/pages/Service') const Service = () => import(/* webpackChunkName: "index" */ '@/pages/Service');
const Setup = () => import(/* webpackChunkName: "index" */ '@/forms/Setup') const Setup = () => import(/* webpackChunkName: "index" */ '@/forms/Setup');
const Incidents = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Incidents') const Incidents = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Incidents');
const Checkins = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Checkins') const Checkins = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Checkins');
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: "downtimes" */ '@/components/Dashboard/DashboardDowntimes');
import VueRouter from "vue-router"; import VueRouter from 'vue-router';
import Api from "./API"; import Api from './API';
import store from "./store" import store from './store';
const Loading = { const Loading = {
template: '<div class="jumbotron">LOADING</div>' template: '<div class="jumbotron">LOADING</div>'
} };
const routes = [ const routes = [
{ {
@ -50,34 +51,34 @@ const routes = [
if (to.matched.some(record => record.meta.requiresAuth)) { if (to.matched.some(record => record.meta.requiresAuth)) {
if (to.path !== '/login') { if (to.path !== '/login') {
if (store.getters.loggedIn) { if (store.getters.loggedIn) {
next() next();
return return;
} }
const token = $cookies.get('statping_auth') const token = $cookies.get('statping_auth');
if (!token) { if (!token) {
next('/login') next('/login');
return return;
} }
try { try {
const jwt = await Api.check_token(token) const jwt = await Api.check_token(token);
store.commit('setAdmin', jwt.admin) store.commit('setAdmin', jwt.admin);
if (jwt.admin) { if (jwt.admin) {
store.commit('setLoggedIn', true) store.commit('setLoggedIn', true);
store.commit('setUser', true) store.commit('setUser', true);
} else { } else {
store.commit('setLoggedIn', false) store.commit('setLoggedIn', false);
next('/login') next('/login');
return return;
} }
} catch (e) { } catch (e) {
console.error(e) console.error(e);
next('/login') next('/login');
return return;
} }
} }
next() next();
} else { } else {
next() next();
} }
}, },
children: [ { children: [ {
@ -137,6 +138,13 @@ 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: 'messages', path: 'messages',
component: DashboardMessages, component: DashboardMessages,
@ -200,21 +208,21 @@ const router = new VueRouter({
mode: 'history', mode: 'history',
scrollBehavior (to, from, savedPosition) { scrollBehavior (to, from, savedPosition) {
if (savedPosition) { if (savedPosition) {
return savedPosition return savedPosition;
} else { } else {
return { x: 0, y: 0 } return { x: 0, y: 0 };
} }
}, },
routes routes
}) });
router.beforeEach((to, from, next) => { router.beforeEach((to, from, next) => {
const nearestWithTitle = to.matched.slice().reverse().find(r => r.meta && r.meta.title); const nearestWithTitle = to.matched.slice().reverse().find(r => r.meta && r.meta.title);
const nearestWithMeta = to.matched.slice().reverse().find(r => r.meta && r.meta.metaTags); const nearestWithMeta = to.matched.slice().reverse().find(r => r.meta && r.meta.metaTags);
const previousNearestWithMeta = from.matched.slice().reverse().find(r => r.meta && r.meta.metaTags); const previousNearestWithMeta = from.matched.slice().reverse().find(r => r.meta && r.meta.metaTags);
if (nearestWithTitle) document.title = nearestWithTitle.meta.title; if (nearestWithTitle) {document.title = nearestWithTitle.meta.title;}
Array.from(document.querySelectorAll('[data-vue-router-controlled]')).map(el => el.parentNode.removeChild(el)); Array.from(document.querySelectorAll('[data-vue-router-controlled]')).map(el => el.parentNode.removeChild(el));
if (!nearestWithMeta) return next(); if (!nearestWithMeta) {return next();}
nearestWithMeta.meta.metaTags.map(tagDef => { nearestWithMeta.meta.metaTags.map(tagDef => {
const tag = document.createElement('meta'); const tag = document.createElement('meta');
Object.keys(tagDef).forEach(key => { Object.keys(tagDef).forEach(key => {
@ -227,4 +235,4 @@ router.beforeEach((to, from, next) => {
next(); next();
}); });
export default router export default router;

View File

@ -1,19 +1,19 @@
import Vuex from 'vuex' import Vuex from 'vuex';
import Vue from 'vue' import Vue from 'vue';
import Api from "./API" import Api from './API';
Vue.use(Vuex) Vue.use(Vuex);
export const HAS_ALL_DATA = 'HAS_ALL_DATA' export const HAS_ALL_DATA = 'HAS_ALL_DATA';
export const HAS_PUBLIC_DATA = 'HAS_PUBLIC_DATA' export const HAS_PUBLIC_DATA = 'HAS_PUBLIC_DATA';
export const GET_CORE = 'GET_CORE' export const GET_CORE = 'GET_CORE';
export const GET_SERVICES = 'GET_SERVICES' export const GET_SERVICES = 'GET_SERVICES';
export const GET_TOKEN = 'GET_TOKEN' export const GET_TOKEN = 'GET_TOKEN';
export const GET_GROUPS = 'GET_GROUPS' export const GET_GROUPS = 'GET_GROUPS';
export const GET_MESSAGES = 'GET_MESSAGES' export const GET_MESSAGES = 'GET_MESSAGES';
export const GET_NOTIFIERS = 'GET_NOTIFIERS' export const GET_NOTIFIERS = 'GET_NOTIFIERS';
export const GET_USERS = 'GET_USERS' export const GET_USERS = 'GET_USERS';
export default new Vuex.Store({ export default new Vuex.Store({
state: { state: {
@ -32,12 +32,13 @@ export default new Vuex.Store({
admin: false, admin: false,
user: false, user: false,
loggedIn: false, loggedIn: false,
downtimes: [],
modal: { modal: {
visible: false, visible: false,
title: "Modal Header", title: 'Modal Header',
body: "This is the content for the modal body", body: 'This is the content for the modal body',
btnText: "Save Changes", btnText: 'Save Changes',
btnColor: "btn-primary", btnColor: 'btn-primary',
func: null, func: null,
} }
}, },
@ -69,130 +70,141 @@ export default new Vuex.Store({
groupsClean: state => state.groups.filter(g => g.name !== '').sort((a, b) => a.order_id - b.order_id), groupsClean: state => state.groups.filter(g => g.name !== '').sort((a, b) => a.order_id - b.order_id),
groupsCleanInOrder: state => state.groups.filter(g => g.name !== '').sort((a, b) => a.order_id - b.order_id).sort((a, b) => a.order_id - b.order_id), groupsCleanInOrder: state => state.groups.filter(g => g.name !== '').sort((a, b) => a.order_id - b.order_id).sort((a, b) => a.order_id - b.order_id),
serviceCheckins: (state) => (id) => { serviceCheckins: (state) => (id) => {
return state.checkins.filter(c => c.service_id === id) return state.checkins.filter(c => c.service_id === id);
}, },
serviceByAll: (state) => (element) => { serviceByAll: (state) => (element) => {
if (!isNaN(parseFloat(element)) && isFinite(element)) { if (!isNaN(parseFloat(element)) && isFinite(element)) {
return state.services.find(s => s.id === parseInt(element)) return state.services.find(s => s.id === parseInt(element));
} else { } else {
return state.services.find(s => s.permalink === element) return state.services.find(s => s.permalink === element);
} }
}, },
serviceById: (state) => (id) => { serviceById: (state) => (id) => {
return state.services.find(s => s.permalink === id || s.id === id) return state.services.find(s => s.permalink === id || s.id === id);
}, },
servicesInGroup: (state) => (id) => { servicesInGroup: (state) => (id) => {
return state.services.filter(s => s.group_id === id).sort((a, b) => a.order_id - b.order_id) return state.services.filter(s => s.group_id === id).sort((a, b) => a.order_id - b.order_id);
}, },
serviceMessages: (state) => (id) => { serviceMessages: (state) => (id) => {
return state.messages.filter(s => s.service === id) return state.messages.filter(s => s.service === id);
}, },
onlineServices: (state) => (online) => { onlineServices: (state) => (online) => {
return state.services.filter(s => s.online === online) return state.services.filter(s => s.online === online);
}, },
groupById: (state) => (id) => { groupById: (state) => (id) => {
return state.groups.find(g => g.id === id) return state.groups.find(g => g.id === id);
}, },
cleanGroups: (state) => () => { cleanGroups: (state) => () => {
return state.groups.filter(g => g.name !== '').sort((a, b) => a.order_id - b.order_id) return state.groups.filter(g => g.name !== '').sort((a, b) => a.order_id - b.order_id);
}, },
userById: (state) => (id) => { userById: (state) => (id) => {
return state.users.find(u => u.id === id) return state.users.find(u => u.id === id);
}, },
messageById: (state) => (id) => { messageById: (state) => (id) => {
return state.messages.find(m => m.id === id) return state.messages.find(m => m.id === id);
}, },
}, },
mutations: { mutations: {
setHasAllData (state, bool) { setHasAllData (state, bool) {
state.hasAllData = bool state.hasAllData = bool;
}, },
setHasPublicData (state, bool) { setHasPublicData (state, bool) {
state.hasPublicData = bool state.hasPublicData = bool;
}, },
setCore (state, core) { setCore (state, core) {
state.core = core state.core = core;
}, },
setToken (state, token) { setToken (state, token) {
state.token = token state.token = token;
}, },
setService (state, service) { setService (state, service) {
state.service = service state.service = service;
}, },
setServices (state, services) { setServices (state, services) {
state.services = services state.services = services;
}, },
setCheckins (state, checkins) { setCheckins (state, checkins) {
state.checkins = checkins state.checkins = checkins;
}, },
setGroups (state, groups) { setGroups (state, groups) {
state.groups = groups state.groups = groups;
}, },
setMessages (state, messages) { setMessages (state, messages) {
state.messages = messages state.messages = messages;
}, },
setUsers (state, users) { setUsers (state, users) {
state.users = users state.users = users;
}, },
setNotifiers (state, notifiers) { setNotifiers (state, notifiers) {
state.notifiers = notifiers state.notifiers = notifiers;
}, },
setAdmin (state, admin) { setAdmin (state, admin) {
state.admin = admin state.admin = admin;
}, },
setLoggedIn (state, loggedIn) { setLoggedIn (state, loggedIn) {
state.loggedIn = loggedIn state.loggedIn = loggedIn;
}, },
setUser (state, user) { setUser (state, user) {
state.user = user state.user = user;
}, },
setOAuth (state, oauth) { setOAuth (state, oauth) {
state.oauth = oauth state.oauth = oauth;
}, },
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.getDowntimes(payload);
context.commit('setDowntimes', output);
}, },
async loadCore (context) { async loadCore (context) {
const core = await Api.core() const [ core, token ] = await Promise.all([ Api.core(), Api.token() ]);
const token = await Api.token() context.commit('setCore', core);
context.commit("setCore", core); context.commit('setAdmin', token);
context.commit('setAdmin', token) context.commit('setCore', core);
context.commit('setCore', core) context.commit('setUser', token !== undefined);
context.commit('setUser', token !== undefined)
}, },
async loadRequired (context) { async loadRequired (context) {
const groups = await Api.groups() const [ groups, services, messages, oauth ] = await Promise.all([
context.commit("setGroups", groups); Api.groups(),
const services = await Api.services() Api.services(),
context.commit("setServices", services); Api.messages(),
const messages = await Api.messages() Api.oauth()
context.commit("setMessages", messages) ]);
const oauth = await Api.oauth() context.commit('setGroups', groups);
context.commit("setOAuth", oauth); context.commit('setServices', services);
context.commit("setHasPublicData", true) context.commit('setMessages', messages);
context.commit('setOAuth', oauth);
context.commit('setHasPublicData', true);
}, },
async loadAdmin (context) { async loadAdmin (context) {
const groups = await Api.groups() const [ groups, services, messages, checkins, notifiers, users, oauth ] = await Promise.all([
context.commit("setGroups", groups); Api.groups(),
const services = await Api.services() Api.services(),
context.commit("setServices", services); Api.messages(),
const messages = await Api.messages() Api.checkins(),
context.commit("setMessages", messages) Api.notifiers(),
context.commit("setHasPublicData", true) Api.users(),
const checkins = await Api.checkins() Api.oauth(),
context.commit("setCheckins", checkins); ]);
const notifiers = await Api.notifiers()
context.commit("setNotifiers", notifiers); context.commit('setGroups', groups);
const users = await Api.users() context.commit('setServices', services);
context.commit("setUsers", users); context.commit('setMessages', messages);
const oauth = await Api.oauth() context.commit('setHasPublicData', true);
context.commit("setOAuth", oauth); context.commit('setCheckins', checkins);
context.commit('setNotifiers', notifiers);
context.commit('setUsers', users);
context.commit('setOAuth', oauth);
} }
} }
}); });

View File

@ -1,29 +1,31 @@
module.exports = { module.exports = {
baseUrl: '/',
assetsDir: 'assets', assetsDir: 'assets',
filenameHashing: false, filenameHashing: false,
productionTip: process.env.NODE_ENV !== 'production',
devtools: process.env.NODE_ENV !== 'production',
performance: process.env.NODE_ENV !== 'production',
devServer: { devServer: {
disableHostCheck: true, proxy: 'https://statping.concierge.stage.razorpay.in',
proxyTable: {
'/api': {
logLevel: 'debug',
target: 'http://0.0.0.0:8585',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}, },
'/oauth': { // productionTip: process.env.NODE_ENV !== 'production',
logLevel: 'debug', // devtools: process.env.NODE_ENV !== 'production',
target: 'http://0.0.0.0:8585', // performance: process.env.NODE_ENV !== 'production',
changeOrigin: true, // devServer: {
pathRewrite: { // disableHostCheck: true,
'^/oauth': '' // proxyTable: {
} // '/api': {
} // logLevel: 'debug',
} // target: 'http://0.0.0.0:8585',
} // changeOrigin: true,
// pathRewrite: {
// '^/api': ''
// }
// },
// '/oauth': {
// logLevel: 'debug',
// target: 'http://0.0.0.0:8585',
// changeOrigin: true,
// pathRewrite: {
// '^/oauth': ''
// }
// }
// }
// }
}; };

File diff suppressed because it is too large Load Diff