groups reorder - time fixes

pull/143/head
Hunter Long 2019-02-19 18:11:40 -08:00
parent d5e346fa0e
commit f2dac7bcc8
14 changed files with 116 additions and 68 deletions

View File

@ -2,6 +2,7 @@ package core
import ( import (
"github.com/hunterlong/statping/types" "github.com/hunterlong/statping/types"
"sort"
"time" "time"
) )
@ -26,6 +27,13 @@ func (g *Group) Create() (int64, error) {
return g.Id, db.Error return g.Id, db.Error
} }
// Update will update a group
func (g *Group) Update() (int64, error) {
g.UpdatedAt = time.Now()
db := groupsDb().Update(g)
return g.Id, db.Error
}
// Services returns all services belonging to a group // Services returns all services belonging to a group
func (g *Group) Services() []*Service { func (g *Group) Services() []*Service {
var services []*Service var services []*Service
@ -41,7 +49,7 @@ func (g *Group) Services() []*Service {
func SelectGroups(includeAll bool, auth bool) []*Group { func SelectGroups(includeAll bool, auth bool) []*Group {
var groups []*Group var groups []*Group
var validGroups []*Group var validGroups []*Group
groupsDb().Find(&groups).Order("id desc") groupsDb().Find(&groups).Order("order_id desc")
if includeAll { if includeAll {
emptyGroup := &Group{&types.Group{Id: 0, Public: types.NewNullBool(true)}} emptyGroup := &Group{&types.Group{Id: 0, Public: types.NewNullBool(true)}}
groups = append(groups, emptyGroup) groups = append(groups, emptyGroup)
@ -55,15 +63,24 @@ func SelectGroups(includeAll bool, auth bool) []*Group {
validGroups = append(validGroups, g) validGroups = append(validGroups, g)
} }
} }
sort.Sort(GroupOrder(validGroups))
return validGroups return validGroups
} }
// SelectGroup returns a *core.Group // SelectGroup returns a *core.Group
func SelectGroup(id int64) *Group { func SelectGroup(id int64) *Group {
for _, g := range SelectGroups(false, false) { for _, g := range SelectGroups(true, true) {
if g.Id == id { if g.Id == id {
return g return g
} }
} }
return nil return nil
} }
// GroupOrder will reorder the groups based on 'order_id' (Order)
type GroupOrder []*Group
// Sort interface for resorting the Groups in order
func (c GroupOrder) Len() int { return len(c) }
func (c GroupOrder) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c GroupOrder) Less(i, j int) bool { return c[i].Order < c[j].Order }

View File

@ -112,13 +112,21 @@ func insertSampleGroups() error {
group1 := &Group{&types.Group{ group1 := &Group{&types.Group{
Name: "Main Services", Name: "Main Services",
Public: types.NewNullBool(true), Public: types.NewNullBool(true),
Order: 2,
}} }}
_, err := group1.Create() _, err := group1.Create()
group2 := &Group{&types.Group{ group2 := &Group{&types.Group{
Name: "Linked Services", Name: "Linked Services",
Public: types.NewNullBool(false), Public: types.NewNullBool(false),
Order: 1,
}} }}
_, err = group2.Create() _, err = group2.Create()
group3 := &Group{&types.Group{
Name: "Empty Group",
Public: types.NewNullBool(false),
Order: 3,
}}
_, err = group3.Create()
return err return err
} }

View File

@ -98,6 +98,9 @@ var handlerFuncs = func(w http.ResponseWriter, r *http.Request) template.FuncMap
"ToUnix": func(t time.Time) int64 { "ToUnix": func(t time.Time) int64 {
return t.UTC().Unix() return t.UTC().Unix()
}, },
"ParseTime": func(t time.Time, format string) string {
return t.Format(format)
},
"FromUnix": func(t int64) string { "FromUnix": func(t int64) string {
return utils.Timezoner(time.Unix(t, 0), core.CoreApp.Timezone).Format("Monday, January 02") return utils.Timezoner(time.Unix(t, 0), core.CoreApp.Timezone).Format("Monday, January 02")
}, },

