Merge pull request #236 from safe1ine/main

增加了前端授权激活的功能
This commit is contained in:
safe1ine
2025-08-07 23:51:27 +08:00
committed by GitHub
8 changed files with 361 additions and 55 deletions

74
ui/src/api/License.ts Normal file
View File

@@ -0,0 +1,74 @@
/* eslint-disable */
/* tslint:disable */
// @ts-nocheck
/*
* ---------------------------------------------------------------
* ## THIS FILE WAS GENERATED VIA SWAGGER-TYPESCRIPT-API ##
* ## ##
* ## AUTHOR: acacode ##
* ## SOURCE: https://github.com/acacode/swagger-typescript-api ##
* ---------------------------------------------------------------
*/
import request, { ContentType, RequestParams } from "./httpClient";
import {
GithubComChaitinMonkeyCodeBackendProDomainLicenseResp,
V1LicenseCreatePayload,
WebResp,
} from "./types";
/**
* @description Get license
*
* @tags license
* @name V1LicenseList
* @summary Get license
* @request GET:/api/v1/license
* @response `200` `(WebResp & {
data?: GithubComChaitinMonkeyCodeBackendProDomainLicenseResp,
})` OK
*/
export const v1LicenseList = (params: RequestParams = {}) =>
request<
WebResp & {
data?: GithubComChaitinMonkeyCodeBackendProDomainLicenseResp;
}
>({
path: `/api/v1/license`,
method: "GET",
type: ContentType.Json,
format: "json",
...params,
});
/**
* @description Upload license
*
* @tags license
* @name V1LicenseCreate
* @summary Upload license
* @request POST:/api/v1/license
* @response `200` `(WebResp & {
data?: GithubComChaitinMonkeyCodeBackendProDomainLicenseResp,
})` OK
*/
export const v1LicenseCreate = (
data: V1LicenseCreatePayload,
params: RequestParams = {},
) =>
request<
WebResp & {
data?: GithubComChaitinMonkeyCodeBackendProDomainLicenseResp;
}
>({
path: `/api/v1/license`,
method: "POST",
body: data,
type: ContentType.FormData,
format: "json",
...params,
});

View File

@@ -3,6 +3,7 @@ export * from './Billing'
export * from './Cli'
export * from './CodeSnippet'
export * from './Dashboard'
export * from './License'
export * from './Model'
export * from './OpenAiv1'
export * from './SecurityScanning'

View File

@@ -1179,6 +1179,13 @@ export interface GithubComChaitinMonkeyCodeBackendEntTypesPosition {
offset?: number;
}
export interface GithubComChaitinMonkeyCodeBackendProDomainLicenseResp {
edition?: number;
expired_at?: number;
started_at?: number;
state?: number;
}
export interface InternalCodesnippetHandlerHttpV1GetContextReq {
/** 返回结果数量限制默认10 */
limit?: number;
@@ -1372,6 +1379,18 @@ export interface GetUserStatDashboardParams {
user_id?: string;
}
export interface V1LicenseCreatePayload {
/** license type */
license_type: "file" | "code";
/**
* license file
* @format binary
*/
license_file: File;
/** license code */
license_code: string;
}
export interface DeleteDeleteModelParams {
/** 模型ID */
id: string;

View File

@@ -0,0 +1,89 @@
import dayjs from 'dayjs';
import { useState } from 'react';
import { Ellipsis, Modal } from '@c-x/ui';
import { Box, Button, Link, Stack } from '@mui/material';
import { GithubComChaitinMonkeyCodeBackendProDomainLicenseResp } from '@/api/types';
import ChangeLicense from './changeLicense';
interface LicenseModalProps {
open: boolean;
onClose: () => void;
curVersion: string;
latestVersion: string;
license: GithubComChaitinMonkeyCodeBackendProDomainLicenseResp | undefined;
}
const AboutModal = ({
open,
onClose,
curVersion,
latestVersion,
license
}: LicenseModalProps) => {
const [openChangeLicense, setOpenChangeLicense] = useState(false);
const editionText = (edition: any) => {
if (edition === 0) {
return '开源版'
} else if (edition === 1) {
return '联创版'
} else if (edition === 2) {
return '企业版'
} else {
return '未知'
}
}
return (
<Modal
title='关于 MonkeyCode'
width={600}
open={open}
onCancel={onClose}
footer={null}>
<Stack direction={'column'} gap={2} sx={{
fontSize: '14px'
}}>
<Stack direction={'row'}>
<Box sx={{
width: '120px'
}}></Box>
<Box sx={{
width: '120px',
fontWeight: 700
}}>{curVersion}</Box>
</Stack>
<Stack direction={'row'}>
<Box sx={{
width: '120px',
}}></Box>
<Box sx={{
mr: '20px'
}}>{editionText(license?.edition)}</Box>
<Link href="#" sx={{
color: 'info.main',
'&:hover': {
fontWeight: 700
}
}}
onClick={() => {
setOpenChangeLicense(true);
}}></Link>
</Stack>
{license && license?.edition !== 0 && <Stack direction={'row'}>
<Box sx={{
width: '120px'
}}></Box>
<Box sx={{
}}>{dayjs.unix(license.started_at!).format('YYYY-MM-DD')} ~ {dayjs.unix(license.expired_at!).format('YYYY-MM-DD')}</Box>
</Stack>}
</Stack>
<ChangeLicense
open={openChangeLicense}
onClose={() => {setOpenChangeLicense(false)}} />
</Modal>
);
};
export default AboutModal;

View File

@@ -0,0 +1,82 @@
import dayjs from 'dayjs';
import { useState } from 'react';
import { Ellipsis, message, Modal } from '@c-x/ui';
import { Box, Button, Link, Stack, TextField } from '@mui/material';
import { GithubComChaitinMonkeyCodeBackendProDomainLicenseResp } from '@/api/types';
import { v1LicenseCreate } from '@/api';
interface LicenseModalProps {
open: boolean;
onClose: () => void;
}
const ChangeLicense = ({
open,
onClose
}: LicenseModalProps) => {
const [code, setCode] = useState('');
const [verifing, setVerifing] = useState(false);
const updateLicense = () => {
if (code.length === 0) {
message.error("授权码不能为空");
return;
}
setVerifing(true);
v1LicenseCreate({
license_type: 'code',
license_code: code,
license_file: '' as any
}).then((resp: GithubComChaitinMonkeyCodeBackendProDomainLicenseResp) => {
message.success("切换授权成功");
console.log(resp)
setVerifing(false);
onClose();
setTimeout(() => {
location.reload();
}, 1000)
}).catch(() => {
message.error("遇到一点意外,无法激活");
setVerifing(false);
})
}
return (
<Modal
title='切换 MonkeyCode 授权'
width={400}
open={open}
onCancel={onClose}
footer={null}>
<Stack direction={'column'} gap={2} sx={{
fontSize: '14px'
}}>
<Stack direction={'row'} gap={2} sx={{
fontSize: '14px'
}}>
<TextField label="授权码" variant="outlined" sx={{
width: '100%'
}}
onChange={(e) => {
setCode(e.target.value);
}} />
</Stack>
<Stack direction={'row'} gap={2} sx={{
fontSize: '14px'
}}>
<Button variant="contained"
loading={verifing}
sx={{
width: '100%'
}}
onClick={() => {
updateLicense()
}}>线</Button>
</Stack>
</Stack>
</Modal>
);
};
export default ChangeLicense;

View File

@@ -2,16 +2,32 @@ import HelpCenter from '@/assets/json/help-center.json';
import IconUpgrade from '@/assets/json/upgrade.json';
import LottieIcon from '@/components/lottieIcon';
import { Box, Stack, Tooltip } from '@mui/material';
import { Icon } from '@c-x/ui';
import { useEffect, useState } from 'react';
import packageJson from '../../../package.json';
import DiamondIcon from '@mui/icons-material/Diamond';
import { useRequest } from 'ahooks';
import { v1LicenseList } from '@/api/License';
import AboutModal from './aboutModal';
const Version = () => {
const curVersion =
import.meta.env.VITE_APP_VERSION || `v${packageJson.version}`;
const [latestVersion, setLatestVersion] = useState<string | undefined>(
undefined
);
const curVersion = import.meta.env.VITE_APP_VERSION || packageJson.version
const [latestVersion, setLatestVersion] = useState<string | undefined>(undefined)
const [licenseModalOpen, setLicenseModalOpen] = useState(false)
const license = useRequest(() => {
return v1LicenseList({})
}).data
const editionText = (edition: any) => {
if (edition === 0) {
return '开源版'
} else if (edition === 1) {
return '联创版'
} else if (edition === 2) {
return '企业版'
} else {
return '未知'
}
}
useEffect(() => {
fetch('https://release.baizhi.cloud/monkeycode/version.txt')
@@ -25,57 +41,64 @@ const Version = () => {
});
}, []);
// if (latestVersion === undefined) return null;
return (
<Stack
justifyContent={'center'}
alignItems={'center'}
gap={0.5}
sx={{
<>
<Stack justifyContent={'center'} gap={0.5} sx={{
borderTop: '1px solid',
borderColor: 'divider',
pt: 2,
p: 0,
mt: 1,
cursor: 'pointer',
color: 'text.primary',
fontSize: 12,
'&:hover': {
color: 'primary.main',
},
}}
onClick={() => {
window.open('https://monkeycode.docs.baizhi.cloud/welcome');
}}
>
<Stack direction={'row'} alignItems={'center'} gap={0.5}>
<Icon type='icon-banben' sx={{ fontSize: 16, color: 'success.main' }} />
}} onClick={() => setLicenseModalOpen(true)}>
<Box sx={{
borderRadius: '8px',
p: 1,
mt: 1,
mb: -1,
'&:hover': {
backgroundColor: 'background.paper'
}
}}>
<Stack direction={'row'} alignItems="center" gap={0.5}>
<Box sx={{ width: 40, color: 'text.auxiliary' }}></Box>
<DiamondIcon style={{
width: 13,
height: 13,
color: license && license.edition === 0 ? 'rgba(33,34,45, 0.2)' : '#35B37E'
}} />
{editionText(license?.edition)}
</Stack>
<Stack direction={'row'} gap={0.5}>
<Box sx={{ width: 40, color: 'text.auxiliary' }}></Box>
<Box sx={{ whiteSpace: 'nowrap' }}>{curVersion}</Box>
{latestVersion !== `v${curVersion}` && <Tooltip
placement='top'
arrow
title={latestVersion === '' ? '无法获取最新版本' : '检测到新版本,点击查看'}
>
<Box>
<LottieIcon
id='version'
src={latestVersion === '' ? HelpCenter : IconUpgrade}
style={{ width: 16, height: 16 }}
/>
</Box>
</Tooltip>}
</Stack>
</Box>
</Stack>
<Stack direction={'row'} alignItems={'center'} gap={0.5}>
<Box sx={{ whiteSpace: 'nowrap' }}>{curVersion}</Box>
{latestVersion !== `${curVersion}` && (
<Tooltip
placement='top'
arrow
title={
latestVersion === ''
? '无法获取最新版本'
: '检测到新版本,点击查看'
}
>
<Box>
<LottieIcon
id='version'
src={latestVersion === '' ? HelpCenter : IconUpgrade}
style={{ width: 16, height: 16 }}
/>
</Box>
</Tooltip>
)}
</Stack>
</Stack>
);
<AboutModal
open={licenseModalOpen}
onClose={() => setLicenseModalOpen(false)}
latestVersion={latestVersion || ''}
curVersion={curVersion}
license={license} />
</>
)
};
export default Version;

