mirror of
https://github.com/labring/FastGPT.git
synced 2025-07-23 05:12:39 +00:00
fix: invite link (#4229)
* fix: invite link * feat: create invite link and copy it directly
This commit is contained in:
@@ -1,15 +1,18 @@
|
|||||||
import {
|
import { TeamCollectionName } from '@fastgpt/global/support/user/team/constant';
|
||||||
TeamCollectionName,
|
|
||||||
TeamMemberCollectionName
|
|
||||||
} from '@fastgpt/global/support/user/team/constant';
|
|
||||||
import { connectionMongo, getMongoModel } from '../../../../common/mongo';
|
import { connectionMongo, getMongoModel } from '../../../../common/mongo';
|
||||||
import { InvitationSchemaType } from './type';
|
import { InvitationSchemaType } from './type';
|
||||||
import addDays from 'date-fns/esm/fp/addDays/index.js';
|
import { randomUUID } from 'crypto';
|
||||||
const { Schema } = connectionMongo;
|
const { Schema } = connectionMongo;
|
||||||
|
|
||||||
export const InvitationCollectionName = 'team_invitation_links';
|
export const InvitationCollectionName = 'team_invitation_links';
|
||||||
|
|
||||||
const InvitationSchema = new Schema({
|
const InvitationSchema = new Schema({
|
||||||
|
linkId: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
unique: true,
|
||||||
|
default: () => randomUUID()
|
||||||
|
},
|
||||||
teamId: {
|
teamId: {
|
||||||
type: Schema.Types.ObjectId,
|
type: Schema.Types.ObjectId,
|
||||||
ref: TeamCollectionName,
|
ref: TeamCollectionName,
|
||||||
|
@@ -2,6 +2,7 @@ import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
|
|||||||
|
|
||||||
export type InvitationSchemaType = {
|
export type InvitationSchemaType = {
|
||||||
_id: string;
|
_id: string;
|
||||||
|
linkId: string;
|
||||||
teamId: string;
|
teamId: string;
|
||||||
usedTimesLimit?: number;
|
usedTimesLimit?: number;
|
||||||
forbidden?: boolean;
|
forbidden?: boolean;
|
||||||
@@ -25,11 +26,10 @@ export type InvitationLinkCreateType = {
|
|||||||
expires: InvitationLinkExpiresType;
|
expires: InvitationLinkExpiresType;
|
||||||
usedTimesLimit: 1 | -1;
|
usedTimesLimit: 1 | -1;
|
||||||
};
|
};
|
||||||
export type InvitationLinkUpdateType = Partial<
|
|
||||||
Omit<InvitationSchemaType, 'members' | 'teamId' | '_id'>
|
// export type InvitationLinkUpdateType = Partial<
|
||||||
> & {
|
// Omit<InvitationSchemaType, 'members' | 'teamId' | '_id'>
|
||||||
linkId: string;
|
// >;
|
||||||
};
|
|
||||||
|
|
||||||
export type InvitationInfoType = InvitationSchemaType & {
|
export type InvitationInfoType = InvitationSchemaType & {
|
||||||
teamAvatar: string;
|
teamAvatar: string;
|
||||||
|
@@ -22,7 +22,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
|
|||||||
import { useTranslation } from 'next-i18next';
|
import { useTranslation } from 'next-i18next';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
|
|
||||||
function CreateInvitationModal({ onClose }: { onClose: () => void }) {
|
function CreateInvitationModal({ onClose }: { onClose: (linkId?: string) => void }) {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const expiresOptions: Array<{ label: string; value: InvitationLinkExpiresType }> = [
|
const expiresOptions: Array<{ label: string; value: InvitationLinkExpiresType }> = [
|
||||||
{ label: t('account_team:30mins'), value: '30m' }, // 30 mins
|
{ label: t('account_team:30mins'), value: '30m' }, // 30 mins
|
||||||
@@ -45,6 +45,9 @@ function CreateInvitationModal({ onClose }: { onClose: () => void }) {
|
|||||||
manual: true,
|
manual: true,
|
||||||
successToast: t('common:common.Create Success'),
|
successToast: t('common:common.Create Success'),
|
||||||
errorToast: t('common:common.Create Failed'),
|
errorToast: t('common:common.Create Failed'),
|
||||||
|
onSuccess: (data) => {
|
||||||
|
onClose(data);
|
||||||
|
},
|
||||||
onFinally: () => onClose()
|
onFinally: () => onClose()
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -55,7 +58,7 @@ function CreateInvitationModal({ onClose }: { onClose: () => void }) {
|
|||||||
iconColor="primary.500"
|
iconColor="primary.500"
|
||||||
title={<Box>{t('account_team:create_invitation_link')}</Box>}
|
title={<Box>{t('account_team:create_invitation_link')}</Box>}
|
||||||
>
|
>
|
||||||
<ModalCloseButton onClick={onClose} />
|
<ModalCloseButton onClick={() => onClose()} />
|
||||||
<ModalBody>
|
<ModalBody>
|
||||||
<Grid gap={6} templateColumns="max-content 1fr" alignItems="center">
|
<Grid gap={6} templateColumns="max-content 1fr" alignItems="center">
|
||||||
<>
|
<>
|
||||||
@@ -91,7 +94,7 @@ function CreateInvitationModal({ onClose }: { onClose: () => void }) {
|
|||||||
</Grid>
|
</Grid>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button isLoading={loading} onClick={onClose} variant="outline">
|
<Button isLoading={loading} onClick={() => onClose()} variant="outline">
|
||||||
{t('common:common.Cancel')}
|
{t('common:common.Cancel')}
|
||||||
</Button>
|
</Button>
|
||||||
<Button isLoading={loading} onClick={handleSubmit(createInvitationLink)} ml="4">
|
<Button isLoading={loading} onClick={handleSubmit(createInvitationLink)} ml="4">
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import MemberTag from '@/components/support/user/team/Info/MemberTag';
|
import MemberTag from '@/components/support/user/team/Info/MemberTag';
|
||||||
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
import { useSystemStore } from '@/web/common/system/useSystemStore';
|
||||||
import { getInvitationLinkList, putUpdateInvitationInfo } from '@/web/support/user/team/api';
|
import { getInvitationLinkList, putForbidInvitationLink } from '@/web/support/user/team/api';
|
||||||
import { useUserStore } from '@/web/support/user/useUserStore';
|
import { useUserStore } from '@/web/support/user/useUserStore';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
@@ -79,18 +79,11 @@ const InviteModal = ({
|
|||||||
[copyData]
|
[copyData]
|
||||||
);
|
);
|
||||||
|
|
||||||
const { runAsync: onForbid, loading: forbiding } = useRequest2(
|
const { runAsync: onForbid, loading: forbiding } = useRequest2(putForbidInvitationLink, {
|
||||||
(linkId: string) =>
|
manual: true,
|
||||||
putUpdateInvitationInfo({
|
onSuccess: refetchInvitationLinkList,
|
||||||
linkId,
|
successToast: t('account_team:forbid_success')
|
||||||
forbidden: true
|
});
|
||||||
}),
|
|
||||||
{
|
|
||||||
manual: true,
|
|
||||||
onSuccess: refetchInvitationLinkList,
|
|
||||||
successToast: t('account_team:forbid_success')
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyModal
|
<MyModal
|
||||||
@@ -134,7 +127,7 @@ const InviteModal = ({
|
|||||||
{invitationLinkList?.map((item) => {
|
{invitationLinkList?.map((item) => {
|
||||||
const isForbidden = item.forbidden || new Date(item.expires) < new Date();
|
const isForbidden = item.forbidden || new Date(item.expires) < new Date();
|
||||||
return (
|
return (
|
||||||
<Tr key={item._id} overflow={'unset'}>
|
<Tr key={item.linkId} overflow={'unset'}>
|
||||||
<Td maxW="200px" minW="100px">
|
<Td maxW="200px" minW="100px">
|
||||||
{item.description}
|
{item.description}
|
||||||
</Td>
|
</Td>
|
||||||
@@ -209,7 +202,7 @@ const InviteModal = ({
|
|||||||
<Button
|
<Button
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
onClick={() => onCopy(item._id)}
|
onClick={() => onCopy(item.linkId)}
|
||||||
color="myGray.900"
|
color="myGray.900"
|
||||||
>
|
>
|
||||||
<Icon name="common/link" w="16px" mr="1" />
|
<Icon name="common/link" w="16px" mr="1" />
|
||||||
@@ -239,7 +232,7 @@ const InviteModal = ({
|
|||||||
variant="outline"
|
variant="outline"
|
||||||
colorScheme="red"
|
colorScheme="red"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
onForbid(item._id);
|
onForbid(item.linkId);
|
||||||
onClosePopover();
|
onClosePopover();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -268,7 +261,17 @@ const InviteModal = ({
|
|||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
{isOpenCreate && (
|
{isOpenCreate && (
|
||||||
<CreateInvitationModal
|
<CreateInvitationModal
|
||||||
onClose={() => Promise.all([onCloseCreate(), refetchInvitationLinkList()])}
|
onClose={(linkId?: string) =>
|
||||||
|
Promise.all([
|
||||||
|
onCloseCreate(),
|
||||||
|
refetchInvitationLinkList(),
|
||||||
|
(() => {
|
||||||
|
if (linkId) {
|
||||||
|
onCopy(linkId);
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
])
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</MyModal>
|
</MyModal>
|
||||||
|
@@ -21,7 +21,6 @@ import type { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fe
|
|||||||
import type {
|
import type {
|
||||||
InvitationInfoType,
|
InvitationInfoType,
|
||||||
InvitationLinkCreateType,
|
InvitationLinkCreateType,
|
||||||
InvitationLinkUpdateType,
|
|
||||||
InvitationType
|
InvitationType
|
||||||
} from '@fastgpt/service/support/user/team/invitationLink/type';
|
} from '@fastgpt/service/support/user/team/invitationLink/type';
|
||||||
|
|
||||||
@@ -66,9 +65,8 @@ export const postAcceptInvitationLink = (linkId: string) =>
|
|||||||
|
|
||||||
export const getInvitationInfo = (linkId: string) =>
|
export const getInvitationInfo = (linkId: string) =>
|
||||||
GET<InvitationInfoType>(`/proApi/support/user/team/invitationLink/info`, { linkId });
|
GET<InvitationInfoType>(`/proApi/support/user/team/invitationLink/info`, { linkId });
|
||||||
|
export const putForbidInvitationLink = (linkId: string) =>
|
||||||
export const putUpdateInvitationInfo = (data: InvitationLinkUpdateType) =>
|
PUT<string>(`/proApi/support/user/team/invitationLink/forbid`, { linkId });
|
||||||
PUT('/proApi/support/user/team/invitationLink/update', data);
|
|
||||||
|
|
||||||
/* -------------- team collaborator -------------------- */
|
/* -------------- team collaborator -------------------- */
|
||||||
export const getTeamClbs = () =>
|
export const getTeamClbs = () =>
|
||||||
|
@@ -32,3 +32,32 @@ export async function getRootUser(): Promise<parseHeaderCertRet> {
|
|||||||
tmbId: tmb?._id
|
tmbId: tmb?._id
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getUser(username: string): Promise<parseHeaderCertRet> {
|
||||||
|
const user = await MongoUser.create({
|
||||||
|
username,
|
||||||
|
password: '123456'
|
||||||
|
});
|
||||||
|
|
||||||
|
const team = await MongoTeam.create({
|
||||||
|
name: 'test team',
|
||||||
|
ownerId: user._id
|
||||||
|
});
|
||||||
|
|
||||||
|
const tmb = await MongoTeamMember.create({
|
||||||
|
teamId: team._id,
|
||||||
|
userId: user._id,
|
||||||
|
status: 'active'
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
userId: user._id,
|
||||||
|
apikey: '',
|
||||||
|
appId: '',
|
||||||
|
authType: AuthUserTypeEnum.token,
|
||||||
|
isRoot: false,
|
||||||
|
sourceName: undefined,
|
||||||
|
teamId: tmb?.teamId,
|
||||||
|
tmbId: tmb?._id
|
||||||
|
};
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user