New Auth Methods (#8119)

This commit is contained in:
NFish
2024-10-21 09:23:20 +08:00
committed by GitHub
parent 853b0e84cc
commit 3898fe3311
32 changed files with 1568 additions and 787 deletions

View File

@@ -0,0 +1,5 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g id="lock">
<path id="Vector" fill-rule="evenodd" clip-rule="evenodd" d="M8 1.75C6.27411 1.75 4.875 3.14911 4.875 4.875V6.125C3.83947 6.125 3 6.96444 3 8V12.375C3 13.4106 3.83947 14.25 4.875 14.25H11.125C12.1606 14.25 13 13.4106 13 12.375V8C13 6.96444 12.1606 6.125 11.125 6.125V4.875C11.125 3.14911 9.72587 1.75 8 1.75ZM9.875 6.125V4.875C9.875 3.83947 9.03556 3 8 3C6.96444 3 6.125 3.83947 6.125 4.875V6.125H9.875ZM8 8.625C8.34519 8.625 8.625 8.90481 8.625 9.25V11.125C8.625 11.4702 8.34519 11.75 8 11.75C7.65481 11.75 7.375 11.4702 7.375 11.125V9.25C7.375 8.90481 7.65481 8.625 8 8.625Z" fill="#155AEF"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 717 B

View File

@@ -2,7 +2,7 @@
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { useContext } from 'use-context-selector'
import { useContext, useContextSelector } from 'use-context-selector'
import Collapse from '../collapse'
import type { IItem } from '../collapse'
import s from './index.module.css'
@@ -11,7 +11,7 @@ import Modal from '@/app/components/base/modal'
import Confirm from '@/app/components/base/confirm'
import Button from '@/app/components/base/button'
import { updateUserProfile } from '@/service/common'
import { useAppContext } from '@/context/app-context'
import AppContext, { useAppContext } from '@/context/app-context'
import { ToastContext } from '@/app/components/base/toast'
import AppIcon from '@/app/components/base/app-icon'
import Avatar from '@/app/components/base/avatar'
@@ -42,6 +42,7 @@ export default function AccountPage() {
const [password, setPassword] = useState('')
const [confirmPassword, setConfirmPassword] = useState('')
const [showDeleteAccountModal, setShowDeleteAccountModal] = useState(false)
const systemFeatures = useContextSelector(AppContext, state => state.systemFeatures)
const handleEditName = () => {
setEditNameModalVisible(true)
@@ -144,7 +145,7 @@ export default function AccountPage() {
<div className={titleClassName}>{t('common.account.email')}</div>
<div className={classNames(inputClassName, 'cursor-pointer')}>{userProfile.email}</div>
</div>
{IS_CE_EDITION && (
{systemFeatures.enable_email_password_login && (
<div className='mb-8'>
<div className='mb-1 text-sm font-medium text-gray-900'>{t('common.account.password')}</div>
<div className='mb-2 text-xs text-gray-500'>{t('common.account.passwordTip')}</div>

View File

@@ -0,0 +1,41 @@
'use client'
import { useCountDown } from 'ahooks'
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
export const COUNT_DOWN_TIME_MS = 59000
export const COUNT_DOWN_KEY = 'leftTime'
type CountdownProps = {
onResend?: () => void
}
export default function Countdown({ onResend }: CountdownProps) {
const { t } = useTranslation()
const [leftTime, setLeftTime] = useState(Number(localStorage.getItem(COUNT_DOWN_KEY) || COUNT_DOWN_TIME_MS))
const [time] = useCountDown({
leftTime,
onEnd: () => {
setLeftTime(0)
localStorage.removeItem(COUNT_DOWN_KEY)
},
})
const resend = async function () {
setLeftTime(COUNT_DOWN_TIME_MS)
localStorage.setItem(COUNT_DOWN_KEY, `${COUNT_DOWN_TIME_MS}`)
onResend?.()
}
useEffect(() => {
localStorage.setItem(COUNT_DOWN_KEY, `${time}`)
}, [time])
return <p className='system-xs-regular text-text-tertiary'>
<span>{t('login.checkCode.didNotReceiveCode')}</span>
{time > 0 && <span>{Math.round(time / 1000)}s</span>}
{
time <= 0 && <span className='system-xs-medium text-text-accent-secondary cursor-pointer' onClick={resend}>{t('login.checkCode.resend')}</span>
}
</p>
}

View File

@@ -1,10 +1,11 @@
'use client'
import { SWRConfig } from 'swr'
import { useEffect, useState } from 'react'
import { useCallback, useEffect, useState } from 'react'
import type { ReactNode } from 'react'
import { useRouter, useSearchParams } from 'next/navigation'
import useRefreshToken from '@/hooks/use-refresh-token'
import { fetchSetupStatus } from '@/service/common'
type SwrInitorProps = {
children: ReactNode
@@ -21,27 +22,60 @@ const SwrInitor = ({
const refreshTokenFromLocalStorage = localStorage?.getItem('refresh_token')
const [init, setInit] = useState(false)
useEffect(() => {
if (!(consoleToken || refreshToken || consoleTokenFromLocalStorage || refreshTokenFromLocalStorage)) {
router.replace('/signin')
return
const isSetupFinished = useCallback(async () => {
try {
if (localStorage.getItem('setup_status') === 'finished')
return true
const setUpStatus = await fetchSetupStatus()
if (setUpStatus.step !== 'finished') {
localStorage.removeItem('setup_status')
return false
}
localStorage.setItem('setup_status', 'finished')
return true
}
if (consoleTokenFromLocalStorage && refreshTokenFromLocalStorage)
getNewAccessToken()
if (consoleToken && refreshToken) {
localStorage.setItem('console_token', consoleToken)
localStorage.setItem('refresh_token', refreshToken)
getNewAccessToken().then(() => {
router.replace('/apps', { forceOptimisticNavigation: false } as any)
}).catch(() => {
router.replace('/signin')
})
catch (error) {
console.error(error)
return false
}
setInit(true)
}, [])
const setRefreshToken = useCallback(async () => {
try {
if (!(consoleToken || refreshToken || consoleTokenFromLocalStorage || refreshTokenFromLocalStorage))
return Promise.reject(new Error('No token found'))
if (consoleTokenFromLocalStorage && refreshTokenFromLocalStorage)
await getNewAccessToken()
if (consoleToken && refreshToken) {
localStorage.setItem('console_token', consoleToken)
localStorage.setItem('refresh_token', refreshToken)
await getNewAccessToken()
}
}
catch (error) {
return Promise.reject(error)
}
}, [consoleToken, refreshToken, consoleTokenFromLocalStorage, refreshTokenFromLocalStorage, getNewAccessToken])
useEffect(() => {
(async () => {
try {
const isFinished = await isSetupFinished()
if (!isFinished) {
router.replace('/install')
return
}
await setRefreshToken()
setInit(true)
}
catch (error) {
router.replace('/signin')
}
})()
}, [isSetupFinished, setRefreshToken, router])
return init
? (
<SWRConfig value={{