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