plugin-cluster-manager 1.1.10 → 1.1.13
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.
- package/client-v2.d.ts +2 -0
- package/client-v2.js +1 -0
- package/client.js +1 -0
- package/dist/client/index.js +1 -1
- package/dist/client-v2/914.5dc1105cf3ada6a6.js +10 -0
- package/dist/client-v2/index.js +10 -0
- package/dist/externalVersion.js +6 -5
- package/dist/locale/en-US.json +138 -28
- package/dist/locale/vi-VN.json +139 -28
- package/dist/locale/zh-CN.json +140 -28
- package/dist/server/actions/cache-monitor.js +301 -0
- package/dist/server/actions/cluster-nodes.js +391 -11
- package/dist/server/actions/doctor.js +1246 -0
- package/dist/server/actions/orchestrator.js +37 -0
- package/dist/server/actions/queue-mappings.js +107 -0
- package/dist/server/collections/cluster-manager-doctor-runs.js +52 -0
- package/dist/server/collections/cluster-manager-doctor.js +44 -0
- package/dist/server/collections/worker-queue-mappings.js +106 -0
- package/dist/server/hooks/cacheInvalidationHooks.js +81 -0
- package/dist/server/middlewares/listMetaCacheMiddleware.js +79 -0
- package/dist/server/orchestrator/PackageManager.js +21 -24
- package/dist/server/orchestrator/docker-adapter.js +49 -27
- package/dist/server/plugin.js +71 -16
- package/dist/server/queue-scanner.js +141 -0
- package/dist/server/utils/node.js +30 -2
- package/dist/server/utils/versionManager.js +91 -0
- package/package.json +9 -5
- package/server.js +1 -0
- package/src/client/AclCacheManager.tsx +292 -287
- package/src/client/CacheMonitor.tsx +166 -179
- package/src/client/ClusterManagerLayout.tsx +54 -42
- package/src/client/ClusterNodes.tsx +698 -418
- package/src/client/ContainerOrchestrator.tsx +184 -102
- package/src/client/Doctor.tsx +559 -0
- package/src/client/NginxCacheManager.tsx +415 -0
- package/src/client/PluginOperations.tsx +234 -234
- package/src/client/QueueAssignment.tsx +355 -0
- package/src/client/TaskManager.tsx +194 -187
- package/src/client/WorkflowExecutions.tsx +243 -238
- package/src/client/index.tsx +22 -14
- package/src/client/utils/clientSafeCache.ts +41 -0
- package/src/client/utils/requestDedupInterceptor.ts +213 -0
- package/src/client-v2/plugin.tsx +24 -0
- package/src/locale/en-US.json +138 -28
- package/src/locale/vi-VN.json +139 -28
- package/src/locale/zh-CN.json +140 -28
- package/src/server/__tests__/doctor.test.ts +53 -0
- package/src/server/actions/acl-cache.ts +272 -272
- package/src/server/actions/cache-monitor.ts +453 -116
- package/src/server/actions/cluster-nodes.ts +878 -378
- package/src/server/actions/doctor.ts +1536 -0
- package/src/server/actions/orchestrator.ts +54 -2
- package/src/server/actions/queue-mappings.ts +94 -0
- package/src/server/collections/cluster-manager-doctor-runs.ts +23 -0
- package/src/server/collections/cluster-manager-doctor.ts +19 -0
- package/src/server/collections/worker-queue-mappings.ts +85 -0
- package/src/server/hooks/cacheInvalidationHooks.ts +58 -0
- package/src/server/middlewares/listMetaCacheMiddleware.ts +55 -0
- package/src/server/orchestrator/PackageManager.ts +20 -24
- package/src/server/orchestrator/docker-adapter.ts +74 -37
- package/src/server/plugin.ts +347 -270
- package/src/server/queue-scanner.ts +154 -0
- package/src/server/utils/node.ts +48 -0
- package/src/server/utils/versionManager.ts +69 -0
- package/dist/client/AclCacheManager.d.ts +0 -2
- package/dist/client/CacheMonitor.d.ts +0 -2
- package/dist/client/ClusterManagerLayout.d.ts +0 -2
- package/dist/client/ClusterNodes.d.ts +0 -2
- package/dist/client/ContainerOrchestrator.d.ts +0 -2
- package/dist/client/EventQueueMonitor.d.ts +0 -2
- package/dist/client/LockMonitor.d.ts +0 -2
- package/dist/client/PackageInstaller.d.ts +0 -2
- package/dist/client/PluginOperations.d.ts +0 -2
- package/dist/client/RedisMonitor.d.ts +0 -2
- package/dist/client/TaskManager.d.ts +0 -2
- package/dist/client/WorkflowExecutions.d.ts +0 -2
- package/dist/client/index.d.ts +0 -5
- package/dist/client/utils.d.ts +0 -12
- package/dist/index.d.ts +0 -2
- package/dist/server/actions/acl-cache.d.ts +0 -53
- package/dist/server/actions/cache-monitor.d.ts +0 -23
- package/dist/server/actions/cluster-nodes.d.ts +0 -49
- package/dist/server/actions/event-queue-monitor.d.ts +0 -13
- package/dist/server/actions/lock-monitor.d.ts +0 -19
- package/dist/server/actions/orchestrator.d.ts +0 -58
- package/dist/server/actions/package-manager.d.ts +0 -6
- package/dist/server/actions/plugin-operations.d.ts +0 -6
- package/dist/server/actions/redis-monitor.d.ts +0 -12
- package/dist/server/actions/tasks.d.ts +0 -7
- package/dist/server/actions/workflow-executions.d.ts +0 -7
- package/dist/server/adapters/redis-lock-adapter.d.ts +0 -15
- package/dist/server/adapters/redis-node-registry.d.ts +0 -12
- package/dist/server/adapters/redis-pubsub-adapter.d.ts +0 -16
- package/dist/server/collections/app.d.ts +0 -8
- package/dist/server/collections/cluster-manager-acl-cache.d.ts +0 -22
- package/dist/server/collections/cluster-manager-cache-mgr.d.ts +0 -22
- package/dist/server/collections/cluster-manager-cluster.d.ts +0 -22
- package/dist/server/collections/cluster-manager-lock.d.ts +0 -22
- package/dist/server/collections/cluster-manager-plugins.d.ts +0 -18
- package/dist/server/collections/cluster-manager-queue.d.ts +0 -22
- package/dist/server/collections/cluster-manager-redis.d.ts +0 -22
- package/dist/server/collections/cluster-manager-workflow.d.ts +0 -22
- package/dist/server/collections/cluster-manager.d.ts +0 -22
- package/dist/server/collections/orchestrator-settings.d.ts +0 -59
- package/dist/server/collections/orchestrator-stacks.d.ts +0 -102
- package/dist/server/collections/worker-orchestrator.d.ts +0 -22
- package/dist/server/collections/worker-packages-configs.d.ts +0 -3
- package/dist/server/collections/worker-packages.d.ts +0 -22
- package/dist/server/orchestrator/PackageManager.d.ts +0 -39
- package/dist/server/orchestrator/docker-adapter.d.ts +0 -41
- package/dist/server/orchestrator/index.d.ts +0 -4
- package/dist/server/orchestrator/k8s-adapter.d.ts +0 -50
- package/dist/server/orchestrator/leader-election.d.ts +0 -48
- package/dist/server/orchestrator/types.d.ts +0 -84
- package/dist/server/plugin.d.ts +0 -26
- package/dist/server/utils/node.d.ts +0 -6
- package/dist/server/utils/redis.d.ts +0 -29
- package/dist/shared/packages.d.ts +0 -23
- /package/{dist/server/index.d.ts → src/client-v2/index.tsx} +0 -0
|
@@ -1,6 +1,41 @@
|
|
|
1
1
|
import React, { useState, useEffect, useCallback } from 'react';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
Card,
|
|
4
|
+
Table,
|
|
5
|
+
Button,
|
|
6
|
+
Space,
|
|
7
|
+
Tag,
|
|
8
|
+
InputNumber,
|
|
9
|
+
Popconfirm,
|
|
10
|
+
message,
|
|
11
|
+
Modal,
|
|
12
|
+
Typography,
|
|
13
|
+
Alert,
|
|
14
|
+
Spin,
|
|
15
|
+
Tooltip,
|
|
16
|
+
Badge,
|
|
17
|
+
Row,
|
|
18
|
+
Col,
|
|
19
|
+
Form,
|
|
20
|
+
Input,
|
|
21
|
+
Select,
|
|
22
|
+
Switch,
|
|
23
|
+
} from 'antd';
|
|
24
|
+
import {
|
|
25
|
+
PlayCircleOutlined,
|
|
26
|
+
PauseCircleOutlined,
|
|
27
|
+
DeleteOutlined,
|
|
28
|
+
FileTextOutlined,
|
|
29
|
+
ReloadOutlined,
|
|
30
|
+
ExpandAltOutlined,
|
|
31
|
+
ShrinkOutlined,
|
|
32
|
+
CloudServerOutlined,
|
|
33
|
+
ApiOutlined,
|
|
34
|
+
SettingOutlined,
|
|
35
|
+
SaveOutlined,
|
|
36
|
+
PlusOutlined,
|
|
37
|
+
EditOutlined,
|
|
38
|
+
} from '@ant-design/icons';
|
|
4
39
|
import { useAPIClient } from '@nocobase/client';
|
|
5
40
|
import { useT } from './utils';
|
|
6
41
|
|
|
@@ -131,7 +166,7 @@ function stackToFormValues(stack?: StackInfo | null) {
|
|
|
131
166
|
return {
|
|
132
167
|
name: 'app-workers',
|
|
133
168
|
adapter: 'kubernetes',
|
|
134
|
-
image: 'nocobase/nocobase:2.
|
|
169
|
+
image: 'nocobase/nocobase:2.1.6-full',
|
|
135
170
|
command: DEFAULT_WORKER_COMMAND,
|
|
136
171
|
envVars: jsonText(DEFAULT_WORKER_ENV, {}),
|
|
137
172
|
resourceLimits: jsonText({ memory: '1536Mi' }, {}),
|
|
@@ -184,10 +219,14 @@ export function ContainerOrchestrator() {
|
|
|
184
219
|
const [containerMeta, setContainerMeta] = useState<Record<number, any>>({});
|
|
185
220
|
const [scaleValues, setScaleValues] = useState<Record<number, number>>({});
|
|
186
221
|
const [logModal, setLogModal] = useState<{ visible: boolean; containerId: string; logs: string[] }>({
|
|
187
|
-
visible: false,
|
|
222
|
+
visible: false,
|
|
223
|
+
containerId: '',
|
|
224
|
+
logs: [],
|
|
188
225
|
});
|
|
189
226
|
const [pkgLogModal, setPkgLogModal] = useState<{ visible: boolean; containerName: string; logs: string }>({
|
|
190
|
-
visible: false,
|
|
227
|
+
visible: false,
|
|
228
|
+
containerName: '',
|
|
229
|
+
logs: '',
|
|
191
230
|
});
|
|
192
231
|
const [pingResult, setPingResult] = useState<any>(null);
|
|
193
232
|
const [settingsModal, setSettingsModal] = useState(false);
|
|
@@ -197,7 +236,7 @@ export function ContainerOrchestrator() {
|
|
|
197
236
|
});
|
|
198
237
|
const [settingsForm] = Form.useForm();
|
|
199
238
|
const [stackForm] = Form.useForm();
|
|
200
|
-
const [dockerNetworks, setDockerNetworks] = useState<{id: string
|
|
239
|
+
const [dockerNetworks, setDockerNetworks] = useState<{ id: string; name: string }[]>([]);
|
|
201
240
|
|
|
202
241
|
// Fetch Docker networks
|
|
203
242
|
const fetchNetworks = useCallback(async () => {
|
|
@@ -229,7 +268,9 @@ export function ContainerOrchestrator() {
|
|
|
229
268
|
setStacks(data);
|
|
230
269
|
// Init scale values
|
|
231
270
|
const sv: Record<number, number> = {};
|
|
232
|
-
data.forEach((s: StackInfo) => {
|
|
271
|
+
data.forEach((s: StackInfo) => {
|
|
272
|
+
sv[s.id] = s.desiredReplicas;
|
|
273
|
+
});
|
|
233
274
|
setScaleValues(sv);
|
|
234
275
|
} catch {
|
|
235
276
|
// Collection might not exist yet
|
|
@@ -237,26 +278,31 @@ export function ContainerOrchestrator() {
|
|
|
237
278
|
}, [api]);
|
|
238
279
|
|
|
239
280
|
// Load containers for a specific stack
|
|
240
|
-
const fetchContainers = useCallback(
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
281
|
+
const fetchContainers = useCallback(
|
|
282
|
+
async (stackId: number) => {
|
|
283
|
+
try {
|
|
284
|
+
const res = await api.request({
|
|
285
|
+
url: '/workerOrchestrator:containers',
|
|
286
|
+
params: { stackId },
|
|
287
|
+
});
|
|
288
|
+
const data = res.data?.data || res.data;
|
|
289
|
+
setContainers((prev) => ({ ...prev, [stackId]: data.data || [] }));
|
|
290
|
+
setContainerMeta((prev) => ({ ...prev, [stackId]: data.meta || {} }));
|
|
291
|
+
} catch (err: any) {
|
|
292
|
+
message.error(`Failed to load containers: ${err.message}`);
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
[api],
|
|
296
|
+
);
|
|
253
297
|
|
|
254
298
|
// Load settings
|
|
255
299
|
const fetchSettings = useCallback(async () => {
|
|
256
300
|
try {
|
|
257
301
|
const res = await api.request({ url: '/workerOrchestrator:getSettings' });
|
|
258
302
|
settingsForm.setFieldsValue(res.data?.data || res.data);
|
|
259
|
-
} catch {
|
|
303
|
+
} catch {
|
|
304
|
+
// Settings not yet available — leave form at defaults
|
|
305
|
+
}
|
|
260
306
|
}, [api, settingsForm]);
|
|
261
307
|
|
|
262
308
|
// Save settings
|
|
@@ -332,12 +378,12 @@ export function ContainerOrchestrator() {
|
|
|
332
378
|
stacks.forEach((s) => fetchContainers(s.id));
|
|
333
379
|
};
|
|
334
380
|
doFetch();
|
|
335
|
-
|
|
381
|
+
|
|
336
382
|
const interval = setInterval(() => {
|
|
337
383
|
fetchStacks(); // refresh stack info
|
|
338
384
|
doFetch(); // refresh containers
|
|
339
385
|
}, 10000);
|
|
340
|
-
|
|
386
|
+
|
|
341
387
|
return () => clearInterval(interval);
|
|
342
388
|
}, [stacks, fetchContainers, fetchStacks]);
|
|
343
389
|
|
|
@@ -353,9 +399,7 @@ export function ContainerOrchestrator() {
|
|
|
353
399
|
data: { stackId, replicas },
|
|
354
400
|
});
|
|
355
401
|
const result = res.data?.data || res.data;
|
|
356
|
-
message.success(
|
|
357
|
-
`Scaled: ${result.previousReplicas} → ${result.currentReplicas} replicas`,
|
|
358
|
-
);
|
|
402
|
+
message.success(`Scaled: ${result.previousReplicas} → ${result.currentReplicas} replicas`);
|
|
359
403
|
await fetchContainers(stackId);
|
|
360
404
|
await fetchStacks();
|
|
361
405
|
} catch (err: any) {
|
|
@@ -405,7 +449,9 @@ export function ContainerOrchestrator() {
|
|
|
405
449
|
key: 'name',
|
|
406
450
|
render: (name: string, record: ContainerInfo) => (
|
|
407
451
|
<Space>
|
|
408
|
-
<Text code style={{ fontSize: 11 }}>
|
|
452
|
+
<Text code style={{ fontSize: 11 }}>
|
|
453
|
+
{record.id}
|
|
454
|
+
</Text>
|
|
409
455
|
<Text>{name}</Text>
|
|
410
456
|
</Space>
|
|
411
457
|
),
|
|
@@ -415,9 +461,7 @@ export function ContainerOrchestrator() {
|
|
|
415
461
|
dataIndex: 'status',
|
|
416
462
|
key: 'status',
|
|
417
463
|
width: 100,
|
|
418
|
-
render: (status: string) => (
|
|
419
|
-
<Tag color={STATUS_COLORS[status] || 'default'}>{status.toUpperCase()}</Tag>
|
|
420
|
-
),
|
|
464
|
+
render: (status: string) => <Tag color={STATUS_COLORS[status] || 'default'}>{status.toUpperCase()}</Tag>,
|
|
421
465
|
},
|
|
422
466
|
{
|
|
423
467
|
title: t('Packages'),
|
|
@@ -434,10 +478,16 @@ export function ContainerOrchestrator() {
|
|
|
434
478
|
<Tag
|
|
435
479
|
color={color}
|
|
436
480
|
style={{ cursor: 'pointer' }}
|
|
437
|
-
onClick={() =>
|
|
481
|
+
onClick={() =>
|
|
482
|
+
setPkgLogModal({ visible: true, containerName: record.name, logs: pStatus.lastInitLog || '' })
|
|
483
|
+
}
|
|
438
484
|
>
|
|
439
485
|
{pStatus.initStatus === 'running' && <ReloadOutlined spin style={{ marginRight: 4 }} />}
|
|
440
|
-
{pStatus.initStatus === 'succeeded'
|
|
486
|
+
{pStatus.initStatus === 'succeeded'
|
|
487
|
+
? 'Installed'
|
|
488
|
+
: pStatus.initStatus === 'failed'
|
|
489
|
+
? 'Failed'
|
|
490
|
+
: `${pStatus.initProgressPercent || 0}%`}
|
|
441
491
|
</Tag>
|
|
442
492
|
</Tooltip>
|
|
443
493
|
);
|
|
@@ -448,14 +498,18 @@ export function ContainerOrchestrator() {
|
|
|
448
498
|
dataIndex: 'image',
|
|
449
499
|
key: 'image',
|
|
450
500
|
ellipsis: true,
|
|
451
|
-
render: (img: string) =>
|
|
501
|
+
render: (img: string) => (
|
|
502
|
+
<Text type="secondary" style={{ fontSize: 12 }}>
|
|
503
|
+
{img}
|
|
504
|
+
</Text>
|
|
505
|
+
),
|
|
452
506
|
},
|
|
453
507
|
{
|
|
454
508
|
title: t('Age'),
|
|
455
509
|
dataIndex: 'createdAt',
|
|
456
510
|
key: 'age',
|
|
457
511
|
width: 80,
|
|
458
|
-
render: (dt: string) => dt ? formatAge(dt) : '-',
|
|
512
|
+
render: (dt: string) => (dt ? formatAge(dt) : '-'),
|
|
459
513
|
},
|
|
460
514
|
{
|
|
461
515
|
title: t('Actions'),
|
|
@@ -464,46 +518,56 @@ export function ContainerOrchestrator() {
|
|
|
464
518
|
render: (_: any, record: ContainerInfo & { _stackId?: number }) => {
|
|
465
519
|
const isK8s = pingResult?.adapter === 'kubernetes';
|
|
466
520
|
return (
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
<Tooltip title={t('Remove')}>
|
|
496
|
-
<Button size="small" type="text" danger icon={<DeleteOutlined />} />
|
|
521
|
+
<Space size="small">
|
|
522
|
+
{!isK8s &&
|
|
523
|
+
(record.status === 'running' ? (
|
|
524
|
+
<Tooltip title={t('Stop')}>
|
|
525
|
+
<Button
|
|
526
|
+
size="small"
|
|
527
|
+
type="text"
|
|
528
|
+
icon={<PauseCircleOutlined />}
|
|
529
|
+
onClick={() => handleAction('stop', record.id, record._stackId!)}
|
|
530
|
+
/>
|
|
531
|
+
</Tooltip>
|
|
532
|
+
) : (
|
|
533
|
+
<Tooltip title={t('Start')}>
|
|
534
|
+
<Button
|
|
535
|
+
size="small"
|
|
536
|
+
type="text"
|
|
537
|
+
icon={<PlayCircleOutlined style={{ color: '#52c41a' }} />}
|
|
538
|
+
onClick={() => handleAction('start', record.id, record._stackId!)}
|
|
539
|
+
/>
|
|
540
|
+
</Tooltip>
|
|
541
|
+
))}
|
|
542
|
+
<Tooltip title={t('Logs')}>
|
|
543
|
+
<Button
|
|
544
|
+
size="small"
|
|
545
|
+
type="text"
|
|
546
|
+
icon={<FileTextOutlined />}
|
|
547
|
+
onClick={() => handleViewLogs(record.id, record._stackId!)}
|
|
548
|
+
/>
|
|
497
549
|
</Tooltip>
|
|
498
|
-
|
|
499
|
-
|
|
550
|
+
<Popconfirm
|
|
551
|
+
title={t('Remove this container?')}
|
|
552
|
+
onConfirm={() => handleAction('remove', record.id, record._stackId!)}
|
|
553
|
+
>
|
|
554
|
+
<Tooltip title={t('Remove')}>
|
|
555
|
+
<Button size="small" type="text" danger icon={<DeleteOutlined />} />
|
|
556
|
+
</Tooltip>
|
|
557
|
+
</Popconfirm>
|
|
558
|
+
</Space>
|
|
500
559
|
);
|
|
501
560
|
},
|
|
502
561
|
},
|
|
503
562
|
];
|
|
504
563
|
|
|
505
564
|
if (!pingResult) {
|
|
506
|
-
return
|
|
565
|
+
return (
|
|
566
|
+
<Spin
|
|
567
|
+
tip={t('Connecting to orchestrator...')}
|
|
568
|
+
style={{ display: 'flex', justifyContent: 'center', padding: 48 }}
|
|
569
|
+
/>
|
|
570
|
+
);
|
|
507
571
|
}
|
|
508
572
|
|
|
509
573
|
return (
|
|
@@ -516,33 +580,35 @@ export function ContainerOrchestrator() {
|
|
|
516
580
|
<ApiOutlined style={{ fontSize: 18 }} />
|
|
517
581
|
<Text strong>{t('Adapter')}: </Text>
|
|
518
582
|
<Tag color="blue">{pingResult.adapter?.toUpperCase() || 'NONE'}</Tag>
|
|
519
|
-
<Badge
|
|
583
|
+
<Badge
|
|
584
|
+
status={pingResult.connected ? 'success' : 'error'}
|
|
585
|
+
text={pingResult.connected ? t('Connected') : t('Disconnected')}
|
|
586
|
+
/>
|
|
520
587
|
{pingResult.leader !== undefined && (
|
|
521
588
|
<>
|
|
522
589
|
<Text type="secondary">|</Text>
|
|
523
|
-
<Badge
|
|
590
|
+
<Badge
|
|
591
|
+
status={pingResult.leader ? 'success' : 'default'}
|
|
592
|
+
text={pingResult.leader ? t('Leader (this node)') : t('Follower')}
|
|
593
|
+
/>
|
|
524
594
|
</>
|
|
525
595
|
)}
|
|
526
596
|
</Space>
|
|
527
597
|
</Col>
|
|
528
598
|
<Col>
|
|
529
599
|
<Space>
|
|
530
|
-
<Button
|
|
531
|
-
type="primary"
|
|
532
|
-
icon={<PlusOutlined />}
|
|
533
|
-
onClick={() => openStackModal()}
|
|
534
|
-
>
|
|
600
|
+
<Button type="primary" icon={<PlusOutlined />} onClick={() => openStackModal()}>
|
|
535
601
|
{t('New worker stack')}
|
|
536
602
|
</Button>
|
|
537
|
-
<Button
|
|
538
|
-
icon={<SettingOutlined />}
|
|
539
|
-
onClick={() => setSettingsModal(true)}
|
|
540
|
-
>
|
|
603
|
+
<Button icon={<SettingOutlined />} onClick={() => setSettingsModal(true)}>
|
|
541
604
|
{t('Settings')}
|
|
542
605
|
</Button>
|
|
543
606
|
<Button
|
|
544
607
|
icon={<ReloadOutlined />}
|
|
545
|
-
onClick={() => {
|
|
608
|
+
onClick={() => {
|
|
609
|
+
fetchPing();
|
|
610
|
+
fetchStacks();
|
|
611
|
+
}}
|
|
546
612
|
>
|
|
547
613
|
{t('Refresh')}
|
|
548
614
|
</Button>
|
|
@@ -577,8 +643,8 @@ export function ContainerOrchestrator() {
|
|
|
577
643
|
/>
|
|
578
644
|
)}
|
|
579
645
|
|
|
580
|
-
{stacks.map(stack => {
|
|
581
|
-
const stackContainers = (containers[stack.id] || []).map(c => ({ ...c, _stackId: stack.id }));
|
|
646
|
+
{stacks.map((stack) => {
|
|
647
|
+
const stackContainers = (containers[stack.id] || []).map((c) => ({ ...c, _stackId: stack.id }));
|
|
582
648
|
const meta = containerMeta[stack.id] || {};
|
|
583
649
|
|
|
584
650
|
return (
|
|
@@ -593,7 +659,9 @@ export function ContainerOrchestrator() {
|
|
|
593
659
|
}
|
|
594
660
|
extra={
|
|
595
661
|
<Space>
|
|
596
|
-
<Text type="secondary" style={{ fontSize: 12 }}>
|
|
662
|
+
<Text type="secondary" style={{ fontSize: 12 }}>
|
|
663
|
+
{stack.image}
|
|
664
|
+
</Text>
|
|
597
665
|
<Tooltip title={t('Edit worker stack')}>
|
|
598
666
|
<Button size="small" type="text" icon={<EditOutlined />} onClick={() => openStackModal(stack)} />
|
|
599
667
|
</Tooltip>
|
|
@@ -608,20 +676,24 @@ export function ContainerOrchestrator() {
|
|
|
608
676
|
size="small"
|
|
609
677
|
icon={<ShrinkOutlined />}
|
|
610
678
|
disabled={loading || (scaleValues[stack.id] || 0) <= 0}
|
|
611
|
-
onClick={() =>
|
|
679
|
+
onClick={() =>
|
|
680
|
+
setScaleValues((prev) => ({ ...prev, [stack.id]: Math.max(0, (prev[stack.id] || 0) - 1) }))
|
|
681
|
+
}
|
|
612
682
|
/>
|
|
613
683
|
<InputNumber
|
|
614
684
|
min={0}
|
|
615
685
|
max={20}
|
|
616
686
|
value={scaleValues[stack.id] ?? stack.desiredReplicas}
|
|
617
|
-
onChange={(v) => setScaleValues(prev => ({ ...prev, [stack.id]: v ?? 0 }))}
|
|
687
|
+
onChange={(v) => setScaleValues((prev) => ({ ...prev, [stack.id]: v ?? 0 }))}
|
|
618
688
|
style={{ width: 70 }}
|
|
619
689
|
/>
|
|
620
690
|
<Button
|
|
621
691
|
size="small"
|
|
622
692
|
icon={<ExpandAltOutlined />}
|
|
623
693
|
disabled={loading || (scaleValues[stack.id] || 0) >= 20}
|
|
624
|
-
onClick={() =>
|
|
694
|
+
onClick={() =>
|
|
695
|
+
setScaleValues((prev) => ({ ...prev, [stack.id]: Math.min(20, (prev[stack.id] || 0) + 1) }))
|
|
696
|
+
}
|
|
625
697
|
/>
|
|
626
698
|
<Button
|
|
627
699
|
type="primary"
|
|
@@ -634,11 +706,7 @@ export function ContainerOrchestrator() {
|
|
|
634
706
|
<Text type="secondary">
|
|
635
707
|
{meta.running ?? '?'} / {scaleValues[stack.id] ?? stack.desiredReplicas} {t('running')}
|
|
636
708
|
</Text>
|
|
637
|
-
<Button
|
|
638
|
-
size="small"
|
|
639
|
-
icon={<ReloadOutlined />}
|
|
640
|
-
onClick={() => fetchContainers(stack.id)}
|
|
641
|
-
/>
|
|
709
|
+
<Button size="small" icon={<ReloadOutlined />} onClick={() => fetchContainers(stack.id)} />
|
|
642
710
|
</div>
|
|
643
711
|
|
|
644
712
|
{/* Container list */}
|
|
@@ -718,11 +786,7 @@ export function ContainerOrchestrator() {
|
|
|
718
786
|
width={860}
|
|
719
787
|
destroyOnClose
|
|
720
788
|
>
|
|
721
|
-
<Form
|
|
722
|
-
form={stackForm}
|
|
723
|
-
layout="vertical"
|
|
724
|
-
onFinish={handleSaveStack}
|
|
725
|
-
>
|
|
789
|
+
<Form form={stackForm} layout="vertical" onFinish={handleSaveStack}>
|
|
726
790
|
{/* Row 1: Stack name | Image */}
|
|
727
791
|
<Row gutter={12}>
|
|
728
792
|
<Col span={12}>
|
|
@@ -806,12 +870,18 @@ export function ContainerOrchestrator() {
|
|
|
806
870
|
{() => {
|
|
807
871
|
if (stackForm.getFieldValue('adapter') === 'docker') {
|
|
808
872
|
return (
|
|
809
|
-
<Form.Item
|
|
873
|
+
<Form.Item
|
|
874
|
+
name="networkMode"
|
|
875
|
+
label={t('Docker Network')}
|
|
876
|
+
extra={t('Main network. Workers also inherit app networks.')}
|
|
877
|
+
>
|
|
810
878
|
<Select allowClear placeholder={t('Default (bridge)')}>
|
|
811
879
|
<Select.Option value="bridge">bridge</Select.Option>
|
|
812
880
|
<Select.Option value="host">host</Select.Option>
|
|
813
|
-
{(Array.isArray(dockerNetworks) ? dockerNetworks : []).map(n => (
|
|
814
|
-
<Select.Option key={n.id} value={n.name}>
|
|
881
|
+
{(Array.isArray(dockerNetworks) ? dockerNetworks : []).map((n) => (
|
|
882
|
+
<Select.Option key={n.id} value={n.name}>
|
|
883
|
+
{n.name}
|
|
884
|
+
</Select.Option>
|
|
815
885
|
))}
|
|
816
886
|
</Select>
|
|
817
887
|
</Form.Item>
|
|
@@ -889,7 +959,7 @@ export function ContainerOrchestrator() {
|
|
|
889
959
|
<Select.Option value="kubernetes">{t('Kubernetes')}</Select.Option>
|
|
890
960
|
</Select>
|
|
891
961
|
</Form.Item>
|
|
892
|
-
|
|
962
|
+
|
|
893
963
|
<Form.Item noStyle shouldUpdate={(prev, curr) => prev.adapterType !== curr.adapterType}>
|
|
894
964
|
{() => {
|
|
895
965
|
const adapter = settingsForm.getFieldValue('adapterType');
|
|
@@ -899,7 +969,11 @@ export function ContainerOrchestrator() {
|
|
|
899
969
|
<Form.Item name="dockerSocketPath" label={t('Socket Path')} extra="/var/run/docker.sock">
|
|
900
970
|
<Input />
|
|
901
971
|
</Form.Item>
|
|
902
|
-
<Form.Item
|
|
972
|
+
<Form.Item
|
|
973
|
+
name="dockerHost"
|
|
974
|
+
label={t('TCP Host')}
|
|
975
|
+
extra="e.g. tcp://192.168.1.10:2376 (Overrides socket)"
|
|
976
|
+
>
|
|
903
977
|
<Input />
|
|
904
978
|
</Form.Item>
|
|
905
979
|
</Card>
|
|
@@ -911,7 +985,11 @@ export function ContainerOrchestrator() {
|
|
|
911
985
|
<Form.Item name="k8sNamespace" label={t('Namespace')} extra="nocobase">
|
|
912
986
|
<Input />
|
|
913
987
|
</Form.Item>
|
|
914
|
-
<Form.Item
|
|
988
|
+
<Form.Item
|
|
989
|
+
name="k8sKubeconfig"
|
|
990
|
+
label={t('Kubeconfig')}
|
|
991
|
+
extra="Paste kubeconfig YAML or enter a file path. Leave empty to use in-cluster ServiceAccount."
|
|
992
|
+
>
|
|
915
993
|
<Input.TextArea rows={4} />
|
|
916
994
|
</Form.Item>
|
|
917
995
|
</Card>
|
|
@@ -921,7 +999,11 @@ export function ContainerOrchestrator() {
|
|
|
921
999
|
}}
|
|
922
1000
|
</Form.Item>
|
|
923
1001
|
|
|
924
|
-
<Form.Item
|
|
1002
|
+
<Form.Item
|
|
1003
|
+
name="workerLabelSelector"
|
|
1004
|
+
label={t('Worker Label Selector')}
|
|
1005
|
+
extra="Only containers with this label will be managed. Example: role=worker"
|
|
1006
|
+
>
|
|
925
1007
|
<Input />
|
|
926
1008
|
</Form.Item>
|
|
927
1009
|
|