jettask 0.2.7__py3-none-any.whl → 0.2.8__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 +152 -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.8.dist-info}/METADATA +1 -1
  55. {jettask-0.2.7.dist-info → jettask-0.2.8.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.8.dist-info}/WHEEL +0 -0
  64. {jettask-0.2.7.dist-info → jettask-0.2.8.dist-info}/entry_points.txt +0 -0
  65. {jettask-0.2.7.dist-info → jettask-0.2.8.dist-info}/licenses/LICENSE +0 -0
  66. {jettask-0.2.7.dist-info → jettask-0.2.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,137 @@
1
+ /* 侧边栏样式 */
2
+ .app-sider {
3
+ position: fixed;
4
+ left: 0;
5
+ top: 0;
6
+ bottom: 0;
7
+ z-index: 100;
8
+ box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
9
+ }
10
+
11
+ /* Logo区域 */
12
+ .sider-header {
13
+ height: 48px;
14
+ display: flex;
15
+ align-items: center;
16
+ justify-content: center;
17
+ border-bottom: 1px solid #303030;
18
+ }
19
+
20
+ .logo-container {
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ }
25
+
26
+ .logo-icon {
27
+ color: #1890ff;
28
+ font-size: 20px;
29
+ font-weight: bold;
30
+ width: 28px;
31
+ height: 28px;
32
+ display: flex;
33
+ align-items: center;
34
+ justify-content: center;
35
+ background: rgba(24, 144, 255, 0.1);
36
+ border-radius: 4px;
37
+ }
38
+
39
+ /* 菜单样式 */
40
+ .side-menu {
41
+ padding: 12px 0;
42
+ }
43
+
44
+ /* 紧凑菜单样式 */
45
+ .side-menu.compact .ant-menu-item {
46
+ height: 56px;
47
+ line-height: 56px;
48
+ margin: 0 0 8px 0 !important;
49
+ padding: 0 !important;
50
+ display: flex;
51
+ flex-direction: column;
52
+ align-items: center;
53
+ justify-content: center;
54
+ color: rgba(255, 255, 255, 0.65);
55
+ transition: all 0.2s;
56
+ }
57
+
58
+ .side-menu.compact .ant-menu-item:hover {
59
+ color: #fff !important;
60
+ background: rgba(24, 144, 255, 0.1) !important;
61
+ }
62
+
63
+ .side-menu.compact .ant-menu-item-selected {
64
+ background: transparent !important;
65
+ color: #1890ff !important;
66
+ position: relative;
67
+ }
68
+
69
+ .side-menu.compact .ant-menu-item-selected::before {
70
+ content: '';
71
+ position: absolute;
72
+ left: 0;
73
+ top: 50%;
74
+ transform: translateY(-50%);
75
+ width: 3px;
76
+ height: 24px;
77
+ background: #1890ff;
78
+ border-radius: 0 2px 2px 0;
79
+ }
80
+
81
+ .side-menu.compact .ant-menu-item-selected::after {
82
+ display: none;
83
+ }
84
+
85
+ /* 菜单项内容容器 */
86
+ .menu-item-content {
87
+ display: flex;
88
+ flex-direction: column;
89
+ align-items: center;
90
+ justify-content: center;
91
+ width: 100%;
92
+ height: 100%;
93
+ }
94
+
95
+ /* 图标包装器 */
96
+ .menu-icon-wrapper {
97
+ display: flex;
98
+ align-items: center;
99
+ justify-content: center;
100
+ font-size: 18px;
101
+ margin-bottom: 6px;
102
+ }
103
+
104
+ /* 菜单标签 */
105
+ .menu-label {
106
+ font-size: 11px;
107
+ line-height: 1;
108
+ white-space: nowrap;
109
+ opacity: 0.85;
110
+ font-weight: 400;
111
+ }
112
+
113
+ /* 图标和文字垂直排列 */
114
+ .side-menu.compact .ant-menu-item .ant-menu-item-icon {
115
+ margin: 0;
116
+ min-width: auto;
117
+ }
118
+
119
+ .side-menu.compact .ant-menu-title-content {
120
+ margin: 0;
121
+ padding: 0;
122
+ }
123
+
124
+ /* 分割线样式 */
125
+ .ant-menu-item-divider {
126
+ margin: 12px 8px;
127
+ border-color: #303030;
128
+ }
129
+
130
+ /* 移除默认的内边距 */
131
+ .side-menu.compact .ant-menu-inline {
132
+ border: none;
133
+ }
134
+
135
+ .side-menu.compact .ant-menu-sub {
136
+ background: transparent !important;
137
+ }
@@ -0,0 +1,209 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Layout, Menu, Tooltip } from 'antd';
3
+ import { useNavigate, useLocation } from 'react-router-dom';
4
+ import {
5
+ DashboardOutlined,
6
+ AppstoreOutlined,
7
+ ClockCircleOutlined,
8
+ AlertOutlined,
9
+ SettingOutlined,
10
+ DatabaseOutlined,
11
+ BarChartOutlined,
12
+ TeamOutlined,
13
+ CloudServerOutlined,
14
+ MenuFoldOutlined,
15
+ MenuUnfoldOutlined,
16
+ FundOutlined,
17
+ FileTextOutlined,
18
+ SafetyOutlined,
19
+ ApiOutlined
20
+ } from '@ant-design/icons';
21
+ import './SideMenu.css';
22
+
23
+ const { Sider } = Layout;
24
+
25
+ const SideMenu = ({ onMenuClick }) => {
26
+ const navigate = useNavigate();
27
+ const location = useLocation();
28
+ const [collapsed, setCollapsed] = useState(false);
29
+ const [selectedKeys, setSelectedKeys] = useState([]);
30
+
31
+ const menuItems = [
32
+ {
33
+ key: 'dashboard',
34
+ icon: <DashboardOutlined />,
35
+ label: '概览',
36
+ shortLabel: '概览',
37
+ path: '/dashboard',
38
+ },
39
+ {
40
+ key: 'queues',
41
+ icon: <AppstoreOutlined />,
42
+ label: '任务队列',
43
+ shortLabel: '队列',
44
+ path: '/queues',
45
+ },
46
+ {
47
+ key: 'scheduled-tasks',
48
+ icon: <ClockCircleOutlined />,
49
+ label: '定时任务',
50
+ shortLabel: '定时',
51
+ path: '/scheduled-tasks',
52
+ },
53
+ {
54
+ key: 'alerts',
55
+ icon: <AlertOutlined />,
56
+ label: '监控告警',
57
+ shortLabel: '告警',
58
+ path: '/alerts',
59
+ },
60
+ {
61
+ key: 'analytics',
62
+ icon: <BarChartOutlined />,
63
+ label: '数据分析',
64
+ shortLabel: '分析',
65
+ path: '/analytics',
66
+ },
67
+ // {
68
+ // key: 'performance',
69
+ // icon: <FundOutlined />,
70
+ // label: '性能监控',
71
+ // shortLabel: '性能',
72
+ // path: '/performance',
73
+ // },
74
+ // {
75
+ // key: 'logs',
76
+ // icon: <FileTextOutlined />,
77
+ // label: '日志查询',
78
+ // shortLabel: '日志',
79
+ // path: '/logs',
80
+ // },
81
+ {
82
+ key: 'divider2',
83
+ type: 'divider',
84
+ },
85
+ {
86
+ key: 'api-docs',
87
+ icon: <ApiOutlined />,
88
+ label: 'API文档',
89
+ shortLabel: 'API',
90
+ path: '/api-docs',
91
+ },
92
+ {
93
+ key: 'settings',
94
+ icon: <SettingOutlined />,
95
+ label: '系统设置',
96
+ shortLabel: '设置',
97
+ path: '/settings',
98
+ },
99
+ ];
100
+
101
+ useEffect(() => {
102
+ const currentItem = menuItems.find(item => item.path === location.pathname);
103
+ if (currentItem) {
104
+ setSelectedKeys([currentItem.key]);
105
+ }
106
+ }, [location.pathname]);
107
+
108
+ const handleMenuClick = ({ key }) => {
109
+ // 查找菜单项(包括子菜单项)
110
+ let targetItem = null;
111
+ for (const item of menuItems) {
112
+ if (item.key === key) {
113
+ targetItem = item;
114
+ break;
115
+ }
116
+ if (item.children) {
117
+ const childItem = item.children.find(child => child.key === key);
118
+ if (childItem) {
119
+ targetItem = childItem;
120
+ break;
121
+ }
122
+ }
123
+ }
124
+
125
+ if (targetItem && targetItem.path) {
126
+ navigate(targetItem.path);
127
+ if (onMenuClick) {
128
+ onMenuClick(targetItem);
129
+ }
130
+ }
131
+ };
132
+
133
+ return (
134
+ <Sider
135
+ trigger={null}
136
+ collapsible
137
+ collapsed={false}
138
+ className="app-sider"
139
+ width={64}
140
+ style={{
141
+ background: '#1a1d21',
142
+ borderRight: '1px solid #303030',
143
+ }}
144
+ >
145
+ <div className="sider-header">
146
+ <div className="logo-container">
147
+ <span className="logo-icon">A</span>
148
+ </div>
149
+ </div>
150
+
151
+ <Menu
152
+ mode="inline"
153
+ selectedKeys={selectedKeys}
154
+ onClick={handleMenuClick}
155
+ className="side-menu compact"
156
+ style={{
157
+ background: 'transparent',
158
+ borderRight: 0,
159
+ }}
160
+ items={menuItems.map(item => {
161
+ if (item.type === 'divider') {
162
+ return {
163
+ type: 'divider',
164
+ style: { margin: '8px 0', borderColor: '#303030' }
165
+ };
166
+ }
167
+
168
+ // 如果有子菜单项
169
+ if (item.children) {
170
+ return {
171
+ key: item.key,
172
+ label: (
173
+ <Tooltip placement="right" title={item.label}>
174
+ <div className="menu-item-content">
175
+ <div className="menu-icon-wrapper">
176
+ {item.icon}
177
+ </div>
178
+ <span className="menu-label">{item.shortLabel}</span>
179
+ </div>
180
+ </Tooltip>
181
+ ),
182
+ children: item.children.map(child => ({
183
+ key: child.key,
184
+ label: child.label,
185
+ icon: child.icon,
186
+ }))
187
+ };
188
+ }
189
+
190
+ return {
191
+ key: item.key,
192
+ label: (
193
+ <Tooltip placement="right" title={item.label}>
194
+ <div className="menu-item-content">
195
+ <div className="menu-icon-wrapper">
196
+ {item.icon}
197
+ </div>
198
+ <span className="menu-label">{item.shortLabel}</span>
199
+ </div>
200
+ </Tooltip>
201
+ ),
202
+ };
203
+ })}
204
+ />
205
+ </Sider>
206
+ );
207
+ };
208
+
209
+ export default SideMenu;
@@ -0,0 +1,244 @@
1
+ /* 标签导航容器 */
2
+ .tabs-nav-container {
3
+ flex: 1;
4
+ height: 100%;
5
+ display: flex;
6
+ align-items: center;
7
+ padding: 0;
8
+ padding-right: 8px;
9
+ overflow: hidden;
10
+ position: relative;
11
+ }
12
+
13
+ /* 标签样式 - 扁平化设计 */
14
+ .tabs-nav {
15
+ height: 100%;
16
+ width: 100%;
17
+ }
18
+
19
+ .tabs-nav .ant-tabs-nav {
20
+ height: 100%;
21
+ margin: 0;
22
+ background: transparent;
23
+ border: none;
24
+ display: flex;
25
+ align-items: center;
26
+ }
27
+
28
+ .tabs-nav .ant-tabs-nav::before {
29
+ border: none;
30
+ }
31
+
32
+ .tabs-nav .ant-tabs-nav-wrap {
33
+ display: flex;
34
+ align-items: center;
35
+ height: auto;
36
+ }
37
+
38
+ .tabs-nav .ant-tabs-nav-list {
39
+ height: 48px;
40
+ display: flex;
41
+ align-items: flex-end;
42
+ gap: 2px;
43
+ padding-top: 8px;
44
+ }
45
+
46
+ .tabs-nav .ant-tabs-tab {
47
+ background: transparent;
48
+ border: none;
49
+ border-radius: 4px 4px 0 0;
50
+ margin: 0;
51
+ padding: 0 12px;
52
+ height: 40px;
53
+ line-height: 40px;
54
+ min-width: 90px;
55
+ color: rgba(255, 255, 255, 0.65);
56
+ transition: all 0.2s;
57
+ }
58
+
59
+ /* 第一个标签特殊处理 - 左侧无圆角 */
60
+ .tabs-nav .ant-tabs-tab:first-child {
61
+ border-top-left-radius: 0;
62
+ padding-left: 16px;
63
+ }
64
+
65
+ .tabs-nav .ant-tabs-tab:hover {
66
+ background: rgba(255, 255, 255, 0.08);
67
+ color: rgba(255, 255, 255, 0.85);
68
+ }
69
+
70
+ .tabs-nav .ant-tabs-tab-active {
71
+ background: #f0f2f5;
72
+ color: #262626;
73
+ position: relative;
74
+ border-left: 1px solid #d9d9d9;
75
+ border-top: 1px solid #d9d9d9;
76
+ border-right: 1px solid #d9d9d9;
77
+ border-bottom: 1px solid #f0f2f5;
78
+ font-weight: 500;
79
+ z-index: 20;
80
+ height: 41px;
81
+ padding: 0 12px;
82
+ margin-bottom: -1px;
83
+ }
84
+
85
+ /* 第一个激活标签左侧无边框 */
86
+ .tabs-nav .ant-tabs-tab-active:first-child {
87
+ padding-left: 16px;
88
+ border-left: none;
89
+ }
90
+
91
+ .tabs-nav .ant-tabs-tab-active:hover {
92
+ background: #f0f2f5;
93
+ color: #262626;
94
+ }
95
+
96
+ .tabs-nav .ant-tabs-tab-btn {
97
+ color: inherit;
98
+ }
99
+
100
+ /* 标签内容样式 */
101
+ .tab-label {
102
+ display: inline-flex;
103
+ align-items: center;
104
+ gap: 4px;
105
+ user-select: none;
106
+ font-size: 13px;
107
+ white-space: nowrap;
108
+ line-height: 1;
109
+ }
110
+
111
+ /* 概览标签特殊样式 - 居中显示 */
112
+ .tab-label-center {
113
+ justify-content: center;
114
+ width: 100%;
115
+ text-align: center;
116
+ }
117
+
118
+ .tab-icon {
119
+ display: inline-flex;
120
+ align-items: center;
121
+ font-size: 12px;
122
+ opacity: 0.8;
123
+ }
124
+
125
+ /* 关闭按钮样式 */
126
+ .tabs-nav .ant-tabs-tab-remove {
127
+ margin-left: 8px;
128
+ margin-right: -6px;
129
+ color: rgba(255, 255, 255, 0.45);
130
+ font-size: 10px;
131
+ transition: all 0.2s;
132
+ padding: 0;
133
+ display: inline-flex;
134
+ align-items: center;
135
+ justify-content: center;
136
+ width: 12px;
137
+ height: 12px;
138
+ opacity: 0;
139
+ visibility: hidden;
140
+ }
141
+
142
+ /* 鼠标悬停在标签上时显示关闭按钮 */
143
+ .tabs-nav .ant-tabs-tab:hover .ant-tabs-tab-remove {
144
+ opacity: 1;
145
+ visibility: visible;
146
+ }
147
+
148
+ .tabs-nav .ant-tabs-tab-remove:hover {
149
+ color: rgba(255, 255, 255, 0.85);
150
+ background: rgba(255, 255, 255, 0.1);
151
+ border-radius: 2px;
152
+ }
153
+
154
+ /* 激活标签的关闭按钮 - 始终显示 */
155
+ .tabs-nav .ant-tabs-tab-active .ant-tabs-tab-remove {
156
+ color: rgba(0, 0, 0, 0.45);
157
+ opacity: 1;
158
+ visibility: visible;
159
+ }
160
+
161
+ .tabs-nav .ant-tabs-tab-active .ant-tabs-tab-remove:hover {
162
+ color: rgba(0, 0, 0, 0.65);
163
+ background: rgba(0, 0, 0, 0.06);
164
+ }
165
+
166
+ /* 禁用Ant Design的卡片样式 */
167
+ .tabs-nav.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab,
168
+ .tabs-nav.ant-tabs-editable-card > .ant-tabs-nav .ant-tabs-tab {
169
+ background: transparent;
170
+ border: none;
171
+ }
172
+
173
+ .tabs-nav.ant-tabs-card > .ant-tabs-nav .ant-tabs-tab-active,
174
+ .tabs-nav.ant-tabs-editable-card > .ant-tabs-nav .ant-tabs-tab-active {
175
+ background: #f0f2f5;
176
+ border-left: 1px solid #d9d9d9;
177
+ border-top: 1px solid #d9d9d9;
178
+ border-right: 1px solid #d9d9d9;
179
+ border-bottom: 1px solid #f0f2f5;
180
+ margin-bottom: -1px;
181
+ }
182
+
183
+ /* 导航操作按钮 */
184
+ .tabs-nav .ant-tabs-nav-operations {
185
+ display: none;
186
+ }
187
+
188
+ /* 标签ink bar隐藏 */
189
+ .tabs-nav .ant-tabs-ink-bar {
190
+ display: none;
191
+ }
192
+
193
+ /* 滚动条样式 */
194
+ .tabs-nav .ant-tabs-nav-wrap {
195
+ overflow: hidden;
196
+ }
197
+
198
+ .tabs-nav .ant-tabs-nav-list {
199
+ transition: transform 0.3s;
200
+ }
201
+
202
+ /* 上下文菜单样式 */
203
+ .ant-dropdown-menu {
204
+ min-width: 120px;
205
+ }
206
+
207
+ .ant-dropdown-menu-item {
208
+ padding: 8px 12px;
209
+ }
210
+
211
+ .ant-dropdown-menu-item .anticon {
212
+ margin-right: 8px;
213
+ }
214
+
215
+ /* 移除多余的边距和填充 */
216
+ .tabs-nav.ant-tabs {
217
+ line-height: 1;
218
+ }
219
+
220
+ .tabs-nav .ant-tabs-content-holder {
221
+ display: none;
222
+ }
223
+
224
+ /* 标签分隔线 - 仅非激活标签显示 */
225
+ .tabs-nav .ant-tabs-tab {
226
+ position: relative;
227
+ }
228
+
229
+ .tabs-nav .ant-tabs-tab:not(.ant-tabs-tab-active)::after {
230
+ content: '';
231
+ position: absolute;
232
+ top: 50%;
233
+ right: 0;
234
+ transform: translateY(-50%);
235
+ width: 1px;
236
+ height: 14px;
237
+ background: rgba(255, 255, 255, 0.08);
238
+ }
239
+
240
+ .tabs-nav .ant-tabs-tab:last-child::after,
241
+ .tabs-nav .ant-tabs-tab-active::after,
242
+ .tabs-nav .ant-tabs-tab-active + .ant-tabs-tab::after {
243
+ display: none;
244
+ }