File size: 7,605 Bytes
9564069 11b4cc9 7e2344c 270cb30 11b4cc9 4421a58 11b4cc9 ce48dbe 11b4cc9 270cb30 4421a58 11b4cc9 270cb30 9564069 270cb30 0d8fa96 c114561 0d8fa96 c114561 270cb30 382e628 270cb30 9564069 a845c0d 270cb30 a845c0d 9564069 17643b8 9564069 382e628 270cb30 08aa15f 270cb30 9564069 270cb30 17643b8 6fd7cb8 17643b8 270cb30 9564069 6fd7cb8 270cb30 9564069 270cb30 382e628 270cb30 11b4cc9 4421a58 11b4cc9 382e628 7e2344c 270cb30 08aa15f a845c0d 9564069 382e628 270cb30 05f0f17 f9b14ab 11b4cc9 4421a58 05f0f17 f9b14ab 05f0f17 f9b14ab 11b4cc9 4421a58 ce48dbe 4421a58 11b4cc9 c9b5d82 11b4cc9 4421a58 11b4cc9 a2fdb3c 4421a58 11b4cc9 a2fdb3c 4421a58 11b4cc9 a2fdb3c 4421a58 11b4cc9 a2fdb3c 11b4cc9 4421a58 11b4cc9 4421a58 11b4cc9 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 |
import { useState, useEffect, useRef } from 'react'
import { useNavigate } from 'react-router-dom'
import { useAuthStore } from '@/stores/state'
import { useSettingsStore } from '@/stores/settings'
import { loginToServer, getAuthStatus } from '@/api/lightrag'
import { toast } from 'sonner'
import { useTranslation } from 'react-i18next'
import { Card, CardContent, CardHeader } from '@/components/ui/Card'
import Input from '@/components/ui/Input'
import Button from '@/components/ui/Button'
import { ZapIcon } from 'lucide-react'
import AppSettings from '@/components/AppSettings'
const LoginPage = () => {
const navigate = useNavigate()
const { login, isAuthenticated } = useAuthStore()
const { t } = useTranslation()
const [loading, setLoading] = useState(false)
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [checkingAuth, setCheckingAuth] = useState(true)
const authCheckRef = useRef(false); // Prevent duplicate calls in Vite dev mode
useEffect(() => {
console.log('LoginPage mounted')
}, []);
// Check if authentication is configured, skip login if not
useEffect(() => {
const checkAuthConfig = async () => {
// Prevent duplicate calls in Vite dev mode
if (authCheckRef.current) {
return;
}
authCheckRef.current = true;
try {
// If already authenticated, redirect to home
if (isAuthenticated) {
navigate('/')
return
}
// Check auth status
const status = await getAuthStatus()
// Set session flag for version check to avoid duplicate checks in App component
if (status.core_version || status.api_version) {
sessionStorage.setItem('VERSION_CHECKED_FROM_LOGIN', 'true');
}
if (!status.auth_configured && status.access_token) {
// If auth is not configured, use the guest token and redirect
login(status.access_token, true, status.core_version, status.api_version, status.webui_title || null, status.webui_description || null)
if (status.message) {
toast.info(status.message)
}
navigate('/')
return
}
// Only set checkingAuth to false if we need to show the login page
setCheckingAuth(false);
} catch (error) {
console.error('Failed to check auth configuration:', error)
// Also set checkingAuth to false in case of error
setCheckingAuth(false);
}
// Removed finally block as we're setting checkingAuth earlier
}
// Execute immediately
checkAuthConfig()
// Cleanup function to prevent state updates after unmount
return () => {
}
}, [isAuthenticated, login, navigate])
// Don't render anything while checking auth
if (checkingAuth) {
return null
}
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault()
if (!username || !password) {
toast.error(t('login.errorEmptyFields'))
return
}
try {
setLoading(true)
const response = await loginToServer(username, password)
// Get previous username from localStorage
const previousUsername = localStorage.getItem('LIGHTRAG-PREVIOUS-USER')
// Check if it's the same user logging in again
const isSameUser = previousUsername === username
// If it's not the same user, clear chat history
if (isSameUser) {
console.log('Same user logging in, preserving chat history')
} else {
console.log('Different user logging in, clearing chat history')
// Directly clear chat history instead of setting a flag
useSettingsStore.getState().setRetrievalHistory([])
}
// Update previous username
localStorage.setItem('LIGHTRAG-PREVIOUS-USER', username)
// Check authentication mode
const isGuestMode = response.auth_mode === 'disabled'
login(response.access_token, isGuestMode, response.core_version, response.api_version, response.webui_title || null, response.webui_description || null)
// Set session flag for version check
if (response.core_version || response.api_version) {
sessionStorage.setItem('VERSION_CHECKED_FROM_LOGIN', 'true');
}
if (isGuestMode) {
// Show authentication disabled notification
toast.info(response.message || t('login.authDisabled', 'Authentication is disabled. Using guest access.'))
} else {
toast.success(t('login.successMessage'))
}
// Navigate to home page after successful login
navigate('/')
} catch (error) {
console.error('Login failed...', error)
toast.error(t('login.errorInvalidCredentials'))
// Clear any existing auth state
useAuthStore.getState().logout()
// Clear local storage
localStorage.removeItem('LIGHTRAG-API-TOKEN')
} finally {
setLoading(false)
}
}
return (
<div className="flex h-screen w-screen items-center justify-center bg-gradient-to-br from-emerald-50 to-teal-100 dark:from-gray-900 dark:to-gray-800">
<div className="absolute top-4 right-4 flex items-center gap-2">
<AppSettings className="bg-white/30 dark:bg-gray-800/30 backdrop-blur-sm rounded-md" />
</div>
<Card className="w-full max-w-[480px] shadow-lg mx-4">
<CardHeader className="flex items-center justify-center space-y-2 pb-8 pt-6">
<div className="flex flex-col items-center space-y-4">
<div className="flex items-center gap-3">
<img src="logo.png" alt="LightRAG Logo" className="h-12 w-12" />
<ZapIcon className="size-10 text-emerald-400" aria-hidden="true" />
</div>
<div className="text-center space-y-2">
<h1 className="text-3xl font-bold tracking-tight">LightRAG</h1>
<p className="text-muted-foreground text-sm">
{t('login.description')}
</p>
</div>
</div>
</CardHeader>
<CardContent className="px-8 pb-8">
<form onSubmit={handleSubmit} className="space-y-6">
<div className="flex items-center gap-4">
<label htmlFor="username-input" className="text-sm font-medium w-16 shrink-0">
{t('login.username')}
</label>
<Input
id="username-input"
placeholder={t('login.usernamePlaceholder')}
value={username}
onChange={(e) => setUsername(e.target.value)}
required
className="h-11 flex-1"
/>
</div>
<div className="flex items-center gap-4">
<label htmlFor="password-input" className="text-sm font-medium w-16 shrink-0">
{t('login.password')}
</label>
<Input
id="password-input"
type="password"
placeholder={t('login.passwordPlaceholder')}
value={password}
onChange={(e) => setPassword(e.target.value)}
required
className="h-11 flex-1"
/>
</div>
<Button
type="submit"
className="w-full h-11 text-base font-medium mt-2"
disabled={loading}
>
{loading ? t('login.loggingIn') : t('login.loginButton')}
</Button>
</form>
</CardContent>
</Card>
</div>
)
}
export default LoginPage
|