mirror of https://github.com/statping/statping
building
parent
e0288b21a4
commit
de6cf2407f
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
|
@ -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
|
||||
|
|
|
@ -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/>
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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))
|
||||
|
|
Loading…
Reference in New Issue