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

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

@ -1,199 +1,300 @@
<template>
<div>
<div v-if="servicesList.length === 0">
<div class="alert alert-dark d-block mt-3 mb-0">
You currently don't have any services!
</div>
</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('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')}}
<div class="btn-group float-right" role="group">
<a @click="list_timeframe='3h'" type="button" class="small" :class="{'text-success': list_timeframe==='3h', 'text-muted': list_timeframe!=='3h'}">3h</a>
<a @click="list_timeframe='12h'" type="button" class="small" :class="{'text-success': list_timeframe==='12h', 'text-muted': list_timeframe!=='12h'}">12h</a>
<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>
</div>
</th>
<th scope="col"></th>
</tr>
</thead>
<draggable id="services_list" tag="tbody" v-model="servicesList" handle=".drag_icon">
<tr v-for="(service, index) in servicesList" :key="service.id">
<td>
<span v-if="$store.state.admin" class="drag_icon d-none d-md-inline">
<font-awesome-icon icon="bars" class="mr-3"/>
</span> {{service.name}}
</td>
<td class="d-none d-md-table-cell">
<span class="badge text-uppercase" :class="{'badge-success': service.online, 'badge-danger': !service.online}">
{{service.online ? $t('online') : $t('offline')}}
</span>
</td>
<td class="d-none d-md-table-cell">
<span class="badge text-uppercase" :class="{'badge-primary': service.public, 'badge-secondary': !service.public}">
{{service.public ? $t('public') : $t('private')}}
</span>
</td>
<td class="d-none d-md-table-cell">
<div v-if="service.group_id !== 0">
<span class="badge badge-secondary">{{serviceGroup(service)}}</span>
</div>
</td>
<td class="d-none d-md-table-cell">
<ServiceSparkList :service="service" :timeframe="list_timeframe"/>
</td>
<td class="text-right">
<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">
<font-awesome-icon icon="edit" />
</button>
<button :disabled="loading" @click.prevent="goto({path: serviceLink(service), params: {service: service} })" class="btn btn-sm btn-outline-secondary">
<font-awesome-icon icon="chart-area" />
</button>
<button :disabled="loading" v-if="$store.state.admin" @click.prevent="deleteService(service)" class="btn btn-sm btn-danger">
<font-awesome-icon v-if="!loading" icon="times" />
<font-awesome-icon v-if="loading" icon="circle-notch" spin/>
</button>
</div>
</td>
</tr>
</draggable>
</table>
<div>
<div v-if="servicesList.length === 0">
<div class="alert alert-dark d-block mt-3 mb-0">
You currently don't have any services!
</div>
</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('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') }}
<div
class="btn-group float-right"
role="group"
>
<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>
</th>
<th scope="col" />
</tr>
</thead>
<Draggable
id="services_list"
v-model="servicesList"
tag="tbody"
handle=".drag_icon"
>
<tr
v-for="(service, index) in servicesList"
:key="service.id"
>
<td>
<span
v-if="$store.state.admin"
class="drag_icon d-none d-md-inline"
>
<FontAwesomeIcon
icon="bars"
class="mr-3"
/>
</span> {{ service.name }}
</td>
<td class="d-none d-md-table-cell">
<span
class="badge text-uppercase"
:class="{'badge-success': service.online, 'badge-danger': !service.online}"
>
{{ service.online ? $t('online') : $t('offline') }}
</span>
</td>
<td class="d-none d-md-table-cell">
<span
class="badge text-uppercase"
:class="{'badge-primary': service.public, 'badge-secondary': !service.public}"
>
{{ service.public ? $t('public') : $t('private') }}
</span>
</td>
<td class="d-none d-md-table-cell">
<div v-if="service.group_id !== 0">
<span class="badge badge-secondary">
{{ serviceGroup(service) }}
</span>
</div>
</td>
<td class="d-none d-md-table-cell">
<ServiceSparkList
:service="service"
:timeframe="list_timeframe"
/>
</td>
<td class="text-right">
<div class="btn-group">
<button
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
:disabled="loading"
class="btn btn-sm btn-outline-secondary"
@click.prevent="goto({path: serviceLink(service), params: {service: service} })"
>
<FontAwesomeIcon icon="chart-area" />
</button>
<button
v-if="$store.state.admin"
: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>
</div>
</td>
</tr>
</Draggable>
</table>
</div>
</template>
<script>
import Api from "../../API";
import ServiceSparkList from "@/components/Service/ServiceSparkList";
import Modal from "@/components/Elements/Modal";
const draggable = () => import(/* webpackChunkName: "dashboard" */ 'vuedraggable')
import Api from '../../API';
import ServiceSparkList from '@/components/Service/ServiceSparkList';
import Modal from '@/components/Elements/Modal';
const Draggable = () => import(/* webpackChunkName: "dashboard" */ 'vuedraggable');
const ToggleSwitch = () => import(/* webpackChunkName: "dashboard" */ '../../forms/ToggleSwitch');
export default {
name: 'ServicesList',
name: 'ServicesList',
components: {
Modal,
ServiceSparkList,
Modal,
ServiceSparkList,
ToggleSwitch,
draggable
Draggable
},
data() {
data () {
return {
loading: false,
list_timeframe: "12h",
chartOpts: {
chart: {
type: 'bar',
height: 50,
sparkline: {
enabled: true
},
},
xaxis: {
type: 'numeric',
},
showPoint: false,
fullWidth:true,
chartPadding: {top: 0,right: 0,bottom: 0,left: 0},
stroke: {
curve: 'straight'
},
fill: {
opacity: 0.8,
},
yaxis: {
min: 0
},
plotOptions: {
bar: {
colors: {
ranges: [{
from: 0,
to: 1,
color: '#39c10a'
}, {
from: 2,
to: 90,
color: '#e01a1a'
}]
loading: false,
list_timeframe: '12h',
chartOpts: {
chart: {
type: 'bar',
height: 50,
sparkline: {
enabled: true
},
},
},
},
tooltip: {
theme: false,
enabled: false,
},
title: {
enabled: false,
},
subtitle: {
enabled: false,
xaxis: {
type: 'numeric',
},
showPoint: false,
fullWidth:true,
chartPadding: { top: 0,right: 0,bottom: 0,left: 0 },
stroke: {
curve: 'straight'
},
fill: {
opacity: 0.8,
},
yaxis: {
min: 0
},
plotOptions: {
bar: {
colors: {
ranges: [ {
from: 0,
to: 1,
color: '#39c10a'
}, {
from: 2,
to: 90,
color: '#e01a1a'
} ]
},
},
},
tooltip: {
theme: false,
enabled: false,
},
title: {
enabled: false,
},
subtitle: {
enabled: false,
}
}
}
}
},
};
},
computed: {
servicesList: {
get () {
return this.$store.getters.servicesInOrder
return this.$store.getters.servicesInOrder;
},
set (value) {
this.updateOrder(value)
this.updateOrder(value);
}
}
},
methods: {
goto(to) {
this.$router.push(to)
methods: {
goto (to) {
this.$router.push(to);
},
async updateOrder(value) {
let data = [];
value.forEach((s, k) => {
data.push({ service: s.id, order: k + 1 })
});
await Api.services_reorder(data)
await this.update()
},
tester(s) {
console.log(s)
async updateOrder (value) {
const data = [];
value.forEach((s, k) => {
data.push({ service: s.id, order: k + 1 });
});
await Api.services_reorder(data);
await this.update();
},
async delete(s) {
this.loading = true
await Api.service_delete(s.id)
await this.update()
this.loading = false
tester (s) {
console.log(s);
},
async deleteService(s) {
async delete (s) {
this.loading = true;
await Api.service_delete(s.id);
await this.update();
this.loading = false;
},
async deleteService (s) {
const modal = {
visible: true,
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.`,
btnColor: "btn-danger",
btnText: "Delete Service",
func: () => this.delete(s),
visible: true,
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.`,
btnColor: 'btn-danger',
btnText: 'Delete Service',
func: () => this.delete(s),
};
this.$store.commit('setModal', modal);
},
serviceGroup (s) {
const group = this.$store.getters.groupById(s.group_id);
if (group) {
return group.name;
}
this.$store.commit("setModal", modal)
},
serviceGroup(s) {
let group = this.$store.getters.groupById(s.group_id)
if (group) {
return group.name
}
return ""
},
async update() {
const services = await Api.services()
this.$store.commit('setServices', services)
}
}
}
return '';
},
async update () {
const services = await Api.services();
this.$store.commit('setServices', services);
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->

View File

@ -1,67 +1,183 @@
<template>
<nav class="navbar navbar-expand-lg">
<router-link to="/" class="navbar-brand">Statping</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">
<font-awesome-icon v-if="!navopen" icon="bars"/>
<font-awesome-icon v-if="navopen" icon="times"/>
</button>
<div class="navbar-collapse" :class="{collapse: !navopen}" id="navbarText">
<ul class="navbar-nav mr-auto">
<li @click="navopen = !navopen" class="nav-item navbar-item">
<router-link to="/dashboard" class="nav-link">{{ $t('dashboard') }}</router-link>
</li>
<li @click="navopen = !navopen" class="nav-item navbar-item">
<router-link to="/dashboard/services" class="nav-link">{{ $t('services') }}</router-link>
</li>
<li v-if="admin" @click="navopen = !navopen" class="nav-item navbar-item">
<router-link to="/dashboard/users" class="nav-link">{{ $t('users') }}</router-link>
</li>
<li @click="navopen = !navopen" class="nav-item navbar-item">
<router-link to="/dashboard/messages" class="nav-link">{{ $t('announcements') }}</router-link>
</li>
<li v-if="admin" @click="navopen = !navopen" class="nav-item navbar-item">
<router-link to="/dashboard/settings" class="nav-link">{{ $t('settings') }}</router-link>
</li>
<li v-if="admin" @click="navopen = !navopen" class="nav-item navbar-item">
<router-link to="/dashboard/logs" class="nav-link">{{ $t('logs') }}</router-link>
</li>
<li v-if="admin" @click="navopen = !navopen" class="nav-item navbar-item">
<router-link to="/dashboard/help" class="nav-link">{{ $t('help') }}</router-link>
</li>
</ul>
<span class="navbar-text">
<a href="#" class="nav-link" @click.prevent="logout">{{ $t('logout') }}</a>
</span>
</div>
</nav>
<nav class="navbar navbar-expand-lg">
<router-link
to="/"
class="navbar-brand"
>
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>
<div
id="navbarText"
class="navbar-collapse"
:class="{ collapse: !navopen }"
>
<ul class="navbar-nav mr-auto">
<li
class="nav-item navbar-item"
@click="navopen = !navopen"
>
<router-link
to="/dashboard"
class="nav-link"
>
{{ $t('dashboard') }}
</router-link>
</li>
<li
class="nav-item navbar-item"
@click="navopen = !navopen"
>
<router-link
to="/dashboard/services"
class="nav-link"
>
{{
$t('services')
}}
</router-link>
</li>
<li
class="nav-item navbar-item"
@click="navopen = !navopen"
>
<router-link
to="/dashboard/downtimes"
class="nav-link"
>
{{
'Downtimes'
}}
</router-link>
</li>
<li
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
class="nav-item navbar-item"
@click="navopen = !navopen"
>
<router-link
to="/dashboard/messages"
class="nav-link"
>
{{
$t('announcements')
}}
</router-link>
</li>
<li
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
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>
</ul>
<span class="navbar-text">
<a
href="#"
class="nav-link"
@click.prevent="logout"
>
{{
$t('logout')
}}
</a>
</span>
</div>
</nav>
</template>
<script>
import Api from "../../API"
import Api from '../../API';
export default {
name: 'TopNav',
data () {
return {
navopen: false
}
},
computed: {
admin() {
return this.$store.state.admin
}
export default {
name: 'TopNav',
data () {
return {
navopen: false,
};
},
methods: {
computed: {
admin () {
return this.$store.state.admin;
},
},
methods: {
async logout () {
await Api.logout()
this.$store.commit('setHasAllData', false)
this.$store.commit('setToken', null)
this.$store.commit('setAdmin', false)
// this.$cookies.remove("statping_auth")
await this.$router.push('/logout')
}
}
}
await Api.logout();
this.$store.commit('setHasAllData', false);
this.$store.commit('setToken', null);
this.$store.commit('setAdmin', false);
// this.$cookies.remove("statping_auth")
await this.$router.push('/logout');
},
},
};
</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>
<div>
<form @submit.prevent="login" autocomplete="on">
<div class="form-group row">
<label for="username" class="col-4 col-form-label">{{$t('username')}}</label>
<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">
</div>
<div>
<form
autocomplete="on"
@submit.prevent="login"
>
<div class="form-group row">
<label
for="username"
class="col-4 col-form-label"
>
{{ $t('username') }}
</label>
<div class="col-8">
<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 class="form-group row">
<label for="password" class="col-4 col-form-label">{{$t('password')}}</label>
<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="************">
</div>
</div>
<div class="form-group row">
<label
for="password"
class="col-4 col-form-label"
>
{{ $t('password') }}
</label>
<div class="col-8">
<input
id="password"
v-model="password"
type="password"
autocomplete="current-password"
name="password"
class="form-control"
placeholder="************"
@keyup="checkForm"
@change="checkForm"
>
</div>
<div class="form-group row">
<div class="col-sm-12">
<div v-if="error" class="alert alert-danger" role="alert">
{{$t('wrong_login')}}
</div>
<button @click.prevent="login" type="submit" class="btn btn-block btn-primary" :disabled="disabled || loading">
<font-awesome-icon v-if="loading" icon="circle-notch" class="mr-2" spin/>{{loading ? $t('loading') : $t('sign_in')}}
</button>
</div>
</div>
<div class="form-group row">
<div class="col-sm-12">
<div
v-if="error"
class="alert alert-danger"
role="alert"
>
{{ $t('wrong_login') }}
</div>
<button
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>
</div>
</div>
</form>
<a v-if="oauth && oauth.gh_client_id" @click.prevent="GHlogin" href="#" class="mt-4 btn btn-block btn-outline-dark">
<font-awesome-icon :icon="['fab', 'github']" /> Login with Github
</a>
<a
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 v-if="oauth && oauth.slack_client_id" @click.prevent="Slacklogin" href="#" class="btn btn-block btn-outline-dark">
<font-awesome-icon :icon="['fab', 'slack']" /> Login with Slack
</a>
<a
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 v-if="oauth && oauth.google_client_id" @click.prevent="Googlelogin" href="#" class="btn btn-block btn-outline-dark">
<font-awesome-icon :icon="['fab', 'google']" /> Login with Google
</a>
<a
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 v-if="oauth && oauth.custom_client_id" @click.prevent="Customlogin" href="#" class="btn btn-block btn-outline-dark">
<font-awesome-icon :icon="['fas', 'address-card']" /> Login with {{oauth.custom_name}}
</a>
</div>
<a
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>
</div>
</template>
<script>
import Api from "../API";
import Api from '../API';
export default {
name: 'FormLogin',
computed: {
core() {
return this.$store.getters.core
},
oauth() {
return this.$store.getters.oauth
}
},
data() {
return {
username: "",
password: "",
export default {
name: 'FormLogin',
data () {
return {
username: '',
password: '',
auth: {},
loading: false,
error: false,
disabled: true,
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"
}
},
mounted() {
this.$cookies.remove("statping_auth")
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'
};
},
computed: {
core () {
return this.$store.getters.core;
},
oauth () {
return this.$store.getters.oauth;
}
},
mounted () {
this.$cookies.remove('statping_auth');
},
methods: {
checkForm() {
if (!this.username || !this.password) {
this.disabled = true
} else {
this.disabled = false
}
},
async login() {
this.loading = true
this.error = false
const auth = await Api.login(this.username, this.password)
if (auth.error) {
this.error = true
} else if (auth.token) {
this.$cookies.set("statping_auth", auth.token)
await this.$store.dispatch('loadAdmin')
this.$store.commit('setAdmin', auth.admin)
this.$store.commit('setLoggedIn', true)
this.$router.push('/dashboard')
}
this.loading = false
},
encode(val) {
return encodeURI(val)
},
custom_scopes() {
let scopes = []
if (this.oauth.custom_open_id) {
scopes.push("openid")
}
scopes.push(this.oauth.custom_scopes.split(","))
if (scopes.length !== 0) {
return "&scopes="+scopes.join(",")
}
return ""
},
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`
checkForm () {
if (!this.username || !this.password) {
this.disabled = true;
} else {
this.disabled = false;
}
},
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`
async login () {
this.loading = true;
this.error = false;
const auth = await Api.login(this.username, this.password);
if (auth.error) {
this.error = true;
} else if (auth.token) {
this.$cookies.set('statping_auth', auth.token);
await this.$store.dispatch('loadAdmin');
this.$store.commit('setAdmin', auth.admin);
this.$store.commit('setLoggedIn', true);
this.$router.push('/dashboard');
}
this.loading = false;
},
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`
encode (val) {
return encodeURI(val);
},
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()}`
custom_scopes () {
const scopes = [];
if (this.oauth.custom_open_id) {
scopes.push('openid');
}
scopes.push(this.oauth.custom_scopes.split(','));
if (scopes.length !== 0) {
return '&scopes='+scopes.join(',');
}
return '';
},
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`;
},
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`;
},
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`;
},
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()}`;
}
}
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->

View File

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

View File

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

View File

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

View File

@ -1,36 +1,39 @@
<template>
<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">
<img alt="Statping Login" class="embed-responsive" src="banner.png">
</div>
<div class="login_container col-12 p-4">
<FormLogin/>
</div>
<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">
<img
alt="Statping Login"
class="embed-responsive"
src="banner.png"
>
</div>
<div class="login_container col-12 p-4">
<FormLogin />
</div>
</div>
</template>
<script>
const FormLogin = () => import(/* webpackChunkName: "index" */ '@/forms/Login')
const FormLogin = () => import(/* webpackChunkName: "index" */ '@/forms/Login');
export default {
name: 'Login',
components: {
FormLogin
},
data () {
return {
export default {
name: 'Login',
components: {
FormLogin
},
data () {
return {
}
},
mounted() {
};
},
mounted () {
},
methods: {
}
}
}
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->

View File

@ -1,230 +1,238 @@
const Index = () => import(/* webpackChunkName: "index" */ '@/pages/Index')
const Dashboard = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Dashboard')
const DashboardIndex = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardIndex')
const DashboardUsers = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardUsers')
const DashboardServices = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardServices')
const DashboardMessages = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardMessages')
const EditService = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/EditService')
const Logs = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Logs')
const Help = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Help')
const Settings = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Settings')
const Login = () => import(/* webpackChunkName: "index" */ '@/pages/Login')
const Service = () => import(/* webpackChunkName: "index" */ '@/pages/Service')
const Setup = () => import(/* webpackChunkName: "index" */ '@/forms/Setup')
const Incidents = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Incidents')
const Checkins = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Checkins')
const Failures = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Failures')
const NotFound = () => import(/* webpackChunkName: "index" */ '@/pages/NotFound')
const Importer = () => import(/* webpackChunkName: "index" */ '@/components/Dashboard/Importer')
const Index = () => import(/* webpackChunkName: "index" */ '@/pages/Index');
const Dashboard = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Dashboard');
const DashboardIndex = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardIndex');
const DashboardUsers = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardUsers');
const DashboardServices = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardServices');
const DashboardMessages = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/DashboardMessages');
const EditService = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/EditService');
const Logs = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Logs');
const Help = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Help');
const Settings = () => import(/* webpackChunkName: "dashboard" */ '@/pages/Settings');
const Login = () => import(/* webpackChunkName: "index" */ '@/pages/Login');
const Service = () => import(/* webpackChunkName: "index" */ '@/pages/Service');
const Setup = () => import(/* webpackChunkName: "index" */ '@/forms/Setup');
const Incidents = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Incidents');
const Checkins = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Checkins');
const Failures = () => import(/* webpackChunkName: "dashboard" */ '@/components/Dashboard/Failures');
const NotFound = () => import(/* webpackChunkName: "index" */ '@/pages/NotFound');
const Importer = () => import(/* webpackChunkName: "index" */ '@/components/Dashboard/Importer');
const DashboardDowntimes = () => import(/* webpackChunkName: "downtimes" */ '@/components/Dashboard/DashboardDowntimes');
import VueRouter from "vue-router";
import Api from "./API";
import store from "./store"
import VueRouter from 'vue-router';
import Api from './API';
import store from './store';
const Loading = {
template: '<div class="jumbotron">LOADING</div>'
}
template: '<div class="jumbotron">LOADING</div>'
};
const routes = [
{
path: '/setup',
name: 'Setup',
component: Setup,
meta: {
title: 'Statping Setup',
}
},
{
path: '/',
name: 'Index',
component: Index,
},
{
path: '/dashboard',
component: Dashboard,
meta: {
requiresAuth: true,
title: 'Statping - Dashboard',
{
path: '/setup',
name: 'Setup',
component: Setup,
meta: {
title: 'Statping Setup',
}
},
beforeEnter: async (to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (to.path !== '/login') {
if(store.getters.loggedIn) {
next()
return
}
const token = $cookies.get('statping_auth')
if (!token) {
next('/login')
return
}
try {
const jwt = await Api.check_token(token)
store.commit('setAdmin', jwt.admin)
if (jwt.admin) {
store.commit('setLoggedIn', true)
store.commit('setUser', true)
{
path: '/',
name: 'Index',
component: Index,
},
{
path: '/dashboard',
component: Dashboard,
meta: {
requiresAuth: true,
title: 'Statping - Dashboard',
},
beforeEnter: async (to, from, next) => {
if (to.matched.some(record => record.meta.requiresAuth)) {
if (to.path !== '/login') {
if (store.getters.loggedIn) {
next();
return;
}
const token = $cookies.get('statping_auth');
if (!token) {
next('/login');
return;
}
try {
const jwt = await Api.check_token(token);
store.commit('setAdmin', jwt.admin);
if (jwt.admin) {
store.commit('setLoggedIn', true);
store.commit('setUser', true);
} else {
store.commit('setLoggedIn', false);
next('/login');
return;
}
} catch (e) {
console.error(e);
next('/login');
return;
}
}
next();
} else {
store.commit('setLoggedIn', false)
next('/login')
return
next();
}
} catch (e) {
console.error(e)
next('/login')
return
}
}
next()
} else {
next()
}
},
children: [ {
path: '',
component: DashboardIndex,
meta: {
requiresAuth: true,
title: 'Statping - Dashboard',
}
},{
path: 'users',
component: DashboardUsers,
loading: Loading,
meta: {
requiresAuth: true,
title: 'Statping - Users',
}
},{
path: 'services',
component: DashboardServices,
meta: {
requiresAuth: true,
title: 'Statping - Services',
}
},{
path: 'create_service',
component: EditService,
meta: {
requiresAuth: true,
title: 'Statping - Create Service',
}
},{
path: 'edit_service/:id',
component: EditService,
meta: {
requiresAuth: true,
title: 'Statping - Edit Service',
}
},{
path: 'service/:id/incidents',
component: Incidents,
meta: {
requiresAuth: true,
title: 'Statping - Incidents',
}
},{
path: 'service/:id/checkins',
component: Checkins,
meta: {
requiresAuth: true,
title: 'Statping - Checkins',
}
},{
path: 'service/:id/failures',
component: Failures,
meta: {
requiresAuth: true,
title: 'Statping - Service Failures',
}
},{
path: 'downtimes',
component: DashboardDowntimes,
meta: {
requiresAuth: true,
title: 'Statping - Downtimes',
}
},{
path: 'messages',
component: DashboardMessages,
meta: {
requiresAuth: true,
title: 'Statping - Messages',
}
},{
path: 'settings',
component: Settings,
meta: {
requiresAuth: true,
title: 'Statping - Settings',
}
},{
path: 'logs',
component: Logs,
meta: {
requiresAuth: true,
title: 'Statping - Logs',
}
},{
path: 'help',
component: Help,
meta: {
requiresAuth: true,
title: 'Statping - Help',
}
},{
path: 'import',
component: Importer,
meta: {
requiresAuth: true,
title: 'Statping - Import',
}
} ]
},
children: [{
path: '',
component: DashboardIndex,
meta: {
requiresAuth: true,
title: 'Statping - Dashboard',
}
},{
path: 'users',
component: DashboardUsers,
loading: Loading,
{
path: '/login',
name: 'Login',
component: Login,
meta: {
requiresAuth: true,
title: 'Statping - Users',
title: 'Statping - Login',
}
},{
path: 'services',
component: DashboardServices,
meta: {
requiresAuth: true,
title: 'Statping - Services',
}
},{
path: 'create_service',
component: EditService,
meta: {
requiresAuth: true,
title: 'Statping - Create Service',
}
},{
path: 'edit_service/:id',
component: EditService,
meta: {
requiresAuth: true,
title: 'Statping - Edit Service',
}
},{
path: 'service/:id/incidents',
component: Incidents,
meta: {
requiresAuth: true,
title: 'Statping - Incidents',
}
},{
path: 'service/:id/checkins',
component: Checkins,
meta: {
requiresAuth: true,
title: 'Statping - Checkins',
}
},{
path: 'service/:id/failures',
component: Failures,
meta: {
requiresAuth: true,
title: 'Statping - Service Failures',
}
},{
path: 'messages',
component: DashboardMessages,
meta: {
requiresAuth: true,
title: 'Statping - Messages',
}
},{
path: 'settings',
component: Settings,
meta: {
requiresAuth: true,
title: 'Statping - Settings',
}
},{
path: 'logs',
component: Logs,
meta: {
requiresAuth: true,
title: 'Statping - Logs',
}
},{
path: 'help',
component: Help,
meta: {
requiresAuth: true,
title: 'Statping - Help',
}
},{
path: 'import',
component: Importer,
meta: {
requiresAuth: true,
title: 'Statping - Import',
}
}]
},
{
path: '/login',
name: 'Login',
component: Login,
meta: {
title: 'Statping - Login',
},
{ path: '/logout', redirect: '/' },
{
path: '/service/:id',
name: 'Service',
component: Service,
props: true
},
{
path: '*',
component: NotFound,
name: 'NotFound',
}
},
{ path: '/logout', redirect: '/' },
{
path: '/service/:id',
name: 'Service',
component: Service,
props: true
},
{
path: '*',
component: NotFound,
name: 'NotFound',
}
];
const router = new VueRouter({
mode: 'history',
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return { x: 0, y: 0 }
}
if (savedPosition) {
return savedPosition;
} else {
return { x: 0, y: 0 };
}
},
routes
})
router.beforeEach((to, from, next) => {
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 previousNearestWithMeta = from.matched.slice().reverse().find(r => r.meta && r.meta.metaTags);
if (nearestWithTitle) document.title = nearestWithTitle.meta.title;
Array.from(document.querySelectorAll('[data-vue-router-controlled]')).map(el => el.parentNode.removeChild(el));
if (!nearestWithMeta) return next();
nearestWithMeta.meta.metaTags.map(tagDef => {
const tag = document.createElement('meta');
Object.keys(tagDef).forEach(key => {
tag.setAttribute(key, tagDef[key]);
});
tag.setAttribute('data-vue-router-controlled', '');
return tag;
})
.forEach(tag => document.head.appendChild(tag));
next();
});
export default router
router.beforeEach((to, from, next) => {
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 previousNearestWithMeta = from.matched.slice().reverse().find(r => r.meta && r.meta.metaTags);
if (nearestWithTitle) {document.title = nearestWithTitle.meta.title;}
Array.from(document.querySelectorAll('[data-vue-router-controlled]')).map(el => el.parentNode.removeChild(el));
if (!nearestWithMeta) {return next();}
nearestWithMeta.meta.metaTags.map(tagDef => {
const tag = document.createElement('meta');
Object.keys(tagDef).forEach(key => {
tag.setAttribute(key, tagDef[key]);
});
tag.setAttribute('data-vue-router-controlled', '');
return tag;
})
.forEach(tag => document.head.appendChild(tag));
next();
});
export default router;

View File

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

View File

@ -1,29 +1,31 @@
module.exports = {
baseUrl: '/',
assetsDir: 'assets',
filenameHashing: false,
productionTip: process.env.NODE_ENV !== 'production',
devtools: process.env.NODE_ENV !== 'production',
performance: process.env.NODE_ENV !== 'production',
devServer: {
disableHostCheck: true,
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': ''
}
}
}
}
assetsDir: 'assets',
filenameHashing: false,
devServer: {
proxy: 'https://statping.concierge.stage.razorpay.in',
},
// productionTip: process.env.NODE_ENV !== 'production',
// devtools: process.env.NODE_ENV !== 'production',
// performance: process.env.NODE_ENV !== 'production',
// devServer: {
// disableHostCheck: true,
// 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