feat: app module

This commit is contained in:
archer
2023-06-27 20:41:36 +08:00
parent 7e6272ca1b
commit 4c54e1821b
17 changed files with 2059 additions and 121 deletions

View File

@@ -0,0 +1,25 @@
import { Configuration, OpenAIApi } from 'openai';
export const getSystemOpenAiKey = () => {
return process.env.ONEAPI_KEY || '';
};
export const getOpenAIApi = () => {
return new OpenAIApi(
new Configuration({
basePath: process.env.ONEAPI_URL
})
);
};
/* openai axios config */
export const axiosConfig = () => {
return {
baseURL: process.env.ONEAPI_URL, // 此处仅对非 npm 模块有效
httpsAgent: global.httpsAgent,
headers: {
Authorization: `Bearer ${getSystemOpenAiKey()}`,
auth: process.env.OPENAI_BASE_URL_AUTH || ''
}
};
};

View File

@@ -1,122 +1,79 @@
import axios, { Method, InternalAxiosRequestConfig, AxiosResponse } from 'axios';
import { sseResponseEventEnum } from '@/constants/chat';
import { getErrText } from '@/utils/tools';
import { parseStreamChunk } from '@/utils/adapt';
import { NextApiResponse } from 'next';
import { sseResponse } from '../utils/tools';
interface ConfigType {
headers?: { [key: string]: string };
hold?: boolean;
}
interface ResponseDataType {
code: number;
message: string;
data: any;
interface Props {
res: NextApiResponse; // 用于流转发
url: string;
data: Record<string, any>;
}
export const moduleFetch = ({ url, data, res }: Props) =>
new Promise<Record<string, any>>(async (resolve, reject) => {
try {
const baseUrl = `http://localhost:3000/api`;
const requestUrl = url.startsWith('/') ? `${baseUrl}${url}` : url;
const response = await fetch(requestUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});
/**
* 请求开始
*/
function requestStart(config: InternalAxiosRequestConfig): InternalAxiosRequestConfig {
if (config.headers) {
config.headers.rootkey = process.env.ROOT_KEY;
}
if (!response?.body) {
throw new Error('Request Error');
}
return config;
}
const responseType = response.headers.get('content-type');
if (responseType && responseType.includes('application/json')) {
const jsonResponse = await response.json();
return resolve(jsonResponse?.data || {});
}
/**
* 请求成功,检查请求头
*/
function responseSuccess(response: AxiosResponse<ResponseDataType>) {
return response;
}
/**
* 响应数据检查
*/
function checkRes(data: ResponseDataType) {
if (data === undefined) {
return Promise.reject('服务器异常');
} else if (data.code < 200 || data.code >= 400) {
return Promise.reject(data);
}
return data.data;
}
const reader = response.body?.getReader();
/**
* 响应错误
*/
function responseError(err: any) {
if (!err) {
return Promise.reject({ message: '未知错误' });
}
if (typeof err === 'string') {
return Promise.reject({ message: err });
}
return Promise.reject(err);
}
let chatResponse = {};
/* 创建请求实例 */
export const instance = axios.create({
timeout: 60000, // 超时时间
baseURL: `http://localhost:${process.env.PORT || 3000}/api`,
headers: {
rootkey: process.env.ROOT_KEY
}
});
const read = async () => {
try {
const { done, value } = await reader.read();
if (done) {
return resolve(chatResponse);
}
const chunkResponse = parseStreamChunk(value);
/* 请求拦截 */
instance.interceptors.request.use(requestStart, (err) => Promise.reject(err));
/* 响应拦截 */
instance.interceptors.response.use(responseSuccess, (err) => Promise.reject(err));
function request(url: string, data: any, config: ConfigType, method: Method): any {
/* 去空 */
for (const key in data) {
if (data[key] === null || data[key] === undefined) {
delete data[key];
chunkResponse.forEach((item) => {
// parse json data
const data = (() => {
try {
return JSON.parse(item.data);
} catch (error) {
return {};
}
})();
if (item.event === sseResponseEventEnum.moduleFetchResponse) {
chatResponse = {
...chatResponse,
...data
};
} else if (item.event === sseResponseEventEnum.answer && data?.choices?.[0]?.delta) {
sseResponse({
res,
event: sseResponseEventEnum.answer,
data: JSON.stringify(data)
});
}
});
read();
} catch (err: any) {
reject(getErrText(err, '请求异常'));
}
};
read();
} catch (err: any) {
console.log(err);
reject(getErrText(err, '请求异常'));
}
}
return instance
.request({
url,
method,
data: method === 'GET' ? null : data,
params: method === 'GET' ? data : null, // get请求不携带dataparams放在url上
...config // 用户自定义配置,可以覆盖前面的配置
})
.then((res) => checkRes(res.data))
.catch((err) => responseError(err));
}
/**
* api请求方式
* @param {String} url
* @param {Any} params
* @param {Object} config
* @returns
*/
export function GET<T = { data: any }>(
url: string,
params = {},
config: ConfigType = {}
): Promise<T> {
return request(url, params, config, 'GET');
}
export function POST<T = { data: any }>(
url: string,
data = {},
config: ConfigType = {}
): Promise<T> {
return request(url, data, config, 'POST');
}
export function PUT<T = { data: any }>(
url: string,
data = {},
config: ConfigType = {}
): Promise<T> {
return request(url, data, config, 'PUT');
}
export function DELETE<T = { data: any }>(url: string, config: ConfigType = {}): Promise<T> {
return request(url, {}, config, 'DELETE');
}
});

View File

@@ -1,3 +1,4 @@
import { sseResponseEventEnum } from '@/constants/chat';
import { NextApiResponse } from 'next';
import {
openaiError,
@@ -6,7 +7,7 @@ import {
ERROR_RESPONSE,
ERROR_ENUM
} from './errorCode';
import { clearCookie } from './utils/tools';
import { clearCookie, sseResponse } from './utils/tools';
export interface ResponseType<T = any> {
code: number;
@@ -61,3 +62,41 @@ export const jsonRes = <T = any>(
data: data !== undefined ? data : null
});
};
export const sseErrRes = (res: NextApiResponse, error: any) => {
const errResponseKey = typeof error === 'string' ? error : error?.message;
// Specified error
if (ERROR_RESPONSE[errResponseKey]) {
// login is expired
if (errResponseKey === ERROR_ENUM.unAuthorization) {
clearCookie(res);
}
return sseResponse({
res,
event: sseResponseEventEnum.error,
data: JSON.stringify(ERROR_RESPONSE[errResponseKey])
});
}
let msg = error?.message || '请求错误';
if (typeof error === 'string') {
msg = error;
} else if (proxyError[error?.code]) {
msg = '接口连接异常';
} else if (error?.response?.data?.error?.message) {
msg = error?.response?.data?.error?.message;
} else if (openaiAccountError[error?.response?.data?.error?.code]) {
msg = openaiAccountError[error?.response?.data?.error?.code];
} else if (openaiError[error?.response?.statusText]) {
msg = openaiError[error.response.statusText];
}
console.log(error);
sseResponse({
res,
event: sseResponseEventEnum.error,
data: JSON.stringify({ message: msg })
});
};

View File

@@ -79,7 +79,7 @@ export const sseResponse = ({
data
}: {
res: NextApiResponse;
event?: `${sseResponseEventEnum}`;
event?: string;
data: string;
}) => {
event && res.write(`event: ${event}\n`);