@flusys/nestjs-auth 0.1.0-beta.2 → 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.
Files changed (38) hide show
  1. package/README.md +1 -11
  2. package/cjs/config/auth.constants.js +10 -4
  3. package/cjs/controllers/authentication.controller.js +7 -32
  4. package/cjs/controllers/branch.controller.js +3 -4
  5. package/cjs/controllers/company.controller.js +3 -4
  6. package/cjs/controllers/user-permission.controller.js +6 -18
  7. package/cjs/controllers/user.controller.js +17 -22
  8. package/cjs/interfaces/authentication.interface.js +0 -3
  9. package/cjs/services/auth-config.service.js +18 -44
  10. package/cjs/services/auth-datasource.provider.js +13 -31
  11. package/cjs/services/authentication.service.js +80 -85
  12. package/cjs/services/branch.service.js +10 -44
  13. package/cjs/services/company.service.js +7 -36
  14. package/cjs/services/user-permission.service.js +9 -27
  15. package/cjs/services/user.service.js +33 -91
  16. package/cjs/strategies/jwt.strategy.js +14 -1
  17. package/config/auth.constants.d.ts +2 -0
  18. package/fesm/config/auth.constants.js +7 -8
  19. package/fesm/controllers/authentication.controller.js +7 -32
  20. package/fesm/controllers/branch.controller.js +5 -6
  21. package/fesm/controllers/company.controller.js +5 -6
  22. package/fesm/controllers/user-permission.controller.js +6 -18
  23. package/fesm/controllers/user.controller.js +17 -22
  24. package/fesm/dtos/authentication.dto.js +17 -97
  25. package/fesm/interfaces/authentication.interface.js +1 -7
  26. package/fesm/services/auth-config.service.js +18 -44
  27. package/fesm/services/auth-datasource.provider.js +13 -31
  28. package/fesm/services/authentication.service.js +78 -83
  29. package/fesm/services/branch.service.js +11 -45
  30. package/fesm/services/company.service.js +8 -37
  31. package/fesm/services/user-permission.service.js +9 -27
  32. package/fesm/services/user.service.js +34 -92
  33. package/fesm/strategies/jwt.strategy.js +14 -1
  34. package/package.json +4 -4
  35. package/services/authentication.service.d.ts +6 -2
  36. package/services/branch.service.d.ts +2 -5
  37. package/services/company.service.d.ts +1 -4
  38. 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
- // Password hashing configuration
38
- const BCRYPT_SALT_ROUNDS = 12; // Increased from 10 for better security
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
- // LOGIN
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
- // REGISTRATION
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
- // COMPANY/BRANCH SELECTION
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
- // TOKEN MANAGEMENT
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
- * Logout (placeholder for token blacklisting if needed)
515
- */ async logout(_userId) {}
516
- // ==========================================================================
517
- // QUERY METHODS
518
- // ==========================================================================
519
- /**
520
- * Get user's companies
521
- */ async getUserCompanies(userId) {
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(AuthDataSourceProvider)),
820
+ _ts_param(2, Inject('CACHE_INSTANCE')),
829
821
  _ts_param(3, Optional()),
830
- _ts_param(3, Inject(CompanySelectionSessionService)),
822
+ _ts_param(3, Inject(AuthDataSourceProvider)),
831
823
  _ts_param(4, Optional()),
832
- _ts_param(4, InjectRepository(User)),
824
+ _ts_param(4, Inject(CompanySelectionSessionService)),
833
825
  _ts_param(5, Optional()),
834
- _ts_param(5, InjectRepository(UserCompanyPermission)),
826
+ _ts_param(5, InjectRepository(User)),
835
827
  _ts_param(6, Optional()),
836
- _ts_param(6, InjectRepository(Company)),
828
+ _ts_param(6, InjectRepository(UserCompanyPermission)),
837
829
  _ts_param(7, Optional()),
838
- _ts_param(7, InjectRepository(CompanyBranch)),
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, Logger, NotFoundException, Scope } from '@nestjs/common';
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
- // ==================== Entity Conversion ====================
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
- // Repository initialization is handled automatically by RequestScopedApiService
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
- const selectFields = select.map((field)=>`${this.entityName}.${field}`);
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, _user) {
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
- * Override: Filter branches by user's company
96
- * Ensures users can only see branches belonging to their company
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
- // Repository will be set asynchronously by RequestScopedApiService
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, Logger, NotFoundException, Scope } from '@nestjs/common';
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
- // ==================== Entity Conversion ====================
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
- // Repository initialization is handled automatically by RequestScopedApiService
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
- const selectFields = select.map((field)=>`${this.entityName}.${field}`);
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, _user) {
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
- // Repository will be set asynchronously by RequestScopedApiService
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
- // COMPANY PERMISSIONS
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
- // BRANCH PERMISSIONS
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: {