""" API routes for SQLite fallback database management. Provides endpoints for monitoring, testing, and managing the SQLite fallback database system. """ import logging from typing import Dict, Any from fastapi import APIRouter, HTTPException, status, Depends from sqlalchemy.ext.asyncio import AsyncSession from core.database import get_db from core.fallback_database import ( check_database_health, get_current_database_type, switch_to_primary, switch_to_fallback ) from core.sqlite_fallback_manager import SQLiteFallbackManager, test_fallback_mechanism from schemas.response_schema import SuccessResponse, ErrorResponse from api.dependencies.auth import get_current_active_user from db_models.user import User logger = logging.getLogger(__name__) router = APIRouter(prefix="/sqlite-fallback", tags=["sqlite-fallback"]) @router.get("/status", response_model=SuccessResponse) async def get_fallback_status( current_user: User = Depends(get_current_active_user), db: AsyncSession = Depends(get_db) ): """ Get SQLite fallback database status. Args: current_user: Current authenticated user db: Database session Returns: SuccessResponse: Fallback database status Raises: HTTPException: If status check fails """ try: # Only allow superusers to access fallback status if not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Access denied. Superuser access required." ) manager = SQLiteFallbackManager() status_info = await manager.get_fallback_status() # Add current database type information is_healthy, active_db_type = await check_database_health() status_info.update({ "current_active_database": active_db_type, "overall_healthy": is_healthy }) return SuccessResponse( message="SQLite fallback status retrieved successfully", data=status_info ) except HTTPException: raise except Exception as e: logger.error(f"Failed to get fallback status: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to retrieve fallback status" ) @router.post("/test", response_model=SuccessResponse) async def test_fallback_system( current_user: User = Depends(get_current_active_user), db: AsyncSession = Depends(get_db) ): """ Test SQLite fallback mechanism. Args: current_user: Current authenticated user db: Database session Returns: SuccessResponse: Test results Raises: HTTPException: If test fails """ try: # Only allow superusers to test fallback system if not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Access denied. Superuser access required." ) test_results = await test_fallback_mechanism() return SuccessResponse( message="SQLite fallback test completed", data=test_results ) except HTTPException: raise except Exception as e: logger.error(f"Failed to test fallback system: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to test fallback system" ) @router.post("/switch-to-primary", response_model=SuccessResponse) async def force_switch_to_primary( current_user: User = Depends(get_current_active_user), db: AsyncSession = Depends(get_db) ): """ Force switch back to primary PostgreSQL database. Args: current_user: Current authenticated user db: Database session Returns: SuccessResponse: Switch operation result Raises: HTTPException: If switch fails """ try: # Only allow superusers to switch databases if not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Access denied. Superuser access required." ) success = await switch_to_primary() if success: return SuccessResponse( message="Successfully switched to primary PostgreSQL database", data={"current_database": "primary"} ) else: return SuccessResponse( message="Failed to switch to primary database - database not available", data={"current_database": get_current_database_type()} ) except HTTPException: raise except Exception as e: logger.error(f"Failed to switch to primary database: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to switch to primary database" ) @router.post("/switch-to-fallback", response_model=SuccessResponse) async def force_switch_to_fallback( current_user: User = Depends(get_current_active_user), db: AsyncSession = Depends(get_db) ): """ Force switch to SQLite fallback database. Args: current_user: Current authenticated user db: Database session Returns: SuccessResponse: Switch operation result Raises: HTTPException: If switch fails """ try: # Only allow superusers to switch databases if not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Access denied. Superuser access required." ) success = await switch_to_fallback() if success: return SuccessResponse( message="Successfully switched to SQLite fallback database", data={"current_database": "fallback"} ) else: return SuccessResponse( message="Failed to switch to fallback database - fallback not available", data={"current_database": get_current_database_type()} ) except HTTPException: raise except Exception as e: logger.error(f"Failed to switch to fallback database: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to switch to fallback database" ) @router.post("/migrate-from-primary", response_model=SuccessResponse) async def migrate_from_primary_database( current_user: User = Depends(get_current_active_user), db: AsyncSession = Depends(get_db) ): """ Migrate data from primary PostgreSQL to SQLite fallback. Args: current_user: Current authenticated user db: Database session Returns: SuccessResponse: Migration result Raises: HTTPException: If migration fails """ try: # Only allow superusers to perform migration if not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Access denied. Superuser access required." ) manager = SQLiteFallbackManager() success = await manager.migrate_from_postgresql() if success: return SuccessResponse( message="Data migration from PostgreSQL to SQLite completed successfully", data={"migration_status": "completed"} ) else: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Data migration failed" ) except HTTPException: raise except Exception as e: logger.error(f"Failed to migrate from primary database: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to migrate from primary database" ) @router.post("/sync-to-primary", response_model=SuccessResponse) async def sync_to_primary_database( current_user: User = Depends(get_current_active_user), db: AsyncSession = Depends(get_db) ): """ Sync data from SQLite fallback back to primary PostgreSQL. Args: current_user: Current authenticated user db: Database session Returns: SuccessResponse: Sync result Raises: HTTPException: If sync fails """ try: # Only allow superusers to perform sync if not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Access denied. Superuser access required." ) manager = SQLiteFallbackManager() success = await manager.sync_to_postgresql() if success: return SuccessResponse( message="Data sync from SQLite to PostgreSQL completed successfully", data={"sync_status": "completed"} ) else: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Data sync failed" ) except HTTPException: raise except Exception as e: logger.error(f"Failed to sync to primary database: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to sync to primary database" ) @router.post("/backup", response_model=SuccessResponse) async def backup_fallback_database( current_user: User = Depends(get_current_active_user), db: AsyncSession = Depends(get_db) ): """ Create backup of SQLite fallback database. Args: current_user: Current authenticated user db: Database session Returns: SuccessResponse: Backup result Raises: HTTPException: If backup fails """ try: # Only allow superusers to create backups if not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Access denied. Superuser access required." ) manager = SQLiteFallbackManager() success = await manager.backup_fallback_database() if success: return SuccessResponse( message="SQLite fallback database backup created successfully", data={"backup_status": "completed"} ) else: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Database backup failed" ) except HTTPException: raise except Exception as e: logger.error(f"Failed to backup fallback database: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to backup fallback database" ) @router.delete("/cleanup", response_model=SuccessResponse) async def cleanup_fallback_database( current_user: User = Depends(get_current_active_user), db: AsyncSession = Depends(get_db) ): """ Clean up (remove) SQLite fallback database file. Args: current_user: Current authenticated user db: Database session Returns: SuccessResponse: Cleanup result Raises: HTTPException: If cleanup fails """ try: # Only allow superusers to cleanup database if not current_user.is_superuser: raise HTTPException( status_code=status.HTTP_403_FORBIDDEN, detail="Access denied. Superuser access required." ) manager = SQLiteFallbackManager() success = await manager.cleanup_fallback_database() if success: return SuccessResponse( message="SQLite fallback database cleaned up successfully", data={"cleanup_status": "completed"} ) else: raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Database cleanup failed" ) except HTTPException: raise except Exception as e: logger.error(f"Failed to cleanup fallback database: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Failed to cleanup fallback database" )