@sockethub/platform-feeds 4.0.0-alpha.11 → 4.0.0-alpha.13
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/README.md +148 -10
- package/dist/index.d.ts +107 -0
- package/dist/index.js +37496 -50
- package/dist/index.js.map +263 -5
- package/dist/schema.d.ts +162 -0
- package/{src/types.ts → dist/types.d.ts} +10 -25
- package/package.json +8 -7
- package/src/index.test.data.ts +0 -299
- package/src/index.test.ts +0 -143
- package/src/index.ts +0 -288
- package/src/schema.ts +0 -22
package/README.md
CHANGED
|
@@ -8,7 +8,7 @@ This platform fetches RSS and Atom feeds from URLs and converts feed entries int
|
|
|
8
8
|
ActivityStreams objects. It handles various feed formats and provides structured data for
|
|
9
9
|
web applications to consume feed content.
|
|
10
10
|
|
|
11
|
-
## Implemented Verbs (
|
|
11
|
+
## Implemented Verbs (`type`)
|
|
12
12
|
|
|
13
13
|
* **fetch** - Retrieve and parse an RSS or Atom feed
|
|
14
14
|
|
|
@@ -18,33 +18,82 @@ web applications to consume feed content.
|
|
|
18
18
|
|
|
19
19
|
```json
|
|
20
20
|
{
|
|
21
|
-
"
|
|
22
|
-
"context":
|
|
21
|
+
"type": "fetch",
|
|
22
|
+
"@context": [
|
|
23
|
+
"https://www.w3.org/ns/activitystreams",
|
|
24
|
+
"https://sockethub.org/ns/context/v1.jsonld",
|
|
25
|
+
"https://sockethub.org/ns/context/platform/feeds/v1.jsonld"
|
|
26
|
+
],
|
|
23
27
|
"actor": {
|
|
24
|
-
"
|
|
28
|
+
"id": "https://example.com/feed.xml",
|
|
29
|
+
"type": "feed"
|
|
25
30
|
}
|
|
26
31
|
}
|
|
27
32
|
```
|
|
28
33
|
|
|
34
|
+
#### Optional Fetch Parameters
|
|
35
|
+
|
|
36
|
+
A fetch request may carry an `object` with filtering parameters. The `object`
|
|
37
|
+
is strictly validated: only the parameters below are accepted, anything else
|
|
38
|
+
is rejected.
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"type": "fetch",
|
|
43
|
+
"@context": [
|
|
44
|
+
"https://www.w3.org/ns/activitystreams",
|
|
45
|
+
"https://sockethub.org/ns/context/v1.jsonld",
|
|
46
|
+
"https://sockethub.org/ns/context/platform/feeds/v1.jsonld"
|
|
47
|
+
],
|
|
48
|
+
"actor": {
|
|
49
|
+
"id": "https://example.com/feed.xml",
|
|
50
|
+
"type": "feed"
|
|
51
|
+
},
|
|
52
|
+
"object": {
|
|
53
|
+
"since": "2024-01-01T00:00:00.000Z",
|
|
54
|
+
"limit": 10
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
* **since** (RFC3339 `date-time` string) — only return entries published at or
|
|
60
|
+
after this instant. Entries without a parseable publication date are
|
|
61
|
+
excluded when `since` is set, since they cannot be confirmed as recent
|
|
62
|
+
enough.
|
|
63
|
+
* **limit** (integer, minimum `1`) — return at most this many entries. Applied
|
|
64
|
+
after `since`, preserving feed order.
|
|
65
|
+
|
|
29
66
|
### Response Format
|
|
30
67
|
|
|
31
68
|
Returns an ActivityStreams Collection with feed entries:
|
|
32
69
|
|
|
33
70
|
```json
|
|
34
71
|
{
|
|
35
|
-
"
|
|
36
|
-
"context":
|
|
72
|
+
"type": "collection",
|
|
73
|
+
"@context": [
|
|
74
|
+
"https://www.w3.org/ns/activitystreams",
|
|
75
|
+
"https://sockethub.org/ns/context/v1.jsonld",
|
|
76
|
+
"https://sockethub.org/ns/context/platform/feeds/v1.jsonld"
|
|
77
|
+
],
|
|
78
|
+
"summary": "Example Blog",
|
|
37
79
|
"totalItems": 10,
|
|
38
80
|
"items": [
|
|
39
81
|
{
|
|
40
|
-
"
|
|
82
|
+
"type": "post",
|
|
83
|
+
"@context": [
|
|
84
|
+
"https://www.w3.org/ns/activitystreams",
|
|
85
|
+
"https://sockethub.org/ns/context/v1.jsonld",
|
|
86
|
+
"https://sockethub.org/ns/context/platform/feeds/v1.jsonld"
|
|
87
|
+
],
|
|
41
88
|
"actor": {
|
|
42
|
-
"
|
|
89
|
+
"id": "https://example.com/feed.xml",
|
|
90
|
+
"type": "feed",
|
|
43
91
|
"name": "Example Blog"
|
|
44
92
|
},
|
|
45
93
|
"object": {
|
|
46
|
-
"
|
|
47
|
-
"
|
|
94
|
+
"id": "https://example.com/post/1",
|
|
95
|
+
"type": "note",
|
|
96
|
+
"title": "Blog Post Title",
|
|
48
97
|
"content": "Post content...",
|
|
49
98
|
"url": "https://example.com/post/1",
|
|
50
99
|
"published": "2023-01-01T12:00:00Z"
|
|
@@ -54,6 +103,95 @@ Returns an ActivityStreams Collection with feed entries:
|
|
|
54
103
|
}
|
|
55
104
|
```
|
|
56
105
|
|
|
106
|
+
### Detailed Response Example
|
|
107
|
+
|
|
108
|
+
```json
|
|
109
|
+
{
|
|
110
|
+
"@context": [
|
|
111
|
+
"https://www.w3.org/ns/activitystreams",
|
|
112
|
+
"https://sockethub.org/ns/context/v1.jsonld",
|
|
113
|
+
"https://sockethub.org/ns/context/platform/feeds/v1.jsonld"
|
|
114
|
+
],
|
|
115
|
+
"type": "collection",
|
|
116
|
+
"summary": "Best Feed Inc.",
|
|
117
|
+
"totalItems": 10,
|
|
118
|
+
"items": [
|
|
119
|
+
{
|
|
120
|
+
"@context": [
|
|
121
|
+
"https://www.w3.org/ns/activitystreams",
|
|
122
|
+
"https://sockethub.org/ns/context/v1.jsonld",
|
|
123
|
+
"https://sockethub.org/ns/context/platform/feeds/v1.jsonld"
|
|
124
|
+
],
|
|
125
|
+
"type": "post",
|
|
126
|
+
"actor": {
|
|
127
|
+
"type": "feed",
|
|
128
|
+
"name": "Best Feed Inc.",
|
|
129
|
+
"id": "http://blog.example.com/rss",
|
|
130
|
+
"description": "Where the best feed comes to be the best",
|
|
131
|
+
"image": "http://blog.example.com/images/bestfeed.jpg",
|
|
132
|
+
"link": "http://blog.example.com",
|
|
133
|
+
"categories": ["best", "feed", "animals"],
|
|
134
|
+
"language": "en",
|
|
135
|
+
"author": "John Doe"
|
|
136
|
+
},
|
|
137
|
+
"object": {
|
|
138
|
+
"id": "http://blog.example.com/articles/about-stuff",
|
|
139
|
+
"type": "note",
|
|
140
|
+
"title": "About stuff...",
|
|
141
|
+
"url": "http://blog.example.com/articles/about-stuff",
|
|
142
|
+
"published": "2013-05-28T12:00:00.000Z",
|
|
143
|
+
"datenum": 1369742400000,
|
|
144
|
+
"brief": "Brief synopsis of stuff...",
|
|
145
|
+
"content": "Once upon a time...",
|
|
146
|
+
"contentType": "text"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
## Request Hardening (SSRF Guard)
|
|
154
|
+
|
|
155
|
+
Because the server fetches whatever URL a client supplies as `actor.id`, feed
|
|
156
|
+
requests are hardened against server-side request forgery (SSRF) and resource
|
|
157
|
+
exhaustion, using the shared guarded fetch dispatcher from `@sockethub/util`:
|
|
158
|
+
|
|
159
|
+
* Only `http:` and `https:` URLs are accepted.
|
|
160
|
+
* The connection is refused if the destination resolves to a loopback, private,
|
|
161
|
+
link-local, carrier-grade NAT, or otherwise non-public address — validated at
|
|
162
|
+
the connection layer, so it also covers every redirect hop (no
|
|
163
|
+
check-then-fetch rebinding gap). IPv4-mapped, IPv4-compatible, and NAT64 IPv6
|
|
164
|
+
spellings of those ranges are normalized and blocked as well.
|
|
165
|
+
* Response bodies are capped (a missing or lying `Content-Length` does not
|
|
166
|
+
bypass the cap).
|
|
167
|
+
* Non-2xx responses fail the job with a descriptive error.
|
|
168
|
+
|
|
169
|
+
### `allowPrivateAddresses`
|
|
170
|
+
|
|
171
|
+
Set in the Sockethub config file under this platform's `packageConfig` entry:
|
|
172
|
+
|
|
173
|
+
```json
|
|
174
|
+
{
|
|
175
|
+
"packageConfig": {
|
|
176
|
+
"@sockethub/platform-feeds": {
|
|
177
|
+
"allowPrivateAddresses": true
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
This disables **only** the private/loopback destination checks. Scheme
|
|
184
|
+
validation, the redirect limit, and the response size cap always remain in
|
|
185
|
+
effect.
|
|
186
|
+
|
|
187
|
+
This is an escape hatch for development and test harnesses that serve feed
|
|
188
|
+
fixtures from `localhost`, or for self-hosted deployments that intentionally
|
|
189
|
+
fetch intranet feeds. **Do not enable it on a server that accepts requests
|
|
190
|
+
from untrusted clients** — it allows any client to make the server issue HTTP
|
|
191
|
+
requests to internal services, including cloud metadata endpoints such as
|
|
192
|
+
`169.254.169.254`. It defaults to off; the full guard is active unless
|
|
193
|
+
`allowPrivateAddresses` is explicitly `true`.
|
|
194
|
+
|
|
57
195
|
## Supported Feed Formats
|
|
58
196
|
|
|
59
197
|
* **RSS 2.0**: Standard RSS feeds
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* This is a platform for Sockethub implementing Atom/RSS fetching functionality.
|
|
3
|
+
*
|
|
4
|
+
* Developed by Nick Jennings (https://github.com/silverbucket)
|
|
5
|
+
*
|
|
6
|
+
* Sockethub is licensed under the LGPLv3.
|
|
7
|
+
* See the LICENSE file for details.
|
|
8
|
+
*
|
|
9
|
+
* The latest version of this module can be found here:
|
|
10
|
+
* git://github.com/sockethub/sockethub.git
|
|
11
|
+
*
|
|
12
|
+
* For more information about Sockethub visit http://sockethub.org/.
|
|
13
|
+
*
|
|
14
|
+
* This program is distributed in the hope that it will be useful,
|
|
15
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
17
|
+
*/
|
|
18
|
+
import type { ActivityStream, PlatformCallback, PlatformConfig, PlatformInterface, PlatformSchemaStruct, PlatformSession } from "@sockethub/schemas";
|
|
19
|
+
import { type Episode } from "podparse";
|
|
20
|
+
import { type PlatformFeedsActivityObject, type PlatformFeedsActivityStream } from "./types.js";
|
|
21
|
+
/**
|
|
22
|
+
* Class: Feeds
|
|
23
|
+
*
|
|
24
|
+
* Handles all actions related to fetching feeds.
|
|
25
|
+
*
|
|
26
|
+
* Current supported feed types:
|
|
27
|
+
*
|
|
28
|
+
* - RSS (1 & 2)
|
|
29
|
+
*
|
|
30
|
+
* - Atom
|
|
31
|
+
*
|
|
32
|
+
* Uses the `podparse` module as a base tool fetching feeds.
|
|
33
|
+
*
|
|
34
|
+
* https://github.com/Tombarr/podcast-feed-parser
|
|
35
|
+
*
|
|
36
|
+
*/
|
|
37
|
+
export default class Feeds implements PlatformInterface {
|
|
38
|
+
id: string;
|
|
39
|
+
private readonly log;
|
|
40
|
+
private dispatcher?;
|
|
41
|
+
config: PlatformConfig;
|
|
42
|
+
/**
|
|
43
|
+
* @constructor
|
|
44
|
+
* @param session - a unique session object for this platform instance
|
|
45
|
+
*/
|
|
46
|
+
constructor(session: PlatformSession);
|
|
47
|
+
get schema(): PlatformSchemaStruct;
|
|
48
|
+
/**
|
|
49
|
+
* Stateless platforms are always ready to handle jobs.
|
|
50
|
+
*/
|
|
51
|
+
isInitialized(): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Fetch feeds from specified source. Upon completion, it will send back a
|
|
54
|
+
* response to the original request with a complete ActivityStreams Collection
|
|
55
|
+
* containing all feed items and metadata.
|
|
56
|
+
*
|
|
57
|
+
* The request may carry an optional `object` with filtering parameters:
|
|
58
|
+
* `since` (RFC3339 date-time; only entries published at or after this
|
|
59
|
+
* instant, undated entries excluded) and `limit` (integer >= 1; cap on
|
|
60
|
+
* returned entries, applied after `since`, feed order preserved).
|
|
61
|
+
*
|
|
62
|
+
* @param job - Activity streams object containing job data with actor.id as feed URL
|
|
63
|
+
* @param done - Callback function that receives (error, ASCollection)
|
|
64
|
+
* See the package README for canonical request and response payload
|
|
65
|
+
* examples.
|
|
66
|
+
*/
|
|
67
|
+
fetch(job: ActivityStream, done: PlatformCallback): void;
|
|
68
|
+
/**
|
|
69
|
+
* Cleanup method called when platform instance is being shut down.
|
|
70
|
+
*
|
|
71
|
+
* @param done - Callback function to signal completion
|
|
72
|
+
*/
|
|
73
|
+
cleanup(done: PlatformCallback): void;
|
|
74
|
+
/**
|
|
75
|
+
* The SSRF-guarded undici dispatcher, created once per instance (its
|
|
76
|
+
* `allowPrivateAddresses` setting comes from packageConfig, fixed before the
|
|
77
|
+
* first job) and reused so connections/timers are pooled.
|
|
78
|
+
*/
|
|
79
|
+
private getDispatcher;
|
|
80
|
+
private makeRequest;
|
|
81
|
+
private fetchFeed;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Optional parameters a client may attach to a `fetch` request `object`.
|
|
85
|
+
* Mirrors the strict `messages.object` schema; both must stay in sync.
|
|
86
|
+
*/
|
|
87
|
+
export interface FeedFetchParams {
|
|
88
|
+
since?: string;
|
|
89
|
+
limit?: number;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Extract the supported fetch parameters from a request `object`. Inbound
|
|
93
|
+
* messages are already validated against the strict `messages` schema on the
|
|
94
|
+
* server, so this only needs to read the recognized fields defensively (the
|
|
95
|
+
* platform may also be called directly, e.g. in tests).
|
|
96
|
+
*/
|
|
97
|
+
export declare function extractFetchParams(object: unknown): FeedFetchParams;
|
|
98
|
+
/**
|
|
99
|
+
* Apply the `since` and `limit` fetch parameters to built feed entries.
|
|
100
|
+
*
|
|
101
|
+
* - `since`: drop entries published before the given instant. Entries without a
|
|
102
|
+
* parseable date (`datenum === 0`) are excluded when `since` is set, since we
|
|
103
|
+
* cannot confirm they are recent enough. An unparseable `since` is ignored.
|
|
104
|
+
* - `limit`: return at most `limit` entries, preserving feed order.
|
|
105
|
+
*/
|
|
106
|
+
export declare function applyFetchFilters(articles: Array<PlatformFeedsActivityStream>, params: FeedFetchParams): Array<PlatformFeedsActivityStream>;
|
|
107
|
+
export declare function buildFeedItem(item: Episode, channelUrl: string): PlatformFeedsActivityObject;
|