@flusys/nestjs-auth 1.0.0-beta → 1.0.0

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 (106) hide show
  1. package/README.md +590 -473
  2. package/cjs/config/auth.constants.js +12 -0
  3. package/cjs/controllers/authentication.controller.js +365 -24
  4. package/cjs/controllers/branch.controller.js +44 -1
  5. package/cjs/controllers/company-selection.controller.js +27 -20
  6. package/cjs/controllers/company.controller.js +44 -1
  7. package/cjs/controllers/user-permission.controller.js +82 -154
  8. package/cjs/controllers/user.controller.js +211 -7
  9. package/cjs/docs/auth-swagger.config.js +14 -7
  10. package/cjs/dtos/authentication.dto.js +114 -38
  11. package/cjs/dtos/index.js +0 -1
  12. package/cjs/dtos/user-permission.dto.js +28 -0
  13. package/cjs/dtos/user.dto.js +19 -4
  14. package/cjs/entities/index.js +5 -1
  15. package/cjs/entities/pending-registration.entity.js +125 -0
  16. package/cjs/entities/user-company-permission.entity.js +3 -9
  17. package/cjs/entities/user-token.entity.js +129 -0
  18. package/cjs/interceptors/clear-token.interceptor.js +10 -23
  19. package/cjs/interceptors/set-token.interceptor.js +15 -55
  20. package/cjs/interfaces/auth-email-provider.interface.js +45 -0
  21. package/cjs/interfaces/index.js +2 -0
  22. package/cjs/interfaces/user-enricher.interface.js +4 -0
  23. package/cjs/modules/auth.module.js +65 -143
  24. package/cjs/services/auth-config.service.js +25 -27
  25. package/cjs/services/auth-datasource.provider.js +13 -23
  26. package/cjs/services/auth-email.service.js +456 -0
  27. package/cjs/services/authentication.service.js +297 -349
  28. package/cjs/services/branch.service.js +9 -4
  29. package/cjs/services/company-selection-session.service.js +1 -1
  30. package/cjs/services/index.js +1 -0
  31. package/cjs/services/user-permission.service.js +43 -73
  32. package/cjs/services/user.service.js +184 -34
  33. package/cjs/strategies/jwt.strategy.js +44 -44
  34. package/cjs/validators/password.validator.js +45 -0
  35. package/config/auth.constants.d.ts +3 -0
  36. package/controllers/authentication.controller.d.ts +16 -10
  37. package/controllers/company-selection.controller.d.ts +6 -2
  38. package/controllers/user-permission.controller.d.ts +5 -4
  39. package/controllers/user.controller.d.ts +33 -0
  40. package/dtos/authentication.dto.d.ts +20 -6
  41. package/dtos/company-branch.dto.d.ts +0 -1
  42. package/dtos/company.dto.d.ts +0 -1
  43. package/dtos/index.d.ts +0 -1
  44. package/dtos/user-permission.dto.d.ts +4 -0
  45. package/dtos/user.dto.d.ts +1 -2
  46. package/entities/index.d.ts +7 -3
  47. package/entities/pending-registration.entity.d.ts +17 -0
  48. package/entities/user-company-permission.entity.d.ts +1 -3
  49. package/entities/user-token.entity.d.ts +15 -0
  50. package/fesm/config/auth.constants.js +6 -2
  51. package/fesm/controllers/authentication.controller.js +368 -27
  52. package/fesm/controllers/branch.controller.js +45 -2
  53. package/fesm/controllers/company-selection.controller.js +28 -21
  54. package/fesm/controllers/company.controller.js +45 -2
  55. package/fesm/controllers/user-permission.controller.js +84 -156
  56. package/fesm/controllers/user.controller.js +214 -10
  57. package/fesm/docs/auth-swagger.config.js +14 -7
  58. package/fesm/dtos/authentication.dto.js +101 -39
  59. package/fesm/dtos/index.js +0 -1
  60. package/fesm/dtos/user-permission.dto.js +28 -0
  61. package/fesm/dtos/user.dto.js +20 -5
  62. package/fesm/entities/index.js +7 -1
  63. package/fesm/entities/pending-registration.entity.js +115 -0
  64. package/fesm/entities/user-company-permission.entity.js +3 -9
  65. package/fesm/entities/user-token.entity.js +113 -0
  66. package/fesm/interceptors/clear-token.interceptor.js +9 -22
  67. package/fesm/interceptors/set-token.interceptor.js +14 -54
  68. package/fesm/interfaces/auth-email-provider.interface.js +38 -0
  69. package/fesm/interfaces/authentication.interface.js +1 -1
  70. package/fesm/interfaces/index.js +2 -0
  71. package/fesm/interfaces/user-enricher.interface.js +26 -0
  72. package/fesm/modules/auth.module.js +67 -145
  73. package/fesm/services/auth-config.service.js +26 -28
  74. package/fesm/services/auth-datasource.provider.js +14 -24
  75. package/fesm/services/auth-email.service.js +405 -0
  76. package/fesm/services/authentication.service.js +297 -349
  77. package/fesm/services/branch.service.js +9 -4
  78. package/fesm/services/company-selection-session.service.js +1 -1
  79. package/fesm/services/index.js +1 -0
  80. package/fesm/services/user-permission.service.js +44 -74
  81. package/fesm/services/user.service.js +186 -36
  82. package/fesm/strategies/jwt.strategy.js +44 -44
  83. package/fesm/validators/password.validator.js +48 -0
  84. package/interceptors/clear-token.interceptor.d.ts +0 -1
  85. package/interceptors/set-token.interceptor.d.ts +1 -1
  86. package/interfaces/auth-email-provider.interface.d.ts +6 -0
  87. package/interfaces/auth-module-options.interface.d.ts +4 -1
  88. package/interfaces/authentication.interface.d.ts +13 -3
  89. package/interfaces/index.d.ts +2 -0
  90. package/interfaces/user-enricher.interface.d.ts +64 -0
  91. package/interfaces/user.interface.d.ts +1 -0
  92. package/modules/auth.module.d.ts +6 -3
  93. package/package.json +4 -4
  94. package/services/auth-config.service.d.ts +7 -8
  95. package/services/auth-datasource.provider.d.ts +2 -4
  96. package/services/auth-email.service.d.ts +54 -0
  97. package/services/authentication.service.d.ts +27 -16
  98. package/services/company-selection-session.service.d.ts +1 -1
  99. package/services/index.d.ts +1 -0
  100. package/services/user-permission.service.d.ts +6 -9
  101. package/services/user.service.d.ts +19 -3
  102. package/strategies/jwt.strategy.d.ts +4 -1
  103. package/validators/password.validator.d.ts +10 -0
  104. package/cjs/dtos/user-company-permission.dto.js +0 -110
  105. package/dtos/user-company-permission.dto.d.ts +0 -10
  106. package/fesm/dtos/user-company-permission.dto.js +0 -98
