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,12 @@
1
+ import React from 'react';
2
+ import { Card, Empty } from 'antd';
3
+
4
+ function Workers() {
5
+ return (
6
+ <Card title="Workers 管理">
7
+ <Empty description="Workers 功能开发中" />
8
+ </Card>
9
+ );
10
+ }
11
+
12
+ export default Workers;
@@ -0,0 +1,114 @@
1
+ import axios from 'axios';
2
+
3
+ // 智能获取 API 基础 URL
4
+ const getApiBaseUrl = () => {
5
+ // 在开发环境中使用 localhost
6
+ if (typeof window !== 'undefined') {
7
+ const hostname = window.location.hostname;
8
+ const protocol = window.location.protocol;
9
+
10
+ // 如果是生产环境,使用相同的域名
11
+ if (hostname !== 'localhost' && hostname !== '127.0.0.1') {
12
+ return `${protocol}//${hostname}:8001/api`;
13
+ }
14
+ }
15
+
16
+ // 默认开发环境
17
+ return 'http://localhost:8001/api';
18
+ };
19
+
20
+ // 智能获取 WebSocket URL
21
+ const getWsBaseUrl = () => {
22
+ if (typeof window !== 'undefined') {
23
+ const hostname = window.location.hostname;
24
+ const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
25
+
26
+ // 如果是生产环境,使用相同的域名
27
+ if (hostname !== 'localhost' && hostname !== '127.0.0.1') {
28
+ return `${wsProtocol}//${hostname}:8001/ws`;
29
+ }
30
+ }
31
+
32
+ // 默认开发环境
33
+ return 'ws://localhost:8001/ws';
34
+ };
35
+
36
+ const API_BASE_URL = getApiBaseUrl();
37
+ const WS_BASE_URL = getWsBaseUrl();
38
+
39
+ // 创建 axios 实例
40
+ const api = axios.create({
41
+ baseURL: API_BASE_URL,
42
+ timeout: 10000,
43
+ });
44
+
45
+ // 获取全局统计信息
46
+ export const fetchGlobalStats = async () => {
47
+ try {
48
+ const response = await api.get('/stats');
49
+ return response.data.data;
50
+ } catch (error) {
51
+ console.error('Failed to fetch global stats:', error);
52
+ throw error;
53
+ }
54
+ };
55
+
56
+ // 获取队列列表
57
+ export const fetchQueues = async () => {
58
+ try {
59
+ const response = await api.get('/queues');
60
+ return response.data;
61
+ } catch (error) {
62
+ console.error('Failed to fetch queues:', error);
63
+ throw error;
64
+ }
65
+ };
66
+
67
+ // 获取队列时间线数据
68
+ export const fetchQueueTimeline = async (params) => {
69
+ try {
70
+ // 从参数中提取命名空间,如果没有则使用 default
71
+ const namespace = params.namespace || 'default';
72
+ const response = await api.post(`/queue-timeline/${namespace}`, params);
73
+ // 处理数据,确保不包含填充的空值
74
+ const data = response.data.data || response.data || [];
75
+ return {
76
+ ...response.data,
77
+ data: data.filter(item => item.value > 0) // 只返回有实际值的数据点
78
+ };
79
+ } catch (error) {
80
+ console.error('Failed to fetch queue timeline:', error);
81
+ throw error;
82
+ }
83
+ };
84
+
85
+ // WebSocket 连接
86
+ export const connectWebSocket = (onMessage, onError) => {
87
+ const ws = new WebSocket(`${WS_BASE_URL}/monitor`);
88
+
89
+ ws.onopen = () => {
90
+ console.log('WebSocket connected');
91
+ };
92
+
93
+ ws.onmessage = (event) => {
94
+ try {
95
+ const data = JSON.parse(event.data);
96
+ onMessage(data);
97
+ } catch (error) {
98
+ console.error('Failed to parse WebSocket message:', error);
99
+ }
100
+ };
101
+
102
+ ws.onerror = (error) => {
103
+ console.error('WebSocket error:', error);
104
+ if (onError) onError(error);
105
+ };
106
+
107
+ ws.onclose = () => {
108
+ console.log('WebSocket disconnected');
109
+ };
110
+
111
+ return ws;
112
+ };
113
+
114
+ export default api;
@@ -0,0 +1,152 @@
1
+ import axios from 'axios';
2
+
3
+ // 在 React 应用中,环境变量需要以 REACT_APP_ 开头
4
+ // 使用 window.location 作为后备方案
5
+ const getApiBaseUrl = () => {
6
+ // 尝试从环境变量获取(Create React App 会在构建时注入)
7
+ if (typeof window !== 'undefined' && window.REACT_APP_API_URL) {
8
+ return window.REACT_APP_API_URL;
9
+ }
10
+
11
+ // 如果是生产环境,使用相同的域名和端口
12
+ if (typeof window !== 'undefined' && window.location.hostname !== 'localhost') {
13
+ return `${window.location.protocol}//${window.location.hostname}:8001`;
14
+ }
15
+
16
+ // 默认使用 localhost
17
+ return 'http://localhost:8001';
18
+ };
19
+
20
+ const API_BASE_URL = getApiBaseUrl();
21
+
22
+ /**
23
+ * 获取队列趋势数据
24
+ * @param {string} timeRange - 时间范围 (15m, 30m, 1h, 3h, 6h, 12h, 24h, 3d, 7d)
25
+ * @param {Array} queues - 队列名称列表
26
+ * @param {Object} customTimeRange - 自定义时间范围 {start: Date, end: Date}
27
+ * @returns {Promise<Array>} 趋势数据
28
+ */
29
+ export const fetchQueueTrend = async (timeRange = '1h', queues = [], customTimeRange = null) => {
30
+ try {
31
+ const params = {
32
+ range: timeRange,
33
+ queues: queues.join(',')
34
+ };
35
+
36
+ // 如果有自定义时间范围
37
+ if (customTimeRange) {
38
+ params.start = customTimeRange[0].valueOf();
39
+ params.end = customTimeRange[1].valueOf();
40
+ }
41
+
42
+ const response = await axios.get(`${API_BASE_URL}/api/queue-trend`, { params });
43
+
44
+ // 处理响应数据 - 后端现在直接返回时间线数据
45
+ const data = response.data.data || response.data || [];
46
+
47
+ // 转换数据格式,保持时间字段为ISO字符串,让组件转换为Date对象
48
+ return data.map(item => ({
49
+ time: item.time || item.timestamp, // 保持原始时间字符串
50
+ value: item.value || item.count || 0,
51
+ queue: item.queue || item.queue_name
52
+ }));
53
+ } catch (error) {
54
+ console.error('Failed to fetch queue trend:', error);
55
+
56
+ // 如果API失败,返回模拟数据
57
+ return generateMockTrendData(timeRange, queues);
58
+ }
59
+ };
60
+
61
+ /**
62
+ * 生成模拟趋势数据(用于开发和测试)
63
+ */
64
+ const generateMockTrendData = (timeRange, queues) => {
65
+ const now = Date.now();
66
+ const points = getDataPoints(timeRange);
67
+ const interval = getIntervalByRange(timeRange);
68
+
69
+ const data = [];
70
+
71
+ // 为每个队列生成数据
72
+ queues.slice(0, 3).forEach(queue => {
73
+ // 生成基础值和趋势
74
+ const baseValue = Math.floor(Math.random() * 50) + 20;
75
+ const trend = Math.random() > 0.5 ? 1 : -1;
76
+
77
+ for (let i = 0; i < points; i++) {
78
+ // 添加一些随机波动
79
+ const variation = Math.sin(i / 5) * 10 + Math.random() * 10 - 5;
80
+ const value = Math.max(0, baseValue + trend * i * 0.5 + variation);
81
+
82
+ data.push({
83
+ time: now - (points - i - 1) * interval,
84
+ value: Math.floor(value),
85
+ queue: queue.name || queue,
86
+ originalTime: new Date(now - (points - i - 1) * interval).toISOString()
87
+ });
88
+ }
89
+ });
90
+
91
+ return data;
92
+ };
93
+
94
+ /**
95
+ * 根据时间范围获取数据点数量
96
+ */
97
+ const getDataPoints = (range) => {
98
+ const points = {
99
+ '15m': 15, // 每分钟一个点
100
+ '30m': 30, // 每分钟一个点
101
+ '1h': 30, // 每2分钟一个点
102
+ '3h': 36, // 每5分钟一个点
103
+ '6h': 36, // 每10分钟一个点
104
+ '12h': 36, // 每20分钟一个点
105
+ '24h': 48, // 每30分钟一个点
106
+ '3d': 36, // 每2小时一个点
107
+ '7d': 42, // 每4小时一个点
108
+ };
109
+ return points[range] || 30;
110
+ };
111
+
112
+ /**
113
+ * 根据时间范围获取数据间隔(毫秒)
114
+ */
115
+ const getIntervalByRange = (range) => {
116
+ const intervals = {
117
+ '15m': 60 * 1000, // 1分钟
118
+ '30m': 60 * 1000, // 1分钟
119
+ '1h': 2 * 60 * 1000, // 2分钟
120
+ '3h': 5 * 60 * 1000, // 5分钟
121
+ '6h': 10 * 60 * 1000, // 10分钟
122
+ '12h': 20 * 60 * 1000, // 20分钟
123
+ '24h': 30 * 60 * 1000, // 30分钟
124
+ '3d': 2 * 60 * 60 * 1000, // 2小时
125
+ '7d': 4 * 60 * 60 * 1000, // 4小时
126
+ };
127
+ return intervals[range] || 60 * 1000;
128
+ };
129
+
130
+ /**
131
+ * 获取队列统计信息
132
+ */
133
+ export const fetchQueueStats = async (queueName, timeRange = '1h') => {
134
+ try {
135
+ const response = await axios.get(`${API_BASE_URL}/api/queue/${queueName}/stats`, {
136
+ params: { range: timeRange }
137
+ });
138
+
139
+ return response.data;
140
+ } catch (error) {
141
+ console.error('Failed to fetch queue stats:', error);
142
+ return {
143
+ total: 0,
144
+ pending: 0,
145
+ running: 0,
146
+ completed: 0,
147
+ failed: 0,
148
+ avgProcessTime: 0,
149
+ throughput: 0
150
+ };
151
+ }
152
+ };
@@ -0,0 +1,22 @@
1
+ // 在开发环境中临时屏蔽特定的控制台警告
2
+ // 注意:这只是为了减少开发时的控制台噪音,不影响实际功能
3
+
4
+ if (process.env.NODE_ENV === 'development') {
5
+ const originalWarning = console.warn;
6
+ console.warn = (...args) => {
7
+ const warningMessage = args[0]?.toString() || '';
8
+
9
+ // 屏蔽 findDOMNode 相关警告(来自第三方库)
10
+ if (warningMessage.includes('findDOMNode')) {
11
+ return;
12
+ }
13
+
14
+ // 屏蔽 React Router v7 迁移提示
15
+ if (warningMessage.includes('React Router Future Flag Warning')) {
16
+ return;
17
+ }
18
+
19
+ // 其他警告正常显示
20
+ originalWarning.apply(console, args);
21
+ };
22
+ }
@@ -0,0 +1,154 @@
1
+ /**
2
+ * 用户偏好设置管理工具
3
+ */
4
+
5
+ const STORAGE_KEY = 'jettask_user_preferences';
6
+
7
+ /**
8
+ * 获取用户偏好设置
9
+ * @returns {Object} 用户偏好设置对象
10
+ */
11
+ export const getUserPreferences = () => {
12
+ try {
13
+ const stored = localStorage.getItem(STORAGE_KEY);
14
+ return stored ? JSON.parse(stored) : {};
15
+ } catch (error) {
16
+ console.error('Failed to load user preferences:', error);
17
+ return {};
18
+ }
19
+ };
20
+
21
+ /**
22
+ * 保存用户偏好设置
23
+ * @param {Object} preferences - 偏好设置对象
24
+ */
25
+ export const saveUserPreferences = (preferences) => {
26
+ try {
27
+ const current = getUserPreferences();
28
+ console.log('[saveUserPreferences] 当前值:', current);
29
+ console.log('[saveUserPreferences] 要更新的值:', preferences);
30
+ const updated = { ...current, ...preferences };
31
+ console.log('[saveUserPreferences] 合并后的值:', updated);
32
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(updated));
33
+ console.log('[saveUserPreferences] 已保存到 localStorage');
34
+ } catch (error) {
35
+ console.error('Failed to save user preferences:', error);
36
+ }
37
+ };
38
+
39
+ /**
40
+ * 获取特定的偏好设置
41
+ * @param {string} key - 设置项的键
42
+ * @param {*} defaultValue - 默认值
43
+ * @returns {*} 设置值
44
+ */
45
+ export const getPreference = (key, defaultValue = null) => {
46
+ const preferences = getUserPreferences();
47
+ return preferences[key] !== undefined ? preferences[key] : defaultValue;
48
+ };
49
+
50
+ /**
51
+ * 保存特定的偏好设置
52
+ * @param {string} key - 设置项的键
53
+ * @param {*} value - 设置值
54
+ */
55
+ export const setPreference = (key, value) => {
56
+ console.log('[setPreference] 保存偏好设置 - key:', key, 'value:', value);
57
+ saveUserPreferences({ [key]: value });
58
+ // 立即验证保存
59
+ const saved = getUserPreferences();
60
+ console.log('[setPreference] 保存后立即验证 - saved[key]:', saved[key]);
61
+ };
62
+
63
+ /**
64
+ * 清除所有偏好设置
65
+ */
66
+ export const clearPreferences = () => {
67
+ try {
68
+ localStorage.removeItem(STORAGE_KEY);
69
+ } catch (error) {
70
+ console.error('Failed to clear user preferences:', error);
71
+ }
72
+ };
73
+
74
+ // 偏好设置的键
75
+ export const PREFERENCE_KEYS = {
76
+ QUEUE_MONITOR_TIME_RANGE: 'queueMonitor.timeRange',
77
+ QUEUE_MONITOR_SELECTED_QUEUES: 'queueMonitor.selectedQueues',
78
+ QUEUE_MONITOR_CUSTOM_TIME_RANGE: 'queueMonitor.customTimeRange',
79
+ QUEUE_DETAILS_PAGE_SIZE: 'queueDetails.pageSize',
80
+ };
81
+
82
+ /**
83
+ * 获取队列特定的筛选条件
84
+ * @param {string} queueName - 队列名称
85
+ * @returns {Object} 队列的筛选设置
86
+ */
87
+ export const getQueueFilters = (queueName) => {
88
+ const key = `queue.${queueName}.filters`;
89
+ const preferences = getUserPreferences();
90
+ const queueSettings = preferences[key] || {};
91
+
92
+ // 返回默认结构
93
+ return {
94
+ filters: queueSettings.filters || [],
95
+ timeRange: queueSettings.timeRange || '1h',
96
+ customTimeRange: queueSettings.customTimeRange || null
97
+ };
98
+ };
99
+
100
+ /**
101
+ * 保存队列特定的筛选条件
102
+ * @param {string} queueName - 队列名称
103
+ * @param {Object} settings - 筛选设置
104
+ */
105
+ export const saveQueueFilters = (queueName, settings) => {
106
+ const key = `queue.${queueName}.filters`;
107
+ const currentPrefs = getUserPreferences();
108
+
109
+ // 限制存储的队列数量,防止localStorage过大
110
+ const queueKeys = Object.keys(currentPrefs).filter(k => k.startsWith('queue.'));
111
+ if (queueKeys.length > 50) {
112
+ // 删除最旧的队列设置(简单策略:删除第一个)
113
+ delete currentPrefs[queueKeys[0]];
114
+ }
115
+
116
+ // 保存新的设置
117
+ currentPrefs[key] = {
118
+ filters: settings.filters || [],
119
+ timeRange: settings.timeRange || '1h',
120
+ customTimeRange: settings.customTimeRange || null,
121
+ lastUpdated: new Date().toISOString()
122
+ };
123
+
124
+ try {
125
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(currentPrefs));
126
+ } catch (error) {
127
+ console.error('Failed to save queue filters:', error);
128
+ // 如果存储失败(可能是容量问题),清理旧数据
129
+ if (error.name === 'QuotaExceededError') {
130
+ // 清理最旧的一半队列设置
131
+ const halfLength = Math.floor(queueKeys.length / 2);
132
+ for (let i = 0; i < halfLength; i++) {
133
+ delete currentPrefs[queueKeys[i]];
134
+ }
135
+ // 重试保存
136
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(currentPrefs));
137
+ }
138
+ }
139
+ };
140
+
141
+ /**
142
+ * 清除特定队列的筛选设置
143
+ * @param {string} queueName - 队列名称
144
+ */
145
+ export const clearQueueFilters = (queueName) => {
146
+ const key = `queue.${queueName}.filters`;
147
+ const currentPrefs = getUserPreferences();
148
+ delete currentPrefs[key];
149
+ try {
150
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(currentPrefs));
151
+ } catch (error) {
152
+ console.error('Failed to clear queue filters:', error);
153
+ }
154
+ };
@@ -0,0 +1,26 @@
1
+ import { defineConfig } from 'vite'
2
+ import react from '@vitejs/plugin-react'
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ server: {
7
+ port: 3000,
8
+ proxy: {
9
+ '/api': {
10
+ target: 'http://localhost:8001',
11
+ changeOrigin: true,
12
+ },
13
+ '/ws': {
14
+ target: 'ws://localhost:8001',
15
+ ws: true,
16
+ },
17
+ },
18
+ },
19
+ build: {
20
+ outDir: '../static/dist',
21
+ emptyOutDir: true,
22
+ },
23
+ optimizeDeps: {
24
+ include: ['antd', '@ant-design/icons', '@ant-design/plots'],
25
+ },
26
+ })
@@ -1,8 +1,9 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: jettask
3
- Version: 0.2.6
3
+ Version: 0.2.8
4
4
  Summary: A high-performance distributed task queue system with web monitoring
5
5
  Author-email: JetTask Team <support@jettask.io>
6
+ License-Expression: MIT
6
7
  Project-URL: Homepage, https://github.com/yourusername/jettask
7
8
  Project-URL: Bug Tracker, https://github.com/yourusername/jettask/issues
8
9
  Project-URL: Documentation, https://jettask.readthedocs.io
@@ -10,7 +11,6 @@ Classifier: Development Status :: 4 - Beta
10
11
  Classifier: Intended Audience :: Developers
11
12
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
13
  Classifier: Topic :: System :: Distributed Computing
13
- Classifier: License :: OSI Approved :: MIT License
14
14
  Classifier: Programming Language :: Python :: 3
15
15
  Classifier: Programming Language :: Python :: 3.8
16
16
  Classifier: Programming Language :: Python :: 3.9
@@ -47,3 +47,71 @@ Requires-Dist: flake8>=6.0; extra == "dev"
47
47
  Requires-Dist: mypy>=1.4.0; extra == "dev"
48
48
  Requires-Dist: coverage>=7.0; extra == "dev"
49
49
  Dynamic: license-file
50
+
51
+ # JetTask
52
+
53
+ 一个高性能的分布式任务队列系统,支持Web监控界面。
54
+
55
+ ## 特性
56
+
57
+ - 🚀 高性能异步任务执行
58
+ - 📊 实时Web监控界面
59
+ - ⏰ 支持定时任务和延迟任务
60
+ - 🔄 任务重试和错误处理
61
+ - 🎯 多队列和优先级支持
62
+ - 🌍 多命名空间隔离
63
+ - 📈 任务统计和性能监控
64
+ - 🔧 简单易用的API
65
+
66
+ ## 安装
67
+
68
+ ```bash
69
+ pip install jettask
70
+ ```
71
+
72
+ ## 快速开始
73
+
74
+ ### 1. 创建任务
75
+
76
+ ```python
77
+ from jettask import JetTask
78
+
79
+ app = JetTask()
80
+
81
+ @app.task(queue="default")
82
+ async def hello_task(name):
83
+ return f"Hello, {name}!"
84
+ ```
85
+
86
+ ### 2. 启动Worker
87
+
88
+ ```bash
89
+ jettask worker -a app:app --queues default
90
+ ```
91
+
92
+ ### 3. 发送任务
93
+
94
+ ```python
95
+ result = await hello_task.send("World")
96
+ print(result) # Hello, World!
97
+ ```
98
+
99
+ ### 4. 启动Web监控界面
100
+
101
+ ```bash
102
+ # 启动API服务
103
+ jettask api
104
+
105
+ # 启动前端界面
106
+ jettask frontend
107
+ ```
108
+
109
+ 然后访问 http://localhost:3000 查看监控界面。
110
+
111
+ ## 文档
112
+
113
+ 详细文档请参见 [docs/](docs/) 目录。
114
+
115
+ ## 许可证
116
+
117
+ MIT License
@@ -7,7 +7,7 @@ jettask/config/performance.py,sha256=bOdLEskfB_6cRfS10IRgmtKEsJw_CaIZsPHbXxaHwbU
7
7
  jettask/core/__init__.py,sha256=CvBoBCERXCo-jgnkPqAuIgT4uC7oQMnSi7okRxMi6Vc,181
8
8
  jettask/core/app.py,sha256=D6wqdGIVR8E1a7RNevNDaICfKGDsea4RqS9aRlMU810,74906
9
9
  jettask/core/app_importer.py,sha256=B8WiSUz5_O5jlFIBr1CxI_F2gqFYK6ItpONiY_4AiXI,10266
10
- jettask/core/cli.py,sha256=C4OJAhLKjXk7kc9Xg1LyAQh3E7TyD8D7x1aWq_e2mRA,18185
10
+ jettask/core/cli.py,sha256=QG7MOE3m89cQSyHwEiGTzKlFaWHJOFLWqpY6aLdV4Wc,24452
11
11
  jettask/core/consumer_manager.py,sha256=7z3IBvH85YD61ZkVNfOVsuP5NaAwV2Ki8aVz9TCplAM,79870
12
12
  jettask/core/context.py,sha256=XI4Q1s75NIWImcrHIkCLgGo_kpuJIk8MlBoNIJuEfF0,993
13
13
  jettask/core/delay_scanner.py,sha256=rwbIA7SFyOxAV14FW7NB40_djFrrqJJpNkpOri7XcZI,8394
@@ -32,6 +32,11 @@ jettask/monitoring/__init__.py,sha256=xWEEH5C2e8TW2iH6KNxBj5R-aTMg1GH3DrrwjRzRGD
32
32
  jettask/monitoring/file_watcher.py,sha256=r3Mgekb_5sOssDrnFBCbbyvpWkwD2ZPA_j22ztzRCT0,1207
33
33
  jettask/pg_consumer/pg_consumer_v2.py,sha256=ajd6ZM_HY9YWK_I5BNbGuWxNqSS892d3ZzvDlTSQgwc,16211
34
34
  jettask/pg_consumer/sql_utils.py,sha256=dZM_8kIzUmRq0me4yLwb3UeoDvIVKJdIc9VNP1nbS0E,6461
35
+ jettask/pg_consumer/sql/add_execution_time_field.sql,sha256=z2Jx-0uG-IW5RsTGxcLFu2_BcMReluEptMJWB6Qrj08,1184
36
+ jettask/pg_consumer/sql/create_new_tables.sql,sha256=layb08_MFGVSE-NQEcjlQBklZ307JtJaccrnqkcj7Vg,6566
37
+ jettask/pg_consumer/sql/create_tables_v3.sql,sha256=ZwBjxKWU6imWACoWMCIACLsYUPiPep9WmJsdIjI2a58,8173
38
+ jettask/pg_consumer/sql/migrate_to_new_structure.sql,sha256=elhObWvbtqAhb4osJC6D5JhxcOxhGJgIBqx4ItB_xO8,5716
39
+ jettask/pg_consumer/sql/modify_time_fields.sql,sha256=mRfoXbP9iXbAJBj-xNume8-4AV_UPQcBALNUqEf3aKw,2572
35
40
  jettask/scheduler/__init__.py,sha256=egJ-b3VjifFPOFhdEe6JOGyBBNxtqtH4cQvB6Vm_t-c,372
36
41
  jettask/scheduler/add_execution_count.sql,sha256=x4jBsOFGj4JvEd-Ene3OhbR5QCKh5rv8dF01_qLSeE8,383
37
42
  jettask/scheduler/add_priority_field.sql,sha256=z9S10u2D2eekVqB8xOAtDD4Rw1Ey497cmDL4pN2MgB0,915
@@ -108,20 +113,60 @@ jettask/webui/backend/models/requests.py,sha256=fsHiv4sU46biQ1bK1KfV-yxak0LRoFLo
108
113
  jettask/webui/backend/models/responses.py,sha256=FwGpAFNL61a2okJFD41TYLcuwu-41FHXrk6P190jxzw,8528
109
114
  jettask/webui/backend/services/__init__.py,sha256=DCf9NuJo1a65q3CXG0GtRksQpDhg3QuvmTYiWjxGjVE,57
110
115
  jettask/webui/frontend/index.html,sha256=A2PgxT8KBJW15LVnmobQxDNctxsJJc2kA9ej4Jf7yec,367
116
+ jettask/webui/frontend/package.json,sha256=5e-hqT-GhjgXXQT669jYhllJ51Edz1-6wZiZKh_M1bw,723
117
+ jettask/webui/frontend/vite.config.js,sha256=WqgVesX8iqlLFtWq7u4L33hdJXaDqy9EOJJFhKpqNto,517
118
+ jettask/webui/frontend/src/App.css,sha256=TLwSOdKNsUtfAtnTyCxeLZBkRhhw34zWeA8zhEP7dDs,1753
119
+ jettask/webui/frontend/src/App.jsx,sha256=wyTfHb3hp0dThO9fhn4r8JLc9RIkaxnwOieY0o8e7tg,2497
120
+ jettask/webui/frontend/src/index.css,sha256=s2mxkzEDoY10XqRcV_5_32r4GZk22cvZoBMh6_NKVq0,2074
121
+ jettask/webui/frontend/src/main.jsx,sha256=u6wZT3jYm4Ae19rvs7l4kdg8r-k2XKLOdl68DRpfL2U,501
122
+ jettask/webui/frontend/src/components/NamespaceSelector.jsx,sha256=xUIvLPhY_KdUQEHJD7sxim5ZCprk6lK2RRyI3qIs9UQ,5341
123
+ jettask/webui/frontend/src/components/QueueBacklogChart.jsx,sha256=fzwI4QBUN8lBuE89Sf-7fM3kFgH5J8mbDCPyHjkL8ao,8114
124
+ jettask/webui/frontend/src/components/QueueBacklogTrend.jsx,sha256=SPwEeGWDoxemeIrcRqgEA0Yx59VUO4cNvOx_EHCIp_I,21255
125
+ jettask/webui/frontend/src/components/QueueDetailsTable.css,sha256=6d4baJkfPVqjouOkmEt1auqE1lsNr-vfZaMhQpXLQPU,1270
126
+ jettask/webui/frontend/src/components/QueueDetailsTable.jsx,sha256=mL5g0Gu7Q2kFqV796esa7UMG93khcJ2Fv_3esOAImZM,15443
127
+ jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx,sha256=Gw-xKmvcJ9ey2MH2luco5Yu8Mp4X6doOQ_kPuKs1dkg,12956
128
+ jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx,sha256=fE0q62TU_T7tK3zUANwHfvKfZesV54P6B7fgZzEJWXY,13580
129
+ jettask/webui/frontend/src/components/TaskFilter.jsx,sha256=hcm-6-G0IINhU2vgJBSefJ7s2OIzONPsNRU6OI__RfI,13423
130
+ jettask/webui/frontend/src/components/TimeRangeSelector.css,sha256=UZftnjo7tFLV0VBIfdkEvDnKn4NItz0r3iwCzgpesCc,497
131
+ jettask/webui/frontend/src/components/TimeRangeSelector.jsx,sha256=5tJRafL0zJT0SwpH6wXkmOifwphWUvbUhilLoPTVbuo,4861
132
+ jettask/webui/frontend/src/components/charts/QueueChart.jsx,sha256=UHtImnh_zRzMB6znfI8Sh8fIgZS4Py5a8Y2z88wI2fc,2796
133
+ jettask/webui/frontend/src/components/charts/QueueTrendChart.jsx,sha256=riJXsPFpgP_4FpB5Eea3DZVOpA_wpcgSQPnEhLe9oNo,2879
134
+ jettask/webui/frontend/src/components/charts/WorkerChart.jsx,sha256=jek5e97ioLlHcsEIBmoLsCi_j16X2eWgJo95g4sxQIo,901
135
+ jettask/webui/frontend/src/components/common/StatsCard.jsx,sha256=YYT2yZ9vPKq-GZE0NoTCjwvgyTlhoMulX6Zh-q9_j2s,360
136
+ jettask/webui/frontend/src/components/layout/AppLayout.css,sha256=We0la8VcffRxUgijAYrjjKIJYHhzWXG7EvbNNKeNy3A,1456
137
+ jettask/webui/frontend/src/components/layout/AppLayout.jsx,sha256=yaR7VzzHWRgv3Qv3rG3FeggvxhtXFOnnB2990IzF2Ng,1429
138
+ jettask/webui/frontend/src/components/layout/Header.css,sha256=lLkEsTGmKCRcZtJjWj_qOLfzlj-y61_IVf-WAUpRgFU,1730
139
+ jettask/webui/frontend/src/components/layout/Header.jsx,sha256=rC-jGhx201jcv5k-ansOrlVU0toI42TXkXZ88Aqx0vY,2639
140
+ jettask/webui/frontend/src/components/layout/SideMenu.css,sha256=pf_xQuBSfQoh6F-PNE3pZb0gKM2gr-Fg-KjRjivL4Ro,2473
141
+ jettask/webui/frontend/src/components/layout/SideMenu.jsx,sha256=P-mnj_sB-A3mKmCeqUBMq31vVC1v0fTiMOvjnYUs-Yg,5124
142
+ jettask/webui/frontend/src/components/layout/TabsNav.css,sha256=snT4sTHox1lUQg_h7sALyOwJYldlY1LergufgKHYpes,4745
143
+ jettask/webui/frontend/src/components/layout/TabsNav.jsx,sha256=aTapaf0TH9BrHWBFuy2hPcEprA0ClmOMd-8TwznEEL0,5496
144
+ jettask/webui/frontend/src/components/layout/UserInfo.css,sha256=nTUsAtPK4v_yk38BXi5jW3vLgCy2TNKkpkdpnQlj0QI,3241
145
+ jettask/webui/frontend/src/components/layout/UserInfo.jsx,sha256=xHyCN0C4FNXkkDE-j_kdBuLo7OsoinLHsLBFt_uz9Iw,5896
146
+ jettask/webui/frontend/src/contexts/LoadingContext.jsx,sha256=q-Ltc0teW0jt1RJrg2JdiaPiOZXmE9AenVeU8cRI8-M,715
147
+ jettask/webui/frontend/src/contexts/NamespaceContext.jsx,sha256=84iTN_kZfr67eSEHy6QgbFyDYjKXHCdNmwOGkhUI-eo,2348
148
+ jettask/webui/frontend/src/contexts/TabsContext.backup.jsx,sha256=yov_rmnzkBC84YA9pgkOrYk2d_p4srEv1rpc57osMxA,7136
149
+ jettask/webui/frontend/src/pages/Alerts.jsx,sha256=o5fHDHly-sRCR4cXTm0_jDVO-xYRdv4_zTO5bkKZ_Mo,20504
150
+ jettask/webui/frontend/src/pages/Dashboard.jsx,sha256=uMD5hIpgFgapACkz_exQI-tz5u0jA2KQhbuFjrbasBo,46490
151
+ jettask/webui/frontend/src/pages/QueueDetail.jsx,sha256=04LlmiIf5qJiPh6ilTIBb1x9mAQHkza7vA2QeM75Nqo,36054
152
+ jettask/webui/frontend/src/pages/QueueMonitor.jsx,sha256=zp5eR0jBiqZktzsP6NsbTbIo7J7Vr2pwdyozMakB1gE,17749
153
+ jettask/webui/frontend/src/pages/Queues.jsx,sha256=FRE5EMaW3ln_yU9K1_LyFDLgfwmR7jUqIcC_n6mVPVk,201
154
+ jettask/webui/frontend/src/pages/ScheduledTasks.jsx,sha256=vJtSAgJImlom-Qb4xHu3tmQZGJMC2qk8GJnHwReej58,23972
155
+ jettask/webui/frontend/src/pages/Settings.jsx,sha256=9h0K6GJwoRju2GC39kSkThD2lLhcKFWjtpuqGMuT9Bg,26506
156
+ jettask/webui/frontend/src/pages/Workers.jsx,sha256=S3JI-gDfkBuAIeHQdBCyFg8OuwOa970Kz4cgBGqaAmo,227
157
+ jettask/webui/frontend/src/pages/Dashboard/index.css,sha256=DNzBEusK0LtDgcVie_Tg-bcKHs_0EtLUPtqnBfYdERs,511
158
+ jettask/webui/frontend/src/pages/Dashboard/index.jsx,sha256=h-852VjLYcuoDGlkmJ0odrnLU554NsFh332lK99AzWI,7873
159
+ jettask/webui/frontend/src/services/api.js,sha256=ziLbv2lMzSizb-t_1gdHUuYqt30K5K0D1oMJ7gikKS0,2943
160
+ jettask/webui/frontend/src/services/queueTrend.js,sha256=z-oADVYIUD923ilyAn0pVUzfhEmo6OGQ6RHRCvgfWgs,4591
161
+ jettask/webui/frontend/src/utils/suppressWarnings.js,sha256=L2_mnKbkh7_5l0JkLDOxXe6smgMsv7I3sB0ANGrS2JA,678
162
+ jettask/webui/frontend/src/utils/userPreferences.js,sha256=u9VC47XrG0fl0tfx4dY5BBs4ztmGQR0oS-V8LYVw8a4,4685
111
163
  jettask/webui/models/__init__.py,sha256=5cv0oZksj1B3_rzCqsPmF3Gn9NRZLwzMnaJ8bOKGB4I,25
112
164
  jettask/webui/models/namespace.py,sha256=jDj-hZF_-wXzrWAWVDyZVU0JUWDax9apb4Gyykwg-rE,2006
113
165
  jettask/webui/sql/batch_upsert_functions.sql,sha256=5eWOhOD8gWHhtcop_BrCpZTxPFeyBHtt_leNQZO89Cs,6615
114
166
  jettask/webui/sql/init_database.sql,sha256=CSjhBZldtfV0SGBLTFf576ALaRfCFe3wywpezzkX1TQ,22369
115
- jettask/webui/static/index.html,sha256=2XsZQyCmwmgCCHd20ugeRrGQb0jqI1p8YB4yRTsx6ZI,79389
116
- jettask/webui/static/queue.html,sha256=UORFtntu0JQuUslMCxiuPaKxMl4dMzT1HgiUO1eYHno,46774
117
- jettask/webui/static/queues.html,sha256=WkTaKSAydpwjemHMMVx_GTVyLf6Dz2Qy2Yu7UbMLf4E,20807
118
- jettask/webui/static/workers.html,sha256=Tybg1uEcecAv6B5dCCK84deon9shRVBWGiTtqunkJkc,30015
119
- jettask/webui/static/dist/index.html,sha256=h2tFcGYZQ8byRZaLA8_mruZCbzl5mzKFt40iyOUh5s8,454
120
- jettask/webui/static/dist/assets/index-7129cfe1.css,sha256=cSnP4SXPWnF_GNtmJq7QRvF3HMvlp364jfusGRWbSSs,12296
121
- jettask/webui/static/dist/assets/index-8d1935cc.js,sha256=FKAjJHhI3Fz0kROP3JgDI46YvsJxW9zW4eV0nOdLTIQ,3339169
122
- jettask-0.2.6.dist-info/licenses/LICENSE,sha256=sKR8OPWvnqxzcHAmnaVSANpRpeM0Z52PNLCB0ZlFN6c,1062
123
- jettask-0.2.6.dist-info/METADATA,sha256=xlFf9QfXotAGxrh9LmPZ64GSKQWBiWPWdCN3S57c0CU,1954
124
- jettask-0.2.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
125
- jettask-0.2.6.dist-info/entry_points.txt,sha256=VC3byRkkSfRHu_QzczGtpGjcFERkJUlCrD__TFLVqxI,153
126
- jettask-0.2.6.dist-info/top_level.txt,sha256=uymyRUF87-OsSurk5NhpeTW0jy3Wltnn91Zoa6jmAaw,8
127
- jettask-0.2.6.dist-info/RECORD,,
167
+ jettask-0.2.8.dist-info/licenses/LICENSE,sha256=sKR8OPWvnqxzcHAmnaVSANpRpeM0Z52PNLCB0ZlFN6c,1062
168
+ jettask-0.2.8.dist-info/METADATA,sha256=qqiSm1sQjdaDL6zI70A6eFcOZHHEWQGIyfNIuk2cm0o,2955
169
+ jettask-0.2.8.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
170
+ jettask-0.2.8.dist-info/entry_points.txt,sha256=VC3byRkkSfRHu_QzczGtpGjcFERkJUlCrD__TFLVqxI,153
171
+ jettask-0.2.8.dist-info/top_level.txt,sha256=uymyRUF87-OsSurk5NhpeTW0jy3Wltnn91Zoa6jmAaw,8
172
+ jettask-0.2.8.dist-info/RECORD,,