refactor openapikey and outlink apis (#2134)

* refactor: OpenAPIKey refactor

* refactor: outlink api refactor

fix: list return wrong data

* chore: remove deprecated type definition

* chore: remove throw Error. instead of Promise.reject

* fix: auth openapikey's owner

* fix: manager could read all keys
This commit is contained in:
Finley Ge
2024-07-24 11:11:36 +08:00
committed by GitHub
parent a233ab9584
commit a478621730
12 changed files with 300 additions and 244 deletions

View File

@@ -0,0 +1,88 @@
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { MongoDataset } from '@fastgpt/service/core/dataset/schema';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import {
PerResourceTypeEnum,
WritePermissionVal
} from '@fastgpt/global/support/permission/constant';
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
import { FolderImgUrl } from '@fastgpt/global/common/file/image/constants';
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
import { DatasetDefaultPermissionVal } from '@fastgpt/global/support/permission/dataset/constant';
import { getResourceAllClbs } from '@fastgpt/service/support/permission/controller';
import { syncCollaborators } from '@fastgpt/service/support/permission/inheritPermission';
export type DatasetFolderCreateQuery = {};
export type DatasetFolderCreateBody = {
parentId?: string;
name: string;
intro: string;
};
export type DatasetFolderCreateResponse = {};
async function handler(
req: ApiRequestProps<DatasetFolderCreateBody, DatasetFolderCreateQuery>,
_res: ApiResponseType<any>
): Promise<DatasetFolderCreateResponse> {
const { parentId, name, intro } = req.body;
if (!name) {
return Promise.reject(CommonErrEnum.missingParams);
}
const { tmbId, teamId } = await authUserPer({
req,
per: WritePermissionVal,
authToken: true
});
const parentFolder = await (async () => {
if (parentId) {
return (
await authDataset({
datasetId: parentId,
per: WritePermissionVal,
req,
authToken: true
})
).dataset;
}
})();
await mongoSessionRun(async (session) => {
const app = await MongoDataset.create({
...parseParentIdInMongo(parentId),
avatar: FolderImgUrl,
name,
intro,
teamId,
tmbId,
type: DatasetTypeEnum.folder,
defaultPermission: !!parentFolder
? parentFolder.defaultPermission
: DatasetDefaultPermissionVal
});
if (parentId) {
const parentClbs = await getResourceAllClbs({
teamId,
resourceId: parentId,
resourceType: PerResourceTypeEnum.dataset,
session
});
await syncCollaborators({
resourceType: PerResourceTypeEnum.dataset,
teamId,
resourceId: app._id,
collaborators: parentClbs,
session
});
}
});
return {};
}
export default NextAPI(handler);

View File

@@ -1,44 +1,56 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { MongoOpenApi } from '@fastgpt/service/support/openapi/schema';
import { customAlphabet } from 'nanoid';
import type { EditApiKeyProps } from '@/global/support/openapi/api';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
import { getNanoid } from '@fastgpt/global/common/string/tools';
import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import {
ManagePermissionVal,
WritePermissionVal
} from '@fastgpt/global/support/permission/constant';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { OpenApiErrEnum } from '@fastgpt/global/common/error/code/openapi';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { appId, name, limit } = req.body as EditApiKeyProps;
const { teamId, tmbId } = await authUserPer({ req, authToken: true, per: WritePermissionVal });
const count = await MongoOpenApi.find({ tmbId, appId }).countDocuments();
if (count >= 10) {
throw new Error('最多 10 组 API 秘钥');
async function handler(req: ApiRequestProps<EditApiKeyProps>): Promise<string> {
const { appId, name, limit } = req.body;
const { tmbId, teamId } = await (async () => {
if (!appId) {
// global apikey is being created, auth the tmb
const { teamId, tmbId } = await authUserPer({
req,
authToken: true,
per: WritePermissionVal
});
return { teamId, tmbId };
} else {
const { teamId, tmbId } = await authApp({
req,
per: ManagePermissionVal,
appId,
authToken: true
});
return { teamId, tmbId };
}
})();
const nanoid = getNanoid(Math.floor(Math.random() * 14) + 52);
const apiKey = `${global.systemEnv?.openapiPrefix || 'fastgpt'}-${nanoid}`;
const count = await MongoOpenApi.find({ tmbId, appId }).countDocuments();
await MongoOpenApi.create({
teamId,
tmbId,
apiKey,
appId,
name,
limit
});
jsonRes(res, {
data: apiKey
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
if (count >= 10) {
return Promise.reject(OpenApiErrEnum.exceedLimit);
}
const nanoid = getNanoid(Math.floor(Math.random() * 14) + 52);
const apiKey = `${global.systemEnv?.openapiPrefix || 'fastgpt'}-${nanoid}`;
await MongoOpenApi.create({
teamId,
tmbId,
apiKey,
appId,
name,
limit
});
return apiKey;
}
export default NextAPI(handler);

View File

@@ -4,6 +4,7 @@ import { connectToDatabase } from '@/service/mongo';
import { MongoOpenApi } from '@fastgpt/service/support/openapi/schema';
import { authOpenApiKeyCrud } from '@fastgpt/service/support/permission/auth/openapi';
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
@@ -11,7 +12,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const { id } = req.query as { id: string };
if (!id) {
throw new Error('缺少参数');
return Promise.reject(CommonErrEnum.missingParams);
}
await authOpenApiKeyCrud({ req, authToken: true, id, per: OwnerPermissionVal });

View File

@@ -1,53 +1,42 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { MongoOpenApi } from '@fastgpt/service/support/openapi/schema';
import type { GetApiKeyProps } from '@/global/support/openapi/api';
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { appId } = req.query as GetApiKeyProps;
async function handler(req: ApiRequestProps<any, GetApiKeyProps>) {
const { appId } = req.query;
if (appId) {
await authApp({
req,
authToken: true,
appId,
per: ManagePermissionVal
});
const findResponse = await MongoOpenApi.find({
appId
}).sort({ _id: -1 });
return jsonRes(res, {
data: findResponse.map((item) => item.toObject())
});
}
const { teamId, tmbId, permission } = await authUserPer({
if (appId) {
// app-level apikey
await authApp({
req,
authToken: true,
appId,
per: ManagePermissionVal
});
const findResponse = await MongoOpenApi.find({
appId,
teamId,
...(!permission.isOwner && { tmbId })
appId
}).sort({ _id: -1 });
return jsonRes(res, {
data: findResponse.map((item) => item.toObject())
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
return findResponse.map((item) => item.toObject());
}
// global apikey
const { teamId, tmbId, permission } = await authUserPer({
req,
authToken: true
});
const findResponse = await MongoOpenApi.find({
appId,
teamId,
...(!permission.hasManagePer && { tmbId }) // if not manager, read own key
}).sort({ _id: -1 });
return findResponse.map((item) => item.toObject());
}
export default NextAPI(handler);

View File

@@ -1,28 +1,19 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { MongoOpenApi } from '@fastgpt/service/support/openapi/schema';
import type { EditApiKeyProps } from '@/global/support/openapi/api.d';
import { authOpenApiKeyCrud } from '@fastgpt/service/support/permission/auth/openapi';
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { _id, name, limit } = req.body as EditApiKeyProps & { _id: string };
async function handler(req: ApiRequestProps<EditApiKeyProps & { _id: string }>): Promise<void> {
const { _id, name, limit } = req.body;
await authOpenApiKeyCrud({ req, authToken: true, id: _id, per: OwnerPermissionVal });
await authOpenApiKeyCrud({ req, authToken: true, id: _id, per: OwnerPermissionVal });
await MongoOpenApi.findByIdAndUpdate(_id, {
...(name && { name }),
...(limit && { limit })
});
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
await MongoOpenApi.findByIdAndUpdate(_id, {
...(name && { name }),
...(limit && { limit })
});
}
export default NextAPI(handler);

View File

@@ -1,47 +1,45 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import type { OutLinkEditType } from '@fastgpt/global/support/outLink/type.d';
import { customAlphabet } from 'nanoid';
import { PublishChannelEnum } from '@fastgpt/global/support/outLink/constant';
import { WritePermissionVal } from '@fastgpt/global/support/permission/constant';
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
/* create a shareChat */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { appId, ...props } = req.body as OutLinkEditType &
OutLinkEditType & {
appId: string;
type: PublishChannelEnum;
};
const nanoid = customAlphabet('abcdefghijklmnopqrstuvwxyz1234567890', 24);
const { teamId, tmbId } = await authApp({
req,
authToken: true,
appId,
per: WritePermissionVal
});
export type OutLinkCreateQuery = {};
export type OutLinkCreateBody = OutLinkEditType &
OutLinkEditType & {
appId: string;
type: PublishChannelEnum;
};
export type OutLinkCreateResponse = string;
const shareId = nanoid();
await MongoOutLink.create({
shareId,
teamId,
tmbId,
appId,
...props
});
async function handler(
req: ApiRequestProps<OutLinkCreateBody, OutLinkCreateQuery>
): Promise<OutLinkCreateResponse> {
const { appId, ...props } = req.body;
jsonRes(res, {
data: shareId
});
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
const { teamId, tmbId } = await authApp({
req,
authToken: true,
appId,
per: ManagePermissionVal
});
const shareId = nanoid();
await MongoOutLink.create({
shareId,
teamId,
tmbId,
appId,
...props
});
return shareId;
}
export default NextAPI(handler);

View File

@@ -1,28 +1,23 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
import { authOutLinkCrud } from '@fastgpt/service/support/permission/publish/authLink';
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
export type OutLinkDeleteQuery = {
id: string;
};
export type OutLinkDeleteBody = {};
export type OutLinkDeleteResponse = {};
/* delete a shareChat by shareChatId */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
const { id } = req.query as {
id: string;
};
await authOutLinkCrud({ req, outLinkId: id, authToken: true, per: ManagePermissionVal });
await MongoOutLink.findByIdAndRemove(id);
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
async function handler(
req: ApiRequestProps<OutLinkDeleteBody, OutLinkDeleteQuery>
): Promise<OutLinkDeleteResponse> {
const { id } = req.query;
await authOutLinkCrud({ req, outLinkId: id, authToken: true, per: OwnerPermissionVal });
await MongoOutLink.findByIdAndRemove(id);
return {};
}
export default NextAPI(handler);

View File

@@ -1,39 +1,34 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
import { authApp } from '@fastgpt/service/support/permission/app/auth';
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
export type OutLinkListQuery = {
appId: string;
type: string;
};
export type OutLinkListBody = {};
export type OutLinkListResponse = OutLinkSchema[];
/* get shareChat list by appId */
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
async function handler(
req: ApiRequestProps<OutLinkListBody, OutLinkListQuery>
): Promise<OutLinkListResponse> {
const { appId, type } = req.query;
await authApp({
req,
authToken: true,
appId,
per: ManagePermissionVal
});
const { appId, type } = req.query as {
appId: string;
type: string;
};
const data = await MongoOutLink.find({
appId,
type: type
}).sort({
_id: -1
});
await authApp({
req,
authToken: true,
appId,
per: ManagePermissionVal
});
const data = await MongoOutLink.find({
appId,
type: type
}).sort({
_id: -1
});
jsonRes(res, { data });
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
}
return data;
}
export default NextAPI(handler);

View File

@@ -1,34 +1,31 @@
import type { NextApiRequest, NextApiResponse } from 'next';
import { jsonRes } from '@fastgpt/service/common/response';
import { connectToDatabase } from '@/service/mongo';
import { MongoOutLink } from '@fastgpt/service/support/outLink/schema';
import type { OutLinkEditType } from '@fastgpt/global/support/outLink/type.d';
import { authOutLinkCrud } from '@fastgpt/service/support/permission/publish/authLink';
import { ManagePermissionVal } from '@fastgpt/global/support/permission/constant';
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
import type { ApiRequestProps } from '@fastgpt/service/type/next';
import { NextAPI } from '@/service/middleware/entry';
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
await connectToDatabase();
export type OutLinkUpdateQuery = {};
export type OutLinkUpdateBody = OutLinkEditType & {};
export type OutLinkUpdateResponse = {};
const { _id, name, responseDetail, limit } = req.body as OutLinkEditType & {};
async function handler(
req: ApiRequestProps<OutLinkUpdateBody, OutLinkUpdateQuery>
): Promise<OutLinkUpdateResponse> {
const { _id, name, responseDetail, limit } = req.body;
if (!_id) {
throw new Error('_id is required');
}
await authOutLinkCrud({ req, outLinkId: _id, authToken: true, per: ManagePermissionVal });
await MongoOutLink.findByIdAndUpdate(_id, {
name,
responseDetail,
limit
});
jsonRes(res);
} catch (err) {
jsonRes(res, {
code: 500,
error: err
});
if (!_id) {
return Promise.reject(CommonErrEnum.missingParams);
}
await authOutLinkCrud({ req, outLinkId: _id, authToken: true, per: OwnerPermissionVal });
await MongoOutLink.findByIdAndUpdate(_id, {
name,
responseDetail,
limit
});
return {};
}
export default NextAPI(handler);