from fastapi import FastAPI, HTTPException, Request, Response, Depends, status from fastapi.middleware.cors import CORSMiddleware from fastapi.security import OAuth2PasswordRequestForm import uvicorn from datetime import datetime, timedelta import httpx import os from typing import Any from auth import ( Token, UserLogin, UserInDB, verify_password, get_password_hash, create_access_token, get_current_user, ACCESS_TOKEN_EXPIRE_MINUTES ) app = FastAPI( title="Console API Gateway", description="Central orchestrator for microservices", version="0.1.0" ) # Service URLs from environment USERS_SERVICE_URL = os.getenv("USERS_SERVICE_URL", "http://users-backend:8000") # CORS middleware app.add_middleware( CORSMiddleware, allow_origins=["*"], allow_credentials=True, allow_methods=["*"], allow_headers=["*"], ) @app.get("/") async def root(): return { "message": "Console API Gateway", "status": "running", "timestamp": datetime.now().isoformat() } @app.get("/health") async def health_check(): return { "status": "healthy", "service": "console", "timestamp": datetime.now().isoformat() } # Authentication endpoints @app.post("/api/auth/login", response_model=Token) async def login(form_data: OAuth2PasswordRequestForm = Depends()): """Login endpoint for authentication""" # For demo purposes - in production, check against database # This is temporary until we integrate with Users service demo_users = { "admin": { "username": "admin", "hashed_password": get_password_hash("admin123"), "email": "admin@site11.com", "full_name": "Administrator", "is_active": True }, "user": { "username": "user", "hashed_password": get_password_hash("user123"), "email": "user@site11.com", "full_name": "Test User", "is_active": True } } user = demo_users.get(form_data.username) if not user or not verify_password(form_data.password, user["hashed_password"]): raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect username or password", headers={"WWW-Authenticate": "Bearer"}, ) access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) access_token = create_access_token( data={"sub": user["username"]}, expires_delta=access_token_expires ) return {"access_token": access_token, "token_type": "bearer"} @app.get("/api/auth/me") async def get_me(current_user = Depends(get_current_user)): """Get current user information""" return { "username": current_user.username, "email": f"{current_user.username}@site11.com", "is_active": True } @app.post("/api/auth/logout") async def logout(current_user = Depends(get_current_user)): """Logout endpoint""" # In a real application, you might want to blacklist the token return {"message": "Successfully logged out"} @app.get("/api/status") async def system_status(): services_status = {} # Check Users service try: async with httpx.AsyncClient() as client: response = await client.get(f"{USERS_SERVICE_URL}/health", timeout=2.0) services_status["users"] = "online" if response.status_code == 200 else "error" except: services_status["users"] = "offline" # Other services (not yet implemented) services_status["oauth"] = "pending" services_status["images"] = "pending" services_status["applications"] = "pending" services_status["data"] = "pending" services_status["statistics"] = "pending" return { "console": "online", "services": services_status, "timestamp": datetime.now().isoformat() } # Protected endpoint example @app.get("/api/protected") async def protected_route(current_user = Depends(get_current_user)): """Example of a protected route""" return { "message": "This is a protected route", "user": current_user.username } # API Gateway - Route to Users service @app.api_route("/api/users/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "PATCH"]) async def proxy_to_users(path: str, request: Request, current_user = Depends(get_current_user)): """Proxy requests to Users service (protected)""" try: async with httpx.AsyncClient() as client: # Build the target URL url = f"{USERS_SERVICE_URL}/{path}" # Get request body if exists body = None if request.method in ["POST", "PUT", "PATCH"]: body = await request.body() # Forward the request response = await client.request( method=request.method, url=url, headers={ key: value for key, value in request.headers.items() if key.lower() not in ["host", "content-length"] }, content=body, params=request.query_params ) # Return the response return Response( content=response.content, status_code=response.status_code, headers=dict(response.headers) ) except httpx.ConnectError: raise HTTPException(status_code=503, detail="Users service unavailable") except Exception as e: raise HTTPException(status_code=500, detail=str(e)) if __name__ == "__main__": uvicorn.run( "main:app", host="0.0.0.0", port=8000, reload=True )