@unchainedshop/core-users 4.8.16 → 4.8.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.
@@ -0,0 +1,2 @@
1
+ import type { MigrationRepository } from '@unchainedshop/mongodb';
2
+ export default function normalizeProfilePhone(repository: MigrationRepository): void;
@@ -0,0 +1,52 @@
1
+ import { normalizePhoneNumber } from '@unchainedshop/utils';
2
+ import { UsersCollection } from "../db/UsersCollection.js";
3
+ export default function normalizeProfilePhone(repository) {
4
+ repository?.register({
5
+ id: 20260625120200,
6
+ name: 'Normalize user.profile.phoneMobile and user.lastContact.telNumber to E.164 format',
7
+ up: async ({ logger }) => {
8
+ const Users = await UsersCollection(repository.db);
9
+ const users = await Users.find({
10
+ $or: [
11
+ { 'profile.phoneMobile': { $exists: true, $nin: [null, ''] } },
12
+ { 'lastContact.telNumber': { $exists: true, $nin: [null, ''] } },
13
+ ],
14
+ }, {
15
+ projection: {
16
+ _id: true,
17
+ profile: true,
18
+ lastContact: true,
19
+ lastBillingAddress: true,
20
+ lastLogin: true,
21
+ },
22
+ }).toArray();
23
+ let changed = 0;
24
+ let skipped = 0;
25
+ for (const user of users) {
26
+ const defaultCountry = user.lastBillingAddress?.countryCode ||
27
+ user.profile?.address?.countryCode ||
28
+ user.lastLogin?.countryCode;
29
+ const $set = {};
30
+ const phoneMobile = user.profile?.phoneMobile;
31
+ const normalizedPhoneMobile = normalizePhoneNumber(phoneMobile, defaultCountry);
32
+ if (normalizedPhoneMobile && normalizedPhoneMobile !== phoneMobile) {
33
+ $set['profile.phoneMobile'] = normalizedPhoneMobile;
34
+ }
35
+ const telNumber = user.lastContact?.telNumber;
36
+ const normalizedTelNumber = normalizePhoneNumber(telNumber, defaultCountry);
37
+ if (normalizedTelNumber && normalizedTelNumber !== telNumber) {
38
+ $set['lastContact.telNumber'] = normalizedTelNumber;
39
+ }
40
+ if (Object.keys($set).length === 0) {
41
+ if ((phoneMobile && !normalizedPhoneMobile) || (telNumber && !normalizedTelNumber)) {
42
+ skipped += 1;
43
+ }
44
+ continue;
45
+ }
46
+ await Users.updateOne({ _id: user._id }, { $set });
47
+ changed += 1;
48
+ }
49
+ logger?.info(`Normalize user phone numbers: ${changed} updated, ${skipped} left unchanged (unparseable)`);
50
+ },
51
+ });
52
+ }
@@ -2,12 +2,13 @@ import * as bcrypt from 'bcryptjs';
2
2
  import { generateDbFilterById, buildSortOptions, generateDbObjectId, insensitiveTrimmedRegexOperator, escapeRegexString, } from '@unchainedshop/mongodb';
3
3
  import { UsersCollection, } from "../db/UsersCollection.js";
4
4
  import { emit, registerEvents } from '@unchainedshop/events';
5
- import { systemLocale, SortDirection, sha256 } from '@unchainedshop/utils';
5
+ import { systemLocale, SortDirection, sha256, normalizePhoneNumber, } from '@unchainedshop/utils';
6
6
  import { UserAccountAction, userSettings, } from "../users-settings.js";
7
7
  import { configureUsersWebAuthnModule } from "./configureUsersWebAuthnModule.js";
8
8
  import * as pbkdf2 from "./pbkdf2.js";
9
9
  import { verifyWeb3Signature } from "../utils/web3-verification.js";
10
10
  import convertUserLocale from "../migrations/20241218092300-convert-locale.js";
