abmp-npm 2.0.77 → 2.0.78

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 (99) hide show
  1. package/package.json +8 -1
  2. package/.claude/skills/wix-data-query/SKILL.md +0 -167
  3. package/.claude/skills/wix-data-query/references/backup-archive.md +0 -136
  4. package/.claude/skills/wix-data-query/references/members-data-latest.md +0 -125
  5. package/.claude/skills/wix-data-query/references/recipes.md +0 -264
  6. package/.claude/worktrees/fix-save-button-disable/.husky/pre-commit +0 -19
  7. package/.claude/worktrees/fix-save-button-disable/.prettierignore +0 -7
  8. package/.claude/worktrees/fix-save-button-disable/.prettierrc.json +0 -16
  9. package/.claude/worktrees/fix-save-button-disable/README.md +0 -1
  10. package/.claude/worktrees/fix-save-button-disable/backend/README-member-contact-flows.md +0 -136
  11. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/daily-pull-execution-check.test.js +0 -124
  12. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/ensure-unique-urls-in-batch.test.js +0 -129
  13. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/login-email-sync.test.js +0 -49
  14. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/transient-retry-and-bulk-lookup.test.js +0 -129
  15. package/.claude/worktrees/fix-save-button-disable/backend/__tests__/url-uniqueness.test.js +0 -194
  16. package/.claude/worktrees/fix-save-button-disable/backend/automations-methods.js +0 -13
  17. package/.claude/worktrees/fix-save-button-disable/backend/cms-data-methods.js +0 -251
  18. package/.claude/worktrees/fix-save-button-disable/backend/consts.js +0 -58
  19. package/.claude/worktrees/fix-save-button-disable/backend/contacts-methods.js +0 -67
  20. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/bulk-process-methods.js +0 -222
  21. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/consts.js +0 -39
  22. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/index.js +0 -5
  23. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/process-member-methods.js +0 -213
  24. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/schedule-methods.js +0 -41
  25. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/sync-to-cms-methods.js +0 -161
  26. package/.claude/worktrees/fix-save-button-disable/backend/daily-pull/utils.js +0 -151
  27. package/.claude/worktrees/fix-save-button-disable/backend/data-hooks.js +0 -29
  28. package/.claude/worktrees/fix-save-button-disable/backend/dev-only-methods.js +0 -58
  29. package/.claude/worktrees/fix-save-button-disable/backend/elevated-modules.js +0 -18
  30. package/.claude/worktrees/fix-save-button-disable/backend/forms-methods.js +0 -52
  31. package/.claude/worktrees/fix-save-button-disable/backend/http-functions/httpFunctions.js +0 -86
  32. package/.claude/worktrees/fix-save-button-disable/backend/http-functions/index.js +0 -3
  33. package/.claude/worktrees/fix-save-button-disable/backend/http-functions/interests.js +0 -35
  34. package/.claude/worktrees/fix-save-button-disable/backend/index.js +0 -19
  35. package/.claude/worktrees/fix-save-button-disable/backend/jobs.js +0 -158
  36. package/.claude/worktrees/fix-save-button-disable/backend/login/generate-member-session-token.js +0 -27
  37. package/.claude/worktrees/fix-save-button-disable/backend/login/index.js +0 -8
  38. package/.claude/worktrees/fix-save-button-disable/backend/login/qa-login-methods.js +0 -54
  39. package/.claude/worktrees/fix-save-button-disable/backend/login/sso-methods.js +0 -159
  40. package/.claude/worktrees/fix-save-button-disable/backend/member-contact-orchestration.js +0 -111
  41. package/.claude/worktrees/fix-save-button-disable/backend/members-area-methods.js +0 -88
  42. package/.claude/worktrees/fix-save-button-disable/backend/members-data-methods.js +0 -789
  43. package/.claude/worktrees/fix-save-button-disable/backend/pac-api-methods.js +0 -54
  44. package/.claude/worktrees/fix-save-button-disable/backend/routers/index.js +0 -3
  45. package/.claude/worktrees/fix-save-button-disable/backend/routers/methods.js +0 -191
  46. package/.claude/worktrees/fix-save-button-disable/backend/routers/utils.js +0 -128
  47. package/.claude/worktrees/fix-save-button-disable/backend/search-filters-methods.js +0 -124
  48. package/.claude/worktrees/fix-save-button-disable/backend/tasks/address-primary-methods.js +0 -256
  49. package/.claude/worktrees/fix-save-button-disable/backend/tasks/address-visibility-methods.js +0 -160
  50. package/.claude/worktrees/fix-save-button-disable/backend/tasks/consts.js +0 -34
  51. package/.claude/worktrees/fix-save-button-disable/backend/tasks/daily-pull-check-methods.js +0 -78
  52. package/.claude/worktrees/fix-save-button-disable/backend/tasks/email-normalize-methods.js +0 -134
  53. package/.claude/worktrees/fix-save-button-disable/backend/tasks/index.js +0 -10
  54. package/.claude/worktrees/fix-save-button-disable/backend/tasks/migration-methods.js +0 -46
  55. package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-configs.js +0 -251
  56. package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-helpers-methods.js +0 -433
  57. package/.claude/worktrees/fix-save-button-disable/backend/tasks/tasks-process-methods.js +0 -606
  58. package/.claude/worktrees/fix-save-button-disable/backend/tasks/url-migration-methods.js +0 -378
  59. package/.claude/worktrees/fix-save-button-disable/backend/tasks/url-space-fix-methods.js +0 -174
  60. package/.claude/worktrees/fix-save-button-disable/backend/utils.js +0 -277
  61. package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/extract-duplicate-url-groups.js +0 -201
  62. package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/find-duplicate-ids.js +0 -159
  63. package/.claude/worktrees/fix-save-button-disable/dev-only-scripts/find-duplicate-urls.js +0 -201
  64. package/.claude/worktrees/fix-save-button-disable/eslint.config.js +0 -120
  65. package/.claude/worktrees/fix-save-button-disable/index.js +0 -5
  66. package/.claude/worktrees/fix-save-button-disable/package-lock.json +0 -12160
  67. package/.claude/worktrees/fix-save-button-disable/package.json +0 -60
  68. package/.claude/worktrees/fix-save-button-disable/pages/ContactUs.js +0 -129
  69. package/.claude/worktrees/fix-save-button-disable/pages/Home.js +0 -779
  70. package/.claude/worktrees/fix-save-button-disable/pages/LearnMore.js +0 -27
  71. package/.claude/worktrees/fix-save-button-disable/pages/LoadingPage.js +0 -29
  72. package/.claude/worktrees/fix-save-button-disable/pages/Profile.js +0 -354
  73. package/.claude/worktrees/fix-save-button-disable/pages/QAPage.js +0 -39
  74. package/.claude/worktrees/fix-save-button-disable/pages/SaveAlerts.js +0 -13
  75. package/.claude/worktrees/fix-save-button-disable/pages/SelectBannerImages.js +0 -46
  76. package/.claude/worktrees/fix-save-button-disable/pages/deleteConfirm.js +0 -19
  77. package/.claude/worktrees/fix-save-button-disable/pages/index.js +0 -12
  78. package/.claude/worktrees/fix-save-button-disable/pages/personalDetails.js +0 -2486
  79. package/.claude/worktrees/fix-save-button-disable/public/Utils/homePage.js +0 -798
  80. package/.claude/worktrees/fix-save-button-disable/public/Utils/personalDetailsUtils.js +0 -25
  81. package/.claude/worktrees/fix-save-button-disable/public/Utils/sharedUtils.js +0 -235
  82. package/.claude/worktrees/fix-save-button-disable/public/consts.js +0 -113
  83. package/.claude/worktrees/fix-save-button-disable/public/index.js +0 -5
  84. package/.claude/worktrees/fix-save-button-disable/public/messages.js +0 -16
  85. package/.claude/worktrees/fix-save-button-disable/public/sso-auth-methods.js +0 -43
  86. package/.husky/pre-commit +0 -19
  87. package/.prettierignore +0 -7
  88. package/.prettierrc.json +0 -16
  89. package/backend/__tests__/daily-pull-execution-check.test.js +0 -124
  90. package/backend/__tests__/ensure-unique-urls-in-batch.test.js +0 -129
  91. package/backend/__tests__/login-email-sync.test.js +0 -49
  92. package/backend/__tests__/map-link.test.js +0 -115
  93. package/backend/__tests__/transient-retry-and-bulk-lookup.test.js +0 -129
  94. package/backend/__tests__/url-uniqueness.test.js +0 -194
  95. package/backend/__tests__/url-validation.test.js +0 -69
  96. package/dev-only-scripts/extract-duplicate-url-groups.js +0 -201
  97. package/dev-only-scripts/find-duplicate-ids.js +0 -159
  98. package/dev-only-scripts/find-duplicate-urls.js +0 -201
  99. package/eslint.config.js +0 -120
