Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
赵月辉
/
fastgpt-migrated
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Unverified
Commit
73451dbc
authored
Mar 19, 2025
by
Finley Ge
Committed by
archer
Mar 27, 2025
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: invite link (#4229)
* fix: invite link * feat: create invite link and copy it directly
parent
077350e6
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
67 additions
and
31 deletions
+67
-31
packages/service/support/user/team/invitationLink/schema.ts
+8
-5
packages/service/support/user/team/invitationLink/type.ts
+5
-5
projects/app/src/pageComponents/account/team/Invite/CreateInvitationModal.tsx
+6
-3
projects/app/src/pageComponents/account/team/Invite/InviteModal.tsx
+17
-14
projects/app/src/web/support/user/team/api.ts
+2
-4
test/datas/users.ts
+29
-0
No files found.
packages/service/support/user/team/invitationLink/schema.ts
View file @
73451dbc
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
,
...
...
packages/service/support/user/team/invitationLink/type.ts
View file @
73451dbc
...
@@ -2,6 +2,7 @@ import { TeamMemberSchema } from '@fastgpt/global/support/user/team/type';
...
@@ -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 = {
...
@@ -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
;
...
...
projects/app/src/pageComponents/account/team/Invite/CreateInvitationModal.tsx
View file @
73451dbc
...
@@ -22,7 +22,7 @@ import { useRequest2 } from '@fastgpt/web/hooks/useRequest';
...
@@ -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 }) {
...
@@ -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 }) {
...
@@ -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 }) {
...
@@ -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"
>
...
...
projects/app/src/pageComponents/account/team/Invite/InviteModal.tsx
View file @
73451dbc
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
,
put
UpdateInvitationInfo
}
from
'@/web/support/user/team/api'
;
import
{
getInvitationLinkList
,
put
ForbidInvitationLink
}
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 = ({
...
@@ -79,18 +79,11 @@ const InviteModal = ({
[
copyData
]
[
copyData
]
);
);
const
{
runAsync
:
onForbid
,
loading
:
forbiding
}
=
useRequest2
(
const
{
runAsync
:
onForbid
,
loading
:
forbiding
}
=
useRequest2
(
putForbidInvitationLink
,
{
(
linkId
:
string
)
=>
putUpdateInvitationInfo
({
linkId
,
forbidden
:
true
}),
{
manual
:
true
,
manual
:
true
,
onSuccess
:
refetchInvitationLinkList
,
onSuccess
:
refetchInvitationLinkList
,
successToast
:
t
(
'account_team:forbid_success'
)
successToast
:
t
(
'account_team:forbid_success'
)
}
});
);
return
(
return
(
<
MyModal
<
MyModal
...
@@ -134,7 +127,7 @@ const InviteModal = ({
...
@@ -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
.
_i
d
}
overflow=
{
'unset'
}
>
<
Tr
key=
{
item
.
linkI
d
}
overflow=
{
'unset'
}
>
<
Td
maxW=
"200px"
minW=
"100px"
>
<
Td
maxW=
"200px"
minW=
"100px"
>
{
item
.
description
}
{
item
.
description
}
</
Td
>
</
Td
>
...
@@ -209,7 +202,7 @@ const InviteModal = ({
...
@@ -209,7 +202,7 @@ const InviteModal = ({
<
Button
<
Button
size=
"sm"
size=
"sm"
variant=
"outline"
variant=
"outline"
onClick=
{
()
=>
onCopy
(
item
.
_i
d
)
}
onClick=
{
()
=>
onCopy
(
item
.
linkI
d
)
}
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 = ({
...
@@ -239,7 +232,7 @@ const InviteModal = ({
variant=
"outline"
variant=
"outline"
colorScheme=
"red"
colorScheme=
"red"
onClick=
{
()
=>
{
onClick=
{
()
=>
{
onForbid
(
item
.
_i
d
);
onForbid
(
item
.
linkI
d
);
onClosePopover
();
onClosePopover
();
}
}
}
}
>
>
...
@@ -268,7 +261,17 @@ const InviteModal = ({
...
@@ -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
>
...
...
projects/app/src/web/support/user/team/api.ts
View file @
73451dbc
...
@@ -21,7 +21,6 @@ import type { PaginationProps, PaginationResponse } from '@fastgpt/web/common/fe
...
@@ -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) =>
...
@@ -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
=
()
=>
...
...
test/datas/users.ts
View file @
73451dbc
...
@@ -32,3 +32,32 @@ export async function getRootUser(): Promise<parseHeaderCertRet> {
...
@@ -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
};
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment