mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-21 23:20:24 +00:00
WebAdmin: allow to search and export event logs
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
91e4a54385
commit
75d911f29e
22 changed files with 1438 additions and 94 deletions
30
go.mod
30
go.mod
|
@ -24,17 +24,17 @@ require (
|
|||
github.com/fclairamb/ftpserverlib v0.20.1-0.20221012093027-95be4ae0c9a6
|
||||
github.com/fclairamb/go-log v0.4.1
|
||||
github.com/go-acme/lego/v4 v4.9.1
|
||||
github.com/go-chi/chi/v5 v5.0.8-0.20221018120124-e5529d9db4d3
|
||||
github.com/go-chi/chi/v5 v5.0.8
|
||||
github.com/go-chi/jwtauth/v5 v5.1.0
|
||||
github.com/go-chi/render v1.0.2
|
||||
github.com/go-sql-driver/mysql v1.7.0
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/hashicorp/go-hclog v1.3.1
|
||||
github.com/hashicorp/go-hclog v1.4.0
|
||||
github.com/hashicorp/go-plugin v1.4.6
|
||||
github.com/hashicorp/go-retryablehttp v0.7.1
|
||||
github.com/jackc/pgx/v5 v5.1.1
|
||||
github.com/jackc/pgx/v5 v5.2.0
|
||||
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
|
||||
github.com/klauspost/compress v1.15.12
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.8
|
||||
|
@ -51,7 +51,7 @@ require (
|
|||
github.com/rs/cors v1.8.3-0.20220619195839-da52b0701de5
|
||||
github.com/rs/xid v1.4.0
|
||||
github.com/rs/zerolog v1.28.0
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221203095324-2feef3600930
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e
|
||||
github.com/shirou/gopsutil/v3 v3.22.11
|
||||
github.com/spf13/afero v1.9.3
|
||||
github.com/spf13/cobra v1.6.1
|
||||
|
@ -67,19 +67,19 @@ require (
|
|||
go.uber.org/automaxprocs v1.5.1
|
||||
gocloud.dev v0.27.0
|
||||
golang.org/x/crypto v0.3.0
|
||||
golang.org/x/net v0.2.0
|
||||
golang.org/x/oauth2 v0.2.0
|
||||
golang.org/x/sys v0.2.0
|
||||
golang.org/x/time v0.2.0
|
||||
google.golang.org/api v0.103.0
|
||||
golang.org/x/net v0.4.0
|
||||
golang.org/x/oauth2 v0.3.0
|
||||
golang.org/x/sys v0.3.0
|
||||
golang.org/x/time v0.3.0
|
||||
google.golang.org/api v0.104.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
cloud.google.com/go v0.107.0 // indirect
|
||||
cloud.google.com/go/compute v1.13.0 // indirect
|
||||
cloud.google.com/go/compute v1.14.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.2 // indirect
|
||||
cloud.google.com/go/iam v0.7.0 // indirect
|
||||
cloud.google.com/go/iam v0.8.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.1 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
|
||||
|
@ -119,7 +119,7 @@ require (
|
|||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.1 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.2 // indirect
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/lestrrat-go/blackmagic v1.0.1 // indirect
|
||||
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||
|
@ -154,11 +154,11 @@ require (
|
|||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/mod v0.7.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
golang.org/x/tools v0.3.0 // indirect
|
||||
golang.org/x/text v0.5.0 // indirect
|
||||
golang.org/x/tools v0.4.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd // indirect
|
||||
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
|
58
go.sum
58
go.sum
|
@ -50,8 +50,8 @@ cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6m
|
|||
cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
|
||||
cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
|
||||
cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
|
||||
cloud.google.com/go/compute v1.13.0 h1:AYrLkB8NPdDRslNp4Jxmzrhdr03fUAIDbiGFjLWowoU=
|
||||
cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE=
|
||||
cloud.google.com/go/compute v1.14.0 h1:hfm2+FfxVmnRlh6LpB7cg1ZNU+5edAHmW679JePztk0=
|
||||
cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo=
|
||||
cloud.google.com/go/compute/metadata v0.2.2 h1:aWKAjYaBaOSrpKl57+jnS/3fJRQnxL7TvR/u1VVbt6k=
|
||||
cloud.google.com/go/compute/metadata v0.2.2/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
|
@ -60,8 +60,8 @@ cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqCl
|
|||
cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY=
|
||||
cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c=
|
||||
cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
|
||||
cloud.google.com/go/iam v0.7.0 h1:k4MuwOsS7zGJJ+QfZ5vBK8SgHBAvYN/23BWsiihJ1vs=
|
||||
cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg=
|
||||
cloud.google.com/go/iam v0.8.0 h1:E2osAkZzxI/+8pZcxVLcDtAQx/u+hZXVryUaYQ5O0Kk=
|
||||
cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE=
|
||||
cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA=
|
||||
cloud.google.com/go/kms v1.6.0 h1:OWRZzrPmOZUzurjI2FBGtgY2mB1WaJkqhw6oIwSj0Yg=
|
||||
cloud.google.com/go/longrunning v0.3.0 h1:NjljC+FYPV3uh5/OwWT6pVU+doBqMg2x/rZlE+CamDs=
|
||||
|
@ -609,8 +609,8 @@ github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwv
|
|||
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
|
||||
github.com/go-acme/lego/v4 v4.9.1 h1:n9Z5MQwANeGSQKlVE3bEh9SDvAySK9oVYOKCGCESqQE=
|
||||
github.com/go-acme/lego/v4 v4.9.1/go.mod h1:g3JRUyWS3L/VObpp4bCxzJftKyf/Wba8QrSSnoiqjg4=
|
||||
github.com/go-chi/chi/v5 v5.0.8-0.20221018120124-e5529d9db4d3 h1:qzwVVqrbdP93ZaSHy0yWQRYnig+t+j1OxnVtEs8SFuQ=
|
||||
github.com/go-chi/chi/v5 v5.0.8-0.20221018120124-e5529d9db4d3/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
|
||||
github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/jwtauth/v5 v5.1.0 h1:wJyf2YZ/ohPvNJBwPOzZaQbyzwgMZZceE1m8FOzXLeA=
|
||||
github.com/go-chi/jwtauth/v5 v5.1.0/go.mod h1:MA93hc1au3tAQwCKry+fI4LqJ5MIVN4XSsglOo+lSc8=
|
||||
github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
|
||||
|
@ -914,8 +914,8 @@ github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/S
|
|||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v0.12.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
|
||||
github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo=
|
||||
github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I=
|
||||
github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.2.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
|
@ -1014,8 +1014,8 @@ github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQ
|
|||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
||||
github.com/jackc/pgx/v4 v4.16.0/go.mod h1:N0A9sFdWzkw/Jy1lwoiB64F2+ugFZi987zRxcPez/wI=
|
||||
github.com/jackc/pgx/v4 v4.16.1/go.mod h1:SIhx0D5hoADaiXZVyv+3gSm3LCIIINTVO0PficsvWGQ=
|
||||
github.com/jackc/pgx/v5 v5.1.1 h1:pZD79K1SYv8wc2HmCQA6VdmRQi7/OtCfv9bM3WAXUYA=
|
||||
github.com/jackc/pgx/v5 v5.1.1/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
|
||||
github.com/jackc/pgx/v5 v5.2.0 h1:NdPpngX0Y6z6XDFKqmFQaE+bCtkqzvQIOt1wvBlAqs8=
|
||||
github.com/jackc/pgx/v5 v5.2.0/go.mod h1:Ptn7zmohNsWEsdxRawMzk3gaKma2obW+NWTnKa0S4nk=
|
||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
||||
|
@ -1064,8 +1064,9 @@ github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47e
|
|||
github.com/klauspost/compress v1.15.12 h1:YClS/PImqYbn+UILDnqxQCZ3RehC9N318SU3kElDUEM=
|
||||
github.com/klauspost/compress v1.15.12/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
|
||||
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.1 h1:U33DW0aiEj633gHYw3LoDNfkDiYnE5Q8M/TKJn2f2jI=
|
||||
github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/klauspost/cpuid/v2 v2.2.2 h1:xPMwiykqNK9VK0NYC3+jTMYv9I6Vl3YdjZgPZKG3zO0=
|
||||
github.com/klauspost/cpuid/v2 v2.2.2/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/kolo/xmlrpc v0.0.0-20201022064351-38db28db192b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
|
@ -1449,8 +1450,8 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg
|
|||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||
github.com/secsy/goftp v0.0.0-20200609142545-aa2de14babf4 h1:PT+ElG/UUFMfqy5HrxJxNzj3QBOf7dZwupeVC+mG1Lo=
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221203095324-2feef3600930 h1:znJ52fQBSAQhacaQvZAfkpTioqpcutDREM/H8NttKzU=
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221203095324-2feef3600930/go.mod h1:S2S/Q9fgUpXmL11YoCCt0hyCkEwH1LzQM/6QVsbUCFg=
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e h1:F3G/BReUSU8TX6Kmk0moQgQAk9Ouiv2I+pg//o1IR6U=
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e/go.mod h1:S2S/Q9fgUpXmL11YoCCt0hyCkEwH1LzQM/6QVsbUCFg=
|
||||
github.com/shirou/gopsutil/v3 v3.22.11 h1:kxsPKS+Eeo+VnEQ2XCaGJepeP6KY53QoRTETx3+1ndM=
|
||||
github.com/shirou/gopsutil/v3 v3.22.11/go.mod h1:xl0EeL4vXJ+hQMAGN8B9VFpxukEMA0XdevQOe5MZ1oY=
|
||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
||||
|
@ -1814,8 +1815,9 @@ golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug
|
|||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20220802222814-0bcc04d9c69b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
|
@ -1842,8 +1844,8 @@ golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7Lm
|
|||
golang.org/x/oauth2 v0.0.0-20220628200809-02e64fa58f26/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
|
||||
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
|
||||
golang.org/x/oauth2 v0.2.0 h1:GtQkldQ9m7yvzCL1V+LrYow3Khe0eJH0w7RbX/VbaIU=
|
||||
golang.org/x/oauth2 v0.2.0/go.mod h1:Cwn6afJ8jrQwYMxQDTpISoXmXW9I6qF6vDeuuoX3Ibs=
|
||||
golang.org/x/oauth2 v0.3.0 h1:6l90koy8/LaBLmLu8jpHeHexzMwEita0zFfYlggy2F8=
|
||||
golang.org/x/oauth2 v0.3.0/go.mod h1:rQrIauxkUhJ6CuwEXwymO2/eh4xz2ZWF1nBkcxS+tGk=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@ -2005,14 +2007,15 @@ golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0 h1:z85xZCsEl7bi/KwbNADeBYoOP0++7W1ipu+aGnpwzRM=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.3.0 h1:qoo4akIqOcDME5bhc/NgxUdovd6BSS2uMsVjB56q1xI=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -2022,8 +2025,9 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|||
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
@ -2036,8 +2040,8 @@ golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxb
|
|||
golang.org/x/time v0.0.0-20220224211638-0e9765cccd65/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.2.0 h1:52I/1L54xyEQAYdtcSuxtiT84KGYTBGXwayxmIpNJhE=
|
||||
golang.org/x/time v0.2.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
|
||||
golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
|
@ -2122,8 +2126,8 @@ golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
|||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.3.0 h1:SrNbZl6ECOS1qFzgTdQfWXZM9XBkiA6tkFrH9YSTPHM=
|
||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
golang.org/x/tools v0.4.0 h1:7mTAgkunk3fr4GAloyyCasadO6h9zSsQZbwvcaIciV4=
|
||||
golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
|
||||
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
@ -2183,8 +2187,8 @@ google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6F
|
|||
google.golang.org/api v0.86.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
|
||||
google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
|
||||
google.golang.org/api v0.91.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
|
||||
google.golang.org/api v0.103.0 h1:9yuVqlu2JCvcLg9p8S3fcFLZij8EPSyvODIY1rkMizQ=
|
||||
google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0=
|
||||
google.golang.org/api v0.104.0 h1:KBfmLRqdZEbwQleFlSLnzpQJwhjpmNOk4cKQIBDZ9mg=
|
||||
google.golang.org/api v0.104.0/go.mod h1:JCspTXJbBxa5ySXw4UgUqVer7DfVxbvc/CTUFqAED5U=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
|
@ -2295,8 +2299,8 @@ google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljW
|
|||
google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
|
||||
google.golang.org/genproto v0.0.0-20220802133213-ce4fa296bf78/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
|
||||
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd h1:OjndDrsik+Gt+e6fs45z9AxiewiKyLKYpA45W5Kpkks=
|
||||
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=
|
||||
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37 h1:jmIfw8+gSvXcZSgaFAGyInDXeWzUhvYH57G/5GKMn70=
|
||||
google.golang.org/genproto v0.0.0-20221207170731-23e4bf6bdc37/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
|
|
|
@ -15,9 +15,13 @@
|
|||
package httpd
|
||||
|
||||
import (
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/sftpgo/sdk/plugin/eventsearcher"
|
||||
|
||||
|
@ -88,7 +92,6 @@ func getFsSearchParamsFromRequest(r *http.Request) (eventsearcher.FsEventSearch,
|
|||
}
|
||||
s.FsProvider = val
|
||||
}
|
||||
s.IP = r.URL.Query().Get("ip")
|
||||
s.SSHCmd = r.URL.Query().Get("ssh_cmd")
|
||||
s.Bucket = r.URL.Query().Get("bucket")
|
||||
s.Endpoint = r.URL.Query().Get("endpoint")
|
||||
|
@ -132,6 +135,14 @@ func searchFsEvents(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
filters.Role = getRoleFilterForEventSearch(r, claims.Role)
|
||||
|
||||
if getBoolQueryParam(r, "csv_export") {
|
||||
filters.Limit = 100
|
||||
if err := exportFsEvents(w, &filters); err != nil {
|
||||
panic(http.ErrAbortHandler)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
data, _, _, err := plugin.Handler.SearchFsEvents(&filters)
|
||||
if err != nil {
|
||||
sendAPIResponse(w, r, err, "", getRespStatus(err))
|
||||
|
@ -150,13 +161,21 @@ func searchProviderEvents(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
filters, err := getProviderSearchParamsFromRequest(r)
|
||||
if err != nil {
|
||||
var filters eventsearcher.ProviderEventSearch
|
||||
if filters, err = getProviderSearchParamsFromRequest(r); err != nil {
|
||||
sendAPIResponse(w, r, err, "", getRespStatus(err))
|
||||
return
|
||||
}
|
||||
filters.Role = getRoleFilterForEventSearch(r, claims.Role)
|
||||
|
||||
if getBoolQueryParam(r, "csv_export") {
|
||||
filters.Limit = 100
|
||||
if err := exportProviderEvents(w, &filters); err != nil {
|
||||
panic(http.ErrAbortHandler)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
data, _, _, err := plugin.Handler.SearchProviderEvents(&filters)
|
||||
if err != nil {
|
||||
sendAPIResponse(w, r, err, "", getRespStatus(err))
|
||||
|
@ -167,9 +186,159 @@ func searchProviderEvents(w http.ResponseWriter, r *http.Request) {
|
|||
w.Write(data) //nolint:errcheck
|
||||
}
|
||||
|
||||
func exportFsEvents(w http.ResponseWriter, filters *eventsearcher.FsEventSearch) error {
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=fslogs-%s.csv", time.Now().Format("2006-01-02T15-04-05")))
|
||||
w.Header().Set("Content-Type", "text/csv")
|
||||
w.Header().Set("Accept-Ranges", "none")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
csvWriter := csv.NewWriter(w)
|
||||
ev := fsEvent{}
|
||||
err := csvWriter.Write(ev.getCSVHeader())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
results := make([]fsEvent, 0, filters.Limit)
|
||||
for {
|
||||
data, _, _, err := plugin.Handler.SearchFsEvents(filters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(data, &results); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, event := range results {
|
||||
if err := csvWriter.Write(event.getCSVData()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(results) == 0 || len(results) < filters.Limit {
|
||||
break
|
||||
}
|
||||
filters.StartTimestamp = results[len(results)-1].Timestamp
|
||||
filters.ExcludeIDs = []string{results[len(results)-1].ID}
|
||||
results = nil
|
||||
}
|
||||
csvWriter.Flush()
|
||||
return csvWriter.Error()
|
||||
}
|
||||
|
||||
func exportProviderEvents(w http.ResponseWriter, filters *eventsearcher.ProviderEventSearch) error {
|
||||
w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=providerlogs-%s.csv", time.Now().Format("2006-01-02T15-04-05")))
|
||||
w.Header().Set("Content-Type", "text/csv")
|
||||
w.Header().Set("Accept-Ranges", "none")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
ev := providerEvent{}
|
||||
csvWriter := csv.NewWriter(w)
|
||||
err := csvWriter.Write(ev.getCSVHeader())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
results := make([]providerEvent, 0, filters.Limit)
|
||||
for {
|
||||
data, _, _, err := plugin.Handler.SearchProviderEvents(filters)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := json.Unmarshal(data, &results); err != nil {
|
||||
return err
|
||||
}
|
||||
for _, event := range results {
|
||||
if err := csvWriter.Write(event.getCSVData()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(results) == 0 || len(results) < filters.Limit {
|
||||
break
|
||||
}
|
||||
filters.StartTimestamp = results[len(results)-1].Timestamp
|
||||
filters.ExcludeIDs = []string{results[len(results)-1].ID}
|
||||
results = nil
|
||||
}
|
||||
csvWriter.Flush()
|
||||
return csvWriter.Error()
|
||||
}
|
||||
|
||||
func getRoleFilterForEventSearch(r *http.Request, defaultValue string) string {
|
||||
if defaultValue != "" {
|
||||
return defaultValue
|
||||
}
|
||||
return r.URL.Query().Get("role")
|
||||
}
|
||||
|
||||
type fsEvent struct {
|
||||
ID string `json:"id"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Action string `json:"action"`
|
||||
Username string `json:"username"`
|
||||
FsPath string `json:"fs_path"`
|
||||
FsTargetPath string `json:"fs_target_path,omitempty"`
|
||||
VirtualPath string `json:"virtual_path"`
|
||||
VirtualTargetPath string `json:"virtual_target_path,omitempty"`
|
||||
SSHCmd string `json:"ssh_cmd,omitempty"`
|
||||
FileSize int64 `json:"file_size,omitempty"`
|
||||
Status int `json:"status"`
|
||||
Protocol string `json:"protocol"`
|
||||
IP string `json:"ip,omitempty"`
|
||||
SessionID string `json:"session_id"`
|
||||
FsProvider int `json:"fs_provider"`
|
||||
Bucket string `json:"bucket,omitempty"`
|
||||
Endpoint string `json:"endpoint,omitempty"`
|
||||
OpenFlags int `json:"open_flags,omitempty"`
|
||||
Role string `json:"role,omitempty"`
|
||||
InstanceID string `json:"instance_id,omitempty"`
|
||||
}
|
||||
|
||||
func (e *fsEvent) getCSVHeader() []string {
|
||||
return []string{"Time", "Action", "Path", "Size", "Status", "User", "Protocol",
|
||||
"IP", "SSH command"}
|
||||
}
|
||||
|
||||
func (e *fsEvent) getCSVData() []string {
|
||||
timestamp := time.Unix(0, e.Timestamp).UTC()
|
||||
var pathInfo strings.Builder
|
||||
pathInfo.Write([]byte(e.VirtualPath))
|
||||
if e.VirtualTargetPath != "" {
|
||||
pathInfo.WriteString(" => ")
|
||||
pathInfo.WriteString(e.VirtualTargetPath)
|
||||
}
|
||||
var status string
|
||||
switch e.Status {
|
||||
case 1:
|
||||
status = "OK"
|
||||
case 2:
|
||||
status = "KO"
|
||||
case 3:
|
||||
status = "Quota exceeded"
|
||||
}
|
||||
var fileSize string
|
||||
if e.FileSize > 0 {
|
||||
fileSize = util.ByteCountIEC(e.FileSize)
|
||||
}
|
||||
return []string{timestamp.Format(time.RFC3339), e.Action, pathInfo.String(),
|
||||
fileSize, status, e.Username, e.Protocol, e.IP, e.SSHCmd}
|
||||
}
|
||||
|
||||
type providerEvent struct {
|
||||
ID string `json:"id"`
|
||||
Timestamp int64 `json:"timestamp"`
|
||||
Action string `json:"action"`
|
||||
Username string `json:"username"`
|
||||
IP string `json:"ip,omitempty"`
|
||||
ObjectType string `json:"object_type"`
|
||||
ObjectName string `json:"object_name"`
|
||||
ObjectData []byte `json:"object_data"`
|
||||
Role string `json:"role,omitempty"`
|
||||
InstanceID string `json:"instance_id,omitempty"`
|
||||
}
|
||||
|
||||
func (e *providerEvent) getCSVHeader() []string {
|
||||
return []string{"Time", "Action", "Object Type", "Object Name", "User", "IP"}
|
||||
}
|
||||
|
||||
func (e *providerEvent) getCSVData() []string {
|
||||
timestamp := time.Unix(0, e.Timestamp).UTC()
|
||||
return []string{timestamp.Format(time.RFC3339), e.Action, e.ObjectType, e.ObjectName,
|
||||
e.Username, e.IP}
|
||||
}
|
||||
|
|
|
@ -139,6 +139,9 @@ const (
|
|||
webTemplateFolderDefault = "/web/admin/template/folder"
|
||||
webDefenderPathDefault = "/web/admin/defender"
|
||||
webDefenderHostsPathDefault = "/web/admin/defender/hosts"
|
||||
webEventsPathDefault = "/web/admin/events"
|
||||
webEventsFsSearchPathDefault = "/web/admin/events/fs"
|
||||
webEventsProviderSearchPathDefault = "/web/admin/events/provider"
|
||||
webClientLoginPathDefault = "/web/client/login"
|
||||
webClientOIDCLoginPathDefault = "/web/client/oidclogin"
|
||||
webClientTwoFactorPathDefault = "/web/client/twofactor"
|
||||
|
@ -226,6 +229,9 @@ var (
|
|||
webTemplateUser string
|
||||
webTemplateFolder string
|
||||
webDefenderPath string
|
||||
webEventsPath string
|
||||
webEventsFsSearchPath string
|
||||
webEventsProviderSearchPath string
|
||||
webDefenderHostsPath string
|
||||
webClientLoginPath string
|
||||
webClientOIDCLoginPath string
|
||||
|
@ -1025,6 +1031,9 @@ func updateWebAdminURLs(baseURL string) {
|
|||
webTemplateFolder = path.Join(baseURL, webTemplateFolderDefault)
|
||||
webDefenderHostsPath = path.Join(baseURL, webDefenderHostsPathDefault)
|
||||
webDefenderPath = path.Join(baseURL, webDefenderPathDefault)
|
||||
webEventsPath = path.Join(baseURL, webEventsPathDefault)
|
||||
webEventsFsSearchPath = path.Join(baseURL, webEventsFsSearchPathDefault)
|
||||
webEventsProviderSearchPath = path.Join(baseURL, webEventsProviderSearchPathDefault)
|
||||
webStaticFilesPath = path.Join(baseURL, webStaticFilesPathDefault)
|
||||
webOpenAPIPath = path.Join(baseURL, webOpenAPIPathDefault)
|
||||
}
|
||||
|
|
|
@ -162,6 +162,7 @@ const (
|
|||
webAdminEventActionPath = "/web/admin/eventaction"
|
||||
webAdminRolesPath = "/web/admin/roles"
|
||||
webAdminRolePath = "/web/admin/role"
|
||||
webEventsPath = "/web/admin/events"
|
||||
webBasePathClient = "/web/client"
|
||||
webClientLoginPath = "/web/client/login"
|
||||
webClientFilesPath = "/web/client/files"
|
||||
|
@ -8791,6 +8792,8 @@ func TestWebUserTwoFactorLogin(t *testing.T) {
|
|||
func TestSearchEvents(t *testing.T) {
|
||||
token, err := getJWTAPITokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
|
||||
assert.NoError(t, err)
|
||||
webToken, err := getJWTWebTokenFromTestServer(defaultTokenAuthUser, defaultTokenAuthPass)
|
||||
assert.NoError(t, err)
|
||||
|
||||
req, err := http.NewRequest(http.MethodGet, fsEventsPath+"?limit=10&order=ASC&fs_provider=0", nil)
|
||||
assert.NoError(t, err)
|
||||
|
@ -8817,13 +8820,32 @@ func TestSearchEvents(t *testing.T) {
|
|||
err = json.Unmarshal(rr.Body.Bytes(), &events)
|
||||
assert.NoError(t, err)
|
||||
assert.Len(t, events, 1)
|
||||
|
||||
// CSV export
|
||||
req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?limit=10&order=ASC&csv_export=true", nil)
|
||||
assert.NoError(t, err)
|
||||
setBearerForReq(req, token)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusOK, rr)
|
||||
assert.Equal(t, "text/csv", rr.Header().Get("Content-Type"))
|
||||
// the test eventsearcher plugin returns error if start_timestamp < 0
|
||||
req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?start_timestamp=-1&end_timestamp=123456&statuses=1,2", nil)
|
||||
assert.NoError(t, err)
|
||||
setBearerForReq(req, token)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusInternalServerError, rr)
|
||||
// CSV export with error
|
||||
exportFunc := func() {
|
||||
defer func() {
|
||||
rcv := recover()
|
||||
assert.Equal(t, http.ErrAbortHandler, rcv)
|
||||
}()
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?start_timestamp=-1&csv_export=true", nil)
|
||||
assert.NoError(t, err)
|
||||
setBearerForReq(req, token)
|
||||
rr = executeRequest(req)
|
||||
}
|
||||
exportFunc()
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, fsEventsPath+"?limit=a", nil)
|
||||
assert.NoError(t, err)
|
||||
|
@ -8847,6 +8869,13 @@ func TestSearchEvents(t *testing.T) {
|
|||
assert.True(t, ok, field)
|
||||
}
|
||||
}
|
||||
// CSV export
|
||||
req, err = http.NewRequest(http.MethodGet, providerEventsPath+"?csv_export=true", nil)
|
||||
assert.NoError(t, err)
|
||||
setBearerForReq(req, token)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusOK, rr)
|
||||
assert.Equal(t, "text/csv", rr.Header().Get("Content-Type"))
|
||||
|
||||
// the test eventsearcher plugin returns error if start_timestamp < 0
|
||||
req, err = http.NewRequest(http.MethodGet, providerEventsPath+"?start_timestamp=-1", nil)
|
||||
|
@ -8854,6 +8883,19 @@ func TestSearchEvents(t *testing.T) {
|
|||
setBearerForReq(req, token)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusInternalServerError, rr)
|
||||
// CSV export with error
|
||||
exportFunc = func() {
|
||||
defer func() {
|
||||
rcv := recover()
|
||||
assert.Equal(t, http.ErrAbortHandler, rcv)
|
||||
}()
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, providerEventsPath+"?start_timestamp=-1&csv_export=true", nil)
|
||||
assert.NoError(t, err)
|
||||
setBearerForReq(req, token)
|
||||
rr = executeRequest(req)
|
||||
}
|
||||
exportFunc()
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, providerEventsPath+"?limit=2000", nil)
|
||||
assert.NoError(t, err)
|
||||
|
@ -8890,6 +8932,12 @@ func TestSearchEvents(t *testing.T) {
|
|||
setBearerForReq(req, token)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusBadRequest, rr)
|
||||
|
||||
req, err = http.NewRequest(http.MethodGet, webEventsPath, nil)
|
||||
assert.NoError(t, err)
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusOK, rr)
|
||||
}
|
||||
|
||||
func TestMFAErrors(t *testing.T) {
|
||||
|
|
|
@ -2911,6 +2911,20 @@ func TestEventRoleFilter(t *testing.T) {
|
|||
assert.Equal(t, "role1", role)
|
||||
}
|
||||
|
||||
func TestEventsCSV(t *testing.T) {
|
||||
e := fsEvent{
|
||||
Status: 1,
|
||||
}
|
||||
data := e.getCSVData()
|
||||
assert.Equal(t, "OK", data[4])
|
||||
e.Status = 2
|
||||
data = e.getCSVData()
|
||||
assert.Equal(t, "KO", data[4])
|
||||
e.Status = 3
|
||||
data = e.getCSVData()
|
||||
assert.Equal(t, "Quota exceeded", data[4])
|
||||
}
|
||||
|
||||
func isSharedProviderSupported() bool {
|
||||
// SQLite shares the implementation with other SQL-based provider but it makes no sense
|
||||
// to use it outside test cases
|
||||
|
|
|
@ -1662,6 +1662,12 @@ func (s *httpdServer) setupWebAdminRoutes() {
|
|||
s.handleWebUpdateRolePost)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminManageRoles), verifyCSRFHeader).
|
||||
Delete(webAdminRolePath+"/{name}", deleteRole)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), s.refreshCookie).Get(webEventsPath,
|
||||
s.handleWebGetEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler, s.refreshCookie).
|
||||
Get(webEventsFsSearchPath, searchFsEvents)
|
||||
router.With(s.checkPerm(dataprovider.PermAdminViewEvents), compressor.Handler, s.refreshCookie).
|
||||
Get(webEventsProviderSearchPath, searchProviderEvents)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,6 +36,7 @@ import (
|
|||
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
|
||||
"github.com/drakkan/sftpgo/v2/internal/kms"
|
||||
"github.com/drakkan/sftpgo/v2/internal/mfa"
|
||||
"github.com/drakkan/sftpgo/v2/internal/plugin"
|
||||
"github.com/drakkan/sftpgo/v2/internal/smtp"
|
||||
"github.com/drakkan/sftpgo/v2/internal/util"
|
||||
"github.com/drakkan/sftpgo/v2/internal/version"
|
||||
|
@ -86,6 +87,7 @@ const (
|
|||
templateEventAction = "eventaction.html"
|
||||
templateRoles = "roles.html"
|
||||
templateRole = "role.html"
|
||||
templateEvents = "events.html"
|
||||
templateMessage = "message.html"
|
||||
templateStatus = "status.html"
|
||||
templateLogin = "login.html"
|
||||
|
@ -108,6 +110,7 @@ const (
|
|||
pageChangePwdTitle = "Change password"
|
||||
pageMaintenanceTitle = "Maintenance"
|
||||
pageDefenderTitle = "Defender"
|
||||
pageEventsTitle = "Logs"
|
||||
pageForgotPwdTitle = "SFTPGo Admin - Forgot password"
|
||||
pageResetPwdTitle = "SFTPGo Admin - Reset password"
|
||||
pageSetupTitle = "Create first admin user"
|
||||
|
@ -135,6 +138,7 @@ type basePage struct {
|
|||
FolderURL string
|
||||
FolderTemplateURL string
|
||||
DefenderURL string
|
||||
EventsURL string
|
||||
LogoutURL string
|
||||
ProfileURL string
|
||||
ChangePwdURL string
|
||||
|
@ -160,10 +164,12 @@ type basePage struct {
|
|||
StatusTitle string
|
||||
MaintenanceTitle string
|
||||
DefenderTitle string
|
||||
EventsTitle string
|
||||
Version string
|
||||
CSRFToken string
|
||||
IsEventManagerPage bool
|
||||
HasDefender bool
|
||||
HasSearcher bool
|
||||
HasExternalLogin bool
|
||||
LoggedAdmin *dataprovider.Admin
|
||||
Branding UIBranding
|
||||
|
@ -349,6 +355,12 @@ type eventRulePage struct {
|
|||
IsShared bool
|
||||
}
|
||||
|
||||
type eventsPage struct {
|
||||
basePage
|
||||
FsEventsSearchURL string
|
||||
ProviderEventsSearchURL string
|
||||
}
|
||||
|
||||
type messagePage struct {
|
||||
basePage
|
||||
Error string
|
||||
|
@ -505,6 +517,11 @@ func loadAdminTemplates(templatesPath string) {
|
|||
filepath.Join(templatesPath, templateAdminDir, templateBase),
|
||||
filepath.Join(templatesPath, templateAdminDir, templateRole),
|
||||
}
|
||||
eventsPaths := []string{
|
||||
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS),
|
||||
filepath.Join(templatesPath, templateAdminDir, templateBase),
|
||||
filepath.Join(templatesPath, templateAdminDir, templateEvents),
|
||||
}
|
||||
|
||||
fsBaseTpl := template.New("fsBaseTemplate").Funcs(template.FuncMap{
|
||||
"ListFSProviders": func() []sdk.FilesystemProvider {
|
||||
|
@ -543,6 +560,7 @@ func loadAdminTemplates(templatesPath string) {
|
|||
resetPwdTmpl := util.LoadTemplate(nil, resetPwdPaths...)
|
||||
rolesTmpl := util.LoadTemplate(nil, rolesPaths...)
|
||||
roleTmpl := util.LoadTemplate(nil, rolePaths...)
|
||||
eventsTmpl := util.LoadTemplate(nil, eventsPaths...)
|
||||
|
||||
adminTemplates[templateUsers] = usersTmpl
|
||||
adminTemplates[templateUser] = userTmpl
|
||||
|
@ -572,6 +590,7 @@ func loadAdminTemplates(templatesPath string) {
|
|||
adminTemplates[templateResetPassword] = resetPwdTmpl
|
||||
adminTemplates[templateRoles] = rolesTmpl
|
||||
adminTemplates[templateRole] = roleTmpl
|
||||
adminTemplates[templateEvents] = eventsTmpl
|
||||
}
|
||||
|
||||
func isEventManagerResource(currentURL string) bool {
|
||||
|
@ -609,6 +628,7 @@ func (s *httpdServer) getBasePageData(title, currentURL string, r *http.Request)
|
|||
FolderURL: webFolderPath,
|
||||
FolderTemplateURL: webTemplateFolder,
|
||||
DefenderURL: webDefenderPath,
|
||||
EventsURL: webEventsPath,
|
||||
LogoutURL: webLogoutPath,
|
||||
ProfileURL: webAdminProfilePath,
|
||||
ChangePwdURL: webChangeAdminPwdPath,
|
||||
|
@ -636,10 +656,12 @@ func (s *httpdServer) getBasePageData(title, currentURL string, r *http.Request)
|
|||
StatusTitle: pageStatusTitle,
|
||||
MaintenanceTitle: pageMaintenanceTitle,
|
||||
DefenderTitle: pageDefenderTitle,
|
||||
EventsTitle: pageEventsTitle,
|
||||
Version: version.GetAsString(),
|
||||
LoggedAdmin: getAdminFromToken(r),
|
||||
IsEventManagerPage: isEventManagerResource(currentURL),
|
||||
HasDefender: common.Config.DefenderConfig.Enabled,
|
||||
HasSearcher: plugin.Handler.HasSearcher(),
|
||||
HasExternalLogin: isLoggedInWithOIDC(r),
|
||||
CSRFToken: csrfToken,
|
||||
Branding: s.binding.Branding.WebAdmin,
|
||||
|
@ -3636,3 +3658,14 @@ func (s *httpdServer) handleWebUpdateRolePost(w http.ResponseWriter, r *http.Req
|
|||
}
|
||||
http.Redirect(w, r, webAdminRolesPath, http.StatusSeeOther)
|
||||
}
|
||||
|
||||
func (s *httpdServer) handleWebGetEvents(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||
|
||||
data := eventsPage{
|
||||
basePage: s.getBasePageData(pageEventsTitle, webEventsPath, r),
|
||||
FsEventsSearchURL: webEventsFsSearchPath,
|
||||
ProviderEventsSearchURL: webEventsProviderSearchPath,
|
||||
}
|
||||
renderAdminTemplate(w, templateEvents, data)
|
||||
}
|
||||
|
|
|
@ -291,6 +291,11 @@ func (m *Manager) NotifyProviderEvent(event *notifier.ProviderEvent, object Rend
|
|||
}
|
||||
}
|
||||
|
||||
// HasSearcher returns true if an event searcher plugin is defined
|
||||
func (m *Manager) HasSearcher() bool {
|
||||
return m.hasSearcher
|
||||
}
|
||||
|
||||
// SearchFsEvents returns the filesystem events matching the specified filters
|
||||
func (m *Manager) SearchFsEvents(searchFilters *eventsearcher.FsEventSearch) ([]byte, []string, []string, error) {
|
||||
if !m.hasSearcher {
|
||||
|
|
|
@ -2305,6 +2305,13 @@ paths:
|
|||
type: string
|
||||
description: 'User role. Empty or missing means omit this filter. Ignored if the admin has a role'
|
||||
required: false
|
||||
- in: query
|
||||
name: csv_export
|
||||
schema:
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
description: 'If enabled, events are exported as a CSV file'
|
||||
- in: query
|
||||
name: limit
|
||||
schema:
|
||||
|
@ -2313,7 +2320,7 @@ paths:
|
|||
maximum: 1000
|
||||
default: 100
|
||||
required: false
|
||||
description: 'The maximum number of items to return. Max value is 500, default is 100'
|
||||
description: 'The maximum number of items to return. Max value is 1000, default is 100'
|
||||
- in: query
|
||||
name: order
|
||||
required: false
|
||||
|
@ -2333,6 +2340,9 @@ paths:
|
|||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/FsEvent'
|
||||
text/csv:
|
||||
schema:
|
||||
type: string
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
|
@ -2429,6 +2439,13 @@ paths:
|
|||
type: string
|
||||
description: 'Admin role. Empty or missing means omit this filter. Ignored if the admin has a role'
|
||||
required: false
|
||||
- in: query
|
||||
name: csv_export
|
||||
schema:
|
||||
type: boolean
|
||||
default: false
|
||||
required: false
|
||||
description: 'If enabled, events are exported as a CSV file'
|
||||
- in: query
|
||||
name: limit
|
||||
schema:
|
||||
|
@ -2437,7 +2454,7 @@ paths:
|
|||
maximum: 1000
|
||||
default: 100
|
||||
required: false
|
||||
description: 'The maximum number of items to return. Max value is 500, default is 100'
|
||||
description: 'The maximum number of items to return. Max value is 1000, default is 100'
|
||||
- in: query
|
||||
name: order
|
||||
required: false
|
||||
|
@ -2457,6 +2474,9 @@ paths:
|
|||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/ProviderEvent'
|
||||
text/csv:
|
||||
schema:
|
||||
type: string
|
||||
'400':
|
||||
$ref: '#/components/responses/BadRequest'
|
||||
'401':
|
||||
|
@ -4616,16 +4636,19 @@ components:
|
|||
- HTTP
|
||||
- HTTPShare
|
||||
- DataRetention
|
||||
- EventAction
|
||||
- OIDC
|
||||
description: |
|
||||
Protocols:
|
||||
* `SSH` - SSH commands
|
||||
* `SFTP` - SFTP protocol
|
||||
* `SCP` - SCP protocol
|
||||
* `FTP` - plain FTP and FTPES/FTPS
|
||||
* `DAV` - WebDAV
|
||||
* `HTTP` - WebClient/REST API
|
||||
* `HTTPShare` - the event is generated in a public share
|
||||
* `DataRetention` - the event is generated by a data retention check
|
||||
* `EventAction` - the event is generated by an EventManager action
|
||||
* `OIDC` - OpenID Connect
|
||||
WebClientOptions:
|
||||
type: string
|
||||
|
|
410
static/vendor/daterangepicker/daterangepicker.css
vendored
Normal file
410
static/vendor/daterangepicker/daterangepicker.css
vendored
Normal file
|
@ -0,0 +1,410 @@
|
|||
.daterangepicker {
|
||||
position: absolute;
|
||||
color: inherit;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ddd;
|
||||
width: 278px;
|
||||
max-width: none;
|
||||
padding: 0;
|
||||
margin-top: 7px;
|
||||
top: 100px;
|
||||
left: 20px;
|
||||
z-index: 3001;
|
||||
display: none;
|
||||
font-family: arial;
|
||||
font-size: 15px;
|
||||
line-height: 1em;
|
||||
}
|
||||
|
||||
.daterangepicker:before, .daterangepicker:after {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
border-bottom-color: rgba(0, 0, 0, 0.2);
|
||||
content: '';
|
||||
}
|
||||
|
||||
.daterangepicker:before {
|
||||
top: -7px;
|
||||
border-right: 7px solid transparent;
|
||||
border-left: 7px solid transparent;
|
||||
border-bottom: 7px solid #ccc;
|
||||
}
|
||||
|
||||
.daterangepicker:after {
|
||||
top: -6px;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid #fff;
|
||||
border-left: 6px solid transparent;
|
||||
}
|
||||
|
||||
.daterangepicker.opensleft:before {
|
||||
right: 9px;
|
||||
}
|
||||
|
||||
.daterangepicker.opensleft:after {
|
||||
right: 10px;
|
||||
}
|
||||
|
||||
.daterangepicker.openscenter:before {
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.daterangepicker.openscenter:after {
|
||||
left: 0;
|
||||
right: 0;
|
||||
width: 0;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.daterangepicker.opensright:before {
|
||||
left: 9px;
|
||||
}
|
||||
|
||||
.daterangepicker.opensright:after {
|
||||
left: 10px;
|
||||
}
|
||||
|
||||
.daterangepicker.drop-up {
|
||||
margin-top: -7px;
|
||||
}
|
||||
|
||||
.daterangepicker.drop-up:before {
|
||||
top: initial;
|
||||
bottom: -7px;
|
||||
border-bottom: initial;
|
||||
border-top: 7px solid #ccc;
|
||||
}
|
||||
|
||||
.daterangepicker.drop-up:after {
|
||||
top: initial;
|
||||
bottom: -6px;
|
||||
border-bottom: initial;
|
||||
border-top: 6px solid #fff;
|
||||
}
|
||||
|
||||
.daterangepicker.single .daterangepicker .ranges, .daterangepicker.single .drp-calendar {
|
||||
float: none;
|
||||
}
|
||||
|
||||
.daterangepicker.single .drp-selected {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.daterangepicker.show-calendar .drp-calendar {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.daterangepicker.show-calendar .drp-buttons {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.daterangepicker.auto-apply .drp-buttons {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar {
|
||||
display: none;
|
||||
max-width: 270px;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.left {
|
||||
padding: 8px 0 8px 8px;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.right {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.single .calendar-table {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-table .next span, .daterangepicker .calendar-table .prev span {
|
||||
color: #fff;
|
||||
border: solid black;
|
||||
border-width: 0 2px 2px 0;
|
||||
border-radius: 0;
|
||||
display: inline-block;
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-table .next span {
|
||||
transform: rotate(-45deg);
|
||||
-webkit-transform: rotate(-45deg);
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-table .prev span {
|
||||
transform: rotate(135deg);
|
||||
-webkit-transform: rotate(135deg);
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-table th, .daterangepicker .calendar-table td {
|
||||
white-space: nowrap;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
min-width: 32px;
|
||||
width: 32px;
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
font-size: 12px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent;
|
||||
white-space: nowrap;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-table {
|
||||
border: 1px solid #fff;
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-table table {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.daterangepicker td.available:hover, .daterangepicker th.available:hover {
|
||||
background-color: #eee;
|
||||
border-color: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.daterangepicker td.week, .daterangepicker th.week {
|
||||
font-size: 80%;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.daterangepicker td.off, .daterangepicker td.off.in-range, .daterangepicker td.off.start-date, .daterangepicker td.off.end-date {
|
||||
background-color: #fff;
|
||||
border-color: transparent;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.daterangepicker td.in-range {
|
||||
background-color: #ebf4f8;
|
||||
border-color: transparent;
|
||||
color: #000;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.daterangepicker td.start-date {
|
||||
border-radius: 4px 0 0 4px;
|
||||
}
|
||||
|
||||
.daterangepicker td.end-date {
|
||||
border-radius: 0 4px 4px 0;
|
||||
}
|
||||
|
||||
.daterangepicker td.start-date.end-date {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.daterangepicker td.active, .daterangepicker td.active:hover {
|
||||
background-color: #357ebd;
|
||||
border-color: transparent;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.daterangepicker th.month {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.daterangepicker td.disabled, .daterangepicker option.disabled {
|
||||
color: #999;
|
||||
cursor: not-allowed;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.daterangepicker select.monthselect, .daterangepicker select.yearselect {
|
||||
font-size: 12px;
|
||||
padding: 1px;
|
||||
height: auto;
|
||||
margin: 0;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.daterangepicker select.monthselect {
|
||||
margin-right: 2%;
|
||||
width: 56%;
|
||||
}
|
||||
|
||||
.daterangepicker select.yearselect {
|
||||
width: 40%;
|
||||
}
|
||||
|
||||
.daterangepicker select.hourselect, .daterangepicker select.minuteselect, .daterangepicker select.secondselect, .daterangepicker select.ampmselect {
|
||||
width: 50px;
|
||||
margin: 0 auto;
|
||||
background: #eee;
|
||||
border: 1px solid #eee;
|
||||
padding: 2px;
|
||||
outline: 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-time {
|
||||
text-align: center;
|
||||
margin: 4px auto 0 auto;
|
||||
line-height: 30px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.daterangepicker .calendar-time select.disabled {
|
||||
color: #ccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-buttons {
|
||||
clear: both;
|
||||
text-align: right;
|
||||
padding: 8px;
|
||||
border-top: 1px solid #ddd;
|
||||
display: none;
|
||||
line-height: 12px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-selected {
|
||||
display: inline-block;
|
||||
font-size: 12px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-buttons .btn {
|
||||
margin-left: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: bold;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.daterangepicker.show-ranges.single.rtl .drp-calendar.left {
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.daterangepicker.show-ranges.single.ltr .drp-calendar.left {
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.daterangepicker.show-ranges.rtl .drp-calendar.right {
|
||||
border-right: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.daterangepicker.show-ranges.ltr .drp-calendar.left {
|
||||
border-left: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges {
|
||||
float: none;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.daterangepicker.show-calendar .ranges {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges ul {
|
||||
list-style: none;
|
||||
margin: 0 auto;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges li {
|
||||
font-size: 12px;
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges li:hover {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges li.active {
|
||||
background-color: #08c;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Larger Screen Styling */
|
||||
@media (min-width: 564px) {
|
||||
.daterangepicker {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges ul {
|
||||
width: 140px;
|
||||
}
|
||||
|
||||
.daterangepicker.single .ranges ul {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.daterangepicker.single .drp-calendar.left {
|
||||
clear: none;
|
||||
}
|
||||
|
||||
.daterangepicker.single .ranges, .daterangepicker.single .drp-calendar {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.daterangepicker {
|
||||
direction: ltr;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.left {
|
||||
clear: left;
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.left .calendar-table {
|
||||
border-right: none;
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.right {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.right .calendar-table {
|
||||
border-left: none;
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.left .calendar-table {
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges, .daterangepicker .drp-calendar {
|
||||
float: left;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 730px) {
|
||||
.daterangepicker .ranges {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.daterangepicker .ranges {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.daterangepicker.rtl .ranges {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.daterangepicker .drp-calendar.left {
|
||||
clear: none !important;
|
||||
}
|
||||
}
|
8
static/vendor/daterangepicker/daterangepicker.min.js
vendored
Normal file
8
static/vendor/daterangepicker/daterangepicker.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -137,6 +137,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
</li>
|
||||
{{end}}
|
||||
|
||||
{{ if and .HasSearcher (.LoggedAdmin.HasPermission "view_events")}}
|
||||
<li class="nav-item {{if eq .CurrentURL .EventsURL}}active{{end}}">
|
||||
<a class="nav-link" href="{{.EventsURL}}">
|
||||
<i class="fas fa-clipboard-list"></i>
|
||||
<span>{{.EventsTitle}}</span></a>
|
||||
</li>
|
||||
{{end}}
|
||||
|
||||
{{ if .LoggedAdmin.HasPermission "manage_system"}}
|
||||
<li class="nav-item {{if eq .CurrentURL .MaintenanceURL}}active{{end}}">
|
||||
<a class="nav-link" href="{{.MaintenanceURL}}">
|
||||
|
|
607
templates/webadmin/events.html
Normal file
607
templates/webadmin/events.html
Normal file
|
@ -0,0 +1,607 @@
|
|||
<!--
|
||||
Copyright (C) 2019-2022 Nicola Murino
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as published
|
||||
by the Free Software Foundation, version 3.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
{{template "base" .}}
|
||||
|
||||
{{define "title"}}{{.Title}}{{end}}
|
||||
|
||||
{{define "extra_css"}}
|
||||
<link href="{{.StaticURL}}/vendor/datatables/dataTables.bootstrap4.min.css" rel="stylesheet">
|
||||
<link href="{{.StaticURL}}/vendor/datatables/buttons.bootstrap4.min.css" rel="stylesheet">
|
||||
<link href="{{.StaticURL}}/vendor/datatables/fixedHeader.bootstrap4.min.css" rel="stylesheet">
|
||||
<link href="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.css" rel="stylesheet">
|
||||
<link href="{{.StaticURL}}/vendor/datatables/select.bootstrap4.min.css" rel="stylesheet">
|
||||
<link href="{{.StaticURL}}/vendor/bootstrap-select/css/bootstrap-select.min.css" rel="stylesheet">
|
||||
<link href="{{.StaticURL}}/vendor/daterangepicker/daterangepicker.css" rel="stylesheet">
|
||||
{{end}}
|
||||
|
||||
{{define "page_body"}}
|
||||
<div id="errorMsg" class="card mb-4 border-left-warning" style="display: none;">
|
||||
<div id="errorTxt" class="card-body text-form-error"></div>
|
||||
</div>
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Search logs</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-3">
|
||||
<select class="form-control selectpicker" id="idEventType" name="events_type" onchange="onEventChanged(this.value)">
|
||||
<option value="1" selected>Fs events</option>
|
||||
<option value="2">Provider events</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<select class="form-control selectpicker" id="idActions" name="actions" title="Actions" multiple>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<input type="text" class="form-control" id="idUsername" name="username" placeholder="Username">
|
||||
</div>
|
||||
<div class="form-group col-md-3">
|
||||
<input type="text" class="form-control" id="idIp" name="ip" placeholder="IP address">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-4">
|
||||
<select class="form-control selectpicker fs-events" id="idProtocols" name="protocols" title="Protocols" multiple>
|
||||
<option value="SFTP">SFTP</option>
|
||||
<option value="SCP">SCP</option>
|
||||
<option value="SSH">SSH</option>
|
||||
<option value="FTP">FTP</option>
|
||||
<option value="DAV">DAV</option>
|
||||
<option value="HTTP">HTTP</option>
|
||||
<option value="OIDC">OIDC</option>
|
||||
<option value="HTTPShare">HTTPShare</option>
|
||||
<option value="DataRetention">DataRetention</option>
|
||||
<option value="EventAction">EventAction</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<select class="form-control selectpicker fs-events" id="idStatuses" name="statuses" title="Statuses" multiple>
|
||||
<option value="1">OK</option>
|
||||
<option value="2">KO</option>
|
||||
<option value="3">Quota exceeded</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<div class="input-group">
|
||||
<input type="text" id="dateTimeRange" class="form-control bg-light border-0" aria-describedby="search-button">
|
||||
<div class="input-group-append">
|
||||
<button id="search-button" class="btn btn-primary" type="button" onclick="onSearchClicked()">
|
||||
<i class="fas fa-search fa-sm"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group col-md-12">
|
||||
<button class="btn btn-secondary mt-1 mb-1 px-5" type="button" onclick="onExportClicked()">Export</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive fs-events">
|
||||
<table class="table table-hover nowrap" id="dataTableFs" width="100%" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Time</th>
|
||||
<th>Action</th>
|
||||
<th>Path</th>
|
||||
<th>User</th>
|
||||
<th>Proto</th>
|
||||
<th>IP</th>
|
||||
<th>Info</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive provider-events">
|
||||
<table class="table table-hover nowrap" id="dataTableProvider" width="100%" cellspacing="0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Time</th>
|
||||
<th>Action</th>
|
||||
<th>Object</th>
|
||||
<th>User</th>
|
||||
<th>IP</th>
|
||||
</tr>
|
||||
</thead>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div id="paginationContainer" class="m-4 d-none">
|
||||
<nav aria-label="Pagination">
|
||||
<ul class="pagination justify-content-end">
|
||||
<li id="pageItemPrev" class="page-item disabled"><a id="pagePrevious" class="page-link" href="#" onclick="prevClicked()">Previous</a></li>
|
||||
<li id="pageItemNext" class="page-item disabled"><a id="pageNext" class="page-link" href="#" onclick="nextClicked()">Next</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
|
||||
{{define "extra_js"}}
|
||||
<script src="{{.StaticURL}}/vendor/datatables/jquery.dataTables.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.bootstrap4.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.buttons.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/buttons.bootstrap4.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/buttons.colVis.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.fixedHeader.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.responsive.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/dataTables.select.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/datatables/ellipsis.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/bootstrap-select/js/bootstrap-select.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/moment/js/moment.min.js"></script>
|
||||
<script src="{{.StaticURL}}/vendor/daterangepicker/daterangepicker.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
let dateFn = $.fn.dataTable.render.datetime();
|
||||
let isFsDataTableInitialized = false;
|
||||
let isProviderDataTableInitialized = false;
|
||||
let pageSize = 20;
|
||||
const paginationData = new Map();
|
||||
|
||||
function fileSizeIEC(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]+'iB':'Bytes')
|
||||
}
|
||||
|
||||
function resetPagination() {
|
||||
$('#pageItemPrev').addClass("disabled");
|
||||
$('#pageItemNext').addClass("disabled");
|
||||
$('#paginationContainer').addClass("d-none");
|
||||
paginationData.delete("firstId");
|
||||
paginationData.delete("firstTs");
|
||||
paginationData.delete("lastId");
|
||||
paginationData.delete("lastTs");
|
||||
paginationData.set("prevClicked",false);
|
||||
paginationData.set("nextClicked",false);
|
||||
}
|
||||
|
||||
function prevClicked(){
|
||||
paginationData.set("prevClicked",true);
|
||||
paginationData.set("nextClicked",false);
|
||||
doSearch();
|
||||
}
|
||||
|
||||
function nextClicked(){
|
||||
paginationData.set("prevClicked",false);
|
||||
paginationData.set("nextClicked",true);
|
||||
doSearch();
|
||||
}
|
||||
|
||||
function handleResponseData(data) {
|
||||
let length = data.length;
|
||||
let isNext = paginationData.get("nextClicked");
|
||||
let isPrev = paginationData.get("prevClicked");
|
||||
|
||||
if (length > pageSize) {
|
||||
data.pop();
|
||||
length--;
|
||||
if (isPrev || isNext){
|
||||
$('#pageItemPrev').removeClass("disabled");
|
||||
}
|
||||
$('#pageItemNext').removeClass("disabled");
|
||||
} else {
|
||||
if (isPrev){
|
||||
$('#pageItemPrev').addClass("disabled");
|
||||
$('#pageItemNext').removeClass("disabled");
|
||||
} else if (isNext){
|
||||
$('#pageItemPrev').removeClass("disabled");
|
||||
$('#pageItemNext').addClass("disabled");
|
||||
} else {
|
||||
$('#pageItemNext').addClass("disabled");
|
||||
}
|
||||
}
|
||||
if (isPrev){
|
||||
data = data.reverse();
|
||||
}
|
||||
if (length > 0){
|
||||
paginationData.set("lastId",data[0].id);
|
||||
paginationData.set("lastTs",data[0].timestamp);
|
||||
paginationData.set("firstId",data[length-1].id);
|
||||
paginationData.set("firstTs",data[length-1].timestamp);
|
||||
$('#paginationContainer').removeClass("d-none");
|
||||
} else {
|
||||
resetPagination();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function onExportClicked() {
|
||||
paginationData.set("prevClicked",false);
|
||||
paginationData.set("nextClicked",false);
|
||||
let exportURL = getSearchURL(true);
|
||||
var ts = new Date().getTime().toString();
|
||||
window.open(`${exportURL}&_=${ts}`);
|
||||
}
|
||||
|
||||
function onSearchClicked() {
|
||||
resetPagination();
|
||||
doSearch();
|
||||
}
|
||||
|
||||
function doSearch() {
|
||||
let eventType = $('#idEventType').val();
|
||||
let table;
|
||||
if (eventType == 1){
|
||||
if (!isFsDataTableInitialized){
|
||||
initFsDatatable();
|
||||
return;
|
||||
}
|
||||
table = $('#dataTableFs').DataTable();
|
||||
} else {
|
||||
if (!isProviderDataTableInitialized){
|
||||
initProviderDatatable();
|
||||
return;
|
||||
}
|
||||
table = $('#dataTableProvider').DataTable();
|
||||
}
|
||||
table.clear().draw();
|
||||
table.ajax.url(getSearchURL(false)).load();
|
||||
}
|
||||
|
||||
function getSearchURL(csvExport) {
|
||||
let url = "";
|
||||
let eventType = $('#idEventType').val();
|
||||
let order = "DESC";
|
||||
let limit = pageSize + 1;
|
||||
if (csvExport){
|
||||
order = "ASC";
|
||||
}
|
||||
if (eventType == 1){
|
||||
url = "{{.FsEventsSearchURL}}?limit="+limit;
|
||||
let protocols = [];
|
||||
$('#idProtocols').find('option:selected').each(function(){
|
||||
protocols.push($(this).val());
|
||||
});
|
||||
if (protocols.length > 0){
|
||||
url+="&protocols="+encodeURIComponent(String(protocols));
|
||||
}
|
||||
let statuses = [];
|
||||
$('#idStatuses').find('option:selected').each(function(){
|
||||
statuses.push($(this).val());
|
||||
});
|
||||
if (statuses.length > 0){
|
||||
url+="&statuses="+encodeURIComponent(String(statuses));
|
||||
}
|
||||
} else {
|
||||
url = "{{.ProviderEventsSearchURL}}?limit="+limit;
|
||||
}
|
||||
let actions = [];
|
||||
$('#idActions').find('option:selected').each(function(){
|
||||
actions.push($(this).val());
|
||||
});
|
||||
if (actions.length > 0){
|
||||
url+="&actions="+encodeURIComponent(String(actions));
|
||||
}
|
||||
let username = $('#idUsername').val();
|
||||
if (username){
|
||||
url+="&username="+encodeURIComponent(username);
|
||||
}
|
||||
let ip = $('#idIp').val();
|
||||
if (ip){
|
||||
url+="&ip="+encodeURIComponent(ip);
|
||||
}
|
||||
let drp = $('#dateTimeRange').data('daterangepicker');
|
||||
let excludeIds = [];
|
||||
let start_ts;
|
||||
if (!csvExport && paginationData.get("prevClicked") && paginationData.has("lastId") && paginationData.has("lastTs")){
|
||||
order = "ASC";
|
||||
start_ts = paginationData.get("lastTs");
|
||||
excludeIds.push(paginationData.get("lastId"));
|
||||
} else {
|
||||
start_ts = drp.startDate.valueOf()*1000000;
|
||||
}
|
||||
let end_ts;
|
||||
if (!csvExport && paginationData.get("nextClicked") && paginationData.has("firstId") && paginationData.has("firstTs")){
|
||||
end_ts = paginationData.get("firstTs");
|
||||
excludeIds.push(paginationData.get("firstId"));
|
||||
} else {
|
||||
end_ts = drp.endDate.valueOf()*1000000;
|
||||
}
|
||||
url+="&start_timestamp="+encodeURIComponent(start_ts);
|
||||
url+="&end_timestamp="+encodeURIComponent(end_ts);
|
||||
if (excludeIds.length > 0){
|
||||
url+="&exclude_ids="+encodeURIComponent(String(excludeIds));
|
||||
}
|
||||
url+="&order="+order;
|
||||
if (csvExport){
|
||||
url+="&csv_export=true";
|
||||
}
|
||||
return url;
|
||||
}
|
||||
|
||||
function initProviderDatatable(){
|
||||
let tableProvider = $('#dataTableProvider').DataTable({
|
||||
"ajax": {
|
||||
"url": getSearchURL(false),
|
||||
"dataSrc": handleResponseData,
|
||||
"error": function ($xhr, textStatus, errorThrown) {
|
||||
$(".dataTables_processing").hide();
|
||||
var txt = "Failed to get provider events";
|
||||
if ($xhr) {
|
||||
var json = $xhr.responseJSON;
|
||||
if (json) {
|
||||
if (json.message){
|
||||
txt += ": " + json.message;
|
||||
} else {
|
||||
txt += ": " + json.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
$('#errorTxt').text(txt);
|
||||
$('#errorMsg').show();
|
||||
setTimeout(function () {
|
||||
$('#errorMsg').hide();
|
||||
}, 10000);
|
||||
}
|
||||
},
|
||||
"deferRender": true,
|
||||
"processing": true,
|
||||
"columns": [
|
||||
{ "data": "id" },
|
||||
{
|
||||
"data": "timestamp",
|
||||
"render": function (data, type, row) {
|
||||
if (type === 'display') {
|
||||
return dateFn(data/1000000,type);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
},
|
||||
{
|
||||
"data": "action"
|
||||
},
|
||||
{
|
||||
"data": "object_type",
|
||||
"render": function (data, type, row) {
|
||||
if (type === 'display') {
|
||||
let ellipsisFn = $.fn.dataTable.render.ellipsis(70, true);
|
||||
return ellipsisFn(`${data}: ${row["object_name"]}`,type);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
},
|
||||
{
|
||||
"data": "username",
|
||||
"defaultContent": ""
|
||||
},
|
||||
{
|
||||
"data": "ip",
|
||||
"defaultContent": ""
|
||||
}
|
||||
],
|
||||
"buttons": [],
|
||||
"lengthChange": false,
|
||||
"columnDefs": [
|
||||
{
|
||||
"targets": [0],
|
||||
"visible": false,
|
||||
"searchable": false
|
||||
},
|
||||
],
|
||||
"responsive": true,
|
||||
"searching": false,
|
||||
"paging": false,
|
||||
"info": false,
|
||||
"ordering": false,
|
||||
"language": {
|
||||
"loadingRecords": "",
|
||||
"emptyTable": "No logs found"
|
||||
}
|
||||
});
|
||||
|
||||
new $.fn.dataTable.FixedHeader(tableProvider);
|
||||
|
||||
isProviderDataTableInitialized = true;
|
||||
}
|
||||
|
||||
function initFsDatatable(){
|
||||
let tableFs = $('#dataTableFs').DataTable({
|
||||
"ajax": {
|
||||
"url": getSearchURL(false),
|
||||
"dataSrc": handleResponseData,
|
||||
"error": function ($xhr, textStatus, errorThrown) {
|
||||
$(".dataTables_processing").hide();
|
||||
var txt = "Failed to get filesystem events";
|
||||
if ($xhr) {
|
||||
let json = $xhr.responseJSON;
|
||||
if (json) {
|
||||
if (json.message){
|
||||
txt += ": " + json.message;
|
||||
} else {
|
||||
txt += ": " + json.error;
|
||||
}
|
||||
}
|
||||
}
|
||||
$('#errorTxt').text(txt);
|
||||
$('#errorMsg').show();
|
||||
setTimeout(function () {
|
||||
$('#errorMsg').hide();
|
||||
}, 10000);
|
||||
}
|
||||
},
|
||||
"deferRender": true,
|
||||
"processing": true,
|
||||
"columns": [
|
||||
{ "data": "id" },
|
||||
{
|
||||
"data": "timestamp",
|
||||
"render": function (data, type, row) {
|
||||
if (type === 'display') {
|
||||
return dateFn(data/1000000,type);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
},
|
||||
{
|
||||
"data": "action"
|
||||
},
|
||||
{
|
||||
"data": "virtual_path",
|
||||
"render": function (data, type, row) {
|
||||
if (type === 'display') {
|
||||
let ellipsisFn = $.fn.dataTable.render.ellipsis(70, true);
|
||||
if (row["virtual_target_path"]){
|
||||
return ellipsisFn(`${data} => ${row["virtual_target_path"]}`,type);
|
||||
}
|
||||
return ellipsisFn(data,type);
|
||||
}
|
||||
return data;
|
||||
}
|
||||
},
|
||||
{
|
||||
"data": "username",
|
||||
"defaultContent": ""
|
||||
},
|
||||
{
|
||||
"data": "protocol",
|
||||
"defaultContent": ""
|
||||
},
|
||||
{
|
||||
"data": "ip",
|
||||
"defaultContent": ""
|
||||
},
|
||||
{
|
||||
"data": "status",
|
||||
"render": function (data, type, row) {
|
||||
if (type === 'display') {
|
||||
let info = 'OK';
|
||||
if (data == 2){
|
||||
info = 'KO';
|
||||
} else if (data == 3){
|
||||
info = 'Quota exceeded'
|
||||
}
|
||||
if (row["file_size"]){
|
||||
let humanSize = fileSizeIEC(row["file_size"]);
|
||||
info+=`. ${humanSize}`
|
||||
}
|
||||
if (row["ssh_cmd"]){
|
||||
info+=`. Command: ${row["ssh_cmd"]}`
|
||||
}
|
||||
return info;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
],
|
||||
"buttons": [],
|
||||
"lengthChange": false,
|
||||
"columnDefs": [
|
||||
{
|
||||
"targets": [0],
|
||||
"visible": false,
|
||||
"searchable": false
|
||||
},
|
||||
],
|
||||
"responsive": true,
|
||||
"searching": false,
|
||||
"paging": false,
|
||||
"info": false,
|
||||
"ordering": false,
|
||||
"language": {
|
||||
"loadingRecords": "",
|
||||
"emptyTable": "No logs found"
|
||||
}
|
||||
});
|
||||
|
||||
new $.fn.dataTable.FixedHeader(tableFs);
|
||||
isFsDataTableInitialized = true;
|
||||
}
|
||||
|
||||
function selectFsEvents(){
|
||||
let idActions = $('#idActions');
|
||||
idActions.selectpicker('deselectAll');
|
||||
idActions.find('option').remove();
|
||||
idActions.find('li').remove();
|
||||
idActions.append($('<option>').val('upload').text('Upload'));
|
||||
idActions.append($('<option>').val('download').text('Download'));
|
||||
idActions.append($('<option>').val('mkdir').text('Mkdir'));
|
||||
idActions.append($('<option>').val('rmdir').text('Rmdir'));
|
||||
idActions.append($('<option>').val('rename').text('Rename'));
|
||||
idActions.append($('<option>').val('delete').text('Delete'));
|
||||
idActions.append($('<option>').val('first-upload').text('First upload'));
|
||||
idActions.append($('<option>').val('first-download').text('First download'));
|
||||
idActions.append($('<option>').val('pre-upload').text('Pre-upload'));
|
||||
idActions.append($('<option>').val('pre-download').text('Pre-download'));
|
||||
idActions.append($('<option>').val('pre-delete').text('Pre-delete'));
|
||||
idActions.append($('<option>').val('ssh_cmd').text('SSH command'));
|
||||
idActions.selectpicker('refresh');
|
||||
|
||||
$('#idUsername').val("");
|
||||
$('#idIp').val("");
|
||||
$('.provider-events').hide();
|
||||
$('.fs-events').show();
|
||||
onSearchClicked();
|
||||
}
|
||||
|
||||
function selectProviderEvents(){
|
||||
let idActions = $('#idActions');
|
||||
idActions.selectpicker('deselectAll');
|
||||
idActions.find('option').remove();
|
||||
idActions.find('li').remove();
|
||||
idActions.append($('<option>').val('add').text('Add'));
|
||||
idActions.append($('<option>').val('update').text('Update'));
|
||||
idActions.append($('<option>').val('delete').text('Delete'));
|
||||
idActions.selectpicker('refresh');
|
||||
|
||||
$('#idUsername').val("");
|
||||
$('#idIp').val("");
|
||||
$('.fs-events').hide();
|
||||
$('.provider-events').show();
|
||||
onSearchClicked();
|
||||
}
|
||||
|
||||
function onEventChanged(val){
|
||||
switch (val){
|
||||
case '1':
|
||||
case 1:
|
||||
selectFsEvents();
|
||||
break;
|
||||
case '2':
|
||||
case 2:
|
||||
selectProviderEvents();
|
||||
break;
|
||||
default:
|
||||
console.log(`unsupported event type: ${val}`);
|
||||
}
|
||||
}
|
||||
|
||||
$(document).ready(function () {
|
||||
$('#dateTimeRange').daterangepicker({
|
||||
timePicker: true,
|
||||
timePicker24Hour: true,
|
||||
opens: 'left',
|
||||
startDate: moment().add(-1,'hour'),
|
||||
endDate: moment(),
|
||||
locale: {
|
||||
format: 'DD/MM HH:mm'
|
||||
}
|
||||
});
|
||||
|
||||
$.fn.dataTable.ext.errMode = 'none';
|
||||
|
||||
onEventChanged('1');
|
||||
});
|
||||
</script>
|
||||
{{end}}
|
|
@ -243,7 +243,8 @@ function deleteAction() {
|
|||
enabled: false
|
||||
};
|
||||
|
||||
var table = $('#dataTable').DataTable({
|
||||
let dateFn = $.fn.dataTable.render.datetime();
|
||||
let table = $('#dataTable').DataTable({
|
||||
"select": {
|
||||
"style": "single",
|
||||
"blurable": true
|
||||
|
@ -296,11 +297,10 @@ function deleteAction() {
|
|||
return data;
|
||||
}
|
||||
if (row[0] !== ""){
|
||||
var dateFn = $.fn.dataTable.render.datetime();
|
||||
var formattedDate = dateFn(row[0], type);
|
||||
let formattedDate = dateFn(row[0], type);
|
||||
data = `${data}. Updated at: ${formattedDate}`;
|
||||
}
|
||||
var ellipsisFn = $.fn.dataTable.render.ellipsis(60, true);
|
||||
let ellipsisFn = $.fn.dataTable.render.ellipsis(60, true);
|
||||
return ellipsisFn(data, type);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -263,7 +263,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
enabled: false
|
||||
};
|
||||
|
||||
var table = $('#dataTable').DataTable({
|
||||
let dateFn = $.fn.dataTable.render.datetime();
|
||||
let table = $('#dataTable').DataTable({
|
||||
"select": {
|
||||
"style": "single",
|
||||
"blurable": true
|
||||
|
@ -318,11 +319,10 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
return data;
|
||||
}
|
||||
if (row[12] !== ""){
|
||||
var dateFn = $.fn.dataTable.render.datetime();
|
||||
var formattedDate = dateFn(row[12], type);
|
||||
data = `${data}. Updated at: ${formattedDate}`;
|
||||
}
|
||||
var ellipsisFn = $.fn.dataTable.render.ellipsis(70, true);
|
||||
let ellipsisFn = $.fn.dataTable.render.ellipsis(70, true);
|
||||
return ellipsisFn(data, type);
|
||||
}
|
||||
},
|
||||
|
|
|
@ -953,7 +953,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
var downloadURL = '{{.DownloadURL}}';
|
||||
var currentDir = '{{.CurrentDir}}';
|
||||
var ts = new Date().getTime().toString();
|
||||
window.location = `${downloadURL}?path=${currentDir}&files=${files}&_=${ts}`;
|
||||
window.open(`${downloadURL}?path=${currentDir}&files=${files}&_=${ts}`);
|
||||
},
|
||||
enabled: false
|
||||
};
|
||||
|
@ -1019,7 +1019,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
var shareURL = '{{.ShareURL}}';
|
||||
var currentDir = '{{.CurrentDir}}';
|
||||
var ts = new Date().getTime().toString();
|
||||
window.location = `${shareURL}?path=${currentDir}&files=${files}&_=${ts}`;
|
||||
window.open(`${shareURL}?path=${currentDir}&files=${files}&_=${ts}`,'_blank');
|
||||
},
|
||||
enabled: false
|
||||
};
|
||||
|
|
|
@ -410,7 +410,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|||
var downloadURL = '{{.DownloadURL}}';
|
||||
var currentDir = '{{.CurrentDir}}';
|
||||
var ts = new Date().getTime().toString();
|
||||
window.location = `${downloadURL}?path=${currentDir}&files=${files}&_=${ts}`;
|
||||
window.open(`${downloadURL}?path=${currentDir}&files=${files}&_=${ts}`);
|
||||
},
|
||||
enabled: false
|
||||
};
|
||||
|
|
|
@ -4,23 +4,23 @@ go 1.19
|
|||
|
||||
require (
|
||||
github.com/hashicorp/go-plugin v1.4.6
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221203095324-2feef3600930
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/google/go-cmp v0.5.9 // indirect
|
||||
github.com/hashicorp/go-hclog v1.3.1 // indirect
|
||||
github.com/hashicorp/go-hclog v1.4.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd // indirect
|
||||
golang.org/x/net v0.4.0 // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
golang.org/x/text v0.5.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221206210731-b1a01be3a5f6 // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
)
|
||||
|
|
|
@ -9,8 +9,8 @@ github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiu
|
|||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo=
|
||||
github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I=
|
||||
github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-plugin v1.4.6 h1:MDV3UrKQBM3du3G7MApDGvOsMYy3JQJ4exhSoKBAeVA=
|
||||
github.com/hashicorp/go-plugin v1.4.6/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
|
@ -30,26 +30,26 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
|||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221203095324-2feef3600930 h1:znJ52fQBSAQhacaQvZAfkpTioqpcutDREM/H8NttKzU=
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221203095324-2feef3600930/go.mod h1:S2S/Q9fgUpXmL11YoCCt0hyCkEwH1LzQM/6QVsbUCFg=
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e h1:F3G/BReUSU8TX6Kmk0moQgQAk9Ouiv2I+pg//o1IR6U=
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e/go.mod h1:S2S/Q9fgUpXmL11YoCCt0hyCkEwH1LzQM/6QVsbUCFg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd h1:OjndDrsik+Gt+e6fs45z9AxiewiKyLKYpA45W5Kpkks=
|
||||
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=
|
||||
google.golang.org/genproto v0.0.0-20221206210731-b1a01be3a5f6 h1:AGXp12e/9rItf6/4QymU7WsAUwCf+ICW75cuR91nJIc=
|
||||
google.golang.org/genproto v0.0.0-20221206210731-b1a01be3a5f6/go.mod h1:1dOng4TWOomJrDGhpXjfCD35wQC6jnC7HpRmOFRqEV0=
|
||||
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
|
|
|
@ -4,22 +4,22 @@ go 1.19
|
|||
|
||||
require (
|
||||
github.com/hashicorp/go-plugin v1.4.6
|
||||
github.com/sftpgo/sdk v0.1.2
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/fatih/color v1.13.0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/hashicorp/go-hclog v1.3.1 // indirect
|
||||
github.com/hashicorp/go-hclog v1.4.0 // indirect
|
||||
github.com/hashicorp/yamux v0.1.1 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
|
||||
github.com/oklog/run v1.1.0 // indirect
|
||||
golang.org/x/net v0.2.0 // indirect
|
||||
golang.org/x/sys v0.2.0 // indirect
|
||||
golang.org/x/text v0.4.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd // indirect
|
||||
golang.org/x/net v0.4.0 // indirect
|
||||
golang.org/x/sys v0.3.0 // indirect
|
||||
golang.org/x/text v0.5.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20221206210731-b1a01be3a5f6 // indirect
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
)
|
||||
|
|
|
@ -8,8 +8,8 @@ github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw
|
|||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
|
||||
github.com/hashicorp/go-hclog v1.3.1 h1:vDwF1DFNZhntP4DAjuTpOw3uEgMUpXh1pB5fW9DqHpo=
|
||||
github.com/hashicorp/go-hclog v1.3.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-hclog v1.4.0 h1:ctuWFGrhFha8BnnzxqeRGidlEcQkDyL5u8J8t5eA11I=
|
||||
github.com/hashicorp/go-hclog v1.4.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-plugin v1.4.6 h1:MDV3UrKQBM3du3G7MApDGvOsMYy3JQJ4exhSoKBAeVA=
|
||||
github.com/hashicorp/go-plugin v1.4.6/go.mod h1:viDMjcLJuDui6pXb8U4HVfb8AamCWhHGUjr2IrTF67s=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
|
@ -29,27 +29,27 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA=
|
|||
github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sftpgo/sdk v0.1.2 h1:j4V63RuVcYfJAOWV0zRUofa1PlQvKU2ujly0lB7quVA=
|
||||
github.com/sftpgo/sdk v0.1.2/go.mod h1:PTp1TfXa+95wHw9yuZu7BA3vmzLqbRkz3gBmMNnwFQg=
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e h1:F3G/BReUSU8TX6Kmk0moQgQAk9Ouiv2I+pg//o1IR6U=
|
||||
github.com/sftpgo/sdk v0.1.3-0.20221205110949-c15308d4236e/go.mod h1:S2S/Q9fgUpXmL11YoCCt0hyCkEwH1LzQM/6QVsbUCFg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.2 h1:4jaiDzPyXQvSd7D0EjG45355tLlV3VOECpq10pLC+8s=
|
||||
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
|
||||
golang.org/x/net v0.2.0 h1:sZfSu1wtKLGlWI4ZZayP0ck9Y73K1ynO6gqzTdBVdPU=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.4.0 h1:Q5QPcMlvfxFTAPV0+07Xz/MpK9NTXu2VDUuy0FeMfaU=
|
||||
golang.org/x/net v0.4.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0 h1:ljd4t30dBnAvMZaQCevtY0xLLD0A+bRZXbgLMLU1F/A=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
|
||||
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd h1:OjndDrsik+Gt+e6fs45z9AxiewiKyLKYpA45W5Kpkks=
|
||||
google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE=
|
||||
google.golang.org/genproto v0.0.0-20221206210731-b1a01be3a5f6 h1:AGXp12e/9rItf6/4QymU7WsAUwCf+ICW75cuR91nJIc=
|
||||
google.golang.org/genproto v0.0.0-20221206210731-b1a01be3a5f6/go.mod h1:1dOng4TWOomJrDGhpXjfCD35wQC6jnC7HpRmOFRqEV0=
|
||||
google.golang.org/grpc v1.51.0 h1:E1eGv1FTqoLIdnBCZufiSHgKjlqG6fKFf6pPWtMTh8U=
|
||||
google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
|
|
Loading…
Reference in a new issue