@flusys/nestjs-auth 0.1.0-beta.3 → 1.0.0-beta
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 +1 -11
- package/cjs/config/auth.constants.js +10 -4
- package/cjs/controllers/authentication.controller.js +7 -32
- package/cjs/controllers/branch.controller.js +3 -4
- package/cjs/controllers/company.controller.js +3 -4
- package/cjs/controllers/user-permission.controller.js +6 -18
- package/cjs/controllers/user.controller.js +17 -22
- package/cjs/interfaces/authentication.interface.js +0 -3
- package/cjs/services/auth-config.service.js +18 -44
- package/cjs/services/auth-datasource.provider.js +13 -31
- package/cjs/services/authentication.service.js +80 -85
- package/cjs/services/branch.service.js +10 -44
- package/cjs/services/company.service.js +7 -36
- package/cjs/services/user-permission.service.js +9 -27
- package/cjs/services/user.service.js +33 -91
- package/cjs/strategies/jwt.strategy.js +14 -1
- package/config/auth.constants.d.ts +2 -0
- package/fesm/config/auth.constants.js +7 -8
- package/fesm/controllers/authentication.controller.js +7 -32
- package/fesm/controllers/branch.controller.js +5 -6
- package/fesm/controllers/company.controller.js +5 -6
- package/fesm/controllers/user-permission.controller.js +6 -18
- package/fesm/controllers/user.controller.js +17 -22
- package/fesm/dtos/authentication.dto.js +17 -97
- package/fesm/interfaces/authentication.interface.js +1 -7
- package/fesm/services/auth-config.service.js +18 -44
- package/fesm/services/auth-datasource.provider.js +13 -31
- package/fesm/services/authentication.service.js +78 -83
- package/fesm/services/branch.service.js +11 -45
- package/fesm/services/company.service.js +8 -37
- package/fesm/services/user-permission.service.js +9 -27
- package/fesm/services/user.service.js +34 -92
- package/fesm/strategies/jwt.strategy.js +14 -1
- package/package.json +3 -3
- package/services/authentication.service.d.ts +6 -2
- package/services/branch.service.d.ts +2 -5
- package/services/company.service.d.ts +1 -4
- package/services/user.service.d.ts +1 -3
|
@@ -34,17 +34,15 @@ import { ErrorHandler } from '@flusys/nestjs-shared/utils';
|
|
|
34
34
|
import { AuthConfigService } from './auth-config.service';
|
|
35
35
|
import { AuthDataSourceProvider } from './auth-datasource.provider';
|
|
36
36
|
import { PermissionType } from '../enums';
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
import { BCRYPT_SALT_ROUNDS, TOKEN_REVOKED_PREFIX } from '../config';
|
|
38
|
+
import { HybridCache } from '@flusys/nestjs-shared/classes';
|
|
39
39
|
import { CompanyBranch } from '../entities/company-branch.entity';
|
|
40
40
|
import { Company } from '../entities/company.entity';
|
|
41
41
|
import { UserCompanyPermission } from '../entities/user-company-permission.entity';
|
|
42
42
|
import { User } from '../entities/user.entity';
|
|
43
43
|
import { CompanySelectionSessionService } from './company-selection-session.service';
|
|
44
44
|
export class AuthenticationService {
|
|
45
|
-
//
|
|
46
|
-
// REPOSITORY GETTERS
|
|
47
|
-
// ==========================================================================
|
|
45
|
+
// Repository Getters
|
|
48
46
|
async getUserRepository() {
|
|
49
47
|
if (this.dataSourceProvider) {
|
|
50
48
|
return this.dataSourceProvider.getRepository(User);
|
|
@@ -75,13 +73,8 @@ export class AuthenticationService {
|
|
|
75
73
|
}
|
|
76
74
|
return this.injectedBranchRepository ?? null;
|
|
77
75
|
}
|
|
78
|
-
//
|
|
79
|
-
|
|
80
|
-
// ==========================================================================
|
|
81
|
-
/**
|
|
82
|
-
* Login with email and password
|
|
83
|
-
* Returns either tokens directly or requires company/branch selection
|
|
84
|
-
*/ async login(email, password) {
|
|
76
|
+
// Login
|
|
77
|
+
/** Login with email and password. Returns tokens or requires company/branch selection. */ async login(email, password) {
|
|
85
78
|
const user = await this.validateUser(email, password);
|
|
86
79
|
if (!user.isActive) {
|
|
87
80
|
throw new UnauthorizedException('User account is deactivated');
|
|
@@ -92,9 +85,7 @@ export class AuthenticationService {
|
|
|
92
85
|
}
|
|
93
86
|
return this.handleCompanyBasedLogin(user);
|
|
94
87
|
}
|
|
95
|
-
/**
|
|
96
|
-
* Handle login when company feature is enabled
|
|
97
|
-
*/ async handleCompanyBasedLogin(user) {
|
|
88
|
+
/** Handle login when company feature is enabled */ async handleCompanyBasedLogin(user) {
|
|
98
89
|
const permissionRepository = await this.getPermissionRepository();
|
|
99
90
|
const branchRepository = await this.getBranchRepository();
|
|
100
91
|
if (!permissionRepository || !branchRepository) {
|
|
@@ -144,13 +135,8 @@ export class AuthenticationService {
|
|
|
144
135
|
// Returns all companies with their branches for frontend to display
|
|
145
136
|
return await this.generateCompanySelectionResponse(user, companies, branches);
|
|
146
137
|
}
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
// ==========================================================================
|
|
150
|
-
/**
|
|
151
|
-
* Register a new user
|
|
152
|
-
* If company feature enabled, optionally join/create company
|
|
153
|
-
*/ async register(dto) {
|
|
138
|
+
// Registration
|
|
139
|
+
/** Register a new user. If company feature enabled, optionally join/create company. */ async register(dto) {
|
|
154
140
|
const userRepository = await this.getUserRepository();
|
|
155
141
|
// Check if user exists
|
|
156
142
|
// Note: Generic error message to prevent email enumeration attacks
|
|
@@ -181,9 +167,7 @@ export class AuthenticationService {
|
|
|
181
167
|
// Handle company feature
|
|
182
168
|
return this.handleCompanyRegistration(user, dto);
|
|
183
169
|
}
|
|
184
|
-
/**
|
|
185
|
-
* Handle company-related registration logic
|
|
186
|
-
*/ async handleCompanyRegistration(user, dto) {
|
|
170
|
+
/** Handle company-related registration logic */ async handleCompanyRegistration(user, dto) {
|
|
187
171
|
const companyRepository = await this.getCompanyRepository();
|
|
188
172
|
const branchRepository = await this.getBranchRepository();
|
|
189
173
|
const permissionRepository = await this.getPermissionRepository();
|
|
@@ -280,18 +264,11 @@ export class AuthenticationService {
|
|
|
280
264
|
// No company assigned
|
|
281
265
|
return this.generateLoginResponse(user, true);
|
|
282
266
|
}
|
|
283
|
-
/**
|
|
284
|
-
* Generate URL-friendly slug from name
|
|
285
|
-
*/ generateSlug(name) {
|
|
267
|
+
/** Generate URL-friendly slug from name */ generateSlug(name) {
|
|
286
268
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').substring(0, 100);
|
|
287
269
|
}
|
|
288
|
-
//
|
|
289
|
-
|
|
290
|
-
// ==========================================================================
|
|
291
|
-
/**
|
|
292
|
-
* Select company and branch (step 2 of login)
|
|
293
|
-
* Frontend receives all options and user selects both together
|
|
294
|
-
*/ async select(sessionId, companyId, branchId) {
|
|
270
|
+
// Company/Branch Selection
|
|
271
|
+
/** Select company and branch (step 2 of login). Frontend receives all options and user selects both. */ async select(sessionId, companyId, branchId) {
|
|
295
272
|
this.ensureCompanyFeatureEnabled();
|
|
296
273
|
if (!this.sessionService) {
|
|
297
274
|
throw new BadRequestException('Session service not available');
|
|
@@ -358,9 +335,7 @@ export class AuthenticationService {
|
|
|
358
335
|
}
|
|
359
336
|
return this.generateLoginResponseWithCompany(user, company, branch);
|
|
360
337
|
}
|
|
361
|
-
/**
|
|
362
|
-
* Switch company/branch after login
|
|
363
|
-
*/ async switchCompany(userId, companyId, branchId) {
|
|
338
|
+
/** Switch company/branch after login */ async switchCompany(userId, companyId, branchId) {
|
|
364
339
|
this.ensureCompanyFeatureEnabled();
|
|
365
340
|
const userRepository = await this.getUserRepository();
|
|
366
341
|
const user = await userRepository.findOne({
|
|
@@ -419,13 +394,8 @@ export class AuthenticationService {
|
|
|
419
394
|
}
|
|
420
395
|
return this.generateLoginResponseWithCompany(user, company, branch);
|
|
421
396
|
}
|
|
422
|
-
//
|
|
423
|
-
|
|
424
|
-
// ==========================================================================
|
|
425
|
-
/**
|
|
426
|
-
* Refresh access token (with token rotation)
|
|
427
|
-
* Returns new access token and new refresh token
|
|
428
|
-
*/ async refreshToken(refreshToken) {
|
|
397
|
+
// Token Management
|
|
398
|
+
/** Refresh access token (with token rotation). Returns new access token and new refresh token. */ async refreshToken(refreshToken) {
|
|
429
399
|
try {
|
|
430
400
|
const payload = this.jwtService.verify(refreshToken, {
|
|
431
401
|
secret: this.authConfig.getRefreshTokenSecret()
|
|
@@ -491,9 +461,7 @@ export class AuthenticationService {
|
|
|
491
461
|
throw new UnauthorizedException('Invalid or expired refresh token');
|
|
492
462
|
}
|
|
493
463
|
}
|
|
494
|
-
/**
|
|
495
|
-
* Change password
|
|
496
|
-
*/ async changePassword(userId, currentPassword, newPassword) {
|
|
464
|
+
/** Change password */ async changePassword(userId, currentPassword, newPassword) {
|
|
497
465
|
const userRepository = await this.getUserRepository();
|
|
498
466
|
const user = await userRepository.findOne({
|
|
499
467
|
where: {
|
|
@@ -510,15 +478,50 @@ export class AuthenticationService {
|
|
|
510
478
|
user.password = await bcrypt.hash(newPassword, BCRYPT_SALT_ROUNDS);
|
|
511
479
|
await userRepository.save(user);
|
|
512
480
|
}
|
|
513
|
-
/**
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
481
|
+
/** Logout - Revokes all tokens. Stores revocation timestamp in cache. */ async logout(userId) {
|
|
482
|
+
if (!this.cacheManager) {
|
|
483
|
+
// Cache not available - graceful degradation (client-side logout only)
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
const revokedAt = Math.floor(Date.now() / 1000); // Unix timestamp in seconds
|
|
487
|
+
const cacheKey = `${TOKEN_REVOKED_PREFIX}:${userId}`;
|
|
488
|
+
// TTL matches refresh token expiry (tokens older than this are already expired)
|
|
489
|
+
const refreshTokenExpiry = this.authConfig.getRefreshTokenExpiration();
|
|
490
|
+
const ttlMs = this.parseExpiryToMs(refreshTokenExpiry);
|
|
491
|
+
await this.cacheManager.set(cacheKey, revokedAt, ttlMs);
|
|
492
|
+
}
|
|
493
|
+
/** Check if a token is revoked (issued before user's revocation timestamp) */ async isTokenRevoked(userId, tokenIssuedAt) {
|
|
494
|
+
if (!this.cacheManager || !tokenIssuedAt) {
|
|
495
|
+
return false;
|
|
496
|
+
}
|
|
497
|
+
const cacheKey = `${TOKEN_REVOKED_PREFIX}:${userId}`;
|
|
498
|
+
const revokedAt = await this.cacheManager.get(cacheKey);
|
|
499
|
+
if (!revokedAt) {
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
// Token is revoked if it was issued before the revocation timestamp
|
|
503
|
+
return tokenIssuedAt < revokedAt;
|
|
504
|
+
}
|
|
505
|
+
/** Parse JWT expiry string to milliseconds */ parseExpiryToMs(expiry) {
|
|
506
|
+
const match = expiry.match(/^(\d+)([smhd])$/);
|
|
507
|
+
if (!match) return 7 * 24 * 60 * 60 * 1000; // Default 7 days
|
|
508
|
+
const value = parseInt(match[1], 10);
|
|
509
|
+
const unit = match[2];
|
|
510
|
+
switch(unit){
|
|
511
|
+
case 's':
|
|
512
|
+
return value * 1000;
|
|
513
|
+
case 'm':
|
|
514
|
+
return value * 60 * 1000;
|
|
515
|
+
case 'h':
|
|
516
|
+
return value * 60 * 60 * 1000;
|
|
517
|
+
case 'd':
|
|
518
|
+
return value * 24 * 60 * 60 * 1000;
|
|
519
|
+
default:
|
|
520
|
+
return 7 * 24 * 60 * 60 * 1000;
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
// Query Methods
|
|
524
|
+
/** Get user's companies */ async getUserCompanies(userId) {
|
|
522
525
|
this.ensureCompanyFeatureEnabled();
|
|
523
526
|
const permissionRepository = await this.getPermissionRepository();
|
|
524
527
|
const companyRepository = await this.getCompanyRepository();
|
|
@@ -539,9 +542,7 @@ export class AuthenticationService {
|
|
|
539
542
|
active: true
|
|
540
543
|
}).orderBy('c.name', 'ASC').getMany();
|
|
541
544
|
}
|
|
542
|
-
/**
|
|
543
|
-
* Get branches for a company that user has access to
|
|
544
|
-
*/ async getUserBranches(userId, companyId) {
|
|
545
|
+
/** Get branches for a company that user has access to */ async getUserBranches(userId, companyId) {
|
|
545
546
|
this.ensureCompanyFeatureEnabled();
|
|
546
547
|
const permissionRepository = await this.getPermissionRepository();
|
|
547
548
|
const branchRepository = await this.getBranchRepository();
|
|
@@ -576,9 +577,7 @@ export class AuthenticationService {
|
|
|
576
577
|
active: true
|
|
577
578
|
}).orderBy('b.serial', 'ASC').addOrderBy('b.name', 'ASC').getMany();
|
|
578
579
|
}
|
|
579
|
-
/**
|
|
580
|
-
* Get company by ID
|
|
581
|
-
*/ async getCompanyById(companyId) {
|
|
580
|
+
/** Get company by ID */ async getCompanyById(companyId) {
|
|
582
581
|
const companyRepository = await this.getCompanyRepository();
|
|
583
582
|
if (!companyRepository) {
|
|
584
583
|
return null;
|
|
@@ -589,9 +588,7 @@ export class AuthenticationService {
|
|
|
589
588
|
}
|
|
590
589
|
});
|
|
591
590
|
}
|
|
592
|
-
/**
|
|
593
|
-
* Get branch by ID
|
|
594
|
-
*/ async getBranchById(branchId) {
|
|
591
|
+
/** Get branch by ID */ async getBranchById(branchId) {
|
|
595
592
|
const branchRepository = await this.getBranchRepository();
|
|
596
593
|
if (!branchRepository) {
|
|
597
594
|
return null;
|
|
@@ -602,10 +599,7 @@ export class AuthenticationService {
|
|
|
602
599
|
}
|
|
603
600
|
});
|
|
604
601
|
}
|
|
605
|
-
/**
|
|
606
|
-
* Get user info for /auth/me endpoint
|
|
607
|
-
* Returns only user, company, and branch information (no tokens)
|
|
608
|
-
*/ async getUserInfo(userId, companyId, branchId) {
|
|
602
|
+
/** Get user info for /auth/me endpoint. Returns user, company, and branch info (no tokens). */ async getUserInfo(userId, companyId, branchId) {
|
|
609
603
|
const userRepository = await this.getUserRepository();
|
|
610
604
|
const user = await userRepository.findOne({
|
|
611
605
|
where: {
|
|
@@ -656,9 +650,7 @@ export class AuthenticationService {
|
|
|
656
650
|
}
|
|
657
651
|
return response;
|
|
658
652
|
}
|
|
659
|
-
//
|
|
660
|
-
// HELPER METHODS
|
|
661
|
-
// ==========================================================================
|
|
653
|
+
// Helper Methods
|
|
662
654
|
ensureCompanyFeatureEnabled() {
|
|
663
655
|
if (!this.authConfig.isCompanyFeatureEnabled()) {
|
|
664
656
|
throw new BadRequestException('Company feature is not enabled');
|
|
@@ -682,9 +674,7 @@ export class AuthenticationService {
|
|
|
682
674
|
await userRepository.save(user);
|
|
683
675
|
return user;
|
|
684
676
|
}
|
|
685
|
-
//
|
|
686
|
-
// RESPONSE GENERATORS
|
|
687
|
-
// ==========================================================================
|
|
677
|
+
// Response Generators
|
|
688
678
|
generateLoginResponse(user, companyFeatureEnabled = false) {
|
|
689
679
|
const payload = {
|
|
690
680
|
id: user.id,
|
|
@@ -797,9 +787,10 @@ export class AuthenticationService {
|
|
|
797
787
|
};
|
|
798
788
|
}
|
|
799
789
|
// NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
|
|
800
|
-
constructor(jwtService, authConfig, dataSourceProvider, sessionService, injectedUserRepository, injectedPermissionRepository, injectedCompanyRepository, injectedBranchRepository){
|
|
790
|
+
constructor(jwtService, authConfig, cacheManager, dataSourceProvider, sessionService, injectedUserRepository, injectedPermissionRepository, injectedCompanyRepository, injectedBranchRepository){
|
|
801
791
|
_define_property(this, "jwtService", void 0);
|
|
802
792
|
_define_property(this, "authConfig", void 0);
|
|
793
|
+
_define_property(this, "cacheManager", void 0);
|
|
803
794
|
_define_property(this, "dataSourceProvider", void 0);
|
|
804
795
|
_define_property(this, "sessionService", void 0);
|
|
805
796
|
_define_property(this, "injectedUserRepository", void 0);
|
|
@@ -809,6 +800,7 @@ export class AuthenticationService {
|
|
|
809
800
|
_define_property(this, "logger", void 0);
|
|
810
801
|
this.jwtService = jwtService;
|
|
811
802
|
this.authConfig = authConfig;
|
|
803
|
+
this.cacheManager = cacheManager;
|
|
812
804
|
this.dataSourceProvider = dataSourceProvider;
|
|
813
805
|
this.sessionService = sessionService;
|
|
814
806
|
this.injectedUserRepository = injectedUserRepository;
|
|
@@ -825,21 +817,24 @@ AuthenticationService = _ts_decorate([
|
|
|
825
817
|
_ts_param(0, Inject(JwtService)),
|
|
826
818
|
_ts_param(1, Inject(AuthConfigService)),
|
|
827
819
|
_ts_param(2, Optional()),
|
|
828
|
-
_ts_param(2, Inject(
|
|
820
|
+
_ts_param(2, Inject('CACHE_INSTANCE')),
|
|
829
821
|
_ts_param(3, Optional()),
|
|
830
|
-
_ts_param(3, Inject(
|
|
822
|
+
_ts_param(3, Inject(AuthDataSourceProvider)),
|
|
831
823
|
_ts_param(4, Optional()),
|
|
832
|
-
_ts_param(4,
|
|
824
|
+
_ts_param(4, Inject(CompanySelectionSessionService)),
|
|
833
825
|
_ts_param(5, Optional()),
|
|
834
|
-
_ts_param(5, InjectRepository(
|
|
826
|
+
_ts_param(5, InjectRepository(User)),
|
|
835
827
|
_ts_param(6, Optional()),
|
|
836
|
-
_ts_param(6, InjectRepository(
|
|
828
|
+
_ts_param(6, InjectRepository(UserCompanyPermission)),
|
|
837
829
|
_ts_param(7, Optional()),
|
|
838
|
-
_ts_param(7, InjectRepository(
|
|
830
|
+
_ts_param(7, InjectRepository(Company)),
|
|
831
|
+
_ts_param(8, Optional()),
|
|
832
|
+
_ts_param(8, InjectRepository(CompanyBranch)),
|
|
839
833
|
_ts_metadata("design:type", Function),
|
|
840
834
|
_ts_metadata("design:paramtypes", [
|
|
841
835
|
typeof JwtService === "undefined" ? Object : JwtService,
|
|
842
836
|
typeof AuthConfigService === "undefined" ? Object : AuthConfigService,
|
|
837
|
+
typeof HybridCache === "undefined" ? Object : HybridCache,
|
|
843
838
|
typeof AuthDataSourceProvider === "undefined" ? Object : AuthDataSourceProvider,
|
|
844
839
|
typeof CompanySelectionSessionService === "undefined" ? Object : CompanySelectionSessionService,
|
|
845
840
|
typeof Repository === "undefined" ? Object : Repository,
|
|
@@ -27,46 +27,20 @@ function _ts_param(paramIndex, decorator) {
|
|
|
27
27
|
}
|
|
28
28
|
import { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/classes';
|
|
29
29
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
30
|
-
import { Inject, Injectable,
|
|
30
|
+
import { Inject, Injectable, Scope } from '@nestjs/common';
|
|
31
31
|
import { CompanyBranch } from '../entities/company-branch.entity';
|
|
32
32
|
import { AuthConfigService } from './auth-config.service';
|
|
33
33
|
import { AuthDataSourceProvider } from './auth-datasource.provider';
|
|
34
34
|
export class BranchService extends RequestScopedApiService {
|
|
35
|
-
|
|
36
|
-
* Resolve entity class for this service
|
|
37
|
-
* @returns CompanyBranch entity class
|
|
38
|
-
*/ resolveEntity() {
|
|
35
|
+
resolveEntity() {
|
|
39
36
|
return CompanyBranch;
|
|
40
37
|
}
|
|
41
|
-
|
|
42
|
-
* Get DataSource provider for this service
|
|
43
|
-
* @returns AuthDataSourceProvider instance
|
|
44
|
-
*/ getDataSourceProvider() {
|
|
38
|
+
getDataSourceProvider() {
|
|
45
39
|
return this.dataSourceProvider;
|
|
46
40
|
}
|
|
47
|
-
//
|
|
48
|
-
async convertSingleDtoToEntity(dto, _user) {
|
|
49
|
-
// Repository initialization is handled automatically by RequestScopedApiService
|
|
50
|
-
let branch = new CompanyBranch();
|
|
51
|
-
if ('id' in dto && dto.id && typeof dto.id === 'string') {
|
|
52
|
-
const dbData = await this.repository.findOne({
|
|
53
|
-
where: {
|
|
54
|
-
id: dto.id
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
if (!dbData) {
|
|
58
|
-
throw new NotFoundException('Branch not found');
|
|
59
|
-
}
|
|
60
|
-
branch = dbData;
|
|
61
|
-
}
|
|
62
|
-
return {
|
|
63
|
-
...branch,
|
|
64
|
-
...dto
|
|
65
|
-
};
|
|
66
|
-
}
|
|
41
|
+
// Query Customization
|
|
67
42
|
async getSelectQuery(query, _user, select) {
|
|
68
|
-
|
|
69
|
-
if (!select || !select.length) {
|
|
43
|
+
if (!select?.length) {
|
|
70
44
|
select = [
|
|
71
45
|
'id',
|
|
72
46
|
'name',
|
|
@@ -75,14 +49,13 @@ export class BranchService extends RequestScopedApiService {
|
|
|
75
49
|
'createdAt'
|
|
76
50
|
];
|
|
77
51
|
}
|
|
78
|
-
|
|
79
|
-
query.select(selectFields);
|
|
52
|
+
query.select(select.map((f)=>`${this.entityName}.${f}`));
|
|
80
53
|
return {
|
|
81
54
|
query,
|
|
82
55
|
isRaw: false
|
|
83
56
|
};
|
|
84
57
|
}
|
|
85
|
-
async getGlobalSearchQuery(query, search
|
|
58
|
+
async getGlobalSearchQuery(query, search) {
|
|
86
59
|
query.andWhere('(company_branch.name LIKE :search OR company_branch.slug LIKE :search OR company_branch.email LIKE :search)', {
|
|
87
60
|
search: `%${search}%`
|
|
88
61
|
});
|
|
@@ -91,24 +64,17 @@ export class BranchService extends RequestScopedApiService {
|
|
|
91
64
|
isRaw: false
|
|
92
65
|
};
|
|
93
66
|
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
*/ async getExtraManipulateQuery(query, _filterAndPaginationDto, user) {
|
|
98
|
-
const result = await super.getExtraManipulateQuery(query, _filterAndPaginationDto, user);
|
|
99
|
-
// Only apply company filtering if company feature is enabled and user has company context
|
|
100
|
-
const isCompanyFeatureEnabled = this.authConfig.isCompanyFeatureEnabled();
|
|
101
|
-
if (isCompanyFeatureEnabled && user?.companyId) {
|
|
67
|
+
async getExtraManipulateQuery(query, filterDto, user) {
|
|
68
|
+
const result = await super.getExtraManipulateQuery(query, filterDto, user);
|
|
69
|
+
if (this.authConfig.isCompanyFeatureEnabled() && user?.companyId) {
|
|
102
70
|
query.andWhere('company_branch.company_id = :companyId', {
|
|
103
71
|
companyId: user.companyId
|
|
104
72
|
});
|
|
105
73
|
}
|
|
106
74
|
return result;
|
|
107
75
|
}
|
|
108
|
-
// NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
|
|
109
76
|
constructor(cacheManager, utilsService, dataSourceProvider, authConfig){
|
|
110
|
-
|
|
111
|
-
super('company_branch', null, cacheManager, utilsService, BranchService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), _define_property(this, "authConfig", void 0), _define_property(this, "logger", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider, this.authConfig = authConfig, this.logger = new Logger(BranchService.name);
|
|
77
|
+
super('company_branch', null, cacheManager, utilsService, BranchService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), _define_property(this, "authConfig", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider, this.authConfig = authConfig;
|
|
112
78
|
}
|
|
113
79
|
}
|
|
114
80
|
BranchService = _ts_decorate([
|
|
@@ -27,45 +27,19 @@ function _ts_param(paramIndex, decorator) {
|
|
|
27
27
|
}
|
|
28
28
|
import { RequestScopedApiService, HybridCache } from '@flusys/nestjs-shared/classes';
|
|
29
29
|
import { UtilsService } from '@flusys/nestjs-shared/modules';
|
|
30
|
-
import { Inject, Injectable,
|
|
30
|
+
import { Inject, Injectable, Scope } from '@nestjs/common';
|
|
31
31
|
import { Company } from '../entities/company.entity';
|
|
32
32
|
import { AuthDataSourceProvider } from './auth-datasource.provider';
|
|
33
33
|
export class CompanyService extends RequestScopedApiService {
|
|
34
|
-
|
|
35
|
-
* Resolve entity class for this service
|
|
36
|
-
* @returns Company entity class
|
|
37
|
-
*/ resolveEntity() {
|
|
34
|
+
resolveEntity() {
|
|
38
35
|
return Company;
|
|
39
36
|
}
|
|
40
|
-
|
|
41
|
-
* Get DataSource provider for this service
|
|
42
|
-
* @returns AuthDataSourceProvider instance
|
|
43
|
-
*/ getDataSourceProvider() {
|
|
37
|
+
getDataSourceProvider() {
|
|
44
38
|
return this.dataSourceProvider;
|
|
45
39
|
}
|
|
46
|
-
//
|
|
47
|
-
async convertSingleDtoToEntity(dto, _user) {
|
|
48
|
-
// Repository initialization is handled automatically by RequestScopedApiService
|
|
49
|
-
let company = new Company();
|
|
50
|
-
if ('id' in dto && dto.id && typeof dto.id === 'string') {
|
|
51
|
-
const dbData = await this.repository.findOne({
|
|
52
|
-
where: {
|
|
53
|
-
id: dto.id
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
if (!dbData) {
|
|
57
|
-
throw new NotFoundException('Company not found');
|
|
58
|
-
}
|
|
59
|
-
company = dbData;
|
|
60
|
-
}
|
|
61
|
-
return {
|
|
62
|
-
...company,
|
|
63
|
-
...dto
|
|
64
|
-
};
|
|
65
|
-
}
|
|
40
|
+
// Query Customization
|
|
66
41
|
async getSelectQuery(query, _user, select) {
|
|
67
|
-
|
|
68
|
-
if (!select || !select.length) {
|
|
42
|
+
if (!select?.length) {
|
|
69
43
|
select = [
|
|
70
44
|
'id',
|
|
71
45
|
'name',
|
|
@@ -74,14 +48,13 @@ export class CompanyService extends RequestScopedApiService {
|
|
|
74
48
|
'createdAt'
|
|
75
49
|
];
|
|
76
50
|
}
|
|
77
|
-
|
|
78
|
-
query.select(selectFields);
|
|
51
|
+
query.select(select.map((f)=>`${this.entityName}.${f}`));
|
|
79
52
|
return {
|
|
80
53
|
query,
|
|
81
54
|
isRaw: false
|
|
82
55
|
};
|
|
83
56
|
}
|
|
84
|
-
async getGlobalSearchQuery(query, search
|
|
57
|
+
async getGlobalSearchQuery(query, search) {
|
|
85
58
|
query.andWhere('(company.name LIKE :search OR company.slug LIKE :search OR company.email LIKE :search)', {
|
|
86
59
|
search: `%${search}%`
|
|
87
60
|
});
|
|
@@ -90,10 +63,8 @@ export class CompanyService extends RequestScopedApiService {
|
|
|
90
63
|
isRaw: false
|
|
91
64
|
};
|
|
92
65
|
}
|
|
93
|
-
// NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
|
|
94
66
|
constructor(cacheManager, utilsService, dataSourceProvider){
|
|
95
|
-
|
|
96
|
-
super('company', null, cacheManager, utilsService, CompanyService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), _define_property(this, "logger", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider, this.logger = new Logger(CompanyService.name);
|
|
67
|
+
super('company', null, cacheManager, utilsService, CompanyService.name, true), _define_property(this, "cacheManager", void 0), _define_property(this, "utilsService", void 0), _define_property(this, "dataSourceProvider", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider;
|
|
97
68
|
}
|
|
98
69
|
}
|
|
99
70
|
CompanyService = _ts_decorate([
|
|
@@ -29,20 +29,14 @@ import { BadRequestException, Inject, Injectable, Logger, NotFoundException, Sco
|
|
|
29
29
|
import { PermissionType, UserCompanyPermission } from '../entities/user-company-permission.entity';
|
|
30
30
|
import { AuthDataSourceProvider } from './auth-datasource.provider';
|
|
31
31
|
export class UserPermissionService {
|
|
32
|
-
/**
|
|
33
|
-
* Get repository lazily - initialized on first access
|
|
34
|
-
*/ async getRepository() {
|
|
32
|
+
/** Get repository lazily - initialized on first access */ async getRepository() {
|
|
35
33
|
if (!this._repository) {
|
|
36
34
|
this._repository = await this.dataSourceProvider.getRepository(UserCompanyPermission);
|
|
37
35
|
}
|
|
38
36
|
return this._repository;
|
|
39
37
|
}
|
|
40
|
-
//
|
|
41
|
-
|
|
42
|
-
// ==========================================================================
|
|
43
|
-
/**
|
|
44
|
-
* Find all companies a user has access to
|
|
45
|
-
*/ async findUserCompanies(userId) {
|
|
38
|
+
// Company Permissions
|
|
39
|
+
/** Find all companies a user has access to */ async findUserCompanies(userId) {
|
|
46
40
|
const repository = await this.getRepository();
|
|
47
41
|
return repository.find({
|
|
48
42
|
where: {
|
|
@@ -57,9 +51,7 @@ export class UserPermissionService {
|
|
|
57
51
|
}
|
|
58
52
|
});
|
|
59
53
|
}
|
|
60
|
-
/**
|
|
61
|
-
* Assign user to a company
|
|
62
|
-
*/ async assignUserToCompany(dto) {
|
|
54
|
+
/** Assign user to a company */ async assignUserToCompany(dto) {
|
|
63
55
|
const repository = await this.getRepository();
|
|
64
56
|
// Check if already exists
|
|
65
57
|
const existing = await repository.findOne({
|
|
@@ -80,9 +72,7 @@ export class UserPermissionService {
|
|
|
80
72
|
});
|
|
81
73
|
await repository.save(permission);
|
|
82
74
|
}
|
|
83
|
-
/**
|
|
84
|
-
* Revoke user access to a company
|
|
85
|
-
*/ async revokeCompanyAccess(userId, companyId) {
|
|
75
|
+
/** Revoke user access to a company */ async revokeCompanyAccess(userId, companyId) {
|
|
86
76
|
const repository = await this.getRepository();
|
|
87
77
|
const permission = await repository.findOne({
|
|
88
78
|
where: {
|
|
@@ -96,12 +86,8 @@ export class UserPermissionService {
|
|
|
96
86
|
}
|
|
97
87
|
await repository.remove(permission);
|
|
98
88
|
}
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
// ==========================================================================
|
|
102
|
-
/**
|
|
103
|
-
* Find all branches a user has access to
|
|
104
|
-
*/ async findUserBranches(userId) {
|
|
89
|
+
// Branch Permissions
|
|
90
|
+
/** Find all branches a user has access to */ async findUserBranches(userId) {
|
|
105
91
|
const repository = await this.getRepository();
|
|
106
92
|
return repository.find({
|
|
107
93
|
where: {
|
|
@@ -116,9 +102,7 @@ export class UserPermissionService {
|
|
|
116
102
|
}
|
|
117
103
|
});
|
|
118
104
|
}
|
|
119
|
-
/**
|
|
120
|
-
* Assign user to a branch
|
|
121
|
-
*/ async assignUserToBranch(dto) {
|
|
105
|
+
/** Assign user to a branch */ async assignUserToBranch(dto) {
|
|
122
106
|
const repository = await this.getRepository();
|
|
123
107
|
// Check if already exists
|
|
124
108
|
const existing = await repository.findOne({
|
|
@@ -139,9 +123,7 @@ export class UserPermissionService {
|
|
|
139
123
|
});
|
|
140
124
|
await repository.save(permission);
|
|
141
125
|
}
|
|
142
|
-
/**
|
|
143
|
-
* Revoke user access to a branch
|
|
144
|
-
*/ async revokeBranchAccess(userId, branchId) {
|
|
126
|
+
/** Revoke user access to a branch */ async revokeBranchAccess(userId, branchId) {
|
|
145
127
|
const repository = await this.getRepository();
|
|
146
128
|
const permission = await repository.findOne({
|
|
147
129
|
where: {
|