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

This commit is contained in:
Dmitry 2025-12-11 22:57:55 +03:00
parent 4f63b48ca2
commit 20db3638a1
2 changed files with 82 additions and 10 deletions

View file

@ -10,8 +10,25 @@ export const authApi = {
// Get code status
getCodeStatus: async (code: string): Promise<CodeStatusResponse> => {
const response = await adminApiClient.get(`/api/v2/admin/auth/code-status/${code}`)
return response.data
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

View file

@ -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'
toast.error(errorMessage)
// 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>
)}