jettask 0.2.8__tar.gz → 0.2.13__tar.gz

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 (178) hide show
  1. {jettask-0.2.8/jettask.egg-info → jettask-0.2.13}/PKG-INFO +1 -1
  2. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/cli.py +175 -34
  3. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/namespace_data_access.py +52 -25
  4. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/main.jsx +2 -0
  5. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/pages/ScheduledTasks.jsx +2 -1
  6. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/pages/Settings.jsx +2 -1
  7. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/services/api.js +49 -4
  8. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/services/queueTrend.js +16 -2
  9. {jettask-0.2.8 → jettask-0.2.13/jettask.egg-info}/PKG-INFO +1 -1
  10. {jettask-0.2.8 → jettask-0.2.13}/pyproject.toml +1 -1
  11. {jettask-0.2.8 → jettask-0.2.13}/LICENSE +0 -0
  12. {jettask-0.2.8 → jettask-0.2.13}/MANIFEST.in +0 -0
  13. {jettask-0.2.8 → jettask-0.2.13}/README.md +0 -0
  14. {jettask-0.2.8 → jettask-0.2.13}/jettask/__init__.py +0 -0
  15. {jettask-0.2.8 → jettask-0.2.13}/jettask/config/__init__.py +0 -0
  16. {jettask-0.2.8 → jettask-0.2.13}/jettask/config/performance.py +0 -0
  17. {jettask-0.2.8 → jettask-0.2.13}/jettask/constants.py +0 -0
  18. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/__init__.py +0 -0
  19. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/app.py +0 -0
  20. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/app_importer.py +0 -0
  21. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/consumer_manager.py +0 -0
  22. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/context.py +0 -0
  23. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/delay_scanner.py +0 -0
  24. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/enums.py +0 -0
  25. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/event_pool.py +0 -0
  26. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/heartbeat_process.py +0 -0
  27. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/message.py +0 -0
  28. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/offline_worker_recovery.py +0 -0
  29. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/retry.py +0 -0
  30. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/task.py +0 -0
  31. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/task_batch.py +0 -0
  32. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/unified_manager_base.py +0 -0
  33. {jettask-0.2.8 → jettask-0.2.13}/jettask/core/worker_scanner.py +0 -0
  34. {jettask-0.2.8 → jettask-0.2.13}/jettask/exceptions.py +0 -0
  35. {jettask-0.2.8 → jettask-0.2.13}/jettask/executors/__init__.py +0 -0
  36. {jettask-0.2.8 → jettask-0.2.13}/jettask/executors/asyncio.py +0 -0
  37. {jettask-0.2.8 → jettask-0.2.13}/jettask/executors/base.py +0 -0
  38. {jettask-0.2.8 → jettask-0.2.13}/jettask/executors/common.py +0 -0
  39. {jettask-0.2.8 → jettask-0.2.13}/jettask/executors/multi_asyncio.py +0 -0
  40. {jettask-0.2.8 → jettask-0.2.13}/jettask/monitor/run_backlog_collector.py +0 -0
  41. {jettask-0.2.8 → jettask-0.2.13}/jettask/monitor/stream_backlog_monitor.py +0 -0
  42. {jettask-0.2.8 → jettask-0.2.13}/jettask/monitoring/__init__.py +0 -0
  43. {jettask-0.2.8 → jettask-0.2.13}/jettask/monitoring/file_watcher.py +0 -0
  44. {jettask-0.2.8 → jettask-0.2.13}/jettask/pg_consumer/pg_consumer_v2.py +0 -0
  45. {jettask-0.2.8 → jettask-0.2.13}/jettask/pg_consumer/sql/add_execution_time_field.sql +0 -0
  46. {jettask-0.2.8 → jettask-0.2.13}/jettask/pg_consumer/sql/create_new_tables.sql +0 -0
  47. {jettask-0.2.8 → jettask-0.2.13}/jettask/pg_consumer/sql/create_tables_v3.sql +0 -0
  48. {jettask-0.2.8 → jettask-0.2.13}/jettask/pg_consumer/sql/migrate_to_new_structure.sql +0 -0
  49. {jettask-0.2.8 → jettask-0.2.13}/jettask/pg_consumer/sql/modify_time_fields.sql +0 -0
  50. {jettask-0.2.8 → jettask-0.2.13}/jettask/pg_consumer/sql_utils.py +0 -0
  51. {jettask-0.2.8 → jettask-0.2.13}/jettask/router.py +0 -0
  52. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/__init__.py +0 -0
  53. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/add_execution_count.sql +0 -0
  54. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/add_priority_field.sql +0 -0
  55. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/add_scheduler_id.sql +0 -0
  56. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/add_scheduler_id_index.sql +0 -0
  57. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/loader.py +0 -0
  58. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/make_scheduler_id_required.sql +0 -0
  59. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/manager.py +0 -0
  60. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/migrate_interval_seconds.sql +0 -0
  61. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/models.py +0 -0
  62. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/multi_namespace_scheduler.py +0 -0
  63. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/performance_optimization.sql +0 -0
  64. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/run_scheduler.py +0 -0
  65. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/scheduler.py +0 -0
  66. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/schema.sql +0 -0
  67. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/unified_manager.py +0 -0
  68. {jettask-0.2.8 → jettask-0.2.13}/jettask/scheduler/unified_scheduler_manager.py +0 -0
  69. {jettask-0.2.8 → jettask-0.2.13}/jettask/utils/__init__.py +0 -0
  70. {jettask-0.2.8 → jettask-0.2.13}/jettask/utils/error_handler.py +0 -0
  71. {jettask-0.2.8 → jettask-0.2.13}/jettask/utils/exception_hook.py +0 -0
  72. {jettask-0.2.8 → jettask-0.2.13}/jettask/utils/helpers.py +0 -0
  73. {jettask-0.2.8 → jettask-0.2.13}/jettask/utils/logger.py +0 -0
  74. {jettask-0.2.8 → jettask-0.2.13}/jettask/utils/serializer.py +0 -0
  75. {jettask-0.2.8 → jettask-0.2.13}/jettask/utils/serializer_optimized.py +0 -0
  76. {jettask-0.2.8 → jettask-0.2.13}/jettask/utils/task_logger.py +0 -0
  77. {jettask-0.2.8 → jettask-0.2.13}/jettask/utils/traceback_filter.py +0 -0
  78. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/__init__.py +0 -0
  79. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/__main__.py +0 -0
  80. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/api.py +0 -0
  81. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/__init__.py +0 -0
  82. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/api/__init__.py +0 -0
  83. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/api/v1/__init__.py +0 -0
  84. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/api/v1/monitoring.py +0 -0
  85. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/api/v1/namespaces.py +0 -0
  86. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/api/v1/queues.py +0 -0
  87. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/api/v1/tasks.py +0 -0
  88. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/config.py +0 -0
  89. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/core/__init__.py +0 -0
  90. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/core/cache.py +0 -0
  91. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/core/database.py +0 -0
  92. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/core/exceptions.py +0 -0
  93. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/data_access.py +0 -0
  94. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/data_api.py +0 -0
  95. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/dependencies.py +0 -0
  96. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/init_meta_db.py +0 -0
  97. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/main.py +0 -0
  98. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/main_unified.py +0 -0
  99. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/main_v2.py +0 -0
  100. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/models/__init__.py +0 -0
  101. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/models/requests.py +0 -0
  102. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/models/responses.py +0 -0
  103. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/namespace_api.py +0 -0
  104. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/namespace_api_old.py +0 -0
  105. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/queue_backlog_api.py +0 -0
  106. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/queue_stats_v2.py +0 -0
  107. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/redis_monitor_api.py +0 -0
  108. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/services/__init__.py +0 -0
  109. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/start.py +0 -0
  110. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/backend/unified_api_router.py +0 -0
  111. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/cleanup_deprecated_tables.sql +0 -0
  112. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/config.py +0 -0
  113. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/db_init.py +0 -0
  114. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/index.html +0 -0
  115. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/package.json +0 -0
  116. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/App.css +0 -0
  117. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/App.jsx +0 -0
  118. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/NamespaceSelector.jsx +0 -0
  119. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/QueueBacklogChart.jsx +0 -0
  120. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +0 -0
  121. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/QueueDetailsTable.css +0 -0
  122. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/QueueDetailsTable.jsx +0 -0
  123. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +0 -0
  124. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +0 -0
  125. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/TaskFilter.jsx +0 -0
  126. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/TimeRangeSelector.css +0 -0
  127. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/TimeRangeSelector.jsx +0 -0
  128. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/charts/QueueChart.jsx +0 -0
  129. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/charts/QueueTrendChart.jsx +0 -0
  130. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/charts/WorkerChart.jsx +0 -0
  131. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/common/StatsCard.jsx +0 -0
  132. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/layout/AppLayout.css +0 -0
  133. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/layout/AppLayout.jsx +0 -0
  134. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/layout/Header.css +0 -0
  135. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/layout/Header.jsx +0 -0
  136. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/layout/SideMenu.css +0 -0
  137. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/layout/SideMenu.jsx +0 -0
  138. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/layout/TabsNav.css +0 -0
  139. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/layout/TabsNav.jsx +0 -0
  140. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/layout/UserInfo.css +0 -0
  141. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/components/layout/UserInfo.jsx +0 -0
  142. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/contexts/LoadingContext.jsx +0 -0
  143. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/contexts/NamespaceContext.jsx +0 -0
  144. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +0 -0
  145. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/index.css +0 -0
  146. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/pages/Alerts.jsx +0 -0
  147. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/pages/Dashboard/index.css +0 -0
  148. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/pages/Dashboard/index.jsx +0 -0
  149. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/pages/Dashboard.jsx +0 -0
  150. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/pages/QueueDetail.jsx +0 -0
  151. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/pages/QueueMonitor.jsx +0 -0
  152. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/pages/Queues.jsx +0 -0
  153. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/pages/Workers.jsx +0 -0
  154. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/utils/suppressWarnings.js +0 -0
  155. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/src/utils/userPreferences.js +0 -0
  156. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/frontend/vite.config.js +0 -0
  157. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/gradio_app.py +0 -0
  158. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/integrated_gradio_app.py +0 -0
  159. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/models/__init__.py +0 -0
  160. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/models/namespace.py +0 -0
  161. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/models.py +0 -0
  162. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/multi_namespace_consumer.py +0 -0
  163. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/pg_consumer.py +0 -0
  164. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/run.py +0 -0
  165. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/run_monitor.py +0 -0
  166. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/run_webui.py +0 -0
  167. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/schema.sql +0 -0
  168. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/sql/batch_upsert_functions.sql +0 -0
  169. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/sql/init_database.sql +0 -0
  170. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/task_center.py +0 -0
  171. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/task_center_client.py +0 -0
  172. {jettask-0.2.8 → jettask-0.2.13}/jettask/webui/unified_consumer_manager.py +0 -0
  173. {jettask-0.2.8 → jettask-0.2.13}/jettask.egg-info/SOURCES.txt +0 -0
  174. {jettask-0.2.8 → jettask-0.2.13}/jettask.egg-info/dependency_links.txt +0 -0
  175. {jettask-0.2.8 → jettask-0.2.13}/jettask.egg-info/entry_points.txt +0 -0
  176. {jettask-0.2.8 → jettask-0.2.13}/jettask.egg-info/requires.txt +0 -0
  177. {jettask-0.2.8 → jettask-0.2.13}/jettask.egg-info/top_level.txt +0 -0
  178. {jettask-0.2.8 → jettask-0.2.13}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jettask
