mirror of https://github.com/statping/statping
vue
parent
b0ea8b3621
commit
bbf8073315
|
@ -131,10 +131,6 @@ func main() {
|
|||
exit(err)
|
||||
}
|
||||
|
||||
if err = confgs.VerifyMigration(); err != nil {
|
||||
exit(err)
|
||||
}
|
||||
|
||||
exists := confgs.Db.HasTable("core")
|
||||
if !exists {
|
||||
var srvs int64
|
||||
|
@ -161,6 +157,10 @@ func main() {
|
|||
|
||||
}
|
||||
|
||||
if err = confgs.DatabaseChanges(); err != nil {
|
||||
exit(err)
|
||||
}
|
||||
|
||||
if err := confgs.MigrateDatabase(); err != nil {
|
||||
exit(err)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package database
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/statping/statping/types"
|
||||
"github.com/statping/statping/utils"
|
||||
|
@ -144,7 +145,7 @@ type isObject interface {
|
|||
Db() Database
|
||||
}
|
||||
|
||||
func ParseQueries(r *http.Request, o isObject) *GroupQuery {
|
||||
func ParseQueries(r *http.Request, o isObject) (*GroupQuery, error) {
|
||||
fields := parseGet(r)
|
||||
grouping := fields.Get("group")
|
||||
startField := utils.ToInt(fields.Get("start"))
|
||||
|
@ -179,11 +180,15 @@ func ParseQueries(r *http.Request, o isObject) *GroupQuery {
|
|||
db: q,
|
||||
}
|
||||
|
||||
if query.Start.After(query.End) {
|
||||
return nil, errors.New("start time is after ending time")
|
||||
}
|
||||
|
||||
if startField == 0 {
|
||||
query.Start = time.Now().Add(-7 * types.Day).UTC()
|
||||
query.Start = utils.Now().Add(-7 * types.Day)
|
||||
}
|
||||
if endField == 0 {
|
||||
query.End = time.Now().UTC()
|
||||
query.End = utils.Now()
|
||||
}
|
||||
if query.End.After(utils.Now()) {
|
||||
query.End = utils.Now()
|
||||
|
@ -203,7 +208,7 @@ func ParseQueries(r *http.Request, o isObject) *GroupQuery {
|
|||
}
|
||||
query.db = q
|
||||
|
||||
return query
|
||||
return query, nil
|
||||
}
|
||||
|
||||
func parseForm(r *http.Request) url.Values {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div id="app">
|
||||
<router-view :loaded="loaded"/>
|
||||
<router-view :app="app" :loaded="loaded"/>
|
||||
<Footer :logged_in="logged_in" :version="version" v-if="$route.path !== '/setup'"/>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -19,10 +19,13 @@
|
|||
loaded: false,
|
||||
version: "",
|
||||
logged_in: false,
|
||||
app: null
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
await this.$store.dispatch('loadRequired')
|
||||
this.app = await this.$store.dispatch('loadRequired')
|
||||
|
||||
this.app = {...this.$store.state}
|
||||
|
||||
if (this.$store.getters.core.logged_in) {
|
||||
await this.$store.dispatch('loadAdmin')
|
||||
|
|
|
@ -6,7 +6,34 @@ HTML,BODY {
|
|||
}
|
||||
|
||||
.index-chart {
|
||||
height: 490px;
|
||||
height: $service-card-height;
|
||||
}
|
||||
|
||||
.sub-service-card {
|
||||
border: 1px solid #dcdcdc87;
|
||||
padding-top: 7px;
|
||||
padding-bottom: 10px;
|
||||
height: 155px;
|
||||
}
|
||||
|
||||
.expanded-service {
|
||||
height: 89vh;
|
||||
/*Animation*/
|
||||
-webkit-transition: height 2s ease;
|
||||
-moz-transition: height 2s ease;
|
||||
-o-transition: height 2s ease;
|
||||
-ms-transition: height 2s ease;
|
||||
transition: height 2s ease;
|
||||
|
||||
.stats_area {
|
||||
display: none;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.service-chart {
|
||||
bottom: -15px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.chartmarker {
|
||||
|
@ -167,7 +194,19 @@ HTML,BODY {
|
|||
}
|
||||
|
||||
.font-5 {
|
||||
font-size: 20pt;
|
||||
font-size: 17pt;
|
||||
}
|
||||
|
||||
.font-6 {
|
||||
font-size: 24pt;
|
||||
}
|
||||
|
||||
.font-7 {
|
||||
font-size: 31pt;
|
||||
}
|
||||
|
||||
.font-8 {
|
||||
font-size: 38pt;
|
||||
}
|
||||
|
||||
.badge {
|
||||
|
@ -225,6 +264,7 @@ HTML,BODY {
|
|||
|
||||
.card-body H4 A {
|
||||
color: $service-title;
|
||||
font-size: $service-title-size;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
@ -248,7 +288,7 @@ HTML,BODY {
|
|||
|
||||
.service-chart-heatmap {
|
||||
position: relative;
|
||||
height: 300px;
|
||||
height: 180px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
}
|
||||
|
||||
.index-chart {
|
||||
height: 33vh;
|
||||
height: 380px;
|
||||
}
|
||||
|
||||
.sm-container {
|
||||
|
|
|
@ -8,9 +8,11 @@ $description-color: #939393;
|
|||
$service-background: #ffffff;
|
||||
$service-border: 1px solid rgba(0,0,0,.125);
|
||||
$service-title: #444444;
|
||||
$service-title-size: 1.8rem;
|
||||
$service-stats-color: #4f4f4f;
|
||||
$service-description-color: #fff;
|
||||
$service-stats-size: 2.3rem;
|
||||
$service-card-height: 490px;
|
||||
|
||||
/* Button Colors */
|
||||
$success-color: #47d337;
|
||||
|
@ -25,7 +27,6 @@ $footer-display: block;
|
|||
/* Global Settings */
|
||||
$global-border-radius: 0.2rem;
|
||||
|
||||
|
||||
/* Mobile Settings */
|
||||
$sm-background-color: #fcfcfc;
|
||||
$sm-border-radius: 0rem;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
<a v-for="(service, index) in $store.getters.servicesInGroup(group.id)" v-bind:key="index" class="service_li list-group-item list-group-item-action">
|
||||
<router-link class="no-decoration" :to="serviceLink(service)">{{service.name}}</router-link>
|
||||
<span class="badge bg-success float-right">{{service.online ? "ONLINE" : "OFFLINE"}}</span>
|
||||
<span class="badge float-right" :class="{'bg-success': service.online, 'bg-danger': !service.online }">{{service.online ? "ONLINE" : "OFFLINE"}}</span>
|
||||
|
||||
<GroupServiceFailures :service="service"/>
|
||||
</a>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div>
|
||||
<h1 class="col-12 text-center mb-4 mt-sm-3 header-title">{{$store.getters.core.name}}</h1>
|
||||
<h1 class="col-12 text-center pt-4 mt-4 header-title">{{$store.getters.core.name}}</h1>
|
||||
<h5 class="col-12 text-center mb-5 header-desc">{{$store.getters.core.description}}</h5>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<div class="alert alert-primary" role="alert">
|
||||
<h3>{{message.title}}</h3>
|
||||
<div class="alert alert-primary pb-4 pt-3 mt-5 mb-5" role="alert">
|
||||
<h3 class="mb-3">{{message.title}}</h3>
|
||||
<span class="mb-3">{{message.description}}</span>
|
||||
<div class="d-block mt-2 mb-4">
|
||||
<span class="float-left small">
|
||||
Started {{toLocal(message.start_on)}} ({{duration(new Date(), message.start_on)}} ago)
|
||||
<div class="row d-block mt-3">
|
||||
<span class="col-12 col-md-6 text-left small">
|
||||
Started {{niceDate(message.start_on)}} ({{ago(parseISO(message.start_on))}} ago)
|
||||
</span>
|
||||
<span class="float-right small">
|
||||
Ends on {{toLocal(message.end_on)}} (in {{duration(message.end_on, new Date())}})</span>
|
||||
<span class="col-12 col-md-6 text-right float-right small">
|
||||
Ends on {{niceDate(message.end_on)}} (in {{ago(parseISO(message.end_on))}})</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
<template>
|
||||
<div class="col-6 mt-4">
|
||||
<div class="col-12 sub-service-card">
|
||||
<div class="col-8 float-left p-0 mt-1 mb-3">
|
||||
<span class="font-5 d-block">{{title}}</span>
|
||||
<span class="text-muted font-3 d-block font-weight-bold">{{subtitle}}</span>
|
||||
</div>
|
||||
<div class="col-4 float-right text-right mt-2 p-0">
|
||||
<span class="text-success font-5 font-weight-bold">{{value}}</span>
|
||||
</div>
|
||||
|
||||
<MiniSparkLine :series="[{name: 'okokokok', data:[{x: '2019-01-01', y: 120},{x: '2019-01-02', y: 160},{x: '2019-01-03', y: 240},{x: '2019-01-04', y: 45}]}]"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../../API";
|
||||
import MiniSparkLine from './MiniSparkLine';
|
||||
import ServiceSparkLine from './ServiceSparkLine';
|
||||
|
||||
export default {
|
||||
name: 'Analytics',
|
||||
components: { MiniSparkLine, ServiceSparkLine },
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
subtitle: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
level: {
|
||||
type: Number,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.latencyYesterday();
|
||||
},
|
||||
async latencyYesterday() {
|
||||
const todayTime = await Api.service_hits(this.service.id, this.toUnix(this.nowSubtract(86400)), this.toUnix(new Date()), this.group, false)
|
||||
const fetched = await Api.service_hits(this.service.id, this.start, this.end, this.group, false)
|
||||
|
||||
let todayAmount = this.addAmounts(todayTime)
|
||||
let yesterday = this.addAmounts(fetched)
|
||||
|
||||
window.console.log(todayAmount)
|
||||
window.console.log(yesterday)
|
||||
|
||||
},
|
||||
addAmounts(data) {
|
||||
let total = 0
|
||||
data.forEach((f) => {
|
||||
total += parseInt(f.amount)
|
||||
});
|
||||
return total
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -0,0 +1,85 @@
|
|||
<template v-if="series.length">
|
||||
<apexchart width="100%" height="70" type="bar" :options="chartOpts" :series="series"></apexchart>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const timeoptions = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' };
|
||||
|
||||
|
||||
export default {
|
||||
name: 'MiniSparkLine',
|
||||
props: {
|
||||
series: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
},
|
||||
subtitle: {
|
||||
type: String,
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
title () {
|
||||
|
||||
},
|
||||
subtitle () {
|
||||
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
chartOpts: {
|
||||
chart: {
|
||||
type: 'bar',
|
||||
height: 180,
|
||||
sparkline: {
|
||||
enabled: true
|
||||
},
|
||||
},
|
||||
stroke: {
|
||||
curve: 'straight'
|
||||
},
|
||||
fill: {
|
||||
opacity: 0.3,
|
||||
},
|
||||
yaxis: {
|
||||
min: 0
|
||||
},
|
||||
colors: ['#b3bdc3'],
|
||||
tooltip: {
|
||||
theme: false,
|
||||
enabled: false,
|
||||
x: {
|
||||
show: false,
|
||||
},
|
||||
y: {
|
||||
formatter: (value) => { return value + "%" },
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: this.title,
|
||||
offsetX: 0,
|
||||
style: {
|
||||
fontSize: '28px',
|
||||
cssClass: 'apexcharts-yaxis-title'
|
||||
}
|
||||
},
|
||||
subtitle: {
|
||||
text: this.subtitle,
|
||||
offsetX: 0,
|
||||
style: {
|
||||
fontSize: '14px',
|
||||
cssClass: 'apexcharts-yaxis-title'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -1,26 +1,35 @@
|
|||
<template>
|
||||
<div class="mb-4">
|
||||
<div class="card index-chart">
|
||||
<div class="mb-md-4 mb-5">
|
||||
<div class="card index-chart" :class="{'expanded-service': expanded}">
|
||||
<div class="card-body">
|
||||
<div class="col-12">
|
||||
<h4 class="mt-3">
|
||||
<router-link :to="serviceLink(service)" :in_service="service">{{service.name}}</router-link>
|
||||
<router-link :to="serviceLink(service)" class="d-inline-block text-truncate" style="max-width: 65vw;" :in_service="service">{{service.name}}</router-link>
|
||||
<span class="badge float-right" :class="{'bg-success': service.online, 'bg-danger': !service.online}">{{service.online ? "ONLINE" : "OFFLINE"}}</span>
|
||||
</h4>
|
||||
|
||||
<ServiceTopStats :service="service"/>
|
||||
|
||||
<div v-if="expanded" class="row">
|
||||
<Analytics title="Last Failure" level="100" value="35%" subtitle="417 Days ago"/>
|
||||
<Analytics title="Total Failures" level="100" value="35%" subtitle="417 Days ago"/>
|
||||
<Analytics title="Highest Latency" level="100" value="450ms" subtitle="417 Days ago"/>
|
||||
<Analytics title="Lowest Latency" level="100" value="120ms" subtitle="417 Days ago"/>
|
||||
<Analytics title="Total Uptime" level="100" value="35%" subtitle="850ms"/>
|
||||
<Analytics title="Total Downtime" level="100" value="35%" subtitle="32ms"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-observe-visibility="visibleChart" class="chart-container">
|
||||
<div v-if="!expanded" v-observe-visibility="visibleChart" class="chart-container">
|
||||
<ServiceChart :service="service" :visible="visible"/>
|
||||
</div>
|
||||
|
||||
<div class="row lower_canvas full-col-12 text-white" :class="{'bg-success': service.online, 'bg-danger': !service.online}">
|
||||
<div class="col-md-8 col-6">
|
||||
<div class="dropup" :class="{show: dropDownMenu}">
|
||||
<button style="font-size: 10pt;" @focusout="dropDownMenu = false" @click="dropDownMenu = !dropDownMenu" type="button" class="col-4 float-left btn btn-sm float-right btn-block text-white dropdown-toggle service_scale" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
<button style="font-size: 10pt;" @focusout="dropDownMenu = false" @click="dropDownMenu = !dropDownMenu" type="button" class="d-none col-4 float-left btn btn-sm float-right btn-block text-white dropdown-toggle service_scale" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
|
||||
24 Hours
|
||||
</button>
|
||||
<div class="dropdown-menu" :class="{show: dropDownMenu}">
|
||||
|
@ -34,8 +43,13 @@
|
|||
</div>
|
||||
|
||||
<div class="col-md-4 col-6 float-right">
|
||||
<router-link :to="serviceLink(service)" class="btn btn-sm float-right dyn-dark btn-block text-white" :class="{'bg-success': service.online, 'bg-danger': !service.online}">
|
||||
<router-link :to="serviceLink(service)" class="d-none btn btn-sm float-right dyn-dark btn-block text-white" :class="{'bg-success': service.online, 'bg-danger': !service.online}">
|
||||
View Service</router-link>
|
||||
<button @click="expanded = !expanded" class="btn btn-sm float-right dyn-dark btn-block text-white" :class="{'bg-success': service.online, 'bg-danger': !service.online}">View Service</button>
|
||||
</div>
|
||||
|
||||
<div v-if="expanded" class="row">
|
||||
<Analytics title="Last Failure" value="417 Days ago"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -44,12 +58,13 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import Analytics from './Analytics';
|
||||
import ServiceChart from "./ServiceChart";
|
||||
import ServiceTopStats from "@/components/Service/ServiceTopStats";
|
||||
|
||||
export default {
|
||||
name: 'ServiceBlock',
|
||||
components: {ServiceTopStats, ServiceChart},
|
||||
components: { Analytics, ServiceTopStats, ServiceChart},
|
||||
props: {
|
||||
service: {
|
||||
type: Object,
|
||||
|
@ -58,6 +73,7 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
expanded: false,
|
||||
visible: false,
|
||||
dropDownMenu: false,
|
||||
timeframes: [
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template v-show="showing">
|
||||
<apexchart v-if="ready" width="100%" height="235" type="area" :options="chartOptions" :series="series"/>
|
||||
<apexchart v-if="ready" class="service-chart" width="100%" height="100%" type="area" :options="chartOptions" :series="series"/>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -49,7 +49,7 @@
|
|||
text: 'Loading...'
|
||||
},
|
||||
chart: {
|
||||
height: 210,
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
type: "area",
|
||||
animations: {
|
||||
|
@ -163,14 +163,14 @@
|
|||
visible: function(newVal, oldVal) {
|
||||
if (newVal && !this.showing) {
|
||||
this.showing = true
|
||||
this.chartHits("2h")
|
||||
this.chartHits("1h")
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async chartHits(group) {
|
||||
window.console.log(this.service.created_at)
|
||||
this.data = await Api.service_hits(this.service.id, this.toUnix(this.service.created_at), this.toUnix(new Date()), group, false)
|
||||
const start = this.nowSubtract(84600 * 3)
|
||||
this.data = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(new Date()), group, false)
|
||||
|
||||
if (this.data.length === 0 && group !== "1h") {
|
||||
await this.chartHits("1h")
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<apexchart v-if="ready" width="100%" height="300" type="heatmap" :options="chartOptions" :series="series"></apexchart>
|
||||
<apexchart v-if="ready" width="100%" height="180" type="heatmap" :options="plotOptions" :series="series"></apexchart>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
|
@ -20,19 +20,32 @@
|
|||
return {
|
||||
ready: false,
|
||||
data: [],
|
||||
chartOptions: {
|
||||
heatmap: {
|
||||
plotOptions: {
|
||||
chart: {
|
||||
selection: {
|
||||
enabled: false
|
||||
},
|
||||
zoom: {
|
||||
enabled: false
|
||||
},
|
||||
toolbar: {
|
||||
show: false
|
||||
},
|
||||
},
|
||||
colors: [ "#cb3d36" ],
|
||||
enableShades: true,
|
||||
shadeIntensity: 0.5,
|
||||
colorScale: {
|
||||
ranges: [{
|
||||
ranges: [ {
|
||||
from: 0,
|
||||
to: 1,
|
||||
color: 'rgba(235,63,48,0.69)',
|
||||
name: 'low',
|
||||
to: 0,
|
||||
color: '#bababa',
|
||||
name: 'none',
|
||||
},
|
||||
{
|
||||
from: 2,
|
||||
to: 10,
|
||||
color: 'rgba(245,43,43,0.58)',
|
||||
color: '#cb3d36',
|
||||
name: 'medium',
|
||||
},
|
||||
{
|
||||
|
@ -42,76 +55,66 @@
|
|||
name: 'high',
|
||||
}
|
||||
]
|
||||
},
|
||||
xaxis: {
|
||||
tickAmount: '1',
|
||||
tickPlacement: 'between',
|
||||
min: 1,
|
||||
max: 31,
|
||||
type: "numeric",
|
||||
labels: {
|
||||
show: true
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false
|
||||
}
|
||||
},
|
||||
chart: {
|
||||
height: "100%",
|
||||
width: "100%",
|
||||
type: 'heatmap',
|
||||
toolbar: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
enableShades: true,
|
||||
shadeIntensity: 0.5,
|
||||
colors: ["#d53a3b"],
|
||||
series: [{data: [{}]}],
|
||||
yaxis: {
|
||||
labels: {
|
||||
formatter: (value) => {
|
||||
return value
|
||||
},
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
enabled: true,
|
||||
x: {
|
||||
show: false,
|
||||
},
|
||||
y: {
|
||||
formatter: function(val, opts) { return val+" Failures" },
|
||||
title: {
|
||||
formatter: (seriesName) => seriesName,
|
||||
},
|
||||
show: true
|
||||
},
|
||||
}
|
||||
},
|
||||
series: [{
|
||||
},
|
||||
series: [ {
|
||||
data: []
|
||||
}]
|
||||
} ]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async chartHeatmap() {
|
||||
let start = new Date(new Date().getUTCFullYear(), new Date().getUTCMonth()-3, 1);
|
||||
|
||||
let start = new Date(new Date().getUTCFullYear(), new Date().getUTCMonth()-2, 1);
|
||||
let monthData = [];
|
||||
let monthNum = start.getUTCMonth()
|
||||
|
||||
for (i=0; i<=3; i++) {
|
||||
let end = this.lastDayOfMonth(start.getUTCMonth()+start)
|
||||
const inputdata = this.heatmapData(start,end)
|
||||
for (let i=1; i<=3; i++) {
|
||||
let end = this.lastDayOfMonth(monthNum)
|
||||
|
||||
window.console.log("getting: ",start, end)
|
||||
|
||||
const inputdata = await this.heatmapData(start, end)
|
||||
monthData.push(inputdata)
|
||||
start = new Date(start.getUTCFullYear(), start.getUTCMonth()+1, 1);
|
||||
monthNum += 1
|
||||
}
|
||||
|
||||
this.series = monthData
|
||||
window.console.log(monthData)
|
||||
this.series = monthData.reverse()
|
||||
this.ready = true
|
||||
},
|
||||
async heatmapData(start, end) {
|
||||
console.log(start, end)
|
||||
|
||||
window.console.log("start: ", start)
|
||||
window.console.log("end: ", end)
|
||||
|
||||
const data = await Api.service_failures_data(this.service.id, this.toUnix(start), this.toUnix(end), "24h", true)
|
||||
|
||||
let dataArr = []
|
||||
data.forEach(function(d) {
|
||||
dataArr.push({x: d.timeframe, y: d.amount});
|
||||
data.forEach((d) => {
|
||||
dataArr.push({x: this.parseTime(d.timeframe).getUTCDate(), y: d.amount});
|
||||
});
|
||||
|
||||
let date = new Date(dataArr[0].x);
|
||||
const output = [{name: date.toLocaleString('en-us', { month: 'long'}), data: dataArr}]
|
||||
|
||||
return {name: start.toLocaleString('en-us', { month: 'long'}), data: dataArr}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import Vue from "vue";
|
||||
const { zonedTimeToUtc, utcToZonedTime, lastDayOfMonth, subSeconds, parse, parseISO, getUnixTime, fromUnixTime, format, differenceInSeconds, formatDistanceToNow, formatDistance } = require('date-fns')
|
||||
const { zonedTimeToUtc, utcToZonedTime, lastDayOfMonth, subSeconds, parse, getUnixTime, fromUnixTime, differenceInSeconds, formatDistance } = require('date-fns')
|
||||
import formatDistanceToNow from 'date-fns/formatDistanceToNow'
|
||||
import format from 'date-fns/format'
|
||||
import parseISO from 'date-fns/parseISO'
|
||||
|
||||
export default Vue.mixin({
|
||||
methods: {
|
||||
|
@ -7,10 +10,10 @@ export default Vue.mixin({
|
|||
return new Date()
|
||||
},
|
||||
current() {
|
||||
return parse(new Date())
|
||||
return parseISO(new Date())
|
||||
},
|
||||
utc(val) {
|
||||
return fromUnixTime(this.toUnix(val) + val.getTimezoneOffset() * 60 * 1000)
|
||||
return new Date.UTC(val)
|
||||
},
|
||||
ago(t1) {
|
||||
return formatDistanceToNow(t1)
|
||||
|
@ -25,11 +28,14 @@ export default Vue.mixin({
|
|||
return formatDistance(t1, t2)
|
||||
},
|
||||
niceDate(val) {
|
||||
return this.parseTime(val).format('LLLL')
|
||||
return format(parseISO(val), "EEEE, MMM do h:mma")
|
||||
},
|
||||
parseTime(val) {
|
||||
return parseISO(val)
|
||||
},
|
||||
parseISO(v) {
|
||||
return parseISO(v)
|
||||
},
|
||||
toLocal(val, suf = 'at') {
|
||||
const t = this.parseTime(val)
|
||||
return format(t, `EEEE, MMM do h:mma`)
|
||||
|
@ -41,7 +47,7 @@ export default Vue.mixin({
|
|||
return fromUnixTime(val)
|
||||
},
|
||||
isBetween(t1, t2) {
|
||||
return differenceInSeconds(parseISO(t1), parseISO(t2)) > 0
|
||||
return differenceInSeconds(t1, t2) >= 0
|
||||
},
|
||||
hour() {
|
||||
return 3600
|
||||
|
@ -111,10 +117,10 @@ export default Vue.mixin({
|
|||
return {data: newSet}
|
||||
},
|
||||
lastDayOfMonth(month) {
|
||||
return new Date(new Date().getUTCFullYear(), month + 1, 0);
|
||||
return new Date(Date.UTC(new Date().getUTCFullYear(), month + 1, 0))
|
||||
},
|
||||
firstDayOfMonth(month) {
|
||||
return new Date(new Date().getUTCFullYear(), month, 1).getUTCDate();
|
||||
return new Date(Date.UTC(new Date().getUTCFullYear(), month, 1)).getUTCDate()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div class="container col-md-7 col-sm-12 mt-4 sm-container index_container pt-5">
|
||||
<div class="container col-md-7 col-sm-12 sm-container index_container">
|
||||
|
||||
<Header/>
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div v-if="ready" class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
||||
<div v-if="service" class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
||||
|
||||
<div class="col-12 mb-4">
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
|||
</span>
|
||||
|
||||
<h4 class="mt-2">
|
||||
<router-link to="/">{{$store.getters.core.name}}</router-link> - {{service.name}}
|
||||
<router-link to="/" class="text-black-50 text-decoration-none">{{$store.getters.core.name}}</router-link> - <span class="text-muted">{{service.name}}</span>
|
||||
<span class="badge float-right d-none d-md-block" :class="{'bg-success': service.online, 'bg-danger': !service.online}">
|
||||
{{service.online ? "ONLINE" : "OFFLINE"}}
|
||||
</span>
|
||||
|
@ -16,7 +16,7 @@
|
|||
|
||||
<ServiceTopStats :service="service"/>
|
||||
|
||||
<div v-for="(message, index) in messages" v-if="messageInRange(message)">
|
||||
<div v-for="(message, index) in $store.getters.serviceMessages(service.id)" v-if="messageInRange(message)">
|
||||
<MessageBlock :message="message"/>
|
||||
</div>
|
||||
|
||||
|
@ -33,13 +33,13 @@
|
|||
<apexchart width="100%" height="420" type="area" :options="chartOptions" :series="series"></apexchart>
|
||||
</div>
|
||||
|
||||
<div class="service-chart-heatmap mt-3 mb-4">
|
||||
<div class="service-chart-heatmap mt-5 mb-4">
|
||||
<ServiceHeatmap :service="service"/>
|
||||
</div>
|
||||
|
||||
<div v-if="series" class="service-chart-container">
|
||||
<apexchart width="100%" height="300" type="range" :options="dailyRangeOpts" :series="series"></apexchart>
|
||||
</div>
|
||||
<!-- <div v-if="series" class="service-chart-container">-->
|
||||
<!-- <apexchart width="100%" height="300" type="range" :options="dailyRangeOpts" :series="series"></apexchart>-->
|
||||
<!-- </div>-->
|
||||
|
||||
<nav v-if="service.failures" class="nav nav-pills flex-column flex-sm-row mt-3" id="service_tabs">
|
||||
<a @click="tab='failures'" class="flex-sm-fill text-sm-center nav-link active">Failures</a>
|
||||
|
@ -95,8 +95,10 @@
|
|||
import Checkin from "../forms/Checkin";
|
||||
import ServiceHeatmap from "@/components/Service/ServiceHeatmap";
|
||||
import ServiceTopStats from "@/components/Service/ServiceTopStats";
|
||||
import store from '../store'
|
||||
import flatPickr from 'vue-flatpickr-component';
|
||||
import 'flatpickr/dist/flatpickr.css';
|
||||
const timeoptions = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' };
|
||||
|
||||
const axisOptions = {
|
||||
labels: {
|
||||
|
@ -134,16 +136,15 @@ export default {
|
|||
},
|
||||
data() {
|
||||
return {
|
||||
id: null,
|
||||
id: this.$route.params.id,
|
||||
tab: "failures",
|
||||
service: {},
|
||||
authenticated: false,
|
||||
ready: false,
|
||||
ready: true,
|
||||
data: null,
|
||||
messages: [],
|
||||
failures: [],
|
||||
start_time: "",
|
||||
end_time: "",
|
||||
start_time: this.nowSubtract(84600 * 30),
|
||||
end_time: new Date(),
|
||||
dailyRangeOpts: {
|
||||
chart: {
|
||||
height: 500,
|
||||
|
@ -153,6 +154,19 @@ export default {
|
|||
},
|
||||
chartOptions: {
|
||||
chart: {
|
||||
events: {
|
||||
beforeZoom: async (chartContext, { xaxis }) => {
|
||||
const start = (xaxis.min / 1000).toFixed(0)
|
||||
const end = (xaxis.max / 1000).toFixed(0)
|
||||
await this.chartHits(start, end, "10m")
|
||||
return {
|
||||
xaxis: {
|
||||
min: this.fromUnix(start),
|
||||
max: this.fromUnix(end)
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
height: 500,
|
||||
width: "100%",
|
||||
type: "area",
|
||||
|
@ -163,39 +177,65 @@ export default {
|
|||
}
|
||||
},
|
||||
selection: {
|
||||
enabled: false
|
||||
enabled: true
|
||||
},
|
||||
zoom: {
|
||||
enabled: false
|
||||
enabled: true
|
||||
},
|
||||
toolbar: {
|
||||
show: false
|
||||
show: true
|
||||
},
|
||||
stroke: {
|
||||
show: false,
|
||||
curve: 'smooth',
|
||||
lineCap: 'butt',
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
show: true,
|
||||
padding: {
|
||||
top: 0,
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
left: 0,
|
||||
}
|
||||
},
|
||||
xaxis: {
|
||||
type: "datetime",
|
||||
...axisOptions
|
||||
labels: {
|
||||
show: true
|
||||
},
|
||||
tooltip: {
|
||||
enabled: true
|
||||
}
|
||||
},
|
||||
yaxis: {
|
||||
...axisOptions
|
||||
labels: {
|
||||
show: true
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
enabled: false,
|
||||
marker: {
|
||||
show: false,
|
||||
theme: false,
|
||||
enabled: true,
|
||||
custom: function ({ 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];
|
||||
if (val >= 1000) {
|
||||
val = (val * 0.1).toFixed(0) + " milliseconds"
|
||||
} else {
|
||||
val = (val * 0.01).toFixed(0) + " microseconds"
|
||||
}
|
||||
return `<div class="chartmarker"><span>Average Response Time: </span><span class="font-3">${val}</span><span>${dt}</span></div>`
|
||||
},
|
||||
fixed: {
|
||||
enabled: true,
|
||||
position: 'topRight',
|
||||
offsetX: -30,
|
||||
offsetY: 40,
|
||||
},
|
||||
x: {
|
||||
show: false,
|
||||
}
|
||||
format: 'dd MMM',
|
||||
formatter: undefined,
|
||||
},
|
||||
y: {
|
||||
formatter: undefined,
|
||||
title: {
|
||||
formatter: (seriesName) => seriesName,
|
||||
},
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
show: false,
|
||||
|
@ -205,7 +245,7 @@ export default {
|
|||
},
|
||||
floating: true,
|
||||
axisTicks: {
|
||||
show: false
|
||||
show: true
|
||||
},
|
||||
axisBorder: {
|
||||
show: false
|
||||
|
@ -231,23 +271,32 @@ export default {
|
|||
},
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
const id = this.$attrs.id
|
||||
this.id = id
|
||||
let service;
|
||||
if (this.isInt(id)) {
|
||||
service = this.$store.getters.serviceById(id)
|
||||
} else {
|
||||
service = this.$store.getters.serviceByPermalink(id)
|
||||
computed: {
|
||||
service () {
|
||||
return this.$store.getters.serviceByAll(this.id)
|
||||
}
|
||||
this.service = service
|
||||
this.getService(service)
|
||||
this.messages = this.$store.getters.serviceMessages(service.id)
|
||||
},
|
||||
watch: {
|
||||
service: function(n, o) {
|
||||
this.chartHits()
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
async get() {
|
||||
const s = store.getters.serviceByAll(this.id)
|
||||
window.console.log("service: ", s)
|
||||
this.getService(this.service)
|
||||
this.messages = this.$store.getters.serviceMessages(this.service.id)
|
||||
},
|
||||
messageInRange(message) {
|
||||
const start = this.isBetween(new Date(), message.start_on)
|
||||
const end = this.isBetween(message.end_on, new Date())
|
||||
const start = this.isBetween(this.now(), this.parseTime(message.start_on))
|
||||
const end = this.isBetween(this.parseTime(message.end_on), this.now())
|
||||
return start && end
|
||||
},
|
||||
async getService(s) {
|
||||
|
@ -255,11 +304,19 @@ export default {
|
|||
await this.serviceFailures()
|
||||
},
|
||||
async serviceFailures() {
|
||||
this.failures = await Api.service_failures(this.service.id, this.now() - 3600, this.now(), 15)
|
||||
let tt = this.startEndTimes()
|
||||
|
||||
this.failures = await Api.service_failures(this.service.id, tt.start, tt.end)
|
||||
},
|
||||
async chartHits() {
|
||||
this.end_time = new Date()
|
||||
this.data = await Api.service_hits(this.service.id, this.toUnix(this.service.created_at), this.toUnix(new Date()), "30m", false)
|
||||
async chartHits(start=0, end=99999999999, group="30m") {
|
||||
let tt = {};
|
||||
if (start === 0) {
|
||||
tt = this.startEndTimes()
|
||||
} else {
|
||||
tt = {start, end}
|
||||
}
|
||||
|
||||
this.data = await Api.service_hits(this.service.id, tt.start, tt.end, group, false)
|
||||
if (this.data.length === 0 && group !== "1h") {
|
||||
await this.chartHits("1h")
|
||||
}
|
||||
|
@ -268,11 +325,15 @@ export default {
|
|||
...this.convertToChartData(this.data)
|
||||
}]
|
||||
this.ready = true
|
||||
},
|
||||
startEndTimes() {
|
||||
const start = this.toUnix(this.parseTime(this.service.stats.first_hit))
|
||||
const end = this.toUnix(new Date())
|
||||
return {start, end}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
||||
|
|
|
@ -27,7 +27,6 @@ const routes = [
|
|||
},
|
||||
{
|
||||
path: '/dashboard',
|
||||
name: 'Dashboard',
|
||||
component: Dashboard,
|
||||
meta: {
|
||||
requiresAuth: true
|
||||
|
|
|
@ -25,8 +25,7 @@ export default new Vuex.Store({
|
|||
groups: [],
|
||||
messages: [],
|
||||
users: [],
|
||||
notifiers: [],
|
||||
integrations: []
|
||||
notifiers: []
|
||||
},
|
||||
getters: {
|
||||
hasAllData: state => state.hasAllData,
|
||||
|
@ -38,13 +37,19 @@ export default new Vuex.Store({
|
|||
messages: state => state.messages,
|
||||
users: state => state.users,
|
||||
notifiers: state => state.notifiers,
|
||||
integrations: state => state.integrations,
|
||||
|
||||
servicesInOrder: state => state.services.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),
|
||||
|
||||
serviceByAll: (state) => (element) => {
|
||||
if (element % 1 === 0) {
|
||||
return state.services.find(s => s.id == element)
|
||||
} else {
|
||||
return state.services.find(s => s.permalink === element)
|
||||
}
|
||||
},
|
||||
serviceById: (state) => (id) => {
|
||||
return state.services.find(s => s.id == id)
|
||||
},
|
||||
|
@ -100,12 +105,13 @@ export default new Vuex.Store({
|
|||
},
|
||||
setNotifiers (state, notifiers) {
|
||||
state.notifiers = notifiers
|
||||
},
|
||||
setIntegrations (state, integrations) {
|
||||
state.integrations = integrations
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async getAllServices(context) {
|
||||
const services = await Api.services()
|
||||
context.commit("setServices", services);
|
||||
},
|
||||
async loadRequired(context) {
|
||||
const core = await Api.core()
|
||||
context.commit("setCore", core);
|
||||
|
@ -140,8 +146,6 @@ export default new Vuex.Store({
|
|||
context.commit("setNotifiers", notifiers);
|
||||
const users = await Api.users()
|
||||
context.commit("setUsers", users);
|
||||
const integrations = await Api.integrations()
|
||||
context.commit("setIntegrations", integrations);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -128,7 +128,11 @@ func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
groupQuery := database.ParseQueries(r, service.AllHits())
|
||||
groupQuery, err := database.ParseQueries(r, service.AllHits())
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
objs, err := groupQuery.GraphData(database.ByAverage("latency", 1000))
|
||||
if err != nil {
|
||||
|
@ -146,7 +150,11 @@ func apiServiceFailureDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
groupQuery := database.ParseQueries(r, service.AllFailures())
|
||||
groupQuery, err := database.ParseQueries(r, service.AllFailures())
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
objs, err := groupQuery.GraphData(database.ByCount)
|
||||
if err != nil {
|
||||
|
@ -164,7 +172,11 @@ func apiServicePingDataHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
groupQuery := database.ParseQueries(r, service.AllHits())
|
||||
groupQuery, err := database.ParseQueries(r, service.AllHits())
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
objs, err := groupQuery.GraphData(database.ByAverage("ping_time", 1000))
|
||||
if err != nil {
|
||||
|
@ -216,7 +228,11 @@ func apiServiceFailuresHandler(r *http.Request) interface{} {
|
|||
}
|
||||
|
||||
var fails []*failures.Failure
|
||||
database.ParseQueries(r, service.AllFailures()).Find(&fails)
|
||||
query, err := database.ParseQueries(r, service.AllFailures())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
query.Find(&fails)
|
||||
return fails
|
||||
}
|
||||
|
||||
|
@ -228,6 +244,10 @@ func apiServiceHitsHandler(r *http.Request) interface{} {
|
|||
}
|
||||
|
||||
var hts []*hits.Hit
|
||||
database.ParseQueries(r, service.AllHits()).Find(&hts)
|
||||
query, err := database.ParseQueries(r, service.AllHits())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
query.Find(&hts)
|
||||
return hts
|
||||
}
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
package configs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/statping/statping/utils"
|
||||
"os"
|
||||
)
|
||||
|
||||
const latestMigration = 1583860000
|
||||
|
||||
func init() {
|
||||
os.Setenv("MIGRATION_ID", utils.ToString(latestMigration))
|
||||
}
|
||||
|
||||
func (c *DbConfig) genericMigration(alterStr string) error {
|
||||
if err := c.Db.Exec(fmt.Sprintf("ALTER TABLE hits %s COLUMN latency TYPE BIGINT;", alterStr)).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(fmt.Sprintf("ALTER TABLE hits %s COLUMN ping_time TYPE BIGINT;", alterStr)).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(fmt.Sprintf("ALTER TABLE failures %s COLUMN latency TYPE BIGINT;", alterStr)).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec("UPDATE hits SET latency = CAST(latency * 1000000 AS bigint);").Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec("UPDATE hits SET ping_time = CAST(ping_time * 1000000 AS bigint);").Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec("UPDATE failures SET ping_time = CAST(ping_time * 1000000 AS bigint);").Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *DbConfig) sqliteMigration() error {
|
||||
if err := c.Db.Exec(`ALTER TABLE hits RENAME TO hits_backup;`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(`CREATE TABLE hits (id INTEGER PRIMARY KEY AUTOINCREMENT, service bigint, latency bigint, ping_time bigint, created_at datetime);`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(`INSERT INTO hits (id, service, latency, ping_time, created_at) SELECT id, service, CAST(latency * 1000000 AS bigint), CAST(ping_time * 1000000 AS bigint), created_at FROM hits_backup;`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
// failures table
|
||||
if err := c.Db.Exec(`ALTER TABLE failures RENAME TO failures_backup;`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(`CREATE TABLE failures (id INTEGER PRIMARY KEY AUTOINCREMENT, issue varchar(255), method varchar(255), method_id bigint, service bigint, ping_time bigint, checkin bigint, error_code bigint, created_at datetime);`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(`INSERT INTO failures (id, issue, method, method_id, service, ping_time, checkin, created_at) SELECT id, issue, method, method_id, service, CAST(ping_time * 1000000 AS bigint), checkin, created_at FROM failures_backup;`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(`DROP TABLE hits_backup;`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := c.Db.Exec(`DROP TABLE failures_backup;`).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -2,6 +2,9 @@ package configs
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
"github.com/statping/statping/types/checkins"
|
||||
"github.com/statping/statping/types/core"
|
||||
"github.com/statping/statping/types/failures"
|
||||
|
@ -12,53 +15,36 @@ import (
|
|||
"github.com/statping/statping/types/notifications"
|
||||
"github.com/statping/statping/types/services"
|
||||
"github.com/statping/statping/types/users"
|
||||
|
||||
_ "github.com/jinzhu/gorm/dialects/mysql"
|
||||
_ "github.com/jinzhu/gorm/dialects/postgres"
|
||||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
)
|
||||
|
||||
// InsertNotifierDB inject the Statping database instance to the Notifier package
|
||||
//func (c *DbConfig) InsertNotifierDB() error {
|
||||
// if !database.Available() {
|
||||
// err := c.Connect()
|
||||
// if err != nil {
|
||||
// return errors.New("database connection has not been created")
|
||||
// }
|
||||
// }
|
||||
// notifiers.SetDB(database.DB())
|
||||
// return nil
|
||||
//}
|
||||
func (c *DbConfig) DatabaseChanges() error {
|
||||
var cr core.Core
|
||||
c.Db.Model(&core.Core{}).Find(&cr)
|
||||
|
||||
// InsertIntegratorDB inject the Statping database instance to the Integrations package
|
||||
//func (c *DbConfig) InsertIntegratorDB() error {
|
||||
// if !database.Available() {
|
||||
// err := c.Connect()
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err,"database connection has not been created")
|
||||
// }
|
||||
// }
|
||||
// integrations.SetDB(database.DB())
|
||||
// return nil
|
||||
//}
|
||||
if latestMigration > cr.MigrationId {
|
||||
log.Infof("Statping database is out of date, migrating to: %d", latestMigration)
|
||||
|
||||
func (c *DbConfig) VerifyMigration() error {
|
||||
switch c.Db.DbType() {
|
||||
case "mysql":
|
||||
if err := c.genericMigration("MODIFY"); err != nil {
|
||||
return err
|
||||
}
|
||||
case "postgres":
|
||||
if err := c.genericMigration("ALTER"); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
if err := c.sqliteMigration(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
query := `
|
||||
BEGIN TRANSACTION;
|
||||
ALTER TABLE hits ALTER COLUMN latency BIGINT;
|
||||
ALTER TABLE hits ALTER COLUMN ping_time BIGINT;
|
||||
ALTER TABLE failures ALTER COLUMN ping_time BIGINT;
|
||||
UPDATE hits SET latency = CAST(latency * 10000 AS BIGINT);
|
||||
UPDATE hits SET ping_time = CAST(ping_time * 100000 AS BIGINT);
|
||||
UPDATE failures SET ping_time = CAST(ping_time * 100000 AS BIGINT);
|
||||
COMMIT;`
|
||||
if err := c.Db.Exec(fmt.Sprintf("UPDATE core SET migration_id = %d", latestMigration)).Error(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Println(c.Db.DbType())
|
||||
|
||||
q := c.Db.Raw(query).Debug()
|
||||
|
||||
return q.Error()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//MigrateDatabase will migrate the database structure to current version.
|
||||
|
|
|
@ -3,12 +3,11 @@ package core
|
|||
import (
|
||||
"github.com/statping/statping/types/null"
|
||||
"github.com/statping/statping/utils"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Samples() error {
|
||||
apiKey := utils.Getenv("API_KEY", "samplekey")
|
||||
apiSecret := utils.Getenv("API_SECRET", "samplesecret")
|
||||
apiKey := utils.Getenv("API_KEY", utils.RandomString(16))
|
||||
apiSecret := utils.Getenv("API_SECRET", utils.RandomString(16))
|
||||
|
||||
core := &Core{
|
||||
Name: "Statping Sample Data",
|
||||
|
@ -16,10 +15,10 @@ func Samples() error {
|
|||
ApiKey: apiKey.(string),
|
||||
ApiSecret: apiSecret.(string),
|
||||
Domain: "http://localhost:8080",
|
||||
Version: "test",
|
||||
CreatedAt: time.Now().UTC(),
|
||||
CreatedAt: utils.Now(),
|
||||
UseCdn: null.NewNullBool(false),
|
||||
Footer: null.NewNullString(""),
|
||||
MigrationId: utils.Now().Unix(),
|
||||
}
|
||||
|
||||
return core.Create()
|
||||
|
|
Loading…
Reference in New Issue