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:
@@ -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 MyModal from '@fastgpt/web/components/common/MyModal/index';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
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 { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
|
||||
export type EditResourceInfoFormType = {
|
||||
id: string;
|
||||
@@ -31,7 +27,6 @@ const EditResourceModal = ({
|
||||
onEdit: (data: EditResourceInfoFormType) => any;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const { register, watch, setValue, handleSubmit } = useForm<EditResourceInfoFormType>({
|
||||
defaultValues: defaultForm
|
||||
});
|
||||
@@ -46,31 +41,14 @@ const EditResourceModal = ({
|
||||
}
|
||||
);
|
||||
|
||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||
const {
|
||||
File,
|
||||
onOpen: onOpenSelectFile,
|
||||
onSelectImage
|
||||
} = useSelectFile({
|
||||
fileType: '.jpg,.png',
|
||||
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 (
|
||||
<MyModal isOpen onClose={onClose} iconSrc={avatar} title={title}>
|
||||
@@ -108,7 +86,15 @@ const EditResourceModal = ({
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
<File onSelect={onSelectFile} />
|
||||
<File
|
||||
onSelect={(e) =>
|
||||
onSelectImage(e, {
|
||||
maxH: 300,
|
||||
maxW: 300,
|
||||
callback: (e) => setValue('avatar', e)
|
||||
})
|
||||
}
|
||||
/>
|
||||
</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 { useTranslation } from 'next-i18next';
|
||||
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 { PasswordRule } from '@/web/support/user/login/constants';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
|
||||
type FormType = {
|
||||
oldPsw: string;
|
||||
@@ -14,7 +16,9 @@ type FormType = {
|
||||
|
||||
const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const { t } = useTranslation();
|
||||
const { register, handleSubmit } = useForm<FormType>({
|
||||
const { toast } = useToast();
|
||||
|
||||
const { register, handleSubmit, getValues } = useForm<FormType>({
|
||||
defaultValues: {
|
||||
oldPsw: '',
|
||||
newPsw: '',
|
||||
@@ -22,19 +26,25 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
||||
}
|
||||
});
|
||||
|
||||
const { mutate: onSubmit, isLoading } = useRequest({
|
||||
mutationFn: (data: FormType) => {
|
||||
if (data.newPsw !== data.confirmPsw) {
|
||||
return Promise.reject(t('account_info:password_mismatch'));
|
||||
}
|
||||
return updatePasswordByOld(data);
|
||||
},
|
||||
const { runAsync: onSubmit, loading: isLoading } = useRequest2(updatePasswordByOld, {
|
||||
onSuccess() {
|
||||
onClose();
|
||||
},
|
||||
successToast: t('account_info:password_update_success'),
|
||||
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 (
|
||||
<MyModal
|
||||
@@ -45,34 +55,39 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
||||
>
|
||||
<ModalBody>
|
||||
<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>
|
||||
</Flex>
|
||||
<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
|
||||
flex={1}
|
||||
type={'password'}
|
||||
placeholder={t('account_info:password_tip')}
|
||||
{...register('newPsw', {
|
||||
required: true,
|
||||
maxLength: {
|
||||
value: 60,
|
||||
message: t('account_info:password_length_error')
|
||||
pattern: {
|
||||
value: PasswordRule,
|
||||
message: t('account_info:password_tip')
|
||||
}
|
||||
})}
|
||||
></Input>
|
||||
</Flex>
|
||||
<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
|
||||
flex={1}
|
||||
type={'password'}
|
||||
placeholder={t('user:password.confirm')}
|
||||
{...register('confirmPsw', {
|
||||
required: true,
|
||||
maxLength: {
|
||||
value: 60,
|
||||
message: t('account_info:password_length_error')
|
||||
}
|
||||
validate: (val) => (getValues('newPsw') === val ? true : t('user:password.not_match'))
|
||||
})}
|
||||
></Input>
|
||||
</Flex>
|
||||
@@ -81,7 +96,7 @@ const UpdatePswModal = ({ onClose }: { onClose: () => void }) => {
|
||||
<Button mr={3} variant={'whiteBase'} onClick={onClose}>
|
||||
{t('account_info:cancel')}
|
||||
</Button>
|
||||
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data))}>
|
||||
<Button isLoading={isLoading} onClick={handleSubmit((data) => onSubmit(data), onSubmitErr)}>
|
||||
{t('account_info:confirm')}
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
@@ -20,7 +20,6 @@ import type { UserType } from '@fastgpt/global/support/user/type.d';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
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 { putUpdateMemberName } from '@/web/support/user/team/api';
|
||||
import { getDocPath } from '@/web/common/system/doc';
|
||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||
import {
|
||||
StandardSubLevelEnum,
|
||||
standardSubLevelMap
|
||||
@@ -131,7 +129,11 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
|
||||
onClose: onCloseUpdateNotification,
|
||||
onOpen: onOpenUpdateNotification
|
||||
} = useDisclosure();
|
||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||
const {
|
||||
File,
|
||||
onOpen: onOpenSelectFile,
|
||||
onSelectImage
|
||||
} = useSelectFile({
|
||||
fileType: '.jpg,.png',
|
||||
multiple: false
|
||||
});
|
||||
@@ -151,32 +153,6 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
|
||||
[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 = {
|
||||
flex: '0 0 80px',
|
||||
fontSize: 'sm',
|
||||
@@ -329,7 +305,21 @@ const MyInfo = ({ onOpenContact }: { onOpenContact: () => void }) => {
|
||||
)}
|
||||
{isOpenUpdatePsw && <UpdatePswModal onClose={onCloseUpdatePsw} />}
|
||||
{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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
import React, { useCallback } from 'react';
|
||||
import React from 'react';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { getErrText } from '@fastgpt/global/common/error/utils';
|
||||
import { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
import MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
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 { postCreateTeam, putUpdateTeam } from '@/web/support/user/team/api';
|
||||
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';
|
||||
|
||||
export type EditTeamFormDataType = CreateTeamProps & {
|
||||
@@ -41,33 +38,15 @@ function EditModal({
|
||||
});
|
||||
const avatar = watch('avatar');
|
||||
|
||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||
const {
|
||||
File,
|
||||
onOpen: onOpenSelectFile,
|
||||
onSelectImage
|
||||
} = useSelectFile({
|
||||
fileType: '.jpg,.png,.svg',
|
||||
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({
|
||||
mutationFn: async (data: CreateTeamProps) => {
|
||||
return postCreateTeam(data);
|
||||
@@ -154,7 +133,15 @@ function EditModal({
|
||||
</Button>
|
||||
)}
|
||||
</ModalFooter>
|
||||
<File onSelect={onSelectFile} />
|
||||
<File
|
||||
onSelect={(e) =>
|
||||
onSelectImage(e, {
|
||||
maxH: 300,
|
||||
maxW: 300,
|
||||
callback: (e) => setValue('avatar', e)
|
||||
})
|
||||
}
|
||||
/>
|
||||
</MyModal>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,9 +6,7 @@ import FormLabel from '@fastgpt/web/components/common/MyBox/FormLabel';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import React, { useMemo } from 'react';
|
||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { TeamContext } from '../context';
|
||||
@@ -23,7 +21,11 @@ export type GroupFormType = {
|
||||
function GroupInfoModal({ onClose, editGroupId }: { onClose: () => void; editGroupId?: string }) {
|
||||
const { refetchGroups, groups, refetchMembers } = useContextSelector(TeamContext, (v) => v);
|
||||
const { t } = useTranslation();
|
||||
const { File: AvatarSelect, onOpen: onOpenSelectAvatar } = useSelectFile({
|
||||
const {
|
||||
File: AvatarSelect,
|
||||
onOpen: onOpenSelectAvatar,
|
||||
onSelectImage
|
||||
} = useSelectFile({
|
||||
fileType: '.jpg, .jpeg, .png',
|
||||
multiple: false
|
||||
});
|
||||
@@ -41,13 +43,10 @@ function GroupInfoModal({ onClose, editGroupId }: { onClose: () => void; editGro
|
||||
|
||||
const { loading: uploadingAvatar, run: onSelectAvatar } = useRequest2(
|
||||
async (file: File[]) => {
|
||||
const src = await compressImgFileAndUpload({
|
||||
type: MongoImageTypeEnum.groupAvatar,
|
||||
file: file[0],
|
||||
return onSelectImage(file, {
|
||||
maxW: 300,
|
||||
maxH: 300
|
||||
});
|
||||
return src;
|
||||
},
|
||||
{
|
||||
onSuccess: (src: string) => {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
import { postCreateOrg, putUpdateOrg } from '@/web/support/user/team/org/api';
|
||||
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 Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
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',
|
||||
multiple: false
|
||||
});
|
||||
const { loading: uploadingAvatar, run: onSelectAvatar } = useRequest2(
|
||||
async (file: File[]) => {
|
||||
const src = await compressImgFileAndUpload({
|
||||
type: MongoImageTypeEnum.groupAvatar,
|
||||
file: file[0],
|
||||
return onSelectImage(file, {
|
||||
maxW: 300,
|
||||
maxH: 300
|
||||
});
|
||||
return src;
|
||||
},
|
||||
{
|
||||
onSuccess: (src: string) => {
|
||||
|
||||
@@ -1,38 +1,30 @@
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { jsonRes } from '@fastgpt/service/common/response';
|
||||
import { connectToDatabase } from '@/service/mongo';
|
||||
import { uploadMongoImg } from '@fastgpt/service/common/file/image/controller';
|
||||
import { UploadImgProps } from '@fastgpt/global/common/file/api';
|
||||
import { authCert } from '@fastgpt/service/support/permission/auth/common';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
|
||||
/*
|
||||
Upload avatar image
|
||||
*/
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
try {
|
||||
await connectToDatabase();
|
||||
const body = req.body as UploadImgProps;
|
||||
async function handler(req: NextApiRequest, res: NextApiResponse): Promise<string> {
|
||||
await connectToDatabase();
|
||||
const body = req.body as UploadImgProps;
|
||||
|
||||
const { teamId } = await authCert({ req, authToken: true });
|
||||
const { teamId } = await authCert({ req, authToken: true });
|
||||
|
||||
const imgId = await uploadMongoImg({
|
||||
teamId,
|
||||
...body
|
||||
});
|
||||
|
||||
jsonRes(res, { data: imgId });
|
||||
} catch (error) {
|
||||
jsonRes(res, {
|
||||
code: 500,
|
||||
error
|
||||
});
|
||||
}
|
||||
return uploadMongoImg({
|
||||
teamId,
|
||||
...body
|
||||
});
|
||||
}
|
||||
export default NextAPI(handler);
|
||||
|
||||
export const config = {
|
||||
api: {
|
||||
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 { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
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 { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
|
||||
|
||||
export type CreateAppBody = {
|
||||
parentId?: ParentIdType;
|
||||
@@ -148,6 +148,8 @@ export const onCreateApp = async ({
|
||||
);
|
||||
}
|
||||
|
||||
await refreshSourceAvatar(avatar, undefined, session);
|
||||
|
||||
return appId;
|
||||
};
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ import { ClientSession } from '@fastgpt/service/common/mongo';
|
||||
import { deleteChatFiles } from '@fastgpt/service/core/chat/controller';
|
||||
import { pushTrack } from '@fastgpt/service/common/middle/tracks/utils';
|
||||
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>) {
|
||||
const { appId } = req.query as { appId: string };
|
||||
@@ -57,7 +58,7 @@ export const onDelOneApp = async ({
|
||||
const apps = await findAppAndAllChildren({
|
||||
teamId,
|
||||
appId,
|
||||
fields: '_id'
|
||||
fields: '_id avatar'
|
||||
});
|
||||
|
||||
const del = async (session: ClientSession) => {
|
||||
@@ -109,6 +110,8 @@ export const onDelOneApp = async ({
|
||||
},
|
||||
{ session }
|
||||
);
|
||||
|
||||
await removeImageByPath(app.avatar, session);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { MongoApp } from '@fastgpt/service/core/app/schema';
|
||||
import { isEqual } from 'lodash';
|
||||
import { onCreateApp } from '../create';
|
||||
import { onDelOneApp } from '../del';
|
||||
import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
|
||||
|
||||
export type UpdateHttpPluginBody = {
|
||||
appId: string;
|
||||
@@ -49,13 +50,15 @@ async function handler(req: ApiRequestProps<UpdateHttpPluginBody>, res: NextApiR
|
||||
await MongoApp.findByIdAndUpdate(
|
||||
appId,
|
||||
{
|
||||
name,
|
||||
avatar,
|
||||
intro,
|
||||
...(name && { name }),
|
||||
...(avatar && { avatar }),
|
||||
...(intro !== undefined && { intro }),
|
||||
pluginData
|
||||
},
|
||||
{ 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 { TeamWritePermissionVal } from '@fastgpt/global/support/permission/user/constant';
|
||||
import { AppErrEnum } from '@fastgpt/global/common/error/code/app';
|
||||
import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
|
||||
|
||||
export type AppUpdateQuery = {
|
||||
appId: string;
|
||||
@@ -95,6 +96,8 @@ async function handler(req: ApiRequestProps<AppUpdateBody, AppUpdateQuery>) {
|
||||
isPlugin: app.type === AppTypeEnum.plugin
|
||||
});
|
||||
|
||||
await refreshSourceAvatar(avatar, app.avatar, session);
|
||||
|
||||
return MongoApp.findByIdAndUpdate(
|
||||
appId,
|
||||
{
|
||||
|
||||
@@ -11,6 +11,8 @@ import type { ApiRequestProps } from '@fastgpt/service/type/next';
|
||||
import { parseParentIdInMongo } from '@fastgpt/global/common/parentFolder/utils';
|
||||
import { authDataset } from '@fastgpt/service/support/permission/dataset/auth';
|
||||
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 DatasetCreateBody = CreateDatasetParams;
|
||||
@@ -63,19 +65,29 @@ async function handler(
|
||||
// check limit
|
||||
await checkTeamDatasetLimit(teamId);
|
||||
|
||||
const { _id } = await MongoDataset.create({
|
||||
...parseParentIdInMongo(parentId),
|
||||
name,
|
||||
intro,
|
||||
teamId,
|
||||
tmbId,
|
||||
vectorModel,
|
||||
agentModel,
|
||||
avatar,
|
||||
type,
|
||||
apiServer,
|
||||
feishuServer,
|
||||
yuqueServer
|
||||
const datasetId = await mongoSessionRun(async (session) => {
|
||||
const [{ _id }] = await MongoDataset.create(
|
||||
[
|
||||
{
|
||||
...parseParentIdInMongo(parentId),
|
||||
name,
|
||||
intro,
|
||||
teamId,
|
||||
tmbId,
|
||||
vectorModel,
|
||||
agentModel,
|
||||
avatar,
|
||||
type,
|
||||
apiServer,
|
||||
feishuServer,
|
||||
yuqueServer
|
||||
}
|
||||
],
|
||||
{ session }
|
||||
);
|
||||
await refreshSourceAvatar(avatar, undefined, session);
|
||||
|
||||
return _id;
|
||||
});
|
||||
|
||||
pushTrack.createDataset({
|
||||
@@ -85,6 +97,6 @@ async function handler(
|
||||
uid: userId
|
||||
});
|
||||
|
||||
return _id;
|
||||
return datasetId;
|
||||
}
|
||||
export default NextAPI(handler);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { NextAPI } from '@/service/middleware/entry';
|
||||
import { OwnerPermissionVal } from '@fastgpt/global/support/permission/constant';
|
||||
import { CommonErrEnum } from '@fastgpt/global/common/error/code/common';
|
||||
import { MongoDatasetCollectionTags } from '@fastgpt/service/core/dataset/tag/schema';
|
||||
import { removeImageByPath } from '@fastgpt/service/common/file/image/controller';
|
||||
|
||||
async function handler(req: NextApiRequest) {
|
||||
const { id: datasetId } = req.query as {
|
||||
@@ -51,6 +52,10 @@ async function handler(req: NextApiRequest) {
|
||||
},
|
||||
{ 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 { MongoDatasetCollection } from '@fastgpt/service/core/dataset/collection/schema';
|
||||
import { addDays } from 'date-fns';
|
||||
import { refreshSourceAvatar } from '@fastgpt/service/common/file/image/controller';
|
||||
|
||||
export type DatasetUpdateQuery = {};
|
||||
export type DatasetUpdateResponse = any;
|
||||
@@ -144,6 +145,8 @@ async function handler(
|
||||
autoSync,
|
||||
session
|
||||
});
|
||||
|
||||
await refreshSourceAvatar(avatar, dataset.avatar, session);
|
||||
};
|
||||
|
||||
await mongoSessionRun(async (session) => {
|
||||
|
||||
@@ -7,12 +7,14 @@ import { UserStatusEnum } from '@fastgpt/global/support/user/constant';
|
||||
import { NextAPI } from '@/service/middleware/entry';
|
||||
import { useReqFrequencyLimit } from '@fastgpt/service/common/middle/reqFrequencyLimit';
|
||||
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) {
|
||||
const { username, password } = req.body as PostLoginProps;
|
||||
|
||||
if (!username || !password) {
|
||||
throw new Error('缺少参数');
|
||||
return Promise.reject(CommonErrEnum.invalidParams);
|
||||
}
|
||||
|
||||
// 检测用户是否存在
|
||||
@@ -23,11 +25,11 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
'status'
|
||||
);
|
||||
if (!authCert) {
|
||||
throw new Error('用户未注册');
|
||||
return Promise.reject(UserErrEnum.unAuthUser);
|
||||
}
|
||||
|
||||
if (authCert.status === UserStatusEnum.forbidden) {
|
||||
throw new Error('账号已停用,无法登录');
|
||||
return Promise.reject('Invalid account!');
|
||||
}
|
||||
|
||||
const user = await MongoUser.findOne({
|
||||
@@ -36,7 +38,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
throw new Error('密码错误');
|
||||
return Promise.reject(UserErrEnum.binVisitor);
|
||||
}
|
||||
|
||||
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 */
|
||||
import type { ApiRequestProps, ApiResponseType } from '@fastgpt/service/type/next';
|
||||
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 UserAccountUpdateBody = UserUpdateParams;
|
||||
export type UserAccountUpdateResponse = {};
|
||||
@@ -16,22 +19,22 @@ async function handler(
|
||||
const { avatar, timezone } = req.body;
|
||||
|
||||
const { tmbId } = await authCert({ req, authToken: true });
|
||||
const tmb = await MongoTeamMember.findById(tmbId);
|
||||
if (!tmb) {
|
||||
throw new Error('can not find it');
|
||||
}
|
||||
const userId = tmb.userId;
|
||||
const user = await getUserDetail({ tmbId });
|
||||
|
||||
// 更新对应的记录
|
||||
await MongoUser.updateOne(
|
||||
{
|
||||
_id: userId
|
||||
},
|
||||
{
|
||||
...(avatar && { avatar }),
|
||||
...(timezone && { timezone })
|
||||
}
|
||||
);
|
||||
await mongoSessionRun(async (session) => {
|
||||
await MongoUser.updateOne(
|
||||
{
|
||||
_id: user._id
|
||||
},
|
||||
{
|
||||
...(avatar && { avatar }),
|
||||
...(timezone && { timezone })
|
||||
}
|
||||
).session(session);
|
||||
|
||||
await refreshSourceAvatar(avatar, user.avatar, session);
|
||||
});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import CollaboratorContextProvider from '@/components/support/permission/MemberManager/context';
|
||||
import ResumeInherit from '@/components/support/permission/ResumeInheritText';
|
||||
import { AppContext } from '@/pages/app/detail/components/context';
|
||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
import { resumeInheritPer } from '@/web/core/app/api';
|
||||
@@ -20,8 +19,6 @@ import {
|
||||
ModalFooter,
|
||||
Textarea
|
||||
} 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 { AppSchema } from '@fastgpt/global/core/app/type.d';
|
||||
import { AppPermissionList } from '@fastgpt/global/support/permission/app/constant';
|
||||
@@ -42,7 +39,11 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const { toast } = useToast();
|
||||
const { updateAppDetail, appDetail, reloadApp } = useContextSelector(AppContext, (v) => v);
|
||||
|
||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||
const {
|
||||
File,
|
||||
onOpen: onOpenSelectFile,
|
||||
onSelectImage
|
||||
} = useSelectFile({
|
||||
fileType: '.jpg,.png',
|
||||
multiple: false
|
||||
});
|
||||
@@ -101,28 +102,6 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
||||
[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 = ({
|
||||
members,
|
||||
groups,
|
||||
@@ -273,7 +252,15 @@ const InfoModal = ({ onClose }: { onClose: () => void }) => {
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
|
||||
<File onSelect={onSelectFile} />
|
||||
<File
|
||||
onSelect={(e) =>
|
||||
onSelectImage(e, {
|
||||
maxH: 300,
|
||||
maxW: 300,
|
||||
callback: (e) => setValue('avatar', e)
|
||||
})
|
||||
}
|
||||
/>
|
||||
</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 { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
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 { useRouter } from 'next/router';
|
||||
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 MyModal from '@fastgpt/web/components/common/MyModal';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||
import { useContextSelector } from 'use-context-selector';
|
||||
import { AppListContext } from './context';
|
||||
import { AppTypeEnum } from '@fastgpt/global/core/app/constants';
|
||||
@@ -25,7 +21,6 @@ import {
|
||||
getTemplateMarketItemList
|
||||
} from '@/web/core/app/api/template';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { AppTemplateSchemaType } from '@fastgpt/global/core/app/type';
|
||||
|
||||
type FormType = {
|
||||
avatar: string;
|
||||
@@ -44,7 +39,6 @@ const CreateModal = ({
|
||||
onOpenTemplateModal: (type: AppTypeEnum) => void;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { toast } = useToast();
|
||||
const router = useRouter();
|
||||
const { parentId, loadMyApps } = useContextSelector(AppListContext, (v) => v);
|
||||
const { isPc } = useSystem();
|
||||
@@ -86,33 +80,15 @@ const CreateModal = ({
|
||||
});
|
||||
const avatar = watch('avatar');
|
||||
|
||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||
const {
|
||||
File,
|
||||
onOpen: onOpenSelectFile,
|
||||
onSelectImage
|
||||
} = useSelectFile({
|
||||
fileType: '.jpg,.png',
|
||||
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(
|
||||
async (data: FormType, templateId?: string) => {
|
||||
if (!templateId) {
|
||||
@@ -290,7 +266,15 @@ const CreateModal = ({
|
||||
))}
|
||||
</Grid>
|
||||
</ModalBody>
|
||||
<File onSelect={onSelectFile} />
|
||||
<File
|
||||
onSelect={(e) =>
|
||||
onSelectImage(e, {
|
||||
maxH: 300,
|
||||
maxW: 300,
|
||||
callback: (e) => setValue('avatar', e)
|
||||
})
|
||||
}
|
||||
/>
|
||||
</MyModal>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -17,14 +17,12 @@ import {
|
||||
} from '@chakra-ui/react';
|
||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
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 { useRequest } from '@fastgpt/web/hooks/useRequest';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
|
||||
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 {
|
||||
postCreateHttpPlugin,
|
||||
putUpdateHttpPlugin,
|
||||
@@ -124,33 +122,15 @@ const HttpPluginEditModal = ({
|
||||
errorToast: t('common:common.Update Failed')
|
||||
});
|
||||
|
||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||
const {
|
||||
File,
|
||||
onOpen: onOpenSelectFile,
|
||||
onSelectImage
|
||||
} = useSelectFile({
|
||||
fileType: 'image/*',
|
||||
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 */
|
||||
const { mutate: onClickUrlLoadApi, isLoading: isLoadingUrlApi } = useRequest({
|
||||
mutationFn: async () => {
|
||||
@@ -473,7 +453,15 @@ const HttpPluginEditModal = ({
|
||||
)}
|
||||
</ModalFooter>
|
||||
</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 { Box, Flex, Switch, Input } from '@chakra-ui/react';
|
||||
import { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
import { useConfirm } from '@fastgpt/web/hooks/useConfirm';
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { compressImgFileAndUpload } from '@/web/common/file/controller';
|
||||
import type { DatasetItemType } from '@fastgpt/global/core/dataset/type.d';
|
||||
import Avatar from '@fastgpt/web/components/common/Avatar';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||
import AIModelSelector from '@/components/Select/AIModelSelector';
|
||||
import { postRebuildEmbedding } from '@/web/core/dataset/api';
|
||||
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')
|
||||
});
|
||||
|
||||
const { File } = useSelectFile({
|
||||
fileType: '.jpg,.png',
|
||||
multiple: false
|
||||
});
|
||||
|
||||
const { runAsync: onSave } = useRequest2(
|
||||
(data: DatasetItemType) => {
|
||||
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(
|
||||
(vectorModel: VectorModelItemType) => {
|
||||
return postRebuildEmbedding({
|
||||
@@ -416,7 +387,6 @@ const Info = ({ datasetId }: { datasetId: string }) => {
|
||||
</>
|
||||
)}
|
||||
|
||||
<File onSelect={onSelectFile} />
|
||||
<ConfirmDelModal />
|
||||
<ConfirmRebuildModal countDown={10} />
|
||||
<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 { useSelectFile } from '@/web/common/file/hooks/useSelectFile';
|
||||
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 { useRouter } from 'next/router';
|
||||
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 { useTranslation } from 'next-i18next';
|
||||
import { DatasetTypeEnum } from '@fastgpt/global/core/dataset/constants';
|
||||
import { MongoImageTypeEnum } from '@fastgpt/global/common/file/image/constants';
|
||||
import AIModelSelector from '@/components/Select/AIModelSelector';
|
||||
import { useSystem } from '@fastgpt/web/hooks/useSystem';
|
||||
import QuestionTip from '@fastgpt/web/components/common/MyTooltip/QuestionTip';
|
||||
@@ -90,33 +87,15 @@ const CreateModal = ({
|
||||
const vectorModel = watch('vectorModel');
|
||||
const agentModel = watch('agentModel');
|
||||
|
||||
const { File, onOpen: onOpenSelectFile } = useSelectFile({
|
||||
fileType: '.jpg,.png',
|
||||
const {
|
||||
File,
|
||||
onOpen: onOpenSelectFile,
|
||||
onSelectImage
|
||||
} = useSelectFile({
|
||||
fileType: 'image/*',
|
||||
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 */
|
||||
const { run: onclickCreate, loading: creating } = useRequest2(
|
||||
async (data: CreateDatasetParams) => await postCreateDataset(data),
|
||||
@@ -275,7 +254,15 @@ const CreateModal = ({
|
||||
|
||||
<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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 { DatasetTypeEnum, DatasetTypeMap } from '@fastgpt/global/core/dataset/constants';
|
||||
import MyMenu from '@fastgpt/web/components/common/MyMenu';
|
||||
@@ -423,7 +423,7 @@ function List() {
|
||||
{!!editPerDataset && (
|
||||
<ConfigPerModal
|
||||
onChangeOwner={(tmbId: string) =>
|
||||
changeOwner({
|
||||
postChangeOwner({
|
||||
datasetId: editPerDataset._id,
|
||||
ownerId: tmbId
|
||||
}).then(() => loadMyDatasets())
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Dispatch } from 'react';
|
||||
import { FormControl, Box, Input, Button } from '@chakra-ui/react';
|
||||
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 { useSendCode } from '@/web/support/user/hooks/useSendCode';
|
||||
import type { ResLogin } from '@/global/support/api/userRes.d';
|
||||
@@ -70,6 +70,18 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
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 (
|
||||
<>
|
||||
@@ -79,8 +91,8 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
<Box
|
||||
mt={9}
|
||||
onKeyDown={(e) => {
|
||||
if (e.keyCode === 13 && !e.shiftKey && !requesting) {
|
||||
handleSubmit(onclickFindPassword)();
|
||||
if (e.key === 'Enter' && !e.shiftKey && !requesting) {
|
||||
handleSubmit(onclickFindPassword, onSubmitErr)();
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -123,16 +135,12 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
bg={'myGray.50'}
|
||||
type={'password'}
|
||||
size={'lg'}
|
||||
placeholder={t('user:password.new_password')}
|
||||
placeholder={t('login:password_tip')}
|
||||
{...register('password', {
|
||||
required: t('user:password.password_required'),
|
||||
minLength: {
|
||||
value: 4,
|
||||
message: t('user:password.password_condition')
|
||||
},
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: t('user:password.password_condition')
|
||||
required: true,
|
||||
pattern: {
|
||||
value: PasswordRule,
|
||||
message: t('login:password_tip')
|
||||
}
|
||||
})}
|
||||
></Input>
|
||||
@@ -160,7 +168,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
fontWeight={['medium', 'medium']}
|
||||
colorScheme="blue"
|
||||
isLoading={requesting}
|
||||
onClick={handleSubmit(onclickFindPassword)}
|
||||
onClick={handleSubmit(onclickFindPassword, onSubmitErr)}
|
||||
>
|
||||
{t('user:password.retrieve')}
|
||||
</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 { useForm } from 'react-hook-form';
|
||||
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 { useTranslation } from 'next-i18next';
|
||||
import FormLayout from './components/FormLayout';
|
||||
import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
||||
|
||||
interface Props {
|
||||
setPageType: Dispatch<`${LoginPageTypeEnum}`>;
|
||||
@@ -30,31 +31,22 @@ const LoginForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
formState: { errors }
|
||||
} = useForm<LoginFormType>();
|
||||
|
||||
const [requesting, setRequesting] = useState(false);
|
||||
|
||||
const onclickLogin = useCallback(
|
||||
const { runAsync: onclickLogin, loading: requesting } = useRequest2(
|
||||
async ({ username, password }: LoginFormType) => {
|
||||
setRequesting(true);
|
||||
try {
|
||||
loginSuccess(
|
||||
await postLogin({
|
||||
username,
|
||||
password
|
||||
})
|
||||
);
|
||||
toast({
|
||||
title: t('login:login_success'),
|
||||
status: 'success'
|
||||
});
|
||||
} catch (error: any) {
|
||||
toast({
|
||||
title: error.message || t('login:login_failed'),
|
||||
status: 'error'
|
||||
});
|
||||
}
|
||||
setRequesting(false);
|
||||
loginSuccess(
|
||||
await postLogin({
|
||||
username,
|
||||
password
|
||||
})
|
||||
);
|
||||
toast({
|
||||
title: t('login:login_success'),
|
||||
status: 'success'
|
||||
});
|
||||
},
|
||||
[loginSuccess, t, toast]
|
||||
{
|
||||
refreshDeps: [loginSuccess]
|
||||
}
|
||||
);
|
||||
|
||||
const isCommunityVersion = !!(feConfigs?.register_method && !feConfigs?.isPlus);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React, { Dispatch } from 'react';
|
||||
import { FormControl, Box, Input, Button } from '@chakra-ui/react';
|
||||
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 { useSendCode } from '@/web/support/user/hooks/useSendCode';
|
||||
import type { ResLogin } from '@/global/support/api/userRes';
|
||||
@@ -87,6 +87,18 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
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
|
||||
?.map((item) => {
|
||||
@@ -108,7 +120,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
mt={9}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && !e.shiftKey && !requesting) {
|
||||
handleSubmit(onclickRegister)();
|
||||
handleSubmit(onclickRegister, onSubmitErr)();
|
||||
}
|
||||
}}
|
||||
>
|
||||
@@ -151,16 +163,12 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
bg={'myGray.50'}
|
||||
size={'lg'}
|
||||
type={'password'}
|
||||
placeholder={t('user:password.new_password')}
|
||||
placeholder={t('login:password_tip')}
|
||||
{...register('password', {
|
||||
required: t('user:password.password_required'),
|
||||
minLength: {
|
||||
value: 4,
|
||||
message: t('user:password.password_condition')
|
||||
},
|
||||
maxLength: {
|
||||
value: 20,
|
||||
message: t('user:password.password_condition')
|
||||
required: true,
|
||||
pattern: {
|
||||
value: PasswordRule,
|
||||
message: t('login:password_tip')
|
||||
}
|
||||
})}
|
||||
></Input>
|
||||
@@ -175,7 +183,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
validate: (val) =>
|
||||
getValues('password') === val ? true : t('user:password.not_match')
|
||||
})}
|
||||
></Input>
|
||||
/>
|
||||
</FormControl>
|
||||
<Button
|
||||
type="submit"
|
||||
@@ -187,7 +195,7 @@ const RegisterForm = ({ setPageType, loginSuccess }: Props) => {
|
||||
fontWeight={['medium', 'medium']}
|
||||
colorScheme="blue"
|
||||
isLoading={requesting}
|
||||
onClick={handleSubmit(onclickRegister)}
|
||||
onClick={handleSubmit(onclickRegister, onSubmitErr)}
|
||||
>
|
||||
{t('user:register.confirm')}
|
||||
</Button>
|
||||
|
||||
@@ -16,7 +16,7 @@ let isOauthLogging = false;
|
||||
|
||||
const provider = () => {
|
||||
const { t } = useTranslation();
|
||||
const { loginStore } = useSystemStore();
|
||||
const { initd, loginStore, setLoginStore } = useSystemStore();
|
||||
const { setUserInfo } = useUserStore();
|
||||
const router = useRouter();
|
||||
const { code, state, error } = router.query as { code: string; state: string; error?: string };
|
||||
@@ -34,13 +34,9 @@ const provider = () => {
|
||||
|
||||
const authCode = useCallback(
|
||||
async (code: string) => {
|
||||
if (!loginStore) {
|
||||
router.replace('/login');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await oauthLogin({
|
||||
type: loginStore?.provider as `${OAuthEnum}`,
|
||||
type: loginStore?.provider || OAuthEnum.sso,
|
||||
code,
|
||||
callbackUrl: `${location.origin}/login/provider`,
|
||||
inviterId: localStorage.getItem('inviterId') || undefined,
|
||||
@@ -76,8 +72,9 @@ const provider = () => {
|
||||
router.replace('/login');
|
||||
}, 1000);
|
||||
}
|
||||
setLoginStore(undefined);
|
||||
},
|
||||
[loginStore, loginSuccess, router, t, toast]
|
||||
[loginStore?.provider, loginSuccess, router, setLoginStore, t, toast]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
@@ -90,8 +87,8 @@ const provider = () => {
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('SSO', { loginStore, code, state });
|
||||
if (!code || !loginStore) return;
|
||||
console.log('SSO', { initd, loginStore, code, state });
|
||||
if (!code || !initd) return;
|
||||
|
||||
if (isOauthLogging) return;
|
||||
|
||||
@@ -101,7 +98,7 @@ const provider = () => {
|
||||
await clearToken();
|
||||
router.prefetch('/app/list');
|
||||
|
||||
if (loginStore.provider !== OAuthEnum.sso && state !== loginStore?.state) {
|
||||
if (loginStore && state !== loginStore.state) {
|
||||
toast({
|
||||
status: 'warning',
|
||||
title: t('common:support.user.login.security_failed')
|
||||
@@ -114,7 +111,7 @@ const provider = () => {
|
||||
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 />;
|
||||
};
|
||||
@@ -123,6 +120,8 @@ export default provider;
|
||||
|
||||
export async function getServerSideProps(context: any) {
|
||||
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
|
||||
* @param maxSize The max size of the compressed image
|
||||
*/
|
||||
export const compressBase64ImgAndUpload = async ({
|
||||
const compressBase64ImgAndUpload = async ({
|
||||
base64Img,
|
||||
maxW,
|
||||
maxH,
|
||||
@@ -89,7 +70,7 @@ export const compressImgFileAndUpload = async ({
|
||||
reader.readAsDataURL(file);
|
||||
|
||||
const base64Img = await new Promise<string>((resolve, reject) => {
|
||||
reader.onload = async () => {
|
||||
reader.onload = () => {
|
||||
resolve(reader.result as string);
|
||||
};
|
||||
reader.onerror = (err) => {
|
||||
|
||||
@@ -3,12 +3,17 @@ import { Box } from '@chakra-ui/react';
|
||||
import { useToast } from '@fastgpt/web/hooks/useToast';
|
||||
import { useI18n } from '@/web/context/I18n';
|
||||
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?: {
|
||||
fileType?: string;
|
||||
multiple?: boolean;
|
||||
maxCount?: number;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { fileT } = useI18n();
|
||||
const { fileType = '*', multiple = false, maxCount = 10 } = props || {};
|
||||
const { toast } = useToast();
|
||||
@@ -48,8 +53,44 @@ export const useSelectFile = (props?: {
|
||||
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 {
|
||||
File,
|
||||
onOpen
|
||||
onOpen,
|
||||
onSelectImage,
|
||||
loading
|
||||
};
|
||||
};
|
||||
|
||||
@@ -27,7 +27,8 @@ type State = {
|
||||
setLastAppListRouteType: (e?: string) => void;
|
||||
|
||||
loginStore?: LoginStoreType;
|
||||
setLoginStore: (e: LoginStoreType) => void;
|
||||
setLoginStore: (e?: LoginStoreType) => void;
|
||||
|
||||
loading: boolean;
|
||||
setLoading: (val: boolean) => null;
|
||||
gitStar: number;
|
||||
|
||||
@@ -101,7 +101,7 @@ export const postCreateDatasetFolder = (data: DatasetFolderCreateBody) =>
|
||||
export const resumeInheritPer = (datasetId: string) =>
|
||||
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);
|
||||
|
||||
/* =========== search test ============ */
|
||||
|
||||
@@ -4,3 +4,6 @@ export enum LoginPageTypeEnum {
|
||||
forgetPassword = 'forgetPassword',
|
||||
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