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.
Files changed (119) hide show
  1. package/client-v2.d.ts +2 -0
  2. package/client-v2.js +1 -0
  3. package/client.js +1 -0
  4. package/dist/client/index.js +1 -1
  5. package/dist/client-v2/914.5dc1105cf3ada6a6.js +10 -0
  6. package/dist/client-v2/index.js +10 -0
  7. package/dist/externalVersion.js +6 -5
  8. package/dist/locale/en-US.json +138 -28
  9. package/dist/locale/vi-VN.json +139 -28
  10. package/dist/locale/zh-CN.json +140 -28
  11. package/dist/server/actions/cache-monitor.js +301 -0
  12. package/dist/server/actions/cluster-nodes.js +391 -11
  13. package/dist/server/actions/doctor.js +1246 -0
  14. package/dist/server/actions/orchestrator.js +37 -0
  15. package/dist/server/actions/queue-mappings.js +107 -0
  16. package/dist/server/collections/cluster-manager-doctor-runs.js +52 -0
  17. package/dist/server/collections/cluster-manager-doctor.js +44 -0
  18. package/dist/server/collections/worker-queue-mappings.js +106 -0
  19. package/dist/server/hooks/cacheInvalidationHooks.js +81 -0
  20. package/dist/server/middlewares/listMetaCacheMiddleware.js +79 -0
  21. package/dist/server/orchestrator/PackageManager.js +21 -24
  22. package/dist/server/orchestrator/docker-adapter.js +49 -27
  23. package/dist/server/plugin.js +71 -16
  24. package/dist/server/queue-scanner.js +141 -0
  25. package/dist/server/utils/node.js +30 -2
  26. package/dist/server/utils/versionManager.js +91 -0
  27. package/package.json +9 -5
  28. package/server.js +1 -0
  29. package/src/client/AclCacheManager.tsx +292 -287
  30. package/src/client/CacheMonitor.tsx +166 -179
  31. package/src/client/ClusterManagerLayout.tsx +54 -42
  32. package/src/client/ClusterNodes.tsx +698 -418
  33. package/src/client/ContainerOrchestrator.tsx +184 -102
  34. package/src/client/Doctor.tsx +559 -0
  35. package/src/client/NginxCacheManager.tsx +415 -0
  36. package/src/client/PluginOperations.tsx +234 -234
  37. package/src/client/QueueAssignment.tsx +355 -0
  38. package/src/client/TaskManager.tsx +194 -187
  39. package/src/client/WorkflowExecutions.tsx +243 -238
  40. package/src/client/index.tsx +22 -14
  41. package/src/client/utils/clientSafeCache.ts +41 -0
  42. package/src/client/utils/requestDedupInterceptor.ts +213 -0
  43. package/src/client-v2/plugin.tsx +24 -0
  44. package/src/locale/en-US.json +138 -28
  45. package/src/locale/vi-VN.json +139 -28
  46. package/src/locale/zh-CN.json +140 -28
  47. package/src/server/__tests__/doctor.test.ts +53 -0
  48. package/src/server/actions/acl-cache.ts +272 -272
  49. package/src/server/actions/cache-monitor.ts +453 -116
  50. package/src/server/actions/cluster-nodes.ts +878 -378
  51. package/src/server/actions/doctor.ts +1536 -0
  52. package/src/server/actions/orchestrator.ts +54 -2
  53. package/src/server/actions/queue-mappings.ts +94 -0
  54. package/src/server/collections/cluster-manager-doctor-runs.ts +23 -0
  55. package/src/server/collections/cluster-manager-doctor.ts +19 -0
  56. package/src/server/collections/worker-queue-mappings.ts +85 -0
  57. package/src/server/hooks/cacheInvalidationHooks.ts +58 -0
  58. package/src/server/middlewares/listMetaCacheMiddleware.ts +55 -0
  59. package/src/server/orchestrator/PackageManager.ts +20 -24
  60. package/src/server/orchestrator/docker-adapter.ts +74 -37
  61. package/src/server/plugin.ts +347 -270
  62. package/src/server/queue-scanner.ts +154 -0
  63. package/src/server/utils/node.ts +48 -0
  64. package/src/server/utils/versionManager.ts +69 -0
  65. package/dist/client/AclCacheManager.d.ts +0 -2
  66. package/dist/client/CacheMonitor.d.ts +0 -2
  67. package/dist/client/ClusterManagerLayout.d.ts +0 -2
  68. package/dist/client/ClusterNodes.d.ts +0 -2
  69. package/dist/client/ContainerOrchestrator.d.ts +0 -2
  70. package/dist/client/EventQueueMonitor.d.ts +0 -2
  71. package/dist/client/LockMonitor.d.ts +0 -2
  72. package/dist/client/PackageInstaller.d.ts +0 -2
  73. package/dist/client/PluginOperations.d.ts +0 -2
  74. package/dist/client/RedisMonitor.d.ts +0 -2
  75. package/dist/client/TaskManager.d.ts +0 -2
  76. package/dist/client/WorkflowExecutions.d.ts +0 -2
  77. package/dist/client/index.d.ts +0 -5
  78. package/dist/client/utils.d.ts +0 -12
  79. package/dist/index.d.ts +0 -2
  80. package/dist/server/actions/acl-cache.d.ts +0 -53
  81. package/dist/server/actions/cache-monitor.d.ts +0 -23
  82. package/dist/server/actions/cluster-nodes.d.ts +0 -49
  83. package/dist/server/actions/event-queue-monitor.d.ts +0 -13
  84. package/dist/server/actions/lock-monitor.d.ts +0 -19
  85. package/dist/server/actions/orchestrator.d.ts +0 -58
  86. package/dist/server/actions/package-manager.d.ts +0 -6
  87. package/dist/server/actions/plugin-operations.d.ts +0 -6
  88. package/dist/server/actions/redis-monitor.d.ts +0 -12
  89. package/dist/server/actions/tasks.d.ts +0 -7
  90. package/dist/server/actions/workflow-executions.d.ts +0 -7
  91. package/dist/server/adapters/redis-lock-adapter.d.ts +0 -15
  92. package/dist/server/adapters/redis-node-registry.d.ts +0 -12
  93. package/dist/server/adapters/redis-pubsub-adapter.d.ts +0 -16
  94. package/dist/server/collections/app.d.ts +0 -8
  95. package/dist/server/collections/cluster-manager-acl-cache.d.ts +0 -22
  96. package/dist/server/collections/cluster-manager-cache-mgr.d.ts +0 -22
  97. package/dist/server/collections/cluster-manager-cluster.d.ts +0 -22
  98. package/dist/server/collections/cluster-manager-lock.d.ts +0 -22
  99. package/dist/server/collections/cluster-manager-plugins.d.ts +0 -18
  100. package/dist/server/collections/cluster-manager-queue.d.ts +0 -22
  101. package/dist/server/collections/cluster-manager-redis.d.ts +0 -22
  102. package/dist/server/collections/cluster-manager-workflow.d.ts +0 -22
  103. package/dist/server/collections/cluster-manager.d.ts +0 -22
  104. package/dist/server/collections/orchestrator-settings.d.ts +0 -59
  105. package/dist/server/collections/orchestrator-stacks.d.ts +0 -102
  106. package/dist/server/collections/worker-orchestrator.d.ts +0 -22
  107. package/dist/server/collections/worker-packages-configs.d.ts +0 -3
  108. package/dist/server/collections/worker-packages.d.ts +0 -22
  109. package/dist/server/orchestrator/PackageManager.d.ts +0 -39
  110. package/dist/server/orchestrator/docker-adapter.d.ts +0 -41
  111. package/dist/server/orchestrator/index.d.ts +0 -4
  112. package/dist/server/orchestrator/k8s-adapter.d.ts +0 -50
  113. package/dist/server/orchestrator/leader-election.d.ts +0 -48
  114. package/dist/server/orchestrator/types.d.ts +0 -84
  115. package/dist/server/plugin.d.ts +0 -26
  116. package/dist/server/utils/node.d.ts +0 -6
  117. package/dist/server/utils/redis.d.ts +0 -29
  118. package/dist/shared/packages.d.ts +0 -23
  119. /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 { Card, Table, Button, Space, Tag, InputNumber, Popconfirm, message, Modal, Typography, Alert, Spin, Tooltip, Badge, Row, Col, Form, Input, Select, Switch } from 'antd';
