frontend UI changes, removed sass

pull/792/head
hunterlong 2020-08-21 06:14:30 -07:00
parent d17a3e2384
commit 5a2afd9672
25 changed files with 215 additions and 179 deletions

View File

@ -9,6 +9,8 @@
- Modified Service view page, updated Latency and Ping charts, added failures below
- Modified Service chart on index page to show ping data along with latency
- Added AWS SNS Notifier
- Modified dashboard services UI
- Removed sass dependency, now generates css from scss directly from go
# 0.90.63 (08-17-2020)
- Modified build process to use xgo for all arch builds

View File

@ -5,14 +5,12 @@ FROM alpine:latest
RUN apk --no-cache add libgcc libstdc++ ca-certificates curl jq && update-ca-certificates
COPY --from=base /go/bin/statping /usr/local/bin/
COPY --from=base /root/sassc/bin/sassc /usr/local/bin/
COPY --from=base /usr/local/share/ca-certificates /usr/local/share/
WORKDIR /app
VOLUME /app
ENV IS_DOCKER=true
ENV SASS=/usr/local/bin/sassc
ENV STATPING_DIR=/app
ENV PORT=8080

View File

@ -20,11 +20,6 @@ RUN apk add --update --no-cache libstdc++ gcc g++ make git autoconf \
libtool ca-certificates linux-headers wget curl jq && \
update-ca-certificates
WORKDIR /root
RUN git clone https://github.com/sass/sassc.git
RUN . sassc/script/bootstrap && make -C sassc -j4
# sassc binary: /root/sassc/bin/sassc
WORKDIR /go/src/github.com/statping/statping
ADD go.mod go.sum ./
RUN go mod download
@ -41,5 +36,4 @@ RUN make clean generate embed
RUN go build -a -ldflags "-s -w -extldflags -static -X main.VERSION=${VERSION} -X main.COMMIT=${COMMIT}" -o statping --tags "netgo linux" ./cmd
RUN chmod a+x statping && mv statping /go/bin/statping
# /go/bin/statping - statping binary
# /root/sassc/bin/sassc - sass binary
# /statping - Vue frontend (from frontend)

View File

@ -8,7 +8,7 @@ const tokenKey = "statping_auth";
class Api {
constructor() {
this.version = "0.90.64";
this.commit = "2e5fcabc96739d64903d27b3dc1884450056736f";
this.commit = "d17a3e2384c18e41cd81109f3ac7b42c18481a6d";
}
async oauth() {

View File

@ -83,13 +83,13 @@
}
.chartmarker {
padding: 5px;
width: 240px;
padding: 0px;
width: 200px;
text-align: right;
}
.chartmarker SPAN {
font-size: 9pt;
font-size: 4pt;
display: block;
color: #8b8b8b;
}
@ -176,10 +176,6 @@
padding: 5px 7px;
}
.service_li {
min-height: 115px !important;
}
.btn-sm {
line-height: 1.3;
font-size: 0.75rem;
@ -311,10 +307,6 @@
color: #a0a0a0;
}
.service_block {
min-height: 340px;
}
.json-field {
font-size: 10pt;
}

View File