View File

@ -92,3 +92,26 @@ func apiGroupDeleteHandler(w http.ResponseWriter, r *http.Request) {
} }
sendJsonAction(group, "delete", w, r) sendJsonAction(group, "delete", w, r)
} }
type groupOrder struct {
Id int64 `json:"group"`
Order int `json:"order"`
}
func apiGroupReorderHandler(w http.ResponseWriter, r *http.Request) {
if !IsFullAuthenticated(r) {
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
r.ParseForm()
var newOrder []*groupOrder
decoder := json.NewDecoder(r.Body)
decoder.Decode(&newOrder)
for _, g := range newOrder {
group := core.SelectGroup(g.Id)
group.Order = g.Order
group.Update()
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(newOrder)
}

View File

@ -92,6 +92,7 @@ func Router() *mux.Router {
r.Handle("/api/groups", http.HandlerFunc(apiCreateGroupHandler)).Methods("POST") r.Handle("/api/groups", http.HandlerFunc(apiCreateGroupHandler)).Methods("POST")
r.Handle("/api/groups/{id}", http.HandlerFunc(apiGroupHandler)).Methods("GET") r.Handle("/api/groups/{id}", http.HandlerFunc(apiGroupHandler)).Methods("GET")
r.Handle("/api/groups/{id}", http.HandlerFunc(apiGroupDeleteHandler)).Methods("DELETE") r.Handle("/api/groups/{id}", http.HandlerFunc(apiGroupDeleteHandler)).Methods("DELETE")
r.Handle("/api/groups/reorder", http.HandlerFunc(apiGroupReorderHandler)).Methods("POST")
// API Routes // API Routes
r.Handle("/api", http.HandlerFunc(apiIndexHandler)) r.Handle("/api", http.HandlerFunc(apiIndexHandler))
@ -102,7 +103,7 @@ func Router() *mux.Router {
r.Handle("/api/services", http.HandlerFunc(apiAllServicesHandler)).Methods("GET") r.Handle("/api/services", http.HandlerFunc(apiAllServicesHandler)).Methods("GET")
r.Handle("/api/services", http.HandlerFunc(apiCreateServiceHandler)).Methods("POST") r.Handle("/api/services", http.HandlerFunc(apiCreateServiceHandler)).Methods("POST")
r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceHandler)).Methods("GET") r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceHandler)).Methods("GET")
r.Handle("/api/reorder", http.HandlerFunc(reorderServiceHandler)).Methods("POST") r.Handle("/api/services/reorder", http.HandlerFunc(reorderServiceHandler)).Methods("POST")
r.Handle("/api/services/{id}/data", cached("30s", "application/json", http.HandlerFunc(apiServiceDataHandler))).Methods("GET") r.Handle("/api/services/{id}/data", cached("30s", "application/json", http.HandlerFunc(apiServiceDataHandler))).Methods("GET")
r.Handle("/api/services/{id}/ping", cached("30s", "application/json", http.HandlerFunc(apiServicePingDataHandler))).Methods("GET") r.Handle("/api/services/{id}/ping", cached("30s", "application/json", http.HandlerFunc(apiServicePingDataHandler))).Methods("GET")
r.Handle("/api/services/{id}/heatmap", cached("30s", "application/json", http.HandlerFunc(apiServiceHeatmapHandler))).Methods("GET") r.Handle("/api/services/{id}/heatmap", cached("30s", "application/json", http.HandlerFunc(apiServiceHeatmapHandler))).Methods("GET")

View File

