@dangao/bun-server 1.3.0 → 1.5.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/README.md +15 -0
- package/dist/config/config-module.d.ts +17 -0
- package/dist/config/config-module.d.ts.map +1 -1
- package/dist/config/service.d.ts +18 -1
- package/dist/config/service.d.ts.map +1 -1
- package/dist/config/types.d.ts +25 -0
- package/dist/config/types.d.ts.map +1 -1
- package/dist/controller/controller.d.ts +5 -0
- package/dist/controller/controller.d.ts.map +1 -1
- package/dist/core/application.d.ts +42 -1
- package/dist/core/application.d.ts.map +1 -1
- package/dist/core/context.d.ts +1 -0
- package/dist/core/context.d.ts.map +1 -1
- package/dist/core/server.d.ts +33 -1
- package/dist/core/server.d.ts.map +1 -1
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6510 -4164
- package/dist/microservice/config-center/config-center-module.d.ts +43 -0
- package/dist/microservice/config-center/config-center-module.d.ts.map +1 -0
- package/dist/microservice/config-center/decorators.d.ts +58 -0
- package/dist/microservice/config-center/decorators.d.ts.map +1 -0
- package/dist/microservice/config-center/index.d.ts +9 -0
- package/dist/microservice/config-center/index.d.ts.map +1 -0
- package/dist/microservice/config-center/nacos-config-center.d.ts +37 -0
- package/dist/microservice/config-center/nacos-config-center.d.ts.map +1 -0
- package/dist/microservice/config-center/nacos-decorators.d.ts +24 -0
- package/dist/microservice/config-center/nacos-decorators.d.ts.map +1 -0
- package/dist/microservice/config-center/types.d.ts +63 -0
- package/dist/microservice/config-center/types.d.ts.map +1 -0
- package/dist/microservice/governance/circuit-breaker.d.ts +54 -0
- package/dist/microservice/governance/circuit-breaker.d.ts.map +1 -0
- package/dist/microservice/governance/decorators.d.ts +51 -0
- package/dist/microservice/governance/decorators.d.ts.map +1 -0
- package/dist/microservice/governance/index.d.ts +9 -0
- package/dist/microservice/governance/index.d.ts.map +1 -0
- package/dist/microservice/governance/rate-limiter.d.ts +26 -0
- package/dist/microservice/governance/rate-limiter.d.ts.map +1 -0
- package/dist/microservice/governance/redis-rate-limiter.d.ts +76 -0
- package/dist/microservice/governance/redis-rate-limiter.d.ts.map +1 -0
- package/dist/microservice/governance/retry-strategy.d.ts +21 -0
- package/dist/microservice/governance/retry-strategy.d.ts.map +1 -0
- package/dist/microservice/governance/types.d.ts +212 -0
- package/dist/microservice/governance/types.d.ts.map +1 -0
- package/dist/microservice/index.d.ts +10 -0
- package/dist/microservice/index.d.ts.map +1 -0
- package/dist/microservice/monitoring/index.d.ts +4 -0
- package/dist/microservice/monitoring/index.d.ts.map +1 -0
- package/dist/microservice/monitoring/metrics-collector.d.ts +54 -0
- package/dist/microservice/monitoring/metrics-collector.d.ts.map +1 -0
- package/dist/microservice/monitoring/metrics-integration.d.ts +24 -0
- package/dist/microservice/monitoring/metrics-integration.d.ts.map +1 -0
- package/dist/microservice/monitoring/types.d.ts +99 -0
- package/dist/microservice/monitoring/types.d.ts.map +1 -0
- package/dist/microservice/service-client/call-decorators.d.ts +52 -0
- package/dist/microservice/service-client/call-decorators.d.ts.map +1 -0
- package/dist/microservice/service-client/decorators.d.ts +35 -0
- package/dist/microservice/service-client/decorators.d.ts.map +1 -0
- package/dist/microservice/service-client/index.d.ts +7 -0
- package/dist/microservice/service-client/index.d.ts.map +1 -0
- package/dist/microservice/service-client/interceptors.d.ts +96 -0
- package/dist/microservice/service-client/interceptors.d.ts.map +1 -0
- package/dist/microservice/service-client/load-balancer.d.ts +59 -0
- package/dist/microservice/service-client/load-balancer.d.ts.map +1 -0
- package/dist/microservice/service-client/service-client.d.ts +74 -0
- package/dist/microservice/service-client/service-client.d.ts.map +1 -0
- package/dist/microservice/service-client/types.d.ts +155 -0
- package/dist/microservice/service-client/types.d.ts.map +1 -0
- package/dist/microservice/service-registry/decorators.d.ts +84 -0
- package/dist/microservice/service-registry/decorators.d.ts.map +1 -0
- package/dist/microservice/service-registry/discovery-decorators.d.ts +58 -0
- package/dist/microservice/service-registry/discovery-decorators.d.ts.map +1 -0
- package/dist/microservice/service-registry/health-integration.d.ts +32 -0
- package/dist/microservice/service-registry/health-integration.d.ts.map +1 -0
- package/dist/microservice/service-registry/index.d.ts +10 -0
- package/dist/microservice/service-registry/index.d.ts.map +1 -0
- package/dist/microservice/service-registry/nacos-service-registry.d.ts +68 -0
- package/dist/microservice/service-registry/nacos-service-registry.d.ts.map +1 -0
- package/dist/microservice/service-registry/service-registry-module.d.ts +48 -0
- package/dist/microservice/service-registry/service-registry-module.d.ts.map +1 -0
- package/dist/microservice/service-registry/types.d.ts +121 -0
- package/dist/microservice/service-registry/types.d.ts.map +1 -0
- package/dist/microservice/tracing/collectors.d.ts +27 -0
- package/dist/microservice/tracing/collectors.d.ts.map +1 -0
- package/dist/microservice/tracing/index.d.ts +4 -0
- package/dist/microservice/tracing/index.d.ts.map +1 -0
- package/dist/microservice/tracing/tracer.d.ts +59 -0
- package/dist/microservice/tracing/tracer.d.ts.map +1 -0
- package/dist/microservice/tracing/types.d.ts +179 -0
- package/dist/microservice/tracing/types.d.ts.map +1 -0
- package/dist/request/request.d.ts +1 -0
- package/dist/request/request.d.ts.map +1 -1
- package/docs/microservice-config-center.md +258 -0
- package/docs/microservice-nacos.md +346 -0
- package/docs/microservice-service-registry.md +306 -0
- package/docs/microservice.md +680 -0
- package/docs/troubleshooting.md +41 -0
- package/docs/zh/troubleshooting.md +41 -0
- package/package.json +5 -4
- package/src/config/config-module.ts +210 -0
- package/src/config/service.ts +52 -1
- package/src/config/types.ts +31 -0
- package/src/controller/controller.ts +8 -0
- package/src/core/application.ts +189 -3
- package/src/core/context.ts +1 -0
- package/src/core/server.ts +128 -2
- package/src/index.ts +81 -0
- package/src/microservice/config-center/config-center-module.ts +98 -0
- package/src/microservice/config-center/decorators.ts +159 -0
- package/src/microservice/config-center/index.ts +13 -0
- package/src/microservice/config-center/nacos-config-center.ts +126 -0
- package/src/microservice/config-center/nacos-decorators.ts +34 -0
- package/src/microservice/config-center/types.ts +80 -0
- package/src/microservice/governance/circuit-breaker.ts +229 -0
- package/src/microservice/governance/decorators.ts +113 -0
- package/src/microservice/governance/index.ts +18 -0
- package/src/microservice/governance/rate-limiter.ts +72 -0
- package/src/microservice/governance/redis-rate-limiter.ts +154 -0
- package/src/microservice/governance/retry-strategy.ts +74 -0
- package/src/microservice/governance/types.ts +247 -0
- package/src/microservice/index.ts +12 -0
- package/src/microservice/monitoring/index.ts +8 -0
- package/src/microservice/monitoring/metrics-collector.ts +223 -0
- package/src/microservice/monitoring/metrics-integration.ts +154 -0
- package/src/microservice/monitoring/types.ts +118 -0
- package/src/microservice/service-client/call-decorators.ts +107 -0
- package/src/microservice/service-client/decorators.ts +87 -0
- package/src/microservice/service-client/index.ts +37 -0
- package/src/microservice/service-client/interceptors.ts +182 -0
- package/src/microservice/service-client/load-balancer.ts +205 -0
- package/src/microservice/service-client/service-client.ts +488 -0
- package/src/microservice/service-client/types.ts +186 -0
- package/src/microservice/service-registry/decorators.ts +238 -0
- package/src/microservice/service-registry/discovery-decorators.ts +156 -0
- package/src/microservice/service-registry/health-integration.ts +146 -0
- package/src/microservice/service-registry/index.ts +20 -0
- package/src/microservice/service-registry/nacos-service-registry.ts +259 -0
- package/src/microservice/service-registry/service-registry-module.ts +105 -0
- package/src/microservice/service-registry/types.ts +149 -0
- package/src/microservice/tracing/collectors.ts +50 -0
- package/src/microservice/tracing/index.ts +15 -0
- package/src/microservice/tracing/tracer.ts +293 -0
- package/src/microservice/tracing/types.ts +213 -0
- package/src/request/request.ts +1 -0
- package/tests/config/set-value-by-path.test.ts +53 -0
- package/tests/core/graceful-shutdown.test.ts +321 -0
- package/tests/microservice/config-center.test.ts +77 -0
- package/tests/microservice/governance.test.ts +157 -0
- package/tests/microservice/monitoring.test.ts +75 -0
- package/tests/microservice/service-client.test.ts +136 -0
- package/tests/microservice/service-registry.test.ts +80 -0
- package/tests/microservice/tracing.test.ts +143 -0
- package/tests/utils/test-port.ts +29 -19
|
@@ -93,6 +93,47 @@ const { SomeService } = await import("./some-service");
|
|
|
93
93
|
|
|
94
94
|
## 依赖注入
|
|
95
95
|
|
|
96
|
+
### ⚠️ 重要:注入的依赖为 undefined
|
|
97
|
+
|
|
98
|
+
**错误**:注入的服务在运行时为 `undefined`,或通过 `bun app.ts` 运行正常,但在 VSCode 调试器中失败。
|
|
99
|
+
|
|
100
|
+
**原因**:`tsconfig.json` 中缺少 TypeScript 装饰器元数据配置。
|
|
101
|
+
|
|
102
|
+
**解决方案**:**这非常重要!** 确保你的 `tsconfig.json` 包含以下两个选项:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"compilerOptions": {
|
|
107
|
+
"emitDecoratorMetadata": true,
|
|
108
|
+
"experimentalDecorators": true
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**为什么重要**:
|
|
114
|
+
- `emitDecoratorMetadata`:使 TypeScript 能够为装饰器发出元数据,这是依赖注入正常工作所必需的
|
|
115
|
+
- `experimentalDecorators`:启用装饰器语法支持
|
|
116
|
+
- 没有这些配置,DI 容器无法确定参数类型,将注入 `undefined`
|
|
117
|
+
|
|
118
|
+
**检查所有 tsconfig.json 文件**:
|
|
119
|
+
- 根目录的 `tsconfig.json`
|
|
120
|
+
- `examples/tsconfig.json`
|
|
121
|
+
- 任何项目特定的 `tsconfig.json` 文件
|
|
122
|
+
|
|
123
|
+
**示例**:
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"extends": "../tsconfig.json",
|
|
127
|
+
"compilerOptions": {
|
|
128
|
+
"outDir": "dist",
|
|
129
|
+
"rootDir": "./",
|
|
130
|
+
"emitDecoratorMetadata": true, // ⚠️ 必需
|
|
131
|
+
"experimentalDecorators": true // ⚠️ 必需
|
|
132
|
+
},
|
|
133
|
+
"include": ["**/*.ts"]
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
96
137
|
### 服务不可注入
|
|
97
138
|
|
|
98
139
|
**错误**:`Cannot resolve dependency` 或找不到服务。
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dangao/bun-server",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -45,10 +45,10 @@
|
|
|
45
45
|
"test": "bun test",
|
|
46
46
|
"type-check": "tsc --noEmit",
|
|
47
47
|
"clean": "rm -rf dist",
|
|
48
|
-
"bundle": "bun build src/index.ts --target=bun --format=esm --outfile=dist/index.js --external reflect-metadata --external @dangao/logsmith",
|
|
48
|
+
"bundle": "bun build src/index.ts --target=bun --format=esm --outfile=dist/index.js --external reflect-metadata --external @dangao/logsmith --external @dangao/nacos-client",
|
|
49
49
|
"dts": "tsc -p tsconfig.build.json",
|
|
50
50
|
"build": "bun run clean && bun run bundle && bun run dts",
|
|
51
|
-
"
|
|
51
|
+
"prepublishOnly": "bun run build",
|
|
52
52
|
"publish:package": "cp -r ../../README.md ../../LICENSE ../../docs . && bun publish --access public && rm -rf ./README.md ./LICENSE ./docs"
|
|
53
53
|
},
|
|
54
54
|
"devDependencies": {
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
},
|
|
58
58
|
"dependencies": {
|
|
59
59
|
"reflect-metadata": "^0.2.2",
|
|
60
|
-
"@dangao/logsmith": "0.1.1"
|
|
60
|
+
"@dangao/logsmith": "0.1.1",
|
|
61
|
+
"@dangao/nacos-client": "0.1.0"
|
|
61
62
|
}
|
|
62
63
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import { Module, MODULE_METADATA_KEY, type ModuleProvider } from '../di/module';
|
|
2
|
+
import { CONFIG_CENTER_TOKEN } from '../microservice/config-center/types';
|
|
3
|
+
import type { ConfigCenter } from '../microservice/config-center/types';
|
|
4
|
+
import { ControllerRegistry } from '../controller/controller';
|
|
2
5
|
|
|
3
6
|
import { ConfigService } from './service';
|
|
4
7
|
import { CONFIG_SERVICE_TOKEN, type ConfigModuleOptions } from './types';
|
|
@@ -32,6 +35,13 @@ export class ConfigModule {
|
|
|
32
35
|
|
|
33
36
|
const service = new ConfigService(mergedConfig, options.namespace);
|
|
34
37
|
|
|
38
|
+
// 如果启用配置中心集成,延迟初始化(等待 ConfigCenterModule 注册)
|
|
39
|
+
// 初始化将在 Application.listen() 时执行,确保所有模块都已注册
|
|
40
|
+
if (options.configCenter?.enabled) {
|
|
41
|
+
// 保存配置中心选项,稍后在应用启动时初始化
|
|
42
|
+
(service as any)._configCenterOptions = options.configCenter;
|
|
43
|
+
}
|
|
44
|
+
|
|
35
45
|
if (options.validate) {
|
|
36
46
|
options.validate(mergedConfig);
|
|
37
47
|
}
|
|
@@ -71,6 +81,206 @@ export class ConfigModule {
|
|
|
71
81
|
}
|
|
72
82
|
return env;
|
|
73
83
|
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 初始化配置中心集成(延迟初始化,在应用启动时调用)
|
|
87
|
+
*/
|
|
88
|
+
public static async initializeConfigCenter(
|
|
89
|
+
service: ConfigService,
|
|
90
|
+
configCenterOptions: NonNullable<ConfigModuleOptions['configCenter']>,
|
|
91
|
+
): Promise<void> {
|
|
92
|
+
// 加载配置中心配置
|
|
93
|
+
const configCenterConfig = await ConfigModule.loadConfigCenterConfig(
|
|
94
|
+
configCenterOptions,
|
|
95
|
+
);
|
|
96
|
+
if (configCenterConfig) {
|
|
97
|
+
// 根据优先级合并配置
|
|
98
|
+
const currentConfig = service.getAll();
|
|
99
|
+
let updatedConfig: Record<string, unknown>;
|
|
100
|
+
if (configCenterOptions.configCenterPriority !== false) {
|
|
101
|
+
// 配置中心配置 > 环境变量 > 默认配置
|
|
102
|
+
updatedConfig = {
|
|
103
|
+
...currentConfig,
|
|
104
|
+
...configCenterConfig,
|
|
105
|
+
};
|
|
106
|
+
} else {
|
|
107
|
+
// 默认配置 > 环境变量 > 配置中心配置(配置中心配置不覆盖现有配置)
|
|
108
|
+
updatedConfig = {
|
|
109
|
+
...configCenterConfig,
|
|
110
|
+
...currentConfig,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
service.updateConfig(updatedConfig as any);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// 设置配置变更监听
|
|
117
|
+
if (configCenterOptions.configs) {
|
|
118
|
+
ConfigModule.setupConfigCenterWatcher(service, configCenterOptions);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 从配置中心加载配置
|
|
124
|
+
*/
|
|
125
|
+
private static async loadConfigCenterConfig(
|
|
126
|
+
configCenterOptions: NonNullable<ConfigModuleOptions['configCenter']>,
|
|
127
|
+
retries: number = 5,
|
|
128
|
+
delay: number = 200,
|
|
129
|
+
): Promise<Record<string, unknown> | null> {
|
|
130
|
+
const container = ControllerRegistry.getInstance().getContainer();
|
|
131
|
+
|
|
132
|
+
// 重试机制:等待 ConfigCenterModule 注册完成
|
|
133
|
+
let configCenter: ConfigCenter | undefined;
|
|
134
|
+
for (let i = 0; i < retries; i++) {
|
|
135
|
+
configCenter = container.resolve<ConfigCenter>(CONFIG_CENTER_TOKEN);
|
|
136
|
+
if (configCenter) {
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
if (i < retries - 1) {
|
|
140
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!configCenter) {
|
|
145
|
+
console.warn(
|
|
146
|
+
'[ConfigModule] ConfigCenter not found after retries, skipping config center integration. ' +
|
|
147
|
+
'Make sure ConfigCenterModule is registered before ConfigModule.',
|
|
148
|
+
);
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
|
|
154
|
+
if (!configCenterOptions.configs || configCenterOptions.configs.size === 0) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const configMap: Record<string, unknown> = {};
|
|
159
|
+
|
|
160
|
+
// 并行加载所有配置
|
|
161
|
+
const loadPromises: Promise<void>[] = [];
|
|
162
|
+
for (const [configPath, configInfo] of configCenterOptions.configs.entries()) {
|
|
163
|
+
loadPromises.push(
|
|
164
|
+
configCenter
|
|
165
|
+
.getConfig(
|
|
166
|
+
configInfo.dataId,
|
|
167
|
+
configInfo.groupName,
|
|
168
|
+
configInfo.namespaceId,
|
|
169
|
+
)
|
|
170
|
+
.then((result) => {
|
|
171
|
+
// 解析配置内容(支持 JSON)
|
|
172
|
+
try {
|
|
173
|
+
const parsed = JSON.parse(result.content);
|
|
174
|
+
ConfigModule.setValueByPath(configMap, configPath, parsed);
|
|
175
|
+
} catch {
|
|
176
|
+
// 如果不是 JSON,直接使用字符串值
|
|
177
|
+
ConfigModule.setValueByPath(configMap, configPath, result.content);
|
|
178
|
+
}
|
|
179
|
+
})
|
|
180
|
+
.catch((error) => {
|
|
181
|
+
console.error(
|
|
182
|
+
`[ConfigModule] Failed to load config ${configInfo.dataId}:`,
|
|
183
|
+
error,
|
|
184
|
+
);
|
|
185
|
+
}),
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
await Promise.all(loadPromises);
|
|
190
|
+
|
|
191
|
+
return configMap;
|
|
192
|
+
} catch (error) {
|
|
193
|
+
console.error('[ConfigModule] Failed to load config center config:', error);
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 设置配置中心监听器
|
|
200
|
+
*/
|
|
201
|
+
private static setupConfigCenterWatcher(
|
|
202
|
+
service: ConfigService,
|
|
203
|
+
configCenterOptions: NonNullable<ConfigModuleOptions['configCenter']>,
|
|
204
|
+
): void {
|
|
205
|
+
try {
|
|
206
|
+
const container = ControllerRegistry.getInstance().getContainer();
|
|
207
|
+
const configCenter = container.resolve<ConfigCenter>(CONFIG_CENTER_TOKEN);
|
|
208
|
+
|
|
209
|
+
if (!configCenter || !configCenterOptions.configs) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// 为每个配置设置监听器
|
|
214
|
+
for (const [configPath, configInfo] of configCenterOptions.configs.entries()) {
|
|
215
|
+
configCenter.watchConfig(
|
|
216
|
+
configInfo.dataId,
|
|
217
|
+
configInfo.groupName,
|
|
218
|
+
(result) => {
|
|
219
|
+
try {
|
|
220
|
+
// 解析配置内容
|
|
221
|
+
let parsedValue: unknown;
|
|
222
|
+
try {
|
|
223
|
+
parsedValue = JSON.parse(result.content);
|
|
224
|
+
} catch {
|
|
225
|
+
parsedValue = result.content;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// 更新配置服务
|
|
229
|
+
const currentConfig = service.getAll();
|
|
230
|
+
const updatedConfig = {
|
|
231
|
+
...currentConfig,
|
|
232
|
+
};
|
|
233
|
+
ConfigModule.setValueByPath(updatedConfig, configPath, parsedValue);
|
|
234
|
+
service.updateConfig(updatedConfig as any);
|
|
235
|
+
|
|
236
|
+
console.log(
|
|
237
|
+
`[ConfigModule] Config updated: ${configPath} from ${configInfo.dataId}`,
|
|
238
|
+
);
|
|
239
|
+
} catch (error) {
|
|
240
|
+
console.error(
|
|
241
|
+
`[ConfigModule] Failed to update config ${configPath}:`,
|
|
242
|
+
error,
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
configInfo.namespaceId,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error(
|
|
251
|
+
'[ConfigModule] Failed to setup config center watcher:',
|
|
252
|
+
error,
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* 根据路径设置对象值(支持点号路径)
|
|
259
|
+
*/
|
|
260
|
+
private static setValueByPath(
|
|
261
|
+
obj: Record<string, unknown>,
|
|
262
|
+
path: string,
|
|
263
|
+
value: unknown,
|
|
264
|
+
): void {
|
|
265
|
+
const segments = path.split('.');
|
|
266
|
+
let current: Record<string, unknown> = obj;
|
|
267
|
+
|
|
268
|
+
for (let i = 0; i < segments.length - 1; i++) {
|
|
269
|
+
const segment = segments[i]!;
|
|
270
|
+
// 检查是否需要创建嵌套对象
|
|
271
|
+
// 注意:typeof null === 'object',所以需要明确排除 null
|
|
272
|
+
if (
|
|
273
|
+
!(segment in current) ||
|
|
274
|
+
current[segment] == null ||
|
|
275
|
+
typeof current[segment] !== 'object'
|
|
276
|
+
) {
|
|
277
|
+
current[segment] = {};
|
|
278
|
+
}
|
|
279
|
+
current = current[segment] as Record<string, unknown>;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
current[segments[segments.length - 1]!] = value;
|
|
283
|
+
}
|
|
74
284
|
}
|
|
75
285
|
|
|
76
286
|
|
package/src/config/service.ts
CHANGED
|
@@ -3,14 +3,65 @@
|
|
|
3
3
|
* 提供类型安全的配置访问能力
|
|
4
4
|
*/
|
|
5
5
|
export class ConfigService<TConfig extends Record<string, unknown> = Record<string, unknown>> {
|
|
6
|
-
private
|
|
6
|
+
private config: TConfig;
|
|
7
7
|
private readonly namespace?: string;
|
|
8
|
+
private configUpdateListeners: Array<(config: TConfig) => void> = [];
|
|
8
9
|
|
|
9
10
|
public constructor(config: TConfig, namespace?: string) {
|
|
10
11
|
this.config = config;
|
|
11
12
|
this.namespace = namespace;
|
|
12
13
|
}
|
|
13
14
|
|
|
15
|
+
/**
|
|
16
|
+
* 更新配置(用于配置中心动态刷新)
|
|
17
|
+
* @param newConfig - 新配置对象
|
|
18
|
+
*/
|
|
19
|
+
public updateConfig(newConfig: TConfig): void {
|
|
20
|
+
this.config = newConfig;
|
|
21
|
+
// 通知所有监听器
|
|
22
|
+
for (const listener of this.configUpdateListeners) {
|
|
23
|
+
try {
|
|
24
|
+
listener(newConfig);
|
|
25
|
+
} catch (error) {
|
|
26
|
+
console.error('[ConfigService] Error in config update listener:', error);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* 合并配置(用于配置中心增量更新)
|
|
33
|
+
* @param partialConfig - 部分配置对象
|
|
34
|
+
*/
|
|
35
|
+
public mergeConfig(partialConfig: Partial<TConfig>): void {
|
|
36
|
+
this.config = {
|
|
37
|
+
...this.config,
|
|
38
|
+
...partialConfig,
|
|
39
|
+
} as TConfig;
|
|
40
|
+
// 通知所有监听器
|
|
41
|
+
for (const listener of this.configUpdateListeners) {
|
|
42
|
+
try {
|
|
43
|
+
listener(this.config);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
console.error('[ConfigService] Error in config update listener:', error);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 添加配置更新监听器
|
|
52
|
+
* @param listener - 监听器函数
|
|
53
|
+
* @returns 取消监听的函数
|
|
54
|
+
*/
|
|
55
|
+
public onConfigUpdate(listener: (config: TConfig) => void): () => void {
|
|
56
|
+
this.configUpdateListeners.push(listener);
|
|
57
|
+
return () => {
|
|
58
|
+
const index = this.configUpdateListeners.indexOf(listener);
|
|
59
|
+
if (index > -1) {
|
|
60
|
+
this.configUpdateListeners.splice(index, 1);
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
14
65
|
/**
|
|
15
66
|
* 获取完整配置对象
|
|
16
67
|
*/
|
package/src/config/types.ts
CHANGED
|
@@ -20,6 +20,37 @@ export interface ConfigModuleOptions<TConfig extends Record<string, unknown> = R
|
|
|
20
20
|
* 命名空间前缀,用于逻辑分组(可选)
|
|
21
21
|
*/
|
|
22
22
|
namespace?: string;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 配置中心集成选项(可选)
|
|
26
|
+
*/
|
|
27
|
+
configCenter?: {
|
|
28
|
+
/**
|
|
29
|
+
* 是否启用配置中心集成
|
|
30
|
+
*/
|
|
31
|
+
enabled?: boolean;
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 配置中心配置映射
|
|
35
|
+
* key: 配置路径(如 "app.name")
|
|
36
|
+
* value: { dataId: string, groupName: string, namespaceId?: string }
|
|
37
|
+
*/
|
|
38
|
+
configs?: Map<
|
|
39
|
+
string,
|
|
40
|
+
{
|
|
41
|
+
dataId: string;
|
|
42
|
+
groupName: string;
|
|
43
|
+
namespaceId?: string;
|
|
44
|
+
}
|
|
45
|
+
>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* 配置优先级
|
|
49
|
+
* 配置中心配置 > 环境变量 > 默认配置
|
|
50
|
+
* @default true
|
|
51
|
+
*/
|
|
52
|
+
configCenterPriority?: boolean;
|
|
53
|
+
};
|
|
23
54
|
}
|
|
24
55
|
|
|
25
56
|
export const CONFIG_SERVICE_TOKEN = Symbol('@dangao/bun-server:config:service');
|
|
@@ -258,6 +258,14 @@ export class ControllerRegistry {
|
|
|
258
258
|
return this.container;
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
/**
|
|
262
|
+
* 获取所有已注册的控制器类
|
|
263
|
+
* @returns 控制器类数组
|
|
264
|
+
*/
|
|
265
|
+
public getAllControllers(): Constructor<unknown>[] {
|
|
266
|
+
return Array.from(this.controllerContainers.keys());
|
|
267
|
+
}
|
|
268
|
+
|
|
261
269
|
/**
|
|
262
270
|
* 获取所有已注册的控制器类
|
|
263
271
|
* @returns 控制器类数组
|
package/src/core/application.ts
CHANGED
|
@@ -13,6 +13,10 @@ import { ModuleRegistry } from '../di/module-registry';
|
|
|
13
13
|
import type { ModuleClass } from '../di/module';
|
|
14
14
|
import type { Constructor } from './types';
|
|
15
15
|
import { InterceptorRegistry, INTERCEPTOR_REGISTRY_TOKEN } from '../interceptor';
|
|
16
|
+
import { CONFIG_SERVICE_TOKEN } from '../config/types';
|
|
17
|
+
import { ConfigService } from '../config/service';
|
|
18
|
+
import { ConfigModule } from '../config/config-module';
|
|
19
|
+
import { LoggerManager } from '@dangao/logsmith';
|
|
16
20
|
|
|
17
21
|
/**
|
|
18
22
|
* 应用配置选项
|
|
@@ -27,6 +31,18 @@ export interface ApplicationOptions {
|
|
|
27
31
|
* 主机名
|
|
28
32
|
*/
|
|
29
33
|
hostname?: string;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 优雅停机超时时间(毫秒)
|
|
37
|
+
* 默认 30 秒
|
|
38
|
+
*/
|
|
39
|
+
gracefulShutdownTimeout?: number;
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 是否启用信号监听(SIGTERM、SIGINT)
|
|
43
|
+
* 默认 true
|
|
44
|
+
*/
|
|
45
|
+
enableSignalHandlers?: boolean;
|
|
30
46
|
}
|
|
31
47
|
|
|
32
48
|
/**
|
|
@@ -39,6 +55,7 @@ export class Application {
|
|
|
39
55
|
private readonly middlewarePipeline: MiddlewarePipeline;
|
|
40
56
|
private readonly websocketRegistry: WebSocketGatewayRegistry;
|
|
41
57
|
private readonly extensions: ApplicationExtension[] = [];
|
|
58
|
+
private signalHandlersInstalled: boolean = false;
|
|
42
59
|
|
|
43
60
|
public constructor(options: ApplicationOptions = {}) {
|
|
44
61
|
this.options = options;
|
|
@@ -81,15 +98,30 @@ export class Application {
|
|
|
81
98
|
// 初始化所有扩展(包括数据库连接等)
|
|
82
99
|
await this.initializeExtensions();
|
|
83
100
|
|
|
101
|
+
// 初始化配置中心集成(在所有模块注册完成后)
|
|
102
|
+
await this.initializeConfigCenter();
|
|
103
|
+
|
|
104
|
+
const finalPort = port ?? this.options.port ?? 3000;
|
|
105
|
+
const finalHostname = hostname ?? this.options.hostname;
|
|
106
|
+
|
|
84
107
|
const serverOptions: ServerOptions = {
|
|
85
|
-
port:
|
|
86
|
-
hostname:
|
|
108
|
+
port: finalPort,
|
|
109
|
+
hostname: finalHostname,
|
|
87
110
|
fetch: this.handleRequest.bind(this),
|
|
88
111
|
websocketRegistry: this.websocketRegistry,
|
|
112
|
+
gracefulShutdownTimeout: this.options.gracefulShutdownTimeout,
|
|
89
113
|
};
|
|
90
114
|
|
|
91
115
|
this.server = new BunServer(serverOptions);
|
|
92
116
|
this.server.start();
|
|
117
|
+
|
|
118
|
+
// 安装信号处理器(如果启用)
|
|
119
|
+
if (this.options.enableSignalHandlers !== false) {
|
|
120
|
+
this.installSignalHandlers();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// 自动注册服务到注册中心(如果使用了 @ServiceRegistry 装饰器)
|
|
124
|
+
await this.registerServices(finalPort, finalHostname);
|
|
93
125
|
}
|
|
94
126
|
|
|
95
127
|
/**
|
|
@@ -117,14 +149,71 @@ export class Application {
|
|
|
117
149
|
}
|
|
118
150
|
|
|
119
151
|
/**
|
|
120
|
-
*
|
|
152
|
+
* 初始化配置中心集成
|
|
153
|
+
* 在所有模块注册完成后调用,确保 ConfigCenterModule 已注册
|
|
154
|
+
*/
|
|
155
|
+
private async initializeConfigCenter(): Promise<void> {
|
|
156
|
+
const container = this.getContainer();
|
|
157
|
+
// ConfigModule 可能未注册(很多测试/应用不使用配置模块),此时不要抛错
|
|
158
|
+
if (!container.isRegistered(CONFIG_SERVICE_TOKEN)) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const configService = container.resolve<ConfigService>(CONFIG_SERVICE_TOKEN);
|
|
163
|
+
|
|
164
|
+
// 检查是否有待初始化的配置中心选项
|
|
165
|
+
const configCenterOptions = (configService as any)._configCenterOptions;
|
|
166
|
+
if (configCenterOptions) {
|
|
167
|
+
try {
|
|
168
|
+
await ConfigModule.initializeConfigCenter(configService, configCenterOptions);
|
|
169
|
+
// 清除临时选项
|
|
170
|
+
delete (configService as any)._configCenterOptions;
|
|
171
|
+
} catch (error) {
|
|
172
|
+
console.error(
|
|
173
|
+
'[Application] Failed to initialize config center:',
|
|
174
|
+
error,
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* 停止应用(立即停止,不等待请求完成)
|
|
121
182
|
*/
|
|
122
183
|
public async stop(): Promise<void> {
|
|
184
|
+
// 移除信号处理器
|
|
185
|
+
this.removeSignalHandlers();
|
|
186
|
+
|
|
187
|
+
// 自动注销服务(如果使用了 @ServiceRegistry 装饰器)
|
|
188
|
+
await this.deregisterServices();
|
|
189
|
+
|
|
123
190
|
// 关闭所有扩展(包括数据库连接等)
|
|
124
191
|
await this.closeExtensions();
|
|
125
192
|
this.server?.stop();
|
|
126
193
|
}
|
|
127
194
|
|
|
195
|
+
/**
|
|
196
|
+
* 优雅停机
|
|
197
|
+
* 停止接受新请求,等待正在处理的请求完成,然后关闭应用
|
|
198
|
+
* @param timeout - 超时时间(毫秒),默认使用配置的 gracefulShutdownTimeout 或 30000
|
|
199
|
+
* @returns Promise,在停机完成时 resolve
|
|
200
|
+
*/
|
|
201
|
+
public async gracefulShutdown(timeout?: number): Promise<void> {
|
|
202
|
+
// 移除信号处理器
|
|
203
|
+
this.removeSignalHandlers();
|
|
204
|
+
|
|
205
|
+
// 自动注销服务(如果使用了 @ServiceRegistry 装饰器)
|
|
206
|
+
await this.deregisterServices();
|
|
207
|
+
|
|
208
|
+
// 关闭所有扩展(包括数据库连接等)
|
|
209
|
+
await this.closeExtensions();
|
|
210
|
+
|
|
211
|
+
// 优雅关闭服务器
|
|
212
|
+
if (this.server) {
|
|
213
|
+
await this.server.gracefulShutdown(timeout);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
128
217
|
/**
|
|
129
218
|
* 关闭所有扩展
|
|
130
219
|
*/
|
|
@@ -235,6 +324,60 @@ export class Application {
|
|
|
235
324
|
return this.server;
|
|
236
325
|
}
|
|
237
326
|
|
|
327
|
+
/**
|
|
328
|
+
* 自动注册服务到注册中心
|
|
329
|
+
* 扫描所有使用 @ServiceRegistry 装饰器的控制器,自动注册服务
|
|
330
|
+
*/
|
|
331
|
+
private async registerServices(port: number, hostname?: string): Promise<void> {
|
|
332
|
+
try {
|
|
333
|
+
// 动态导入服务注册装饰器(避免循环依赖)
|
|
334
|
+
const { registerServiceInstance } = await import(
|
|
335
|
+
'../microservice/service-registry/decorators'
|
|
336
|
+
);
|
|
337
|
+
|
|
338
|
+
const registry = ControllerRegistry.getInstance();
|
|
339
|
+
const controllers = registry.getRegisteredControllers();
|
|
340
|
+
|
|
341
|
+
for (const controllerClass of controllers) {
|
|
342
|
+
await registerServiceInstance(controllerClass, port, hostname);
|
|
343
|
+
}
|
|
344
|
+
} catch (error) {
|
|
345
|
+
// 如果服务注册失败,不影响应用启动(可能是没有配置 ServiceRegistryModule)
|
|
346
|
+
// 只在调试模式下输出警告
|
|
347
|
+
if (process.env.NODE_ENV === 'development') {
|
|
348
|
+
console.warn('[Application] Failed to register services:', error);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* 自动注销服务
|
|
355
|
+
* 注销所有使用 @ServiceRegistry 装饰器的服务
|
|
356
|
+
*/
|
|
357
|
+
private async deregisterServices(): Promise<void> {
|
|
358
|
+
try {
|
|
359
|
+
// 动态导入服务注册装饰器(避免循环依赖)
|
|
360
|
+
const { deregisterServiceInstance } = await import(
|
|
361
|
+
'../microservice/service-registry/decorators'
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
const registry = ControllerRegistry.getInstance();
|
|
365
|
+
const controllers = registry.getRegisteredControllers();
|
|
366
|
+
|
|
367
|
+
const port = this.server?.getPort() ?? this.options.port ?? 3000;
|
|
368
|
+
const hostname = this.server?.getHostname() ?? this.options.hostname;
|
|
369
|
+
|
|
370
|
+
for (const controllerClass of controllers) {
|
|
371
|
+
await deregisterServiceInstance(controllerClass, port, hostname);
|
|
372
|
+
}
|
|
373
|
+
} catch (error) {
|
|
374
|
+
// 如果服务注销失败,不影响应用关闭
|
|
375
|
+
if (process.env.NODE_ENV === 'development') {
|
|
376
|
+
console.warn('[Application] Failed to deregister services:', error);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
238
381
|
/**
|
|
239
382
|
* 获取 DI 容器(用于注册服务)
|
|
240
383
|
* @returns DI 容器
|
|
@@ -242,5 +385,48 @@ export class Application {
|
|
|
242
385
|
public getContainer() {
|
|
243
386
|
return ControllerRegistry.getInstance().getContainer();
|
|
244
387
|
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* 安装信号处理器(SIGTERM、SIGINT)
|
|
391
|
+
*/
|
|
392
|
+
private installSignalHandlers(): void {
|
|
393
|
+
if (this.signalHandlersInstalled) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const logger = LoggerManager.getLogger();
|
|
398
|
+
|
|
399
|
+
const shutdownHandler = async (signal: string) => {
|
|
400
|
+
logger.info(`Received ${signal}, starting graceful shutdown...`);
|
|
401
|
+
try {
|
|
402
|
+
await this.gracefulShutdown();
|
|
403
|
+
process.exit(0);
|
|
404
|
+
} catch (error) {
|
|
405
|
+
logger.error(`Error during graceful shutdown:`, error);
|
|
406
|
+
process.exit(1);
|
|
407
|
+
}
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
// 监听 SIGTERM(通常由进程管理器发送)
|
|
411
|
+
process.on('SIGTERM', () => shutdownHandler('SIGTERM'));
|
|
412
|
+
|
|
413
|
+
// 监听 SIGINT(通常由 Ctrl+C 触发)
|
|
414
|
+
process.on('SIGINT', () => shutdownHandler('SIGINT'));
|
|
415
|
+
|
|
416
|
+
this.signalHandlersInstalled = true;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* 移除信号处理器
|
|
421
|
+
*/
|
|
422
|
+
private removeSignalHandlers(): void {
|
|
423
|
+
if (!this.signalHandlersInstalled) {
|
|
424
|
+
return;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
process.removeAllListeners('SIGTERM');
|
|
428
|
+
process.removeAllListeners('SIGINT');
|
|
429
|
+
this.signalHandlersInstalled = false;
|
|
430
|
+
}
|
|
245
431
|
}
|
|
246
432
|
|