@flusys/nestjs-shared 0.1.0-alpha.1

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 (82) hide show
  1. package/cjs/classes-index.js +46 -0
  2. package/cjs/constants-index.js +1 -0
  3. package/cjs/decorators-index.js +1 -0
  4. package/cjs/dtos-index.js +1 -0
  5. package/cjs/entities-index.js +1 -0
  6. package/cjs/exceptions-index.js +1 -0
  7. package/cjs/guards-index.js +22 -0
  8. package/cjs/index.js +151 -0
  9. package/cjs/interceptors-index.js +106 -0
  10. package/cjs/interfaces-index.js +1 -0
  11. package/cjs/middlewares-index.js +22 -0
  12. package/cjs/modules-index.js +106 -0
  13. package/cjs/utils-index.js +1 -0
  14. package/classes/api-controller.class.d.ts +30 -0
  15. package/classes/api-service.class.d.ts +72 -0
  16. package/classes/hybrid-cache.class.d.ts +10 -0
  17. package/classes/index.d.ts +6 -0
  18. package/classes/request-scoped-api.service.d.ts +13 -0
  19. package/classes/winston-logger-adapter.class.d.ts +20 -0
  20. package/classes/winston.logger.class.d.ts +2 -0
  21. package/constants/index.d.ts +10 -0
  22. package/decorators/api-response.decorator.d.ts +3 -0
  23. package/decorators/current-user.decorator.d.ts +1 -0
  24. package/decorators/index.d.ts +4 -0
  25. package/decorators/public.decorator.d.ts +1 -0
  26. package/decorators/require-permission.decorator.d.ts +4 -0
  27. package/dtos/delete.dto.d.ts +4 -0
  28. package/dtos/filter-and-pagination.dto.d.ts +13 -0
  29. package/dtos/identity-response.dto.d.ts +9 -0
  30. package/dtos/index.d.ts +5 -0
  31. package/dtos/pagination.dto.d.ts +4 -0
  32. package/dtos/response-payload.dto.d.ts +63 -0
  33. package/entities/identity.d.ts +9 -0
  34. package/entities/index.d.ts +2 -0
  35. package/entities/user-root.d.ts +19 -0
  36. package/exceptions/index.d.ts +1 -0
  37. package/exceptions/permission.exception.d.ts +10 -0
  38. package/fesm/classes-index.js +46 -0
  39. package/fesm/constants-index.js +1 -0
  40. package/fesm/decorators-index.js +1 -0
  41. package/fesm/dtos-index.js +1 -0
  42. package/fesm/entities-index.js +1 -0
  43. package/fesm/exceptions-index.js +1 -0
  44. package/fesm/guards-index.js +22 -0
  45. package/fesm/index.js +151 -0
  46. package/fesm/interceptors-index.js +106 -0
  47. package/fesm/interfaces-index.js +0 -0
  48. package/fesm/middlewares-index.js +22 -0
  49. package/fesm/modules-index.js +106 -0
  50. package/fesm/utils-index.js +1 -0
  51. package/guards/index.d.ts +2 -0
  52. package/guards/jwt-auth.guard.d.ts +10 -0
  53. package/guards/permission.guard.d.ts +18 -0
  54. package/index.d.ts +12 -0
  55. package/interceptors/delete-empty-id-from-body.interceptor.d.ts +5 -0
  56. package/interceptors/idempotency.interceptor.d.ts +10 -0
  57. package/interceptors/index.d.ts +8 -0
  58. package/interceptors/query-performance.interceptor.d.ts +8 -0
  59. package/interceptors/response-meta.interceptor.d.ts +5 -0
  60. package/interceptors/set-create-by-on-body.interceptor.d.ts +5 -0
  61. package/interceptors/set-delete-by-on-body.interceptor.d.ts +5 -0
  62. package/interceptors/set-update-by-on-body.interceptor.d.ts +5 -0
  63. package/interceptors/slug.interceptor.d.ts +7 -0
  64. package/interfaces/api.interface.d.ts +14 -0
  65. package/interfaces/base-query.interface.d.ts +7 -0
  66. package/interfaces/identity.interface.d.ts +9 -0
  67. package/interfaces/index.d.ts +6 -0
  68. package/interfaces/logged-user-info.interface.d.ts +11 -0
  69. package/interfaces/logger.interface.d.ts +7 -0
  70. package/interfaces/permission.interface.d.ts +17 -0
  71. package/middlewares/index.d.ts +1 -0
  72. package/middlewares/logger.middleware.d.ts +23 -0
  73. package/modules/cache/cache.module.d.ts +4 -0
  74. package/modules/datasource/datasource.module.d.ts +18 -0
  75. package/modules/datasource/index.d.ts +2 -0
  76. package/modules/datasource/multi-tenant-datasource.service.d.ts +45 -0
  77. package/modules/index.d.ts +4 -0
  78. package/modules/utils/utils.module.d.ts +2 -0
  79. package/modules/utils/utils.service.d.ts +21 -0
  80. package/package.json +100 -0
  81. package/utils/error-handler.util.d.ts +24 -0
  82. package/utils/index.d.ts +1 -0
