Mbonea commited on
Commit
41eef54
·
1 Parent(s): 69d4494
App/Android/Android.py CHANGED
@@ -75,14 +75,18 @@ class AndroidClient:
75
  async def remove_session(self, phone_number) -> APIResponse:
76
  """Enable or disable a user."""
77
  path = f"/users/{phone_number}/remove-session"
78
- response = await self.client.post(path, json=None)
79
- return APIResponse(**response.json())
 
 
 
80
 
81
  @require_base_url
82
- async def disable_user(self, request: SetUserStatusRequest) -> APIResponse:
83
  """disable a user."""
84
  path = f"/users/{request.phone}/disable"
85
  response = await self.client.post(path, json=None)
 
86
  return APIResponse(**response.json())
87
 
88
  @require_base_url
@@ -143,6 +147,6 @@ class AndroidClient:
143
 
144
  async def deactivate_user(self, phone_number: str):
145
  request = SetUserStatusRequest(phone=phone_number, disabled=False)
146
- await self.disable_user(request=request)
147
  # Replace this with actual API call logic
148
  return {"status": "success", "message": "User activated."}
 
75
  async def remove_session(self, phone_number) -> APIResponse:
76
  """Enable or disable a user."""
77
  path = f"/users/{phone_number}/remove-session"
78
+ try:
79
+ response = await self.client.post(path, json=None)
80
+ return APIResponse(**response.json())
81
+ except:
82
+ pass
83
 
84
  @require_base_url
85
+ async def _disable_request(self, request: SetUserStatusRequest) -> APIResponse:
86
  """disable a user."""
87
  path = f"/users/{request.phone}/disable"
88
  response = await self.client.post(path, json=None)
89
+
90
  return APIResponse(**response.json())
91
 
92
  @require_base_url
 
147
 
148
  async def deactivate_user(self, phone_number: str):
149
  request = SetUserStatusRequest(phone=phone_number, disabled=False)
150
+ await self._disable_request(request=request)
151
  # Replace this with actual API call logic
152
  return {"status": "success", "message": "User activated."}
App/Android/Schema.py CHANGED
@@ -66,8 +66,9 @@ class UserListItem(BaseModel):
66
  class APIResponse(BaseModel):
67
  success: bool
68
  message: str
69
- data: Optional[Dict[str, Any]] = None
70
  error: Optional[str] = None
 
71
 
72
 
73
  # Specific Response Schemas
 
66
  class APIResponse(BaseModel):
67
  success: bool
68
  message: str
69
+ data: Optional[Any] = None
70
  error: Optional[str] = None
71
+ active_users: Optional[Any] = None
72
 
73
 
74
  # Specific Response Schemas
App/Messages/MessagesRoute.py CHANGED
@@ -1,5 +1,6 @@
1
  # App/Messages/Routes.py
2
- from fastapi import APIRouter, HTTPException
 
3
  from .Model import Message
4
  from .Schema import MessageCreate, MessageResponse
5
  from typing import Optional
@@ -16,6 +17,30 @@ from tortoise.contrib.pydantic import pydantic_model_creator
16
  import traceback
17
  import logging
18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  message_router = APIRouter(tags=["Messages"], prefix="/messages")
20
  logging.basicConfig(level=logging.WARNING)
21
 
@@ -57,9 +82,7 @@ async def receive_message(message_data: MessageCreate):
57
  message = await Message.get_or_none(message_id=message_data.id)
58
  if not message:
59
  # Process Mpesa Payments
60
- user: User = await User.get_or_none(
61
- phoneNumber="+" + parsed_data["phone_number"]
62
- )
63
  data_plan: Plan = await Plan.get_or_none(
64
  amount=Decimal(parsed_data["amount_received"])
65
  )
 
1
  # App/Messages/Routes.py
2
+ from fastapi import APIRouter, HTTPException, Depends
3
+ from typing import List
4
  from .Model import Message
5
  from .Schema import MessageCreate, MessageResponse
6
  from typing import Optional
 
17
  import traceback
18
  import logging
19
 
