Adds dbAuth
This commit is contained in:
@@ -1,21 +1,19 @@
|
||||
import { AuthProvider } from '@redwoodjs/auth'
|
||||
import netlifyIdentity from 'netlify-identity-widget'
|
||||
|
||||
import { AuthProvider } from '@redwoodjs/auth'
|
||||
import { FatalErrorBoundary, RedwoodProvider } from '@redwoodjs/web'
|
||||
import { RedwoodApolloProvider } from '@redwoodjs/web/apollo'
|
||||
import FatalErrorPage from 'src/pages/FatalErrorPage'
|
||||
import { isBrowser } from '@redwoodjs/prerender/browserUtils'
|
||||
|
||||
import Routes from 'src/Routes'
|
||||
|
||||
import './scaffold.css'
|
||||
import './index.css'
|
||||
|
||||
isBrowser && netlifyIdentity.init()
|
||||
|
||||
const App = () => (
|
||||
<FatalErrorBoundary page={FatalErrorPage}>
|
||||
<RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
|
||||
<AuthProvider client={netlifyIdentity} type="netlify">
|
||||
<AuthProvider type="dbAuth">
|
||||
<RedwoodApolloProvider>
|
||||
<Routes />
|
||||
</RedwoodApolloProvider>
|
||||
|
||||
@@ -14,12 +14,10 @@ import BlogLayout from 'src/layouts/BlogLayout'
|
||||
const Routes = () => {
|
||||
return (
|
||||
<Router>
|
||||
<Set wrap={BlogLayout}>
|
||||
<Route path="/contact" page={ContactPage} name="contact" />
|
||||
<Route path="/blog-post/{id:Int}" page={BlogPostPage} name="blogPost" />
|
||||
<Route path="/about" page={AboutPage} name="about" />
|
||||
<Route path="/" page={HomePage} name="home" />
|
||||
</Set>
|
||||
<Route path="/login" page={LoginPage} name="login" />
|
||||
<Route path="/signup" page={SignupPage} name="signup" />
|
||||
<Route path="/forgot-password" page={ForgotPasswordPage} name="forgotPassword" />
|
||||
<Route path="/reset-password" page={ResetPasswordPage} name="resetPassword" />
|
||||
<Private unauthenticated="home">
|
||||
<Set wrap={PostsLayout}>
|
||||
<Route path="/admin/posts/new" page={PostNewPostPage} name="newPost" />
|
||||
|
||||
89
web/src/pages/ForgotPasswordPage/ForgotPasswordPage.js
Normal file
89
web/src/pages/ForgotPasswordPage/ForgotPasswordPage.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import { useEffect, useRef } from 'react'
|
||||
import { useAuth } from '@redwoodjs/auth'
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
import { MetaTags } from '@redwoodjs/web'
|
||||
import { toast, Toaster } from '@redwoodjs/web/toast'
|
||||
import { Form, Label, TextField, Submit, FieldError } from '@redwoodjs/forms'
|
||||
|
||||
const ForgotPasswordPage = () => {
|
||||
const { isAuthenticated, forgotPassword } = useAuth()
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
navigate(routes.home())
|
||||
}
|
||||
}, [isAuthenticated])
|
||||
|
||||
const usernameRef = useRef()
|
||||
useEffect(() => {
|
||||
usernameRef.current.focus()
|
||||
}, [])
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
const response = await forgotPassword(data.username)
|
||||
|
||||
if (response.error) {
|
||||
toast.error(response.error)
|
||||
} else {
|
||||
// The function `forgotPassword.handler` in api/src/functions/auth.js has
|
||||
// been invoked, let the user know how to get the link to reset their
|
||||
// password (sent in email, perhaps?)
|
||||
toast.success(
|
||||
'A link to reset your password was sent to ' + response.email
|
||||
)
|
||||
navigate(routes.login())
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<MetaTags title="Forgot Password" />
|
||||
|
||||
<main className="rw-main">
|
||||
<Toaster toastOptions={{ className: 'rw-toast', duration: 6000 }} />
|
||||
<div className="rw-scaffold rw-login-container">
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">
|
||||
Forgot Password
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<div className="rw-segment-main">
|
||||
<div className="rw-form-wrapper">
|
||||
<Form onSubmit={onSubmit} className="rw-form-wrapper">
|
||||
<div className="text-left">
|
||||
<Label
|
||||
name="username"
|
||||
className="rw-label"
|
||||
errorClassName="rw-label rw-label-error"
|
||||
>
|
||||
Username
|
||||
</Label>
|
||||
<TextField
|
||||
name="username"
|
||||
className="rw-input"
|
||||
errorClassName="rw-input rw-input-error"
|
||||
ref={usernameRef}
|
||||
validation={{
|
||||
required: true,
|
||||
}}
|
||||
/>
|
||||
|
||||
<FieldError name="username" className="rw-field-error" />
|
||||
</div>
|
||||
|
||||
<div className="rw-button-group">
|
||||
<Submit className="rw-button rw-button-blue">Submit</Submit>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ForgotPasswordPage
|
||||
129
web/src/pages/LoginPage/LoginPage.js
Normal file
129
web/src/pages/LoginPage/LoginPage.js
Normal file
@@ -0,0 +1,129 @@
|
||||
import { Link, navigate, routes } from '@redwoodjs/router'
|
||||
import { useRef } from 'react'
|
||||
import {
|
||||
Form,
|
||||
Label,
|
||||
TextField,
|
||||
PasswordField,
|
||||
Submit,
|
||||
FieldError,
|
||||
} from '@redwoodjs/forms'
|
||||
import { useAuth } from '@redwoodjs/auth'
|
||||
import { MetaTags } from '@redwoodjs/web'
|
||||
import { toast, Toaster } from '@redwoodjs/web/toast'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
const LoginPage = () => {
|
||||
const { isAuthenticated, logIn } = useAuth()
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
navigate(routes.home())
|
||||
}
|
||||
}, [isAuthenticated])
|
||||
|
||||
const usernameRef = useRef()
|
||||
useEffect(() => {
|
||||
usernameRef.current.focus()
|
||||
}, [])
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
const response = await logIn({ ...data })
|
||||
|
||||
if (response.message) {
|
||||
toast(response.message)
|
||||
} else if (response.error) {
|
||||
toast.error(response.error)
|
||||
} else {
|
||||
toast.success('Welcome back!')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<MetaTags title="Login" />
|
||||
|
||||
<main className="rw-main">
|
||||
<Toaster toastOptions={{ className: 'rw-toast', duration: 6000 }} />
|
||||
<div className="rw-scaffold rw-login-container">
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">Login</h2>
|
||||
</header>
|
||||
|
||||
<div className="rw-segment-main">
|
||||
<div className="rw-form-wrapper">
|
||||
<Form onSubmit={onSubmit} className="rw-form-wrapper">
|
||||
<Label
|
||||
name="username"
|
||||
className="rw-label"
|
||||
errorClassName="rw-label rw-label-error"
|
||||
>
|
||||
Username
|
||||
</Label>
|
||||
<TextField
|
||||
name="username"
|
||||
className="rw-input"
|
||||
errorClassName="rw-input rw-input-error"
|
||||
ref={usernameRef}
|
||||
validation={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Username is required',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<FieldError name="username" className="rw-field-error" />
|
||||
|
||||
<Label
|
||||
name="password"
|
||||
className="rw-label"
|
||||
errorClassName="rw-label rw-label-error"
|
||||
>
|
||||
Password
|
||||
</Label>
|
||||
<PasswordField
|
||||
name="password"
|
||||
className="rw-input"
|
||||
errorClassName="rw-input rw-input-error"
|
||||
autoComplete="current-password"
|
||||
validation={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Password is required',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<div className="rw-forgot-link">
|
||||
<Link
|
||||
to={routes.forgotPassword()}
|
||||
className="rw-forgot-link"
|
||||
>
|
||||
Forgot Password?
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<FieldError name="password" className="rw-field-error" />
|
||||
|
||||
<div className="rw-button-group">
|
||||
<Submit className="rw-button rw-button-blue">Login</Submit>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="rw-login-link">
|
||||
<span>Don't have an account?</span>{' '}
|
||||
<Link to={routes.signup()} className="rw-link">
|
||||
Sign up!
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default LoginPage
|
||||
119
web/src/pages/ResetPasswordPage/ResetPasswordPage.js
Normal file
119
web/src/pages/ResetPasswordPage/ResetPasswordPage.js
Normal file
@@ -0,0 +1,119 @@
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { useAuth } from '@redwoodjs/auth'
|
||||
import { navigate, routes } from '@redwoodjs/router'
|
||||
import { MetaTags } from '@redwoodjs/web'
|
||||
import { toast, Toaster } from '@redwoodjs/web/toast'
|
||||
import {
|
||||
Form,
|
||||
Label,
|
||||
PasswordField,
|
||||
Submit,
|
||||
FieldError,
|
||||
} from '@redwoodjs/forms'
|
||||
|
||||
const ResetPasswordPage = ({ resetToken }) => {
|
||||
const { isAuthenticated, reauthenticate, validateResetToken, resetPassword } =
|
||||
useAuth()
|
||||
const [enabled, setEnabled] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
navigate(routes.home())
|
||||
}
|
||||
}, [isAuthenticated])
|
||||
|
||||
useEffect(() => {
|
||||
const validateToken = async () => {
|
||||
const response = await validateResetToken(resetToken)
|
||||
if (response.error) {
|
||||
setEnabled(false)
|
||||
toast.error(response.error)
|
||||
} else {
|
||||
setEnabled(true)
|
||||
}
|
||||
}
|
||||
validateToken()
|
||||
}, [])
|
||||
|
||||
const passwordRef = useRef()
|
||||
useEffect(() => {
|
||||
passwordRef.current.focus()
|
||||
}, [])
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
const response = await resetPassword({
|
||||
resetToken,
|
||||
password: data.password,
|
||||
})
|
||||
|
||||
if (response.error) {
|
||||
toast.error(response.error)
|
||||
} else {
|
||||
toast.success('Password changed!')
|
||||
await reauthenticate()
|
||||
navigate(routes.login())
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<MetaTags title="Reset Password" />
|
||||
|
||||
<main className="rw-main">
|
||||
<Toaster toastOptions={{ className: 'rw-toast', duration: 6000 }} />
|
||||
<div className="rw-scaffold rw-login-container">
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">
|
||||
Reset Password
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<div className="rw-segment-main">
|
||||
<div className="rw-form-wrapper">
|
||||
<Form onSubmit={onSubmit} className="rw-form-wrapper">
|
||||
<div className="text-left">
|
||||
<Label
|
||||
name="password"
|
||||
className="rw-label"
|
||||
errorClassName="rw-label rw-label-error"
|
||||
>
|
||||
New Password
|
||||
</Label>
|
||||
<PasswordField
|
||||
name="password"
|
||||
autoComplete="new-password"
|
||||
className="rw-input"
|
||||
errorClassName="rw-input rw-input-error"
|
||||
disabled={!enabled}
|
||||
ref={passwordRef}
|
||||
validation={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Password is required',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<FieldError name="password" className="rw-field-error" />
|
||||
</div>
|
||||
|
||||
<div className="rw-button-group">
|
||||
<Submit
|
||||
className="rw-button rw-button-blue"
|
||||
disabled={!enabled}
|
||||
>
|
||||
Submit
|
||||
</Submit>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default ResetPasswordPage
|
||||
124
web/src/pages/SignupPage/SignupPage.js
Normal file
124
web/src/pages/SignupPage/SignupPage.js
Normal file
@@ -0,0 +1,124 @@
|
||||
import { Link, navigate, routes } from '@redwoodjs/router'
|
||||
import { useRef } from 'react'
|
||||
import {
|
||||
Form,
|
||||
Label,
|
||||
TextField,
|
||||
PasswordField,
|
||||
FieldError,
|
||||
Submit,
|
||||
} from '@redwoodjs/forms'
|
||||
import { useAuth } from '@redwoodjs/auth'
|
||||
import { MetaTags } from '@redwoodjs/web'
|
||||
import { toast, Toaster } from '@redwoodjs/web/toast'
|
||||
import { useEffect } from 'react'
|
||||
|
||||
const SignupPage = () => {
|
||||
const { isAuthenticated, signUp } = useAuth()
|
||||
|
||||
useEffect(() => {
|
||||
if (isAuthenticated) {
|
||||
navigate(routes.home())
|
||||
}
|
||||
}, [isAuthenticated])
|
||||
|
||||
// focus on email box on page load
|
||||
const usernameRef = useRef()
|
||||
useEffect(() => {
|
||||
usernameRef.current.focus()
|
||||
}, [])
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
const response = await signUp({ ...data })
|
||||
|
||||
if (response.message) {
|
||||
toast(response.message)
|
||||
} else if (response.error) {
|
||||
toast.error(response.error)
|
||||
} else {
|
||||
// user is signed in automatically
|
||||
toast.success('Welcome!')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<MetaTags title="Signup" />
|
||||
|
||||
<main className="rw-main">
|
||||
<Toaster toastOptions={{ className: 'rw-toast', duration: 6000 }} />
|
||||
<div className="rw-scaffold rw-login-container">
|
||||
<div className="rw-segment">
|
||||
<header className="rw-segment-header">
|
||||
<h2 className="rw-heading rw-heading-secondary">Signup</h2>
|
||||
</header>
|
||||
|
||||
<div className="rw-segment-main">
|
||||
<div className="rw-form-wrapper">
|
||||
<Form onSubmit={onSubmit} className="rw-form-wrapper">
|
||||
<Label
|
||||
name="username"
|
||||
className="rw-label"
|
||||
errorClassName="rw-label rw-label-error"
|
||||
>
|
||||
Username
|
||||
</Label>
|
||||
<TextField
|
||||
name="username"
|
||||
className="rw-input"
|
||||
errorClassName="rw-input rw-input-error"
|
||||
ref={usernameRef}
|
||||
validation={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Username is required',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<FieldError name="username" className="rw-field-error" />
|
||||
|
||||
<Label
|
||||
name="password"
|
||||
className="rw-label"
|
||||
errorClassName="rw-label rw-label-error"
|
||||
>
|
||||
Password
|
||||
</Label>
|
||||
<PasswordField
|
||||
name="password"
|
||||
className="rw-input"
|
||||
errorClassName="rw-input rw-input-error"
|
||||
autoComplete="current-password"
|
||||
validation={{
|
||||
required: {
|
||||
value: true,
|
||||
message: 'Password is required',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
|
||||
<FieldError name="password" className="rw-field-error" />
|
||||
|
||||
<div className="rw-button-group">
|
||||
<Submit className="rw-button rw-button-blue">
|
||||
Sign Up
|
||||
</Submit>
|
||||
</div>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="rw-login-link">
|
||||
<span>Already have an account?</span>{' '}
|
||||
<Link to={routes.login()} className="rw-link">
|
||||
Log in!
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default SignupPage
|
||||
Reference in New Issue
Block a user