这里的逻辑是一个人可以在多个团队中,但在每个团队的角色有可能是不一样的,当一个人同时存在2个或者多个团队中时,下拉列表中会有多个团队详情,切换团队会查询不同的成员和部门。
团队列表查询api实现代码:
import { NextAPI } from '@/service/middleware/entry';
import type { NextApiRequest, NextApiResponse } from 'next';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
import { getResourcePermission } from '@fastgpt/service/support/permission/controller';
import { PerResourceTypeEnum } from '@fastgpt/global/support/permission/constant';
import { TeamPermission } from '@fastgpt/global/support/permission/user/controller';
import { TeamDefaultPermissionVal } from '@fastgpt/global/support/permission/user/constant';
import { TeamMemberRoleEnum } from '@fastgpt/global/support/user/team/constant';
import { TeamSchema } from '@fastgpt/global/support/user/team/type';
async function handler(req: NextApiRequest, res: NextApiResponse) {
try {
const { userId } = await authCert({ req, authToken: true });
let userInTeams = await MongoTeamMember.find({ userId })
.populate<{ team: TeamSchema }>('team')
.lean();
if (!userInTeams.length) {
return res.status(404).json({ message: 'member not exist' });
}
const aggreTeam = await Promise.all(userInTeams.map(async (tmb) => await getTeam(tmb)));
// console.log(aggreTeam, 'aggreTeam');
return aggreTeam;
} catch (error: any) {
return res.status(500).json({ message: '服务器内部错误' });
}
}
async function getTeam(tmb: any) {
const Per = await getResourcePermission({
resourceType: PerResourceTypeEnum.team,
teamId: tmb.teamId,
tmbId: tmb._id
});
return {
userId: String(tmb.userId),
teamId: String(tmb.teamId),
teamAvatar: tmb.team.avatar,
teamName: tmb.team.name,
memberName: tmb.name,
avatar: tmb.avatar,
balance: tmb.team.balance,
tmbId: String(tmb._id),
teamDomain: tmb.team?.teamDomain,
role: tmb.role,
status: tmb.status,
permission: new TeamPermission({
per: Per ?? TeamDefaultPermissionVal,
isOwner: tmb.role === TeamMemberRoleEnum.owner
}),
notificationAccount: tmb.team.notificationAccount,
lafAccount: tmb.team.lafAccount,
openaiAccount: tmb.team.openaiAccount,
externalWorkflowVariables: tmb.team.externalWorkflowVariables
};
}
export default NextAPI(handler);
切换团队api接口代码:
import type { NextApiRequest, NextApiResponse } from 'next';
import { NextAPI } from '@/service/middleware/entry';
import { MongoTeam } from '@fastgpt/service/support/user/team/teamSchema';
import { createJWT, setCookie } from '@fastgpt/service/support/permission/controller';
import { authCert } from '@fastgpt/service/support/permission/auth/common';
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
async function handler(req: NextApiRequest, res: NextApiResponse) {
let { teamId } = req.body;
// const team = await MongoTeam.findById(teamId);
const { userId, isRoot } = await authCert({ req, authToken: true });
const userTeam = await MongoTeamMember.findOne({ userId, teamId });
const tmbId: any = userTeam?._id;
const token = createJWT({
_id: userId,
team: {
teamId,
tmbId
},
isRoot
});
setCookie(res, token);
}
export default NextAPI(handler);
这里主要的逻辑是通过userid和当前切换的teamid还有tmbid(也就是当前team的id)使用jwt去创建一个token,和用户登录的逻辑是一样的,只是使用的teamid不一样,其它接口调用的时候都是根据cookie里面的token又去用jwt解析验证的,开源代码里面有个文件有以下代码主要是用来解析验证的:
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 = token || cookies[TokenName];
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 { teamId, tmbId, appId: apiKeyAppId = '', sourceName } = await authOpenApiKey({ apikey });
return {
uid: '',
teamId,
tmbId,
apikey,
appId: apiKeyAppId || authorizationAppid,
sourceName
};
}
// root user
async function parseRootKey(rootKey?: string) {
if (!rootKey || !process.env.ROOT_KEY || rootKey !== process.env.ROOT_KEY) {
return Promise.reject(ERROR_ENUM.unAuthorization);
}
}
const { cookie, token, rootkey, authorization } = (req.headers || {}) as ReqHeaderAuthType;
const { uid, teamId, tmbId, appId, openApiKey, authType, isRoot, sourceName } =
await (async () => {
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,
sourceName: authResponse.sourceName
};
}
if (authToken && (token || cookie)) {
// 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,
isRoot: res.isRoot
};
}
if (authRoot && rootkey) {
await parseRootKey(rootkey);
// root user
return {
uid: '',
teamId: '',
tmbId: '',
appId: '',
openApiKey: '',
authType: AuthUserTypeEnum.root,
isRoot: true
};
}
return Promise.reject(ERROR_ENUM.unAuthorization);
})();
if (!authRoot && (!teamId || !tmbId)) {
return Promise.reject(ERROR_ENUM.unAuthorization);
}
return {
userId: String(uid),
teamId: String(teamId),
tmbId: String(tmbId),
appId,
authType,
sourceName,
apikey: openApiKey,
isRoot: !!isRoot
};
}
用户正常登录时主要是使用authCookieToken这个函数的逻辑,因为有token;
使用创建api密钥也就是前文教大家用过的fastGPT—前端开发获取api密钥调用机器人对话接口(HTML实现)-CSDN博客那种方法就是用parseAuthorization函数的方法去验证的;
parseRootKey是.env文件中的ROOT_KEY,env文件大概配置是这样:
mongo 数据库、PG 向量库、milvus 向量库都得有
PORT=3001
LOG_DEPTH=3
# 默认用户密码,用户名为 root,每次重启时会自动更新。
DEFAULT_ROOT_PSW=245ZXCV@qaz
# 数据库最大连接数
DB_MAX_LINK=5
# token
TOKEN_KEY=sadasgvfd
# 文件阅读时的秘钥
FILE_TOKEN_KEY=filetokenkey
# root key, 最高权限
ROOT_KEY=fdafasd
# openai 基本地址,可用作中转。
OPENAI_BASE_URL=http://10.165.182.205:3000/
# oneapi 地址,可以使用 oneapi 来实现多模型接入
ONEAPI_URL=http://10.165.182.205:3000/v1/
# 通用key。可以是 openai 的也可以是 oneapi 的。
# 此处逻辑:优先走 ONEAPI_URL,如果填写了 ONEAPI_URL,key 也需要是 ONEAPI 的 key
CHAT_API_KEY=sk-HSgxOJ390gdckdqvBd7e70C1CfA3479cA3A9D4E1F693D822
# mongo 数据库连接参数,本地开发时,mongo可能需要增加 directConnection=true 参数,才能连接上。
MONGODB_URI=mongodb://10.185.92.58:27017/fastgpt?authSource=admin&directConnection=true
# 向量库优先级: pg > milvus
# PG 向量库连接参数
PG_URL=postgresql://user08:245ZXCV@qaz@10.195.162.55:5432/fastgpt
# milvus 向量库连接参数
MILVUS_ADDRESS=https://in03-78bd7f60e6e2a7c.api.gcp-us-west1.zillizcloud.com
MILVUS_TOKEN=133964348b00dfdawgsrhagwarge385c8c64a3ec16b1ab92d3c67dcc4e0370fb9dd15791bcd6dadaferagerwgseagrw56d
# code sandbox url
SANDBOX_URL=https://fastgptsandbox.encloudx.com
# 商业版地址
PRO_URL=
# 首页路径
HOME_URL=/
# 日志等级: debug, info, warn, error
LOG_LEVEL=debug
STORE_LOG_LEVEL=warn
# Loki Log Path