3
- Version: 0.2.8
3
+ Version: 0.2.13
4
4
  Summary: A high-performance distributed task queue system with web monitoring
5
5
  Author-email: JetTask Team <support@jettask.io>
6
6
  License-Expression: MIT
@@ -475,28 +475,32 @@ def scheduler(task_center, interval, batch_size, check_interval, debug):
475
475
  click.echo("\nShutdown complete")
476
476
 
477
477
  @cli.command()
478
- @click.option('--port', default=5173, help='前端开发服务器端口')
479
- @click.option('--host', default='0.0.0.0', help='前端开发服务器监听地址')
478
+ @click.option('--port', default=8080, help='前端服务器端口')
479
+ @click.option('--host', default='0.0.0.0', help='前端服务器监听地址')
480
+ @click.option('--api-url', default='http://localhost:8001', help='后端 API 服务器地址')
480
481
  @click.option('--auto-install', is_flag=True, default=True, help='自动安装缺失的依赖')
481
482
  @click.option('--force-install', is_flag=True, help='强制重新安装依赖')
482
- @click.option('--build', is_flag=True, help='构建生产版本而不是启动开发服务器')
483
- def frontend(port, host, auto_install, force_install, build):
483
+ @click.option('--build-only', is_flag=True, help='仅构建生产版本,不启动服务器')
484
+ def frontend(port, host, api_url, auto_install, force_install, build_only):
484
485
  """启动 WebUI 前端界面
485
486
 
486
- 自动检测环境并启动前端开发服务器:
487
+ 启动生产版本服务器:
487
488
  1. 检查 Node.js 和 npm 是否安装
488
- 2. 自动安装缺失的依赖
489
- 3. 启动开发服务器或构建生产版本
489
+ 2. 在包目录安装依赖并构建
490
+ 3. 启动生产版本服务器
490
491
 
491
492
  示例:
492
- # 启动开发服务器(默认)
493
+ # 启动生产版本服务器(默认)
493
494
  jettask frontend
494
495
 
495
496
  # 指定端口
496
497
  jettask frontend --port 3000
497
498
 
498
- # 构建生产版本
499
- jettask frontend --build
499
+ # 指定后端API地址
500
+ jettask frontend --api-url http://192.168.1.100:8001
501
+
502
+ # 仅构建生产版本
503
+ jettask frontend --build-only
500
504
 
501
505
  # 强制重新安装依赖
502
506
  jettask frontend --force-install
@@ -541,7 +545,7 @@ def frontend(port, host, auto_install, force_install, build):
541
545
 
542
546
  # 切换到前端目录
543
547
  os.chdir(frontend_dir)
544
- click.echo(f"切换到前端目录: {frontend_dir}")
548
+ click.echo(f"前端目录: {frontend_dir}")
545
549
 
546
550
  # 检查 package.json 是否存在
547
551
  package_json = frontend_dir / "package.json"
@@ -583,48 +587,185 @@ def frontend(port, host, auto_install, force_install, build):
583
587
 
584
588
  # 构建或启动
585
589
  try:
586
- if build:
587
- # 构建生产版本
590
+ # 构建生产版本
591
+ # 注意:根据 vite.config.js,构建输出到 ../static/dist
592
+ dist_dir = frontend_dir.parent / "static" / "dist"
593
+
594
+ # 检查是否需要构建
595
+ need_build = not dist_dir.exists() or build_only
596
+
597
+ # 如果 dist 不存在或明确要求构建,则进行构建
598
+ if need_build:
588
599
  click.echo("\n正在构建生产版本...")
589
600
  subprocess.run([npm_cmd, 'run', 'build'], check=True)
590
601
 
591
- # 显示构建结果
592
- dist_dir = frontend_dir.parent / "static" / "dist"
593
602
  if dist_dir.exists():
594
- click.echo(f"\n✓ 构建完成!输出目录: {dist_dir}")
595
603
  # 统计文件
596
604
  files = list(dist_dir.rglob('*'))
597
605
  file_count = len([f for f in files if f.is_file()])
598
606
  total_size = sum(f.stat().st_size for f in files if f.is_file())
607
+ click.echo(f"\n✓ 构建完成!")
608
+ click.echo(f" - 输出目录: {dist_dir}")
599
609
  click.echo(f" - 文件数量: {file_count}")
600
610
  click.echo(f" - 总大小: {total_size / 1024 / 1024:.2f} MB")
601
611
  else:
602
- click.echo("警告:构建完成但未找到输出目录")
603
- else:
604
- # 启动开发服务器
605
- click.echo(f"\n正在启动前端开发服务器...")
606
- click.echo(f" - 地址: http://{host}:{port}")
607
- click.echo(f" - 本地访问: http://localhost:{port}")
608
- click.echo("\n按 Ctrl+C 停止服务器\n")
612
+ click.echo("错误:构建失败,未生成输出目录", err=True)
613
+ sys.exit(1)
614
+
615
+ # 如果不是仅构建模式,启动生产服务器
616
+ if not build_only:
617
+ if not dist_dir.exists():
618
+ click.echo("错误:未找到构建输出,请先运行构建", err=True)
619
+ sys.exit(1)
609
620
 
610
- # 构建启动命令
611
- cmd = [npm_cmd, 'run', 'dev']
621
+ click.echo(f"\n正在启动生产版本服务器...")
622
+ click.echo(f" - 访问地址: http://localhost:{port}")
612
623
  if host != 'localhost':
613
- cmd.extend(['--', '--host', host])
614
- if port != 5173:
615
- cmd.extend(['--port', str(port)])
624
+ click.echo(f" - 网络地址: http://{host}:{port}")
625
+ click.echo(f" - 静态文件: {dist_dir}")
626
+ click.echo(f" - API 地址: {api_url}")
627
+ click.echo("\n按 Ctrl+C 停止服务器\n")
628
+
629
+ # 使用 Python 内置的 HTTP 服务器提供静态文件
630
+ import http.server
631
+ import socketserver
632
+ import urllib.request
633
+ import urllib.parse
634
+ import json
635
+
636
+ class ProxyHandler(http.server.SimpleHTTPRequestHandler):
637
+ def __init__(self, *args, **kwargs):
638
+ super().__init__(*args, directory=str(dist_dir), **kwargs)
639
+
640
+ def proxy_request(self, method='GET', body=None):
641
+ """代理 API 请求到后端服务器"""
642
+ if self.path.startswith('/api/') or self.path.startswith('/ws/'):
643
+ # 构建目标 URL
644
+ target_url = api_url + self.path
645
+
646
+ try:
647
+ # 准备请求
648
+ req = urllib.request.Request(target_url, method=method)
649
+
650
+ # 复制请求头
651
+ for header, value in self.headers.items():
652
+ if header.lower() not in ['host', 'connection']:
653
+ req.add_header(header, value)
654
+
655
+ # 添加请求体
656
+ if body:
657
+ req.data = body
658
+
659
+ # 发送请求
660
+ with urllib.request.urlopen(req, timeout=30) as response:
661
+ # 返回响应
662
+ self.send_response(response.getcode())
663
+ for header, value in response.headers.items():
664
+ if header.lower() not in ['connection', 'transfer-encoding']:
665
+ self.send_header(header, value)
666
+ self.end_headers()
667
+ self.wfile.write(response.read())
668
+ return True
669
+
670
+ except urllib.error.HTTPError as e:
671
+ self.send_response(e.code)
672
+ self.end_headers()
673
+ self.wfile.write(e.read())
674
+ return True
675
+ except Exception as e:
676
+ self.send_error(502, f"Bad Gateway: {str(e)}")
677
+ return True
678
+ return False
679
+
680
+ def do_GET(self):
681
+ # 先尝试代理 API 请求
682
+ if self.proxy_request('GET'):
683
+ return
684
+
685
+ # 对于 index.html,注入 API URL
686
+ if self.path == '/' or self.path == '/index.html' or (
687
+ self.path != '/' and not Path(dist_dir / self.path[1:]).exists()
688
+ ):
689
+ # 读取 index.html
690
+ index_path = dist_dir / 'index.html'
691
+ if index_path.exists():
692
+ with open(index_path, 'r', encoding='utf-8') as f:
693
+ html_content = f.read()
694
+
695
+ # 注入 API URL 配置(使用相对路径,因为我们会代理)
696
+ api_config = f'''
697
+ <script>
698
+ window.JETTASK_API_URL = ''; // 使用相对路径,由服务器代理
699
+ console.log('API requests will be proxied by server');
700
+ </script>
701
+ '''
702
+
703
+ # 在 </head> 标签前注入配置
704
+ html_content = html_content.replace('</head>', api_config + '</head>')
705
+
706
+ # 发送响应
707
+ self.send_response(200)
708
+ self.send_header('Content-type', 'text/html')
709
+ self.send_header('Content-Length', str(len(html_content.encode('utf-8'))))
710
+ self.end_headers()
711
+ self.wfile.write(html_content.encode('utf-8'))
712
+ return
713
+
714
+ # 其他文件正常处理
715
+ return super().do_GET()
716
+
717
+ def do_POST(self):
718
+ # 读取请求体
719
+ content_length = int(self.headers.get('Content-Length', 0))
720
+ body = self.rfile.read(content_length) if content_length > 0 else None
721
+
722
+ # 代理 POST 请求
723
+ if self.proxy_request('POST', body):
724
+ return
725
+
726
+ # 不支持的请求
727
+ self.send_error(405, "Method Not Allowed")
728
+
729
+ def do_PUT(self):
730
+ content_length = int(self.headers.get('Content-Length', 0))
731
+ body = self.rfile.read(content_length) if content_length > 0 else None
732
+ if self.proxy_request('PUT', body):
733
+ return
734
+ self.send_error(405, "Method Not Allowed")
735
+
736
+ def do_DELETE(self):
737
+ if self.proxy_request('DELETE'):
738
+ return
739
+ self.send_error(405, "Method Not Allowed")
740
+
741
+ def log_message(self, format, *args):
742
+ # 自定义日志格式
743
+ click.echo(f"[{self.log_date_time_string()}] {format % args}")
744
+
745
+ # 创建服务器
746
+ class ReuseAddrTCPServer(socketserver.TCPServer):
747
+ allow_reuse_address = True
748
+ allow_reuse_port = True
616
749
 
617
- # 启动开发服务器
618
- process = subprocess.Popen(cmd)
619
- process.wait()
750
+ try:
751
+ click.echo(f"正在绑定到 {host}:{port}...")
752
+ httpd = ReuseAddrTCPServer((host, port), ProxyHandler)
753
+ click.echo(f"✓ 服务器已启动,监听 {host}:{port}")
754
+ click.echo(f"✓ API 请求将被代理到 {api_url}")
755
+ httpd.serve_forever()
756
+ except OSError as e:
757
+ if e.errno == 98: # Address already in use
758
+ click.echo(f"错误:端口 {port} 已被占用,请尝试其他端口", err=True)
759
+ else:
760
+ click.echo(f"错误:{e}", err=True)
761
+ sys.exit(1)
762
+ except KeyboardInterrupt:
763
+ pass
620
764
  except subprocess.CalledProcessError as e:
621
765
  click.echo(f"错误:命令执行失败 - {e}", err=True)
622
766
  sys.exit(1)
623
767
  except KeyboardInterrupt:
624
768
  click.echo("\n停止前端服务器")
625
- if 'process' in locals():
626
- process.terminate()
627
- process.wait()
628
769
 
629
770
  def main():
630
771
  """主入口函数"""
@@ -181,35 +181,62 @@ class NamespaceDataAccessManager:
181
181
 
182
182
  async def get_namespace_config(self, namespace_name: str) -> dict:
183
183
  """从任务中心API获取命名空间配置"""
184
- url = f"{self.task_center_base_url}/api/namespaces/{namespace_name}"
185
-
184
+ # 直接从数据库获取,而不是通过HTTP调用自己
185
+ # 这避免了循环依赖和网络问题
186
186
  try:
187
- session = await self._get_session()
188
- async with session.get(url) as resp:
189
- if resp.status == 200:
190
- data = await resp.json()
191
- # API返回的是redis_config和pg_config,直接使用
192
- redis_config = data.get('redis_config', {})
193
- pg_config = data.get('pg_config', {})
194
-
195
- # 兼容旧格式:如果有redis_url和pg_url字段
196
- if not redis_config and data.get('redis_url'):
197
- redis_config = {'url': data.get('redis_url')}
198
-
199
- if not pg_config and data.get('pg_url'):
200
- pg_config = {'url': data.get('pg_url')}
201
-
187
+ # 先尝试从缓存或直接数据库访问
188
+ from jettask.webui.backend.models import NamespaceModel
189
+ from jettask.webui.backend.db_init import get_async_session
190
+ from sqlalchemy import select
191
+
192
+ async with get_async_session() as session:
193
+ stmt = select(NamespaceModel).where(NamespaceModel.name == namespace_name)
194
+ result = await session.execute(stmt)
195
+ namespace = result.scalar_one_or_none()
196
+
197
+ if namespace:
202
198
  return {
203
- 'name': data.get('name'),
204
- 'redis_config': redis_config,
205
- 'pg_config': pg_config
199
+ 'name': namespace.name,
200
+ 'redis_config': namespace.redis_config or {},
201
+ 'pg_config': namespace.pg_config or {}
206
202
  }
207
203
  else:
208
- raise ValueError(f"无法获取命名空间 {namespace_name} 的配置: HTTP {resp.status}")
209
- except Exception as e:
210
- logger.error(f"获取命名空间 {namespace_name} 配置失败: {e}")
211
- traceback.print_exc()
212
- raise
204
+ raise ValueError(f"命名空间 {namespace_name} 不存在")
205
+ except (ImportError, Exception) as e:
206
+ # 如果无法直接访问数据库,则通过HTTP调用(但使用127.0.0.1)
207
+ logger.warning(f"直接数据库访问失败,回退到HTTP调用: {e}")
208
+ base_url = self.task_center_base_url
209
+ if 'localhost' in base_url:
210
+ base_url = base_url.replace('localhost', '127.0.0.1')
211
+ url = f"{base_url}/api/namespaces/{namespace_name}"
212
+
213
+ try:
214
+ session = await self._get_session()
215
+ async with session.get(url) as resp:
216
+ if resp.status == 200:
217
+ data = await resp.json()
218
+ # API返回的是redis_config和pg_config,直接使用
219
+ redis_config = data.get('redis_config', {})
220
+ pg_config = data.get('pg_config', {})
221
+
222
+ # 兼容旧格式:如果有redis_url和pg_url字段
223
+ if not redis_config and data.get('redis_url'):
224
+ redis_config = {'url': data.get('redis_url')}
225
+
226
+ if not pg_config and data.get('pg_url'):
227
+ pg_config = {'url': data.get('pg_url')}
228
+
229
+ return {
230
+ 'name': data.get('name'),
231
+ 'redis_config': redis_config,
232
+ 'pg_config': pg_config
233
+ }
234
+ else:
235
+ raise ValueError(f"无法获取命名空间 {namespace_name} 的配置: HTTP {resp.status}")
236
+ except Exception as e:
237
+ logger.error(f"通过HTTP获取命名空间 {namespace_name} 配置失败: {e}")
238
+ traceback.print_exc()
239
+ raise
213
240
 
214
241
  async def get_connection(self, namespace_name: str) -> NamespaceConnection:
215
242
  """
