n8n-nodes-pinterest 0.1.3 → 0.1.4

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.
@@ -6,15 +6,18 @@ function randomCsrf() {
6
6
  const seed = `${Date.now()}${Math.floor(Math.random() * 100000)}`;
7
7
  return Buffer.from(seed).toString('base64');
8
8
  }
9
- function buildCommonHeaders(csrf) {
10
- return {
9
+ function buildCommonHeaders(csrf, strict = false) {
10
+ const base = {
11
11
  'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:75.0) Gecko/20100101 Firefox/75.0',
12
12
  'x-pinterest-pws-handler': 'www/[username].js',
13
13
  'x-CSRFToken': csrf,
14
- 'Accept-Language': 'en-US,en;q=0.9',
15
- Accept: 'application/json, text/plain, */*',
16
- 'X-Requested-With': 'XMLHttpRequest',
17
14
  };
15
+ if (!strict) {
16
+ base['Accept-Language'] = 'en-US,en;q=0.9';
17
+ base['Accept'] = 'application/json, text/plain, */*';
18
+ base['X-Requested-With'] = 'XMLHttpRequest';
19
+ }
20
+ return base;
18
21
  }
19
22
  function buildCookieHeader(sess, csrf) {
20
23
  return `_pinterest_sess=${sess}; csrftoken=${csrf};`;
@@ -135,6 +138,14 @@ class PinterestCookie {
135
138
  },
136
139
  ],
137
140
  },
141
+ {
142
+ displayName: 'Strict Plugin Mode',
143
+ name: 'strictPluginMode',
144
+ type: 'boolean',
145
+ default: false,
146
+ description: 'Force exact request shape and headers used by the referenced plugin. Disables retries/fallbacks for 1:1 parity while debugging.',
147
+ displayOptions: { show: { resource: ['pin'], operation: ['create'] } },
148
+ },
138
149
  // Board ops
139
150
  {
140
151
  displayName: 'Operation',
@@ -292,6 +303,7 @@ class PinterestCookie {
292
303
  const title = this.getNodeParameter('title', i, '');
293
304
  const description = this.getNodeParameter('description', i, '');
294
305
  const additionalFields = this.getNodeParameter('additionalFields', i, {});
306
+ const strictPluginMode = this.getNodeParameter('strictPluginMode', i, false);
295
307
  const binary = (_c = items[i].binary) === null || _c === void 0 ? void 0 : _c[binaryProperty];
296
308
  if (!binary)
297
309
  throw new Error(`Binary property "${binaryProperty}" is missing on item ${i}`);
@@ -303,7 +315,16 @@ class PinterestCookie {
303
315
  source_url: '/pin-builder/',
304
316
  data: '{"options":{"type":"pinimage"},"context":{}}',
305
317
  },
306
- headers: { ...baseHeaders, Cookie: cookieHeader, Referer: `${base}/pin-builder/`, Origin: base },
318
+ headers: strictPluginMode
319
+ ? { ...buildCommonHeaders(csrf, true), Cookie: cookieHeader }
320
+ : {
321
+ ...baseHeaders,
322
+ Cookie: cookieHeader,
323
+ Referer: `${base}/pin-builder/`,
324
+ Origin: base,
325
+ 'X-Pinterest-Source-Url': '/pin-builder/',
326
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
327
+ },
307
328
  proxy: proxy || undefined,
308
329
  }));
309
330
  const vip = JSON.parse(vipResp);
@@ -332,9 +353,23 @@ class PinterestCookie {
332
353
  headers: {
333
354
  Accept: '*/*',
334
355
  'Accept-Encoding': 'gzip',
335
- 'User-Agent': baseHeaders['User-Agent'],
336
- Origin: base,
337
- Referer: `${base}/pin-builder/`,
356
+ 'User-Agent': strictPluginMode
357
+ ? 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.0.0 Safari/537.36'
358
+ : baseHeaders['User-Agent'],
359
+ Origin: strictPluginMode ? 'https://www.pinterest.com' : base,
360
+ Referer: strictPluginMode ? 'https://www.pinterest.com' : `${base}/pin-builder/`,
361
+ ...(strictPluginMode
362
+ ? {
363
+ 'sec-ch-ua': '".Not/A)Brand";v="99", "Google Chrome";v="103", "Chromium";v="103"',
364
+ 'sec-ch-ua-mobile': '?0',
365
+ 'sec-ch-ua-full': '?1',
366
+ 'sec-ch-ua-platform': '"Windows"',
367
+ 'Sec-Fetch-Dest': 'empty',
368
+ 'Sec-Fetch-Mode': 'cors',
369
+ 'Sec-Fetch-Site': 'same-origin',
370
+ Connection: 'keep-alive',
371
+ }
372
+ : {}),
338
373
  },
339
374
  formData,
340
375
  proxy: proxy || undefined,
@@ -350,19 +385,24 @@ class PinterestCookie {
350
385
  const imageUrl = `https://i.pinimg.com/736x/${etag[0]}${etag[1]}/${etag[2]}${etag[3]}/${etag[4]}${etag[5]}/${etag}.jpg`;
351
386
  // Step 3: create pin
352
387
  const attachLink = (additionalFields.attachLink !== undefined) ? Boolean(additionalFields.attachLink) : true;
388
+ // Build payload
353
389
  const sendData = {
354
390
  options: {
355
- board_id: boardId,
391
+ board_id: String(boardId),
356
392
  field_set_key: 'create_success',
357
393
  skip_pin_create_log: true,
358
- ...(description ? { description } : {}),
359
- ...(additionalFields.alt_text ? { alt_text: additionalFields.alt_text } : {}),
360
- ...(attachLink && additionalFields.link ? { link: additionalFields.link } : {}),
361
- ...(title ? { title } : {}),
362
- image_url: imageUrl,
394
+ description: String(description || ''),
395
+ alt_text: String(additionalFields.alt_text || ''),
396
+ // Strict mode: always include link key
397
+ link: String(additionalFields.link || ''),
398
+ // Non-strict: allow disabling link entirely
399
+ ...(strictPluginMode ? {} : (!attachLink ? { link: '' } : {})),
400
+ title: String(title || ''),
401
+ image_url: String(imageUrl),
363
402
  method: 'uploaded',
364
403
  upload_metric: { source: 'pinner_upload_standalone' },
365
- // omit user_mention_tags/no_fetch_context for stricter compatibility
404
+ user_mention_tags: [],
405
+ no_fetch_context_on_resource: false,
366
406
  },
367
407
  context: {},
368
408
  };
@@ -375,7 +415,16 @@ class PinterestCookie {
375
415
  source_url: '/pin-builder/',
376
416
  data: JSON.stringify(payload),
377
417
  },
378
- headers: { ...baseHeaders, Cookie: cookieHeader, Referer: `${base}/pin-builder/`, Origin: base },
418
+ headers: strictPluginMode
419
+ ? { ...buildCommonHeaders(csrf, true), Cookie: cookieHeader }
420
+ : {
421
+ ...baseHeaders,
422
+ Cookie: cookieHeader,
423
+ Referer: `${base}/pin-builder/`,
424
+ Origin: base,
425
+ 'X-Pinterest-Source-Url': '/pin-builder/',
426
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
427
+ },
379
428
  proxy: proxy || undefined,
380
429
  // Get body even on 4xx
381
430
  simple: false,
@@ -394,37 +443,39 @@ class PinterestCookie {
394
443
  const msgDetail = ((_b = createdRes.error) === null || _b === void 0 ? void 0 : _b.message_detail) || '';
395
444
  return { ok: Boolean(id), id, msg: String(msg || msgDetail || ''), body };
396
445
  };
397
- // Try strategies progressively to avoid generic "Invalid parameters"
398
- const strategies = [];
399
- // 1) As built
400
- strategies.push(JSON.parse(JSON.stringify(sendData)));
401
- // 2) Without link
402
- const s2 = JSON.parse(JSON.stringify(sendData));
403
- if (s2.options)
404
- delete s2.options.link;
405
- strategies.push(s2);
406
- // 3) Minimal fields only
407
- strategies.push({
408
- options: {
409
- board_id: boardId,
410
- field_set_key: 'create_success',
411
- image_url: imageUrl,
412
- method: 'uploaded',
413
- },
414
- context: {},
415
- });
416
446
  let result = { ok: false };
417
- for (const strat of strategies) {
418
- result = await attemptCreate(strat);
419
- if (result.ok)
420
- break;
421
- // If explicit site-block message, skip to minimal next
422
- if (/doesn\'t allow you to save Pins|does not allow you to save Pins/i.test(result.msg || ''))
423
- continue;
447
+ if (strictPluginMode) {
448
+ result = await attemptCreate(sendData);
449
+ if (!result.ok)
450
+ throw new Error(result.msg || result.body || 'Create failed');
424
451
  }
425
- if (!result.ok) {
426
- // surface full server message for troubleshooting
427
- throw new Error(result.msg || result.body || 'Create failed');
452
+ else {
453
+ // Try strategies progressively to avoid generic errors
454
+ const strategies = [];
455
+ strategies.push(JSON.parse(JSON.stringify(sendData)));
456
+ const s2 = JSON.parse(JSON.stringify(sendData));
457
+ if (s2.options)
458
+ delete s2.options.link;
459
+ strategies.push(s2);
460
+ strategies.push({
461
+ options: {
462
+ board_id: boardId,
463
+ field_set_key: 'create_success',
464
+ image_url: imageUrl,
465
+ method: 'uploaded',
466
+ },
467
+ context: {},
468
+ });
469
+ for (const strat of strategies) {
470
+ result = await attemptCreate(strat);
471
+ if (result.ok)
472
+ break;
473
+ if (/doesn\'t allow you to save Pins|does not allow you to save Pins/i.test(result.msg || ''))
474
+ continue;
475
+ }
476
+ if (!result.ok) {
477
+ throw new Error(result.msg || result.body || 'Create failed');
478
+ }
428
479
  }
429
480
  returnData.push({ json: { id: result.id, link: `https://www.pinterest.com/pin/${result.id}` } });
430
481
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-pinterest",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "description": "n8n community nodes for Pinterest v5 API (list boards, create pins)",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",