pull/429/head
hunterlong 2020-03-09 08:15:15 -07:00
parent a14fb1f616
commit 71b7254fce
80 changed files with 461 additions and 3217 deletions

View File

@ -20,6 +20,9 @@ up:
down:
docker-compose -f docker-compose.yml -f dev/docker-compose.full.yml down --volumes --remove-orphans
test: clean
go test -v -p=1 -ldflags="-X main.VERSION=dev" -coverprofile=coverage.out ./...
lite: clean
docker build -t hunterlong/statping:dev -f dev/Dockerfile.dev .
docker-compose -f dev/docker-compose.lite.yml down
@ -107,7 +110,7 @@ clean:
rm -rf ./{logs,assets,plugins,*.db,config.yml,.sass-cache,config.yml,statping,build,.sass-cache,index.html,vendor}
rm -rf cmd/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log,*.html,*.json}
rm -rf core/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
rm -rf core/notifier/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
rm -rf types/notifications/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
rm -rf handlers/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
rm -rf notifiers/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}
rm -rf source/{logs,assets,plugins,*.db,config.yml,.sass-cache,*.log}

View File

@ -106,7 +106,7 @@ func catchCLI(args []string) error {
if err != nil {
return err
}
if err = configs.ConnectConfigs(config, false); err != nil {
if err = configs.ConnectConfigs(config); 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, false)
err = configs.ConnectConfigs(config)
if err != nil {
return errors.Wrap(err, "issue connecting to database")
}

View File

@ -6,7 +6,6 @@ import (
"github.com/hunterlong/statping/types/groups"
"github.com/hunterlong/statping/types/hits"
"github.com/hunterlong/statping/types/incidents"
"github.com/hunterlong/statping/types/integrations"
"github.com/hunterlong/statping/types/messages"
"github.com/hunterlong/statping/types/notifications"
"github.com/hunterlong/statping/types/services"
@ -19,5 +18,5 @@ var (
)
func init() {
DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifications.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}, &integrations.Integration{}}
DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifications.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}}
}

View File

