abmp-npm 1.1.98 → 1.1.100

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.
@@ -11,7 +11,7 @@ const elevatedCreateContact = auth.elevate(contacts.createContact);
11
11
  * @param {boolean} allowDuplicates - Allow duplicates if contact with same email already exists, will be true only when handling existing members, after that should be removed
12
12
  * @returns {Promise<Object>} - Contact data
13
13
  */
14
- async function createContact(contactData, allowDuplicates = false) {
14
+ async function createSiteContact(contactData, allowDuplicates = false) {
15
15
  if (!contactData || !(contactData.contactFormEmail || contactData.email)) {
16
16
  throw new Error('Contact data is required');
17
17
  }
@@ -22,10 +22,10 @@ async function createContact(contactData, allowDuplicates = false) {
22
22
  first: contactData.firstName,
23
23
  last: contactData.lastName,
24
24
  },
25
- emails: [
26
- { items: [{ email: contactData.contactFormEmail || contactData.email, primary: true }] },
27
- ],
28
- phones: [{ items: phones.map(phone => ({ phone })) }],
25
+ emails: {
26
+ items: [{ email: contactData.contactFormEmail || contactData.email, primary: true }],
27
+ },
28
+ phones: { items: phones.map(phone => ({ phone })) },
29
29
  };
30
30
  const createContactResponse = await elevatedCreateContact(contactInfo, { allowDuplicates });
31
31
  return createContactResponse.contact._id;
@@ -157,5 +157,5 @@ const updateMemberContactInfo = async (data, existingMemberData) => {
157
157
 
158
158
  module.exports = {
159
159
  updateMemberContactInfo,
160
- createContact,
160
+ createSiteContact,
161
161
  };
@@ -1,3 +1,5 @@
1
+ const { COLLECTIONS } = require('../public/consts');
2
+
1
3
  const { ensureUniqueUrlsInBatch } = require('./daily-pull/bulk-process-methods');
2
4
  const { wixData } = require('./elevated-modules');
3
5
  const { bulkSaveMembers } = require('./members-data-methods');
@@ -15,4 +17,14 @@ async function deduplicateURls(collectionName, duplicateUrlsList) {
15
17
  return await bulkSaveMembers(membersWithUniqueUrls, collectionName);
16
18
  }
17
19
 
18
- module.exports = { deduplicateURls };
20
+ async function copyContactIdToWixMemberId() {
21
+ const query = wixData.query(COLLECTIONS.MEMBERS_DATA).isNotEmpty('contactId');
22
+ const members = await queryAllItems(query);
23
+ const updatedMembers = members.map(member => ({
24
+ ...member,
25
+ wixMemberId: member.contactId,
26
+ }));
27
+ return await bulkSaveMembers(updatedMembers, COLLECTIONS.MEMBERS_DATA);
28
+ }
29
+
30
+ module.exports = { deduplicateURls, copyContactIdToWixMemberId };
@@ -5,7 +5,7 @@ const { decode } = require('jwt-js-decode');
5
5
  const { CONFIG_KEYS, SSO_TOKEN_AUTH_API_URL } = require('../consts');
6
6
  const { MEMBER_ACTIONS } = require('../daily-pull/consts');
7
7
  const { getCurrentMember } = require('../members-area-methods');
8
- const { getMemberByContactId, prepareMemberForSSOLogin } = require('../members-data-methods');
8
+ const { getCMSMemberByWixMemberId, prepareMemberForSSOLogin } = require('../members-data-methods');
9
9
  const {
10
10
  formatDateToMonthYear,
11
11
  getAddressDisplayOptions,
@@ -37,12 +37,12 @@ async function validateMemberToken(memberIdInput) {
37
37
  }
38
38
 
39
39
  const [dbMember, siteConfigs] = await Promise.all([
40
- getMemberByContactId(member._id),
40
+ getCMSMemberByWixMemberId(member._id),
41
41
  getSiteConfigs(),
42
42
  ]);
43
43
  const siteAssociation = siteConfigs[CONFIG_KEYS.SITE_ASSOCIATION];
44
44
  const membersExternalPortalUrl = siteConfigs[CONFIG_KEYS.MEMBERS_EXTERNAL_PORTAL_URL];
45
- console.log('dbMember by contact id is:', dbMember);
45
+ console.log('dbMember by wix member id is:', dbMember);
46
46
  console.log('member._id', member._id);
47
47
 
48
48
  if (!dbMember?._id) {
@@ -2,7 +2,7 @@ const { COLLECTIONS } = require('../public/consts');
2
2
  const { isWixHostedImage } = require('../public/Utils/sharedUtils');
3
3
 
4
4
  const { MEMBERSHIPS_TYPES } = require('./consts');
5
- const { updateMemberContactInfo, createContact } = require('./contacts-methods');
5
+ const { updateMemberContactInfo, createSiteContact } = require('./contacts-methods');
6
6
  const { MEMBER_ACTIONS } = require('./daily-pull/consts');
7
7
  const { wixData } = require('./elevated-modules');
8
8
  const { createSiteMember, getCurrentMember } = require('./members-area-methods');
@@ -43,14 +43,16 @@ async function createContactAndMemberIfNew(memberData) {
43
43
  phones: memberData.phones,
44
44
  contactFormEmail: memberData.contactFormEmail || memberData.email,
45
45
  };
46
- const [wixMemberId, wixContactId] = await Promise.all([
47
- createSiteMember(toCreateMemberData),
48
- createContact(toCreateMemberData),
46
+ const needsWixMember = !memberData.wixMemberId;
47
+ const needsWixContact = !memberData.wixContactId;
48
+ const [newWixMemberId, newWixContactId] = await Promise.all([
49
+ needsWixMember ? createSiteMember(toCreateMemberData) : Promise.resolve(null),
50
+ needsWixContact ? createSiteContact(toCreateMemberData) : Promise.resolve(null),
49
51
  ]);
50
52
  let memberDataWithContactId = {
51
53
  ...memberData,
52
- wixMemberId,
53
- wixContactId,
54
+ wixMemberId: newWixMemberId || memberData.wixMemberId,
55
+ wixContactId: newWixContactId || memberData.wixContactId,
54
56
  };
55
57
  const updatedResult = await updateMember(memberDataWithContactId);
56
58
  memberDataWithContactId = {
@@ -174,26 +176,26 @@ async function getMemberBySlug({
174
176
  }
175
177
  }
176
178
 
177
- async function getMemberByContactId(contactId) {
178
- if (!contactId) {
179
- throw new Error('Contact ID is required');
179
+ async function getCMSMemberByWixMemberId(wixMemberId) {
180
+ if (!wixMemberId) {
181
+ throw new Error('Wix Member ID is required');
180
182
  }
181
183
  try {
182
184
  const members = await wixData
183
185
  .query(COLLECTIONS.MEMBERS_DATA)
184
- .eq('contactId', contactId)
186
+ .eq('wixMemberId', wixMemberId)
185
187
  .limit(2)
186
188
  .find()
187
189
  .then(res => res.items);
188
190
  if (members.length > 1) {
189
191
  throw new Error(
190
- `[getMemberByContactId] Multiple members found with contactId ${contactId} membersIds are : [${members.map(member => member.memberId).join(', ')}]`
192
+ `[getCMSMemberByWixMemberId] Multiple members found with wixMemberId ${wixMemberId} membersIds are : [${members.map(member => member.memberId).join(', ')}]`
191
193
  );
192
194
  }
193
195
  return members[0] || null;
194
196
  } catch (error) {
195
197
  throw new Error(
196
- `[getMemberByContactId] Failed to retrieve member by contactId ${contactId} data: ${error.message}`
198
+ `[getCMSMemberByWixMemberId] Failed to retrieve member by wixMemberId ${wixMemberId} data: ${error.message}`
197
199
  );
198
200
  }
199
201
  }
@@ -455,13 +457,13 @@ const getQAUsers = async () => {
455
457
  /**
456
458
  * Ensures member has a contact - creates one if missing
457
459
  * @param {Object} memberData - Member data from DB
458
- * @returns {Promise<Object>} - Member data with contactId
460
+ * @returns {Promise<Object>} - Member data with contact and member IDs
459
461
  */
460
- async function ensureMemberHasContact(memberData) {
462
+ async function ensureWixMemberAndContactExist(memberData) {
461
463
  if (!memberData) {
462
464
  throw new Error('Member data is required');
463
465
  }
464
- if (!memberData.contactId) {
466
+ if (!memberData.wixContactId || !memberData.wixMemberId) {
465
467
  const memberDataWithContactId = await createContactAndMemberIfNew(memberData);
466
468
  return memberDataWithContactId;
467
469
  }
@@ -479,7 +481,7 @@ async function prepareMemberForSSOLogin(data) {
479
481
  throw new Error(`Member data not found for memberId ${memberId}`);
480
482
  }
481
483
  console.log('memberData', memberData);
482
- return await ensureMemberHasContact(memberData);
484
+ return await ensureWixMemberAndContactExist(memberData);
483
485
  } catch (error) {
484
486
  console.error('Error in prepareMemberForSSOLogin', error.message);
485
487
  throw error;
@@ -496,7 +498,7 @@ async function prepareMemberForQALogin(email) {
496
498
  throw new Error(`Member data not found for email ${email}`);
497
499
  }
498
500
  console.log('memberData', memberData);
499
- return await ensureMemberHasContact(memberData);
501
+ return await ensureWixMemberAndContactExist(memberData);
500
502
  } catch (error) {
501
503
  console.error('Error in prepareMemberForQALogin', error.message);
502
504
  throw error;
@@ -518,11 +520,11 @@ async function trackButtonClick({ pageName, buttonName }) {
518
520
  return null;
519
521
  }
520
522
 
521
- const dbMember = await getMemberByContactId(wixMember._id);
523
+ const dbMember = await getCMSMemberByWixMemberId(wixMember._id);
522
524
 
523
525
  if (!dbMember) {
524
526
  console.warn(
525
- `[trackButtonClick]: Member not found in MembersDataLatest for contactId: ${wixMember._id}`
527
+ `[trackButtonClick]: Member not found in MembersDataLatest for wixMemberId: ${wixMember._id}`
526
528
  );
527
529
  return null;
528
530
  }
@@ -548,14 +550,20 @@ async function trackButtonClick({ pageName, buttonName }) {
548
550
  }
549
551
  }
550
552
 
553
+ async function getAllMembersWithWixMemberId() {
554
+ const membersQuery = wixData.query(COLLECTIONS.MEMBERS_DATA).isNotEmpty('wixMemberId');
555
+ return await queryAllItems(membersQuery);
556
+ }
557
+
551
558
  module.exports = {
552
559
  findMemberByWixDataId,
553
560
  createContactAndMemberIfNew,
561
+ getAllMembersWithWixMemberId,
554
562
  saveRegistrationData,
555
563
  bulkSaveMembers,
556
564
  findMemberById,
557
565
  getMemberBySlug,
558
- getMemberByContactId,
566
+ getCMSMemberByWixMemberId,
559
567
  getAllEmptyAboutYouMembers,
560
568
  updateMember,
561
569
  getAllMembersWithExternalImages,
@@ -16,6 +16,8 @@ const TASKS_NAMES = {
16
16
  migrateUrlsChunk: 'migrateUrlsChunk',
17
17
  scheduleGenerateMissingUrls: 'scheduleGenerateMissingUrls',
18
18
  generateUrlsChunk: 'generateUrlsChunk',
19
+ scheduleCreateContactsFromMembers: 'scheduleCreateContactsFromMembers',
20
+ createContactsFromMembers: 'createContactsFromMembers',
19
21
  };
20
22
 
21
23
  module.exports = {
@@ -16,6 +16,8 @@ const {
16
16
  migrateContactFormEmails,
17
17
  scheduleEmailSync,
18
18
  syncMemberLoginEmails,
19
+ scheduleCreateContactsFromMembers,
20
+ createContactsFromMembers,
19
21
  } = require('./tasks-process-methods');
20
22
  const {
21
23
  scheduleMigrateExistingUrls,
@@ -153,6 +155,20 @@ const TASKS = {
153
155
  shouldSkipCheck: () => false,
154
156
  estimatedDurationSec: 80,
155
157
  },
158
+ [TASKS_NAMES.scheduleCreateContactsFromMembers]: {
159
+ name: TASKS_NAMES.scheduleCreateContactsFromMembers,
160
+ getIdentifier: () => 'SHOULD_NEVER_SKIP',
161
+ process: scheduleCreateContactsFromMembers,
162
+ shouldSkipCheck: () => false,
163
+ estimatedDurationSec: 80,
164
+ },
165
+ [TASKS_NAMES.createContactsFromMembers]: {
166
+ name: TASKS_NAMES.createContactsFromMembers,
167
+ getIdentifier: task => task.data,
168
+ process: createContactsFromMembers,
169
+ shouldSkipCheck: () => false,
170
+ estimatedDurationSec: 80,
171
+ },
156
172
  };
157
173
 
158
174
  module.exports = { TASKS };
@@ -21,10 +21,12 @@ function isValidImageUrl(url) {
21
21
  let parsedUrl;
22
22
  try {
23
23
  parsedUrl = new URL(url);
24
+ console.log('parsedUrl', parsedUrl);
24
25
  } catch {
25
26
  return false;
26
27
  }
27
-
28
+ console.log('parsedUrl', parsedUrl);
29
+ console.log('parsedUrl.protocol', parsedUrl.protocol);
28
30
  // Only allow HTTP and HTTPS protocols (reject blob:, data:, file:, etc.)
29
31
  const validProtocols = ['http:', 'https:'];
30
32
  if (!validProtocols.includes(parsedUrl.protocol)) {
@@ -34,10 +36,10 @@ function isValidImageUrl(url) {
34
36
  // Extract file extension from URL (handle query parameters)
35
37
  const urlPath = url.split('?')[0].toLowerCase();
36
38
  const validExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp'];
37
-
39
+ console.log('urlPath', urlPath);
38
40
  // Check if URL ends with valid extension
39
41
  const hasValidExtension = validExtensions.some(ext => urlPath.endsWith(ext));
40
-
42
+ console.log('hasValidExtension', hasValidExtension);
41
43
  // Reject obviously invalid extensions
42
44
  const invalidExtensions = [
43
45
  '.pdf',
@@ -52,7 +54,7 @@ function isValidImageUrl(url) {
52
54
  '_gif',
53
55
  ];
54
56
  const hasInvalidExtension = invalidExtensions.some(ext => urlPath.includes(ext));
55
-
57
+ console.log('hasInvalidExtension', hasInvalidExtension);
56
58
  return hasValidExtension && !hasInvalidExtension;
57
59
  }
58
60
 
@@ -139,7 +141,7 @@ async function updateMemberRichContent(memberId) {
139
141
  async function updateMemberProfileImage(memberId) {
140
142
  try {
141
143
  const member = await findMemberByWixDataId(memberId);
142
- const trimmedProfileImage = member.profileImage.trim();
144
+ const trimmedProfileImage = member.profileImage?.trim();
143
145
  // Check if member has an external profile image URL
144
146
  if (!trimmedProfileImage || isWixHostedImage(trimmedProfileImage)) {
145
147
  console.log(`Member ${memberId} already has Wix-hosted image or no image`);
@@ -13,6 +13,8 @@ const {
13
13
  bulkSaveMembers,
14
14
  getAllUpdatedLoginEmails,
15
15
  getMembersByIds,
16
+ createContactAndMemberIfNew,
17
+ getAllMembersWithWixMemberId,
16
18
  } = require('../members-data-methods');
17
19
  const {
18
20
  getCompleteStateList,
@@ -530,6 +532,46 @@ const syncMemberLoginEmails = async data => {
530
532
  throw new Error(errorMessage);
531
533
  }
532
534
  };
535
+ /**
536
+ * Schedules tasks to create contacts from members
537
+ * dev-only task, run only once by the developers
538
+ */
539
+ const scheduleCreateContactsFromMembers = async () => {
540
+ const members = await getAllMembersWithWixMemberId();
541
+ console.log(
542
+ `Starting to schedule create contacts from members tasks for ${members.length} members in chunks of 500 members`
543
+ );
544
+ const membersChunks = chunkArray(members, 500);
545
+ for (let chunkIndex = 0; chunkIndex < membersChunks.length; chunkIndex++) {
546
+ const chunk = membersChunks[chunkIndex];
547
+ const toScheduleTask = {
548
+ name: TASKS_NAMES.createContactsFromMembers,
549
+ data: { chunk, chunkIndex },
550
+ };
551
+ await taskManager().schedule(toScheduleTask);
552
+ console.log(`Scheduled task for chunk ${chunkIndex} with ${chunk.length} members`);
553
+ }
554
+ console.log(`Successfully scheduled ${membersChunks.length} tasks for ${members.length} members`);
555
+ };
556
+
557
+ /**
558
+ * Creates contacts from members
559
+ * dev-only task, run only once by the developers
560
+ */
561
+ const createContactsFromMembers = async data => {
562
+ const { chunk, chunkIndex } = data;
563
+ console.log(`Creating contacts from ${chunk.length} members in chunk ${chunkIndex}`);
564
+ const createPromises = chunk.map(member => createContactAndMemberIfNew(member));
565
+ const createResults = await Promise.all(createPromises);
566
+ console.log(
567
+ `Created ${createResults.length} contacts from ${chunk.length} members in chunk ${chunkIndex}`
568
+ );
569
+ const saveResult = await bulkSaveMembers(createResults);
570
+ console.log(
571
+ `Successfully saved ${saveResult.totalSaved} contacts from ${chunk.length} members in chunk ${chunkIndex}`
572
+ );
573
+ return saveResult;
574
+ };
533
575
 
534
576
  module.exports = {
535
577
  scheduleTaskForEmptyAboutYouMembers,
@@ -542,4 +584,6 @@ module.exports = {
542
584
  migrateContactFormEmails,
543
585
  scheduleEmailSync,
544
586
  syncMemberLoginEmails,
587
+ scheduleCreateContactsFromMembers, // run only once by the developers
588
+ createContactsFromMembers,
545
589
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abmp-npm",
3
- "version": "1.1.98",
3
+ "version": "1.1.100",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "check-cycles": "madge --circular .",