perf: password check;perf: image upload check;perf: sso login check (#3509)
* perf: password check * perf: image upload check * perf: sso login check
This commit is contained in:
@@ -11,3 +11,4 @@ weight: 806
|
|||||||
|
|
||||||
1.
|
1.
|
||||||
2. 新增 - 支持部门架构权限模式
|
2. 新增 - 支持部门架构权限模式
|
||||||
|
3. 优化 - 图片上传安全校验。并增加头像图片唯一存储,确保不会累计存储。
|
||||||
@@ -1,14 +1,20 @@
|
|||||||
|
import { i18nT } from '../../../../web/i18n/utils';
|
||||||
import { ErrType } from '../errorCode';
|
import { ErrType } from '../errorCode';
|
||||||
|
|
||||||
/* dataset: 507000 */
|
/* dataset: 507000 */
|
||||||
const startCode = 507000;
|
const startCode = 507000;
|
||||||
export enum CommonErrEnum {
|
export enum CommonErrEnum {
|
||||||
|
invalidParams = 'invalidParams',
|
||||||
fileNotFound = 'fileNotFound',
|
fileNotFound = 'fileNotFound',
|
||||||
unAuthFile = 'unAuthFile',
|
unAuthFile = 'unAuthFile',
|
||||||
missingParams = 'missingParams',
|
missingParams = 'missingParams',
|
||||||
inheritPermissionError = 'inheritPermissionError'
|
inheritPermissionError = 'inheritPermissionError'
|
||||||
}
|
}
|
||||||
const datasetErr = [
|
const datasetErr = [
|
||||||
|
{
|
||||||
|
statusText: CommonErrEnum.fileNotFound,
|
||||||
|
message: i18nT('common:error.invalid_params')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
statusText: CommonErrEnum.fileNotFound,
|
statusText: CommonErrEnum.fileNotFound,
|
||||||
message: 'error.fileNotFound'
|
message: 'error.fileNotFound'
|
||||||
|
|||||||
@@ -16,11 +16,7 @@ const errList = [
|
|||||||
{
|
{
|
||||||
statusText: UserErrEnum.binVisitor,
|
statusText: UserErrEnum.binVisitor,
|
||||||
message: i18nT('common:code_error.user_error.bin_visitor')
|
message: i18nT('common:code_error.user_error.bin_visitor')
|
||||||
}, // 身份校验未通过
|
},
|
||||||
{
|
|
||||||
statusText: UserErrEnum.binVisitor,
|
|
||||||
message: i18nT('common:code_error.user_error.bin_visitor_guest')
|
|
||||||
}, // 游客身份
|
|
||||||
{
|
{
|
||||||
statusText: UserErrEnum.balanceNotEnough,
|
statusText: UserErrEnum.balanceNotEnough,
|
||||||
message: i18nT('common:code_error.user_error.balance_not_enough')
|
message: i18nT('common:code_error.user_error.balance_not_enough')
|
||||||
|
|||||||
5
packages/global/common/file/api.d.ts
vendored
5
packages/global/common/file/api.d.ts
vendored
@@ -1,10 +1,7 @@
|
|||||||
import { MongoImageTypeEnum } from './image/constants';
|
|
||||||
import { OutLinkChatAuthProps } from '../../support/permission/chat.d';
|
import { OutLinkChatAuthProps } from '../../support/permission/chat.d';
|
||||||
|
|
||||||
export type preUploadImgProps = OutLinkChatAuthProps & {
|
export type preUploadImgProps = OutLinkChatAuthProps & {
|
||||||
type: `${MongoImageTypeEnum}`;
|
// expiredTime?: Date;
|
||||||
|
|
||||||
expiredTime?: Date;
|
|
||||||
metadata?: Record<string, any>;
|
metadata?: Record<string, any>;
|
||||||
};
|
};
|
||||||
export type UploadImgProps = preUploadImgProps & {
|
export type UploadImgProps = preUploadImgProps & {
|
||||||
|
|||||||
@@ -1,66 +1,5 @@
|
|||||||
export const imageBaseUrl = '/api/system/img/';
|
export const imageBaseUrl = '/api/system/img/';
|
||||||
|
|
||||||
export enum MongoImageTypeEnum {
|
|
||||||
systemAvatar = 'systemAvatar',
|
|
||||||
appAvatar = 'appAvatar',
|
|
||||||
pluginAvatar = 'pluginAvatar',
|
|
||||||
datasetAvatar = 'datasetAvatar',
|
|
||||||
userAvatar = 'userAvatar',
|
|
||||||
teamAvatar = 'teamAvatar',
|
|
||||||
groupAvatar = 'groupAvatar',
|
|
||||||
orgAvatar = 'orgAvatar',
|
|
||||||
|
|
||||||
chatImage = 'chatImage',
|
|
||||||
collectionImage = 'collectionImage'
|
|
||||||
}
|
|
||||||
export const mongoImageTypeMap = {
|
|
||||||
[MongoImageTypeEnum.systemAvatar]: {
|
|
||||||
label: 'appAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.appAvatar]: {
|
|
||||||
label: 'appAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.pluginAvatar]: {
|
|
||||||
label: 'pluginAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.datasetAvatar]: {
|
|
||||||
label: 'datasetAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.userAvatar]: {
|
|
||||||
label: 'userAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.teamAvatar]: {
|
|
||||||
label: 'teamAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.groupAvatar]: {
|
|
||||||
label: 'groupAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.orgAvatar]: {
|
|
||||||
label: 'orgAvatar',
|
|
||||||
unique: true
|
|
||||||
},
|
|
||||||
|
|
||||||
[MongoImageTypeEnum.chatImage]: {
|
|
||||||
label: 'chatImage',
|
|
||||||
unique: false
|
|
||||||
},
|
|
||||||
[MongoImageTypeEnum.collectionImage]: {
|
|
||||||
label: 'collectionImage',
|
|
||||||
unique: false
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
export const uniqueImageTypeList = Object.entries(mongoImageTypeMap)
|
|
||||||
.filter(([key, value]) => value.unique)
|
|
||||||
.map(([key]) => key as `${MongoImageTypeEnum}`);
|
|
||||||
|
|
||||||
export const FolderIcon = 'file/fill/folder';
|
export const FolderIcon = 'file/fill/folder';
|
||||||
export const FolderImgUrl = '/imgs/files/folder.svg';
|
export const FolderImgUrl = '/imgs/files/folder.svg';
|
||||||
export const HttpPluginImgUrl = '/imgs/app/httpPluginFill.svg';
|
export const HttpPluginImgUrl = '/imgs/app/httpPluginFill.svg';
|
||||||
|
|||||||
4
packages/global/common/file/image/type.d.ts
vendored
4
packages/global/common/file/image/type.d.ts
vendored
@@ -1,12 +1,8 @@
|
|||||||
import { MongoImageTypeEnum } from './constants';
|
|
||||||
|
|
||||||
export type MongoImageSchemaType = {
|
export type MongoImageSchemaType = {
|
||||||
_id: string;
|
_id: string;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
binary: Buffer;
|
binary: Buffer;
|
||||||
createTime: Date;
|
|
||||||
expiredTime?: Date;
|
expiredTime?: Date;
|
||||||
type: `${MongoImageTypeEnum}`;
|
|
||||||
|
|
||||||
metadata?: {
|
metadata?: {
|
||||||
mime?: string; // image mime type.
|
mime?: string; // image mime type.
|
||||||
|
|||||||
@@ -1,43 +1,92 @@
|
|||||||
import { UploadImgProps } from '@fastgpt/global/common/file/api';
|
import { UploadImgProps } from '@fastgpt/global/common/file/api';
|
||||||
import { imageBaseUrl } from '@fastgpt/global/common/file/image/constants';
|
import { imageBaseUrl } from '@fastgpt/global/common/file/image/constants';
|
||||||
import { MongoImage } from './schema';
|
import { MongoImage } from './schema';
|
||||||
import { ClientSession } from '../../../common/mongo';
|
import { ClientSession, Types } from '../../../common/mongo';
|
||||||
import { guessBase64ImageType } from '../utils';
|
import { guessBase64ImageType } from '../utils';
|
||||||
import { readFromSecondary } from '../../mongo/utils';
|
import { readFromSecondary } from '../../mongo/utils';
|
||||||
|
import { addHours } from 'date-fns';
|
||||||
|
|
||||||
export const maxImgSize = 1024 * 1024 * 12;
|
export const maxImgSize = 1024 * 1024 * 12;
|
||||||
const base64MimeRegex = /data:image\/([^\)]+);base64/;
|
const base64MimeRegex = /data:image\/([^\)]+);base64/;
|
||||||
export async function uploadMongoImg({
|
export async function uploadMongoImg({
|
||||||
type,
|
|
||||||
base64Img,
|
base64Img,
|
||||||
teamId,
|
teamId,
|
||||||
expiredTime,
|
|
||||||
metadata,
|
metadata,
|
||||||
shareId
|
shareId,
|
||||||
|
forever = false
|
||||||
}: UploadImgProps & {
|
}: UploadImgProps & {
|
||||||
teamId: string;
|
teamId: string;
|
||||||
|
forever?: Boolean;
|
||||||
}) {
|
}) {
|
||||||
if (base64Img.length > maxImgSize) {
|
if (base64Img.length > maxImgSize) {
|
||||||
return Promise.reject('Image too large');
|
return Promise.reject('Image too large');
|
||||||
}
|
}
|
||||||
|
|
||||||
const [base64Mime, base64Data] = base64Img.split(',');
|
const [base64Mime, base64Data] = base64Img.split(',');
|
||||||
|
// Check if mime type is valid
|
||||||
|
if (!base64MimeRegex.test(base64Mime)) {
|
||||||
|
return Promise.reject('Invalid image mime type');
|
||||||
|
}
|
||||||
|
|
||||||
const mime = `image/${base64Mime.match(base64MimeRegex)?.[1] ?? 'image/jpeg'}`;
|
const mime = `image/${base64Mime.match(base64MimeRegex)?.[1] ?? 'image/jpeg'}`;
|
||||||
const binary = Buffer.from(base64Data, 'base64');
|
const binary = Buffer.from(base64Data, 'base64');
|
||||||
const extension = mime.split('/')[1];
|
const extension = mime.split('/')[1];
|
||||||
|
|
||||||
const { _id } = await MongoImage.create({
|
const { _id } = await MongoImage.create({
|
||||||
type,
|
|
||||||
teamId,
|
teamId,
|
||||||
binary,
|
binary,
|
||||||
expiredTime,
|
|
||||||
metadata: Object.assign({ mime }, metadata),
|
metadata: Object.assign({ mime }, metadata),
|
||||||
shareId
|
shareId,
|
||||||
|
expiredTime: forever ? undefined : addHours(new Date(), 1)
|
||||||
});
|
});
|
||||||
|
|
||||||
return `${process.env.FE_DOMAIN || ''}${process.env.NEXT_PUBLIC_BASE_URL || ''}${imageBaseUrl}${String(_id)}.${extension}`;
|
return `${process.env.FE_DOMAIN || ''}${process.env.NEXT_PUBLIC_BASE_URL || ''}${imageBaseUrl}${String(_id)}.${extension}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const getIdFromPath = (path?: string) => {
|
||||||
|
if (!path) return;
|
||||||
|
|
||||||
|
const paths = path.split('/');
|
||||||
|
const name = paths[paths.length - 1];
|
||||||
|
|
||||||
|
if (!name) return;
|
||||||
|
|
||||||
|
const id = name.split('.')[0];
|
||||||
|
if (!id || !Types.ObjectId.isValid(id)) return;
|
||||||
|
|
||||||
|
return id;
|
||||||
|
};
|
||||||
|
// 删除旧的头像,新的头像去除过期时间
|
||||||
|
export const refreshSourceAvatar = async (
|
||||||
|
path?: string,
|
||||||
|
oldPath?: string,
|
||||||
|
session?: ClientSession
|
||||||
|
) => {
|
||||||
|
const newId = getIdFromPath(path);
|
||||||
|
const oldId = getIdFromPath(oldPath);
|
||||||
|
|
||||||
|
if (!newId) return;
|
||||||
|
|
||||||
|
await MongoImage.updateOne({ _id: newId }, { $unset: { expiredTime: 1 } }, { session });
|
||||||
|
|
||||||
|
if (oldId) {
|
||||||
|
await MongoImage.deleteOne({ _id: oldId }, { session });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const removeImageByPath = (path?: string, session?: ClientSession) => {
|
||||||
|
if (!path) return;
|
||||||
|
|
||||||
|
const paths = path.split('/');
|
||||||
|
const name = paths[paths.length - 1];
|
||||||
|
|
||||||
|
if (!name) return;
|
||||||
|
|
||||||
|
const id = name.split('.')[0];
|
||||||
|
if (!id || !Types.ObjectId.isValid(id)) return;
|
||||||
|
|
||||||
|
return MongoImage.deleteOne({ _id: id }, { session });
|
||||||
|
};
|
||||||
|
|
||||||
export async function readMongoImg({ id }: { id: string }) {
|
export async function readMongoImg({ id }: { id: string }) {
|
||||||
const formatId = id.replace(/\.[^/.]+$/, '');
|
const formatId = id.replace(/\.[^/.]+$/, '');
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
||||||
import { connectionMongo, getMongoModel, type Model } from '../../mongo';
|
import { connectionMongo, getMongoModel } from '../../mongo';
|
||||||
import { MongoImageSchemaType } from '@fastgpt/global/common/file/image/type.d';
|
import { MongoImageSchemaType } from '@fastgpt/global/common/file/image/type.d';
|
||||||
import { mongoImageTypeMap } from '@fastgpt/global/common/file/image/constants';
|
const { Schema } = connectionMongo;
|
||||||
const { Schema, model, models } = connectionMongo;
|
|
||||||
|
|
||||||
const ImageSchema = new Schema({
|
const ImageSchema = new Schema({
|
||||||
teamId: {
|
teamId: {
|
||||||
@@ -14,27 +13,15 @@ const ImageSchema = new Schema({
|
|||||||
type: Date,
|
type: Date,
|
||||||
default: () => new Date()
|
default: () => new Date()
|
||||||
},
|
},
|
||||||
expiredTime: {
|
expiredTime: Date,
|
||||||
type: Date
|
binary: Buffer,
|
||||||
},
|
metadata: Object
|
||||||
binary: {
|
|
||||||
type: Buffer
|
|
||||||
},
|
|
||||||
type: {
|
|
||||||
type: String,
|
|
||||||
enum: Object.keys(mongoImageTypeMap),
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
metadata: {
|
|
||||||
type: Object
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// tts expired(60 Minutes)
|
// tts expired(60 Minutes)
|
||||||
ImageSchema.index({ expiredTime: 1 }, { expireAfterSeconds: 60 * 60 });
|
ImageSchema.index({ expiredTime: 1 }, { expireAfterSeconds: 60 * 60 });
|
||||||
ImageSchema.index({ type: 1 });
|
ImageSchema.index({ type: 1 });
|
||||||
ImageSchema.index({ createTime: 1 });
|
|
||||||
// delete related img
|
// delete related img
|
||||||
ImageSchema.index({ teamId: 1, 'metadata.relatedId': 1 });
|
ImageSchema.index({ teamId: 1, 'metadata.relatedId': 1 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { uploadMongoImg } from '../image/controller';
|
import { uploadMongoImg } from '../image/controller';
|
||||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
|
||||||
import FormData from 'form-data';
|
import FormData from 'form-data';
|
||||||
|
|
||||||
import { WorkerNameEnum, runWorker } from '../../../worker/utils';
|
import { WorkerNameEnum, runWorker } from '../../../worker/utils';
|
||||||
@@ -114,10 +113,9 @@ export const readRawContentByFileBuffer = async ({
|
|||||||
if (imageList) {
|
if (imageList) {
|
||||||
await batchRun(imageList, async (item) => {
|
await batchRun(imageList, async (item) => {
|
||||||
const src = await uploadMongoImg({
|
const src = await uploadMongoImg({
|
||||||
type: MongoImageTypeEnum.collectionImage,
|
|
||||||
base64Img: `data:${item.mime};base64,${item.base64}`,
|
base64Img: `data:${item.mime};base64,${item.base64}`,
|
||||||
teamId,
|
teamId,
|
||||||
expiredTime: addHours(new Date(), 1),
|
// expiredTime: addHours(new Date(), 1),
|
||||||
metadata: {
|
metadata: {
|
||||||
...metadata,
|
...metadata,
|
||||||
mime: item.mime
|
mime: item.mime
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import { jsonRes } from '../response';
|
|||||||
// unit: times/s
|
// unit: times/s
|
||||||
// how to use?
|
// how to use?
|
||||||
// export default NextAPI(useQPSLimit(10), handler); // limit 10 times per second for a ip
|
// export default NextAPI(useQPSLimit(10), handler); // limit 10 times per second for a ip
|
||||||
export function useReqFrequencyLimit(seconds: number, limit: number) {
|
export function useReqFrequencyLimit(seconds: number, limit: number, force = false) {
|
||||||
return async (req: ApiRequestProps, res: NextApiResponse) => {
|
return async (req: ApiRequestProps, res: NextApiResponse) => {
|
||||||
const ip = requestIp.getClientIp(req);
|
const ip = requestIp.getClientIp(req);
|
||||||
if (!ip || process.env.USE_IP_LIMIT !== 'true') {
|
if (!ip || (process.env.USE_IP_LIMIT !== 'true' && !force)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@@ -25,7 +25,7 @@ export function useReqFrequencyLimit(seconds: number, limit: number) {
|
|||||||
res.status(429);
|
res.status(429);
|
||||||
jsonRes(res, {
|
jsonRes(res, {
|
||||||
code: 429,
|
code: 429,
|
||||||
message: ERROR_ENUM.tooManyRequest
|
error: ERROR_ENUM.tooManyRequest
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ export const jsonRes = <T = any>(
|
|||||||
|
|
||||||
addLog.error(`Api response error: ${url}`, ERROR_RESPONSE[errResponseKey]);
|
addLog.error(`Api response error: ${url}`, ERROR_RESPONSE[errResponseKey]);
|
||||||
|
|
||||||
|
res.status(ERROR_RESPONSE[errResponseKey].code);
|
||||||
return res.json(ERROR_RESPONSE[errResponseKey]);
|
return res.json(ERROR_RESPONSE[errResponseKey]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -97,8 +97,12 @@ export const authGroupMemberRole = async ({
|
|||||||
tmbId
|
tmbId
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
const groupMember = await MongoGroupMemberModel.findOne({ groupId, tmbId });
|
const [groupMember, tmb] = await Promise.all([
|
||||||
const tmb = await getTmbInfoByTmbId({ tmbId });
|
MongoGroupMemberModel.findOne({ groupId, tmbId }),
|
||||||
|
getTmbInfoByTmbId({ tmbId })
|
||||||
|
]);
|
||||||
|
|
||||||
|
// Team admin or role check
|
||||||
if (tmb.permission.hasManagePer || (groupMember && role.includes(groupMember.role))) {
|
if (tmb.permission.hasManagePer || (groupMember && role.includes(groupMember.role))) {
|
||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import { mongoSessionRun } from '../../../common/mongo/sessionRun';
|
|||||||
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
import { DefaultGroupName } from '@fastgpt/global/support/user/team/group/constant';
|
||||||
import { getAIApi, openaiBaseUrl } from '../../../core/ai/config';
|
import { getAIApi, openaiBaseUrl } from '../../../core/ai/config';
|
||||||
import { createRootOrg } from '../../permission/org/controllers';
|
import { createRootOrg } from '../../permission/org/controllers';
|
||||||
|
import { refreshSourceAvatar } from '../../../common/file/image/controller';
|
||||||
|
|
||||||
async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemType> {
|
async function getTeamMember(match: Record<string, any>): Promise<TeamTmbItemType> {
|
||||||
const tmb = await MongoTeamMember.findOne(match).populate<{ team: TeamSchema }>('team').lean();
|
const tmb = await MongoTeamMember.findOne(match).populate<{ team: TeamSchema }>('team').lean();
|
||||||
@@ -218,7 +219,8 @@ export async function updateTeam({
|
|||||||
return obj;
|
return obj;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
await MongoTeam.findByIdAndUpdate(
|
// This is where we get the old team
|
||||||
|
const team = await MongoTeam.findByIdAndUpdate(
|
||||||
teamId,
|
teamId,
|
||||||
{
|
{
|
||||||
$set: {
|
$set: {
|
||||||
@@ -244,6 +246,8 @@ export async function updateTeam({
|
|||||||
},
|
},
|
||||||
{ session }
|
{ session }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await refreshSourceAvatar(avatar, team?.avatar, session);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,8 +49,8 @@
|
|||||||
"package_expiry_time": "Expired",
|
"package_expiry_time": "Expired",
|
||||||
"package_usage_rules": "Package usage rules: The system will give priority to using more advanced packages, and the original unused packages will take effect later.",
|
"package_usage_rules": "Package usage rules: The system will give priority to using more advanced packages, and the original unused packages will take effect later.",
|
||||||
"password": "Password",
|
"password": "Password",
|
||||||
"password_length_error": "Password must be at least 4 characters and at most 60 characters",
|
|
||||||
"password_mismatch": "Password Inconsistency: Two passwords are inconsistent",
|
"password_mismatch": "Password Inconsistency: Two passwords are inconsistent",
|
||||||
|
"password_tip": "Password must be at least 6 characters long and contain at least two combinations: numbers, letters, or special characters",
|
||||||
"password_update_error": "Exception when changing password",
|
"password_update_error": "Exception when changing password",
|
||||||
"password_update_success": "Password changed successfully",
|
"password_update_success": "Password changed successfully",
|
||||||
"pending_usage": "To be used",
|
"pending_usage": "To be used",
|
||||||
|
|||||||
@@ -74,24 +74,24 @@
|
|||||||
"code_error.team_error.ai_points_not_enough": "Insufficient AI Points",
|
"code_error.team_error.ai_points_not_enough": "Insufficient AI Points",
|
||||||
"code_error.team_error.app_amount_not_enough": "Application Limit Reached",
|
"code_error.team_error.app_amount_not_enough": "Application Limit Reached",
|
||||||
"code_error.team_error.cannot_delete_default_group": "Cannot delete default group",
|
"code_error.team_error.cannot_delete_default_group": "Cannot delete default group",
|
||||||
|
"code_error.team_error.cannot_delete_non_empty_org": "Cannot delete non-empty organization",
|
||||||
|
"code_error.team_error.cannot_modify_root_org": "Cannot modify root organization",
|
||||||
|
"code_error.team_error.cannot_move_to_sub_path": "Cannot move to same or subdirectory",
|
||||||
"code_error.team_error.dataset_amount_not_enough": "Dataset Limit Reached",
|
"code_error.team_error.dataset_amount_not_enough": "Dataset Limit Reached",
|
||||||
"code_error.team_error.dataset_size_not_enough": "Insufficient Dataset Capacity, Please Expand",
|
"code_error.team_error.dataset_size_not_enough": "Insufficient Dataset Capacity, Please Expand",
|
||||||
"code_error.team_error.group_name_duplicate": "Duplicate group name",
|
"code_error.team_error.group_name_duplicate": "Duplicate group name",
|
||||||
"code_error.team_error.group_name_empty": "Group name cannot be empty",
|
"code_error.team_error.group_name_empty": "Group name cannot be empty",
|
||||||
"code_error.team_error.group_not_exist": "Group does not exist",
|
"code_error.team_error.group_not_exist": "Group does not exist",
|
||||||
|
"code_error.team_error.org_member_duplicated": "Duplicate organization member",
|
||||||
|
"code_error.team_error.org_member_not_exist": "Organization member does not exist",
|
||||||
|
"code_error.team_error.org_not_exist": "Organization does not exist",
|
||||||
|
"code_error.team_error.org_parent_not_exist": "Parent organization does not exist",
|
||||||
"code_error.team_error.over_size": "error.team.overSize",
|
"code_error.team_error.over_size": "error.team.overSize",
|
||||||
"code_error.team_error.plugin_amount_not_enough": "Plugin Limit Reached",
|
"code_error.team_error.plugin_amount_not_enough": "Plugin Limit Reached",
|
||||||
"code_error.team_error.re_rank_not_enough": "Unauthorized to Use Re-Rank",
|
"code_error.team_error.re_rank_not_enough": "Unauthorized to Use Re-Rank",
|
||||||
"code_error.team_error.un_auth": "Unauthorized to Operate This Team",
|
"code_error.team_error.un_auth": "Unauthorized to Operate This Team",
|
||||||
"code_error.team_error.user_not_active": "The user did not accept or has left the team",
|
"code_error.team_error.user_not_active": "The user did not accept or has left the team",
|
||||||
"code_error.team_error.website_sync_not_enough": "Unauthorized to Use Website Sync",
|
"code_error.team_error.website_sync_not_enough": "Unauthorized to Use Website Sync",
|
||||||
"code_error.team_error.org_member_not_exist": "Organization member does not exist",
|
|
||||||
"code_error.team_error.org_member_duplicated": "Duplicate organization member",
|
|
||||||
"code_error.team_error.org_not_exist": "Organization does not exist",
|
|
||||||
"code_error.team_error.org_parent_not_exist": "Parent organization does not exist",
|
|
||||||
"code_error.team_error.cannot_move_to_sub_path": "Cannot move to same or subdirectory",
|
|
||||||
"code_error.team_error.cannot_modify_root_org": "Cannot modify root organization",
|
|
||||||
"code_error.team_error.cannot_delete_non_empty_org": "Cannot delete non-empty organization",
|
|
||||||
"code_error.token_error_code.403": "Invalid Login Status, Please Re-login",
|
"code_error.token_error_code.403": "Invalid Login Status, Please Re-login",
|
||||||
"code_error.user_error.balance_not_enough": "Insufficient Account Balance",
|
"code_error.user_error.balance_not_enough": "Insufficient Account Balance",
|
||||||
"code_error.user_error.bin_visitor": "Identity Verification Failed",
|
"code_error.user_error.bin_visitor": "Identity Verification Failed",
|
||||||
@@ -880,9 +880,11 @@
|
|||||||
"error.code_error": "Verification code error",
|
"error.code_error": "Verification code error",
|
||||||
"error.fileNotFound": "File not found~",
|
"error.fileNotFound": "File not found~",
|
||||||
"error.inheritPermissionError": "Inherit permission Error",
|
"error.inheritPermissionError": "Inherit permission Error",
|
||||||
|
"error.invalid_params": "Invalid parameter",
|
||||||
"error.missingParams": "Insufficient parameters",
|
"error.missingParams": "Insufficient parameters",
|
||||||
"error.too_many_request": "Too many request",
|
"error.too_many_request": "Too many request",
|
||||||
"error.upload_file_error_filename": "{{name}} Upload Failed",
|
"error.upload_file_error_filename": "{{name}} Upload Failed",
|
||||||
|
"error.upload_image_error": "File upload failed",
|
||||||
"error.username_empty": "Account cannot be empty",
|
"error.username_empty": "Account cannot be empty",
|
||||||
"extraction_results": "Extraction Results",
|
"extraction_results": "Extraction Results",
|
||||||
"field_name": "Field Name",
|
"field_name": "Field Name",
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
{
|
{
|
||||||
"Chinese_ip_tip": "It is detected that you are a mainland Chinese IP, click to jump to visit the mainland China version.",
|
"Chinese_ip_tip": "It is detected that you are a mainland Chinese IP, click to jump to visit the mainland China version.",
|
||||||
"Login": "Login",
|
"Login": "Login",
|
||||||
|
"agree": "agree",
|
||||||
|
"cookies_tip": " This website uses cookies to provide a better service experience. By continuing to use the site, you agree to our Cookie Policy.",
|
||||||
"forget_password": "Find password",
|
"forget_password": "Find password",
|
||||||
"login_failed": "Login failed",
|
"login_failed": "Login failed",
|
||||||
"login_success": "Login successful",
|
"login_success": "Login successful",
|
||||||
"no_remind": "Don't remind again",
|
"no_remind": "Don't remind again",
|
||||||
"password_condition": "Password maximum 60 characters",
|
"password_condition": "Password maximum 60 characters",
|
||||||
|
"password_tip": "Password must be at least 6 characters long and contain at least two combinations: numbers, letters, or special characters",
|
||||||
"policy_tip": "By useing, you agree to our",
|
"policy_tip": "By useing, you agree to our",
|
||||||
"privacy": "Privacy policy",
|
"privacy": "Privacy policy",
|
||||||
|
"privacy_policy": "Privacy Policy",
|
||||||
"redirect": "Jump",
|
"redirect": "Jump",
|
||||||
"register": "Register",
|
"register": "Register",
|
||||||
"root_password_placeholder": "The root user password is the value of the environment variable DEFAULT_ROOT_PSW",
|
"root_password_placeholder": "The root user password is the value of the environment variable DEFAULT_ROOT_PSW",
|
||||||
"terms": "Terms",
|
"terms": "Terms",
|
||||||
"use_root_login": "Log in as root user",
|
"use_root_login": "Log in as root user"
|
||||||
"agree": "agree",
|
|
||||||
"cookies_tip": " This website uses cookies to provide a better service experience. By continuing to use the site, you agree to our Cookie Policy.",
|
|
||||||
"privacy_policy": "Privacy Policy"
|
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,6 @@
|
|||||||
"password.email_phone_void": "Email/Phone Number Cannot Be Empty",
|
"password.email_phone_void": "Email/Phone Number Cannot Be Empty",
|
||||||
"password.get_code": "Get Verification Code",
|
"password.get_code": "Get Verification Code",
|
||||||
"password.get_code_again": "Get Again in s",
|
"password.get_code_again": "Get Again in s",
|
||||||
"password.new_password": "New Password (4-20 characters)",
|
|
||||||
"password.not_match": "Passwords Do Not Match",
|
"password.not_match": "Passwords Do Not Match",
|
||||||
"password.password_condition": "Password must be between 4 and 20 characters",
|
"password.password_condition": "Password must be between 4 and 20 characters",
|
||||||
"password.password_required": "Password Cannot Be Empty",
|
"password.password_required": "Password Cannot Be Empty",
|
||||||
|
|||||||
@@ -47,8 +47,8 @@
|
|||||||
"package_expiry_time": "套餐到期时间",
|
"package_expiry_time": "套餐到期时间",
|
||||||
"package_usage_rules": "套餐使用规则:系统优先使用更高级的套餐,原未用完的套餐将延后生效",
|
"package_usage_rules": "套餐使用规则:系统优先使用更高级的套餐,原未用完的套餐将延后生效",
|
||||||
"password": "密码",
|
"password": "密码",
|
||||||
"password_length_error": "密码最少 4 位最多 60 位",
|
|
||||||
"password_mismatch": "密码不一致: 两次密码不一致",
|
"password_mismatch": "密码不一致: 两次密码不一致",
|
||||||
|
"password_tip": "密码至少 6 位,且至少包含两种组合:数字、字母或特殊字符",
|
||||||
"password_update_error": "修改密码异常",
|
"password_update_error": "修改密码异常",
|
||||||
"password_update_success": "修改密码成功",
|
"password_update_success": "修改密码成功",
|
||||||
"pending_usage": "待使用",
|
"pending_usage": "待使用",
|
||||||
|
|||||||
@@ -78,24 +78,24 @@
|
|||||||
"code_error.team_error.ai_points_not_enough": "",
|
"code_error.team_error.ai_points_not_enough": "",
|
||||||
"code_error.team_error.app_amount_not_enough": "应用数量已达上限~",
|
"code_error.team_error.app_amount_not_enough": "应用数量已达上限~",
|
||||||
"code_error.team_error.cannot_delete_default_group": "不能删除默认群组",
|
"code_error.team_error.cannot_delete_default_group": "不能删除默认群组",
|
||||||
|
"code_error.team_error.cannot_delete_non_empty_org": "不能删除非空部门",
|
||||||
|
"code_error.team_error.cannot_modify_root_org": "不能修改根部门",
|
||||||
|
"code_error.team_error.cannot_move_to_sub_path": "不能移动到相同或子目录",
|
||||||
"code_error.team_error.dataset_amount_not_enough": "知识库数量已达上限~",
|
"code_error.team_error.dataset_amount_not_enough": "知识库数量已达上限~",
|
||||||
"code_error.team_error.dataset_size_not_enough": "知识库容量不足,请先扩容~",
|
"code_error.team_error.dataset_size_not_enough": "知识库容量不足,请先扩容~",
|
||||||
"code_error.team_error.group_name_duplicate": "群组名称重复",
|
"code_error.team_error.group_name_duplicate": "群组名称重复",
|
||||||
"code_error.team_error.group_name_empty": "群组名称不能为空",
|
"code_error.team_error.group_name_empty": "群组名称不能为空",
|
||||||
"code_error.team_error.group_not_exist": "群组不存在",
|
"code_error.team_error.group_not_exist": "群组不存在",
|
||||||
|
"code_error.team_error.org_member_duplicated": "重复的部门成员",
|
||||||
|
"code_error.team_error.org_member_not_exist": "部门成员不存在",
|
||||||
|
"code_error.team_error.org_not_exist": "部门不存在",
|
||||||
|
"code_error.team_error.org_parent_not_exist": "父部门不存在",
|
||||||
"code_error.team_error.over_size": "error.team.overSize",
|
"code_error.team_error.over_size": "error.team.overSize",
|
||||||
"code_error.team_error.plugin_amount_not_enough": "插件数量已达上限~",
|
"code_error.team_error.plugin_amount_not_enough": "插件数量已达上限~",
|
||||||
"code_error.team_error.re_rank_not_enough": "无权使用检索重排~",
|
"code_error.team_error.re_rank_not_enough": "无权使用检索重排~",
|
||||||
"code_error.team_error.un_auth": "无权操作该团队",
|
"code_error.team_error.un_auth": "无权操作该团队",
|
||||||
"code_error.team_error.user_not_active": "用户未接受或已离开团队",
|
"code_error.team_error.user_not_active": "用户未接受或已离开团队",
|
||||||
"code_error.team_error.website_sync_not_enough": "无权使用Web站点同步~",
|
"code_error.team_error.website_sync_not_enough": "无权使用Web站点同步~",
|
||||||
"code_error.team_error.org_member_not_exist": "部门成员不存在",
|
|
||||||
"code_error.team_error.org_member_duplicated": "重复的部门成员",
|
|
||||||
"code_error.team_error.org_not_exist": "部门不存在",
|
|
||||||
"code_error.team_error.org_parent_not_exist": "父部门不存在",
|
|
||||||
"code_error.team_error.cannot_move_to_sub_path": "不能移动到相同或子目录",
|
|
||||||
"code_error.team_error.cannot_modify_root_org": "不能修改根部门",
|
|
||||||
"code_error.team_error.cannot_delete_non_empty_org": "不能删除非空部门",
|
|
||||||
"code_error.token_error_code.403": "登录状态无效,请重新登录",
|
"code_error.token_error_code.403": "登录状态无效,请重新登录",
|
||||||
"code_error.user_error.balance_not_enough": "账号余额不足~",
|
"code_error.user_error.balance_not_enough": "账号余额不足~",
|
||||||
"code_error.user_error.bin_visitor": "您的身份校验未通过",
|
"code_error.user_error.bin_visitor": "您的身份校验未通过",
|
||||||
@@ -883,9 +883,11 @@
|
|||||||
"error.code_error": "验证码错误",
|
"error.code_error": "验证码错误",
|
||||||
"error.fileNotFound": "文件找不到了~",
|
"error.fileNotFound": "文件找不到了~",
|
||||||
"error.inheritPermissionError": "权限继承错误",
|
"error.inheritPermissionError": "权限继承错误",
|
||||||
|
"error.invalid_params": "参数无效",
|
||||||
"error.missingParams": "参数缺失",
|
"error.missingParams": "参数缺失",
|
||||||
"error.too_many_request": "请求太频繁了,请稍后重试",
|
"error.too_many_request": "请求太频繁了,请稍后重试",
|
||||||
"error.upload_file_error_filename": "{{name}} 上传失败",
|
"error.upload_file_error_filename": "{{name}} 上传失败",
|
||||||
|
"error.upload_image_error": "上传文件失败",
|
||||||
"error.username_empty": "账号不能为空",
|
"error.username_empty": "账号不能为空",
|
||||||
"extraction_results": "提取结果",
|
"extraction_results": "提取结果",
|
||||||
"field_name": "字段名",
|
"field_name": "字段名",
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
{
|
{
|
||||||
|
"Chinese_ip_tip": "检测到您是中国大陆 IP,点击跳转访问中国大陆版。",
|
||||||
"Login": "登录",
|
"Login": "登录",
|
||||||
|
"agree": "同意",
|
||||||
|
"cookies_tip": "本网站使用 cookies 提供更好的服务体验。继续使用即表示您同意我们的 Cookie 政策。",
|
||||||
"forget_password": "忘记密码?",
|
"forget_password": "忘记密码?",
|
||||||
"login_failed": "登录异常",
|
"login_failed": "登录异常",
|
||||||
"login_success": "登录成功",
|
"login_success": "登录成功",
|
||||||
|
"no_remind": "不再提醒",
|
||||||
"password_condition": "密码最多 60 位",
|
"password_condition": "密码最多 60 位",
|
||||||
|
"password_tip": "密码至少 6 位,且至少包含两种组合:数字、字母或特殊字符",
|
||||||
"policy_tip": "使用即代表你同意我们的",
|
"policy_tip": "使用即代表你同意我们的",
|
||||||
"privacy": "隐私协议",
|
"privacy": "隐私协议",
|
||||||
|
"privacy_policy": "隐私政策",
|
||||||
|
"redirect": "跳转",
|
||||||
"register": "注册账号",
|
"register": "注册账号",
|
||||||
"root_password_placeholder": "root 用户密码为环境变量 DEFAULT_ROOT_PSW 的值",
|
"root_password_placeholder": "root 用户密码为环境变量 DEFAULT_ROOT_PSW 的值",
|
||||||
"terms": "服务协议",
|
"terms": "服务协议",
|
||||||
"use_root_login": "使用 root 用户登录",
|
"use_root_login": "使用 root 用户登录"
|
||||||
"redirect": "跳转",
|
|
||||||
"no_remind": "不再提醒",
|
|
||||||
"Chinese_ip_tip": "检测到您是中国大陆 IP,点击跳转访问中国大陆版。",
|
|
||||||
"agree": "同意",
|
|
||||||
"cookies_tip": "本网站使用 cookies 提供更好的服务体验。继续使用即表示您同意我们的 Cookie 政策。",
|
|
||||||
"privacy_policy": "隐私政策"
|
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,6 @@
|
|||||||
"password.email_phone_void": "邮箱/手机号不能为空",
|
"password.email_phone_void": "邮箱/手机号不能为空",
|
||||||
"password.get_code": "获取验证码",
|
"password.get_code": "获取验证码",
|
||||||
"password.get_code_again": "s后重新获取",
|
"password.get_code_again": "s后重新获取",
|
||||||
"password.new_password": "新密码(4~20位)",
|
|
||||||
"password.not_match": "两次密码不一致",
|
"password.not_match": "两次密码不一致",
|
||||||
"password.password_condition": "密码最少 4 位最多 20 位",
|
"password.password_condition": "密码最少 4 位最多 20 位",
|
||||||
"password.password_required": "密码不能为空",
|
"password.password_required": "密码不能为空",
|
||||||
|
|||||||
@@ -49,8 +49,8 @@
|
|||||||
"package_expiry_time": "套餐到期時間",
|
"package_expiry_time": "套餐到期時間",
|
||||||
"package_usage_rules": "套餐使用規則:系統優先使用更進階的套餐,原未用完的套餐將延遲生效",
|
"package_usage_rules": "套餐使用規則:系統優先使用更進階的套餐,原未用完的套餐將延遲生效",
|
||||||
"password": "密碼",
|
"password": "密碼",
|
||||||
"password_length_error": "密碼最少 4 位最多 60 位",
|
|
||||||
"password_mismatch": "密碼不一致: 兩次密碼不一致",
|
"password_mismatch": "密碼不一致: 兩次密碼不一致",
|
||||||
|
"password_tip": "密碼至少 6 位,且至少包含兩種組合:數字、字母或特殊字符",
|
||||||
"password_update_error": "修改密碼異常",
|
"password_update_error": "修改密碼異常",
|
||||||
"password_update_success": "修改密碼成功",
|
"password_update_success": "修改密碼成功",
|
||||||
"pending_usage": "待使用",
|
"pending_usage": "待使用",
|
||||||
|
|||||||
@@ -74,24 +74,24 @@
|
|||||||
"code_error.team_error.ai_points_not_enough": "AI 點數不足",
|
"code_error.team_error.ai_points_not_enough": "AI 點數不足",
|
||||||
"code_error.team_error.app_amount_not_enough": "已達應用程式數量上限",
|
"code_error.team_error.app_amount_not_enough": "已達應用程式數量上限",
|
||||||
"code_error.team_error.cannot_delete_default_group": "無法刪除預設群組",
|
"code_error.team_error.cannot_delete_default_group": "無法刪除預設群組",
|
||||||
|
"code_error.team_error.cannot_delete_non_empty_org": "無法刪除非空組織",
|
||||||
|
"code_error.team_error.cannot_modify_root_org": "無法修改根組織",
|
||||||
|
"code_error.team_error.cannot_move_to_sub_path": "無法移動到相同或子目錄",
|
||||||
"code_error.team_error.dataset_amount_not_enough": "已達知識庫數量上限",
|
"code_error.team_error.dataset_amount_not_enough": "已達知識庫數量上限",
|
||||||
"code_error.team_error.dataset_size_not_enough": "知識庫容量不足,請先擴充容量",
|
"code_error.team_error.dataset_size_not_enough": "知識庫容量不足,請先擴充容量",
|
||||||
"code_error.team_error.group_name_duplicate": "群組名稱重複",
|
"code_error.team_error.group_name_duplicate": "群組名稱重複",
|
||||||
"code_error.team_error.group_name_empty": "群組名稱不能為空",
|
"code_error.team_error.group_name_empty": "群組名稱不能為空",
|
||||||
"code_error.team_error.group_not_exist": "群組不存在",
|
"code_error.team_error.group_not_exist": "群組不存在",
|
||||||
|
"code_error.team_error.org_member_duplicated": "重複的組織成員",
|
||||||
|
"code_error.team_error.org_member_not_exist": "組織成員不存在",
|
||||||
|
"code_error.team_error.org_not_exist": "組織不存在",
|
||||||
|
"code_error.team_error.org_parent_not_exist": "父組織不存在",
|
||||||
"code_error.team_error.over_size": "error.team.overSize",
|
"code_error.team_error.over_size": "error.team.overSize",
|
||||||
"code_error.team_error.plugin_amount_not_enough": "已達外掛程式數量上限",
|
"code_error.team_error.plugin_amount_not_enough": "已達外掛程式數量上限",
|
||||||
"code_error.team_error.re_rank_not_enough": "無權使用結果重新排名",
|
"code_error.team_error.re_rank_not_enough": "無權使用結果重新排名",
|
||||||
"code_error.team_error.un_auth": "無權操作此團隊",
|
"code_error.team_error.un_auth": "無權操作此團隊",
|
||||||
"code_error.team_error.user_not_active": "使用者未接受或已離開團隊",
|
"code_error.team_error.user_not_active": "使用者未接受或已離開團隊",
|
||||||
"code_error.team_error.website_sync_not_enough": "無權使用網站同步",
|
"code_error.team_error.website_sync_not_enough": "無權使用網站同步",
|
||||||
"code_error.team_error.org_member_not_exist": "組織成員不存在",
|
|
||||||
"code_error.team_error.org_member_duplicated": "重複的組織成員",
|
|
||||||
"code_error.team_error.org_not_exist": "組織不存在",
|
|
||||||
"code_error.team_error.org_parent_not_exist": "父組織不存在",
|
|
||||||
"code_error.team_error.cannot_move_to_sub_path": "無法移動到相同或子目錄",
|
|
||||||
"code_error.team_error.cannot_modify_root_org": "無法修改根組織",
|
|
||||||
"code_error.team_error.cannot_delete_non_empty_org": "無法刪除非空組織",
|
|
||||||
"code_error.token_error_code.403": "登入狀態無效,請重新登入",
|
"code_error.token_error_code.403": "登入狀態無效,請重新登入",
|
||||||
"code_error.user_error.balance_not_enough": "帳戶餘額不足",
|
"code_error.user_error.balance_not_enough": "帳戶餘額不足",
|
||||||
"code_error.user_error.bin_visitor": "身份驗證未通過",
|
"code_error.user_error.bin_visitor": "身份驗證未通過",
|
||||||
@@ -881,9 +881,11 @@
|
|||||||
"error.code_error": "驗證碼錯誤",
|
"error.code_error": "驗證碼錯誤",
|
||||||
"error.fileNotFound": "找不到檔案",
|
"error.fileNotFound": "找不到檔案",
|
||||||
"error.inheritPermissionError": "繼承權限錯誤",
|
"error.inheritPermissionError": "繼承權限錯誤",
|
||||||
|
"error.invalid_params": "參數無效",
|
||||||
"error.missingParams": "參數不足",
|
"error.missingParams": "參數不足",
|
||||||
"error.too_many_request": "請求太頻繁了,請稍後重試",
|
"error.too_many_request": "請求太頻繁了,請稍後重試",
|
||||||
"error.upload_file_error_filename": "{{name}} 上傳失敗",
|
"error.upload_file_error_filename": "{{name}} 上傳失敗",
|
||||||
|
"error.upload_image_error": "上傳文件失敗",
|
||||||
"error.username_empty": "帳號不能為空",
|
"error.username_empty": "帳號不能為空",
|
||||||
"extraction_results": "提取結果",
|
"extraction_results": "提取結果",
|
||||||
"field_name": "欄位名稱",
|
"field_name": "欄位名稱",
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
{
|
{
|
||||||
"Chinese_ip_tip": "偵測到您使用中國大陸 IP,點選這裡前往中國大陸版本。",
|
"Chinese_ip_tip": "偵測到您使用中國大陸 IP,點選這裡前往中國大陸版本。",
|
||||||
"Login": "登入",
|
"Login": "登入",
|
||||||
|
"agree": "同意",
|
||||||
|
"cookies_tip": "本網站使用 cookies 提供更好的服務體驗。繼續使用即表示您同意我們的 Cookie 政策。",
|
||||||
"forget_password": "忘記密碼?",
|
"forget_password": "忘記密碼?",
|
||||||
"login_failed": "登入失敗",
|
"login_failed": "登入失敗",
|
||||||
"login_success": "登入成功",
|
"login_success": "登入成功",
|
||||||
"no_remind": "不再提醒",
|
"no_remind": "不再提醒",
|
||||||
"password_condition": "密碼最多 60 個字元",
|
"password_condition": "密碼最多 60 個字元",
|
||||||
|
"password_tip": "密碼至少 6 位,且至少包含兩種組合:數字、字母或特殊字符",
|
||||||
"policy_tip": "使用即代表您同意我們的",
|
"policy_tip": "使用即代表您同意我們的",
|
||||||
"privacy": "隱私權政策",
|
"privacy": "隱私權政策",
|
||||||
|
"privacy_policy": "隱私權政策",
|
||||||
"redirect": "跳轉",
|
"redirect": "跳轉",
|
||||||
"register": "註冊帳號",
|
"register": "註冊帳號",
|
||||||
"root_password_placeholder": "root 使用者密碼為環境變數 DEFAULT_ROOT_PSW 的值",
|
"root_password_placeholder": "root 使用者密碼為環境變數 DEFAULT_ROOT_PSW 的值",
|
||||||
"terms": "服務條款",
|
"terms": "服務條款",
|
||||||
"use_root_login": "使用 root 使用者登入",
|
"use_root_login": "使用 root 使用者登入"
|
||||||
"agree": "同意",
|
|
||||||
"cookies_tip": "本網站使用 cookies 提供更好的服務體驗。繼續使用即表示您同意我們的 Cookie 政策。",
|
|
||||||
"privacy_policy": "隱私權政策"
|
|
||||||
}
|
}
|
||||||
@@ -40,7 +40,6 @@
|
|||||||
"password.email_phone_void": "電子郵件/手機號碼不能空白",
|
"password.email_phone_void": "電子郵件/手機號碼不能空白",
|
||||||
"password.get_code": "取得驗證碼",
|
"password.get_code": "取得驗證碼",
|
||||||
"password.get_code_again": "秒後重新取得",
|
"password.get_code_again": "秒後重新取得",
|
||||||
"password.new_password": "新密碼(4 至 20 字元)",
|
|
||||||
"password.not_match": "兩次輸入的密碼不相符",
|
"password.not_match": "兩次輸入的密碼不相符",
|
||||||
"password.password_condition": "密碼長度需介於 4 至 20 字元之間",
|
"password.password_condition": "密碼長度需介於 4 至 20 字元之間",
|
||||||
"password.password_required": "密碼不能空白",
|
"password.password_required": "密碼不能空白",
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
import { ModalFooter, ModalBody, Input, Button, Box, Textarea, HStack } from '@chakra-ui/react';
|
import { ModalFooter, ModalBody, Input, Button, Box, Textarea, HStack } from '@chakra-ui/react';
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal/index';
|
import MyModal from '@fastgpt/web/components/common/MyModal/index';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
|
||||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
|
||||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
|
||||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
|
||||||
|
|
||||||
export type EditResourceInfoFormType = {
|
export type EditResourceInfoFormType = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -31,7 +27,6 @@ const EditResourceModal = ({
|
|||||||
onEdit: (data: EditResourceInfoFormType) => any;
|
onEdit: (data: EditResourceInfoFormType) => any;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { toast } = useToast();
|
|
||||||
const { register, watch, setValue, handleSubmit } = useForm<EditResourceInfoFormType>({
|
const { register, watch, setValue, handleSubmit } = useForm<EditResourceInfoFormType>({
|
||||||
defaultValues: defaultForm
|
defaultValues: defaultForm
|
||||||
});
|
});
|
||||||
@@ -46,31 +41,14 @@ const EditResourceModal = ({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
const {
|
||||||
|
File,
|
||||||
|
onOpen: onOpenSelectFile,
|
||||||
|
onSelectImage
|
||||||
|
} = useSelectFile({
|
||||||
fileType: '.jpg,.png',
|
fileType: '.jpg,.png',
|
||||||
multiple: false
|
multiple: false
|
||||||
});
|
});
|
||||||
const onSelectFile = useCallback(
|
|
||||||
async (e: File[]) => {
|
|
||||||
const file = e[0];
|
|
||||||
if (!file) return;
|
|
||||||
try {
|
|
||||||
const src = await compressImgFileAndUpload({
|
|
||||||
type: MongoImageTypeEnum.appAvatar,
|
|
||||||
file,
|
|
||||||
maxW: 300,
|
|
||||||
maxH: 300
|
|
||||||
});
|
|
||||||
setValue('avatar', src);
|
|
||||||
} catch (err: any) {
|
|
||||||
toast({
|
|
||||||
title: getErrText(err, t('common:common.error.Select avatar failed')),
|
|
||||||
status: 'warning'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setValue, t, toast]
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyModal isOpen onClose={onClose} iconSrc={avatar} title={title}>
|
<MyModal isOpen onClose={onClose} iconSrc={avatar} title={title}>
|
||||||
@@ -108,7 +86,15 @@ const EditResourceModal = ({
|
|||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
|
||||||
<File onSelect={onSelectFile} />
|
<File
|
||||||
|
onSelect={(e) =>
|
||||||
|
onSelectImage(e, {
|
||||||
|
maxH: 300,
|
||||||
|
maxW: 300,
|
||||||
|
callback: (e) => setValue('avatar', e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
</MyModal>
|
</MyModal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,8 +3,10 @@ import { ModalBody, Box, Flex, Input, ModalFooter, Button } from '@chakra-ui/rea
|
|||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { updatePasswordByOld } from '@/web/support/user/api';
|
import { updatePasswordByOld } from '@/web/support/user/api';
|
||||||
|
import { PasswordRule } from '@/web/support/user/login/constants';
|
||||||
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
|
|
||||||
type FormType = {
|
type FormType = {
|
||||||
oldPsw: string;
|
oldPsw: string;
|
||||||
@@ -14,7 +16,9 @@ type FormType = {
|
|||||||
|
|
||||||
const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { register, handleSubmit } = useForm<FormType>({
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const { register, handleSubmit, getValues } = useForm<FormType>({
|
||||||
defaultValues: {
|
defaultValues: {
|
||||||
oldPsw: '',
|
oldPsw: '',
|
||||||
newPsw: '',
|
newPsw: '',
|
||||||
@@ -22,19 +26,25 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const { mutate: onSubmit, isLoading } = useRequest({
|
const { runAsync: onSubmit, loading: isLoading } = useRequest2(updatePasswordByOld, {
|
||||||
mutationFn: (data: FormType) => {
|
|
||||||
if (data.newPsw !== data.confirmPsw) {
|
|
||||||
return Promise.reject(t('account_info:password_mismatch'));
|
|
||||||
}
|
|
||||||
return updatePasswordByOld(data);
|
|
||||||
},
|
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
onClose();
|
onClose();
|
||||||
},
|
},
|
||||||
successToast: t('account_info:password_update_success'),
|
successToast: t('account_info:password_update_success'),
|
||||||
errorToast: t('account_info:password_update_error')
|
errorToast: t('account_info:password_update_error')
|
||||||
});
|
});
|
||||||
|
const onSubmitErr = (err: Record<string, any>) => {
|
||||||
|
const val = Object.values(err)[0];
|
||||||
|
if (!val) return;
|
||||||
|
if (val.message) {
|
||||||
|
toast({
|
||||||
|
status: 'warning',
|
||||||
|
title: val.message,
|
||||||
|
duration: 3000,
|
||||||
|
isClosable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyModal
|
<MyModal
|
||||||
@@ -45,34 +55,39 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
>
|
>
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<Flex alignItems={'center'}>
|
<Flex alignItems={'center'}>
|
||||||
<Box flex={'0 0 70px'}>{t('account_info:old_password') + ':'}</Box>
|
<Box flex={'0 0 70px'} fontSize={'sm'}>
|
||||||
|
{t('account_info:old_password') + ':'}
|
||||||
|
</Box>
|
||||||
<Input flex={1} type={'password'} {...register('oldPsw', { required: true })}></Input>
|
<Input flex={1} type={'password'} {...register('oldPsw', { required: true })}></Input>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex alignItems={'center'} mt={5}>
|
<Flex alignItems={'center'} mt={5}>
|
||||||
<Box flex={'0 0 70px'}>{t('account_info:new_password') + ':'}</Box>
|
<Box flex={'0 0 70px'} fontSize={'sm'}>
|
||||||
|
{t('account_info:new_password') + ':'}
|
||||||
|
</Box>
|
||||||
<Input
|
<Input
|
||||||
flex={1}
|
flex={1}
|
||||||
type={'password'}
|
type={'password'}
|
||||||
|
placeholder={t('account_info:password_tip')}
|
||||||
{...register('newPsw', {
|
{...register('newPsw', {
|
||||||
required: true,
|
required: true,
|
||||||
maxLength: {
|
pattern: {
|
||||||
value: 60,
|
value: PasswordRule,
|
||||||
message: t('account_info:password_length_error')
|
message: t('account_info:password_tip')
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
></Input>
|
></Input>
|
||||||
</Flex>
|
</Flex>
|
||||||
<Flex alignItems={'center'} mt={5}>
|
<Flex alignItems={'center'} mt={5}>
|
||||||
<Box flex={'0 0 70px'}>{t('account_info:confirm_password') + ':'}</Box>
|
<Box flex={'0 0 70px'} fontSize={'sm'}>
|
||||||
|
{t('account_info:confirm_password') + ':'}
|
||||||
|
</Box>
|
||||||
<Input
|
<Input
|
||||||
flex={1}
|
flex={1}
|
||||||
type={'password'}
|
type={'password'}
|
||||||
|
placeholder={t('user:password.confirm')}
|
||||||
{...register('confirmPsw', {
|
{...register('confirmPsw', {
|
||||||
required: true,
|
required: true,
|
||||||
maxLength: {
|
validate: (val) => (getValues('newPsw') === val ? true : t('user:password.not_match'))
|
||||||
value: 60,
|
|
||||||
message: t('account_info:password_length_error')
|
|
||||||
}
|
|
||||||
})}
|
})}
|
||||||
></Input>
|
></Input>
|
||||||
</Flex>
|
</Flex>
|
||||||
@@ -81,7 +96,7 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
|
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
|
||||||
{t('account_info:cancel')}
|
{t('account_info:cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}>
|
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data), onSubmitErr)}>
|
||||||
{t('account_info:confirm')}
|
{t('account_info:confirm')}
|
||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import type { UserType } from '@fastgpt/global/support/user/type.d';
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
@@ -29,7 +28,6 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
|||||||
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
|
import { formatStorePrice2Read } from '@fastgpt/global/support/wallet/usage/tools';
|
||||||
import { putUpdateMemberName } from '@/web/support/user/team/api';
|
import { putUpdateMemberName } from '@/web/support/user/team/api';
|
||||||
import { getDocPath } from '@/web/common/system/doc';
|
import { getDocPath } from '@/web/common/system/doc';
|
||||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
|
||||||
import {
|
import {
|
||||||
StandardSubLevelEnum,
|
StandardSubLevelEnum,
|
||||||
standardSubLevelMap
|
standardSubLevelMap
|
||||||
@@ -131,7 +129,11 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
|
|||||||
onClose: onCloseUpdateNotification,
|
onClose: onCloseUpdateNotification,
|
||||||
onOpen: onOpenUpdateNotification
|
onOpen: onOpenUpdateNotification
|
||||||
} = useDisclosure();
|
} = useDisclosure();
|
||||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
const {
|
||||||
|
File,
|
||||||
|
onOpen: onOpenSelectFile,
|
||||||
|
onSelectImage
|
||||||
|
} = useSelectFile({
|
||||||
fileType: '.jpg,.png',
|
fileType: '.jpg,.png',
|
||||||
multiple: false
|
multiple: false
|
||||||
});
|
});
|
||||||
@@ -151,32 +153,6 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
|
|||||||
[reset, t, toast, updateUserInfo]
|
[reset, t, toast, updateUserInfo]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSelectFile = useCallback(
|
|
||||||
async (e: File[]) => {
|
|
||||||
const file = e[0];
|
|
||||||
if (!file || !userInfo) return;
|
|
||||||
try {
|
|
||||||
const src = await compressImgFileAndUpload({
|
|
||||||
type: MongoImageTypeEnum.userAvatar,
|
|
||||||
file,
|
|
||||||
maxW: 300,
|
|
||||||
maxH: 300
|
|
||||||
});
|
|
||||||
|
|
||||||
onclickSave({
|
|
||||||
...userInfo,
|
|
||||||
avatar: src
|
|
||||||
});
|
|
||||||
} catch (err: any) {
|
|
||||||
toast({
|
|
||||||
title: typeof err === 'string' ? err : t('account_info:avatar_selection_exception'),
|
|
||||||
status: 'warning'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[onclickSave, t, toast, userInfo]
|
|
||||||
);
|
|
||||||
|
|
||||||
const labelStyles: BoxProps = {
|
const labelStyles: BoxProps = {
|
||||||
flex: '0 0 80px',
|
flex: '0 0 80px',
|
||||||
fontSize: 'sm',
|
fontSize: 'sm',
|
||||||
@@ -329,7 +305,21 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
|
|||||||
)}
|
)}
|
||||||
{isOpenUpdatePsw && <UpdatePswModal onClose={onCloseUpdatePsw} />}
|
{isOpenUpdatePsw && <UpdatePswModal onClose={onCloseUpdatePsw} />}
|
||||||
{isOpenUpdateNotification && <UpdateNotification onClose={onCloseUpdateNotification} />}
|
{isOpenUpdateNotification && <UpdateNotification onClose={onCloseUpdateNotification} />}
|
||||||
<File onSelect={onSelectFile} />
|
<File
|
||||||
|
onSelect={(e) =>
|
||||||
|
onSelectImage(e, {
|
||||||
|
maxW: 300,
|
||||||
|
maxH: 300,
|
||||||
|
callback: (src) => {
|
||||||
|
if (!userInfo) return;
|
||||||
|
onclickSave({
|
||||||
|
...userInfo,
|
||||||
|
avatar: src
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
import React, { useCallback } from 'react';
|
import React from 'react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
|
||||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
import { Box, Button, Flex, Input, ModalBody, ModalFooter } from '@chakra-ui/react';
|
import { Box, Button, Flex, Input, ModalBody, ModalFooter } from '@chakra-ui/react';
|
||||||
@@ -12,7 +10,6 @@ import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
|||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import { postCreateTeam, putUpdateTeam } from '@/web/support/user/team/api';
|
import { postCreateTeam, putUpdateTeam } from '@/web/support/user/team/api';
|
||||||
import { CreateTeamProps } from '@fastgpt/global/support/user/team/controller.d';
|
import { CreateTeamProps } from '@fastgpt/global/support/user/team/controller.d';
|
||||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
|
||||||
import { DEFAULT_TEAM_AVATAR } from '@fastgpt/global/common/system/constants';
|
import { DEFAULT_TEAM_AVATAR } from '@fastgpt/global/common/system/constants';
|
||||||
|
|
||||||
export type EditTeamFormDataType = CreateTeamProps & {
|
export type EditTeamFormDataType = CreateTeamProps & {
|
||||||
@@ -41,33 +38,15 @@ function EditModal({
|
|||||||
});
|
});
|
||||||
const avatar = watch('avatar');
|
const avatar = watch('avatar');
|
||||||
|
|
||||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
const {
|
||||||
|
File,
|
||||||
|
onOpen: onOpenSelectFile,
|
||||||
|
onSelectImage
|
||||||
|
} = useSelectFile({
|
||||||
fileType: '.jpg,.png,.svg',
|
fileType: '.jpg,.png,.svg',
|
||||||
multiple: false
|
multiple: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSelectFile = useCallback(
|
|
||||||
async (e: File[]) => {
|
|
||||||
const file = e[0];
|
|
||||||
if (!file) return;
|
|
||||||
try {
|
|
||||||
const src = await compressImgFileAndUpload({
|
|
||||||
type: MongoImageTypeEnum.teamAvatar,
|
|
||||||
file,
|
|
||||||
maxW: 300,
|
|
||||||
maxH: 300
|
|
||||||
});
|
|
||||||
setValue('avatar', src);
|
|
||||||
} catch (err: any) {
|
|
||||||
toast({
|
|
||||||
title: getErrText(err, t('common:common.Select File Failed')),
|
|
||||||
status: 'warning'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setValue, t, toast]
|
|
||||||
);
|
|
||||||
|
|
||||||
const { mutate: onclickCreate, isLoading: creating } = useRequest({
|
const { mutate: onclickCreate, isLoading: creating } = useRequest({
|
||||||
mutationFn: async (data: CreateTeamProps) => {
|
mutationFn: async (data: CreateTeamProps) => {
|
||||||
return postCreateTeam(data);
|
return postCreateTeam(data);
|
||||||
@@ -154,7 +133,15 @@ function EditModal({
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
<File onSelect={onSelectFile} />
|
<File
|
||||||
|
onSelect={(e) =>
|
||||||
|
onSelectImage(e, {
|
||||||
|
maxH: 300,
|
||||||
|
maxW: 300,
|
||||||
|
callback: (e) => setValue('avatar', e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
</MyModal>
|
</MyModal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,9 +6,7 @@ import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
|||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import React, { useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { TeamContext } from '../context';
|
import { TeamContext } from '../context';
|
||||||
@@ -23,7 +21,11 @@ export type GroupFormType = {
|
|||||||
function GroupInfoModal({ onClose, editGroupId }: { onClose: () => void; editGroupId?: string }) {
|
function GroupInfoModal({ onClose, editGroupId }: { onClose: () => void; editGroupId?: string }) {
|
||||||
const { refetchGroups, groups, refetchMembers } = useContextSelector(TeamContext, (v) => v);
|
const { refetchGroups, groups, refetchMembers } = useContextSelector(TeamContext, (v) => v);
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { File: AvatarSelect, onOpen: onOpenSelectAvatar } = useSelectFile({
|
const {
|
||||||
|
File: AvatarSelect,
|
||||||
|
onOpen: onOpenSelectAvatar,
|
||||||
|
onSelectImage
|
||||||
|
} = useSelectFile({
|
||||||
fileType: '.jpg, .jpeg, .png',
|
fileType: '.jpg, .jpeg, .png',
|
||||||
multiple: false
|
multiple: false
|
||||||
});
|
});
|
||||||
@@ -41,13 +43,10 @@ function GroupInfoModal({ onClose, editGroupId }: { onClose: () => void; editGro
|
|||||||
|
|
||||||
const { loading: uploadingAvatar, run: onSelectAvatar } = useRequest2(
|
const { loading: uploadingAvatar, run: onSelectAvatar } = useRequest2(
|
||||||
async (file: File[]) => {
|
async (file: File[]) => {
|
||||||
const src = await compressImgFileAndUpload({
|
return onSelectImage(file, {
|
||||||
type: MongoImageTypeEnum.groupAvatar,
|
|
||||||
file: file[0],
|
|
||||||
maxW: 300,
|
maxW: 300,
|
||||||
maxH: 300
|
maxH: 300
|
||||||
});
|
});
|
||||||
return src;
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: (src: string) => {
|
onSuccess: (src: string) => {
|
||||||
|
|||||||
@@ -1,8 +1,6 @@
|
|||||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
|
||||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||||
import { postCreateOrg, putUpdateOrg } from '@/web/support/user/team/org/api';
|
import { postCreateOrg, putUpdateOrg } from '@/web/support/user/team/org/api';
|
||||||
import { Button, HStack, Input, ModalBody, ModalFooter, Textarea } from '@chakra-ui/react';
|
import { Button, HStack, Input, ModalBody, ModalFooter, Textarea } from '@chakra-ui/react';
|
||||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
|
||||||
import { DEFAULT_ORG_AVATAR } from '@fastgpt/global/common/system/constants';
|
import { DEFAULT_ORG_AVATAR } from '@fastgpt/global/common/system/constants';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||||
@@ -89,19 +87,20 @@ function OrgInfoModal({
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const { File: AvatarSelect, onOpen: onOpenSelectAvatar } = useSelectFile({
|
const {
|
||||||
|
File: AvatarSelect,
|
||||||
|
onOpen: onOpenSelectAvatar,
|
||||||
|
onSelectImage
|
||||||
|
} = useSelectFile({
|
||||||
fileType: '.jpg, .jpeg, .png',
|
fileType: '.jpg, .jpeg, .png',
|
||||||
multiple: false
|
multiple: false
|
||||||
});
|
});
|
||||||
const { loading: uploadingAvatar, run: onSelectAvatar } = useRequest2(
|
const { loading: uploadingAvatar, run: onSelectAvatar } = useRequest2(
|
||||||
async (file: File[]) => {
|
async (file: File[]) => {
|
||||||
const src = await compressImgFileAndUpload({
|
return onSelectImage(file, {
|
||||||
type: MongoImageTypeEnum.groupAvatar,
|
|
||||||
file: file[0],
|
|
||||||
maxW: 300,
|
maxW: 300,
|
||||||
maxH: 300
|
maxH: 300
|
||||||
});
|
});
|
||||||
return src;
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onSuccess: (src: string) => {
|
onSuccess: (src: string) => {
|
||||||
|
|||||||
@@ -1,38 +1,30 @@
|
|||||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
import { jsonRes } from '@fastgpt/service/common/response';
|
|
||||||
import { connectToDatabase } from '@/service/mongo';
|
import { connectToDatabase } from '@/service/mongo';
|
||||||
import { uploadMongoImg } from '@fastgpt/service/common/file/image/controller';
|
import { uploadMongoImg } from '@fastgpt/service/common/file/image/controller';
|
||||||
import { UploadImgProps } from '@fastgpt/global/common/file/api';
|
import { UploadImgProps } from '@fastgpt/global/common/file/api';
|
||||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||||
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Upload avatar image
|
Upload avatar image
|
||||||
*/
|
*/
|
||||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
async function handler(req: NextApiRequest, res: NextApiResponse): Promise<string> {
|
||||||
try {
|
await connectToDatabase();
|
||||||
await connectToDatabase();
|
const body = req.body as UploadImgProps;
|
||||||
const body = req.body as UploadImgProps;
|
|
||||||
|
|
||||||
const { teamId } = await authCert({ req, authToken: true });
|
const { teamId } = await authCert({ req, authToken: true });
|
||||||
|
|
||||||
const imgId = await uploadMongoImg({
|
return uploadMongoImg({
|
||||||
teamId,
|
teamId,
|
||||||
...body
|
...body
|
||||||
});
|
});
|
||||||
|
|
||||||
jsonRes(res, { data: imgId });
|
|
||||||
} catch (error) {
|
|
||||||
jsonRes(res, {
|
|
||||||
code: 500,
|
|
||||||
error
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
export default NextAPI(handler);
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
api: {
|
api: {
|
||||||
bodyParser: {
|
bodyParser: {
|
||||||
sizeLimit: '16mb'
|
sizeLimit: '12mb'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ import { ClientSession } from '@fastgpt/service/common/mongo';
|
|||||||
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
import { authApp } from '@fastgpt/service/support/permission/app/auth';
|
||||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||||
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
|
import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSchema';
|
||||||
import { MongoUser } from '@fastgpt/service/support/user/schema';
|
|
||||||
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
||||||
|
import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
|
||||||
|
|
||||||
export type CreateAppBody = {
|
export type CreateAppBody = {
|
||||||
parentId?: ParentIdType;
|
parentId?: ParentIdType;
|
||||||
@@ -148,6 +148,8 @@ export const onCreateApp = async ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await refreshSourceAvatar(avatar, undefined, session);
|
||||||
|
|
||||||
return appId;
|
return appId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { ClientSession } from '@fastgpt/service/common/mongo';
|
|||||||
import { deleteChatFiles } from '@fastgpt/service/core/chat/controller';
|
import { deleteChatFiles } from '@fastgpt/service/core/chat/controller';
|
||||||
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
||||||
import { MongoOpenApi } from '@fastgpt/service/support/openapi/schema';
|
import { MongoOpenApi } from '@fastgpt/service/support/openapi/schema';
|
||||||
|
import { removeImageByPath } from '@fastgpt/service/common/file/image/controller';
|
||||||
|
|
||||||
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
|
||||||
const { appId } = req.query as { appId: string };
|
const { appId } = req.query as { appId: string };
|
||||||
@@ -57,7 +58,7 @@ export const onDelOneApp = async ({
|
|||||||
const apps = await findAppAndAllChildren({
|
const apps = await findAppAndAllChildren({
|
||||||
teamId,
|
teamId,
|
||||||
appId,
|
appId,
|
||||||
fields: '_id'
|
fields: '_id avatar'
|
||||||
});
|
});
|
||||||
|
|
||||||
const del = async (session: ClientSession) => {
|
const del = async (session: ClientSession) => {
|
||||||
@@ -109,6 +110,8 @@ export const onDelOneApp = async ({
|
|||||||
},
|
},
|
||||||
{ session }
|
{ session }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await removeImageByPath(app.avatar, session);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import { MongoApp } from '@fastgpt/service/core/app/schema';
|
|||||||
import { isEqual } from 'lodash';
|
import { isEqual } from 'lodash';
|
||||||
import { onCreateApp } from '../create';
|
import { onCreateApp } from '../create';
|
||||||
import { onDelOneApp } from '../del';
|
import { onDelOneApp } from '../del';
|
||||||
|
import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
|
||||||
|
|
||||||
export type UpdateHttpPluginBody = {
|
export type UpdateHttpPluginBody = {
|
||||||
appId: string;
|
appId: string;
|
||||||
@@ -49,13 +50,15 @@ async function handler(req: ApiRequestProps<UpdateHttpPluginBody>, res: NextApiR
|
|||||||
await MongoApp.findByIdAndUpdate(
|
await MongoApp.findByIdAndUpdate(
|
||||||
appId,
|
appId,
|
||||||
{
|
{
|
||||||
name,
|
...(name && { name }),
|
||||||
avatar,
|
...(avatar && { avatar }),
|
||||||
intro,
|
...(intro !== undefined && { intro }),
|
||||||
pluginData
|
pluginData
|
||||||
},
|
},
|
||||||
{ session }
|
{ session }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
await refreshSourceAvatar(avatar, app.avatar, session);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { getResourceClbsAndGroups } from '@fastgpt/service/support/permission/co
|
|||||||
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
import { authUserPer } from '@fastgpt/service/support/permission/user/auth';
|
||||||
import { TeamWritePermissionVal } from '@fastgpt/global/support/permission/user/constant';
|
import { TeamWritePermissionVal } from '@fastgpt/global/support/permission/user/constant';
|
||||||
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||||
|
import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
|
||||||
|
|
||||||
export type AppUpdateQuery = {
|
export type AppUpdateQuery = {
|
||||||
appId: string;
|
appId: string;
|
||||||
@@ -95,6 +96,8 @@ async function handler(req: ApiRequestProps<AppUpdateBody, AppUpdateQuery>) {
|
|||||||
isPlugin: app.type === AppTypeEnum.plugin
|
isPlugin: app.type === AppTypeEnum.plugin
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await refreshSourceAvatar(avatar, app.avatar, session);
|
||||||
|
|
||||||
return MongoApp.findByIdAndUpdate(
|
return MongoApp.findByIdAndUpdate(
|
||||||
appId,
|
appId,
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ import type { ApiRequestProps } from '@fastgpt/service/type/next';
|
|||||||
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
|
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
|
||||||
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
|
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
|
||||||
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
||||||
|
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||||
|
import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
|
||||||
|
|
||||||
export type DatasetCreateQuery = {};
|
export type DatasetCreateQuery = {};
|
||||||
export type DatasetCreateBody = CreateDatasetParams;
|
export type DatasetCreateBody = CreateDatasetParams;
|
||||||
@@ -63,19 +65,29 @@ async function handler(
|
|||||||
// check limit
|
// check limit
|
||||||
await checkTeamDatasetLimit(teamId);
|
await checkTeamDatasetLimit(teamId);
|
||||||
|
|
||||||
const { _id } = await MongoDataset.create({
|
const datasetId = await mongoSessionRun(async (session) => {
|
||||||
...parseParentIdInMongo(parentId),
|
const [{ _id }] = await MongoDataset.create(
|
||||||
name,
|
[
|
||||||
intro,
|
{
|
||||||
teamId,
|
...parseParentIdInMongo(parentId),
|
||||||
tmbId,
|
name,
|
||||||
vectorModel,
|
intro,
|
||||||
agentModel,
|
teamId,
|
||||||
avatar,
|
tmbId,
|
||||||
type,
|
vectorModel,
|
||||||
apiServer,
|
agentModel,
|
||||||
feishuServer,
|
avatar,
|
||||||
yuqueServer
|
type,
|
||||||
|
apiServer,
|
||||||
|
feishuServer,
|
||||||
|
yuqueServer
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{ session }
|
||||||
|
);
|
||||||
|
await refreshSourceAvatar(avatar, undefined, session);
|
||||||
|
|
||||||
|
return _id;
|
||||||
});
|
});
|
||||||
|
|
||||||
pushTrack.createDataset({
|
pushTrack.createDataset({
|
||||||
@@ -85,6 +97,6 @@ async function handler(
|
|||||||
uid: userId
|
uid: userId
|
||||||
});
|
});
|
||||||
|
|
||||||
return _id;
|
return datasetId;
|
||||||
}
|
}
|
||||||
export default NextAPI(handler);
|
export default NextAPI(handler);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { NextAPI } from '@/service/middleware/entry';
|
|||||||
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
|
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||||
import { MongoDatasetCollectionTags } from '@fastgpt/service/core/dataset/tag/schema';
|
import { MongoDatasetCollectionTags } from '@fastgpt/service/core/dataset/tag/schema';
|
||||||
|
import { removeImageByPath } from '@fastgpt/service/common/file/image/controller';
|
||||||
|
|
||||||
async function handler(req: NextApiRequest) {
|
async function handler(req: NextApiRequest) {
|
||||||
const { id: datasetId } = req.query as {
|
const { id: datasetId } = req.query as {
|
||||||
@@ -51,6 +52,10 @@ async function handler(req: NextApiRequest) {
|
|||||||
},
|
},
|
||||||
{ session }
|
{ session }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
for await (const dataset of datasets) {
|
||||||
|
await removeImageByPath(dataset.avatar, session);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ import { DatasetErrEnum } from '@fastgpt/global/common/error/code/dataset';
|
|||||||
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
import { MongoDatasetTraining } from '@fastgpt/service/core/dataset/training/schema';
|
||||||
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
import { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
||||||
import { addDays } from 'date-fns';
|
import { addDays } from 'date-fns';
|
||||||
|
import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
|
||||||
|
|
||||||
export type DatasetUpdateQuery = {};
|
export type DatasetUpdateQuery = {};
|
||||||
export type DatasetUpdateResponse = any;
|
export type DatasetUpdateResponse = any;
|
||||||
@@ -144,6 +145,8 @@ async function handler(
|
|||||||
autoSync,
|
autoSync,
|
||||||
session
|
session
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await refreshSourceAvatar(avatar, dataset.avatar, session);
|
||||||
};
|
};
|
||||||
|
|
||||||
await mongoSessionRun(async (session) => {
|
await mongoSessionRun(async (session) => {
|
||||||
|
|||||||
@@ -7,12 +7,14 @@ import { UserStatusEnum } from '@fastgpt/global/support/user/constant';
|
|||||||
import { NextAPI } from '@/service/middleware/entry';
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
import { useReqFrequencyLimit } from '@fastgpt/service/common/middle/reqFrequencyLimit';
|
import { useReqFrequencyLimit } from '@fastgpt/service/common/middle/reqFrequencyLimit';
|
||||||
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
||||||
|
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||||
|
import { UserErrEnum } from '@fastgpt/global/common/error/code/user';
|
||||||
|
|
||||||
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const { username, password } = req.body as PostLoginProps;
|
const { username, password } = req.body as PostLoginProps;
|
||||||
|
|
||||||
if (!username || !password) {
|
if (!username || !password) {
|
||||||
throw new Error('缺少参数');
|
return Promise.reject(CommonErrEnum.invalidParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检测用户是否存在
|
// 检测用户是否存在
|
||||||
@@ -23,11 +25,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
'status'
|
'status'
|
||||||
);
|
);
|
||||||
if (!authCert) {
|
if (!authCert) {
|
||||||
throw new Error('用户未注册');
|
return Promise.reject(UserErrEnum.unAuthUser);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (authCert.status === UserStatusEnum.forbidden) {
|
if (authCert.status === UserStatusEnum.forbidden) {
|
||||||
throw new Error('账号已停用,无法登录');
|
return Promise.reject('Invalid account!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const user = await MongoUser.findOne({
|
const user = await MongoUser.findOne({
|
||||||
@@ -36,7 +38,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new Error('密码错误');
|
return Promise.reject(UserErrEnum.binVisitor);
|
||||||
}
|
}
|
||||||
|
|
||||||
const userDetail = await getUserDetail({
|
const userDetail = await getUserDetail({
|
||||||
@@ -68,4 +70,4 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default NextAPI(useReqFrequencyLimit(120, 10), handler);
|
export default NextAPI(useReqFrequencyLimit(120, 10, true), handler);
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ import { MongoTeamMember } from '@fastgpt/service/support/user/team/teamMemberSc
|
|||||||
/* update user info */
|
/* update user info */
|
||||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||||
import { NextAPI } from '@/service/middleware/entry';
|
import { NextAPI } from '@/service/middleware/entry';
|
||||||
|
import { mongoSessionRun } from '@fastgpt/service/common/mongo/sessionRun';
|
||||||
|
import { getUserDetail } from '@fastgpt/service/support/user/controller';
|
||||||
|
import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
|
||||||
export type UserAccountUpdateQuery = {};
|
export type UserAccountUpdateQuery = {};
|
||||||
export type UserAccountUpdateBody = UserUpdateParams;
|
export type UserAccountUpdateBody = UserUpdateParams;
|
||||||
export type UserAccountUpdateResponse = {};
|
export type UserAccountUpdateResponse = {};
|
||||||
@@ -16,22 +19,22 @@ async function handler(
|
|||||||
const { avatar, timezone } = req.body;
|
const { avatar, timezone } = req.body;
|
||||||
|
|
||||||
const { tmbId } = await authCert({ req, authToken: true });
|
const { tmbId } = await authCert({ req, authToken: true });
|
||||||
const tmb = await MongoTeamMember.findById(tmbId);
|
const user = await getUserDetail({ tmbId });
|
||||||
if (!tmb) {
|
|
||||||
throw new Error('can not find it');
|
|
||||||
}
|
|
||||||
const userId = tmb.userId;
|
|
||||||
|
|
||||||
// 更新对应的记录
|
// 更新对应的记录
|
||||||
await MongoUser.updateOne(
|
await mongoSessionRun(async (session) => {
|
||||||
{
|
await MongoUser.updateOne(
|
||||||
_id: userId
|
{
|
||||||
},
|
_id: user._id
|
||||||
{
|
},
|
||||||
...(avatar && { avatar }),
|
{
|
||||||
...(timezone && { timezone })
|
...(avatar && { avatar }),
|
||||||
}
|
...(timezone && { timezone })
|
||||||
);
|
}
|
||||||
|
).session(session);
|
||||||
|
|
||||||
|
await refreshSourceAvatar(avatar, user.avatar, session);
|
||||||
|
});
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import CollaboratorContextProvider from '@/components/support/permission/MemberManager/context';
|
import CollaboratorContextProvider from '@/components/support/permission/MemberManager/context';
|
||||||
import ResumeInherit from '@/components/support/permission/ResumeInheritText';
|
import ResumeInherit from '@/components/support/permission/ResumeInheritText';
|
||||||
import { AppContext } from '@/pages/app/detail/components/context';
|
import { AppContext } from '@/pages/app/detail/components/context';
|
||||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
|
||||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||||
import { useI18n } from '@/web/context/I18n';
|
import { useI18n } from '@/web/context/I18n';
|
||||||
import { resumeInheritPer } from '@/web/core/app/api';
|
import { resumeInheritPer } from '@/web/core/app/api';
|
||||||
@@ -20,8 +19,6 @@ import {
|
|||||||
ModalFooter,
|
ModalFooter,
|
||||||
Textarea
|
Textarea
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
|
||||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
|
||||||
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
import type { RequireOnlyOne } from '@fastgpt/global/common/type/utils';
|
||||||
import type { AppSchema } from '@fastgpt/global/core/app/type.d';
|
import type { AppSchema } from '@fastgpt/global/core/app/type.d';
|
||||||
import { AppPermissionList } from '@fastgpt/global/support/permission/app/constant';
|
import { AppPermissionList } from '@fastgpt/global/support/permission/app/constant';
|
||||||
@@ -42,7 +39,11 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
const { updateAppDetail, appDetail, reloadApp } = useContextSelector(AppContext, (v) => v);
|
const { updateAppDetail, appDetail, reloadApp } = useContextSelector(AppContext, (v) => v);
|
||||||
|
|
||||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
const {
|
||||||
|
File,
|
||||||
|
onOpen: onOpenSelectFile,
|
||||||
|
onSelectImage
|
||||||
|
} = useSelectFile({
|
||||||
fileType: '.jpg,.png',
|
fileType: '.jpg,.png',
|
||||||
multiple: false
|
multiple: false
|
||||||
});
|
});
|
||||||
@@ -101,28 +102,6 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
[handleSubmit, onClose, saveSubmitError, saveSubmitSuccess]
|
[handleSubmit, onClose, saveSubmitError, saveSubmitSuccess]
|
||||||
);
|
);
|
||||||
|
|
||||||
const onSelectFile = useCallback(
|
|
||||||
async (e: File[]) => {
|
|
||||||
const file = e[0];
|
|
||||||
if (!file) return;
|
|
||||||
try {
|
|
||||||
const src = await compressImgFileAndUpload({
|
|
||||||
type: MongoImageTypeEnum.appAvatar,
|
|
||||||
file,
|
|
||||||
maxW: 300,
|
|
||||||
maxH: 300
|
|
||||||
});
|
|
||||||
setValue('avatar', src);
|
|
||||||
} catch (err: any) {
|
|
||||||
toast({
|
|
||||||
title: getErrText(err, t('common:common.error.Select avatar failed')),
|
|
||||||
status: 'warning'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setValue, t, toast]
|
|
||||||
);
|
|
||||||
|
|
||||||
const onUpdateCollaborators = ({
|
const onUpdateCollaborators = ({
|
||||||
members,
|
members,
|
||||||
groups,
|
groups,
|
||||||
@@ -273,7 +252,15 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
|||||||
</Button>
|
</Button>
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
|
||||||
<File onSelect={onSelectFile} />
|
<File
|
||||||
|
onSelect={(e) =>
|
||||||
|
onSelectImage(e, {
|
||||||
|
maxH: 300,
|
||||||
|
maxW: 300,
|
||||||
|
callback: (e) => setValue('avatar', e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
</MyModal>
|
</MyModal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
import React, { useCallback, useMemo, useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
import { Box, Flex, Button, ModalBody, Input, Grid, Card } from '@chakra-ui/react';
|
import { Box, Flex, Button, ModalBody, Input, Grid, Card } from '@chakra-ui/react';
|
||||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
|
||||||
import { postCreateApp } from '@/web/core/app/api';
|
import { postCreateApp } from '@/web/core/app/api';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { emptyTemplates } from '@/web/core/app/templates';
|
import { emptyTemplates } from '@/web/core/app/templates';
|
||||||
@@ -13,7 +10,6 @@ import Avatar from '@fastgpt/web/components/common/Avatar';
|
|||||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
|
||||||
import { useContextSelector } from 'use-context-selector';
|
import { useContextSelector } from 'use-context-selector';
|
||||||
import { AppListContext } from './context';
|
import { AppListContext } from './context';
|
||||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||||
@@ -25,7 +21,6 @@ import {
|
|||||||
getTemplateMarketItemList
|
getTemplateMarketItemList
|
||||||
} from '@/web/core/app/api/template';
|
} from '@/web/core/app/api/template';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import { AppTemplateSchemaType } from '@fastgpt/global/core/app/type';
|
|
||||||
|
|
||||||
type FormType = {
|
type FormType = {
|
||||||
avatar: string;
|
avatar: string;
|
||||||
@@ -44,7 +39,6 @@ const CreateModal = ({
|
|||||||
onOpenTemplateModal: (type: AppTypeEnum) => void;
|
onOpenTemplateModal: (type: AppTypeEnum) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { toast } = useToast();
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { parentId, loadMyApps } = useContextSelector(AppListContext, (v) => v);
|
const { parentId, loadMyApps } = useContextSelector(AppListContext, (v) => v);
|
||||||
const { isPc } = useSystem();
|
const { isPc } = useSystem();
|
||||||
@@ -86,33 +80,15 @@ const CreateModal = ({
|
|||||||
});
|
});
|
||||||
const avatar = watch('avatar');
|
const avatar = watch('avatar');
|
||||||
|
|
||||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
const {
|
||||||
|
File,
|
||||||
|
onOpen: onOpenSelectFile,
|
||||||
|
onSelectImage
|
||||||
|
} = useSelectFile({
|
||||||
fileType: '.jpg,.png',
|
fileType: '.jpg,.png',
|
||||||
multiple: false
|
multiple: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSelectFile = useCallback(
|
|
||||||
async (e: File[]) => {
|
|
||||||
const file = e[0];
|
|
||||||
if (!file) return;
|
|
||||||
try {
|
|
||||||
const src = await compressImgFileAndUpload({
|
|
||||||
type: MongoImageTypeEnum.appAvatar,
|
|
||||||
file,
|
|
||||||
maxW: 300,
|
|
||||||
maxH: 300
|
|
||||||
});
|
|
||||||
setValue('avatar', src);
|
|
||||||
} catch (err: any) {
|
|
||||||
toast({
|
|
||||||
title: getErrText(err, t('common:common.error.Select avatar failed')),
|
|
||||||
status: 'warning'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setValue, t, toast]
|
|
||||||
);
|
|
||||||
|
|
||||||
const { runAsync: onclickCreate, loading: isCreating } = useRequest2(
|
const { runAsync: onclickCreate, loading: isCreating } = useRequest2(
|
||||||
async (data: FormType, templateId?: string) => {
|
async (data: FormType, templateId?: string) => {
|
||||||
if (!templateId) {
|
if (!templateId) {
|
||||||
@@ -290,7 +266,15 @@ const CreateModal = ({
|
|||||||
))}
|
))}
|
||||||
</Grid>
|
</Grid>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<File onSelect={onSelectFile} />
|
<File
|
||||||
|
onSelect={(e) =>
|
||||||
|
onSelectImage(e, {
|
||||||
|
maxH: 300,
|
||||||
|
maxW: 300,
|
||||||
|
callback: (e) => setValue('avatar', e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
</MyModal>
|
</MyModal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -17,14 +17,12 @@ import {
|
|||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { HttpPluginImgUrl, MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
import { HttpPluginImgUrl } from '@fastgpt/global/common/file/image/constants';
|
||||||
import {
|
import {
|
||||||
postCreateHttpPlugin,
|
postCreateHttpPlugin,
|
||||||
putUpdateHttpPlugin,
|
putUpdateHttpPlugin,
|
||||||
@@ -124,33 +122,15 @@ const HttpPluginEditModal = ({
|
|||||||
errorToast: t('common:common.Update Failed')
|
errorToast: t('common:common.Update Failed')
|
||||||
});
|
});
|
||||||
|
|
||||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
const {
|
||||||
|
File,
|
||||||
|
onOpen: onOpenSelectFile,
|
||||||
|
onSelectImage
|
||||||
|
} = useSelectFile({
|
||||||
fileType: 'image/*',
|
fileType: 'image/*',
|
||||||
multiple: false
|
multiple: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSelectFile = useCallback(
|
|
||||||
async (e: File[]) => {
|
|
||||||
const file = e[0];
|
|
||||||
if (!file) return;
|
|
||||||
try {
|
|
||||||
const src = await compressImgFileAndUpload({
|
|
||||||
type: MongoImageTypeEnum.pluginAvatar,
|
|
||||||
file,
|
|
||||||
maxW: 300,
|
|
||||||
maxH: 300
|
|
||||||
});
|
|
||||||
setValue('avatar', src);
|
|
||||||
} catch (err: any) {
|
|
||||||
toast({
|
|
||||||
title: getErrText(err, t('common:common.Select File Failed')),
|
|
||||||
status: 'warning'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setValue, t, toast]
|
|
||||||
);
|
|
||||||
|
|
||||||
/* load api from url */
|
/* load api from url */
|
||||||
const { mutate: onClickUrlLoadApi, isLoading: isLoadingUrlApi } = useRequest({
|
const { mutate: onClickUrlLoadApi, isLoading: isLoadingUrlApi } = useRequest({
|
||||||
mutationFn: async () => {
|
mutationFn: async () => {
|
||||||
@@ -473,7 +453,15 @@ const HttpPluginEditModal = ({
|
|||||||
)}
|
)}
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
</MyModal>
|
</MyModal>
|
||||||
<File onSelect={onSelectFile} />
|
<File
|
||||||
|
onSelect={(e) =>
|
||||||
|
onSelectImage(e, {
|
||||||
|
maxH: 300,
|
||||||
|
maxW: 300,
|
||||||
|
callback: (e) => setValue('avatar', e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { Box, Flex, Switch, Input } from '@chakra-ui/react';
|
import { Box, Flex, Switch, Input } from '@chakra-ui/react';
|
||||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
|
||||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
|
||||||
import type { DatasetItemType } from '@fastgpt/global/core/dataset/type.d';
|
import type { DatasetItemType } from '@fastgpt/global/core/dataset/type.d';
|
||||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
|
||||||
import AIModelSelector from '@/components/Select/AIModelSelector';
|
import AIModelSelector from '@/components/Select/AIModelSelector';
|
||||||
import { postRebuildEmbedding } from '@/web/core/dataset/api';
|
import { postRebuildEmbedding } from '@/web/core/dataset/api';
|
||||||
import type { VectorModelItemType } from '@fastgpt/global/core/ai/model.d';
|
import type { VectorModelItemType } from '@fastgpt/global/core/ai/model.d';
|
||||||
@@ -68,11 +65,6 @@ const Info = ({ datasetId }: { datasetId: string }) => {
|
|||||||
title: t('common:common.confirm.Common Tip')
|
title: t('common:common.confirm.Common Tip')
|
||||||
});
|
});
|
||||||
|
|
||||||
const { File } = useSelectFile({
|
|
||||||
fileType: '.jpg,.png',
|
|
||||||
multiple: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const { runAsync: onSave } = useRequest2(
|
const { runAsync: onSave } = useRequest2(
|
||||||
(data: DatasetItemType) => {
|
(data: DatasetItemType) => {
|
||||||
return updateDataset({
|
return updateDataset({
|
||||||
@@ -87,27 +79,6 @@ const Info = ({ datasetId }: { datasetId: string }) => {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const { runAsync: onSelectFile } = useRequest2(
|
|
||||||
(e: File[]) => {
|
|
||||||
const file = e[0];
|
|
||||||
if (!file) return Promise.resolve(null);
|
|
||||||
return compressImgFileAndUpload({
|
|
||||||
type: MongoImageTypeEnum.datasetAvatar,
|
|
||||||
file,
|
|
||||||
maxW: 300,
|
|
||||||
maxH: 300
|
|
||||||
});
|
|
||||||
},
|
|
||||||
{
|
|
||||||
onSuccess(src: string | null) {
|
|
||||||
if (src) {
|
|
||||||
setValue('avatar', src);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
errorToast: t('common:common.avatar.Select Failed')
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const { runAsync: onRebuilding } = useRequest2(
|
const { runAsync: onRebuilding } = useRequest2(
|
||||||
(vectorModel: VectorModelItemType) => {
|
(vectorModel: VectorModelItemType) => {
|
||||||
return postRebuildEmbedding({
|
return postRebuildEmbedding({
|
||||||
@@ -416,7 +387,6 @@ const Info = ({ datasetId }: { datasetId: string }) => {
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<File onSelect={onSelectFile} />
|
|
||||||
<ConfirmDelModal />
|
<ConfirmDelModal />
|
||||||
<ConfirmRebuildModal countDown={10} />
|
<ConfirmRebuildModal countDown={10} />
|
||||||
<ConfirmSyncScheduleModal />
|
<ConfirmSyncScheduleModal />
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useMemo } from 'react';
|
||||||
import { Box, Flex, Button, ModalFooter, ModalBody, Input, HStack } from '@chakra-ui/react';
|
import { Box, Flex, Button, ModalFooter, ModalBody, Input, HStack } from '@chakra-ui/react';
|
||||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
|
||||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
|
||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
import { useRouter } from 'next/router';
|
import { useRouter } from 'next/router';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
@@ -15,7 +13,6 @@ import { postCreateDataset } from '@/web/core/dataset/api';
|
|||||||
import type { CreateDatasetParams } from '@/global/core/dataset/api.d';
|
import type { CreateDatasetParams } from '@/global/core/dataset/api.d';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
|
||||||
import AIModelSelector from '@/components/Select/AIModelSelector';
|
import AIModelSelector from '@/components/Select/AIModelSelector';
|
||||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||||
@@ -90,33 +87,15 @@ const CreateModal = ({
|
|||||||
const vectorModel = watch('vectorModel');
|
const vectorModel = watch('vectorModel');
|
||||||
const agentModel = watch('agentModel');
|
const agentModel = watch('agentModel');
|
||||||
|
|
||||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
const {
|
||||||
fileType: '.jpg,.png',
|
File,
|
||||||
|
onOpen: onOpenSelectFile,
|
||||||
|
onSelectImage
|
||||||
|
} = useSelectFile({
|
||||||
|
fileType: 'image/*',
|
||||||
multiple: false
|
multiple: false
|
||||||
});
|
});
|
||||||
|
|
||||||
const onSelectFile = useCallback(
|
|
||||||
async (e: File[]) => {
|
|
||||||
const file = e[0];
|
|
||||||
if (!file) return;
|
|
||||||
try {
|
|
||||||
const src = await compressImgFileAndUpload({
|
|
||||||
type: MongoImageTypeEnum.datasetAvatar,
|
|
||||||
file,
|
|
||||||
maxW: 300,
|
|
||||||
maxH: 300
|
|
||||||
});
|
|
||||||
setValue('avatar' as const, src);
|
|
||||||
} catch (err: any) {
|
|
||||||
toast({
|
|
||||||
title: getErrText(err, t('common:common.avatar.Select Failed')),
|
|
||||||
status: 'warning'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[setValue, t, toast]
|
|
||||||
);
|
|
||||||
|
|
||||||
/* create a new kb and router to it */
|
/* create a new kb and router to it */
|
||||||
const { run: onclickCreate, loading: creating } = useRequest2(
|
const { run: onclickCreate, loading: creating } = useRequest2(
|
||||||
async (data: CreateDatasetParams) => await postCreateDataset(data),
|
async (data: CreateDatasetParams) => await postCreateDataset(data),
|
||||||
@@ -275,7 +254,15 @@ const CreateModal = ({
|
|||||||
|
|
||||||
<ComplianceTip pb={6} pt={0} px={9} type={'dataset'} />
|
<ComplianceTip pb={6} pt={0} px={9} type={'dataset'} />
|
||||||
|
|
||||||
<File onSelect={onSelectFile} />
|
<File
|
||||||
|
onSelect={(e) =>
|
||||||
|
onSelectImage(e, {
|
||||||
|
maxH: 300,
|
||||||
|
maxW: 300,
|
||||||
|
callback: (e) => setValue('avatar', e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>
|
||||||
</MyModal>
|
</MyModal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useMemo, useRef, useState } from 'react';
|
import React, { useMemo, useRef, useState } from 'react';
|
||||||
import { changeOwner, resumeInheritPer } from '@/web/core/dataset/api';
|
import { postChangeOwner, resumeInheritPer } from '@/web/core/dataset/api';
|
||||||
import { Box, Flex, Grid, HStack } from '@chakra-ui/react';
|
import { Box, Flex, Grid, HStack } from '@chakra-ui/react';
|
||||||
import { DatasetTypeEnum, DatasetTypeMap } from '@fastgpt/global/core/dataset/constants';
|
import { DatasetTypeEnum, DatasetTypeMap } from '@fastgpt/global/core/dataset/constants';
|
||||||
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
||||||
@@ -423,7 +423,7 @@ function List() {
|
|||||||
{!!editPerDataset && (
|
{!!editPerDataset && (
|
||||||
<ConfigPerModal
|
<ConfigPerModal
|
||||||
onChangeOwner={(tmbId: string) =>
|
onChangeOwner={(tmbId: string) =>
|
||||||
changeOwner({
|
postChangeOwner({
|
||||||
datasetId: editPerDataset._id,
|
datasetId: editPerDataset._id,
|
||||||
ownerId: tmbId
|
ownerId: tmbId
|
||||||
}).then(() => loadMyDatasets())
|
}).then(() => loadMyDatasets())
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { Dispatch } from 'react';
|
import React, { Dispatch } from 'react';
|
||||||
import { FormControl, Box, Input, Button } from '@chakra-ui/react';
|
import { FormControl, Box, Input, Button } from '@chakra-ui/react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { LoginPageTypeEnum } from '@/web/support/user/login/constants';
|
import { LoginPageTypeEnum, PasswordRule } from '@/web/support/user/login/constants';
|
||||||
import { postFindPassword } from '@/web/support/user/api';
|
import { postFindPassword } from '@/web/support/user/api';
|
||||||
import { useSendCode } from '@/web/support/user/hooks/useSendCode';
|
import { useSendCode } from '@/web/support/user/hooks/useSendCode';
|
||||||
import type { ResLogin } from '@/global/support/api/userRes.d';
|
import type { ResLogin } from '@/global/support/api/userRes.d';
|
||||||
@@ -70,6 +70,18 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
refreshDeps: [loginSuccess, t, toast]
|
refreshDeps: [loginSuccess, t, toast]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
const onSubmitErr = (err: Record<string, any>) => {
|
||||||
|
const val = Object.values(err)[0];
|
||||||
|
if (!val) return;
|
||||||
|
if (val.message) {
|
||||||
|
toast({
|
||||||
|
status: 'warning',
|
||||||
|
title: val.message,
|
||||||
|
duration: 3000,
|
||||||
|
isClosable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -79,8 +91,8 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
<Box
|
<Box
|
||||||
mt={9}
|
mt={9}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.keyCode === 13 && !e.shiftKey && !requesting) {
|
if (e.key === 'Enter' && !e.shiftKey && !requesting) {
|
||||||
handleSubmit(onclickFindPassword)();
|
handleSubmit(onclickFindPassword, onSubmitErr)();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -123,16 +135,12 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
bg={'myGray.50'}
|
bg={'myGray.50'}
|
||||||
type={'password'}
|
type={'password'}
|
||||||
size={'lg'}
|
size={'lg'}
|
||||||
placeholder={t('user:password.new_password')}
|
placeholder={t('login:password_tip')}
|
||||||
{...register('password', {
|
{...register('password', {
|
||||||
required: t('user:password.password_required'),
|
required: true,
|
||||||
minLength: {
|
pattern: {
|
||||||
value: 4,
|
value: PasswordRule,
|
||||||
message: t('user:password.password_condition')
|
message: t('login:password_tip')
|
||||||
},
|
|
||||||
maxLength: {
|
|
||||||
value: 20,
|
|
||||||
message: t('user:password.password_condition')
|
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
></Input>
|
></Input>
|
||||||
@@ -160,7 +168,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
fontWeight={['medium', 'medium']}
|
fontWeight={['medium', 'medium']}
|
||||||
colorScheme="blue"
|
colorScheme="blue"
|
||||||
isLoading={requesting}
|
isLoading={requesting}
|
||||||
onClick={handleSubmit(onclickFindPassword)}
|
onClick={handleSubmit(onclickFindPassword, onSubmitErr)}
|
||||||
>
|
>
|
||||||
{t('user:password.retrieve')}
|
{t('user:password.retrieve')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React, { useState, Dispatch, useCallback } from 'react';
|
import React, { Dispatch } from 'react';
|
||||||
import { FormControl, Flex, Input, Button, Box, Link } from '@chakra-ui/react';
|
import { FormControl, Flex, Input, Button, Box, Link } from '@chakra-ui/react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { LoginPageTypeEnum } from '@/web/support/user/login/constants';
|
import { LoginPageTypeEnum } from '@/web/support/user/login/constants';
|
||||||
@@ -9,6 +9,7 @@ import { useSystemStore } from '@/web/common/system/useSystemStore';
|
|||||||
import { getDocPath } from '@/web/common/system/doc';
|
import { getDocPath } from '@/web/common/system/doc';
|
||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import FormLayout from './components/FormLayout';
|
import FormLayout from './components/FormLayout';
|
||||||
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
setPageType: Dispatch<`${LoginPageTypeEnum}`>;
|
setPageType: Dispatch<`${LoginPageTypeEnum}`>;
|
||||||
@@ -30,31 +31,22 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
formState: { errors }
|
formState: { errors }
|
||||||
} = useForm<LoginFormType>();
|
} = useForm<LoginFormType>();
|
||||||
|
|
||||||
const [requesting, setRequesting] = useState(false);
|
const { runAsync: onclickLogin, loading: requesting } = useRequest2(
|
||||||
|
|
||||||
const onclickLogin = useCallback(
|
|
||||||
async ({ username, password }: LoginFormType) => {
|
async ({ username, password }: LoginFormType) => {
|
||||||
setRequesting(true);
|
loginSuccess(
|
||||||
try {
|
await postLogin({
|
||||||
loginSuccess(
|
username,
|
||||||
await postLogin({
|
password
|
||||||
username,
|
})
|
||||||
password
|
);
|
||||||
})
|
toast({
|
||||||
);
|
title: t('login:login_success'),
|
||||||
toast({
|
status: 'success'
|
||||||
title: t('login:login_success'),
|
});
|
||||||
status: 'success'
|
|
||||||
});
|
|
||||||
} catch (error: any) {
|
|
||||||
toast({
|
|
||||||
title: error.message || t('login:login_failed'),
|
|
||||||
status: 'error'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
setRequesting(false);
|
|
||||||
},
|
},
|
||||||
[loginSuccess, t, toast]
|
{
|
||||||
|
refreshDeps: [loginSuccess]
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
const isCommunityVersion = !!(feConfigs?.register_method && !feConfigs?.isPlus);
|
const isCommunityVersion = !!(feConfigs?.register_method && !feConfigs?.isPlus);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import React, { Dispatch } from 'react';
|
import React, { Dispatch } from 'react';
|
||||||
import { FormControl, Box, Input, Button } from '@chakra-ui/react';
|
import { FormControl, Box, Input, Button } from '@chakra-ui/react';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { LoginPageTypeEnum } from '@/web/support/user/login/constants';
|
import { LoginPageTypeEnum, PasswordRule } from '@/web/support/user/login/constants';
|
||||||
import { postRegister } from '@/web/support/user/api';
|
import { postRegister } from '@/web/support/user/api';
|
||||||
import { useSendCode } from '@/web/support/user/hooks/useSendCode';
|
import { useSendCode } from '@/web/support/user/hooks/useSendCode';
|
||||||
import type { ResLogin } from '@/global/support/api/userRes';
|
import type { ResLogin } from '@/global/support/api/userRes';
|
||||||
@@ -87,6 +87,18 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
refreshDeps: [loginSuccess, t, toast]
|
refreshDeps: [loginSuccess, t, toast]
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
const onSubmitErr = (err: Record<string, any>) => {
|
||||||
|
const val = Object.values(err)[0];
|
||||||
|
if (!val) return;
|
||||||
|
if (val.message) {
|
||||||
|
toast({
|
||||||
|
status: 'warning',
|
||||||
|
title: val.message,
|
||||||
|
duration: 3000,
|
||||||
|
isClosable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const placeholder = feConfigs?.register_method
|
const placeholder = feConfigs?.register_method
|
||||||
?.map((item) => {
|
?.map((item) => {
|
||||||
@@ -108,7 +120,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
mt={9}
|
mt={9}
|
||||||
onKeyDown={(e) => {
|
onKeyDown={(e) => {
|
||||||
if (e.key === 'Enter' && !e.shiftKey && !requesting) {
|
if (e.key === 'Enter' && !e.shiftKey && !requesting) {
|
||||||
handleSubmit(onclickRegister)();
|
handleSubmit(onclickRegister, onSubmitErr)();
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -151,16 +163,12 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
bg={'myGray.50'}
|
bg={'myGray.50'}
|
||||||
size={'lg'}
|
size={'lg'}
|
||||||
type={'password'}
|
type={'password'}
|
||||||
placeholder={t('user:password.new_password')}
|
placeholder={t('login:password_tip')}
|
||||||
{...register('password', {
|
{...register('password', {
|
||||||
required: t('user:password.password_required'),
|
required: true,
|
||||||
minLength: {
|
pattern: {
|
||||||
value: 4,
|
value: PasswordRule,
|
||||||
message: t('user:password.password_condition')
|
message: t('login:password_tip')
|
||||||
},
|
|
||||||
maxLength: {
|
|
||||||
value: 20,
|
|
||||||
message: t('user:password.password_condition')
|
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
></Input>
|
></Input>
|
||||||
@@ -175,7 +183,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
validate: (val) =>
|
validate: (val) =>
|
||||||
getValues('password') === val ? true : t('user:password.not_match')
|
getValues('password') === val ? true : t('user:password.not_match')
|
||||||
})}
|
})}
|
||||||
></Input>
|
/>
|
||||||
</FormControl>
|
</FormControl>
|
||||||
<Button
|
<Button
|
||||||
type="submit"
|
type="submit"
|
||||||
@@ -187,7 +195,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
|||||||
fontWeight={['medium', 'medium']}
|
fontWeight={['medium', 'medium']}
|
||||||
colorScheme="blue"
|
colorScheme="blue"
|
||||||
isLoading={requesting}
|
isLoading={requesting}
|
||||||
onClick={handleSubmit(onclickRegister)}
|
onClick={handleSubmit(onclickRegister, onSubmitErr)}
|
||||||
>
|
>
|
||||||
{t('user:register.confirm')}
|
{t('user:register.confirm')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ let isOauthLogging = false;
|
|||||||
|
|
||||||
const provider = () => {
|
const provider = () => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { loginStore } = useSystemStore();
|
const { initd, loginStore, setLoginStore } = useSystemStore();
|
||||||
const { setUserInfo } = useUserStore();
|
const { setUserInfo } = useUserStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { code, state, error } = router.query as { code: string; state: string; error?: string };
|
const { code, state, error } = router.query as { code: string; state: string; error?: string };
|
||||||
@@ -34,13 +34,9 @@ const provider = () => {
|
|||||||
|
|
||||||
const authCode = useCallback(
|
const authCode = useCallback(
|
||||||
async (code: string) => {
|
async (code: string) => {
|
||||||
if (!loginStore) {
|
|
||||||
router.replace('/login');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
const res = await oauthLogin({
|
const res = await oauthLogin({
|
||||||
type: loginStore?.provider as `${OAuthEnum}`,
|
type: loginStore?.provider || OAuthEnum.sso,
|
||||||
code,
|
code,
|
||||||
callbackUrl: `${location.origin}/login/provider`,
|
callbackUrl: `${location.origin}/login/provider`,
|
||||||
inviterId: localStorage.getItem('inviterId') || undefined,
|
inviterId: localStorage.getItem('inviterId') || undefined,
|
||||||
@@ -76,8 +72,9 @@ const provider = () => {
|
|||||||
router.replace('/login');
|
router.replace('/login');
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
setLoginStore(undefined);
|
||||||
},
|
},
|
||||||
[loginStore, loginSuccess, router, t, toast]
|
[loginStore?.provider, loginSuccess, router, setLoginStore, t, toast]
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -90,8 +87,8 @@ const provider = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('SSO', { loginStore, code, state });
|
console.log('SSO', { initd, loginStore, code, state });
|
||||||
if (!code || !loginStore) return;
|
if (!code || !initd) return;
|
||||||
|
|
||||||
if (isOauthLogging) return;
|
if (isOauthLogging) return;
|
||||||
|
|
||||||
@@ -101,7 +98,7 @@ const provider = () => {
|
|||||||
await clearToken();
|
await clearToken();
|
||||||
router.prefetch('/app/list');
|
router.prefetch('/app/list');
|
||||||
|
|
||||||
if (loginStore.provider !== OAuthEnum.sso && state !== loginStore?.state) {
|
if (loginStore && state !== loginStore.state) {
|
||||||
toast({
|
toast({
|
||||||
status: 'warning',
|
status: 'warning',
|
||||||
title: t('common:support.user.login.security_failed')
|
title: t('common:support.user.login.security_failed')
|
||||||
@@ -114,7 +111,7 @@ const provider = () => {
|
|||||||
authCode(code);
|
authCode(code);
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
}, [authCode, code, error, loginStore, loginStore?.state, router, state, t, toast]);
|
}, [initd, authCode, code, error, loginStore, loginStore?.state, router, state, t, toast]);
|
||||||
|
|
||||||
return <Loading />;
|
return <Loading />;
|
||||||
};
|
};
|
||||||
@@ -123,6 +120,8 @@ export default provider;
|
|||||||
|
|
||||||
export async function getServerSideProps(context: any) {
|
export async function getServerSideProps(context: any) {
|
||||||
return {
|
return {
|
||||||
props: { ...(await serviceSideProps(context)) }
|
props: {
|
||||||
|
...(await serviceSideProps(context))
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,30 +35,11 @@ export const uploadFile2DB = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getUploadBase64ImgController = (
|
|
||||||
props: CompressImgProps & UploadImgProps,
|
|
||||||
retry = 3
|
|
||||||
): Promise<string> => {
|
|
||||||
try {
|
|
||||||
return compressBase64ImgAndUpload({
|
|
||||||
maxW: 4000,
|
|
||||||
maxH: 4000,
|
|
||||||
maxSize: 1024 * 1024 * 5,
|
|
||||||
...props
|
|
||||||
});
|
|
||||||
} catch (error) {
|
|
||||||
if (retry > 0) {
|
|
||||||
return getUploadBase64ImgController(props, retry - 1);
|
|
||||||
}
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* compress image. response base64
|
* compress image. response base64
|
||||||
* @param maxSize The max size of the compressed image
|
* @param maxSize The max size of the compressed image
|
||||||
*/
|
*/
|
||||||
export const compressBase64ImgAndUpload = async ({
|
const compressBase64ImgAndUpload = async ({
|
||||||
base64Img,
|
base64Img,
|
||||||
maxW,
|
maxW,
|
||||||
maxH,
|
maxH,
|
||||||
@@ -89,7 +70,7 @@ export const compressImgFileAndUpload = async ({
|
|||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
|
|
||||||
const base64Img = await new Promise<string>((resolve, reject) => {
|
const base64Img = await new Promise<string>((resolve, reject) => {
|
||||||
reader.onload = async () => {
|
reader.onload = () => {
|
||||||
resolve(reader.result as string);
|
resolve(reader.result as string);
|
||||||
};
|
};
|
||||||
reader.onerror = (err) => {
|
reader.onerror = (err) => {
|
||||||
|
|||||||
@@ -3,12 +3,17 @@ import { Box } from '@chakra-ui/react';
|
|||||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||||
import { useI18n } from '@/web/context/I18n';
|
import { useI18n } from '@/web/context/I18n';
|
||||||
import { useMemoizedFn } from 'ahooks';
|
import { useMemoizedFn } from 'ahooks';
|
||||||
|
import { compressImgFileAndUpload } from '../controller';
|
||||||
|
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||||
|
import { useTranslation } from 'next-i18next';
|
||||||
|
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||||
|
|
||||||
export const useSelectFile = (props?: {
|
export const useSelectFile = (props?: {
|
||||||
fileType?: string;
|
fileType?: string;
|
||||||
multiple?: boolean;
|
multiple?: boolean;
|
||||||
maxCount?: number;
|
maxCount?: number;
|
||||||
}) => {
|
}) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
const { fileT } = useI18n();
|
const { fileT } = useI18n();
|
||||||
const { fileType = '*', multiple = false, maxCount = 10 } = props || {};
|
const { fileType = '*', multiple = false, maxCount = 10 } = props || {};
|
||||||
const { toast } = useToast();
|
const { toast } = useToast();
|
||||||
@@ -48,8 +53,44 @@ export const useSelectFile = (props?: {
|
|||||||
SelectFileDom.current && SelectFileDom.current.click();
|
SelectFileDom.current && SelectFileDom.current.click();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
const { runAsync: onSelectImage, loading } = useRequest2(
|
||||||
|
async (
|
||||||
|
e: File[],
|
||||||
|
{
|
||||||
|
maxW,
|
||||||
|
maxH,
|
||||||
|
callback
|
||||||
|
}: {
|
||||||
|
maxW?: number;
|
||||||
|
maxH?: number;
|
||||||
|
callback?: (e: string) => any;
|
||||||
|
}
|
||||||
|
) => {
|
||||||
|
const file = e[0];
|
||||||
|
if (!file) return Promise.resolve('Can not found image');
|
||||||
|
try {
|
||||||
|
const src = await compressImgFileAndUpload({
|
||||||
|
file,
|
||||||
|
maxW,
|
||||||
|
maxH
|
||||||
|
});
|
||||||
|
console.log(src, '--');
|
||||||
|
callback?.(src);
|
||||||
|
return src;
|
||||||
|
} catch (err: any) {
|
||||||
|
toast({
|
||||||
|
title: getErrText(err, t('common:error.upload_image_error')),
|
||||||
|
status: 'warning'
|
||||||
|
});
|
||||||
|
return Promise.reject(getErrText(err, t('common:error.upload_image_error')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
File,
|
File,
|
||||||
onOpen
|
onOpen,
|
||||||
|
onSelectImage,
|
||||||
|
loading
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,7 +27,8 @@ type State = {
|
|||||||
setLastAppListRouteType: (e?: string) => void;
|
setLastAppListRouteType: (e?: string) => void;
|
||||||
|
|
||||||
loginStore?: LoginStoreType;
|
loginStore?: LoginStoreType;
|
||||||
setLoginStore: (e: LoginStoreType) => void;
|
setLoginStore: (e?: LoginStoreType) => void;
|
||||||
|
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
setLoading: (val: boolean) => null;
|
setLoading: (val: boolean) => null;
|
||||||
gitStar: number;
|
gitStar: number;
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ export const postCreateDatasetFolder = (data: DatasetFolderCreateBody) =>
|
|||||||
export const resumeInheritPer = (datasetId: string) =>
|
export const resumeInheritPer = (datasetId: string) =>
|
||||||
GET(`/core/dataset/resumeInheritPermission`, { datasetId });
|
GET(`/core/dataset/resumeInheritPermission`, { datasetId });
|
||||||
|
|
||||||
export const changeOwner = (data: { ownerId: string; datasetId: string }) =>
|
export const postChangeOwner = (data: { ownerId: string; datasetId: string }) =>
|
||||||
POST(`/proApi/core/dataset/changeOwner`, data);
|
POST(`/proApi/core/dataset/changeOwner`, data);
|
||||||
|
|
||||||
/* =========== search test ============ */
|
/* =========== search test ============ */
|
||||||
|
|||||||
@@ -4,3 +4,6 @@ export enum LoginPageTypeEnum {
|
|||||||
forgetPassword = 'forgetPassword',
|
forgetPassword = 'forgetPassword',
|
||||||
wechat = 'wechat'
|
wechat = 'wechat'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const PasswordRule =
|
||||||
|
/^(?:(?=.*\d)(?=.*[a-z])|(?=.*\d)(?=.*[A-Z])|(?=.*\d)(?=.*[!@#$%^&*_])|(?=.*[a-z])(?=.*[A-Z])|(?=.*[a-z])(?=.*[!@#$%^&*_])|(?=.*[A-Z])(?=.*[!@#$%^&*_]))[\dA-Za-z!@#$%^&*_]{6,}$/;
|
||||||
|
|||||||
Reference in New Issue
Block a user