mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-21 15:10:23 +00:00
WebClient: refactor long-running tasks to improve browser compatibility
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
baaef63d1d
commit
f38966c6ac
12 changed files with 1118 additions and 243 deletions
58
go.mod
58
go.mod
|
@ -9,35 +9,35 @@ require (
|
|||
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5
|
||||
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.25.2
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.6
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.6
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.8
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.21.1
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.51.3
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.1
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.3
|
||||
github.com/aws/aws-sdk-go-v2 v1.25.3
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.7
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.7
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.3
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.9
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.21.2
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.51.4
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.2
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.4
|
||||
github.com/bmatcuk/doublestar/v4 v4.6.1
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.3.7
|
||||
github.com/coreos/go-oidc/v3 v3.9.0
|
||||
github.com/drakkan/webdav v0.0.0-20240212101318-94e905cb9adb
|
||||
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001
|
||||
github.com/fclairamb/ftpserverlib v0.22.0
|
||||
github.com/fclairamb/go-log v0.4.1
|
||||
github.com/go-acme/lego/v4 v4.15.0
|
||||
github.com/fclairamb/ftpserverlib v0.24.0
|
||||
github.com/fclairamb/go-log v0.5.0
|
||||
github.com/go-acme/lego/v4 v4.16.1
|
||||
github.com/go-chi/chi/v5 v5.0.12
|
||||
github.com/go-chi/jwtauth/v5 v5.3.1
|
||||
github.com/go-chi/render v1.0.3
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/go-sql-driver/mysql v1.8.0
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/hashicorp/go-hclog v1.6.2
|
||||
github.com/hashicorp/go-plugin v1.6.0
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5
|
||||
github.com/jackc/pgx/v5 v5.5.4
|
||||
github.com/jlaffaye/ftp v0.0.0-20201112195030-9aae4d151126
|
||||
github.com/jackc/pgx/v5 v5.5.5
|
||||
github.com/jlaffaye/ftp v0.2.0
|
||||
github.com/klauspost/compress v1.17.7
|
||||
github.com/lestrrat-go/jwx/v2 v2.0.21
|
||||
github.com/lithammer/shortuuid/v3 v3.0.7
|
||||
|
@ -74,7 +74,7 @@ require (
|
|||
golang.org/x/sys v0.18.0
|
||||
golang.org/x/term v0.18.0
|
||||
golang.org/x/time v0.5.0
|
||||
google.golang.org/api v0.168.0
|
||||
google.golang.org/api v0.169.0
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1
|
||||
)
|
||||
|
||||
|
@ -83,19 +83,20 @@ require (
|
|||
cloud.google.com/go/compute v1.25.0 // indirect
|
||||
cloud.google.com/go/compute/metadata v0.2.3 // indirect
|
||||
cloud.google.com/go/iam v1.1.6 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
|
||||
github.com/ajg/form v1.5.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.2 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.2 // indirect
|
||||
github.com/aws/smithy-go v1.20.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/boombuler/barcode v1.0.1 // indirect
|
||||
|
@ -108,7 +109,8 @@ require (
|
|||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.2 // indirect
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.1 // indirect
|
||||
github.com/go-logr/logr v1.4.1 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
|
@ -173,9 +175,9 @@ require (
|
|||
golang.org/x/tools v0.19.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240304212257-790db918fca8 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20240311132316-a219d84964c2 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2 // indirect
|
||||
google.golang.org/grpc v1.62.1 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
|
108
go.sum
108
go.sum
|
@ -11,6 +11,8 @@ cloud.google.com/go/kms v1.15.7 h1:7caV9K3yIxvlQPAcaFffhlT7d1qpxjB1wHBtjWa13SM=
|
|||
cloud.google.com/go/kms v1.15.7/go.mod h1:ub54lbsa6tDkUwnu4W7Yt1aAIFLnspgh0kPGToDukeI=
|
||||
cloud.google.com/go/storage v1.39.0 h1:brbjUa4hbDHhpQf48tjqMaXEV+f1OGoaTmQau9tmCsA=
|
||||
cloud.google.com/go/storage v1.39.0/go.mod h1:OAEj/WZwUYjA3YHQ10/YcN9ttGuEpLwvaoyBXIPikEk=
|
||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0 h1:n1DH8TPV4qqPTje2RcUBYwtrTWlabVp4n46+74X2pn4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.10.0/go.mod h1:HDcZnuGbiyppErN6lB+idp4CKhjbc8gwjto6OPpyggM=
|
||||
|
@ -33,46 +35,46 @@ github.com/alexedwards/argon2id v1.0.0 h1:wJzDx66hqWX7siL/SRUmgz3F8YMrd/nfX/xHHc
|
|||
github.com/alexedwards/argon2id v1.0.0/go.mod h1:tYKkqIjzXvZdzPvADMWOEZ+l6+BD6CtBXMj5fnJppiw=
|
||||
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 h1:I9YN9WMo3SUh7p/4wKeNvD/IQla3U3SUa61U7ul+xM4=
|
||||
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964/go.mod h1:eFiR01PwTcpbzXtdMces7zxg6utvFM5puiWHpWB8D/k=
|
||||
github.com/aws/aws-sdk-go-v2 v1.25.2 h1:/uiG1avJRgLGiQM9X3qJM8+Qa6KRGK5rRPuXE0HUM+w=
|
||||
github.com/aws/aws-sdk-go-v2 v1.25.2/go.mod h1:Evoc5AsmtveRt1komDwIsjHFyrP5tDuF1D1U+6z6pNo=
|
||||
github.com/aws/aws-sdk-go-v2 v1.25.3 h1:xYiLpZTQs1mzvz5PaI6uR0Wh57ippuEthxS4iK5v0n0=
|
||||
github.com/aws/aws-sdk-go-v2 v1.25.3/go.mod h1:35hUlJVYd+M++iLI3ALmVwMOyRYMmRqUXpTtRGW+K9I=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1 h1:gTK2uhtAPtFcdRRJilZPx8uJLL2J85xK11nKtWL0wfU=
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.1/go.mod h1:sxpLb+nZk7tIfCWChfd+h4QwHNUR57d8hA1cleTkjJo=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.6 h1:WmoH1aPrxwcqAZTTnETjKr+fuvqzKd4hRrKxQUiuKP4=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.6/go.mod h1:W9RZFF2pL+OhnUSZsQS/eDMWD8v+R+yWgjj3nSlrXVU=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.6 h1:akhj/nSC6SEx3OmiYGG/7mAyXMem9ZNVVf+DXkikcTk=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.6/go.mod h1:chJZuJ7TkW4kiMwmldOJOEueBoSkUb4ynZS1d9dhygo=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2 h1:AK0J8iYBFeUk2Ax7O8YpLtFsfhdOByh2QIkHmigpRYk=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.2/go.mod h1:iRlGzMix0SExQEviAyptRWRGdYNo3+ufW/lCzvKVTUc=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.8 h1:fjsaZ2EUoOaosuYMLbQAVJsPIAOV4Xn52AQmk5JbhAs=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.8/go.mod h1:WPJcs0Mze3WntafH9Df2NdJ1oSQkEQVL6piZxoS0ecY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2 h1:bNo4LagzUKbjdxE0tIcR9pMzLR2U/Tgie1Hq1HQ3iH8=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.2/go.mod h1:wRQv0nN6v9wDXuWThpovGQjqF1HFdcgWjporw14lS8k=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2 h1:EtOU5jsPdIQNP+6Q2C5e3d65NKT1PeCiQk+9OdzO12Q=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.2/go.mod h1:tyF5sKccmDz0Bv4NrstEr+/9YkSPJHrcO7UsUKf7pWM=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.7 h1:JSfb5nOQF01iOgxFI5OIKWwDiEXWTyTgg1Mm1mHi0A4=
|
||||
github.com/aws/aws-sdk-go-v2/config v1.27.7/go.mod h1:PH0/cNpoMO+B04qET699o5W92Ca79fVtbUnvMIZro4I=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.7 h1:WJd+ubWKoBeRh7A5iNMnxEOs982SyVKOJD+K8HIezu4=
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.7/go.mod h1:UQi7LMR0Vhvs+44w5ec8Q+VS+cd10cjwgHwiVkE0YGU=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.3 h1:p+y7FvkK2dxS+FEwRIDHDe//ZX+jDhP8HHE50ppj4iI=
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.15.3/go.mod h1:/fYB+FZbDlwlAiynK9KDXlzZl3ANI9JkD0Uhz5FjNT4=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.9 h1:vXY/Hq1XdxHBIYgBUmug/AbMyIe1AKulPYS2/VE1X70=
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.9/go.mod h1:GyJJTZoHVuENM4TeJEl5Ffs4W9m19u+4wKJcDi/GZ4A=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.3 h1:ifbIbHZyGl1alsAhPIYsHOg5MuApgqOvVeI8wIugXfs=
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.3/go.mod h1:oQZXg3c6SNeY6OZrDY+xHcF4VGIEoNotX2B4PrDeoJI=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.3 h1:Qvodo9gHG9F3E8SfYOspPeBt0bjSbsevK8WhRAUHcoY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.3/go.mod h1:vCKrdLXtybdf/uQd/YfVR2r5pcbNuEYKzMQpcxmeSJw=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.2 h1:en92G0Z7xlksoOylkUhuBSfJgijC7rHVLRdnIlHEs0E=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.2/go.mod h1:HgtQ/wN5G+8QSlK62lbOtNwQ3wTSByJ4wH2rCkPt+AE=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.3 h1:mDnFOE2sVkyphMWtTH+stv0eW3k0OTx94K63xpxHty4=
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.3/go.mod h1:V8MuRVcCRt5h1S+Fwu8KbC7l/gBGo3yBAyUbJM2IJOk=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1 h1:EyBZibRTVAs6ECHZOw5/wlylS9OcTzwyjeQMudmREjE=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.1/go.mod h1:JKpmtYhhPs7D97NL/ltqz7yCkERFW5dOlHyVl66ZYF8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.4 h1:J3Q6N2sTChfYLZSTey3Qeo7n3JSm6RTJDcKev+7Sbus=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.4/go.mod h1:ZopsdDMVg1H03X7BdzpGaufOkuz27RjtKDzioP2U0Hg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.4 h1:jRiWxyuVO8PlkN72wDMVn/haVH4SDCBkUt0Lf/dxd7s=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.4/go.mod h1:Ru7vg1iQ7cR4i7SZ/JTLYN9kaXtbL69UdgG0OQWQxW0=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.2 h1:1oY1AVEisRI4HNuFoLdRUB0hC63ylDAN6Me3MrfclEg=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.2/go.mod h1:KZ03VgvZwSjkT7fOetQ/wF3MZUvYFirlI1H5NklUNsY=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.21.1 h1:cy+x/R8zd4Zluf+6ZWzbPPdLh+l4MeYPlYxdqK+Qr0M=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.21.1/go.mod h1:ITcPsa7HwiY6ddwbwmtuf+/q7sfr4MjH5HzUvn5FHBQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.51.3 h1:7cR4xxS480TI0R6Bd75g9Npdw89VriquvQPlMNmuds4=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.51.3/go.mod h1:zb72GZ2MvfCX5ynVJ+Mc/NCx7hncbsko4NZm5E+p6J4=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.1 h1:DtKw4TxZT3VrzYupXQJPBqT9ImyobZZE+JIQPPAVxqs=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.1/go.mod h1:bit9G2ORpSjUTr4PA4usvbBfbOyvMj0LbE1dXF14Sug=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.1 h1:utEGkfdQ4L6YW/ietH7111ZYglLJvS+sLriHJ1NBJEQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.1/go.mod h1:RsYqzYr2F2oPDdpy+PdhephuZxTfjHQe7SOBcZGoAU8=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.1 h1:9/GylMS45hGGFCcMrUZDVayQE1jYSIN6da9jo7RAYIw=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.1/go.mod h1:YjAPFn4kGFqKC54VsHs5fn5B6d+PCY2tziEa3U/GB5Y=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.3 h1:TkiFkSVX990ryWIMBCT4kPqZEgThQe1xPU/AQXavtvU=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.3/go.mod h1:xYNauIUqSuvzlPVb3VB5no/n48YGhmlInD3Uh0Co8Zc=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.5 h1:mbWNpfRUTT6bnacmvOTKXZjR/HycibdWzNpfbrbLDIs=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.5/go.mod h1:FCOPWGjsshkkICJIn9hq9xr6dLKtyaWpuUojiN3W1/8=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.5 h1:K/NXvIftOlX+oGgWGIa3jDyYLDNsdVhsjHmsBH2GLAQ=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.5/go.mod h1:cl9HGLV66EnCmMNzq4sYOti+/xo8w34CsgzVtm2GgsY=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.3 h1:4t+QEX7BsXz98W8W1lNvMAG+NX8qHz2CjLBxQKku40g=
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.3/go.mod h1:oFcjjUq5Hm09N9rpxTdeMeLeQcxS7mIkBkL8qUKng+A=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.21.2 h1:SxqH8OxVZ804yweHp2bNERqys7om9cmjxowlI/XOnS8=
|
||||
github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.21.2/go.mod h1:Djusz31OLPJV5vma0ujfKxgsl8gBezwLZh5LA5044fk=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.51.4 h1:lW5xUzOPGAMY7HPuNF4FdyBwRc3UJ/e8KsapbesVeNU=
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.51.4/go.mod h1:MGTaf3x/+z7ZGugCGvepnx2DS6+caCYYqKhzVoLNYPk=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.2 h1:WrqqLhD5St2cbXsvR0yuY43pdhXsUL0yjQepBJIpTvI=
|
||||
github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.28.2/go.mod h1:GvNHKQAAOSKjmlccE/+Ww2gDbwYP9EewIuvWiQSquQs=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.2 h1:XOPfar83RIRPEzfihnp+U6udOveKZJvPQ76SKWrLRHc=
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.2/go.mod h1:Vv9Xyk1KMHXrR3vNQe8W5LMFdTjSeWk0gBZBzvf3Qa0=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.2 h1:pi0Skl6mNl2w8qWZXcdOyg197Zsf4G97U7Sso9JXGZE=
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.23.2/go.mod h1:JYzLoEVeLXk+L4tn1+rrkfhkxl6mLDEVaDSvGq9og90=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.4 h1:Ppup1nVNAOWbBOrcoOxaxPeEnSFB2RnnQdguhXpmeQk=
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.4/go.mod h1:+K1rNPVyGxkRuv9NNiaZ4YhBFuyw2MMA9SlIJ1Zlpz8=
|
||||
github.com/aws/smithy-go v1.20.1 h1:4SZlSlMr36UEqC7XOyRVb27XMeZubNcBNN+9IgEPIQw=
|
||||
github.com/aws/smithy-go v1.20.1/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
|
@ -128,24 +130,26 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
|
|||
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/fclairamb/go-log v0.4.1 h1:rLtdSG9x2pK41AIAnE8WYpl05xBJfw1ZyYxZaXFcBsM=
|
||||
github.com/fclairamb/go-log v0.4.1/go.mod h1:sw1KvnkZ4wKCYkvy4SL3qVZcJSWFP8Ure4pM3z+KNn4=
|
||||
github.com/fclairamb/go-log v0.5.0 h1:Gz9wSamEaA6lta4IU2cjJc2xSq5sV5VYSB5w/SUHhVc=
|
||||
github.com/fclairamb/go-log v0.5.0/go.mod h1:XoRO1dYezpsGmLLkZE9I+sHqpqY65p8JA+Vqblb7k40=
|
||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/go-acme/lego/v4 v4.15.0 h1:A7MHEU3b+TDFqhC/HmzMJnzPbyeaYvMZQBbqgvbThhU=
|
||||
github.com/go-acme/lego/v4 v4.15.0/go.mod h1:eeGhjW4zWT7Ccqa3sY7ayEqFLCAICx+mXgkMHKIkLxg=
|
||||
github.com/go-acme/lego/v4 v4.16.1 h1:JxZ93s4KG0jL27rZ30UsIgxap6VGzKuREsSkkyzeoCQ=
|
||||
github.com/go-acme/lego/v4 v4.16.1/go.mod h1:AVvwdPned/IWpD/ihHhMsKnveF7HHYAz/CmtXi7OZoE=
|
||||
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/go-chi/jwtauth/v5 v5.3.1 h1:1ePWrjVctvp1tyBq5b/2ER8Th/+RbYc7x4qNsc5rh5A=
|
||||
github.com/go-chi/jwtauth/v5 v5.3.1/go.mod h1:6Fl2RRmWXs3tJYE1IQGX81FsPoGqDwq9c15j52R5q80=
|
||||
github.com/go-chi/render v1.0.3 h1:AsXqd2a1/INaIfUSKq3G5uA8weYx20FOsM7uSoCyyt4=
|
||||
github.com/go-chi/render v1.0.3/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
|
||||
github.com/go-jose/go-jose/v3 v3.0.2 h1:2Edjn8Nrb44UvTdp84KU0bBPs1cO7noRCybtS3eJEUQ=
|
||||
github.com/go-jose/go-jose/v3 v3.0.2/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
|
||||
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
||||
github.com/go-jose/go-jose/v4 v4.0.1 h1:QVEPDE3OluqXBQZDcnNvQrInro2h0e4eqNbnZSWqS6U=
|
||||
github.com/go-jose/go-jose/v4 v4.0.1/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
||||
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
|
||||
|
@ -158,8 +162,8 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
|
|||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
|
||||
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-sql-driver/mysql v1.8.0 h1:UtktXaU2Nb64z/pLiGIxY4431SJ4/dR5cjMmlVHgnT4=
|
||||
github.com/go-sql-driver/mysql v1.8.0/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
|
@ -238,8 +242,8 @@ github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsI
|
|||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9 h1:L0QtFUgDarD7Fpv9jeVMgy/+Ec0mtnmYuImjTz6dtDA=
|
||||
github.com/jackc/pgservicefile v0.0.0-20231201235250-de7065d80cb9/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||
github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8=
|
||||
github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
|
||||
github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
|
||||
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
|
||||
|
@ -522,8 +526,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.168.0 h1:MBRe+Ki4mMN93jhDDbpuRLjRddooArz4FeSObvUMmjY=
|
||||
google.golang.org/api v0.168.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg=
|
||||
google.golang.org/api v0.169.0 h1:QwWPy71FgMWqJN/l6jVlFHUa29a7dcUy02I8o799nPY=
|
||||
google.golang.org/api v0.169.0/go.mod h1:gpNOiMA2tZ4mf5R9Iwf4rK/Dcz0fbdIgWYWVoxmsyLg=
|
||||
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=
|
||||
|
@ -531,12 +535,12 @@ google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJ
|
|||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20240304212257-790db918fca8 h1:Fe8QycXyEd9mJgnwB9kmw00WgB43eQ/xYO5C6gceybQ=
|
||||
google.golang.org/genproto v0.0.0-20240304212257-790db918fca8/go.mod h1:yA7a1bW1kwl459Ol0m0lV4hLTfrL/7Bkk4Mj2Ir1mWI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 h1:8eadJkXbwDEMNwcB5O0s5Y5eCfyuCLdvaiOIaGTrWmQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 h1:IR+hp6ypxjH24bkMfEJ0yHR21+gwPWdV+/IBrPQyn3k=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs=
|
||||
google.golang.org/genproto v0.0.0-20240311132316-a219d84964c2 h1:rrOOzm+NteCjTNqCnDAdYhvKL1G/9N/Lj1GRxJtQEL0=
|
||||
google.golang.org/genproto v0.0.0-20240311132316-a219d84964c2/go.mod h1:yA7a1bW1kwl459Ol0m0lV4hLTfrL/7Bkk4Mj2Ir1mWI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2 h1:rIo7ocm2roD9DcFIX67Ym8icoGCKSARAiPljFhh5suQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20240311132316-a219d84964c2/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2 h1:9IZDv+/GcI6u+a4jRFRLxQs0RUCfavGfoOgEW6jpkI0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20240311132316-a219d84964c2/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
|
|
|
@ -29,6 +29,7 @@ const (
|
|||
SessionTypeResetCode
|
||||
SessionTypeOAuth2Auth
|
||||
SessionTypeInvalidToken
|
||||
SessionTypeWebTask
|
||||
)
|
||||
|
||||
// Session defines a shared session persisted in the data provider
|
||||
|
@ -43,7 +44,7 @@ func (s *Session) validate() error {
|
|||
if s.Key == "" {
|
||||
return errors.New("unable to save a session with an empty key")
|
||||
}
|
||||
if s.Type < SessionTypeOIDCAuth || s.Type > SessionTypeInvalidToken {
|
||||
if s.Type < SessionTypeOIDCAuth || s.Type > SessionTypeWebTask {
|
||||
return fmt.Errorf("invalid session type: %v", s.Type)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -3247,6 +3247,9 @@ func sqlCommonGetSession(key string, dbHandle sqlQuerier) (Session, error) {
|
|||
var data []byte // type hint, some driver will use string instead of []byte if the type is any
|
||||
err := dbHandle.QueryRowContext(ctx, q, key).Scan(&session.Key, &data, &session.Type, &session.Timestamp)
|
||||
if err != nil {
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
return session, util.NewRecordNotFoundError(err.Error())
|
||||
}
|
||||
return session, err
|
||||
}
|
||||
session.Data = data
|
||||
|
|
|
@ -177,6 +177,7 @@ const (
|
|||
webClientViewPDFPathDefault = "/web/client/viewpdf"
|
||||
webClientGetPDFPathDefault = "/web/client/getpdf"
|
||||
webClientExistPathDefault = "/web/client/exist"
|
||||
webClientTasksPathDefault = "/web/client/tasks"
|
||||
webStaticFilesPathDefault = "/static"
|
||||
webOpenAPIPathDefault = "/openapi"
|
||||
// MaxRestoreSize defines the max size for the loaddata input file
|
||||
|
@ -278,6 +279,7 @@ var (
|
|||
webClientViewPDFPath string
|
||||
webClientGetPDFPath string
|
||||
webClientExistPath string
|
||||
webClientTasksPath string
|
||||
webStaticFilesPath string
|
||||
webOpenAPIPath string
|
||||
// max upload size for http clients, 1GB by default
|
||||
|
@ -936,6 +938,7 @@ func (c *Conf) Initialize(configDir string, isShared int) error {
|
|||
resetCodesMgr = newResetCodeManager(isShared)
|
||||
oidcMgr = newOIDCManager(isShared)
|
||||
oauth2Mgr = newOAuth2Manager(isShared)
|
||||
webTaskMgr = newWebTaskManager(isShared)
|
||||
staticFilesPath := util.FindSharedDataPath(c.StaticFilesPath, configDir)
|
||||
templatesPath := util.FindSharedDataPath(c.TemplatesPath, configDir)
|
||||
openAPIPath := util.FindSharedDataPath(c.OpenAPIPath, configDir)
|
||||
|
@ -1108,6 +1111,7 @@ func updateWebClientURLs(baseURL string) {
|
|||
webClientViewPDFPath = path.Join(baseURL, webClientViewPDFPathDefault)
|
||||
webClientGetPDFPath = path.Join(baseURL, webClientGetPDFPathDefault)
|
||||
webClientExistPath = path.Join(baseURL, webClientExistPathDefault)
|
||||
webClientTasksPath = path.Join(baseURL, webClientTasksPathDefault)
|
||||
webStaticFilesPath = path.Join(baseURL, webStaticFilesPathDefault)
|
||||
webOpenAPIPath = path.Join(baseURL, webOpenAPIPathDefault)
|
||||
}
|
||||
|
@ -1196,6 +1200,7 @@ func startCleanupTicker(duration time.Duration) {
|
|||
counter++
|
||||
invalidatedJWTTokens.Cleanup()
|
||||
resetCodesMgr.Cleanup()
|
||||
webTaskMgr.Cleanup()
|
||||
if counter%2 == 0 {
|
||||
oidcMgr.cleanup()
|
||||
oauth2Mgr.cleanup()
|
||||
|
|
|
@ -193,6 +193,9 @@ const (
|
|||
webClientViewPDFPath = "/web/client/viewpdf"
|
||||
webClientGetPDFPath = "/web/client/getpdf"
|
||||
webClientExistPath = "/web/client/exist"
|
||||
webClientTasksPath = "/web/client/tasks"
|
||||
webClientFileMovePath = "/web/client/file-actions/move"
|
||||
webClientFileCopyPath = "/web/client/file-actions/copy"
|
||||
jsonAPISuffix = "/json"
|
||||
httpBaseURL = "http://127.0.0.1:8081"
|
||||
defaultRemoteAddr = "127.0.0.1:1234"
|
||||
|
@ -16505,6 +16508,28 @@ func TestRenameDifferentResource(t *testing.T) {
|
|||
testFileName := "file.txt"
|
||||
|
||||
webAPIToken, err := getJWTAPIUserTokenFromTestServer(defaultUsername, defaultPassword)
|
||||
assert.NoError(t, err)
|
||||
webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
|
||||
assert.NoError(t, err)
|
||||
csrfToken, err := getCSRFToken(httpBaseURL + webClientLoginPath)
|
||||
|
||||
getStatusResponse := func(taskID string) int {
|
||||
req, _ := http.NewRequest(http.MethodGet, webClientTasksPath+"/"+url.PathEscape(taskID), nil)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("X-CSRF-TOKEN", csrfToken)
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr := executeRequest(req)
|
||||
if rr.Code != http.StatusOK {
|
||||
return -1
|
||||
}
|
||||
resp := make(map[string]any)
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &resp)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return int(resp["status"].(float64))
|
||||
}
|
||||
|
||||
assert.NoError(t, err)
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, userFileActionsPath+"/move?path="+testFileName+"&target="+url.QueryEscape(path.Join("/", "folderPath", testFileName)), nil) //nolint:goconst
|
||||
|
@ -16514,6 +16539,24 @@ func TestRenameDifferentResource(t *testing.T) {
|
|||
checkResponseCode(t, http.StatusNotFound, rr)
|
||||
assert.Contains(t, rr.Body.String(), "Cannot perform copy step")
|
||||
|
||||
req, err = http.NewRequest(http.MethodPost, webClientFileMovePath+"?path="+testFileName+"&target="+url.QueryEscape(path.Join("/", "folderPath", testFileName)), nil)
|
||||
assert.NoError(t, err)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("X-CSRF-TOKEN", csrfToken)
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusAccepted, rr)
|
||||
taskResp := make(map[string]any)
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &taskResp)
|
||||
assert.NoError(t, err)
|
||||
taskID := taskResp["message"].(string)
|
||||
assert.NotEmpty(t, taskID)
|
||||
|
||||
assert.Eventually(t, func() bool {
|
||||
status := getStatusResponse(taskID)
|
||||
return status == http.StatusNotFound
|
||||
}, 1000*time.Millisecond, 100*time.Millisecond)
|
||||
|
||||
err = os.WriteFile(filepath.Join(user.GetHomeDir(), testFileName), []byte("just a test"), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
|
||||
|
@ -16557,6 +16600,24 @@ func TestRenameDifferentResource(t *testing.T) {
|
|||
checkResponseCode(t, http.StatusForbidden, rr)
|
||||
assert.Contains(t, rr.Body.String(), "Cannot perform remove step")
|
||||
|
||||
req, err = http.NewRequest(http.MethodPost, webClientFileMovePath+"?path="+testFileName+"&target="+url.QueryEscape(path.Join("/", "folderPath", testFileName)), nil)
|
||||
assert.NoError(t, err)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("X-CSRF-TOKEN", csrfToken)
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusAccepted, rr)
|
||||
taskResp = make(map[string]any)
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &taskResp)
|
||||
assert.NoError(t, err)
|
||||
taskID = taskResp["message"].(string)
|
||||
assert.NotEmpty(t, taskID)
|
||||
|
||||
assert.Eventually(t, func() bool {
|
||||
status := getStatusResponse(taskID)
|
||||
return status == http.StatusForbidden
|
||||
}, 1000*time.Millisecond, 100*time.Millisecond)
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
|
@ -17185,6 +17246,209 @@ func TestBufferedWebFilesAPI(t *testing.T) {
|
|||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
func TestWebClientTasksAPI(t *testing.T) {
|
||||
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
u1 := getTestUser()
|
||||
u1.Username = xid.New().String()
|
||||
user1, _, err := httpdtest.AddUser(u1, http.StatusCreated)
|
||||
assert.NoError(t, err)
|
||||
|
||||
testDir := "subdir"
|
||||
testFileData := []byte("data")
|
||||
testFilePath := filepath.Join(user.GetHomeDir(), testDir, "file.txt")
|
||||
testFileName := filepath.Base(testFilePath)
|
||||
err = os.MkdirAll(filepath.Dir(testFilePath), os.ModePerm)
|
||||
assert.NoError(t, err)
|
||||
err = os.WriteFile(testFilePath, testFileData, 0666)
|
||||
assert.NoError(t, err)
|
||||
|
||||
webToken, err := getJWTWebClientTokenFromTestServer(defaultUsername, defaultPassword)
|
||||
assert.NoError(t, err)
|
||||
csrfToken, err := getCSRFToken(httpBaseURL + webClientLoginPath)
|
||||
assert.NoError(t, err)
|
||||
webToken1, err := getJWTWebClientTokenFromTestServer(user1.Username, defaultPassword)
|
||||
assert.NoError(t, err)
|
||||
|
||||
getStatusResponse := func(taskID string) int {
|
||||
req, _ := http.NewRequest(http.MethodGet, webClientTasksPath+"/"+url.PathEscape(taskID), nil)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("X-CSRF-TOKEN", csrfToken)
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr := executeRequest(req)
|
||||
if rr.Code != http.StatusOK {
|
||||
return -1
|
||||
}
|
||||
resp := make(map[string]any)
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &resp)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return int(resp["status"].(float64))
|
||||
}
|
||||
// missing task
|
||||
assert.Equal(t, -1, getStatusResponse("missing"))
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, webClientFileCopyPath+"?path="+
|
||||
url.QueryEscape(path.Join(testDir, testFileName))+"&target="+url.QueryEscape(testFileName), nil)
|
||||
assert.NoError(t, err)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("X-CSRF-TOKEN", csrfToken)
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr := executeRequest(req)
|
||||
checkResponseCode(t, http.StatusAccepted, rr)
|
||||
resp := make(map[string]any)
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &resp)
|
||||
assert.NoError(t, err)
|
||||
taskID := resp["message"].(string)
|
||||
assert.NotEmpty(t, taskID)
|
||||
|
||||
assert.Eventually(t, func() bool {
|
||||
status := getStatusResponse(taskID)
|
||||
return status == http.StatusOK
|
||||
}, 1000*time.Millisecond, 100*time.Millisecond)
|
||||
|
||||
// cannot get the task with a different user
|
||||
req, err = http.NewRequest(http.MethodGet, webClientTasksPath+"/"+url.PathEscape(taskID), nil)
|
||||
assert.NoError(t, err)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("X-CSRF-TOKEN", csrfToken)
|
||||
setJWTCookieForReq(req, webToken1)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusForbidden, rr)
|
||||
|
||||
req, err = http.NewRequest(http.MethodPost, webClientFileMovePath+"?path="+
|
||||
url.QueryEscape(path.Join(testDir, testFileName))+"&target="+url.QueryEscape(testFileName), nil)
|
||||
assert.NoError(t, err)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("X-CSRF-TOKEN", csrfToken)
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusAccepted, rr)
|
||||
resp = make(map[string]any)
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &resp)
|
||||
assert.NoError(t, err)
|
||||
taskID = resp["message"].(string)
|
||||
assert.NotEmpty(t, taskID)
|
||||
|
||||
assert.Eventually(t, func() bool {
|
||||
status := getStatusResponse(taskID)
|
||||
return status == http.StatusOK
|
||||
}, 1000*time.Millisecond, 100*time.Millisecond)
|
||||
|
||||
req, err = http.NewRequest(http.MethodDelete, webClientDirsPath+"?path="+
|
||||
url.QueryEscape(testDir), nil)
|
||||
assert.NoError(t, err)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("X-CSRF-TOKEN", csrfToken)
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusAccepted, rr)
|
||||
resp = make(map[string]any)
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &resp)
|
||||
assert.NoError(t, err)
|
||||
taskID = resp["message"].(string)
|
||||
assert.NotEmpty(t, taskID)
|
||||
|
||||
assert.Eventually(t, func() bool {
|
||||
status := getStatusResponse(taskID)
|
||||
return status == http.StatusOK
|
||||
}, 1000*time.Millisecond, 100*time.Millisecond)
|
||||
|
||||
req, err = http.NewRequest(http.MethodDelete, webClientDirsPath+"?path="+
|
||||
url.QueryEscape(testDir), nil)
|
||||
assert.NoError(t, err)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("X-CSRF-TOKEN", csrfToken)
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusAccepted, rr)
|
||||
resp = make(map[string]any)
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &resp)
|
||||
assert.NoError(t, err)
|
||||
taskID = resp["message"].(string)
|
||||
assert.NotEmpty(t, taskID)
|
||||
|
||||
assert.Eventually(t, func() bool {
|
||||
status := getStatusResponse(taskID)
|
||||
return status == http.StatusNotFound
|
||||
}, 1000*time.Millisecond, 100*time.Millisecond)
|
||||
|
||||
req, err = http.NewRequest(http.MethodPost, webClientFileMovePath+"?path="+
|
||||
url.QueryEscape(path.Join(testDir, testFileName))+"&target="+url.QueryEscape(testFileName), nil)
|
||||
assert.NoError(t, err)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("X-CSRF-TOKEN", csrfToken)
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusAccepted, rr)
|
||||
resp = make(map[string]any)
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &resp)
|
||||
assert.NoError(t, err)
|
||||
taskID = resp["message"].(string)
|
||||
assert.NotEmpty(t, taskID)
|
||||
|
||||
assert.Eventually(t, func() bool {
|
||||
status := getStatusResponse(taskID)
|
||||
return status == http.StatusNotFound
|
||||
}, 1000*time.Millisecond, 100*time.Millisecond)
|
||||
|
||||
req, err = http.NewRequest(http.MethodPost, webClientFileCopyPath+"?path="+
|
||||
url.QueryEscape(path.Join(testDir, testFileName)+"/")+"&target="+url.QueryEscape(testFileName+"/"), nil)
|
||||
assert.NoError(t, err)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("X-CSRF-TOKEN", csrfToken)
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusAccepted, rr)
|
||||
resp = make(map[string]any)
|
||||
err = json.Unmarshal(rr.Body.Bytes(), &resp)
|
||||
assert.NoError(t, err)
|
||||
taskID = resp["message"].(string)
|
||||
assert.NotEmpty(t, taskID)
|
||||
|
||||
assert.Eventually(t, func() bool {
|
||||
status := getStatusResponse(taskID)
|
||||
return status == http.StatusNotFound
|
||||
}, 1000*time.Millisecond, 100*time.Millisecond)
|
||||
|
||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
_, err = httpdtest.RemoveUser(user1, http.StatusOK)
|
||||
assert.NoError(t, err)
|
||||
err = os.RemoveAll(user1.GetHomeDir())
|
||||
assert.NoError(t, err)
|
||||
// user deleted
|
||||
req, err = http.NewRequest(http.MethodDelete, webClientDirsPath+"?path="+
|
||||
url.QueryEscape(testDir), nil)
|
||||
assert.NoError(t, err)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("X-CSRF-TOKEN", csrfToken)
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusNotFound, rr)
|
||||
|
||||
req, err = http.NewRequest(http.MethodPost, webClientFileMovePath+"?path="+
|
||||
url.QueryEscape(path.Join(testDir, testFileName))+"&target="+url.QueryEscape(testFileName), nil)
|
||||
assert.NoError(t, err)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("X-CSRF-TOKEN", csrfToken)
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusNotFound, rr)
|
||||
|
||||
req, err = http.NewRequest(http.MethodPost, webClientFileCopyPath+"?path="+
|
||||
url.QueryEscape(path.Join(testDir, testFileName))+"&target="+url.QueryEscape(testFileName), nil)
|
||||
assert.NoError(t, err)
|
||||
req.RemoteAddr = defaultRemoteAddr
|
||||
req.Header.Set("X-CSRF-TOKEN", csrfToken)
|
||||
setJWTCookieForReq(req, webToken)
|
||||
rr = executeRequest(req)
|
||||
checkResponseCode(t, http.StatusNotFound, rr)
|
||||
}
|
||||
|
||||
func TestStartDirectory(t *testing.T) {
|
||||
u := getTestUser()
|
||||
u.Filters.StartDirectory = "/start/dir"
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
@ -286,6 +285,7 @@ RKjnkiEZeG4+G91Xu7+HmcBLwV86k5I+tXK9O1Okomr6Zry8oqVcxU5TB6VRS+rA
|
|||
ubwF00Drdvk2+kDZfxIM137nBiy7wgCJi2Ksm5ihN3dUF6Q0oNPl
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
defaultAdminUsername = "admin"
|
||||
defeaultUsername = "test_user"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -521,6 +521,11 @@ func TestInvalidToken(t *testing.T) {
|
|||
assert.Equal(t, http.StatusBadRequest, rr.Code)
|
||||
assert.Contains(t, rr.Body.String(), "Invalid token claims")
|
||||
|
||||
rr = httptest.NewRecorder()
|
||||
getWebTask(rr, req)
|
||||
assert.Equal(t, http.StatusBadRequest, rr.Code)
|
||||
assert.Contains(t, rr.Body.String(), "Invalid token claims")
|
||||
|
||||
rr = httptest.NewRecorder()
|
||||
getAdminProfile(rr, req)
|
||||
assert.Equal(t, http.StatusBadRequest, rr.Code)
|
||||
|
@ -3167,7 +3172,7 @@ func TestDbResetCodeManager(t *testing.T) {
|
|||
assert.ErrorIs(t, err, util.ErrNotFound)
|
||||
}
|
||||
_, err = mgr.Get(resetCode.Code)
|
||||
assert.ErrorIs(t, err, sql.ErrNoRows)
|
||||
assert.ErrorIs(t, err, util.ErrNotFound)
|
||||
// add an expired reset code
|
||||
resetCode = newResetCode("user", false)
|
||||
resetCode.ExpiresAt = time.Now().Add(-24 * time.Hour)
|
||||
|
@ -3179,7 +3184,7 @@ func TestDbResetCodeManager(t *testing.T) {
|
|||
}
|
||||
mgr.Cleanup()
|
||||
_, err = mgr.Get(resetCode.Code)
|
||||
assert.ErrorIs(t, err, sql.ErrNoRows)
|
||||
assert.ErrorIs(t, err, util.ErrNotFound)
|
||||
|
||||
dbMgr, ok := mgr.(*dbResetCodeManager)
|
||||
if assert.True(t, ok) {
|
||||
|
|
|
@ -1583,6 +1583,8 @@ func (s *httpdServer) setupWebClientRoutes() {
|
|||
router.With(s.checkAuthRequirements, s.refreshCookie).Get(webClientViewPDFPath, s.handleClientViewPDF)
|
||||
router.With(s.checkAuthRequirements, s.refreshCookie).Get(webClientGetPDFPath, s.handleClientGetPDF)
|
||||
router.With(s.checkAuthRequirements, s.refreshCookie, verifyCSRFHeader).Get(webClientFilePath, getUserFile)
|
||||
router.With(s.checkAuthRequirements, s.refreshCookie, verifyCSRFHeader).Get(webClientTasksPath+"/{id}",
|
||||
getWebTask)
|
||||
router.With(s.checkAuthRequirements, s.checkHTTPUserPerm(sdk.WebClientWriteDisabled), verifyCSRFHeader).
|
||||
Post(webClientFilePath, uploadUserFile)
|
||||
router.With(s.checkAuthRequirements, s.checkHTTPUserPerm(sdk.WebClientWriteDisabled), verifyCSRFHeader).
|
||||
|
@ -1595,11 +1597,11 @@ func (s *httpdServer) setupWebClientRoutes() {
|
|||
router.With(s.checkAuthRequirements, s.checkHTTPUserPerm(sdk.WebClientWriteDisabled), verifyCSRFHeader).
|
||||
Post(webClientDirsPath, createUserDir)
|
||||
router.With(s.checkAuthRequirements, s.checkHTTPUserPerm(sdk.WebClientWriteDisabled), verifyCSRFHeader).
|
||||
Delete(webClientDirsPath, deleteUserDir)
|
||||
Delete(webClientDirsPath, taskDeleteDir)
|
||||
router.With(s.checkAuthRequirements, s.checkHTTPUserPerm(sdk.WebClientWriteDisabled), verifyCSRFHeader).
|
||||
Post(webClientFileActionsPath+"/move", renameUserFsEntry)
|
||||
Post(webClientFileActionsPath+"/move", taskRenameFsEntry)
|
||||
router.With(s.checkAuthRequirements, s.checkHTTPUserPerm(sdk.WebClientWriteDisabled), verifyCSRFHeader).
|
||||
Post(webClientFileActionsPath+"/copy", copyUserFsEntry)
|
||||
Post(webClientFileActionsPath+"/copy", taskCopyFsEntry)
|
||||
router.With(s.checkAuthRequirements, s.refreshCookie).
|
||||
Post(webClientDownloadZipPath, s.handleWebClientDownloadZip)
|
||||
router.With(s.checkAuthRequirements, s.refreshCookie).Get(webClientPingPath, handlePingRequest)
|
||||
|
|
|
@ -129,6 +129,7 @@ type filesPage struct {
|
|||
DownloadURL string
|
||||
ViewPDFURL string
|
||||
FileURL string
|
||||
TasksURL string
|
||||
CanAddFiles bool
|
||||
CanCreateDirs bool
|
||||
CanRename bool
|
||||
|
@ -750,6 +751,7 @@ func (s *httpdServer) renderSharedFilesPage(w http.ResponseWriter, r *http.Reque
|
|||
FileURL: "",
|
||||
FileActionsURL: "",
|
||||
CheckExistURL: path.Join(baseSharePath, "browse", "exist"),
|
||||
TasksURL: "",
|
||||
CanAddFiles: share.Scope == dataprovider.ShareScopeReadWrite,
|
||||
CanCreateDirs: false,
|
||||
CanRename: false,
|
||||
|
@ -793,6 +795,7 @@ func (s *httpdServer) renderFilesPage(w http.ResponseWriter, r *http.Request, di
|
|||
FileURL: webClientFilePath,
|
||||
FileActionsURL: webClientFileActionsPath,
|
||||
CheckExistURL: webClientExistPath,
|
||||
TasksURL: webClientTasksPath,
|
||||
CanAddFiles: user.CanAddFilesFromWeb(dirName),
|
||||
CanCreateDirs: user.CanAddDirsFromWeb(dirName),
|
||||
CanRename: user.CanRenameFromWeb(dirName, dirName),
|
||||
|
@ -2015,3 +2018,204 @@ func checkShareRedirectURL(next, base string) (bool, string) {
|
|||
}
|
||||
return true, next
|
||||
}
|
||||
|
||||
func getWebTask(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxLoginBodySize)
|
||||
claims, err := getTokenClaims(r)
|
||||
if err != nil || claims.Username == "" {
|
||||
sendAPIResponse(w, r, err, "Invalid token claims", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
taskID := getURLParam(r, "id")
|
||||
|
||||
task, err := webTaskMgr.Get(taskID)
|
||||
if err != nil {
|
||||
sendAPIResponse(w, r, err, "Unable to get task", getMappedStatusCode(err))
|
||||
return
|
||||
}
|
||||
if task.User != claims.Username {
|
||||
sendAPIResponse(w, r, nil, http.StatusText(http.StatusForbidden), http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
render.JSON(w, r, task)
|
||||
}
|
||||
|
||||
func taskDeleteDir(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||
connection, err := getUserConnection(w, r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
name := connection.User.GetCleanedPath(r.URL.Query().Get("path"))
|
||||
task := webTaskData{
|
||||
ID: connection.GetID(),
|
||||
User: connection.GetUsername(),
|
||||
Path: name,
|
||||
Timestamp: util.GetTimeAsMsSinceEpoch(time.Now()),
|
||||
Status: 0,
|
||||
}
|
||||
if err := webTaskMgr.Add(task); err != nil {
|
||||
common.Connections.Remove(connection.GetID())
|
||||
sendAPIResponse(w, r, nil, "Unable to create task", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
go executeDeleteTask(connection, task)
|
||||
sendAPIResponse(w, r, nil, task.ID, http.StatusAccepted)
|
||||
}
|
||||
|
||||
func taskRenameFsEntry(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||
connection, err := getUserConnection(w, r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
oldName := connection.User.GetCleanedPath(r.URL.Query().Get("path"))
|
||||
newName := connection.User.GetCleanedPath(r.URL.Query().Get("target"))
|
||||
task := webTaskData{
|
||||
ID: connection.GetID(),
|
||||
User: connection.GetUsername(),
|
||||
Path: oldName,
|
||||
Target: newName,
|
||||
Timestamp: util.GetTimeAsMsSinceEpoch(time.Now()),
|
||||
Status: 0,
|
||||
}
|
||||
if err := webTaskMgr.Add(task); err != nil {
|
||||
common.Connections.Remove(connection.GetID())
|
||||
sendAPIResponse(w, r, nil, "Unable to create task", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
go executeRenameTask(connection, task)
|
||||
sendAPIResponse(w, r, nil, task.ID, http.StatusAccepted)
|
||||
}
|
||||
|
||||
func taskCopyFsEntry(w http.ResponseWriter, r *http.Request) {
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
|
||||
connection, err := getUserConnection(w, r)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
source := r.URL.Query().Get("path")
|
||||
target := r.URL.Query().Get("target")
|
||||
copyFromSource := strings.HasSuffix(source, "/")
|
||||
copyInTarget := strings.HasSuffix(target, "/")
|
||||
source = connection.User.GetCleanedPath(source)
|
||||
target = connection.User.GetCleanedPath(target)
|
||||
if copyFromSource {
|
||||
source += "/"
|
||||
}
|
||||
if copyInTarget {
|
||||
target += "/"
|
||||
}
|
||||
task := webTaskData{
|
||||
ID: connection.GetID(),
|
||||
User: connection.GetUsername(),
|
||||
Path: source,
|
||||
Target: target,
|
||||
Timestamp: util.GetTimeAsMsSinceEpoch(time.Now()),
|
||||
Status: 0,
|
||||
}
|
||||
if err := webTaskMgr.Add(task); err != nil {
|
||||
common.Connections.Remove(connection.GetID())
|
||||
sendAPIResponse(w, r, nil, "Unable to create task", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
go executeCopyTask(connection, task)
|
||||
sendAPIResponse(w, r, nil, task.ID, http.StatusAccepted)
|
||||
}
|
||||
|
||||
func executeDeleteTask(conn *Connection, task webTaskData) {
|
||||
done := make(chan bool)
|
||||
|
||||
defer func() {
|
||||
close(done)
|
||||
common.Connections.Remove(conn.GetID())
|
||||
}()
|
||||
|
||||
go keepAliveTask(task, done, 2*time.Minute)
|
||||
|
||||
status := http.StatusOK
|
||||
if err := conn.RemoveAll(task.Path); err != nil {
|
||||
status = getMappedStatusCode(err)
|
||||
}
|
||||
|
||||
task.Timestamp = util.GetTimeAsMsSinceEpoch(time.Now())
|
||||
task.Status = status
|
||||
err := webTaskMgr.Add(task)
|
||||
conn.Log(logger.LevelDebug, "delete task finished, status: %d, update task err: %v", status, err)
|
||||
}
|
||||
|
||||
func executeRenameTask(conn *Connection, task webTaskData) {
|
||||
done := make(chan bool)
|
||||
|
||||
defer func() {
|
||||
close(done)
|
||||
common.Connections.Remove(conn.GetID())
|
||||
}()
|
||||
|
||||
go keepAliveTask(task, done, 2*time.Minute)
|
||||
|
||||
status := http.StatusOK
|
||||
|
||||
if !conn.IsSameResource(task.Path, task.Target) {
|
||||
if err := conn.Copy(task.Path, task.Target); err != nil {
|
||||
status = getMappedStatusCode(err)
|
||||
task.Timestamp = util.GetTimeAsMsSinceEpoch(time.Now())
|
||||
task.Status = status
|
||||
err = webTaskMgr.Add(task)
|
||||
conn.Log(logger.LevelDebug, "copy step for rename task finished, status: %d, update task err: %v", status, err)
|
||||
return
|
||||
}
|
||||
if err := conn.RemoveAll(task.Path); err != nil {
|
||||
status = getMappedStatusCode(err)
|
||||
}
|
||||
} else {
|
||||
if err := conn.Rename(task.Path, task.Target); err != nil {
|
||||
status = getMappedStatusCode(err)
|
||||
}
|
||||
}
|
||||
|
||||
task.Timestamp = util.GetTimeAsMsSinceEpoch(time.Now())
|
||||
task.Status = status
|
||||
err := webTaskMgr.Add(task)
|
||||
conn.Log(logger.LevelDebug, "rename task finished, status: %d, update task err: %v", status, err)
|
||||
}
|
||||
|
||||
func executeCopyTask(conn *Connection, task webTaskData) {
|
||||
done := make(chan bool)
|
||||
|
||||
defer func() {
|
||||
close(done)
|
||||
common.Connections.Remove(conn.GetID())
|
||||
}()
|
||||
|
||||
go keepAliveTask(task, done, 2*time.Minute)
|
||||
|
||||
status := http.StatusOK
|
||||
if err := conn.Copy(task.Path, task.Target); err != nil {
|
||||
status = getMappedStatusCode(err)
|
||||
}
|
||||
|
||||
task.Timestamp = util.GetTimeAsMsSinceEpoch(time.Now())
|
||||
task.Status = status
|
||||
err := webTaskMgr.Add(task)
|
||||
conn.Log(logger.LevelDebug, "copy task finished, status: %d, update task err: %v", status, err)
|
||||
}
|
||||
|
||||
func keepAliveTask(task webTaskData, done chan bool, interval time.Duration) {
|
||||
ticker := time.NewTicker(interval)
|
||||
defer func() {
|
||||
ticker.Stop()
|
||||
}()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-done:
|
||||
return
|
||||
case <-ticker.C:
|
||||
task.Timestamp = util.GetTimeAsMsSinceEpoch(time.Now())
|
||||
err := webTaskMgr.Add(task)
|
||||
logger.Debug(logSender, task.ID, "task timestamp updated, err: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
108
internal/httpd/webtask.go
Normal file
108
internal/httpd/webtask.go
Normal file
|
@ -0,0 +1,108 @@
|
|||
// Copyright (C) 2024 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/>.
|
||||
|
||||
package httpd
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
|
||||
"github.com/drakkan/sftpgo/v2/internal/logger"
|
||||
"github.com/drakkan/sftpgo/v2/internal/util"
|
||||
)
|
||||
|
||||
var (
|
||||
webTaskMgr webTaskManager
|
||||
)
|
||||
|
||||
func newWebTaskManager(isShared int) webTaskManager {
|
||||
if isShared == 1 {
|
||||
logger.Info(logSender, "", "using provider task manager")
|
||||
return &dbTaskManager{}
|
||||
}
|
||||
logger.Info(logSender, "", "using memory task manager")
|
||||
return &memoryTaskManager{}
|
||||
}
|
||||
|
||||
type webTaskManager interface {
|
||||
Add(data webTaskData) error
|
||||
Get(ID string) (webTaskData, error)
|
||||
Cleanup()
|
||||
}
|
||||
|
||||
type webTaskData struct {
|
||||
ID string `json:"id"`
|
||||
User string `json:"user"`
|
||||
Path string `json:"path"`
|
||||
Target string `json:"target"`
|
||||
Timestamp int64 `json:"ts"`
|
||||
Status int `json:"status"` // 0 in progress or http status code (200 ok, 403 and so on)
|
||||
}
|
||||
|
||||
type memoryTaskManager struct {
|
||||
tasks sync.Map
|
||||
}
|
||||
|
||||
func (m *memoryTaskManager) Add(data webTaskData) error {
|
||||
m.tasks.Store(data.ID, &data)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *memoryTaskManager) Get(ID string) (webTaskData, error) {
|
||||
data, ok := m.tasks.Load(ID)
|
||||
if !ok {
|
||||
return webTaskData{}, util.NewRecordNotFoundError(fmt.Sprintf("task for ID %q not found", ID))
|
||||
}
|
||||
return *data.(*webTaskData), nil
|
||||
}
|
||||
|
||||
func (m *memoryTaskManager) Cleanup() {
|
||||
m.tasks.Range(func(key, value any) bool {
|
||||
data := value.(*webTaskData)
|
||||
if data.Timestamp < util.GetTimeAsMsSinceEpoch(time.Now().Add(-5*time.Minute)) {
|
||||
m.tasks.Delete(key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
|
||||
type dbTaskManager struct{}
|
||||
|
||||
func (m *dbTaskManager) Add(data webTaskData) error {
|
||||
session := dataprovider.Session{
|
||||
Key: data.ID,
|
||||
Data: data,
|
||||
Type: dataprovider.SessionTypeWebTask,
|
||||
Timestamp: data.Timestamp,
|
||||
}
|
||||
return dataprovider.AddSharedSession(session)
|
||||
}
|
||||
|
||||
func (m *dbTaskManager) Get(ID string) (webTaskData, error) {
|
||||
sess, err := dataprovider.GetSharedSession(ID)
|
||||
if err != nil {
|
||||
return webTaskData{}, err
|
||||
}
|
||||
d := sess.Data.([]byte)
|
||||
var data webTaskData
|
||||
err = json.Unmarshal(d, &data)
|
||||
return data, err
|
||||
}
|
||||
|
||||
func (m *dbTaskManager) Cleanup() {
|
||||
dataprovider.CleanupSharedSessions(dataprovider.SessionTypeWebTask, time.Now().Add(-5*time.Minute)) //nolint:errcheck
|
||||
}
|
133
internal/httpd/webtask_test.go
Normal file
133
internal/httpd/webtask_test.go
Normal file
|
@ -0,0 +1,133 @@
|
|||
// Copyright (C) 2024 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/>.
|
||||
|
||||
package httpd
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/rs/xid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/drakkan/sftpgo/v2/internal/util"
|
||||
)
|
||||
|
||||
func TestMemoryWebTaskManager(t *testing.T) {
|
||||
mgr := newWebTaskManager(0)
|
||||
m, ok := mgr.(*memoryTaskManager)
|
||||
require.True(t, ok)
|
||||
task := webTaskData{
|
||||
ID: xid.New().String(),
|
||||
User: defeaultUsername,
|
||||
Timestamp: time.Now().Add(-1 * time.Hour).UnixMilli(),
|
||||
Status: 0,
|
||||
}
|
||||
task1 := webTaskData{
|
||||
ID: xid.New().String(),
|
||||
User: defeaultUsername,
|
||||
Timestamp: time.Now().UnixMilli(),
|
||||
Status: 0,
|
||||
}
|
||||
err := m.Add(task)
|
||||
require.NoError(t, err)
|
||||
err = m.Add(task1)
|
||||
require.NoError(t, err)
|
||||
taskGet, err := m.Get(task.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, task, taskGet)
|
||||
m.Cleanup()
|
||||
_, err = m.Get(task.ID)
|
||||
require.ErrorIs(t, err, util.ErrNotFound)
|
||||
taskGet, err = m.Get(task1.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, task1, taskGet)
|
||||
task1.Timestamp = time.Now().Add(-1 * time.Hour).UnixMilli()
|
||||
err = m.Add(task1)
|
||||
require.NoError(t, err)
|
||||
m.Cleanup()
|
||||
_, err = m.Get(task.ID)
|
||||
require.ErrorIs(t, err, util.ErrNotFound)
|
||||
// test keep alive task
|
||||
oldMgr := webTaskMgr
|
||||
webTaskMgr = mgr
|
||||
|
||||
done := make(chan bool)
|
||||
go keepAliveTask(task, done, 50*time.Millisecond)
|
||||
|
||||
time.Sleep(120 * time.Millisecond)
|
||||
close(done)
|
||||
taskGet, err = m.Get(task.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Greater(t, taskGet.Timestamp, task.Timestamp)
|
||||
m.Cleanup()
|
||||
_, err = m.Get(task.ID)
|
||||
require.NoError(t, err)
|
||||
err = m.Add(task)
|
||||
require.NoError(t, err)
|
||||
m.Cleanup()
|
||||
_, err = m.Get(task.ID)
|
||||
require.ErrorIs(t, err, util.ErrNotFound)
|
||||
|
||||
webTaskMgr = oldMgr
|
||||
}
|
||||
|
||||
func TestDbWebTaskManager(t *testing.T) {
|
||||
if !isSharedProviderSupported() {
|
||||
t.Skip("this test it is not available with this provider")
|
||||
}
|
||||
mgr := newWebTaskManager(1)
|
||||
m, ok := mgr.(*dbTaskManager)
|
||||
require.True(t, ok)
|
||||
|
||||
task := webTaskData{
|
||||
ID: xid.New().String(),
|
||||
User: defeaultUsername,
|
||||
Timestamp: time.Now().Add(-1 * time.Hour).UnixMilli(),
|
||||
Status: 0,
|
||||
}
|
||||
err := m.Add(task)
|
||||
require.NoError(t, err)
|
||||
taskGet, err := m.Get(task.ID)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, task, taskGet)
|
||||
m.Cleanup()
|
||||
_, err = m.Get(task.ID)
|
||||
require.ErrorIs(t, err, util.ErrNotFound)
|
||||
err = m.Add(task)
|
||||
require.NoError(t, err)
|
||||
// test keep alive task
|
||||
oldMgr := webTaskMgr
|
||||
webTaskMgr = mgr
|
||||
|
||||
done := make(chan bool)
|
||||
go keepAliveTask(task, done, 50*time.Millisecond)
|
||||
|
||||
time.Sleep(120 * time.Millisecond)
|
||||
close(done)
|
||||
taskGet, err = m.Get(task.ID)
|
||||
require.NoError(t, err)
|
||||
assert.Greater(t, taskGet.Timestamp, task.Timestamp)
|
||||
m.Cleanup()
|
||||
_, err = m.Get(task.ID)
|
||||
require.NoError(t, err)
|
||||
err = m.Add(task)
|
||||
require.NoError(t, err)
|
||||
m.Cleanup()
|
||||
_, err = m.Get(task.ID)
|
||||
require.ErrorIs(t, err, util.ErrNotFound)
|
||||
|
||||
webTaskMgr = oldMgr
|
||||
}
|
|
@ -264,6 +264,62 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
//{{- end}}
|
||||
}
|
||||
|
||||
var taskStatusWaiter = function () {
|
||||
var promiseResolve;
|
||||
|
||||
function getTaskStatus(taskID, numErrors) {
|
||||
axios.get('{{.TasksURL}}'+"/"+encodeURIComponent(taskID),{
|
||||
timeout: 15000,
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{.CSRFToken}}'
|
||||
},
|
||||
validateStatus: function (status) {
|
||||
return status == 200;
|
||||
}
|
||||
}).then(function(response){
|
||||
let status = response.data.status;
|
||||
if (status == 0){
|
||||
setTimeout(function() {
|
||||
getTaskStatus(taskID, numErrors);
|
||||
}, 2500);
|
||||
} else {
|
||||
promiseResolve({
|
||||
status: status
|
||||
});
|
||||
}
|
||||
}).catch(function(error){
|
||||
numErrors++;
|
||||
if (numErrors >= 3){
|
||||
promiseResolve({
|
||||
status: 0
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (error && error.response && error.response.status == 404){
|
||||
promiseResolve({
|
||||
status: 404
|
||||
});
|
||||
return;
|
||||
}
|
||||
setTimeout(function() {
|
||||
getTaskStatus(taskID, numErrors);
|
||||
}, 2500);
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
wait: function(params){
|
||||
setTimeout(function() {
|
||||
getTaskStatus(params.taskID, 0);
|
||||
}, params.delay);
|
||||
|
||||
return new Promise(function(resolve, reject) {
|
||||
promiseResolve = resolve;
|
||||
});
|
||||
}
|
||||
}
|
||||
}();
|
||||
|
||||
//{{- if not .ShareUploadBaseURL}}
|
||||
var KTDatatablesFoldersExplorer = function () {
|
||||
var dt;
|
||||
|
@ -1048,6 +1104,35 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
|
||||
const deleteButton = document.querySelector('[data-kt-filemanager-table-select="delete_selected"]');
|
||||
if (deleteButton) {
|
||||
|
||||
function getMultiDeleteErrorMessage(status, deleted) {
|
||||
let errorMessage;
|
||||
switch (status) {
|
||||
case 403:
|
||||
if (deleted > 0){
|
||||
errorMessage = "fs.delete_multi.err_403_partial";
|
||||
} else {
|
||||
errorMessage = "fs.delete_multi.err_403";
|
||||
}
|
||||
break;
|
||||
case 429:
|
||||
if (deleted > 0){
|
||||
errorMessage = "fs.delete_multi.err_429_partial";
|
||||
} else {
|
||||
errorMessage = "fs.delete_multi.err_429";
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (!errorMessage){
|
||||
if (deleted > 0){
|
||||
errorMessage = "fs.delete_multi.err_generic_partial";
|
||||
} else {
|
||||
errorMessage = "fs.delete_multi.err_generic";
|
||||
}
|
||||
}
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
let el = $(deleteButton);
|
||||
el.off("click");
|
||||
el.on('click', function(e){
|
||||
|
@ -1075,14 +1160,11 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
if (selectedRowsIdx.length == 0){
|
||||
return;
|
||||
}
|
||||
keepAlive();
|
||||
let keepAliveTimer = setInterval(keepAlive, 300000);
|
||||
$('#loading_message').text("");
|
||||
KTApp.showPageLoading();
|
||||
|
||||
function deleteSelected() {
|
||||
if (index >= selectedRowsIdx.length || hasError){
|
||||
clearInterval(keepAliveTimer);
|
||||
KTApp.hidePageLoading();
|
||||
if (!hasError){
|
||||
location.reload();
|
||||
|
@ -1090,57 +1172,66 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
return;
|
||||
}
|
||||
let meta = dt.row(selectedRowsIdx[index]).data()['meta'];
|
||||
let attrs = getDeleteReqAttrs(meta);
|
||||
let itemName = getNameFromMeta(meta);
|
||||
let isDir = (getTypeFromMeta(meta) == "1");
|
||||
let path;
|
||||
if (isDir){
|
||||
path = '{{.DirsURL}}';
|
||||
} else {
|
||||
path = '{{.FilesURL}}';
|
||||
}
|
||||
path+='?path={{.CurrentDir}}'+encodeURIComponent("/"+itemName);
|
||||
|
||||
let deleteTxt = "";
|
||||
if (selectedRowsIdx.length > 1){
|
||||
deleteTxt = $.t('fs.deleting', {
|
||||
idx : index + 1,
|
||||
total: selectedRowsIdx.length,
|
||||
name: getNameFromMeta(meta)
|
||||
name: itemName
|
||||
});
|
||||
}
|
||||
$('#loading_message').text(deleteTxt);
|
||||
axios.delete(attrs.path,{
|
||||
timeout: attrs.reqTimeout,
|
||||
axios.delete(path,{
|
||||
timeout: 15000,
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{.CSRFToken}}'
|
||||
},
|
||||
validateStatus: function (status) {
|
||||
if (isDir){
|
||||
return status == 202;
|
||||
}
|
||||
return status == 200;
|
||||
}
|
||||
}).then(function(response){
|
||||
index++;
|
||||
deleted++;
|
||||
deleteSelected();
|
||||
if (isDir){
|
||||
taskStatusWaiter.wait({
|
||||
taskID: response.data.message,
|
||||
delay: 500
|
||||
}).then((result) => {
|
||||
index++;
|
||||
if (result.status == 200){
|
||||
deleted++;
|
||||
} else {
|
||||
hasError = true;
|
||||
let errorMessage = getMultiDeleteErrorMessage(result.status, deleted);
|
||||
setI18NData(errTxtEl, errorMessage);
|
||||
errDivEl.removeClass("d-none");
|
||||
}
|
||||
deleteSelected();
|
||||
});
|
||||
} else {
|
||||
index++;
|
||||
deleted++;
|
||||
deleteSelected();
|
||||
}
|
||||
}).catch(function(error){
|
||||
index++;
|
||||
hasError = true;
|
||||
let errorMessage;
|
||||
let status = 0;
|
||||
if (error && error.response) {
|
||||
switch (error.response.status) {
|
||||
case 403:
|
||||
if (deleted > 0){
|
||||
errorMessage = "fs.delete_multi.err_403_partial";
|
||||
} else {
|
||||
errorMessage = "fs.delete_multi.err_403";
|
||||
}
|
||||
break;
|
||||
case 429:
|
||||
if (deleted > 0){
|
||||
errorMessage = "fs.delete_multi.err_429_partial";
|
||||
} else {
|
||||
errorMessage = "fs.delete_multi.err_429";
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!errorMessage){
|
||||
if (deleted > 0){
|
||||
errorMessage = "fs.delete_multi.err_generic_partial";
|
||||
} else {
|
||||
errorMessage = "fs.delete_multi.err_generic";
|
||||
}
|
||||
status = error.response.status;
|
||||
}
|
||||
let errorMessage = getMultiDeleteErrorMessage(status, deleted);
|
||||
setI18NData(errTxtEl, errorMessage);
|
||||
errDivEl.removeClass("d-none");
|
||||
deleteSelected();
|
||||
|
@ -1274,8 +1365,30 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
});
|
||||
return;
|
||||
}
|
||||
keepAlive();
|
||||
let keepAliveTimer = setInterval(keepAlive, 300000);
|
||||
|
||||
function showCopyError(status) {
|
||||
KTApp.hidePageLoading();
|
||||
let errorMessage = "";
|
||||
switch (status) {
|
||||
case 403:
|
||||
errorMessage = "fs.copy.err_403";
|
||||
break;
|
||||
case 429:
|
||||
errorMessage = "fs.copy.err_429";
|
||||
break;
|
||||
default:
|
||||
errorMessage = "fs.copy.err_generic";
|
||||
}
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let hasError = false;
|
||||
let index = 0;
|
||||
|
||||
|
@ -1284,7 +1397,6 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
|
||||
function copyItem() {
|
||||
if (index >= items.length || hasError){
|
||||
clearInterval(keepAliveTimer);
|
||||
KTApp.hidePageLoading();
|
||||
if (!hasError){
|
||||
location.reload();
|
||||
|
@ -1316,41 +1428,33 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
path+='?path={{.CurrentDir}}'+encodeURIComponent("/"+item.sourceName)+'&target='+item.targetDir+encodeURIComponent("/"+item.targetName);
|
||||
|
||||
axios.post(path, null, {
|
||||
timeout: 180000,
|
||||
timeout: 15000,
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{.CSRFToken}}'
|
||||
},
|
||||
validateStatus: function (status) {
|
||||
return status == 200;
|
||||
return status == 202;
|
||||
}
|
||||
}).then(function (response) {
|
||||
index++;
|
||||
copyItem();
|
||||
taskStatusWaiter.wait({
|
||||
taskID: response.data.message,
|
||||
delay: 500
|
||||
}).then((result) => {
|
||||
index++;
|
||||
if (result.status != 200){
|
||||
hasError = true;
|
||||
showCopyError(result.status);
|
||||
}
|
||||
copyItem();
|
||||
});
|
||||
}).catch(function (error) {
|
||||
index++;
|
||||
hasError = true;
|
||||
let errorMessage = "";
|
||||
let status = 0;
|
||||
if (error && error.response) {
|
||||
switch (error.response.status) {
|
||||
case 403:
|
||||
errorMessage = "fs.copy.err_403";
|
||||
break;
|
||||
case 429:
|
||||
errorMessage = "fs.copy.err_429";
|
||||
break;
|
||||
}
|
||||
status = error.response.status;
|
||||
}
|
||||
if (!errorMessage){
|
||||
errorMessage = "fs.copy.err_generic";
|
||||
}
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
showCopyError(status);
|
||||
copyItem();
|
||||
});
|
||||
}
|
||||
|
@ -1410,8 +1514,33 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
});
|
||||
return;
|
||||
}
|
||||
keepAlive();
|
||||
let keepAliveTimer = setInterval(keepAlive, 300000);
|
||||
|
||||
function showMoveError(status) {
|
||||
KTApp.hidePageLoading();
|
||||
let errorMessage = "";
|
||||
switch (status) {
|
||||
case 400:
|
||||
errorMessage = "fs.move.err_unsupported";
|
||||
break;
|
||||
case 403:
|
||||
errorMessage = "fs.move.err_403";
|
||||
break;
|
||||
case 429:
|
||||
errorMessage = "fs.move.err_429";
|
||||
break;
|
||||
default:
|
||||
errorMessage = "fs.move.err_generic";
|
||||
}
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let hasError = false;
|
||||
let index = 0;
|
||||
|
||||
|
@ -1420,7 +1549,6 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
|
||||
function moveItem() {
|
||||
if (index >= items.length || hasError){
|
||||
clearInterval(keepAliveTimer);
|
||||
KTApp.hidePageLoading();
|
||||
if (!hasError){
|
||||
location.reload();
|
||||
|
@ -1452,44 +1580,33 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
path+='?path={{.CurrentDir}}'+encodeURIComponent("/"+item.sourceName)+'&target='+item.targetDir+encodeURIComponent("/"+item.targetName);
|
||||
|
||||
axios.post(path, null, {
|
||||
timeout: 180000,
|
||||
timeout: 15000,
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{.CSRFToken}}'
|
||||
},
|
||||
validateStatus: function (status) {
|
||||
return status == 200;
|
||||
return status == 202;
|
||||
}
|
||||
}).then(function (response) {
|
||||
index++;
|
||||
moveItem();
|
||||
taskStatusWaiter.wait({
|
||||
taskID: response.data.message,
|
||||
delay: 500
|
||||
}).then((result) => {
|
||||
index++;
|
||||
if (result.status != 200){
|
||||
hasError = true;
|
||||
showMoveError(result.status);
|
||||
}
|
||||
moveItem();
|
||||
});
|
||||
}).catch(function (error) {
|
||||
index++;
|
||||
hasError = true;
|
||||
let errorMessage = "";
|
||||
let status = 0;
|
||||
if (error && error.response) {
|
||||
switch (error.response.status) {
|
||||
case 400:
|
||||
errorMessage = "fs.move.err_unsupported";
|
||||
break;
|
||||
case 403:
|
||||
errorMessage = "fs.move.err_403";
|
||||
break;
|
||||
case 429:
|
||||
errorMessage = "fs.move.err_429";
|
||||
break;
|
||||
}
|
||||
status = error.response.status;
|
||||
}
|
||||
if (!errorMessage){
|
||||
errorMessage = "fs.move.err_generic";
|
||||
}
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
showMoveError(status);
|
||||
moveItem();
|
||||
});
|
||||
}
|
||||
|
@ -1531,20 +1648,27 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
});
|
||||
}
|
||||
|
||||
function getDeleteReqAttrs(meta) {
|
||||
let path;
|
||||
let reqTimeout = 15000;
|
||||
let itemType = getTypeFromMeta(meta);
|
||||
let itemName = getNameFromMeta(meta);
|
||||
if (itemType == "1"){
|
||||
path = '{{.DirsURL}}';
|
||||
reqTimeout = 120000
|
||||
} else {
|
||||
path = '{{.FilesURL}}';
|
||||
function showDeleteItemError(status, itemName) {
|
||||
KTApp.hidePageLoading();
|
||||
let errorMessage;
|
||||
switch (status) {
|
||||
case 403:
|
||||
errorMessage = "fs.delete.err_403";
|
||||
break;
|
||||
case 429:
|
||||
errorMessage = "fs.delete.err_429";
|
||||
break;
|
||||
default:
|
||||
errorMessage = "fs.delete.err_generic";
|
||||
}
|
||||
path+='?path={{.CurrentDir}}'+encodeURIComponent("/"+itemName);
|
||||
|
||||
return { path, reqTimeout}
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage, {name: itemName}),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function deleteItem(meta) {
|
||||
|
@ -1564,42 +1688,47 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
if (result.isConfirmed){
|
||||
$('#loading_message').text("");
|
||||
KTApp.showPageLoading();
|
||||
let attrs = getDeleteReqAttrs(meta);
|
||||
let isDir = (getTypeFromMeta(meta) == "1");
|
||||
let path;
|
||||
if (isDir){
|
||||
path = '{{.DirsURL}}';
|
||||
} else {
|
||||
path = '{{.FilesURL}}';
|
||||
}
|
||||
path+='?path={{.CurrentDir}}'+encodeURIComponent("/"+itemName);
|
||||
|
||||
axios.delete(attrs.path, {
|
||||
timeout: attrs.reqTimeout,
|
||||
axios.delete(path, {
|
||||
timeout: 15000,
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': '{{.CSRFToken}}'
|
||||
},
|
||||
validateStatus: function (status) {
|
||||
if (isDir){
|
||||
return status == 202;
|
||||
}
|
||||
return status == 200;
|
||||
}
|
||||
}).then(function(response){
|
||||
location.reload();
|
||||
if (isDir){
|
||||
taskStatusWaiter.wait({
|
||||
taskID: response.data.message,
|
||||
delay: 500
|
||||
}).then((result) => {
|
||||
if (result.status == 200){
|
||||
location.reload();
|
||||
} else {
|
||||
showDeleteItemError(result.status, itemName);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
location.reload();
|
||||
}
|
||||
}).catch(function(error){
|
||||
KTApp.hidePageLoading();
|
||||
let errorMessage;
|
||||
let status = 0;
|
||||
if (error && error.response) {
|
||||
switch (error.response.status) {
|
||||
case 403:
|
||||
errorMessage = "fs.delete.err_403";
|
||||
break;
|
||||
case 429:
|
||||
errorMessage = "fs.delete.err_429";
|
||||
break;
|
||||
}
|
||||
status = error.response.status;
|
||||
}
|
||||
if (!errorMessage){
|
||||
errorMessage = "fs.delete.err_generic";
|
||||
}
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage, {name: itemName}),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
showDeleteItemError(status, itemName);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -1624,6 +1753,33 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
$('#modal_rename').modal('show');
|
||||
}
|
||||
|
||||
function showRenameItemError(status, oldName) {
|
||||
KTApp.hidePageLoading();
|
||||
let errorMessage;
|
||||
switch (status) {
|
||||
case 400:
|
||||
errorMessage = "fs.rename.err_unsupported";
|
||||
break;
|
||||
case 403:
|
||||
errorMessage = "fs.rename.err_403";
|
||||
break;
|
||||
case 429:
|
||||
errorMessage = "fs.rename.err_429";
|
||||
break;
|
||||
default:
|
||||
errorMessage = "fs.rename.err_generic";
|
||||
}
|
||||
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage, {name: oldName}),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function doRename() {
|
||||
let meta = $('#rename_old_name').val();
|
||||
let oldName = getNameFromMeta(meta);
|
||||
|
@ -1674,37 +1830,25 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
|
|||
'X-CSRF-TOKEN': '{{.CSRFToken}}'
|
||||
},
|
||||
validateStatus: function (status) {
|
||||
return status == 200;
|
||||
return status == 202;
|
||||
}
|
||||
}).then(function (response) {
|
||||
location.reload();
|
||||
}).catch(function (error) {
|
||||
KTApp.hidePageLoading();
|
||||
let errorMessage;
|
||||
if (error && error.response) {
|
||||
switch (error.response.status) {
|
||||
case 400:
|
||||
errorMessage = "fs.rename.err_unsupported";
|
||||
break;
|
||||
case 403:
|
||||
errorMessage = "fs.rename.err_403";
|
||||
break;
|
||||
case 429:
|
||||
errorMessage = "fs.rename.err_429";
|
||||
break;
|
||||
taskStatusWaiter.wait({
|
||||
taskID: response.data.message,
|
||||
delay: 500
|
||||
}).then((result) => {
|
||||
if (result.status == 200){
|
||||
location.reload();
|
||||
} else {
|
||||
showRenameItemError(result.status, oldName);
|
||||
}
|
||||
});
|
||||
}).catch(function (error) {
|
||||
let status = 0;
|
||||
if (error && error.response) {
|
||||
status = error.response.status;
|
||||
}
|
||||
if (!errorMessage) {
|
||||
errorMessage = "fs.rename.err_generic";
|
||||
}
|
||||
ModalAlert.fire({
|
||||
text: $.t(errorMessage, {name: oldName}),
|
||||
icon: "warning",
|
||||
confirmButtonText: $.t('general.ok'),
|
||||
customClass: {
|
||||
confirmButton: "btn btn-primary"
|
||||
}
|
||||
});
|
||||
showRenameItemError(status, oldName);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue