Skip to content

Commit

Permalink
feat:implement license id scheme (#4413)
Browse files Browse the repository at this point in the history
* feat:implement license id scheme

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

* add nocluster active

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

* fix active cluster

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

---------

Signed-off-by: jingyang <3161362058@qq.com>
  • Loading branch information
zjy365 committed Dec 14, 2023
1 parent c3f5bf6 commit 94ac016
Show file tree
Hide file tree
Showing 59 changed files with 2,294 additions and 1,005 deletions.
67 changes: 57 additions & 10 deletions frontend/desktop/deploy/manifests/rbac.yaml
Expand Up @@ -3,16 +3,16 @@ kind: ClusterRole
metadata:
name: auth-system-manager-role
rules:
- apiGroups: ["user.sealos.io"]
resources: ["users"]
verbs: ["list", "get", "create", "update", "patch", "watch"]
- apiGroups: ["user.sealos.io"]
resources: ["users/status"]
verbs: ["list", "get", "create", "update", "patch", "watch"]
- apiGroups: ["user.sealos.io"]
resources: ["operationrequests", "deleterequests"]
verbs: ["create","get"]
- apiGroups: ['user.sealos.io']
resources: ['users']
verbs: ['list', 'get', 'create', 'update', 'patch', 'watch']
- apiGroups: ['user.sealos.io']
resources: ['users/status']
verbs: ['list', 'get', 'create', 'update', 'patch', 'watch']
- apiGroups: ['user.sealos.io']
resources: ['operationrequests', 'deleterequests']
verbs: ['create', 'get']

---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
Expand Down Expand Up @@ -52,3 +52,50 @@ subjects:
- kind: ServiceAccount
name: desktop-frontend
namespace: sealos
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: recharge-gift-cm-reader
namespace: sealos
rules:
- apiGroups: [''] # "" indicates the core API group
resources: ['configmaps']
resourceNames: ['recharge-gift']
verbs: ['get', 'watch', 'list']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: recharge-gift-cm-reader-rolebinding
namespace: sealos
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: recharge-gift-cm-reader
subjects:
- kind: Group
name: system:serviceaccounts
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: account-editor-role
rules:
- apiGroups: ['account.sealos.io']
resources: ['accounts']
verbs: ['list', 'get', 'create', 'update', 'patch', 'watch']
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: desktop-account-editor-role-binding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: account-editor-role
subjects:
- kind: ServiceAccount
name: desktop-frontend
namespace: sealos
6 changes: 4 additions & 2 deletions frontend/providers/license/public/locales/en/common.json
Expand Up @@ -4,11 +4,13 @@
"Activation Record": "Activation Record",
"Purchase License": "Purchase License",
"Please go to": "Please go to",
"Purchase": "purchase",
"Activation Time": "Activation time: ",
"Go to the message center to see the results": "Go to the message center to see the results",
"token does not exist": "license does not exist",
"No Record": "No Record",
"Activation time": "Activation time",
"Activation Successful": "Activation Successful"
"Activation Successful": "Activation Successful",
"Failed to obtain cluster ID, contact administrator": "Failed to obtain cluster ID, contact administrator",
"Cluster ID": "Cluster ID: {{id}}",
"Purchase Tip": "Activate / Purchase"
}
6 changes: 4 additions & 2 deletions frontend/providers/license/public/locales/zh/common.json
Expand Up @@ -4,11 +4,13 @@
"Activation Record": "激活记录",
"Purchase License": "购买 License",
"Please go to": "请前往",
"Purchase": "购买",
"Activation Time": "激活时间: ",
"Go to the message center to see the results": "前往消息中心查看结果",
"token does not exist": "license 不存在",
"No Record": "暂无记录",
"Activation time": "激活时间",
"Activation Successful": "激活成功"
"Activation Successful": "激活成功",
"Failed to obtain cluster ID, contact administrator": "获取集群id失败, 联系管理员",
"Cluster ID": "集群 ID: {{id}}",
"Purchase Tip": "激活 / 购买"
}
2 changes: 2 additions & 0 deletions frontend/providers/license/src/api/platform.ts
Expand Up @@ -2,3 +2,5 @@ import { GET } from '@/services/request';
import { Response as EnvResponse } from '@/pages/api/platform/getEnv';

export const getPlatformEnv = () => GET<EnvResponse>('/api/platform/getEnv');

export const getClusterId = () => GET<{ systemId: string }>('/api/platform/getClusterId');
31 changes: 31 additions & 0 deletions frontend/providers/license/src/pages/api/platform/getClusterId.ts
@@ -0,0 +1,31 @@
import { authSession } from '@/services/backend/auth';
import { K8sApiDefault, getK8s } from '@/services/backend/kubernetes';
import { jsonRes } from '@/services/backend/response';
import { ApiResp } from '@/services/kubernet';
import * as k8s from '@kubernetes/client-node';
import type { NextApiRequest, NextApiResponse } from 'next';

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

const defaultKc = K8sApiDefault();

const result = await defaultKc.makeApiClient(k8s.CoreV1Api).readNamespace('kube-system');

const systemId = result?.body?.metadata?.uid?.substring(0, 8);

jsonRes(res, {
data: {
systemId: systemId
}
});
} catch (err: any) {
jsonRes(res, {
code: 500,
error: err
});
}
}
33 changes: 25 additions & 8 deletions frontend/providers/license/src/pages/index.tsx
@@ -1,5 +1,5 @@
import { applyLicense, getLicenseRecord } from '@/api/license';
import { getPlatformEnv } from '@/api/platform';
import { getClusterId, getPlatformEnv } from '@/api/platform';
import CurrencySymbol from '@/components/CurrencySymbol';
import FileSelect, { FileItemType } from '@/components/FileSelect';
import MyIcon from '@/components/Icon';
Expand Down Expand Up @@ -46,11 +46,28 @@ export default function LicenseApp() {
}
});

