diff --git a/cmd/load.go b/cmd/load.go index 8bf969d..cb01686 100644 --- a/cmd/load.go +++ b/cmd/load.go @@ -102,10 +102,7 @@ to quickly create a Cobra application.`, db = database.New(&data) // Initialise the Gui / Tui - gui := gui.New(db) - if err := gui.Start(); err != nil { - log.Fatalf("main: Cannot start tui: %s", err) - } + gui.Gui(db) } }, diff --git a/go.mod b/go.mod index 7617250..c731ba1 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/charmbracelet/bubbletea v0.12.2 github.com/gdamore/tcell v1.2.0 github.com/gdamore/tcell/v2 v2.0.1-0.20201019142633-1057d5591ed1 + github.com/gizak/termui/v3 v3.1.0 github.com/idlephysicist/cave-logger v1.2.2 github.com/lib/pq v1.8.0 github.com/mitchellh/go-homedir v1.1.0 diff --git a/go.sum b/go.sum index ace5c65..4803403 100644 --- a/go.sum +++ b/go.sum @@ -55,6 +55,9 @@ github.com/gdamore/tcell/v2 v2.0.1-0.20201017141208-acf90d56d591/go.mod h1:vSVL/ github.com/gdamore/tcell/v2 v2.0.1-0.20201019142633-1057d5591ed1 h1:gp9ujdOQmQf1gMvqOYYgxdMS5tRpRGE3HAgRH4Hgzd4= github.com/gdamore/tcell/v2 v2.0.1-0.20201019142633-1057d5591ed1/go.mod h1:vSVL/GV5mCSlPC6thFP5kfOFdM9MGZcalipmpTxTgQA= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gizak/termui v3.1.0+incompatible h1:N3CFm+j087lanTxPpHOmQs0uS3s5I9TxoAFy6DqPqv8= +github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc= +github.com/gizak/termui/v3 v3.1.0/go.mod h1:bXQEBkJpzxUAKf0+xq9MSWAvWZlE7c+aidmyFlkYTrY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= @@ -131,6 +134,7 @@ github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaO github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.7/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= @@ -141,6 +145,8 @@ github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceT github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7 h1:DpOJ2HYzCv8LZP15IdmG+YdwD2luVPHITV96TkirNBM= +github.com/mitchellh/go-wordwrap v0.0.0-20150314170334-ad45545899c7/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= @@ -151,6 +157,8 @@ github.com/muesli/termenv v0.7.2/go.mod h1:ct2L5N2lmix82RaY3bMWwVu/jUFc9Ule0KGDC github.com/muesli/termenv v0.7.4 h1:/pBqvU5CpkY53tU0vVn+xgs2ZTX63aH5nY+SSps5Xa8= github.com/muesli/termenv v0.7.4/go.mod h1:pZ7qY9l3F7e5xsAOS0zCew2tME+p7bWeBkotCEcIIcc= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d h1:x3S6kxmy49zXVVyhcnrFqxvNVCBPb2KZ9hV2RBdS840= +github.com/nsf/termbox-go v0.0.0-20190121233118-02980233997d/go.mod h1:IuKpRQcYE1Tfu+oAQqaLisqDeXgjyyltCfsaoYN18NQ= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= diff --git a/internal/database/database.go b/internal/database/database.go index 496cb29..efd0479 100644 --- a/internal/database/database.go +++ b/internal/database/database.go @@ -60,7 +60,7 @@ func (db *Database) UserSessions() ([]*model.Session, error) { var sessCount model.Session var l logger.Log err = rows.Scan(&sessCount.Username, &sessCount.Count) - l = logger.Log{"info", logrus.Fields{"session" : sessCount.Count}, " count " + sessCount.Username + " users on host"} + l = logger.Log{"info", logrus.Fields{"session" : sessCount.Count, "users" : sessCount.Username}, " count " + sessCount.Username + " users on host"} logger.Lgr(&l) if err != nil { // handle this error @@ -77,4 +77,27 @@ func (db *Database) UserSessions() ([]*model.Session, error) { return sessions, err +} + +func (db *Database) ConnUse() (*model.Guage, error) { + var i int + var i1 int + var m model.Guage + query := `SELECT sum(numbackends) FROM pg_stat_database;` + q2 := `show max_connections` + err := db.conn.QueryRow(query).Scan(&i) + if err != nil { + // handle this error better than this + panic(err) + } + err2 := db.conn.QueryRow(q2).Scan(&i1) + if err2 != nil { + // handle this error better than this + panic(err) + } + m = model.Guage{i, i1} + return &m, nil + + + } \ No newline at end of file diff --git a/internal/database/query.toml b/internal/database/query.toml index 65fc79d..de59acf 100644 --- a/internal/database/query.toml +++ b/internal/database/query.toml @@ -1,4 +1,13 @@ -[sessions.Current] -columns = 2 -headers = ["users", "count users"] -query = "select usename, count(usename) from pg_stat_activity where usename is not null" \ No newline at end of file +[sessions.Current.table] +headers = ["users", "count sessions"] +query = "select usename, count(usename) from pg_stat_activity where usename is not null" +[sessions.Query.table] +headers = ["query", "username"] +query = "SELECT query ,usename FROM pg_stat_activity where query is not null and usename is not null and state != 'idle';" +[sessions.Limit.guage] +headers = ["used", "total"] +q1 = "SHOW max_connections" +q2 = "SELECT sum(numbackends) FROM pg_stat_database" +[sessions.identification.box] +widget = "box" +q = "SELECT current_database();" \ No newline at end of file diff --git a/internal/gui/gui.go b/internal/gui/gui.go index f18388d..9697d45 100644 --- a/internal/gui/gui.go +++ b/internal/gui/gui.go @@ -1,120 +1,132 @@ package gui import ( - "fmt" + + ui "github.com/gizak/termui/v3" + "github.com/gizak/termui/v3/widgets" "github.com/sirupsen/logrus" + "log" + "shiny-pancake/internal/database" "shiny-pancake/internal/model" "shiny-pancake/logger" - - - "shiny-pancake/internal/database" - "strings" - "github.com/rivo/tview" ) -type resources struct { - ActSess []*model.Session -} - - -type panels struct { - currentPanel int - panel []panel -} - - -type state struct { - panels panels - tabBar *tview.TextView - resources resources - stopChans map[string]chan int -} - -func newState() *state { - return &state{ - stopChans: make(map[string]chan int), - } -} - -type Gui struct { - app *tview.Application - pages *tview.Pages - state *state - db *database.Database -} - -func New(db *database.Database) *Gui { - return &Gui{ - app: tview.NewApplication(), - pages: tview.NewPages(), - state: newState(), - db: db, - } -} - - -func (g *Gui) sessionPanel() *activeSessions { - for _, panel := range g.state.panels.panel { - if panel.name() == `activeSessions` { - return panel.(*activeSessions) +func loadModels(grid *ui.Grid, s *model.Screen){ + _ = grid + // try to force two rows when possible. + // max 3 columns + for _, i := range s.Queries { + l := logger.Log{"info", logrus.Fields{"query" : i}, " query for session"} + logger.Lgr(&l) + switch s.Queries { + case s.Queries } } - return nil + } -// Start start application -func (g *Gui) Start() error { - g.initPanels() - g.startMonitoring() - var l logger.Log - l = logger.Log{"info", logrus.Fields{"init" : "ran"}, "got this far"} - logger.Lgr(&l) - if err := g.app.Run(); err != nil { - g.app.Stop() - return err + +func Gui(db *database.Database) { + if err := ui.Init(); err != nil { + log.Fatalf("failed to initialize termui: %v", err) } + defer ui.Close() + tabpane := widgets.NewTabPane("sessions", "disk", "") + tabpane.SetRect(0, 1, 100, 4) + tabpane.Border = false + var session *model.Screen + session = Session() + grid := ui.NewGrid() + loadModels(grid, session) + //termWidth, termHeight := ui.TerminalDimensions() + //grid.SetRect(0, 0, termWidth, termHeight) + // + //grid.Set( + // ui.NewRow(1.0/2, + // ui.NewCol(1.0/2, tabpane), + // + // ), + //) + // + //ui.Render(grid) + //tickerCount := 1 + //uiEvents := ui.PollEvents() + //ticker := time.NewTicker(time.Second).C + //for { + // select { + // case e := <-uiEvents: + // switch e.ID { + // case "q", "": + // return + // case "": + // payload := e.Payload.(ui.Resize) + // grid.SetRect(0, 0, payload.Width, payload.Height) + // ui.Clear() + // ui.Render(grid) + // } + // case <-ticker: + // if tickerCount == 100 { + // return + // } + // ui.Render(grid) + // tickerCount++ + // } + //} - return nil } -func (g *Gui) Stop() { - g.stopMonitoring() - g.app.Stop() -} - -// Page "definitions" - - - -func (g *Gui) initPanels() { - - g.state.tabBar = newTabBar() - // Page definitions - s := newSessions(g) - - var l logger.Log - l = logger.Log{"info", logrus.Fields{"initPanels" : "ran"}, "got this far"} - logger.Lgr(&l) - - - g.pages.AddPage(`activeSessions`, s, true, true) - fmt.Fprintf(g.state.tabBar, ` ["%d"]%d %s[""] `, 0, 1, strings.Title(s.name())) - - g.state.tabBar.Highlight("0") - - // Panels - - g.state.panels.panel = append(g.state.panels.panel, s) - - layout := tview.NewFlex().SetDirection(tview.FlexRow). - AddItem(g.state.tabBar, 1, 1, false). - AddItem(g.pages, 0, 16, true) - - g.app.SetRoot(layout, true) - g.goTo(`activeSessions`) -} - -func (g *Gui) goTo(page string) { - g.pages.SwitchToPage(page) -} +//func placeholder(){ +// _ = db +// if err := ui.Init(); err != nil { +// log.Fatalf("failed to initialize termui: %v", err) +// } +// defer ui.Close() +// sessions, err := db.UserSessions() +// if err != nil { +// log.Fatal(err) +// } +// table1 := widgets.NewTable() +// table1.Rows = [][]string{ +// []string{"user", "count sessions"}, +// } +// table1.TextAlignment = ui.AlignLeft +// +// for _, s := range sessions { +// var sd []string +// sd = append(sd, s.Username) +// sd = append(sd, strconv.Itoa(s.Count)) +// table1.Rows = append(table1.Rows, sd) +// } +// +// table1.TextStyle = ui.NewStyle(ui.ColorWhite) +// table1.SetRect(0, 0, 60, 10) +// table1.Title = "user sessions" +// connsUsed, err := db.ConnUse() +// if err != nil { +// log.Fatal() +// } +// g3 := widgets.NewGauge() +// g3.Title = "connections used" +// g3.SetRect(0, 11, 50, 14) +// percent := float64(connsUsed.I)/float64(connsUsed.I1) +// l := logger.Log{"info", logrus.Fields{"countOpenConn": strconv.Itoa(connsUsed.I), "countMaxConn": strconv.Itoa(connsUsed.I1) }, "count of conns"} +// logger.Lgr(&l) +// g3.Percent = int(percent) +// g3.BarColor = ui.ColorGreen +// g3.LabelStyle = ui.NewStyle(ui.ColorYellow) +// g3.Label = fmt.Sprintf("%d of %d total", g3.Percent, connsUsed.I1) +// +// ui.Render(table1) +// ui.Render(g3) +// +// +// uiEvents := ui.PollEvents() +// for { +// e := <-uiEvents +// switch e.ID { +// case "q", "": +// return +// } +// } +//} \ No newline at end of file diff --git a/internal/gui/monitoring.go b/internal/gui/monitoring.go deleted file mode 100644 index bfd249e..0000000 --- a/internal/gui/monitoring.go +++ /dev/null @@ -1,18 +0,0 @@ -package gui - -func (g *Gui) startMonitoring() { - stop := make(chan int, 1) - g.state.stopChans["activeSessions"] = stop - go g.sessionPanel().monitoringSessions(g) -} - -func (g *Gui) stopMonitoring() { - g.state.stopChans["activeSessions"] <- 1 - -} - -/*func (g *Gui) updateTask() { - g.app.QueueUpdateDraw(func() { - g.caversPanel().setEntries(g) // REVIEW: Why is this just people ? - }) -}*/ diff --git a/internal/gui/panel.go b/internal/gui/panel.go deleted file mode 100644 index f608ec1..0000000 --- a/internal/gui/panel.go +++ /dev/null @@ -1,10 +0,0 @@ -package gui - -type panel interface { - name() string - entries(*Gui) - setEntries(*Gui) - updateEntries(*Gui) - focus(*Gui) - unfocus() -} diff --git a/internal/gui/sessions.go b/internal/gui/sessions.go deleted file mode 100644 index a040c8d..0000000 --- a/internal/gui/sessions.go +++ /dev/null @@ -1,108 +0,0 @@ -package gui - -import ( - - "github.com/gdamore/tcell/v2" - "github.com/rivo/tview" - "shiny-pancake/internal/model" - "time" -) - - - -type activeSessions struct { - *tview.Table - sn chan *model.Session - filterCol, filterTerm string -} - - -func newSessions(g *Gui) *activeSessions { - sess := &activeSessions{ - Table: tview.NewTable(), - sn: make(chan *model.Session), - } - - sess.SetBorder(true) - sess.entries(g) - return sess -} - -func (t *activeSessions) name() string { - return `activeSessions` -} - - - -func (t *activeSessions) entries(g *Gui) { - sss, err := g.db.UserSessions() - if err != nil { - return - } - - g.state.resources.ActSess = sss -} - -func (t *activeSessions) setEntries(g *Gui) { - t.entries(g) - table := t.Clear() - - headers := []string{ - "user", - "session count", - } - - for i, header := range headers { - table.SetCell(0, i, &tview.TableCell{ - Text: header, - NotSelectable: true, - Align: tview.AlignLeft, - Color: tview.Styles.PrimaryTextColor, - BackgroundColor: tview.Styles.PrimitiveBackgroundColor, - Attributes: tcell.AttrBold, - }) - } - - for i, s := range g.state.resources.ActSess { - table.SetCell(i+1, 0, tview.NewTableCell(s.Username). - SetTextColor(tview.Styles.PrimaryTextColor). - SetMaxWidth(30). - SetExpansion(1)) - - table.SetCell(i+1, 1, tview.NewTableCell(string(s.Count)). - SetTextColor(tview.Styles.PrimaryTextColor). - SetMaxWidth(30). - SetExpansion(1)) - - } -} - -func (t *activeSessions) updateEntries(g *Gui) { - g.app.QueueUpdateDraw(func() { - t.setEntries(g) - }) -} - -func (t *activeSessions) focus(g *Gui) { - t.SetSelectable(true, false) - g.app.SetFocus(t) -} - -func (t *activeSessions) unfocus() { - t.SetSelectable(false, false) -} - -func (t *activeSessions) monitoringSessions(g *Gui) { - ticker := time.NewTicker(5 * time.Minute) - -LOOP: - for { - select { - case <-ticker.C: - t.updateEntries(g) - case <-g.state.stopChans["trips"]: - ticker.Stop() - break LOOP - } - } -} diff --git a/internal/gui/state.go b/internal/gui/state.go deleted file mode 100644 index 194757a..0000000 --- a/internal/gui/state.go +++ /dev/null @@ -1 +0,0 @@ -package gui diff --git a/internal/gui/tabbar.go b/internal/gui/tabbar.go deleted file mode 100644 index 87fa0ea..0000000 --- a/internal/gui/tabbar.go +++ /dev/null @@ -1,15 +0,0 @@ -package gui - -import ( - "github.com/rivo/tview" -) - -func newTabBar() *tview.TextView { - return tview.NewTextView(). - SetDynamicColors(true). - SetRegions(true). - SetWrap(false)/*. - SetHighlightedFunc(func(added, removed, remaining []string) { - g.pages.SwitchToPage(added[0]) - })*/ -} diff --git a/internal/gui/tabs.go b/internal/gui/tabs.go new file mode 100644 index 0000000..d3ee4a5 --- /dev/null +++ b/internal/gui/tabs.go @@ -0,0 +1,14 @@ +package gui + +import "shiny-pancake/internal/model" + + + +func Session() *model.Screen{ + var sessions model.Screen + sessions = model.Screen{ + 4, + []string{"sessions.Limit", "sessions.Query", "sessions.Current", "sessions.identification"}, + } + return &sessions +} \ No newline at end of file diff --git a/internal/model/forms.go b/internal/model/forms.go index fb0943a..c073440 100644 --- a/internal/model/forms.go +++ b/internal/model/forms.go @@ -11,3 +11,31 @@ type Session struct { Username string Count int } + +type Guage struct { + I int + I1 int +} + +type Table struct { + Headers []string + Q string + Rows [][]string +} + +type TextBox struct { + Q string + Content []string +} + +type Screen struct { + Count int + Queries []string +} + +type Identification struct { + Dbname string + PGVersion string + DbQuery string + VersionQuery string +} \ No newline at end of file diff --git a/main b/main index 29181fd..a439c8e 100755 Binary files a/main and b/main differ