@flusys/nestjs-auth 0.1.0-beta.3 → 1.1.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 +14 -4
- package/cjs/controllers/authentication.controller.js +173 -36
- 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 +168 -22
- package/cjs/dtos/authentication.dto.js +106 -3
- package/cjs/entities/index.js +5 -1
- package/cjs/entities/pending-registration.entity.js +125 -0
- package/cjs/entities/user-token.entity.js +129 -0
- package/cjs/interfaces/auth-email-provider.interface.js +45 -0
- package/cjs/interfaces/authentication.interface.js +0 -3
- package/cjs/interfaces/index.js +2 -0
- package/cjs/interfaces/user-enricher.interface.js +4 -0
- package/cjs/modules/auth.module.js +8 -2
- package/cjs/services/auth-config.service.js +22 -44
- package/cjs/services/auth-datasource.provider.js +17 -33
- package/cjs/services/auth-email.service.js +452 -0
- package/cjs/services/authentication.service.js +229 -115
- package/cjs/services/branch.service.js +10 -44
- package/cjs/services/company.service.js +7 -36
- package/cjs/services/index.js +1 -0
- package/cjs/services/user-permission.service.js +9 -27
- package/cjs/services/user.service.js +177 -99
- package/cjs/strategies/jwt.strategy.js +14 -1
- package/config/auth.constants.d.ts +3 -0
- package/controllers/authentication.controller.d.ts +13 -9
- package/controllers/user.controller.d.ts +27 -0
- package/dtos/authentication.dto.d.ts +21 -3
- package/dtos/user.dto.d.ts +1 -0
- package/entities/index.d.ts +7 -3
- package/entities/pending-registration.entity.d.ts +17 -0
- package/entities/user-token.entity.d.ts +15 -0
- package/fesm/config/auth.constants.js +18 -8
- package/fesm/controllers/authentication.controller.js +174 -37
- 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 +168 -22
- package/fesm/dtos/authentication.dto.js +109 -100
- package/fesm/entities/index.js +7 -1
- package/fesm/entities/pending-registration.entity.js +115 -0
- package/fesm/entities/user-token.entity.js +113 -0
- package/fesm/interfaces/auth-email-provider.interface.js +38 -0
- package/fesm/interfaces/authentication.interface.js +1 -7
- package/fesm/interfaces/index.js +2 -0
- package/fesm/interfaces/user-enricher.interface.js +26 -0
- package/fesm/modules/auth.module.js +9 -3
- package/fesm/services/auth-config.service.js +22 -44
- package/fesm/services/auth-datasource.provider.js +17 -33
- package/fesm/services/auth-email.service.js +401 -0
- package/fesm/services/authentication.service.js +227 -113
- package/fesm/services/branch.service.js +11 -45
- package/fesm/services/company.service.js +8 -37
- package/fesm/services/index.js +1 -0
- package/fesm/services/user-permission.service.js +9 -27
- package/fesm/services/user.service.js +178 -100
- package/fesm/strategies/jwt.strategy.js +14 -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 +14 -0
- package/interfaces/index.d.ts +2 -0
- package/interfaces/user-enricher.interface.d.ts +67 -0
- package/interfaces/user.interface.d.ts +1 -0
- package/package.json +3 -3
- package/services/auth-config.service.d.ts +1 -0
- package/services/auth-email.service.d.ts +50 -0
- package/services/authentication.service.d.ts +21 -5
- package/services/branch.service.d.ts +2 -5
- package/services/company.service.d.ts +1 -4
- package/services/index.d.ts +1 -0
- package/services/user.service.d.ts +17 -6
|
@@ -42,41 +42,15 @@ function _ts_param(paramIndex, decorator) {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
let BranchService = class BranchService extends _classes.RequestScopedApiService {
|
|
45
|
-
|
|
46
|
-
* Resolve entity class for this service
|
|
47
|
-
* @returns CompanyBranch entity class
|
|
48
|
-
*/ resolveEntity() {
|
|
45
|
+
resolveEntity() {
|
|
49
46
|
return _companybranchentity.CompanyBranch;
|
|
50
47
|
}
|
|
51
|
-
|
|
52
|
-
* Get DataSource provider for this service
|
|
53
|
-
* @returns AuthDataSourceProvider instance
|
|
54
|
-
*/ getDataSourceProvider() {
|
|
48
|
+
getDataSourceProvider() {
|
|
55
49
|
return this.dataSourceProvider;
|
|
56
50
|
}
|
|
57
|
-
//
|
|
58
|
-
async convertSingleDtoToEntity(dto, _user) {
|
|
59
|
-
// Repository initialization is handled automatically by RequestScopedApiService
|
|
60
|
-
let branch = new _companybranchentity.CompanyBranch();
|
|
61
|
-
if ('id' in dto && dto.id && typeof dto.id === 'string') {
|
|
62
|
-
const dbData = await this.repository.findOne({
|
|
63
|
-
where: {
|
|
64
|
-
id: dto.id
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
if (!dbData) {
|
|
68
|
-
throw new _common.NotFoundException('Branch not found');
|
|
69
|
-
}
|
|
70
|
-
branch = dbData;
|
|
71
|
-
}
|
|
72
|
-
return {
|
|
73
|
-
...branch,
|
|
74
|
-
...dto
|
|
75
|
-
};
|
|
76
|
-
}
|
|
51
|
+
// Query Customization
|
|
77
52
|
async getSelectQuery(query, _user, select) {
|
|
78
|
-
|
|
79
|
-
if (!select || !select.length) {
|
|
53
|
+
if (!select?.length) {
|
|
80
54
|
select = [
|
|
81
55
|
'id',
|
|
82
56
|
'name',
|
|
@@ -85,14 +59,13 @@ let BranchService = class BranchService extends _classes.RequestScopedApiService
|
|
|
85
59
|
'createdAt'
|
|
86
60
|
];
|
|
87
61
|
}
|
|
88
|
-
|
|
89
|
-
query.select(selectFields);
|
|
62
|
+
query.select(select.map((f)=>`${this.entityName}.${f}`));
|
|
90
63
|
return {
|
|
91
64
|
query,
|
|
92
65
|
isRaw: false
|
|
93
66
|
};
|
|
94
67
|
}
|
|
95
|
-
async getGlobalSearchQuery(query, search
|
|
68
|
+
async getGlobalSearchQuery(query, search) {
|
|
96
69
|
query.andWhere('(company_branch.name LIKE :search OR company_branch.slug LIKE :search OR company_branch.email LIKE :search)', {
|
|
97
70
|
search: `%${search}%`
|
|
98
71
|
});
|
|
@@ -101,24 +74,17 @@ let BranchService = class BranchService extends _classes.RequestScopedApiService
|
|
|
101
74
|
isRaw: false
|
|
102
75
|
};
|
|
103
76
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
*/ async getExtraManipulateQuery(query, _filterAndPaginationDto, user) {
|
|
108
|
-
const result = await super.getExtraManipulateQuery(query, _filterAndPaginationDto, user);
|
|
109
|
-
// Only apply company filtering if company feature is enabled and user has company context
|
|
110
|
-
const isCompanyFeatureEnabled = this.authConfig.isCompanyFeatureEnabled();
|
|
111
|
-
if (isCompanyFeatureEnabled && user?.companyId) {
|
|
77
|
+
async getExtraManipulateQuery(query, filterDto, user) {
|
|
78
|
+
const result = await super.getExtraManipulateQuery(query, filterDto, user);
|
|
79
|
+
if (this.authConfig.isCompanyFeatureEnabled() && user?.companyId) {
|
|
112
80
|
query.andWhere('company_branch.company_id = :companyId', {
|
|
113
81
|
companyId: user.companyId
|
|
114
82
|
});
|
|
115
83
|
}
|
|
116
84
|
return result;
|
|
117
85
|
}
|
|
118
|
-
// NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
|
|
119
86
|
constructor(cacheManager, utilsService, dataSourceProvider, authConfig){
|
|
120
|
-
|
|
121
|
-
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 _common.Logger(BranchService.name);
|
|
87
|
+
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;
|
|
122
88
|
}
|
|
123
89
|
};
|
|
124
90
|
BranchService = _ts_decorate([
|
|
@@ -41,41 +41,15 @@ function _ts_param(paramIndex, decorator) {
|
|
|
41
41
|
};
|
|
42
42
|
}
|
|
43
43
|
let CompanyService = class CompanyService extends _classes.RequestScopedApiService {
|
|
44
|
-
|
|
45
|
-
* Resolve entity class for this service
|
|
46
|
-
* @returns Company entity class
|
|
47
|
-
*/ resolveEntity() {
|
|
44
|
+
resolveEntity() {
|
|
48
45
|
return _companyentity.Company;
|
|
49
46
|
}
|
|
50
|
-
|
|
51
|
-
* Get DataSource provider for this service
|
|
52
|
-
* @returns AuthDataSourceProvider instance
|
|
53
|
-
*/ getDataSourceProvider() {
|
|
47
|
+
getDataSourceProvider() {
|
|
54
48
|
return this.dataSourceProvider;
|
|
55
49
|
}
|
|
56
|
-
//
|
|
57
|
-
async convertSingleDtoToEntity(dto, _user) {
|
|
58
|
-
// Repository initialization is handled automatically by RequestScopedApiService
|
|
59
|
-
let company = new _companyentity.Company();
|
|
60
|
-
if ('id' in dto && dto.id && typeof dto.id === 'string') {
|
|
61
|
-
const dbData = await this.repository.findOne({
|
|
62
|
-
where: {
|
|
63
|
-
id: dto.id
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
if (!dbData) {
|
|
67
|
-
throw new _common.NotFoundException('Company not found');
|
|
68
|
-
}
|
|
69
|
-
company = dbData;
|
|
70
|
-
}
|
|
71
|
-
return {
|
|
72
|
-
...company,
|
|
73
|
-
...dto
|
|
74
|
-
};
|
|
75
|
-
}
|
|
50
|
+
// Query Customization
|
|
76
51
|
async getSelectQuery(query, _user, select) {
|
|
77
|
-
|
|
78
|
-
if (!select || !select.length) {
|
|
52
|
+
if (!select?.length) {
|
|
79
53
|
select = [
|
|
80
54
|
'id',
|
|
81
55
|
'name',
|
|
@@ -84,14 +58,13 @@ let CompanyService = class CompanyService extends _classes.RequestScopedApiServi
|
|
|
84
58
|
'createdAt'
|
|
85
59
|
];
|
|
86
60
|
}
|
|
87
|
-
|
|
88
|
-
query.select(selectFields);
|
|
61
|
+
query.select(select.map((f)=>`${this.entityName}.${f}`));
|
|
89
62
|
return {
|
|
90
63
|
query,
|
|
91
64
|
isRaw: false
|
|
92
65
|
};
|
|
93
66
|
}
|
|
94
|
-
async getGlobalSearchQuery(query, search
|
|
67
|
+
async getGlobalSearchQuery(query, search) {
|
|
95
68
|
query.andWhere('(company.name LIKE :search OR company.slug LIKE :search OR company.email LIKE :search)', {
|
|
96
69
|
search: `%${search}%`
|
|
97
70
|
});
|
|
@@ -100,10 +73,8 @@ let CompanyService = class CompanyService extends _classes.RequestScopedApiServi
|
|
|
100
73
|
isRaw: false
|
|
101
74
|
};
|
|
102
75
|
}
|
|
103
|
-
// NOTE: @Inject() required for bundled code - type metadata may be lost during esbuild
|
|
104
76
|
constructor(cacheManager, utilsService, dataSourceProvider){
|
|
105
|
-
|
|
106
|
-
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 _common.Logger(CompanyService.name);
|
|
77
|
+
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;
|
|
107
78
|
}
|
|
108
79
|
};
|
|
109
80
|
CompanyService = _ts_decorate([
|
package/cjs/services/index.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
3
3
|
value: true
|
|
4
4
|
});
|
|
5
5
|
_export_star(require("./authentication.service"), exports);
|
|
6
|
+
_export_star(require("./auth-email.service"), exports);
|
|
6
7
|
_export_star(require("./company-selection-session.service"), exports);
|
|
7
8
|
_export_star(require("./user.service"), exports);
|
|
8
9
|
_export_star(require("./company.service"), exports);
|
|
@@ -39,20 +39,14 @@ function _ts_param(paramIndex, decorator) {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
let UserPermissionService = class UserPermissionService {
|
|
42
|
-
/**
|
|
43
|
-
* Get repository lazily - initialized on first access
|
|
44
|
-
*/ async getRepository() {
|
|
42
|
+
/** Get repository lazily - initialized on first access */ async getRepository() {
|
|
45
43
|
if (!this._repository) {
|
|
46
44
|
this._repository = await this.dataSourceProvider.getRepository(_usercompanypermissionentity.UserCompanyPermission);
|
|
47
45
|
}
|
|
48
46
|
return this._repository;
|
|
49
47
|
}
|
|
50
|
-
//
|
|
51
|
-
|
|
52
|
-
// ==========================================================================
|
|
53
|
-
/**
|
|
54
|
-
* Find all companies a user has access to
|
|
55
|
-
*/ async findUserCompanies(userId) {
|
|
48
|
+
// Company Permissions
|
|
49
|
+
/** Find all companies a user has access to */ async findUserCompanies(userId) {
|
|
56
50
|
const repository = await this.getRepository();
|
|
57
51
|
return repository.find({
|
|
58
52
|
where: {
|
|
@@ -67,9 +61,7 @@ let UserPermissionService = class UserPermissionService {
|
|
|
67
61
|
}
|
|
68
62
|
});
|
|
69
63
|
}
|
|
70
|
-
/**
|
|
71
|
-
* Assign user to a company
|
|
72
|
-
*/ async assignUserToCompany(dto) {
|
|
64
|
+
/** Assign user to a company */ async assignUserToCompany(dto) {
|
|
73
65
|
const repository = await this.getRepository();
|
|
74
66
|
// Check if already exists
|
|
75
67
|
const existing = await repository.findOne({
|
|
@@ -90,9 +82,7 @@ let UserPermissionService = class UserPermissionService {
|
|
|
90
82
|
});
|
|
91
83
|
await repository.save(permission);
|
|
92
84
|
}
|
|
93
|
-
/**
|
|
94
|
-
* Revoke user access to a company
|
|
95
|
-
*/ async revokeCompanyAccess(userId, companyId) {
|
|
85
|
+
/** Revoke user access to a company */ async revokeCompanyAccess(userId, companyId) {
|
|
96
86
|
const repository = await this.getRepository();
|
|
97
87
|
const permission = await repository.findOne({
|
|
98
88
|
where: {
|
|
@@ -106,12 +96,8 @@ let UserPermissionService = class UserPermissionService {
|
|
|
106
96
|
}
|
|
107
97
|
await repository.remove(permission);
|
|
108
98
|
}
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
// ==========================================================================
|
|
112
|
-
/**
|
|
113
|
-
* Find all branches a user has access to
|
|
114
|
-
*/ async findUserBranches(userId) {
|
|
99
|
+
// Branch Permissions
|
|
100
|
+
/** Find all branches a user has access to */ async findUserBranches(userId) {
|
|
115
101
|
const repository = await this.getRepository();
|
|
116
102
|
return repository.find({
|
|
117
103
|
where: {
|
|
@@ -126,9 +112,7 @@ let UserPermissionService = class UserPermissionService {
|
|
|
126
112
|
}
|
|
127
113
|
});
|
|
128
114
|
}
|
|
129
|
-
/**
|
|
130
|
-
* Assign user to a branch
|
|
131
|
-
*/ async assignUserToBranch(dto) {
|
|
115
|
+
/** Assign user to a branch */ async assignUserToBranch(dto) {
|
|
132
116
|
const repository = await this.getRepository();
|
|
133
117
|
// Check if already exists
|
|
134
118
|
const existing = await repository.findOne({
|
|
@@ -149,9 +133,7 @@ let UserPermissionService = class UserPermissionService {
|
|
|
149
133
|
});
|
|
150
134
|
await repository.save(permission);
|
|
151
135
|
}
|
|
152
|
-
/**
|
|
153
|
-
* Revoke user access to a branch
|
|
154
|
-
*/ async revokeBranchAccess(userId, branchId) {
|
|
136
|
+
/** Revoke user access to a branch */ async revokeBranchAccess(userId, branchId) {
|
|
155
137
|
const repository = await this.getRepository();
|
|
156
138
|
const permission = await repository.findOne({
|
|
157
139
|
where: {
|
|
@@ -15,7 +15,9 @@ const _common = require("@nestjs/common");
|
|
|
15
15
|
const _bcrypt = /*#__PURE__*/ _interop_require_wildcard(require("bcrypt"));
|
|
16
16
|
const _authconfigservice = require("./auth-config.service");
|
|
17
17
|
const _authdatasourceprovider = require("./auth-datasource.provider");
|
|
18
|
+
const _config = require("../config");
|
|
18
19
|
const _enums = require("../enums");
|
|
20
|
+
const _userenricherinterface = require("../interfaces/user-enricher.interface");
|
|
19
21
|
const _usercompanypermissionentity = require("../entities/user-company-permission.entity");
|
|
20
22
|
const _userentity = require("../entities/user.entity");
|
|
21
23
|
function _define_property(obj, key, value) {
|
|
@@ -87,22 +89,13 @@ function _ts_param(paramIndex, decorator) {
|
|
|
87
89
|
};
|
|
88
90
|
}
|
|
89
91
|
let UserService = class UserService extends _classes.RequestScopedApiService {
|
|
90
|
-
|
|
91
|
-
* Resolve entity class for this service
|
|
92
|
-
* @returns User entity class
|
|
93
|
-
*/ resolveEntity() {
|
|
92
|
+
resolveEntity() {
|
|
94
93
|
return _userentity.User;
|
|
95
94
|
}
|
|
96
|
-
|
|
97
|
-
* Get DataSource provider for this service
|
|
98
|
-
* @returns AuthDataSourceProvider instance
|
|
99
|
-
*/ getDataSourceProvider() {
|
|
95
|
+
getDataSourceProvider() {
|
|
100
96
|
return this.dataSourceProvider;
|
|
101
97
|
}
|
|
102
|
-
|
|
103
|
-
* Override ensureRepositoryInitialized to also initialize permission repository and dataSource
|
|
104
|
-
* CRITICAL: Also initializes permissionRepository and dataSource for company feature support
|
|
105
|
-
*/ async ensureRepositoryInitialized() {
|
|
98
|
+
async ensureRepositoryInitialized() {
|
|
106
99
|
await super.ensureRepositoryInitialized();
|
|
107
100
|
// Initialize permission repository if company feature is enabled
|
|
108
101
|
const isCompanyFeatureEnabled = this.authConfig.isCompanyFeatureEnabled();
|
|
@@ -116,26 +109,8 @@ let UserService = class UserService extends _classes.RequestScopedApiService {
|
|
|
116
109
|
this.dataSource = await this.getDataSourceForService();
|
|
117
110
|
}
|
|
118
111
|
}
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
// ==========================================================================
|
|
122
|
-
/**
|
|
123
|
-
* Update last login timestamp
|
|
124
|
-
*/ async updateLastLogin(id) {
|
|
125
|
-
// Ensure repository is initialized for custom method
|
|
126
|
-
await this.ensureRepositoryInitialized();
|
|
127
|
-
await this.repository.update(id, {
|
|
128
|
-
lastLoginAt: new Date()
|
|
129
|
-
});
|
|
130
|
-
await this.clearCacheForId([
|
|
131
|
-
{
|
|
132
|
-
id
|
|
133
|
-
}
|
|
134
|
-
]);
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Verify email
|
|
138
|
-
*/ async verifyEmail(id) {
|
|
112
|
+
// Custom Methods
|
|
113
|
+
/** Verify email */ async verifyEmail(id) {
|
|
139
114
|
// Ensure repository is initialized for custom method
|
|
140
115
|
await this.ensureRepositoryInitialized();
|
|
141
116
|
await this.repository.update(id, {
|
|
@@ -148,9 +123,7 @@ let UserService = class UserService extends _classes.RequestScopedApiService {
|
|
|
148
123
|
}
|
|
149
124
|
]);
|
|
150
125
|
}
|
|
151
|
-
/**
|
|
152
|
-
* Verify phone
|
|
153
|
-
*/ async verifyPhone(id) {
|
|
126
|
+
/** Verify phone */ async verifyPhone(id) {
|
|
154
127
|
// Ensure repository is initialized for custom method
|
|
155
128
|
await this.ensureRepositoryInitialized();
|
|
156
129
|
await this.repository.update(id, {
|
|
@@ -163,12 +136,7 @@ let UserService = class UserService extends _classes.RequestScopedApiService {
|
|
|
163
136
|
}
|
|
164
137
|
]);
|
|
165
138
|
}
|
|
166
|
-
/**
|
|
167
|
-
* Update user status
|
|
168
|
-
*
|
|
169
|
-
* When company feature is disabled: Updates User.isActive directly
|
|
170
|
-
* When company feature is enabled: Updates UserCompanyPermission.isActive for the logged user's company
|
|
171
|
-
*/ async updateStatus(id, isActive, user) {
|
|
139
|
+
/** Update user status. When company feature enabled, updates permission isActive for logged user's company. */ async updateStatus(id, isActive, user) {
|
|
172
140
|
// Ensure repository is initialized for custom method
|
|
173
141
|
await this.ensureRepositoryInitialized();
|
|
174
142
|
const isCompanyFeatureEnabled = this.authConfig.isCompanyFeatureEnabled();
|
|
@@ -199,50 +167,43 @@ let UserService = class UserService extends _classes.RequestScopedApiService {
|
|
|
199
167
|
}
|
|
200
168
|
]);
|
|
201
169
|
}
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
// ==========================================================================
|
|
205
|
-
/**
|
|
206
|
-
* Override: Convert DTO to entity (hash password before saving)
|
|
207
|
-
*/ async convertSingleDtoToEntity(dto, user) {
|
|
170
|
+
// ApiService Overrides
|
|
171
|
+
/** Convert DTO to entity (hash password before saving) */ async convertSingleDtoToEntity(dto, user) {
|
|
208
172
|
// Repository initialization is handled automatically by RequestScopedApiService
|
|
209
173
|
const entity = await super.convertSingleDtoToEntity(dto, user);
|
|
210
|
-
// Hash password if provided
|
|
211
|
-
if (dto.password) {
|
|
212
|
-
entity.password = await _bcrypt.hash(dto.password,
|
|
174
|
+
// Hash password if provided (CreateUserDto has password field)
|
|
175
|
+
if ('password' in dto && dto.password) {
|
|
176
|
+
entity.password = await _bcrypt.hash(dto.password, _config.BCRYPT_SALT_ROUNDS);
|
|
213
177
|
}
|
|
214
178
|
return entity;
|
|
215
179
|
}
|
|
216
|
-
/**
|
|
217
|
-
* Override: Convert entity to response DTO (remove password!)
|
|
218
|
-
*
|
|
219
|
-
* When company feature is enabled and raw mode is used, isActive comes from
|
|
220
|
-
* the permission table (user_companyIsActive). Otherwise, it comes from User entity.
|
|
221
|
-
*/ convertEntityToResponseDto(entity, isRaw) {
|
|
180
|
+
/** Convert entity to response DTO (removes password). In raw mode with company feature, isActive comes from permission table. */ convertEntityToResponseDto(entity, isRaw) {
|
|
222
181
|
// When raw mode is used with company feature, isActive comes from computed field
|
|
223
|
-
|
|
182
|
+
// Raw mode uses snake_case column names (due to SnakeNamingStrategy)
|
|
183
|
+
const isActive = isRaw && entity.user_companyIsActive !== undefined ? Boolean(entity.user_companyIsActive) : isRaw ? entity.user_is_active : entity.isActive;
|
|
224
184
|
return {
|
|
225
185
|
id: isRaw ? entity.user_id : entity.id,
|
|
226
186
|
name: isRaw ? entity.user_name : entity.name,
|
|
227
187
|
phone: isRaw ? entity.user_phone : entity.phone,
|
|
228
188
|
email: isRaw ? entity.user_email : entity.email,
|
|
229
|
-
profilePictureId: isRaw ? entity.
|
|
189
|
+
profilePictureId: isRaw ? entity.user_profile_picture_id : entity.profilePictureId,
|
|
230
190
|
isActive,
|
|
231
|
-
emailVerified: isRaw ? entity.
|
|
232
|
-
phoneVerified: isRaw ? entity.
|
|
233
|
-
lastLoginAt: isRaw ? entity.
|
|
234
|
-
additionalFields: isRaw ? entity.
|
|
235
|
-
createdAt: isRaw ? entity.
|
|
236
|
-
updatedAt: isRaw ? entity.
|
|
237
|
-
deletedAt: isRaw ? entity.
|
|
238
|
-
createdById: isRaw ? entity.
|
|
239
|
-
updatedById: isRaw ? entity.
|
|
240
|
-
deletedById: isRaw ? entity.
|
|
191
|
+
emailVerified: isRaw ? entity.user_email_verified : entity.emailVerified,
|
|
192
|
+
phoneVerified: isRaw ? entity.user_phone_verified : entity.phoneVerified,
|
|
193
|
+
lastLoginAt: isRaw ? entity.user_last_login_at : entity.lastLoginAt,
|
|
194
|
+
additionalFields: isRaw ? entity.user_additional_fields : entity.additionalFields,
|
|
195
|
+
createdAt: isRaw ? entity.user_created_at : entity.createdAt,
|
|
196
|
+
updatedAt: isRaw ? entity.user_updated_at : entity.updatedAt,
|
|
197
|
+
deletedAt: isRaw ? entity.user_deleted_at : entity.deletedAt,
|
|
198
|
+
createdById: isRaw ? entity.user_created_by_id : entity.createdById,
|
|
199
|
+
updatedById: isRaw ? entity.user_updated_by_id : entity.updatedById,
|
|
200
|
+
deletedById: isRaw ? entity.user_deleted_by_id : entity.deletedById
|
|
241
201
|
};
|
|
242
202
|
}
|
|
243
|
-
/**
|
|
244
|
-
|
|
245
|
-
|
|
203
|
+
/** Convert entity list to response DTO list */ convertEntityListToResponseListDto(entities, isRaw) {
|
|
204
|
+
return entities.map((entity)=>this.convertEntityToResponseDto(entity, isRaw));
|
|
205
|
+
}
|
|
206
|
+
/** Global search - search by name, email, or phone */ async getGlobalSearchQuery(query, search, _user) {
|
|
246
207
|
query.andWhere('(user.name LIKE :search OR user.email LIKE :search OR user.phone LIKE :search)', {
|
|
247
208
|
search: `%${search}%`
|
|
248
209
|
});
|
|
@@ -251,18 +212,13 @@ let UserService = class UserService extends _classes.RequestScopedApiService {
|
|
|
251
212
|
isRaw: false
|
|
252
213
|
};
|
|
253
214
|
}
|
|
254
|
-
/**
|
|
255
|
-
* Override: getExtraManipulateQuery - Add company permission-based isActive check when company feature enabled
|
|
256
|
-
*
|
|
257
|
-
* Note: When company feature is disabled at bootstrap, this method returns without modifications.
|
|
258
|
-
* All company-related filtering is skipped, and users are returned normally with isActive from User entity.
|
|
259
|
-
*
|
|
260
|
-
* When company feature is enabled:
|
|
261
|
-
* - isActive is computed from UserCompanyPermission table
|
|
262
|
-
* - Returns isRaw: true to use raw query results
|
|
263
|
-
*/ async getExtraManipulateQuery(query, filterAndPaginationDto, user) {
|
|
215
|
+
/** Add company permission-based isActive check when company feature enabled. Returns isRaw: true for computed fields. */ async getExtraManipulateQuery(query, filterAndPaginationDto, user) {
|
|
264
216
|
// Repository initialization is handled automatically by RequestScopedApiService
|
|
265
|
-
|
|
217
|
+
let result = await super.getExtraManipulateQuery(query, filterAndPaginationDto, user);
|
|
218
|
+
// Apply user enricher query modifications if available
|
|
219
|
+
if (this.userEnricher?.enrichListQuery) {
|
|
220
|
+
await this.userEnricher.enrichListQuery(query, user);
|
|
221
|
+
}
|
|
266
222
|
// Only apply company filtering if:
|
|
267
223
|
// 1. Company feature is enabled at bootstrap
|
|
268
224
|
// 2. User has company context
|
|
@@ -315,12 +271,7 @@ let UserService = class UserService extends _classes.RequestScopedApiService {
|
|
|
315
271
|
}
|
|
316
272
|
return result;
|
|
317
273
|
}
|
|
318
|
-
/**
|
|
319
|
-
* Override: insert - Create company permission when company feature is enabled
|
|
320
|
-
*
|
|
321
|
-
* Note: When company feature is disabled at bootstrap, this method falls back to parent implementation.
|
|
322
|
-
* No company permissions are created, and user is inserted normally.
|
|
323
|
-
*/ async insert(dto, user) {
|
|
274
|
+
/** Insert user. Creates company permission when company feature is enabled. */ async insert(dto, user) {
|
|
324
275
|
// Repository initialization is handled automatically by RequestScopedApiService
|
|
325
276
|
// Check if company feature is enabled
|
|
326
277
|
const isCompanyFeatureEnabled = this.authConfig.isCompanyFeatureEnabled();
|
|
@@ -388,17 +339,139 @@ let UserService = class UserService extends _classes.RequestScopedApiService {
|
|
|
388
339
|
}
|
|
389
340
|
}
|
|
390
341
|
/**
|
|
391
|
-
*
|
|
392
|
-
|
|
393
|
-
|
|
342
|
+
* Get user profile with extras from enricher
|
|
343
|
+
* Returns base user data plus any extra data provided by enricher (roles, permissions, etc.)
|
|
344
|
+
*/ async getProfileWithExtras(userId, user) {
|
|
345
|
+
await this.ensureRepositoryInitialized();
|
|
346
|
+
const userEntity = await this.repository.findOne({
|
|
347
|
+
where: {
|
|
348
|
+
id: userId
|
|
349
|
+
}
|
|
350
|
+
});
|
|
351
|
+
if (!userEntity) {
|
|
352
|
+
throw new _common.NotFoundException('User not found');
|
|
353
|
+
}
|
|
354
|
+
const userData = this.convertEntityToResponseDto(userEntity, false);
|
|
355
|
+
// Get extras from enricher if available
|
|
356
|
+
let extras;
|
|
357
|
+
if (this.userEnricher?.getProfileExtras) {
|
|
358
|
+
extras = await this.userEnricher.getProfileExtras(userId, user);
|
|
359
|
+
}
|
|
360
|
+
return {
|
|
361
|
+
user: userData,
|
|
362
|
+
extras
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
/**
|
|
366
|
+
* Get enriched user list
|
|
367
|
+
* Applies enricher transformations after query execution
|
|
368
|
+
*/ async getEnrichedList(users, user) {
|
|
369
|
+
if (this.userEnricher?.enrichListItems) {
|
|
370
|
+
return this.userEnricher.enrichListItems(users, user);
|
|
371
|
+
}
|
|
372
|
+
return users;
|
|
373
|
+
}
|
|
374
|
+
// ============================================
|
|
375
|
+
// Multi-Step Profile Methods (via Enricher)
|
|
376
|
+
// ============================================
|
|
377
|
+
/**
|
|
378
|
+
* Get profile section definitions from enricher
|
|
379
|
+
*/ async getProfileSections() {
|
|
380
|
+
if (this.userEnricher?.getProfileSections) {
|
|
381
|
+
return this.userEnricher.getProfileSections();
|
|
382
|
+
}
|
|
383
|
+
return [];
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Get specific profile section data
|
|
387
|
+
*/ async getProfileSectionData(userId, sectionId, user) {
|
|
388
|
+
await this.ensureRepositoryInitialized();
|
|
389
|
+
// Verify user exists
|
|
390
|
+
const userEntity = await this.repository.findOne({
|
|
391
|
+
where: {
|
|
392
|
+
id: userId
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
if (!userEntity) {
|
|
396
|
+
throw new _common.NotFoundException('User not found');
|
|
397
|
+
}
|
|
398
|
+
if (this.userEnricher?.getProfileSectionData) {
|
|
399
|
+
return this.userEnricher.getProfileSectionData(userId, sectionId, user);
|
|
400
|
+
}
|
|
401
|
+
return null;
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Update specific profile section
|
|
405
|
+
*/ async updateProfileSection(userId, sectionId, data, user) {
|
|
406
|
+
await this.ensureRepositoryInitialized();
|
|
407
|
+
if (!this.dataSource) {
|
|
408
|
+
throw new _common.BadRequestException('DataSource not available');
|
|
409
|
+
}
|
|
410
|
+
// Verify user exists
|
|
411
|
+
const userEntity = await this.repository.findOne({
|
|
412
|
+
where: {
|
|
413
|
+
id: userId
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
if (!userEntity) {
|
|
417
|
+
throw new _common.NotFoundException('User not found');
|
|
418
|
+
}
|
|
419
|
+
if (!this.userEnricher?.updateProfileSection) {
|
|
420
|
+
throw new _common.BadRequestException('Profile sections not supported');
|
|
421
|
+
}
|
|
422
|
+
const queryRunner = this.dataSource.createQueryRunner();
|
|
423
|
+
await queryRunner.connect();
|
|
424
|
+
await queryRunner.startTransaction();
|
|
425
|
+
try {
|
|
426
|
+
await this.userEnricher.updateProfileSection(userId, sectionId, data, queryRunner);
|
|
427
|
+
await queryRunner.commitTransaction();
|
|
428
|
+
await this.clearCacheForId([
|
|
429
|
+
userEntity
|
|
430
|
+
]);
|
|
431
|
+
} catch (error) {
|
|
432
|
+
await queryRunner.rollbackTransaction();
|
|
433
|
+
_utils.ErrorHandler.logError(this.logger, error, 'updateProfileSection', {
|
|
434
|
+
entity: this.entityName,
|
|
435
|
+
sectionId
|
|
436
|
+
});
|
|
437
|
+
_utils.ErrorHandler.rethrowError(error);
|
|
438
|
+
} finally{
|
|
439
|
+
await queryRunner.release();
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* Get profile completion percentage
|
|
444
|
+
*/ async getProfileCompletion(userId, user) {
|
|
445
|
+
await this.ensureRepositoryInitialized();
|
|
446
|
+
// Verify user exists
|
|
447
|
+
const userEntity = await this.repository.findOne({
|
|
448
|
+
where: {
|
|
449
|
+
id: userId
|
|
450
|
+
}
|
|
451
|
+
});
|
|
452
|
+
if (!userEntity) {
|
|
453
|
+
throw new _common.NotFoundException('User not found');
|
|
454
|
+
}
|
|
455
|
+
if (this.userEnricher?.calculateProfileCompletion) {
|
|
456
|
+
return this.userEnricher.calculateProfileCompletion(userId, user);
|
|
457
|
+
}
|
|
458
|
+
return 100; // Default to 100% if no enricher
|
|
459
|
+
}
|
|
460
|
+
/** Update user profile with password handling and extras */ async profile(dto, user) {
|
|
461
|
+
// Ensure repository and dataSource are initialized
|
|
462
|
+
await this.ensureRepositoryInitialized();
|
|
394
463
|
if (!this.dataSource) {
|
|
395
464
|
throw new _common.BadRequestException('DataSource not available');
|
|
396
465
|
}
|
|
466
|
+
const userId = dto.id ?? user.id;
|
|
467
|
+
// Validate extras if enricher provides validation
|
|
468
|
+
if (dto.extras && this.userEnricher?.validateProfileExtras) {
|
|
469
|
+
await this.userEnricher.validateProfileExtras(userId, dto.extras, user);
|
|
470
|
+
}
|
|
397
471
|
const queryRunner = this.dataSource.createQueryRunner();
|
|
398
472
|
await queryRunner.connect();
|
|
399
473
|
await queryRunner.startTransaction();
|
|
400
474
|
try {
|
|
401
|
-
const userId = dto.id ?? user.id;
|
|
402
475
|
const findUser = await queryRunner.manager.findOne(this.repository.target, {
|
|
403
476
|
where: {
|
|
404
477
|
id: userId
|
|
@@ -417,10 +490,14 @@ let UserService = class UserService extends _classes.RequestScopedApiService {
|
|
|
417
490
|
const salt = await _bcrypt.genSalt();
|
|
418
491
|
findUser.password = await _bcrypt.hash(dto.password, salt);
|
|
419
492
|
}
|
|
420
|
-
// Merge other fields (excluding password)
|
|
421
|
-
const { password, ...restDto } = dto;
|
|
493
|
+
// Merge other fields (excluding password and extras)
|
|
494
|
+
const { password, extras, ...restDto } = dto;
|
|
422
495
|
Object.assign(findUser, restDto);
|
|
423
496
|
await queryRunner.manager.save(this.repository.target, findUser);
|
|
497
|
+
// Handle extras update via enricher
|
|
498
|
+
if (extras && this.userEnricher?.updateProfileExtras) {
|
|
499
|
+
await this.userEnricher.updateProfileExtras(userId, extras, queryRunner);
|
|
500
|
+
}
|
|
424
501
|
await queryRunner.commitTransaction();
|
|
425
502
|
await this.clearCacheForId([
|
|
426
503
|
findUser
|
|
@@ -436,10 +513,8 @@ let UserService = class UserService extends _classes.RequestScopedApiService {
|
|
|
436
513
|
await queryRunner.release();
|
|
437
514
|
}
|
|
438
515
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
// Repository will be set asynchronously by RequestScopedApiService
|
|
442
|
-
super('user', null, cacheManager, utilsService, UserService.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), _define_property(this, "permissionRepository", void 0), _define_property(this, "dataSource", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider, this.authConfig = authConfig, this.logger = new _common.Logger(UserService.name);
|
|
516
|
+
constructor(cacheManager, utilsService, dataSourceProvider, authConfig, userEnricher){
|
|
517
|
+
super('user', null, cacheManager, utilsService, UserService.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, "userEnricher", void 0), _define_property(this, "permissionRepository", void 0), _define_property(this, "dataSource", void 0), this.cacheManager = cacheManager, this.utilsService = utilsService, this.dataSourceProvider = dataSourceProvider, this.authConfig = authConfig, this.userEnricher = userEnricher;
|
|
443
518
|
}
|
|
444
519
|
};
|
|
445
520
|
UserService = _ts_decorate([
|
|
@@ -450,11 +525,14 @@ UserService = _ts_decorate([
|
|
|
450
525
|
_ts_param(1, (0, _common.Inject)(_modules.UtilsService)),
|
|
451
526
|
_ts_param(2, (0, _common.Inject)(_authdatasourceprovider.AuthDataSourceProvider)),
|
|
452
527
|
_ts_param(3, (0, _common.Inject)(_authconfigservice.AuthConfigService)),
|
|
528
|
+
_ts_param(4, (0, _common.Optional)()),
|
|
529
|
+
_ts_param(4, (0, _common.Inject)(_config.USER_ENRICHER)),
|
|
453
530
|
_ts_metadata("design:type", Function),
|
|
454
531
|
_ts_metadata("design:paramtypes", [
|
|
455
532
|
typeof _classes.HybridCache === "undefined" ? Object : _classes.HybridCache,
|
|
456
533
|
typeof _modules.UtilsService === "undefined" ? Object : _modules.UtilsService,
|
|
457
534
|
typeof _authdatasourceprovider.AuthDataSourceProvider === "undefined" ? Object : _authdatasourceprovider.AuthDataSourceProvider,
|
|
458
|
-
typeof _authconfigservice.AuthConfigService === "undefined" ? Object : _authconfigservice.AuthConfigService
|
|
535
|
+
typeof _authconfigservice.AuthConfigService === "undefined" ? Object : _authconfigservice.AuthConfigService,
|
|
536
|
+
typeof _userenricherinterface.IUserEnricher === "undefined" ? Object : _userenricherinterface.IUserEnricher
|
|
459
537
|
])
|
|
460
538
|
], UserService);
|