forked from cardosofelipe/fast-next-template
- Introduced `pyproject.toml` to centralize backend tool configurations (e.g., Ruff, mypy, coverage, pytest). - Replaced Black, isort, and Flake8 with Ruff for linting, formatting, and import sorting. - Updated `requirements.txt` to include Ruff and remove replaced tools. - Added `Makefile` to streamline development workflows with commands for linting, formatting, type-checking, testing, and cleanup.
184 lines
5.5 KiB
Python
184 lines
5.5 KiB
Python
"""
|
|
Pydantic schemas for user session management.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from uuid import UUID
|
|
|
|
from pydantic import BaseModel, ConfigDict, Field
|
|
|
|
|
|
class SessionBase(BaseModel):
|
|
"""Base schema for user sessions."""
|
|
|
|
device_name: str | None = Field(
|
|
None, max_length=255, description="Friendly device name"
|
|
)
|
|
device_id: str | None = Field(
|
|
None, max_length=255, description="Persistent device identifier"
|
|
)
|
|
|
|
|
|
class SessionCreate(SessionBase):
|
|
"""Schema for creating a new session (internal use)."""
|
|
|
|
user_id: UUID
|
|
refresh_token_jti: str = Field(..., max_length=255)
|
|
ip_address: str | None = Field(None, max_length=45)
|
|
user_agent: str | None = Field(None, max_length=500)
|
|
last_used_at: datetime
|
|
expires_at: datetime
|
|
location_city: str | None = Field(None, max_length=100)
|
|
location_country: str | None = Field(None, max_length=100)
|
|
|
|
|
|
class SessionUpdate(BaseModel):
|
|
"""Schema for updating a session (internal use)."""
|
|
|
|
last_used_at: datetime | None = None
|
|
is_active: bool | None = None
|
|
refresh_token_jti: str | None = None
|
|
expires_at: datetime | None = None
|
|
|
|
|
|
class SessionResponse(SessionBase):
|
|
"""
|
|
Schema for session responses to clients.
|
|
|
|
This is what users see when they list their active sessions.
|
|
"""
|
|
|
|
id: UUID
|
|
ip_address: str | None = None
|
|
location_city: str | None = None
|
|
location_country: str | None = None
|
|
last_used_at: datetime
|
|
created_at: datetime
|
|
expires_at: datetime
|
|
is_current: bool = Field(
|
|
default=False, description="Whether this is the current session"
|
|
)
|
|
|
|
model_config = ConfigDict(
|
|
from_attributes=True,
|
|
json_schema_extra={
|
|
"example": {
|
|
"id": "123e4567-e89b-12d3-a456-426614174000",
|
|
"device_name": "iPhone 14",
|
|
"device_id": "device-abc-123",
|
|
"ip_address": "192.168.1.100",
|
|
"location_city": "San Francisco",
|
|
"location_country": "United States",
|
|
"last_used_at": "2025-10-31T12:00:00Z",
|
|
"created_at": "2025-10-30T09:00:00Z",
|
|
"expires_at": "2025-11-06T09:00:00Z",
|
|
"is_current": True,
|
|
}
|
|
},
|
|
)
|
|
|
|
|
|
class SessionListResponse(BaseModel):
|
|
"""Response containing list of sessions."""
|
|
|
|
sessions: list[SessionResponse]
|
|
total: int = Field(..., description="Total number of active sessions")
|
|
|
|
model_config = ConfigDict(
|
|
json_schema_extra={
|
|
"example": {
|
|
"sessions": [
|
|
{
|
|
"id": "123e4567-e89b-12d3-a456-426614174000",
|
|
"device_name": "iPhone 14",
|
|
"ip_address": "192.168.1.100",
|
|
"last_used_at": "2025-10-31T12:00:00Z",
|
|
"created_at": "2025-10-30T09:00:00Z",
|
|
"expires_at": "2025-11-06T09:00:00Z",
|
|
"is_current": True,
|
|
}
|
|
],
|
|
"total": 1,
|
|
}
|
|
}
|
|
)
|
|
|
|
|
|
class LogoutRequest(BaseModel):
|
|
"""Request schema for logout endpoint."""
|
|
|
|
refresh_token: str = Field(
|
|
..., description="Refresh token for the session to logout from", min_length=10
|
|
)
|
|
|
|
model_config = ConfigDict(
|
|
json_schema_extra={
|
|
"example": {"refresh_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."}
|
|
}
|
|
)
|
|
|
|
|
|
class AdminSessionResponse(SessionBase):
|
|
"""
|
|
Schema for session responses in admin panel.
|
|
|
|
Includes user information for admin to see who owns each session.
|
|
"""
|
|
|
|
id: UUID
|
|
user_id: UUID
|
|
user_email: str = Field(..., description="Email of the user who owns this session")
|
|
user_full_name: str | None = Field(None, description="Full name of the user")
|
|
ip_address: str | None = None
|
|
location_city: str | None = None
|
|
location_country: str | None = None
|
|
last_used_at: datetime
|
|
created_at: datetime
|
|
expires_at: datetime
|
|
is_active: bool
|
|
|
|
model_config = ConfigDict(
|
|
from_attributes=True,
|
|
json_schema_extra={
|
|
"example": {
|
|
"id": "123e4567-e89b-12d3-a456-426614174000",
|
|
"user_id": "456e7890-e89b-12d3-a456-426614174001",
|
|
"user_email": "user@example.com",
|
|
"user_full_name": "John Doe",
|
|
"device_name": "iPhone 14",
|
|
"device_id": "device-abc-123",
|
|
"ip_address": "192.168.1.100",
|
|
"location_city": "San Francisco",
|
|
"location_country": "United States",
|
|
"last_used_at": "2025-10-31T12:00:00Z",
|
|
"created_at": "2025-10-30T09:00:00Z",
|
|
"expires_at": "2025-11-06T09:00:00Z",
|
|
"is_active": True,
|
|
}
|
|
},
|
|
)
|
|
|
|
|
|
class DeviceInfo(BaseModel):
|
|
"""Device information extracted from request."""
|
|
|
|
device_name: str | None = None
|
|
device_id: str | None = None
|
|
ip_address: str | None = None
|
|
user_agent: str | None = None
|
|
location_city: str | None = None
|
|
location_country: str | None = None
|
|
|
|
model_config = ConfigDict(
|
|
json_schema_extra={
|
|
"example": {
|
|
"device_name": "Chrome on MacBook",
|
|
"device_id": "device-xyz-789",
|
|
"ip_address": "192.168.1.50",
|
|
"user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)...",
|
|
"location_city": "San Francisco",
|
|
"location_country": "United States",
|
|
}
|
|
}
|
|
)
|