mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-16 16:14:43 -04:00
enhancement(auth): better error message handling
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
parent
8e57188ab5
commit
ca9836d691
37 changed files with 199 additions and 207 deletions
|
@ -18,7 +18,7 @@ export const NewPasswordField: React.FC<CommonFieldProps> = ({ onChange, value,
|
|||
const { t } = useTranslation()
|
||||
|
||||
const isValid = useMemo(() => {
|
||||
return value.trim() !== '' && value.length >= 8
|
||||
return value.trim() !== ''
|
||||
}, [value])
|
||||
|
||||
return (
|
||||
|
@ -34,7 +34,6 @@ export const NewPasswordField: React.FC<CommonFieldProps> = ({ onChange, value,
|
|||
onChange={onChange}
|
||||
placeholder={t('login.auth.password') ?? undefined}
|
||||
className='bg-dark text-light'
|
||||
minLength={8}
|
||||
autoComplete='new-password'
|
||||
required
|
||||
/>
|
||||
|
|
|
@ -19,14 +19,17 @@ exports[`Note loading boundary shows an error 1`] = `
|
|||
</span>
|
||||
<span>
|
||||
titleI18nKey:
|
||||
api.note.notFound.title
|
||||
noteLoadingBoundary.error.notFound.title
|
||||
</span>
|
||||
<span>
|
||||
descriptionI18nKey:
|
||||
api.note.notFound.description
|
||||
noteLoadingBoundary.error.notFound.description
|
||||
</span>
|
||||
<span>
|
||||
children:
|
||||
<span>
|
||||
This is a mock for CreateNonExistingNoteHint
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiError } from '../../../api/common/api-error'
|
||||
import * as getNoteModule from '../../../api/notes'
|
||||
import type { Note } from '../../../api/notes/types'
|
||||
import * as LoadingScreenModule from '../../../components/application-loader/loading-screen/loading-screen'
|
||||
|
@ -87,7 +88,7 @@ describe('Note loading boundary', () => {
|
|||
jest.spyOn(getNoteModule, 'getNote').mockImplementation((id) => {
|
||||
expect(id).toBe(mockedNoteId)
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => reject(new Error('api.note.notFound')), 0)
|
||||
setTimeout(() => reject(new ApiError(404, undefined, undefined)), 0)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { ApiError } from '../../../api/common/api-error'
|
||||
import { ErrorToI18nKeyMapper } from '../../../api/common/error-to-i18n-key-mapper'
|
||||
import { LoadingScreen } from '../../application-loader/loading-screen/loading-screen'
|
||||
import { CommonErrorPage } from '../../error-pages/common-error-page'
|
||||
import { CustomAsyncLoadingBoundary } from '../async-loading-boundary/custom-async-loading-boundary'
|
||||
|
@ -28,11 +30,18 @@ export const NoteLoadingBoundary: React.FC<PropsWithChildren> = ({ children }) =
|
|||
|
||||
const errorComponent = useMemo(() => {
|
||||
if (error === undefined) {
|
||||
return <></>
|
||||
return null
|
||||
}
|
||||
const errorI18nKeyPrefix = new ErrorToI18nKeyMapper(error, 'noteLoadingBoundary.error')
|
||||
.withHttpCode(404, 'notFound')
|
||||
.withHttpCode(403, 'forbidden')
|
||||
.withHttpCode(401, 'forbidden')
|
||||
.orFallbackI18nKey('other')
|
||||
return (
|
||||
<CommonErrorPage titleI18nKey={`${error.message}.title`} descriptionI18nKey={`${error.message}.description`}>
|
||||
<ShowIf condition={error.message === 'api.error.note.not_found'}>
|
||||
<CommonErrorPage
|
||||
titleI18nKey={`${errorI18nKeyPrefix}.title`}
|
||||
descriptionI18nKey={`${errorI18nKeyPrefix}.description`}>
|
||||
<ShowIf condition={error instanceof ApiError && error.statusCode === 404}>
|
||||
<CreateNonExistingNoteHint onNoteCreated={loadNoteFromServer} />
|
||||
</ShowIf>
|
||||
</CommonErrorPage>
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { doLocalLogin } from '../../../api/auth/local'
|
||||
import { ErrorToI18nKeyMapper } from '../../../api/common/error-to-i18n-key-mapper'
|
||||
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
||||
import { useOnInputChange } from '../../../hooks/common/use-on-input-change'
|
||||
import { ShowIf } from '../../common/show-if/show-if'
|
||||
|
@ -30,7 +31,14 @@ export const ViaLocal: React.FC = () => {
|
|||
(event: FormEvent) => {
|
||||
doLocalLogin(username, password)
|
||||
.then(() => fetchAndSetUser())
|
||||
.catch((error: Error) => setError(error.message))
|
||||
.catch((error: Error) => {
|
||||
const errorI18nKey = new ErrorToI18nKeyMapper(error, 'login.auth.error')
|
||||
.withHttpCode(404, 'usernamePassword')
|
||||
.withHttpCode(401, 'usernamePassword')
|
||||
.withBackendErrorName('FeatureDisabledError', 'loginDisabled')
|
||||
.orFallbackI18nKey('other')
|
||||
setError(errorI18nKey)
|
||||
})
|
||||
event.preventDefault()
|
||||
},
|
||||
[username, password]
|
||||
|
@ -45,25 +53,23 @@ export const ViaLocal: React.FC = () => {
|
|||
<Card.Title>
|
||||
<Trans i18nKey='login.signInVia' values={{ service: t('login.auth.username') }} />
|
||||
</Card.Title>
|
||||
<Form onSubmit={onLoginSubmit}>
|
||||
<Form onSubmit={onLoginSubmit} className={'d-flex gap-3 flex-column'}>
|
||||
<UsernameField onChange={onUsernameChange} invalid={!!error} />
|
||||
<PasswordField onChange={onPasswordChange} invalid={!!error} />
|
||||
<Alert className='small' show={!!error} variant='danger'>
|
||||
<Trans i18nKey={error} />
|
||||
</Alert>
|
||||
|
||||
<div className='flex flex-row' dir='auto'>
|
||||
<Button type='submit' variant='primary' className='mx-2'>
|
||||
<Trans i18nKey='login.signIn' />
|
||||
</Button>
|
||||
<ShowIf condition={allowRegister}>
|
||||
<Link href={'/register'} passHref={true}>
|
||||
<Button type='button' variant='secondary' className='mx-2'>
|
||||
<Trans i18nKey='login.register.title' />
|
||||
</Button>
|
||||
</Link>
|
||||
</ShowIf>
|
||||
</div>
|
||||
<Button type='submit' variant='primary'>
|
||||
<Trans i18nKey='login.signIn' />
|
||||
</Button>
|
||||
<ShowIf condition={allowRegister}>
|
||||
<Trans i18nKey={'login.register.question'} />
|
||||
<Link href={'/register'} passHref={true}>
|
||||
<Button type='button' variant='secondary' className={'d-block w-100'}>
|
||||
<Trans i18nKey='login.register.title' />
|
||||
</Button>
|
||||
</Link>
|
||||
</ShowIf>
|
||||
</Form>
|
||||
</Card.Body>
|
||||
</Card>
|
||||
|
|
|
@ -37,8 +37,8 @@ export const ProfileChangePassword: React.FC = () => {
|
|||
} catch (error) {
|
||||
const foundI18nKey = new ErrorToI18nKeyMapper(error as Error, 'login.auth.error')
|
||||
.withHttpCode(401, 'invalidCredentials')
|
||||
.withBackendErrorName('loginDisabled', 'loginDisabled')
|
||||
.withBackendErrorName('passwordTooWeak', 'passwordTooWeak')
|
||||
.withBackendErrorName('FeatureDisabledError', 'loginDisabled')
|
||||
.withBackendErrorName('PasswordTooWeakError', 'passwordTooWeak')
|
||||
.orFallbackI18nKey('other')
|
||||
return Promise.reject(foundI18nKey)
|
||||
} finally {
|
||||
|
|
|
@ -21,8 +21,8 @@ export const RegisterError: React.FC<RegisterErrorProps> = ({ error }) => {
|
|||
}
|
||||
return new ErrorToI18nKeyMapper(error, 'login.register.error')
|
||||
.withHttpCode(409, 'usernameExisting')
|
||||
.withBackendErrorName('registrationDisabled', 'registrationDisabled')
|
||||
.withBackendErrorName('passwordTooWeak', 'passwordTooWeak')
|
||||
.withBackendErrorName('FeatureDisabledError', 'registrationDisabled')
|
||||
.withBackendErrorName('PasswordTooWeakError', 'passwordTooWeak')
|
||||
.orFallbackI18nKey('other')
|
||||
}, [error])
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue