feat: add OpenGraph metadata for feed URLs in bskyweb

Enable rich link previews when feed URLs are shared in iMessage, Slack, and other social platforms. Adds feed title, description, creator info, and avatar images to improve sharing experience.
This commit is contained in:
Caidan Williams
2025-09-09 20:03:50 -07:00
parent c7591ef058
commit f5079b8158
2 changed files with 106 additions and 1 deletions
+52 -1
View File
@@ -313,7 +313,7 @@ func serve(cctx *cli.Context) error {
e.GET("/profile/:handleOrDID/known-followers", server.WebGeneric)
e.GET("/profile/:handleOrDID/search", server.WebGeneric)
e.GET("/profile/:handleOrDID/lists/:rkey", server.WebGeneric)
e.GET("/profile/:handleOrDID/feed/:rkey", server.WebGeneric)
e.GET("/profile/:handleOrDID/feed/:rkey", server.WebFeed)
e.GET("/profile/:handleOrDID/feed/:rkey/liked-by", server.WebGeneric)
e.GET("/profile/:handleOrDID/labeler/liked-by", server.WebGeneric)
@@ -603,6 +603,57 @@ func (srv *Server) WebProfile(c echo.Context) error {
return c.Render(http.StatusOK, "profile.html", data)
}
func (srv *Server) WebFeed(c echo.Context) error {
ctx := c.Request().Context()
data := srv.NewTemplateContext()
// sanity check arguments. don't 4xx, just let app handle if not expected format
rkeyParam := c.Param("rkey")
rkey, err := syntax.ParseRecordKey(rkeyParam)
if err != nil {
return c.Render(http.StatusOK, "feed.html", data)
}
handleOrDIDParam := c.Param("handleOrDID")
handleOrDID, err := syntax.ParseAtIdentifier(handleOrDIDParam)
if err != nil {
return c.Render(http.StatusOK, "feed.html", data)
}
identifier := handleOrDID.Normalize().String()
// requires two fetches: first fetch profile to get DID
pv, err := appbsky.ActorGetProfile(ctx, srv.xrpcc, identifier)
if err != nil {
log.Warnf("failed to fetch profile for: %s\t%v", identifier, err)
return c.Render(http.StatusOK, "feed.html", data)
}
unauthedViewingOkay := true
for _, label := range pv.Labels {
if label.Src == pv.Did && label.Val == "!no-unauthenticated" {
unauthedViewingOkay = false
}
}
if !unauthedViewingOkay {
return c.Render(http.StatusOK, "feed.html", data)
}
did := pv.Did
data["did"] = did
// then fetch the feed generator
feedURI := fmt.Sprintf("at://%s/app.bsky.feed.generator/%s", did, rkey)
fgv, err := appbsky.FeedGetFeedGenerator(ctx, srv.xrpcc, feedURI)
if err != nil {
log.Warnf("failed to fetch feed generator: %s\t%v", feedURI, err)
return c.Render(http.StatusOK, "feed.html", data)
}
req := c.Request()
data["feedView"] = fgv.View
data["requestURI"] = fmt.Sprintf("https://%s%s", req.Host, req.URL.Path)
return c.Render(http.StatusOK, "feed.html", data)
}
type IPCCRequest struct {
IP string `json:"ip"`
}
+54
View File
@@ -0,0 +1,54 @@
{% extends "base.html" %}
{% block head_title %}
{%- if feedView -%}
{{ feedView.DisplayName }} by @{{ feedView.Creator.Handle }} | Bluesky Feed
{%- else -%}
Bluesky
{%- endif -%}
{% endblock %}
{% block html_head_extra -%}
{%- if feedView -%}
<meta property="og:site_name" content="Bluesky Social">
<meta property="og:type" content="website">
{%- if requestURI %}
<meta property="og:url" content="{{ requestURI }}">
<link rel="canonical" href="{{ requestURI|canonicalize_url }}" />
{% endif -%}
{%- if feedView.DisplayName %}
<meta property="og:title" content="{{ feedView.DisplayName }} by @{{ feedView.Creator.Handle }}">
{% else %}
<meta property="og:title" content="Feed by @{{ feedView.Creator.Handle }}">
{% endif -%}
{%- if feedView.Description %}
<meta name="description" content="{{ feedView.Description }}">
<meta property="og:description" content="{{ feedView.Description }}">
<meta property="twitter:description" content="{{ feedView.Description }}">
{% endif -%}
{%- if feedView.Avatar %}
<meta property="og:image" content="{{ feedView.Avatar }}">
<meta property="twitter:image" content="{{ feedView.Avatar }}">
<meta name="twitter:card" content="summary">
{% endif %}
<meta name="twitter:label1" content="Created by">
<meta name="twitter:value1" content="@{{ feedView.Creator.Handle }}">
<link rel="alternate" href="{{ feedView.Uri }}" />
{% endif -%}
{%- endblock %}
{% block noscript_extra -%}
{%- if feedView -%}
<div id="bsky_feed_summary">
<h3>Feed</h3>
<p id="bsky_feed_name">{{ feedView.DisplayName }}</p>
<p id="bsky_feed_creator">{{ feedView.Creator.Handle }}</p>
<p id="bsky_feed_description">{{ feedView.Description }}</p>
</div>
{% endif -%}
{%- endblock %}