Merge pull request #1115 from danielaskdd/main

Hot fix: Server can not startup with legacy data (json_doc_status_impl.py)
This commit is contained in:
Daniel.y
2025-03-18 20:59:42 +08:00
committed by GitHub
8 changed files with 64 additions and 44 deletions

View File

@@ -42,45 +42,38 @@ def get_auth_dependency():
request: Request, request: Request,
token: str = Depends(OAuth2PasswordBearer(tokenUrl="login", auto_error=False)), token: str = Depends(OAuth2PasswordBearer(tokenUrl="login", auto_error=False)),
): ):
if request.url.path in whitelist:
return
# Check if authentication is configured # Check if authentication is configured
auth_configured = bool( auth_configured = bool(
os.getenv("AUTH_USERNAME") and os.getenv("AUTH_PASSWORD") 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, skip all validation
if not auth_configured: if not auth_configured:
if token: # If token is provided, still validate it
try:
# Validate token but don't raise exception
token_info = auth_handler.validate_token(token)
# Check if it's a guest token
if token_info.get("role") != "guest":
# Non-guest tokens are not valid when auth is not configured
pass
except Exception as e:
# Ignore validation errors but log them
print(f"Token validation error (ignored): {str(e)}")
return return
# If authentication is configured, validate the token and reject guest tokens # For configured auth, allow whitelist paths without token
if request.url.path in whitelist:
return
# Require token for all other paths when auth is configured
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) try:
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(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Authentication required. Guest access not allowed when authentication is configured.",
)
except Exception:
raise HTTPException( raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED, status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token"
detail="Authentication required. Guest access not allowed when authentication is configured.",
) )
# At this point, we have a valid non-guest token
return return
return dependency return dependency

View File

@@ -87,6 +87,9 @@ class JsonDocStatusStorage(DocStatusStorage):
# If content is missing, use content_summary as content # If content is missing, use content_summary as content
if "content" not in data and "content_summary" in data: if "content" not in data and "content_summary" in data:
data["content"] = data["content_summary"] data["content"] = data["content_summary"]
# If file_path is not in data, use document id as file path
if "file_path" not in data:
data["file_path"] = "no-file-path"
result[k] = DocProcessingStatus(**data) result[k] = DocProcessingStatus(**data)
except KeyError as e: except KeyError as e:
logger.error(f"Missing required field for document {k}: {e}") logger.error(f"Missing required field for document {k}: {e}")

View File

