mirror of
https://github.com/labring/FastGPT.git
synced 2025-10-18 17:51:24 +00:00
feat: login limit (#3369)
* feat: login limit * feat: env template * fix: ts
This commit is contained in:
@@ -72,7 +72,7 @@ export const ERROR_RESPONSE: Record<
|
|||||||
[ERROR_ENUM.tooManyRequest]: {
|
[ERROR_ENUM.tooManyRequest]: {
|
||||||
code: 429,
|
code: 429,
|
||||||
statusText: ERROR_ENUM.tooManyRequest,
|
statusText: ERROR_ENUM.tooManyRequest,
|
||||||
message: 'Too many request',
|
message: i18nT('common:error.too_many_request'),
|
||||||
data: null
|
data: null
|
||||||
},
|
},
|
||||||
[ERROR_ENUM.insufficientQuota]: {
|
[ERROR_ENUM.insufficientQuota]: {
|
||||||
|
@@ -24,7 +24,7 @@ export async function getTmpData<T extends TmpDataEnum>({
|
|||||||
}).lean()) as TmpDataSchema<TmpDataType<T>> | null;
|
}).lean()) as TmpDataSchema<TmpDataType<T>> | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setTmpData<T extends TmpDataEnum>({
|
export function setTmpData<T extends TmpDataEnum>({
|
||||||
type,
|
type,
|
||||||
metadata,
|
metadata,
|
||||||
data
|
data
|
||||||
@@ -33,7 +33,7 @@ export async function setTmpData<T extends TmpDataEnum>({
|
|||||||
metadata: TmpDataMetadata<T>;
|
metadata: TmpDataMetadata<T>;
|
||||||
data: TmpDataType<T>;
|
data: TmpDataType<T>;
|
||||||
}) {
|
}) {
|
||||||
return await MongoTmpData.updateOne(
|
return MongoTmpData.updateOne(
|
||||||
{
|
{
|
||||||
dataId: getDataId(type, metadata)
|
dataId: getDataId(type, metadata)
|
||||||
},
|
},
|
||||||
@@ -43,7 +43,8 @@ export async function setTmpData<T extends TmpDataEnum>({
|
|||||||
expireAt: addMilliseconds(Date.now(), TmpDataExpireTime[type])
|
expireAt: addMilliseconds(Date.now(), TmpDataExpireTime[type])
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
upsert: true
|
upsert: true,
|
||||||
|
new: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@@ -874,6 +874,7 @@
|
|||||||
"error.fileNotFound": "File not found~",
|
"error.fileNotFound": "File not found~",
|
||||||
"error.inheritPermissionError": "Inherit permission Error",
|
"error.inheritPermissionError": "Inherit permission Error",
|
||||||
"error.missingParams": "Insufficient parameters",
|
"error.missingParams": "Insufficient parameters",
|
||||||
|
"error.too_many_request": "Too many request",
|
||||||
"error.upload_file_error_filename": "{{name}} Upload Failed",
|
"error.upload_file_error_filename": "{{name}} Upload Failed",
|
||||||
"error.username_empty": "Account cannot be empty",
|
"error.username_empty": "Account cannot be empty",
|
||||||
"extraction_results": "Extraction Results",
|
"extraction_results": "Extraction Results",
|
||||||
|
@@ -873,6 +873,7 @@
|
|||||||
"error.fileNotFound": "文件找不到了~",
|
"error.fileNotFound": "文件找不到了~",
|
||||||
"error.inheritPermissionError": "权限继承错误",
|
"error.inheritPermissionError": "权限继承错误",
|
||||||
"error.missingParams": "参数缺失",
|
"error.missingParams": "参数缺失",
|
||||||
|
"error.too_many_request": "请求太频繁了,请稍后重试",
|
||||||
"error.upload_file_error_filename": "{{name}} 上传失败",
|
"error.upload_file_error_filename": "{{name}} 上传失败",
|
||||||
"error.username_empty": "账号不能为空",
|
"error.username_empty": "账号不能为空",
|
||||||
"extraction_results": "提取结果",
|
"extraction_results": "提取结果",
|
||||||
@@ -1179,7 +1180,6 @@
|
|||||||
"user.password_message": "密码最少 4 位最多 60 位",
|
"user.password_message": "密码最少 4 位最多 60 位",
|
||||||
"user.team.Balance": "团队余额",
|
"user.team.Balance": "团队余额",
|
||||||
"user.team.Check Team": "切换",
|
"user.team.Check Team": "切换",
|
||||||
|
|
||||||
"user.team.Invite Member": "邀请成员",
|
"user.team.Invite Member": "邀请成员",
|
||||||
"user.team.Invite Member Tips": "对方可查阅或使用团队内的其他资源",
|
"user.team.Invite Member Tips": "对方可查阅或使用团队内的其他资源",
|
||||||
"user.team.Leave Team": "离开团队",
|
"user.team.Leave Team": "离开团队",
|
||||||
@@ -1192,13 +1192,10 @@
|
|||||||
"user.team.Processing invitations Tips": "你有 {{amount}} 个需要处理的团队邀请",
|
"user.team.Processing invitations Tips": "你有 {{amount}} 个需要处理的团队邀请",
|
||||||
"user.team.Remove Member Confirm Tip": "确认将 {{username}} 移出团队?",
|
"user.team.Remove Member Confirm Tip": "确认将 {{username}} 移出团队?",
|
||||||
"user.team.Select Team": "团队选择",
|
"user.team.Select Team": "团队选择",
|
||||||
|
|
||||||
"user.team.Switch Team Failed": "切换团队异常",
|
"user.team.Switch Team Failed": "切换团队异常",
|
||||||
"user.team.Tags Async": "保存",
|
"user.team.Tags Async": "保存",
|
||||||
|
|
||||||
"user.team.Team Tags Async": "标签同步",
|
"user.team.Team Tags Async": "标签同步",
|
||||||
"user.team.Team Tags Async Success": "链接报错成功,标签信息更新",
|
"user.team.Team Tags Async Success": "链接报错成功,标签信息更新",
|
||||||
|
|
||||||
"user.team.invite.Accept Confirm": "确认加入该团队?",
|
"user.team.invite.Accept Confirm": "确认加入该团队?",
|
||||||
"user.team.invite.Accepted": "已加入团队",
|
"user.team.invite.Accepted": "已加入团队",
|
||||||
"user.team.invite.Deal Width Footer Tip": "处理完会自动关闭噢~",
|
"user.team.invite.Deal Width Footer Tip": "处理完会自动关闭噢~",
|
||||||
|
@@ -873,6 +873,7 @@
|
|||||||
"error.fileNotFound": "找不到檔案",
|
"error.fileNotFound": "找不到檔案",
|
||||||
"error.inheritPermissionError": "繼承權限錯誤",
|
"error.inheritPermissionError": "繼承權限錯誤",
|
||||||
"error.missingParams": "參數不足",
|
"error.missingParams": "參數不足",
|
||||||
|
"error.too_many_request": "請求太頻繁了,請稍後重試",
|
||||||
"error.upload_file_error_filename": "{{name}} 上傳失敗",
|
"error.upload_file_error_filename": "{{name}} 上傳失敗",
|
||||||
"error.username_empty": "帳號不能為空",
|
"error.username_empty": "帳號不能為空",
|
||||||
"extraction_results": "提取結果",
|
"extraction_results": "提取結果",
|
||||||
|
@@ -43,6 +43,8 @@ LOG_LEVEL=debug
|
|||||||
STORE_LOG_LEVEL=warn
|
STORE_LOG_LEVEL=warn
|
||||||
|
|
||||||
# 安全配置
|
# 安全配置
|
||||||
|
# 启动 IP 限流(true),部分接口增加了 ip 限流策略,防止非正常请求操作。
|
||||||
|
USE_IP_LIMIT=false
|
||||||
# 工作流最大运行次数,避免极端的死循环情况
|
# 工作流最大运行次数,避免极端的死循环情况
|
||||||
WORKFLOW_MAX_RUN_TIMES=500
|
WORKFLOW_MAX_RUN_TIMES=500
|
||||||
# 循环最大运行次数,避免极端的死循环情况
|
# 循环最大运行次数,避免极端的死循环情况
|
||||||
|
@@ -99,4 +99,4 @@ async function handler(req: NextApiRequest) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NextAPI(useReqFrequencyLimit(1, 2), handler);
|
export default NextAPI(useReqFrequencyLimit(1, 15), handler);
|
||||||
|
@@ -1,71 +1,63 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@fastgpt/service/common/response';
|
|
||||||
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
||||||
import { createJWT, setCookie } from '@fastgpt/service/support/permission/controller';
|
import { createJWT, setCookie } from '@fastgpt/service/support/permission/controller';
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
|
||||||
import { getUserDetail } from '@fastgpt/service/support/user/controller';
|
import { getUserDetail } from '@fastgpt/service/support/user/controller';
|
||||||
import type { PostLoginProps } from '@fastgpt/global/support/user/api.d';
|
import type { PostLoginProps } from '@fastgpt/global/support/user/api.d';
|
||||||
import { UserStatusEnum } from '@fastgpt/global/support/user/constant';
|
import { UserStatusEnum } from '@fastgpt/global/support/user/constant';
|
||||||
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
|
import { useReqFrequencyLimit } from '@fastgpt/service/common/middle/reqFrequencyLimit';
|
||||||
|
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
try {
|
const { username, password } = req.body as PostLoginProps;
|
||||||
await connectToDatabase();
|
|
||||||
const { username, password } = req.body as PostLoginProps;
|
|
||||||
|
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
throw new Error('缺少参数');
|
throw new Error('缺少参数');
|
||||||
}
|
|
||||||
|
|
||||||
// 检测用户是否存在
|
|
||||||
const authCert = await MongoUser.findOne(
|
|
||||||
{
|
|
||||||
username
|
|
||||||
},
|
|
||||||
'status'
|
|
||||||
);
|
|
||||||
if (!authCert) {
|
|
||||||
throw new Error('用户未注册');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (authCert.status === UserStatusEnum.forbidden) {
|
|
||||||
throw new Error('账号已停用,无法登录');
|
|
||||||
}
|
|
||||||
|
|
||||||
const user = await MongoUser.findOne({
|
|
||||||
username,
|
|
||||||
password
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
throw new Error('密码错误');
|
|
||||||
}
|
|
||||||
|
|
||||||
const userDetail = await getUserDetail({
|
|
||||||
tmbId: user?.lastLoginTmbId,
|
|
||||||
userId: user._id
|
|
||||||
});
|
|
||||||
|
|
||||||
MongoUser.findByIdAndUpdate(user._id, {
|
|
||||||
lastLoginTmbId: userDetail.team.tmbId
|
|
||||||
});
|
|
||||||
|
|
||||||
const token = createJWT({
|
|
||||||
...userDetail,
|
|
||||||
isRoot: username === 'root'
|
|
||||||
});
|
|
||||||
|
|
||||||
setCookie(res, token);
|
|
||||||
|
|
||||||
jsonRes(res, {
|
|
||||||
data: {
|
|
||||||
user: userDetail,
|
|
||||||
token
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (err) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error: err
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检测用户是否存在
|
||||||
|
const authCert = await MongoUser.findOne(
|
||||||
|
{
|
||||||
|
username
|
||||||
|
},
|
||||||
|
'status'
|
||||||
|
);
|
||||||
|
if (!authCert) {
|
||||||
|
throw new Error('用户未注册');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authCert.status === UserStatusEnum.forbidden) {
|
||||||
|
throw new Error('账号已停用,无法登录');
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await MongoUser.findOne({
|
||||||
|
username,
|
||||||
|
password
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new Error('密码错误');
|
||||||
|
}
|
||||||
|
|
||||||
|
const userDetail = await getUserDetail({
|
||||||
|
tmbId: user?.lastLoginTmbId,
|
||||||
|
userId: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
MongoUser.findByIdAndUpdate(user._id, {
|
||||||
|
lastLoginTmbId: userDetail.team.tmbId
|
||||||
|
});
|
||||||
|
|
||||||
|
const token = createJWT({
|
||||||
|
...userDetail,
|
||||||
|
isRoot: username === 'root'
|
||||||
|
});
|
||||||
|
|
||||||
|
setCookie(res, token);
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: userDetail,
|
||||||
|
token
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default NextAPI(useReqFrequencyLimit(120, 10), handler);
|
||||||
|
@@ -89,7 +89,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
|||||||
removeFilesByPaths(filePaths);
|
removeFilesByPaths(filePaths);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NextAPI(useReqFrequencyLimit(1, 2), handler);
|
export default NextAPI(useReqFrequencyLimit(1, 1), handler);
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
api: {
|
api: {
|
||||||
|
Reference in New Issue
Block a user