From 5e02fbcd53a7c8536610c8c4a1f74ade1e78a7db Mon Sep 17 00:00:00 2001 From: WittF Date: Thu, 22 Jan 2026 12:07:56 +0000 Subject: [PATCH] fix(share): improve OG preview with edge cases and code reuse - Handle invalid/expired/password-protected shares gracefully - Show appropriate messages for login-required shares - Simplify bot detection to social media crawlers only - Extract isShareUnlocked() for code reuse --- routers/controllers/share.go | 10 ----- service/share/visit.go | 74 +++++++++++++++++++++++++++--------- 2 files changed, 56 insertions(+), 28 deletions(-) diff --git a/routers/controllers/share.go b/routers/controllers/share.go index eda524c2..d70b6a03 100644 --- a/routers/controllers/share.go +++ b/routers/controllers/share.go @@ -101,17 +101,7 @@ func isSocialMediaBot(ua string) bool { "slackbot", "discordbot", "telegrambot", - "applebot", - "pinterest", - "googlebot", - "bingbot", - "yandexbot", - "duckduckbot", - "baiduspider", - "sogou", - "exabot", "facebot", - "ia_archiver", } ua = strings.ToLower(ua) for _, bot := range bots { diff --git a/service/share/visit.go b/service/share/visit.go index ee69fded..e941a0c3 100644 --- a/service/share/visit.go +++ b/service/share/visit.go @@ -54,19 +54,20 @@ func (s *ShortLinkRedirectService) RenderOGPage(c *gin.Context) (string, error) dep := dependency.FromContext(c) shareClient := dep.ShareClient() settings := dep.SettingProvider() + u := inventory.UserFromContext(c) + + // Check if anonymous users have permission to access share links + anonymousGroup, err := dep.GroupClient().AnonymousGroup(c) + needLogin := err != nil || anonymousGroup.Permissions == nil || !anonymousGroup.Permissions.Enabled(int(types.GroupPermissionShareDownload)) // Load share with user and file info ctx := context.WithValue(c, inventory.LoadShareUser{}, true) ctx = context.WithValue(ctx, inventory.LoadShareFile{}, true) share, err := shareClient.GetByHashID(ctx, s.ID) - if err != nil { - return "", err - } - // Check if share is valid - if err := inventory.IsValidShare(share); err != nil { - return "", err - } + // Determine share status + shareNotFound := err != nil + shareExpired := !shareNotFound && inventory.IsValidShare(share) != nil // Get site settings siteBasic := settings.SiteBasic(c) @@ -85,12 +86,40 @@ func (s *ShortLinkRedirectService) RenderOGPage(c *gin.Context) (string, error) thumbnailURL = base.ResolveReference(&url.URL{Path: thumbnailURL}).String() } - // Get file info - fileName := share.Edges.File.Name - fileSize := FormatFileSize(share.Edges.File.Size) - - // Get owner name (don't expose email for privacy) - ownerName := share.Edges.User.Nick + // Get file info (hide for invalid/expired/password-protected/login-required shares) + var fileName, fileSize, ownerName string + if shareNotFound { + fileName = siteBasic.Name + fileSize = "Invalid Link" + } else if needLogin { + fileName = siteBasic.Name + fileSize = "Need Login" + } else if shareExpired { + fileName = siteBasic.Name + fileSize = "Share Expired" + } else if share.Password != "" && !isShareUnlocked(share, s.Password, u) { + // Password required but not provided or incorrect + fileName = siteBasic.Name + fileSize = "Password Required" + } else if share.Edges.File != nil { + fileName = share.Edges.File.Name + if fileName == "" { + fileName = "Shared File" + } + // Show "Folder" for directories, file size for files + if types.FileType(share.Edges.File.Type) == types.FileTypeFolder { + fileSize = "Folder" + } else { + fileSize = FormatFileSize(share.Edges.File.Size) + } + // Get owner name (don't expose email for privacy) + if share.Edges.User != nil { + ownerName = share.Edges.User.Nick + } + } else { + fileName = siteBasic.Name + fileSize = "Invalid Link" + } // Prepare OG data ogData := &ShareOGData{ @@ -111,6 +140,19 @@ func isAbsoluteURL(u string) bool { return strings.HasPrefix(u, "http://") || strings.HasPrefix(u, "https://") } +func isShareUnlocked(share *ent.Share, password string, viewer *ent.User) bool { + if share.Password == "" { + return true + } + if password == share.Password { + return true + } + if viewer != nil && share.Edges.User != nil && share.Edges.User.ID == viewer.ID { + return true + } + return false +} + type ( ShareInfoService struct { Password string `form:"password"` @@ -143,11 +185,7 @@ func (s *ShareInfoService) Get(c *gin.Context) (*explorer.Share, error) { _ = shareClient.Viewed(c, share) } - unlocked := true - // Share requires password - if share.Password != "" && s.Password != share.Password && share.Edges.User.ID != u.ID { - unlocked = false - } + unlocked := isShareUnlocked(share, s.Password, u) base := dep.SettingProvider().SiteURL(c) res := explorer.BuildShare(share, base, dep.HashIDEncoder(), u, share.Edges.User, share.Edges.File.Name,