jettask 0.2.15__py3-none-any.whl → 0.2.16__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.
Files changed (148) hide show
  1. jettask/__init__.py +14 -35
  2. jettask/{webui/__main__.py → __main__.py} +4 -4
  3. jettask/api/__init__.py +103 -0
  4. jettask/api/v1/__init__.py +29 -0
  5. jettask/api/v1/alerts.py +226 -0
  6. jettask/api/v1/analytics.py +323 -0
  7. jettask/api/v1/namespaces.py +134 -0
  8. jettask/api/v1/overview.py +136 -0
  9. jettask/api/v1/queues.py +530 -0
  10. jettask/api/v1/scheduled.py +420 -0
  11. jettask/api/v1/settings.py +44 -0
  12. jettask/{webui/api.py → api.py} +4 -46
  13. jettask/{webui/backend → backend}/main.py +21 -109
  14. jettask/{webui/backend → backend}/main_unified.py +1 -1
  15. jettask/{webui/backend → backend}/namespace_api_old.py +3 -30
  16. jettask/{webui/backend → backend}/namespace_data_access.py +2 -1
  17. jettask/{webui/backend → backend}/unified_api_router.py +14 -74
  18. jettask/{core/cli.py → cli.py} +106 -26
  19. jettask/config/nacos_config.py +386 -0
  20. jettask/core/app.py +8 -100
  21. jettask/core/db_manager.py +515 -0
  22. jettask/core/event_pool.py +5 -2
  23. jettask/core/unified_manager_base.py +47 -14
  24. jettask/{webui/db_init.py → db_init.py} +1 -1
  25. jettask/executors/asyncio.py +2 -2
  26. jettask/{webui/integrated_gradio_app.py → integrated_gradio_app.py} +1 -1
  27. jettask/{webui/multi_namespace_consumer.py → multi_namespace_consumer.py} +5 -2
  28. jettask/{webui/pg_consumer.py → pg_consumer.py} +137 -69
  29. jettask/{webui/run.py → run.py} +1 -1
  30. jettask/{webui/run_webui.py → run_webui.py} +4 -4
  31. jettask/scheduler/multi_namespace_scheduler.py +2 -2
  32. jettask/scheduler/unified_manager.py +5 -5
  33. jettask/scheduler/unified_scheduler_manager.py +1 -1
  34. jettask/schemas/__init__.py +166 -0
  35. jettask/schemas/alert.py +99 -0
  36. jettask/schemas/backlog.py +122 -0
  37. jettask/schemas/common.py +139 -0
  38. jettask/schemas/monitoring.py +181 -0
  39. jettask/schemas/namespace.py +168 -0
  40. jettask/schemas/queue.py +83 -0
  41. jettask/schemas/scheduled_task.py +128 -0
  42. jettask/schemas/task.py +70 -0
  43. jettask/services/__init__.py +24 -0
  44. jettask/services/alert_service.py +454 -0
  45. jettask/services/analytics_service.py +46 -0
  46. jettask/services/overview_service.py +978 -0
  47. jettask/services/queue_service.py +711 -0
  48. jettask/services/redis_monitor_service.py +151 -0
  49. jettask/services/scheduled_task_service.py +207 -0
  50. jettask/services/settings_service.py +758 -0
  51. jettask/services/task_service.py +157 -0
  52. jettask/{webui/task_center.py → task_center.py} +30 -8
  53. jettask/{webui/task_center_client.py → task_center_client.py} +1 -1
  54. jettask/{webui/config.py → webui_config.py} +6 -1
  55. jettask/webui_exceptions.py +67 -0
  56. jettask/webui_sql/verify_database.sql +72 -0
  57. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/METADATA +2 -1
  58. jettask-0.2.16.dist-info/RECORD +150 -0
  59. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/entry_points.txt +1 -1
  60. jettask/webui/backend/data_api.py +0 -3294
  61. jettask/webui/backend/namespace_api.py +0 -295
  62. jettask/webui/backend/queue_backlog_api.py +0 -727
  63. jettask/webui/backend/redis_monitor_api.py +0 -476
  64. jettask/webui/frontend/index.html +0 -13
  65. jettask/webui/frontend/package.json +0 -30
  66. jettask/webui/frontend/src/App.css +0 -109
  67. jettask/webui/frontend/src/App.jsx +0 -66
  68. jettask/webui/frontend/src/components/NamespaceSelector.jsx +0 -166
  69. jettask/webui/frontend/src/components/QueueBacklogChart.jsx +0 -298
  70. jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +0 -638
  71. jettask/webui/frontend/src/components/QueueDetailsTable.css +0 -65
  72. jettask/webui/frontend/src/components/QueueDetailsTable.jsx +0 -487
  73. jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +0 -465
  74. jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +0 -423
  75. jettask/webui/frontend/src/components/TaskFilter.jsx +0 -425
  76. jettask/webui/frontend/src/components/TimeRangeSelector.css +0 -21
  77. jettask/webui/frontend/src/components/TimeRangeSelector.jsx +0 -160
  78. jettask/webui/frontend/src/components/charts/QueueChart.jsx +0 -111
  79. jettask/webui/frontend/src/components/charts/QueueTrendChart.jsx +0 -115
  80. jettask/webui/frontend/src/components/charts/WorkerChart.jsx +0 -40
  81. jettask/webui/frontend/src/components/common/StatsCard.jsx +0 -18
  82. jettask/webui/frontend/src/components/layout/AppLayout.css +0 -95
  83. jettask/webui/frontend/src/components/layout/AppLayout.jsx +0 -49
  84. jettask/webui/frontend/src/components/layout/Header.css +0 -106
  85. jettask/webui/frontend/src/components/layout/Header.jsx +0 -106
  86. jettask/webui/frontend/src/components/layout/SideMenu.css +0 -137
  87. jettask/webui/frontend/src/components/layout/SideMenu.jsx +0 -209
  88. jettask/webui/frontend/src/components/layout/TabsNav.css +0 -244
  89. jettask/webui/frontend/src/components/layout/TabsNav.jsx +0 -206
  90. jettask/webui/frontend/src/components/layout/UserInfo.css +0 -197
  91. jettask/webui/frontend/src/components/layout/UserInfo.jsx +0 -197
  92. jettask/webui/frontend/src/contexts/LoadingContext.jsx +0 -27
  93. jettask/webui/frontend/src/contexts/NamespaceContext.jsx +0 -72
  94. jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +0 -245
  95. jettask/webui/frontend/src/index.css +0 -114
  96. jettask/webui/frontend/src/main.jsx +0 -22
  97. jettask/webui/frontend/src/pages/Alerts.jsx +0 -684
  98. jettask/webui/frontend/src/pages/Dashboard/index.css +0 -35
  99. jettask/webui/frontend/src/pages/Dashboard/index.jsx +0 -281
  100. jettask/webui/frontend/src/pages/Dashboard.jsx +0 -1330
  101. jettask/webui/frontend/src/pages/QueueDetail.jsx +0 -1117
  102. jettask/webui/frontend/src/pages/QueueMonitor.jsx +0 -527
  103. jettask/webui/frontend/src/pages/Queues.jsx +0 -12
  104. jettask/webui/frontend/src/pages/ScheduledTasks.jsx +0 -810
  105. jettask/webui/frontend/src/pages/Settings.jsx +0 -801
  106. jettask/webui/frontend/src/pages/Workers.jsx +0 -12
  107. jettask/webui/frontend/src/services/api.js +0 -159
  108. jettask/webui/frontend/src/services/queueTrend.js +0 -166
  109. jettask/webui/frontend/src/utils/suppressWarnings.js +0 -22
  110. jettask/webui/frontend/src/utils/userPreferences.js +0 -154
  111. jettask/webui/frontend/vite.config.js +0 -26
  112. jettask/webui/sql/init_database.sql +0 -640
  113. jettask-0.2.15.dist-info/RECORD +0 -172
  114. /jettask/{webui/backend → backend}/__init__.py +0 -0
  115. /jettask/{webui/backend → backend}/api/__init__.py +0 -0
  116. /jettask/{webui/backend → backend}/api/v1/__init__.py +0 -0
  117. /jettask/{webui/backend → backend}/api/v1/monitoring.py +0 -0
  118. /jettask/{webui/backend → backend}/api/v1/namespaces.py +0 -0
  119. /jettask/{webui/backend → backend}/api/v1/queues.py +0 -0
  120. /jettask/{webui/backend → backend}/api/v1/tasks.py +0 -0
  121. /jettask/{webui/backend → backend}/config.py +0 -0
  122. /jettask/{webui/backend → backend}/core/__init__.py +0 -0
  123. /jettask/{webui/backend → backend}/core/cache.py +0 -0
  124. /jettask/{webui/backend → backend}/core/database.py +0 -0
  125. /jettask/{webui/backend → backend}/core/exceptions.py +0 -0
  126. /jettask/{webui/backend → backend}/data_access.py +0 -0
  127. /jettask/{webui/backend → backend}/dependencies.py +0 -0
  128. /jettask/{webui/backend → backend}/init_meta_db.py +0 -0
  129. /jettask/{webui/backend → backend}/main_v2.py +0 -0
  130. /jettask/{webui/backend → backend}/models/__init__.py +0 -0
  131. /jettask/{webui/backend → backend}/models/requests.py +0 -0
  132. /jettask/{webui/backend → backend}/models/responses.py +0 -0
  133. /jettask/{webui/backend → backend}/queue_stats_v2.py +0 -0
  134. /jettask/{webui/backend → backend}/services/__init__.py +0 -0
  135. /jettask/{webui/backend → backend}/start.py +0 -0
  136. /jettask/{webui/cleanup_deprecated_tables.sql → cleanup_deprecated_tables.sql} +0 -0
  137. /jettask/{webui/gradio_app.py → gradio_app.py} +0 -0
  138. /jettask/{webui/__init__.py → main.py} +0 -0
  139. /jettask/{webui/models.py → models.py} +0 -0
  140. /jettask/{webui/run_monitor.py → run_monitor.py} +0 -0
  141. /jettask/{webui/schema.sql → schema.sql} +0 -0
  142. /jettask/{webui/unified_consumer_manager.py → unified_consumer_manager.py} +0 -0
  143. /jettask/{webui/models → webui_models}/__init__.py +0 -0
  144. /jettask/{webui/models → webui_models}/namespace.py +0 -0
  145. /jettask/{webui/sql → webui_sql}/batch_upsert_functions.sql +0 -0
  146. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/WHEEL +0 -0
  147. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/licenses/LICENSE +0 -0
  148. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,420 @@
