stuff
Some checks are pending
Deploy Admin Panel / Deploy Admin Panel (push) Waiting to run
Deploy Admin Panel / Admin Panel Verification (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
Some checks are pending
Deploy Admin Panel / Deploy Admin Panel (push) Waiting to run
Deploy Admin Panel / Admin Panel Verification (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions
This commit is contained in:
parent
4f63b48ca2
commit
20db3638a1
2 changed files with 82 additions and 10 deletions
|
|
@ -10,8 +10,25 @@ export const authApi = {
|
|||
|
||||
// Get code status
|
||||
getCodeStatus: async (code: string): Promise<CodeStatusResponse> => {
|
||||
try {
|
||||
const response = await adminApiClient.get(`/api/v2/admin/auth/code-status/${code}`)
|
||||
return response.data
|
||||
} catch (error: any) {
|
||||
// If 404, return a proper error response
|
||||
if (error.response?.status === 404) {
|
||||
return {
|
||||
success: false,
|
||||
code: code,
|
||||
status: 'not_found',
|
||||
remainingSeconds: 0,
|
||||
isClaimed: false,
|
||||
isUsed: false,
|
||||
error: 'NotFound',
|
||||
message: 'Code not found',
|
||||
}
|
||||
}
|
||||
throw error
|
||||
}
|
||||
},
|
||||
|
||||
// Verify authentication code and login
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ export default function LoginPage() {
|
|||
const [code, setCode] = useState('')
|
||||
const [codeStatus, setCodeStatus] = useState<CodeStatusResponse | null>(null)
|
||||
const [countdown, setCountdown] = useState<number>(0)
|
||||
const [isVerifying, setIsVerifying] = useState(false)
|
||||
const statusPollIntervalRef = useRef<NodeJS.Timeout | null>(null)
|
||||
const countdownIntervalRef = useRef<NodeJS.Timeout | null>(null)
|
||||
|
||||
|
|
@ -66,32 +67,58 @@ export default function LoginPage() {
|
|||
const verifyCodeMutation = useMutation({
|
||||
mutationFn: (code: string) => authApi.verifyCode(code),
|
||||
onSuccess: (data) => {
|
||||
console.log('Verify code response:', data)
|
||||
|
||||
// Check if response has success field and it's false
|
||||
if (data.success === false) {
|
||||
toast.error(data.message || 'Verification failed')
|
||||
const errorMsg = data.message || 'Verification failed'
|
||||
console.error('Verification failed:', errorMsg)
|
||||
toast.error(errorMsg)
|
||||
setIsVerifying(false)
|
||||
// Don't restart polling if verification failed - user needs to generate new code
|
||||
return
|
||||
}
|
||||
|
||||
// Validate response structure
|
||||
if (!data.token || !data.user) {
|
||||
console.error('Invalid response structure:', data)
|
||||
toast.error('Invalid response from server')
|
||||
setIsVerifying(false)
|
||||
// Don't restart polling - this is a server error
|
||||
return
|
||||
}
|
||||
|
||||
// Stop polling
|
||||
stopStatusPolling()
|
||||
stopCountdown()
|
||||
setIsVerifying(false)
|
||||
|
||||
console.log('Login successful, setting token and user')
|
||||
login(data.token, data.user)
|
||||
toast.success('Welcome back!')
|
||||
navigate('/')
|
||||
},
|
||||
onError: (error: unknown) => {
|
||||
console.error('Verify code error:', error)
|
||||
setIsVerifying(false)
|
||||
const errorResponse = error as { response?: { data?: { message?: string }; status?: number } }
|
||||
const errorMessage =
|
||||
(error as { response?: { data?: { message?: string } } })?.response?.data?.message ||
|
||||
errorResponse?.response?.data?.message ||
|
||||
(error as { message?: string })?.message ||
|
||||
'Invalid code'
|
||||
|
||||
// If 403, it means code was claimed but user is not admin
|
||||
if (errorResponse?.response?.status === 403) {
|
||||
toast.error('Access denied: You are not an admin. Code was claimed but verification failed.')
|
||||
stopStatusPolling()
|
||||
stopCountdown()
|
||||
} else {
|
||||
toast.error(errorMessage)
|
||||
// Restart polling in case of other errors
|
||||
if (code) {
|
||||
startStatusPolling(code)
|
||||
}
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
|
|
@ -117,27 +144,45 @@ export default function LoginPage() {
|
|||
const checkCodeStatus = async (codeToCheck: string) => {
|
||||
try {
|
||||
const status = await authApi.getCodeStatus(codeToCheck)
|
||||
|
||||
// Check if response is successful
|
||||
if (!status.success) {
|
||||
console.error('Code status check failed:', status.message || status.error)
|
||||
if (status.error === 'NotFound') {
|
||||
stopStatusPolling()
|
||||
stopCountdown()
|
||||
toast.error('Code not found. Please generate a new one.')
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
setCodeStatus(status)
|
||||
|
||||
if (status.status === 'claimed' && !status.isUsed) {
|
||||
console.log('Code status:', status.status, 'isClaimed:', status.isClaimed, 'isUsed:', status.isUsed)
|
||||
|
||||
if (status.status === 'claimed' && !status.isUsed && !isVerifying) {
|
||||
// Code is claimed, automatically verify and login
|
||||
console.log('Code claimed, attempting login...')
|
||||
setIsVerifying(true)
|
||||
stopStatusPolling()
|
||||
verifyCodeMutation.mutate(codeToCheck)
|
||||
} else if (status.status === 'expired' || status.remainingSeconds <= 0) {
|
||||
} else if (status.status === 'expired' || (status.remainingSeconds !== undefined && status.remainingSeconds <= 0)) {
|
||||
stopStatusPolling()
|
||||
stopCountdown()
|
||||
toast.error('Code expired. Please generate a new one.')
|
||||
} else if (status.isUsed) {
|
||||
stopStatusPolling()
|
||||
stopCountdown()
|
||||
toast.info('Code already used')
|
||||
} else {
|
||||
// Update countdown
|
||||
if (status.remainingSeconds > 0) {
|
||||
if (status.remainingSeconds !== undefined && status.remainingSeconds > 0) {
|
||||
setCountdown(status.remainingSeconds)
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to check code status:', error)
|
||||
// Don't stop polling on network errors, just log
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -195,6 +240,7 @@ export default function LoginPage() {
|
|||
const handleBack = () => {
|
||||
stopStatusPolling()
|
||||
stopCountdown()
|
||||
setIsVerifying(false)
|
||||
setCode('')
|
||||
setCodeStatus(null)
|
||||
setCountdown(0)
|
||||
|
|
@ -283,11 +329,11 @@ export default function LoginPage() {
|
|||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium">Status:</span>
|
||||
<span className={`text-sm font-semibold ${
|
||||
codeStatus.status === 'claimed' ? 'text-green-600' :
|
||||
codeStatus.status === 'claimed' ? (isVerifying ? 'text-blue-600' : 'text-green-600') :
|
||||
codeStatus.status === 'expired' || codeStatus.status === 'used' ? 'text-red-600' :
|
||||
'text-gray-600'
|
||||
}`}>
|
||||
{getStatusLabel(codeStatus.status)}
|
||||
{isVerifying ? 'Verifying and logging in...' : getStatusLabel(codeStatus.status)}
|
||||
</span>
|
||||
</div>
|
||||
{isCodeActive && (
|
||||
|
|
@ -298,6 +344,15 @@ export default function LoginPage() {
|
|||
</span>
|
||||
</div>
|
||||
)}
|
||||
{/* Debug info */}
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<div className="mt-2 pt-2 border-t text-xs text-gray-500">
|
||||
<div>Status: {codeStatus.status}</div>
|
||||
<div>Claimed: {codeStatus.isClaimed ? 'Yes' : 'No'}</div>
|
||||
<div>Used: {codeStatus.isUsed ? 'Yes' : 'No'}</div>
|
||||
<div>Verifying: {isVerifying ? 'Yes' : 'No'}</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
Loading…
Reference in a new issue