feat(editor): preview cards for external links 🤖🤖🤖 - #3493
Open
AlexDeus20 wants to merge 1 commit into
Open
Conversation
Add a GET /api/v2/link-preview endpoint that fetches an external http(s) URL and returns its OpenGraph/meta summary (title, description, image, site name, favicon). Outbound requests reuse the existing SSRF-safe HTTP client, and the metadata is parsed with golang.org/x/net/html — no new dependencies. The endpoint requires authentication. On the frontend, a ProseMirror widget-decoration extension renders Slack-style preview cards below external links in task descriptions and comments, but only in read-only (preview) mode, so nothing is stored in the document. Previews are fetched lazily and cached per URL. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #3492.
What
Slack-style preview cards for external links in task descriptions and comments.
Backend —
GET /api/v2/link-preview?url={url, title, description, image, site_name, favicon}.utils.NewSSRFSafeHTTPClient()(code.dny.dev/ssrf), whoseDialer.Controlhook validates the resolved IP on every dial — including each redirect hop — so loopback/private/link-local targets are refused before any connection. Same pattern (and//nolint:gosec) as the existing gravatar fetcher.http(s)accepted (→ 422 otherwise); 8s per-request timeout (→ 504); upstream/dial failures → 502; response body capped at 2 MiB;og:image/favicon resolved against the final URL and non-http(s)schemes (data:,javascript:) dropped.golang.org/x/net/html(already in go.mod) — no new dependency.Frontend
linkPreview.ts) renders a card below each external link, only in read-only/preview mode, so nothing is stored in the document. Cards are plain DOM (text viatextContent), lazily fetched and cached per URL viaservices/linkPreview.ts.Testing
pkg/routes/api/v2/link_preview_test.go) + endpoint test for auth/validation/SSRF (pkg/webtests/huma_link_preview_test.go, run viamage test:filter).gofmt/go vetclean.https://github.com/ai/nanoid→ full OG card;http://127.0.0.1→ 502 (SSRF-blocked);ftp://→ 422; unauthenticated → 401.pnpm lint/pnpm lint:styles/vite buildpass; verified in a browser (WebKit) that external links in a description render inline cards (GitHub with image + description, example.com), with no new console errors.Notes
Implemented with the help of Claude Code (AI); I reviewed the code and runtime-tested both the endpoint and the UI. Happy to add a
linkpreview.enabledconfig toggle if you'd like an admin opt-out.