""" Main FastAPI application for the search service. Usage: uvicorn api.app:app --host 0.0.0.0 --port 6002 --reload """ import os import sys from typing import Optional from fastapi import FastAPI, Request from fastapi.responses import JSONResponse from fastapi.middleware.cors import CORSMiddleware import argparse # Add parent directory to path sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from config import ConfigLoader, CustomerConfig from utils import ESClient from search import Searcher from query import QueryParser # Global instances _config: Optional[CustomerConfig] = None _es_client: Optional[ESClient] = None _searcher: Optional[Searcher] = None _query_parser: Optional[QueryParser] = None def init_service(customer_id: str = "customer1", es_host: str = "http://localhost:9200"): """ Initialize search service with configuration. Args: customer_id: Customer configuration ID es_host: Elasticsearch host URL """ global _config, _es_client, _searcher, _query_parser print(f"Initializing search service for customer: {customer_id}") # Load configuration config_loader = ConfigLoader("config/schema") _config = config_loader.load_customer_config(customer_id) # Validate configuration errors = config_loader.validate_config(_config) if errors: raise ValueError(f"Configuration validation failed: {errors}") print(f"Configuration loaded: {_config.customer_name}") # Get ES credentials from environment variables or .env file es_username = os.getenv('ES_USERNAME') es_password = os.getenv('ES_PASSWORD') # Try to load from config if not in env if not es_username or not es_password: try: from config.env_config import get_es_config es_config = get_es_config() es_username = es_username or es_config.get('username') es_password = es_password or es_config.get('password') except Exception: pass # Initialize ES client with authentication if credentials are available if es_username and es_password: print(f"Connecting to Elasticsearch with authentication: {es_username}") _es_client = ESClient(hosts=[es_host], username=es_username, password=es_password) else: print(f"Connecting to Elasticsearch without authentication") _es_client = ESClient(hosts=[es_host]) if not _es_client.ping(): raise ConnectionError(f"Failed to connect to Elasticsearch at {es_host}") print(f"Connected to Elasticsearch: {es_host}") # Initialize query parser _query_parser = QueryParser(_config) print("Query parser initialized") # Initialize searcher _searcher = Searcher(_config, _es_client, _query_parser) print("Searcher initialized") print("Search service ready!") def get_config() -> CustomerConfig: """Get customer configuration.""" if _config is None: raise RuntimeError("Service not initialized") return _config def get_es_client() -> ESClient: """Get Elasticsearch client.""" if _es_client is None: raise RuntimeError("Service not initialized") return _es_client def get_searcher() -> Searcher: """Get searcher instance.""" if _searcher is None: raise RuntimeError("Service not initialized") return _searcher def get_query_parser() -> QueryParser: """Get query parser instance.""" if _query_parser is None: raise RuntimeError("Service not initialized") return _query_parser # Create FastAPI app app = FastAPI( title="E-Commerce Search API", description="Configurable search engine for cross-border e-commerce", version="1.0.0" ) # Add CORS middleware app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.on_event("startup") async def startup_event(): """Initialize service on startup.""" customer_id = os.getenv("CUSTOMER_ID", "customer1") es_host = os.getenv("ES_HOST", "http://localhost:9200") try: init_service(customer_id=customer_id, es_host=es_host) except Exception as e: print(f"Failed to initialize service: {e}") print("Service will start but may not function correctly") @app.exception_handler(Exception) async def global_exception_handler(request: Request, exc: Exception): """Global exception handler.""" return JSONResponse( status_code=500, content={ "error": "Internal server error", "detail": str(exc) } ) @app.get("/") async def root(): """Root endpoint.""" return { "service": "E-Commerce Search API", "version": "1.0.0", "status": "running" } # Include routers from .routes import search, admin app.include_router(search.router) app.include_router(admin.router) if __name__ == "__main__": import uvicorn parser = argparse.ArgumentParser(description='Start search API service') parser.add_argument('--host', default='0.0.0.0', help='Host to bind to') parser.add_argument('--port', type=int, default=6002, help='Port to bind to') parser.add_argument('--customer', default='customer1', help='Customer ID') parser.add_argument('--es-host', default='http://localhost:9200', help='Elasticsearch host') parser.add_argument('--reload', action='store_true', help='Enable auto-reload') args = parser.parse_args() # Set environment variables os.environ['CUSTOMER_ID'] = args.customer os.environ['ES_HOST'] = args.es_host # Run server uvicorn.run( "api.app:app", host=args.host, port=args.port, reload=args.reload )