Skip to content

Commit 795afdf

Browse files
authored
feat: add admin view of chat history switch (#661)
Signed-off-by: Bob Du <[email protected]>
1 parent 4375f51 commit 795afdf

File tree

11 files changed

+72
-35
lines changed

11 files changed

+72
-35
lines changed

service/src/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,7 @@ router.post('/session', async (req, res) => {
180180
chatModels,
181181
allChatModels: chatModelOptions,
182182
showWatermark: config.siteConfig?.showWatermark,
183+
adminViewChatHistoryEnabled: process.env.ADMIN_VIEW_CHAT_HISTORY_ENABLED === 'true',
183184
},
184185
})
185186
return
@@ -244,6 +245,7 @@ router.post('/session', async (req, res) => {
244245
allChatModels: chatModelOptions,
245246
usageCountLimit: config.siteConfig?.usageCountLimit,
246247
showWatermark: config.siteConfig?.showWatermark,
248+
adminViewChatHistoryEnabled: process.env.ADMIN_VIEW_CHAT_HISTORY_ENABLED === 'true',
247249
userInfo,
248250
},
249251
})
@@ -261,6 +263,7 @@ router.post('/session', async (req, res) => {
261263
chatModels: chatModelOptions,
262264
allChatModels: chatModelOptions,
263265
showWatermark: config.siteConfig?.showWatermark,
266+
adminViewChatHistoryEnabled: process.env.ADMIN_VIEW_CHAT_HISTORY_ENABLED === 'true',
264267
userInfo,
265268
},
266269
})

service/src/routes/chat.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import type { ResponseChunk } from '../chatgpt/types'
22
import type { ChatInfo, ChatOptions, UsageResponse, UserInfo } from '../storage/model'
33
import type { RequestProps } from '../types'
44
import * as console from 'node:console'
5+
import * as process from 'node:process'
56
import Router from 'express'
67
import { ObjectId } from 'mongodb'
78
import { abortChatProcess, chatReplyProcess, containsSensitiveWords } from '../chatgpt'
@@ -38,12 +39,14 @@ router.get('/chat-history', auth, async (req, res) => {
3839
return
3940
}
4041

42+
// When 'all' parameter is not empty, it means requesting to view all users' chat history
43+
// This requires: 1) user must be an admin, 2) ADMIN_VIEW_CHAT_HISTORY_ENABLED environment variable must be set to 'true'
4144
if (all !== null && all !== 'undefined' && all !== undefined && all.trim().length !== 0) {
4245
const config = await getCacheConfig()
4346
if (config.siteConfig.loginEnabled) {
4447
try {
4548
const user = await getUserById(userId)
46-
if (user == null || user.status !== Status.Normal || !user.roles.includes(UserRole.Admin)) {
49+
if (user == null || user.status !== Status.Normal || !user.roles.includes(UserRole.Admin) || process.env.ADMIN_VIEW_CHAT_HISTORY_ENABLED !== 'true') {
4750
res.send({ status: 'Fail', message: '无权限 | No permission.', data: null })
4851
return
4952
}

service/src/storage/mongo.ts

Lines changed: 39 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -431,14 +431,40 @@ export async function getChatRoomsCount(userId: string, page: number, size: numb
431431
{
432432
$lookup: {
433433
from: 'chat',
434-
localField: 'roomId',
435-
foreignField: 'roomId',
436-
as: 'chat',
434+
let: { roomId: '$roomId' },
435+
pipeline: [
436+
{
437+
$match: {
438+
$expr: {
439+
$and: [
440+
{ $eq: ['$roomId', '$$roomId'] },
441+
{ $ne: ['$status', Status.InversionDeleted] },
442+
],
443+
},
444+
},
445+
},
446+
{
447+
$sort: { dateTime: -1 },
448+
},
449+
{
450+
$group: {
451+
_id: null,
452+
chatCount: { $sum: 1 },
453+
lastChat: { $first: '$$ROOT' },
454+
},
455+
},
456+
],
457+
as: 'chatInfo',
437458
},
438459
},
439460
{
440461
$addFields: {
441-
title: '$chat.prompt',
462+
chatCount: {
463+
$ifNull: [{ $arrayElemAt: ['$chatInfo.chatCount', 0] }, 0],
464+
},
465+
lastChat: {
466+
$arrayElemAt: ['$chatInfo.lastChat', 0],
467+
},
442468
user_ObjectId: {
443469
$toObjectId: '$userId',
444470
},
@@ -450,6 +476,13 @@ export async function getChatRoomsCount(userId: string, page: number, size: numb
450476
localField: 'user_ObjectId',
451477
foreignField: '_id',
452478
as: 'user',
479+
pipeline: [
480+
{
481+
$project: {
482+
name: 1,
483+
},
484+
},
485+
],
453486
},
454487
},
455488
{
@@ -458,37 +491,14 @@ export async function getChatRoomsCount(userId: string, page: number, size: numb
458491
preserveNullAndEmptyArrays: false,
459492
},
460493
},
461-
{
462-
$sort: {
463-
'chat.dateTime': -1,
464-
},
465-
},
466-
{
467-
$addFields: {
468-
chatCount: {
469-
$size: '$chat',
470-
},
471-
chat: {
472-
$arrayElemAt: [
473-
{
474-
$slice: [
475-
'$chat',
476-
-1,
477-
],
478-
},
479-
0,
480-
],
481-
},
482-
},
483-
},
484494
{
485495
$project: {
486496
userId: 1,
487-
title: '$chat.prompt',
497+
title: { $ifNull: ['$lastChat.prompt', ''] },
488498
username: '$user.name',
489499
roomId: 1,
490500
chatCount: 1,
491-
dateTime: '$chat.dateTime',
501+
dateTime: { $ifNull: ['$lastChat.dateTime', null] },
492502
},
493503
},
494504
{

src/api/index.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,8 +377,15 @@ export function fetchDeleteChatRoom<T = any>(roomId: number) {
377377
}
378378

379379
export function fetchGetChatHistory<T = any>(roomId: number, lastId?: number, all?: string) {
380+
let url = `/chat-history?roomId=${roomId}`
381+
if (lastId !== undefined && lastId !== null) {
382+
url += `&lastId=${lastId}`
383+
}
384+
if (all !== undefined && all !== null) {
385+
url += `&all=${all}`
386+
}
380387
return get<T>({
381-
url: `/chat-history?roomId=${roomId}&lastId=${lastId}&all=${all}`,
388+
url,
382389
})
383390
}
384391

src/components/common/Setting/ChatRecord.vue

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import { SvgIcon } from '@/components/common'
55
import { useBasicLayout } from '@/hooks/useBasicLayout'
66
import Message from '@/views/chat/components/Message/index.vue'
77
8+
const { t } = useI18n()
9+
810
interface HistoryChat {
911
uuid?: number
1012
model?: string
@@ -70,13 +72,13 @@ const columns = [{
7072
show.value = true
7173
dataSources.value.length = 0
7274
chatLoading.value = true
73-
fetchGetChatHistory(row.uuid, undefined, 'all').then((res: any) => {
75+
fetchGetChatHistory(row.roomId, undefined, 'all').then((res: any) => {
7476
dataSources.value = res.data as HistoryChat[]
7577
chatLoading.value = false
7678
})
7779
},
7880
},
79-
{ default: () => 'view' },
81+
{ default: () => t('setting.view') },
8082
))
8183
return actions
8284
},
@@ -144,6 +146,7 @@ onMounted(async () => {
144146
<NSelect
145147
v-model:value="selectUserId"
146148
style="width: 250px"
149+
filterable
147150
:options="userOptions"
148151
@update:value="handleSelectUser"
149152
/>

src/components/common/Setting/index.vue

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import { SvgIcon } from '@/components/common'
33
import ChatRecord from '@/components/common/Setting/ChatRecord.vue'
44
import { useBasicLayout } from '@/hooks/useBasicLayout'
5-
import { useUserStore } from '@/store'
5+
import { useAuthStore, useUserStore } from '@/store'
66
import About from './About.vue'
77
import Advanced from './Advanced.vue'
88
import Announcement from './Anonuncement.vue'
@@ -25,6 +25,7 @@ const emit = defineEmits<Emit>()
2525
const { t } = useI18n()
2626
2727
const userStore = useUserStore()
28+
const authStore = useAuthStore()
2829
const { isMobile } = useBasicLayout()
2930
3031
interface Props {
@@ -37,6 +38,11 @@ interface Emit {
3738
3839
const active = ref('General')
3940
41+
// Check if admin view chat history is enabled
42+
const showChatRecordTab = computed(() => {
43+
return userStore.userInfo.root && authStore.session?.adminViewChatHistoryEnabled === true
44+
})
45+
4046
const show = computed({
4147
get() {
4248
return props.visible
@@ -99,7 +105,7 @@ const show = computed({
99105
</template>
100106
<About />
101107
</NTabPane>
102-
<NTabPane v-if="userStore.userInfo.root" name="ChatRecord" tab="ChatRecord">
108+
<NTabPane v-if="showChatRecordTab" name="ChatRecord" tab="ChatRecord">
103109
<template #tab>
104110
<SvgIcon class="text-lg" icon="ic:outline-chat" />
105111
<span class="ml-2">{{ t('setting.chatRecord') }}</span>

src/locales/en-US.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
"statistics": "Statistics",
118118
"config": "Base Config",
119119
"chatRecord": "Chat History",
120+
"view": "View",
120121
"siteConfig": "Site Config",
121122
"mailConfig": "Mail Config",
122123
"auditConfig": "Audit Config",

src/locales/ko-KR.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
"config": "기본 구성",
118118
"statistics": "통계학",
119119
"chatRecord": "채팅 기록",
120+
"view": "보기",
120121
"siteConfig": "사이트 구성",
121122
"mailConfig": "메일 구성",
122123
"auditConfig": "감사 구성",

src/locales/zh-CN.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
"statistics": "统计",
118118
"config": "基本配置",
119119
"chatRecord": "聊天记录",
120+
"view": "查看",
120121
"siteConfig": "网站配置",
121122
"mailConfig": "邮箱配置",
122123
"auditConfig": "敏感词审核",

src/locales/zh-TW.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
"statistics": "統計",
118118
"config": "基本設定",
119119
"chatRecord": "聊天紀錄",
120+
"view": "查看",
120121
"siteConfig": "网站配置",
121122
"mailConfig": "邮箱配置",
122123
"auditConfig": "敏感词审核",

0 commit comments

Comments
 (0)