diff --git a/common/constants.go b/common/constants.go index 7895203f..722df3bc 100644 --- a/common/constants.go +++ b/common/constants.go @@ -91,7 +91,12 @@ const ( ) const ( - ChannelTypeUnknown = 0 - ChannelTypeOpenAI = 1 - ChannelTypeAPI2D = 2 + ChannelTypeUnknown = 0 + ChannelTypeOpenAI = 1 + ChannelTypeAPI2D = 2 + ChannelTypeAzure = 3 + ChannelTypeCloseAI = 4 + ChannelTypeOpenAISB = 5 + ChannelTypeOpenAIMax = 6 + ChannelTypeOhMyGPT = 7 ) diff --git a/controller/channel.go b/controller/channel.go index 6dd81b56..d6bc98d4 100644 --- a/controller/channel.go +++ b/controller/channel.go @@ -82,6 +82,8 @@ func AddChannel(c *gin.Context) { }) return } + channel.CreatedTime = common.GetTimestamp() + channel.AccessedTime = common.GetTimestamp() err = channel.Insert() if err != nil { c.JSON(http.StatusOK, gin.H{ @@ -136,6 +138,7 @@ func UpdateChannel(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "success": true, "message": "", + "data": channel, }) return } diff --git a/model/channel.go b/model/channel.go index 3b885adb..7c298ee2 100644 --- a/model/channel.go +++ b/model/channel.go @@ -9,7 +9,7 @@ type Channel struct { Type int `json:"type" gorm:"default:0"` Key string `json:"key"` Status int `json:"status" gorm:"default:1"` - Name string `json:"name" gorm:"unique;index"` + Name string `json:"name" gorm:"index"` Weight int `json:"weight"` CreatedTime int64 `json:"created_time" gorm:"bigint"` AccessedTime int64 `json:"accessed_time" gorm:"bigint"` diff --git a/web/src/components/ChannelsTable.js b/web/src/components/ChannelsTable.js index 744e44ba..40181d3c 100644 --- a/web/src/components/ChannelsTable.js +++ b/web/src/components/ChannelsTable.js @@ -1,40 +1,48 @@ import React, { useEffect, useState } from 'react'; import { Button, Form, Label, Pagination, Table } from 'semantic-ui-react'; import { Link } from 'react-router-dom'; -import { API, showError, showSuccess } from '../helpers'; +import { API, copy, showError, showSuccess, timestamp2string } from '../helpers'; -import { ITEMS_PER_PAGE } from '../constants'; +import { CHANNEL_OPTIONS, ITEMS_PER_PAGE } from '../constants'; -function renderRole(role) { - switch (role) { - case 1: - return ; - case 10: - return ; - case 100: - return ; - default: - return ; +function renderTimestamp(timestamp) { + return ( + <> + {timestamp2string(timestamp)} + + ); +} + +let type2label = undefined; + +function renderType(type) { + if (!type2label) { + type2label = new Map; + for (let i = 0; i < CHANNEL_OPTIONS.length; i++) { + type2label[CHANNEL_OPTIONS[i].value] = CHANNEL_OPTIONS[i]; + } + type2label[0] = { value: 0, text: '未知类型', color: 'grey' }; } + return ; } const ChannelsTable = () => { - const [users, setUsers] = useState([]); + const [channels, setChannels] = useState([]); const [loading, setLoading] = useState(true); const [activePage, setActivePage] = useState(1); const [searchKeyword, setSearchKeyword] = useState(''); const [searching, setSearching] = useState(false); - const loadUsers = async (startIdx) => { - const res = await API.get(`/api/user/?p=${startIdx}`); + const loadChannels = async (startIdx) => { + const res = await API.get(`/api/channel/?p=${startIdx}`); const { success, message, data } = res.data; if (success) { if (startIdx === 0) { - setUsers(data); + setChannels(data); } else { - let newUsers = users; - newUsers.push(...data); - setUsers(newUsers); + let newChannels = channels; + newChannels.push(...data); + setChannels(newChannels); } } else { showError(message); @@ -44,55 +52,63 @@ const ChannelsTable = () => { const onPaginationChange = (e, { activePage }) => { (async () => { - if (activePage === Math.ceil(users.length / ITEMS_PER_PAGE) + 1) { + if (activePage === Math.ceil(channels.length / ITEMS_PER_PAGE) + 1) { // In this case we have to load more data and then append them. - await loadUsers(activePage - 1); + await loadChannels(activePage - 1); } setActivePage(activePage); })(); }; useEffect(() => { - loadUsers(0) + loadChannels(0) .then() .catch((reason) => { showError(reason); }); }, []); - const manageUser = (username, action, idx) => { - (async () => { - const res = await API.post('/api/user/manage', { - username, - action, - }); - const { success, message } = res.data; - if (success) { - showSuccess('操作成功完成!'); - let user = res.data.data; - let newUsers = [...users]; - let realIdx = (activePage - 1) * ITEMS_PER_PAGE + idx; - if (action === 'delete') { - newUsers[realIdx].deleted = true; - } else { - newUsers[realIdx].status = user.status; - newUsers[realIdx].role = user.role; - } - setUsers(newUsers); + const manageChannel = async (id, action, idx) => { + let data = { id }; + let res; + switch (action) { + case 'delete': + res = await API.delete(`/api/channel/${id}/`); + break; + case 'enable': + data.status = 1; + res = await API.put('/api/channel/', data); + break; + case 'disable': + data.status = 2; + res = await API.put('/api/channel/', data); + break; + } + const { success, message } = res.data; + if (success) { + showSuccess('操作成功完成!'); + let channel = res.data.data; + let newChannels = [...channels]; + let realIdx = (activePage - 1) * ITEMS_PER_PAGE + idx; + if (action === 'delete') { + newChannels[realIdx].deleted = true; } else { - showError(message); + newChannels[realIdx].status = channel.status; } - })(); + setChannels(newChannels); + } else { + showError(message); + } }; const renderStatus = (status) => { switch (status) { case 1: - return ; + return ; case 2: return ( ); default: @@ -104,18 +120,18 @@ const ChannelsTable = () => { } }; - const searchUsers = async () => { + const searchChannels = async () => { if (searchKeyword === '') { // if keyword is blank, load files instead. - await loadUsers(0); + await loadChannels(0); setActivePage(1); return; } setSearching(true); - const res = await API.get(`/api/user/search?keyword=${searchKeyword}`); + const res = await API.get(`/api/channel/search?keyword=${searchKeyword}`); const { success, message, data } = res.data; if (success) { - setUsers(data); + setChannels(data); setActivePage(1); } else { showError(message); @@ -127,28 +143,28 @@ const ChannelsTable = () => { setSearchKeyword(value.trim()); }; - const sortUser = (key) => { - if (users.length === 0) return; + const sortChannel = (key) => { + if (channels.length === 0) return; setLoading(true); - let sortedUsers = [...users]; - sortedUsers.sort((a, b) => { + let sortedChannels = [...channels]; + sortedChannels.sort((a, b) => { return ('' + a[key]).localeCompare(b[key]); }); - if (sortedUsers[0].id === users[0].id) { - sortedUsers.reverse(); + if (sortedChannels[0].id === channels[0].id) { + sortedChannels.reverse(); } - setUsers(sortedUsers); + setChannels(sortedChannels); setLoading(false); }; return ( <> -
+ { { - sortUser('username'); + sortChannel('id'); }} > - 用户名 + ID { - sortUser('display_name'); + sortChannel('name'); }} > - 显示名称 + 名称 { - sortUser('email'); + sortChannel('type'); }} > - 邮箱地址 + 类型 { - sortUser('role'); - }} - > - 用户角色 - - { - sortUser('status'); + sortChannel('status'); }} > 状态 + { + sortChannel('created_time'); + }} + > + 创建时间 + + { + sortChannel('accessed_time'); + }} + > + 访问时间 + 操作 - {users + {channels .slice( (activePage - 1) * ITEMS_PER_PAGE, activePage * ITEMS_PER_PAGE ) - .map((user, idx) => { - if (user.deleted) return <>; + .map((channel, idx) => { + if (channel.deleted) return <>; return ( - - {user.username} - {user.display_name} - {user.email ? user.email : '无'} - {renderRole(user.role)} - {renderStatus(user.status)} + + {channel.id} + {channel.name ? channel.name : '无'} + {renderType(channel.type)} + {renderStatus(channel.status)} + {renderTimestamp(channel.created_time)} + {renderTimestamp(channel.accessed_time)}
- - @@ -275,8 +282,8 @@ const ChannelsTable = () => { - { size='small' siblingRange={1} totalPages={ - Math.ceil(users.length / ITEMS_PER_PAGE) + - (users.length % ITEMS_PER_PAGE === 0 ? 1 : 0) + Math.ceil(channels.length / ITEMS_PER_PAGE) + + (channels.length % ITEMS_PER_PAGE === 0 ? 1 : 0) } /> diff --git a/web/src/components/TokensTable.js b/web/src/components/TokensTable.js index 4065875a..59e6afd2 100644 --- a/web/src/components/TokensTable.js +++ b/web/src/components/TokensTable.js @@ -115,7 +115,7 @@ const TokensTable = () => { return; } setSearching(true); - const res = await API.get(`/api/token/search?keyword=${searchKeyword}/`); + const res = await API.get(`/api/token/search?keyword=${searchKeyword}`); const { success, message, data } = res.data; if (success) { setTokens(data); diff --git a/web/src/constants/channel.constants.js b/web/src/constants/channel.constants.js new file mode 100644 index 00000000..5cd3d1e3 --- /dev/null +++ b/web/src/constants/channel.constants.js @@ -0,0 +1,9 @@ +export const CHANNEL_OPTIONS = [ + { key: 1, text: 'OpenAI', value: 1, color: 'green' }, + { key: 2, text: 'API2D', value: 2, color: 'blue' }, + { key: 3, text: 'Azure', value: 3, color: 'olive' }, + { key: 4, text: 'CloseAI', value: 4, color: 'teal' }, + { key: 5, text: 'OpenAI-SB', value: 5, color: 'brown' }, + { key: 6, text: 'OpenAI Max', value: 6, color: 'violet' }, + { key: 7, text: 'OhMyGPT', value: 7, color: 'purple' } +]; diff --git a/web/src/constants/index.js b/web/src/constants/index.js index 8f9ba808..e83152bc 100644 --- a/web/src/constants/index.js +++ b/web/src/constants/index.js @@ -1,3 +1,4 @@ export * from './toast.constants'; export * from './user.constants'; -export * from './common.constant'; \ No newline at end of file +export * from './common.constant'; +export * from './channel.constants'; \ No newline at end of file diff --git a/web/src/pages/Channel/AddChannel.js b/web/src/pages/Channel/AddChannel.js index 1930eb75..8547f6b0 100644 --- a/web/src/pages/Channel/AddChannel.js +++ b/web/src/pages/Channel/AddChannel.js @@ -1,26 +1,27 @@ import React, { useState } from 'react'; import { Button, Form, Header, Segment } from 'semantic-ui-react'; import { API, showError, showSuccess } from '../../helpers'; +import { CHANNEL_OPTIONS } from '../../constants'; const AddChannel = () => { const originInputs = { - username: '', - display_name: '', - password: '', + name: '', + type: 1, + key: '' }; const [inputs, setInputs] = useState(originInputs); - const { username, display_name, password } = inputs; + const { name, type, key } = inputs; const handleInputChange = (e, { name, value }) => { setInputs((inputs) => ({ ...inputs, [name]: value })); }; const submit = async () => { - if (inputs.username === '' || inputs.password === '') return; - const res = await API.post(`/api/user/`, inputs); + if (inputs.name === '' || inputs.key === '') return; + const res = await API.post(`/api/channel/`, inputs); const { success, message } = res.data; if (success) { - showSuccess('用户账户创建成功!'); + showSuccess('渠道创建成功!'); setInputs(originInputs); } else { showError(message); @@ -30,38 +31,37 @@ const AddChannel = () => { return ( <> -
创建新用户账户
- +
创建新的渠道
+ + + + - - - diff --git a/web/src/pages/Channel/EditChannel.js b/web/src/pages/Channel/EditChannel.js index d4faea6b..0a6b6f1c 100644 --- a/web/src/pages/Channel/EditChannel.js +++ b/web/src/pages/Channel/EditChannel.js @@ -2,32 +2,23 @@ import React, { useEffect, useState } from 'react'; import { Button, Form, Header, Segment } from 'semantic-ui-react'; import { useParams } from 'react-router-dom'; import { API, showError, showSuccess } from '../../helpers'; +import { CHANNEL_OPTIONS } from '../../constants'; const EditChannel = () => { const params = useParams(); - const userId = params.id; + const channelId = params.id; const [loading, setLoading] = useState(true); const [inputs, setInputs] = useState({ - username: '', - display_name: '', - password: '', - github_id: '', - wechat_id: '', - email: '', + name: '', + key: '', + type: 1, }); - const { username, display_name, password, github_id, wechat_id, email } = - inputs; const handleInputChange = (e, { name, value }) => { setInputs((inputs) => ({ ...inputs, [name]: value })); }; - const loadUser = async () => { - let res = undefined; - if (userId) { - res = await API.get(`/api/user/${userId}`); - } else { - res = await API.get(`/api/user/self`); - } + const loadChannel = async () => { + let res = await API.get(`/api/channel/${channelId}`); const { success, message, data } = res.data; if (success) { data.password = ''; @@ -38,19 +29,14 @@ const EditChannel = () => { setLoading(false); }; useEffect(() => { - loadUser().then(); + loadChannel().then(); }, []); const submit = async () => { - let res = undefined; - if (userId) { - res = await API.put(`/api/user/`, { ...inputs, id: parseInt(userId) }); - } else { - res = await API.put(`/api/user/self`, inputs); - } + let res = await API.put(`/api/channel/`, { ...inputs, id: parseInt(channelId) }); const { success, message } = res.data; if (success) { - showSuccess('用户信息更新成功!'); + showSuccess('渠道更新成功!'); } else { showError(message); } @@ -59,69 +45,38 @@ const EditChannel = () => { return ( <> -
更新用户信息
+
更新渠道信息
- - - - - - - - - -