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.
- jettask/core/cli.py +152 -0
- jettask/pg_consumer/sql/add_execution_time_field.sql +29 -0
- jettask/pg_consumer/sql/create_new_tables.sql +137 -0
- jettask/pg_consumer/sql/create_tables_v3.sql +175 -0
- jettask/pg_consumer/sql/migrate_to_new_structure.sql +179 -0
- jettask/pg_consumer/sql/modify_time_fields.sql +69 -0
- jettask/webui/frontend/package.json +30 -0
- jettask/webui/frontend/src/App.css +109 -0
- jettask/webui/frontend/src/App.jsx +66 -0
- jettask/webui/frontend/src/components/NamespaceSelector.jsx +166 -0
- jettask/webui/frontend/src/components/QueueBacklogChart.jsx +298 -0
- jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +638 -0
- jettask/webui/frontend/src/components/QueueDetailsTable.css +65 -0
- jettask/webui/frontend/src/components/QueueDetailsTable.jsx +487 -0
- jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +465 -0
- jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +423 -0
- jettask/webui/frontend/src/components/TaskFilter.jsx +425 -0
- jettask/webui/frontend/src/components/TimeRangeSelector.css +21 -0
- jettask/webui/frontend/src/components/TimeRangeSelector.jsx +160 -0
- jettask/webui/frontend/src/components/charts/QueueChart.jsx +111 -0
- jettask/webui/frontend/src/components/charts/QueueTrendChart.jsx +115 -0
- jettask/webui/frontend/src/components/charts/WorkerChart.jsx +40 -0
- jettask/webui/frontend/src/components/common/StatsCard.jsx +18 -0
- jettask/webui/frontend/src/components/layout/AppLayout.css +95 -0
- jettask/webui/frontend/src/components/layout/AppLayout.jsx +49 -0
- jettask/webui/frontend/src/components/layout/Header.css +106 -0
- jettask/webui/frontend/src/components/layout/Header.jsx +106 -0
- jettask/webui/frontend/src/components/layout/SideMenu.css +137 -0
- jettask/webui/frontend/src/components/layout/SideMenu.jsx +209 -0
- jettask/webui/frontend/src/components/layout/TabsNav.css +244 -0
- jettask/webui/frontend/src/components/layout/TabsNav.jsx +206 -0
- jettask/webui/frontend/src/components/layout/UserInfo.css +197 -0
- jettask/webui/frontend/src/components/layout/UserInfo.jsx +197 -0
- jettask/webui/frontend/src/contexts/LoadingContext.jsx +27 -0
- jettask/webui/frontend/src/contexts/NamespaceContext.jsx +72 -0
- jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +245 -0
- jettask/webui/frontend/src/index.css +114 -0
- jettask/webui/frontend/src/main.jsx +20 -0
- jettask/webui/frontend/src/pages/Alerts.jsx +684 -0
- jettask/webui/frontend/src/pages/Dashboard/index.css +35 -0
- jettask/webui/frontend/src/pages/Dashboard/index.jsx +281 -0
- jettask/webui/frontend/src/pages/Dashboard.jsx +1330 -0
- jettask/webui/frontend/src/pages/QueueDetail.jsx +1117 -0
- jettask/webui/frontend/src/pages/QueueMonitor.jsx +527 -0
- jettask/webui/frontend/src/pages/Queues.jsx +12 -0
- jettask/webui/frontend/src/pages/ScheduledTasks.jsx +809 -0
- jettask/webui/frontend/src/pages/Settings.jsx +800 -0
- jettask/webui/frontend/src/pages/Workers.jsx +12 -0
- jettask/webui/frontend/src/services/api.js +114 -0
- jettask/webui/frontend/src/services/queueTrend.js +152 -0
- jettask/webui/frontend/src/utils/suppressWarnings.js +22 -0
- jettask/webui/frontend/src/utils/userPreferences.js +154 -0
- jettask/webui/frontend/vite.config.js +26 -0
- {jettask-0.2.7.dist-info → jettask-0.2.8.dist-info}/METADATA +1 -1
- {jettask-0.2.7.dist-info → jettask-0.2.8.dist-info}/RECORD +59 -14
- jettask/webui/static/dist/assets/index-7129cfe1.css +0 -1
- jettask/webui/static/dist/assets/index-8d1935cc.js +0 -774
- jettask/webui/static/dist/index.html +0 -15
- jettask/webui/static/index.html +0 -1734
- jettask/webui/static/queue.html +0 -981
- jettask/webui/static/queues.html +0 -549
- jettask/webui/static/workers.html +0 -734
- {jettask-0.2.7.dist-info → jettask-0.2.8.dist-info}/WHEEL +0 -0
- {jettask-0.2.7.dist-info → jettask-0.2.8.dist-info}/entry_points.txt +0 -0
- {jettask-0.2.7.dist-info → jettask-0.2.8.dist-info}/licenses/LICENSE +0 -0
- {jettask-0.2.7.dist-info → jettask-0.2.8.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,298 @@
|
|
1
|
+
import { useState, useEffect, useCallback } from 'react';
|
2
|
+
import { Card, Select, Spin, Empty, Space, Button, message } from 'antd';
|
3
|
+
import { Line } from '@ant-design/plots';
|
4
|
+
import { ReloadOutlined, ExpandOutlined } from '@ant-design/icons';
|
5
|
+
import { useNamespace } from '../contexts/NamespaceContext';
|
6
|
+
import dayjs from 'dayjs';
|
7
|
+
import axios from 'axios';
|
8
|
+
|
9
|
+
const { Option } = Select;
|
10
|
+
|
11
|
+
// 时间范围选项
|
12
|
+
const TIME_RANGES = {
|
13
|
+
'15m': { label: '15分钟', minutes: 15 },
|
14
|
+
'30m': { label: '30分钟', minutes: 30 },
|
15
|
+
'1h': { label: '1小时', minutes: 60 },
|
16
|
+
'3h': { label: '3小时', minutes: 180 },
|
17
|
+
'6h': { label: '6小时', minutes: 360 },
|
18
|
+
'12h': { label: '12小时', minutes: 720 },
|
19
|
+
'24h': { label: '24小时', minutes: 1440 },
|
20
|
+
};
|
21
|
+
|
22
|
+
function QueueBacklogChart({
|
23
|
+
height = 300,
|
24
|
+
showTitle = true,
|
25
|
+
defaultTimeRange = '1h',
|
26
|
+
autoRefresh = true,
|
27
|
+
refreshInterval = 60000, // 默认60秒刷新一次
|
28
|
+
onExpand = null
|
29
|
+
}) {
|
30
|
+
const { currentNamespace } = useNamespace();
|
31
|
+
const [loading, setLoading] = useState(false);
|
32
|
+
const [timeRange, setTimeRange] = useState(defaultTimeRange);
|
33
|
+
const [selectedQueues, setSelectedQueues] = useState([]);
|
34
|
+
const [availableQueues, setAvailableQueues] = useState([]);
|
35
|
+
const [chartData, setChartData] = useState([]);
|
36
|
+
const [lastUpdateTime, setLastUpdateTime] = useState(null);
|
37
|
+
|
38
|
+
// 获取可用队列列表
|
39
|
+
const fetchAvailableQueues = useCallback(async () => {
|
40
|
+
try {
|
41
|
+
const namespace = currentNamespace || 'default';
|
42
|
+
const response = await axios.get(`/api/queues/${namespace}`);
|
43
|
+
if (response.data.success) {
|
44
|
+
const queues = response.data.data.map(q => q.name);
|
45
|
+
setAvailableQueues(queues);
|
46
|
+
|
47
|
+
// 默认选择前5个队列
|
48
|
+
if (selectedQueues.length === 0 && queues.length > 0) {
|
49
|
+
setSelectedQueues(queues.slice(0, 5));
|
50
|
+
}
|
51
|
+
}
|
52
|
+
} catch (error) {
|
53
|
+
console.error('Failed to fetch queues:', error);
|
54
|
+
}
|
55
|
+
}, [currentNamespace, selectedQueues.length]);
|
56
|
+
|
57
|
+
// 获取队列积压数据
|
58
|
+
const fetchBacklogData = useCallback(async () => {
|
59
|
+
if (selectedQueues.length === 0) return;
|
60
|
+
|
61
|
+
setLoading(true);
|
62
|
+
try {
|
63
|
+
const namespace = currentNamespace || 'default';
|
64
|
+
const endTime = dayjs();
|
65
|
+
const startTime = endTime.subtract(TIME_RANGES[timeRange].minutes, 'minute');
|
66
|
+
|
67
|
+
const params = {
|
68
|
+
namespace,
|
69
|
+
queues: selectedQueues,
|
70
|
+
time_range: timeRange,
|
71
|
+
start_time: startTime.toISOString(),
|
72
|
+
end_time: endTime.toISOString(),
|
73
|
+
filters: [] // 不需要额外筛选
|
74
|
+
};
|
75
|
+
|
76
|
+
// 使用队列流量接口获取积压数据
|
77
|
+
const response = await axios.post(`/api/data/queue-flow-rates/${namespace}`, params);
|
78
|
+
|
79
|
+
if (response.data.success) {
|
80
|
+
const { data } = response.data;
|
81
|
+
|
82
|
+
// 转换数据格式,只保留pending数据作为积压量
|
83
|
+
const backlogData = data
|
84
|
+
.filter(item => item.metric === 'pending')
|
85
|
+
.map(item => ({
|
86
|
+
time: dayjs(item.time).format('HH:mm'),
|
87
|
+
timestamp: item.time,
|
88
|
+
queue: item.queue,
|
89
|
+
value: item.value || 0,
|
90
|
+
type: '积压量'
|
91
|
+
}));
|
92
|
+
|
93
|
+
setChartData(backlogData);
|
94
|
+
setLastUpdateTime(dayjs());
|
95
|
+
}
|
96
|
+
} catch (error) {
|
97
|
+
message.error('获取队列积压数据失败');
|
98
|
+
console.error('Failed to fetch backlog data:', error);
|
99
|
+
} finally {
|
100
|
+
setLoading(false);
|
101
|
+
}
|
102
|
+
}, [currentNamespace, selectedQueues, timeRange]);
|
103
|
+
|
104
|
+
// 初始化
|
105
|
+
useEffect(() => {
|
106
|
+
fetchAvailableQueues();
|
107
|
+
}, [fetchAvailableQueues]);
|
108
|
+
|
109
|
+
// 当选中队列变化时,获取数据
|
110
|
+
useEffect(() => {
|
111
|
+
if (selectedQueues.length > 0) {
|
112
|
+
fetchBacklogData();
|
113
|
+
}
|
114
|
+
}, [selectedQueues, fetchBacklogData]);
|
115
|
+
|
116
|
+
// 自动刷新
|
117
|
+
useEffect(() => {
|
118
|
+
if (!autoRefresh || selectedQueues.length === 0) return;
|
119
|
+
|
120
|
+
const timer = setInterval(() => {
|
121
|
+
fetchBacklogData();
|
122
|
+
}, refreshInterval);
|
123
|
+
|
124
|
+
return () => clearInterval(timer);
|
125
|
+
}, [autoRefresh, refreshInterval, fetchBacklogData, selectedQueues.length]);
|
126
|
+
|
127
|
+
// 图表配置
|
128
|
+
const config = {
|
129
|
+
data: chartData,
|
130
|
+
xField: 'time',
|
131
|
+
yField: 'value',
|
132
|
+
seriesField: 'queue',
|
133
|
+
height: height,
|
134
|
+
smooth: true,
|
135
|
+
animation: {
|
136
|
+
appear: {
|
137
|
+
animation: 'wave-in',
|
138
|
+
duration: 1000,
|
139
|
+
},
|
140
|
+
},
|
141
|
+
xAxis: {
|
142
|
+
title: {
|
143
|
+
text: '时间',
|
144
|
+
style: { fontSize: 12 },
|
145
|
+
},
|
146
|
+
label: {
|
147
|
+
autoRotate: true,
|
148
|
+
autoHide: true,
|
149
|
+
},
|
150
|
+
},
|
151
|
+
yAxis: {
|
152
|
+
title: {
|
153
|
+
text: '积压任务数',
|
154
|
+
style: { fontSize: 12 },
|
155
|
+
},
|
156
|
+
min: 0,
|
157
|
+
nice: true,
|
158
|
+
},
|
159
|
+
legend: {
|
160
|
+
position: 'top-right',
|
161
|
+
itemSpacing: 10,
|
162
|
+
},
|
163
|
+
tooltip: {
|
164
|
+
shared: true,
|
165
|
+
showCrosshairs: true,
|
166
|
+
formatter: (datum) => {
|
167
|
+
return {
|
168
|
+
name: datum.queue,
|
169
|
+
value: `${datum.value.toLocaleString()} 个任务`,
|
170
|
+
};
|
171
|
+
},
|
172
|
+
},
|
173
|
+
theme: {
|
174
|
+
colors10: [
|
175
|
+
'#5B8FF9',
|
176
|
+
'#5AD8A6',
|
177
|
+
'#5D7092',
|
178
|
+
'#F6BD16',
|
179
|
+
'#E8684A',
|
180
|
+
'#6DC8EC',
|
181
|
+
'#9270CA',
|
182
|
+
'#FF9D4D',
|
183
|
+
'#269A99',
|
184
|
+
'#FF99C3',
|
185
|
+
],
|
186
|
+
},
|
187
|
+
// 添加告警线(可选)
|
188
|
+
annotations: [
|
189
|
+
{
|
190
|
+
type: 'line',
|
191
|
+
start: ['min', 1000],
|
192
|
+
end: ['max', 1000],
|
193
|
+
style: {
|
194
|
+
stroke: '#ff9800',
|
195
|
+
lineDash: [4, 4],
|
196
|
+
lineWidth: 1,
|
197
|
+
},
|
198
|
+
text: {
|
199
|
+
content: '警告线 (1000)',
|
200
|
+
position: 'end',
|
201
|
+
style: {
|
202
|
+
textAlign: 'end',
|
203
|
+
fontSize: 10,
|
204
|
+
fill: '#ff9800',
|
205
|
+
},
|
206
|
+
offsetY: -5,
|
207
|
+
},
|
208
|
+
},
|
209
|
+
{
|
210
|
+
type: 'line',
|
211
|
+
start: ['min', 5000],
|
212
|
+
end: ['max', 5000],
|
213
|
+
style: {
|
214
|
+
stroke: '#f44336',
|
215
|
+
lineDash: [4, 4],
|
216
|
+
lineWidth: 1,
|
217
|
+
},
|
218
|
+
text: {
|
219
|
+
content: '危险线 (5000)',
|
220
|
+
position: 'end',
|
221
|
+
style: {
|
222
|
+
textAlign: 'end',
|
223
|
+
fontSize: 10,
|
224
|
+
fill: '#f44336',
|
225
|
+
},
|
226
|
+
offsetY: -5,
|
227
|
+
},
|
228
|
+
},
|
229
|
+
],
|
230
|
+
};
|
231
|
+
|
232
|
+
return (
|
233
|
+
<Card
|
234
|
+
title={showTitle ? "队列积压趋势" : null}
|
235
|
+
size="small"
|
236
|
+
extra={
|
237
|
+
<Space>
|
238
|
+
{lastUpdateTime && (
|
239
|
+
<span style={{ fontSize: 12, color: '#999' }}>
|
240
|
+
更新于 {lastUpdateTime.format('HH:mm:ss')}
|
241
|
+
</span>
|
242
|
+
)}
|
243
|
+
<Select
|
244
|
+
value={timeRange}
|
245
|
+
onChange={setTimeRange}
|
246
|
+
style={{ width: 100 }}
|
247
|
+
size="small"
|
248
|
+
>
|
249
|
+
{Object.entries(TIME_RANGES).map(([key, { label }]) => (
|
250
|
+
<Option key={key} value={key}>{label}</Option>
|
251
|
+
))}
|
252
|
+
</Select>
|
253
|
+
<Select
|
254
|
+
mode="multiple"
|
255
|
+
placeholder="选择队列"
|
256
|
+
value={selectedQueues}
|
257
|
+
onChange={setSelectedQueues}
|
258
|
+
style={{ minWidth: 200, maxWidth: 400 }}
|
259
|
+
size="small"
|
260
|
+
maxTagCount={2}
|
261
|
+
maxTagTextLength={10}
|
262
|
+
>
|
263
|
+
{availableQueues.map(queue => (
|
264
|
+
<Option key={queue} value={queue}>{queue}</Option>
|
265
|
+
))}
|
266
|
+
</Select>
|
267
|
+
<Button
|
268
|
+
icon={<ReloadOutlined />}
|
269
|
+
size="small"
|
270
|
+
onClick={fetchBacklogData}
|
271
|
+
loading={loading}
|
272
|
+
/>
|
273
|
+
{onExpand && (
|
274
|
+
<Button
|
275
|
+
icon={<ExpandOutlined />}
|
276
|
+
size="small"
|
277
|
+
onClick={onExpand}
|
278
|
+
title="展开详情"
|
279
|
+
/>
|
280
|
+
)}
|
281
|
+
</Space>
|
282
|
+
}
|
283
|
+
>
|
284
|
+
<Spin spinning={loading}>
|
285
|
+
{chartData.length > 0 ? (
|
286
|
+
<Line {...config} />
|
287
|
+
) : (
|
288
|
+
<Empty
|
289
|
+
description={selectedQueues.length === 0 ? "请选择要监控的队列" : "暂无数据"}
|
290
|
+
style={{ height: height, display: 'flex', alignItems: 'center', justifyContent: 'center', flexDirection: 'column' }}
|
291
|
+
/>
|
292
|
+
)}
|
293
|
+
</Spin>
|
294
|
+
</Card>
|
295
|
+
);
|
296
|
+
}
|
297
|
+
|
298
|
+
export default QueueBacklogChart;
|