Add share link hook (#351)

This commit is contained in:
Archer
2023-09-25 23:12:42 +08:00
committed by GitHub
parent 9136c9306a
commit 63cd379682
29 changed files with 430 additions and 98 deletions

View File

@@ -248,7 +248,10 @@
"QPM Tips": "The maximum number of queries per IP address per minute",
"QPM is empty": "QPM is empty",
"Response Detail": "Quote",
"Response Detail tips": "Whether detailed data such as references to be returned"
"Response Detail tips": "Whether detailed data such as references to be returned",
"token auth": "Token Auth",
"token auth Tips": "Identity verification server address. If this value is set, the server will be specified to send a request for identity verification before each session",
"token auth use cases": "Review the authentication instructions"
},
"system": {
"Help Document": "Document"

View File

@@ -248,7 +248,10 @@
"QPM Tips": "每个 IP 每分钟最多提问多少次",
"QPM is empty": "QPM 不能为空",
"Response Detail": "返回详情",
"Response Detail tips": "是否需要返回详情(引用内容,调用时间等,不会返回预设提示词和完整上下文)"
"Response Detail tips": "是否需要返回详情(引用内容,调用时间等,不会返回预设提示词和完整上下文)",
"token auth": "身份验证",
"token auth Tips": "身份校验服务器地址,如填写该值,每次对话前都会想指定服务器发送一个请求,进行身份校验",
"token auth use cases": "查看身份验证使用说明"
},
"system": {
"Help Document": "帮助文档"

View File

@@ -1,10 +1,11 @@
import { KbTypeEnum } from '@/constants/dataset';
import type { RequestPaging } from '@/types';
import { TrainingModeEnum } from '@/constants/plugin';
import { DatasetDataItemType } from '@/types/core/dataset/data';
export type PushDataProps = {
kbId: string;
data: DatasetItemType[];
data: DatasetDataItemType[];
mode: `${TrainingModeEnum}`;
prompt?: string;
billId?: string;

View File

@@ -6,7 +6,7 @@ import type { OutLinkSchema } from '@/types/support/outLink';
/**
* 初始化分享聊天
*/
export const initShareChatInfo = (data: { shareId: string }) =>
export const initShareChatInfo = (data: { shareId: string; authToken?: string }) =>
GET<InitShareChatResponse>(`/support/outLink/init`, data);
/**

View File

@@ -139,6 +139,7 @@ const ChatBox = (
userAvatar,
variableModules,
welcomeText,
active = true,
onUpdateVariable,
onStartChat,
onDelMessage
@@ -152,6 +153,7 @@ const ChatBox = (
userAvatar?: string;
variableModules?: VariableItemType[];
welcomeText?: string;
active?: boolean;
onUpdateVariable?: (e: Record<string, any>) => void;
onStartChat?: (e: StartChatFnProps) => Promise<{
responseText: string;
@@ -860,7 +862,7 @@ const ChatBox = (
</Box>
</Box>
{/* input */}
{onStartChat && variableIsFinish ? (
{onStartChat && variableIsFinish && active ? (
<Box m={['0 auto', '10px auto']} w={'100%'} maxW={['auto', 'min(750px, 100%)']} px={[0, 5]}>
<Box
py={'18px'}

View File

@@ -40,7 +40,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
await connectToDatabase();
/* user auth */
const { userId, user } = await authUser({ req, authBalance: true });
const { userId, user } = await authUser({ req, authToken: true, authBalance: true });
if (!user) {
throw new Error('user not found');

View File

@@ -9,7 +9,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
try {
const { name } = req.body as CreateTrainingBillType;
const { userId } = await authUser({ req, authToken: true });
const { userId } = await authUser({ req, authToken: true, authApiKey: true });
await connectToDatabase();

View File

@@ -16,7 +16,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
}
// 凭证校验
const { userId } = await authUser({ req });
const { userId } = await authUser({ req, authToken: true });
await PgClient.delete(PgDatasetTableName, {
where: [['user_id', userId], 'AND', ['id', dataId]]

View File

@@ -21,7 +21,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
await connectToDatabase();
// 凭证校验
const { userId } = await authUser({ req });
const { userId } = await authUser({ req, authToken: true });
jsonRes(res, {
data: await getVectorAndInsertDataset({

View File

@@ -36,7 +36,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
await connectToDatabase();
// 凭证校验
const { userId } = await authUser({ req });
const { userId } = await authUser({ req, authToken: true, authApiKey: true });
jsonRes<PushDataResponse>(res, {
data: await pushDataToKb({

View File

@@ -20,7 +20,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
// auth user and get kb
const [{ userId }, kb] = await Promise.all([
authUser({ req }),
authUser({ req, authToken: true }),
KB.findById(kbId, 'vectorModel')
]);

View File

@@ -18,7 +18,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
// 凭证校验
const [{ userId }, kb] = await Promise.all([
authUser({ req }),
authUser({ req, authToken: true, authApiKey: true }),
KB.findById(kbId, 'vectorModel')
]);

View File

@@ -17,7 +17,7 @@ type Response = {
export default withNextCors(async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
try {
const { userId } = await authUser({ req });
const { userId } = await authUser({ req, authToken: true });
let { input, model } = req.query as Props;
if (!Array.isArray(input)) {

View File

@@ -34,7 +34,7 @@ import requestIp from 'request-ip';
import { replaceVariable } from '@/utils/common/tools/text';
import { ModuleDispatchProps } from '@/types/core/modules';
import { selectShareResponse } from '@/utils/service/core/chat';
import { updateOutLinkUsage } from '@/service/support/outLink';
import { pushResult2Remote, updateOutLinkUsage } from '@/service/support/outLink';
import { updateApiKeyUsage } from '@/service/support/openapi';
export type MessageItemType = ChatCompletionRequestMessage & { dataId?: string };
@@ -44,6 +44,7 @@ type FastGptWebChatProps = {
};
type FastGptShareChatProps = {
shareId?: string;
authToken?: string;
};
export type Props = CreateChatCompletionRequest &
FastGptWebChatProps &
@@ -71,6 +72,7 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
chatId,
appId,
shareId,
authToken,
stream = false,
detail = false,
messages = [],
@@ -111,10 +113,15 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
if (shareId) {
return authOutLinkChat({
shareId,
ip: requestIp.getClientIp(req)
ip: requestIp.getClientIp(req),
authToken,
question:
(messages[messages.length - 2]?.role === 'user'
? messages[messages.length - 2].content
: messages[messages.length - 1]?.content) || ''
});
}
return authUser({ req, authBalance: true });
return authUser({ req, authToken: true, authApiKey: true, authBalance: true });
})();
if (!user) {
@@ -260,11 +267,13 @@ export default withNextCors(async function handler(req: NextApiRequest, res: Nex
response: responseData
});
!!shareId &&
if (shareId) {
pushResult2Remote({ authToken, shareId, responseData });
updateOutLinkUsage({
shareId,
total
});
}
!!apikey &&
updateApiKeyUsage({
apikey,

View File

@@ -16,7 +16,7 @@ export type Response = { history: ChatItemType[] };
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { userId } = await authUser({ req });
const { userId } = await authUser({ req, authToken: true });
const { chatId, limit } = req.body as Props;
jsonRes<Response>(res, {

View File

@@ -18,7 +18,7 @@ const fetchContent = async (req: NextApiRequest, res: NextApiResponse) => {
throw new Error('urlList is empty');
}
await authUser({ req });
await authUser({ req, authToken: true });
urlList = urlList.filter((url) => /^(http|https):\/\/[^ "]+$/.test(url));

View File

@@ -14,7 +14,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
throw new Error('fileId is empty');
}
const { userId } = await authUser({ req });
const { userId } = await authUser({ req, authToken: true });
const gridFs = new GridFSStorage('dataset', userId);

View File

@@ -16,7 +16,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
throw new Error('fileId is empty');
}
const { userId } = await authUser({ req });
const { userId } = await authUser({ req, authToken: true });
// auth file
const gridFs = new GridFSStorage('dataset', userId);

View File

@@ -5,12 +5,14 @@ import type { InitShareChatResponse } from '@/api/response/chat';
import { authApp } from '@/service/utils/auth';
import { HUMAN_ICON } from '@/constants/chat';
import { getChatModelNameList, getSpecialModule } from '@/components/ChatBox/utils';
import { authShareChatInit } from '@/service/support/outLink/auth';
/* init share chat window */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
let { shareId } = req.query as {
let { shareId, authToken } = req.query as {
shareId: string;
authToken?: string;
};
if (!shareId) {
@@ -36,7 +38,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
userId: String(shareChat.userId),
authOwner: false
}),
User.findById(shareChat.userId, 'avatar')
User.findById(shareChat.userId, 'avatar'),
authShareChatInit(authToken, shareChat.limit?.hookUrl)
]);
jsonRes<InitShareChatResponse>(res, {

View File

@@ -17,7 +17,8 @@ import {
Menu,
MenuButton,
MenuList,
MenuItem
MenuItem,
Link
} from '@chakra-ui/react';
import { QuestionOutlineIcon } from '@chakra-ui/icons';
import MyIcon from '@/components/Icon';
@@ -58,7 +59,7 @@ const Share = ({ appId }: { appId: string }) => {
} = useQuery(['initShareChatList', appId], () => getShareChatList(appId));
return (
<Box position={'relative'} pt={[3, 5, 8]} px={[5, 8]} minH={'50vh'}>
<Box position={'relative'} pt={[3, 5, 8]} px={[2, 8]} minH={'50vh'}>
<Flex justifyContent={'space-between'}>
<Box fontWeight={'bold'}>
@@ -85,7 +86,7 @@ const Share = ({ appId }: { appId: string }) => {
</Button>
</Flex>
<TableContainer mt={3}>
<Table variant={'simple'} w={'100%'} overflowX={'auto'}>
<Table variant={'simple'} w={'100%'} overflowX={'auto'} fontSize={'sm'}>
<Thead>
<Tr>
<Th></Th>
@@ -96,6 +97,7 @@ const Share = ({ appId }: { appId: string }) => {
<Th>()</Th>
<Th>IP限流/</Th>
<Th></Th>
<Th>token校验</Th>
</>
)}
<Th>使</Th>
@@ -113,12 +115,13 @@ const Share = ({ appId }: { appId: string }) => {
<Td>
{item.limit && item.limit.credit > -1 ? `${item.limit.credit}` : '无限制'}
</Td>
<Td>{item.limit?.QPM || '-'}</Td>
<Td>{item?.limit?.QPM || '-'}</Td>
<Td>
{item.limit?.expiredTime
{item?.limit?.expiredTime
? dayjs(item.limit?.expiredTime).format('YYYY/MM/DD\nHH:mm')
: '-'}
</Td>
<Th>{item?.limit?.hookUrl ? '✔' : '✖'}</Th>
</>
)}
<Td>{item.lastTime ? formatTimeToChatTime(item.lastTime) : '未使用'}</Td>
@@ -267,7 +270,6 @@ function EditLinkModal({
});
const { mutate: onclickUpdate, isLoading: updating } = useRequest({
mutationFn: (e: OutLinkEditType) => {
console.log(e);
return putShareChat(e);
},
errorToast: '更新链接异常',
@@ -338,6 +340,26 @@ function EditLinkModal({
}}
/>
</Flex>
<Flex alignItems={'center'} mt={4}>
<Flex flex={'0 0 90px'}>
{t('outlink.token auth')}
<MyTooltip label={t('outlink.token auth Tips') || ''}>
<QuestionOutlineIcon ml={1} />
</MyTooltip>
</Flex>
<Input
placeholder={t('outlink.token auth Tips') || ''}
{...register('limit.hookUrl')}
/>
</Flex>
<Link
href="https://doc.fastgpt.run/docs/development/openapi/#分享链接中增加额外-query"
target={'_blank'}
fontSize={'sm'}
color={'myGray.500'}
>
{t('outlink.token auth use cases')}
</Link>
</>
)}

View File

@@ -21,11 +21,20 @@ import ChatHeader from './components/ChatHeader';
import ChatHistorySlider from './components/ChatHistorySlider';
import { serviceSideProps } from '@/utils/web/i18n';
const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
const OutLink = ({
shareId,
chatId,
authToken
}: {
shareId: string;
chatId: string;
authToken?: string;
}) => {
const router = useRouter();
const { toast } = useToast();
const { isOpen: isOpenSlider, onClose: onCloseSlider, onOpen: onOpenSlider } = useDisclosure();
const { isPc } = useGlobalStore();
const forbidRefresh = useRef(false);
const ChatBoxRef = useRef<ComponentRef>(null);
@@ -53,7 +62,8 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
messages: prompts,
variables,
shareId,
chatId: completionChatId
chatId: completionChatId,
authToken
},
onMessage: generatingMessage,
abortSignal: controller
@@ -75,10 +85,12 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
});
if (completionChatId !== chatId && controller.signal.reason !== 'leave') {
forbidRefresh.current = true;
router.replace({
query: {
shareId,
chatId: completionChatId
chatId: completionChatId,
authToken
}
});
}
@@ -96,11 +108,11 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
return { responseText, responseData };
},
[chatId, router, saveChatResponse, shareId]
[authToken, chatId, router, saveChatResponse, shareId]
);
const loadAppInfo = useCallback(
async (shareId: string, chatId: string) => {
async (shareId: string, chatId: string, authToken?: string) => {
if (!shareId) return null;
const history = shareChatHistory.find((item) => item.chatId === chatId) || defaultHistory;
@@ -111,7 +123,8 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
const chatData = await (async () => {
if (shareChatData.app.name === '') {
return initShareChatInfo({
shareId
shareId,
authToken
});
}
return shareChatData;
@@ -142,8 +155,12 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
[delManyShareChatHistoryByShareId, setShareChatData, shareChatData, shareChatHistory, toast]
);
useQuery(['init', shareId, chatId], () => {
return loadAppInfo(shareId, chatId);
useQuery(['init', shareId, chatId, authToken], () => {
if (forbidRefresh.current) {
forbidRefresh.current = false;
return null;
}
return loadAppInfo(shareId, chatId, authToken);
});
return (
@@ -185,7 +202,8 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
router.replace({
query: {
chatId: chatId || '',
shareId
shareId,
authToken
}
});
if (!isPc) {
@@ -197,7 +215,8 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
delManyShareChatHistoryByShareId(shareId);
router.replace({
query: {
shareId
shareId,
authToken
}
});
}}
@@ -222,6 +241,7 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
{/* chat box */}
<Box flex={1}>
<ChatBox
active={!!shareChatData.app.name}
ref={ChatBoxRef}
appAvatar={shareChatData.app.avatar}
userAvatar={shareChatData.userAvatar}
@@ -252,9 +272,10 @@ const OutLink = ({ shareId, chatId }: { shareId: string; chatId: string }) => {
export async function getServerSideProps(context: any) {
const shareId = context?.query?.shareId || '';
const chatId = context?.query?.chatId || '';
const authToken = context?.query?.authToken || '';
return {
props: { shareId, chatId, ...(await serviceSideProps(context)) }
props: { shareId, chatId, authToken, ...(await serviceSideProps(context)) }
};
}

View File

@@ -3,8 +3,18 @@ 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';
export async function authOutLinkChat({ shareId, ip }: { shareId: string; ip?: string | null }) {
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
@@ -18,7 +28,7 @@ export async function authOutLinkChat({ shareId, ip }: { shareId: string; ip?: s
const [user] = await Promise.all([
authBalanceByUid(uid), // authBalance
...(global.feConfigs?.isPlus ? [authOutLinkLimit({ outLink, ip })] : []) // limit auth
...(global.feConfigs?.isPlus ? [authOutLinkLimit({ outLink, ip, authToken, question })] : []) // limit auth
]);
return {
@@ -32,10 +42,11 @@ export async function authOutLinkChat({ shareId, ip }: { shareId: string; ip?: s
export async function authOutLinkLimit({
outLink,
ip
}: {
ip,
authToken,
question
}: AuthLinkProps & {
outLink: OutLinkSchema;
ip?: string | null;
}) {
if (!ip || !outLink.limit) {
return;
@@ -49,30 +60,97 @@ export async function authOutLinkLimit({
return Promise.reject('链接超出使用限制');
}
const ipLimit = await IpLimit.findOne({ ip, eventId: outLink._id });
try {
if (!ipLimit) {
await IpLimit.create({
eventId: outLink._id,
ip,
account: outLink.limit.QPM - 1
});
// ip limit
await (async () => {
if (!outLink.limit) {
return;
}
// 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();
}
if (ipLimit.account <= 0) {
return Promise.reject(
`每分钟仅能请求 ${outLink.limit.QPM} 次, ${60 - Math.round(diffTime / 1000)}s 后重试~`
);
}
ipLimit.account = ipLimit.account - 1;
await ipLimit.save();
} catch (error) {}
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 });
}
type TokenAuthResponseType = {
success: boolean;
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 || '身份校验失败');
}
} 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 || '身份校验失败');
}
} catch (error) {
return Promise.reject('身份校验失败');
}
};

View File

@@ -1,4 +1,6 @@
import { addLog } from '@/service/utils/tools';
import { ChatHistoryItemResType } from '@/types/chat';
import axios from 'axios';
import { OutLink } from './schema';
export const updateOutLinkUsage = async ({
@@ -20,3 +22,31 @@ export const updateOutLinkUsage = async ({
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

@@ -48,6 +48,9 @@ const OutLinkSchema = new Schema({
credit: {
type: Number,
default: -1
},
hookUrl: {
type: String
}
}
});

View File

@@ -12,18 +12,6 @@ export enum AuthUserTypeEnum {
apikey = 'apikey'
}
export const authCookieToken = async (cookie?: string, token?: string): Promise<string> => {
// 获取 cookie
const cookies = Cookie.parse(cookie || '');
const cookieToken = cookies.token || token;
if (!cookieToken) {
return Promise.reject(ERROR_ENUM.unAuthorization);
}
return await authJWT(cookieToken);
};
/* auth balance */
export const authBalanceByUid = async (uid: string) => {
const user = await User.findById<UserModelSchema>(
@@ -45,13 +33,27 @@ export const authUser = async ({
req,
authToken = false,
authRoot = false,
authApiKey = false,
authBalance = false
}: {
req: NextApiRequest;
authToken?: boolean;
authRoot?: boolean;
authApiKey?: boolean;
authBalance?: boolean;
}) => {
const authCookieToken = async (cookie?: string, token?: string): Promise<string> => {
// 获取 cookie
const cookies = Cookie.parse(cookie || '');
const cookieToken = cookies.token || token;
if (!cookieToken) {
return Promise.reject(ERROR_ENUM.unAuthorization);
}
return await authJWT(cookieToken);
};
// from authorization get apikey
const parseAuthorization = async (authorization?: string) => {
if (!authorization) {
return Promise.reject(ERROR_ENUM.unAuthorization);
@@ -89,6 +91,7 @@ export const authUser = async ({
appId: apiKeyAppId || authorizationAppid
};
};
// root user
const parseRootKey = async (rootKey?: string, userId = '') => {
if (!rootKey || !process.env.ROOT_KEY || rootKey !== process.env.ROOT_KEY) {
return Promise.reject(ERROR_ENUM.unAuthorization);
@@ -110,30 +113,31 @@ export const authUser = async ({
let openApiKey = apikey;
let authType: `${AuthUserTypeEnum}` = AuthUserTypeEnum.token;
if (authToken) {
if (authToken && (cookie || token)) {
// user token(from fastgpt web)
uid = await authCookieToken(cookie, token);
authType = AuthUserTypeEnum.token;
} else if (authRoot) {
} else if (authRoot && rootkey) {
// root user
uid = await parseRootKey(rootkey, userid);
authType = AuthUserTypeEnum.root;
} else if (cookie || token) {
uid = await authCookieToken(cookie, token);
authType = AuthUserTypeEnum.token;
} else if (apikey) {
} else if (authApiKey && apikey) {
// apikey
const parseResult = await authOpenApiKey({ apikey });
uid = parseResult.userId;
authType = AuthUserTypeEnum.apikey;
openApiKey = parseResult.apikey;
} else if (authorization) {
} else if (authApiKey && authorization) {
// apikey from authorization
const authResponse = await parseAuthorization(authorization);
uid = authResponse.uid;
appId = authResponse.appId;
openApiKey = authResponse.apikey;
authType = AuthUserTypeEnum.apikey;
} else if (rootkey) {
uid = await parseRootKey(rootkey, userid);
authType = AuthUserTypeEnum.root;
} else {
}
// not rootUser and no uid, reject request
if (!rootkey && !uid) {
return Promise.reject(ERROR_ENUM.unAuthorization);
}
@@ -158,14 +162,12 @@ export const authApp = async ({
appId,
userId,
authUser = true,
authOwner = true,
reserveDetail = false
authOwner = true
}: {
appId: string;
userId: string;
authUser?: boolean;
authOwner?: boolean;
reserveDetail?: boolean; // focus reserve detail
}) => {
// 获取 app 数据
const app = await App.findById<AppSchema>(appId);

View File

@@ -4,6 +4,7 @@ export type DatasetDataItemType = {
source?: string;
file_id?: string;
};
export type PgDataItemType = DatasetItemType & {
id: string;
};

View File

@@ -14,6 +14,7 @@ export interface OutLinkSchema {
expiredTime?: Date;
QPM: number;
credit: number;
hookUrl?: string;
};
}