From 8648351fc7e52b8aecccff399816835b0864be97 Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Sat, 20 Jan 2024 15:35:05 +0100 Subject: [PATCH] WIP new WebAdmin: connections page Signed-off-by: Nicola Murino --- go.mod | 14 +- go.sum | 28 +- internal/common/common.go | 60 +--- internal/common/common_test.go | 14 - internal/httpd/server.go | 2 + internal/httpd/webadmin.go | 16 +- internal/sftpd/internal_test.go | 49 --- internal/util/i18n.go | 1 + static/locales/en/translation.json | 20 +- static/locales/it/translation.json | 20 +- templates/common/base.html | 5 + templates/webadmin/connections.html | 529 ++++++++++++++++++---------- templates/webadmin/folders.html | 2 - 13 files changed, 413 insertions(+), 347 deletions(-) diff --git a/go.mod b/go.mod index 5720b78e..c2f11f63 100644 --- a/go.mod +++ b/go.mod @@ -10,16 +10,16 @@ require ( github.com/alexedwards/argon2id v1.0.0 github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 github.com/aws/aws-sdk-go-v2 v1.24.1 - github.com/aws/aws-sdk-go-v2/config v1.26.4 - github.com/aws/aws-sdk-go-v2/credentials v1.16.15 + github.com/aws/aws-sdk-go-v2/config v1.26.5 + github.com/aws/aws-sdk-go-v2/credentials v1.16.16 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.12 + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.13 github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.19.6 github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.2 github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 github.com/bmatcuk/doublestar/v4 v4.6.1 - github.com/cockroachdb/cockroach-go/v2 v2.3.5 + github.com/cockroachdb/cockroach-go/v2 v2.3.6 github.com/coreos/go-oidc/v3 v3.9.0 github.com/drakkan/webdav v0.0.0-20230227175313-32996838bcd8 github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 @@ -74,7 +74,7 @@ require ( golang.org/x/sys v0.16.0 golang.org/x/term v0.16.0 golang.org/x/time v0.5.0 - google.golang.org/api v0.156.0 + google.golang.org/api v0.157.0 gopkg.in/natefinch/lumberjack.v2 v2.2.1 ) @@ -94,7 +94,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.2.10 // indirect github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.10.10 // indirect github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 // indirect github.com/aws/smithy-go v1.19.0 // indirect github.com/beorn7/perks v1.0.1 // indirect @@ -164,7 +164,7 @@ require ( go.opentelemetry.io/otel/metric v1.22.0 // indirect go.opentelemetry.io/otel/trace v1.22.0 // indirect go.uber.org/multierr v1.11.0 // indirect - golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect + golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/sync v0.6.0 // indirect golang.org/x/text v0.14.0 // indirect diff --git a/go.sum b/go.sum index c0b7dd68..f78e735f 100644 --- a/go.sum +++ b/go.sum @@ -37,14 +37,14 @@ github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3 github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo= -github.com/aws/aws-sdk-go-v2/config v1.26.4 h1:Juj7LhtxNudNUlfX22K5AnLafO+v4eq9PA3VWSCIQs4= -github.com/aws/aws-sdk-go-v2/config v1.26.4/go.mod h1:tioqQ7wvxMYnTDpoTTLHhV3Zh+z261i/f2oz+ds8eNI= -github.com/aws/aws-sdk-go-v2/credentials v1.16.15 h1:P0/m1LU08MF2kRzx4P//+7lNjiJod1z4xI2WpWhdpTQ= -github.com/aws/aws-sdk-go-v2/credentials v1.16.15/go.mod h1:pgtMCf7Dx4GWw5EpHOTc2Sy17LIP0A0N2C9nQ83pQ/0= +github.com/aws/aws-sdk-go-v2/config v1.26.5 h1:lodGSevz7d+kkFJodfauThRxK9mdJbyutUxGq1NNhvw= +github.com/aws/aws-sdk-go-v2/config v1.26.5/go.mod h1:DxHrz6diQJOc9EwDslVRh84VjjrE17g+pVZXUeSxaDU= +github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5gbadPbWdV1WcAddK8= +github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.12 h1:0FMZy36RSYvcvVzEf1xbNdebLHZewW40QWP+P8jCMVk= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.12/go.mod h1:+chyahvarkb3HibkNei9IQEM9P5cWD5w2kgXCa3Hh0I= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.13 h1:8Nt4LBUEKV0FxLBO2BmRzDKax3hp2LRMKySMBwL4vMc= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.13/go.mod h1:t5QEDu/FBJJM4kslbQlTSpYtnhoWDNmHSsgQojIxE0o= github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw= @@ -67,8 +67,8 @@ github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 h1:PJTdBMsyvra6FtED7JZtDpQrIAflY github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0/go.mod h1:4qXHrG1Ne3VGIMZPCB8OjH/pLFO94sKABIusjh0KWPU= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.2 h1:A5sGOT/mukuU+4At1vkSIWAN8tPwPCoYZBp7aruR540= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.2/go.mod h1:qutL00aW8GSo2D0I6UEOqMvRS3ZyuBrOC1BLe5D2jPc= -github.com/aws/aws-sdk-go-v2/service/sso v1.18.6 h1:dGrs+Q/WzhsiUKh82SfTVN66QzyulXuMDTV/G8ZxOac= -github.com/aws/aws-sdk-go-v2/service/sso v1.18.6/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM= +github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow= +github.com/aws/aws-sdk-go-v2/service/sso v1.18.7/go.mod h1:+mJNDdF+qiUlNKNC3fxn74WWNN+sOiGOEImje+3ScPM= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7 h1:QPMJf+Jw8E1l7zqhZmMlFw6w1NmfkfiSK8mS4zOx3BA= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.7/go.mod h1:ykf3COxYI0UJmxcfcxcVuz7b6uADi1FkiUz6Eb7AgM8= github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 h1:NzO4Vrau795RkUdSHKEwiR01FaGzGOH1EETJ+5QHnm0= @@ -93,8 +93,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cockroachdb/cockroach-go/v2 v2.3.5 h1:Khtm8K6fTTz/ZCWPzU9Ne3aOW9VyAnj4qIPCJgKtwK0= -github.com/cockroachdb/cockroach-go/v2 v2.3.5/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8= +github.com/cockroachdb/cockroach-go/v2 v2.3.6 h1:Wlv9TzkrG9V7i6u8dEtmXPrBzvfFp+CgJNs696rAajM= +github.com/cockroachdb/cockroach-go/v2 v2.3.6/go.mod h1:1wNJ45eSXW9AnOc3skntW9ZUZz6gxrQK3cOj3rK+BC8= github.com/coreos/go-oidc/v3 v3.9.0 h1:0J/ogVOd4y8P0f0xUh8l9t07xRP/d8tccvjHl2dcsSo= github.com/coreos/go-oidc/v3 v3.9.0/go.mod h1:rTKz2PYwftcrtoCzV5g5kvfJoWcm0Mk8AF8y1iAQro4= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= @@ -428,8 +428,8 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 gocloud.dev v0.36.0 h1:q5zoXux4xkOZP473e1EZbG8Gq9f0vlg1VNH5Du/ybus= gocloud.dev v0.36.0/go.mod h1:bLxah6JQVKBaIxzsr5BQLYB4IYdWHkMZdzCXlo6F0gg= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= -golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA= +golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= @@ -522,8 +522,8 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= -google.golang.org/api v0.156.0 h1:yloYcGbBtVYjLKQe4enCunxvwn3s2w/XPrrhVf6MsvQ= -google.golang.org/api v0.156.0/go.mod h1:bUSmn4KFO0Q+69zo9CNIDp4Psi6BqM0np0CbzKRSiSY= +google.golang.org/api v0.157.0 h1:ORAeqmbrrozeyw5NjnMxh7peHO0UzV4wWYSwZeCUb20= +google.golang.org/api v0.157.0/go.mod h1:+z4v4ufbZ1WEpld6yMGHyggs+PmAHiaLNj5ytP3N01g= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= diff --git a/internal/common/common.go b/internal/common/common.go index 860cfb49..dd09e26c 100644 --- a/internal/common/common.go +++ b/internal/common/common.go @@ -475,24 +475,6 @@ type ConnectionTransfer struct { DLSize int64 `json:"-"` } -func (t *ConnectionTransfer) getConnectionTransferAsString() string { - result := "" - switch t.OperationType { - case operationUpload: - result += "UL " - case operationDownload: - result += "DL " - } - result += fmt.Sprintf("%q ", t.VirtualPath) - if t.Size > 0 { - elapsed := time.Since(util.GetTimeFromMsecSinceEpoch(t.StartTime)) - speed := float64(t.Size) / float64(util.GetTimeAsMsSinceEpoch(time.Now())-t.StartTime) - result += fmt.Sprintf("Size: %s Elapsed: %s Speed: \"%.1f KB/s\"", util.ByteCountIEC(t.Size), - util.GetDurationAsString(elapsed), speed) - } - return result -} - // MetadataConfig defines how to handle metadata for cloud storage backends type MetadataConfig struct { // If not zero the metadata will be read before downloads and will be @@ -1254,6 +1236,7 @@ func (conns *ActiveConnections) GetStats(role string) []ConnectionStatus { RemoteAddress: c.GetRemoteAddress(), ConnectionTime: util.GetTimeAsMsSinceEpoch(c.GetConnectionTime()), LastActivity: util.GetTimeAsMsSinceEpoch(c.GetLastActivity()), + CurrentTime: util.GetTimeAsMsSinceEpoch(time.Now()), Protocol: c.GetProtocol(), Command: c.GetCommand(), Transfers: c.GetTransfers(), @@ -1279,6 +1262,8 @@ type ConnectionStatus struct { ConnectionTime int64 `json:"connection_time"` // Last activity as unix timestamp in milliseconds LastActivity int64 `json:"last_activity"` + // Current time as unix timestamp in milliseconds + CurrentTime int64 `json:"current_time"` // Protocol for this connection Protocol string `json:"protocol"` // active uploads/downloads @@ -1289,45 +1274,6 @@ type ConnectionStatus struct { Node string `json:"node,omitempty"` } -// GetConnectionDuration returns the connection duration as string -func (c *ConnectionStatus) GetConnectionDuration() string { - elapsed := time.Since(util.GetTimeFromMsecSinceEpoch(c.ConnectionTime)) - return util.GetDurationAsString(elapsed) -} - -// GetConnectionInfo returns connection info. -// Protocol,Client Version and RemoteAddress are returned. -func (c *ConnectionStatus) GetConnectionInfo() string { - var result strings.Builder - - result.WriteString(fmt.Sprintf("%v. Client: %q From: %q", c.Protocol, c.ClientVersion, c.RemoteAddress)) - - if c.Command == "" { - return result.String() - } - - switch c.Protocol { - case ProtocolSSH, ProtocolFTP: - result.WriteString(fmt.Sprintf(". Command: %q", c.Command)) - case ProtocolWebDAV: - result.WriteString(fmt.Sprintf(". Method: %q", c.Command)) - } - - return result.String() -} - -// GetTransfersAsString returns the active transfers as string -func (c *ConnectionStatus) GetTransfersAsString() string { - result := "" - for _, t := range c.Transfers { - if result != "" { - result += ". " - } - result += t.getConnectionTransferAsString() - } - return result -} - // ActiveQuotaScan defines an active quota scan for a user type ActiveQuotaScan struct { // Username to which the quota scan refers diff --git a/internal/common/common_test.go b/internal/common/common_test.go index 76f5004b..c6bb3684 100644 --- a/internal/common/common_test.go +++ b/internal/common/common_test.go @@ -23,7 +23,6 @@ import ( "os/exec" "path/filepath" "runtime" - "strings" "sync" "testing" "time" @@ -892,23 +891,10 @@ func TestConnectionStatus(t *testing.T) { assert.Len(t, stats, 3) for _, stat := range stats { assert.Equal(t, stat.Username, username) - assert.True(t, strings.HasPrefix(stat.GetConnectionInfo(), stat.Protocol)) - assert.True(t, strings.HasPrefix(stat.GetConnectionDuration(), "00:")) if stat.ConnectionID == "SFTP_id1" { assert.Len(t, stat.Transfers, 2) - assert.Greater(t, len(stat.GetTransfersAsString()), 0) - for _, tr := range stat.Transfers { - if tr.OperationType == operationDownload { - assert.True(t, strings.HasPrefix(tr.getConnectionTransferAsString(), "DL")) - } else if tr.OperationType == operationUpload { - assert.True(t, strings.HasPrefix(tr.getConnectionTransferAsString(), "UL")) - } - } } else if stat.ConnectionID == "DAV_id3" { assert.Len(t, stat.Transfers, 1) - assert.Greater(t, len(stat.GetTransfersAsString()), 0) - } else { - assert.Equal(t, 0, len(stat.GetTransfersAsString())) } } diff --git a/internal/httpd/server.go b/internal/httpd/server.go index e7c7f29f..50156bbd 100644 --- a/internal/httpd/server.go +++ b/internal/httpd/server.go @@ -1710,6 +1710,8 @@ func (s *httpdServer) setupWebAdminRoutes() { Delete(webGroupPath+"/{name}", deleteGroup) router.With(s.checkPerm(dataprovider.PermAdminViewConnections), s.refreshCookie). Get(webConnectionsPath, s.handleWebGetConnections) + router.With(s.checkPerm(dataprovider.PermAdminViewConnections), s.refreshCookie). + Get(webConnectionsPath+jsonAPISuffix, getActiveConnections) router.With(s.checkPerm(dataprovider.PermAdminManageFolders), s.refreshCookie). Get(webFoldersPath, s.handleWebGetFolders) router.With(s.checkPerm(dataprovider.PermAdminManageFolders), compressor.Handler, s.refreshCookie). diff --git a/internal/httpd/webadmin.go b/internal/httpd/webadmin.go index f3837054..e976ef7f 100644 --- a/internal/httpd/webadmin.go +++ b/internal/httpd/webadmin.go @@ -99,7 +99,6 @@ const ( templateMFA = "mfa.html" templateSetup = "adminsetup.html" pageAdminsTitle = "Admins" - pageConnectionsTitle = "Connections" pageStatusTitle = "Status" pageEventRulesTitle = "Event rules" pageEventActionsTitle = "Event actions" @@ -185,11 +184,6 @@ type eventActionsPage struct { Actions []dataprovider.BaseEventAction } -type connectionsPage struct { - basePage - Connections []common.ConnectionStatus -} - type statusPage struct { basePage Status *ServicesStatus @@ -412,7 +406,7 @@ func loadAdminTemplates(templatesPath string) { filepath.Join(templatesPath, templateCommonDir, templateChangePwd), } connectionsPaths := []string{ - filepath.Join(templatesPath, templateCommonDir, templateCommonCSS), + filepath.Join(templatesPath, templateCommonDir, templateCommonBase), filepath.Join(templatesPath, templateAdminDir, templateBase), filepath.Join(templatesPath, templateAdminDir, templateConnections), } @@ -3336,12 +3330,8 @@ func (s *httpdServer) handleWebGetConnections(w http.ResponseWriter, r *http.Req s.renderForbiddenPage(w, r, util.NewI18nError(errInvalidTokenClaims, util.I18nErrorInvalidToken)) return } - connectionStats := common.Connections.GetStats(claims.Role) - connectionStats = append(connectionStats, getNodesConnections(claims.Username, claims.Role)...) - data := connectionsPage{ - basePage: s.getBasePageData(pageConnectionsTitle, webConnectionsPath, r), - Connections: connectionStats, - } + + data := s.getBasePageData(util.I18nSessionsTitle, webConnectionsPath, r) renderAdminTemplate(w, templateConnections, data) } diff --git a/internal/sftpd/internal_test.go b/internal/sftpd/internal_test.go index 79962c09..fa93743f 100644 --- a/internal/sftpd/internal_test.go +++ b/internal/sftpd/internal_test.go @@ -1077,19 +1077,6 @@ func TestCommandGetFsError(t *testing.T) { assert.Error(t, err) } -func TestGetConnectionInfo(t *testing.T) { - c := common.ConnectionStatus{ - Username: "test_user", - ConnectionID: "123", - ClientVersion: "client", - RemoteAddress: "127.0.0.1:1234", - Protocol: common.ProtocolSSH, - Command: "sha1sum /test_file_ftp.dat", - } - info := c.GetConnectionInfo() - assert.Contains(t, info, "sha1sum /test_file_ftp.dat") -} - func TestSCPFileMode(t *testing.T) { mode := getFileModeAsString(0, true) assert.Equal(t, "0755", mode) @@ -1832,42 +1819,6 @@ func TestTransferFailingReader(t *testing.T) { assert.Len(t, connection.GetTransfers(), 0) } -func TestConnectionStatusStruct(t *testing.T) { - var transfers []common.ConnectionTransfer - transferUL := common.ConnectionTransfer{ - OperationType: "upload", - StartTime: util.GetTimeAsMsSinceEpoch(time.Now()), - Size: 123, - VirtualPath: "/test.upload", - } - transferDL := common.ConnectionTransfer{ - OperationType: "download", - StartTime: util.GetTimeAsMsSinceEpoch(time.Now()), - Size: 123, - VirtualPath: "/test.download", - } - transfers = append(transfers, transferUL) - transfers = append(transfers, transferDL) - c := common.ConnectionStatus{ - Username: "test", - ConnectionID: "123", - ClientVersion: "fakeClient-1.0.0", - RemoteAddress: "127.0.0.1:1234", - ConnectionTime: util.GetTimeAsMsSinceEpoch(time.Now()), - LastActivity: util.GetTimeAsMsSinceEpoch(time.Now()), - Protocol: "SFTP", - Transfers: transfers, - } - durationString := c.GetConnectionDuration() - assert.NotEqual(t, 0, len(durationString)) - - transfersString := c.GetTransfersAsString() - assert.NotEqual(t, 0, len(transfersString)) - - connInfo := c.GetConnectionInfo() - assert.NotEqual(t, 0, len(connInfo)) -} - func TestConfigsFromProvider(t *testing.T) { err := dataprovider.UpdateConfigs(nil, "", "", "") assert.NoError(t, err) diff --git a/internal/util/i18n.go b/internal/util/i18n.go index 0dd79ad2..747a0e64 100644 --- a/internal/util/i18n.go +++ b/internal/util/i18n.go @@ -58,6 +58,7 @@ const ( I18nConfigsTitle = "title.configs" I18nOAuth2Title = "title.oauth2_success" I18nOAuth2ErrorTitle = "title.oauth2_error" + I18nSessionsTitle = "title.connections" I18nErrorSetupInstallCode = "setup.install_code_mismatch" I18nInvalidAuth = "general.invalid_auth_request" I18nError429Message = "general.error429" diff --git a/static/locales/en/translation.json b/static/locales/en/translation.json index 9f02bf8e..6de42e43 100644 --- a/static/locales/en/translation.json +++ b/static/locales/en/translation.json @@ -31,7 +31,7 @@ "users": "Users", "groups": "Groups", "folders": "Virtual folders", - "connections": "Active sessions", + "connections": "Active connections", "event_manager": "Event Manager", "event_rules": "Rules", "event_actions": "Actions", @@ -219,7 +219,9 @@ "duplicated_name": "The specified name already exists", "permissions_required": "Permissions are required", "backup_ok": "Backup successfully restored", - "configs_saved": "Configurations has been successfully updated" + "configs_saved": "Configurations has been successfully updated", + "protocol": "Protocol", + "refresh": "Refresh" }, "fs": { "view_file": "View file \"{{- path}}\"", @@ -670,5 +672,19 @@ }, "admin": { "role_permissions": "A role admin cannot have the following permissions: {{val}}" + }, + "connections": { + "view_manage": "View and manage connections", + "started": "Started", + "remote_address": "Remote address", + "last_activity": "Last activity", + "disconnect_confirm_btn": "Yes, disconnect", + "disconnect_confirm": "Do you want to disconnect the selected connection? This action is irreversible", + "disconnect_ko": "Unable to disconnect the selected connection", + "upload": "UL: \"{{- path}}\"", + "download": "DL: \"{{- path}}\"", + "upload_info": "$t(connections.upload). Size: {{- size}}. Speed: {{- speed}}", + "download_info": "$t(connections.download). Size: {{- size}}. Speed: {{- speed}}", + "client": "Client: {{- val}}" } } \ No newline at end of file diff --git a/static/locales/it/translation.json b/static/locales/it/translation.json index a210eb3b..aa8c3f6f 100644 --- a/static/locales/it/translation.json +++ b/static/locales/it/translation.json @@ -31,7 +31,7 @@ "users": "Utenti", "groups": "Gruppi", "folders": "Cartelle virtuali", - "connections": "Sessioni attive", + "connections": "Connessioni attive", "event_manager": "Gestione eventi", "event_rules": "Regole", "event_actions": "Azioni", @@ -219,7 +219,9 @@ "duplicated_name": "Il nome specificato esiste già", "permissions_required": "I permessi sono obbligatori", "backup_ok": "Backup ripristinato correttamente", - "configs_saved": "Configurazioni aggiornate" + "configs_saved": "Configurazioni aggiornate", + "protocol": "Protocollo", + "refresh": "Aggiorna" }, "fs": { "view_file": "Visualizza file \"{{- path}}\"", @@ -670,5 +672,19 @@ }, "admin": { "role_permissions": "Un amministratore di ruolo non può avere le seguenti autorizzazioni: {{val}}" + }, + "connections": { + "view_manage": "Visualizza e gestisci connessioni attive", + "started": "Iniziata", + "remote_address": "Indirizzo remoto", + "last_activity": "Ultima attività", + "disconnect_confirm_btn": "Si, disconnetti", + "disconnect_confirm": "Vuoi disconnettere la connessione selezionata? Questa azione è irreversibile", + "disconnect_ko": "Impossibile disconnettere la connessione selezionata", + "upload": "UL: \"{{- path}}\"", + "download": "DL: \"{{- path}}\"", + "upload_info": "$t(connections.upload). Dimensione: {{- size}}. Velocità: {{- speed}}", + "download_info": "$t(connections.download). Dimensione: {{- size}}. Velocità: {{- speed}}", + "client": "Client: {{- val}}" } } \ No newline at end of file diff --git a/templates/common/base.html b/templates/common/base.html index 92913120..00697a26 100644 --- a/templates/common/base.html +++ b/templates/common/base.html @@ -81,6 +81,11 @@ explicit grant from the SFTPGo Team (support@sftpgo.com). +' '+(e?'KMGTPEZY'[--e]+'iB':'Bytes') } + function humanizeSpeed(a,b,c,d,e){ + return (b=Math,c=b.log,d=1024,e=c(a)/c(d)|0,a/b.pow(d,e)).toFixed(1) + +' '+(e?'KMGTPEZY'[--e]+'B/s':'Bytes/s') + } + function initRepeaterItems() { let repeaterDeleteButtons = document.querySelectorAll('[data-repeater-delete]'); let repeaterCreateButtons = document.querySelectorAll('[data-repeater-create]'); diff --git a/templates/webadmin/connections.html b/templates/webadmin/connections.html index 4d209609..a1c0d806 100644 --- a/templates/webadmin/connections.html +++ b/templates/webadmin/connections.html @@ -1,221 +1,376 @@ {{template "base" .}} -{{define "title"}}{{.Title}}{{end}} +{{- define "extra_css"}} + +{{- end}} -{{define "extra_css"}} - - - - - -{{end}} - -{{define "page_body"}} - - - -
-
-
View and manage connections
+{{- define "page_body"}} +
+
+

View and manage connections

-
-
- +
+
+ + Loading... +
+
+
+
+ + +
+ +
+ +
- + - - - - + + + + + + + - - {{range .Connections}} - - - - - - - - - {{end}} - +
ID NodeUsernameTimeInfoTransfersUsernameStartedRemote addressProtocolLast activityInfo
{{.ConnectionID}}{{.Node}}{{.Username}}{{.GetConnectionDuration}}{{.GetConnectionInfo}}{{.GetTransfersAsString}}
+
-{{end}} +{{- end}} -{{define "dialog"}} - -{{end}} +{{- define "extra_js"}} + + - - - - - - - - - -{{end}} \ No newline at end of file +{{- end}} \ No newline at end of file diff --git a/templates/webadmin/folders.html b/templates/webadmin/folders.html index 53b7979d..75678d7e 100644 --- a/templates/webadmin/folders.html +++ b/templates/webadmin/folders.html @@ -77,7 +77,6 @@ explicit grant from the SFTPGo Team (support@sftpgo.com). {{- end}}
- @@ -284,7 +283,6 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).