@@ -26,35 +26,56 @@ function _ts_param(paramIndex, decorator) {
26
26
  };
27
27
  }
28
28
  import { CurrentUser } from '@flusys/nestjs-shared/decorators';
29
+ import { MessageResponseDto } from '@flusys/nestjs-shared/dtos';
29
30
  import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
30
31
  import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
31
- import { Body, Controller, Get, HttpCode, HttpStatus, Inject, Optional, Post, Req, UnauthorizedException, UseGuards, UseInterceptors } from '@nestjs/common';
32
- import { ApiBearerAuth, ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
32
+ import { Body, Controller, HttpCode, HttpStatus, Inject, Optional, Post, Req, UnauthorizedException, UseGuards, UseInterceptors } from '@nestjs/common';
33
+ import { ApiBearerAuth, ApiBody, ApiExtraModels, ApiHeader, ApiOperation, ApiResponse, ApiTags, getSchemaPath } from '@nestjs/swagger';
34
+ import { SkipThrottle, Throttle } from '@nestjs/throttler';
33
35
  import { Request } from 'express';
34
- import { ChangePasswordDto, CompanySelectionResponseDto, LoginDto, LoginResponseDto, MeResponseDto, RefreshTokenDto, RegistrationDto, RegistrationResponseDto } from '../dtos/authentication.dto';
36
+ import { DEFAULT_COOKIE_NAME } from '../config';
37
+ import { ChangePasswordDto, CompanySelectionResponseDto, ForgotPasswordDto, LoginDto, LoginResponseDto, LoginResponseRequiresEmailVerificationDto, MeResponseDto, RefreshTokenDto, RegistrationDto, RegistrationResponseDto, ResendVerificationDto, ResetPasswordDto, VerifyEmailDto } from '../dtos/authentication.dto';
35
38
  import { ClearToken } from '../interceptors/clear-token.interceptor';
36
39
  import { SetToken } from '../interceptors/set-token.interceptor';
37
40
  import { AuthConfigService } from '../services/auth-config.service';
41
+ import { AuthEmailService } from '../services/auth-email.service';
38
42
  import { AuthenticationService } from '../services/authentication.service';
39
43
  export class AuthenticationController {
40
- // Core Endpoints
41
- /** Login - Returns tokens or company/branch selection requirement */ async login(loginDto) {
44
+ buildAuthUrl(req, path) {
45
+ const frontendUrl = this.authConfig?.getFrontendUrl();
46
+ return frontendUrl ? `${frontendUrl}${path}` : `${req.protocol}://${req.get('host')}${path}`;
47
+ }
48
+ get emailService() {
49
+ if (!this.authEmailService) {
50
+ throw new Error('Email service is not configured');
51
+ }
52
+ return this.authEmailService;
53
+ }
54
+ get isEmailEnabled() {
55
+ return !!this.authEmailService;
56
+ }
57
+ async login(loginDto) {
42
58
  const result = await this.authService.login(loginDto.email, loginDto.password);
59
+ if ('requiresEmailVerification' in result) {
60
+ return result;
61
+ }
43
62
  if ('requiresSelection' in result) {
44
63
  return result;
45
64
  }
46
65
  return result;
47
66
  }
48
- /** Register new user. If company feature enabled, can join/create company. */ async register(registrationDto) {
49
- const result = await this.authService.register(registrationDto);
50
- return {
51
- ...result,
52
- companyFeatureEnabled: this.authConfig?.isCompanyFeatureEnabled() ?? false
53
- };
67
+ /** Register new user. If company feature enabled, can join/create company. */ async register(req, registrationDto) {
68
+ const verifyUrlBase = this.buildAuthUrl(req, '/auth/verify-email');
69
+ const result = await this.authService.register(registrationDto, verifyUrlBase);
70
+ return result;
54
71
  }
55
- /** Refresh access token. Browser uses HTTP-only cookie, API/Mobile uses request body. */ async refreshToken(req, refreshTokenDto) {
56
- // Get refresh token from cookie (browser) or body (API/mobile)
57
- const cookieName = this.authConfig?.getRefreshTokenCookieName() ?? 'fsn_refresh_token';
72
+ /**
73
+ * Refresh access token.
74
+ * - **Browser**: Prefers HTTP-only cookie, falls back to body if cookie missing.
75
+ * - **API/Mobile**: Must send `refreshToken` in request body. Set header `x-client-type: mobile` or `x-client-type: api`.
76
+ */ async refreshToken(req, refreshTokenDto) {
77
+ const cookieName = this.authConfig?.getRefreshTokenCookieName() ?? DEFAULT_COOKIE_NAME;
78
+ // Always check cookie first, then body - handles Browser, SSR, and Mobile/API
58
79
  const refreshToken = req.cookies?.[cookieName] || refreshTokenDto.refreshToken;
59
80
  if (!refreshToken) {
60
81
  throw new UnauthorizedException('Refresh token is required');
@@ -67,26 +88,78 @@ export class AuthenticationController {
67
88
  /** Logout - Clears refresh token cookie for browsers */ async logout(userId) {
68
89
  await this.authService.logout(userId);
69
90
  return {
91
+ success: true,
70
92
  message: 'Logged out successfully'
71
93
  };
72
94
  }
73
- /** Change password */ async changePassword(userId, dto) {
95
+ async changePassword(userId, dto) {
74
96
  await this.authService.changePassword(userId, dto.currentPassword, dto.newPassword);
75
97
  return {
98
+ success: true,
76
99
  message: 'Password changed successfully'
77
100
  };
78
101
  }
79
- // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
80
- constructor(authService, authConfig){
102
+ async forgotPassword(req, dto) {
103
+ if (!this.isEmailEnabled) {
104
+ return {
105
+ success: false,
106
+ message: 'Email service is not configured'
107
+ };
108
+ }
109
+ return this.emailService.forgotPassword(dto.email, this.buildAuthUrl(req, '/auth/reset-password'));
110
+ }
111
+ async resetPassword(dto) {
112
+ if (!this.isEmailEnabled) {
113
+ return {
114
+ success: false,
115
+ message: 'Email service is not configured'
116
+ };
117
+ }
118
+ return this.emailService.resetPassword(dto.token, dto.newPassword);
119
+ }
120
+ async resendVerification(req, dto) {
121
+ if (!this.isEmailEnabled) {
122
+ return {
123
+ success: false,
124
+ message: 'Email service is not configured'
125
+ };
126
+ }
127
+ return this.emailService.resendVerificationEmail(dto.email, this.buildAuthUrl(req, '/auth/verify-email'));
128
+ }
129
+ /** Verify email - Verify email using token. Handles both pending registration and existing user verification. */ async verifyEmail(dto) {
130
+ if (!this.isEmailEnabled) {
131
+ return {
132
+ success: false,
133
+ message: 'Email service is not configured'
134
+ };
135
+ }
136
+ const result = await this.emailService.verifyEmail(dto.token);
137
+ if (result.type === 'pending') {
138
+ return this.authService.createUserFromPendingRegistration(result.pending);
139
+ }
140
+ return {
141
+ success: true,
142
+ message: result.message
143
+ };
144
+ }
145
+ constructor(authService, authConfig, authEmailService){
81
146
  _define_property(this, "authService", void 0);
82
147
  _define_property(this, "authConfig", void 0);
148
+ _define_property(this, "authEmailService", void 0);
83
149
  this.authService = authService;
84
150
  this.authConfig = authConfig;
151
+ this.authEmailService = authEmailService;
85
152
  }
86
153
  }
87
154
  _ts_decorate([
88
155
  Post('login'),
89
156
  HttpCode(HttpStatus.OK),
157
+ Throttle({
158
+ default: {
159
+ limit: 5,
160
+ ttl: 60000
161
+ }
162
+ }),
90
163
  UseInterceptors(SetToken),
91
164
  ApiOperation({
92
165
  summary: 'User login'
@@ -94,13 +167,102 @@ _ts_decorate([
94
167
  ApiBody({
95
168
  type: LoginDto
96
169
  }),
170
+ ApiExtraModels(LoginResponseDto, CompanySelectionResponseDto, LoginResponseRequiresEmailVerificationDto),
97
171
  ApiResponse({
98
172
  status: 200,
99
- type: LoginResponseDto
100
- }),
101
- ApiResponse({
102
- status: 200,
103
- type: CompanySelectionResponseDto
173
+ description: 'Login successful, requires company selection, or requires email verification',
174
+ content: {
175
+ 'application/json': {
176
+ schema: {
177
+ oneOf: [
178
+ {
179
+ $ref: getSchemaPath(LoginResponseDto)
180
+ },
181
+ {
182
+ $ref: getSchemaPath(CompanySelectionResponseDto)
183
+ },
184
+ {
185
+ $ref: getSchemaPath(LoginResponseRequiresEmailVerificationDto)
186
+ }
187
+ ]
188
+ },
189
+ examples: {
190
+ loginSuccess: {
191
+ summary: 'Login Success',
192
+ description: 'Successful login with tokens (company/branch included when company feature enabled)',
193
+ value: {
194
+ accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
195
+ refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
196
+ user: {
197
+ id: '550e8400-e29b-41d4-a716-446655440000',
198
+ email: 'john@example.com',
199
+ name: 'John Doe'
200
+ }
201
+ }
202
+ },
203
+ loginSuccessWithCompany: {
204
+ summary: 'Login Success (Company Feature)',
205
+ description: 'Login with company/branch - only when company feature is enabled',
206
+ value: {
207
+ accessToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
208
+ refreshToken: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
209
+ user: {
210
+ id: '550e8400-e29b-41d4-a716-446655440000',
211
+ email: 'john@example.com',
212
+ name: 'John Doe'
213
+ },
214
+ company: {
215
+ id: '550e8400-e29b-41d4-a716-446655440001',
216
+ name: 'Acme Corporation',
217
+ slug: 'acme-corp'
218
+ },
219
+ branch: {
220
+ id: '550e8400-e29b-41d4-a716-446655440002',
221
+ name: 'Main Branch',
222
+ slug: 'main-branch'
223
+ }
224
+ }
225
+ },
226
+ companySelection: {
227
+ summary: 'Company Selection Required (Company Feature)',
228
+ description: 'User has multiple companies/branches - only when company feature is enabled',
229
+ value: {
230
+ requiresSelection: true,
231
+ sessionId: 'sess_abc123def456',
232
+ expiresAt: '2024-12-18T15:30:00.000Z',
233
+ user: {
234
+ id: '550e8400-e29b-41d4-a716-446655440000',
235
+ email: 'john@example.com',
236
+ name: 'John Doe'
237
+ },
238
+ companies: [
239
+ {
240
+ id: '550e8400-e29b-41d4-a716-446655440001',
241
+ name: 'Company A',
242
+ slug: 'company-a',
243
+ branches: [
244
+ {
245
+ id: '550e8400-e29b-41d4-a716-446655440010',
246
+ name: 'Branch 1',
247
+ slug: 'branch-1'
248
+ }
249
+ ]
250
+ }
251
+ ]
252
+ }
253
+ },
254
+ emailVerification: {
255
+ summary: 'Email Verification Required',
256
+ description: 'User must verify email before logging in',
257
+ value: {
258
+ requiresEmailVerification: true,
259
+ email: 'john@example.com',
260
+ message: 'Please verify your email address before logging in.'
261
+ }
262
+ }
263
+ }
264
+ }
265
+ }
104
266
  }),
105
267
  _ts_param(0, Body()),
106
268
  _ts_metadata("design:type", Function),
@@ -112,6 +274,12 @@ _ts_decorate([
112
274
  _ts_decorate([
113
275
  Post('register'),
114
276
  HttpCode(HttpStatus.CREATED),
277
+ Throttle({
278
+ default: {
279
+ limit: 3,
280
+ ttl: 60000
281
+ }
282
+ }),
115
283
  UseInterceptors(SetToken),
116
284
  ApiOperation({
117
285
  summary: 'Register new user'
@@ -123,9 +291,11 @@ _ts_decorate([
123
291
  status: 201,
124
292
  type: RegistrationResponseDto
125
293
  }),
126
- _ts_param(0, Body()),
294
+ _ts_param(0, Req()),
295
+ _ts_param(1, Body()),
127
296
  _ts_metadata("design:type", Function),
128
297
  _ts_metadata("design:paramtypes", [
298
+ typeof Request === "undefined" ? Object : Request,
129
299
  typeof RegistrationDto === "undefined" ? Object : RegistrationDto
130
300
  ]),
131
301
  _ts_metadata("design:returntype", Promise)
@@ -133,11 +303,50 @@ _ts_decorate([
133
303
  _ts_decorate([
134
304
  Post('refresh'),
135
305
  HttpCode(HttpStatus.OK),
306
+ Throttle({
307
+ default: {
308
+ limit: 30,
309
+ ttl: 60000
310
+ }
311
+ }),
136
312
  ApiOperation({
137
- summary: 'Refresh access token'
313
+ summary: 'Refresh access token',
314
+ description: `
315
+ **Browser clients**: Refresh token is read from HTTP-only cookie (automatically set on login/register).
316
+ Falls back to request body if cookie is not available.
317
+
318
+ **API/Mobile clients**: Must send \`refreshToken\` in request body.
319
+ Set header \`x-client-type: mobile\` or \`x-client-type: api\` to identify as non-browser client.
320
+
321
+ **Client detection**: Uses \`x-client-type\` header first, then falls back to User-Agent detection.
322
+ `
323
+ }),
324
+ ApiHeader({
325
+ name: 'x-client-type',
326
+ required: false,
327
+ description: 'Client type: "browser"/"web" for cookie-based auth, "mobile"/"api" for body-based auth',
328
+ example: 'mobile'
138
329
  }),
139
330
  ApiBody({
140
- type: RefreshTokenDto
331
+ type: RefreshTokenDto,
332
+ description: 'Required for API/Mobile clients. Browser clients prefer HTTP-only cookie with body fallback.'
333
+ }),
334
+ ApiResponse({
335
+ status: 200,
336
+ description: 'New access token',
337
+ schema: {
338
+ type: 'object',
339
+ properties: {
340
+ accessToken: {
341
+ type: 'string',
342
+ example: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...'
343
+ }
344
+ }
345
+ }
346
+ }),
347
+ ApiResponse({
348
+ status: 401,
349
+ description: 'Invalid or missing refresh token'
141
350
  }),
142
351
  _ts_param(0, Req()),
143
352
  _ts_param(1, Body()),
@@ -150,7 +359,8 @@ _ts_decorate([
150
359
  ], AuthenticationController.prototype, "refreshToken", null);
151
360
  _ts_decorate([
152
361
  UseGuards(JwtAuthGuard),
153
- Get('me'),
362
+ Post('me'),
363
+ SkipThrottle(),
154
364
  ApiBearerAuth(),
155
365
  ApiOperation({
156
366
  summary: 'Get current user info'
@@ -170,11 +380,16 @@ _ts_decorate([
170
380
  UseGuards(JwtAuthGuard),
171
381
  Post('logout'),
172
382
  HttpCode(HttpStatus.OK),
383
+ SkipThrottle(),
173
384
  UseInterceptors(ClearToken),
174
385
  ApiBearerAuth(),
175
386
  ApiOperation({
176
387
  summary: 'Logout user'
177
388
  }),
389
+ ApiResponse({
390
+ status: 200,
391
+ type: MessageResponseDto
392
+ }),
178
393
  _ts_param(0, CurrentUser('id')),
179
394
  _ts_metadata("design:type", Function),
180
395
  _ts_metadata("design:paramtypes", [
@@ -186,6 +401,12 @@ _ts_decorate([
186
401
  UseGuards(JwtAuthGuard),
187
402
  Post('change-password'),
188
403
  HttpCode(HttpStatus.OK),
404
+ Throttle({
405
+ default: {
406
+ limit: 5,
407
+ ttl: 60000
408
+ }
409
+ }),
189
410
  ApiBearerAuth(),
190
411
  ApiOperation({
191
412
  summary: 'Change password'
@@ -193,6 +414,10 @@ _ts_decorate([
193
414
  ApiBody({
194
415
  type: ChangePasswordDto
195
416
  }),
417
+ ApiResponse({
418
+ status: 200,
419
+ type: MessageResponseDto
420
+ }),
196
421
  _ts_param(0, CurrentUser('id')),
197
422
  _ts_param(1, Body()),
198
423
  _ts_metadata("design:type", Function),
@@ -202,15 +427,131 @@ _ts_decorate([
202
427
  ]),
203
428
  _ts_metadata("design:returntype", Promise)
204
429
  ], AuthenticationController.prototype, "changePassword", null);
430
+ _ts_decorate([
431
+ Post('forgot-password'),
432
+ HttpCode(HttpStatus.OK),
433
+ Throttle({
434
+ default: {
435
+ limit: 3,
436
+ ttl: 300000
437
+ }
438
+ }),
439
+ ApiOperation({
440
+ summary: 'Request password reset email'
441
+ }),
442
+ ApiBody({
443
+ type: ForgotPasswordDto
444
+ }),
445
+ ApiResponse({
446
+ status: 200,
447
+ type: MessageResponseDto
448
+ }),
449
+ _ts_param(0, Req()),
450
+ _ts_param(1, Body()),
451
+ _ts_metadata("design:type", Function),
452
+ _ts_metadata("design:paramtypes", [
453
+ typeof Request === "undefined" ? Object : Request,
454
+ typeof ForgotPasswordDto === "undefined" ? Object : ForgotPasswordDto
455
+ ]),
456
+ _ts_metadata("design:returntype", Promise)
457
+ ], AuthenticationController.prototype, "forgotPassword", null);
458
+ _ts_decorate([
459
+ Post('reset-password'),
460
+ HttpCode(HttpStatus.OK),
461
+ Throttle({
462
+ default: {
463
+ limit: 5,
464
+ ttl: 900000
465
+ }
466
+ }),
467
+ ApiOperation({
468
+ summary: 'Reset password using token'
469
+ }),
470
+ ApiBody({
471
+ type: ResetPasswordDto
472
+ }),
473
+ ApiResponse({
474
+ status: 200,
475
+ type: MessageResponseDto
476
+ }),
477
+ _ts_param(0, Body()),
478
+ _ts_metadata("design:type", Function),
479
+ _ts_metadata("design:paramtypes", [
480
+ typeof ResetPasswordDto === "undefined" ? Object : ResetPasswordDto
481
+ ]),
482
+ _ts_metadata("design:returntype", Promise)
483
+ ], AuthenticationController.prototype, "resetPassword", null);
484
+ _ts_decorate([
485
+ Post('resend-verification'),
486
+ HttpCode(HttpStatus.OK),
487
+ Throttle({
488
+ default: {
489
+ limit: 3,
490
+ ttl: 300000
491
+ }
492
+ }),
493
+ ApiOperation({
494
+ summary: 'Resend email verification link'
495
+ }),
496
+ ApiBody({
497
+ type: ResendVerificationDto
498
+ }),
499
+ ApiResponse({
500
+ status: 200,
501
+ type: MessageResponseDto
502
+ }),
503
+ _ts_param(0, Req()),
504
+ _ts_param(1, Body()),
505
+ _ts_metadata("design:type", Function),
506
+ _ts_metadata("design:paramtypes", [
507
+ typeof Request === "undefined" ? Object : Request,
508
+ typeof ResendVerificationDto === "undefined" ? Object : ResendVerificationDto
509
+ ]),
510
+ _ts_metadata("design:returntype", Promise)
511
+ ], AuthenticationController.prototype, "resendVerification", null);
512
+ _ts_decorate([
513
+ Post('verify-email'),
514
+ HttpCode(HttpStatus.OK),
515
+ Throttle({
516
+ default: {
517
+ limit: 10,
518
+ ttl: 60000
519
+ }
520
+ }),
521
+ UseInterceptors(SetToken),
522
+ ApiOperation({
523
+ summary: 'Verify email using token'
524
+ }),
525
+ ApiBody({
526
+ type: VerifyEmailDto
527
+ }),
528
+ ApiResponse({
529
+ status: 200,
530
+ type: MessageResponseDto
531
+ }),
532
+ ApiResponse({
533
+ status: 200,
534
+ type: RegistrationResponseDto
535
+ }),
536
+ _ts_param(0, Body()),
537
+ _ts_metadata("design:type", Function),
538
+ _ts_metadata("design:paramtypes", [
539
+ typeof VerifyEmailDto === "undefined" ? Object : VerifyEmailDto
540
+ ]),
541
+ _ts_metadata("design:returntype", Promise)
542
+ ], AuthenticationController.prototype, "verifyEmail", null);
205
543
  AuthenticationController = _ts_decorate([
206
544
  ApiTags('Authentication'),
207
545
  Controller('auth'),
208
546
  _ts_param(0, Inject(AuthenticationService)),
209
547
  _ts_param(1, Optional()),
210
548
  _ts_param(1, Inject(AuthConfigService)),
549
+ _ts_param(2, Optional()),
550
+ _ts_param(2, Inject(AuthEmailService)),
211
551
  _ts_metadata("design:type", Function),
212
552
  _ts_metadata("design:paramtypes", [
213
553
  typeof AuthenticationService === "undefined" ? Object : AuthenticationService,
214
- typeof AuthConfigService === "undefined" ? Object : AuthConfigService
554
+ typeof AuthConfigService === "undefined" ? Object : AuthConfigService,
555
+ typeof AuthEmailService === "undefined" ? Object : AuthEmailService
215
556
  ])
216
557
  ], AuthenticationController);
@@ -25,13 +25,56 @@ function _ts_param(paramIndex, decorator) {
25
25
  decorator(target, key, paramIndex);
26
26
  };
27
27
  }
28
- import { createApiController } from '@flusys/nestjs-shared/classes';
28
+ import { createApiController, BRANCH_PERMISSIONS } from '@flusys/nestjs-shared/classes';
29
29
  import { Controller, Inject } from '@nestjs/common';
30
30
  import { ApiTags } from '@nestjs/swagger';
31
31
  import { CompanyBranchResponseDto, CreateCompanyBranchDto, UpdateCompanyBranchDto } from '../dtos/company-branch.dto';
32
32
  import { BranchService } from '../services/branch.service';
33
33
  export class BranchController extends createApiController(CreateCompanyBranchDto, UpdateCompanyBranchDto, CompanyBranchResponseDto, {
34
- security: 'jwt'
34
+ security: {
35
+ insert: {
36
+ level: 'permission',
37
+ permissions: [
38
+ BRANCH_PERMISSIONS.CREATE
39
+ ]
40
+ },
41
+ insertMany: {
42
+ level: 'permission',
43
+ permissions: [
44
+ BRANCH_PERMISSIONS.CREATE
45
+ ]
46
+ },
47
+ getById: {
48
+ level: 'permission',
49
+ permissions: [
50
+ BRANCH_PERMISSIONS.READ
51
+ ]
52
+ },
53
+ getAll: {
54
+ level: 'permission',
55
+ permissions: [
56
+ BRANCH_PERMISSIONS.READ
57
+ ]
58
+ },
59
+ update: {
60
+ level: 'permission',
61
+ permissions: [
62
+ BRANCH_PERMISSIONS.UPDATE
63
+ ]
64
+ },
65
+ updateMany: {
66
+ level: 'permission',
67
+ permissions: [
68
+ BRANCH_PERMISSIONS.UPDATE
69
+ ]
70
+ },
71
+ delete: {
72
+ level: 'permission',
73
+ permissions: [
74
+ BRANCH_PERMISSIONS.DELETE
75
+ ]
76
+ }
77
+ }
35
78
  }) {
36
79
  constructor(branchService){
37
80
  super(branchService), _define_property(this, "branchService", void 0), this.branchService = branchService;
@@ -26,42 +26,35 @@ function _ts_param(paramIndex, decorator) {
26
26
  };
27
27
  }
28
28
  import { CurrentUser } from '@flusys/nestjs-shared/decorators';
29
- import { Body, Controller, Get, HttpCode, HttpStatus, Inject, Param, Post, UseGuards, UseInterceptors } from '@nestjs/common';
29
+ import { Body, Controller, HttpCode, HttpStatus, Inject, Post, UseGuards, UseInterceptors } from '@nestjs/common';
30
30
  import { ApiBearerAuth, ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
31
31
  import { LoginResponseDto, SelectDto, SwitchCompanyDto } from '../dtos/authentication.dto';
32
32
  import { JwtAuthGuard } from '@flusys/nestjs-shared/guards';
33
33
  import { SetToken } from '../interceptors/set-token.interceptor';
34
34
  import { AuthenticationService } from '../services/authentication.service';
35
35
  export class CompanySelectionController {
36
- /**
37
- * Select Company & Branch
38
- * Used after login when user needs to select company and branch
39
- */ async select(dto) {
36
+ async select(dto) {
40
37
  return this.authService.select(dto.sessionId, dto.companyId, dto.branchId);
41
38
  }
42
- /**
43
- * Switch Company/Branch
44
- * Used to switch company/branch after already logged in
45
- */ async switchCompany(userId, dto) {
39
+ async switchCompany(userId, dto) {
46
40
  return this.authService.switchCompany(userId, dto.companyId, dto.branchId);
47
41
  }
48
- /**
49
- * Get user's companies
50
- */ async getUserCompanies(userId) {
42
+ async getUserCompanies(userId) {
51
43
  const companies = await this.authService.getUserCompanies(userId);
52
44
  return {
53
- companies
45
+ success: true,
46
+ message: 'User companies retrieved successfully',
47
+ data: companies
54
48
  };
55
49
  }
56
- /**
57
- * Get branches for a specific company
58
- */ async getUserBranches(userId, companyId) {
50
+ async getUserBranches(userId, companyId) {
59
51
  const branches = await this.authService.getUserBranches(userId, companyId);
60
52
  return {
61
- branches
53
+ success: true,
54
+ message: 'Company branches retrieved successfully',
55
+ data: branches
62
56
  };
63
57
  }
64
- // NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
65
58
  constructor(authService){
66
59
  _define_property(this, "authService", void 0);
67
60
  this.authService = authService;
@@ -117,7 +110,8 @@ _ts_decorate([
117
110
  ], CompanySelectionController.prototype, "switchCompany", null);
118
111
  _ts_decorate([
119
112
  UseGuards(JwtAuthGuard),
120
- Get('companies'),
113
+ Post('get-companies'),
114
+ HttpCode(HttpStatus.OK),
121
115
  ApiBearerAuth(),
122
116
  ApiOperation({
123
117
  summary: "Get user's available companies",
@@ -132,14 +126,27 @@ _ts_decorate([
132
126
  ], CompanySelectionController.prototype, "getUserCompanies", null);
133
127
  _ts_decorate([
134
128
  UseGuards(JwtAuthGuard),
135
- Get('companies/:companyId/branches'),
129
+ Post('get-company-branches'),
130
+ HttpCode(HttpStatus.OK),
136
131
  ApiBearerAuth(),
137
132
  ApiOperation({
138
133
  summary: 'Get branches for a company',
139
134
  description: 'Get list of branches for a specific company that the user has access to'
140
135
  }),
136
+ ApiBody({
137
+ schema: {
138
+ properties: {
139
+ companyId: {
140
+ type: 'string'
141
+ }
142
+ },
143
+ required: [
144
+ 'companyId'
145
+ ]
146
+ }
147
+ }),
141
148
  _ts_param(0, CurrentUser('id')),
142
- _ts_param(1, Param('companyId')),
149
+ _ts_param(1, Body('companyId')),
143
150
  _ts_metadata("design:type", Function),
144
151
  _ts_metadata("design:paramtypes", [
145
152
  String,