20
+ from App.Users.dependencies import (
21
+ get_current_active_user,
22
+ UserType,
23
+ ) # Assuming you have a dependency to get the current user
24
+
25
+ message_router = APIRouter(tags=["Messages"])
26
+
27
+
28
+ @message_router.get("/messages", response_model=List[MessageResponse])
29
+ async def get_all_messages(current_user: User = Depends(get_current_active_user)):
30
+ # Check if the current user is an admin
31
+ if current_user.user_type != UserType.ADMIN:
32
+ raise HTTPException(
33
+ status_code=403,
34
+ detail="User does not have permission to access this resource",
35
+ )
36
+
37
+ # Fetch all messages from the database
38
+ messages = await Message.all()
39
+
40
+ # Serialize the messages
41
+ return [MessageResponse.from_orm(message) for message in messages]
42
+
43
+
44
  message_router = APIRouter(tags=["Messages"], prefix="/messages")
45
  logging.basicConfig(level=logging.WARNING)
46
 
 
82
  message = await Message.get_or_none(message_id=message_data.id)
83
  if not message:
84
  # Process Mpesa Payments
85
+ user: User = await User.get_or_none(phoneNumber=parsed_data["phone_number"])
 
 
86
  data_plan: Plan = await Plan.get_or_none(
87
  amount=Decimal(parsed_data["amount_received"])
88
  )
App/Payments/PaymentsRoutes.py CHANGED
@@ -1,5 +1,6 @@
1
- from fastapi import APIRouter, HTTPException, status
2
  from typing import List
 
3
  from .Model import Payment
4
  from App.Users.Model import User
5
  from App.Plans.Model import Plan
@@ -12,12 +13,17 @@ from .Schema import (
12
  PaymentListResponse,
13
  )
14
  from .Schema import PaymentMethod
 
 
 
 
 
15
 
16
  payment_router = APIRouter(tags=["Payments"])
17
 
18
 
19
  @payment_router.post("/payment/create", response_model=BaseResponse)
20
- async def create_payment(request: CreatePaymentRequest, internal=False):
21
  # If payment method is "Lipa Number", transaction_id is required
22
  if (
23
  request.payment_method == PaymentMethod.LIPA_NUMBER
@@ -35,6 +41,14 @@ async def create_payment(request: CreatePaymentRequest, internal=False):
35
  status_code=status.HTTP_404_NOT_FOUND, detail="Plan not found"
36
  )
37
 
 
 
 
 
 
 
 
 
38
  # Create new payment without linking a user initially
