Skip to content

Commit 785e307

Browse files
author
Kerwin
committed
feat: 用户信息管理
1 parent f2d5d3e commit 785e307

File tree

14 files changed

+78
-72
lines changed

14 files changed

+78
-72
lines changed

service/src/index.ts

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import express from 'express'
22
import jwt from 'jsonwebtoken'
3+
import { ObjectId } from 'mongodb'
34
import type { ChatContext, ChatMessage } from './chatgpt'
45
import { chatConfig, chatReplyProcess, currentModel } from './chatgpt'
56
import { auth } from './middleware/auth'
6-
import type { ChatOptions } from './storage/model'
7+
import type { ChatOptions, UserInfo } from './storage/model'
78
import { Status } from './storage/model'
8-
import { clearChat, createChatRoom, createUser, deleteChat, deleteChatRoom, existsChatRoom, getChat, getChatRooms, getChats, getUser, insertChat, renameChatRoom, updateChat, verifyUser } from './storage/mongo'
9+
import { clearChat, createChatRoom, createUser, deleteChat, deleteChatRoom, existsChatRoom, getChat, getChatRooms, getChats, getUser, getUserById, insertChat, renameChatRoom, updateChat, updateUserInfo, verifyUser } from './storage/mongo'
910
import { sendMail } from './utils/mail'
1011
import { checkUserVerify, getUserVerifyUrl, md5 } from './utils/security'
1112

@@ -248,14 +249,36 @@ router.post('/user-login', async (req, res) => {
248249
throw new Error('用户不存在或密码错误 | User does not exist or incorrect password.')
249250
}
250251

251-
const token = jwt.sign({ email: username, userId: user._id }, process.env.AUTH_SECRET_KEY)
252+
const token = jwt.sign({
253+
name: user.name ? user.name : user.email,
254+
avatar: user.avatar,
255+
description: user.description,
256+
userId: user._id,
257+
root: username.toLowerCase() === process.env.ROOT_USER,
258+
}, process.env.AUTH_SECRET_KEY)
252259
res.send({ status: 'Success', message: '登录成功 | Login successfully', data: { token } })
253260
}
254261
catch (error) {
255262
res.send({ status: 'Fail', message: error.message, data: null })
256263
}
257264
})
258265

