1
0
Fork 0
freesewing/sites/shared/components/wrappers/auth/index.mjs

206 lines
5.8 KiB
JavaScript
Raw Normal View History

2023-01-22 19:46:56 +01:00
import Link from 'next/link'
import { useTranslation } from 'next-i18next'
2023-03-24 17:43:38 +01:00
import { useAccount } from 'shared/hooks/use-account.mjs'
import { useBackend } from 'shared/hooks/use-backend.mjs'
2023-05-08 09:31:37 +02:00
import { roles } from 'config/roles.mjs'
2023-08-14 19:16:47 +02:00
import { useEffect, useState } from 'react'
import { Loading } from 'shared/components/spinner.mjs'
import { horFlexClasses } from 'shared/utils.mjs'
import { LockIcon, PlusIcon } from 'shared/components/icons.mjs'
import { ConsentForm, ns as gdprNs } from 'shared/components/gdpr/form.mjs'
2023-01-22 19:46:56 +01:00
export const ns = ['auth', 'gdpr']
2023-01-22 19:46:56 +01:00
const Wrap = ({ children }) => (
<div className="m-auto max-w-xl text-center mt-8 p-8">{children}</div>
2023-01-22 19:46:56 +01:00
)
const ContactSupport = ({ t }) => (
<div className="flex flex-row items-center justify-center gap-4 mt-8">
<Link href="/support" className="btn btn-success w-full">
2023-01-22 19:46:56 +01:00
{t('contactSupport')}
</Link>
</div>
)
const AuthRequired = ({ t, banner }) => (
2023-01-22 19:46:56 +01:00
<Wrap>
{banner}
<h2>{t('authRequired')}</h2>
2023-01-22 19:46:56 +01:00
<p>{t('membersOnly')}</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 mt-8">
<Link href="/signup" className={`${horFlexClasses} btn btn-secondary w-full`}>
<PlusIcon />
2023-01-22 19:46:56 +01:00
{t('signUp')}
</Link>
<Link href="/signin" className={`${horFlexClasses} btn btn-secondary btn-outline w-full`}>
<LockIcon />
{t('signIn')}
2023-01-22 19:46:56 +01:00
</Link>
</div>
</Wrap>
)
const AccountInactive = ({ t, banner }) => (
2023-01-22 19:46:56 +01:00
<Wrap>
{banner}
2023-01-22 19:46:56 +01:00
<h1>{t('accountInactive')}</h1>
<p>{t('accountInactiveMsg')}</p>
<p>{t('signupAgain')}</p>
<div className="flex flex-row items-center justify-center gap-4 mt-8">
<Link href="/signup" className="btn btn-primary w-full">
{t('signUp')}
</Link>
</div>
</Wrap>
)
const AccountDisabled = ({ t, banner }) => (
2023-01-22 19:46:56 +01:00
<Wrap>
{banner}
2023-01-22 19:46:56 +01:00
<h1>{t('accountDisabled')}</h1>
<p>{t('accountDisabledMsg')}</p>
<ContactSupport t={t} />
</Wrap>
)
const AccountProhibited = ({ t, banner }) => (
2023-01-22 19:46:56 +01:00
<Wrap>
{banner}
2023-01-22 19:46:56 +01:00
<h1>{t('accountProhibited')}</h1>
<p>{t('accountProhibitedMsg')}</p>
<ContactSupport t={t} />
</Wrap>
)
const AccountStatusUnknown = ({ t, banner }) => (
2023-01-22 19:46:56 +01:00
<Wrap>
{banner}
2023-01-22 19:46:56 +01:00
<h1>{t('statusUnknown')}</h1>
<p>{t('statusUnknownMsg')}</p>
<ContactSupport t={t} />
</Wrap>
)
const RoleLacking = ({ t, requiredRole, role, banner }) => (
2023-05-08 09:31:37 +02:00
<Wrap>
{banner}
2023-05-08 09:31:37 +02:00
<h1>{t('roleLacking')}</h1>
<p dangerouslySetInnerHTML={{ __html: t('roleLackingMsg', { requiredRole, role }) }} />
<ContactSupport t={t} />
</Wrap>
)
const ConsentLacking = ({ banner, refresh }) => {
const { setAccount, setToken, setSeenUser } = useAccount()
const backend = useBackend()
const { t } = useTranslation(ns)
const updateConsent = async ({ consent1, consent2 }) => {
let consent = 0
if (consent1) consent = 1
if (consent1 && consent2) consent = 2
if (consent > 0) {
const result = await backend.updateConsent(consent)
console.log({ result })
if (result.success) {
setToken(result.data.token)
setAccount(result.data.account)
setSeenUser(result.data.account.username)
refresh()
} else {
console.log('something went wrong', result)
refresh()
}
}
}
return (
<Wrap>
<div className="text-left">
{banner}
<ConsentForm submit={updateConsent} />
</div>
</Wrap>
)
}
2023-01-22 19:46:56 +01:00
2023-08-17 20:02:46 +02:00
export const AuthWrapper = ({ children, requiredRole = 'user' }) => {
const { t } = useTranslation(ns)
const { account, token, admin, stopImpersonating, signOut } = useAccount()
const backend = useBackend()
2023-08-14 19:16:47 +02:00
const [ready, setReady] = useState(false)
const [impersonating, setImpersonating] = useState(false)
const [error, setError] = useState(false)
const [refreshCount, setRefreshCount] = useState(0)
2023-08-14 19:16:47 +02:00
/*
* Avoid hydration errors
*/
useEffect(() => {
const verifyAdmin = async () => {
const result = await backend.adminPing(admin.token)
if (result.success && result.data.account.role === 'admin') {
setImpersonating({
admin: result.data.account.username,
user: account.username,
})
}
setReady(true)
}
const verifyUser = async () => {
const result = await backend.ping()
if (!result.success) {
if (result.data?.error?.error) setError(result.data.error.error)
else signOut()
}
setReady(true)
}
if (admin && admin.token) verifyAdmin()
if (token) verifyUser()
else setReady(true)
}, [admin, token, refreshCount])
const refresh = () => {
setRefreshCount(refreshCount + 1)
setError(false)
}
2023-08-14 19:16:47 +02:00
if (!ready)
return (
<>
<p>not ready</p>
<Loading />
</>
)
2023-08-14 19:16:47 +02:00
const banner = impersonating ? (
<div className="bg-warning rounded-lg shadow py-4 px-6 flex flex-row items-center gap-4 justify-between">
<span className="text-base-100 text-left">
Hi <b>{impersonating.admin}</b>, you are currently impersonating <b>{impersonating.user}</b>
</span>
<button className="btn btn-neutral" onClick={stopImpersonating}>
Stop Impersonating
</button>
</div>
) : null
const childProps = { t, banner }
if (!token || !account.username) return <AuthRequired {...childProps} />
if (error) {
if (error === 'accountInactive') return <AccountInactive {...childProps} />
if (error === 'accountDisabled') return <AccountDisabled {...childProps} />
if (error === 'accountBlocked') return <AccountProhibited {...childProps} />
if (error === 'consentLacking') return <ConsentLacking {...childProps} refresh={refresh} />
return <AccountStatusUnknown {...childProps} />
2023-01-22 19:46:56 +01:00
}
2023-05-08 09:31:37 +02:00
if (!roles.levels[account.role] || roles.levels[account.role] < roles.levels[requiredRole]) {
return <RoleLacking {...childProps} role={account.role} requiredRole={requiredRole} />
2023-05-08 09:31:37 +02:00
}
2023-01-22 19:46:56 +01:00
return children
}