mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-12-24 01:22:24 +03:00
Improves error messages in auth (#1120)
This commit is contained in:
parent
28fb8310e2
commit
ee5272e6de
@ -1,5 +1,5 @@
|
||||
{{={= =}=}}
|
||||
import { useState, FormEvent, useEffect, useCallback } from 'react'
|
||||
import { useState, FormEvent, useEffect } from 'react'
|
||||
import { useHistory, useLocation } from 'react-router-dom'
|
||||
import { createTheme } from '@stitches/react'
|
||||
|
||||
@ -22,6 +22,11 @@ import config from '../../config.js'
|
||||
import { styled } from '../../stitches.config'
|
||||
import { State, CustomizationOptions } from './types'
|
||||
|
||||
type ErrorMessage = {
|
||||
title: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
const logoStyle = {
|
||||
height: '3rem'
|
||||
}
|
||||
@ -216,13 +221,13 @@ function Auth ({ state, appearance, logo, socialLayout = 'horizontal' }: {
|
||||
state: State;
|
||||
} & CustomizationOptions) {
|
||||
const isLogin = state === "login";
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
const [errorMessage, setErrorMessage] = useState<ErrorMessage | null>(null);
|
||||
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
{=# isAnyPasswordBasedAuthEnabled =}
|
||||
const history = useHistory();
|
||||
const onErrorHandler = (error) => {
|
||||
setErrorMessage(error.message)
|
||||
setErrorMessage({ title: error.message, description: error.data?.data?.message })
|
||||
};
|
||||
{=/ isAnyPasswordBasedAuthEnabled =}
|
||||
{=# isUsernameAndPasswordAuthEnabled =}
|
||||
@ -358,7 +363,9 @@ function Auth ({ state, appearance, logo, socialLayout = 'horizontal' }: {
|
||||
<HeaderText>{title}</HeaderText>
|
||||
</div>
|
||||
|
||||
{errorMessage && <ErrorMessage>{errorMessage}</ErrorMessage>}
|
||||
{errorMessage && (<ErrorMessage>
|
||||
{errorMessage.title}{errorMessage.description && ': '}{errorMessage.description}
|
||||
</ErrorMessage>)}
|
||||
{successMessage && <SuccessMessage>{successMessage}</SuccessMessage>}
|
||||
{(state === 'login' || state === 'signup') && loginSignupForm}
|
||||
{=# isEmailAuthEnabled =}
|
||||
@ -388,7 +395,14 @@ function Auth ({ state, appearance, logo, socialLayout = 'horizontal' }: {
|
||||
export default Auth;
|
||||
|
||||
{=# isEmailAuthEnabled =}
|
||||
const ForgotPasswordForm = ({ isLoading, setIsLoading, setErrorMessage, setSuccessMessage }) => {
|
||||
const ForgotPasswordForm = (
|
||||
{ isLoading, setIsLoading, setErrorMessage, setSuccessMessage }: {
|
||||
isLoading: boolean;
|
||||
setIsLoading: (isLoading: boolean) => void;
|
||||
setErrorMessage: (errorMessage: ErrorMessage | null) => void;
|
||||
setSuccessMessage: (successMessage: string | null) => void;
|
||||
},
|
||||
) => {
|
||||
const [email, setEmail] = useState('')
|
||||
|
||||
const onSubmit = async (event: FormEvent<HTMLFormElement>) => {
|
||||
@ -401,7 +415,7 @@ const ForgotPasswordForm = ({ isLoading, setIsLoading, setErrorMessage, setSucce
|
||||
setSuccessMessage('Check your email for a password reset link.')
|
||||
setEmail('')
|
||||
} catch (error) {
|
||||
setErrorMessage(error.message)
|
||||
setErrorMessage({ title: error.message, description: error.data?.data?.message })
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
@ -428,7 +442,14 @@ const ForgotPasswordForm = ({ isLoading, setIsLoading, setErrorMessage, setSucce
|
||||
)
|
||||
}
|
||||
|
||||
const ResetPasswordForm = ({ isLoading, setIsLoading, setErrorMessage, setSuccessMessage }) => {
|
||||
const ResetPasswordForm = (
|
||||
{ isLoading, setIsLoading, setErrorMessage, setSuccessMessage }: {
|
||||
isLoading: boolean;
|
||||
setIsLoading: (isLoading: boolean) => void;
|
||||
setErrorMessage: (errorMessage: ErrorMessage | null) => void;
|
||||
setSuccessMessage: (successMessage: string | null) => void;
|
||||
},
|
||||
) => {
|
||||
const location = useLocation()
|
||||
const token = new URLSearchParams(location.search).get('token')
|
||||
const [password, setPassword] = useState('')
|
||||
@ -438,12 +459,12 @@ const ResetPasswordForm = ({ isLoading, setIsLoading, setErrorMessage, setSucces
|
||||
event.preventDefault()
|
||||
|
||||
if (!token) {
|
||||
setErrorMessage('The token is missing from the URL. Please check the link you received in your email.')
|
||||
setErrorMessage({ title: 'The token is missing from the URL. Please check the link you received in your email.' })
|
||||
return
|
||||
}
|
||||
|
||||
if (!password || password !== passwordConfirmation) {
|
||||
setErrorMessage("Passwords don't match!")
|
||||
setErrorMessage({ title: `Passwords don't match!` })
|
||||
return
|
||||
}
|
||||
|
||||
@ -456,7 +477,7 @@ const ResetPasswordForm = ({ isLoading, setIsLoading, setErrorMessage, setSucces
|
||||
setPassword('')
|
||||
setPasswordConfirmation('')
|
||||
} catch (error) {
|
||||
setErrorMessage(error.message)
|
||||
setErrorMessage({ title: error.message, description: error.data?.data?.message })
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
@ -493,7 +514,14 @@ const ResetPasswordForm = ({ isLoading, setIsLoading, setErrorMessage, setSucces
|
||||
)
|
||||
}
|
||||
|
||||
const VerifyEmailForm = ({ isLoading, setIsLoading, setErrorMessage, setSuccessMessage }) => {
|
||||
const VerifyEmailForm = (
|
||||
{ isLoading, setIsLoading, setErrorMessage, setSuccessMessage }: {
|
||||
isLoading: boolean;
|
||||
setIsLoading: (isLoading: boolean) => void;
|
||||
setErrorMessage: (errorMessage: ErrorMessage | null) => void;
|
||||
setSuccessMessage: (successMessage: string | null) => void;
|
||||
},
|
||||
) => {
|
||||
const location = useLocation()
|
||||
const token = new URLSearchParams(location.search).get('token')
|
||||
|
||||
@ -509,7 +537,7 @@ const VerifyEmailForm = ({ isLoading, setIsLoading, setErrorMessage, setSuccessM
|
||||
await verifyEmail({ token })
|
||||
setSuccessMessage('Your email has been verified. You can now log in.')
|
||||
} catch (error) {
|
||||
setErrorMessage(error.message)
|
||||
setErrorMessage({ title: error.message, description: error.data?.data?.message })
|
||||
} finally {
|
||||
setIsLoading(false)
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { Request, Response } from 'express';
|
||||
import { verifyPassword } from "../../../core/auth.js";
|
||||
import { findUserBy, createAuthToken, ensureValidEmailAndPassword } from "../../utils.js";
|
||||
import { findUserBy, createAuthToken, ensureValidEmailAndPassword, throwInvalidCredentialsError } from "../../utils.js";
|
||||
|
||||
export function getLoginRoute({
|
||||
allowUnverifiedLogin,
|
||||
@ -11,25 +11,25 @@ export function getLoginRoute({
|
||||
req: Request<{ email: string; password: string; }>,
|
||||
res: Response,
|
||||
): Promise<Response<{ token: string } | undefined>> {
|
||||
const args = req.body || {};
|
||||
ensureValidEmailAndPassword(args);
|
||||
const args = req.body || {}
|
||||
ensureValidEmailAndPassword(args)
|
||||
|
||||
args.email = args.email.toLowerCase();
|
||||
args.email = args.email.toLowerCase()
|
||||
|
||||
const user = await findUserBy<'email'>({ email: args.email });
|
||||
const user = await findUserBy<'email'>({ email: args.email })
|
||||
if (!user) {
|
||||
return res.status(401).send();
|
||||
throwInvalidCredentialsError()
|
||||
}
|
||||
if (!user.isEmailVerified && !allowUnverifiedLogin) {
|
||||
return res.status(401).send();
|
||||
throwInvalidCredentialsError()
|
||||
}
|
||||
try {
|
||||
await verifyPassword(user.password, args.password);
|
||||
} catch(e) {
|
||||
return res.status(401).send();
|
||||
throwInvalidCredentialsError()
|
||||
}
|
||||
|
||||
const token = await createAuthToken(user);
|
||||
const token = await createAuthToken(user)
|
||||
|
||||
return res.json({ token })
|
||||
};
|
||||
|
@ -2,20 +2,20 @@
|
||||
import { verifyPassword } from '../../../core/auth.js'
|
||||
import { handleRejection } from '../../../utils.js'
|
||||
|
||||
import { findUserBy, createAuthToken } from '../../utils.js'
|
||||
import { findUserBy, createAuthToken, throwInvalidCredentialsError } from '../../utils.js'
|
||||
|
||||
export default handleRejection(async (req, res) => {
|
||||
const args = req.body || {}
|
||||
|
||||
const user = await findUserBy<'username'>({ username: args.username })
|
||||
if (!user) {
|
||||
return res.status(401).send()
|
||||
throwInvalidCredentialsError()
|
||||
}
|
||||
|
||||
try {
|
||||
await verifyPassword(user.password, args.password)
|
||||
} catch(e) {
|
||||
return res.status(401).send()
|
||||
throwInvalidCredentialsError()
|
||||
}
|
||||
|
||||
// Username & password valid - generate token.
|
||||
|
@ -7,5 +7,5 @@ export default handleRejection(async (req, res) => {
|
||||
|
||||
await createUser(userFields)
|
||||
|
||||
return res.send()
|
||||
return res.json({ success: true })
|
||||
})
|
||||
|
@ -34,7 +34,7 @@ export async function createUser(data: Prisma.{= userEntityUpper =}CreateInput):
|
||||
try {
|
||||
return await prisma.{= userEntityLower =}.create({ data })
|
||||
} catch (e) {
|
||||
rethrowError(e);
|
||||
rethrowPossiblePrismaError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ export async function deleteUser(user: {= userEntityUpper =}): Promise<{= userEn
|
||||
try {
|
||||
return await prisma.{= userEntityLower =}.delete({ where: { id: user.id } })
|
||||
} catch (e) {
|
||||
rethrowError(e);
|
||||
rethrowPossiblePrismaError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,7 +74,7 @@ export async function updateUserEmailVerification(userId: {= userEntityUpper =}I
|
||||
data: { isEmailVerified: true },
|
||||
})
|
||||
} catch (e) {
|
||||
rethrowError(e);
|
||||
rethrowPossiblePrismaError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -85,7 +85,7 @@ export async function updateUserPassword(userId: {= userEntityUpper =}Id, passwo
|
||||
data: { password },
|
||||
})
|
||||
} catch (e) {
|
||||
rethrowError(e);
|
||||
rethrowPossiblePrismaError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,7 +135,7 @@ async function sendEmailAndLogTimestamp(
|
||||
data: { [field]: new Date() },
|
||||
})
|
||||
} catch (e) {
|
||||
rethrowError(e);
|
||||
rethrowPossiblePrismaError(e);
|
||||
}
|
||||
emailSender.send(content).catch((e) => {
|
||||
console.error(`Failed to send email for ${field}`, e);
|
||||
@ -200,18 +200,26 @@ export function ensureValidPassword(args: unknown): void {
|
||||
function validate(args: unknown, validators: { validates: string, message: string, validator: (value: unknown) => boolean }[]): void {
|
||||
for (const { validates, message, validator } of validators) {
|
||||
if (!validator(args[validates])) {
|
||||
throw new HttpError(422, `Validation failed: ${message}`, { message, field: validates })
|
||||
throwValidationError(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
{=/ isEmailAuthEnabled =}
|
||||
|
||||
function rethrowError(e: unknown): void {
|
||||
export function throwInvalidCredentialsError(message?: string): void {
|
||||
throw new HttpError(401, 'Invalid credentials', { message })
|
||||
}
|
||||
|
||||
function rethrowPossiblePrismaError(e: unknown): void {
|
||||
if (e instanceof AuthError) {
|
||||
throw new HttpError(422, 'Validation failed', { message: e.message })
|
||||
throwValidationError(e.message);
|
||||
} else if (isPrismaError(e)) {
|
||||
throw prismaErrorToHttpError(e)
|
||||
} else {
|
||||
throw new HttpError(500)
|
||||
}
|
||||
}
|
||||
|
||||
function throwValidationError(message: string): void {
|
||||
throw new HttpError(422, 'Validation failed', { message })
|
||||
}
|
@ -7,6 +7,7 @@ import { randomInt } from 'node:crypto'
|
||||
import prisma from '../dbClient.js'
|
||||
import { handleRejection } from '../utils.js'
|
||||
import config from '../config.js'
|
||||
import { throwInvalidCredentialsError } from '../auth/utils.js'
|
||||
|
||||
const jwtSign = util.promisify(jwt.sign)
|
||||
const jwtVerify = util.promisify(jwt.verify)
|
||||
@ -33,7 +34,7 @@ const auth = handleRejection(async (req, res, next) => {
|
||||
userIdFromToken = (await verify(token)).id
|
||||
} catch (error) {
|
||||
if (['TokenExpiredError', 'JsonWebTokenError', 'NotBeforeError'].includes(error.name)) {
|
||||
return res.status(401).send()
|
||||
throwInvalidCredentialsError()
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
@ -41,7 +42,7 @@ const auth = handleRejection(async (req, res, next) => {
|
||||
|
||||
const user = await prisma.{= userEntityLower =}.findUnique({ where: { id: userIdFromToken } })
|
||||
if (!user) {
|
||||
return res.status(401).send()
|
||||
throwInvalidCredentialsError()
|
||||
}
|
||||
|
||||
// TODO: This logic must match the type in types/index.ts (if we remove the
|
||||
@ -52,7 +53,7 @@ const auth = handleRejection(async (req, res, next) => {
|
||||
|
||||
req.user = userView
|
||||
} else {
|
||||
return res.status(401).send()
|
||||
throwInvalidCredentialsError()
|
||||
}
|
||||
|
||||
next()
|
||||
|
@ -1,11 +1,12 @@
|
||||
{{={= =}=}}
|
||||
import { serialize as superjsonSerialize } from 'superjson'
|
||||
import { handleRejection } from '../../utils.js'
|
||||
import { throwInvalidCredentialsError } from '../../auth/utils.js'
|
||||
|
||||
export default handleRejection(async (req, res) => {
|
||||
if (req.{= userEntityLower =}) {
|
||||
return res.json(superjsonSerialize(req.{= userEntityLower =}))
|
||||
} else {
|
||||
return res.status(401).send()
|
||||
throwInvalidCredentialsError()
|
||||
}
|
||||
})
|
||||
|
@ -32,7 +32,7 @@ export const prismaErrorToHttpError = (e) => {
|
||||
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
if (e.code === 'P2002') {
|
||||
return new HttpError(422, 'Save failed', {
|
||||
message: `A record with the same ${e.meta.target.join(', ')} already exists.`,
|
||||
message: `user with the same ${e.meta.target.join(', ')} already exists`,
|
||||
target: e.meta.target
|
||||
})
|
||||
} else {
|
||||
|
@ -235,7 +235,7 @@
|
||||
"file",
|
||||
"server/src/utils.js"
|
||||
],
|
||||
"9f0afaa88132ee2f05e0acababf1a84d60a83aa6e0ccbd3028b2982500b29459"
|
||||
"300e9bb586b163f2608acb27346b5c94ec3e58cdc25dace5381f3d0c6710a7ec"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -32,7 +32,7 @@ export const prismaErrorToHttpError = (e) => {
|
||||
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
if (e.code === 'P2002') {
|
||||
return new HttpError(422, 'Save failed', {
|
||||
message: `A record with the same ${e.meta.target.join(', ')} already exists.`,
|
||||
message: `user with the same ${e.meta.target.join(', ')} already exists`,
|
||||
target: e.meta.target
|
||||
})
|
||||
} else {
|
||||
|
@ -242,7 +242,7 @@
|
||||
"file",
|
||||
"server/src/utils.js"
|
||||
],
|
||||
"9f0afaa88132ee2f05e0acababf1a84d60a83aa6e0ccbd3028b2982500b29459"
|
||||
"300e9bb586b163f2608acb27346b5c94ec3e58cdc25dace5381f3d0c6710a7ec"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -32,7 +32,7 @@ export const prismaErrorToHttpError = (e) => {
|
||||
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
if (e.code === 'P2002') {
|
||||
return new HttpError(422, 'Save failed', {
|
||||
message: `A record with the same ${e.meta.target.join(', ')} already exists.`,
|
||||
message: `user with the same ${e.meta.target.join(', ')} already exists`,
|
||||
target: e.meta.target
|
||||
})
|
||||
} else {
|
||||
|
@ -179,7 +179,7 @@
|
||||
"file",
|
||||
"server/src/auth/utils.ts"
|
||||
],
|
||||
"e5b365c0914f8da5dfeb721db11bb241b97ce5f82b59537786747bdb6521bf93"
|
||||
"cc5c7e899b9e4389999748f19177f8a392098279f19d99875d09986334d173c8"
|
||||
],
|
||||
[
|
||||
[
|
||||
@ -207,7 +207,7 @@
|
||||
"file",
|
||||
"server/src/core/auth.js"
|
||||
],
|
||||
"da2adb670df6bb76d9df157a44056c7f13bf16a2eb5b78fa90dbaa8d98674689"
|
||||
"0ea7ff5b8576c4bd455328a56d60a038c1107eaa2e20cba28437cdfc630ce571"
|
||||
],
|
||||
[
|
||||
[
|
||||
@ -403,7 +403,7 @@
|
||||
"file",
|
||||
"server/src/routes/auth/me.js"
|
||||
],
|
||||
"b122d0dfbe6b9ef6599a1848b15da291d2d17005fcfdd965f48fc1362a0aaad0"
|
||||
"9a9cb533bb94af63caf448f73a0d0fef8902c8f8d1af411bed2570a32da2fab9"
|
||||
],
|
||||
[
|
||||
[
|
||||
@ -466,7 +466,7 @@
|
||||
"file",
|
||||
"server/src/utils.js"
|
||||
],
|
||||
"9f0afaa88132ee2f05e0acababf1a84d60a83aa6e0ccbd3028b2982500b29459"
|
||||
"300e9bb586b163f2608acb27346b5c94ec3e58cdc25dace5381f3d0c6710a7ec"
|
||||
],
|
||||
[
|
||||
[
|
||||
@ -592,7 +592,7 @@
|
||||
"file",
|
||||
"web-app/src/auth/forms/Auth.tsx"
|
||||
],
|
||||
"6e74c3affea9fdc6c71d94fa2875298b4e5bdcf71b89b63320d0a52d81c18652"
|
||||
"8879b1f60150d66a478b5d9f849d4d021359db5b5c255d57994a0ed7f497d70e"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -28,7 +28,7 @@ export async function createUser(data: Prisma.UserCreateInput): Promise<User> {
|
||||
try {
|
||||
return await prisma.user.create({ data })
|
||||
} catch (e) {
|
||||
rethrowError(e);
|
||||
rethrowPossiblePrismaError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ export async function deleteUser(user: User): Promise<User> {
|
||||
try {
|
||||
return await prisma.user.delete({ where: { id: user.id } })
|
||||
} catch (e) {
|
||||
rethrowError(e);
|
||||
rethrowPossiblePrismaError(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,12 +61,20 @@ export async function doFakeWork() {
|
||||
}
|
||||
|
||||
|
||||
function rethrowError(e: unknown): void {
|
||||
export function throwInvalidCredentialsError(message?: string): void {
|
||||
throw new HttpError(401, 'Invalid credentials', { message })
|
||||
}
|
||||
|
||||
function rethrowPossiblePrismaError(e: unknown): void {
|
||||
if (e instanceof AuthError) {
|
||||
throw new HttpError(422, 'Validation failed', { message: e.message })
|
||||
throwValidationError(e.message);
|
||||
} else if (isPrismaError(e)) {
|
||||
throw prismaErrorToHttpError(e)
|
||||
} else {
|
||||
throw new HttpError(500)
|
||||
}
|
||||
}
|
||||
|
||||
function throwValidationError(message: string): void {
|
||||
throw new HttpError(422, 'Validation failed', { message })
|
||||
}
|
@ -6,6 +6,7 @@ import { randomInt } from 'node:crypto'
|
||||
import prisma from '../dbClient.js'
|
||||
import { handleRejection } from '../utils.js'
|
||||
import config from '../config.js'
|
||||
import { throwInvalidCredentialsError } from '../auth/utils.js'
|
||||
|
||||
const jwtSign = util.promisify(jwt.sign)
|
||||
const jwtVerify = util.promisify(jwt.verify)
|
||||
@ -32,7 +33,7 @@ const auth = handleRejection(async (req, res, next) => {
|
||||
userIdFromToken = (await verify(token)).id
|
||||
} catch (error) {
|
||||
if (['TokenExpiredError', 'JsonWebTokenError', 'NotBeforeError'].includes(error.name)) {
|
||||
return res.status(401).send()
|
||||
throwInvalidCredentialsError()
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
@ -40,7 +41,7 @@ const auth = handleRejection(async (req, res, next) => {
|
||||
|
||||
const user = await prisma.user.findUnique({ where: { id: userIdFromToken } })
|
||||
if (!user) {
|
||||
return res.status(401).send()
|
||||
throwInvalidCredentialsError()
|
||||
}
|
||||
|
||||
// TODO: This logic must match the type in types/index.ts (if we remove the
|
||||
@ -51,7 +52,7 @@ const auth = handleRejection(async (req, res, next) => {
|
||||
|
||||
req.user = userView
|
||||
} else {
|
||||
return res.status(401).send()
|
||||
throwInvalidCredentialsError()
|
||||
}
|
||||
|
||||
next()
|
||||
|
@ -1,10 +1,11 @@
|
||||
import { serialize as superjsonSerialize } from 'superjson'
|
||||
import { handleRejection } from '../../utils.js'
|
||||
import { throwInvalidCredentialsError } from '../../auth/utils.js'
|
||||
|
||||
export default handleRejection(async (req, res) => {
|
||||
if (req.user) {
|
||||
return res.json(superjsonSerialize(req.user))
|
||||
} else {
|
||||
return res.status(401).send()
|
||||
throwInvalidCredentialsError()
|
||||
}
|
||||
})
|
||||
|
@ -32,7 +32,7 @@ export const prismaErrorToHttpError = (e) => {
|
||||
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
if (e.code === 'P2002') {
|
||||
return new HttpError(422, 'Save failed', {
|
||||
message: `A record with the same ${e.meta.target.join(', ')} already exists.`,
|
||||
message: `user with the same ${e.meta.target.join(', ')} already exists`,
|
||||
target: e.meta.target
|
||||
})
|
||||
} else {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useState, FormEvent, useEffect, useCallback } from 'react'
|
||||
import { useState, FormEvent, useEffect } from 'react'
|
||||
import { useHistory, useLocation } from 'react-router-dom'
|
||||
import { createTheme } from '@stitches/react'
|
||||
|
||||
@ -9,6 +9,11 @@ import config from '../../config.js'
|
||||
import { styled } from '../../stitches.config'
|
||||
import { State, CustomizationOptions } from './types'
|
||||
|
||||
type ErrorMessage = {
|
||||
title: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
const logoStyle = {
|
||||
height: '3rem'
|
||||
}
|
||||
@ -198,7 +203,7 @@ function Auth ({ state, appearance, logo, socialLayout = 'horizontal' }: {
|
||||
state: State;
|
||||
} & CustomizationOptions) {
|
||||
const isLogin = state === "login";
|
||||
const [errorMessage, setErrorMessage] = useState<string | null>(null);
|
||||
const [errorMessage, setErrorMessage] = useState<ErrorMessage | null>(null);
|
||||
const [successMessage, setSuccessMessage] = useState<string | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
@ -239,7 +244,9 @@ function Auth ({ state, appearance, logo, socialLayout = 'horizontal' }: {
|
||||
<HeaderText>{title}</HeaderText>
|
||||
</div>
|
||||
|
||||
{errorMessage && <ErrorMessage>{errorMessage}</ErrorMessage>}
|
||||
{errorMessage && (<ErrorMessage>
|
||||
{errorMessage.title}{errorMessage.description && ': '}{errorMessage.description}
|
||||
</ErrorMessage>)}
|
||||
{successMessage && <SuccessMessage>{successMessage}</SuccessMessage>}
|
||||
{(state === 'login' || state === 'signup') && loginSignupForm}
|
||||
</Container>
|
||||
|
@ -256,7 +256,7 @@
|
||||
"file",
|
||||
"server/src/utils.js"
|
||||
],
|
||||
"9f0afaa88132ee2f05e0acababf1a84d60a83aa6e0ccbd3028b2982500b29459"
|
||||
"300e9bb586b163f2608acb27346b5c94ec3e58cdc25dace5381f3d0c6710a7ec"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -32,7 +32,7 @@ export const prismaErrorToHttpError = (e) => {
|
||||
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
if (e.code === 'P2002') {
|
||||
return new HttpError(422, 'Save failed', {
|
||||
message: `A record with the same ${e.meta.target.join(', ')} already exists.`,
|
||||
message: `user with the same ${e.meta.target.join(', ')} already exists`,
|
||||
target: e.meta.target
|
||||
})
|
||||
} else {
|
||||
|
@ -242,7 +242,7 @@
|
||||
"file",
|
||||
"server/src/utils.js"
|
||||
],
|
||||
"9f0afaa88132ee2f05e0acababf1a84d60a83aa6e0ccbd3028b2982500b29459"
|
||||
"300e9bb586b163f2608acb27346b5c94ec3e58cdc25dace5381f3d0c6710a7ec"
|
||||
],
|
||||
[
|
||||
[
|
||||
|
@ -32,7 +32,7 @@ export const prismaErrorToHttpError = (e) => {
|
||||
if (e instanceof Prisma.PrismaClientKnownRequestError) {
|
||||
if (e.code === 'P2002') {
|
||||
return new HttpError(422, 'Save failed', {
|
||||
message: `A record with the same ${e.meta.target.join(', ')} already exists.`,
|
||||
message: `user with the same ${e.meta.target.join(', ')} already exists`,
|
||||
target: e.meta.target
|
||||
})
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user