Commit 19d3d1be authored by Eric's avatar Eric

[Update] add pagination for interactivehandler

parent 6cbe3f88
package handler
import (
"fmt"
"strconv"
"strings"
"github.com/gliderlabs/ssh"
"github.com/olekukonko/tablewriter"
"golang.org/x/crypto/ssh/terminal"
"cocogo/pkg/config"
"cocogo/pkg/logger"
"cocogo/pkg/model"
"cocogo/pkg/utils"
)
const (
IDColumnMinSize = 4
HostNameColumnMaxSize = 15
IPColumnSize = 16
CommentColumnMinSize = 2
)
type AssetPagination struct {
term *terminal.Terminal
winChan <-chan ssh.Window
TermWidth int
TermHeight int
CurrentPage int
TotalPage int
PageSize int
TotalNumber int
Data []model.Asset
dataBulk [][]string
columnSize [5]int
}
func (p *AssetPagination) Initial() {
var (
pageSize int
totalPage int
)
switch config.Conf.AssetListPageSize {
case "auto":
pageSize = p.TermHeight - 7
case "all":
pageSize = p.TotalNumber
default:
if value, err := strconv.Atoi(config.Conf.AssetListPageSize); err == nil {
pageSize = value
} else {
pageSize = p.TermHeight - 7
}
}
if pageSize <= 0 {
pageSize = 1
}
if p.TotalNumber%pageSize == 0 {
totalPage = p.TotalNumber / pageSize
} else {
totalPage = p.TotalNumber/pageSize + 1
}
p.CurrentPage = 1
p.PageSize = pageSize
p.TotalPage = totalPage
p.dataBulk = make([][]string, 0)
}
func (p *AssetPagination) watchWinSizeChange(done <-chan struct{}) {
for {
select {
case <-done:
logger.Debug("Pagination watch win size done")
return
case win, ok := <-p.winChan:
if !ok {
return
}
p.TermHeight, p.TermWidth = win.Height, win.Width
logger.Debugf("Term window size change: %d*%d", win.Height, win.Width)
_ = p.term.SetSize(win.Width, win.Height)
}
}
}
func (p *AssetPagination) setPageSize() {
if config.Conf.AssetListPageSize == "auto" {
var pageSize int
remainSize := p.TermHeight - 7
if remainSize > 0 {
pageSize = remainSize
} else {
pageSize = 1
}
if p.PageSize != pageSize {
p.PageSize = pageSize
p.CurrentPage = 1
if p.TotalNumber%pageSize == 0 {
p.TotalPage = p.TotalNumber / pageSize
} else {
p.TotalPage = p.TotalNumber/pageSize + 1
}
}
}
}
func (p *AssetPagination) getColumnMaxSize() {
var (
IDSize int
HostNameSize int
systemUserSize int
CommentSize int
)
p.setPageSize()
IDSize = IDColumnMinSize
CommentSize = CommentColumnMinSize
endIndex := p.CurrentPage * p.PageSize
startIndex := endIndex - p.PageSize
if endIndex > len(p.Data) {
endIndex = len(p.Data)
}
if len(strconv.Itoa(endIndex)) > IDColumnMinSize {
IDSize = len(strconv.Itoa(endIndex))
}
p.dataBulk = p.dataBulk[:0]
for i, item := range p.Data[startIndex:endIndex] {
tmpDat := make([]string, 5)
var tmpSystemUserArray []string
result := selectHighestPrioritySystemUsers(item.SystemUsers)
tmpSystemUserArray = make([]string, len(result))
for index, sysUser := range result {
tmpSystemUserArray[index] = sysUser.Name
}
tmpSystemUserStr := fmt.Sprintf("[%s]", strings.Join(tmpSystemUserArray, ","))
if len(tmpSystemUserStr) > systemUserSize {
systemUserSize = len(tmpSystemUserStr)
}
if len(item.Hostname) >= HostNameColumnMaxSize {
HostNameSize = HostNameColumnMaxSize
tmpDat[1] = item.Hostname[:HostNameColumnMaxSize]
} else if len(item.Hostname) < HostNameColumnMaxSize && len(item.Hostname) > HostNameSize {
HostNameSize = len(item.Hostname)
tmpDat[1] = item.Hostname
} else {
tmpDat[1] = item.Hostname
}
if len(item.Comment) > CommentSize {
CommentSize = len(item.Comment)
}
tmpDat[0] = strconv.Itoa(startIndex + i + 1)
tmpDat[2] = item.Ip
tmpDat[3] = tmpSystemUserStr
p.dataBulk = append(p.dataBulk, tmpDat)
}
// table writer 空白空间占用宽度 4 + (columnNum - 1) * 4
remainSize := p.TermWidth - 16 - IDSize - HostNameSize - IPColumnSize - systemUserSize
if remainSize > 0 && CommentSize < remainSize {
CommentSize = remainSize
}
for i, item := range p.Data[startIndex:endIndex] {
if len(item.Comment) > CommentSize {
p.dataBulk[i][4] = item.Comment[:CommentSize]
} else {
p.dataBulk[i][4] = item.Comment
}
}
p.columnSize = [5]int{IDSize, HostNameSize, IPColumnSize, systemUserSize, CommentSize}
fmt.Println(p.columnSize)
}
func (p *AssetPagination) PaginationState() []model.Asset {
done := make(chan struct{})
go p.watchWinSizeChange(done)
defer close(done)
if p.PageSize > p.TotalNumber {
p.displayAssets()
return []model.Asset{}
}
for {
p.displayAssets()
p.displayTipsInfo()
line, err := p.term.ReadLine()
if err != nil {
return []model.Asset{}
}
line = strings.TrimSpace(line)
switch len(line) {
case 0, 1:
switch strings.ToLower(line) {
case "p":
p.CurrentPage--
if p.CurrentPage <= 0 {
p.CurrentPage = 1
}
continue
case "", "n":
p.CurrentPage++
if p.CurrentPage >= p.TotalPage {
p.CurrentPage = p.TotalPage
}
continue
case "b":
return []model.Asset{}
}
}
if indexID, err := strconv.Atoi(line); err == nil {
if indexID > 0 && indexID <= p.TotalNumber {
return []model.Asset{p.Data[indexID-1]}
}
}
}
}
func (p *AssetPagination) displayAssets() {
p.getColumnMaxSize()
_, _ = p.term.Write([]byte(utils.CharClear))
table := tablewriter.NewWriter(p.term)
table.SetHeader([]string{"ID", "Hostname", "IP", "LoginAs", "Comment"})
table.AppendBulk(p.dataBulk)
table.SetBorder(false)
greens := tablewriter.Colors{tablewriter.Normal, tablewriter.FgGreenColor}
table.SetHeaderColor(greens, greens, greens, greens, greens)
table.SetHeaderAlignment(tablewriter.ALIGN_LEFT)
table.SetAlignment(tablewriter.ALIGN_LEFT)
for i, value := range p.columnSize {
table.SetColMinWidth(i, value)
}
currentCapMsg := fmt.Sprintf("Page: %d, Count: %d, Total Page: %d, Total Count:%d\n",
p.CurrentPage, p.PageSize, p.TotalPage, p.TotalNumber)
table.SetCaption(true, utils.WrapperString(currentCapMsg, utils.Green))
table.Render()
}
func (p *AssetPagination) displayTipsInfo() {
tips := []string{
"\nTips: Enter the asset ID and log directly into the asset.\n",
"\nPage up: P/p Page down: Enter|N/n BACK: b.\n",
}
for _, tip := range tips {
_, _ = p.term.Write([]byte(tip))
}
}
...@@ -22,11 +22,12 @@ import ( ...@@ -22,11 +22,12 @@ import (
) )
func SessionHandler(sess ssh.Session) { func SessionHandler(sess ssh.Session) {
pty, _, ok := sess.Pty() pty, winCH, ok := sess.Pty()
if ok { if ok {
ctx, cancel := cctx.NewContext(sess) ctx, cancel := cctx.NewContext(sess)
defer cancel() defer cancel()
handler := newInteractiveHandler(sess, ctx.User()) handler := newInteractiveHandler(sess, ctx.User(), winCH)
handler.termHeight, handler.termWidth = pty.Window.Height, pty.Window.Width
logger.Debugf("User Request pty: %s %s", sess.User(), pty.Term) logger.Debugf("User Request pty: %s %s", sess.User(), pty.Term)
handler.Dispatch(ctx) handler.Dispatch(ctx)
} else { } else {
...@@ -35,17 +36,18 @@ func SessionHandler(sess ssh.Session) { ...@@ -35,17 +36,18 @@ func SessionHandler(sess ssh.Session) {
} }
} }
func newInteractiveHandler(sess ssh.Session, user *model.User) *interactiveHandler { func newInteractiveHandler(sess ssh.Session, user *model.User, winCh <-chan ssh.Window) *interactiveHandler {
term := terminal.NewTerminal(sess, "Opt> ") term := terminal.NewTerminal(sess, "Opt> ")
handler := &interactiveHandler{sess: sess, user: user, term: term} handler := &interactiveHandler{sess: sess, user: user, term: term, winChan: winCh}
handler.Initial() handler.Initial()
return handler return handler
} }
type interactiveHandler struct { type interactiveHandler struct {
sess ssh.Session sess ssh.Session
user *model.User user *model.User
term *terminal.Terminal term *terminal.Terminal
winChan <-chan ssh.Window
assetSelect *model.Asset assetSelect *model.Asset
systemUserSelect *model.SystemUser systemUserSelect *model.SystemUser
...@@ -53,6 +55,9 @@ type interactiveHandler struct { ...@@ -53,6 +55,9 @@ type interactiveHandler struct {
searchResult model.AssetList searchResult model.AssetList
nodes model.NodeList nodes model.NodeList
mu *sync.RWMutex mu *sync.RWMutex
termWidth int
termHeight int
} }
func (h *interactiveHandler) Initial() { func (h *interactiveHandler) Initial() {
...@@ -66,16 +71,17 @@ func (h *interactiveHandler) displayBanner() { ...@@ -66,16 +71,17 @@ func (h *interactiveHandler) displayBanner() {
displayBanner(h.sess, h.user.Name) displayBanner(h.sess, h.user.Name)
} }
func (h *interactiveHandler) watchWinSizeChange(winCh <-chan ssh.Window, done <-chan struct{}) { func (h *interactiveHandler) watchWinSizeChange(done <-chan struct{}) {
for { for {
select { select {
case <-done: case <-done:
logger.Debug("Interactive handler watch win size done") logger.Debug("Interactive handler watch win size done")
return return
case win, ok := <-winCh: case win, ok := <-h.winChan:
if !ok { if !ok {
return return
} }
h.termHeight, h.termWidth = win.Height, win.Width
logger.Debugf("Term window size change: %d*%d", win.Height, win.Width) logger.Debugf("Term window size change: %d*%d", win.Height, win.Width)
_ = h.term.SetSize(win.Width, win.Height) _ = h.term.SetSize(win.Width, win.Height)
} }
...@@ -83,10 +89,9 @@ func (h *interactiveHandler) watchWinSizeChange(winCh <-chan ssh.Window, done <- ...@@ -83,10 +89,9 @@ func (h *interactiveHandler) watchWinSizeChange(winCh <-chan ssh.Window, done <-
} }
func (h *interactiveHandler) Dispatch(ctx cctx.Context) { func (h *interactiveHandler) Dispatch(ctx cctx.Context) {
_, winCh, _ := h.sess.Pty()
for { for {
doneChan := make(chan struct{}) doneChan := make(chan struct{})
go h.watchWinSizeChange(winCh, doneChan) go h.watchWinSizeChange(doneChan)
line, err := h.term.ReadLine() line, err := h.term.ReadLine()
close(doneChan) close(doneChan)
...@@ -98,13 +103,12 @@ func (h *interactiveHandler) Dispatch(ctx cctx.Context) { ...@@ -98,13 +103,12 @@ func (h *interactiveHandler) Dispatch(ctx cctx.Context) {
} }
break break
} }
line = strings.TrimSpace(line)
switch len(line) { switch len(line) {
case 0, 1: case 0, 1:
switch strings.ToLower(line) { switch strings.ToLower(line) {
case "", "p": case "", "p":
h.displayAssets(h.assets) h.displayAssets(h.assets)
//h.Proxy(ctx)
case "g": case "g":
h.displayNodes(h.nodes) h.displayNodes(h.nodes)
case "h": case "h":
...@@ -210,19 +214,22 @@ func (h *interactiveHandler) displayAssets(assets model.AssetList) { ...@@ -210,19 +214,22 @@ func (h *interactiveHandler) displayAssets(assets model.AssetList) {
if len(assets) == 0 { if len(assets) == 0 {
_, _ = io.WriteString(h.term, "\r\n No Assets\r\n\r") _, _ = io.WriteString(h.term, "\r\n No Assets\r\n\r")
} else { } else {
table := tablewriter.NewWriter(h.term) pageTerm := terminal.NewTerminal(h.sess, ":")
table.SetHeader([]string{"ID", "Hostname", "IP", "LoginAs", "Comment"}) pag := AssetPagination{
for index, assetItem := range assets { term: pageTerm,
sysUserArray := make([]string, len(assetItem.SystemUsers)) TermWidth: h.termWidth,
for index, sysUser := range assetItem.SystemUsers { TermHeight: h.termHeight,
sysUserArray[index] = sysUser.Name TotalNumber: len(assets),
} winChan: h.winChan,
sysUsers := "[" + strings.Join(sysUserArray, " ") + "]" Data: assets}
table.Append([]string{strconv.Itoa(index + 1), assetItem.Hostname, assetItem.Ip, sysUsers, assetItem.Comment}) pag.Initial()
selectOneAssets := pag.PaginationState()
if len(selectOneAssets) == 1 {
systemUser := h.chooseSystemUser(selectOneAssets[0].SystemUsers)
h.assetSelect = &selectOneAssets[0]
h.systemUserSelect = &systemUser
h.Proxy(context.TODO())
} }
table.SetBorder(false)
table.Render()
} }
} }
...@@ -335,6 +342,22 @@ func isSubstring(sArray []string, substr string) bool { ...@@ -335,6 +342,22 @@ func isSubstring(sArray []string, substr string) bool {
return false return false
} }
func selectHighestPrioritySystemUsers(systemUsers []model.SystemUser) []model.SystemUser {
var result = make([]model.SystemUser, 0)
length := len(systemUsers)
model.SortSystemUserByPriority(systemUsers)
highestPriority := systemUsers[length-1].Priority
result = append(result, systemUsers[length-1])
for i := length - 2; i >= 0; i-- {
if highestPriority == systemUsers[i].Priority {
result = append(result, systemUsers[i])
}
}
return result
}
//func (h *InteractiveHandler) JoinShareRoom(roomID string) { //func (h *InteractiveHandler) JoinShareRoom(roomID string) {
//sshConn := userhome.NewSSHConn(h.sess) //sshConn := userhome.NewSSHConn(h.sess)
//ctx, cancelFuc := context.WithCancel(h.sess.Context()) //ctx, cancelFuc := context.WithCancel(h.sess.Context())
......
...@@ -158,7 +158,7 @@ func (s *systemUserSorter) Less(i, j int) bool { ...@@ -158,7 +158,7 @@ func (s *systemUserSorter) Less(i, j int) bool {
} }
func systemUserPrioritySort(use1, user2 *SystemUser) bool { func systemUserPrioritySort(use1, user2 *SystemUser) bool {
return use1.Priority <= user2.Priority return use1.Priority < user2.Priority
} }
func SortSystemUserByPriority(users []SystemUser) { func SortSystemUserByPriority(users []SystemUser) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment