@unchainedshop/core-users 3.0.0-alpha6 → 3.0.0-rc1

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.
Files changed (51) hide show
  1. package/lib/module/configureUsersModule.d.ts +10 -68
  2. package/lib/module/configureUsersModule.d.ts.map +1 -1
  3. package/lib/module/configureUsersModule.js +58 -52
  4. package/lib/module/configureUsersModule.js.map +1 -1
  5. package/lib/module/configureUsersModule_BACKUP_57123.d.ts +73 -0
  6. package/lib/module/configureUsersModule_BACKUP_57123.d.ts.map +1 -0
  7. package/lib/module/configureUsersModule_BACKUP_57123.js +567 -0
  8. package/lib/module/configureUsersModule_BACKUP_57123.js.map +1 -0
  9. package/lib/module/configureUsersModule_BASE_57123.d.ts +131 -0
  10. package/lib/module/configureUsersModule_BASE_57123.d.ts.map +1 -0
  11. package/lib/module/configureUsersModule_BASE_57123.js +563 -0
  12. package/lib/module/configureUsersModule_BASE_57123.js.map +1 -0
  13. package/lib/module/configureUsersModule_LOCAL_57123.d.ts +131 -0
  14. package/lib/module/configureUsersModule_LOCAL_57123.d.ts.map +1 -0
  15. package/lib/module/configureUsersModule_LOCAL_57123.js +564 -0
  16. package/lib/module/configureUsersModule_LOCAL_57123.js.map +1 -0
  17. package/lib/module/configureUsersModule_REMOTE_57123.d.ts +74 -0
  18. package/lib/module/configureUsersModule_REMOTE_57123.d.ts.map +1 -0
  19. package/lib/module/configureUsersModule_REMOTE_57123.js +567 -0
  20. package/lib/module/configureUsersModule_REMOTE_57123.js.map +1 -0
  21. package/lib/module/configureUsersWebAuthnModule.d.ts.map +1 -1
  22. package/lib/module/configureUsersWebAuthnModule.js.map +1 -1
  23. package/lib/types.d.ts +5 -2
  24. package/lib/types.d.ts.map +1 -1
  25. package/lib/users-index.d.ts +0 -1
  26. package/lib/users-index.d.ts.map +1 -1
  27. package/lib/users-index.js +0 -1
  28. package/lib/users-index.js.map +1 -1
  29. package/package.json +14 -17
  30. package/src/module/configureUsersModule.ts +78 -72
  31. package/src/module/configureUsersWebAuthnModule.ts +0 -1
  32. package/src/types.ts +5 -2
  33. package/src/users-index.ts +0 -1
  34. package/tests/mock/user-mock.ts +2 -3
  35. package/tsconfig.json +5 -7
  36. package/lib/services/migrateUserDataService.d.ts +0 -4
  37. package/lib/services/migrateUserDataService.d.ts.map +0 -1
  38. package/lib/services/migrateUserDataService.js +0 -18
  39. package/lib/services/migrateUserDataService.js.map +0 -1
  40. package/lib/services/updateUserAvatarAfterUploadService.d.ts +0 -15
  41. package/lib/services/updateUserAvatarAfterUploadService.d.ts.map +0 -1
  42. package/lib/services/updateUserAvatarAfterUploadService.js +0 -24
  43. package/lib/services/updateUserAvatarAfterUploadService.js.map +0 -1
  44. package/lib/services/userServices.d.ts +0 -8
  45. package/lib/services/userServices.d.ts.map +0 -1
  46. package/lib/services/userServices.js +0 -7
  47. package/lib/services/userServices.js.map +0 -1
  48. package/src/module/sha256.ts +0 -5
  49. package/src/services/migrateUserDataService.ts +0 -37
  50. package/src/services/updateUserAvatarAfterUploadService.ts +0 -42
  51. package/src/services/userServices.ts +0 -15
package/lib/types.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { TimestampFields, Address, Contact, mongodb } from '@unchainedshop/mongodb';
1
+ import { TimestampFields, Address, Contact, mongodb } from '@unchainedshop/mongodb';
2
+ import { DateFilterInput } from '@unchainedshop/utils';
2
3
  export interface PushSubscription {
3
4
  _id: string;
4
5
  userAgent: string;
@@ -47,7 +48,7 @@ export interface PushSubscriptionObject {
47
48
  };
48
49
  }