@@ -0,0 +1,106 @@
1
+ "use strict";var __create=Object.create;var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __getProtoOf=Object.getPrototypeOf;var __hasOwnProp=Object.prototype.hasOwnProperty;var __defNormalProp=(obj,key,value)=>key in obj?__defProp(obj,key,{enumerable:true,configurable:true,writable:true,value}):obj[key]=value;var __name=(target,value)=>__defProp(target,"name",{value,configurable:true});var __commonJS=(cb,mod)=>function __require(){return mod||(0,cb[__getOwnPropNames(cb)[0]])((mod={exports:{}}).exports,mod),mod.exports};var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:true})};var __copyProps=(to,from,except,desc)=>{if(from&&typeof from==="object"||typeof from==="function"){for(let key of __getOwnPropNames(from))if(!__hasOwnProp.call(to,key)&&key!==except)__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable})}return to};var __toESM=(mod,isNodeMode,target)=>(target=mod!=null?__create(__getProtoOf(mod)):{},__copyProps(isNodeMode||!mod||!mod.__esModule?__defProp(target,"default",{value:mod,enumerable:true}):target,mod));var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:true}),mod);var __decorateClass=(decorators,target,key,kind)=>{var result=kind>1?void 0:kind?__getOwnPropDesc(target,key):target;for(var i=decorators.length-1,decorator;i>=0;i--)if(decorator=decorators[i])result=(kind?decorator(target,key,result):decorator(result))||result;if(kind&&result)__defProp(target,key,result);return result};var __decorateParam=(index,decorator)=>(target,key)=>decorator(target,key,index);var __publicField=(obj,key,value)=>__defNormalProp(obj,typeof key!=="symbol"?key+"":key,value);var require_snake_naming_strategy=__commonJS({"node_modules/typeorm-naming-strategies/snake-naming.strategy.js"(exports2){"use strict";Object.defineProperty(exports2,"__esModule",{value:true});exports2.SnakeNamingStrategy=void 0;var typeorm_1=require("typeorm");var StringUtils_1=require("typeorm/util/StringUtils");var SnakeNamingStrategy2=class extends typeorm_1.DefaultNamingStrategy{static{__name(this,"SnakeNamingStrategy")}tableName(className,customName){return customName?customName:StringUtils_1.snakeCase(className)}columnName(propertyName,customName,embeddedPrefixes){return StringUtils_1.snakeCase(embeddedPrefixes.concat("").join("_"))+(customName?customName:StringUtils_1.snakeCase(propertyName))}relationName(propertyName){return StringUtils_1.snakeCase(propertyName)}joinColumnName(relationName,referencedColumnName){return StringUtils_1.snakeCase(relationName+"_"+referencedColumnName)}joinTableName(firstTableName,secondTableName,firstPropertyName,secondPropertyName){return StringUtils_1.snakeCase(firstTableName+"_"+firstPropertyName.replace(/\./gi,"_")+"_"+secondTableName)}joinTableColumnName(tableName,propertyName,columnName){return StringUtils_1.snakeCase(tableName+"_"+(columnName?columnName:propertyName))}classTableInheritanceParentColumnName(parentTableName,parentTableIdPropertyName){return StringUtils_1.snakeCase(parentTableName+"_"+parentTableIdPropertyName)}eagerJoinRelationAlias(alias,propertyPath){return alias+"__"+propertyPath.replace(".","_")}};exports2.SnakeNamingStrategy=SnakeNamingStrategy2}});var require_typeorm_naming_strategies=__commonJS({"node_modules/typeorm-naming-strategies/index.js"(exports2){"use strict";Object.defineProperty(exports2,"__esModule",{value:true});exports2.SnakeNamingStrategy=void 0;var snake_naming_strategy_1=require_snake_naming_strategy();Object.defineProperty(exports2,"SnakeNamingStrategy",{enumerable:true,get:__name(function(){return snake_naming_strategy_1.SnakeNamingStrategy},"get")})}});var index_exports={};__export(index_exports,{CacheModule:()=>CacheModule,DataSourceModule:()=>DataSourceModule,MultiTenantDataSourceService:()=>MultiTenantDataSourceService,UtilsModule:()=>UtilsModule,UtilsService:()=>UtilsService});module.exports=__toCommonJS(index_exports);var import_classes=require("@flusys/nestjs-shared/classes");var import_constants=require("@flusys/nestjs-shared/constants");var import_common=require("@nestjs/common");var CacheModule=class{static forRoot(isGlobal=true,memoryTtl=6e4,memorySize=5e3){return{module:CacheModule,global:isGlobal,providers:[{provide:import_constants.CACHE_INSTANCE,useFactory:__name(()=>new import_classes.HybridCache(memoryTtl,memorySize),"useFactory")}],exports:[import_constants.CACHE_INSTANCE]}}};__name(CacheModule,"CacheModule");CacheModule=__decorateClass([(0,import_common.Module)({})],CacheModule);var import_nestjs_core2=require("@flusys/nestjs-core");var import_common3=require("@nestjs/common");var import_nestjs_core=require("@flusys/nestjs-core");var import_common2=require("@nestjs/common");var import_core=require("@nestjs/core");var import_typeorm=require("typeorm");var import_typeorm_naming_strategies=__toESM(require_typeorm_naming_strategies());var MultiTenantDataSourceService=class{constructor(options,request){this.options=options;this.request=request;this.initializeFromOptions()}logger=new import_common2.Logger(this.constructor.name);tenantHeader=import_nestjs_core.DEFAULT_TENANT_HEADER;initializeFromOptions(){if(!this.options)return;if(!MultiTenantDataSourceService.initialized){this.registerTenants();MultiTenantDataSourceService.initialized=true}}registerTenants(){this.options?.tenants?.forEach(tenant=>{MultiTenantDataSourceService.tenantsRegistry.set(tenant.id,tenant)})}setTenantHeader(header){this.tenantHeader=header}getDatabaseMode(){return this.options?.bootstrapAppConfig?.databaseMode??"single"}isMultiTenant(){return this.getDatabaseMode()==="multi-tenant"}getCurrentTenantId(){if(!this.request)return null;const tenantId=this.request.headers[this.tenantHeader];if(!tenantId)return null;if(!/^[a-zA-Z0-9_-]+$/.test(tenantId)){throw new import_common2.BadRequestException("Invalid tenant ID format. Only alphanumeric characters, hyphens, and underscores are allowed.")}return tenantId}getCurrentTenant(){const tenantId=this.getCurrentTenantId();return tenantId?this.getTenant(tenantId):null}getTenant(tenantId){return MultiTenantDataSourceService.tenantsRegistry.get(tenantId)??null}getAllTenants(){return Array.from(MultiTenantDataSourceService.tenantsRegistry.values())}getActiveTenants(){return this.getAllTenants()}async getDataSource(){return this.isMultiTenant()?this.getTenantDataSource():this.getSingleDataSource()}async getDataSourceForTenant(tenantId){const tenant=this.getTenant(tenantId);if(!tenant){throw new Error(`Tenant '${tenantId}' not found`)}return this.getOrCreateTenantConnection(tenant)}setDataSource(dataSource){MultiTenantDataSourceService.singleDataSource=dataSource}async getRepository(entity){const dataSource=await this.getDataSource();return dataSource.getRepository(entity)}async getRepositoryForTenant(entity,tenantId){const dataSource=await this.getDataSourceForTenant(tenantId);return dataSource.getRepository(entity)}async withTenant(tenantId,callback){const dataSource=await this.getDataSourceForTenant(tenantId);return callback(dataSource)}async forAllTenants(callback){const results=new Map;for(const tenant of this.getActiveTenants()){try{const dataSource=await this.getOrCreateTenantConnection(tenant);results.set(tenant.id,await callback(tenant,dataSource))}catch(error){this.logger.error(`Error processing tenant ${tenant.id}: ${error}`)}}return results}registerTenant(tenant){MultiTenantDataSourceService.tenantsRegistry.set(tenant.id,tenant)}async removeTenant(tenantId){await this.closeTenantConnection(tenantId);MultiTenantDataSourceService.tenantsRegistry.delete(tenantId)}async closeTenantConnection(tenantId){const connection=MultiTenantDataSourceService.tenantConnections.get(tenantId);if(connection?.isInitialized){await connection.destroy();MultiTenantDataSourceService.tenantConnections.delete(tenantId);this.logger.log(`Closed connection for tenant: ${tenantId}`)}}async onModuleDestroy(){for(const[tenantId]of MultiTenantDataSourceService.tenantConnections){await this.closeTenantConnection(tenantId)}}static reset(){MultiTenantDataSourceService.initialized=false;MultiTenantDataSourceService.singleDataSource=null;MultiTenantDataSourceService.singleConnectionLock=null;MultiTenantDataSourceService.tenantConnections.clear();MultiTenantDataSourceService.tenantsRegistry.clear();MultiTenantDataSourceService.connectionLocks.clear()}async getSingleDataSource(){if(MultiTenantDataSourceService.singleDataSource?.isInitialized){return MultiTenantDataSourceService.singleDataSource}if(MultiTenantDataSourceService.singleConnectionLock){return MultiTenantDataSourceService.singleConnectionLock}const config=this.getDefaultDatabaseConfig();if(!config){throw new Error("No database config available. Provide defaultDatabaseConfig or tenantDefaultDatabaseConfig.")}const connectionPromise=this.createDataSourceFromConfig(config);MultiTenantDataSourceService.singleConnectionLock=connectionPromise;try{const dataSource=await connectionPromise;MultiTenantDataSourceService.singleDataSource=dataSource;return dataSource}finally{MultiTenantDataSourceService.singleConnectionLock=null}}async getTenantDataSource(){const tenant=this.getCurrentTenant();if(!tenant){throw new Error(`Tenant not found. Ensure '${this.tenantHeader}' header is set.`)}return this.getOrCreateTenantConnection(tenant)}async getOrCreateTenantConnection(tenant){const existing=MultiTenantDataSourceService.tenantConnections.get(tenant.id);if(existing?.isInitialized){return existing}const pendingConnection=MultiTenantDataSourceService.connectionLocks.get(tenant.id);if(pendingConnection){return pendingConnection}const config=this.buildTenantDatabaseConfig(tenant);const connectionPromise=this.createDataSourceFromConfig(config);MultiTenantDataSourceService.connectionLocks.set(tenant.id,connectionPromise);try{const dataSource=await connectionPromise;MultiTenantDataSourceService.tenantConnections.set(tenant.id,dataSource);this.logger.log(`Created connection for tenant: ${tenant.id}`);return dataSource}finally{MultiTenantDataSourceService.connectionLocks.delete(tenant.id)}}getDefaultDatabaseConfig(){return this.options?.defaultDatabaseConfig??this.options?.tenantDefaultDatabaseConfig}buildTenantDatabaseConfig(tenant){const defaultConfig=this.getDefaultDatabaseConfig();if(!defaultConfig){throw new Error("No default database config for multi-tenant mode.")}return{type:defaultConfig.type,host:tenant.host??defaultConfig.host,port:tenant.port??defaultConfig.port,username:tenant.username??defaultConfig.username,password:tenant.password??defaultConfig.password,database:tenant.database}}async createDataSourceFromConfig(config,entities=[]){const dataSource=new import_typeorm.DataSource({type:config.type,host:config.host,port:config.port,username:config.username,password:config.password,database:config.database,entities,synchronize:false,namingStrategy:new import_typeorm_naming_strategies.SnakeNamingStrategy});if(!dataSource.isInitialized){await dataSource.initialize()}return dataSource}};__name(MultiTenantDataSourceService,"MultiTenantDataSourceService");__publicField(MultiTenantDataSourceService,"tenantConnections",new Map);__publicField(MultiTenantDataSourceService,"singleDataSource",null);__publicField(MultiTenantDataSourceService,"tenantsRegistry",new Map);__publicField(MultiTenantDataSourceService,"initialized",false);__publicField(MultiTenantDataSourceService,"connectionLocks",new Map);__publicField(MultiTenantDataSourceService,"singleConnectionLock",null);MultiTenantDataSourceService=__decorateClass([(0,import_common2.Injectable)({scope:import_common2.Scope.REQUEST}),__decorateParam(0,(0,import_common2.Optional)()),__decorateParam(0,(0,import_common2.Inject)(import_nestjs_core.MODULE_OPTIONS)),__decorateParam(1,(0,import_common2.Optional)()),__decorateParam(1,(0,import_common2.Inject)(import_core.REQUEST))],MultiTenantDataSourceService);var DataSourceModule=class{static forRoot(options){const{global=false,...moduleOptions}=options;return{module:DataSourceModule,global,providers:[{provide:import_nestjs_core2.MODULE_OPTIONS,useValue:moduleOptions},MultiTenantDataSourceService],exports:[import_nestjs_core2.MODULE_OPTIONS,MultiTenantDataSourceService]}}static forRootAsync(asyncOptions){const{global=false,imports=[]}=asyncOptions;return{module:DataSourceModule,global,imports,providers:[...this.createAsyncProviders(asyncOptions),MultiTenantDataSourceService],exports:[import_nestjs_core2.MODULE_OPTIONS,MultiTenantDataSourceService]}}static forFeature(){return{module:DataSourceModule,providers:[MultiTenantDataSourceService],exports:[MultiTenantDataSourceService]}}static createAsyncProviders(options){if(options.useFactory){return[{provide:import_nestjs_core2.MODULE_OPTIONS,useFactory:options.useFactory,inject:options.inject||[]}]}if(options.useClass){return[{provide:options.useClass,useClass:options.useClass},{provide:import_nestjs_core2.MODULE_OPTIONS,useFactory:__name(async factory=>factory.createOptions(),"useFactory"),inject:[options.useClass]}]}if(options.useExisting){return[{provide:import_nestjs_core2.MODULE_OPTIONS,useFactory:__name(async factory=>factory.createOptions(),"useFactory"),inject:[options.useExisting]}]}return[{provide:import_nestjs_core2.MODULE_OPTIONS,useValue:{}}]}};__name(DataSourceModule,"DataSourceModule");DataSourceModule=__decorateClass([(0,import_common3.Module)({})],DataSourceModule);var import_common5=require("@nestjs/common");var import_common4=require("@nestjs/common");var import_class_validator=require("class-validator");var UtilsService=class{constructor(){}getCacheKey(entityName,params,entityId,tenantId){const tenantPrefix=tenantId?`tenant_${tenantId}_`:"";if(entityId)return`${tenantPrefix}entity_${entityName}_id_${entityId}${params?"_select_"+JSON.stringify(params):""}`;return`${tenantPrefix}entity_${entityName}_all_${JSON.stringify(params)}`}async trackCacheKey(cacheKey,entityName,cacheManager,entityId,tenantId){const tenantPrefix=tenantId?`tenant_${tenantId}_`:"";try{if(entityId){const trackingKey=`${tenantPrefix}entity_${entityName}_id_${entityId}_keys`;const idKeys=await cacheManager.get(trackingKey)||[];if(!idKeys.includes(cacheKey))idKeys.push(cacheKey);await cacheManager.set(trackingKey,idKeys)}else{const trackingKey=`${tenantPrefix}entity_${entityName}_keys`;const allKeys=await cacheManager.get(trackingKey)||[];if(!allKeys.includes(cacheKey))allKeys.push(cacheKey);await cacheManager.set(trackingKey,allKeys)}}catch(error){console.error(`Cache tracking failed for ${entityName}:`,error)}}async clearCache(entityName,cacheManager,entityId,tenantId){const tenantPrefix=tenantId?`tenant_${tenantId}_`:"";try{if(entityId){const trackingKey=`${tenantPrefix}entity_${entityName}_id_${entityId}_keys`;const idKeys=await cacheManager.get(trackingKey)||[];await Promise.allSettled(idKeys.map(key=>cacheManager.del(key)));await cacheManager.del(trackingKey)}else{const trackingKey=`${tenantPrefix}entity_${entityName}_keys`;const keySet=await cacheManager.get(trackingKey)||[];await Promise.allSettled(keySet.map(key=>cacheManager.del(key)));await cacheManager.del(trackingKey)}}catch(error){console.error(`Cache invalidation failed for ${entityName}:`,error)}}checkPhoneOrEmail(value){if((0,import_class_validator.isEmail)(value)){return{value,type:"email"}}const phoneMatch=value.match(/^((\+880)|0)?(13|15|16|17|18|19)\d{8}$/);if(phoneMatch){let phone=phoneMatch[0];if(!phone.startsWith("+88")){phone="+88"+phone}return{value:phone,type:"phone"}}return{value:null,type:null}}transformToSlug(value,salt){const slug=value?.trim().replace(/[^A-Z0-9]+/gi,"-").toLowerCase();return salt?`${slug}-${this.getRandomInt(1,100)}`:slug}getRandomInt(min,max){return Math.floor(Math.random()*(max-min+1))+min}generateRandomId(length){const characters="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";let result="";for(let i=0;i<length;i++){result+=characters.charAt(Math.floor(Math.random()*characters.length))}return result}getRandomOtpCode(){return Math.floor(Math.random()*(9999-1e3+1))+1e3}extractColumnNameFromError(detail){const match=detail.match(/\((.*?)\)=\((.*?)\)/);return{columnName:match?match[1]:"unknown",value:match?match[2]:"unknown"}}getOtpEmailFormat(otp,userName){return`
2
+ <!DOCTYPE html>
3
+ <html lang="en">
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
7
+ <title>Your OTP Code</title>
8
+ <style>
9
+ body {
10
+ font-family: Arial, sans-serif;
11
+ background-color: #f4f4f7;
12
+ margin: 0;
13
+ padding: 0;
14
+ }
15
+ .email-container {
16
+ max-width: 500px;
17
+ margin: 40px auto;
18
+ background-color: #ffffff;
19
+ padding: 30px;
20
+ border-radius: 8px;
21
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
22
+ text-align: center;
23
+ }
24
+ h1 {
25
+ color: #333;
26
+ }
27
+ p {
28
+ font-size: 16px;
29
+ color: #555;
30
+ }
31
+ .otp-box {
32
+ display: inline-block;
33
+ margin: 20px 0;
34
+ padding: 14px 28px;
35
+ font-size: 24px;
36
+ letter-spacing: 6px;
37
+ background-color: #007BFF;
38
+ color: white;
39
+ border-radius: 8px;
40
+ font-weight: bold;
41
+ }
42
+ </style>
43
+ </head>
44
+ <body>
45
+ <div class="email-container">
46
+ <p>Hi ${userName||"Sir/Madam"},</p>
47
+ <p>Use the code below to verify your identity:</p>
48
+ <div class="otp-box">${otp}</div>
49
+ <p>This OTP is valid for a limited time. Do not share it with anyone.</p>
50
+ <p>If you didn\u2019t request this, please ignore this email.</p>
51
+ </div>
52
+ </body>
53
+ </html>
54
+ `}getResetPasswordEmailFormat(resetLink,userName){return`
55
+ <!DOCTYPE html>
56
+ <html lang="en">
57
+ <head>
58
+ <meta charset="UTF-8" />
59
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
60
+ <title>Reset Your Password</title>
61
+ <style>
62
+ body {
63
+ font-family: Arial, sans-serif;
64
+ background-color: #f4f4f7;
65
+ margin: 0;
66
+ padding: 0;
67
+ }
68
+ .email-container {
69
+ max-width: 500px;
70
+ margin: 40px auto;
71
+ background-color: #ffffff;
72
+ padding: 30px;
73
+ border-radius: 8px;
74
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
75
+ text-align: center;
76
+ }
77
+ h1 {
78
+ color: #333;
79
+ }
80
+ p {
81
+ font-size: 16px;
82
+ color: #555;
83
+ }
84
+ .reset-button {
85
+ display: inline-block;
86
+ margin: 20px 0;
87
+ padding: 14px 28px;
88
+ font-size: 16px;
89
+ background-color: #007BFF;
90
+ color: #ffffff !important;
91
+ text-decoration: none;
92
+ border-radius: 6px;
93
+ font-weight: bold;
94
+ }
95
+ </style>
96
+ </head>
97
+ <body>
98
+ <div class="email-container">
99
+ <p>Hi ${userName||"Sir/Madam"},</p>
100
+ <p>We received a request to reset your password. Click the button below to reset it:</p>
101
+ <a href="${resetLink}" class="reset-button" target="_blank">Reset Password</a>
102
+ <p>If you didn\u2019t request a password reset, you can safely ignore this email.</p>
103
+ </div>
104
+ </body>
105
+ </html>
106
+ `}};__name(UtilsService,"UtilsService");UtilsService=__decorateClass([(0,import_common4.Injectable)()],UtilsService);var UtilsModule=class{};__name(UtilsModule,"UtilsModule");UtilsModule=__decorateClass([(0,import_common5.Global)(),(0,import_common5.Module)({imports:[],providers:[UtilsService],exports:[UtilsService]})],UtilsModule);0&&(module.exports={CacheModule,DataSourceModule,MultiTenantDataSourceService,UtilsModule,UtilsService});
@@ -0,0 +1 @@
1
+ "use strict";var __defProp=Object.defineProperty;var __getOwnPropDesc=Object.getOwnPropertyDescriptor;var __getOwnPropNames=Object.getOwnPropertyNames;var __hasOwnProp=Object.prototype.hasOwnProperty;var __name=(target,value)=>__defProp(target,"name",{value,configurable:true});var __export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:true})};var __copyProps=(to,from,except,desc)=>{if(from&&typeof from==="object"||typeof from==="function"){for(let key of __getOwnPropNames(from))if(!__hasOwnProp.call(to,key)&&key!==except)__defProp(to,key,{get:()=>from[key],enumerable:!(desc=__getOwnPropDesc(from,key))||desc.enumerable})}return to};var __toCommonJS=mod=>__copyProps(__defProp({},"__esModule",{value:true}),mod);var index_exports={};__export(index_exports,{ErrorHandler:()=>ErrorHandler});module.exports=__toCommonJS(index_exports);var ErrorHandler=class{static{__name(this,"ErrorHandler")}static getErrorMessage(error){if(error instanceof Error){return error.message}if(typeof error==="string"){return error}return"Unknown error occurred"}static getErrorStack(error){if(error instanceof Error){return error.stack}return void 0}static createErrorContext(error,context){const errorContext={error:{message:this.getErrorMessage(error)}};if(error instanceof Error){errorContext.error.stack=error.stack;errorContext.error.name=error.name}if(context&&Object.keys(context).length>0){errorContext.context=context}return errorContext}static logError(logger,error,operation,context){const errorContext=this.createErrorContext(error,{operation,...context});const errorMessage=`Failed to ${operation}: ${errorContext.error.message}`;const loggerContext=logger.context||"ErrorHandler";logger.error(errorMessage,errorContext.error.stack,loggerContext,errorContext)}static rethrowError(error){if(error instanceof Error){throw error}throw new Error(`Unexpected error: ${String(error)}`)}};0&&(module.exports={ErrorHandler});
@@ -0,0 +1,30 @@
1
+ import { BulkResponseDto, DeleteDto, FilterAndPaginationDto, GetByIdBodyDto, ListResponseDto, MessageResponseDto, SingleResponseDto } from '@flusys/nestjs-shared/dtos';
2
+ import { Identity } from '@flusys/nestjs-shared/entities';
3
+ import { ILoggedUserInfo, IService, PermissionCondition, PermissionOperator } from '@flusys/nestjs-shared/interfaces';
4
+ import { Type } from '@nestjs/common';
5
+ export type ApiEndpoint = 'insert' | 'insertMany' | 'getById' | 'getAll' | 'update' | 'updateMany' | 'delete';
6
+ export type SecurityLevel = 'public' | 'jwt' | 'permission';
7
+ export interface EndpointSecurity {
8
+ level: SecurityLevel;
9
+ permissions?: string[];
10
+ operator?: PermissionOperator;
11
+ condition?: PermissionCondition;
12
+ }
13
+ export type ApiSecurityConfig = {
14
+ [K in ApiEndpoint]?: EndpointSecurity | SecurityLevel;
15
+ };
16
+ export interface ApiControllerOptions {
17
+ security?: ApiSecurityConfig | EndpointSecurity | SecurityLevel;
18
+ }
19
+ export declare function createApiController<CreateDtoT extends Record<string, unknown>, UpdateDtoT extends {
20
+ id: string;
21
+ }, ResponseDtoT extends Record<string, unknown>, InterfaceT extends Identity, ServiceT extends IService<CreateDtoT, UpdateDtoT, InterfaceT>>(createDtoClass: Type<CreateDtoT>, updateDtoClass: Type<UpdateDtoT>, responseDtoClass: Type<ResponseDtoT>, options?: ApiControllerOptions): abstract new (service: ServiceT) => {
22
+ service: ServiceT;
23
+ insert(addDto: CreateDtoT, user: ILoggedUserInfo | null): Promise<SingleResponseDto<ResponseDtoT>>;
24
+ insertMany(addDto: CreateDtoT[], user: ILoggedUserInfo | null): Promise<BulkResponseDto<ResponseDtoT>>;
25
+ getById(id: string, body: GetByIdBodyDto, user: ILoggedUserInfo | null): Promise<SingleResponseDto<ResponseDtoT>>;
26
+ update(updateDto: UpdateDtoT, user: ILoggedUserInfo | null): Promise<SingleResponseDto<ResponseDtoT>>;
27
+ updateMany(updateDtos: UpdateDtoT[], user: ILoggedUserInfo | null): Promise<BulkResponseDto<ResponseDtoT>>;
28
+ getAll(filterAndPaginationDto: FilterAndPaginationDto, user: ILoggedUserInfo | null, search?: string): Promise<ListResponseDto<ResponseDtoT>>;
29
+ delete(deleteDto: DeleteDto, user: ILoggedUserInfo | null): Promise<MessageResponseDto>;
30
+ };
@@ -0,0 +1,72 @@
1
+ import { DeleteDto, FilterAndPaginationDto } from '@flusys/nestjs-shared/dtos';
2
+ import { Identity } from '@flusys/nestjs-shared/entities';
3
+ import { ILoggedUserInfo, IService } from '@flusys/nestjs-shared/interfaces';
4
+ import { UtilsService } from '@flusys/nestjs-shared/modules';
5
+ import { Logger } from '@nestjs/common';
6
+ import { QueryRunner, Repository, SelectQueryBuilder } from 'typeorm';
7
+ import { HybridCache } from './hybrid-cache.class';
8
+ export declare abstract class ApiService<CreateDtoT extends Record<string, unknown>, UpdateDtoT extends {
9
+ id: string;
10
+ }, InterfaceT extends Identity, EntityT extends Identity, RepositoryT extends Repository<EntityT>> implements IService<CreateDtoT, UpdateDtoT, InterfaceT> {
11
+ protected entityName: string;
12
+ protected repository: RepositoryT;
13
+ protected cacheManager: HybridCache;
14
+ protected utilsService: UtilsService;
15
+ protected loggerName: string;
16
+ protected isCacheable: boolean;
17
+ protected logger: Logger;
18
+ constructor(entityName: string, repository: RepositoryT, cacheManager: HybridCache, utilsService: UtilsService, loggerName: string, isCacheable?: boolean);
19
+ insert(dto: CreateDtoT, user: ILoggedUserInfo | null): Promise<InterfaceT>;
20
+ insertMany(dtos: Array<CreateDtoT>, user: ILoggedUserInfo | null): Promise<InterfaceT[]>;
21
+ update(dto: UpdateDtoT, user: ILoggedUserInfo | null): Promise<InterfaceT>;
22
+ updateMany(dtos: UpdateDtoT[], user: ILoggedUserInfo | null): Promise<InterfaceT[]>;
23
+ findByIds(ids: string[], user: ILoggedUserInfo | null): Promise<InterfaceT[]>;
24
+ findById(id: string, user: ILoggedUserInfo | null, select?: string[]): Promise<InterfaceT>;
25
+ getAll(search: string, filterAndPaginationDto: FilterAndPaginationDto, user: ILoggedUserInfo | null): Promise<{
26
+ data: Array<InterfaceT>;
27
+ total: number;
28
+ }>;
29
+ delete(option: DeleteDto, user: ILoggedUserInfo | null): Promise<null>;
30
+ clearCacheForAll(): Promise<void>;
31
+ clearCacheForId(entities: EntityT[]): Promise<void>;
32
+ private handleError;
33
+ protected ensureRepositoryInitialized(): Promise<void>;
34
+ protected beforeInsertOperation(_dto: CreateDtoT | Array<CreateDtoT>, _user: ILoggedUserInfo | null, _queryRunner: QueryRunner): Promise<void>;
35
+ protected afterInsertOperation(_entity: EntityT[], _user: ILoggedUserInfo | null, _queryRunner: QueryRunner): Promise<void>;
36
+ protected beforeUpdateOperation(_dto: UpdateDtoT | Array<UpdateDtoT>, _user: ILoggedUserInfo | null, _queryRunner: QueryRunner): Promise<void>;
37
+ protected afterUpdateOperation(_entity: EntityT[], _user: ILoggedUserInfo | null, _queryRunner: QueryRunner): Promise<void>;
38
+ protected beforeDeleteOperation(_dto: DeleteDto, _user: ILoggedUserInfo | null, _queryRunner: QueryRunner): Promise<void>;
39
+ protected afterDeleteOperation(_entity: Array<{
40
+ id: string;
41
+ }>, _user: ILoggedUserInfo | null, _queryRunner: QueryRunner): Promise<void>;
42
+ protected getFilterQuery(query: SelectQueryBuilder<EntityT>, filter: {
43
+ [key: string]: any;
44
+ }, _user: ILoggedUserInfo | null): Promise<{
45
+ query: SelectQueryBuilder<EntityT>;
46
+ isRaw: boolean;
47
+ }>;
48
+ protected getSortQuery(query: SelectQueryBuilder<EntityT>, sort: {
49
+ [key: string]: 'ASC' | 'DESC';
50
+ }, _user: ILoggedUserInfo | null): Promise<{
51
+ query: SelectQueryBuilder<EntityT>;
52
+ isRaw: boolean;
53
+ }>;
54
+ protected getSelectQuery(query: SelectQueryBuilder<EntityT>, _user: ILoggedUserInfo | null, select?: string[]): Promise<{
55
+ query: SelectQueryBuilder<EntityT>;
56
+ isRaw: boolean;
57
+ }>;
58
+ protected getGlobalSearchQuery(query: SelectQueryBuilder<EntityT>, search: string, _user: ILoggedUserInfo | null): Promise<{
59
+ query: SelectQueryBuilder<EntityT>;
60
+ isRaw: boolean;
61
+ }>;
62
+ protected getExtraManipulateQuery(query: SelectQueryBuilder<EntityT>, _dto: FilterAndPaginationDto, _user: ILoggedUserInfo | null): Promise<{
63
+ query: SelectQueryBuilder<EntityT>;
64
+ isRaw: boolean;
65
+ }>;
66
+ protected convertRequestDtoToEntity(dto: CreateDtoT | UpdateDtoT | Array<CreateDtoT | UpdateDtoT>, user: ILoggedUserInfo | null): Promise<Array<EntityT>>;
67
+ protected convertSingleDtoToEntity(dto: CreateDtoT | UpdateDtoT, _user: ILoggedUserInfo | null): Promise<EntityT>;
68
+ protected convertArrayDtoToEntities(dtos: Array<CreateDtoT | UpdateDtoT>, user: ILoggedUserInfo | null): Promise<Array<EntityT>>;
69
+ protected convertEntityToResponseDto(entity: EntityT, _isRaw: boolean): InterfaceT;
70
+ protected convertEntityListToResponseListDto(entities: EntityT[], _isRaw: boolean): InterfaceT[];
71
+ protected getEntityClass(): Promise<EntityT>;
72
+ }
@@ -0,0 +1,10 @@
1
+ export declare class HybridCache {
2
+ private memory?;
3
+ private redis?;
4
+ constructor(memoryTtl?: number, memorySize?: number);
5
+ get<T>(key: string): Promise<T | undefined>;
6
+ set<T>(key: string, value: T, ttl?: number): Promise<void>;
7
+ del(key: string): Promise<void>;
8
+ reset(): Promise<void>;
9
+ resetL2(): Promise<void>;
10
+ }
@@ -0,0 +1,6 @@
1
+ export * from './api-controller.class';
2
+ export * from './api-service.class';
3
+ export * from './request-scoped-api.service';
4
+ export * from './hybrid-cache.class';
5
+ export * from './winston-logger-adapter.class';
6
+ export * from './winston.logger.class';
@@ -0,0 +1,13 @@
1
+ import { DataSource, EntityTarget, Repository } from 'typeorm';
2
+ import { Identity } from '../entities';
3
+ import { ApiService } from './api-service.class';
4
+ export declare abstract class RequestScopedApiService<CreateDtoT extends Record<string, unknown>, UpdateDtoT extends {
5
+ id: string;
6
+ }, InterfaceT extends Identity, EntityT extends Identity, RepositoryT extends Repository<EntityT>> extends ApiService<CreateDtoT, UpdateDtoT, InterfaceT, EntityT, RepositoryT> {
7
+ private repositoryInitialized;
8
+ protected abstract resolveEntity(): EntityTarget<EntityT>;
9
+ protected abstract getDataSourceProvider(): any;
10
+ protected ensureRepositoryInitialized(): Promise<void>;
11
+ protected initializeAdditionalRepositories(entities: EntityTarget<any>[]): Promise<Repository<any>[]>;
12
+ protected getDataSourceForService(): Promise<DataSource>;
13
+ }
@@ -0,0 +1,20 @@
1
+ import { Logger } from '@nestjs/common';
2
+ import { ILogger } from '../interfaces/logger.interface';
3
+ export declare class WinstonLoggerAdapter implements ILogger {
4
+ private readonly context?;
5
+ constructor(context?: string);
6
+ log(message: string, context?: string, ...args: any[]): void;
7
+ error(message: string, trace?: string, context?: string, ...args: any[]): void;
8
+ warn(message: string, context?: string, ...args: any[]): void;
9
+ debug(message: string, context?: string, ...args: any[]): void;
10
+ verbose(message: string, context?: string, ...args: any[]): void;
11
+ }
12
+ export declare class NestLoggerAdapter implements ILogger {
13
+ private readonly logger;
14
+ constructor(logger: Logger);
15
+ log(message: string, context?: string, ...args: any[]): void;
16
+ error(message: string, trace?: string, context?: string, ...args: any[]): void;
17
+ warn(message: string, context?: string, ...args: any[]): void;
18
+ debug(message: string, context?: string, ...args: any[]): void;
19
+ verbose(message: string, context?: string, ...args: any[]): void;
20
+ }
@@ -0,0 +1,2 @@
1
+ import { Logger } from 'winston';
2
+ export declare const instance: Logger;
@@ -0,0 +1,10 @@
1
+ export declare const IS_PUBLIC_KEY = "isPublic";
2
+ export declare const PERMISSIONS_KEY = "permissions";
3
+ export declare const CACHE_INSTANCE = "CACHE_INSTANCE";
4
+ export declare const PERMISSION_GUARD_CONFIG = "PERMISSION_GUARD_CONFIG";
5
+ export declare const LOGGER_INSTANCE = "LOGGER_INSTANCE";
6
+ export declare const IDEMPOTENCY_KEY_HEADER = "x-idempotency-key";
7
+ export declare const REQUEST_ID_HEADER = "x-request-id";
8
+ export declare const CLIENT_TYPE_HEADER = "x-client-type";
9
+ export declare const PERMISSIONS_CACHE_PREFIX = "permissions";
10
+ export declare const IDEMPOTENCY_CACHE_PREFIX = "idempotency";
@@ -0,0 +1,3 @@
1
+ import { Type } from '@nestjs/common';
2
+ export type ArrayResponseType = 'list' | 'bulk';
3
+ export declare const ApiResponseDto: <T extends Type<unknown>>(dto: T, isArray?: boolean, arrayType?: ArrayResponseType) => <TFunction extends Function, Y>(target: TFunction | object, propertyKey?: string | symbol, descriptor?: TypedPropertyDescriptor<Y>) => void;
@@ -0,0 +1 @@
1
+ export declare const CurrentUser: (...dataOrPipes: (string | import("@nestjs/common").PipeTransform<any, any> | import("@nestjs/common").Type<import("@nestjs/common").PipeTransform<any, any>>)[]) => ParameterDecorator;
@@ -0,0 +1,4 @@
1
+ export * from './api-response.decorator';
2
+ export * from './current-user.decorator';
3
+ export * from './public.decorator';
4
+ export * from './require-permission.decorator';
@@ -0,0 +1 @@
1
+ export declare const Public: () => import("@nestjs/common").CustomDecorator<string>;
@@ -0,0 +1,4 @@
1
+ import { PermissionCondition } from '../interfaces/permission.interface';
2
+ export declare const RequirePermission: (...permissions: string[]) => import("@nestjs/common").CustomDecorator<string>;
3
+ export declare const RequireAnyPermission: (...permissions: string[]) => import("@nestjs/common").CustomDecorator<string>;
4
+ export declare const RequirePermissionCondition: (condition: PermissionCondition) => import("@nestjs/common").CustomDecorator<string>;
@@ -0,0 +1,4 @@
1
+ export declare class DeleteDto {
2
+ id: string | string[];
3
+ type: 'delete' | 'restore' | 'permanent';
4
+ }
@@ -0,0 +1,13 @@
1
+ import { PaginationDto } from './pagination.dto';
2
+ export declare class FilterAndPaginationDto {
3
+ filter?: Record<string, unknown>;
4
+ pagination?: PaginationDto;
5
+ sort?: Record<string, 'ASC' | 'DESC'>;
6
+ select?: string[];
7
+ withDeleted?: boolean;
8
+ extraKey?: string[];
9
+ }
10
+ export declare class GetByIdBodyDto {
11
+ select?: string[];
12
+ extraKey?: string[];
13
+ }
@@ -0,0 +1,9 @@
1
+ export declare class IdentityResponseDto {
2
+ id: string;
3
+ createdAt: Date;
4
+ updatedAt: Date;
5
+ deletedAt?: Date | null;
6
+ createdById?: string | null;
7
+ updatedById?: string | null;
8
+ deletedById?: string | null;
9
+ }
@@ -0,0 +1,5 @@
1
+ export * from './delete.dto';
2
+ export * from './filter-and-pagination.dto';
3
+ export * from './identity-response.dto';
4
+ export * from './pagination.dto';
5
+ export * from './response-payload.dto';
@@ -0,0 +1,4 @@
1
+ export declare class PaginationDto {
2
+ pageSize: number;
3
+ currentPage: number;
4
+ }
@@ -0,0 +1,63 @@
1
+ export declare class RequestMetaDto {
2
+ requestId?: string;
3
+ timestamp?: string;
4
+ responseTime?: number;
5
+ }
6
+ export declare class SingleResponseDto<T> {
7
+ success: boolean;
8
+ message: string;
9
+ data?: T;
10
+ _meta?: RequestMetaDto;
11
+ }
12
+ export declare class PaginationMetaDto {
13
+ total: number;
14
+ page: number;
15
+ pageSize: number;
16
+ count: number;
17
+ hasMore?: boolean;
18
+ totalPages?: number;
19
+ }
20
+ export declare class ListResponseDto<T> {
21
+ success: boolean;
22
+ message: string;
23
+ data?: T[];
24
+ meta: PaginationMetaDto;
25
+ _meta?: RequestMetaDto;
26
+ }
27
+ export declare class BulkMetaDto {
28
+ count: number;
29
+ failed?: number;
30
+ total?: number;
31
+ }
32
+ export declare class BulkResponseDto<T> {
33
+ success: boolean;
34
+ message: string;
35
+ data?: T[];
36
+ meta: BulkMetaDto;
37
+ _meta?: RequestMetaDto;
38
+ }
39
+ export declare class MessageResponseDto {
40
+ success: boolean;
41
+ message: string;
42
+ _meta?: RequestMetaDto;
43
+ }
44
+ export declare class ValidationErrorDto {
45
+ field: string;
46
+ message: string;
47
+ constraint?: string;
48
+ }
49
+ export declare class ErrorResponseDto {
50
+ success: false;
51
+ message: string;
52
+ code?: string;
53
+ errors?: ValidationErrorDto[];
54
+ _meta?: RequestMetaDto;
55
+ }
56
+ export declare class ResponsePayloadDto<T> {
57
+ success: boolean;
58
+ message: string;
59
+ data?: T;
60
+ meta?: PaginationMetaDto | BulkMetaDto;
61
+ _meta?: RequestMetaDto;
62
+ }
63
+ export type ApiResponse<T> = SingleResponseDto<T> | ListResponseDto<T> | BulkResponseDto<T> | MessageResponseDto | ErrorResponseDto;
@@ -0,0 +1,9 @@
1
+ export declare abstract class Identity {
2
+ id: string;
3
+ createdAt: Date;
4
+ updatedAt: Date;
5
+ deletedAt: Date | null;
6
+ createdById: string | null;
7
+ updatedById: string | null;
8
+ deletedById: string | null;
9
+ }
@@ -0,0 +1,2 @@
1
+ export * from './identity';
2
+ export * from './user-root';
@@ -0,0 +1,19 @@
1
+ export declare abstract class UserRoot {
2
+ id: string;
3
+ name: string | null;
4
+ password: string | null;
5
+ email: string;
6
+ phone: string | null;
7
+ isActive: boolean;
8
+ emailVerified: boolean;
9
+ phoneVerified: boolean;
10
+ profilePictureId: string | null;
11
+ lastLoginAt: Date | null;
12
+ additionalFields: Record<string, any> | null;
13
+ createdAt: Date;
14
+ updatedAt: Date;
15
+ deletedAt: Date | null;
16
+ createdById: string | null;
17
+ updatedById: string | null;
18
+ deletedById: string | null;
19
+ }
@@ -0,0 +1 @@
1
+ export * from './permission.exception';
@@ -0,0 +1,10 @@
1
+ import { ForbiddenException, InternalServerErrorException } from '@nestjs/common';
2
+ export declare class PermissionSystemUnavailableException extends InternalServerErrorException {
3
+ constructor(message?: string);
4
+ }
5
+ export declare class InsufficientPermissionsException extends ForbiddenException {
6
+ constructor(missingPermissions: string[], operator?: 'and' | 'or');
7
+ }
8
+ export declare class NoPermissionsFoundException extends ForbiddenException {
9
+ constructor();
10
+ }