notifier - moved core

pull/29/head
Hunter Long 2018-07-17 02:18:20 -07:00
parent 949ba3958a
commit d0ad55488d
25 changed files with 336 additions and 234 deletions

14
cli.go
View File

@ -4,7 +4,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/hunterlong/statup/core" "github.com/hunterlong/statup/core"
"github.com/hunterlong/statup/plugin"
"github.com/hunterlong/statup/types" "github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils" "github.com/hunterlong/statup/utils"
"github.com/joho/godotenv" "github.com/joho/godotenv"
@ -106,9 +105,8 @@ func RunOnce() {
if err != nil { if err != nil {
utils.Log(4, err) utils.Log(4, err)
} }
for _, ser := range core.CoreApp.Services { for _, s := range core.CoreApp.Services {
s := ser.ToService() out := core.ServiceCheck(s.ToService())
out := core.ServiceCheck(s)
fmt.Printf(" Service %v | URL: %v | Latency: %0.0fms | Online: %v\n", out.Name, out.Domain, (out.Latency * 1000), out.Online) fmt.Printf(" Service %v | URL: %v | Latency: %0.0fms | Online: %v\n", out.Name, out.Domain, (out.Latency * 1000), out.Online)
} }
} }
@ -129,7 +127,7 @@ func HelpEcho() {
fmt.Println("Give Statup a Star at https://github.com/hunterlong/statup") fmt.Println("Give Statup a Star at https://github.com/hunterlong/statup")
} }
func TestPlugin(plug plugin.PluginActions) { func TestPlugin(plug types.PluginActions) {
defer utils.DeleteFile("./.plugin_test.db") defer utils.DeleteFile("./.plugin_test.db")
RenderBoxes() RenderBoxes()
@ -160,7 +158,7 @@ func TestPlugin(plug plugin.PluginActions) {
fmt.Println("\n" + BRAKER) fmt.Println("\n" + BRAKER)
fmt.Println(POINT + "Sending 'OnSettingsSaved(Core)'") fmt.Println(POINT + "Sending 'OnSettingsSaved(Core)'")
fmt.Println(BRAKER) fmt.Println(BRAKER)
core.OnSettingsSaved(core.CoreApp) core.OnSettingsSaved(core.CoreApp.ToCore())
fmt.Println("\n" + BRAKER) fmt.Println("\n" + BRAKER)
fmt.Println(POINT + "Sending 'OnNewService(Service)'") fmt.Println(POINT + "Sending 'OnNewService(Service)'")
core.OnNewService(core.SelectService(2).ToService()) core.OnNewService(core.SelectService(2).ToService())
@ -180,11 +178,11 @@ func TestPlugin(plug plugin.PluginActions) {
fmt.Println("\n" + BRAKER) fmt.Println("\n" + BRAKER)
} }
func FakeSeed(plug plugin.PluginActions) { func FakeSeed(plug types.PluginActions) {
var err error var err error
core.CoreApp = core.NewCore() core.CoreApp = core.NewCore()
core.CoreApp.AllPlugins = []plugin.PluginActions{plug} core.CoreApp.AllPlugins = []types.PluginActions{plug}
fmt.Printf("\n" + BRAKER) fmt.Printf("\n" + BRAKER)

View File

@ -20,10 +20,9 @@ func CheckServices() {
utils.Log(1, fmt.Sprintf("Starting monitoring process for %v Services", len(CoreApp.Services))) utils.Log(1, fmt.Sprintf("Starting monitoring process for %v Services", len(CoreApp.Services)))
for _, ser := range CoreApp.Services { for _, ser := range CoreApp.Services {
s := ser.ToService() s := ser.ToService()
obj := s
//go obj.StartCheckins() //go obj.StartCheckins()
obj.StopRoutine = make(chan struct{}) s.StopRoutine = make(chan struct{})
go CheckQueue(obj) go CheckQueue(s)
} }
} }

View File

@ -16,8 +16,7 @@ func (c *Checkin) String() string {
func FindCheckin(api string) *types.Checkin { func FindCheckin(api string) *types.Checkin {
for _, ser := range CoreApp.Services { for _, ser := range CoreApp.Services {
s := ser.ToService() for _, c := range ser.ToService().Checkins {
for _, c := range s.Checkins {
if c.Api == api { if c.Api == api {
return c return c
} }

View File

@ -85,7 +85,7 @@ func LoadUsingEnv() (*types.Config, error) {
DropDatabase() DropDatabase()
CreateDatabase() CreateDatabase()
CoreApp = &Core{ CoreApp = &Core{Core: &types.Core{
Name: dbConfig.Project, Name: dbConfig.Project,
Description: dbConfig.Description, Description: dbConfig.Description,
Config: "config.yml", Config: "config.yml",
@ -93,11 +93,11 @@ func LoadUsingEnv() (*types.Config, error) {
ApiSecret: utils.NewSHA1Hash(16), ApiSecret: utils.NewSHA1Hash(16),
Domain: dbConfig.Domain, Domain: dbConfig.Domain,
MigrationId: time.Now().Unix(), MigrationId: time.Now().Unix(),
} }}
CoreApp.DbConnection = dbConfig.DbConn CoreApp.DbConnection = dbConfig.DbConn
err := CoreApp.Insert() err := InsertCore(CoreApp)
if err != nil { if err != nil {
utils.Log(3, err) utils.Log(3, err)
} }

View File

@ -3,7 +3,6 @@ package core
import ( import (
"github.com/GeertJohan/go.rice" "github.com/GeertJohan/go.rice"
"github.com/hunterlong/statup/notifiers" "github.com/hunterlong/statup/notifiers"
"github.com/hunterlong/statup/plugin"
"github.com/hunterlong/statup/types" "github.com/hunterlong/statup/types"
"github.com/pkg/errors" "github.com/pkg/errors"
"os" "os"
@ -14,24 +13,8 @@ type PluginJSON types.PluginJSON
type PluginRepos types.PluginRepos type PluginRepos types.PluginRepos
type Core struct { type Core struct {
Name string `db:"name" json:"name"` *types.Core
Description string `db:"description" json:"name"` Services []*Service
Config string `db:"config" json:"-"`
ApiKey string `db:"api_key" json:"-"`
ApiSecret string `db:"api_secret" json:"-"`
Style string `db:"style" json:"-"`
Footer string `db:"footer" json:"-"`
Domain string `db:"domain" json:"domain,omitempty"`
Version string `db:"version" json:"version,omitempty"`
MigrationId int64 `db:"migration_id" json:"-"`
UseCdn bool `db:"use_cdn" json:"-"`
Services []*Service `json:"services,omitempty"`
Plugins []plugin.Info
Repos []PluginJSON
AllPlugins []plugin.PluginActions
Communications []notifiers.AllNotifiers
DbConnection string
started time.Time
} }
var ( var (
@ -53,16 +36,21 @@ func init() {
func NewCore() *Core { func NewCore() *Core {
CoreApp = new(Core) CoreApp = new(Core)
CoreApp.started = time.Now() CoreApp.Core = new(types.Core)
CoreApp.Started = time.Now()
return CoreApp return CoreApp
} }
func (c *Core) Insert() error { func InsertCore(c *Core) error {
col := DbSession.Collection("core") col := DbSession.Collection("core")
_, err := col.Insert(c) _, err := col.Insert(c.Core)
return err return err
} }
func (c *Core) ToCore() *types.Core {
return c.Core
}
func InitApp() { func InitApp() {
SelectCore() SelectCore()
notifiers.Collections = DbSession.Collection("communication") notifiers.Collections = DbSession.Collection("communication")
@ -72,9 +60,9 @@ func InitApp() {
go DatabaseMaintence() go DatabaseMaintence()
} }
func (c *Core) Update() (*Core, error) { func UpdateCore(c *Core) (*Core, error) {
res := DbSession.Collection("core").Find().Limit(1) res := DbSession.Collection("core").Find().Limit(1)
err := res.Update(c) err := res.Update(c.Core)
return c, err return c, err
} }
@ -114,7 +102,7 @@ func (c Core) AllOnline() bool {
} }
func SelectLastMigration() (int64, error) { func SelectLastMigration() (int64, error) {
var c *Core var c *types.Core
if DbSession == nil { if DbSession == nil {
return 0, errors.New("Database connection has not been created yet") return 0, errors.New("Database connection has not been created yet")
} }
@ -126,17 +114,16 @@ func SelectLastMigration() (int64, error) {
} }
func SelectCore() (*Core, error) { func SelectCore() (*Core, error) {
var c *Core var c *types.Core
exists := DbSession.Collection("core").Exists() exists := DbSession.Collection("core").Exists()
if !exists { if !exists {
return nil, errors.New("core database has not been setup yet.") return nil, errors.New("core database has not been setup yet.")
} }
err := DbSession.Collection("core").Find().One(&c) err := DbSession.Collection("core").Find().One(&c)
if err != nil { if err != nil {
return nil, err return nil, err
} }
CoreApp = c CoreApp.Core = c
CoreApp.DbConnection = Configs.Connection CoreApp.DbConnection = Configs.Connection
CoreApp.Version = VERSION CoreApp.Version = VERSION
CoreApp.Services, _ = SelectAllServices() CoreApp.Services, _ = SelectAllServices()

View File

@ -119,7 +119,7 @@ func (c *DbConfig) Save() error {
DropDatabase() DropDatabase()
CreateDatabase() CreateDatabase()
newCore := &Core{ newCore := &types.Core{
Name: c.Project, Name: c.Project,
Description: c.Description, Description: c.Description,
Config: "config.yml", Config: "config.yml",
@ -131,7 +131,7 @@ func (c *DbConfig) Save() error {
col := DbSession.Collection("core") col := DbSession.Collection("core")
_, err = col.Insert(newCore) _, err = col.Insert(newCore)
if err == nil { if err == nil {
CoreApp = newCore CoreApp = &Core{Core: newCore}
} }
CoreApp, err = SelectCore() CoreApp, err = SelectCore()
@ -204,7 +204,7 @@ func RunDatabaseUpgrades() error {
panic(err) panic(err)
} }
CoreApp.MigrationId = currentMigration CoreApp.MigrationId = currentMigration
CoreApp.Update() UpdateCore(CoreApp)
} }
return err return err
} }

View File

@ -3,7 +3,6 @@ package core
import ( import (
"github.com/fatih/structs" "github.com/fatih/structs"
"github.com/hunterlong/statup/notifiers" "github.com/hunterlong/statup/notifiers"
"github.com/hunterlong/statup/plugin"
"github.com/hunterlong/statup/types" "github.com/hunterlong/statup/types"
"upper.io/db.v3/lib/sqlbuilder" "upper.io/db.v3/lib/sqlbuilder"
) )
@ -28,7 +27,7 @@ func OnFailure(s *types.Service, f FailureData) {
notifiers.OnFailure(s) notifiers.OnFailure(s)
} }
func OnSettingsSaved(c *Core) { func OnSettingsSaved(c *types.Core) {
for _, p := range CoreApp.AllPlugins { for _, p := range CoreApp.AllPlugins {
p.OnSettingsSaved(structs.Map(c)) p.OnSettingsSaved(structs.Map(c))
} }
@ -58,11 +57,11 @@ func OnUpdateService(s *types.Service) {
} }
} }
func SelectPlugin(name string) plugin.PluginActions { func SelectPlugin(name string) types.PluginActions {
for _, p := range CoreApp.AllPlugins { for _, p := range CoreApp.AllPlugins {
if p.GetInfo().Name == name { if p.GetInfo().Name == name {
return p return p
} }
} }
return plugin.PluginInfo{} return types.PluginInfo{}
} }

View File

@ -46,12 +46,16 @@ func DeleteFailures(u *types.Service) {
} }
} }
func (ser *Service) LimitedFailures() []*types.Failure { func (ser *Service) LimitedFailures() []*Failure {
s := ser.ToService() s := ser.ToService()
var fails []*types.Failure var fails []*types.Failure
var failArr []*Failure
col := DbSession.Collection("failures").Find("service", s.Id).OrderBy("-id").Limit(10) col := DbSession.Collection("failures").Find("service", s.Id).OrderBy("-id").Limit(10)
col.All(&fails) col.All(&fails)
return fails for _, f := range fails {
failArr = append(failArr, MakeFailure(f))
}
return failArr
} }
func reverseFailures(input []*types.Failure) []*types.Failure { func reverseFailures(input []*types.Failure) []*types.Failure {

View File

@ -11,7 +11,7 @@ import (
) )
type Service struct { type Service struct {
S interface{} s *types.Service
} }
type Failure struct { type Failure struct {
@ -23,10 +23,10 @@ func serviceCol() db.Collection {
} }
func SelectService(id int64) *Service { func SelectService(id int64) *Service {
for _, ser := range CoreApp.Services { for _, s := range CoreApp.Services {
s := ser.ToService() ser := s.ToService()
if s.Id == id { if ser.Id == id {
return ser return &Service{ser}
} }
} }
return nil return nil
@ -64,7 +64,7 @@ func (s *Service) AvgTime() float64 {
} }
func (ser *Service) Online24() float32 { func (ser *Service) Online24() float32 {
s := ser.S.(*types.Service) s := ser.ToService()
total, _ := ser.TotalHits() total, _ := ser.TotalHits()
failed, _ := ser.TotalFailures24Hours() failed, _ := ser.TotalFailures24Hours()
if failed == 0 { if failed == 0 {
@ -91,12 +91,11 @@ type DateScan struct {
} }
func (s *Service) ToService() *types.Service { func (s *Service) ToService() *types.Service {
return s.S.(*types.Service) return s.s
} }
func NewService(s *types.Service) *Service { func NewService(s *types.Service) *Service {
ser := &Service{s} return &Service{s}
return ser
} }
func (ser *Service) SmallText() string { func (ser *Service) SmallText() string {
@ -105,8 +104,8 @@ func (ser *Service) SmallText() string {
hits, _ := ser.LimitedHits() hits, _ := ser.LimitedHits()
if !s.Online { if !s.Online {
if len(last) > 0 { if len(last) > 0 {
lastFailure := MakeFailure(last[0]) lastFailure := MakeFailure(last[0].ToFailure())
return fmt.Sprintf("%v on %v", lastFailure.ParseError(), last[0].CreatedAt.Format("Monday 3:04PM, Jan _2 2006")) return fmt.Sprintf("%v on %v", lastFailure.ParseError(), last[0].ToFailure().CreatedAt.Format("Monday 3:04PM, Jan _2 2006"))
} else { } else {
return fmt.Sprintf("%v is currently offline", s.Name) return fmt.Sprintf("%v is currently offline", s.Name)
} }
@ -194,10 +193,10 @@ func (ser *Service) AvgUptime() string {
func RemoveArray(u *types.Service) []*Service { func RemoveArray(u *types.Service) []*Service {
var srvcs []*Service var srvcs []*Service
for _, ser := range CoreApp.Services { for _, s := range CoreApp.Services {
s := ser.ToService() ser := s.ToService()
if s.Id != u.Id { if ser.Id != u.Id {
srvcs = append(srvcs, ser) srvcs = append(srvcs, s)
} }
} }
CoreApp.Services = srvcs CoreApp.Services = srvcs
@ -241,16 +240,15 @@ func CreateService(u *types.Service) (int64, error) {
} }
u.Id = uuid.(int64) u.Id = uuid.(int64)
u.StopRoutine = make(chan struct{}) u.StopRoutine = make(chan struct{})
nn := &Service{u} CoreApp.Services = append(CoreApp.Services, &Service{u})
CoreApp.Services = append(CoreApp.Services, nn)
return uuid.(int64), err return uuid.(int64), err
} }
func CountOnline() int { func CountOnline() int {
amount := 0 amount := 0
for _, ser := range CoreApp.Services { for _, s := range CoreApp.Services {
s := ser.ToService() ser := s.ToService()
if s.Online { if ser.Online {
amount++ amount++
} }
} }

View File

@ -17,6 +17,21 @@ func ApiIndexHandler(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(core.CoreApp) json.NewEncoder(w).Encode(core.CoreApp)
} }
func ApiRenewHandler(w http.ResponseWriter, r *http.Request) {
if !isAPIAuthorized(r) {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
return
}
var err error
core.CoreApp.ApiKey = utils.NewSHA1Hash(40)
core.CoreApp.ApiSecret = utils.NewSHA1Hash(40)
core.CoreApp, err = core.UpdateCore(core.CoreApp)
if err != nil {
utils.Log(3, err)
}
http.Redirect(w, r, "/settings", http.StatusSeeOther)
}
func ApiCheckinHandler(w http.ResponseWriter, r *http.Request) { func ApiCheckinHandler(w http.ResponseWriter, r *http.Request) {
if !isAPIAuthorized(r) { if !isAPIAuthorized(r) {
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized) http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)

View File

@ -33,7 +33,7 @@ func Router() *mux.Router {
r.Handle("/user/{id}", http.HandlerFunc(UsersEditHandler)).Methods("GET") r.Handle("/user/{id}", http.HandlerFunc(UsersEditHandler)).Methods("GET")
r.Handle("/user/{id}", http.HandlerFunc(UpdateUserHandler)).Methods("POST") r.Handle("/user/{id}", http.HandlerFunc(UpdateUserHandler)).Methods("POST")
r.Handle("/user/{id}/delete", http.HandlerFunc(UsersDeleteHandler)).Methods("GET") r.Handle("/user/{id}/delete", http.HandlerFunc(UsersDeleteHandler)).Methods("GET")
r.Handle("/settings", http.HandlerFunc(PluginsHandler)).Methods("GET") r.Handle("/settings", http.HandlerFunc(SettingsHandler)).Methods("GET")
r.Handle("/settings", http.HandlerFunc(SaveSettingsHandler)).Methods("POST") r.Handle("/settings", http.HandlerFunc(SaveSettingsHandler)).Methods("POST")
r.Handle("/settings/css", http.HandlerFunc(SaveSASSHandler)).Methods("POST") r.Handle("/settings/css", http.HandlerFunc(SaveSASSHandler)).Methods("POST")
r.Handle("/settings/build", http.HandlerFunc(SaveAssetsHandler)).Methods("GET") r.Handle("/settings/build", http.HandlerFunc(SaveAssetsHandler)).Methods("GET")
@ -42,6 +42,7 @@ func Router() *mux.Router {
r.Handle("/plugins/{name}/save", http.HandlerFunc(PluginSavedHandler)).Methods("POST") r.Handle("/plugins/{name}/save", http.HandlerFunc(PluginSavedHandler)).Methods("POST")
r.Handle("/help", http.HandlerFunc(HelpHandler)) r.Handle("/help", http.HandlerFunc(HelpHandler))
r.Handle("/api", http.HandlerFunc(ApiIndexHandler)) r.Handle("/api", http.HandlerFunc(ApiIndexHandler))
r.Handle("/api/renew", http.HandlerFunc(ApiRenewHandler))
r.Handle("/api/checkin/{api}", http.HandlerFunc(ApiCheckinHandler)) r.Handle("/api/checkin/{api}", http.HandlerFunc(ApiCheckinHandler))
r.Handle("/api/services", http.HandlerFunc(ApiAllServicesHandler)) r.Handle("/api/services", http.HandlerFunc(ApiAllServicesHandler))
r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceHandler)).Methods("GET") r.Handle("/api/services/{id}", http.HandlerFunc(ApiServiceHandler)).Methods("GET")

View File

@ -78,6 +78,9 @@ func ServicesDeleteHandler(w http.ResponseWriter, r *http.Request) {
func ServicesViewHandler(w http.ResponseWriter, r *http.Request) { func ServicesViewHandler(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r) vars := mux.Vars(r)
serv := core.SelectService(utils.StringInt(vars["id"])) serv := core.SelectService(utils.StringInt(vars["id"]))
fmt.Println(serv.ToService())
ExecuteResponse(w, r, "service.html", serv) ExecuteResponse(w, r, "service.html", serv)
} }
@ -94,9 +97,7 @@ func ServicesBadgeHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "image/svg+xml") w.Header().Set("Content-Type", "image/svg+xml")
w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate") w.Header().Set("Cache-Control", "no-cache, no-store, must-revalidate")
w.Write(badge) w.Write(badge)
} }
func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) { func ServicesUpdateHandler(w http.ResponseWriter, r *http.Request) {

View File

@ -9,23 +9,11 @@ import (
"net/http" "net/http"
) )
func PluginsHandler(w http.ResponseWriter, r *http.Request) { func SettingsHandler(w http.ResponseWriter, r *http.Request) {
if !IsAuthenticated(r) { if !IsAuthenticated(r) {
http.Redirect(w, r, "/", http.StatusSeeOther) http.Redirect(w, r, "/", http.StatusSeeOther)
return return
} }
//CoreApp.FetchPluginRepo()
//var pluginFields []PluginSelect
//
//for _, p := range allPlugins {
// fields := structs.Map(p.GetInfo())
//
// pluginFields = append(pluginFields, PluginSelect{p.GetInfo().Name, p.GetForm(), fields})
//}
//CoreApp.PluginFields = pluginFields
ExecuteResponse(w, r, "settings.html", core.CoreApp) ExecuteResponse(w, r, "settings.html", core.CoreApp)
} }
@ -56,8 +44,8 @@ func SaveSettingsHandler(w http.ResponseWriter, r *http.Request) {
core.CoreApp.Domain = domain core.CoreApp.Domain = domain
} }
core.CoreApp.UseCdn = (r.PostForm.Get("enable_cdn") == "on") core.CoreApp.UseCdn = (r.PostForm.Get("enable_cdn") == "on")
core.CoreApp.Update() core.CoreApp, _ = core.UpdateCore(core.CoreApp)
core.OnSettingsSaved(core.CoreApp) core.OnSettingsSaved(core.CoreApp.ToCore())
http.Redirect(w, r, "/settings", http.StatusSeeOther) http.Redirect(w, r, "/settings", http.StatusSeeOther)
} }
@ -105,8 +93,8 @@ func SaveNotificationHandler(w http.ResponseWriter, r *http.Request) {
var2 := r.PostForm.Get("var2") var2 := r.PostForm.Get("var2")
apiKey := r.PostForm.Get("api_key") apiKey := r.PostForm.Get("api_key")
apiSecret := r.PostForm.Get("api_secret") apiSecret := r.PostForm.Get("api_secret")
limits := int64(utils.StringInt(r.PostForm.Get("limits"))) limits := int(utils.StringInt(r.PostForm.Get("limits")))
notifer := notifiers.Select(utils.StringInt(notifierId)) notifer := notifiers.SelectNotifier(utils.StringInt(notifierId)).Select()
if host != "" { if host != "" {
notifer.Host = host notifer.Host = host

View File

@ -6,7 +6,7 @@ import (
"github.com/fatih/structs" "github.com/fatih/structs"
"github.com/hunterlong/statup/core" "github.com/hunterlong/statup/core"
"github.com/hunterlong/statup/handlers" "github.com/hunterlong/statup/handlers"
"github.com/hunterlong/statup/plugin" "github.com/hunterlong/statup/types"
"github.com/hunterlong/statup/utils" "github.com/hunterlong/statup/utils"
"github.com/joho/godotenv" "github.com/joho/godotenv"
"io/ioutil" "io/ioutil"
@ -123,8 +123,8 @@ func LoadPlugins(debug bool) {
utils.Log(1, structs.Map(symPlugin)) utils.Log(1, structs.Map(symPlugin))
} }
var plugActions plugin.PluginActions var plugActions types.PluginActions
plugActions, ok := symPlugin.(plugin.PluginActions) plugActions, ok := symPlugin.(types.PluginActions)
if !ok { if !ok {
utils.Log(3, fmt.Sprintf("Plugin '%v' could not load correctly, error: %v", f.Name(), err)) utils.Log(3, fmt.Sprintf("Plugin '%v' could not load correctly, error: %v", f.Name(), err))
if debug { if debug {

View File

@ -20,7 +20,7 @@ const (
var ( var (
emailer *Email emailer *Email
emailArray []string emailArray []string
emailQueue []*types.Email emailQueue []*EmailOutgoing
emailBox *rice.Box emailBox *rice.Box
mailer *gomail.Dialer mailer *gomail.Dialer
) )
@ -37,41 +37,35 @@ func init() {
Id: EMAIL_ID, Id: EMAIL_ID,
Method: EMAIL_METHOD, Method: EMAIL_METHOD,
Form: []NotificationForm{{ Form: []NotificationForm{{
id: 1, Id: 1,
Type: "text", Type: "text",
Title: "SMTP Host", Title: "SMTP Host",
Placeholder: "Insert your SMTP Host here.", Placeholder: "Insert your SMTP Host here.",
DbField: "Host", DbField: "Host",
}, { }, {
id: 1, Id: 1,
Type: "text", Type: "text",
Title: "SMTP Username", Title: "SMTP Username",
Placeholder: "Insert your SMTP Username here.", Placeholder: "Insert your SMTP Username here.",
DbField: "Username", DbField: "Username",
}, { }, {
id: 1, Id: 1,
Type: "password", Type: "password",
Title: "SMTP Password", Title: "SMTP Password",
Placeholder: "Insert your SMTP Password here.", Placeholder: "Insert your SMTP Password here.",
DbField: "Password", DbField: "Password",
}, { }, {
id: 1, Id: 1,
Type: "number", Type: "number",
Title: "SMTP Port", Title: "SMTP Port",
Placeholder: "Insert your SMTP Port here.", Placeholder: "Insert your SMTP Port here.",
DbField: "Port", DbField: "Port",
}, { }, {
id: 1, Id: 1,
Type: "text", Type: "text",
Title: "Outgoing Email Address", Title: "Outgoing Email Address",
Placeholder: "Insert your Outgoing Email Address", Placeholder: "Insert your Outgoing Email Address",
DbField: "Var1", DbField: "Var1",
}, {
id: 1,
Type: "number",
Title: "Limits per Hour",
Placeholder: "How many emails can it send per hour",
DbField: "Limits",
}}, }},
}} }}
@ -108,7 +102,7 @@ func (u *Email) Init() error {
func (u *Email) Test() error { func (u *Email) Test() error {
if u.Enabled { if u.Enabled {
email := &types.Email{ email := &EmailOutgoing{
To: "info@socialeck.com", To: "info@socialeck.com",
Subject: "Test Email", Subject: "Test Email",
Template: "message.html", Template: "message.html",
@ -124,6 +118,16 @@ type emailMessage struct {
Service *types.Service Service *types.Service
} }
type EmailOutgoing struct {
To string
Subject string
Template string
From string
Data interface{}
Source string
Sent bool
}
// AFTER NOTIFIER LOADS, IF ENABLED, START A QUEUE PROCESS // AFTER NOTIFIER LOADS, IF ENABLED, START A QUEUE PROCESS
func (u *Email) Run() error { func (u *Email) Run() error {
var sentAddresses []string var sentAddresses []string
@ -132,16 +136,18 @@ func (u *Email) Run() error {
emailQueue = removeEmail(emailQueue, email) emailQueue = removeEmail(emailQueue, email)
continue continue
} }
e := email if u.CanSend() {
go func(email *types.Email) {
err := u.dialSend(email) err := u.dialSend(email)
if err == nil { if err == nil {
email.Sent = true email.Sent = true
sentAddresses = append(sentAddresses, email.To) sentAddresses = append(sentAddresses, email.To)
utils.Log(1, fmt.Sprintf("Email '%v' sent to: %v using the %v template (size: %v)", email.Subject, email.To, email.Template, len([]byte(email.Source)))) utils.Log(1, fmt.Sprintf("Email '%v' sent to: %v using the %v template (size: %v)", email.Subject, email.To, email.Template, len([]byte(email.Source))))
emailQueue = removeEmail(emailQueue, email) emailQueue = removeEmail(emailQueue, email)
u.Log(fmt.Sprintf("Subject: %v to %v", email.Subject, email.To))
} else {
utils.Log(3, fmt.Sprintf("Email Notifier could not send email: %v", err))
}
} }
}(e)
} }
time.Sleep(60 * time.Second) time.Sleep(60 * time.Second)
if u.Enabled { if u.Enabled {
@ -158,7 +164,7 @@ func (u *Email) OnFailure(s *types.Service) error {
Service: s, Service: s,
} }
email := &types.Email{ email := &EmailOutgoing{
To: "info@socialeck.com", To: "info@socialeck.com",
Subject: fmt.Sprintf("Service %v is Failing", s.Name), Subject: fmt.Sprintf("Service %v is Failing", s.Name),
Template: "failure.html", Template: "failure.html",
@ -190,10 +196,7 @@ func (u *Email) OnSave() error {
// ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS // ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS
func (u *Email) Install() error { func (u *Email) Install() error {
inDb, err := emailer.Notification.IsInDatabase()
fmt.Println("installing emailer")
inDb, err := emailer.Notification.isInDatabase()
if !inDb { if !inDb {
newNotifer, err := InsertDatabase(u.Notification) newNotifer, err := InsertDatabase(u.Notification)
if err != nil { if err != nil {
@ -205,7 +208,7 @@ func (u *Email) Install() error {
return err return err
} }
func (u *Email) dialSend(email *types.Email) error { func (u *Email) dialSend(email *EmailOutgoing) error {
fmt.Println("sending dailsend to emailer") fmt.Println("sending dailsend to emailer")
m := gomail.NewMessage() m := gomail.NewMessage()
m.SetHeader("From", email.From) m.SetHeader("From", email.From)
@ -219,7 +222,7 @@ func (u *Email) dialSend(email *types.Email) error {
return nil return nil
} }
func SendEmail(box *rice.Box, email *types.Email) { func SendEmail(box *rice.Box, email *EmailOutgoing) {
source := EmailTemplate(box, email.Template, email.Data) source := EmailTemplate(box, email.Template, email.Data)
email.Source = source email.Source = source
emailQueue = append(emailQueue, email) emailQueue = append(emailQueue, email)
@ -243,8 +246,8 @@ func EmailTemplate(box *rice.Box, tmpl string, data interface{}) string {
return result return result
} }
func removeEmail(emails []*types.Email, em *types.Email) []*types.Email { func removeEmail(emails []*EmailOutgoing, em *EmailOutgoing) []*EmailOutgoing {
var newArr []*types.Email var newArr []*EmailOutgoing
for _, e := range emails { for _, e := range emails {
if e != em { if e != em {
newArr = append(newArr, e) newArr = append(newArr, e)

View File

@ -10,28 +10,11 @@ import (
) )
var ( var (
AllCommunications []AllNotifiers AllCommunications []types.AllNotifiers
Collections db.Collection Collections db.Collection
Logs []*NotificationLog
) )
type AllNotifiers interface{}
func add(c interface{}) {
AllCommunications = append(AllCommunications, c)
}
func Load() []AllNotifiers {
utils.Log(1, "Loading notifiers")
var notifiers []AllNotifiers
for _, comm := range AllCommunications {
n := comm.(Notifier)
n.Init()
notifiers = append(notifiers, n)
n.Test()
}
return notifiers
}
type Notification struct { type Notification struct {
Id int64 `db:"id,omitempty" json:"id"` Id int64 `db:"id,omitempty" json:"id"`
Method string `db:"method" json:"method"` Method string `db:"method" json:"method"`
@ -44,7 +27,7 @@ type Notification struct {
ApiKey string `db:"api_key" json:"-"` ApiKey string `db:"api_key" json:"-"`
ApiSecret string `db:"api_secret" json:"-"` ApiSecret string `db:"api_secret" json:"-"`
Enabled bool `db:"enabled" json:"enabled"` Enabled bool `db:"enabled" json:"enabled"`
Limits int64 `db:"limits" json:"-"` Limits int `db:"limits" json:"-"`
Removable bool `db:"removable" json:"-"` Removable bool `db:"removable" json:"-"`
CreatedAt time.Time `db:"created_at" json:"created_at"` CreatedAt time.Time `db:"created_at" json:"created_at"`
Form []NotificationForm Form []NotificationForm
@ -62,14 +45,62 @@ type Notifier interface {
} }
type NotificationForm struct { type NotificationForm struct {
id int64 Id int64
Type string Type string
Title string Title string
Placeholder string Placeholder string
DbField string DbField string
} }
func (n *Notification) isInDatabase() (bool, error) { type NotificationLog struct {
Notifier *Notification
Message string
Time utils.Timestamp
}
func add(c interface{}) {
AllCommunications = append(AllCommunications, c)
}
func Load() []types.AllNotifiers {
utils.Log(1, "Loading notifiers")
var notifiers []types.AllNotifiers
for _, comm := range AllCommunications {
n := comm.(Notifier)
n.Init()
notifiers = append(notifiers, n)
n.Test()
}
return notifiers
}
func (n *Notification) Log(msg string) {
log := &NotificationLog{
Notifier: n,
Message: msg,
Time: utils.Timestamp(time.Now()),
}
Logs = append(Logs, log)
}
func (n *Notification) Logs() []*NotificationLog {
var logs []*NotificationLog
for _, v := range Logs {
if v.Notifier.Id == n.Id {
logs = append(logs, v)
}
}
return reverseLogs(logs)
}
func reverseLogs(input []*NotificationLog) []*NotificationLog {
if len(input) == 0 {
return input
}
return append(reverseLogs(input[1:]), input[0])
}
func (n *Notification) IsInDatabase() (bool, error) {
return Collections.Find("id", n.Id).Exists() return Collections.Find("id", n.Id).Exists()
} }
@ -87,6 +118,7 @@ func (n *Notification) Update() (*Notification, error) {
func InsertDatabase(n *Notification) (int64, error) { func InsertDatabase(n *Notification) (int64, error) {
n.CreatedAt = time.Now() n.CreatedAt = time.Now()
n.Limits = 3
newId, err := Collections.Insert(n) newId, err := Collections.Insert(n)
if err != nil { if err != nil {
return 0, err return 0, err
@ -94,18 +126,6 @@ func InsertDatabase(n *Notification) (int64, error) {
return newId.(int64), err return newId.(int64), err
} }
func Select(id int64) *Notification {
var notifier *Notification
for _, n := range AllCommunications {
notif := n.(Notifier)
notifier = notif.Select()
if notifier.Id == id {
return notifier
}
}
return notifier
}
func SelectNotifier(id int64) Notifier { func SelectNotifier(id int64) Notifier {
var notifier Notifier var notifier Notifier
for _, n := range AllCommunications { for _, n := range AllCommunications {
@ -118,9 +138,33 @@ func SelectNotifier(id int64) Notifier {
return notifier return notifier
} }
func (f Notification) CanSend() bool {
if f.SentLastHour() >= f.Limits {
return false
}
return true
}
func (f Notification) SentLastHour() int {
sent := 0
hourAgo := time.Now().Add(-1 * time.Hour)
for _, v := range f.Logs() {
lastTime := time.Time(v.Time)
if lastTime.After(hourAgo) {
sent++
}
}
return sent
}
func (f NotificationForm) Value() string { func (f NotificationForm) Value() string {
notifier := Select(f.id) noti := SelectNotifier(f.Id)
return notifier.GetValue(f.DbField) return noti.Select().GetValue(f.DbField)
}
func (f Notification) LimitValue() int64 {
notifier, _ := SelectNotification(f.Id)
return utils.StringInt(notifier.GetValue("limits"))
} }
func (n *Notification) GetValue(dbField string) string { func (n *Notification) GetValue(dbField string) string {
@ -144,6 +188,8 @@ func (n *Notification) GetValue(dbField string) string {
return n.ApiKey return n.ApiKey
case "api_secret": case "api_secret":
return n.ApiSecret return n.ApiSecret
case "limits":
return utils.IntString(int(n.Limits))
} }
return "" return ""
} }

View File

@ -41,7 +41,7 @@ func init() {
Method: SLACK_METHOD, Method: SLACK_METHOD,
Host: "https://webhooksurl.slack.com/***", Host: "https://webhooksurl.slack.com/***",
Form: []NotificationForm{{ Form: []NotificationForm{{
id: 2, Id: 2,
Type: "text", Type: "text",
Title: "Incoming Webhook Url", Title: "Incoming Webhook Url",
Placeholder: "Insert your Slack webhook URL here.", Placeholder: "Insert your Slack webhook URL here.",
@ -84,12 +84,16 @@ func (u *Slack) Run() error {
messageLock.Lock() messageLock.Lock()
slackMessages = uniqueStrings(slackMessages) slackMessages = uniqueStrings(slackMessages)
for _, msg := range slackMessages { for _, msg := range slackMessages {
if u.CanSend() {
utils.Log(1, fmt.Sprintf("Sending JSON to Slack Webhook")) utils.Log(1, fmt.Sprintf("Sending JSON to Slack Webhook"))
client := http.Client{Timeout: 15 * time.Second} client := http.Client{Timeout: 15 * time.Second}
_, err := client.Post(u.Host, "application/json", bytes.NewBuffer([]byte(msg))) _, err := client.Post(u.Host, "application/json", bytes.NewBuffer([]byte(msg)))
if err != nil { if err != nil {
utils.Log(3, fmt.Sprintf("Issue sending Slack notification: %v", err)) utils.Log(3, fmt.Sprintf("Issue sending Slack notification: %v", err))
} }
u.Log(msg)
}
} }
slackMessages = []string{} slackMessages = []string{}
messageLock.Unlock() messageLock.Unlock()
@ -146,7 +150,7 @@ func (u *Slack) OnSave() error {
// ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS // ON SERVICE FAILURE, DO YOUR OWN FUNCTIONS
func (u *Slack) Install() error { func (u *Slack) Install() error {
inDb, err := slacker.Notification.isInDatabase() inDb, err := slacker.Notification.IsInDatabase()
if !inDb { if !inDb {
newNotifer, err := InsertDatabase(u.Notification) newNotifer, err := InsertDatabase(u.Notification)
if err != nil { if err != nil {

View File

@ -1,7 +1,7 @@
package plugin package plugin
import ( import (
"net/http" "github.com/hunterlong/statup/types"
"upper.io/db.v3/lib/sqlbuilder" "upper.io/db.v3/lib/sqlbuilder"
) )
@ -21,48 +21,14 @@ var (
DB sqlbuilder.Database DB sqlbuilder.Database
) )
func SetDatabase(database sqlbuilder.Database) { type PluginInfo struct {
DB = database i *types.Info
} }
type PluginInfo struct { func SetDatabase(database sqlbuilder.Database) {
Info Info DB = database
PluginActions
} }
func (p *PluginInfo) Form() string { func (p *PluginInfo) Form() string {
return "okkokokkok" return "okkokokkok"
} }
type PluginActions interface {
GetInfo() Info
GetForm() string
OnLoad(sqlbuilder.Database)
SetInfo(map[string]interface{}) Info
Routes() []Routing
OnSave(map[string]interface{})
OnFailure(map[string]interface{})
OnSuccess(map[string]interface{})
OnSettingsSaved(map[string]interface{})
OnNewUser(map[string]interface{})
OnNewService(map[string]interface{})
OnUpdatedService(map[string]interface{})
OnDeletedService(map[string]interface{})
OnInstall(map[string]interface{})
OnUninstall(map[string]interface{})
OnBeforeRequest(map[string]interface{})
OnAfterRequest(map[string]interface{})
OnShutdown()
}
type Routing struct {
URL string
Method string
Handler func(http.ResponseWriter, *http.Request)
}
type Info struct {
Name string
Description string
Form string
}

View File

@ -21,6 +21,11 @@ $(".confirm-btn").on('click', function() {
}); });
$(".select-input").on("click", function () {
$(this).select();
});
var ranVar = false; var ranVar = false;
var ranTheme = false; var ranTheme = false;
$('a[data-toggle="pill"]').on('shown.bs.tab', function (e) { $('a[data-toggle="pill"]').on('shown.bs.tab', function (e) {

View File

@ -59,26 +59,23 @@
{{ end }} {{ end }}
</div> </div>
<h3>Latest Failures</h3>
{{ range .Services }} {{ range .Services }}
{{ $s := .ToService }} {{ $s := .ToService }}
{{ if .LimitedFailures }} {{ if .LimitedFailures }}
<div class="list-group mt-3"> <h4>{{$s.Name}} Failures</h4>
<div class="list-group mt-3 mb-4">
{{ range .LimitedFailures }} {{ range .LimitedFailures }}
{{ $f := .ToFailure }}
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start"> <a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
<div class="d-flex w-100 justify-content-between"> <div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">{{.ParseError}}</h5> <h5 class="mb-1">{{.ParseError}}</h5>
<small>Reported {{.Ago}}</small> <small>Reported {{.Ago}}</small>
</div> </div>
<p class="mb-1">{{.Issue}}</p> <p class="mb-1">{{$f.Issue}}</p>
</a> </a>
{{ end }} {{ end }}
</div> </div>
{{ end }} {{ end }}
{{ end }} {{ end }}

View File

@ -59,14 +59,15 @@
<canvas id="service" width="400" height="120"></canvas> <canvas id="service" width="400" height="120"></canvas>
{{ if .LimitedFailures }} {{ if .LimitedFailures }}
<div class="list-group mt-5"> <div class="list-group mt-3 mb-4">
{{ range .LimitedFailures }} {{ range .LimitedFailures }}
{{ $f := .ToFailure }}
<a href="#" class="list-group-item list-group-item-action flex-column align-items-start"> <a href="#" class="list-group-item list-group-item-action flex-column align-items-start">
<div class="d-flex w-100 justify-content-between"> <div class="d-flex w-100 justify-content-between">
<h5 class="mb-1">{{.ParseError}}</h5> <h5 class="mb-1">{{.ParseError}}</h5>
<small>Reported {{.Ago}}</small> <small>Reported {{.Ago}}</small>
</div> </div>
<p class="mb-1">{{.Issue}}</p> <p class="mb-1">{{$f.Issue}}</p>
</a> </a>
{{ end }} {{ end }}
</div> </div>
@ -147,7 +148,7 @@
<button type="submit" class="btn btn-success btn-block">Update Service</button> <button type="submit" class="btn btn-success btn-block">Update Service</button>
</div> </div>
<div class="col-6"> <div class="col-6">
<a href="/service/{{ $s.Id }}/delete_failures" class="btn btn-danger btn-block">Delete All Failures</a> <a href="/service/{{ $s.Id }}/delete_failures" class="btn btn-danger btn-block confirm-btn">Delete All Failures</a>
</div> </div>
</div> </div>
</form> </form>
@ -156,9 +157,7 @@
<div class="col-12 mt-4"> <div class="col-12 mt-4">
<h3>Last Response</h3> <h3>Last Response</h3>
<textarea rows="8" class="form-control" readonly>{{ $s.LastResponse }}</textarea> <textarea rows="8" class="form-control" readonly>{{ $s.LastResponse }}</textarea>
<div class="form-group row mt-2"> <div class="form-group row mt-2">
<label for="last_status_code" class="col-sm-3 col-form-label">Status Code</label> <label for="last_status_code" class="col-sm-3 col-form-label">Status Code</label>
<div class="col-sm-2"> <div class="col-sm-2">

View File

@ -40,7 +40,7 @@
<td class="text-right"> <td class="text-right">
<div class="btn-group"> <div class="btn-group">
<a href="/service/{{$s.Id}}" class="btn btn-primary">View</a> <a href="/service/{{$s.Id}}" class="btn btn-primary">View</a>
<a href="/service/{{$s.Id}}/delete" class="btn btn-danger">Delete</a> <a href="/service/{{$s.Id}}/delete" class="btn btn-danger confirm-btn">Delete</a>
</div> </div>
</td> </td>
</tr> </tr>
@ -91,6 +91,7 @@
<label for="service_response" class="col-sm-4 col-form-label">Expected Response (Regex)</label> <label for="service_response" class="col-sm-4 col-form-label">Expected Response (Regex)</label>
<div class="col-sm-8"> <div class="col-sm-8">
<textarea name="expected" class="form-control" id="service_response" rows="3"></textarea> <textarea name="expected" class="form-control" id="service_response" rows="3"></textarea>
<small id="emailHelp" class="form-text text-muted">You can insert <a target="_blank" href="https://regex101.com/r/I5bbj9/1">Regex</a> to validate the response</small>
</div> </div>
</div> </div>
<div class="form-group row"> <div class="form-group row">

View File

@ -20,7 +20,6 @@
<div class="container col-md-7 col-sm-12 mt-md-5 bg-light"> <div class="container col-md-7 col-sm-12 mt-md-5 bg-light">
{{template "nav"}} {{template "nav"}}
<div class="col-12"> <div class="col-12">
@ -79,14 +78,19 @@
<button type="submit" class="btn btn-primary btn-block">Save Settings</button> <button type="submit" class="btn btn-primary btn-block">Save Settings</button>
<div class="form-group mt-3"> <div class="form-group row mt-3">
<label for="api_key">API Key</label> <label for="api_key" class="col-sm-3 col-form-label">API Key</label>
<input type="text" class="form-control" value="{{ .ApiKey }}" id="api_key" readonly> <div class="col-sm-9">
<input type="text" class="form-control select-input" value="{{ .ApiKey }}" id="api_key" readonly>
</div>
</div> </div>
<div class="form-group"> <div class="form-group row">
<label for="api_secret">API Secret</label> <label for="api_secret" class="col-sm-3 col-form-label">API Secret</label>
<input type="text" class="form-control" value="{{ .ApiSecret }}" id="api_secret" readonly> <div class="col-sm-9">
<input type="text" class="form-control select-input" value="{{ .ApiSecret }}" id="api_secret" readonly>
<small id="emailHelp" class="form-text text-muted">You can <a href="/api/renew">Regenerate API Keys</a> if you need to.</small>
</div>
</div> </div>
</form> </form>
@ -122,7 +126,7 @@
{{ range .Communications }} {{ range .Communications }}
<div class="tab-pane fade" id="v-pills-{{underscore .Method}}" role="tabpanel" aria-labelledby="v-pills-{{underscore .Method }}-tab"> <div class="tab-pane" id="v-pills-{{underscore .Method}}" role="tabpanel" aria-labelledby="v-pills-{{underscore .Method }}-tab">
<form method="POST" action="/settings/notifier/{{ .Id }}"> <form method="POST" action="/settings/notifier/{{ .Id }}">
{{range .Form}} {{range .Form}}
<div class="form-group"> <div class="form-group">
@ -130,6 +134,12 @@
<input type="{{.Type}}" name="{{underscore .DbField}}" class="form-control" value="{{ .Value }}" id="{{underscore .Title}}" placeholder="{{.Placeholder}}"> <input type="{{.Type}}" name="{{underscore .DbField}}" class="form-control" value="{{ .Value }}" id="{{underscore .Title}}" placeholder="{{.Placeholder}}">
</div> </div>
{{end}} {{end}}
<div class="form-group">
<label class="text-capitalize" for="limits_per_hour_{{underscore .Method }}">Limits per Hour</label>
<input type="number" name="limits" class="form-control" value="{{.LimitValue}}" id="limits_per_hour_{{underscore .Method }}" min="1" max="60" placeholder="How many messages can send per hour">
</div>
<div class="form-group row"> <div class="form-group row">
<div class="col-sm-6"> <div class="col-sm-6">
<span class="switch"> <span class="switch">
@ -143,6 +153,18 @@
</div> </div>
</form> </form>
{{ if .Logs }}
Sent {{.SentLastHour}} out of {{.LimitValue}} in the last hour<br>
{{ range .Logs }}
<div class="card mt-1">
<div class="card-body">
{{.Message}}
<p class="card-text"><small class="text-muted">Sent {{.Time.Ago}}</small></p>
</div>
</div>
{{ end }}
{{ end }}
</div> </div>
{{ end }} {{ end }}

View File

@ -1,9 +1,72 @@
package types package types
import ( import (
"net/http"
"time" "time"
"upper.io/db.v3/lib/sqlbuilder"
) )
type PluginInfo struct {
Info Info
PluginActions
}
type Routing struct {
URL string
Method string
Handler func(http.ResponseWriter, *http.Request)
}
type Info struct {
Name string
Description string
Form string
}
type PluginActions interface {
GetInfo() Info
GetForm() string
OnLoad(sqlbuilder.Database)
SetInfo(map[string]interface{}) Info
Routes() []Routing
OnSave(map[string]interface{})
OnFailure(map[string]interface{})
OnSuccess(map[string]interface{})
OnSettingsSaved(map[string]interface{})
OnNewUser(map[string]interface{})
OnNewService(map[string]interface{})
OnUpdatedService(map[string]interface{})
OnDeletedService(map[string]interface{})
OnInstall(map[string]interface{})
OnUninstall(map[string]interface{})
OnBeforeRequest(map[string]interface{})
OnAfterRequest(map[string]interface{})
OnShutdown()
}
type AllNotifiers interface{}
type Core struct {
Name string `db:"name" json:"name"`
Description string `db:"description" json:"name"`
Config string `db:"config" json:"-"`
ApiKey string `db:"api_key" json:"-"`
ApiSecret string `db:"api_secret" json:"-"`
Style string `db:"style" json:"-"`
Footer string `db:"footer" json:"-"`
Domain string `db:"domain" json:"domain,omitempty"`
Version string `db:"version" json:"version,omitempty"`
MigrationId int64 `db:"migration_id" json:"-"`
UseCdn bool `db:"use_cdn" json:"-"`
Services []*Service `json:"services,omitempty"`
Plugins []Info
Repos []PluginJSON
AllPlugins []PluginActions
Communications []AllNotifiers
DbConnection string
Started time.Time
}
type Service struct { type Service struct {
Id int64 `db:"id,omitempty" json:"id"` Id int64 `db:"id,omitempty" json:"id"`
Name string `db:"name" json:"name"` Name string `db:"name" json:"name"`
@ -67,16 +130,6 @@ type Checkin struct {
Last time.Time `json:"last"` Last time.Time `json:"last"`
} }
type Email struct {
To string
Subject string
Template string
From string
Data interface{}
Source string
Sent bool
}
type Config struct { type Config struct {
Connection string `yaml:"connection"` Connection string `yaml:"connection"`
Host string `yaml:"host"` Host string `yaml:"host"`

View File

@ -1,10 +1,12 @@
package utils package utils
import ( import (
"github.com/ararog/timeago"
"os" "os"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"time"
) )
func StringInt(s string) int64 { func StringInt(s string) int64 {
@ -12,6 +14,21 @@ func StringInt(s string) int64 {
return int64(num) return int64(num)
} }
func IntString(s int) string {
return strconv.Itoa(s)
}
type Timestamp time.Time
type Timestamper interface {
Ago() string
}
func (t Timestamp) Ago() string {
got, _ := timeago.TimeAgoWithTime(time.Now(), time.Time(t))
return got
}
func UnderScoreString(str string) string { func UnderScoreString(str string) string {
// convert every letter to lower case // convert every letter to lower case