File size: 3,816 Bytes
83bdb4a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
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 ───
    @field_validator("secret_key", "reset_password_token_secret", "verification_token_secret",
                     "mfa_encryption_key", "jwt_private_key", "jwt_public_key",
                     mode="before")
    @classmethod
    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"


@lru_cache()
def get_settings() -> Settings:
    return Settings()