Handle missing WebUI assets gracefully without blocking server startup

- Change build check from error to warning
- Redirect to /docs when WebUI unavailable
- Add webui_available to health endpoint
- Only mount /webui if assets exist
- Return status tuple from build check
This commit is contained in:
yangdx 2025-11-25 02:51:55 +08:00
parent 2832a2ca7e
commit 48b67d3077

View file

@ -159,19 +159,22 @@ def check_frontend_build():
"""Check if frontend is built and optionally check if source is up-to-date """Check if frontend is built and optionally check if source is up-to-date
Returns: Returns:
bool: True if frontend is outdated, False if up-to-date or production environment tuple: (assets_exist: bool, is_outdated: bool)
- assets_exist: True if WebUI build files exist
- is_outdated: True if source is newer than build (only in dev environment)
""" """
webui_dir = Path(__file__).parent / "webui" webui_dir = Path(__file__).parent / "webui"
index_html = webui_dir / "index.html" index_html = webui_dir / "index.html"
# 1. Check if build files exist (required) # 1. Check if build files exist
if not index_html.exists(): if not index_html.exists():
ASCIIColors.red("\n" + "=" * 80) ASCIIColors.yellow("\n" + "=" * 80)
ASCIIColors.red("ERROR: Frontend Not Built") ASCIIColors.yellow("WARNING: Frontend Not Built")
ASCIIColors.red("=" * 80) ASCIIColors.yellow("=" * 80)
ASCIIColors.yellow("The WebUI frontend has not been built yet.") ASCIIColors.yellow("The WebUI frontend has not been built yet.")
ASCIIColors.yellow("The API server will start without the WebUI interface.")
ASCIIColors.yellow( ASCIIColors.yellow(
"Please build the frontend code first using the following commands:\n" "\nTo enable WebUI, build the frontend using these commands:\n"
) )
ASCIIColors.cyan(" cd lightrag_webui") ASCIIColors.cyan(" cd lightrag_webui")
ASCIIColors.cyan(" bun install --frozen-lockfile") ASCIIColors.cyan(" bun install --frozen-lockfile")
@ -181,8 +184,8 @@ def check_frontend_build():
ASCIIColors.cyan( ASCIIColors.cyan(
"Note: Make sure you have Bun installed. Visit https://bun.sh for installation." "Note: Make sure you have Bun installed. Visit https://bun.sh for installation."
) )
ASCIIColors.red("=" * 80 + "\n") ASCIIColors.yellow("=" * 80 + "\n")
sys.exit(1) # Exit immediately return (False, False) # Assets don't exist, not outdated
# 2. Check if this is a development environment (source directory exists) # 2. Check if this is a development environment (source directory exists)
try: try:
@ -195,7 +198,7 @@ def check_frontend_build():
logger.debug( logger.debug(
"Production environment detected, skipping source freshness check" "Production environment detected, skipping source freshness check"
) )
return False return (True, False) # Assets exist, not outdated (prod environment)
# Development environment, perform source code timestamp check # Development environment, perform source code timestamp check
logger.debug("Development environment detected, checking source freshness") logger.debug("Development environment detected, checking source freshness")
@ -270,20 +273,20 @@ def check_frontend_build():
ASCIIColors.cyan(" cd ..") ASCIIColors.cyan(" cd ..")
ASCIIColors.yellow("\nThe server will continue with the current build.") ASCIIColors.yellow("\nThe server will continue with the current build.")
ASCIIColors.yellow("=" * 80 + "\n") ASCIIColors.yellow("=" * 80 + "\n")
return True # Frontend is outdated return (True, True) # Assets exist, outdated
else: else:
logger.info("Frontend build is up-to-date") logger.info("Frontend build is up-to-date")
return False # Frontend is up-to-date return (True, False) # Assets exist, up-to-date
except Exception as e: except Exception as e:
# If check fails, log warning but don't affect startup # If check fails, log warning but don't affect startup
logger.warning(f"Failed to check frontend source freshness: {e}") logger.warning(f"Failed to check frontend source freshness: {e}")
return False # Assume up-to-date on error return (True, False) # Assume assets exist and up-to-date on error
def create_app(args): def create_app(args):
# Check frontend build first and get outdated status # Check frontend build first and get status
is_frontend_outdated = check_frontend_build() webui_assets_exist, is_frontend_outdated = check_frontend_build()
# Create unified API version display with warning symbol if frontend is outdated # Create unified API version display with warning symbol if frontend is outdated
api_version_display = ( api_version_display = (
@ -1067,8 +1070,11 @@ def create_app(args):
@app.get("/") @app.get("/")
async def redirect_to_webui(): async def redirect_to_webui():
"""Redirect root path to /webui""" """Redirect root path based on WebUI availability"""
return RedirectResponse(url="/webui") if webui_assets_exist:
return RedirectResponse(url="/webui")
else:
return RedirectResponse(url="/docs")
@app.get("/auth-status") @app.get("/auth-status")
async def get_auth_status(): async def get_auth_status():
@ -1135,9 +1141,41 @@ def create_app(args):
"webui_description": webui_description, "webui_description": webui_description,
} }
@app.get("/health", dependencies=[Depends(combined_auth)]) @app.get(
"/health",
dependencies=[Depends(combined_auth)],
summary="Get system health and configuration status",
description="Returns comprehensive system status including WebUI availability, configuration, and operational metrics",
response_description="System health status with configuration details",
responses={
200: {
"description": "Successful response with system status",
"content": {
"application/json": {
"example": {
"status": "healthy",
"webui_available": True,
"working_directory": "/path/to/working/dir",
"input_directory": "/path/to/input/dir",
"configuration": {
"llm_binding": "openai",
"llm_model": "gpt-4",
"embedding_binding": "openai",
"embedding_model": "text-embedding-ada-002",
"workspace": "default",
},
"auth_mode": "enabled",
"pipeline_busy": False,
"core_version": "0.0.1",
"api_version": "0.0.1",
}
}
},
}
},
)
async def get_status(request: Request): async def get_status(request: Request):
"""Get current system status""" """Get current system status including WebUI availability"""
try: try:
workspace = get_workspace_from_request(request) workspace = get_workspace_from_request(request)
default_workspace = get_default_workspace() default_workspace = get_default_workspace()
@ -1157,6 +1195,7 @@ def create_app(args):
return { return {
"status": "healthy", "status": "healthy",
"webui_available": webui_assets_exist,
"working_directory": str(args.working_dir), "working_directory": str(args.working_dir),
"input_directory": str(args.input_dir), "input_directory": str(args.input_dir),
"configuration": { "configuration": {
@ -1246,16 +1285,27 @@ def create_app(args):
name="swagger-ui-static", name="swagger-ui-static",
) )
# Webui mount webui/index.html # Conditionally mount WebUI only if assets exist
static_dir = Path(__file__).parent / "webui" if webui_assets_exist:
static_dir.mkdir(exist_ok=True) static_dir = Path(__file__).parent / "webui"
app.mount( static_dir.mkdir(exist_ok=True)
"/webui", app.mount(
SmartStaticFiles( "/webui",
directory=static_dir, html=True, check_dir=True SmartStaticFiles(
), # Use SmartStaticFiles directory=static_dir, html=True, check_dir=True
name="webui", ), # Use SmartStaticFiles
) name="webui",
)
logger.info("WebUI assets mounted at /webui")
else:
logger.info("WebUI assets not available, /webui route not mounted")
# Add redirect for /webui when assets are not available
@app.get("/webui")
@app.get("/webui/")
async def webui_redirect_to_docs():
"""Redirect /webui to /docs when WebUI is not available"""
return RedirectResponse(url="/docs")
return app return app