@paywalls-net/filter 1.2.4 → 1.3.1

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +130 -9
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.2.4",
6
+ "version": "1.3.1",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
package/src/index.js CHANGED
@@ -34,10 +34,7 @@ let runtime = detectRuntime();
34
34
  let fetchVersion = detectFetchVersion();
35
35
  const sdkUserAgent = `pw-filter-sdk/${sdk_version} (${runtime}; fetch/${fetchVersion})`;
36
36
 
37
- async function logAccess(cfg, request, access) {
38
- // Separate html from the status in the access object.
39
- const { response, ...status } = access;
40
-
37
+ function getAllHeaders(request) {
41
38
  // Get all headers as a plain object (name-value pairs)
42
39
  let headers = {};
43
40
  if (typeof request.headers.entries === "function") {
@@ -51,6 +48,80 @@ async function logAccess(cfg, request, access) {
51
48
  headers[key] = request.headers[key][0]?.value || "";
52
49
  }
53
50
  }
51
+ return headers;
52
+ }
53
+
54
+ /**
55
+ * Check if the request is for a VAI endpoint (vai.json or vai.js)
56
+ * @param {Request} request - The incoming request
57
+ * @param {string} vaiPath - The path prefix for VAI endpoints (default: '/pw')
58
+ * @returns {boolean} - True if this is a VAI endpoint request
59
+ */
60
+ function isVAIRequest(request, vaiPath = '/pw') {
61
+ try {
62
+ const url = new URL(request.url || `http://host${request.uri || ''}`);
63
+ const pathname = url.pathname;
64
+ return pathname === `${vaiPath}/vai.json` || pathname === `${vaiPath}/vai.js`;
65
+ } catch (err) {
66
+ return false;
67
+ }
68
+ }
69
+
70
+ /**
71
+ * Proxy VAI requests to the cloud-api service
72
+ * @param {Object} cfg - Configuration object with paywallsAPIHost and paywallsAPIKey
73
+ * @param {Request} request - The incoming request
74
+ * @returns {Promise<Response>} - The proxied response from cloud-api
75
+ */
76
+ async function proxyVAIRequest(cfg, request) {
77
+ try {
78
+ const url = new URL(request.url || `http://host${request.uri || ''}`);
79
+ const isJson = url.pathname.endsWith('/vai.json');
80
+ const cloudApiPath = isJson ? '/pw/vai.json' : '/pw/vai.js';
81
+
82
+ // Get all request headers
83
+ const headers = getAllHeaders(request);
84
+
85
+ // Build forwarding headers
86
+ const forwardHeaders = {
87
+ 'User-Agent': headers['user-agent'] || sdkUserAgent,
88
+ 'Authorization': `Bearer ${cfg.paywallsAPIKey}`
89
+ };
90
+
91
+ // Add forwarding headers if available
92
+ if (headers['x-forwarded-for']) {
93
+ forwardHeaders['X-Forwarded-For'] = headers['x-forwarded-for'];
94
+ } else if (headers['cf-connecting-ip']) {
95
+ forwardHeaders['X-Forwarded-For'] = headers['cf-connecting-ip'];
96
+ }
97
+
98
+ if (headers['host']) {
99
+ forwardHeaders['X-Original-Host'] = headers['host'];
100
+ }
101
+
102
+ // Forward request to cloud-api
103
+ const response = await fetch(`${cfg.paywallsAPIHost}${cloudApiPath}`, {
104
+ method: 'GET',
105
+ headers: forwardHeaders
106
+ });
107
+
108
+ if (!response.ok) {
109
+ console.error(`VAI proxy error: ${response.status} ${response.statusText}`);
110
+ }
111
+
112
+ return response;
113
+ } catch (err) {
114
+ console.error(`Error proxying VAI request: ${err.message}`);
115
+ return new Response('Internal Server Error', { status: 500 });
116
+ }
117
+ }
118
+
119
+ async function logAccess(cfg, request, access) {
120
+ // Separate html from the status in the access object.
121
+ const { response, ...status } = access;
122
+
123
+ // Get all headers as a plain object (name-value pairs)
124
+ let headers = getAllHeaders(request);
54
125
 
55
126
  const url = new URL(request.url);
56
127
  let body = {
@@ -107,14 +178,15 @@ async function checkAgentStatus(cfg, request) {
107
178
  response: { code: 401, html: "Unauthorized access." }
108
179
  };
109
180
  }
110
-
181
+ let headers = getAllHeaders(request);
111
182
  const agentInfo = await classifyUserAgent(cfg, userAgent);
112
183
 
113
184
  const body = JSON.stringify({
114
185
  account_id: cfg.paywallsPublisherId,
115
186
  operator: agentInfo.operator,
116
187
  agent: agentInfo.agent,
117
- token: token
188
+ token: token,
189
+ headers: headers
118
190
  });
119
191
 
120
192
  const response = await fetch(`${cfg.paywallsAPIHost}/api/filter/agents/auth`, {
@@ -256,8 +328,15 @@ async function cloudflare(config = null) {
256
328
  const paywallsConfig = {
257
329
  paywallsAPIHost: env.PAYWALLS_CLOUD_API_HOST || PAYWALLS_CLOUD_API_HOST,
258
330
  paywallsAPIKey: env.PAYWALLS_CLOUD_API_KEY,
259
- paywallsPublisherId: env.PAYWALLS_PUBLISHER_ID
331
+ paywallsPublisherId: env.PAYWALLS_PUBLISHER_ID,
332
+ vaiPath: env.PAYWALLS_VAI_PATH || '/pw'
260
333
  };
334
+
335
+ // Check if this is a VAI endpoint request and proxy it
336
+ if (isVAIRequest(request, paywallsConfig.vaiPath)) {
337
+ return await proxyVAIRequest(paywallsConfig, request);
338
+ }
339
+
261
340
  await loadAgentPatterns(paywallsConfig);
262
341
 
263
342
  if (await isRecognizedBot(paywallsConfig, request)) {
@@ -281,8 +360,14 @@ async function fastly() {
281
360
  const paywallsConfig = {
282
361
  paywallsAPIHost: config.get('PAYWALLS_CLOUD_API_HOST') || PAYWALLS_CLOUD_API_HOST,
283
362
  paywallsAPIKey: config.get('PAYWALLS_API_KEY'),
284
- paywallsPublisherId: config.get('PAYWALLS_PUBLISHER_ID')
363
+ paywallsPublisherId: config.get('PAYWALLS_PUBLISHER_ID'),
364
+ vaiPath: config.get('PAYWALLS_VAI_PATH') || '/pw'
285
365
  };
366
+
367
+ // Check if this is a VAI endpoint request and proxy it
368
+ if (isVAIRequest(request, paywallsConfig.vaiPath)) {
369
+ return await proxyVAIRequest(paywallsConfig, request);
370
+ }
286
371
 
287
372
  await loadAgentPatterns(paywallsConfig);
288
373
 
@@ -297,6 +382,34 @@ async function fastly() {
297
382
  }
298
383
  };
299
384
  }
385
+ /**
386
+ * Convert a standard Response to CloudFront format
387
+ * @param {Response} response - Standard fetch Response object
388
+ * @returns {Promise<Object>} - CloudFront-formatted response
389
+ */
390
+ async function responseToCloudFront(response) {
391
+ const headers = {};
392
+
393
+ // Convert response headers to CloudFront format
394
+ for (const [key, value] of response.headers.entries()) {
395
+ headers[key.toLowerCase()] = [
396
+ {
397
+ key: key,
398
+ value: value
399
+ }
400
+ ];
401
+ }
402
+
403
+ const body = await response.text();
404
+
405
+ return {
406
+ status: response.status,
407
+ statusDescription: response.statusText || 'OK',
408
+ headers: headers,
409
+ body: body
410
+ };
411
+ }
412
+
300
413
  /**
301
414
  * Adapt to CloudFront format
302
415
  * Lambda@Edge events see https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-event-structure.html#lambda-event-structure-request
@@ -329,13 +442,21 @@ async function cloudfront(config) {
329
442
  const paywallsConfig = {
330
443
  paywallsAPIHost: config.PAYWALLS_CLOUD_API_HOST || PAYWALLS_CLOUD_API_HOST,
331
444
  paywallsAPIKey: config.PAYWALLS_API_KEY,
332
- paywallsPublisherId: config.PAYWALLS_PUBLISHER_ID
445
+ paywallsPublisherId: config.PAYWALLS_PUBLISHER_ID,
446
+ vaiPath: config.PAYWALLS_VAI_PATH || '/pw'
333
447
  };
334
448
  await loadAgentPatterns(paywallsConfig);
335
449
 
336
450
  return async function handle(event, ctx) {
337
451
  let request = event.Records[0].cf.request;
338
452
  request = requestShim(request);
453
+
454
+ // Check if this is a VAI endpoint request and proxy it
455
+ if (isVAIRequest(request, paywallsConfig.vaiPath)) {
456
+ const response = await proxyVAIRequest(paywallsConfig, request);
457
+ return await responseToCloudFront(response);
458
+ }
459
+
339
460
  if (await isRecognizedBot(paywallsConfig, request)) {
340
461
  const authz = await checkAgentStatus(paywallsConfig, request);
341
462