@paywalls-net/filter 1.1.1 → 1.2.0

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 CHANGED
@@ -9,9 +9,9 @@ npm install @paywalls-net/filter
9
9
  ```
10
10
 
11
11
  ## Environment Variables
12
- - `PAYWALLS_PUBLISHER_ID`: The unique identifier for the publisher using Paywalls.net services.
13
- - `PAYWALLS_CLOUD_API_HOST`: The host for the Paywalls.net API. eg `https://cloud-api.paywalls.net`.
14
- - `PAYWALLS_CLOUD_API_KEY`: The API key for accessing Paywalls.net services. NOTE: This key should be treated like a password and kept secret and stored in a secure secrets vault or environment variable.
12
+ - `PAYWALLS_PUBLISHER_ID`: The unique identifier for the publisher using paywalls.net services.
13
+ - `PAYWALLS_CLOUD_API_HOST`: The host for the paywalls.net API. eg `https://cloud-api.paywalls.net`.
14
+ - `PAYWALLS_CLOUD_API_KEY`: The API key for accessing paywalls.net services. NOTE: This key should be treated like a password and kept secret and stored in a secure secrets vault or environment variable.
15
15
 
16
16
  ## Usage
17
17
  ```javascript
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Client SDK for integrating paywalls.net bot filtering and authorization services into your server or CDN.",
4
4
  "author": "paywalls.net",
5
5
  "license": "MIT",
6
- "version": "1.1.1",
6
+ "version": "1.2.0",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
package/src/index.js CHANGED
@@ -140,7 +140,7 @@ async function isRecognizedBot(cfg, request) {
140
140
  }
141
141
 
142
142
 
143
- function sendResponse(authz) {
143
+ function setHeaders(authz) {
144
144
  let headers = {
145
145
  "Content-Type": "text/html",
146
146
  };
@@ -157,6 +157,38 @@ function sendResponse(authz) {
157
157
  });
158
158
  }
159
159
 
160
+ function setCloudFrontHeaders(authz) {
161
+ const headers = {};
162
+
163
+ if (authz.response?.headers) {
164
+ for (const [key, value] of Object.entries(authz.response.headers)) {
165
+ headers[key.toLowerCase()] = [
166
+ {
167
+ key: key,
168
+ value: value
169
+ }
170
+ ];
171
+ }
172
+ }
173
+
174
+ // Add default Content-Type header if not already set
175
+ if (!headers["content-type"]) {
176
+ headers["content-type"] = [
177
+ {
178
+ key: "Content-Type",
179
+ value: "text/html"
180
+ }
181
+ ];
182
+ }
183
+
184
+ return {
185
+ status: authz.response?.code || 402,
186
+ statusDescription: authz.response?.code === 402 ? "Payment Required" : "Error",
187
+ headers: headers,
188
+ body: authz.response?.html || "Payment required."
189
+ };
190
+ }
191
+
160
192
  /**
161
193
  * Detect AI Bot and authorize it using paywalls.net.
162
194
  * @param {Request} request
@@ -174,19 +206,17 @@ async function cloudflare(config = null) {
174
206
  };
175
207
  await loadAgentPatterns(paywallsConfig);
176
208
 
177
- if (isRecognizedBot(paywallsConfig, request)) {
209
+ if (await isRecognizedBot(paywallsConfig, request)) {
178
210
  const authz = await checkAgentStatus(paywallsConfig, request);
179
211
 
180
212
  ctx.waitUntil(logAccess(paywallsConfig, request, authz));
181
213
 
182
214
  if (authz.access === 'deny') {
183
- return sendResponse(authz);
215
+ return setHeaders(authz);
184
216
  } else {
185
217
  console.log("Bot-like request allowed. Proceeding to origin/CDN.");
186
218
  }
187
219
  }
188
-
189
- return fetch(request); // Proceed to origin/CDN
190
220
  };
191
221
  }
192
222
 
@@ -200,17 +230,37 @@ async function fastly(config) {
200
230
  await loadAgentPatterns(paywallsConfig);
201
231
 
202
232
  return async function handle(request) {
203
- if (isRecognizedBot(paywallsConfig,request)) {
233
+ if (await isRecognizedBot(paywallsConfig, request)) {
234
+ const authz = await checkAgentStatus(paywallsConfig, request);
235
+
236
+ await logAccess(paywallsConfig, request, authz);
237
+
238
+ if (authz.access === 'deny') {
239
+ return setHeaders(authz);
240
+ }
241
+ }
242
+ };
243
+ }
244
+
245
+ async function cloudfront(config) {
246
+ const paywallsConfig = {
247
+ paywallsAPIHost: config.get('PAYWALLS_CLOUD_API_HOST'),
248
+ paywallsAPIKey: config.get('PAYWALLS_API_KEY'),
249
+ paywallsPublisherId: config.get('PAYWALLS_PUBLISHER_ID')
250
+ };
251
+ await loadAgentPatterns(paywallsConfig);
252
+
253
+ return async function handle(request) {
254
+ if (await isRecognizedBot(paywallsConfig, request)) {
204
255
  const authz = await checkAgentStatus(paywallsConfig, request);
205
256
 
206
257
  await logAccess(paywallsConfig, request, authz);
207
258
 
208
259
  if (authz.access === 'deny') {
209
- return sendResponse(authz);
260
+ return setCloudFrontHeaders(authz);
210
261
  }
211
262
  }
212
263
 
213
- return fetch(request, { backend: 'origin' });
214
264
  };
215
265
  }
216
266
 
@@ -228,6 +278,8 @@ export async function init(cdn, config = {}) {
228
278
  return await cloudflare(config);
229
279
  case 'fastly':
230
280
  return await fastly(config);
281
+ case 'cloudfront':
282
+ return await cloudfront(config);
231
283
  default:
232
284
  throw new Error(`Unsupported CDN: ${cdn}`);
233
285
  }
@@ -27,15 +27,22 @@ export async function loadAgentPatterns(cfg) {
27
27
  });
28
28
 
29
29
  if (!response.ok) {
30
- throw new Error(`Failed to fetch user agent patterns: ${response.status} ${response.statusText}`);
30
+ throw new Error(`Failed to fetch agent patterns: ${response.status} ${response.statusText}`);
31
31
  }
32
32
 
33
- cachedUserAgentPatterns = await response.json();
33
+ const serializedPatterns = await response.json();
34
+
35
+ // Deserialize RegExp strings back into RegExp objects
36
+ cachedUserAgentPatterns = serializedPatterns.map((pattern) => ({
37
+ ...pattern,
38
+ patterns: pattern.patterns.map((regexString) => new RegExp(regexString.slice(1, -1))) // Remove leading and trailing slashes
39
+ }));
40
+
34
41
  cacheTimestamp = now;
35
42
  return cachedUserAgentPatterns;
36
43
  } catch (error) {
37
- console.error('Failed to fetch user agent patterns:', error);
38
- throw new Error('Could not load user agent patterns');
44
+ console.error('Error loading agent patterns:', error);
45
+ throw error;
39
46
  }
40
47
  }
41
48
 
@@ -56,7 +63,7 @@ export async function classifyUserAgent(cfg, userAgent) {
56
63
  for (const config of userAgentPatterns) {
57
64
  if (!config.patterns) continue;
58
65
  for (const pattern of config.patterns) {
59
- if (new RegExp(pattern).test(userAgent)) {
66
+ if (new RegExp(pattern).test(userAgent)) {
60
67
  return {
61
68
  operator: config.operator,
62
69
  agent: config.agent || browser,