diff --git a/src/components/icon-font/icons.ts b/src/components/icon-font/icons.ts
index 119d661a..8e36f053 100644
--- a/src/components/icon-font/icons.ts
+++ b/src/components/icon-font/icons.ts
@@ -10,6 +10,7 @@ import {
KubernetesOutlined,
ProfileOutlined,
RetweetOutlined,
+ StarOutlined,
ThunderboltOutlined
} from '@ant-design/icons';
import React from 'react';
@@ -58,7 +59,8 @@ const icons = {
LockPerson: React.createElement(IconFont, { type: 'icon-lock_person' }),
LockOpen: React.createElement(IconFont, { type: 'icon-lock_open' }),
Permission: React.createElement(IconFont, { type: 'icon-permission' }),
- CaptivePortal: React.createElement(IconFont, { type: 'icon-captive_portal' })
+ CaptivePortal: React.createElement(IconFont, { type: 'icon-captive_portal' }),
+ StarOutlined: React.createElement(StarOutlined)
};
export default icons;
diff --git a/src/locales/en-US/clusters.ts b/src/locales/en-US/clusters.ts
index b1624f39..f528f55f 100644
--- a/src/locales/en-US/clusters.ts
+++ b/src/locales/en-US/clusters.ts
@@ -110,5 +110,7 @@ Same applies to the /opt/dtk directory.`,
'clusters.table.ip.internal': 'Internal',
'clusters.table.ip.external': 'External',
'clusters.form.serverUrl.tips':
- 'Specify the server URL accessible from your cloud provider.'
+ 'Specify the server URL accessible from your cloud provider.',
+ 'clusters.form.setDefault': 'Set as Default',
+ 'clusters.form.setDefault.tips': 'Default for deployment.'
};
diff --git a/src/locales/ja-JP/clusters.ts b/src/locales/ja-JP/clusters.ts
index 4b0eaaf2..95a3089a 100644
--- a/src/locales/ja-JP/clusters.ts
+++ b/src/locales/ja-JP/clusters.ts
@@ -110,7 +110,9 @@ Same applies to the /opt/dtk directory.`,
'clusters.table.ip.internal': 'Internal',
'clusters.table.ip.external': 'External',
'clusters.form.serverUrl.tips':
- 'Specify the server URL accessible from your cloud provider.'
+ 'Specify the server URL accessible from your cloud provider.',
+ 'clusters.form.setDefault': 'Set as Default',
+ 'clusters.form.setDefault.tips': 'Default for deployment.'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -200,4 +202,6 @@ Same applies to the /opt/dtk directory.`,
// 80. 'clusters.table.ip.external': 'External',
// 81. 'clusters.form.serverUrl.tips': 'Specify the server URL accessible from your cloud provider.',
// 82. 'clusters.addworker.externalIP.tips': 'Specify an external IP if the worker is in a VPC or private network.',
+// 83. 'clusters.form.setDefault': 'Set as Default',
+// 84. 'clusters.form.setDefault.tips': 'Default for deployment'
// ========== End of To-Do List ==========
diff --git a/src/locales/ru-RU/clusters.ts b/src/locales/ru-RU/clusters.ts
index d802f2cc..c9abacfc 100644
--- a/src/locales/ru-RU/clusters.ts
+++ b/src/locales/ru-RU/clusters.ts
@@ -110,7 +110,9 @@ export default {
'clusters.table.ip.internal': 'Internal',
'clusters.table.ip.external': 'External',
'clusters.form.serverUrl.tips':
- 'Specify the server URL accessible from your cloud provider.'
+ 'Specify the server URL accessible from your cloud provider.',
+ 'clusters.form.setDefault': 'Set as Default',
+ 'clusters.form.setDefault.tips': 'Default for deployment.'
};
// ========== To-Do: Translate Keys (Remove After Translation) ==========
@@ -130,4 +132,6 @@ export default {
// 11. 'clusters.table.ip.external': 'External',
// 12. 'clusters.form.serverUrl.tips': 'Specify the server URL accessible from your cloud provider.'
// 13. 'clusters.addworker.specifyWorkerIP': 'Specify Worker IP {type}',
+// 14. 'clusters.form.setDefault': 'Set as Default',
+// 15. 'clusters.form.setDefault.tips': 'Default for deployment'
// ================================================================
diff --git a/src/locales/zh-CN/clusters.ts b/src/locales/zh-CN/clusters.ts
index 438022eb..385eabd0 100644
--- a/src/locales/zh-CN/clusters.ts
+++ b/src/locales/zh-CN/clusters.ts
@@ -106,5 +106,7 @@ export default {
'clusters.addworker.dataVolume.tips': '为 GPUStack 指定数据存储路径。',
'clusters.table.ip.internal': '内',
'clusters.table.ip.external': '外',
- 'clusters.form.serverUrl.tips': '指定可从您的云服务提供商访问的服务器地址。'
+ 'clusters.form.serverUrl.tips': '指定可从您的云服务提供商访问的服务器地址。',
+ 'clusters.form.setDefault': '设为默认',
+ 'clusters.form.setDefault.tips': '部署时的默认集群。'
};
diff --git a/src/pages/api-keys/hooks/use-keys-columns.tsx b/src/pages/api-keys/hooks/use-keys-columns.tsx
index 7deb0758..ad4a2a80 100644
--- a/src/pages/api-keys/hooks/use-keys-columns.tsx
+++ b/src/pages/api-keys/hooks/use-keys-columns.tsx
@@ -11,7 +11,7 @@ import { ListItem } from '../config/types';
interface ColumnsHookProps {
handleSelect: (val: string, record: ListItem) => void;
- sortOrder: string;
+ sortOrder: string[];
}
const actionList: Global.ActionItem[] = [
diff --git a/src/pages/cluster-management/apis/index.ts b/src/pages/cluster-management/apis/index.ts
index b6f19a82..4b3b9b37 100644
--- a/src/pages/cluster-management/apis/index.ts
+++ b/src/pages/cluster-management/apis/index.ts
@@ -198,3 +198,9 @@ export async function querySystemConfig() {
method: 'GET'
});
}
+
+export async function setDefaultCluster(params: { id: number }) {
+ return request(`${CLUSTERS_API}/${params.id}/set-default`, {
+ method: 'POST'
+ });
+}
diff --git a/src/pages/cluster-management/cluster-create.tsx b/src/pages/cluster-management/cluster-create.tsx
index 6376e5f8..3bf43870 100644
--- a/src/pages/cluster-management/cluster-create.tsx
+++ b/src/pages/cluster-management/cluster-create.tsx
@@ -8,7 +8,13 @@ import _ from 'lodash';
import React, { useEffect, useMemo, useRef, useState } from 'react';
import styled from 'styled-components';
import { PageContainerInner } from '../_components/page-box';
-import { createCluster, queryClusterToken, queryCredentialList } from './apis';
+import {
+ createCluster,
+ queryClusterList,
+ queryClusterToken,
+ queryCredentialList,
+ setDefaultCluster
+} from './apis';
import {
DockerStepsFromCluster,
K8sStepsFromCluter
@@ -248,6 +254,20 @@ const ClusterCreate = () => {
return step?.showButtons ? step?.showButtons(extraData.provider) : {};
}, [currentStep, steps, extraData.provider]);
+ const checkDefaultCluster = async () => {
+ try {
+ const res = await queryClusterList({ page: 1, perPage: 10 });
+ if (res.items.length === 1) {
+ const cluster = res.items[0];
+ if (!cluster.is_default) {
+ await setDefaultCluster({
+ id: cluster.id
+ });
+ }
+ }
+ } catch (error) {}
+ };
+
const submit = async (values: ClusterFormData) => {
const data = {
...extraData,
@@ -259,6 +279,7 @@ const ClusterCreate = () => {
...info,
cluster_id: res.id
});
+ checkDefaultCluster();
return true;
};
diff --git a/src/pages/cluster-management/clusters.tsx b/src/pages/cluster-management/clusters.tsx
index c22188b8..0cf9e045 100644
--- a/src/pages/cluster-management/clusters.tsx
+++ b/src/pages/cluster-management/clusters.tsx
@@ -26,6 +26,7 @@ import {
queryClusterList,
queryCredentialList,
queryWorkerPools,
+ setDefaultCluster,
updateCluster,
WORKER_POOLS_API
} from './apis';
@@ -192,6 +193,10 @@ const Clusters: React.FC = () => {
} else if (val === 'register_cluster') {
handleAddWorker(row);
setStepList(K8sStepsFromCluter);
+ } else if (val === 'isDefault') {
+ setDefaultCluster({ id: row.id }).then(() => {
+ message.success(intl.formatMessage({ id: 'common.message.success' }));
+ });
}
});
diff --git a/src/pages/cluster-management/components/cluster-form.tsx b/src/pages/cluster-management/components/cluster-form.tsx
index 4b8703e7..e84682ec 100644
--- a/src/pages/cluster-management/components/cluster-form.tsx
+++ b/src/pages/cluster-management/components/cluster-form.tsx
@@ -151,7 +151,11 @@ const ClusterForm: React.FC = forwardRef(
credentialList={credentialList}
>
)}
- name="description" rules={[{ required: false }]}>
+
+ name="description"
+ rules={[{ required: false }]}
+ style={{ marginBottom: 8 }}
+ >
;
provider: ProviderType;
@@ -72,6 +73,7 @@ export interface ClusterFormData {
name: string;
description: string;
provider: ProviderType;
+ is_default?: boolean;
credential_id: number;
zone: string;
region: string;
diff --git a/src/pages/cluster-management/hooks/use-cluster-columns.tsx b/src/pages/cluster-management/hooks/use-cluster-columns.tsx
index 10fa261a..99ad869f 100644
--- a/src/pages/cluster-management/hooks/use-cluster-columns.tsx
+++ b/src/pages/cluster-management/hooks/use-cluster-columns.tsx
@@ -4,7 +4,9 @@ import DropdownButtons from '@/components/drop-down-buttons';
import { SealColumnProps } from '@/components/seal-table/types';
import StatusTag from '@/components/status-tag';
import { tableSorter } from '@/config/settings';
+import { StarFilled } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
+import { Tooltip } from 'antd';
import dayjs from 'dayjs';
import { useMemo } from 'react';
import {
@@ -37,7 +39,20 @@ const useClusterColumns = (
sorter: tableSorter(1),
span: 3,
render: (text: string, record: ClusterListItem) => (
- {text}
+ <>
+ {text}
+ {record.is_default && (
+
+
+
+ )}
+ >
)
},
{
diff --git a/src/pages/llmodels/components/deploy-builtin-modal.tsx b/src/pages/llmodels/components/deploy-builtin-modal.tsx
index a7de8d05..a7e7d7c4 100644
--- a/src/pages/llmodels/components/deploy-builtin-modal.tsx
+++ b/src/pages/llmodels/components/deploy-builtin-modal.tsx
@@ -191,6 +191,10 @@ const AddModal: React.FC = (props) => {
};
const initClusterId = (): number => {
+ const defaultCluster = clusterList?.find((item) => item.is_default);
+ if (defaultCluster) {
+ return defaultCluster.value;
+ }
const cluster_id =
clusterList?.find((item) => item.state === ClusterStatusValueMap.Ready)
?.value || clusterList?.[0]?.value;
diff --git a/src/pages/llmodels/components/deploy-modal.tsx b/src/pages/llmodels/components/deploy-modal.tsx
index 4959659d..4548ee24 100644
--- a/src/pages/llmodels/components/deploy-modal.tsx
+++ b/src/pages/llmodels/components/deploy-modal.tsx
@@ -75,7 +75,7 @@ type AddModalProps = {
deploymentType?: 'modelList' | 'modelFiles';
clusterList: Global.BaseOption<
number,
- { provider: string; state: string | number }
+ { provider: string; state: string | number; is_default: boolean }
>[];
onOk: (values: FormData) => void;
onCancel: () => void;
@@ -441,6 +441,12 @@ const AddModal: FC = (props) => {
if (initialValues?.cluster_id) {
return initialValues.cluster_id;
}
+ // Find default cluster
+ const defaultCluster = clusterList?.find((item) => item.is_default);
+ if (defaultCluster) {
+ return defaultCluster.value;
+ }
+
const cluster_id =
clusterList?.find((item) => item.state === ClusterStatusValueMap.Ready)
?.value || clusterList?.[0]?.value;
diff --git a/src/pages/llmodels/hooks/use-form-initial-values.ts b/src/pages/llmodels/hooks/use-form-initial-values.ts
index 482d9773..00efd192 100644
--- a/src/pages/llmodels/hooks/use-form-initial-values.ts
+++ b/src/pages/llmodels/hooks/use-form-initial-values.ts
@@ -217,7 +217,8 @@ export const useGenerateWorkerOptions = () => {
label: item.name,
value: item.id,
provider: item.provider as string,
- state: item.state
+ state: item.state,
+ is_default: item.is_default
}))
);
};
@@ -235,7 +236,10 @@ export default function useFormInitialValues() {
const [, setWorkerListAtom] = useAtom(workerListAtom);
const [clusterList, setClusterList] = useState<
- Global.BaseOption[]
+ Global.BaseOption<
+ number,
+ { provider: string; state: string; is_default: boolean }
+ >[]
>([]);
const [workerList, setWorkerList] = useState([]);
@@ -249,7 +253,8 @@ export default function useFormInitialValues() {
label: item.name,
value: item.id,
provider: item.provider as string,
- state: item.state
+ state: item.state,
+ is_default: item.is_default
}));
setClusterList(list);
setClusterListAtom(list);
diff --git a/src/pages/login/hooks/use-local-auth.ts b/src/pages/login/hooks/use-local-auth.ts
index acc90cd6..d6fa3412 100644
--- a/src/pages/login/hooks/use-local-auth.ts
+++ b/src/pages/login/hooks/use-local-auth.ts
@@ -1,4 +1,5 @@
import { initialPasswordAtom } from '@/atoms/user';
+import { clearStorageUserSettings } from '@/atoms/utils';
import {
CRYPT_TEXT,
REMEMBER_ME_KEY,
@@ -81,7 +82,7 @@ export const useLocalAuth = ({
if (userInfo?.require_password_change) {
setInitialPassword(encryptPassword(values.password));
}
-
+ clearStorageUserSettings();
onSuccess?.(userInfo);
} catch (error: any) {
onError?.(error);