jettask 0.2.15__py3-none-any.whl → 0.2.16__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 (148) hide show
  1. jettask/__init__.py +14 -35
  2. jettask/{webui/__main__.py → __main__.py} +4 -4
  3. jettask/api/__init__.py +103 -0
  4. jettask/api/v1/__init__.py +29 -0
  5. jettask/api/v1/alerts.py +226 -0
  6. jettask/api/v1/analytics.py +323 -0
  7. jettask/api/v1/namespaces.py +134 -0
  8. jettask/api/v1/overview.py +136 -0
  9. jettask/api/v1/queues.py +530 -0
  10. jettask/api/v1/scheduled.py +420 -0
  11. jettask/api/v1/settings.py +44 -0
  12. jettask/{webui/api.py → api.py} +4 -46
  13. jettask/{webui/backend → backend}/main.py +21 -109
  14. jettask/{webui/backend → backend}/main_unified.py +1 -1
  15. jettask/{webui/backend → backend}/namespace_api_old.py +3 -30
  16. jettask/{webui/backend → backend}/namespace_data_access.py +2 -1
  17. jettask/{webui/backend → backend}/unified_api_router.py +14 -74
  18. jettask/{core/cli.py → cli.py} +106 -26
  19. jettask/config/nacos_config.py +386 -0
  20. jettask/core/app.py +8 -100
  21. jettask/core/db_manager.py +515 -0
  22. jettask/core/event_pool.py +5 -2
  23. jettask/core/unified_manager_base.py +47 -14
  24. jettask/{webui/db_init.py → db_init.py} +1 -1
  25. jettask/executors/asyncio.py +2 -2
  26. jettask/{webui/integrated_gradio_app.py → integrated_gradio_app.py} +1 -1
  27. jettask/{webui/multi_namespace_consumer.py → multi_namespace_consumer.py} +5 -2
  28. jettask/{webui/pg_consumer.py → pg_consumer.py} +137 -69
  29. jettask/{webui/run.py → run.py} +1 -1
  30. jettask/{webui/run_webui.py → run_webui.py} +4 -4
  31. jettask/scheduler/multi_namespace_scheduler.py +2 -2
  32. jettask/scheduler/unified_manager.py +5 -5
  33. jettask/scheduler/unified_scheduler_manager.py +1 -1
  34. jettask/schemas/__init__.py +166 -0
  35. jettask/schemas/alert.py +99 -0
  36. jettask/schemas/backlog.py +122 -0
  37. jettask/schemas/common.py +139 -0
  38. jettask/schemas/monitoring.py +181 -0
  39. jettask/schemas/namespace.py +168 -0
  40. jettask/schemas/queue.py +83 -0
  41. jettask/schemas/scheduled_task.py +128 -0
  42. jettask/schemas/task.py +70 -0
  43. jettask/services/__init__.py +24 -0
  44. jettask/services/alert_service.py +454 -0
  45. jettask/services/analytics_service.py +46 -0
  46. jettask/services/overview_service.py +978 -0
  47. jettask/services/queue_service.py +711 -0
  48. jettask/services/redis_monitor_service.py +151 -0
  49. jettask/services/scheduled_task_service.py +207 -0
  50. jettask/services/settings_service.py +758 -0
  51. jettask/services/task_service.py +157 -0
  52. jettask/{webui/task_center.py → task_center.py} +30 -8
  53. jettask/{webui/task_center_client.py → task_center_client.py} +1 -1
  54. jettask/{webui/config.py → webui_config.py} +6 -1
  55. jettask/webui_exceptions.py +67 -0
  56. jettask/webui_sql/verify_database.sql +72 -0
  57. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/METADATA +2 -1
  58. jettask-0.2.16.dist-info/RECORD +150 -0
  59. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/entry_points.txt +1 -1
  60. jettask/webui/backend/data_api.py +0 -3294
  61. jettask/webui/backend/namespace_api.py +0 -295
  62. jettask/webui/backend/queue_backlog_api.py +0 -727
  63. jettask/webui/backend/redis_monitor_api.py +0 -476
  64. jettask/webui/frontend/index.html +0 -13
  65. jettask/webui/frontend/package.json +0 -30
  66. jettask/webui/frontend/src/App.css +0 -109
  67. jettask/webui/frontend/src/App.jsx +0 -66
  68. jettask/webui/frontend/src/components/NamespaceSelector.jsx +0 -166
  69. jettask/webui/frontend/src/components/QueueBacklogChart.jsx +0 -298
  70. jettask/webui/frontend/src/components/QueueBacklogTrend.jsx +0 -638
  71. jettask/webui/frontend/src/components/QueueDetailsTable.css +0 -65
  72. jettask/webui/frontend/src/components/QueueDetailsTable.jsx +0 -487
  73. jettask/webui/frontend/src/components/QueueDetailsTableV2.jsx +0 -465
  74. jettask/webui/frontend/src/components/ScheduledTaskFilter.jsx +0 -423
  75. jettask/webui/frontend/src/components/TaskFilter.jsx +0 -425
  76. jettask/webui/frontend/src/components/TimeRangeSelector.css +0 -21
  77. jettask/webui/frontend/src/components/TimeRangeSelector.jsx +0 -160
  78. jettask/webui/frontend/src/components/charts/QueueChart.jsx +0 -111
  79. jettask/webui/frontend/src/components/charts/QueueTrendChart.jsx +0 -115
  80. jettask/webui/frontend/src/components/charts/WorkerChart.jsx +0 -40
  81. jettask/webui/frontend/src/components/common/StatsCard.jsx +0 -18
  82. jettask/webui/frontend/src/components/layout/AppLayout.css +0 -95
  83. jettask/webui/frontend/src/components/layout/AppLayout.jsx +0 -49
  84. jettask/webui/frontend/src/components/layout/Header.css +0 -106
  85. jettask/webui/frontend/src/components/layout/Header.jsx +0 -106
  86. jettask/webui/frontend/src/components/layout/SideMenu.css +0 -137
  87. jettask/webui/frontend/src/components/layout/SideMenu.jsx +0 -209
  88. jettask/webui/frontend/src/components/layout/TabsNav.css +0 -244
  89. jettask/webui/frontend/src/components/layout/TabsNav.jsx +0 -206
  90. jettask/webui/frontend/src/components/layout/UserInfo.css +0 -197
  91. jettask/webui/frontend/src/components/layout/UserInfo.jsx +0 -197
  92. jettask/webui/frontend/src/contexts/LoadingContext.jsx +0 -27
  93. jettask/webui/frontend/src/contexts/NamespaceContext.jsx +0 -72
  94. jettask/webui/frontend/src/contexts/TabsContext.backup.jsx +0 -245
  95. jettask/webui/frontend/src/index.css +0 -114
  96. jettask/webui/frontend/src/main.jsx +0 -22
  97. jettask/webui/frontend/src/pages/Alerts.jsx +0 -684
  98. jettask/webui/frontend/src/pages/Dashboard/index.css +0 -35
  99. jettask/webui/frontend/src/pages/Dashboard/index.jsx +0 -281
  100. jettask/webui/frontend/src/pages/Dashboard.jsx +0 -1330
  101. jettask/webui/frontend/src/pages/QueueDetail.jsx +0 -1117
  102. jettask/webui/frontend/src/pages/QueueMonitor.jsx +0 -527
  103. jettask/webui/frontend/src/pages/Queues.jsx +0 -12
  104. jettask/webui/frontend/src/pages/ScheduledTasks.jsx +0 -810
  105. jettask/webui/frontend/src/pages/Settings.jsx +0 -801
  106. jettask/webui/frontend/src/pages/Workers.jsx +0 -12
  107. jettask/webui/frontend/src/services/api.js +0 -159
  108. jettask/webui/frontend/src/services/queueTrend.js +0 -166
  109. jettask/webui/frontend/src/utils/suppressWarnings.js +0 -22
  110. jettask/webui/frontend/src/utils/userPreferences.js +0 -154
  111. jettask/webui/frontend/vite.config.js +0 -26
  112. jettask/webui/sql/init_database.sql +0 -640
  113. jettask-0.2.15.dist-info/RECORD +0 -172
  114. /jettask/{webui/backend → backend}/__init__.py +0 -0
  115. /jettask/{webui/backend → backend}/api/__init__.py +0 -0
  116. /jettask/{webui/backend → backend}/api/v1/__init__.py +0 -0
  117. /jettask/{webui/backend → backend}/api/v1/monitoring.py +0 -0
  118. /jettask/{webui/backend → backend}/api/v1/namespaces.py +0 -0
  119. /jettask/{webui/backend → backend}/api/v1/queues.py +0 -0
  120. /jettask/{webui/backend → backend}/api/v1/tasks.py +0 -0
  121. /jettask/{webui/backend → backend}/config.py +0 -0
  122. /jettask/{webui/backend → backend}/core/__init__.py +0 -0
  123. /jettask/{webui/backend → backend}/core/cache.py +0 -0
  124. /jettask/{webui/backend → backend}/core/database.py +0 -0
  125. /jettask/{webui/backend → backend}/core/exceptions.py +0 -0
  126. /jettask/{webui/backend → backend}/data_access.py +0 -0
  127. /jettask/{webui/backend → backend}/dependencies.py +0 -0
  128. /jettask/{webui/backend → backend}/init_meta_db.py +0 -0
  129. /jettask/{webui/backend → backend}/main_v2.py +0 -0
  130. /jettask/{webui/backend → backend}/models/__init__.py +0 -0
  131. /jettask/{webui/backend → backend}/models/requests.py +0 -0
  132. /jettask/{webui/backend → backend}/models/responses.py +0 -0
  133. /jettask/{webui/backend → backend}/queue_stats_v2.py +0 -0
  134. /jettask/{webui/backend → backend}/services/__init__.py +0 -0
  135. /jettask/{webui/backend → backend}/start.py +0 -0
  136. /jettask/{webui/cleanup_deprecated_tables.sql → cleanup_deprecated_tables.sql} +0 -0
  137. /jettask/{webui/gradio_app.py → gradio_app.py} +0 -0
  138. /jettask/{webui/__init__.py → main.py} +0 -0
  139. /jettask/{webui/models.py → models.py} +0 -0
  140. /jettask/{webui/run_monitor.py → run_monitor.py} +0 -0
  141. /jettask/{webui/schema.sql → schema.sql} +0 -0
  142. /jettask/{webui/unified_consumer_manager.py → unified_consumer_manager.py} +0 -0
  143. /jettask/{webui/models → webui_models}/__init__.py +0 -0
  144. /jettask/{webui/models → webui_models}/namespace.py +0 -0
  145. /jettask/{webui/sql → webui_sql}/batch_upsert_functions.sql +0 -0
  146. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/WHEEL +0 -0
  147. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/licenses/LICENSE +0 -0
  148. {jettask-0.2.15.dist-info → jettask-0.2.16.dist-info}/top_level.txt +0 -0
@@ -1,801 +0,0 @@
1
- import { useState, useEffect } from 'react';
2
- import {
3
- Card,
4
- Button,
5
- Table,
6
- Modal,
7
- Form,
8
- Input,
9
- InputNumber,
10
- message,
11
- Space,
12
- Tag,
13
- Popconfirm,
14
- Typography,
15
- Row,
16
- Col,
17
- Descriptions,
18
- Tooltip,
19
- Alert,
20
- Divider,
21
- Select,
22
- Switch
23
- } from 'antd';
24
- import {
25
- PlusOutlined,
26
- EditOutlined,
27
- DeleteOutlined,
28
- CopyOutlined,
29
- DatabaseOutlined,
30
- CloudServerOutlined,
31
- KeyOutlined,
32
- SettingOutlined,
33
- GlobalOutlined,
34
- BellOutlined,
35
- SecurityScanOutlined
36
- } from '@ant-design/icons';
37
- import axios from 'axios';
38
- import { useNamespace } from '../contexts/NamespaceContext';
39
-
40
- const { Title, Text, Paragraph } = Typography;
41
- const { Option } = Select;
42
-
43
- function Settings() {
44
- const { refreshNamespaceList } = useNamespace(); // 获取刷新方法
45
- // 命名空间管理相关状态
46
- const [namespaces, setNamespaces] = useState([]);
47
- const [loading, setLoading] = useState(false);
48
- const [modalVisible, setModalVisible] = useState(false);
49
- const [editingNamespace, setEditingNamespace] = useState(null);
50
- const [form] = Form.useForm();
51
- const [selectedNamespace, setSelectedNamespace] = useState(null);
52
- const [detailModalVisible, setDetailModalVisible] = useState(false);
53
-
54
- // 系统设置相关状态
55
- const [systemSettings, setSystemSettings] = useState({
56
- theme: 'light',
57
- autoRefresh: true,
58
- refreshInterval: 30,
59
- notifications: true,
60
- security: {
61
- sessionTimeout: 120,
62
- enableSSL: false
63
- }
64
- });
65
-
66
- // 获取命名空间列表
67
- const fetchNamespaces = async () => {
68
- setLoading(true);
69
- try {
70
- const response = await axios.get('/api/namespaces');
71
- setNamespaces(response.data);
72
- } catch (error) {
73
- console.error('Failed to fetch namespaces:', error);
74
- message.error('获取命名空间列表失败');
75
- } finally {
76
- setLoading(false);
77
- }
78
- };
79
-
80
- useEffect(() => {
81
- fetchNamespaces();
82
- }, []);
83
-
84
- // 创建或编辑命名空间
85
- const handleSubmit = async () => {
86
- try {
87
- const values = await form.validateFields();
88
-
89
- if (editingNamespace) {
90
- // 编辑模式
91
- const updateData = {
92
- description: values.description,
93
- redis_config: {
94
- host: values.redis_host,
95
- port: values.redis_port,
96
- password: values.redis_password,
97
- db: values.redis_db || 0
98
- },
99
- pg_config: {
100
- host: values.pg_host,
101
- port: values.pg_port,
102
- user: values.pg_user,
103
- password: values.pg_password,
104
- database: values.pg_database
105
- }
106
- };
107
-
108
- const response = await axios.put(
109
- `/api/namespaces/${editingNamespace.name}`,
110
- updateData
111
- );
112
-
113
- message.success('命名空间配置已更新');
114
- setModalVisible(false);
115
- form.resetFields();
116
- setEditingNamespace(null);
117
- fetchNamespaces();
118
- refreshNamespaceList(); // 触发全局命名空间列表刷新
119
-
120
- // 显示更新后的版本信息
121
- Modal.success({
122
- title: '配置更新成功',
123
- content: (
124
- <div>
125
- <p>命名空间 "{editingNamespace.name}" 的配置已成功更新。</p>
126
- <p>新版本号:v{response.data.version}</p>
127
- <Alert
128
- message="版本自动递增"
129
- description="配置更新后,版本号会自动递增。Worker将在下次重启时获取最新配置。"
130
- type="info"
131
- showIcon
132
- />
133
- </div>
134
- ),
135
- width: 600
136
- });
137
- } else {
138
- // 创建新命名空间
139
- const response = await axios.post('/api/namespaces', {
140
- name: values.name,
141
- description: values.description,
142
- redis_config: {
143
- host: values.redis_host,
144
- port: values.redis_port,
145
- password: values.redis_password,
146
- db: values.redis_db || 0
147
- },
148
- pg_config: {
149
- host: values.pg_host,
150
- port: values.pg_port,
151
- user: values.pg_user,
152
- password: values.pg_password,
153
- database: values.pg_database
154
- }
155
- });
156
-
157
- message.success('命名空间创建成功');
158
- setModalVisible(false);
159
- form.resetFields();
160
- fetchNamespaces();
161
- refreshNamespaceList(); // 触发全局命名空间列表刷新
162
-
163
- // 显示连接URL
164
- const apiBase = window.JETTASK_API_URL || 'http://localhost:8001';
165
- const fullUrl = `${apiBase}${response.data.connection_url}`;
166
- Modal.success({
167
- title: '命名空间创建成功',
168
- content: (
169
- <div>
170
- <p>命名空间 "{values.name}" 已成功创建。</p>
171
- <p>连接路径:</p>
172
- <Input.TextArea
173
- value={response.data.connection_url}
174
- readOnly
175
- autoSize={{ minRows: 1, maxRows: 2 }}
176
- />
177
- <Alert
178
- style={{ marginTop: 12, marginBottom: 12 }}
179
- message="使用说明"
180
- description={
181
- <div>
182
- <p>在JetTask应用中使用时,需要添加完整的服务器地址:</p>
183
- <code style={{ background: '#f5f5f5', padding: '4px 8px', borderRadius: 3, display: 'block', marginTop: 8 }}>
184
- task_center_url='http://[服务器地址]:8001{response.data.connection_url}'
185
- </code>
186
- <p style={{ marginTop: 8 }}>示例:</p>
187
- <code style={{ background: '#f5f5f5', padding: '4px 8px', borderRadius: 3, display: 'block' }}>
188
- task_center_url='{fullUrl}'
189
- </code>
190
- </div>
191
- }
192
- type="info"
193
- showIcon
194
- />
195
- <Button
196
- type="primary"
197
- icon={<CopyOutlined />}
198
- onClick={() => {
199
- navigator.clipboard.writeText(fullUrl);
200
- message.success('完整URL已复制到剪贴板');
201
- }}
202
- >
203
- 复制完整URL(本地开发)
204
- </Button>
205
- <Button
206
- type="link"
207
- icon={<CopyOutlined />}
208
- onClick={() => {
209
- navigator.clipboard.writeText(response.data.connection_url);
210
- message.success('路径已复制到剪贴板');
211
- }}
212
- style={{ marginLeft: 8 }}
213
- >
214
- 仅复制路径
215
- </Button>
216
- </div>
217
- ),
218
- width: 700
219
- });
220
- }
221
- } catch (error) {
222
- console.error('Failed to save namespace:', error);
223
- message.error(error.response?.data?.detail || '操作失败');
224
- }
225
- };
226
-
227
- // 删除命名空间
228
- const handleDelete = async (namespace) => {
229
- if (namespace.name === 'default') {
230
- message.error('默认命名空间不能删除');
231
- return;
232
- }
233
-
234
- try {
235
- await axios.delete(`/api/namespaces/${namespace.name}`);
236
- message.success('命名空间已删除');
237
- fetchNamespaces();
238
- refreshNamespaceList(); // 触发全局命名空间列表刷新
239
- } catch (error) {
240
- console.error('Failed to delete namespace:', error);
241
- message.error(error.response?.data?.detail || '删除失败');
242
- }
243
- };
244
-
245
- // 查看命名空间详情
246
- const handleViewDetails = async (namespace) => {
247
- try {
248
- const response = await axios.get(`/api/namespaces/${namespace.name}`);
249
- setSelectedNamespace({
250
- ...namespace,
251
- config: response.data
252
- });
253
- setDetailModalVisible(true);
254
- } catch (error) {
255
- console.error('Failed to fetch namespace config:', error);
256
- message.error('获取命名空间配置失败');
257
- }
258
- };
259
-
260
- // 编辑命名空间
261
- const handleEdit = async (namespace) => {
262
- try {
263
- // 获取最新配置
264
- const response = await axios.get(`/api/namespaces/${namespace.name}`);
265
- const config = response.data;
266
-
267
- // 设置编辑状态
268
- setEditingNamespace(namespace);
269
-
270
- // 填充表单数据
271
- form.setFieldsValue({
272
- name: namespace.name,
273
- description: namespace.description || '',
274
- redis_host: config.redis_config?.host || 'localhost',
275
- redis_port: config.redis_config?.port || 6379,
276
- redis_password: config.redis_config?.password || '',
277
- redis_db: config.redis_config?.db || 0,
278
- pg_host: config.pg_config?.host || 'localhost',
279
- pg_port: config.pg_config?.port || 5432,
280
- pg_user: config.pg_config?.user || 'jettask',
281
- pg_password: config.pg_config?.password || '',
282
- pg_database: config.pg_config?.database || 'jettask'
283
- });
284
-
285
- setModalVisible(true);
286
- } catch (error) {
287
- console.error('Failed to fetch namespace config:', error);
288
- message.error('获取命名空间配置失败');
289
- }
290
- };
291
-
292
- // 系统设置保存
293
- const handleSystemSettingsSave = () => {
294
- message.success('系统设置已保存');
295
- };
296
-
297
- const namespaceColumns = [
298
- {
299
- title: '命名空间名称',
300
- dataIndex: 'name',
301
- key: 'name',
302
- width: 200,
303
- ellipsis: true,
304
- render: (text, record) => (
305
- <Space>
306
- <Tooltip title={text}>
307
- <Text strong style={{ maxWidth: 150, display: 'inline-block' }} ellipsis>{text}</Text>
308
- </Tooltip>
309
- {text === 'default' && <Tag color="blue">默认</Tag>}
310
- </Space>
311
- )
312
- },
313
- {
314
- title: '描述',
315
- dataIndex: 'description',
316
- key: 'description',
317
- ellipsis: true,
318
- render: (text) => text || '-'
319
- },
320
- {
321
- title: '版本',
322
- dataIndex: 'version',
323
- key: 'version',
324
- width: 80,
325
- render: (text) => <Tag color="blue">v{text || 1}</Tag>
326
- },
327
- {
328
- title: '连接路径',
329
- dataIndex: 'connection_url',
330
- key: 'connection_url',
331
- ellipsis: true,
332
- render: (text) => (
333
- <Tooltip title={`完整URL: http://[服务器]:8001${text}`}>
334
- <Text copyable={{ text }} style={{ maxWidth: 300 }} ellipsis>
335
- {text}
336
- </Text>
337
- </Tooltip>
338
- )
339
- },
340
- {
341
- title: '创建时间',
342
- dataIndex: 'created_at',
343
- key: 'created_at',
344
- width: 180,
345
- render: (text) => text ? new Date(text).toLocaleString() : '-'
346
- },
347
- {
348
- title: '操作',
349
- key: 'actions',
350
- width: 250,
351
- render: (_, record) => (
352
- <Space>
353
- <Tooltip title="查看配置">
354
- <Button
355
- type="link"
356
- icon={<DatabaseOutlined />}
357
- onClick={() => handleViewDetails(record)}
358
- />
359
- </Tooltip>
360
- <Tooltip title="复制路径">
361
- <Button
362
- type="link"
363
- icon={<CopyOutlined />}
364
- onClick={() => {
365
- navigator.clipboard.writeText(record.connection_url);
366
- message.success(`路径已复制: ${record.connection_url}`);
367
- }}
368
- />
369
- </Tooltip>
370
- <Tooltip title="编辑配置">
371
- <Button
372
- type="link"
373
- icon={<EditOutlined />}
374
- onClick={() => handleEdit(record)}
375
- />
376
- </Tooltip>
377
- {record.name !== 'default' && (
378
- <Popconfirm
379
- title="确定要删除这个命名空间吗?"
380
- description="删除后无法恢复,请谨慎操作。"
381
- onConfirm={() => handleDelete(record)}
382
- okText="确定"
383
- cancelText="取消"
384
- >
385
- <Tooltip title="删除">
386
- <Button
387
- type="link"
388
- danger
389
- icon={<DeleteOutlined />}
390
- />
391
- </Tooltip>
392
- </Popconfirm>
393
- )}
394
- </Space>
395
- )
396
- }
397
- ];
398
-
399
- return (
400
- <div style={{ padding: 24 }}>
401
- {/* 页面标题 */}
402
- <Title level={2}>
403
- <SettingOutlined /> 系统设置
404
- </Title>
405
- <Text type="secondary" style={{ display: 'block', marginBottom: 24 }}>
406
- 管理系统配置、命名空间、通知和安全设置等
407
- </Text>
408
-
409
- {/* 命名空间管理 */}
410
- <Card style={{ marginBottom: 24 }}>
411
- <Title level={3}>
412
- <GlobalOutlined style={{ marginRight: 8 }} />
413
- 命名空间管理
414
- </Title>
415
- <Text type="secondary" style={{ display: 'block', marginBottom: 16 }}>
416
- 管理多租户命名空间,每个命名空间拥有独立的配置和数据隔离
417
- </Text>
418
-
419
- <Row justify="space-between" align="middle" style={{ marginBottom: 16 }}>
420
- <Col>
421
- <Alert
422
- message="使用说明"
423
- description={
424
- <div style={{ fontSize: '13px' }}>
425
- <Space direction="vertical" size={4} style={{ width: '100%' }}>
426
- <div>
427
- <strong>快速使用:</strong> ① 创建命名空间 → ② 复制连接路径 → ③ 在代码中使用:
428
- <code style={{ background: '#f5f5f5', padding: '2px 6px', borderRadius: 3, marginLeft: 8 }}>
429
- Jettask(task_center_url='http://[服务器]:8001[路径]')
430
- </code>
431
- </div>
432
- <div>
433
- <strong>特性:</strong> 多租户隔离 | 配置热更新 | 版本自动递增 | 默认命名空间保护
434
- </div>
435
- </Space>
436
- </div>
437
- }
438
- type="info"
439
- showIcon
440
- closable
441
- style={{ marginBottom: 16 }}
442
- />
443
- </Col>
444
- <Col>
445
- <Button
446
- type="primary"
447
- icon={<PlusOutlined />}
448
- onClick={() => {
449
- setEditingNamespace(null);
450
- form.resetFields();
451
- setModalVisible(true);
452
- }}
453
- >
454
- 创建命名空间
455
- </Button>
456
- </Col>
457
- </Row>
458
-
459
- <Table
460
- columns={namespaceColumns}
461
- dataSource={namespaces}
462
- rowKey="id"
463
- loading={loading}
464
- pagination={false}
465
- size="small"
466
- />
467
- </Card>
468
-
469
- {/* 系统配置 */}
470
- <Card style={{ marginBottom: 24 }}>
471
- <Title level={3}>
472
- <SettingOutlined style={{ marginRight: 8 }} />
473
- 系统配置
474
- </Title>
475
- <Text type="secondary" style={{ display: 'block', marginBottom: 16 }}>
476
- 配置系统的基本行为和外观设置
477
- </Text>
478
-
479
- <Form layout="vertical" onFinish={handleSystemSettingsSave}>
480
- <Row gutter={24}>
481
- <Col span={12}>
482
- <Form.Item label="主题设置">
483
- <Select
484
- value={systemSettings.theme}
485
- onChange={(value) => setSystemSettings({...systemSettings, theme: value})}
486
- >
487
- <Option value="light">浅色主题</Option>
488
- <Option value="dark">深色主题</Option>
489
- <Option value="auto">跟随系统</Option>
490
- </Select>
491
- </Form.Item>
492
- </Col>
493
- <Col span={12}>
494
- <Form.Item label="自动刷新间隔(秒)">
495
- <InputNumber
496
- value={systemSettings.refreshInterval}
497
- onChange={(value) => setSystemSettings({...systemSettings, refreshInterval: value})}
498
- min={5}
499
- max={300}
500
- style={{ width: '100%' }}
501
- />
502
- </Form.Item>
503
- </Col>
504
- </Row>
505
-
506
- <Row gutter={24}>
507
- <Col span={12}>
508
- <Form.Item label="启用自动刷新">
509
- <Switch
510
- checked={systemSettings.autoRefresh}
511
- onChange={(checked) => setSystemSettings({...systemSettings, autoRefresh: checked})}
512
- />
513
- </Form.Item>
514
- </Col>
515
- <Col span={12}>
516
- <Form.Item label="启用系统通知">
517
- <Switch
518
- checked={systemSettings.notifications}
519
- onChange={(checked) => setSystemSettings({...systemSettings, notifications: checked})}
520
- />
521
- </Form.Item>
522
- </Col>
523
- </Row>
524
- </Form>
525
- </Card>
526
-
527
- {/* 通知设置 */}
528
- <Card style={{ marginBottom: 24 }}>
529
- <Title level={3}>
530
- <BellOutlined style={{ marginRight: 8 }} />
531
- 通知设置
532
- </Title>
533
- <Text type="secondary" style={{ display: 'block', marginBottom: 16 }}>
534
- 配置系统告警和通知方式
535
- </Text>
536
- <Alert
537
- message="功能开发中"
538
- description="通知设置功能正在开发中,敬请期待。"
539
- type="info"
540
- showIcon
541
- />
542
- </Card>
543
-
544
- {/* 安全设置 */}
545
- <Card>
546
- <Title level={3}>
547
- <SecurityScanOutlined style={{ marginRight: 8 }} />
548
- 安全设置
549
- </Title>
550
- <Text type="secondary" style={{ display: 'block', marginBottom: 16 }}>
551
- 配置系统安全策略和访问控制
552
- </Text>
553
-
554
- <Row gutter={24}>
555
- <Col span={12}>
556
- <Form.Item label="会话超时时间(分钟)">
557
- <InputNumber
558
- value={systemSettings.security.sessionTimeout}
559
- onChange={(value) => setSystemSettings({
560
- ...systemSettings,
561
- security: {...systemSettings.security, sessionTimeout: value}
562
- })}
563
- min={15}
564
- max={480}
565
- style={{ width: '100%' }}
566
- />
567
- </Form.Item>
568
- </Col>
569
- <Col span={12}>
570
- <Form.Item label="启用SSL">
571
- <Switch
572
- checked={systemSettings.security.enableSSL}
573
- onChange={(checked) => setSystemSettings({
574
- ...systemSettings,
575
- security: {...systemSettings.security, enableSSL: checked}
576
- })}
577
- />
578
- </Form.Item>
579
- </Col>
580
- </Row>
581
-
582
- <Alert
583
- message="安全提示"
584
- description="建议在生产环境中启用SSL,并设置合理的会话超时时间。"
585
- type="warning"
586
- showIcon
587
- style={{ marginTop: 16 }}
588
- />
589
- </Card>
590
-
591
- {/* 创建/编辑命名空间的模态框 */}
592
- <Modal
593
- title={editingNamespace ? '编辑命名空间' : '创建命名空间'}
594
- open={modalVisible}
595
- onOk={handleSubmit}
596
- onCancel={() => {
597
- setModalVisible(false);
598
- form.resetFields();
599
- setEditingNamespace(null);
600
- }}
601
- width={700}
602
- >
603
- <Form
604
- form={form}
605
- layout="vertical"
606
- initialValues={{
607
- redis_host: 'localhost',
608
- redis_port: 6379,
609
- redis_db: 0,
610
- pg_host: 'localhost',
611
- pg_port: 5432,
612
- pg_user: 'jettask',
613
- pg_database: 'jettask'
614
- }}
615
- >
616
- <Form.Item
617
- name="name"
618
- label="命名空间名称"
619
- rules={[
620
- { required: true, message: '请输入命名空间名称' },
621
- { pattern: /^[a-zA-Z0-9_-]+$/, message: '只能包含字母、数字、下划线和中划线' }
622
- ]}
623
- >
624
- <Input
625
- placeholder="例如:production、dev-team-1"
626
- disabled={!!editingNamespace}
627
- />
628
- </Form.Item>
629
-
630
- <Form.Item
631
- name="description"
632
- label="描述"
633
- >
634
- <Input.TextArea
635
- placeholder="命名空间的描述信息(可选)"
636
- rows={2}
637
- />
638
- </Form.Item>
639
-
640
- <Row gutter={24}>
641
- <Col span={12}>
642
- <Card
643
- title={<Space><CloudServerOutlined /> Redis 配置</Space>}
644
- size="small"
645
- style={{ marginBottom: 16 }}
646
- >
647
- <Form.Item
648
- name="redis_host"
649
- label="主机地址"
650
- rules={[{ required: true, message: '请输入Redis主机地址' }]}
651
- >
652
- <Input placeholder="localhost" />
653
- </Form.Item>
654
- <Form.Item
655
- name="redis_port"
656
- label="端口"
657
- rules={[{ required: true, message: '请输入Redis端口' }]}
658
- >
659
- <InputNumber min={1} max={65535} style={{ width: '100%' }} />
660
- </Form.Item>
661
- <Form.Item
662
- name="redis_password"
663
- label="密码"
664
- >
665
- <Input.Password placeholder="可选" />
666
- </Form.Item>
667
- <Form.Item
668
- name="redis_db"
669
- label="数据库索引"
670
- >
671
- <InputNumber min={0} max={15} style={{ width: '100%' }} />
672
- </Form.Item>
673
- </Card>
674
- </Col>
675
-
676
- <Col span={12}>
677
- <Card
678
- title={<Space><DatabaseOutlined /> PostgreSQL 配置</Space>}
679
- size="small"
680
- style={{ marginBottom: 16 }}
681
- >
682
- <Form.Item
683
- name="pg_host"
684
- label="主机地址"
685
- rules={[{ required: true, message: '请输入PostgreSQL主机地址' }]}
686
- >
687
- <Input placeholder="localhost" />
688
- </Form.Item>
689
- <Form.Item
690
- name="pg_port"
691
- label="端口"
692
- rules={[{ required: true, message: '请输入PostgreSQL端口' }]}
693
- >
694
- <InputNumber min={1} max={65535} style={{ width: '100%' }} />
695
- </Form.Item>
696
- <Form.Item
697
- name="pg_user"
698
- label="用户名"
699
- rules={[{ required: true, message: '请输入用户名' }]}
700
- >
701
- <Input placeholder="jettask" />
702
- </Form.Item>
703
- <Form.Item
704
- name="pg_password"
705
- label="密码"
706
- rules={[{ required: true, message: '请输入密码' }]}
707
- >
708
- <Input.Password />
709
- </Form.Item>
710
- <Form.Item
711
- name="pg_database"
712
- label="数据库名"
713
- rules={[{ required: true, message: '请输入数据库名' }]}
714
- >
715
- <Input placeholder="jettask" />
716
- </Form.Item>
717
- </Card>
718
- </Col>
719
- </Row>
720
- </Form>
721
- </Modal>
722
-
723
- {/* 查看命名空间详情的模态框 */}
724
- <Modal
725
- title={`命名空间详情 - ${selectedNamespace?.name}`}
726
- open={detailModalVisible}
727
- onCancel={() => {
728
- setDetailModalVisible(false);
729
- setSelectedNamespace(null);
730
- }}
731
- footer={null}
732
- width={800}
733
- >
734
- {selectedNamespace && (
735
- <div>
736
- <Descriptions bordered column={1} style={{ marginBottom: 24 }}>
737
- <Descriptions.Item label="命名空间名称">
738
- {selectedNamespace.name}
739
- </Descriptions.Item>
740
- <Descriptions.Item label="描述">
741
- {selectedNamespace.description || '-'}
742
- </Descriptions.Item>
743
- <Descriptions.Item label="版本">
744
- <Tag color="blue">v{selectedNamespace.version || 1}</Tag>
745
- </Descriptions.Item>
746
- <Descriptions.Item label="连接URL">
747
- <Paragraph copyable={{ text: selectedNamespace.connection_url }}>
748
- {selectedNamespace.connection_url}
749
- </Paragraph>
750
- </Descriptions.Item>
751
- <Descriptions.Item label="创建时间">
752
- {selectedNamespace.created_at ? new Date(selectedNamespace.created_at).toLocaleString() : '-'}
753
- </Descriptions.Item>
754
- <Descriptions.Item label="更新时间">
755
- {selectedNamespace.updated_at ? new Date(selectedNamespace.updated_at).toLocaleString() : '-'}
756
- </Descriptions.Item>
757
- </Descriptions>
758
-
759
- {selectedNamespace.config && (
760
- <>
761
- <Title level={5}>Redis 配置</Title>
762
- <Descriptions bordered size="small" style={{ marginBottom: 24 }}>
763
- <Descriptions.Item label="主机" span={2}>
764
- {selectedNamespace.config.redis_config?.host}
765
- </Descriptions.Item>
766
- <Descriptions.Item label="端口">
767
- {selectedNamespace.config.redis_config?.port}
768
- </Descriptions.Item>
769
- <Descriptions.Item label="密码" span={2}>
770
- {selectedNamespace.config.redis_config?.password ? '******' : '未设置'}
771
- </Descriptions.Item>
772
- <Descriptions.Item label="数据库">
773
- {selectedNamespace.config.redis_config?.db || 0}
774
- </Descriptions.Item>
775
- </Descriptions>
776
-
777
- <Title level={5}>PostgreSQL 配置</Title>
778
- <Descriptions bordered size="small">
779
- <Descriptions.Item label="主机" span={2}>
780
- {selectedNamespace.config.pg_config?.host}
781
- </Descriptions.Item>
782
- <Descriptions.Item label="端口">
783
- {selectedNamespace.config.pg_config?.port}
784
- </Descriptions.Item>
785
- <Descriptions.Item label="用户名" span={2}>
786
- {selectedNamespace.config.pg_config?.user}
787
- </Descriptions.Item>
788
- <Descriptions.Item label="数据库">
789
- {selectedNamespace.config.pg_config?.database}
790
- </Descriptions.Item>
791
- </Descriptions>
792
- </>
793
- )}
794
- </div>
795
- )}
796
- </Modal>
797
- </div>
798
- );
799
- }
800
-
801
- export default Settings;