Skip to content

Commit

Permalink
feat:license deployment command (#4316)
Browse files Browse the repository at this point in the history
* feat:license deployment command

Signed-off-by: jingyang <3161362058@qq.com>

* fix application call

Signed-off-by: jingyang <3161362058@qq.com>

---------

Signed-off-by: jingyang <3161362058@qq.com>
  • Loading branch information
zjy365 committed Nov 18, 2023
1 parent 78543a3 commit 7fd40c2
Show file tree
Hide file tree
Showing 36 changed files with 13,830 additions and 9,364 deletions.
22,023 changes: 12,890 additions & 9,133 deletions frontend/pnpm-lock.yaml

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions frontend/providers/applaunchpad/src/api/platform.ts
Expand Up @@ -2,13 +2,18 @@ import { GET, POST } from '@/services/request';
import type { Response as InitDataType } from '@/pages/api/platform/getInitData';
import type { UserQuotaItemType, userPriceType } from '@/types/user';
import { AuthCnamePrams } from './params';
import { EnvResponse } from '@/types';

export const getResourcePrice = () => GET<userPriceType>('/api/platform/resourcePrice');

export const getInitData = () => GET<InitDataType>('/api/platform/getInitData');

export const getUserQuota = () =>
GET<{
balance: number;
quota: UserQuotaItemType[];
}>('/api/platform/getQuota');

export const postAuthCname = (data: AuthCnamePrams) => POST('/api/platform/authCname', data);

export const getPlatformEnv = () => GET<EnvResponse>('/api/platform/getEnv');
60 changes: 42 additions & 18 deletions frontend/providers/applaunchpad/src/pages/_app.tsx
@@ -1,25 +1,25 @@
import { useEffect, useState } from 'react';
import Head from 'next/head';
import type { AppProps } from 'next/app';
import { ChakraProvider } from '@chakra-ui/react';
import { theme } from '@/constants/theme';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import Router from 'next/router';
import NProgress from 'nprogress'; //nprogress module
import { sealosApp, createSealosApp } from 'sealos-desktop-sdk/app';
import { EVENT_NAME } from 'sealos-desktop-sdk';
import { useConfirm } from '@/hooks/useConfirm';
import throttle from 'lodash/throttle';
import { useGlobalStore } from '@/store/global';
import { useLoading } from '@/hooks/useLoading';
import { useGlobalStore } from '@/store/global';
import { loadInitData } from '@/store/static';
import { useRouter } from 'next/router';
import { appWithTranslation, useTranslation } from 'next-i18next';
import { getLangStore, setLangStore } from '@/utils/cookieUtils';
import { useUserStore } from '@/store/user';
import { getLangStore, setLangStore } from '@/utils/cookieUtils';
import { ChakraProvider } from '@chakra-ui/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import throttle from 'lodash/throttle';
import { appWithTranslation, useTranslation } from 'next-i18next';
import type { AppProps } from 'next/app';
import Head from 'next/head';
import Router, { useRouter } from 'next/router';
import NProgress from 'nprogress'; //nprogress module
import { useEffect, useState } from 'react';
import { EVENT_NAME } from 'sealos-desktop-sdk';
import { createSealosApp, sealosApp } from 'sealos-desktop-sdk/app';

import 'nprogress/nprogress.css';
import { getPlatformEnv } from '@/api/platform';
import '@/styles/reset.scss';
import 'nprogress/nprogress.css';

//Binding events.
Router.events.on('routeChangeStart', () => NProgress.start());
Expand Down Expand Up @@ -51,9 +51,7 @@ const App = ({ Component, pageProps }: AppProps) => {

useEffect(() => {
NProgress.start();

const response = createSealosApp();

(async () => {
const { SEALOS_DOMAIN, FORM_SLIDER_LIST_CONFIG } = await (() => loadInitData())();
initFormSliderList(FORM_SLIDER_LIST_CONFIG);
Expand All @@ -79,7 +77,6 @@ const App = ({ Component, pageProps }: AppProps) => {
}
})();
NProgress.done();

return response;
}, []);

Expand Down Expand Up @@ -136,6 +133,33 @@ const App = ({ Component, pageProps }: AppProps) => {
i18n?.changeLanguage?.(lang);
}, [refresh, router.pathname]);

// InternalAppCall
useEffect(() => {
const event = async (e: MessageEvent) => {
const envs = await getPlatformEnv();
const whitelist = [`https://${envs?.domain}`];
console.log(e, whitelist, 'post message');
if (!whitelist.includes(e.origin)) {
return;
}
try {
if (e.data?.type === 'InternalAppCall' && e.data?.name) {
router.push({
pathname: '/app/detail',
query: {
name: e.data.name
}
});
}
} catch (error) {
console.log(error, 'error');
}
};
window.addEventListener('message', event);
return () => window.removeEventListener('message', event);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return (
<>
<Head>
Expand Down
146 changes: 78 additions & 68 deletions frontend/providers/applaunchpad/src/pages/api/delApp.ts
Expand Up @@ -5,86 +5,96 @@ import { getK8s } from '@/services/backend/kubernetes';
import { jsonRes } from '@/services/backend/response';
import { appDeployKey } from '@/constants/app';

export type DeleteAppParams = { name: string };

export default async function handler(req: NextApiRequest, res: NextApiResponse<ApiResp>) {
try {
const { name } = req.query as { name: string };
const { name } = req.query as DeleteAppParams;
if (!name) {
throw new Error('deploy name is empty');
}

const { k8sApp, k8sCore, k8sAutoscaling, k8sNetworkingApp, namespace, k8sCustomObjects } =
await getK8s({
kubeconfig: await authSession(req.headers)
});

/* delete all sources */
const delDependent = await Promise.allSettled([
k8sCore.deleteNamespacedService(name, namespace), // delete service
k8sCore.deleteNamespacedConfigMap(name, namespace), // delete configMap
k8sCore.deleteNamespacedSecret(name, namespace), // delete secret
k8sNetworkingApp.deleteCollectionNamespacedIngress(
namespace,
undefined,
undefined,
undefined,
undefined,
undefined,
`${appDeployKey}=${name}`
), // delete Ingress
k8sCustomObjects.deleteNamespacedCustomObject(
// delete Issuer
'cert-manager.io',
'v1',
namespace,
'issuers',
name
),
k8sCustomObjects.deleteNamespacedCustomObject(
// delete Certificate
'cert-manager.io',
'v1',
namespace,
'certificates',
name
),
k8sCore.deleteCollectionNamespacedPersistentVolumeClaim(
// delete pvc
namespace,
undefined,
undefined,
undefined,
undefined,
undefined,
`app=${name}`
),
k8sAutoscaling.deleteNamespacedHorizontalPodAutoscaler(name, namespace) // delete HorizontalPodAutoscaler
]);

/* find not 404 error */
delDependent.forEach((item) => {
if (item.status === 'rejected' && +item?.reason?.body?.code !== 404) {
throw new Error('删除 App 异常');
}
});

// delete deploy and statefulSet
const delApp = await Promise.allSettled([
k8sApp.deleteNamespacedDeployment(name, namespace), // delete deploy
k8sApp.deleteNamespacedStatefulSet(name, namespace) // delete stateFuleSet
]);
await DeleteAppByName({ name, req });

/* find not 404 error */
delApp.forEach((item) => {
if (item.status === 'rejected' && +item?.reason?.body?.code !== 404) {
throw new Error('删除 App 异常');
}
jsonRes(res, {
message: 'successfully deleted'
});

jsonRes(res);
} catch (err: any) {
jsonRes(res, {
code: 500,
error: err
});
}
}

export async function DeleteAppByName({ name, req }: DeleteAppParams & { req: NextApiRequest }) {
const { k8sApp, k8sCore, k8sAutoscaling, k8sNetworkingApp, namespace, k8sCustomObjects } =
await getK8s({
kubeconfig: await authSession(req.headers)
});

/* delete all sources */
const delDependent = await Promise.allSettled([
k8sCore.deleteNamespacedService(name, namespace), // delete service
k8sCore.deleteNamespacedConfigMap(name, namespace), // delete configMap
k8sCore.deleteNamespacedSecret(name, namespace), // delete secret
k8sNetworkingApp.deleteCollectionNamespacedIngress(
namespace,
undefined,
undefined,
undefined,
undefined,
undefined,
`${appDeployKey}=${name}`
), // delete Ingress
k8sCustomObjects.deleteNamespacedCustomObject(
// delete Issuer
'cert-manager.io',
'v1',
namespace,
'issuers',
name
),
k8sCustomObjects.deleteNamespacedCustomObject(
// delete Certificate
'cert-manager.io',
'v1',
namespace,
'certificates',
name
),
k8sCore.deleteCollectionNamespacedPersistentVolumeClaim(
// delete pvc
namespace,
undefined,
undefined,
undefined,
undefined,
undefined,
`app=${name}`
),
k8sAutoscaling.deleteNamespacedHorizontalPodAutoscaler(name, namespace) // delete HorizontalPodAutoscaler
]);

/* find not 404 error */
delDependent.forEach((item) => {
console.log(item, 'delApp err');
if (item.status === 'rejected' && +item?.reason?.body?.code !== 404) {
throw new Error('删除 App 异常');
}
});

// delete deploy and statefulSet
const delApp = await Promise.allSettled([
k8sApp.deleteNamespacedDeployment(name, namespace), // delete deploy
k8sApp.deleteNamespacedStatefulSet(name, namespace) // delete stateFuleSet
]);

/* find not 404 error */
delApp.forEach((item) => {
console.log(item, 'delApp err');
if (item.status === 'rejected' && +item?.reason?.body?.code !== 404) {
throw new Error(item?.reason?.body);
}
});
}
62 changes: 34 additions & 28 deletions frontend/providers/applaunchpad/src/pages/api/getApps.ts
Expand Up @@ -7,34 +7,7 @@ import { appDeployKey } from '@/constants/app';

export default async function handler(req: NextApiRequest, res: NextApiResponse<ApiResp>) {
try {
const { k8sApp, namespace } = await getK8s({
kubeconfig: await authSession(req.headers)
});

const response = await Promise.allSettled([
k8sApp.listNamespacedDeployment(
namespace,
undefined,
undefined,
undefined,
undefined,
appDeployKey
),
k8sApp.listNamespacedStatefulSet(
namespace,
undefined,
undefined,
undefined,
undefined,
appDeployKey
)
]);

const apps = response
.filter((item) => item.status === 'fulfilled')
.map((item: any) => item?.value?.body?.items)
.filter((item) => item)
.flat();
const apps = await GetApps({ req });

jsonRes(res, { data: apps });
} catch (err: any) {
Expand All @@ -44,3 +17,36 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
});
}
}

export async function GetApps({ req }: { req: NextApiRequest }) {
const { k8sApp, namespace } = await getK8s({
kubeconfig: await authSession(req.headers)
});

const response = await Promise.allSettled([
k8sApp.listNamespacedDeployment(
namespace,
undefined,
undefined,
undefined,
undefined,
appDeployKey
),
k8sApp.listNamespacedStatefulSet(
namespace,
undefined,
undefined,
undefined,
undefined,
appDeployKey
)
]);

const apps = response
.filter((item) => item.status === 'fulfilled')
.map((item: any) => item?.value?.body?.items)
.filter((item) => item)
.flat();

return apps;
}
12 changes: 12 additions & 0 deletions frontend/providers/applaunchpad/src/pages/api/platform/getEnv.ts
@@ -0,0 +1,12 @@
import { jsonRes } from '@/services/backend/response';
import { ApiResp } from '@/services/kubernet';
import { EnvResponse } from '@/types/index';
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse<ApiResp>) {
jsonRes<EnvResponse>(res, {
data: {
domain: process.env.SEALOS_DOMAIN || 'cloud.sealos.io'
}
});
}
@@ -0,0 +1,24 @@
import { jsonRes } from '@/services/backend/response';
import { ApiResp } from '@/services/kubernet';
import type { NextApiRequest, NextApiResponse } from 'next';
import { DeleteAppByName, DeleteAppParams } from '../delApp';

export default async function handler(req: NextApiRequest, res: NextApiResponse<ApiResp>) {
try {
const { name } = req.query as DeleteAppParams;
if (!name) {
throw new Error('deploy name is empty');
}

await DeleteAppByName({ name, req });

jsonRes(res, {
message: 'successfully deleted'
});
} catch (err: any) {
jsonRes(res, {
code: 500,
error: err
});
}
}

0 comments on commit 7fd40c2

Please sign in to comment.