from fastapi import APIRouter, HTTPException, status, Depends, Query from fastapi.responses import JSONResponse from .Schema import ( RegisterUserRequest, LoginUserRequest, AccessTokenResponse, ForgotPasswordRequest, VerifyResetTokenRequest, ResetPasswordRequest, BaseResponse, UserResponse, ) from .Model import User from jose import jwt from typing import List from datetime import datetime, timedelta from App.Subscriptions.Model import Subscription from App.Android.Android import AndroidClient from App.Android.Schema import RegisterUserRequest as AndroidRegister from App.Templates.Templates import MessageTemplate from .dependencies import get_current_active_user, get_admin_user from App.Android.Schema import APIResponse # JWT Configurations SECRET_KEY = "your_secret_key_here" ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 300 user_router = APIRouter(tags=["User"]) client = AndroidClient() templates = MessageTemplate() def create_access_token(data: dict, expires_delta: timedelta = None): to_encode = data.copy() expire = datetime.utcnow() + ( expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) ) to_encode.update({"exp": expire}) return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) # The user registers # It sends userdetails to the router # It sends the user a message to welcome the user @user_router.post( "/user/register", response_model=BaseResponse, status_code=status.HTTP_201_CREATED ) async def register_user(request: RegisterUserRequest): existing_user = await User.filter(phoneNumber=request.phoneNumber).first() if existing_user: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="User already exists." ) new_user = await User.create_user(request.dict()) return BaseResponse( code=200, message="User created successfully", payload={"user_id": new_user.id} ) @user_router.post( "/user/login", response_model=AccessTokenResponse, status_code=status.HTTP_200_OK ) async def login_user(request: LoginUserRequest): # Find user by phone number db_user: User = await User.filter(phoneNumber=request.phoneNumber).first() # Raise an error if the user does not exist if db_user is None: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found." ) valid_password = await db_user.verify_password(request.password) # Check if user exists and password is correct if db_user and valid_password: # Fetch active subscription if it exists subscription = await Subscription.filter(user=db_user, active=True).first() # Handle case when no active subscription is found subscription_end = ( subscription.expiration_time.isoformat() if subscription else None ) is_active = await db_user.is_active() if is_active: await db_user.remover_user_session() access_token = create_access_token( data={ "user_type": db_user.user_type, "user_name": db_user.name, "sub": db_user.phoneNumber, "locked": db_user.account_locked, "userId": db_user.id, "subscription_end": subscription_end, } ) return AccessTokenResponse(access_token=access_token, token_type="bearer") # Raise an error if credentials are invalid raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid credentials" ) @user_router.post( "/user/forgot-password", response_model=BaseResponse, status_code=status.HTTP_200_OK ) async def forgot_password(request: ForgotPasswordRequest): user = await User.filter(phoneNumber=request.phoneNumber).first() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found." ) await user.initiate_password_reset() return BaseResponse(code=200, message="Password reset token sent to your phone.") @user_router.post( "/user/verify-reset-token", response_model=BaseResponse, status_code=status.HTTP_200_OK, ) async def verify_reset_token(request: VerifyResetTokenRequest): user = await User.filter( phoneNumber=request.phoneNumber, reset_token=request.reset_token ).first() if not user or datetime.utcnow() > user.reset_token_expiration: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid or expired token." ) return BaseResponse(code=200, message="Token verified. Proceed to reset password.") @user_router.post( "/user/reset-password", response_model=BaseResponse, status_code=status.HTTP_200_OK ) async def reset_password(request: ResetPasswordRequest): user = await User.filter(phoneNumber=request.phoneNumber).first() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found." ) if not await user.reset_password(request.reset_token, request.new_password): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Invalid or expired token." ) return BaseResponse(code=200, message="Password has been reset successfully.") @user_router.get( "/user/all", response_model=List[UserResponse], status_code=status.HTTP_200_OK, ) async def get_all_users(admin: User = Depends(get_admin_user)): users = await User.all() return [UserResponse.from_orm(user) for user in users] @user_router.delete( "/user/{user_id}", response_model=BaseResponse, status_code=status.HTTP_200_OK ) async def delete_user(user_id: str, admin: User = Depends(get_current_active_user)): user = await User.filter(id=user_id).first() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found." ) await user.delete() return BaseResponse(code=200, message="User deleted successfully.") @user_router.put( "/user/toggle_status/{user_id}", response_model=BaseResponse, status_code=status.HTTP_200_OK, ) async def toggle_user_status(user_id: str): user = await User.filter(id=user_id).first() if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found." ) await user.toggle_status() message = ( "User disabled successfully." if user.account_locked else "User enabled successfully." ) return BaseResponse(code=200, message=message) @user_router.get( "/user/me", response_model=UserResponse, status_code=status.HTTP_200_OK ) async def get_user_details(current_user: User = Depends(get_current_active_user)): """ Get the current user's details and balance. """ return UserResponse.from_orm(current_user) @user_router.post("/user/{user_id}/activate", response_model=BaseResponse) async def activate_user(user_id: str): user = await User.get_or_none(id=user_id) if not user: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, detail="User not found." ) if await user.activate_user(): return BaseResponse(code=200, message="User activated successfully.") else: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Failed to activate user." ) @user_router.get( "/users/active", response_model=List[str] ) # Change response model to List[str] async def get_active_users(): # Fetch all active users using the Android client api_response: APIResponse = await client.get_active_users() active_users = api_response.active_users if not api_response or not active_users: return JSONResponse(content={"status": 200, "message": "no users online"}) # Collect phone numbers from active users active_phone_numbers = [user["phone"] for user in active_users] # Filter users from the database using the collected phone numbers users_in_db = await User.filter(phoneNumber__in=active_phone_numbers).all() # Extract usernames from the matched users usernames = [ {"name": user.name, "phone": user.phoneNumber} for user in users_in_db ] # Assuming 'name' is the field for username in the User model if not usernames: return JSONResponse( content={"status": 200, "message": "No matching active users found."} ) return JSONResponse( content={ "status": 200, "message": f"Found {len(usernames)} active", "payload": usernames, } ) @user_router.get("/user/active", response_model=List[UserResponse]) async def get_active_user(phone_numbers: List[str] = Query(...)): # Fetch all active users using the Android client api_response: APIResponse = await client.get_active_users() active_users = api_response.active_users if not api_response or not api_response.active_users: return JSONResponse(content={"status": 200, "message": "no users online"}) # Filter active users by matching phone numbers matched_users = [ UserResponse.from_orm(user) for user in active_users if user["phone"] in phone_numbers ] if not matched_users: return JSONResponse( content={"status": 200, "message": "No matching active users found."} ) return matched_users