#main.py from fastapi import FastAPI, Form, Depends, HTTPException, status from fastapi.requests import Request from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse from fastapi.templating import Jinja2Templates from sqlalchemy.orm import Session from auth import verify_token, oauth2_scheme, auth_views, register, UserCreate, authenticate_user, get_user_by_verification_token from database import get_db, get_user_by_email from datetime import timedelta from typing import Optional #import auth #import tts import os my_secret_key = os.environ['my_secret_key'] app = FastAPI() #router = APIRouter() templates = Jinja2Templates(directory="templates") # Include the authentication router with the prefix '/auth' #app.include_router(auth.router, prefix="") # Include the TTS router with the prefix '/tts' #app.include_router(tts.router, prefix="/tts") # Dependency for verifying the user's token def get_current_user(token: str = Depends(verify_token)): if not token: raise HTTPException(status_code=401, detail="Token not valid") return token # Route for the landing page @app.get("/", response_class=HTMLResponse) async def landing(request: Request): return templates.TemplateResponse("landing.html", {"request": request}) # Your other routes and app configuration go here @app.get("/login", response_class=HTMLResponse) async def login(request: Request): return templates.TemplateResponse("login.html", {"request": request}) @app.post("/login") async def login_post( request: Request, email: str = Form(...), password: str = Form(...), db: Session = Depends(get_db) ): if not email or not password: raise HTTPException(status_code=400, detail="Invalid email or password") user = authenticate_user(db, email, password) if user and user.is_verified: # Check if user is verified access_token = auth_views.create_access_token( data={"sub": user.email}, expires_delta=timedelta(minutes=auth_views.ACCESS_TOKEN_EXPIRE_MINUTES) ) # Redirect the user to the protected route with the token in the URL url = app.url_path_for("get_protected") # Ensure you have a name="get_protected" in your app.get("/protected") decorator #return RedirectResponse(url=f"/protected?token={access_token}", status_code=status.HTTP_303_SEE_OTHER) #return RedirectResponse(f"{url}?token={access_token}") response = RedirectResponse(f"{url}?token={access_token}", status_code=status.HTTP_303_SEE_OTHER) response.set_cookie(key="access_token", value=f"Bearer {access_token}", httponly=True) # response.set_cookie(key="access_token", value=access_token, httponly=True) return response elif user and not user.is_verified: # User is not verified raise HTTPException( status_code=400, detail="You must verify your email before accessing this resource." ) else: # If authentication fails, return to the login page with an error message return templates.TemplateResponse( "login.html", {"request": request, "error_message": "Invalid email or password"} ) @app.get("/register", response_class=HTMLResponse) async def register_get(request: Request): return templates.TemplateResponse("register.html", {"request": request}) @app.post("/register", response_class=HTMLResponse) async def register_post( request: Request, username: str = Form(...), email: str = Form(...), password: str = Form(...), confirm_password: str = Form(...), db: Session = Depends(get_db) ): if password != confirm_password: # Return to the registration page with an error return templates.TemplateResponse("register.html", { "request": request, "error_message": "Passwords do not match." }) try: user = UserCreate(username=username, email=email, password=password, confirm_password=confirm_password) register(user, db) # If this function raises an HTTPException, it should be handled except HTTPException as e: # Return to the registration page with the error detail return templates.TemplateResponse("register.html", { "request": request, "error_message": e.detail }) # Redirect to the successful registration page response = RedirectResponse("/registration_successful", status_code=status.HTTP_302_FOUND) return response @app.get("/registration_successful", response_class=HTMLResponse) async def registration_successful(request: Request): # Render the successful registration page return templates.TemplateResponse("registration_successful.html", {"request": request}) @app.get("/verify/{verification_token}", response_class=HTMLResponse) async def verify_email(verification_token: str, db: Session = Depends(get_db)): user = get_user_by_verification_token(db, verification_token) if not user: raise HTTPException(status_code=400, detail="Invalid verification token") if user.is_verified: raise HTTPException(status_code=400, detail="Email already verified") user.is_verified = True user.email_verification_token = None # Clear the verification token db.commit() # Create access token for the user after successful verification access_token = auth_views.create_access_token(data={"sub": user.email}, expires_delta=timedelta(minutes=auth_views.ACCESS_TOKEN_EXPIRE_MINUTES)) # Redirect to the protected route with the token as a query parameter (or as required by your front-end/client) return RedirectResponse(url=f"/protected?token={access_token}") #from jwt import decode, PyJWTError # make sure jwt is imported @app.get("/protected", response_class=HTMLResponse) async def get_protected( request: Request, db: Session = Depends(get_db), token: Optional[str] = None # token is Optional because it may come from the cookie ): # Try to get the token from the query parameter first, then fall back to the cookie token = token or request.cookies.get("access_token") if not token: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Not authenticated") # Here verify_token is used directly in the endpoint # If the token is invalid, verify_token will raise an HTTPException and the following lines will not be executed user_email = verify_token(token) # Assuming that verify_token returns the user's email if the token is valid # Get the user from the database db_user = get_user_by_email(db, user_email) if db_user is None or not db_user.is_verified: raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found or not verified in the database") # Render a template response return templates.TemplateResponse("protected.html", {"request": request, "user": db_user.username}) #@app.get("/protected", response_class=HTMLResponse) #async def get_protected(request: Request, token: Optional[str] = None, db: Session = Depends(get_db)): # Try to get the token from the query parameter first, then fall back to the cookie # token = token or request.cookies.get("access_token") # if not token: # raise HTTPException(status_code=401, detail="Not authenticated") # try: # payload = decode(token, auth_views.SECRET_KEY, algorithms=[auth_views.ALGORITHM]) # user_email = payload.get("sub") # if user_email is None: # raise HTTPException(status_code=401, detail="Not authenticated") # except PyJWTError: # raise HTTPException(status_code=401, detail="Could not validate credentials") # db_user = get_user_by_email(db, user_email) # if db_user is None or not db_user.is_verified: # raise HTTPException(status_code=401, detail="User not found or not verified in the database") # return templates.TemplateResponse("protected.html", {"request": request, "user": db_user.username}) #async def get_protected( # request: Request, # token: str = Query(None), # Accept token from query parameters # db: Session = Depends(get_db) #): # Now pass both the request and token to the protected_route function # return await protected_route(request, token, db)