@ -92,13 +92,34 @@ A:HOVER {
color: #fff;
}
.dashboard_card {
background-color: $group-list-background;
box-shadow: rgba(0,0,0,.05) 0px 2px 3px 1px;
}
.dashboard_card:HOVER {
background-color: lighten($group-list-background, 2%) !important;
box-shadow: rgba(0,0,0,.05) 0px 1px 5px 3px;
-webkit-transition-duration: 300ms;
-moz-transition-duration: 300ms;
-o-transition-duration: 300ms;
transition-duration: 300ms;
}
.list-group-item {
min-height: 85pt;
background-color: $group-list-background;
}
.list-group-item:HOVER {
background-color: lighten($group-list-background, 2%) !important;
background-color: lighten($group-list-background, 5%) !important;
box-shadow: #00000014 0px 4px 4px 2px;
-webkit-transition-duration: 300ms;
-moz-transition-duration: 300ms;
-o-transition-duration: 300ms;
transition-duration: 300ms;
margin-top: -1px;
margin-bottom: 1px;
}
.list-group-item A {
@ -118,18 +139,33 @@ A:HOVER {
background-color: $container-color;
}
.login_container {
border-radius: 5px;
box-shadow: 0 .5rem 1rem rgba(0, 0, 0, 0.15) !important;
background-color: $container-color;
}
.footer {
text-decoration: none;
margin-top: 20px;
}
.footer A {
.footer .links {
color: $footer-text-color;
text-decoration: none;
}
.footer A:HOVER {
color: #6d6d6d;
.footer .links:HOVER {
color: #8c8c8c;
}
.footer .statping {
color: lighten($footer-text-color, 10%);
text-decoration: none;
}
.footer .statping:HOVER {
color: lighten($footer-text-color, 0%);
text-decoration: none;
}
.no-select {

View File

@ -40,10 +40,6 @@
font-size: 0.9rem;
}
.service_li {
border: 1px solid #f3f3f3 !important;
}
.container {
padding: 0px !important;
padding-top: 0vh !important;

View File

@ -8,7 +8,7 @@ $description-color: #828282;
$subtitle-color: #747474;
$mobile-card-shadow: 2px 3px 10px #b7b7b7;
$group-list-background: #fafafa;
$group-list-background: #fcfcfc;
$group-list-title: #474747;
$navbar-color: #1c1c1c;
$navbar-background: #ffffff;
@ -35,7 +35,7 @@ $danger-color: #dd3545;
$primary-color: #3e9bff;
/* Footer Settings */
$footer-text-color: #8d8d8d;
$footer-text-color: #b0b0b0;
$nav-tab-color: #13a00d;
$footer-display: block;

View File

@ -30,13 +30,12 @@
<span class="d-block small text-muted mt-3">
Starts at <strong>{{niceDate(message.start_on)}}</strong> till <strong>{{niceDate(message.end_on)}}</strong>
({{dur(parseISO(message.start_on), parseISO(message.end_on))}})
</span>
</span>
</div>
<div v-for="(service, index) in services" class="service_block" v-bind:key="index">
<ServiceInfo :service=service />
</div>
<div class="row">
<ServiceInfo v-for="(service, index) in services" v-bind:key="index" :service="service" />
</div>
</div>
</template>

View File

@ -1,28 +1,31 @@
<template>
<div>
<Loading :loading="!loaded"/>
<div v-if="loaded && !service.online" class="bg-white shadow-sm mt-3 p-3 pr-4 pl-4 col-12">
<font-awesome-icon icon="exclamation" class="mr-3" size="1x"/>
Last failure was {{ago(service.last_error)}} ago.
<code v-if="failure" class="d-block bg-light p-3 mt-3">
{{failure.issue}}
<span class="d-block text-dim float-right small mt-3 mb-1">Failure #{{failure.id}}</span>
</code>
</div>
<div v-if="loaded" v-for="message in $store.getters.serviceMessages(service.id)" class="bg-light shadow-sm p-3 pr-4 pl-4 col-12 mt-3">
<font-awesome-icon icon="calendar" class="mr-3" size="1x"/> {{message.description}}
<span class="d-block small text-muted mt-3">
Starts at <strong>{{niceDate(message.start_on)}}</strong> till <strong>{{niceDate(message.end_on)}}</strong>
({{dur(parseISO(message.start_on), parseISO(message.end_on))}})
<div v-if="loaded && isBefore(nowSubtract(86400), service.last_error)" class="text-danger font-2 p-0 m-0 mb-2">
<font-awesome-icon icon="exclamation" class="mr-1 text-danger" size="1x"/> Recent Failure<br>
<span class="font-italic d-inline-block text-dim" style="max-width: 270px">
Last failure was {{ago(service.last_error)}} ago. {{service.failures[0].issue}}
</span>
</div>
<div v-if="loaded" v-for="incident in incidents" class="bg-light shadow-sm p-3 pr-4 pl-4 col-12 mt-3">
<font-awesome-icon icon="calendar" class="mr-3" size="1x"/>
{{incident.title}} - {{incident.description}}
<div v-for="update in incident.updates" class="d-block small">
<span class="font-weight-bold text-capitalize">{{update.type}}</span> - {{update.message}}
</div>
<div v-if="loaded" v-for="message in messages" class="font-2 p-0 m-0 mb-2">
<font-awesome-icon icon="calendar" class="mr-1" size="1x"/> Upcoming Announcement<br>
<span class="font-italic font-weight-light">{{message.description}}</span>
<span class="font-0 text-dim float-right font-weight-light">@ <strong>{{niceDate(message.start_on)}}</strong>
</span>
</div>
<div v-if="loaded" v-for="incident in incidents" class="font-2 p-0 m-0 mb-2">
<font-awesome-icon icon="bullhorn" class="mr-1" size="1x"/>Recent Incident<br>
<span class="font-italic d-inline-block text-truncate" style="max-width: 270px">{{incident.title}} - {{incident.description}}</span>
<span class="font-0 text-dim float-right font-weight-light">@ <strong>{{niceDate(incident.created_at)}}</strong></span>
</div>
<div v-if="success_event" class="font-2 p-0 m-0 mt-1 mb-3">
<span class="text-success"><font-awesome-icon icon="check" class="mr-1" size="1x"/>No New Events</span>
<span class="font-italic d-inline-block text-truncate text-dim" style="max-width: 270px">Last failure was {{ago(service.last_error)}} ago.</span>
</div>
</div>
</template>
@ -34,7 +37,6 @@ export default {
name: "ServiceEvents",
components: {
Loading
},
props: {
service: {
@ -55,13 +57,19 @@ name: "ServiceEvents",
computed: {
messages() {
return this.$store.getters.serviceMessages(this.service.id)
},
success_event() {
if (this.service.online && this.service.messages.length === 0 && this.service.incidents.length === 0) {
return true
}
return false
}
},
methods: {
async load() {
this.loaded = false
if (!this.service.online) {
await this.getFailure()
this.failure = this.service.failures[0]
}
await this.getMessages()
await this.getIncidents()
@ -70,10 +78,6 @@ name: "ServiceEvents",
async getMessages() {
// this.messages = this.$store.getters.serviceMessages(this.service.id)
},
async getFailure() {
const f = await Api.service_failures(this.service.id, null, null, 1)
this.failure = f[0]
},
async getIncidents() {
this.incidents = await Api.incidents_service(this.service.id)
},

View File

@ -1,64 +1,59 @@
<template>
<div class="card mb-4" :class="{'offline-card': !service.online}">
<div class="col-4">
<div class="dashboard_card card mb-4" :class="{'offline-card': !service.online}">
<div class="card-header pb-1">
<h4 v-observe-visibility="setVisible">
<h6 v-observe-visibility="setVisible">
<router-link :to="serviceLink(service)">{{service.name}}</router-link>
<span class="badge float-right text-uppercase" :class="{'badge-success': service.online, 'badge-danger': !service.online}">
{{service.online ? $t('online') : $t('offline')}}
</span>
</h4>
</h6>
</div>
<div class="card-body">
<transition name="fade">
<div v-if="loaded" class="row pl-2 pr-2">
<div class="col-md-6 col-sm-12 mt-2 mt-md-0 mb-3">
<ServiceSparkLine :title="set2_name" subtitle="Latency Last 24 Hours" :series="set2"/>
</div>
<div class="col-md-6 col-sm-12 mt-4 mt-md-0 mb-3">
<ServiceSparkLine :title="set1_name" subtitle="Latency Last 7 Days" :series="set1"/>
</div>
<div class="col-12 mt-2 mt-md-0 mb-3">
<ServiceEvents :service="service"/>
<div class="col-md-6 col-sm-12 mt-2 mt-md-0 mb-3 pl-0 pr-0">
<ServiceSparkLine :title="set2_name" subtitle="Latency Last 24 Hours" :series="set2"/>
</div>
<div class="col-md-6 col-sm-12 mt-4 mt-md-0 mb-3">
<ServiceSparkLine :title="set1_name" subtitle="Latency Last 7 Days" :series="set1"/>
</div>
<ServiceEvents :service="service"/>
</div>
<div v-else class="row mt-5 mb-5 pt-5 pb-5">
<div class="col-6 text-center text-muted">
<font-awesome-icon icon="circle-notch" size="3x" spin/>
<div v-else class="row mb-5 pt-5 pb-5">
<div class="col-6 text-center">
<font-awesome-icon icon="circle-notch" class="text-dim" size="3x" spin/>
</div>
<div class="col-6 text-center text-muted">
<font-awesome-icon icon="circle-notch" size="3x" spin/>
<div class="col-6 text-center text-dim">
<font-awesome-icon icon="circle-notch" class="text-dim" size="3x" spin/>
</div>
</div>
</transition>
</div>
<div class="card-footer">
<div class="row">
<div class="col-12 col-md-3 mb-2 mb-md-0">
<router-link :to="{path: `/dashboard/service/${service.id}/incidents`, params: {id: service.id} }" class="btn btn-block btn-white text-capitalize incident">
{{$tc('incident', 2)}}
</router-link>
</div>
<div class="col-12 col-md-3 mb-2 mb-md-0">
<router-link :to="{path: `/dashboard/service/${service.id}/checkins`, params: {id: service.id} }" class="btn btn-block btn-white text-capitalize checkins">
{{$tc('checkin', 2)}}
</router-link>
</div>
<div class="col-12 col-md-3 mb-2 mb-md-0">
<router-link :to="{path: `/dashboard/service/${service.id}/failures`, params: {id: service.id} }" class="btn btn-block btn-white text-capitalize failures">
{{$tc('failure', 2)}} <span class="badge badge-danger float-right mt-1">{{service.stats.failures}}</span>
</router-link>
</div>
<div class="col-12 col-md-3 mb-2 mb-md-0 mt-0 mt-md-1">
<span class="float-md-right">
{{$t('uptime', [service.online_7_days])}}
</span>
</div>
<div class="row">
<div class="col-5 pr-0">
<span class="small text-dim"> {{ hoverbtn }}</span>
</div>
<div class="col-7 pr-2 pl-0">
<div class="btn-group float-right">
<button @click="$router.push({path: `/dashboard/service/${service.id}/incidents`, params: {id: service.id}})" @mouseleave="unsetHover" @mouseover="setHover('Incidents')" class="btn btn-sm btn-white incident">
<font-awesome-icon icon="bullhorn"/>
</button>
<button @click="$router.push({path: `/dashboard/service/${service.id}/checkins`, params: {id: service.id}})" @mouseleave="unsetHover" @mouseover="setHover('Checkins')" class="btn btn-sm btn-white checkins">
<font-awesome-icon icon="calendar-check"/>
</button>
<button @click="$router.push({path: `/dashboard/service/${service.id}/failures`, params: {id: service.id}})" @mouseleave="unsetHover" @mouseover="setHover('Failures')" class="btn btn-sm btn-white failures">
<font-awesome-icon icon="exclamation-triangle"/> <span v-if="service.stats.failures !== 0" class="badge badge-danger ml-1">{{service.stats.failures}}</span>
</button>
</div>
</div>
</div>
</div>
<span v-for="(failure, index) in failures" v-bind:key="index" class="alert alert-light">
@ -67,6 +62,7 @@
</span>
</div>
</div>
</template>
<script>
@ -96,6 +92,8 @@
data() {
return {
uptime: null,
hovered: false,
hoverbtn: "",
openTab: "",
set1: [],
set2: [],
@ -109,7 +107,16 @@
watch: {
},
methods: {
mounted() {
this.unsetHover()
},
methods: {
setHover(name) {
this.hoverbtn = name
},
unsetHover() {
this.hoverbtn = this.$t('uptime', [this.service.online_7_days])
},
async setVisible(isVisible, entry) {
if (isVisible && !this.visible) {
await this.loadInfo()

View File

@ -1,5 +1,5 @@
<template v-if="series.length">
<apexchart width="100%" height="180" type="bar" :options="chartOpts" :series="series"></apexchart>
<apexchart width="100%" height="100" type="bar" :options="chartOpts" :series="series"></apexchart>
</template>
<script>
@ -37,6 +37,9 @@
enabled: true
},
},
showPoint: false,
fullWidth:true,
chartPadding: {top: 0,right: 0,bottom: 0,left: 0},
stroke: {
curve: 'straight'
},
@ -50,12 +53,11 @@
tooltip: {
theme: false,
enabled: true,
custom: function({series, seriesIndex, dataPointIndex, w}) {
custom: ({series, seriesIndex, dataPointIndex, w}) => {
let ts = w.globals.seriesX[seriesIndex][dataPointIndex];
const dt = new Date(ts).toLocaleDateString("en-us", timeoptions)
let val = series[seriesIndex][dataPointIndex];
val = val + " ms"
return `<div class="chartmarker"><span>Average Response Time: </span><span class="font-3">${val}</span><span>${dt}</span></div>`
return `<div class="chartmarker"><span>Average Response Time: </span><span class="font-3">${this.humanTime(val)}</span><span>${dt}</span></div>`
},
fixed: {
enabled: true,
@ -74,15 +76,16 @@
text: this.title,
offsetX: 0,
style: {
fontSize: '28px',
fontSize: '18px',
cssClass: 'apexcharts-yaxis-title'
}
},
subtitle: {
text: this.subtitle,
offsetX: 0,
offsetY: 20,
style: {
fontSize: '14px',
fontSize: '9px',
cssClass: 'apexcharts-yaxis-title'
}
}

View File

@ -3,11 +3,11 @@
<div v-if="!core.footer" class="footer text-center mb-4 p-2">
<div class="d-block text-dim">
<div class="mb-3">
<router-link :to="admin ? '/dashboard' : '/login'">{{$t('top_nav.dashboard')}}</router-link>
<router-link class="links" :to="admin ? '/dashboard' : '/login'">{{$t('top_nav.dashboard')}}</router-link>
</div>
<span class="text-muted font-1 mt-3">
<a href="https://github.com/statping/statping" target="_blank">Statping v{{core.version}}
made with <font-awesome-icon icon="heart" class="font-1 text-danger"/></a>
<span class="font-1 mt-3">
<a href="https://github.com/statping/statping" class="statping" target="_blank">
Statping v{{core.version}} made with <font-awesome-icon icon="heart" class="hlight font-1"/></a>
</span>
</div>
</div>
@ -37,6 +37,8 @@
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.hlight {
color: #f6cbcb;
}
</style>

View File

@ -3,7 +3,7 @@
<h4 v-if="group.name !== 'Empty Group'" class="group_header mb-3 mt-4">{{group.name}}</h4>
<div class="list-group online_list mb-4">
<div v-for="(service, index) in services" v-bind:key="index" class="service_li list-group-item list-group-item-action">
<div v-for="(service, index) in services" v-bind:key="index" class="list-group-item list-group-item-action">
<router-link class="no-decoration font-3" :to="serviceLink(service)">{{service.name}}</router-link>
<span class="badge text-uppercase float-right" :class="{'bg-success': service.online, 'bg-danger': !service.online }">
{{service.online ? $t('online') : $t('offline')}}

View File

@ -2,15 +2,15 @@
<div>
<form @submit.prevent="login" autocomplete="on">
<div class="form-group row">
<label for="username" class="col-sm-2 col-form-label">{{$t('username')}}</label>
<div class="col-sm-10">
<input @keyup="checkForm" type="text" v-model="username" autocomplete="username" name="username" class="form-control" id="username" placeholder="Username" autocorrect="off" autocapitalize="none">
<label for="username" class="col-4 col-md-3 col-form-label">{{$t('username')}}</label>
<div class="col-8 col-md-9">
<input @keyup="checkForm" type="text" v-model="username" autocomplete="username" name="username" class="form-control" id="username" placeholder="admin" autocorrect="off" autocapitalize="none">
</div>
</div>
<div class="form-group row">
<label for="password" class="col-sm-2 col-form-label">{{$t('password')}}</label>
<div class="col-sm-10">
<input @keyup="checkForm" type="password" v-model="password" autocomplete="current-password" name="password" class="form-control" id="password" placeholder="Password">
<label for="password" class="col-4 col-md-3 col-form-label">{{$t('password')}}</label>
<div class="col-8 col-md-9">
<input @keyup="checkForm" type="password" v-model="password" autocomplete="current-password" name="password" class="form-control" id="password" placeholder="password123">
</div>
</div>
<div class="form-group row">
@ -18,14 +18,14 @@
<div v-if="error" class="alert alert-danger" role="alert">
{{$t('dashboard.wrong_login')}}
</div>
<button @click.prevent="login" type="submit" class="btn btn-block mb-3 btn-primary" :disabled="disabled || loading">
<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('dashboard.loading') : $t('dashboard.sign_in')}}
</button>
</div>
</div>
</form>
<a v-if="oauth && oauth.gh_client_id" @click.prevent="GHlogin" href="#" class="btn btn-block btn-outline-dark">
<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>

View File

@ -3,13 +3,13 @@
<div class="card contain-card mb-4">
<div class="card-header">{{ $t('service.info') }}</div>
<div class="card-body">
<div class="form-group row">
<label class="col-sm-4 col-form-label">{{ $t('service.name') }}</label>
<div class="col-sm-8">
<input v-model="service.name" @input="updatePermalink" id="name" type="text" name="name" class="form-control" placeholder="Server Name" required spellcheck="false" autocorrect="off">
<small class="form-text text-muted">Give your service a name you can recognize</small>
<div class="form-group row">
<label class="col-sm-4 col-form-label">{{ $t('service.name') }}</label>
<div class="col-sm-8">
<input v-model="service.name" @input="updatePermalink" id="name" type="text" name="name" class="form-control" placeholder="Server Name" required spellcheck="false" autocorrect="off">
<small class="form-text text-muted">Give your service a name you can recognize</small>
</div>
</div>
</div>
<div class="form-group row">
<label for="service_type" class="col-sm-4 col-form-label">{{ $t('service.type') }}</label>
<div class="col-sm-8">
@ -61,7 +61,7 @@
<small id="interval" class="form-text text-muted">Interval to check your service state</small>
</div>
<div class="col-sm-2">
<input v-model="service.check_interval" type="text" name="check_interval" class="form-control">
<input v-model="service.check_interval" type="number" name="check_interval" class="form-control">
</div>
</div>
@ -73,9 +73,11 @@
<div class="card-body">
<div class="form-group row">
<label for="service_url" class="col-sm-4 col-form-label">Service Endpoint {{service.type === 'http' ? "(URL)" : "(Domain)"}}</label>
<label for="service_url" class="col-sm-4 col-form-label">
Service Endpoint {{service.type === 'http' ? "(URL)" : "(Domain)"}}
</label>
<div class="col-sm-8">
<input v-model="service.domain" type="text" class="form-control" id="service_url" :placeholder="service.type === 'http' ? 'https://google.com' : '192.168.1.1'" required autocapitalize="none" spellcheck="false">
<input v-model="service.domain" type="url" class="form-control" id="service_url" :placeholder="service.type === 'http' ? 'https://google.com' : '192.168.1.1'" required autocapitalize="none" spellcheck="false">
<small class="form-text text-muted">Statping will attempt to connect to this address</small>
</div>
</div>
@ -110,7 +112,7 @@
</div>
<div class="col-sm-2">
<input v-model="service.timeout" type="text" name="service_timeout" class="form-control">
<input v-model="service.timeout" type="number" name="service_timeout" class="form-control">
</div>
</div>

View File

@ -1,10 +1,11 @@
import Vue from "vue";
const { startOfToday, startOfMonth, lastDayOfMonth, subSeconds, getUnixTime, fromUnixTime, differenceInSeconds, formatDistance, addMonths, addSeconds, isWithinInterval } = require('date-fns')
const { endOfTomorrow, endOfToday, endOfDay, startOfToday, 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: {
@ -53,6 +54,20 @@ export default Vue.mixin({
parseISO(v) {
return parseISO(v)
},
round(minutes) {
return roundToNearestMinutes(minutes)
},
endOf(method, val) {
switch (val) {
case "day":
return endOfDay(val)
case "today":
return endOfToday()
case "tomorrow":
return endOfTomorrow()
}
return roundToNearestMinutes(val)
},
isZero(val) {
return getUnixTime(parseISO(val)) <= 0
},

View File

@ -2275,7 +2275,7 @@ OluFxewsEO0QNDrfFb+0gnjYlnGqOFcZjUMXbDdY5oLSPtXohynuTK1qyQ==
</div>
<div class="text-center small text-dim" v-pre>
Automatically generated from Statping's Wiki on 2020-08-20 20:45:46.398081 &#43;0000 UTC
Automatically generated from Statping's Wiki on 2020-08-21 04:07:11.080469 &#43;0000 UTC
</div>
</div>

View File

@ -14,7 +14,7 @@
<div class="col-12 full-col-12">
<div v-for="service in services_no_group" v-bind:key="service.id" class="list-group online_list mb-4">
<div class="service_li list-group-item list-group-item-action">
<div class="list-group-item list-group-item-action">
<router-link class="no-decoration font-3" :to="serviceLink(service)">{{service.name}}</router-link>
<span class="badge float-right" :class="{'bg-success': service.online, 'bg-danger': !service.online }">{{service.online ? "ONLINE" : "OFFLINE"}}</span>
<GroupServiceFailures :service="service"/>

View File

@ -1,9 +1,11 @@
<template>
<div class="container col-md-7 col-sm-12 mt-md-5">
<div class="col-10 offset-1 col-md-8 offset-md-2 mt-md-2">
<div class="col-12 col-md-8 offset-md-2 mb-4">
<img alt="Statping Login" class="col-12 mt-5 mt-md-0" style="max-width:650px" src="banner.png">
</div>
<div class="offset-md-3 offset-0 col-md-6 mt-5">
<div class="offset-1 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>

3
go.mod
View File

@ -13,7 +13,6 @@ require (
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/getsentry/sentry-go v0.5.1
github.com/go-mail/mail v2.3.1+incompatible
github.com/gomarkdown/markdown v0.0.0-20200817091206-6efd7696db82
github.com/gorilla/mux v1.7.4
github.com/hako/durafmt v0.0.0-20200605151348-3a43fc422dd9
github.com/jinzhu/gorm v1.9.12
@ -30,7 +29,7 @@ require (
github.com/spf13/viper v1.6.3
github.com/stretchr/testify v1.5.1
github.com/t-tiger/gorm-bulk-insert/v2 v2.0.1
github.com/tdewolff/minify/v2 v2.8.0 // indirect
github.com/wellington/go-libsass v0.9.2
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/net v0.0.0-20200625001655-4c5254603344 // indirect
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d

14
go.sum
View File

@ -114,7 +114,6 @@ github.com/cenkalti/backoff/v4 v4.0.2/go.mod h1:eEew/i+1Q6OrCDZh3WiXYv3+nJwBASZ8
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cheekybits/is v0.0.0-20150225183255-68e9c0620927/go.mod h1:h/aW8ynjgkuj+NQRlZcDbAbM1ORAbXjXX77sX7T289U=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@ -254,8 +253,6 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
github.com/golang/protobuf v1.4.0 h1:oOuy+ugB+P/kBdUnG5QaMXSIyJ1q38wWSojYCb3z5VQ=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomarkdown/markdown v0.0.0-20200817091206-6efd7696db82 h1:2EHQMBglPpettuq2LG+2NRGHhVaRYhxgwPTcgpXVXz8=
github.com/gomarkdown/markdown v0.0.0-20200817091206-6efd7696db82/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@ -400,7 +397,6 @@ github.com/liquidweb/liquidweb-go v1.6.1/go.mod h1:UDcVnAMDkZxpw4Y7NOHkqoeiGacVL
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/matryer/try v0.0.0-20161228173917-9ac251b645a2/go.mod h1:0KeJpeMD6o+O4hW7qJOT7vyQPKrWmj26uf5wMc/IiIs=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
@ -578,13 +574,6 @@ github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/t-tiger/gorm-bulk-insert/v2 v2.0.1 h1:HGVkRrwDCbmSP6h1CoBDj6l/mhnvsP5JbYaQ4ss0R6o=
github.com/t-tiger/gorm-bulk-insert/v2 v2.0.1/go.mod h1:I3xbaE9ud9/TEXzehwkHx86SyJwqeSNsX2X5oV61jIg=
github.com/tdewolff/minify v1.1.0 h1:nxHQi1ML+g3ZbZHffiZ6eC7vMqNvSRfX3KB5Y5y/kfw=
github.com/tdewolff/minify v2.3.6+incompatible h1:2hw5/9ZvxhWLvBUnHE06gElGYz+Jv9R4Eys0XUzItYo=
github.com/tdewolff/minify/v2 v2.8.0 h1:t3tOPWkTpKhsgxm3IM9Sy8hE2eIt30Oaa+2havJGGIE=
github.com/tdewolff/minify/v2 v2.8.0/go.mod h1:6zN8VLhMfFxNrwHROcboYNo2+huPNu4SV8DPh3PUQ8E=
github.com/tdewolff/parse/v2 v2.4.4 h1:uMdbQRtYbKR/msP9CbI7li9wK6pionYiH6s7ipltyGY=
github.com/tdewolff/parse/v2 v2.4.4/go.mod h1:WzaJpRSbwq++EIQHYIRTpbYKNA3gn9it1Ik++q4zyho=
github.com/tdewolff/test v1.0.6/go.mod h1:6DAvZliBAAnD7rhVgwaM7DE5/d9NMOAJ09SqYqeK4QE=
github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7 h1:CpHxIaZzVy26GqJn8ptRyto8fuoYOd1v0fXm9bG3wQ8=
github.com/timewasted/linode v0.0.0-20160829202747-37e84520dcf7/go.mod h1:imsgLplxEC/etjIhdr3dNzV3JeT27LbVu5pYWm0JCBY=
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
@ -608,6 +597,8 @@ github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV
github.com/vultr/govultr v0.1.4/go.mod h1:9H008Uxr/C4vFNGLqKx232C206GL0PBHzOP0809bGNA=
github.com/vultr/govultr v0.3.3 h1:fVaF4h9u3VzTXxFsxvgBUCiM52EiahLqAPkizamLzYM=
github.com/vultr/govultr v0.3.3/go.mod h1:TUuUizMOFc7z+PNMssb6iGjKjQfpw5arIaOLfocVudQ=
github.com/wellington/go-libsass v0.9.2 h1:6Ims04UDdBs6/CGSVK5JC8FNikR5ssrsMMKE/uaO5Q8=
github.com/wellington/go-libsass v0.9.2/go.mod h1:mxgxgam0N0E+NAUMHLcu20Ccfc3mVpDkyrLDayqfiTs=
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
@ -634,7 +625,6 @@ go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZ
go.uber.org/ratelimit v0.1.0 h1:U2AruXqeTb4Eh9sYQSTrMhH8Cb7M0Ian2ibBOnBcnAw=
go.uber.org/ratelimit v0.1.0/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=

View File

@ -22,8 +22,3 @@ func healthCheckHandler(w http.ResponseWriter, r *http.Request) {
}
returnJson(health, w, r)
}
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound)
ExecuteResponse(w, r, "base.gohtml", core.App, nil)
}

View File

@ -9,6 +9,7 @@ import (
"github.com/statping/statping/utils"
"net/http"
"net/http/pprof"
"time"
_ "github.com/statping/statping/types/metrics"
)
@ -38,7 +39,7 @@ func Router() *mux.Router {
}
bPath := utils.Params.GetString("BASE_PATH")
sentryHandler := sentryhttp.New(sentryhttp.Options{})
sentryHandler := sentryhttp.New(sentryhttp.Options{Timeout: 5 * time.Second})
if bPath != "" {
basePath = "/" + bPath + "/"
@ -180,7 +181,7 @@ func Router() *mux.Router {
// API Generic Routes
r.Handle("/metrics", readOnly(promhttp.Handler(), false))
r.Handle("/health", http.HandlerFunc(healthCheckHandler))
r.NotFoundHandler = http.HandlerFunc(notFoundHandler)
r.NotFoundHandler = http.HandlerFunc(indexHandler)
return r
}

View File

@ -4,12 +4,13 @@ package source
//go:generate go run generate_version.go
import (
"bytes"
"fmt"
"github.com/GeertJohan/go.rice"
"github.com/pkg/errors"
"github.com/statping/statping/utils"
"github.com/wellington/go-libsass"
"os"
"os/exec"
"path/filepath"
"strings"
)
@ -58,30 +59,28 @@ func scssRendered(name string) string {
// CompileSASS will attempt to compile the SASS files into CSS
func CompileSASS() error {
sassBin := utils.Params.GetString("SASS")
if sassBin == "" {
bin, err := exec.LookPath("sass")
if err != nil {
log.Warnf("could not find sass executable in PATH: %s", err)
return err
}
sassBin = bin
}
scssFile := filepath.Join(utils.Params.GetString("STATPING_DIR"), "assets", "scss", "index.scss")
log.Infoln(fmt.Sprintf("Compiling SASS %v into %v", scssFile, scssRendered(scssFile)))
indexCSS := filepath.Join(utils.Params.GetString("STATPING_DIR"), "assets", "scss", "index.css")
partials := filepath.Join(utils.Params.GetString("STATPING_DIR"), "assets", "scss")
stdout, stderr, err := utils.Command(sassBin, scssFile, scssRendered(scssFile))
index, err := utils.OpenFile(scssFile)
if err != nil {
log.Errorln(fmt.Sprintf("Failed to compile assets with SASS %v", err))
log.Errorln(fmt.Sprintf("%s %s %s", sassBin, scssFile, scssRendered(scssFile)))
return errors.Wrapf(err, "failed to compile assets, %s %s %s", err, stdout, stderr)
return err
}
if stdout != "" || stderr != "" {
log.Infoln(fmt.Sprintf("out: %v | error: %v", stdout, stderr))
w := bytes.NewBuffer(nil)
comp, err := libsass.New(w, bytes.NewBufferString(index))
if err != nil {
return err
}
if err = comp.Option(libsass.IncludePaths([]string{partials})); err != nil {
return err
}
if err := comp.Run(); err != nil {
return err
}
if err := utils.SaveFile(indexCSS, w.Bytes()); err != nil {
return err
}
log.Infoln("SASS Compiling is complete!")
return nil
}