@@ -7,6 +7,8 @@ import 'dayjs/locale/zh-cn'
7
7
  import App from './App'
8
8
  import './index.css'
9
9
  import './utils/suppressWarnings'
10
+ // 初始化 API 配置(必须在其他组件之前)
11
+ import './services/api'
10
12
 
11
13
  // 设置 dayjs 为中文
12
14
  dayjs.locale('zh-cn')
@@ -102,8 +102,9 @@ function ScheduledTasks() {
102
102
  offset: (params.current - 1) * params.pageSize,
103
103
  };
104
104
 
105
+ const apiBase = window.JETTASK_API_URL || 'http://localhost:8001';
105
106
  const response = await axios.get(
106
- `http://localhost:8001/api/data/scheduled-tasks/${currentNamespace}`,
107
+ `${apiBase}/api/data/scheduled-tasks/${currentNamespace}`,
107
108
  { params: requestParams }
108
109
  );
109
110
 
@@ -161,7 +161,8 @@ function Settings() {
161
161
  refreshNamespaceList(); // 触发全局命名空间列表刷新
162
162
 
163
163
  // 显示连接URL
164
- const fullUrl = `http://localhost:8001${response.data.connection_url}`;
164
+ const apiBase = window.JETTASK_API_URL || 'http://localhost:8001';
165
+ const fullUrl = `${apiBase}${response.data.connection_url}`;
165
166
  Modal.success({
166
167
  title: '命名空间创建成功',
167
168
  content: (
@@ -2,6 +2,17 @@ import axios from 'axios';
2
2
 
3
3
  // 智能获取 API 基础 URL
4
4
  const getApiBaseUrl = () => {
5
+ // 优先使用注入的 API URL
6
+ if (typeof window !== 'undefined' && window.JETTASK_API_URL !== undefined) {
7
+ const url = window.JETTASK_API_URL;
8
+ // 如果是空字符串,表示使用相对路径(代理模式)
9
+ if (url === '') {
10
+ return ''; // 使用相对路径,不加前缀
11
+ }
12
+ // 否则确保 URL 以 /api 结尾
13
+ return url.endsWith('/api') ? url : `${url}/api`;
14
+ }
15
+
5
16
  // 在开发环境中使用 localhost
6
17
  if (typeof window !== 'undefined') {
7
18
  const hostname = window.location.hostname;
@@ -19,6 +30,19 @@ const getApiBaseUrl = () => {
19
30
 
20
31
  // 智能获取 WebSocket URL
21
32
  const getWsBaseUrl = () => {
33
+ // 优先使用注入的 API URL
34
+ if (typeof window !== 'undefined' && window.JETTASK_API_URL !== undefined) {
35
+ const url = window.JETTASK_API_URL;
36
+ // 如果是空字符串,使用相对路径
37
+ if (url === '') {
38
+ const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
39
+ return `${wsProtocol}//${window.location.host}/ws`;
40
+ }
41
+ // 将 http 转换为 ws,https 转换为 wss
42
+ const wsUrl = url.replace(/^http/, 'ws');
43
+ return wsUrl.endsWith('/ws') ? wsUrl : `${wsUrl}/ws`;
44
+ }
45
+
22
46
  if (typeof window !== 'undefined') {
23
47
  const hostname = window.location.hostname;
24
48
  const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
@@ -36,16 +60,37 @@ const getWsBaseUrl = () => {
36
60
  const API_BASE_URL = getApiBaseUrl();
37
61
  const WS_BASE_URL = getWsBaseUrl();
38
62
 
63
+ // 设置 axios 全局默认配置
64
+ // 空字符串时不设置 baseURL,让 axios 使用相对路径
65
+ if (API_BASE_URL !== '') {
66
+ axios.defaults.baseURL = API_BASE_URL;
67
+ }
68
+
39
69
  // 创建 axios 实例
70
+ // 如果 baseURL 为空,设置一个请求拦截器来处理路径
40
71
  const api = axios.create({
41
- baseURL: API_BASE_URL,
72
+ baseURL: API_BASE_URL === '' ? undefined : API_BASE_URL, // 空字符串时不设置 baseURL
42
73
  timeout: 10000,
43
74
  });
44
75
 
76
+ // 如果是代理模式,确保路径正确
77
+ if (API_BASE_URL === '') {
78
+ api.interceptors.request.use(config => {
79
+ // 确保路径以 /api/ 开头
80
+ if (!config.url.startsWith('/api/')) {
81
+ config.url = '/api/' + config.url;
82
+ }
83
+ return config;
84
+ });
85
+ }
86
+
87
+ // 导出配置好的实例和URLs
88
+ export { api, API_BASE_URL, WS_BASE_URL };
89
+
45
90
  // 获取全局统计信息
46
91
  export const fetchGlobalStats = async () => {
47
92
  try {
48
- const response = await api.get('/stats');
93
+ const response = await api.get('stats'); // 不要前导斜杠,axios 会自动处理
49
94
  return response.data.data;
50
95
  } catch (error) {
51
96
  console.error('Failed to fetch global stats:', error);
@@ -56,7 +101,7 @@ export const fetchGlobalStats = async () => {
56
101
  // 获取队列列表
57
102
  export const fetchQueues = async () => {
58
103
  try {
59
- const response = await api.get('/queues');
104
+ const response = await api.get('queues'); // 不要前导斜杠
60
105
  return response.data;
61
106
  } catch (error) {
62
107
  console.error('Failed to fetch queues:', error);
@@ -69,7 +114,7 @@ export const fetchQueueTimeline = async (params) => {
69
114
  try {
70
115
  // 从参数中提取命名空间,如果没有则使用 default
71
116
  const namespace = params.namespace || 'default';
72
- const response = await api.post(`/queue-timeline/${namespace}`, params);
117
+ const response = await api.post(`queue-timeline/${namespace}`, params); // 不要前导斜杠
73
118
  // 处理数据,确保不包含填充的空值
74
119
  const data = response.data.data || response.data || [];
75
120
  return {
@@ -3,6 +3,16 @@ import axios from 'axios';
3
3
  // 在 React 应用中,环境变量需要以 REACT_APP_ 开头
4
4
  // 使用 window.location 作为后备方案
5
5
  const getApiBaseUrl = () => {
6
+ // 优先使用注入的 API URL
7
+ if (typeof window !== 'undefined' && window.JETTASK_API_URL !== undefined) {
8
+ const url = window.JETTASK_API_URL;
9
+ // 如果是空字符串,表示使用相对路径(代理模式)
10
+ if (url === '') {
11
+ return ''; // 使用相对路径
12
+ }
13
+ return url;
14
+ }
15
+
6
16
  // 尝试从环境变量获取(Create React App 会在构建时注入)
7
17
  if (typeof window !== 'undefined' && window.REACT_APP_API_URL) {
8
18
  return window.REACT_APP_API_URL;
@@ -39,7 +49,9 @@ export const fetchQueueTrend = async (timeRange = '1h', queues = [], customTimeR
39
49
  params.end = customTimeRange[1].valueOf();
40
50
  }
41
51
 
42
- const response = await axios.get(`${API_BASE_URL}/api/queue-trend`, { params });
52
+ // API_BASE_URL 为空时,使用相对路径;否则需要加 /api
53
+ const url = API_BASE_URL === '' ? '/api/queue-trend' : `${API_BASE_URL}/api/queue-trend`;
54
+ const response = await axios.get(url, { params });
43
55
 
44
56
  // 处理响应数据 - 后端现在直接返回时间线数据
45
57
  const data = response.data.data || response.data || [];
@@ -132,7 +144,9 @@ const getIntervalByRange = (range) => {
132
144
  */
133
145
  export const fetchQueueStats = async (queueName, timeRange = '1h') => {
134
146
  try {
135
- const response = await axios.get(`${API_BASE_URL}/api/queue/${queueName}/stats`, {
147
+ // API_BASE_URL 为空时,使用相对路径;否则需要加 /api
148
+ const url = API_BASE_URL === '' ? `/api/queue/${queueName}/stats` : `${API_BASE_URL}/api/queue/${queueName}/stats`;
149
+ const response = await axios.get(url, {
136
150
  params: { range: timeRange }
137
151
  });
138
152
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jettask
3
- Version: 0.2.8
3
+ Version: 0.2.13
4
4
  Summary: A high-performance distributed task queue system with web monitoring
5
5
  Author-email: JetTask Team <support@jettask.io>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "jettask"
7
- version = "0.2.8"
7
+ version = "0.2.13"
8
8
  authors = [
9
9
  {name = "JetTask Team", email = "support@jettask.io"},
10
10
  ]
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes