Mejora de fromts

This commit is contained in:
2026-03-03 11:25:37 -07:00
parent 49dfb3ef24
commit f10b15d91b
19 changed files with 180 additions and 2115 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -21,6 +21,7 @@ from app.services.audit_service import AuditService
from app.services.token_service import TokenService
from app.api.deps import oauth2_scheme, get_current_user
from app.core.cache import cache, cache_key
from app.core.limiter import limiter
# Nombres de cookie por tipo de usuario
CLIENT_ROLES = {"CLIENT_ADMIN", "CLIENT_USER"}
@@ -46,6 +47,7 @@ settings = get_settings()
# ===================================
@router.post("/login", response_model=LoginResponse)
@limiter.limit("10/minute")
async def login(
login_data: LoginRequest,
request: Request,
@@ -707,7 +709,9 @@ _RESET_KEY_PREFIX = "pwd_reset:"
@router.post("/forgot-password", status_code=status.HTTP_200_OK)
@limiter.limit("5/minute")
async def forgot_password(
request: Request,
data: ForgotPasswordRequest,
db: AsyncSession = Depends(get_db),
):
@@ -774,7 +778,9 @@ async def forgot_password(
@router.post("/reset-password", status_code=status.HTTP_200_OK)
@limiter.limit("5/minute")
async def reset_password(
request: Request,
data: ResetPasswordRequest,
db: AsyncSession = Depends(get_db),
):

File diff suppressed because it is too large Load Diff

View File

@@ -5,7 +5,6 @@ Configuración centralizada usando Pydantic Settings v2
"""
from functools import lru_cache
from typing import List, Optional
from pydantic_settings import BaseSettings
from pydantic import field_validator, Field
import os
@@ -60,8 +59,8 @@ class Settings(BaseSettings):
# ===================================
SMTP_HOST: str = Field(default="localhost")
SMTP_PORT: int = Field(default=587)
SMTP_USER: Optional[str] = Field(default=None)
SMTP_PASSWORD: Optional[str] = Field(default=None)
SMTP_USER: str | None = Field(default=None)
SMTP_PASSWORD: str | None = Field(default=None)
SMTP_USE_TLS: bool = Field(default=True)
SMTP_USE_SSL: bool = Field(default=False)
@@ -79,7 +78,7 @@ class Settings(BaseSettings):
UPLOAD_PATH: str = Field(default="/app/uploads")
@property
def ALLOWED_FILE_EXTENSIONS(self) -> List[str]:
def ALLOWED_FILE_EXTENSIONS(self) -> list[str]:
"""Parse the comma-separated file extensions."""
return [ext.strip().lower() for ext in self.ALLOWED_FILE_EXTENSIONS_STR.split(",")]
@@ -102,7 +101,7 @@ class Settings(BaseSettings):
# ===================================
LOG_LEVEL: str = Field(default="INFO")
LOG_FORMAT: str = Field(default="json")
LOG_FILE: Optional[str] = Field(default=None)
LOG_FILE: str | None = Field(default=None)
# ===================================
# FRONTEND URLS

View File

@@ -0,0 +1,20 @@
"""
Rate Limiter - ServiceManagerWeb
Configura slowapi con Redis como storage backend.
Respeta settings.RATE_LIMIT_ENABLED: si está desactivado usa memoria
y el limiter queda en modo noop (enabled=False).
"""
from slowapi import Limiter
from slowapi.util import get_remote_address
from app.core.config import get_settings
settings = get_settings()
limiter = Limiter(
key_func=get_remote_address,
storage_uri=settings.REDIS_URL if settings.RATE_LIMIT_ENABLED else "memory://",
enabled=settings.RATE_LIMIT_ENABLED,
)

View File

@@ -9,6 +9,9 @@ from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.gzip import GZipMiddleware
from fastapi.responses import JSONResponse
from contextlib import asynccontextmanager
from slowapi import _rate_limit_exceeded_handler
from slowapi.errors import RateLimitExceeded
from slowapi.middleware import SlowAPIMiddleware
import structlog
import time
import uuid
@@ -31,6 +34,7 @@ from app.api.v1.router import api_router
from app.middleware.tenant import TenantMiddleware
from app.middleware.correlation_id import CorrelationIDMiddleware
from app.core.cache import cache
from app.core.limiter import limiter
settings = get_settings()
setup_logging()
@@ -70,6 +74,11 @@ app = FastAPI(
openapi_url=f"/{settings.API_VERSION}/openapi.json"
)
# SlowAPI rate limiting
app.state.limiter = limiter
app.add_exception_handler(RateLimitExceeded, _rate_limit_exceeded_handler)
app.add_middleware(SlowAPIMiddleware)
# ===================================
# MIDDLEWARE
# ===================================

View File

@@ -0,0 +1,43 @@
"""add_ticket_indexes
Revision ID: b7c8d9e0f1a2
Revises: fix_client_timestamps
Create Date: 2026-03-03 00:00:00.000000
Agrega índices a la tabla tickets para optimizar queries frecuentes:
- idx_tickets_status → filtros por estado
- idx_tickets_priority → filtros por prioridad
- idx_tickets_assigned_to → tickets por agente asignado
- idx_tickets_tenant_status → compuesto multi-tenant (tenant_id, status)
"""
from alembic import op
# revision identifiers, used by Alembic.
revision = 'b7c8d9e0f1a2'
down_revision = 'fix_client_timestamps'
branch_labels = None
depends_on = None
def upgrade() -> None:
op.execute(
"CREATE INDEX IF NOT EXISTS idx_tickets_status ON tickets (status)"
)
op.execute(
"CREATE INDEX IF NOT EXISTS idx_tickets_priority ON tickets (priority)"
)
op.execute(
"CREATE INDEX IF NOT EXISTS idx_tickets_assigned_to "
"ON tickets (assigned_to) WHERE assigned_to IS NOT NULL"
)
op.execute(
"CREATE INDEX IF NOT EXISTS idx_tickets_tenant_status "
"ON tickets (tenant_id, status)"
)
def downgrade() -> None:
op.execute("DROP INDEX IF EXISTS idx_tickets_tenant_status")
op.execute("DROP INDEX IF EXISTS idx_tickets_assigned_to")
op.execute("DROP INDEX IF EXISTS idx_tickets_priority")
op.execute("DROP INDEX IF EXISTS idx_tickets_status")

View File

@@ -31,6 +31,7 @@ pyotp==2.9.0 # TOTP/2FA support
# ===================================
celery==5.3.4
redis==5.0.1
slowapi==0.1.9 # Rate limiting middleware
# ===================================
# EMAIL