@@ -246,7 +246,8 @@ async def _merge_nodes_then_upsert(
file_path = GRAPH_FIELD_SEP.join( file_path = GRAPH_FIELD_SEP.join(
set([dp["metadata"]["file_path"] for dp in nodes_data] + already_file_paths) set([dp["metadata"]["file_path"] for dp in nodes_data] + already_file_paths)
) )
print(f"file_path: {file_path}")
logger.debug(f"file_path: {file_path}")
description = await _handle_entity_relation_summary( description = await _handle_entity_relation_summary(
entity_name, description, global_config entity_name, description, global_config
) )

View File

@@ -8,6 +8,8 @@ import { healthCheckInterval } from '@/lib/constants'
import { useBackendState, useAuthStore } from '@/stores/state' import { useBackendState, useAuthStore } from '@/stores/state'
import { useSettingsStore } from '@/stores/settings' import { useSettingsStore } from '@/stores/settings'
import { useEffect } from 'react' import { useEffect } from 'react'
import { useNavigate } from 'react-router-dom'
import { navigationService } from '@/services/navigation'
import SiteHeader from '@/features/SiteHeader' import SiteHeader from '@/features/SiteHeader'
import { InvalidApiKeyError, RequireApiKeError } from '@/api/lightrag' import { InvalidApiKeyError, RequireApiKeError } from '@/api/lightrag'
@@ -19,7 +21,13 @@ import ApiSite from '@/features/ApiSite'
import { Tabs, TabsContent } from '@/components/ui/Tabs' import { Tabs, TabsContent } from '@/components/ui/Tabs'
function App() { function App() {
const navigate = useNavigate();
const message = useBackendState.use.message() const message = useBackendState.use.message()
// Initialize navigation service
useEffect(() => {
navigationService.setNavigate(navigate);
}, [navigate]);
const enableHealthCheck = useSettingsStore.use.enableHealthCheck() const enableHealthCheck = useSettingsStore.use.enableHealthCheck()
const currentTab = useSettingsStore.use.currentTab() const currentTab = useSettingsStore.use.currentTab()
const [apiKeyInvalid, setApiKeyInvalid] = useState(false) const [apiKeyInvalid, setApiKeyInvalid] = useState(false)

View File

@@ -1,8 +1,9 @@
import axios, { AxiosError } from 'axios' import axios, { AxiosError } from 'axios'
import { backendBaseUrl, webuiPrefix } from '@/lib/constants' import { backendBaseUrl } from '@/lib/constants'
import { errorMessage } from '@/lib/utils' import { errorMessage } from '@/lib/utils'
import { useSettingsStore } from '@/stores/settings' import { useSettingsStore } from '@/stores/settings'
import { useAuthStore } from '@/stores/state' import { useAuthStore } from '@/stores/state'
import { navigationService } from '@/services/navigation'
// Types // Types
export type LightragNodeType = { export type LightragNodeType = {
@@ -157,21 +158,13 @@ axiosInstance.interceptors.request.use((config) => {
const apiKey = useSettingsStore.getState().apiKey const apiKey = useSettingsStore.getState().apiKey
const token = localStorage.getItem('LIGHTRAG-API-TOKEN'); const token = localStorage.getItem('LIGHTRAG-API-TOKEN');
// Check authentication status for paths that require authentication // Always include token if it exists, regardless of path
const authRequiredPaths = ['/documents', '/graphs', '/query', '/health']; // Add all paths that require authentication
const isAuthRequired = authRequiredPaths.some(path => config.url?.includes(path));
if (isAuthRequired && !token && config.url !== '/login') {
// Cancel the request and return a rejected Promise
return Promise.reject(new Error('Authentication required'));
}
if (apiKey) {
config.headers['X-API-Key'] = apiKey
}
if (token) { if (token) {
config.headers['Authorization'] = `Bearer ${token}` config.headers['Authorization'] = `Bearer ${token}`
} }
if (apiKey) {
config.headers['X-API-Key'] = apiKey
}
return config return config
}) })
@@ -185,11 +178,11 @@ axiosInstance.interceptors.response.use(
sessionStorage.clear(); sessionStorage.clear();
useAuthStore.getState().logout(); useAuthStore.getState().logout();
if (window.location.pathname !== `${webuiPrefix}/#/login`) { // Use navigation service to handle redirection
window.location.href = `${webuiPrefix}/#/login`; navigationService.navigateToLogin();
}
return Promise.reject(error); // Return a never-resolving promise to prevent further execution
return new Promise(() => {});
} }
throw new Error( throw new Error(
`${error.response.status} ${error.response.statusText}\n${JSON.stringify( `${error.response.status} ${error.response.statusText}\n${JSON.stringify(

View File

@@ -45,7 +45,6 @@ export default function DocumentManager() {
} else { } else {
setDocs(null) setDocs(null)
} }
// console.log(docs)
} else { } else {
setDocs(null) setDocs(null)
} }

View File

@@ -141,7 +141,13 @@ const fetchGraph = async (label: string, maxDepth: number, minDegree: number) =>
// Create a new graph instance with the raw graph data // Create a new graph instance with the raw graph data
const createSigmaGraph = (rawGraph: RawGraph | null) => { const createSigmaGraph = (rawGraph: RawGraph | null) => {
// Always create a new graph instance // Skip graph creation if no data or empty nodes
if (!rawGraph || !rawGraph.nodes.length) {
console.log('No graph data available, skipping sigma graph creation');
return null;
}
// Create new graph instance
const graph = new DirectedGraph() const graph = new DirectedGraph()
// Add nodes from raw graph data // Add nodes from raw graph data
@@ -242,7 +248,7 @@ const useLightrangeGraph = () => {
if (!isFetching && !fetchInProgressRef.current && if (!isFetching && !fetchInProgressRef.current &&
(paramsChanged || !useGraphStore.getState().graphDataFetchAttempted)) { (paramsChanged || !useGraphStore.getState().graphDataFetchAttempted)) {
// Only fetch data if the Graph tab is visible // Only fetch data if the Graph tab is visible and we haven't attempted a fetch yet
if (!isGraphTabVisible) { if (!isGraphTabVisible) {
console.log('Graph tab not visible, skipping data fetch'); console.log('Graph tab not visible, skipping data fetch');
return; return;

View File

@@ -0,0 +1,17 @@
import { NavigateFunction } from 'react-router-dom';
class NavigationService {
private navigate: NavigateFunction | null = null;
setNavigate(navigate: NavigateFunction) {
this.navigate = navigate;
}
navigateToLogin() {
if (this.navigate) {
this.navigate('/login');
}
}
}
export const navigationService = new NavigationService();