gateway/modules/routes/routeGeneral.py

198 lines
No EOL
7.4 KiB
Python

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 modules.shared.configuration import APP_CONFIG
from modules.security.auth import (
createAccessToken,
getCurrentActiveUser,
getRootInterface,
ACCESS_TOKEN_EXPIRE_MINUTES
)
import modules.interfaces.gatewayModel as gatewayModel
from modules.interfaces.gatewayInterface import getInterface
router = APIRouter()
# Static folder setup - using absolute path from app root
baseDir = pathlib.Path(__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("/favicon.ico", tags=["General"])
async def favicon():
return FileResponse(str(staticFolder / "favicon.ico"), media_type="image/x-icon")
@router.get("/", tags=["General"])
async def root():
"""API status endpoint"""
return {"status": "online", "message": "Data Platform API is active"}
@router.get("/api/test", tags=["General"])
async def get_test():
return f"Status: OK. Alowed origins: {APP_CONFIG.get('APP_ALLOWED_ORIGINS')}"
@router.options("/{fullPath:path}", tags=["General"])
async def options_route(fullPath: str):
return Response(status_code=200)
@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.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
myInterface = getRootInterface()
try:
# Authenticate user
user = myInterface.authenticateUser(formData.username, formData.password)
# Create token with mandate ID and user ID
accessTokenExpires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
accessToken = createAccessToken(
data={
"sub": user["username"],
"_mandateId": str(user["_mandateId"]), # Ensure string
"_userId": str(user["id"]), # Ensure string
"authenticationAuthority": user.get("authenticationAuthority", "local") # Add auth authority
},
expiresDelta=accessTokenExpires
)
logger.info(f"User {user['username']} successfully logged in with context: _mandateId={user['_mandateId']}, _userId={user['id']}, auth={user.get('authenticationAuthority', 'local')}")
return {"accessToken": accessToken, "tokenType": "bearer"}
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(getCurrentActiveUser)):
return currentUser
@router.post("/api/users/register", response_model=Dict[str, Any], tags=["General"])
async def register_user(userData: Dict[str, Any]):
"""Register a new user."""
try:
logger.debug("Received registration request")
# Create a new gateway interface instance with admin context
myInterface = getRootInterface()
# Check required fields
if not userData or not isinstance(userData, dict):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Invalid user data format"
)
if not userData.get("username") or not userData.get("password"):
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Username and password are required"
)
# Create user data in same mandate as admin user
userData = {
"username": userData["username"],
"password": userData["password"],
"email": userData.get("email"),
"fullName": userData.get("fullName"),
"language": userData.get("language", "en"),
"disabled": False,
"privilege": "user"
}
# Create the user
try:
createdUser = myInterface.createUser(**userData)
except ValueError as e:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=str(e)
)
# Verify the user was created
if not createdUser:
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Failed to create user"
)
return createdUser
except HTTPException:
raise
except Exception as e:
logger.error(f"Error in user registration: {str(e)}")
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Registration failed: {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:
# Create a new gateway interface instance with root context
myInterface = getRootInterface()
# Check if user exists
existingUser = myInterface.getUserByUsername(username)
if not existingUser:
return {"available": True}
# If user exists, check authentication authority
if existingUser.get("authenticationAuthority") == authenticationAuthority:
return {
"available": False,
"message": f"Username already exists with {authenticationAuthority} authentication"
}
else:
return {
"available": True,
"message": f"Username exists but with different authentication authority"
}
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)}"
)