Add missing fields in js model and rename fields for mode clarity. A link token can be valid for multiple shares. Signed-off-by: Michael Mayer <michael@liquidbytes.net>
This commit is contained in:
parent
e6bb13240d
commit
f2955fdefc
9 changed files with 80 additions and 78 deletions
|
@ -40,6 +40,8 @@ export default class Link extends Model {
|
|||
Slug: "",
|
||||
Token: "",
|
||||
Expires: 0,
|
||||
Views: 0,
|
||||
MaxViews: 0,
|
||||
Password: "",
|
||||
HasPassword: false,
|
||||
CanComment: false,
|
||||
|
|
|
@ -33,10 +33,10 @@ func UpdateLink(c *gin.Context) {
|
|||
|
||||
link.SetSlug(f.ShareSlug)
|
||||
link.MaxViews = f.MaxViews
|
||||
link.ShareExpires = f.ShareExpires
|
||||
link.LinkExpires = f.LinkExpires
|
||||
|
||||
if f.ShareToken != "" {
|
||||
link.ShareToken = strings.ToLower(f.ShareToken)
|
||||
if f.LinkToken != "" {
|
||||
link.LinkToken = strings.ToLower(f.LinkToken)
|
||||
}
|
||||
|
||||
if f.Password != "" {
|
||||
|
@ -97,7 +97,7 @@ func CreateLink(c *gin.Context) {
|
|||
|
||||
link.SetSlug(f.ShareSlug)
|
||||
link.MaxViews = f.MaxViews
|
||||
link.ShareExpires = f.ShareExpires
|
||||
link.LinkExpires = f.LinkExpires
|
||||
|
||||
if f.Password != "" {
|
||||
if err := link.SetPassword(f.Password); err != nil {
|
||||
|
|
|
@ -30,9 +30,9 @@ func TestLinkAlbum(t *testing.T) {
|
|||
|
||||
assert.NotEmpty(t, link.LinkUID)
|
||||
assert.NotEmpty(t, link.ShareUID)
|
||||
assert.NotEmpty(t, link.ShareToken)
|
||||
assert.NotEmpty(t, link.LinkToken)
|
||||
assert.Equal(t, true, link.CanEdit)
|
||||
assert.Equal(t, 0, link.ShareExpires)
|
||||
assert.Equal(t, 0, link.LinkExpires)
|
||||
assert.False(t, link.CanComment)
|
||||
assert.True(t, link.CanEdit)
|
||||
})
|
||||
|
@ -78,8 +78,8 @@ func TestLinkPhoto(t *testing.T) {
|
|||
|
||||
assert.NotEmpty(t, link.LinkUID)
|
||||
assert.NotEmpty(t, link.ShareUID)
|
||||
assert.NotEmpty(t, link.ShareToken)
|
||||
assert.Equal(t, 0, link.ShareExpires)
|
||||
assert.NotEmpty(t, link.LinkToken)
|
||||
assert.Equal(t, 0, link.LinkExpires)
|
||||
assert.False(t, link.CanComment)
|
||||
assert.True(t, link.CanEdit)
|
||||
})
|
||||
|
@ -119,8 +119,8 @@ func TestLinkLabel(t *testing.T) {
|
|||
|
||||
assert.NotEmpty(t, link.LinkUID)
|
||||
assert.NotEmpty(t, link.ShareUID)
|
||||
assert.NotEmpty(t, link.ShareToken)
|
||||
assert.Equal(t, 0, link.ShareExpires)
|
||||
assert.NotEmpty(t, link.LinkToken)
|
||||
assert.Equal(t, 0, link.LinkExpires)
|
||||
assert.False(t, link.CanComment)
|
||||
assert.True(t, link.CanEdit)
|
||||
})
|
||||
|
|
|
@ -15,9 +15,9 @@ func Shares(router *gin.RouterGroup) {
|
|||
router.GET("/:token", func(c *gin.Context) {
|
||||
conf := service.Config()
|
||||
|
||||
shareToken := c.Param("token")
|
||||
token := c.Param("token")
|
||||
|
||||
links := entity.FindValidLinks(shareToken, "")
|
||||
links := entity.FindValidLinks(token, "")
|
||||
|
||||
if len(links) == 0 {
|
||||
log.Warn("share: invalid token")
|
||||
|
@ -26,21 +26,21 @@ func Shares(router *gin.RouterGroup) {
|
|||
}
|
||||
|
||||
clientConfig := conf.GuestConfig()
|
||||
clientConfig.SiteUrl = fmt.Sprintf("%ss/%s", clientConfig.SiteUrl, shareToken)
|
||||
clientConfig.SiteUrl = fmt.Sprintf("%ss/%s", clientConfig.SiteUrl, token)
|
||||
|
||||
c.HTML(http.StatusOK, "share.tmpl", gin.H{"config": clientConfig})
|
||||
})
|
||||
|
||||
router.GET("/:token/:uid", func(c *gin.Context) {
|
||||
router.GET("/:token/:share", func(c *gin.Context) {
|
||||
conf := service.Config()
|
||||
|
||||
shareToken := c.Param("token")
|
||||
share := c.Param("uid")
|
||||
token := c.Param("token")
|
||||
share := c.Param("share")
|
||||
|
||||
links := entity.FindValidLinks(shareToken, share)
|
||||
links := entity.FindValidLinks(token, share)
|
||||
|
||||
if len(links) != 1 {
|
||||
log.Warn("share: invalid token or uid")
|
||||
log.Warn("share: invalid token or share")
|
||||
c.Redirect(http.StatusTemporaryRedirect, "/")
|
||||
return
|
||||
}
|
||||
|
@ -48,12 +48,12 @@ func Shares(router *gin.RouterGroup) {
|
|||
uid := links[0].ShareUID
|
||||
|
||||
if uid != share {
|
||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("/s/%s/%s", shareToken, uid))
|
||||
c.Redirect(http.StatusPermanentRedirect, fmt.Sprintf("/s/%s/%s", token, uid))
|
||||
return
|
||||
}
|
||||
|
||||
clientConfig := conf.GuestConfig()
|
||||
clientConfig.SiteUrl = fmt.Sprintf("%ss/%s/%s", clientConfig.SiteUrl, shareToken, uid)
|
||||
clientConfig.SiteUrl = fmt.Sprintf("%ss/%s/%s", clientConfig.SiteUrl, token, uid)
|
||||
clientConfig.SitePreview = fmt.Sprintf("%s/preview", clientConfig.SiteUrl)
|
||||
|
||||
if a, err := query.AlbumByUID(uid); err == nil {
|
||||
|
|
|
@ -24,12 +24,12 @@ import (
|
|||
// GET /s/:token/:uid/preview
|
||||
// TODO: Proof of concept, needs refactoring.
|
||||
func SharePreview(router *gin.RouterGroup) {
|
||||
router.GET("/:token/:uid/preview", func(c *gin.Context) {
|
||||
router.GET("/:token/:share/preview", func(c *gin.Context) {
|
||||
conf := service.Config()
|
||||
shareToken := c.Param("token")
|
||||
shareUID := c.Param("uid")
|
||||
|
||||
links := entity.FindLinks(shareToken, shareUID)
|
||||
token := c.Param("token")
|
||||
share := c.Param("share")
|
||||
links := entity.FindLinks(token, share)
|
||||
|
||||
if len(links) != 1 {
|
||||
log.Warn("share: invalid token (preview)")
|
||||
|
@ -45,17 +45,17 @@ func SharePreview(router *gin.RouterGroup) {
|
|||
return
|
||||
}
|
||||
|
||||
previewFilename := fmt.Sprintf("%s/%s.jpg", thumbPath, shareUID)
|
||||
previewFilename := fmt.Sprintf("%s/%s.jpg", thumbPath, share)
|
||||
yesterday := time.Now().Add(-24 * time.Hour)
|
||||
|
||||
if info, err := os.Stat(previewFilename); err != nil {
|
||||
log.Debugf("share: creating new preview for %s", shareUID)
|
||||
log.Debugf("share: creating new preview for %s", share)
|
||||
} else if info.ModTime().After(yesterday) {
|
||||
log.Debugf("share: using cached preview for %s", shareUID)
|
||||
log.Debugf("share: using cached preview for %s", share)
|
||||
c.File(previewFilename)
|
||||
return
|
||||
} else if err := os.Remove(previewFilename); err != nil {
|
||||
log.Errorf("share: could not remove old preview of %s", shareUID)
|
||||
log.Errorf("share: could not remove old preview of %s", share)
|
||||
c.Redirect(http.StatusTemporaryRedirect, conf.SitePreview())
|
||||
return
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ func SharePreview(router *gin.RouterGroup) {
|
|||
var f form.PhotoSearch
|
||||
|
||||
// Previews may only contain public content in shared albums.
|
||||
f.Album = shareUID
|
||||
f.Album = share
|
||||
f.Public = true
|
||||
f.Private = false
|
||||
f.Hidden = false
|
||||
|
|
|
@ -15,18 +15,18 @@ type Links []Link
|
|||
|
||||
// Link represents a sharing link.
|
||||
type Link struct {
|
||||
LinkUID string `gorm:"type:varbinary(42);primary_key;" json:"UID,omitempty" yaml:"UID,omitempty"`
|
||||
ShareUID string `gorm:"type:varbinary(42);unique_index:idx_links_uid_token;" json:"Share" yaml:"Share"`
|
||||
ShareSlug string `gorm:"type:varbinary(255);index;" json:"Slug" yaml:"Slug,omitempty"`
|
||||
ShareToken string `gorm:"type:varbinary(255);unique_index:idx_links_uid_token;" json:"Token" yaml:"Token,omitempty"`
|
||||
ShareExpires int `json:"Expires" yaml:"Expires,omitempty"`
|
||||
ShareViews uint `json:"ShareViews" yaml:"-"`
|
||||
MaxViews uint `json:"MaxViews" yaml:"-"`
|
||||
HasPassword bool `json:"HasPassword" yaml:"HasPassword,omitempty"`
|
||||
CanComment bool `json:"CanComment" yaml:"CanComment,omitempty"`
|
||||
CanEdit bool `json:"CanEdit" yaml:"CanEdit,omitempty"`
|
||||
CreatedAt time.Time `deepcopier:"skip" json:"CreatedAt" yaml:"CreatedAt"`
|
||||
ModifiedAt time.Time `deepcopier:"skip" yaml:"ModifiedAt"`
|
||||
LinkUID string `gorm:"type:varbinary(42);primary_key;" json:"UID,omitempty" yaml:"UID,omitempty"`
|
||||
ShareUID string `gorm:"type:varbinary(42);unique_index:idx_links_uid_token;" json:"Share" yaml:"Share"`
|
||||
ShareSlug string `gorm:"type:varbinary(255);index;" json:"Slug" yaml:"Slug,omitempty"`
|
||||
LinkToken string `gorm:"type:varbinary(255);unique_index:idx_links_uid_token;" json:"Token" yaml:"Token,omitempty"`
|
||||
LinkExpires int `json:"Expires" yaml:"Expires,omitempty"`
|
||||
LinkViews uint `json:"Views" yaml:"-"`
|
||||
MaxViews uint `json:"MaxViews" yaml:"-"`
|
||||
HasPassword bool `json:"HasPassword" yaml:"HasPassword,omitempty"`
|
||||
CanComment bool `json:"CanComment" yaml:"CanComment,omitempty"`
|
||||
CanEdit bool `json:"CanEdit" yaml:"CanEdit,omitempty"`
|
||||
CreatedAt time.Time `deepcopier:"skip" json:"CreatedAt" yaml:"CreatedAt"`
|
||||
ModifiedAt time.Time `deepcopier:"skip" yaml:"ModifiedAt"`
|
||||
}
|
||||
|
||||
// BeforeCreate creates a random UID if needed before inserting a new row to the database.
|
||||
|
@ -45,7 +45,7 @@ func NewLink(shareUID string, canComment, canEdit bool) Link {
|
|||
result := Link{
|
||||
LinkUID: rnd.PPID('s'),
|
||||
ShareUID: shareUID,
|
||||
ShareToken: rnd.Token(10),
|
||||
LinkToken: rnd.Token(10),
|
||||
CanComment: canComment,
|
||||
CanEdit: canEdit,
|
||||
CreatedAt: now,
|
||||
|
@ -56,9 +56,9 @@ func NewLink(shareUID string, canComment, canEdit bool) Link {
|
|||
}
|
||||
|
||||
func (m *Link) Redeem() {
|
||||
m.ShareViews += 1
|
||||
m.LinkViews += 1
|
||||
|
||||
result := Db().Model(m).UpdateColumn("ShareViews", m.ShareViews)
|
||||
result := Db().Model(m).UpdateColumn("LinkViews", m.LinkViews)
|
||||
|
||||
if result.RowsAffected == 0 {
|
||||
log.Warnf("link: failed updating share view counter for %s", m.LinkUID)
|
||||
|
@ -66,16 +66,16 @@ func (m *Link) Redeem() {
|
|||
}
|
||||
|
||||
func (m *Link) Expired() bool {
|
||||
if m.MaxViews > 0 && m.ShareViews >= m.MaxViews {
|
||||
if m.MaxViews > 0 && m.LinkViews >= m.MaxViews {
|
||||
return true
|
||||
}
|
||||
|
||||
if m.ShareExpires <= 0 {
|
||||
if m.LinkExpires <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
now := Timestamp()
|
||||
expires := m.ModifiedAt.Add(Seconds(m.ShareExpires))
|
||||
expires := m.ModifiedAt.Add(Seconds(m.LinkExpires))
|
||||
|
||||
return now.Before(expires)
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ func (m *Link) Save() error {
|
|||
return fmt.Errorf("link: invalid share uid (%s)", m.ShareUID)
|
||||
}
|
||||
|
||||
if m.ShareToken == "" {
|
||||
if m.LinkToken == "" {
|
||||
return fmt.Errorf("link: empty share token")
|
||||
}
|
||||
|
||||
|
@ -127,7 +127,7 @@ func (m *Link) Save() error {
|
|||
|
||||
// Deletes the link.
|
||||
func (m *Link) Delete() error {
|
||||
if m.ShareToken == "" {
|
||||
if m.LinkToken == "" {
|
||||
return fmt.Errorf("link: empty share token")
|
||||
}
|
||||
|
||||
|
@ -148,16 +148,16 @@ func FindLink(linkUID string) *Link {
|
|||
}
|
||||
|
||||
// FindLinks returns a slice of links for a token and share UID (at least one must be provided).
|
||||
func FindLinks(shareToken, share string) (result Links) {
|
||||
if shareToken == "" && share == "" {
|
||||
func FindLinks(token, share string) (result Links) {
|
||||
if token == "" && share == "" {
|
||||
log.Errorf("link: share token and uid must not be empty at the same time (find links)")
|
||||
return []Link{}
|
||||
}
|
||||
|
||||
q := Db()
|
||||
|
||||
if shareToken != "" {
|
||||
q = q.Where("share_token = ?", shareToken)
|
||||
if token != "" {
|
||||
q = q.Where("link_token = ?", token)
|
||||
}
|
||||
|
||||
if share != "" {
|
||||
|
@ -176,8 +176,8 @@ func FindLinks(shareToken, share string) (result Links) {
|
|||
}
|
||||
|
||||
// FindValidLinks returns a slice of non-expired links for a token and share UID (at least one must be provided).
|
||||
func FindValidLinks(shareToken, shareUID string) (result Links) {
|
||||
for _, link := range FindLinks(shareToken, shareUID) {
|
||||
func FindValidLinks(token, share string) (result Links) {
|
||||
for _, link := range FindLinks(token, share) {
|
||||
if !link.Expired() {
|
||||
result = append(result, link)
|
||||
}
|
||||
|
@ -188,5 +188,5 @@ func FindValidLinks(shareToken, shareUID string) (result Links) {
|
|||
|
||||
// String returns an human readable identifier for logging.
|
||||
func (m *Link) String() string {
|
||||
return fmt.Sprintf("%s/%s", m.ShareUID, m.ShareToken)
|
||||
return m.LinkUID
|
||||
}
|
||||
|
|
|
@ -8,12 +8,12 @@ type LinkMap map[string]Link
|
|||
|
||||
var LinkFixtures = LinkMap{
|
||||
"1jxf3jfn2k": {
|
||||
ShareToken: "1jxf3jfn2k",
|
||||
ShareExpires: 0,
|
||||
ShareUID: "st9lxuqxpogaaba7",
|
||||
CanComment: true,
|
||||
CanEdit: false,
|
||||
CreatedAt: time.Date(2020, 3, 6, 2, 6, 51, 0, time.UTC),
|
||||
LinkToken: "1jxf3jfn2k",
|
||||
LinkExpires: 0,
|
||||
ShareUID: "st9lxuqxpogaaba7",
|
||||
CanComment: true,
|
||||
CanEdit: false,
|
||||
CreatedAt: time.Date(2020, 3, 6, 2, 6, 51, 0, time.UTC),
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ func TestNewLink(t *testing.T) {
|
|||
assert.Equal(t, "st9lxuqxpogaaba1", link.ShareUID)
|
||||
assert.Equal(t, false, link.CanEdit)
|
||||
assert.Equal(t, true, link.CanComment)
|
||||
assert.Equal(t, 10, len(link.ShareToken))
|
||||
assert.Equal(t, 10, len(link.LinkToken))
|
||||
assert.Equal(t, 16, len(link.LinkUID))
|
||||
}
|
||||
|
||||
|
@ -22,20 +22,20 @@ func TestLink_Expired(t *testing.T) {
|
|||
link := NewLink("st9lxuqxpogaaba1", true, false)
|
||||
|
||||
link.ModifiedAt = Timestamp().Add(-7 * Day)
|
||||
link.ShareExpires = 0
|
||||
link.LinkExpires = 0
|
||||
|
||||
assert.False(t, link.Expired())
|
||||
|
||||
link.ShareExpires = oneDay
|
||||
link.LinkExpires = oneDay
|
||||
|
||||
assert.False(t, link.Expired())
|
||||
|
||||
link.ShareExpires = oneDay * 8
|
||||
link.LinkExpires = oneDay * 8
|
||||
|
||||
assert.True(t, link.Expired())
|
||||
|
||||
link.ShareExpires = oneDay
|
||||
link.ShareViews = 9
|
||||
link.LinkExpires = oneDay
|
||||
link.LinkViews = 9
|
||||
link.MaxViews = 10
|
||||
|
||||
assert.False(t, link.Expired())
|
||||
|
@ -48,11 +48,11 @@ func TestLink_Expired(t *testing.T) {
|
|||
func TestLink_Redeem(t *testing.T) {
|
||||
link := NewLink(rnd.PPID('a'), false, false)
|
||||
|
||||
assert.Equal(t, uint(0), link.ShareViews)
|
||||
assert.Equal(t, uint(0), link.LinkViews)
|
||||
|
||||
link.Redeem()
|
||||
|
||||
assert.Equal(t, uint(1), link.ShareViews)
|
||||
assert.Equal(t, uint(1), link.LinkViews)
|
||||
|
||||
if err := link.Save(); err != nil {
|
||||
t.Fatal(err)
|
||||
|
@ -60,5 +60,5 @@ func TestLink_Redeem(t *testing.T) {
|
|||
|
||||
link.Redeem()
|
||||
|
||||
assert.Equal(t, uint(2), link.ShareViews)
|
||||
assert.Equal(t, uint(2), link.LinkViews)
|
||||
}
|
||||
|
|
|
@ -2,11 +2,11 @@ package form
|
|||
|
||||
// Link represents a link sharing form.
|
||||
type Link struct {
|
||||
Password string `json:"Password"`
|
||||
ShareSlug string `json:"Slug"`
|
||||
ShareToken string `json:"Token"`
|
||||
ShareExpires int `json:"Expires"`
|
||||
MaxViews uint `json:"MaxViews"`
|
||||
CanComment bool `json:"CanComment"`
|
||||
CanEdit bool `json:"CanEdit"`
|
||||
Password string `json:"Password"`
|
||||
ShareSlug string `json:"Slug"`
|
||||
LinkToken string `json:"Token"`
|
||||
LinkExpires int `json:"Expires"`
|
||||
MaxViews uint `json:"MaxViews"`
|
||||
CanComment bool `json:"CanComment"`
|
||||
CanEdit bool `json:"CanEdit"`
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue