n8n-nodes-pinterest 0.1.2 → 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,13 +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
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;
16
21
  }
17
22
  function buildCookieHeader(sess, csrf) {
18
23
  return `_pinterest_sess=${sess}; csrftoken=${csrf};`;
@@ -133,6 +138,14 @@ class PinterestCookie {
133
138
  },
134
139
  ],
135
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
+ },
136
149
  // Board ops
137
150
  {
138
151
  displayName: 'Operation',
@@ -290,6 +303,7 @@ class PinterestCookie {
290
303
  const title = this.getNodeParameter('title', i, '');
291
304
  const description = this.getNodeParameter('description', i, '');
292
305
  const additionalFields = this.getNodeParameter('additionalFields', i, {});
306
+ const strictPluginMode = this.getNodeParameter('strictPluginMode', i, false);
293
307
  const binary = (_c = items[i].binary) === null || _c === void 0 ? void 0 : _c[binaryProperty];
294
308
  if (!binary)
295
309
  throw new Error(`Binary property "${binaryProperty}" is missing on item ${i}`);
@@ -301,7 +315,16 @@ class PinterestCookie {
301
315
  source_url: '/pin-builder/',
302
316
  data: '{"options":{"type":"pinimage"},"context":{}}',
303
317
  },
304
- 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
+ },
305
328
  proxy: proxy || undefined,
306
329
  }));
307
330
  const vip = JSON.parse(vipResp);
@@ -330,9 +353,23 @@ class PinterestCookie {
330
353
  headers: {
331
354
  Accept: '*/*',
332
355
  'Accept-Encoding': 'gzip',
333
- 'User-Agent': baseHeaders['User-Agent'],
334
- Origin: base,
335
- 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
+ : {}),
336
373
  },
337
374
  formData,
338
375
  proxy: proxy || undefined,
@@ -348,16 +385,20 @@ class PinterestCookie {
348
385
  const imageUrl = `https://i.pinimg.com/736x/${etag[0]}${etag[1]}/${etag[2]}${etag[3]}/${etag[4]}${etag[5]}/${etag}.jpg`;
349
386
  // Step 3: create pin
350
387
  const attachLink = (additionalFields.attachLink !== undefined) ? Boolean(additionalFields.attachLink) : true;
388
+ // Build payload
351
389
  const sendData = {
352
390
  options: {
353
- board_id: boardId,
391
+ board_id: String(boardId),
354
392
  field_set_key: 'create_success',
355
393
  skip_pin_create_log: true,
356
- description,
357
- alt_text: additionalFields.alt_text || '',
358
- ...(attachLink && additionalFields.link ? { link: additionalFields.link } : {}),
359
- title,
360
- 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),
361
402
  method: 'uploaded',
362
403
  upload_metric: { source: 'pinner_upload_standalone' },
363
404
  user_mention_tags: [],
@@ -374,7 +415,16 @@ class PinterestCookie {
374
415
  source_url: '/pin-builder/',
375
416
  data: JSON.stringify(payload),
376
417
  },
377
- headers: { ...baseHeaders, Cookie: cookieHeader, Accept: '*/*', 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
+ },
378
428
  proxy: proxy || undefined,
379
429
  // Get body even on 4xx
380
430
  simple: false,
@@ -393,17 +443,39 @@ class PinterestCookie {
393
443
  const msgDetail = ((_b = createdRes.error) === null || _b === void 0 ? void 0 : _b.message_detail) || '';
394
444
  return { ok: Boolean(id), id, msg: String(msg || msgDetail || ''), body };
395
445
  };
396
- // First attempt (with link if provided and attachLink=true)
397
- let result = await attemptCreate(sendData);
398
- // If blocked due to destination site, retry without link
399
- if (!result.ok && /doesn\'t allow you to save Pins|does not allow you to save Pins/i.test(result.msg)) {
400
- const withoutLink = JSON.parse(JSON.stringify(sendData));
401
- if (withoutLink.options)
402
- delete withoutLink.options.link;
403
- result = await attemptCreate(withoutLink);
446
+ let result = { ok: false };
447
+ if (strictPluginMode) {
448
+ result = await attemptCreate(sendData);
449
+ if (!result.ok)
450
+ throw new Error(result.msg || result.body || 'Create failed');
404
451
  }
405
- if (!result.ok) {
406
- throw new Error(result.msg || '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
+ }
407
479
  }
408
480
  returnData.push({ json: { id: result.id, link: `https://www.pinterest.com/pin/${result.id}` } });
409
481
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n8n-nodes-pinterest",
3
- "version": "0.1.2",
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",