Mbonea commited on
Commit
6f4f5f9
·
1 Parent(s): e21d0d6
App/Authentication.py DELETED
@@ -1,2 +0,0 @@
1
- from fastapi import APIRouter, status
2
- from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
 
 
 
App/Mikrotik/mikrotikRoutes.py DELETED
@@ -1,133 +0,0 @@
1
- from fastapi import APIRouter, HTTPException, status
2
- from typing import List, Optional
3
- from fastapi import FastAPI, HTTPException, Depends, Body, Path
4
- from fastapi.responses import JSONResponse
5
- from .utils.helperfx import get_mikrotik, MikrotikAPI
6
- from .schema import (
7
- UserCreate,
8
- RegisterResponse,
9
- LogoutResponse,
10
- UserStatsResponse,
11
- ActiveUsersResponse,
12
- UserStatusResponse,
13
- )
14
-
15
- mikrotik_router = APIRouter(tags=["Mikrotik"])
16
-
17
-
18
- # New route to remove a user's active session
19
- @mikrotik_router.post("/users/{phone}/remove-session")
20
- async def remove_active_session(
21
- phone: str = Path(..., description="User phone number"),
22
- mikrotik: MikrotikAPI = Depends(get_mikrotik),
23
- ):
24
- """Remove an active session for a hotspot user"""
25
- response = mikrotik.remove_active_session(phone)
26
- if not response["success"]:
27
- return JSONResponse(
28
- status_code=400,
29
- content=response,
30
- )
31
- return JSONResponse(content={"message": f"User {phone} session removed"})
32
-
33
-
34
- @mikrotik_router.post("/users/register", response_model=RegisterResponse)
35
- async def register_user(
36
- user: UserCreate = Body(...), mikrotik: MikrotikAPI = Depends(get_mikrotik)
37
- ):
38
- """Register a new hotspot user"""
39
- response = mikrotik.add_hotspot_user(
40
- phone=user.phoneNumber, password=user.password, profile=user.profile
41
- )
42
- print(response)
43
- if not response.code == 200:
44
- return JSONResponse(
45
- status_code=400,
46
- content=response.__dict__,
47
- )
48
- return response.__dict__
49
-
50
-
51
- @mikrotik_router.post("/users/logout/{phone}", response_model=LogoutResponse)
52
- async def logout_user(
53
- phone: str = Path(..., description="User phone number"),
54
- mikrotik: MikrotikAPI = Depends(get_mikrotik),
55
- ):
56
- """Logout a hotspot user"""
57
- response = mikrotik.logout_hotspot_user(phone)
58
- if not response.code == 200:
59
- return JSONResponse(
60
- status_code=400,
61
- content=response.__dict__,
62
- )
63
- return response.__dict__
64
-
65
-
66
- @mikrotik_router.post("/users/{phone}/disable", response_model=UserStatusResponse)
67
- async def disable_user(
68
- phone: str = Path(..., description="User phone number"),
69
- mikrotik: MikrotikAPI = Depends(get_mikrotik),
70
- ):
71
- """Disable a hotspot user"""
72
- response = mikrotik.set_user_status(phone, disabled=True)
73
- print(response)
74
- if not response.code == 200:
75
- return JSONResponse(
76
- status_code=400,
77
- content=response,
78
- )
79
- return response.__dict__
80
-
81
-
82
- @mikrotik_router.post("/users/{phone}/enable", response_model=UserStatusResponse)
83
- async def enable_user(
84
- phone: str = Path(..., description="User phone number"),
85
- mikrotik: MikrotikAPI = Depends(get_mikrotik),
86
- ):
87
- """Enable a hotspot user"""
88
- response = mikrotik.set_user_status(phone, disabled=False)
89
- if not response.code == 200:
90
- return JSONResponse(
91
- status_code=400,
92
- content=response.__dict__,
93
- )
94
- return response.__dict__
95
-
96
-
97
- @mikrotik_router.get("/users/active", response_model=ActiveUsersResponse)
98
- async def get_active_users(mikrotik: MikrotikAPI = Depends(get_mikrotik)):
99
- """Get list of active hotspot users"""
100
- response = mikrotik.get_active_users()
101
- print(response)
102
- if not response.code == 200:
103
- return JSONResponse(
104
- status_code=400,
105
- content=response.__dict__,
106
- )
107
- return response
108
-
109
-
110
- @mikrotik_router.get("/users/stats", response_model=UserStatsResponse)
111
- async def get_users_stats(
112
- phone: Optional[str] = None, mikrotik: MikrotikAPI = Depends(get_mikrotik)
113
- ):
114
- """Get user statistics"""
115
- response = mikrotik.get_user_stats(phone)
116
- if not response.code == 200:
117
- return JSONResponse(
118
- status_code=400,
119
- content=response.__dict__,
120
- )
121
- return response.__dict__
122
-
123
-
124
- @mikrotik_router.get("/users")
125
- async def get_users_list(mikrotik: MikrotikAPI = Depends(get_mikrotik)):
126
- """Get list of all hotspot users"""
127
- response = mikrotik.get_users_list()
128
- if not response.code == 200:
129
- return JSONResponse(
130
- status_code=400,
131
- content=response.__dict__,
132
- )
133
- return response
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
App/Mikrotik/schema.py DELETED
@@ -1,246 +0,0 @@
1
- from pydantic import BaseModel, Field, constr
2
- from typing import Optional, List
3
- from datetime import datetime
4
- from pydantic import BaseModel, Field, validator
5
- from typing import Optional, List, Any, Dict
6
- from .utils.helperfx import PhoneValidator
7
-
8
- # Extend the existing schema
9
-
10
- # Constants for phone and MAC address patterns
11
- PHONE_PATTERN = r"^(?:\+255|0)\d{9}$"
12
- MAC_PATTERN = r"^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$"
13
-
14
-
15
- # Register User Request
16
- class RegisterUserRequest(BaseModel):
17
- name: str = Field(..., max_length=100)
18
- password: str = Field(..., max_length=100)
19
- phoneNumber: str = Field(
20
- ...,
21
- pattern=PHONE_PATTERN,
22
- description="Tanzanian phone number starting with +255 or 0 followed by 9 digits",
23
- )
24
- mac_address: str = Field(
25
- ..., pattern=MAC_PATTERN, description="MAC address in standard format"
26
- )
27
-
28
- def hash_password(self):
29
- self.password = pwd_context.hash(self.password)
30
-
31
- class Config:
32
- schema_extra = {
33
- "example": {
34
- "name": "John Doe",
35
- "password": "StrongPassword1!",
36
- "phoneNumber": "+255123456789",
37
- "mac_address": "00:1A:2B:3C:4D:5E",
38
- }
39
- }
40
-
41
-
42
- # Login User Request
43
- class LoginUserRequest(BaseModel):
44
- phoneNumber: str = Field(..., pattern=PHONE_PATTERN)
45
- password: str
46
- mac_address: str = Field(..., pattern=MAC_PATTERN)
47
-
48
-
49
- # User Response with session data and additional information
50
- class UserSessionData(BaseModel):
51
- phone: str
52
- mac_address: str
53
- ip_address: Optional[str] = None
54
- uptime: Optional[str] = None
55
- bytes_in: int = 0
56
- bytes_out: int = 0
57
- status: str # Active, Inactive, Disabled, etc.
58
- profile: str
59
- login_time: Optional[datetime] = None
60
- logout_time: Optional[datetime] = None
61
-
62
-
63
- # Extended response for listing active users and all users with detailed session data
64
- class ActiveUsersResponse(BaseModel):
65
- code: int
66
- message: str
67
- active_users: List[UserSessionData]
68
-
69
-
70
- class UsersListResponse(BaseModel):
71
- code: int
72
- message: str
73
- data: list
74
-
75
-
76
- # Add User Status Response to manage user enabling/disabling status
77
- class UserStatusResponse(BaseModel):
78
- code: int
79
- message: str
80
- user_status: str # Indicates whether the user is enabled or disabled
81
-
82
-
83
- # Base Response Schema for standardized responses
84
- class BaseResponse(BaseModel):
85
- code: int
86
- message: str
87
- payload: Optional[dict] = None
88
-
89
-
90
- # Login and Logout Responses with tracking for login/logout times
91
- class LoginResponse(BaseResponse):
92
- login_time: Optional[datetime] = None
93
-
94
-
95
- class LogoutResponse(BaseResponse):
96
- logout_time: Optional[datetime] = None
97
-
98
-
99
- # Forgot Password and Reset Password Requests for account management
100
- class ForgotPasswordRequest(BaseModel):
101
- phoneNumber: str = Field(..., pattern=PHONE_PATTERN)
102
-
103
-
104
- class ResetPasswordRequest(BaseModel):
105
- phoneNumber: str = Field(..., pattern=PHONE_PATTERN)
106
- new_password: str = Field(..., max_length=100)
107
-
108
-
109
- # User Statistics Response to get detailed statistics for specific or all users
110
- class UserStats(BaseModel):
111
- phone: str
112
- profile: str
113
- status: str
114
- uptime: str
115
- bytes_in: int
116
- bytes_out: int
117
-
118
-
119
- class UserListData(BaseModel):
120
- phoneNumber: str = Field(..., description="User phone number")
121
- profile: str = Field(..., description="User profile, e.g., bandwidth limit profile")
122
- status: str = Field(..., description="User status, either 'active' or 'inactive'")
123
- disabled: bool = Field(..., description="Whether the user is disabled")
124
- comment: Optional[str] = Field(
125
- None, description="Additional comments or notes for the user"
126
- )
127
- data_limit: Optional[int] = Field(
128
- None, description="Total data limit for the user in bytes"
129
- )
130
-
131
-
132
- class UserStatsResponse(BaseResponse):
133
- user_stats: List[UserStats]
134
-
135
-
136
- # Example of a response builder for structured success and error responses
137
- class ResponseBuilder:
138
- @staticmethod
139
- def success(response_type: BaseModel, message: str, data: dict = None):
140
- return response_type(code=200, message=message, payload=data)
141
-
142
- @staticmethod
143
- def error(response_type: BaseModel, message: str, error_details: str):
144
- return response_type(
145
- code=400, message=message, payload={"error": error_details}
146
- )
147
-
148
-
149
- # Shared Phone Number Validator
150
- class UserBase(BaseModel):
151
- phoneNumber: str = Field(..., description="User phone number")
152
-
153
- @validator("phoneNumber")
154
- def validate_phone(cls, phoneNumber):
155
- is_valid, formatted_phone = PhoneValidator.validate_and_format(phoneNumber)
156
- if not is_valid:
157
- raise ValueError("Invalid phone number format")
158
- return formatted_phone
159
-
160
-
161
- # Request Models
162
- class UserCreate(UserBase):
163
- password: str = Field(..., min_length=4, description="User password")
164
- profile: str = Field(default="2mbps_profile", description="User profile")
165
-
166
-
167
- class UserLogin(UserBase):
168
- password: str = Field(..., description="User password")
169
- mac_address: str = Field(..., description="Device MAC address")
170
- ip_address: str = Field(..., description="User's IP address")
171
-
172
-
173
- # Base API Response Model
174
- class ApiResponse(BaseModel):
175
- success: bool = True
176
- message: Optional[str] = Field("success", description="Response message")
177
- error: Optional[str] = None
178
- data: Optional[Dict[str, Any]] = None
179
-
180
-
181
- # Detailed User Models
182
- class UserParams(BaseModel):
183
- name: str
184
- password: str
185
- profile: str
186
- disabled: bool
187
-
188
-
189
- class ActiveUserData(BaseModel):
190
- phone: str
191
- uptime: str
192
- mac_address: str
193
- ip_address: str
194
- usage: UserStats
195
-
196
-
197
- class UserStatsData(BaseModel):
198
- phoneNumber: str
199
- profile: str
200
- status: str # Enabled or Disabled
201
- uptime: str
202
- usage: UserStats
203
-
204
-
205
- class UserListData(BaseModel):
206
- phoneNumber: str
207
- profile: str
208
- status: str # Active or Inactive
209
- disabled: bool
210
- comment: Optional[str] = ""
211
- data_limit: Optional[int] = None
212
-
213
-
214
- # Specific Response Models
215
- class RegisterResponse(ApiResponse):
216
- data: Optional[Dict[str, Any]] = Field(None, description="Registration details")
217
-
218
-
219
- class LoginResponse(ApiResponse):
220
- data: Optional[Dict[str, Any]] = Field(None, description="Login details")
221
-
222
-
223
- class LogoutResponse(ApiResponse):
224
- data: Optional[Dict[str, Any]] = Field(None, description="Logout details")
225
-
226
-
227
- class UserStatusResponse(ApiResponse):
228
- data: Optional[Dict[str, Any]] = Field(
229
- None, description="User status change details"
230
- )
231
-
232
-
233
- class ActiveUsersResponse(ApiResponse):
234
- active_users: List[UserSessionData]
235
-
236
-
237
- class UserStatsResponse(ApiResponse):
238
- data: Optional[Dict[str, List[UserStatsData]]] = Field(
239
- None, description="Statistics for users"
240
- )
241
-
242
-
243
- class UsersListResponse(ApiResponse):
244
- data: Optional[Dict[str, List[UserListData]]] = Field(
245
- None, description="List of all users with details"
246
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
App/Mikrotik/utils/Config.py DELETED
@@ -1,10 +0,0 @@
1
- from dataclasses import dataclass
2
-
3
-
4
- @dataclass
5
- class RouterConfig:
6
- """Class to store router configuration settings"""
7
-
8
- host: str = "10.8.0.6"
9
- username: str = "admin"
10
- password: str = "G4TZ7QFJTW"
 
 
 
 
 
 
 
 
 
 
 
App/Mikrotik/utils/api.py DELETED
@@ -1,426 +0,0 @@
1
- from librouteros import connect
2
- from librouteros.query import Key
3
- import logging
4
- from typing import Optional
5
- from datetime import datetime
6
- from ..schema import *
7
- from ..schema import UserListData
8
- from .helperfx import PhoneValidator
9
- from .Config import RouterConfig
10
- from time import sleep
11
-
12
-
13
- class MikrotikAPI:
14
- """Class to handle MikroTik router API operations, inherits VPNManager."""
15
-
16
- def __init__(
17
- self,
18
- config: RouterConfig,
19
- ):
20
- """Initialize with router configuration and VPN configuration."""
21
- self.config = config
22
- self.api = None
23
- self.logger = self._setup_logging()
24
- self.phone_validator = PhoneValidator()
25
-
26
- def connect(self) -> bool:
27
- """Establish connection to VPN and MikroTik router."""
28
-
29
- try:
30
- self.api = connect(
31
- username=self.config.username,
32
- password=self.config.password,
33
- host=self.config.host,
34
- port=8728,
35
- )
36
- self.logger.info("Successfully connected to MikroTik router.")
37
- return True
38
- except Exception as e:
39
- self.logger.error(f"Failed to connect to MikroTik router: {e}")
40
- return False
41
-
42
- def close(self):
43
- """Close the router connection and disconnect VPN."""
44
- if self.api:
45
- self.api.close()
46
- self.logger.info("Router connection closed.")
47
-
48
- def add_hotspot_user(
49
- self, phone: str, password: str, profile: str = "default"
50
- ) -> BaseResponse:
51
- """Add a hotspot user to the router"""
52
- if not self.api:
53
- return ResponseBuilder.error(
54
- BaseResponse, "Registration failed", "Not connected to router"
55
- )
56
-
57
- # Validate phone number
58
- is_valid, formatted_phone = self.phone_validator.validate_and_format(phone)
59
- if not is_valid:
60
- return ResponseBuilder.error(
61
- BaseResponse,
62
- "Registration failed",
63
- f"Invalid phone number format: {phone}",
64
- )
65
-
66
- users = self.api.path("ip", "hotspot", "user")
67
- existing_users = users.select(Key("name"))
68
-
69
- # Check if user already exists
70
- if any(str(user["name"]) == formatted_phone for user in existing_users):
71
- return ResponseBuilder.error(
72
- BaseResponse,
73
- "Registration failed",
74
- f"User {formatted_phone} already exists",
75
- )
76
-
77
- try:
78
- # Add user to router
79
- users.add(
80
- name=formatted_phone,
81
- password=password,
82
- profile=profile,
83
- disabled=True,
84
- )
85
- return ResponseBuilder.success(BaseResponse, "User registered successfully")
86
-
87
- except Exception as e:
88
- self.logger.error(f"Failed to add user {formatted_phone}: {e}")
89
- return ResponseBuilder.error(BaseResponse, "Registration failed", str(e))
90
-
91
- def login_hotspot_user(
92
- self, phone: str, password: str, mac_address: str, ip_address: str
93
- ) -> LoginResponse:
94
- """Login a hotspot user"""
95
- if not self.api:
96
- return ResponseBuilder.error(
97
- LoginResponse, "Login failed", "Not connected to router"
98
- )
99
-
100
- is_valid, formatted_phone = self.phone_validator.validate_and_format(phone)
101
- if not is_valid:
102
- return ResponseBuilder.error(
103
- LoginResponse, "Login failed", f"Invalid phone number format: {phone}"
104
- )
105
-
106
- try:
107
- users = self.api.path("ip", "hotspot", "user")
108
- active = self.api.path("ip", "hotspot", "active")
109
-
110
- # Verify if user exists and password is correct
111
- existing_users = users.select(Key("name"), Key("password"), Key("profile"))
112
- user_data = next(
113
- (
114
- user
115
- for user in existing_users
116
- if str(user["name"]) == formatted_phone
117
- and user["password"] == password
118
- ),
119
- None,
120
- )
121
-
122
- if not user_data:
123
- return ResponseBuilder.error(
124
- LoginResponse, "Login failed", "Invalid credentials"
125
- )
126
-
127
- # Check if already logged in
128
- active_users = active.select(Key("user"))
129
- if any(str(user["user"]) == formatted_phone for user in active_users):
130
- return ResponseBuilder.error(
131
- LoginResponse,
132
- "Login failed",
133
- f"User {formatted_phone} is already logged in",
134
- )
135
-
136
- # Perform the login
137
- self.api.path("ip", "hotspot", "host").call(
138
- "login",
139
- user=formatted_phone,
140
- password=password,
141
- mac_address=mac_address,
142
- ip_address=ip_address,
143
- )
144
- login_time = datetime.now()
145
- return LoginResponse(
146
- code=200, message="Login successful", login_time=login_time
147
- )
148
-
149
- except Exception as e:
150
- self.logger.error(f"Login failed for {formatted_phone}: {e}")
151
- return ResponseBuilder.error(LoginResponse, "Login failed", str(e))
152
-
153
- def logout_hotspot_user(self, phone: str) -> LogoutResponse:
154
- """Logout a hotspot user"""
155
- if not self.api:
156
- return ResponseBuilder.error(
157
- LogoutResponse, "Logout failed", "Not connected to router"
158
- )
159
-
160
- is_valid, formatted_phone = self.phone_validator.validate_and_format(phone)
161
- if not is_valid:
162
- return ResponseBuilder.error(
163
- LogoutResponse, "Logout failed", f"Invalid phone number format: {phone}"
164
- )
165
-
166
- active = self.api.path("ip", "hotspot", "active")
167
-
168
- try:
169
- active_users = active.select(Key("user"), Key(".id"))
170
- user_session = next(
171
- (
172
- session
173
- for session in active_users
174
- if str(session["user"]) == formatted_phone
175
- ),
176
- None,
177
- )
178
-
179
- if not user_session:
180
- return ResponseBuilder.error(
181
- LogoutResponse,
182
- "Logout failed",
183
- f"User {formatted_phone} is not logged in",
184
- )
185
-
186
- # Logout the user
187
- active.remove(user_session[".id"])
188
- logout_time = datetime.now()
189
- return LogoutResponse(
190
- code=200, message="Logout successful", logout_time=logout_time
191
- )
192
-
193
- except Exception as e:
194
- self.logger.error(f"Logout failed for user {formatted_phone}: {e}")
195
- return ResponseBuilder.error(LogoutResponse, "Logout failed", str(e))
196
-
197
- def get_active_users(self) -> ActiveUsersResponse:
198
- """Get list of active hotspot users"""
199
- if not self.api:
200
- return ResponseBuilder.error(
201
- ActiveUsersResponse,
202
- "Failed to get active users",
203
- "Not connected to router",
204
- )
205
-
206
- try:
207
- active = self.api.path("ip", "hotspot", "active")
208
- active_users = active.select(
209
- Key("user"),
210
- Key("uptime"),
211
- Key("mac-address"),
212
- Key("address"),
213
- Key("bytes-in"),
214
- Key("bytes-out"),
215
- # Key("profile"),
216
- )
217
-
218
- users_list = [
219
- UserSessionData(
220
- phone=str(user["user"]),
221
- uptime=user["uptime"],
222
- mac_address=user["mac-address"],
223
- ip_address=user["address"],
224
- bytes_in=int(user["bytes-in"]),
225
- bytes_out=int(user["bytes-out"]),
226
- status="active",
227
- profile=str("2mbps_profile"),
228
- )
229
- for user in active_users
230
- ]
231
- print(users_list)
232
- return ActiveUsersResponse(
233
- code=200,
234
- message="Active users retrieved successfully",
235
- active_users=users_list,
236
- )
237
-
238
- except Exception as e:
239
- self.logger.error(f"Failed to get active users: {e}")
240
- return ResponseBuilder.error(
241
- ActiveUsersResponse, "Failed to get active users", str(e)
242
- )
243
-
244
- def get_user_stats(self, phone: Optional[str] = None) -> UserStatsResponse:
245
- """Get statistics for all users or a specific user"""
246
- if not self.api:
247
- return ResponseBuilder.error(
248
- UserStatsResponse,
249
- "Failed to get user statistics",
250
- "Not connected to router",
251
- )
252
-
253
- try:
254
- users = self.api.path("ip", "hotspot", "user")
255
- all_users = users.select(
256
- Key("name"),
257
- Key("profile"),
258
- Key("disabled"),
259
- Key("uptime"),
260
- Key("bytes-in"),
261
- Key("bytes-out"),
262
- )
263
-
264
- user_stats = [
265
- UserStats(
266
- phone=str(user["name"]),
267
- profile=user.get("profile", "default"),
268
- status=(
269
- "disabled"
270
- if user.get("disabled", "false") == "true"
271
- else "enabled"
272
- ),
273
- uptime=user.get("uptime", "0s"),
274
- bytes_in=int(user.get("bytes-in", 0)),
275
- bytes_out=int(user.get("bytes-out", 0)),
276
- )
277
- for user in all_users
278
- if not phone or str(user["name"]) == phone
279
- ]
280
-
281
- if phone and not user_stats:
282
- return ResponseBuilder.error(
283
- UserStatsResponse, "User not found", f"User {phone} does not exist"
284
- )
285
-
286
- return UserStatsResponse(
287
- code=200,
288
- message="User statistics retrieved successfully",
289
- user_stats=user_stats,
290
- )
291
-
292
- except Exception as e:
293
- self.logger.error(f"Failed to get user statistics: {e}")
294
- return ResponseBuilder.error(
295
- UserStatsResponse, "Failed to get user statistics", str(e)
296
- )
297
-
298
- # Existing initialization, connect, and other methods...
299
-
300
- def set_user_status(self, phone: str, disabled: bool) -> UserStatusResponse:
301
- """Enable or disable a hotspot user."""
302
- if not self.api:
303
- return ResponseBuilder.error(
304
- UserStatusResponse, "Status update failed", "Not connected to router"
305
- )
306
-
307
- # Validate phone number
308
- is_valid, formatted_phone = self.phone_validator.validate_and_format(phone)
309
- if not is_valid:
310
- return ResponseBuilder.error(
311
- UserStatusResponse,
312
- "Status update failed",
313
- f"Invalid phone number format: {phone}",
314
- )
315
- users = self.api.path("ip", "hotspot", "user")
316
-
317
- try:
318
- # Find the user
319
- existing_users = users.select(
320
- Key("name"),
321
- Key(".id"),
322
- Key("mbonea"),
323
- )
324
- for user in existing_users:
325
- if str(user["name"]) == formatted_phone:
326
- user_data = user
327
- break
328
-
329
- if not user_data:
330
- return ResponseBuilder.error(
331
- UserStatusResponse,
332
- "Status update failed",
333
- f"User {formatted_phone} does not exist",
334
- )
335
-
336
- user_id = user_data[".id"]
337
- status_value = "true" if disabled else "false"
338
- users.update(**{".id": user_id, "disabled": status_value})
339
- status = "disabled" if disabled else "enabled"
340
- self.logger.info(f"User {formatted_phone} successfully {status}")
341
-
342
- # If disabling the user, log them out of any active session
343
- if disabled:
344
- self.logout_hotspot_user(formatted_phone)
345
-
346
- return UserStatusResponse(
347
- code=200, message=f"User successfully {status}", user_status=status
348
- )
349
-
350
- except Exception as e:
351
- print(f"Failed to update status for user {formatted_phone}: {e}")
352
- self.logger.error(
353
- f"Failed to update status for user {formatted_phone}: {e}"
354
- )
355
- return ResponseBuilder.error(
356
- UserStatusResponse, "Status update failed", str(e)
357
- )
358
-
359
- def activate_user(self, phone: str) -> UserStatusResponse:
360
- """Activate a user (set disabled to false)."""
361
- return self.set_user_status(phone, disabled=False)
362
-
363
- def deactivate_user(self, phone: str) -> UserStatusResponse:
364
- """Deactivate a user (set disabled to true and log them out)."""
365
- return self.set_user_status(phone, disabled=True)
366
-
367
- def get_users_list(self) -> UsersListResponse:
368
- """Get list of all hotspot users (both active and inactive)"""
369
- if not self.api:
370
- return UsersListResponse(
371
- success=False,
372
- message="Failed to get users list",
373
- error="Not connected to router",
374
- )
375
-
376
- try:
377
- users = self.api.path("ip", "hotspot", "user")
378
- active = self.api.path("ip", "hotspot", "active")
379
-
380
- # Retrieve all users
381
- all_users = users.select(
382
- Key("name"),
383
- Key("profile"),
384
- Key("disabled"),
385
- Key("comment"),
386
- Key("limit-bytes-total"),
387
- Key("bytes-in"),
388
- Key("bytes-out"),
389
- )
390
-
391
- # Retrieve active users to check statuses
392
- active_users = active.select(Key("user"))
393
- active_phones = [str(user.get("user", "")) for user in active_users]
394
-
395
- # Format the response
396
- users_list = []
397
- for user in all_users:
398
- phone_number = str(user["name"])
399
- bytes_in = int(user.get("bytes-in", 0))
400
- bytes_out = int(user.get("bytes-out", 0))
401
- data_limit = (
402
- int(user.get("limit-bytes-total", 0))
403
- if user.get("limit-bytes-total")
404
- else None
405
- )
406
-
407
- user_data = UserListData(
408
- phoneNumber=phone_number,
409
- profile=user.get("profile", "default"),
410
- status="active" if phone_number in active_phones else "inactive",
411
- disabled=user.get("disabled", "false") == "true",
412
- comment=user.get("comment", ""),
413
- data_limit=data_limit,
414
- )
415
- users_list.append(user_data)
416
-
417
- return UsersListResponse(
418
- code=200,
419
- message=f"Retrieved {len(users_list)} users",
420
- data=users,
421
- )
422
-
423
- except Exception as e:
424
- return UsersListResponse(
425
- success=False, message="Failed to get users list", error=str(e)
426
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
App/Mikrotik/utils/client1-working.ovpn DELETED
@@ -1,152 +0,0 @@
1
- client
2
- dev tun
3
- proto tcp
4
- remote 3.76.188.80 1194
5
- resolv-retry infinite
6
- nobind
7
- persist-key
8
- persist-tun
9
- remote-cert-tls server
10
- auth SHA1
11
- cipher AES-256-CBC
12
- data-ciphers AES-256-CBC
13
- verb 3
14
-
15
- <ca>
16
- -----BEGIN CERTIFICATE-----
17
- MIIDSzCCAjOgAwIBAgIURVMIu580Y2d7neve8yFnlspmVSkwDQYJKoZIhvcNAQEL
18
- BQAwFjEUMBIGA1UEAwwLRWFzeS1SU0EgQ0EwHhcNMjQxMTIzMTYwODUzWhcNMzQx
19
- MTIxMTYwODUzWjAWMRQwEgYDVQQDDAtFYXN5LVJTQSBDQTCCASIwDQYJKoZIhvcN
20
- AQEBBQADggEPADCCAQoCggEBANcRjNoLIaWYkXXd1Th/24sXAkse4xK6FMUqaV6Y
21
- gM8WPaepF4muyYrwDFgC+IloicFKqtfle5qL02MH16UqIJpNvulgMSgp4YwgzEtV
22
- FZY4mzvE4ZjMc54tW8gf22R+iMFPumRnMtZuPq4U8i5lpKcZ1X5V7X/0Sc2aPbwO
23
- LckbWkZhZdoVbJO8XhBo133Cur0FMnfEA/9dlrQ5ZMpj7xdO6Kpz/scmtw4JQ36q
24
- mAF77LYYogiA/QvyxBAXv1UoElucIdIgMFQ8PiwBKi+FfShHlVr+m5OLlNN71eoC
25
- N6TUOqtT4o3hLGGK31YWjoeSIJtvF251G+c3V99FecORdL0CAwEAAaOBkDCBjTAM
26
- BgNVHRMEBTADAQH/MB0GA1UdDgQWBBRm5QQIqG+pv1VNdHI6tyuTz44HjzBRBgNV
27
- HSMESjBIgBRm5QQIqG+pv1VNdHI6tyuTz44Hj6EapBgwFjEUMBIGA1UEAwwLRWFz
28
- eS1SU0EgQ0GCFEVTCLufNGNne53r3vMhZ5bKZlUpMAsGA1UdDwQEAwIBBjANBgkq
29
- hkiG9w0BAQsFAAOCAQEAIYaeBqeDLmdcqNZYP5Sf5h7NN32R9+MHkP2ZQ/MAgxjy
30
- Ozr26L21t5jvuEw6qr+kix4pEnWnud9xTBeaTsO3TLjSrp0J0o5KBuwlXmFw1hr8
31
- L8nJ52FlqpTv2FvhCLX5L+f5i6tL4Q5bC3AlD17eM1RWza6969NaAc6Gzl9I1sgj
32
- 3WHyKw6bnx4NTGyjCBnDH+gUK9htzVIpr31b3fEySg8U6eYbvDlX9jDZjV6Mnjrw
33
- DvuiOZONMcmzlZvUTvHoOQHq5yIl8AG0CtaTd5YT2ksmMw18m6K/mMIwzzgENIiI
34
- Zdf9tUqqzy+ckHTcCM8Bx0x0MUXsWzWJTs8pHmWYTQ==
35
- -----END CERTIFICATE-----
36
- </ca>
37
- <cert>
38
- Certificate:
39
- Data:
40
- Version: 3 (0x2)
41
- Serial Number:
42
- 59:94:39:5f:89:05:2e:c7:bd:d4:8b:da:b3:2c:88:24
43
- Signature Algorithm: sha256WithRSAEncryption
44
- Issuer: CN=Easy-RSA CA
45
- Validity
46
- Not Before: Nov 23 16:09:16 2024 GMT
47
- Not After : Feb 26 16:09:16 2027 GMT
48
- Subject: CN=client1
49
- Subject Public Key Info:
50
- Public Key Algorithm: rsaEncryption
51
- Public-Key: (2048 bit)
52
- Modulus:
53
- 00:c7:2e:e4:e9:41:1e:2f:47:8f:3f:a5:59:d4:d2:
54
- 47:04:46:2b:fb:47:91:ce:5c:9d:94:6a:a6:99:28:
55
- 1d:73:ec:56:ad:07:7e:5c:42:ba:15:2d:f9:f9:dd:
56
- 67:d8:a4:ae:47:bb:21:db:de:9a:69:8b:fe:8d:8d:
57
- 91:dd:90:12:98:16:c6:e5:e5:36:85:4d:e0:f0:31:
58
- 07:bd:22:0b:c3:ad:07:d1:91:98:07:38:a9:b6:db:
59
- 0f:c8:a1:ed:16:2e:ff:f4:f1:d3:b8:ea:a4:44:f5:
60
- 2c:21:64:c5:b0:7c:6a:87:2d:28:3d:03:71:d1:12:
61
- 9d:be:9c:ed:e9:f8:fb:de:de:8e:d2:2d:39:de:f1:
62
- 23:36:a3:b1:74:03:97:61:db:90:bc:04:26:94:61:
63
- d9:ee:62:7b:a7:d3:b7:f5:e8:f6:5d:e4:8b:0d:bf:
64
- f9:cc:bb:f9:32:b2:2d:05:05:4c:29:cf:fb:89:5e:
65
- dd:40:74:66:5d:04:16:7a:85:31:e2:ce:47:83:9c:
66
- ff:72:8c:26:24:c2:94:9e:6c:6c:ce:fb:b2:9d:07:
67
- dd:58:39:7b:05:fe:f5:0e:cd:98:97:9f:d9:c2:de:
68
- a8:08:59:4a:c9:d2:c9:4f:b8:b3:69:1e:84:8c:e4:
69
- d4:41:5a:2a:7f:43:13:04:b4:be:ae:ca:26:bb:a6:
70
- 9b:33
71
- Exponent: 65537 (0x10001)
72
- X509v3 extensions:
73
- X509v3 Basic Constraints:
74
- CA:FALSE
75
- X509v3 Subject Key Identifier:
76
- 83:3E:E4:7C:7D:93:EE:E0:20:2F:CF:FB:E0:D4:90:D9:C5:26:5D:04
77
- X509v3 Authority Key Identifier:
78
- keyid:66:E5:04:08:A8:6F:A9:BF:55:4D:74:72:3A:B7:2B:93:CF:8E:07:8F
79
- DirName:/CN=Easy-RSA CA
80
- serial:45:53:08:BB:9F:34:63:67:7B:9D:EB:DE:F3:21:67:96:CA:66:55:29
81
- X509v3 Extended Key Usage:
82
- TLS Web Client Authentication
83
- X509v3 Key Usage:
84
- Digital Signature
85
- Signature Algorithm: sha256WithRSAEncryption
86
- Signature Value:
87
- ba:36:55:f9:d6:9c:3f:81:ce:4f:27:36:8c:d6:d3:df:69:f9:
88
- b7:e7:34:85:33:4b:c8:71:24:63:64:10:a8:69:6a:02:78:0f:
89
- 8c:c4:81:df:8b:70:c3:75:9b:77:00:48:bd:5d:0b:91:6a:bc:
90
- 13:c1:af:99:4c:ab:59:81:05:ca:89:9b:1d:1c:94:b0:3a:f6:
91
- 27:da:be:7b:f8:7f:6a:7b:37:01:f3:f6:9f:35:73:25:54:72:
92
- b7:de:ae:a5:a5:98:0b:84:14:60:03:34:d3:d2:e6:07:8b:bb:
93
- 2c:65:50:4f:2a:07:b5:94:96:3e:58:e4:b7:c2:52:56:b6:9e:
94
- 23:e4:56:bb:e2:59:ba:38:c8:5d:f0:ac:21:ab:f0:4a:83:ef:
95
- 7f:a4:b0:4f:0a:22:fc:c7:aa:9f:47:28:2f:7f:70:e9:4e:12:
96
- 36:7d:f6:e3:97:6a:5f:90:7e:fc:91:26:8f:0f:1b:d0:85:a2:
97
- 6e:ed:b8:1b:00:8d:67:1b:0d:06:a9:a5:d1:4f:2b:4c:f5:ac:
98
- 8e:4f:6b:18:0e:7d:77:1a:45:7b:79:f0:ca:8b:e8:23:0e:aa:
99
- e8:3e:14:3e:e5:ba:11:b7:74:a4:6c:89:af:b3:f7:1d:af:6d:
100
- 9b:69:30:97:6a:28:af:05:ae:4c:d8:d0:c3:53:cd:57:56:33:
101
- 8f:ae:e9:04
102
- -----BEGIN CERTIFICATE-----
103
- MIIDVTCCAj2gAwIBAgIQWZQ5X4kFLse91IvasyyIJDANBgkqhkiG9w0BAQsFADAW
104
- MRQwEgYDVQQDDAtFYXN5LVJTQSBDQTAeFw0yNDExMjMxNjA5MTZaFw0yNzAyMjYx
105
- NjA5MTZaMBIxEDAOBgNVBAMMB2NsaWVudDEwggEiMA0GCSqGSIb3DQEBAQUAA4IB
106
- DwAwggEKAoIBAQDHLuTpQR4vR48/pVnU0kcERiv7R5HOXJ2UaqaZKB1z7FatB35c
107
- QroVLfn53WfYpK5HuyHb3pppi/6NjZHdkBKYFsbl5TaFTeDwMQe9IgvDrQfRkZgH
108
- OKm22w/Ioe0WLv/08dO46qRE9SwhZMWwfGqHLSg9A3HREp2+nO3p+Pve3o7SLTne
109
- 8SM2o7F0A5dh25C8BCaUYdnuYnun07f16PZd5IsNv/nMu/kysi0FBUwpz/uJXt1A
110
- dGZdBBZ6hTHizkeDnP9yjCYkwpSebGzO+7KdB91YOXsF/vUOzZiXn9nC3qgIWUrJ
111
- 0slPuLNpHoSM5NRBWip/QxMEtL6uyia7ppszAgMBAAGjgaIwgZ8wCQYDVR0TBAIw
112
- ADAdBgNVHQ4EFgQUgz7kfH2T7uAgL8/74NSQ2cUmXQQwUQYDVR0jBEowSIAUZuUE
113
- CKhvqb9VTXRyOrcrk8+OB4+hGqQYMBYxFDASBgNVBAMMC0Vhc3ktUlNBIENBghRF
114
- Uwi7nzRjZ3ud697zIWeWymZVKTATBgNVHSUEDDAKBggrBgEFBQcDAjALBgNVHQ8E
115
- BAMCB4AwDQYJKoZIhvcNAQELBQADggEBALo2VfnWnD+Bzk8nNozW099p+bfnNIUz
116
- S8hxJGNkEKhpagJ4D4zEgd+LcMN1m3cASL1dC5FqvBPBr5lMq1mBBcqJmx0clLA6
117
- 9ifavnv4f2p7NwHz9p81cyVUcrferqWlmAuEFGADNNPS5geLuyxlUE8qB7WUlj5Y
118
- 5LfCUla2niPkVrviWbo4yF3wrCGr8EqD73+ksE8KIvzHqp9HKC9/cOlOEjZ99uOX
119
- al+QfvyRJo8PG9CFom7tuBsAjWcbDQappdFPK0z1rI5PaxgOfXcaRXt58MqL6CMO
120
- qug+FD7luhG3dKRsia+z9x2vbZtpMJdqKK8FrkzY0MNTzVdWM4+u6QQ=
121
- -----END CERTIFICATE-----
122
- </cert>
123
- <key>
124
- -----BEGIN PRIVATE KEY-----
125
- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHLuTpQR4vR48/
126
- pVnU0kcERiv7R5HOXJ2UaqaZKB1z7FatB35cQroVLfn53WfYpK5HuyHb3pppi/6N
127
- jZHdkBKYFsbl5TaFTeDwMQe9IgvDrQfRkZgHOKm22w/Ioe0WLv/08dO46qRE9Swh
128
- ZMWwfGqHLSg9A3HREp2+nO3p+Pve3o7SLTne8SM2o7F0A5dh25C8BCaUYdnuYnun
129
- 07f16PZd5IsNv/nMu/kysi0FBUwpz/uJXt1AdGZdBBZ6hTHizkeDnP9yjCYkwpSe
130
- bGzO+7KdB91YOXsF/vUOzZiXn9nC3qgIWUrJ0slPuLNpHoSM5NRBWip/QxMEtL6u
131
- yia7ppszAgMBAAECggEAIF40gNs+JnzAgJ1EPdt2AvHMT+dPgHN4gBfcvuLP9nif
132
- lTq0hBWr26k/CCW8rG4GjE2SsQI5oZFIaoRpAdJZ0zFQXSekdoEzXpT5JvkTZFcI
133
- ADxisjm5CqgKppX5yzMUESADQfePfk1BQKP5pDZzsUfbVB7tLgaSb9lcqDr34z2J
134
- yf2Ref6dIL5BKdjwQm/fOzeRbjio6bXHyJ6fxrPUPjGt7GXRBkOrofXJJ7bb/x4L
135
- ND6cClKSiM+jd/i/sOapNiqceqIWvmlvLl5hLJVIQUz505io9S9f8fv1gMy3hVZ6
136
- j7P/LgJWlDRHDLhMgqTkNr1vz5gPnjx/kiOEROe40QKBgQD4czLuB7Fm3FqsjYkD
137
- hvgBUji9Y6GbQM91RfXlRcvhIYwFmBfnl7Z2mIUeCwLLWO5Vzc9lvkKF/27qnls4
138
- pr+GMaVmpmlcL7DKPSrrm5K/8cPfln2TezHn64tIjbFi8xQWw1XLEv4i2ndgSEyV
139
- MqEen77g0Hc+2pPigVQZmkzKXQKBgQDNPG1aUmpNprECA2rnCj1fXCuLqlSsbLRO
140
- GcrGltXot7VZ/3IxW5vwjxWqzNGeJG9deAT0oC3RSOf/fv05R3FnEDRleKnxajlq
141
- CseAQ6X7sXAMjNb1GmSzfjguXjOvQCSRBlu+Wx1r/FNe7kK82d7Hedtb1JYZh6G0
142
- vZy98L9CzwKBgQC2QHNMzxHgvaY6S/0FPF3zQihjLZHf/JPymCaAUEn11RENDXwD
143
- pHPx3YJQ/ozHNG5pPPd10DKmbzEjJJUQIqn+O670dQB24nkScfppKQ9mhGhGPPPT
144
- WxzJ3yymRWKpjlzfMd1egYkxcgb99ytOivxMJaz055eB4P94uZxCx8Cq9QKBgAri
145
- rZogzOqZcMH+lGj0rhSkutqJijwq99U8oPivf2D8fW3sko3zoe28aRXKD0QoApAe
146
- kYS4CjYTe9qdTakAFQ+2WFEZeUoIrErnj3VKIT+cRakkvzH42GZ8x1YOQQeGi2n1
147
- wF/0TTcxBur+ECQcGijSWcQhHmT0QKtpcyrP3hUZAoGAW8NqevJyg3PDQI9kbShn
148
- c08sVwV6jHniAnQHTNPKQ/KR8fzO0PS8B7eaImD4ZJOKjIAp5AfmKpwM4dvWl/i4
149
- Vs1gY9eHFkDfxDDoCbcn0R6tPMR2LeqPbqZvhEyJaDVEd6Lh/Y9X7PQhhONCityJ
150
- DhsXoTwnE70b3QezzBi6yk0=
151
- -----END PRIVATE KEY-----
152
- </key>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
App/Mikrotik/utils/helperfx.py DELETED
@@ -1,75 +0,0 @@
1
- from .api import MikrotikAPI
2
- from .Config import RouterConfig
3
- from fastapi import HTTPException
4
-
5
- import re
6
- from typing import Tuple
7
-
8
-
9
- class PhoneValidator:
10
- """Validator for Tanzanian phone numbers"""
11
-
12
- # Valid Tanzanian mobile operator prefixes
13
- VALID_PREFIXES = [
14
- "071",
15
- "074",
16
- "075",
17
- "076",
18
- "077", # Vodacom
19
- "068",
20
- "069", # Airtel
21
- "065",
22
- "067", # Tigo
23
- "078",
24
- "079", # TTCL
25
- "073", # Zantel
26
- "061",
27
- "062", # Halotel
28
- ]
29
-
30
- @staticmethod
31
- def validate_and_format(phone: str) -> Tuple[bool, str]:
32
- """
33
- Validates and formats Tanzanian phone numbers.
34
- Returns (is_valid, formatted_number)
35
-
36
- Valid format:
37
- - 255712345678 (12 digits starting with 255)
38
- """
39
- # Remove any spaces or special characters
40
- phone = re.sub(r"[\s\-\(\)]", "", phone)
41
-
42
- # Convert all formats to 255 format
43
- if phone.startswith("+255"):
44
- phone = "255" + phone[4:]
45
- elif phone.startswith("0"):
46
- phone = "255" + phone[1:]
47
- elif not phone.startswith("255"):
48
- return False, ""
49
-
50
- # Check if it matches the basic pattern (12 digits starting with 255)
51
- if not re.match(r"^255\d{9}$", phone):
52
- return False, ""
53
-
54
- # Check if the prefix is valid (check the digits after 255)
55
- prefix = "0" + phone[3:5]
56
- if prefix not in PhoneValidator.VALID_PREFIXES:
57
- return False, ""
58
-
59
- return True, phone
60
-
61
-
62
- # Dependency to get MikrotikAPI instance
63
- def get_mikrotik():
64
- config = RouterConfig()
65
- mikrotik = MikrotikAPI(config)
66
- try:
67
- if mikrotik.connect():
68
- yield mikrotik
69
- else:
70
- raise HTTPException(
71
- status_code=503,
72
- detail={"success": False, "message": "Could not connect to router"},
73
- )
74
- finally:
75
- mikrotik.close()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
App/app.py CHANGED
@@ -10,7 +10,6 @@ from .Plans.PlanRoutes import plan_router
10
  from .Portals.PortalRoutes import portal_router
11
  from .Metrics.MetricsRoutes import metrics_router
12
  from .Messages.MessagesRoute import message_router
13
- from .Mikrotik.mikrotikRoutes import mikrotik_router
14
 
15
  # from .Subscriptions.background_tasks import check_expiring_subscriptions
16
  import asyncio, os
@@ -24,42 +23,17 @@ app = FastAPI()
24
  # Configure CORS to allow all origins
25
  app.add_middleware(
26
  CORSMiddleware,
27
- allow_origins=[
28
- "http://localhost:3000", # Localhost for development
29
- "https://captive-hotspot.vercel.app", # Your production domain
30
- ], # Allows all origins
31
  allow_credentials=True, # Allows cookies and authentication headers
32
  allow_methods=["*"], # Allows all HTTP methods (GET, POST, PUT, DELETE, etc.)
33
  allow_headers=["*"], # Allows all headers
34
  )
35
 
36
 
37
- async def connect_to_vpn():
38
- """Connect to the VPN on startup."""
39
- try:
40
- # Replace 'your_vpn_command' with the actual command to connect to your VPN
41
- # For example, if using OpenVPN:
42
- command = [
43
- "openvpn",
44
- "--config",
45
- f"{os.path.dirname(__file__)}/Mikrotik/utils/client1-working.ovpn",
46
- "--dev",
47
- "tun", # Simulate device without needing /dev/net/tun
48
- ]
49
-
50
- print(f"{os.path.dirname(__file__)}/Mikrotik/utils/client1-working.ovpn")
51
- process = subprocess.Popen(command)
52
- await asyncio.sleep(5) # Wait for a few seconds to ensure the VPN connects
53
- logging.info("VPN connected successfully.")
54
- except Exception as e:
55
- logging.error(f"Failed to connect to VPN: {str(e)}")
56
-
57
-
58
  @app.on_event("startup")
59
  async def startup_event():
60
  await Tortoise.init(config=TORTOISE_ORM)
61
  await Tortoise.generate_schemas()
62
- await connect_to_vpn() # Connect to VPN on startup
63
 
64
 
65
  @app.get("/")
@@ -76,7 +50,6 @@ app.include_router(plan_router)
76
  app.include_router(portal_router)
77
  app.include_router(metrics_router)
78
  app.include_router(message_router)
79
- app.include_router(mikrotik_router)
80
  # if __name__ == "__main__":
81
  # import uvicorn
82
 
 
10
  from .Portals.PortalRoutes import portal_router
11
  from .Metrics.MetricsRoutes import metrics_router
12
  from .Messages.MessagesRoute import message_router
 
13
 
14
  # from .Subscriptions.background_tasks import check_expiring_subscriptions
15
  import asyncio, os
 
23
  # Configure CORS to allow all origins
24
  app.add_middleware(
25
  CORSMiddleware,
26
+ allow_origins=["*"], # Allows all origins
 
 
 
27
  allow_credentials=True, # Allows cookies and authentication headers
28
  allow_methods=["*"], # Allows all HTTP methods (GET, POST, PUT, DELETE, etc.)
29
  allow_headers=["*"], # Allows all headers
30
  )
31
 
32
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  @app.on_event("startup")
34
  async def startup_event():
35
  await Tortoise.init(config=TORTOISE_ORM)
36
  await Tortoise.generate_schemas()
 
37
 
38
 
39
  @app.get("/")
 
50
  app.include_router(portal_router)
51
  app.include_router(metrics_router)
52
  app.include_router(message_router)
 
53
  # if __name__ == "__main__":
54
  # import uvicorn
55