import path from 'node:path' import dotenv from 'dotenv' // Carga .env local si existe y luego el .env raíz del ecosistema (no pisa vars ya definidas) dotenv.config({ path: [path.resolve(process.cwd(), '.env'), path.resolve(process.cwd(), '..', '.env')] }) function env(name: string, fallback?: string): string { const v = process.env[name] ?? fallback if (v === undefined) throw new Error(`Falta la variable de entorno ${name}`) return v } export const config = { port: Number(env('PORT', '3000')), nodeEnv: env('NODE_ENV', 'development'), databaseUrl: env('DATABASE_URL', 'file:./dev.db'), uploadsDir: path.resolve(process.cwd(), env('UPLOADS_DIR', './uploads')), monday: { apiKey: env('MONDAY_API_KEY'), apiUrl: env('MONDAY_API_URL', 'https://api.monday.com/v2'), apiVersion: '2024-10', workspace: env('MONDAY_WORKSPACE', ''), boards: { contactos: env('MONDAY_BOARD_CONTACTOS'), desarrollos: env('MONDAY_BOARD_DESARROLLOS'), unidades: env('MONDAY_BOARD_UNIDADES'), leads: env('MONDAY_BOARD_LEADS'), oportunidades: env('MONDAY_BOARD_OPORTUNIDADES'), apartado: env('MONDAY_BOARD_APARTADO'), ventas: env('MONDAY_BOARD_VENTAS'), // Tableros creados por scripts/setup-crm.ts; '' = aún no configurado motivos: env('MONDAY_BOARD_MOTIVOS', ''), actividades: env('MONDAY_BOARD_ACTIVIDADES', ''), }, }, automatizaciones: { // 'false' desactiva los crons (útil en tests/CI); los endpoints admin siguen vivos enabled: env('MONDAY_AUTOMATIONS_ENABLED', 'true') !== 'false', // 'false' desactiva la creación de actividades en Monday (default true; false en tests) actividadesEnabled: env('ACTIVIDADES_ENABLED', 'true') !== 'false', // IDs de usuario Monday separados por coma; vacío = auto-detectar usuarios no-guest del workspace rrUserIds: env('MONDAY_RR_USER_IDS', ''), inactividadDias: Number(env('INACTIVIDAD_DIAS', '10')), // Horas que un lead asignado puede seguir en NUEVO/Intento de contacto antes de escalar escaladoHoras: Number(env('LEAD_ESCALADO_HORAS', '48')), // User ID de Monday del coordinador que recibe los escalados; vacío = al vendedor asignado coordinadorUserId: Number(env('MONDAY_COORDINADOR_USER_ID', '0')), pollCron: env('POLL_CRON', '*/5 * * * *'), recordatorioCron: env('RECORDATORIO_CRON', '0 9 * * *'), }, recordatoriosCobranza: { // Escaneo diario de pagos por vencer / vencidos (motor de recordatorios) cron: env('RECORDATORIOS_CRON', '30 9 * * *'), // Días de anticipación para el recordatorio "próximo a vencer" diasAntes: Number(env('RECORDATORIO_DIAS_ANTES', '3')), // Cada cuántos días se re-recuerda un pago que sigue vencido revolverDias: Number(env('RECORDATORIO_REVOLVER_DIAS', '7')), }, ai: { // Proveedor NAN (OpenAI-compatible): chat + embeddings apiKey: env('NAN_API_KEY'), baseUrl: env('NAN_BASE_URL', 'https://api.nan.builders/v1'), chatModel: env('NAN_CHAT_MODEL', 'qwen3.6'), embedModel: env('NAN_EMBED_MODEL', 'qwen3-embedding'), // Umbral mínimo de similitud coseno para recuperar contexto (0-1) minScore: Number(env('AI_MIN_SCORE', '0.3')), }, auth: { jwtSecret: env('JWT_SECRET'), // Cadena aceptada por jsonwebtoken (p. ej. '7d') jwtExpiresIn: env('JWT_EXPIRES_IN', '7d'), // 'false' desactiva la exigencia de token en /api (tests, arranque temprano) enabled: env('AUTH_ENABLED', 'true') !== 'false', // Password del usuario admin sembrado (seed-usuarios.ts) adminPassword: env('ADMIN_PASSWORD', 'Frasa2026'), }, ligaRemota: { // Expiración del token de la liga pública de firma/documentos (cadena // aceptada por jsonwebtoken, p. ej. '72h') expiresIn: env('LIGA_REMOTA_EXP', '72h'), }, } as const export type AppConfig = typeof config