* Fix: Load extensions in server.py for multi-worker deployments When running with multiple workers (--workers 2), uvicorn uses `hindsight_api.server:app` import string instead of passing an app object. The server.py module was not loading tenant/operation validator extensions, causing authentication bypass in production. This fix: - Adds extension loading to server.py matching main.py behavior - Sets extension context on tenant extension for schema provisioning - Adds comprehensive unit tests for server.py extension loading The tests specifically verify: - TENANT extension is loaded when HINDSIGHT_API_TENANT_EXTENSION is set - OPERATION_VALIDATOR is loaded when configured - Extensions are passed to MemoryEngine constructor - Extension context is set on tenant extension - Server works correctly without extensions configured * Add unit tests for main.py extension loading (single-worker path)
77 lines
2.4 KiB
Python
77 lines
2.4 KiB
Python
"""
|
|
FastAPI server for Hindsight API.
|
|
|
|
This module provides the ASGI app for uvicorn import string usage:
|
|
uvicorn hindsight_api.server:app
|
|
|
|
For CLI usage, use the hindsight-api command instead.
|
|
"""
|
|
|
|
import logging
|
|
import os
|
|
import warnings
|
|
|
|
# Filter deprecation warnings from third-party libraries
|
|
warnings.filterwarnings("ignore", message="websockets.legacy is deprecated")
|
|
warnings.filterwarnings("ignore", message="websockets.server.WebSocketServerProtocol is deprecated")
|
|
|
|
from hindsight_api import MemoryEngine
|
|
from hindsight_api.api import create_app
|
|
from hindsight_api.config import get_config
|
|
from hindsight_api.extensions import (
|
|
DefaultExtensionContext,
|
|
OperationValidatorExtension,
|
|
TenantExtension,
|
|
load_extension,
|
|
)
|
|
|
|
# Disable tokenizers parallelism to avoid warnings
|
|
os.environ["TOKENIZERS_PARALLELISM"] = "false"
|
|
|
|
# Load configuration and configure logging
|
|
config = get_config()
|
|
config.configure_logging()
|
|
|
|
# Load operation validator extension if configured
|
|
operation_validator = load_extension("OPERATION_VALIDATOR", OperationValidatorExtension)
|
|
if operation_validator:
|
|
logging.info(f"Loaded operation validator: {operation_validator.__class__.__name__}")
|
|
|
|
# Load tenant extension if configured
|
|
tenant_extension = load_extension("TENANT", TenantExtension)
|
|
if tenant_extension:
|
|
logging.info(f"Loaded tenant extension: {tenant_extension.__class__.__name__}")
|
|
|
|
# Create app at module level (required for uvicorn import string)
|
|
# MemoryEngine reads configuration from environment variables automatically
|
|
# Note: run_migrations=True by default, but migrations are idempotent so safe with workers
|
|
_memory = MemoryEngine(
|
|
operation_validator=operation_validator,
|
|
tenant_extension=tenant_extension,
|
|
run_migrations=config.run_migrations_on_startup,
|
|
)
|
|
|
|
# Set extension context on tenant extension (needed for schema provisioning)
|
|
if tenant_extension:
|
|
extension_context = DefaultExtensionContext(
|
|
database_url=config.database_url,
|
|
memory_engine=_memory,
|
|
)
|
|
tenant_extension.set_context(extension_context)
|
|
logging.info("Extension context set on tenant extension")
|
|
|
|
# Create unified app with both HTTP and optionally MCP
|
|
app = create_app(
|
|
memory=_memory,
|
|
http_api_enabled=True,
|
|
mcp_api_enabled=config.mcp_enabled,
|
|
mcp_mount_path="/mcp",
|
|
initialize_memory=True,
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
# When run directly, delegate to the CLI
|
|
from hindsight_api.main import main
|
|
|
|
main()
|