266+
router.post('/user-info', auth, async (req, res) => {
267+
try {
268+
const { name, avatar, description } = req.body as UserInfo
269+
const userId = new ObjectId(req.headers.userId.toString())
270+
271+
const user = await getUserById(userId)
272+
if (user == null || user.status !== Status.Normal)
273+
throw new Error('用户不存在 | User does not exist.')
274+
await updateUserInfo(userId, { name, avatar, description } as UserInfo)
275+
res.send({ status: 'Success', message: '更新成功 | Update successfully' })
276+
}
277+
catch (error) {
278+
res.send({ status: 'Fail', message: error.message, data: null })
279+
}
280+
})
281+
259282
router.post('/verify', async (req, res) => {
260283
try {
261284
const { token } = req.body as { token: string }

service/src/storage/model.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class UserInfo {
1616
status: Status
1717
createTime: string
1818
verifyTime?: string
19+
avatar?: string
20+
description?: string
1921
constructor(email: string, password: string) {
2022
this.name = email
2123
this.email = email

service/src/storage/mongo.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ export async function deleteChat(roomId: number, uuid: number, inversion: boolea
117117
chatCol.updateOne(query, update)
118118
}
119119

120-
export async function createUser(email: string, password: string) {
120+
export async function createUser(email: string, password: string): Promise<UserInfo> {
121121
email = email.toLowerCase()
122122
const userInfo = new UserInfo(email, password)
123123
if (email === process.env.ROOT_USER)
@@ -127,14 +127,19 @@ export async function createUser(email: string, password: string) {
127127
return userInfo
128128
}
129129

130-
export async function updateUserName(userId: ObjectId, name: string) {
131-
const result = userCol.updateOne({ _id: userId }, { name: { $set: name } })
130+
export async function updateUserInfo(userId: ObjectId, user: UserInfo) {
131+
const result = userCol.updateOne({ _id: userId }
132+
, { $set: { name: user.name, description: user.description, avatar: user.avatar } })
132133
return result
133134
}
134135

135-
export async function getUser(email: string) {
136+
export async function getUser(email: string): Promise<UserInfo> {
136137
email = email.toLowerCase()
137-
return await userCol.findOne({ email })
138+
return await userCol.findOne({ email }) as UserInfo
139+
}
140+
141+
export async function getUserById(userId: ObjectId): Promise<UserInfo> {
142+
return await userCol.findOne({ _id: userId }) as UserInfo
138143
}
139144

140145
export async function verifyUser(email: string) {

src/api/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,13 @@ export function fetchRegister<T = any>(username: string, password: string) {
6464
})
6565
}
6666

67+
export function fetchUpdateUserInfo<T = any>(name: string, avatar: string, description: string) {
68+
return post<T>({
69+
url: '/user-info',
70+
data: { name, avatar, description },
71+
})
72+
}
73+
6774
export function fetchGetChatRooms<T = any>() {
6875
return get<T>({
6976
url: '/chatrooms',

src/components/common/Setting/About.vue

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -44,29 +44,6 @@ onMounted(() => {
4444
<h2 class="text-xl font-bold">
4545
Version - {{ pkg.version }}
4646
</h2>
47-
<div class="p-2 space-y-2 rounded-md bg-neutral-100 dark:bg-neutral-700">
48-
<p>
49-
此项目开源于
50-
<a
51-
class="text-blue-600 dark:text-blue-500"
52-
href="https://github.com/kerwin1202/chatgpt-web"
53-
target="_blank"
54-
>
55-
kerwin1202/chatgpt-web
56-
</a>是基于
57-
<a
58-
class="text-blue-600 dark:text-blue-500"
59-
href="https://github.com/Chanzhaoyu/chatgpt-web"
60-
target="_blank"
61-
>
62-
Chanzhaoyu/chatgpt-web
63-
</a> 分支而来
64-
,免费且基于 MIT 协议,没有任何形式的付费行为!
65-
</p>
66-
<p>
67-
如果你觉得此项目对你有帮助,请在 Github 帮我点个 Star 或者给予一点赞助,谢谢!
68-
</p>
69-
</div>
7047
<p>{{ $t("setting.api") }}:{{ config?.apiModel ?? '-' }}</p>
7148
<p v-if="isChatGPTAPI">
7249
{{ $t("setting.balance") }}:{{ config?.balance ?? '-' }}

src/components/common/Setting/General.vue

Lines changed: 11 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -59,17 +59,11 @@ const languageOptions: { label: string; key: Language; value: Language }[] = [
5959
{ label: 'English', key: 'en-US', value: 'en-US' },
6060
]
6161
62-
function updateUserInfo(options: Partial<UserInfo>) {
63-
userStore.updateUserInfo(options)
62+
async function updateUserInfo(options: Partial<UserInfo>) {
63+
await userStore.updateUserInfo(true, options)
6464
ms.success(t('common.success'))
6565
}
6666
67-
function handleReset() {
68-
userStore.resetUserInfo()
69-
ms.success(t('common.success'))
70-
window.location.reload()
71-
}
72-
7367
function exportData(): void {
7468
const date = getCurrentDate()
7569
const data: string = localStorage.getItem('chatStorage') || '{}'
@@ -123,32 +117,23 @@ function handleImportButtonClick(): void {
123117
<template>
124118
<div class="p-4 space-y-5 min-h-[200px]">
125119
<div class="space-y-6">
126-
<div class="flex items-center space-x-4">
127-
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.avatarLink') }}</span>
128-
<div class="flex-1">
129-
<NInput v-model:value="avatar" placeholder="" />
130-
</div>
131-
<NButton size="tiny" text type="primary" @click="updateUserInfo({ avatar })">
132-
{{ $t('common.save') }}
133-
</NButton>
134-
</div>
135120
<div class="flex items-center space-x-4">
136121
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.name') }}</span>
137122
<div class="w-[200px]">
138123
<NInput v-model:value="name" placeholder="" />
139124
</div>
140-
<NButton size="tiny" text type="primary" @click="updateUserInfo({ name })">
141-
{{ $t('common.save') }}
142-
</NButton>
143125
</div>
144126
<div class="flex items-center space-x-4">
145127
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.description') }}</span>
146128
<div class="flex-1">
147129
<NInput v-model:value="description" placeholder="" />
148130
</div>
149-
<NButton size="tiny" text type="primary" @click="updateUserInfo({ description })">
150-
{{ $t('common.save') }}
151-
</NButton>
131+
</div>
132+
<div class="flex items-center space-x-4">
133+
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.avatarLink') }}</span>
134+
<div class="flex-1">
135+
<NInput v-model:value="avatar" placeholder="" />
136+
</div>
152137
</div>
153138

154139
<div
@@ -214,9 +199,9 @@ function handleImportButtonClick(): void {
214199
</div>
215200
</div>
216201
<div class="flex items-center space-x-4">
217-
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.resetUserInfo') }}</span>
218-
<NButton size="small" @click="handleReset">
219-
{{ $t('common.reset') }}
202+
<span class="flex-shrink-0 w-[100px]">{{ $t('setting.saveUserInfo') }}</span>
203+
<NButton size="small" type="primary" @click="updateUserInfo({ avatar, name, description })">
204+
{{ $t('common.save') }}
220205
</NButton>
221206
</div>
222207
</div>

src/components/common/Setting/index.vue

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,14 @@ import { NModal, NTabPane, NTabs } from 'naive-ui'
44
import General from './General.vue'
55
import About from './About.vue'
66
import { SvgIcon } from '@/components/common'
7+
import { useUserStore } from '@/store'
78
89
const props = defineProps<Props>()
910
1011
const emit = defineEmits<Emit>()
1112
13+
const userStore = useUserStore()
14+
1215
interface Props {
1316
visible: boolean
1417
}
@@ -42,7 +45,7 @@ const show = computed({
4245
<General />
4346
</div>
4447
</NTabPane>
45-
<NTabPane name="Config" tab="Config">
48+
<NTabPane v-if="userStore.userInfo.root" name="Config" tab="Config">
4649
<template #tab>
4750
<SvgIcon class="text-lg" icon="ri:list-settings-line" />
4851
<span class="ml-2">{{ $t('setting.config') }}</span>

src/locales/en-US.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export default {
88
deleteSuccess: 'Delete Success',
99
save: 'Save',
1010
saveSuccess: 'Save Success',
11-
reset: 'Reset',
1211
action: 'Action',
1312
export: 'Export',
1413
exportSuccess: 'Export Success',
@@ -57,7 +56,7 @@ export default {
5756
avatarLink: 'Avatar Link',
5857
name: 'Name',
5958
description: 'Description',
60-
resetUserInfo: 'Reset UserInfo',
59+
saveUserInfo: 'Save User Info',
6160
chatHistory: 'ChatHistory',
6261
theme: 'Theme',
6362
language: 'Language',

src/locales/zh-CN.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export default {
88
deleteSuccess: '删除成功',
99
save: '保存',
1010
saveSuccess: '保存成功',
11-
reset: '重置',
1211
action: '操作',
1312
export: '导出',
1413
exportSuccess: '导出成功',
@@ -57,7 +56,7 @@ export default {
5756
avatarLink: '头像链接',
5857
name: '名称',
5958
description: '描述',
60-
resetUserInfo: '重置用户信息',
59+
saveUserInfo: '保存用户信息',
6160
chatHistory: '聊天记录',
6261
theme: '主题',
6362
language: '语言',

src/locales/zh-TW.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ export default {
88
deleteSuccess: '刪除成功',
99
save: '儲存',
1010
saveSuccess: '儲存成功',
11-
reset: '重設',
1211
action: '操作',
1312
export: '匯出',
1413
exportSuccess: '匯出成功',
@@ -57,7 +56,7 @@ export default {
5756
avatarLink: '頭貼連結',
5857
name: '名稱',
5958
description: '描述',
60-
resetUserInfo: '重設使用者資訊',
59+
saveUserInfo: '保存用户資訊',
6160
chatHistory: '紀錄',
6261
theme: '主題',
6362
language: '語言',

src/store/modules/auth/index.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { defineStore } from 'pinia'
22
import jwt_decode from 'jwt-decode'
3+
import type { UserInfo } from '../user/helper'
34
import { getToken, removeToken, setToken } from './helper'
45
import { store, useUserStore } from '@/store'
56
import { fetchSession } from '@/api'
@@ -39,14 +40,15 @@ export const useAuthStore = defineStore('auth-store', {
3940
}
4041
},
4142

42-
setToken(token: string) {
43+
async setToken(token: string) {
4344
this.token = token
44-
const decoded = jwt_decode(token) as { email: string }
45+
const decoded = jwt_decode(token) as UserInfo
4546
const userStore = useUserStore()
46-
userStore.updateUserInfo({
47-
avatar: '',
48-
name: decoded.email,
49-
description: '',
47+
await userStore.updateUserInfo(false, {
48+
avatar: decoded.avatar,
49+
name: decoded.name,
50+
description: decoded.description,
51+
root: decoded.root,
5052
})
5153
setToken(token)
5254
},

src/store/modules/user/helper.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export interface UserInfo {
66
avatar: string
77
name: string
88
description: string
9+
root: boolean
910
}
1011

1112
export interface UserState {
@@ -18,6 +19,7 @@ export function defaultSetting(): UserState {
1819
avatar: '',
1920
name: '',
2021
description: '',
22+
root: false,
2123
},
2224
}
2325
}

src/store/modules/user/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
11
import { defineStore } from 'pinia'
2+
import { fetchUpdateUserInfo } from '../../../api/'
23
import type { UserInfo, UserState } from './helper'
34
import { defaultSetting, getLocalState, setLocalState } from './helper'
45

56
export const useUserStore = defineStore('user-store', {
67
state: (): UserState => getLocalState(),
78
actions: {
8-
updateUserInfo(userInfo: Partial<UserInfo>) {
9+
async updateUserInfo(update: boolean, userInfo: Partial<UserInfo>) {
910
this.userInfo = { ...this.userInfo, ...userInfo }
1011
this.recordState()
12+
if (update)
13+
await fetchUpdateUserInfo(userInfo.name ?? '', userInfo.avatar ?? '', userInfo.description ?? '')
1114
},
1215

1316
resetUserInfo() {

src/views/chat/layout/Permission.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ async function handleLogin() {
6666
try {
6767
loading.value = true
6868
const result = await fetchLogin(name, pwd)
69-
authStore.setToken(result.data.token)
69+
await authStore.setToken(result.data.token)
7070
ms.success('success')
7171
router.go(0)
7272
}

0 commit comments

Comments
 (0)