jettask 0.2.7__py3-none-any.whl → 0.2.9__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 (66) hide show
  1. jettask/core/cli.py +242 -0
  2. jettask/pg_consumer/sql/add_execution_time_field.sql +29 -0
  3. jettask/pg_consumer/sql/create_new_tables.sql +137 -0
  4. jettask/pg_consumer/sql/create_tables_v3.sql +175 -0
  5. jettask/pg_consumer/sql/migrate_to_new_structure.sql +179 -0
  6. jettask/pg_consumer/sql/modify_time_fields.sql +69 -0
  7. jettask/webui/frontend/package.json +30 -0
  8. jettask/webui/frontend/src/App.css +109 -0
  9. jettask/webui/frontend/src/App.jsx +66 -0
  10. jettask/webui/frontend/src/components/NamespaceSelector.jsx +166 -0
  11. jettask/webui/frontend/src/components/QueueBacklogChart.jsx +298 -0
  12. jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +638 -0
  13. jettask/webui/frontend/src/components/QueueDetailsTable.css +65 -0
  14. jettask/webui/frontend/src/components/QueueDetailsTable.jsx +487 -0
  15. jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +465 -0
  16. jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +423 -0
  17. jettask/webui/frontend/src/components/TaskFilter.jsx +425 -0
  18. jettask/webui/frontend/src/components/TimeRangeSelector.css +21 -0
  19. jettask/webui/frontend/src/components/TimeRangeSelector.jsx +160 -0
  20. jettask/webui/frontend/src/components/charts/QueueChart.jsx +111 -0
  21. jettask/webui/frontend/src/components/charts/QueueTrendChart.jsx +115 -0
  22. jettask/webui/frontend/src/components/charts/WorkerChart.jsx +40 -0
  23. jettask/webui/frontend/src/components/common/StatsCard.jsx +18 -0
  24. jettask/webui/frontend/src/components/layout/AppLayout.css +95 -0
  25. jettask/webui/frontend/src/components/layout/AppLayout.jsx +49 -0
  26. jettask/webui/frontend/src/components/layout/Header.css +106 -0
  27. jettask/webui/frontend/src/components/layout/Header.jsx +106 -0
  28. jettask/webui/frontend/src/components/layout/SideMenu.css +137 -0
  29. jettask/webui/frontend/src/components/layout/SideMenu.jsx +209 -0
  30. jettask/webui/frontend/src/components/layout/TabsNav.css +244 -0
  31. jettask/webui/frontend/src/components/layout/TabsNav.jsx +206 -0
  32. jettask/webui/frontend/src/components/layout/UserInfo.css +197 -0
  33. jettask/webui/frontend/src/components/layout/UserInfo.jsx +197 -0
  34. jettask/webui/frontend/src/contexts/LoadingContext.jsx +27 -0
  35. jettask/webui/frontend/src/contexts/NamespaceContext.jsx +72 -0
  36. jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +245 -0
  37. jettask/webui/frontend/src/index.css +114 -0
  38. jettask/webui/frontend/src/main.jsx +20 -0
  39. jettask/webui/frontend/src/pages/Alerts.jsx +684 -0
  40. jettask/webui/frontend/src/pages/Dashboard/index.css +35 -0
  41. jettask/webui/frontend/src/pages/Dashboard/index.jsx +281 -0
  42. jettask/webui/frontend/src/pages/Dashboard.jsx +1330 -0
  43. jettask/webui/frontend/src/pages/QueueDetail.jsx +1117 -0
  44. jettask/webui/frontend/src/pages/QueueMonitor.jsx +527 -0
  45. jettask/webui/frontend/src/pages/Queues.jsx +12 -0
  46. jettask/webui/frontend/src/pages/ScheduledTasks.jsx +809 -0
  47. jettask/webui/frontend/src/pages/Settings.jsx +800 -0
  48. jettask/webui/frontend/src/pages/Workers.jsx +12 -0
  49. jettask/webui/frontend/src/services/api.js +114 -0
  50. jettask/webui/frontend/src/services/queueTrend.js +152 -0
  51. jettask/webui/frontend/src/utils/suppressWarnings.js +22 -0
  52. jettask/webui/frontend/src/utils/userPreferences.js +154 -0
  53. jettask/webui/frontend/vite.config.js +26 -0
  54. {jettask-0.2.7.dist-info → jettask-0.2.9.dist-info}/METADATA +1 -1
  55. {jettask-0.2.7.dist-info → jettask-0.2.9.dist-info}/RECORD +59 -14
  56. jettask/webui/static/dist/assets/index-7129cfe1.css +0 -1
  57. jettask/webui/static/dist/assets/index-8d1935cc.js +0 -774
  58. jettask/webui/static/dist/index.html +0 -15
  59. jettask/webui/static/index.html +0 -1734
  60. jettask/webui/static/queue.html +0 -981
  61. jettask/webui/static/queues.html +0 -549
  62. jettask/webui/static/workers.html +0 -734
  63. {jettask-0.2.7.dist-info → jettask-0.2.9.dist-info}/WHEEL +0 -0
  64. {jettask-0.2.7.dist-info → jettask-0.2.9.dist-info}/entry_points.txt +0 -0
  65. {jettask-0.2.7.dist-info → jettask-0.2.9.dist-info}/licenses/LICENSE +0 -0
  66. {jettask-0.2.7.dist-info → jettask-0.2.9.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,245 @@
1
+ import { createContext, useContext, useState, useCallback, useEffect, useRef } from 'react';
2
+ import { useLocation, useNavigate } from 'react-router-dom';
3
+
4
+ const TabsContext = createContext();
5
+
6
+ export const useTabs = () => {
7
+ const context = useContext(TabsContext);
8
+ if (!context) {
9
+ throw new Error('useTabs must be used within TabsProvider');
10
+ }
11
+ return context;
12
+ };
13
+
14
+ // 生成标签页的唯一ID
15
+ const generateTabId = (path, params = {}) => {
16
+ // 对于队列详情页等需要区分不同参数的页面,将参数包含在ID中
17
+ const paramStr = Object.keys(params).sort().map(key => `${key}=${params[key]}`).join('&');
18
+ return paramStr ? `${path}?${paramStr}` : path;
19
+ };
20
+
21
+ // 获取页面标题
22
+ const getPageTitle = (path, params = {}) => {
23
+ if (path === '/dashboard') return '概览';
24
+ if (path === '/queues') return '队列';
25
+ if (path === '/workers') return 'Workers';
26
+ if (path === '/scheduled-tasks') return '定时任务';
27
+ if (path === '/alerts') return '监控告警';
28
+ if (path.startsWith('/queue/')) {
29
+ const queueName = path.replace('/queue/', '');
30
+ // 如果有scheduled_task_id参数,说明是从定时任务页面跳转过来的历史记录
31
+ if (params.scheduled_task_id) {
32
+ return `历史记录 (ID:${params.scheduled_task_id})`;
33
+ }
34
+ return `队列: ${decodeURIComponent(queueName)}`;
35
+ }
36
+ return '未知页面';
37
+ };
38
+
39
+ export const TabsProvider = ({ children }) => {
40
+ const [tabs, setTabs] = useState([]);
41
+ const [activeTabId, setActiveTabId] = useState(null);
42
+ const [tabStates, setTabStates] = useState({}); // 保存每个标签页的状态
43
+ const location = useLocation();
44
+ const navigate = useNavigate();
45
+ const isNavigatingRef = useRef(false); // 标记是否正在通过addOrActivateTab导航
46
+
47
+ // 添加或激活标签页
48
+ const addOrActivateTab = useCallback((path, title, params = {}, state = {}) => {
49
+ const tabId = generateTabId(path, params);
50
+
51
+ // 设置导航标记,防止useEffect重复创建标签页
52
+ isNavigatingRef.current = true;
53
+
54
+ setTabs(prevTabs => {
55
+ const existingTab = prevTabs.find(tab => tab.id === tabId);
56
+
57
+ if (existingTab) {
58
+ // 标签页已存在,不需要创建新的
59
+ return prevTabs;
60
+ }
61
+
62
+ // 创建新标签页
63
+ const newTab = {
64
+ id: tabId,
65
+ path,
66
+ title: title || getPageTitle(path, params),
67
+ params,
68
+ closable: true, // 除了某些页面外都可以关闭
69
+ };
70
+
71
+ // 限制最大标签页数量
72
+ const maxTabs = 10;
73
+ if (prevTabs.length >= maxTabs) {
74
+ // 关闭最早的可关闭标签页
75
+ const closableTab = prevTabs.find(tab => tab.closable);
76
+ if (closableTab) {
77
+ return [...prevTabs.filter(tab => tab.id !== closableTab.id), newTab];
78
+ }
79
+ }
80
+ return [...prevTabs, newTab];
81
+ });
82
+
83
+ // 保存标签页状态
84
+ if (state && Object.keys(state).length > 0) {
85
+ setTabStates(prev => ({
86
+ ...prev,
87
+ [tabId]: state
88
+ }));
89
+ }
90
+ // 无论标签页是否已存在,都激活并导航
91
+ setActiveTabId(tabId);
92
+
93
+ // 导航到新标签页的路径
94
+ const fullPath = params && Object.keys(params).length > 0
95
+ ? `${path}?${new URLSearchParams(params).toString()}`
96
+ : path;
97
+ navigate(fullPath);
98
+
99
+ // 导航完成后重置标记
100
+ setTimeout(() => {
101
+ isNavigatingRef.current = false;
102
+ }, 100);
103
+ }, [navigate]);
104
+
105
+ // 关闭标签页
106
+ const closeTab = useCallback((tabId, event) => {
107
+ if (event) {
108
+ event.stopPropagation();
109
+ }
110
+
111
+ setTabs(prevTabs => {
112
+ const tabIndex = prevTabs.findIndex(tab => tab.id === tabId);
113
+ const newTabs = prevTabs.filter(tab => tab.id !== tabId);
114
+
115
+ // 如果关闭的是当前激活的标签页,需要切换到其他标签页
116
+ if (tabId === activeTabId && newTabs.length > 0) {
117
+ // 优先切换到右边的标签页,如果没有则切换到左边
118
+ const newActiveTab = newTabs[Math.min(tabIndex, newTabs.length - 1)];
119
+ setActiveTabId(newActiveTab.id);
120
+ navigate(newActiveTab.path);
121
+ }
122
+
123
+ return newTabs;
124
+ });
125
+
126
+ // 清除标签页状态
127
+ setTabStates(prev => {
128
+ const newStates = { ...prev };
129
+ delete newStates[tabId];
130
+ return newStates;
131
+ });
132
+ }, [activeTabId, navigate]);
133
+
134
+ // 切换标签页
135
+ const switchTab = useCallback((tabId) => {
136
+ const tab = tabs.find(t => t.id === tabId);
137
+ if (tab) {
138
+ setActiveTabId(tabId);
139
+ navigate(tab.path);
140
+ }
141
+ }, [tabs, navigate]);
142
+
143
+ // 保存当前标签页状态
144
+ const saveTabState = useCallback((state) => {
145
+ if (activeTabId) {
146
+ setTabStates(prev => ({
147
+ ...prev,
148
+ [activeTabId]: {
149
+ ...prev[activeTabId],
150
+ ...state
151
+ }
152
+ }));
153
+ }
154
+ }, [activeTabId]);
155
+
156
+ // 获取当前标签页状态
157
+ const getTabState = useCallback(() => {
158
+ return activeTabId ? (tabStates[activeTabId] || {}) : {};
159
+ }, [activeTabId, tabStates]);
160
+
161
+ // 关闭所有标签页
162
+ const closeAllTabs = useCallback(() => {
163
+ setTabs([]);
164
+ setTabStates({});
165
+ setActiveTabId(null);
166
+ }, []);
167
+
168
+ // 关闭其他标签页
169
+ const closeOtherTabs = useCallback((tabId) => {
170
+ const tab = tabs.find(t => t.id === tabId);
171
+ if (tab) {
172
+ setTabs([tab]);
173
+ setTabStates(prev => ({
174
+ [tabId]: prev[tabId]
175
+ }));
176
+ setActiveTabId(tabId);
177
+ }
178
+ }, [tabs]);
179
+
180
+ // 监听路由变化
181
+ useEffect(() => {
182
+ // 如果是通过addOrActivateTab导航的,跳过处理
183
+ if (isNavigatingRef.current) {
184
+ return;
185
+ }
186
+
187
+ const path = location.pathname;
188
+ const search = location.search;
189
+ const params = Object.fromEntries(new URLSearchParams(search));
190
+
191
+ // 生成标签页ID
192
+ const tabId = generateTabId(path, params);
193
+ const existingTab = tabs.find(tab => tab.id === tabId);
194
+
195
+ if (!existingTab) {
196
+ // 如果标签页不存在,创建新的(但不再调用navigate,因为已经在这个路径了)
197
+ setTabs(prevTabs => {
198
+ // 再次检查避免重复
199
+ if (prevTabs.find(tab => tab.id === tabId)) {
200
+ return prevTabs;
201
+ }
202
+
203
+ const newTab = {
204
+ id: tabId,
205
+ path,
206
+ title: getPageTitle(path, params),
207
+ params,
208
+ closable: true,
209
+ };
210
+
211
+ const maxTabs = 10;
212
+ if (prevTabs.length >= maxTabs) {
213
+ const closableTab = prevTabs.find(tab => tab.closable);
214
+ if (closableTab) {
215
+ return [...prevTabs.filter(tab => tab.id !== closableTab.id), newTab];
216
+ }
217
+ }
218
+
219
+ return [...prevTabs, newTab];
220
+ });
221
+ setActiveTabId(tabId);
222
+ } else {
223
+ // 如果标签页存在,激活它
224
+ setActiveTabId(tabId);
225
+ }
226
+ }, [location, tabs]);
227
+
228
+ const value = {
229
+ tabs,
230
+ activeTabId,
231
+ addOrActivateTab,
232
+ closeTab,
233
+ switchTab,
234
+ saveTabState,
235
+ getTabState,
236
+ closeAllTabs,
237
+ closeOtherTabs,
238
+ };
239
+
240
+ return (
241
+ <TabsContext.Provider value={value}>
242
+ {children}
243
+ </TabsContext.Provider>
244
+ );
245
+ };
@@ -0,0 +1,114 @@
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ :root {
8
+ --primary-color: #1890ff;
9
+ --success-color: #52c41a;
10
+ --warning-color: #faad14;
11
+ --error-color: #ff4d4f;
12
+ --bg-primary: #ffffff;
13
+ --bg-secondary: #f5f5f5;
14
+ --bg-card: #ffffff;
15
+ --border-color: rgba(0, 0, 0, 0.06);
16
+ }
17
+
18
+ body {
19
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
20
+ background: #f5f5f5;
21
+ min-height: 100vh;
22
+ color: rgba(0, 0, 0, 0.85);
23
+ }
24
+
25
+ /* 背景动画 - 移除 */
26
+
27
+ #root {
28
+ position: relative;
29
+ z-index: 1;
30
+ min-height: 100vh;
31
+ }
32
+
33
+ /* 自定义滚动条 */
34
+ ::-webkit-scrollbar {
35
+ width: 8px;
36
+ height: 8px;
37
+ }
38
+
39
+ ::-webkit-scrollbar-track {
40
+ background: rgba(0, 0, 0, 0.05);
41
+ border-radius: 4px;
42
+ }
43
+
44
+ ::-webkit-scrollbar-thumb {
45
+ background: rgba(0, 0, 0, 0.2);
46
+ border-radius: 4px;
47
+ }
48
+
49
+ ::-webkit-scrollbar-thumb:hover {
50
+ background: rgba(0, 0, 0, 0.3);
51
+ }
52
+
53
+ /* Ant Design 自定义样式覆盖 */
54
+ .ant-layout {
55
+ background: transparent !important;
56
+ }
57
+
58
+ .ant-card {
59
+ background: #ffffff !important;
60
+ border: 1px solid #f0f0f0;
61
+ }
62
+
63
+ .stats-card {
64
+ background: #ffffff;
65
+ border-radius: 8px;
66
+ border: 1px solid #f0f0f0;
67
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
68
+ overflow: hidden;
69
+ position: relative;
70
+ }
71
+
72
+ .stats-card::before {
73
+ content: '';
74
+ position: absolute;
75
+ top: 0;
76
+ left: 0;
77
+ right: 0;
78
+ height: 4px;
79
+ background: linear-gradient(90deg, transparent, var(--primary-color), transparent);
80
+ opacity: 0;
81
+ transition: opacity 0.3s;
82
+ }
83
+
84
+ .stats-card:hover {
85
+ transform: translateY(-4px);
86
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
87
+ border-color: #e8e8e8;
88
+ }
89
+
90
+ .stats-card:hover::before {
91
+ opacity: 1;
92
+ }
93
+
94
+ /* 图表容器样式 */
95
+ .chart-container {
96
+ position: relative;
97
+ height: 100%;
98
+ min-height: 300px;
99
+ }
100
+
101
+ /* 加载动画 */
102
+ .loading-spinner {
103
+ display: inline-block;
104
+ width: 20px;
105
+ height: 20px;
106
+ border: 2px solid rgba(0, 0, 0, 0.1);
107
+ border-top-color: var(--primary-color);
108
+ border-radius: 50%;
109
+ animation: spin 0.8s linear infinite;
110
+ }
111
+
112
+ @keyframes spin {
113
+ to { transform: rotate(360deg); }
114
+ }
@@ -0,0 +1,20 @@
1
+ import React from 'react'
2
+ import ReactDOM from 'react-dom/client'
3
+ import { ConfigProvider } from 'antd'
4
+ import zhCN from 'antd/locale/zh_CN'
5
+ import dayjs from 'dayjs'
6
+ import 'dayjs/locale/zh-cn'
7
+ import App from './App'
8
+ import './index.css'
9
+ import './utils/suppressWarnings'
10
+
11
+ // 设置 dayjs 为中文
12
+ dayjs.locale('zh-cn')
13
+
14
+ ReactDOM.createRoot(document.getElementById('root')).render(
15
+ <React.StrictMode>
16
+ <ConfigProvider locale={zhCN}>
17
+ <App />
18
+ </ConfigProvider>
19
+ </React.StrictMode>,
20
+ )