ErisPulse 2.2.1.dev0__py3-none-any.whl → 2.3.0.dev5__py3-none-any.whl
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.
- ErisPulse/Core/Bases/__init__.py +14 -0
- ErisPulse/Core/Bases/adapter.py +196 -0
- ErisPulse/Core/Bases/module.py +54 -0
- ErisPulse/Core/Event/__init__.py +14 -0
- ErisPulse/Core/Event/base.py +15 -2
- ErisPulse/Core/Event/command.py +21 -2
- ErisPulse/Core/Event/message.py +15 -0
- ErisPulse/Core/Event/meta.py +15 -0
- ErisPulse/Core/Event/notice.py +15 -0
- ErisPulse/Core/Event/request.py +16 -1
- ErisPulse/Core/__init__.py +38 -19
- ErisPulse/Core/{erispulse_config.py → _self_config.py} +16 -3
- ErisPulse/Core/adapter.py +374 -377
- ErisPulse/Core/config.py +137 -38
- ErisPulse/Core/exceptions.py +6 -1
- ErisPulse/Core/lifecycle.py +167 -0
- ErisPulse/Core/logger.py +97 -49
- ErisPulse/Core/module.py +279 -56
- ErisPulse/Core/router.py +112 -23
- ErisPulse/Core/storage.py +258 -77
- ErisPulse/Core/ux.py +664 -0
- ErisPulse/__init__.py +587 -242
- ErisPulse/__main__.py +1 -1999
- ErisPulse/utils/__init__.py +15 -0
- ErisPulse/utils/cli.py +1102 -0
- ErisPulse/utils/package_manager.py +847 -0
- ErisPulse/utils/reload_handler.py +135 -0
- {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/METADATA +24 -6
- erispulse-2.3.0.dev5.dist-info/RECORD +33 -0
- {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/WHEEL +1 -1
- {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/licenses/LICENSE +1 -1
- ErisPulse/Core/env.py +0 -15
- ErisPulse/Core/module_registry.py +0 -227
- erispulse-2.2.1.dev0.dist-info/RECORD +0 -26
- {erispulse-2.2.1.dev0.dist-info → erispulse-2.3.0.dev5.dist-info}/entry_points.txt +0 -0
ErisPulse/Core/module.py
CHANGED
|
@@ -1,66 +1,295 @@
|
|
|
1
1
|
"""
|
|
2
|
-
ErisPulse
|
|
2
|
+
ErisPulse 模块系统
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
提供标准化的模块注册、加载和管理功能,与适配器系统保持一致的设计模式
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
from
|
|
7
|
+
import asyncio
|
|
8
|
+
from typing import Any, Dict, List, Type, Optional
|
|
9
9
|
from .logger import logger
|
|
10
|
+
from .config import config
|
|
11
|
+
from .Bases import BaseModule
|
|
12
|
+
from .lifecycle import lifecycle
|
|
10
13
|
|
|
11
14
|
class ModuleManager:
|
|
12
15
|
"""
|
|
13
16
|
模块管理器
|
|
14
17
|
|
|
15
|
-
|
|
18
|
+
提供标准化的模块注册、加载和管理功能,模仿适配器管理器的模式
|
|
19
|
+
|
|
20
|
+
{!--< tips >!--}
|
|
21
|
+
1. 使用register方法注册模块类
|
|
22
|
+
2. 使用load/unload方法加载/卸载模块
|
|
23
|
+
3. 通过get方法获取模块实例
|
|
24
|
+
{!--< /tips >!--}
|
|
16
25
|
"""
|
|
17
26
|
|
|
18
27
|
def __init__(self):
|
|
19
|
-
|
|
28
|
+
# 模块存储
|
|
29
|
+
self._modules: Dict[str, Any] = {} # 已加载的模块实例
|
|
30
|
+
self._module_classes: Dict[str, Type] = {} # 模块类映射
|
|
31
|
+
self._loaded_modules: set = set() # 已加载的模块名称
|
|
32
|
+
self._module_info: Dict[str, Dict] = {} # 模块信息
|
|
33
|
+
|
|
34
|
+
# ==================== 模块注册与管理 ====================
|
|
20
35
|
|
|
21
|
-
def
|
|
36
|
+
def register(self, module_name: str, module_class: Type, module_info: Optional[Dict] = None) -> bool:
|
|
22
37
|
"""
|
|
23
|
-
|
|
38
|
+
注册模块类
|
|
24
39
|
|
|
25
|
-
:param module_name:
|
|
26
|
-
:
|
|
40
|
+
:param module_name: 模块名称
|
|
41
|
+
:param module_class: 模块类
|
|
42
|
+
:param module_info: 模块信息
|
|
43
|
+
:return: 是否注册成功
|
|
44
|
+
|
|
45
|
+
:raises TypeError: 当模块类无效时抛出
|
|
46
|
+
|
|
47
|
+
:example:
|
|
48
|
+
>>> module.register("MyModule", MyModuleClass)
|
|
27
49
|
"""
|
|
28
|
-
#
|
|
29
|
-
if
|
|
30
|
-
|
|
50
|
+
# 严格验证模块类,确保继承自BaseModule
|
|
51
|
+
if not issubclass(module_class, BaseModule):
|
|
52
|
+
warn_msg = f"模块 {module_name} 的类 {module_class.__name__} 没有继承自BaseModule,但我们仍会继续尝试加载这个模块,但请注意这可能引发其他问题"
|
|
53
|
+
logger.warning(warn_msg)
|
|
54
|
+
# error_msg = f"模块 {module_name} 的类 {module_class.__name__} 必须继承自BaseModule"
|
|
55
|
+
# logger.error(error_msg)
|
|
56
|
+
# raise TypeError(error_msg)
|
|
57
|
+
|
|
58
|
+
# 验证模块名是否合法
|
|
59
|
+
if not module_name or not isinstance(module_name, str):
|
|
60
|
+
error_msg = "模块名称必须是非空字符串"
|
|
61
|
+
logger.error(error_msg)
|
|
62
|
+
raise TypeError(error_msg)
|
|
31
63
|
|
|
32
|
-
#
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
logger.warning(f"模块 {module_name} 未注册")
|
|
36
|
-
return None
|
|
64
|
+
# 检查模块名是否已存在
|
|
65
|
+
if module_name in self._module_classes:
|
|
66
|
+
logger.warning(f"模块 {module_name} 已存在,将覆盖原模块类")
|
|
37
67
|
|
|
38
|
-
|
|
39
|
-
if
|
|
40
|
-
|
|
41
|
-
|
|
68
|
+
self._module_classes[module_name] = module_class
|
|
69
|
+
if module_info:
|
|
70
|
+
self._module_info[module_name] = module_info
|
|
71
|
+
|
|
72
|
+
logger.info(f"模块 {module_name} 已注册")
|
|
73
|
+
return True
|
|
74
|
+
|
|
75
|
+
async def load(self, module_name: str) -> bool:
|
|
76
|
+
"""
|
|
77
|
+
加载指定模块(标准化加载逻辑)
|
|
78
|
+
|
|
79
|
+
:param module_name: 模块名称
|
|
80
|
+
:return: 是否加载成功
|
|
81
|
+
|
|
82
|
+
:example:
|
|
83
|
+
>>> await module.load("MyModule")
|
|
84
|
+
"""
|
|
85
|
+
# 检查模块是否已注册
|
|
86
|
+
if module_name not in self._module_classes:
|
|
87
|
+
logger.error(f"模块 {module_name} 未注册")
|
|
88
|
+
return False
|
|
89
|
+
|
|
90
|
+
# 检查模块是否已加载
|
|
91
|
+
if module_name in self._loaded_modules:
|
|
92
|
+
logger.info(f"模块 {module_name} 已加载")
|
|
93
|
+
return True
|
|
42
94
|
|
|
43
95
|
try:
|
|
44
96
|
from .. import sdk
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
97
|
+
import inspect
|
|
98
|
+
|
|
99
|
+
# 创建模块实例
|
|
100
|
+
module_class = self._module_classes[module_name]
|
|
101
|
+
|
|
102
|
+
# 检查是否需要传入sdk参数
|
|
103
|
+
init_signature = inspect.signature(module_class.__init__)
|
|
104
|
+
params = init_signature.parameters
|
|
105
|
+
|
|
106
|
+
if 'sdk' in params:
|
|
107
|
+
instance = module_class(sdk)
|
|
49
108
|
else:
|
|
50
|
-
|
|
51
|
-
|
|
109
|
+
instance = module_class()
|
|
110
|
+
|
|
111
|
+
# 设置模块信息
|
|
112
|
+
if module_name in self._module_info:
|
|
113
|
+
setattr(instance, "moduleInfo", self._module_info[module_name])
|
|
114
|
+
|
|
115
|
+
# 调用模块的on_load卸载方法
|
|
116
|
+
if hasattr(instance, 'on_load'):
|
|
117
|
+
try:
|
|
118
|
+
if asyncio.iscoroutinefunction(instance.on_load):
|
|
119
|
+
await instance.on_load({"module_name": module_name})
|
|
120
|
+
else:
|
|
121
|
+
instance.on_load({"module_name": module_name})
|
|
122
|
+
except Exception as e:
|
|
123
|
+
logger.error(f"模块 {module_name} on_load 方法执行失败: {e}")
|
|
124
|
+
return False
|
|
125
|
+
|
|
126
|
+
# 缓存模块实例
|
|
127
|
+
self._modules[module_name] = instance
|
|
128
|
+
self._loaded_modules.add(module_name)
|
|
129
|
+
|
|
130
|
+
await lifecycle.submit_event(
|
|
131
|
+
"module_load",
|
|
132
|
+
data={
|
|
133
|
+
"module_name": module_name,
|
|
134
|
+
"success": True,
|
|
135
|
+
},
|
|
136
|
+
msg=f"模块 {module_name if module_name else 'All'} 加载成功",
|
|
137
|
+
)
|
|
138
|
+
logger.info(f"模块 {module_name} 加载成功")
|
|
139
|
+
return True
|
|
140
|
+
|
|
52
141
|
except Exception as e:
|
|
53
|
-
|
|
54
|
-
|
|
142
|
+
await lifecycle.submit_event(
|
|
143
|
+
"module_load",
|
|
144
|
+
data={
|
|
145
|
+
"module_name": module_name,
|
|
146
|
+
"success": False,
|
|
147
|
+
},
|
|
148
|
+
msg=f"模块 {module_name if module_name else 'All'} 加载失败: {e}",
|
|
149
|
+
)
|
|
150
|
+
logger.error(f"加载模块 {module_name} 失败: {e}")
|
|
151
|
+
return False
|
|
152
|
+
|
|
153
|
+
async def unload(self, module_name: str = "Unknown") -> bool:
|
|
154
|
+
"""
|
|
155
|
+
卸载指定模块或所有模块
|
|
156
|
+
|
|
157
|
+
:param module_name: 模块名称,如果为None则卸载所有模块
|
|
158
|
+
:return: 是否卸载成功
|
|
159
|
+
|
|
160
|
+
:example:
|
|
161
|
+
>>> await module.unload("MyModule")
|
|
162
|
+
>>> await module.unload() # 卸载所有模块
|
|
163
|
+
"""
|
|
164
|
+
if module_name == "Unknown":
|
|
165
|
+
# 卸载所有模块
|
|
166
|
+
success = True
|
|
167
|
+
for name in list(self._loaded_modules):
|
|
168
|
+
if not await self._unload_single_module(name):
|
|
169
|
+
success = False
|
|
170
|
+
return success
|
|
171
|
+
else:
|
|
172
|
+
success = await self._unload_single_module(module_name)
|
|
173
|
+
|
|
174
|
+
await lifecycle.submit_event(
|
|
175
|
+
"module.unload",
|
|
176
|
+
msg=f"模块 {module_name if module_name else 'All'} 卸载完成" if success else f"模块 {module_name if module_name else 'All'} 卸载失败",
|
|
177
|
+
data={
|
|
178
|
+
"module_name": module_name if module_name else 'All',
|
|
179
|
+
"success": success
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
return success
|
|
55
183
|
|
|
184
|
+
async def _unload_single_module(self, module_name: str) -> bool:
|
|
185
|
+
"""
|
|
186
|
+
{!--< internal-use >!--}
|
|
187
|
+
卸载单个模块
|
|
188
|
+
|
|
189
|
+
:param module_name: 模块名称
|
|
190
|
+
:return: 是否卸载成功
|
|
191
|
+
"""
|
|
192
|
+
if module_name not in self._loaded_modules:
|
|
193
|
+
logger.warning(f"模块 {module_name} 未加载")
|
|
194
|
+
return False
|
|
195
|
+
|
|
196
|
+
try:
|
|
197
|
+
# 调用模块的on_unload卸载方法
|
|
198
|
+
instance = self._modules[module_name]
|
|
199
|
+
if hasattr(instance, 'on_unload'):
|
|
200
|
+
try:
|
|
201
|
+
if asyncio.iscoroutinefunction(instance.on_unload):
|
|
202
|
+
await instance.on_unload({"module_name": module_name})
|
|
203
|
+
else:
|
|
204
|
+
instance.on_unload({"module_name": module_name})
|
|
205
|
+
except Exception as e:
|
|
206
|
+
logger.error(f"模块 {module_name} on_unload 方法执行失败: {e}")
|
|
207
|
+
|
|
208
|
+
# 清理缓存
|
|
209
|
+
del self._modules[module_name]
|
|
210
|
+
self._loaded_modules.discard(module_name)
|
|
211
|
+
|
|
212
|
+
logger.info(f"模块 {module_name} 卸载成功")
|
|
213
|
+
return True
|
|
214
|
+
|
|
215
|
+
except Exception as e:
|
|
216
|
+
logger.error(f"卸载模块 {module_name} 失败: {e}")
|
|
217
|
+
return False
|
|
218
|
+
|
|
219
|
+
def get(self, module_name: str) -> Any:
|
|
220
|
+
"""
|
|
221
|
+
获取模块实例
|
|
222
|
+
|
|
223
|
+
:param module_name: 模块名称
|
|
224
|
+
:return: 模块实例或None
|
|
225
|
+
|
|
226
|
+
:example:
|
|
227
|
+
>>> my_module = module.get("MyModule")
|
|
228
|
+
"""
|
|
229
|
+
return self._modules.get(module_name)
|
|
230
|
+
|
|
56
231
|
def exists(self, module_name: str) -> bool:
|
|
57
232
|
"""
|
|
58
|
-
|
|
233
|
+
检查模块是否存在(在配置中注册)
|
|
59
234
|
|
|
60
235
|
:param module_name: [str] 模块名称
|
|
61
236
|
:return: [bool] 模块是否存在
|
|
62
237
|
"""
|
|
63
|
-
|
|
238
|
+
module_statuses = config.getConfig("ErisPulse.modules.status", {})
|
|
239
|
+
return module_name in module_statuses
|
|
240
|
+
|
|
241
|
+
def is_loaded(self, module_name: str) -> bool:
|
|
242
|
+
"""
|
|
243
|
+
检查模块是否已加载
|
|
244
|
+
|
|
245
|
+
:param module_name: 模块名称
|
|
246
|
+
:return: 模块是否已加载
|
|
247
|
+
|
|
248
|
+
:example:
|
|
249
|
+
>>> if module.is_loaded("MyModule"): ...
|
|
250
|
+
"""
|
|
251
|
+
return module_name in self._loaded_modules
|
|
252
|
+
|
|
253
|
+
def list_registered(self) -> List[str]:
|
|
254
|
+
"""
|
|
255
|
+
列出所有已注册的模块
|
|
256
|
+
|
|
257
|
+
:return: 模块名称列表
|
|
258
|
+
|
|
259
|
+
:example:
|
|
260
|
+
>>> registered = module.list_registered()
|
|
261
|
+
"""
|
|
262
|
+
return list(self._module_classes.keys())
|
|
263
|
+
|
|
264
|
+
def list_loaded(self) -> List[str]:
|
|
265
|
+
"""
|
|
266
|
+
列出所有已加载的模块
|
|
267
|
+
|
|
268
|
+
:return: 模块名称列表
|
|
269
|
+
|
|
270
|
+
:example:
|
|
271
|
+
>>> loaded = module.list_loaded()
|
|
272
|
+
"""
|
|
273
|
+
return list(self._loaded_modules)
|
|
274
|
+
|
|
275
|
+
# ==================== 模块配置管理 ====================
|
|
276
|
+
|
|
277
|
+
def _config_register(self, module_name: str, enabled: bool = False) -> bool:
|
|
278
|
+
"""
|
|
279
|
+
注册新模块信息
|
|
280
|
+
|
|
281
|
+
:param module_name: [str] 模块名称
|
|
282
|
+
:param enabled: [bool] 是否启用模块
|
|
283
|
+
:return: [bool] 操作是否成功
|
|
284
|
+
"""
|
|
285
|
+
if self.exists(module_name):
|
|
286
|
+
return True
|
|
287
|
+
|
|
288
|
+
# 模块不存在,进行注册
|
|
289
|
+
config.setConfig(f"ErisPulse.modules.status.{module_name}", enabled)
|
|
290
|
+
status = "启用" if enabled else "禁用"
|
|
291
|
+
logger.info(f"模块 {module_name} 已注册并{status}")
|
|
292
|
+
return True
|
|
64
293
|
|
|
65
294
|
def is_enabled(self, module_name: str) -> bool:
|
|
66
295
|
"""
|
|
@@ -69,7 +298,15 @@ class ModuleManager:
|
|
|
69
298
|
:param module_name: [str] 模块名称
|
|
70
299
|
:return: [bool] 模块是否启用
|
|
71
300
|
"""
|
|
72
|
-
|
|
301
|
+
status = config.getConfig(f"ErisPulse.modules.status.{module_name}")
|
|
302
|
+
|
|
303
|
+
if status is None:
|
|
304
|
+
return False
|
|
305
|
+
|
|
306
|
+
if isinstance(status, str):
|
|
307
|
+
return status.lower() not in ('false', '0', 'no', 'off')
|
|
308
|
+
|
|
309
|
+
return bool(status)
|
|
73
310
|
|
|
74
311
|
def enable(self, module_name: str) -> bool:
|
|
75
312
|
"""
|
|
@@ -78,11 +315,7 @@ class ModuleManager:
|
|
|
78
315
|
:param module_name: [str] 模块名称
|
|
79
316
|
:return: [bool] 操作是否成功
|
|
80
317
|
"""
|
|
81
|
-
|
|
82
|
-
logger.error(f"模块 {module_name} 不存在")
|
|
83
|
-
return False
|
|
84
|
-
|
|
85
|
-
module_registry.set_module_status(module_name, True)
|
|
318
|
+
config.setConfig(f"ErisPulse.modules.status.{module_name}", True)
|
|
86
319
|
logger.info(f"模块 {module_name} 已启用")
|
|
87
320
|
return True
|
|
88
321
|
|
|
@@ -93,33 +326,23 @@ class ModuleManager:
|
|
|
93
326
|
:param module_name: [str] 模块名称
|
|
94
327
|
:return: [bool] 操作是否成功
|
|
95
328
|
"""
|
|
96
|
-
|
|
97
|
-
logger.error(f"模块 {module_name} 不存在")
|
|
98
|
-
return False
|
|
99
|
-
|
|
100
|
-
module_registry.set_module_status(module_name, False)
|
|
329
|
+
config.setConfig(f"ErisPulse.modules.status.{module_name}", False)
|
|
101
330
|
logger.info(f"模块 {module_name} 已禁用")
|
|
102
|
-
|
|
331
|
+
|
|
103
332
|
if module_name in self._modules:
|
|
104
333
|
del self._modules[module_name]
|
|
334
|
+
self._loaded_modules.discard(module_name)
|
|
105
335
|
return True
|
|
106
336
|
|
|
107
|
-
def list_modules(self) -> Dict[str,
|
|
337
|
+
def list_modules(self) -> Dict[str, bool]:
|
|
108
338
|
"""
|
|
109
|
-
|
|
339
|
+
列出所有模块状态
|
|
110
340
|
|
|
111
|
-
:return: [Dict[str,
|
|
341
|
+
:return: [Dict[str, bool]] 模块状态字典
|
|
112
342
|
"""
|
|
113
|
-
return
|
|
343
|
+
return config.getConfig("ErisPulse.modules.status", {})
|
|
114
344
|
|
|
115
|
-
|
|
116
|
-
"""
|
|
117
|
-
获取模块详细信息
|
|
118
|
-
|
|
119
|
-
:param module_name: [str] 模块名称
|
|
120
|
-
:return: [Optional[Dict[str, Any]]] 模块信息字典
|
|
121
|
-
"""
|
|
122
|
-
return module_registry.get_module(module_name)
|
|
345
|
+
# ==================== 工具方法 ====================
|
|
123
346
|
|
|
124
347
|
def __getattr__(self, module_name: str) -> Any:
|
|
125
348
|
"""
|
ErisPulse/Core/router.py
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
# router.py (新文件名)
|
|
2
1
|
"""
|
|
3
2
|
ErisPulse 路由系统
|
|
4
3
|
|
|
@@ -7,7 +6,6 @@ ErisPulse 路由系统
|
|
|
7
6
|
{!--< tips >!--}
|
|
8
7
|
1. 适配器只需注册路由,无需自行管理服务器
|
|
9
8
|
2. WebSocket支持自定义认证逻辑
|
|
10
|
-
3. 兼容FastAPI 0.68+ 版本
|
|
11
9
|
{!--< /tips >!--}
|
|
12
10
|
"""
|
|
13
11
|
|
|
@@ -16,6 +14,7 @@ from fastapi.routing import APIRoute
|
|
|
16
14
|
from typing import Dict, List, Optional, Callable, Any, Awaitable, Tuple
|
|
17
15
|
from collections import defaultdict
|
|
18
16
|
from .logger import logger
|
|
17
|
+
from .lifecycle import lifecycle
|
|
19
18
|
import asyncio
|
|
20
19
|
from hypercorn.config import Config
|
|
21
20
|
from hypercorn.asyncio import serve
|
|
@@ -140,7 +139,8 @@ class RouterManager:
|
|
|
140
139
|
)
|
|
141
140
|
self.app.router.routes.append(route)
|
|
142
141
|
self._http_routes[module_name][full_path] = handler
|
|
143
|
-
|
|
142
|
+
display_url = self._format_display_url(f"{self.base_url}{full_path}")
|
|
143
|
+
logger.info(f"注册HTTP路由: {display_url} 方法: {methods}")
|
|
144
144
|
|
|
145
145
|
def register_webhook(self, *args, **kwargs) -> None:
|
|
146
146
|
"""
|
|
@@ -148,6 +148,38 @@ class RouterManager:
|
|
|
148
148
|
"""
|
|
149
149
|
return self.register_http_route(*args, **kwargs)
|
|
150
150
|
|
|
151
|
+
def unregister_http_route(self, module_name: str, path: str) -> bool:
|
|
152
|
+
"""
|
|
153
|
+
取消注册HTTP路由
|
|
154
|
+
|
|
155
|
+
:param module_name: 模块名称
|
|
156
|
+
:param path: 路由路径
|
|
157
|
+
|
|
158
|
+
:return: Bool
|
|
159
|
+
"""
|
|
160
|
+
try:
|
|
161
|
+
full_path = f"/{module_name}{path}"
|
|
162
|
+
if full_path not in self._http_routes[module_name]:
|
|
163
|
+
display_url = self._format_display_url(f"{self.base_url}{full_path}")
|
|
164
|
+
logger.warning(f"取消注册HTTP路由失败: 路由不存在: {display_url}")
|
|
165
|
+
return False
|
|
166
|
+
|
|
167
|
+
display_url = self._format_display_url(f"{self.base_url}{full_path}")
|
|
168
|
+
logger.info(f"取消注册HTTP路由: {display_url}")
|
|
169
|
+
del self._http_routes[module_name][full_path]
|
|
170
|
+
|
|
171
|
+
# 从路由列表中移除匹配的路由
|
|
172
|
+
routes = self.app.router.routes
|
|
173
|
+
self.app.router.routes = [
|
|
174
|
+
route for route in routes
|
|
175
|
+
if not (isinstance(route, APIRoute) and route.path == full_path)
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
return True
|
|
179
|
+
except Exception as e:
|
|
180
|
+
logger.error(f"取消注册HTTP路由失败: {e}")
|
|
181
|
+
return False
|
|
182
|
+
|
|
151
183
|
def register_websocket(
|
|
152
184
|
self,
|
|
153
185
|
module_name: str,
|
|
@@ -195,8 +227,28 @@ class RouterManager:
|
|
|
195
227
|
name=f"{module_name}_{path.replace('/', '_')}"
|
|
196
228
|
)
|
|
197
229
|
self._websocket_routes[module_name][full_path] = (handler, auth_handler)
|
|
198
|
-
logger.info(f"注册WebSocket: {self.base_url}{full_path} {'(需认证)' if auth_handler else ''}")
|
|
199
230
|
|
|
231
|
+
display_url = self._format_display_url(f"{self.base_url}{full_path}")
|
|
232
|
+
logger.info(f"注册WebSocket: {display_url} {'(需认证)' if auth_handler else ''}")
|
|
233
|
+
|
|
234
|
+
def unregister_websocket(self, module_name: str, path: str) -> bool:
|
|
235
|
+
try:
|
|
236
|
+
full_path = f"/{module_name}{path}"
|
|
237
|
+
|
|
238
|
+
# 使用类型忽略注释
|
|
239
|
+
if full_path in self.app.websocket_routes: # type: ignore || 原因:实际上,FastAPI的API提供了websocket_routes属性
|
|
240
|
+
self.app.remove_api_websocket_route(full_path) # type: ignore || 原因:实际上,FastAPI的API提供了remove_api_websocket_route方法
|
|
241
|
+
display_url = self._format_display_url(f"{self.base_url}{full_path}")
|
|
242
|
+
logger.info(f"注销WebSocket: {display_url}")
|
|
243
|
+
del self._websocket_routes[module_name][full_path]
|
|
244
|
+
return True
|
|
245
|
+
display_url = self._format_display_url(f"{self.base_url}{full_path}")
|
|
246
|
+
logger.error(f"注销WebSocket失败: 路径 {display_url} 不存在")
|
|
247
|
+
return False
|
|
248
|
+
except Exception as e:
|
|
249
|
+
logger.error(f"注销WebSocket失败: {e}")
|
|
250
|
+
return False
|
|
251
|
+
|
|
200
252
|
def get_app(self) -> FastAPI:
|
|
201
253
|
"""
|
|
202
254
|
获取FastAPI应用实例
|
|
@@ -222,21 +274,46 @@ class RouterManager:
|
|
|
222
274
|
|
|
223
275
|
:raises RuntimeError: 当服务器已在运行时抛出
|
|
224
276
|
"""
|
|
225
|
-
|
|
226
|
-
|
|
277
|
+
try:
|
|
278
|
+
if self._server_task and not self._server_task.done():
|
|
279
|
+
raise RuntimeError("服务器已在运行中")
|
|
227
280
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
281
|
+
config = Config()
|
|
282
|
+
config.bind = [f"{host}:{port}"]
|
|
283
|
+
config.loglevel = "warning"
|
|
284
|
+
|
|
285
|
+
if ssl_certfile and ssl_keyfile:
|
|
286
|
+
config.certfile = ssl_certfile
|
|
287
|
+
config.keyfile = ssl_keyfile
|
|
288
|
+
|
|
289
|
+
self.base_url = f"http{'s' if ssl_certfile else ''}://{host}:{port}"
|
|
290
|
+
display_url = self._format_display_url(self.base_url)
|
|
291
|
+
logger.info(f"启动路由服务器 {display_url}")
|
|
292
|
+
|
|
293
|
+
self._server_task = asyncio.create_task(serve(self.app, config)) # type: ignore || 原因: Hypercorn与FastAPIl类型不兼容
|
|
294
|
+
|
|
295
|
+
await lifecycle.submit_event(
|
|
296
|
+
"server.start",
|
|
297
|
+
msg="路由服务器已启动",
|
|
298
|
+
data={
|
|
299
|
+
"base_url": self.base_url,
|
|
300
|
+
"host": host,
|
|
301
|
+
"port": port,
|
|
302
|
+
},
|
|
303
|
+
)
|
|
304
|
+
except Exception as e:
|
|
305
|
+
display_url = self._format_display_url(self.base_url)
|
|
306
|
+
await lifecycle.submit_event(
|
|
307
|
+
"server.start",
|
|
308
|
+
msg="路由服务器启动失败",
|
|
309
|
+
data={
|
|
310
|
+
"base_url": self.base_url,
|
|
311
|
+
"host": host,
|
|
312
|
+
"port": port,
|
|
313
|
+
}
|
|
314
|
+
)
|
|
315
|
+
logger.error(f"启动服务器失败: {e}")
|
|
316
|
+
raise e
|
|
240
317
|
|
|
241
318
|
async def stop(self) -> None:
|
|
242
319
|
"""
|
|
@@ -249,14 +326,26 @@ class RouterManager:
|
|
|
249
326
|
except asyncio.CancelledError:
|
|
250
327
|
logger.info("路由服务器已停止")
|
|
251
328
|
self._server_task = None
|
|
329
|
+
|
|
330
|
+
await lifecycle.submit_event("server.stop", msg="服务器已停止")
|
|
252
331
|
|
|
253
|
-
|
|
254
|
-
|
|
332
|
+
def _format_display_url(self, url: str) -> str:
|
|
333
|
+
"""
|
|
334
|
+
格式化URL显示,将回环地址转换为更友好的格式
|
|
335
|
+
|
|
336
|
+
:param url: 原始URL
|
|
337
|
+
:return: 格式化后的URL
|
|
338
|
+
"""
|
|
339
|
+
if "0.0.0.0" in url:
|
|
340
|
+
display_url = url.replace("0.0.0.0", "127.0.0.1")
|
|
341
|
+
return f"{url} (可访问: {display_url})"
|
|
342
|
+
elif "::" in url:
|
|
343
|
+
display_url = url.replace("::", "localhost")
|
|
344
|
+
return f"{url} (可访问: {display_url})"
|
|
345
|
+
return url
|
|
255
346
|
|
|
256
|
-
|
|
257
|
-
adapter_server = router
|
|
347
|
+
router = RouterManager()
|
|
258
348
|
|
|
259
349
|
__all__ = [
|
|
260
350
|
"router",
|
|
261
|
-
"adapter_server"
|
|
262
351
|
]
|