Sistema completo para monitoreo y gestion de flotas de vehiculos con: - Backend FastAPI con PostgreSQL/TimescaleDB - Frontend React con TypeScript y TailwindCSS - App movil React Native con Expo - Soporte para dispositivos GPS, Meshtastic y celulares - Video streaming en vivo con MediaMTX - Geocercas, alertas, viajes y reportes - Autenticacion JWT y WebSockets en tiempo real Documentacion completa y guias de usuario incluidas.
117 lines
3.0 KiB
Python
117 lines
3.0 KiB
Python
"""
|
|
Schemas Pydantic para Usuario.
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from typing import Optional
|
|
|
|
from pydantic import BaseModel, EmailStr, Field, field_validator
|
|
|
|
from app.schemas.base import BaseSchema, TimestampSchema
|
|
|
|
|
|
class UsuarioBase(BaseSchema):
|
|
"""Schema base de usuario."""
|
|
|
|
email: EmailStr
|
|
nombre: str = Field(..., min_length=2, max_length=100)
|
|
apellido: Optional[str] = Field(None, max_length=100)
|
|
telefono: Optional[str] = Field(None, max_length=20)
|
|
|
|
|
|
class UsuarioCreate(UsuarioBase):
|
|
"""Schema para crear usuario."""
|
|
|
|
password: str = Field(..., min_length=8, max_length=100)
|
|
es_admin: bool = False
|
|
|
|
@field_validator("password")
|
|
@classmethod
|
|
def validate_password(cls, v: str) -> str:
|
|
if len(v) < 8:
|
|
raise ValueError("La contraseña debe tener al menos 8 caracteres")
|
|
if not any(c.isupper() for c in v):
|
|
raise ValueError("La contraseña debe tener al menos una mayúscula")
|
|
if not any(c.islower() for c in v):
|
|
raise ValueError("La contraseña debe tener al menos una minúscula")
|
|
if not any(c.isdigit() for c in v):
|
|
raise ValueError("La contraseña debe tener al menos un número")
|
|
return v
|
|
|
|
|
|
class UsuarioUpdate(BaseSchema):
|
|
"""Schema para actualizar usuario."""
|
|
|
|
nombre: Optional[str] = Field(None, min_length=2, max_length=100)
|
|
apellido: Optional[str] = Field(None, max_length=100)
|
|
telefono: Optional[str] = Field(None, max_length=20)
|
|
avatar_url: Optional[str] = None
|
|
preferencias: Optional[str] = None
|
|
|
|
|
|
class UsuarioUpdatePassword(BaseModel):
|
|
"""Schema para cambiar contraseña."""
|
|
|
|
password_actual: str
|
|
password_nuevo: str = Field(..., min_length=8, max_length=100)
|
|
|
|
@field_validator("password_nuevo")
|
|
@classmethod
|
|
def validate_password(cls, v: str) -> str:
|
|
if len(v) < 8:
|
|
raise ValueError("La contraseña debe tener al menos 8 caracteres")
|
|
return v
|
|
|
|
|
|
class UsuarioResponse(UsuarioBase, TimestampSchema):
|
|
"""Schema de respuesta de usuario."""
|
|
|
|
id: int
|
|
es_admin: bool
|
|
activo: bool
|
|
ultimo_acceso: Optional[datetime] = None
|
|
avatar_url: Optional[str] = None
|
|
|
|
|
|
class UsuarioInDB(UsuarioResponse):
|
|
"""Schema interno con hash de password."""
|
|
|
|
password_hash: str
|
|
|
|
|
|
# ============================================================================
|
|
# Schemas de Autenticación
|
|
# ============================================================================
|
|
|
|
|
|
class LoginRequest(BaseModel):
|
|
"""Schema para solicitud de login."""
|
|
|
|
email: EmailStr
|
|
password: str
|
|
|
|
|
|
class LoginResponse(BaseModel):
|
|
"""Schema de respuesta de login."""
|
|
|
|
access_token: str
|
|
refresh_token: str
|
|
token_type: str = "bearer"
|
|
expires_in: int
|
|
user: UsuarioResponse
|
|
|
|
|
|
class RefreshTokenRequest(BaseModel):
|
|
"""Schema para refresh token."""
|
|
|
|
refresh_token: str
|
|
|
|
|
|
class TokenResponse(BaseModel):
|
|
"""Schema de respuesta de tokens."""
|
|
|
|
access_token: str
|
|
refresh_token: str
|
|
token_type: str = "bearer"
|
|
expires_in: int
|