mirror of https://github.com/statping/statping
tests - Shell Command Notifier - Annoucements in sample/seed
parent
c95e3b85a5
commit
28880e2c5e
2
Makefile
2
Makefile
|
@ -1,4 +1,4 @@
|
|||
VERSION=0.79.3
|
||||
VERSION=0.79.4
|
||||
BINARY_NAME=statup
|
||||
GOPATH:=$(GOPATH)
|
||||
GOCMD=go
|
||||
|
|
|
@ -291,7 +291,7 @@ func RunSelectAllNotifiers(t *testing.T) {
|
|||
notifier.SetDB(core.DbSession)
|
||||
core.CoreApp.Notifications = notifier.Load()
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 7, len(core.CoreApp.Notifications))
|
||||
assert.Equal(t, 8, len(core.CoreApp.Notifications))
|
||||
}
|
||||
|
||||
func RunUserSelectAll(t *testing.T) {
|
||||
|
@ -305,7 +305,7 @@ func RunUserCreate(t *testing.T) {
|
|||
Username: "hunterlong",
|
||||
Password: "password123",
|
||||
Email: "info@gmail.com",
|
||||
Admin: utils.NullBool(true),
|
||||
Admin: types.NewNullBool(true),
|
||||
})
|
||||
id, err := user.Create()
|
||||
assert.Nil(t, err)
|
||||
|
@ -314,7 +314,7 @@ func RunUserCreate(t *testing.T) {
|
|||
Username: "superadmin",
|
||||
Password: "admin",
|
||||
Email: "info@adminer.com",
|
||||
Admin: utils.NullBool(true),
|
||||
Admin: types.NewNullBool(true),
|
||||
})
|
||||
id, err = user2.Create()
|
||||
assert.Nil(t, err)
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/go-yaml/yaml"
|
||||
|
@ -72,7 +71,7 @@ func LoadUsingEnv() (*DbConfig, error) {
|
|||
CoreApp.Name = os.Getenv("NAME")
|
||||
CoreApp.Domain = os.Getenv("DOMAIN")
|
||||
CoreApp.DbConnection = Configs.DbConn
|
||||
CoreApp.UseCdn = sql.NullBool{os.Getenv("USE_CDN") == "true", true}
|
||||
CoreApp.UseCdn = types.NewNullBool(os.Getenv("USE_CDN") == "true")
|
||||
|
||||
err := Configs.Connect(true, utils.Directory)
|
||||
if err != nil {
|
||||
|
@ -94,7 +93,7 @@ func LoadUsingEnv() (*DbConfig, error) {
|
|||
Username: "admin",
|
||||
Password: "admin",
|
||||
Email: "info@admin.com",
|
||||
Admin: sql.NullBool{true, true},
|
||||
Admin: types.NewNullBool(true),
|
||||
})
|
||||
_, err := admin.Create()
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"github.com/hunterlong/statup/core/notifier"
|
||||
"github.com/hunterlong/statup/source"
|
||||
|
@ -148,7 +147,7 @@ func SelectCore() (*Core, error) {
|
|||
}
|
||||
CoreApp.DbConnection = Configs.DbConn
|
||||
CoreApp.Version = VERSION
|
||||
CoreApp.UseCdn = sql.NullBool{os.Getenv("USE_CDN") == "true", true}
|
||||
CoreApp.UseCdn = types.NewNullBool(os.Getenv("USE_CDN") == "true")
|
||||
return CoreApp, db.Error
|
||||
}
|
||||
|
||||
|
|
|
@ -17,9 +17,9 @@ package core
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statup/source"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"html/template"
|
||||
)
|
||||
|
@ -33,7 +33,7 @@ func ExportIndexHTML() string {
|
|||
source.Assets()
|
||||
injectDatabase()
|
||||
CoreApp.SelectAllServices(false)
|
||||
CoreApp.UseCdn = sql.NullBool{true, true}
|
||||
CoreApp.UseCdn = types.NewNullBool(true)
|
||||
for _, srv := range CoreApp.Services {
|
||||
service := srv.(*Service)
|
||||
service.Check(true)
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package notifier
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -47,7 +46,7 @@ type Notification struct {
|
|||
Var2 string `gorm:"not null;column:var2" json:"var2,omitempty"`
|
||||
ApiKey string `gorm:"not null;column:api_key" json:"api_key,omitempty"`
|
||||
ApiSecret string `gorm:"not null;column:api_secret" json:"api_secret,omitempty"`
|
||||
Enabled sql.NullBool `gorm:"column:enabled;type:boolean;default:false" json:"enabled"`
|
||||
Enabled types.NullBool `gorm:"column:enabled;type:boolean;default:false" json:"enabled"`
|
||||
Limits int `gorm:"not null;column:limits" json:"limits"`
|
||||
Removable bool `gorm:"column:removable" json:"-"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
|
|
|
@ -138,7 +138,7 @@ func TestNotification_Update(t *testing.T) {
|
|||
func TestEnableNotification(t *testing.T) {
|
||||
notifier, err := SelectNotification(example)
|
||||
assert.Nil(t, err)
|
||||
notifier.Enabled = utils.NullBool(true)
|
||||
notifier.Enabled = types.NewNullBool(true)
|
||||
updated, err := Update(example, notifier)
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, updated.Enabled.Bool)
|
||||
|
@ -180,57 +180,57 @@ func TestOnSave(t *testing.T) {
|
|||
|
||||
func TestOnSuccess(t *testing.T) {
|
||||
OnSuccess(service)
|
||||
assert.Equal(t, 7, len(example.Queue))
|
||||
assert.Equal(t, 2, len(example.Queue))
|
||||
}
|
||||
|
||||
func TestOnFailure(t *testing.T) {
|
||||
OnFailure(service, failure)
|
||||
assert.Equal(t, 8, len(example.Queue))
|
||||
assert.Equal(t, 3, len(example.Queue))
|
||||
}
|
||||
|
||||
func TestOnNewService(t *testing.T) {
|
||||
OnNewService(service)
|
||||
assert.Equal(t, 9, len(example.Queue))
|
||||
assert.Equal(t, 4, len(example.Queue))
|
||||
}
|
||||
|
||||
func TestOnUpdatedService(t *testing.T) {
|
||||
OnUpdatedService(service)
|
||||
assert.Equal(t, 10, len(example.Queue))
|
||||
assert.Equal(t, 5, len(example.Queue))
|
||||
}
|
||||
|
||||
func TestOnDeletedService(t *testing.T) {
|
||||
OnDeletedService(service)
|
||||
assert.Equal(t, 11, len(example.Queue))
|
||||
assert.Equal(t, 6, len(example.Queue))
|
||||
}
|
||||
|
||||
func TestOnNewUser(t *testing.T) {
|
||||
OnNewUser(user)
|
||||
assert.Equal(t, 12, len(example.Queue))
|
||||
assert.Equal(t, 7, len(example.Queue))
|
||||
}
|
||||
|
||||
func TestOnUpdatedUser(t *testing.T) {
|
||||
OnUpdatedUser(user)
|
||||
assert.Equal(t, 13, len(example.Queue))
|
||||
assert.Equal(t, 8, len(example.Queue))
|
||||
}
|
||||
|
||||
func TestOnDeletedUser(t *testing.T) {
|
||||
OnDeletedUser(user)
|
||||
assert.Equal(t, 14, len(example.Queue))
|
||||
assert.Equal(t, 9, len(example.Queue))
|
||||
}
|
||||
|
||||
func TestOnUpdatedCore(t *testing.T) {
|
||||
OnUpdatedCore(core)
|
||||
assert.Equal(t, 15, len(example.Queue))
|
||||
assert.Equal(t, 10, len(example.Queue))
|
||||
}
|
||||
|
||||
func TestOnUpdatedNotifier(t *testing.T) {
|
||||
OnUpdatedNotifier(example.Select())
|
||||
assert.Equal(t, 16, len(example.Queue))
|
||||
assert.Equal(t, 11, len(example.Queue))
|
||||
}
|
||||
|
||||
func TestRunAllQueueAndStop(t *testing.T) {
|
||||
assert.True(t, example.IsRunning())
|
||||
assert.Equal(t, 16, len(example.Queue))
|
||||
assert.Equal(t, 11, len(example.Queue))
|
||||
go Queue(example)
|
||||
time.Sleep(13 * time.Second)
|
||||
assert.NotZero(t, len(example.Queue))
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package core
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
|
@ -61,11 +60,11 @@ func InsertSampleData() error {
|
|||
Name: "JSON API Tester",
|
||||
Domain: "https://jsonplaceholder.typicode.com/posts",
|
||||
ExpectedStatus: 201,
|
||||
Expected: utils.NullString(`(title)": "((\\"|[statup])*)"`),
|
||||
Expected: types.NewNullString(`(title)": "((\\"|[statup])*)"`),
|
||||
Interval: 30,
|
||||
Type: "http",
|
||||
Method: "POST",
|
||||
PostData: utils.NullString(`{ "title": "statup", "body": "bar", "userId": 19999 }`),
|
||||
PostData: types.NewNullString(`{ "title": "statup", "body": "bar", "userId": 19999 }`),
|
||||
Timeout: 30,
|
||||
Order: 4,
|
||||
})
|
||||
|
@ -158,7 +157,7 @@ func insertSampleCore() error {
|
|||
Domain: "http://localhost:8080",
|
||||
Version: "test",
|
||||
CreatedAt: time.Now(),
|
||||
UseCdn: sql.NullBool{false, true},
|
||||
UseCdn: types.NewNullBool(false),
|
||||
}
|
||||
query := coreDB().Create(core)
|
||||
return query.Error
|
||||
|
@ -170,14 +169,14 @@ func insertSampleUsers() {
|
|||
Username: "testadmin",
|
||||
Password: "password123",
|
||||
Email: "info@betatude.com",
|
||||
Admin: sql.NullBool{true, true},
|
||||
Admin: types.NewNullBool(true),
|
||||
})
|
||||
|
||||
u3 := ReturnUser(&types.User{
|
||||
Username: "testadmin2",
|
||||
Password: "password123",
|
||||
Email: "info@adminhere.com",
|
||||
Admin: sql.NullBool{true, true},
|
||||
Admin: types.NewNullBool(true),
|
||||
})
|
||||
|
||||
u2.Create()
|
||||
|
|
|
@ -82,6 +82,7 @@ func SelectAllUsers() ([]*user, error) {
|
|||
db := usersDB().Find(&users)
|
||||
if db.Error != nil {
|
||||
utils.Log(3, fmt.Sprintf("Failed to load all users. %v", db.Error))
|
||||
return nil, db.Error
|
||||
}
|
||||
return users, db.Error
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ package core
|
|||
|
||||
import (
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
)
|
||||
|
@ -27,7 +26,7 @@ func TestCreateUser(t *testing.T) {
|
|||
Username: "hunter",
|
||||
Password: "password123",
|
||||
Email: "test@email.com",
|
||||
Admin: utils.NullBool(true),
|
||||
Admin: types.NewNullBool(true),
|
||||
})
|
||||
userId, err := user.Create()
|
||||
assert.Nil(t, err)
|
||||
|
@ -71,7 +70,7 @@ func TestCreateUser2(t *testing.T) {
|
|||
Username: "hunterlong",
|
||||
Password: "password123",
|
||||
Email: "user@email.com",
|
||||
Admin: utils.NullBool(true),
|
||||
Admin: types.NewNullBool(true),
|
||||
})
|
||||
userId, err := user.Create()
|
||||
assert.Nil(t, err)
|
||||
|
|
107
handlers/api.go
107
handlers/api.go
|
@ -16,7 +16,6 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
|
@ -297,7 +296,10 @@ func apiAllUsersHandler(w http.ResponseWriter, r *http.Request) {
|
|||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
users, _ := core.SelectAllUsers()
|
||||
users, err := core.SelectAllUsers()
|
||||
if err != nil {
|
||||
utils.Log(3, err)
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(users)
|
||||
}
|
||||
|
@ -311,13 +313,13 @@ func apiCreateUsersHandler(w http.ResponseWriter, r *http.Request) {
|
|||
decoder := json.NewDecoder(r.Body)
|
||||
err := decoder.Decode(&user)
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
newUser := core.ReturnUser(user)
|
||||
uId, err := newUser.Create()
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
output := apiResponse{
|
||||
|
@ -368,7 +370,7 @@ func apiNotifierUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
notifer.Port = notification.Port
|
||||
notifer.Password = notification.Password
|
||||
notifer.Username = notification.Username
|
||||
notifer.Enabled = sql.NullBool{notification.Enabled.Bool, true}
|
||||
notifer.Enabled = notification.Enabled
|
||||
notifer.ApiKey = notification.ApiKey
|
||||
notifer.ApiSecret = notification.ApiSecret
|
||||
|
||||
|
@ -382,6 +384,101 @@ func apiNotifierUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
json.NewEncoder(w).Encode(notifer)
|
||||
}
|
||||
|
||||
func apiAllMessagesHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !isAPIAuthorized(r) {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
messages, err := core.SelectMessages()
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("error fetching all messages: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(messages)
|
||||
}
|
||||
|
||||
func apiMessageGetHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !isAPIAuthorized(r) {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
message, err := core.SelectMessage(utils.StringInt(vars["id"]))
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("message #%v was not found", vars["id"]), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(message)
|
||||
}
|
||||
|
||||
func apiMessageDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !isAPIAuthorized(r) {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
message, err := core.SelectMessage(utils.StringInt(vars["id"]))
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("message #%v was not found", vars["id"]), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
err = message.Delete()
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("message #%v could not be deleted %v", vars["id"], err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
output := apiResponse{
|
||||
Object: "message",
|
||||
Method: "delete",
|
||||
Id: message.Id,
|
||||
Status: "success",
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(output)
|
||||
}
|
||||
|
||||
func apiMessageUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !isAPIAuthorized(r) {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
vars := mux.Vars(r)
|
||||
message, err := core.SelectMessage(utils.StringInt(vars["id"]))
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("message #%v was not found", vars["id"]), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
var messageBody *types.Message
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
err = decoder.Decode(&messageBody)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
messageBody.Id = message.Id
|
||||
message = core.ReturnMessage(messageBody)
|
||||
_, err = message.Update()
|
||||
if err != nil {
|
||||
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
output := apiResponse{
|
||||
Object: "message",
|
||||
Method: "update",
|
||||
Id: message.Id,
|
||||
Status: "success",
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(output)
|
||||
}
|
||||
|
||||
func isAPIAuthorized(r *http.Request) bool {
|
||||
if os.Getenv("GO_ENV") == "test" {
|
||||
return true
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"github.com/hunterlong/statup/core"
|
||||
"github.com/hunterlong/statup/types"
|
||||
|
@ -109,7 +108,7 @@ func DesktopInit(ip string, port int) {
|
|||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
Email: config.Email,
|
||||
Admin: sql.NullBool{true, true},
|
||||
Admin: types.NewNullBool(true),
|
||||
})
|
||||
admin.Create()
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ func updateMessageHandler(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
message.Title = title
|
||||
message.Description = description
|
||||
message.NotifyUsers = utils.NullBool(notifyUsers == "on")
|
||||
message.NotifyUsers = types.NewNullBool(notifyUsers == "on")
|
||||
message.NotifyMethod = notifyMethod
|
||||
message.StartOn = start.UTC()
|
||||
message.EndOn = end.UTC()
|
||||
|
@ -134,7 +134,7 @@ func createMessageHandler(w http.ResponseWriter, r *http.Request) {
|
|||
StartOn: start.UTC(),
|
||||
EndOn: end.UTC(),
|
||||
ServiceId: serviceId,
|
||||
NotifyUsers: utils.NullBool(notifyUsers == "on"),
|
||||
NotifyUsers: types.NewNullBool(notifyUsers == "on"),
|
||||
NotifyMethod: notifyMethod,
|
||||
NotifyBefore: before,
|
||||
})
|
||||
|
|
|
@ -176,7 +176,7 @@ func TestApiAllUsersHandler(t *testing.T) {
|
|||
assert.Equal(t, 200, rr.Code)
|
||||
var obj []types.User
|
||||
formatJSON(body, &obj)
|
||||
assert.Equal(t, true, obj[0].Admin)
|
||||
assert.Equal(t, true, obj[0].Admin.Bool)
|
||||
assert.Equal(t, "admin", obj[0].Username)
|
||||
}
|
||||
|
||||
|
@ -191,6 +191,7 @@ func TestApiCreateUserHandler(t *testing.T) {
|
|||
body := rr.Body.String()
|
||||
var obj apiResponse
|
||||
formatJSON(body, &obj)
|
||||
t.Log(body)
|
||||
assert.Equal(t, 200, rr.Code)
|
||||
assert.Contains(t, "create", obj.Method)
|
||||
assert.Contains(t, "success", obj.Status)
|
||||
|
@ -203,8 +204,9 @@ func TestApiViewUserHandler(t *testing.T) {
|
|||
assert.Equal(t, 200, rr.Code)
|
||||
var obj types.User
|
||||
formatJSON(body, &obj)
|
||||
t.Log(body)
|
||||
assert.Equal(t, "admin", obj.Username)
|
||||
assert.Equal(t, true, obj.Admin)
|
||||
assert.Equal(t, true, obj.Admin.Bool)
|
||||
}
|
||||
|
||||
func TestApiUpdateUserHandler(t *testing.T) {
|
||||
|
@ -220,7 +222,7 @@ func TestApiUpdateUserHandler(t *testing.T) {
|
|||
formatJSON(body, &obj)
|
||||
assert.Equal(t, 200, rr.Code)
|
||||
assert.Equal(t, "adminupdated", obj.Username)
|
||||
assert.Equal(t, true, obj.Admin)
|
||||
assert.Equal(t, true, obj.Admin.Bool)
|
||||
}
|
||||
|
||||
func TestApiDeleteUserHandler(t *testing.T) {
|
||||
|
|
|
@ -121,10 +121,18 @@ func Router() *mux.Router {
|
|||
r.Handle("/api/users/{id}", http.HandlerFunc(apiUserUpdateHandler)).Methods("POST")
|
||||
r.Handle("/api/users/{id}", http.HandlerFunc(apiUserDeleteHandler)).Methods("DELETE")
|
||||
|
||||
// API Notifier Routes
|
||||
// API NOTIFIER Routes
|
||||
r.Handle("/api/notifier/{notifier}", http.HandlerFunc(apiNotifierGetHandler)).Methods("GET")
|
||||
r.Handle("/api/notifier/{notifier}", http.HandlerFunc(apiNotifierUpdateHandler)).Methods("POST")
|
||||
|
||||
// API MESSAGES Routes
|
||||
r.Handle("/api/messages", http.HandlerFunc(apiAllMessagesHandler)).Methods("GET")
|
||||
r.Handle("/api/messages", http.HandlerFunc(apiNotifierUpdateHandler)).Methods("POST")
|
||||
r.Handle("/api/messages/{id}", http.HandlerFunc(apiMessageGetHandler)).Methods("GET")
|
||||
r.Handle("/api/messages/{id}", http.HandlerFunc(apiMessageUpdateHandler)).Methods("POST")
|
||||
r.Handle("/api/messages/{id}", http.HandlerFunc(apiMessageDeleteHandler)).Methods("DELETE")
|
||||
|
||||
// API Generic Routes
|
||||
r.Handle("/metrics", http.HandlerFunc(prometheusHandler))
|
||||
r.Handle("/health", http.HandlerFunc(healthCheckHandler))
|
||||
r.Handle("/tray", http.HandlerFunc(trayHandler))
|
||||
|
|
|
@ -104,12 +104,12 @@ func createServiceHandler(w http.ResponseWriter, r *http.Request) {
|
|||
Name: name,
|
||||
Domain: domain,
|
||||
Method: method,
|
||||
Expected: utils.NullString(expected),
|
||||
Expected: types.NewNullString(expected),
|
||||
ExpectedStatus: status,
|
||||
Interval: interval,
|
||||
Type: checkType,
|
||||
Port: port,
|
||||
PostData: utils.NullString(postData),
|
||||
PostData: types.NewNullString(postData),
|
||||
Timeout: timeout,
|
||||
Order: order,
|
||||
})
|
||||
|
@ -199,11 +199,11 @@ func servicesUpdateHandler(w http.ResponseWriter, r *http.Request) {
|
|||
service.Domain = domain
|
||||
service.Method = method
|
||||
service.ExpectedStatus = status
|
||||
service.Expected = utils.NullString(expected)
|
||||
service.Expected = types.NewNullString(expected)
|
||||
service.Interval = interval
|
||||
service.Type = checkType
|
||||
service.Port = port
|
||||
service.PostData = utils.NullString(postData)
|
||||
service.PostData = types.NewNullString(postData)
|
||||
service.Timeout = timeout
|
||||
service.Order = order
|
||||
|
||||
|
|
|
@ -16,12 +16,12 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/hunterlong/statup/core"
|
||||
"github.com/hunterlong/statup/core/notifier"
|
||||
"github.com/hunterlong/statup/source"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"net/http"
|
||||
"net/url"
|
||||
|
@ -57,7 +57,7 @@ func saveSettingsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
footer := r.PostForm.Get("footer")
|
||||
if footer != app.Footer.String {
|
||||
app.Footer = utils.NullString(footer)
|
||||
app.Footer = types.NewNullString(footer)
|
||||
}
|
||||
domain := r.PostForm.Get("domain")
|
||||
if domain != app.Domain {
|
||||
|
@ -67,7 +67,7 @@ func saveSettingsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
timeFloat, _ := strconv.ParseFloat(timezone, 10)
|
||||
app.Timezone = float32(timeFloat)
|
||||
|
||||
app.UseCdn = utils.NullBool(r.PostForm.Get("enable_cdn") == "on")
|
||||
app.UseCdn = types.NewNullBool(r.PostForm.Get("enable_cdn") == "on")
|
||||
core.CoreApp, _ = core.UpdateCore(app)
|
||||
//notifiers.OnSettingsSaved(core.CoreApp.ToCore())
|
||||
executeResponse(w, r, "settings.html", core.CoreApp, "/settings")
|
||||
|
@ -189,7 +189,7 @@ func saveNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
|||
if limits != 0 {
|
||||
notifer.Limits = limits
|
||||
}
|
||||
notifer.Enabled = sql.NullBool{enabled == "on", true}
|
||||
notifer.Enabled = types.NewNullBool(enabled == "on")
|
||||
|
||||
_, err = notifier.Update(notif, notifer)
|
||||
if err != nil {
|
||||
|
@ -255,7 +255,7 @@ func testNotificationHandler(w http.ResponseWriter, r *http.Request) {
|
|||
if limits != 0 {
|
||||
notifer.Limits = limits
|
||||
}
|
||||
notifer.Enabled = sql.NullBool{enabled == "on", true}
|
||||
notifer.Enabled = types.NewNullBool(enabled == "on")
|
||||
|
||||
err = notif.(notifier.Tester).OnTest()
|
||||
if err == nil {
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"github.com/hunterlong/statup/core"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
|
@ -114,7 +113,7 @@ func processSetupHandler(w http.ResponseWriter, r *http.Request) {
|
|||
Username: config.Username,
|
||||
Password: config.Password,
|
||||
Email: config.Email,
|
||||
Admin: sql.NullBool{true, true},
|
||||
Admin: types.NewNullBool(true),
|
||||
})
|
||||
admin.Create()
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package handlers
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/hunterlong/statup/core"
|
||||
|
@ -64,7 +63,7 @@ func updateUserHandler(w http.ResponseWriter, r *http.Request) {
|
|||
user.Username = r.PostForm.Get("username")
|
||||
user.Email = r.PostForm.Get("email")
|
||||
isAdmin := r.PostForm.Get("admin") == "on"
|
||||
user.Admin = sql.NullBool{isAdmin, true}
|
||||
user.Admin = types.NewNullBool(isAdmin)
|
||||
password := r.PostForm.Get("password")
|
||||
if password != "##########" {
|
||||
user.Password = utils.HashPassword(password)
|
||||
|
@ -97,7 +96,7 @@ func createUserHandler(w http.ResponseWriter, r *http.Request) {
|
|||
Username: username,
|
||||
Password: password,
|
||||
Email: email,
|
||||
Admin: sql.NullBool{admin == "on", true},
|
||||
Admin: types.NewNullBool(admin == "on"),
|
||||
})
|
||||
_, err := user.Create()
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,164 @@
|
|||
// Statup
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statup
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/hunterlong/statup/core/notifier"
|
||||
"github.com/hunterlong/statup/types"
|
||||
"github.com/hunterlong/statup/utils"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
)
|
||||
|
||||
type commandLine struct {
|
||||
*notifier.Notification
|
||||
}
|
||||
|
||||
var command = &commandLine{¬ifier.Notification{
|
||||
Method: "command",
|
||||
Title: "Shell Command",
|
||||
Description: "Shell Command allows you to run customize shell or bash commands on the local machine it's running on.",
|
||||
Author: "Hunter Long",
|
||||
AuthorUrl: "https://github.com/hunterlong",
|
||||
Delay: time.Duration(1 * time.Second),
|
||||
Icon: "fas fa-command-alt",
|
||||
Host: "sh",
|
||||
Form: []notifier.NotificationForm{{
|
||||
Type: "text",
|
||||
Title: "Shell or Bash",
|
||||
Placeholder: "sh",
|
||||
DbField: "host",
|
||||
SmallText: "You can use 'sh', 'bash' or even an absolute path for an application",
|
||||
}, {
|
||||
Type: "text",
|
||||
Title: "Command to Run on OnSuccess",
|
||||
Placeholder: "ping google.com",
|
||||
DbField: "var1",
|
||||
SmallText: "This command will run everytime a service is receiving a Successful event.",
|
||||
}, {
|
||||
Type: "text",
|
||||
Title: "Command to Run on OnFailure",
|
||||
Placeholder: "ping offline.com",
|
||||
DbField: "var2",
|
||||
SmallText: "This command will run everytime a service is receiving a Failing event.",
|
||||
}}},
|
||||
}
|
||||
|
||||
// init the command notifier
|
||||
func init() {
|
||||
err := notifier.AddNotifier(command)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func runCommand(app, cmd string) (string, string, error) {
|
||||
testCmd := exec.Command(app, "-c", cmd)
|
||||
|
||||
var stdout, stderr []byte
|
||||
var errStdout, errStderr error
|
||||
stdoutIn, _ := testCmd.StdoutPipe()
|
||||
stderrIn, _ := testCmd.StderrPipe()
|
||||
testCmd.Start()
|
||||
|
||||
go func() {
|
||||
stdout, errStdout = copyAndCapture(os.Stdout, stdoutIn)
|
||||
}()
|
||||
|
||||
go func() {
|
||||
stderr, errStderr = copyAndCapture(os.Stderr, stderrIn)
|
||||
}()
|
||||
|
||||
err := testCmd.Wait()
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
if errStdout != nil || errStderr != nil {
|
||||
return "", "", errors.New("failed to capture stdout or stderr")
|
||||
}
|
||||
|
||||
outStr, errStr := string(stdout), string(stderr)
|
||||
return outStr, errStr, err
|
||||
}
|
||||
|
||||
// copyAndCapture captures the response from a terminal command
|
||||
func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
|
||||
var out []byte
|
||||
buf := make([]byte, 1024, 1024)
|
||||
for {
|
||||
n, err := r.Read(buf[:])
|
||||
if n > 0 {
|
||||
d := buf[:n]
|
||||
out = append(out, d...)
|
||||
_, err := w.Write(d)
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
// Read returns io.EOF at the end of file, which is not an error for us
|
||||
if err == io.EOF {
|
||||
err = nil
|
||||
}
|
||||
return out, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *commandLine) Select() *notifier.Notification {
|
||||
return u.Notification
|
||||
}
|
||||
|
||||
// OnFailure for commandLine will trigger failing service
|
||||
func (u *commandLine) OnFailure(s *types.Service, f *types.Failure) {
|
||||
u.AddQueue(s.Id, u.Var2)
|
||||
u.Online = false
|
||||
}
|
||||
|
||||
// OnSuccess for commandLine will trigger successful service
|
||||
func (u *commandLine) OnSuccess(s *types.Service) {
|
||||
if !u.Online {
|
||||
u.ResetUniqueQueue(s.Id)
|
||||
u.AddQueue(s.Id, u.Var1)
|
||||
}
|
||||
u.Online = true
|
||||
}
|
||||
|
||||
// OnSave for commandLine triggers when this notifier has been saved
|
||||
func (u *commandLine) OnSave() error {
|
||||
u.AddQueue(0, u.Var1)
|
||||
u.AddQueue(0, u.Var2)
|
||||
return nil
|
||||
}
|
||||
|
||||
// OnTest for commandLine triggers when this notifier has been saved
|
||||
func (u *commandLine) OnTest() error {
|
||||
in, out, err := runCommand(u.Host, u.Var1)
|
||||
utils.Log(1, in)
|
||||
utils.Log(1, out)
|
||||
return err
|
||||
}
|
||||
|
||||
// Send for commandLine will send message to expo command push notifications endpoint
|
||||
func (u *commandLine) Send(msg interface{}) error {
|
||||
cmd := msg.(string)
|
||||
_, _, err := runCommand(u.Host, cmd)
|
||||
return err
|
||||
}
|
|
@ -0,0 +1,125 @@
|
|||
// Statup
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statup
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package notifiers
|
||||
|
||||
import (
|
||||
"github.com/hunterlong/statup/core/notifier"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
commandTest = "curl -I https://statup.io/"
|
||||
)
|
||||
|
||||
func TestCommandNotifier(t *testing.T) {
|
||||
t.Parallel()
|
||||
command.Host = "sh"
|
||||
command.Var1 = commandTest
|
||||
command.Var2 = commandTest
|
||||
currentCount = CountNotifiers()
|
||||
|
||||
t.Run("Load command", func(t *testing.T) {
|
||||
command.Host = "sh"
|
||||
command.Var1 = commandTest
|
||||
command.Var2 = commandTest
|
||||
command.Delay = time.Duration(100 * time.Millisecond)
|
||||
command.Limits = 99
|
||||
err := notifier.AddNotifier(command)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "Hunter Long", command.Author)
|
||||
assert.Equal(t, "sh", command.Host)
|
||||
assert.Equal(t, commandTest, command.Var1)
|
||||
assert.Equal(t, commandTest, command.Var2)
|
||||
})
|
||||
|
||||
t.Run("Load command Notifier", func(t *testing.T) {
|
||||
notifier.Load()
|
||||
})
|
||||
|
||||
t.Run("command Notifier Tester", func(t *testing.T) {
|
||||
assert.True(t, command.CanTest())
|
||||
})
|
||||
|
||||
t.Run("command Within Limits", func(t *testing.T) {
|
||||
ok, err := command.WithinLimits()
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, ok)
|
||||
})
|
||||
|
||||
t.Run("command OnFailure", func(t *testing.T) {
|
||||
command.OnFailure(TestService, TestFailure)
|
||||
assert.Equal(t, 1, len(command.Queue))
|
||||
})
|
||||
|
||||
t.Run("command OnFailure multiple times", func(t *testing.T) {
|
||||
for i := 0; i <= 50; i++ {
|
||||
command.OnFailure(TestService, TestFailure)
|
||||
}
|
||||
assert.Equal(t, 52, len(command.Queue))
|
||||
})
|
||||
|
||||
t.Run("command Check Offline", func(t *testing.T) {
|
||||
assert.False(t, command.Online)
|
||||
})
|
||||
|
||||
t.Run("command OnSuccess", func(t *testing.T) {
|
||||
command.OnSuccess(TestService)
|
||||
assert.Equal(t, 1, len(command.Queue))
|
||||
})
|
||||
|
||||
t.Run("command Queue after being online", func(t *testing.T) {
|
||||
assert.True(t, command.Online)
|
||||
assert.Equal(t, 1, len(command.Queue))
|
||||
})
|
||||
|
||||
t.Run("command OnSuccess Again", func(t *testing.T) {
|
||||
assert.True(t, command.Online)
|
||||
command.OnSuccess(TestService)
|
||||
assert.Equal(t, 1, len(command.Queue))
|
||||
go notifier.Queue(command)
|
||||
time.Sleep(5 * time.Second)
|
||||
assert.Equal(t, 0, len(command.Queue))
|
||||
})
|
||||
|
||||
t.Run("command Within Limits again", func(t *testing.T) {
|
||||
ok, err := command.WithinLimits()
|
||||
assert.Nil(t, err)
|
||||
assert.True(t, ok)
|
||||
})
|
||||
|
||||
t.Run("command Send", func(t *testing.T) {
|
||||
err := command.Send(commandTest)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, 0, len(command.Queue))
|
||||
})
|
||||
|
||||
t.Run("command Test", func(t *testing.T) {
|
||||
err := command.OnTest()
|
||||
assert.Nil(t, err)
|
||||
})
|
||||
|
||||
t.Run("command Queue", func(t *testing.T) {
|
||||
go notifier.Queue(command)
|
||||
time.Sleep(5 * time.Second)
|
||||
assert.Equal(t, "sh", command.Host)
|
||||
assert.Equal(t, commandTest, command.Var1)
|
||||
assert.Equal(t, commandTest, command.Var2)
|
||||
assert.Equal(t, 0, len(command.Queue))
|
||||
})
|
||||
|
||||
}
|
|
@ -164,7 +164,6 @@ func (u *email) Send(msg interface{}) error {
|
|||
email := msg.(*emailOutgoing)
|
||||
err := u.dialSend(email)
|
||||
if err != nil {
|
||||
utils.Log(3, fmt.Sprintf("email Notifier could not send email: %v", err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
@ -246,7 +245,7 @@ func (u *email) OnTest() error {
|
|||
Method: "GET",
|
||||
Timeout: 20,
|
||||
LastStatusCode: 200,
|
||||
Expected: utils.NullString("test example"),
|
||||
Expected: types.NewNullString("test example"),
|
||||
LastResponse: "<html>this is an example response</html>",
|
||||
CreatedAt: time.Now().Add(-24 * time.Hour),
|
||||
}
|
||||
|
@ -271,7 +270,7 @@ func (u *email) dialSend(email *emailOutgoing) error {
|
|||
m.SetHeader("Subject", email.Subject)
|
||||
m.SetBody("text/html", email.Source)
|
||||
if err := mailer.DialAndSend(m); err != nil {
|
||||
utils.Log(3, fmt.Sprintf("email '%v' sent to: %v using the %v template (size: %v) %v", email.Subject, email.To, email.Template, len([]byte(email.Source)), err))
|
||||
utils.Log(3, fmt.Sprintf("email '%v' sent to: %v (size: %v) %v", email.Subject, email.To, len([]byte(email.Source)), err))
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -36,7 +36,7 @@ var TestService = &types.Service{
|
|||
Name: "Interpol - All The Rage Back Home",
|
||||
Domain: "https://www.youtube.com/watch?v=-u6DvRyyKGU",
|
||||
ExpectedStatus: 200,
|
||||
Expected: "test example",
|
||||
Expected: types.NewNullString("test example"),
|
||||
Interval: 30,
|
||||
Type: "http",
|
||||
Method: "GET",
|
||||
|
|
|
@ -152,7 +152,7 @@ func (w *webhooker) OnTest() error {
|
|||
Method: "GET",
|
||||
Timeout: 20,
|
||||
LastStatusCode: 404,
|
||||
Expected: utils.NullString("test example"),
|
||||
Expected: types.NewNullString("test example"),
|
||||
LastResponse: "<html>this is an example response</html>",
|
||||
CreatedAt: time.Now().Add(-24 * time.Hour),
|
||||
}
|
||||
|
|
|
@ -23,14 +23,14 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
WEBHOOK_URL = "https://jsonplaceholder.typicode.com/posts"
|
||||
webhookTestUrl = "https://jsonplaceholder.typicode.com/posts"
|
||||
webhookMessage = `{ "title": "%service.Id", "body": "%service.Name", "online": %service.Online, "userId": 19999 }`
|
||||
apiKey = "application/json"
|
||||
fullMsg string
|
||||
)
|
||||
|
||||
func init() {
|
||||
webhook.Host = WEBHOOK_URL
|
||||
webhook.Host = webhookTestUrl
|
||||
webhook.Var1 = "POST"
|
||||
}
|
||||
|
||||
|
@ -40,13 +40,13 @@ func TestWebhookNotifier(t *testing.T) {
|
|||
currentCount = CountNotifiers()
|
||||
|
||||
t.Run("Load webhooker", func(t *testing.T) {
|
||||
webhook.Host = WEBHOOK_URL
|
||||
webhook.Host = webhookTestUrl
|
||||
webhook.Delay = time.Duration(100 * time.Millisecond)
|
||||
webhook.ApiKey = apiKey
|
||||
err := notifier.AddNotifier(webhook)
|
||||
assert.Nil(t, err)
|
||||
assert.Equal(t, "Hunter Long", webhook.Author)
|
||||
assert.Equal(t, WEBHOOK_URL, webhook.Host)
|
||||
assert.Equal(t, webhookTestUrl, webhook.Host)
|
||||
assert.Equal(t, apiKey, webhook.ApiKey)
|
||||
})
|
||||
|
||||
|
@ -101,7 +101,7 @@ func TestWebhookNotifier(t *testing.T) {
|
|||
t.Run("webhooker Queue", func(t *testing.T) {
|
||||
go notifier.Queue(webhook)
|
||||
time.Sleep(8 * time.Second)
|
||||
assert.Equal(t, WEBHOOK_URL, webhook.Host)
|
||||
assert.Equal(t, webhookTestUrl, webhook.Host)
|
||||
assert.Equal(t, 1, len(webhook.Queue))
|
||||
})
|
||||
|
||||
|
|
|
@ -1,255 +0,0 @@
|
|||
/*!
|
||||
* Pikaday
|
||||
* Copyright © 2014 David Bushell | BSD & MIT license | http://dbushell.com/
|
||||
*/
|
||||
|
||||
// Variables
|
||||
// Declare any of these variables before importing this SCSS file to easily override defaults
|
||||
// Variables are namespaced with the pd (pikaday) prefix
|
||||
|
||||
// Colours
|
||||
$pd-text-color: #333 !default;
|
||||
$pd-title-color: #333 !default;
|
||||
$pd-title-bg: #fff !default;
|
||||
$pd-picker-bg: #fff !default;
|
||||
$pd-picker-border: #ccc !default;
|
||||
$pd-picker-border-bottom: #bbb !default;
|
||||
$pd-picker-shadow: rgba(0,0,0,.5) !default;
|
||||
$pd-th-color: #999 !default;
|
||||
$pd-day-color: #666 !default;
|
||||
$pd-day-bg: #f5f5f5 !default;
|
||||
$pd-day-hover-color: #fff !default;
|
||||
$pd-day-hover-bg: #ff8000 !default;
|
||||
$pd-day-today-color: #33aaff !default;
|
||||
$pd-day-selected-color: #fff !default;
|
||||
$pd-day-selected-bg: #33aaff !default;
|
||||
$pd-day-selected-shadow: #178fe5 !default;
|
||||
$pd-day-disabled-color: #999 !default;
|
||||
$pd-week-color: #999 !default;
|
||||
|
||||
// Font
|
||||
$pd-font-family: "Helvetica Neue", Helvetica, Arial, sans-serif !default;
|
||||
|
||||
|
||||
.pika-single {
|
||||
z-index: 9999;
|
||||
display: block;
|
||||
position: relative;
|
||||
color: $pd-text-color;
|
||||
background: $pd-picker-bg;
|
||||
border: 1px solid $pd-picker-border;
|
||||
border-bottom-color: $pd-picker-border-bottom;
|
||||
font-family: $pd-font-family;
|
||||
|
||||
&.is-hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.is-bound {
|
||||
position: absolute;
|
||||
box-shadow: 0 5px 15px -5px $pd-picker-shadow;
|
||||
}
|
||||
}
|
||||
|
||||
// clear child float (pika-lendar), using the famous micro clearfix hack
|
||||
// http://nicolasgallagher.com/micro-clearfix-hack/
|
||||
.pika-single {
|
||||
*zoom: 1;
|
||||
|
||||
&:before,
|
||||
&:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
|
||||
&:after { clear: both }
|
||||
}
|
||||
|
||||
.pika-lendar {
|
||||
float: left;
|
||||
width: 240px;
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
.pika-title {
|
||||
position: relative;
|
||||
text-align: center;
|
||||
|
||||
select {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
z-index: 9998;
|
||||
margin: 0;
|
||||
left: 0;
|
||||
top: 5px;
|
||||
filter: alpha(opacity=0);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.pika-label {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
position: relative;
|
||||
z-index: 9999;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 5px 3px;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
font-weight: bold;
|
||||
color: $pd-title-color;
|
||||
background-color: $pd-title-bg;
|
||||
}
|
||||
|
||||
.pika-prev,
|
||||
.pika-next {
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
outline: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
width: 20px;
|
||||
height: 30px;
|
||||
text-indent: 20px; // hide text using text-indent trick, using width value (it's enough)
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
background-color: transparent;
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 75% 75%;
|
||||
opacity: .5;
|
||||
*position: absolute;
|
||||
*top: 0;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
&.is-disabled {
|
||||
cursor: default;
|
||||
opacity: .2;
|
||||
}
|
||||
}
|
||||
|
||||
.pika-prev,
|
||||
.is-rtl .pika-next {
|
||||
float: left;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAUklEQVR42u3VMQoAIBADQf8Pgj+OD9hG2CtONJB2ymQkKe0HbwAP0xucDiQWARITIDEBEnMgMQ8S8+AqBIl6kKgHiXqQqAeJepBo/z38J/U0uAHlaBkBl9I4GwAAAABJRU5ErkJggg==');
|
||||
*left: 0;
|
||||
}
|
||||
|
||||
.pika-next,
|
||||
.is-rtl .pika-prev {
|
||||
float: right;
|
||||
background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAeCAYAAAAsEj5rAAAAU0lEQVR42u3VOwoAMAgE0dwfAnNjU26bYkBCFGwfiL9VVWoO+BJ4Gf3gtsEKKoFBNTCoCAYVwaAiGNQGMUHMkjGbgjk2mIONuXo0nC8XnCf1JXgArVIZAQh5TKYAAAAASUVORK5CYII=');
|
||||
*right: 0;
|
||||
}
|
||||
|
||||
.pika-select {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
}
|
||||
|
||||
.pika-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
border: 0;
|
||||
|
||||
th,
|
||||
td {
|
||||
width: 14.285714285714286%;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
th {
|
||||
color: $pd-th-color;
|
||||
font-size: 12px;
|
||||
line-height: 25px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
abbr {
|
||||
border-bottom: none;
|
||||
cursor: help;
|
||||
}
|
||||
}
|
||||
|
||||
.pika-button {
|
||||
cursor: pointer;
|
||||
display: block;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
outline: none;
|
||||
border: 0;
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
color: $pd-day-color;
|
||||
font-size: 12px;
|
||||
line-height: 15px;
|
||||
text-align: right;
|
||||
background: $pd-day-bg;
|
||||
|
||||
.is-today & {
|
||||
color: $pd-day-today-color;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.is-selected & {
|
||||
color: $pd-day-selected-color;
|
||||
font-weight: bold;
|
||||
background: $pd-day-selected-bg;
|
||||
box-shadow: inset 0 1px 3px $pd-day-selected-shadow;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.is-disabled &,
|
||||
.is-outside-current-month & {
|
||||
color: $pd-day-disabled-color;
|
||||
opacity: .3;
|
||||
}
|
||||
|
||||
.is-disabled & {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: $pd-day-hover-color;
|
||||
background: $pd-day-hover-bg;
|
||||
box-shadow: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.is-selection-disabled {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.pika-week {
|
||||
font-size: 11px;
|
||||
color: $pd-week-color;
|
||||
}
|
||||
|
||||
.is-inrange .pika-button {
|
||||
background: #D5E9F7;
|
||||
}
|
||||
|
||||
.is-startrange .pika-button {
|
||||
color: #fff;
|
||||
background: #6CB31D;
|
||||
box-shadow: none;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.is-endrange .pika-button {
|
||||
color: #fff;
|
||||
background: #33aaff;
|
||||
box-shadow: none;
|
||||
border-radius: 3px;
|
||||
}
|
|
@ -16,7 +16,6 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -33,11 +32,11 @@ type Core struct {
|
|||
ApiKey string `gorm:"column:api_key" json:"-"`
|
||||
ApiSecret string `gorm:"column:api_secret" json:"-"`
|
||||
Style string `gorm:"not null;column:style" json:"style,omitempty"`
|
||||
Footer sql.NullString `gorm:"column:footer" json:"footer"`
|
||||
Footer NullString `gorm:"column:footer" json:"footer"`
|
||||
Domain string `gorm:"not null;column:domain" json:"domain"`
|
||||
Version string `gorm:"column:version" json:"version"`
|
||||
MigrationId int64 `gorm:"column:migration_id" json:"migration_id,omitempty"`
|
||||
UseCdn sql.NullBool `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"`
|
||||
UseCdn NullBool `gorm:"column:use_cdn;default:false" json:"using_cdn,omitempty"`
|
||||
Timezone float32 `gorm:"column:timezone;default:-8.0" json:"timezone,omitempty"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
|
@ -49,10 +48,3 @@ type Core struct {
|
|||
AllPlugins []PluginActions `gorm:"-" json:"-"`
|
||||
Notifications []AllNotifiers `gorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
//type CoreInterface interface {
|
||||
// SelectAllServices() ([]*Service, error)
|
||||
// Count24HFailures() uint64
|
||||
// ServicesCount() int
|
||||
// CountOnline() int
|
||||
//}
|
||||
|
|
|
@ -33,6 +33,6 @@ type Failure struct {
|
|||
}
|
||||
|
||||
type FailureInterface interface {
|
||||
Ago() string // Ago returns a human readble timestamp
|
||||
Ago() string // Ago returns a human readable timestamp
|
||||
ParseError() string // ParseError returns a human readable error for a service failure
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
@ -28,7 +27,7 @@ type Message struct {
|
|||
StartOn time.Time `gorm:"column:start_on"`
|
||||
EndOn time.Time `gorm:"column:end_on"`
|
||||
ServiceId int64 `gorm:"index;column:service"`
|
||||
NotifyUsers sql.NullBool `gorm:"column:notify_users"`
|
||||
NotifyUsers NullBool `gorm:"column:notify_users"`
|
||||
NotifyMethod string `gorm:"column:notify_method"`
|
||||
NotifyBefore time.Duration `gorm:"column:notify_before"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
|
|
|
@ -0,0 +1,121 @@
|
|||
// Statup
|
||||
// Copyright (C) 2018. Hunter Long and the project contributors
|
||||
// Written by Hunter Long <info@socialeck.com> and the project contributors
|
||||
//
|
||||
// https://github.com/hunterlong/statup
|
||||
//
|
||||
// The licenses for most software and other practical works are designed
|
||||
// to take away your freedom to share and change the works. By contrast,
|
||||
// the GNU General Public License is intended to guarantee your freedom to
|
||||
// share and change all versions of a program--to make sure it remains free
|
||||
// software for all its users.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package types
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
)
|
||||
|
||||
// NewNullString returns a sql.NullString for JSON parsing
|
||||
func NewNullString(s string) NullString {
|
||||
return NullString{sql.NullString{s, true}}
|
||||
}
|
||||
|
||||
// NewNullBool returns a sql.NullBool for JSON parsing
|
||||
func NewNullBool(s bool) NullBool {
|
||||
return NullBool{sql.NullBool{s, true}}
|
||||
}
|
||||
|
||||
// NewNullInt64 returns a sql.NullInt64 for JSON parsing
|
||||
func NewNullInt64(s int64) NullInt64 {
|
||||
return NullInt64{sql.NullInt64{s, true}}
|
||||
}
|
||||
|
||||
// NewNullFloat64 returns a sql.NullFloat64 for JSON parsing
|
||||
func NewNullFloat64(s float64) NullFloat64 {
|
||||
return NullFloat64{sql.NullFloat64{s, true}}
|
||||
}
|
||||
|
||||
// NullInt64 is an alias for sql.NullInt64 data type
|
||||
type NullInt64 struct {
|
||||
sql.NullInt64
|
||||
}
|
||||
|
||||
// NullBool is an alias for sql.NullBool data type
|
||||
type NullBool struct {
|
||||
sql.NullBool
|
||||
}
|
||||
|
||||
// NullString is an alias for sql.NullString data type
|
||||
type NullString struct {
|
||||
sql.NullString
|
||||
}
|
||||
|
||||
// NullFloat64 is an alias for sql.NullFloat64 data type
|
||||
type NullFloat64 struct {
|
||||
sql.NullFloat64
|
||||
}
|
||||
|
||||
// MarshalJSON for NullInt64
|
||||
func (ni *NullInt64) MarshalJSON() ([]byte, error) {
|
||||
if !ni.Valid {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
return json.Marshal(ni.Int64)
|
||||
}
|
||||
|
||||
// MarshalJSON for NullFloat64
|
||||
func (ni *NullFloat64) MarshalJSON() ([]byte, error) {
|
||||
if !ni.Valid {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
return json.Marshal(ni.Float64)
|
||||
}
|
||||
|
||||
// MarshalJSON for NullBool
|
||||
func (nb *NullBool) MarshalJSON() ([]byte, error) {
|
||||
if !nb.Valid {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
return json.Marshal(nb.Bool)
|
||||
}
|
||||
|
||||
// MarshalJSON for NullString
|
||||
func (ns *NullString) MarshalJSON() ([]byte, error) {
|
||||
if !ns.Valid {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
return json.Marshal(ns.String)
|
||||
}
|
||||
|
||||
// Unmarshaler for NullInt64
|
||||
func (nf *NullInt64) UnmarshalJSON(b []byte) error {
|
||||
err := json.Unmarshal(b, &nf.Int64)
|
||||
nf.Valid = (err == nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Unmarshaler for NullFloat64
|
||||
func (nf *NullFloat64) UnmarshalJSON(b []byte) error {
|
||||
err := json.Unmarshal(b, &nf.Float64)
|
||||
nf.Valid = (err == nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Unmarshaler for NullBool
|
||||
func (nf *NullBool) UnmarshalJSON(b []byte) error {
|
||||
err := json.Unmarshal(b, &nf.Bool)
|
||||
nf.Valid = (err == nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// Unmarshaler for NullString
|
||||
func (nf *NullString) UnmarshalJSON(b []byte) error {
|
||||
err := json.Unmarshal(b, &nf.String)
|
||||
nf.Valid = (err == nil)
|
||||
return err
|
||||
}
|
|
@ -16,39 +16,38 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Service is the main struct for Services
|
||||
type Service struct {
|
||||
Id int64 `gorm:"primary_key;column:id" json:"id"`
|
||||
Name string `gorm:"column:name" json:"name"`
|
||||
Domain string `gorm:"column:domain" json:"domain"`
|
||||
Expected sql.NullString `gorm:"column:expected" json:"expected"`
|
||||
ExpectedStatus int `gorm:"default:200;column:expected_status" json:"expected_status"`
|
||||
Interval int `gorm:"default:30;column:check_interval" json:"check_interval"`
|
||||
Type string `gorm:"column:check_type" json:"type"`
|
||||
Method string `gorm:"column:method" json:"method"`
|
||||
PostData sql.NullString `gorm:"column:post_data" json:"post_data"`
|
||||
Port int `gorm:"not null;column:port" json:"port"`
|
||||
Timeout int `gorm:"default:30;column:timeout" json:"timeout"`
|
||||
Order int `gorm:"default:0;column:order_id" json:"order_id"`
|
||||
AllowNotifications sql.NullBool `gorm:"default:false;column:allow_notifications" json:"allow_notifications"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
Online bool `gorm:"-" json:"online"`
|
||||
Latency float64 `gorm:"-" json:"latency"`
|
||||
PingTime float64 `gorm:"-" json:"ping_time"`
|
||||
Online24Hours float32 `gorm:"-" json:"online_24_hours"`
|
||||
AvgResponse string `gorm:"-" json:"avg_response"`
|
||||
Running chan bool `gorm:"-" json:"-"`
|
||||
Checkpoint time.Time `gorm:"-" json:"-"`
|
||||
SleepDuration time.Duration `gorm:"-" json:"-"`
|
||||
LastResponse string `gorm:"-" json:"-"`
|
||||
LastStatusCode int `gorm:"-" json:"status_code"`
|
||||
LastOnline time.Time `gorm:"-" json:"last_online"`
|
||||
Failures []interface{} `gorm:"-" json:"failures,omitempty"`
|
||||
Id int64 `gorm:"primary_key;column:id" json:"id"`
|
||||
Name string `gorm:"column:name" json:"name"`
|
||||
Domain string `gorm:"column:domain" json:"domain"`
|
||||
Expected NullString `gorm:"column:expected" json:"expected"`
|
||||
ExpectedStatus int `gorm:"default:200;column:expected_status" json:"expected_status"`
|
||||
Interval int `gorm:"default:30;column:check_interval" json:"check_interval"`
|
||||
Type string `gorm:"column:check_type" json:"type"`
|
||||
Method string `gorm:"column:method" json:"method"`
|
||||
PostData NullString `gorm:"column:post_data" json:"post_data"`
|
||||
Port int `gorm:"not null;column:port" json:"port"`
|
||||
Timeout int `gorm:"default:30;column:timeout" json:"timeout"`
|
||||
Order int `gorm:"default:0;column:order_id" json:"order_id"`
|
||||
AllowNotifications NullBool `gorm:"default:false;column:allow_notifications" json:"allow_notifications"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
Online bool `gorm:"-" json:"online"`
|
||||
Latency float64 `gorm:"-" json:"latency"`
|
||||
PingTime float64 `gorm:"-" json:"ping_time"`
|
||||
Online24Hours float32 `gorm:"-" json:"online_24_hours"`
|
||||
AvgResponse string `gorm:"-" json:"avg_response"`
|
||||
Running chan bool `gorm:"-" json:"-"`
|
||||
Checkpoint time.Time `gorm:"-" json:"-"`
|
||||
SleepDuration time.Duration `gorm:"-" json:"-"`
|
||||
LastResponse string `gorm:"-" json:"-"`
|
||||
LastStatusCode int `gorm:"-" json:"status_code"`
|
||||
LastOnline time.Time `gorm:"-" json:"last_online"`
|
||||
Failures []interface{} `gorm:"-" json:"failures,omitempty"`
|
||||
}
|
||||
|
||||
type ServiceInterface interface {
|
||||
|
|
|
@ -16,21 +16,20 @@
|
|||
package types
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"time"
|
||||
)
|
||||
|
||||
// User is the main struct for Users
|
||||
type User struct {
|
||||
Id int64 `gorm:"primary_key;column:id" json:"id"`
|
||||
Username string `gorm:"type:varchar(100);unique;column:username;" json:"username"`
|
||||
Password string `gorm:"column:password" json:"-"`
|
||||
Email string `gorm:"type:varchar(100);unique;column:email" json:"-"`
|
||||
ApiKey string `gorm:"column:api_key" json:"api_key"`
|
||||
ApiSecret string `gorm:"column:api_secret" json:"-"`
|
||||
Admin sql.NullBool `gorm:"column:administrator" json:"admin"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
Id int64 `gorm:"primary_key;column:id" json:"id"`
|
||||
Username string `gorm:"type:varchar(100);unique;column:username;" json:"username"`
|
||||
Password string `gorm:"column:password" json:"-"`
|
||||
Email string `gorm:"type:varchar(100);unique;column:email" json:"-"`
|
||||
ApiKey string `gorm:"column:api_key" json:"api_key"`
|
||||
ApiSecret string `gorm:"column:api_secret" json:"-"`
|
||||
Admin NullBool `gorm:"column:administrator" json:"admin"`
|
||||
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
|
||||
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
|
||||
UserInterface `gorm:"-" json:"-"`
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/ararog/timeago"
|
||||
|
@ -44,14 +43,6 @@ func init() {
|
|||
}
|
||||
}
|
||||
|
||||
func NullString(s string) sql.NullString {
|
||||
return sql.NullString{s, true}
|
||||
}
|
||||
|
||||
func NullBool(s bool) sql.NullBool {
|
||||
return sql.NullBool{s, true}
|
||||
}
|
||||
|
||||
func StringPoint(s string) *string {
|
||||
val := new(string)
|
||||
*val = s
|
||||
|
|
Loading…
Reference in New Issue