@ -93,7 +93,7 @@ func dataJson(s *types.Service, f *types.Failure) map[string]interface{} {
// OnFailure will trigger failing service // OnFailure will trigger failing service
func (u *mobilePush) OnFailure(s *types.Service, f *types.Failure) { func (u *mobilePush) OnFailure(s *types.Service, f *types.Failure) {
data := dataJson(s, f) data := dataJson(s, f)
msg := &PushArray{ msg := &pushArray{
Message: fmt.Sprintf("Your service '%v' is currently failing! Reason: %v", s.Name, f.Issue), Message: fmt.Sprintf("Your service '%v' is currently failing! Reason: %v", s.Name, f.Issue),
Title: "Service Offline", Title: "Service Offline",
Topic: mobileIdentifier, Topic: mobileIdentifier,
@ -108,7 +108,7 @@ func (u *mobilePush) OnSuccess(s *types.Service) {
data := dataJson(s, nil) data := dataJson(s, nil)
if !u.Online { if !u.Online {
u.ResetUniqueQueue(s.Id) u.ResetUniqueQueue(s.Id)
msg := &PushArray{ msg := &pushArray{
Message: fmt.Sprintf("Your service '%v' is back online!", s.Name), Message: fmt.Sprintf("Your service '%v' is back online!", s.Name),
Title: "Service Online", Title: "Service Online",
Topic: mobileIdentifier, Topic: mobileIdentifier,
@ -121,7 +121,7 @@ func (u *mobilePush) OnSuccess(s *types.Service) {
// OnSave triggers when this notifier has been saved // OnSave triggers when this notifier has been saved
func (u *mobilePush) OnSave() error { func (u *mobilePush) OnSave() error {
msg := &PushArray{ msg := &pushArray{
Message: "The Mobile Notifier has been saved", Message: "The Mobile Notifier has been saved",
Title: "Notification Saved", Title: "Notification Saved",
Topic: mobileIdentifier, Topic: mobileIdentifier,
@ -132,7 +132,7 @@ func (u *mobilePush) OnSave() error {
// OnTest triggers when this notifier has been saved // OnTest triggers when this notifier has been saved
func (u *mobilePush) OnTest() error { func (u *mobilePush) OnTest() error {
msg := &PushArray{ msg := &pushArray{
Message: "Testing the Mobile Notifier", Message: "Testing the Mobile Notifier",
Title: "Testing Notifications", Title: "Testing Notifications",
Topic: mobileIdentifier, Topic: mobileIdentifier,
@ -143,7 +143,7 @@ func (u *mobilePush) OnTest() error {
if err != nil { if err != nil {
return err return err
} }
var output MobileResponse var output mobileResponse
err = json.Unmarshal(body, &output) err = json.Unmarshal(body, &output)
if err != nil { if err != nil {
return err return err
@ -159,7 +159,7 @@ func (u *mobilePush) OnTest() error {
// Send will send message to Statping push notifications endpoint // Send will send message to Statping push notifications endpoint
func (u *mobilePush) Send(msg interface{}) error { func (u *mobilePush) Send(msg interface{}) error {
pushMessage := msg.(*PushArray) pushMessage := msg.(*pushArray)
pushMessage.Tokens = []string{u.Var1} pushMessage.Tokens = []string{u.Var1}
pushMessage.Platform = utils.ToInt(u.Var2) pushMessage.Platform = utils.ToInt(u.Var2)
_, err := pushRequest(pushMessage) _, err := pushRequest(pushMessage)
@ -169,24 +169,24 @@ func (u *mobilePush) Send(msg interface{}) error {
return nil return nil
} }
func pushRequest(msg *PushArray) ([]byte, error) { func pushRequest(msg *pushArray) ([]byte, error) {
if msg.Platform == 1 { if msg.Platform == 1 {
msg.Title = "" msg.Title = ""
} }
body, _ := json.Marshal(&PushNotification{[]*PushArray{msg}}) body, err := json.Marshal(&PushNotification{[]*pushArray{msg}})
if err != nil {
return nil, err
}
url := "https://push.statping.com/api/push" url := "https://push.statping.com/api/push"
//if os.Getenv("GO_ENV") == "test" { body, _, err = utils.HttpRequest(url, "POST", "application/json", nil, bytes.NewBuffer(body), time.Duration(20*time.Second))
// url = "https://pushdev.statping.com/api/push"
//}
body, _, err := utils.HttpRequest(url, "POST", "application/json", nil, bytes.NewBuffer(body), time.Duration(20*time.Second))
return body, err return body, err
} }
type PushNotification struct { type PushNotification struct {
Array []*PushArray `json:"notifications"` Array []*pushArray `json:"notifications"`
} }
type PushArray struct { type pushArray struct {
Tokens []string `json:"tokens"` Tokens []string `json:"tokens"`
Platform int64 `json:"platform"` Platform int64 `json:"platform"`
Message string `json:"message"` Message string `json:"message"`
@ -195,13 +195,13 @@ type PushArray struct {
Data map[string]interface{} `json:"data,omitempty"` Data map[string]interface{} `json:"data,omitempty"`
} }
type MobileResponse struct { type mobileResponse struct {
Counts int `json:"counts"` Counts int `json:"counts"`
Logs []*MobileResponseLogs `json:"logs"` Logs []*mobileResponseLogs `json:"logs"`
Success string `json:"success"` Success string `json:"success"`
} }
type MobileResponseLogs struct { type mobileResponseLogs struct {
Type string `json:"type"` Type string `json:"type"`
Platform string `json:"platform"` Platform string `json:"platform"`
Token string `json:"token"` Token string `json:"token"`

View File

@ -34,6 +34,13 @@ const (
slackText = `{"text":"{{.}}"}` slackText = `{"text":"{{.}}"}`
) )
func init() {
err := notifier.AddNotifier(slacker)
if err != nil {
panic(err)
}
}
type slack struct { type slack struct {
*notifier.Notification *notifier.Notification
} }
@ -74,14 +81,6 @@ type slackMessage struct {
Time int64 Time int64
} }
// DEFINE YOUR NOTIFICATION HERE.
func init() {
err := notifier.AddNotifier(slacker)
if err != nil {
panic(err)
}
}
// Send will send a HTTP Post to the slack webhooker API. It accepts type: string // Send will send a HTTP Post to the slack webhooker API. It accepts type: string
func (u *slack) Send(msg interface{}) error { func (u *slack) Send(msg interface{}) error {
message := msg.(string) message := msg.(string)

View File

@ -24,10 +24,10 @@
<div class="form-group row"> <div class="form-group row">
<label class="col-sm-4 col-form-label">Message Date Range</label> <label class="col-sm-4 col-form-label">Message Date Range</label>
<div class="col-sm-4"> <div class="col-sm-4">
<input type="text" name="start_on" class="form-control form-control-plaintext" id="start_on" value="{{.StartOn}}" required> <input type="text" name="start_on" class="form-control form-control-plaintext" id="start_on" value="{{ParseTime .StartOn "2006-01-02T15:04:05Z"}}" required>
</div> </div>
<div class="col-sm-4"> <div class="col-sm-4">
<input type="text" name="end_on" class="form-control form-control-plaintext" id="end_on" value="{{.EndOn}}" required> <input type="text" name="end_on" class="form-control form-control-plaintext" id="end_on" value="{{ParseTime .EndOn "2006-01-02T15:04:05Z"}}" required>
</div> </div>
</div> </div>

View File

@ -9,6 +9,7 @@
{{ end }} {{ end }}
{{ range Groups true }} {{ range Groups true }}
{{if ne (len .Services) 0}}
<div class="col-12 full-col-12"> <div class="col-12 full-col-12">
<h4 class="group_header">{{.Name}}</h4> <h4 class="group_header">{{.Name}}</h4>
<div class="list-group online_list mb-3"> <div class="list-group online_list mb-3">
@ -24,6 +25,7 @@
{{ end }} {{ end }}
</div> </div>
</div> </div>
{{ end }}
{{end}} {{end}}
{{ if .Messages }} {{ if .Messages }}

View File

@ -230,37 +230,7 @@ let options = {
lineCap: 'butt', lineCap: 'butt',
colors: ["#3aa82d"], colors: ["#3aa82d"],
}, },
series: [ series: [{data: [{}]}],
{
name: "Response Time",
data: [
{
x: "02-10-2017 GMT",
y: 34
},
{
x: "02-11-2017 GMT",
y: 43
},
{
x: "02-12-2017 GMT",
y: 31
},
{
x: "02-13-2017 GMT",
y: 43
},
{
x: "02-14-2017 GMT",
y: 33
},
{
x: "02-15-2017 GMT",
y: 52
}
]
}
],
xaxis: { xaxis: {
type: "datetime", type: "datetime",
tickAmount: 8, tickAmount: 8,
@ -294,7 +264,7 @@ var heat_options = {
enableShades: true, enableShades: true,
shadeIntensity: 0.5, shadeIntensity: 0.5,
colors: ["#d53a3b"], colors: ["#d53a3b"],
series: [{}], series: [{data: [{}]}],
yaxis: { yaxis: {
labels: { labels: {
formatter: (value) => { formatter: (value) => {

View File

@ -49,10 +49,10 @@
<th scope="col"></th> <th scope="col"></th>
</tr> </tr>
</thead> </thead>
<tbody id="groups_table"> <tbody class="sortable_groups" id="groups_table">
{{range Groups false}} {{range Groups false}}
<tr id="group_{{.Id}}" data-id="{{.Id}}"> <tr id="group_{{.Id}}" data-id="{{.Id}}">
<td>{{.Name}}</td> <td><span class="drag_icon d-none d-md-inline"><i class="fas fa-bars"></i></span>{{.Name}}</td>
<td>{{len .Services}}</td> <td>{{len .Services}}</td>
<td>{{if .Public.Bool}}<span class="badge badge-primary">PUBLIC</span>{{else}}<span class="badge badge-secondary">PRIVATE</span>{{end}}</td> <td>{{if .Public.Bool}}<span class="badge badge-primary">PUBLIC</span>{{else}}<span class="badge badge-secondary">PRIVATE</span>{{end}}</td>
<td class="text-right"> <td class="text-right">
@ -95,7 +95,31 @@
newOrder.push(o); newOrder.push(o);
}); });
$.ajax({ $.ajax({
url: "/api/reorder", url: "/api/services/reorder",
type: 'POST',
data: JSON.stringify(newOrder),
contentType: "application/json",
dataType: "json",
success: function(data) { }
});
});
sortable('.sortable_groups', {
forcePlaceholderSize: true,
hoverClass: 'sortable_drag',
handle: '.drag_icon'
});
sortable('.sortable_groups')[0].addEventListener('sortupdate', function(e) {
var i = 0;
var newOrder = [];
var dest = e.detail.destination.items;
dest.forEach(function(d) {
i++;
var dId = $(d).attr('data-id');
var o = {group: parseInt(dId), order: i};
newOrder.push(o);
});
$.ajax({
url: "/api/groups/reorder",
type: 'POST', type: 'POST',
data: JSON.stringify(newOrder), data: JSON.stringify(newOrder),
contentType: "application/json", contentType: "application/json",

File diff suppressed because one or more lines are too long

View File

@ -7,6 +7,7 @@ type Group struct {
Id int64 `gorm:"primary_key;column:id" json:"id"` Id int64 `gorm:"primary_key;column:id" json:"id"`
Name string `gorm:"column:name" json:"name"` Name string `gorm:"column:name" json:"name"`
Public NullBool `gorm:"default:true;column:public" json:"public"` Public NullBool `gorm:"default:true;column:public" json:"public"`
Order int `gorm:"default:0;column:order_id" json:"order_id"`
CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` CreatedAt time.Time `gorm:"column:created_at" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"` UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"`
} }

View File

@ -1 +1 @@
0.80.48 0.80.49