mirror of
https://github.com/labring/FastGPT.git
synced 2026-05-07 01:02:55 +08:00
fix: stt usage (#6855)
* fix: stt usage * perf: skil ssrf with some api * fix:test
This commit is contained in:
@@ -43,6 +43,7 @@ export function createProxyAxios(config?: AxiosRequestConfig, ssrfCheck = true)
|
||||
|
||||
/** @see https://github.com/axios/axios/issues/4531 */
|
||||
export const axios = createProxyAxios();
|
||||
export const axiosWithoutSSRF = createProxyAxios(undefined, false);
|
||||
|
||||
/**
|
||||
* 内部相对路径请求专用的 axios 实例:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type fs from 'fs';
|
||||
import { getAxiosConfig } from '../config';
|
||||
import { axios } from '../../../common/api/axios';
|
||||
import { axiosWithoutSSRF } from '../../../common/api/axios';
|
||||
import FormData from 'form-data';
|
||||
import { type STTModelType } from '@fastgpt/global/core/ai/model.schema';
|
||||
import { UserError } from '@fastgpt/global/common/error/utils';
|
||||
@@ -24,22 +24,19 @@ export const aiTranscriptions = async ({
|
||||
|
||||
const aiAxiosConfig = getAxiosConfig();
|
||||
|
||||
const { data: result } = await axios<{ text: string; usage?: { total_tokens: number } }>({
|
||||
method: 'post',
|
||||
...(modelData.requestUrl
|
||||
? { url: modelData.requestUrl }
|
||||
: {
|
||||
baseURL: aiAxiosConfig.baseUrl,
|
||||
url: '/audio/transcriptions'
|
||||
}),
|
||||
// 管理员配置的 url,允许是内网
|
||||
const { data: result } = await axiosWithoutSSRF.post<{
|
||||
text: string;
|
||||
usage?: { total_tokens: number };
|
||||
}>(modelData.requestUrl ? modelData.requestUrl : '/audio/transcriptions', data, {
|
||||
...(modelData.requestUrl ? {} : { baseURL: aiAxiosConfig.baseUrl }),
|
||||
headers: {
|
||||
Authorization: modelData.requestAuth
|
||||
? `Bearer ${modelData.requestAuth}`
|
||||
: aiAxiosConfig.authorization,
|
||||
...data.getHeaders(),
|
||||
...headers
|
||||
},
|
||||
data: data
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { axios } from '../../../common/api/axios';
|
||||
import { axiosWithoutSSRF } from '../../../common/api/axios';
|
||||
import { getDefaultRerankModel } from '../model';
|
||||
import { getAxiosConfig } from '../config';
|
||||
import { type RerankModelItemType } from '@fastgpt/global/core/ai/model.schema';
|
||||
@@ -93,8 +93,9 @@ export async function reRankRecall({
|
||||
const { baseUrl, authorization } = getAxiosConfig();
|
||||
const start = Date.now();
|
||||
|
||||
// 模型的请求 url,允许是内网
|
||||
const requestUrl = model.requestUrl ? model.requestUrl : `${baseUrl}/rerank`;
|
||||
const apiResult = await axios
|
||||
const apiResult = await axiosWithoutSSRF
|
||||
.post<PostReRankResponse>(
|
||||
requestUrl,
|
||||
{
|
||||
|
||||
@@ -24,7 +24,6 @@ import { JSONPath } from 'jsonpath-plus';
|
||||
import { getSecretValue } from '../../../../common/secret/utils';
|
||||
import type { StoreSecretValueType } from '@fastgpt/global/common/secret/type';
|
||||
import { getLogger, LogCategories } from '../../../../common/logger';
|
||||
import { SERVICE_LOCAL_HOST } from '../../../../common/system/tools';
|
||||
import { formatHttpError } from '../utils';
|
||||
import { isInternalAddress, PRIVATE_URL_TEXT } from '../../../../common/system/utils';
|
||||
import { serviceRequestMaxContentLength } from '../../../../common/system/constants';
|
||||
@@ -504,10 +503,10 @@ async function fetchData({
|
||||
return Promise.reject(PRIVATE_URL_TEXT);
|
||||
}
|
||||
|
||||
// 都认为是用户的请求,强制 SSRF 检查
|
||||
const { data: response } = await axios({
|
||||
method,
|
||||
maxContentLength: serviceRequestMaxContentLength,
|
||||
baseURL: `http://${SERVICE_LOCAL_HOST}`,
|
||||
url,
|
||||
headers: {
|
||||
...headers
|
||||
|
||||
@@ -3,7 +3,6 @@ import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workfl
|
||||
import { DispatchNodeResponseKeyEnum } from '@fastgpt/global/core/workflow/runtime/constants';
|
||||
import { axios } from '../../../../common/api/axios';
|
||||
import { valueTypeFormat } from '@fastgpt/global/core/workflow/runtime/utils';
|
||||
import { SERVICE_LOCAL_HOST } from '../../../../common/system/tools';
|
||||
import { type DispatchNodeResultType } from '@fastgpt/global/core/workflow/runtime/type';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { getLogger, LogCategories } from '../../../../common/logger';
|
||||
@@ -122,7 +121,6 @@ async function fetchData({
|
||||
}): Promise<Record<string, any>> {
|
||||
const { data: response } = await axios({
|
||||
method,
|
||||
baseURL: `http://${SERVICE_LOCAL_HOST}`,
|
||||
url,
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
|
||||
@@ -13,9 +13,9 @@ vi.mock('@fastgpt/service/common/string/tiktoken', () => ({
|
||||
countPromptTokens: mockCountPromptTokens
|
||||
}));
|
||||
|
||||
// rerank 现在改用统一 axios(带 SSRF 拦截),mock axios.post 返回 axios 风格的 { data, ... }
|
||||
// rerank 走 axiosWithoutSSRF(管理员配置的 url 允许内网),mock 它的 .post 返回 axios 风格的 { data, ... }
|
||||
vi.mock('@fastgpt/service/common/api/axios', () => ({
|
||||
axios: {
|
||||
axiosWithoutSSRF: {
|
||||
post: (...args: any[]) => Promise.resolve(mockAxiosPost(...args)).then((data) => ({ data }))
|
||||
}
|
||||
}));
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SandboxCodeTypeEnum } from '@fastgpt/global/core/workflow/template/system/sandbox/constants';
|
||||
import type { AxiosInstance } from 'axios';
|
||||
import axios from 'axios';
|
||||
import { createProxyAxios } from '../../common/api/axios';
|
||||
import { getLogger, LogCategories } from '../../common/logger';
|
||||
const logger = getLogger(LogCategories.MODULE.WORKFLOW.CODE_SANDBOX);
|
||||
|
||||
@@ -17,14 +17,17 @@ export class CodeSandbox {
|
||||
const baseUrl = process.env.CODE_SANDBOX_URL || '';
|
||||
const token = process.env.CODE_SANDBOX_TOKEN || '';
|
||||
|
||||
this.client = axios.create({
|
||||
baseURL: `${baseUrl.replace(/\/$/, '')}/sandbox`,
|
||||
timeout: 180000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: token ? `Bearer ${token}` : undefined
|
||||
}
|
||||
});
|
||||
this.client = createProxyAxios(
|
||||
{
|
||||
baseURL: `${baseUrl.replace(/\/$/, '')}/sandbox`,
|
||||
timeout: 180000,
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: token ? `Bearer ${token}` : undefined
|
||||
}
|
||||
},
|
||||
false
|
||||
);
|
||||
|
||||
this.client.interceptors.response.use(
|
||||
(response) => {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { matchMdImg } from '@fastgpt/global/common/string/markdown';
|
||||
import axios from 'axios';
|
||||
import { createProxyAxios } from '../../common/api/axios';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { getLogger, LogCategories } from '../../common/logger';
|
||||
|
||||
export const useTextinServer = ({ appId, secretCode }: { appId: string; secretCode: string }) => {
|
||||
const logger = getLogger(LogCategories.MODULE.DATASET.FILE);
|
||||
// Init request
|
||||
const instance = axios.create({
|
||||
const instance = createProxyAxios({
|
||||
baseURL: 'https://api.textin.com/ai/service/v1',
|
||||
timeout: 300000,
|
||||
headers: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
import { authSystemAdmin } from '@fastgpt/service/support/permission/user/auth';
|
||||
import { axios } from '@fastgpt/service/common/api/axios';
|
||||
import { axiosWithoutSSRF } from '@fastgpt/service/common/api/axios';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
|
||||
const baseUrl = process.env.AIPROXY_API_ENDPOINT;
|
||||
@@ -14,7 +14,7 @@ async function handler(req: ApiRequestProps, res: ApiResponseType<any>) {
|
||||
return Promise.reject('AIPROXY_API_ENDPOINT or AIPROXY_API_TOKEN is not set');
|
||||
}
|
||||
|
||||
const { data } = await axios.post(`${baseUrl}/api/channel/`, req.body, {
|
||||
const { data } = await axiosWithoutSSRF.post(`${baseUrl}/api/channel/`, req.body, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
import { getLogger, LogCategories } from '@fastgpt/service/common/logger';
|
||||
import { type Method } from 'axios';
|
||||
import { createProxyAxios } from '@fastgpt/service/common/api/axios';
|
||||
const logger = getLogger(LogCategories.NETWORK);
|
||||
|
||||
const url = process.env.API_PROXY_URL;
|
||||
const token = process.env.API_PROXY_TOKEN;
|
||||
|
||||
const instance = createProxyAxios({
|
||||
baseURL: url,
|
||||
timeout: 60000, // 超时时间
|
||||
headers: {
|
||||
Authorization: `Bearer ${token}`
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* 响应数据检查
|
||||
*/
|
||||
const checkRes = (data: any) => {
|
||||
if (data === undefined) {
|
||||
logger.info('api proxy data is empty');
|
||||
return Promise.reject('服务器异常');
|
||||
}
|
||||
return data.data;
|
||||
};
|
||||
const responseError = (err: any) => {
|
||||
logger.error('API proxy request failed', { error: err });
|
||||
|
||||
if (!err) {
|
||||
return Promise.reject({ message: '未知错误' });
|
||||
}
|
||||
if (typeof err === 'string') {
|
||||
return Promise.reject({ message: err });
|
||||
}
|
||||
if (typeof err.message === 'string') {
|
||||
return Promise.reject({ message: err.message });
|
||||
}
|
||||
if (typeof err.data === 'string') {
|
||||
return Promise.reject({ message: err.data });
|
||||
}
|
||||
if (err?.response?.data) {
|
||||
return Promise.reject(err?.response?.data);
|
||||
}
|
||||
return Promise.reject(err);
|
||||
};
|
||||
|
||||
const request = <T>(url: string, data: any, method: Method): Promise<T> => {
|
||||
/* 去空 */
|
||||
for (const key in data) {
|
||||
if (data[key] === undefined) {
|
||||
delete data[key];
|
||||
}
|
||||
}
|
||||
|
||||
return instance
|
||||
.request({
|
||||
url,
|
||||
method,
|
||||
data: ['POST', 'PUT'].includes(method) ? data : undefined,
|
||||
params: !['POST', 'PUT'].includes(method) ? data : undefined
|
||||
})
|
||||
.then((res) => checkRes(res.data))
|
||||
.catch((err) => responseError(err));
|
||||
};
|
||||
|
||||
// TODO: channel crud
|
||||
export const ApiProxy = {};
|
||||
@@ -2,9 +2,8 @@ import { UsageItemTypeEnum, UsageSourceEnum } from '@fastgpt/global/support/wall
|
||||
import { createUsage, concatUsage } from '@fastgpt/service/support/wallet/usage/controller';
|
||||
import { formatModelChars2Points } from '@fastgpt/service/support/wallet/usage/utils';
|
||||
import { i18nT } from '@fastgpt/web/i18n/utils';
|
||||
import { getDefaultTTSModel } from '@fastgpt/service/core/ai/model';
|
||||
import { getDefaultSTTModel } from '@fastgpt/service/core/ai/model';
|
||||
import type { UsageItemType } from '@fastgpt/global/support/wallet/usage/type';
|
||||
import type { HelperBotTypeEnumType } from '@fastgpt/global/core/chat/helperBot/type';
|
||||
|
||||
export const pushHelperBotUsage = ({
|
||||
teamId,
|
||||
@@ -246,7 +245,7 @@ export const pushWhisperUsage = ({
|
||||
tmbId: string;
|
||||
duration: number;
|
||||
}) => {
|
||||
const whisperModel = getDefaultTTSModel();
|
||||
const whisperModel = getDefaultSTTModel();
|
||||
|
||||
if (!whisperModel) return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user