39
  payment = await Payment.create(
40
  user_id=request.user_id,
@@ -148,3 +162,64 @@ async def link_user_to_payment(payment_id: str, request: UpdatePaymentUserReques
148
  message="Payment linked to user successfully",
149
  payload={"payment_id": str(payment.id), "user_id": request.user_id},
150
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, HTTPException, status, Query, Depends
2
  from typing import List
3
+ from datetime import datetime
4
  from .Model import Payment
5
  from App.Users.Model import User
6
  from App.Plans.Model import Plan
 
13
  PaymentListResponse,
14
  )
15
  from .Schema import PaymentMethod
16
+ from App.Users.dependencies import (
17
+ get_current_active_user,
18
+ UserType,
19
+ ) # Assuming you have a dependency to get the current user
20
+
21
 
22
  payment_router = APIRouter(tags=["Payments"])
23
 
24
 
25
  @payment_router.post("/payment/create", response_model=BaseResponse)
26
+ async def create_payment(request: CreatePaymentRequest, internal: bool = False):
27
  # If payment method is "Lipa Number", transaction_id is required
28
  if (
29
  request.payment_method == PaymentMethod.LIPA_NUMBER
 
41
  status_code=status.HTTP_404_NOT_FOUND, detail="Plan not found"
42
  )
43
 
44
+ payment = await Payment.get_or_none(transaction_id=request.transaction_id)
45
+
46
+ if payment:
47
+ return BaseResponse(
48
+ code=404,
49
+ message="Payment already exists",
50
+ payload={"payment_id": str(payment.id)},
51
+ )
52
  # Create new payment without linking a user initially
53
  payment = await Payment.create(
54
  user_id=request.user_id,
 
162
  message="Payment linked to user successfully",
163
  payload={"payment_id": str(payment.id), "user_id": request.user_id},
164
  )
165
+
166
+
167
+ @payment_router.get("/payments/date-range", response_model=PaymentListResponse)
168
+ async def get_payments_by_date_range(
169
+ start_date: datetime = Query(..., description="Start date in ISO format"),
170
+ end_date: datetime = Query(..., description="End date in ISO format"),
171
+ ):
172
+ if start_date > end_date:
173
+ raise HTTPException(
174
+ status_code=400, detail="Start date must be less than or equal to end date"
175
+ )
176
+
177
+ payments = await Payment.filter(created_time__range=(start_date, end_date))
178
+
179
+ result = [
180
+ PaymentResponse(
181
+ id=str(payment.id),
182
+ user_id=payment.user_id,
183
+ plan_id=payment.plan_id,
184
+ amount=payment.amount,
185
+ payment_method=payment.payment_method,
186
+ status=payment.status,
187
+ transaction_id=payment.transaction_id,
188
+ created_time=payment.created_time,
189
+ updated_time=payment.updated_time,
190
+ )
191
+ for payment in payments
192
+ ]
193
+
194
+ return PaymentListResponse(payments=result, total_count=len(result))
195
+
196
+
197
+ @payment_router.get("/payments/user/{user_id}", response_model=PaymentListResponse)
198
+ async def get_user_payment_history(
199
+ user_id: str, current_user: User = Depends(get_current_active_user)
200
+ ):
201
+ # Optionally, you can check if the current user has permission to view this user's payment history
202
+ if current_user.user_type != UserType.ADMIN and current_user.id != user_id:
203
+ raise HTTPException(
204
+ status_code=403,
205
+ detail="User does not have permission to view this payment history",
206
+ )
207
+
208
+ payments = await Payment.filter(user_id=user_id)
209
+
210
+ result = [
211
+ PaymentResponse(
212
+ id=str(payment.id),
213
+ user_id=payment.user_id,
214
+ plan_id=payment.plan_id,
215
+ amount=payment.amount,
216
+ payment_method=payment.payment_method,
217
+ status=payment.status,
218
+ transaction_id=payment.transaction_id,
219
+ created_time=payment.created_time,
220
+ updated_time=payment.updated_time,
221
+ )
222
+ for payment in payments
223
+ ]
224
+
225
+ return PaymentListResponse(payments=result, total_count=len(result))
App/Payments/Schema.py CHANGED
@@ -10,6 +10,7 @@ class BaseResponse(BaseModel):
10
  payload: Optional[dict] = None
11
 
12
 
 
13
  class PaymentMethod:
14
  CASH = "cash"
15
  MPESA = "mpesa"
@@ -19,6 +20,12 @@ class PaymentMethod:
19
  CHOICES = [CASH, MPESA, LIPA_NUMBER, CREDIT_CARD]
20
 
21
 
 
 
 
 
 
 
22
  class CreatePaymentRequest(BaseModel):
23
  user_id: Optional[str] = Field(
24
  None, description="ID of the user making the payment"
 
10
  payload: Optional[dict] = None
11
 
12
 
13
+ ### Constants
14
  class PaymentMethod:
15
  CASH = "cash"
16
  MPESA = "mpesa"
 
20
  CHOICES = [CASH, MPESA, LIPA_NUMBER, CREDIT_CARD]
21
 
22
 
23
+ class PaymentStatus:
24
+ ADDED_TO_BALANCE = "Added to balance"
25
+ PURCHASED_PLAN = "Purchased plan"
26
+ PENDING = "PENDING"
27
+
28
+
29
  class CreatePaymentRequest(BaseModel):
30
  user_id: Optional[str] = Field(
31
  None, description="ID of the user making the payment"
App/Users/Constants.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class UserType:
2
+ ADMIN = 1
3
+ MODERATOR = 2
4
+ USER = 4
5
+
6
+
7
+ class ErrorCodes:
8
+ NOT_FOUND = 1
9
+ MIKROTIK = 2
10
+ INVALID_CREDENTIALS = 3
11
+ SERVER_ERROR = 0
App/Users/Model.py CHANGED
@@ -7,6 +7,7 @@ from App.Android.Schema import RegisterUserRequest as AndroidRegister
7
  from App.Templates.Templates import MessageTemplate
8
  from App.Subscriptions.Model import Subscription
9
  from App.Plans.Model import Plan
 
10
  import datetime
11
  import uuid
12
  import random
@@ -36,6 +37,7 @@ class User(models.Model):
36
  id = fields.CharField(primary_key=True, max_length=5, default=generate_short_uuid)
37
  name = fields.CharField(max_length=100)
38
  password = fields.CharField(max_length=100) # Stores hashed password
 
39
  phoneNumber = fields.CharField(max_length=15, unique=True)
40
  balance = fields.DecimalField(max_digits=10, decimal_places=2, default=0.00)
41
  mac_address = fields.CharField(max_length=17)
@@ -43,6 +45,7 @@ class User(models.Model):
43
  updatedAt = fields.DatetimeField(auto_now=True)
44
  lastLogin = fields.DatetimeField(default=datetime.datetime.now)
45
  failed_attempts = fields.IntField(default=0)
 
46
  account_locked = fields.BooleanField(default=False)
47
  reset_token = fields.CharField(max_length=6, null=True, unique=True)
48
  reset_token_expiration = fields.DatetimeField(null=True)
@@ -96,6 +99,16 @@ class User(models.Model):
96
  await self.save()
97
  return False
98
 
 
 
 
 
 
 
 
 
 
 
99
  async def initiate_password_reset(self):
100
  """Generates a reset token and sends it to the user via message."""
101
  self.reset_token = f"{random.randint(100000, 999999)}"
@@ -321,8 +334,6 @@ class User(models.Model):
321
 
322
  async def deactivate_user(self):
323
 
324
- self.account_locked = True
325
- await self.save()
326
  await self.remover_user_session()
327
 
328
  try:
 
7
  from App.Templates.Templates import MessageTemplate
8
  from App.Subscriptions.Model import Subscription
9
  from App.Plans.Model import Plan
10
+ from .Constants import UserType
11
  import datetime
12
  import uuid
13
  import random
 
37
  id = fields.CharField(primary_key=True, max_length=5, default=generate_short_uuid)
38
  name = fields.CharField(max_length=100)
39
  password = fields.CharField(max_length=100) # Stores hashed password
40
+ user_type = fields.IntField(default=UserType.USER)
41
  phoneNumber = fields.CharField(max_length=15, unique=True)
42
  balance = fields.DecimalField(max_digits=10, decimal_places=2, default=0.00)
43
  mac_address = fields.CharField(max_length=17)
 
45
  updatedAt = fields.DatetimeField(auto_now=True)
46
  lastLogin = fields.DatetimeField(default=datetime.datetime.now)
47
  failed_attempts = fields.IntField(default=0)
48
+
49
  account_locked = fields.BooleanField(default=False)
50
  reset_token = fields.CharField(max_length=6, null=True, unique=True)
51
  reset_token_expiration = fields.DatetimeField(null=True)
 
99
  await self.save()
100
  return False
101
 
102
+ async def toggle_status(self):
103
+ if self.account_locked:
104
+ await self.activate_user()
105
+ self.account_locked = False
106
+ else:
107
+ await self.deactivate_user()
108
+ self.account_locked = True
109
+ # self.account_locked = not self.account_locked
110
+ await self.save()
111
+
112
  async def initiate_password_reset(self):
113
  """Generates a reset token and sends it to the user via message."""
114
  self.reset_token = f"{random.randint(100000, 999999)}"
 
334
 
335
  async def deactivate_user(self):
336
 
 
 
337
  await self.remover_user_session()
338
 
339
  try:
App/Users/Schema.py CHANGED
@@ -54,7 +54,8 @@ class RegisterUserRequest(BaseModel):
54
  class LoginUserRequest(BaseModel):
55
  phoneNumber: str = Field(..., pattern=PHONE_PATTERN)
56
  password: str
57
- mac_address: str = Field(..., pattern=MAC_PATTERN)
 
58
 
59
 
60
  # Access Token Response
 
54
  class LoginUserRequest(BaseModel):
55
  phoneNumber: str = Field(..., pattern=PHONE_PATTERN)
56
  password: str
57
+ # grant_type: Optional[str] = None
58
+ mac_address: Optional[str] = Field(..., pattern=MAC_PATTERN)
59
 
60
 
61
  # Access Token Response
App/Users/UserRoutes.py CHANGED
@@ -1,4 +1,5 @@
1
- from fastapi import APIRouter, HTTPException, status, Depends
 
2
  from .Schema import (
3
  RegisterUserRequest,
4
  LoginUserRequest,
@@ -17,12 +18,14 @@ from App.Subscriptions.Model import Subscription
17
  from App.Android.Android import AndroidClient
18
  from App.Android.Schema import RegisterUserRequest as AndroidRegister
19
  from App.Templates.Templates import MessageTemplate
20
- from .dependencies import get_current_active_user
 
 
21
 
22
  # JWT Configurations
23
  SECRET_KEY = "your_secret_key_here"
24
  ALGORITHM = "HS256"
25
- ACCESS_TOKEN_EXPIRE_MINUTES = 30
26
  user_router = APIRouter(tags=["User"])
27
  client = AndroidClient()
28
  templates = MessageTemplate()
@@ -64,6 +67,13 @@ async def register_user(request: RegisterUserRequest):
64
  async def login_user(request: LoginUserRequest):
65
  # Find user by phone number
66
  db_user: User = await User.filter(phoneNumber=request.phoneNumber).first()
 
 
 
 
 
 
 
67
  valid_password = await db_user.verify_password(request.password)
68
  # Check if user exists and password is correct
69
  if db_user and valid_password:
@@ -81,6 +91,7 @@ async def login_user(request: LoginUserRequest):
81
  await db_user.remover_user_session()
82
  access_token = create_access_token(
83
  data={
 
84
  "user_name": db_user.name,
85
  "sub": db_user.phoneNumber,
86
  "locked": db_user.account_locked,
@@ -142,24 +153,26 @@ async def reset_password(request: ResetPasswordRequest):
142
 
143
 
144
  @user_router.get(
145
- "/user/all", response_model=List[UserResponse], status_code=status.HTTP_200_OK
 
 
146
  )
147
- async def get_all_users():
148
  users = await User.all()
149
  return [UserResponse.from_orm(user) for user in users]
150
 
151
 
152
- # @user_router.delete(
153
- # "/user/{user_id}", response_model=BaseResponse, status_code=status.HTTP_200_OK
154
- # )
155
- # async def delete_user(user_id: str):
156
- # user = await User.filter(id=user_id).first()
157
- # if not user:
158
- # raise HTTPException(
159
- # status_code=status.HTTP_404_NOT_FOUND, detail="User not found."
160
- # )
161
- # await user.delete()
162
- # return BaseResponse(code=200, message="User deleted successfully.")
163
 
164
 
165
  @user_router.put(
@@ -173,8 +186,7 @@ async def toggle_user_status(user_id: str):
173
  raise HTTPException(
174
  status_code=status.HTTP_404_NOT_FOUND, detail="User not found."
175
  )
176
- user.account_locked = not user.account_locked
177
- await user.save()
178
  message = (
179
  "User disabled successfully."
180
  if user.account_locked
@@ -207,3 +219,62 @@ async def activate_user(user_id: str):
207
  raise HTTPException(
208
  status_code=status.HTTP_400_BAD_REQUEST, detail="Failed to activate user."
209
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import APIRouter, HTTPException, status, Depends, Query
2
+ from fastapi.responses import JSONResponse
3
  from .Schema import (
4
  RegisterUserRequest,
5
  LoginUserRequest,
 
18
  from App.Android.Android import AndroidClient
19
  from App.Android.Schema import RegisterUserRequest as AndroidRegister
20
  from App.Templates.Templates import MessageTemplate
21
+ from .dependencies import get_current_active_user, get_admin_user
22
+ from App.Android.Schema import APIResponse
23
+
24
 
25
  # JWT Configurations
26
  SECRET_KEY = "your_secret_key_here"
27
  ALGORITHM = "HS256"
28
+ ACCESS_TOKEN_EXPIRE_MINUTES = 300
29
  user_router = APIRouter(tags=["User"])
30
  client = AndroidClient()
31
  templates = MessageTemplate()
 
67
  async def login_user(request: LoginUserRequest):
68
  # Find user by phone number
69
  db_user: User = await User.filter(phoneNumber=request.phoneNumber).first()
70
+
71
+ # Raise an error if the user does not exist
72
+ if db_user is None:
73
+ raise HTTPException(
74
+ status_code=status.HTTP_404_NOT_FOUND, detail="User not found."
75
+ )
76
+
77
  valid_password = await db_user.verify_password(request.password)
78
  # Check if user exists and password is correct
79
  if db_user and valid_password:
 
91
  await db_user.remover_user_session()
92
  access_token = create_access_token(
93
  data={
94
+ "user_type": db_user.user_type,
95
  "user_name": db_user.name,
96
  "sub": db_user.phoneNumber,
97
  "locked": db_user.account_locked,
 
153
 
154
 
155
  @user_router.get(
156
+ "/user/all",
157
+ response_model=List[UserResponse],
158
+ status_code=status.HTTP_200_OK,
159
  )
160
+ async def get_all_users(admin: User = Depends(get_admin_user)):
161
  users = await User.all()
162
  return [UserResponse.from_orm(user) for user in users]
163
 
164
 
165
+ @user_router.delete(
166
+ "/user/{user_id}", response_model=BaseResponse, status_code=status.HTTP_200_OK
167
+ )
168
+ async def delete_user(user_id: str, admin: User = Depends(get_current_active_user)):
169
+ user = await User.filter(id=user_id).first()
170
+ if not user:
171
+ raise HTTPException(
172
+ status_code=status.HTTP_404_NOT_FOUND, detail="User not found."
173
+ )
174
+ await user.delete()
175
+ return BaseResponse(code=200, message="User deleted successfully.")
176
 
177
 
178
  @user_router.put(
 
186
  raise HTTPException(
187
  status_code=status.HTTP_404_NOT_FOUND, detail="User not found."
188
  )
189
+ await user.toggle_status()
 
190
  message = (
191
  "User disabled successfully."
192
  if user.account_locked
 
219
  raise HTTPException(
220
  status_code=status.HTTP_400_BAD_REQUEST, detail="Failed to activate user."
221
  )
222
+
223
+
224
+ @user_router.get(
225
+ "/users/active", response_model=List[str]
226
+ ) # Change response model to List[str]
227
+ async def get_active_users():
228
+ # Fetch all active users using the Android client
229
+ api_response: APIResponse = await client.get_active_users()
230
+ active_users = api_response.active_users
231
+
232
+ if not api_response or not active_users:
233
+ return JSONResponse(content={"status": 200, "message": "no users online"})
234
+
235
+ # Collect phone numbers from active users
236
+ active_phone_numbers = [user["phone"] for user in active_users]
237
+
238
+ # Filter users from the database using the collected phone numbers
239
+ users_in_db = await User.filter(phoneNumber__in=active_phone_numbers).all()
240
+
241
+ # Extract usernames from the matched users
242
+ usernames = [
243
+ {"name": user.name, "phone": user.phoneNumber} for user in users_in_db
244
+ ] # Assuming 'name' is the field for username in the User model
245
+
246
+ if not usernames:
247
+ return JSONResponse(
248
+ content={"status": 200, "message": "No matching active users found."}
249
+ )
250
+
251
+ return JSONResponse(
252
+ content={
253
+ "status": 200,
254
+ "message": f"Found {len(usernames)} active",
255
+ "payload": usernames,
256
+ }
257
+ )
258
+
259
+
260
+ @user_router.get("/user/active", response_model=List[UserResponse])
261
+ async def get_active_user(phone_numbers: List[str] = Query(...)):
262
+ # Fetch all active users using the Android client
263
+ api_response: APIResponse = await client.get_active_users()
264
+ active_users = api_response.active_users
265
+ if not api_response or not api_response.active_users:
266
+ return JSONResponse(content={"status": 200, "message": "no users online"})
267
+
268
+ # Filter active users by matching phone numbers
269
+ matched_users = [
270
+ UserResponse.from_orm(user)
271
+ for user in active_users
272
+ if user["phone"] in phone_numbers
273
+ ]
274
+
275
+ if not matched_users:
276
+ return JSONResponse(
277
+ content={"status": 200, "message": "No matching active users found."}
278
+ )
279
+
280
+ return matched_users
App/Users/dependencies.py CHANGED
@@ -1,6 +1,7 @@
1
  from fastapi import Depends, HTTPException, status
2
  from jose import jwt
3
- from App.Users.Model import User
 
4
  from fastapi.security import OAuth2PasswordBearer
5
 
6
  SECRET_KEY = "your_secret_key_here"
@@ -26,3 +27,14 @@ async def get_current_user(token: str = Depends(oauth2_scheme)):
26
 
27
  async def get_current_active_user(current_user: User = Depends(get_current_user)):
28
  return current_user
 
 
 
 
 
 
 
 
 
 
 
 
1
  from fastapi import Depends, HTTPException, status
2
  from jose import jwt
3
+ from .Model import User
4
+ from .Constants import UserType
5
  from fastapi.security import OAuth2PasswordBearer
6
 
7
  SECRET_KEY = "your_secret_key_here"
 
27
 
28
  async def get_current_active_user(current_user: User = Depends(get_current_user)):
29
  return current_user
30
+
31
+
32
+ async def get_admin_user(current_user: User = Depends(get_current_active_user)):
33
+ print(current_user.user_type, current_user.name)
34
+ if current_user.user_type != UserType.ADMIN:
35
+ raise HTTPException(
36
+ status_code=status.HTTP_401_UNAUTHORIZED,
37
+ detail="User does not have permission",
38
+ headers={"WWW-Authenticate": "Bearer"},
39
+ )
40
+ return current_user
migrations/models/0_20241204103359_init.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tortoise import BaseDBAsyncClient
2
+
3
+
4
+ async def upgrade(db: BaseDBAsyncClient) -> str:
5
+ return """
6
+ CREATE TABLE IF NOT EXISTS "messages" (
7
+ "id" UUID NOT NULL PRIMARY KEY,
8
+ "device_id" VARCHAR(100),
9
+ "event" VARCHAR(100),
10
+ "message_id" VARCHAR(100),
11
+ "webhook_id" VARCHAR(100),
12
+ "message_content" TEXT NOT NULL,
13
+ "phone_number" VARCHAR(20) NOT NULL,
14
+ "received_at" TIMESTAMPTZ NOT NULL,
15
+ "sim_number" INT,
16
+ "parsed_data" JSONB,
17
+ "created_time" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
18
+ );
19
+ CREATE TABLE IF NOT EXISTS "plans" (
20
+ "id" UUID NOT NULL PRIMARY KEY,
21
+ "name" VARCHAR(100) NOT NULL UNIQUE,
22
+ "amount" DECIMAL(10,2) NOT NULL,
23
+ "duration" INT NOT NULL,
24
+ "download_speed" DOUBLE PRECISION NOT NULL,
25
+ "upload_speed" DOUBLE PRECISION NOT NULL,
26
+ "expire_date" TIMESTAMPTZ,
27
+ "is_promo" BOOL NOT NULL DEFAULT False,
28
+ "promo_duration_days" INT,
29
+ "is_valid" BOOL NOT NULL DEFAULT True
30
+ );
31
+ COMMENT ON COLUMN "plans"."name" IS 'Name of the subscription plan';
32
+ COMMENT ON COLUMN "plans"."amount" IS 'Cost of the plan';
33
+ COMMENT ON COLUMN "plans"."duration" IS 'Duration of the subscription in hours';
34
+ COMMENT ON COLUMN "plans"."download_speed" IS 'Download speed in Mbps';
35
+ COMMENT ON COLUMN "plans"."upload_speed" IS 'Upload speed in Mbps';
36
+ COMMENT ON COLUMN "plans"."expire_date" IS 'Expiration date of the plan';
37
+ COMMENT ON COLUMN "plans"."is_promo" IS 'Indicates if the plan is a promotional plan';
38
+ COMMENT ON COLUMN "plans"."promo_duration_days" IS 'Number of days the promotion is valid';
39
+ COMMENT ON COLUMN "plans"."is_valid" IS 'Indicates if the plan is valid';
40
+ CREATE TABLE IF NOT EXISTS "portals" (
41
+ "id" SERIAL NOT NULL PRIMARY KEY,
42
+ "name" VARCHAR(50) NOT NULL UNIQUE,
43
+ "description" VARCHAR(255) NOT NULL,
44
+ "url" VARCHAR(255) NOT NULL
45
+ );
46
+ COMMENT ON COLUMN "portals"."name" IS 'Name of the portal, e.g., Android or MikroTik';
47
+ COMMENT ON COLUMN "portals"."description" IS 'Description of the portal';
48
+ COMMENT ON COLUMN "portals"."url" IS 'URL of the portal, must start with http or https';
49
+ CREATE TABLE IF NOT EXISTS "users" (
50
+ "id" VARCHAR(5) NOT NULL PRIMARY KEY,
51
+ "name" VARCHAR(100) NOT NULL,
52
+ "password" VARCHAR(100) NOT NULL,
53
+ "phoneNumber" VARCHAR(15) NOT NULL UNIQUE,
54
+ "balance" DECIMAL(10,2) NOT NULL DEFAULT 0,
55
+ "mac_address" VARCHAR(17) NOT NULL,
56
+ "createdAt" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
57
+ "updatedAt" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
58
+ "lastLogin" TIMESTAMPTZ NOT NULL,
59
+ "failed_attempts" INT NOT NULL DEFAULT 0,
60
+ "account_locked" BOOL NOT NULL DEFAULT False,
61
+ "reset_token" VARCHAR(6) UNIQUE,
62
+ "reset_token_expiration" TIMESTAMPTZ
63
+ );
64
+ CREATE TABLE IF NOT EXISTS "payments" (
65
+ "id" UUID NOT NULL PRIMARY KEY,
66
+ "amount" DECIMAL(10,2) NOT NULL,
67
+ "status" VARCHAR(50) NOT NULL DEFAULT 'pending',
68
+ "payment_method" VARCHAR(50) NOT NULL,
69
+ "transaction_id" VARCHAR(100) UNIQUE,
70
+ "created_time" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
71
+ "updated_time" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
72
+ "plan_id" UUID REFERENCES "plans" ("id") ON DELETE CASCADE,
73
+ "user_id" VARCHAR(5) REFERENCES "users" ("id") ON DELETE CASCADE
74
+ );
75
+ COMMENT ON COLUMN "payments"."amount" IS 'Payment amount';
76
+ COMMENT ON COLUMN "payments"."status" IS 'Payment status (e.g., pending, completed, failed, balance-assigned)';
77
+ COMMENT ON COLUMN "payments"."payment_method" IS 'Payment method';
78
+ COMMENT ON COLUMN "payments"."transaction_id" IS 'Unique transaction ID for payment (for methods like Lipa Number)';
79
+ COMMENT ON COLUMN "payments"."plan_id" IS 'Plan associated with the payment';
80
+ CREATE TABLE IF NOT EXISTS "subscriptions" (
81
+ "id" UUID NOT NULL PRIMARY KEY,
82
+ "active" BOOL NOT NULL DEFAULT True,
83
+ "duration" INT NOT NULL,
84
+ "download_mb" DOUBLE PRECISION NOT NULL DEFAULT 0,
85
+ "upload_mb" DOUBLE PRECISION NOT NULL DEFAULT 0,
86
+ "created_time" TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
87
+ "expiration_time" TIMESTAMPTZ,
88
+ "plan_id" UUID REFERENCES "plans" ("id") ON DELETE CASCADE,
89
+ "user_id" VARCHAR(5) NOT NULL REFERENCES "users" ("id") ON DELETE CASCADE
90
+ );
91
+ COMMENT ON COLUMN "subscriptions"."duration" IS 'Duration in hours';
92
+ COMMENT ON COLUMN "subscriptions"."download_mb" IS 'Download usage in megabytes';
93
+ COMMENT ON COLUMN "subscriptions"."upload_mb" IS 'Upload usage in megabytes';
94
+ COMMENT ON COLUMN "subscriptions"."plan_id" IS 'Plan associated with the subscription';
95
+ CREATE TABLE IF NOT EXISTS "aerich" (
96
+ "id" SERIAL NOT NULL PRIMARY KEY,
97
+ "version" VARCHAR(255) NOT NULL,
98
+ "app" VARCHAR(100) NOT NULL,
99
+ "content" JSONB NOT NULL
100
+ );"""
101
+
102
+
103
+ async def downgrade(db: BaseDBAsyncClient) -> str:
104
+ return """
105
+ """
migrations/models/1_20241204103440_add_user_type_field.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from tortoise import BaseDBAsyncClient
2
+
3
+
4
+ async def upgrade(db: BaseDBAsyncClient) -> str:
5
+ return """
6
+ ALTER TABLE "users" ADD "user_type" INT NOT NULL DEFAULT 4;"""
7
+
8
+
9
+ async def downgrade(db: BaseDBAsyncClient) -> str:
10
+ return """
11
+ ALTER TABLE "users" DROP COLUMN "user_type";"""