v4.6 -1 (#459)
This commit is contained in:
@@ -10,13 +10,11 @@ export async function authOpenApiKey({ apikey }: { apikey: string }) {
|
||||
if (!apikey) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthApiKey);
|
||||
}
|
||||
|
||||
try {
|
||||
const openApi = await MongoOpenApi.findOne({ apiKey: apikey });
|
||||
if (!openApi) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthApiKey);
|
||||
}
|
||||
const userId = String(openApi.userId);
|
||||
|
||||
// auth limit
|
||||
if (global.feConfigs?.isPlus) {
|
||||
@@ -25,7 +23,13 @@ export async function authOpenApiKey({ apikey }: { apikey: string }) {
|
||||
|
||||
updateApiKeyUsedTime(openApi._id);
|
||||
|
||||
return { apikey, userId, appId: openApi.appId };
|
||||
return {
|
||||
apikey,
|
||||
userId: String(openApi.userId),
|
||||
teamId: String(openApi.teamId),
|
||||
tmbId: String(openApi.tmbId),
|
||||
appId: openApi.appId || ''
|
||||
};
|
||||
} catch (error) {
|
||||
return Promise.reject(error);
|
||||
}
|
||||
|
||||
@@ -1,14 +1,27 @@
|
||||
import { connectionMongo, type Model } from '../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import type { OpenApiSchema } from '@fastgpt/global/support/openapi/type';
|
||||
import { PRICE_SCALE } from '@fastgpt/global/common/bill/constants';
|
||||
import { formatPrice } from '@fastgpt/global/common/bill/tools';
|
||||
import { PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import { formatPrice } from '@fastgpt/global/support/wallet/bill/tools';
|
||||
import {
|
||||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
|
||||
const OpenApiSchema = new Schema(
|
||||
{
|
||||
userId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'user',
|
||||
ref: 'user'
|
||||
},
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamCollectionName,
|
||||
required: true
|
||||
},
|
||||
tmbId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamMemberCollectionName,
|
||||
required: true
|
||||
},
|
||||
apiKey: {
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
import { AuthUserTypeEnum, authBalanceByUid } from '../user/auth';
|
||||
import { MongoOutLink } from './schema';
|
||||
import { POST } from '../../common/api/plusRequest';
|
||||
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
||||
|
||||
export type AuthLinkProps = { ip?: string | null; authToken?: string; question: string };
|
||||
export type AuthLinkLimitProps = AuthLinkProps & { outLink: OutLinkSchema };
|
||||
|
||||
export async function authOutLinkChat({
|
||||
shareId,
|
||||
ip,
|
||||
authToken,
|
||||
question
|
||||
}: AuthLinkProps & {
|
||||
shareId: string;
|
||||
}) {
|
||||
// get outLink
|
||||
const outLink = await MongoOutLink.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 function authOutLinkLimit(data: AuthLinkLimitProps) {
|
||||
return POST('/support/outLink/authLimit', data);
|
||||
}
|
||||
|
||||
export async function authOutLinkId({ id }: { id: string }) {
|
||||
const outLink = await MongoOutLink.findOne({
|
||||
shareId: id
|
||||
});
|
||||
|
||||
if (!outLink) {
|
||||
return Promise.reject('分享链接无效');
|
||||
}
|
||||
|
||||
return {
|
||||
userId: String(outLink.userId)
|
||||
};
|
||||
}
|
||||
|
||||
export type AuthShareChatInitProps = {
|
||||
authToken?: string;
|
||||
tokenUrl?: string;
|
||||
};
|
||||
|
||||
export function authShareChatInit(data: AuthShareChatInitProps) {
|
||||
if (!global.feConfigs?.isPlus) return;
|
||||
return POST('/support/outLink/authShareChatInit', data);
|
||||
}
|
||||
@@ -2,6 +2,10 @@ import { connectionMongo, type Model } from '../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { OutLinkSchema as SchemaType } from '@fastgpt/global/support/outLink/type';
|
||||
import { OutLinkTypeEnum } from '@fastgpt/global/support/outLink/constant';
|
||||
import {
|
||||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
|
||||
const OutLinkSchema = new Schema({
|
||||
shareId: {
|
||||
@@ -10,7 +14,16 @@ const OutLinkSchema = new Schema({
|
||||
},
|
||||
userId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'user',
|
||||
ref: 'user'
|
||||
},
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamCollectionName,
|
||||
required: true
|
||||
},
|
||||
tmbId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamMemberCollectionName,
|
||||
required: true
|
||||
},
|
||||
appId: {
|
||||
|
||||
72
packages/service/support/permission/auth/app.ts
Normal file
72
packages/service/support/permission/auth/app.ts
Normal file
@@ -0,0 +1,72 @@
|
||||
import { MongoApp } from '../../../core/app/schema';
|
||||
import { AppDetailType } from '@fastgpt/global/core/app/type.d';
|
||||
import { AuthModeType } from '../type';
|
||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||
import { getTeamInfoByTmbId } from '../../user/team/controller';
|
||||
|
||||
// 模型使用权校验
|
||||
export async function authApp({
|
||||
appId,
|
||||
per = 'owner',
|
||||
...props
|
||||
}: AuthModeType & {
|
||||
appId: string;
|
||||
}): Promise<
|
||||
AuthResponseType & {
|
||||
teamOwner: boolean;
|
||||
app: AppDetailType;
|
||||
}
|
||||
> {
|
||||
const result = await parseHeaderCert(props);
|
||||
const { userId, teamId, tmbId } = result;
|
||||
const { role } = await getTeamInfoByTmbId({ tmbId });
|
||||
|
||||
const { app, isOwner, canWrite } = await (async () => {
|
||||
// get app
|
||||
const app = (await MongoApp.findOne({ _id: appId, teamId }))?.toJSON();
|
||||
if (!app) {
|
||||
return Promise.reject(AppErrEnum.unAuthApp);
|
||||
}
|
||||
|
||||
const isOwner =
|
||||
role !== TeamMemberRoleEnum.visitor &&
|
||||
(String(app.tmbId) === tmbId || role === TeamMemberRoleEnum.owner);
|
||||
const canWrite =
|
||||
isOwner ||
|
||||
(app.permission === PermissionTypeEnum.public && role !== TeamMemberRoleEnum.visitor);
|
||||
|
||||
if (per === 'r') {
|
||||
if (!isOwner && app.permission !== PermissionTypeEnum.public) {
|
||||
return Promise.reject(AppErrEnum.unAuthApp);
|
||||
}
|
||||
}
|
||||
if (per === 'w' && !canWrite) {
|
||||
return Promise.reject(AppErrEnum.unAuthApp);
|
||||
}
|
||||
if (per === 'owner' && !isOwner) {
|
||||
return Promise.reject(AppErrEnum.unAuthApp);
|
||||
}
|
||||
|
||||
return {
|
||||
app: {
|
||||
...app,
|
||||
isOwner,
|
||||
canWrite
|
||||
},
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
})();
|
||||
|
||||
return {
|
||||
...result,
|
||||
app,
|
||||
isOwner,
|
||||
canWrite,
|
||||
teamOwner: role === TeamMemberRoleEnum.owner
|
||||
};
|
||||
}
|
||||
67
packages/service/support/permission/auth/chat.ts
Normal file
67
packages/service/support/permission/auth/chat.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
||||
import { AuthModeType } from '../type';
|
||||
import type { ChatSchema, ChatWithAppSchema } from '@fastgpt/global/core/chat/type';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { MongoChat } from '../../../core/chat/chatSchema';
|
||||
import { ChatErrEnum } from '@fastgpt/global/common/error/code/chat';
|
||||
import { getTeamInfoByTmbId } from '../../user/team/controller';
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
|
||||
export async function authChat({
|
||||
chatId,
|
||||
per = 'owner',
|
||||
...props
|
||||
}: AuthModeType & {
|
||||
chatId: string;
|
||||
}): Promise<
|
||||
AuthResponseType & {
|
||||
chat: ChatSchema;
|
||||
}
|
||||
> {
|
||||
const { userId, teamId, tmbId } = await parseHeaderCert(props);
|
||||
const { role } = await getTeamInfoByTmbId({ tmbId });
|
||||
|
||||
const { chat, isOwner, canWrite } = await (async () => {
|
||||
// get chat
|
||||
const chat = (
|
||||
await MongoChat.findOne({ chatId, teamId }).populate('appId')
|
||||
)?.toJSON() as ChatWithAppSchema;
|
||||
|
||||
if (!chat) {
|
||||
return Promise.reject('Chat is not exists');
|
||||
}
|
||||
|
||||
const isOwner = role === TeamMemberRoleEnum.owner || String(chat.tmbId) === tmbId;
|
||||
const canWrite = isOwner;
|
||||
|
||||
if (
|
||||
per === 'r' &&
|
||||
role !== TeamMemberRoleEnum.owner &&
|
||||
chat.appId.permission !== PermissionTypeEnum.public
|
||||
) {
|
||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||
}
|
||||
if (per === 'w' && !canWrite) {
|
||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||
}
|
||||
if (per === 'owner' && !isOwner) {
|
||||
return Promise.reject(ChatErrEnum.unAuthChat);
|
||||
}
|
||||
|
||||
return {
|
||||
chat,
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
})();
|
||||
|
||||
return {
|
||||
userId,
|
||||
teamId,
|
||||
tmbId,
|
||||
chat,
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
}
|
||||
12
packages/service/support/permission/auth/common.ts
Normal file
12
packages/service/support/permission/auth/common.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { AuthModeType } from '../type';
|
||||
|
||||
export const authCert = async (props: AuthModeType) => {
|
||||
const result = await parseHeaderCert(props);
|
||||
|
||||
return {
|
||||
...result,
|
||||
isOwner: true,
|
||||
canWrite: true
|
||||
};
|
||||
};
|
||||
181
packages/service/support/permission/auth/dataset.ts
Normal file
181
packages/service/support/permission/auth/dataset.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
import { AuthModeType } from '../type';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
|
||||
import { MongoDataset } from '../../../core/dataset/schema';
|
||||
import { getCollectionWithDataset } from '../../../core/dataset/controller';
|
||||
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
||||
import {
|
||||
CollectionWithDatasetType,
|
||||
DatasetFileSchema,
|
||||
DatasetSchemaType
|
||||
} from '@fastgpt/global/core/dataset/type';
|
||||
import { getFileById } from '../../../common/file/gridfs/controller';
|
||||
import { BucketNameEnum } from '@fastgpt/global/common/file/constants';
|
||||
import { getTeamInfoByTmbId } from '../../user/team/controller';
|
||||
|
||||
export async function authDataset({
|
||||
datasetId,
|
||||
per = 'owner',
|
||||
...props
|
||||
}: AuthModeType & {
|
||||
datasetId: string;
|
||||
}): Promise<
|
||||
AuthResponseType & {
|
||||
dataset: DatasetSchemaType;
|
||||
}
|
||||
> {
|
||||
const result = await parseHeaderCert(props);
|
||||
const { userId, teamId, tmbId } = result;
|
||||
const { role } = await getTeamInfoByTmbId({ tmbId });
|
||||
|
||||
const { dataset, isOwner, canWrite } = await (async () => {
|
||||
const dataset = (await MongoDataset.findOne({ _id: datasetId, teamId }))?.toJSON();
|
||||
|
||||
if (!dataset) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDataset);
|
||||
}
|
||||
|
||||
const isOwner =
|
||||
role !== TeamMemberRoleEnum.visitor &&
|
||||
(String(dataset.tmbId) === tmbId || role === TeamMemberRoleEnum.owner);
|
||||
const canWrite =
|
||||
isOwner ||
|
||||
(role !== TeamMemberRoleEnum.visitor && dataset.permission === PermissionTypeEnum.public);
|
||||
if (per === 'r') {
|
||||
if (!isOwner && dataset.permission !== PermissionTypeEnum.public) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDataset);
|
||||
}
|
||||
}
|
||||
if (per === 'w' && !canWrite) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDataset);
|
||||
}
|
||||
if (per === 'owner' && !isOwner) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDataset);
|
||||
}
|
||||
|
||||
return { dataset, isOwner, canWrite };
|
||||
})();
|
||||
|
||||
return {
|
||||
...result,
|
||||
dataset,
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
Read: in team and dataset permission is public
|
||||
Write: in team, not visitor and dataset permission is public
|
||||
*/
|
||||
export async function authDatasetCollection({
|
||||
collectionId,
|
||||
per = 'owner',
|
||||
...props
|
||||
}: AuthModeType & {
|
||||
collectionId: string;
|
||||
}): Promise<
|
||||
AuthResponseType & {
|
||||
collection: CollectionWithDatasetType;
|
||||
}
|
||||
> {
|
||||
const { userId, teamId, tmbId } = await parseHeaderCert(props);
|
||||
const { role } = await getTeamInfoByTmbId({ tmbId });
|
||||
|
||||
const { collection, isOwner, canWrite } = await (async () => {
|
||||
const collection = await getCollectionWithDataset(collectionId);
|
||||
|
||||
if (!collection || String(collection.teamId) !== teamId) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDatasetCollection);
|
||||
}
|
||||
|
||||
const isOwner =
|
||||
String(collection.datasetId.tmbId) === tmbId || role === TeamMemberRoleEnum.owner;
|
||||
const canWrite =
|
||||
isOwner ||
|
||||
(role !== TeamMemberRoleEnum.visitor &&
|
||||
collection.datasetId.permission === PermissionTypeEnum.public);
|
||||
|
||||
if (per === 'r') {
|
||||
if (!isOwner && collection.datasetId.permission !== PermissionTypeEnum.public) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDatasetCollection);
|
||||
}
|
||||
}
|
||||
if (per === 'w' && !canWrite) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDatasetCollection);
|
||||
}
|
||||
if (per === 'owner' && !isOwner) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDatasetCollection);
|
||||
}
|
||||
|
||||
return {
|
||||
collection,
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
})();
|
||||
|
||||
return {
|
||||
userId,
|
||||
teamId,
|
||||
tmbId,
|
||||
collection,
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
}
|
||||
|
||||
export async function authDatasetFile({
|
||||
fileId,
|
||||
per = 'owner',
|
||||
...props
|
||||
}: AuthModeType & {
|
||||
fileId: string;
|
||||
}): Promise<
|
||||
AuthResponseType & {
|
||||
file: DatasetFileSchema;
|
||||
}
|
||||
> {
|
||||
const { userId, teamId, tmbId } = await parseHeaderCert(props);
|
||||
const { role } = await getTeamInfoByTmbId({ tmbId });
|
||||
|
||||
const file = await getFileById({ bucketName: BucketNameEnum.dataset, fileId });
|
||||
|
||||
if (file.metadata.teamId !== teamId) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
|
||||
}
|
||||
|
||||
const { dataset } = await authDataset({
|
||||
...props,
|
||||
datasetId: file.metadata.datasetId,
|
||||
per
|
||||
});
|
||||
const isOwner =
|
||||
role !== TeamMemberRoleEnum.visitor &&
|
||||
(String(dataset.tmbId) === tmbId || role === TeamMemberRoleEnum.owner);
|
||||
|
||||
const canWrite =
|
||||
isOwner ||
|
||||
(role !== TeamMemberRoleEnum.visitor && dataset.permission === PermissionTypeEnum.public);
|
||||
|
||||
if (per === 'r' && !isOwner && dataset.permission !== PermissionTypeEnum.public) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
|
||||
}
|
||||
if (per === 'w' && !canWrite) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
|
||||
}
|
||||
if (per === 'owner' && !isOwner) {
|
||||
return Promise.reject(DatasetErrEnum.unAuthDatasetFile);
|
||||
}
|
||||
|
||||
return {
|
||||
userId,
|
||||
teamId,
|
||||
tmbId,
|
||||
file,
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
}
|
||||
61
packages/service/support/permission/auth/openapi.ts
Normal file
61
packages/service/support/permission/auth/openapi.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
||||
import { AuthModeType } from '../type';
|
||||
import { OpenApiSchema } from '@fastgpt/global/support/openapi/type';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { getTeamInfoByTmbId } from '../../user/team/controller';
|
||||
import { MongoOpenApi } from '../../openapi/schema';
|
||||
import { OpenApiErrEnum } from '@fastgpt/global/common/error/code/openapi';
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
|
||||
export async function authOpenApiKeyCrud({
|
||||
id,
|
||||
per = 'owner',
|
||||
...props
|
||||
}: AuthModeType & {
|
||||
id: string;
|
||||
}): Promise<
|
||||
AuthResponseType & {
|
||||
openapi: OpenApiSchema;
|
||||
}
|
||||
> {
|
||||
const result = await parseHeaderCert(props);
|
||||
const { tmbId, teamId } = result;
|
||||
|
||||
const { role } = await getTeamInfoByTmbId({ tmbId });
|
||||
|
||||
const { openapi, isOwner, canWrite } = await (async () => {
|
||||
const openapi = await MongoOpenApi.findOne({ _id: id, teamId });
|
||||
|
||||
if (!openapi) {
|
||||
throw new Error(OpenApiErrEnum.unExist);
|
||||
}
|
||||
|
||||
const isOwner = String(openapi.tmbId) === tmbId || role === TeamMemberRoleEnum.owner;
|
||||
const canWrite =
|
||||
isOwner || (String(openapi.tmbId) === tmbId && role !== TeamMemberRoleEnum.visitor);
|
||||
|
||||
if (per === 'r' && !canWrite) {
|
||||
return Promise.reject(OpenApiErrEnum.unAuth);
|
||||
}
|
||||
if (per === 'w' && !canWrite) {
|
||||
return Promise.reject(OpenApiErrEnum.unAuth);
|
||||
}
|
||||
if (per === 'owner' && !isOwner) {
|
||||
return Promise.reject(OpenApiErrEnum.unAuth);
|
||||
}
|
||||
|
||||
return {
|
||||
openapi,
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
})();
|
||||
|
||||
return {
|
||||
...result,
|
||||
openapi,
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
}
|
||||
98
packages/service/support/permission/auth/outLink.ts
Normal file
98
packages/service/support/permission/auth/outLink.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { AuthModeType } from '../type';
|
||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
||||
import { AppDetailType } from '@fastgpt/global/core/app/type';
|
||||
import { OutLinkSchema } from '@fastgpt/global/support/outLink/type';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { MongoOutLink } from '../../outLink/schema';
|
||||
import { MongoApp } from '../../../core/app/schema';
|
||||
import { OutLinkErrEnum } from '@fastgpt/global/common/error/code/outLink';
|
||||
import { PermissionTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||
import { getTeamInfoByTmbId } from '../../user/team/controller';
|
||||
|
||||
/* crud outlink permission */
|
||||
export async function authOutLinkCrud({
|
||||
outLinkId,
|
||||
per = 'owner',
|
||||
...props
|
||||
}: AuthModeType & {
|
||||
outLinkId: string;
|
||||
}): Promise<
|
||||
AuthResponseType & {
|
||||
app: AppDetailType;
|
||||
outLink: OutLinkSchema;
|
||||
}
|
||||
> {
|
||||
const result = await parseHeaderCert(props);
|
||||
const { tmbId, teamId } = result;
|
||||
|
||||
const { role } = await getTeamInfoByTmbId({ tmbId });
|
||||
|
||||
const { app, outLink, isOwner, canWrite } = await (async () => {
|
||||
const outLink = await MongoOutLink.findOne({ _id: outLinkId, teamId });
|
||||
|
||||
if (!outLink) {
|
||||
throw new Error(OutLinkErrEnum.unExist);
|
||||
}
|
||||
|
||||
const app = await MongoApp.findById(outLink.appId);
|
||||
|
||||
if (!app) {
|
||||
return Promise.reject(AppErrEnum.unExist);
|
||||
}
|
||||
|
||||
const isOwner = String(outLink.tmbId) === tmbId || role === TeamMemberRoleEnum.owner;
|
||||
const canWrite =
|
||||
isOwner ||
|
||||
(app.permission === PermissionTypeEnum.public && role !== TeamMemberRoleEnum.visitor);
|
||||
|
||||
if (per === 'r' && !isOwner && app.permission !== PermissionTypeEnum.public) {
|
||||
return Promise.reject(OutLinkErrEnum.unAuthLink);
|
||||
}
|
||||
if (per === 'w' && !canWrite) {
|
||||
return Promise.reject(OutLinkErrEnum.unAuthLink);
|
||||
}
|
||||
if (per === 'owner' && !isOwner) {
|
||||
return Promise.reject(OutLinkErrEnum.unAuthLink);
|
||||
}
|
||||
|
||||
return {
|
||||
app: {
|
||||
...app,
|
||||
isOwner: String(app.tmbId) === tmbId,
|
||||
canWrite
|
||||
},
|
||||
outLink,
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
})();
|
||||
|
||||
return {
|
||||
...result,
|
||||
app,
|
||||
outLink,
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
}
|
||||
|
||||
export async function authOutLinkValid({ shareId }: { shareId?: string }) {
|
||||
const shareChat = await MongoOutLink.findOne({ shareId });
|
||||
|
||||
if (!shareChat) {
|
||||
return Promise.reject(OutLinkErrEnum.linkUnInvalid);
|
||||
}
|
||||
|
||||
const app = await MongoApp.findById(shareChat.appId);
|
||||
|
||||
if (!app) {
|
||||
return Promise.reject(AppErrEnum.unExist);
|
||||
}
|
||||
|
||||
return {
|
||||
app,
|
||||
shareChat
|
||||
};
|
||||
}
|
||||
56
packages/service/support/permission/auth/plugin.ts
Normal file
56
packages/service/support/permission/auth/plugin.ts
Normal file
@@ -0,0 +1,56 @@
|
||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
||||
import { AuthModeType } from '../type';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { getTeamInfoByTmbId } from '../../user/team/controller';
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { MongoPlugin } from '../../../core/plugin/schema';
|
||||
import { PluginErrEnum } from '@fastgpt/global/common/error/code/plugin';
|
||||
import { PluginItemSchema } from '@fastgpt/global/core/plugin/type';
|
||||
|
||||
export async function authPluginCrud({
|
||||
id,
|
||||
per = 'owner',
|
||||
...props
|
||||
}: AuthModeType & {
|
||||
id: string;
|
||||
}): Promise<
|
||||
AuthResponseType & {
|
||||
plugin: PluginItemSchema;
|
||||
}
|
||||
> {
|
||||
const result = await parseHeaderCert(props);
|
||||
const { tmbId, teamId } = result;
|
||||
|
||||
const { role } = await getTeamInfoByTmbId({ tmbId });
|
||||
|
||||
const { plugin, isOwner, canWrite } = await (async () => {
|
||||
const plugin = await MongoPlugin.findOne({ _id: id, teamId });
|
||||
|
||||
if (!plugin) {
|
||||
throw new Error(PluginErrEnum.unExist);
|
||||
}
|
||||
|
||||
const isOwner = String(plugin.tmbId) === tmbId || role === TeamMemberRoleEnum.owner;
|
||||
const canWrite = isOwner;
|
||||
|
||||
if (per === 'w' && !canWrite) {
|
||||
return Promise.reject(PluginErrEnum.unAuth);
|
||||
}
|
||||
if (per === 'owner' && !isOwner) {
|
||||
return Promise.reject(PluginErrEnum.unAuth);
|
||||
}
|
||||
|
||||
return {
|
||||
plugin,
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
})();
|
||||
|
||||
return {
|
||||
...result,
|
||||
plugin,
|
||||
isOwner,
|
||||
canWrite
|
||||
};
|
||||
}
|
||||
52
packages/service/support/permission/auth/user.ts
Normal file
52
packages/service/support/permission/auth/user.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { AuthResponseType } from '@fastgpt/global/support/permission/type';
|
||||
import { AuthModeType } from '../type';
|
||||
import { TeamItemType } from '@fastgpt/global/support/user/team/type';
|
||||
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
|
||||
import { parseHeaderCert } from '../controller';
|
||||
import { getTeamInfoByTmbId } from '../../user/team/controller';
|
||||
import { UserErrEnum } from '../../../../global/common/error/code/user';
|
||||
|
||||
export async function authUserNotVisitor(props: AuthModeType): Promise<
|
||||
AuthResponseType & {
|
||||
team: TeamItemType;
|
||||
role: `${TeamMemberRoleEnum}`;
|
||||
}
|
||||
> {
|
||||
const { userId, teamId, tmbId } = await parseHeaderCert(props);
|
||||
const team = await getTeamInfoByTmbId({ tmbId });
|
||||
|
||||
if (team.role === TeamMemberRoleEnum.visitor) {
|
||||
return Promise.reject(UserErrEnum.binVisitor);
|
||||
}
|
||||
|
||||
return {
|
||||
userId,
|
||||
teamId,
|
||||
tmbId,
|
||||
team,
|
||||
role: team.role,
|
||||
isOwner: team.role === TeamMemberRoleEnum.owner, // teamOwner
|
||||
canWrite: true
|
||||
};
|
||||
}
|
||||
|
||||
/* auth user role */
|
||||
export async function authUserRole(props: AuthModeType): Promise<
|
||||
AuthResponseType & {
|
||||
role: `${TeamMemberRoleEnum}`;
|
||||
teamOwner: boolean;
|
||||
}
|
||||
> {
|
||||
const { userId, teamId, tmbId } = await parseHeaderCert(props);
|
||||
const { role: userRole, canWrite } = await getTeamInfoByTmbId({ tmbId });
|
||||
|
||||
return {
|
||||
userId,
|
||||
teamId,
|
||||
tmbId,
|
||||
isOwner: true,
|
||||
role: userRole,
|
||||
teamOwner: userRole === TeamMemberRoleEnum.owner,
|
||||
canWrite
|
||||
};
|
||||
}
|
||||
241
packages/service/support/permission/controller.ts
Normal file
241
packages/service/support/permission/controller.ts
Normal file
@@ -0,0 +1,241 @@
|
||||
import Cookie from 'cookie';
|
||||
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { NextApiResponse } from 'next';
|
||||
import type { AuthModeType, ReqHeaderAuthType } from './type.d';
|
||||
import { AuthUserTypeEnum } from '@fastgpt/global/support/permission/constant';
|
||||
import { authOpenApiKey } from '../openapi/auth';
|
||||
import { FileTokenQuery } from '@fastgpt/global/common/file/type';
|
||||
|
||||
/* create token */
|
||||
export function createJWT(user: { _id?: string; team?: { teamId?: string; tmbId: string } }) {
|
||||
const key = process.env.TOKEN_KEY as string;
|
||||
const token = jwt.sign(
|
||||
{
|
||||
userId: String(user._id),
|
||||
teamId: String(user.team?.teamId),
|
||||
tmbId: String(user.team?.tmbId),
|
||||
exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 7
|
||||
},
|
||||
key
|
||||
);
|
||||
return token;
|
||||
}
|
||||
|
||||
// auth token
|
||||
export function authJWT(token: string) {
|
||||
return new Promise<{
|
||||
userId: string;
|
||||
teamId: string;
|
||||
tmbId: string;
|
||||
}>((resolve, reject) => {
|
||||
const key = process.env.TOKEN_KEY as string;
|
||||
|
||||
jwt.verify(token, key, function (err, decoded: any) {
|
||||
if (err || !decoded?.userId) {
|
||||
reject(ERROR_ENUM.unAuthorization);
|
||||
return;
|
||||
}
|
||||
|
||||
resolve({
|
||||
userId: decoded.userId,
|
||||
teamId: decoded.teamId || '',
|
||||
tmbId: decoded.tmbId
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function parseHeaderCert({
|
||||
req,
|
||||
authToken = false,
|
||||
authRoot = false,
|
||||
authApiKey = false
|
||||
}: AuthModeType) {
|
||||
// parse jwt
|
||||
async function authCookieToken(cookie?: string, token?: 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
|
||||
async function parseAuthorization(authorization?: string) {
|
||||
if (!authorization) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
// Bearer fastgpt-xxxx-appId
|
||||
const auth = authorization.split(' ')[1];
|
||||
if (!auth) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
const { apikey, appId: authorizationAppid = '' } = await (async () => {
|
||||
const arr = auth.split('-');
|
||||
// abandon
|
||||
if (arr.length === 3) {
|
||||
return {
|
||||
apikey: `${arr[0]}-${arr[1]}`,
|
||||
appId: arr[2]
|
||||
};
|
||||
}
|
||||
if (arr.length === 2) {
|
||||
return {
|
||||
apikey: auth
|
||||
};
|
||||
}
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
})();
|
||||
|
||||
// auth apikey
|
||||
const { userId, teamId, tmbId, appId: apiKeyAppId = '' } = await authOpenApiKey({ apikey });
|
||||
|
||||
return {
|
||||
uid: userId,
|
||||
teamId,
|
||||
tmbId,
|
||||
apikey,
|
||||
appId: apiKeyAppId || authorizationAppid
|
||||
};
|
||||
}
|
||||
// root user
|
||||
async function parseRootKey(rootKey?: string, userId = '') {
|
||||
if (!rootKey || !process.env.ROOT_KEY || rootKey !== process.env.ROOT_KEY) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
return userId;
|
||||
}
|
||||
|
||||
const { cookie, token, apikey, rootkey, userid, authorization } = (req.headers ||
|
||||
{}) as ReqHeaderAuthType;
|
||||
|
||||
const { uid, teamId, tmbId, appId, openApiKey, authType } = await (async () => {
|
||||
if (authToken && (cookie || token)) {
|
||||
// user token(from fastgpt web)
|
||||
const res = await authCookieToken(cookie, token);
|
||||
return {
|
||||
uid: res.userId,
|
||||
teamId: res.teamId,
|
||||
tmbId: res.tmbId,
|
||||
appId: '',
|
||||
openApiKey: '',
|
||||
authType: AuthUserTypeEnum.token
|
||||
};
|
||||
}
|
||||
if (authRoot && rootkey) {
|
||||
// root user
|
||||
return {
|
||||
uid: await parseRootKey(rootkey, userid),
|
||||
teamId: '',
|
||||
tmbId: '',
|
||||
appId: '',
|
||||
openApiKey: '',
|
||||
authType: AuthUserTypeEnum.root
|
||||
};
|
||||
}
|
||||
if (authApiKey && apikey) {
|
||||
// apikey
|
||||
const parseResult = await authOpenApiKey({ apikey });
|
||||
return {
|
||||
uid: parseResult.userId,
|
||||
teamId: parseResult.teamId,
|
||||
tmbId: parseResult.tmbId,
|
||||
appId: parseResult.appId,
|
||||
openApiKey: parseResult.apikey,
|
||||
authType: AuthUserTypeEnum.apikey
|
||||
};
|
||||
}
|
||||
|
||||
if (authApiKey && authorization) {
|
||||
// apikey from authorization
|
||||
const authResponse = await parseAuthorization(authorization);
|
||||
return {
|
||||
uid: authResponse.uid,
|
||||
teamId: authResponse.teamId,
|
||||
tmbId: authResponse.tmbId,
|
||||
appId: authResponse.appId,
|
||||
openApiKey: authResponse.apikey,
|
||||
authType: AuthUserTypeEnum.apikey
|
||||
};
|
||||
}
|
||||
return {
|
||||
uid: '',
|
||||
teamId: '',
|
||||
tmbId: '',
|
||||
appId: '',
|
||||
openApiKey: '',
|
||||
authType: AuthUserTypeEnum.token
|
||||
};
|
||||
})();
|
||||
|
||||
// not rootUser and no uid, reject request
|
||||
if (!rootkey && !uid && !teamId && !tmbId) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
return {
|
||||
userId: String(uid),
|
||||
teamId: String(teamId),
|
||||
tmbId: String(tmbId),
|
||||
appId,
|
||||
authType,
|
||||
apikey: openApiKey
|
||||
};
|
||||
}
|
||||
|
||||
/* set cookie */
|
||||
export const setCookie = (res: NextApiResponse, token: string) => {
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
`token=${token}; Path=/; HttpOnly; Max-Age=604800; Samesite=None; Secure;`
|
||||
);
|
||||
};
|
||||
/* clear cookie */
|
||||
export const clearCookie = (res: NextApiResponse) => {
|
||||
res.setHeader('Set-Cookie', 'token=; Path=/; Max-Age=0');
|
||||
};
|
||||
|
||||
/* file permission */
|
||||
export const createFileToken = (data: FileTokenQuery) => {
|
||||
if (!process.env.FILE_TOKEN_KEY) {
|
||||
return Promise.reject('System unset FILE_TOKEN_KEY');
|
||||
}
|
||||
const expiredTime = Math.floor(Date.now() / 1000) + 60 * 30;
|
||||
|
||||
const key = process.env.FILE_TOKEN_KEY as string;
|
||||
const token = jwt.sign(
|
||||
{
|
||||
...data,
|
||||
exp: expiredTime
|
||||
},
|
||||
key
|
||||
);
|
||||
return Promise.resolve(token);
|
||||
};
|
||||
|
||||
export const authFileToken = (token?: string) =>
|
||||
new Promise<FileTokenQuery>((resolve, reject) => {
|
||||
if (!token) {
|
||||
return reject(ERROR_ENUM.unAuthFile);
|
||||
}
|
||||
const key = process.env.FILE_TOKEN_KEY as string;
|
||||
|
||||
jwt.verify(token, key, function (err, decoded: any) {
|
||||
if (err || !decoded.bucketName || !decoded?.teamId || !decoded?.tmbId || !decoded?.fileId) {
|
||||
reject(ERROR_ENUM.unAuthFile);
|
||||
return;
|
||||
}
|
||||
resolve({
|
||||
bucketName: decoded.bucketName,
|
||||
teamId: decoded.teamId,
|
||||
tmbId: decoded.tmbId,
|
||||
fileId: decoded.fileId
|
||||
});
|
||||
});
|
||||
});
|
||||
17
packages/service/support/permission/type.d.ts
vendored
Normal file
17
packages/service/support/permission/type.d.ts
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
import { NextApiRequest } from 'next';
|
||||
|
||||
export type ReqHeaderAuthType = {
|
||||
cookie?: string;
|
||||
token?: string;
|
||||
apikey?: string; // abandon
|
||||
rootkey?: string;
|
||||
userid?: string;
|
||||
authorization?: string;
|
||||
};
|
||||
export type AuthModeType = {
|
||||
req: NextApiRequest;
|
||||
authToken?: boolean;
|
||||
authRoot?: boolean;
|
||||
authApiKey?: boolean;
|
||||
per?: 'r' | 'w' | 'owner';
|
||||
};
|
||||
@@ -1,206 +0,0 @@
|
||||
import type { NextApiResponse, NextApiRequest } from 'next';
|
||||
import Cookie from 'cookie';
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { authOpenApiKey } from '../openapi/auth';
|
||||
import { authOutLinkId } from '../outLink/auth';
|
||||
import { MongoUser } from './schema';
|
||||
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
|
||||
import { ERROR_ENUM } from '@fastgpt/global/common/error/errorCode';
|
||||
|
||||
export enum AuthUserTypeEnum {
|
||||
token = 'token',
|
||||
root = 'root',
|
||||
apikey = 'apikey',
|
||||
outLink = 'outLink'
|
||||
}
|
||||
|
||||
/* auth balance */
|
||||
export const authBalanceByUid = async (uid: string) => {
|
||||
const user = await MongoUser.findById<UserModelSchema>(
|
||||
uid,
|
||||
'_id username balance openaiAccount timezone'
|
||||
);
|
||||
if (!user) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
if (user.balance <= 0) {
|
||||
return Promise.reject(ERROR_ENUM.insufficientQuota);
|
||||
}
|
||||
return user;
|
||||
};
|
||||
|
||||
/* uniform auth user */
|
||||
export const authUser = async ({
|
||||
req,
|
||||
authToken = false,
|
||||
authRoot = false,
|
||||
authApiKey = false,
|
||||
authBalance = false,
|
||||
authOutLink
|
||||
}: {
|
||||
req: NextApiRequest;
|
||||
authToken?: boolean;
|
||||
authRoot?: boolean;
|
||||
authApiKey?: boolean;
|
||||
authBalance?: boolean;
|
||||
authOutLink?: 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);
|
||||
}
|
||||
|
||||
// Bearer fastgpt-xxxx-appId
|
||||
const auth = authorization.split(' ')[1];
|
||||
if (!auth) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
const { apikey, appId: authorizationAppid = '' } = await (async () => {
|
||||
const arr = auth.split('-');
|
||||
// abandon
|
||||
if (arr.length === 3) {
|
||||
return {
|
||||
apikey: `${arr[0]}-${arr[1]}`,
|
||||
appId: arr[2]
|
||||
};
|
||||
}
|
||||
if (arr.length === 2) {
|
||||
return {
|
||||
apikey: auth
|
||||
};
|
||||
}
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
})();
|
||||
|
||||
// auth apikey
|
||||
const { userId, appId: apiKeyAppId = '' } = await authOpenApiKey({ apikey });
|
||||
|
||||
return {
|
||||
uid: userId,
|
||||
apikey,
|
||||
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);
|
||||
}
|
||||
return userId;
|
||||
};
|
||||
|
||||
const { cookie, token, apikey, rootkey, userid, authorization } = (req.headers || {}) as {
|
||||
cookie?: string;
|
||||
token?: string;
|
||||
apikey?: string;
|
||||
rootkey?: string; // abandon
|
||||
userid?: string;
|
||||
authorization?: string;
|
||||
};
|
||||
const { shareId } = (req?.body || {}) as { shareId?: string };
|
||||
|
||||
let uid = '';
|
||||
let appId = '';
|
||||
let openApiKey = apikey;
|
||||
let authType: `${AuthUserTypeEnum}` = AuthUserTypeEnum.token;
|
||||
|
||||
if (authOutLink && shareId) {
|
||||
const res = await authOutLinkId({ id: shareId });
|
||||
uid = res.userId;
|
||||
authType = AuthUserTypeEnum.outLink;
|
||||
} else if (authToken && (cookie || token)) {
|
||||
// user token(from fastgpt web)
|
||||
uid = await authCookieToken(cookie, token);
|
||||
authType = AuthUserTypeEnum.token;
|
||||
} else if (authRoot && rootkey) {
|
||||
// root user
|
||||
uid = await parseRootKey(rootkey, userid);
|
||||
authType = AuthUserTypeEnum.root;
|
||||
} else if (authApiKey && apikey) {
|
||||
// apikey
|
||||
const parseResult = await authOpenApiKey({ apikey });
|
||||
uid = parseResult.userId;
|
||||
authType = AuthUserTypeEnum.apikey;
|
||||
openApiKey = parseResult.apikey;
|
||||
} else if (authApiKey && authorization) {
|
||||
// apikey from authorization
|
||||
const authResponse = await parseAuthorization(authorization);
|
||||
uid = authResponse.uid;
|
||||
appId = authResponse.appId;
|
||||
openApiKey = authResponse.apikey;
|
||||
authType = AuthUserTypeEnum.apikey;
|
||||
}
|
||||
|
||||
// not rootUser and no uid, reject request
|
||||
if (!rootkey && !uid) {
|
||||
return Promise.reject(ERROR_ENUM.unAuthorization);
|
||||
}
|
||||
|
||||
// balance check
|
||||
const user = await (() => {
|
||||
if (authBalance) {
|
||||
return authBalanceByUid(uid);
|
||||
}
|
||||
})();
|
||||
|
||||
return {
|
||||
userId: String(uid),
|
||||
appId,
|
||||
authType,
|
||||
user,
|
||||
apikey: openApiKey
|
||||
};
|
||||
};
|
||||
|
||||
/* 生成 token */
|
||||
export function generateToken(userId: string) {
|
||||
const key = process.env.TOKEN_KEY as string;
|
||||
const token = jwt.sign(
|
||||
{
|
||||
userId,
|
||||
exp: Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 7
|
||||
},
|
||||
key
|
||||
);
|
||||
return token;
|
||||
}
|
||||
// auth token
|
||||
export function authJWT(token: string) {
|
||||
return new Promise<string>((resolve, reject) => {
|
||||
const key = process.env.TOKEN_KEY as string;
|
||||
|
||||
jwt.verify(token, key, function (err, decoded: any) {
|
||||
if (err || !decoded?.userId) {
|
||||
reject(ERROR_ENUM.unAuthorization);
|
||||
return;
|
||||
}
|
||||
resolve(decoded.userId);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/* set cookie */
|
||||
export const setCookie = (res: NextApiResponse, token: string) => {
|
||||
res.setHeader(
|
||||
'Set-Cookie',
|
||||
`token=${token}; Path=/; HttpOnly; Max-Age=604800; Samesite=None; Secure;`
|
||||
);
|
||||
};
|
||||
/* clear cookie */
|
||||
export const clearCookie = (res: NextApiResponse) => {
|
||||
res.setHeader('Set-Cookie', 'token=; Path=/; Max-Age=0');
|
||||
};
|
||||
11
packages/service/support/user/controller.ts
Normal file
11
packages/service/support/user/controller.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { MongoUser } from './schema';
|
||||
|
||||
export async function authUserExist({ userId, username }: { userId?: string; username?: string }) {
|
||||
if (userId) {
|
||||
return MongoUser.findOne({ _id: userId });
|
||||
}
|
||||
if (username) {
|
||||
return MongoUser.findOne({ username });
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
import { MongoUserInform } from './schema';
|
||||
import { MongoUser } from '../schema';
|
||||
import { InformTypeEnum } from '@fastgpt/global/support/user/constant';
|
||||
|
||||
export type SendInformProps = {
|
||||
type: `${InformTypeEnum}`;
|
||||
title: string;
|
||||
content: string;
|
||||
};
|
||||
|
||||
export async function sendInform2AllUser({ type, title, content }: SendInformProps) {
|
||||
const users = await MongoUser.find({}, '_id');
|
||||
await MongoUserInform.insertMany(
|
||||
users.map(({ _id }) => ({
|
||||
type,
|
||||
title,
|
||||
content,
|
||||
userId: _id
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
||||
export async function sendInform2OneUser({
|
||||
type,
|
||||
title,
|
||||
content,
|
||||
userId
|
||||
}: SendInformProps & { userId: string }) {
|
||||
const inform = await MongoUserInform.findOne({
|
||||
type,
|
||||
title,
|
||||
content,
|
||||
userId,
|
||||
time: { $gte: new Date(Date.now() - 5 * 60 * 1000) }
|
||||
});
|
||||
|
||||
if (inform) return;
|
||||
|
||||
await MongoUserInform.create({
|
||||
type,
|
||||
title,
|
||||
content,
|
||||
userId
|
||||
});
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { connectionMongo, type Model } from '../../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import type { UserInformSchema } from '@fastgpt/global/support/user/type.d';
|
||||
import { InformTypeMap } from '@fastgpt/global/support/user/constant';
|
||||
|
||||
const InformSchema = new Schema({
|
||||
userId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'user',
|
||||
required: true
|
||||
},
|
||||
time: {
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
enum: Object.keys(InformTypeMap)
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
content: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
read: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
InformSchema.index({ time: -1 });
|
||||
InformSchema.index({ userId: 1 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoUserInform: Model<UserInformSchema> =
|
||||
models['inform'] || model('inform', InformSchema);
|
||||
@@ -1,7 +1,7 @@
|
||||
import { connectionMongo, type Model } from '../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { hashStr } from '@fastgpt/global/common/string/tools';
|
||||
import { PRICE_SCALE } from '@fastgpt/global/common/bill/constants';
|
||||
import { PRICE_SCALE } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import type { UserModelSchema } from '@fastgpt/global/support/user/type';
|
||||
|
||||
export const userCollectionName = 'users';
|
||||
|
||||
125
packages/service/support/user/team/controller.ts
Normal file
125
packages/service/support/user/team/controller.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
import { TeamItemType } from '@fastgpt/global/support/user/team/type';
|
||||
import { connectionMongo, Types } from '../../../common/mongo';
|
||||
import {
|
||||
TeamMemberRoleEnum,
|
||||
TeamMemberStatusEnum,
|
||||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
|
||||
export async function getTeamInfoByTmbId({
|
||||
tmbId,
|
||||
userId
|
||||
}: {
|
||||
tmbId?: string;
|
||||
userId?: string;
|
||||
}): Promise<TeamItemType> {
|
||||
if (!tmbId && !userId) {
|
||||
return Promise.reject('tmbId or userId is required');
|
||||
}
|
||||
|
||||
const db = connectionMongo?.connection?.db;
|
||||
|
||||
const TeamMember = db.collection(TeamMemberCollectionName);
|
||||
|
||||
const results = await TeamMember.aggregate([
|
||||
{
|
||||
$match: tmbId
|
||||
? {
|
||||
_id: new Types.ObjectId(tmbId)
|
||||
}
|
||||
: {
|
||||
userId: new Types.ObjectId(userId),
|
||||
defaultTeam: true
|
||||
}
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: TeamCollectionName, // 关联的集合名
|
||||
localField: 'teamId', // TeamMember 集合中用于关联的字段
|
||||
foreignField: '_id', // Team 集合中用于关联的字段
|
||||
as: 'team' // 查询结果中的字段名,存放关联查询的结果
|
||||
}
|
||||
},
|
||||
{
|
||||
$unwind: '$team' // 将查询结果中的 team 字段展开,变成一个对象
|
||||
}
|
||||
]).toArray();
|
||||
const tmb = results[0];
|
||||
|
||||
if (!tmb) {
|
||||
return Promise.reject('team not exist');
|
||||
}
|
||||
|
||||
return {
|
||||
userId: String(tmb.userId),
|
||||
teamId: String(tmb.teamId),
|
||||
teamName: tmb.team.name,
|
||||
avatar: tmb.team.avatar,
|
||||
balance: tmb.team.balance,
|
||||
tmbId: String(tmb._id),
|
||||
role: tmb.role,
|
||||
status: tmb.status,
|
||||
defaultTeam: tmb.defaultTeam,
|
||||
canWrite: tmb.role !== TeamMemberRoleEnum.visitor,
|
||||
maxSize: tmb.team.maxSize
|
||||
};
|
||||
}
|
||||
export async function createDefaultTeam({
|
||||
userId,
|
||||
teamName = 'My Team',
|
||||
avatar = '/icon/logo.svg',
|
||||
balance = 0,
|
||||
maxSize = 5
|
||||
}: {
|
||||
userId: string;
|
||||
teamName?: string;
|
||||
avatar?: string;
|
||||
balance?: number;
|
||||
maxSize?: number;
|
||||
}) {
|
||||
const db = connectionMongo.connection.db;
|
||||
const Team = db.collection(TeamCollectionName);
|
||||
const TeamMember = db.collection(TeamMemberCollectionName);
|
||||
|
||||
// auth default team
|
||||
const tmb = await TeamMember.findOne({
|
||||
userId: new Types.ObjectId(userId),
|
||||
defaultTeam: true
|
||||
});
|
||||
|
||||
if (!tmb) {
|
||||
console.log('create default team', userId);
|
||||
|
||||
// create
|
||||
const { insertedId } = await Team.insertOne({
|
||||
ownerId: userId,
|
||||
name: teamName,
|
||||
avatar,
|
||||
balance,
|
||||
maxSize,
|
||||
createTime: new Date()
|
||||
});
|
||||
await TeamMember.insertOne({
|
||||
teamId: insertedId,
|
||||
userId,
|
||||
role: TeamMemberRoleEnum.owner,
|
||||
status: TeamMemberStatusEnum.active,
|
||||
createTime: new Date(),
|
||||
defaultTeam: true
|
||||
});
|
||||
} else {
|
||||
console.log('default team exist', userId);
|
||||
await Team.updateOne(
|
||||
{
|
||||
_id: new Types.ObjectId(tmb.teamId)
|
||||
},
|
||||
{
|
||||
$set: {
|
||||
balance,
|
||||
maxSize
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
60
packages/service/support/wallet/bill/schema.ts
Normal file
60
packages/service/support/wallet/bill/schema.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { connectionMongo, type Model } from '../../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { BillSchema as BillType } from '@fastgpt/global/support/wallet/bill/type';
|
||||
import { BillSourceMap } from '@fastgpt/global/support/wallet/bill/constants';
|
||||
import {
|
||||
TeamCollectionName,
|
||||
TeamMemberCollectionName
|
||||
} from '@fastgpt/global/support/user/team/constant';
|
||||
|
||||
const BillSchema = new Schema({
|
||||
userId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'user'
|
||||
},
|
||||
teamId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamCollectionName,
|
||||
required: true
|
||||
},
|
||||
tmbId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: TeamMemberCollectionName,
|
||||
required: true
|
||||
},
|
||||
appName: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
appId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'model',
|
||||
required: false
|
||||
},
|
||||
time: {
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
source: {
|
||||
type: String,
|
||||
enum: Object.keys(BillSourceMap),
|
||||
required: true
|
||||
},
|
||||
list: {
|
||||
type: Array,
|
||||
default: []
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
BillSchema.index({ userId: 1 });
|
||||
BillSchema.index({ time: 1 }, { expireAfterSeconds: 90 * 24 * 60 * 60 });
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
export const MongoBill: Model<BillType> = models['bill'] || model('bill', BillSchema);
|
||||
@@ -1,31 +0,0 @@
|
||||
import { connectionMongo, type Model } from '../../../common/mongo';
|
||||
const { Schema, model, models } = connectionMongo;
|
||||
import { PaySchema as PayType } from '@fastgpt/global/support/wallet/type.d';
|
||||
|
||||
const PaySchema = new Schema({
|
||||
userId: {
|
||||
type: Schema.Types.ObjectId,
|
||||
ref: 'user',
|
||||
required: true
|
||||
},
|
||||
createTime: {
|
||||
type: Date,
|
||||
default: () => new Date()
|
||||
},
|
||||
price: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
orderId: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
status: {
|
||||
// 支付的状态
|
||||
type: String,
|
||||
default: 'NOTPAY',
|
||||
enum: ['SUCCESS', 'REFUND', 'NOTPAY', 'CLOSED']
|
||||
}
|
||||
});
|
||||
|
||||
export const MongoPay: Model<PayType> = models['pay'] || model('pay', PaySchema);
|
||||
Reference in New Issue
Block a user