Refactor dashboard management

This commit is contained in:
cuigh 2018-03-27 16:32:30 +08:00
parent f4546d0888
commit d5b5ff114a
15 changed files with 160 additions and 99 deletions

View File

@ -1348,6 +1348,13 @@ var Swirl;
this.charts.push(g); this.charts.push(g);
} }
}); });
Core.Dispatcher.bind(this.$panel).on("remove-chart", e => {
let name = $(e.target).closest("div.column").data("chart-name");
Core.Modal.confirm(`Are you sure to delete chart: <strong>${name}</strong>?`, "Delete chart", dlg => {
this.removeGraph(name);
dlg.close();
});
});
$(window).resize(e => { $(window).resize(e => {
$.each(this.charts, (i, g) => { $.each(this.charts, (i, g) => {
g.resize(0, 0); g.resize(0, 0);
@ -1409,7 +1416,7 @@ var Swirl;
this.loadData(); this.loadData();
} }
removeGraph(name) { removeGraph(name) {
let index; let index = -1;
for (let i = 0; i < this.charts.length; i++) { for (let i = 0; i < this.charts.length; i++) {
let c = this.charts[i]; let c = this.charts[i];
if (c.getName() === name) { if (c.getName() === name) {
@ -1417,7 +1424,13 @@ var Swirl;
break; break;
} }
} }
this.loadData(); if (index >= 0) {
console.info(this.charts.length);
let $elem = this.charts[index].getElem();
this.charts.splice(index, 1);
$elem.remove();
console.info(this.charts.length);
}
} }
save() { save() {
let charts = this.charts.map(c => { let charts = this.charts.map(c => {
@ -1432,9 +1445,12 @@ var Swirl;
key: this.opts.key || '', key: this.opts.key || '',
charts: charts, charts: charts,
}; };
$ajax.post(`/system/chart/save_panel`, args).json((r) => { $ajax.post(`/system/chart/save_dashboard`, args).json((r) => {
if (!r.success) { if (r.success) {
Core.Modal.alert(r.message); Core.Notification.show("success", "Successfully saved.");
}
else {
Core.Notification.show("danger", r.message);
} }
}); });
} }
@ -2343,6 +2359,9 @@ var Swirl;
$("#btn-add").click(() => { $("#btn-add").click(() => {
Modal.alert("Coming soon..."); Modal.alert("Coming soon...");
}); });
$("#btn-save").click(() => {
Modal.alert("Coming soon...");
});
$cb_time.change(e => { $cb_time.change(e => {
this.panel.setTime($(e.target).val()); this.panel.setTime($(e.target).val());
}); });

File diff suppressed because one or more lines are too long

View File

@ -320,6 +320,13 @@ namespace Swirl.Core {
} }
}); });
Dispatcher.bind(this.$panel).on("remove-chart", e => {
let name = $(e.target).closest("div.column").data("chart-name");
Modal.confirm(`Are you sure to delete chart: <strong>${name}</strong>?`, "Delete chart", dlg => {
this.removeGraph(name);
dlg.close();
});
});
$(window).resize(e => { $(window).resize(e => {
$.each(this.charts, (i: number, g: Graph) => { $.each(this.charts, (i: number, g: Graph) => {
g.resize(0, 0); g.resize(0, 0);
@ -356,7 +363,7 @@ namespace Swirl.Core {
} }
addGraph(c: any) { addGraph(c: any) {
for (let i =0; i< this.charts.length; i++) { for (let i = 0; i < this.charts.length; i++) {
let chart = this.charts[i]; let chart = this.charts[i];
if (chart.getName() === c.name) { if (chart.getName() === c.name) {
// chart already added. // chart already added.
@ -393,15 +400,22 @@ namespace Swirl.Core {
removeGraph(name: string) { removeGraph(name: string) {
// todo: // todo:
let index:number; let index = -1;
for (let i =0; i< this.charts.length; i++) { for (let i = 0; i < this.charts.length; i++) {
let c = this.charts[i]; let c = this.charts[i];
if (c.getName() === name) { if (c.getName() === name) {
index = i; index = i;
break; break;
} }
} }
this.loadData();
if (index >= 0) {
console.info(this.charts.length);
let $elem = this.charts[index].getElem();
this.charts.splice(index, 1);
$elem.remove();
console.info(this.charts.length);
}
} }
save() { save() {
@ -418,9 +432,11 @@ namespace Swirl.Core {
key: this.opts.key || '', key: this.opts.key || '',
charts: charts, charts: charts,
}; };
$ajax.post(`/system/chart/save_panel`, args).json<AjaxResult>((r: AjaxResult) => { $ajax.post(`/system/chart/save_dashboard`, args).json<AjaxResult>((r: AjaxResult) => {
if (!r.success) { if (r.success) {
Modal.alert(r.message); Notification.show("success", "Successfully saved.");
} else {
Notification.show("danger", r.message);
} }
}); });
} }

View File

@ -21,6 +21,9 @@ namespace Swirl.Service {
$("#btn-add").click(() => { $("#btn-add").click(() => {
Modal.alert("Coming soon..."); Modal.alert("Coming soon...");
}); });
$("#btn-save").click(() => {
Modal.alert("Coming soon...");
});
$cb_time.change(e => { $cb_time.change(e => {
this.panel.setTime($(e.target).val()); this.panel.setTime($(e.target).val());
}); });

View File

@ -99,20 +99,34 @@ func (b *chartBiz) GetServiceCharts(name string) (charts []*model.Chart, err err
return return
} }
// nolint: gocyclo func (b *chartBiz) GetDashboard(name, key string) (dashboard *model.ChartDashboard, err error) {
func (b *chartBiz) Panel(panel model.ChartPanel) (charts []*model.Chart, err error) {
do(func(d dao.Interface) { do(func(d dao.Interface) {
if len(panel.Charts) == 0 { dashboard, err = d.DashboardGet(name, key)
})
return
}
func (b *chartBiz) UpdateDashboard(dashboard *model.ChartDashboard, user web.User) (err error) {
do(func(d dao.Interface) {
err = d.DashboardUpdate(dashboard)
})
return
}
// nolint: gocyclo
func (b *chartBiz) GetDashboardCharts(dashboard *model.ChartDashboard) (charts []*model.Chart, err error) {
do(func(d dao.Interface) {
if len(dashboard.Charts) == 0 {
return return
} }
names := make([]string, len(panel.Charts)) names := make([]string, len(dashboard.Charts))
for i, c := range panel.Charts { for i, c := range dashboard.Charts {
names[i] = c.Name names[i] = c.Name
} }
var cs []*model.Chart var cs []*model.Chart
cs, err = d.ChartBatch(names...) cs, err = b.getCharts(names)
if err != nil { if err != nil {
return return
} }
@ -122,7 +136,7 @@ func (b *chartBiz) Panel(panel model.ChartPanel) (charts []*model.Chart, err err
for _, c := range cs { for _, c := range cs {
m[c.Name] = c m[c.Name] = c
} }
for _, c := range panel.Charts { for _, c := range dashboard.Charts {
if chart := m[c.Name]; chart != nil { if chart := m[c.Name]; chart != nil {
if c.Width > 0 { if c.Width > 0 {
chart.Width = c.Width chart.Width = c.Width

View File

@ -33,13 +33,3 @@ func (b *settingBiz) Update(setting *model.Setting, user web.User) (err error) {
}) })
return return
} }
func (b *settingBiz) UpdateDashboard(name string, dashboard *model.ChartPanel, user web.User) (err error) {
do(func(d dao.Interface) {
err = d.UpdateDashboard(name, dashboard)
if err == nil {
Event.CreateSetting(model.EventActionUpdate, user)
}
})
return
}

View File

@ -4,6 +4,7 @@ import (
"strings" "strings"
"time" "time"
"github.com/cuigh/auxo/data"
"github.com/cuigh/auxo/errors" "github.com/cuigh/auxo/errors"
"github.com/cuigh/auxo/net/web" "github.com/cuigh/auxo/net/web"
"github.com/cuigh/auxo/util/cast" "github.com/cuigh/auxo/util/cast"
@ -13,29 +14,29 @@ import (
// ChartController is a controller of metric chart. // ChartController is a controller of metric chart.
type ChartController struct { type ChartController struct {
List web.HandlerFunc `path:"/" name:"chart.list" authorize:"!" desc:"chart list page"` List web.HandlerFunc `path:"/" name:"chart.list" authorize:"!" desc:"chart list page"`
Query web.HandlerFunc `path:"/query" name:"chart.query" authorize:"?" desc:"chart query"` Query web.HandlerFunc `path:"/query" name:"chart.query" authorize:"?" desc:"chart query"`
New web.HandlerFunc `path:"/new" name:"chart.new" authorize:"!" desc:"new chart page"` New web.HandlerFunc `path:"/new" name:"chart.new" authorize:"!" desc:"new chart page"`
Create web.HandlerFunc `path:"/new" method:"post" name:"chart.create" authorize:"!" desc:"create chart"` Create web.HandlerFunc `path:"/new" method:"post" name:"chart.create" authorize:"!" desc:"create chart"`
Edit web.HandlerFunc `path:"/:name/edit" name:"chart.edit" authorize:"!" desc:"edit chart page"` Edit web.HandlerFunc `path:"/:name/edit" name:"chart.edit" authorize:"!" desc:"edit chart page"`
Delete web.HandlerFunc `path:"/:name/delete" method:"post" name:"chart.delete" authorize:"!" desc:"delete chart"` Delete web.HandlerFunc `path:"/:name/delete" method:"post" name:"chart.delete" authorize:"!" desc:"delete chart"`
Update web.HandlerFunc `path:"/:name/edit" method:"post" name:"chart.update" authorize:"!" desc:"update chart"` Update web.HandlerFunc `path:"/:name/edit" method:"post" name:"chart.update" authorize:"!" desc:"update chart"`
Data web.HandlerFunc `path:"/data" name:"chart.data" authorize:"?" desc:"fetch chart datas"` Data web.HandlerFunc `path:"/data" name:"chart.data" authorize:"?" desc:"fetch chart datas"`
SavePanel web.HandlerFunc `path:"/save_panel" method:"post" name:"chart.save_panel" authorize:"!" desc:"save panel"` SaveDashboard web.HandlerFunc `path:"/save_dashboard" method:"post" name:"chart.save_dashboard" authorize:"!" desc:"save dashboard"`
} }
// Chart creates an instance of RoleController // Chart creates an instance of RoleController
func Chart() (c *ChartController) { func Chart() (c *ChartController) {
return &ChartController{ return &ChartController{
List: chartList, List: chartList,
Query: chartQuery, Query: chartQuery,
New: chartNew, New: chartNew,
Create: chartCreate, Create: chartCreate,
Edit: chartEdit, Edit: chartEdit,
Update: chartUpdate, Update: chartUpdate,
Delete: chartDelete, Delete: chartDelete,
Data: chartData, Data: chartData,
SavePanel: chartSavePanel, SaveDashboard: chartSaveDashboard,
} }
} }
@ -115,31 +116,30 @@ func chartDelete(ctx web.Context) error {
func chartData(ctx web.Context) error { func chartData(ctx web.Context) error {
period := cast.ToDuration(ctx.Q("time"), time.Hour) period := cast.ToDuration(ctx.Q("time"), time.Hour)
charts := strings.Split(ctx.Q("charts"), ",") if v := ctx.Q("charts"); v != "" {
key := ctx.Q("key") names := strings.Split(v, ",")
datas, err := biz.Chart.FetchDatas(key, charts, period) key := ctx.Q("key")
if err != nil { datas, err := biz.Chart.FetchDatas(key, names, period)
return err if err != nil {
return err
}
return ctx.JSON(datas)
} }
return ctx.JSON(datas) return ctx.JSON(data.Map{})
} }
func chartSavePanel(ctx web.Context) error { func chartSaveDashboard(ctx web.Context) error {
data := struct { dashboard := &model.ChartDashboard{}
Name string `json:"name"` err := ctx.Bind(dashboard)
Key string `json:"key"`
model.ChartPanel
}{}
err := ctx.Bind(&data)
if err != nil { if err != nil {
return err return err
} }
switch data.Name { switch dashboard.Name {
case "home": case "home":
err = biz.Setting.UpdateDashboard(data.Name, &data.ChartPanel, ctx.User()) err = biz.Chart.UpdateDashboard(dashboard, ctx.User())
default: default:
err = errors.New("unknown dashboard: " + data.Name) err = errors.New("unknown dashboard: " + dashboard.Name)
} }
return ajaxResult(ctx, err) return ajaxResult(ctx, err)
} }

View File

@ -32,10 +32,10 @@ func Home() (c *HomeController) {
func homeIndex(ctx web.Context) (err error) { func homeIndex(ctx web.Context) (err error) {
var ( var (
count int count int
setting *model.Setting dashboard *model.ChartDashboard
charts []*model.Chart charts []*model.Chart
m = newModel(ctx) m = newModel(ctx)
) )
if count, err = docker.NodeCount(); err != nil { if count, err = docker.NodeCount(); err != nil {
@ -58,10 +58,10 @@ func homeIndex(ctx web.Context) (err error) {
} }
m.Set("StackCount", count) m.Set("StackCount", count)
if setting, err = biz.Setting.Get(); err != nil { if dashboard, err = biz.Chart.GetDashboard("home", ""); err != nil {
return return
} }
charts, err = biz.Chart.Panel(setting.Dashboard.Home) charts, err = biz.Chart.GetDashboardCharts(dashboard)
if err != nil { if err != nil {
return err return err
} }

View File

@ -62,7 +62,6 @@ type Interface interface {
SettingGet() (setting *model.Setting, err error) SettingGet() (setting *model.Setting, err error)
SettingUpdate(setting *model.Setting) error SettingUpdate(setting *model.Setting) error
UpdateDashboard(name string, dashboard *model.ChartPanel) error
ChartGet(name string) (*model.Chart, error) ChartGet(name string) (*model.Chart, error)
ChartBatch(names ...string) ([]*model.Chart, error) ChartBatch(names ...string) ([]*model.Chart, error)
@ -70,6 +69,9 @@ type Interface interface {
ChartCreate(chart *model.Chart) error ChartCreate(chart *model.Chart) error
ChartUpdate(chart *model.Chart) error ChartUpdate(chart *model.Chart) error
ChartDelete(name string) error ChartDelete(name string) error
DashboardGet(name, key string) (dashboard *model.ChartDashboard, err error)
DashboardUpdate(dashboard *model.ChartDashboard) error
} }
// Get return a dao instance according to DB_TYPE. // Get return a dao instance according to DB_TYPE.

View File

@ -63,3 +63,29 @@ func (d *Dao) ChartDelete(name string) (err error) {
}) })
return return
} }
func (d *Dao) DashboardGet(name, key string) (dashboard *model.ChartDashboard, err error) {
d.do(func(db *database) {
dashboard = &model.ChartDashboard{
Name: name,
Key: key,
}
err = db.C("dashboard").FindId(dashboard.ID()).One(dashboard)
if err == mgo.ErrNotFound {
dashboard, err = nil, nil
} else if err != nil {
dashboard = nil
}
})
return
}
func (d *Dao) DashboardUpdate(dashboard *model.ChartDashboard) (err error) {
d.do(func(db *database) {
update := bson.M{
"$set": dashboard,
}
_, err = db.C("dashboard").UpsertId(dashboard.ID(), update)
})
return
}

View File

@ -28,17 +28,3 @@ func (d *Dao) SettingUpdate(setting *model.Setting) (err error) {
}) })
return return
} }
func (d *Dao) UpdateDashboard(name string, dashboard *model.ChartPanel) (err error) {
d.do(func(db *database) {
update := bson.M{
"$set": bson.M{
"dashboard": bson.M{
name: dashboard,
},
},
}
err = db.C("setting").UpdateId(settingID, update)
})
return
}

View File

@ -43,13 +43,20 @@ type ChartItem struct {
Colors []string `json:"colors"` Colors []string `json:"colors"`
} }
type ChartPanel struct { type ChartDashboard struct {
Refresh bool `json:"refresh"` Name string `json:"name"`
Period int32 `json:"period"` // minutes Key string `json:"key"`
Charts []ChartItem `json:"charts"` Period int32 `json:"period"` // minutes
RefreshInterval int32 `json:"refresh_interval"` // seconds, 0 means disabled.
Charts []ChartItem `json:"charts"`
} }
//type ChartPanel []ChartItem func (cd *ChartDashboard) ID() string {
if cd.Key == "" {
return cd.Name
}
return cd.Name + ":" + cd.Key
}
type ChartPoint struct { type ChartPoint struct {
X int64 `json:"x"` X int64 `json:"x"`

View File

@ -47,9 +47,6 @@ type Setting struct {
Metrics struct { Metrics struct {
Prometheus string `bson:"prometheus" json:"prometheus"` Prometheus string `bson:"prometheus" json:"prometheus"`
} `bson:"metrics" json:"metrics"` } `bson:"metrics" json:"metrics"`
Dashboard struct {
Home ChartPanel `bson:"home" json:"home"`
} `bson:"dashboard" json:"dashboard"`
UpdatedBy string `bson:"updated_by" json:"updated_by,omitempty"` UpdatedBy string `bson:"updated_by" json:"updated_by,omitempty"`
UpdatedAt time.Time `bson:"updated_at" json:"updated_at,omitempty"` UpdatedAt time.Time `bson:"updated_at" json:"updated_at,omitempty"`
} }

View File

@ -230,6 +230,7 @@ var Perms = []PermGroup{
{Key: "chart.create", Text: "Create"}, {Key: "chart.create", Text: "Create"},
{Key: "chart.delete", Text: "Delete"}, {Key: "chart.delete", Text: "Delete"},
{Key: "chart.update", Text: "Update"}, {Key: "chart.update", Text: "Update"},
{Key: "chart.save_dashboard", Text: "Save dashboard"},
}, },
}, },
} }

View File

@ -87,16 +87,16 @@
<div class="card"> <div class="card">
<header class="card-header"> <header class="card-header">
<p class="card-header-title">{{ .Title }}</p> <p class="card-header-title">{{ .Title }}</p>
<a data-action="remove-chart" class="card-header-icon is-paddingless" aria-label="remove chart"> <a data-action="remove-chart" class="card-header-icon" aria-label="remove chart">
<span class="icon"> <span class="icon">
<i class="fas fa-times has-text-danger" aria-hidden="true"></i> <i class="fas fa-times has-text-danger" aria-hidden="true"></i>
</span> </span>
</a> </a>
<a data-action="edit-options" class="card-header-icon" aria-label="edit options"> {*<a data-action="edit-options" class="card-header-icon" aria-label="edit options">*}
<span class="icon"> {*<span class="icon">*}
<i class="fas fa-ellipsis-h has-text-info" aria-hidden="true"></i> {*<i class="fas fa-ellipsis-h has-text-info" aria-hidden="true"></i>*}
</span> {*</span>*}
</a> {*</a>*}
</header> </header>
<div class="card-content"> <div class="card-content">
<div style="height: {{ .Height }}px"> <div style="height: {{ .Height }}px">