style: apikey create

This commit is contained in:
jialin
2024-07-01 17:13:23 +08:00
parent 0f9123ee4f
commit 13d71e28c9
10 changed files with 155 additions and 93 deletions
+4 -1
View File
@@ -7,24 +7,27 @@ type ModalFooterProps = {
okText?: string; okText?: string;
htmlType?: 'button' | 'submit'; htmlType?: 'button' | 'submit';
form?: any; form?: any;
loading?: boolean;
}; };
const ModalFooter: React.FC<ModalFooterProps> = ({ const ModalFooter: React.FC<ModalFooterProps> = ({
onOk, onOk,
onCancel, onCancel,
cancelText, cancelText,
okText, okText,
loading,
htmlType = 'button', htmlType = 'button',
form form
}) => { }) => {
return ( return (
<Space size={20}> <Space size={20}>
<Button onClick={onCancel} style={{ width: '88px' }}> <Button onClick={onCancel} style={{ width: '88px' }} loading={loading}>
{cancelText || '取消'} {cancelText || '取消'}
</Button> </Button>
<Button <Button
type="primary" type="primary"
onClick={onOk} onClick={onOk}
style={{ width: '88px' }} style={{ width: '88px' }}
loading={loading}
htmlType={htmlType} htmlType={htmlType}
> >
{okText || '保存'} {okText || '保存'}
+1
View File
@@ -16,6 +16,7 @@
.transition-content-wrapper { .transition-content-wrapper {
background-color: var(--color-fill-1); background-color: var(--color-fill-1);
padding: 8px;
} }
} }
+1 -1
View File
@@ -10,7 +10,7 @@ html {
--color-logs-bg: #1e1e1e; --color-logs-bg: #1e1e1e;
--color-logs-text: #d4d4d4; --color-logs-text: #d4d4d4;
--menu-border-radius-base: 8px; --menu-border-radius-base: 8px;
--border-radius-base: 16px; --border-radius-base: 8px;
--border-radius-middle: 20px; --border-radius-middle: 20px;
--border-radius-small: 8px; --border-radius-small: 8px;
--color-white-1: rgba(255, 255, 255, 100%); --color-white-1: rgba(255, 255, 255, 100%);
+1
View File
@@ -3,6 +3,7 @@ export default {
'apikeys.table.apikeys': 'keys', 'apikeys.table.apikeys': 'keys',
'apikeys.button.create': 'New API Key', 'apikeys.button.create': 'New API Key',
'apikeys.form.expiretime': 'Expiration', 'apikeys.form.expiretime': 'Expiration',
'apikeys.form.apikey': 'API Key',
'apikeys.table.name': 'Key Name', 'apikeys.table.name': 'Key Name',
'apikeys.table.save.tips': 'apikeys.table.save.tips':
'Make sure to copy your key immediately. You will not be able to see it again.', 'Make sure to copy your key immediately. You will not be able to see it again.',
+1
View File
@@ -3,6 +3,7 @@ export default {
'apikeys.table.apikeys': '密钥', 'apikeys.table.apikeys': '密钥',
'apikeys.button.create': '新建 API 密钥', 'apikeys.button.create': '新建 API 密钥',
'apikeys.form.expiretime': '过期时间', 'apikeys.form.expiretime': '过期时间',
'apikeys.form.apikey': 'API 密钥',
'apikeys.table.name': '密钥名称', 'apikeys.table.name': '密钥名称',
'apikeys.table.save.tips': '确保立即复制您的密钥。您将无法再次看到它!', 'apikeys.table.save.tips': '确保立即复制您的密钥。您将无法再次看到它!',
'apikeys.form.expiration.7days': '7天', 'apikeys.form.expiration.7days': '7天',
+132 -49
View File
@@ -1,10 +1,14 @@
import CopyButton from '@/components/copy-button';
import ModalFooter from '@/components/modal-footer'; import ModalFooter from '@/components/modal-footer';
import SealInput from '@/components/seal-form/seal-input'; import SealInput from '@/components/seal-form/seal-input';
import SealSelect from '@/components/seal-form/seal-select'; import SealSelect from '@/components/seal-form/seal-select';
import { PageAction } from '@/config'; import { PageAction } from '@/config';
import { PageActionType } from '@/config/types'; import { PageActionType } from '@/config/types';
import { useIntl } from '@umijs/max'; import { useIntl } from '@umijs/max';
import { Form, Modal, Select } from 'antd'; import { Button, Form, Modal, Select, Tag } from 'antd';
import dayjs from 'dayjs';
import { useState } from 'react';
import { createApisKey } from '../apis';
import { expirationOptions } from '../config'; import { expirationOptions } from '../config';
import { FormData } from '../config/types'; import { FormData } from '../config/types';
@@ -12,7 +16,7 @@ type AddModalProps = {
title: string; title: string;
action: PageActionType; action: PageActionType;
open: boolean; open: boolean;
onOk: (values: FormData) => void; onOk: () => void;
onCancel: () => void; onCancel: () => void;
}; };
@@ -25,6 +29,9 @@ const AddModal: React.FC<AddModalProps> = ({
}) => { }) => {
const [form] = Form.useForm(); const [form] = Form.useForm();
const intl = useIntl(); const intl = useIntl();
const [showKey, setShowKey] = useState(false);
const [apikeyValue, setAPIKeyValue] = useState('');
const [loading, setLoading] = useState(false);
if (action === PageAction.CREATE && open) { if (action === PageAction.CREATE && open) {
form.setFieldsValue({ form.setFieldsValue({
@@ -32,16 +39,59 @@ const AddModal: React.FC<AddModalProps> = ({
}); });
} }
const getExpireValue = (val: number | null) => {
const expires_in = val;
if (expires_in === -1) {
return 0;
}
const selected = expirationOptions.find(
(item) => expires_in === item.value
);
const d1 = dayjs().add(
selected?.value as number,
`${selected?.type}` as never
);
const d2 = dayjs();
const res = d1.diff(d2, 'second');
return res;
};
const handleOnOk = async (data: FormData) => {
try {
setLoading(true);
const params = {
...data,
expires_in: getExpireValue(data.expires_in)
};
const res = await createApisKey({ data: params });
setAPIKeyValue(res.value);
setShowKey(true);
setLoading(false);
} catch (error) {
setLoading(false);
}
};
const handleSumit = () => { const handleSumit = () => {
form.submit(); form.submit();
}; };
const handleDone = () => {
onOk();
};
const handleAfterOpenChange = (isOpen: boolean) => {
setShowKey(false);
};
return ( return (
<Modal <Modal
title={title} title={title}
open={open} open={open}
onOk={handleSumit} onOk={handleSumit}
onCancel={onCancel} onCancel={onCancel}
afterOpenChange={handleAfterOpenChange}
destroyOnClose={true} destroyOnClose={true}
closeIcon={false} closeIcon={false}
maskClosable={false} maskClosable={false}
@@ -49,61 +99,94 @@ const AddModal: React.FC<AddModalProps> = ({
width={600} width={600}
styles={{}} styles={{}}
footer={ footer={
<ModalFooter onOk={handleSumit} onCancel={onCancel}></ModalFooter> !showKey ? (
<ModalFooter
onOk={handleSumit}
onCancel={onCancel}
loading={loading}
></ModalFooter>
) : (
<Button type="primary" onClick={handleDone}>
{intl.formatMessage({ id: 'common.button.done' })}
</Button>
)
} }
> >
<Form name="addAPIKey" form={form} onFinish={onOk} preserve={false}> <Form name="addAPIKey" form={form} onFinish={handleOnOk} preserve={false}>
<Form.Item<FormData> {!showKey ? (
name="name" <>
rules={[ <Form.Item<FormData>
{ name="name"
required: true, rules={[
message: intl.formatMessage(
{ id: 'common.form.rule.input' },
{ {
name: intl.formatMessage({ id: 'common.table.name' }) required: true,
message: intl.formatMessage(
{ id: 'common.form.rule.input' },
{
name: intl.formatMessage({ id: 'common.table.name' })
}
)
} }
) ]}
} >
]} <SealInput.Input
> label={intl.formatMessage({ id: 'common.table.name' })}
<SealInput.Input required
label={intl.formatMessage({ id: 'common.table.name' })} ></SealInput.Input>
required </Form.Item>
></SealInput.Input> <Form.Item<FormData>
</Form.Item> name="expires_in"
<Form.Item<FormData> rules={[
name="expires_in"
rules={[
{
required: true,
message: intl.formatMessage(
{ id: 'common.form.rule.select' },
{ {
name: intl.formatMessage({ id: 'apikeys.form.expiretime' }) required: true,
message: intl.formatMessage(
{ id: 'common.form.rule.select' },
{
name: intl.formatMessage({
id: 'apikeys.form.expiretime'
})
}
)
} }
) ]}
>
<SealSelect
label={intl.formatMessage({ id: 'apikeys.form.expiretime' })}
required
>
{expirationOptions.map((option) => {
return (
<Select.Option key={option.value} value={option.value}>
{intl.formatMessage({ id: option.label })}
</Select.Option>
);
})}
</SealSelect>
</Form.Item>
<Form.Item<FormData>
name="description"
rules={[{ required: false }]}
>
<SealInput.TextArea
label={intl.formatMessage({ id: 'common.table.description' })}
></SealInput.TextArea>
</Form.Item>
</>
) : (
<Form.Item
extra={
<Tag color="error" style={{ padding: '6px 8px', marginTop: 10 }}>
{intl.formatMessage({ id: 'apikeys.table.save.tips' })}
</Tag>
} }
]}
>
<SealSelect
label={intl.formatMessage({ id: 'apikeys.form.expiretime' })}
required
> >
{expirationOptions.map((option) => { <SealInput.Input
return ( label={intl.formatMessage({ id: 'apikeys.form.apikey' })}
<Select.Option key={option.value} value={option.value}> value={apikeyValue}
{intl.formatMessage({ id: option.label })} addonAfter={<CopyButton text={apikeyValue}></CopyButton>}
</Select.Option> ></SealInput.Input>
); </Form.Item>
})} )}
</SealSelect>
</Form.Item>
<Form.Item<FormData> name="description" rules={[{ required: false }]}>
<SealInput.TextArea
label={intl.formatMessage({ id: 'common.table.description' })}
></SealInput.TextArea>
</Form.Item>
</Form> </Form>
</Modal> </Modal>
); );
+5 -34
View File
@@ -21,10 +21,9 @@ import {
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import _ from 'lodash'; import _ from 'lodash';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { createApisKey, deleteApisKey, queryApisKeysList } from './apis'; import { deleteApisKey, queryApisKeysList } from './apis';
import AddAPIKeyModal from './components/add-apikey'; import AddAPIKeyModal from './components/add-apikey';
import { expirationOptions } from './config'; import { ListItem } from './config/types';
import { FormData, ListItem } from './config/types';
const { Column } = Table; const { Column } = Table;
@@ -67,24 +66,6 @@ const Models: React.FC = () => {
setSortOrder(sorter.order); setSortOrder(sorter.order);
}; };
const getExpireValue = (val: number | null) => {
const expires_in = val;
if (expires_in === -1) {
return 0;
}
const selected = expirationOptions.find(
(item) => expires_in === item.value
);
const d1 = dayjs().add(
selected?.value as number,
`${selected?.type}` as never
);
const d2 = dayjs();
const res = d1.diff(d2, 'second');
return res;
};
const fetchData = async () => { const fetchData = async () => {
setLoading(true); setLoading(true);
try { try {
@@ -118,19 +99,10 @@ const Models: React.FC = () => {
setAction(PageAction.CREATE); setAction(PageAction.CREATE);
}; };
const handleModalOk = async (data: FormData) => { const handleModalOk = async () => {
console.log('handleModalOk');
try { try {
const params = {
...data,
expires_in: getExpireValue(data.expires_in)
};
const res = await createApisKey({ data: params });
setOpenAddModal(false); setOpenAddModal(false);
message.success(intl.formatMessage({ id: 'common.message.success' })); fetchData();
setDataSource([res, ...dataSource]);
setTotal(total + 1);
} catch (error) { } catch (error) {
setOpenAddModal(false); setOpenAddModal(false);
} }
@@ -273,9 +245,8 @@ const Models: React.FC = () => {
key="name" key="name"
width={400} width={400}
ellipsis={{ ellipsis={{
showTitle: false showTitle: true
}} }}
render={renderSecrectKey}
/> />
<Column <Column
+1 -1
View File
@@ -1,6 +1,6 @@
export const overviewConfigs = [ export const overviewConfigs = [
{ {
key: 'workworker_count', key: 'worker_count',
label: 'dashboard.workers', label: 'dashboard.workers',
// backgroundColor: 'linear-gradient(135deg, #ffffff, rgb(232 249 240 / 60%))' // backgroundColor: 'linear-gradient(135deg, #ffffff, rgb(232 249 240 / 60%))'
backgroundColor: 'var(--color-white-1)' backgroundColor: 'var(--color-white-1)'
+6 -4
View File
@@ -464,10 +464,12 @@ const Models: React.FC = () => {
</span> </span>
</Col> </Col>
<Col span={5}> <Col span={5}>
<DropdownButtons <div style={{ paddingLeft: 39 }}>
items={childActionList} <DropdownButtons
onSelect={(val) => handleChildSelect(val, item)} items={childActionList}
></DropdownButtons> onSelect={(val) => handleChildSelect(val, item)}
></DropdownButtons>
</div>
</Col> </Col>
</Row> </Row>
</RowChildren> </RowChildren>
@@ -53,7 +53,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
const systemList = systemMessage const systemList = systemMessage
? [{ role: 'system', content: systemMessage }] ? [{ role: 'system', content: systemMessage }]
: []; : [];
const code = `curl ${window.location.origin}/v1/chat/completions \n-H "Content-Type: application/json" \n-H "Authorization: Bearer $\{GPUSTACK_API_KEY}" \n-d '${JSON.stringify( const code = `curl ${window.location.origin}/v1-openai/chat/completions \\ \n-H "Content-Type: application/json" \\\n-H "Authorization: Bearer $\{GPUSTACK_API_KEY}" \\\n-d '${JSON.stringify(
{ {
...parameters, ...parameters,
messages: [...systemList, ...messageList] messages: [...systemList, ...messageList]
@@ -66,7 +66,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
const systemList = systemMessage const systemList = systemMessage
? [{ role: 'system', content: systemMessage }] ? [{ role: 'system', content: systemMessage }]
: []; : [];
const code = `import OpenAI from "openai";\nconst openai = new OpenAI();\n\nasync function main(){\nconst params = ${JSON.stringify( const code = `import OpenAI from "openai";\nconst openai = new OpenAI({\n"base_url": "/v1-openai", \n "gpustack_api_key": "$\{GPUSTACK_API_KEY}"\n });\n\nasync function main(){\nconst params = ${JSON.stringify(
{ {
...parameters, ...parameters,
messages: [...systemList, ...messageList] messages: [...systemList, ...messageList]
@@ -92,7 +92,7 @@ const ViewCodeModal: React.FC<ViewModalProps> = (props) => {
const systemList = systemMessage const systemList = systemMessage
? [{ role: 'system', content: systemMessage }] ? [{ role: 'system', content: systemMessage }]
: []; : [];
const code = `from openai import OpenAI\nclient = OpenAI()\n\ncompletion = client.chat.completions.create(\n${formattedParams} messages=${JSON.stringify([...systemList, ...messageList], null, 2)})\nprint(completion.choices[0].message)`; const code = `from openai import OpenAI\nclient = OpenAI({\n "base_url": "/v1-openai", \n "gpustack_api_key": "$\{GPUSTACK_API_KEY}"\n })\n\ncompletion = client.chat.completions.create(\n${formattedParams} messages=${JSON.stringify([...systemList, ...messageList], null, 2)})\nprint(completion.choices[0].message)`;
setCodeValue(code); setCodeValue(code);
} }
formatCode(); formatCode();