diff --git a/package.json b/package.json index 000ed0323..c2ccad361 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "mongoose": "^6.10.0", "nanoid": "^4.0.1", "next": "13.1.6", + "nextjs-cors": "^2.1.2", "nodemailer": "^6.9.1", "nprogress": "^0.2.0", "openai": "^3.2.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d9de80e66..a9056797b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -46,6 +46,7 @@ specifiers: mongoose: ^6.10.0 nanoid: ^4.0.1 next: 13.1.6 + nextjs-cors: ^2.1.2 nodemailer: ^6.9.1 nprogress: ^0.2.0 openai: ^3.2.1 @@ -97,6 +98,7 @@ dependencies: mongoose: registry.npmmirror.com/mongoose/6.10.0 nanoid: registry.npmmirror.com/nanoid/4.0.1 next: registry.npmmirror.com/next/13.1.6_wiv434v7erz4aedd5whhdwmpv4 + nextjs-cors: 2.1.2_next@13.1.6 nodemailer: registry.npmmirror.com/nodemailer/6.9.1 nprogress: registry.npmmirror.com/nprogress/0.2.0 openai: registry.npmmirror.com/openai/3.2.1 @@ -309,6 +311,14 @@ packages: engines: {node: '>= 0.6'} dev: false + /cors/2.8.5: + resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} + engines: {node: '>= 0.10'} + dependencies: + object-assign: 4.1.1 + vary: 1.1.2 + dev: false + /fsevents/2.3.2: resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -323,6 +333,20 @@ packages: dev: false optional: true + /nextjs-cors/2.1.2_next@13.1.6: + resolution: {integrity: sha512-2yOVivaaf2ILe4f/qY32hnj3oC77VCOsUQJQfhVMGsXE/YMEWUY2zy78sH9FKUCM7eG42/l3pDofIzMD781XGA==} + peerDependencies: + next: ^8.1.1-canary.54 || ^9.0.0 || ^10.0.0-0 || ^11.0.0 || ^12.0.0 || ^13.0.0 + dependencies: + cors: 2.8.5 + next: registry.npmmirror.com/next/13.1.6_wiv434v7erz4aedd5whhdwmpv4 + dev: false + + /object-assign/4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + dev: false + /saslprep/1.0.3: resolution: {integrity: sha512-/MY/PEMbk2SuY5sScONwhUDsV2p77Znkb/q3nSVstq/yQzYJOH/Azh29p9oJLsl3LnQwSvZDKagDGBsBwSooag==} engines: {node: '>=6'} @@ -339,6 +363,11 @@ packages: dev: false optional: true + /vary/1.1.2: + resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} + engines: {node: '>= 0.8'} + dev: false + registry.npmmirror.com/@alicloud/credentials/2.2.6: resolution: {integrity: sha512-jG+msY77dHmAF3x+8VTy7fEgORyXLHmDci8t92HeipBdCHsPptDegA++GEwKgR7f6G4wvafYt+aqMZ1iligdrQ==, registry: https://registry.npm.taobao.org/, tarball: https://registry.npmmirror.com/@alicloud/credentials/-/credentials-2.2.6.tgz} name: '@alicloud/credentials' @@ -9453,7 +9482,7 @@ packages: version: 2.7.0 dependencies: any-promise: registry.npmmirror.com/any-promise/1.3.0 - object-assign: registry.npmmirror.com/object-assign/4.1.1 + object-assign: 4.1.1 thenify-all: registry.npmmirror.com/thenify-all/1.6.0 dev: false diff --git a/src/pages/api/openapi/chat/chat.ts b/src/pages/api/openapi/chat/chat.ts index f8b80bea3..ae9c18d64 100644 --- a/src/pages/api/openapi/chat/chat.ts +++ b/src/pages/api/openapi/chat/chat.ts @@ -8,9 +8,10 @@ import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model'; import { pushChatBill } from '@/service/events/pushBill'; import { searchKb } from '@/service/plugins/searchKb'; import { ChatRoleEnum } from '@/constants/chat'; +import { withNextCors } from '@/service/utils/tools'; /* 发送提示词 */ -export default async function handler(req: NextApiRequest, res: NextApiResponse) { +export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { let step = 0; // step=1时,表示开始了流响应 res.on('error', () => { console.log('error: ', 'request error'); @@ -143,4 +144,4 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) }); } } -} +}); diff --git a/src/pages/api/openapi/chat/lafGpt.ts b/src/pages/api/openapi/chat/lafGpt.ts deleted file mode 100644 index 6e7864588..000000000 --- a/src/pages/api/openapi/chat/lafGpt.ts +++ /dev/null @@ -1,184 +0,0 @@ -import type { NextApiRequest, NextApiResponse } from 'next'; -import { connectToDatabase } from '@/service/mongo'; -import { authOpenApiKey, authModel, getApiKey } from '@/service/utils/auth'; -import { resStreamResponse, modelServiceToolMap } from '@/service/utils/chat'; -import { ChatItemSimpleType } from '@/types/chat'; -import { jsonRes } from '@/service/response'; -import { ChatModelMap, ModelVectorSearchModeMap } from '@/constants/model'; -import { pushChatBill } from '@/service/events/pushBill'; -import { searchKb } from '@/service/plugins/searchKb'; -import { ChatRoleEnum } from '@/constants/chat'; - -/* 发送提示词 */ -export default async function handler(req: NextApiRequest, res: NextApiResponse) { - let step = 0; // step=1时,表示开始了流响应 - res.on('error', () => { - console.log('error: ', 'request error'); - res.end(); - }); - - try { - const { - prompt, - modelId, - isStream = true - } = req.body as { - prompt: ChatItemSimpleType; - modelId: string; - isStream: boolean; - }; - - if (!prompt || !modelId) { - throw new Error('缺少参数'); - } - - await connectToDatabase(); - let startTime = Date.now(); - - /* 凭证校验 */ - const { userId } = await authOpenApiKey(req); - - /* 查找数据库里的模型信息 */ - const { model } = await authModel({ - userId, - modelId - }); - - /* get api key */ - const { systemAuthKey: apiKey } = await getApiKey({ - model: model.chat.chatModel, - userId, - mustPay: true - }); - - const modelConstantsData = ChatModelMap[model.chat.chatModel]; - - console.log('laf gpt start'); - - // 请求一次 chatgpt 拆解需求 - const { responseText: resolveText, totalTokens: resolveTokens } = await modelServiceToolMap[ - model.chat.chatModel - ].chatCompletion({ - apiKey, - temperature: 0, - messages: [ - { - obj: ChatRoleEnum.System, - value: `服务端逻辑生成器.根据用户输入的需求,拆解成 laf 云函数实现的步骤,只返回步骤,按格式返回步骤: 1.\n2.\n3.\n ...... -下面是一些例子: -一个 hello world 例子 -1. 返回字符串: "hello world" - -计算圆的面积 -1. 从 body 中获取半径 radius. -2. 校验 radius 是否为有效的数字. -3. 计算圆的面积. -4. 返回圆的面积: {area} - -实现一个手机号发生注册验证码方法. -1. 从 query 中获取 phone. -2. 校验手机号格式是否正确,不正确则返回错误原因:手机号格式错误. -3. 给 phone 发送一个短信验证码,验证码长度为6位字符串,内容为:你正在注册laf,验证码为:code. -4. 数据库添加数据,表为"codes",内容为 {phone, code}. - -实现一个云函数,使用手机号注册账号,需要验证手机验证码. -1. 从 body 中获取 phone 和 code. -2. 校验手机号格式是否正确,不正确则返回错误原因:手机号格式错误. -2. 获取数据库数据,表为"codes",查找是否有符合 phone, code 等于body参数的记录,没有的话返回错误原因:验证码不正确. -4. 添加数据库数据,表为"users" ,内容为{phone, code, createTime}. -5. 删除数据库数据,删除 code 记录. -6. 返回新建用户的Id: return {userId} - -更新博客记录。传入blogId,blogText,tags,还需要记录更新的时间. -1. 从 body 中获取 blogId,blogText 和 tags. -2. 校验 blogId 是否为空,为空则返回错误原因:博客ID不能为空. -3. 校验 blogText 是否为空,为空则返回错误原因:博客内容不能为空. -4. 校验 tags 是否为数组,不是则返回错误原因:标签必须为数组. -5. 获取当前时间,记录为 updateTime. -6. 更新数据库数据,表为"blogs",更新符合 blogId 的记录的内容为{blogText, tags, updateTime}. -7. 返回结果 "更新博客记录成功"` - }, - { - obj: ChatRoleEnum.Human, - value: prompt.value - } - ], - stream: false - }); - - prompt.value += ` ${resolveText}`; - console.log('prompt resolve success, time:', `${(Date.now() - startTime) / 1000}s`); - - // 读取对话内容 - const prompts = [prompt]; - - // 获取向量匹配到的提示词 - const { searchPrompts } = await searchKb({ - similarity: ModelVectorSearchModeMap[model.chat.searchMode]?.similarity, - prompts, - model, - userId - }); - - prompts.splice(prompts.length - 1, 0, ...searchPrompts); - - // 计算温度 - const temperature = (modelConstantsData.maxTemperature * (model.chat.temperature / 10)).toFixed( - 2 - ); - - // 发出请求 - const { streamResponse, responseMessages, responseText, totalTokens } = - await modelServiceToolMap[model.chat.chatModel].chatCompletion({ - apiKey, - temperature: +temperature, - messages: prompts, - stream: isStream - }); - - console.log('api response time:', `${(Date.now() - startTime) / 1000}s`); - - let textLen = resolveText.length; - let tokens = resolveTokens; - - if (isStream) { - step = 1; - const { finishMessages, totalTokens } = await resStreamResponse({ - model: model.chat.chatModel, - res, - chatResponse: streamResponse, - prompts - }); - textLen += finishMessages.map((item) => item.value).join('').length; - tokens += totalTokens; - } else { - textLen += responseMessages.map((item) => item.value).join('').length; - tokens += totalTokens; - jsonRes(res, { - data: responseText - }); - } - - console.log('laf gpt done. time:', `${(Date.now() - startTime) / 1000}s`); - - pushChatBill({ - isPay: true, - chatModel: model.chat.chatModel, - userId, - textLen, - tokens - }); - } catch (err: any) { - if (step === 1) { - // 直接结束流 - console.log('error,结束'); - res.end(); - } else { - res.status(500); - jsonRes(res, { - code: 500, - error: err - }); - } - } -} diff --git a/src/pages/api/openapi/kb/delDataById.ts b/src/pages/api/openapi/kb/delDataById.ts index 77b61d23a..a3905677a 100644 --- a/src/pages/api/openapi/kb/delDataById.ts +++ b/src/pages/api/openapi/kb/delDataById.ts @@ -2,8 +2,9 @@ import type { NextApiRequest, NextApiResponse } from 'next'; import { jsonRes } from '@/service/response'; import { authToken } from '@/service/utils/auth'; import { PgClient } from '@/service/pg'; +import { withNextCors } from '@/service/utils/tools'; -export default async function handler(req: NextApiRequest, res: NextApiResponse) { +export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { let { dataId } = req.query as { dataId: string; @@ -28,4 +29,4 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< error: err }); } -} +}); diff --git a/src/pages/api/openapi/kb/pushData.ts b/src/pages/api/openapi/kb/pushData.ts index 9d0d12e62..7099be58e 100644 --- a/src/pages/api/openapi/kb/pushData.ts +++ b/src/pages/api/openapi/kb/pushData.ts @@ -6,8 +6,9 @@ import { authToken } from '@/service/utils/auth'; import { generateVector } from '@/service/events/generateVector'; import { PgClient } from '@/service/pg'; import { authKb } from '@/service/utils/auth'; +import { withNextCors } from '@/service/utils/tools'; -export default async function handler(req: NextApiRequest, res: NextApiResponse) { +export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { const { kbId, @@ -89,4 +90,4 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< error: err }); } -} +}); diff --git a/src/pages/api/openapi/kb/updateData.ts b/src/pages/api/openapi/kb/updateData.ts index 8750db26b..5ade8020f 100644 --- a/src/pages/api/openapi/kb/updateData.ts +++ b/src/pages/api/openapi/kb/updateData.ts @@ -4,8 +4,9 @@ import { authToken } from '@/service/utils/auth'; import { ModelDataStatusEnum } from '@/constants/model'; import { generateVector } from '@/service/events/generateVector'; import { PgClient } from '@/service/pg'; +import { withNextCors } from '@/service/utils/tools'; -export default async function handler(req: NextApiRequest, res: NextApiResponse) { +export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { const { dataId, a, q } = req.body as { dataId: string; a: string; q?: string }; @@ -39,4 +40,4 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse< error: err }); } -} +}); diff --git a/src/pages/api/openapi/text/splitText.ts b/src/pages/api/openapi/text/splitText.ts index ea1241e8d..9f7633a42 100644 --- a/src/pages/api/openapi/text/splitText.ts +++ b/src/pages/api/openapi/text/splitText.ts @@ -6,9 +6,10 @@ import { generateVector } from '@/service/events/generateVector'; import { generateQA } from '@/service/events/generateQA'; import { PgClient } from '@/service/pg'; import { SplitTextTypEnum } from '@/constants/plugin'; +import { withNextCors } from '@/service/utils/tools'; /* split text */ -export default async function handler(req: NextApiRequest, res: NextApiResponse) { +export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse) { try { const { chunks, kbId, prompt, mode } = req.body as { kbId: string; @@ -62,7 +63,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse) error: err }); } -} +}); export const config = { api: { diff --git a/src/service/events/generateQA.ts b/src/service/events/generateQA.ts index cab56239b..1a8d80a1e 100644 --- a/src/service/events/generateQA.ts +++ b/src/service/events/generateQA.ts @@ -8,7 +8,7 @@ import { PgClient } from '@/service/pg'; import { SplitDataSchema } from '@/types/mongoSchema'; import { modelServiceToolMap } from '../utils/chat'; import { ChatRoleEnum } from '@/constants/chat'; -import { getErrMessage } from '../utils/tools'; +import { getErrText } from '@/utils/tools'; export async function generateQA(next = false): Promise { if (process.env.queueTask !== '1') { @@ -56,7 +56,7 @@ export async function generateQA(next = false): Promise { // 余额不够了, 清空该记录 await SplitData.findByIdAndUpdate(dataItem._id, { textList: [], - errorText: getErrMessage(err, '获取 OpenAi Key 失败') + errorText: getErrText(err, '获取 OpenAi Key 失败') }); generateQA(true); return; diff --git a/src/service/events/generateVector.ts b/src/service/events/generateVector.ts index 8202abf4c..fef40efab 100644 --- a/src/service/events/generateVector.ts +++ b/src/service/events/generateVector.ts @@ -2,7 +2,7 @@ import { openaiCreateEmbedding } from '../utils/chat/openai'; import { getApiKey } from '../utils/auth'; import { openaiError2 } from '../errorCode'; import { PgClient } from '@/service/pg'; -import { getErrMessage } from '../utils/tools'; +import { getErrText } from '@/utils/tools'; export async function generateVector(next = false): Promise { if (process.env.queueTask !== '1') { @@ -51,7 +51,7 @@ export async function generateVector(next = false): Promise { where: [['id', dataId]] }); generateVector(true); - getErrMessage(err, '获取 OpenAi Key 失败'); + getErrText(err, '获取 OpenAi Key 失败'); return; } diff --git a/src/service/utils/tools.ts b/src/service/utils/tools.ts index f657935e0..106528888 100644 --- a/src/service/utils/tools.ts +++ b/src/service/utils/tools.ts @@ -1,4 +1,5 @@ -import type { NextApiResponse } from 'next'; +import type { NextApiResponse, NextApiHandler, NextApiRequest } from 'next'; +import NextCors from 'nextjs-cors'; import crypto from 'crypto'; import jwt from 'jsonwebtoken'; @@ -37,11 +38,19 @@ export const axiosConfig = () => ({ } }); -/** - * get error message - */ -export const getErrMessage = (err: any, defaultMsg = ''): string => { - const msg = typeof err === 'string' ? err : err?.message || defaultMsg || ''; - msg && console.log('error =>', msg); - return msg; -}; +export function withNextCors(handler: NextApiHandler): NextApiHandler { + return async function nextApiHandlerWrappedWithNextCors( + req: NextApiRequest, + res: NextApiResponse + ) { + const methods = ['GET', 'HEAD', 'PUT', 'PATCH', 'POST', 'DELETE']; + const origin = req.headers.origin; + await NextCors(req, res, { + methods, + origin: origin, + optionsSuccessStatus: 200 + }); + + return handler(req, res); + }; +} diff --git a/src/utils/tools.ts b/src/utils/tools.ts index b16ca753f..94468353d 100644 --- a/src/utils/tools.ts +++ b/src/utils/tools.ts @@ -122,5 +122,7 @@ export const formatLinkText = (text: string) => { }; export const getErrText = (err: any, def = '') => { - return typeof err === 'string' ? err : err?.message || def; + const msg = typeof err === 'string' ? err : err?.message || def || ''; + msg && console.log('error =>', msg); + return msg; };