Files
Autoparts-DB/pos/config.py
consultoria-as a386ca5b4a
Some checks failed
CI / lint-and-test (3.11) (push) Has been cancelled
CI / lint-and-test (3.13) (push) Has been cancelled
Mejoras de sesion: estabilizacion, IA, Nexus Yonke y marketplace B2B
- F0 blindaje: Redis/Celery, pg_hba sin trust LAN, secretos a env 600, backups, webhook auth, jobs muertos
- F1 caja: bugs POS, reembolsos al corte, apartados, CFDI, topes de descuento en cotizacion/apartado
- F2/F7 IA de catalogo: autocategorizacion, dedup de SKUs con embeddings, OCR de facturas, descripciones, claves SAT, limpieza de fitment
- F3 IA en el flujo: vision Yonke, preguntas MeLi, venta cruzada, anomalias, sinonimos, busqueda semantica (rerank/embeddings)
- F4/F8 escala y cartera: /metrics+Prometheus+Grafana, pool, score de credito, churn, metricas multi-tenant
- Nexus Yonke (M1-M6): modelo de datos 1-de-1, alta por donador, vitrina publica, MeLi usado, consignacion, garantia por dias, reportes, etiquetas
- Marketplace B2B entre yonkes (catalogo + pedidos cross-tenant)
- Seguridad: auth en /api/admin/* (35 endpoints), alertas por tenant
- Migraciones v4.27/v4.28; timers expire-demos y rollup-metrics
2026-10-12 00:57:31 +00:00

116 lines
6.4 KiB
Python

import os
import secrets
import warnings
# ─── Database ──────────────────────────────────────────────────────────────
MASTER_DB_URL = os.environ.get("MASTER_DB_URL")
TENANT_DB_URL_TEMPLATE = os.environ.get("TENANT_DB_URL_TEMPLATE")
if not MASTER_DB_URL:
raise ValueError(
"MASTER_DB_URL environment variable is required. "
"Example: postgresql://user:pass@localhost/nexus_autoparts"
)
if not TENANT_DB_URL_TEMPLATE:
raise ValueError(
"TENANT_DB_URL_TEMPLATE environment variable is required. "
"Example: postgresql://user:pass@localhost/{db_name}"
)
# ─── JWT Authentication ────────────────────────────────────────────────────
JWT_SECRET = os.environ.get("POS_JWT_SECRET")
if not JWT_SECRET:
raise ValueError(
"POS_JWT_SECRET environment variable is required. "
"Generate one with: python3 -c 'import secrets; print(secrets.token_hex(32))'"
)
JWT_ACCESS_EXPIRES = 28800 # 8 hours (full shift)
JWT_REFRESH_EXPIRES = 2592000 # 30 days
# ─── PIN Security ──────────────────────────────────────────────────────────
PIN_MAX_ATTEMPTS_PER_MINUTE = 5
PIN_LOCKOUT_THRESHOLD = 10
PIN_LOCKOUT_MINUTES = 15
TENANT_TEMPLATE_DB = "tenant_template"
# ─── AI / OpenRouter ───────────────────────────────────────────────────────
OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY")
if not OPENROUTER_API_KEY:
warnings.warn(
"OPENROUTER_API_KEY not set. AI chatbot features will be disabled.",
RuntimeWarning
)
# ─── Hermes Agent ──────────────────────────────────────────────────────────
HERMES_API_URL = os.environ.get("HERMES_API_URL", "http://192.168.10.71:8642/v1")
HERMES_API_KEY = os.environ.get("HERMES_API_KEY")
if not HERMES_API_KEY:
warnings.warn(
"HERMES_API_KEY not set. Hermes Agent integration will fall back to OpenRouter.",
RuntimeWarning
)
# ─── SMTP ──────────────────────────────────────────────────────────────────
SMTP_HOST = os.environ.get('SMTP_HOST', 'smtp.gmail.com')
SMTP_PORT = int(os.environ.get('SMTP_PORT', '587'))
SMTP_USER = os.environ.get('SMTP_USER', '')
SMTP_PASS = os.environ.get('SMTP_PASS', '')
SMTP_FROM = os.environ.get('SMTP_FROM', 'noreply@nexusautoparts.com.mx')
# ─── WhatsApp Bridge ───────────────────────────────────────────────────────
WHATSAPP_BRIDGE_URL = os.environ.get('WHATSAPP_BRIDGE_URL', 'http://localhost:21465')
WHATSAPP_BRIDGE_KEY = os.environ.get('WHATSAPP_BRIDGE_KEY')
if not WHATSAPP_BRIDGE_KEY:
warnings.warn(
"WHATSAPP_BRIDGE_KEY not set. WhatsApp integration will be disabled.",
RuntimeWarning
)
# ─── Multi-currency ────────────────────────────────────────────────────────
DEFAULT_CURRENCY = os.environ.get('DEFAULT_CURRENCY', 'MXN')
EXCHANGE_RATE_USD_MXN = float(os.environ.get('EXCHANGE_RATE_USD_MXN', '17.5'))
# ─── Redis Cache ───────────────────────────────────────────────────────────
REDIS_URL = os.environ.get('REDIS_URL', 'redis://localhost:6379/0')
REDIS_ENABLED = os.environ.get('REDIS_ENABLED', 'true').lower() == 'true'
REDIS_STOCK_TTL = int(os.environ.get('REDIS_STOCK_TTL', '300'))
# ─── Meilisearch ───────────────────────────────────────────────────────────
MEILI_URL = os.environ.get('MEILI_URL', 'http://localhost:7700')
MEILI_API_KEY = os.environ.get('MEILI_API_KEY', '')
MEILI_ENABLED = os.environ.get('MEILI_ENABLED', 'true').lower() == 'true'
# ─── Catalog OEM Access ────────────────────────────────────────────────────
CATALOG_OEM_ENABLED = os.environ.get('CATALOG_OEM_ENABLED', 'false').lower() == 'true'
# ─── QWEN AI Fitment (private cloud server) ────────────────────────────────
QWEN_API_URL = os.environ.get('QWEN_API_URL', '')
QWEN_API_KEY = os.environ.get('QWEN_API_KEY', '')
QWEN_MODEL = os.environ.get('QWEN_MODEL', 'qwen3.6')
# ─── Búsqueda semántica (rerank / embeddings, mismo proveedor QWEN) ────────
# Consumidas por services/semantic_search.py. OJO: se leen al importar el
# módulo — cambiar el flag requiere recargar el worker, no es hot-reload.
QWEN_RERANK_MODEL = os.environ.get('QWEN_RERANK_MODEL', 'rerank')
QWEN_EMBED_MODEL = os.environ.get('QWEN_EMBED_MODEL', 'qwen3-embedding')
# Interruptor maestro (scope 'catalog': catálogo público / API / dashboard).
SEARCH_RERANK_ENABLED = os.environ.get('SEARCH_RERANK_ENABLED', 'true').lower() == 'true'
# Scope POS (caja + chat): apagado por defecto, el rerank agrega ~0.6-1.8 s a
# una búsqueda que responde en ~18 ms (ver services/semantic_search.py).
SEARCH_RERANK_POS_ENABLED = os.environ.get('SEARCH_RERANK_POS_ENABLED', 'false').lower() == 'true'
# Un valor no numérico en el .env no debe tumbar el arranque del POS.
try:
SEARCH_RERANK_TIMEOUT = float(os.environ.get('SEARCH_RERANK_TIMEOUT', '2'))
except ValueError:
SEARCH_RERANK_TIMEOUT = 2.0
try:
SEARCH_RERANK_MAX_CANDIDATES = int(os.environ.get('SEARCH_RERANK_MAX_CANDIDATES', '50'))
except ValueError:
SEARCH_RERANK_MAX_CANDIDATES = 50
# ─── Internal Cron / Job Security ──────────────────────────────────────────
INTERNAL_API_KEY = os.environ.get('INTERNAL_API_KEY', '')