Skip to content

Commit

Permalink
feat:frontend license app (#4203)
Browse files Browse the repository at this point in the history
* feat:frontend license app

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

* fix

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

* fix

* fix detail

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

---------

Signed-off-by: jingyang <3161362058@qq.com>
  • Loading branch information
zjy365 authored Oct 30, 2023
1 parent c352224 commit fb52ae2
Show file tree
Hide file tree
Showing 28 changed files with 228 additions and 204 deletions.
3 changes: 2 additions & 1 deletion frontend/providers/license/.env.template
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
NEXT_PUBLIC_MOCK_USER=
SEALOS_DOMAIN="cloud.sealos.io"
LICENSE_DOMAIN="dev.sealos.top"
MONGODB_URI=
# Same as desktop important
PASSWORD_SALT=
# PASSWORD_SALT=
7 changes: 7 additions & 0 deletions frontend/providers/license/deploy/manifests/deploy.yaml.tmpl
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
apiVersion: v1
kind: Namespace
metadata:
labels:
app: license-frontend
name: license-frontend
---
apiVersion: v1
kind: ConfigMap
metadata:
name: license-frontend-config
Expand Down
12 changes: 7 additions & 5 deletions frontend/providers/license/public/locales/en/common.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
{
"Upload Token File": "Upload Token File",
"Upload Token File": "Upload License File",
"Activate License": "Activate License",
"Activation Record": "Activation Record",
"Purchase License": "Purchase License",
"Please go to": "Please go to",
"Purchase": "purchase a license.",
"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": "token does not exist",
"No Record": "No Record"
}
"token does not exist": "license does not exist",
"No Record": "No Record",
"Activation time": "Activation time",
"Activation Successful": "Activation Successful"
}
14 changes: 8 additions & 6 deletions frontend/providers/license/public/locales/zh/common.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
{
"Upload Token File": "上传 token 文件",
"Upload Token File": "上传 License 文件",
"Activate License": "激活 License",
"Activation Record": "激活记录",
"Purchase License": "购买 License",
"Please go to": "请到",
"Purchase": "购买 license",
"Please go to": "请前往",
"Purchase": "购买",
"Activation Time": "激活时间: ",
"Go to the message center to see the results": "前往消息中心查看结果",
"token does not exist": "token 不存在",
"No Record": "暂无记录"
}
"token does not exist": "license 不存在",
"No Record": "暂无记录",
"Activation time": "激活时间",
"Activation Successful": "激活成功"
}
4 changes: 2 additions & 2 deletions frontend/providers/license/src/api/license.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { GET, POST } from '@/services/request';
import { License } from '@/types';
import { LicenseRecord } from '@/types';

export const applyLicense = (yamlList: string[], type: 'create' | 'replace' | 'update') =>
POST('/api/applyYamlList', { yamlList, type });

export const getLicenseRecord = ({ page = 1, pageSize = 10 }: { page: number; pageSize: number }) =>
GET<{
totalCount: number;
items: License[];
items: LicenseRecord[];
}>('/api/license/getLicense', { page, pageSize });
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { DragEvent, useCallback, useState } from 'react';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 12);
const fileImgs = [
{
reg: /txt/gi,
reg: /yaml/gi,
src: (
<Icon viewBox="0 0 16 17" fill="#7B838B">
<path d="M9.33334 2.12411H4.00001C3.26667 2.12411 2.67334 2.72411 2.67334 3.45745L2.66667 14.1241C2.66667 14.8574 3.26 15.4574 3.99334 15.4574H12C12.7333 15.4574 13.3333 14.8574 13.3333 14.1241V6.12411L9.33334 2.12411ZM4.00001 14.1241V3.45745H8.66667V6.79078H12V14.1241H4.00001Z" />
Expand Down Expand Up @@ -49,15 +49,17 @@ const FileSelect = ({ fileExtension, setFiles, files, ...props }: Props) => {
// Parse file by file
let promise = Promise.resolve<FileItemType[]>([]);
files.forEach((file) => {
console.log(file, 'fiel');

promise = promise.then(async (result) => {
const extension = file?.name?.split('.')?.pop()?.toLowerCase();

/* text file */
const hardcodedRegex = /txt/gi;
const hardcodedRegex = /yaml/gi;
const icon = fileImgs.find((item) => hardcodedRegex.test(file.name))?.src;
let text = await (async () => {
switch (extension) {
case 'txt':
case 'yaml':
return readTxtContent(file);
}
return '';
Expand Down
12 changes: 4 additions & 8 deletions frontend/providers/license/src/pages/api/license/getLicense.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,15 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
const { kube_user } = await getK8s({
kubeconfig: await authSession(req)
});
const userId = kube_user.name;
console.log(userId, 'userId');
console.log(kube_user?.name, 'user get license record');

const skip = (parseInt(page) - 1) * parseInt(pageSize);

const license_collection = await connectToLicenseCollection();
const totalCountPipeline = [
{ $match: { uid: userId, 'meta.token': { $ne: '' } } },
{ $count: 'totalCount' }
];
const totalCountPipeline = [{ $match: { token: { $ne: '' } } }, { $count: 'totalCount' }];
const contentPipeline = [
{ $match: { uid: userId, 'meta.token': { $ne: '' } } },
{ $sort: { 'meta.createTime': -1 } },
{ $match: { token: { $ne: '' } } },
{ $sort: { activationTime: -1 } },
{ $skip: skip },
{ $limit: parseInt(pageSize) }
];
Expand Down
2 changes: 0 additions & 2 deletions frontend/providers/license/src/pages/api/platform/getEnv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,13 @@ import type { NextApiRequest, NextApiResponse } from 'next';

export type Response = {
domain?: string;
hid: string; // PASSWORD_SALT
LICENSE_DOMAIN: string;
};

export default async function handler(req: NextApiRequest, res: NextApiResponse<ApiResp>) {
jsonRes<Response>(res, {
data: {
domain: process.env.SEALOS_DOMAIN || 'cloud.sealos.io',
hid: hashCrypto(process.env.PASSWORD_SALT || ''),
LICENSE_DOMAIN: process.env.LICENSE_DOMAIN || 'cloud.sealos.io'
}
});
Expand Down
92 changes: 44 additions & 48 deletions frontend/providers/license/src/pages/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@ import { useToast } from '@/hooks/useToast';
import download from '@/utils/downloadFIle';
import { serviceSideProps } from '@/utils/i18n';
import { json2License } from '@/utils/json2Yaml';
import { useCopyData } from '@/utils/tools';
import { Box, Flex, Icon, Image, Text } from '@chakra-ui/react';
import { useMutation, useQuery } from '@tanstack/react-query';
import { addHoursToTime, formatTime, useCopyData } from '@/utils/tools';
import { Box, Flex, Icon, Image, Link, Text } from '@chakra-ui/react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { debounce } from 'lodash';
import { useTranslation } from 'next-i18next';
import { useState } from 'react';
Expand All @@ -23,32 +23,33 @@ export default function LicenseApp() {
const [pageSize, setPageSize] = useState(5);
const { copyData } = useCopyData();
const [purchaseLink, setPurchaseLink] = useState('');
const queryClient = useQueryClient();

const licenseMutation = useMutation({
mutationFn: (yamlList: string[]) => applyLicense(yamlList, 'create'),
onSuccess(data) {
console.log(data, 'data');
toast({
title: t('Go to the message center to see the results'),
title: t('Activation Successful'),
status: 'success'
});
queryClient.invalidateQueries(['getLicenseActive']);
},
onError(error) {
onError(error: { message?: string }) {
console.log(error);
if (error?.message && typeof error?.message === 'string') {
toast({
title: error.message,
status: 'error'
});
}
}
});

useQuery(['getPlatformEnv'], () => getPlatformEnv(), {
onSuccess(data) {
const hid = data.hid;
if (!hid) {
return toast({
title: 'env hid error',
status: 'error'
});
}
const encodedHid = encodeURIComponent(hid);
const main = data.LICENSE_DOMAIN;
const link = `https:/${main}/license?hid=${encodedHid}`;
const link = `https://${main}`;
setPurchaseLink(link);
}
});
Expand All @@ -68,16 +69,6 @@ export default function LicenseApp() {
licenseMutation.mutate(yamlList);
}, 500);

const copyLicenseLink = () => {
if (!purchaseLink) {
return toast({
title: 'env hid error',
status: 'error'
});
}
copyData(purchaseLink);
};

const downloadToken = (token: string) => {
const result = Buffer.from(token, 'binary').toString('base64');
download('token.txt', result);
Expand All @@ -102,23 +93,17 @@ export default function LicenseApp() {
src="/icons/license-bg.svg"
alt="license"
objectFit="cover"
borderRadius={'16px'}
/>
<Box position={'absolute'} color={'#FFF'} top={'180px'} left={'80px'}>
<Text fontSize={'32px'} fontWeight={600}>
{t('Purchase License')}
</Text>
<Flex mt="45px" cursor={'copy'} onClick={copyLicenseLink}>
<Flex mt="45px" cursor={'copy'}>
{t('Please go to')}
<Text
px="4px"
w="220px"
color={'#36ADEF'}
textOverflow="ellipsis"
whiteSpace="nowrap"
overflow="hidden"
>
{purchaseLink}
</Text>
<Link color={'#36ADEF'} px={'4px'} href={purchaseLink} isExternal>
Sealos License
</Link>
{t('Purchase')}
</Flex>
</Box>
Expand All @@ -132,7 +117,7 @@ export default function LicenseApp() {
<Text color={'#262A32'} fontSize={'24px'} fontWeight={600}>
{t('Activate License')}
</Text>
<FileSelect fileExtension={'.txt'} files={files} setFiles={setFiles} />
<FileSelect fileExtension={'.yaml'} files={files} setFiles={setFiles} />
<Flex
userSelect={'none'}
ml={'auto'}
Expand Down Expand Up @@ -174,12 +159,21 @@ export default function LicenseApp() {
</Text>
<CurrencySymbol />
<Text ml="6px" color={'#5A646E'} fontSize={'14px'} fontWeight={500}>
{license.payload?.amt}
{license?.claims?.data?.amount}
</Text>
<Text color={'#5A646E'} fontSize={'12px'} fontWeight={500} ml="auto">
{t('Activation time')} {license.meta.createTime}
<Text
color={'#5A646E'}
fontSize={'12px'}
fontWeight={500}
ml="auto"
mr={{
sm: '20px',
md: '34px'
}}
>
{t('Activation time')} {addHoursToTime(license?.activationTime || '')}
</Text>
<Flex
{/* <Flex
alignItems={'center'}
mx={{
sm: '8px',
Expand All @@ -192,14 +186,14 @@ export default function LicenseApp() {
fontSize={'14px'}
fontWeight={600}
px="8px"
onClick={() => downloadToken(license?.meta?.token)}
onClick={() => downloadToken(license?.token)}
>
Token
License
</Text>
<Icon fill="#1D8CDC" viewBox="0 0 16 16">
<path d="M4.76693 14.0667C4.60026 13.9 4.51693 13.7027 4.51693 13.4747C4.51693 13.2471 4.60026 13.05 4.76693 12.8833L9.65026 8L4.75026 3.1C4.59471 2.94444 4.51693 2.75 4.51693 2.51666C4.51693 2.28333 4.60026 2.08333 4.76693 1.91666C4.93359 1.75 5.13093 1.66666 5.35893 1.66666C5.58648 1.66666 5.78359 1.75 5.95026 1.91666L11.5503 7.53333C11.6169 7.6 11.6643 7.67222 11.6923 7.75C11.7198 7.82778 11.7336 7.91111 11.7336 8C11.7336 8.08889 11.7198 8.17222 11.6923 8.25C11.6643 8.32778 11.6169 8.4 11.5503 8.46666L5.93359 14.0833C5.77804 14.2389 5.58648 14.3167 5.35893 14.3167C5.13093 14.3167 4.93359 14.2333 4.76693 14.0667Z" />
</Icon>
</Flex>
</Flex> */}
</Flex>
))}
</Box>
Expand All @@ -219,11 +213,13 @@ export default function LicenseApp() {
</Flex>
)}

<Pagination
totalItems={data?.totalCount || 0}
itemsPerPage={pageSize}
onPageChange={(page: number) => setPage(page)}
/>
{data?.totalCount !== 0 && (
<Pagination
totalItems={data?.totalCount || 0}
itemsPerPage={pageSize}
onPageChange={(page: number) => setPage(page)}
/>
)}
</Box>
</Flex>
);
Expand Down
2 changes: 1 addition & 1 deletion frontend/providers/license/src/services/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ request.interceptors.response.use(

const apiResp = data as ApiResp;
if (apiResp.code < 200 || apiResp.code >= 400) {
return Promise.reject(apiResp.code + ':' + apiResp.message);
return Promise.reject(apiResp);
}

response.data = apiResp.data;
Expand Down
50 changes: 36 additions & 14 deletions frontend/providers/license/src/types/license.d.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,42 @@
export interface License {
_id: string;
uid: string;
meta: {
token: string;
createTime: string; // 激活时间
export type LicenseRecord = {
_id?: string;
token: string;
activationTime: string;
claims: {
type: string;
data: {
amount: number;
};
registeredclaims: {
issuer: string;
subject: string;
audience: null | string[];
expiresat: {
time: Date;
};
notbefore: null | string;
issuedat: {
time: Date;
};
id: string;
};
};

payload: {
iss: string;
iat: Date; // 签发日期
exp: Date; // 有效期
amt: number; // 额度
};
}
};

export type LicenseCollection = {
uid: string;
license: License[];
};

export type LicenseYaml = {
apiVersion: string;
kind: string;
metadata: {
name: string;
namespace: string;
};
spec: {
type: string;
token: string;
};
};
Loading

0 comments on commit fb52ae2

Please sign in to comment.