@modular-rest/server 1.11.13 → 1.11.14

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 (112) hide show
  1. package/.nvmrc +1 -0
  2. package/.prettierrc.json +9 -0
  3. package/.releaserc.json +24 -0
  4. package/README.md +79 -94
  5. package/dist/index.js +79 -0
  6. package/docs/.keep +0 -0
  7. package/docs/system-access-type.md +26 -0
  8. package/package.json +58 -45
  9. package/src/application.ts +206 -0
  10. package/src/class/cms_trigger.ts +68 -0
  11. package/src/class/collection_definition.ts +134 -0
  12. package/src/class/combinator.ts +176 -0
  13. package/src/class/database_trigger.ts +99 -0
  14. package/src/class/db_schemas.ts +44 -0
  15. package/src/class/{directory.js → directory.ts} +40 -18
  16. package/src/class/paginator.ts +51 -0
  17. package/src/class/reply.ts +59 -0
  18. package/src/class/security.ts +250 -0
  19. package/src/class/trigger_operator.ts +142 -0
  20. package/src/class/user.ts +199 -0
  21. package/src/class/validator.ts +123 -0
  22. package/src/config.ts +122 -0
  23. package/src/defult-permissions.ts +31 -0
  24. package/src/events.ts +59 -0
  25. package/src/helper/data_insertion.ts +94 -0
  26. package/src/helper/presetup_services.ts +96 -0
  27. package/src/index.ts +146 -0
  28. package/src/middlewares.ts +75 -0
  29. package/src/play-test.ts +8 -0
  30. package/src/services/data_provider/router.ts +191 -0
  31. package/src/services/data_provider/service.ts +305 -0
  32. package/src/services/data_provider/typeCasters.ts +15 -0
  33. package/src/services/file/db.ts +29 -0
  34. package/src/services/file/router.ts +88 -0
  35. package/src/services/file/service.ts +387 -0
  36. package/src/services/functions/router.ts +34 -0
  37. package/src/services/functions/service.ts +203 -0
  38. package/src/services/jwt/router.ts +73 -0
  39. package/src/services/jwt/service.ts +139 -0
  40. package/src/services/user_manager/db.ts +87 -0
  41. package/src/services/user_manager/permissionManager.ts +49 -0
  42. package/src/services/user_manager/router.ts +193 -0
  43. package/src/services/user_manager/service.ts +698 -0
  44. package/tsconfig.json +16 -9
  45. package/typedoc.mjs +41 -0
  46. package/LICENSE +0 -21
  47. package/package-lock.json +0 -1373
  48. package/src/application.js +0 -239
  49. package/src/class/cms_trigger.js +0 -20
  50. package/src/class/collection_definition.js +0 -33
  51. package/src/class/combinator.js +0 -133
  52. package/src/class/database_trigger.js +0 -20
  53. package/src/class/db_schemas.js +0 -18
  54. package/src/class/paginator.js +0 -31
  55. package/src/class/reply.js +0 -37
  56. package/src/class/security.js +0 -141
  57. package/src/class/trigger_operator.js +0 -39
  58. package/src/class/user.js +0 -112
  59. package/src/class/validator.js +0 -91
  60. package/src/config.js +0 -67
  61. package/src/events.js +0 -15
  62. package/src/helper/data_insertion.js +0 -64
  63. package/src/helper/presetup_services.js +0 -31
  64. package/src/index.js +0 -66
  65. package/src/middlewares.js +0 -44
  66. package/src/services/data_provider/router.js +0 -552
  67. package/src/services/data_provider/service.js +0 -262
  68. package/src/services/data_provider/typeCasters.js +0 -10
  69. package/src/services/file/db.js +0 -29
  70. package/src/services/file/router.js +0 -92
  71. package/src/services/file/service.js +0 -231
  72. package/src/services/functions/router.js +0 -37
  73. package/src/services/functions/service.js +0 -74
  74. package/src/services/jwt/router.js +0 -82
  75. package/src/services/jwt/service.js +0 -37
  76. package/src/services/user_manager/db.js +0 -83
  77. package/src/services/user_manager/permissionManager.js +0 -43
  78. package/src/services/user_manager/router.js +0 -176
  79. package/src/services/user_manager/service.js +0 -377
  80. package/types/application.d.ts +0 -97
  81. package/types/class/cms_trigger.d.ts +0 -24
  82. package/types/class/collection_definition.d.ts +0 -36
  83. package/types/class/combinator.d.ts +0 -30
  84. package/types/class/database_trigger.d.ts +0 -28
  85. package/types/class/db_schemas.d.ts +0 -2
  86. package/types/class/directory.d.ts +0 -2
  87. package/types/class/paginator.d.ts +0 -8
  88. package/types/class/reply.d.ts +0 -8
  89. package/types/class/security.d.ts +0 -109
  90. package/types/class/trigger_operator.d.ts +0 -19
  91. package/types/class/user.d.ts +0 -24
  92. package/types/class/validator.d.ts +0 -9
  93. package/types/config.d.ts +0 -101
  94. package/types/events.d.ts +0 -7
  95. package/types/helper/data_insertion.d.ts +0 -4
  96. package/types/helper/presetup_services.d.ts +0 -5
  97. package/types/index.d.ts +0 -72
  98. package/types/middlewares.d.ts +0 -10
  99. package/types/services/data_provider/router.d.ts +0 -3
  100. package/types/services/data_provider/service.d.ts +0 -40
  101. package/types/services/data_provider/typeCasters.d.ts +0 -3
  102. package/types/services/file/db.d.ts +0 -3
  103. package/types/services/file/router.d.ts +0 -3
  104. package/types/services/file/service.d.ts +0 -81
  105. package/types/services/functions/router.d.ts +0 -3
  106. package/types/services/functions/service.d.ts +0 -23
  107. package/types/services/jwt/router.d.ts +0 -3
  108. package/types/services/jwt/service.d.ts +0 -10
  109. package/types/services/user_manager/db.d.ts +0 -3
  110. package/types/services/user_manager/permissionManager.d.ts +0 -3
  111. package/types/services/user_manager/router.d.ts +0 -3
  112. package/types/services/user_manager/service.d.ts +0 -131
@@ -0,0 +1,698 @@
1
+ import { User } from '../../class/user';
2
+ import * as DataProvider from '../data_provider/service';
3
+ import * as JWT from '../jwt/service';
4
+ import {
5
+ getDefaultPermissionGroups,
6
+ getDefaultAnonymousPermissionGroup,
7
+ } from './permissionManager';
8
+
9
+ /**
10
+ * Service name constant
11
+ * @constant {string}
12
+ */
13
+ export const name = 'userManager';
14
+
15
+ /**
16
+ * Interface for temporary IDs storage
17
+ * @interface TemporaryId
18
+ * @property {string} type - Type of temporary ID (e.g., 'password_reset', 'verification')
19
+ * @property {string} code - Verification code
20
+ * @property {number} timestamp - Unix timestamp when the ID was created
21
+ */
22
+ interface TemporaryId {
23
+ type: string;
24
+ code: string;
25
+ timestamp: number;
26
+ }
27
+
28
+ /**
29
+ * User registration details
30
+ * @interface UserRegistrationDetail
31
+ * @property {string} [permissionGroup] - User's permission group
32
+ * @property {string} [phone] - User's phone number
33
+ * @property {string} [email] - User's email address
34
+ * @property {string} [password] - User's password (base64 encoded)
35
+ * @property {'user' | 'anonymous'} [type='user'] - User type
36
+ * @property {any} [key: string] - Additional user properties
37
+ */
38
+ interface UserRegistrationDetail {
39
+ permissionGroup?: string;
40
+ phone?: string;
41
+ email?: string;
42
+ password?: string;
43
+ type?: 'user' | 'anonymous';
44
+ [key: string]: any;
45
+ }
46
+
47
+ /**
48
+ * User manager class for handling user operations
49
+
50
+ * This service provides functionality for managing users, including:
51
+ * - User registration and authentication
52
+ * - Password management
53
+ * - Token generation and verification
54
+ * - Temporary ID handling for password reset and verification
55
+ *
56
+ */
57
+ class UserManager {
58
+ /**
59
+ * @hidden
60
+ */
61
+ private tempIds: Record<string, TemporaryId> = {};
62
+
63
+ /**
64
+ * @hidden
65
+ */
66
+ private verificationCodeGeneratorMethod?: (id: string, idType: string) => string;
67
+
68
+ /**
69
+ * @hidden
70
+ */
71
+ constructor() {}
72
+
73
+ /**
74
+ * Sets a custom method for generating verification codes
75
+ * @param {Function} generatorMethod - Function that generates verification codes
76
+ * @example
77
+ * ```typescript
78
+ * import { userManager } from '@modular-rest/server';
79
+ *
80
+ * userManager.setCustomVerificationCodeGeneratorMethod((id, type) => {
81
+ * return Math.random().toString(36).substring(2, 8).toUpperCase();
82
+ * });
83
+ * ```
84
+ */
85
+ setCustomVerificationCodeGeneratorMethod(
86
+ generatorMethod: (id: string, idType: string) => string
87
+ ): void {
88
+ this.verificationCodeGeneratorMethod = generatorMethod;
89
+ }
90
+
91
+ /**
92
+ * Generates a verification code for a user
93
+ * @param {string} id - User ID or identifier
94
+ * @param {string} idType - Type of ID (email, phone)
95
+ * @returns {string} Verification code
96
+ * @example
97
+ * ```typescript
98
+ * import { userManager } from '@modular-rest/server';
99
+ *
100
+ * const code = userManager.generateVerificationCode('user@example.com', 'email');
101
+ * // Returns: '123' (default) or custom generated code
102
+ * ```
103
+ */
104
+ generateVerificationCode(id: string, idType: string): string {
105
+ if (this.verificationCodeGeneratorMethod)
106
+ return this.verificationCodeGeneratorMethod(id, idType);
107
+
108
+ // this is default code
109
+ return '123';
110
+ }
111
+
112
+ /**
113
+ * Gets a user by their ID
114
+ * @param {string} id - The ID of the user
115
+ * @returns {Promise<User>} Promise resolving to the user
116
+ * @throws {Error} If user model is not found or user is not found
117
+ * @example
118
+ * ```typescript
119
+ * import { userManager } from '@modular-rest/server';
120
+ *
121
+ * try {
122
+ * const user = await userManager.getUserById('user123');
123
+ * console.log('User details:', user);
124
+ * } catch (error) {
125
+ * console.error('Failed to get user:', error);
126
+ * }
127
+ * ```
128
+ */
129
+ getUserById(id: string): Promise<User> {
130
+ return new Promise(async (done, reject) => {
131
+ const userModel = DataProvider.getCollection('cms', 'auth');
132
+
133
+ if (!userModel) {
134
+ return reject(new Error('User model not found'));
135
+ }
136
+
137
+ let userDoc;
138
+ try {
139
+ userDoc = await userModel.findOne({ _id: id }).select({ password: 0 }).exec();
140
+ } catch (error) {
141
+ return reject(error);
142
+ }
143
+
144
+ if (!userDoc) {
145
+ return reject(new Error('User not found'));
146
+ }
147
+
148
+ try {
149
+ const user = await User.loadFromModel(userDoc);
150
+ done(user);
151
+ } catch (error) {
152
+ reject(error);
153
+ }
154
+ });
155
+ }
156
+
157
+ /**
158
+ * Gets a user by their identity (email or phone)
159
+ * @param {string} id - The identity of the user
160
+ * @param {string} idType - The type of the identity (phone or email)
161
+ * @returns {Promise<User>} Promise resolving to the user
162
+ * @throws {Error} If user model is not found or user is not found
163
+ * @example
164
+ * ```typescript
165
+ * import { userManager } from '@modular-rest/server';
166
+ *
167
+ * // Get user by email
168
+ * const user = await userManager.getUserByIdentity('user@example.com', 'email');
169
+ *
170
+ * // Get user by phone
171
+ * const user = await userManager.getUserByIdentity('+1234567890', 'phone');
172
+ * ```
173
+ */
174
+ getUserByIdentity(id: string, idType: string): Promise<User> {
175
+ return new Promise(async (done, reject) => {
176
+ const userModel = DataProvider.getCollection('cms', 'auth');
177
+
178
+ if (!userModel) {
179
+ return reject(new Error('User model not found'));
180
+ }
181
+
182
+ const query: Record<string, any> = {};
183
+
184
+ if (idType === 'phone') query['phone'] = id;
185
+ else if (idType === 'email') query['email'] = id;
186
+
187
+ let userDoc;
188
+ try {
189
+ userDoc = await userModel.findOne(query).select({ password: 0 }).exec();
190
+ } catch (error) {
191
+ return reject(error);
192
+ }
193
+
194
+ if (!userDoc) {
195
+ return reject(new Error('User not found'));
196
+ }
197
+
198
+ try {
199
+ const user = await User.loadFromModel(userDoc);
200
+ done(user);
201
+ } catch (error) {
202
+ reject(error);
203
+ }
204
+ });
205
+ }
206
+
207
+ /**
208
+ * Gets a user by their JWT token
209
+ * @param {string} token - The JWT token of the user
210
+ * @returns {Promise<User>} Promise resolving to the user
211
+ * @throws {Error} If token is invalid or user is not found
212
+ * @example
213
+ * ```typescript
214
+ * import { userManager } from '@modular-rest/server';
215
+ *
216
+ * try {
217
+ * const user = await userManager.getUserByToken('jwt.token.here');
218
+ * console.log('Authenticated user:', user);
219
+ * } catch (error) {
220
+ * console.error('Invalid token:', error);
221
+ * }
222
+ * ```
223
+ */
224
+ async getUserByToken(token: string): Promise<User> {
225
+ const decoded = await JWT.main.verify(token);
226
+ return this.getUserById(decoded.id);
227
+ }
228
+
229
+ /**
230
+ * Checks if a verification code is valid
231
+ * @param {string} id - The ID of the user
232
+ * @param {string} code - The verification code
233
+ * @returns {boolean} Whether the verification code is valid
234
+ * @example
235
+ * ```typescript
236
+ * import { userManager } from '@modular-rest/server';
237
+ *
238
+ * const isValid = userManager.isCodeValid('user123', '123');
239
+ * if (isValid) {
240
+ * // Proceed with verification
241
+ * }
242
+ * ```
243
+ */
244
+ isCodeValid(id: string, code: string): boolean {
245
+ return this.tempIds.hasOwnProperty(id) && this.tempIds[id].code.toString() === code.toString();
246
+ }
247
+
248
+ /**
249
+ * Logs in a user and returns their JWT token
250
+ * @param {string} [id=''] - The ID of the user (email or phone)
251
+ * @param {string} [idType=''] - The type of the ID (phone or email)
252
+ * @param {string} [password=''] - The password of the user
253
+ * @returns {Promise<string>} Promise resolving to the JWT token
254
+ * @throws {Error} If user is not found or credentials are invalid
255
+ * @example
256
+ * ```typescript
257
+ * import { userManager } from '@modular-rest/server';
258
+ *
259
+ * try {
260
+ * // Login with email
261
+ * const token = await userManager.loginUser('user@example.com', 'email', 'password123');
262
+ *
263
+ * // Login with phone
264
+ * const token = await userManager.loginUser('+1234567890', 'phone', 'password123');
265
+ * } catch (error) {
266
+ * console.error('Login failed:', error);
267
+ * }
268
+ * ```
269
+ */
270
+ loginUser(id: string = '', idType: string = '', password: string = ''): Promise<string> {
271
+ return new Promise(async (done, reject) => {
272
+ // Get user model
273
+ const userModel = DataProvider.getCollection('cms', 'auth');
274
+
275
+ if (!userModel) {
276
+ return reject(new Error('User model not found'));
277
+ }
278
+
279
+ /**
280
+ * Setup query to find by phone or email
281
+ */
282
+ const query: Record<string, any> = {
283
+ password: Buffer.from(password).toString('base64'),
284
+ type: 'user',
285
+ };
286
+
287
+ if (idType === 'phone') query['phone'] = id;
288
+ else if (idType === 'email') query['email'] = id;
289
+
290
+ // Get from database
291
+ let gottenFromDB;
292
+ try {
293
+ gottenFromDB = await userModel.findOne(query).exec();
294
+ } catch (error) {
295
+ return reject(error);
296
+ }
297
+
298
+ if (!gottenFromDB) {
299
+ return reject(new Error('User not found'));
300
+ }
301
+
302
+ try {
303
+ // Load user
304
+ const user = await User.loadFromModel(gottenFromDB);
305
+
306
+ // Get token payload
307
+ // This is some information about the user.
308
+ const payload = user.getBrief();
309
+
310
+ // Generate json web token
311
+ const token = await JWT.main.sign(payload);
312
+ done(token);
313
+ } catch (error) {
314
+ reject(error);
315
+ }
316
+ });
317
+ }
318
+
319
+ /**
320
+ * Issues a JWT token for a user by email
321
+ * @param {string} email - The email of the user
322
+ * @returns {Promise<string>} Promise resolving to the JWT token
323
+ * @throws {Error} If user is not found
324
+ * @example
325
+ * ```typescript
326
+ * import { userManager } from '@modular-rest/server';
327
+ *
328
+ * try {
329
+ * const token = await userManager.issueTokenForUser('user@example.com');
330
+ * console.log('Issued token:', token);
331
+ * } catch (error) {
332
+ * console.error('Failed to issue token:', error);
333
+ * }
334
+ * ```
335
+ */
336
+ issueTokenForUser(email: string): Promise<string> {
337
+ return new Promise(async (done, reject) => {
338
+ const userModel = DataProvider.getCollection('cms', 'auth');
339
+
340
+ if (!userModel) {
341
+ return reject(new Error('User model not found'));
342
+ }
343
+
344
+ const query = { email: email };
345
+
346
+ // Get from database
347
+ let gottenFromDB;
348
+ try {
349
+ gottenFromDB = await userModel.findOne(query).exec();
350
+ } catch (error) {
351
+ return reject(error);
352
+ }
353
+
354
+ if (!gottenFromDB) {
355
+ return reject(new Error('User not found'));
356
+ }
357
+
358
+ try {
359
+ // Load user
360
+ const user = await User.loadFromModel(gottenFromDB);
361
+
362
+ // Get token payload
363
+ const payload = user.getBrief();
364
+
365
+ // Generate json web token
366
+ const token = await JWT.main.sign(payload);
367
+ done(token);
368
+ } catch (error) {
369
+ reject(error);
370
+ }
371
+ });
372
+ }
373
+
374
+ /**
375
+ * Logs in an anonymous user and returns their JWT token
376
+ * @returns {Promise<string>} Promise resolving to the JWT token
377
+ * @example
378
+ * ```typescript
379
+ * import { userManager } from '@modular-rest/server';
380
+ *
381
+ * const token = await userManager.loginAnonymous();
382
+ * console.log('Anonymous token:', token);
383
+ * ```
384
+ */
385
+ loginAnonymous(): Promise<string> {
386
+ return new Promise(async (done, reject) => {
387
+ const userModel = DataProvider.getCollection('cms', 'auth');
388
+
389
+ if (!userModel) {
390
+ return reject(new Error('User model not found'));
391
+ }
392
+
393
+ try {
394
+ // Create anonymous user document
395
+ const anonymousUserDoc = await userModel.create({
396
+ type: 'anonymous',
397
+ permissionGroup: getDefaultAnonymousPermissionGroup().title,
398
+ phone: '',
399
+ email: '',
400
+ password: '',
401
+ });
402
+
403
+ // Load user from document
404
+ const user = await User.loadFromModel(anonymousUserDoc);
405
+
406
+ // Get token payload
407
+ const payload = user.getBrief();
408
+
409
+ // Generate json web token
410
+ const token = await JWT.main.sign(payload);
411
+ done(token);
412
+ } catch (error) {
413
+ reject(error);
414
+ }
415
+ });
416
+ }
417
+
418
+ /**
419
+ * Registers a temporary ID for verification or password reset
420
+ * @param {string} id - The ID to register
421
+ * @param {string} type - The type of temporary ID
422
+ * @param {string} code - The verification code
423
+ * @returns {string} The registered ID
424
+ * @example
425
+ * ```typescript
426
+ * import { userManager } from '@modular-rest/server';
427
+ *
428
+ * const tempId = userManager.registerTemporaryID('user@example.com', 'password_reset', '123456');
429
+ * ```
430
+ */
431
+ registerTemporaryID(id: string, type: string, code: string): string {
432
+ this.tempIds[id] = {
433
+ type,
434
+ code,
435
+ timestamp: Date.now(),
436
+ };
437
+ return id;
438
+ }
439
+
440
+ /**
441
+ * Submits a password for a temporary ID
442
+ * @param {string} id - The temporary ID
443
+ * @param {string} password - The new password
444
+ * @param {string} code - The verification code
445
+ * @returns {Promise<string>} Promise resolving to the JWT token
446
+ * @throws {Error} If verification code is invalid or user is not found
447
+ * @example
448
+ * ```typescript
449
+ * import { userManager } from '@modular-rest/server';
450
+ *
451
+ * try {
452
+ * const token = await userManager.submitPasswordForTemporaryID(
453
+ * 'user@example.com',
454
+ * 'newpassword123',
455
+ * '123456'
456
+ * );
457
+ * console.log('Password set successfully');
458
+ * } catch (error) {
459
+ * console.error('Failed to set password:', error);
460
+ * }
461
+ * ```
462
+ */
463
+ async submitPasswordForTemporaryID(id: string, password: string, code: string): Promise<string> {
464
+ if (!this.isCodeValid(id, code)) {
465
+ throw new Error('Invalid verification code');
466
+ }
467
+
468
+ const userModel = DataProvider.getCollection('cms', 'auth');
469
+
470
+ if (!userModel) {
471
+ throw new Error('User model not found');
472
+ }
473
+
474
+ const query = { email: id };
475
+
476
+ // Get from database
477
+ let gottenFromDB;
478
+ try {
479
+ gottenFromDB = await userModel.findOne(query).exec();
480
+ } catch (error) {
481
+ throw error;
482
+ }
483
+
484
+ if (!gottenFromDB) {
485
+ throw new Error('User not found');
486
+ }
487
+
488
+ try {
489
+ // Load user
490
+ const user = await User.loadFromModel(gottenFromDB);
491
+
492
+ // Update password
493
+ user.password = Buffer.from(password).toString('base64');
494
+
495
+ // Save to database
496
+ await user.save();
497
+
498
+ // Get token payload
499
+ const payload = user.getBrief();
500
+
501
+ // Generate json web token
502
+ const token = await JWT.main.sign(payload);
503
+
504
+ // Remove temporary ID
505
+ delete this.tempIds[id];
506
+
507
+ return token;
508
+ } catch (error) {
509
+ throw error;
510
+ }
511
+ }
512
+
513
+ /**
514
+ * Changes password for a temporary ID
515
+ * @param {string} id - The temporary ID
516
+ * @param {string} password - The new password
517
+ * @param {string} code - The verification code
518
+ * @returns {Promise<string>} Promise resolving to the JWT token
519
+ * @throws {Error} If verification code is invalid or user is not found
520
+ * @example
521
+ * ```typescript
522
+ * import { userManager } from '@modular-rest/server';
523
+ *
524
+ * try {
525
+ * const token = await userManager.changePasswordForTemporaryID(
526
+ * 'user@example.com',
527
+ * 'newpassword123',
528
+ * '123456'
529
+ * );
530
+ * console.log('Password changed successfully');
531
+ * } catch (error) {
532
+ * console.error('Failed to change password:', error);
533
+ * }
534
+ * ```
535
+ */
536
+ async changePasswordForTemporaryID(id: string, password: string, code: string): Promise<string> {
537
+ if (!this.isCodeValid(id, code)) {
538
+ throw new Error('Invalid verification code');
539
+ }
540
+
541
+ const userModel = DataProvider.getCollection('cms', 'auth');
542
+
543
+ if (!userModel) {
544
+ throw new Error('User model not found');
545
+ }
546
+
547
+ const query = { email: id };
548
+
549
+ // Get from database
550
+ let gottenFromDB;
551
+ try {
552
+ gottenFromDB = await userModel.findOne(query).exec();
553
+ } catch (error) {
554
+ throw error;
555
+ }
556
+
557
+ if (!gottenFromDB) {
558
+ throw new Error('User not found');
559
+ }
560
+
561
+ try {
562
+ // Load user
563
+ const user = await User.loadFromModel(gottenFromDB);
564
+
565
+ // Update password
566
+ user.password = Buffer.from(password).toString('base64');
567
+
568
+ // Save to database
569
+ await user.save();
570
+
571
+ // Get token payload
572
+ const payload = user.getBrief();
573
+
574
+ // Generate json web token
575
+ const token = await JWT.main.sign(payload);
576
+
577
+ // Remove temporary ID
578
+ delete this.tempIds[id];
579
+
580
+ return token;
581
+ } catch (error) {
582
+ throw error;
583
+ }
584
+ }
585
+
586
+ /**
587
+ * Registers a new user
588
+ * @param {UserRegistrationDetail} detail - User registration details
589
+ * @returns {Promise<string>} Promise resolving to the JWT token
590
+ * @throws {Error} If user model is not found or registration fails
591
+ * @example
592
+ * ```typescript
593
+ * import { userManager } from '@modular-rest/server';
594
+ *
595
+ * try {
596
+ * const token = await userManager.registerUser({
597
+ * email: 'user@example.com',
598
+ * password: 'secure123',
599
+ * permissionGroup: 'user',
600
+ * phone: '+1234567890'
601
+ * });
602
+ * console.log('User registered successfully');
603
+ * } catch (error) {
604
+ * console.error('Registration failed:', error);
605
+ * }
606
+ * ```
607
+ */
608
+ registerUser(detail: UserRegistrationDetail): Promise<string> {
609
+ return new Promise(async (done, reject) => {
610
+ const userModel = DataProvider.getCollection('cms', 'auth');
611
+
612
+ if (!userModel) {
613
+ return reject(new Error('User model not found'));
614
+ }
615
+
616
+ try {
617
+ // Create user document
618
+ const userDoc = await userModel.create({
619
+ ...detail,
620
+ type: detail.type || 'user',
621
+ permissionGroup: detail.permissionGroup || getDefaultPermissionGroups().title,
622
+ phone: detail.phone || '',
623
+ email: detail.email || '',
624
+ password: detail.password ? Buffer.from(detail.password).toString('base64') : '',
625
+ });
626
+
627
+ // Load user from document
628
+ const user = await User.loadFromModel(userDoc);
629
+
630
+ // Get token payload
631
+ const payload = user.getBrief();
632
+
633
+ // Generate json web token
634
+ const token = await JWT.main.sign(payload);
635
+ done(token);
636
+ } catch (error) {
637
+ reject(error);
638
+ }
639
+ });
640
+ }
641
+
642
+ /**
643
+ * Changes a user's password
644
+ * @param {Record<string, any>} query - Query to find the user
645
+ * @param {string} newPass - The new password
646
+ * @returns {Promise<void>} Promise resolving when password is changed
647
+ * @throws {Error} If user is not found or password change fails
648
+ * @example
649
+ * ```typescript
650
+ * import { userManager } from '@modular-rest/server';
651
+ *
652
+ * try {
653
+ * await userManager.changePassword(
654
+ * { email: 'user@example.com' },
655
+ * 'newpassword123'
656
+ * );
657
+ * console.log('Password changed successfully');
658
+ * } catch (error) {
659
+ * console.error('Failed to change password:', error);
660
+ * }
661
+ * ```
662
+ */
663
+ async changePassword(query: Record<string, any>, newPass: string): Promise<void> {
664
+ const userModel = DataProvider.getCollection('cms', 'auth');
665
+
666
+ if (!userModel) {
667
+ throw new Error('User model not found');
668
+ }
669
+
670
+ const userDoc = await userModel.findOne(query).exec();
671
+
672
+ if (!userDoc) {
673
+ throw new Error('User not found');
674
+ }
675
+
676
+ const user = await User.loadFromModel(userDoc);
677
+ user.password = Buffer.from(newPass).toString('base64');
678
+ await user.save();
679
+ }
680
+
681
+ /**
682
+ * Gets the singleton instance of UserManager
683
+ * @returns {UserManager} The UserManager instance
684
+ * @hidden
685
+ */
686
+ static get instance(): UserManager {
687
+ if (!(this as any)._instance) {
688
+ (this as any)._instance = new UserManager();
689
+ }
690
+ return (this as any)._instance;
691
+ }
692
+ }
693
+
694
+ /**
695
+ * Main user manager instance
696
+ * @constant {UserManager}
697
+ */
698
+ export const main = UserManager.instance;