abmp-npm 1.1.62 → 1.1.63
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.
- package/backend/consts.js +0 -4
- package/backend/daily-pull/sync-to-cms-methods.js +1 -1
- package/backend/index.js +1 -0
- package/backend/members-data-methods.js +1 -60
- package/backend/routers/index.js +3 -0
- package/backend/routers/methods.js +177 -0
- package/backend/routers/utils.js +118 -0
- package/backend/sso-methods.js +2 -75
- package/backend/utils.js +64 -15
- package/package.json +2 -8
- package/pages/index.js +0 -1
- package/public/Utils/sharedUtils.js +1 -0
- package/public/index.js +1 -0
- package/pages/LoadingPage.js +0 -20
- package/public/sso-auth-methods.js +0 -43
package/backend/consts.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
const PAC_API_URL = 'https://members.abmp.com/eweb/api/Wix';
|
|
2
|
-
const SSO_TOKEN_AUTH_API_URL = 'https://members.professionalassistcorp.com/';
|
|
3
|
-
const SSO_TOKEN_AUTH_API_KEY = 'testkey';
|
|
4
2
|
|
|
5
3
|
/**
|
|
6
4
|
* Valid configuration keys for getSiteConfigs function
|
|
@@ -40,6 +38,4 @@ module.exports = {
|
|
|
40
38
|
PAC_API_URL,
|
|
41
39
|
COMPILED_FILTERS_FIELDS,
|
|
42
40
|
MEMBERSHIPS_TYPES,
|
|
43
|
-
SSO_TOKEN_AUTH_API_URL,
|
|
44
|
-
SSO_TOKEN_AUTH_API_KEY,
|
|
45
41
|
};
|
|
@@ -2,7 +2,7 @@ const { taskManager } = require('psdev-task-manager');
|
|
|
2
2
|
|
|
3
3
|
const { CONFIG_KEYS } = require('../consts');
|
|
4
4
|
const { fetchPACMembers } = require('../pac-api-methods');
|
|
5
|
-
const { TASKS_NAMES } = require('../tasks
|
|
5
|
+
const { TASKS_NAMES } = require('../tasks');
|
|
6
6
|
const { getSiteConfigs } = require('../utils');
|
|
7
7
|
|
|
8
8
|
const { bulkProcessAndSaveMemberData } = require('./bulk-process-methods');
|
package/backend/index.js
CHANGED
|
@@ -9,6 +9,7 @@ module.exports = {
|
|
|
9
9
|
...require('./members-area-methods'), //TODO: remove it once we finish NPM movement
|
|
10
10
|
...require('./members-data-methods'), //TODO: remove it once we finish NPM movement
|
|
11
11
|
...require('./cms-data-methods'), //TODO: remove it once we finish NPM movement
|
|
12
|
+
...require('./routers'),
|
|
12
13
|
...require('./sso-methods'),
|
|
13
14
|
...require('./data-hooks'),
|
|
14
15
|
};
|
|
@@ -2,7 +2,7 @@ const { COLLECTIONS } = require('../public/consts');
|
|
|
2
2
|
|
|
3
3
|
const { MEMBERSHIPS_TYPES } = require('./consts');
|
|
4
4
|
const { updateMemberContactInfo } = require('./contacts-methods');
|
|
5
|
-
const { MEMBER_ACTIONS } = require('./daily-pull
|
|
5
|
+
const { MEMBER_ACTIONS } = require('./daily-pull');
|
|
6
6
|
const { wixData } = require('./elevated-modules');
|
|
7
7
|
const { createSiteMember } = require('./members-area-methods');
|
|
8
8
|
const {
|
|
@@ -296,27 +296,6 @@ async function urlExists(url, excludeMemberId) {
|
|
|
296
296
|
}
|
|
297
297
|
}
|
|
298
298
|
|
|
299
|
-
/**
|
|
300
|
-
* Checks URL uniqueness for a member
|
|
301
|
-
* @param {string} url - The URL to check
|
|
302
|
-
* @param {string} memberId - The member ID to exclude from the check
|
|
303
|
-
* @returns {Promise<Object>} Result object with isUnique boolean
|
|
304
|
-
*/
|
|
305
|
-
async function checkUrlUniqueness(url, memberId) {
|
|
306
|
-
if (!url || !memberId) {
|
|
307
|
-
throw new Error('Missing required parameters: url and memberId are required');
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
try {
|
|
311
|
-
const trimmedUrl = url.trim();
|
|
312
|
-
const exists = await urlExists(trimmedUrl, memberId);
|
|
313
|
-
|
|
314
|
-
return { isUnique: !exists };
|
|
315
|
-
} catch (error) {
|
|
316
|
-
console.error('Error checking URL uniqueness:', error);
|
|
317
|
-
throw new Error(`Failed to check URL uniqueness: ${error.message}`);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
299
|
/**
|
|
321
300
|
* Get all members with external profile images
|
|
322
301
|
* @returns {Promise<Array>} - Array of member IDs
|
|
@@ -421,42 +400,6 @@ const getMembersByIds = async memberIds => {
|
|
|
421
400
|
}
|
|
422
401
|
};
|
|
423
402
|
|
|
424
|
-
async function getSiteMemberId(data) {
|
|
425
|
-
try {
|
|
426
|
-
console.log('data', data);
|
|
427
|
-
const memberId = data?.pac?.cst_recno;
|
|
428
|
-
if (!memberId) {
|
|
429
|
-
const errorMessage = `Member ID is missing in passed data ${JSON.stringify(data)}`;
|
|
430
|
-
console.error(errorMessage);
|
|
431
|
-
throw new Error(errorMessage);
|
|
432
|
-
}
|
|
433
|
-
const queryMemberResult = await wixData
|
|
434
|
-
.query(COLLECTIONS.MEMBERS_DATA)
|
|
435
|
-
.eq('memberId', Number(memberId))
|
|
436
|
-
.find()
|
|
437
|
-
.then(res => res.items);
|
|
438
|
-
if (!queryMemberResult.length || queryMemberResult.length > 1) {
|
|
439
|
-
throw new Error(
|
|
440
|
-
`Invalid Members count found in DB for email ${data.email} members count is : [${
|
|
441
|
-
queryMemberResult.length
|
|
442
|
-
}] membersIds are : [${queryMemberResult.map(member => member.memberId).join(', ')}]`
|
|
443
|
-
);
|
|
444
|
-
}
|
|
445
|
-
let memberData = queryMemberResult[0];
|
|
446
|
-
console.log('memberData', memberData);
|
|
447
|
-
const isNewUser = !memberData.contactId;
|
|
448
|
-
if (isNewUser) {
|
|
449
|
-
const memberDataWithContactId = await createContactAndMemberIfNew(memberData);
|
|
450
|
-
console.log('memberDataWithContactId', memberDataWithContactId);
|
|
451
|
-
memberData = memberDataWithContactId;
|
|
452
|
-
}
|
|
453
|
-
return memberData;
|
|
454
|
-
} catch (error) {
|
|
455
|
-
console.error('Error in getSiteMemberId', error.message);
|
|
456
|
-
throw error;
|
|
457
|
-
}
|
|
458
|
-
}
|
|
459
|
-
|
|
460
403
|
module.exports = {
|
|
461
404
|
findMemberByWixDataId,
|
|
462
405
|
createContactAndMemberIfNew,
|
|
@@ -472,6 +415,4 @@ module.exports = {
|
|
|
472
415
|
getAllMembersWithoutContactFormEmail,
|
|
473
416
|
getAllUpdatedLoginEmails,
|
|
474
417
|
getMembersByIds,
|
|
475
|
-
getSiteMemberId,
|
|
476
|
-
checkUrlUniqueness,
|
|
477
418
|
};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
const { PAGES_PATHS } = require('../../public/consts');
|
|
2
|
+
//const { fetchAllItemsInParallel } = require('../cms-data-methods'); unused at host site
|
|
3
|
+
const { CONFIG_KEYS } = require('../consts');
|
|
4
|
+
const { getSiteConfigs } = require('../utils');
|
|
5
|
+
|
|
6
|
+
const { generateSEOTitle, stripHtmlTags, getMemberProfileData } = require('./utils');
|
|
7
|
+
|
|
8
|
+
const createRoutersHandlers = wixRouterMethods => {
|
|
9
|
+
const {
|
|
10
|
+
redirect,
|
|
11
|
+
ok,
|
|
12
|
+
notFound,
|
|
13
|
+
sendStatus,
|
|
14
|
+
WixRouterSitemapEntry: _WixRouterSitemapEntry,
|
|
15
|
+
} = wixRouterMethods;
|
|
16
|
+
|
|
17
|
+
async function profileRouter(request) {
|
|
18
|
+
const slug = request.path[0];
|
|
19
|
+
if (!slug) {
|
|
20
|
+
return redirect(request.baseUrl);
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const siteConfigs = await getSiteConfigs();
|
|
24
|
+
const siteAssociation = siteConfigs[CONFIG_KEYS.SITE_ASSOCIATION];
|
|
25
|
+
const defaultSEODescription = siteConfigs[CONFIG_KEYS.DEFAULT_PROFILE_SEO_DESCRIPTION];
|
|
26
|
+
const siteLogoUrl = siteConfigs[CONFIG_KEYS.SITE_LOGO_URL];
|
|
27
|
+
const defaultProfileImage = siteConfigs[CONFIG_KEYS.DEFAULT_PROFILE_IMAGE];
|
|
28
|
+
const profileData = await getMemberProfileData(slug, siteAssociation);
|
|
29
|
+
if (profileData && profileData.showWixUrl) {
|
|
30
|
+
const ogImage = profileData.profileImage || profileData.logoImage || siteLogoUrl;
|
|
31
|
+
const seoTitle = generateSEOTitle({
|
|
32
|
+
fullName: profileData.fullName,
|
|
33
|
+
areasOfPractices: profileData.areasOfPractices,
|
|
34
|
+
siteAssociation,
|
|
35
|
+
});
|
|
36
|
+
// Use stripped HTML from aboutService rich text content
|
|
37
|
+
let description = stripHtmlTags(profileData.aboutService) || defaultSEODescription;
|
|
38
|
+
|
|
39
|
+
// Limit description to 160 characters for optimal SEO
|
|
40
|
+
if (description.length > 160) {
|
|
41
|
+
description = description.substring(0, 157) + '...';
|
|
42
|
+
}
|
|
43
|
+
const profileUrl = `${request.baseUrl}/${PAGES_PATHS.PROFILE}/${profileData.url}`;
|
|
44
|
+
const isPrivateMember = profileData.isPrivateMember;
|
|
45
|
+
const seoData = {
|
|
46
|
+
title: seoTitle,
|
|
47
|
+
description: description,
|
|
48
|
+
noIndex: isPrivateMember,
|
|
49
|
+
metaTags: [
|
|
50
|
+
{
|
|
51
|
+
name: 'description',
|
|
52
|
+
content: description,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'keywords',
|
|
56
|
+
content:
|
|
57
|
+
`${profileData.fullName}, ${profileData.areasOfPractices ? profileData.areasOfPractices.slice(0, 3).join(', ') : ''}, ${siteAssociation}, ${profileData.city || ''}, ${profileData.state || ''}`
|
|
58
|
+
.replace(/,\s*,/g, ',')
|
|
59
|
+
.replace(/^,|,$/g, ''),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'author',
|
|
63
|
+
content: profileData.fullName,
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
name: 'robots',
|
|
67
|
+
content: isPrivateMember ? 'noindex, nofollow' : 'index, follow',
|
|
68
|
+
},
|
|
69
|
+
// Open Graph tags
|
|
70
|
+
{
|
|
71
|
+
property: 'og:type',
|
|
72
|
+
content: 'profile',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
property: 'og:title',
|
|
76
|
+
content: seoTitle,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
property: 'og:description',
|
|
80
|
+
content: description,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
property: 'og:image',
|
|
84
|
+
content: ogImage,
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
property: 'og:url',
|
|
88
|
+
content: profileUrl,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
property: 'og:site_name',
|
|
92
|
+
content: `${siteAssociation} Members`,
|
|
93
|
+
},
|
|
94
|
+
// Twitter Card tags
|
|
95
|
+
{
|
|
96
|
+
name: 'twitter:card',
|
|
97
|
+
content: 'summary_large_image',
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: 'twitter:title',
|
|
101
|
+
content: seoTitle,
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
name: 'twitter:description',
|
|
105
|
+
content: description,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: 'twitter:image',
|
|
109
|
+
content: ogImage,
|
|
110
|
+
},
|
|
111
|
+
// Additional SEO tags
|
|
112
|
+
{
|
|
113
|
+
name: 'geo.region',
|
|
114
|
+
content: profileData.state || '',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
name: 'geo.placename',
|
|
118
|
+
content: profileData.city || '',
|
|
119
|
+
},
|
|
120
|
+
].filter(tag => tag.content && tag.content.trim() !== ''), // Remove empty tags
|
|
121
|
+
};
|
|
122
|
+
return ok('profile', { ...profileData, defaultProfileImage }, seoData);
|
|
123
|
+
}
|
|
124
|
+
return notFound();
|
|
125
|
+
} catch (error) {
|
|
126
|
+
console.error(error);
|
|
127
|
+
return sendStatus('500', 'Internal Server Error');
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
function profileSiteMap(_sitemapRequest) {
|
|
131
|
+
return [];
|
|
132
|
+
// Commented out - currently disabled in host site
|
|
133
|
+
// try {
|
|
134
|
+
// const membersQuery = wixData
|
|
135
|
+
// .query(COLLECTIONS.MEMBERS_DATA)
|
|
136
|
+
// .eq('showWixUrl', true)
|
|
137
|
+
// .isNotEmpty('url')
|
|
138
|
+
// .ne('action', 'drop')
|
|
139
|
+
// .fields('url', 'fullName');
|
|
140
|
+
|
|
141
|
+
// const allMembers = await fetchAllItemsInParallel(membersQuery);
|
|
142
|
+
|
|
143
|
+
// const batchSize = 1000;
|
|
144
|
+
// const sitemapEntries = [];
|
|
145
|
+
// const totalItems = allMembers.items.length;
|
|
146
|
+
|
|
147
|
+
// for (let i = 0; i < totalItems; i += batchSize) {
|
|
148
|
+
// const batch = allMembers.items.slice(i, i + batchSize);
|
|
149
|
+
// const batchEntries = batch.map(member => {
|
|
150
|
+
// const entry = new WixRouterSitemapEntry(member.fullName);
|
|
151
|
+
// entry.pageName = 'profile';
|
|
152
|
+
// entry.url = `${PAGES_PATHS.PROFILE}/${member.url}`;
|
|
153
|
+
// entry.title = member.fullName;
|
|
154
|
+
// entry.changeFrequency = 'monthly';
|
|
155
|
+
// entry.priority = 1.0;
|
|
156
|
+
// return entry;
|
|
157
|
+
// });
|
|
158
|
+
// sitemapEntries.push(...batchEntries);
|
|
159
|
+
// }
|
|
160
|
+
|
|
161
|
+
// return sitemapEntries;
|
|
162
|
+
// } catch (error) {
|
|
163
|
+
// console.error('Error generating profile sitemap:', error);
|
|
164
|
+
// return [];
|
|
165
|
+
// }
|
|
166
|
+
}
|
|
167
|
+
//Add other routers here
|
|
168
|
+
return {
|
|
169
|
+
profileRouter,
|
|
170
|
+
profileSiteMap,
|
|
171
|
+
//Add other routers here
|
|
172
|
+
};
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
module.exports = {
|
|
176
|
+
createRoutersHandlers,
|
|
177
|
+
};
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
const { getMainAddress } = require('../../public/Utils/sharedUtils');
|
|
2
|
+
const { getMemberBySlug } = require('../members-data-methods');
|
|
3
|
+
const {
|
|
4
|
+
getAddressesByStatus,
|
|
5
|
+
formatDateToMonthYear,
|
|
6
|
+
hasStudentMembership,
|
|
7
|
+
isPAC_STAFF,
|
|
8
|
+
} = require('../utils');
|
|
9
|
+
|
|
10
|
+
function generateSEOTitle({ fullName, areasOfPractices, siteAssociation }) {
|
|
11
|
+
return `${fullName}${
|
|
12
|
+
areasOfPractices && areasOfPractices.length > 0
|
|
13
|
+
? ` | ${areasOfPractices.slice(0, 3).join(', ')}`
|
|
14
|
+
: ''
|
|
15
|
+
} | ${siteAssociation} Member`;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function stripHtmlTags(html) {
|
|
19
|
+
if (!html) return '';
|
|
20
|
+
// Remove HTML tags and decode HTML entities
|
|
21
|
+
return html
|
|
22
|
+
.replace(/<[^>]*>/g, '') // Remove HTML tags
|
|
23
|
+
.replace(/ /g, ' ') // Replace non-breaking spaces
|
|
24
|
+
.replace(/&/g, '&') // Replace encoded ampersands
|
|
25
|
+
.replace(/</g, '<') // Replace encoded less than
|
|
26
|
+
.replace(/>/g, '>') // Replace encoded greater than
|
|
27
|
+
.replace(/"/g, '"') // Replace encoded quotes
|
|
28
|
+
.replace(/'/g, "'") // Replace encoded apostrophes
|
|
29
|
+
.replace(/\s+/g, ' ') // Replace multiple whitespace with single space
|
|
30
|
+
.trim(); // Remove leading/trailing whitespace
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function shouldHaveStudentBadge(member, siteAssociation) {
|
|
34
|
+
return hasStudentMembership({
|
|
35
|
+
member,
|
|
36
|
+
checkAssociation: true,
|
|
37
|
+
siteAssociation,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function transformMemberToProfileData(member, siteAssociation) {
|
|
42
|
+
if (!member) {
|
|
43
|
+
throw new Error('member is required');
|
|
44
|
+
}
|
|
45
|
+
const addresses = member.addresses || [];
|
|
46
|
+
const mainAddress = getMainAddress(member.addressDisplayOption, addresses);
|
|
47
|
+
const licenceNo = member.licenses
|
|
48
|
+
?.map(val => val.license)
|
|
49
|
+
.filter(Boolean)
|
|
50
|
+
.join(', ');
|
|
51
|
+
const processedAddresses = getAddressesByStatus(member.addresses, member.addressDisplayOption);
|
|
52
|
+
|
|
53
|
+
const memberships = member.memberships || [];
|
|
54
|
+
const siteAssociationMembership = memberships.find(m => m.association === siteAssociation);
|
|
55
|
+
|
|
56
|
+
const areasOfPractices =
|
|
57
|
+
member.areasOfPractices
|
|
58
|
+
?.filter(item => typeof item === 'string' && item.trim().length > 0)
|
|
59
|
+
.map(item => item.trim())
|
|
60
|
+
.sort((a, b) =>
|
|
61
|
+
a.localeCompare(b, undefined, {
|
|
62
|
+
sensitivity: 'base',
|
|
63
|
+
numeric: true,
|
|
64
|
+
})
|
|
65
|
+
) || [];
|
|
66
|
+
return {
|
|
67
|
+
mainAddress,
|
|
68
|
+
testimonials: member.testimonial || [],
|
|
69
|
+
licenceNo,
|
|
70
|
+
processedAddresses,
|
|
71
|
+
memberSince:
|
|
72
|
+
(member.showABMP &&
|
|
73
|
+
siteAssociationMembership &&
|
|
74
|
+
formatDateToMonthYear(siteAssociationMembership?.membersince)) ||
|
|
75
|
+
'',
|
|
76
|
+
shouldHaveStudentBadge: shouldHaveStudentBadge(member, siteAssociation),
|
|
77
|
+
logoImage: member.logoImage,
|
|
78
|
+
fullName: member.fullName,
|
|
79
|
+
profileImage: member.profileImage,
|
|
80
|
+
showContactForm: member.showContactForm,
|
|
81
|
+
bookingUrl: member.bookingUrl,
|
|
82
|
+
aboutService: member.aboutService,
|
|
83
|
+
businessName: (member.showBusinessName && member.businessName) || '',
|
|
84
|
+
phone: member.toShowPhone || '',
|
|
85
|
+
areasOfPractices,
|
|
86
|
+
gallery: member.gallery,
|
|
87
|
+
bannerImages: member.bannerImages,
|
|
88
|
+
showWixUrl: member.showWixUrl,
|
|
89
|
+
_id: member._id,
|
|
90
|
+
url: member.url,
|
|
91
|
+
isPrivateMember: isPAC_STAFF(member),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const getMemberProfileData = async (slug, siteAssociation) => {
|
|
96
|
+
try {
|
|
97
|
+
const member = await getMemberBySlug({
|
|
98
|
+
slug,
|
|
99
|
+
excludeDropped: true,
|
|
100
|
+
excludeSearchedMember: false,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
if (!member) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return transformMemberToProfileData(member, siteAssociation);
|
|
108
|
+
} catch (error) {
|
|
109
|
+
console.error(error);
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
module.exports = {
|
|
115
|
+
generateSEOTitle,
|
|
116
|
+
stripHtmlTags,
|
|
117
|
+
getMemberProfileData,
|
|
118
|
+
};
|
package/backend/sso-methods.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
const {
|
|
2
|
-
|
|
3
|
-
const { auth } = require('@wix/essentials');
|
|
4
|
-
const { authentication } = require('@wix/identity'); //importing from @wix/identity because @wix/members authentication do not have generateSessionToken method
|
|
5
|
-
const generateSessionToken = auth.elevate(authentication.signOn);
|
|
6
|
-
const { decode } = require('jwt-js-decode');
|
|
7
|
-
|
|
8
|
-
const { CONFIG_KEYS, SSO_TOKEN_AUTH_API_URL, SSO_TOKEN_AUTH_API_KEY } = require('./consts');
|
|
1
|
+
const { CONFIG_KEYS } = require('./consts');
|
|
9
2
|
const { MEMBER_ACTIONS } = require('./daily-pull');
|
|
10
3
|
const { getCurrentMember } = require('./members-area-methods');
|
|
11
|
-
const { getMemberByContactId
|
|
4
|
+
const { getMemberByContactId } = require('./members-data-methods');
|
|
12
5
|
const {
|
|
13
6
|
formatDateToMonthYear,
|
|
14
7
|
getAddressDisplayOptions,
|
|
@@ -89,73 +82,7 @@ async function validateMemberToken(memberIdInput) {
|
|
|
89
82
|
throw error;
|
|
90
83
|
}
|
|
91
84
|
}
|
|
92
|
-
async function checkAndFetchSSO(token) {
|
|
93
|
-
const signature = createHmac('sha256', SSO_TOKEN_AUTH_API_KEY).update(token).digest('hex');
|
|
94
|
-
const professionalassistcorpUrl = `${SSO_TOKEN_AUTH_API_URL}/eweb/SSOToken.ashx?token=${token}&Partner=Wix&Signature=${signature}`;
|
|
95
|
-
const options = {
|
|
96
|
-
method: 'get',
|
|
97
|
-
};
|
|
98
|
-
try {
|
|
99
|
-
const httpResponse = await fetch(professionalassistcorpUrl, options);
|
|
100
|
-
console.log('httpResponse status', httpResponse.status);
|
|
101
|
-
if (!httpResponse.ok) {
|
|
102
|
-
throw new Error('Fetch did not succeed with status: ' + httpResponse.status);
|
|
103
|
-
}
|
|
104
|
-
const responseToken = await httpResponse.text();
|
|
105
|
-
return responseToken;
|
|
106
|
-
} catch (error) {
|
|
107
|
-
console.error('Error in checkAndFetchSSO', error);
|
|
108
|
-
return null;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function generateSessionTokenFunction(email) {
|
|
113
|
-
return generateSessionToken({ email })
|
|
114
|
-
.then(response => response.sessionToken)
|
|
115
|
-
.catch(error => {
|
|
116
|
-
console.error('Error in generateSessionTokenFunction', error);
|
|
117
|
-
throw error;
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
const authenticateSSOToken = async token => {
|
|
122
|
-
const responseToken = await checkAndFetchSSO(token);
|
|
123
|
-
const isValidToken = Boolean(
|
|
124
|
-
responseToken && typeof responseToken === 'string' && responseToken?.trim()
|
|
125
|
-
);
|
|
126
|
-
const toLogTokenData = {
|
|
127
|
-
isValidToken,
|
|
128
|
-
tokenData: responseToken
|
|
129
|
-
? {
|
|
130
|
-
length: responseToken.length,
|
|
131
|
-
preview: responseToken.substring(0, 50),
|
|
132
|
-
}
|
|
133
|
-
: 'No token',
|
|
134
|
-
};
|
|
135
|
-
console.log('checkAndFetchSSO responseToken data', JSON.stringify(toLogTokenData, null, 2));
|
|
136
|
-
if (isValidToken) {
|
|
137
|
-
const jwt = decode(responseToken);
|
|
138
|
-
const payload = jwt.payload;
|
|
139
|
-
const membersData = await getSiteMemberId(payload);
|
|
140
|
-
console.log('membersDataCollectionId', membersData._id);
|
|
141
|
-
const sessionToken = await generateSessionTokenFunction(membersData.email);
|
|
142
|
-
const authObj = {
|
|
143
|
-
type: 'success',
|
|
144
|
-
memberId: membersData._id,
|
|
145
|
-
sessionToken,
|
|
146
|
-
};
|
|
147
|
-
return authObj;
|
|
148
|
-
} else {
|
|
149
|
-
console.log('invalid Token responseToken is: ', responseToken);
|
|
150
|
-
return {
|
|
151
|
-
type: 'error',
|
|
152
|
-
memberId: '',
|
|
153
|
-
sessionToken: '',
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
};
|
|
157
85
|
|
|
158
86
|
module.exports = {
|
|
159
87
|
validateMemberToken,
|
|
160
|
-
authenticateSSOToken,
|
|
161
88
|
};
|
package/backend/utils.js
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
const {
|
|
1
|
+
const { elevate } = require('@wix/essentials');
|
|
2
2
|
const { secrets } = require('@wix/secrets');
|
|
3
3
|
const { site } = require('@wix/urls');
|
|
4
4
|
const { encode } = require('ngeohash');
|
|
5
5
|
|
|
6
|
-
const { COLLECTIONS } = require('../public/consts');
|
|
6
|
+
const { COLLECTIONS, ADDRESS_STATUS_TYPES } = require('../public/consts');
|
|
7
|
+
const { formatAddress, generateId } = require('../public/Utils/sharedUtils');
|
|
7
8
|
|
|
8
|
-
const { CONFIG_KEYS, GEO_HASH_PRECISION } = require('./consts');
|
|
9
|
+
const { CONFIG_KEYS, GEO_HASH_PRECISION, MEMBERSHIPS_TYPES } = require('./consts');
|
|
9
10
|
const { wixData } = require('./elevated-modules');
|
|
10
|
-
const
|
|
11
|
+
const { urlExists } = require('./members-data-methods');
|
|
12
|
+
const elevatedGetSecretValue = elevate(secrets.getSecretValue);
|
|
11
13
|
|
|
12
14
|
/**
|
|
13
15
|
* Retrieves site configuration values from the database
|
|
14
|
-
* @param {
|
|
15
|
-
* - 'AUTOMATION_EMAIL_TRIGGER_ID' - Email template ID for triggered emails
|
|
16
|
-
* - 'SITE_ASSOCIATION' - Site association configuration
|
|
16
|
+
* @param {keyof typeof CONFIG_KEYS} [configKey] - The configuration key to retrieve
|
|
17
17
|
* @returns {Promise<any>} The configuration value for the specified key, or all configs if no key provided
|
|
18
18
|
* @example
|
|
19
19
|
* // Get specific config
|
|
@@ -59,18 +59,26 @@ function formatDateToMonthYear(dateString) {
|
|
|
59
59
|
return date.toLocaleDateString('en-US', options);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
* Check if member is a student
|
|
64
|
-
* @param {Object} member - The member object
|
|
65
|
-
* @returns {boolean} True if member has student membership
|
|
66
|
-
*/
|
|
67
|
-
function isStudent(member) {
|
|
62
|
+
function hasStudentMembership({ member, checkAssociation = false, siteAssociation = null }) {
|
|
68
63
|
const memberships = member?.memberships;
|
|
69
64
|
if (!Array.isArray(memberships)) return false;
|
|
70
65
|
|
|
71
|
-
return memberships.some(membership =>
|
|
66
|
+
return memberships.some(membership => {
|
|
67
|
+
const isStudent = membership.membertype === MEMBERSHIPS_TYPES.STUDENT;
|
|
68
|
+
const hasCorrectAssociation = !checkAssociation || membership.association === siteAssociation;
|
|
69
|
+
return isStudent && hasCorrectAssociation;
|
|
70
|
+
});
|
|
72
71
|
}
|
|
73
72
|
|
|
73
|
+
function isStudent(member) {
|
|
74
|
+
return hasStudentMembership({ member, checkAssociation: false });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function isPAC_STAFF(member) {
|
|
78
|
+
return Boolean(
|
|
79
|
+
member?.memberships?.some(membership => membership.membertype === MEMBERSHIPS_TYPES.PAC_STAFF)
|
|
80
|
+
);
|
|
81
|
+
}
|
|
74
82
|
/**
|
|
75
83
|
* Get address display options for member
|
|
76
84
|
* @param {Object} member - The member object
|
|
@@ -84,7 +92,22 @@ function getAddressDisplayOptions(member) {
|
|
|
84
92
|
}
|
|
85
93
|
return displayOptions;
|
|
86
94
|
}
|
|
87
|
-
|
|
95
|
+
function getAddressesByStatus(addresses = [], addressDisplayOption = []) {
|
|
96
|
+
const visible = addresses.filter(addr => addr.addressStatus !== ADDRESS_STATUS_TYPES.DONT_SHOW);
|
|
97
|
+
if (visible.length < 2) {
|
|
98
|
+
return [];
|
|
99
|
+
}
|
|
100
|
+
const opts = Array.isArray(addressDisplayOption) ? addressDisplayOption : [];
|
|
101
|
+
const mainOpt = opts.find(o => o.isMain);
|
|
102
|
+
const mainKey = mainOpt ? mainOpt.key : visible[0].key; // fallback to the first visible if none marked
|
|
103
|
+
return visible
|
|
104
|
+
.filter(addr => addr?.key !== mainKey)
|
|
105
|
+
.map(addr => {
|
|
106
|
+
const addressString = formatAddress(addr);
|
|
107
|
+
return addressString ? { _id: generateId(), address: addressString } : null;
|
|
108
|
+
})
|
|
109
|
+
.filter(Boolean);
|
|
110
|
+
}
|
|
88
111
|
const queryAllItems = async query => {
|
|
89
112
|
console.log('start query');
|
|
90
113
|
let oldResults = await query.find();
|
|
@@ -131,6 +154,28 @@ const normalizeUrlForComparison = url => {
|
|
|
131
154
|
return url.toLowerCase().replace(/-\d+$/, '');
|
|
132
155
|
};
|
|
133
156
|
|
|
157
|
+
/**
|
|
158
|
+
* Checks URL uniqueness for a member
|
|
159
|
+
* @param {string} url - The URL to check
|
|
160
|
+
* @param {string} memberId - The member ID to exclude from the check
|
|
161
|
+
* @returns {Promise<Object>} Result object with isUnique boolean
|
|
162
|
+
*/
|
|
163
|
+
async function checkUrlUniqueness(url, memberId) {
|
|
164
|
+
if (!url || !memberId) {
|
|
165
|
+
throw new Error('Missing required parameters: url and memberId are required');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
try {
|
|
169
|
+
const trimmedUrl = url.trim();
|
|
170
|
+
const exists = await urlExists(trimmedUrl, memberId);
|
|
171
|
+
|
|
172
|
+
return { isUnique: !exists };
|
|
173
|
+
} catch (error) {
|
|
174
|
+
console.error('Error checking URL uniqueness:', error);
|
|
175
|
+
throw new Error(`Failed to check URL uniqueness: ${error.message}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
134
179
|
async function getSecret(secretKey) {
|
|
135
180
|
return await elevatedGetSecretValue(secretKey).value;
|
|
136
181
|
}
|
|
@@ -174,11 +219,15 @@ module.exports = {
|
|
|
174
219
|
isValidArray,
|
|
175
220
|
normalizeUrlForComparison,
|
|
176
221
|
queryAllItems,
|
|
222
|
+
checkUrlUniqueness,
|
|
177
223
|
formatDateToMonthYear,
|
|
178
224
|
isStudent,
|
|
225
|
+
hasStudentMembership,
|
|
179
226
|
getAddressDisplayOptions,
|
|
180
227
|
getSecret,
|
|
181
228
|
getSiteBaseUrl,
|
|
182
229
|
encodeXml,
|
|
183
230
|
formatDateOnly,
|
|
231
|
+
getAddressesByStatus,
|
|
232
|
+
isPAC_STAFF,
|
|
184
233
|
};
|
package/package.json
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "abmp-npm",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.63",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"check-cycles": "madge --circular .",
|
|
7
6
|
"test": "echo \"Error: no test specified\" && exit 1",
|
|
8
|
-
"lint": "
|
|
7
|
+
"lint": "eslint .",
|
|
9
8
|
"lint:fix": "eslint . --fix",
|
|
10
9
|
"format": "prettier --write \"**/*.{js,json,md}\"",
|
|
11
10
|
"format:check": "prettier --check \"**/*.{js,json,md}\"",
|
|
@@ -24,7 +23,6 @@
|
|
|
24
23
|
"eslint-plugin-promise": "^7.1.0",
|
|
25
24
|
"globals": "^15.10.0",
|
|
26
25
|
"husky": "^9.1.6",
|
|
27
|
-
"madge": "^8.0.0",
|
|
28
26
|
"prettier": "^3.3.3"
|
|
29
27
|
},
|
|
30
28
|
"dependencies": {
|
|
@@ -32,19 +30,15 @@
|
|
|
32
30
|
"@wix/crm": "^1.0.1061",
|
|
33
31
|
"@wix/data": "^1.0.303",
|
|
34
32
|
"@wix/essentials": "^0.1.28",
|
|
35
|
-
"@wix/identity": "^1.0.178",
|
|
36
33
|
"@wix/media": "^1.0.213",
|
|
37
34
|
"@wix/members": "^1.0.365",
|
|
38
35
|
"@wix/secrets": "^1.0.62",
|
|
39
36
|
"@wix/site-location": "^1.31.0",
|
|
40
|
-
"@wix/site-members": "^1.32.0",
|
|
41
|
-
"@wix/site-storage": "^1.22.0",
|
|
42
37
|
"@wix/site-window": "^1.44.0",
|
|
43
38
|
"@wix/urls": "^1.0.57",
|
|
44
39
|
"aws4": "^1.13.2",
|
|
45
40
|
"axios": "^1.13.1",
|
|
46
41
|
"crypto": "^1.0.1",
|
|
47
|
-
"jwt-js-decode": "^1.9.0",
|
|
48
42
|
"lodash": "^4.17.21",
|
|
49
43
|
"ngeohash": "^0.6.3",
|
|
50
44
|
"phone": "^3.1.67",
|
package/pages/index.js
CHANGED
package/public/index.js
CHANGED
package/pages/LoadingPage.js
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
const { window: wixWindow, rendering } = require('@wix/site-window');
|
|
2
|
-
|
|
3
|
-
const { LIGHTBOX_NAMES } = require('../public/consts');
|
|
4
|
-
const { checkAndLogin } = require('../public/sso-auth-methods');
|
|
5
|
-
|
|
6
|
-
async function loadingPageOnReady(authenticateSSOToken) {
|
|
7
|
-
const renderingEnv = await rendering.env();
|
|
8
|
-
//This calls needs to triggered on client side, otherwise PAC API will return 401 error
|
|
9
|
-
if (renderingEnv === 'browser') {
|
|
10
|
-
//Need to pass processSubmission to checkAndLogin so it will run as a web method not a public one.
|
|
11
|
-
await checkAndLogin(authenticateSSOToken).catch(error => {
|
|
12
|
-
wixWindow.openLightbox(LIGHTBOX_NAMES.LOGIN_ERROR_ALERT);
|
|
13
|
-
console.error(`Something went wrong while logging in: ${error}`);
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
module.exports = {
|
|
19
|
-
loadingPageOnReady,
|
|
20
|
-
};
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
const { location: wixLocationFrontend } = require('@wix/site-location');
|
|
2
|
-
const { authentication } = require('@wix/site-members');
|
|
3
|
-
const { local } = require('@wix/site-storage');
|
|
4
|
-
|
|
5
|
-
const { PAGES_PATHS } = require('./consts');
|
|
6
|
-
|
|
7
|
-
const checkAndLogin = async authenticateSSOToken => {
|
|
8
|
-
const query = await wixLocationFrontend.query();
|
|
9
|
-
const token = query['token']?.trim();
|
|
10
|
-
try {
|
|
11
|
-
if (token) {
|
|
12
|
-
const authObj = await authenticateSSOToken(token);
|
|
13
|
-
console.log('authObj', authObj);
|
|
14
|
-
if (authObj.type == 'success') {
|
|
15
|
-
console.log('success');
|
|
16
|
-
await Promise.all([
|
|
17
|
-
authentication.applySessionToken(authObj?.sessionToken),
|
|
18
|
-
local.setItem('memberId', authObj.memberId),
|
|
19
|
-
]);
|
|
20
|
-
console.log('memberId', authObj.memberId);
|
|
21
|
-
const queryParams = {
|
|
22
|
-
...query,
|
|
23
|
-
token: authObj?.memberId,
|
|
24
|
-
};
|
|
25
|
-
const redirectTo = `${PAGES_PATHS.MEMBERS_FORM}?${new URLSearchParams(queryParams).toString()}`;
|
|
26
|
-
await wixLocationFrontend.to(`/${redirectTo}`);
|
|
27
|
-
} else {
|
|
28
|
-
console.error('Something went wrong while logging in');
|
|
29
|
-
throw new Error('Authentication failed - invalid response from server');
|
|
30
|
-
}
|
|
31
|
-
} else {
|
|
32
|
-
console.log('checkAndLogin: No token found');
|
|
33
|
-
throw new Error('No authentication token found in URL');
|
|
34
|
-
}
|
|
35
|
-
} catch (error) {
|
|
36
|
-
console.error('Error in checkAndLogin', error);
|
|
37
|
-
throw error;
|
|
38
|
-
}
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
module.exports = {
|
|
42
|
-
checkAndLogin,
|
|
43
|
-
};
|