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,37 +0,0 @@
|
|
|
1
|
-
import svgCaptcha from 'svg-captcha';
|
|
2
|
-
import jwt from 'jsonwebtoken';
|
|
3
|
-
|
|
4
|
-
export class CaptchaGenerator {
|
|
5
|
-
private static readonly JWT_SECRET = process.env.CAPTCHA_JWT_SECRET || 'captcha_secret';
|
|
6
|
-
private static readonly JWT_EXPIRES_IN = '5m';
|
|
7
|
-
|
|
8
|
-
static generate() {
|
|
9
|
-
const captcha = svgCaptcha.create({
|
|
10
|
-
size: 4,
|
|
11
|
-
ignoreChars: '0o1i',
|
|
12
|
-
noise: 2,
|
|
13
|
-
color: true,
|
|
14
|
-
background: '#f0f0f0'
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
const token = jwt.sign(
|
|
18
|
-
{ captcha: captcha.text.toLowerCase() },
|
|
19
|
-
this.JWT_SECRET,
|
|
20
|
-
{ expiresIn: this.JWT_EXPIRES_IN }
|
|
21
|
-
);
|
|
22
|
-
|
|
23
|
-
return {
|
|
24
|
-
image: captcha.data,
|
|
25
|
-
token
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
static verify(token: string, input: string): boolean {
|
|
30
|
-
try {
|
|
31
|
-
const decoded = jwt.verify(token, this.JWT_SECRET) as { captcha: string };
|
|
32
|
-
return decoded.captcha === input?.toLowerCase();
|
|
33
|
-
} catch {
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
package/template/src/libs/cos.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import COS from "cos-nodejs-sdk-v5";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
|
|
4
|
-
const SECRET_ID = process.env.COS_SECRET_ID || "";
|
|
5
|
-
const SECRET_KEY = process.env.COS_SECRET_KEY || "";
|
|
6
|
-
const BUCKET = process.env.COS_BUCKET || "";
|
|
7
|
-
const REGION = process.env.COS_REGION || "";
|
|
8
|
-
const FILES_HOST =
|
|
9
|
-
process.env.WEBOFFICE_FILES_HOST || process.env.FILES_HOST || "";
|
|
10
|
-
|
|
11
|
-
function getCosClient(): COS | null {
|
|
12
|
-
if (!SECRET_ID || !SECRET_KEY) return null;
|
|
13
|
-
return new COS({
|
|
14
|
-
SecretId: SECRET_ID,
|
|
15
|
-
SecretKey: SECRET_KEY,
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export function isCosConfigured(): boolean {
|
|
20
|
-
return !!(SECRET_ID && SECRET_KEY && BUCKET && REGION);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export async function uploadToCos(localFilePath: string, key: string): Promise<void> {
|
|
24
|
-
const client = getCosClient();
|
|
25
|
-
if (!client) throw new Error("COS client not configured");
|
|
26
|
-
if (!BUCKET || !REGION) throw new Error("COS bucket/region not configured");
|
|
27
|
-
await client.putObject({
|
|
28
|
-
Bucket: BUCKET,
|
|
29
|
-
Region: REGION,
|
|
30
|
-
Key: key,
|
|
31
|
-
Body: fs.createReadStream(localFilePath),
|
|
32
|
-
});
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function getFilePublicUrl(key: string): string {
|
|
36
|
-
if (!FILES_HOST) return "";
|
|
37
|
-
const host = FILES_HOST.replace(/\/$/, "");
|
|
38
|
-
const k = key.replace(/^\//, "");
|
|
39
|
-
return `${host}/${k}`;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function webofficeCosKey(fileId: string, version: number): string {
|
|
43
|
-
return `weboffice/files/${fileId}/v${version}`;
|
|
44
|
-
}
|
|
45
|
-
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import * as tencentcloud from 'tencentcloud-sdk-nodejs';
|
|
2
|
-
import { config } from 'dotenv';
|
|
3
|
-
|
|
4
|
-
type SmsParams = {
|
|
5
|
-
PhoneNumberSet: string[];
|
|
6
|
-
TemplateId: string;
|
|
7
|
-
SignName: string;
|
|
8
|
-
TemplateParamSet?: string[];
|
|
9
|
-
SmsSdkAppId: string;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
export class TencentSms {
|
|
13
|
-
private client: any;
|
|
14
|
-
|
|
15
|
-
constructor() {
|
|
16
|
-
config();
|
|
17
|
-
const SmsClient = tencentcloud.sms.v20210111.Client;
|
|
18
|
-
|
|
19
|
-
this.client = new SmsClient({
|
|
20
|
-
credential: {
|
|
21
|
-
secretId: process.env.TENCENT_SMS_SECRET_ID,
|
|
22
|
-
secretKey: process.env.TENCENT_SMS_SECRET_KEY,
|
|
23
|
-
},
|
|
24
|
-
region: process.env.TENCENT_SMS_REGION || 'ap-guangzhou',
|
|
25
|
-
profile: {
|
|
26
|
-
httpProfile: {
|
|
27
|
-
endpoint: 'sms.tencentcloudapi.com',
|
|
28
|
-
},
|
|
29
|
-
},
|
|
30
|
-
});
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async sendSms(params: SmsParams): Promise<any> {
|
|
34
|
-
try {
|
|
35
|
-
const request = {
|
|
36
|
-
PhoneNumberSet: params.PhoneNumberSet,
|
|
37
|
-
TemplateId: params.TemplateId,
|
|
38
|
-
SignName: params.SignName,
|
|
39
|
-
TemplateParamSet: params.TemplateParamSet || [],
|
|
40
|
-
SmsSdkAppId: params.SmsSdkAppId,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
return await this.client.SendSms(request);
|
|
44
|
-
} catch (error) {
|
|
45
|
-
throw error;
|
|
46
|
-
// throw new Error(`Failed to send SMS: ${error.message}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export const tencentSms = new TencentSms();
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import { logger } from "@src/framework/utils/logger";
|
|
2
|
-
|
|
3
|
-
export default function () {
|
|
4
|
-
return async (ctx: any, next: Function) => {
|
|
5
|
-
try {
|
|
6
|
-
await next();
|
|
7
|
-
} catch (err) {
|
|
8
|
-
const error = err as any;
|
|
9
|
-
ctx.status = 500;
|
|
10
|
-
logger.error((err as Error).message || 'Unknown error');
|
|
11
|
-
return ctx.body = error;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
}
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
import { Context, Next } from 'koa'
|
|
2
|
-
import { logger, createRequestLogger } from '@src/framework/utils/logger'
|
|
3
|
-
import { isDebug } from '@src/framework/utils/function'
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* 企业级日志中间件
|
|
7
|
-
* 提供请求追踪、性能监控、安全审计等功能
|
|
8
|
-
*/
|
|
9
|
-
export const loggingMiddleware = () => {
|
|
10
|
-
return async (ctx: Context, next: Next) => {
|
|
11
|
-
const startTime = Date.now()
|
|
12
|
-
const requestId = logger.generateRequestId()
|
|
13
|
-
|
|
14
|
-
// 创建请求专用的日志器
|
|
15
|
-
const requestLogger = createRequestLogger(requestId)
|
|
16
|
-
|
|
17
|
-
// 设置请求上下文
|
|
18
|
-
requestLogger.setRequestContext(
|
|
19
|
-
requestId,
|
|
20
|
-
ctx.state.user?.id,
|
|
21
|
-
ctx.session?.id
|
|
22
|
-
)
|
|
23
|
-
|
|
24
|
-
// 设置追踪上下文(如果存在)
|
|
25
|
-
const traceId = ctx.get('X-Trace-ID') || ctx.get('X-Request-ID')
|
|
26
|
-
if (traceId) {
|
|
27
|
-
requestLogger.setTraceContext(traceId, requestId)
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// 记录请求开始 - 只在调试模式下记录详细信息
|
|
31
|
-
if (isDebug()) {
|
|
32
|
-
requestLogger.debug('Request started', {
|
|
33
|
-
method: ctx.method,
|
|
34
|
-
url: ctx.url,
|
|
35
|
-
userAgent: ctx.get('User-Agent'),
|
|
36
|
-
ip: ctx.ip,
|
|
37
|
-
headers: sanitizeHeaders(ctx.headers)
|
|
38
|
-
})
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// 审计日志 - 请求访问(只在调试模式下输出到控制台)
|
|
42
|
-
if (isDebug()) {
|
|
43
|
-
requestLogger.audit('REQUEST_ACCESS', ctx.url, {
|
|
44
|
-
method: ctx.method,
|
|
45
|
-
ip: ctx.ip,
|
|
46
|
-
userAgent: ctx.get('User-Agent'),
|
|
47
|
-
userId: ctx.state.user?.id
|
|
48
|
-
})
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// 将日志器添加到上下文
|
|
52
|
-
ctx.logger = requestLogger
|
|
53
|
-
ctx.requestId = requestId
|
|
54
|
-
|
|
55
|
-
try {
|
|
56
|
-
await next()
|
|
57
|
-
|
|
58
|
-
const duration = Date.now() - startTime
|
|
59
|
-
|
|
60
|
-
// 记录请求完成 - 只在调试模式或慢请求时记录
|
|
61
|
-
if (isDebug() || duration > 1000) {
|
|
62
|
-
requestLogger.info('Request completed', {
|
|
63
|
-
method: ctx.method,
|
|
64
|
-
url: ctx.url,
|
|
65
|
-
status: ctx.status,
|
|
66
|
-
duration: `${duration}ms`,
|
|
67
|
-
responseSize: ctx.response.length || 0
|
|
68
|
-
})
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// 性能监控日志 - 只在调试模式下输出到控制台
|
|
72
|
-
if (isDebug()) {
|
|
73
|
-
requestLogger.performance('HTTP_REQUEST', duration, {
|
|
74
|
-
method: ctx.method,
|
|
75
|
-
url: ctx.url,
|
|
76
|
-
status: ctx.status,
|
|
77
|
-
responseSize: ctx.response.length || 0
|
|
78
|
-
})
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
// 安全审计 - 响应状态
|
|
82
|
-
if (ctx.status >= 400) {
|
|
83
|
-
requestLogger.security('HTTP_ERROR_RESPONSE',
|
|
84
|
-
ctx.status >= 500 ? 'high' : 'medium',
|
|
85
|
-
{
|
|
86
|
-
status: ctx.status,
|
|
87
|
-
method: ctx.method,
|
|
88
|
-
url: ctx.url,
|
|
89
|
-
userId: ctx.state.user?.id
|
|
90
|
-
}
|
|
91
|
-
)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
} catch (error) {
|
|
95
|
-
const duration = Date.now() - startTime
|
|
96
|
-
|
|
97
|
-
// 记录错误
|
|
98
|
-
requestLogger.error('Request failed', error as Error, {
|
|
99
|
-
method: ctx.method,
|
|
100
|
-
url: ctx.url,
|
|
101
|
-
duration: `${duration}ms`
|
|
102
|
-
})
|
|
103
|
-
|
|
104
|
-
// 安全审计 - 错误事件
|
|
105
|
-
requestLogger.security('REQUEST_ERROR', 'high', {
|
|
106
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
107
|
-
method: ctx.method,
|
|
108
|
-
url: ctx.url,
|
|
109
|
-
userId: ctx.state.user?.id
|
|
110
|
-
})
|
|
111
|
-
|
|
112
|
-
throw error
|
|
113
|
-
} finally {
|
|
114
|
-
// 清理上下文
|
|
115
|
-
requestLogger.clearContext()
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* 脱敏请求头
|
|
122
|
-
*/
|
|
123
|
-
function sanitizeHeaders(headers: any): any {
|
|
124
|
-
const sanitized = { ...headers }
|
|
125
|
-
const sensitiveHeaders = ['authorization', 'cookie', 'x-api-key', 'x-auth-token']
|
|
126
|
-
|
|
127
|
-
sensitiveHeaders.forEach(header => {
|
|
128
|
-
if (sanitized[header]) {
|
|
129
|
-
sanitized[header] = '***REDACTED***'
|
|
130
|
-
}
|
|
131
|
-
})
|
|
132
|
-
|
|
133
|
-
return sanitized
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
/**
|
|
137
|
-
* 业务日志中间件
|
|
138
|
-
* 用于记录业务操作
|
|
139
|
-
*/
|
|
140
|
-
export const businessLoggingMiddleware = () => {
|
|
141
|
-
return async (ctx: Context, next: Next) => {
|
|
142
|
-
const originalBody = ctx.body
|
|
143
|
-
|
|
144
|
-
await next()
|
|
145
|
-
|
|
146
|
-
// 记录业务操作 - 注释掉,不在控制台输出
|
|
147
|
-
// 如果需要查看业务日志,请查看 logs/structured.log 文件
|
|
148
|
-
// if (ctx.logger && ctx.status < 400 && isDebug()) {
|
|
149
|
-
// ctx.logger.business('API_CALL', {
|
|
150
|
-
// method: ctx.method,
|
|
151
|
-
// url: ctx.url,
|
|
152
|
-
// status: ctx.status,
|
|
153
|
-
// userId: ctx.state.user?.id,
|
|
154
|
-
// timestamp: new Date().toISOString()
|
|
155
|
-
// })
|
|
156
|
-
// }
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* 数据库操作日志中间件
|
|
162
|
-
*/
|
|
163
|
-
export const databaseLoggingMiddleware = () => {
|
|
164
|
-
return async (ctx: Context, next: Next) => {
|
|
165
|
-
const startTime = Date.now()
|
|
166
|
-
|
|
167
|
-
await next()
|
|
168
|
-
|
|
169
|
-
const duration = Date.now() - startTime
|
|
170
|
-
|
|
171
|
-
if (ctx.logger && duration > 100) { // 只记录超过100ms的数据库操作
|
|
172
|
-
ctx.logger.performance('DATABASE_OPERATION', duration, {
|
|
173
|
-
method: ctx.method,
|
|
174
|
-
url: ctx.url,
|
|
175
|
-
userId: ctx.state.user?.id
|
|
176
|
-
})
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
// 扩展Context类型
|
|
182
|
-
declare module 'koa' {
|
|
183
|
-
interface Context {
|
|
184
|
-
logger: any
|
|
185
|
-
requestId: string
|
|
186
|
-
}
|
|
187
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import path from "path";
|
|
2
|
-
import mount from "koa-mount";
|
|
3
|
-
import serve from "koa-static";
|
|
4
|
-
import { isDebug } from "@src/framework/utils/function";
|
|
5
|
-
|
|
6
|
-
export interface StaticMiddlewareOptions {
|
|
7
|
-
/**
|
|
8
|
-
* URL 前缀,例如:/static
|
|
9
|
-
* - 不建议以 / 结尾
|
|
10
|
-
*/
|
|
11
|
-
urlPrefix?: string;
|
|
12
|
-
/**
|
|
13
|
-
* 本地目录(相对项目根目录 process.cwd()),例如:assets / uploads
|
|
14
|
-
*/
|
|
15
|
-
dir?: string;
|
|
16
|
-
/**
|
|
17
|
-
* 生产环境缓存秒数(Cache-Control max-age)
|
|
18
|
-
*/
|
|
19
|
-
maxAgeSeconds?: number;
|
|
20
|
-
/**
|
|
21
|
-
* 访问前缀根路径时的默认文件,例如:index.html
|
|
22
|
-
* - false 表示不提供默认文件
|
|
23
|
-
*/
|
|
24
|
-
index?: string | false;
|
|
25
|
-
/**
|
|
26
|
-
* 是否在 next() 之后再尝试提供静态文件(默认 false,优先静态)
|
|
27
|
-
*/
|
|
28
|
-
defer?: boolean;
|
|
29
|
-
/**
|
|
30
|
-
* 是否允许访问隐藏文件(默认 false)
|
|
31
|
-
*/
|
|
32
|
-
hidden?: boolean;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function normalizePrefix(prefix: string): string {
|
|
36
|
-
let p = prefix.trim();
|
|
37
|
-
if (!p.startsWith("/")) p = `/${p}`;
|
|
38
|
-
// 只保留末尾非根路径的前缀(避免 /static/ 和 /static 产生差异)
|
|
39
|
-
if (p.length > 1 && p.endsWith("/")) p = p.slice(0, -1);
|
|
40
|
-
return p;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 静态文件映射中间件(基于 koa-static + koa-mount)
|
|
45
|
-
* - 默认:/static -> ./static
|
|
46
|
-
* - 支持环境变量覆盖:STATIC_URL_PREFIX、STATIC_DIR、STATIC_MAX_AGE_SECONDS、STATIC_INDEX
|
|
47
|
-
*/
|
|
48
|
-
export default function staticMiddleware(options: StaticMiddlewareOptions = {}) {
|
|
49
|
-
const urlPrefix = normalizePrefix(
|
|
50
|
-
options.urlPrefix ?? process.env.STATIC_URL_PREFIX ?? "/static"
|
|
51
|
-
);
|
|
52
|
-
const dir = options.dir ?? process.env.STATIC_DIR ?? "static";
|
|
53
|
-
const root = path.resolve(process.cwd(), dir);
|
|
54
|
-
const index =
|
|
55
|
-
options.index ??
|
|
56
|
-
(typeof process.env.STATIC_INDEX === "string"
|
|
57
|
-
? process.env.STATIC_INDEX
|
|
58
|
-
: false);
|
|
59
|
-
|
|
60
|
-
const maxAgeSeconds =
|
|
61
|
-
options.maxAgeSeconds ??
|
|
62
|
-
(process.env.STATIC_MAX_AGE_SECONDS
|
|
63
|
-
? Number(process.env.STATIC_MAX_AGE_SECONDS)
|
|
64
|
-
: !isDebug()
|
|
65
|
-
? 86400
|
|
66
|
-
: 0);
|
|
67
|
-
|
|
68
|
-
return mount(
|
|
69
|
-
urlPrefix,
|
|
70
|
-
serve(root, {
|
|
71
|
-
maxage: Math.max(0, maxAgeSeconds) * 1000,
|
|
72
|
-
index,
|
|
73
|
-
defer: options.defer ?? false,
|
|
74
|
-
hidden: options.hidden ?? false,
|
|
75
|
-
})
|
|
76
|
-
);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
|
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import { getSwaggerSpec } from '@src/framework/utils/swagger';
|
|
2
|
-
import { logger } from '@src/framework/utils/logger';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Swagger UI中间件 - 简化版本
|
|
6
|
-
*/
|
|
7
|
-
export function swaggerMiddleware() {
|
|
8
|
-
logger.info('Swagger UI已配置');
|
|
9
|
-
|
|
10
|
-
return async (ctx: any, next: any) => {
|
|
11
|
-
if (ctx.path === '/swagger') {
|
|
12
|
-
ctx.set('Content-Type', 'text/html');
|
|
13
|
-
ctx.body = `
|
|
14
|
-
<!DOCTYPE html>
|
|
15
|
-
<html>
|
|
16
|
-
<head>
|
|
17
|
-
<title>DP Koa Framework API文档</title>
|
|
18
|
-
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@3.25.0/swagger-ui.css" />
|
|
19
|
-
<style>
|
|
20
|
-
.topbar { display: none !important; }
|
|
21
|
-
.swagger-ui .info { margin: 20px 0; }
|
|
22
|
-
</style>
|
|
23
|
-
</head>
|
|
24
|
-
<body>
|
|
25
|
-
<div id="swagger-ui"></div>
|
|
26
|
-
<script src="https://unpkg.com/swagger-ui-dist@3.25.0/swagger-ui-bundle.js"></script>
|
|
27
|
-
<script>
|
|
28
|
-
SwaggerUIBundle({
|
|
29
|
-
url: '/swagger.json',
|
|
30
|
-
dom_id: '#swagger-ui',
|
|
31
|
-
presets: [
|
|
32
|
-
SwaggerUIBundle.presets.apis,
|
|
33
|
-
SwaggerUIBundle.presets.standalone
|
|
34
|
-
],
|
|
35
|
-
layout: "StandaloneLayout"
|
|
36
|
-
});
|
|
37
|
-
</script>
|
|
38
|
-
</body>
|
|
39
|
-
</html>
|
|
40
|
-
`;
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
await next();
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Swagger JSON端点
|
|
49
|
-
*/
|
|
50
|
-
export async function swaggerJson(ctx: any, next: any) {
|
|
51
|
-
if (ctx.path === '/swagger.json') {
|
|
52
|
-
try {
|
|
53
|
-
const swaggerSpec = getSwaggerSpec();
|
|
54
|
-
|
|
55
|
-
// 动态从Controller中提取Swagger信息
|
|
56
|
-
// 这里可以集成现有的路由系统来生成完整的文档
|
|
57
|
-
|
|
58
|
-
ctx.set('Content-Type', 'application/json');
|
|
59
|
-
ctx.body = swaggerSpec;
|
|
60
|
-
} catch (error) {
|
|
61
|
-
logger.error('生成Swagger文档失败:', error as Error);
|
|
62
|
-
ctx.status = 500;
|
|
63
|
-
ctx.body = { error: '无法生成Swagger文档' };
|
|
64
|
-
}
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
await next();
|
|
69
|
-
}
|
|
70
|
-
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import * as jwt from "jsonwebtoken";
|
|
2
|
-
import { Context } from "koa";
|
|
3
|
-
import { getTokenFromHeader } from "@src/framework/utils/token";
|
|
4
|
-
|
|
5
|
-
export default function tokenMiddleware() {
|
|
6
|
-
|
|
7
|
-
return async (ctx: Context, next: Function) => {
|
|
8
|
-
// 1. 从请求头获取token
|
|
9
|
-
const token = getTokenFromHeader(ctx);
|
|
10
|
-
if (!token) {
|
|
11
|
-
ctx.status = 401;
|
|
12
|
-
ctx.body = { code: -1, message: "未提供认证token" };
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
try {
|
|
17
|
-
// 2. 验证token
|
|
18
|
-
const decoded = jwt.verify(token, process.env.jwt_secret ?? "123456") as any;
|
|
19
|
-
|
|
20
|
-
// 3. 将用户信息挂载到ctx.state
|
|
21
|
-
ctx.state.user = {
|
|
22
|
-
userId: decoded.userId,
|
|
23
|
-
type: decoded.type,
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
await next();
|
|
27
|
-
} catch (err) {
|
|
28
|
-
ctx.status = 401;
|
|
29
|
-
ctx.body = { code: -1, message: "无效或过期的token" };
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
}
|