Skip to content

feat(share): add Open Graph support for social media previews#3227

Closed
WittF wants to merge 6 commits intocloudreve:masterfrom
WittF:master
Closed

feat(share): add Open Graph support for social media previews#3227
WittF wants to merge 6 commits intocloudreve:masterfrom
WittF:master

Conversation

@WittF
Copy link
Copy Markdown
Contributor

@WittF WittF commented Jan 22, 2026

@WittF WittF marked this pull request as draft January 22, 2026 11:35
@HFO4
Copy link
Copy Markdown
Member

HFO4 commented Jan 22, 2026

For a single shared file, is it a easy job to get thumbnail as the hero image?

@WittF
Copy link
Copy Markdown
Contributor Author

WittF commented Jan 22, 2026

For a single shared file, is it a easy job to get thumbnail as the hero image?

This need adding a new public thumbnail endpoint.
Personally, I think a consistent logo looks cleaner than file thumbnails🌼

@WittF WittF force-pushed the master branch 2 times, most recently from 5c41e47 to 3c66839 Compare January 22, 2026 13:24
@WittF WittF marked this pull request as ready for review January 22, 2026 13:28
@YUDONGLING
Copy link
Copy Markdown
Member

As enhancement, maybe later we can add multi-language support (like email template) & also support magic variables?

@WittF
Copy link
Copy Markdown
Contributor Author

WittF commented Jan 23, 2026

As enhancement, maybe later we can add multi-language support (like email template) & also support magic variables?

Good idea!
Though social media bot typically don't send Accept-Language headers, so multi-language auto-detection may not work well.
But customizable templates with magic variables sounds great, I'll give it a try✊

@WittF WittF marked this pull request as draft January 23, 2026 03:38
@WittF WittF marked this pull request as ready for review January 23, 2026 07:28
@HFO4
Copy link
Copy Markdown
Member

HFO4 commented Jan 23, 2026

As enhancement, maybe later we can add multi-language support (like email template) & also support magic variables?

Good idea! Though social media bot typically don't send Accept-Language headers, so multi-language auto-detection may not work well. But customizable templates with magic variables sounds great, I'll give it a try✊

Localization for open graph is hard to implement, I don't think they have a standard for that. For now, we'd better use a unified template, and try not to include language specific phrase, If you have to, use English for now.

@WittF WittF marked this pull request as draft January 23, 2026 08:24
WittF added 3 commits January 23, 2026 08:29
- Detect social media bots (Facebook, Twitter, Discord, etc.)
- Render OG meta tags with file name, size, and owner info
- Auto-redirect to share page after preview is rendered
- 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
- Extend ShareOGData with additional fields for richer previews
- Add magic variable replacement for File/Folder/Status scenarios
- Refactor status handling with switch/case pattern
- Separate rendering paths for files, folders, and status messages
WittF added 2 commits January 23, 2026 12:50
- Extract LoadShareForInfo() and BuildRedirectURL() to pkg/share
- Move OG template and preview logic to pkg/sharepreview
- Extract IsSocialMediaBot() to pkg/util
- Simplify service/share/visit.go to use new packages
Intercept /home route for social media bots viewing cloudreve://share
paths and render OG preview directly.
@WittF
Copy link
Copy Markdown
Contributor Author

WittF commented Jan 23, 2026

As enhancement, maybe later we can add multi-language support (like email template) & also support magic variables?

Good idea! Though social media bot typically don't send Accept-Language headers, so multi-language auto-detection may not work well. But customizable templates with magic variables sounds great, I'll give it a try✊

Localization for open graph is hard to implement, I don't think they have a standard for that. For now, we'd better use a unified template, and try not to include language specific phrase, If you have to, use English for now.

State Title Description Source
File {file_name} {file_size} · {owner_name} L60-61
Folder {folder_name} Folder · {owner_name} L62-63

Status constants (preview.go#L21-L27):

const (  
    ogStatusInvalidLink      = "Invalid Link"                               
    ogStatusShareExpired     = "Share Expired"                                                            
    ogStatusPasswordRequired = "Password Required"                                                             
    ogDefaultFileName        = "Shared File"                                                        
    ogDefaultFolderName      = "Shared Folder"
)              

@WittF WittF marked this pull request as ready for review January 23, 2026 13:00
@WittF WittF requested a review from HFO4 January 23, 2026 13:00
Add facebookcatalog, meta-externalagent, pinterestbot and organize
bot list with comments by platform.
@HFO4
Copy link
Copy Markdown
Member

HFO4 commented Jan 24, 2026

This feels "vibe coded"—the implementation is becoming messy and far too complicated. We need to stop iterating on this branch. I suggest STOP letting an AI handle another new iteration, or I can take it over myself. If you prefer to continue, please open a fresh PR.

return shareURI
}

func sanitizeInvalidPercentEscapes(raw string) string {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain why it this needed?

Comment on lines +132 to +146
shareURI, err := filemanagerfs.NewUriFromString(raw)
if err != nil {
sanitized := sanitizeInvalidPercentEscapes(raw)
if sanitized != raw {
shareURI, err = filemanagerfs.NewUriFromString(sanitized)
}
}
if err != nil && strings.Contains(raw, "%") {
unescaped, err := url.QueryUnescape(raw)
if err == nil {
shareURI, err = filemanagerfs.NewUriFromString(unescaped)
if err != nil {
sanitized := sanitizeInvalidPercentEscapes(unescaped)
if sanitized != unescaped {
shareURI, err = filemanagerfs.NewUriFromString(sanitized)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't get what you're doing here.

Comment on lines +122 to +123
c.Header("Content-Type", "text/html; charset=utf-8")
c.String(http.StatusOK, html)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

c.HTML does the exact same thing in one line.

Comment on lines +23 to +41
// LoadShareForInfo loads share info for public preview/metadata access.
func LoadShareForInfo(ctx context.Context, shareClient inventory.ShareClient, shareID int, viewer *ent.User, password string) (*ent.Share, bool, LoadStatus, error) {
ctx = context.WithValue(ctx, inventory.LoadShareUser{}, true)
ctx = context.WithValue(ctx, inventory.LoadShareFile{}, true)
share, err := shareClient.GetByID(ctx, shareID)
if err != nil {
if ent.IsNotFound(err) {
return nil, false, LoadNotFound, nil
}
return nil, false, LoadError, err
}

if err := inventory.IsValidShare(share); err != nil {
return share, false, LoadExpired, err
}

unlocked := isShareUnlocked(share, password, viewer)
return share, unlocked, LoadOK, nil
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@WittF
Copy link
Copy Markdown
Contributor Author

WittF commented Jan 24, 2026

@HFO4 Sorry! I admit my fault.🥲
I let OpenAI Codex review and optimize my change after the basics, then had it review its own work in a new session. This indeed generate a lot of complex and even useless "garbage".
I'll redo this myself and open a new PR later.🙇‍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants