mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-24 16:40:26 +00:00
web client: add HTML5 player
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
2da19ef233
commit
d34446e6e9
10 changed files with 172 additions and 72 deletions
|
@ -208,6 +208,16 @@ func (u *User) checkDirWithParents(virtualDirPath, connectionID string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (u *User) checkLocalHomeDir(connectionID string) {
|
||||||
|
switch u.FsConfig.Provider {
|
||||||
|
case sdk.LocalFilesystemProvider, sdk.CryptedFilesystemProvider:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
osFs := vfs.NewOsFs(connectionID, u.GetHomeDir(), "")
|
||||||
|
osFs.CheckRootPath(u.Username, u.GetUID(), u.GetGID())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (u *User) checkRootPath(connectionID string) error {
|
func (u *User) checkRootPath(connectionID string) error {
|
||||||
fs, err := u.GetFilesystemForPath("/", connectionID)
|
fs, err := u.GetFilesystemForPath("/", connectionID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -233,8 +243,8 @@ func (u *User) CheckFsRoot(connectionID string) error {
|
||||||
}
|
}
|
||||||
if isLastActivityRecent(u.LastLogin, delay) {
|
if isLastActivityRecent(u.LastLogin, delay) {
|
||||||
if u.LastLogin > u.UpdatedAt {
|
if u.LastLogin > u.UpdatedAt {
|
||||||
if u.FsConfig.Provider != sdk.LocalFilesystemProvider && config.IsShared == 1 {
|
if config.IsShared == 1 {
|
||||||
return u.checkRootPath(connectionID)
|
u.checkLocalHomeDir(connectionID)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
8
go.mod
8
go.mod
|
@ -65,8 +65,8 @@ require (
|
||||||
go.etcd.io/bbolt v1.3.6
|
go.etcd.io/bbolt v1.3.6
|
||||||
go.uber.org/automaxprocs v1.5.1
|
go.uber.org/automaxprocs v1.5.1
|
||||||
gocloud.dev v0.25.0
|
gocloud.dev v0.25.0
|
||||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d
|
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
|
||||||
golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e
|
golang.org/x/net v0.0.0-20220722155237-a158d28d115b
|
||||||
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c
|
golang.org/x/oauth2 v0.0.0-20220722155238-128564f6959c
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
|
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
|
||||||
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
|
golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9
|
||||||
|
@ -165,6 +165,6 @@ require (
|
||||||
|
|
||||||
replace (
|
replace (
|
||||||
github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9
|
github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9
|
||||||
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20220624102318-5fdf29404a2d
|
golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20220723134236-2c43acbc2436
|
||||||
golang.org/x/net => github.com/drakkan/net v0.0.0-20220628152131-9c7397602ad7
|
golang.org/x/net => github.com/drakkan/net v0.0.0-20220723134121-37f73db6d7c4
|
||||||
)
|
)
|
||||||
|
|
8
go.sum
8
go.sum
|
@ -259,12 +259,12 @@ github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQ
|
||||||
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
|
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
|
||||||
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||||
github.com/drakkan/crypto v0.0.0-20220624102318-5fdf29404a2d h1:VLfi41ryCFZIcCC4jZGAa1zLmF3Mu/GpplwXIuYV8/k=
|
github.com/drakkan/crypto v0.0.0-20220723134236-2c43acbc2436 h1:cctYUw+d5DrSgUhRSZF/GzDZtvNswdduqIkZM1Wi+qg=
|
||||||
github.com/drakkan/crypto v0.0.0-20220624102318-5fdf29404a2d/go.mod h1:SiM6ypd8Xu1xldObYtbDztuUU7xUzMnUULfphXFZmro=
|
github.com/drakkan/crypto v0.0.0-20220723134236-2c43acbc2436/go.mod h1:SiM6ypd8Xu1xldObYtbDztuUU7xUzMnUULfphXFZmro=
|
||||||
github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHPMtBLXhQmjaga91/DDjWk9jWA=
|
github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHPMtBLXhQmjaga91/DDjWk9jWA=
|
||||||
github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU=
|
github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU=
|
||||||
github.com/drakkan/net v0.0.0-20220628152131-9c7397602ad7 h1:/KuSaF3lQkzeeNsXiTPhnI2w4PlH3hbxN6h8tfKAc5E=
|
github.com/drakkan/net v0.0.0-20220723134121-37f73db6d7c4 h1:NbZDB/9nbn6i9XYsOfeu15vVw8bSnm3tRTVwgkP8OiQ=
|
||||||
github.com/drakkan/net v0.0.0-20220628152131-9c7397602ad7/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
github.com/drakkan/net v0.0.0-20220723134121-37f73db6d7c4/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||||
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 h1:/ZshrfQzayqRSBDodmp3rhNCHJCff+utvgBuWRbiqu4=
|
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 h1:/ZshrfQzayqRSBDodmp3rhNCHJCff+utvgBuWRbiqu4=
|
||||||
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001/go.mod h1:kltMsfRMTHSFdMbK66XdS8mfMW77+FZA1fGY1xYMF84=
|
github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001/go.mod h1:kltMsfRMTHSFdMbK66XdS8mfMW77+FZA1fGY1xYMF84=
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||||
|
|
|
@ -443,7 +443,7 @@ func checkIfRange(r *http.Request, modtime time.Time) condResult {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return condFalse
|
return condFalse
|
||||||
}
|
}
|
||||||
if modtime.Add(60 * time.Second).Before(t) {
|
if modtime.Unix() == t.Unix() {
|
||||||
return condTrue
|
return condTrue
|
||||||
}
|
}
|
||||||
return condFalse
|
return condFalse
|
||||||
|
|
|
@ -11885,6 +11885,8 @@ func TestWebGetFiles(t *testing.T) {
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusPartialContent, rr)
|
checkResponseCode(t, http.StatusPartialContent, rr)
|
||||||
assert.Equal(t, testFileContents[2:], rr.Body.Bytes())
|
assert.Equal(t, testFileContents[2:], rr.Body.Bytes())
|
||||||
|
lastModified, err := http.ParseTime(rr.Header().Get("Last-Modified"))
|
||||||
|
assert.NoError(t, err)
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
|
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
|
||||||
req.Header.Set("Range", "bytes=-2")
|
req.Header.Set("Range", "bytes=-2")
|
||||||
|
@ -11913,44 +11915,44 @@ func TestWebGetFiles(t *testing.T) {
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
|
req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
|
||||||
req.Header.Set("Range", "bytes=2-")
|
req.Header.Set("Range", "bytes=2-")
|
||||||
req.Header.Set("If-Range", time.Now().UTC().Add(120*time.Second).Format(http.TimeFormat))
|
req.Header.Set("If-Range", lastModified.UTC().Format(http.TimeFormat))
|
||||||
setJWTCookieForReq(req, webToken)
|
setJWTCookieForReq(req, webToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusPartialContent, rr)
|
checkResponseCode(t, http.StatusPartialContent, rr)
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
|
req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
|
||||||
req.Header.Set("Range", "bytes=2-")
|
req.Header.Set("Range", "bytes=2-")
|
||||||
req.Header.Set("If-Range", time.Now().UTC().Add(-120*time.Second).Format(http.TimeFormat))
|
req.Header.Set("If-Range", lastModified.UTC().Add(-120*time.Second).Format(http.TimeFormat))
|
||||||
setJWTCookieForReq(req, webToken)
|
setJWTCookieForReq(req, webToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusOK, rr)
|
checkResponseCode(t, http.StatusOK, rr)
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
|
req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
|
||||||
req.Header.Set("If-Modified-Since", time.Now().UTC().Add(-120*time.Second).Format(http.TimeFormat))
|
req.Header.Set("If-Modified-Since", lastModified.UTC().Add(-120*time.Second).Format(http.TimeFormat))
|
||||||
setJWTCookieForReq(req, webToken)
|
setJWTCookieForReq(req, webToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusOK, rr)
|
checkResponseCode(t, http.StatusOK, rr)
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
|
req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
|
||||||
req.Header.Set("If-Modified-Since", time.Now().UTC().Add(120*time.Second).Format(http.TimeFormat))
|
req.Header.Set("If-Modified-Since", lastModified.UTC().Add(120*time.Second).Format(http.TimeFormat))
|
||||||
setJWTCookieForReq(req, webToken)
|
setJWTCookieForReq(req, webToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusNotModified, rr)
|
checkResponseCode(t, http.StatusNotModified, rr)
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
|
req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
|
||||||
req.Header.Set("If-Unmodified-Since", time.Now().UTC().Add(-120*time.Second).Format(http.TimeFormat))
|
req.Header.Set("If-Unmodified-Since", lastModified.UTC().Add(-120*time.Second).Format(http.TimeFormat))
|
||||||
setJWTCookieForReq(req, webToken)
|
setJWTCookieForReq(req, webToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusPreconditionFailed, rr)
|
checkResponseCode(t, http.StatusPreconditionFailed, rr)
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodHead, userFilesPath+"?path="+testFileName, nil)
|
req, _ = http.NewRequest(http.MethodHead, userFilesPath+"?path="+testFileName, nil)
|
||||||
req.Header.Set("If-Unmodified-Since", time.Now().UTC().Add(-120*time.Second).Format(http.TimeFormat))
|
req.Header.Set("If-Unmodified-Since", lastModified.UTC().Add(-120*time.Second).Format(http.TimeFormat))
|
||||||
setBearerForReq(req, webAPIToken)
|
setBearerForReq(req, webAPIToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusPreconditionFailed, rr)
|
checkResponseCode(t, http.StatusPreconditionFailed, rr)
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
|
req, _ = http.NewRequest(http.MethodHead, webClientFilesPath+"?path="+testFileName, nil)
|
||||||
req.Header.Set("If-Unmodified-Since", time.Now().UTC().Add(120*time.Second).Format(http.TimeFormat))
|
req.Header.Set("If-Unmodified-Since", lastModified.UTC().Add(120*time.Second).Format(http.TimeFormat))
|
||||||
setJWTCookieForReq(req, webToken)
|
setJWTCookieForReq(req, webToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusOK, rr)
|
checkResponseCode(t, http.StatusOK, rr)
|
||||||
|
@ -13342,6 +13344,7 @@ func TestGetFilesSFTPBackend(t *testing.T) {
|
||||||
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
|
user, _, err := httpdtest.AddUser(getTestUser(), http.StatusCreated)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
u := getTestSFTPUser()
|
u := getTestSFTPUser()
|
||||||
|
u.HomeDir = filepath.Clean(os.TempDir())
|
||||||
u.FsConfig.SFTPConfig.BufferSize = 2
|
u.FsConfig.SFTPConfig.BufferSize = 2
|
||||||
u.Permissions["/adir"] = nil
|
u.Permissions["/adir"] = nil
|
||||||
u.Permissions["/adir1"] = []string{dataprovider.PermListItems}
|
u.Permissions["/adir1"] = []string{dataprovider.PermListItems}
|
||||||
|
@ -13351,7 +13354,11 @@ func TestGetFilesSFTPBackend(t *testing.T) {
|
||||||
DeniedPatterns: []string{"*.txt"},
|
DeniedPatterns: []string{"*.txt"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
sftpUser, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
sftpUserBuffered, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
u.Username += "_unbuffered"
|
||||||
|
u.FsConfig.SFTPConfig.BufferSize = 0
|
||||||
|
sftpUserUnbuffered, _, err := httpdtest.AddUser(u, http.StatusCreated)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
testFileName := "testsftpfile"
|
testFileName := "testsftpfile"
|
||||||
|
@ -13369,58 +13376,58 @@ func TestGetFilesSFTPBackend(t *testing.T) {
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = os.WriteFile(filepath.Join(user.GetHomeDir(), "adir2", "afile.txt"), testFileContents, os.ModePerm)
|
err = os.WriteFile(filepath.Join(user.GetHomeDir(), "adir2", "afile.txt"), testFileContents, os.ModePerm)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
webToken, err := getJWTWebClientTokenFromTestServer(sftpUser.Username, defaultPassword)
|
for _, sftpUser := range []dataprovider.User{sftpUserBuffered, sftpUserUnbuffered} {
|
||||||
assert.NoError(t, err)
|
webToken, err := getJWTWebClientTokenFromTestServer(sftpUser.Username, defaultPassword)
|
||||||
req, _ := http.NewRequest(http.MethodGet, webClientFilesPath, nil)
|
assert.NoError(t, err)
|
||||||
setJWTCookieForReq(req, webToken)
|
req, _ := http.NewRequest(http.MethodGet, webClientFilesPath, nil)
|
||||||
rr := executeRequest(req)
|
setJWTCookieForReq(req, webToken)
|
||||||
checkResponseCode(t, http.StatusOK, rr)
|
rr := executeRequest(req)
|
||||||
|
checkResponseCode(t, http.StatusOK, rr)
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+path.Join(testDir, "sub"), nil)
|
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+path.Join(testDir, "sub"), nil)
|
||||||
setJWTCookieForReq(req, webToken)
|
setJWTCookieForReq(req, webToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusOK, rr)
|
checkResponseCode(t, http.StatusOK, rr)
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+path.Join(testDir, "missing"), nil)
|
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+path.Join(testDir, "missing"), nil)
|
||||||
setJWTCookieForReq(req, webToken)
|
setJWTCookieForReq(req, webToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusOK, rr)
|
checkResponseCode(t, http.StatusOK, rr)
|
||||||
assert.Contains(t, rr.Body.String(), "card-body text-form-error")
|
assert.Contains(t, rr.Body.String(), "card-body text-form-error")
|
||||||
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path=adir/sub", nil)
|
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path=adir/sub", nil)
|
||||||
setJWTCookieForReq(req, webToken)
|
setJWTCookieForReq(req, webToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusOK, rr)
|
checkResponseCode(t, http.StatusOK, rr)
|
||||||
assert.Contains(t, rr.Body.String(), "card-body text-form-error")
|
assert.Contains(t, rr.Body.String(), "card-body text-form-error")
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path=adir1/afile", nil)
|
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path=adir1/afile", nil)
|
||||||
setJWTCookieForReq(req, webToken)
|
setJWTCookieForReq(req, webToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusOK, rr)
|
checkResponseCode(t, http.StatusOK, rr)
|
||||||
assert.Contains(t, rr.Body.String(), "card-body text-form-error")
|
assert.Contains(t, rr.Body.String(), "card-body text-form-error")
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path=adir2/afile.txt", nil)
|
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path=adir2/afile.txt", nil)
|
||||||
setJWTCookieForReq(req, webToken)
|
setJWTCookieForReq(req, webToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusOK, rr)
|
checkResponseCode(t, http.StatusOK, rr)
|
||||||
assert.Contains(t, rr.Body.String(), "card-body text-form-error")
|
assert.Contains(t, rr.Body.String(), "card-body text-form-error")
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
|
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
|
||||||
setJWTCookieForReq(req, webToken)
|
setJWTCookieForReq(req, webToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusOK, rr)
|
checkResponseCode(t, http.StatusOK, rr)
|
||||||
assert.Equal(t, testFileContents, rr.Body.Bytes())
|
assert.Equal(t, testFileContents, rr.Body.Bytes())
|
||||||
|
|
||||||
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
|
req, _ = http.NewRequest(http.MethodGet, webClientFilesPath+"?path="+testFileName, nil)
|
||||||
req.Header.Set("Range", "bytes=2-")
|
req.Header.Set("Range", "bytes=2-")
|
||||||
setJWTCookieForReq(req, webToken)
|
setJWTCookieForReq(req, webToken)
|
||||||
rr = executeRequest(req)
|
rr = executeRequest(req)
|
||||||
checkResponseCode(t, http.StatusPartialContent, rr)
|
checkResponseCode(t, http.StatusPartialContent, rr)
|
||||||
assert.Equal(t, testFileContents[2:], rr.Body.Bytes())
|
assert.Equal(t, testFileContents[2:], rr.Body.Bytes())
|
||||||
|
|
||||||
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
|
_, err = httpdtest.RemoveUser(sftpUser, http.StatusOK)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = os.RemoveAll(sftpUser.GetHomeDir())
|
}
|
||||||
assert.NoError(t, err)
|
|
||||||
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
_, err = httpdtest.RemoveUser(user, http.StatusOK)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
err = os.RemoveAll(user.GetHomeDir())
|
err = os.RemoveAll(user.GetHomeDir())
|
||||||
|
|
1
static/vendor/video-js/video-js.min.css
vendored
Normal file
1
static/vendor/video-js/video-js.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
25
static/vendor/video-js/video.min.js
vendored
Normal file
25
static/vendor/video-js/video.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -24,6 +24,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
<link href="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.css" rel="stylesheet">
|
<link href="{{.StaticURL}}/vendor/datatables/responsive.bootstrap4.min.css" rel="stylesheet">
|
||||||
<link href="{{.StaticURL}}/vendor/datatables/dataTables.checkboxes.css" rel="stylesheet">
|
<link href="{{.StaticURL}}/vendor/datatables/dataTables.checkboxes.css" rel="stylesheet">
|
||||||
<link href="{{.StaticURL}}/vendor/lightbox2/css/lightbox.min.css" rel="stylesheet">
|
<link href="{{.StaticURL}}/vendor/lightbox2/css/lightbox.min.css" rel="stylesheet">
|
||||||
|
<link href="{{.StaticURL}}/vendor/video-js/video-js.min.css" rel="stylesheet" />
|
||||||
<style>
|
<style>
|
||||||
div.dataTables_wrapper span.selected-info,
|
div.dataTables_wrapper span.selected-info,
|
||||||
div.dataTables_wrapper span.selected-item {
|
div.dataTables_wrapper span.selected-item {
|
||||||
|
@ -185,6 +186,27 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="modal fade" id="videoModal" tabindex="-1" role="dialog" aria-labelledby="videoModalLabel"
|
||||||
|
aria-hidden="true">
|
||||||
|
<div class="modal-dialog modal-lg" role="document">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="videoModalLabel">
|
||||||
|
<span id="video_title"></span>
|
||||||
|
</h5>
|
||||||
|
<button class="close" type="button" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<video id="video_player" class="video-js vjs-big-play-centered vjs-fluid">
|
||||||
|
<p class="vjs-no-js">To view this video please enable JavaScript, and consider upgrading to a web browser that supports HTML5 video</p>
|
||||||
|
</video>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="modal fade" id="spinnerModal" tabindex="-1" role="dialog" data-keyboard="false" data-backdrop="static">
|
<div class="modal fade" id="spinnerModal" tabindex="-1" role="dialog" data-keyboard="false" data-backdrop="static">
|
||||||
<div class="modal-dialog modal-dialog-centered justify-content-center" role="document">
|
<div class="modal-dialog modal-dialog-centered justify-content-center" role="document">
|
||||||
<span style="color: #333333;" class="fa fa-spinner fa-spin fa-3x"></span>
|
<span style="color: #333333;" class="fa fa-spinner fa-spin fa-3x"></span>
|
||||||
|
@ -206,6 +228,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
<script src="{{.StaticURL}}/vendor/pdfobject/pdfobject.min.js"></script>
|
<script src="{{.StaticURL}}/vendor/pdfobject/pdfobject.min.js"></script>
|
||||||
<script src="{{.StaticURL}}/vendor/codemirror/codemirror.js"></script>
|
<script src="{{.StaticURL}}/vendor/codemirror/codemirror.js"></script>
|
||||||
<script src="{{.StaticURL}}/vendor/codemirror/meta.js"></script>
|
<script src="{{.StaticURL}}/vendor/codemirror/meta.js"></script>
|
||||||
|
<script src="{{.StaticURL}}/vendor/video-js/video.min.js"></script>
|
||||||
{{if .HasIntegrations}}
|
{{if .HasIntegrations}}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var childReference = null;
|
var childReference = null;
|
||||||
|
@ -407,6 +430,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
{{end}}
|
{{end}}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var spinnerDone = false;
|
var spinnerDone = false;
|
||||||
|
var player;
|
||||||
|
var playerKeepAlive;
|
||||||
|
|
||||||
var escapeHTML = function ( t ) {
|
var escapeHTML = function ( t ) {
|
||||||
return t
|
return t
|
||||||
|
@ -429,6 +454,17 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
return shortened+'…';
|
return shortened+'…';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function openVideoPlayer(name, url, videoType){
|
||||||
|
$("#video_title").text(name);
|
||||||
|
$('#videoModal').modal('show');
|
||||||
|
player.src({
|
||||||
|
type: videoType,
|
||||||
|
src: url
|
||||||
|
});
|
||||||
|
keepAlive();
|
||||||
|
playerKeepAlive = setInterval(keepAlive, 300000);
|
||||||
|
}
|
||||||
|
|
||||||
function getIconForFile(filename) {
|
function getIconForFile(filename) {
|
||||||
var extension = filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
|
var extension = filename.slice((filename.lastIndexOf(".") - 1 >>> 0) + 2).toLowerCase();
|
||||||
switch (extension) {
|
switch (extension) {
|
||||||
|
@ -460,6 +496,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
case "mpeg":
|
case "mpeg":
|
||||||
case "mpv":
|
case "mpv":
|
||||||
case "3gp":
|
case "3gp":
|
||||||
|
case "mp4":
|
||||||
return "far fa-file-video";
|
return "far fa-file-video";
|
||||||
case "jpeg":
|
case "jpeg":
|
||||||
case "jpg":
|
case "jpg":
|
||||||
|
@ -622,6 +659,19 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
}
|
}
|
||||||
|
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
player = videojs('video_player', {
|
||||||
|
controls: true,
|
||||||
|
autoplay: false,
|
||||||
|
preload: 'auto'
|
||||||
|
});
|
||||||
|
$('#videoModal').on('hide.bs.modal', function () {
|
||||||
|
player.pause();
|
||||||
|
player.reset();
|
||||||
|
if (playerKeepAlive != null){
|
||||||
|
clearInterval(playerKeepAlive);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
$('#spinnerModal').on('shown.bs.modal', function () {
|
$('#spinnerModal').on('shown.bs.modal', function () {
|
||||||
if (spinnerDone){
|
if (spinnerDone){
|
||||||
$('#spinnerModal').modal('hide');
|
$('#spinnerModal').modal('hide');
|
||||||
|
@ -998,6 +1048,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
case "ico":
|
case "ico":
|
||||||
var view_url = row['url']+"&inline=1";
|
var view_url = row['url']+"&inline=1";
|
||||||
return `<a href="${view_url}" data-lightbox="${filename}" data-title="${filename}"><i class="fas fa-eye"></i></a>`;
|
return `<a href="${view_url}" data-lightbox="${filename}" data-title="${filename}"><i class="fas fa-eye"></i></a>`;
|
||||||
|
case "mp4":
|
||||||
|
case "mov":
|
||||||
|
return `<a href="#" onclick="openVideoPlayer('${row["name"]}', '${row['url']}', 'video/mp4');"><i class="fas fa-eye"></i></a>`;
|
||||||
|
case "webm":
|
||||||
|
return `<a href="#" onclick="openVideoPlayer('${row["name"]}', '${row['url']}', 'video/webm');"><i class="fas fa-eye"></i></a>`;
|
||||||
|
case "ogv":
|
||||||
|
case "ogg":
|
||||||
|
return `<a href="#" onclick="openVideoPlayer('${row["name"]}', '${row['url']}', 'video/ogg');"><i class="fas fa-eye"></i></a>`;
|
||||||
case "pdf":
|
case "pdf":
|
||||||
if (PDFObject.supportsPDFs){
|
if (PDFObject.supportsPDFs){
|
||||||
var view_url = row['url'];
|
var view_url = row['url'];
|
||||||
|
|
|
@ -147,6 +147,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
case "mpeg":
|
case "mpeg":
|
||||||
case "mpv":
|
case "mpv":
|
||||||
case "3gp":
|
case "3gp":
|
||||||
|
case "mp4":
|
||||||
return "far fa-file-video";
|
return "far fa-file-video";
|
||||||
case "jpeg":
|
case "jpeg":
|
||||||
case "jpg":
|
case "jpg":
|
||||||
|
|
|
@ -295,9 +295,6 @@ func (fs *SFTPFs) Open(name string, offset int64) (File, *pipeat.PipeReaderAt, f
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
if fs.config.BufferSize == 0 {
|
|
||||||
return f, nil, nil, err
|
|
||||||
}
|
|
||||||
if offset > 0 {
|
if offset > 0 {
|
||||||
_, err = f.Seek(offset, io.SeekStart)
|
_, err = f.Seek(offset, io.SeekStart)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -305,6 +302,9 @@ func (fs *SFTPFs) Open(name string, offset int64) (File, *pipeat.PipeReaderAt, f
|
||||||
return nil, nil, nil, err
|
return nil, nil, nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if fs.config.BufferSize == 0 {
|
||||||
|
return f, nil, nil, nil
|
||||||
|
}
|
||||||
r, w, err := pipeat.PipeInDir(fs.localTempDir)
|
r, w, err := pipeat.PipeInDir(fs.localTempDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.Close()
|
f.Close()
|
||||||
|
@ -507,11 +507,9 @@ func (*SFTPFs) IsNotSupported(err error) bool {
|
||||||
|
|
||||||
// CheckRootPath creates the specified local root directory if it does not exists
|
// CheckRootPath creates the specified local root directory if it does not exists
|
||||||
func (fs *SFTPFs) CheckRootPath(username string, uid int, gid int) bool {
|
func (fs *SFTPFs) CheckRootPath(username string, uid int, gid int) bool {
|
||||||
if fs.config.BufferSize > 0 {
|
// we need a local directory for temporary files
|
||||||
// we need a local directory for temporary files
|
osFs := NewOsFs(fs.ConnectionID(), fs.localTempDir, "")
|
||||||
osFs := NewOsFs(fs.ConnectionID(), fs.localTempDir, "")
|
osFs.CheckRootPath(username, uid, gid)
|
||||||
osFs.CheckRootPath(username, uid, gid)
|
|
||||||
}
|
|
||||||
if fs.config.Prefix == "/" {
|
if fs.config.Prefix == "/" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue