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