create-dp-koa 1.1.1 → 1.1.3
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/package.json +1 -1
- package/template/scripts/sync-template.mjs +21 -0
- package/template/src/app.ts +1 -2
- package/template/src/{framework/plugins → plugins}/registry.ts +2 -2
- package/template/src/plugins/weboffice/http/routes.ts +2 -2
- package/template/src/plugins/weboffice/index.ts +3 -3
- package/template/src/plugins/weboffice/services/webofficeCallback.service.ts +3 -4
- package/template/src/utils/testDataInitializer.ts +1 -1
- package/template/tsconfig.json +6 -0
- package/template/src/annotations/decorators/ConfigManagement.ts +0 -9
- package/template/src/annotations/decorators/DistributedTracing.ts +0 -9
- package/template/src/annotations/decorators/EnterprisePerformance.ts +0 -9
- package/template/src/annotations/decorators/PerformanceMonitor.ts +0 -32
- package/template/src/annotations/decorators/SecurityAudit.ts +0 -9
- package/template/src/annotations/index.ts +0 -50
- package/template/src/annotations/processors/ConfigManagementProcessor.ts +0 -369
- package/template/src/annotations/processors/DistributedTracingProcessor.ts +0 -288
- package/template/src/annotations/processors/EnterprisePerformanceProcessor.ts +0 -189
- package/template/src/annotations/processors/PerformanceMonitorProcessor.ts +0 -101
- package/template/src/annotations/processors/SecurityAuditProcessor.ts +0 -345
- package/template/src/annotations/processors/SwaggerProcessor.ts +0 -612
- package/template/src/annotations/processors/index.ts +0 -10
- package/template/src/examples/InterceptorExampleRunner.ts +0 -284
- package/template/src/examples/ServiceInterceptorExample.ts +0 -214
- package/template/src/examples/cacheExamples.ts +0 -155
- package/template/src/framework/decorator/controller.ts +0 -311
- package/template/src/framework/decorator/processor/AnnotationDecorators.ts +0 -100
- package/template/src/framework/decorator/processor/AnnotationProcessor.ts +0 -160
- package/template/src/framework/decorator/processor/AnnotationProcessorConfig.ts +0 -45
- package/template/src/framework/decorator/processor/AnnotationRegistry.ts +0 -117
- package/template/src/framework/decorator/processor/AnnotationSystemInitializer.ts +0 -95
- package/template/src/framework/decorator/processor/ProcessorManager.ts +0 -76
- package/template/src/framework/decorator/processor/processors/CustomProcessors.ts +0 -126
- package/template/src/framework/decorator/processor/processors/DefaultProcessors.ts +0 -207
- package/template/src/framework/decorator/refactored/DecoratorFactory.ts +0 -99
- package/template/src/framework/decorator/refactored/DecoratorMetadataManager.ts +0 -125
- package/template/src/framework/decorator/refactored/DecoratorValidator.ts +0 -128
- package/template/src/framework/decorator/refactored/TypeSafeDecorators.ts +0 -139
- package/template/src/framework/decorator/refactored/index.ts +0 -98
- package/template/src/framework/decorator/swagger.ts +0 -150
- package/template/src/framework/interceptors/AdvancedServiceCallInterceptor.ts +0 -375
- package/template/src/framework/interceptors/ServiceCallInterceptor.ts +0 -348
- package/template/src/framework/interceptors/index.ts +0 -19
- package/template/src/framework/plugins/types.ts +0 -15
- package/template/src/framework/types/ServiceResult.ts +0 -151
- package/template/src/framework/types/index.ts +0 -16
- package/template/src/framework/utils/CacheManager.ts +0 -430
- package/template/src/framework/utils/CacheService.ts +0 -248
- package/template/src/framework/utils/DtoValidator.ts +0 -164
- package/template/src/framework/utils/MigrationHelper.ts +0 -179
- package/template/src/framework/utils/MigrationManager.ts +0 -256
- package/template/src/framework/utils/NewRouter.ts +0 -207
- package/template/src/framework/utils/TransactionManager.ts +0 -172
- package/template/src/framework/utils/bootstrap.ts +0 -445
- package/template/src/framework/utils/cache.ts +0 -269
- package/template/src/framework/utils/databaseConfig.ts +0 -148
- package/template/src/framework/utils/db.ts +0 -39
- package/template/src/framework/utils/dbMonitor.ts +0 -106
- package/template/src/framework/utils/function.ts +0 -61
- package/template/src/framework/utils/gracefulShutdown.ts +0 -131
- package/template/src/framework/utils/logger.ts +0 -388
- package/template/src/framework/utils/metrics.ts +0 -182
- package/template/src/framework/utils/router.ts +0 -417
- package/template/src/framework/utils/swagger.ts +0 -184
- package/template/src/framework/utils/testDb.ts +0 -19
- package/template/src/framework/utils/token.ts +0 -23
- package/template/src/framework/utils/transform.ts +0 -17
- package/template/src/libs/aokEmailSender.ts +0 -42
- package/template/src/libs/captcha.ts +0 -37
- package/template/src/libs/cos.ts +0 -45
- package/template/src/libs/mCache.ts +0 -7
- package/template/src/libs/serviceValidate.ts +0 -3
- package/template/src/libs/tecentSms.ts +0 -51
- package/template/src/middlewares/a.middleware.ts +0 -6
- package/template/src/middlewares/error.middleware.ts +0 -14
- package/template/src/middlewares/logging.middleware.ts +0 -187
- package/template/src/middlewares/static.middleware.ts +0 -79
- package/template/src/middlewares/swagger.middleware.ts +0 -70
- package/template/src/middlewares/token.middleware.ts +0 -32
- package/template/src/migrations/1700000000000-InitialDatabaseStructure.ts +0 -172
- package/template/src/migrations/index.ts +0 -6
- package/template/src/repository/base/BaseRepository.ts +0 -124
- package/template/src/repository/interfaces/IBaseRepository.ts +0 -67
- package/template/src/service/base.service.ts +0 -116
|
@@ -1,445 +0,0 @@
|
|
|
1
|
-
import Koa from 'koa';
|
|
2
|
-
const app = new Koa();
|
|
3
|
-
import dotenv from 'dotenv';
|
|
4
|
-
import "reflect-metadata";
|
|
5
|
-
import koaBody from 'koa-body';
|
|
6
|
-
const moduleAlias = require("module-alias");//路径别名
|
|
7
|
-
import path from 'path';
|
|
8
|
-
import type { Server } from 'http';
|
|
9
|
-
|
|
10
|
-
// 首先设置路径别名
|
|
11
|
-
moduleAlias.addAliases({
|
|
12
|
-
"@src": path.join(process.cwd(), "./src"),
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
// import koaCros from "@koa/cors"
|
|
16
|
-
const koaCros = require("@koa/cors")
|
|
17
|
-
import { logger } from '@src/framework/utils/logger';
|
|
18
|
-
import { isDebug } from '@src/framework/utils/function';
|
|
19
|
-
import { router } from '@src/framework/utils/router';
|
|
20
|
-
import { loggingMiddleware, businessLoggingMiddleware, databaseLoggingMiddleware } from '@src/middlewares/logging.middleware';
|
|
21
|
-
|
|
22
|
-
// 开发/调试用 .env.development,生产用 .env.production(与 NODE_ENV 解耦:仅 `--env=debug` 走 development)
|
|
23
|
-
const envFile = isDebug() ? '.env.development' : '.env.production';
|
|
24
|
-
dotenv.config({ path: envFile });
|
|
25
|
-
|
|
26
|
-
let beforeBootstraps: Function | null = null;
|
|
27
|
-
let afterBootstraps: Function | null = null;
|
|
28
|
-
let server: Server | null = null;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* 企业级错误分类枚举
|
|
32
|
-
*/
|
|
33
|
-
enum ErrorCategory {
|
|
34
|
-
VALIDATION = 'VALIDATION_ERROR',
|
|
35
|
-
AUTHENTICATION = 'AUTH_ERROR',
|
|
36
|
-
AUTHORIZATION = 'AUTHZ_ERROR',
|
|
37
|
-
BUSINESS_LOGIC = 'BUSINESS_ERROR',
|
|
38
|
-
DATABASE = 'DATABASE_ERROR',
|
|
39
|
-
EXTERNAL_SERVICE = 'EXTERNAL_SERVICE_ERROR',
|
|
40
|
-
SYSTEM = 'SYSTEM_ERROR',
|
|
41
|
-
NETWORK = 'NETWORK_ERROR',
|
|
42
|
-
UNKNOWN = 'UNKNOWN_ERROR'
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* 企业级错误码定义
|
|
47
|
-
*/
|
|
48
|
-
enum ErrorCode {
|
|
49
|
-
// 4xx 客户端错误
|
|
50
|
-
INVALID_REQUEST = 'E400001',
|
|
51
|
-
VALIDATION_FAILED = 'E400002',
|
|
52
|
-
UNAUTHORIZED = 'E401001',
|
|
53
|
-
FORBIDDEN = 'E403001',
|
|
54
|
-
NOT_FOUND = 'E404001',
|
|
55
|
-
METHOD_NOT_ALLOWED = 'E405001',
|
|
56
|
-
CONFLICT = 'E409001',
|
|
57
|
-
|
|
58
|
-
// 5xx 服务器错误
|
|
59
|
-
INTERNAL_ERROR = 'E500001',
|
|
60
|
-
DATABASE_ERROR = 'E500002',
|
|
61
|
-
EXTERNAL_SERVICE_ERROR = 'E500003',
|
|
62
|
-
SERVICE_UNAVAILABLE = 'E503001',
|
|
63
|
-
TIMEOUT = 'E504001'
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* 企业级错误处理中间件
|
|
68
|
-
* 符合企业开发规范:错误分类、标准化错误码、安全防护、监控告警
|
|
69
|
-
*/
|
|
70
|
-
const frameworkErrorMiddleware = () => {
|
|
71
|
-
return async (ctx: any, next: Function) => {
|
|
72
|
-
try {
|
|
73
|
-
await next();
|
|
74
|
-
} catch (err) {
|
|
75
|
-
const error = err as any;
|
|
76
|
-
|
|
77
|
-
// 1. 错误分类和标准化处理
|
|
78
|
-
const errorInfo = classifyAndStandardizeError(error);
|
|
79
|
-
|
|
80
|
-
// 2. 设置响应状态码
|
|
81
|
-
ctx.status = errorInfo.statusCode;
|
|
82
|
-
|
|
83
|
-
// 3. 安全信息过滤
|
|
84
|
-
const sanitizedError = sanitizeErrorForLogging(error, ctx);
|
|
85
|
-
|
|
86
|
-
// 4. 记录结构化错误日志
|
|
87
|
-
logger.error('企业级异常处理', error as Error, {
|
|
88
|
-
error: {
|
|
89
|
-
category: errorInfo.category,
|
|
90
|
-
code: errorInfo.code,
|
|
91
|
-
message: sanitizedError.message,
|
|
92
|
-
stack: sanitizedError.stack,
|
|
93
|
-
statusCode: errorInfo.statusCode,
|
|
94
|
-
severity: errorInfo.severity
|
|
95
|
-
},
|
|
96
|
-
request: {
|
|
97
|
-
method: ctx.method,
|
|
98
|
-
url: sanitizeUrl(ctx.url),
|
|
99
|
-
userAgent: ctx.headers['user-agent'],
|
|
100
|
-
ip: ctx.ip,
|
|
101
|
-
requestId: ctx.requestId || 'unknown',
|
|
102
|
-
userId: ctx.userId || 'anonymous'
|
|
103
|
-
},
|
|
104
|
-
context: {
|
|
105
|
-
timestamp: new Date().toISOString(),
|
|
106
|
-
environment: isDebug() ? 'development' : 'production',
|
|
107
|
-
service: 'dp-koa-framework',
|
|
108
|
-
version: process.env.npm_package_version || '1.0.0'
|
|
109
|
-
}
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
// 5. 错误监控和告警(严重错误)
|
|
113
|
-
if (errorInfo.severity === 'critical' || errorInfo.severity === 'high') {
|
|
114
|
-
triggerErrorAlert(errorInfo, sanitizedError, ctx);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
// 6. 标准化错误响应
|
|
118
|
-
ctx.body = buildStandardErrorResponse(errorInfo, ctx);
|
|
119
|
-
|
|
120
|
-
// 7. 设置安全响应头
|
|
121
|
-
setSecurityHeaders(ctx);
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* 错误分类和标准化处理
|
|
128
|
-
*/
|
|
129
|
-
function classifyAndStandardizeError(error: any): {
|
|
130
|
-
category: ErrorCategory;
|
|
131
|
-
code: ErrorCode;
|
|
132
|
-
statusCode: number;
|
|
133
|
-
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
134
|
-
message: string;
|
|
135
|
-
} {
|
|
136
|
-
// 根据错误类型进行分类
|
|
137
|
-
if (error.name === 'ValidationError' || error.name === 'CastError') {
|
|
138
|
-
return {
|
|
139
|
-
category: ErrorCategory.VALIDATION,
|
|
140
|
-
code: ErrorCode.VALIDATION_FAILED,
|
|
141
|
-
statusCode: 400,
|
|
142
|
-
severity: 'low',
|
|
143
|
-
message: '请求参数验证失败'
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (error.name === 'UnauthorizedError' || error.status === 401) {
|
|
148
|
-
return {
|
|
149
|
-
category: ErrorCategory.AUTHENTICATION,
|
|
150
|
-
code: ErrorCode.UNAUTHORIZED,
|
|
151
|
-
statusCode: 401,
|
|
152
|
-
severity: 'medium',
|
|
153
|
-
message: '身份验证失败'
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
if (error.name === 'ForbiddenError' || error.status === 403) {
|
|
158
|
-
return {
|
|
159
|
-
category: ErrorCategory.AUTHORIZATION,
|
|
160
|
-
code: ErrorCode.FORBIDDEN,
|
|
161
|
-
statusCode: 403,
|
|
162
|
-
severity: 'medium',
|
|
163
|
-
message: '访问权限不足'
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
if (error.name === 'NotFoundError' || error.status === 404) {
|
|
168
|
-
return {
|
|
169
|
-
category: ErrorCategory.BUSINESS_LOGIC,
|
|
170
|
-
code: ErrorCode.NOT_FOUND,
|
|
171
|
-
statusCode: 404,
|
|
172
|
-
severity: 'low',
|
|
173
|
-
message: '请求的资源不存在'
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
if (error.code && error.code.startsWith('ER_')) {
|
|
178
|
-
return {
|
|
179
|
-
category: ErrorCategory.DATABASE,
|
|
180
|
-
code: ErrorCode.DATABASE_ERROR,
|
|
181
|
-
statusCode: 500,
|
|
182
|
-
severity: 'high',
|
|
183
|
-
message: '数据库操作失败'
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
if (error.code === 'ECONNREFUSED' || error.code === 'ETIMEDOUT') {
|
|
188
|
-
return {
|
|
189
|
-
category: ErrorCategory.NETWORK,
|
|
190
|
-
code: ErrorCode.EXTERNAL_SERVICE_ERROR,
|
|
191
|
-
statusCode: 503,
|
|
192
|
-
severity: 'high',
|
|
193
|
-
message: '外部服务不可用'
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// 默认系统错误
|
|
198
|
-
return {
|
|
199
|
-
category: ErrorCategory.SYSTEM,
|
|
200
|
-
code: ErrorCode.INTERNAL_ERROR,
|
|
201
|
-
statusCode: error.status || error.statusCode || 500,
|
|
202
|
-
severity: error.status >= 500 ? 'critical' : 'medium',
|
|
203
|
-
message: error.message || '系统内部错误'
|
|
204
|
-
};
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* 安全信息过滤
|
|
209
|
-
*/
|
|
210
|
-
function sanitizeErrorForLogging(error: any, ctx: any): any {
|
|
211
|
-
const sensitiveFields = ['password', 'token', 'secret', 'key', 'auth', 'credential'];
|
|
212
|
-
|
|
213
|
-
const sanitize = (obj: any): any => {
|
|
214
|
-
if (typeof obj !== 'object' || obj === null) return obj;
|
|
215
|
-
|
|
216
|
-
const sanitized = { ...obj };
|
|
217
|
-
for (const [key, value] of Object.entries(sanitized)) {
|
|
218
|
-
if (sensitiveFields.some(field => key.toLowerCase().includes(field))) {
|
|
219
|
-
sanitized[key] = '***REDACTED***';
|
|
220
|
-
} else if (typeof value === 'object') {
|
|
221
|
-
sanitized[key] = sanitize(value);
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
return sanitized;
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
return {
|
|
228
|
-
message: error.message || 'Unknown error',
|
|
229
|
-
stack: isDebug() ? error.stack : undefined,
|
|
230
|
-
...sanitize(error)
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* URL 安全过滤
|
|
236
|
-
*/
|
|
237
|
-
function sanitizeUrl(url: string): string {
|
|
238
|
-
// 移除查询参数中的敏感信息
|
|
239
|
-
try {
|
|
240
|
-
// 如果URL是相对路径,则直接处理查询参数
|
|
241
|
-
if (!url.startsWith('http://') && !url.startsWith('https://')) {
|
|
242
|
-
// 处理相对路径,如 /api/users?token=xxx
|
|
243
|
-
const [pathname, search] = url.split('?');
|
|
244
|
-
if (!search) return pathname;
|
|
245
|
-
|
|
246
|
-
const params = new URLSearchParams(search);
|
|
247
|
-
const sensitiveParams = ['password', 'token', 'secret', 'key'];
|
|
248
|
-
|
|
249
|
-
sensitiveParams.forEach(param => {
|
|
250
|
-
if (params.has(param)) {
|
|
251
|
-
params.set(param, '***REDACTED***');
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
return pathname + '?' + params.toString();
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
// 处理完整URL
|
|
259
|
-
const urlObj = new URL(url);
|
|
260
|
-
const sensitiveParams = ['password', 'token', 'secret', 'key'];
|
|
261
|
-
|
|
262
|
-
sensitiveParams.forEach(param => {
|
|
263
|
-
if (urlObj.searchParams.has(param)) {
|
|
264
|
-
urlObj.searchParams.set(param, '***REDACTED***');
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
return urlObj.pathname + urlObj.search;
|
|
269
|
-
} catch {
|
|
270
|
-
// 如果URL解析失败,至少移除明显的敏感参数
|
|
271
|
-
return url.replace(/([?&])(password|token|secret|key)=[^&]*/gi, '$1$2=***REDACTED***');
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
/**
|
|
276
|
-
* 错误告警触发
|
|
277
|
-
*/
|
|
278
|
-
function triggerErrorAlert(errorInfo: any, error: any, ctx: any): void {
|
|
279
|
-
// 这里可以集成告警系统,如钉钉、企业微信、邮件等
|
|
280
|
-
logger.warn('触发错误告警', {
|
|
281
|
-
alert: {
|
|
282
|
-
level: errorInfo.severity,
|
|
283
|
-
category: errorInfo.category,
|
|
284
|
-
code: errorInfo.code,
|
|
285
|
-
message: errorInfo.message,
|
|
286
|
-
timestamp: new Date().toISOString()
|
|
287
|
-
},
|
|
288
|
-
request: {
|
|
289
|
-
method: ctx.method,
|
|
290
|
-
url: sanitizeUrl(ctx.url),
|
|
291
|
-
ip: ctx.ip,
|
|
292
|
-
requestId: ctx.requestId
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* 构建标准化错误响应
|
|
299
|
-
*/
|
|
300
|
-
function buildStandardErrorResponse(errorInfo: any, ctx: any): any {
|
|
301
|
-
const baseResponse = {
|
|
302
|
-
success: false,
|
|
303
|
-
error: {
|
|
304
|
-
code: errorInfo.code,
|
|
305
|
-
category: errorInfo.category,
|
|
306
|
-
message: errorInfo.message,
|
|
307
|
-
timestamp: new Date().toISOString(),
|
|
308
|
-
requestId: ctx.requestId || 'unknown'
|
|
309
|
-
}
|
|
310
|
-
};
|
|
311
|
-
|
|
312
|
-
// 开发环境添加调试信息
|
|
313
|
-
if (isDebug()) {
|
|
314
|
-
return {
|
|
315
|
-
...baseResponse,
|
|
316
|
-
debug: {
|
|
317
|
-
stack: errorInfo.stack,
|
|
318
|
-
originalError: errorInfo.originalError
|
|
319
|
-
}
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
|
|
323
|
-
return baseResponse;
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* 设置安全响应头
|
|
328
|
-
*/
|
|
329
|
-
function setSecurityHeaders(ctx: any): void {
|
|
330
|
-
// 防止信息泄露
|
|
331
|
-
ctx.set('X-Content-Type-Options', 'nosniff');
|
|
332
|
-
ctx.set('X-Frame-Options', 'DENY');
|
|
333
|
-
ctx.set('X-XSS-Protection', '1; mode=block');
|
|
334
|
-
|
|
335
|
-
// 移除可能泄露信息的头
|
|
336
|
-
ctx.remove('X-Powered-By');
|
|
337
|
-
ctx.remove('Server');
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* 启动应用
|
|
342
|
-
* @param fn 启动函数,该函数会在应用启动前执行,所以可以在该函数中执行耗时操作,比如数据库连接、日志初始化等。
|
|
343
|
-
* @param port 应用端口,默认为3000
|
|
344
|
-
/**
|
|
345
|
-
* 启动前执行,用户可以在这里执行一些初始化操作,比如数据库连接、日志初始化等。
|
|
346
|
-
* 注意:该函数会在应用启动前执行,所以不要在该函数中执行耗时操作,否则会影响应用启动速度。
|
|
347
|
-
* 注意:该函数是异步函数,所以需要使用await关键字来等待执行完成。
|
|
348
|
-
* @param fn
|
|
349
|
-
*/
|
|
350
|
-
export const setBeforeBootstrap = async (fn: Function) => {
|
|
351
|
-
beforeBootstraps = fn;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* 启动后执行,用户可以在这里执行一些初始化操作,比如数据库连接、日志初始化等。
|
|
356
|
-
* 注意:该函数会在应用启动后执行,所以可以在该函数中执行耗时操作,比如数据库连接、日志初始化等。
|
|
357
|
-
* 注意:该函数是异步函数,所以需要使用await关键字来等待执行完成。
|
|
358
|
-
* @param fn
|
|
359
|
-
*/
|
|
360
|
-
export const setAfterBootstrap = async (fn: Function) => {
|
|
361
|
-
afterBootstraps = fn;
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
export const bootstrap = async () => {
|
|
366
|
-
if (beforeBootstraps) {
|
|
367
|
-
await beforeBootstraps(app);
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
app.use(koaCros({
|
|
371
|
-
origin: '*', //允许所有域名访问
|
|
372
|
-
maxAge: 5,
|
|
373
|
-
credentials: true, //允许携带cookie
|
|
374
|
-
allowMethods: ['GET', 'POST', 'PUT', 'DELETE'], //允许的请求方法
|
|
375
|
-
allowHeaders: ['Content-Type', 'Authorization', 'Accept'], //允许的请求头
|
|
376
|
-
}));
|
|
377
|
-
|
|
378
|
-
// 企业级日志中间件
|
|
379
|
-
app.use(loggingMiddleware());
|
|
380
|
-
app.use(businessLoggingMiddleware());
|
|
381
|
-
app.use(databaseLoggingMiddleware());
|
|
382
|
-
|
|
383
|
-
// 框架层错误处理中间件
|
|
384
|
-
app.use(frameworkErrorMiddleware())
|
|
385
|
-
app.use(koaBody());
|
|
386
|
-
|
|
387
|
-
// Swagger文档和UI(仅开发环境启用)
|
|
388
|
-
// if (isDebug()) {
|
|
389
|
-
// app.use(swaggerJson);
|
|
390
|
-
// app.use(swaggerMiddleware());
|
|
391
|
-
// }
|
|
392
|
-
|
|
393
|
-
// 添加路由调试中间件
|
|
394
|
-
// app.use(async (ctx, next) => {
|
|
395
|
-
// logger.info(`收到请求: ${ctx.method} ${ctx.path}`);
|
|
396
|
-
// await next();
|
|
397
|
-
// });
|
|
398
|
-
|
|
399
|
-
// 启用路由
|
|
400
|
-
app.use(router.routes()).use(router.allowedMethods());
|
|
401
|
-
|
|
402
|
-
// 启动应用
|
|
403
|
-
const port = Number(process.env.port) || 3000;
|
|
404
|
-
const host = process.env.host || '0.0.0.0';
|
|
405
|
-
|
|
406
|
-
await new Promise<void>((resolve, reject) => {
|
|
407
|
-
try {
|
|
408
|
-
server = app.listen(port, host, () => {
|
|
409
|
-
logger.info(`Server is running on ${host}:${port}`);
|
|
410
|
-
resolve();
|
|
411
|
-
})
|
|
412
|
-
server.on('error', (err) => reject(err));
|
|
413
|
-
} catch (error) {
|
|
414
|
-
reject(error)
|
|
415
|
-
}
|
|
416
|
-
})
|
|
417
|
-
|
|
418
|
-
if (afterBootstraps) {
|
|
419
|
-
await afterBootstraps(app);
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
// 导出app 对象
|
|
424
|
-
export const App = app;
|
|
425
|
-
|
|
426
|
-
/**
|
|
427
|
-
* 获取当前 HTTP Server(如果已启动)
|
|
428
|
-
*/
|
|
429
|
-
export const getServer = (): Server | null => server;
|
|
430
|
-
|
|
431
|
-
/**
|
|
432
|
-
* 关闭 HTTP Server(幂等)
|
|
433
|
-
*/
|
|
434
|
-
export const closeServer = async (): Promise<void> => {
|
|
435
|
-
if (!server) return;
|
|
436
|
-
const s = server;
|
|
437
|
-
server = null;
|
|
438
|
-
|
|
439
|
-
await new Promise<void>((resolve, reject) => {
|
|
440
|
-
s.close((err?: Error) => {
|
|
441
|
-
if (err) return reject(err);
|
|
442
|
-
resolve();
|
|
443
|
-
});
|
|
444
|
-
});
|
|
445
|
-
}
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
import { logger } from "@src/framework/utils/logger";
|
|
2
|
-
|
|
3
|
-
const NodeCache = require("node-cache");
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 缓存配置接口
|
|
7
|
-
*/
|
|
8
|
-
export interface CacheConfig {
|
|
9
|
-
/** 默认TTL时间(秒) */
|
|
10
|
-
stdTTL?: number;
|
|
11
|
-
/** 最大缓存条目数 */
|
|
12
|
-
maxKeys?: number;
|
|
13
|
-
/** 检查过期时间间隔(秒) */
|
|
14
|
-
checkperiod?: number;
|
|
15
|
-
/** 是否使用克隆模式 */
|
|
16
|
-
useClones?: boolean;
|
|
17
|
-
/** 是否删除过期键 */
|
|
18
|
-
deleteOnExpire?: boolean;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* 默认缓存配置
|
|
23
|
-
* 统一管理所有缓存的默认参数,防止内存泄露
|
|
24
|
-
*/
|
|
25
|
-
const DEFAULT_CACHE_CONFIG: Required<CacheConfig> = {
|
|
26
|
-
stdTTL: 300, // 默认5分钟过期
|
|
27
|
-
maxKeys: 1000, // 最大1000个缓存条目
|
|
28
|
-
checkperiod: 120, // 每2分钟检查一次过期
|
|
29
|
-
useClones: false, // 不使用克隆,提高性能
|
|
30
|
-
deleteOnExpire: true // 自动删除过期键
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* 缓存类型枚举
|
|
35
|
-
*/
|
|
36
|
-
export enum CacheType {
|
|
37
|
-
/** 用户缓存 */
|
|
38
|
-
USER = 'user',
|
|
39
|
-
/** 控制器结果缓存 */
|
|
40
|
-
CONTROLLER = 'controller',
|
|
41
|
-
/** 会话缓存 */
|
|
42
|
-
SESSION = 'session',
|
|
43
|
-
/** 验证码缓存 */
|
|
44
|
-
CAPTCHA = 'captcha',
|
|
45
|
-
/** 临时缓存 */
|
|
46
|
-
TEMP = 'temp'
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* 不同类型缓存的特定配置
|
|
51
|
-
*/
|
|
52
|
-
const CACHE_TYPE_CONFIGS: Record<CacheType, Partial<CacheConfig>> = {
|
|
53
|
-
[CacheType.USER]: {
|
|
54
|
-
stdTTL: 1800, // 30分钟
|
|
55
|
-
maxKeys: 500
|
|
56
|
-
},
|
|
57
|
-
[CacheType.CONTROLLER]: {
|
|
58
|
-
stdTTL: 60, // 1分钟
|
|
59
|
-
maxKeys: 2000
|
|
60
|
-
},
|
|
61
|
-
[CacheType.SESSION]: {
|
|
62
|
-
stdTTL: 3600, // 1小时
|
|
63
|
-
maxKeys: 1000
|
|
64
|
-
},
|
|
65
|
-
[CacheType.CAPTCHA]: {
|
|
66
|
-
stdTTL: 300, // 5分钟
|
|
67
|
-
maxKeys: 100
|
|
68
|
-
},
|
|
69
|
-
[CacheType.TEMP]: {
|
|
70
|
-
stdTTL: 60, // 1分钟
|
|
71
|
-
maxKeys: 100
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* 缓存管理器类
|
|
77
|
-
*/
|
|
78
|
-
class CacheManager {
|
|
79
|
-
private caches: Map<string, any> = new Map();
|
|
80
|
-
private stats: Map<string, { hits: number; misses: number; keys: number }> = new Map();
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* 创建缓存实例
|
|
84
|
-
* @param name 缓存名称
|
|
85
|
-
* @param type 缓存类型
|
|
86
|
-
* @param customConfig 自定义配置
|
|
87
|
-
*/
|
|
88
|
-
createCache(name: string, type: CacheType = CacheType.TEMP, customConfig?: Partial<CacheConfig>) {
|
|
89
|
-
if (this.caches.has(name)) {
|
|
90
|
-
logger.warn(`缓存 ${name} 已存在,返回现有实例`);
|
|
91
|
-
return this.caches.get(name);
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// 合并配置
|
|
95
|
-
const typeConfig = CACHE_TYPE_CONFIGS[type] || {};
|
|
96
|
-
const finalConfig = { ...DEFAULT_CACHE_CONFIG, ...typeConfig, ...customConfig };
|
|
97
|
-
|
|
98
|
-
// 创建缓存实例
|
|
99
|
-
const cache = new NodeCache(finalConfig);
|
|
100
|
-
|
|
101
|
-
// 添加统计信息
|
|
102
|
-
this.stats.set(name, { hits: 0, misses: 0, keys: 0 });
|
|
103
|
-
|
|
104
|
-
// 监听缓存事件
|
|
105
|
-
cache.on('set', (key: string, value: any) => {
|
|
106
|
-
const stats = this.stats.get(name);
|
|
107
|
-
if (stats) {
|
|
108
|
-
stats.keys = cache.keys().length;
|
|
109
|
-
}
|
|
110
|
-
logger.debug(`缓存 ${name} 设置键: ${key}`);
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
cache.on('del', (key: string, value: any) => {
|
|
114
|
-
const stats = this.stats.get(name);
|
|
115
|
-
if (stats) {
|
|
116
|
-
stats.keys = cache.keys().length;
|
|
117
|
-
}
|
|
118
|
-
logger.debug(`缓存 ${name} 删除键: ${key}`);
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
cache.on('expired', (key: string, value: any) => {
|
|
122
|
-
const stats = this.stats.get(name);
|
|
123
|
-
if (stats) {
|
|
124
|
-
stats.keys = cache.keys().length;
|
|
125
|
-
}
|
|
126
|
-
logger.debug(`缓存 ${name} 键过期: ${key}`);
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
// 包装get方法以添加统计
|
|
130
|
-
const originalGet = cache.get.bind(cache);
|
|
131
|
-
cache.get = (key: string) => {
|
|
132
|
-
const result = originalGet(key);
|
|
133
|
-
const stats = this.stats.get(name);
|
|
134
|
-
if (stats) {
|
|
135
|
-
if (result !== undefined) {
|
|
136
|
-
stats.hits++;
|
|
137
|
-
} else {
|
|
138
|
-
stats.misses++;
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
return result;
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
this.caches.set(name, cache);
|
|
145
|
-
logger.debug(`创建缓存 ${name},类型: ${type},配置:`, finalConfig);
|
|
146
|
-
|
|
147
|
-
return cache;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* 获取缓存实例
|
|
152
|
-
* @param name 缓存名称
|
|
153
|
-
*/
|
|
154
|
-
getCache(name: string) {
|
|
155
|
-
return this.caches.get(name);
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* 删除缓存实例
|
|
160
|
-
* @param name 缓存名称
|
|
161
|
-
*/
|
|
162
|
-
deleteCache(name: string) {
|
|
163
|
-
const cache = this.caches.get(name);
|
|
164
|
-
if (cache) {
|
|
165
|
-
cache.close();
|
|
166
|
-
this.caches.delete(name);
|
|
167
|
-
this.stats.delete(name);
|
|
168
|
-
logger.info(`删除缓存 ${name}`);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* 清空所有缓存
|
|
174
|
-
*/
|
|
175
|
-
clearAllCaches() {
|
|
176
|
-
this.caches.forEach((cache, name) => {
|
|
177
|
-
cache.flushAll();
|
|
178
|
-
logger.info(`清空缓存 ${name}`);
|
|
179
|
-
});
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* 获取缓存统计信息
|
|
184
|
-
*/
|
|
185
|
-
getStats() {
|
|
186
|
-
const result: Record<string, any> = {};
|
|
187
|
-
this.stats.forEach((stats, name) => {
|
|
188
|
-
const cache = this.caches.get(name);
|
|
189
|
-
result[name] = {
|
|
190
|
-
...stats,
|
|
191
|
-
hitRate: stats.hits + stats.misses > 0 ? (stats.hits / (stats.hits + stats.misses) * 100).toFixed(2) + '%' : '0%',
|
|
192
|
-
config: cache ? cache.options : null
|
|
193
|
-
};
|
|
194
|
-
});
|
|
195
|
-
return result;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* 获取所有缓存的内存使用情况
|
|
200
|
-
*/
|
|
201
|
-
getMemoryUsage() {
|
|
202
|
-
const result: Record<string, any> = {};
|
|
203
|
-
this.caches.forEach((cache, name) => {
|
|
204
|
-
const keys = cache.keys();
|
|
205
|
-
result[name] = {
|
|
206
|
-
keyCount: keys.length,
|
|
207
|
-
maxKeys: cache.options.maxKeys,
|
|
208
|
-
usage: ((keys.length / cache.options.maxKeys) * 100).toFixed(2) + '%'
|
|
209
|
-
};
|
|
210
|
-
});
|
|
211
|
-
return result;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// 创建全局缓存管理器实例
|
|
216
|
-
const cacheManager = new CacheManager();
|
|
217
|
-
|
|
218
|
-
/**
|
|
219
|
-
* 创建缓存的便捷函数
|
|
220
|
-
* @param name 缓存名称
|
|
221
|
-
* @param type 缓存类型
|
|
222
|
-
* @param customConfig 自定义配置
|
|
223
|
-
*/
|
|
224
|
-
export const createCache = (name: string, type: CacheType = CacheType.TEMP, customConfig?: Partial<CacheConfig>) => {
|
|
225
|
-
return cacheManager.createCache(name, type, customConfig);
|
|
226
|
-
};
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* 获取缓存实例
|
|
230
|
-
* @param name 缓存名称
|
|
231
|
-
*/
|
|
232
|
-
export const getCache = (name: string) => {
|
|
233
|
-
return cacheManager.getCache(name);
|
|
234
|
-
};
|
|
235
|
-
|
|
236
|
-
/**
|
|
237
|
-
* 删除缓存实例
|
|
238
|
-
* @param name 缓存名称
|
|
239
|
-
*/
|
|
240
|
-
export const deleteCache = (name: string) => {
|
|
241
|
-
return cacheManager.deleteCache(name);
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* 清空所有缓存
|
|
246
|
-
*/
|
|
247
|
-
export const clearAllCaches = () => {
|
|
248
|
-
return cacheManager.clearAllCaches();
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* 获取缓存统计信息
|
|
253
|
-
*/
|
|
254
|
-
export const getCacheStats = () => {
|
|
255
|
-
return cacheManager.getStats();
|
|
256
|
-
};
|
|
257
|
-
|
|
258
|
-
/**
|
|
259
|
-
* 获取缓存内存使用情况
|
|
260
|
-
*/
|
|
261
|
-
export const getCacheMemoryUsage = () => {
|
|
262
|
-
return cacheManager.getMemoryUsage();
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
// 导出缓存管理器实例(用于高级操作)
|
|
266
|
-
export { cacheManager };
|
|
267
|
-
|
|
268
|
-
// 导出默认配置(用于参考)
|
|
269
|
-
export { DEFAULT_CACHE_CONFIG, CACHE_TYPE_CONFIGS };
|