import asyncio from contextlib import asynccontextmanager import os import threading from dotenv import load_dotenv from fastapi.responses import RedirectResponse from config import SanatanConfig from db import SanatanDatabase from drive_downloader import ZipDownloader from modules.firebase.listener import start_firestore_listener import uvicorn from fastapi import FastAPI from modules.dropbox.audio import cleanup_audio_url_cache # from modules.home.app import home_app from modules.youtube_metadata.app import initialize_youtube_metadata_and_poll, youtube_metadata_app from server import router as mobile_router # from app import gradio_app # your Blocks object import gradio as gr import logging from fastapi import Request from fastapi.middleware.cors import CORSMiddleware from fastapi.staticfiles import StaticFiles from slowapi import Limiter, _rate_limit_exceeded_handler from slowapi.util import get_remote_address from slowapi.errors import RateLimitExceeded from contextlib import asynccontextmanager logging.basicConfig(level=logging.INFO) logger = logging.getLogger() logger.setLevel(logging.INFO) def init(): logger.info("Application Initializing ...") load_dotenv(override=True) try: SanatanDatabase().test_sanity() except Exception as e: logger.warning("Sanity Test Failed - %s", e) logger.info("Downloading database ...") downloader = ZipDownloader( service_account_json=os.getenv("GOOGLE_SERVICE_ACCOUNT_JSON") ) zip_path = downloader.download_zip_from_drive( file_id=os.getenv("CHROMADB_FILE_ID"), output_path=SanatanConfig.dbStorePath, ) downloader.unzip(zip_path, extract_to="./") # add global index # delete taniyan records logger.info("STARTED: Deleting Taniyans ...") sanatanDatabase = SanatanDatabase() sanatanDatabase.delete_taniyans_in_divya_prabandham() logger.info("STARED: Building Global Index for all scriptures ...") sanatanDatabase.build_global_index_for_all_scriptures() logger.info("FINISHED: Building Global Index for all scriptures ...") # Launch the whole thing in a background thread logger.info("STARTED: Initializing youtube metadata poller...") yt_init_thread = threading.Thread(target=initialize_youtube_metadata_and_poll, daemon=True) yt_init_thread.start() logger.info("FINISHED: Initializing youtube metadata poller...") @asynccontextmanager async def fn_lifespan(app: FastAPI): logging.info("🚀 Starting Lifespan ...") ###### Initialize the database ###### init() ###### Initialize the database ###### logging.info("🚀 Starting Firestore listener...") start_firestore_listener() yield logging.info("🛑 Firestore listener shutdown (no explicit cleanup needed).") app = FastAPI(title="Sanatan AI Unified Server",lifespan=fn_lifespan) limiter = Limiter(key_func=get_remote_address) app.state.limiter = limiter app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler) # Allow all origins (for dev) app.add_middleware( CORSMiddleware, allow_origins=["*"], # or ["https://your-flutter-web-app.com"] allow_credentials=True, allow_methods=["*"], # GET, POST, PUT, DELETE, OPTIONS allow_headers=["*"], # e.g. Authorization, Content-Type ) # Mount mobile endpoints app.include_router(mobile_router, prefix="/api") # Convert Gradio Blocks to ASGI app # app = gr.mount_gradio_app(app, gradio_app,"/sanatan_ai_web") # app = gr.mount_gradio_app(app, youtube_metadata_app,"/yt_web") ##### Commenting out to bypass GRADIO ISSUES ON 24-Nov-2025 # app = gr.mount_gradio_app(app, home_app,"/web") from fastapi.responses import FileResponse @app.get("/firebase-messaging-sw.js") async def firebase_sw(): return FileResponse("static/firebase-messaging-sw.js") app.mount("/home", StaticFiles(directory="static", html=True), name="static") # Redirect root URL to /home/ @app.get("/") async def redirect_to_web(): return RedirectResponse(url="/home/") @app.middleware("http") async def log_requests(request: Request, call_next): logging.info(f"Request: {request.method} {request.url}") response = await call_next(request) logging.info(f"Response status: {response.status_code}") return response @asynccontextmanager async def lifespan(app: FastAPI): # Startup code: start cache cleanup asyncio.create_task(cleanup_audio_url_cache()) yield # Shutdown code (optional) can go here if __name__ == "__main__": uvicorn.run("main:app", host="0.0.0.0", port=7860, reload=False, access_log=False, log_level=logging.WARNING)