mirror of
https://github.com/Infisical/infisical.git
synced 2025-04-17 19:37:38 +00:00
Merge pull request #3001 from Infisical/fix/address-org-invite-resend
fix: address org invite resend issues
This commit is contained in:
@ -73,6 +73,40 @@ export const registerInviteOrgRouter = async (server: FastifyZodProvider) => {
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/signup-resend",
|
||||
config: {
|
||||
rateLimit: inviteUserRateLimit
|
||||
},
|
||||
method: "POST",
|
||||
schema: {
|
||||
body: z.object({
|
||||
membershipId: z.string()
|
||||
}),
|
||||
response: {
|
||||
200: z.object({
|
||||
signupToken: z
|
||||
.object({
|
||||
email: z.string(),
|
||||
link: z.string()
|
||||
})
|
||||
.optional()
|
||||
})
|
||||
}
|
||||
},
|
||||
onRequest: verifyAuth([AuthMode.JWT]),
|
||||
handler: async (req) => {
|
||||
return server.services.org.resendOrgMemberInvitation({
|
||||
orgId: req.permission.orgId,
|
||||
actor: req.permission.type,
|
||||
actorId: req.permission.id,
|
||||
actorAuthMethod: req.permission.authMethod,
|
||||
actorOrgId: req.permission.orgId,
|
||||
membershipId: req.body.membershipId
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
server.route({
|
||||
url: "/verify",
|
||||
method: "POST",
|
||||
|
@ -69,6 +69,7 @@ import {
|
||||
TGetOrgMembershipDTO,
|
||||
TInviteUserToOrgDTO,
|
||||
TListProjectMembershipsByOrgMembershipIdDTO,
|
||||
TResendOrgMemberInvitationDTO,
|
||||
TUpdateOrgDTO,
|
||||
TUpdateOrgMembershipDTO,
|
||||
TVerifyUserToOrgDTO
|
||||
@ -584,6 +585,66 @@ export const orgServiceFactory = ({
|
||||
});
|
||||
return membership;
|
||||
};
|
||||
|
||||
const resendOrgMemberInvitation = async ({
|
||||
orgId,
|
||||
actorId,
|
||||
actor,
|
||||
actorAuthMethod,
|
||||
actorOrgId,
|
||||
membershipId
|
||||
}: TResendOrgMemberInvitationDTO) => {
|
||||
const appCfg = getConfig();
|
||||
const { permission } = await permissionService.getOrgPermission(actor, actorId, orgId, actorAuthMethod, actorOrgId);
|
||||
|
||||
ForbiddenError.from(permission).throwUnlessCan(OrgPermissionActions.Create, OrgPermissionSubjects.Member);
|
||||
|
||||
const org = await orgDAL.findOrgById(orgId);
|
||||
|
||||
const [inviteeOrgMembership] = await orgDAL.findMembership({
|
||||
[`${TableName.OrgMembership}.orgId` as "orgId"]: orgId,
|
||||
[`${TableName.OrgMembership}.id` as "id"]: membershipId
|
||||
});
|
||||
|
||||
if (inviteeOrgMembership.status !== OrgMembershipStatus.Invited) {
|
||||
throw new BadRequestError({
|
||||
message: "Organization invitation already accepted"
|
||||
});
|
||||
}
|
||||
|
||||
const token = await tokenService.createTokenForUser({
|
||||
type: TokenType.TOKEN_EMAIL_ORG_INVITATION,
|
||||
userId: inviteeOrgMembership.userId,
|
||||
orgId
|
||||
});
|
||||
|
||||
if (!appCfg.isSmtpConfigured) {
|
||||
return {
|
||||
signupToken: {
|
||||
email: inviteeOrgMembership.email as string,
|
||||
link: `${appCfg.SITE_URL}/signupinvite?token=${token}&to=${inviteeOrgMembership.email}&organization_id=${org?.id}`
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
await smtpService.sendMail({
|
||||
template: SmtpTemplates.OrgInvite,
|
||||
subjectLine: "Infisical organization invitation",
|
||||
recipients: [inviteeOrgMembership.email as string],
|
||||
substitutions: {
|
||||
inviterFirstName: inviteeOrgMembership.firstName,
|
||||
inviterUsername: inviteeOrgMembership.email,
|
||||
organizationName: org?.name,
|
||||
email: inviteeOrgMembership.email,
|
||||
organizationId: org?.id.toString(),
|
||||
token,
|
||||
callback_url: `${appCfg.SITE_URL}/signupinvite`
|
||||
}
|
||||
});
|
||||
|
||||
return { signupToken: undefined };
|
||||
};
|
||||
|
||||
/*
|
||||
* Invite user to organization
|
||||
*/
|
||||
@ -627,6 +688,7 @@ export const orgServiceFactory = ({
|
||||
}
|
||||
})
|
||||
: [];
|
||||
|
||||
if (projectsToInvite.length !== invitedProjects?.length) {
|
||||
throw new ForbiddenRequestError({
|
||||
message: "Access denied to one or more of the specified projects"
|
||||
@ -1221,6 +1283,7 @@ export const orgServiceFactory = ({
|
||||
deleteIncidentContact,
|
||||
getOrgGroups,
|
||||
listProjectMembershipsByOrgMembershipId,
|
||||
findOrgBySlug
|
||||
findOrgBySlug,
|
||||
resendOrgMemberInvitation
|
||||
};
|
||||
};
|
||||
|
@ -35,6 +35,10 @@ export type TInviteUserToOrgDTO = {
|
||||
}[];
|
||||
} & TOrgPermission;
|
||||
|
||||
export type TResendOrgMemberInvitationDTO = {
|
||||
membershipId: string;
|
||||
} & TOrgPermission;
|
||||
|
||||
export type TVerifyUserToOrgDTO = {
|
||||
email: string;
|
||||
orgId: string;
|
||||
|
@ -156,3 +156,18 @@ export const useCreateNewTotpRecoveryCodes = () => {
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useResendOrgMemberInvitation = () => {
|
||||
return useMutation({
|
||||
mutationFn: async (dto: { membershipId: string }) => {
|
||||
const { data } = await apiRequest.post<{
|
||||
signupToken?: {
|
||||
email: string;
|
||||
link: string;
|
||||
};
|
||||
}>("/api/v1/invite-org/signup-resend", dto);
|
||||
|
||||
return data.signupToken;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -44,13 +44,13 @@ import {
|
||||
} from "@app/context";
|
||||
import { usePagination, useResetPageHelper } from "@app/hooks";
|
||||
import {
|
||||
useAddUsersToOrg,
|
||||
useFetchServerStatus,
|
||||
useGetOrgRoles,
|
||||
useGetOrgUsers,
|
||||
useUpdateOrgMembership
|
||||
} from "@app/hooks/api";
|
||||
import { OrderByDirection } from "@app/hooks/api/generic/types";
|
||||
import { useResendOrgMemberInvitation } from "@app/hooks/api/users/mutation";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
type Props = {
|
||||
@ -83,7 +83,7 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLinks }: Pro
|
||||
const { data: serverDetails } = useFetchServerStatus();
|
||||
const { data: members = [], isPending: isMembersLoading } = useGetOrgUsers(orgId);
|
||||
|
||||
const { mutateAsync: addUsersMutateAsync } = useAddUsersToOrg();
|
||||
const { mutateAsync: resendOrgMemberInvitation } = useResendOrgMemberInvitation();
|
||||
const { mutateAsync: updateOrgMembership } = useUpdateOrgMembership();
|
||||
|
||||
const onRoleChange = async (membershipId: string, role: string) => {
|
||||
@ -119,26 +119,25 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLinks }: Pro
|
||||
}
|
||||
};
|
||||
|
||||
const onResendInvite = async (email: string) => {
|
||||
const onResendInvite = async (membershipId: string) => {
|
||||
try {
|
||||
const { data } = await addUsersMutateAsync({
|
||||
organizationId: orgId,
|
||||
inviteeEmails: [email],
|
||||
organizationRoleSlug: "member"
|
||||
const signupToken = await resendOrgMemberInvitation({
|
||||
membershipId
|
||||
});
|
||||
|
||||
setCompleteInviteLinks(data?.completeInviteLinks || null);
|
||||
|
||||
if (!data.completeInviteLinks) {
|
||||
createNotification({
|
||||
text: `Successfully resent invite to ${email}`,
|
||||
type: "success"
|
||||
});
|
||||
if (signupToken) {
|
||||
setCompleteInviteLinks([signupToken]);
|
||||
return;
|
||||
}
|
||||
|
||||
createNotification({
|
||||
text: "Successfully resent org invitation",
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: `Failed to resend invite to ${email}`,
|
||||
text: "Failed to resend org invitation",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
@ -370,7 +369,10 @@ export const OrgMembersTable = ({ handlePopUpOpen, setCompleteInviteLinks }: Pro
|
||||
className="w-48"
|
||||
colorSchema="primary"
|
||||
variant="outline_bg"
|
||||
onClick={() => onResendInvite(email)}
|
||||
onClick={(e) => {
|
||||
onResendInvite(orgMembershipId);
|
||||
e.stopPropagation();
|
||||
}}
|
||||
>
|
||||
Resend invite
|
||||
</Button>
|
||||
|
@ -18,13 +18,9 @@ import {
|
||||
useUser
|
||||
} from "@app/context";
|
||||
import { useTimedReset } from "@app/hooks";
|
||||
import {
|
||||
useAddUsersToOrg,
|
||||
useFetchServerStatus,
|
||||
useGetOrgMembership,
|
||||
useGetOrgRoles
|
||||
} from "@app/hooks/api";
|
||||
import { useFetchServerStatus, useGetOrgMembership, useGetOrgRoles } from "@app/hooks/api";
|
||||
import { OrgUser } from "@app/hooks/api/types";
|
||||
import { useResendOrgMemberInvitation } from "@app/hooks/api/users/mutation";
|
||||
import { UsePopUpState } from "@app/hooks/usePopUp";
|
||||
|
||||
type Props = {
|
||||
@ -45,28 +41,27 @@ export const UserDetailsSection = ({ membershipId, handlePopUpOpen }: Props) =>
|
||||
const { data: roles } = useGetOrgRoles(orgId);
|
||||
const { data: serverDetails } = useFetchServerStatus();
|
||||
const { data: membership } = useGetOrgMembership(orgId, membershipId);
|
||||
const { mutateAsync: inviteUsers, isPending } = useAddUsersToOrg();
|
||||
|
||||
const onResendInvite = async (email: string) => {
|
||||
const { mutateAsync: resendOrgMemberInvitation, isPending } = useResendOrgMemberInvitation();
|
||||
|
||||
const onResendInvite = async () => {
|
||||
try {
|
||||
const { data } = await inviteUsers({
|
||||
organizationId: orgId,
|
||||
inviteeEmails: [email],
|
||||
organizationRoleSlug: "member"
|
||||
const signupToken = await resendOrgMemberInvitation({
|
||||
membershipId
|
||||
});
|
||||
|
||||
// setCompleteInviteLink(data?.completeInviteLink || "");
|
||||
|
||||
if (!data.completeInviteLinks) {
|
||||
createNotification({
|
||||
text: `Successfully resent invite to ${email}`,
|
||||
type: "success"
|
||||
});
|
||||
if (signupToken) {
|
||||
return;
|
||||
}
|
||||
|
||||
createNotification({
|
||||
text: "Successfully resent org invitation",
|
||||
type: "success"
|
||||
});
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
createNotification({
|
||||
text: `Failed to resend invite to ${email}`,
|
||||
text: "Failed to resend org invitation",
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
@ -210,9 +205,7 @@ export const UserDetailsSection = ({ membershipId, handlePopUpOpen }: Props) =>
|
||||
colorSchema="primary"
|
||||
type="submit"
|
||||
isLoading={isPending}
|
||||
onClick={() => {
|
||||
onResendInvite(membership.user.email as string);
|
||||
}}
|
||||
onClick={onResendInvite}
|
||||
>
|
||||
Resend Invite
|
||||
</Button>
|
||||
|
Reference in New Issue
Block a user