abmp-npm 2.0.16 → 2.0.18

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.
@@ -9,9 +9,10 @@ const { bulkProcessAndSaveMemberData } = require('./bulk-process-methods');
9
9
  const { SITES_WITH_INTERESTS_TO_MIGRATE } = require('./consts');
10
10
  const { isUpdatedMember, isSiteAssociatedMember } = require('./utils');
11
11
 
12
- async function syncMembersDataPerAction(action) {
12
+ async function syncMembersDataPerAction(taskData) {
13
+ const { action, backupDate } = taskData;
13
14
  try {
14
- const firstPageResponse = await fetchPACMembers(1, action);
15
+ const firstPageResponse = await fetchPACMembers({ page: 1, action, backupDate });
15
16
 
16
17
  if (
17
18
  !firstPageResponse ||
@@ -48,6 +49,7 @@ async function syncMembersDataPerAction(action) {
48
49
  data: {
49
50
  pageNumber,
50
51
  action,
52
+ ...(backupDate ? { backupDate } : {}),
51
53
  },
52
54
  type: 'scheduled',
53
55
  }));
@@ -74,11 +76,11 @@ async function syncMembersDataPerAction(action) {
74
76
  * @returns {Promise<Object>} - Page synchronization result
75
77
  */
76
78
  async function synchronizeSinglePage(taskObject) {
77
- const { pageNumber, action } = taskObject.data;
79
+ const { pageNumber, action, backupDate } = taskObject.data;
78
80
  try {
79
81
  const [siteAssociation, memberDataResponse] = await Promise.all([
80
82
  getSiteConfigs(CONFIG_KEYS.SITE_ASSOCIATION),
81
- fetchPACMembers(pageNumber, action),
83
+ fetchPACMembers({ page: pageNumber, action, backupDate }),
82
84
  ]);
83
85
  const addInterests = SITES_WITH_INTERESTS_TO_MIGRATE.includes(siteAssociation);
84
86
  if (
package/backend/jobs.js CHANGED
@@ -13,12 +13,18 @@ async function runScheduledTasks() {
13
13
  }
14
14
  }
15
15
 
16
- async function scheduleDailyPullTask() {
16
+ /**
17
+ * Schedule a daily pull task for the given backup date
18
+ * @param {string} backupDate - Optional. The date of the backup to pull in format YYYY-MM-DD
19
+ * @returns {Promise<void>}
20
+ */
21
+ async function scheduleDailyPullTask(backupDate = null) {
17
22
  try {
18
23
  console.log('scheduleDailyPullTask started!');
24
+ console.log(`backupDate: ${backupDate}`);
19
25
  return await taskManager().schedule({
20
26
  name: TASKS_NAMES.ScheduleDailyMembersDataSync,
21
- data: {},
27
+ data: backupDate ? { backupDate } : {}, // keeping it like this so it would be easier to understand which task was backed up which is not while looking into CMS.
22
28
  type: 'scheduled',
23
29
  });
24
30
  } catch (error) {
@@ -1,4 +1,5 @@
1
1
  const { COLLECTIONS } = require('../public/consts');
2
+ const { isWixHostedImage } = require('../public/Utils/sharedUtils');
2
3
 
3
4
  const { MEMBERSHIPS_TYPES } = require('./consts');
4
5
  const { updateMemberContactInfo } = require('./contacts-methods');
@@ -318,9 +319,9 @@ async function getAllMembersWithExternalImages() {
318
319
 
319
320
  const allItems = await queryAllItems(membersQuery);
320
321
 
321
- // Filter for external images (not starting with 'wix:')
322
+ // Filter for external images (not 'wix hosted images')
322
323
  const membersWithExternalImages = allItems.filter(
323
- member => member.profileImage && !member.profileImage.startsWith('wix:')
324
+ member => member.profileImage && !isWixHostedImage(member.profileImage)
324
325
  );
325
326
 
326
327
  return membersWithExternalImages;
@@ -1,4 +1,4 @@
1
- const { PAC_API_URL } = require('./consts');
1
+ const { PAC_API_URL, BACKUP_API_URL } = require('./consts');
2
2
  const { getSecret } = require('./utils');
3
3
 
4
4
  const getHeaders = async () => {
@@ -8,8 +8,22 @@ const getHeaders = async () => {
8
8
  };
9
9
  return headers;
10
10
  };
11
- const fetchPACMembers = async (pageNum, actionFilter) => {
12
- const url = `${PAC_API_URL}/Members?page=${pageNum}&actionFilter=${actionFilter}`;
11
+ /**
12
+ *
13
+ * @param {*} params
14
+ * @param {number} params.page - The page number to fetch
15
+ * @param {string} params.action - The action to fetch
16
+ * @param {string} [params.backupDate] - Optional. The backup date to fetch in format YYYY-MM-DD, use only to fetch from backup endpoint not from PAC endpoint.
17
+ * @returns {Promise<Object>} - The response from the API
18
+ */
19
+ const fetchPACMembers = async ({ page, action, backupDate }) => {
20
+ const baseUrl = backupDate ? BACKUP_API_URL : PAC_API_URL;
21
+ const queryParams = { page, actionFilter: action };
22
+ if (backupDate) {
23
+ queryParams.date = backupDate;
24
+ }
25
+ const url = `${baseUrl}/Members?${new URLSearchParams(queryParams).toString()}`;
26
+ console.log(`Fetching PAC members from: ${url}`);
13
27
  const headers = await getHeaders();
14
28
  const fetchOptions = {
15
29
  method: 'get',
@@ -18,14 +32,14 @@ const fetchPACMembers = async (pageNum, actionFilter) => {
18
32
  const response = await fetch(url, fetchOptions);
19
33
  const responseType = response.headers.get('content-type');
20
34
  if (!responseType.includes('application/json')) {
21
- const errorMessage = `[fetchPACMembers] got invalid responseType: ${responseType} for page ${pageNum} and actionFilter ${actionFilter}`;
35
+ const errorMessage = `[fetchPACMembers] got invalid responseType: ${responseType} for page ${page} and actionFilter ${action}`;
22
36
  console.error(errorMessage);
23
37
  throw new Error(errorMessage);
24
38
  }
25
39
  if (response.ok) {
26
40
  return response.json();
27
41
  } else {
28
- const errorMessage = `[fetchPACMembers] failed with status ${response.status} for page ${pageNum} and actionFilter ${actionFilter}`;
42
+ const errorMessage = `[fetchPACMembers] failed with status ${response.status} for page ${page} and actionFilter ${action}`;
29
43
  console.error(errorMessage);
30
44
  throw new Error(errorMessage);
31
45
  }
@@ -1,4 +1,5 @@
1
1
  const { PAGES_PATHS } = require('../../public/consts');
2
+ const { isWixHostedImage } = require('../../public/Utils/sharedUtils');
2
3
  //const { fetchAllItemsInParallel } = require('../cms-data-methods'); unused at host site
3
4
  const { CONFIG_KEYS } = require('../consts');
4
5
  const { getSiteConfigs } = require('../utils');
@@ -27,7 +28,11 @@ const createRoutersHandlers = wixRouterMethods => {
27
28
  const defaultProfileImage = siteConfigs[CONFIG_KEYS.DEFAULT_PROFILE_IMAGE];
28
29
  const profileData = await getMemberProfileData(slug, siteAssociation);
29
30
  if (profileData && profileData.showWixUrl) {
30
- const ogImage = profileData.profileImage || profileData.logoImage || siteLogoUrl;
31
+ const profileImage =
32
+ profileData.profileImage?.trim() && isWixHostedImage(profileData.profileImage)
33
+ ? profileData.profileImage
34
+ : defaultProfileImage;
35
+ const ogImage = profileImage || profileData.logoImage || siteLogoUrl;
31
36
  const seoTitle = generateSEOTitle({
32
37
  fullName: profileData.fullName,
33
38
  areasOfPractices: profileData.areasOfPractices,
@@ -43,7 +43,7 @@ const TASKS = {
43
43
  },
44
44
  [TASKS_NAMES.ScheduleMembersDataPerAction]: {
45
45
  name: TASKS_NAMES.ScheduleMembersDataPerAction,
46
- getIdentifier: task => task.data.action,
46
+ getIdentifier: task => task.data,
47
47
  process: syncMembersDataPerAction,
48
48
  shouldSkipCheck: () => false,
49
49
  estimatedDurationSec: 6,
@@ -1,29 +1,36 @@
1
1
  const crypto = require('crypto');
2
2
 
3
+ const { auth } = require('@wix/essentials');
3
4
  const { files } = require('@wix/media');
4
5
  const aws4 = require('aws4');
5
6
  const axios = require('axios');
6
7
 
8
+ const elevatedGenerateFileUploadUrl = auth.elevate(files.generateFileUploadUrl);
9
+
7
10
  const { PAGES_PATHS } = require('../../public/consts');
11
+ const { isWixHostedImage } = require('../../public/Utils/sharedUtils');
8
12
  const { findMemberByWixDataId, updateMember } = require('../members-data-methods');
9
13
  const { getSecret, getSiteBaseUrl, encodeXml, formatDateOnly } = require('../utils');
10
-
11
14
  async function getServerlessAuth() {
12
15
  const serverlessAuth = await getSecret('serverless_auth');
13
16
  return serverlessAuth;
14
17
  }
15
18
 
16
19
  function isValidImageUrl(url) {
20
+ console.log('url', url);
21
+ console.log('typeof url', typeof url);
17
22
  if (!url || typeof url !== 'string') return false;
18
23
 
19
24
  // Check for valid URL format
20
25
  let parsedUrl;
21
26
  try {
22
27
  parsedUrl = new URL(url);
28
+ console.log('parsedUrl', parsedUrl);
23
29
  } catch {
24
30
  return false;
25
31
  }
26
-
32
+ console.log('parsedUrl', parsedUrl);
33
+ console.log('parsedUrl.protocol', parsedUrl.protocol);
27
34
  // Only allow HTTP and HTTPS protocols (reject blob:, data:, file:, etc.)
28
35
  const validProtocols = ['http:', 'https:'];
29
36
  if (!validProtocols.includes(parsedUrl.protocol)) {
@@ -33,10 +40,10 @@ function isValidImageUrl(url) {
33
40
  // Extract file extension from URL (handle query parameters)
34
41
  const urlPath = url.split('?')[0].toLowerCase();
35
42
  const validExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.webp', '.bmp'];
36
-
43
+ console.log('urlPath', urlPath);
37
44
  // Check if URL ends with valid extension
38
45
  const hasValidExtension = validExtensions.some(ext => urlPath.endsWith(ext));
39
-
46
+ console.log('hasValidExtension', hasValidExtension);
40
47
  // Reject obviously invalid extensions
41
48
  const invalidExtensions = [
42
49
  '.pdf',
@@ -51,7 +58,7 @@ function isValidImageUrl(url) {
51
58
  '_gif',
52
59
  ];
53
60
  const hasInvalidExtension = invalidExtensions.some(ext => urlPath.includes(ext));
54
-
61
+ console.log('hasInvalidExtension', hasInvalidExtension);
55
62
  return hasValidExtension && !hasInvalidExtension;
56
63
  }
57
64
 
@@ -138,20 +145,23 @@ async function updateMemberRichContent(memberId) {
138
145
  async function updateMemberProfileImage(memberId) {
139
146
  try {
140
147
  const member = await findMemberByWixDataId(memberId);
141
-
148
+ const trimmedProfileImage = member.profileImage?.trim();
142
149
  // Check if member has an external profile image URL
143
- if (!member.profileImage || member.profileImage.startsWith('wix:')) {
150
+ if (!trimmedProfileImage || isWixHostedImage(trimmedProfileImage)) {
144
151
  console.log(`Member ${memberId} already has Wix-hosted image or no image`);
145
152
  return { success: true, message: 'No update needed' };
146
153
  }
147
154
 
148
155
  // Validate image URL format before attempting download
149
- if (!isValidImageUrl(member.profileImage)) {
150
- console.log(`Member ${memberId} has invalid image URL format: ${member.profileImage}`);
156
+ if (!isValidImageUrl(trimmedProfileImage)) {
157
+ console.log(`Member ${memberId} has invalid image URL format: ${trimmedProfileImage}`);
151
158
  return { success: true, message: 'Invalid image URL format - skipped' };
152
159
  }
153
160
 
154
- const response = await axios.get(member.profileImage, {
161
+ // Encode URL to handle spaces and special characters in the path
162
+ const encodedImageUrl = encodeURI(trimmedProfileImage);
163
+
164
+ const response = await axios.get(encodedImageUrl, {
155
165
  responseType: 'arraybuffer',
156
166
  headers: {
157
167
  'User-Agent':
@@ -208,7 +218,7 @@ async function updateMemberProfileImage(memberId) {
208
218
 
209
219
  const sanitizedFileName = `profile-${memberId}-${Date.now()}.${extension}`.replace(/\./g, '_');
210
220
  const uploadUrl = (
211
- await files.generateFileUploadUrl(contentType, {
221
+ await elevatedGenerateFileUploadUrl(contentType, {
212
222
  fileName: sanitizedFileName,
213
223
  filePath: 'member-profiles',
214
224
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "abmp-npm",
3
- "version": "2.0.16",
3
+ "version": "2.0.18",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "check-cycles": "madge --circular .",
@@ -50,7 +50,7 @@
50
50
  "csv-parser": "^3.0.0",
51
51
  "ngeohash": "^0.6.3",
52
52
  "phone": "^3.1.67",
53
- "psdev-task-manager": "1.1.7",
53
+ "psdev-task-manager": "1.1.10",
54
54
  "psdev-utils": "1.1.1"
55
55
  }
56
56
  }
package/pages/Home.js CHANGED
@@ -9,6 +9,7 @@ const {
9
9
  getMainAddress,
10
10
  formatPracticeAreasForDisplay,
11
11
  checkAddressIsVisible,
12
+ isWixHostedImage,
12
13
  } = require('../public/Utils/sharedUtils.js');
13
14
 
14
15
  let filter = JSON.parse(JSON.stringify(DEFAULT_FILTER));
@@ -187,7 +188,7 @@ const homePageOnReady = async ({
187
188
  : [];
188
189
 
189
190
  // 2) Profile image
190
- if (itemData.profileImage) {
191
+ if (itemData.profileImage?.trim() && isWixHostedImage(itemData.profileImage)) {
191
192
  $item('#profileImage').src = itemData.profileImage;
192
193
  }
193
194
 
@@ -216,12 +217,19 @@ const homePageOnReady = async ({
216
217
  $item('#milesAwayText').text = '';
217
218
  }
218
219
 
219
- // 7) "Show maps" button enabled only if there's at least one visible address
220
+ // 7) "Show maps" button enabled only if there's a full address with valid coordinates
220
221
  const visible = checkAddressIsVisible(addresses);
221
- if (visible.length && visible[0].addressStatus === ADDRESS_STATUS_TYPES.FULL_ADDRESS) {
222
+ const fullAddressWithValidCoords = visible.find(
223
+ addr =>
224
+ addr.addressStatus === ADDRESS_STATUS_TYPES.FULL_ADDRESS &&
225
+ addr.latitude &&
226
+ addr.longitude
227
+ );
228
+
229
+ if (fullAddressWithValidCoords) {
222
230
  $item('#showMaps').enable();
223
231
  $item('#showMaps').show();
224
- const { latitude, longitude } = visible[0];
232
+ const { latitude, longitude } = fullAddressWithValidCoords;
225
233
  $item('#showMaps').link = `https://maps.google.com/?q=${latitude},${longitude}`;
226
234
  $item('#showMaps').target = '_blank';
227
235
  } else {
package/pages/Profile.js CHANGED
@@ -2,7 +2,11 @@ const { location: wixLocation } = require('@wix/site-location');
2
2
  const { window: wixWindow } = require('@wix/site-window');
3
3
 
4
4
  const { LIGHTBOX_NAMES } = require('../public/consts');
5
- const { generateId, formatPracticeAreasForDisplay } = require('../public/Utils/sharedUtils');
5
+ const {
6
+ generateId,
7
+ formatPracticeAreasForDisplay,
8
+ isWixHostedImage,
9
+ } = require('../public/Utils/sharedUtils');
6
10
 
7
11
  const TESTIMONIALS_PER_PAGE_CONFIG = {
8
12
  DESKTOP: 4,
@@ -117,7 +121,7 @@ async function profileOnReady({ $w: _$w }) {
117
121
  _$w('#logoImage').delete();
118
122
  }
119
123
 
120
- if (profileData.profileImage) {
124
+ if (profileData.profileImage && isWixHostedImage(profileData.profileImage)) {
121
125
  _$w('#profileImage').src = profileData.profileImage;
122
126
  } else {
123
127
  _$w('#profileImage').src = profileData.defaultProfileImage;
@@ -9,7 +9,7 @@ const {
9
9
  LIGHTBOX_NAMES,
10
10
  } = require('../public/consts');
11
11
  const { handleOnCustomValidation, isNotValidUrl } = require('../public/Utils/personalDetailsUtils');
12
- const { generateId } = require('../public/Utils/sharedUtils');
12
+ const { generateId, isWixHostedImage } = require('../public/Utils/sharedUtils');
13
13
 
14
14
  const MAX_PHONES_COUNT = 10;
15
15
  const MAX_ADDRESSES_COUNT = 10;
@@ -793,8 +793,14 @@ async function personalDetailsOnReady({
793
793
  : null
794
794
  : itemMemberObj[key];
795
795
 
796
- if (imageValue) {
797
- _$w(imageSelector).src = imageValue;
796
+ if (imageValue && imageValue?.trim()) {
797
+ // Only set profile image if it's Wix-hosted; other images will always be wix url
798
+ const isProfileImage = imageSelector === '#profileImage';
799
+ const shouldSetImage = !isProfileImage || isWixHostedImage(imageValue);
800
+
801
+ if (shouldSetImage) {
802
+ _$w(imageSelector).src = imageValue;
803
+ }
798
804
  _$w(nameSelector).text = formatFileName(extractFileName(imageValue));
799
805
  _$w(containerSelector).expand();
800
806
  uploadedImages[key === 'bannerImages' ? 'bannerImage' : key] = imageValue;
@@ -158,6 +158,13 @@ function generateId() {
158
158
  return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
159
159
  }
160
160
 
161
+ function isWixHostedImage(imageUrl) {
162
+ return (
163
+ imageUrl?.trim() &&
164
+ (imageUrl?.startsWith('wix:') || imageUrl?.startsWith('https://static.wixstatic.com'))
165
+ );
166
+ }
167
+
161
168
  module.exports = {
162
169
  checkAddressIsVisible,
163
170
  formatPracticeAreasForDisplay,
@@ -169,4 +176,5 @@ module.exports = {
169
176
  toRadians,
170
177
  generateId,
171
178
  formatAddress,
179
+ isWixHostedImage,
172
180
  };