useQuery(['getPlatformEnv'], () => getPlatformEnv(), {
const { data: kubeSystem } = useQuery(['getClusterId'], () => getClusterId(), {
onSuccess(data) {
const main = data.LICENSE_DOMAIN;
const link = `https://${main}`;
setPurchaseLink(link);
getPlatformEnv()
.then((res) => {
const main = res.LICENSE_DOMAIN;
const link = `https://${main}/cluster?systemId=${data?.systemId}`;
setPurchaseLink(link);
})
.catch((err) => {
toast({
position: 'top',
status: 'error',
description: 'Get system env error'
});
});
},
onError(err) {
toast({
position: 'top',
status: 'error',
description: t('Failed to obtain cluster ID, contact administrator')
});
}
});

Expand Down Expand Up @@ -104,8 +121,8 @@ export default function LicenseApp() {
left="50%"
transform="translate(-50%, -50%)"
>
<Text fontSize={'32px'} fontWeight={600}>
{t('Purchase License')}
<Text fontSize={'24px'} fontWeight={600}>
{t('Cluster ID', { id: kubeSystem?.systemId })}
</Text>
<Box w="194px" h="54px" mt="50px" position={'relative'}>
<Center
Expand All @@ -132,7 +149,7 @@ export default function LicenseApp() {
fontSize={'16px'}
onClick={() => window.open(purchaseLink)}
>
{t('Purchase')}
{t('Purchase Tip')}
</Center>
</Box>
</Flex>
Expand Down
6 changes: 6 additions & 0 deletions frontend/providers/license/src/services/backend/kubernetes.ts
@@ -1,6 +1,12 @@
import * as k8s from '@kubernetes/client-node';
import * as yaml from 'js-yaml';

export function K8sApiDefault(): k8s.KubeConfig {
const kc = new k8s.KubeConfig();
kc.loadFromDefault();
return kc;
}

export function CheckIsInCluster(): [boolean, string] {
if (
process.env.KUBERNETES_SERVICE_HOST !== undefined &&
Expand Down
Binary file modified frontend/providers/template/public/favicon.ico
Binary file not shown.
2 changes: 1 addition & 1 deletion frontend/providers/template/src/pages/_app.tsx
Expand Up @@ -120,7 +120,7 @@ const App = ({ Component, pageProps, domain }: AppProps & { domain: string }) =>
return (
<>
<Head>
<title>Sealos Fast Deploy</title>
<title>Sealos Templates</title>
<meta name="description" content="Generated by Sealos Team" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" />
Expand Down
4 changes: 2 additions & 2 deletions service/license/public/locales/en/common.json
Expand Up @@ -76,7 +76,6 @@
"Recive Tips": "{{managerName}} invite you join in {{teamName}} as {{role}}",
"pay with stripe": "Pay With Stripe",
"pay with wechat": "Pay With Wechat",
"License Buy": "License Buy",
"Purchase History": "Purchase History",
"Purchase License": "Purchase License",
"Remaining Time": "Remaining Time: ",
Expand Down Expand Up @@ -106,5 +105,6 @@
"No Elements to Delete": "No Elements to Delete"
}
},
"Enter Save": "Enter Save"
"Enter Save": "Enter Save",
"License History": "License History"
}
4 changes: 2 additions & 2 deletions service/license/public/locales/zh/common.json
Expand Up @@ -77,7 +77,6 @@
"Recive Tips": "{{managerName}} 邀请你到 {{teamName}} 当 {{role}}",
"pay with wechat": "微信支付",
"pay with stripe": "Stripe 支付",
"License Buy": "License 购买",
"Purchase History": "购买记录",
"Purchase License": "购买 License",
"Remaining Time": "剩余激活时间: ",
Expand Down Expand Up @@ -106,5 +105,6 @@
"No Elements to Delete": "没有元素可以删除"
}
},
"Enter Save": "回车保存"
"Enter Save": "回车保存",
"License History": "历史 License"
}
30 changes: 21 additions & 9 deletions service/license/src/api/cluster.ts
@@ -1,17 +1,29 @@
import { GET, POST } from '@/services/request';
import { CreateClusterParams, ClusterDB, ClusterType, ClusterResult } from '@/types';
import { ActiveClusterParams } from '@/pages/api/cluster/activeCluster';
import { DELETE, GET, POST } from '@/services/request';
import { ClusterResult, CreateClusterParams } from '@/types';

export const createCluster = (payload: { type: ClusterType }) =>
POST('/api/cluster/create', payload);
export const createCluster = (payload: CreateClusterParams) => POST('/api/cluster/create', payload);

export const createClusterAndLicense = (payload: CreateClusterParams) =>
POST('/api/cluster/clusterAndLicense', payload);

export const getClusterRecord = ({ page, pageSize }: { page: number; pageSize: number }) =>
POST<{ total: number; records: ClusterDB[] }>('/api/cluster/getRecord', {
export const getClusterList = ({ page, pageSize }: { page: number; pageSize: number }) =>
POST<{ total: number; records: ClusterResult[] }>('/api/cluster/getClusterList', {
page,
pageSize
});

export const findClusterById = (payload: { clusterId: string }) =>
GET<ClusterResult>('/api/cluster/findById', payload);

export const updateClusterName = (payload: { clusterId: string; displayName: string }) =>
POST<ClusterResult>('/api/cluster/updateName', payload);

export const deleteClusterById = (payload: { clusterId: string }) =>
DELETE<ClusterResult>('/api/cluster/delete', payload);

export const isKubeSystemIDBound = (id: string) =>
GET<{ isBound: boolean }>('/api/cluster/isBound', { kubeSystemID: id });

export const activeClusterBySystemId = (payload: ActiveClusterParams) =>
POST<ClusterResult>('/api/cluster/activeCluster', payload);

export const findClusterBySystemId = (payload: { systemId: string }) =>
GET<ClusterResult>('/api/cluster/findBySystemId', payload);
19 changes: 18 additions & 1 deletion service/license/src/api/license.ts
@@ -1,4 +1,4 @@
import { POST } from '@/services/request';
import { GET, POST } from '@/services/request';
import { CreateLicenseParams, LicenseDB } from '@/types';

export const createLicense = (payload: CreateLicenseParams) => POST('/api/license/create', payload);
Expand All @@ -8,3 +8,20 @@ export const getLicenseRecord = ({ page, pageSize }: { page: number; pageSize: n
page,
pageSize
});

export const hasHistorical = () => GET('/api/license/hasHistorical');

export const getLicenseByClusterId = ({
page,
pageSize,
clusterId
}: {
page: number;
pageSize: number;
clusterId: string;
}) =>
POST<{ total: number; records: LicenseDB[] }>('/api/license/getByClusterId', {
page,
pageSize,
clusterId
});
10 changes: 5 additions & 5 deletions service/license/src/components/Icon/CheckIcon.tsx
Expand Up @@ -4,15 +4,15 @@ export const CheckIcon = (props: IconProps) => {
return (
<Icon
xmlns="http://www.w3.org/2000/svg"
width="21px"
height="21px"
viewBox="0 0 21 21"
width="20px"
height="20px"
viewBox="0 0 20 20"
fill="none"
{...props}
>
<path
d="M17.0667 5.22549L7.90007 14.3922L3.7334 10.2255"
stroke="#36ADEF"
d="M16.6667 5L7.50004 14.1667L3.33337 10"
stroke={'#36ADEF'}
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
Expand Down
15 changes: 15 additions & 0 deletions service/license/src/components/Icon/LeftIcon.tsx
@@ -0,0 +1,15 @@
import { Icon, IconProps } from '@chakra-ui/react';
export const LeftIcon = (props: IconProps) => {
return (
<Icon
xmlns="http://www.w3.org/2000/svg"
width="20px"
height="20px"
viewBox="0 0 20 20"
fill="#24282C"
{...props}
>
<path d="M12.25 17.5832L5.22917 10.5832C5.14583 10.4998 5.08694 10.4096 5.0525 10.3123C5.0175 10.2151 5 10.1109 5 9.99984C5 9.88873 5.0175 9.78456 5.0525 9.68734C5.08694 9.59012 5.14583 9.49984 5.22917 9.4165L12.25 2.39567C12.4444 2.20123 12.6875 2.104 12.9792 2.104C13.2708 2.104 13.5208 2.20817 13.7292 2.4165C13.9375 2.62484 14.0417 2.86789 14.0417 3.14567C14.0417 3.42345 13.9375 3.6665 13.7292 3.87484L7.60417 9.99984L13.7292 16.1248C13.9236 16.3193 14.0208 16.5587 14.0208 16.8432C14.0208 17.1282 13.9167 17.3748 13.7083 17.5832C13.5 17.7915 13.2569 17.8957 12.9792 17.8957C12.7014 17.8957 12.4583 17.7915 12.25 17.5832Z" />
</Icon>
);
};
24 changes: 24 additions & 0 deletions service/license/src/components/Icon/TimeIcon.tsx
@@ -0,0 +1,24 @@
import { Icon, IconProps } from '@chakra-ui/react';
export const TimeIcon = (props: IconProps) => {
return (
<Icon
xmlns="http://www.w3.org/2000/svg"
width="16px"
height="17px"
viewBox="0 0 16 17"
{...props}
>
<g clipPath="url(#clip0_877_36741)">
<path
d="M7.99992 1.8335C11.6819 1.8335 14.6666 4.81816 14.6666 8.50016C14.6666 12.1822 11.6819 15.1668 7.99992 15.1668C4.31792 15.1668 1.33325 12.1822 1.33325 8.50016C1.33325 4.81816 4.31792 1.8335 7.99992 1.8335ZM7.99992 3.16683C6.58543 3.16683 5.22888 3.72873 4.22868 4.72893C3.22849 5.72912 2.66659 7.08567 2.66659 8.50016C2.66659 9.91465 3.22849 11.2712 4.22868 12.2714C5.22888 13.2716 6.58543 13.8335 7.99992 13.8335C9.41441 13.8335 10.771 13.2716 11.7712 12.2714C12.7713 11.2712 13.3333 9.91465 13.3333 8.50016C13.3333 7.08567 12.7713 5.72912 11.7712 4.72893C10.771 3.72873 9.41441 3.16683 7.99992 3.16683ZM7.99992 4.50016C8.16321 4.50018 8.32081 4.56013 8.44283 4.66864C8.56486 4.77715 8.64281 4.92666 8.66192 5.08883L8.66658 5.16683V8.22416L10.4713 10.0288C10.5908 10.1488 10.6602 10.3098 10.6654 10.4791C10.6706 10.6484 10.6111 10.8133 10.4991 10.9403C10.3871 11.0674 10.2309 11.147 10.0623 11.1631C9.89365 11.1792 9.72525 11.1304 9.59125 11.0268L9.52858 10.9715L7.52858 8.9715C7.42497 8.86779 7.35843 8.73283 7.33925 8.5875L7.33325 8.50016V5.16683C7.33325 4.99002 7.40349 4.82045 7.52851 4.69542C7.65354 4.5704 7.82311 4.50016 7.99992 4.50016Z"
fill="#8172D8"
/>
</g>
<defs>
<clipPath id="clip0_877_36741">
<rect width="16" height="16" fill="white" transform="translate(0 0.5)" />
</clipPath>
</defs>
</Icon>
);
};
2 changes: 2 additions & 0 deletions service/license/src/components/Icon/index.ts
Expand Up @@ -33,3 +33,5 @@ export * from './CheckListIcon';
export * from './OfflineIcon';
export * from './OnlineComputerIcon';
export * from './CopyIcon';
export * from './TimeIcon';
export * from './LeftIcon';

0 comments on commit 94ac016

Please sign in to comment.