WebDAV: Skip download sync if storage is full or quota is exceeded #4266

Signed-off-by: Michael Mayer <michael@photoprism.app>
This commit is contained in:
Michael Mayer
2025-03-11 23:45:41 +01:00
parent c3d22c157c
commit eb9371d089
7 changed files with 19 additions and 14 deletions

View File

@@ -56,7 +56,7 @@ func StartImport(router *gin.RouterGroup) {
} }
// Abort if there is not enough free storage to import new files. // Abort if there is not enough free storage to import new files.
if conf.FilesQuotaReached() { if conf.FilesQuotaExceeded() {
event.AuditErr([]string{ClientIP(c), "session %s", "import files", "insufficient storage"}, s.RefID) event.AuditErr([]string{ClientIP(c), "session %s", "import files", "insufficient storage"}, s.RefID)
Abort(c, http.StatusInsufficientStorage, i18n.ErrInsufficientStorage) Abort(c, http.StatusInsufficientStorage, i18n.ErrInsufficientStorage)
return return

View File

@@ -53,7 +53,7 @@ func UploadUserFiles(router *gin.RouterGroup) {
} }
// Abort if there is not enough free storage to upload new files. // Abort if there is not enough free storage to upload new files.
if conf.FilesQuotaReached() { if conf.FilesQuotaExceeded() {
event.AuditErr([]string{ClientIP(c), "session %s", "upload files", "insufficient storage"}, s.RefID) event.AuditErr([]string{ClientIP(c), "session %s", "upload files", "insufficient storage"}, s.RefID)
Abort(c, http.StatusInsufficientStorage, i18n.ErrInsufficientStorage) Abort(c, http.StatusInsufficientStorage, i18n.ErrInsufficientStorage)
return return

View File

@@ -118,8 +118,8 @@ func (c *Config) FilesQuotaBytes() uint64 {
return c.options.FilesQuota * fs.GB return c.options.FilesQuota * fs.GB
} }
// FilesQuotaReached checks if the filesystem usage has been reached or exceeded. // FilesQuotaExceeded checks if the filesystem usage has been reached or exceeded.
func (c *Config) FilesQuotaReached() bool { func (c *Config) FilesQuotaExceeded() bool {
return c.Usage().FilesUsedPct >= 100 return c.Usage().FilesUsedPct >= 100
} }

View File

@@ -56,19 +56,19 @@ func TestConfig_Quota(t *testing.T) {
c.options.UsersQuota = 0 c.options.UsersQuota = 0
} }
func TestConfig_FilesQuotaReached(t *testing.T) { func TestConfig_FilesQuotaExceeded(t *testing.T) {
c := TestConfig() c := TestConfig()
FlushUsageCache() FlushUsageCache()
assert.False(t, c.FilesQuotaReached()) assert.False(t, c.FilesQuotaExceeded())
c.options.FilesQuota = uint64(1) c.options.FilesQuota = uint64(1)
FlushUsageCache() FlushUsageCache()
assert.True(t, c.FilesQuotaReached()) assert.True(t, c.FilesQuotaExceeded())
c.options.FilesQuota = uint64(5) c.options.FilesQuota = uint64(5)
FlushUsageCache() FlushUsageCache()
assert.False(t, c.FilesQuotaReached()) assert.False(t, c.FilesQuotaExceeded())
c.options.FilesQuota = uint64(0) c.options.FilesQuota = uint64(0)
} }

View File

@@ -95,7 +95,7 @@ func WebDAV(dir string, router *gin.RouterGroup, conf *config.Config) {
// is not enough free storage to upload new files. // is not enough free storage to upload new files.
switch c.Request.Method { switch c.Request.Method {
case MethodPut, MethodPost, MethodPatch, MethodCopy: case MethodPut, MethodPost, MethodPatch, MethodCopy:
if conf.FilesQuotaReached() { if conf.FilesQuotaExceeded() {
c.AbortWithStatus(http.StatusInsufficientStorage) c.AbortWithStatus(http.StatusInsufficientStorage)
return return
} }

View File

@@ -106,9 +106,9 @@ func (w *Sync) Start() (err error) {
} }
} }
case entity.SyncStatusDownload: case entity.SyncStatusDownload:
if complete, err := w.download(a); err != nil { if complete, downloadErr := w.download(a); downloadErr != nil {
accErrors++ accErrors++
accError = err.Error() accError = downloadErr.Error()
syncStatus = entity.SyncStatusRefresh syncStatus = entity.SyncStatusRefresh
} else if complete { } else if complete {
if a.SyncUpload { if a.SyncUpload {

View File

@@ -105,6 +105,11 @@ func (w *Sync) download(a entity.Service) (complete bool, err error) {
done := make(map[string]bool) done := make(map[string]bool)
for _, files := range relatedFiles { for _, files := range relatedFiles {
if w.conf.FilesQuotaExceeded() {
log.Warnf("sync: skipped downloading files from %s due to insufficient storage", clean.Log(a.AccName))
return false, nil
}
for i, file := range files { for i, file := range files {
if mutex.SyncWorker.Canceled() { if mutex.SyncWorker.Canceled() {
return false, nil return false, nil
@@ -112,14 +117,14 @@ func (w *Sync) download(a entity.Service) (complete bool, err error) {
// Failed too often? // Failed too often?
if a.RetryLimit > 0 && file.Errors > a.RetryLimit { if a.RetryLimit > 0 && file.Errors > a.RetryLimit {
log.Debugf("sync: downloading %s failed more than %d times", file.RemoteName, a.RetryLimit) log.Debugf("sync: downloading %s from %s failed more than %d times", file.RemoteName, clean.Log(a.AccName), a.RetryLimit)
continue continue
} }
localName := baseDir + file.RemoteName localName := baseDir + file.RemoteName
if _, err = os.Stat(localName); err == nil { if _, err = os.Stat(localName); err == nil {
log.Warnf("sync: download skipped, %s already exists", localName) log.Warnf("sync: skipped download of %s from %s because local file %s already exists", file.RemoteName, clean.Log(a.AccName), localName)
file.Status = entity.FileSyncExists file.Status = entity.FileSyncExists
file.Error = "" file.Error = ""
file.Errors = 0 file.Errors = 0
@@ -128,7 +133,7 @@ func (w *Sync) download(a entity.Service) (complete bool, err error) {
file.Errors++ file.Errors++
file.Error = err.Error() file.Error = err.Error()
} else { } else {
log.Infof("sync: downloaded %s from %s", file.RemoteName, a.AccName) log.Infof("sync: downloaded %s from %s", file.RemoteName, clean.Log(a.AccName))
file.Status = entity.FileSyncDownloaded file.Status = entity.FileSyncDownloaded
file.Error = "" file.Error = ""
file.Errors = 0 file.Errors = 0