2020-04-06 09:41:42 +02:00
|
|
|
package workers
|
2020-04-03 18:08:49 +02:00
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
2020-04-04 13:53:52 +02:00
|
|
|
"path/filepath"
|
2020-07-13 10:41:45 +02:00
|
|
|
"runtime/debug"
|
2020-04-03 18:08:49 +02:00
|
|
|
|
|
|
|
"github.com/photoprism/photoprism/internal/config"
|
|
|
|
"github.com/photoprism/photoprism/internal/entity"
|
|
|
|
"github.com/photoprism/photoprism/internal/form"
|
|
|
|
"github.com/photoprism/photoprism/internal/mutex"
|
2020-06-07 10:09:35 +02:00
|
|
|
"github.com/photoprism/photoprism/internal/photoprism"
|
2020-04-03 18:08:49 +02:00
|
|
|
"github.com/photoprism/photoprism/internal/query"
|
2020-04-05 22:26:53 +02:00
|
|
|
"github.com/photoprism/photoprism/internal/remote"
|
|
|
|
"github.com/photoprism/photoprism/internal/remote/webdav"
|
2021-09-18 15:32:39 +02:00
|
|
|
"github.com/photoprism/photoprism/internal/search"
|
2020-04-03 18:08:49 +02:00
|
|
|
"github.com/photoprism/photoprism/internal/thumb"
|
2022-10-03 22:59:29 +02:00
|
|
|
"github.com/photoprism/photoprism/pkg/fs"
|
2020-04-03 18:08:49 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// Share represents a share worker.
|
|
|
|
type Share struct {
|
|
|
|
conf *config.Config
|
|
|
|
}
|
|
|
|
|
2020-05-26 15:15:14 +02:00
|
|
|
// NewShare returns a new share worker.
|
2020-04-03 18:08:49 +02:00
|
|
|
func NewShare(conf *config.Config) *Share {
|
|
|
|
return &Share{conf: conf}
|
|
|
|
}
|
|
|
|
|
2020-05-26 15:15:14 +02:00
|
|
|
// logError logs an error message if err is not nil.
|
2022-10-03 22:59:29 +02:00
|
|
|
func (w *Share) logError(err error) {
|
2020-05-26 15:15:14 +02:00
|
|
|
if err != nil {
|
2020-12-05 00:13:44 +01:00
|
|
|
log.Errorf("share: %s", err.Error())
|
2020-05-26 15:15:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:08:49 +02:00
|
|
|
// Start starts the share worker.
|
2022-10-03 22:59:29 +02:00
|
|
|
func (w *Share) Start() (err error) {
|
2020-07-13 10:41:45 +02:00
|
|
|
defer func() {
|
|
|
|
if r := recover(); r != nil {
|
2020-12-05 00:13:44 +01:00
|
|
|
err = fmt.Errorf("share: %s (panic)\nstack: %s", r, debug.Stack())
|
2020-07-13 10:41:45 +02:00
|
|
|
log.Error(err)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2020-05-26 15:15:14 +02:00
|
|
|
if err := mutex.ShareWorker.Start(); err != nil {
|
2020-04-03 18:08:49 +02:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-05-26 15:15:14 +02:00
|
|
|
defer mutex.ShareWorker.Stop()
|
2020-04-03 18:08:49 +02:00
|
|
|
|
2022-10-15 21:54:11 +02:00
|
|
|
f := form.SearchServices{
|
2020-04-03 18:08:49 +02:00
|
|
|
Share: true,
|
|
|
|
}
|
|
|
|
|
2020-04-06 16:34:29 +02:00
|
|
|
// Find accounts for which sharing is enabled
|
2021-09-18 15:32:39 +02:00
|
|
|
accounts, err := search.Accounts(f)
|
2020-04-03 18:08:49 +02:00
|
|
|
|
2020-04-06 16:34:29 +02:00
|
|
|
// Upload newly shared files
|
2020-04-03 18:08:49 +02:00
|
|
|
for _, a := range accounts {
|
2020-05-26 15:15:14 +02:00
|
|
|
if mutex.ShareWorker.Canceled() {
|
2020-04-04 17:19:34 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-05 22:26:53 +02:00
|
|
|
if a.AccType != remote.ServiceWebDAV {
|
2020-04-03 18:08:49 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-05-08 15:41:01 +02:00
|
|
|
files, err := query.FileShares(a.ID, entity.FileShareNew)
|
2020-04-03 18:08:49 +02:00
|
|
|
|
|
|
|
if err != nil {
|
2022-10-03 22:59:29 +02:00
|
|
|
w.logError(err)
|
2020-04-03 18:08:49 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(files) == 0 {
|
2020-04-06 16:34:29 +02:00
|
|
|
// No files to upload for this account
|
2020-04-03 18:08:49 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-05-16 23:59:28 +02:00
|
|
|
size := thumb.Size{}
|
|
|
|
|
|
|
|
if a.ShareSize != "" {
|
|
|
|
if s, ok := thumb.Sizes[thumb.Name(a.ShareSize)]; ok {
|
|
|
|
size = s
|
|
|
|
} else {
|
|
|
|
size = thumb.Sizes[thumb.Fit2048]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-27 21:37:11 +02:00
|
|
|
client := webdav.New(a.AccURL, a.AccUser, a.AccPass, webdav.Timeout(a.AccTimeout))
|
2020-04-04 13:53:52 +02:00
|
|
|
existingDirs := make(map[string]string)
|
2020-04-03 18:08:49 +02:00
|
|
|
|
|
|
|
for _, file := range files {
|
2020-05-26 15:15:14 +02:00
|
|
|
if mutex.ShareWorker.Canceled() {
|
2020-04-04 17:19:34 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-04 13:53:52 +02:00
|
|
|
dir := filepath.Dir(file.RemoteName)
|
|
|
|
|
2020-04-07 12:51:01 +02:00
|
|
|
if _, ok := existingDirs[dir]; !ok {
|
2020-04-04 13:53:52 +02:00
|
|
|
if err := client.CreateDir(dir); err != nil {
|
2020-12-05 00:13:44 +01:00
|
|
|
log.Errorf("share: failed creating folder %s", dir)
|
2020-04-04 13:53:52 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-07 10:09:35 +02:00
|
|
|
srcFileName := photoprism.FileName(file.File.FileRoot, file.File.FileName)
|
2020-04-03 18:08:49 +02:00
|
|
|
|
2022-05-16 23:59:28 +02:00
|
|
|
if fs.ImageJPEG.Equal(file.File.FileType) && size.Width > 0 && size.Height > 0 {
|
2022-10-03 22:59:29 +02:00
|
|
|
srcFileName, err = thumb.FromFile(srcFileName, file.File.FileHash, w.conf.ThumbCachePath(), size.Width, size.Height, file.File.FileOrientation, size.Options...)
|
2020-04-03 18:08:49 +02:00
|
|
|
|
|
|
|
if err != nil {
|
2022-10-03 22:59:29 +02:00
|
|
|
w.logError(err)
|
2020-04-03 18:08:49 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := client.Upload(srcFileName, file.RemoteName); err != nil {
|
2022-10-03 22:59:29 +02:00
|
|
|
w.logError(err)
|
2020-04-03 18:08:49 +02:00
|
|
|
file.Errors++
|
|
|
|
file.Error = err.Error()
|
|
|
|
} else {
|
2020-12-05 00:13:44 +01:00
|
|
|
log.Infof("share: uploaded %s to %s", file.RemoteName, a.AccName)
|
2020-04-03 18:08:49 +02:00
|
|
|
file.Errors = 0
|
|
|
|
file.Error = ""
|
|
|
|
file.Status = entity.FileShareShared
|
|
|
|
}
|
|
|
|
|
2022-10-15 21:54:11 +02:00
|
|
|
// RunFailed too often?
|
2022-03-27 21:37:11 +02:00
|
|
|
if a.RetryLimit > 0 && file.Errors > a.RetryLimit {
|
2020-04-03 18:08:49 +02:00
|
|
|
file.Status = entity.FileShareError
|
|
|
|
}
|
|
|
|
|
2020-05-26 15:15:14 +02:00
|
|
|
if mutex.ShareWorker.Canceled() {
|
2020-04-04 17:19:34 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2022-10-03 22:59:29 +02:00
|
|
|
w.logError(entity.Db().Save(&file).Error)
|
2020-04-03 18:08:49 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-06 16:34:29 +02:00
|
|
|
// Remove previously shared files if expired
|
2020-04-04 13:53:52 +02:00
|
|
|
for _, a := range accounts {
|
2020-05-26 15:15:14 +02:00
|
|
|
if mutex.ShareWorker.Canceled() {
|
2020-04-04 17:19:34 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-05 22:26:53 +02:00
|
|
|
if a.AccType != remote.ServiceWebDAV {
|
2020-04-04 13:53:52 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2020-05-08 15:41:01 +02:00
|
|
|
files, err := query.ExpiredFileShares(a)
|
2020-04-04 13:53:52 +02:00
|
|
|
|
|
|
|
if err != nil {
|
2022-10-03 22:59:29 +02:00
|
|
|
w.logError(err)
|
2020-04-04 13:53:52 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(files) == 0 {
|
2020-04-06 16:34:29 +02:00
|
|
|
// No files to remove for this account
|
2020-04-04 13:53:52 +02:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2022-03-27 21:37:11 +02:00
|
|
|
client := webdav.New(a.AccURL, a.AccUser, a.AccPass, webdav.Timeout(a.AccTimeout))
|
2020-04-04 13:53:52 +02:00
|
|
|
|
|
|
|
for _, file := range files {
|
2020-05-26 15:15:14 +02:00
|
|
|
if mutex.ShareWorker.Canceled() {
|
2020-04-04 17:19:34 +02:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2020-04-04 13:53:52 +02:00
|
|
|
if err := client.Delete(file.RemoteName); err != nil {
|
|
|
|
file.Errors++
|
|
|
|
file.Error = err.Error()
|
|
|
|
} else {
|
2020-12-05 00:13:44 +01:00
|
|
|
log.Infof("share: removed %s from %s", file.RemoteName, a.AccName)
|
2020-04-04 13:53:52 +02:00
|
|
|
file.Errors = 0
|
|
|
|
file.Error = ""
|
|
|
|
file.Status = entity.FileShareRemoved
|
|
|
|
}
|
|
|
|
|
2020-05-08 15:41:01 +02:00
|
|
|
if err := entity.Db().Save(&file).Error; err != nil {
|
2022-10-03 22:59:29 +02:00
|
|
|
w.logError(err)
|
2020-04-04 13:53:52 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-04-03 18:08:49 +02:00
|
|
|
return err
|
|
|
|
}
|