@nest-omni/core 4.1.3-19 → 4.1.3-20
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/cache/cache.module.d.ts +0 -6
- package/cache/cache.module.js +7 -7
- package/cache/cache.service.js +12 -0
- package/cache/dependencies/db.dependency.d.ts +0 -13
- package/cache/dependencies/db.dependency.js +0 -16
- package/cache/dependencies/tag.dependency.d.ts +39 -4
- package/cache/dependencies/tag.dependency.js +109 -11
- package/cache/interfaces/cache-options.interface.d.ts +8 -0
- package/cache/providers/memory-cache.provider.d.ts +20 -0
- package/cache/providers/memory-cache.provider.js +40 -0
- package/http-client/config/http-client.config.d.ts +5 -0
- package/http-client/config/http-client.config.js +24 -13
- package/http-client/decorators/http-client.decorators.d.ts +1 -25
- package/http-client/decorators/http-client.decorators.js +97 -90
- package/http-client/entities/http-log.entity.d.ts +0 -20
- package/http-client/entities/http-log.entity.js +0 -12
- package/http-client/examples/advanced-usage.example.d.ts +4 -5
- package/http-client/examples/advanced-usage.example.js +4 -56
- package/http-client/http-client.module.d.ts +35 -2
- package/http-client/http-client.module.js +80 -75
- package/http-client/index.d.ts +1 -1
- package/http-client/interfaces/api-client-config.interface.d.ts +1 -91
- package/http-client/interfaces/http-client-config.interface.d.ts +53 -62
- package/http-client/services/api-client-registry.service.d.ts +5 -23
- package/http-client/services/api-client-registry.service.js +41 -284
- package/http-client/services/circuit-breaker.service.d.ts +69 -2
- package/http-client/services/circuit-breaker.service.js +185 -7
- package/http-client/services/http-client.service.d.ts +58 -23
- package/http-client/services/http-client.service.js +294 -150
- package/http-client/services/http-log-query.service.js +0 -13
- package/http-client/services/index.d.ts +0 -1
- package/http-client/services/index.js +0 -1
- package/http-client/services/logging.service.d.ts +79 -10
- package/http-client/services/logging.service.js +246 -51
- package/http-client/utils/call-stack-extractor.util.d.ts +26 -0
- package/http-client/utils/call-stack-extractor.util.js +35 -0
- package/http-client/utils/security-validator.util.d.ts +118 -0
- package/http-client/utils/security-validator.util.js +352 -0
- package/package.json +1 -1
- package/redis-lock/lock-heartbeat.service.d.ts +2 -0
- package/redis-lock/lock-heartbeat.service.js +12 -2
- package/redis-lock/redis-lock.service.d.ts +4 -0
- package/redis-lock/redis-lock.service.js +61 -8
- package/http-client/services/cache.service.d.ts +0 -76
- package/http-client/services/cache.service.js +0 -333
package/cache/cache.module.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DynamicModule, OnModuleInit } from '@nestjs/common';
|
|
2
|
-
import type { DataSource } from 'typeorm';
|
|
3
2
|
import type { Redis } from 'ioredis';
|
|
4
3
|
import { CacheService } from './cache.service';
|
|
5
4
|
/**
|
|
@@ -20,11 +19,6 @@ export interface CacheModuleOptions {
|
|
|
20
19
|
* If not provided, Redis caching will be disabled
|
|
21
20
|
*/
|
|
22
21
|
redisClient?: Redis;
|
|
23
|
-
/**
|
|
24
|
-
* TypeORM DataSource for DbDependency
|
|
25
|
-
* If not provided, DbDependency will throw error when used
|
|
26
|
-
*/
|
|
27
|
-
dataSource?: DataSource;
|
|
28
22
|
/**
|
|
29
23
|
* Default TTL for memory cache in milliseconds
|
|
30
24
|
*/
|
package/cache/cache.module.js
CHANGED
|
@@ -80,10 +80,10 @@ let CacheModule = CacheModule_1 = class CacheModule {
|
|
|
80
80
|
* Register cache module with options
|
|
81
81
|
*/
|
|
82
82
|
static forRoot(options = {}) {
|
|
83
|
-
const { isGlobal = true, redisClient,
|
|
84
|
-
// Set
|
|
85
|
-
if (
|
|
86
|
-
dependencies_1.
|
|
83
|
+
const { isGlobal = true, redisClient, memoryTtl, memoryNamespace, enableCompression = false, compressionThreshold = 1024, } = options;
|
|
84
|
+
// Set Redis client for TagDependency for distributed tag support
|
|
85
|
+
if (redisClient) {
|
|
86
|
+
dependencies_1.TagDependency.setRedisClient(redisClient);
|
|
87
87
|
}
|
|
88
88
|
return {
|
|
89
89
|
module: CacheModule_1,
|
|
@@ -161,9 +161,9 @@ let CacheModule = CacheModule_1 = class CacheModule {
|
|
|
161
161
|
{
|
|
162
162
|
provide: providers_1.MemoryCacheProvider,
|
|
163
163
|
useFactory: (moduleOptions) => {
|
|
164
|
-
// Set
|
|
165
|
-
if (moduleOptions.
|
|
166
|
-
dependencies_1.
|
|
164
|
+
// Set Redis client for TagDependency for distributed tag support
|
|
165
|
+
if (moduleOptions.redisClient) {
|
|
166
|
+
dependencies_1.TagDependency.setRedisClient(moduleOptions.redisClient);
|
|
167
167
|
}
|
|
168
168
|
return new providers_1.MemoryCacheProvider({
|
|
169
169
|
ttl: moduleOptions.memoryTtl,
|
package/cache/cache.service.js
CHANGED
|
@@ -333,6 +333,18 @@ let CacheService = CacheService_1 = class CacheService {
|
|
|
333
333
|
setWithOptions(key, value, options) {
|
|
334
334
|
return __awaiter(this, void 0, void 0, function* () {
|
|
335
335
|
this.stats.totalSets++;
|
|
336
|
+
// Validate TTL if provided
|
|
337
|
+
if ((options === null || options === void 0 ? void 0 : options.ttl) !== undefined) {
|
|
338
|
+
if (typeof options.ttl !== 'number' || options.ttl < 0) {
|
|
339
|
+
this.logger.warn(`Invalid TTL value: ${options.ttl}. TTL must be a non-negative number. Using default behavior.`);
|
|
340
|
+
// Continue without TTL
|
|
341
|
+
options.ttl = undefined;
|
|
342
|
+
}
|
|
343
|
+
else if (options.ttl === 0) {
|
|
344
|
+
// TTL of 0 means no expiration
|
|
345
|
+
options.ttl = undefined;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
336
348
|
// Check condition
|
|
337
349
|
if ((options === null || options === void 0 ? void 0 : options.condition) && !(yield Promise.resolve(options.condition()))) {
|
|
338
350
|
return;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { DataSource } from 'typeorm';
|
|
2
1
|
import type { CacheDependency } from '../interfaces';
|
|
3
2
|
/**
|
|
4
3
|
* Options for DbDependency
|
|
@@ -77,18 +76,6 @@ export declare class DbDependency implements CacheDependency {
|
|
|
77
76
|
private static fallbackDataSource;
|
|
78
77
|
private static defaultDataSourceName;
|
|
79
78
|
constructor(sql: string, paramsFactory?: (...args: any[]) => any[], options?: DbDependencyOptions);
|
|
80
|
-
/**
|
|
81
|
-
* Set a fallback DataSource for database queries
|
|
82
|
-
* This is used when DataSourceUtil cannot provide a DataSource
|
|
83
|
-
* This method is deprecated - use DataSourceUtil.registerDataSource() instead
|
|
84
|
-
* @deprecated Use DataSourceUtil.registerDataSource() to register DataSources
|
|
85
|
-
*/
|
|
86
|
-
static setDataSource(dataSource: DataSource): void;
|
|
87
|
-
/**
|
|
88
|
-
* Set the default data source name for DbDependency
|
|
89
|
-
* @param name The data source name (default: 'default')
|
|
90
|
-
*/
|
|
91
|
-
static setDefaultDataSourceName(name: string): void;
|
|
92
79
|
/**
|
|
93
80
|
* Get DataSource for executing queries
|
|
94
81
|
* Priority: 1. Specific dataSourceName from options, 2. Default from getDataSourceByName, 3. Fallback
|
|
@@ -75,22 +75,6 @@ class DbDependency {
|
|
|
75
75
|
throw new Error('DbDependency requires a valid SQL query');
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
|
-
/**
|
|
79
|
-
* Set a fallback DataSource for database queries
|
|
80
|
-
* This is used when DataSourceUtil cannot provide a DataSource
|
|
81
|
-
* This method is deprecated - use DataSourceUtil.registerDataSource() instead
|
|
82
|
-
* @deprecated Use DataSourceUtil.registerDataSource() to register DataSources
|
|
83
|
-
*/
|
|
84
|
-
static setDataSource(dataSource) {
|
|
85
|
-
this.fallbackDataSource = dataSource;
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* Set the default data source name for DbDependency
|
|
89
|
-
* @param name The data source name (default: 'default')
|
|
90
|
-
*/
|
|
91
|
-
static setDefaultDataSourceName(name) {
|
|
92
|
-
this.defaultDataSourceName = name;
|
|
93
|
-
}
|
|
94
78
|
/**
|
|
95
79
|
* Get DataSource for executing queries
|
|
96
80
|
* Priority: 1. Specific dataSourceName from options, 2. Default from getDataSourceByName, 3. Fallback
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CacheDependency } from '../interfaces/cache-dependency.interface';
|
|
2
|
+
import type Redis from 'ioredis';
|
|
2
3
|
/**
|
|
3
4
|
* Tag-based cache dependency
|
|
4
5
|
*
|
|
@@ -7,6 +8,9 @@ import type { CacheDependency } from '../interfaces/cache-dependency.interface';
|
|
|
7
8
|
*
|
|
8
9
|
* This is the most commonly used dependency type for managing related cache entries.
|
|
9
10
|
*
|
|
11
|
+
* **Distributed Support**: Tag versions are stored in Redis for multi-instance deployments.
|
|
12
|
+
* Falls back to in-memory storage if Redis is not available.
|
|
13
|
+
*
|
|
10
14
|
* @example
|
|
11
15
|
* ```typescript
|
|
12
16
|
* // Cache user list with tag dependency
|
|
@@ -23,28 +27,59 @@ import type { CacheDependency } from '../interfaces/cache-dependency.interface';
|
|
|
23
27
|
export declare class TagDependency implements CacheDependency {
|
|
24
28
|
private readonly tags;
|
|
25
29
|
private static readonly PREFIX;
|
|
30
|
+
private static readonly REDIS_KEY_PREFIX;
|
|
26
31
|
private static tagVersions;
|
|
32
|
+
private static redis;
|
|
27
33
|
constructor(tags: string[]);
|
|
34
|
+
/**
|
|
35
|
+
* Set Redis client for distributed tag version storage
|
|
36
|
+
* Called by CacheModule during initialization
|
|
37
|
+
*
|
|
38
|
+
* @param redis - Redis client instance
|
|
39
|
+
*/
|
|
40
|
+
static setRedisClient(redis: Redis): void;
|
|
28
41
|
/**
|
|
29
42
|
* Get current version of a tag
|
|
43
|
+
* Prioritizes Redis, falls back to in-memory storage
|
|
44
|
+
*
|
|
45
|
+
* @param tag - Tag name
|
|
46
|
+
* @returns Current tag version
|
|
30
47
|
*/
|
|
31
|
-
static getTagVersion(tag: string): number
|
|
48
|
+
static getTagVersion(tag: string): Promise<number>;
|
|
32
49
|
/**
|
|
33
50
|
* Invalidate a tag (increment its version)
|
|
51
|
+
* Updates both Redis and in-memory storage
|
|
52
|
+
*
|
|
53
|
+
* @param tag - Tag name to invalidate
|
|
34
54
|
*/
|
|
35
|
-
static invalidateTag(tag: string): void
|
|
55
|
+
static invalidateTag(tag: string): Promise<void>;
|
|
36
56
|
/**
|
|
37
57
|
* Invalidate multiple tags
|
|
58
|
+
*
|
|
59
|
+
* @param tags - Array of tag names to invalidate
|
|
38
60
|
*/
|
|
39
|
-
static invalidateTags(tags: string[]): void
|
|
61
|
+
static invalidateTags(tags: string[]): Promise<void>;
|
|
40
62
|
/**
|
|
41
63
|
* Reset all tag versions (useful for testing)
|
|
64
|
+
* Clears both Redis and in-memory storage
|
|
42
65
|
*/
|
|
43
|
-
static resetAllTags(): void
|
|
66
|
+
static resetAllTags(): Promise<void>;
|
|
44
67
|
/**
|
|
45
68
|
* Get all tags and their versions
|
|
69
|
+
* Note: This only returns in-memory tags for backward compatibility
|
|
70
|
+
*
|
|
71
|
+
* @returns Map of tag names to versions
|
|
46
72
|
*/
|
|
47
73
|
static getAllTags(): Map<string, number>;
|
|
74
|
+
/**
|
|
75
|
+
* Get tag version (synchronous version for backward compatibility)
|
|
76
|
+
* Only reads from in-memory storage
|
|
77
|
+
*
|
|
78
|
+
* @deprecated Use getTagVersion(tag) instead for distributed support
|
|
79
|
+
* @param tag - Tag name
|
|
80
|
+
* @returns Current tag version from memory
|
|
81
|
+
*/
|
|
82
|
+
static getTagVersionSync(tag: string): number;
|
|
48
83
|
getKey(): string;
|
|
49
84
|
getData(): Promise<Record<string, number>>;
|
|
50
85
|
isChanged(oldData: Record<string, number>): Promise<boolean>;
|
|
@@ -18,6 +18,9 @@ exports.TagDependency = void 0;
|
|
|
18
18
|
*
|
|
19
19
|
* This is the most commonly used dependency type for managing related cache entries.
|
|
20
20
|
*
|
|
21
|
+
* **Distributed Support**: Tag versions are stored in Redis for multi-instance deployments.
|
|
22
|
+
* Falls back to in-memory storage if Redis is not available.
|
|
23
|
+
*
|
|
21
24
|
* @example
|
|
22
25
|
* ```typescript
|
|
23
26
|
* // Cache user list with tag dependency
|
|
@@ -38,38 +41,129 @@ class TagDependency {
|
|
|
38
41
|
throw new Error('TagDependency requires at least one tag');
|
|
39
42
|
}
|
|
40
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Set Redis client for distributed tag version storage
|
|
46
|
+
* Called by CacheModule during initialization
|
|
47
|
+
*
|
|
48
|
+
* @param redis - Redis client instance
|
|
49
|
+
*/
|
|
50
|
+
static setRedisClient(redis) {
|
|
51
|
+
TagDependency.redis = redis;
|
|
52
|
+
}
|
|
41
53
|
/**
|
|
42
54
|
* Get current version of a tag
|
|
55
|
+
* Prioritizes Redis, falls back to in-memory storage
|
|
56
|
+
*
|
|
57
|
+
* @param tag - Tag name
|
|
58
|
+
* @returns Current tag version
|
|
43
59
|
*/
|
|
44
60
|
static getTagVersion(tag) {
|
|
45
|
-
return this
|
|
61
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
// Try to get from Redis first
|
|
63
|
+
if (TagDependency.redis) {
|
|
64
|
+
try {
|
|
65
|
+
const key = `${TagDependency.REDIS_KEY_PREFIX}:${tag}`;
|
|
66
|
+
const result = yield TagDependency.redis.get(key);
|
|
67
|
+
if (result !== null) {
|
|
68
|
+
const version = parseInt(result, 10);
|
|
69
|
+
// Cache in memory for faster access
|
|
70
|
+
TagDependency.tagVersions.set(tag, version);
|
|
71
|
+
return version;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
// Log warning but don't throw - fall back to memory
|
|
76
|
+
console.warn(`[TagDependency] Failed to get tag version from Redis: ${error instanceof Error ? error.message : String(error)}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// Fall back to in-memory storage
|
|
80
|
+
return TagDependency.tagVersions.get(tag) || 0;
|
|
81
|
+
});
|
|
46
82
|
}
|
|
47
83
|
/**
|
|
48
84
|
* Invalidate a tag (increment its version)
|
|
85
|
+
* Updates both Redis and in-memory storage
|
|
86
|
+
*
|
|
87
|
+
* @param tag - Tag name to invalidate
|
|
49
88
|
*/
|
|
50
89
|
static invalidateTag(tag) {
|
|
51
|
-
|
|
52
|
-
|
|
90
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
91
|
+
const currentVersion = TagDependency.tagVersions.get(tag) || 0;
|
|
92
|
+
const newVersion = currentVersion + 1;
|
|
93
|
+
// Update in-memory storage
|
|
94
|
+
TagDependency.tagVersions.set(tag, newVersion);
|
|
95
|
+
// Update Redis if available
|
|
96
|
+
if (TagDependency.redis) {
|
|
97
|
+
try {
|
|
98
|
+
const key = `${TagDependency.REDIS_KEY_PREFIX}:${tag}`;
|
|
99
|
+
yield TagDependency.redis.set(key, newVersion.toString());
|
|
100
|
+
}
|
|
101
|
+
catch (error) {
|
|
102
|
+
console.error(`[TagDependency] Failed to invalidate tag in Redis: ${error instanceof Error ? error.message : String(error)}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
});
|
|
53
106
|
}
|
|
54
107
|
/**
|
|
55
108
|
* Invalidate multiple tags
|
|
109
|
+
*
|
|
110
|
+
* @param tags - Array of tag names to invalidate
|
|
56
111
|
*/
|
|
57
112
|
static invalidateTags(tags) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
}
|
|
113
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
114
|
+
yield Promise.all(tags.map((tag) => TagDependency.invalidateTag(tag)));
|
|
115
|
+
});
|
|
61
116
|
}
|
|
62
117
|
/**
|
|
63
118
|
* Reset all tag versions (useful for testing)
|
|
119
|
+
* Clears both Redis and in-memory storage
|
|
64
120
|
*/
|
|
65
121
|
static resetAllTags() {
|
|
66
|
-
this
|
|
122
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
123
|
+
// Clear in-memory storage
|
|
124
|
+
TagDependency.tagVersions.clear();
|
|
125
|
+
// Clear Redis storage
|
|
126
|
+
if (TagDependency.redis) {
|
|
127
|
+
try {
|
|
128
|
+
let cursor = '0';
|
|
129
|
+
const pattern = `${TagDependency.REDIS_KEY_PREFIX}:*`;
|
|
130
|
+
const keysToDelete = [];
|
|
131
|
+
// Use SCAN to find all tag version keys
|
|
132
|
+
do {
|
|
133
|
+
const [nextCursor, keys] = yield TagDependency.redis.scan(cursor, 'MATCH', pattern, 'COUNT', 100);
|
|
134
|
+
cursor = nextCursor;
|
|
135
|
+
keysToDelete.push(...keys);
|
|
136
|
+
} while (cursor !== '0');
|
|
137
|
+
// Delete all found keys
|
|
138
|
+
if (keysToDelete.length > 0) {
|
|
139
|
+
yield TagDependency.redis.del(...keysToDelete);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
console.error(`[TagDependency] Failed to reset tags in Redis: ${error instanceof Error ? error.message : String(error)}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
67
147
|
}
|
|
68
148
|
/**
|
|
69
149
|
* Get all tags and their versions
|
|
150
|
+
* Note: This only returns in-memory tags for backward compatibility
|
|
151
|
+
*
|
|
152
|
+
* @returns Map of tag names to versions
|
|
70
153
|
*/
|
|
71
154
|
static getAllTags() {
|
|
72
|
-
return new Map(
|
|
155
|
+
return new Map(TagDependency.tagVersions);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Get tag version (synchronous version for backward compatibility)
|
|
159
|
+
* Only reads from in-memory storage
|
|
160
|
+
*
|
|
161
|
+
* @deprecated Use getTagVersion(tag) instead for distributed support
|
|
162
|
+
* @param tag - Tag name
|
|
163
|
+
* @returns Current tag version from memory
|
|
164
|
+
*/
|
|
165
|
+
static getTagVersionSync(tag) {
|
|
166
|
+
return TagDependency.tagVersions.get(tag) || 0;
|
|
73
167
|
}
|
|
74
168
|
getKey() {
|
|
75
169
|
return `tag:${this.tags.sort().join(',')}`;
|
|
@@ -78,7 +172,7 @@ class TagDependency {
|
|
|
78
172
|
return __awaiter(this, void 0, void 0, function* () {
|
|
79
173
|
const data = {};
|
|
80
174
|
for (const tag of this.tags) {
|
|
81
|
-
data[tag] = TagDependency.getTagVersion(tag);
|
|
175
|
+
data[tag] = yield TagDependency.getTagVersion(tag);
|
|
82
176
|
}
|
|
83
177
|
return data;
|
|
84
178
|
});
|
|
@@ -89,7 +183,7 @@ class TagDependency {
|
|
|
89
183
|
return true;
|
|
90
184
|
}
|
|
91
185
|
for (const tag of this.tags) {
|
|
92
|
-
const currentVersion = TagDependency.getTagVersion(tag);
|
|
186
|
+
const currentVersion = yield TagDependency.getTagVersion(tag);
|
|
93
187
|
const oldVersion = oldData[tag];
|
|
94
188
|
if (currentVersion !== oldVersion) {
|
|
95
189
|
return true;
|
|
@@ -101,11 +195,15 @@ class TagDependency {
|
|
|
101
195
|
reset() {
|
|
102
196
|
return __awaiter(this, void 0, void 0, function* () {
|
|
103
197
|
for (const tag of this.tags) {
|
|
104
|
-
TagDependency.invalidateTag(tag);
|
|
198
|
+
yield TagDependency.invalidateTag(tag);
|
|
105
199
|
}
|
|
106
200
|
});
|
|
107
201
|
}
|
|
108
202
|
}
|
|
109
203
|
exports.TagDependency = TagDependency;
|
|
110
204
|
TagDependency.PREFIX = 'cache:tag:';
|
|
205
|
+
TagDependency.REDIS_KEY_PREFIX = 'cache:tag:versions';
|
|
206
|
+
// In-memory fallback for when Redis is not available
|
|
111
207
|
TagDependency.tagVersions = new Map();
|
|
208
|
+
// Redis client for distributed tag version storage
|
|
209
|
+
TagDependency.redis = null;
|
|
@@ -35,6 +35,14 @@ export interface CacheOptions {
|
|
|
35
35
|
* @returns true to cache, false to skip
|
|
36
36
|
*/
|
|
37
37
|
condition?: (...args: any[]) => boolean | Promise<boolean>;
|
|
38
|
+
/**
|
|
39
|
+
* Unless condition - skip caching when this condition is true
|
|
40
|
+
* Can be a function (safe) or a string expression (use with caution)
|
|
41
|
+
* @param result - Method return value
|
|
42
|
+
* @param args - Method arguments
|
|
43
|
+
* @returns true to skip caching, false to cache normally
|
|
44
|
+
*/
|
|
45
|
+
unless?: ((result: any, args: any[]) => boolean) | string;
|
|
38
46
|
/**
|
|
39
47
|
* Cache dependencies - cache will be invalidated when dependencies change
|
|
40
48
|
* Similar to Yii2 cache dependency system
|
|
@@ -6,16 +6,36 @@ import { BaseCacheProvider } from './base-cache.provider';
|
|
|
6
6
|
* Data is stored in memory and shared across all requests in the same process.
|
|
7
7
|
*
|
|
8
8
|
* Faster than Redis but not shared across multiple processes/servers.
|
|
9
|
+
*
|
|
10
|
+
* **Automatic Cleanup**: Includes periodic cleanup of expired items to prevent memory leaks.
|
|
9
11
|
*/
|
|
10
12
|
export declare class MemoryCacheProvider extends BaseCacheProvider {
|
|
11
13
|
private cache;
|
|
12
14
|
private keys;
|
|
13
15
|
private defaultTtl?;
|
|
14
16
|
private namespace;
|
|
17
|
+
private cleanupTimer?;
|
|
18
|
+
/**
|
|
19
|
+
* Cleanup interval in milliseconds
|
|
20
|
+
* @default 300000 (5 minutes)
|
|
21
|
+
*/
|
|
22
|
+
private readonly CLEANUP_INTERVAL;
|
|
15
23
|
constructor(options?: {
|
|
16
24
|
ttl?: number;
|
|
17
25
|
namespace?: string;
|
|
18
26
|
});
|
|
27
|
+
/**
|
|
28
|
+
* Start periodic cleanup of expired items
|
|
29
|
+
*/
|
|
30
|
+
private startCleanupTimer;
|
|
31
|
+
/**
|
|
32
|
+
* Stop automatic cleanup timer
|
|
33
|
+
*/
|
|
34
|
+
private stopCleanupTimer;
|
|
35
|
+
/**
|
|
36
|
+
* Destroy the provider and cleanup resources
|
|
37
|
+
*/
|
|
38
|
+
destroy(): void;
|
|
19
39
|
getName(): string;
|
|
20
40
|
get<T>(key: string): Promise<T | null>;
|
|
21
41
|
set<T>(key: string, value: T, ttl?: number): Promise<void>;
|
|
@@ -18,14 +18,54 @@ const base_cache_provider_1 = require("./base-cache.provider");
|
|
|
18
18
|
* Data is stored in memory and shared across all requests in the same process.
|
|
19
19
|
*
|
|
20
20
|
* Faster than Redis but not shared across multiple processes/servers.
|
|
21
|
+
*
|
|
22
|
+
* **Automatic Cleanup**: Includes periodic cleanup of expired items to prevent memory leaks.
|
|
21
23
|
*/
|
|
22
24
|
class MemoryCacheProvider extends base_cache_provider_1.BaseCacheProvider {
|
|
23
25
|
constructor(options) {
|
|
24
26
|
super();
|
|
25
27
|
this.cache = new Map();
|
|
26
28
|
this.keys = new Set();
|
|
29
|
+
/**
|
|
30
|
+
* Cleanup interval in milliseconds
|
|
31
|
+
* @default 300000 (5 minutes)
|
|
32
|
+
*/
|
|
33
|
+
this.CLEANUP_INTERVAL = 5 * 60 * 1000;
|
|
27
34
|
this.defaultTtl = options === null || options === void 0 ? void 0 : options.ttl;
|
|
28
35
|
this.namespace = (options === null || options === void 0 ? void 0 : options.namespace) || 'cache:memory';
|
|
36
|
+
// Start automatic cleanup timer
|
|
37
|
+
this.startCleanupTimer();
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Start periodic cleanup of expired items
|
|
41
|
+
*/
|
|
42
|
+
startCleanupTimer() {
|
|
43
|
+
this.cleanupTimer = setInterval(() => {
|
|
44
|
+
const cleaned = this.cleanup();
|
|
45
|
+
if (cleaned > 0) {
|
|
46
|
+
console.debug(`[MemoryCache] Cleaned up ${cleaned} expired items from namespace: ${this.namespace}`);
|
|
47
|
+
}
|
|
48
|
+
}, this.CLEANUP_INTERVAL);
|
|
49
|
+
// Allow Node.js to exit if this is the only active timer
|
|
50
|
+
if (this.cleanupTimer.unref) {
|
|
51
|
+
this.cleanupTimer.unref();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Stop automatic cleanup timer
|
|
56
|
+
*/
|
|
57
|
+
stopCleanupTimer() {
|
|
58
|
+
if (this.cleanupTimer) {
|
|
59
|
+
clearInterval(this.cleanupTimer);
|
|
60
|
+
this.cleanupTimer = undefined;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Destroy the provider and cleanup resources
|
|
65
|
+
*/
|
|
66
|
+
destroy() {
|
|
67
|
+
this.stopCleanupTimer();
|
|
68
|
+
this.clear();
|
|
29
69
|
}
|
|
30
70
|
getName() {
|
|
31
71
|
return 'Memory';
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import { HttpClientConfig } from '../interfaces/http-client-config.interface';
|
|
2
|
+
/**
|
|
3
|
+
* 深度合并配置
|
|
4
|
+
* 将用户配置与默认配置合并,用户配置会覆盖默认配置
|
|
5
|
+
*/
|
|
6
|
+
export declare function mergeHttpClientConfig(userConfig?: Partial<HttpClientConfig>, defaultConfig?: Partial<HttpClientConfig>): HttpClientConfig;
|
|
2
7
|
/**
|
|
3
8
|
* HTTP客户端默认配置
|
|
4
9
|
*/
|
|
@@ -1,8 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TEST_HTTP_CLIENT_CONFIG = exports.PRODUCTION_HTTP_CLIENT_CONFIG = exports.DEVELOPMENT_HTTP_CLIENT_CONFIG = exports.DEFAULT_HTTP_CLIENT_CONFIG = void 0;
|
|
4
|
+
exports.mergeHttpClientConfig = mergeHttpClientConfig;
|
|
4
5
|
exports.getHttpClientConfig = getHttpClientConfig;
|
|
5
|
-
|
|
6
|
+
/**
|
|
7
|
+
* 深度合并对象
|
|
8
|
+
* 用于合并用户配置和默认配置
|
|
9
|
+
*/
|
|
10
|
+
function deepMerge(target, source) {
|
|
11
|
+
const result = Object.assign({}, target);
|
|
12
|
+
for (const key in source) {
|
|
13
|
+
if (source[key] instanceof Object && key in target && !Array.isArray(source[key])) {
|
|
14
|
+
result[key] = deepMerge(target[key], source[key]);
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
result[key] = source[key];
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return result;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 深度合并配置
|
|
24
|
+
* 将用户配置与默认配置合并,用户配置会覆盖默认配置
|
|
25
|
+
*/
|
|
26
|
+
function mergeHttpClientConfig(userConfig = {}, defaultConfig = exports.DEFAULT_HTTP_CLIENT_CONFIG) {
|
|
27
|
+
return deepMerge(defaultConfig, userConfig);
|
|
28
|
+
}
|
|
6
29
|
/**
|
|
7
30
|
* HTTP客户端默认配置
|
|
8
31
|
*/
|
|
@@ -29,15 +52,6 @@ exports.DEFAULT_HTTP_CLIENT_CONFIG = {
|
|
|
29
52
|
minimumThroughputThreshold: 10,
|
|
30
53
|
countHalfOpenCalls: false,
|
|
31
54
|
},
|
|
32
|
-
cache: {
|
|
33
|
-
enabled: true,
|
|
34
|
-
defaultTtl: 300000, // 5分钟
|
|
35
|
-
cacheableMethods: ['GET', 'HEAD'],
|
|
36
|
-
cacheableStatusCodes: [200, 201, 304],
|
|
37
|
-
options: {
|
|
38
|
-
layers: [cache_options_interface_1.CacheLayer.CLS, cache_options_interface_1.CacheLayer.MEMORY, cache_options_interface_1.CacheLayer.REDIS],
|
|
39
|
-
},
|
|
40
|
-
},
|
|
41
55
|
logging: {
|
|
42
56
|
enabled: true,
|
|
43
57
|
logRequests: true,
|
|
@@ -51,7 +65,6 @@ exports.DEFAULT_HTTP_CLIENT_CONFIG = {
|
|
|
51
65
|
databaseLogging: {
|
|
52
66
|
enabled: false, // 默认不启用数据库日志
|
|
53
67
|
dataSource: 'default',
|
|
54
|
-
tableName: 'http_logs',
|
|
55
68
|
},
|
|
56
69
|
},
|
|
57
70
|
logCleanup: {
|
|
@@ -69,7 +82,6 @@ exports.DEFAULT_HTTP_CLIENT_CONFIG = {
|
|
|
69
82
|
exports.DEVELOPMENT_HTTP_CLIENT_CONFIG = Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG), { logging: Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG.logging), { enabled: true, logHeaders: true, logBody: true, logLevel: 'debug', databaseLogging: {
|
|
70
83
|
enabled: true, // 开发环境启用数据库日志
|
|
71
84
|
dataSource: 'default',
|
|
72
|
-
tableName: 'http_logs',
|
|
73
85
|
} }), logCleanup: Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG.logCleanup), { enabled: false }) });
|
|
74
86
|
/**
|
|
75
87
|
* 生产环境配置
|
|
@@ -77,7 +89,6 @@ exports.DEVELOPMENT_HTTP_CLIENT_CONFIG = Object.assign(Object.assign({}, exports
|
|
|
77
89
|
exports.PRODUCTION_HTTP_CLIENT_CONFIG = Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG), { timeout: 60000, retry: Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG.retry), { retries: 5 }), circuitBreaker: Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG.circuitBreaker), { failureThreshold: 3, recoveryTimeoutMs: 120000 }), logging: Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG.logging), { enabled: true, logHeaders: false, logBody: false, logLevel: 'warn', databaseLogging: {
|
|
78
90
|
enabled: true, // 生产环境启用数据库日志
|
|
79
91
|
dataSource: 'default',
|
|
80
|
-
tableName: 'http_logs',
|
|
81
92
|
} }), logCleanup: Object.assign(Object.assign({}, exports.DEFAULT_HTTP_CLIENT_CONFIG.logCleanup), { enabled: true, retentionDays: 7, maxRecords: 50000, schedule: '0 3 * * *' }) });
|
|
82
93
|
/**
|
|
83
94
|
* 测试环境配置
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
|
-
import { CircuitBreakerConfig,
|
|
2
|
+
import { CircuitBreakerConfig, HttpClientConfig, RetryConfig } from '../interfaces/http-client-config.interface';
|
|
3
3
|
export declare const HTTP_CLIENT_OPTIONS_KEY = "http_client_options";
|
|
4
4
|
export declare const RETRY_OPTIONS_KEY = "http_retry_options";
|
|
5
5
|
export declare const CIRCUIT_BREAKER_OPTIONS_KEY = "http_circuit_breaker_options";
|
|
6
|
-
export declare const CACHE_OPTIONS_KEY = "http_cache_options";
|
|
7
6
|
export declare const LOGGING_OPTIONS_KEY = "http_logging_options";
|
|
8
7
|
export declare const TIMEOUT_OPTIONS_KEY = "http_timeout_options";
|
|
9
8
|
export declare const PROXY_OPTIONS_KEY = "http_proxy_options";
|
|
@@ -34,24 +33,6 @@ export declare const HttpCircuitBreaker: (options?: {
|
|
|
34
33
|
monitoringPeriodMs?: number;
|
|
35
34
|
minimumThroughputThreshold?: number;
|
|
36
35
|
}) => (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
37
|
-
/**
|
|
38
|
-
* HTTP缓存装饰器
|
|
39
|
-
* 集成现有的三层缓存架构
|
|
40
|
-
*/
|
|
41
|
-
export declare const HttpCacheable: (options?: {
|
|
42
|
-
/** 缓存TTL(毫秒) */
|
|
43
|
-
ttl?: number;
|
|
44
|
-
/** 缓存键 */
|
|
45
|
-
key?: string | ((...args: any[]) => string);
|
|
46
|
-
/** 缓存层 */
|
|
47
|
-
layers?: Array<"cls" | "memory" | "redis">;
|
|
48
|
-
/** 缓存条件 */
|
|
49
|
-
condition?: (...args: any[]) => boolean | Promise<boolean>;
|
|
50
|
-
/** 除非条件 */
|
|
51
|
-
unless?: string | ((result: any, args: any[]) => boolean);
|
|
52
|
-
/** 命名空间 */
|
|
53
|
-
namespace?: string;
|
|
54
|
-
}) => (target: any, propertyKey: string, descriptor: PropertyDescriptor) => PropertyDescriptor;
|
|
55
36
|
/**
|
|
56
37
|
* 请求日志装饰器
|
|
57
38
|
* 类似Spring Boot的@LogRequest注解
|
|
@@ -97,10 +78,6 @@ export declare class HttpDecoratorUtils {
|
|
|
97
78
|
* 获取方法上的熔断器配置
|
|
98
79
|
*/
|
|
99
80
|
static getCircuitBreakerOptions(target: any, propertyKey: string): CircuitBreakerConfig | undefined;
|
|
100
|
-
/**
|
|
101
|
-
* 获取方法上的缓存配置
|
|
102
|
-
*/
|
|
103
|
-
static getCacheOptions(target: any, propertyKey: string): HttpCacheConfig | undefined;
|
|
104
81
|
/**
|
|
105
82
|
* 获取方法上的日志配置
|
|
106
83
|
*/
|
|
@@ -127,7 +104,6 @@ export declare class HttpDecoratorUtils {
|
|
|
127
104
|
static getAllDecoratorConfigs(target: any, propertyKey: string): {
|
|
128
105
|
retry?: RetryConfig;
|
|
129
106
|
circuitBreaker?: CircuitBreakerConfig;
|
|
130
|
-
cache?: HttpCacheConfig;
|
|
131
107
|
logging?: any;
|
|
132
108
|
timeout?: number;
|
|
133
109
|
proxy?: any;
|