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
|
// Get code status
|
||||||
getCodeStatus: async (code: string): Promise<CodeStatusResponse> => {
|
getCodeStatus: async (code: string): Promise<CodeStatusResponse> => {
|
||||||
|
try {
|
||||||
const response = await adminApiClient.get(`/api/v2/admin/auth/code-status/${code}`)
|
const response = await adminApiClient.get(`/api/v2/admin/auth/code-status/${code}`)
|
||||||
return response.data
|
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
|
// Verify authentication code and login
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ export default function LoginPage() {
|
||||||
const [code, setCode] = useState('')
|
const [code, setCode] = useState('')
|
||||||
const [codeStatus, setCodeStatus] = useState<CodeStatusResponse | null>(null)
|
const [codeStatus, setCodeStatus] = useState<CodeStatusResponse | null>(null)
|
||||||
const [countdown, setCountdown] = useState<number>(0)
|
const [countdown, setCountdown] = useState<number>(0)
|
||||||
|
const [isVerifying, setIsVerifying] = useState(false)
|
||||||
const statusPollIntervalRef = useRef<NodeJS.Timeout | null>(null)
|
const statusPollIntervalRef = useRef<NodeJS.Timeout | null>(null)
|
||||||
const countdownIntervalRef = useRef<NodeJS.Timeout | null>(null)
|
const countdownIntervalRef = useRef<NodeJS.Timeout | null>(null)
|
||||||
|
|
||||||
|
|
@ -66,32 +67,58 @@ export default function LoginPage() {
|
||||||
const verifyCodeMutation = useMutation({
|
const verifyCodeMutation = useMutation({
|
||||||
mutationFn: (code: string) => authApi.verifyCode(code),
|
mutationFn: (code: string) => authApi.verifyCode(code),
|
||||||
onSuccess: (data) => {
|
onSuccess: (data) => {
|
||||||
|
console.log('Verify code response:', data)
|
||||||
|
|
||||||
// Check if response has success field and it's false
|
// Check if response has success field and it's false
|
||||||
if (data.success === 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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate response structure
|
// Validate response structure
|
||||||
if (!data.token || !data.user) {
|
if (!data.token || !data.user) {
|
||||||
|
console.error('Invalid response structure:', data)
|
||||||
toast.error('Invalid response from server')
|
toast.error('Invalid response from server')
|
||||||
|
setIsVerifying(false)
|
||||||
|
// Don't restart polling - this is a server error
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop polling
|
// Stop polling
|
||||||
stopStatusPolling()
|
stopStatusPolling()
|
||||||
stopCountdown()
|
stopCountdown()
|
||||||
|
setIsVerifying(false)
|
||||||
|
|
||||||
|
console.log('Login successful, setting token and user')
|
||||||
login(data.token, data.user)
|
login(data.token, data.user)
|
||||||
toast.success('Welcome back!')
|
toast.success('Welcome back!')
|
||||||
navigate('/')
|
navigate('/')
|
||||||
},
|
},
|
||||||
onError: (error: unknown) => {
|
onError: (error: unknown) => {
|
||||||
|
console.error('Verify code error:', error)
|
||||||
|
setIsVerifying(false)
|
||||||
|
const errorResponse = error as { response?: { data?: { message?: string }; status?: number } }
|
||||||
const errorMessage =
|
const errorMessage =
|
||||||
(error as { response?: { data?: { message?: string } } })?.response?.data?.message ||
|
errorResponse?.response?.data?.message ||
|
||||||
(error as { message?: string })?.message ||
|
(error as { message?: string })?.message ||
|
||||||
'Invalid code'
|
'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)
|
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) => {
|
const checkCodeStatus = async (codeToCheck: string) => {
|
||||||
try {
|
try {
|
||||||
const status = await authApi.getCodeStatus(codeToCheck)
|
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)
|
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
|
// Code is claimed, automatically verify and login
|
||||||
|
console.log('Code claimed, attempting login...')
|
||||||
|
setIsVerifying(true)
|
||||||
stopStatusPolling()
|
stopStatusPolling()
|
||||||
verifyCodeMutation.mutate(codeToCheck)
|
verifyCodeMutation.mutate(codeToCheck)
|
||||||
} else if (status.status === 'expired' || status.remainingSeconds <= 0) {
|
} else if (status.status === 'expired' || (status.remainingSeconds !== undefined && status.remainingSeconds <= 0)) {
|
||||||
stopStatusPolling()
|
stopStatusPolling()
|
||||||
stopCountdown()
|
stopCountdown()
|
||||||
toast.error('Code expired. Please generate a new one.')
|
toast.error('Code expired. Please generate a new one.')
|
||||||
} else if (status.isUsed) {
|
} else if (status.isUsed) {
|
||||||
stopStatusPolling()
|
stopStatusPolling()
|
||||||
stopCountdown()
|
stopCountdown()
|
||||||
|
toast.info('Code already used')
|
||||||
} else {
|
} else {
|
||||||
// Update countdown
|
// Update countdown
|
||||||
if (status.remainingSeconds > 0) {
|
if (status.remainingSeconds !== undefined && status.remainingSeconds > 0) {
|
||||||
setCountdown(status.remainingSeconds)
|
setCountdown(status.remainingSeconds)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to check code status:', 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 = () => {
|
const handleBack = () => {
|
||||||
stopStatusPolling()
|
stopStatusPolling()
|
||||||
stopCountdown()
|
stopCountdown()
|
||||||
|
setIsVerifying(false)
|
||||||
setCode('')
|
setCode('')
|
||||||
setCodeStatus(null)
|
setCodeStatus(null)
|
||||||
setCountdown(0)
|
setCountdown(0)
|
||||||
|
|
@ -283,11 +329,11 @@ export default function LoginPage() {
|
||||||
<div className="flex items-center justify-between">
|
<div className="flex items-center justify-between">
|
||||||
<span className="text-sm font-medium">Status:</span>
|
<span className="text-sm font-medium">Status:</span>
|
||||||
<span className={`text-sm font-semibold ${
|
<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' :
|
codeStatus.status === 'expired' || codeStatus.status === 'used' ? 'text-red-600' :
|
||||||
'text-gray-600'
|
'text-gray-600'
|
||||||
}`}>
|
}`}>
|
||||||
{getStatusLabel(codeStatus.status)}
|
{isVerifying ? 'Verifying and logging in...' : getStatusLabel(codeStatus.status)}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{isCodeActive && (
|
{isCodeActive && (
|
||||||
|
|
@ -298,6 +344,15 @@ export default function LoginPage() {
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue