diff --git a/server/nativeapi/albums.go b/server/nativeapi/albums.go index d170b6ddd..469963fcc 100644 --- a/server/nativeapi/albums.go +++ b/server/nativeapi/albums.go @@ -32,6 +32,8 @@ func (api *Router) addAlbumRoute(r chi.Router) { func (api *Router) uploadAlbumImage() http.HandlerFunc { return handleImageUpload(func(ctx context.Context, reader io.Reader, ext string) error { + api.albumImgOps.Lock() + defer api.albumImgOps.Unlock() albumID := chi.URLParamFromCtx(ctx, "id") al, err := api.ds.Album(ctx).Get(albumID) if err != nil { @@ -77,6 +79,8 @@ func (api *Router) albumImagePathToRemove(ctx context.Context, al *model.Album) func (api *Router) deleteAlbumImage() http.HandlerFunc { return handleImageDelete(func(ctx context.Context) error { + api.albumImgOps.Lock() + defer api.albumImgOps.Unlock() albumID := chi.URLParamFromCtx(ctx, "id") al, err := api.ds.Album(ctx).Get(albumID) if err != nil { diff --git a/server/nativeapi/native_api.go b/server/nativeapi/native_api.go index d6c356b77..671910a9b 100644 --- a/server/nativeapi/native_api.go +++ b/server/nativeapi/native_api.go @@ -6,6 +6,7 @@ import ( "html" "net/http" "strconv" + "sync" "time" "github.com/deluan/rest" @@ -45,6 +46,9 @@ type Router struct { maintenance core.Maintenance pluginManager PluginManager imgUpload core.ImageUploadService + // Serializes album image check-and-act sequences: shared-file ref-counting is + // check-then-act, and concurrent requests could orphan or clobber a shared file. + albumImgOps sync.Mutex } func New(ds model.DataStore, share core.Share, playlists playlistsvc.Playlists, insights metrics.Insights, libraryService core.Library, userService core.User, maintenance core.Maintenance, pluginManager PluginManager, imgUpload core.ImageUploadService) *Router {