Extraction schema (#398)

This commit is contained in:
Archer
2023-10-14 23:02:01 +08:00
committed by GitHub
parent 7db8d3ea0f
commit dd8f2744bf
193 changed files with 2036 additions and 15694 deletions

View File

@@ -1,38 +0,0 @@
import { ERROR_ENUM } from '@/service/errorCode';
import { updateApiKeyUsedTime } from './index';
import { OpenApi } from './schema';
export async function authOpenApiKey({ apikey }: { apikey: string }) {
if (!apikey) {
return Promise.reject(ERROR_ENUM.unAuthApiKey);
}
try {
const openApi = await OpenApi.findOne({ apiKey: apikey });
if (!openApi) {
return Promise.reject(ERROR_ENUM.unAuthApiKey);
}
const userId = String(openApi.userId);
// auth limit
if (global.feConfigs?.isPlus) {
if (openApi?.limit?.expiredTime && openApi.limit.expiredTime.getTime() < Date.now()) {
return Promise.reject(`Key ${openApi.apiKey} is expired`);
}
if (
openApi?.limit?.credit &&
openApi.limit.credit > -1 &&
openApi.usage > openApi.limit.credit
) {
return Promise.reject(`Key ${openApi.apiKey} is over usage`);
}
}
updateApiKeyUsedTime(openApi._id);
return { apikey, userId, appId: openApi.appId };
} catch (error) {
return Promise.reject(error);
}
}

View File

@@ -1,18 +0,0 @@
import { OpenApi } from './schema';
export async function updateApiKeyUsedTime(id: string) {
await OpenApi.findByIdAndUpdate(id, {
lastUsedTime: new Date()
});
}
export async function updateApiKeyUsage({ apikey, usage }: { apikey: string; usage: number }) {
await OpenApi.findOneAndUpdate(
{ apiKey: apikey },
{
$inc: {
usage
}
}
);
}

View File

@@ -1,57 +0,0 @@
import { Schema, model, models, Model } from 'mongoose';
import { OpenApiSchema } from '@/types/support/openapi';
import { PRICE_SCALE } from '@fastgpt/common/bill/constants';
import { formatPrice } from '@fastgpt/common/bill/index';
const OpenApiSchema = new Schema(
{
userId: {
type: Schema.Types.ObjectId,
ref: 'user',
required: true
},
apiKey: {
type: String,
required: true,
get: (val: string) => `******${val.substring(val.length - 4)}`
},
createTime: {
type: Date,
default: () => new Date()
},
lastUsedTime: {
type: Date
},
appId: {
type: String,
required: false
},
name: {
type: String,
default: 'Api Key'
},
usage: {
// total usage. value from bill total
type: Number,
default: 0,
get: (val: number) => formatPrice(val)
},
limit: {
expiredTime: {
type: Date
},
credit: {
// value from user settings
type: Number,
default: -1,
set: (val: number) => val * PRICE_SCALE,
get: (val: number) => formatPrice(val)
}
}
},
{
toObject: { getters: true }
}
);
export const OpenApi: Model<OpenApiSchema> = models['openapi'] || model('openapi', OpenApiSchema);

View File

@@ -1,171 +0,0 @@
import { PRICE_SCALE } from '@fastgpt/common/bill/constants';
import { IpLimit } from '@/service/common/ipLimit/schema';
import { authBalanceByUid, AuthUserTypeEnum } from '@/service/utils/auth';
import { OutLinkSchema } from '@/types/support/outLink';
import { OutLink } from './schema';
import axios from 'axios';
type AuthLinkProps = { ip?: string | null; authToken?: string; question: string };
export async function authOutLinkChat({
shareId,
ip,
authToken,
question
}: AuthLinkProps & {
shareId: string;
}) {
// get outLink
const outLink = await OutLink.findOne({
shareId
});
if (!outLink) {
return Promise.reject('分享链接无效');
}
const uid = String(outLink.userId);
const [user] = await Promise.all([
authBalanceByUid(uid), // authBalance
...(global.feConfigs?.isPlus ? [authOutLinkLimit({ outLink, ip, authToken, question })] : []) // limit auth
]);
return {
user,
userId: String(outLink.userId),
appId: String(outLink.appId),
authType: AuthUserTypeEnum.token,
responseDetail: outLink.responseDetail
};
}
export async function authOutLinkLimit({
outLink,
ip,
authToken,
question
}: AuthLinkProps & {
outLink: OutLinkSchema;
}) {
if (!ip || !outLink.limit) {
return;
}
if (outLink.limit.expiredTime && outLink.limit.expiredTime.getTime() < Date.now()) {
return Promise.reject('分享链接已过期');
}
if (outLink.limit.credit > -1 && outLink.total > outLink.limit.credit * PRICE_SCALE) {
return Promise.reject('链接超出使用限制');
}
// ip limit
await (async () => {
if (!outLink.limit) {
return;
}
try {
const ipLimit = await IpLimit.findOne({ ip, eventId: outLink._id });
// first request
if (!ipLimit) {
return await IpLimit.create({
eventId: outLink._id,
ip,
account: outLink.limit.QPM - 1
});
}
// over one minute
const diffTime = Date.now() - ipLimit.lastMinute.getTime();
if (diffTime >= 60 * 1000) {
ipLimit.account = outLink.limit.QPM - 1;
ipLimit.lastMinute = new Date();
return await ipLimit.save();
}
// over limit
if (ipLimit.account <= 0) {
return Promise.reject(
`每分钟仅能请求 ${outLink.limit.QPM} 次, ${60 - Math.round(diffTime / 1000)}s 后重试~`
);
}
// update limit
ipLimit.account = ipLimit.account - 1;
await ipLimit.save();
} catch (error) {}
})();
// url auth. send request
await authShareStart({ authToken, tokenUrl: outLink.limit.hookUrl, question });
}
export async function authOutLinkId({ id }: { id: string }) {
const outLink = await OutLink.findOne({
shareId: id
});
if (!outLink) {
return Promise.reject('分享链接无效');
}
return {
userId: String(outLink.userId)
};
}
type TokenAuthResponseType = {
success: boolean;
msg?: string;
message?: string;
};
export const authShareChatInit = async (authToken?: string, tokenUrl?: string) => {
if (!tokenUrl || !global.feConfigs?.isPlus) return;
try {
const { data } = await axios<TokenAuthResponseType>({
baseURL: tokenUrl,
url: '/shareAuth/init',
method: 'POST',
data: {
token: authToken
}
});
if (data?.success !== true) {
return Promise.reject(data?.message || data?.msg || '身份校验失败');
}
} catch (error) {
return Promise.reject('身份校验失败');
}
};
export const authShareStart = async ({
tokenUrl,
authToken,
question
}: {
authToken?: string;
question: string;
tokenUrl?: string;
}) => {
if (!tokenUrl || !global.feConfigs?.isPlus) return;
try {
const { data } = await axios<TokenAuthResponseType>({
baseURL: tokenUrl,
url: '/shareAuth/start',
method: 'POST',
data: {
token: authToken,
question
}
});
if (data?.success !== true) {
return Promise.reject(data?.message || data?.msg || '身份校验失败');
}
} catch (error) {
return Promise.reject('身份校验失败');
}
};

View File

@@ -1,52 +0,0 @@
import { addLog } from '@/service/utils/tools';
import { ChatHistoryItemResType } from '@/types/chat';
import axios from 'axios';
import { OutLink } from './schema';
export const updateOutLinkUsage = async ({
shareId,
total
}: {
shareId: string;
total: number;
}) => {
try {
await OutLink.findOneAndUpdate(
{ shareId },
{
$inc: { total },
lastTime: new Date()
}
);
} catch (err) {
addLog.error('update shareChat error', err);
}
};
export const pushResult2Remote = async ({
authToken,
shareId,
responseData
}: {
authToken?: string;
shareId?: string;
responseData?: ChatHistoryItemResType[];
}) => {
if (!shareId || !authToken) return;
try {
const outLink = await OutLink.findOne({
shareId
});
if (!outLink?.limit?.hookUrl) return;
axios({
method: 'post',
baseURL: outLink.limit.hookUrl,
url: '/shareAuth/finish',
data: {
token: authToken,
responseData
}
});
} catch (error) {}
};

View File

@@ -1,58 +0,0 @@
import { Schema, model, models, Model } from 'mongoose';
import { OutLinkSchema as SchemaType } from '@/types/support/outLink';
import { OutLinkTypeEnum } from '@/constants/chat';
const OutLinkSchema = new Schema({
shareId: {
type: String,
required: true
},
userId: {
type: Schema.Types.ObjectId,
ref: 'user',
required: true
},
appId: {
type: Schema.Types.ObjectId,
ref: 'model',
required: true
},
type: {
type: String,
default: OutLinkTypeEnum.share
},
name: {
type: String,
required: true
},
total: {
// total amount
type: Number,
default: 0
},
lastTime: {
type: Date
},
responseDetail: {
type: Boolean,
default: false
},
limit: {
expiredTime: {
type: Date
},
QPM: {
type: Number,
default: 1000
},
credit: {
type: Number,
default: -1
},
hookUrl: {
type: String
}
}
});
export const OutLink: Model<SchemaType> = models['outlinks'] || model('outlinks', OutLinkSchema);