abmp-npm 1.8.44 → 1.9.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.
@@ -0,0 +1,545 @@
1
+ const { taskManager } = require('psdev-task-manager');
2
+
3
+ const { COLLECTIONS } = require('../../public/consts');
4
+ const { COMPILED_FILTERS_FIELDS, CONFIG_KEYS } = require('../consts');
5
+ const { wixData } = require('../elevated-modules');
6
+ const { updateWixMemberLoginEmail } = require('../members-area-methods');
7
+ const {
8
+ getAllEmptyAboutYouMembers,
9
+ getAllMembersWithExternalImages,
10
+ getMembersWithWixUrl,
11
+ getAllMembersWithoutContactFormEmail,
12
+ findMemberByWixDataId,
13
+ bulkSaveMembers,
14
+ getAllUpdatedLoginEmails,
15
+ getMembersByIds,
16
+ } = require('../members-data-methods');
17
+ const {
18
+ getCompleteStateList,
19
+ getAreasOfPracticeList,
20
+ getStateCityMap,
21
+ getCompiledFiltersOptions,
22
+ } = require('../search-filters-methods');
23
+ const { chunkArray, getSiteConfigs } = require('../utils');
24
+
25
+ const { TASKS_NAMES } = require('./consts');
26
+ const {
27
+ updateMemberRichContent,
28
+ updateMemberProfileImage,
29
+ getAWSTokens,
30
+ uploadMembersSitemap,
31
+ } = require('./tasks-helpers-methods');
32
+
33
+ const scheduleTaskForEmptyAboutYouMembers = async () => {
34
+ const createTasksFromMembers = members => {
35
+ const memberIds = members.map(member => member._id);
36
+ return {
37
+ name: TASKS_NAMES.convertHtmlToRichContent,
38
+ data: { memberIds },
39
+ type: 'scheduled',
40
+ };
41
+ };
42
+ const members = await getAllEmptyAboutYouMembers();
43
+ console.log('starting to schedule tasks for empty about you members');
44
+ const membersChunks = chunkArray(members, 1000);
45
+ for (const chunk of membersChunks) {
46
+ const toScheduleTask = createTasksFromMembers(chunk);
47
+ await taskManager().schedule(toScheduleTask);
48
+ }
49
+ };
50
+
51
+ //this funciton takes ~0.5 seconds per member
52
+ const convertAboutYouHtmlToRichContent = async membersIds => {
53
+ const result = {};
54
+
55
+ // Process members in chunks of 30
56
+ const chunks = chunkArray(membersIds, 30);
57
+
58
+ for (const chunk of chunks) {
59
+ console.log(`Processing chunk of ${chunk.length} members`);
60
+
61
+ // Process each chunk concurrently using Promise.all
62
+ const chunkPromises = chunk.map(async memberId => {
63
+ console.log('memberId ======', memberId);
64
+ try {
65
+ await updateMemberRichContent(memberId);
66
+ return { memberId, success: true };
67
+ } catch (error) {
68
+ console.error('error in updating member', error);
69
+ return { memberId, success: false };
70
+ }
71
+ });
72
+
73
+ // Wait for all promises in the chunk to complete
74
+ const chunkResults = await Promise.all(chunkPromises);
75
+
76
+ // Update result object with chunk results
77
+ chunkResults.forEach(({ memberId, success }) => {
78
+ result[memberId] = success;
79
+ });
80
+ }
81
+
82
+ return result;
83
+ };
84
+
85
+ async function compileFiltersOptions(field) {
86
+ const getNonCompiledFilter = field => {
87
+ const filterMap = {
88
+ [COMPILED_FILTERS_FIELDS.COMPILED_STATE_LIST]: getCompleteStateList,
89
+ [COMPILED_FILTERS_FIELDS.COMPILED_AREAS_OF_PRACTICES]: getAreasOfPracticeList,
90
+ [COMPILED_FILTERS_FIELDS.COMPILED_STATE_CITY_MAP]: getStateCityMap,
91
+ };
92
+ const filterFunction = filterMap[field];
93
+ if (!filterFunction) {
94
+ throw new Error(`Unknown filter field: ${field}`);
95
+ }
96
+ return filterFunction();
97
+ };
98
+ const [nonCompiledFilterData, compiledFiltersOptions] = await Promise.all([
99
+ getNonCompiledFilter(field),
100
+ getCompiledFiltersOptions(),
101
+ ]);
102
+ compiledFiltersOptions[field] = nonCompiledFilterData;
103
+ await wixData.save(COLLECTIONS.COMPILED_STATE_CITY_MAP, compiledFiltersOptions);
104
+ }
105
+
106
+ const scheduleTaskForExternalProfileImages = async () => {
107
+ const createImageMigrationTasksFromMembers = members => {
108
+ const memberIds = members.map(member => member._id);
109
+
110
+ return {
111
+ name: TASKS_NAMES.convertExternalProfilesToWixImages,
112
+ data: { memberIds },
113
+ type: 'scheduled',
114
+ };
115
+ };
116
+ const members = await getAllMembersWithExternalImages();
117
+ console.log('Starting to schedule tasks for external profile image migration');
118
+ const membersChunks = chunkArray(members, 300);
119
+ for (const chunk of membersChunks) {
120
+ const toScheduleTask = createImageMigrationTasksFromMembers(chunk);
121
+ await taskManager().schedule(toScheduleTask);
122
+ }
123
+ console.log(`Scheduled ${members.length} members for profile image migration`);
124
+ };
125
+
126
+ const convertExternalProfilesToWixImages = async membersIds => {
127
+ const result = {};
128
+
129
+ // Process members in chunks of 30 (optimal concurrent processing)
130
+ const chunks = chunkArray(membersIds, 30);
131
+
132
+ for (const chunk of chunks) {
133
+ console.log(`Processing profile image chunk of ${chunk.length} members`);
134
+
135
+ // Process each chunk concurrently using Promise.all
136
+ const chunkPromises = chunk.map(async memberId => {
137
+ console.log('Processing profile image for member:', memberId);
138
+ try {
139
+ const updateResult = await updateMemberProfileImage(memberId);
140
+ return {
141
+ memberId,
142
+ success: updateResult.success,
143
+ message: updateResult.message,
144
+ };
145
+ } catch (error) {
146
+ console.error('Error updating member profile image:', error);
147
+ return { memberId, success: false, error: error.message };
148
+ }
149
+ });
150
+
151
+ const chunkResults = await Promise.all(chunkPromises);
152
+
153
+ // Log results for this chunk
154
+ chunkResults.forEach(result => {
155
+ if (result.success) {
156
+ console.log(`✅ Successfully processed profile image for member ${result.memberId}`);
157
+ } else {
158
+ console.error(
159
+ `❌ Failed to process profile image for member ${result.memberId}: ${result.error}`
160
+ );
161
+ }
162
+ });
163
+
164
+ // Add small delay between chunks to avoid overwhelming the media manager
165
+ await new Promise(resolve => setTimeout(resolve, 2000));
166
+ }
167
+
168
+ return result;
169
+ };
170
+ const updateSiteMapS3 = async () => {
171
+ const relevantMembers = await getMembersWithWixUrl();
172
+ console.log('number of profiles to upload', relevantMembers.length);
173
+ const [tokens, siteAssociation] = await Promise.all([
174
+ getAWSTokens(),
175
+ getSiteConfigs(CONFIG_KEYS.SITE_ASSOCIATION),
176
+ ]);
177
+ // const creds = await getNewStsSessionToken(tokens.AWS_ACCESS_KEY_ID, tokens.AWS_SECRET_ACCESS_KEY, 3600);
178
+ // console.log("creds",creds); // verify it’s fresh
179
+ try {
180
+ const chunkSize = 50000;
181
+ console.log('Total items will be split into', relevantMembers.length / chunkSize);
182
+ const chunks = chunkArray(relevantMembers, chunkSize);
183
+ console.log(`Uploading ${chunks.length} sitemap files...`);
184
+ for (let i = 0; i < chunks.length; i++) {
185
+ const index = i + 1;
186
+ const fileName = `profiles-sitemap-${index}.xml`;
187
+ console.log(
188
+ `Uploading chunk ${index}/${chunks.length} -> ${fileName} (${chunks[i].length} items)`
189
+ );
190
+ await uploadMembersSitemap({
191
+ members: chunks[i],
192
+ tokens,
193
+ destinationFileName: fileName,
194
+ siteAssociation,
195
+ });
196
+ console.log(`Uploaded ${fileName}`);
197
+ }
198
+ console.log('All sitemap files uploaded successfully');
199
+ } catch (e) {
200
+ console.error('Sitemap upload failed:', e?.message || e);
201
+ }
202
+ };
203
+
204
+ /**
205
+ * Schedules tasks to migrate contactFormEmail for all members who don't have it set
206
+ * This function gets all members missing contactFormEmail and schedules batch processing tasks
207
+ */
208
+ const scheduleContactFormEmailMigration = async () => {
209
+ try {
210
+ console.log('Starting to schedule contactFormEmail migration tasks');
211
+ const createContactFormEmailMigrationTask = (members, chunkIndex) => {
212
+ const memberIds = members.map(member => member._id);
213
+ return {
214
+ name: TASKS_NAMES.migrateContactFormEmails,
215
+ data: { memberIds, chunkIndex },
216
+ type: 'scheduled',
217
+ };
218
+ };
219
+ const membersToMigrate = await getAllMembersWithoutContactFormEmail();
220
+ console.log(`Found ${membersToMigrate.length} members needing contactFormEmail migration`);
221
+ if (membersToMigrate.length === 0) {
222
+ console.log('No members need contactFormEmail migration');
223
+ return {
224
+ success: true,
225
+ message: 'No members need migration',
226
+ totalMembers: 0,
227
+ tasksScheduled: 0,
228
+ };
229
+ }
230
+
231
+ // Process in chunks of 500 members per task (smaller chunks for reliability)
232
+ const membersChunks = chunkArray(membersToMigrate, 500);
233
+ console.log(`Creating ${membersChunks.length} migration tasks`);
234
+
235
+ for (let chunkIndex = 0; chunkIndex < membersChunks.length; chunkIndex++) {
236
+ const chunk = membersChunks[chunkIndex];
237
+ const migrationTask = createContactFormEmailMigrationTask(chunk, chunkIndex);
238
+ await taskManager().schedule(migrationTask);
239
+ console.log(`Scheduled task for chunk ${chunkIndex} with ${chunk.length} members`);
240
+ }
241
+
242
+ console.log(
243
+ `Successfully scheduled ${membersChunks.length} tasks for ${membersToMigrate.length} members`
244
+ );
245
+
246
+ return {
247
+ success: true,
248
+ message: `Scheduled ${membersChunks.length} tasks for ${membersToMigrate.length} members`,
249
+ totalMembers: membersToMigrate.length,
250
+ tasksScheduled: membersChunks.length,
251
+ };
252
+ } catch (error) {
253
+ const errorMessage = `Failed to schedule contactFormEmail migration: ${error.message}`;
254
+ console.error(errorMessage);
255
+ throw new Error(errorMessage);
256
+ }
257
+ };
258
+
259
+ /**
260
+ * Migrates contactFormEmail for a batch of members
261
+ * Sets contactFormEmail to the same value as the member's current email
262
+ * @param {Object} data - Data object with memberIds and chunkIndex
263
+ * @param {Array} data.memberIds - Array of member IDs to process
264
+ * @param {number} data.chunkIndex - Index of the chunk
265
+ * @returns {Promise<Object>} - Result object with success/failure counts
266
+ */
267
+ const migrateContactFormEmails = async data => {
268
+ const { memberIds, chunkIndex } = data;
269
+ const result = {
270
+ successful: 0,
271
+ failed: 0,
272
+ errors: [],
273
+ skipped: 0,
274
+ skippedIds: [],
275
+ };
276
+
277
+ console.log(
278
+ `Starting contactFormEmail migration for ${memberIds.length} members in chunk ${chunkIndex}`
279
+ );
280
+
281
+ try {
282
+ // Get all members for this batch
283
+ const memberPromises = memberIds.map(async memberId => {
284
+ try {
285
+ const member = await findMemberByWixDataId(memberId);
286
+
287
+ // Skip if member already has contactFormEmail set
288
+ if (member.contactFormEmail) {
289
+ console.log(`Member ${memberId} already has contactFormEmail set`);
290
+ result.skipped++;
291
+ result.skippedIds.push(memberId);
292
+ return null;
293
+ }
294
+
295
+ // Skip if member doesn't have email
296
+ if (!member.email) {
297
+ console.log(`Member ${memberId} doesn't have email - skipping`);
298
+ result.skipped++;
299
+ result.skippedIds.push(memberId);
300
+ return null;
301
+ }
302
+
303
+ return {
304
+ ...member,
305
+ contactFormEmail: member.email,
306
+ };
307
+ } catch (error) {
308
+ console.error(`Error preparing member ${memberId}:`, error);
309
+ result.failed++;
310
+ result.errors.push({ memberId, error: error.message });
311
+ return null;
312
+ }
313
+ });
314
+
315
+ const membersToUpdate = (await Promise.all(memberPromises)).filter(Boolean);
316
+
317
+ if (membersToUpdate.length === 0) {
318
+ console.log('No members need updating in this batch');
319
+ return result;
320
+ }
321
+
322
+ console.log(
323
+ `Started Updating ${membersToUpdate.length} members with contactFormEmail in chunk ${chunkIndex}`
324
+ );
325
+
326
+ // Process in smaller chunks for bulk update (1000 is Wix limit)
327
+ const updateChunks = chunkArray(membersToUpdate, 1000);
328
+
329
+ for (let chunkIndex = 0; chunkIndex < updateChunks.length; chunkIndex++) {
330
+ const chunk = updateChunks[chunkIndex];
331
+ try {
332
+ await bulkSaveMembers(chunk);
333
+ result.successful += chunk.length;
334
+ console.log(`✅ Successfully updated ${chunk.length} members in chunk ${chunkIndex}`);
335
+ } catch (error) {
336
+ console.error(`❌ Error updating chunk ${chunkIndex}:`, error);
337
+ result.failed += chunk.length;
338
+ result.errors.push({
339
+ chunk: chunkIndex,
340
+ error: error.message,
341
+ memberCount: chunk.length,
342
+ });
343
+ }
344
+ }
345
+ } catch (error) {
346
+ const errorMessage = `Failed to migrate contactFormEmail for chunk ${chunkIndex}: ${error.message}`;
347
+ console.error(errorMessage);
348
+ throw new Error(errorMessage);
349
+ }
350
+
351
+ console.log(
352
+ `ContactFormEmail migration task completed: ${result.successful} successful, ${result.failed} failed, ${result.skipped} skipped, in chunk ${chunkIndex}`
353
+ );
354
+ return result;
355
+ };
356
+
357
+ /**
358
+ Schedules tasks to sync updated emails from the updated login emails database
359
+ * This function gets all updated emails and schedules batch processing tasks
360
+ */
361
+ const scheduleEmailSync = async () => {
362
+ try {
363
+ console.log('Starting to schedule email sync tasks');
364
+ const createEmailSyncTask = (chunk, chunkIndex) => {
365
+ //To reduce stored Items size inside task data
366
+ const emailUpdates = chunk.map(emailUpdate => ({
367
+ memberId: emailUpdate.memberId,
368
+ loginEmail: emailUpdate.loginEmail,
369
+ }));
370
+ return {
371
+ name: TASKS_NAMES.syncMemberLoginEmails,
372
+ data: { emailUpdates, chunkIndex },
373
+ type: 'scheduled',
374
+ };
375
+ };
376
+ const updatedEmails = await getAllUpdatedLoginEmails();
377
+ console.log(`Found ${updatedEmails.length} updated email records`);
378
+
379
+ if (updatedEmails.length === 0) {
380
+ console.log('No updated emails found');
381
+ return {
382
+ success: true,
383
+ message: 'No updated emails to sync',
384
+ totalEmails: 0,
385
+ tasksScheduled: 0,
386
+ };
387
+ }
388
+
389
+ const emailChunks = chunkArray(updatedEmails, 500);
390
+ console.log(`Creating ${emailChunks.length} email sync tasks`);
391
+
392
+ for (let chunkIndex = 0; chunkIndex < emailChunks.length; chunkIndex++) {
393
+ const chunk = emailChunks[chunkIndex];
394
+ const syncTask = createEmailSyncTask(chunk, chunkIndex);
395
+ await taskManager().schedule(syncTask);
396
+ console.log(`Scheduled task for chunk ${chunkIndex} with ${chunk.length} email updates`);
397
+ }
398
+
399
+ console.log(
400
+ `Successfully scheduled ${emailChunks.length} tasks for ${updatedEmails.length} email updates`
401
+ );
402
+
403
+ return {
404
+ success: true,
405
+ message: `Scheduled ${emailChunks.length} tasks for ${updatedEmails.length} email updates`,
406
+ totalEmails: updatedEmails.length,
407
+ tasksScheduled: emailChunks.length,
408
+ };
409
+ } catch (error) {
410
+ const errorMessage = `Failed to schedule email sync: ${error.message}`;
411
+ console.error(errorMessage);
412
+ throw new Error(errorMessage);
413
+ }
414
+ };
415
+
416
+ /**
417
+ * Syncs member emails with updated login emails
418
+ * @param {Object} data - Data object with emailUpdates and chunkIndex
419
+ * @param {Array} data.emailUpdates - Array of email update objects with memberId and loginEmail
420
+ * @param {number} data.chunkIndex - Index of the chunk
421
+ * @returns {Object} - Result object with success/failure counts
422
+ */
423
+ const syncMemberLoginEmails = async data => {
424
+ const { emailUpdates, chunkIndex } = data;
425
+ const result = {
426
+ successful: 0,
427
+ failed: 0,
428
+ skipped: 0,
429
+ skippedIds: [],
430
+ missingMemberIds: [],
431
+ errors: [],
432
+ };
433
+
434
+ console.log(
435
+ `Starting email sync for ${emailUpdates.length} email updates in chunk ${chunkIndex}`
436
+ );
437
+
438
+ try {
439
+ const memberIds = emailUpdates.map(update => update.memberId);
440
+
441
+ const existingMembers = await getMembersByIds(memberIds);
442
+ console.log(`Found ${existingMembers.length} existing members to update`);
443
+ const existingMemberIds = new Set(existingMembers.map(member => member.memberId));
444
+ const missingMemberIds = memberIds.filter(memberId => !existingMemberIds.has(memberId));
445
+
446
+ // Add missing member IDs to skipped count and log them
447
+ if (missingMemberIds.length > 0) {
448
+ console.log(
449
+ `Found ${missingMemberIds.length} members in emailUpdates but not in database:`,
450
+ missingMemberIds
451
+ );
452
+ result.missingMemberIds = result.missingMemberIds || [];
453
+ result.missingMemberIds.push(...missingMemberIds);
454
+ }
455
+ const emailUpdateMap = new Map();
456
+ emailUpdates.forEach(update => {
457
+ emailUpdateMap.set(update.memberId, update.loginEmail);
458
+ });
459
+
460
+ const membersToUpdate = [];
461
+
462
+ for (const member of existingMembers) {
463
+ const newEmail = emailUpdateMap.get(member.memberId);
464
+
465
+ if (!newEmail) {
466
+ console.log(`No email update found for member ${member.memberId}`);
467
+ result.skipped++;
468
+ result.skippedIds.push(member.memberId);
469
+ continue;
470
+ }
471
+
472
+ if (member.email === newEmail) {
473
+ console.log(`Email already up to date for member ${member.memberId}`);
474
+ result.skipped++;
475
+ result.skippedIds.push(member.memberId);
476
+ continue;
477
+ }
478
+
479
+ membersToUpdate.push({
480
+ ...member,
481
+ email: newEmail,
482
+ });
483
+ }
484
+
485
+ if (membersToUpdate.length === 0) {
486
+ console.log('No members need email updates in this batch', chunkIndex);
487
+ return result;
488
+ }
489
+
490
+ console.log(
491
+ `Updating ${membersToUpdate.length} members with new emails in chunk ${chunkIndex}`
492
+ );
493
+
494
+ const updateChunks = chunkArray(membersToUpdate, 1000);
495
+
496
+ for (const chunk of updateChunks) {
497
+ try {
498
+ await bulkSaveMembers(chunk);
499
+
500
+ for (const member of chunk) {
501
+ await updateWixMemberLoginEmail(member, result);
502
+ }
503
+
504
+ result.successful += chunk.length;
505
+ console.log(`✅ Successfully updated ${chunkIndex} ${chunk.length} members`);
506
+ } catch (error) {
507
+ console.error(`❌ Error updating chunk ${chunkIndex}:`, error);
508
+ result.failed += chunk.length;
509
+ result.errors.push({
510
+ chunk: chunkIndex,
511
+ error: error.message,
512
+ memberCount: chunk.length,
513
+ });
514
+ }
515
+ }
516
+ // Log comprehensive results including Wix member updates
517
+ const wixStats = result.wixMemberUpdates || { successful: 0, failed: 0 };
518
+ console.log(`Login Emails sync task completed:`);
519
+ console.log(
520
+ ` - Member data updates: ${result.successful} successful, ${result.failed} failed, ${result.skipped} skipped`
521
+ );
522
+ console.log(
523
+ ` - Wix member login emails: ${wixStats.successful} successful, ${wixStats.failed} failed`
524
+ );
525
+
526
+ return result;
527
+ } catch (error) {
528
+ const errorMessage = `Failed to syncMemberLoginEmails for chunk ${chunkIndex} of length ${emailUpdates.length} with error: ${error.message}`;
529
+ console.error(errorMessage);
530
+ throw new Error(errorMessage);
531
+ }
532
+ };
533
+
534
+ module.exports = {
535
+ scheduleTaskForEmptyAboutYouMembers,
536
+ convertAboutYouHtmlToRichContent,
537
+ compileFiltersOptions,
538
+ scheduleTaskForExternalProfileImages,
539
+ convertExternalProfilesToWixImages,
540
+ updateSiteMapS3,
541
+ scheduleContactFormEmailMigration,
542
+ migrateContactFormEmails,
543
+ scheduleEmailSync,
544
+ syncMemberLoginEmails,
545
+ };
package/backend/utils.js CHANGED
@@ -1,10 +1,13 @@
1
+ const { auth } = require('@wix/essentials');
2
+ const { secrets } = require('@wix/secrets');
3
+ const { site } = require('@wix/urls');
1
4
  const { encode } = require('ngeohash');
2
5
 
3
6
  const { COLLECTIONS } = require('../public/consts');
4
7
 
5
8
  const { CONFIG_KEYS, GEO_HASH_PRECISION } = require('./consts');
6
9
  const { wixData } = require('./elevated-modules');
7
- const { urlExists } = require('./members-data-methods');
10
+ const elevatedGetSecretValue = auth.elevate(secrets.getSecretValue);
8
11
 
9
12
  /**
10
13
  * Retrieves site configuration values from the database
@@ -95,17 +98,17 @@ const queryAllItems = async query => {
95
98
  return allItems;
96
99
  };
97
100
  /**
98
- * Batches large arrays into smaller chunks for processing
99
- * @param {Array} array - Array to batch
100
- * @param {number} batchSize - Size of each batch
101
- * @returns {Array} - Array of batches
101
+ * Chunks large arrays into smaller chunks for processing
102
+ * @param {Array} array - Array to chunk
103
+ * @param {number} chunkSize - Size of each chunk
104
+ * @returns {Array} - Array of chunks
102
105
  */
103
- const createBatches = (array, batchSize = 50) => {
104
- const batches = [];
105
- for (let i = 0; i < array.length; i += batchSize) {
106
- batches.push(array.slice(i, i + batchSize));
106
+ const chunkArray = (array, chunkSize = 50) => {
107
+ const chunks = [];
108
+ for (let i = 0; i < array.length; i += chunkSize) {
109
+ chunks.push(array.slice(i, i + chunkSize));
107
110
  }
108
- return batches;
111
+ return chunks;
109
112
  };
110
113
 
111
114
  const generateGeoHash = addresses => {
@@ -128,38 +131,54 @@ const normalizeUrlForComparison = url => {
128
131
  return url.toLowerCase().replace(/-\d+$/, '');
129
132
  };
130
133
 
131
- /**
132
- * Checks URL uniqueness for a member
133
- * @param {string} url - The URL to check
134
- * @param {string} memberId - The member ID to exclude from the check
135
- * @returns {Promise<Object>} Result object with isUnique boolean
136
- */
137
- async function checkUrlUniqueness(url, memberId) {
138
- if (!url || !memberId) {
139
- throw new Error('Missing required parameters: url and memberId are required');
140
- }
134
+ async function getSecret(secretKey) {
135
+ return await elevatedGetSecretValue(secretKey).value;
136
+ }
141
137
 
138
+ async function getSiteBaseUrl() {
142
139
  try {
143
- const trimmedUrl = url.trim();
144
- const exists = await urlExists(trimmedUrl, memberId);
145
-
146
- return { isUnique: !exists };
140
+ const result = await site.listPublishedSiteUrls({
141
+ filters: { primary: true },
142
+ });
143
+ const baseUrl = result.urls[0].url;
144
+ if (!baseUrl) {
145
+ throw new Error('No Base URL Found');
146
+ }
147
+ return baseUrl;
147
148
  } catch (error) {
148
- console.error('Error checking URL uniqueness:', error);
149
- throw new Error(`Failed to check URL uniqueness: ${error.message}`);
149
+ throw new Error(`Failed to get site base URL: ${error?.message || error}`);
150
150
  }
151
151
  }
152
152
 
153
+ function encodeXml(value) {
154
+ if (!value) return '';
155
+ return (
156
+ String(value)
157
+ .replace(/&/g, '&amp;')
158
+ .replace(/</g, '&lt;')
159
+ .replace(/>/g, '&gt;')
160
+ // eslint-disable-next-line no-useless-escape
161
+ .replace(/\"/g, '&quot;')
162
+ .replace(/'/g, '&apos;')
163
+ );
164
+ }
165
+
166
+ function formatDateOnly(dateStr) {
167
+ return new Date(dateStr).toISOString().slice(0, 10);
168
+ }
153
169
  module.exports = {
154
170
  getSiteConfigs,
155
171
  retrieveAllItems,
156
- createBatches,
172
+ chunkArray,
157
173
  generateGeoHash,
158
174
  isValidArray,
159
175
  normalizeUrlForComparison,
160
176
  queryAllItems,
161
- checkUrlUniqueness,
162
177
  formatDateToMonthYear,
163
178
  isStudent,
164
179
  getAddressDisplayOptions,
180
+ getSecret,
181
+ getSiteBaseUrl,
182
+ encodeXml,
183
+ formatDateOnly,
165
184
  };
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "abmp-npm",
3
- "version": "1.8.44",
3
+ "version": "1.9.1",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
+ "check-cycles": "madge --circular .",
6
7
  "test": "echo \"Error: no test specified\" && exit 1",
7
- "lint": "eslint .",
8
+ "lint": "npm run check-cycles && eslint .",
8
9
  "lint:fix": "eslint . --fix",
9
10
  "format": "prettier --write \"**/*.{js,json,md}\"",
10
11
  "format:check": "prettier --check \"**/*.{js,json,md}\"",
@@ -23,6 +24,7 @@
23
24
  "eslint-plugin-promise": "^7.1.0",
24
25
  "globals": "^15.10.0",
25
26
  "husky": "^9.1.6",
27
+ "madge": "^8.0.0",
26
28
  "prettier": "^3.3.3"
27
29
  },
28
30
  "dependencies": {
@@ -30,10 +32,19 @@
30
32
  "@wix/crm": "^1.0.1061",
31
33
  "@wix/data": "^1.0.303",
32
34
  "@wix/essentials": "^0.1.28",
35
+ "@wix/identity": "^1.0.178",
36
+ "@wix/media": "^1.0.213",
33
37
  "@wix/members": "^1.0.365",
34
38
  "@wix/secrets": "^1.0.62",
35
39
  "@wix/site-location": "^1.31.0",
40
+ "@wix/site-members": "^1.32.0",
41
+ "@wix/site-storage": "^1.22.0",
36
42
  "@wix/site-window": "^1.44.0",
43
+ "@wix/urls": "^1.0.57",
44
+ "aws4": "^1.13.2",
45
+ "axios": "^1.13.1",
46
+ "crypto": "^1.0.1",
47
+ "jwt-js-decode": "^1.9.0",
37
48
  "lodash": "^4.17.21",
38
49
  "ngeohash": "^0.6.3",
39
50
  "phone": "^3.1.67",