import express from 'express' import jwt from 'jsonwebtoken' import * as dotenv from 'dotenv' import { ObjectId } from 'mongodb' import type { RequestProps } from './types' import type { ChatContext, ChatMessage } from './chatgpt' import { abortChatProcess, chatConfig, chatReplyProcess, containsSensitiveWords, initAuditService } from './chatgpt' import { auth, getUserId } from './middleware/auth' import { clearApiKeyCache, clearConfigCache, getApiKeys, getCacheApiKeys, getCacheConfig, getOriginConfig } from './storage/config' import type { AuditConfig, CHATMODEL, ChatInfo, ChatOptions, Config, KeyConfig, MailConfig, SiteConfig, UsageResponse, UserInfo } from './storage/model' import { Status, UserRole, chatModelOptions } from './storage/model' import { clearChat, createChatRoom, createUser, deleteAllChatRooms, deleteChat, deleteChatRoom, existsChatRoom, getChat, getChatRoom, getChatRooms, getChats, getUser, getUserById, getUserStatisticsByDay, getUsers, insertChat, insertChatUsage, renameChatRoom, updateApiKeyStatus, updateChat, updateConfig, updateRoomPrompt, updateRoomUsingContext, updateUserChatModel, updateUserInfo, updateUserPassword, updateUserRole, updateUserStatus, upsertKey, verifyUser, } from './storage/mongo' import { authLimiter, limiter } from './middleware/limiter' import { hasAnyRole, isEmail, isNotEmptyString } from './utils/is' import { sendNoticeMail, sendResetPasswordMail, sendTestMail, sendVerifyMail, sendVerifyMailAdmin } from './utils/mail' import { checkUserResetPassword, checkUserVerify, checkUserVerifyAdmin, getUserResetPasswordUrl, getUserVerifyUrl, getUserVerifyUrlAdmin, md5 } from './utils/security' import { rootAuth } from './middleware/rootAuth' dotenv.config() const app = express() const router = express.Router() app.use(express.static('public')) app.use(express.json()) app.all('*', (_, res, next) => { res.header('Access-Control-Allow-Origin', '*') res.header('Access-Control-Allow-Headers', 'authorization, Content-Type') res.header('Access-Control-Allow-Methods', '*') next() }) router.get('/chatrooms', auth, async (req, res) => { try { const userId = req.headers.userId as string const rooms = await getChatRooms(userId) const result = [] rooms.forEach((r) => { result.push({ uuid: r.roomId, title: r.title, isEdit: false, prompt: r.prompt, usingContext: r.usingContext === undefined ? true : r.usingContext, }) }) res.send({ status: 'Success', message: null, data: result }) } catch (error) { console.error(error) res.send({ status: 'Fail', message: 'Load error', data: [] }) } }) router.post('/room-create', auth, async (req, res) => { try { const userId = req.headers.userId as string const { title, roomId } = req.body as { title: string; roomId: number } const room = await createChatRoom(userId, title, roomId) res.send({ status: 'Success', message: null, data: room }) } catch (error) { console.error(error) res.send({ status: 'Fail', message: 'Create error', data: null }) } }) router.post('/room-rename', auth, async (req, res) => { try { const userId = req.headers.userId as string const { title, roomId } = req.body as { title: string; roomId: number } const room = await renameChatRoom(userId, title, roomId) res.send({ status: 'Success', message: null, data: room }) } catch (error) { console.error(error) res.send({ status: 'Fail', message: 'Rename error', data: null }) } }) router.post('/room-prompt', auth, async (req, res) => { try { const userId = req.headers.userId as string const { prompt, roomId } = req.body as { prompt: string; roomId: number } const success = await updateRoomPrompt(userId, roomId, prompt) if (success) res.send({ status: 'Success', message: 'Saved successfully', data: null }) else res.send({ status: 'Fail', message: 'Saved Failed', data: null }) } catch (error) { console.error(error) res.send({ status: 'Fail', message: 'Rename error', data: null }) } }) router.post('/room-context', auth, async (req, res) => { try { const userId = req.headers.userId as string const { using, roomId } = req.body as { using: boolean; roomId: number } const success = await updateRoomUsingContext(userId, roomId, using) if (success) res.send({ status: 'Success', message: 'Saved successfully', data: null }) else res.send({ status: 'Fail', message: 'Saved Failed', data: null }) } catch (error) { console.error(error) res.send({ status: 'Fail', message: 'Rename error', data: null }) } }) router.post('/room-delete', auth, async (req, res) => { try { const userId = req.headers.userId as string const { roomId } = req.body as { roomId: number } if (!roomId || !await existsChatRoom(userId, roomId)) { res.send({ status: 'Fail', message: 'Unknow room', data: null }) return } await deleteChatRoom(userId, roomId) res.send({ status: 'Success', message: null }) } catch (error) { console.error(error) res.send({ status: 'Fail', message: 'Delete error', data: null }) } }) router.get('/chat-history', auth, async (req, res) => { try { const userId = req.headers.userId as string const roomId = +req.query.roomId const lastId = req.query.lastId as string if (!roomId || !await existsChatRoom(userId, roomId)) { res.send({ status: 'Success', message: null, data: [] }) // res.send({ status: 'Fail', message: 'Unknow room', data: null }) return } const chats = await getChats(roomId, !isNotEmptyString(lastId) ? null : parseInt(lastId)) const result = [] chats.forEach((c) => { if (c.status !== Status.InversionDeleted) { result.push({ uuid: c.uuid, dateTime: new Date(c.dateTime).toLocaleString(), text: c.prompt, inversion: true, error: false, conversationOptions: null, requestOptions: { prompt: c.prompt, options: null, }, }) } if (c.status !== Status.ResponseDeleted) { const usage = c.options.completion_tokens ? { completion_tokens: c.options.completion_tokens || null, prompt_tokens: c.options.prompt_tokens || null, total_tokens: c.options.total_tokens || null, estimated: c.options.estimated || null, } : undefined result.push({ uuid: c.uuid, dateTime: new Date(c.dateTime).toLocaleString(), text: c.response, inversion: false, error: false, loading: false, responseCount: (c.previousResponse?.length ?? 0) + 1, conversationOptions: { parentMessageId: c.options.messageId, conversationId: c.options.conversationId, }, requestOptions: { prompt: c.prompt, parentMessageId: c.options.parentMessageId, options: { parentMessageId: c.options.messageId, conversationId: c.options.conversationId, }, }, usage, }) } }) res.send({ status: 'Success', message: null, data: result }) } catch (error) { console.error(error) res.send({ status: 'Fail', message: 'Load error', data: null }) } }) router.get('/chat-response-history', auth, async (req, res) => { try { const userId = req.headers.userId as string const roomId = +req.query.roomId const uuid = +req.query.uuid const index = +req.query.index if (!roomId || !await existsChatRoom(userId, roomId)) { res.send({ status: 'Success', message: null, data: [] }) // res.send({ status: 'Fail', message: 'Unknow room', data: null }) return } const chat = await getChat(roomId, uuid) if (chat.previousResponse === undefined || chat.previousResponse.length < index) { res.send({ status: 'Fail', message: 'Error', data: [] }) return } const response = index >= chat.previousResponse.length ? chat : chat.previousResponse[index] const usage = response.options.completion_tokens ? { completion_tokens: response.options.completion_tokens || null, prompt_tokens: response.options.prompt_tokens || null, total_tokens: response.options.total_tokens || null, estimated: response.options.estimated || null, } : undefined res.send({ status: 'Success', message: null, data: { uuid: chat.uuid, dateTime: new Date(chat.dateTime).toLocaleString(), text: response.response, inversion: false, error: false, loading: false, responseCount: (chat.previousResponse?.length ?? 0) + 1, conversationOptions: { parentMessageId: response.options.messageId, conversationId: response.options.conversationId, }, requestOptions: { prompt: chat.prompt, parentMessageId: response.options.parentMessageId, options: { parentMessageId: response.options.messageId, conversationId: response.options.conversationId, }, }, usage, }, }) } catch (error) { console.error(error) res.send({ status: 'Fail', message: 'Load error', data: null }) } }) router.post('/chat-delete', auth, async (req, res) => { try { const userId = req.headers.userId as string const { roomId, uuid, inversion } = req.body as { roomId: number; uuid: number; inversion: boolean } if (!roomId || !await existsChatRoom(userId, roomId)) { res.send({ status: 'Fail', message: 'Unknow room', data: null }) return } await deleteChat(roomId, uuid, inversion) res.send({ status: 'Success', message: null, data: null }) } catch (error) { console.error(error) res.send({ status: 'Fail', message: 'Delete error', data: null }) } }) router.post('/chat-clear-all', auth, async (req, res) => { try { const userId = req.headers.userId as string await deleteAllChatRooms(userId) res.send({ status: 'Success', message: null, data: null }) } catch (error) { console.error(error) res.send({ status: 'Fail', message: 'Delete error', data: null }) } }) router.post('/chat-clear', auth, async (req, res) => { try { const userId = req.headers.userId as string const { roomId } = req.body as { roomId: number } if (!roomId || !await existsChatRoom(userId, roomId)) { res.send({ status: 'Fail', message: 'Unknow room', data: null }) return } await clearChat(roomId) res.send({ status: 'Success', message: null, data: null }) } catch (error) { console.error(error) res.send({ status: 'Fail', message: 'Delete error', data: null }) } }) router.post('/chat', auth, async (req, res) => { try { const { roomId, uuid, regenerate, prompt, options = {} } = req.body as { roomId: number; uuid: number; regenerate: boolean; prompt: string; options?: ChatContext } const message = regenerate ? await getChat(roomId, uuid) : await insertChat(uuid, prompt, roomId, options as ChatOptions) const response = await chatReply(prompt, options) if (response.status === 'Success') { if (regenerate && message.options.messageId) { const previousResponse = message.previousResponse || [] previousResponse.push({ response: message.response, options: message.options }) await updateChat(message._id as unknown as string, response.data.text, response.data.id, response.data.detail?.usage as UsageResponse, previousResponse as []) } else { await updateChat(message._id as unknown as string, response.data.text, response.data.id, response.data.detail?.usage as UsageResponse) } if (response.data.usage) { await insertChatUsage(new ObjectId(req.headers.userId as string), roomId, message._id, response.data.id, response.data.detail?.usage as UsageResponse) } } res.send(response) } catch (error) { res.send(error) } }) router.post('/chat-process', [auth, limiter], async (req, res) => { res.setHeader('Content-type', 'application/octet-stream') let { roomId, uuid, regenerate, prompt, options = {}, systemMessage, temperature, top_p } = req.body as RequestProps const userId = req.headers.userId as string const room = await getChatRoom(userId, roomId) if (room == null) global.console.error(`Unable to get chat room \t ${userId}\t ${roomId}`) if (room != null && isNotEmptyString(room.prompt)) systemMessage = room.prompt let lastResponse let result let message: ChatInfo try { const config = await getCacheConfig() const userId = req.headers.userId.toString() const user = await getUserById(userId) if (config.auditConfig.enabled || config.auditConfig.customizeEnabled) { if (!user.roles.includes(UserRole.Admin) && await containsSensitiveWords(config.auditConfig, prompt)) { res.send({ status: 'Fail', message: '含有敏感词 | Contains sensitive words', data: null }) return } } message = regenerate ? await getChat(roomId, uuid) : await insertChat(uuid, prompt, roomId, options as ChatOptions) let firstChunk = true result = await chatReplyProcess({ message: prompt, lastContext: options, process: (chat: ChatMessage) => { lastResponse = chat const chuck = { id: chat.id, conversationId: chat.conversationId, text: chat.text, detail: { choices: [ { finish_reason: undefined, }, ], }, } if (chat.detail && chat.detail.choices.length > 0) chuck.detail.choices[0].finish_reason = chat.detail.choices[0].finish_reason res.write(firstChunk ? JSON.stringify(chuck) : `\n${JSON.stringify(chuck)}`) firstChunk = false }, systemMessage, temperature, top_p, user, messageId: message._id.toString(), tryCount: 0, room, }) // return the whole response including usage res.write(`\n${JSON.stringify(result.data)}`) } catch (error) { res.write(JSON.stringify({ message: error?.message })) } finally { res.end() try { if (result == null || result === undefined || result.status !== 'Success') { if (result && result.status !== 'Success') lastResponse = { text: result.message } result = { data: lastResponse } } if (result.data === undefined) // eslint-disable-next-line no-unsafe-finally return if (regenerate && message.options.messageId) { const previousResponse = message.previousResponse || [] previousResponse.push({ response: message.response, options: message.options }) await updateChat(message._id as unknown as string, result.data.text, result.data.id, result.data.conversationId, result.data.detail?.usage as UsageResponse, previousResponse as []) } else { await updateChat(message._id as unknown as string, result.data.text, result.data.id, result.data.conversationId, result.data.detail?.usage as UsageResponse) } if (result.data.detail?.usage) { await insertChatUsage(new ObjectId(req.headers.userId), roomId, message._id, result.data.id, result.data.detail?.usage as UsageResponse) } } catch (error) { global.console.error(error) } } }) router.post('/chat-abort', [auth, limiter], async (req, res) => { try { const userId = req.headers.userId.toString() const { text, messageId, conversationId } = req.body as { text: string; messageId: string; conversationId: string } const msgId = await abortChatProcess(userId) await updateChat(msgId, text, messageId, conversationId, null) res.send({ status: 'Success', message: 'OK', data: null }) } catch (error) { res.send({ status: 'Fail', message: '重置邮件已发送 | Reset email has been sent', data: null }) } }) router.post('/user-register', authLimiter, async (req, res) => { try { const { username, password } = req.body as { username: string; password: string } const config = await getCacheConfig() if (!config.siteConfig.registerEnabled) { res.send({ status: 'Fail', message: '注册账号功能未启用 | Register account is disabled!', data: null }) return } if (!isEmail(username)) { res.send({ status: 'Fail', message: '请输入正确的邮箱 | Please enter a valid email address.', data: null }) return } if (isNotEmptyString(config.siteConfig.registerMails)) { let allowSuffix = false const emailSuffixs = config.siteConfig.registerMails.split(',') for (let index = 0; index < emailSuffixs.length; index++) { const element = emailSuffixs[index] allowSuffix = username.toLowerCase().endsWith(element) if (allowSuffix) break } if (!allowSuffix) { res.send({ status: 'Fail', message: '该邮箱后缀不支持 | The email service provider is not allowed', data: null }) return } } const user = await getUser(username) if (user != null) { if (user.status === Status.PreVerify) { await sendVerifyMail(username, await getUserVerifyUrl(username)) throw new Error('请去邮箱中验证 | Please verify in the mailbox') } if (user.status === Status.AdminVerify) throw new Error('请等待管理员开通 | Please wait for the admin to activate') res.send({ status: 'Fail', message: '账号已存在 | The email exists', data: null }) return } const newPassword = md5(password) const isRoot = username.toLowerCase() === process.env.ROOT_USER await createUser(username, newPassword, isRoot) if (isRoot) { res.send({ status: 'Success', message: '注册成功 | Register success', data: null }) } else { await sendVerifyMail(username, await getUserVerifyUrl(username)) res.send({ status: 'Success', message: '注册成功, 去邮箱中验证吧 | Registration is successful, you need to go to email verification', data: null }) } } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/config', rootAuth, async (req, res) => { try { const userId = req.headers.userId.toString() const user = await getUserById(userId) if (user == null || user.status !== Status.Normal || !user.roles.includes(UserRole.Admin)) throw new Error('无权限 | No permission.') const response = await chatConfig() res.send(response) } catch (error) { res.send(error) } }) router.post('/session', async (req, res) => { try { const config = await getCacheConfig() const hasAuth = config.siteConfig.loginEnabled const allowRegister = (await getCacheConfig()).siteConfig.registerEnabled if (config.apiModel !== 'ChatGPTAPI' && config.apiModel !== 'ChatGPTUnofficialProxyAPI') config.apiModel = 'ChatGPTAPI' const userId = await getUserId(req) const chatModels: { label key: string value: string }[] = [] if (userId != null) { const user = await getUserById(userId) const keys = (await getCacheApiKeys()).filter(d => hasAnyRole(d.userRoles, user.roles)) const count: { key: string; count: number }[] = [] chatModelOptions.forEach((chatModel) => { keys.forEach((key) => { if (key.chatModels.includes(chatModel.value)) { if (count.filter(d => d.key === chatModel.value).length <= 0) { count.push({ key: chatModel.value, count: 1 }) } else { const thisCount = count.filter(d => d.key === chatModel.value)[0] thisCount.count++ } } }) }) count.forEach((c) => { const thisChatModel = chatModelOptions.filter(d => d.value === c.key)[0] const suffix = c.count > 1 ? ` (${c.count})` : '' chatModels.push({ label: `${thisChatModel.label}${suffix}`, key: c.key, value: c.key, }) }) } res.send({ status: 'Success', message: '', data: { auth: hasAuth, allowRegister, model: config.apiModel, title: config.siteConfig.siteTitle, chatModels, allChatModels: chatModelOptions, }, }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/user-login', authLimiter, async (req, res) => { try { const { username, password } = req.body as { username: string; password: string } if (!username || !password || !isEmail(username)) throw new Error('用户名或密码为空 | Username or password is empty') const user = await getUser(username) if (user == null || user.password !== md5(password)) throw new Error('用户不存在或密码错误 | User does not exist or incorrect password.') if (user.status === Status.PreVerify) throw new Error('请去邮箱中验证 | Please verify in the mailbox') if (user != null && user.status === Status.AdminVerify) throw new Error('请等待管理员开通 | Please wait for the admin to activate') if (user.status !== Status.Normal) throw new Error('账户状态异常 | Account status abnormal.') const config = await getCacheConfig() const token = jwt.sign({ name: user.name ? user.name : user.email, avatar: user.avatar, description: user.description, userId: user._id, root: user.roles.includes(UserRole.Admin), config: user.config, }, config.siteConfig.loginSalt.trim()) res.send({ status: 'Success', message: '登录成功 | Login successfully', data: { token } }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/user-send-reset-mail', authLimiter, async (req, res) => { try { const { username } = req.body as { username: string } if (!username || !isEmail(username)) throw new Error('请输入格式正确的邮箱 | Please enter a correctly formatted email address.') const user = await getUser(username) if (user == null || user.status !== Status.Normal) throw new Error('账户状态异常 | Account status abnormal.') await sendResetPasswordMail(username, await getUserResetPasswordUrl(username)) res.send({ status: 'Success', message: '重置邮件已发送 | Reset email has been sent', data: null }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/user-reset-password', authLimiter, async (req, res) => { try { const { username, password, sign } = req.body as { username: string; password: string; sign: string } if (!username || !password || !isEmail(username)) throw new Error('用户名或密码为空 | Username or password is empty') if (!sign || !checkUserResetPassword(sign, username)) throw new Error('链接失效, 请重新发送 | The link is invalid, please resend.') const user = await getUser(username) if (user == null || user.status !== Status.Normal) throw new Error('账户状态异常 | Account status abnormal.') updateUserPassword(user._id.toString(), md5(password)) res.send({ status: 'Success', message: '密码重置成功 | Password reset successful', data: null }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/user-info', auth, async (req, res) => { try { const { name, avatar, description } = req.body as UserInfo const userId = req.headers.userId.toString() const user = await getUserById(userId) if (user == null || user.status !== Status.Normal) throw new Error('用户不存在 | User does not exist.') await updateUserInfo(userId, { name, avatar, description } as UserInfo) res.send({ status: 'Success', message: '更新成功 | Update successfully' }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/user-chat-model', auth, async (req, res) => { try { const { chatModel } = req.body as { chatModel: CHATMODEL } const userId = req.headers.userId.toString() const user = await getUserById(userId) if (user == null || user.status !== Status.Normal) throw new Error('用户不存在 | User does not exist.') await updateUserChatModel(userId, chatModel) res.send({ status: 'Success', message: '更新成功 | Update successfully' }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.get('/users', rootAuth, async (req, res) => { try { const page = +req.query.page const size = +req.query.size const data = await getUsers(page, size) res.send({ status: 'Success', message: '获取成功 | Get successfully', data }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/user-status', rootAuth, async (req, res) => { try { const { userId, status } = req.body as { userId: string; status: Status } const user = await getUserById(userId) await updateUserStatus(userId, status) if ((user.status === Status.PreVerify || user.status === Status.AdminVerify) && status === Status.Normal) await sendNoticeMail(user.email) res.send({ status: 'Success', message: '更新成功 | Update successfully' }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/user-role', rootAuth, async (req, res) => { try { const { userId, roles } = req.body as { userId: string; roles: UserRole[] } await updateUserRole(userId, roles) res.send({ status: 'Success', message: '更新成功 | Update successfully' }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/verify', authLimiter, async (req, res) => { try { const { token } = req.body as { token: string } if (!token) throw new Error('Secret key is empty') const username = await checkUserVerify(token) const user = await getUser(username) if (user != null && user.status === Status.Normal) { res.send({ status: 'Fail', message: '账号已存在 | The email exists', data: null }) return } const config = await getCacheConfig() let message = '验证成功 | Verify successfully' if (config.siteConfig.registerReview) { await verifyUser(username, Status.AdminVerify) await sendVerifyMailAdmin(process.env.ROOT_USER, username, await getUserVerifyUrlAdmin(username)) message = '验证成功, 请等待管理员开通 | Verify successfully, Please wait for the admin to activate' } else { await verifyUser(username, Status.Normal) } res.send({ status: 'Success', message, data: null }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/verifyadmin', authLimiter, async (req, res) => { try { const { token } = req.body as { token: string } if (!token) throw new Error('Secret key is empty') const username = await checkUserVerifyAdmin(token) const user = await getUser(username) if (user != null && user.status === Status.Normal) { res.send({ status: 'Fail', message: '账户已开通 | The email has been opened.', data: null }) return } await verifyUser(username, Status.Normal) await sendNoticeMail(username) res.send({ status: 'Success', message: '账户已激活 | Account has been activated.', data: null }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/setting-base', rootAuth, async (req, res) => { try { const { apiKey, apiModel, apiBaseUrl, accessToken, timeoutMs, reverseProxy, socksProxy, socksAuth, httpsProxy } = req.body as Config const thisConfig = await getOriginConfig() thisConfig.apiKey = apiKey thisConfig.apiModel = apiModel thisConfig.apiBaseUrl = apiBaseUrl thisConfig.accessToken = accessToken thisConfig.reverseProxy = reverseProxy thisConfig.timeoutMs = timeoutMs thisConfig.socksProxy = socksProxy thisConfig.socksAuth = socksAuth thisConfig.httpsProxy = httpsProxy await updateConfig(thisConfig) clearConfigCache() const response = await chatConfig() res.send({ status: 'Success', message: '操作成功 | Successfully', data: response.data }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/setting-site', rootAuth, async (req, res) => { try { const config = req.body as SiteConfig const thisConfig = await getOriginConfig() thisConfig.siteConfig = config const result = await updateConfig(thisConfig) clearConfigCache() res.send({ status: 'Success', message: '操作成功 | Successfully', data: result.siteConfig }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/setting-mail', rootAuth, async (req, res) => { try { const config = req.body as MailConfig const thisConfig = await getOriginConfig() thisConfig.mailConfig = config const result = await updateConfig(thisConfig) clearConfigCache() res.send({ status: 'Success', message: '操作成功 | Successfully', data: result.mailConfig }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/mail-test', rootAuth, async (req, res) => { try { const config = req.body as MailConfig const userId = req.headers.userId as string const user = await getUserById(userId) await sendTestMail(user.email, config) res.send({ status: 'Success', message: '发送成功 | Successfully', data: null }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/setting-audit', rootAuth, async (req, res) => { try { const config = req.body as AuditConfig const thisConfig = await getOriginConfig() thisConfig.auditConfig = config const result = await updateConfig(thisConfig) clearConfigCache() if (config.enabled) initAuditService(config) res.send({ status: 'Success', message: '操作成功 | Successfully', data: result.auditConfig }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/audit-test', rootAuth, async (req, res) => { try { const { audit, text } = req.body as { audit: AuditConfig; text: string } const config = await getCacheConfig() if (audit.enabled) initAuditService(audit) const result = await containsSensitiveWords(audit, text) if (audit.enabled) initAuditService(config.auditConfig) res.send({ status: 'Success', message: result ? '含敏感词 | Contains sensitive words' : '不含敏感词 | Does not contain sensitive words.', data: null }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.get('/setting-keys', rootAuth, async (req, res) => { try { const result = await getApiKeys() res.send({ status: 'Success', message: null, data: result }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/setting-key-status', rootAuth, async (req, res) => { try { const { id, status } = req.body as { id: string; status: Status } await updateApiKeyStatus(id, status) clearApiKeyCache() res.send({ status: 'Success', message: '更新成功 | Update successfully' }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/setting-key-upsert', rootAuth, async (req, res) => { try { const keyConfig = req.body as KeyConfig if (keyConfig._id !== undefined) keyConfig._id = new ObjectId(keyConfig._id) await upsertKey(keyConfig) clearApiKeyCache() res.send({ status: 'Success', message: '成功 | Successfully' }) } catch (error) { res.send({ status: 'Fail', message: error.message, data: null }) } }) router.post('/statistics/by-day', auth, async (req, res) => { try { const userId = req.headers.userId const { start, end } = req.body as { start: number; end: number } const data = await getUserStatisticsByDay(new ObjectId(userId as string), start, end) res.send({ status: 'Success', message: '', data }) } catch (error) { res.send(error) } }) app.use('', router) app.use('/api', router) app.listen(3002, () => globalThis.console.log('Server is running on port 3002'))