gitscope
← marzneshin / v0.6.0 / app/models/user.py
https://dev.kronoz.win/r/marzneshin.git
app/models/user.py · Python
Blame Raw
  1import secrets
  2from datetime import datetime
  3from enum import Enum
  4from typing import Annotated
  5
  6from pydantic import (
  7    ConfigDict,
  8    BaseModel,
  9    Field,
 10    StringConstraints,
 11    model_validator,
 12)
 13
 14USERNAME_REGEXP = r"^\w{3,32}$"
 15
 16
 17class ReminderType(str, Enum):
 18    expiration_date = "expiration_date"
 19    data_usage = "data_usage"
 20
 21
 22class UserStatus(str, Enum):
 23    ACTIVE = "active"
 24    INACTIVE = "inactive"
 25
 26
 27class UserDataUsageResetStrategy(str, Enum):
 28    no_reset = "no_reset"
 29    day = "day"
 30    week = "week"
 31    month = "month"
 32    year = "year"
 33
 34
 35class UserExpireStrategy(str, Enum):
 36    NEVER = "never"
 37    FIXED_DATE = "fixed_date"
 38    START_ON_FIRST_USE = "start_on_first_use"
 39
 40
 41class User(BaseModel):
 42    id: int | None = None
 43    username: Annotated[
 44        str, StringConstraints(to_lower=True, pattern=USERNAME_REGEXP)
 45    ]
 46    expire_strategy: UserExpireStrategy
 47    expire_date: datetime | None = Field(None)
 48    usage_duration: int | None = Field(None)
 49    activation_deadline: datetime | None = Field(None)
 50    key: str = Field(default_factory=lambda: secrets.token_hex(16))
 51    data_limit: int | None = Field(
 52        ge=0, default=None, description="data_limit can be 0 or greater"
 53    )
 54    data_limit_reset_strategy: UserDataUsageResetStrategy = (
 55        UserDataUsageResetStrategy.no_reset
 56    )
 57    note: Annotated[str, Field(max_length=500)] | None = None
 58    sub_updated_at: datetime | None = Field(None)
 59    sub_last_user_agent: str | None = Field(None)
 60    online_at: datetime | None = Field(None)
 61
 62    model_config = ConfigDict(from_attributes=True)
 63
 64    @model_validator(mode="after")
 65    def validate_expiry(self):
 66        if self.expire_strategy == UserExpireStrategy.START_ON_FIRST_USE:
 67            if not self.usage_duration:
 68                raise ValueError(
 69                    "User expire_strategy cannot be start_on_first_use without a valid usage_duration."
 70                )
 71
 72            # Set expire_date to None if expire_strategy is START_ON_FIRST_USE
 73            self.expire_date = None
 74
 75        elif self.expire_strategy == UserExpireStrategy.FIXED_DATE:
 76            if not self.expire_date:
 77                raise ValueError(
 78                    "User expire_strategy cannot be fixed_date without a valid expire date."
 79                )
 80
 81            # Set usage_duration and activation_deadline to None if expire_strategy is START_ON_FIRST_USE
 82            self.usage_duration = None
 83            self.activation_deadline = None
 84
 85        elif self.expire_strategy == UserExpireStrategy.NEVER:
 86            # Set expire_date, usage_duration and activation_deadline to None if expire_strategy is NEVER
 87            self.expire_date = None
 88            self.usage_duration = None
 89            self.activation_deadline = None
 90
 91        return self
 92
 93
 94class UserCreate(User):
 95    service_ids: list[int] = Field([])
 96    model_config = ConfigDict(
 97        json_schema_extra={
 98            "example": {
 99                "username": "user1234",
100                "service_ids": [1, 2, 3],
101                "expire_strategy": "start_on_first_use",
102                "usage_duration": 86400 * 14,
103                "activation_deadline": "2024-11-03T20:30:00",
104                "data_limit": 0,
105                "data_limit_reset_strategy": "no_reset",
106                "note": "",
107            }
108        }
109    )
110
111
112class UserModify(UserCreate):
113    service_ids: list[int] | None = Field(None)
114    data_limit_reset_strategy: UserDataUsageResetStrategy | None = Field(None)
115    model_config = ConfigDict(
116        json_schema_extra={
117            "example": {
118                "username": "mammad1234",
119                "service_ids": [1, 2, 3],
120                "expire_strategy": "fixed_date",
121                "expire_date": "2023-11-03T20:30:00",
122                "data_limit": 0,
123                "data_limit_reset_strategy": "no_reset",
124                "note": "",
125            }
126        }
127    )
128
129
130class UserResponse(User):
131    id: int
132    activated: bool
133    is_active: bool
134    expired: bool
135    data_limit_reached: bool
136    enabled: bool
137    used_traffic: int
138    lifetime_used_traffic: int
139    sub_revoked_at: datetime | None
140    created_at: datetime
141    service_ids: list[int]
142    subscription_url: str
143    owner_username: str | None
144    traffic_reset_at: datetime | None
145
146    model_config = ConfigDict(from_attributes=True)
147
148
149class UserNodeUsageSeries(BaseModel):
150    node_id: int | None = None
151    node_name: str
152    usages: list[tuple[int, int]]  # list[UserUsage]
153
154
155class UserUsageSeriesResponse(BaseModel):
156    username: str
157    node_usages: list[UserNodeUsageSeries]
158    total: int