@linkforty/core 1.22.1 → 1.22.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/routes/preview.d.ts +25 -0
- package/dist/routes/preview.d.ts.map +1 -1
- package/dist/routes/preview.js +49 -19
- package/dist/routes/preview.js.map +1 -1
- package/dist/routes/preview.test.d.ts +2 -0
- package/dist/routes/preview.test.d.ts.map +1 -0
- package/dist/routes/preview.test.js +112 -0
- package/dist/routes/preview.test.js.map +1 -0
- package/package.json +1 -1
package/dist/routes/preview.d.ts
CHANGED
|
@@ -1,3 +1,28 @@
|
|
|
1
1
|
import { FastifyInstance } from 'fastify';
|
|
2
|
+
/**
|
|
3
|
+
* User-agents that get the Open Graph preview page instead of a redirect.
|
|
4
|
+
*
|
|
5
|
+
* These bots crawl links to generate previews when shared on social platforms.
|
|
6
|
+
* A user-agent that is not listed here falls through to the redirect handler,
|
|
7
|
+
* so the share card ends up showing the *destination's* metadata rather than
|
|
8
|
+
* the link's.
|
|
9
|
+
*
|
|
10
|
+
* Every entry must be a token that ONLY a server-side fetcher sends. Bare
|
|
11
|
+
* platform names are the trap: `Instagram`, `Threads`, `Snapchat` and `Tumblr`
|
|
12
|
+
* all appear in those apps' in-app *browser* user-agents, so matching on the
|
|
13
|
+
* name alone would serve a real person the meta-refresh interstitial instead of
|
|
14
|
+
* their redirect — a worse failure than a missing preview card. They are
|
|
15
|
+
* deliberately absent. Instagram and Threads previews are generated by Meta's
|
|
16
|
+
* `facebookexternalhit`, which is already covered.
|
|
17
|
+
*
|
|
18
|
+
* `WhatsApp` below has that defect and predates this rule. Narrowing it would
|
|
19
|
+
* risk breaking previews that currently work, so it stays until someone
|
|
20
|
+
* captures a real WhatsApp scraper user-agent to replace it with.
|
|
21
|
+
*/
|
|
22
|
+
export declare const SCRAPER_PATTERNS: RegExp[];
|
|
23
|
+
/**
|
|
24
|
+
* Detect if the request is from a social media scraper/bot.
|
|
25
|
+
*/
|
|
26
|
+
export declare function isSocialScraper(userAgent: string): boolean;
|
|
2
27
|
export declare function previewRoutes(fastify: FastifyInstance): Promise<void>;
|
|
3
28
|
//# sourceMappingURL=preview.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/routes/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"preview.d.ts","sourceRoot":"","sources":["../../src/routes/preview.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAG1C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,gBAAgB,UAyB5B,CAAC;AAEF;;GAEG;AACH,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE1D;AAkID,wBAAsB,aAAa,CAAC,OAAO,EAAE,eAAe,iBAgG3D"}
|
package/dist/routes/preview.js
CHANGED
|
@@ -1,25 +1,55 @@
|
|
|
1
1
|
import { db } from '../lib/database.js';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* User-agents that get the Open Graph preview page instead of a redirect.
|
|
4
|
+
*
|
|
5
|
+
* These bots crawl links to generate previews when shared on social platforms.
|
|
6
|
+
* A user-agent that is not listed here falls through to the redirect handler,
|
|
7
|
+
* so the share card ends up showing the *destination's* metadata rather than
|
|
8
|
+
* the link's.
|
|
9
|
+
*
|
|
10
|
+
* Every entry must be a token that ONLY a server-side fetcher sends. Bare
|
|
11
|
+
* platform names are the trap: `Instagram`, `Threads`, `Snapchat` and `Tumblr`
|
|
12
|
+
* all appear in those apps' in-app *browser* user-agents, so matching on the
|
|
13
|
+
* name alone would serve a real person the meta-refresh interstitial instead of
|
|
14
|
+
* their redirect — a worse failure than a missing preview card. They are
|
|
15
|
+
* deliberately absent. Instagram and Threads previews are generated by Meta's
|
|
16
|
+
* `facebookexternalhit`, which is already covered.
|
|
17
|
+
*
|
|
18
|
+
* `WhatsApp` below has that defect and predates this rule. Narrowing it would
|
|
19
|
+
* risk breaking previews that currently work, so it stays until someone
|
|
20
|
+
* captures a real WhatsApp scraper user-agent to replace it with.
|
|
5
21
|
*/
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
export const SCRAPER_PATTERNS = [
|
|
23
|
+
/facebookexternalhit/i, // Facebook, Instagram, Threads
|
|
24
|
+
/Facebot/i, // Facebook
|
|
25
|
+
/Twitterbot/i, // X
|
|
26
|
+
/LinkedInBot/i, // LinkedIn
|
|
27
|
+
/Slackbot/i, // Slack (also matches Slackbot-LinkExpanding)
|
|
28
|
+
/Discordbot/i, // Discord
|
|
29
|
+
/TelegramBot/i, // Telegram
|
|
30
|
+
/WhatsApp/i, // WhatsApp — see caveat above
|
|
31
|
+
/PinterestBot/i, // Pinterest
|
|
32
|
+
/SkypeUriPreview/i, // Skype
|
|
33
|
+
/Googlebot/i, // Google (for search previews)
|
|
34
|
+
/bingbot/i, // Bing
|
|
35
|
+
/ia_archiver/i, // Alexa
|
|
36
|
+
/Applebot/i, // iMessage, Notes, Spotlight
|
|
37
|
+
/redditbot/i, // Reddit
|
|
38
|
+
/Cardyb/i, // Bluesky link cards — UA masquerades as Chrome
|
|
39
|
+
/Mastodon/i, // Fediverse
|
|
40
|
+
/vkShare/i, // VK
|
|
41
|
+
/Embedly/i, // Aggregator
|
|
42
|
+
/Iframely/i, // Aggregator
|
|
43
|
+
/opengraph/i, // opengraph.xyz and similar validators
|
|
44
|
+
/MetaInspector/i, // Validator
|
|
45
|
+
/PreviewBot/i, // Generic preview fetchers
|
|
46
|
+
/LinkPreview/i, // linkpreview.net and similar
|
|
47
|
+
];
|
|
48
|
+
/**
|
|
49
|
+
* Detect if the request is from a social media scraper/bot.
|
|
50
|
+
*/
|
|
51
|
+
export function isSocialScraper(userAgent) {
|
|
52
|
+
return SCRAPER_PATTERNS.some(pattern => pattern.test(userAgent));
|
|
23
53
|
}
|
|
24
54
|
/**
|
|
25
55
|
* Generate HTML preview page with Open Graph meta tags
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/routes/preview.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,EAAE,EAAE,MAAM,oBAAoB,CAAC;AAExC
|
|
1
|
+
{"version":3,"file":"preview.js","sourceRoot":"","sources":["../../src/routes/preview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview.test.d.ts","sourceRoot":"","sources":["../../src/routes/preview.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { isSocialScraper, SCRAPER_PATTERNS } from './preview.js';
|
|
3
|
+
/**
|
|
4
|
+
* The gate that decides whether a request gets the Open Graph preview page or a
|
|
5
|
+
* redirect. Both directions matter, and they fail differently:
|
|
6
|
+
*
|
|
7
|
+
* - a missed scraper renders the destination's metadata in the share card
|
|
8
|
+
* - a matched human gets a meta-refresh interstitial instead of their
|
|
9
|
+
* redirect, which is the worse of the two
|
|
10
|
+
*
|
|
11
|
+
* Kept in sync with the same suite in Cloud
|
|
12
|
+
* (`cloud/backend/src/lib/social-preview-hook.test.ts`). Cloud's hook registers
|
|
13
|
+
* ahead of these routes, so this list governs self-hosted deployments.
|
|
14
|
+
*/
|
|
15
|
+
/** Real server-side fetchers. Each must be served the preview page. */
|
|
16
|
+
const SCRAPERS = [
|
|
17
|
+
['Facebook', 'facebookexternalhit/1.1 (+http://www.facebook.com/externalhit_uatext.php)'],
|
|
18
|
+
['Facebot', 'Facebot/1.0'],
|
|
19
|
+
['X', 'Twitterbot/1.0'],
|
|
20
|
+
['LinkedIn', 'LinkedInBot/1.0 (compatible; Mozilla/5.0; +https://www.linkedin.com)'],
|
|
21
|
+
['Slack', 'Slackbot-LinkExpanding 1.0 (+https://api.slack.com/robots)'],
|
|
22
|
+
['Discord', 'Mozilla/5.0 (compatible; Discordbot/2.0; +https://discordapp.com)'],
|
|
23
|
+
['Telegram', 'TelegramBot (like TwitterBot)'],
|
|
24
|
+
['Pinterest', 'Mozilla/5.0 (compatible; PinterestBot/1.0; +http://www.pinterest.com/bot.html)'],
|
|
25
|
+
['Skype', 'SkypeUriPreview Preview/0.5'],
|
|
26
|
+
['Google', 'Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)'],
|
|
27
|
+
['Bing', 'Mozilla/5.0 (compatible; bingbot/2.0; +http://www.bing.com/bingbot.htm)'],
|
|
28
|
+
['Alexa', 'ia_archiver (+http://www.alexa.com/site/help/webmasters)'],
|
|
29
|
+
// Added by SIT-359, matching Cloud. All three were verified in production
|
|
30
|
+
// falling through to a 302 before the Cloud fix.
|
|
31
|
+
[
|
|
32
|
+
'Apple / iMessage',
|
|
33
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15 (Applebot/0.1; +http://www.apple.com/go/applebot)',
|
|
34
|
+
],
|
|
35
|
+
['Reddit', 'Mozilla/5.0 (compatible; redditbot/1.0; +https://www.reddit.com/robots.txt)'],
|
|
36
|
+
// CardyB masquerades as Chrome; "Cardyb" is the only non-browser token in it.
|
|
37
|
+
[
|
|
38
|
+
'Bluesky',
|
|
39
|
+
'Mozilla/5.0 AppleWebKit/537.36 (KHTML, like Gecko; compatible; Bluesky Cardyb/1.1; +mailto:support@bsky.app) Chrome/120.0.0.0 Safari/537.36',
|
|
40
|
+
],
|
|
41
|
+
['Mastodon', 'http.rb/5.1.1 (Mastodon/4.2.1; +https://mastodon.social/)'],
|
|
42
|
+
['VK', 'Mozilla/5.0 (compatible; vkShare; +http://vk.com/dev/Share)'],
|
|
43
|
+
['Embedly', 'Mozilla/5.0 (compatible; Embedly/0.2; +http://support.embed.ly/)'],
|
|
44
|
+
['Iframely', 'Iframely/1.3.1 (+https://iframely.com/docs/about)'],
|
|
45
|
+
['opengraph.xyz', 'Mozilla/5.0 (compatible; opengraph.xyz/1.0)'],
|
|
46
|
+
['MetaInspector', 'MetaInspector/5.11.0 (+https://github.com/jaimeiniesta/metainspector)'],
|
|
47
|
+
['PreviewBot', 'Mozilla/5.0 (compatible; PreviewBot/1.0)'],
|
|
48
|
+
['LinkPreview', 'Mozilla/5.0 (compatible; LinkPreview/1.0; +https://www.linkpreview.net)'],
|
|
49
|
+
];
|
|
50
|
+
/**
|
|
51
|
+
* Real people. Serving any of these the preview page is a regression.
|
|
52
|
+
*
|
|
53
|
+
* The in-app browser entries are the point of this block: each carries its
|
|
54
|
+
* platform's name, so a bare-name pattern would match a human. That is why
|
|
55
|
+
* those patterns are absent, and this is the guard that keeps someone from
|
|
56
|
+
* "helpfully" adding them back.
|
|
57
|
+
*/
|
|
58
|
+
const HUMANS = [
|
|
59
|
+
[
|
|
60
|
+
'Desktop Chrome',
|
|
61
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
62
|
+
],
|
|
63
|
+
[
|
|
64
|
+
'Desktop Firefox',
|
|
65
|
+
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:121.0) Gecko/20100101 Firefox/121.0',
|
|
66
|
+
],
|
|
67
|
+
[
|
|
68
|
+
'Mobile Safari',
|
|
69
|
+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1',
|
|
70
|
+
],
|
|
71
|
+
[
|
|
72
|
+
'Android Chrome',
|
|
73
|
+
'Mozilla/5.0 (Linux; Android 14; Pixel 8) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Mobile Safari/537.36',
|
|
74
|
+
],
|
|
75
|
+
[
|
|
76
|
+
'Instagram in-app browser',
|
|
77
|
+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Instagram 300.0.0.29.110',
|
|
78
|
+
],
|
|
79
|
+
[
|
|
80
|
+
'Threads in-app browser',
|
|
81
|
+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Barcelona 300.0.0.29.110 Threads',
|
|
82
|
+
],
|
|
83
|
+
[
|
|
84
|
+
'Snapchat in-app browser',
|
|
85
|
+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Snapchat/12.80.0.42',
|
|
86
|
+
],
|
|
87
|
+
[
|
|
88
|
+
'Tumblr in-app browser',
|
|
89
|
+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Tumblr/23.9.1.100',
|
|
90
|
+
],
|
|
91
|
+
[
|
|
92
|
+
'Reddit app (not the scraper)',
|
|
93
|
+
'Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 Reddit/Version 2024.1.0/Build 1234',
|
|
94
|
+
],
|
|
95
|
+
['Empty', ''],
|
|
96
|
+
];
|
|
97
|
+
describe('isSocialScraper', () => {
|
|
98
|
+
it.each(SCRAPERS)('serves the preview to %s', (_name, ua) => {
|
|
99
|
+
expect(isSocialScraper(ua)).toBe(true);
|
|
100
|
+
});
|
|
101
|
+
it.each(HUMANS)('redirects %s', (_name, ua) => {
|
|
102
|
+
expect(isSocialScraper(ua)).toBe(false);
|
|
103
|
+
});
|
|
104
|
+
it('does not carry a bare-name pattern for a platform with an in-app browser', () => {
|
|
105
|
+
const banned = ['Instagram', 'Threads', 'Snapchat', 'Tumblr'];
|
|
106
|
+
const sources = SCRAPER_PATTERNS.map((p) => p.source);
|
|
107
|
+
for (const name of banned) {
|
|
108
|
+
expect(sources, `${name} matches its own in-app browser UA`).not.toContain(name);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
//# sourceMappingURL=preview.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview.test.js","sourceRoot":"","sources":["../../src/routes/preview.test.ts"],"names":[],"mappings":"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"}
|