From 90dd094a21f2958d6baa16f2ddb55d2e572dab2d Mon Sep 17 00:00:00 2001 From: Michael Mayer Date: Sun, 2 Feb 2020 00:31:09 +0100 Subject: [PATCH] Sharing: Add accounts table #11 Signed-off-by: Michael Mayer --- internal/api/album.go | 33 ++++++++++++++++++++++++++----- internal/api/label.go | 12 +++++------ internal/api/photo_thumbnail.go | 6 ++++-- internal/config/db.go | 4 ++-- internal/entity/account.go | 32 ++++++++++++++++++++++++++++++ internal/entity/album.go | 12 +++++------ internal/entity/photo.go | 4 +++- internal/entity/share.go | 35 --------------------------------- internal/entity/share_test.go | 14 ------------- 9 files changed, 80 insertions(+), 72 deletions(-) create mode 100644 internal/entity/account.go delete mode 100644 internal/entity/share.go delete mode 100644 internal/entity/share_test.go diff --git a/internal/api/album.go b/internal/api/album.go index 3cd18d419..3203cec58 100644 --- a/internal/api/album.go +++ b/internal/api/album.go @@ -3,6 +3,7 @@ package api import ( "archive/zip" "fmt" + "io/ioutil" "net/http" "os" "path" @@ -427,21 +428,31 @@ func AlbumThumbnail(router *gin.RouterGroup, conf *config.Config) { router.GET("/albums/:uuid/thumbnail/:type", func(c *gin.Context) { typeName := c.Param("type") uuid := c.Param("uuid") + start := time.Now() thumbType, ok := thumb.Types[typeName] if !ok { - log.Errorf("thumbs: invalid type \"%s\"", typeName) + log.Errorf("album: invalid thumb type %s", typeName) c.Data(http.StatusBadRequest, "image/svg+xml", photoIconSvg) return } q := query.New(conf.OriginalsPath(), conf.Db()) + gc := conf.Cache() + cacheKey := fmt.Sprintf("album-thumbnail:%s:%s", uuid, typeName) + + if cacheData, ok := gc.Get(cacheKey); ok { + log.Debugf("album: %s cache hit [%s]", cacheKey, time.Since(start)) + c.Data(http.StatusOK, "image/jpeg", cacheData.([]byte)) + return + } + f, err := q.FindAlbumThumbByUUID(uuid) if err != nil { - log.Debugf("album has no photos yet, using generic thumb image: %s", uuid) + log.Debugf("album: no photos yet, using generic image for %s", uuid) c.Data(http.StatusOK, "image/svg+xml", albumIconSvg) return } @@ -449,7 +460,7 @@ func AlbumThumbnail(router *gin.RouterGroup, conf *config.Config) { fileName := path.Join(conf.OriginalsPath(), f.FileName) if !fs.FileExists(fileName) { - log.Errorf("could not find original for thumbnail: %s", fileName) + log.Errorf("album: could not find original for %s", fileName) c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg) // Set missing flag so that the file doesn't show up in search results anymore @@ -472,9 +483,21 @@ func AlbumThumbnail(router *gin.RouterGroup, conf *config.Config) { c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=%s", f.DownloadFileName())) } - c.File(thumbnail) + thumbData, err := ioutil.ReadFile(thumbnail) + + if err != nil { + log.Errorf("album: %s", err) + c.Data(http.StatusOK, "image/svg+xml", albumIconSvg) + return + } + + gc.Set(cacheKey, thumbData, time.Hour) + + log.Debugf("album: %s cached [%s]", cacheKey, time.Since(start)) + + c.Data(http.StatusOK, "image/jpeg", thumbData) } else { - log.Errorf("could not create thumbnail: %s", err) + log.Errorf("album: %s", err) c.Data(http.StatusBadRequest, "image/svg+xml", photoIconSvg) } }) diff --git a/internal/api/label.go b/internal/api/label.go index cc1612f5d..563ae5b1f 100644 --- a/internal/api/label.go +++ b/internal/api/label.go @@ -132,7 +132,7 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) { thumbType, ok := thumb.Types[typeName] if !ok { - log.Errorf("thumbs: invalid type \"%s\"", typeName) + log.Errorf("label: invalid thumb type \"%s\"", typeName) c.Data(http.StatusOK, "image/svg+xml", labelIconSvg) return } @@ -143,7 +143,7 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) { cacheKey := fmt.Sprintf("label-thumbnail:%s:%s", labelUUID, typeName) if cacheData, ok := gc.Get(cacheKey); ok { - log.Debugf("%s cache hit [%s]", cacheKey, time.Since(start)) + log.Debugf("label: %s cache hit [%s]", cacheKey, time.Since(start)) c.Data(http.StatusOK, "image/jpeg", cacheData.([]byte)) return } @@ -159,7 +159,7 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) { fileName := path.Join(conf.OriginalsPath(), f.FileName) if !fs.FileExists(fileName) { - log.Errorf("could not find original for thumbnail: %s", fileName) + log.Errorf("label: could not find original for %s", fileName) c.Data(http.StatusOK, "image/svg+xml", labelIconSvg) // Set missing flag so that the file doesn't show up in search results anymore @@ -181,18 +181,18 @@ func LabelThumbnail(router *gin.RouterGroup, conf *config.Config) { thumbData, err := ioutil.ReadFile(thumbnail) if err != nil { - log.Errorf("could not read thumbnail: %s", err) + log.Errorf("label: %s", err) c.Data(http.StatusOK, "image/svg+xml", labelIconSvg) return } gc.Set(cacheKey, thumbData, time.Hour*4) - log.Debugf("%s cached [%s]", cacheKey, time.Since(start)) + log.Debugf("label: %s cached [%s]", cacheKey, time.Since(start)) c.Data(http.StatusOK, "image/jpeg", thumbData) } else { - log.Errorf("could not create thumbnail: %s", err) + log.Errorf("label: %s", err) c.Data(http.StatusOK, "image/svg+xml", labelIconSvg) return diff --git a/internal/api/photo_thumbnail.go b/internal/api/photo_thumbnail.go index 7bd41c48a..1ffe15925 100644 --- a/internal/api/photo_thumbnail.go +++ b/internal/api/photo_thumbnail.go @@ -25,7 +25,7 @@ func GetThumbnail(router *gin.RouterGroup, conf *config.Config) { thumbType, ok := thumb.Types[typeName] if !ok { - log.Errorf("thumbs: invalid type \"%s\"", typeName) + log.Errorf("photo: invalid thumb type \"%s\"", typeName) c.Data(http.StatusBadRequest, "image/svg+xml", photoIconSvg) return } @@ -47,7 +47,7 @@ func GetThumbnail(router *gin.RouterGroup, conf *config.Config) { fileName := path.Join(conf.OriginalsPath(), f.FileName) if !fs.FileExists(fileName) { - log.Errorf("could not find original for thumbnail: %s", fileName) + log.Errorf("photo: could not find original for %s", fileName) c.Data(http.StatusNotFound, "image/svg+xml", photoIconSvg) // Set missing flag so that the file doesn't show up in search results anymore @@ -72,6 +72,8 @@ func GetThumbnail(router *gin.RouterGroup, conf *config.Config) { c.File(thumbnail) } else { + log.Errorf("photo: %s", err) + f.FileError = err.Error() db.Save(&f) diff --git a/internal/config/db.go b/internal/config/db.go index d6d27a959..fe7b6ee69 100644 --- a/internal/config/db.go +++ b/internal/config/db.go @@ -60,6 +60,7 @@ func (c *Config) MigrateDb() { db := c.Db() db.AutoMigrate( + &entity.Account{}, &entity.File{}, &entity.Photo{}, &entity.Event{}, @@ -68,7 +69,6 @@ func (c *Config) MigrateDb() { &entity.Camera{}, &entity.Lens{}, &entity.Country{}, - &entity.Share{}, &entity.Album{}, &entity.PhotoAlbum{}, @@ -154,6 +154,7 @@ func (c *Config) DropTables() { db := c.Db() db.DropTableIfExists( + &entity.Account{}, &entity.File{}, &entity.Photo{}, &entity.Event{}, @@ -162,7 +163,6 @@ func (c *Config) DropTables() { &entity.Camera{}, &entity.Lens{}, &entity.Country{}, - &entity.Share{}, &entity.Album{}, &entity.PhotoAlbum{}, diff --git a/internal/entity/account.go b/internal/entity/account.go new file mode 100644 index 000000000..95cf6eb8f --- /dev/null +++ b/internal/entity/account.go @@ -0,0 +1,32 @@ +package entity + +import ( + "database/sql" + "time" +) + +// Account represents a remote service account for uploading, downloading or syncing media files. +type Account struct { + ID uint `gorm:"primary_key"` + Name string `gorm:"type:varchar(128);"` + URL string `gorm:"type:varbinary(512);"` + Protocol string `gorm:"type:varbinary(256);"` + ApiKey string `gorm:"type:varbinary(256);"` + Username string `gorm:"type:varbinary(256);"` + Password string `gorm:"type:varbinary(256);"` + LastError string `gorm:"type:varbinary(256);"` + IgnoreErrors bool + PushSize string `gorm:"type:varbinary(16);"` + PushExif bool + PushDelete bool + PushSidecar bool + SyncPush bool + SyncPull bool + SyncPaused int + SyncInterval int + SyncRetry int + SyncedAt sql.NullTime + CreatedAt time.Time + UpdatedAt time.Time + DeletedAt *time.Time `sql:"index"` +} diff --git a/internal/entity/album.go b/internal/entity/album.go index 9ee56f34e..9f237eac5 100644 --- a/internal/entity/album.go +++ b/internal/entity/album.go @@ -1,6 +1,7 @@ package entity import ( + "database/sql" "strings" "time" @@ -18,14 +19,11 @@ type Album struct { AlbumName string `gorm:"type:varchar(128);"` AlbumDescription string `gorm:"type:text;"` AlbumNotes string `gorm:"type:text;"` - AlbumViews uint AlbumFavorite bool - AlbumPublic bool - AlbumLat float64 - AlbumLng float64 - AlbumRadius float64 - AlbumOrder string `gorm:"type:varchar(16);"` - AlbumTemplate string `gorm:"type:varchar(128);"` + AlbumOrder string `gorm:"type:varbinary(32);"` + ShareTemplate string `gorm:"type:varbinary(256);"` + SharePassword string `gorm:"type:varbinary(256);"` + ShareExpires sql.NullTime CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time `sql:"index"` diff --git a/internal/entity/photo.go b/internal/entity/photo.go index 7be81aad4..38743efa4 100644 --- a/internal/entity/photo.go +++ b/internal/entity/photo.go @@ -9,7 +9,7 @@ import ( "github.com/photoprism/photoprism/pkg/txt" ) -// A photo can have multiple images and sidecar files +// Photo represents a photo that can have multiple image or sidecar files. type Photo struct { ID uint `gorm:"primary_key"` TakenAt time.Time `gorm:"type:datetime;index:idx_photos_taken_uuid;" json:"TakenAt"` @@ -36,6 +36,7 @@ type Photo struct { LensID uint `gorm:"index:idx_photos_camera_lens;" json:"LensID"` LocationID string `gorm:"type:varbinary(16);index;" json:"LocationID"` PlaceID string `gorm:"type:varbinary(16);index;" json:"PlaceID"` + AccountID uint `json:"AccountID"` LocationEstimated bool `json:"LocationEstimated"` PhotoCountry string `gorm:"index:idx_photos_country_year_month;" json:"PhotoCountry"` PhotoYear int `gorm:"index:idx_photos_country_year_month;"` @@ -50,6 +51,7 @@ type Photo struct { Lens *Lens `json:"Lens"` Location *Location `json:"-"` Place *Place `json:"-"` + Account *Account `json:"-"` Files []File Labels []PhotoLabel Keywords []Keyword `json:"-"` diff --git a/internal/entity/share.go b/internal/entity/share.go deleted file mode 100644 index 35c32b939..000000000 --- a/internal/entity/share.go +++ /dev/null @@ -1,35 +0,0 @@ -package entity - -import ( - "time" - - "github.com/jinzhu/gorm" - "github.com/photoprism/photoprism/pkg/rnd" -) - -// Shared photos and/or albums -type Share struct { - UUID string `gorm:"type:varbinary(36);primary_key;auto_increment:false"` - ShareUUID string `gorm:"type:varbinary(36);index;"` - ShareViews uint - ShareUrl string `gorm:"type:varchar(64);"` - SharePassword string `gorm:"type:varbinary(200);"` - ShareExpires time.Time - Photo *Photo - Album *Album - CreatedAt time.Time - UpdatedAt time.Time - DeletedAt *time.Time `sql:"index"` -} - -func (Share) TableName() string { - return "shares" -} - -func (s *Share) BeforeCreate(scope *gorm.Scope) error { - if err := scope.SetColumn("ShareUUID", rnd.PPID('s')); err != nil { - return err - } - - return nil -} diff --git a/internal/entity/share_test.go b/internal/entity/share_test.go deleted file mode 100644 index 855e528e4..000000000 --- a/internal/entity/share_test.go +++ /dev/null @@ -1,14 +0,0 @@ -package entity - -import ( - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestShare_TableName(t *testing.T) { - share := &Share{} - tableName := share.TableName() - - assert.Equal(t, "shares", tableName) -}