View File

@@ -152,7 +152,16 @@ const Completion = () => {
title: '时间',
width: 200,
render(value: number) {
return dayjs.unix(value).format('YYYY-MM-DD HH:mm:ss');
return (
<Stack direction='column'>
<Box sx={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
{dayjs.unix(value).format('YYYY-MM-DD')}
</Box>
<Box sx={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
{dayjs.unix(value).format('HH:mm:ss')}
</Box>
</Stack>
)
},
},
];

View File

@@ -62,7 +62,7 @@ const Chat = () => {
{
dataIndex: 'user',
title: '成员',
width: 260,
width: 200,
render(value: DomainUser) {
return (
<User
@@ -133,7 +133,7 @@ const Chat = () => {
{
dataIndex: 'input_tokens',
title: '输入 Token',
width: 150,
width: 120,
render(value: number) {
return addCommasToNumber(value);
},
@@ -141,7 +141,7 @@ const Chat = () => {
{
dataIndex: 'output_tokens',
title: '输出 Token',
width: 150,
width: 120,
render(value: number) {
return addCommasToNumber(value);
},
@@ -149,9 +149,18 @@ const Chat = () => {
{
dataIndex: 'created_at',
title: '时间',
width: 180,
width: 160,
render(value: number) {
return dayjs.unix(value).format('YYYY-MM-DD HH:mm:ss');
return (
<Stack direction='column'>
<Box sx={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
{dayjs.unix(value).format('YYYY-MM-DD')}
</Box>
<Box sx={{ whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
{dayjs.unix(value).format('HH:mm:ss')}
</Box>
</Stack>
)
},
},
];