Commit f2079123 authored by ibuler's avatar ibuler

[Update] 修改分页

parents 4864ccfb 9d0d79f5
package handler
import (
"fmt"
"strconv"
"strings"
"github.com/olekukonko/tablewriter"
"cocogo/pkg/config"
"cocogo/pkg/model"
"cocogo/pkg/utils"
)
const (
IDColumnMinSize = 4
HostNameColumnMaxSize = 15
IPColumnSize = 16
CommentColumnMinSize = 2
)
type AssetPagination struct {
term *utils.Terminal
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
)
_, height := p.term.GetSize()
switch config.Conf.AssetListPageSize {
case "auto":
pageSize = height - 7
case "all":
pageSize = p.TotalNumber
default:
if value, err := strconv.Atoi(config.Conf.AssetListPageSize); err == nil {
pageSize = value
} else {
pageSize = height - 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) setPageSize() {
if config.Conf.AssetListPageSize == "auto" {
var pageSize int
_, height := p.term.GetSize()
remainSize := height - 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
width, _ := p.term.GetSize()
remainSize := width - 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{})
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))
}
}
type Pagination interface {
GetNextPageData() []interface{}
GetPrePageData() []interface{}
GetPageData(p int) []interface{}
CurrentPage() int
TotalCount() int
TotalPage() int
SetPageSize(int)
GetPageSize() int
}
...@@ -27,7 +27,8 @@ func SessionHandler(sess ssh.Session) { ...@@ -27,7 +27,8 @@ func SessionHandler(sess ssh.Session) {
ctx, cancel := cctx.NewContext(sess) ctx, cancel := cctx.NewContext(sess)
defer cancel() defer cancel()
handler := newInteractiveHandler(sess, ctx.User()) handler := newInteractiveHandler(sess, ctx.User())
logger.Debugf("User Request pty %s: %s", pty.Term, sess.User()) handler.termHeight, handler.termWidth = pty.Window.Height, pty.Window.Width
logger.Debugf("User Request pty: %s %s", sess.User(), pty.Term)
handler.Dispatch(ctx) handler.Dispatch(ctx)
} else { } else {
utils.IgnoreErrWriteString(sess, "No PTY requested.\n") utils.IgnoreErrWriteString(sess, "No PTY requested.\n")
...@@ -36,16 +37,17 @@ func SessionHandler(sess ssh.Session) { ...@@ -36,16 +37,17 @@ func SessionHandler(sess ssh.Session) {
} }
func newInteractiveHandler(sess ssh.Session, user *model.User) *interactiveHandler { func newInteractiveHandler(sess ssh.Session, user *model.User) *interactiveHandler {
term := terminal.NewTerminal(sess, "Opt> ") term := utils.NewTerminal(sess, "Opt> ")
handler := &interactiveHandler{sess: sess, user: user, term: term} handler := &interactiveHandler{sess: sess, user: user, term: term}
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 *utils.Terminal
winWatchChan chan bool
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() {
...@@ -60,19 +65,29 @@ func (h *interactiveHandler) Initial() { ...@@ -60,19 +65,29 @@ func (h *interactiveHandler) Initial() {
h.loadUserAssets() h.loadUserAssets()
h.loadUserAssetNodes() h.loadUserAssetNodes()
h.searchResult = h.assets h.searchResult = h.assets
h.winWatchChan = make(chan bool)
} }
func (h *interactiveHandler) displayBanner() { 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() {
_, sessChan, _ := h.sess.Pty()
winChan := sessChan
for { for {
select { select {
case <-done: case sig, ok := <-h.winWatchChan:
logger.Debug("Interactive handler watch win size done") if !ok {
return return
case win, ok := <-winCh: }
switch sig {
case false:
winChan = nil
case true:
winChan = sessChan
}
case win, ok := <-winChan:
if !ok { if !ok {
return return
} }
...@@ -82,13 +97,19 @@ func (h *interactiveHandler) watchWinSizeChange(winCh <-chan ssh.Window, done <- ...@@ -82,13 +97,19 @@ func (h *interactiveHandler) watchWinSizeChange(winCh <-chan ssh.Window, done <-
} }
} }
func (h *interactiveHandler) pauseWatchWinSize() {
h.winWatchChan <- false
}
func (h *interactiveHandler) resumeWatchWinSize() {
h.winWatchChan <- true
}
func (h *interactiveHandler) Dispatch(ctx cctx.Context) { func (h *interactiveHandler) Dispatch(ctx cctx.Context) {
_, winCh, _ := h.sess.Pty() go h.watchWinSizeChange()
for { for {
doneChan := make(chan struct{})
go h.watchWinSizeChange(winCh, doneChan)
line, err := h.term.ReadLine() line, err := h.term.ReadLine()
close(doneChan)
if err != nil { if err != nil {
if err != io.EOF { if err != io.EOF {
...@@ -98,13 +119,12 @@ func (h *interactiveHandler) Dispatch(ctx cctx.Context) { ...@@ -98,13 +119,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 +230,19 @@ func (h *interactiveHandler) displayAssets(assets model.AssetList) { ...@@ -210,19 +230,19 @@ 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) pag := AssetPagination{
table.SetHeader([]string{"ID", "Hostname", "IP", "LoginAs", "Comment"}) term: h.term,
for index, assetItem := range assets { TotalNumber: len(assets),
sysUserArray := make([]string, len(assetItem.SystemUsers)) Data: assets,
for index, sysUser := range assetItem.SystemUsers { }
sysUserArray[index] = sysUser.Name pag.Initial()
} selectOneAssets := pag.PaginationState()
sysUsers := "[" + strings.Join(sysUserArray, " ") + "]" if len(selectOneAssets) == 1 {
table.Append([]string{strconv.Itoa(index + 1), assetItem.Hostname, assetItem.Ip, sysUsers, assetItem.Comment}) systemUser := h.chooseSystemUser(selectOneAssets[0].SystemUsers)
h.assetSelect = &selectOneAssets[0]
h.systemUserSelect = &systemUser
h.Proxy(context.TODO())
} }
table.SetBorder(false)
table.Render()
} }
} }
...@@ -299,7 +319,9 @@ func (h *interactiveHandler) Proxy(ctx context.Context) { ...@@ -299,7 +319,9 @@ func (h *interactiveHandler) Proxy(ctx context.Context) {
Asset: h.assetSelect, Asset: h.assetSelect,
SystemUser: h.systemUserSelect, SystemUser: h.systemUserSelect,
} }
h.pauseWatchWinSize()
p.Proxy() p.Proxy()
h.resumeWatchWinSize()
} }
func ConstructAssetNodeTree(assetNodes []model.Node) treeprint.Tree { func ConstructAssetNodeTree(assetNodes []model.Node) treeprint.Tree {
...@@ -335,6 +357,22 @@ func isSubstring(sArray []string, substr string) bool { ...@@ -335,6 +357,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) {
......
...@@ -42,7 +42,7 @@ type Parser struct { ...@@ -42,7 +42,7 @@ type Parser struct {
userOutputChan chan []byte userOutputChan chan []byte
srvInputChan chan []byte srvInputChan chan []byte
srvOutputChan chan []byte srvOutputChan chan []byte
cmdChan chan [2]string cmdRecordChan chan [2]string
inputInitial bool inputInitial bool
inputPreState bool inputPreState bool
...@@ -76,7 +76,7 @@ func (p *Parser) initial() { ...@@ -76,7 +76,7 @@ func (p *Parser) initial() {
p.userOutputChan = make(chan []byte, 1024) p.userOutputChan = make(chan []byte, 1024)
p.srvInputChan = make(chan []byte, 1024) p.srvInputChan = make(chan []byte, 1024)
p.srvOutputChan = make(chan []byte, 1024) p.srvOutputChan = make(chan []byte, 1024)
p.cmdChan = make(chan [2]string, 1024) p.cmdRecordChan = make(chan [2]string, 1024)
} }
func (p *Parser) Parse() { func (p *Parser) Parse() {
...@@ -124,7 +124,7 @@ func (p *Parser) parseInputState(b []byte) []byte { ...@@ -124,7 +124,7 @@ func (p *Parser) parseInputState(b []byte) []byte {
// 用户又开始输入,并上次不处于输入状态,开始结算上次命令的结果 // 用户又开始输入,并上次不处于输入状态,开始结算上次命令的结果
if !p.inputPreState { if !p.inputPreState {
p.parseCmdOutput() p.parseCmdOutput()
p.cmdChan <- [2]string{p.command, p.output} p.cmdRecordChan <- [2]string{p.command, p.output}
} }
} }
return b return b
...@@ -232,6 +232,6 @@ func (p *Parser) Close() { ...@@ -232,6 +232,6 @@ func (p *Parser) Close() {
close(p.userOutputChan) close(p.userOutputChan)
close(p.srvInputChan) close(p.srvInputChan)
close(p.srvOutputChan) close(p.srvOutputChan)
close(p.cmdChan) close(p.cmdRecordChan)
p.closed = true p.closed = true
} }
...@@ -150,7 +150,7 @@ func (s *SwitchSession) readParserToUser(ctx context.Context) { ...@@ -150,7 +150,7 @@ func (s *SwitchSession) readParserToUser(ctx context.Context) {
} }
func (s *SwitchSession) recordCmd() { func (s *SwitchSession) recordCmd() {
for cmd := range s.cmdRecordChan { for cmd := range s.parser.cmdRecordChan {
s.cmdRecorder.Record(cmd) s.cmdRecorder.Record(cmd)
} }
} }
......
...@@ -890,6 +890,12 @@ func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) { ...@@ -890,6 +890,12 @@ func (t *Terminal) clearAndRepaintLinePlusNPrevious(numPrevLines int) {
t.moveCursorToPos(t.pos) t.moveCursorToPos(t.pos)
} }
func (t *Terminal) GetSize() (width, height int) {
t.lock.Lock()
defer t.lock.Unlock()
return t.termWidth, t.termHeight
}
func (t *Terminal) SetSize(width, height int) error { func (t *Terminal) SetSize(width, height int) error {
t.lock.Lock() t.lock.Lock()
defer t.lock.Unlock() defer t.lock.Unlock()
......
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