Fix linting

This commit is contained in:
yangdx
2025-03-18 03:30:43 +08:00
parent a1a74d3338
commit 43996656d3
9 changed files with 79 additions and 81 deletions

View File

@@ -17,18 +17,26 @@ class AuthHandler:
self.secret = os.getenv("TOKEN_SECRET", "4f85ds4f56dsf46") self.secret = os.getenv("TOKEN_SECRET", "4f85ds4f56dsf46")
self.algorithm = "HS256" self.algorithm = "HS256"
self.expire_hours = int(os.getenv("TOKEN_EXPIRE_HOURS", 4)) self.expire_hours = int(os.getenv("TOKEN_EXPIRE_HOURS", 4))
self.guest_expire_hours = int(os.getenv("GUEST_TOKEN_EXPIRE_HOURS", 2)) # Guest token default expiration time self.guest_expire_hours = int(
os.getenv("GUEST_TOKEN_EXPIRE_HOURS", 2)
) # Guest token default expiration time
def create_token(self, username: str, role: str = "user", custom_expire_hours: int = None, metadata: dict = None) -> str: def create_token(
self,
username: str,
role: str = "user",
custom_expire_hours: int = None,
metadata: dict = None,
) -> str:
""" """
Create JWT token Create JWT token
Args: Args:
username: Username username: Username
role: User role, default is "user", guest is "guest" role: User role, default is "user", guest is "guest"
custom_expire_hours: Custom expiration time (hours), if None use default value custom_expire_hours: Custom expiration time (hours), if None use default value
metadata: Additional metadata metadata: Additional metadata
Returns: Returns:
str: Encoded JWT token str: Encoded JWT token
""" """
@@ -40,29 +48,26 @@ class AuthHandler:
expire_hours = self.expire_hours expire_hours = self.expire_hours
else: else:
expire_hours = custom_expire_hours expire_hours = custom_expire_hours
expire = datetime.utcnow() + timedelta(hours=expire_hours) expire = datetime.utcnow() + timedelta(hours=expire_hours)
# Create payload # Create payload
payload = TokenPayload( payload = TokenPayload(
sub=username, sub=username, exp=expire, role=role, metadata=metadata or {}
exp=expire,
role=role,
metadata=metadata or {}
) )
return jwt.encode(payload.dict(), self.secret, algorithm=self.algorithm) return jwt.encode(payload.dict(), self.secret, algorithm=self.algorithm)
def validate_token(self, token: str) -> dict: def validate_token(self, token: str) -> dict:
""" """
Validate JWT token Validate JWT token
Args: Args:
token: JWT token token: JWT token
Returns: Returns:
dict: Dictionary containing user information dict: Dictionary containing user information
Raises: Raises:
HTTPException: If token is invalid or expired HTTPException: If token is invalid or expired
""" """
@@ -75,13 +80,13 @@ class AuthHandler:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Token expired" status_code=status.HTTP_401_UNAUTHORIZED, detail="Token expired"
) )
# Return complete payload instead of just username # Return complete payload instead of just username
return { return {
"username": payload["sub"], "username": payload["sub"],
"role": payload.get("role", "user"), "role": payload.get("role", "user"),
"metadata": payload.get("metadata", {}), "metadata": payload.get("metadata", {}),
"exp": expire_time "exp": expire_time,
} }
except jwt.PyJWTError: except jwt.PyJWTError:
raise HTTPException( raise HTTPException(

View File

@@ -350,22 +350,17 @@ def create_app(args):
if not (username and password): if not (username and password):
# Authentication not configured, return guest token # Authentication not configured, return guest token
guest_token = auth_handler.create_token( guest_token = auth_handler.create_token(
username="guest", username="guest", role="guest", metadata={"auth_mode": "disabled"}
role="guest",
metadata={"auth_mode": "disabled"}
) )
return { return {
"auth_configured": False, "auth_configured": False,
"access_token": guest_token, "access_token": guest_token,
"token_type": "bearer", "token_type": "bearer",
"auth_mode": "disabled", "auth_mode": "disabled",
"message": "Authentication is disabled. Using guest access." "message": "Authentication is disabled. Using guest access.",
} }
return { return {"auth_configured": True, "auth_mode": "enabled"}
"auth_configured": True,
"auth_mode": "enabled"
}
@app.post("/login", dependencies=[Depends(optional_api_key)]) @app.post("/login", dependencies=[Depends(optional_api_key)])
async def login(form_data: OAuth2PasswordRequestForm = Depends()): async def login(form_data: OAuth2PasswordRequestForm = Depends()):
@@ -375,15 +370,13 @@ def create_app(args):
if not (username and password): if not (username and password):
# Authentication not configured, return guest token # Authentication not configured, return guest token
guest_token = auth_handler.create_token( guest_token = auth_handler.create_token(
username="guest", username="guest", role="guest", metadata={"auth_mode": "disabled"}
role="guest",
metadata={"auth_mode": "disabled"}
) )
return { return {
"access_token": guest_token, "access_token": guest_token,
"token_type": "bearer", "token_type": "bearer",
"auth_mode": "disabled", "auth_mode": "disabled",
"message": "Authentication is disabled. Using guest access." "message": "Authentication is disabled. Using guest access.",
} }
if form_data.username != username or form_data.password != password: if form_data.username != username or form_data.password != password:
@@ -393,14 +386,12 @@ def create_app(args):
# Regular user login # Regular user login
user_token = auth_handler.create_token( user_token = auth_handler.create_token(
username=username, username=username, role="user", metadata={"auth_mode": "enabled"}
role="user",
metadata={"auth_mode": "enabled"}
) )
return { return {
"access_token": user_token, "access_token": user_token,
"token_type": "bearer", "token_type": "bearer",
"auth_mode": "enabled" "auth_mode": "enabled",
} }
@app.get("/health", dependencies=[Depends(optional_api_key)]) @app.get("/health", dependencies=[Depends(optional_api_key)])

View File

@@ -46,8 +46,10 @@ def get_auth_dependency():
return return
# Check if authentication is configured # Check if authentication is configured
auth_configured = bool(os.getenv("AUTH_USERNAME") and os.getenv("AUTH_PASSWORD")) auth_configured = bool(
os.getenv("AUTH_USERNAME") and os.getenv("AUTH_PASSWORD")
)
# If authentication is not configured, accept any token including guest tokens # If authentication is not configured, accept any token including guest tokens
if not auth_configured: if not auth_configured:
if token: # If token is provided, still validate it if token: # If token is provided, still validate it
@@ -62,22 +64,22 @@ def get_auth_dependency():
# Ignore validation errors but log them # Ignore validation errors but log them
print(f"Token validation error (ignored): {str(e)}") print(f"Token validation error (ignored): {str(e)}")
return return
# If authentication is configured, validate the token and reject guest tokens # If authentication is configured, validate the token and reject guest tokens
if not token: if not token:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, detail="Token required" status_code=status.HTTP_401_UNAUTHORIZED, detail="Token required"
) )
token_info = auth_handler.validate_token(token) token_info = auth_handler.validate_token(token)
# Reject guest tokens when authentication is configured # Reject guest tokens when authentication is configured
if token_info.get("role") == "guest": if token_info.get("role") == "guest":
raise HTTPException( raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED,
detail="Authentication required. Guest access not allowed when authentication is configured." detail="Authentication required. Guest access not allowed when authentication is configured.",
) )
# At this point, we have a valid non-guest token # At this point, we have a valid non-guest token
return return

