44from fastapi import Depends , HTTPException , status
55from fastapi .security import OAuth2PasswordBearer
66
7+ from sqlalchemy import func , select
78
89from app .db import AsyncSession , get_db
910from app .db .crud .admin import find_admins_by_telegram_id , get_admin as get_admin_by_username , get_admin_by_telegram_id
11+ from app .db .models import Admin , AdminUsageLogs , User
1012from app .models .admin import AdminDetails , AdminValidationResult , verify_password
1113from app .models .settings import Telegram
1214from app .settings import telegram_settings
1618oauth2_scheme = OAuth2PasswordBearer (tokenUrl = "/api/admin/token" )
1719
1820
21+ def _build_admin_details (
22+ db_admin : Admin ,
23+ * ,
24+ total_users : int = 0 ,
25+ reseted_usage : int | None = None ,
26+ ) -> AdminDetails :
27+ used_traffic = int (db_admin .used_traffic or 0 )
28+ return AdminDetails (
29+ id = db_admin .id ,
30+ username = db_admin .username ,
31+ is_sudo = db_admin .is_sudo ,
32+ total_users = int (total_users or 0 ),
33+ used_traffic = used_traffic ,
34+ is_disabled = db_admin .is_disabled ,
35+ telegram_id = db_admin .telegram_id ,
36+ discord_webhook = db_admin .discord_webhook ,
37+ sub_domain = db_admin .sub_domain ,
38+ profile_title = db_admin .profile_title ,
39+ support_url = db_admin .support_url ,
40+ note = db_admin .note ,
41+ notification_enable = db_admin .notification_enable ,
42+ discord_id = db_admin .discord_id ,
43+ sub_template = db_admin .sub_template ,
44+ lifetime_used_traffic = None if reseted_usage is None else int (reseted_usage or 0 ) + used_traffic ,
45+ )
46+
47+
48+ def _is_token_valid_for_admin (db_admin : Admin , payload : dict ) -> bool :
49+ if not db_admin .password_reset_at :
50+ return True
51+ if not payload .get ("created_at" ):
52+ return False
53+ return db_admin .password_reset_at .astimezone (tz .utc ) <= payload .get ("created_at" )
54+
55+
1956async def get_admin (db : AsyncSession , token : str ) -> AdminDetails | None :
2057 payload = await get_admin_payload (token )
2158 if not payload :
2259 return
2360
24- db_admin = await get_admin_by_username (db , payload ["username" ], load_users = True , load_usage_logs = True )
61+ db_admin = await get_admin_by_username (db , payload ["username" ], load_users = False , load_usage_logs = False )
2562 if db_admin :
26- if db_admin .password_reset_at :
27- if not payload .get ("created_at" ):
28- return
29- if db_admin .password_reset_at .astimezone (tz .utc ) > payload .get ("created_at" ):
30- return
63+ if not _is_token_valid_for_admin (db_admin , payload ):
64+ return
3165
32- return AdminDetails .model_validate (db_admin )
66+ return _build_admin_details (db_admin )
67+
68+ elif payload ["username" ] in SUDOERS and payload ["is_sudo" ] is True :
69+ return AdminDetails (username = payload ["username" ], is_sudo = True )
70+
71+
72+ async def get_admin_with_metrics (db : AsyncSession , token : str ) -> AdminDetails | None :
73+ payload = await get_admin_payload (token )
74+ if not payload :
75+ return
76+
77+ total_users_subquery = (
78+ select (func .count (User .id )).where (User .admin_id == Admin .id ).correlate (Admin ).scalar_subquery ()
79+ )
80+ reseted_usage_subquery = (
81+ select (func .coalesce (func .sum (AdminUsageLogs .used_traffic_at_reset ), 0 ))
82+ .where (AdminUsageLogs .admin_id == Admin .id )
83+ .correlate (Admin )
84+ .scalar_subquery ()
85+ )
86+ admin_row = (
87+ await db .execute (
88+ select (Admin , total_users_subquery , reseted_usage_subquery ).where (Admin .username == payload ["username" ])
89+ )
90+ ).one_or_none ()
91+
92+ if admin_row :
93+ db_admin , total_users , reseted_usage = admin_row
94+ if not _is_token_valid_for_admin (db_admin , payload ):
95+ return
96+
97+ return _build_admin_details (db_admin , total_users = total_users , reseted_usage = reseted_usage )
3398
3499 elif payload ["username" ] in SUDOERS and payload ["is_sudo" ] is True :
35100 return AdminDetails (username = payload ["username" ], is_sudo = True )
@@ -53,6 +118,24 @@ async def get_current(db: AsyncSession = Depends(get_db), token: str = Depends(o
53118 return admin
54119
55120
121+ async def get_current_with_metrics (db : AsyncSession = Depends (get_db ), token : str = Depends (oauth2_scheme )):
122+ admin : AdminDetails | None = await get_admin_with_metrics (db , token )
123+ if not admin :
124+ raise HTTPException (
125+ status_code = status .HTTP_401_UNAUTHORIZED ,
126+ detail = "Could not validate credentials" ,
127+ headers = {"WWW-Authenticate" : "Bearer" },
128+ )
129+ if admin .is_disabled :
130+ raise HTTPException (
131+ status_code = status .HTTP_403_FORBIDDEN ,
132+ detail = "your account has been disabled" ,
133+ headers = {"WWW-Authenticate" : "Bearer" },
134+ )
135+
136+ return admin
137+
138+
56139async def check_sudo_admin (admin : AdminDetails = Depends (get_current )):
57140 if not admin .is_sudo :
58141 raise HTTPException (status_code = status .HTTP_403_FORBIDDEN , detail = "You're not allowed" )
0 commit comments