Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -424,5 +424,7 @@
"within_5_minutes": "五分钟内",
"yaml_file": "YAML 文件",
"you_have_successfully_deployed_database": "您已成功部署创建一个数据库!",
"app_already_exists": "应用名称已被使用,请更换应用名称或前往应用列表查看现有应用"
"app_already_exists": "应用名称已被使用,请更换应用名称或前往应用列表查看现有应用",
"kafka_resource_desc": "kafka-broker占50%、controller、kafka-expoter各占25%",
"clickhouse_resource_desc": "clickhouse占50%、ch-keeper和zookeeper各占25%"
}
4 changes: 2 additions & 2 deletions frontend/providers/dbprovider/src/constants/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,10 +228,10 @@ export const DBTypeList = [
{ id: DBTypeEnum.mysql, label: 'MySQL' },
{ id: DBTypeEnum.redis, label: 'Redis' },
{ id: DBTypeEnum.kafka, label: 'Kafka' },
{ id: DBTypeEnum.milvus, label: 'Milvus' }
{ id: DBTypeEnum.milvus, label: 'Milvus' },
// { id: DBTypeEnum.qdrant, label: 'qdrant' },
// { id: DBTypeEnum.pulsar, label: 'pulsar' },
// { id: DBTypeEnum.clickhouse, label: 'clickhouse' }
{ id: DBTypeEnum.clickhouse, label: 'clickhouse' }
// { id: DBTypeEnum.nebula, label: 'nebula' },
// { id: DBTypeEnum.weaviate, label: 'weaviate' }
];
Expand Down
11 changes: 9 additions & 2 deletions frontend/providers/dbprovider/src/pages/api/db/editPassword.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
}
}

const secretName = dbName + '-conn-credential';
body.data![dbTypeMap[dbType].passwordKey] = Buffer.from(newPassword).toString('base64');
// ensure secret exists and is writable
if (!body) {
throw new Error('Secret not found for updating password');
}
body.data = body.data ?? {};

const secretName = body.metadata?.name ?? `${dbName}-conn-credential`;
body.data[dbTypeMap[dbType].passwordKey] = Buffer.from(newPassword).toString('base64');

const k8s_result = await k8sCore.replaceNamespacedSecret(secretName, namespace, body);
if (k8s_result.response.statusCode !== 200) {
throw new Error('Failed to patch secret!!!');
Expand Down
7 changes: 1 addition & 6 deletions frontend/providers/dbprovider/src/pages/api/getDBList.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
'apps.kubeblocks.io',
'v1alpha1',
namespace,
'clusters',
undefined,
undefined,
undefined,
undefined,
`clusterdefinition.kubeblocks.io/name`
'clusters'
)) as {
body: {
items: KbPgClusterType[];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
dbType: DBType;
mock?: string;
};

console.log(`[getSecretByName] API called with:`, { dbName, dbType, mock });

if (!dbName) {
throw new Error('dbName is empty');
}
Expand All @@ -40,13 +43,17 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
kubeconfig: await authSession(req)
});

console.log(`[getSecretByName] Got k8s client, namespace: ${namespace}`);

const { username, password, host, port } = await fetchDBSecret(
k8sCore,
dbName,
dbType,
namespace
);

console.log(`[getSecretByName] fetchDBSecret result:`, { username, password, host, port });

const connectionInfo = buildConnectionInfo(dbType, username, password, host, port, namespace);

const data = {
Expand All @@ -56,11 +63,12 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
port,
...connectionInfo
};

console.log(`[getSecretByName] Final response data:`, data);
jsonRes<SecretResponse>(res, {
data
});
} catch (err: any) {
console.error(`[getSecretByName] Error:`, err);
jsonRes(res, {
code: 500,
error: err
Expand Down
127 changes: 108 additions & 19 deletions frontend/providers/dbprovider/src/pages/api/platform/getVersion.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,28 @@ export type Response = Record<

const MOCK: Response = DBVersionMap;

// Databases that use ComponentVersion (cmpv) instead of ClusterVersion (cv)
const COMPONENT_VERSION_DBS = [DBTypeEnum.mongodb, DBTypeEnum.redis, DBTypeEnum.clickhouse];

// Helper function to parse component versions
const parseComponentVersions = (cmpvItem: any): Array<{ id: string; label: string }> => {
const versions =
cmpvItem?.status?.serviceVersions || cmpvItem?.spec?.compatibilityRules?.[0]?.releases || [];
if (typeof versions === 'string') {
return versions.split(',').map((version: string) => ({
id: version.trim(),
label: version.trim()
}));
}
if (Array.isArray(versions)) {
return versions.map((version: any) => ({
id: typeof version === 'string' ? version : version.name || version.version,
label: typeof version === 'string' ? version : version.name || version.version
}));
}
return [];
};

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const DBVersionMap: Response = {
Expand All @@ -31,35 +53,102 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
[DBTypeEnum.clickhouse]: []
};

// source price
const kc = K8sApi();
const k8sCustomObjects = kc.makeApiClient(k8s.CustomObjectsApi);

const { body } = (await k8sCustomObjects.listClusterCustomObject(
'apps.kubeblocks.io',
'v1alpha1',
'clusterversions'
)) as any;
// Fetch ClusterVersions (cv) - for most databases
try {
const { body: cvBody } = (await k8sCustomObjects.listClusterCustomObject(
'apps.kubeblocks.io',
'v1alpha1',
'clusterversions'
)) as any;

cvBody.items.forEach((item: any) => {
const db = item?.spec?.clusterDefinitionRef as `${DBTypeEnum}`;
// Only use ClusterVersion for databases not using ComponentVersion
if (
DBVersionMap[db] &&
!COMPONENT_VERSION_DBS.includes(db as DBTypeEnum) &&
item?.metadata?.name &&
!DBVersionMap[db].find((existingDb) => existingDb.id === item.metadata.name)
) {
DBVersionMap[db].unshift({
id: item.metadata.name,
label: item.metadata.name
});
}
});
} catch (cvError) {
console.log('Error fetching ClusterVersions:', cvError);
}

// Fetch ComponentVersions (cmpv) - for MongoDB and Redis
try {
const { body: cmpvBody } = (await k8sCustomObjects.listClusterCustomObject(
'apps.kubeblocks.io',
'v1alpha1',
'componentversions'
)) as any;

cmpvBody.items.forEach((item: any) => {
const componentName = item?.metadata?.name;
let dbType: DBTypeEnum | null = null;
let versionPrefix = '';

// Map component names to database types and set version prefix
if (componentName === 'mongodb') {
dbType = DBTypeEnum.mongodb;
versionPrefix = 'mongodb-';
} else if (
componentName === 'redis' ||
componentName === 'redis-cluster' ||
componentName === 'redis-sentinel'
) {
dbType = DBTypeEnum.redis;
versionPrefix = 'redis-';
}

if (dbType && DBVersionMap[dbType]) {
const versions = parseComponentVersions(item);
versions.forEach((version) => {
// Add prefix to match static data format and avoid duplicates
const prefixedVersion = {
id: `${versionPrefix}${version.id}`,
label: `${versionPrefix}${version.id}`
};

if (!DBVersionMap[dbType!].find((existing) => existing.id === prefixedVersion.id)) {
DBVersionMap[dbType!].push(prefixedVersion);
}
});
}
});
} catch (cmpvError) {
console.log('Error fetching ComponentVersions:', cmpvError);
}

body.items.forEach((item: any) => {
const db = item?.spec?.clusterDefinitionRef as `${DBTypeEnum}`;
if (
DBVersionMap[db] &&
item?.metadata?.name &&
!DBVersionMap[db].find((db) => db.id === item.metadata.name)
) {
DBVersionMap[db].unshift({
id: item.metadata.name,
label: item.metadata.name
});
}
// Sort versions for better UX (latest first for each database)
Object.keys(DBVersionMap).forEach((dbType) => {
DBVersionMap[dbType as keyof typeof DBVersionMap].sort((a, b) => {
// Try to sort by version number if possible
const aVersion = a.id.match(/[\d.]+/)?.[0];
const bVersion = b.id.match(/[\d.]+/)?.[0];
if (aVersion && bVersion) {
return bVersion.localeCompare(aVersion, undefined, {
numeric: true,
sensitivity: 'base'
});
}
return b.id.localeCompare(a.id);
});
});

jsonRes(res, {
data: DBVersionMap
});
} catch (error) {
console.log(error);
console.log('Error in getVersion API:', error);
jsonRes(res, {
data: MOCK
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,9 +157,15 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => {
}, [applistCompleted, detailCompleted, router?.query?.guide, t]);

const supportConnectDB = useMemo(() => {
return !!['postgresql', 'mongodb', 'apecloud-mysql', 'redis', 'milvus', 'kafka'].find(
(item) => item === db.dbType
);
return !![
'postgresql',
'mongodb',
'apecloud-mysql',
'redis',
'milvus',
'kafka',
'clickhouse'
].find((item) => item === db.dbType);
}, [db.dbType]);

const { data: dbStatefulSet, refetch: refetchDBStatefulSet } = useQuery(
Expand All @@ -182,15 +188,15 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => {
})
: null,
{
enabled: supportConnectDB
enabled: supportConnectDB && !!dbStatefulSet
}
);

const { data: service, refetch: refetchService } = useQuery(
['getDBService', db.dbName, db.dbType],
() => (db.dbName ? getDBServiceByName(`${db.dbName}-export`) : null),
{
enabled: supportConnectDB,
enabled: supportConnectDB && !!dbStatefulSet,
retry: 3,
onSuccess(data) {
setIsChecked(!!data);
Expand Down Expand Up @@ -667,7 +673,7 @@ const AppBaseInfo = ({ db = defaultDBDetail }: { db: DBDetailType }) => {
</HStack>
</Flex>

{!['milvus', 'kafka'].includes(db.dbType) && (
{!['milvus'].includes(db.dbType) && (
<Flex position={'relative'} fontSize={'base'} mt={'16px'} gap={'12px'}>
{Object.entries(baseSecret).map(([name, value]) => (
<Box key={name} flex={1}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ const AppDetail = ({
});
},
onError(err) {
console.log('err', err);
router.replace('/dbs');
toast({
title: String(err),
Expand Down Expand Up @@ -242,7 +243,7 @@ const AppDetail = ({
</Flex>
{listType === TabEnum.Overview ? (
<Flex boxSize={'full'} flex={1} flexDirection={'column'}>
<AppBaseInfo db={dbDetail} />
<AppBaseInfo db={dbDetail} />
<Box
flex={'1 0 200px'}
bg={'white'}
Expand Down
Loading
Loading