1
+ """
2
+ 定时任务模块 - 定时任务管理、执行和监控
3
+ 提供轻量级的路由入口,业务逻辑在 ScheduledTaskService 中实现
4
+ """
5
+ from fastapi import APIRouter, HTTPException, Request, Query
6
+ from typing import Optional
7
+ import logging
8
+
9
+ from jettask.schemas import ScheduledTaskRequest
10
+ from jettask.services.scheduled_task_service import ScheduledTaskService
11
+
12
+ router = APIRouter(prefix="/scheduled", tags=["scheduled"])
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ # ============ 定时任务管理 ============
17
+
18
+ @router.get("/")
19
+ async def get_scheduled_tasks(
20
+ request: Request,
21
+ page: int = Query(1, ge=1),
22
+ page_size: int = Query(20, ge=1, le=100),
23
+ search: Optional[str] = Query(None, description="搜索关键字"),
24
+ is_active: Optional[bool] = Query(None, description="是否激活")
25
+ ):
26
+ """
27
+ 获取定时任务列表
28
+
29
+ Args:
30
+ page: 页码(从1开始)
31
+ page_size: 每页数量
32
+ search: 搜索关键字
33
+ is_active: 是否激活
34
+ """
35
+ try:
36
+ app = request.app
37
+ if not app or not hasattr(app.state, 'data_access'):
38
+ raise HTTPException(status_code=500, detail="Data access not initialized")
39
+
40
+ data_access = app.state.data_access
41
+
42
+ async with data_access.get_session() as session:
43
+ tasks, total = await data_access.fetch_scheduled_tasks(
44
+ session=session,
45
+ page=page,
46
+ page_size=page_size,
47
+ search=search,
48
+ is_active=is_active
49
+ )
50
+
51
+ return {
52
+ "success": True,
53
+ "data": tasks,
54
+ "total": total,
55
+ "page": page,
56
+ "page_size": page_size
57
+ }
58
+ except Exception as e:
59
+ logger.error(f"获取定时任务列表失败: {e}")
60
+ raise HTTPException(status_code=500, detail=str(e))
61
+
62
+
63
+ @router.post("/filter")
64
+ async def get_scheduled_tasks_with_filters(request: Request):
65
+ """
66
+ 获取定时任务列表(支持高级筛选)
67
+ """
68
+ try:
69
+ app = request.app
70
+ if not app or not hasattr(app.state, 'data_access'):
71
+ raise HTTPException(status_code=500, detail="Data access not initialized")
72
+
73
+ data_access = app.state.data_access
74
+
75
+ # 解析请求体
76
+ body = await request.json()
77
+
78
+ page = body.get('page', 1)
79
+ page_size = body.get('page_size', 20)
80
+ search = body.get('search')
81
+ is_active = body.get('is_active')
82
+ filters = body.get('filters', [])
83
+ time_range = body.get('time_range')
84
+ start_time = body.get('start_time')
85
+ end_time = body.get('end_time')
86
+
87
+ async with data_access.get_session() as session:
88
+ tasks, total = await data_access.fetch_scheduled_tasks(
89
+ session=session,
90
+ page=page,
91
+ page_size=page_size,
92
+ search=search,
93
+ is_active=is_active,
94
+ filters=filters,
95
+ time_range=time_range,
96
+ start_time=start_time,
97
+ end_time=end_time
98
+ )
99
+
100
+ return {
101
+ "success": True,
102
+ "data": tasks,
103
+ "total": total,
104
+ "page": page,
105
+ "page_size": page_size
106
+ }
107
+ except Exception as e:
108
+ logger.error(f"获取定时任务列表失败: {e}")
109
+ raise HTTPException(status_code=500, detail=str(e))
110
+
111
+
112
+ @router.post("/", status_code=201)
113
+ async def create_scheduled_task(request: Request, task_request: ScheduledTaskRequest):
114
+ """
115
+ 创建定时任务
116
+
117
+ Args:
118
+ task_request: 定时任务创建请求
119
+ """
120
+ try:
121
+ app = request.app
122
+ if not app or not hasattr(app.state, 'data_access'):
123
+ raise HTTPException(status_code=500, detail="Data access not initialized")
124
+
125
+ data_access = app.state.data_access
126
+
127
+ # 验证调度配置
128
+ ScheduledTaskService.validate_schedule_config(
129
+ task_request.schedule_type,
130
+ task_request.schedule_config
131
+ )
132
+
133
+ task_data = {
134
+ "namespace": task_request.namespace,
135
+ "name": task_request.name,
136
+ "queue_name": task_request.queue_name,
137
+ "task_data": task_request.task_data,
138
+ "schedule_type": task_request.schedule_type,
139
+ "schedule_config": task_request.schedule_config,
140
+ "is_active": task_request.is_active,
141
+ "description": task_request.description,
142
+ "max_retry": task_request.max_retry,
143
+ "timeout": task_request.timeout
144
+ }
145
+
146
+ async with data_access.get_session() as session:
147
+ task = await data_access.create_scheduled_task(session, task_data)
148
+
149
+ return {
150
+ "success": True,
151
+ "data": task,
152
+ "message": "定时任务创建成功"
153
+ }
154
+ except ValueError as e:
155
+ raise HTTPException(status_code=400, detail=str(e))
156
+ except Exception as e:
157
+ logger.error(f"创建定时任务失败: {e}")
158
+ raise HTTPException(status_code=500, detail=str(e))
159
+
160
+
161
+ @router.put("/{task_id}")
162
+ async def update_scheduled_task(request: Request, task_id: str, task_request: ScheduledTaskRequest):
163
+ """
164
+ 更新定时任务
165
+
166
+ Args:
167
+ task_id: 任务ID
168
+ task_request: 定时任务更新请求
169
+ """
170
+ try:
171
+ app = request.app
172
+ if not app or not hasattr(app.state, 'data_access'):
173
+ raise HTTPException(status_code=500, detail="Data access not initialized")
174
+
175
+ data_access = app.state.data_access
176
+
177
+ # 验证调度配置
178
+ ScheduledTaskService.validate_schedule_config(
179
+ task_request.schedule_type,
180
+ task_request.schedule_config
181
+ )
182
+
183
+ task_data = {
184
+ "namespace": task_request.namespace,
185
+ "name": task_request.name,
186
+ "queue_name": task_request.queue_name,
187
+ "task_data": task_request.task_data,
188
+ "schedule_type": task_request.schedule_type,
189
+ "schedule_config": task_request.schedule_config,
190
+ "is_active": task_request.is_active,
191
+ "description": task_request.description,
192
+ "max_retry": task_request.max_retry,
193
+ "timeout": task_request.timeout
194
+ }
195
+
196
+ async with data_access.get_session() as session:
197
+ task = await data_access.update_scheduled_task(session, task_id, task_data)
198
+
199
+ return {
200
+ "success": True,
201
+ "data": task,
202
+ "message": "定时任务更新成功"
203
+ }
204
+ except ValueError as e:
205
+ raise HTTPException(status_code=400, detail=str(e))
206
+ except Exception as e:
207
+ logger.error(f"更新定时任务失败: {e}")
208
+ raise HTTPException(status_code=500, detail=str(e))
209
+
210
+
211
+ @router.delete("/{task_id}")
212
+ async def delete_scheduled_task(request: Request, task_id: str):
213
+ """
214
+ 删除定时任务
215
+
216
+ Args:
217
+ task_id: 任务ID
218
+ """
219
+ try:
220
+ app = request.app
221
+ if not app or not hasattr(app.state, 'data_access'):
222
+ raise HTTPException(status_code=500, detail="Data access not initialized")
223
+
224
+ data_access = app.state.data_access
225
+
226
+ async with data_access.get_session() as session:
227
+ success = await data_access.delete_scheduled_task(session, task_id)
228
+
229
+ if success:
230
+ return {
231
+ "success": True,
232
+ "message": f"定时任务 {task_id} 已删除"
233
+ }
234
+ else:
235
+ raise HTTPException(status_code=404, detail="定时任务不存在")
236
+ except HTTPException:
237
+ raise
238
+ except Exception as e:
239
+ logger.error(f"删除定时任务失败: {e}")
240
+ raise HTTPException(status_code=500, detail=str(e))
241
+
242
+
243
+ @router.post("/{task_id}/toggle")
244
+ async def toggle_scheduled_task(request: Request, task_id: str):
245
+ """
246
+ 启用/禁用定时任务
247
+
248
+ Args:
249
+ task_id: 任务ID
250
+ """
251
+ try:
252
+ app = request.app
253
+ if not app or not hasattr(app.state, 'data_access'):
254
+ raise HTTPException(status_code=500, detail="Data access not initialized")
255
+
256
+ data_access = app.state.data_access
257
+
258
+ async with data_access.get_session() as session:
259
+ task = await data_access.toggle_scheduled_task(session, task_id)
260
+
261
+ if task:
262
+ return {
263
+ "success": True,
264
+ "data": {
265
+ "id": task["id"],
266
+ "is_active": task["enabled"] # 映射 enabled 到 is_active
267
+ },
268
+ "message": "定时任务状态已更新"
269
+ }
270
+ else:
271
+ raise HTTPException(status_code=404, detail="定时任务不存在")
272
+ except HTTPException:
273
+ raise
274
+ except Exception as e:
275
+ logger.error(f"切换定时任务状态失败: {e}")
276
+ raise HTTPException(status_code=500, detail=str(e))
277
+
278
+
279
+ @router.post("/{task_id}/execute")
280
+ async def execute_scheduled_task_now(request: Request, task_id: str):
281
+ """
282
+ 立即执行定时任务
283
+
284
+ Args:
285
+ task_id: 任务ID
286
+ """
287
+ try:
288
+ app = request.app
289
+ if not app or not hasattr(app.state, 'data_access'):
290
+ raise HTTPException(status_code=500, detail="Data access not initialized")
291
+
292
+ if not hasattr(app.state, 'namespace_data_access'):
293
+ raise HTTPException(status_code=500, detail="Namespace data access not initialized")
294
+
295
+ data_access = app.state.data_access
296
+ namespace_data_access = app.state.namespace_data_access
297
+
298
+ # 调用服务层执行任务
299
+ result = await ScheduledTaskService.execute_task_now(
300
+ data_access,
301
+ namespace_data_access,
302
+ task_id
303
+ )
304
+
305
+ return result
306
+ except ValueError as e:
307
+ raise HTTPException(status_code=404, detail=str(e))
308
+ except RuntimeError as e:
309
+ raise HTTPException(status_code=400, detail=str(e))
310
+ except Exception as e:
311
+ logger.error(f"执行定时任务失败: {e}")
312
+ raise HTTPException(status_code=500, detail=str(e))
313
+
314
+
315
+ # ============ 执行历史和统计 ============
316
+
317
+ @router.get("/{task_id}/history")
318
+ async def get_scheduled_task_history(
319
+ request: Request,
320
+ task_id: str,
321
+ page: int = Query(1, ge=1),
322
+ page_size: int = Query(20, ge=1, le=100)
323
+ ):
324
+ """
325
+ 获取定时任务执行历史
326
+
327
+ Args:
328
+ task_id: 任务ID
329
+ page: 页码
330
+ page_size: 每页数量
331
+ """
332
+ try:
333
+ app = request.app
334
+ if not app or not hasattr(app.state, 'data_access'):
335
+ raise HTTPException(status_code=500, detail="Data access not initialized")
336
+
337
+ data_access = app.state.data_access
338
+
339
+ async with data_access.get_session() as session:
340
+ history, total = await data_access.fetch_task_execution_history(
341
+ session=session,
342
+ task_id=task_id,
343
+ page=page,
344
+ page_size=page_size
345
+ )
346
+
347
+ return {
348
+ "success": True,
349
+ "data": history,
350
+ "total": total,
351
+ "page": page,
352
+ "page_size": page_size
353
+ }
354
+ except Exception as e:
355
+ logger.error(f"获取定时任务执行历史失败: {e}")
356
+ raise HTTPException(status_code=500, detail=str(e))
357
+
358
+
359
+ @router.get("/{task_id}/trend")
360
+ async def get_scheduled_task_execution_trend(
361
+ request: Request,
362
+ task_id: str,
363
+ time_range: str = Query("7d", description="时间范围")
364
+ ):
365
+ """
366
+ 获取定时任务执行趋势
367
+
368
+ Args:
369
+ task_id: 任务ID
370
+ time_range: 时间范围
371
+ """
372
+ try:
373
+ app = request.app
374
+ if not app or not hasattr(app.state, 'data_access'):
375
+ raise HTTPException(status_code=500, detail="Data access not initialized")
376
+
377
+ data_access = app.state.data_access
378
+
379
+ async with data_access.get_session() as session:
380
+ data = await data_access.fetch_task_execution_trend(
381
+ session=session,
382
+ task_id=task_id,
383
+ time_range=time_range
384
+ )
385
+
386
+ return {
387
+ "success": True,
388
+ "data": data,
389
+ "time_range": time_range
390
+ }
391
+ except Exception as e:
392
+ logger.error(f"获取定时任务执行趋势失败: {e}")
393
+ raise HTTPException(status_code=500, detail=str(e))
394
+
395
+
396
+ @router.get("/statistics/{namespace}")
397
+ async def get_scheduled_tasks_statistics(request: Request, namespace: str):
398
+ """
399
+ 获取定时任务统计数据
400
+
401
+ Args:
402
+ namespace: 命名空间
403
+ """
404
+ try:
405
+ app = request.app
406
+ if not app or not hasattr(app.state, 'data_access'):
407
+ raise HTTPException(status_code=500, detail="Data access not initialized")
408
+
409
+ data_access = app.state.data_access
410
+
411
+ async with data_access.AsyncSessionLocal() as session:
412
+ # 获取统计数据,传递命名空间参数
413
+ stats = await data_access.get_scheduled_tasks_statistics(session, namespace)
414
+ return stats
415
+ except Exception as e:
416
+ logger.error(f"获取定时任务统计失败: {e}")
417
+ raise HTTPException(status_code=500, detail=str(e))
418
+
419
+
420
+ __all__ = ['router']
@@ -0,0 +1,44 @@
1
+ """
2
+ 设置模块 - 系统配置
3
+ 提供轻量级的路由入口,业务逻辑在 SettingsService 中实现
4
+ """
5
+ from fastapi import APIRouter, HTTPException
6
+ import logging
7
+ import traceback
8
+
9
+ from jettask.services.settings_service import SettingsService
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ # 创建设置模块路由,添加 /settings 前缀
14
+ router = APIRouter(prefix="/settings", tags=["settings"])
15
+
16
+
17
+ # ============ 系统配置接口 ============
18
+
19
+ @router.get("/system")
20
+ async def get_system_settings():
21
+ """
22
+ 获取系统配置信息
23
+ 返回系统级别的配置,如数据库连接信息、API配置等
24
+ """
25
+ try:
26
+ return SettingsService.get_system_settings()
27
+ except Exception as e:
28
+ logger.error(f"获取系统配置失败: {e}")
29
+ raise HTTPException(status_code=500, detail=str(e))
30
+
31
+
32
+ @router.get("/database-status")
33
+ async def check_database_status():
34
+ """
35
+ 检查数据库连接状态
36
+ """
37
+ try:
38
+ return await SettingsService.check_database_status()
39
+ except Exception as e:
40
+ logger.error(f"数据库状态检查失败: {e}")
41
+ raise HTTPException(status_code=500, detail=str(e))
42
+
43
+
44
+ __all__ = ['router']
@@ -17,9 +17,9 @@ from sqlalchemy.orm import sessionmaker
17
17
  from sqlalchemy import select, func, and_, or_, text
