Merge remote-tracking branch 'origin/develop' into develop
This commit is contained in:
commit
73e6988aea
11 changed files with 138 additions and 40 deletions
|
@ -6,7 +6,7 @@ import (
|
|||
"github.com/gin-gonic/gin"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/form"
|
||||
"github.com/photoprism/photoprism/internal/session"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/txt"
|
||||
)
|
||||
|
||||
|
@ -27,7 +27,7 @@ func CreateSession(router *gin.RouterGroup, conf *config.Config) {
|
|||
|
||||
user := gin.H{"ID": 1, "FirstName": "Admin", "LastName": "", "Role": "admin", "Email": "photoprism@localhost"}
|
||||
|
||||
token := session.Create(user)
|
||||
token := service.Session().Create(user)
|
||||
|
||||
c.Header("X-Session-Token", token)
|
||||
|
||||
|
@ -42,7 +42,7 @@ func DeleteSession(router *gin.RouterGroup, conf *config.Config) {
|
|||
router.DELETE("/session/:token", func(c *gin.Context) {
|
||||
token := c.Param("token")
|
||||
|
||||
session.Delete(token)
|
||||
service.Session().Delete(token)
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok", "token": token})
|
||||
})
|
||||
|
@ -59,5 +59,5 @@ func Unauthorized(c *gin.Context, conf *config.Config) bool {
|
|||
token := c.GetHeader("X-Session-Token")
|
||||
|
||||
// Check if session token is valid
|
||||
return !session.Exists(token)
|
||||
return !service.Session().Exists(token)
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"github.com/gorilla/websocket"
|
||||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
"github.com/photoprism/photoprism/internal/session"
|
||||
"github.com/photoprism/photoprism/internal/service"
|
||||
"github.com/photoprism/photoprism/pkg/rnd"
|
||||
)
|
||||
|
||||
|
@ -54,7 +54,7 @@ func wsReader(ws *websocket.Conn, writeMutex *sync.Mutex, connId string, conf *c
|
|||
if err := json.Unmarshal(m, &info); err != nil {
|
||||
log.Error(err)
|
||||
} else {
|
||||
if session.Exists(info.SessionToken) {
|
||||
if service.Session().Exists(info.SessionToken) {
|
||||
log.Debug("websocket: authenticated")
|
||||
|
||||
wsAuth.mutex.Lock()
|
||||
|
|
|
@ -49,8 +49,9 @@ func NewAlbum(albumName string) *Album {
|
|||
albumSlug := slug.Make(albumName)
|
||||
|
||||
result := &Album{
|
||||
AlbumSlug: albumSlug,
|
||||
AlbumName: albumName,
|
||||
AlbumSlug: albumSlug,
|
||||
AlbumName: albumName,
|
||||
AlbumOrder: SortOrderOldest,
|
||||
}
|
||||
|
||||
return result
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package entity
|
||||
|
||||
const (
|
||||
// data sources
|
||||
SrcAuto = ""
|
||||
SrcManual = "manual"
|
||||
SrcLocation = "location"
|
||||
|
@ -9,4 +10,11 @@ const (
|
|||
SrcXmp = "xmp"
|
||||
SrcYml = "yml"
|
||||
SrcJson = "json"
|
||||
|
||||
// sort orders
|
||||
SortOrderRelevance = "relevance"
|
||||
SortOrderNewest = "newest"
|
||||
SortOrderOldest = "oldest"
|
||||
SortOrderImported = "imported"
|
||||
SortOrderSimilar = "similar"
|
||||
)
|
|
@ -317,15 +317,15 @@ func (q *Query) Photos(f form.PhotoSearch) (results []PhotoResult, err error) {
|
|||
}
|
||||
|
||||
switch f.Order {
|
||||
case "relevance":
|
||||
case entity.SortOrderRelevance:
|
||||
s = s.Order("photo_story DESC, photo_favorite DESC, taken_at DESC")
|
||||
case "newest":
|
||||
case entity.SortOrderNewest:
|
||||
s = s.Order("taken_at DESC, photos.photo_uuid")
|
||||
case "oldest":
|
||||
case entity.SortOrderOldest:
|
||||
s = s.Order("taken_at, photos.photo_uuid")
|
||||
case "imported":
|
||||
case entity.SortOrderImported:
|
||||
s = s.Order("photos.id DESC")
|
||||
case "similar":
|
||||
case entity.SortOrderSimilar:
|
||||
s = s.Order("files.file_main_color, photos.location_id, files.file_diff, taken_at DESC")
|
||||
default:
|
||||
s = s.Order("taken_at DESC, photos.photo_uuid")
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"github.com/photoprism/photoprism/internal/config"
|
||||
"github.com/photoprism/photoprism/internal/nsfw"
|
||||
"github.com/photoprism/photoprism/internal/photoprism"
|
||||
"github.com/photoprism/photoprism/internal/session"
|
||||
)
|
||||
|
||||
var conf *config.Config
|
||||
|
@ -16,6 +17,7 @@ var services struct {
|
|||
Convert *photoprism.Convert
|
||||
Resample *photoprism.Resample
|
||||
Classify *classify.TensorFlow
|
||||
Session *session.Session
|
||||
}
|
||||
|
||||
func SetConfig(c *config.Config) {
|
||||
|
|
21
internal/service/session.go
Normal file
21
internal/service/session.go
Normal file
|
@ -0,0 +1,21 @@
|
|||
package service
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/photoprism/photoprism/internal/session"
|
||||
)
|
||||
|
||||
var onceSession sync.Once
|
||||
|
||||
func initSession() {
|
||||
// keep sessions for 7 days by default
|
||||
services.Session = session.New(168*time.Hour, Config().CachePath())
|
||||
}
|
||||
|
||||
func Session() *session.Session {
|
||||
onceSession.Do(initSession)
|
||||
|
||||
return services.Session
|
||||
}
|
|
@ -8,7 +8,46 @@ https://github.com/photoprism/photoprism/wiki
|
|||
package session
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"time"
|
||||
|
||||
gc "github.com/patrickmn/go-cache"
|
||||
"github.com/photoprism/photoprism/internal/event"
|
||||
)
|
||||
|
||||
var log = event.Log
|
||||
|
||||
// Session represents a session store.
|
||||
type Session struct {
|
||||
cacheFile string
|
||||
cache *gc.Cache
|
||||
}
|
||||
|
||||
// New returns a new session store with an optional cachePath.
|
||||
func New(expiration time.Duration, cachePath string) *Session {
|
||||
s := &Session{}
|
||||
|
||||
cleanupInterval := 15 * time.Minute
|
||||
|
||||
if cachePath != "" {
|
||||
var items map[string]gc.Item
|
||||
|
||||
s.cacheFile = path.Join(cachePath, "sessions.json")
|
||||
|
||||
if cached, err := ioutil.ReadFile(s.cacheFile); err != nil {
|
||||
log.Infof("session: %s", err)
|
||||
} else if err := json.Unmarshal(cached, &items); err != nil {
|
||||
log.Errorf("session: %s", err)
|
||||
} else {
|
||||
s.cache = gc.NewFrom(expiration, cleanupInterval, items)
|
||||
}
|
||||
}
|
||||
|
||||
if s.cache == nil {
|
||||
s.cache = gc.New(expiration, cleanupInterval)
|
||||
}
|
||||
|
||||
return s
|
||||
}
|
||||
|
|
|
@ -1,31 +1,51 @@
|
|||
package session
|
||||
|
||||
import (
|
||||
"time"
|
||||
"encoding/json"
|
||||
"io/ioutil"
|
||||
|
||||
gc "github.com/patrickmn/go-cache"
|
||||
)
|
||||
|
||||
var cache = gc.New(72*time.Hour, 30*time.Minute)
|
||||
|
||||
func Create(data interface{}) string {
|
||||
func (s *Session) Create(data interface{}) string {
|
||||
token := Token()
|
||||
cache.Set(token, data, gc.DefaultExpiration)
|
||||
s.cache.Set(token, data, gc.DefaultExpiration)
|
||||
log.Debugf("session: created")
|
||||
|
||||
if err := s.Save(); err != nil {
|
||||
log.Errorf("session: %s", err)
|
||||
}
|
||||
|
||||
return token
|
||||
}
|
||||
|
||||
func Delete(token string) {
|
||||
cache.Delete(token)
|
||||
func (s *Session) Delete(token string) {
|
||||
s.cache.Delete(token)
|
||||
log.Debugf("session: deleted")
|
||||
|
||||
if err := s.Save(); err != nil {
|
||||
log.Errorf("session: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Get(token string) (data interface{}, exists bool) {
|
||||
return cache.Get(token)
|
||||
func (s *Session) Get(token string) (data interface{}, exists bool) {
|
||||
return s.cache.Get(token)
|
||||
}
|
||||
|
||||
func Exists(token string) bool {
|
||||
_, found := cache.Get(token)
|
||||
func (s *Session) Exists(token string) bool {
|
||||
_, found := s.cache.Get(token)
|
||||
|
||||
return found
|
||||
}
|
||||
|
||||
func (s *Session) Save() error {
|
||||
if s.cacheFile == "" {
|
||||
return nil
|
||||
} else if serialized, err := json.MarshalIndent(s.cache.Items(), "", " "); err != nil {
|
||||
return err
|
||||
} else if err = ioutil.WriteFile(s.cacheFile, serialized, 0600); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -2,44 +2,49 @@ package session
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
token := Create(23)
|
||||
func TestSession_Create(t *testing.T) {
|
||||
s := New(time.Hour, "testdata")
|
||||
token := s.Create(23)
|
||||
t.Logf("token: %s", token)
|
||||
assert.Equal(t, 48, len(token))
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
Delete("abc")
|
||||
func TestSession_Delete(t *testing.T) {
|
||||
s := New(time.Hour, "testdata")
|
||||
s.Delete("abc")
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
token := Create(42)
|
||||
func TestSession_Get(t *testing.T) {
|
||||
s := New(time.Hour, "testdata")
|
||||
token := s.Create(42)
|
||||
t.Logf("token: %s", token)
|
||||
assert.Equal(t, 48, len(token))
|
||||
|
||||
data, exists := Get(token)
|
||||
data, exists := s.Get(token)
|
||||
|
||||
assert.Equal(t, 42, data)
|
||||
assert.True(t, exists)
|
||||
|
||||
Delete(token)
|
||||
s.Delete(token)
|
||||
|
||||
data, exists = Get(token)
|
||||
data, exists = s.Get(token)
|
||||
|
||||
assert.Nil(t, data)
|
||||
assert.False(t, Exists(token))
|
||||
assert.False(t, s.Exists(token))
|
||||
}
|
||||
|
||||
func TestExists(t *testing.T) {
|
||||
assert.False(t, Exists("xyz"))
|
||||
token := Create(23)
|
||||
func TestSession_Exists(t *testing.T) {
|
||||
s := New(time.Hour, "testdata")
|
||||
assert.False(t, s.Exists("xyz"))
|
||||
token := s.Create(23)
|
||||
t.Logf("token: %s", token)
|
||||
assert.Equal(t, 48, len(token))
|
||||
assert.True(t, Exists(token))
|
||||
Delete(token)
|
||||
assert.False(t, Exists(token))
|
||||
assert.True(t, s.Exists(token))
|
||||
s.Delete(token)
|
||||
assert.False(t, s.Exists(token))
|
||||
}
|
||||
|
|
2
internal/session/testdata/.gitignore
vendored
Normal file
2
internal/session/testdata/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
*
|
||||
!.gitignore
|
Loading…
Reference in a new issue