jettask 0.2.6__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.6.dist-info → jettask-0.2.8.dist-info}/METADATA +70 -2
  55. {jettask-0.2.6.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.6.dist-info → jettask-0.2.8.dist-info}/WHEEL +0 -0
  64. {jettask-0.2.6.dist-info → jettask-0.2.8.dist-info}/entry_points.txt +0 -0
  65. {jettask-0.2.6.dist-info → jettask-0.2.8.dist-info}/licenses/LICENSE +0 -0
  66. {jettask-0.2.6.dist-info → jettask-0.2.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,69 @@
1
+ -- 修改task_runs表的时间字段,改为存储秒数而不是毫秒
2
+ -- 1. 重命名字段并修改类型
3
+ -- 2. 将已有的毫秒数据转换为秒
4
+
5
+ DO $$
6
+ BEGIN
7
+ -- 处理duration_ms字段
8
+ IF EXISTS (
9
+ SELECT 1 FROM information_schema.columns
10
+ WHERE table_name = 'task_runs' AND column_name = 'duration_ms'
11
+ ) THEN
12
+ -- 如果duration字段不存在,创建它
13
+ IF NOT EXISTS (
14
+ SELECT 1 FROM information_schema.columns
15
+ WHERE table_name = 'task_runs' AND column_name = 'duration'
16
+ ) THEN
17
+ ALTER TABLE task_runs ADD COLUMN duration DOUBLE PRECISION;
18
+ END IF;
19
+
20
+ -- 将毫秒转换为秒
21
+ UPDATE task_runs
22
+ SET duration = duration_ms / 1000.0
23
+ WHERE duration_ms IS NOT NULL AND duration IS NULL;
24
+
25
+ -- 删除旧字段
26
+ ALTER TABLE task_runs DROP COLUMN IF EXISTS duration_ms;
27
+ END IF;
28
+
29
+ -- 处理execution_time_ms字段
30
+ IF EXISTS (
31
+ SELECT 1 FROM information_schema.columns
32
+ WHERE table_name = 'task_runs' AND column_name = 'execution_time_ms'
33
+ ) THEN
34
+ -- 如果execution_time字段不存在,创建它
35
+ IF NOT EXISTS (
36
+ SELECT 1 FROM information_schema.columns
37
+ WHERE table_name = 'task_runs' AND column_name = 'execution_time'
38
+ ) THEN
39
+ ALTER TABLE task_runs ADD COLUMN execution_time DOUBLE PRECISION;
40
+ END IF;
41
+
42
+ -- 将毫秒转换为秒
43
+ UPDATE task_runs
44
+ SET execution_time = execution_time_ms / 1000.0
45
+ WHERE execution_time_ms IS NOT NULL AND execution_time IS NULL;
46
+
47
+ -- 删除旧字段
48
+ ALTER TABLE task_runs DROP COLUMN IF EXISTS execution_time_ms;
49
+ END IF;
50
+
51
+ -- 如果字段已经是正确的格式,确保它们存在
52
+ IF NOT EXISTS (
53
+ SELECT 1 FROM information_schema.columns
54
+ WHERE table_name = 'task_runs' AND column_name = 'duration'
55
+ ) THEN
56
+ ALTER TABLE task_runs ADD COLUMN duration DOUBLE PRECISION;
57
+ END IF;
58
+
59
+ IF NOT EXISTS (
60
+ SELECT 1 FROM information_schema.columns
61
+ WHERE table_name = 'task_runs' AND column_name = 'execution_time'
62
+ ) THEN
63
+ ALTER TABLE task_runs ADD COLUMN execution_time DOUBLE PRECISION;
64
+ END IF;
65
+ END $$;
66
+
67
+ -- 添加注释
68
+ COMMENT ON COLUMN task_runs.duration IS '总耗时(秒),从任务创建到执行完成的时间';
69
+ COMMENT ON COLUMN task_runs.execution_time IS '实际执行时间(秒),从任务开始执行到执行完成的时间';
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "jettask-monitor",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "dependencies": {
6
+ "@ant-design/charts": "^2.6.1",
7
+ "@ant-design/colors": "^7.2.1",
8
+ "@ant-design/cssinjs": "^1.24.0",
9
+ "@ant-design/icons": "^5.0.1",
10
+ "@ant-design/plots": "^2.6.3",
11
+ "@ant-design/pro-components": "^2.8.10",
12
+ "@ant-design/pro-table": "^3.21.0",
13
+ "antd": "^5.4.0",
14
+ "axios": "^1.3.4",
15
+ "dayjs": "^1.11.7",
16
+ "react": "^18.2.0",
17
+ "react-dom": "^18.2.0",
18
+ "react-router-dom": "^6.8.2",
19
+ "uuid": "^11.1.0"
20
+ },
21
+ "devDependencies": {
22
+ "@vitejs/plugin-react": "^3.1.0",
23
+ "vite": "^4.1.4"
24
+ },
25
+ "scripts": {
26
+ "dev": "vite",
27
+ "build": "vite build",
28
+ "preview": "vite preview"
29
+ }
30
+ }
@@ -0,0 +1,109 @@
1
+ /* 全局样式重置 */
2
+ * {
3
+ margin: 0;
4
+ padding: 0;
5
+ box-sizing: border-box;
6
+ }
7
+
8
+ html, body, #root {
9
+ height: 100%;
10
+ width: 100%;
11
+ overflow: hidden;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen',
16
+ 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
17
+ sans-serif;
18
+ -webkit-font-smoothing: antialiased;
19
+ -moz-osx-font-smoothing: grayscale;
20
+ }
21
+
22
+ /* 页面容器样式 */
23
+ .page-container {
24
+ padding: 24px;
25
+ height: 100%;
26
+ overflow: auto;
27
+ background: #f0f2f5;
28
+ }
29
+
30
+ /* 页面内容包装器 - 用于需要padding的页面 */
31
+ .page-wrapper {
32
+ padding: 24px;
33
+ height: 100%;
34
+ overflow: auto;
35
+ background: #f0f2f5;
36
+ }
37
+
38
+ .page-header {
39
+ background: #fff;
40
+ padding: 16px 24px;
41
+ border-radius: 4px;
42
+ margin-bottom: 16px;
43
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
44
+ }
45
+
46
+ .page-title {
47
+ margin: 0;
48
+ font-size: 20px;
49
+ font-weight: 500;
50
+ color: #262626;
51
+ }
52
+
53
+ /* 卡片样式优化 */
54
+ .ant-card {
55
+ border-radius: 4px;
56
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.03);
57
+ }
58
+
59
+ /* 表格样式优化 */
60
+ .ant-table {
61
+ font-size: 13px;
62
+ }
63
+
64
+ .ant-table-thead > tr > th {
65
+ background: #fafafa;
66
+ font-weight: 600;
67
+ }
68
+
69
+ /* 按钮样式优化 */
70
+ .ant-btn {
71
+ border-radius: 4px;
72
+ font-weight: 400;
73
+ }
74
+
75
+ /* 滚动条统一样式 */
76
+ ::-webkit-scrollbar {
77
+ width: 8px;
78
+ height: 8px;
79
+ }
80
+
81
+ ::-webkit-scrollbar-track {
82
+ background: #f0f0f0;
83
+ border-radius: 4px;
84
+ }
85
+
86
+ ::-webkit-scrollbar-thumb {
87
+ background: #bfbfbf;
88
+ border-radius: 4px;
89
+ }
90
+
91
+ ::-webkit-scrollbar-thumb:hover {
92
+ background: #8c8c8c;
93
+ }
94
+
95
+ /* 动画效果 */
96
+ @keyframes fadeIn {
97
+ from {
98
+ opacity: 0;
99
+ transform: translateY(10px);
100
+ }
101
+ to {
102
+ opacity: 1;
103
+ transform: translateY(0);
104
+ }
105
+ }
106
+
107
+ .fade-in {
108
+ animation: fadeIn 0.3s ease-in-out;
109
+ }
@@ -0,0 +1,66 @@
1
+ import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom'
2
+ import { ConfigProvider, theme } from 'antd'
3
+ import zhCN from 'antd/locale/zh_CN'
4
+ import AppLayout from './components/layout/AppLayout'
5
+ import Dashboard from './pages/Dashboard'
6
+ import Queues from './pages/Queues'
7
+ import Workers from './pages/Workers'
8
+ import QueueDetail from './pages/QueueDetail'
9
+ import ScheduledTasks from './pages/ScheduledTasks'
10
+ import Alerts from './pages/Alerts'
11
+ import Settings from './pages/Settings'
12
+ import { LoadingProvider } from './contexts/LoadingContext'
13
+ import { NamespaceProvider } from './contexts/NamespaceContext'
14
+ import './App.css'
15
+
16
+ function App() {
17
+ // 自定义主题
18
+ const customTheme = {
19
+ algorithm: theme.defaultAlgorithm,
20
+ token: {
21
+ colorPrimary: '#1890ff',
22
+ borderRadius: 4,
23
+ colorBgContainer: '#ffffff',
24
+ },
25
+ components: {
26
+ Layout: {
27
+ siderBg: '#1a1d21',
28
+ headerBg: '#2a2d31',
29
+ },
30
+ Menu: {
31
+ darkItemBg: 'transparent',
32
+ darkItemSelectedBg: '#1890ff',
33
+ darkItemHoverBg: 'rgba(24, 144, 255, 0.1)',
34
+ },
35
+ },
36
+ }
37
+
38
+ return (
39
+ <ConfigProvider theme={customTheme} locale={zhCN}>
40
+ <LoadingProvider>
41
+ <NamespaceProvider>
42
+ <Router>
43
+ <Routes>
44
+ <Route path="/" element={<AppLayout />}>
45
+ <Route index element={<Navigate to="/dashboard" replace />} />
46
+ <Route path="dashboard" element={<Dashboard />} />
47
+ <Route path="queues" element={<Queues />} />
48
+ <Route path="queue/:queueName" element={<QueueDetail />} />
49
+ <Route path="scheduled-tasks" element={<ScheduledTasks />} />
50
+ <Route path="alerts" element={<Alerts />} />
51
+ {/* 新增路由 */}
52
+ <Route path="analytics" element={<div style={{ padding: 24 }}>数据分析页面开发中...</div>} />
53
+ <Route path="performance" element={<div style={{ padding: 24 }}>性能监控页面开发中...</div>} />
54
+ <Route path="logs" element={<div style={{ padding: 24 }}>日志查询页面开发中...</div>} />
55
+ <Route path="api-docs" element={<div style={{ padding: 24 }}>API文档页面开发中...</div>} />
56
+ <Route path="settings" element={<Settings />} />
57
+ </Route>
58
+ </Routes>
59
+ </Router>
60
+ </NamespaceProvider>
61
+ </LoadingProvider>
62
+ </ConfigProvider>
63
+ )
64
+ }
65
+
66
+ export default App
@@ -0,0 +1,166 @@
1
+ /**
2
+ * 命名空间选择器组件
3
+ * 放置在页面顶部,用于切换不同的命名空间
4
+ */
5
+ import React, { useState, useEffect } from 'react';
6
+ import { Select, Space, Tag, Tooltip, message, Button, Dropdown, Modal } from 'antd';
7
+ import { DatabaseOutlined, CloudServerOutlined, SettingOutlined, PlusOutlined, EditOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
8
+ import { useNavigate } from 'react-router-dom';
9
+ import { useNamespace } from '../contexts/NamespaceContext';
10
+
11
+ const { Option } = Select;
12
+
13
+ const NamespaceSelector = ({ value, onChange, style }) => {
14
+ const [namespaces, setNamespaces] = useState([]);
15
+ const [loading, setLoading] = useState(false);
16
+ const navigate = useNavigate();
17
+ const { refreshTrigger } = useNamespace(); // 获取刷新触发器
18
+
19
+ // 获取命名空间列表
20
+ const fetchNamespaces = async () => {
21
+ setLoading(true);
22
+ try {
23
+ const response = await fetch('/api/data/namespaces');
24
+ if (response.ok) {
25
+ const data = await response.json();
26
+ setNamespaces(data);
27
+
28
+ // 如果没有选中的命名空间,默认选中第一个
29
+ if (!value && data.length > 0) {
30
+ const firstNamespace = data[0].name;
31
+ if (onChange) {
32
+ onChange(firstNamespace);
33
+ }
34
+ } else if (data.length === 0) {
35
+ // 如果没有任何命名空间,弹出提示并引导到管理页面
36
+ showNoNamespaceModal();
37
+ }
38
+ } else {
39
+ message.error('获取命名空间列表失败');
40
+ }
41
+ } catch (error) {
42
+ console.error('获取命名空间失败:', error);
43
+ message.error('连接任务中心失败');
44
+ } finally {
45
+ setLoading(false);
46
+ }
47
+ };
48
+
49
+ // 组件挂载时获取命名空间列表,以及当refreshTrigger变化时刷新
50
+ useEffect(() => {
51
+ fetchNamespaces();
52
+ }, [refreshTrigger]); // 监听刷新触发器而不是value
53
+
54
+ // 处理命名空间切换
55
+ const handleNamespaceChange = (namespaceName) => {
56
+ console.log('🔧 NamespaceSelector切换命名空间:', namespaceName);
57
+ console.log('🔧 当前props.value:', value);
58
+ console.log('🔧 onChange函数存在:', !!onChange);
59
+
60
+ if (onChange) {
61
+ onChange(namespaceName);
62
+ console.log('🔧 已调用onChange函数');
63
+ }
64
+ message.success(`已切换到命名空间: ${namespaceName}`);
65
+ };
66
+
67
+ // 显示无命名空间提示弹窗
68
+ const showNoNamespaceModal = () => {
69
+ Modal.confirm({
70
+ title: '暂无可用命名空间',
71
+ icon: <ExclamationCircleOutlined />,
72
+ content: '当前系统中没有配置任何命名空间,请先创建一个命名空间。',
73
+ okText: '去管理命名空间',
74
+ cancelText: '取消',
75
+ onOk: () => {
76
+ navigate('/settings');
77
+ },
78
+ });
79
+ };
80
+
81
+ // 获取命名空间标签颜色
82
+ const getNamespaceTagColor = (name) => {
83
+ if (name === 'default') return 'blue';
84
+ if (name.includes('test')) return 'orange';
85
+ if (name.includes('prod')) return 'green';
86
+ if (name.includes('dev')) return 'purple';
87
+ return 'default';
88
+ };
89
+
90
+
91
+ return (
92
+ <Space style={{ ...style }}>
93
+ <span style={{ color: 'rgba(255, 255, 255, 0.65)', fontSize: '14px' }}>
94
+ <DatabaseOutlined style={{ marginRight: '6px' }} />
95
+ 命名空间:
96
+ </span>
97
+
98
+ <Select
99
+ value={value}
100
+ onChange={handleNamespaceChange}
101
+ loading={loading}
102
+ style={{ minWidth: 200 }}
103
+ placeholder="请选择命名空间"
104
+ showSearch
105
+ optionFilterProp="children"
106
+ filterOption={(input, option) =>
107
+ option.children.props.children[1]?.toLowerCase().includes(input.toLowerCase())
108
+ }
109
+ dropdownRender={(menu) => (
110
+ <>
111
+ {menu}
112
+ <div
113
+ style={{
114
+ padding: '8px 12px',
115
+ textAlign: 'center',
116
+ borderTop: '1px solid #f0f0f0',
117
+ cursor: 'pointer'
118
+ }}
119
+ onClick={(e) => {
120
+ e.stopPropagation();
121
+ e.preventDefault();
122
+ console.log('🔧 点击管理命名空间区域');
123
+ navigate('/settings');
124
+ }}
125
+ >
126
+ <SettingOutlined style={{ marginRight: 4 }} />
127
+ 管理命名空间
128
+ </div>
129
+ </>
130
+ )}
131
+ >
132
+ {namespaces.map(ns => (
133
+ <Option key={ns.name} value={ns.name}>
134
+ <Space>
135
+ <Tag color={getNamespaceTagColor(ns.name)} style={{ margin: 0 }}>
136
+ {ns.name}
137
+ </Tag>
138
+ {ns.description && (
139
+ <Tooltip title={ns.description}>
140
+ <span style={{ color: '#999', fontSize: '12px' }}>
141
+ {ns.description.length > 20
142
+ ? `${ns.description.substring(0, 20)}...`
143
+ : ns.description}
144
+ </span>
145
+ </Tooltip>
146
+ )}
147
+ </Space>
148
+ </Option>
149
+ ))}
150
+ </Select>
151
+
152
+ {value && (
153
+ <Tooltip title={`当前命名空间: ${value}`}>
154
+ <CloudServerOutlined
155
+ style={{
156
+ color: '#52c41a',
157
+ fontSize: '16px'
158
+ }}
159
+ />
160
+ </Tooltip>
161
+ )}
162
+ </Space>
163
+ );
164
+ };
165
+
166
+ export default NamespaceSelector;