@unchainedshop/core-users 4.0.0-rc.16 → 4.0.0-rc.17
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/lib/db/UsersCollection.d.ts +2 -2
- package/lib/db/UsersCollection.d.ts.map +1 -1
- package/lib/db/UsersCollection.js +30 -26
- package/lib/db/UsersCollection.js.map +1 -1
- package/lib/module/addMigrations.d.ts +3 -0
- package/lib/module/addMigrations.d.ts.map +1 -0
- package/lib/module/addMigrations.js +48 -0
- package/lib/module/addMigrations.js.map +1 -0
- package/lib/module/configureUsersModule.d.ts +30 -30
- package/lib/module/configureUsersModule.d.ts.map +1 -1
- package/lib/module/configureUsersModule.js +63 -36
- package/lib/module/configureUsersModule.js.map +1 -1
- package/lib/module/configureUsersWebAuthnModule.d.ts +5 -5
- package/lib/module/configureUsersWebAuthnModule.d.ts.map +1 -1
- package/lib/module/configureUsersWebAuthnModule.js +5 -1
- package/lib/module/configureUsersWebAuthnModule.js.map +1 -1
- package/lib/services/getUserCountryService.d.ts +3 -0
- package/lib/services/getUserCountryService.d.ts.map +1 -0
- package/lib/services/getUserCountryService.js +7 -0
- package/lib/services/getUserCountryService.js.map +1 -0
- package/lib/services/getUserLanguageService.d.ts +3 -0
- package/lib/services/getUserLanguageService.d.ts.map +1 -0
- package/lib/services/getUserLanguageService.js +7 -0
- package/lib/services/getUserLanguageService.js.map +1 -0
- package/lib/services/updateUserAvatarAfterUploadService.d.ts +3 -0
- package/lib/services/updateUserAvatarAfterUploadService.d.ts.map +1 -0
- package/lib/services/updateUserAvatarAfterUploadService.js +24 -0
- package/lib/services/updateUserAvatarAfterUploadService.js.map +1 -0
- package/lib/services/userServices.d.ts +3 -0
- package/lib/services/userServices.d.ts.map +1 -0
- package/lib/services/userServices.js +9 -0
- package/lib/services/userServices.js.map +1 -0
- package/lib/users-settings.d.ts +1 -14
- package/lib/users-settings.d.ts.map +1 -1
- package/lib/users-settings.js +24 -24
- package/lib/users-settings.js.map +1 -1
- package/package.json +7 -7
- package/src/db/UsersCollection.ts +33 -29
- package/src/module/buildFindSelector.test.ts +13 -4
- package/src/module/configureUsersModule.ts +102 -80
- package/src/module/configureUsersWebAuthnModule.ts +4 -6
- package/src/users-settings.ts +31 -45
- package/tsconfig.json +2 -1
|
@@ -62,8 +62,8 @@ export const buildFindSelector = ({
|
|
|
62
62
|
...rest
|
|
63
63
|
}: UserQuery) => {
|
|
64
64
|
const selector: mongodb.Filter<User> = { ...rest };
|
|
65
|
-
if (!includeDeleted) selector.deleted =
|
|
66
|
-
if (!includeGuests) selector.guest = { $
|
|
65
|
+
if (!includeDeleted) selector.deleted = { $exists: false };
|
|
66
|
+
if (!includeGuests) selector.guest = { $ne: true };
|
|
67
67
|
if (emailVerified === true) {
|
|
68
68
|
selector['emails.verified'] = true;
|
|
69
69
|
}
|
|
@@ -91,12 +91,13 @@ export const buildFindSelector = ({
|
|
|
91
91
|
return selector;
|
|
92
92
|
};
|
|
93
93
|
|
|
94
|
-
export const configureUsersModule = async (
|
|
94
|
+
export const configureUsersModule = async (moduleInput: ModuleInput<UserSettingsOptions>) => {
|
|
95
|
+
const { db, options } = moduleInput;
|
|
95
96
|
userSettings.configureSettings(options || {}, db);
|
|
96
97
|
registerEvents(USER_EVENTS);
|
|
97
98
|
const Users = await UsersCollection(db);
|
|
98
99
|
|
|
99
|
-
const webAuthn = await configureUsersWebAuthnModule(
|
|
100
|
+
const webAuthn = await configureUsersWebAuthnModule(moduleInput);
|
|
100
101
|
|
|
101
102
|
return {
|
|
102
103
|
// Queries
|
|
@@ -106,17 +107,17 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
106
107
|
return userCount;
|
|
107
108
|
},
|
|
108
109
|
|
|
109
|
-
async findUserById(userId: string)
|
|
110
|
+
async findUserById(userId: string) {
|
|
110
111
|
if (!userId) return null;
|
|
111
112
|
return Users.findOne(generateDbFilterById(userId), {});
|
|
112
113
|
},
|
|
113
114
|
|
|
114
|
-
async findUserByUsername(username: string)
|
|
115
|
+
async findUserByUsername(username: string) {
|
|
115
116
|
if (!username) return null;
|
|
116
117
|
return Users.findOne({ username: insensitiveTrimmedRegexOperator(username) }, {});
|
|
117
118
|
},
|
|
118
119
|
|
|
119
|
-
async findUserByEmail(email: string)
|
|
120
|
+
async findUserByEmail(email: string) {
|
|
120
121
|
if (!email) return null;
|
|
121
122
|
return Users.findOne({ 'emails.address': insensitiveTrimmedRegexOperator(email) }, {});
|
|
122
123
|
},
|
|
@@ -125,7 +126,7 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
125
126
|
userId: string;
|
|
126
127
|
address: string;
|
|
127
128
|
when: Date;
|
|
128
|
-
}> {
|
|
129
|
+
} | null> {
|
|
129
130
|
if (!plainToken) return null;
|
|
130
131
|
const token = await sha256(plainToken);
|
|
131
132
|
const user = await Users.findOne(
|
|
@@ -178,7 +179,7 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
178
179
|
userId: string;
|
|
179
180
|
address: string;
|
|
180
181
|
when: Date;
|
|
181
|
-
}> {
|
|
182
|
+
} | null> {
|
|
182
183
|
const token = await sha256(plainToken);
|
|
183
184
|
const user = await Users.findOne(
|
|
184
185
|
{
|
|
@@ -199,7 +200,7 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
199
200
|
};
|
|
200
201
|
},
|
|
201
202
|
|
|
202
|
-
async findUserByToken(plainToken: string)
|
|
203
|
+
async findUserByToken(plainToken: string) {
|
|
203
204
|
const token = await sha256(plainToken);
|
|
204
205
|
|
|
205
206
|
if (token) {
|
|
@@ -211,10 +212,7 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
211
212
|
return null;
|
|
212
213
|
},
|
|
213
214
|
|
|
214
|
-
async findUser(
|
|
215
|
-
query: UserQuery & { sort?: SortOption[] },
|
|
216
|
-
findOptions?: mongodb.FindOptions,
|
|
217
|
-
): Promise<User> {
|
|
215
|
+
async findUser(query: UserQuery & { sort?: SortOption[] }, findOptions?: mongodb.FindOptions) {
|
|
218
216
|
const selector = buildFindSelector(query);
|
|
219
217
|
return Users.findOne(selector, findOptions);
|
|
220
218
|
},
|
|
@@ -248,7 +246,10 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
248
246
|
},
|
|
249
247
|
|
|
250
248
|
async userExists({ userId }: { userId: string }): Promise<boolean> {
|
|
251
|
-
const userCount = await Users.countDocuments(
|
|
249
|
+
const userCount = await Users.countDocuments(
|
|
250
|
+
{ _id: userId, deleted: { $exists: false } },
|
|
251
|
+
{ limit: 1 },
|
|
252
|
+
);
|
|
252
253
|
return userCount === 1;
|
|
253
254
|
},
|
|
254
255
|
|
|
@@ -259,7 +260,7 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
259
260
|
)?.[0];
|
|
260
261
|
},
|
|
261
262
|
|
|
262
|
-
userLocale(user: User): Intl.Locale {
|
|
263
|
+
userLocale(user: User | null): Intl.Locale {
|
|
263
264
|
if (!user?.lastLogin?.locale) return systemLocale;
|
|
264
265
|
try {
|
|
265
266
|
return new Intl.Locale(user.lastLogin.locale);
|
|
@@ -348,7 +349,7 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
348
349
|
/* */
|
|
349
350
|
}
|
|
350
351
|
|
|
351
|
-
const user = await Users.findOne({ _id: userId }, {});
|
|
352
|
+
const user = (await Users.findOne({ _id: userId }, {})) as User;
|
|
352
353
|
await emit('USER_CREATE', {
|
|
353
354
|
user: removeConfidentialServiceHashes(user),
|
|
354
355
|
});
|
|
@@ -457,39 +458,46 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
457
458
|
});
|
|
458
459
|
},
|
|
459
460
|
|
|
460
|
-
addRoles: async (userId: string, roles: string[])
|
|
461
|
+
addRoles: async (userId: string, roles: string[]) => {
|
|
461
462
|
const selector = generateDbFilterById(userId);
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
463
|
+
const user = await Users.findOneAndUpdate(
|
|
464
|
+
selector,
|
|
465
|
+
{
|
|
466
|
+
$addToSet: { roles: { $each: roles } },
|
|
467
|
+
},
|
|
468
|
+
{ returnDocument: 'after' },
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
if (!user) return null;
|
|
465
472
|
|
|
466
|
-
const user = await Users.findOne(selector, {});
|
|
467
473
|
await emit('USER_ADD_ROLES', {
|
|
468
474
|
user: removeConfidentialServiceHashes(user),
|
|
469
475
|
});
|
|
470
476
|
|
|
471
|
-
return
|
|
477
|
+
return user;
|
|
472
478
|
},
|
|
473
479
|
|
|
474
480
|
async setUsername(userId: string, username: string) {
|
|
475
481
|
if (!(await userSettings.validateUsername(username))) {
|
|
476
482
|
throw new Error(`Username ${username} is invalid`, { cause: 'USERNAME_INVALID' });
|
|
477
483
|
}
|
|
478
|
-
await Users.
|
|
484
|
+
const user = await Users.findOneAndUpdate(
|
|
479
485
|
{ _id: userId },
|
|
480
486
|
{
|
|
481
487
|
$set: {
|
|
482
488
|
username: username.trim(),
|
|
483
489
|
},
|
|
484
490
|
},
|
|
491
|
+
{ returnDocument: 'after' },
|
|
485
492
|
);
|
|
486
|
-
|
|
493
|
+
if (!user) return null;
|
|
487
494
|
await emit('USER_UPDATE_USERNAME', {
|
|
488
495
|
user: removeConfidentialServiceHashes(user),
|
|
489
496
|
});
|
|
497
|
+
return user;
|
|
490
498
|
},
|
|
491
499
|
|
|
492
|
-
async setPassword(userId: string, plainPassword: string)
|
|
500
|
+
async setPassword(userId: string, plainPassword: string) {
|
|
493
501
|
if (!(await userSettings.validatePassword(plainPassword))) {
|
|
494
502
|
throw new Error(`Password ***** is invalid`, { cause: 'PASSWORD_INVALID' });
|
|
495
503
|
}
|
|
@@ -506,13 +514,14 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
506
514
|
},
|
|
507
515
|
{ returnDocument: 'after' },
|
|
508
516
|
);
|
|
517
|
+
if (!user) return null;
|
|
509
518
|
await emit('USER_UPDATE_PASSWORD', {
|
|
510
519
|
user: removeConfidentialServiceHashes(user),
|
|
511
520
|
});
|
|
512
521
|
return user;
|
|
513
522
|
},
|
|
514
523
|
|
|
515
|
-
async resetPassword(token: string, newPassword: string)
|
|
524
|
+
async resetPassword(token: string, newPassword: string) {
|
|
516
525
|
const resetToken = await this.findResetToken(token);
|
|
517
526
|
if (!resetToken) return null;
|
|
518
527
|
const updatedUser = await this.setPassword(resetToken.userId, newPassword);
|
|
@@ -540,7 +549,7 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
540
549
|
return updatedUser;
|
|
541
550
|
},
|
|
542
551
|
|
|
543
|
-
updateAvatar: async (_id: string, fileId: string)
|
|
552
|
+
updateAvatar: async (_id: string, fileId: string) => {
|
|
544
553
|
const userFilter = generateDbFilterById(_id);
|
|
545
554
|
const modifier = {
|
|
546
555
|
$set: {
|
|
@@ -553,44 +562,54 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
553
562
|
returnDocument: 'after',
|
|
554
563
|
});
|
|
555
564
|
|
|
565
|
+
if (!user) return null;
|
|
556
566
|
await emit('USER_UPDATE_AVATAR', {
|
|
557
567
|
user: removeConfidentialServiceHashes(user),
|
|
558
568
|
});
|
|
559
569
|
return user;
|
|
560
570
|
},
|
|
561
571
|
|
|
562
|
-
updateGuest: async (user: User, guest: boolean)
|
|
563
|
-
const
|
|
564
|
-
|
|
572
|
+
updateGuest: async (user: User, guest: boolean) => {
|
|
573
|
+
const updatedUser = await Users.findOneAndUpdate(
|
|
574
|
+
generateDbFilterById(user._id),
|
|
575
|
+
{
|
|
576
|
+
$set: { guest },
|
|
577
|
+
},
|
|
578
|
+
{ returnDocument: 'after' },
|
|
579
|
+
);
|
|
580
|
+
if (!updatedUser) return null;
|
|
565
581
|
await emit('USER_UPDATE_GUEST', {
|
|
566
582
|
user: removeConfidentialServiceHashes({
|
|
567
|
-
...
|
|
583
|
+
...updatedUser,
|
|
568
584
|
guest,
|
|
569
585
|
}),
|
|
570
586
|
});
|
|
587
|
+
return updatedUser;
|
|
571
588
|
},
|
|
572
589
|
|
|
573
|
-
updateHeartbeat: async (userId: string, lastLogin: UserLastLogin)
|
|
574
|
-
const
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
590
|
+
updateHeartbeat: async (userId: string, lastLogin: UserLastLogin) => {
|
|
591
|
+
const user = await Users.findOneAndUpdate(
|
|
592
|
+
generateDbFilterById(userId),
|
|
593
|
+
{
|
|
594
|
+
$set: {
|
|
595
|
+
lastLogin: {
|
|
596
|
+
timestamp: new Date(),
|
|
597
|
+
...lastLogin,
|
|
598
|
+
},
|
|
579
599
|
},
|
|
580
600
|
},
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
601
|
+
{
|
|
602
|
+
returnDocument: 'after',
|
|
603
|
+
},
|
|
604
|
+
);
|
|
605
|
+
if (!user) return null;
|
|
587
606
|
await emit('USER_UPDATE_HEARTBEAT', {
|
|
588
607
|
user: removeConfidentialServiceHashes(user),
|
|
589
608
|
});
|
|
590
609
|
return user;
|
|
591
610
|
},
|
|
592
611
|
|
|
593
|
-
markDeleted: async (userId: string)
|
|
612
|
+
markDeleted: async (userId: string) => {
|
|
594
613
|
await db.collection('sessions').deleteMany({
|
|
595
614
|
session: insensitiveTrimmedRegexOperator(`"user":"${userId}"`),
|
|
596
615
|
});
|
|
@@ -602,18 +621,21 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
602
621
|
deleted: new Date(),
|
|
603
622
|
emails: [],
|
|
604
623
|
roles: [],
|
|
605
|
-
profile: null,
|
|
606
|
-
lastBillingAddress: null,
|
|
607
624
|
services: {},
|
|
608
625
|
pushSubscriptions: [],
|
|
609
|
-
avatarId: null,
|
|
610
626
|
initialPassword: false,
|
|
611
|
-
|
|
612
|
-
|
|
627
|
+
},
|
|
628
|
+
$unset: {
|
|
629
|
+
profile: 1,
|
|
630
|
+
lastBillingAddress: 1,
|
|
631
|
+
lastContact: 1,
|
|
632
|
+
lastLogin: 1,
|
|
633
|
+
avatarId: 1,
|
|
613
634
|
},
|
|
614
635
|
},
|
|
615
636
|
{ returnDocument: 'after' },
|
|
616
637
|
);
|
|
638
|
+
if (!user) return null;
|
|
617
639
|
|
|
618
640
|
await emit('USER_REMOVE', {
|
|
619
641
|
user,
|
|
@@ -621,14 +643,11 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
621
643
|
return user;
|
|
622
644
|
},
|
|
623
645
|
|
|
624
|
-
deletePermanently: async ({ userId }: { userId: string })
|
|
646
|
+
deletePermanently: async ({ userId }: { userId: string }) => {
|
|
625
647
|
return Users.findOneAndDelete({ _id: userId });
|
|
626
648
|
},
|
|
627
649
|
|
|
628
|
-
updateProfile: async (
|
|
629
|
-
userId: string,
|
|
630
|
-
updatedData: { profile?: UserProfile; meta?: any },
|
|
631
|
-
): Promise<User> => {
|
|
650
|
+
updateProfile: async (userId: string, updatedData: { profile?: UserProfile; meta?: any }) => {
|
|
632
651
|
const userFilter = generateDbFilterById(userId);
|
|
633
652
|
const { meta, profile } = updatedData;
|
|
634
653
|
|
|
@@ -657,16 +676,17 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
657
676
|
returnDocument: 'after',
|
|
658
677
|
});
|
|
659
678
|
|
|
679
|
+
if (!user) return null;
|
|
660
680
|
await emit('USER_UPDATE_PROFILE', {
|
|
661
681
|
user: removeConfidentialServiceHashes(user),
|
|
662
682
|
});
|
|
663
683
|
return user;
|
|
664
684
|
},
|
|
665
685
|
|
|
666
|
-
updateLastBillingAddress: async (_id: string, lastBillingAddress: Address)
|
|
686
|
+
updateLastBillingAddress: async (_id: string, lastBillingAddress: Address) => {
|
|
667
687
|
const userFilter = generateDbFilterById(_id);
|
|
668
688
|
const user = await Users.findOne(userFilter, {});
|
|
669
|
-
|
|
689
|
+
if (!user) return null;
|
|
670
690
|
if (!lastBillingAddress) return user;
|
|
671
691
|
|
|
672
692
|
const modifier = {
|
|
@@ -691,18 +711,17 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
691
711
|
returnDocument: 'after',
|
|
692
712
|
});
|
|
693
713
|
|
|
694
|
-
if (updatedUser)
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
}
|
|
699
|
-
|
|
714
|
+
if (!updatedUser) return null;
|
|
715
|
+
await emit('USER_UPDATE_BILLING_ADDRESS', {
|
|
716
|
+
user: removeConfidentialServiceHashes(updatedUser),
|
|
717
|
+
});
|
|
700
718
|
return updatedUser;
|
|
701
719
|
},
|
|
702
720
|
|
|
703
|
-
updateLastContact: async (_id: string, lastContact: Contact)
|
|
721
|
+
updateLastContact: async (_id: string, lastContact: Contact) => {
|
|
704
722
|
const userFilter = generateDbFilterById(_id);
|
|
705
723
|
const user = await Users.findOne(userFilter, {});
|
|
724
|
+
if (!user) return null;
|
|
706
725
|
const profile = user.profile || {};
|
|
707
726
|
const isGuest = !!user.guest;
|
|
708
727
|
|
|
@@ -722,13 +741,14 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
722
741
|
returnDocument: 'after',
|
|
723
742
|
});
|
|
724
743
|
|
|
744
|
+
if (!updatedUser) return null;
|
|
725
745
|
await emit('USER_UPDATE_LAST_CONTACT', {
|
|
726
746
|
user: removeConfidentialServiceHashes(user),
|
|
727
747
|
});
|
|
728
748
|
return updatedUser;
|
|
729
749
|
},
|
|
730
750
|
|
|
731
|
-
updateRoles: async (_id: string, roles: string[])
|
|
751
|
+
updateRoles: async (_id: string, roles: string[]) => {
|
|
732
752
|
const modifier = {
|
|
733
753
|
$set: {
|
|
734
754
|
updated: new Date(),
|
|
@@ -739,23 +759,27 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
739
759
|
const user = await Users.findOneAndUpdate(generateDbFilterById(_id), modifier, {
|
|
740
760
|
returnDocument: 'after',
|
|
741
761
|
});
|
|
742
|
-
|
|
762
|
+
if (!user) return null;
|
|
743
763
|
await emit('USER_UPDATE_ROLE', {
|
|
744
764
|
user: removeConfidentialServiceHashes(user),
|
|
745
765
|
});
|
|
746
766
|
return user;
|
|
747
767
|
},
|
|
748
768
|
|
|
749
|
-
updateTags: async (_id: string, tags: string[])
|
|
750
|
-
const
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
769
|
+
updateTags: async (_id: string, tags: string[]) => {
|
|
770
|
+
const user = await Users.findOneAndUpdate(
|
|
771
|
+
generateDbFilterById(_id),
|
|
772
|
+
{
|
|
773
|
+
$set: {
|
|
774
|
+
updated: new Date(),
|
|
775
|
+
tags,
|
|
776
|
+
},
|
|
754
777
|
},
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
778
|
+
{
|
|
779
|
+
returnDocument: 'after',
|
|
780
|
+
},
|
|
781
|
+
);
|
|
782
|
+
if (!user) return null;
|
|
759
783
|
await emit('USER_UPDATE_TAGS', {
|
|
760
784
|
user: removeConfidentialServiceHashes(user),
|
|
761
785
|
});
|
|
@@ -766,11 +790,12 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
766
790
|
selector: mongodb.Filter<User>,
|
|
767
791
|
modifier: mongodb.UpdateFilter<User>,
|
|
768
792
|
updateOptions?: mongodb.FindOneAndUpdateOptions,
|
|
769
|
-
)
|
|
793
|
+
) => {
|
|
770
794
|
const user = await Users.findOneAndUpdate(selector, modifier, {
|
|
771
795
|
...updateOptions,
|
|
772
796
|
returnDocument: 'after',
|
|
773
797
|
});
|
|
798
|
+
if (!user) return null;
|
|
774
799
|
await emit('USER_UPDATE', {
|
|
775
800
|
user: removeConfidentialServiceHashes(user),
|
|
776
801
|
});
|
|
@@ -825,10 +850,7 @@ export const configureUsersModule = async ({ db, options }: ModuleInput<UserSett
|
|
|
825
850
|
tags: { $exists: true },
|
|
826
851
|
deleted: { $exists: false },
|
|
827
852
|
})) as string[];
|
|
828
|
-
return tags
|
|
829
|
-
.filter(Boolean)
|
|
830
|
-
.map((t) => t.trim())
|
|
831
|
-
.filter(Boolean);
|
|
853
|
+
return tags.filter(Boolean).toSorted();
|
|
832
854
|
},
|
|
833
855
|
};
|
|
834
856
|
};
|
|
@@ -6,7 +6,7 @@ import type { PublicKeyCredentialCreationOptions, PublicKeyCredentialRequestOpti
|
|
|
6
6
|
|
|
7
7
|
const logger = createLogger('unchained:core-users');
|
|
8
8
|
|
|
9
|
-
const { ROOT_URL, EMAIL_WEBSITE_NAME } = process.env;
|
|
9
|
+
const { ROOT_URL = 'http://localhost:4010', EMAIL_WEBSITE_NAME = 'Unchained' } = process.env;
|
|
10
10
|
|
|
11
11
|
type SerializedOptions<T> = Omit<T, 'challenge' | 'requestId'> & {
|
|
12
12
|
challenge: string;
|
|
@@ -103,11 +103,7 @@ export const configureUsersWebAuthnModule = async ({ db }: ModuleInput<any>) =>
|
|
|
103
103
|
} as SerializedOptions<PublicKeyCredentialCreationOptions>;
|
|
104
104
|
},
|
|
105
105
|
|
|
106
|
-
createCredentialRequestOptions: async (
|
|
107
|
-
origin: string,
|
|
108
|
-
username?: string,
|
|
109
|
-
extensionOptions?: any,
|
|
110
|
-
) => {
|
|
106
|
+
createCredentialRequestOptions: async (origin: string, username: string, extensionOptions?: any) => {
|
|
111
107
|
if (!f2l) return null;
|
|
112
108
|
|
|
113
109
|
const loginOptions = await f2l.assertionOptions(extensionOptions);
|
|
@@ -137,6 +133,7 @@ export const configureUsersWebAuthnModule = async ({ db }: ModuleInput<any>) =>
|
|
|
137
133
|
},
|
|
138
134
|
{ sort: { _id: -1 } },
|
|
139
135
|
);
|
|
136
|
+
if (!request) return null;
|
|
140
137
|
|
|
141
138
|
const attestationExpectations = {
|
|
142
139
|
challenge: request.challenge,
|
|
@@ -186,6 +183,7 @@ export const configureUsersWebAuthnModule = async ({ db }: ModuleInput<any>) =>
|
|
|
186
183
|
},
|
|
187
184
|
{ sort: { _id: -1 } },
|
|
188
185
|
);
|
|
186
|
+
if (!request) return null;
|
|
189
187
|
|
|
190
188
|
const id = Buffer.from(credentials.id, 'base64');
|
|
191
189
|
const authenticatorData = Buffer.from(credentials.response.authenticatorData, 'base64');
|
package/src/users-settings.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { insensitiveTrimmedRegexOperator, mongodb } from '@unchainedshop/mongodb';
|
|
2
2
|
import { User } from './db/UsersCollection.js';
|
|
3
|
-
|
|
4
3
|
export interface UserRegistrationData extends Partial<User> {
|
|
5
4
|
email?: string;
|
|
6
5
|
password: string | null;
|
|
@@ -14,22 +13,6 @@ export enum UserAccountAction {
|
|
|
14
13
|
PASSWORD_RESETTED = 'password-resetted',
|
|
15
14
|
EMAIL_VERIFIED = 'email-verified',
|
|
16
15
|
}
|
|
17
|
-
export interface UserSettingsOptions {
|
|
18
|
-
mergeUserCartsOnLogin?: boolean;
|
|
19
|
-
autoMessagingAfterUserCreation?: boolean;
|
|
20
|
-
/**
|
|
21
|
-
* Function to calculate the earliest valid token date. Defaults to 1h if not set.
|
|
22
|
-
* @param type The type of user account action.
|
|
23
|
-
* @returns A date. All tokens created after that date are considered valid.
|
|
24
|
-
*/
|
|
25
|
-
earliestValidTokenDate?: (
|
|
26
|
-
type: UserAccountAction.VERIFY_EMAIL | UserAccountAction.RESET_PASSWORD,
|
|
27
|
-
) => Date;
|
|
28
|
-
validateEmail?: (email: string) => Promise<boolean>;
|
|
29
|
-
validateUsername?: (username: string) => Promise<boolean>;
|
|
30
|
-
validateNewUser?: (user: UserRegistrationData) => Promise<UserRegistrationData>;
|
|
31
|
-
validatePassword?: (password: string) => Promise<boolean>;
|
|
32
|
-
}
|
|
33
16
|
export interface UserSettings {
|
|
34
17
|
mergeUserCartsOnLogin: boolean;
|
|
35
18
|
autoMessagingAfterUserCreation: boolean;
|
|
@@ -43,14 +26,37 @@ export interface UserSettings {
|
|
|
43
26
|
configureSettings: (options: UserSettingsOptions, db: mongodb.Db) => void;
|
|
44
27
|
}
|
|
45
28
|
|
|
29
|
+
export type UserSettingsOptions = Omit<Partial<UserSettings>, 'configureSettings'>;
|
|
30
|
+
|
|
31
|
+
const defaultAutoMessagingAfterUserCreation = true;
|
|
32
|
+
const defaultMergeUserCartsOnLogin = true;
|
|
33
|
+
|
|
34
|
+
const defaultEarliestValidTokenDate = () => {
|
|
35
|
+
// 1 hour ago
|
|
36
|
+
return new Date(new Date().getTime() - 1000 * 60 * 60);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const defaultValidateNewUser = async (user: UserRegistrationData) => {
|
|
40
|
+
return {
|
|
41
|
+
...user,
|
|
42
|
+
username: user.username?.trim().toLowerCase(),
|
|
43
|
+
email: user.email?.trim().toLowerCase(),
|
|
44
|
+
password: user.password ?? null,
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const defaultValidatePassword = async (password: string) => {
|
|
49
|
+
return password?.length >= 8;
|
|
50
|
+
};
|
|
51
|
+
|
|
46
52
|
export const userSettings: UserSettings = {
|
|
47
|
-
autoMessagingAfterUserCreation:
|
|
48
|
-
mergeUserCartsOnLogin:
|
|
49
|
-
earliestValidTokenDate:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
validatePassword:
|
|
53
|
+
autoMessagingAfterUserCreation: defaultAutoMessagingAfterUserCreation,
|
|
54
|
+
mergeUserCartsOnLogin: defaultMergeUserCartsOnLogin,
|
|
55
|
+
earliestValidTokenDate: defaultEarliestValidTokenDate,
|
|
56
|
+
validateNewUser: defaultValidateNewUser,
|
|
57
|
+
validateEmail: () => Promise.resolve(true),
|
|
58
|
+
validateUsername: () => Promise.resolve(true),
|
|
59
|
+
validatePassword: () => Promise.resolve(true),
|
|
54
60
|
|
|
55
61
|
configureSettings: (
|
|
56
62
|
{
|
|
@@ -61,17 +67,9 @@ export const userSettings: UserSettings = {
|
|
|
61
67
|
validateUsername,
|
|
62
68
|
validateNewUser,
|
|
63
69
|
validatePassword,
|
|
64
|
-
}
|
|
70
|
+
},
|
|
65
71
|
db: mongodb.Db,
|
|
66
72
|
) => {
|
|
67
|
-
const defaultAutoMessagingAfterUserCreation = true;
|
|
68
|
-
const defaultMergeUserCartsOnLogin = true;
|
|
69
|
-
|
|
70
|
-
const defaultEarliestValidTokenDate = () => {
|
|
71
|
-
// 1 hour ago
|
|
72
|
-
return new Date(new Date().getTime() - 1000 * 60 * 60);
|
|
73
|
-
};
|
|
74
|
-
|
|
75
73
|
const defaultValidateEmail = async (rawEmail: string) => {
|
|
76
74
|
if (!rawEmail?.includes?.('@')) return false;
|
|
77
75
|
const emailAlreadyExists = await db
|
|
@@ -88,18 +86,6 @@ export const userSettings: UserSettings = {
|
|
|
88
86
|
if (usernameAlreadyExists) return false;
|
|
89
87
|
return true;
|
|
90
88
|
};
|
|
91
|
-
const defaultValidateNewUser = async (user: UserRegistrationData) => {
|
|
92
|
-
return {
|
|
93
|
-
...user,
|
|
94
|
-
username: user.username?.trim().toLowerCase(),
|
|
95
|
-
email: user.email?.trim().toLowerCase(),
|
|
96
|
-
password: user.password ?? null,
|
|
97
|
-
};
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
const defaultValidatePassword = async (password: string) => {
|
|
101
|
-
return password?.length >= 8;
|
|
102
|
-
};
|
|
103
89
|
|
|
104
90
|
userSettings.mergeUserCartsOnLogin = mergeUserCartsOnLogin ?? defaultMergeUserCartsOnLogin;
|
|
105
91
|
userSettings.autoMessagingAfterUserCreation =
|