View File

@@ -18,7 +18,7 @@ const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
useEffect(() => { useEffect(() => {
let isMounted = true; // Flag to prevent state updates after unmount let isMounted = true; // Flag to prevent state updates after unmount
// This effect will run when the component mounts // This effect will run when the component mounts
// and will check if authentication is required // and will check if authentication is required
const checkAuthStatus = async () => { const checkAuthStatus = async () => {
@@ -28,12 +28,12 @@ const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
if (isMounted) setIsChecking(false); if (isMounted) setIsChecking(false);
return; return;
} }
const status = await getAuthStatus() const status = await getAuthStatus()
// Only proceed if component is still mounted // Only proceed if component is still mounted
if (!isMounted) return; if (!isMounted) return;
if (!status.auth_configured && status.access_token) { if (!status.auth_configured && status.access_token) {
// If auth is not configured, use the guest token // If auth is not configured, use the guest token
useAuthStore.getState().login(status.access_token, true) useAuthStore.getState().login(status.access_token, true)
@@ -53,7 +53,7 @@ const ProtectedRoute = ({ children }: ProtectedRouteProps) => {
// Execute immediately // Execute immediately
checkAuthStatus() checkAuthStatus()
// Cleanup function to prevent state updates after unmount // Cleanup function to prevent state updates after unmount
return () => { return () => {
isMounted = false; isMounted = false;
@@ -80,23 +80,23 @@ const AppRouter = () => {
// Check token validity and auth configuration on app initialization // Check token validity and auth configuration on app initialization
useEffect(() => { useEffect(() => {
let isMounted = true; // Flag to prevent state updates after unmount let isMounted = true; // Flag to prevent state updates after unmount
const checkAuth = async () => { const checkAuth = async () => {
try { try {
const token = localStorage.getItem('LIGHTRAG-API-TOKEN') const token = localStorage.getItem('LIGHTRAG-API-TOKEN')
// If we have a token, we're already authenticated // If we have a token, we're already authenticated
if (token && isAuthenticated) { if (token && isAuthenticated) {
if (isMounted) setInitializing(false); if (isMounted) setInitializing(false);
return; return;
} }
// If no token or not authenticated, check if auth is configured // If no token or not authenticated, check if auth is configured
const status = await getAuthStatus() const status = await getAuthStatus()
// Only proceed if component is still mounted // Only proceed if component is still mounted
if (!isMounted) return; if (!isMounted) return;
if (!status.auth_configured && status.access_token) { if (!status.auth_configured && status.access_token) {
// If auth is not configured, use the guest token // If auth is not configured, use the guest token
useAuthStore.getState().login(status.access_token, true) useAuthStore.getState().login(status.access_token, true)
@@ -122,7 +122,7 @@ const AppRouter = () => {
// Execute immediately // Execute immediately
checkAuth() checkAuth()
// Cleanup function to prevent state updates after unmount // Cleanup function to prevent state updates after unmount
return () => { return () => {
isMounted = false; isMounted = false;

View File

@@ -375,7 +375,7 @@ export const getAuthStatus = async (): Promise<AuthStatusResponse> => {
'Accept': 'application/json' // Explicitly request JSON 'Accept': 'application/json' // Explicitly request JSON
} }
}); });
// Check if response is HTML (which indicates a redirect or wrong endpoint) // Check if response is HTML (which indicates a redirect or wrong endpoint)
const contentType = response.headers['content-type'] || ''; const contentType = response.headers['content-type'] || '';
if (contentType.includes('text/html')) { if (contentType.includes('text/html')) {
@@ -385,13 +385,13 @@ export const getAuthStatus = async (): Promise<AuthStatusResponse> => {
auth_mode: 'enabled' auth_mode: 'enabled'
}; };
} }
// Strict validation of the response data // Strict validation of the response data
if (response.data && if (response.data &&
typeof response.data === 'object' && typeof response.data === 'object' &&
'auth_configured' in response.data && 'auth_configured' in response.data &&
typeof response.data.auth_configured === 'boolean') { typeof response.data.auth_configured === 'boolean') {
// For unconfigured auth, ensure we have an access token // For unconfigured auth, ensure we have an access token
if (!response.data.auth_configured) { if (!response.data.auth_configured) {
if (response.data.access_token && typeof response.data.access_token === 'string') { if (response.data.access_token && typeof response.data.access_token === 'string') {
@@ -404,10 +404,10 @@ export const getAuthStatus = async (): Promise<AuthStatusResponse> => {
return response.data; return response.data;
} }
} }
// If response data is invalid but we got a response, log it // If response data is invalid but we got a response, log it
console.warn('Received invalid auth status response:', response.data); console.warn('Received invalid auth status response:', response.data);
// Default to auth configured if response is invalid // Default to auth configured if response is invalid
return { return {
auth_configured: true, auth_configured: true,

View File

@@ -23,7 +23,7 @@ const LoginPage = () => {
// Check if authentication is configured // Check if authentication is configured
useEffect(() => { useEffect(() => {
let isMounted = true; // Flag to prevent state updates after unmount let isMounted = true; // Flag to prevent state updates after unmount
const checkAuthConfig = async () => { const checkAuthConfig = async () => {
try { try {
// If already authenticated, redirect to home // If already authenticated, redirect to home
@@ -34,10 +34,10 @@ const LoginPage = () => {
// Check auth status // Check auth status
const status = await getAuthStatus() const status = await getAuthStatus()
// Only proceed if component is still mounted // Only proceed if component is still mounted
if (!isMounted) return; if (!isMounted) return;
if (!status.auth_configured && status.access_token) { if (!status.auth_configured && status.access_token) {
// If auth is not configured, use the guest token and redirect // If auth is not configured, use the guest token and redirect
login(status.access_token, true) login(status.access_token, true)
@@ -59,7 +59,7 @@ const LoginPage = () => {
// Execute immediately // Execute immediately
checkAuthConfig() checkAuthConfig()
// Cleanup function to prevent state updates after unmount // Cleanup function to prevent state updates after unmount
return () => { return () => {
isMounted = false; isMounted = false;
@@ -81,18 +81,18 @@ const LoginPage = () => {
try { try {
setLoading(true) setLoading(true)
const response = await loginToServer(username, password) const response = await loginToServer(username, password)
// Check authentication mode // Check authentication mode
const isGuestMode = response.auth_mode === 'disabled' const isGuestMode = response.auth_mode === 'disabled'
login(response.access_token, isGuestMode) login(response.access_token, isGuestMode)
if (isGuestMode) { if (isGuestMode) {
// Show authentication disabled notification // Show authentication disabled notification
toast.info(response.message || t('login.authDisabled', 'Authentication is disabled. Using guest access.')) toast.info(response.message || t('login.authDisabled', 'Authentication is disabled. Using guest access.'))
} else { } else {
toast.success(t('login.successMessage')) toast.success(t('login.successMessage'))
} }
navigate('/') navigate('/')
} catch (error) { } catch (error) {
console.error('Login failed...', error) console.error('Login failed...', error)

View File

@@ -29,8 +29,8 @@
"successMessage": "Login succeeded", "successMessage": "Login succeeded",
"errorEmptyFields": "Please enter your username and password", "errorEmptyFields": "Please enter your username and password",
"errorInvalidCredentials": "Login failed, please check username and password", "errorInvalidCredentials": "Login failed, please check username and password",
"authDisabled": "Authentication is disabled. Using guest access.", "authDisabled": "Authentication is disabled. Using login free mode.",
"guestMode": "Guest Mode" "guestMode": "Login Free"
}, },
"documentPanel": { "documentPanel": {
"clearDocuments": { "clearDocuments": {

View File

@@ -29,8 +29,8 @@
"successMessage": "登录成功", "successMessage": "登录成功",
"errorEmptyFields": "请输入您的用户名和密码", "errorEmptyFields": "请输入您的用户名和密码",
"errorInvalidCredentials": "登录失败,请检查用户名和密码", "errorInvalidCredentials": "登录失败,请检查用户名和密码",
"authDisabled": "认证已禁用,使用访客访问模式。", "authDisabled": "认证已禁用,使用无需登陆模式。",
"guestMode": "访客模式" "guestMode": "无需登陆"
}, },
"documentPanel": { "documentPanel": {
"clearDocuments": { "clearDocuments": {

View File

@@ -73,10 +73,10 @@ const isGuestToken = (token: string): boolean => {
// JWT tokens are in the format: header.payload.signature // JWT tokens are in the format: header.payload.signature
const parts = token.split('.'); const parts = token.split('.');
if (parts.length !== 3) return false; if (parts.length !== 3) return false;
// Decode the payload (second part) // Decode the payload (second part)
const payload = JSON.parse(atob(parts[1])); const payload = JSON.parse(atob(parts[1]));
// Check if the token has a role field with value "guest" // Check if the token has a role field with value "guest"
return payload.role === 'guest'; return payload.role === 'guest';
} catch (e) { } catch (e) {
@@ -91,9 +91,9 @@ const initAuthState = (): { isAuthenticated: boolean; isGuestMode: boolean } =>
if (!token) { if (!token) {
return { isAuthenticated: false, isGuestMode: false }; return { isAuthenticated: false, isGuestMode: false };
} }
return { return {
isAuthenticated: true, isAuthenticated: true,
isGuestMode: isGuestToken(token) isGuestMode: isGuestToken(token)
}; };
}; };
@@ -101,29 +101,29 @@ const initAuthState = (): { isAuthenticated: boolean; isGuestMode: boolean } =>
export const useAuthStore = create<AuthState>(set => { export const useAuthStore = create<AuthState>(set => {
// Get initial state from localStorage // Get initial state from localStorage
const initialState = initAuthState(); const initialState = initAuthState();
return { return {
isAuthenticated: initialState.isAuthenticated, isAuthenticated: initialState.isAuthenticated,
showLoginModal: false, showLoginModal: false,
isGuestMode: initialState.isGuestMode, isGuestMode: initialState.isGuestMode,
login: (token, isGuest = false) => { login: (token, isGuest = false) => {
localStorage.setItem('LIGHTRAG-API-TOKEN', token); localStorage.setItem('LIGHTRAG-API-TOKEN', token);
set({ set({
isAuthenticated: true, isAuthenticated: true,
showLoginModal: false, showLoginModal: false,
isGuestMode: isGuest isGuestMode: isGuest
}); });
}, },
logout: () => { logout: () => {
localStorage.removeItem('LIGHTRAG-API-TOKEN'); localStorage.removeItem('LIGHTRAG-API-TOKEN');
set({ set({
isAuthenticated: false, isAuthenticated: false,
isGuestMode: false isGuestMode: false
}); });
}, },
setShowLoginModal: (show) => set({ showLoginModal: show }) setShowLoginModal: (show) => set({ showLoginModal: show })
}; };
}); });