Spaces:
Paused
Paused
| import os | |
| from pathlib import Path | |
| from pydantic_settings import BaseSettings | |
| from functools import lru_cache | |
| from pydantic import field_validator | |
| _backend_dir = str(Path(__file__).resolve().parent.parent) | |
| class Settings(BaseSettings): | |
| # App | |
| app_name: str = "CrowData API" | |
| environment: str = "development" | |
| debug: bool = False # Default: False por seguridad. Usar ENVIRONMENT=development para debug. | |
| # Database | |
| database_url: str = "" | |
| # Redis | |
| redis_url: str = "redis://localhost:6379/0" | |
| # Security — MUST be set in .env | |
| secret_key: str = "" | |
| reset_password_token_secret: str = "" # Separate secret for password reset tokens | |
| verification_token_secret: str = "" # Separate secret for email verification tokens | |
| # MFA Encryption Key (for encrypting TOTP secrets and backup codes in DB) | |
| mfa_encryption_key: str = "" # Fernet key (32 bytes base64) | |
| # JWT RS256 (asymmetric) — new keys for production | |
| jwt_private_key: str = "" # RS256 private key (PEM) | |
| jwt_public_key: str = "" # RS256 public key (PEM) | |
| jwt_algorithm: str = "RS256" # New tokens signed with RS256 | |
| jwt_key_id: str = "key-1" # Key ID for rotation | |
| # Legacy HS256 (symmetric) — for backward compatibility during transition | |
| jwt_legacy_secret_key: str = "" # HS256 secret (same as secret_key) | |
| jwt_legacy_algorithm: str = "HS256" | |
| jwt_legacy_enabled: bool = False # Disabled by default in production | |
| access_token_expire_minutes: int = 15 # 15 minutes (short-lived access token) | |
| refresh_token_expire_days: int = 7 # 7 days (refresh token in HttpOnly cookie) | |
| # Auth transport | |
| use_cookie_auth: bool = True # Enable HttpOnly cookie auth | |
| cookie_secure: bool = True # Secure cookie (HTTPS only) | |
| cookie_samesite: str = "lax" # Lax for cross-site top-level nav, Strict for more security | |
| # Cache | |
| cache_ttl_seconds: int = 86400 # 24 hours | |
| # Database pool | |
| db_pool_size: int = 10 | |
| db_max_overflow: int = 20 | |
| # Scrapers | |
| playwright_headless: bool = True | |
| scraper_timeout_seconds: int = 30 | |
| proxy_url: str | None = None | |
| proxy_list: list[str] = [] | |
| captcha_api_key: str | None = None # 2Captcha API key | |
| nopecha_api_key: str | None = None # NopeCHA API key (reCAPTCHA v3 solver) | |
| groq_api_key: str | None = None | |
| searchapi_key: str | None = None | |
| searchapi_keys: list[str] = [] | |
| ai_verification_enabled: bool = True | |
| # Payments — default: empty string (must be set in .env for production) | |
| mp_access_token: str = "" | |
| mp_public_key: str = "" | |
| # AFIP | |
| afip_cuit_representada: str = "" | |
| afip_cert_path: str = "" | |
| afip_key_path: str = "" | |
| afip_cache_file: str = "" | |
| # Email / SMTP | |
| smtp_host: str = "localhost" | |
| smtp_port: int = 587 | |
| smtp_user: str = "" | |
| smtp_password: str = "" | |
| smtp_use_tls: bool = True | |
| from_email: str = "crowsistemas@proton.me" | |
| from_name: str = "CrowData" | |
| # Allowed Origins (for CORS) | |
| allowed_origins: str = "" | |
| # ─── Validation ─── | |
| def _require_secrets_in_prod(cls, v, info): | |
| # Solo validar en producción | |
| if info.data.get("environment") == "production" and not v: | |
| raise ValueError(f"{info.field_name} must be set in production") | |
| return v | |
| class Config: | |
| env_file = os.path.join(_backend_dir, ".env") | |
| env_file_encoding = "utf-8" | |
| extra = "allow" | |
| def get_settings() -> Settings: | |
| return Settings() | |