| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| import { useState, useEffect } from 'react'; |
| import { useTranslation } from 'react-i18next'; |
| import { Modal } from '@douyinfe/semi-ui'; |
| import { |
| API, |
| copy, |
| showError, |
| showSuccess, |
| encodeToBase64, |
| } from '../../helpers'; |
| import { ITEMS_PER_PAGE } from '../../constants'; |
| import { useTableCompactMode } from '../common/useTableCompactMode'; |
|
|
| export const useTokensData = (openFluentNotification) => { |
| const { t } = useTranslation(); |
|
|
| |
| const [tokens, setTokens] = useState([]); |
| const [loading, setLoading] = useState(true); |
| const [activePage, setActivePage] = useState(1); |
| const [tokenCount, setTokenCount] = useState(0); |
| const [pageSize, setPageSize] = useState(ITEMS_PER_PAGE); |
| const [searching, setSearching] = useState(false); |
|
|
| |
| const [selectedKeys, setSelectedKeys] = useState([]); |
|
|
| |
| const [showEdit, setShowEdit] = useState(false); |
| const [editingToken, setEditingToken] = useState({ |
| id: undefined, |
| }); |
|
|
| |
| const [compactMode, setCompactMode] = useTableCompactMode('tokens'); |
| const [showKeys, setShowKeys] = useState({}); |
|
|
| |
| const [formApi, setFormApi] = useState(null); |
| const formInitValues = { |
| searchKeyword: '', |
| searchToken: '', |
| }; |
|
|
| |
| const getFormValues = () => { |
| const formValues = formApi ? formApi.getValues() : {}; |
| return { |
| searchKeyword: formValues.searchKeyword || '', |
| searchToken: formValues.searchToken || '', |
| }; |
| }; |
|
|
| |
| const closeEdit = () => { |
| setShowEdit(false); |
| setTimeout(() => { |
| setEditingToken({ |
| id: undefined, |
| }); |
| }, 500); |
| }; |
|
|
| |
| const syncPageData = (payload) => { |
| setTokens(payload.items || []); |
| setTokenCount(payload.total || 0); |
| setActivePage(payload.page || 1); |
| setPageSize(payload.page_size || pageSize); |
| }; |
|
|
| |
| const loadTokens = async (page = 1, size = pageSize) => { |
| setLoading(true); |
| const res = await API.get(`/api/token/?p=${page}&size=${size}`); |
| const { success, message, data } = res.data; |
| if (success) { |
| syncPageData(data); |
| } else { |
| showError(message); |
| } |
| setLoading(false); |
| }; |
|
|
| |
| const refresh = async (page = activePage) => { |
| await loadTokens(page); |
| setSelectedKeys([]); |
| }; |
|
|
| |
| const copyText = async (text) => { |
| if (await copy(text)) { |
| showSuccess(t('已复制到剪贴板!')); |
| } else { |
| Modal.error({ |
| title: t('无法复制到剪贴板,请手动复制'), |
| content: text, |
| size: 'large', |
| }); |
| } |
| }; |
|
|
| |
| const onOpenLink = async (type, url, record) => { |
| if (url && url.startsWith('fluent')) { |
| openFluentNotification(record.key); |
| return; |
| } |
| let status = localStorage.getItem('status'); |
| let serverAddress = ''; |
| if (status) { |
| status = JSON.parse(status); |
| serverAddress = status.server_address; |
| } |
| if (serverAddress === '') { |
| serverAddress = window.location.origin; |
| } |
| if (url.includes('{cherryConfig}') === true) { |
| let cherryConfig = { |
| id: 'new-api', |
| baseUrl: serverAddress, |
| apiKey: 'sk-' + record.key, |
| }; |
| let encodedConfig = encodeURIComponent( |
| encodeToBase64(JSON.stringify(cherryConfig)), |
| ); |
| url = url.replaceAll('{cherryConfig}', encodedConfig); |
| } else { |
| let encodedServerAddress = encodeURIComponent(serverAddress); |
| url = url.replaceAll('{address}', encodedServerAddress); |
| url = url.replaceAll('{key}', 'sk-' + record.key); |
| } |
|
|
| window.open(url, '_blank'); |
| }; |
|
|
| |
| const manageToken = async (id, action, record) => { |
| setLoading(true); |
| let data = { id }; |
| let res; |
| switch (action) { |
| case 'delete': |
| res = await API.delete(`/api/token/${id}/`); |
| break; |
| case 'enable': |
| data.status = 1; |
| res = await API.put('/api/token/?status_only=true', data); |
| break; |
| case 'disable': |
| data.status = 2; |
| res = await API.put('/api/token/?status_only=true', data); |
| break; |
| } |
| const { success, message } = res.data; |
| if (success) { |
| showSuccess('操作成功完成!'); |
| let token = res.data.data; |
| let newTokens = [...tokens]; |
| if (action !== 'delete') { |
| record.status = token.status; |
| } |
| setTokens(newTokens); |
| } else { |
| showError(message); |
| } |
| setLoading(false); |
| }; |
|
|
| |
| const searchTokens = async () => { |
| const { searchKeyword, searchToken } = getFormValues(); |
| if (searchKeyword === '' && searchToken === '') { |
| await loadTokens(1); |
| return; |
| } |
| setSearching(true); |
| const res = await API.get( |
| `/api/token/search?keyword=${searchKeyword}&token=${searchToken}`, |
| ); |
| const { success, message, data } = res.data; |
| if (success) { |
| setTokens(data); |
| setTokenCount(data.length); |
| setActivePage(1); |
| } else { |
| showError(message); |
| } |
| setSearching(false); |
| }; |
|
|
| |
| const sortToken = (key) => { |
| if (tokens.length === 0) return; |
| setLoading(true); |
| let sortedTokens = [...tokens]; |
| sortedTokens.sort((a, b) => { |
| return ('' + a[key]).localeCompare(b[key]); |
| }); |
| if (sortedTokens[0].id === tokens[0].id) { |
| sortedTokens.reverse(); |
| } |
| setTokens(sortedTokens); |
| setLoading(false); |
| }; |
|
|
| |
| const handlePageChange = (page) => { |
| loadTokens(page, pageSize).then(); |
| }; |
|
|
| const handlePageSizeChange = async (size) => { |
| setPageSize(size); |
| await loadTokens(1, size); |
| }; |
|
|
| |
| const rowSelection = { |
| onSelect: (record, selected) => {}, |
| onSelectAll: (selected, selectedRows) => {}, |
| onChange: (selectedRowKeys, selectedRows) => { |
| setSelectedKeys(selectedRows); |
| }, |
| }; |
|
|
| |
| const handleRow = (record, index) => { |
| if (record.status !== 1) { |
| return { |
| style: { |
| background: 'var(--semi-color-disabled-border)', |
| }, |
| }; |
| } else { |
| return {}; |
| } |
| }; |
|
|
| |
| const batchDeleteTokens = async () => { |
| if (selectedKeys.length === 0) { |
| showError(t('请先选择要删除的令牌!')); |
| return; |
| } |
| setLoading(true); |
| try { |
| const ids = selectedKeys.map((token) => token.id); |
| const res = await API.post('/api/token/batch', { ids }); |
| if (res?.data?.success) { |
| const count = res.data.data || 0; |
| showSuccess(t('已删除 {{count}} 个令牌!', { count })); |
| await refresh(); |
| setTimeout(() => { |
| if (tokens.length === 0 && activePage > 1) { |
| refresh(activePage - 1); |
| } |
| }, 100); |
| } else { |
| showError(res?.data?.message || t('删除失败')); |
| } |
| } catch (error) { |
| showError(error.message); |
| } finally { |
| setLoading(false); |
| } |
| }; |
|
|
| |
| const batchCopyTokens = (copyType) => { |
| if (selectedKeys.length === 0) { |
| showError(t('请至少选择一个令牌!')); |
| return; |
| } |
|
|
| Modal.info({ |
| title: t('复制令牌'), |
| icon: null, |
| content: t('请选择你的复制方式'), |
| footer: ( |
| <div className='flex gap-2'> |
| <button |
| className='px-3 py-1 bg-gray-200 rounded' |
| onClick={async () => { |
| let content = ''; |
| for (let i = 0; i < selectedKeys.length; i++) { |
| content += |
| selectedKeys[i].name + ' sk-' + selectedKeys[i].key + '\n'; |
| } |
| await copyText(content); |
| Modal.destroyAll(); |
| }} |
| > |
| {t('名称+密钥')} |
| </button> |
| <button |
| className='px-3 py-1 bg-blue-500 text-white rounded' |
| onClick={async () => { |
| let content = ''; |
| for (let i = 0; i < selectedKeys.length; i++) { |
| content += 'sk-' + selectedKeys[i].key + '\n'; |
| } |
| await copyText(content); |
| Modal.destroyAll(); |
| }} |
| > |
| {t('仅密钥')} |
| </button> |
| </div> |
| ), |
| }); |
| }; |
|
|
| |
| useEffect(() => { |
| loadTokens(1) |
| .then() |
| .catch((reason) => { |
| showError(reason); |
| }); |
| }, [pageSize]); |
|
|
| return { |
| |
| tokens, |
| loading, |
| activePage, |
| tokenCount, |
| pageSize, |
| searching, |
|
|
| |
| selectedKeys, |
| setSelectedKeys, |
|
|
| |
| showEdit, |
| setShowEdit, |
| editingToken, |
| setEditingToken, |
| closeEdit, |
|
|
| |
| compactMode, |
| setCompactMode, |
| showKeys, |
| setShowKeys, |
|
|
| |
| formApi, |
| setFormApi, |
| formInitValues, |
| getFormValues, |
|
|
| |
| loadTokens, |
| refresh, |
| copyText, |
| onOpenLink, |
| manageToken, |
| searchTokens, |
| sortToken, |
| handlePageChange, |
| handlePageSizeChange, |
| rowSelection, |
| handleRow, |
| batchDeleteTokens, |
| batchCopyTokens, |
| syncPageData, |
|
|
| |
| t, |
| }; |
| }; |
|
|