Wire production domain, CORS for tenant subdomains, celery volume mounts, and nginx reverse proxy configs for apex, API, identity, auth, and wildcard tenants. Co-authored-by: Cursor <cursoragent@cursor.com>
67 lines
1.7 KiB
Python
67 lines
1.7 KiB
Python
"""ابزارهای صفحهبندی مشترک (offset/limit).
|
||
|
||
از این مدلها در همه سرویسها برای پاسخهای لیستی استفاده میشود.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from typing import Generic, Sequence, TypeVar
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
T = TypeVar("T")
|
||
|
||
DEFAULT_PAGE_SIZE = 20
|
||
MAX_PAGE_SIZE = 100
|
||
|
||
|
||
class PaginationParams(BaseModel):
|
||
"""پارامترهای صفحهبندی ورودی."""
|
||
|
||
page: int = Field(default=1, ge=1, description="شماره صفحه از ۱")
|
||
page_size: int = Field(
|
||
default=DEFAULT_PAGE_SIZE,
|
||
ge=1,
|
||
le=MAX_PAGE_SIZE,
|
||
description="تعداد آیتم در هر صفحه",
|
||
)
|
||
|
||
@property
|
||
def offset(self) -> int:
|
||
return (self.page - 1) * self.page_size
|
||
|
||
@property
|
||
def limit(self) -> int:
|
||
return self.page_size
|
||
|
||
|
||
class PageMeta(BaseModel):
|
||
page: int
|
||
page_size: int
|
||
total_items: int
|
||
total_pages: int
|
||
|
||
|
||
class Page(BaseModel, Generic[T]):
|
||
"""پاسخ لیستی صفحهبندیشده."""
|
||
|
||
items: Sequence[T]
|
||
meta: PageMeta
|
||
|
||
@classmethod
|
||
def create(
|
||
cls,
|
||
items: Sequence[T],
|
||
total_items: int,
|
||
params: PaginationParams,
|
||
) -> "Page[T]":
|
||
total_pages = (total_items + params.page_size - 1) // params.page_size if params.page_size else 0
|
||
return cls(
|
||
items=items,
|
||
meta=PageMeta(
|
||
page=params.page,
|
||
page_size=params.page_size,
|
||
total_items=total_items,
|
||
total_pages=total_pages,
|
||
),
|
||
)
|