diff --git a/go.mod b/go.mod index 90e05b2b..84a9cfa5 100644 --- a/go.mod +++ b/go.mod @@ -46,6 +46,7 @@ require ( github.com/golang/geo v0.0.0-20250606134707-e8fe6a72b492 // indirect github.com/golang/snappy v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/jellydator/ttlcache/v3 v3.4.0 // indirect github.com/klauspost/compress v1.18.0 // indirect github.com/klauspost/pgzip v1.2.6 // indirect github.com/nwaples/rardecode v1.1.3 // indirect @@ -61,6 +62,7 @@ require ( github.com/yusufpapurcu/wmi v1.2.4 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/net v0.41.0 // indirect + golang.org/x/sync v0.15.0 // indirect golang.org/x/sys v0.33.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 27e66a86..72fa02b5 100644 --- a/go.sum +++ b/go.sum @@ -83,6 +83,8 @@ github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aN github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY= +github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= diff --git a/http/tus_handlers.go b/http/tus_handlers.go index 4677b12b..3bb99f1e 100644 --- a/http/tus_handlers.go +++ b/http/tus_handlers.go @@ -1,6 +1,7 @@ package http import ( + "context" "errors" "fmt" "io" @@ -8,32 +9,47 @@ import ( "os" "path/filepath" "strconv" + "time" + "github.com/jellydator/ttlcache/v3" "github.com/spf13/afero" - "sync" - "github.com/filebrowser/filebrowser/v2/files" ) -// Tracks active uploads along with their respective upload lengths -var activeUploads sync.Map +const maxUploadWait = 3 * time.Minute -func registerUpload(filePath string, fileSize int64) { - activeUploads.Store(filePath, fileSize) +// Tracks active uploads along with their respective upload lengths +var activeUploads = initActiveUploads() + +func initActiveUploads() *ttlcache.Cache[string, int64] { + cache := ttlcache.New[string, int64]() + cache.OnEviction(func(_ context.Context, reason ttlcache.EvictionReason, item *ttlcache.Item[string, int64]) { + if reason == ttlcache.EvictionReasonExpired { + fmt.Printf("deleting incomplete upload file: \"%s\"", item.Key()) + os.Remove(item.Key()) + } + }) + go cache.Start() + + return cache } -func unregisterUpload(filePath string) { +func registerUpload(filePath string, fileSize int64) { + activeUploads.Set(filePath, fileSize, maxUploadWait) +} + +func completeUpload(filePath string) { activeUploads.Delete(filePath) } func getActiveUploadLength(filePath string) (int64, error) { - value, exists := activeUploads.Load(filePath) - if !exists { + item := activeUploads.Get(filePath) + if item == nil { return 0, fmt.Errorf("no active upload found for the given path") } - return value.(int64), nil + return item.Value(), nil } func tusPostHandler() handleFunc { @@ -212,7 +228,7 @@ func tusPatchHandler() handleFunc { w.Header().Set("Upload-Offset", strconv.FormatInt(newOffset, 10)) if newOffset >= uploadLength { - unregisterUpload(file.RealPath()) + completeUpload(file.RealPath()) _ = d.RunHook(func() error { return nil }, "upload", r.URL.Path, "", d.user) }