@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.
- package/README.md +590 -473
- package/cjs/config/auth.constants.js +12 -0
- package/cjs/controllers/authentication.controller.js +365 -24
- package/cjs/controllers/branch.controller.js +44 -1
- package/cjs/controllers/company-selection.controller.js +27 -20
- package/cjs/controllers/company.controller.js +44 -1
- package/cjs/controllers/user-permission.controller.js +82 -154
- package/cjs/controllers/user.controller.js +211 -7
- package/cjs/docs/auth-swagger.config.js +14 -7
- package/cjs/dtos/authentication.dto.js +114 -38
- package/cjs/dtos/index.js +0 -1
- package/cjs/dtos/user-permission.dto.js +28 -0
- package/cjs/dtos/user.dto.js +19 -4
- package/cjs/entities/index.js +5 -1
- package/cjs/entities/pending-registration.entity.js +125 -0
- package/cjs/entities/user-company-permission.entity.js +3 -9
- package/cjs/entities/user-token.entity.js +129 -0
- package/cjs/interceptors/clear-token.interceptor.js +10 -23
- package/cjs/interceptors/set-token.interceptor.js +15 -55
- package/cjs/interfaces/auth-email-provider.interface.js +45 -0
- package/cjs/interfaces/index.js +2 -0
- package/cjs/interfaces/user-enricher.interface.js +4 -0
- package/cjs/modules/auth.module.js +65 -143
- package/cjs/services/auth-config.service.js +25 -27
- package/cjs/services/auth-datasource.provider.js +13 -23
- package/cjs/services/auth-email.service.js +456 -0
- package/cjs/services/authentication.service.js +297 -349
- package/cjs/services/branch.service.js +9 -4
- package/cjs/services/company-selection-session.service.js +1 -1
- package/cjs/services/index.js +1 -0
- package/cjs/services/user-permission.service.js +43 -73
- package/cjs/services/user.service.js +184 -34
- package/cjs/strategies/jwt.strategy.js +44 -44
- package/cjs/validators/password.validator.js +45 -0
- package/config/auth.constants.d.ts +3 -0
- package/controllers/authentication.controller.d.ts +16 -10
- package/controllers/company-selection.controller.d.ts +6 -2
- package/controllers/user-permission.controller.d.ts +5 -4
- package/controllers/user.controller.d.ts +33 -0
- package/dtos/authentication.dto.d.ts +20 -6
- package/dtos/company-branch.dto.d.ts +0 -1
- package/dtos/company.dto.d.ts +0 -1
- package/dtos/index.d.ts +0 -1
- package/dtos/user-permission.dto.d.ts +4 -0
- package/dtos/user.dto.d.ts +1 -2
- package/entities/index.d.ts +7 -3
- package/entities/pending-registration.entity.d.ts +17 -0
- package/entities/user-company-permission.entity.d.ts +1 -3
- package/entities/user-token.entity.d.ts +15 -0
- package/fesm/config/auth.constants.js +6 -2
- package/fesm/controllers/authentication.controller.js +368 -27
- package/fesm/controllers/branch.controller.js +45 -2
- package/fesm/controllers/company-selection.controller.js +28 -21
- package/fesm/controllers/company.controller.js +45 -2
- package/fesm/controllers/user-permission.controller.js +84 -156
- package/fesm/controllers/user.controller.js +214 -10
- package/fesm/docs/auth-swagger.config.js +14 -7
- package/fesm/dtos/authentication.dto.js +101 -39
- package/fesm/dtos/index.js +0 -1
- package/fesm/dtos/user-permission.dto.js +28 -0
- package/fesm/dtos/user.dto.js +20 -5
- package/fesm/entities/index.js +7 -1
- package/fesm/entities/pending-registration.entity.js +115 -0
- package/fesm/entities/user-company-permission.entity.js +3 -9
- package/fesm/entities/user-token.entity.js +113 -0
- package/fesm/interceptors/clear-token.interceptor.js +9 -22
- package/fesm/interceptors/set-token.interceptor.js +14 -54
- package/fesm/interfaces/auth-email-provider.interface.js +38 -0
- package/fesm/interfaces/authentication.interface.js +1 -1
- package/fesm/interfaces/index.js +2 -0
- package/fesm/interfaces/user-enricher.interface.js +26 -0
- package/fesm/modules/auth.module.js +67 -145
- package/fesm/services/auth-config.service.js +26 -28
- package/fesm/services/auth-datasource.provider.js +14 -24
- package/fesm/services/auth-email.service.js +405 -0
- package/fesm/services/authentication.service.js +297 -349
- package/fesm/services/branch.service.js +9 -4
- package/fesm/services/company-selection-session.service.js +1 -1
- package/fesm/services/index.js +1 -0
- package/fesm/services/user-permission.service.js +44 -74
- package/fesm/services/user.service.js +186 -36
- package/fesm/strategies/jwt.strategy.js +44 -44
- package/fesm/validators/password.validator.js +48 -0
- package/interceptors/clear-token.interceptor.d.ts +0 -1
- package/interceptors/set-token.interceptor.d.ts +1 -1
- package/interfaces/auth-email-provider.interface.d.ts +6 -0
- package/interfaces/auth-module-options.interface.d.ts +4 -1
- package/interfaces/authentication.interface.d.ts +13 -3
- package/interfaces/index.d.ts +2 -0
- package/interfaces/user-enricher.interface.d.ts +64 -0
- package/interfaces/user.interface.d.ts +1 -0
- package/modules/auth.module.d.ts +6 -3
- package/package.json +4 -4
- package/services/auth-config.service.d.ts +7 -8
- package/services/auth-datasource.provider.d.ts +2 -4
- package/services/auth-email.service.d.ts +54 -0
- package/services/authentication.service.d.ts +27 -16
- package/services/company-selection-session.service.d.ts +1 -1
- package/services/index.d.ts +1 -0
- package/services/user-permission.service.d.ts +6 -9
- package/services/user.service.d.ts +19 -3
- package/strategies/jwt.strategy.d.ts +4 -1
- package/validators/password.validator.d.ts +10 -0
- package/cjs/dtos/user-company-permission.dto.js +0 -110
- package/dtos/user-company-permission.dto.d.ts +0 -10
- 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,
|
|
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 {
|
|
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
|
-
|
|
41
|
-
|
|
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
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
/**
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
|
|
80
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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,
|
|
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
|
-
|
|
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:
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
149
|
+
_ts_param(1, Body('companyId')),
|
|
143
150
|
_ts_metadata("design:type", Function),
|
|
144
151
|
_ts_metadata("design:paramtypes", [
|
|
145
152
|
String,
|