File size: 9,533 Bytes
41eef54
 
9e798a1
 
 
 
 
 
 
 
f30e75c
b856986
8450c71
b856986
f30e75c
9e798a1
39fc4b3
f30e75c
7875b25
 
41eef54
 
 
8450c71
9e798a1
b856986
 
41eef54
8450c71
7875b25
 
8450c71
9e798a1
b856986
 
9e798a1
 
 
b856986
 
 
9e798a1
7875b25
 
 
 
 
9e798a1
 
 
 
 
b856986
9e798a1
 
 
7875b25
 
9e798a1
 
 
b856986
9e798a1
 
 
 
 
39fc4b3
6a3e1a1
41eef54
 
 
 
 
 
 
a4d5446
39fc4b3
a4d5446
6a3e1a1
39fc4b3
 
 
 
4aa1d81
 
 
39fc4b3
e0be833
 
 
84b44a7
 
41eef54
3931352
84b44a7
 
 
39fc4b3
84b44a7
 
b856986
39fc4b3
 
b856986
9e798a1
b856986
 
9e798a1
 
 
 
b856986
 
 
9e798a1
 
 
 
 
b856986
 
9e798a1
 
 
 
 
b856986
9e798a1
 
 
 
 
 
 
 
 
 
 
 
 
b856986
 
 
9e798a1
 
 
 
 
 
 
b856986
f30e75c
 
 
41eef54
 
 
f30e75c
41eef54
f30e75c
 
57ab719
 
41eef54
 
 
 
 
 
 
 
 
 
 
6c582f0
 
 
427d907
6c582f0
 
 
427d907
6c582f0
 
 
 
 
41eef54
fbeb0b0
 
 
 
 
 
7c11a75
 
 
 
 
 
 
 
 
 
4aa1d81
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41eef54
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
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