mirror of https://github.com/statping/statping
parent
11538c38e3
commit
f331e9bbf9
|
@ -1,21 +0,0 @@
|
||||||
on: [push, pull_request]
|
|
||||||
name: Golang Test
|
|
||||||
jobs:
|
|
||||||
test:
|
|
||||||
env:
|
|
||||||
GOPATH: ${{ github.workspace }}
|
|
||||||
GO111MODULE: on
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Install Go
|
|
||||||
uses: actions/setup-go@v1
|
|
||||||
with:
|
|
||||||
go-version: 1.14.x
|
|
||||||
- name: Checkout code
|
|
||||||
uses: actions/checkout@v2
|
|
||||||
with:
|
|
||||||
path: ./src/github.com/${{ github.repository }}
|
|
||||||
- name: Go Mod
|
|
||||||
run: go mod download
|
|
||||||
- name: Test
|
|
||||||
run: go test -p=1 ./...
|
|
10
CHANGELOG.md
10
CHANGELOG.md
|
@ -1,3 +1,11 @@
|
||||||
|
# 0.90.16
|
||||||
|
- Added Notify After (int) field for Services. Will send notifications after x amount of failures.
|
||||||
|
- Added new method in utils package for replacing `{{.Service.*}}` and `{{.Failure.*}}` variables from string to it's true value
|
||||||
|
- Fixed Notifer get endpoint
|
||||||
|
- Cleaned Notifier methods
|
||||||
|
- Updated recommended changes from [sonarcloud.io](https://sonarcloud.io/organizations/statping/projects)
|
||||||
|
- Organized utils package files
|
||||||
|
|
||||||
# 0.90.15
|
# 0.90.15
|
||||||
- Fixed /dashboard authentication state to show admin tabs if your an admin. [Issue #438](https://github.com/statping/statping/issues/438)
|
- Fixed /dashboard authentication state to show admin tabs if your an admin. [Issue #438](https://github.com/statping/statping/issues/438)
|
||||||
- Fixed Cache JS error on Dashboard
|
- Fixed Cache JS error on Dashboard
|
||||||
|
@ -16,4 +24,4 @@
|
||||||
- Fixed version from not being added into Core table. [Issue #436](https://github.com/statping/statping/issues/436)
|
- Fixed version from not being added into Core table. [Issue #436](https://github.com/statping/statping/issues/436)
|
||||||
|
|
||||||
# 0.90.12
|
# 0.90.12
|
||||||
- Fixed MySQL timestamp formatting. (issue #432)
|
- Fixed MySQL timestamp formatting. (issue #432)
|
||||||
|
|
|
@ -469,7 +469,6 @@ HTML,BODY {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
font: inherit;
|
font: inherit;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
font-family: inherit;
|
|
||||||
display: block;
|
display: block;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
@ -485,7 +484,6 @@ HTML,BODY {
|
||||||
/* Code Mirror Settings */
|
/* Code Mirror Settings */
|
||||||
font-family: monospace;
|
font-family: monospace;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: hidden;
|
|
||||||
height:80vh;
|
height:80vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -138,7 +138,7 @@
|
||||||
name: c.name, description: c.description, domain: c.domain,
|
name: c.name, description: c.description, domain: c.domain,
|
||||||
timezone: c.timezone, using_cdn: c.using_cdn, footer: c.footer, update_notify: c.update_notify,
|
timezone: c.timezone, using_cdn: c.using_cdn, footer: c.footer, update_notify: c.update_notify,
|
||||||
gh_client_id: c.github_clientId, gh_client_secret: c.github_clientSecret
|
gh_client_id: c.github_clientId, gh_client_secret: c.github_clientSecret
|
||||||
}
|
};
|
||||||
await Api.core_save(coreForm)
|
await Api.core_save(coreForm)
|
||||||
const core = await Api.core()
|
const core = await Api.core()
|
||||||
this.$store.commit('setCore', core)
|
this.$store.commit('setCore', core)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<template>
|
<template>
|
||||||
<form @submit.prevent="login">
|
<form @submit.prevent="login" autocomplete="on">
|
||||||
<div class="form-group row">
|
<div class="form-group row">
|
||||||
<label for="username" class="col-sm-2 col-form-label">Username</label>
|
<label for="username" class="col-sm-2 col-form-label">Username</label>
|
||||||
<div class="col-sm-10">
|
<div class="col-sm-10">
|
||||||
|
|
|
@ -47,7 +47,7 @@
|
||||||
|
|
||||||
<div class="col-12 col-sm-12 mt-3">
|
<div class="col-12 col-sm-12 mt-3">
|
||||||
<button @click.prevent="testNotifier" class="btn btn-secondary btn-block text-capitalize col-12 float-right"><i class="fa fa-vial"></i>
|
<button @click.prevent="testNotifier" class="btn btn-secondary btn-block text-capitalize col-12 float-right"><i class="fa fa-vial"></i>
|
||||||
{{loading ? "Loading..." : "Test Notifier"}}</button>
|
{{loadingTest ? "Loading..." : "Test Notifier"}}</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -74,6 +74,7 @@ export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
|
loadingTest: false,
|
||||||
error: null,
|
error: null,
|
||||||
saved: false,
|
saved: false,
|
||||||
ok: false,
|
ok: false,
|
||||||
|
@ -104,10 +105,11 @@ export default {
|
||||||
},
|
},
|
||||||
async testNotifier() {
|
async testNotifier() {
|
||||||
this.ok = false
|
this.ok = false
|
||||||
this.loading = true
|
this.loadingTest = true
|
||||||
let form = {}
|
let form = {}
|
||||||
this.notifier.form.forEach((f) => {
|
this.notifier.form.forEach((f) => {
|
||||||
form[f.field] = this.notifier[f.field]
|
let field = f.field.toLowerCase()
|
||||||
|
this.form[field] = this.notifier[field]
|
||||||
});
|
});
|
||||||
form.enabled = this.notifier.enabled
|
form.enabled = this.notifier.enabled
|
||||||
form.limits = parseInt(this.notifier.limits)
|
form.limits = parseInt(this.notifier.limits)
|
||||||
|
@ -118,7 +120,7 @@ export default {
|
||||||
} else {
|
} else {
|
||||||
this.error = tested
|
this.error = tested
|
||||||
}
|
}
|
||||||
this.loading = false
|
this.loadingTest = false
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,6 +139,13 @@
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div v-if="service.allow_notifications" class="form-group row">
|
||||||
|
<label class="col-sm-4 col-form-label">Notify After Failures</label>
|
||||||
|
<div class="col-sm-8">
|
||||||
|
<input v-model="service.notify_after" type="number" name="notify_after" class="form-control" id="notify_after" autocapitalize="none">
|
||||||
|
<small class="form-text text-muted">Send Notification after {{service.notify_after === 0 ? 'the first Failure' : service.notify_after+' Failures'}} </small>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div v-if="service.allow_notifications" class="form-group row">
|
<div v-if="service.allow_notifications" class="form-group row">
|
||||||
<label class="col-sm-4 col-form-label">Notify All Changes</label>
|
<label class="col-sm-4 col-form-label">Notify All Changes</label>
|
||||||
<div class="col-8 mt-1">
|
<div class="col-8 mt-1">
|
||||||
|
@ -196,6 +203,7 @@
|
||||||
verify_ssl: true,
|
verify_ssl: true,
|
||||||
allow_notifications: true,
|
allow_notifications: true,
|
||||||
notify_all_changes: true,
|
notify_all_changes: true,
|
||||||
|
notify_after: 2,
|
||||||
public: true,
|
public: true,
|
||||||
},
|
},
|
||||||
groups: [],
|
groups: [],
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="container col-md-7 col-sm-12 mt-2 sm-container">
|
<div class="container col-md-7 col-sm-12 mt-2 sm-container">
|
||||||
<div class="col-12 col-md-8 offset-md-2 mb-4">
|
<div class="col-12 col-md-8 offset-md-2 mb-4">
|
||||||
<img class="col-12 mt-5 mt-md-0" style="max-width:680px" src="/banner.png">
|
<img alt="Statping Setup" class="col-12 mt-5 mt-md-0" style="max-width:680px" src="banner.png">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
<div class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
<div class="container col-md-7 col-sm-12 mt-md-5 bg-light">
|
||||||
<div class="col-10 offset-1 col-md-8 offset-md-2 mt-md-2">
|
<div class="col-10 offset-1 col-md-8 offset-md-2 mt-md-2">
|
||||||
<div class="col-12 col-md-8 offset-md-2 mb-4">
|
<div class="col-12 col-md-8 offset-md-2 mb-4">
|
||||||
<img class="col-12 mt-5 mt-md-0" style="max-width:680px" src="/banner.png">
|
<img alt="Statping Login" class="col-12 mt-5 mt-md-0" style="max-width:680px" src="banner.png">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<FormLogin/>
|
<FormLogin/>
|
||||||
|
|
|
@ -169,7 +169,7 @@ func sendJsonAction(obj interface{}, method string, w http.ResponseWriter, r *ht
|
||||||
objName = "incident_update"
|
objName = "incident_update"
|
||||||
objId = v.Id
|
objId = v.Id
|
||||||
default:
|
default:
|
||||||
objName = "missing"
|
objName = fmt.Sprintf("%T", v)
|
||||||
}
|
}
|
||||||
|
|
||||||
output := apiResponse{
|
output := apiResponse{
|
||||||
|
|
|
@ -17,6 +17,7 @@ package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/statping/statping/types/notifications"
|
"github.com/statping/statping/types/notifications"
|
||||||
|
@ -28,21 +29,21 @@ import (
|
||||||
|
|
||||||
func apiNotifiersHandler(w http.ResponseWriter, r *http.Request) {
|
func apiNotifiersHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
notifiers := services.AllNotifiers()
|
notifiers := services.AllNotifiers()
|
||||||
var notifs []*notifications.Notification
|
returnJson(notifiers, w, r)
|
||||||
for _, n := range notifiers {
|
|
||||||
notifs = append(notifs, notifications.SelectNotifier(n.Select()))
|
|
||||||
}
|
|
||||||
returnJson(notifs, w, r)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiNotifierGetHandler(w http.ResponseWriter, r *http.Request) {
|
func apiNotifierGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
vars := mux.Vars(r)
|
vars := mux.Vars(r)
|
||||||
notifier, err := notifications.Find(vars["notifier"])
|
notifier := vars["notifier"]
|
||||||
if err != nil {
|
notifiers := services.AllNotifiers()
|
||||||
sendErrorJson(err, w, r)
|
for _, n := range notifiers {
|
||||||
return
|
notf := n.Select()
|
||||||
|
if notifier == notf.Method {
|
||||||
|
returnJson(n, w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
returnJson(notifier, w, r)
|
sendErrorJson(errors.New("notifier not found"), w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiNotifierUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
func apiNotifierUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
@ -66,7 +67,7 @@ func apiNotifierUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
//notifications.OnSave(notifer.Method)
|
//notifications.OnSave(notifer.Method)
|
||||||
sendJsonAction(notifer, "update", w, r)
|
sendJsonAction(vars["notifier"], "update", w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
func testNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/statping/statping/notifiers"
|
"github.com/statping/statping/notifiers"
|
||||||
|
"github.com/statping/statping/types/services"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -17,22 +18,23 @@ func TestApiNotifiersRoutes(t *testing.T) {
|
||||||
URL: "/api/notifiers",
|
URL: "/api/notifiers",
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
|
ResponseLen: len(services.AllNotifiers()),
|
||||||
BeforeTest: SetTestENV,
|
BeforeTest: SetTestENV,
|
||||||
SecureRoute: true,
|
SecureRoute: true,
|
||||||
}, {
|
}, {
|
||||||
Name: "Statping Mobile Notifier",
|
Name: "Statping Slack Notifier",
|
||||||
URL: "/api/notifier/mobile",
|
URL: "/api/notifier/slack",
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
BeforeTest: SetTestENV,
|
BeforeTest: SetTestENV,
|
||||||
SecureRoute: true,
|
SecureRoute: true,
|
||||||
}, {
|
}, {
|
||||||
Name: "Statping Update Notifier",
|
Name: "Statping Update Notifier",
|
||||||
URL: "/api/notifier/mobile",
|
URL: "/api/notifier/slack",
|
||||||
Method: "POST",
|
Method: "POST",
|
||||||
Body: `{
|
Body: `{
|
||||||
"method": "mobile",
|
"method": "slack",
|
||||||
"var1": "ExponentPushToken[ToBadIWillError123456]",
|
"host": "https://slack.api/example/12345",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"limits": 55
|
"limits": 55
|
||||||
}`,
|
}`,
|
||||||
|
@ -40,11 +42,11 @@ func TestApiNotifiersRoutes(t *testing.T) {
|
||||||
BeforeTest: SetTestENV,
|
BeforeTest: SetTestENV,
|
||||||
SecureRoute: true,
|
SecureRoute: true,
|
||||||
}, {
|
}, {
|
||||||
Name: "Statping Mobile Notifier",
|
Name: "Statping Slack Notifier",
|
||||||
URL: "/api/notifier/mobile",
|
URL: "/api/notifier/slack",
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
ExpectedStatus: 200,
|
ExpectedStatus: 200,
|
||||||
ExpectedContains: []string{`"method":"mobile"`, `"var1":"ExponentPushToken[ToBadIWillError123456]"`, `"enabled":true`, `"limits":55`},
|
ExpectedContains: []string{`"method":"slack"`},
|
||||||
BeforeTest: SetTestENV,
|
BeforeTest: SetTestENV,
|
||||||
SecureRoute: true,
|
SecureRoute: true,
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -74,14 +74,16 @@ func runCommand(app string, cmd ...string) (string, string, error) {
|
||||||
// OnFailure for commandLine will trigger failing service
|
// OnFailure for commandLine will trigger failing service
|
||||||
func (u *commandLine) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (u *commandLine) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
msg := u.GetValue("var2")
|
msg := u.GetValue("var2")
|
||||||
_, _, err := runCommand(u.Host, msg)
|
tmpl := ReplaceVars(msg, s, f)
|
||||||
|
_, _, err := runCommand(u.Host, tmpl)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSuccess for commandLine will trigger successful service
|
// OnSuccess for commandLine will trigger successful service
|
||||||
func (u *commandLine) OnSuccess(s *services.Service) error {
|
func (u *commandLine) OnSuccess(s *services.Service) error {
|
||||||
msg := u.GetValue("var1")
|
msg := u.GetValue("var1")
|
||||||
_, _, err := runCommand(u.Host, msg)
|
tmpl := ReplaceVars(msg, s, nil)
|
||||||
|
_, _, err := runCommand(u.Host, tmpl)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,41 +54,41 @@ var Discorder = &discord{¬ifications.Notification{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send will send a HTTP Post to the discord API. It accepts type: []byte
|
// Send will send a HTTP Post to the discord API. It accepts type: []byte
|
||||||
func (u *discord) sendRequest(msg string) error {
|
func (d *discord) sendRequest(msg string) error {
|
||||||
_, _, err := utils.HttpRequest(Discorder.GetValue("host"), "POST", "application/json", nil, strings.NewReader(msg), time.Duration(10*time.Second), true)
|
_, _, err := utils.HttpRequest(Discorder.GetValue("host"), "POST", "application/json", nil, strings.NewReader(msg), time.Duration(10*time.Second), true)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *discord) Select() *notifications.Notification {
|
func (d *discord) Select() *notifications.Notification {
|
||||||
return u.Notification
|
return d.Notification
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
func (u *discord) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (d *discord) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
msg := fmt.Sprintf(`{"content": "Your service '%v' is currently failing! Reason: %v"}`, s.Name, f.Issue)
|
msg := `{"content": "Your service '{{.Service.Name}}' is currently failing! Reason: {{.Failure.Issue}}"}`
|
||||||
return u.sendRequest(msg)
|
return d.sendRequest(ReplaceVars(msg, s, f))
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSuccess will trigger successful service
|
// OnSuccess will trigger successful service
|
||||||
func (u *discord) OnSuccess(s *services.Service) error {
|
func (d *discord) OnSuccess(s *services.Service) error {
|
||||||
msg := fmt.Sprintf(`{"content": "Your service '%s' is currently online!"}`, s.Name)
|
msg := `{"content": "Your service '{{.Service.Name}}' is currently online!"}`
|
||||||
return u.sendRequest(msg)
|
return d.sendRequest(ReplaceVars(msg, s, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSave triggers when this notifier has been saved
|
// OnSave triggers when this notifier has been saved
|
||||||
func (u *discord) OnTest() error {
|
func (d *discord) OnTest() error {
|
||||||
outError := errors.New("Incorrect discord URL, please confirm URL is correct")
|
outError := errors.New("Incorrect discord URL, please confirm URL is correct")
|
||||||
message := `{"content": "Testing the discord notifier"}`
|
message := `{"content": "Testing the discord notifier"}`
|
||||||
contents, _, err := utils.HttpRequest(Discorder.Host, "POST", "application/json", nil, bytes.NewBuffer([]byte(message)), time.Duration(10*time.Second), true)
|
contents, _, err := utils.HttpRequest(Discorder.Host, "POST", "application/json", nil, bytes.NewBuffer([]byte(message)), time.Duration(10*time.Second), true)
|
||||||
if string(contents) == "" {
|
if string(contents) == "" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var d discordTestJson
|
var dtt discordTestJson
|
||||||
err = json.Unmarshal(contents, &d)
|
err = json.Unmarshal(contents, &dtt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return outError
|
return outError
|
||||||
}
|
}
|
||||||
if d.Code == 0 {
|
if dtt.Code == 0 {
|
||||||
return outError
|
return outError
|
||||||
}
|
}
|
||||||
fmt.Println("discord: ", string(contents))
|
fmt.Println("discord: ", string(contents))
|
||||||
|
|
|
@ -76,23 +76,23 @@ const (
|
||||||
<tr>
|
<tr>
|
||||||
<td class="content-cell" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; padding: 35px; word-break: break-word;">
|
<td class="content-cell" style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; padding: 35px; word-break: break-word;">
|
||||||
<h1 style="box-sizing: border-box; color: #2F3133; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 19px; font-weight: bold; margin-top: 0;" align="left">
|
<h1 style="box-sizing: border-box; color: #2F3133; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 19px; font-weight: bold; margin-top: 0;" align="left">
|
||||||
{{ .Name }} is {{ if .Online }}Online{{else}}Offline{{end}}!
|
{{ .Service.Name }} is {{ if .Service.Online }}Online{{else}}Offline{{end}}!
|
||||||
</h1>
|
</h1>
|
||||||
<p style="box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 16px; line-height: 1.5em; margin-top: 0;" align="left">
|
<p style="box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 16px; line-height: 1.5em; margin-top: 0;" align="left">
|
||||||
|
|
||||||
{{ if .Online }}
|
{{ if .Service.Online }}
|
||||||
Your Statping service <a target="_blank" href="{{.Domain}}">{{.Name}}</a> is back online. This service has been triggered with a HTTP status code of '{{.LastStatusCode}}' and is currently online based on your requirements. Your service was reported online at {{.CreatedAt}}. </p>
|
Your Statping service <a target="_blank" href="{{.Service.Domain}}">{{.Service.Name}}</a> is back online. This service has been triggered with a HTTP status code of '{{.Service.LastStatusCode}}' and is currently online based on your requirements. Your service was reported online at {{.Service.CreatedAt}}. </p>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
Your Statping service <a target="_blank" href="{{.Domain}}">{{.Name}}</a> has been triggered with a HTTP status code of '{{.LastStatusCode}}' and is currently offline based on your requirements. This failure was created on {{.CreatedAt}}. </p>
|
Your Statping service <a target="_blank" href="{{.Service.Domain}}">{{.Service.Name}}</a> has been triggered with a HTTP status code of '{{.Service.LastStatusCode}}' and is currently offline based on your requirements. This failure was created on {{.Service.CreatedAt}}. </p>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
|
|
||||||
{{if .LastResponse }}
|
{{if .Service.LastResponse }}
|
||||||
<h1 style="box-sizing: border-box; color: #2F3133; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 19px; font-weight: bold; margin-top: 0;" align="left">
|
<h1 style="box-sizing: border-box; color: #2F3133; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 19px; font-weight: bold; margin-top: 0;" align="left">
|
||||||
Last Response</h1>
|
Last Response</h1>
|
||||||
<p style="box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 16px; line-height: 1.5em; margin-top: 0;" align="left">
|
<p style="box-sizing: border-box; color: #74787E; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; font-size: 16px; line-height: 1.5em; margin-top: 0;" align="left">
|
||||||
{{ .LastResponse }} </p> {{end}}
|
{{ .Service.LastResponse }} </p> {{end}}
|
||||||
<table class="body-sub" style="border-top-color: #EDEFF2; border-top-style: solid; border-top-width: 1px; box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin-top: 25px; padding-top: 25px;">
|
<table class="body-sub" style="border-top-color: #EDEFF2; border-top-style: solid; border-top-width: 1px; box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; margin-top: 25px; padding-top: 25px;">
|
||||||
<td style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; word-break: break-word;"> <a href="/service/{{.Id}}" class="button button--blue" target="_blank" style="-webkit-text-size-adjust: none; background: #3869D4; border-color: #3869d4; border-radius: 3px; border-style: solid; border-width: 10px 18px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); box-sizing: border-box; color: #FFF; display: inline-block; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; text-decoration: none;">View Service</a> </td>
|
<td style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; word-break: break-word;"> <a href="/service/{{.Service.Id}}" class="button button--blue" target="_blank" style="-webkit-text-size-adjust: none; background: #3869D4; border-color: #3869d4; border-radius: 3px; border-style: solid; border-width: 10px 18px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); box-sizing: border-box; color: #FFF; display: inline-block; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; text-decoration: none;">View Service</a> </td>
|
||||||
<td style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; word-break: break-word;"> <a href="/dashboard" class="button button--blue" target="_blank" style="-webkit-text-size-adjust: none; background: #3869D4; border-color: #3869d4; border-radius: 3px; border-style: solid; border-width: 10px 18px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); box-sizing: border-box; color: #FFF; display: inline-block; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; text-decoration: none;">Statping Dashboard</a> </td>
|
<td style="box-sizing: border-box; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; word-break: break-word;"> <a href="/dashboard" class="button button--blue" target="_blank" style="-webkit-text-size-adjust: none; background: #3869D4; border-color: #3869d4; border-radius: 3px; border-style: solid; border-width: 10px 18px; box-shadow: 0 2px 3px rgba(0, 0, 0, 0.16); box-sizing: border-box; color: #FFF; display: inline-block; font-family: Arial, 'Helvetica Neue', Helvetica, sans-serif; text-decoration: none;">Statping Dashboard</a> </td>
|
||||||
</table>
|
</table>
|
||||||
</td>
|
</td>
|
||||||
|
@ -178,32 +178,32 @@ type emailOutgoing struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
func (u *emailer) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (e *emailer) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
email := &emailOutgoing{
|
email := &emailOutgoing{
|
||||||
To: u.Var2,
|
To: e.Var2,
|
||||||
Subject: fmt.Sprintf("Service %v is Failing", s.Name),
|
Subject: fmt.Sprintf("Service %v is Failing", s.Name),
|
||||||
Template: mainEmailTemplate,
|
Template: mainEmailTemplate,
|
||||||
Data: interface{}(s),
|
Data: interface{}(s),
|
||||||
From: u.Var1,
|
From: e.Var1,
|
||||||
}
|
}
|
||||||
return u.dialSend(email)
|
return e.dialSend(email)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSuccess will trigger successful service
|
// OnSuccess will trigger successful service
|
||||||
func (u *emailer) OnSuccess(s *services.Service) error {
|
func (e *emailer) OnSuccess(s *services.Service) error {
|
||||||
msg := s.DownText
|
msg := s.DownText
|
||||||
email := &emailOutgoing{
|
email := &emailOutgoing{
|
||||||
To: u.Var2,
|
To: e.Var2,
|
||||||
Subject: msg,
|
Subject: msg,
|
||||||
Template: mainEmailTemplate,
|
Template: mainEmailTemplate,
|
||||||
Data: interface{}(s),
|
Data: interface{}(s),
|
||||||
From: u.Var1,
|
From: e.Var1,
|
||||||
}
|
}
|
||||||
return u.dialSend(email)
|
return e.dialSend(email)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnTest triggers when this notifier has been saved
|
// OnTest triggers when this notifier has been saved
|
||||||
func (u *emailer) OnTest() error {
|
func (e *emailer) OnTest() error {
|
||||||
testService := &services.Service{
|
testService := &services.Service{
|
||||||
Id: 1,
|
Id: 1,
|
||||||
Name: "Example Service",
|
Name: "Example Service",
|
||||||
|
@ -219,21 +219,21 @@ func (u *emailer) OnTest() error {
|
||||||
CreatedAt: utils.Now().Add(-24 * time.Hour),
|
CreatedAt: utils.Now().Add(-24 * time.Hour),
|
||||||
}
|
}
|
||||||
email := &emailOutgoing{
|
email := &emailOutgoing{
|
||||||
To: u.Var2,
|
To: e.Var2,
|
||||||
Subject: fmt.Sprintf("Service %v is Back Online", testService.Name),
|
Subject: fmt.Sprintf("Service %v is Back Online", testService.Name),
|
||||||
Template: mainEmailTemplate,
|
Template: mainEmailTemplate,
|
||||||
Data: testService,
|
Data: testService,
|
||||||
From: u.Var1,
|
From: e.Var1,
|
||||||
}
|
}
|
||||||
return u.dialSend(email)
|
return e.dialSend(email)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *emailer) dialSend(email *emailOutgoing) error {
|
func (e *emailer) dialSend(email *emailOutgoing) error {
|
||||||
mailer = mail.NewDialer(u.Host, u.Port, u.Username, u.Password)
|
mailer = mail.NewDialer(e.Host, e.Port, e.Username, e.Password)
|
||||||
emailSource(email)
|
emailSource(email)
|
||||||
m := mail.NewMessage()
|
m := mail.NewMessage()
|
||||||
// if email setting TLS is Disabled
|
// if email setting TLS is Disabled
|
||||||
if u.ApiKey == "true" {
|
if e.ApiKey == "true" {
|
||||||
mailer.SSL = false
|
mailer.SSL = false
|
||||||
} else {
|
} else {
|
||||||
mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
mailer.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||||
|
|
|
@ -58,28 +58,28 @@ var LineNotify = &lineNotifier{¬ifications.Notification{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send will send a HTTP Post with the Authorization to the notify-api.line.me server. It accepts type: string
|
// Send will send a HTTP Post with the Authorization to the notify-api.line.me server. It accepts type: string
|
||||||
func (u *lineNotifier) sendMessage(message string) error {
|
func (l *lineNotifier) sendMessage(message string) error {
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("message", message)
|
v.Set("message", message)
|
||||||
headers := []string{fmt.Sprintf("Authorization=Bearer %v", u.ApiSecret)}
|
headers := []string{fmt.Sprintf("Authorization=Bearer %v", l.ApiSecret)}
|
||||||
_, _, err := utils.HttpRequest("https://notify-api.line.me/api/notify", "POST", "application/x-www-form-urlencoded", headers, strings.NewReader(v.Encode()), time.Duration(10*time.Second), true)
|
_, _, err := utils.HttpRequest("https://notify-api.line.me/api/notify", "POST", "application/x-www-form-urlencoded", headers, strings.NewReader(v.Encode()), time.Duration(10*time.Second), true)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
func (u *lineNotifier) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (l *lineNotifier) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
||||||
return u.sendMessage(msg)
|
return l.sendMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSuccess will trigger successful service
|
// OnSuccess will trigger successful service
|
||||||
func (u *lineNotifier) OnSuccess(s *services.Service) error {
|
func (l *lineNotifier) OnSuccess(s *services.Service) error {
|
||||||
msg := fmt.Sprintf("Service %s is online!", s.Name)
|
msg := fmt.Sprintf("Service %s is online!", s.Name)
|
||||||
return u.sendMessage(msg)
|
return l.sendMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnTest triggers when this notifier has been saved
|
// OnTest triggers when this notifier has been saved
|
||||||
func (u *lineNotifier) OnTest() error {
|
func (l *lineNotifier) OnTest() error {
|
||||||
msg := fmt.Sprintf("Testing if Line Notifier is working!")
|
msg := fmt.Sprintf("Testing if Line Notifier is working!")
|
||||||
return u.sendMessage(msg)
|
return l.sendMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,7 +88,7 @@ func dataJson(s *services.Service, f *failures.Failure) map[string]interface{} {
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
func (u *mobilePush) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (m *mobilePush) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
data := dataJson(s, f)
|
data := dataJson(s, f)
|
||||||
msg := &pushArray{
|
msg := &pushArray{
|
||||||
Message: fmt.Sprintf("Your service '%v' is currently failing! Reason: %v", s.Name, f.Issue),
|
Message: fmt.Sprintf("Your service '%v' is currently failing! Reason: %v", s.Name, f.Issue),
|
||||||
|
@ -96,11 +96,11 @@ func (u *mobilePush) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
Topic: mobileIdentifier,
|
Topic: mobileIdentifier,
|
||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
return u.Send(msg)
|
return m.Send(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSuccess will trigger successful service
|
// OnSuccess will trigger successful service
|
||||||
func (u *mobilePush) OnSuccess(s *services.Service) error {
|
func (m *mobilePush) OnSuccess(s *services.Service) error {
|
||||||
data := dataJson(s, nil)
|
data := dataJson(s, nil)
|
||||||
msg := &pushArray{
|
msg := &pushArray{
|
||||||
Message: "Service is Online!",
|
Message: "Service is Online!",
|
||||||
|
@ -108,17 +108,17 @@ func (u *mobilePush) OnSuccess(s *services.Service) error {
|
||||||
Topic: mobileIdentifier,
|
Topic: mobileIdentifier,
|
||||||
Data: data,
|
Data: data,
|
||||||
}
|
}
|
||||||
return u.Send(msg)
|
return m.Send(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnTest triggers when this notifier has been saved
|
// OnTest triggers when this notifier has been saved
|
||||||
func (u *mobilePush) OnTest() error {
|
func (m *mobilePush) OnTest() error {
|
||||||
msg := &pushArray{
|
msg := &pushArray{
|
||||||
Message: "Testing the Mobile Notifier",
|
Message: "Testing the Mobile Notifier",
|
||||||
Title: "Testing Notifications",
|
Title: "Testing Notifications",
|
||||||
Topic: mobileIdentifier,
|
Topic: mobileIdentifier,
|
||||||
Tokens: []string{u.Var1},
|
Tokens: []string{m.Var1},
|
||||||
Platform: utils.ToInt(u.Var2),
|
Platform: utils.ToInt(m.Var2),
|
||||||
}
|
}
|
||||||
body, err := pushRequest(msg)
|
body, err := pushRequest(msg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -138,9 +138,9 @@ func (u *mobilePush) OnTest() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send will send message to Statping push notifications endpoint
|
// Send will send message to Statping push notifications endpoint
|
||||||
func (u *mobilePush) Send(pushMessage *pushArray) error {
|
func (m *mobilePush) Send(pushMessage *pushArray) error {
|
||||||
pushMessage.Tokens = []string{u.Var1}
|
pushMessage.Tokens = []string{m.Var1}
|
||||||
pushMessage.Platform = utils.ToInt(u.Var2)
|
pushMessage.Platform = utils.ToInt(m.Var2)
|
||||||
_, err := pushRequest(pushMessage)
|
_, err := pushRequest(pushMessage)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -1,8 +1,12 @@
|
||||||
package notifiers
|
package notifiers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/prometheus/common/log"
|
||||||
|
"github.com/statping/statping/types/failures"
|
||||||
|
"github.com/statping/statping/types/null"
|
||||||
"github.com/statping/statping/types/services"
|
"github.com/statping/statping/types/services"
|
||||||
|
"github.com/statping/statping/utils"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitNotifiers() {
|
func InitNotifiers() {
|
||||||
|
@ -23,7 +27,63 @@ func Add(notifs ...services.ServiceNotifier) {
|
||||||
for _, n := range notifs {
|
for _, n := range notifs {
|
||||||
services.AddNotifier(n)
|
services.AddNotifier(n)
|
||||||
if err := n.Select().Create(); err != nil {
|
if err := n.Select().Create(); err != nil {
|
||||||
fmt.Println(err)
|
log.Error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ReplaceVars(input string, s *services.Service, f *failures.Failure) string {
|
||||||
|
input = utils.ReplaceTemplate(input, s)
|
||||||
|
if f != nil {
|
||||||
|
input = utils.ReplaceTemplate(input, f)
|
||||||
|
}
|
||||||
|
return input
|
||||||
|
}
|
||||||
|
|
||||||
|
var exampleService = &services.Service{
|
||||||
|
Id: 1,
|
||||||
|
Name: "Statping",
|
||||||
|
Domain: "https://statping.com",
|
||||||
|
Expected: null.NewNullString("a better response"),
|
||||||
|
ExpectedStatus: 200,
|
||||||
|
Interval: 60,
|
||||||
|
Type: "http",
|
||||||
|
Method: "get",
|
||||||
|
Timeout: 10,
|
||||||
|
Order: 2,
|
||||||
|
VerifySSL: null.NewNullBool(true),
|
||||||
|
Public: null.NewNullBool(true),
|
||||||
|
GroupId: 0,
|
||||||
|
Permalink: null.NewNullString("statping"),
|
||||||
|
Online: true,
|
||||||
|
Latency: 324399,
|
||||||
|
PingTime: 18399,
|
||||||
|
Online24Hours: 99.2,
|
||||||
|
Online7Days: 99.8,
|
||||||
|
AvgResponse: 300233,
|
||||||
|
FailuresLast24Hours: 4,
|
||||||
|
Checkpoint: utils.Now().Add(-10 * time.Minute),
|
||||||
|
SleepDuration: 55,
|
||||||
|
LastResponse: "returning from a response",
|
||||||
|
AllowNotifications: null.NewNullBool(true),
|
||||||
|
UserNotified: false,
|
||||||
|
UpdateNotify: null.NewNullBool(true),
|
||||||
|
SuccessNotified: false,
|
||||||
|
LastStatusCode: 200,
|
||||||
|
LastLookupTime: 5233,
|
||||||
|
LastLatency: 270233,
|
||||||
|
LastCheck: utils.Now().Add(-15 * time.Second),
|
||||||
|
LastOnline: utils.Now().Add(-15 * time.Second),
|
||||||
|
LastOffline: utils.Now().Add(-10 * time.Minute),
|
||||||
|
SecondsOnline: 4500,
|
||||||
|
SecondsOffline: 300,
|
||||||
|
}
|
||||||
|
|
||||||
|
var exampleFailure = &failures.Failure{
|
||||||
|
Id: 1,
|
||||||
|
Issue: "HTTP returned a 500 status code",
|
||||||
|
ErrorCode: 500,
|
||||||
|
Service: 1,
|
||||||
|
PingTime: 43203,
|
||||||
|
CreatedAt: utils.Now().Add(-10 * time.Minute),
|
||||||
|
}
|
||||||
|
|
|
@ -18,14 +18,12 @@ package notifiers
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
|
||||||
"github.com/statping/statping/types/failures"
|
"github.com/statping/statping/types/failures"
|
||||||
"github.com/statping/statping/types/notifications"
|
"github.com/statping/statping/types/notifications"
|
||||||
"github.com/statping/statping/types/notifier"
|
"github.com/statping/statping/types/notifier"
|
||||||
"github.com/statping/statping/types/services"
|
"github.com/statping/statping/types/services"
|
||||||
"github.com/statping/statping/utils"
|
"github.com/statping/statping/utils"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -33,9 +31,8 @@ var _ notifier.Notifier = (*slack)(nil)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
slackMethod = "slack"
|
slackMethod = "slack"
|
||||||
failingTemplate = `{ "attachments": [ { "fallback": "Service {{.Service.Name}} - is currently failing", "text": "Your Statping service <{{.Service.Domain}}|{{.Service.Name}}> has just received a Failure notification based on your expected results. {{.Service.Name}} responded with a HTTP Status code of {{.Service.LastStatusCode}}.", "fields": [ { "title": "Expected Status Code", "value": "{{.Service.ExpectedStatus}}", "short": true }, { "title": "Received Status Code", "value": "{{.Service.LastStatusCode}}", "short": true } ,{ "title": "Error Message", "value": "{{.Issue}}", "short": false } ], "color": "#FF0000", "thumb_url": "https://statping.com", "footer": "Statping", "footer_icon": "https://img.cjx.io/statuplogo32.png" } ] }`
|
failingTemplate = `{ "attachments": [ { "fallback": "Service {{.Service.Name}} - is currently failing", "text": "Your Statping service <{{.Service.Domain}}|{{.Service.Name}}> has just received a Failure notification based on your expected results. {{.Service.Name}} responded with a HTTP Status code of {{.Service.LastStatusCode}}.", "fields": [ { "title": "Expected Status Code", "value": "{{.Service.ExpectedStatus}}", "short": true }, { "title": "Received Status Code", "value": "{{.Service.LastStatusCode}}", "short": true } ,{ "title": "Error Message", "value": "{{.Failure.Issue}}", "short": false } ], "color": "#FF0000", "thumb_url": "https://statping.com", "footer": "Statping", "footer_icon": "https://img.cjx.io/statuplogo32.png" } ] }`
|
||||||
successTemplate = `{ "attachments": [ { "fallback": "Service {{.Service.Name}} - is now back online", "text": "Your Statping service <{{.Service.Domain}}|{{.Service.Name}}> is now back online and meets your expected responses.", "color": "#00FF00", "thumb_url": "https://statping.com", "footer": "Statping", "footer_icon": "https://img.cjx.io/statuplogo32.png" } ] }`
|
successTemplate = `{ "attachments": [ { "fallback": "Service {{.Service.Name}} - is now back online", "text": "Your Statping service <{{.Service.Domain}}|{{.Service.Name}}> is now back online and meets your expected responses.", "color": "#00FF00", "thumb_url": "https://statping.com", "footer": "Statping", "footer_icon": "https://img.cjx.io/statuplogo32.png" } ] }`
|
||||||
slackText = `{"text":"{{.}}"}`
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type slack struct {
|
type slack struct {
|
||||||
|
@ -66,58 +63,36 @@ var slacker = &slack{¬ifications.Notification{
|
||||||
}}},
|
}}},
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseSlackMessage(id int64, temp string, data interface{}) string {
|
|
||||||
buf := new(bytes.Buffer)
|
|
||||||
slackTemp, _ := template.New("slack").Parse(temp)
|
|
||||||
err := slackTemp.Execute(buf, data)
|
|
||||||
if err != nil {
|
|
||||||
return err.Error()
|
|
||||||
}
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
type slackMessage struct {
|
|
||||||
Service *services.Service
|
|
||||||
Template string
|
|
||||||
Time int64
|
|
||||||
Issue string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send will send a HTTP Post to the slack webhooker API. It accepts type: string
|
// Send will send a HTTP Post to the slack webhooker API. It accepts type: string
|
||||||
func (u *slack) sendSlack(msg string) error {
|
func (s *slack) sendSlack(msg string) error {
|
||||||
contents, resp, err := utils.HttpRequest(u.Host, "POST", "application/json", nil, strings.NewReader(msg), time.Duration(10*time.Second), true)
|
_, resp, err := utils.HttpRequest(s.Host, "POST", "application/json", nil, strings.NewReader(msg), time.Duration(10*time.Second), true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
fmt.Println("CONTENTS: ", string(contents))
|
return nil
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (u *slack) OnTest() error {
|
func (s *slack) OnTest() error {
|
||||||
contents, resp, err := utils.HttpRequest(u.Host, "POST", "application/json", nil, bytes.NewBuffer([]byte(`{"text":"testing message"}`)), time.Duration(10*time.Second), true)
|
contents, resp, err := utils.HttpRequest(s.Host, "POST", "application/json", nil, bytes.NewBuffer([]byte(`{"text":"testing message"}`)), time.Duration(10*time.Second), true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
if string(contents) != "ok" {
|
if string(contents) != "ok" {
|
||||||
return errors.New("the slack response was incorrect, check the URL")
|
return errors.New("the slack response was incorrect, check the URL")
|
||||||
}
|
}
|
||||||
return err
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
func (u *slack) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (s *slack) OnFailure(srv *services.Service, f *failures.Failure) error {
|
||||||
message := slackMessage{
|
msg := ReplaceVars(failingTemplate, srv, f)
|
||||||
Service: s,
|
return s.sendSlack(msg)
|
||||||
Template: failingTemplate,
|
|
||||||
Time: utils.Now().Unix(),
|
|
||||||
}
|
|
||||||
msg := parseSlackMessage(s.Id, failingTemplate, message)
|
|
||||||
return u.sendSlack(msg)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSuccess will trigger successful service
|
// OnSuccess will trigger successful service
|
||||||
func (u *slack) OnSuccess(s *services.Service) error {
|
func (s *slack) OnSuccess(srv *services.Service) error {
|
||||||
message := slackMessage{
|
msg := ReplaceVars(successTemplate, srv, nil)
|
||||||
Service: s,
|
return s.sendSlack(msg)
|
||||||
Template: successTemplate,
|
|
||||||
Time: utils.Now().Unix(),
|
|
||||||
}
|
|
||||||
msg := parseSlackMessage(s.Id, successTemplate, message)
|
|
||||||
return u.sendSlack(msg)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,11 +66,11 @@ var Telegram = &telegram{¬ifications.Notification{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send will send a HTTP Post to the Telegram API. It accepts type: string
|
// Send will send a HTTP Post to the Telegram API. It accepts type: string
|
||||||
func (u *telegram) sendMessage(message string) error {
|
func (t *telegram) sendMessage(message string) error {
|
||||||
apiEndpoint := fmt.Sprintf("https://api.telegram.org/bot%v/sendMessage", u.ApiSecret)
|
apiEndpoint := fmt.Sprintf("https://api.telegram.org/bot%v/sendMessage", t.ApiSecret)
|
||||||
|
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("chat_id", u.Var1)
|
v.Set("chat_id", t.Var1)
|
||||||
v.Set("text", message)
|
v.Set("text", message)
|
||||||
rb := *strings.NewReader(v.Encode())
|
rb := *strings.NewReader(v.Encode())
|
||||||
|
|
||||||
|
@ -86,21 +86,21 @@ func (u *telegram) sendMessage(message string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
func (u *telegram) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (t *telegram) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
||||||
return u.sendMessage(msg)
|
return t.sendMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSuccess will trigger successful service
|
// OnSuccess will trigger successful service
|
||||||
func (u *telegram) OnSuccess(s *services.Service) error {
|
func (t *telegram) OnSuccess(s *services.Service) error {
|
||||||
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
|
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
|
||||||
return u.sendMessage(msg)
|
return t.sendMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnTest will test the Twilio SMS messaging
|
// OnTest will test the Twilio SMS messaging
|
||||||
func (u *telegram) OnTest() error {
|
func (t *telegram) OnTest() error {
|
||||||
msg := fmt.Sprintf("Testing the Twilio SMS Notifier on your Statping server")
|
msg := fmt.Sprintf("Testing the Twilio SMS Notifier on your Statping server")
|
||||||
return u.sendMessage(msg)
|
return t.sendMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func telegramSuccess(res []byte) (bool, telegramResponse) {
|
func telegramSuccess(res []byte) (bool, telegramResponse) {
|
||||||
|
|
|
@ -76,12 +76,12 @@ var Twilio = &twilio{¬ifications.Notification{
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send will send a HTTP Post to the Twilio SMS API. It accepts type: string
|
// Send will send a HTTP Post to the Twilio SMS API. It accepts type: string
|
||||||
func (u *twilio) sendMessage(message string) error {
|
func (t *twilio) sendMessage(message string) error {
|
||||||
twilioUrl := fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%v/Messages.json", u.GetValue("api_key"))
|
twilioUrl := fmt.Sprintf("https://api.twilio.com/2010-04-01/Accounts/%v/Messages.json", t.GetValue("api_key"))
|
||||||
|
|
||||||
v := url.Values{}
|
v := url.Values{}
|
||||||
v.Set("To", "+"+u.Var1)
|
v.Set("To", "+"+t.Var1)
|
||||||
v.Set("From", "+"+u.Var2)
|
v.Set("From", "+"+t.Var2)
|
||||||
v.Set("Body", message)
|
v.Set("Body", message)
|
||||||
rb := *strings.NewReader(v.Encode())
|
rb := *strings.NewReader(v.Encode())
|
||||||
|
|
||||||
|
@ -96,21 +96,21 @@ func (u *twilio) sendMessage(message string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
func (u *twilio) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (t *twilio) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
msg := fmt.Sprintf("Your service '%v' is currently offline!", s.Name)
|
||||||
return u.sendMessage(msg)
|
return t.sendMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnSuccess will trigger successful service
|
// OnSuccess will trigger successful service
|
||||||
func (u *twilio) OnSuccess(s *services.Service) error {
|
func (t *twilio) OnSuccess(s *services.Service) error {
|
||||||
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
|
msg := fmt.Sprintf("Your service '%v' is currently online!", s.Name)
|
||||||
return u.sendMessage(msg)
|
return t.sendMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnTest will test the Twilio SMS messaging
|
// OnTest will test the Twilio SMS messaging
|
||||||
func (u *twilio) OnTest() error {
|
func (t *twilio) OnTest() error {
|
||||||
msg := fmt.Sprintf("Testing the Twilio SMS Notifier")
|
msg := fmt.Sprintf("Testing the Twilio SMS Notifier")
|
||||||
return u.sendMessage(msg)
|
return t.sendMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func twilioSuccess(res []byte) (bool, twilioResponse) {
|
func twilioSuccess(res []byte) (bool, twilioResponse) {
|
||||||
|
|
|
@ -65,8 +65,8 @@ var Webhook = &webhooker{¬ifications.Notification{
|
||||||
}, {
|
}, {
|
||||||
Type: "textarea",
|
Type: "textarea",
|
||||||
Title: "HTTP Body",
|
Title: "HTTP Body",
|
||||||
Placeholder: `{"service_id": "%s.Id", "service_name": "%s.Name"}`,
|
Placeholder: `{"service_id": {{.Service.Id}}", "service_name": "{{.Service.Name}"}`,
|
||||||
SmallText: "Optional HTTP body for a POST request. You can insert variables into your body request.<br>%service.Id, %service.Name, %service.Online<br>%failure.Issue",
|
SmallText: "Optional HTTP body for a POST request. You can insert variables into your body request.<br>{{.Service.Id}}, {{.Service.Name}}, {{.Service.Online}}<br>{{.Failure.Issue}}",
|
||||||
DbField: "Var2",
|
DbField: "Var2",
|
||||||
}, {
|
}, {
|
||||||
Type: "text",
|
Type: "text",
|
||||||
|
@ -96,12 +96,6 @@ func (w *webhooker) Select() *notifications.Notification {
|
||||||
return w.Notification
|
return w.Notification
|
||||||
}
|
}
|
||||||
|
|
||||||
func replaceBodyText(body string, s *services.Service, f *failures.Failure) string {
|
|
||||||
body = utils.ConvertInterface(body, s)
|
|
||||||
body = utils.ConvertInterface(body, f)
|
|
||||||
return body
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *webhooker) sendHttpWebhook(body string) (*http.Response, error) {
|
func (w *webhooker) sendHttpWebhook(body string) (*http.Response, error) {
|
||||||
utils.Log.Infoln(fmt.Sprintf("sending body: '%v' to %v as a %v request", body, w.Host, w.Var1))
|
utils.Log.Infoln(fmt.Sprintf("sending body: '%v' to %v as a %v request", body, w.Host, w.Var1))
|
||||||
client := new(http.Client)
|
client := new(http.Client)
|
||||||
|
@ -130,11 +124,12 @@ func (w *webhooker) sendHttpWebhook(body string) (*http.Response, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return resp, err
|
return resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (w *webhooker) OnTest() error {
|
func (w *webhooker) OnTest() error {
|
||||||
body := replaceBodyText(w.Var2, nil, nil)
|
body := ReplaceVars(w.Var2, exampleService, exampleFailure)
|
||||||
resp, err := w.sendHttpWebhook(body)
|
resp, err := w.sendHttpWebhook(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -147,7 +142,7 @@ func (w *webhooker) OnTest() error {
|
||||||
|
|
||||||
// OnFailure will trigger failing service
|
// OnFailure will trigger failing service
|
||||||
func (w *webhooker) OnFailure(s *services.Service, f *failures.Failure) error {
|
func (w *webhooker) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
msg := replaceBodyText(w.Var2, s, f)
|
msg := ReplaceVars(w.Var2, s, f)
|
||||||
resp, err := w.sendHttpWebhook(msg)
|
resp, err := w.sendHttpWebhook(msg)
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
return err
|
return err
|
||||||
|
@ -155,7 +150,7 @@ func (w *webhooker) OnFailure(s *services.Service, f *failures.Failure) error {
|
||||||
|
|
||||||
// OnSuccess will trigger successful service
|
// OnSuccess will trigger successful service
|
||||||
func (w *webhooker) OnSuccess(s *services.Service) error {
|
func (w *webhooker) OnSuccess(s *services.Service) error {
|
||||||
msg := replaceBodyText(w.Var2, s, nil)
|
msg := ReplaceVars(w.Var2, s, nil)
|
||||||
resp, err := w.sendHttpWebhook(msg)
|
resp, err := w.sendHttpWebhook(msg)
|
||||||
defer resp.Body.Close()
|
defer resp.Body.Close()
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -76,7 +76,11 @@ func Connect(configs *DbConfig, retry bool) error {
|
||||||
maxIdleConn := utils.Getenv("MAX_IDLE_CONN", 5)
|
maxIdleConn := utils.Getenv("MAX_IDLE_CONN", 5)
|
||||||
maxLifeConn := utils.Getenv("MAX_LIFE_CONN", 2*time.Minute)
|
maxLifeConn := utils.Getenv("MAX_LIFE_CONN", 2*time.Minute)
|
||||||
|
|
||||||
dbSession.DB().SetMaxOpenConns(maxOpenConn.(int))
|
if configs.DbConn == "sqlite3" {
|
||||||
|
dbSession.DB().SetMaxOpenConns(2)
|
||||||
|
} else {
|
||||||
|
dbSession.DB().SetMaxOpenConns(maxOpenConn.(int))
|
||||||
|
}
|
||||||
dbSession.DB().SetMaxIdleConns(maxIdleConn.(int))
|
dbSession.DB().SetMaxIdleConns(maxIdleConn.(int))
|
||||||
dbSession.DB().SetConnMaxLifetime(maxLifeConn.(time.Duration))
|
dbSession.DB().SetConnMaxLifetime(maxLifeConn.(time.Duration))
|
||||||
|
|
||||||
|
@ -84,7 +88,7 @@ func Connect(configs *DbConfig, retry bool) error {
|
||||||
if utils.VerboseMode >= 4 {
|
if utils.VerboseMode >= 4 {
|
||||||
dbSession.LogMode(true).Debug().SetLogger(gorm.Logger{log})
|
dbSession.LogMode(true).Debug().SetLogger(gorm.Logger{log})
|
||||||
}
|
}
|
||||||
log.Infoln(fmt.Sprintf("Database %v connection was successful.", configs.DbConn))
|
log.Infoln(fmt.Sprintf("Database %s connection was successful.", configs.DbConn))
|
||||||
}
|
}
|
||||||
|
|
||||||
configs.Db = dbSession
|
configs.Db = dbSession
|
||||||
|
|
|
@ -35,8 +35,6 @@ func Select() (*Core, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Core) Create() error {
|
func (c *Core) Create() error {
|
||||||
c.ApiKey = c.ApiKey
|
|
||||||
c.ApiSecret = c.ApiSecret
|
|
||||||
apiKey := utils.Getenv("API_KEY", utils.NewSHA256Hash()).(string)
|
apiKey := utils.Getenv("API_KEY", utils.NewSHA256Hash()).(string)
|
||||||
apiSecret := utils.Getenv("API_SECRET", utils.NewSHA256Hash()).(string)
|
apiSecret := utils.Getenv("API_SECRET", utils.NewSHA256Hash()).(string)
|
||||||
|
|
||||||
|
@ -66,23 +64,3 @@ func (c *Core) Update() error {
|
||||||
func (c *Core) Delete() error {
|
func (c *Core) Delete() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Sample() error {
|
|
||||||
apiKey := utils.Getenv("API_KEY", "samplekey")
|
|
||||||
apiSecret := utils.Getenv("API_SECRET", "samplesecret")
|
|
||||||
|
|
||||||
core := &Core{
|
|
||||||
Name: "Statping Sample Data",
|
|
||||||
Description: "This data is only used to testing",
|
|
||||||
ApiKey: apiKey.(string),
|
|
||||||
ApiSecret: apiSecret.(string),
|
|
||||||
Domain: "http://localhost:8080",
|
|
||||||
Version: "test",
|
|
||||||
CreatedAt: time.Now().UTC(),
|
|
||||||
UseCdn: null.NewNullBool(false),
|
|
||||||
Footer: null.NewNullString(""),
|
|
||||||
}
|
|
||||||
|
|
||||||
q := db.Create(core)
|
|
||||||
return q.Error()
|
|
||||||
}
|
|
||||||
|
|
|
@ -12,12 +12,6 @@ func DB() database.Database {
|
||||||
return db
|
return db
|
||||||
}
|
}
|
||||||
|
|
||||||
func Find(id int64) (*Failure, error) {
|
|
||||||
var failure Failure
|
|
||||||
q := db.Where("id = ?", id).Find(&failure)
|
|
||||||
return &failure, q.Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
func All() []*Failure {
|
func All() []*Failure {
|
||||||
var failures []*Failure
|
var failures []*Failure
|
||||||
db.Find(&failures)
|
db.Find(&failures)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package notifications
|
package notifications
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"github.com/statping/statping/database"
|
"github.com/statping/statping/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -15,6 +16,9 @@ func SetDB(database database.Database) {
|
||||||
func Find(method string) (*Notification, error) {
|
func Find(method string) (*Notification, error) {
|
||||||
var notification Notification
|
var notification Notification
|
||||||
q := db.Where("method = ?", method).Find(¬ification)
|
q := db.Where("method = ?", method).Find(¬ification)
|
||||||
|
if ¬ification == nil {
|
||||||
|
return nil, errors.New("cannot find notifier")
|
||||||
|
}
|
||||||
return ¬ification, q.Error()
|
return ¬ification, q.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,8 +83,7 @@ func (n *Notification) CanSend() bool {
|
||||||
|
|
||||||
// GetValue returns the database value of a accept DbField value.
|
// GetValue returns the database value of a accept DbField value.
|
||||||
func (n *Notification) GetValue(dbField string) string {
|
func (n *Notification) GetValue(dbField string) string {
|
||||||
dbField = strings.ToLower(dbField)
|
switch strings.ToLower(dbField) {
|
||||||
switch dbField {
|
|
||||||
case "host":
|
case "host":
|
||||||
return n.Host
|
return n.Host
|
||||||
case "port":
|
case "port":
|
||||||
|
@ -109,6 +108,36 @@ func (n *Notification) GetValue(dbField string) string {
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ResetQueue will clear the notifiers Queue
|
||||||
|
func (n *Notification) ResetQueue() {
|
||||||
|
n.Queue = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// start will start the go routine for the notifier queue
|
||||||
|
func (n *Notification) Start() {
|
||||||
|
n.Running = make(chan bool)
|
||||||
|
}
|
||||||
|
|
||||||
|
// close will stop the go routine for queue
|
||||||
|
func (n *Notification) Close() {
|
||||||
|
if n.IsRunning() {
|
||||||
|
close(n.Running)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsRunning will return true if the notifier is currently running a queue
|
||||||
|
func (n *Notification) IsRunning() bool {
|
||||||
|
if n.Running == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
select {
|
||||||
|
case <-n.Running:
|
||||||
|
return false
|
||||||
|
default:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Init accepts the Notifier interface to initialize the notifier
|
// Init accepts the Notifier interface to initialize the notifier
|
||||||
//func Init(n Notifier) (*Notification, error) {
|
//func Init(n Notifier) (*Notification, error) {
|
||||||
// if Exists(n.Select().Method) {
|
// if Exists(n.Select().Method) {
|
||||||
|
@ -139,33 +168,3 @@ func (n *Notification) GetValue(dbField string) string {
|
||||||
//
|
//
|
||||||
// return nil, err
|
// return nil, err
|
||||||
//}
|
//}
|
||||||
|
|
||||||
// ResetQueue will clear the notifiers Queue
|
|
||||||
func (n *Notification) ResetQueue() {
|
|
||||||
n.Queue = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// start will start the go routine for the notifier queue
|
|
||||||
func (n *Notification) Start() {
|
|
||||||
n.Running = make(chan bool)
|
|
||||||
}
|
|
||||||
|
|
||||||
// close will stop the go routine for queue
|
|
||||||
func (n *Notification) Close() {
|
|
||||||
if n.IsRunning() {
|
|
||||||
close(n.Running)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsRunning will return true if the notifier is currently running a queue
|
|
||||||
func (n *Notification) IsRunning() bool {
|
|
||||||
if n.Running == nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
select {
|
|
||||||
case <-n.Running:
|
|
||||||
return false
|
|
||||||
default:
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -271,6 +271,7 @@ func sendSuccess(s *Service) {
|
||||||
//s.UpdateNotify.Bool
|
//s.UpdateNotify.Bool
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
s.notifyAfterCount = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// recordFailure will create a new 'Failure' record in the database for a offline service
|
// recordFailure will create a new 'Failure' record in the database for a offline service
|
||||||
|
@ -307,18 +308,22 @@ func sendFailure(s *Service, f *failures.Failure) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, n := range allNotifiers {
|
if s.NotifyAfter == 0 || s.notifyAfterCount > s.NotifyAfter {
|
||||||
notif := n.Select()
|
for _, n := range allNotifiers {
|
||||||
if notif.CanSend() {
|
notif := n.Select()
|
||||||
log.Infof("Sending Failure notification to: %s!", notif.Method)
|
if notif.CanSend() {
|
||||||
if err := n.OnFailure(s, f); err != nil {
|
log.Infof("Sending Failure notification to: %s!", notif.Method)
|
||||||
log.Errorln(err)
|
if err := n.OnFailure(s, f); err != nil {
|
||||||
|
log.Errorln(err)
|
||||||
|
}
|
||||||
|
s.UserNotified = true
|
||||||
|
s.SuccessNotified = true
|
||||||
|
//s.UpdateNotify.Bool
|
||||||
}
|
}
|
||||||
s.UserNotified = true
|
|
||||||
s.SuccessNotified = true
|
|
||||||
//s.UpdateNotify.Bool
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
s.notifyAfterCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check will run checkHttp for HTTP services and checkTcp for TCP services
|
// Check will run checkHttp for HTTP services and checkTcp for TCP services
|
||||||
|
|
|
@ -2,11 +2,12 @@ package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/statping/statping/types/null"
|
"github.com/statping/statping/types/null"
|
||||||
|
"github.com/statping/statping/utils"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Samples() error {
|
func Samples() error {
|
||||||
createdOn := time.Now().Add(((-24 * 30) * 3) * time.Hour).UTC()
|
createdOn := utils.Now().Add(((-24 * 30) * 3) * time.Hour)
|
||||||
s1 := &Service{
|
s1 := &Service{
|
||||||
Name: "Google",
|
Name: "Google",
|
||||||
Domain: "https://google.com",
|
Domain: "https://google.com",
|
||||||
|
@ -19,6 +20,7 @@ func Samples() error {
|
||||||
GroupId: 1,
|
GroupId: 1,
|
||||||
Permalink: null.NewNullString("google"),
|
Permalink: null.NewNullString("google"),
|
||||||
VerifySSL: null.NewNullBool(true),
|
VerifySSL: null.NewNullBool(true),
|
||||||
|
NotifyAfter: 3,
|
||||||
CreatedAt: createdOn,
|
CreatedAt: createdOn,
|
||||||
}
|
}
|
||||||
if err := s1.Create(); err != nil {
|
if err := s1.Create(); err != nil {
|
||||||
|
@ -36,6 +38,7 @@ func Samples() error {
|
||||||
Order: 2,
|
Order: 2,
|
||||||
Permalink: null.NewNullString("statping_github"),
|
Permalink: null.NewNullString("statping_github"),
|
||||||
VerifySSL: null.NewNullBool(true),
|
VerifySSL: null.NewNullBool(true),
|
||||||
|
NotifyAfter: 1,
|
||||||
CreatedAt: createdOn,
|
CreatedAt: createdOn,
|
||||||
}
|
}
|
||||||
if err := s2.Create(); err != nil {
|
if err := s2.Create(); err != nil {
|
||||||
|
@ -54,6 +57,7 @@ func Samples() error {
|
||||||
Public: null.NewNullBool(true),
|
Public: null.NewNullBool(true),
|
||||||
VerifySSL: null.NewNullBool(true),
|
VerifySSL: null.NewNullBool(true),
|
||||||
GroupId: 2,
|
GroupId: 2,
|
||||||
|
NotifyAfter: 2,
|
||||||
CreatedAt: createdOn,
|
CreatedAt: createdOn,
|
||||||
}
|
}
|
||||||
if err := s3.Create(); err != nil {
|
if err := s3.Create(); err != nil {
|
||||||
|
@ -74,6 +78,7 @@ func Samples() error {
|
||||||
Public: null.NewNullBool(true),
|
Public: null.NewNullBool(true),
|
||||||
VerifySSL: null.NewNullBool(true),
|
VerifySSL: null.NewNullBool(true),
|
||||||
GroupId: 2,
|
GroupId: 2,
|
||||||
|
NotifyAfter: 3,
|
||||||
CreatedAt: createdOn,
|
CreatedAt: createdOn,
|
||||||
}
|
}
|
||||||
if err := s4.Create(); err != nil {
|
if err := s4.Create(); err != nil {
|
||||||
|
|
|
@ -51,6 +51,8 @@ type Service struct {
|
||||||
Checkpoint time.Time `gorm:"-" json:"-"`
|
Checkpoint time.Time `gorm:"-" json:"-"`
|
||||||
SleepDuration time.Duration `gorm:"-" json:"-"`
|
SleepDuration time.Duration `gorm:"-" json:"-"`
|
||||||
LastResponse string `gorm:"-" json:"-"`
|
LastResponse string `gorm:"-" json:"-"`
|
||||||
|
NotifyAfter int64 `gorm:"column:notify_after" json:"notify_after" scope:"user,admin"`
|
||||||
|
notifyAfterCount int64 `gorm:"-" json:"-"`
|
||||||
AllowNotifications null.NullBool `gorm:"default:true;column:allow_notifications" json:"allow_notifications" scope:"user,admin"`
|
AllowNotifications null.NullBool `gorm:"default:true;column:allow_notifications" json:"allow_notifications" scope:"user,admin"`
|
||||||
UserNotified bool `gorm:"-" json:"-"` // True if the User was already notified about a Downtime
|
UserNotified bool `gorm:"-" json:"-"` // True if the User was already notified about a Downtime
|
||||||
UpdateNotify null.NullBool `gorm:"default:true;column:notify_all_changes" json:"notify_all_changes" scope:"user,admin"` // This Variable is a simple copy of `core.CoreApp.UpdateNotify.Bool`
|
UpdateNotify null.NullBool `gorm:"default:true;column:notify_all_changes" json:"notify_all_changes" scope:"user,admin"` // This Variable is a simple copy of `core.CoreApp.UpdateNotify.Bool`
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DeleteDirectory will attempt to delete a directory and all contents inside
|
||||||
|
// DeleteDirectory("assets")
|
||||||
|
func DeleteDirectory(directory string) error {
|
||||||
|
Log.Debugln("removing directory: " + directory)
|
||||||
|
return os.RemoveAll(directory)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CreateDirectory will attempt to create a directory
|
||||||
|
// CreateDirectory("assets")
|
||||||
|
func CreateDirectory(directory string) error {
|
||||||
|
Log.Debugln("creating directory: " + directory)
|
||||||
|
if err := os.Mkdir(directory, os.ModePerm); err != os.ErrExist {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FolderExists will return true if the folder exists
|
||||||
|
func FolderExists(folder string) bool {
|
||||||
|
if _, err := os.Stat(folder); os.IsExist(err) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileExists returns true if a file exists
|
||||||
|
// exists := FileExists("assets/css/base.css")
|
||||||
|
func FileExists(name string) bool {
|
||||||
|
if _, err := os.Stat(name); err != nil {
|
||||||
|
Log.Debugf("file exist: %v (%v)", name, !os.IsNotExist(err))
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteFile will attempt to delete a file
|
||||||
|
// DeleteFile("newfile.json")
|
||||||
|
func DeleteFile(file string) error {
|
||||||
|
Log.Debugln("deleting file: " + file)
|
||||||
|
return os.Remove(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenameDirectory will attempt rename a directory to a new name
|
||||||
|
func RenameDirectory(fromDir string, toDir string) error {
|
||||||
|
Log.Debugln("renaming directory: " + fromDir + "to: " + toDir)
|
||||||
|
return os.Rename(fromDir, toDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SaveFile will create a new file with data inside it
|
||||||
|
// SaveFile("newfile.json", []byte('{"data": "success"}')
|
||||||
|
func SaveFile(filename string, data []byte) error {
|
||||||
|
err := ioutil.WriteFile(filename, data, os.ModePerm)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func OpenFile(filePath string) (string, error) {
|
||||||
|
data, err := ioutil.ReadFile(filePath)
|
||||||
|
return string(data), err
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ func init() {
|
||||||
|
|
||||||
var (
|
var (
|
||||||
httpMetric *Metrics
|
httpMetric *Metrics
|
||||||
StartTime = time.Now()
|
StartTime = Now()
|
||||||
)
|
)
|
||||||
|
|
||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
B = 0x100
|
||||||
|
N = 0x1000
|
||||||
|
BM = 0xff
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewPerlin(alpha, beta float64, n int, seed int64) *Perlin {
|
||||||
|
return NewPerlinRandSource(alpha, beta, n, rand.NewSource(seed))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perlin is the noise generator
|
||||||
|
type Perlin struct {
|
||||||
|
alpha float64
|
||||||
|
beta float64
|
||||||
|
n int
|
||||||
|
|
||||||
|
p [B + B + 2]int
|
||||||
|
g3 [B + B + 2][3]float64
|
||||||
|
g2 [B + B + 2][2]float64
|
||||||
|
g1 [B + B + 2]float64
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPerlinRandSource(alpha, beta float64, n int, source rand.Source) *Perlin {
|
||||||
|
var p Perlin
|
||||||
|
var i int
|
||||||
|
|
||||||
|
p.alpha = alpha
|
||||||
|
p.beta = beta
|
||||||
|
p.n = n
|
||||||
|
|
||||||
|
r := rand.New(source)
|
||||||
|
|
||||||
|
for i = 0; i < B; i++ {
|
||||||
|
p.p[i] = i
|
||||||
|
p.g1[i] = float64((r.Int()%(B+B))-B) / B
|
||||||
|
|
||||||
|
for j := 0; j < 2; j++ {
|
||||||
|
p.g2[i][j] = float64((r.Int()%(B+B))-B) / B
|
||||||
|
}
|
||||||
|
|
||||||
|
normalize2(&p.g2[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
for ; i > 0; i-- {
|
||||||
|
k := p.p[i]
|
||||||
|
j := r.Int() % B
|
||||||
|
p.p[i] = p.p[j]
|
||||||
|
p.p[j] = k
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < B+2; i++ {
|
||||||
|
p.p[B+i] = p.p[i]
|
||||||
|
p.g1[B+i] = p.g1[i]
|
||||||
|
for j := 0; j < 2; j++ {
|
||||||
|
p.g2[B+i][j] = p.g2[i][j]
|
||||||
|
}
|
||||||
|
for j := 0; j < 3; j++ {
|
||||||
|
p.g3[B+i][j] = p.g3[i][j]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &p
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalize2(v *[2]float64) {
|
||||||
|
s := math.Sqrt(v[0]*v[0] + v[1]*v[1])
|
||||||
|
v[0] = v[0] / s
|
||||||
|
v[1] = v[1] / s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Perlin) Noise1D(x float64) float64 {
|
||||||
|
var scale float64 = 1
|
||||||
|
var sum float64
|
||||||
|
px := x
|
||||||
|
|
||||||
|
for i := 0; i < p.n; i++ {
|
||||||
|
val := p.noise1(px)
|
||||||
|
sum += val / scale
|
||||||
|
scale *= p.alpha
|
||||||
|
px *= p.beta
|
||||||
|
}
|
||||||
|
if sum < 0 {
|
||||||
|
sum = sum * -1
|
||||||
|
}
|
||||||
|
return sum
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Perlin) noise1(arg float64) float64 {
|
||||||
|
var vec [1]float64
|
||||||
|
vec[0] = arg
|
||||||
|
|
||||||
|
t := vec[0] + N
|
||||||
|
bx0 := int(t) & BM
|
||||||
|
bx1 := (bx0 + 1) & BM
|
||||||
|
rx0 := t - float64(int(t))
|
||||||
|
rx1 := rx0 - 1.
|
||||||
|
|
||||||
|
sx := sCurve(rx0)
|
||||||
|
u := rx0 * p.g1[p.p[bx0]]
|
||||||
|
v := rx1 * p.g1[p.p[bx1]]
|
||||||
|
|
||||||
|
return lerp(sx, u, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sCurve(t float64) float64 {
|
||||||
|
return t * t * (3. - 2.*t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func lerp(t, a, b float64) float64 {
|
||||||
|
return a + t*(b-a)
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ReplaceTemplate(tmpl string, data interface{}) string {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
var varStr string
|
||||||
|
switch fmt.Sprintf("%T", data) {
|
||||||
|
case "*services.Service":
|
||||||
|
varStr = "Service"
|
||||||
|
case "*failures.Failure":
|
||||||
|
varStr = "Failure"
|
||||||
|
default:
|
||||||
|
varStr = "Object"
|
||||||
|
}
|
||||||
|
injectVars := make(map[string]interface{})
|
||||||
|
injectVars[varStr] = data
|
||||||
|
slackTemp, err := template.New("replacement").Parse(tmpl)
|
||||||
|
if err != nil {
|
||||||
|
Log.Error(err)
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
err = slackTemp.Execute(buf, injectVars)
|
||||||
|
if err != nil {
|
||||||
|
Log.Error(err)
|
||||||
|
return err.Error()
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
|
@ -20,12 +20,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
FlatpickrTime = "2006-01-02 15:04"
|
|
||||||
FlatpickrDay = "2006-01-02"
|
|
||||||
FlatpickrReadable = "Mon, 02 Jan 2006"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Timezoner returns the time.Time with the user set timezone
|
// Timezoner returns the time.Time with the user set timezone
|
||||||
func Timezoner(t time.Time, zone float32) time.Time {
|
func Timezoner(t time.Time, zone float32) time.Time {
|
||||||
zoneInt := float32(3600) * zone
|
zoneInt := float32(3600) * zone
|
||||||
|
|
261
utils/utils.go
261
utils/utils.go
|
@ -23,14 +23,10 @@ import (
|
||||||
"github.com/ararog/timeago"
|
"github.com/ararog/timeago"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"math"
|
|
||||||
"math/rand"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"reflect"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
@ -91,14 +87,6 @@ func Getenv(key string, defaultValue interface{}) interface{} {
|
||||||
return defaultValue
|
return defaultValue
|
||||||
}
|
}
|
||||||
|
|
||||||
func SliceConvert(g []*interface{}) []interface{} {
|
|
||||||
var arr []interface{}
|
|
||||||
for _, v := range g {
|
|
||||||
arr = append(arr, v)
|
|
||||||
}
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToInt converts a int to a string
|
// ToInt converts a int to a string
|
||||||
func ToInt(s interface{}) int64 {
|
func ToInt(s interface{}) int64 {
|
||||||
switch v := s.(type) {
|
switch v := s.(type) {
|
||||||
|
@ -127,25 +115,6 @@ func ToInt(s interface{}) int64 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ConvertInterface will take all the keys/values from an interface and replace all %type.Key from a string
|
|
||||||
// Input: {"name": "%service.Name", "domain": "%service.Domain"}
|
|
||||||
// Output: {"name": "Google DNS", "domain": "8.8.8.8"}
|
|
||||||
func ConvertInterface(in string, obj interface{}) string {
|
|
||||||
if reflect.ValueOf(obj).IsNil() {
|
|
||||||
return in
|
|
||||||
}
|
|
||||||
s := reflect.ValueOf(obj).Elem()
|
|
||||||
typeOfT := s.Type()
|
|
||||||
for i := 0; i < s.NumField(); i++ {
|
|
||||||
f := s.Field(i)
|
|
||||||
find := strings.Split(fmt.Sprintf("%s.%v", typeOfT, typeOfT.Field(i).Name), ".")
|
|
||||||
find[1] = strings.ToLower(find[1])
|
|
||||||
key := strings.Join(find[1:], ".")
|
|
||||||
in = strings.ReplaceAll(in, fmt.Sprintf("%%%v", key), fmt.Sprintf("%v", f.Interface()))
|
|
||||||
}
|
|
||||||
return in
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToString converts a int to a string
|
// ToString converts a int to a string
|
||||||
func ToString(s interface{}) string {
|
func ToString(s interface{}) string {
|
||||||
switch v := s.(type) {
|
switch v := s.(type) {
|
||||||
|
@ -177,117 +146,6 @@ func (t Timestamp) Ago() string {
|
||||||
return got
|
return got
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnderScoreString will return a string that replaces spaces and other characters to underscores
|
|
||||||
// UnderScoreString("Example String")
|
|
||||||
// // example_string
|
|
||||||
func UnderScoreString(str string) string {
|
|
||||||
|
|
||||||
// convert every letter to lower case
|
|
||||||
newStr := strings.ToLower(str)
|
|
||||||
|
|
||||||
// convert all spaces/tab to underscore
|
|
||||||
regExp := regexp.MustCompile("[[:space:][:blank:]]")
|
|
||||||
newStrByte := regExp.ReplaceAll([]byte(newStr), []byte("_"))
|
|
||||||
|
|
||||||
regExp = regexp.MustCompile("`[^a-z0-9]`i")
|
|
||||||
newStrByte = regExp.ReplaceAll(newStrByte, []byte("_"))
|
|
||||||
|
|
||||||
regExp = regexp.MustCompile("[!/']")
|
|
||||||
newStrByte = regExp.ReplaceAll(newStrByte, []byte("_"))
|
|
||||||
|
|
||||||
// and remove underscore from beginning and ending
|
|
||||||
|
|
||||||
newStr = strings.TrimPrefix(string(newStrByte), "_")
|
|
||||||
newStr = strings.TrimSuffix(newStr, "_")
|
|
||||||
|
|
||||||
return newStr
|
|
||||||
}
|
|
||||||
|
|
||||||
// FileExists returns true if a file exists
|
|
||||||
// exists := FileExists("assets/css/base.css")
|
|
||||||
func FileExists(name string) bool {
|
|
||||||
if _, err := os.Stat(name); err != nil {
|
|
||||||
Log.Debugf("file exist: %v (%v)", name, !os.IsNotExist(err))
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteFile will attempt to delete a file
|
|
||||||
// DeleteFile("newfile.json")
|
|
||||||
func DeleteFile(file string) error {
|
|
||||||
Log.Debugln("deleting file: " + file)
|
|
||||||
return os.Remove(file)
|
|
||||||
}
|
|
||||||
|
|
||||||
// RenameDirectory will attempt rename a directory to a new name
|
|
||||||
func RenameDirectory(fromDir string, toDir string) error {
|
|
||||||
Log.Debugln("renaming directory: " + fromDir + "to: " + toDir)
|
|
||||||
return os.Rename(fromDir, toDir)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeleteDirectory will attempt to delete a directory and all contents inside
|
|
||||||
// DeleteDirectory("assets")
|
|
||||||
func DeleteDirectory(directory string) error {
|
|
||||||
Log.Debugln("removing directory: " + directory)
|
|
||||||
return os.RemoveAll(directory)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateDirectory will attempt to create a directory
|
|
||||||
// CreateDirectory("assets")
|
|
||||||
func CreateDirectory(directory string) error {
|
|
||||||
Log.Debugln("creating directory: " + directory)
|
|
||||||
if err := os.Mkdir(directory, os.ModePerm); err != os.ErrExist {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FolderExists will return true if the folder exists
|
|
||||||
func FolderExists(folder string) bool {
|
|
||||||
if _, err := os.Stat(folder); os.IsExist(err) {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func OpenFile(filePath string) (string, error) {
|
|
||||||
data, err := ioutil.ReadFile(filePath)
|
|
||||||
return string(data), err
|
|
||||||
}
|
|
||||||
|
|
||||||
// CopyFile will copy a file to a new directory
|
|
||||||
// CopyFile("source.jpg", "/tmp/source.jpg")
|
|
||||||
func CopyFile(src, dst string) error {
|
|
||||||
Log.Debugln(fmt.Sprintf("copying file: %v to %v", src, dst))
|
|
||||||
in, err := os.Open(src)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer in.Close()
|
|
||||||
|
|
||||||
out, err := os.Create(dst)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer out.Close()
|
|
||||||
|
|
||||||
_, err = io.Copy(out, in)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
return out.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsType will return true if a variable can implement an interface
|
|
||||||
func IsType(n interface{}, obj interface{}) bool {
|
|
||||||
one := reflect.TypeOf(n)
|
|
||||||
two := reflect.ValueOf(obj).Elem()
|
|
||||||
return one.Implements(two.Type())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Command will run a terminal command with 'sh -c COMMAND' and return stdout and errOut as strings
|
// Command will run a terminal command with 'sh -c COMMAND' and return stdout and errOut as strings
|
||||||
// in, out, err := Command("sass assets/scss assets/css/base.css")
|
// in, out, err := Command("sass assets/scss assets/css/base.css")
|
||||||
func Command(name string, args ...string) (string, string, error) {
|
func Command(name string, args ...string) (string, string, error) {
|
||||||
|
@ -362,13 +220,6 @@ func DurationReadable(d time.Duration) string {
|
||||||
return d.String()
|
return d.String()
|
||||||
}
|
}
|
||||||
|
|
||||||
// SaveFile will create a new file with data inside it
|
|
||||||
// SaveFile("newfile.json", []byte('{"data": "success"}')
|
|
||||||
func SaveFile(filename string, data []byte) error {
|
|
||||||
err := ioutil.WriteFile(filename, data, os.ModePerm)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// HttpRequest is a global function to send a HTTP request
|
// HttpRequest is a global function to send a HTTP request
|
||||||
// // url - The URL for HTTP request
|
// // url - The URL for HTTP request
|
||||||
// // method - GET, POST, DELETE, PATCH
|
// // method - GET, POST, DELETE, PATCH
|
||||||
|
@ -446,115 +297,3 @@ func HttpRequest(url, method string, content interface{}, headers []string, body
|
||||||
|
|
||||||
return contents, resp, err
|
return contents, resp, err
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
|
||||||
B = 0x100
|
|
||||||
N = 0x1000
|
|
||||||
BM = 0xff
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewPerlin(alpha, beta float64, n int, seed int64) *Perlin {
|
|
||||||
return NewPerlinRandSource(alpha, beta, n, rand.NewSource(seed))
|
|
||||||
}
|
|
||||||
|
|
||||||
// Perlin is the noise generator
|
|
||||||
type Perlin struct {
|
|
||||||
alpha float64
|
|
||||||
beta float64
|
|
||||||
n int
|
|
||||||
|
|
||||||
p [B + B + 2]int
|
|
||||||
g3 [B + B + 2][3]float64
|
|
||||||
g2 [B + B + 2][2]float64
|
|
||||||
g1 [B + B + 2]float64
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewPerlinRandSource(alpha, beta float64, n int, source rand.Source) *Perlin {
|
|
||||||
var p Perlin
|
|
||||||
var i int
|
|
||||||
|
|
||||||
p.alpha = alpha
|
|
||||||
p.beta = beta
|
|
||||||
p.n = n
|
|
||||||
|
|
||||||
r := rand.New(source)
|
|
||||||
|
|
||||||
for i = 0; i < B; i++ {
|
|
||||||
p.p[i] = i
|
|
||||||
p.g1[i] = float64((r.Int()%(B+B))-B) / B
|
|
||||||
|
|
||||||
for j := 0; j < 2; j++ {
|
|
||||||
p.g2[i][j] = float64((r.Int()%(B+B))-B) / B
|
|
||||||
}
|
|
||||||
|
|
||||||
normalize2(&p.g2[i])
|
|
||||||
}
|
|
||||||
|
|
||||||
for ; i > 0; i-- {
|
|
||||||
k := p.p[i]
|
|
||||||
j := r.Int() % B
|
|
||||||
p.p[i] = p.p[j]
|
|
||||||
p.p[j] = k
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := 0; i < B+2; i++ {
|
|
||||||
p.p[B+i] = p.p[i]
|
|
||||||
p.g1[B+i] = p.g1[i]
|
|
||||||
for j := 0; j < 2; j++ {
|
|
||||||
p.g2[B+i][j] = p.g2[i][j]
|
|
||||||
}
|
|
||||||
for j := 0; j < 3; j++ {
|
|
||||||
p.g3[B+i][j] = p.g3[i][j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &p
|
|
||||||
}
|
|
||||||
|
|
||||||
func normalize2(v *[2]float64) {
|
|
||||||
s := math.Sqrt(v[0]*v[0] + v[1]*v[1])
|
|
||||||
v[0] = v[0] / s
|
|
||||||
v[1] = v[1] / s
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Perlin) Noise1D(x float64) float64 {
|
|
||||||
var scale float64 = 1
|
|
||||||
var sum float64
|
|
||||||
px := x
|
|
||||||
|
|
||||||
for i := 0; i < p.n; i++ {
|
|
||||||
val := p.noise1(px)
|
|
||||||
sum += val / scale
|
|
||||||
scale *= p.alpha
|
|
||||||
px *= p.beta
|
|
||||||
}
|
|
||||||
if sum < 0 {
|
|
||||||
sum = sum * -1
|
|
||||||
}
|
|
||||||
return sum
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *Perlin) noise1(arg float64) float64 {
|
|
||||||
var vec [1]float64
|
|
||||||
vec[0] = arg
|
|
||||||
|
|
||||||
t := vec[0] + N
|
|
||||||
bx0 := int(t) & BM
|
|
||||||
bx1 := (bx0 + 1) & BM
|
|
||||||
rx0 := t - float64(int(t))
|
|
||||||
rx1 := rx0 - 1.
|
|
||||||
|
|
||||||
sx := sCurve(rx0)
|
|
||||||
u := rx0 * p.g1[p.p[bx0]]
|
|
||||||
v := rx1 * p.g1[p.p[bx1]]
|
|
||||||
|
|
||||||
return lerp(sx, u, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sCurve(t float64) float64 {
|
|
||||||
return t * t * (3. - 2.*t)
|
|
||||||
}
|
|
||||||
|
|
||||||
func lerp(t, a, b float64) float64 {
|
|
||||||
return a + t*(b-a)
|
|
||||||
}
|
|
||||||
|
|
|
@ -26,17 +26,6 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConvertInterface(t *testing.T) {
|
|
||||||
type Service struct {
|
|
||||||
Name string
|
|
||||||
Domain string
|
|
||||||
}
|
|
||||||
sample := `{"name": "%service.Name", "domain": "%service.Domain"}`
|
|
||||||
input := &Service{"Test Name", "statping.com"}
|
|
||||||
out := ConvertInterface(sample, input)
|
|
||||||
assert.Equal(t, `{"name": "Test Name", "domain": "statping.com"}`, out)
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestCreateLog(t *testing.T) {
|
func TestCreateLog(t *testing.T) {
|
||||||
err := createLog(Directory)
|
err := createLog(Directory)
|
||||||
assert.Nil(t, err)
|
assert.Nil(t, err)
|
||||||
|
@ -59,6 +48,22 @@ func TestCommand(t *testing.T) {
|
||||||
assert.Empty(t, out)
|
assert.Empty(t, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestReplaceTemplate(t *testing.T) {
|
||||||
|
|
||||||
|
type Object struct {
|
||||||
|
Id int64
|
||||||
|
String string
|
||||||
|
Online bool
|
||||||
|
Example string
|
||||||
|
}
|
||||||
|
example := &Object{
|
||||||
|
1, "this is an example", true, "it should work",
|
||||||
|
}
|
||||||
|
|
||||||
|
result := ReplaceTemplate(`{"id": {{.Object.Id}} }`, example)
|
||||||
|
assert.Equal(t, "{\"id\": 1 }", result)
|
||||||
|
}
|
||||||
|
|
||||||
func TestToInt(t *testing.T) {
|
func TestToInt(t *testing.T) {
|
||||||
assert.Equal(t, int64(55), ToInt("55"))
|
assert.Equal(t, int64(55), ToInt("55"))
|
||||||
assert.Equal(t, int64(55), ToInt(55))
|
assert.Equal(t, int64(55), ToInt(55))
|
||||||
|
@ -73,7 +78,7 @@ func TestToString(t *testing.T) {
|
||||||
dir, _ := time.ParseDuration("55s")
|
dir, _ := time.ParseDuration("55s")
|
||||||
assert.Equal(t, "55s", ToString(dir))
|
assert.Equal(t, "55s", ToString(dir))
|
||||||
assert.Equal(t, "true", ToString(true))
|
assert.Equal(t, "true", ToString(true))
|
||||||
assert.Equal(t, time.Now().Format("Monday January _2, 2006 at 03:04PM"), ToString(time.Now()))
|
assert.Equal(t, Now().Format("Monday January _2, 2006 at 03:04PM"), ToString(Now()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExampleToString() {
|
func ExampleToString() {
|
||||||
|
@ -146,10 +151,6 @@ func TestTimestamp_Ago(t *testing.T) {
|
||||||
assert.Equal(t, "Just now", now.Ago())
|
assert.Equal(t, "Just now", now.Ago())
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnderScoreString(t *testing.T) {
|
|
||||||
assert.Equal(t, "this_is_a_test", UnderScoreString("this is a test"))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestHashPassword(t *testing.T) {
|
func TestHashPassword(t *testing.T) {
|
||||||
assert.Equal(t, 60, len(HashPassword("password123")))
|
assert.Equal(t, 60, len(HashPassword("password123")))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0.90.15
|
0.90.16
|
Loading…
Reference in New Issue