@ambiten/core 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +247 -0
- package/bin/bootstrap.js +26 -0
- package/dist/ambiten-cache/ambitenCache.d.ts +64 -0
- package/dist/ambiten-cache/ambitenCache.d.ts.map +1 -0
- package/dist/ambiten-cache/ambitenCache.js +127 -0
- package/dist/ambiten-cache/index.d.ts +2 -0
- package/dist/ambiten-cache/index.d.ts.map +1 -0
- package/dist/ambiten-cache/index.js +4 -0
- package/dist/browser.d.ts +32 -0
- package/dist/browser.d.ts.map +1 -0
- package/dist/browser.js +37 -0
- package/dist/cli/ambiten-core-cli.d.ts +4 -0
- package/dist/cli/ambiten-core-cli.d.ts.map +1 -0
- package/dist/cli/ambiten-core-cli.js +41 -0
- package/dist/cli/buildInteractiveConfig.d.ts +4 -0
- package/dist/cli/buildInteractiveConfig.d.ts.map +1 -0
- package/dist/cli/buildInteractiveConfig.js +198 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/prompt.d.ts +10 -0
- package/dist/cli/prompt.d.ts.map +1 -0
- package/dist/cli/prompt.js +48 -0
- package/dist/cli/types.d.ts +11 -0
- package/dist/cli/types.d.ts.map +1 -0
- package/dist/cli/types.js +2 -0
- package/dist/config/ambiten.config.schema.json +278 -0
- package/dist/config/index.d.ts +2 -0
- package/dist/config/index.d.ts.map +1 -0
- package/dist/config/index.js +4 -0
- package/dist/config/loadAmbitenConfig.d.ts +3 -0
- package/dist/config/loadAmbitenConfig.d.ts.map +1 -0
- package/dist/config/loadAmbitenConfig.js +131 -0
- package/dist/context/ambitenContext.d.ts +35 -0
- package/dist/context/ambitenContext.d.ts.map +1 -0
- package/dist/context/ambitenContext.js +147 -0
- package/dist/context/helpers/runManualTransaction.d.ts +3 -0
- package/dist/context/helpers/runManualTransaction.d.ts.map +1 -0
- package/dist/context/helpers/runManualTransaction.js +22 -0
- package/dist/context/helpers/transactionSession.d.ts +9 -0
- package/dist/context/helpers/transactionSession.d.ts.map +1 -0
- package/dist/context/helpers/transactionSession.js +8 -0
- package/dist/context/index.d.ts +4 -0
- package/dist/context/index.d.ts.map +1 -0
- package/dist/context/index.js +6 -0
- package/dist/debug/debugLog.d.ts +2 -0
- package/dist/debug/debugLog.d.ts.map +1 -0
- package/dist/debug/debugLog.js +23 -0
- package/dist/debug/index.d.ts +2 -0
- package/dist/debug/index.d.ts.map +1 -0
- package/dist/debug/index.js +4 -0
- package/dist/gc/ambitenGC.d.ts +49 -0
- package/dist/gc/ambitenGC.d.ts.map +1 -0
- package/dist/gc/ambitenGC.js +138 -0
- package/dist/gc/gcCron.browser.d.ts +2 -0
- package/dist/gc/gcCron.browser.d.ts.map +1 -0
- package/dist/gc/gcCron.browser.js +9 -0
- package/dist/gc/gcCron.node.d.ts +19 -0
- package/dist/gc/gcCron.node.d.ts.map +1 -0
- package/dist/gc/gcCron.node.js +47 -0
- package/dist/gc/gcManager.d.ts +37 -0
- package/dist/gc/gcManager.d.ts.map +1 -0
- package/dist/gc/gcManager.js +62 -0
- package/dist/gc/index.d.ts +5 -0
- package/dist/gc/index.d.ts.map +1 -0
- package/dist/gc/index.js +7 -0
- package/dist/graphql/ambitenGraphQL.d.ts +24 -0
- package/dist/graphql/ambitenGraphQL.d.ts.map +1 -0
- package/dist/graphql/ambitenGraphQL.js +215 -0
- package/dist/graphql/index.d.ts +2 -0
- package/dist/graphql/index.d.ts.map +1 -0
- package/dist/graphql/index.js +4 -0
- package/dist/index-browser.js +25 -0
- package/dist/index-browser.js.map +1 -0
- package/dist/index-cli.js +26 -0
- package/dist/index-cli.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/index.js.map +1 -0
- package/dist/init-cli/generate.project.d.ts +9 -0
- package/dist/init-cli/generate.project.d.ts.map +1 -0
- package/dist/init-cli/generate.project.js +38 -0
- package/dist/instrumentation/index.d.ts +3 -0
- package/dist/instrumentation/index.d.ts.map +1 -0
- package/dist/instrumentation/index.js +5 -0
- package/dist/instrumentation/measureQuery.browser.d.ts +3 -0
- package/dist/instrumentation/measureQuery.browser.d.ts.map +1 -0
- package/dist/instrumentation/measureQuery.browser.js +6 -0
- package/dist/instrumentation/measureQueryWithErrors.d.ts +11 -0
- package/dist/instrumentation/measureQueryWithErrors.d.ts.map +1 -0
- package/dist/instrumentation/measureQueryWithErrors.js +90 -0
- package/dist/lib-core/ambitenClient.d.ts +115 -0
- package/dist/lib-core/ambitenClient.d.ts.map +1 -0
- package/dist/lib-core/ambitenClient.js +537 -0
- package/dist/lib-core/ambitenModelFactory.d.ts +979 -0
- package/dist/lib-core/ambitenModelFactory.d.ts.map +1 -0
- package/dist/lib-core/ambitenModelFactory.js +3984 -0
- package/dist/lib-core/ambitenSchema.d.ts +126 -0
- package/dist/lib-core/ambitenSchema.d.ts.map +1 -0
- package/dist/lib-core/ambitenSchema.js +247 -0
- package/dist/lib-core/bootstrap/ambitenBootstrap.d.ts +38 -0
- package/dist/lib-core/bootstrap/ambitenBootstrap.d.ts.map +1 -0
- package/dist/lib-core/bootstrap/ambitenBootstrap.js +381 -0
- package/dist/lib-core/bootstrap/index.d.ts +2 -0
- package/dist/lib-core/bootstrap/index.d.ts.map +1 -0
- package/dist/lib-core/bootstrap/index.js +4 -0
- package/dist/lib-core/index.d.ts +5 -0
- package/dist/lib-core/index.d.ts.map +1 -0
- package/dist/lib-core/index.js +7 -0
- package/dist/middleware/authorization/auth.d.ts +22 -0
- package/dist/middleware/authorization/auth.d.ts.map +1 -0
- package/dist/middleware/authorization/auth.js +37 -0
- package/dist/middleware/index.d.ts +4 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +6 -0
- package/dist/middleware/rbac/rbacMiddleware.d.ts +103 -0
- package/dist/middleware/rbac/rbacMiddleware.d.ts.map +1 -0
- package/dist/middleware/rbac/rbacMiddleware.js +246 -0
- package/dist/middleware/rbac/rbacTypes.d.ts +4 -0
- package/dist/middleware/rbac/rbacTypes.d.ts.map +1 -0
- package/dist/middleware/rbac/rbacTypes.js +8 -0
- package/dist/plugins/index.d.ts +3 -0
- package/dist/plugins/index.d.ts.map +1 -0
- package/dist/plugins/index.js +5 -0
- package/dist/plugins/softDelete/applySoftDelete.d.ts +5 -0
- package/dist/plugins/softDelete/applySoftDelete.d.ts.map +1 -0
- package/dist/plugins/softDelete/applySoftDelete.js +139 -0
- package/dist/plugins/softDelete/types.d.ts +14 -0
- package/dist/plugins/softDelete/types.d.ts.map +1 -0
- package/dist/plugins/softDelete/types.js +2 -0
- package/dist/redis-manager/index.d.ts +2 -0
- package/dist/redis-manager/index.d.ts.map +1 -0
- package/dist/redis-manager/index.js +4 -0
- package/dist/redis-manager/redisClient.d.ts +32 -0
- package/dist/redis-manager/redisClient.d.ts.map +1 -0
- package/dist/redis-manager/redisClient.js +146 -0
- package/dist/tanancy/MultiTenantManager.d.ts +98 -0
- package/dist/tanancy/MultiTenantManager.d.ts.map +1 -0
- package/dist/tanancy/MultiTenantManager.js +190 -0
- package/dist/tanancy/TenantContext.d.ts +31 -0
- package/dist/tanancy/TenantContext.d.ts.map +1 -0
- package/dist/tanancy/TenantContext.js +59 -0
- package/dist/tanancy/TenantModelResolver.d.ts +10 -0
- package/dist/tanancy/TenantModelResolver.d.ts.map +1 -0
- package/dist/tanancy/TenantModelResolver.js +114 -0
- package/dist/tanancy/index.d.ts +5 -0
- package/dist/tanancy/index.d.ts.map +1 -0
- package/dist/tanancy/index.js +7 -0
- package/dist/tanancy/init/initMultiTenancy.d.ts +29 -0
- package/dist/tanancy/init/initMultiTenancy.d.ts.map +1 -0
- package/dist/tanancy/init/initMultiTenancy.js +62 -0
- package/dist/templates/generateDefaultConfigContent.d.ts +10 -0
- package/dist/templates/generateDefaultConfigContent.d.ts.map +1 -0
- package/dist/templates/generateDefaultConfigContent.js +108 -0
- package/dist/templates/generateGCManagerFile.d.ts +2 -0
- package/dist/templates/generateGCManagerFile.d.ts.map +1 -0
- package/dist/templates/generateGCManagerFile.js +39 -0
- package/dist/templates/generateGCRunnerFile.d.ts +2 -0
- package/dist/templates/generateGCRunnerFile.d.ts.map +1 -0
- package/dist/templates/generateGCRunnerFile.js +25 -0
- package/dist/templates/generateMainEntry.d.ts +3 -0
- package/dist/templates/generateMainEntry.d.ts.map +1 -0
- package/dist/templates/generateMainEntry.js +24 -0
- package/dist/templates/index.d.ts +6 -0
- package/dist/templates/index.d.ts.map +1 -0
- package/dist/templates/index.js +8 -0
- package/dist/templates/scaffold/structureGenerator.d.ts +4 -0
- package/dist/templates/scaffold/structureGenerator.d.ts.map +1 -0
- package/dist/templates/scaffold/structureGenerator.js +193 -0
- package/dist/types/GCConfig.d.ts +7 -0
- package/dist/types/GCConfig.d.ts.map +1 -0
- package/dist/types/GCConfig.js +2 -0
- package/dist/types/ambiten-runtime-type.d.ts +24 -0
- package/dist/types/ambiten-runtime-type.d.ts.map +1 -0
- package/dist/types/ambiten-runtime-type.js +2 -0
- package/dist/types/ambiten.client.type.d.ts +136 -0
- package/dist/types/ambiten.client.type.d.ts.map +1 -0
- package/dist/types/ambiten.client.type.js +3 -0
- package/dist/types/ambiten.model.type.d.ts +135 -0
- package/dist/types/ambiten.model.type.d.ts.map +1 -0
- package/dist/types/ambiten.model.type.js +4 -0
- package/dist/types/ambitenConfig.d.ts +103 -0
- package/dist/types/ambitenConfig.d.ts.map +1 -0
- package/dist/types/ambitenConfig.js +3 -0
- package/dist/types/bootstrapClient.type.d.ts +11 -0
- package/dist/types/bootstrapClient.type.d.ts.map +1 -0
- package/dist/types/bootstrapClient.type.js +2 -0
- package/dist/types/db.provider.d.ts +66 -0
- package/dist/types/db.provider.d.ts.map +1 -0
- package/dist/types/db.provider.js +2 -0
- package/dist/types/document.d.ts +7 -0
- package/dist/types/document.d.ts.map +1 -0
- package/dist/types/document.js +2 -0
- package/dist/types/event.type.d.ts +6 -0
- package/dist/types/event.type.d.ts.map +1 -0
- package/dist/types/event.type.js +27 -0
- package/dist/types/graphql.type.d.ts +22 -0
- package/dist/types/graphql.type.d.ts.map +1 -0
- package/dist/types/graphql.type.js +2 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +17 -0
- package/dist/types/logger.types.d.ts +15 -0
- package/dist/types/logger.types.d.ts.map +1 -0
- package/dist/types/logger.types.js +3 -0
- package/dist/types/middleware/types.d.ts +28 -0
- package/dist/types/middleware/types.d.ts.map +1 -0
- package/dist/types/middleware/types.js +2 -0
- package/dist/types/result.utils.types.d.ts +7 -0
- package/dist/types/result.utils.types.d.ts.map +1 -0
- package/dist/types/result.utils.types.js +2 -0
- package/dist/types/schema.type.d.ts +23 -0
- package/dist/types/schema.type.d.ts.map +1 -0
- package/dist/types/schema.type.js +10 -0
- package/dist/types/user.type.d.ts +11 -0
- package/dist/types/user.type.d.ts.map +1 -0
- package/dist/types/user.type.js +2 -0
- package/dist/utils/AuthUtils.d.ts +15 -0
- package/dist/utils/AuthUtils.d.ts.map +1 -0
- package/dist/utils/AuthUtils.js +26 -0
- package/dist/utils/ModelRegistry.d.ts +15 -0
- package/dist/utils/ModelRegistry.d.ts.map +1 -0
- package/dist/utils/ModelRegistry.js +35 -0
- package/dist/utils/banner.d.ts +2 -0
- package/dist/utils/banner.d.ts.map +1 -0
- package/dist/utils/banner.js +20 -0
- package/dist/utils/builders/createModel.d.ts +37 -0
- package/dist/utils/builders/createModel.d.ts.map +1 -0
- package/dist/utils/builders/createModel.js +38 -0
- package/dist/utils/builders/getTenantDb.d.ts +12 -0
- package/dist/utils/builders/getTenantDb.d.ts.map +1 -0
- package/dist/utils/builders/getTenantDb.js +25 -0
- package/dist/utils/builders/index.d.ts +4 -0
- package/dist/utils/builders/index.d.ts.map +1 -0
- package/dist/utils/builders/index.js +6 -0
- package/dist/utils/builders/schema.d.ts +22 -0
- package/dist/utils/builders/schema.d.ts.map +1 -0
- package/dist/utils/builders/schema.js +26 -0
- package/dist/utils/cacheWithRedis.d.ts +27 -0
- package/dist/utils/cacheWithRedis.d.ts.map +1 -0
- package/dist/utils/cacheWithRedis.js +22 -0
- package/dist/utils/castId.d.ts +5 -0
- package/dist/utils/castId.d.ts.map +1 -0
- package/dist/utils/castId.js +52 -0
- package/dist/utils/color-palatte.d.ts +4 -0
- package/dist/utils/color-palatte.d.ts.map +1 -0
- package/dist/utils/color-palatte.js +24 -0
- package/dist/utils/configureAmbitenContext.d.ts +13 -0
- package/dist/utils/configureAmbitenContext.d.ts.map +1 -0
- package/dist/utils/configureAmbitenContext.js +35 -0
- package/dist/utils/detect-package-manager.d.ts +5 -0
- package/dist/utils/detect-package-manager.d.ts.map +1 -0
- package/dist/utils/detect-package-manager.js +59 -0
- package/dist/utils/ensureModelNameSafe.d.ts +12 -0
- package/dist/utils/ensureModelNameSafe.d.ts.map +1 -0
- package/dist/utils/ensureModelNameSafe.js +28 -0
- package/dist/utils/ensureRedis.d.ts +7 -0
- package/dist/utils/ensureRedis.d.ts.map +1 -0
- package/dist/utils/ensureRedis.js +40 -0
- package/dist/utils/error/ambitenError-handler.d.ts +24 -0
- package/dist/utils/error/ambitenError-handler.d.ts.map +1 -0
- package/dist/utils/error/ambitenError-handler.js +39 -0
- package/dist/utils/error/errorTypes.d.ts +27 -0
- package/dist/utils/error/errorTypes.d.ts.map +1 -0
- package/dist/utils/error/errorTypes.js +30 -0
- package/dist/utils/error/index.d.ts +3 -0
- package/dist/utils/error/index.d.ts.map +1 -0
- package/dist/utils/error/index.js +5 -0
- package/dist/utils/eventOptions.d.ts +38 -0
- package/dist/utils/eventOptions.d.ts.map +1 -0
- package/dist/utils/eventOptions.js +130 -0
- package/dist/utils/events.d.ts +5 -0
- package/dist/utils/events.d.ts.map +1 -0
- package/dist/utils/events.js +7 -0
- package/dist/utils/index.d.ts +23 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +25 -0
- package/dist/utils/invalidateTenantCache.d.ts +18 -0
- package/dist/utils/invalidateTenantCache.d.ts.map +1 -0
- package/dist/utils/invalidateTenantCache.js +27 -0
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +27 -0
- package/dist/utils/modelLoader.d.ts +3 -0
- package/dist/utils/modelLoader.d.ts.map +1 -0
- package/dist/utils/modelLoader.js +41 -0
- package/dist/utils/resolveAmbitenOption.d.ts +2 -0
- package/dist/utils/resolveAmbitenOption.d.ts.map +1 -0
- package/dist/utils/resolveAmbitenOption.js +10 -0
- package/dist/utils/resolveRuntimeConfig.d.ts +22 -0
- package/dist/utils/resolveRuntimeConfig.d.ts.map +1 -0
- package/dist/utils/resolveRuntimeConfig.js +33 -0
- package/dist/utils/scaffoldLoger.d.ts +11 -0
- package/dist/utils/scaffoldLoger.d.ts.map +1 -0
- package/dist/utils/scaffoldLoger.js +68 -0
- package/dist/utils/symbols.d.ts +4 -0
- package/dist/utils/symbols.d.ts.map +1 -0
- package/dist/utils/symbols.js +6 -0
- package/dist/utils/time.d.ts +5 -0
- package/dist/utils/time.d.ts.map +1 -0
- package/dist/utils/time.js +39 -0
- package/package.json +180 -0
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.checkPermission = checkPermission;
|
|
4
|
+
exports.getCachedData = getCachedData;
|
|
5
|
+
exports.setCachedData = setCachedData;
|
|
6
|
+
exports.invalidateTenantCache = invalidateTenantCache;
|
|
7
|
+
exports.enforceRBAC = enforceRBAC;
|
|
8
|
+
exports.getRBACAction = getRBACAction;
|
|
9
|
+
exports.getOriginalResolver = getOriginalResolver;
|
|
10
|
+
const redis_manager_1 = require("../../redis-manager");
|
|
11
|
+
const rbacTypes_1 = require("./rbacTypes");
|
|
12
|
+
function buildCacheKey(role, key) {
|
|
13
|
+
if (Array.isArray(role)) {
|
|
14
|
+
return `${role.join(',')}:${key}`;
|
|
15
|
+
}
|
|
16
|
+
if (role && typeof role === 'object') {
|
|
17
|
+
if ('tenantId' in role)
|
|
18
|
+
return `${role.tenantId}:${key}`;
|
|
19
|
+
// If role is actually a role object or other shape, fallback to JSON-safe string
|
|
20
|
+
try {
|
|
21
|
+
const convertCredentials = JSON.parse(JSON.stringify(role));
|
|
22
|
+
return `${convertCredentials}:${key}`;
|
|
23
|
+
}
|
|
24
|
+
catch (err) {
|
|
25
|
+
return `${String(role)}:${key}`;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return `${role}:${key}`;
|
|
29
|
+
}
|
|
30
|
+
;
|
|
31
|
+
// const tenantCache: Map<string, any> = new Map();
|
|
32
|
+
/**
|
|
33
|
+
* Check if a user has a specific permission
|
|
34
|
+
* @param role The role of the user
|
|
35
|
+
* @param permission The permission to check
|
|
36
|
+
* @returns A promise that resolves to true if the user has the permission, false otherwise
|
|
37
|
+
* @example
|
|
38
|
+
* const hasPermission = await checkPermission('admin', 'createUser');
|
|
39
|
+
* if (hasPermission) {
|
|
40
|
+
* // User has permission to create a user
|
|
41
|
+
* } else {
|
|
42
|
+
* // User does not have permission to create a user
|
|
43
|
+
* }
|
|
44
|
+
*/
|
|
45
|
+
async function checkPermission(role, permission) {
|
|
46
|
+
const cacheKey = buildCacheKey(role, 'permissions');
|
|
47
|
+
let allowedActions = await getCachedData(role, 'permissions');
|
|
48
|
+
if (!allowedActions) {
|
|
49
|
+
console.info(`Cache miss for role "${role}". Fetching permissions...`);
|
|
50
|
+
// Fetch permissions from the rolePermissions object
|
|
51
|
+
allowedActions = rbacTypes_1.rolePermissions[role] || [];
|
|
52
|
+
if (allowedActions.length > 0) {
|
|
53
|
+
console.info(`Permissions found for role "${role}": ${allowedActions.join(', ')}`);
|
|
54
|
+
}
|
|
55
|
+
else {
|
|
56
|
+
console.warn(`No permissions found for role "${role}"`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
;
|
|
60
|
+
const cacheExists = await redis_manager_1.redis.exists(cacheKey);
|
|
61
|
+
if (cacheExists) {
|
|
62
|
+
console.info(`Cache hit for role "${role}"`);
|
|
63
|
+
}
|
|
64
|
+
// If allowedActions is not already cached, set it
|
|
65
|
+
if (allowedActions) {
|
|
66
|
+
allowedActions = Promise.resolve(rbacTypes_1.rolePermissions[role] || []);
|
|
67
|
+
await setCachedData(role, 'permissions', await allowedActions, 500); // Cache for 500s
|
|
68
|
+
}
|
|
69
|
+
return Promise.resolve(rbacTypes_1.rolePermissions[role] || []).then(permissions => {
|
|
70
|
+
if (permissions.includes(permission)) {
|
|
71
|
+
console.info(`Permission granted for role "${role}" to perform action "${permission}"`);
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
console.warn(`Permission denied for role "${role}" to perform action "${permission}"`);
|
|
76
|
+
return false;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Get cached data for a specific role and key
|
|
82
|
+
* @param role The role or tenant object
|
|
83
|
+
* @param key The key to retrieve from the cache
|
|
84
|
+
* @return {Promise<any>} A promise that resolves to the cached data or null if not found
|
|
85
|
+
* This function retrieves cached data from Redis based on the provided role and key.
|
|
86
|
+
* It constructs a unique cache key using the role and key, checks if the data exists in the cache,
|
|
87
|
+
* and returns the parsed data if found. If the data is not found, it logs a cache miss and returns null.
|
|
88
|
+
* @throws {Error} If there is an issue with the Redis operation
|
|
89
|
+
* @example
|
|
90
|
+
* // Get cached permissions for role 'admin'
|
|
91
|
+
* const cachedPermissions = await getCachedData('admin', 'permissions');
|
|
92
|
+
* if (cachedPermissions) {
|
|
93
|
+
* console.log('Cached permissions:', cachedPermissions);
|
|
94
|
+
* } else {
|
|
95
|
+
* console.log('No cached permissions found');
|
|
96
|
+
* }
|
|
97
|
+
*/
|
|
98
|
+
async function getCachedData(role, key) {
|
|
99
|
+
const cacheKey = buildCacheKey(role, key);
|
|
100
|
+
const cachedData = await redis_manager_1.redis.get(cacheKey);
|
|
101
|
+
if (cachedData) {
|
|
102
|
+
console.info(`Cache hit for key "${cacheKey}"`);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
console.warn(`Cache miss for key "${cacheKey}"`);
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
return cachedData ? JSON.parse(JSON.stringify(cachedData)) : null;
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
console.warn(`Failed to parse cached data for key ${cacheKey}:`, err);
|
|
112
|
+
// If parsing fails, return raw value to avoid crashing
|
|
113
|
+
return cachedData;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
;
|
|
117
|
+
async function setCachedData(role, key, data, ttl) {
|
|
118
|
+
// Build a consistent cache key and store JSON-stringified data with TTL
|
|
119
|
+
const cacheKey = buildCacheKey(role, key);
|
|
120
|
+
try {
|
|
121
|
+
await redis_manager_1.redis?.set(cacheKey, JSON.stringify(data), { EX: ttl });
|
|
122
|
+
console.info(`Cache set for ${cacheKey}:`, data);
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
console.error(`Failed to set cache for ${cacheKey}:`, err);
|
|
126
|
+
}
|
|
127
|
+
// Only log permissions if role is a string or array with values
|
|
128
|
+
let roleKey;
|
|
129
|
+
if (typeof role === 'string') {
|
|
130
|
+
roleKey = role;
|
|
131
|
+
}
|
|
132
|
+
else if (Array.isArray(role) && role.length > 0) {
|
|
133
|
+
roleKey = role[0];
|
|
134
|
+
}
|
|
135
|
+
else if (role && typeof role === 'object' && 'tenantId' in role) {
|
|
136
|
+
roleKey = role.tenantId;
|
|
137
|
+
}
|
|
138
|
+
if (roleKey) {
|
|
139
|
+
const permissions = rbacTypes_1.rolePermissions[roleKey] || [];
|
|
140
|
+
if (permissions.length > 0) {
|
|
141
|
+
console.info(`Permissions cached for role "${roleKey}": ${permissions.join(', ')}`);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
console.warn(`No permissions found for role "${roleKey}" to cache`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
console.warn(`Role key is not a string, cannot cache permissions`);
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Invalidate the cache for a specific tenant and role
|
|
154
|
+
* @param tenantId The ID of the tenant
|
|
155
|
+
* @param role The role to invalidate cache for
|
|
156
|
+
* @return {Promise<void>}
|
|
157
|
+
* This function removes the cached permissions for the specified tenant and role.
|
|
158
|
+
* It is useful when permissions change and you want to ensure the cache reflects the latest state.
|
|
159
|
+
* It logs the invalidation action and deletes the cache entry from Redis.
|
|
160
|
+
* This is particularly important in multi-tenant applications where each tenant may have different permissions.
|
|
161
|
+
* @throws {Error} If there is an issue with the Redis operation
|
|
162
|
+
* @example
|
|
163
|
+
* // Invalidate cache for tenant 'tenant123' with role 'admin'
|
|
164
|
+
* await invalidateTenantCache('tenant123', 'admin');
|
|
165
|
+
*/
|
|
166
|
+
async function invalidateTenantCache(tenantId, role) {
|
|
167
|
+
try {
|
|
168
|
+
const cacheKey = `${tenantId}:permissions:${role}`;
|
|
169
|
+
const cacheExists = await redis_manager_1.redis.exists(cacheKey);
|
|
170
|
+
if (cacheExists) {
|
|
171
|
+
await redis_manager_1.redis.del(cacheKey);
|
|
172
|
+
console.info(`Cache invalidated for tenant ${tenantId} and role ${role}`);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
console.warn(`No cache found for tenant ${tenantId} and role ${role}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
console.error(`Error invalidating cache for tenant ${tenantId} and role ${role}:`, error);
|
|
180
|
+
throw new Error(`Failed to invalidate cache for tenant ${tenantId} and role ${role}`);
|
|
181
|
+
}
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Middleware to enforce RBAC (Role-Based Access Control) on GraphQL resolvers
|
|
186
|
+
* @param resolver The original resolver function
|
|
187
|
+
* @param permission The required permission for the action
|
|
188
|
+
* @returns A wrapped resolver function that checks permissions
|
|
189
|
+
* @throws {Error} If the user does not have the required permission
|
|
190
|
+
* @example
|
|
191
|
+
* // Wrap a resolver with RBAC enforcement
|
|
192
|
+
* const securedResolver = enforceRBAC(originalResolver, 'createUser');
|
|
193
|
+
*
|
|
194
|
+
* // Use the secured resolver in your GraphQL schema
|
|
195
|
+
* const resolvers = {
|
|
196
|
+
* Mutation: {
|
|
197
|
+
* createUser: securedResolver,
|
|
198
|
+
* }, };
|
|
199
|
+
*
|
|
200
|
+
* In this example, the `enforceRBAC` function wraps the `originalResolver` and checks if the user has the 'createUser' permission before allowing access to the resolver. If the user lacks the required permission, an error is thrown, preventing unauthorized access.
|
|
201
|
+
* * This middleware is essential for implementing fine-grained access control in GraphQL APIs, ensuring that only authorized users can perform specific actions based on their roles and permissions.
|
|
202
|
+
* @remarks This function is designed to be used in GraphQL resolvers to enforce role-based access control (RBAC). It checks if the user has the necessary permissions before allowing access to the resolver function. If the user lacks the required permission, an error is thrown, preventing unauthorized access.
|
|
203
|
+
* * @see {@link checkPermission} for checking user permissions
|
|
204
|
+
* * @see {@link Role} and {@link Permission} for role and permission types
|
|
205
|
+
* * @see {@link rolePermissions} for the mapping of roles to permissions
|
|
206
|
+
* * @see {@link getCachedData} and {@link setCachedData} for caching permissions
|
|
207
|
+
*/
|
|
208
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
209
|
+
function enforceRBAC(resolver, permission) {
|
|
210
|
+
const wrapped = async (parent, args, context, info) => {
|
|
211
|
+
const { user } = context;
|
|
212
|
+
// Ensure we pass the user's role (string) and await the async permission check
|
|
213
|
+
if (!user || !(await checkPermission(user.role, permission))) {
|
|
214
|
+
throw new Error('Unauthorized: You do not have permission to perform this action.');
|
|
215
|
+
}
|
|
216
|
+
return await resolver(parent, args, context, info);
|
|
217
|
+
};
|
|
218
|
+
// Preserve function name (useful for logging, debugging)
|
|
219
|
+
Object.defineProperty(wrapped, 'name', {
|
|
220
|
+
value: resolver.name || 'anonymous',
|
|
221
|
+
writable: false,
|
|
222
|
+
configurable: true,
|
|
223
|
+
});
|
|
224
|
+
// Attach metadata
|
|
225
|
+
wrapped.__originalResolver = resolver;
|
|
226
|
+
wrapped.__rbacAction = permission;
|
|
227
|
+
return wrapped;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Get the RBAC action from a wrapped resolver
|
|
231
|
+
* @param resolver The wrapped resolver function
|
|
232
|
+
* @returns The RBAC action
|
|
233
|
+
*/
|
|
234
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
235
|
+
function getRBACAction(resolver) {
|
|
236
|
+
return resolver.__rbacAction || null;
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Get the original resolver function from a wrapped resolver
|
|
240
|
+
* @param resolver The wrapped resolver function
|
|
241
|
+
* @returns The original resolver function
|
|
242
|
+
*/
|
|
243
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
|
|
244
|
+
function getOriginalResolver(resolver) {
|
|
245
|
+
return resolver.__originalResolver || resolver;
|
|
246
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rbacTypes.d.ts","sourceRoot":"","sources":["../../../src/middleware/rbac/rbacTypes.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC;AAC/C,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAEjE,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,IAAI,EAAE,UAAU,EAAE,CAItD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/plugins/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC;AAC7C,cAAc,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Document } from 'mongodb';
|
|
2
|
+
import type { AmbitenModel } from '../../lib-core';
|
|
3
|
+
import type { SoftDeleteOptions } from './types';
|
|
4
|
+
export declare function applySoftDelete<T extends Document>(model: AmbitenModel<T>, opts?: SoftDeleteOptions): AmbitenModel<T>;
|
|
5
|
+
//# sourceMappingURL=applySoftDelete.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applySoftDelete.d.ts","sourceRoot":"","sources":["../../../src/plugins/softDelete/applySoftDelete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAwB,MAAM,SAAS,CAAC;AAC9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAmEjD,wBAAgB,eAAe,CAAC,CAAC,SAAS,QAAQ,EAChD,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,EACtB,IAAI,GAAE,iBAAsB,GAC3B,YAAY,CAAC,CAAC,CAAC,CA+GjB"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applySoftDelete = applySoftDelete;
|
|
4
|
+
const DEFAULTS = {
|
|
5
|
+
field: 'deletedAt',
|
|
6
|
+
deletedAtField: 'deletedAt',
|
|
7
|
+
deletedByField: 'deletedBy',
|
|
8
|
+
isDeletedField: 'isDeleted',
|
|
9
|
+
enabled: true,
|
|
10
|
+
excludeDeletedByDefault: true
|
|
11
|
+
};
|
|
12
|
+
function buildNotDeletedFilter(options) {
|
|
13
|
+
return {
|
|
14
|
+
$and: [
|
|
15
|
+
{
|
|
16
|
+
$or: [
|
|
17
|
+
{ [options.deletedAtField]: { $exists: false } },
|
|
18
|
+
{ [options.deletedAtField]: null }
|
|
19
|
+
]
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
$or: [
|
|
23
|
+
{ [options.isDeletedField]: { $exists: false } },
|
|
24
|
+
{ [options.isDeletedField]: false }
|
|
25
|
+
]
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function buildOnlyDeletedFilter(options) {
|
|
31
|
+
return {
|
|
32
|
+
$or: [
|
|
33
|
+
{ [options.deletedAtField]: { $ne: null } },
|
|
34
|
+
{ [options.isDeletedField]: true }
|
|
35
|
+
]
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
function mergeFilter(original, extra) {
|
|
39
|
+
if (!original || Object.keys(original).length === 0) {
|
|
40
|
+
return extra;
|
|
41
|
+
}
|
|
42
|
+
return {
|
|
43
|
+
$and: [original, extra]
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
function buildSoftDeleteUpdate(options) {
|
|
47
|
+
return {
|
|
48
|
+
$set: {
|
|
49
|
+
[options.deletedAtField]: new Date(),
|
|
50
|
+
[options.isDeletedField]: true
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function applySoftDelete(model, opts = {}) {
|
|
55
|
+
const options = { ...DEFAULTS, ...opts };
|
|
56
|
+
if (!options.enabled) {
|
|
57
|
+
return model;
|
|
58
|
+
}
|
|
59
|
+
const applyReadFilter = (ctx) => {
|
|
60
|
+
if (ctx.withDeleted === true) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (ctx.onlyDeleted === true) {
|
|
64
|
+
ctx.filter = mergeFilter(ctx.filter, buildOnlyDeletedFilter(options));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (options.excludeDeletedByDefault) {
|
|
68
|
+
ctx.filter = mergeFilter(ctx.filter, buildNotDeletedFilter(options));
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
model.beforeFind((ctx) => {
|
|
72
|
+
applyReadFilter(ctx);
|
|
73
|
+
});
|
|
74
|
+
model.beforeFindOne((ctx) => {
|
|
75
|
+
applyReadFilter(ctx);
|
|
76
|
+
});
|
|
77
|
+
model.beforeFindOneAndUpdate((ctx) => {
|
|
78
|
+
applyReadFilter(ctx);
|
|
79
|
+
});
|
|
80
|
+
model.beforeFindOneAndReplace((ctx) => {
|
|
81
|
+
applyReadFilter(ctx);
|
|
82
|
+
});
|
|
83
|
+
model.beforeFindOneAndUpsert((ctx) => {
|
|
84
|
+
applyReadFilter(ctx);
|
|
85
|
+
});
|
|
86
|
+
model.beforeAggregate((ctx) => {
|
|
87
|
+
if (ctx.withDeleted === true) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const stage = ctx.onlyDeleted === true
|
|
91
|
+
? { $match: buildOnlyDeletedFilter(options) }
|
|
92
|
+
: options.excludeDeletedByDefault
|
|
93
|
+
? { $match: buildNotDeletedFilter(options) }
|
|
94
|
+
: null;
|
|
95
|
+
if (!stage) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
ctx.pipeline = [stage, ...(ctx.pipeline ?? [])];
|
|
99
|
+
});
|
|
100
|
+
model.beforeDeleteOne((ctx) => {
|
|
101
|
+
if (ctx.hardDelete === true) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
ctx.filter = mergeFilter(ctx.filter, buildNotDeletedFilter(options));
|
|
105
|
+
ctx.update = buildSoftDeleteUpdate(options);
|
|
106
|
+
ctx.meta = {
|
|
107
|
+
...(ctx.meta ?? {}),
|
|
108
|
+
softDelete: true
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
model.beforeDeleteMany((ctx) => {
|
|
112
|
+
if (ctx.hardDelete === true) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
ctx.filter = mergeFilter(ctx.filter, buildNotDeletedFilter(options));
|
|
116
|
+
ctx.update = buildSoftDeleteUpdate(options);
|
|
117
|
+
ctx.meta = {
|
|
118
|
+
...(ctx.meta ?? {}),
|
|
119
|
+
softDelete: true
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
model.beforeFindOneAndDelete((ctx) => {
|
|
123
|
+
ctx.filter = mergeFilter(ctx.filter, buildNotDeletedFilter(options));
|
|
124
|
+
if (ctx.hardDelete === true) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
ctx.update = buildSoftDeleteUpdate(options);
|
|
128
|
+
ctx.meta = {
|
|
129
|
+
...(ctx.meta ?? {}),
|
|
130
|
+
softDelete: true
|
|
131
|
+
};
|
|
132
|
+
});
|
|
133
|
+
model.setSoftDeleteConfig({
|
|
134
|
+
deletedAtField: options.deletedAtField,
|
|
135
|
+
isDeletedField: options.isDeletedField
|
|
136
|
+
});
|
|
137
|
+
return model;
|
|
138
|
+
}
|
|
139
|
+
;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface SoftDeleteOptions {
|
|
2
|
+
field?: string;
|
|
3
|
+
deletedAtField?: string;
|
|
4
|
+
deletedByField?: string;
|
|
5
|
+
isDeletedField?: string;
|
|
6
|
+
enabled?: boolean;
|
|
7
|
+
excludeDeletedByDefault?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface SoftDeletableDocument extends Document {
|
|
10
|
+
deletedAt?: Date | null;
|
|
11
|
+
isDeleted?: boolean;
|
|
12
|
+
deletedBy?: string | null;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/plugins/softDelete/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,iBAAiB;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,uBAAuB,CAAC,EAAE,OAAO,CAAC;CACnC;AAED,MAAM,WAAW,qBAAsB,SAAQ,QAAQ;IACrD,SAAS,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;IACxB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/redis-manager/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
type AmbitenRedisClient = {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
connect(): Promise<any>;
|
|
5
|
+
disconnect(): Promise<any>;
|
|
6
|
+
quit?: () => Promise<any>;
|
|
7
|
+
publish(channel: string, message: string): Promise<any>;
|
|
8
|
+
subscribe(channel: string, listener: any): Promise<any>;
|
|
9
|
+
duplicate(): AmbitenRedisClient;
|
|
10
|
+
get(key: string): Promise<string | null>;
|
|
11
|
+
set(key: string, value: any, options?: any): Promise<any>;
|
|
12
|
+
del(...keys: string[]): Promise<number>;
|
|
13
|
+
exists(key: string): Promise<number>;
|
|
14
|
+
scan(cursor?: number | string, options?: any): Promise<any>;
|
|
15
|
+
multi(): any;
|
|
16
|
+
};
|
|
17
|
+
type RedisLike = AmbitenRedisClient & {
|
|
18
|
+
isOpen: boolean;
|
|
19
|
+
scan?: (...args: any[]) => Promise<any>;
|
|
20
|
+
};
|
|
21
|
+
export declare const redis: any;
|
|
22
|
+
export declare class RedisService {
|
|
23
|
+
static getInstance(): RedisService;
|
|
24
|
+
connect(url?: string): Promise<void>;
|
|
25
|
+
getClient(): Promise<RedisLike>;
|
|
26
|
+
close(): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export declare function connectRedis(): Promise<{
|
|
29
|
+
connect: (url?: string) => Promise<any>;
|
|
30
|
+
}>;
|
|
31
|
+
export {};
|
|
32
|
+
//# sourceMappingURL=redisClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"redisClient.d.ts","sourceRoot":"","sources":["../../src/redis-manager/redisClient.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC;AAIvB,KAAK,kBAAkB,GAAG;IACxB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;IACxB,UAAU,IAAI,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1B,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACxD,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IACxD,SAAS,IAAI,kBAAkB,CAAC;IAChC,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IACzC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1D,GAAG,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACxC,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC5D,KAAK,IAAI,GAAG,CAAC;CACd,CAAC;AASF,KAAK,SAAS,GAAG,kBAAkB,GAAG;IACpC,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC;CACzC,CAAC;AAiCF,eAAO,MAAM,KAAK,EAAE,GAkHnB,CAAC;AAEF,qBAAa,YAAY;IACvB,MAAM,CAAC,WAAW,IAAI,YAAY;IAI5B,OAAO,CAAC,GAAG,CAAC,EAAE,MAAM;IAI1B,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC;IAIzB,KAAK;CAGZ;AAED,wBAAsB,YAAY;oBAER,MAAM;GAE/B"}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RedisService = exports.redis = void 0;
|
|
4
|
+
exports.connectRedis = connectRedis;
|
|
5
|
+
require("dotenv/config");
|
|
6
|
+
const redis_1 = require("redis");
|
|
7
|
+
const DEFAULT_RECONNECT = (retries) => Math.min(retries * 50, 1000);
|
|
8
|
+
const redisUrl = process.env.REDIS_URI;
|
|
9
|
+
let currentClient = null;
|
|
10
|
+
const createStub = () => ({
|
|
11
|
+
isOpen: false,
|
|
12
|
+
connect: async () => null,
|
|
13
|
+
disconnect: async () => null,
|
|
14
|
+
publish: async () => 0,
|
|
15
|
+
subscribe: async () => null,
|
|
16
|
+
duplicate: () => createStub(),
|
|
17
|
+
get: async () => null,
|
|
18
|
+
set: async () => 'OK',
|
|
19
|
+
del: async () => 0,
|
|
20
|
+
exists: async () => 0,
|
|
21
|
+
scan: async () => ({
|
|
22
|
+
cursor: 0,
|
|
23
|
+
keys: [],
|
|
24
|
+
}),
|
|
25
|
+
multi: () => ({
|
|
26
|
+
incr() {
|
|
27
|
+
return this;
|
|
28
|
+
},
|
|
29
|
+
expire() {
|
|
30
|
+
return this;
|
|
31
|
+
},
|
|
32
|
+
exec: async () => [],
|
|
33
|
+
}),
|
|
34
|
+
});
|
|
35
|
+
exports.redis = {
|
|
36
|
+
async getClient(uri) {
|
|
37
|
+
if (currentClient && currentClient.isOpen) {
|
|
38
|
+
return currentClient;
|
|
39
|
+
}
|
|
40
|
+
const url = uri || redisUrl;
|
|
41
|
+
if (!url) {
|
|
42
|
+
return createStub();
|
|
43
|
+
}
|
|
44
|
+
try {
|
|
45
|
+
const client = (0, redis_1.createClient)({
|
|
46
|
+
url,
|
|
47
|
+
socket: {
|
|
48
|
+
reconnectStrategy: DEFAULT_RECONNECT,
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
await client.connect();
|
|
52
|
+
currentClient = client;
|
|
53
|
+
return client;
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return createStub();
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
async disconnect() {
|
|
60
|
+
if (currentClient) {
|
|
61
|
+
try {
|
|
62
|
+
if (currentClient.isOpen &&
|
|
63
|
+
typeof currentClient.disconnect === 'function') {
|
|
64
|
+
await currentClient.disconnect();
|
|
65
|
+
}
|
|
66
|
+
else if (currentClient.isOpen &&
|
|
67
|
+
typeof currentClient.quit === 'function') {
|
|
68
|
+
await currentClient.quit();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// swallow disconnect errors
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
currentClient = null;
|
|
76
|
+
},
|
|
77
|
+
get isOpen() {
|
|
78
|
+
return !!currentClient?.isOpen;
|
|
79
|
+
},
|
|
80
|
+
async get(key) {
|
|
81
|
+
const client = await this.getClient();
|
|
82
|
+
return client.get?.(key);
|
|
83
|
+
},
|
|
84
|
+
async set(key, value, options) {
|
|
85
|
+
const client = await this.getClient();
|
|
86
|
+
return client.set?.(key, value, options);
|
|
87
|
+
},
|
|
88
|
+
async del(...keys) {
|
|
89
|
+
const client = await this.getClient();
|
|
90
|
+
return client.del?.(keys);
|
|
91
|
+
},
|
|
92
|
+
async exists(key) {
|
|
93
|
+
const client = await this.getClient();
|
|
94
|
+
return client.exists?.(key);
|
|
95
|
+
},
|
|
96
|
+
async scan(cursor = 0, options) {
|
|
97
|
+
const client = await this.getClient();
|
|
98
|
+
if (typeof client.scan !== 'function') {
|
|
99
|
+
return {
|
|
100
|
+
cursor: 0,
|
|
101
|
+
keys: [],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
return client.scan(cursor, options);
|
|
105
|
+
},
|
|
106
|
+
async publish(channel, message) {
|
|
107
|
+
const client = await this.getClient();
|
|
108
|
+
return client.publish?.(channel, message);
|
|
109
|
+
},
|
|
110
|
+
async subscribe(channel, listener) {
|
|
111
|
+
const client = await this.getClient();
|
|
112
|
+
return client.subscribe?.(channel, listener);
|
|
113
|
+
},
|
|
114
|
+
duplicate() {
|
|
115
|
+
if (currentClient?.duplicate) {
|
|
116
|
+
return currentClient.duplicate();
|
|
117
|
+
}
|
|
118
|
+
return createStub();
|
|
119
|
+
},
|
|
120
|
+
multi() {
|
|
121
|
+
if (currentClient?.multi) {
|
|
122
|
+
return currentClient.multi();
|
|
123
|
+
}
|
|
124
|
+
return createStub().multi?.();
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
class RedisService {
|
|
128
|
+
static getInstance() {
|
|
129
|
+
return new RedisService();
|
|
130
|
+
}
|
|
131
|
+
async connect(url) {
|
|
132
|
+
await exports.redis.getClient(url);
|
|
133
|
+
}
|
|
134
|
+
getClient() {
|
|
135
|
+
return exports.redis.getClient();
|
|
136
|
+
}
|
|
137
|
+
async close() {
|
|
138
|
+
await exports.redis.disconnect();
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
exports.RedisService = RedisService;
|
|
142
|
+
async function connectRedis() {
|
|
143
|
+
return {
|
|
144
|
+
connect: async (url) => exports.redis.getClient(url),
|
|
145
|
+
};
|
|
146
|
+
}
|