mirror of https://github.com/statping/statping
db changes
parent
d9e47e6d3d
commit
c92f99a455
|
@ -4,20 +4,20 @@ about: If you're having an issue or see an error
|
|||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
### Describe the bug
|
||||
Try to explain what issue your'e having in detail. You can copy and paste the issue from the log file in your root directory `/logs/statup.log`.
|
||||
|
||||
**To Reproduce**
|
||||
### To Reproduce
|
||||
Steps to reproduce the behavior:
|
||||
1. I'm using version: '...'
|
||||
2. I went to '....'
|
||||
3. Press this button '....'
|
||||
4. And things did '....'
|
||||
|
||||
**Expected Behavior**
|
||||
### Expected Behavior
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
### Screenshots
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/statup-app/general) [![GitHub release](https://img.shields.io/github/release/hunterlong/statup.svg)](https://github.com/hunterlong/statping/releases/latest) [![Build Status](https://travis-ci.com/hunterlong/statup.svg?branch=master)](https://travis-ci.com/hunterlong/statup)
|
||||
[![Slack](https://slack.statping.com/badge.svg)](https://slack.statping.com/) [![GitHub release](https://img.shields.io/github/release/hunterlong/statping.svg)](https://github.com/hunterlong/statping/releases/latest) [![Build Status](https://travis-ci.com/hunterlong/statping.svg?branch=master)](https://travis-ci.com/hunterlong/statping)
|
||||
|
|
|
@ -4,19 +4,19 @@ about: Suggest a feature and let's see what others say
|
|||
|
||||
---
|
||||
|
||||
**What would you like on Statping?**
|
||||
### What would you like on Statping?
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe the solution you'd like**
|
||||
### Describe the solution you'd like
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
### Describe alternatives you've considered
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
### Is your feature request related to a problem? Please describe.
|
||||
I'm always frustrated when [...]
|
||||
|
||||
**Additional context**
|
||||
### Additional context
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/statup-app/general) [![GitHub release](https://img.shields.io/github/release/hunterlong/statup.svg)](https://github.com/hunterlong/statping/releases/latest) [![Build Status](https://travis-ci.com/hunterlong/statup.svg?branch=master)](https://travis-ci.com/hunterlong/statup)
|
||||
[![Slack](https://slack.statping.com/badge.svg)](https://slack.statping.com/) [![GitHub release](https://img.shields.io/github/release/hunterlong/statping.svg)](https://github.com/hunterlong/statping/releases/latest) [![Build Status](https://travis-ci.com/hunterlong/statping.svg?branch=master)](https://travis-ci.com/hunterlong/statping)
|
||||
|
|
|
@ -4,20 +4,20 @@ about: If you're having an issue or see an error
|
|||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
### Describe the bug
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**To Reproduce**
|
||||
### To Reproduce
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
|
||||
**Expected behavior**
|
||||
### Expected behavior
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
### Screenshots
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
[![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/statup-app/general) [![GitHub release](https://img.shields.io/github/release/hunterlong/statup.svg)](https://github.com/hunterlong/statping/releases/latest) [![Build Status](https://travis-ci.com/hunterlong/statup.svg?branch=master)](https://travis-ci.com/hunterlong/statup)
|
||||
|
|
42
Dockerfile
42
Dockerfile
|
@ -1,16 +1,43 @@
|
|||
FROM hunterlong/statping:base as base
|
||||
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
|
||||
COPY ./frontend .
|
||||
RUN yarn build && rm -rf node_modules
|
||||
|
||||
FROM alpine:latest
|
||||
# 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)"
|
||||
COPY --from=base /usr/local/bin/sass /usr/local/bin/sass
|
||||
COPY --from=base /go/bin/statping /usr/local/bin/statping
|
||||
|
||||
ARG VERSION
|
||||
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/hunterlong/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
|
||||
COPY . .
|
||||
COPY --from=frontend /statping/dist ./source/
|
||||
RUN make clean generate embed build
|
||||
RUN chmod a+x statping && mv statping /go/bin/statping
|
||||
|
||||
RUN apk --no-cache add curl jq
|
||||
# Statping main Docker image that contains all required libraries
|
||||
FROM alpine:latest
|
||||
RUN apk --no-cache add libgcc libstdc++ curl jq
|
||||
|
||||
COPY --from=backend /go/bin/statping /usr/local/bin/
|
||||
COPY --from=backend /usr/local/bin/sass /usr/local/bin/
|
||||
COPY --from=backend /usr/local/share/ca-certificates /usr/local/share/
|
||||
|
||||
WORKDIR /app
|
||||
VOLUME /app
|
||||
|
||||
ENV IS_DOCKER=true
|
||||
ENV STATPING_DIR=/app
|
||||
|
@ -21,3 +48,4 @@ EXPOSE $PORT
|
|||
HEALTHCHECK --interval=60s --timeout=10s --retries=3 CMD curl -s "http://localhost:$PORT/health" | jq -r -e ".online==true"
|
||||
|
||||
CMD statping -port $PORT
|
||||
|
||||
|
|
11
Makefile
11
Makefile
|
@ -80,15 +80,20 @@ top:
|
|||
docker-compose -f docker-compose.yml -f dev/docker-compose.full.yml top
|
||||
|
||||
frontend-build:
|
||||
cd frontend && rm -rf dist && yarn build
|
||||
rm -rf source/dist && cp -r frontend/dist source/ && cp -r frontend/src/assets/scss source/dist/
|
||||
rm -rf source/dist && rm -rf frontend/dist
|
||||
cd frontend && yarn build
|
||||
cp -r frontend/dist source/ && cp -r frontend/src/assets/scss source/dist/
|
||||
cp -r source/tmpl/*.* source/dist/
|
||||
|
||||
# compile assets using SASS and Rice. compiles scss -> css, and run rice embed-go
|
||||
compile: generate frontend-build
|
||||
rm -f source/rice-box.go
|
||||
cd source && rice embed-go
|
||||
|
||||
build: clean
|
||||
embed:
|
||||
cd source && rice embed-go
|
||||
|
||||
build:
|
||||
$(GOBUILD) $(BUILDVERSION) -o $(BINARY_NAME) ./cmd
|
||||
|
||||
install: build
|
||||
|
|
|
@ -106,7 +106,7 @@ func catchCLI(args []string) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err = configs.ConnectConfigs(config); err != nil {
|
||||
if err = configs.ConnectConfigs(config, false); err != nil {
|
||||
return err
|
||||
}
|
||||
if data, err = handlers.ExportSettings(); err != nil {
|
||||
|
@ -206,7 +206,7 @@ func runOnce() error {
|
|||
if err != nil {
|
||||
return errors.Wrap(err, "config.yml file not found")
|
||||
}
|
||||
err = configs.ConnectConfigs(config)
|
||||
err = configs.ConnectConfigs(config, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "issue connecting to database")
|
||||
}
|
||||
|
|
22
cmd/main.go
22
cmd/main.go
|
@ -16,20 +16,21 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/hunterlong/statping/source"
|
||||
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/handlers"
|
||||
"github.com/hunterlong/statping/types/configs"
|
||||
"github.com/hunterlong/statping/types/core"
|
||||
"github.com/hunterlong/statping/types/services"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"flag"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/handlers"
|
||||
"github.com/hunterlong/statping/source"
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -113,7 +114,7 @@ func main() {
|
|||
}
|
||||
}
|
||||
|
||||
if err = configs.ConnectConfigs(c); err != nil {
|
||||
if err = configs.ConnectConfigs(c, true); err != nil {
|
||||
exit(err)
|
||||
}
|
||||
|
||||
|
@ -139,8 +140,9 @@ func SetupMode() error {
|
|||
// sigterm will attempt to close the database connections gracefully
|
||||
func sigterm() {
|
||||
sigs := make(chan os.Signal, 1)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL)
|
||||
signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigs
|
||||
fmt.Println("Shutting down Statping")
|
||||
Close()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
|
|
@ -497,6 +497,6 @@ func (it *Db) Between(t1 time.Time, t2 time.Time) Database {
|
|||
}
|
||||
|
||||
type TimeValue struct {
|
||||
Timeframe string `json:"timeframe"`
|
||||
Amount float64 `json:"amount"`
|
||||
Timeframe string `json:"timeframe"`
|
||||
Amount int64 `json:"amount"`
|
||||
}
|
||||
|
|
|
@ -68,37 +68,13 @@ func (t *TimeVar) ToValues() ([]*TimeValue, error) {
|
|||
return t.data, nil
|
||||
}
|
||||
|
||||
func (g *GroupQuery) toFloatRows() []*TimeValue {
|
||||
rows, err := g.db.Rows()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var data []*TimeValue
|
||||
for rows.Next() {
|
||||
var timeframe time.Time
|
||||
amount := float64(0)
|
||||
if err := rows.Scan(&timeframe, &amount); err != nil {
|
||||
log.Errorln(err)
|
||||
}
|
||||
|
||||
fmt.Println("float rows: ", timeframe, amount)
|
||||
|
||||
newTs := types.FixedTime(timeframe, g.Group)
|
||||
data = append(data, &TimeValue{
|
||||
Timeframe: newTs,
|
||||
Amount: amount,
|
||||
})
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// GraphData will return all hits or failures
|
||||
func (g *GroupQuery) GraphData(by By) ([]*TimeValue, error) {
|
||||
|
||||
dbQuery := g.db.MultipleSelects(
|
||||
g.db.SelectByTime(g.Group),
|
||||
by.String(),
|
||||
).Group("timeframe").Debug()
|
||||
).Group("timeframe")
|
||||
|
||||
g.db = dbQuery
|
||||
|
||||
|
@ -121,7 +97,7 @@ func (g *GroupQuery) ToTimeValue() (*TimeVar, error) {
|
|||
var data []*TimeValue
|
||||
for rows.Next() {
|
||||
var timeframe string
|
||||
amount := float64(0)
|
||||
amount := int64(0)
|
||||
if err := rows.Scan(&timeframe, &amount); err != nil {
|
||||
log.Error(err, timeframe)
|
||||
}
|
||||
|
@ -136,7 +112,7 @@ func (g *GroupQuery) ToTimeValue() (*TimeVar, error) {
|
|||
}
|
||||
|
||||
func (t *TimeVar) FillMissing(current, end time.Time) ([]*TimeValue, error) {
|
||||
timeMap := make(map[string]float64)
|
||||
timeMap := make(map[string]int64)
|
||||
var validSet []*TimeValue
|
||||
dur := t.g.Group
|
||||
for _, v := range t.data {
|
||||
|
@ -146,7 +122,7 @@ func (t *TimeVar) FillMissing(current, end time.Time) ([]*TimeValue, error) {
|
|||
currentStr := types.FixedTime(current, t.g.Group)
|
||||
|
||||
for {
|
||||
var amount float64
|
||||
var amount int64
|
||||
if timeMap[currentStr] != 0 {
|
||||
amount = timeMap[currentStr]
|
||||
}
|
||||
|
|
|
@ -543,6 +543,10 @@ input.inputTags-field:focus {
|
|||
min-height: 85pt;
|
||||
}
|
||||
|
||||
.list-group-item:HOVER {
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.index_container {
|
||||
min-height: 980pt;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,11 @@
|
|||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.lower_canvas {
|
||||
height: 4rem;
|
||||
margin-top: -1px;
|
||||
}
|
||||
|
||||
.list-group-item H5 {
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
@ -36,8 +41,8 @@
|
|||
}
|
||||
|
||||
.btn-sm {
|
||||
line-height: 0.9rem;
|
||||
font-size: 0.65rem;
|
||||
line-height: 1.4rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.full-col-12 {
|
||||
|
|
|
@ -126,17 +126,17 @@
|
|||
visible: function(newVal, oldVal) {
|
||||
if (newVal && !this.showing) {
|
||||
this.showing = true
|
||||
this.chartHits("1h")
|
||||
this.chartHits("2h")
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async chartHits(group) {
|
||||
const start = this.nowSubtract((3600 * 24) * 30)
|
||||
this.data = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(new Date()), group)
|
||||
const start = this.nowSubtract((3600 * 24) * 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("30m")
|
||||
await this.chartHits("1h")
|
||||
}
|
||||
this.series = [{
|
||||
name: this.service.name,
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
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)
|
||||
const fetched = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(this.now()), group, false)
|
||||
if (!data) {
|
||||
return [{name: "None", data: []}]
|
||||
}
|
||||
|
|
|
@ -170,9 +170,7 @@
|
|||
return
|
||||
}
|
||||
|
||||
const auth = await Api.login(s.username, s.password)
|
||||
this.auth = Api.saveToken(s.username, auth.token)
|
||||
await this.$store.dispatch('loadAdmin')
|
||||
await this.$store.dispatch('loadRequired')
|
||||
|
||||
this.loading = false
|
||||
this.$router.push('/')
|
||||
|
|
|
@ -247,10 +247,10 @@ export default {
|
|||
this.failures = await Api.service_failures(this.service.id, this.now() - 3600, this.now(), 15)
|
||||
},
|
||||
async chartHits() {
|
||||
const start = this.nowSubtract((3600 * 24) * 7)
|
||||
const start = this.nowSubtract((3600 * 24) * 3)
|
||||
this.start_time = start
|
||||
this.end_time = new Date()
|
||||
this.data = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(new Date()), "1h")
|
||||
this.data = await Api.service_hits(this.service.id, this.toUnix(start), this.toUnix(new Date()), "15m", false)
|
||||
this.series = [{
|
||||
name: this.service.name,
|
||||
...this.data
|
||||
|
|
1
go.mod
1
go.mod
|
@ -8,7 +8,6 @@ require (
|
|||
github.com/Microsoft/go-winio v0.4.14 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
|
||||
github.com/ararog/timeago v0.0.0-20160328174124-e9969cf18b8d
|
||||
github.com/crazy-max/xgo v0.3.2 // indirect
|
||||
github.com/daaku/go.zipexe v1.0.1 // indirect
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/docker/distribution v2.7.1+incompatible // indirect
|
||||
|
|
8
go.sum
8
go.sum
|
@ -1,10 +1,12 @@
|
|||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/GeertJohan/go.incremental v1.0.0 h1:7AH+pY1XUgQE4Y1HcXYaMqAI0m9yrFqo/jt0CW30vsg=
|
||||
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
||||
github.com/GeertJohan/go.rice v1.0.0 h1:KkI6O9uMaQU3VEKaj01ulavtF7o1fWT7+pk/4voiMLQ=
|
||||
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
|
||||
github.com/Microsoft/go-winio v0.4.14 h1:+hMXMk01us9KgxGb7ftKQt2Xpf5hH/yky+TDA+qxleU=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
github.com/akavel/rsrc v0.8.0 h1:zjWn7ukO9Kc5Q62DOJCcxGpXC18RawVtYAGdz2aLlfw=
|
||||
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
|
@ -18,8 +20,6 @@ github.com/ararog/timeago v0.0.0-20160328174124-e9969cf18b8d h1:ZX0t+GA3MWiP7LWt
|
|||
github.com/ararog/timeago v0.0.0-20160328174124-e9969cf18b8d/go.mod h1:EcJ034SpbWy4heOSDiBZJRn3b5wKJM1b4sFfYeVAkI4=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/crazy-max/xgo v0.3.2 h1:A5KmtgpPVzzqJzYcEmzpM0zFgCCyUar1FSvHFXxLiWA=
|
||||
github.com/crazy-max/xgo v0.3.2/go.mod h1:9sA9qZWLR9koBkgNTcktix7/MVbYrLWNnIQvYYq6/DA=
|
||||
github.com/daaku/go.zipexe v1.0.0 h1:VSOgZtH418pH9L16hC/JrgSNJbbAL26pj7lmD1+CGdY=
|
||||
github.com/daaku/go.zipexe v1.0.0/go.mod h1:z8IiR6TsVLEYKwXAoE/I+8ys/sDkgTzSL0CLnGVd57E=
|
||||
github.com/daaku/go.zipexe v1.0.1 h1:wV4zMsDOI2SZ2m7Tdz1Ps96Zrx+TzaK15VbUaGozw0M=
|
||||
|
@ -64,6 +64,7 @@ github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPg
|
|||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/gorilla/mux v1.7.4 h1:VuZ8uybHlWmqV03+zRzdwKL4tUnIp1MAQtp1mIFE1bc=
|
||||
github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q=
|
||||
github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs=
|
||||
|
@ -92,6 +93,7 @@ github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229 h1:E2B8qYyeSgv5MXpmzZXRNp8IAQ4vjxIjhpAf5hv/tAg=
|
||||
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
|
||||
github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
|
||||
|
@ -130,7 +132,9 @@ github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H
|
|||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e h1:nt2877sKfojlHCTOBXbpWjBkuWKritFaGIfgQwbQUls=
|
||||
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e/go.mod h1:B4+Kq1u5FlULTjFSM707Q6e/cOHFv0z/6QRoxubDIQ8=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||
github.com/valyala/fasttemplate v1.0.1 h1:tY9CJiPnMXf1ERmG2EyK7gNUd+c6RKGD0IfU8WdUSz8=
|
||||
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
|
|
|
@ -62,11 +62,12 @@ func reorderServiceHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
func apiServiceHandler(r *http.Request) interface{} {
|
||||
service, err := serviceByID(r)
|
||||
srv, err := serviceByID(r)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return *service
|
||||
srv = srv.UpdateStats()
|
||||
return *srv
|
||||
}
|
||||
|
||||
func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
@ -192,15 +193,6 @@ func apiAllServicesHandler(r *http.Request) interface{} {
|
|||
return services.AllInOrder()
|
||||
}
|
||||
|
||||
func joinServices(srvss map[int64]*services.Service) []*services.Service {
|
||||
var srvs []*services.Service
|
||||
for _, v := range srvss {
|
||||
v.UpdateStats()
|
||||
srvs = append(srvs, v)
|
||||
}
|
||||
return srvs
|
||||
}
|
||||
|
||||
func servicesDeleteFailuresHandler(w http.ResponseWriter, r *http.Request) {
|
||||
service, err := serviceByID(r)
|
||||
if err != nil {
|
||||
|
|
|
@ -20,10 +20,8 @@ import (
|
|||
"github.com/hunterlong/statping/types/configs"
|
||||
"github.com/hunterlong/statping/types/core"
|
||||
"github.com/hunterlong/statping/types/null"
|
||||
"github.com/hunterlong/statping/types/users"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -33,59 +31,21 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
sendErrorJson(errors.New("Statping has already been setup"), w, r)
|
||||
return
|
||||
}
|
||||
if err = r.ParseForm(); err != nil {
|
||||
log.Errorln(err)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
dbHost := r.PostForm.Get("db_host")
|
||||
dbUser := r.PostForm.Get("db_user")
|
||||
dbPass := r.PostForm.Get("db_password")
|
||||
dbDatabase := r.PostForm.Get("db_database")
|
||||
dbConn := r.PostForm.Get("db_connection")
|
||||
dbPort := utils.ToInt(r.PostForm.Get("db_port"))
|
||||
project := r.PostForm.Get("project")
|
||||
username := r.PostForm.Get("username")
|
||||
password := r.PostForm.Get("password")
|
||||
description := r.PostForm.Get("description")
|
||||
domain := r.PostForm.Get("domain")
|
||||
email := r.PostForm.Get("email")
|
||||
sample, _ := strconv.ParseBool(r.PostForm.Get("sample_data"))
|
||||
|
||||
confg := &configs.DbConfig{
|
||||
DbConn: dbConn,
|
||||
DbHost: dbHost,
|
||||
DbUser: dbUser,
|
||||
DbPass: dbPass,
|
||||
DbData: dbDatabase,
|
||||
DbPort: int(dbPort),
|
||||
Project: project,
|
||||
Description: description,
|
||||
Domain: domain,
|
||||
Username: username,
|
||||
Password: password,
|
||||
Email: email,
|
||||
Error: nil,
|
||||
Location: utils.Directory,
|
||||
}
|
||||
|
||||
log.WithFields(utils.ToFields(core.App, confg)).Debugln("new configs posted")
|
||||
|
||||
if err := confg.Save(utils.Directory); err != nil {
|
||||
confgs, err := configs.LoadConfigForm(r)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if confg, err = configs.LoadConfigFile(utils.Directory); err != nil {
|
||||
log.Errorln(err)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
//sample, _ := strconv.ParseBool(r.PostForm.Get("sample_data"))
|
||||
|
||||
if err = configs.ConnectConfigs(confg); err != nil {
|
||||
log.WithFields(utils.ToFields(core.App, confgs)).Debugln("new configs posted")
|
||||
|
||||
if err = configs.ConnectConfigs(confgs, true); err != nil {
|
||||
log.Errorln(err)
|
||||
if err := confg.Delete(); err != nil {
|
||||
if err := confgs.Delete(); err != nil {
|
||||
log.Errorln(err)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
|
@ -94,7 +54,13 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if err = confg.MigrateDatabase(); err != nil {
|
||||
if err := confgs.Save(utils.Directory); err != nil {
|
||||
log.Errorln(err)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if err = confgs.MigrateDatabase(); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
@ -111,6 +77,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
Footer: null.NewNullString(""),
|
||||
}
|
||||
|
||||
log.Infoln("Creating new Core")
|
||||
if err := c.Create(); err != nil {
|
||||
log.Errorln(err)
|
||||
sendErrorJson(err, w, r)
|
||||
|
@ -119,27 +86,22 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
core.App = c
|
||||
|
||||
admin := &users.User{
|
||||
Username: confg.Username,
|
||||
Password: confg.Password,
|
||||
Email: confg.Email,
|
||||
Admin: null.NewNullBool(true),
|
||||
}
|
||||
//if sample {
|
||||
// log.Infoln("Adding sample data into new database")
|
||||
// if err = configs.TriggerSamples(); err != nil {
|
||||
// log.Errorln(err)
|
||||
// sendErrorJson(err, w, r)
|
||||
// return
|
||||
// }
|
||||
//}
|
||||
|
||||
if err := admin.Create(); err != nil {
|
||||
log.Infoln("Initializing new Statping instance")
|
||||
if err := core.InitApp(); err != nil {
|
||||
log.Errorln(err)
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
if sample {
|
||||
if err = configs.TriggerSamples(); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
core.InitApp()
|
||||
CacheStorage.Delete("/")
|
||||
resetCookies()
|
||||
time.Sleep(1 * time.Second)
|
||||
|
@ -148,11 +110,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
Config *configs.DbConfig `json:"config"`
|
||||
}{
|
||||
"success",
|
||||
confg,
|
||||
confgs,
|
||||
}
|
||||
returnJson(out, w, r)
|
||||
}
|
||||
|
||||
func setupResponseError(w http.ResponseWriter, r *http.Request, a interface{}) {
|
||||
ExecuteResponse(w, r, "setup.gohtml", a, nil)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package configs
|
||||
|
||||
import (
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"github.com/pkg/errors"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func LoadConfigForm(r *http.Request) (*DbConfig, error) {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbHost := r.PostForm.Get("db_host")
|
||||
dbUser := r.PostForm.Get("db_user")
|
||||
dbPass := r.PostForm.Get("db_password")
|
||||
dbDatabase := r.PostForm.Get("db_database")
|
||||
dbConn := r.PostForm.Get("db_connection")
|
||||
dbPort := utils.ToInt(r.PostForm.Get("db_port"))
|
||||
project := r.PostForm.Get("project")
|
||||
username := r.PostForm.Get("username")
|
||||
password := r.PostForm.Get("password")
|
||||
description := r.PostForm.Get("description")
|
||||
domain := r.PostForm.Get("domain")
|
||||
email := r.PostForm.Get("email")
|
||||
|
||||
if project == "" || username == "" || password == "" {
|
||||
err := errors.New("Missing required elements on setup form")
|
||||
return nil, err
|
||||
}
|
||||
|
||||
confg := &DbConfig{
|
||||
DbConn: dbConn,
|
||||
DbHost: dbHost,
|
||||
DbUser: dbUser,
|
||||
DbPass: dbPass,
|
||||
DbData: dbDatabase,
|
||||
DbPort: int(dbPort),
|
||||
Project: project,
|
||||
Description: description,
|
||||
Domain: domain,
|
||||
Username: username,
|
||||
Password: password,
|
||||
Email: email,
|
||||
Error: nil,
|
||||
Location: utils.Directory,
|
||||
}
|
||||
|
||||
return confg, nil
|
||||
|
||||
}
|
|
@ -19,7 +19,7 @@ func TestDbConfig_Save(t *testing.T) {
|
|||
Location: utils.Directory,
|
||||
}
|
||||
|
||||
err := ConnectConfigs(config)
|
||||
err := ConnectConfigs(config, true)
|
||||
require.Nil(t, err)
|
||||
|
||||
err = config.Save(utils.Directory)
|
||||
|
|
|
@ -99,15 +99,18 @@ func InitialSetup(configs *DbConfig) error {
|
|||
return errors.Wrap(err, "error creating database")
|
||||
}
|
||||
|
||||
username := utils.Getenv("ADMIN_USER", "admin").(string)
|
||||
password := utils.Getenv("ADMIN_PASSWORD", "admin").(string)
|
||||
if configs.Username == "" && configs.Password == "" {
|
||||
configs.Username = utils.Getenv("ADMIN_USER", "admin").(string)
|
||||
configs.Password = utils.Getenv("ADMIN_PASSWORD", "admin").(string)
|
||||
}
|
||||
|
||||
admin := &users.User{
|
||||
Username: username,
|
||||
Password: password,
|
||||
Username: configs.Username,
|
||||
Password: configs.Password,
|
||||
Email: "info@admin.com",
|
||||
Admin: null.NewNullBool(true),
|
||||
}
|
||||
|
||||
if err := admin.Create(); err != nil {
|
||||
return errors.Wrap(err, "error creating admin")
|
||||
}
|
||||
|
|
|
@ -11,7 +11,7 @@ import (
|
|||
|
||||
var log = utils.Log
|
||||
|
||||
func ConnectConfigs(configs *DbConfig) error {
|
||||
func ConnectConfigs(configs *DbConfig, initiate bool) error {
|
||||
err := Connect(configs, true)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "error connecting to database")
|
||||
|
@ -21,7 +21,7 @@ func ConnectConfigs(configs *DbConfig) error {
|
|||
}
|
||||
|
||||
exists := database.DB().HasTable("core")
|
||||
if !exists {
|
||||
if !exists && initiate {
|
||||
return InitialSetup(configs)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -3,6 +3,7 @@ package failures
|
|||
import (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"github.com/prometheus/common/log"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -16,7 +17,7 @@ func Samples() {
|
|||
tx := DB().Begin()
|
||||
sg := new(sync.WaitGroup)
|
||||
|
||||
createdAt := time.Now().Add(-1 * types.Month)
|
||||
createdAt := utils.Now().Add(-3 * types.Day)
|
||||
|
||||
for i := int64(1); i <= 4; i++ {
|
||||
sg.Add(1)
|
||||
|
@ -25,8 +26,8 @@ func Samples() {
|
|||
|
||||
go func() {
|
||||
defer sg.Done()
|
||||
for fi := 0.; fi <= float64(730); fi++ {
|
||||
createdAt = createdAt.Add(2 * time.Minute)
|
||||
for fi := 0.; fi <= float64(400); fi++ {
|
||||
createdAt = createdAt.Add(35 * time.Minute)
|
||||
failure := &Failure{
|
||||
Service: i,
|
||||
Issue: "testing right here",
|
||||
|
|
|
@ -63,6 +63,15 @@ func (h Hitters) Sum() float64 {
|
|||
return result.amount
|
||||
}
|
||||
|
||||
func (h Hitters) Avg() int64 {
|
||||
result := struct {
|
||||
amount int64
|
||||
}{0}
|
||||
|
||||
h.db.Select("AVG(latency) as amount").Scan(&result)
|
||||
return result.amount
|
||||
}
|
||||
|
||||
func AllHits(obj ColumnIDInterfacer) Hitters {
|
||||
column, id := obj.HitsColumnID()
|
||||
return Hitters{DB().Where(fmt.Sprintf("%s = ?", column), id)}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package hits
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
|
@ -13,13 +12,13 @@ import (
|
|||
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
||||
)
|
||||
|
||||
var SampleHits = 9900.
|
||||
var SampleHits = 99900.
|
||||
|
||||
func Samples() {
|
||||
tx := DB().Begin()
|
||||
sg := new(sync.WaitGroup)
|
||||
|
||||
for i := int64(1); i <= 4; i++ {
|
||||
for i := int64(1); i <= 5; i++ {
|
||||
err := createHitsAt(tx, i, sg)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
|
@ -30,14 +29,14 @@ func Samples() {
|
|||
}
|
||||
|
||||
func createHitsAt(db database.Database, serviceID int64, sg *sync.WaitGroup) error {
|
||||
createdAt := utils.Now().Add(-30 * types.Day)
|
||||
createdAt := utils.Now().Add(-3 * types.Day)
|
||||
p := utils.NewPerlin(2, 2, 5, utils.Now().UnixNano())
|
||||
|
||||
i := 0
|
||||
for hi := 0.; hi <= SampleHits; hi++ {
|
||||
latency := p.Noise1D(hi / 500)
|
||||
|
||||
createdAt = createdAt.Add(10 * time.Minute)
|
||||
createdAt = createdAt.Add(30 * time.Second)
|
||||
|
||||
hit := &Hit{
|
||||
Service: serviceID,
|
||||
|
@ -47,10 +46,11 @@ func createHitsAt(db database.Database, serviceID int64, sg *sync.WaitGroup) err
|
|||
}
|
||||
|
||||
db = db.Create(&hit)
|
||||
fmt.Printf("Creating hit %d hit %d: %.2f %v\n", serviceID, hit.Id, latency, createdAt.String())
|
||||
i++
|
||||
if createdAt.After(utils.Now()) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return db.Commit().Error()
|
||||
|
||||
}
|
||||
|
|
|
@ -133,32 +133,35 @@ func niceDomainName(domain string, paths string) string {
|
|||
return domain + strings.Join(addedName, "/")
|
||||
}
|
||||
|
||||
func (s *Service) UpdateStats() {
|
||||
func (s *Service) UpdateStats() *Service {
|
||||
s.Online24Hours = s.OnlineDaysPercent(1)
|
||||
s.Online7Days = s.OnlineDaysPercent(7)
|
||||
s.AvgResponse = s.AvgTime()
|
||||
s.FailuresLast24Hours = len(s.AllFailures().Since(time.Now().UTC().Add(-time.Hour * 24)))
|
||||
s.FailuresLast24Hours = len(s.AllFailures().Since(utils.Now().Add(-time.Hour * 24)))
|
||||
|
||||
if s.LastOffline.IsZero() {
|
||||
lastFail := s.LastFailure()
|
||||
if lastFail != nil {
|
||||
s.LastOffline = s.LastFailure().CreatedAt
|
||||
}
|
||||
}
|
||||
|
||||
s.Stats = &Stats{
|
||||
Failures: s.AllFailures().Count(),
|
||||
Hits: s.AllHits().Count(),
|
||||
FirstHit: s.FirstHit().CreatedAt,
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// AvgTime will return the average amount of time for a service to response back successfully
|
||||
func (s *Service) AvgTime() float64 {
|
||||
sum := s.AllHits().Sum()
|
||||
return sum
|
||||
}
|
||||
|
||||
// AvgUptime will return the average amount of time for a service to response back successfully
|
||||
func (s *Service) AvgUptime(since time.Time) float64 {
|
||||
sum := s.AllHits().Sum()
|
||||
return sum
|
||||
func (s *Service) AvgTime() int64 {
|
||||
return s.AllHits().Avg()
|
||||
}
|
||||
|
||||
// OnlineDaysPercent returns the service's uptime percent within last 24 hours
|
||||
func (s *Service) OnlineDaysPercent(days int) float32 {
|
||||
ago := time.Now().UTC().Add((-24 * time.Duration(days)) * time.Hour)
|
||||
ago := utils.Now().Add((-24 * time.Duration(days)) * time.Hour)
|
||||
return s.OnlineSince(ago)
|
||||
}
|
||||
|
||||
|
|
|
@ -45,7 +45,7 @@ type Service struct {
|
|||
PingTime float64 `gorm:"-" json:"ping_time"`
|
||||
Online24Hours float32 `gorm:"-" json:"online_24_hours"`
|
||||
Online7Days float32 `gorm:"-" json:"online_7_days"`
|
||||
AvgResponse float64 `gorm:"-" json:"avg_response"`
|
||||
AvgResponse int64 `gorm:"-" json:"avg_response"`
|
||||
FailuresLast24Hours int `gorm:"-" json:"failures_24_hours"`
|
||||
Running chan bool `gorm:"-" json:"-"`
|
||||
Checkpoint time.Time `gorm:"-" json:"-"`
|
||||
|
@ -57,22 +57,25 @@ type Service struct {
|
|||
DownText string `gorm:"-" json:"-"` // Contains the current generated Downtime Text
|
||||
SuccessNotified bool `gorm:"-" json:"-"` // Is 'true' if the user has already be informed that the Services now again available
|
||||
LastStatusCode int `gorm:"-" json:"status_code"`
|
||||
LastOnline time.Time `gorm:"-" json:"last_success"`
|
||||
LastOffline time.Time `gorm:"-" json:"last_error"`
|
||||
Failures []*failures.Failure `gorm:"-" json:"failures,omitempty" scope:"user,admin"`
|
||||
AllCheckins []*checkins.Checkin `gorm:"-" json:"checkins,omitempty" scope:"user,admin"`
|
||||
Stats *Stats `gorm:"-" json:"stats,omitempty"`
|
||||
LastLookupTime int64 `gorm:"-" json:"-"`
|
||||
LastLatency int64 `gorm:"-" json:"-"`
|
||||
LastCheck time.Time `gorm:"-" json:"-"`
|
||||
LastOnline time.Time `gorm:"-" json:"last_success"`
|
||||
LastOffline time.Time `gorm:"-" json:"last_error"`
|
||||
Stats *Stats `gorm:"-" json:"stats,omitempty"`
|
||||
|
||||
SecondsOnline int64 `gorm:"-" json:"-"`
|
||||
SecondsOffline int64 `gorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
type Stats struct {
|
||||
Failures int `gorm:"-" json:"failures"`
|
||||
Hits int `gorm:"-" json:"hits"`
|
||||
Failures int `gorm:"-" json:"failures"`
|
||||
Hits int `gorm:"-" json:"hits"`
|
||||
LastLookupTime int64 `gorm:"-" json:"last_lookup"`
|
||||
LastLatency int64 `gorm:"-" json:"last_latency"`
|
||||
FirstHit time.Time `gorm:"-" json:"first_hit"`
|
||||
}
|
||||
|
||||
// BeforeCreate for Service will set CreatedAt to UTC
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -38,31 +37,29 @@ var (
|
|||
)
|
||||
|
||||
func FixedTime(t time.Time, d time.Duration) string {
|
||||
switch d {
|
||||
case Month:
|
||||
month := fmt.Sprintf("%v", int(t.Month()))
|
||||
if int(t.Month()) < 10 {
|
||||
month = fmt.Sprintf("0%v", int(t.Month()))
|
||||
}
|
||||
return fmt.Sprintf("%v-%v-01T00:00:00Z", t.Year(), month)
|
||||
case Year:
|
||||
return fmt.Sprintf("%v-01-01T00:00:00Z", t.Year())
|
||||
default:
|
||||
return t.Format(durationStr(d))
|
||||
}
|
||||
return t.Format(durationStr(d))
|
||||
}
|
||||
|
||||
func durationStr(d time.Duration) string {
|
||||
switch d {
|
||||
case Second:
|
||||
return "2006-01-02T15:04:05Z"
|
||||
case Minute:
|
||||
return "2006-01-02T15:04:00Z"
|
||||
case Hour:
|
||||
|
||||
switch m := d.Seconds(); {
|
||||
|
||||
case m >= Month.Seconds():
|
||||
return "2006-01-01T00:00:00Z"
|
||||
|
||||
case m >= Week.Seconds():
|
||||
return "2006-01-02T00:00:00Z"
|
||||
|
||||
case m >= Day.Seconds():
|
||||
return "2006-01-02T15:00:00Z"
|
||||
case Day:
|
||||
return "2006-01-02T00:00:00Z"
|
||||
|
||||
case m >= Hour.Seconds():
|
||||
return "2006-01-02T15:04:00Z"
|
||||
|
||||
case m >= Minute.Seconds():
|
||||
return "2006-01-02T15:04:00Z"
|
||||
|
||||
default:
|
||||
return "2006-01-02T00:00:00Z"
|
||||
return "2006-01-02T15:04:05Z"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package users
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/hunterlong/statping/database"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"github.com/prometheus/common/log"
|
||||
|
@ -32,13 +31,11 @@ func All() []*User {
|
|||
|
||||
func (u *User) Create() error {
|
||||
u.CreatedAt = time.Now().UTC()
|
||||
if u.Password == "" {
|
||||
return errors.New("did not supply user password")
|
||||
}
|
||||
u.Password = utils.HashPassword(u.Password)
|
||||
u.ApiKey = utils.NewSHA1Hash(16)
|
||||
u.ApiSecret = utils.NewSHA1Hash(16)
|
||||
|
||||
if u.ApiKey == "" || u.ApiSecret == "" {
|
||||
u.ApiKey = utils.NewSHA1Hash(16)
|
||||
u.ApiSecret = utils.NewSHA1Hash(16)
|
||||
}
|
||||
db := DB().Create(u)
|
||||
if db.Error() == nil {
|
||||
log.Warnf("User #%d (%s) has been created", u.Id, u.Username)
|
||||
|
|
Loading…
Reference in New Issue