mirror of https://github.com/statping/statping
vue
parent
de4143b4f8
commit
d89175a340
23
core/core.go
23
core/core.go
|
@ -64,9 +64,11 @@ func (c *Core) ToCore() *types.Core {
|
|||
func InitApp() {
|
||||
SelectCore()
|
||||
InsertNotifierDB()
|
||||
InsertIntegratorDB()
|
||||
CoreApp.SelectAllServices(true)
|
||||
checkServices()
|
||||
AttachNotifiers()
|
||||
AddIntegrations()
|
||||
CoreApp.Notifications = notifier.AllCommunications
|
||||
CoreApp.Integrations = integrations.Integrations
|
||||
go DatabaseMaintence()
|
||||
|
@ -85,6 +87,18 @@ func InsertNotifierDB() error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// InsertIntegratorDB inject the Statping database instance to the Integrations package
|
||||
func InsertIntegratorDB() error {
|
||||
if DbSession == nil {
|
||||
err := CoreApp.Connect(false, utils.Directory)
|
||||
if err != nil {
|
||||
return errors.New("database connection has not been created")
|
||||
}
|
||||
}
|
||||
integrations.SetDB(DbSession)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateCore will update the CoreApp variable inside of the 'core' table in database
|
||||
func UpdateCore(c *Core) (*Core, error) {
|
||||
db := coreDB().Update(&c)
|
||||
|
@ -198,6 +212,15 @@ func AttachNotifiers() error {
|
|||
)
|
||||
}
|
||||
|
||||
// AddIntegrations will attach all the integrations into the system
|
||||
func AddIntegrations() error {
|
||||
return integrations.AddIntegrations(
|
||||
integrations.CsvIntegrator,
|
||||
integrations.TraefikIntegrator,
|
||||
integrations.DockerIntegrator,
|
||||
)
|
||||
}
|
||||
|
||||
// ServiceOrder will reorder the services based on 'order_id' (Order)
|
||||
type ServiceOrder []types.ServiceInterface
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ var (
|
|||
)
|
||||
|
||||
func init() {
|
||||
DbModels = []interface{}{&types.Service{}, &types.User{}, &types.Hit{}, &types.Failure{}, &types.Message{}, &types.Group{}, &types.Checkin{}, &types.CheckinHit{}, ¬ifier.Notification{}, &types.Incident{}, &types.IncidentUpdate{}}
|
||||
DbModels = []interface{}{&types.Service{}, &types.User{}, &types.Hit{}, &types.Failure{}, &types.Message{}, &types.Group{}, &types.Checkin{}, &types.CheckinHit{}, ¬ifier.Notification{}, &types.Incident{}, &types.IncidentUpdate{}, &types.Integration{}}
|
||||
|
||||
gorm.NowFunc = func() time.Time {
|
||||
return time.Now().UTC()
|
||||
|
|
|
@ -13,6 +13,18 @@ type IncidentUpdate struct {
|
|||
*types.IncidentUpdate
|
||||
}
|
||||
|
||||
// ReturnIncident returns *core.Incident based off a *types.Incident
|
||||
func ReturnIncident(u *types.Incident) *Incident {
|
||||
return &Incident{u}
|
||||
}
|
||||
|
||||
// SelectIncident returns the Incident based on the Incident's ID.
|
||||
func SelectIncident(id int64) (*Incident, error) {
|
||||
var incident Incident
|
||||
err := incidentsDB().Where("id = ?", id).First(&incident)
|
||||
return &incident, err.Error()
|
||||
}
|
||||
|
||||
// AllIncidents will return all incidents and updates recorded
|
||||
func AllIncidents() []*Incident {
|
||||
var incidents []*Incident
|
||||
|
|
|
@ -32,7 +32,7 @@ type csvIntegration struct {
|
|||
*types.Integration
|
||||
}
|
||||
|
||||
var csvIntegrator = &csvIntegration{&types.Integration{
|
||||
var CsvIntegrator = &csvIntegration{&types.Integration{
|
||||
ShortName: "csv",
|
||||
Name: "CSV File",
|
||||
Icon: "<i class=\"fas fa-file-csv\"></i>",
|
||||
|
|
|
@ -14,23 +14,23 @@ func TestCsvFileIntegration(t *testing.T) {
|
|||
t.Run("Set Field Value", func(t *testing.T) {
|
||||
formPost := map[string][]string{}
|
||||
formPost["input"] = []string{string(data)}
|
||||
_, err = SetFields(csvIntegrator, formPost)
|
||||
_, err = SetFields(CsvIntegrator, formPost)
|
||||
require.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("Get Field Value", func(t *testing.T) {
|
||||
value := Value(csvIntegrator, "input").(string)
|
||||
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()
|
||||
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()
|
||||
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)
|
||||
|
|
|
@ -27,7 +27,7 @@ type dockerIntegration struct {
|
|||
*types.Integration
|
||||
}
|
||||
|
||||
var dockerIntegrator = &dockerIntegration{&types.Integration{
|
||||
var DockerIntegrator = &dockerIntegration{&types.Integration{
|
||||
ShortName: "docker",
|
||||
Name: "Docker",
|
||||
Icon: "<i class=\"fab fa-docker\"></i>",
|
||||
|
|
|
@ -12,25 +12,25 @@ func TestDockerIntegration(t *testing.T) {
|
|||
formPost := map[string][]string{}
|
||||
formPost["path"] = []string{"unix:///var/run/docker.sock"}
|
||||
formPost["version"] = []string{"1.25"}
|
||||
_, err := SetFields(csvIntegrator, formPost)
|
||||
_, 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)
|
||||
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()
|
||||
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()
|
||||
services, err := DockerIntegrator.List()
|
||||
require.Nil(t, err)
|
||||
for _, s := range services {
|
||||
t.Log(s)
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
package integrations
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
)
|
||||
|
@ -24,14 +26,25 @@ import (
|
|||
var (
|
||||
Integrations []types.Integrator
|
||||
log = utils.Log.WithField("type", "integration")
|
||||
db types.Database
|
||||
)
|
||||
|
||||
func init() {
|
||||
Integrations = append(Integrations,
|
||||
csvIntegrator,
|
||||
dockerIntegrator,
|
||||
traefikIntegrator,
|
||||
)
|
||||
//func init() {
|
||||
// Integrations = append(Integrations,
|
||||
// CsvIntegrator,
|
||||
// DockerIntegrator,
|
||||
// TraefikIntegrator,
|
||||
// )
|
||||
//}
|
||||
|
||||
// integrationsDb returns the 'integrations' database column
|
||||
func integrationsDb() types.Database {
|
||||
return db.Model(&types.Integration{})
|
||||
}
|
||||
|
||||
// SetDB is called by core to inject the database for a integrator to use
|
||||
func SetDB(d types.Database) {
|
||||
db = d
|
||||
}
|
||||
|
||||
func Value(intg types.Integrator, fieldName string) interface{} {
|
||||
|
@ -43,6 +56,35 @@ func Value(intg types.Integrator, fieldName string) interface{} {
|
|||
return nil
|
||||
}
|
||||
|
||||
func Update(integrator *types.Integration) error {
|
||||
fields := FieldsToJson(integrator)
|
||||
fmt.Println(fields)
|
||||
set := db.Model(&types.Integration{}).Where("name = ?", integrator.Name)
|
||||
set.Set("enabled", integrator.Enabled)
|
||||
set.Set("fields", fields)
|
||||
return set.Error()
|
||||
}
|
||||
|
||||
func FieldsToJson(integrator *types.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 types.Integrator, input string) []*types.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 types.Integrator, data map[string][]string) (*types.Integration, error) {
|
||||
i := intg.Get()
|
||||
for _, v := range i.Fields {
|
||||
|
@ -62,3 +104,63 @@ func Find(name string) (types.Integrator, error) {
|
|||
}
|
||||
return nil, errors.New(name + " not found")
|
||||
}
|
||||
|
||||
// db will return the notifier database column/record
|
||||
func integratorDb(n *types.Integration) types.Database {
|
||||
return db.Model(&types.Integration{}).Where("name = ?", n.Name).Find(n)
|
||||
}
|
||||
|
||||
// isInDatabase returns true if the integration has already been installed
|
||||
func isInDatabase(i types.Integrator) bool {
|
||||
inDb := integratorDb(i.Get()).RecordNotFound()
|
||||
return !inDb
|
||||
}
|
||||
|
||||
// SelectIntegration returns the Notification struct from the database
|
||||
func SelectIntegration(i types.Integrator) (*types.Integration, error) {
|
||||
integration := i.Get()
|
||||
err := db.Model(&types.Integration{}).Where("name = ?", integration.Name).Scan(&integration)
|
||||
return integration, err.Error()
|
||||
}
|
||||
|
||||
// AddIntegrations accept a Integrator interface to be added into the array
|
||||
func AddIntegrations(integrations ...types.Integrator) error {
|
||||
for _, i := range integrations {
|
||||
if utils.IsType(i, new(types.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 types.Integrator) error {
|
||||
inDb := isInDatabase(i)
|
||||
log.WithField("installed", inDb).
|
||||
WithFields(utils.ToFields(i)).
|
||||
Debugln(fmt.Sprintf("Checking if integrator '%v' is installed: %v", i.Get().Name, inDb))
|
||||
if !inDb {
|
||||
_, err := insertDatabase(i)
|
||||
if err != nil {
|
||||
log.Errorln(err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// insertDatabase will create a new record into the database for the integrator
|
||||
func insertDatabase(i types.Integrator) (string, error) {
|
||||
integrator := i.Get()
|
||||
query := db.Create(integrator)
|
||||
if query.Error() != nil {
|
||||
return "", query.Error()
|
||||
}
|
||||
return integrator.Name, query.Error()
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ type traefikIntegration struct {
|
|||
*types.Integration
|
||||
}
|
||||
|
||||
var traefikIntegrator = &traefikIntegration{&types.Integration{
|
||||
var TraefikIntegrator = &traefikIntegration{&types.Integration{
|
||||
ShortName: "traefik",
|
||||
Name: "Traefik",
|
||||
Icon: "<i class=\"fas fa-network-wired\"></i>",
|
||||
|
|
|
@ -10,14 +10,14 @@ func TestTraefikIntegration(t *testing.T) {
|
|||
|
||||
t.Run("List Services from Traefik", func(t *testing.T) {
|
||||
t.SkipNow()
|
||||
services, err := traefikIntegrator.List()
|
||||
services, err := TraefikIntegrator.List()
|
||||
require.Nil(t, err)
|
||||
assert.NotEqual(t, 0, len(services))
|
||||
})
|
||||
|
||||
t.Run("Confirm Services from Traefik", func(t *testing.T) {
|
||||
t.SkipNow()
|
||||
services, err := traefikIntegrator.List()
|
||||
services, err := TraefikIntegrator.List()
|
||||
require.Nil(t, err)
|
||||
for _, s := range services {
|
||||
t.Log(s)
|
||||
|
|
|
@ -24,7 +24,7 @@ import (
|
|||
// OnSave will trigger a notifier when it has been saved - Notifier interface
|
||||
func OnSave(method string) {
|
||||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(Notifier)) {
|
||||
if utils.IsType(comm, new(Notifier)) {
|
||||
notifier := comm.(Notifier)
|
||||
if notifier.Select().Method == method {
|
||||
notifier.OnSave()
|
||||
|
@ -52,11 +52,11 @@ func OnFailure(s *types.Service, f *types.Failure) {
|
|||
|
||||
sendMessages:
|
||||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(BasicEvents)) && isEnabled(comm) && (s.Online || inLimits(comm)) {
|
||||
if utils.IsType(comm, new(BasicEvents)) && isEnabled(comm) && (s.Online || inLimits(comm)) {
|
||||
notifier := comm.(Notifier).Select()
|
||||
log.
|
||||
WithField("trigger", "OnFailure").
|
||||
WithFields(utils.ToFields(notifier, s)).Infoln(fmt.Sprintf("Sending [OnFailure] '%v' notification for service %v", notifier.Method, s.Name))
|
||||
WithFields(utils.ToFields(notifier, s)).Debugln(fmt.Sprintf("Sending [OnFailure] '%v' notification for service %v", notifier.Method, s.Name))
|
||||
comm.(BasicEvents).OnFailure(s, f)
|
||||
}
|
||||
}
|
||||
|
@ -74,11 +74,11 @@ func OnSuccess(s *types.Service) {
|
|||
}
|
||||
|
||||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(BasicEvents)) && isEnabled(comm) && (!s.Online || inLimits(comm)) {
|
||||
if utils.IsType(comm, new(BasicEvents)) && isEnabled(comm) && (!s.Online || inLimits(comm)) {
|
||||
notifier := comm.(Notifier).Select()
|
||||
log.
|
||||
WithField("trigger", "OnSuccess").
|
||||
WithFields(utils.ToFields(notifier, s)).Infoln(fmt.Sprintf("Sending [OnSuccess] '%v' notification for service %v", notifier.Method, s.Name))
|
||||
WithFields(utils.ToFields(notifier, s)).Debugln(fmt.Sprintf("Sending [OnSuccess] '%v' notification for service %v", notifier.Method, s.Name))
|
||||
comm.(BasicEvents).OnSuccess(s)
|
||||
}
|
||||
}
|
||||
|
@ -87,10 +87,10 @@ func OnSuccess(s *types.Service) {
|
|||
// OnNewService is triggered when a new service is created - ServiceEvents interface
|
||||
func OnNewService(s *types.Service) {
|
||||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||
if utils.IsType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||
log.
|
||||
WithField("trigger", "OnNewService").
|
||||
Infoln(fmt.Sprintf("Sending new service notification for service %v", s.Name))
|
||||
Debugln(fmt.Sprintf("Sending new service notification for service %v", s.Name))
|
||||
comm.(ServiceEvents).OnNewService(s)
|
||||
}
|
||||
}
|
||||
|
@ -102,8 +102,8 @@ func OnUpdatedService(s *types.Service) {
|
|||
return
|
||||
}
|
||||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||
log.Infoln(fmt.Sprintf("Sending updated service notification for service %v", s.Name))
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -115,8 +115,8 @@ func OnDeletedService(s *types.Service) {
|
|||
return
|
||||
}
|
||||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(ServiceEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||
log.Infoln(fmt.Sprintf("Sending deleted service notification for service %v", s.Name))
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -125,8 +125,8 @@ func OnDeletedService(s *types.Service) {
|
|||
// OnNewUser is triggered when a new user is created - UserEvents interface
|
||||
func OnNewUser(u *types.User) {
|
||||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||
log.Infoln(fmt.Sprintf("Sending new user notification for user %v", u.Username))
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -135,8 +135,8 @@ func OnNewUser(u *types.User) {
|
|||
// OnUpdatedUser is triggered when a new user is updated - UserEvents interface
|
||||
func OnUpdatedUser(u *types.User) {
|
||||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||
log.Infoln(fmt.Sprintf("Sending updated user notification for user %v", u.Username))
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -145,8 +145,8 @@ func OnUpdatedUser(u *types.User) {
|
|||
// OnDeletedUser is triggered when a new user is deleted - UserEvents interface
|
||||
func OnDeletedUser(u *types.User) {
|
||||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(UserEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||
log.Infoln(fmt.Sprintf("Sending deleted user notification for user %v", u.Username))
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
@ -155,8 +155,8 @@ func OnDeletedUser(u *types.User) {
|
|||
// OnUpdatedCore is triggered when the CoreApp settings are saved - CoreEvents interface
|
||||
func OnUpdatedCore(c *types.Core) {
|
||||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(CoreEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||
log.Infoln(fmt.Sprintf("Sending updated core notification"))
|
||||
if utils.IsType(comm, new(CoreEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||
log.Debugln(fmt.Sprintf("Sending updated core notification"))
|
||||
comm.(CoreEvents).OnUpdatedCore(c)
|
||||
}
|
||||
}
|
||||
|
@ -165,7 +165,7 @@ func OnUpdatedCore(c *types.Core) {
|
|||
// OnStart is triggered when the Statping service has started
|
||||
func OnStart(c *types.Core) {
|
||||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(CoreEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||
if utils.IsType(comm, new(CoreEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||
comm.(CoreEvents).OnUpdatedCore(c)
|
||||
}
|
||||
}
|
||||
|
@ -174,7 +174,7 @@ func OnStart(c *types.Core) {
|
|||
// OnNewNotifier is triggered when a new notifier is loaded
|
||||
func OnNewNotifier(n *Notification) {
|
||||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(NotifierEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||
if utils.IsType(comm, new(NotifierEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||
comm.(NotifierEvents).OnNewNotifier(n)
|
||||
}
|
||||
}
|
||||
|
@ -183,7 +183,7 @@ func OnNewNotifier(n *Notification) {
|
|||
// OnUpdatedNotifier is triggered when a notifier has been updated
|
||||
func OnUpdatedNotifier(n *Notification) {
|
||||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(NotifierEvents)) && isEnabled(comm) && inLimits(comm) {
|
||||
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)
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ func (n *Notification) AfterFind() (err error) {
|
|||
func (n *Notification) AddQueue(uid string, msg interface{}) {
|
||||
data := &QueueData{uid, msg}
|
||||
n.Queue = append(n.Queue, data)
|
||||
log.WithFields(utils.ToFields(data, n)).Infoln(fmt.Sprintf("Notifier '%v' added new item (%v) to the queue. (%v queued)", n.Method, uid, len(n.Queue)))
|
||||
log.WithFields(utils.ToFields(data, n)).Debug(fmt.Sprintf("Notifier '%v' added new item (%v) to the queue. (%v queued)", n.Method, uid, len(n.Queue)))
|
||||
}
|
||||
|
||||
// CanTest returns true if the notifier implements the OnTest interface
|
||||
|
@ -129,7 +129,7 @@ func asNotification(n Notifier) *Notification {
|
|||
// AddNotifier accept a Notifier interface to be added into the array
|
||||
func AddNotifiers(notifiers ...Notifier) error {
|
||||
for _, n := range notifiers {
|
||||
if isType(n, new(Notifier)) {
|
||||
if utils.IsType(n, new(Notifier)) {
|
||||
err := checkNotifierForm(n)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -252,7 +252,7 @@ func Init(n Notifier) (*Notification, error) {
|
|||
if notify.Delay.Seconds() == 0 {
|
||||
notify.Delay = time.Duration(1 * time.Second)
|
||||
}
|
||||
notify.testable = isType(n, new(Tester))
|
||||
notify.testable = utils.IsType(n, new(Tester))
|
||||
notify.Form = n.Select().Form
|
||||
}
|
||||
return notify, err
|
||||
|
@ -261,7 +261,7 @@ func Init(n Notifier) (*Notification, error) {
|
|||
// startAllNotifiers will start the go routine for each loaded notifier
|
||||
func startAllNotifiers() {
|
||||
for _, comm := range AllCommunications {
|
||||
if isType(comm, new(Notifier)) {
|
||||
if utils.IsType(comm, new(Notifier)) {
|
||||
notify := comm.(Notifier)
|
||||
if notify.Select().Enabled.Bool {
|
||||
notify.Select().close()
|
||||
|
@ -290,9 +290,9 @@ CheckNotifier:
|
|||
msg := notification.Queue[0]
|
||||
err := n.Send(msg.Data)
|
||||
if err != nil {
|
||||
log.WithFields(utils.ToFields(notification, msg)).Warnln(fmt.Sprintf("Notifier '%v' had an error: %v", notification.Method, err))
|
||||
log.WithFields(utils.ToFields(notification, msg)).Error(fmt.Sprintf("Notifier '%v' had an error: %v", notification.Method, err))
|
||||
} else {
|
||||
log.WithFields(utils.ToFields(notification, msg)).Infoln(fmt.Sprintf("Notifier '%v' sent outgoing message (%v) %v left in queue.", notification.Method, msg.Id, len(notification.Queue)))
|
||||
log.WithFields(utils.ToFields(notification, msg)).Debug(fmt.Sprintf("Notifier '%v' sent outgoing message (%v) %v left in queue.", notification.Method, msg.Id, len(notification.Queue)))
|
||||
}
|
||||
notification.makeLog(msg.Data)
|
||||
if len(notification.Queue) > 1 {
|
||||
|
@ -386,13 +386,6 @@ func (n *Notification) GetValue(dbField string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
// isType will return true if a variable can implement an interface
|
||||
func isType(n interface{}, obj interface{}) bool {
|
||||
one := reflect.TypeOf(n)
|
||||
two := reflect.ValueOf(obj).Elem()
|
||||
return one.Implements(two.Type())
|
||||
}
|
||||
|
||||
// isEnabled returns true if the notifier is enabled
|
||||
func isEnabled(n interface{}) bool {
|
||||
notifier := n.(Notifier).Select()
|
||||
|
|
|
@ -548,6 +548,10 @@ func TmpRecords(dbFile string) error {
|
|||
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 := CoreApp.SelectAllServices(false); err != nil {
|
||||
return err
|
||||
|
@ -555,6 +559,9 @@ func TmpRecords(dbFile string) error {
|
|||
if err := AttachNotifiers(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := AddIntegrations(); err != nil {
|
||||
return err
|
||||
}
|
||||
CoreApp.Notifications = notifier.AllCommunications
|
||||
return nil
|
||||
}
|
||||
|
@ -584,6 +591,10 @@ func TmpRecords(dbFile string) error {
|
|||
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 := CoreApp.SelectAllServices(false); err != nil {
|
||||
return err
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<div id="app" v-if="loaded">
|
||||
<div id="app">
|
||||
<router-view/>
|
||||
<Footer version="DEV" v-if="$route.path !== '/setup'"/>
|
||||
</div>
|
||||
|
|
|
@ -49,13 +49,17 @@ class Api {
|
|||
}
|
||||
|
||||
async services_reorder (data) {
|
||||
return axios.post('/api/services/reorder', data).then(response => (response.data))
|
||||
return axios.post('/api/reorder/services', data).then(response => (response.data))
|
||||
}
|
||||
|
||||
async groups () {
|
||||
return axios.get('/api/groups').then(response => (response.data))
|
||||
}
|
||||
|
||||
async groups_reorder (data) {
|
||||
return axios.post('/api/reorder/groups', data).then(response => (response.data))
|
||||
}
|
||||
|
||||
async group_delete (id) {
|
||||
return axios.delete('/api/groups/'+id).then(response => (response.data))
|
||||
}
|
||||
|
@ -116,10 +120,38 @@ 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))
|
||||
}
|
||||
|
||||
async cache () {
|
||||
return axios.get('/api/cache').then(response => (response.data))
|
||||
}
|
||||
|
||||
async clearCache () {
|
||||
return axios.get('/api/clear_cache').then(response => (response.data))
|
||||
}
|
||||
|
||||
async logs () {
|
||||
return axios.get('/api/logs').then(response => (response.data))
|
||||
}
|
||||
|
||||
async logs_last () {
|
||||
return axios.get('/api/logs/last').then(response => (response.data))
|
||||
}
|
||||
|
||||
async login (username, password) {
|
||||
const f = {username: username, password: password}
|
||||
return axios.post('/api/login', qs.stringify(f))
|
||||
|
@ -138,7 +170,11 @@ class Api {
|
|||
}
|
||||
|
||||
token () {
|
||||
return JSON.parse(localStorage.getItem(tokenKey));
|
||||
const tk = localStorage.getItem(tokenKey)
|
||||
if (!tk) {
|
||||
return {};
|
||||
}
|
||||
return JSON.parse(tk);
|
||||
}
|
||||
|
||||
authToken () {
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<draggable tag="tbody" v-model="servicesList" :list="$store.getters.servicesInOrder" :key="this.$store.getters.servicesInOrder.length" class="sortable" handle=".drag_icon">
|
||||
<tr v-for="(service, index) in $store.getters.services" :key="index">
|
||||
<draggable tag="tbody" v-model="servicesList" :key="$store.getters.servicesInOrder.length" class="sortable" handle=".drag_icon">
|
||||
<tr v-for="(service, index) in $store.getters.servicesInOrder" :key="index">
|
||||
<td>
|
||||
<span class="drag_icon d-none d-md-inline">
|
||||
<font-awesome-icon icon="bars" />
|
||||
|
@ -66,7 +66,7 @@
|
|||
</thead>
|
||||
|
||||
<draggable tag="tbody" v-model="groupsList" class="sortable_groups" handle=".drag_icon">
|
||||
<tr v-for="(group, index) in $store.getters.groupsCleaned" v-bind:key="index">
|
||||
<tr v-for="(group, index) in $store.getters.groupsInOrder" v-bind:key="index">
|
||||
<td><span class="drag_icon d-none d-md-inline"><font-awesome-icon icon="bars" /></span> {{group.name}}</td>
|
||||
<td>{{$store.getters.servicesInGroup(group.id).length}}</td>
|
||||
<td>
|
||||
|
@ -121,23 +121,23 @@
|
|||
value.forEach((s, k) => {
|
||||
data.push({service: s.id, order: k+1})
|
||||
});
|
||||
alert(JSON.stringify(data))
|
||||
const ord = await Api.services_reorder(data)
|
||||
alert(JSON.parse(ord))
|
||||
await Api.services_reorder(data)
|
||||
const services = await Api.services()
|
||||
this.$store.commit('setServices', services)
|
||||
}
|
||||
},
|
||||
groupsList: {
|
||||
get() {
|
||||
return this.$store.state.groups
|
||||
return this.$store.state.groupsInOrder
|
||||
},
|
||||
async set(value) {
|
||||
let data = [];
|
||||
value.forEach((s, k) => {
|
||||
data.push({group: s.id, order: k+1})
|
||||
});
|
||||
alert(JSON.stringify(data))
|
||||
const ord = await Api.services_reorder(data)
|
||||
alert(JSON.parse(ord))
|
||||
await Api.groups_reorder(data)
|
||||
const groups = await Api.groups()
|
||||
this.$store.commit('setGroups', groups)
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Username</th>
|
||||
<th scope="col">Type</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -12,6 +13,8 @@
|
|||
|
||||
<tr v-for="(user, index) in $store.getters.users" v-bind:key="index" >
|
||||
<td>{{user.username}}</td>
|
||||
<td v-if="user.admin"><span class="badge badge-danger">ADMIN</span></td>
|
||||
<td v-if="!user.admin"><span class="badge badge-primary">USER</span></td>
|
||||
<td class="text-right">
|
||||
<div class="btn-group">
|
||||
<a @click.prevent="editUser(user, edit)" href="" class="btn btn-outline-secondary"><font-awesome-icon icon="user" /> Edit</a>
|
||||
|
|
|
@ -25,8 +25,8 @@
|
|||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button @click="saveGroup" type="submit" class="btn btn-block" :class="{'btn-primary': !group.name, 'btn-secondary': group.name}">
|
||||
{{group.id ? "Update Group" : "Create Group"}}
|
||||
<button @click="saveGroup" 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>
|
||||
|
@ -52,6 +52,7 @@
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
group: {
|
||||
name: "",
|
||||
public: true
|
||||
|
@ -70,13 +71,14 @@
|
|||
},
|
||||
async saveGroup(e) {
|
||||
e.preventDefault();
|
||||
this.loading = true
|
||||
if (this.in_group) {
|
||||
await this.updateGroup()
|
||||
} else {
|
||||
await this.createGroup()
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
|
||||
async createGroup() {
|
||||
const g = this.group
|
||||
const data = {name: g.name, public: g.public}
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
<template>
|
||||
<form @submit="updateIntegration">
|
||||
<h4 class="text-capitalize">{{integration.full_name}}</h4>
|
||||
<p class="small text-muted" v-html="integration.description"></p>
|
||||
|
||||
<div v-for="(field, index) in integration.fields" v-bind:key="index" class="form-group">
|
||||
|
||||
<label class="text-capitalize">{{field.name}}</label>
|
||||
|
||||
<textarea v-if="field.type === 'textarea'" v-model="field.value" rows="3" class="form-control"></textarea>
|
||||
|
||||
<input v-else :type="field.type" v-model="field.value" class="form-control">
|
||||
|
||||
<small class="form-text text-muted" v-html="field.description"></small>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<div class="col-3">
|
||||
<span @click="integration.enabled = !!integration.enabled" class="switch">
|
||||
<input type="checkbox" name="enabled-option" class="switch" v-model="integration.enabled" v-bind:id="`switch-${integration.name}`" v-bind:checked="integration.enabled">
|
||||
<label v-bind:for="`switch-${integration.name}`"></label>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div v-if="services.length !== 0" class="col-12">
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Domain</th>
|
||||
<th scope="col">Port</th>
|
||||
<th scope="col">Interval</th>
|
||||
<th scope="col">Timeout</th>
|
||||
<th scope="col">Type</th>
|
||||
<th scope="col"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(service, index) in services" v-bind:key="index">
|
||||
<td><input v-model="service.name" type="text" style="width: 80pt"></td>
|
||||
<td>{{service.domain}}</td>
|
||||
<td>{{service.port}}</td>
|
||||
<td><input v-model="service.check_interval" type="number" style="width: 35pt"></td>
|
||||
<td><input v-model="service.timeout" type="number" style="width: 35pt"></td>
|
||||
<td>{{service.type}}</td>
|
||||
<td><button @click.prevent="addService(service)" v-bind:disabled="service.added" class="btn btn-sm btn-outline-primary">Add</button></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{out}}
|
||||
|
||||
<div class="col-12">
|
||||
<button @click.prevent="updateIntegration" type="submit" class="btn btn-block btn-info">Fetch Services</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-danger d-none" role="alert"></div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API";
|
||||
|
||||
export default {
|
||||
name: 'FormIntegration',
|
||||
props: {
|
||||
integration: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
out: {},
|
||||
services: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
methods: {
|
||||
async addService(s) {
|
||||
const data = {name: s.name, type: s.type, domain: s.domain, port: s.port, check_interval: s.check_interval, timeout: s.timeout}
|
||||
const out = await Api.service_create(data)
|
||||
window.console.log(out)
|
||||
s.added = true
|
||||
},
|
||||
async updateIntegration() {
|
||||
const i = this.integration
|
||||
const data = {name: i.name, enabled: i.enabled, fields: i.fields}
|
||||
this.out = data
|
||||
const out = await Api.integration_save(data)
|
||||
if (out != null) {
|
||||
this.services = out
|
||||
}
|
||||
window.console.log(out)
|
||||
const integrations = await Api.integrations()
|
||||
this.$store.commit('setIntegrations', integrations)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -74,7 +74,9 @@
|
|||
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button @click="saveMessage" type="submit" class="btn btn-block" :class="{'btn-primary': !message.id, 'btn-secondary': message.id}">
|
||||
<button @click="saveMessage"
|
||||
:disabled="!message.title || !message.description"
|
||||
type="submit" class="btn btn-block" :class="{'btn-primary': !message.id, 'btn-secondary': message.id}">
|
||||
{{message.id ? "Edit Message" : "Create Message"}}
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
@ -1,7 +1,14 @@
|
|||
<template>
|
||||
<form @submit="saveNotifier">
|
||||
|
||||
<div v-if="error" class="alert alert-danger col-12" role="alert">{{error}}</div>
|
||||
|
||||
<div v-if="ok" class="alert alert-success col-12" role="alert">
|
||||
<i class="fa fa-smile-beam"></i> The {{notifier.method}} notifier is working correctly!
|
||||
</div>
|
||||
|
||||
<h4 class="text-capitalize">{{notifier.title}}</h4>
|
||||
<p class="small text-muted" v-html="notifier.description"></p>
|
||||
<p class="small text-muted" v-html="notifier.description"/>
|
||||
|
||||
<div v-for="(form, index) in notifier.form" v-bind:key="index" class="form-group">
|
||||
<label class="text-capitalize">{{form.title}}</label>
|
||||
|
@ -32,30 +39,20 @@
|
|||
|
||||
<div class="col-12 col-sm-4 mb-2 mb-sm-0 mt-2 mt-sm-0">
|
||||
<button @click="saveNotifier" type="submit" class="btn btn-block text-capitalize" :class="{'btn-primary': !saved, 'btn-success': saved}">
|
||||
<i class="fa fa-check-circle"></i> {{saved ? "Saved" : "Save"}}
|
||||
<i class="fa fa-check-circle"></i> {{loading ? "Loading..." : saved ? "Saved" : "Save"}}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-12">
|
||||
<button @click="testNotifier" class="btn btn-secondary btn-block text-capitalize col-12 float-right"><i class="fa fa-vial"></i> Test Notifier</button>
|
||||
<button @click="testNotifier" class="btn btn-secondary btn-block text-capitalize col-12 float-right"><i class="fa fa-vial"></i>
|
||||
{{loading ? "Loading..." : "Test Notifier"}}</button>
|
||||
</div>
|
||||
|
||||
<div class="col-12 col-sm-12 mt-2">
|
||||
<div class="alert alert-danger d-none" id="command-error" role="alert">
|
||||
<i class="fa fa-exclamation-triangle"></i> {{notifier.method}} has an error!
|
||||
</div>
|
||||
|
||||
<div class="alert alert-success d-none" id="command-success" role="alert">
|
||||
<i class="fa fa-smile-beam"></i> The {{notifier.method}} notifier is working correctly!
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span class="d-block small text-center mt-3 mb-5">
|
||||
<span class="text-capitalize">{{notifier.title}}</span> Notifier created by <a :href="notifier.author_url" target="_blank">{{notifier.author}}</a>
|
||||
</span>
|
||||
|
||||
<div v-if="error" class="alert alert-danger d-none" id="alerter" role="alert"></div>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
|
@ -72,8 +69,10 @@ export default {
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
error: null,
|
||||
saved: false,
|
||||
ok: false,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
@ -82,6 +81,7 @@ export default {
|
|||
methods: {
|
||||
async saveNotifier(e) {
|
||||
e.preventDefault();
|
||||
this.loading = true
|
||||
let form = {}
|
||||
this.notifier.form.forEach((f) => {
|
||||
form[f.field] = this.notifier[f.field]
|
||||
|
@ -93,12 +93,15 @@ export default {
|
|||
const notifiers = await Api.notifiers()
|
||||
this.$store.commit('setNotifiers', notifiers)
|
||||
this.saved = true
|
||||
this.loading = false
|
||||
setTimeout(() => {
|
||||
this.saved = false
|
||||
}, 2000)
|
||||
},
|
||||
async testNotifier(e) {
|
||||
e.preventDefault();
|
||||
this.ok = false
|
||||
this.loading = true
|
||||
let form = {}
|
||||
this.notifier.form.forEach((f) => {
|
||||
form[f.field] = this.notifier[f.field]
|
||||
|
@ -106,13 +109,13 @@ export default {
|
|||
form.enabled = this.notifier.enabled
|
||||
form.limits = parseInt(this.notifier.limits)
|
||||
form.method = this.notifier.method
|
||||
alert(JSON.stringify(form))
|
||||
const tested = await Api.notifier_test(form)
|
||||
if (tested === "ok") {
|
||||
alert('This notifier seems to be working!')
|
||||
if (tested === 'ok') {
|
||||
this.ok = true
|
||||
} else {
|
||||
this.error = tested
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,6 +168,7 @@
|
|||
name: 'FormService',
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
service: {
|
||||
name: "",
|
||||
type: "http",
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
<div class="form-group row">
|
||||
<label class="col-sm-4 col-form-label">Username</label>
|
||||
<div class="col-6 col-md-4">
|
||||
<input v-model="user.username" type="text" class="form-control" placeholder="Username" required autocorrect="off" autocapitalize="none">
|
||||
<input v-model="user.username" type="text" class="form-control" placeholder="Username" required autocorrect="off" autocapitalize="none" v-bind:readonly="user.id">
|
||||
</div>
|
||||
<div class="col-6 col-md-4">
|
||||
<span @click="user.admin = !!user.admin" class="switch">
|
||||
|
@ -40,8 +40,10 @@
|
|||
</div>
|
||||
<div class="form-group row">
|
||||
<div class="col-sm-12">
|
||||
<button @click="saveUser" class="btn btn-block" :class="{'btn-primary': !user.id, 'btn-secondary': user.id}">
|
||||
{{user.id ? "Update User" : "Create User"}}
|
||||
<button @click="saveUser"
|
||||
:disabled="loading || !user.username || !user.email || !user.password || !user.confirm_password || (user.password !== user.confirm_password)"
|
||||
class="btn btn-block" :class="{'btn-primary': !user.id, 'btn-secondary': user.id}">
|
||||
{{loading ? "Loading..." : user.id ? "Update User" : "Create User"}}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -67,6 +69,7 @@
|
|||
},
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
user: {
|
||||
username: "",
|
||||
admin: false,
|
||||
|
@ -91,11 +94,13 @@
|
|||
},
|
||||
async saveUser(e) {
|
||||
e.preventDefault();
|
||||
this.loading = true
|
||||
if (this.user.id) {
|
||||
await this.updateUser()
|
||||
} else {
|
||||
await this.createUser()
|
||||
}
|
||||
this.loading = false
|
||||
},
|
||||
async createUser() {
|
||||
let user = this.user
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<template>
|
||||
<div class="col-12 bg-white p-4">
|
||||
Help
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Help',
|
||||
components: {
|
||||
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
created() {
|
||||
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
methods: {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
</style>
|
|
@ -60,7 +60,7 @@
|
|||
this.error = true
|
||||
} else if (auth.token) {
|
||||
this.auth = Api.saveToken(this.username, auth.token)
|
||||
await this.$store.dispatch('loadRequired')
|
||||
await this.$store.dispatch('loadAdmin')
|
||||
this.$router.push('/dashboard')
|
||||
}
|
||||
this.loading = false
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
<template>
|
||||
<div class="col-12 bg-white p-4">
|
||||
<p v-if="logs.length === 0" class="text-monospace sm">
|
||||
Loading Logs...
|
||||
</p>
|
||||
<p v-for="(log, index) in logs.reverse()" class="text-monospace sm">{{log}}</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Api from "../components/API";
|
||||
|
||||
export default {
|
||||
name: 'Logs',
|
||||
components: {
|
||||
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
logs: [],
|
||||
last: "",
|
||||
t: null
|
||||
}
|
||||
},
|
||||
created() {
|
||||
if (!this.t) {
|
||||
this.t = setInterval(() => {
|
||||
this.lastLog()
|
||||
}, 650)
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
await this.getLogs()
|
||||
},
|
||||
beforeDestroy() {
|
||||
clearInterval(this.t)
|
||||
},
|
||||
methods: {
|
||||
cleanLog(l) {
|
||||
const splitLog = l.split(": ")
|
||||
const last = splitLog.slice(1);
|
||||
return last.join(": ")
|
||||
},
|
||||
async getLogs() {
|
||||
const logs = await Api.logs()
|
||||
this.logs = logs.reverse()
|
||||
this.last = this.cleanLog(this.logs[this.logs.length-1])
|
||||
},
|
||||
async lastLog() {
|
||||
const log = await Api.logs_last()
|
||||
const cleanLast = this.cleanLog(log)
|
||||
|
||||
if (this.last !== cleanLast) {
|
||||
this.last = cleanLast
|
||||
this.logs.reverse().push(log)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Add "scoped" attribute to limit CSS to this component only -->
|
||||
<style scoped>
|
||||
.sm {
|
||||
font-size: 8pt;
|
||||
}
|
||||
</style>
|
|
@ -5,23 +5,21 @@
|
|||
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
|
||||
<h6 class="text-muted">Main Settings</h6>
|
||||
|
||||
<a v-on:click="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-home-tab')}" id="v-pills-home-tab" data-toggle="pill" href="#v-pills-home" role="tab" aria-controls="v-pills-home" aria-selected="true"><i class="fa fa-cogs"></i> Settings</a>
|
||||
<a v-on:click="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-style-tab')}" id="v-pills-style-tab" data-toggle="pill" href="#v-pills-style" role="tab" aria-controls="v-pills-style" aria-selected="false"><i class="fa fa-image"></i> Theme Editor</a>
|
||||
<a v-on:click="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-cache-tab')}" id="v-pills-cache-tab" data-toggle="pill" href="#v-pills-cache" role="tab" aria-controls="v-pills-cache" aria-selected="false"><i class="fa fa-paperclip"></i> Cache</a>
|
||||
<a @click.prevent="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-home-tab')}" id="v-pills-home-tab" data-toggle="pill" href="#v-pills-home" role="tab" aria-controls="v-pills-home" aria-selected="true"><i class="fa fa-cogs"></i> Settings</a>
|
||||
<a @click.prevent="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-style-tab')}" id="v-pills-style-tab" data-toggle="pill" href="#v-pills-style" role="tab" aria-controls="v-pills-style" aria-selected="false"><i class="fa fa-image"></i> Theme Editor</a>
|
||||
<a @click.prevent="changeTab" class="nav-link" v-bind:class="{active: liClass('v-pills-cache-tab')}" id="v-pills-cache-tab" data-toggle="pill" href="#v-pills-cache" role="tab" aria-controls="v-pills-cache" aria-selected="false"><i class="fa fa-paperclip"></i> Cache</a>
|
||||
|
||||
<h6 class="mt-4 text-muted">Notifiers</h6>
|
||||
|
||||
<a v-for="(notifier, index) in $store.getters.notifiers" v-bind:key="index" v-on:click="changeTab" class="nav-link text-capitalize" v-bind:class="{active: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}" v-bind:id="`v-pills-${notifier.method.toLowerCase()}-tab`" data-toggle="pill" v-bind:href="`#v-pills-${notifier.method.toLowerCase()}`" role="tab" v-bind:aria-controls="`v-pills-${notifier.method.toLowerCase()}`" aria-selected="false">
|
||||
<a v-for="(notifier, index) in $store.getters.notifiers" v-bind:key="`${notifier.method}_${index}`" @click.prevent="changeTab" class="nav-link text-capitalize" v-bind:class="{active: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}" v-bind:id="`v-pills-${notifier.method.toLowerCase()}-tab`" data-toggle="pill" v-bind:href="`#v-pills-${notifier.method.toLowerCase()}`" role="tab" v-bind:aria-controls="`v-pills-${notifier.method.toLowerCase()}`" aria-selected="false">
|
||||
<i class="fas fa-terminal"></i> {{notifier.method}}
|
||||
</a>
|
||||
|
||||
<h6 class="mt-4 text-muted">Integrations (beta)</h6>
|
||||
|
||||
<a class="nav-link text-capitalize" id="v-pills-integration-csv-tab" data-toggle="pill" href="#v-pills-integration-csv" role="tab" aria-controls="v-pills-integration-csv" aria-selected="false"><i class="fas fa-file-csv"></i> CSV File</a>
|
||||
|
||||
<a class="nav-link text-capitalize" id="v-pills-integration-docker-tab" data-toggle="pill" href="#v-pills-integration-docker" role="tab" aria-controls="v-pills-integration-docker" aria-selected="false"><i class="fab fa-docker"></i> Docker</a>
|
||||
|
||||
<a class="nav-link text-capitalize" id="v-pills-integration-traefik-tab" data-toggle="pill" href="#v-pills-integration-traefik" role="tab" aria-controls="v-pills-integration-traefik" aria-selected="false"><i class="fas fa-network-wired"></i> Traefik</a>
|
||||
<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">
|
||||
<i class="fas fa-file-csv"></i> {{integration.full_name}}
|
||||
</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
@ -101,149 +99,25 @@
|
|||
</thead>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<td>/api/services/7/data?start=1577937580&end=9999999999&group=hour</td>
|
||||
<td>13951</td>
|
||||
<td>2020-01-15 20:00:10 -0800 -0800</td>
|
||||
<tr v-for="(cache, index) in cache">
|
||||
<td>{{cache.url}}</td>
|
||||
<td>{{cache.size}}</td>
|
||||
<td>{{cache.expiration}}</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
<a href="api/clear_cache" class="btn btn-danger btn-block">Clear Cache</a>
|
||||
<a @click.prevent="clearCache" href="#" class="btn btn-danger btn-block">Clear Cache</a>
|
||||
</div>
|
||||
|
||||
<div v-for="(notifier, index) in $store.getters.notifiers" v-bind:key="index" class="tab-pane fade" v-bind:class="{active: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`), show: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}" v-bind:id="`v-pills-${notifier.method.toLowerCase()}-tab`" role="tabpanel" v-bind:aria-labelledby="`v-pills-${notifier.method.toLowerCase()}-tab`">
|
||||
|
||||
<div v-for="(notifier, index) in $store.getters.notifiers" v-bind:key="`${notifier.title}_${index}`" class="tab-pane fade" v-bind:class="{active: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`), show: liClass(`v-pills-${notifier.method.toLowerCase()}-tab`)}" v-bind:id="`v-pills-${notifier.method.toLowerCase()}-tab`" role="tabpanel" v-bind:aria-labelledby="`v-pills-${notifier.method.toLowerCase()}-tab`">
|
||||
<Notifier :notifier="notifier"/>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
<div class="tab-pane fade" id="v-pills-integration-csv" role="tabpanel" aria-labelledby="v-pills-integration-csv-tab">
|
||||
|
||||
|
||||
<form class="integration_csv" action="settings/integrator/csv" method="POST">
|
||||
<input type="hidden" name="integrator" class="form-control" value="csv">
|
||||
<h4 class="text-capitalize">csv</h4>
|
||||
<p class="small text-muted">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>.</p>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize" for="input">input</label>
|
||||
|
||||
<textarea rows="3" class="form-control" name="input" id="input"></textarea>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<button type="submit" class="btn btn-block btn-info fetch_integrator">Fetch Services</button>
|
||||
|
||||
<div class="alert alert-danger d-none" role="alert"></div>
|
||||
</form>
|
||||
|
||||
<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 class="tab-pane fade" id="v-pills-integration-docker" role="tabpanel" aria-labelledby="v-pills-integration-docker-tab">
|
||||
|
||||
|
||||
<form class="integration_docker" action="settings/integrator/docker" method="POST">
|
||||
<input type="hidden" name="integrator" class="form-control" value="docker">
|
||||
<h4 class="text-capitalize">docker</h4>
|
||||
<p class="small text-muted">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.</p>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize" for="path">path</label>
|
||||
|
||||
<input type="text" name="path" class="form-control" value="unix:///var/run/docker.sock" id="path">
|
||||
|
||||
|
||||
<small class="form-text text-muted">The absolute path to the Docker unix socket</small>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize" for="version">version</label>
|
||||
|
||||
<input type="text" name="version" class="form-control" value="1.25" id="version">
|
||||
|
||||
|
||||
<small class="form-text text-muted">Version number of Docker server</small>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<button type="submit" class="btn btn-block btn-info fetch_integrator">Fetch Services</button>
|
||||
|
||||
<div class="alert alert-danger d-none" id="integration_alerter" role="alert"></div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-pane fade" id="v-pills-integration-traefik" role="tabpanel" aria-labelledby="v-pills-integration-traefik-tab">
|
||||
|
||||
|
||||
<form class="integration_traefik" action="settings/integrator/traefik" method="POST">
|
||||
<input type="hidden" name="integrator" class="form-control" value="traefik">
|
||||
<h4 class="text-capitalize">traefik</h4>
|
||||
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize" for="endpoint">endpoint</label>
|
||||
|
||||
<input type="text" name="endpoint" class="form-control" value="http://localhost:8080" id="endpoint">
|
||||
|
||||
|
||||
<small class="form-text text-muted">The URL for the traefik API Endpoint</small>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize" for="username">username</label>
|
||||
|
||||
<input type="text" name="username" class="form-control" value="" id="username">
|
||||
|
||||
|
||||
<small class="form-text text-muted">Username for HTTP Basic Authentication</small>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="text-capitalize" for="password">password</label>
|
||||
|
||||
<input type="password" name="password" class="form-control" value="" id="password">
|
||||
|
||||
|
||||
<small class="form-text text-muted">Password for HTTP Basic Authentication</small>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<button type="submit" class="btn btn-block btn-info fetch_integrator">Fetch Services</button>
|
||||
|
||||
<div class="alert alert-danger d-none" role="alert"></div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="v-pills-browse" role="tabpanel" aria-labelledby="v-pills-browse-tab">
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
<div class="tab-pane fade" id="v-pills-backups" role="tabpanel" aria-labelledby="v-pills-backups-tab">
|
||||
<a href="backups/create" class="btn btn-primary btn-block">Backup Database</a>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -254,11 +128,13 @@
|
|||
<script>
|
||||
import Api from '../components/API';
|
||||
import CoreSettings from '../forms/CoreSettings';
|
||||
import FormIntegration from '../forms/Integration';
|
||||
import Notifier from "../forms/Notifier";
|
||||
|
||||
export default {
|
||||
name: 'Settings',
|
||||
components: {
|
||||
FormIntegration,
|
||||
Notifier,
|
||||
CoreSettings
|
||||
},
|
||||
|
@ -266,16 +142,15 @@
|
|||
return {
|
||||
tab: "v-pills-home-tab",
|
||||
qrcode: "",
|
||||
core: this.$store.getters.core
|
||||
core: this.$store.getters.core,
|
||||
cache: [],
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
const core = await Api.core()
|
||||
this.$store.commit('setCore', core)
|
||||
const notifiers = await Api.notifiers()
|
||||
this.$store.commit('setNotifiers', notifiers)
|
||||
const qrurl = `statping://setup?domain=${core.domain}&api=${core.api_secret}`
|
||||
this.qrcode = "https://chart.googleapis.com/chart?chs=500x500&cht=qr&chl=" + encodeURI(qrurl)
|
||||
|
||||
this.cache = await Api.cache()
|
||||
},
|
||||
beforeMount() {
|
||||
|
||||
|
@ -286,7 +161,11 @@
|
|||
},
|
||||
liClass (id) {
|
||||
return this.tab === id
|
||||
}
|
||||
},
|
||||
async clearCache () {
|
||||
await Api.clearCache()
|
||||
this.cache = await Api.cache()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import Help from './pages/Help';
|
||||
import Index from "./pages/Index";
|
||||
import Dashboard from "./pages/Dashboard";
|
||||
import DashboardIndex from "./components/Dashboard/DashboardIndex";
|
||||
|
@ -5,12 +6,15 @@ import DashboardUsers from "./components/Dashboard/DashboardUsers";
|
|||
import DashboardServices from "./components/Dashboard/DashboardServices";
|
||||
import EditService from "./components/Dashboard/EditService";
|
||||
import DashboardMessages from "./components/Dashboard/DashboardMessages";
|
||||
import Logs from './pages/Logs';
|
||||
import Settings from "./pages/Settings";
|
||||
import Login from "./pages/Login";
|
||||
import Service from "./pages/Service";
|
||||
import VueRouter from "vue-router";
|
||||
import Setup from "./forms/Setup";
|
||||
|
||||
import Api from "./components/API";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/setup',
|
||||
|
@ -52,10 +56,10 @@ const routes = [
|
|||
component: Settings
|
||||
},{
|
||||
path: 'logs',
|
||||
component: DashboardUsers
|
||||
component: Logs
|
||||
},{
|
||||
path: 'help',
|
||||
component: DashboardUsers
|
||||
component: Help
|
||||
}]
|
||||
},
|
||||
{
|
||||
|
@ -79,15 +83,13 @@ const router = new VueRouter({
|
|||
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (to.matched.some(record => record.meta.requiresAuth)) {
|
||||
const tk = localStorage.getItem("statping_user")
|
||||
if (tk !== null) {
|
||||
next()
|
||||
return
|
||||
}
|
||||
if (to.path !== '/login') {
|
||||
const tk = Api.token()
|
||||
if (to.path !== '/login' && !tk.token) {
|
||||
next('/login')
|
||||
return
|
||||
}
|
||||
next()
|
||||
return
|
||||
} else {
|
||||
next()
|
||||
}
|
||||
|
|
|
@ -25,7 +25,8 @@ export default new Vuex.Store({
|
|||
groups: [],
|
||||
messages: [],
|
||||
users: [],
|
||||
notifiers: []
|
||||
notifiers: [],
|
||||
integrations: []
|
||||
},
|
||||
getters: {
|
||||
hasAllData: state => state.hasAllData,
|
||||
|
@ -37,9 +38,10 @@ export default new Vuex.Store({
|
|||
messages: state => state.messages,
|
||||
users: state => state.users,
|
||||
notifiers: state => state.notifiers,
|
||||
integrations: state => state.integrations,
|
||||
|
||||
servicesInOrder: state => state.services.sort((a, b) => a.order_id - b.order_id),
|
||||
groupsCleaned: state => state.groups.filter(g => g.name !== ''),
|
||||
groupsInOrder: state => state.groups.filter(g => g.name !== '').sort((a, b) => a.order_id - b.order_id),
|
||||
|
||||
serviceById: (state) => (id) => {
|
||||
return state.services.find(s => s.id === id)
|
||||
|
@ -93,6 +95,9 @@ export default new Vuex.Store({
|
|||
},
|
||||
setNotifiers(state, notifiers) {
|
||||
state.notifiers = notifiers
|
||||
},
|
||||
setIntegrations(state, integrations) {
|
||||
state.integrations = integrations
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
|
@ -113,6 +118,8 @@ export default new Vuex.Store({
|
|||
context.commit("setNotifiers", notifiers);
|
||||
const users = await Api.users()
|
||||
context.commit("setUsers", users);
|
||||
const integrations = await Api.integrations()
|
||||
context.commit("setIntegrations", integrations);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -86,9 +86,31 @@ func apiCoreHandler(w http.ResponseWriter, r *http.Request) {
|
|||
returnJson(core.CoreApp, w, r)
|
||||
}
|
||||
|
||||
type cacheJson struct {
|
||||
URL string `json:"url"`
|
||||
Expiration int64 `json:"expiration"`
|
||||
Size int `json:"size"`
|
||||
}
|
||||
|
||||
func apiCacheHandler(w http.ResponseWriter, r *http.Request) {
|
||||
cache := CacheStorage
|
||||
var cacheList []cacheJson
|
||||
for k, v := range cache.List() {
|
||||
cacheList = append(cacheList, cacheJson{
|
||||
URL: k,
|
||||
Expiration: v.Expiration,
|
||||
Size: len(v.Content),
|
||||
})
|
||||
}
|
||||
returnJson(cacheList, w, r)
|
||||
}
|
||||
|
||||
func apiClearCacheHandler(w http.ResponseWriter, r *http.Request) {
|
||||
CacheStorage = NewStorage()
|
||||
http.Redirect(w, r, basePath, http.StatusSeeOther)
|
||||
output := apiResponse{
|
||||
Status: "success",
|
||||
}
|
||||
returnJson(output, w, r)
|
||||
}
|
||||
|
||||
func sendErrorJson(err error, w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -76,7 +76,7 @@ func logsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
logs = append(logs, utils.LastLines[i].FormatForHtml()+"\r\n")
|
||||
}
|
||||
utils.LockLines.Unlock()
|
||||
ExecuteResponse(w, r, "logs.gohtml", logs, nil)
|
||||
returnJson(logs, w, r)
|
||||
}
|
||||
|
||||
func logsLineHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
|
|
@ -195,7 +195,6 @@ func IsAdmin(r *http.Request) bool {
|
|||
if err != nil {
|
||||
return false
|
||||
}
|
||||
fmt.Println("user: ", claim.Username, claim.Admin)
|
||||
return claim.Admin
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/hunterlong/statping/core"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"github.com/hunterlong/statping/utils"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
|
@ -9,3 +13,58 @@ func apiAllIncidentsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
incidents := core.AllIncidents()
|
||||
returnJson(incidents, w, r)
|
||||
}
|
||||
|
||||
func apiCreateIncidentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var incident *types.Incident
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&incident)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
newIncident := core.ReturnIncident(incident)
|
||||
_, err = newIncident.Create()
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
sendJsonAction(newIncident, "create", w, r)
|
||||
}
|
||||
|
||||
func apiIncidentUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
incident, err := core.SelectIncident(utils.ToInt(vars["id"]))
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err = decoder.Decode(&incident)
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
_, err = incident.Update()
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
sendJsonAction(incident, "update", w, r)
|
||||
}
|
||||
|
||||
func apiDeleteIncidentHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
incident, err := core.SelectIncident(utils.ToInt(vars["id"]))
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
err = incident.Delete()
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
sendJsonAction(incident, "delete", w, r)
|
||||
}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/hunterlong/statping/core/integrations"
|
||||
"github.com/hunterlong/statping/types"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
|
@ -11,20 +13,41 @@ func apiAllIntegrationsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
returnJson(integrations, w, r)
|
||||
}
|
||||
|
||||
func apiIntegrationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
func apiIntegrationViewHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
intg := vars["name"]
|
||||
r.ParseForm()
|
||||
for k, v := range r.PostForm {
|
||||
log.Info(k, v)
|
||||
}
|
||||
|
||||
integration, err := integrations.Find(intg)
|
||||
intgr, err := integrations.Find(vars["name"])
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
list, err := integration.List()
|
||||
returnJson(intgr.Get(), w, r)
|
||||
}
|
||||
|
||||
func apiIntegrationHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
intgr, err := integrations.Find(vars["name"])
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
var intJson *types.Integration
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
if err := decoder.Decode(&intJson); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
integration := intgr.Get()
|
||||
integration.Enabled = intJson.Enabled
|
||||
integration.Fields = intJson.Fields
|
||||
|
||||
if err := integrations.Update(integration); err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
}
|
||||
|
||||
list, err := intgr.List()
|
||||
if err != nil {
|
||||
sendErrorJson(err, w, r)
|
||||
return
|
||||
|
|
|
@ -65,7 +65,8 @@ func sendLog(next http.Handler) http.Handler {
|
|||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
t1 := utils.Now()
|
||||
t2 := utils.Now().Sub(t1)
|
||||
if r.RequestURI == "/logs/line" {
|
||||
if r.RequestURI == "/api/logs" || r.RequestURI == "/api/logs/last" {
|
||||
next.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
log.WithFields(utils.ToFields(w, r)).
|
||||
|
|
|
@ -76,11 +76,15 @@ func Router() *mux.Router {
|
|||
r.Handle("/api/setup", http.HandlerFunc(processSetupHandler)).Methods("POST")
|
||||
r.Handle("/api/logout", http.HandlerFunc(logoutHandler))
|
||||
r.Handle("/api/renew", authenticated(apiRenewHandler, false))
|
||||
r.Handle("/api/cache", authenticated(apiCacheHandler, false)).Methods("GET")
|
||||
r.Handle("/api/clear_cache", authenticated(apiClearCacheHandler, false))
|
||||
r.Handle("/api/core", authenticated(apiCoreHandler, false)).Methods("POST")
|
||||
r.Handle("/api/logs", authenticated(logsHandler, false)).Methods("GET")
|
||||
r.Handle("/api/logs/last", authenticated(logsLineHandler, false)).Methods("GET")
|
||||
|
||||
// API INTEGRATIONS Routes
|
||||
r.Handle("/api/integrations", authenticated(apiAllIntegrationsHandler, false)).Methods("GET")
|
||||
r.Handle("/api/integrations/{name}", authenticated(apiIntegrationHandler, false)).Methods("GET")
|
||||
r.Handle("/api/integrations/{name}", authenticated(apiIntegrationViewHandler, false)).Methods("GET")
|
||||
r.Handle("/api/integrations/{name}", authenticated(apiIntegrationHandler, false)).Methods("POST")
|
||||
|
||||
// API GROUPS Routes
|
||||
|
@ -109,6 +113,9 @@ func Router() *mux.Router {
|
|||
|
||||
// API INCIDENTS Routes
|
||||
r.Handle("/api/incidents", readOnly(apiAllIncidentsHandler, false)).Methods("GET")
|
||||
r.Handle("/api/incidents", authenticated(apiCreateIncidentHandler, false)).Methods("POST")
|
||||
r.Handle("/api/incidents/:id", authenticated(apiIncidentUpdateHandler, false)).Methods("POST")
|
||||
r.Handle("/api/incidents/:id", authenticated(apiDeleteIncidentHandler, false)).Methods("DELETE")
|
||||
|
||||
// API USER Routes
|
||||
r.Handle("/api/users", authenticated(apiAllUsersHandler, false)).Methods("GET")
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
package types
|
||||
|
||||
type Integration struct {
|
||||
ShortName string `json:"name"`
|
||||
Name string `json:"full_name"`
|
||||
Icon string `json:"-"`
|
||||
Description string `json:"description"`
|
||||
Enabled bool `json:"enabled"`
|
||||
Fields []*IntegrationField `json:"fields"`
|
||||
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 `json:"name"`
|
||||
Value interface{} `json:"value"`
|
||||
Type string `json:"type"`
|
||||
Description string `json:"description,omitempty"`
|
||||
MimeType string `json:"mime_type,omitempty"`
|
||||
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 {
|
||||
|
|
|
@ -246,6 +246,13 @@ func CopyFile(src, dst string) error {
|
|||
return out.Close()
|
||||
}
|
||||
|
||||
// IsType will return true if a variable can implement an interface
|
||||
func IsType(n interface{}, obj interface{}) bool {
|
||||
one := reflect.TypeOf(n)
|
||||
two := reflect.ValueOf(obj).Elem()
|
||||
return one.Implements(two.Type())
|
||||
}
|
||||
|
||||
// Command will run a terminal command with 'sh -c COMMAND' and return stdout and errOut as strings
|
||||
// in, out, err := Command("sass assets/scss assets/css/base.css")
|
||||
func Command(cmd string) (string, string, error) {
|
||||
|
|
Loading…
Reference in New Issue