jettask 0.2.1__py3-none-any.whl → 0.2.4__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 (89) hide show
  1. jettask/constants.py +213 -0
  2. jettask/core/app.py +525 -205
  3. jettask/core/cli.py +193 -185
  4. jettask/core/consumer_manager.py +126 -34
  5. jettask/core/context.py +3 -0
  6. jettask/core/enums.py +137 -0
  7. jettask/core/event_pool.py +501 -168
  8. jettask/core/message.py +147 -0
  9. jettask/core/offline_worker_recovery.py +181 -114
  10. jettask/core/task.py +10 -174
  11. jettask/core/task_batch.py +153 -0
  12. jettask/core/unified_manager_base.py +243 -0
  13. jettask/core/worker_scanner.py +54 -54
  14. jettask/executors/asyncio.py +184 -64
  15. jettask/webui/backend/config.py +51 -0
  16. jettask/webui/backend/data_access.py +2083 -92
  17. jettask/webui/backend/data_api.py +3294 -0
  18. jettask/webui/backend/dependencies.py +261 -0
  19. jettask/webui/backend/init_meta_db.py +158 -0
  20. jettask/webui/backend/main.py +1358 -69
  21. jettask/webui/backend/main_unified.py +78 -0
  22. jettask/webui/backend/main_v2.py +394 -0
  23. jettask/webui/backend/namespace_api.py +295 -0
  24. jettask/webui/backend/namespace_api_old.py +294 -0
  25. jettask/webui/backend/namespace_data_access.py +611 -0
  26. jettask/webui/backend/queue_backlog_api.py +727 -0
  27. jettask/webui/backend/queue_stats_v2.py +521 -0
  28. jettask/webui/backend/redis_monitor_api.py +476 -0
  29. jettask/webui/backend/unified_api_router.py +1601 -0
  30. jettask/webui/db_init.py +204 -32
  31. jettask/webui/frontend/package-lock.json +492 -1
  32. jettask/webui/frontend/package.json +4 -1
  33. jettask/webui/frontend/src/App.css +105 -7
  34. jettask/webui/frontend/src/App.jsx +49 -20
  35. jettask/webui/frontend/src/components/NamespaceSelector.jsx +166 -0
  36. jettask/webui/frontend/src/components/QueueBacklogChart.jsx +298 -0
  37. jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +638 -0
  38. jettask/webui/frontend/src/components/QueueDetailsTable.css +65 -0
  39. jettask/webui/frontend/src/components/QueueDetailsTable.jsx +487 -0
  40. jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +465 -0
  41. jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +423 -0
  42. jettask/webui/frontend/src/components/TaskFilter.jsx +425 -0
  43. jettask/webui/frontend/src/components/TimeRangeSelector.css +21 -0
  44. jettask/webui/frontend/src/components/TimeRangeSelector.jsx +160 -0
  45. jettask/webui/frontend/src/components/layout/AppLayout.css +95 -0
  46. jettask/webui/frontend/src/components/layout/AppLayout.jsx +49 -0
  47. jettask/webui/frontend/src/components/layout/Header.css +34 -10
  48. jettask/webui/frontend/src/components/layout/Header.jsx +31 -23
  49. jettask/webui/frontend/src/components/layout/SideMenu.css +137 -0
  50. jettask/webui/frontend/src/components/layout/SideMenu.jsx +209 -0
  51. jettask/webui/frontend/src/components/layout/TabsNav.css +244 -0
  52. jettask/webui/frontend/src/components/layout/TabsNav.jsx +206 -0
  53. jettask/webui/frontend/src/components/layout/UserInfo.css +197 -0
  54. jettask/webui/frontend/src/components/layout/UserInfo.jsx +197 -0
  55. jettask/webui/frontend/src/contexts/NamespaceContext.jsx +72 -0
  56. jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +245 -0
  57. jettask/webui/frontend/src/main.jsx +1 -0
  58. jettask/webui/frontend/src/pages/Alerts.jsx +684 -0
  59. jettask/webui/frontend/src/pages/Dashboard.jsx +1330 -0
  60. jettask/webui/frontend/src/pages/QueueDetail.jsx +1109 -10
  61. jettask/webui/frontend/src/pages/QueueMonitor.jsx +236 -115
  62. jettask/webui/frontend/src/pages/Queues.jsx +5 -1
  63. jettask/webui/frontend/src/pages/ScheduledTasks.jsx +809 -0
  64. jettask/webui/frontend/src/pages/Settings.jsx +800 -0
  65. jettask/webui/frontend/src/services/api.js +7 -5
  66. jettask/webui/frontend/src/utils/suppressWarnings.js +22 -0
  67. jettask/webui/frontend/src/utils/userPreferences.js +154 -0
  68. jettask/webui/multi_namespace_consumer.py +543 -0
  69. jettask/webui/pg_consumer.py +983 -246
  70. jettask/webui/static/dist/assets/index-7129cfe1.css +1 -0
  71. jettask/webui/static/dist/assets/index-8d1935cc.js +774 -0
  72. jettask/webui/static/dist/index.html +2 -2
  73. jettask/webui/task_center.py +216 -0
  74. jettask/webui/task_center_client.py +150 -0
  75. jettask/webui/unified_consumer_manager.py +193 -0
  76. {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/METADATA +1 -1
  77. jettask-0.2.4.dist-info/RECORD +134 -0
  78. jettask/webui/pg_consumer_slow.py +0 -1099
  79. jettask/webui/pg_consumer_test.py +0 -678
  80. jettask/webui/static/dist/assets/index-823408e8.css +0 -1
  81. jettask/webui/static/dist/assets/index-9968b0b8.js +0 -543
  82. jettask/webui/test_pg_consumer_recovery.py +0 -547
  83. jettask/webui/test_recovery_simple.py +0 -492
  84. jettask/webui/test_self_recovery.py +0 -467
  85. jettask-0.2.1.dist-info/RECORD +0 -91
  86. {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/WHEEL +0 -0
  87. {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/entry_points.txt +0 -0
  88. {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/licenses/LICENSE +0 -0
  89. {jettask-0.2.1.dist-info → jettask-0.2.4.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,49 @@
1
+ import React, { useState } from 'react';
2
+ import { Layout } from 'antd';
3
+ import { Outlet } from 'react-router-dom';
4
+ import SideMenu from './SideMenu';
5
+ import TabsNav from './TabsNav';
6
+ import UserInfo from './UserInfo';
7
+ import NamespaceSelector from '../NamespaceSelector';
8
+ import { useNamespace } from '../../contexts/NamespaceContext';
9
+ import './AppLayout.css';
10
+
11
+ const { Header, Content } = Layout;
12
+
13
+ const AppLayout = () => {
14
+ const { currentNamespace, setCurrentNamespace } = useNamespace();
15
+
16
+ console.log('🔧 AppLayout渲染,currentNamespace:', currentNamespace);
17
+ console.log('🔧 AppLayout渲染,setCurrentNamespace:', typeof setCurrentNamespace);
18
+
19
+ return (
20
+ <Layout className="app-layout">
21
+ {/* 左侧菜单 */}
22
+ <SideMenu />
23
+
24
+ {/* 右侧主体 */}
25
+ <Layout className="main-layout" style={{ marginLeft: 64 }}>
26
+ {/* 顶部导航栏 */}
27
+ <Header className="app-header">
28
+ <div className="header-left">
29
+ <TabsNav />
30
+ </div>
31
+ <div className="header-right">
32
+ <NamespaceSelector
33
+ value={currentNamespace}
34
+ onChange={setCurrentNamespace}
35
+ />
36
+ <UserInfo />
37
+ </div>
38
+ </Header>
39
+
40
+ {/* 内容区域 */}
41
+ <Content className="app-content">
42
+ <Outlet />
43
+ </Content>
44
+ </Layout>
45
+ </Layout>
46
+ );
47
+ };
48
+
49
+ export default AppLayout;
@@ -1,11 +1,10 @@
1
1
  .app-header {
2
- background: rgba(20, 20, 20, 0.8) !important;
3
- backdrop-filter: blur(20px);
2
+ background: rgba(20, 20, 20, 0.95) !important;
4
3
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
5
4
  padding: 0;
6
- position: sticky;
7
- top: 0;
8
- z-index: 1000;
5
+ position: relative;
6
+ height: 48px !important;
7
+ line-height: 48px !important;
9
8
  }
10
9
 
11
10
  .header-container {
@@ -15,7 +14,7 @@
15
14
  display: flex;
16
15
  align-items: center;
17
16
  justify-content: space-between;
18
- height: 64px;
17
+ height: 48px;
19
18
  }
20
19
 
21
20
  .header-left {
@@ -26,15 +25,15 @@
26
25
  .app-logo {
27
26
  display: flex;
28
27
  align-items: center;
29
- gap: 12px;
28
+ gap: 8px;
30
29
  color: white;
31
- font-size: 20px;
30
+ font-size: 16px;
32
31
  font-weight: 700;
33
32
  cursor: pointer;
34
33
  }
35
34
 
36
35
  .logo-icon {
37
- font-size: 24px;
36
+ font-size: 18px;
38
37
  color: var(--primary-color);
39
38
  }
40
39
 
@@ -54,10 +53,13 @@
54
53
  .header-menu {
55
54
  background: transparent !important;
56
55
  border: none !important;
56
+ line-height: 48px !important;
57
57
  }
58
58
 
59
59
  .header-menu .ant-menu-item {
60
60
  color: rgba(255, 255, 255, 0.65) !important;
61
+ height: 48px !important;
62
+ line-height: 48px !important;
61
63
  }
62
64
 
63
65
  .header-menu .ant-menu-item:hover {
@@ -79,4 +81,26 @@
79
81
 
80
82
  .refresh-btn:hover {
81
83
  color: white !important;
82
- }
84
+ }
85
+
86
+ /* 命名空间选择器样式 */
87
+ .header-right {
88
+ display: flex;
89
+ align-items: center;
90
+ gap: 16px;
91
+ }
92
+
93
+ /* 脉动动画 */
94
+ @keyframes pulse {
95
+ 0% {
96
+ opacity: 1;
97
+ }
98
+ 50% {
99
+ opacity: 0.5;
100
+ }
101
+ 100% {
102
+ opacity: 1;
103
+ }
104
+ }
105
+
106
+ /* 响应式设计 */
@@ -1,15 +1,19 @@
1
1
  import React from 'react'
2
2
  import { useNavigate, useLocation } from 'react-router-dom'
3
- import { Layout, Menu, Space, Button, Badge } from 'antd'
3
+ import { Layout, Menu } from 'antd'
4
4
  import {
5
5
  DashboardOutlined,
6
6
  AppstoreOutlined,
7
7
  TeamOutlined,
8
8
  RocketOutlined,
9
- ReloadOutlined,
10
- LoadingOutlined
9
+ LoadingOutlined,
10
+ ClockCircleOutlined,
11
+ AlertOutlined,
12
+ CloudServerOutlined
11
13
  } from '@ant-design/icons'
12
14
  import { useLoading } from '../../contexts/LoadingContext'
15
+ import { useNamespace } from '../../contexts/NamespaceContext'
16
+ import NamespaceSelector from '../NamespaceSelector'
13
17
  import './Header.css'
14
18
 
15
19
  const { Header: AntHeader } = Layout
@@ -18,6 +22,7 @@ const Header = () => {
18
22
  const navigate = useNavigate()
19
23
  const location = useLocation()
20
24
  const { isLoading } = useLoading()
25
+ const { currentNamespace, setCurrentNamespace } = useNamespace()
21
26
 
22
27
  const menuItems = [
23
28
  {
@@ -30,10 +35,20 @@ const Header = () => {
30
35
  icon: <AppstoreOutlined />,
31
36
  label: '队列',
32
37
  },
38
+ // {
39
+ // key: '/workers',
40
+ // icon: <TeamOutlined />,
41
+ // label: 'Workers',
42
+ // },
33
43
  {
34
- key: '/workers',
35
- icon: <TeamOutlined />,
36
- label: 'Workers',
44
+ key: '/scheduled-tasks',
45
+ icon: <ClockCircleOutlined />,
46
+ label: '定时任务',
47
+ },
48
+ {
49
+ key: '/alerts',
50
+ icon: <AlertOutlined />,
51
+ label: '监控告警',
37
52
  },
38
53
  ]
39
54
 
@@ -41,10 +56,6 @@ const Header = () => {
41
56
  navigate(key)
42
57
  }
43
58
 
44
- const handleRefresh = () => {
45
- window.location.reload()
46
- }
47
-
48
59
  return (
49
60
  <AntHeader className="app-header">
50
61
  <div className="header-container">
@@ -54,7 +65,7 @@ const Header = () => {
54
65
  <LoadingOutlined
55
66
  className="logo-icon"
56
67
  style={{
57
- fontSize: 24,
68
+ fontSize: 18,
58
69
  color: '#1890ff'
59
70
  }}
60
71
  spin
@@ -77,19 +88,16 @@ const Header = () => {
77
88
  </div>
78
89
 
79
90
  <div className="header-right">
80
- <Space>
81
- <Badge dot status="success">
82
- <Button
83
- type="text"
84
- icon={<ReloadOutlined />}
85
- onClick={handleRefresh}
86
- className="refresh-btn"
87
- >
88
- 刷新
89
- </Button>
90
- </Badge>
91
- </Space>
91
+ <NamespaceSelector
92
+ value={currentNamespace}
93
+ onChange={(namespace) => {
94
+ console.log('🔧 Header收到命名空间切换:', namespace);
95
+ setCurrentNamespace(namespace);
96
+ }}
97
+ style={{ marginLeft: 'auto' }}
98
+ />
92
99
  </div>
100
+
93
101
  </div>
94
102
  </AntHeader>
95
103
  )
@@ -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;