Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion service/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,8 @@ router.get('/users', rootAuth, async (req, res) => {
try {
const page = +req.query.page
const size = +req.query.size
const data = await getUsers(page, size)
const search = req.query.search as string | undefined
const data = await getUsers(page, size, search)
res.send({ status: 'Success', message: '获取成功 | Get successfully', data })
}
catch (error) {
Expand Down
9 changes: 6 additions & 3 deletions service/src/storage/mongo.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { WithId } from 'mongodb'
import type { Filter, WithId } from 'mongodb'
import type {
AdvancedConfig,
BuiltInPrompt,
Expand Down Expand Up @@ -502,8 +502,11 @@ export async function getUser(email: string): Promise<UserInfo> {
return userInfo
}

export async function getUsers(page: number, size: number): Promise<{ users: UserInfo[], total: number }> {
const query = { status: { $ne: Status.Deleted } }
export async function getUsers(page: number, size: number, search?: string): Promise<{ users: UserInfo[], total: number }> {
const query: Filter<UserInfo> = { status: { $ne: Status.Deleted } }
if (search && search.trim()) {
query.email = { $regex: search.trim(), $options: 'i' }
}
const cursor = userCol.find(query).sort({ createTime: -1 })
const total = await userCol.countDocuments(query)
const skip = (page - 1) * size
Expand Down
4 changes: 2 additions & 2 deletions src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,10 @@ export function fetchUpdateUserMaxContextCount<T = any>(maxContextCount: number)
})
}

export function fetchGetUsers<T = any>(page: number, size: number) {
export function fetchGetUsers<T = any>(page: number, size: number, search?: string) {
return get<T>({
url: '/users',
data: { page, size },
data: { page, size, search },
})
}

Expand Down
60 changes: 39 additions & 21 deletions src/components/common/Setting/User.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,37 +14,38 @@ const loading = ref(false)
const show = ref(false)
const handleSaving = ref(false)
const userRef = ref(new UserInfo([UserRole.User]))
const searchKeyword = ref('')

const users = ref([])

function createColumns(): DataTableColumns {
return [
{
title: 'Email',
title: t('setting.user.email'),
key: 'email',
resizable: true,
width: 200,
minWidth: 80,
maxWidth: 200,
},
{
title: 'Register Time',
title: t('setting.user.registerTime'),
key: 'createTime',
resizable: true,
width: 200,
minWidth: 80,
maxWidth: 200,
},
{
title: 'Verify Time',
title: t('setting.user.verifyTime'),
key: 'verifyTime',
resizable: true,
width: 200,
minWidth: 80,
maxWidth: 200,
},
{
title: 'Roles',
title: t('setting.user.roles'),
key: 'status',
resizable: true,
width: 200,
Expand All @@ -70,15 +71,15 @@ function createColumns(): DataTableColumns {
},
},
{
title: 'Status',
title: t('setting.user.status'),
key: 'status',
width: 80,
render(row: any) {
return Status[row.status]
},
},
{
title: 'Remark',
title: t('setting.user.remark'),
key: 'remark',
resizable: true,
width: 200,
Expand All @@ -87,7 +88,7 @@ function createColumns(): DataTableColumns {
},
// switch off amt limit
{
title: 'Limit Enabled',
title: t('setting.user.limitEnabled'),
key: 'limit_switch',
resizable: true,
width: 100,
Expand All @@ -99,15 +100,15 @@ function createColumns(): DataTableColumns {
},
// 新增额度信息
{
title: 'Amounts',
title: t('setting.user.amounts'),
key: 'useAmount',
resizable: true,
width: 80,
minWidth: 30,
maxWidth: 100,
},
{
title: 'Action',
title: t('setting.user.action'),
key: '_id',
width: 220,
fixed: 'right',
Expand Down Expand Up @@ -136,7 +137,7 @@ function createColumns(): DataTableColumns {
},
onClick: () => handleEditUser(row),
},
{ default: () => t('chat.editUser') },
{ default: () => t('setting.user.editUser') },
))
}
if (row.status === Status.PreVerify || row.status === Status.AdminVerify) {
Expand All @@ -147,7 +148,7 @@ function createColumns(): DataTableColumns {
type: 'info',
onClick: () => handleUpdateUserStatus(row._id, Status.Normal),
},
{ default: () => t('chat.verifiedUser') },
{ default: () => t('setting.user.verifiedUser') },
))
}
if (row.secretKey) {
Expand All @@ -158,7 +159,7 @@ function createColumns(): DataTableColumns {
type: 'warning',
onClick: () => handleDisable2FA(row._id),
},
{ default: () => t('chat.disable2FA') },
{ default: () => t('setting.user.disable2FA') },
))
}
return actions
Expand All @@ -175,7 +176,7 @@ const pagination = reactive ({
pageCount: 1,
itemCount: 1,
prefix({ itemCount }: { itemCount: number | undefined }) {
return `Total is ${itemCount}.`
return `${t('setting.user.total')}: ${itemCount}`
},
showSizePicker: true,
pageSizes: [25, 50, 100],
Expand All @@ -196,7 +197,7 @@ async function handleGetUsers(page: number) {
users.value.length = 0
loading.value = true
const size = pagination.pageSize
const data = (await fetchGetUsers(page, size)).data
const data = (await fetchGetUsers(page, size, searchKeyword.value || undefined)).data
data.users.forEach((user: never) => {
users.value.push(user)
})
Expand All @@ -206,11 +207,16 @@ async function handleGetUsers(page: number) {
loading.value = false
}

function handleSearch() {
pagination.page = 1
handleGetUsers(1)
}

async function handleUpdateUserStatus(userId: string, status: Status) {
if (status === Status.Deleted) {
dialog.warning({
title: t('chat.deleteUser'),
content: t('chat.deleteUserConfirm'),
title: t('setting.user.deleteUser'),
content: t('setting.user.deleteUserConfirm'),
positiveText: t('common.yes'),
negativeText: t('common.no'),
onPositiveClick: async () => {
Expand All @@ -229,8 +235,8 @@ async function handleUpdateUserStatus(userId: string, status: Status) {

async function handleDisable2FA(userId: string) {
dialog.warning({
title: t('chat.disable2FA'),
content: t('chat.disable2FAConfirm'),
title: t('setting.user.disable2FA'),
content: t('setting.user.disable2FAConfirm'),
positiveText: t('common.yes'),
negativeText: t('common.no'),
onPositiveClick: async () => {
Expand Down Expand Up @@ -273,9 +279,21 @@ onMounted(async () => {
<div class="p-4 space-y-5 min-h-[200px]">
<div class="space-y-6">
<NSpace vertical :size="12">
<NSpace>
<NButton @click="handleNewUser()">
New User
<NSpace justify="space-between" align="center">
<NSpace>
<NInput
v-model:value="searchKeyword"
:placeholder="t('setting.user.searchUserPlaceholder')"
clearable
style="width: 280px"
@keyup.enter="handleSearch"
/>
<NButton type="primary" @click="handleSearch">
{{ t('setting.user.searchUser') }}
</NButton>
</NSpace>
<NButton type="primary" @click="handleNewUser()">
{{ t('setting.user.newUser') }}
</NButton>
</NSpace>
<NDataTable
Expand Down
29 changes: 22 additions & 7 deletions src/locales/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,9 @@
"usagePrompt": "Prompt",
"usageResponse": "Response",
"usageTotal": "Total token cost",
"deleteUser": "Delete User",
"editUser": "Edit User",
"deleteUserConfirm": "Are you sure to delete this user? After deletion, this email can never be registered or logged in again.",
"verifiedUser": "Verified User",
"deleteKey": "Delete Key",
"editKeyButton": "Edit Key",
"deleteKeyConfirm": "Are you sure to delete this key?",
"disable2FA": "Disable 2FA",
"disable2FAConfirm": "Are you sure to disable 2FA for this user?",
"thinking": "Thinking",
"reasoningProcess": "Reasoning Process",
"noReasoningProcess": "No Reasoning Process",
Expand Down Expand Up @@ -224,7 +218,28 @@
"info2FAStep3Tip2": "1. After logging in, use the two-step verification on the Two-Step Verification page to disable it.",
"info2FAStep3Tip3": "2. Contact the administrator to disable two-step verification.",
"maxContextCount": "Maximum number of messages included in the context window for default new sessions",
"fastDelMsg": "Fast Delete Message"
"fastDelMsg": "Fast Delete Message",
"user": {
"searchUser": "Search",
"newUser": "New User",
"searchUserPlaceholder": "Search by email",
"editUser": "Edit User",
"deleteUser": "Delete User",
"deleteUserConfirm": "Are you sure to delete this user? After deletion, this email can never be registered or logged in again.",
"verifiedUser": "Verified User",
"disable2FA": "Disable 2FA",
"disable2FAConfirm": "Are you sure to disable 2FA for this user?",
"email": "Email",
"registerTime": "Register Time",
"verifyTime": "Verify Time",
"roles": "Roles",
"status": "Status",
"remark": "Remark",
"limitEnabled": "Limit Enabled",
"amounts": "Amounts",
"action": "Action",
"total": "Total"
}
},
"store": {
"siderButton": "Prompt Store",
Expand Down
29 changes: 22 additions & 7 deletions src/locales/ko-KR.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,9 @@
"usagePrompt": "질문",
"usageResponse": "답변",
"usageTotal": "총 소비 token",
"deleteUser": "Delete User",
"editUser": "Edit User",
"deleteUserConfirm": "Are you sure to delete this user? After deletion, this email can never be registered or logged in again.",
"verifiedUser": "Verified User",
"deleteKey": "Delete Key",
"editKeyButton": "Edit Key",
"deleteKeyConfirm": "Are you sure to delete this key?",
"disable2FA": "Disable 2FA",
"disable2FAConfirm": "Are you sure to disable 2FA for this user?",
"thinking": "생각 중",
"reasoningProcess": "추론 과정",
"noReasoningProcess": "추론 과정 없음",
Expand Down Expand Up @@ -224,7 +218,28 @@
"info2FAStep3Tip2": "1. After logging in, use the two-step verification on the Two-Step Verification page to disable it.",
"info2FAStep3Tip3": "2. Contact the administrator to disable two-step verification.",
"maxContextCount": "기본 새 세션의 컨텍스트 창에 포함된 최대 메시지 수",
"fastDelMsg": "빠르게 메시지 삭제"
"fastDelMsg": "빠르게 메시지 삭제",
"user": {
"searchUser": "검색",
"newUser": "새 사용자",
"searchUserPlaceholder": "이메일로 검색",
"editUser": "사용자 편집",
"deleteUser": "사용자 삭제",
"deleteUserConfirm": "이 사용자를 삭제하시겠습니까? 삭제 후 이 이메일은 영원히 등록하거나 로그인할 수 없습니다.",
"verifiedUser": "인증된 사용자",
"disable2FA": "2FA 비활성화",
"disable2FAConfirm": "이 사용자의 2FA를 비활성화하시겠습니까?",
"email": "Email",
"registerTime": "등록 시간",
"verifyTime": "인증 시간",
"roles": "역할",
"status": "상태",
"remark": "비고",
"limitEnabled": "제한 활성화",
"amounts": "금액",
"action": "작업",
"total": "총계"
}
},
"store": {
"siderButton": "프롬프트 스토어",
Expand Down
29 changes: 22 additions & 7 deletions src/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,9 @@
"usagePrompt": "提问",
"usageResponse": "回复",
"usageTotal": "总消耗 token",
"deleteUser": "删除用户",
"editUser": "编辑用户",
"deleteUserConfirm": "你确定要删除这个用户吗? 删除后这个邮箱永远无法注册登录",
"verifiedUser": "通过验证",
"deleteKey": "删除 Key",
"editKeyButton": "编辑 Key",
"deleteKeyConfirm": "你确定要删除这个 key 吗?",
"disable2FA": "禁用 2FA",
"disable2FAConfirm": "您确定要为此用户禁用两步验证吗??",
"thinking": "思考中",
"reasoningProcess": "推理过程",
"noReasoningProcess": "无推理过程",
Expand Down Expand Up @@ -224,7 +218,28 @@
"info2FAStep3Tip2": "1. 登录后,在 两步验证 页面使用两步验证码关闭。",
"info2FAStep3Tip3": "2. 联系管理员来关闭两步验证。",
"maxContextCount": "默认新会话的上下文窗口中包含的最大消息数量",
"fastDelMsg": "快速删除消息"
"fastDelMsg": "快速删除消息",
"user": {
"searchUser": "搜索",
"newUser": "新建用户",
"searchUserPlaceholder": "按邮箱搜索",
"editUser": "编辑用户",
"deleteUser": "删除用户",
"deleteUserConfirm": "你确定要删除这个用户吗? 删除后这个邮箱永远无法注册登录",
"verifiedUser": "通过验证",
"disable2FA": "禁用 2FA",
"disable2FAConfirm": "您确定要为此用户禁用两步验证吗?",
"email": "Email",
"registerTime": "注册时间",
"verifyTime": "验证时间",
"roles": "角色",
"status": "状态",
"remark": "备注",
"limitEnabled": "限制启用",
"amounts": "额度",
"action": "操作",
"total": "总计"
}
},
"store": {
"siderButton": "提示词商店",
Expand Down
Loading