18
18
  from sqlalchemy.dialects import postgresql
19
19
 
20
- from jettask.webui.pg_consumer import PostgreSQLConsumer
21
- from jettask.webui.config import PostgreSQLConfig, RedisConfig
22
- from jettask.webui.models import Base, Task, QueueStats, Worker
20
+ from jettask.pg_consumer import PostgreSQLConsumer
21
+ from jettask.webui_config import PostgreSQLConfig, RedisConfig
22
+ from jettask.models import Base, Task, QueueStats, Worker
23
23
 
24
24
  logger = logging.getLogger(__name__)
25
25
 
@@ -236,44 +236,7 @@ class RedisMonitor:
236
236
  except Exception as e:
237
237
  print(f"Error reading from stream {prefixed_queue_name}: {e}")
238
238
  return None
239
-
240
- async def get_all_tasks(self) -> List[Dict[str, Any]]:
241
- """获取所有任务状态"""
242
- tasks = []
243
-
244
- # 扫描所有状态键
245
- cursor = 0
246
- pattern = f"{self.redis_prefix}:STATUS:*"
247
-
248
- while True:
249
- cursor, keys = await self.redis.scan(
250
- cursor, match=pattern, count=100
251
- )
252
-
253
- for status_key in keys:
254
- event_id = status_key.split(":")[-1]
255
- task_info = await self.get_task_info(event_id)
256
-
257
- # 尝试获取Stream信息
258
- # 这里需要知道任务所在的队列,可能需要从状态信息中解析
259
- if task_info.get("status"):
260
- try:
261
- status_data = json.loads(task_info["status"])
262
- queue_name = status_data.get("queue")
263
- if queue_name:
264
- stream_info = await self.get_stream_info(queue_name, event_id)
265
- if stream_info:
266
- task_info["stream_info"] = stream_info
267
- except:
268
- pass
269
-
270
- tasks.append(task_info)
271
-
272
- if cursor == 0:
273
- break
274
-
275
- return tasks
276
-
239
+
277
240
  async def get_queue_tasks(self, queue_name: str, start_time: Optional[str] = None,
278
241
  end_time: Optional[str] = None, limit: int = 100) -> Dict[str, Any]:
279
242
  """获取指定队列的任务(基于时间范围)
@@ -1318,11 +1281,6 @@ async def lifespan(app: FastAPI):
1318
1281
 
1319
1282
  app = FastAPI(title="Jettask Monitor", lifespan=lifespan)
1320
1283
 
1321
- @app.get("/api/tasks")
1322
- async def get_tasks():
1323
- """获取所有任务"""
1324
- tasks = await monitor.get_all_tasks()
1325
- return {"tasks": tasks}
1326
1284
 
1327
1285
  @app.get("/api/queue/{queue_name}/tasks")
1328
1286
  async def get_queue_tasks(