3
- import { PlayCircleOutlined, PauseCircleOutlined, DeleteOutlined, FileTextOutlined, ReloadOutlined, ExpandAltOutlined, ShrinkOutlined, CloudServerOutlined, ApiOutlined, SettingOutlined, SaveOutlined, PlusOutlined, EditOutlined } from '@ant-design/icons';
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.0.46-full',
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, containerId: '', logs: [],
222
+ visible: false,
223
+ containerId: '',
224
+ logs: [],
188
225
  });
189
226
  const [pkgLogModal, setPkgLogModal] = useState<{ visible: boolean; containerName: string; logs: string }>({
190
- visible: false, containerName: '', logs: '',
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, name: 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) => { sv[s.id] = s.desiredReplicas; });
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(async (stackId: number) => {
241
- try {
242
- const res = await api.request({
243
- url: '/workerOrchestrator:containers',
244
- params: { stackId },
245
- });
246
- const data = res.data?.data || res.data;
247
- setContainers(prev => ({ ...prev, [stackId]: data.data || [] }));
248
- setContainerMeta(prev => ({ ...prev, [stackId]: data.meta || {} }));
249
- } catch (err: any) {
250
- message.error(`Failed to load containers: ${err.message}`);
251
- }
252
- }, [api]);
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 }}>{record.id}</Text>
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={() => setPkgLogModal({ visible: true, containerName: record.name, logs: pStatus.lastInitLog || '' })}
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' ? 'Installed' : pStatus.initStatus === 'failed' ? 'Failed' : `${pStatus.initProgressPercent || 0}%`}
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) => <Text type="secondary" style={{ fontSize: 12 }}>{img}</Text>,
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
- <Space size="small">
468
- {!isK8s && (
469
- record.status === 'running' ? (
470
- <Tooltip title={t('Stop')}>
471
- <Button
472
- size="small" type="text" icon={<PauseCircleOutlined />}
473
- onClick={() => handleAction('stop', record.id, record._stackId!)}
474
- />
475
- </Tooltip>
476
- ) : (
477
- <Tooltip title={t('Start')}>
478
- <Button
479
- size="small" type="text" icon={<PlayCircleOutlined style={{ color: '#52c41a' }} />}
480
- onClick={() => handleAction('start', record.id, record._stackId!)}
481
- />
482
- </Tooltip>
483
- )
484
- )}
485
- <Tooltip title={t('Logs')}>
486
- <Button
487
- size="small" type="text" icon={<FileTextOutlined />}
488
- onClick={() => handleViewLogs(record.id, record._stackId!)}
489
- />
490
- </Tooltip>
491
- <Popconfirm
492
- title={t('Remove this container?')}
493
- onConfirm={() => handleAction('remove', record.id, record._stackId!)}
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
- </Popconfirm>
499
- </Space>
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 <Spin tip={t('Connecting to orchestrator...')} style={{ display: 'flex', justifyContent: 'center', padding: 48 }} />;
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 status={pingResult.connected ? 'success' : 'error'} text={pingResult.connected ? t('Connected') : t('Disconnected')} />
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 status={pingResult.leader ? 'success' : 'default'} text={pingResult.leader ? t('Leader (this node)') : t('Follower')} />
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={() => { fetchPing(); fetchStacks(); }}
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 }}>{stack.image}</Text>
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={() => setScaleValues(prev => ({ ...prev, [stack.id]: Math.max(0, (prev[stack.id] || 0) - 1) }))}
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={() => setScaleValues(prev => ({ ...prev, [stack.id]: Math.min(20, (prev[stack.id] || 0) + 1) }))}
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 name="networkMode" label={t('Docker Network')} extra={t('Main network. Workers also inherit app networks.')}>
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}>{n.name}</Select.Option>
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 name="dockerHost" label={t('TCP Host')} extra="e.g. tcp://192.168.1.10:2376 (Overrides socket)">
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 name="k8sKubeconfig" label={t('Kubeconfig')} extra="Paste kubeconfig YAML or enter a file path. Leave empty to use in-cluster ServiceAccount.">
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 name="workerLabelSelector" label={t('Worker Label Selector')} extra="Only containers with this label will be managed. Example: role=worker">
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