@@ -1,606 +0,0 @@
1
- const { taskManager } = require('psdev-task-manager');
2
-
3
- const { COLLECTIONS } = require('../../public/consts');
4
- const { COMPILED_FILTERS_FIELDS, CONFIG_KEYS, LOGIN_EMAIL_SYNC_STATUS } = require('../consts');
5
- const { changeWixMembersEmails, summarizeLoginEmailOutcomes } = require('../daily-pull/utils');
6
- const { wixData } = require('../elevated-modules');
7
- const {
8
- getAllEmptyAboutYouMembers,
9
- getAllMembersWithExternalImages,
10
- getMembersWithWixUrl,
11
- getAllMembersWithoutContactFormEmail,
12
- findMemberByWixDataId,
13
- bulkSaveMembers,
14
- getAllUpdatedLoginEmails,
15
- getMembersByIds,
16
- createContactAndMemberIfNew,
17
- getAllMembersWithWixMemberId,
18
- } = require('../members-data-methods');
19
- const {
20
- getCompleteStateList,
21
- getAreasOfPracticeList,
22
- getStateCityMap,
23
- getCompiledFiltersOptions,
24
- } = require('../search-filters-methods');
25
- const { chunkArray, getSiteConfigs } = require('../utils');
26
-
27
- const { TASKS_NAMES } = require('./consts');
28
- const {
29
- updateMemberRichContent,
30
- updateMemberProfileImage,
31
- getAWSTokens,
32
- uploadMembersSitemap,
33
- } = require('./tasks-helpers-methods');
34
-
35
- const scheduleTaskForEmptyAboutYouMembers = async () => {
36
- const createTasksFromMembers = members => {
37
- const memberIds = members.map(member => member._id);
38
- return {
39
- name: TASKS_NAMES.convertHtmlToRichContent,
40
- data: { memberIds },
41
- type: 'scheduled',
42
- };
43
- };
44
- const members = await getAllEmptyAboutYouMembers();
45
- console.log('starting to schedule tasks for empty about you members');
46
- const membersChunks = chunkArray(members, 1000);
47
- for (const chunk of membersChunks) {
48
- const toScheduleTask = createTasksFromMembers(chunk);
49
- await taskManager().schedule(toScheduleTask);
50
- }
51
- };
52
-
53
- //this funciton takes ~0.5 seconds per member
54
- const convertAboutYouHtmlToRichContent = async membersIds => {
55
- const result = {};
56
-
57
- // Process members in chunks of 30
58
- const chunks = chunkArray(membersIds, 30);
59
-
60
- for (const chunk of chunks) {
61
- console.log(`Processing chunk of ${chunk.length} members`);
62
-
63
- // Process each chunk concurrently using Promise.all
64
- const chunkPromises = chunk.map(async memberId => {
65
- console.log('memberId ======', memberId);
66
- try {
67
- await updateMemberRichContent(memberId);
68
- return { memberId, success: true };
69
- } catch (error) {
70
- console.error('error in updating member', error);
71
- return { memberId, success: false };
72
- }
73
- });
74
-
75
- // Wait for all promises in the chunk to complete
76
- const chunkResults = await Promise.all(chunkPromises);
77
-
78
- // Update result object with chunk results
79
- chunkResults.forEach(({ memberId, success }) => {
80
- result[memberId] = success;
81
- });
82
- }
83
-
84
- return result;
85
- };
86
-
87
- async function compileFiltersOptions(field) {
88
- const getNonCompiledFilter = field => {
89
- const filterMap = {
90
- [COMPILED_FILTERS_FIELDS.COMPILED_STATE_LIST]: getCompleteStateList,
91
- [COMPILED_FILTERS_FIELDS.COMPILED_AREAS_OF_PRACTICES]: getAreasOfPracticeList,
92
- [COMPILED_FILTERS_FIELDS.COMPILED_STATE_CITY_MAP]: getStateCityMap,
93
- };
94
- const filterFunction = filterMap[field];
95
- if (!filterFunction) {
96
- throw new Error(`Unknown filter field: ${field}`);
97
- }
98
- return filterFunction();
99
- };
100
- const [nonCompiledFilterData, compiledFiltersOptions] = await Promise.all([
101
- getNonCompiledFilter(field),
102
- getCompiledFiltersOptions(),
103
- ]);
104
- compiledFiltersOptions[field] = nonCompiledFilterData;
105
- await wixData.save(COLLECTIONS.COMPILED_STATE_CITY_MAP, compiledFiltersOptions);
106
- }
107
-
108
- const scheduleTaskForExternalProfileImages = async () => {
109
- const createImageMigrationTasksFromMembers = members => {
110
- const memberIds = members.map(member => member._id);
111
-
112
- return {
113
- name: TASKS_NAMES.convertExternalProfilesToWixImages,
114
- data: { memberIds },
115
- type: 'scheduled',
116
- };
117
- };
118
- const members = await getAllMembersWithExternalImages();
119
- console.log('Starting to schedule tasks for external profile image migration');
120
- const membersChunks = chunkArray(members, 300);
121
- for (const chunk of membersChunks) {
122
- const toScheduleTask = createImageMigrationTasksFromMembers(chunk);
123
- await taskManager().schedule(toScheduleTask);
124
- }
125
- console.log(`Scheduled ${members.length} members for profile image migration`);
126
- };
127
-
128
- const convertExternalProfilesToWixImages = async membersIds => {
129
- const result = {};
130
-
131
- // Process members in chunks of 30 (optimal concurrent processing)
132
- const chunks = chunkArray(membersIds, 30);
133
-
134
- for (const chunk of chunks) {
135
- console.log(`Processing profile image chunk of ${chunk.length} members`);
136
-
137
- // Process each chunk concurrently using Promise.all
138
- const chunkPromises = chunk.map(async memberId => {
139
- console.log('Processing profile image for member:', memberId);
140
- try {
141
- const updateResult = await updateMemberProfileImage(memberId);
142
- return {
143
- memberId,
144
- success: updateResult.success,
145
- message: updateResult.message,
146
- };
147
- } catch (error) {
148
- console.error('Error updating member profile image:', error);
149
- return { memberId, success: false, error: error.message };
150
- }
151
- });
152
-
153
- const chunkResults = await Promise.all(chunkPromises);
154
-
155
- // Log results for this chunk
156
- chunkResults.forEach(result => {
157
- if (result.success) {
158
- console.log(`✅ Successfully processed profile image for member ${result.memberId}`);
159
- } else {
160
- console.error(
161
- `❌ Failed to process profile image for member ${result.memberId}: ${result.error}`
162
- );
163
- }
164
- });
165
-
166
- // Add small delay between chunks to avoid overwhelming the media manager
167
- await new Promise(resolve => setTimeout(resolve, 2000));
168
- }
169
-
170
- return result;
171
- };
172
- const updateSiteMapS3 = async () => {
173
- const relevantMembers = await getMembersWithWixUrl();
174
- console.log('number of profiles to upload', relevantMembers.length);
175
- const [tokens, siteAssociation] = await Promise.all([
176
- getAWSTokens(),
177
- getSiteConfigs(CONFIG_KEYS.SITE_ASSOCIATION),
178
- ]);
179
- // const creds = await getNewStsSessionToken(tokens.AWS_ACCESS_KEY_ID, tokens.AWS_SECRET_ACCESS_KEY, 3600);
180
- // console.log("creds",creds); // verify it’s fresh
181
- try {
182
- const chunkSize = 10000;
183
- console.log('Total items will be split into', relevantMembers.length / chunkSize);
184
- const chunks = chunkArray(relevantMembers, chunkSize);
185
- console.log(`Uploading ${chunks.length} sitemap files...`);
186
- for (let i = 0; i < chunks.length; i++) {
187
- const index = i + 1;
188
- const fileName = `profiles-sitemap-${index}.xml`;
189
- console.log(
190
- `Uploading chunk ${index}/${chunks.length} -> ${fileName} (${chunks[i].length} items)`
191
- );
192
- await uploadMembersSitemap({
193
- members: chunks[i],
194
- tokens,
195
- destinationFileName: fileName,
196
- siteAssociation,
197
- });
198
- console.log(`Uploaded ${fileName}`);
199
- }
200
- console.log('All sitemap files uploaded successfully');
201
- } catch (e) {
202
- console.error('Sitemap upload failed:', e?.message || e);
203
- }
204
- };
205
-
206
- /**
207
- * Schedules tasks to migrate contactFormEmail for all members who don't have it set
208
- * This function gets all members missing contactFormEmail and schedules batch processing tasks
209
- */
210
- const scheduleContactFormEmailMigration = async () => {
211
- try {
212
- console.log('Starting to schedule contactFormEmail migration tasks');
213
- const createContactFormEmailMigrationTask = (members, chunkIndex) => {
214
- const memberIds = members.map(member => member._id);
215
- return {
216
- name: TASKS_NAMES.migrateContactFormEmails,
217
- data: { memberIds, chunkIndex },
218
- type: 'scheduled',
219
- };
220
- };
221
- const membersToMigrate = await getAllMembersWithoutContactFormEmail();
222
- console.log(`Found ${membersToMigrate.length} members needing contactFormEmail migration`);
223
- if (membersToMigrate.length === 0) {
224
- console.log('No members need contactFormEmail migration');
225
- return {
226
- success: true,
227
- message: 'No members need migration',
228
- totalMembers: 0,
229
- tasksScheduled: 0,
230
- };
231
- }
232
-
233
- // Process in chunks of 500 members per task (smaller chunks for reliability)
234
- const membersChunks = chunkArray(membersToMigrate, 500);
235
- console.log(`Creating ${membersChunks.length} migration tasks`);
236
-
237
- for (let chunkIndex = 0; chunkIndex < membersChunks.length; chunkIndex++) {
238
- const chunk = membersChunks[chunkIndex];
239
- const migrationTask = createContactFormEmailMigrationTask(chunk, chunkIndex);
240
- await taskManager().schedule(migrationTask);
241
- console.log(`Scheduled task for chunk ${chunkIndex} with ${chunk.length} members`);
242
- }
243
-
244
- console.log(
245
- `Successfully scheduled ${membersChunks.length} tasks for ${membersToMigrate.length} members`
246
- );
247
-
248
- return {
249
- success: true,
250
- message: `Scheduled ${membersChunks.length} tasks for ${membersToMigrate.length} members`,
251
- totalMembers: membersToMigrate.length,
252
- tasksScheduled: membersChunks.length,
253
- };
254
- } catch (error) {
255
- const errorMessage = `Failed to schedule contactFormEmail migration: ${error.message}`;
256
- console.error(errorMessage);
257
- throw new Error(errorMessage);
258
- }
259
- };
260
-
261
- /**
262
- * Migrates contactFormEmail for a batch of members
263
- * Sets contactFormEmail to the same value as the member's current email
264
- * @param {Object} data - Data object with memberIds and chunkIndex
265
- * @param {Array} data.memberIds - Array of member IDs to process
266
- * @param {number} data.chunkIndex - Index of the chunk
267
- * @returns {Promise<Object>} - Result object with success/failure counts
268
- */
269
- const migrateContactFormEmails = async data => {
270
- const { memberIds, chunkIndex } = data;
271
- const result = {
272
- successful: 0,
273
- failed: 0,
274
- errors: [],
275
- skipped: 0,
276
- skippedIds: [],
277
- };
278
-
279
- console.log(
280
- `Starting contactFormEmail migration for ${memberIds.length} members in chunk ${chunkIndex}`
281
- );
282
-
283
- try {
284
- // Get all members for this batch
285
- const memberPromises = memberIds.map(async memberId => {
286
- try {
287
- const member = await findMemberByWixDataId(memberId);
288
-
289
- // Skip if member already has contactFormEmail set
290
- if (member.contactFormEmail) {
291
- console.log(`Member ${memberId} already has contactFormEmail set`);
292
- result.skipped++;
293
- result.skippedIds.push(memberId);
294
- return null;
295
- }
296
-
297
- // Skip if member doesn't have email
298
- if (!member.email) {
299
- console.log(`Member ${memberId} doesn't have email - skipping`);
300
- result.skipped++;
301
- result.skippedIds.push(memberId);
302
- return null;
303
- }
304
-
305
- return {
306
- ...member,
307
- contactFormEmail: member.email,
308
- };
309
- } catch (error) {
310
- console.error(`Error preparing member ${memberId}:`, error);
311
- result.failed++;
312
- result.errors.push({ memberId, error: error.message });
313
- return null;
314
- }
315
- });
316
-
317
- const membersToUpdate = (await Promise.all(memberPromises)).filter(Boolean);
318
-
319
- if (membersToUpdate.length === 0) {
320
- console.log('No members need updating in this batch');
321
- return result;
322
- }
323
-
324
- console.log(
325
- `Started Updating ${membersToUpdate.length} members with contactFormEmail in chunk ${chunkIndex}`
326
- );
327
-
328
- // Process in smaller chunks for bulk update (1000 is Wix limit)
329
- const updateChunks = chunkArray(membersToUpdate, 1000);
330
-
331
- for (let chunkIndex = 0; chunkIndex < updateChunks.length; chunkIndex++) {
332
- const chunk = updateChunks[chunkIndex];
333
- try {
334
- await bulkSaveMembers(chunk);
335
- result.successful += chunk.length;
336
- console.log(`✅ Successfully updated ${chunk.length} members in chunk ${chunkIndex}`);
337
- } catch (error) {
338
- console.error(`❌ Error updating chunk ${chunkIndex}:`, error);
339
- result.failed += chunk.length;
340
- result.errors.push({
341
- chunk: chunkIndex,
342
- error: error.message,
343
- memberCount: chunk.length,
344
- });
345
- }
346
- }
347
- } catch (error) {
348
- const errorMessage = `Failed to migrate contactFormEmail for chunk ${chunkIndex}: ${error.message}`;
349
- console.error(errorMessage);
350
- throw new Error(errorMessage);
351
- }
352
-
353
- console.log(
354
- `ContactFormEmail migration task completed: ${result.successful} successful, ${result.failed} failed, ${result.skipped} skipped, in chunk ${chunkIndex}`
355
- );
356
- return result;
357
- };
358
-
359
- /**
360
- Schedules tasks to sync updated emails from the updated login emails database
361
- * This function gets all updated emails and schedules batch processing tasks
362
- */
363
- const scheduleEmailSync = async () => {
364
- try {
365
- console.log('Starting to schedule email sync tasks');
366
- const createEmailSyncTask = (chunk, chunkIndex) => {
367
- //To reduce stored Items size inside task data
368
- const emailUpdates = chunk.map(emailUpdate => ({
369
- memberId: emailUpdate.memberId,
370
- loginEmail: emailUpdate.loginEmail,
371
- }));
372
- return {
373
- name: TASKS_NAMES.syncMemberLoginEmails,
374
- data: { emailUpdates, chunkIndex },
375
- type: 'scheduled',
376
- };
377
- };
378
- const updatedEmails = await getAllUpdatedLoginEmails();
379
- console.log(`Found ${updatedEmails.length} updated email records`);
380
-
381
- if (updatedEmails.length === 0) {
382
- console.log('No updated emails found');
383
- return {
384
- success: true,
385
- message: 'No updated emails to sync',
386
- totalEmails: 0,
387
- tasksScheduled: 0,
388
- };
389
- }
390
-
391
- const emailChunks = chunkArray(updatedEmails, 500);
392
- console.log(`Creating ${emailChunks.length} email sync tasks`);
393
-
394
- for (let chunkIndex = 0; chunkIndex < emailChunks.length; chunkIndex++) {
395
- const chunk = emailChunks[chunkIndex];
396
- const syncTask = createEmailSyncTask(chunk, chunkIndex);
397
- await taskManager().schedule(syncTask);
398
- console.log(`Scheduled task for chunk ${chunkIndex} with ${chunk.length} email updates`);
399
- }
400
-
401
- console.log(
402
- `Successfully scheduled ${emailChunks.length} tasks for ${updatedEmails.length} email updates`
403
- );
404
-
405
- return {
406
- success: true,
407
- message: `Scheduled ${emailChunks.length} tasks for ${updatedEmails.length} email updates`,
408
- totalEmails: updatedEmails.length,
409
- tasksScheduled: emailChunks.length,
410
- };
411
- } catch (error) {
412
- const errorMessage = `Failed to schedule email sync: ${error.message}`;
413
- console.error(errorMessage);
414
- throw new Error(errorMessage);
415
- }
416
- };
417
-
418
- /**
419
- * Syncs member emails with updated login emails
420
- * @param {Object} data - Data object with emailUpdates and chunkIndex
421
- * @param {Array} data.emailUpdates - Array of email update objects with memberId and loginEmail
422
- * @param {number} data.chunkIndex - Index of the chunk
423
- * @returns {Object} - Result object with success/failure counts
424
- */
425
- const syncMemberLoginEmails = async data => {
426
- const { emailUpdates, chunkIndex } = data;
427
- const result = {
428
- successful: 0,
429
- failed: 0,
430
- skipped: 0,
431
- skippedIds: [],
432
- missingMemberIds: [],
433
- errors: [],
434
- };
435
-
436
- console.log(
437
- `Starting email sync for ${emailUpdates.length} email updates in chunk ${chunkIndex}`
438
- );
439
-
440
- try {
441
- const memberIds = emailUpdates.map(update => update.memberId);
442
-
443
- const existingMembers = await getMembersByIds(memberIds);
444
- console.log(`Found ${existingMembers.length} existing members to update`);
445
- const existingMemberIds = new Set(existingMembers.map(member => member.memberId));
446
- const missingMemberIds = memberIds.filter(memberId => !existingMemberIds.has(memberId));
447
-
448
- // Add missing member IDs to skipped count and log them
449
- if (missingMemberIds.length > 0) {
450
- console.log(
451
- `Found ${missingMemberIds.length} members in emailUpdates but not in database:`,
452
- missingMemberIds
453
- );
454
- result.missingMemberIds = result.missingMemberIds || [];
455
- result.missingMemberIds.push(...missingMemberIds);
456
- }
457
- const emailUpdateMap = new Map();
458
- emailUpdates.forEach(update => {
459
- emailUpdateMap.set(update.memberId, update.loginEmail);
460
- });
461
-
462
- const membersToUpdate = [];
463
-
464
- for (const member of existingMembers) {
465
- const newEmail = emailUpdateMap.get(member.memberId);
466
-
467
- if (!newEmail) {
468
- console.log(`No email update found for member ${member.memberId}`);
469
- result.skipped++;
470
- result.skippedIds.push(member.memberId);
471
- continue;
472
- }
473
-
474
- if (member.email === newEmail) {
475
- console.log(`Email already up to date for member ${member.memberId}`);
476
- result.skipped++;
477
- result.skippedIds.push(member.memberId);
478
- continue;
479
- }
480
-
481
- membersToUpdate.push({
482
- ...member,
483
- email: newEmail,
484
- previousLoginEmail: member.email,
485
- });
486
- }
487
-
488
- if (membersToUpdate.length === 0) {
489
- console.log('No members need email updates in this batch', chunkIndex);
490
- return result;
491
- }
492
-
493
- console.log(
494
- `Updating ${membersToUpdate.length} members with new emails in chunk ${chunkIndex}`
495
- );
496
-
497
- const updateChunks = chunkArray(membersToUpdate, 1000);
498
-
499
- for (const chunk of updateChunks) {
500
- try {
501
- // Change Wix login emails first; only advance the CMS login email for the ones that
502
- // succeeded. Failures keep their previous email (CMS stays consistent with Wix) and are
503
- // reported in result.errors for manual handling.
504
- const outcomes = await changeWixMembersEmails(chunk);
505
- const { failedMemberIds } = summarizeLoginEmailOutcomes(outcomes);
506
- const toSave = chunk.map(member => {
507
- const { previousLoginEmail, ...restMember } = member;
508
- if (failedMemberIds.has(member.memberId) && previousLoginEmail !== undefined) {
509
- return { ...restMember, email: previousLoginEmail };
510
- }
511
- return restMember;
512
- });
513
- await bulkSaveMembers(toSave);
514
-
515
- outcomes.forEach(outcome => {
516
- if (outcome.status === LOGIN_EMAIL_SYNC_STATUS.UPDATED) {
517
- result.successful++;
518
- } else if (outcome.status === LOGIN_EMAIL_SYNC_STATUS.FAILED) {
519
- result.failed++;
520
- result.errors.push({
521
- memberId: outcome.memberId,
522
- wixMemberId: outcome.wixMemberId,
523
- desiredEmail: outcome.desiredEmail,
524
- error: outcome.error,
525
- });
526
- } else {
527
- result.skipped++;
528
- }
529
- });
530
- } catch (error) {
531
- console.error(`❌ Error updating chunk ${chunkIndex}:`, error);
532
- result.failed += chunk.length;
533
- result.errors.push({
534
- chunk: chunkIndex,
535
- error: error.message,
536
- memberCount: chunk.length,
537
- });
538
- }
539
- }
540
- console.log(
541
- `Login Emails sync task completed: ${result.successful} synced, ${result.failed} failed, ${result.skipped} skipped`
542
- );
543
-
544
- return result;
545
- } catch (error) {
546
- const errorMessage = `Failed to syncMemberLoginEmails for chunk ${chunkIndex} of length ${emailUpdates.length} with error: ${error.message}`;
547
- console.error(errorMessage);
548
- throw new Error(errorMessage);
549
- }
550
- };
551
- /**
552
- * Schedules tasks to create contacts from members
553
- * dev-only task, run only once by the developers
554
- */
555
- const scheduleCreateContactsFromMembers = async () => {
556
- const members = await getAllMembersWithWixMemberId();
557
- console.log(
558
- `Starting to schedule create contacts from members tasks for ${members.length} members in chunks of 500 members`
559
- );
560
- const membersChunks = chunkArray(members, 500);
561
- for (let chunkIndex = 0; chunkIndex < membersChunks.length; chunkIndex++) {
562
- const chunk = membersChunks[chunkIndex];
563
- const toScheduleTask = {
564
- name: TASKS_NAMES.createContactsFromMembers,
565
- data: { chunk, chunkIndex },
566
- type: 'scheduled',
567
- };
568
- await taskManager().schedule(toScheduleTask);
569
- console.log(`Scheduled task for chunk ${chunkIndex} with ${chunk.length} members`);
570
- }
571
- console.log(`Successfully scheduled ${membersChunks.length} tasks for ${members.length} members`);
572
- };
573
-
574
- /**
575
- * Creates contacts from members
576
- * dev-only task, run only once by the developers
577
- */
578
- const createContactsFromMembers = async data => {
579
- const { chunk, chunkIndex } = data;
580
- console.log(`Creating contacts from ${chunk.length} members in chunk ${chunkIndex}`);
581
- const createPromises = chunk.map(member => createContactAndMemberIfNew(member, true));
582
- const createResults = await Promise.all(createPromises);
583
- console.log(
584
- `Created ${createResults.length} contacts from ${chunk.length} members in chunk ${chunkIndex}`
585
- );
586
- const saveResult = await bulkSaveMembers(createResults);
587
- console.log(
588
- `Successfully saved ${saveResult.totalSaved} contacts from ${chunk.length} members in chunk ${chunkIndex}`
589
- );
590
- return saveResult;
591
- };
592
-
593
- module.exports = {
594
- scheduleTaskForEmptyAboutYouMembers,
595
- convertAboutYouHtmlToRichContent,
596
- compileFiltersOptions,
597
- scheduleTaskForExternalProfileImages,
598
- convertExternalProfilesToWixImages,
599
- updateSiteMapS3,
600
- scheduleContactFormEmailMigration,
601
- migrateContactFormEmails,
602
- scheduleEmailSync,
603
- syncMemberLoginEmails,
604
- scheduleCreateContactsFromMembers, // run only once by the developers
605
- createContactsFromMembers,
606
- };