fix: float bug when creating users and showing float data (#1541)

* fix: float bug when creating users and showing float data

* update pydantic

* fix: cast reseted_usage and lifetime_used_traffic to int
This commit is contained in:
Mohammad
2024-12-25 12:43:42 +03:30
committed by GitHub
parent bc7aedff65
commit 1c26cd1dcd
4 changed files with 46 additions and 6 deletions

View File

@@ -103,8 +103,8 @@ class User(Base):
)
@hybrid_property
def reseted_usage(self):
return sum([log.used_traffic_at_reset for log in self.usage_logs])
def reseted_usage(self) -> int:
return int(sum([log.used_traffic_at_reset for log in self.usage_logs]))
@reseted_usage.expression
def reseted_usage(cls):
@@ -115,8 +115,8 @@ class User(Base):
)
@property
def lifetime_used_traffic(self):
return (
def lifetime_used_traffic(self) -> int:
return int(
sum([log.used_traffic_at_reset for log in self.usage_logs])
+ self.used_traffic
)

View File

@@ -3,7 +3,7 @@ from typing import Optional
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from passlib.context import CryptContext
from pydantic import ConfigDict, field_validator, BaseModel
from pydantic import BaseModel, ConfigDict, field_validator
from app.db import Session, crud, get_db
from app.utils.jwt import get_admin_payload
@@ -26,6 +26,16 @@ class Admin(BaseModel):
users_usage: Optional[int] = None
model_config = ConfigDict(from_attributes=True)
@field_validator("users_usage", mode='before')
def cast_to_int(cls, v):
if v is None: # Allow None values
return v
if isinstance(v, float): # Allow float to int conversion
return int(v)
if isinstance(v, int): # Allow integers directly
return v
raise ValueError("must be an integer or a float, not a string") # Reject strings
@classmethod
def get_admin(cls, token: str, db: Session):
payload = get_admin_payload(token)

View File

@@ -77,6 +77,16 @@ class User(BaseModel):
next_plan: Optional[NextPlanModel] = Field(None, nullable=True)
@field_validator('data_limit', mode='before')
def cast_to_int(cls, v):
if v is None: # Allow None values
return v
if isinstance(v, float): # Allow float to int conversion
return int(v)
if isinstance(v, int): # Allow integers directly
return v
raise ValueError("data_limit must be an integer or a float, not a string") # Reject strings
@field_validator("proxies", mode="before")
def validate_proxies(cls, v, values, **kwargs):
if not v:
@@ -306,6 +316,16 @@ class UserResponse(User):
v = {p.type: p.settings for p in v}
return super().validate_proxies(v, values, **kwargs)
@field_validator("used_traffic", "lifetime_used_traffic", mode='before')
def cast_to_int(cls, v):
if v is None: # Allow None values
return v
if isinstance(v, float): # Allow float to int conversion
return int(v)
if isinstance(v, int): # Allow integers directly
return v
raise ValueError("must be an integer or a float, not a string") # Reject strings
class SubscriptionUserResponse(UserResponse):
admin: Admin | None = Field(default=None, exclude=True)
@@ -326,6 +346,16 @@ class UserUsageResponse(BaseModel):
node_name: str
used_traffic: int
@field_validator("used_traffic", mode='before')
def cast_to_int(cls, v):
if v is None: # Allow None values
return v
if isinstance(v, float): # Allow float to int conversion
return int(v)
if isinstance(v, int): # Allow integers directly
return v
raise ValueError("must be an integer or a float, not a string") # Reject strings
class UserUsagesResponse(BaseModel):
username: str

View File

@@ -24,7 +24,7 @@ psutil==5.9.4
pyOpenSSL==24.2.1
PySocks==1.7.1
pyTelegramBotAPI==4.9.0
pydantic==2.10.3
pydantic==2.10.4
python-dateutil==2.8.2
python-decouple==3.6
python-dotenv==0.21.1