Unverified Commit cc9ddb58 authored by Eric_Lee's avatar Eric_Lee Committed by GitHub

Page (#148)

* [update] change asset pagination display

* [Update] add multiple searching
parent 1d3223b1
...@@ -17,11 +17,6 @@ msgstr "" ...@@ -17,11 +17,6 @@ msgstr ""
msgid "Welcome to use Jumpserver open source fortress system" msgid "Welcome to use Jumpserver open source fortress system"
msgstr "" msgstr ""
#. i18n.T
#: pkg/handler/banner.go:48
msgid "directly login"
msgstr ""
#. i18n.T #. i18n.T
#: pkg/handler/banner.go:49 #: pkg/handler/banner.go:49
msgid "part IP, Hostname, Comment" msgid "part IP, Hostname, Comment"
...@@ -99,16 +94,12 @@ msgstr "" ...@@ -99,16 +94,12 @@ msgstr ""
#. i18n.T #. i18n.T
#: pkg/handler/banner.go:96 #: pkg/handler/banner.go:96
msgid "" msgid "Enter ID number directly login the asset."
"\n"
"Tips: Enter the asset ID and directly login the asset.\n"
msgstr "" msgstr ""
#. i18n.T #. i18n.T
#: pkg/handler/banner.go:97 #: pkg/handler/banner.go:97
msgid "" msgid "Page up: b\tPage down: Enter|n"
"\n"
"Page up: P/p\tPage down: Enter|N/n\tBACK: b.\n"
msgstr "" msgstr ""
#. i18n.T #. i18n.T
...@@ -147,7 +138,17 @@ msgid "Username" ...@@ -147,7 +138,17 @@ msgid "Username"
msgstr "" msgstr ""
#. i18n.T #. i18n.T
#: pkg/proxy/parser.go:131 #: pkg/handler/banner.go:105
msgid "all"
msgstr ""
#. i18n.T
#: pkg/handler/banner.go:106
msgid "Search: %s"
msgstr ""
#. i18n.T
#: pkg/proxy/parser.go:130
msgid "Command `%s` is forbidden" msgid "Command `%s` is forbidden"
msgstr "" msgstr ""
...@@ -178,11 +179,11 @@ msgid "Connect with api server failed" ...@@ -178,11 +179,11 @@ msgid "Connect with api server failed"
msgstr "" msgstr ""
#. i18n.T #. i18n.T
#: pkg/proxy/switch.go:159 #: pkg/proxy/switch.go:168
msgid "Connect idle more than %d minutes, disconnect" msgid "Connect idle more than %d minutes, disconnect"
msgstr "" msgstr ""
#. i18n.T #. i18n.T
#: pkg/proxy/switch.go:166 #: pkg/proxy/switch.go:175
msgid "Terminated by administrator" msgid "Terminated by administrator"
msgstr "" msgstr ""
...@@ -17,11 +17,6 @@ msgstr "\t%d) 输入 {{.GreenBoldColor}}%s{{.ColorEnd}} 进行%s.%s" ...@@ -17,11 +17,6 @@ msgstr "\t%d) 输入 {{.GreenBoldColor}}%s{{.ColorEnd}} 进行%s.%s"
msgid "Welcome to use Jumpserver open source fortress system" msgid "Welcome to use Jumpserver open source fortress system"
msgstr "欢迎使用Jumpserver开源堡垒机系统" msgstr "欢迎使用Jumpserver开源堡垒机系统"
#. i18n.T
#: pkg/handler/banner.go:48
msgid "directly login"
msgstr "直接登录"
#. i18n.T #. i18n.T
#: pkg/handler/banner.go:49 #: pkg/handler/banner.go:49
msgid "part IP, Hostname, Comment" msgid "part IP, Hostname, Comment"
...@@ -29,6 +24,7 @@ msgstr "部分IP、主机名、备注" ...@@ -29,6 +24,7 @@ msgstr "部分IP、主机名、备注"
#. i18n.T #. i18n.T
#: pkg/handler/banner.go:49 #: pkg/handler/banner.go:49
#, fuzzy
msgid "to search login if unique" msgid "to search login if unique"
msgstr "搜索登录(如果唯一)" msgstr "搜索登录(如果唯一)"
...@@ -39,6 +35,7 @@ msgstr "/ + IP,主机名 or 备注" ...@@ -39,6 +35,7 @@ msgstr "/ + IP,主机名 or 备注"
#. i18n.T #. i18n.T
#: pkg/handler/banner.go:50 #: pkg/handler/banner.go:50
#, fuzzy
msgid "to search, such as: /192.168" msgid "to search, such as: /192.168"
msgstr "搜索,如:/192.168" msgstr "搜索,如:/192.168"
...@@ -100,21 +97,14 @@ msgstr "没有资产" ...@@ -100,21 +97,14 @@ msgstr "没有资产"
#. i18n.T #. i18n.T
#: pkg/handler/banner.go:96 #: pkg/handler/banner.go:96
#, fuzzy #, fuzzy
msgid "" msgid "Enter ID number directly login the asset."
"\n" msgstr "提示:输入资产ID,登录资产"
"Tips: Enter the asset ID and directly login the asset.\n"
msgstr ""
"\n"
"提示:输入资产ID,登录资产\n"
#. i18n.T #. i18n.T
#: pkg/handler/banner.go:97 #: pkg/handler/banner.go:97
msgid "" #, fuzzy
"\n" msgid "Page up: b\tPage down: Enter|n"
"Page up: P/p\tPage down: Enter|N/n\tBACK: b.\n" msgstr "上一页:b 下一页:n"
msgstr ""
"\n"
"上一页:P/p 下一页:Enter|N/n 返回:B/b\n"
#. i18n.T #. i18n.T
#: pkg/handler/banner.go:98 #: pkg/handler/banner.go:98
...@@ -156,7 +146,18 @@ msgid "Username" ...@@ -156,7 +146,18 @@ msgid "Username"
msgstr "用户名" msgstr "用户名"
#. i18n.T #. i18n.T
#: pkg/proxy/parser.go:131 #: pkg/handler/banner.go:105
msgid "all"
msgstr "所有"
#. i18n.T
#: pkg/handler/banner.go:106
#, fuzzy
msgid "Search: %s"
msgstr "搜索: %s"
#. i18n.T
#: pkg/proxy/parser.go:130
msgid "Command `%s` is forbidden" msgid "Command `%s` is forbidden"
msgstr "命令 `%s` 是被禁止的 ..." msgstr "命令 `%s` 是被禁止的 ..."
...@@ -187,11 +188,11 @@ msgid "Connect with api server failed" ...@@ -187,11 +188,11 @@ msgid "Connect with api server failed"
msgstr "连接API服务失败" msgstr "连接API服务失败"
#. i18n.T #. i18n.T
#: pkg/proxy/switch.go:159 #: pkg/proxy/switch.go:168
msgid "Connect idle more than %d minutes, disconnect" msgid "Connect idle more than %d minutes, disconnect"
msgstr "空闲时间超过%d分钟,断开连接" msgstr "空闲时间超过%d分钟,断开连接"
#. i18n.T #. i18n.T
#: pkg/proxy/switch.go:166 #: pkg/proxy/switch.go:175
msgid "Terminated by administrator" msgid "Terminated by administrator"
msgstr "管理员中断连接" msgstr "管理员中断连接"
...@@ -83,15 +83,16 @@ func (c *Client) parseUrlQuery(url string, params []map[string]string) string { ...@@ -83,15 +83,16 @@ func (c *Client) parseUrlQuery(url string, params []map[string]string) string {
if len(params) < 1 { if len(params) < 1 {
return url return url
} }
var query []string query := neturl.Values{}
for k, v := range params[0] { for _, item := range params {
query = append(query, fmt.Sprintf("%s=%s", k, v)) for k, v := range item {
query.Add(k, v)
}
} }
param := strings.Join(query, "&")
if strings.Contains(url, "?") { if strings.Contains(url, "?") {
url += "&" + param url += "&" + query.Encode()
} else { } else {
url += "?" + param url += "?" + query.Encode()
} }
return url return url
} }
...@@ -159,6 +160,9 @@ func (c *Client) NewRequest(method, url string, body interface{}, params []map[s ...@@ -159,6 +160,9 @@ func (c *Client) NewRequest(method, url string, body interface{}, params []map[s
// 1. query string if set {"name": "ibuler"} // 1. query string if set {"name": "ibuler"}
func (c *Client) Do(method, url string, data, res interface{}, params ...map[string]string) (resp *http.Response, err error) { func (c *Client) Do(method, url string, data, res interface{}, params ...map[string]string) (resp *http.Response, err error) {
req, err := c.NewRequest(method, url, data, params) req, err := c.NewRequest(method, url, data, params)
if err != nil {
return
}
resp, err = c.http.Do(req) resp, err = c.http.Do(req)
if err != nil { if err != nil {
return return
......
This diff is collapsed.
...@@ -45,14 +45,13 @@ type Menu []MenuItem ...@@ -45,14 +45,13 @@ type Menu []MenuItem
func Initial() { func Initial() {
defaultTitle = utils.WrapperTitle(i18n.T("Welcome to use Jumpserver open source fortress system")) defaultTitle = utils.WrapperTitle(i18n.T("Welcome to use Jumpserver open source fortress system"))
menu = Menu{ menu = Menu{
{id: 1, instruct: "ID", helpText: i18n.T("directly login")}, {id: 1, instruct: i18n.T("part IP, Hostname, Comment"), helpText: i18n.T("to search login if unique")},
{id: 2, instruct: i18n.T("part IP, Hostname, Comment"), helpText: i18n.T("to search login if unique")}, {id: 2, instruct: i18n.T("/ + IP, Hostname, Comment"), helpText: i18n.T("to search, such as: /192.168")},
{id: 3, instruct: i18n.T("/ + IP, Hostname, Comment"), helpText: i18n.T("to search, such as: /192.168")}, {id: 3, instruct: "p", helpText: i18n.T("display the host you have permission")},
{id: 4, instruct: "p", helpText: i18n.T("display the host you have permission")}, {id: 4, instruct: "g", helpText: i18n.T("display the node that you have permission")},
{id: 5, instruct: "g", helpText: i18n.T("display the node that you have permission")}, {id: 5, instruct: "r", helpText: i18n.T("refresh your assets and nodes")},
{id: 6, instruct: "r", helpText: i18n.T("refresh your assets and nodes")}, {id: 6, instruct: "h", helpText: i18n.T("print help")},
{id: 7, instruct: "h", helpText: i18n.T("print help")}, {id: 7, instruct: "q", helpText: i18n.T("exit")},
{id: 8, instruct: "q", helpText: i18n.T("exit")},
} }
} }
...@@ -93,8 +92,8 @@ func getI18nFromMap(name string) string { ...@@ -93,8 +92,8 @@ func getI18nFromMap(name string) string {
"Comment": i18n.T("comment"), "Comment": i18n.T("comment"),
"AssetTableCaption": i18n.T("Page: %d, Count: %d, Total Page: %d, Total Count: %d"), "AssetTableCaption": i18n.T("Page: %d, Count: %d, Total Page: %d, Total Count: %d"),
"NoAssets": i18n.T("No Assets"), "NoAssets": i18n.T("No Assets"),
"LoginTip": i18n.T("\nTips: Enter the asset ID and directly login the asset.\n"), "LoginTip": i18n.T("Enter ID number directly login the asset."),
"PageActionTip": i18n.T("\nPage up: P/p Page down: Enter|N/n BACK: b.\n"), "PageActionTip": i18n.T("Page up: b Page down: Enter|n"),
"NodeHeaderTip": i18n.T("Node: [ ID.Name(Asset amount) ]"), "NodeHeaderTip": i18n.T("Node: [ ID.Name(Asset amount) ]"),
"NodeEndTip": i18n.T("Tips: Enter g+NodeID to display the host under the node, such as g1"), "NodeEndTip": i18n.T("Tips: Enter g+NodeID to display the host under the node, such as g1"),
"RefreshDone": i18n.T("Refresh done"), "RefreshDone": i18n.T("Refresh done"),
...@@ -102,6 +101,8 @@ func getI18nFromMap(name string) string { ...@@ -102,6 +101,8 @@ func getI18nFromMap(name string) string {
"BackTip": i18n.T("Back: B/b"), "BackTip": i18n.T("Back: B/b"),
"Name": i18n.T("Name"), "Name": i18n.T("Name"),
"Username": i18n.T("Username"), "Username": i18n.T("Username"),
"All": i18n.T("all"),
"SearchTip": i18n.T("Search: %s"),
} }
}) })
return i18nMap[name] return i18nMap[name]
......
package handler
import (
"fmt"
"io"
"strconv"
"strings"
"github.com/jumpserver/koko/pkg/common"
"github.com/jumpserver/koko/pkg/config"
"github.com/jumpserver/koko/pkg/logger"
"github.com/jumpserver/koko/pkg/model"
"github.com/jumpserver/koko/pkg/utils"
)
func (h *interactiveHandler) Dispatch() {
defer logger.Infof("Request %s: User %s stop interactive", h.sess.ID(), h.user.Name)
for {
line, err := h.term.ReadLine()
if err != nil {
logger.Debugf("User %s close connect", h.user.Name)
break
}
line = strings.TrimSpace(line)
switch len(line) {
case 0, 1:
switch strings.ToLower(line) {
case "p":
h.resetPaginator()
case "b":
if h.assetPaginator != nil {
h.movePrePage()
break
}
if ok := h.searchOrProxy(line); ok {
continue
}
case "n":
if h.assetPaginator != nil {
h.moveNextPage()
break
}
if ok := h.searchOrProxy(line); ok {
continue
}
case "":
if h.assetPaginator != nil {
h.moveNextPage()
} else {
h.resetPaginator()
}
case "g":
h.displayNodeTree()
continue
case "h":
h.displayBanner()
continue
case "r":
h.refreshAssetsAndNodesData()
continue
case "q":
logger.Debugf("user %s enter to exit", h.user.Name)
return
default:
if ok := h.searchOrProxy(line); ok {
continue
}
}
default:
switch {
case line == "exit", line == "quit":
logger.Debugf("user %s enter to exit", h.user.Name)
return
case strings.Index(line, "/") == 0:
if strings.Index(line[1:], "/") == 0 {
line = strings.TrimSpace(line[2:])
h.searchAssetsAgain(line)
break
}
line = strings.TrimSpace(line[1:])
h.searchAssetAndDisplay(line)
case strings.Index(line, "g") == 0:
searchWord := strings.TrimSpace(strings.TrimPrefix(line, "g"))
if num, err := strconv.Atoi(searchWord); err == nil {
if num >= 0 {
h.searchNewNodeAssets(num)
break
}
}
if ok := h.searchOrProxy(line); ok {
continue
}
default:
if ok := h.searchOrProxy(line); ok {
continue
}
}
}
h.displayPageAssets()
}
}
func (h *interactiveHandler) resetPaginator() {
h.assetPaginator = h.getAssetPaginator()
h.currentData = h.assetPaginator.RetrievePageData(1)
}
func (h *interactiveHandler) displayPageAssets() {
if len(h.currentData) == 0 {
_, _ = h.term.Write([]byte(getI18nFromMap("NoAssets") + "\n\r"))
h.assetPaginator = nil
h.currentSortedData = nil
return
}
Labels := []string{getI18nFromMap("ID"), getI18nFromMap("Hostname"),
getI18nFromMap("IP"), getI18nFromMap("Comment")}
fields := []string{"ID", "hostname", "IP", "comment"}
h.currentSortedData = model.AssetList(h.currentData).SortBy(config.GetConf().AssetListSortBy)
data := make([]map[string]string, len(h.currentSortedData))
for i, j := range h.currentSortedData {
row := make(map[string]string)
row["ID"] = strconv.Itoa(i + 1)
row["hostname"] = j.Hostname
row["IP"] = j.IP
comments := make([]string, 0)
for _, item := range strings.Split(strings.TrimSpace(j.Comment), "\r\n") {
if strings.TrimSpace(item) == "" {
continue
}
comments = append(comments, strings.ReplaceAll(strings.TrimSpace(item), " ", ","))
}
row["comment"] = strings.Join(comments, "|")
data[i] = row
}
w, _ := h.term.GetSize()
currentPage := h.assetPaginator.CurrentPage()
pageSize := h.assetPaginator.PageSize()
totalPage := h.assetPaginator.TotalPage()
totalCount := h.assetPaginator.TotalCount()
caption := fmt.Sprintf(getI18nFromMap("AssetTableCaption"),
currentPage, pageSize, totalPage, totalCount)
caption = utils.WrapperString(caption, utils.Green)
table := common.WrapperTable{
Fields: fields,
Labels: Labels,
FieldsSize: map[string][3]int{
"ID": {0, 0, 5},
"hostname": {0, 8, 0},
"IP": {0, 15, 40},
"comment": {0, 0, 0},
},
Data: data,
TotalSize: w,
Caption: caption,
TruncPolicy: common.TruncMiddle,
}
table.Initial()
header := getI18nFromMap("All")
keys := h.assetPaginator.SearchKeys()
switch h.assetPaginator.Name() {
case "local", "remote":
if len(keys) != 0 {
header = strings.Join(keys, " ")
}
default:
header = fmt.Sprintf("%s %s", h.assetPaginator.Name(), strings.Join(keys, " "))
}
searchHeader := fmt.Sprintf(getI18nFromMap("SearchTip"), header)
actionTip := fmt.Sprintf("%s %s", getI18nFromMap("LoginTip"), getI18nFromMap("PageActionTip"))
_, _ = h.term.Write([]byte(utils.CharClear))
_, _ = h.term.Write([]byte(table.Display()))
utils.IgnoreErrWriteString(h.term, utils.WrapperString(actionTip, utils.Green))
utils.IgnoreErrWriteString(h.term, utils.CharNewLine)
utils.IgnoreErrWriteString(h.term, utils.WrapperString(searchHeader, utils.Green))
utils.IgnoreErrWriteString(h.term, utils.CharNewLine)
}
func (h *interactiveHandler) movePrePage() {
if h.assetPaginator == nil || !h.assetPaginator.HasPrev() {
return
}
h.assetPaginator.SetPageSize(getPageSize(h.term))
prePage := h.assetPaginator.CurrentPage() - 1
h.currentData = h.assetPaginator.RetrievePageData(prePage)
}
func (h *interactiveHandler) moveNextPage() {
if h.assetPaginator == nil || !h.assetPaginator.HasNext() {
return
}
h.assetPaginator.SetPageSize(getPageSize(h.term))
nextPage := h.assetPaginator.CurrentPage() + 1
h.currentData = h.assetPaginator.RetrievePageData(nextPage)
}
func (h *interactiveHandler) searchAssets(key string) []model.Asset {
if _, ok := h.assetPaginator.(*nodeAssetsPaginator); ok {
h.assetPaginator = nil
}
if h.assetPaginator == nil {
h.assetPaginator = h.getAssetPaginator()
}
return h.assetPaginator.SearchAsset(key)
}
func (h *interactiveHandler) searchOrProxy(key string) bool {
if indexNum, err := strconv.Atoi(key); err == nil && len(h.currentSortedData) > 0 {
if indexNum > 0 && indexNum <= len(h.currentSortedData) {
assetSelect := h.currentSortedData[indexNum-1]
h.ProxyAsset(assetSelect)
h.assetPaginator = nil
h.currentSortedData = nil
return true
}
}
if data := h.searchAssets(key); len(data) == 1 {
h.ProxyAsset(data[0])
h.assetPaginator = nil
h.currentSortedData = nil
return true
} else {
h.currentData = data
}
return false
}
func (h *interactiveHandler) searchAssetAndDisplay(key string) {
h.currentData = h.searchAssets(key)
}
func (h *interactiveHandler) searchAssetsAgain(key string) {
if h.assetPaginator == nil {
h.assetPaginator = h.getAssetPaginator()
h.currentData = h.assetPaginator.SearchAsset(key)
return
}
h.currentData = h.assetPaginator.SearchAgain(key)
}
func (h *interactiveHandler) displayNodeTree() {
<-h.firstLoadDone
tree := ConstructAssetNodeTree(h.nodes)
_, _ = io.WriteString(h.term, "\n\r"+getI18nFromMap("NodeHeaderTip"))
_, _ = io.WriteString(h.term, tree.String())
_, err := io.WriteString(h.term, getI18nFromMap("NodeEndTip")+"\n\r")
if err != nil {
logger.Info("displayAssetNodes err:", err)
}
}
func (h *interactiveHandler) searchNewNodeAssets(num int) {
<-h.firstLoadDone
if num > len(h.nodes) || num == 0 {
h.currentData = nil
return
}
node := h.nodes[num-1]
h.assetPaginator = h.getNodeAssetPaginator(node)
h.currentData = h.assetPaginator.RetrievePageData(1)
}
func (h *interactiveHandler) getAssetPaginator() AssetPaginator {
switch h.assetLoadPolicy {
case "all":
<-h.firstLoadDone
return NewLocalAssetPaginator(h.allAssets, getPageSize(h.term))
default:
}
return NewRemoteAssetPaginator(*h.user, getPageSize(h.term))
}
func (h *interactiveHandler) getNodeAssetPaginator(node model.Node) AssetPaginator {
return NewNodeAssetPaginator(*h.user, node, getPageSize(h.term))
}
package handler
import (
"fmt"
"io"
"strconv"
"strings"
"github.com/jumpserver/koko/pkg/common"
"github.com/jumpserver/koko/pkg/config"
"github.com/jumpserver/koko/pkg/model"
"github.com/jumpserver/koko/pkg/service"
"github.com/jumpserver/koko/pkg/utils"
)
func NewAssetPagination(term *utils.Terminal, assets []model.Asset) AssetPagination {
assetPage := AssetPagination{term: term, assets: assets}
assetPage.Initial()
return assetPage
}
type AssetPagination struct {
term *utils.Terminal
assets []model.Asset
page *common.Pagination
currentData []model.Asset
}
func (p *AssetPagination) Initial() {
pageData := make([]interface{}, len(p.assets))
for i, v := range p.assets {
pageData[i] = v
}
pageSize := p.getPageSize()
p.page = common.NewPagination(pageData, pageSize)
firstPageData := p.page.GetPageData(1)
p.currentData = make([]model.Asset, len(firstPageData))
for i, item := range firstPageData {
p.currentData[i] = item.(model.Asset)
}
}
func (p *AssetPagination) getPageSize() int {
var (
pageSize int
minHeight = 8 // 分页显示的最小高度
)
_, height := p.term.GetSize()
switch config.GetConf().AssetListPageSize {
case "auto":
pageSize = height - minHeight
case "all":
pageSize = len(p.assets)
default:
if value, err := strconv.Atoi(config.GetConf().AssetListPageSize); err == nil {
pageSize = value
} else {
pageSize = height - minHeight
}
}
if pageSize <= 0 {
pageSize = 1
}
return pageSize
}
func (p *AssetPagination) Start() []model.Asset {
p.term.SetPrompt(": ")
defer p.term.SetPrompt("Opt> ")
for {
// 总数据小于page size,则显示所有资产且退出
if p.page.PageSize() >= p.page.TotalCount() {
p.currentData = p.assets
p.displayPageAssets()
return []model.Asset{}
}
p.displayPageAssets()
p.displayTipsInfo()
line, err := p.term.ReadLine()
if err != nil {
return []model.Asset{}
}
pageSize := p.getPageSize()
p.page.SetPageSize(pageSize)
line = strings.TrimSpace(line)
switch len(line) {
case 0, 1:
switch strings.ToLower(line) {
case "p":
if !p.page.HasPrev() {
continue
}
prePageData := p.page.GetPrevPageData()
if len(p.currentData) != len(prePageData) {
p.currentData = make([]model.Asset, len(prePageData))
}
for i, item := range prePageData {
p.currentData[i] = item.(model.Asset)
}
case "", "n":
if !p.page.HasNext() {
continue
}
nextPageData := p.page.GetNextPageData()
if len(p.currentData) != len(nextPageData) {
p.currentData = make([]model.Asset, len(nextPageData))
}
for i, item := range nextPageData {
p.currentData[i] = item.(model.Asset)
}
case "b", "q":
return []model.Asset{}
default:
if indexID, err := strconv.Atoi(line); err == nil {
if indexID > 0 && indexID <= len(p.currentData) {
return []model.Asset{p.currentData[indexID-1]}
}
}
}
default:
if indexID, err := strconv.Atoi(line); err == nil {
if indexID > 0 && indexID <= len(p.currentData) {
return []model.Asset{p.currentData[indexID-1]}
}
}
}
}
}
func (p *AssetPagination) displayPageAssets() {
Labels := []string{getI18nFromMap("ID"), getI18nFromMap("Hostname"),
getI18nFromMap("IP"), getI18nFromMap("Comment")}
fields := []string{"ID", "hostname", "IP", "comment"}
data := make([]map[string]string, len(p.currentData))
for i, j := range p.currentData {
row := make(map[string]string)
row["ID"] = strconv.Itoa(i + 1)
row["hostname"] = j.Hostname
row["IP"] = j.IP
comments := make([]string, 0)
for _, item := range strings.Split(strings.TrimSpace(j.Comment), "\r\n") {
if strings.TrimSpace(item) == "" {
continue
}
comments = append(comments, strings.ReplaceAll(strings.TrimSpace(item), " ", ","))
}
row["comment"] = strings.Join(comments, "|")
data[i] = row
}
w, _ := p.term.GetSize()
caption := fmt.Sprintf(getI18nFromMap("AssetTableCaption"),
p.page.CurrentPage(), p.page.PageSize(), p.page.TotalPage(), p.page.TotalCount(),
)
caption = utils.WrapperString(caption, utils.Green)
table := common.WrapperTable{
Fields: fields,
Labels: Labels,
FieldsSize: map[string][3]int{
"ID": {0, 0, 5},
"hostname": {0, 8, 0},
"IP": {0, 15, 40},
"comment": {0, 0, 0},
},
Data: data,
TotalSize: w,
Caption: caption,
TruncPolicy: common.TruncMiddle,
}
table.Initial()
_, _ = p.term.Write([]byte(utils.CharClear))
_, _ = p.term.Write([]byte(table.Display()))
}
func (p *AssetPagination) displayTipsInfo() {
displayAssetPaginationTipsInfo(p.term)
}
func NewUserPagination(term *utils.Terminal, uid, search string, policy bool) UserAssetPagination {
return UserAssetPagination{
UserID: uid,
offset: 0,
limit: 0,
search: search,
term: term,
displayPolicy: policy,
Data: model.AssetsPaginationResponse{},
}
}
type UserAssetPagination struct {
UserID string
offset int
limit int
search string
term *utils.Terminal
displayPolicy bool
Data model.AssetsPaginationResponse
IsNeedProxy bool
currentData []model.Asset
}
func (p *UserAssetPagination) Start() []model.Asset {
p.term.SetPrompt(": ")
defer p.term.SetPrompt("Opt> ")
for {
p.retrieveData()
if p.displayPolicy && p.Data.Total == 1 {
p.IsNeedProxy = true
return p.Data.Data
}
// 无上下页,则退出循环
if p.Data.NextURL == "" && p.Data.PreviousURL == "" {
p.displayPageAssets()
return p.currentData
}
inLoop:
p.displayPageAssets()
p.displayTipsInfo()
line, err := p.term.ReadLine()
if err != nil {
return p.currentData
}
line = strings.TrimSpace(line)
switch len(line) {
case 0, 1:
switch strings.ToLower(line) {
case "p":
if p.Data.PreviousURL == "" {
continue
}
p.offset -= p.limit
case "", "n":
if p.Data.NextURL == "" {
continue
}
p.offset += p.limit
case "b", "q":
return []model.Asset{}
default:
if indexID, err := strconv.Atoi(line); err == nil {
if indexID > 0 && indexID <= len(p.currentData) {
p.IsNeedProxy = true
return []model.Asset{p.currentData[indexID-1]}
}
}
goto inLoop
}
default:
if indexID, err := strconv.Atoi(line); err == nil {
if indexID > 0 && indexID <= len(p.currentData) {
p.IsNeedProxy = true
return []model.Asset{p.currentData[indexID-1]}
}
}
goto inLoop
}
}
}
func (p *UserAssetPagination) displayPageAssets() {
if len(p.Data.Data) == 0 {
_, _ = p.term.Write([]byte(getI18nFromMap("NoAssets") + "\n\r"))
return
}
Labels := []string{getI18nFromMap("ID"), getI18nFromMap("Hostname"),
getI18nFromMap("IP"), getI18nFromMap("Comment")}
fields := []string{"ID", "hostname", "IP", "comment"}
p.currentData = model.AssetList(p.Data.Data).SortBy(config.GetConf().AssetListSortBy)
data := make([]map[string]string, len(p.currentData))
for i, j := range p.currentData {
row := make(map[string]string)
row["ID"] = strconv.Itoa(i + 1)
row["hostname"] = j.Hostname
row["IP"] = j.IP
comments := make([]string, 0)
for _, item := range strings.Split(strings.TrimSpace(j.Comment), "\r\n") {
if strings.TrimSpace(item) == "" {
continue
}
comments = append(comments, strings.ReplaceAll(strings.TrimSpace(item), " ", ","))
}
row["comment"] = strings.Join(comments, "|")
data[i] = row
}
w, _ := p.term.GetSize()
var pageSize int
var totalPage int
var currentPage int
var totalCount int
currentOffset := p.offset + len(p.currentData)
switch p.limit {
case 0:
pageSize = len(p.currentData)
totalCount = pageSize
totalPage = 1
currentPage = 1
default:
pageSize = p.limit
totalCount = p.Data.Total
switch totalCount % pageSize {
case 0:
totalPage = totalCount / pageSize
default:
totalPage = (totalCount / pageSize) + 1
}
switch currentOffset % pageSize {
case 0:
currentPage = currentOffset / pageSize
default:
currentPage = (currentOffset / pageSize) + 1
}
}
caption := fmt.Sprintf(getI18nFromMap("AssetTableCaption"),
currentPage, pageSize, totalPage, totalCount)
caption = utils.WrapperString(caption, utils.Green)
table := common.WrapperTable{
Fields: fields,
Labels: Labels,
FieldsSize: map[string][3]int{
"ID": {0, 0, 5},
"hostname": {0, 8, 0},
"IP": {0, 15, 40},
"comment": {0, 0, 0},
},
Data: data,
TotalSize: w,
Caption: caption,
TruncPolicy: common.TruncMiddle,
}
table.Initial()
_, _ = p.term.Write([]byte(utils.CharClear))
_, _ = p.term.Write([]byte(table.Display()))
}
func (p *UserAssetPagination) displayTipsInfo() {
displayAssetPaginationTipsInfo(p.term)
}
func (p *UserAssetPagination) retrieveData() {
p.limit = getPageSize(p.term)
if p.limit == 0 || p.offset < 0 || p.limit >= p.Data.Total {
p.offset = 0
}
p.Data = service.GetUserAssets(p.UserID, p.search, p.limit, p.offset)
}
func getPageSize(term *utils.Terminal) int {
var (
pageSize int
minHeight = 8 // 分页显示的最小高度
)
_, height := term.GetSize()
conf := config.GetConf()
switch conf.AssetListPageSize {
case "auto":
pageSize = height - minHeight
case "all":
return 0
default:
if value, err := strconv.Atoi(conf.AssetListPageSize); err == nil {
pageSize = value
} else {
pageSize = height - minHeight
}
}
if pageSize <= 0 {
pageSize = 1
}
return pageSize
}
func displayAssetPaginationTipsInfo(w io.Writer) {
utils.IgnoreErrWriteString(w, getI18nFromMap("LoginTip"))
utils.IgnoreErrWriteString(w, getI18nFromMap("PageActionTip"))
}
...@@ -29,7 +29,8 @@ func SessionHandler(sess ssh.Session) { ...@@ -29,7 +29,8 @@ func SessionHandler(sess ssh.Session) {
if ok { if ok {
handler := newInteractiveHandler(sess, user) handler := newInteractiveHandler(sess, user)
logger.Infof("Request %s: User %s request pty %s", handler.sess.ID(), sess.User(), pty.Term) logger.Infof("Request %s: User %s request pty %s", handler.sess.ID(), sess.User(), pty.Term)
handler.Dispatch(sess.Context()) go handler.watchWinSizeChange()
handler.Dispatch()
} else { } else {
utils.IgnoreErrWriteString(sess, "No PTY requested.\n") utils.IgnoreErrWriteString(sess, "No PTY requested.\n")
return return
...@@ -57,19 +58,23 @@ type interactiveHandler struct { ...@@ -57,19 +58,23 @@ type interactiveHandler struct {
assetSelect *model.Asset assetSelect *model.Asset
systemUserSelect *model.SystemUser systemUserSelect *model.SystemUser
nodes model.NodeList nodes model.NodeList
searchResult []model.Asset
allAssets []model.Asset allAssets []model.Asset
loadDataDone chan struct{} firstLoadDone chan struct{}
assetLoadPolicy string assetLoadPolicy string
currentSortedData []model.Asset
currentData []model.Asset
assetPaginator AssetPaginator
} }
func (h *interactiveHandler) Initial() { func (h *interactiveHandler) Initial() {
h.assetLoadPolicy = strings.ToLower(config.GetConf().AssetLoadPolicy) h.assetLoadPolicy = strings.ToLower(config.GetConf().AssetLoadPolicy)
h.displayBanner() h.displayBanner()
h.winWatchChan = make(chan bool, 1) h.winWatchChan = make(chan bool, 5)
h.loadDataDone = make(chan struct{}) h.firstLoadDone = make(chan struct{})
go h.firstLoadData() go h.firstLoadData()
} }
...@@ -79,7 +84,7 @@ func (h *interactiveHandler) firstLoadData() { ...@@ -79,7 +84,7 @@ func (h *interactiveHandler) firstLoadData() {
case "all": case "all":
h.loadAllAssets() h.loadAllAssets()
} }
close(h.loadDataDone) close(h.firstLoadDone)
} }
func (h *interactiveHandler) displayBanner() { func (h *interactiveHandler) displayBanner() {
...@@ -115,95 +120,13 @@ func (h *interactiveHandler) watchWinSizeChange() { ...@@ -115,95 +120,13 @@ func (h *interactiveHandler) watchWinSizeChange() {
} }
func (h *interactiveHandler) pauseWatchWinSize() { func (h *interactiveHandler) pauseWatchWinSize() {
select {
case <-h.sess.Sess.Context().Done():
return
default:
}
h.winWatchChan <- false h.winWatchChan <- false
} }
func (h *interactiveHandler) resumeWatchWinSize() { func (h *interactiveHandler) resumeWatchWinSize() {
select {
case <-h.sess.Sess.Context().Done():
return
default:
}
h.winWatchChan <- true h.winWatchChan <- true
} }
func (h *interactiveHandler) Dispatch(ctx context.Context) {
go h.watchWinSizeChange()
defer logger.Infof("Request %s: User %s stop interactive", h.sess.ID(), h.user.Name)
for {
line, err := h.term.ReadLine()
if err != nil {
logger.Debugf("User %s close connect", h.user.Name)
break
}
line = strings.TrimSpace(line)
switch len(line) {
case 0, 1:
switch strings.ToLower(line) {
case "", "p":
// 展示所有的资产
h.displayAllAssets()
case "g":
<-h.loadDataDone
h.displayNodes(h.nodes)
case "h":
h.displayBanner()
case "r":
h.refreshAssetsAndNodesData()
case "q":
logger.Debugf("user %s enter to exit", h.user.Name)
return
default:
h.searchAssetOrProxy(line)
}
default:
switch {
case line == "exit", line == "quit":
logger.Debugf("user %s enter to exit", h.user.Name)
return
case strings.Index(line, "/") == 0:
searchWord := strings.TrimSpace(line[1:])
h.searchAsset(searchWord)
case strings.Index(line, "g") == 0:
searchWord := strings.TrimSpace(strings.TrimPrefix(line, "g"))
if num, err := strconv.Atoi(searchWord); err == nil {
if num >= 0 {
assets := h.searchNodeAssets(num)
h.displayAssets(assets)
continue
}
}
h.searchAssetOrProxy(line)
default:
h.searchAssetOrProxy(line)
}
}
}
}
func (h *interactiveHandler) displayAllAssets() {
switch h.assetLoadPolicy {
case "all":
<-h.loadDataDone
h.displayAssets(h.allAssets)
default:
pag := NewUserPagination(h.term, h.user.ID, "", false)
result := pag.Start()
if pag.IsNeedProxy && len(result) == 1 {
h.searchResult = h.searchResult[:0]
h.ProxyAsset(result[0])
} else {
h.searchResult = result
}
}
}
func (h *interactiveHandler) chooseSystemUser(asset model.Asset, func (h *interactiveHandler) chooseSystemUser(asset model.Asset,
systemUsers []model.SystemUser) (systemUser model.SystemUser, ok bool) { systemUsers []model.SystemUser) (systemUser model.SystemUser, ok bool) {
...@@ -271,50 +194,19 @@ func (h *interactiveHandler) chooseSystemUser(asset model.Asset, ...@@ -271,50 +194,19 @@ func (h *interactiveHandler) chooseSystemUser(asset model.Asset,
} }
} }
func (h *interactiveHandler) displayAssets(assets model.AssetList) {
if len(assets) == 0 {
_, _ = io.WriteString(h.term, getI18nFromMap("NoAssets")+"\n\r")
} else {
sortedAssets := assets.SortBy(config.GetConf().AssetListSortBy)
pag := NewAssetPagination(h.term, sortedAssets)
selectOneAssets := pag.Start()
if len(selectOneAssets) == 1 {
systemUsers := service.GetUserAssetSystemUsers(h.user.ID, selectOneAssets[0].ID)
systemUser, ok := h.chooseSystemUser(selectOneAssets[0], systemUsers)
if !ok {
return
}
h.assetSelect = &selectOneAssets[0]
h.systemUserSelect = &systemUser
h.Proxy(context.TODO())
}
if pag.page.PageSize() >= pag.page.TotalCount() {
h.searchResult = sortedAssets
}
}
}
func (h *interactiveHandler) displayNodes(nodes []model.Node) {
tree := ConstructAssetNodeTree(nodes)
_, _ = io.WriteString(h.term, "\n\r"+getI18nFromMap("NodeHeaderTip"))
_, _ = io.WriteString(h.term, tree.String())
_, err := io.WriteString(h.term, getI18nFromMap("NodeEndTip")+"\n\r")
if err != nil {
logger.Info("displayAssetNodes err:", err)
}
}
func (h *interactiveHandler) refreshAssetsAndNodesData() { func (h *interactiveHandler) refreshAssetsAndNodesData() {
switch h.assetLoadPolicy { switch h.assetLoadPolicy {
case "all": case "all":
h.loadAllAssets() h.loadAllAssets()
default:
_ = service.ForceRefreshUserPemAssets(h.user.ID)
} }
h.loadUserNodes("2") h.loadUserNodes("2")
_, err := io.WriteString(h.term, getI18nFromMap("RefreshDone")+"\n\r") _, err := io.WriteString(h.term, getI18nFromMap("RefreshDone")+"\n\r")
if err != nil { if err != nil {
logger.Error("refresh Assets Nodes err:", err) logger.Error("refresh Assets Nodes err:", err)
} }
h.assetPaginator = nil
} }
func (h *interactiveHandler) loadUserNodes(cachePolicy string) { func (h *interactiveHandler) loadUserNodes(cachePolicy string) {
...@@ -325,76 +217,6 @@ func (h *interactiveHandler) loadAllAssets() { ...@@ -325,76 +217,6 @@ func (h *interactiveHandler) loadAllAssets() {
h.allAssets = service.GetUserAllAssets(h.user.ID) h.allAssets = service.GetUserAllAssets(h.user.ID)
} }
func (h *interactiveHandler) searchAsset(key string) {
switch h.assetLoadPolicy {
case "all":
<-h.loadDataDone
var searchData []model.Asset
switch len(h.searchResult) {
case 0:
searchData = h.allAssets
default:
searchData = h.searchResult
}
assets := searchFromLocalAssets(searchData, key)
h.displayAssets(assets)
default:
pag := NewUserPagination(h.term, h.user.ID, key, false)
result := pag.Start()
if pag.IsNeedProxy && len(result) == 1 {
h.searchResult = h.searchResult[:0]
h.ProxyAsset(result[0])
} else {
h.searchResult = result
}
}
}
func (h *interactiveHandler) searchAssetOrProxy(key string) {
if indexNum, err := strconv.Atoi(key); err == nil && len(h.searchResult) > 0 {
if indexNum > 0 && indexNum <= len(h.searchResult) {
assetSelect := h.searchResult[indexNum-1]
h.ProxyAsset(assetSelect)
return
}
}
var assets []model.Asset
switch h.assetLoadPolicy {
case "all":
<-h.loadDataDone
var searchData []model.Asset
switch len(h.searchResult) {
case 0:
searchData = h.allAssets
default:
searchData = h.searchResult
}
assets = searchFromLocalAssets(searchData, key)
if len(assets) != 1 {
h.displayAssets(assets)
return
}
default:
pag := NewUserPagination(h.term, h.user.ID, key, true)
assets = pag.Start()
}
if len(assets) == 1 {
h.ProxyAsset(assets[0])
} else {
h.searchResult = assets
}
}
func (h *interactiveHandler) searchNodeAssets(num int) (assets model.AssetList) {
if num > len(h.nodes) || num == 0 {
return assets
}
node := h.nodes[num-1]
assets = service.GetUserNodeAssets(h.user.ID, node.ID, "1")
return
}
func (h *interactiveHandler) ProxyAsset(assetSelect model.Asset) { func (h *interactiveHandler) ProxyAsset(assetSelect model.Asset) {
systemUsers := service.GetUserAssetSystemUsers(h.user.ID, assetSelect.ID) systemUsers := service.GetUserAssetSystemUsers(h.user.ID, assetSelect.ID)
systemUserSelect, ok := h.chooseSystemUser(assetSelect, systemUsers) systemUserSelect, ok := h.chooseSystemUser(assetSelect, systemUsers)
...@@ -488,3 +310,29 @@ func searchFromLocalAssets(assets model.AssetList, key string) []model.Asset { ...@@ -488,3 +310,29 @@ func searchFromLocalAssets(assets model.AssetList, key string) []model.Asset {
} }
return displayAssets return displayAssets
} }
func getPageSize(term *utils.Terminal) int {
var (
pageSize int
minHeight = 8 // 分页显示的最小高度
)
_, height := term.GetSize()
conf := config.GetConf()
switch conf.AssetListPageSize {
case "auto":
pageSize = height - minHeight
case "all":
return 0
default:
if value, err := strconv.Atoi(conf.AssetListPageSize); err == nil {
pageSize = value
} else {
pageSize = height - minHeight
}
}
if pageSize <= 0 {
pageSize = 1
}
return pageSize
}
\ No newline at end of file
...@@ -56,8 +56,10 @@ func OnNamespaceConnected(c *neffos.NSConn, msg neffos.Message) error { ...@@ -56,8 +56,10 @@ func OnNamespaceConnected(c *neffos.NSConn, msg neffos.Message) error {
remoteIP = strings.Split(remoteAddr, ",")[0] remoteIP = strings.Split(remoteAddr, ",")[0]
logger.Infof("Accepted %s connect websocket from %s", user.Username, remoteIP) logger.Infof("Accepted %s connect websocket from %s", user.Username, remoteIP)
go func() { go func() {
tick := time.NewTicker(30 * time.Second)
defer tick.Stop()
for { for {
<-time.After(30 * time.Second) <-tick.C
if c.Conn.IsClosed() { if c.Conn.IsClosed() {
logger.Infof("User %s from %s websocket connect closed", user.Username, remoteIP) logger.Infof("User %s from %s websocket connect closed", user.Username, remoteIP)
return return
......
...@@ -60,6 +60,8 @@ func (c *CommandRecorder) record() { ...@@ -60,6 +60,8 @@ func (c *CommandRecorder) record() {
maxRetry := 0 maxRetry := 0
logger.Infof("Session %s: Command recorder start", c.sessionID) logger.Infof("Session %s: Command recorder start", c.sessionID)
defer logger.Infof("Session %s: Command recorder close", c.sessionID) defer logger.Infof("Session %s: Command recorder close", c.sessionID)
tick := time.NewTicker(time.Second * 10)
defer tick.Stop()
for { for {
select { select {
case <-c.closed: case <-c.closed:
...@@ -74,7 +76,7 @@ func (c *CommandRecorder) record() { ...@@ -74,7 +76,7 @@ func (c *CommandRecorder) record() {
if len(cmdList) < 5 { if len(cmdList) < 5 {
continue continue
} }
case <-time.After(time.Second * 5): case <-tick.C:
if len(cmdList) == 0 { if len(cmdList) == 0 {
continue continue
} }
......
...@@ -152,10 +152,19 @@ func (s *SwitchSession) Bridge(userConn UserConnection, srvConn srvconn.ServerCo ...@@ -152,10 +152,19 @@ func (s *SwitchSession) Bridge(userConn UserConnection, srvConn srvconn.ServerCo
go LoopRead(srvConn, srvInChan) go LoopRead(srvConn, srvInChan)
winCh := userConn.WinCh() winCh := userConn.WinCh()
maxIdleTime := s.MaxIdleTime * time.Minute
lastActiveTime := time.Now()
tick := time.NewTicker(30 * time.Second)
defer tick.Stop()
for { for {
select { select {
// 检测是否超过最大空闲时间 // 检测是否超过最大空闲时间
case <-time.After(s.MaxIdleTime * time.Minute): case <-tick.C:
now := time.Now()
outTime := lastActiveTime.Add(maxIdleTime)
if !now.After(outTime) {
continue
}
msg := fmt.Sprintf(i18n.T("Connect idle more than %d minutes, disconnect"), s.MaxIdleTime) msg := fmt.Sprintf(i18n.T("Connect idle more than %d minutes, disconnect"), s.MaxIdleTime)
logger.Debugf("Session idle more than %d minutes, disconnect: %s", s.MaxIdleTime, s.ID) logger.Debugf("Session idle more than %d minutes, disconnect: %s", s.MaxIdleTime, s.ID)
msg = utils.WrapperWarn(msg) msg = utils.WrapperWarn(msg)
...@@ -190,6 +199,7 @@ func (s *SwitchSession) Bridge(userConn UserConnection, srvConn srvconn.ServerCo ...@@ -190,6 +199,7 @@ func (s *SwitchSession) Bridge(userConn UserConnection, srvConn srvconn.ServerCo
} }
_, err = srvConn.Write(p) _, err = srvConn.Write(p)
} }
lastActiveTime = time.Now()
} }
} }
......
...@@ -9,24 +9,30 @@ import ( ...@@ -9,24 +9,30 @@ import (
"github.com/jumpserver/koko/pkg/model" "github.com/jumpserver/koko/pkg/model"
) )
func GetUserAssets(userID, search string, pageSize, offset int) (resp model.AssetsPaginationResponse) { func GetUserAssets(userID string, pageSize, offset int, searches ...string) (resp model.AssetsPaginationResponse) {
if pageSize < 0 { if pageSize < 0 {
pageSize = 0 pageSize = 0
} }
paramsArray := make([]map[string]string, 0, len(searches)+2)
for i := 0; i < len(searches); i++ {
paramsArray = append(paramsArray, map[string]string{
"search": url.QueryEscape(searches[i]),
})
}
params := map[string]string{ params := map[string]string{
"search": url.QueryEscape(search),
"limit": strconv.Itoa(pageSize), "limit": strconv.Itoa(pageSize),
"offset": strconv.Itoa(offset), "offset": strconv.Itoa(offset),
} }
paramsArray = append(paramsArray, params)
Url := fmt.Sprintf(UserAssetsURL, userID) Url := fmt.Sprintf(UserAssetsURL, userID)
var err error var err error
if pageSize > 0 { if pageSize > 0 {
_, err = authClient.Get(Url, &resp, params) _, err = authClient.Get(Url, &resp, paramsArray...)
} else { } else {
var data model.AssetList var data model.AssetList
_, err = authClient.Get(Url, &data, params) _, err = authClient.Get(Url, &data, paramsArray...)
resp.Data = data resp.Data = data
resp.Total = len(data)
} }
if err != nil { if err != nil {
logger.Error("Get user assets error: ", err) logger.Error("Get user assets error: ", err)
...@@ -34,6 +40,21 @@ func GetUserAssets(userID, search string, pageSize, offset int) (resp model.Asse ...@@ -34,6 +40,21 @@ func GetUserAssets(userID, search string, pageSize, offset int) (resp model.Asse
return return
} }
func ForceRefreshUserPemAssets(userID string) error {
params := map[string]string{
"limit": "1",
"offset": "0",
"cache": "2",
}
Url := fmt.Sprintf(UserAssetsURL, userID)
var resp model.AssetsPaginationResponse
_, err := authClient.Get(Url, resp, params)
if err != nil {
logger.Errorf("Refresh user assets error: %s", err)
}
return err
}
func GetUserAllAssets(userID string) (assets []model.Asset) { func GetUserAllAssets(userID string) (assets []model.Asset) {
Url := fmt.Sprintf(UserAssetsURL, userID) Url := fmt.Sprintf(UserAssetsURL, userID)
_, err := authClient.Get(Url, &assets) _, err := authClient.Get(Url, &assets)
...@@ -91,6 +112,38 @@ func GetUserNodeAssets(userID, nodeID, cachePolicy string) (assets model.AssetLi ...@@ -91,6 +112,38 @@ func GetUserNodeAssets(userID, nodeID, cachePolicy string) (assets model.AssetLi
return return
} }
func GetUserNodePaginationAssets(userID, nodeID string, pageSize, offset int, searches ...string) (resp model.AssetsPaginationResponse) {
if pageSize < 0 {
pageSize = 0
}
paramsArray := make([]map[string]string, 0, len(searches)+2)
for i := 0; i < len(searches); i++ {
paramsArray = append(paramsArray, map[string]string{
"search": url.QueryEscape(searches[i]),
})
}
params := map[string]string{
"limit": strconv.Itoa(pageSize),
"offset": strconv.Itoa(offset),
}
paramsArray = append(paramsArray, params)
Url := fmt.Sprintf(UserNodeAssetsListURL, userID, nodeID)
var err error
if pageSize > 0 {
_, err = authClient.Get(Url, &resp, paramsArray...)
} else {
var data model.AssetList
_, err = authClient.Get(Url, &data, paramsArray...)
resp.Data = data
resp.Total = len(data)
}
if err != nil {
logger.Error("Get user node assets error: ", err)
}
return
}
func ValidateUserAssetPermission(userID, assetID, systemUserID, action string) bool { func ValidateUserAssetPermission(userID, assetID, systemUserID, action string) bool {
payload := map[string]string{ payload := map[string]string{
"user_id": userID, "user_id": userID,
......
...@@ -548,17 +548,12 @@ func (u *UserSftp) LoopPushFTPLog() { ...@@ -548,17 +548,12 @@ func (u *UserSftp) LoopPushFTPLog() {
dataChan := make(chan *model.FTPLog) dataChan := make(chan *model.FTPLog)
go u.SendFTPLog(dataChan) go u.SendFTPLog(dataChan)
defer close(dataChan) defer close(dataChan)
var timeoutSecond time.Duration
for {
switch len(ftpLogList) {
case 0:
timeoutSecond = time.Second * 60
default:
timeoutSecond = time.Second * 10
}
tick := time.NewTicker(time.Second * 10)
defer tick.Stop()
for {
select { select {
case <-time.After(timeoutSecond): case <-tick.C:
case logData, ok := <-u.LogChan: case logData, ok := <-u.LogChan:
if !ok { if !ok {
return return
......
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