@ -19,6 +19,7 @@ import (
"flag"
"fmt"
"github.com/getsentry/sentry-go"
"github.com/hunterlong/statping/notifiers"
"os"
"os/signal"
"syscall"
@ -125,14 +126,41 @@ func main() {
}
}
if err = configs.ConnectConfigs(c, true); err != nil {
if err = configs.ConnectConfigs(c); err != nil {
exit(err)
}
exists := database.DB().HasTable("core")
if !exists {
if err := c.DropDatabase(); err != nil {
exit(errors.Wrap(err, "error dropping database"))
}
if err := configs.CreateDatabase(); err != nil {
exit(errors.Wrap(err, "error creating database"))
}
if err := configs.CreateAdminUser(c); err != nil {
exit(errors.Wrap(err, "error creating default admin user"))
}
if err := configs.TriggerSamples(); err != nil {
exit(errors.Wrap(err, "error creating database"))
}
}
if err := c.MigrateDatabase(); err != nil {
exit(err)
}
log.Infoln("Migrating Notifiers...")
if err := notifiers.Migrate(); err != nil {
exit(errors.Wrap(err, "error migrating notifiers"))
}
log.Infoln("Notifiers Migrated")
if err := mainProcess(); err != nil {
exit(err)
}

View File

@ -9,19 +9,3 @@ type DbObject interface {
type Sampler interface {
Sample() DbObject
}
func MigrateTable(table interface{}) error {
tx := database.Begin()
defer func() {
if r := recover(); r != nil {
tx.Rollback()
}
}()
tx = tx.AutoMigrate(table)
if err := tx.Commit().Error(); err != nil {
return err
}
return nil
}

View File

@ -1,171 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package database
import (
"time"
_ "github.com/jinzhu/gorm/dialects/mysql"
_ "github.com/jinzhu/gorm/dialects/postgres"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
var (
sampleStart = time.Now().Add((-24 * 7) * time.Hour).UTC()
SampleHits = 9900.
)
// InsertSampleHits will create a couple new hits for the sample services
//func InsertSampleHits() error {
// tx := Begin(&hits.Hit{})
// sg := new(sync.WaitGroup)
// for i := int64(1); i <= 5; i++ {
// sg.Add(1)
// service := SelectService(i)
// seed := time.Now().UnixNano()
// log.Infoln(fmt.Sprintf("Adding %v sample hit records to service %v", SampleHits, service.Name))
// createdAt := sampleStart
// p := utils.NewPerlin(2., 2., 10, seed)
// go func(sg *sync.WaitGroup) {
// defer sg.Done()
// for hi := 0.; hi <= float64(SampleHits); hi++ {
// latency := p.Noise1D(hi / 500)
// createdAt = createdAt.Add(60 * time.Second)
// hit := &hits.Hit{
// Service: service.Id,
// CreatedAt: createdAt,
// Latency: latency,
// }
// tx = tx.Create(&hit)
// }
// }(sg)
// }
// sg.Wait()
// if err := tx.Commit().Error(); err != nil {
// log.Errorln(err)
// return types.ErrWrap(err, types.ErrorCreateSampleHits)
// }
// return nil
//}
//func TmpRecords(dbFile string) error {
// var sqlFile = utils.Directory + "/" + dbFile
// if err := utils.CreateDirectory(utils.Directory + "/tmp"); err != nil {
// log.Error(err)
// }
// var tmpSqlFile = utils.Directory + "/tmp/" + dbFile
// SampleHits = 480
//
// var err error
// CoreApp = NewCore()
// CoreApp.Name = "Tester"
// CoreApp.Setup = true
// configs := &types.DbConfig{
// DbConn: "sqlite",
// Project: "Tester",
// Location: utils.Directory,
// SqlFile: sqlFile,
// }
// log.Infoln("saving config.yml in: " + utils.Directory)
// if err := configs.Save(utils.Directory); err != nil {
// log.Error(err)
// }
//
// log.Infoln("loading config.yml from: " + utils.Directory)
// if configs, err = LoadConfigs(); err != nil {
// log.Error(err)
// }
// log.Infoln("connecting to database")
//
// exists := utils.FileExists(tmpSqlFile)
// if exists {
// log.Infoln(tmpSqlFile + " was found, copying the temp database to " + sqlFile)
// if err := utils.DeleteFile(sqlFile); err != nil {
// log.Error(err)
// }
// if err := utils.CopyFile(tmpSqlFile, sqlFile); err != nil {
// log.Error(err)
// }
// log.Infoln("loading config.yml from: " + utils.Directory)
//
// if err := CoreApp.Connect(configs, false, utils.Directory); err != nil {
// log.Error(err)
// }
// log.Infoln("selecting the Core variable")
// if _, err := SelectCore(); err != nil {
// log.Error(err)
// }
// log.Infoln("inserting notifiers into database")
// if err := InsertNotifierDB(); err != nil {
// log.Error(err)
// }
// log.Infoln("inserting integrations into database")
// if err := InsertIntegratorDB(); err != nil {
// log.Error(err)
// }
// log.Infoln("loading all services")
// if _, err := SelectAllServices(false); err != nil {
// return err
// }
// if err := AttachNotifiers(); err != nil {
// log.Error(err)
// }
// if err := AddIntegrations(); err != nil {
// log.Error(err)
// }
// CoreApp.Notifications = notifier.AllCommunications
// return nil
// }
//
// log.Infoln(tmpSqlFile + " not found, creating a new database...")
//
// if err := CoreApp.Connect(configs, false, utils.Directory); err != nil {
// return err
// }
// log.Infoln("creating database")
// if err := CoreApp.CreateDatabase(); err != nil {
// return err
// }
// log.Infoln("migrating database")
// if err := MigrateDatabase(); err != nil {
// return err
// }
// log.Infoln("insert large sample data into database")
// if err := InsertLargeSampleData(); err != nil {
// return err
// }
// log.Infoln("selecting the Core variable")
// if CoreApp, err = SelectCore(); err != nil {
// return err
// }
// log.Infoln("inserting notifiers into database")
// if err := InsertNotifierDB(); err != nil {
// return err
// }
// log.Infoln("inserting integrations into database")
// if err := InsertIntegratorDB(); err != nil {
// return err
// }
// log.Infoln("loading all services")
// if _, err := SelectAllServices(false); err != nil {
// return err
// }
// log.Infoln("copying sql database file to: " + tmpSqlFile)
// if err := utils.CopyFile(sqlFile, tmpSqlFile); err != nil {
// return err
// }
// return err
//}

View File

@ -129,18 +129,6 @@ class Api {
return axios.post('/api/notifier/' + data.method + '/test', data).then(response => (response.data))
}
async integrations() {
return axios.get('/api/integrations').then(response => (response.data))
}
async integration(name) {
return axios.get('/api/integrations/' + name).then(response => (response.data))
}
async integration_save(data) {
return axios.post('/api/integrations/' + data.name, data).then(response => (response.data))
}
async renewApiKeys() {
return axios.get('/api/renew').then(response => (response.data))
}

View File

@ -1,10 +1,6 @@
<template>
<div class="col-12">
<div class="card">
<div class="card-body">
<FormService :in_service="service"/>
</div>
</div>
<FormService :in_service="service"/>
</div>
</template>

View File

@ -87,6 +87,37 @@
</div>
</div>
<div class="row d-none">
<div class="col-12">
<h4 class="mt-5">Github Authentication</h4>
<div class="form-group row d-none">
<label class="col-sm-4 col-form-label">Github Client ID</label>
<div class="col-sm-8">
<input v-model="core.github_clientId" type="text" class="form-control" placeholder="" required>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">Github Client Secret</label>
<div class="col-sm-8">
<input v-model="core.github_clientScret" type="text" class="form-control" placeholder="" required>
</div>
</div>
<div class="form-group row">
<label for="switch-group-public" class="col-sm-4 col-form-label">Enabled</label>
<div class="col-md-8 col-xs-12 mt-1">
<span @click="enabled = !!enabled" class="switch float-left">
<input v-model="enabled" type="checkbox" class="switch" id="switch-group-public" :checked="enabled">
<label for="switch-group-public">Enabled Github Auth</label>
</span>
</div>
</div>
<button @click.prevent="saveSettings" type="submit" class="btn btn-primary btn-block">Save Settings</button>
</div>
</div>
</form>
</template>
@ -99,16 +130,14 @@
return {
core: this.$store.getters.core,
}
},
async mounted() {
},
methods: {
async saveSettings() {
const c = this.core
const coreForm = {
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
}
await Api.core_save(coreForm)
const core = await Api.core()

View File

@ -0,0 +1,80 @@
<template>
<form @submit="updateCore">
<div class="form-group row">
<label class="col-sm-4 col-form-label">Github Client ID</label>
<div class="col-sm-8">
<input v-model="clientId" type="text" class="form-control" placeholder="" required>
</div>
</div>
<div class="form-group row">
<label class="col-sm-4 col-form-label">Github Client Secret</label>
<div class="col-sm-8">
<input v-model="clientSecret" type="text" class="form-control" placeholder="" required>
</div>
</div>
<div class="form-group row">
<label for="switch-group-public" class="col-sm-4 col-form-label">Enabled</label>
<div class="col-md-8 col-xs-12 mt-1">
<span @click="enabled = !!enabled" class="switch float-left">
<input v-model="enabled" type="checkbox" class="switch" id="switch-group-public" :checked="enabled">
<label for="switch-group-public">Enabled Github Auth</label>
</span>
</div>
</div>
<div class="form-group row">
<div class="col-sm-12">
<button @click="updateCore" type="submit" :disabled="loading || group.name === ''" class="btn btn-block" :class="{'btn-primary': !group.id, 'btn-secondary': group.id}">
{{loading ? "Loading..." : group.id ? "Update Group" : "Create Group"}}
</button>
</div>
</div>
<div class="alert alert-danger d-none" id="alerter" role="alert"></div>
</form>
</template>
<script>
import Api from "../API";
export default {
name: 'FormGroup',
props: {
in_group: {
type: Object
},
edit: {
type: Function
}
},
data() {
return {
loading: false,
clientId: "",
clientSecret: "",
enabled: true,
}
},
watch: {
in_group() {
this.group = this.in_group
}
},
methods: {
removeEdit() {
this.group = {}
this.edit(false)
},
async updateCore() {
const g = this.group
const data = {id: g.id, name: g.name, public: g.public}
await Api.core_save(data)
const groups = await Api.groups()
this.$store.commit('setGroups', groups)
this.edit(false)
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

View File

@ -22,6 +22,7 @@
</button>
</div>
</div>
</form>
</template>
@ -37,9 +38,13 @@
auth: {},
loading: false,
error: false,
disabled: true
disabled: true,
ghLoginURL: ""
}
},
mounted() {
this.GHlogin()
},
methods: {
checkForm() {
if (!this.username || !this.password) {
@ -60,6 +65,10 @@
this.$router.push('/dashboard')
}
this.loading = false
},
async GHlogin() {
const core = this.$store.getters.core;
this.ghLoginURL = `https://github.com/login/oauth/authorize?client_id=${core.gh_client_id}&redirect_uri=${core.domain}/oauth/callback&scope=user,repo`
}
}
}

View File

@ -1,6 +1,8 @@
<template>
<form @submit.prevent="saveService">
<h4 class="mb-5 text-muted">Basic Information</h4>
<div class="card contain-card text-black-50 bg-white mb-4">
<div class="card-header">Basic Information</div>
<div class="card-body">
<div class="form-group row">
<label class="col-sm-4 col-form-label">Service Name</label>
<div class="col-sm-8">
@ -37,8 +39,12 @@
<small class="form-text text-muted">Attach this service to a group</small>
</div>
</div>
</div>
</div>
<h4 v-if="service.type !== 'icmp'" class="mt-5 mb-5 text-muted">Request Details</h4>
<div v-if="service.type !== 'icmp'" class="card contain-card text-black-50 bg-white mb-4">
<div class="card-header">Request Details</div>
<div class="card-body">
<div v-if="service.type.match(/^(http)$/)" class="form-group row">
<label class="col-sm-4 col-form-label">Service Check Type</label>
@ -87,8 +93,12 @@
<input v-model="service.port" type="number" name="port" class="form-control" id="service_port" placeholder="8080">
</div>
</div>
</div>
</div>
<h4 class="mt-5 mb-5 text-muted">Additional Options</h4>
<div class="card contain-card text-black-50 bg-white mb-4">
<div class="card-header">Additional Options</div>
<div class="card-body">
<div class="form-group row">
<label for="service_interval" class="col-sm-4 col-form-label">Check Interval (Seconds)</label>
@ -153,6 +163,8 @@
{{service.id ? "Update Service" : "Create Service"}}
</button>
</div>
</div>
</div>
</div>
<div class="alert alert-danger d-none" id="alerter" role="alert"></div>
</form>

View File

@ -22,12 +22,6 @@
<span v-if="notifier.enabled" class="badge badge-pill float-right mt-1" :class="{'badge-success': !liClass(`v-pills-${notifier.method.toLowerCase()}-tab`), 'badge-light': liClass(`v-pills-${notifier.method.toLowerCase()}-tab`), 'text-dark': liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}">ON</span>
</a>
<h6 class="mt-4 text-muted">Integrations <span class="badge badge-secondary float-right">BETA</span></h6>
<a v-for="(integration, index) in $store.getters.integrations" v-bind:key="`${integration.name}_${index}`" @click.prevent="changeTab" class="nav-link text-capitalize" v-bind:class="{active: liClass(`v-pills-integration-${integration.name}`)}" v-bind:id="`v-pills-integration-${integration.name}`" data-toggle="pill" v-bind:href="`#v-pills-integration-${integration.name}`" role="tab" :aria-controls="`v-pills-integration-${integration.name}`" aria-selected="false">
<font-awesome-icon :icon="iconName(integration.name)" class="mr-2"/> {{integration.full_name}}
</a>
</div>
</div>
<div class="col-md-9 col-sm-12">
@ -90,10 +84,6 @@
<Notifier :notifier="notifier"/>
</div>
<div v-for="(integration, index) in $store.getters.integrations" v-bind:key="`${integration.name}_${index}`" class="tab-pane fade" v-bind:class="{active: liClass(`v-pills-integration-${integration.name}`), show: liClass(`v-pills-integration-${integration.name}`)}" v-bind:id="`v-pills-integration-${integration.name}`" role="tabpanel">
<FormIntegration :integration="integration"/>
</div>
</div>
</div>

1
go.mod
View File

@ -33,6 +33,7 @@ require (
github.com/tatsushid/go-fastping v0.0.0-20160109021039-d7bb493dee3e
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073
golang.org/x/net v0.0.0-20200301022130-244492dfa37a // indirect
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
gopkg.in/mail.v2 v2.3.1 // indirect

4
go.sum
View File

@ -1,3 +1,4 @@
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8=
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
@ -300,6 +301,7 @@ golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190327091125-710a502c58a2/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
@ -310,6 +312,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=

View File

@ -1,53 +0,0 @@
package handlers
import (
"encoding/json"
"github.com/gorilla/mux"
"github.com/hunterlong/statping/types/integrations"
"net/http"
)
func findIntegration(r *http.Request) (*integrations.Integration, string, error) {
vars := mux.Vars(r)
name := vars["name"]
intgr, err := integrations.Find(name)
if err != nil {
return nil, "", err
}
return intgr, name, nil
}
func apiAllIntegrationsHandler(w http.ResponseWriter, r *http.Request) {
inte := integrations.All()
returnJson(inte, w, r)
}
func apiIntegrationViewHandler(w http.ResponseWriter, r *http.Request) {
intgr, _, err := findIntegration(r)
if err != nil {
sendErrorJson(err, w, r)
return
}
returnJson(intgr, w, r)
}
func apiIntegrationHandler(w http.ResponseWriter, r *http.Request) {
intgr, _, err := findIntegration(r)
if err != nil {
sendErrorJson(err, w, r)
return
}
decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&intgr); err != nil {
sendErrorJson(err, w, r)
return
}
if err := intgr.Update(); err != nil {
sendErrorJson(err, w, r)
return
}
returnJson(intgr, w, r)
}

View File

@ -27,12 +27,8 @@ import (
)
func apiNotifiersHandler(w http.ResponseWriter, r *http.Request) {
var notifiers []*notifications.Notification
for _, n := range core.App.Notifications {
notif := n.(notifications.Notifier)
notifiers = append(notifiers, notif.Select())
}
returnJson(notifiers, w, r)
all := notifications.All()
returnJson(all, w, r)
}
func apiNotifierGetHandler(w http.ResponseWriter, r *http.Request) {

View File

@ -104,7 +104,8 @@ func prometheusHandler(w http.ResponseWriter, r *http.Request) {
}
for _, notif := range notifications.All() {
for _, n := range notifications.All() {
notif := n.Select()
PrometheusComment(fmt.Sprintf("Notifier %s:", notif.Method))
enabled := 0
if notif.Enabled.Bool {

View File

@ -87,6 +87,7 @@ func Router() *mux.Router {
// API Routes
r.Handle("/api", scoped(apiIndexHandler))
//r.Handle("/oauth/callback", http.HandlerFunc(OAuthRedirect))
api.Handle("/api/login", http.HandlerFunc(apiLoginHandler)).Methods("POST")
r.Handle("/api/setup", http.HandlerFunc(processSetupHandler)).Methods("POST")
api.Handle("/api/logout", http.HandlerFunc(logoutHandler))
@ -103,11 +104,6 @@ func Router() *mux.Router {
api.Handle("/api/theme/create", authenticated(apiThemeCreateHandler, false)).Methods("GET")
api.Handle("/api/theme", authenticated(apiThemeRemoveHandler, false)).Methods("DELETE")
// API INTEGRATIONS Routes
api.Handle("/api/integrations", authenticated(apiAllIntegrationsHandler, false)).Methods("GET")
api.Handle("/api/integrations/{name}", authenticated(apiIntegrationViewHandler, false)).Methods("GET")
api.Handle("/api/integrations/{name}", authenticated(apiIntegrationHandler, false)).Methods("POST")
// API GROUPS Routes
api.Handle("/api/groups", scoped(apiAllGroupHandler)).Methods("GET")
api.Handle("/api/groups", authenticated(apiCreateGroupHandler, false)).Methods("POST")

View File

@ -81,6 +81,7 @@ func apiCreateServiceHandler(w http.ResponseWriter, r *http.Request) {
sendErrorJson(err, w, r)
return
}
go services.ServiceCheckQueue(service, true)
sendJsonAction(service, "create", w, r)
}

View File

@ -17,6 +17,8 @@ package handlers
import (
"errors"
"github.com/hunterlong/statping/database"
"github.com/hunterlong/statping/notifiers"
"github.com/hunterlong/statping/types/configs"
"github.com/hunterlong/statping/types/core"
"github.com/hunterlong/statping/types/null"
@ -43,7 +45,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
log.WithFields(utils.ToFields(core.App, confgs)).Debugln("new configs posted")
if err = configs.ConnectConfigs(confgs, true); err != nil {
if err = configs.ConnectConfigs(confgs); err != nil {
log.Errorln(err)
if err := confgs.Delete(); err != nil {
log.Errorln(err)
@ -60,11 +62,40 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
return
}
exists := database.DB().HasTable("core")
if !exists {
if err := confgs.DropDatabase(); err != nil {
sendErrorJson(err, w, r)
return
}
if err := configs.CreateDatabase(); err != nil {
sendErrorJson(err, w, r)
return
}
if err := configs.CreateAdminUser(confgs); err != nil {
sendErrorJson(err, w, r)
return
}
if err := configs.TriggerSamples(); err != nil {
sendErrorJson(err, w, r)
return
}
}
if err = confgs.MigrateDatabase(); err != nil {
sendErrorJson(err, w, r)
return
}
log.Infoln("Migrating Notifiers...")
if err := notifiers.Migrate(); err != nil {
sendErrorJson(err, w, r)
return
}
c := &core.Core{
Name: "Statping Sample Data",
Description: "This data is only used to testing",
@ -86,15 +117,6 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
core.App = c
//if sample {
// log.Infoln("Adding sample data into new database")
// if err = configs.TriggerSamples(); err != nil {
// log.Errorln(err)
// sendErrorJson(err, w, r)
// return
// }
//}
log.Infoln("Initializing new Statping instance")
if err := core.InitApp(); err != nil {
log.Errorln(err)

View File

@ -1,132 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package integrators
import (
"bytes"
"encoding/csv"
"errors"
"fmt"
"github.com/hunterlong/statping/types/integrations"
"github.com/hunterlong/statping/types/null"
"github.com/hunterlong/statping/types/services"
"github.com/hunterlong/statping/utils"
"strconv"
"time"
)
const requiredSize = 17
type csvIntegration struct {
*integrations.Integration
}
var CsvIntegrator = &csvIntegration{&integrations.Integration{
ShortName: "csv",
Name: "CSV File",
Icon: "<i class=\"fas fa-file-csv\"></i>",
Description: "Import multiple services from a CSV file. Please have your CSV file formatted with the correct amount of columns based on the <a href=\"https://raw.githubusercontent.com/hunterlong/statping/master/source/tmpl/bulk_import.csv\">example file on Github</a>.",
Fields: []*integrations.IntegrationField{
{
Name: "input",
Type: "textarea",
Description: "",
},
},
}}
var csvData [][]string
func (t *csvIntegration) Get() *integrations.Integration {
return t.Integration
}
func (t *csvIntegration) List() ([]*services.Service, error) {
data := Value(t, "input").(string)
buf := bytes.NewReader([]byte(data))
r := csv.NewReader(buf)
records, err := r.ReadAll()
if err != nil {
return nil, err
}
var services []*services.Service
for k, v := range records[1:] {
s, err := commaToService(v)
if err != nil {
log.Errorf("error on line %v: %v", k, err)
continue
}
services = append(services, s)
}
return services, nil
}
// commaToService will convert a CSV comma delimited string slice to a Service type
// this function is used for the bulk import services feature
func commaToService(s []string) (*services.Service, error) {
if len(s) != requiredSize {
err := fmt.Errorf("file has %v columns of data, not the expected amount of %v columns for a service", len(s), requiredSize)
return nil, err
}
interval, err := time.ParseDuration(s[4])
if err != nil {
return nil, errors.New("could not parse internal duration: " + s[4])
}
timeout, err := time.ParseDuration(s[9])
if err != nil {
return nil, errors.New("could not parse timeout duration: " + s[9])
}
allowNotifications, err := strconv.ParseBool(s[11])
if err != nil {
return nil, errors.New("could not parse allow notifications boolean: " + s[11])
}
public, err := strconv.ParseBool(s[12])
if err != nil {
return nil, errors.New("could not parse public boolean: " + s[12])
}
verifySsl, err := strconv.ParseBool(s[16])
if err != nil {
return nil, errors.New("could not parse verifiy SSL boolean: " + s[16])
}
newService := &services.Service{
Name: s[0],
Domain: s[1],
Expected: null.NewNullString(s[2]),
ExpectedStatus: int(utils.ToInt(s[3])),
Interval: int(utils.ToInt(interval.Seconds())),
Type: s[5],
Method: s[6],
PostData: null.NewNullString(s[7]),
Port: int(utils.ToInt(s[8])),
Timeout: int(utils.ToInt(timeout.Seconds())),
AllowNotifications: null.NewNullBool(allowNotifications),
Public: null.NewNullBool(public),
GroupId: int(utils.ToInt(s[13])),
Headers: null.NewNullString(s[14]),
Permalink: null.NewNullString(s[15]),
VerifySSL: null.NewNullBool(verifySsl),
}
return newService, nil
}

View File

@ -1,43 +0,0 @@
package integrators
import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"io/ioutil"
"testing"
)
func TestCsvFileIntegration(t *testing.T) {
data, err := ioutil.ReadFile("testdata/bulk_import.csv")
require.Nil(t, err)
t.Run("Set Field Value", func(t *testing.T) {
formPost := map[string][]string{}
formPost["input"] = []string{string(data)}
_, err = SetFields(CsvIntegrator, formPost)
require.Nil(t, err)
})
t.Run("Get Field Value", func(t *testing.T) {
value := Value(CsvIntegrator, "input").(string)
assert.Equal(t, string(data), value)
})
t.Run("List Services from CSV File", func(t *testing.T) {
services, err := CsvIntegrator.List()
require.Nil(t, err)
assert.Equal(t, 10, len(services))
})
t.Run("Confirm Services from CSV File", func(t *testing.T) {
services, err := CsvIntegrator.List()
require.Nil(t, err)
assert.Equal(t, "Bulk Upload", services[0].Name)
assert.Equal(t, "http://google.com", services[0].Domain)
assert.Equal(t, 60, services[0].Interval)
for _, s := range services {
t.Log(s)
}
})
}

View File

@ -1,104 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package integrators
import (
"context"
dTypes "github.com/docker/docker/api/types"
"github.com/docker/docker/client"
"github.com/hunterlong/statping/types/integrations"
"github.com/hunterlong/statping/types/services"
"os"
)
type dockerIntegration struct {
*integrations.Integration
}
var DockerIntegrator = &dockerIntegration{&integrations.Integration{
ShortName: "docker",
Name: "Docker",
Icon: "<i class=\"fab fa-docker\"></i>",
Description: `Import multiple services from Docker by attaching the unix socket to Statping.
You can also do this in Docker by setting <u>-v /var/run/docker.sock:/var/run/docker.sock</u> in the Statping Docker container.
All of the containers with open TCP/UDP ports will be listed for you to choose which services you want to add. If you running Statping inside of a container,
this container must be attached to all networks you want to communicate with.`,
Fields: []*integrations.IntegrationField{
{
Name: "path",
Description: "The absolute path to the Docker unix socket",
Type: "text",
Value: client.DefaultDockerHost,
},
{
Name: "version",
Description: "Version number of Docker server",
Type: "text",
Value: client.DefaultVersion,
},
},
}}
var cli *client.Client
func (t *dockerIntegration) Get() *integrations.Integration {
return t.Integration
}
func (t *dockerIntegration) List() ([]*services.Service, error) {
var err error
path := Value(t, "path").(string)
version := Value(t, "version").(string)
os.Setenv("DOCKER_HOST", path)
os.Setenv("DOCKER_VERSION", version)
cli, err = client.NewEnvClient()
if err != nil {
return nil, err
}
defer cli.Close()
var srvs []*services.Service
containers, err := cli.ContainerList(context.Background(), dTypes.ContainerListOptions{})
if err != nil {
return nil, err
}
for _, container := range containers {
if container.State != "running" {
continue
}
for _, v := range container.Ports {
if v.IP == "" {
continue
}
service := &services.Service{
Name: container.Names[0][1:],
Domain: v.IP,
Type: v.Type,
Port: int(v.PublicPort),
Interval: 60,
Timeout: 2,
}
srvs = append(srvs, service)
}
}
return srvs, nil
}

View File

@ -1,42 +0,0 @@
package integrators
import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)
func TestDockerIntegration(t *testing.T) {
t.SkipNow()
t.Run("Set Field Value", func(t *testing.T) {
formPost := map[string][]string{}
formPost["path"] = []string{"unix:///var/run/docker.sock"}
formPost["version"] = []string{"1.25"}
_, err := SetFields(CsvIntegrator, formPost)
require.Nil(t, err)
})
t.Run("Get Field Value", func(t *testing.T) {
path := Value(DockerIntegrator, "path").(string)
version := Value(DockerIntegrator, "version").(string)
assert.Equal(t, "unix:///var/run/docker.sock", path)
assert.Equal(t, "1.25", version)
})
t.Run("List Services from Docker", func(t *testing.T) {
services, err := DockerIntegrator.List()
require.Nil(t, err)
assert.Equal(t, 0, len(services))
})
t.Run("Confirm Services from Docker", func(t *testing.T) {
services, err := DockerIntegrator.List()
require.Nil(t, err)
for _, s := range services {
t.Log(s)
}
})
}

View File

@ -1,169 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package integrators
import (
"encoding/json"
"errors"
"fmt"
"github.com/hunterlong/statping/database"
"github.com/hunterlong/statping/types/integrations"
"github.com/hunterlong/statping/utils"
)
var (
Integrations []integrations.Integrator
log = utils.Log.WithField("type", "integration")
db database.Database
)
//func init() {
// Integrations = append(Integrations,
// CsvIntegrator,
// DockerIntegrator,
// TraefikIntegrator,
// )
//}
func init() {
AddIntegrations(
CsvIntegrator,
TraefikIntegrator,
DockerIntegrator,
)
}
// integrationsDb returns the 'integrations' database column
func integrationsDb() database.Database {
return db.Model(&integrations.Integration{})
}
// SetDB is called by core to inject the database for a integrator to use
func SetDB(d database.Database) {
db = d
}
func Value(intg integrations.Integrator, fieldName string) interface{} {
for _, v := range intg.Get().Fields {
if fieldName == v.Name {
return v.Value
}
}
return nil
}
func Update(integrator *integrations.Integration) error {
fields := FieldsToJson(integrator)
fmt.Println(fields)
set := db.Model(&integrations.Integration{}).Where("name = ?", integrator.Name)
set.Set("enabled", integrator.Enabled)
set.Set("fields", fields)
return set.Error()
}
func FieldsToJson(integrator *integrations.Integration) string {
jsonData := make(map[string]interface{})
for _, v := range integrator.Fields {
jsonData[v.Name] = v.Value
}
data, _ := json.Marshal(jsonData)
return string(data)
}
func JsonToFields(intg integrations.Integrator, input string) []*integrations.IntegrationField {
integrator := intg.Get()
var jsonData map[string]interface{}
json.Unmarshal([]byte(input), &jsonData)
for _, v := range integrator.Fields {
v.Value = jsonData[v.Name]
}
return integrator.Fields
}
func SetFields(intg integrations.Integrator, data map[string][]string) (*integrations.Integration, error) {
i := intg.Get()
for _, v := range i.Fields {
if data[v.Name] != nil {
v.Value = data[v.Name][0]
}
}
return i, nil
}
func Find(name string) (integrations.Integrator, error) {
for _, i := range Integrations {
obj := i.Get()
if obj.ShortName == name {
return i, nil
}
}
return nil, errors.New(name + " not found")
}
// db will return the notifier database column/record
func integratorDb(n *integrations.Integration) database.Database {
return db.Model(&integrations.Integration{}).Where("name = ?", n.Name).Find(n)
}
// isInDatabase returns true if the integration has already been installed
func isInDatabase(i integrations.Integrator) bool {
inDb := integratorDb(i.Get()).RecordNotFound()
return !inDb
}
// SelectIntegration returns the Notification struct from the database
func SelectIntegration(i integrations.Integrator) (*integrations.Integration, error) {
integration := i.Get()
err := db.Model(&integrations.Integration{}).Where("name = ?", integration.Name).Scan(&integration)
return integration, err.Error()
}
// AddIntegrations accept a Integrator interface to be added into the array
func AddIntegrations(inte ...integrations.Integrator) error {
for _, i := range inte {
if utils.IsType(i, new(integrations.Integrator)) {
Integrations = append(Integrations, i)
err := install(i)
if err != nil {
return err
}
} else {
return errors.New("notifier does not have the required methods")
}
}
return nil
}
// install will check the database for the notification, if its not inserted it will insert a new record for it
func install(i integrations.Integrator) error {
_, err := insertDatabase(i)
if err != nil {
log.Errorln(err)
return err
}
return err
}
// insertDatabase will create a new record into the database for the integrator
func insertDatabase(i integrations.Integrator) (string, error) {
integrator := i.Get()
query := db.FirstOrCreate(integrator)
if query.Error() != nil {
return "", query.Error()
}
return integrator.Name, query.Error()
}

View File

@ -1,11 +0,0 @@
name,domain,expected,expected_status,interval,type,method,post_data,port,timeout,order,allow_notifications,public,group_id,headers,permalink,verify_ssl
Bulk Upload,http://google.com,,200,60s,http,get,,,60s,1,TRUE,TRUE,,Authorization=example,bulk_example,FALSE
JSON Post,https://jsonplaceholder.typicode.com/posts,,200,1m,http,post,"{""id"": 1, ""title"": 'foo', ""body"": 'bar', ""userId"": 1}",,15s,2,TRUE,TRUE,,Content-Type=application/json,json_post_example,FALSE
Google DNS,8.8.8.8,,,60s,tcp,,,53,10s,3,TRUE,TRUE,,,google_dns_example,FALSE
Google DNS UDP,8.8.8.8,,,60s,udp,,,53,10s,4,TRUE,TRUE,,,google_dns_udp_example,FALSE
Statping Demo Page,https://demo.statping.com/health,"(\""online\"": true)",200,30s,http,get,,,10s,5,TRUE,TRUE,,,demo_link,FALSE
Statping MySQL Page,https://mysql.statping.com/health,"(\""online\"": true)",200,30s,http,get,,,10s,6,TRUE,TRUE,,,mysql_demo_link,FALSE
Statping SQLite Page,https://sqlite.statping.com/health,"(\""online\"": true)",200,30s,http,get,,,10s,7,TRUE,TRUE,,,sqlite_demo_link,FALSE
Token Balance,https://status.tokenbalance.com/health,"(\""online\"": true)",200,30s,http,get,,,10s,8,TRUE,TRUE,,,token_balance,FALSE
CloudFlare DNS,1.1.1.1,,,60s,tcp,,,53,10s,9,TRUE,TRUE,,,cloudflare_dns_example,FALSE
Verisign DNS,64.6.64.4,,,60s,tcp,,,53,10s,10,TRUE,TRUE,,,verisign_dns_example,FALSE
1 name domain expected expected_status interval type method post_data port timeout order allow_notifications public group_id headers permalink verify_ssl
2 Bulk Upload http://google.com 200 60s http get 60s 1 TRUE TRUE Authorization=example bulk_example FALSE
3 JSON Post https://jsonplaceholder.typicode.com/posts 200 1m http post {"id": 1, "title": 'foo', "body": 'bar', "userId": 1} 15s 2 TRUE TRUE Content-Type=application/json json_post_example FALSE
4 Google DNS 8.8.8.8 60s tcp 53 10s 3 TRUE TRUE google_dns_example FALSE
5 Google DNS UDP 8.8.8.8 60s udp 53 10s 4 TRUE TRUE google_dns_udp_example FALSE
6 Statping Demo Page https://demo.statping.com/health (\"online\": true) 200 30s http get 10s 5 TRUE TRUE demo_link FALSE
7 Statping MySQL Page https://mysql.statping.com/health (\"online\": true) 200 30s http get 10s 6 TRUE TRUE mysql_demo_link FALSE
8 Statping SQLite Page https://sqlite.statping.com/health (\"online\": true) 200 30s http get 10s 7 TRUE TRUE sqlite_demo_link FALSE
9 Token Balance https://status.tokenbalance.com/health (\"online\": true) 200 30s http get 10s 8 TRUE TRUE token_balance FALSE
10 CloudFlare DNS 1.1.1.1 60s tcp 53 10s 9 TRUE TRUE cloudflare_dns_example FALSE
11 Verisign DNS 64.6.64.4 60s tcp 53 10s 10 TRUE TRUE verisign_dns_example FALSE

View File

@ -1,127 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package integrators
import (
"encoding/json"
"github.com/hunterlong/statping/types/integrations"
"github.com/hunterlong/statping/types/services"
"github.com/hunterlong/statping/utils"
"net/url"
"time"
)
type traefikIntegration struct {
*integrations.Integration
}
var TraefikIntegrator = &traefikIntegration{&integrations.Integration{
ShortName: "traefik",
Name: "Traefik",
Icon: "<i class=\"fas fa-network-wired\"></i>",
Description: ``,
Fields: []*integrations.IntegrationField{
{
Name: "endpoint",
Description: "The URL for the traefik API Endpoint",
Type: "text",
Value: "http://localhost:8080",
},
{
Name: "username",
Description: "Username for HTTP Basic Authentication",
Type: "text",
},
{
Name: "password",
Description: "Password for HTTP Basic Authentication",
Type: "password",
},
},
}}
func (t *traefikIntegration) Get() *integrations.Integration {
return t.Integration
}
func (t *traefikIntegration) List() ([]*services.Service, error) {
var err error
var services []*services.Service
endpoint := Value(t, "endpoint").(string)
httpServices, err := fetchMethod(endpoint, "http")
if err != nil {
return nil, err
}
services = append(services, httpServices...)
tcpServices, err := fetchMethod(endpoint, "tcp")
if err != nil {
return nil, err
}
services = append(services, tcpServices...)
return services, err
}
func fetchMethod(endpoint, method string) ([]*services.Service, error) {
var traefikServices []traefikService
var srvs []*services.Service
d, _, err := utils.HttpRequest(endpoint+"/api/"+method+"/services", "GET", nil, []string{}, nil, 10*time.Second, false)
if err != nil {
return nil, err
}
if err := json.Unmarshal(d, &traefikServices); err != nil {
return nil, err
}
for _, s := range traefikServices {
log.Infoln(s)
for _, l := range s.LoadBalancer.Servers {
url, err := url.Parse(l.URL)
if err != nil {
return nil, err
}
service := &services.Service{
Name: s.Name,
Domain: url.Hostname(),
Port: int(utils.ToInt(url.Port())),
Type: method,
Interval: 60,
Timeout: 2,
}
srvs = append(srvs, service)
}
}
return srvs, err
}
type traefikService struct {
Status string `json:"status"`
UsedBy []string `json:"usedBy"`
Name string `json:"name"`
Provider string `json:"provider"`
LoadBalancer struct {
Servers []struct {
URL string `json:"url"`
} `json:"servers"`
PassHostHeader bool `json:"passHostHeader"`
} `json:"loadBalancer,omitempty"`
}

View File

@ -1,27 +0,0 @@
package integrators
import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)
func TestTraefikIntegration(t *testing.T) {
t.SkipNow()
t.Run("List Services from Traefik", func(t *testing.T) {
services, err := TraefikIntegrator.List()
require.Nil(t, err)
assert.NotEqual(t, 0, len(services))
})
t.Run("Confirm Services from Traefik", func(t *testing.T) {
services, err := TraefikIntegrator.List()
require.Nil(t, err)
for _, s := range services {
t.Log(s)
}
})
}

View File

@ -25,12 +25,14 @@ import (
"time"
)
var _ notifications.Notifier = (*commandLine)(nil)
type commandLine struct {
*notifications.Notification
}
var Command = &commandLine{&notifications.Notification{
Method: "Command",
Method: "command",
Title: "Shell Command",
Description: "Shell Command allows you to run a customized shell/bash Command on the local machine it's running on.",
Author: "Hunter Long",

View File

@ -1,102 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package notifiers
import (
"github.com/hunterlong/statping/types/notifications"
"github.com/stretchr/testify/assert"
"testing"
"time"
)
const (
commandTest = "curl -I https://statping.com/"
)
func TestCommandNotifier(t *testing.T) {
t.Parallel()
Command.Host = "sh"
Command.Var1 = commandTest
Command.Var2 = commandTest
currentCount = CountNotifiers()
t.Run("Load Command", func(t *testing.T) {
Command.Host = "sh"
Command.Var1 = commandTest
Command.Var2 = commandTest
Command.Delay = time.Duration(100 * time.Millisecond)
Command.Limits = 99
err := AddNotifiers(Command)
assert.Nil(t, err)
assert.Equal(t, "Hunter Long", Command.Author)
assert.Equal(t, "sh", Command.Host)
assert.Equal(t, commandTest, Command.Var1)
assert.Equal(t, commandTest, Command.Var2)
})
t.Run("Command Notifier Tester", func(t *testing.T) {
assert.True(t, Command.CanTest())
})
t.Run("Command Within Limits", func(t *testing.T) {
ok, err := Command.WithinLimits()
assert.Nil(t, err)
assert.True(t, ok)
})
t.Run("Command OnFailure", func(t *testing.T) {
Command.OnFailure(TestService, TestFailure)
assert.Equal(t, 1, len(Command.Queue))
})
t.Run("Command OnSuccess", func(t *testing.T) {
Command.OnSuccess(TestService)
assert.Equal(t, 1, len(Command.Queue))
})
t.Run("Command OnSuccess Again", func(t *testing.T) {
Command.OnSuccess(TestService)
assert.Equal(t, 1, len(Command.Queue))
go notifications.Queue(Command)
time.Sleep(20 * time.Second)
assert.Equal(t, 0, len(Command.Queue))
})
t.Run("Command Within Limits again", func(t *testing.T) {
ok, err := Command.WithinLimits()
assert.Nil(t, err)
assert.True(t, ok)
})
t.Run("Command Send", func(t *testing.T) {
Command.Send(commandTest)
assert.Equal(t, 0, len(Command.Queue))
})
t.Run("Command Test", func(t *testing.T) {
Command.OnTest()
})
t.Run("Command Queue", func(t *testing.T) {
go notifications.Queue(Command)
time.Sleep(5 * time.Second)
assert.Equal(t, "sh", Command.Host)
assert.Equal(t, commandTest, Command.Var1)
assert.Equal(t, commandTest, Command.Var2)
assert.Equal(t, 0, len(Command.Queue))
})
}

View File

@ -28,6 +28,8 @@ import (
"time"
)
var _ notifications.Notifier = (*discord)(nil)
type discord struct {
*notifications.Notification
}

View File

@ -1,99 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package notifiers
import (
"github.com/hunterlong/statping/types/notifications"
"github.com/stretchr/testify/assert"
"os"
"testing"
"time"
)
var (
DISCORD_URL = os.Getenv("DISCORD_URL")
discordMessage = `{"content": "The discord notifier on Statping has been tested!"}`
)
func init() {
DISCORD_URL = os.Getenv("DISCORD_URL")
Discorder.Host = DISCORD_URL
}
func TestDiscordNotifier(t *testing.T) {
t.Parallel()
if DISCORD_URL == "" {
t.Log("discord notifier testing skipped, missing DISCORD_URL environment variable")
t.SkipNow()
}
currentCount = CountNotifiers()
t.Run("Load discord", func(t *testing.T) {
Discorder.Host = DISCORD_URL
Discorder.Delay = time.Duration(100 * time.Millisecond)
err := AddNotifiers(Discorder)
assert.Nil(t, err)
assert.Equal(t, "Hunter Long", Discorder.Author)
assert.Equal(t, DISCORD_URL, Discorder.Host)
})
t.Run("discord Notifier Tester", func(t *testing.T) {
assert.True(t, Discorder.CanTest())
})
t.Run("discord Within Limits", func(t *testing.T) {
ok, err := Discorder.WithinLimits()
assert.Nil(t, err)
assert.True(t, ok)
})
t.Run("discord OnFailure", func(t *testing.T) {
Discorder.OnFailure(TestService, TestFailure)
assert.Equal(t, 1, len(Discorder.Queue))
})
t.Run("discord OnSuccess", func(t *testing.T) {
Discorder.OnSuccess(TestService)
assert.Equal(t, 1, len(Discorder.Queue))
})
t.Run("discord Check Back Online", func(t *testing.T) {
assert.True(t, TestService.Online)
})
t.Run("discord OnSuccess Again", func(t *testing.T) {
Discorder.OnSuccess(TestService)
assert.Equal(t, 1, len(Discorder.Queue))
})
t.Run("discord Send", func(t *testing.T) {
err := Discorder.Send(discordMessage)
assert.Nil(t, err)
})
t.Run("discord Test", func(t *testing.T) {
err := Discorder.OnTest()
assert.Nil(t, err)
})
t.Run("discord Queue", func(t *testing.T) {
go notifications.Queue(Discorder)
time.Sleep(1 * time.Second)
assert.Equal(t, DISCORD_URL, Discorder.Host)
assert.Equal(t, 0, len(Discorder.Queue))
})
}

View File

@ -29,6 +29,8 @@ import (
"time"
)
var _ notifications.Notifier = (*email)(nil)
const (
mainEmailTemplate = `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns="http://www.w3.org/1999/xhtml">

View File

@ -1,136 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package notifiers
import (
"fmt"
"github.com/hunterlong/statping/types/notifications"
"github.com/hunterlong/statping/utils"
"github.com/stretchr/testify/assert"
"os"
"testing"
"time"
)
var (
EMAIL_HOST string
EMAIL_USER string
EMAIL_PASS string
EMAIL_OUTGOING string
EMAIL_SEND_TO string
EMAIL_PORT int64
)
var testEmail *emailOutgoing
func init() {
EMAIL_HOST = os.Getenv("EMAIL_HOST")
EMAIL_USER = os.Getenv("EMAIL_USER")
EMAIL_PASS = os.Getenv("EMAIL_PASS")
EMAIL_OUTGOING = os.Getenv("EMAIL_OUTGOING")
EMAIL_SEND_TO = os.Getenv("EMAIL_SEND_TO")
EMAIL_PORT = utils.ToInt(os.Getenv("EMAIL_PORT"))
Emailer.Host = EMAIL_HOST
Emailer.Username = EMAIL_USER
Emailer.Password = EMAIL_PASS
Emailer.Var1 = EMAIL_OUTGOING
Emailer.Var2 = EMAIL_SEND_TO
Emailer.Port = int(EMAIL_PORT)
}
func TestEmailNotifier(t *testing.T) {
t.Parallel()
if EMAIL_HOST == "" || EMAIL_USER == "" || EMAIL_PASS == "" {
t.Log("email notifier testing skipped, missing EMAIL_ environment variables")
t.SkipNow()
}
currentCount = CountNotifiers()
t.Run("New Emailer", func(t *testing.T) {
Emailer.Host = EMAIL_HOST
Emailer.Username = EMAIL_USER
Emailer.Password = EMAIL_PASS
Emailer.Var1 = EMAIL_OUTGOING
Emailer.Var2 = EMAIL_SEND_TO
Emailer.Port = int(EMAIL_PORT)
Emailer.Delay = time.Duration(100 * time.Millisecond)
testEmail = &emailOutgoing{
To: Emailer.GetValue("var2"),
Subject: fmt.Sprintf("Service %v is Failing", TestService.Name),
Template: mainEmailTemplate,
Data: TestService,
From: Emailer.GetValue("var1"),
}
})
t.Run("Add email Notifier", func(t *testing.T) {
err := AddNotifiers(Emailer)
assert.Nil(t, err)
assert.Equal(t, "Hunter Long", Emailer.Author)
assert.Equal(t, EMAIL_HOST, Emailer.Host)
})
t.Run("email Within Limits", func(t *testing.T) {
ok, err := Emailer.WithinLimits()
assert.Nil(t, err)
assert.True(t, ok)
})
t.Run("email Test Source", func(t *testing.T) {
emailSource(testEmail)
assert.NotEmpty(t, testEmail.Source)
})
t.Run("email OnFailure", func(t *testing.T) {
Emailer.OnFailure(TestService, TestFailure)
assert.Equal(t, 1, len(Emailer.Queue))
})
t.Run("email OnSuccess", func(t *testing.T) {
Emailer.OnSuccess(TestService)
assert.Equal(t, 1, len(Emailer.Queue))
})
t.Run("email Check Back Online", func(t *testing.T) {
assert.True(t, TestService.Online)
})
t.Run("email OnSuccess Again", func(t *testing.T) {
Emailer.OnSuccess(TestService)
assert.Equal(t, 1, len(Emailer.Queue))
})
t.Run("email Send", func(t *testing.T) {
err := Emailer.Send(testEmail)
assert.Nil(t, err)
})
t.Run("Emailer Test", func(t *testing.T) {
t.SkipNow()
err := Emailer.OnTest()
assert.Nil(t, err)
})
t.Run("email Run Queue", func(t *testing.T) {
go notifications.Queue(Emailer)
time.Sleep(6 * time.Second)
assert.Equal(t, EMAIL_HOST, Emailer.Host)
assert.Equal(t, 0, len(Emailer.Queue))
})
}

View File

@ -26,8 +26,10 @@ import (
"time"
)
var _ notifications.Notifier = (*lineNotifier)(nil)
const (
lineNotifyMethod = "line notify"
lineNotifyMethod = "line_notify"
)
type lineNotifier struct {

View File

@ -26,6 +26,8 @@ import (
"time"
)
var _ notifications.Notifier = (*mobilePush)(nil)
const mobileIdentifier = "com.statping"
type mobilePush struct {

View File

@ -1,109 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package notifiers
import (
"github.com/hunterlong/statping/types/notifications"
"github.com/stretchr/testify/assert"
"os"
"testing"
"time"
)
var (
MOBILE_ID string
MOBILE_NUMBER string
)
func init() {
MOBILE_ID = os.Getenv("MOBILE_ID")
MOBILE_NUMBER = os.Getenv("MOBILE_NUMBER")
Mobile.Var1 = MOBILE_ID
}
func TestMobileNotifier(t *testing.T) {
t.Parallel()
Mobile.Var1 = MOBILE_ID
Mobile.Var2 = os.Getenv("MOBILE_NUMBER")
if MOBILE_ID == "" {
t.Log("Mobile notifier testing skipped, missing MOBILE_ID environment variable")
t.SkipNow()
}
currentCount = CountNotifiers()
t.Run("Load Mobile", func(t *testing.T) {
Mobile.Var1 = MOBILE_ID
Mobile.Var2 = MOBILE_NUMBER
Mobile.Delay = time.Duration(100 * time.Millisecond)
Mobile.Limits = 10
err := AddNotifiers(Mobile)
assert.Nil(t, err)
assert.Equal(t, "Hunter Long", Mobile.Author)
assert.Equal(t, MOBILE_ID, Mobile.Var1)
assert.Equal(t, MOBILE_NUMBER, Mobile.Var2)
})
t.Run("Mobile Notifier Tester", func(t *testing.T) {
assert.True(t, Mobile.CanTest())
})
t.Run("Mobile Within Limits", func(t *testing.T) {
ok, err := Mobile.WithinLimits()
assert.Nil(t, err)
assert.True(t, ok)
})
t.Run("Mobile OnFailure", func(t *testing.T) {
Mobile.OnFailure(TestService, TestFailure)
assert.Equal(t, 1, len(Mobile.Queue))
})
t.Run("Mobile OnSuccess", func(t *testing.T) {
Mobile.OnSuccess(TestService)
assert.Equal(t, 1, len(Mobile.Queue))
})
t.Run("Mobile OnSuccess Again", func(t *testing.T) {
t.SkipNow()
assert.True(t, TestService.Online)
Mobile.OnSuccess(TestService)
assert.Equal(t, 1, len(Mobile.Queue))
go notifications.Queue(Mobile)
time.Sleep(20 * time.Second)
assert.Equal(t, 1, len(Mobile.Queue))
})
t.Run("Mobile Within Limits again", func(t *testing.T) {
ok, err := Mobile.WithinLimits()
assert.Nil(t, err)
assert.True(t, ok)
})
t.Run("Mobile Test", func(t *testing.T) {
t.SkipNow()
err := Mobile.OnTest()
assert.Nil(t, err)
})
t.Run("Mobile Queue", func(t *testing.T) {
t.SkipNow()
go notifications.Queue(Mobile)
time.Sleep(15 * time.Second)
assert.Equal(t, MOBILE_ID, Mobile.Var1)
assert.Equal(t, 0, len(Mobile.Queue))
})
}

View File

@ -5,7 +5,6 @@ import (
"github.com/google/martian/log"
"github.com/hunterlong/statping/types/notifications"
"github.com/hunterlong/statping/utils"
"github.com/pkg/errors"
"strings"
)
@ -13,9 +12,8 @@ var (
allowedVars = []string{"host", "username", "password", "port", "api_key", "api_secret", "var1", "var2"}
)
func checkNotifierForm(n notifications.Notifier) error {
notifier := n.Select()
for _, f := range notifier.Form {
func checkNotifierForm(n *notifications.Notification) error {
for _, f := range n.Form {
contains := contains(f.DbField, allowedVars)
if !contains {
return fmt.Errorf("the DbField '%v' is not allowed, allowed vars: %v", f.DbField, allowedVars)
@ -35,34 +33,50 @@ func contains(s string, arr []string) bool {
// AddNotifier accept a Notifier interface to be added into the array
func AddNotifiers(notifiers ...notifications.Notifier) error {
log.Infof("Initiating %d Notifiers\n", len(notifiers))
for _, n := range notifiers {
log.Infof("Installing %s Notifier...", n.Select().Method)
if err := checkNotifierForm(n); err != nil {
return errors.Wrap(err, "error with notifier form fields")
notif := n.Select()
log.Infof("Initiating %s Notifier\n", notif.Method)
if err := checkNotifierForm(notif); err != nil {
log.Errorf(err.Error())
return err
}
if _, err := notifications.Init(n); err != nil {
return errors.Wrap(err, "error initiating notifier")
log.Infof("Creating %s Notifier\n", notif.Method)
if err := notif.Create(); err != nil {
return err
}
notifications.Append(notif)
if notif.Enabled.Bool {
notif.Close()
notif.Start()
go notifications.Queue(notif)
}
}
startAllNotifiers()
return nil
}
// startAllNotifiers will start the go routine for each loaded notifier
func startAllNotifiers() {
for _, comm := range notifications.AllCommunications {
if utils.IsType(comm, new(notifications.Notifier)) {
notify := comm.(notifications.Notifier)
if notify.Select().Enabled.Bool {
notify.Select().Close()
notify.Select().Start()
for _, notify := range notifications.All() {
n := notify.Select()
log.Infof("Initiating %s Notifier\n", n.Method)
if utils.IsType(notify, new(notifications.Notifier)) {
if n.Enabled.Bool {
n.Close()
n.Start()
go notifications.Queue(notify)
}
}
}
}
func AttachNotifiers() error {
func Migrate() error {
return AddNotifiers(
Command,
Discorder,

View File

@ -1,84 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package notifiers
import (
"github.com/hunterlong/statping/database"
"github.com/hunterlong/statping/source"
"github.com/hunterlong/statping/types/failures"
"github.com/hunterlong/statping/types/notifications"
"github.com/hunterlong/statping/types/null"
"github.com/hunterlong/statping/types/services"
"github.com/hunterlong/statping/types/users"
"github.com/hunterlong/statping/utils"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"time"
)
var (
dir string
db *gorm.DB
currentCount int
)
var TestService = &services.Service{
Id: 1,
Name: "Interpol - All The Rage Back Home",
Domain: "https://www.youtube.com/watch?v=-u6DvRyyKGU",
ExpectedStatus: 200,
Expected: null.NewNullString("test example"),
Interval: 30,
Type: "http",
Method: "GET",
Timeout: 20,
LastStatusCode: 404,
Online: true,
LastResponse: "<html>this is an example response</html>",
CreatedAt: utils.Now().Add(-24 * time.Hour),
}
var TestFailure = &failures.Failure{
Issue: "testing",
Service: 1,
CreatedAt: utils.Now().Add(-12 * time.Hour),
}
var TestUser = &users.User{
Username: "admin",
Email: "info@email.com",
}
func CountNotifiers() int {
return len(notifications.AllCommunications)
}
func init() {
dir = utils.Directory
source.Assets()
utils.InitLogs()
injectDatabase()
}
func injectDatabase() {
utils.DeleteFile(dir + "/notifiers.db")
db, err := gorm.Open("sqlite3", dir+"/notifiers.db")
if err != nil {
panic(err)
}
db.CreateTable(&notifications.Notification{})
notifications.SetDB(&database.Db{db, "sqlite3"})
}

View File

@ -28,6 +28,8 @@ import (
"time"
)
var _ notifications.Notifier = (*slack)(nil)
const (
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" } ] }`

View File

@ -1,115 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package notifiers
import (
"github.com/hunterlong/statping/types/notifications"
"github.com/stretchr/testify/assert"
"os"
"testing"
"time"
)
var (
SLACK_URL string
slackTestMessage = `{"text":"this is a test from the slack notifier!"}`
)
func init() {
SLACK_URL = os.Getenv("SLACK_URL")
Slacker.Host = SLACK_URL
}
func TestSlackNotifier(t *testing.T) {
t.Parallel()
SLACK_URL = os.Getenv("SLACK_URL")
Slacker.Host = SLACK_URL
if SLACK_URL == "" {
t.Log("slack notifier testing skipped, missing SLACK_URL environment variable")
t.SkipNow()
}
currentCount = CountNotifiers()
t.Run("Load slack", func(t *testing.T) {
Slacker.Host = SLACK_URL
Slacker.Delay = time.Duration(100 * time.Millisecond)
Slacker.Limits = 3
err := AddNotifiers(Slacker)
assert.Nil(t, err)
assert.Equal(t, "Hunter Long", Slacker.Author)
assert.Equal(t, SLACK_URL, Slacker.Host)
})
t.Run("slack Notifier Tester", func(t *testing.T) {
assert.True(t, Slacker.CanTest())
})
//t.Run("slack parse message", func(t *testing.T) {
// err := parseSlackMessage(slackText, "this is a test!")
// assert.Nil(t, err)
// assert.Equal(t, 1, len(Slacker.Queue))
//})
t.Run("slack Within Limits", func(t *testing.T) {
ok, err := Slacker.WithinLimits()
assert.Nil(t, err)
assert.True(t, ok)
})
t.Run("slack OnFailure", func(t *testing.T) {
Slacker.OnFailure(TestService, TestFailure)
assert.Equal(t, 1, len(Slacker.Queue))
})
t.Run("slack OnSuccess", func(t *testing.T) {
Slacker.OnSuccess(TestService)
assert.Equal(t, 1, len(Slacker.Queue))
})
t.Run("slack OnSuccess Again", func(t *testing.T) {
assert.True(t, TestService.Online)
Slacker.OnSuccess(TestService)
assert.Equal(t, 1, len(Slacker.Queue))
go notifications.Queue(Slacker)
time.Sleep(15 * time.Second)
assert.Equal(t, 0, len(Slacker.Queue))
})
t.Run("slack Within Limits again", func(t *testing.T) {
ok, err := Slacker.WithinLimits()
assert.Nil(t, err)
assert.True(t, ok)
})
t.Run("slack Send", func(t *testing.T) {
err := Slacker.Send(slackTestMessage)
assert.Nil(t, err)
assert.Equal(t, 0, len(Slacker.Queue))
})
t.Run("slack Test", func(t *testing.T) {
err := Slacker.OnTest()
assert.Nil(t, err)
})
t.Run("slack Queue", func(t *testing.T) {
go notifications.Queue(Slacker)
time.Sleep(10 * time.Second)
assert.Equal(t, SLACK_URL, Slacker.Host)
assert.Equal(t, 0, len(Slacker.Queue))
})
}

View File

@ -28,6 +28,8 @@ import (
"time"
)
var _ notifications.Notifier = (*telegram)(nil)
type telegram struct {
*notifications.Notification
}

View File

@ -1,105 +0,0 @@
// Statup
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package notifiers
import (
"github.com/hunterlong/statping/types/notifications"
"github.com/stretchr/testify/assert"
"os"
"testing"
"time"
)
var (
telegramToken string
telegramChannel string
telegramMessage = "The Telegram notifier on Statping has been tested!"
)
func init() {
telegramToken = os.Getenv("TELEGRAM_TOKEN")
telegramChannel = os.Getenv("TELEGRAM_CHANNEL")
Telegram.ApiSecret = telegramToken
Telegram.Var1 = telegramChannel
}
func TestTelegramNotifier(t *testing.T) {
t.SkipNow()
t.Parallel()
if telegramToken == "" || telegramChannel == "" {
t.Log("Telegram notifier testing skipped, missing TELEGRAM_TOKEN and TELEGRAM_CHANNEL environment variable")
t.SkipNow()
}
currentCount = CountNotifiers()
t.Run("Load Telegram", func(t *testing.T) {
Telegram.ApiSecret = telegramToken
Telegram.Var1 = telegramChannel
Telegram.Delay = time.Duration(1 * time.Second)
err := AddNotifiers(Telegram)
assert.Nil(t, err)
assert.Equal(t, "Hunter Long", Telegram.Author)
assert.Equal(t, telegramToken, Telegram.ApiSecret)
assert.Equal(t, telegramChannel, Telegram.Var1)
})
t.Run("Telegram Within Limits", func(t *testing.T) {
ok, err := Telegram.WithinLimits()
assert.Nil(t, err)
assert.True(t, ok)
})
t.Run("Telegram OnFailure", func(t *testing.T) {
Telegram.OnFailure(TestService, TestFailure)
assert.Equal(t, 1, len(Telegram.Queue))
})
t.Run("Telegram Check Offline", func(t *testing.T) {
assert.False(t, TestService.Online)
})
t.Run("Telegram OnSuccess", func(t *testing.T) {
Telegram.OnSuccess(TestService)
assert.Equal(t, 1, len(Telegram.Queue))
})
t.Run("Telegram Check Back Online", func(t *testing.T) {
assert.True(t, TestService.Online)
})
t.Run("Telegram OnSuccess Again", func(t *testing.T) {
Telegram.OnSuccess(TestService)
assert.Equal(t, 1, len(Telegram.Queue))
})
t.Run("Telegram Send", func(t *testing.T) {
err := Telegram.Send(telegramMessage)
assert.Nil(t, err)
})
t.Run("Telegram Test", func(t *testing.T) {
err := Telegram.OnTest()
assert.Nil(t, err)
})
t.Run("Telegram Queue", func(t *testing.T) {
go notifications.Queue(Telegram)
time.Sleep(3 * time.Second)
assert.Equal(t, telegramToken, Telegram.ApiSecret)
assert.Equal(t, 0, len(Telegram.Queue))
})
}

View File

@ -28,6 +28,8 @@ import (
"time"
)
var _ notifications.Notifier = (*twilio)(nil)
type twilio struct {
*notifications.Notification
}

View File

@ -1,109 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package notifiers
import (
"github.com/hunterlong/statping/types/notifications"
"github.com/stretchr/testify/assert"
"os"
"testing"
"time"
)
var (
TWILIO_SID = os.Getenv("TWILIO_SID")
TWILIO_SECRET = os.Getenv("TWILIO_SECRET")
TWILIO_FROM = os.Getenv("TWILIO_FROM")
TWILIO_TO = os.Getenv("TWILIO_TO")
twilioMessage = "The Twilio notifier on Statping has been tested!"
)
func init() {
TWILIO_SID = os.Getenv("TWILIO_SID")
TWILIO_SECRET = os.Getenv("TWILIO_SECRET")
TWILIO_FROM = os.Getenv("TWILIO_FROM")
TWILIO_TO = os.Getenv("TWILIO_TO")
Twilio.ApiKey = TWILIO_SID
Twilio.ApiSecret = TWILIO_SECRET
Twilio.Var1 = TWILIO_TO
Twilio.Var2 = TWILIO_FROM
}
func TestTwilioNotifier(t *testing.T) {
t.SkipNow()
if TWILIO_SID == "" || TWILIO_SECRET == "" || TWILIO_FROM == "" {
t.Log("twilio notifier testing skipped, missing TWILIO_SID environment variable")
t.SkipNow()
}
currentCount = CountNotifiers()
t.Run("Load Twilio", func(t *testing.T) {
Twilio.ApiKey = TWILIO_SID
Twilio.Delay = time.Duration(100 * time.Millisecond)
err := AddNotifiers(Twilio)
assert.Nil(t, err)
assert.Equal(t, "Hunter Long", Twilio.Author)
assert.Equal(t, TWILIO_SID, Twilio.ApiKey)
})
t.Run("Twilio Within Limits", func(t *testing.T) {
ok, err := Twilio.WithinLimits()
assert.Nil(t, err)
assert.True(t, ok)
})
t.Run("Twilio OnFailure", func(t *testing.T) {
Twilio.OnFailure(TestService, TestFailure)
assert.Len(t, Twilio.Queue, 1)
})
t.Run("Twilio Check Offline", func(t *testing.T) {
assert.False(t, TestService.Online)
})
t.Run("Twilio OnSuccess", func(t *testing.T) {
Twilio.OnSuccess(TestService)
assert.Len(t, Twilio.Queue, 2)
})
t.Run("Twilio Check Back Online", func(t *testing.T) {
assert.True(t, TestService.Online)
})
t.Run("Twilio OnSuccess Again", func(t *testing.T) {
Twilio.OnSuccess(TestService)
assert.Len(t, Twilio.Queue, 2)
})
t.Run("Twilio Send", func(t *testing.T) {
err := Twilio.Send(twilioMessage)
assert.Nil(t, err)
})
t.Run("Twilio Test", func(t *testing.T) {
err := Twilio.OnTest()
assert.Nil(t, err)
})
t.Run("Twilio Queue", func(t *testing.T) {
go notifications.Queue(Twilio)
time.Sleep(1 * time.Second)
assert.Equal(t, TWILIO_SID, Twilio.ApiKey)
assert.Equal(t, 0, len(Twilio.Queue))
})
}

View File

@ -28,8 +28,10 @@ import (
"time"
)
var _ notifications.Notifier = (*webhooker)(nil)
const (
webhookMethod = "Webhook"
webhookMethod = "webhook"
)
type webhooker struct {

View File

@ -1,101 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package notifiers
import (
"github.com/hunterlong/statping/types/notifications"
"github.com/stretchr/testify/assert"
"testing"
"time"
)
var (
webhookTestUrl = "https://statping.com"
webhookMessage = `{"id": "%service.Id","name": "%service.Name","online": "%service.Online","issue": "%failure.Issue"}`
apiKey = "application/json"
fullMsg string
)
func init() {
Webhook.Host = webhookTestUrl
Webhook.Var1 = "POST"
Webhook.Var2 = webhookMessage
Webhook.ApiKey = "application/json"
}
func TestWebhookNotifier(t *testing.T) {
t.Parallel()
currentCount = CountNotifiers()
t.Run("Load webhooker", func(t *testing.T) {
Webhook.Host = webhookTestUrl
Webhook.Delay = time.Duration(100 * time.Millisecond)
Webhook.ApiKey = apiKey
err := AddNotifiers(Webhook)
assert.Nil(t, err)
assert.Equal(t, "Hunter Long", Webhook.Author)
assert.Equal(t, webhookTestUrl, Webhook.Host)
assert.Equal(t, apiKey, Webhook.ApiKey)
})
t.Run("webhooker Notifier Tester", func(t *testing.T) {
assert.True(t, Webhook.CanTest())
})
t.Run("webhooker Replace Body Text", func(t *testing.T) {
fullMsg = replaceBodyText(webhookMessage, TestService, TestFailure)
assert.Equal(t, `{"id": "1","name": "Interpol - All The Rage Back Home","online": "true","issue": "testing"}`, fullMsg)
})
t.Run("webhooker Within Limits", func(t *testing.T) {
ok, err := Webhook.WithinLimits()
assert.Nil(t, err)
assert.True(t, ok)
})
t.Run("webhooker OnFailure", func(t *testing.T) {
Webhook.OnFailure(TestService, TestFailure)
assert.Len(t, Webhook.Queue, 1)
})
t.Run("webhooker OnSuccess", func(t *testing.T) {
Webhook.OnSuccess(TestService)
assert.Equal(t, len(Webhook.Queue), 1)
})
t.Run("webhooker Check Back Online", func(t *testing.T) {
assert.True(t, TestService.Online)
})
t.Run("webhooker OnSuccess Again", func(t *testing.T) {
Webhook.OnSuccess(TestService)
assert.Equal(t, len(Webhook.Queue), 1)
})
t.Run("webhooker Send", func(t *testing.T) {
err := Webhook.Send(fullMsg)
assert.Nil(t, err)
assert.Equal(t, len(Webhook.Queue), 1)
})
t.Run("webhooker Queue", func(t *testing.T) {
go notifications.Queue(Webhook)
time.Sleep(8 * time.Second)
assert.Equal(t, webhookTestUrl, Webhook.Host)
assert.Equal(t, len(Webhook.Queue), 0)
})
}

View File

@ -5,7 +5,7 @@ import (
"time"
)
func Samples() {
func Samples() error {
checkin1 := &Checkin{
Name: "Example Checkin 1",
ServiceId: 1,
@ -13,7 +13,9 @@ func Samples() {
GracePeriod: 300,
ApiKey: utils.RandomString(7),
}
checkin1.Create()
if err := checkin1.Create(); err != nil {
return err
}
checkin2 := &Checkin{
Name: "Example Checkin 2",
@ -22,10 +24,13 @@ func Samples() {
GracePeriod: 300,
ApiKey: utils.RandomString(7),
}
checkin2.Create()
if err := checkin2.Create(); err != nil {
return err
}
return nil
}
func SamplesChkHits() {
func SamplesChkHits() error {
checkTime := time.Now().UTC().Add(-24 * time.Hour)
for i := int64(1); i <= 2; i++ {
@ -35,8 +40,12 @@ func SamplesChkHits() {
CreatedAt: checkTime.UTC(),
}
checkHit.Create()
if err := checkHit.Create(); err != nil {
return err
}
checkTime = checkTime.Add(10 * time.Minute)
}
return nil
}

View File

@ -1,59 +0,0 @@
package configs
import (
"github.com/hunterlong/statping/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"os"
"testing"
)
var (
testConfigs *DbConfig
)
func TestDbConfig_Save(t *testing.T) {
config := &DbConfig{
DbConn: "sqlite",
Project: "Tester",
Location: utils.Directory,
}
err := ConnectConfigs(config, true)
require.Nil(t, err)
err = config.Save(utils.Directory)
require.Nil(t, err)
assert.Equal(t, "sqlite3", config.DbConn)
assert.NotEmpty(t, config.ApiKey)
assert.NotEmpty(t, config.ApiSecret)
}
func TestLoadDbConfig(t *testing.T) {
Configs, err := LoadConfigFile(utils.Directory)
assert.Nil(t, err)
assert.Equal(t, "sqlite3", Configs.DbConn)
}
func TestEnvToConfig(t *testing.T) {
os.Setenv("DB_CONN", "sqlite")
os.Setenv("DB_USER", "")
os.Setenv("DB_PASS", "")
os.Setenv("DB_DATABASE", "")
os.Setenv("NAME", "Testing")
os.Setenv("DOMAIN", "http://localhost:8080")
os.Setenv("DESCRIPTION", "Testing Statping")
os.Setenv("ADMIN_USER", "admin")
os.Setenv("ADMIN_PASS", "admin123")
os.Setenv("VERBOSE", "1")
config, err := loadConfigEnvs()
assert.Nil(t, err)
assert.Equal(t, config.DbConn, "sqlite")
assert.Equal(t, config.Domain, "http://localhost:8080")
assert.Equal(t, config.Description, "Testing Statping")
assert.Equal(t, config.Username, "admin")
assert.Equal(t, config.Password, "admin123")
testConfigs = config
}

View File

@ -84,21 +84,9 @@ func Connect(configs *DbConfig, retry bool) error {
return err
}
func InitialSetup(configs *DbConfig) error {
func CreateAdminUser(configs *DbConfig) error {
log.Infoln(fmt.Sprintf("Core database does not exist, creating now!"))
if err := configs.DropDatabase(); err != nil {
return errors.Wrap(err, "error dropping database")
}
if err := CreateDatabase(); err != nil {
return errors.Wrap(err, "error creating database")
}
if err := TriggerSamples(); err != nil {
return errors.Wrap(err, "error creating database")
}
if configs.Username == "" && configs.Password == "" {
configs.Username = utils.Getenv("ADMIN_USER", "admin").(string)
configs.Password = utils.Getenv("ADMIN_PASSWORD", "admin").(string)

View File

@ -8,7 +8,6 @@ import (
"github.com/hunterlong/statping/types/groups"
"github.com/hunterlong/statping/types/hits"
"github.com/hunterlong/statping/types/incidents"
"github.com/hunterlong/statping/types/integrations"
"github.com/hunterlong/statping/types/messages"
"github.com/hunterlong/statping/types/notifications"
"github.com/hunterlong/statping/types/services"
@ -18,7 +17,7 @@ import (
"os"
)
type SamplerFunc func()
type SamplerFunc func() error
type Sampler interface {
Samples() []database.DbObject
@ -27,7 +26,7 @@ type Sampler interface {
func TriggerSamples() error {
return createSamples(
core.Samples,
users.Samples,
//users.Samples,
messages.Samples,
services.Samples,
checkins.Samples,
@ -42,7 +41,9 @@ func TriggerSamples() error {
func createSamples(sm ...SamplerFunc) error {
for _, v := range sm {
v()
if err := v(); err != nil {
return err
}
}
return nil
}
@ -72,7 +73,7 @@ func (d *DbConfig) Delete() error {
// DropDatabase will DROP each table Statping created
func (d *DbConfig) DropDatabase() error {
var DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifications.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}, &integrations.Integration{}}
var DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifications.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}}
log.Infoln("Dropping Database Tables...")
for _, t := range DbModels {
if err := database.DB().DropTableIfExists(t); err != nil {
@ -87,7 +88,7 @@ func (d *DbConfig) DropDatabase() error {
func CreateDatabase() error {
var err error
var DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifications.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}, &integrations.Integration{}}
var DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifications.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}}
log.Infoln("Creating Database Tables...")
for _, table := range DbModels {

View File

@ -2,7 +2,6 @@ package configs
import (
"fmt"
"github.com/hunterlong/statping/database"
"github.com/hunterlong/statping/utils"
"github.com/pkg/errors"
"os"
@ -11,7 +10,7 @@ import (
var log = utils.Log
func ConnectConfigs(configs *DbConfig, initiate bool) error {
func ConnectConfigs(configs *DbConfig) error {
err := Connect(configs, true)
if err != nil {
return errors.Wrap(err, "error connecting to database")
@ -20,10 +19,6 @@ func ConnectConfigs(configs *DbConfig, initiate bool) error {
return errors.Wrap(err, "error saving configuration")
}
exists := database.DB().HasTable("core")
if !exists && initiate {
return InitialSetup(configs)
}
return nil
}

View File

@ -9,7 +9,6 @@ import (
"github.com/hunterlong/statping/types/groups"
"github.com/hunterlong/statping/types/hits"
"github.com/hunterlong/statping/types/incidents"
"github.com/hunterlong/statping/types/integrations"
"github.com/hunterlong/statping/types/messages"
"github.com/hunterlong/statping/types/notifications"
"github.com/hunterlong/statping/types/services"
@ -49,7 +48,7 @@ import (
//If this function has an issue, it will ROLLBACK to the previous state.
func (c *DbConfig) MigrateDatabase() error {
var DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifications.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}, &integrations.Integration{}}
var DbModels = []interface{}{&services.Service{}, &users.User{}, &hits.Hit{}, &failures.Failure{}, &messages.Message{}, &groups.Group{}, &checkins.Checkin{}, &checkins.CheckinHit{}, &notifications.Notification{}, &incidents.Incident{}, &incidents.IncidentUpdate{}}
log.Infoln("Migrating Database Tables...")
tx := database.Begin("migration")
@ -91,8 +90,7 @@ func (c *DbConfig) MigrateDatabase() error {
if err := database.DB().Model(&failures.Failure{}).AddIndex("idx_checkin_fail", "checkin").Error(); err != nil {
log.Errorln(err)
}
log.Infoln("Statping Database Indexes Migrated")
log.Infoln("Database Indexes Created")
return nil
}

View File

@ -2,7 +2,6 @@ package core
import (
"github.com/hunterlong/statping/database"
"github.com/hunterlong/statping/notifiers"
"github.com/hunterlong/statping/types/services"
)
@ -10,27 +9,11 @@ func InitApp() error {
if _, err := Select(); err != nil {
return err
}
//if err := InsertNotifierDB(); err != nil {
// return err
//}
//if err := InsertIntegratorDB(); err != nil {
// return err
//}
if _, err := services.SelectAllServices(true); err != nil {
return err
}
if err := notifiers.AttachNotifiers(); err != nil {
return err
}
//App.Notifications = notifications.AllCommunications
//if err := integrations.AddIntegrations(); err != nil {
// return err
//}
//App.Integrations = integrations.Integrations
go services.CheckServices()
database.StartMaintenceRoutine()

View File

@ -6,7 +6,7 @@ import (
"time"
)
func Samples() {
func Samples() error {
apiKey := utils.Getenv("API_KEY", "samplekey")
apiSecret := utils.Getenv("API_SECRET", "samplesecret")
@ -21,5 +21,6 @@ func Samples() {
UseCdn: null.NewNullBool(false),
Footer: null.NewNullString(""),
}
core.Create()
return core.Create()
}

View File

@ -37,6 +37,13 @@ type Core struct {
Image string `gorm:"image" json:"started_on"`
Notifications []AllNotifiers `gorm:"-" json:"-"`
Integrations []Integrator `gorm:"-" json:"-"`
GHAuth
}
type GHAuth struct {
GithubClientID string `gorm:"gh_client_id" json:"gh_client_id"`
GithubClientSecret string `gorm:"gh_client_secret" json:"-"`
}
// AllNotifiers contains all the Notifiers loaded

View File

@ -13,7 +13,7 @@ import (
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
func Samples() {
func Samples() error {
tx := DB().Begin()
sg := new(sync.WaitGroup)
@ -22,7 +22,7 @@ func Samples() {
for i := int64(1); i <= 4; i++ {
sg.Add(1)
log.Infoln(fmt.Sprintf("Adding %v Failure records to service", 730))
log.Infoln(fmt.Sprintf("Adding %v Failure records to service", 400))
go func() {
defer sg.Done()
@ -42,6 +42,8 @@ func Samples() {
if err := tx.Commit().Error(); err != nil {
log.Error(err)
return err
}
return nil
}

View File

@ -1,29 +0,0 @@
package groups
import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
)
func TestGroup_Create(t *testing.T) {
group := &Group{
Name: "Testing",
}
err := group.Create()
assert.Nil(t, err)
assert.NotZero(t, group.Id)
}
func TestGroup_Services(t *testing.T) {
group, err := Find(1)
require.Nil(t, err)
assert.NotEmpty(t, group.Services())
}
func TestSelectGroups(t *testing.T) {
grs := SelectGroups(true, false)
assert.Equal(t, int(3), len(grs))
grs = SelectGroups(true, true)
assert.Equal(t, int(5), len(grs))
}

View File

@ -4,26 +4,33 @@ import (
"github.com/hunterlong/statping/types/null"
)
func Samples() {
func Samples() error {
group1 := &Group{
Name: "Main Services",
Public: null.NewNullBool(true),
Order: 2,
}
group1.Create()
if err := group1.Create(); err != nil {
return err
}
group2 := &Group{
Name: "Linked Services",
Public: null.NewNullBool(false),
Order: 1,
}
group2.Create()
if err := group2.Create(); err != nil {
return err
}
group3 := &Group{
Name: "Empty Group",
Public: null.NewNullBool(false),
Order: 3,
}
group3.Create()
if err := group3.Create(); err != nil {
return err
}
return nil
}

View File

@ -1,6 +1,7 @@
package hits
import (
"fmt"
"github.com/hunterlong/statping/database"
"github.com/hunterlong/statping/types"
"github.com/hunterlong/statping/utils"
@ -14,7 +15,7 @@ import (
var SampleHits = 99900.
func Samples() {
func Samples() error {
tx := DB().Begin()
sg := new(sync.WaitGroup)
@ -26,9 +27,12 @@ func Samples() {
tx = DB().Begin()
}
return tx.Error()
}
func createHitsAt(db database.Database, serviceID int64, sg *sync.WaitGroup) error {
log.Infoln(fmt.Sprintf("Adding sample hit records to service #%d", serviceID))
createdAt := utils.Now().Add(-3 * types.Day)
p := utils.NewPerlin(2, 2, 5, utils.Now().UnixNano())
@ -46,6 +50,10 @@ func createHitsAt(db database.Database, serviceID int64, sg *sync.WaitGroup) err
}
db = db.Create(&hit)
if err := db.Error(); err != nil {
return err
}
i++
if createdAt.After(utils.Now()) {
break

View File

@ -1,40 +1,53 @@
package incidents
func Samples() {
func Samples() error {
incident1 := &Incident{
Title: "Github Downtime",
Description: "This is an example of a incident for a service.",
ServiceId: 2,
}
incident1.Create()
if err := incident1.Create(); err != nil {
return err
}
i1 := &IncidentUpdate{
IncidentId: incident1.Id,
Message: "Github's page for Statping seems to be sending a 501 error.",
Type: "Investigating",
}
i1.Create()
if err := i1.Create(); err != nil {
return err
}
i2 := &IncidentUpdate{
IncidentId: incident1.Id,
Message: "Problem is continuing and we are looking at the issues.",
Type: "Update",
}
i2.Create()
if err := i2.Create(); err != nil {
return err
}
i3 := &IncidentUpdate{
IncidentId: incident1.Id,
Message: "Github is now back online and everything is working.",
Type: "Resolved",
}
i3.Create()
if err := i3.Create(); err != nil {
return err
}
return nil
}
func SamplesUpdates() {
func SamplesUpdates() error {
u1 := &IncidentUpdate{
IncidentId: 1,
Message: "Github is now back online and everything is working.",
Type: "Resolved",
}
u1.Create()
if err := u1.Create(); err != nil {
return err
}
return nil
}

View File

@ -1,41 +0,0 @@
package integrations
import (
"github.com/hunterlong/statping/database"
"github.com/hunterlong/statping/types/services"
)
func DB() database.Database {
return database.DB().Model(&Integration{})
}
func Find(name string) (*Integration, error) {
var integration Integration
db := DB().Where("name = ?", name).Find(&integration)
return &integration, db.Error()
}
func All() []*Integration {
var integrations []*Integration
DB().Find(&integrations)
return integrations
}
func List(i Integrator) ([]*services.Service, error) {
return i.List()
}
func (i *Integration) Create() error {
db := DB().Create(i)
return db.Error()
}
func (i *Integration) Update() error {
db := DB().Update(i)
return db.Error()
}
func (i *Integration) Delete() error {
db := DB().Delete(i)
return db.Error()
}

View File

@ -1,25 +0,0 @@
package integrations
import "github.com/hunterlong/statping/types/services"
type Integration struct {
ShortName string `gorm:"column:name" json:"name"`
Name string `gorm:"-" json:"full_name,omitempty"`
Icon string `gorm:"-" json:"-"`
Description string `gorm:"-" json:"description,omitempty"`
Enabled bool `gorm:"column:enabled;default:false" json:"enabled"`
Fields []*IntegrationField `gorm:"column:fields" json:"fields"`
}
type IntegrationField struct {
Name string `gorm:"-" json:"name"`
Value interface{} `gorm:"-" json:"value"`
Type string `gorm:"-" json:"type"`
Description string `gorm:"-" json:"description,omitempty"`
MimeType string `gorm:"-" json:"mime_type,omitempty"`
}
type Integrator interface {
Get() *Integration
List() ([]*services.Service, error)
}

View File

@ -4,7 +4,7 @@ import (
"time"
)
func Samples() {
func Samples() error {
m1 := &Message{
Title: "Routine Downtime",
Description: "This is an example a upcoming message for a service!",
@ -13,7 +13,9 @@ func Samples() {
EndOn: time.Now().UTC().Add(2 * time.Hour),
}
m1.Create()
if err := m1.Create(); err != nil {
return err
}
m2 := &Message{
Title: "Server Reboot",
@ -23,5 +25,9 @@ func Samples() {
EndOn: time.Now().UTC().Add(2 * time.Hour),
}
m2.Create()
if err := m2.Create(); err != nil {
return err
}
return nil
}

View File

@ -9,8 +9,12 @@ func DB() database.Database {
return database.DB().Model(&Notification{})
}
func Append(n Notifier) {
allNotifiers = append(allNotifiers, n)
}
func Find(name string) (Notifier, error) {
for _, n := range AllCommunications {
for _, n := range allNotifiers {
notif := n.Select()
if notif.Name() == name || notif.Method == name {
return n, nil
@ -19,15 +23,16 @@ func Find(name string) (Notifier, error) {
return nil, errors.New("notifier not found")
}
func All() []*Notification {
var notifiers []*Notification
DB().Find(&notifiers)
return notifiers
func All() []Notifier {
return allNotifiers
}
func (n *Notification) Create() error {
db := DB().FirstOrCreate(&n)
return db.Error()
var notif Notification
if DB().Where("method = ?", n.Method).Find(&notif).RecordNotFound() {
return DB().Create(n).Error()
}
return nil
}
func (n *Notification) Update() error {

View File

@ -25,7 +25,7 @@ import (
// OnSave will trigger a notifier when it has been saved - Notifier interface
func OnSave(method string) {
for _, comm := range AllCommunications {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(Notifier)) {
notifier := comm.(Notifier)
if notifier.Select().Method == method {
@ -53,7 +53,7 @@ func OnFailure(s *services.Service, f *failures.Failure) {
}
sendMessages:
for _, comm := range AllCommunications {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(BasicEvents)) && isEnabled(comm) && (s.Online || inLimits(comm)) {
notifier := comm.(Notifier).Select()
log.
@ -76,7 +76,7 @@ func OnSuccess(s *services.Service) {
s.UserNotified = false
}
for _, comm := range AllCommunications {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(BasicEvents)) && isEnabled(comm) && (!s.Online || inLimits(comm)) {
notifier := comm.(Notifier).Select()
log.
@ -90,7 +90,7 @@ func OnSuccess(s *services.Service) {
// OnNewService is triggered when a new service is created - ServiceEvents interface
func OnNewService(s *services.Service) {
for _, comm := range AllCommunications {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
log.
WithField("trigger", "OnNewService").
@ -106,7 +106,7 @@ func OnUpdatedService(s *services.Service) {
if !s.AllowNotifications.Bool {
return
}
for _, comm := range AllCommunications {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
log.Debugln(fmt.Sprintf("Sending updated service notification for service %v", s.Name))
comm.(ServiceEvents).OnUpdatedService(s)
@ -120,7 +120,7 @@ func OnDeletedService(s *services.Service) {
if !s.AllowNotifications.Bool {
return
}
for _, comm := range AllCommunications {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
log.Debugln(fmt.Sprintf("Sending deleted service notification for service %v", s.Name))
comm.(ServiceEvents).OnDeletedService(s)
@ -131,7 +131,7 @@ func OnDeletedService(s *services.Service) {
// OnNewUser is triggered when a new user is created - UserEvents interface
func OnNewUser(u *users.User) {
for _, comm := range AllCommunications {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
log.Debugln(fmt.Sprintf("Sending new user notification for user %v", u.Username))
comm.(UserEvents).OnNewUser(u)
@ -142,7 +142,7 @@ func OnNewUser(u *users.User) {
// OnUpdatedUser is triggered when a new user is updated - UserEvents interface
func OnUpdatedUser(u *users.User) {
for _, comm := range AllCommunications {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
log.Debugln(fmt.Sprintf("Sending updated user notification for user %v", u.Username))
comm.(UserEvents).OnUpdatedUser(u)
@ -153,7 +153,7 @@ func OnUpdatedUser(u *users.User) {
// OnDeletedUser is triggered when a new user is deleted - UserEvents interface
func OnDeletedUser(u *users.User) {
for _, comm := range AllCommunications {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
log.Debugln(fmt.Sprintf("Sending deleted user notification for user %v", u.Username))
comm.(UserEvents).OnDeletedUser(u)
@ -164,7 +164,7 @@ func OnDeletedUser(u *users.User) {
//// OnUpdatedCore is triggered when the CoreApp settings are saved - CoreEvents interface
//func OnUpdatedCore(c *core.Core) {
// for _, comm := range AllCommunications {
// for _, comm := range allNotifiers {
// if utils.IsType(comm, new(CoreEvents)) && isEnabled(comm) && inLimits(comm) {
// log.Debugln(fmt.Sprintf("Sending updated core notification"))
// comm.(CoreEvents).OnUpdatedCore(c)
@ -174,7 +174,7 @@ func OnDeletedUser(u *users.User) {
//
//// OnStart is triggered when the Statping service has started
//func OnStart(c *core.Core) {
// for _, comm := range AllCommunications {
// for _, comm := range allNotifiers {
// if utils.IsType(comm, new(CoreEvents)) && isEnabled(comm) && inLimits(comm) {
// comm.(CoreEvents).OnUpdatedCore(c)
// }
@ -183,7 +183,7 @@ func OnDeletedUser(u *users.User) {
// OnNewNotifier is triggered when a new notifier is loaded
func OnNewNotifier(n *Notification) {
for _, comm := range AllCommunications {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(NotifierEvents)) && isEnabled(comm) && inLimits(comm) {
comm.(NotifierEvents).OnNewNotifier(n)
comm.Select().Hits.OnNewNotifier++
@ -193,7 +193,7 @@ func OnNewNotifier(n *Notification) {
// OnUpdatedNotifier is triggered when a notifier has been updated
func OnUpdatedNotifier(n *Notification) {
for _, comm := range AllCommunications {
for _, comm := range allNotifiers {
if utils.IsType(comm, new(NotifierEvents)) && isEnabled(comm) && inLimits(comm) {
log.Infoln(fmt.Sprintf("Sending updated notifier for %v", n.Id))
comm.(NotifierEvents).OnUpdatedNotifier(n)

View File

@ -1,179 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package notifications
import (
"errors"
"fmt"
"github.com/hunterlong/statping/source"
"github.com/hunterlong/statping/types/failures"
"github.com/hunterlong/statping/types/services"
"github.com/hunterlong/statping/types/users"
"github.com/hunterlong/statping/utils"
"time"
)
// ExampleNotifier is an example on how to use the Statping notifier struct
type ExampleNotifier struct {
*Notification
}
// example is a example variable for a example notifier
var example = &ExampleNotifier{&Notification{
Method: METHOD,
Host: "http://exmaplehost.com",
Title: "Example",
Description: "Example Notifier",
Author: "Hunter Long",
AuthorUrl: "https://github.com/hunterlong",
Delay: time.Duration(3 * time.Second),
Limits: 7,
Form: []NotificationForm{{
Type: "text",
Title: "Host",
Placeholder: "Insert your Host here.",
DbField: "host",
SmallText: "this is where you would put the host",
}, {
Type: "text",
Title: "Username",
Placeholder: "Insert your Username here.",
DbField: "username",
}, {
Type: "password",
Title: "Password",
Placeholder: "Insert your Password here.",
DbField: "password",
}, {
Type: "number",
Title: "Port",
Placeholder: "Insert your Port here.",
DbField: "port",
}, {
Type: "text",
Title: "API Key",
Placeholder: "Insert your API Key here",
DbField: "api_key",
}, {
Type: "text",
Title: "API Secret",
Placeholder: "Insert your API Secret here",
DbField: "api_secret",
}, {
Type: "text",
Title: "Var 1",
Placeholder: "Insert your Var1 here",
DbField: "var1",
}, {
Type: "text",
Title: "Var2",
Placeholder: "Var2 goes here",
DbField: "var2",
}},
}}
// init will be ran when Statping is loaded, AddNotifier will add the notifier instance to the system
func init() {
dir = utils.Directory
source.Assets()
utils.InitLogs()
injectDatabase()
}
// Send is the main function to hold your notifier functionality
func (n *ExampleNotifier) Send(msg interface{}) error {
message := msg.(string)
fmt.Printf("i received this string: %v\n", message)
return nil
}
// Select is a required basic event for the Notifier interface
func (n *ExampleNotifier) Select() *Notification {
return n.Notification
}
// OnSave is a required basic event for the Notifier interface
func (n *ExampleNotifier) OnSave() error {
msg := fmt.Sprintf("received on save trigger")
n.AddQueue("onsave", msg)
return errors.New("onsave triggered")
}
// OnSuccess is a required basic event for the Notifier interface
func (n *ExampleNotifier) OnSuccess(s *services.Service) {
msg := fmt.Sprintf("received a count trigger for service: %v\n", s.Name)
n.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
}
// OnFailure is a required basic event for the Notifier interface
func (n *ExampleNotifier) OnFailure(s *services.Service, f *failures.Failure) {
msg := fmt.Sprintf("received a failure trigger for service: %v\n", s.Name)
n.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
}
// OnTest is a option testing event for the Notifier interface
func (n *ExampleNotifier) OnTest() error {
fmt.Printf("received a test trigger with form data: %v\n", n.Host)
return nil
}
// OnNewService is a option event for new services
func (n *ExampleNotifier) OnNewService(s *services.Service) {
msg := fmt.Sprintf("received a new service trigger for service: %v\n", s.Name)
n.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
}
// OnUpdatedService is a option event for updated services
func (n *ExampleNotifier) OnUpdatedService(s *services.Service) {
msg := fmt.Sprintf("received a update service trigger for service: %v\n", s.Name)
n.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
}
// OnDeletedService is a option event for deleted services
func (n *ExampleNotifier) OnDeletedService(s *services.Service) {
msg := fmt.Sprintf("received a delete service trigger for service: %v\n", s.Name)
n.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
}
// OnNewUser is a option event for new users
func (n *ExampleNotifier) OnNewUser(s *users.User) {
msg := fmt.Sprintf("received a new user trigger for user: %v\n", s.Username)
n.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
}
// OnUpdatedUser is a option event for updated users
func (n *ExampleNotifier) OnUpdatedUser(s *users.User) {
msg := fmt.Sprintf("received a updated user trigger for user: %v\n", s.Username)
n.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
}
// OnDeletedUser is a option event for deleted users
func (n *ExampleNotifier) OnDeletedUser(s *users.User) {
msg := fmt.Sprintf("received a deleted user trigger for user: %v\n", s.Username)
n.AddQueue(fmt.Sprintf("service_%v", s.Id), msg)
}
// OnNewNotifier is triggered when a new notifier has initialized
func (n *ExampleNotifier) OnNewNotifier(s *Notification) {
msg := fmt.Sprintf("received a new notifier trigger for notifier: %v\n", s.Method)
n.AddQueue(fmt.Sprintf("notifier_%v", s.Id), msg)
}
// OnUpdatedNotifier is triggered when a notifier has been updated
func (n *ExampleNotifier) OnUpdatedNotifier(s *Notification) {
msg := fmt.Sprintf("received a update notifier trigger for notifier: %v\n", s.Method)
n.AddQueue(fmt.Sprintf("notifier_%v", s.Id), msg)
}

View File

@ -3,7 +3,6 @@ package notifications
import (
"fmt"
"github.com/hunterlong/statping/utils"
"github.com/pkg/errors"
"strings"
"time"
)
@ -96,30 +95,35 @@ func (n *Notification) GetValue(dbField string) string {
}
// Init accepts the Notifier interface to initialize the notifier
func Init(n Notifier) (*Notification, error) {
err := install(n)
if err == nil {
notify, err := SelectNotification(n)
if err != nil {
return nil, errors.Wrap(err, "error selecting notification")
}
notify.CreatedAt = time.Now().UTC()
notify.UpdatedAt = time.Now().UTC()
if notify.Delay.Seconds() == 0 {
notify.Delay = 1 * time.Second
}
notify.testable = utils.IsType(n, new(Tester))
notify.Form = n.Select().Form
AllCommunications = append(AllCommunications, n)
} else {
return nil, errors.Wrap(err, "error installing notification")
}
return nil, err
}
//func Init(n Notifier) (*Notification, error) {
// if Exists(n.Select().Method) {
// AllCommunications = append(AllCommunications, n)
// } else {
// _, err := insertDatabase(n)
// if err != nil {
// log.Errorln(err)
// return nil, err
// }
// AllCommunications = append(AllCommunications, n)
// }
//
// notify, err := SelectNotification(n)
// if err != nil {
// return nil, errors.Wrap(err, "error selecting notification")
// }
//
// notify.CreatedAt = time.Now().UTC()
// notify.UpdatedAt = time.Now().UTC()
// if notify.Delay.Seconds() == 0 {
// notify.Delay = 1 * time.Second
// }
// notify.testable = utils.IsType(n, new(Tester))
// notify.Form = n.Select().Form
//
// AllCommunications = append(AllCommunications, n)
//
// return nil, err
//}
// ResetQueue will clear the notifiers Queue
func (n *Notification) ResetQueue() {

View File

@ -19,7 +19,6 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/hunterlong/statping/database"
"github.com/hunterlong/statping/types/null"
"github.com/hunterlong/statping/types/services"
"github.com/hunterlong/statping/utils"
@ -29,16 +28,14 @@ import (
var (
// db holds the Statping database connection
db database.Database
log = utils.Log.WithField("type", "notifier")
AllCommunications []Notifier
log = utils.Log.WithField("type", "notifier")
allNotifiers []Notifier
)
// Notification contains all the fields for a Statping Notifier.
type Notification struct {
Id int64 `gorm:"primary_key;column:id" json:"id"`
Method string `gorm:"column:method" json:"method"`
name string `gorm:"column:name" json:"name"`
Host string `gorm:"not null;column:host" json:"host,omitempty"`
Port int `gorm:"not null;column:port" json:"port,omitempty"`
Username string `gorm:"not null;column:username" json:"username,omitempty"`
@ -108,16 +105,6 @@ type NotificationLog struct {
Timestamp time.Time `json:"timestamp"`
}
// SetDB is called by core to inject the database for a notifier to use
func SetDB(d database.Database) {
db = d
}
// asNotification accepts a Notifier and returns a Notification struct
func asNotification(n Notifier) *Notification {
return n.Select()
}
// normalizeType will accept multiple interfaces and converts it into a string for logging
func normalizeType(ty interface{}) string {
switch v := ty.(type) {
@ -169,20 +156,9 @@ func SelectNotification(n Notifier) (*Notification, error) {
return notifier, err.Error()
}
// insertDatabase will create a new record into the database for the notifier
func insertDatabase(n Notifier) (int64, error) {
noti := n.Select()
noti.Limits = 3
noti.name = noti.Name()
if err := noti.Create(); err != nil {
return 0, err
}
return noti.Id, nil
}
// SelectNotifier returns the Notification struct from the database
func SelectNotifier(method string) (*Notification, Notifier, error) {
for _, comm := range AllCommunications {
for _, comm := range allNotifiers {
n, ok := comm.(Notifier)
if !ok {
return nil, nil, fmt.Errorf("incorrect notification type: %v", reflect.TypeOf(n).String())
@ -231,20 +207,22 @@ CheckNotifier:
}
// install will check the database for the notification, if its not inserted it will insert a new record for it
func install(n Notifier) error {
_, err := insertDatabase(n)
if err != nil {
log.Errorln(err)
return err
}
AllCommunications = append(AllCommunications, n)
log.WithFields(utils.ToFields(n)).
Debugln(fmt.Sprintf("Checking if notifier '%v' is installed", n.Select().Method))
return nil
}
//func install(n Notifier) error {
// log.WithFields(utils.ToFields(n)).
// Debugln(fmt.Sprintf("Checking if notifier '%v' is installed", n.Select().Method))
//
// if Exists(n.Select().Method) {
// AllCommunications = append(AllCommunications, n)
// } else {
// _, err := insertDatabase(n)
// if err != nil {
// log.Errorln(err)
// return err
// }
// AllCommunications = append(AllCommunications, n)
// }
// return nil
//}
// isEnabled returns true if the notifier is enabled
func isEnabled(n interface{}) bool {

View File

@ -1,212 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package notifications
import (
"fmt"
"github.com/hunterlong/statping/database"
"github.com/hunterlong/statping/types/failures"
"github.com/hunterlong/statping/types/null"
"github.com/hunterlong/statping/types/services"
"github.com/hunterlong/statping/types/users"
"github.com/hunterlong/statping/utils"
_ "github.com/jinzhu/gorm/dialects/sqlite"
"github.com/stretchr/testify/assert"
"testing"
"time"
)
var (
dir string
METHOD = "example"
)
var service = &services.Service{
Name: "Interpol - All The Rage Back Home",
Domain: "https://www.youtube.com/watch?v=-u6DvRyyKGU",
ExpectedStatus: 200,
Interval: 30,
Type: "http",
Method: "GET",
Timeout: 20,
AllowNotifications: null.NewNullBool(true),
}
var failure = &failures.Failure{
Issue: "testing",
}
var user = &users.User{
Username: "admin",
Email: "info@email.com",
}
func injectDatabase() {
sqlPath := dir + "/notifier.db"
utils.DeleteFile(sqlPath)
db, _ = database.Openw("sqlite3", sqlPath)
db.CreateTable(&Notification{})
}
func TestIsBasicType(t *testing.T) {
assert.True(t, utils.IsType(example, new(Notifier)))
assert.True(t, utils.IsType(example, new(BasicEvents)))
assert.True(t, utils.IsType(example, new(ServiceEvents)))
assert.True(t, utils.IsType(example, new(UserEvents)))
assert.True(t, utils.IsType(example, new(NotifierEvents)))
assert.True(t, utils.IsType(example, new(Tester)))
}
func TestSelectNotification(t *testing.T) {
notifier, notif, err := SelectNotifier(example.Method)
assert.Nil(t, err)
assert.NotNil(t, notifier)
assert.NotNil(t, notif)
assert.Equal(t, "example", notifier.Method)
assert.False(t, notifier.Enabled.Bool)
assert.False(t, notifier.IsRunning())
}
func TestAddQueue(t *testing.T) {
msg := "this is a test in the queue!"
example.AddQueue(fmt.Sprintf("service_%v", 0), msg)
assert.Equal(t, 1, len(example.Queue))
}
func TestNotification_Update(t *testing.T) {
notifier, err := SelectNotification(example)
assert.Nil(t, err)
notifier.Host = "http://demo.statping.com/api"
notifier.Port = 9090
notifier.Username = "admin"
notifier.Password = "password123"
notifier.Var1 = "var1_is_here"
notifier.Var2 = "var2_is_here"
notifier.ApiKey = "USBdu82HDiiuw9327yGYDGw"
notifier.ApiSecret = "PQopncow929hUIDHGwiud"
notifier.Limits = 10
err = notifier.Update()
assert.Nil(t, err)
selected, err := SelectNotification(example)
assert.Nil(t, err)
assert.Equal(t, "http://demo.statping.com/api", selected.GetValue("host"))
assert.Equal(t, "http://demo.statping.com/api", example.Notification.Host)
assert.Equal(t, "http://demo.statping.com/api", example.Host)
assert.Equal(t, "USBdu82HDiiuw9327yGYDGw", selected.GetValue("api_key"))
assert.Equal(t, "USBdu82HDiiuw9327yGYDGw", example.ApiKey)
assert.False(t, selected.Enabled.Bool)
assert.False(t, selected.IsRunning())
}
func TestEnableNotification(t *testing.T) {
notifier, err := SelectNotification(example)
assert.Nil(t, err)
notifier.Enabled = null.NewNullBool(true)
err = notifier.Update()
assert.Nil(t, err)
assert.True(t, notifier.Enabled.Bool)
assert.True(t, notifier.IsRunning())
}
func TestIsEnabled(t *testing.T) {
assert.True(t, isEnabled(example))
}
func TestIsRunning(t *testing.T) {
assert.True(t, example.IsRunning())
}
func TestLastSent(t *testing.T) {
notifier, err := SelectNotification(example)
assert.Nil(t, err)
assert.Equal(t, "0s", notifier.LastSent().String())
}
func TestWithinLimits(t *testing.T) {
notifier, err := SelectNotification(example)
assert.Nil(t, err)
assert.Equal(t, 10, notifier.Limits)
assert.True(t, inLimits(example))
}
func TestNotification_GetValue(t *testing.T) {
notifier, err := SelectNotification(example)
assert.Nil(t, err)
val := notifier.GetValue("Host")
assert.Equal(t, "http://demo.statping.com/api", val)
}
func TestOnSave(t *testing.T) {
err := example.OnSave()
assert.Equal(t, "onsave triggered", err.Error())
}
func TestOnSuccess(t *testing.T) {
OnSuccess(service)
assert.Equal(t, 2, len(example.Queue))
}
func TestOnFailure(t *testing.T) {
OnFailure(service, failure)
assert.Equal(t, 3, len(example.Queue))
}
func TestOnNewService(t *testing.T) {
OnNewService(service)
assert.Equal(t, 4, len(example.Queue))
}
func TestOnUpdatedService(t *testing.T) {
OnUpdatedService(service)
assert.Equal(t, 5, len(example.Queue))
}
func TestOnDeletedService(t *testing.T) {
OnDeletedService(service)
assert.Equal(t, 6, len(example.Queue))
}
func TestOnNewUser(t *testing.T) {
OnNewUser(user)
assert.Equal(t, 7, len(example.Queue))
}
func TestOnUpdatedUser(t *testing.T) {
OnUpdatedUser(user)
assert.Equal(t, 8, len(example.Queue))
}
func TestOnDeletedUser(t *testing.T) {
OnDeletedUser(user)
assert.Equal(t, 9, len(example.Queue))
}
func TestOnUpdatedNotifier(t *testing.T) {
OnUpdatedNotifier(example.Select())
assert.Equal(t, 11, len(example.Queue))
}
func TestRunAllQueueAndStop(t *testing.T) {
assert.True(t, example.IsRunning())
assert.Equal(t, 11, len(example.Queue))
go Queue(example)
time.Sleep(13 * time.Second)
assert.NotZero(t, len(example.Queue))
example.Close()
assert.False(t, example.IsRunning())
assert.NotZero(t, len(example.Queue))
}

View File

@ -48,8 +48,6 @@ func (s *Service) Create() error {
return err.Error()
}
allServices[s.Id] = s
go ServiceCheckQueue(allServices[s.Id], true)
return nil
}

View File

@ -5,7 +5,7 @@ import (
"time"
)
func Samples() {
func Samples() error {
createdOn := time.Now().Add(((-24 * 30) * 3) * time.Hour).UTC()
s1 := &Service{
Name: "Google",
@ -21,7 +21,9 @@ func Samples() {
VerifySSL: null.NewNullBool(true),
CreatedAt: createdOn,
}
s1.Create()
if err := s1.Create(); err != nil {
return err
}
s2 := &Service{
Name: "Statping Github",
@ -36,7 +38,9 @@ func Samples() {
VerifySSL: null.NewNullBool(true),
CreatedAt: createdOn,
}
s2.Create()
if err := s2.Create(); err != nil {
return err
}
s3 := &Service{
Name: "JSON Users Test",
@ -52,7 +56,9 @@ func Samples() {
GroupId: 2,
CreatedAt: createdOn,
}
s3.Create()
if err := s3.Create(); err != nil {
return err
}
s4 := &Service{
Name: "JSON API Tester",
@ -70,7 +76,9 @@ func Samples() {
GroupId: 2,
CreatedAt: createdOn,
}
s4.Create()
if err := s4.Create(); err != nil {
return err
}
s5 := &Service{
Name: "Google DNS",
@ -84,5 +92,9 @@ func Samples() {
GroupId: 1,
CreatedAt: createdOn,
}
s5.Create()
if err := s5.Create(); err != nil {
return err
}
return nil
}

View File

@ -1,107 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package services
import (
"github.com/hunterlong/statping/types/checkins"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
"time"
)
var (
testCheckin *checkins.Checkin
)
func TestCreateCheckin(t *testing.T) {
service, err := Find(2)
require.Nil(t, err)
checkin := &checkins.Checkin{
ServiceId: service.Id,
Interval: 10,
GracePeriod: 5,
}
err = checkin.Create()
require.Nil(t, err)
assert.NotZero(t, checkin.Id)
assert.NotEmpty(t, testCheckin.ApiKey)
assert.Equal(t, int64(10), testCheckin.Interval)
assert.Equal(t, int64(5), testCheckin.GracePeriod)
assert.True(t, testCheckin.Expected().Minutes() < 0)
}
func TestSelectCheckin(t *testing.T) {
service, err := Find(2)
require.Nil(t, err)
chks := service.Checkins()
assert.NotNil(t, chks)
assert.Equal(t, 1, len(chks))
c := chks[0]
assert.Equal(t, int64(10), c.Interval)
assert.Equal(t, int64(5), c.GracePeriod)
assert.Equal(t, 7, len(c.ApiKey))
}
func TestUpdateCheckin(t *testing.T) {
testCheckin.Interval = 60
testCheckin.GracePeriod = 15
err := testCheckin.Update()
require.Nil(t, err)
assert.NotZero(t, testCheckin.Id)
assert.NotEmpty(t, testCheckin.ApiKey)
service, err := Find(1)
require.Nil(t, err)
checkin := service.Checkins()[0]
assert.Equal(t, int64(60), checkin.Interval)
assert.Equal(t, int64(15), checkin.GracePeriod)
t.Log(testCheckin.Expected())
assert.True(t, testCheckin.Expected().Minutes() < 0)
}
func TestCreateCheckinHits(t *testing.T) {
service, err := Find(1)
require.Nil(t, err)
check := service.Checkins()
assert.Equal(t, 1, len(check))
created := time.Now().UTC().Add(-60 * time.Second)
hit := &checkins.CheckinHit{
Checkin: testCheckin.Id,
From: "192.168.1.1",
CreatedAt: created,
}
err = hit.Create()
require.Nil(t, err)
checks := service.Checkins()
assert.Equal(t, 1, len(checks))
}
func TestSelectCheckinMethods(t *testing.T) {
time.Sleep(5 * time.Second)
service, err := Find(1)
require.Nil(t, err)
checkins := service.Checkins()
assert.NotNil(t, checkins)
assert.Equal(t, float64(60), testCheckin.Period().Seconds())
assert.Equal(t, float64(15), testCheckin.Grace().Seconds())
t.Log(testCheckin.Expected())
lastHit := checkins[0]
assert.True(t, testCheckin.Expected().Seconds() < -5)
assert.False(t, lastHit.CreatedAt.IsZero())
}

View File

@ -1,332 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package services
import (
"github.com/hunterlong/statping/types/failures"
"github.com/hunterlong/statping/utils"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"testing"
"time"
)
var (
newServiceId int64
)
func TestSelectHTTPService(t *testing.T) {
services, err := SelectAllServices(false)
assert.Nil(t, err)
assert.Equal(t, 15, len(services))
assert.Equal(t, "Google", services[0].Name)
assert.Equal(t, "http", services[0].Type)
}
func TestSelectAllServices(t *testing.T) {
services := All()
for _, s := range services {
s.CheckService(false)
assert.False(t, s.IsRunning())
t.Logf("ID: %v %v\n", s.Id, s.Name)
}
assert.Equal(t, 15, len(services))
}
func TestServiceDowntime(t *testing.T) {
service, err := Find(15)
require.Nil(t, err)
downtime := service.Downtime()
assert.True(t, downtime.Seconds() > 0)
}
func TestSelectTCPService(t *testing.T) {
services := All()
assert.Equal(t, 15, len(services))
service, err := Find(5)
require.Nil(t, err)
assert.NotNil(t, service)
assert.Equal(t, "Google DNS", service.Name)
assert.Equal(t, "tcp", service.Type)
}
func TestUpdateService(t *testing.T) {
service, err := Find(1)
require.Nil(t, err)
assert.Equal(t, "Google", service.Name)
service.Name = "Updated Google"
service.Interval = 5
err = service.Update()
require.Nil(t, err)
// check if updating pointer array shutdown any other service
service, err = Find(1)
require.Nil(t, err)
assert.Equal(t, "Updated Google", service.Name)
assert.Equal(t, 5, service.Interval)
}
func TestUpdateAllServices(t *testing.T) {
services, err := SelectAllServices(false)
require.Nil(t, err)
var i int
for _, srv := range services {
srv.Name = "Changed " + srv.Name
srv.Interval = i + 3
err := srv.Update()
require.Nil(t, err)
i++
}
}
func TestServiceHTTPCheck(t *testing.T) {
service, err := Find(1)
require.Nil(t, err)
service.CheckService(true)
assert.Equal(t, "Changed Updated Google", service.Name)
assert.True(t, service.Online)
}
func TestCheckHTTPService(t *testing.T) {
service, err := Find(1)
require.Nil(t, err)
assert.Equal(t, "Changed Updated Google", service.Name)
assert.True(t, service.Online)
assert.Equal(t, 200, service.LastStatusCode)
assert.NotZero(t, service.Latency)
assert.NotZero(t, service.PingTime)
}
func TestServiceTCPCheck(t *testing.T) {
service, err := Find(5)
require.Nil(t, err)
service.CheckService(false)
assert.Equal(t, "Changed Google DNS", service.Name)
assert.True(t, service.Online)
}
func TestCheckTCPService(t *testing.T) {
service, err := Find(5)
require.Nil(t, err)
assert.Equal(t, "Changed Google DNS", service.Name)
assert.True(t, service.Online)
assert.NotZero(t, service.Latency)
assert.NotZero(t, service.PingTime)
}
func TestServiceOnline24Hours(t *testing.T) {
since := utils.Now().Add(-24 * time.Hour).Add(-10 * time.Minute)
service, err := Find(1)
require.Nil(t, err)
assert.Equal(t, float32(100), service.OnlineSince(since))
service2, err := Find(5)
require.Nil(t, err)
assert.Equal(t, float32(100), service2.OnlineSince(since))
service3, err := Find(14)
require.Nil(t, err)
assert.True(t, service3.OnlineSince(since) > float32(49))
}
func TestServiceAvgUptime(t *testing.T) {
since := utils.Now().Add(-24 * time.Hour).Add(-10 * time.Minute)
service, err := Find(1)
require.Nil(t, err)
assert.NotEqual(t, "0.00", service.AvgTime())
service2, err := Find(5)
assert.Equal(t, "100", service2.AvgTime())
service3, err := Find(13)
assert.NotEqual(t, "0", service3.HitsSince(since).Avg())
service4, err := Find(15)
assert.NotEqual(t, "0", service4.HitsSince(since).Avg())
}
func TestCreateService(t *testing.T) {
s := &Service{
Name: "That'll do 🐢",
Domain: "https://www.youtube.com/watch?v=rjQtzV9IZ0Q",
ExpectedStatus: 200,
Interval: 3,
Type: "http",
Method: "GET",
Timeout: 20,
GroupId: 1,
}
err := s.Create()
require.Nil(t, err)
assert.NotZero(t, s.Id)
newService, err := Find(s.Id)
assert.Equal(t, "That'll do 🐢", newService.Name)
}
func TestViewNewService(t *testing.T) {
newService, err := Find(newServiceId)
require.Nil(t, err)
assert.Equal(t, "That'll do 🐢", newService.Name)
}
func TestCreateFailingHTTPService(t *testing.T) {
s := &Service{
Name: "Bad URL",
Domain: "http://localhost/iamnothere",
ExpectedStatus: 200,
Interval: 2,
Type: "http",
Method: "GET",
Timeout: 5,
GroupId: 1,
}
err := s.Create()
require.Nil(t, err)
assert.NotZero(t, s.Id)
newService, err := Find(s.Id)
require.Nil(t, err)
assert.Equal(t, "Bad URL", newService.Name)
t.Log("new service ID: ", newServiceId)
}
func TestServiceFailedCheck(t *testing.T) {
service, err := Find(17)
require.Nil(t, err)
assert.Equal(t, "Bad URL", service.Name)
service.CheckService(false)
assert.Equal(t, "Bad URL", service.Name)
assert.False(t, service.Online)
}
func TestCreateFailingTCPService(t *testing.T) {
s := &Service{
Name: "Bad TCP",
Domain: "localhost",
Port: 5050,
Interval: 30,
Type: "tcp",
Timeout: 5,
GroupId: 1,
}
err := s.Create()
assert.Nil(t, err)
assert.NotZero(t, s.Id)
newService, err := Find(s.Id)
require.Nil(t, err)
assert.Equal(t, "Bad TCP", newService.Name)
t.Log("new failing tcp service ID: ", newServiceId)
}
func TestServiceFailedTCPCheck(t *testing.T) {
srv, err := Find(newServiceId)
require.Nil(t, err)
srv.CheckService(false)
assert.Equal(t, "Bad TCP", srv.Name)
assert.False(t, srv.Online)
}
func TestCreateServiceFailure(t *testing.T) {
service, err := Find(8)
fail := &failures.Failure{
Issue: "This is not an issue, but it would container HTTP response errors.",
Method: "http",
Service: service.Id,
}
err = fail.Create()
assert.Nil(t, err)
assert.NotZero(t, fail.Id)
}
func TestDeleteService(t *testing.T) {
service, err := Find(newServiceId)
count, err := SelectAllServices(false)
assert.Nil(t, err)
assert.Equal(t, 18, len(count))
err = service.Delete()
assert.Nil(t, err)
services := All()
assert.Equal(t, 17, len(services))
}
func TestServiceCloseRoutine(t *testing.T) {
s := new(Service)
s.Name = "example"
s.Domain = "https://google.com"
s.Type = "http"
s.Method = "GET"
s.ExpectedStatus = 200
s.Interval = 1
s.Start()
assert.True(t, s.IsRunning())
t.Log(s.Checkpoint)
t.Log(s.SleepDuration)
go ServiceCheckQueue(s, false)
t.Log(s.Checkpoint)
t.Log(s.SleepDuration)
time.Sleep(5 * time.Second)
t.Log(s.Checkpoint)
t.Log(s.SleepDuration)
assert.True(t, s.IsRunning())
s.Close()
assert.False(t, s.IsRunning())
s.Close()
assert.False(t, s.IsRunning())
}
func TestServiceCheckQueue(t *testing.T) {
s := new(Service)
s.Name = "example"
s.Domain = "https://google.com"
s.Type = "http"
s.Method = "GET"
s.ExpectedStatus = 200
s.Interval = 1
s.Start()
assert.True(t, s.IsRunning())
go ServiceCheckQueue(s, false)
go func() {
time.Sleep(5 * time.Second)
t.Log(s.Checkpoint)
time.Sleep(6 * time.Second)
}()
time.Sleep(5 * time.Second)
assert.True(t, s.IsRunning())
s.Close()
assert.False(t, s.IsRunning())
s.Close()
assert.False(t, s.IsRunning())
}
func TestDNScheckService(t *testing.T) {
s := new(Service)
s.Name = "example"
s.Domain = "http://localhost:9000"
s.Type = "http"
s.Method = "GET"
s.ExpectedStatus = 200
s.Interval = 1
amount, err := dnsCheck(s)
assert.Nil(t, err)
assert.NotZero(t, amount)
}
func TestFindLink(t *testing.T) {
service, err := Find(1)
require.Nil(t, err)
assert.Equal(t, "google", service.Permalink.String)
}

View File

@ -51,10 +51,10 @@ func durationStr(d time.Duration) string {
return "2006-01-02T00:00:00Z"
case m >= Day.Seconds():
return "2006-01-02T15:00:00Z"
return "2006-01-02T00:00:00Z"
case m >= Hour.Seconds():
return "2006-01-02T15:04:00Z"
return "2006-01-02T15:00:00Z"
case m >= Minute.Seconds():
return "2006-01-02T15:04:00Z"

View File

@ -33,14 +33,6 @@ func TestFixedTime(t *testing.T) {
timeVal,
Day,
"2020-05-22T00:00:00Z",
}, {
timeVal.Add(2 * Month),
Month,
"2020-07-01T00:00:00Z",
}, {
timeVal.Add(2 * Year),
Year,
"2022-01-01T00:00:00Z",
}}
for _, e := range examples {

View File

@ -4,7 +4,7 @@ import (
"github.com/hunterlong/statping/types/null"
)
func Samples() {
func Samples() error {
u2 := &User{
Username: "testadmin",
Password: "password123",
@ -12,7 +12,9 @@ func Samples() {
Admin: null.NewNullBool(true),
}
u2.Create()
if err := u2.Create(); err != nil {
return err
}
u3 := &User{
Username: "testadmin2",
@ -21,5 +23,9 @@ func Samples() {
Admin: null.NewNullBool(true),
}
u3.Create()
if err := u3.Create(); err != nil {
return err
}
return nil
}

View File

@ -11,7 +11,7 @@ type User struct {
Id int64 `gorm:"primary_key;column:id" json:"id"`
Username string `gorm:"type:varchar(100);unique;column:username;" json:"username,omitempty"`
Password string `gorm:"column:password" json:"password,omitempty"`
Email string `gorm:"type:varchar(100);unique;column:email" json:"email,omitempty"`
Email string `gorm:"type:varchar(100);column:email" json:"email,omitempty"`
ApiKey string `gorm:"column:api_key" json:"api_key,omitempty"`
ApiSecret string `gorm:"column:api_secret" json:"api_secret,omitempty"`
Admin null.NullBool `gorm:"column:administrator" json:"admin,omitempty"`

View File

@ -1,117 +0,0 @@
// Statping
// Copyright (C) 2018. Hunter Long and the project contributors
// Written by Hunter Long <info@socialeck.com> and the project contributors
//
// https://github.com/hunterlong/statping
//
// The licenses for most software and other practical works are designed
// to take away your freedom to share and change the works. By contrast,
// the GNU General Public License is intended to guarantee your freedom to
// share and change all versions of a program--to make sure it remains free
// software for all its users.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package users
import (
"github.com/hunterlong/statping/database"
"github.com/hunterlong/statping/types/null"
"github.com/stretchr/testify/assert"
"testing"
)
func TestCreateUser(t *testing.T) {
user := &User{
Username: "hunter",
Password: "password123",
Email: "test@email.com",
Admin: null.NewNullBool(true),
}
err := user.Create()
assert.Nil(t, err)
assert.NotZero(t, user.Id)
}
func TestSelectAllUsers(t *testing.T) {
users := All()
assert.Equal(t, 3, len(users))
}
func TestSelectUser(t *testing.T) {
user, err := Find(1)
assert.Nil(t, err)
assert.Equal(t, "info@betatude.com", user.Email)
assert.True(t, user.Admin.Bool)
}
func TestSelectUsername(t *testing.T) {
user, err := FindByUsername("hunter")
assert.Nil(t, err)
assert.Equal(t, "test@email.com", user.Email)
assert.Equal(t, int64(3), user.Id)
assert.True(t, user.Admin.Bool)
}
func TestUpdateUser(t *testing.T) {
user, err := Find(1)
assert.Nil(t, err)
user.Username = "updated"
err = user.Update()
assert.Nil(t, err)
updatedUser, err := Find(1)
assert.Nil(t, err)
assert.Equal(t, "updated", updatedUser.Username)
}
func TestCreateUser2(t *testing.T) {
user := &User{
Username: "hunterlong",
Password: "password123",
Email: "User@email.com",
Admin: null.NewNullBool(true),
}
err := user.Create()
assert.Nil(t, err)
assert.NotZero(t, user.Id)
}
func TestSelectAllUsersAgain(t *testing.T) {
users := All()
assert.Equal(t, 4, len(users))
}
func TestAuthUser(t *testing.T) {
user, auth := AuthUser("hunterlong", "password123")
assert.True(t, auth)
assert.NotNil(t, user)
assert.Equal(t, "User@email.com", user.Email)
assert.Equal(t, int64(4), user.Id)
assert.True(t, user.Admin.Bool)
}
func TestFailedAuthUser(t *testing.T) {
user, auth := AuthUser("hunterlong", "wrongpassword")
assert.False(t, auth)
assert.Nil(t, user)
}
func TestCheckPassword(t *testing.T) {
user, err := Find(2)
assert.Nil(t, err)
pass := CheckHash("password123", user.Password)
assert.True(t, pass)
}
func TestDeleteUser(t *testing.T) {
user, err := Find(2)
assert.Nil(t, err)
err = user.Delete()
assert.Nil(t, err)
}
func TestDbConfig_Close(t *testing.T) {
err := database.Close()
assert.Nil(t, err)
}