from fastapi import APIRouter, HTTPException, Depends, Body, status, Response from fastapi.responses import FileResponse from fastapi.security import OAuth2PasswordRequestForm from fastapi.staticfiles import StaticFiles from typing import Dict, Any from datetime import timedelta import pathlib import os import logging from pathlib import Path as FilePath from modules.shared.configuration import APP_CONFIG import modules.security.auth as auth import modules.interfaces.gatewayModel as gatewayModel import modules.interfaces.gatewayInterface as gatewayInterface router = APIRouter( prefix="", tags=["General"], responses={404: {"description": "Not found"}} ) # Static folder setup - using absolute path from app root baseDir = FilePath(__file__).parent.parent.parent # Go up to gateway root staticFolder = baseDir / "static" os.makedirs(staticFolder, exist_ok=True) # Mount static files router.mount("/static", StaticFiles(directory=str(staticFolder), html=True), name="static") logger = logging.getLogger(__name__) @router.get("/", tags=["General"]) async def root(): """API status endpoint""" return { "status": "online", "message": "Data Platform API is active", "allowedOrigins": f"Allowed origins are {APP_CONFIG.get('APP_ALLOWED_ORIGINS')}" } @router.get("/api/environment", tags=["General"]) async def get_environment(): """Get environment configuration for frontend""" return { "apiBaseUrl": APP_CONFIG.get("APP_API_URL", ""), "environment": APP_CONFIG.get("APP_ENV", "development"), "instanceLabel": APP_CONFIG.get("APP_ENV_LABEL", "Development"), # Add other environment variables the frontend might need } @router.options("/{fullPath:path}", tags=["General"]) async def options_route(fullPath: str): return Response(status_code=200) @router.post("/api/token", response_model=gatewayModel.Token, tags=["General"]) async def login_for_access_token(formData: OAuth2PasswordRequestForm = Depends()): # Create a new gateway interface instance with admin context interfaceRoot = auth.getRootInterface() try: # Authenticate user user = interfaceRoot.authenticateUser(formData.username, formData.password) # Authenticate user and get token token = interfaceRoot.authenticateAndGetToken(formData.username, formData.password) return token except ValueError as e: # Handle authentication errors error_msg = str(e) logger.warning(f"Authentication failed for user {formData.username}: {error_msg}") raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail=error_msg, headers={"WWW-Authenticate": "Bearer"}, ) except Exception as e: # Handle other errors error_msg = f"Login failed: {str(e)}" logger.error(f"Unexpected error during login for user {formData.username}: {error_msg}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=error_msg ) @router.get("/api/user/me", response_model=Dict[str, Any], tags=["General"]) async def read_user_me(currentUser: Dict[str, Any] = Depends(auth.getCurrentActiveUser)): return currentUser @router.post("/api/user/register", response_model=gatewayModel.User, tags=["General"]) async def register_user(userData: gatewayModel.User): """Register a new user.""" try: interfaceRoot = auth.getRootInterface() return interfaceRoot.registerUser(userData.model_dump()) except ValueError as e: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail=str(e) ) except Exception as e: logger.error(f"Error registering user: {str(e)}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to register user: {str(e)}" ) @router.get("/api/user/available", response_model=Dict[str, Any], tags=["General"]) async def check_username_availability( username: str, authenticationAuthority: str = "local" ): """Check if a username is available for registration""" try: interfaceRoot = auth.getRootInterface() return interfaceRoot.checkUsernameAvailability(username, authenticationAuthority) except Exception as e: logger.error(f"Error checking username availability: {str(e)}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Failed to check username availability: {str(e)}" ) @router.get("/favicon.ico", tags=["General"]) async def favicon(): return FileResponse(str(staticFolder / "favicon.ico"), media_type="image/x-icon")