11
+ import normalizeProfilePhone from "../migrations/20260625120200-normalize-profile-phone.js";
11
12
  const USER_EVENTS = [
12
13
  'USER_ACCOUNT_ACTION',
13
14
  'USER_CREATE',
@@ -82,6 +83,7 @@ export const buildFindSelector = ({ includeGuests, includeDeleted, queryString,
82
83
  export const configureUsersModule = async (moduleInput) => {
83
84
  const { db, options, migrationRepository } = moduleInput;
84
85
  convertUserLocale(migrationRepository);
86
+ normalizeProfilePhone(migrationRepository);
85
87
  userSettings.configureSettings(options || {}, db);
86
88
  registerEvents(USER_EVENTS);
87
89
  const Users = await UsersCollection(db);
@@ -202,14 +204,9 @@ export const configureUsersModule = async (moduleInput) => {
202
204
  guest: true,
203
205
  deleted: null,
204
206
  created: { $lte: before },
205
- $and: [
206
- {
207
- $or: [
208
- { 'lastLogin.timestamp': { $exists: false } },
209
- { 'lastLogin.timestamp': { $lte: before } },
210
- ],
211
- },
212
- { $or: [{ updated: { $exists: false } }, { updated: { $lte: before } }] },
207
+ $or: [
208
+ { 'lastLogin.timestamp': { $exists: false } },
209
+ { 'lastLogin.timestamp': { $lte: before } },
213
210
  ],
214
211
  }, { projection: { _id: 1 } }).toArray();
215
212
  return users.map((u) => u._id);
@@ -647,11 +644,28 @@ export const configureUsersModule = async (moduleInput) => {
647
644
  return Users.findOne(userFilter, {});
648
645
  }
649
646
  const modifier = { $set: {} };
650
- if (profile) {
651
- modifier.$set = Object.keys(profile).reduce((acc, profileKey) => {
647
+ let normalizedProfile = profile;
648
+ if (profile?.phoneMobile) {
649
+ let defaultCountry = profile.address?.countryCode;
650
+ if (!defaultCountry) {
651
+ const existing = await Users.findOne(userFilter, {
652
+ projection: { lastBillingAddress: true, profile: true, lastLogin: true },
653
+ });
654
+ defaultCountry =
655
+ existing?.lastBillingAddress?.countryCode ||
656
+ existing?.profile?.address?.countryCode ||
657
+ existing?.lastLogin?.countryCode;
658
+ }
659
+ normalizedProfile = {
660
+ ...profile,
661
+ phoneMobile: normalizePhoneNumber(profile.phoneMobile, defaultCountry) || profile.phoneMobile,
662
+ };
663
+ }
664
+ if (normalizedProfile) {
665
+ modifier.$set = Object.keys(normalizedProfile).reduce((acc, profileKey) => {
652
666
  return {
653
667
  ...acc,
654
- [`profile.${profileKey}`]: profile[profileKey],
668
+ [`profile.${profileKey}`]: normalizedProfile[profileKey],
655
669
  };
656
670
  }, {});
657
671
  }
@@ -708,14 +722,23 @@ export const configureUsersModule = async (moduleInput) => {
708
722
  return null;
709
723
  const profile = user.profile || {};
710
724
  const isGuest = !!user.guest;
725
+ const defaultCountry = user.lastBillingAddress?.countryCode ||
726
+ profile.address?.countryCode ||
727
+ user.lastLogin?.countryCode;
728
+ const normalizedContact = lastContact?.telNumber
729
+ ? {
730
+ ...lastContact,
731
+ telNumber: normalizePhoneNumber(lastContact.telNumber, defaultCountry) || lastContact.telNumber,
732
+ }
733
+ : lastContact;
711
734
  const modifier = {
712
735
  $set: {
713
736
  updated: new Date(),
714
- lastContact,
737
+ lastContact: normalizedContact,
715
738
  },
716
739
  };
717
- if ((!profile.phoneMobile || isGuest) && lastContact.telNumber) {
718
- modifier.$set['profile.phoneMobile'] = lastContact.telNumber;
740
+ if ((!profile.phoneMobile || isGuest) && normalizedContact.telNumber) {
741
+ modifier.$set['profile.phoneMobile'] = normalizedContact.telNumber;
719
742
  }
720
743
  const updatedUser = await Users.findOneAndUpdate(userFilter, modifier, {
721
744
  returnDocument: 'after',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unchainedshop/core-users",
3
3
  "description": "User management module for the Unchained Engine with authentication support",
4
- "version": "4.8.16",
4
+ "version": "4.8.18",
5
5
  "main": "lib/users-index.js",
6
6
  "types": "lib/users-index.d.ts",
7
7
  "type": "module",