diff --git a/services/web/app/src/router.js b/services/web/app/src/router.js index 90739bc5e3..2794bfca65 100644 --- a/services/web/app/src/router.js +++ b/services/web/app/src/router.js @@ -187,7 +187,7 @@ const rateLimiters = { duration: 60, }), resendConfirmation: new RateLimiter('resend-confirmation', { - points: 10, + points: 1, duration: 60, }), sendChatMessage: new RateLimiter('send-chat-message', { diff --git a/services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx b/services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx index cec2e6732b..306763b087 100644 --- a/services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx +++ b/services/web/frontend/js/features/project-list/components/notifications/groups/affiliation/reconfirm-affiliation.tsx @@ -4,7 +4,10 @@ import { Button } from 'react-bootstrap' import Icon from '../../../../../../shared/components/icon' import getMeta from '../../../../../../utils/meta' import useAsync from '../../../../../../shared/hooks/use-async' -import { postJSON } from '../../../../../../infrastructure/fetch-json' +import { + FetchError, + postJSON, +} from '../../../../../../infrastructure/fetch-json' import { UserEmailData } from '../../../../../../../../types/user-email' import { ExposedSettings } from '../../../../../../../../types/exposed-settings' import { Institution } from '../../../../../../../../types/institution' @@ -21,7 +24,7 @@ function ReconfirmAffiliation({ }: ReconfirmAffiliationProps) { const { t } = useTranslation() const { samlInitPath } = getMeta('ol-ExposedSettings') as ExposedSettings - const { isLoading, isError, isSuccess, runAsync } = useAsync() + const { error, isLoading, isError, isSuccess, runAsync } = useAsync() const [hasSent, setHasSent] = useState(false) const [isPending, setIsPending] = useState(false) const ssoEnabled = institution.ssoEnabled @@ -48,6 +51,9 @@ function ReconfirmAffiliation({ } } + const rateLimited = + error && error instanceof FetchError && error.response?.status === 429 + if (hasSent) { return (
@@ -73,7 +79,11 @@ function ReconfirmAffiliation({ {isError && ( <>
-
{t('generic_something_went_wrong')}
+
+ {rateLimited + ? t('too_many_requests') + : t('generic_something_went_wrong')} +
)}
@@ -116,7 +126,11 @@ function ReconfirmAffiliation({ {isError && ( <>
-
{t('generic_something_went_wrong')}
+
+ {rateLimited + ? t('too_many_requests') + : t('generic_something_went_wrong')} +
)} diff --git a/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt.tsx b/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt.tsx index b761bc9fce..773a776ff4 100644 --- a/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt.tsx +++ b/services/web/frontend/js/features/settings/components/emails/reconfirmation-info/reconfirmation-info-prompt.tsx @@ -1,6 +1,6 @@ import { useState, useEffect, useLayoutEffect } from 'react' import useAsync from '../../../../../shared/hooks/use-async' -import { postJSON } from '../../../../../infrastructure/fetch-json' +import { FetchError, postJSON } from '../../../../../infrastructure/fetch-json' import { Trans, useTranslation } from 'react-i18next' import { Institution } from '../../../../../../../types/institution' import { Button } from 'react-bootstrap' @@ -24,7 +24,7 @@ function ReconfirmationInfoPrompt({ }: ReconfirmationInfoPromptProps) { const { t } = useTranslation() const { samlInitPath } = getMeta('ol-ExposedSettings') as ExposedSettings - const { isLoading, isError, isSuccess, runAsync } = useAsync() + const { error, isLoading, isError, isSuccess, runAsync } = useAsync() const { state, setLoading: setUserEmailsContextLoading } = useUserEmailsContext() const [isPending, setIsPending] = useState(false) @@ -59,6 +59,9 @@ function ReconfirmationInfoPrompt({ } } + const rateLimited = + isError && error instanceof FetchError && error.response?.status === 429 + if (hasSent) { return (
@@ -87,7 +90,11 @@ function ReconfirmationInfoPrompt({ )}
{isError && ( -
{t('generic_something_went_wrong')}
+
+ {rateLimited + ? t('too_many_requests') + : t('generic_something_went_wrong')} +
)}
) @@ -117,7 +124,11 @@ function ReconfirmationInfoPrompt({
{isError && ( -
{t('generic_something_went_wrong')}
+
+ {rateLimited + ? t('too_many_requests') + : t('generic_something_went_wrong')} +
)} diff --git a/services/web/frontend/js/features/settings/components/emails/resend-confirmation-email-button.tsx b/services/web/frontend/js/features/settings/components/emails/resend-confirmation-email-button.tsx index cb35cc20c6..fad75ba7e8 100644 --- a/services/web/frontend/js/features/settings/components/emails/resend-confirmation-email-button.tsx +++ b/services/web/frontend/js/features/settings/components/emails/resend-confirmation-email-button.tsx @@ -2,7 +2,7 @@ import { useEffect } from 'react' import { useTranslation } from 'react-i18next' import Icon from '../../../../shared/components/icon' import { Button } from 'react-bootstrap' -import { postJSON } from '../../../../infrastructure/fetch-json' +import { FetchError, postJSON } from '../../../../infrastructure/fetch-json' import useAsync from '../../../../shared/hooks/use-async' import { UserEmailData } from '../../../../../../types/user-email' import { useUserEmailsContext } from '../../context/user-email-context' @@ -15,7 +15,7 @@ function ResendConfirmationEmailButton({ email, }: ResendConfirmationEmailButtonProps) { const { t } = useTranslation() - const { isLoading, isError, runAsync } = useAsync() + const { error, isLoading, isError, runAsync } = useAsync() const { state, setLoading: setUserEmailsContextLoading } = useUserEmailsContext() @@ -42,6 +42,9 @@ function ResendConfirmationEmailButton({ ) } + const rateLimited = + error && error instanceof FetchError && error.response?.status === 429 + return ( <>