feat(og): reuse existing share service and show thumbnail if possible

pull/3234/head
Aaron Liu 1 week ago
parent 572859fd1f
commit d06a29e8a3

@ -2,26 +2,27 @@ package middleware
import (
"bytes"
"crypto/subtle"
"errors"
"fmt"
"html/template"
"net/url"
"strings"
"github.com/cloudreve/Cloudreve/v4/application/constants"
"github.com/cloudreve/Cloudreve/v4/application/dependency"
"github.com/cloudreve/Cloudreve/v4/ent"
"github.com/cloudreve/Cloudreve/v4/inventory"
"github.com/cloudreve/Cloudreve/v4/inventory/types"
"github.com/cloudreve/Cloudreve/v4/pkg/cluster/routes"
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs"
"github.com/cloudreve/Cloudreve/v4/pkg/hashid"
"github.com/cloudreve/Cloudreve/v4/pkg/serializer"
"github.com/cloudreve/Cloudreve/v4/pkg/util"
"github.com/cloudreve/Cloudreve/v4/service/explorer"
"github.com/cloudreve/Cloudreve/v4/service/share"
"github.com/gin-gonic/gin"
)
const (
ogStatusInvalidLink = "Invalid Link"
ogStatusShareUnavailable = "Share Unavailable"
ogStatusPasswordRequired = "Password Required"
ogStatusNeedLogin = "Login Required"
ogStatusInvalidLink = "Invalid Link"
)
type ogData struct {
@ -114,22 +115,14 @@ func extractShareParams(c *gin.Context) (id, password string) {
}
} else if urlPath == "/home" || urlPath == "/home/" {
rawPath := c.Query("path")
if strings.HasPrefix(rawPath, "cloudreve://") {
u, err := url.Parse(rawPath)
if err != nil {
return "", ""
}
if strings.ToLower(u.Host) != "share" || u.User == nil {
return "", ""
}
id = u.User.Username()
password, _ = u.User.Password()
uri, err := fs.NewUriFromString(rawPath)
if err != nil || uri.FileSystem() != constants.FileSystemShare {
return "", ""
}
}
if len(id) > 32 || len(password) > 32 {
return "", ""
return uri.ID(""), uri.Password()
}
return id, password
}
@ -143,7 +136,7 @@ func renderShareOGPage(c *gin.Context, dep dependency.Dep, id, password string)
SiteName: siteBasic.Name,
Title: siteBasic.Name,
Description: siteBasic.Description,
ShareURL: routes.MasterShareUrl(base, id, "").String(),
ShareURL: routes.MasterShareUrl(base, id, password).String(),
RedirectURL: routes.MasterShareLongUrl(id, password).String(),
}
@ -159,62 +152,66 @@ func renderShareOGPage(c *gin.Context, dep dependency.Dep, id, password string)
return renderOGHTML(data)
}
share, err := loadShareForOG(c, dep, shareID)
shareInfo, err := loadShareForOG(c, shareID, password)
if err != nil {
if ent.IsNotFound(err) {
data.Description = ogStatusInvalidLink
var appErr serializer.AppError
if errors.As(err, &appErr) {
data.Description = appErr.Msg
} else {
data.Description = ogStatusShareUnavailable
data.Description = ogStatusInvalidLink
}
return renderOGHTML(data)
}
if err := inventory.IsValidShare(share); err != nil {
data.Description = ogStatusShareUnavailable
return renderOGHTML(data)
data.Title = shareInfo.Name
if shareInfo.SourceType != nil && *shareInfo.SourceType == types.FileTypeFolder {
data.Description = "Folder"
} else {
data.Description = formatFileSize(shareInfo.Size)
thumbnail, err := loadShareThumbnail(c, id, password, shareInfo)
if err == nil {
data.ImageURL = thumbnail
}
}
if !canAnonymousAccessShare(c, dep) {
data.Description = ogStatusNeedLogin
return renderOGHTML(data)
}
data.Description += " · " + shareInfo.Owner.Nickname
return renderOGHTML(data)
}
if share.Password != "" && subtle.ConstantTimeCompare([]byte(share.Password), []byte(password)) != 1 {
data.Description = ogStatusPasswordRequired
return renderOGHTML(data)
func loadShareThumbnail(c *gin.Context, shareID, password string, shareInfo *explorer.Share) (string, error) {
shareUri, err := fs.NewUriFromString(fs.NewShareUri(shareID, password))
if err != nil {
return "", fmt.Errorf("failed to construct share uri: %w", err)
}
targetFile := share.Edges.File
if targetFile != nil {
fileName := targetFile.Name
fileType := types.FileType(targetFile.Type)
subService := &explorer.FileThumbService{
Uri: shareUri.Join(shareInfo.Name).String(),
}
data.Title = fileName
if fileType == types.FileTypeFolder {
data.Description = "Folder"
} else {
data.Description = formatFileSize(targetFile.Size)
}
if err := SetUserCtx(c, 0); err != nil {
return "", err
}
if share.Edges.User != nil && share.Edges.User.Nick != "" {
data.Description += " · " + share.Edges.User.Nick
}
res, err := subService.Get(c)
if err != nil {
return "", err
}
return renderOGHTML(data)
return res.Url, nil
}
func loadShareForOG(c *gin.Context, dep dependency.Dep, shareID int) (*ent.Share, error) {
ctx := inventory.WithShareEagerLoad(c)
return dep.ShareClient().GetByID(ctx, shareID)
}
func loadShareForOG(c *gin.Context, shareID int, password string) (*explorer.Share, error) {
subService := &share.ShareInfoService{
Password: password,
CountViews: false,
}
func canAnonymousAccessShare(c *gin.Context, dep dependency.Dep) bool {
anonymousGroup, err := dep.GroupClient().AnonymousGroup(c)
if err != nil {
return false
if err := SetUserCtx(c, 0); err != nil {
return nil, err
}
return anonymousGroup.Permissions.Enabled(int(types.GroupPermissionShareDownload))
util.WithValue(c, hashid.ObjectIDCtx{}, shareID)
return subService.Get(c)
}
func renderOGHTML(data *ogData) string {

@ -310,6 +310,7 @@ type Share struct {
Expired bool `json:"expired"`
Url string `json:"url"`
ShowReadMe bool `json:"show_readme,omitempty"`
Size int64 `json:"size"`
// Only viewable by owner
IsPrivate bool `json:"is_private,omitempty"`
@ -345,6 +346,10 @@ func BuildShare(s *ent.Share, base *url.URL, hasher hashid.Encoder, requester *e
res.Expires = s.Expires
res.Password = s.Password
res.ShowReadMe = s.Props != nil && s.Props.ShowReadMe
if t == types.FileTypeFile && s.Edges.File != nil {
res.Size = s.Edges.File.Size
}
}
if requester.ID == owner.ID {

Loading…
Cancel
Save