49
50
  export type User = {
50
- _id?: string;
51
+ _id: string;
51
52
  deleted?: Date;
52
53
  avatarId?: string;
53
54
  emails: Array<Email>;
@@ -67,6 +68,8 @@ export type User = {
67
68
  export type UserQuery = mongodb.Filter<User> & {
68
69
  includeGuests?: boolean;
69
70
  queryString?: string;
71
+ emailVerified?: boolean;
72
+ lastLogin?: DateFilterInput;
70
73
  };
71
74
  export interface WebAuthnCredentialsCreationRequest {
72
75
  challenge: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,4 @@
1
1
  export * from './types.js';
2
2
  export * from './module/configureUsersModule.js';
3
- export * from './services/userServices.js';
4
3
  export * from './users-settings.js';
5
4
  //# sourceMappingURL=users-index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"users-index.d.ts","sourceRoot":"","sources":["../src/users-index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kCAAkC,CAAC;AACjD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"users-index.d.ts","sourceRoot":"","sources":["../src/users-index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC"}
@@ -1,5 +1,4 @@
1
1
  export * from './types.js';
2
2
  export * from './module/configureUsersModule.js';
3
- export * from './services/userServices.js';
4
3
  export * from './users-settings.js';
5
4
  //# sourceMappingURL=users-index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"users-index.js","sourceRoot":"","sources":["../src/users-index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kCAAkC,CAAC;AACjD,cAAc,4BAA4B,CAAC;AAC3C,cAAc,qBAAqB,CAAC"}
1
+ {"version":3,"file":"users-index.js","sourceRoot":"","sources":["../src/users-index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,kCAAkC,CAAC;AACjD,cAAc,qBAAqB,CAAC"}
package/package.json CHANGED
@@ -1,16 +1,13 @@
1
1
  {
2
2
  "name": "@unchainedshop/core-users",
3
- "version": "3.0.0-alpha6",
3
+ "version": "3.0.0-rc1",
4
4
  "main": "lib/users-index.js",
5
- "exports": {
6
- ".": "./lib/users-index.js",
7
- "./*": "./lib/*"
8
- },
9
5
  "types": "lib/users-index.d.ts",
10
6
  "type": "module",
11
7
  "scripts": {
12
- "clean": "rm -rf lib",
13
- "prepublishOnly": "npm run clean && tsc",
8
+ "clean": "tsc -b --clean",
9
+ "build": "tsc -b",
10
+ "prepublishOnly": "npm run clean && npm run build",
14
11
  "watch": "tsc -w",
15
12
  "test": "NODE_OPTIONS=--experimental-vm-modules jest --detectOpenHandles --forceExit",
16
13
  "test:watch": "NODE_OPTIONS=--experimental-vm-modules jest --watch --detectOpenHandles --forceExit"
@@ -31,19 +28,19 @@
31
28
  },
32
29
  "homepage": "https://github.com/unchainedshop/unchained#readme",
33
30
  "dependencies": {
34
- "@unchainedshop/events": "^3.0.0-alpha4",
35
- "@unchainedshop/file-upload": "^3.0.0-alpha4",
36
- "@unchainedshop/logger": "^3.0.0-alpha4",
37
- "@unchainedshop/roles": "^3.0.0-alpha4",
38
- "@unchainedshop/utils": "^3.0.0-alpha4",
39
- "bcryptjs": "^2.4.3",
40
- "fido2-lib": "^3.5.3",
41
- "uuid": "^10.0.0"
31
+ "@node-rs/bcrypt": "^1.10.7",
32
+ "@unchainedshop/events": "^3.0.0-rc1",
33
+ "@unchainedshop/file-upload": "^3.0.0-rc1",
34
+ "@unchainedshop/logger": "^3.0.0-rc1",
35
+ "@unchainedshop/mongodb": "^3.0.0-rc1",
36
+ "@unchainedshop/roles": "^3.0.0-rc1",
37
+ "@unchainedshop/utils": "^3.0.0-rc1",
38
+ "fido2-lib": "^3.5.3"
42
39
  },
43
40
  "devDependencies": {
44
- "@types/node": "^22.8.1",
41
+ "@types/node": "^22.10.2",
45
42
  "jest": "^29.7.0",
46
43
  "ts-jest": "^29.2.5",
47
- "typescript": "^5.6.3"
44
+ "typescript": "^5.7.2"
48
45
  }
49
46
  }
@@ -1,5 +1,5 @@
1
- import bcrypt from 'bcryptjs';
2
- import { ModuleInput } from '@unchainedshop/mongodb';
1
+ import bcrypt from '@node-rs/bcrypt';
2
+ import { ModuleInput, Address, Contact } from '@unchainedshop/mongodb';
3
3
  import { User, UserQuery, Email, UserLastLogin, UserProfile, UserData } from '../types.js';
4
4
  import { emit, registerEvents } from '@unchainedshop/events';
5
5
  import {
@@ -8,18 +8,12 @@ import {
8
8
  mongodb,
9
9
  generateDbObjectId,
10
10
  } from '@unchainedshop/mongodb';
11
- import { systemLocale } from '@unchainedshop/utils';
12
- import { FileDirector } from '@unchainedshop/file-upload';
13
- import { SortDirection, SortOption } from '@unchainedshop/utils';
11
+ import { systemLocale, SortDirection, SortOption, sha256 } from '@unchainedshop/utils';
14
12
  import { UsersCollection } from '../db/UsersCollection.js';
15
13
  import addMigrations from './addMigrations.js';
16
14
  import { userSettings, UserSettingsOptions } from '../users-settings.js';
17
15
  import { configureUsersWebAuthnModule, UsersWebAuthnModule } from './configureUsersWebAuthnModule.js';
18
16
  import * as pbkdf2 from './pbkdf2.js';
19
- import * as sha256 from './sha256.js';
20
- import type { Address, Contact } from '@unchainedshop/mongodb';
21
- import { UserServices } from '../services/userServices.js';
22
- import { FileServices, FilesModule } from '@unchainedshop/core-files';
23
17
 
24
18
  export type UsersModule = {
25
19
  // Submodules
@@ -76,7 +70,8 @@ export type UsersModule = {
76
70
  _id: string,
77
71
  { profile, meta }: { profile?: UserProfile; meta?: any },
78
72
  ) => Promise<User>;
79
- delete: (userId: string) => Promise<User>;
73
+ markDeleted: (userId: string) => Promise<User>;
74
+ deletePermanently: (params: { userId: string }) => Promise<User>;
80
75
  updateRoles: (_id: string, roles: Array<string>) => Promise<User>;
81
76
  updateTags: (_id: string, tags: Array<string>) => Promise<User>;
82
77
  updateUser: (
@@ -93,6 +88,9 @@ export type UsersModule = {
93
88
  },
94
89
  ) => Promise<void>;
95
90
  removePushSubscription: (userId: string, p256dh: string) => Promise<void>;
91
+ hashPassword(password: string): Promise<{
92
+ pbkdf2: string;
93
+ }>;
96
94
  };
97
95
 
98
96
  const USER_EVENTS = [
@@ -118,36 +116,44 @@ export const removeConfidentialServiceHashes = (rawUser: User): User => {
118
116
  return user;
119
117
  };
120
118
 
121
- export const buildFindSelector = ({ includeGuests, queryString, ...rest }: UserQuery) => {
119
+ export const buildFindSelector = ({
120
+ includeGuests,
121
+ queryString,
122
+ emailVerified,
123
+ lastLogin,
124
+ ...rest
125
+ }: UserQuery) => {
122
126
  const selector: mongodb.Filter<User> = { ...rest, deleted: null };
123
127
  if (!includeGuests) selector.guest = { $in: [false, null] };
128
+ if (emailVerified === true) {
129
+ selector['emails.verified'] = true;
130
+ }
131
+ if (emailVerified === false) {
132
+ // We need to use $ne here else we'd also find users with many emails where one is
133
+ // unverified
134
+ selector['emails.verified'] = { $ne: true };
135
+ }
136
+ if (lastLogin?.start) {
137
+ selector['lastLogin.timestamp'] = { $exists: true };
138
+ }
139
+ if (lastLogin?.end) {
140
+ selector['lastLogin.timestamp'].$lte = new Date(lastLogin.end);
141
+ }
142
+ if (lastLogin?.start) {
143
+ selector['lastLogin.timestamp'].$gte = new Date(lastLogin.start);
144
+ }
124
145
  if (queryString) {
125
146
  (selector as any).$text = { $search: queryString };
126
147
  }
127
148
  return selector;
128
149
  };
129
150
 
130
- FileDirector.registerFileUploadCallback<{
131
- services: {
132
- users: UserServices;
133
- files: FileServices;
134
- };
135
- modules: {
136
- users: UsersModule;
137
- files: FilesModule;
138
- };
139
- }>('user-avatars', async (file, context) => {
140
- const { services } = context;
141
- return services.users.updateUserAvatarAfterUpload({ file }, context);
142
- });
143
-
144
151
  export const configureUsersModule = async ({
145
152
  db,
146
153
  options,
147
154
  migrationRepository,
148
- }: ModuleInput<UserSettingsOptions>) => {
155
+ }: ModuleInput<UserSettingsOptions>): Promise<UsersModule> => {
149
156
  userSettings.configureSettings(options || {}, db);
150
-
151
157
  registerEvents(USER_EVENTS);
152
158
  const Users = await UsersCollection(db);
153
159
 
@@ -185,13 +191,13 @@ export const configureUsersModule = async ({
185
191
  when: Date;
186
192
  }> {
187
193
  if (!plainToken) return null;
188
- const token = await sha256.hash(plainToken);
194
+ const token = await sha256(plainToken);
189
195
  const user = await Users.findOne(
190
196
  {
191
197
  'services.email.verificationTokens': {
192
198
  $elemMatch: {
193
199
  token,
194
- when: { $gt: new Date().getTime() },
200
+ when: { $gt: new Date(new Date().getTime() - 1000 * 60 * 60) },
195
201
  },
196
202
  },
197
203
  },
@@ -231,13 +237,13 @@ export const configureUsersModule = async ({
231
237
  },
232
238
 
233
239
  async findUserByResetToken(plainToken: string): Promise<User> {
234
- const token = await sha256.hash(plainToken);
240
+ const token = await sha256(plainToken);
235
241
  const user = await Users.findOne(
236
242
  {
237
243
  'services.password.reset': {
238
244
  $elemMatch: {
239
245
  token,
240
- when: { $gt: new Date().getTime() },
246
+ when: { $gt: new Date(new Date().getTime() - 1000 * 60 * 60) },
241
247
  },
242
248
  },
243
249
  },
@@ -247,7 +253,7 @@ export const configureUsersModule = async ({
247
253
  },
248
254
 
249
255
  async findUserByToken(plainToken?: string): Promise<User> {
250
- const token = await sha256.hash(plainToken);
256
+ const token = await sha256(plainToken);
251
257
 
252
258
  if (token) {
253
259
  return Users.findOne({
@@ -276,7 +282,7 @@ export const configureUsersModule = async ({
276
282
  offset?: number;
277
283
  }): Promise<Array<User>> {
278
284
  const defaultSort = [{ key: 'created', value: SortDirection.ASC }] as SortOption[];
279
- const selector = buildFindSelector(query);
285
+ const selector = buildFindSelector({ ...query });
280
286
 
281
287
  if (query.queryString) {
282
288
  return Users.find(selector, {
@@ -295,9 +301,7 @@ export const configureUsersModule = async ({
295
301
  },
296
302
 
297
303
  async userExists({ userId }: { userId: string }): Promise<boolean> {
298
- const selector = generateDbFilterById<User>(userId);
299
- selector.deleted = null; // skip deleted users when checked for existance!
300
- const userCount = await Users.countDocuments(selector, { limit: 1 });
304
+ const userCount = await Users.countDocuments({ _id: userId, deleted: null }, { limit: 1 });
301
305
  return userCount === 1;
302
306
  },
303
307
 
@@ -359,6 +363,7 @@ export const configureUsersModule = async ({
359
363
  _id: normalizedUserData._id || generateDbObjectId(),
360
364
  roles: roles || [],
361
365
  initialPassword: Boolean(initialPassword),
366
+ services,
362
367
  emails: email ? [{ address: email, verified: false }] : [],
363
368
  created: new Date(),
364
369
  };
@@ -409,7 +414,7 @@ export const configureUsersModule = async ({
409
414
  plainPassword: string,
410
415
  ): Promise<boolean> {
411
416
  if (bcryptHash) {
412
- const password = await sha256.hash(plainPassword);
417
+ const password = await sha256(plainPassword);
413
418
  return bcrypt.compare(password, bcryptHash);
414
419
  }
415
420
  if (pbkdf2SaltAndHash) {
@@ -450,9 +455,9 @@ export const configureUsersModule = async ({
450
455
  async sendResetPasswordEmail(userId: string, email: string, isEnrollment?: boolean): Promise<void> {
451
456
  const plainToken = crypto.randomUUID();
452
457
  const resetToken = {
453
- token: await sha256.hash(plainToken),
458
+ token: await sha256(plainToken),
454
459
  address: email,
455
- when: new Date().getTime() + 1000 * 60 * 60, // 1 hour
460
+ when: new Date(),
456
461
  };
457
462
 
458
463
  await Users.updateOne(
@@ -475,9 +480,9 @@ export const configureUsersModule = async ({
475
480
  async sendVerificationEmail(userId: string, email: string): Promise<void> {
476
481
  const plainToken = crypto.randomUUID();
477
482
  const verificationToken = {
478
- token: await sha256.hash(plainToken),
483
+ token: await sha256(plainToken),
479
484
  address: email,
480
- when: new Date().getTime() + 1000 * 60 * 60, // 1 hour
485
+ when: new Date(),
481
486
  };
482
487
 
483
488
  await Users.updateOne(
@@ -601,42 +606,38 @@ export const configureUsersModule = async ({
601
606
  return user;
602
607
  },
603
608
 
604
- delete: async (userId: string): Promise<User> => {
605
- const userFilter = generateDbFilterById(userId);
606
-
607
- const existingUser = await Users.findOne(userFilter, {
608
- projection: { emails: true, username: true },
609
- });
610
- if (!existingUser) return null;
611
-
612
- const uuid = crypto.randomUUID();
613
- const obfuscatedEmails = existingUser.emails?.flatMap(({ address, verified }) => {
614
- if (!verified) return [];
615
- return [
616
- {
617
- address: `${address}@${uuid}.unchained.local`,
618
- verified: true,
609
+ markDeleted: async (userId: string): Promise<User> => {
610
+ const user = await Users.findOneAndUpdate(
611
+ { _id: userId },
612
+ {
613
+ $set: {
614
+ username: `deleted-${Date.now()}`,
615
+ deleted: new Date(),
616
+ emails: [],
617
+ roles: [],
618
+ profile: null,
619
+ lastBillingAddress: null,
620
+ services: {},
621
+ pushSubscriptions: [],
622
+ avatarId: null,
623
+ initialPassword: false,
624
+ lastContact: null,
625
+ lastLogin: null,
619
626
  },
620
- ];
621
- });
622
-
623
- const obfuscatedUsername = existingUser.username ? `${existingUser.username}-${uuid}` : null;
624
-
625
- Users.updateOne(userFilter, {
626
- $set: {
627
- emails: obfuscatedEmails,
628
- username: obfuscatedUsername,
629
- services: {},
630
627
  },
631
- });
628
+ { returnDocument: 'after' },
629
+ );
632
630
 
633
- const user = await Users.findOneAndDelete(userFilter);
634
631
  await emit('USER_REMOVE', {
635
- user: removeConfidentialServiceHashes(user),
632
+ user,
636
633
  });
637
634
  return user;
638
635
  },
639
636
 
637
+ deletePermanently: async ({ userId }: { userId: string }): Promise<User> => {
638
+ return Users.findOneAndDelete({ _id: userId });
639
+ },
640
+
640
641
  updateProfile: async (
641
642
  userId: string,
642
643
  updatedData: { profile?: UserProfile; meta?: any },
@@ -679,6 +680,8 @@ export const configureUsersModule = async ({
679
680
  const userFilter = generateDbFilterById(_id);
680
681
  const user = await Users.findOne(userFilter, {});
681
682
 
683
+ if (!lastBillingAddress) return user;
684
+
682
685
  const modifier = {
683
686
  $set: {
684
687
  lastBillingAddress,
@@ -701,9 +704,12 @@ export const configureUsersModule = async ({
701
704
  returnDocument: 'after',
702
705
  });
703
706
 
704
- await emit('USER_UPDATE_BILLING_ADDRESS', {
705
- user: removeConfidentialServiceHashes(user),
706
- });
707
+ if (updatedUser) {
708
+ await emit('USER_UPDATE_BILLING_ADDRESS', {
709
+ user: removeConfidentialServiceHashes(updatedUser),
710
+ });
711
+ }
712
+
707
713
  return updatedUser;
708
714
  },
709
715
 
@@ -1,4 +1,3 @@
1
- /// <reference lib="dom" />
2
1
  import { ModuleInput } from '@unchainedshop/mongodb';
3
2
  import {
4
3
  Fido2Lib,
package/src/types.ts CHANGED
@@ -1,4 +1,5 @@
1
- import type { TimestampFields, Address, Contact, mongodb } from '@unchainedshop/mongodb';
1
+ import { TimestampFields, Address, Contact, mongodb } from '@unchainedshop/mongodb';
2
+ import { DateFilterInput } from '@unchainedshop/utils';
2
3
 
3
4
  export interface PushSubscription {
4
5
  _id: string;
@@ -55,7 +56,7 @@ export interface PushSubscriptionObject {
55
56
  }
56
57
 
57
58
  export type User = {
58
- _id?: string;
59
+ _id: string;
59
60
  deleted?: Date;
60
61
  avatarId?: string;
61
62
  emails: Array<Email>;
@@ -76,6 +77,8 @@ export type User = {
76
77
  export type UserQuery = mongodb.Filter<User> & {
77
78
  includeGuests?: boolean;
78
79
  queryString?: string;
80
+ emailVerified?: boolean;
81
+ lastLogin?: DateFilterInput;
79
82
  };
80
83
 
81
84
  export interface WebAuthnCredentialsCreationRequest {
@@ -1,4 +1,3 @@
1
1
  export * from './types.js';
2
2
  export * from './module/configureUsersModule.js';
3
- export * from './services/userServices.js';
4
3
  export * from './users-settings.js';
@@ -2,7 +2,7 @@ export default {
2
2
  _id: 'PKve0k9fLCUzn2EUi',
3
3
  guest: false,
4
4
  initialPassword: false,
5
- lastBillingAddress: {},
5
+ lastBillingAddress: null,
6
6
  profile: {
7
7
  address: {
8
8
  firstName: null,
@@ -65,9 +65,8 @@ export default {
65
65
  remotePort: '42978',
66
66
  userAgent:
67
67
  'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36',
68
- locale: 'de_CH',
68
+ locale: 'de-CH',
69
69
  countryCode: 'CH',
70
70
  },
71
71
  updated: new Date('2022-11-30T11:02:19.624Z'),
72
- updatedBy: 'PKve0k9fLCUzn2EUi',
73
72
  };
package/tsconfig.json CHANGED
@@ -1,11 +1,9 @@
1
1
  {
2
2
  "extends": "../shared/base.tsconfig.json",
3
3
  "compilerOptions": {
4
- "rootDir": "src",
5
- "outDir": "lib",
4
+ "declarationDir": "./lib",
5
+ "rootDir": "./src",
6
+ "outDir": "./lib"
6
7
  },
7
- "exclude": ["**/*.test.ts", "**/*.test.js", "tests"],
8
- "include": [
9
- "./src"
10
- ]
11
- }
8
+ "exclude": ["**/*.test.ts", "**/*.test.js", "tests", "lib"]
9
+ }
@@ -1,4 +0,0 @@
1
- import { UnchainedCore } from '@unchainedshop/core';
2
- export type MigrateUserDataService = (userIdBeforeLogin: any, userId: any, unchainedAPI: UnchainedCore) => Promise<void>;
3
- export declare const migrateUserDataService: MigrateUserDataService;
4
- //# sourceMappingURL=migrateUserDataService.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"migrateUserDataService.d.ts","sourceRoot":"","sources":["../../src/services/migrateUserDataService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,MAAM,MAAM,sBAAsB,GAAG,CACnC,iBAAiB,KAAA,EACjB,MAAM,KAAA,EACN,YAAY,EAAE,aAAa,KACxB,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,eAAO,MAAM,sBAAsB,EAAE,sBA2BpC,CAAC"}
@@ -1,18 +0,0 @@
1
- import { userSettings } from '../users-settings.js';
2
- export const migrateUserDataService = async (userIdBeforeLogin, userId, unchainedAPI) => {
3
- const user = await unchainedAPI.modules.users.findUserById(userId);
4
- const userBeforeLogin = await unchainedAPI.modules.users.findUserById(userIdBeforeLogin);
5
- await unchainedAPI.services.orders.migrateOrderCarts({
6
- fromUserId: userIdBeforeLogin,
7
- toUserId: userId,
8
- shouldMerge: userSettings.mergeUserCartsOnLogin,
9
- countryContext: userBeforeLogin.lastLogin?.countryCode || user.lastLogin?.countryCode,
10
- }, unchainedAPI);
11
- await unchainedAPI.services.bookmarks.migrateBookmarks({
12
- fromUserId: userIdBeforeLogin,
13
- toUserId: userId,
14
- shouldMerge: userSettings.mergeUserCartsOnLogin,
15
- countryContext: userBeforeLogin.lastLogin?.countryCode || user.lastLogin?.countryCode,
16
- }, unchainedAPI);
17
- };
18
- //# sourceMappingURL=migrateUserDataService.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"migrateUserDataService.js","sourceRoot":"","sources":["../../src/services/migrateUserDataService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAQpD,MAAM,CAAC,MAAM,sBAAsB,GAA2B,KAAK,EACjE,iBAAiB,EACjB,MAAM,EACN,YAAY,EACZ,EAAE;IACF,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;IACnE,MAAM,eAAe,GAAG,MAAM,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,iBAAiB,CAAC,CAAC;IAEzF,MAAM,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,iBAAiB,CAClD;QACE,UAAU,EAAE,iBAAiB;QAC7B,QAAQ,EAAE,MAAM;QAChB,WAAW,EAAE,YAAY,CAAC,qBAAqB;QAC/C,cAAc,EAAE,eAAe,CAAC,SAAS,EAAE,WAAW,IAAI,IAAI,CAAC,SAAS,EAAE,WAAW;KACtF,EACD,YAAY,CACb,CAAC;IAEF,MAAM,YAAY,CAAC,QAAQ,CAAC,SAAS,CAAC,gBAAgB,CACpD;QACE,UAAU,EAAE,iBAAiB;QAC7B,QAAQ,EAAE,MAAM;QAChB,WAAW,EAAE,YAAY,CAAC,qBAAqB;QAC/C,cAAc,EAAE,eAAe,CAAC,SAAS,EAAE,WAAW,IAAI,IAAI,CAAC,SAAS,EAAE,WAAW;KACtF,EACD,YAAY,CACb,CAAC;AACJ,CAAC,CAAC"}
@@ -1,15 +0,0 @@
1
- import { File, FileServices, FilesModule } from '@unchainedshop/core-files';
2
- import { UsersModule } from '../users-index.js';
3
- export type UpdateUserAvatarAfterUploadService = (params: {
4
- file: File;
5
- }, context: {
6
- modules: {
7
- files: FilesModule;
8
- users: UsersModule;
9
- };
10
- services: {
11
- files: FileServices;
12
- };
13
- }) => Promise<void>;
14
- export declare const updateUserAvatarAfterUploadService: UpdateUserAvatarAfterUploadService;
15
- //# sourceMappingURL=updateUserAvatarAfterUploadService.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"updateUserAvatarAfterUploadService.d.ts","sourceRoot":"","sources":["../../src/services/updateUserAvatarAfterUploadService.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE5E,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,MAAM,MAAM,kCAAkC,GAAG,CAC/C,MAAM,EAAE;IAAE,IAAI,EAAE,IAAI,CAAA;CAAE,EACtB,OAAO,EAAE;IACP,OAAO,EAAE;QAAE,KAAK,EAAE,WAAW,CAAC;QAAC,KAAK,EAAE,WAAW,CAAA;KAAE,CAAC;IACpD,QAAQ,EAAE;QAAE,KAAK,EAAE,YAAY,CAAA;KAAE,CAAC;CACnC,KACE,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,eAAO,MAAM,kCAAkC,EAAE,kCA6BhD,CAAC"}
@@ -1,24 +0,0 @@
1
- import { log, LogLevel } from '@unchainedshop/logger';
2
- export const updateUserAvatarAfterUploadService = async ({ file }, context) => {
3
- const { modules, services } = context;
4
- const { userId } = file.meta;
5
- const files = await modules.files.findFiles({
6
- _id: { $ne: file._id },
7
- path: file.path,
8
- 'meta.userId': userId,
9
- });
10
- const fileIds = files.map((f) => f._id);
11
- try {
12
- if (fileIds?.length) {
13
- await services.files.removeFiles({
14
- fileIds,
15
- }, context);
16
- }
17
- }
18
- catch (e) {
19
- // cleanup error, not critical
20
- log(`could not clean up all old avatars: ${e.message}`, { level: LogLevel.Warning });
21
- }
22
- await modules.users.updateAvatar(userId, file._id);
23
- };
24
- //# sourceMappingURL=updateUserAvatarAfterUploadService.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"updateUserAvatarAfterUploadService.js","sourceRoot":"","sources":["../../src/services/updateUserAvatarAfterUploadService.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAWtD,MAAM,CAAC,MAAM,kCAAkC,GAAuC,KAAK,EACzF,EAAE,IAAI,EAAE,EACR,OAAO,EACP,EAAE;IACF,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IACtC,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,IAA0B,CAAC;IAEnD,MAAM,KAAK,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC;QAC1C,GAAG,EAAE,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE;QACtB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,aAAa,EAAE,MAAM;KACtB,CAAC,CAAC;IACH,MAAM,OAAO,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAExC,IAAI,CAAC;QACH,IAAI,OAAO,EAAE,MAAM,EAAE,CAAC;YACpB,MAAM,QAAQ,CAAC,KAAK,CAAC,WAAW,CAC9B;gBACE,OAAO;aACR,EACD,OAAO,CACR,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,8BAA8B;QAC9B,GAAG,CAAC,uCAAwC,CAAW,CAAC,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IAClG,CAAC;IAED,MAAM,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;AACrD,CAAC,CAAC"}
@@ -1,8 +0,0 @@
1
- import { UpdateUserAvatarAfterUploadService } from './updateUserAvatarAfterUploadService.js';
2
- import { MigrateUserDataService } from './migrateUserDataService.js';
3
- export interface UserServices {
4
- updateUserAvatarAfterUpload: UpdateUserAvatarAfterUploadService;
5
- migrateUserData: MigrateUserDataService;
6
- }
7
- export declare const userServices: UserServices;
8
- //# sourceMappingURL=userServices.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"userServices.d.ts","sourceRoot":"","sources":["../../src/services/userServices.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,kCAAkC,EACnC,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAA0B,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAE7F,MAAM,WAAW,YAAY;IAC3B,2BAA2B,EAAE,kCAAkC,CAAC;IAChE,eAAe,EAAE,sBAAsB,CAAC;CACzC;AAED,eAAO,MAAM,YAAY,EAAE,YAG1B,CAAC"}
@@ -1,7 +0,0 @@
1
- import { updateUserAvatarAfterUploadService, } from './updateUserAvatarAfterUploadService.js';
2
- import { migrateUserDataService } from './migrateUserDataService.js';
3
- export const userServices = {
4
- updateUserAvatarAfterUpload: updateUserAvatarAfterUploadService,
5
- migrateUserData: migrateUserDataService,
6
- };
7
- //# sourceMappingURL=userServices.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"userServices.js","sourceRoot":"","sources":["../../src/services/userServices.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,kCAAkC,GAEnC,MAAM,yCAAyC,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAA0B,MAAM,6BAA6B,CAAC;AAO7F,MAAM,CAAC,MAAM,YAAY,GAAiB;IACxC,2BAA2B,EAAE,kCAAkC;IAC/D,eAAe,EAAE,sBAAsB;CACxC,CAAC"}
@@ -1,5 +0,0 @@
1
- export const hash = async (message) => {
2
- const bytes = new TextEncoder().encode(message);
3
- const hashBuffer = await crypto.subtle.digest('SHA-256', bytes);
4
- return Buffer.from(hashBuffer).toString('hex');
5
- };
@@ -1,37 +0,0 @@
1
- import { UnchainedCore } from '@unchainedshop/core';
2
- import { userSettings } from '../users-settings.js';
3
-
4
- export type MigrateUserDataService = (
5
- userIdBeforeLogin,
6
- userId,
7
- unchainedAPI: UnchainedCore,
8
- ) => Promise<void>;
9
-
10
- export const migrateUserDataService: MigrateUserDataService = async (
11
- userIdBeforeLogin,
12
- userId,
13
- unchainedAPI,
14
- ) => {
15
- const user = await unchainedAPI.modules.users.findUserById(userId);
16
- const userBeforeLogin = await unchainedAPI.modules.users.findUserById(userIdBeforeLogin);
17
-
18
- await unchainedAPI.services.orders.migrateOrderCarts(
19
- {
20
- fromUserId: userIdBeforeLogin,
21
- toUserId: userId,
22
- shouldMerge: userSettings.mergeUserCartsOnLogin,
23
- countryContext: userBeforeLogin.lastLogin?.countryCode || user.lastLogin?.countryCode,
24
- },
25
- unchainedAPI,
26
- );
27
-
28
- await unchainedAPI.services.bookmarks.migrateBookmarks(
29
- {
30
- fromUserId: userIdBeforeLogin,
31
- toUserId: userId,
32
- shouldMerge: userSettings.mergeUserCartsOnLogin,
33
- countryContext: userBeforeLogin.lastLogin?.countryCode || user.lastLogin?.countryCode,
34
- },
35
- unchainedAPI,
36
- );
37
- };