diff --git a/frontend/pnpm-lock.yaml b/frontend/pnpm-lock.yaml index 64cb3d86e11..1681ba10ca3 100644 --- a/frontend/pnpm-lock.yaml +++ b/frontend/pnpm-lock.yaml @@ -631,7 +631,7 @@ importers: version: 5.9.0 next: specifier: 13.1.6 - version: 13.1.6(@babel/core@7.23.0)(react-dom@18.2.0)(react@18.2.0)(sass@1.68.0) + version: 13.1.6(react-dom@18.2.0)(react@18.2.0)(sass@1.68.0) next-i18next: specifier: ^13.3.0 version: 13.3.0(i18next@22.5.1)(next@13.1.6)(react-i18next@12.3.1)(react@18.2.0) @@ -1283,7 +1283,7 @@ importers: version: 5.2.2 zustand: specifier: ^4.4.1 - version: 4.4.1(@types/react@18.2.37)(immer@9.0.21)(react@18.2.0) + version: 4.4.1(@types/react@18.2.37)(immer@10.0.2)(react@18.2.0) devDependencies: '@types/jest': specifier: ^29.5.5 @@ -1414,6 +1414,9 @@ importers: octokit: specifier: ^3.1.1 version: 3.1.1 + pluralize: + specifier: ^8.0.0 + version: 8.0.0 react: specifier: 18.2.0 version: 18.2.0 @@ -1475,6 +1478,9 @@ importers: '@types/nprogress': specifier: ^0.2.1 version: 0.2.1 + '@types/pluralize': + specifier: ^0.0.33 + version: 0.0.33 '@types/react': specifier: 18.2.37 version: 18.2.37 @@ -7658,6 +7664,10 @@ packages: resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==} dev: false + /@types/pluralize@0.0.33: + resolution: {integrity: sha512-JOqsl+ZoCpP4e8TDke9W79FDcSgPAR0l6pixx2JHkhnRjvShyYiAYw2LVsnA7K08Y6DeOnaU6ujmENO4os/cYg==} + dev: true + /@types/prop-types@15.7.7: resolution: {integrity: sha512-FbtmBWCcSa2J4zL781Zf1p5YUBXQomPEcep9QZCfRfQgTxz3pJWiDFLebohZ9fFntX5ibzOkSsrJ0TEew8cAog==} @@ -14046,6 +14056,51 @@ packages: - babel-plugin-macros dev: false + /next@13.1.6(react-dom@18.2.0)(react@18.2.0)(sass@1.68.0): + resolution: {integrity: sha512-hHlbhKPj9pW+Cymvfzc15lvhaOZ54l+8sXDXJWm3OBNBzgrVj6hwGPmqqsXg40xO1Leq+kXpllzRPuncpC0Phw==} + engines: {node: '>=14.6.0'} + hasBin: true + peerDependencies: + fibers: '>= 3.1.0' + node-sass: ^6.0.0 || ^7.0.0 + react: ^18.2.0 + react-dom: ^18.2.0 + sass: ^1.3.0 + peerDependenciesMeta: + fibers: + optional: true + node-sass: + optional: true + sass: + optional: true + dependencies: + '@next/env': 13.1.6 + '@swc/helpers': 0.4.14 + caniuse-lite: 1.0.30001541 + postcss: 8.4.14 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + sass: 1.68.0 + styled-jsx: 5.1.1(@babel/core@7.23.3)(react@18.2.0) + optionalDependencies: + '@next/swc-android-arm-eabi': 13.1.6 + '@next/swc-android-arm64': 13.1.6 + '@next/swc-darwin-arm64': 13.1.6 + '@next/swc-darwin-x64': 13.1.6 + '@next/swc-freebsd-x64': 13.1.6 + '@next/swc-linux-arm-gnueabihf': 13.1.6 + '@next/swc-linux-arm64-gnu': 13.1.6 + '@next/swc-linux-arm64-musl': 13.1.6 + '@next/swc-linux-x64-gnu': 13.1.6 + '@next/swc-linux-x64-musl': 13.1.6 + '@next/swc-win32-arm64-msvc': 13.1.6 + '@next/swc-win32-ia32-msvc': 13.1.6 + '@next/swc-win32-x64-msvc': 13.1.6 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + dev: false + /next@13.2.4(react-dom@18.2.0)(react@18.2.0)(sass@1.68.0): resolution: {integrity: sha512-g1I30317cThkEpvzfXujf0O4wtaQHtDCLhlivwlTJ885Ld+eOgcz7r3TGQzeU+cSRoNHtD8tsJgzxVdYojFssw==} engines: {node: '>=14.6.0'} @@ -14074,7 +14129,7 @@ packages: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) sass: 1.68.0 - styled-jsx: 5.1.1(@babel/core@7.23.0)(react@18.2.0) + styled-jsx: 5.1.1(@babel/core@7.23.3)(react@18.2.0) optionalDependencies: '@next/swc-android-arm-eabi': 13.2.4 '@next/swc-android-arm64': 13.2.4 @@ -14165,7 +14220,7 @@ packages: react: 18.2.0 react-dom: 18.2.0(react@18.2.0) sass: 1.68.0 - styled-jsx: 5.1.1(@babel/core@7.23.0)(react@18.2.0) + styled-jsx: 5.1.1(@babel/core@7.23.3)(react@18.2.0) watchpack: 2.4.0 zod: 3.21.4 optionalDependencies: @@ -14590,6 +14645,11 @@ packages: engines: {node: '>= 0.4.0'} dev: false + /pluralize@8.0.0: + resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} + engines: {node: '>=4'} + dev: false + /postcss@8.4.14: resolution: {integrity: sha512-E398TUmfAYFPBSdzgeieK2Y1+1cpdxJx8yXbK/m57nRhKSmk1GB2tO4lbLBtlkfPQTDKfe4Xqv1ASWPpayPEig==} engines: {node: ^10 || ^12 || >=14} diff --git a/frontend/providers/template/delRoleBinding.ts b/frontend/providers/template/delRoleBinding.ts new file mode 100644 index 00000000000..bf7c4e7e712 --- /dev/null +++ b/frontend/providers/template/delRoleBinding.ts @@ -0,0 +1,27 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { ApiResp } from '@/services/kubernet'; +import { authSession } from '@/services/backend/auth'; +import { getK8s } from '@/services/backend/kubernetes'; +import { jsonRes } from '@/services/backend/response'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + const { instanceName } = req.query as { instanceName: string }; + if (!instanceName) { + throw new Error('deploy name is empty'); + } + + const { namespace, k8sAuth } = await getK8s({ + kubeconfig: await authSession(req.headers) + }); + + const result = await k8sAuth.deleteClusterRoleBinding(instanceName, namespace); + + jsonRes(res, { data: result }); + } catch (err: any) { + jsonRes(res, { + code: 500, + error: err + }); + } +} diff --git a/frontend/providers/template/package.json b/frontend/providers/template/package.json index 4e87cbe773e..e528e35fd7a 100644 --- a/frontend/providers/template/package.json +++ b/frontend/providers/template/package.json @@ -40,6 +40,7 @@ "next-i18next": "^13.3.0", "nprogress": "^0.2.0", "octokit": "^3.1.1", + "pluralize": "^8.0.0", "react": "18.2.0", "react-dom": "18.2.0", "react-hook-form": "^7.46.2", @@ -62,6 +63,7 @@ "@types/lodash": "^4.14.199", "@types/node": "18.13.0", "@types/nprogress": "^0.2.1", + "@types/pluralize": "^0.0.33", "@types/react": "18.2.37", "@types/react-dom": "18.0.10", "@types/react-syntax-highlighter": "^15.5.7", diff --git a/frontend/providers/template/src/api/delete.ts b/frontend/providers/template/src/api/delete.ts index dbdef7f6f12..a66c7cb16ba 100644 --- a/frontend/providers/template/src/api/delete.ts +++ b/frontend/providers/template/src/api/delete.ts @@ -36,8 +36,11 @@ export const delRoleBindingByName = (instanceName: string) => export const delServiceAccountByName = (instanceName: string) => DELETE('/api/resource/delServiceAccount', { instanceName }); - -const deleteResourceByKind: Record void> = { +export const delPersistentVolumeClaim = (instanceName: string) => + DELETE('/api/resource/delPersistentVolumeClaim', { instanceName }); +export const delCR = (data: Record<'kind' | 'name' | 'apiVersion', string>) => + DELETE('/api/resource/delCR', data); +const deleteResourceByKind: Record void)> = { CronJob: (instanceName: string) => delCronJobByName(instanceName), App: (instanceName: string) => deleteAppCRD(instanceName), Secret: (instanceName: string) => deleteSecret(instanceName), @@ -49,12 +52,21 @@ const deleteResourceByKind: Record v Issuer: (instanceName: string) => delIssuerByName(instanceName), Role: (instanceName: string) => delRoleByName(instanceName), RoleBinding: (instanceName: string) => delRoleBindingByName(instanceName), - ServiceAccount: (instanceName: string) => delServiceAccountByName(instanceName) + ServiceAccount: (instanceName: string) => delServiceAccountByName(instanceName), + PersistentVolumeClaim: delPersistentVolumeClaim }; export const deleteAllResources = async (resources: BaseResourceType[]) => { const deletePromises = resources.map((resource) => { - return deleteResourceByKind[resource.kind](resource.name); + const fn = deleteResourceByKind[resource.kind]; + console.log(fn); + if (!!fn) return fn(resource.name); + else + delCR({ + name: resource.name, + apiVersion: resource.apiVersion!, + kind: resource.kind! + }); }); const reuslt = await Promise.allSettled(deletePromises); console.log(reuslt); diff --git a/frontend/providers/template/src/pages/api/app/listOtherByName.ts b/frontend/providers/template/src/pages/api/app/listOtherByName.ts index c3a39af3625..a03ddaa8871 100644 --- a/frontend/providers/template/src/pages/api/app/listOtherByName.ts +++ b/frontend/providers/template/src/pages/api/app/listOtherByName.ts @@ -40,7 +40,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< undefined, labelSelector ); - // issuer const certIssuerPromise = k8sCustomObjects.listNamespacedCustomObject( 'cert-manager.io', @@ -112,6 +111,83 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< undefined, `${labelSelector},!${dbProviderKey},!${deployManagerKey}` ); + // promi + const prometheusPromiseRule = k8sCustomObjects.listNamespacedCustomObject( + 'monitoring.coreos.com', + 'v1', + namespace, + 'prometheusrules', + undefined, + undefined, + undefined, + undefined, + labelSelector + ) as Promise<{ + response: IncomingMessage; + body: { + items: { kind?: string }[]; + kind: 'PromehteusRuleList'; + }; + }>; + const prometheusPromise = k8sCustomObjects.listNamespacedCustomObject( + 'monitoring.coreos.com', + 'v1', + namespace, + 'prometheuses', + undefined, + undefined, + undefined, + undefined, + labelSelector + ) as Promise<{ + response: IncomingMessage; + body: { + items: { kind?: string }[]; + kind: 'PromehteusList'; + }; + }>; + const prometheusPvcPromise = k8sCore.listNamespacedPersistentVolumeClaim( + namespace, + undefined, + undefined, + undefined, + undefined, + `app.kubernetes.io/name=prometheus,prometheus=${instanceName}` + ); + const servicemonitorPromise = k8sCustomObjects.listNamespacedCustomObject( + 'monitoring.coreos.com', + 'v1', + namespace, + 'servicemonitors', + undefined, + undefined, + undefined, + undefined, + labelSelector + ) as Promise<{ + response: IncomingMessage; + body: { + items: { kind?: string }[]; + kind: 'ServiceMonitorList'; + }; + }>; + const probePromise = k8sCustomObjects.listNamespacedCustomObject( + 'monitoring.coreos.com', + 'v1', + namespace, + 'probes', + undefined, + undefined, + undefined, + undefined, + labelSelector + ) as Promise<{ + response: IncomingMessage; + body: { + items: { kind?: string }[]; + kind: 'ProbeList'; + }; + }>; // 使用 Promise.allSettled 获取所有结果 [secretResult, jobResult, customResourceResult] const result = await Promise.allSettled([ secretPromise, @@ -121,9 +197,13 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< rolePromise, roleBindingPromise, saPromise, - configMapPromise + configMapPromise, + prometheusPromise, + prometheusPromiseRule, + prometheusPvcPromise, + servicemonitorPromise, + probePromise ]); - const data = result .map((res) => { if (res.status === 'fulfilled') { diff --git a/frontend/providers/template/src/pages/api/resource/delCR.ts b/frontend/providers/template/src/pages/api/resource/delCR.ts new file mode 100644 index 00000000000..785ebcf0eb5 --- /dev/null +++ b/frontend/providers/template/src/pages/api/resource/delCR.ts @@ -0,0 +1,37 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { ApiResp } from '@/services/kubernet'; +import { authSession } from '@/services/backend/auth'; +import { getK8s } from '@/services/backend/kubernetes'; +import { jsonRes } from '@/services/backend/response'; +import pluralize from 'pluralize'; +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + const { name, apiVersion, kind } = req.query as Record< + 'name' | 'apiVersion' | 'kind', + undefined | null | string + >; + if (!name || !apiVersion || !kind) { + return jsonRes(res, { message: 'bad request', code: 400 }); + } + const [group, version] = apiVersion.split('/'); + if (!version || !group) return jsonRes(res, { message: 'bad request', code: 400 }); + const client = await getK8s({ + kubeconfig: await authSession(req.headers) + }); + const plural = pluralize.plural(kind.toLocaleLowerCase()); + const result = await client.k8sCustomObjects.deleteNamespacedCustomObject( + group, + version, + client.namespace, + plural, + name + ); + + jsonRes(res, { data: result }); + } catch (err: any) { + jsonRes(res, { + code: 500, + error: err + }); + } +} diff --git a/frontend/providers/template/src/pages/api/resource/delPersistentVolumeClaim.ts b/frontend/providers/template/src/pages/api/resource/delPersistentVolumeClaim.ts new file mode 100644 index 00000000000..9b173fdd5d2 --- /dev/null +++ b/frontend/providers/template/src/pages/api/resource/delPersistentVolumeClaim.ts @@ -0,0 +1,27 @@ +import type { NextApiRequest, NextApiResponse } from 'next'; +import { ApiResp } from '@/services/kubernet'; +import { authSession } from '@/services/backend/auth'; +import { K8sApi, getK8s } from '@/services/backend/kubernetes'; +import { jsonRes } from '@/services/backend/response'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + try { + const { instanceName } = req.query as { instanceName: string }; + if (!instanceName) { + throw new Error('pvc name is empty'); + } + + const { namespace, k8sCore } = await getK8s({ + kubeconfig: await authSession(req.headers) + }); + + // 删除 pvc + const result = await k8sCore.deleteNamespacedPersistentVolumeClaim(instanceName, namespace); + jsonRes(res, { data: result }); + } catch (err: any) { + jsonRes(res, { + code: 500, + error: err + }); + } +} diff --git a/frontend/providers/template/src/pages/api/resource/delRoleBinding.ts b/frontend/providers/template/src/pages/api/resource/delRoleBinding.ts index bf7c4e7e712..11240394fe1 100644 --- a/frontend/providers/template/src/pages/api/resource/delRoleBinding.ts +++ b/frontend/providers/template/src/pages/api/resource/delRoleBinding.ts @@ -15,7 +15,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< kubeconfig: await authSession(req.headers) }); - const result = await k8sAuth.deleteClusterRoleBinding(instanceName, namespace); + const result = await k8sAuth.deleteNamespacedRoleBinding(instanceName, namespace); jsonRes(res, { data: result }); } catch (err: any) { diff --git a/frontend/providers/template/src/pages/deploy/index.tsx b/frontend/providers/template/src/pages/deploy/index.tsx index 21f2389cff7..a9c994ac3db 100644 --- a/frontend/providers/template/src/pages/deploy/index.tsx +++ b/frontend/providers/template/src/pages/deploy/index.tsx @@ -256,7 +256,8 @@ export default function EditApp({ appName }: { appName?: string }) { overflow={'auto'} position={'relative'} borderRadius={'12px'} - background={'linear-gradient(180deg, #FFF 0%, rgba(255, 255, 255, 0.70) 100%)'}> + background={'linear-gradient(180deg, #FFF 0%, rgba(255, 255, 255, 0.70) 100%)'} + > + backdropBlur={'100px'} + > + cursor={'pointer'} + > + }} + > router.push('/')}> + onClick={() => router.push('/')} + > router.push('/')}> @@ -303,7 +308,8 @@ export default function EditApp({ appName }: { appName?: string }) { + color={router.pathname === '/deploy' ? '#262A32' : '#7B838B'} + > {data?.templateYaml?.metadata?.name} @@ -314,7 +320,8 @@ export default function EditApp({ appName }: { appName?: string }) { flexDirection={'column'} width={'100%'} flexGrow={1} - backgroundColor={'rgba(255, 255, 255, 0.90)'}> + backgroundColor={'rgba(255, 255, 255, 0.90)'} + >
{ - return { id: item.id, name: item.name, kind: item.kind }; + return { id: item.id, name: item.name, kind: item.kind, apiVersion: item.apiVersion }; }) ); } diff --git a/frontend/providers/template/src/services/backend/kubernetes.ts b/frontend/providers/template/src/services/backend/kubernetes.ts index 492c491b052..1f6a3ae3768 100644 --- a/frontend/providers/template/src/services/backend/kubernetes.ts +++ b/frontend/providers/template/src/services/backend/kubernetes.ts @@ -239,6 +239,7 @@ export async function getK8s({ kubeconfig }: { kubeconfig: string }) { k8sEvents: kc.makeApiClient(k8s.EventsV1Api), k8sAuth: kc.makeApiClient(k8s.RbacAuthorizationV1Api), k8sBatch: kc.makeApiClient(k8s.BatchV1Api), + k8sApiextensions: kc.makeApiClient(k8s.ApiextensionsV1Api), kube_user, namespace, applyYamlList diff --git a/frontend/providers/template/src/types/resource.ts b/frontend/providers/template/src/types/resource.ts index acc3d0dd933..551b92d8c82 100644 --- a/frontend/providers/template/src/types/resource.ts +++ b/frontend/providers/template/src/types/resource.ts @@ -2,6 +2,7 @@ export type BaseResourceType = { id: string; name: string; kind: ResourceKindType; + apiVersion?: string; }; export type ResourceKindType = @@ -24,4 +25,5 @@ export type OtherResourceListItemType = { createTime: string; kind: ResourceKindType; label: string; + apiVersion?: string; }; diff --git a/frontend/providers/template/src/utils/adapt.ts b/frontend/providers/template/src/utils/adapt.ts index 8a77775eaf5..022e1a627a5 100644 --- a/frontend/providers/template/src/utils/adapt.ts +++ b/frontend/providers/template/src/utils/adapt.ts @@ -114,7 +114,8 @@ export const adaptOtherList = ( name: item.metadata?.name || '', createTime: dayjs(item.metadata?.creationTimestamp).format('YYYY/MM/DD HH:mm'), kind: item.kind as ResourceKindType, - label: labels[componentLabel] ?? '' + label: labels[componentLabel] ?? '', + apiVersion: item.apiVersion }; }); });