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)) } }