pull/429/head
hunterlong 2020-03-10 01:49:57 -07:00
parent e0288b21a4
commit de6cf2407f
16 changed files with 251 additions and 80 deletions

View File

@ -3,10 +3,10 @@ RUN npm install yarn -g
WORKDIR /statping
COPY ./frontend/package.json .
COPY ./frontend/yarn.lock .
RUN yarn install
RUN yarn install --pure-lockfile --network-timeout 1000000
COPY ./frontend .
RUN yarn build && rm -rf node_modules
RUN yarn build && rm -rf node_modules && yarn cache clean
# Compiles webpacked Vue production build for frontend at /statping/dist
# Statping Golang BACKEND building from source
# Creates "/go/bin/statping" and "/usr/local/bin/sass" for copying

View File

@ -1,36 +1,31 @@
FROM golang:1.14-alpine
FROM node:10.17.0 AS frontend
RUN npm install yarn -g
WORKDIR /statping
COPY ./frontend/package.json .
COPY ./frontend/yarn.lock .
RUN yarn install --pure-lockfile --network-timeout 1000000
COPY ./frontend .
RUN yarn build && rm -rf node_modules && yarn cache clean
# Statping Golang BACKEND building from source
# Creates "/go/bin/statping" and "/usr/local/bin/sass" for copying
FROM golang:1.14-alpine AS backend
LABEL maintainer="Hunter Long (https://github.com/hunterlong)"
ARG VERSION
RUN apk add --update --no-cache libstdc++ gcc g++ make git ca-certificates linux-headers wget curl jq libsass nodejs nodejs-npm
RUN npm install -g yarn
RUN apk add --update --no-cache libstdc++ gcc g++ make git ca-certificates linux-headers wget curl jq
RUN curl -L -s https://assets.statping.com/sass -o /usr/local/bin/sass && \
chmod +x /usr/local/bin/sass
WORKDIR /go/src/github.com/statping/statping
ADD go.mod go.sum ./
RUN go mod download
ENV GO111MODULE on
RUN go get github.com/stretchr/testify/assert && \
go get github.com/stretchr/testify/require && \
go get github.com/GeertJohan/go.rice/rice && \
go get github.com/cortesi/modd/cmd/modd && \
go get github.com/crazy-max/xgo
ADD frontend/package.json frontend/yarn.lock ./frontend/
RUN cd frontend && yarn install --pure-lockfile --network-timeout 1000000 && \
yarn cache clean
COPY . ./
RUN make clean
RUN make compile
RUN make build
COPY . .
COPY --from=frontend /statping/dist/ ./source/dist/
RUN make clean generate embed build
RUN chmod a+x statping && mv statping /go/bin/statping
WORKDIR /app

View File

@ -58,8 +58,8 @@ func databaseMaintence(dur time.Duration) {
// DeleteAllSince will delete a specific table's records based on a time.
func DeleteAllSince(table string, date time.Time) {
sql := fmt.Sprintf("DELETE FROM %v WHERE created_at < '%v';", table, database.FormatTime(date))
q := database.Exec(sql)
sql := fmt.Sprintf("DELETE FROM %s WHERE created_at < '%s';", table, database.FormatTime(date))
q := database.Exec(sql).Debug()
if q.Error() != nil {
log.Warnln(q.Error())
}

View File

@ -5,14 +5,18 @@ HTML,BODY {
background-color: $background-color;
}
.index-chart {
height: 35vh;
}
.chartmarker {
background-color: white;
padding: 5px;
width: 240px;
text-align: right;
}
.chartmarker SPAN {
font-size: 11pt;
font-size: 9pt;
display: block;
color: #8b8b8b;
}
@ -153,9 +157,17 @@ HTML,BODY {
font-size: 9pt;
}
.font-3 {
font-size: 11pt;
}
.font-3 {
font-size: 11pt;
}
.font-4 {
font-size: 14pt;
}
.font-5 {
font-size: 20pt;
}
.badge {
color: white;
@ -222,7 +234,7 @@ HTML,BODY {
.chart-container {
position: relative;
height: 24.1vh;
height: 18.6vh;
width: 100%;
overflow: hidden;
}

View File

@ -6,8 +6,8 @@
background-color: $sm-background-color;
}
.index_container {
padding-top: 4.5vh !important;
.index-chart {
height: 33vh;
}
.sm-container {
@ -60,7 +60,7 @@
.btn-sm {
line-height: 1.4rem;
font-size: 1rem;
font-size: 0.83rem;
}
.full-col-12 {
@ -73,6 +73,8 @@
border-radius: $sm-border-radius;
padding: $sm-padding;
background-color: $sm-service-background;
box-shadow: 0px 3px 10px 2px;
color: #b5b5b5;
}
.card-body {

View File

@ -1,6 +1,6 @@
<template>
<div class="col-12 full-col-12">
<h4 v-if="group.name !== 'Empty Group'" class="group_header mb-2 mt-4">{{group.name}}</h4>
<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">
<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">

View File

@ -1,6 +1,6 @@
<template>
<div class="mb-4">
<div class="card">
<div class="card index-chart">
<div class="card-body">
<div class="col-12">
<h4 class="mt-3">

View File

@ -25,9 +25,6 @@
grid: {
show: false
},
marker: {
show: true
}
};
export default {
@ -88,29 +85,36 @@
labels: {
show: false
},
tooltip: {
enabled: false
}
},
yaxis: {
labels: {
show: false
},
},
markers: {
size: 0,
strokeWidth: 0,
hover: {
size: undefined,
sizeOffset: 0
}
},
tooltip: {
theme: false,
enabled: true,
markers: {
size: 0
},
custom: function({series, seriesIndex, dataPointIndex, w}) {
let service = w.globals.seriesNames[0];
let ts = w.globals;
window.console.log(ts);
let ts = w.globals.seriesX[seriesIndex][dataPointIndex];
const dt = new Date(ts).toLocaleDateString("en-us", timeoptions)
let val = series[seriesIndex][dataPointIndex];
if (val > 1000) {
if (val >= 1000) {
val = (val * 0.1).toFixed(0) + " milliseconds"
} else {
val = (val * 0.01).toFixed(0) + " microseconds"
}
return `<div class="chartmarker"><span>${service} Average Response</span> <span class="font-3">${val}</span></div>`
return `<div class="chartmarker"><span>Average Response Time: </span><span class="font-3">${val}</span><span>${dt}</span></div>`
},
fixed: {
enabled: true,
@ -121,6 +125,9 @@
x: {
show: false,
},
y: {
formatter: (value) => { return value + "%" },
},
},
legend: {
show: false,

View File

@ -1,20 +1,51 @@
<template v-if="service">
<div class="col-12 card mb-3" style="min-height: 260px" :class="{'offline-card': !service.online}">
<div class="card-body">
<h5 class="card-title"><router-link :to="serviceLink(service)">{{service.name}}</router-link>
<div class="col-12 card mb-4" style="min-height: 360px" :class="{'offline-card': !service.online}">
<div class="card-body p-3 p-md-1 pt-md-3 pb-md-1">
<h4 class="card-title mb-4"><router-link :to="serviceLink(service)">{{service.name}}</router-link>
<span class="badge float-right" :class="{'badge-success': service.online, 'badge-danger': !service.online}">
{{service.online ? "ONLINE" : "OFFLINE"}}
</span>
</h5>
</h4>
<transition name="fade">
<div v-if="loaded && service.online" class="row">
<div class="col-md-6 col-sm-12">
<ServiceSparkLine :title="set1_name" subtitle="Last Day Latency" :series="set1"/>
<div class="col-md-6 col-sm-12 mt-2 mt-md-0">
<ServiceSparkLine :title="set2_name" subtitle="Latency Last 24 Hours" :series="set2"/>
</div>
<div class="col-md-6 col-sm-12">
<ServiceSparkLine :title="set2_name" subtitle="Last 7 Days Latency" :series="set2"/>
<div class="col-md-6 col-sm-12 mt-4 mt-md-0">
<ServiceSparkLine :title="set1_name" subtitle="Latency Last 7 Days" :series="set1"/>
</div>
<div class="d-none row col-12 mt-4 pt-1 mb-3 align-content-center">
<StatsGen :service="service"
title="Since Yesterday"
:start="this.toUnix(this.nowSubtract(86400 * 2))"
:end="this.toUnix(this.nowSubtract(86400))"
group="24h" expression="latencyPercent"/>
<StatsGen :service="service"
title="7 Day Change"
:start="this.toUnix(this.nowSubtract(86400 * 7))"
:end="this.toUnix(this.now())"
group="24h" expression="latencyPercent"/>
<StatsGen :service="service"
title="Max Latency"
:start="this.toUnix(this.nowSubtract(86400 * 2))"
:end="this.toUnix(this.nowSubtract(86400))"
group="24h" expression="latencyPercent"/>
<StatsGen :service="service"
title="Uptime"
:start="this.toUnix(this.nowSubtract(86400 * 2))"
:end="this.toUnix(this.nowSubtract(86400))"
group="24h" expression="latencyPercent"/>
</div>
</div>
</transition>
</div>
<span v-for="(failure, index) in failures" v-bind:key="index" class="alert alert-light">
Failed {{failure.created_at}}<br>
{{failure.issue}}
@ -26,10 +57,12 @@
<script>
import ServiceSparkLine from "./ServiceSparkLine";
import Api from "../../API";
import StatsGen from "./StatsGen";
export default {
name: 'ServiceInfo',
components: {
StatsGen,
ServiceSparkLine
},
props: {
@ -49,34 +82,37 @@
}
},
async mounted() {
this.set1 = await this.getHits(24, "15m")
this.set1 = await this.getHits(24 * 7, "6h")
this.set1_name = this.calc(this.set1)
this.set2 = await this.getHits(24 * 7, "6h")
this.set2 = await this.getHits(24, "1h")
this.set2_name = this.calc(this.set2)
this.loaded = true
window.console.log(this.set1)
},
methods: {
sinceYesterday(data) {
window.console.log(data)
let total = 0
data.forEach((f) => {
total += parseInt(f.y)
});
total = total / data.length
},
async getHits(hours, group) {
const start = this.nowSubtract(3600 * hours)
if (!this.service.online) {
this.failures = await Api.service_failures(this.service.id, this.toUnix(start), this.toUnix(this.now()), 5)
return [{name: "None", data: []}]
}
const fetched = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(this.now()), group, false)
if (!data) {
return [{name: "None", data: []}]
}
const data = this.convertToChartData(fetched, 1000, true)
return [{name: "Latency", data}]
const data = this.convertToChartData(fetched, 0.001, true)
return [{name: "Latency", ...data}]
},
calc(s) {
let data = s[0].data
if (data) {
let total = 0
data.forEach((f) => {
total += f.y
total += parseInt(f.y)
});
total = total / data.length
return total.toFixed(0) + "ms"

View File

@ -1,9 +1,12 @@
<template v-if="series.length">
<apexchart width="100%" height="180" type="area" :options="chartOpts" :series="series"></apexchart>
<apexchart width="100%" height="180" type="bar" :options="chartOpts" :series="series"></apexchart>
</template>
<script>
export default {
const timeoptions = { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', hour: 'numeric', minute: 'numeric' };
export default {
name: 'ServiceSparkLine',
props: {
series: {
@ -29,7 +32,7 @@ export default {
return {
chartOpts: {
chart: {
type: 'area',
type: 'bar',
height: 180,
sparkline: {
enabled: true
@ -44,9 +47,33 @@ export default {
yaxis: {
min: 0
},
colors: ['#DCE6EC'],
colors: ['#b3bdc3'],
tooltip: {
enabled: 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: -5,
offsetY: 0,
},
x: {
show: false,
},
y: {
formatter: (value) => { return value + "%" },
},
},
title: {
text: this.title,

View File

@ -0,0 +1,70 @@
<template>
<div class="col-3 text-center">
<span class="text-success font-5 font-weight-bold">{{value}}</span>
<span class="font-2 d-block">{{title}}</span>
</div>
</template>
<script>
import Api from "../../API";
export default {
name: 'StatsGen',
props: {
service: {
type: Object,
required: true
},
title: {
type: String,
required: true
},
start: {
type: Number,
required: true
},
end: {
type: Number,
required: true
},
group: {
type: String,
required: true
},
expression: {
type: String,
required: true
}
},
data() {
return {
value: "+17%"
}
},
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>

View File

@ -61,7 +61,7 @@
this.error = true
} else if (auth.token) {
this.auth = Api.saveToken(this.username, auth.token)
await this.$store.dispatch('loadAdmin')
this.$store.dispatch('loadAdmin')
this.$router.push('/dashboard')
}
this.loading = false

View File

@ -1,5 +1,5 @@
<template>
<div class="container col-md-7 col-sm-12 mt-4 sm-container index_container">
<div class="container col-md-7 col-sm-12 mt-4 sm-container index_container pt-5">
<Header/>

View File

@ -35,8 +35,15 @@ func Select() (*Core, error) {
}
func (c *Core) Create() error {
//apiKey := utils.Getenv("API_KEY", utils.NewSHA1Hash(40))
//apiSecret := utils.Getenv("API_SECRET", utils.NewSHA1Hash(40))
c.ApiKey = c.ApiKey
c.ApiSecret = c.ApiSecret
apiKey := utils.Getenv("API_KEY", utils.NewSHA1Hash(40)).(string)
apiSecret := utils.Getenv("API_SECRET", utils.NewSHA1Hash(40)).(string)
if c.ApiKey == "" || c.ApiSecret == "" {
c.ApiSecret = apiSecret
c.ApiKey = apiKey
}
newCore := &Core{
Name: c.Name,
Description: c.Description,

View File

@ -22,6 +22,20 @@ func Samples() error {
for i := int64(1); i <= 4; i++ {
sg.Add(1)
f1 := &Failure{
Service: i,
Issue: "Server failure",
CreatedAt: utils.Now().Add(-time.Duration(3*i) * 86400),
}
f1.Create()
f2 := &Failure{
Service: i,
Issue: "Server failure",
CreatedAt: utils.Now().Add(-time.Duration(5*i) * 12400),
}
f2.Create()
log.Infoln(fmt.Sprintf("Adding %v Failure records to service", 400))
go func() {

View File

@ -37,6 +37,7 @@ func All() map[int64]*Service {
func AllInOrder() []Service {
var services []Service
for _, service := range allServices {
service.UpdateStats()
services = append(services, *service)
}
sort.Sort(ServiceOrder(services))